makeURL と アンカー# params ? について

質問全般・改善要望
返信する
shobu
メンバー
メンバー
記事: 91
登録日時: 2011年5月26日(木) 16:54

makeURL と アンカー# params ? について

投稿記事 by shobu »

いつもお世話になっております。

古いMODXサイトをデザインごと一新する作業をしていて気になる問題を発見しました。
以前はTinyMCE内でのリンクやウェブリンクでのURL指定時、アンカーやパラメーターを使用したい場合にドキュメントIDの数字のみでは無く、

[~99~]#anchor

といった形の指定が可能で、結果的には[~99~]の部分が正しくURLになり、末尾に#anchorがついていました。

個人的にアンカーを使う事がないので、いつのバージョンから駄目になったのか不明なのですが、1.0.12Jのソースをながめていますと、前述のような書き方をしても makeUrl 内の処理で一旦数値のみにする部分があり、この部分で上記の書き方は破壊されて最終的には正しいURLになりません。 (カッコ左側だけ外れて 99~]#anchor というような形になってしまいます)

以前は可能だったので古いバージョンで動いているサイトには前述の記述方法を使っている箇所が沢山あるケースもありますし、今後もこのような書き方をしたいという需要もあると思います。

こちらでザックリながめて、いつものようになるべく現ソースをいじらない方法で調整してみました。makeURL には引数としてargsを渡す部分があって、この場合の処理はちゃんとされるのかもしれません(が、この引数を使っている例が見つからなかった)。
今回の調整では…
  • argsの処理とバッティングして2重にargsがついてしまう可能性がある
  • 力尽きて rewriteURL 他関連しそうな部分をチェックしていない
  • #? 以降だけを考慮しているが 他に考えるべき文字がある気がする
あたりの問題を確認して解決しないといけないかもしれません。

※1.0.12j に先日のrelativeの件を手修正しているの1.0.12r1のコードと一部差異があるかもしれません。
※一部配列がNULLになったり""になったりみっともないのですが、オリジナルをなるべく触らない方針と実害無いと言うことでそのままにしてあります。
※処理の妥当性、他への影響、洩れてるURL指定パターンなどがあるようにも思えます…。
※コード内部を検証できていませんが、前述の記述で問題なく[+url+] [+symlink+] で取り出せる ditto は makeURL を使わず、古くから基本が変らないオリジナルコードで処理しているんじゃないでしょうか。
記述の自由度が徒になって、想定すべきパターンが沢山あり、リファクタリング時にはその辺に注意する必要もありそうです。1場合によっては ditto を参考にして、すりあわせる方法もあるような気がします。

private function _getReferenceListing()
{
$referenceListing = array();
$rs = $this->db->select('id,content', '[+prefix+]site_content', "type='reference'");
$rows = $this->db->makeArray($rs);
foreach($rows as $row)
{
extract($row);
$content = trim($content);
$querysaved = preg_split('/(\?|#)/', $content, -1, PREG_SPLIT_NO_EMPTY | PREG_SPLIT_DELIM_CAPTURE);// ?,&,#等のパラメータ前後を分けて保存
$content = $querysaved[0]; //パラメータ以外をオリジナルソースと同じ所に保存
if((strpos($content,'[')!==false || strpos($content,'{')!==false) && strpos($content,'[~')===false)
{
$content = $this->parseDocumentSource($content);
}
elseif(strpos($content,'[~')===0)
{
$content = substr($content,2,-2);
if(strpos($content,'[')!==false || strpos($content,'{')!==false)
{
$content = $this->parseDocumentSource($content);
}
}
$referenceListing[$id.'_param'] = implode('', array_slice($querysaved,1));//$querysaved からパラメータを取戻して引き渡せる形で保存。
$referenceListing[$id] = $content;
}

$this->referenceListing = $referenceListing;
return $referenceListing;
}

