Permalinks Change?

By Perchpole - August 16, 2016 - edited: August 16, 2016

Hello, All -

Has there been a change to the way Permalinks work? I ask because since updating to CMSB v3+ (and updated plugins) the functionality of one of my scripts appears to be broken.

I had used the following code suggested by Greg to update the Permalinks database:

......
$colsToValues['permalink']        = "newpage";
$_REQUEST['permalink']            = $colsToValues['permalink'];
  
$newRecordNum = mysql_insert($tablename, $colsToValues, true);
  
$isNewRecord = true;
$oldRecord   = array();
  
$GLOBALS['escapedTableName'] = mysql_escape( $TABLE_PREFIX . $tablename );
$GLOBALS['schema']           = loadSchema($tablename); // fake currently loaded schema
if (!$GLOBALS['schema']) { die("Invalid table name '" .htmlencode($tablename). "'"); }
include_once(SCRIPT_DIR . '/lib/menus/default/common.php'); // load category menu functions 
updateCategoryMetadata();
  
mysql_update($tableName, $newRecordNum, null, array('permalink' => $_REQUEST['permalink']));
permalink_cms_onSaveUpdatePermalinks($tableName, $isNewRecord, $oldRecord, $newRecordNum);

The issue was originally discussed in this topic:

https://www.interactivetools.com/forum/forum-posts.php?page=1&Front-End-Editing-and-Permalinks-79656

I'm not sure what has changed - but it isn't working anymore.

:0/

Perch

Note:

I've just noticed I am also receiving an error message when trying to save:

E_ERROR: Call to undefined function permalink_cms_onSaveUpdatePermalinks()

Hope this helps!

:0/

Perch

By Daryl - August 16, 2016 - edited: August 16, 2016

Hi Perch,

It seems like permalink_cms_onSaveUpdatePermalinks function is not getting loaded on your page anymore.

I checked the code and the permalink_cms_onSaveUpdatePermalinks() function has been moved to /cmsb/plugins/permalinks/permalinks_cmsMenus.php since version 1.03.

And the permalinks_cmsMenus.php is only called if the logged in user is an admin.

To resolve this, you can either:

  1. Include the permalinks_cmsMenus.php file in your add/edit page (not recommended because the functions inside this file are designed to be used in CMS admin pages)
  2. Use permalink_add() function which returns an error if there is any. Ie: permalink is already in use

    // add/update permalink
    $errors = permalink_add(@$_REQUEST['permalink'], $tableName, $recordNum);

Let me know how it goes!

UPDATE:

The permalink_add() doesn't do the permalink error checking, so it's better to include permalinks_cmsMenus.php file and use permalink_cms_onSaveUpdatePermalinks() function.

Thanks,

Daryl Maximo
PHP Programmer - interactivetools.com

Yes!

Thanks, Daryl. It works. My new code looks like this. Have I added the new line correctly?

......
$GLOBALS['escapedTableName'] = mysql_escape( $TABLE_PREFIX . $tablename );
$GLOBALS['schema']           = loadSchema($tablename); // fake currently loaded schema
if (!$GLOBALS['schema']) { die("Invalid table name '" .htmlencode($tablename). "'"); }
include_once(SCRIPT_DIR . '/lib/menus/default/common.php'); // load category menu functions
include_once('/path/to/htdocs/cmsb/plugins/permalinks/permalinks_cmsMenus.php');  
updateCategoryMetadata();
  
mysql_update($tableName, $newRecordNum, null, array('permalink' => $_REQUEST['permalink']));
permalink_cms_onSaveUpdatePermalinks($tableName, $isNewRecord, $oldRecord, $newRecordNum);

Just 2 questions:

The way the savePage works is that the Permalink is created automatically from the name of the page. Every time the page is saved the script checks to see if the name has changed. If so, the script creates a new permalink derived from the name.

In your reply you mentioned error checking. Currently my savePage does not "report" errors. (If there is a code problem it just doesn't work!). In the case of Permalinks, however, it could be useful to see the error reported. So question one is:

1) In the unlikely event that there is a clash of permalinks, how would I feed the error back to my savePage - without breaking the anything (and losing the changes that could have been made to the page)?

Alternatively, I could make life a lot simpler by removing the automatic permalink creation step all together. Instead I could provide an input box on the savePage and invite users to submit their own choice of permalink. However, I know that the following code isn't enough to save a permalink:

......
$colsToValues['permalink'] = @$_REQUEST['permalink'];
mysql_update($tablename, $recordNum, $where, $colsToValues);

To save permalinks I need to run a second update with something like this:

mysql_update($tablename, $recordNum, null, array('permalink' => $_REQUEST['permalink']));
permalink_cms_onSaveUpdatePermalinks($tablename, $isNewRecord, $oldRecord, $recordNum);

So, my second question is:

