From 03ee3226a7946d8e277066ae77ade8bc685adde4 Mon Sep 17 00:00:00 2001 From: Emmanuel Prochasson Date: Fri, 18 Jan 2013 12:47:59 +0800 Subject: [PATCH 1/3] Add 404 logging + Regexp matching for redirection rules possibility. Popped the version number. --- _build/build.transport.php | 9 +- _build/data/transport.plugins.php | 25 ---- _build/resolvers/resolve.tables.php | 1 + .../redirector/js/mgr/widgets/home.panel.js | 10 ++ .../js/mgr/widgets/pagenotfound.grid.js | 138 ++++++++++++++++++ .../js/mgr/widgets/redirects.grid.js | 103 +++++++++++-- .../redirector/controllers/mgr/index.php | 1 + core/components/redirector/docs/changelog.txt | 6 + core/components/redirector/docs/readme.txt | 13 +- .../elements/plugins/plugin.redirector.php | 72 ++++----- .../redirector/lexicon/de/default.inc.php | 12 +- .../redirector/lexicon/en/default.inc.php | 11 ++ .../redirector/lexicon/fr/default.inc.php | 13 +- .../redirector/lexicon/nl/default.inc.php | 12 +- .../redirector/lexicon/ru/default.inc.php | 12 +- .../model/redirector/metadata.mysql.php | 9 ++ .../model/redirector/metadata.sqlsrv.php | 8 + .../model/redirector/modredirect.class.php | 15 +- .../modredirectpagenotfound.class.php | 13 ++ .../redirector/mysql/modredirect.class.php | 10 +- .../redirector/mysql/modredirect.map.inc.php | 21 +++ .../mysql/modredirectpagenotfound.class.php | 14 ++ .../mysql/modredirectpagenotfound.map.inc.php | 59 ++++++++ .../model/redirector/redirector.class.php | 109 +++++++++++++- .../redirector/sqlsrv/modredirect.map.inc.php | 2 + .../model/schema/redirector.mysql.schema.xml | 12 +- .../processors/mgr/errors/getlist.php | 56 +++++++ .../processors/mgr/errors/remove.php | 17 +++ .../processors/mgr/errors/update.php | 26 ++++ .../processors/mgr/redirect/getlist.php | 6 +- .../processors/mgr/redirect/sort.php | 15 ++ .../processors/mgr/redirect/update.php | 6 + 32 files changed, 743 insertions(+), 93 deletions(-) delete mode 100644 _build/data/transport.plugins.php create mode 100644 assets/components/redirector/js/mgr/widgets/pagenotfound.grid.js create mode 100644 core/components/redirector/model/redirector/metadata.mysql.php create mode 100644 core/components/redirector/model/redirector/metadata.sqlsrv.php create mode 100644 core/components/redirector/model/redirector/modredirectpagenotfound.class.php create mode 100644 core/components/redirector/model/redirector/mysql/modredirectpagenotfound.class.php create mode 100644 core/components/redirector/model/redirector/mysql/modredirectpagenotfound.map.inc.php create mode 100644 core/components/redirector/processors/mgr/errors/getlist.php create mode 100644 core/components/redirector/processors/mgr/errors/remove.php create mode 100644 core/components/redirector/processors/mgr/errors/update.php create mode 100644 core/components/redirector/processors/mgr/redirect/sort.php diff --git a/_build/build.transport.php b/_build/build.transport.php index d1f211b..799d6dd 100644 --- a/_build/build.transport.php +++ b/_build/build.transport.php @@ -14,7 +14,7 @@ /* define package names */ define('PKG_NAME','Redirector'); define('PKG_NAME_LOWER','redirector'); -define('PKG_VERSION','1.0.3'); +define('PKG_VERSION','1.0.4'); define('PKG_RELEASE','pl'); /* define build paths */ @@ -65,6 +65,13 @@ 'priority' => 0, 'propertyset' => 0, ),'',true,true); + $events['OnSiteRefresh'] = $modx->newObject('modPluginEvent'); + $events['OnSiteRefresh']->fromArray(array( + 'event' => 'OnSiteRefresh', + 'priority' => 0, + 'propertyset' => 0, + ),'',true,true); + $plugin->addMany($events); unset($events); diff --git a/_build/data/transport.plugins.php b/_build/data/transport.plugins.php deleted file mode 100644 index 019c8d2..0000000 --- a/_build/data/transport.plugins.php +++ /dev/null @@ -1,25 +0,0 @@ -newObject('modPlugin'); -$plugins[1]->fromArray(array( - 'id' => 1, - 'name' => 'Redirector', - 'description' => 'Handles site redirects.', - 'plugincode' => file_get_contents($sources['elements'].'plugins/plugin.redirector.php'), -),'',true,true); - $events = array(); - $events['OnPageNotFound']= $modx->newObject('modPluginEvent'); - $events['OnPageNotFound']->fromArray(array( - 'event' => 'OnPageNotFound', - 'priority' => 0, - 'propertyset' => 0, - ),'',true,true); - $plugins[1]->addMany($events); - unset($events); - -return $plugins; \ No newline at end of file diff --git a/_build/resolvers/resolve.tables.php b/_build/resolvers/resolve.tables.php index 4769d84..267713b 100644 --- a/_build/resolvers/resolve.tables.php +++ b/_build/resolvers/resolve.tables.php @@ -15,6 +15,7 @@ $manager = $modx->getManager(); $manager->createObjectContainer('modRedirect'); + $manager->createObjectContainer('modRedirectPageNotFound'); break; case xPDOTransport::ACTION_UPGRADE: diff --git a/assets/components/redirector/js/mgr/widgets/home.panel.js b/assets/components/redirector/js/mgr/widgets/home.panel.js index d11ea02..3e5473b 100644 --- a/assets/components/redirector/js/mgr/widgets/home.panel.js +++ b/assets/components/redirector/js/mgr/widgets/home.panel.js @@ -19,6 +19,16 @@ Redi.panel.Home = function(config) { return {activeTab:this.items.indexOf(this.getActiveTab())}; } ,items: [{ + title: _('redirector.errors') + ,defaults: { autoHeight: true } + ,items: [{ + html: '

'+_('redirector.pnfdesc')+'


' + ,border: false + },{ + xtype: 'redirector-grid-pagenotfound' + ,preventRender: true + }] + },{ title: _('redirector.redirects') ,defaults: { autoHeight: true } ,items: [{ diff --git a/assets/components/redirector/js/mgr/widgets/pagenotfound.grid.js b/assets/components/redirector/js/mgr/widgets/pagenotfound.grid.js new file mode 100644 index 0000000..593270e --- /dev/null +++ b/assets/components/redirector/js/mgr/widgets/pagenotfound.grid.js @@ -0,0 +1,138 @@ +Redi.grid.PageNotFound = function(config) { + config = config || {}; + Ext.applyIf(config,{ + id: 'redirector-grid-pagenotfound' + ,url: Redi.config.connector_url + ,baseParams: { action: 'mgr/errors/getList' } + ,fields: ['id','url','times','firsttime','lasttime','menu'] + ,paging: true + ,autosave: true + ,remoteSort: true + ,anchor: '97%' + ,autoExpandColumn: 'url' + ,columns: [{ + header: _('redirector.url') + ,dataIndex: 'url' + ,sortable: false + ,width: 200 + ,editor: { xtype: 'textfield' } + },{ + header: _('redirector.times') + ,dataIndex: 'times' + ,sortable: true + ,width: 40 + ,editor: { xtype: 'textfield' } + },{ + header: _('redirector.firsttime') + ,dataIndex: 'firsttime' + ,sortable: true + ,width: 100 + ,editor: { xtype: 'textfield' } + },{ + header: _('redirector.lasttime') + ,dataIndex: 'lasttime' + ,sortable: true + ,width: 100 + ,editor: { xtype: 'textfield' } + }] + ,tbar: [{ + xtype: 'textfield' + ,id: 'redirector-search-filter' + ,emptyText: _('redirector.search...') + ,listeners: { + 'change': {fn:this.search,scope:this} + ,'render': {fn: function(cmp) { + new Ext.KeyMap(cmp.getEl(), { + key: Ext.EventObject.ENTER + ,fn: function() { + this.fireEvent('change',this.getValue()); + this.blur(); + return true; } + ,scope: cmp + }); + },scope:this} + } + }] + }); + Redi.grid.PageNotFound.superclass.constructor.call(this,config) +}; +Ext.extend(Redi.grid.PageNotFound,MODx.grid.Grid,{ + search: function(tf,nv,ov) { + var s = this.getStore(); + s.baseParams.query = tf.getValue(); + this.getBottomToolbar().changePage(1); + this.refresh(); + } + + ,removeRedirectPageNotFound: function() { + MODx.msg.confirm({ + title: _('redirector.redirect_remove_page_not_found') + ,text: _('redirector.redirect_remove_confirm') + ,url: this.config.url + ,params: { + action: 'mgr/errors/remove' + ,id: this.menu.record.id + } + ,listeners: { + 'success': {fn:this.refresh,scope:this} + } + }); + + } + ,createRedirectPageNotFound: function(btn,e){ + if (!this.createRedirectPNFWindow) { + this.createRedirectPNFWindow = MODx.load({ + xtype: 'redirector-window-redirect-create-from-pnf' + ,record: { + pattern : this.menu.record.url.replace(/^\//, '') + ,target: '' + ,active: 1 + ,isregexp: 0 + } + ,listeners: { + 'success': {fn:this.refresh,scope:this} + } + }); + } + this.createRedirectPNFWindow.setValues(this.menu.record); + this.createRedirectPNFWindow.show(e.target); + } +}); +Ext.reg('redirector-grid-pagenotfound',Redi.grid.PageNotFound); + +// +Redi.window.CreateRedirectPageNotFound = function(config) { + config = config || {}; + Ext.applyIf(config,{ + title: _('redirector.redirect_create') + ,url: Redi.config.connector_url + ,baseParams: { + action: 'mgr/redirect/create' + } + ,fields: [{ + xtype: 'textfield' + ,fieldLabel: _('redirector.pattern') + ,name: 'pattern' + ,width: 300 + },{ + xtype: 'textfield' + ,fieldLabel: _('redirector.target') + ,name: 'target' + ,width: 300 + },{ + xtype: 'checkbox' + ,fieldLabel: _('redirector.active') + ,name: 'active' + ,inputValue: 1 + ,checked: true + },{ + xtype: 'checkbox' + ,fieldLabel: _('redirector.isregexp') + ,name: 'isregexp' + ,inputValue: 1 + }] + }); + Redi.window.CreateRedirectPageNotFound.superclass.constructor.call(this,config); +}; +Ext.extend(Redi.window.CreateRedirectPageNotFound,MODx.Window); +Ext.reg('redirector-window-redirect-create-from-pnf',Redi.window.CreateRedirectPageNotFound); \ No newline at end of file diff --git a/assets/components/redirector/js/mgr/widgets/redirects.grid.js b/assets/components/redirector/js/mgr/widgets/redirects.grid.js index 455f7be..67a2bab 100644 --- a/assets/components/redirector/js/mgr/widgets/redirects.grid.js +++ b/assets/components/redirector/js/mgr/widgets/redirects.grid.js @@ -3,35 +3,53 @@ Redi.grid.Redirects = function(config) { var cb = new Ext.ux.grid.CheckColumn({ header: _('redirector.active') ,dataIndex: 'active' - ,width: 40 + ,width: 30 ,sortable: true ,onMouseDown: this.saveCheckbox }); + var re = new Ext.ux.grid.CheckColumn({ + header: _('redirector.isregexp') + ,dataIndex: 'isregexp' + ,width: 30 + ,sortable: true + ,onMouseDown: this.saveCheckbox + }); + Ext.applyIf(config,{ id: 'redirector-grid-redirects' ,url: Redi.config.connector_url - ,baseParams: { action: 'mgr/redirect/getList' } + ,baseParams: { action: 'mgr/redirect/getList', limit: 0, start: 0 } ,save_action: 'mgr/redirect/updateFromGrid' - ,fields: ['id','pattern','target','active','menu'] - ,paging: true + ,fields: ['id','pattern','target','active','isregexp','sortorder','menu'] + ,paging: false + ,limit: 0 ,autosave: true ,remoteSort: true ,anchor: '97%' - ,autoExpandColumn: 'name' - ,plugins: [cb] + ,enableDragDrop: true + ,ddGroup: 'redirect-dd' + ,ddText: 'Yeah ! You\'re dragging me' + ,plugins: [cb,re] ,columns: [{ header: _('redirector.pattern') ,dataIndex: 'pattern' ,sortable: true - ,width: 200 + ,width: 150 ,editor: { xtype: 'textfield' } },{ header: _('redirector.target') ,dataIndex: 'target' ,sortable: false - ,width: 200 + ,width: 150 ,editor: { xtype: 'textfield' } - },cb] + },cb,re, + { + header: _('redirector.priority') + ,dataIndex: 'sortorder' + ,sortable: true + ,width: 30 + ,editor: { xtype: 'numberfield' } + }] ,tbar: [{ xtype: 'textfield' ,id: 'redirector-search-filter' @@ -53,11 +71,66 @@ Redi.grid.Redirects = function(config) { text: _('redirector.redirect_create') ,handler: { xtype: 'redirector-window-redirect-create' ,blankValues: true } }] + ,listeners:{ + "render": { + scope: this, + fn: function(grid){ + var ddrow = new Ext.dd.DropTarget(grid.container, { + ddGroup : 'redirect-dd', + copy:false, + notifyDrop : function(dd, e, data){ + var ds = grid.store; + var sm = grid.getSelectionModel(); + var rows = sm.getSelections(); + if(dd.getDragData(e)) { + var cindex=dd.getDragData(e).rowIndex; + if(typeof(cindex) != "undefined") { + for(i = 0; i < rows.length; i++) { + ds.remove(ds.getById(rows[i].id)); + } + ds.insert(cindex,data.selections); + sm.clearSelections(); + } + } + grid.collectItems(); + } + }); + + this.setWidth('99%'); + } + } + } }); + + Redi.grid.Redirects.superclass.constructor.call(this,config) }; Ext.extend(Redi.grid.Redirects,MODx.grid.Grid,{ - search: function(tf,nv,ov) { + collectItems: function(){ + var items=[]; + // read jsons from grid-store-items + var griddata=this.store.data; + for(i = 0; i < griddata.length; i++) { + items.push(griddata.items[i].json); + } + + items = Ext.util.JSON.encode(items); + MODx.Ajax.request({ + url: Redi.config.connector_url + ,params: { + action: 'mgr/redirect/sort' + ,items: items + ,start: this.start + ,limit: this.limit + } + ,listeners: { + 'success': {fn:function(r) { + this.refresh(); + },scope:this} + } + }); + } + ,search: function(tf,nv,ov) { var s = this.getStore(); s.baseParams.query = tf.getValue(); this.getBottomToolbar().changePage(1); @@ -148,6 +221,11 @@ Redi.window.CreateRedirect = function(config) { ,name: 'active' ,inputValue: 1 ,checked: true + },{ + xtype: 'checkbox' + ,fieldLabel: _('redirector.isregexp') + ,name: 'isregexp' + ,inputValue: 1 }] }); Redi.window.CreateRedirect.superclass.constructor.call(this,config); @@ -182,6 +260,11 @@ Redi.window.UpdateRedirect = function(config) { ,fieldLabel: _('redirector.active') ,name: 'active' ,inputValue: 1 + },{ + xtype: 'checkbox' + ,fieldLabel: _('redirector.isregexp') + ,name: 'isregexp' + ,inputValue: 1 }] }); Redi.window.UpdateRedirect.superclass.constructor.call(this,config); diff --git a/core/components/redirector/controllers/mgr/index.php b/core/components/redirector/controllers/mgr/index.php index 131a998..78b130f 100644 --- a/core/components/redirector/controllers/mgr/index.php +++ b/core/components/redirector/controllers/mgr/index.php @@ -5,6 +5,7 @@ * @package redirector * @subpackage controllers */ +$modx->regClientStartupScript($redirector->config['jsUrl'].'mgr/widgets/pagenotfound.grid.js'); $modx->regClientStartupScript($redirector->config['jsUrl'].'mgr/widgets/redirects.grid.js'); $modx->regClientStartupScript($redirector->config['jsUrl'].'mgr/widgets/home.panel.js'); $modx->regClientStartupScript($redirector->config['jsUrl'].'mgr/sections/index.js'); diff --git a/core/components/redirector/docs/changelog.txt b/core/components/redirector/docs/changelog.txt index 8f1194f..c1f2f14 100644 --- a/core/components/redirector/docs/changelog.txt +++ b/core/components/redirector/docs/changelog.txt @@ -1,5 +1,11 @@ Changelog file for Redirector component. +Redirector 1.0.4 +==================================== +- Add Regular Expression support +- Add 404 logging and easy way to create redirection from them +- Add priority for Redirection Rules + Redirector 1.0.3 ==================================== - Add sqlsrv support diff --git a/core/components/redirector/docs/readme.txt b/core/components/redirector/docs/readme.txt index 0fd2d5d..bc4df49 100644 --- a/core/components/redirector/docs/readme.txt +++ b/core/components/redirector/docs/readme.txt @@ -1,8 +1,15 @@ -------------------- 3PC: Redirector -------------------- -Version: 1.0 +Version: 1.0.4 Since: April 21st, 2010 -Author: Shaun McCormick and Jason Coward +Author: Shaun McCormick , Jason Coward and Emmanuel Prochasson -Handles 301 redirects for your site. +Handles 301 redirects for your site. Logs 404 error to allow quick integration of new redirect rules if required. + +You can create two kinds of rule. Plain text (default) will perform exact match on the pattern and redirect to the target. +Regexp will perform Regular Expression matching and Replacing. + +Rules are applied in their priority order, however, plain text rules are always tried before the Regexp ones. + +IT IS VERY EASY TO GENERATE INFINITE REDIRECTION LOOP, so use with caution. \ No newline at end of file diff --git a/core/components/redirector/elements/plugins/plugin.redirector.php b/core/components/redirector/elements/plugins/plugin.redirector.php index fa7e854..3e8f3c7 100644 --- a/core/components/redirector/elements/plugins/plugin.redirector.php +++ b/core/components/redirector/elements/plugins/plugin.redirector.php @@ -1,4 +1,6 @@ getService('redirector','Redirector',$corePath.'model/redirector/',$scriptProperties); if (!($redirector instanceof Redirector)) return ''; -/* handle redirects */ -$search = $_SERVER['REQUEST_URI']; -$baseUrl = $modx->getOption('base_url',null,MODX_BASE_URL); -if (!empty($baseUrl) && $baseUrl != '/' && $baseUrl != ' ') { - $search = str_replace($baseUrl,'',$search); -} -$search = ltrim($search,'/'); -$extPos = strrpos($search, '.'); -if ($extPos) { - if ($search) { - $redirect = $modx->getObject('modRedirect', array( - 'pattern' => $search, - 'active' => 1, - )); - if ($redirect) { - $target = $redirect->get('target'); - $modx->parser->processElementTags('', $target, true, true); - if ($target != $modx->resourceIdentifier && $target != $search) { - if (!strpos($target, '://')) { - $target = $modx->getOption('site_url').$target; - } - $modx->log(modX::LOG_LEVEL_INFO, 'Redirector plugin redirecting request for ' . $search . ' to ' . $target); - header('HTTP/1.1 301 Moved Permanently'); - $modx->sendRedirect($target); - } + +switch($modx->event->name){ + case 'OnPageNotFound': + /* handle redirects */ + $search = $_SERVER['REQUEST_URI']; + $originalRequest = $search; + $baseUrl = $modx->getOption('base_url',null,MODX_BASE_URL); + if (!empty($baseUrl) && $baseUrl != '/' && $baseUrl != ' ') { + $search = str_replace($baseUrl,'',$search); + $originalRequest = $search; } - } - $search = substr($search, 0, $extPos); -} -if ($search) { - $redirect = $modx->getObject('modRedirect', array('pattern' => $search, 'active' => 1)); - if ($redirect) { - $target = $redirect->get('target'); - $modx->parser->processElementTags('', $target, true, true); - if ($target != $modx->resourceIdentifier && $target != $search) { - if (!strpos($target, '://')) { - $target = $modx->getOption('site_url').$target; + $search = ltrim($search,'/'); + + $extPos = strrpos($search, '.'); + + if ($extPos) { + if ($search) { + $redirector->getRedirect($search); } - $modx->log(modX::LOG_LEVEL_INFO, 'Redirector plugin redirecting request for ' . $search . ' to ' . $target); - header('HTTP/1.1 301 Moved Permanently'); - $modx->sendRedirect($target); + $search = substr($search, 0, $extPos); } - } + if ($search) { + $redirector->getRedirect($search); + } + + $redirector->record404($originalRequest); + return; + break; + case 'OnSiteRefresh': + $redirector->clearRedirectCache(); + return; + break; } -return; \ No newline at end of file diff --git a/core/components/redirector/lexicon/de/default.inc.php b/core/components/redirector/lexicon/de/default.inc.php index df72584..948d0e5 100644 --- a/core/components/redirector/lexicon/de/default.inc.php +++ b/core/components/redirector/lexicon/de/default.inc.php @@ -26,4 +26,14 @@ $_lang['redirector.pattern'] = 'Muster'; $_lang['redirector.search...'] = 'Suche...'; $_lang['redirector.target'] = 'Ziel'; - +$_lang['redirector.priority'] = 'Priority'; +$_lang['redirector.isregexp'] = 'Regexp?'; +$_lang['redirector.errors'] = 'Pages Not Found'; +$_lang['redirector.pnfdesc'] = 'Manage pages not found'; +$_lang['redirector.url'] = 'Url'; +$_lang['redirector.times'] = 'Occurrences'; +$_lang['redirector.firsttime'] = 'First Occurrence'; +$_lang['redirector.lasttime'] = 'Last Occurrence'; +$_lang['redirector.remove_page_not_found'] = 'Remove this entry'; +$_lang['redirector.ignore_page_not_found'] = 'Ignore this entry'; +$_lang['redirector.create_rule'] = 'Create Redirect Rule From This Entry'; diff --git a/core/components/redirector/lexicon/en/default.inc.php b/core/components/redirector/lexicon/en/default.inc.php index e015a1a..6205a34 100644 --- a/core/components/redirector/lexicon/en/default.inc.php +++ b/core/components/redirector/lexicon/en/default.inc.php @@ -25,4 +25,15 @@ $_lang['redirector.pattern'] = 'Pattern'; $_lang['redirector.search...'] = 'Search...'; $_lang['redirector.target'] = 'Target'; +$_lang['redirector.priority'] = 'Priority'; +$_lang['redirector.isregexp'] = 'Regexp?'; +$_lang['redirector.errors'] = 'Pages Not Found'; +$_lang['redirector.pnfdesc'] = 'Manage pages not found'; +$_lang['redirector.url'] = 'Url'; +$_lang['redirector.times'] = 'Occurrences'; +$_lang['redirector.firsttime'] = 'First Occurrence'; +$_lang['redirector.lasttime'] = 'Last Occurrence'; +$_lang['redirector.remove_page_not_found'] = 'Remove this entry'; +$_lang['redirector.ignore_page_not_found'] = 'Ignore this entry'; +$_lang['redirector.create_rule'] = 'Create Redirect Rule From This Entry'; diff --git a/core/components/redirector/lexicon/fr/default.inc.php b/core/components/redirector/lexicon/fr/default.inc.php index 8bdbde1..f1e695d 100755 --- a/core/components/redirector/lexicon/fr/default.inc.php +++ b/core/components/redirector/lexicon/fr/default.inc.php @@ -24,4 +24,15 @@ $_lang['redirector.menu_desc'] = 'Gérez vos redirections.'; $_lang['redirector.pattern'] = 'Modèle'; $_lang['redirector.search...'] = 'Chercher…'; -$_lang['redirector.target'] = 'Cible'; \ No newline at end of file +$_lang['redirector.target'] = 'Cible'; +$_lang['redirector.isregexp'] = 'Regexp?'; +$_lang['redirector.priority'] = 'Priorité'; +$_lang['redirector.errors'] = 'Pages Manquantes'; +$_lang['redirector.pnfdesc'] = 'Gestion des erreurs 404'; +$_lang['redirector.url'] = 'Url'; +$_lang['redirector.times'] = 'Occurrences'; +$_lang['redirector.firsttime'] = 'Première Occurrence'; +$_lang['redirector.lasttime'] = 'Dernière Occurrence'; +$_lang['redirector.remove_page_not_found'] = 'Supprimer cette entrée'; +$_lang['redirector.ignore_page_not_found'] = 'Ignorer cette entrée'; +$_lang['redirector.create_rule'] = 'Créer une redirection à partir de cette entrée'; diff --git a/core/components/redirector/lexicon/nl/default.inc.php b/core/components/redirector/lexicon/nl/default.inc.php index 34a5e48..a8827d6 100644 --- a/core/components/redirector/lexicon/nl/default.inc.php +++ b/core/components/redirector/lexicon/nl/default.inc.php @@ -25,4 +25,14 @@ $_lang['redirector.pattern'] = 'Patroon'; $_lang['redirector.search...'] = 'Zoeken...'; $_lang['redirector.target'] = 'Doel'; - +$_lang['redirector.priority'] = 'Priority'; +$_lang['redirector.isregexp'] = 'Regexp?'; +$_lang['redirector.errors'] = 'Pages Not Found'; +$_lang['redirector.pnfdesc'] = 'Manage pages not found'; +$_lang['redirector.url'] = 'Url'; +$_lang['redirector.times'] = 'Occurrences'; +$_lang['redirector.firsttime'] = 'First Occurrence'; +$_lang['redirector.lasttime'] = 'Last Occurrence'; +$_lang['redirector.remove_page_not_found'] = 'Remove this entry'; +$_lang['redirector.ignore_page_not_found'] = 'Ignore this entry'; +$_lang['redirector.create_rule'] = 'Create Redirect Rule From This Entry'; diff --git a/core/components/redirector/lexicon/ru/default.inc.php b/core/components/redirector/lexicon/ru/default.inc.php index cfb8ffe..e0adfb6 100755 --- a/core/components/redirector/lexicon/ru/default.inc.php +++ b/core/components/redirector/lexicon/ru/default.inc.php @@ -25,4 +25,14 @@ $_lang['redirector.pattern'] = 'Шаблон'; $_lang['redirector.search...'] = 'Поиск...'; $_lang['redirector.target'] = 'Цель'; - +$_lang['redirector.priority'] = 'Priority'; +$_lang['redirector.isregexp'] = 'Regexp?'; +$_lang['redirector.errors'] = 'Pages Not Found'; +$_lang['redirector.pnfdesc'] = 'Manage pages not found'; +$_lang['redirector.url'] = 'Url'; +$_lang['redirector.times'] = 'Occurrences'; +$_lang['redirector.firsttime'] = 'First Occurrence'; +$_lang['redirector.lasttime'] = 'Last Occurrence'; +$_lang['redirector.remove_page_not_found'] = 'Remove this entry'; +$_lang['redirector.ignore_page_not_found'] = 'Ignore this entry'; +$_lang['redirector.create_rule'] = 'Create Redirect Rule From This Entry'; diff --git a/core/components/redirector/model/redirector/metadata.mysql.php b/core/components/redirector/model/redirector/metadata.mysql.php new file mode 100644 index 0000000..d1b583a --- /dev/null +++ b/core/components/redirector/model/redirector/metadata.mysql.php @@ -0,0 +1,9 @@ + + array ( + 0 => 'modRedirect', + 1 => 'modRedirectPageNotFound', + ), +); \ No newline at end of file diff --git a/core/components/redirector/model/redirector/metadata.sqlsrv.php b/core/components/redirector/model/redirector/metadata.sqlsrv.php new file mode 100644 index 0000000..1ce2540 --- /dev/null +++ b/core/components/redirector/model/redirector/metadata.sqlsrv.php @@ -0,0 +1,8 @@ + + array ( + 0 => 'modRedirect', + ), +); \ No newline at end of file diff --git a/core/components/redirector/model/redirector/modredirect.class.php b/core/components/redirector/model/redirector/modredirect.class.php index b1ea792..1fc11b0 100644 --- a/core/components/redirector/model/redirector/modredirect.class.php +++ b/core/components/redirector/model/redirector/modredirect.class.php @@ -3,5 +3,16 @@ * @package redirector */ class modRedirect extends xPDOSimpleObject { - -} \ No newline at end of file + function modRedirect(& $xpdo) { + $this->__construct($xpdo); + } + function __construct(& $xpdo) { + parent :: __construct($xpdo); + } + + function save(){ + $this->xpdo->cacheManager->refresh(array('Redirector' => array())); + return parent::save(); + } +} +?> \ No newline at end of file diff --git a/core/components/redirector/model/redirector/modredirectpagenotfound.class.php b/core/components/redirector/model/redirector/modredirectpagenotfound.class.php new file mode 100644 index 0000000..1e42a03 --- /dev/null +++ b/core/components/redirector/model/redirector/modredirectpagenotfound.class.php @@ -0,0 +1,13 @@ +__construct($xpdo); + } + function __construct(& $xpdo) { + parent :: __construct($xpdo); + } +} +?> \ No newline at end of file diff --git a/core/components/redirector/model/redirector/mysql/modredirect.class.php b/core/components/redirector/model/redirector/mysql/modredirect.class.php index 8e5ed17..68d62d7 100644 --- a/core/components/redirector/model/redirector/mysql/modredirect.class.php +++ b/core/components/redirector/model/redirector/mysql/modredirect.class.php @@ -3,4 +3,12 @@ * @package redirector */ require_once (strtr(realpath(dirname(dirname(__FILE__))), '\\', '/') . '/modredirect.class.php'); -class modRedirect_mysql extends modRedirect {} \ No newline at end of file +class modRedirect_mysql extends modRedirect { + function modRedirect_mysql(& $xpdo) { + $this->__construct($xpdo); + } + function __construct(& $xpdo) { + parent :: __construct($xpdo); + } +} +?> \ No newline at end of file diff --git a/core/components/redirector/model/redirector/mysql/modredirect.map.inc.php b/core/components/redirector/model/redirector/mysql/modredirect.map.inc.php index 26e89b4..19dde9c 100644 --- a/core/components/redirector/model/redirector/mysql/modredirect.map.inc.php +++ b/core/components/redirector/model/redirector/mysql/modredirect.map.inc.php @@ -4,11 +4,15 @@ */ $xpdo_meta_map['modRedirect']= array ( 'package' => 'redirector', + 'version' => NULL, 'table' => 'redirects', + 'extends' => 'xPDOSimpleObject', 'fields' => array ( 'pattern' => '', 'target' => '', + 'sortorder' => 0, + 'isregexp' => 0, 'active' => 1, ), 'fieldMeta' => @@ -31,6 +35,23 @@ 'default' => '', 'index' => 'index', ), + 'sortorder' => + array ( + 'dbtype' => 'int', + 'precision' => '10', + 'phptype' => 'integer', + 'null' => false, + 'default' => 0, + ), + 'isregexp' => + array ( + 'dbtype' => 'tinyint', + 'precision' => '1', + 'attributes' => 'unsigned', + 'phptype' => 'boolean', + 'null' => false, + 'default' => 0, + ), 'active' => array ( 'dbtype' => 'tinyint', diff --git a/core/components/redirector/model/redirector/mysql/modredirectpagenotfound.class.php b/core/components/redirector/model/redirector/mysql/modredirectpagenotfound.class.php new file mode 100644 index 0000000..cd7154a --- /dev/null +++ b/core/components/redirector/model/redirector/mysql/modredirectpagenotfound.class.php @@ -0,0 +1,14 @@ +__construct($xpdo); + } + function __construct(& $xpdo) { + parent :: __construct($xpdo); + } +} +?> \ No newline at end of file diff --git a/core/components/redirector/model/redirector/mysql/modredirectpagenotfound.map.inc.php b/core/components/redirector/model/redirector/mysql/modredirectpagenotfound.map.inc.php new file mode 100644 index 0000000..27450fc --- /dev/null +++ b/core/components/redirector/model/redirector/mysql/modredirectpagenotfound.map.inc.php @@ -0,0 +1,59 @@ + 'redirector', + 'version' => NULL, + 'table' => 'pagesnotfound', + 'extends' => 'xPDOSimpleObject', + 'fields' => + array ( + 'url' => NULL, + 'times' => 0, + 'firsttime' => 0, + 'lasttime' => 0, + 'visible' => 1, + ), + 'fieldMeta' => + array ( + 'url' => + array ( + 'dbtype' => 'text', + 'phptype' => 'string', + ), + 'times' => + array ( + 'dbtype' => 'int', + 'precision' => '10', + 'phptype' => 'integer', + 'null' => false, + 'default' => 0, + ), + 'firsttime' => + array ( + 'dbtype' => 'int', + 'precision' => '100', + 'phptype' => 'integer', + 'null' => true, + 'default' => 0, + ), + 'lasttime' => + array ( + 'dbtype' => 'int', + 'precision' => '100', + 'phptype' => 'integer', + 'null' => true, + 'default' => 0, + ), + 'visible' => + array ( + 'dbtype' => 'tinyint', + 'precision' => '1', + 'attributes' => 'unsigned', + 'phptype' => 'boolean', + 'null' => false, + 'default' => 1, + ), + ), +); diff --git a/core/components/redirector/model/redirector/redirector.class.php b/core/components/redirector/model/redirector/redirector.class.php index 2bea1aa..9e58666 100644 --- a/core/components/redirector/model/redirector/redirector.class.php +++ b/core/components/redirector/model/redirector/redirector.class.php @@ -26,9 +26,114 @@ function __construct(modX &$modx,array $config = array()) { 'connectorUrl' => $assetsUrl.'connector.php', ),$config); + + $this->cacheName = 'Redirector'; + $this->cache = $this->modx->cacheManager->getCacheProvider($this->cacheName, array( + xPDO::OPT_CACHE_KEY => $this->cacheName + )); + $this->modx->addPackage('redirector',$this->config['modelPath']); } + + /** + * Given a search parameter, look in the cache for a match. If none found, look for a matching pattern. If found, + * redirect. Otherwise do nothing. + * @param $search + */ + public function getRedirect($search){ + // Look for a target in the cache + $redirect = $this->cache->get(md5($search)); + if($redirect){ + $this->doRedirect($redirect, $search, 'Cache'); + exit(); + } + + // No entry in the cache. Try to match a pattern. + + // Exact text match + $pattern = $this->modx->getObject('modRedirect', array( + 'pattern' => $search + ,'active' => 1 + )); + if($pattern){ + $target = $pattern->get('target'); + $this->doRedirect($target, $search, 'Plain Text'); + exit(); + } + + // No luck ? Check the regexp. + $query = $this->modx->newQuery('modRedirect'); + $query->where(array('active' => 1, 'isregexp' => 1)); + $query->sortby('sortorder', 'ASC'); + $patterns = $this->modx->getCollection('modRedirect', $query); + foreach($patterns as $pattern){ + $pat = $pattern->get('pattern'); + if(preg_match("~$pat~", $search)){ + $target = $pattern->get('target'); + $target = preg_replace("~$pat~", $target, $search); + $this->doRedirect($target, $search, 'Regexp'); + exit(); + } + } + } + + /** + * Prepare the new URL and redirect the user (and cache the redirect). + * + * @param $target String target recorded in the database + * @param $search String the search string used + */ + private function doRedirect($target, $search='', $type='Plain Text'){ + $this->modx->parser->processElementTags('', $target, true, true); + if ($target != $this->modx->resourceIdentifier && $target != $search) { + $short_target = $target; + if (!strpos($target, '://')) { + $target = $this->modx->getOption('site_url').$target; + } + $this->modx->log(modX::LOG_LEVEL_INFO, 'Redirector plugin redirecting request for ' . $search . ' to ' . $target. 'Rule: '.$type); + + // Cache the new redirect + $this->cache->set(md5($search), $short_target); + + // Perform the redirect. + header('HTTP/1.1 301 Moved Permanently'); + $this->modx->sendRedirect($target); + exit(); + } + } + + + public function record404($search){ + $error = $this->modx->getObject('modRedirectPageNotFound', array('url' => $search)); + $now = time(); + if(!empty($error)){ // We've encountered this one before. + $count = $error->get('times'); + $count += 1; + $error->set('times', $count); + $error->set('lasttime', $now); + $error->save(); + } else { + $error = $this->modx->newObject('modRedirectPageNotFound'); + $error->set('url', $search); + $error->set('firsttime', $now); + $error->set('lasttime', $now); + $error->set('times', 1); + $error->set('visible', 1); + $error->save(); + } + } + + + /** + * Clear the Redirect cache + * @access public + */ + public function clearRedirectCache(){ + $this->modx->cacheManager->refresh(array($this->cacheName => array())); + $this->cache->refresh(); + } + /** * Initializes the class into the proper context * @@ -45,14 +150,14 @@ public function initialize($ctx = 'web') { } $this->request = new redirectorControllerRequest($this); return $this->request->handleRequest(); - break; + break; case 'connector': if (!$this->modx->loadClass('redirector.request.redirectorConnectorRequest',$this->config['modelPath'],true,true)) { echo 'Could not load connector request handler.'; die(); } $this->request = new redirectorConnectorRequest($this); return $this->request->handle(); - break; + break; default: break; } return true; diff --git a/core/components/redirector/model/redirector/sqlsrv/modredirect.map.inc.php b/core/components/redirector/model/redirector/sqlsrv/modredirect.map.inc.php index cc0098d..965b605 100644 --- a/core/components/redirector/model/redirector/sqlsrv/modredirect.map.inc.php +++ b/core/components/redirector/model/redirector/sqlsrv/modredirect.map.inc.php @@ -4,7 +4,9 @@ */ $xpdo_meta_map['modRedirect']= array ( 'package' => 'redirector', + 'version' => NULL, 'table' => 'redirects', + 'extends' => 'xPDOSimpleObject', 'fields' => array ( 'pattern' => '', diff --git a/core/components/redirector/model/schema/redirector.mysql.schema.xml b/core/components/redirector/model/schema/redirector.mysql.schema.xml index 7aeb785..43402c9 100644 --- a/core/components/redirector/model/schema/redirector.mysql.schema.xml +++ b/core/components/redirector/model/schema/redirector.mysql.schema.xml @@ -3,6 +3,16 @@ - + + + + + + + + + + + \ No newline at end of file diff --git a/core/components/redirector/processors/mgr/errors/getlist.php b/core/components/redirector/processors/mgr/errors/getlist.php new file mode 100644 index 0000000..a18c2cb --- /dev/null +++ b/core/components/redirector/processors/mgr/errors/getlist.php @@ -0,0 +1,56 @@ +getOption('start',$_REQUEST,0); +$limit = $modx->getOption('limit',$_REQUEST,20); +$sort = $modx->getOption('sort',$_REQUEST,'times'); +$dir = $modx->getOption('dir',$_REQUEST,'DESC'); +$query = $modx->getOption('query',$_REQUEST,''); + +/* build query */ +$c = $modx->newQuery('modRedirectPageNotFound'); + +if (!empty($query)) { + $c->where(array( + 'url:LIKE' => '%'.$query.'%', + )); +} + +$count = $modx->getCount('modRedirectPageNotFound',$c); +$c->sortby($sort,$dir); +if ($isLimit) $c->limit($limit,$start); +$redirects= $modx->getCollection('modRedirectPageNotFound', $c); + +/* iterate */ +$list = array(); +foreach ($redirects as $redirect) { + + // Format dates + $a = $redirect->toArray(); + $a['firsttime'] = strftime('%c', $a['firsttime']); + $a['lasttime'] = strftime('%c', $a['lasttime']); + + $redirectArray = $a; + + $redirectArray['menu'][] = array( + 'text' => $modx->lexicon('redirector.create_rule'), + 'handler' => 'this.createRedirectPageNotFound', + ); + + $redirectArray['menu'][] = '-'; + + $redirectArray['menu'][] = array( + 'text' => $modx->lexicon('redirector.remove_page_not_found'), + 'handler' => 'this.removeRedirectPageNotFound', + ); + + + $list[]= $redirectArray; +} +return $this->outputArray($list,$count); \ No newline at end of file diff --git a/core/components/redirector/processors/mgr/errors/remove.php b/core/components/redirector/processors/mgr/errors/remove.php new file mode 100644 index 0000000..bfb2ed7 --- /dev/null +++ b/core/components/redirector/processors/mgr/errors/remove.php @@ -0,0 +1,17 @@ +error->failure($modx->lexicon('redirector.redirect_err_ns')); +$redirect = $modx->getObject('modRedirectPageNotFound',$scriptProperties['id']); +if (empty($redirect)) return $modx->error->failure($modx->lexicon('redirector.redirect_err_nf')); + +/* remove */ +if ($redirect->remove() == false) { + return $modx->error->failure($modx->lexicon('redirector.redirect_err_remove')); +} + +return $modx->error->success('',$redirect); \ No newline at end of file diff --git a/core/components/redirector/processors/mgr/errors/update.php b/core/components/redirector/processors/mgr/errors/update.php new file mode 100644 index 0000000..63ea711 --- /dev/null +++ b/core/components/redirector/processors/mgr/errors/update.php @@ -0,0 +1,26 @@ +error->failure($modx->lexicon('redirector.redirect_err_ns')); +$redirect = $modx->getObject('modRedirectPageNotFound',$scriptProperties['id']); +if (empty($redirect)) return $modx->error->failure($modx->lexicon('redirector.redirect_err_nf')); + +/* put checkbox to 0 if not present in the array */ +if(!array_key_exists('visible', $scriptProperties)) + $scriptProperties['visible'] = '0'; + +/* set fields */ +$redirect->fromArray($scriptProperties); + +/* save */ +if ($redirect->save() == false) { + + return $modx->error->failure($modx->lexicon('redirector.redirect_err_save')); +} + + +return $modx->error->success('',$redirect); \ No newline at end of file diff --git a/core/components/redirector/processors/mgr/redirect/getlist.php b/core/components/redirector/processors/mgr/redirect/getlist.php index 35bc438..cf0b595 100644 --- a/core/components/redirector/processors/mgr/redirect/getlist.php +++ b/core/components/redirector/processors/mgr/redirect/getlist.php @@ -8,8 +8,8 @@ /* setup default properties */ $isLimit = !empty($_REQUEST['limit']); $start = $modx->getOption('start',$_REQUEST,0); -$limit = $modx->getOption('limit',$_REQUEST,20); -$sort = $modx->getOption('sort',$_REQUEST,'pattern'); +$limit = $modx->getOption('limit',$_REQUEST,0); +$sort = $modx->getOption('sort',$_REQUEST,'sortorder'); $dir = $modx->getOption('dir',$_REQUEST,'ASC'); $query = $modx->getOption('query',$_REQUEST,''); @@ -27,7 +27,7 @@ $count = $modx->getCount('modRedirect',$c); $c->sortby($sort,$dir); -if ($isLimit) $c->limit($limit,$start); +//if ($isLimit) $c->limit($limit,$start); //Ignore the limit $redirects= $modx->getCollection('modRedirect', $c); /* iterate */ diff --git a/core/components/redirector/processors/mgr/redirect/sort.php b/core/components/redirector/processors/mgr/redirect/sort.php new file mode 100644 index 0000000..361d30e --- /dev/null +++ b/core/components/redirector/processors/mgr/redirect/sort.php @@ -0,0 +1,15 @@ +fromJSON($scriptProperties['items']); + +if(is_array($items)){ + foreach($items as $sortorder => $fields){ + if($image = $modx->getObject('modRedirect', $fields['id'])){ + $image->fromArray($fields); + $image->set('sortorder', ($sortorder)); + $image->save(); + } + } +} + +return $modx->error->success(); \ No newline at end of file diff --git a/core/components/redirector/processors/mgr/redirect/update.php b/core/components/redirector/processors/mgr/redirect/update.php index f21c7a1..922fe7b 100644 --- a/core/components/redirector/processors/mgr/redirect/update.php +++ b/core/components/redirector/processors/mgr/redirect/update.php @@ -13,11 +13,17 @@ if(!array_key_exists('active', $scriptProperties)) $scriptProperties['active'] = '0'; +/* same with isregexp */ +if(!array_key_exists('isregexp', $scriptProperties)) + $scriptProperties['isregexp'] = '0'; + + /* set fields */ $redirect->fromArray($scriptProperties); /* save */ if ($redirect->save() == false) { + return $modx->error->failure($modx->lexicon('redirector.redirect_err_save')); } From 0dd2c65eac62a297cee7d8a7d90f405d1ba4bff6 Mon Sep 17 00:00:00 2001 From: Emmanuel Prochasson Date: Fri, 18 Jan 2013 14:05:50 +0800 Subject: [PATCH 2/3] Security fix: prevent injecting MODx code in the URL. --- .../components/redirector/model/redirector/redirector.class.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/components/redirector/model/redirector/redirector.class.php b/core/components/redirector/model/redirector/redirector.class.php index 9e58666..2252e7a 100644 --- a/core/components/redirector/model/redirector/redirector.class.php +++ b/core/components/redirector/model/redirector/redirector.class.php @@ -71,7 +71,7 @@ public function getRedirect($search){ $pat = $pattern->get('pattern'); if(preg_match("~$pat~", $search)){ $target = $pattern->get('target'); - $target = preg_replace("~$pat~", $target, $search); + $target = preg_replace("~$pat~", $target, preg_replace('/[\[\]]/', '',$search)); $this->doRedirect($target, $search, 'Regexp'); exit(); } From 9c553c8f7d85e117924637082c59f55c72924e58 Mon Sep 17 00:00:00 2001 From: Emmanuel Prochasson Date: Tue, 22 Jan 2013 10:22:38 +0800 Subject: [PATCH 3/3] Fix bug preventing from clearing the cache properly --- core/components/redirector/model/redirector/redirector.class.php | 1 - 1 file changed, 1 deletion(-) diff --git a/core/components/redirector/model/redirector/redirector.class.php b/core/components/redirector/model/redirector/redirector.class.php index 2252e7a..56e72e3 100644 --- a/core/components/redirector/model/redirector/redirector.class.php +++ b/core/components/redirector/model/redirector/redirector.class.php @@ -131,7 +131,6 @@ public function record404($search){ */ public function clearRedirectCache(){ $this->modx->cacheManager->refresh(array($this->cacheName => array())); - $this->cache->refresh(); } /**