XF 2.3 Toggling Widgets

Ozzy47

Well-known member
I'm working on an addon to turn widgets on/off (toggle) like we do with template modifications. I have that part working, my column in the DB shows 0 for the one I toggled off. But I am having difficulty getting it to not show. I tried extending XF\Widget\WidgetRenderer like this:

PHP:
<?php

namespace OzzModz\ToggleWidgets\XF\Widget;

class WidgetRenderer extends XFCP_WidgetRenderer
{
    public function render()
    {
        \XF::logError('ToggleWidgets: Renderer called');
      
        try {
            $widget = $this->getCurrentWidget();
            \XF::logError('ToggleWidgets: Widget ' . ($widget ? 'FOUND ID ' . $widget->widget_id : 'NOT FOUND'));

            if ($widget) {
                \XF::logError(sprintf(
                    'ToggleWidgets: Widget ID %d - Active: %d, Defined: %d',
                    $widget->widget_id,
                    $widget->ozzmodz_widget_active,
                    isset($widget->ozzmodz_widget_active)
                ));

                if (!$widget->ozzmodz_widget_active) {
                    \XF::logError('ToggleWidgets: BLOCKING widget ID ' . $widget->widget_id);
                    return '<!-- WIDGET DISABLED -->';
                }
            }

            $result = parent::render();
            \XF::logError('ToggleWidgets: Rendered normally');
            return $result;

        } catch (\Throwable $e) {
            \XF::logError('ToggleWidgets ERROR: ' . $e->getMessage());
            return parent::render();
        }
    }

    protected function getCurrentWidget()
    {
        // Check all possible widget locations with explicit logging
        if (property_exists($this, 'widget') && isset($this->widget)) {
            \XF::logError('ToggleWidgets: Found widget in $this->widget');
            return $this->widget;
        }

        if (property_exists($this, 'positionContext')
            && isset($this->positionContext['widget'])
        ) {
            \XF::logError('ToggleWidgets: Found widget in positionContext');
            return $this->positionContext['widget'];
        }

        if (property_exists($this, 'params')
            && isset($this->params['widget'])
        ) {
            \XF::logError('ToggleWidgets: Found widget in params');
            return $this->params['widget'];
        }

        \XF::logError('ToggleWidgets: No widget found in any standard location');
        return null;
    }
}

I get this in the logs due to debug code:

Code:
ToggleWidgets: Widget NOT FOUND A moment ago src/XF/Error.php:81
  
ToggleWidgets: No widget found in any standard location A moment ago src/XF/Error.php:81
  
ToggleWidgets: Renderer called A moment ago src/XF/Error.php:81

I also have a few other things extended:

Code:
XF\Admin\Controller\Widget
OzzModz\ToggleWidgets\XF\Admin\Controller\Widget
   
XF\Entity\Widget
OzzModz\ToggleWidgets\XF\Entity\Widget
   
XF\Repository\Widget
OzzModz\ToggleWidgets\XF\Repository\Widget
   
XF\Widget\WidgetRenderer
OzzModz\ToggleWidgets\XF\Widget\WidgetRenderer
 
For ****s and giggles I ran this through chatgpt:

Code:
You're very close! You're correctly extending `XF\Widget\WidgetRenderer` and attempting to access the current widget, but the issue seems to be **the timing or context of your `render()` override** — specifically, that the `widget` entity isn’t reliably accessible in `$this->widget`, `positionContext`, or `params`.

Here’s a more robust approach you can try to ensure your toggle logic is respected.

---

## ✅ Best Practice Approach

Rather than overriding `WidgetRenderer::render()` directly, hook into the `XF:widget_pre_render` or `XF:widget_render` **template hooks**, or better: **use a class extension on `XF\Widget\AbstractWidget` and intercept `render()` from there**.

---

## 🔧 Recommended Fix: Extend `XF\Widget\AbstractWidget`

Each widget class in XF extends `XF\Widget\AbstractWidget`, and that’s where `render()` gets called for each individual widget instance.

Extend `XF\Widget\AbstractWidget` instead, like this:

```php
namespace OzzModz\ToggleWidgets\XF\Widget;

class AbstractWidget extends XFCP_AbstractWidget
{
    public function render()
    {
        $widgetEntity = $this->widget;

        \XF::logError('ToggleWidgets: AbstractWidget::render called');

        if ($widgetEntity && isset($widgetEntity->ozzmodz_widget_active)) {
            \XF::logError(sprintf(
                'ToggleWidgets: Widget ID %s - Active: %s',
                $widgetEntity->widget_id,
                $widgetEntity->ozzmodz_widget_active
            ));

            if (!$widgetEntity->ozzmodz_widget_active) {
                \XF::logError('ToggleWidgets: BLOCKING widget ID ' . $widgetEntity->widget_id);
                return ''; // or comment, e.g. <!-- Widget Disabled -->
            }
        } else {
            \XF::logError('ToggleWidgets: No widget entity or no ozzmodz_widget_active');
        }

        return parent::render();
    }
}
```

---

## ✅ Don’t Forget to Register the Extension

In your `addon.json`, make sure you’re extending `XF\Widget\AbstractWidget`:

```json
"extend": {
  "XF\\Widget\\AbstractWidget": [
    "OzzModz\\ToggleWidgets\\XF\\Widget\\AbstractWidget"
  ]
}
```

---

## 🧪 Verifying the Column is Available

Make sure you’ve updated the widget entity (`XF\Entity\Widget`) to expose the `ozzmodz_widget_active` field. You should have:

```php
protected function getStructure(Structure $structure)
{
    $structure = parent::getStructure($structure);
    
    $structure->columns['ozzmodz_widget_active'] = ['type' => self::BOOL, 'default' => 1];

    return $structure;
}
```

Also, ensure you’re rebuilding the container or doing a development rebuild after changes (`php cmd.php xf-dev:rebuild` or clear caches in ACP).

---

## ✅ Extra Debugging Tip

In `AbstractWidget::render()`, dump the class name to ensure your extension is actually being used:

```php
\XF::logError('ToggleWidgets: Class is ' . get_class($this));
```

---

Let me know what happens after switching the extension target — if it still doesn't detect the widget properly, we’ll dig into how XF passes widget context during render.

Not sure it will help you but might get you closer to a solution.
 
## 🔧 Recommended Fix: Extend XF\Widget\AbstractWidget
Typical AI nonsense ...

It is impossible to extend AbstractWidget or any other abstract / base class via class proxy system.

Take a look at XF\Repository\WidgetCache::getWidgetCache, put your flag in there and check this in renderer.
 
Last edited:
Back
Top Bottom