+
+ {$e->getMessage()}
+
+
+
+ Debug backtrace
+
+
+
{$e->getFile()}:{$e->getLine()}
+{$e->getTraceAsString()}
+
+HTML;
+
+ }
+ else
+ {
+ // An error occurred. Cache it so that the finally block runs first.
+ $exception = $e;
+ }
+
+ }
+ finally
+ {
+ // Undo the custom template paths and layout
+ $this->_path['template'] = $previousTemplatePaths;
+ $this->setLayout($previousLayout);
+ }
+
+ // If an error had occurred, rethrow the exception and terminate early.
+ if (!is_null($exception))
+ {
+ throw $exception;
+ }
+
+ // Return the HTML of the parsed template.
+ return $ret;
+ }
+
+ /**
+ * Load a template file -- first look in the templates folder for an override
+ *
+ * Copied from Joomla 4.0. Added the $fallbackToDefault and $extraVariables options.
+ *
+ * @param null $tpl The name of the template source file; automatically searches the template
+ * paths and compiles as needed.
+ * @param bool $fallbackToDefault Should I fall back to the default layout?
+ * @param array $extraVariables Extra variables to introduce in the view template's scope
+ *
+ * @return string The output of the the template script.
+ *
+ * @throws Exception
+ * @since 9.0.0
+ */
+ public function loadTemplate($tpl = null, bool $fallbackToDefault = true, array $extraVariables = []): string
+ {
+ // Clear prior output
+ $this->_output = null;
+
+ $template = Factory::getApplication()->getTemplate(true);
+ $layout = $this->getLayout();
+ $layoutTemplate = $this->getLayoutTemplate();
+
+ // Create the template file name based on the layout
+ $file = isset($tpl) ? $layout . '_' . $tpl : $layout;
+
+ // Clean the file name
+ $file = preg_replace('/[^A-Z0-9_\.-]/i', '', $file);
+ $tpl = isset($tpl) ? preg_replace('/[^A-Z0-9_\.-]/i', '', $tpl) : $tpl;
+
+ // Load the language file for the template
+ $lang = Factory::getLanguage();
+ $lang->load('tpl_' . $template->template, JPATH_BASE)
+ || $lang->load('tpl_' . $template->parent, JPATH_THEMES . '/' . $template->parent)
+ || $lang->load('tpl_' . $template->template, JPATH_THEMES . '/' . $template->template);
+
+ // Change the template folder if alternative layout is in different template
+ if (isset($layoutTemplate) && $layoutTemplate !== '_' && $layoutTemplate != $template->template)
+ {
+ $this->_path['template'] = str_replace(
+ JPATH_THEMES . DIRECTORY_SEPARATOR . $template->template,
+ JPATH_THEMES . DIRECTORY_SEPARATOR . $layoutTemplate,
+ $this->_path['template']
+ );
+ }
+
+ // Load the template script
+ $filetofind = $this->_createFileName('template', ['name' => $file]);
+ $this->_template = Path::find($this->_path['template'], $filetofind);
+
+ // If alternate layout can't be found, fall back to default layout
+ if (($this->_template === false) && $fallbackToDefault)
+ {
+ $filetofind = $this->_createFileName('', ['name' => 'default' . (isset($tpl) ? '_' . $tpl : $tpl)]);
+ $this->_template = Path::find($this->_path['template'], $filetofind);
+ }
+
+ if ($this->_template != false)
+ {
+ // Unset so as not to introduce into template scope
+ unset($tpl, $file);
+
+ // Never allow a 'this' property
+ if (isset($this->this))
+ {
+ unset($this->this);
+ }
+
+ // Start capturing output into a buffer
+ ob_start();
+
+ empty($extraVariables) || extract($extraVariables);
+
+ // Include the requested template filename in the local scope
+ // (this will execute the view logic).
+ include $this->_template;
+
+ // Done with the requested template; get the buffer and
+ // clear it.
+ $this->_output = ob_get_contents();
+ ob_end_clean();
+
+ return $this->_output;
+ }
+
+ throw new Exception(Text::sprintf('JLIB_APPLICATION_ERROR_LAYOUTFILE_NOT_FOUND', $file), 500);
+ }
+
+}
\ No newline at end of file
diff --git a/component/backend/src/Mixin/RegisterControllerTasks.php b/component/backend/src/Mixin/RegisterControllerTasks.php
new file mode 100644
index 00000000..effc04a2
--- /dev/null
+++ b/component/backend/src/Mixin/RegisterControllerTasks.php
@@ -0,0 +1,66 @@
+registerDefaultTask($defaultTask);
+
+ $refObj = new ReflectionObject($this);
+
+ /** @var ReflectionMethod $refMethod */
+ foreach ($refObj->getMethods(ReflectionMethod::IS_PUBLIC) as $refMethod)
+ {
+ if (
+ !$refMethod->isUserDefined() ||
+ $refMethod->isStatic() || $refMethod->isAbstract() || $refMethod->isClosure() ||
+ $refMethod->isConstructor() || $refMethod->isDestructor()
+
+ )
+ {
+ continue;
+ }
+
+ $method = $refMethod->getName();
+
+ if (substr($method, 0, 1) == '_')
+ {
+ continue;
+ }
+
+ if (substr($method, 0, 8) == 'onBefore')
+ {
+ continue;
+ }
+
+ if (substr($method, 0, 7) == 'onAfter')
+ {
+ continue;
+ }
+
+ $this->registerTask($method, $method);
+ }
+ }
+}
\ No newline at end of file
diff --git a/component/backend/src/Mixin/ReusableModels.php b/component/backend/src/Mixin/ReusableModels.php
new file mode 100644
index 00000000..734a29ef
--- /dev/null
+++ b/component/backend/src/Mixin/ReusableModels.php
@@ -0,0 +1,92 @@
+input->get('view', $this->default_view));
+ }
+
+ $prefix = ucfirst($prefix ?: $this->app->getName());
+
+ $hash = md5(strtolower($name . $prefix));
+
+ if (isset(self::$_models[$hash]))
+ {
+ return self::$_models[$hash];
+ }
+
+ self::$_models[$hash] = parent::getModel($name, $prefix, $config);
+
+ return self::$_models[$hash];
+ }
+
+ /**
+ * @param string $name
+ * @param string $type
+ * @param string $prefix
+ * @param array $config
+ *
+ * @return ViewInterface|HtmlView
+ * @throws Exception
+ */
+ public function getView($name = '', $type = '', $prefix = '', $config = [])
+ {
+ $document = $this->app->getDocument();
+
+ if (empty($name))
+ {
+ $name = $this->input->get('view', $this->default_view);
+ }
+
+ if (empty($type))
+ {
+ $type = $document->getType();
+ }
+
+ if (empty($config))
+ {
+ $viewLayout = $this->input->get('layout', 'default', 'string');
+ $config = ['base_path' => $this->basePath, 'layout' => $viewLayout];
+ }
+
+ $hadView = isset(self::$views)
+ && isset(self::$views[$name])
+ && isset(self::$views[$name][$type])
+ && isset(self::$views[$name][$type][$prefix])
+ && !empty(self::$views[$name][$type][$prefix]);
+
+ $view = parent::getView($name, $type, $prefix, $config);
+
+ if (!$hadView)
+ {
+ // Get/Create the model
+ if ($model = $this->getModel($name, 'Administrator', ['base_path' => $this->basePath]))
+ {
+ // Push the model into the view (as default)
+ $view->setModel($model, true);
+ }
+
+ $view->document = $document;
+ }
+
+ return $view;
+ }
+}
\ No newline at end of file
diff --git a/component/backend/src/Mixin/TaskBasedEvents.php b/component/backend/src/Mixin/TaskBasedEvents.php
new file mode 100644
index 00000000..ab64fd0d
--- /dev/null
+++ b/component/backend/src/Mixin/TaskBasedEvents.php
@@ -0,0 +1,28 @@
+getModel()->getState('task');
+
+ $eventName = 'onBefore' . ucfirst($task);
+ $this->triggerEvent($eventName, [&$tpl]);
+
+ parent::display($tpl);
+
+ $eventName = 'onAfter' . ucfirst($task);
+ $this->triggerEvent($eventName, [&$tpl]);
+ }
+}
\ No newline at end of file
diff --git a/component/backend/src/Mixin/TriggerEvent.php b/component/backend/src/Mixin/TriggerEvent.php
new file mode 100644
index 00000000..8586770b
--- /dev/null
+++ b/component/backend/src/Mixin/TriggerEvent.php
@@ -0,0 +1,77 @@
+onBeforeSomething(123, 456)
+ * 2. $this->checkACL('@something') if there is no onBeforeSomething and the event starts with onBefore
+ * 3. Joomla! plugin event onComFoobarControllerItemBeforeSomething($this, 123, 456)
+ *
+ * @param string $event The name of the event, typically named onPredicateVerb e.g. onBeforeKick
+ * @param array $arguments The arguments to pass to the event handlers
+ *
+ * @return bool
+ */
+ protected function triggerEvent(string $event, array $arguments = []): bool
+ {
+ // If there is an object method for this event, call it
+ if (method_exists($this, $event))
+ {
+ if (call_user_func([$this, $event], ...$arguments) === false)
+ {
+ return false;
+ }
+ }
+
+ // All other event handlers live outside this object, therefore they need to be passed a reference to this
+ // object as the first argument.
+ array_unshift($arguments, $this);
+
+ // If we have an "on" prefix for the event (e.g. onFooBar) remove it and stash it for later.
+ $prefix = '';
+
+ if (substr($event, 0, 2) == 'on')
+ {
+ $prefix = 'on';
+ $event = substr($event, 2);
+ }
+
+ // Get the component name and object type from the namespace of the caller
+ $callers = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT | DEBUG_BACKTRACE_IGNORE_ARGS);
+ $namespaceParts = explode('\\', $callers[1]['class']);
+ $className = array_pop($namespaceParts);
+ $objectType = array_pop($namespaceParts);
+ array_pop($namespaceParts);
+ $bareComponent = strtolower(array_pop($namespaceParts));
+
+ // Get the component/model prefix for the event
+ $prefix .= 'Com' . ucfirst($bareComponent);
+ $prefix .= ucfirst($className);
+
+ // The event name will be something like onComFoobarControllerItemsBeforeSomething
+ $event = $prefix . $event;
+
+ // Call the Joomla! plugins
+ $results = Factory::getApplication()->triggerEvent($event, $arguments);
+
+ return !in_array(false, $results, true);
+ }
+
+}
\ No newline at end of file
diff --git a/plugins/content/arsdlid/language/index.html b/component/backend/src/Model/index.html
similarity index 100%
rename from plugins/content/arsdlid/language/index.html
rename to component/backend/src/Model/index.html
diff --git a/component/backend/src/Service/Html/AkeebaReleaseSystem.php b/component/backend/src/Service/Html/AkeebaReleaseSystem.php
new file mode 100644
index 00000000..e43ed5b2
--- /dev/null
+++ b/component/backend/src/Service/Html/AkeebaReleaseSystem.php
@@ -0,0 +1,15 @@
+
+