User tests: Successful: Unsuccessful:
When we have to get a component path, we use JPATH_COMPONENT
now. But this constant is dynamic, we can't get a component path in other component. And if we get Model of a component, we need add some path first:
JForm::addFormPath( COMPONENT_PATH . '/models/forms');
JForm::addFieldPath( COMPONENT_PATH . '/models/fields' );
$model->addTablePath(COMPONENT_PATH . '/tables');
$model->getForm();
$model->getTable();
Model cannot guess path they located.
Now we can use JHelperPath
to get extension path, for example:
JHelperPath::get('com_content'); // /var/www/joomla/components/com_content
JHelperPath::get('mod_latest_news', 'site'); // /var/www/joomla/modules/mod_latest_news
JHelperPath::get('plg_system_debug'); // /var/www/joomla/plugins/system/debug
JHelperPath::getAdmin('tpl_isis'); // /var/www/joomla/administrator/templates/isis
JHelperPath::getAdmin('com_content', true); // components/com_content (Relative path)
With this class, controller and model will be able to guess where they located:
// Set the default view search path
if (array_key_exists('table_path', $config))
{
$this->addTablePath($config['table_path']);
}
// Guess component position
elseif (!empty($this->option))
{
$this->addTablePath(JHelperPath::get($this->option) . '/tables');
$this->addTablePath(JHelperPath::get($this->option) . '/table');
}
// Use constant
elseif (defined('JPATH_COMPONENT_ADMINISTRATOR'))
{
$this->addTablePath(JPATH_COMPONENT_ADMINISTRATOR . '/tables');
$this->addTablePath(JPATH_COMPONENT_ADMINISTRATOR . '/table');
}
Description | <h2>Introduction</h2> <p>When we have to get a component path, we use <code>JPATH_COMPONENT</code> now. But this constant is dynamic, we can't get a component path in other component. And if we get Model of a component, we need add some path first:</p> <div class="highlight highlight-php"><pre><span class="nx">JForm</span><span class="o">::</span><span class="na">addFormPath</span><span class="p">(</span> <span class="nx">COMPONENT_PATH</span> <span class="o">.</span> <span class="s1">'/models/forms'</span><span class="p">);</span> <span class="nx">JForm</span><span class="o">::</span><span class="na">addFieldPath</span><span class="p">(</span> <span class="nx">COMPONENT_PATH</span> <span class="o">.</span> <span class="s1">'/models/fields'</span> <span class="p">);</span> <span class="nv">$model</span><span class="o">-></span><span class="na">addTablePath</span><span class="p">(</span><span class="nx">COMPONENT_PATH</span> <span class="o">.</span> <span class="s1">'/tables'</span><span class="p">);</span> <span class="nv">$model</span><span class="o">-></span><span class="na">getForm</span><span class="p">();</span> <span class="nv">$model</span><span class="o">-></span><span class="na">getTable</span><span class="p">();</span> </pre></div> <p>Model cannot guess path.</p> <h2>The new PathHelper</h2> <p>Now we can use <code>JHelperPath</code> to get extension path, for example:</p> <div class="highlight highlight-php"><pre><span class="nx">JHelperPath</span><span class="o">::</span><span class="na">get</span><span class="p">(</span><span class="s1">'com_content'</span><span class="p">);</span> <span class="c1">// /var/www/joomla/components/com_content</span> <span class="nx">JHelperPath</span><span class="o">::</span><span class="na">get</span><span class="p">(</span><span class="s1">'mod_latest_news'</span><span class="p">,</span> <span class="s1">'site'</span><span class="p">);</span> <span class="c1">// /var/www/joomla/modules/mod_latest_news</span> <span class="nx">JHelperPath</span><span class="o">::</span><span class="na">get</span><span class="p">(</span><span class="s1">'plg_system_debug'</span><span class="p">);</span> <span class="c1">// /var/www/joomla/plugins/system/debug</span> <span class="nx">JHelperPath</span><span class="o">::</span><span class="na">getAdmin</span><span class="p">(</span><span class="s1">'tpl_isis'</span><span class="p">);</span> <span class="c1">// /var/www/joomla/administrator/templates/isis</span> <span class="nx">JHelperPath</span><span class="o">::</span><span class="na">getAdmin</span><span class="p">(</span><span class="s1">'com_content'</span><span class="p">,</span> <span class="k">true</span><span class="p">);</span> <span class="c1">// components/com_content</span> </pre></div> <p>With this class, controller and model will be able to guess where they located:</p> <div class="highlight highlight-php"><pre><span class="c1">// Set the default view search path</span> <span class="k">if</span> <span class="p">(</span><span class="nb">array_key_exists</span><span class="p">(</span><span class="s1">'table_path'</span><span class="p">,</span> <span class="nv">$config</span><span class="p">))</span> <span class="p">{</span> <span class="nv">$this</span><span class="o">-></span><span class="na">addTablePath</span><span class="p">(</span><span class="nv">$config</span><span class="p">[</span><span class="s1">'table_path'</span><span class="p">]);</span> <span class="p">}</span> <span class="c1">// Guess component position</span> <span class="k">elseif</span> <span class="p">(</span><span class="o">!</span><span class="k">empty</span><span class="p">(</span><span class="nv">$this</span><span class="o">-></span><span class="na">option</span><span class="p">))</span> <span class="p">{</span> <span class="nv">$this</span><span class="o">-></span><span class="na">addTablePath</span><span class="p">(</span><span class="nx">JHelperPath</span><span class="o">::</span><span class="na">get</span><span class="p">(</span><span class="nv">$this</span><span class="o">-></span><span class="na">option</span><span class="p">)</span> <span class="o">.</span> <span class="s1">'/tables'</span><span class="p">);</span> <span class="nv">$this</span><span class="o">-></span><span class="na">addTablePath</span><span class="p">(</span><span class="nx">JHelperPath</span><span class="o">::</span><span class="na">get</span><span class="p">(</span><span class="nv">$this</span><span class="o">-></span><span class="na">option</span><span class="p">)</span> <span class="o">.</span> <span class="s1">'/table'</span><span class="p">);</span> <span class="p">}</span> <span class="c1">// Use constant</span> <span class="k">elseif</span> <span class="p">(</span><span class="nb">defined</span><span class="p">(</span><span class="s1">'JPATH_COMPONENT_ADMINISTRATOR'</span><span class="p">))</span> <span class="p">{</span> <span class="nv">$this</span><span class="o">-></span><span class="na">addTablePath</span><span class="p">(</span><span class="nx">JPATH_COMPONENT_ADMINISTRATOR</span> <span class="o">.</span> <span class="s1">'/tables'</span><span class="p">);</span> <span class="nv">$this</span><span class="o">-></span><span class="na">addTablePath</span><span class="p">(</span><span class="nx">JPATH_COMPONENT_ADMINISTRATOR</span> <span class="o">.</span> <span class="s1">'/table'</span><span class="p">);</span> <span class="p">}</span> </pre></div> | ⇒ | <h2>Introduction</h2> <p>When we have to get a component path, we use <code>JPATH_COMPONENT</code> now. But this constant is dynamic, we can't get a component path in other component. And if we get Model of a component, we need add some path first:</p> <div class="highlight highlight-php"><pre><span class="nx">JForm</span><span class="o">::</span><span class="na">addFormPath</span><span class="p">(</span> <span class="nx">COMPONENT_PATH</span> <span class="o">.</span> <span class="s1">'/models/forms'</span><span class="p">);</span> <span class="nx">JForm</span><span class="o">::</span><span class="na">addFieldPath</span><span class="p">(</span> <span class="nx">COMPONENT_PATH</span> <span class="o">.</span> <span class="s1">'/models/fields'</span> <span class="p">);</span> <span class="nv">$model</span><span class="o">-></span><span class="na">addTablePath</span><span class="p">(</span><span class="nx">COMPONENT_PATH</span> <span class="o">.</span> <span class="s1">'/tables'</span><span class="p">);</span> <span class="nv">$model</span><span class="o">-></span><span class="na">getForm</span><span class="p">();</span> <span class="nv">$model</span><span class="o">-></span><span class="na">getTable</span><span class="p">();</span> </pre></div> <p>Model cannot guess path they located.</p> <h2>The new PathHelper</h2> <p>Now we can use <code>JHelperPath</code> to get extension path, for example:</p> <div class="highlight highlight-php"><pre><span class="nx">JHelperPath</span><span class="o">::</span><span class="na">get</span><span class="p">(</span><span class="s1">'com_content'</span><span class="p">);</span> <span class="c1">// /var/www/joomla/components/com_content</span> <span class="nx">JHelperPath</span><span class="o">::</span><span class="na">get</span><span class="p">(</span><span class="s1">'mod_latest_news'</span><span class="p">,</span> <span class="s1">'site'</span><span class="p">);</span> <span class="c1">// /var/www/joomla/modules/mod_latest_news</span> <span class="nx">JHelperPath</span><span class="o">::</span><span class="na">get</span><span class="p">(</span><span class="s1">'plg_system_debug'</span><span class="p">);</span> <span class="c1">// /var/www/joomla/plugins/system/debug</span> <span class="nx">JHelperPath</span><span class="o">::</span><span class="na">getAdmin</span><span class="p">(</span><span class="s1">'tpl_isis'</span><span class="p">);</span> <span class="c1">// /var/www/joomla/administrator/templates/isis</span> <span class="nx">JHelperPath</span><span class="o">::</span><span class="na">getAdmin</span><span class="p">(</span><span class="s1">'com_content'</span><span class="p">,</span> <span class="k">true</span><span class="p">);</span> <span class="c1">// components/com_content (Relative path)</span> </pre></div> <p>With this class, controller and model will be able to guess where they located:</p> <div class="highlight highlight-php"><pre><span class="c1">// Set the default view search path</span> <span class="k">if</span> <span class="p">(</span><span class="nb">array_key_exists</span><span class="p">(</span><span class="s1">'table_path'</span><span class="p">,</span> <span class="nv">$config</span><span class="p">))</span> <span class="p">{</span> <span class="nv">$this</span><span class="o">-></span><span class="na">addTablePath</span><span class="p">(</span><span class="nv">$config</span><span class="p">[</span><span class="s1">'table_path'</span><span class="p">]);</span> <span class="p">}</span> <span class="c1">// Guess component position</span> <span class="k">elseif</span> <span class="p">(</span><span class="o">!</span><span class="k">empty</span><span class="p">(</span><span class="nv">$this</span><span class="o">-></span><span class="na">option</span><span class="p">))</span> <span class="p">{</span> <span class="nv">$this</span><span class="o">-></span><span class="na">addTablePath</span><span class="p">(</span><span class="nx">JHelperPath</span><span class="o">::</span><span class="na">get</span><span class="p">(</span><span class="nv">$this</span><span class="o">-></span><span class="na">option</span><span class="p">)</span> <span class="o">.</span> <span class="s1">'/tables'</span><span class="p">);</span> <span class="nv">$this</span><span class="o">-></span><span class="na">addTablePath</span><span class="p">(</span><span class="nx">JHelperPath</span><span class="o">::</span><span class="na">get</span><span class="p">(</span><span class="nv">$this</span><span class="o">-></span><span class="na">option</span><span class="p">)</span> <span class="o">.</span> <span class="s1">'/table'</span><span class="p">);</span> <span class="p">}</span> <span class="c1">// Use constant</span> <span class="k">elseif</span> <span class="p">(</span><span class="nb">defined</span><span class="p">(</span><span class="s1">'JPATH_COMPONENT_ADMINISTRATOR'</span><span class="p">))</span> <span class="p">{</span> <span class="nv">$this</span><span class="o">-></span><span class="na">addTablePath</span><span class="p">(</span><span class="nx">JPATH_COMPONENT_ADMINISTRATOR</span> <span class="o">.</span> <span class="s1">'/tables'</span><span class="p">);</span> <span class="nv">$this</span><span class="o">-></span><span class="na">addTablePath</span><span class="p">(</span><span class="nx">JPATH_COMPONENT_ADMINISTRATOR</span> <span class="o">.</span> <span class="s1">'/table'</span><span class="p">);</span> <span class="p">}</span> </pre></div> |
Labels |
Added:
?
|
Why add such a helper and not just instead write the path into the component itself? It doesn't do anything beside creating the path going from JPATH_SITE
and JPATH_ADMINISTRATOR
.
And the problem (if I understand it correctly) only really applies to com_content
. I don't think modules, plugins, templates or even other components than com_content are used from other extensions.
Or do you have a usecase?
I know of developers hooked into com_config
. So, it's possible there are folks out there doing other stuff. The Smart Search (com_finder
) libraries are all under JPATH_COMPONENT_ADMINISTRATOR . '/helpers/indexer
instead of the /libraries
tree; if someone wanted to do something advanced with that code, they'd need to hook in with direct includes.
This code really looks like it's for advanced use cases more than your day to day activity with the core CMS. Though, I could see it replacing the JPATH_COMPONENT
constants, which are really annoying if you're trying to reuse other component's code by extending classes. On the plus, it doesn't require you to explicitly remember the file paths for every possible extension (granted, they aren't that difficult), and it's code that's usable regardless of location so it returns constant results (ever get burned by using JPATH_BASE
versus JPATH_ROOT
?).
The one thing I'd like to see is unit tests as this is a library class, otherwise I'm not against the concept of it.
What would speak against getting rid of the uses of JPATH_COMPONENT
and just use JPATH_BASE . 'components/com_whatever'
directly? I never really understood the purpose but use it just out of lazyness
Ahh, but are you retrieving JPATH_SITE . '/components/com_whatever'
or JPATH_ADMINISTRATOR . '/components/com_whatever'
with your case? This is why JPATH_BASE
is dangerous too
I provide some example code to describe the usecase. Our JModelForm
provides a getForm
interface:
protected function loadForm($name, $source = null, $options = array(), $clear = false, $xpath = false)
{
// ...
// Get the form.
JForm::addFormPath(JPATH_COMPONENT . '/models/forms');
JForm::addFieldPath(JPATH_COMPONENT . '/models/fields');
// ...
}
But it only works in com_users, if 3rd developers (or a new core extension) want to create a registration module, includes the form.xml from com_users, and if in other components page, module cannot use JPATH_COMPONENT
, they have to do:
JModelLegacy::addIncludePath(JPATH_SITE . '/components/com_users/models');
$model = JModelLegacy::getInstance('Register', 'UsersModel', array('ignore_request', true));
JForm::addFormPath(JPATH_SITE . '/components/com_users/models/forms');
JForm::addFieldPath(JPATH_SITE . '/components/com_users/models/fields');
$form = $model->getForm();
But if we change the JModelForm
:
protected function loadForm($name, $source = null, $options = array(), $clear = false, $xpath = false)
{
// ...
// Get the form.
if (!empty($this->option))
{
JForm::addFormPath(JHelperPath::get($this->option) . '/models/forms');
JForm::addFieldPath(JHelperPath::get($this->option) . '/models/fields');
}
else
{
JForm::addFormPath(JPATH_COMPONENT . '/models/forms');
JForm::addFieldPath(JPATH_COMPONENT . '/models/fields');
}
// ...
}
And JModelLegacy
:
public static function getInstance($type, $prefix = '', $config = array())
{
$option = str_replace('model', '', strtolower($prefix));
self::addIncludePath(JHelperPath::get(com_ . $option) . '/models');
}
Then we just need this code in module:
$model = JModelLegacy::getInstance('Register', 'UsersModel', array('ignore_request', true));
$form = $model->getForm();
And we won't adding too many no use paths to global JForm class.
This class is not for use directly by developers, it can help our base MVC classes guessing where they located. There are not many usecase in CMS core extensions now, but this class just like JHelperRoute
, provides a universal interface to let developers think how they can use it to do what they want. It more like an ExtensionPathResolver
.
Another benefit is that if every extensions use this class to locate itself or other extensions, if one day we change components
path to component
in CMS n.0, just change JHelperPath
and everything still peaceful in our life.
Title |
|
||||||
Labels |
Added:
?
|
Test ok but the php 5.3 case return this error in Travis:
fatal: unable to connect to github.com:
github.com[0: 192.30.252.128]: errno=Connection timed out
The command "git fetch origin +refs/pull/2753/merge:" failed and exited with 128 during checkout.
Your build has been stopped.
All's well. I just reset the job and it passed. One of those occasional Travis hiccups.
@mbabker This is not about site vs administrator as far as I understand it. There are already specific constants (JPATH_COMPONENT_ADMINISTRATOR
and JPATH_COMPONENT_SITE
) to solve this particular issue.
@asika32764 Thanks for the explanations, I now see how you want to do it.
However I think it would be a backward compatibility break for extensions which provide their own forms and fields while using the model from another component. That's something which is possible today and would be harder (or even no longer possible?) with your change. Anyway it would break those extensions as the model would load the form and fields from com_content and not from com_whatever.
JPATH_COMPONENT
in any form is inflexible. It's dynamically set based on the request and what component you've requested. A constant should be, well, consistent. JPATH_BASE
is flawed because it is the base of the requested application. If you're in the site app, JPATH_BASE
= JPATH_SITE
, and in admin it is JPATH_ADMINISTRATOR
.
Say you're doing this in WhateverModelFoo
(JPATH_COMPONENT_ADMINISTRATOR . '/models/foo.php'
):
// Need our helper class
JLoader::register('WhateverHelper', JPATH_COMPONENT_ADMINISTRATOR . '/helpers/helper.php');
class WhateverModelFoo extends JModelAdmin {}
Then you have a com_something and you've decided to hook it into com_whatever and extend that WhateverModelFoo
class:
// Our autoloader still can't load component classes, yay
JLoader::register('WhateverModelFoo', JPATH_ADMINISTRATOR . '/components/com_whatever/models/foo.php');
class SomethingModelThing extends WhateverModelFoo {}
You're gonna get one of two errors:
WhateverHelper
not found at <root>/administrator/components/com_something/helpers/helper.php
(if you have a SomethingHelper
at that file path)<root>/administrator/components/com_something/helpers/helper.php
There really isn't a B/C break using JHelperPath::get(JFactory::getApplication()->input->getCmd('option'));
over the code currently in place in JModelAdmin::loadForm() as it is using the JPATH_COMPONENT
constant already. By default, both are going to load whatever the component being requested via the current URL is. With that said, if someone is in a component explicitly loading their forms and fields within their component using something like JForm::addFormPath(__DIR__ . '/forms');
then that can't be helped.
About the BC, we can just keep this class in library, and wait for CMS slowly update, we don't need change everything immediately. Maybe in 3.4, 3.5 or 4.0
However, I think it won't break BC if we do this way:
protected function loadForm($name, $source = null, $options = array(), $clear = false, $xpath = false)
{
// ...
// Keep original code as first that for backward compatibility
JForm::addFormPath(JPATH_COMPONENT . '/models/forms');
JForm::addFieldPath(JPATH_COMPONENT . '/models/fields');
// Add new auto finding path code after it to make sure the old path first.
if (!empty($this->option))
{
JForm::addFormPath(JHelperPath::get($this->option) . '/models/forms');
JForm::addFieldPath(JHelperPath::get($this->option) . '/models/fields');
}
// ...
}
I'll find time to test it.
It depends where $this->option is coming from. JModelLegacy guesses this from the model name, thus it would change the behavior and could potentially break other extensions.
If you want to be full BC, you need to get the option from the request and not from the model name. As this is how it currently works with JPATH_COMPONENT.
The first part with JPATH_COMPONENT
will get option from request, and the second part will get option by Model name. These two path will all added to JForm and the JPATH_COMPONENT
is priority over $this->option
so that I think it will not a big problem if JForm will find files from first path?
I'm testing this code in a new branch:
https://github.com/asika32764/joomla-cms/tree/helper-path-test-JModelForm
It isn't any page broken in admin and front-end, I'm still testing every pages.
The first part with JPATH_COMPONENT will get option from request, and the second part will get option by Model name. These two path will all added to JForm and the JPATH_COMPONENT is priority over $this->option so that I think it will not a big problem if JForm will find files from first path?
Ah true, that should work.
Labels |
Added:
?
|
Status | New | ⇒ | Pending |
Labels |
Removed:
?
|
Category | ⇒ | Libraries |
I like the concept on this PR but thinking in the future what we really need is a JExtension
class used like:
$component = JExtension::getInstance('com_content');
$params = $component->getParams();
$path = $component->getPath();
JExtension::getInstance()
would return a JExtensionComponent
instance that implements JExtensionInterface
.
This way we could start migrating from JLibraryHelper
, JPluginHelper
, JComponentHelper
to the new classes.
The problem here is that some extensions require things like frontend
/backend
so we maybe should pass an $options
array that would be used:
$path = $component->getPath(array('client' => 1, 'relative' => true));
Not sure if I'm totally off from the PR goal
This is also a good idea, it's similar to ExtensionHelper
which implemented in my RAD framework: https://github.com/ventoviro/windwalker-joomla-rad/blob/staging/src/System/ExtensionHelper.php#L102-L130
ExtensionHelper::getParams('com_content');
ExtensionHelper::getParams('mod_module'); // Not yet support client
ExtensionHelper::getParams('plg_folder_plugin');
I think we should have a service locator pattern to locate our extensions. So I try to create this Path Helper. If we need a JExtension
object, Path Helper can help this object parsing extension element.
Example
class JExtension
{
protected static $instances;
public static function getInstance($element, $client = 0)
{
$extracted = JHelperPath::extractElement($element);
if (!empty(static::$instances[$extracted['name']]))
{
return static::$instances[$extracted['name']];
}
$class = 'JExtension' . ucfirst($extracted['ext']);
$instance = new $class($extracted['name'], $client, $extracted['group']);
return static::$instances[$extracted['name']] = $instance;
}
}
Or maybe rename JHelperPath to JExtensionHelper to support JExtension.
What do you think?
I think I'd go for the JExtensionHelper
to support JExtension
. For now we can use it as you have it and in the future modify it to add there the logic to use JExtension
. So it would be B/C
But off course feel free to implement JExtension
itself
For another concept of JExtensionComponent, we can look Symfony Bundle class, it provides an entry of every bundle that help us do something about this bundle.
I also implemented a Component class for my RAD:
https://github.com/ventoviro/windwalker-joomla-rad/blob/staging/src/Component/Component.php
$component->getPath() :
https://github.com/ventoviro/windwalker-joomla-rad/blob/staging/src/Component/Component.php#L429
Usage:
(new Component($input, $app, $container))->execute();
Maybe in Joomla:
JExtension::getInstance('com_content', $input, $app, $container)->execute();
I think it can be a concept of our component (or any extension) entry.
So, if I have time, I'll try to implement JExtension, thank you for providing this idea.
Looks very promising. I like it.
Thank you very much for working on it.
Title |
|
Title |
|
I like this too. @phproberto do you want this to wait until we can flesh out some of the ideas discussed here for 3.5? Or shall we get this merged in (as JExtensionHelper) for 3.4?
I would like to add a few considerations / simplifications:
getAdmin
is removed and replaced with something that does not enforce force the "admin" and "site" conventions. Ultimately when extensions become contained in a single place, the "admin" or "site" distinction will become a theme issue.
ExtensionHelper
be expanded to serve as the single workhorse for all object lookup, maybe under a different name and not as a "helper". This would be used to retrieve both a working path or file path.
/*
Returns /components/com_content/
*/
JDiscovery::get( 'component/com_content' );
/*
Returns /administrator/components/com_content/
*/
JDiscovery::get( 'component/com_content-admin' );
JDiscovery::get( 'component/com_content', array('theme' => 'admin') );
/*
Lookup
/components/com_content/views/article/tmpl/default.php
/templates/active-template/html/com_content/article/default.php
*/
JDiscovery::get( 'component/com_content/template/article' );
/*
Lookup
/media/com_banners/js/banner.js
/templates/active-template/media/com_banners/js/banner.js
*/
JDiscovery::get( 'component/com_banners/media/js/banner' );
The get
method would return the first result from getAll
The get
method accept a path and parameter set, allowing for future expansion
function get( $rule, $params = array() ){ ... }
/extensions/com_content/
/extensions/com_content/themes/admin
/extensions/com_content/themes/admin/controllers
/extensions/com_content/themes/admin/media
/extensions/com_content/themes/admin/views
/extensions/com_content/themes/admin/templates
or
/vendors/joomla/com_content/...
/vendors/joomla/tpl_prostar/...
with base level overrides still present
/media/...
/templates/...
Since @Kubik-Rubik called my name, I am going to reply :) I have read the whole thing and if I understood correctly, this is supposed to be a class added to Joomla! but not going to be used by Joomla itself, correct? @asika32764 Would it be JExtension or JHelperPath to be added?
I think it will be a helpful class in J! 3.5 for developers, if Joomla core want to use this class, there will be many parts of components need to refactor.
About JExtension, it will be a BIG work, I think it will not so quickly to create.
@asika32764 I am going to take this up with the PLT to come to a decision whether or not we want this included. We can then either close the PR if not accepted or work on it to make it ready.
Thank you.
If PLT not accept, just close it. If you think it is a good idea but needs more work, feel free to take my code to modify.
@asika32764 Thank you for contribution. The PLT has decided not to include it, so I will close it. However I will tag @wilsonge as he thinks it might have some use for a future release. So George, keep it on your list somewhere :)
Status | Pending | ⇒ | Closed |
Closed_Date | 0000-00-00 00:00:00 | ⇒ | 2015-10-21 15:02:52 |
Closed_By | ⇒ | roland-d |
Title |
|
Title |
|
||||||
Labels |
Added:
?
|
JTracker: http://joomlacode.org/gf/project/joomla/tracker/?action=TrackerItemEdit&tracker_item_id=33076&start=0