function makeUrl($id, $alias= '', $args= '', $scheme= 'abs')
{
if($id==0) return $this->config['site_url'];
$makeurl= '';
$f_url_prefix = $this->config['friendly_url_prefix'];
$f_url_suffix = $this->config['friendly_url_suffix'];
if (!preg_match('@^[0-9]+$@',$id))
{
$this->messageQuit("'{$id}' is not numeric and may not be passed to makeUrl()");
}

if(!isset($this->referenceListing)) $this->_getReferenceListing();
if(isset($this->referenceListing[$id]))
{
if(preg_match('/^[0-9]+$/',$this->referenceListing[$id]))
{
$id = $this->referenceListing[$id];
}
else return $this->referenceListing[$id];
}
if(isset($this->referenceListing[$id.'_param'])) $param = $this->referenceListing[$id.'_param'];// param取り出し

if ($this->config['friendly_urls'] == 1)
{
$alPath = '';
if(empty($alias))
{
$alias = $id;
if ($this->config['friendly_alias_urls'] == 1)
{
if(!$this->aliasListing) $this->setAliasListing();

$al= $this->aliasListing[$id];
$alPath = ($al && !empty($al['path'])) ? $al['path'] . '/' : '';
if(!empty($alPath))
{
$_ = explode('/', $alPath);
foreach($_ as $i=>$v)
{
$_[$i] = urlencode($v);
}
$alPath = join('/', $_);
}
if ($al && $al['alias'])
{
if($this->config['xhtml_urls']==='1') $alias = urlencode($al['alias']);
else $alias = $al['alias'];
}
}
}

if(strpos($alias, '.') !== false && $this->config['suffix_mode']==='1')
{
$f_url_suffix = '';
}
elseif($al['isfolder']==='1' && $this->config['make_folders']==='1' && $id != $this->config['site_start'])
{
$f_url_suffix = '/';
}
$makeurl = $alPath . $f_url_prefix . $alias . $f_url_suffix;

}
else $makeurl= "index.php?id={$id}";
$makeurl .= $param; //組み立て後のpathにparamを付加
$site_url = $this->config['site_url'];
$base_url = $this->config['base_url'];
switch($scheme)
{
case 'full':
$site_url = $this->config['site_url'];
$base_url = '';
break;
case 'http':
case '0':
if(strpos($site_url,'http://')!==0)
$site_url = 'http' . substr($site_url,strpos($site_url,':'));
$base_url = '';
break;
case 'https':
case '1':
if(strpos($site_url,'https://')!==0)
$site_url = 'https' . substr($site_url,strpos($site_url,':'));
$base_url = '';
break;
case 'absolute':
case 'abs':
$site_url = '';
$base_url = $this->config['base_url'];
break;
case 'relative':
case '-1':
$site_url = '';
$base_url = '';
break;
default:
$site_url = '';
$base_url = $this->config['base_url'];
}

$url = "{$site_url}{$base_url}{$makeurl}";
if($args!=='')
{
$args = ltrim($args,'?&');
if(strpos($url,'?')===false) $url .= "?{$args}";
else $url .= "&{$args}";
}

if($this->config['xhtml_urls']) $url = preg_replace("/&(?!amp;)/",'&', $url);

$rs = $this->invokeEvent('OnMakeUrl',
array(
"id" => $id,
"alias" => $alias,
"args" => $args,
"scheme"=> $scheme,
"url" => $url
)
);
if (!empty($rs))
{
$url = end($rs);
}
return $url;
}
shobu
メンバー
メンバー
記事: 91
登録日時: 2011年5月26日(木) 16:54

Re: makeURL と アンカー# params ? について

投稿記事 by shobu »

スミマセン。さっきのコードは取り下げです。
修正できたと思ったのですが、上記のバージョンではなかったみたいです。
(キャッシュのせいで勘違いしました)
上記コードだとアンカーがあってもURLは壊れませんが、アンカーを無視してしまいます。

