Core API for building internal Links

Discussions and requests related to new CMSimple features, plugins, templates etc. and how to develop.
Please don't ask for support at this forums!
Post Reply
cmb
Posts: 13273
Joined: Tue Jun 21, 2011 11:04 am
Location: Mü-Sa, RLP, DE
Contact:

Core API for building internal Links

Post by cmb » Fri May 17, 2013 10:38 pm

Hello Community,

I strongly suggest we introduce a core API for building links. Of course it's not hard to build these links manually, but I'm thinking about clean URLs (a feature that's not stricly necessary, but is desired once in a while). The solution I've came up with seems to work fine, but plugins which build internal links will mostly not conform. Currently the only way to comply is to use function a(); but this is somewhat limited, as it only allows <a> tags to be build, but often it's necessary to have the URL only (e.g. for form actions and AJAX requests).

Another benefit of having such an API would be regarding the $s problem. As soon as we're having an appropriate API a plugin developer could use it, without being forced to rewrite the plugin, when we might make incompatible changes on how URLs are handled by CMSimple_XH.

Furthermore a well designed API may clean up the code of extensions a bit.

I have not yet any specific suggestion about this API, but perhaps somebody else has. I'm looking forward to hear your ideas and comments.

Christoph
Christoph M. Becker – Plugins for CMSimple_XH

cmb
Posts: 13273
Joined: Tue Jun 21, 2011 11:04 am
Location: Mü-Sa, RLP, DE
Contact:

Re: Core API for building internal Links

Post by cmb » Sun Jun 23, 2013 2:46 pm

Hello Community,

based on my suggestion in http://cmsimpleforum.com/viewtopic.php? ... 873#p35873, I have written the following function:

Code: Select all

/**
 * Returns a relative URI to a CMSimple page.
 *
 * None of the parameters given to this function must be URL encoded.
 *
 * @param mixed $page       <var>true</var> means the current page; an integer
 *                          value means a page index; a string is the name of
 *                          a special page; an array means the "path" of a special
 *                          subpage; <var>false</var> means no page at all.
 * @param array $parameters An associative array with parameter names/values.
 *                          Numericly indexed elements are treated as names.
 *
 * @return string
 */
function XH_url($page = true, $parameters = array())
{
    global $u, $su, $sn, $cf;

    $params = array();
    if ($page === true) { // current page
        $params[] = $su;
    } elseif ($page === false) { // no page
        $params[] = '';
    } elseif (is_int($page)) { // a numeric page index
        $params[] = $u[$page];
    } elseif (is_array($page)) { // a subpage with its ancestors
        if ($page[0] == 'xhcore') {
            array_shift($page);
            $allowed = array(
                'files', 'logout', 'settings', 'sysinfo', 'validate', 'xhpages'
            );
            if (in_array($page[0], $allowed)) {
                if (in_array($page[0], array('files'))) {
                    $allowed = array('downloads', 'images', 'media', 'userfiles');
                    if (in_array($page[1], $allowed)) {
                        array_shift($page);
                        array_unshift($parameters, array_shift($page));
                    } else {
                        trigger_error('special page not permitted', E_USER_WARNING);
                    }
                } else {
                    array_unshift($parameters, array_shift($page));
                }
                if (!empty($page)) {
                    trigger_error('special page not permitted', E_USER_WARNING);
                }
            } else {
                trigger_error('special page not permitted', E_USER_WARNING);
            }
        } elseif ($page[0] == 'xhplugin') {
            // TODO: check if plugin $page[1] is installed
            array_shift($page);
            array_unshift($parameters, array_shift($page));
            // assert $page is empty?
        } else {
            // special page of some plugin
        }
        $pages = array_map('urlencode', $page);
        $params[] = implode($cf['uri']['seperator'], $pages);
    } else { // a named special page
        $defined = array('login', 'mailform', 'search', 'sitemap');
        if (!in_array($page, $defined)) {
            // special page of some plugin
        }
        $params[] = uenc($page);
    }
    foreach ($parameters as $name => $value) {
        if (is_int($name)) {
            $params[] = urlencode($value);
        } else {
            $params[] = urlencode($name) . '=' . urlencode($value);
        }
    }
    $queryString = implode('&', $params);
    $url = $sn . '?' . $queryString;
    return $url;
}
If this function is used throughout the core and by all plugins to build all site internal URLs, we are able to switch to a cleaner URL handling in the future. The basic idea is to replace the implementation of the function with something that suits the change.

The API of the function is quite complicated, but for plugins usually the following should do:

Code: Select all

XH_url(); // URL to current page
XH_url(17); // URL to page no. 17
XH_url(true, array('param1' => 'value1', 'print')); // URL to current page with two query parameters (the latter triggering print mode)        
The function can also be used for the action attribute of forms:

Code: Select all

$o = '<form action="' . XH_url() . '" method="post">'; // POST form which requests the current page on submit
$o = '<form action="' . XH_url(false) . '" method="get"><input type="hidden" name="selected" value="' . $su . '"'; // GET form which requests the current page on submit       
As the submission of a GET form ignores the query string of the action attribute, a input with the name selected has to be used; the same technique can be applied to POST forms, if one prefers.

Note, that the function returns the URL urlencoded, but not HTML entity escaped (you would have to call htmlspecialchars() before emitting it to the (X)HTML).

It seems reasonable to augment the function with an optional third parameter $absolute which defaults to false, to be able to get an absolute URL, if necessary.

Any comments about potential shortcommings and suggestions on further improvements are welcome. Of course, I'm curious, if plugin developers would agree to use such a function to build site internal URLs.

Christoph
Christoph M. Becker – Plugins for CMSimple_XH

Post Reply