2) What code would I use to check if a new permalink has been added to an input box and, if so, then update the required tables?

Any insight you can give me would be much appreciated!

Thanks, Daryl.

:0)

Perch

By Daryl - August 17, 2016 - edited: August 17, 2016

Hi Perch,

Have I added the new line correctly?

Looks good!

Currently my savePage does not "report" errors. (If there is a code problem it just doesn't work!)

1) In the unlikely event that there is a clash of permalinks, how would I feed the error back to my savePage - without breaking the anything (and losing the changes that could have been made to the page)?

The permalink_cms_onSaveUpdatePermalinks() function doesn't return the error message and instead, calls the die($error) function if there's an error.
So if you want to control the error reporting, you can copy the code and create your own version of this function that returns the error messages instead of dying. See the second step below.

2) What code would I use to check if a new permalink has been added to an input box and, if so, then update the required tables?

First, convert the input box value to SEO-friendly URL (converts spaces to dashes, removes non-alphanumeric characters):

$permalink = _permalink_generate_formatInputText($_REQUEST['permalink_input_box']);

Second, check $permalink for errors. The code below is derived from permalink_cms_onSaveErrorChecking() function (you can convert the code below to you own function):

// check if permalink already exists in database
$permalinkRecord = mysql_get('_permalinks', null, array('permalink' => $permalink)); 
$isAlreadyUsed   = $permalinkRecord && $permalinkRecord['recordNum'] != $recordNum && ($tableName != '_permalinks');

// error checking
$errors = '';
$requiredPrefix  = $GLOBALS['schema']['permalink']['defaultValue'];
if     (!$permalink)                              { $errors .= "You must enter a value for 'permalink'!"; }
elseif (preg_match("!^/!", $requiredPrefix))      { $errors .= "Permalink prefix can not start with a slash! Change the 'Default Value' for permalink field in Field Editor."; }
elseif ($permalink == $requiredPrefix)            { $errors .= "You must enter a value for 'permalink' after '" .htmlencode($requiredPrefix). "'!"; }
elseif (!startsWith($requiredPrefix, $permalink)) { $errors .= "Permalink must start with '" .htmlencode($requiredPrefix). "'!"; }
elseif (preg_match("|[^\w\-\./]|i", $permalink))  { $errors .= "Permalink can only contain these chars (a-z0-9/-.)!"; }
elseif (preg_match("!^/|/$!", $permalink))        { $errors .= "Permalink can not start or end with a slash!"; }
elseif (preg_match("!\.{2,}!", $permalink))       { $errors .= "Permalink can not include '..'!"; }
elseif (preg_match("!/{2,}!", $permalink))        { $errors .= "Permalink can not include '//'!"; }
elseif ($isAlreadyUsed)                           { $errors .= sprintf("Permalink '%s' is already in use, please choose another!", htmlencode($permalink)); }

Next, if there's no error, add/update the permalink of the record.
The permalink_add() function will take care of adding/updating the new permalink and will return an error if there is any.
If it doesn't return an error, you can continue with your other process, ie: update record.

if (!$errors) {
  // add/update permalink
  $errors = permalink_add($permalink, $tableName, $recordNum);
  
  if (!$error) {
    // do other stuff here, ie: update record
  }
}

Or you can do your other process first then add/update the permalink.

Finally, if there's an error, display it on your page.

NOTE: The code above are just examples and were not tested so you might need to tweak it more.

Let me know any questions!

Thanks,

Daryl Maximo
PHP Programmer - interactivetools.com

By Djulia - August 17, 2016

Hi Daryl,

I allow myself to introduce a question here.

It would be possible to exclude the 'old' link in functions permalink_cms_onSaveUpdatePermalinks and permalink_add ?

It would be then possible to again use a link without having to remove it before in permalinks table.

Thanks!

Djulia

By Daryl - August 18, 2016

Hi Djulia.

It would be possible to exclude the 'old' link in functions permalink_cms_onSaveUpdatePermalinks and permalink_add ?

It would be then possible to again use a link without having to remove it before in permalinks table.

You would like to use permalink URL that is already used (old) by another record without having to delete it from the permalink table?

I wouldn't recommend doing it because we're using the old permalink to redirect the users to the current one when they visit the old URL. 
This is helpful for users who bookmarked a URL with an old permalink or when search engines or browsers cached the page's old URL, users will still get redirected to the correct page.

But if you don't mind the old permalinks, you can delete the old permalink record instead of updating it as an old record whenever you add a new one.
To do that, in permalink_add() function, change the block of code with comment "mark all other matching permalinks for this record as old" from mysql update to mysql delete.

I never tried this before and don't know what other negative effects it could cause so I'd like to suggest to do lots of testing first and see if the benefits of not checking the old permalink outweigh the negative effects of it.

Hope that helps!

Cheers,

Daryl Maximo
PHP Programmer - interactivetools.com