問題の発生と解決方法の流れは変りませんが、少し確認しなおします。
shobu
メンバー
メンバー
記事: 91
登録日時: 2011年5月26日(木) 16:54

Re: makeURL と アンカー# params ? について

投稿記事 by shobu »

結局は深夜、大雪の中、ざくざくと帰るハメになったのですが、たぶん焦ってよく確認せずに書き換えちゃったのだと思います(キャッシュのせいで変化無しなのをOKにしちゃったみたいです)

コード: 全て選択

if(isset($this->referenceListing[$id]))
{
    if(preg_match('/^[0-9]+$/',$this->referenceListing[$id]))
    {
        $id = $this->referenceListing[$id];
    }
    else return $this->referenceListing[$id];
}
if(isset($this->referenceListing[$id.'_param'])) $param = $this->referenceListing[$id.'_param'];// param取り出し
これだと途中で$idが変質してるので $this->referenceListing[$id.'_param'] が期待通りに動きませんね。
下記のように修正します。

コード: 全て選択

if(isset($this->referenceListing[$id]))
{
    if(preg_match('/^[0-9]+$/',$this->referenceListing[$id]))
    {
        if(isset($this->referenceListing[$id.'_param'])) $param = $this->referenceListing[$id.'_param'];// param取り出し
        $id = $this->referenceListing[$id];
    }
    else return $this->referenceListing[$id];
}
if(preg_match('/^[0-9]+$/',$this->referenceListing[$id])) がfalseだとreturnするので、この位置で良いかな…

これで [~99~]#anchor が期待した形で戻ることを確認しました。
アバター
yama
管理人
記事: 3251
登録日時: 2009年7月29日(水) 02:50

Re: makeURL と アンカー# params ? について

投稿記事 by yama »

http://140216093339-21661.evo.demo.modx ... -help.html
[~99~]#anchor という記述が正しく展開されないということでしょうか?
上記デモサイト1.0.12J-r1で試してみたところ、問題を再現できませんでした。(前提条件などどこか違う部分があるでしょうか?)
shobu
メンバー
メンバー
記事: 91
登録日時: 2011年5月26日(木) 16:54

Re: makeURL と アンカー# params ? について

投稿記事 by shobu »

いつもありがとうございます。
こちらで発見したきっかけは、ぱんくずリストをtopicspathにしてみたところ、表示されたリンクが破損。topicspathの不具合かなとコードを追った所、内部でmakeUrlを呼んでいる箇所があり、前述の箇所に行きつきました。
yamaさんのデモだとパーサー内部からの処理になるでしょうか。makeUrlの呼び出し方が違うのかな?
ただいま出先なので、後ほど併せて検証してみます。
shobu
メンバー
メンバー
記事: 91
登録日時: 2011年5月26日(木) 16:54

Re: makeURL と アンカー# params ? について

投稿記事 by shobu »

ご用意頂いたデモサイトにログインして再現させてみました。

通る処理によって状況が違うようです。

多少強引ですが、第2階層にウェブリンクを作成して、リンク先を

[~9~]#test

としました。
次にその下にもう1ページ用意して、下記のURLでアクセスしてみると…

http://140216093339-21661.evo.demo.modx ... shobu.html

xrayテンプレートですと、一番上に横ナビがWayfinderで用意されていて、こちらの場合は処理に問題なし。
パンくずリストは Topicspath でしたが、こちらで生成されるリンクは 9#te となってしまっています。
[~9~]#test が Topicspath から makeURL へ渡され、昨日のメッセージの展開により破損する流れのようです。

content中に [~9~]#test と書いた場合に正しく展開される理由は追えていません。

デモサイトはそのままにしてあります。引き続きよろしくお願いいたします。
アバター
yama
管理人
記事: 3251
登録日時: 2009年7月29日(水) 02:50

Re: makeURL と アンカー# params ? について

投稿記事 by yama »

確認しました。後ほど修正パッチを作成します。
返信する