<?php

error_reporting(E_ERROR | E_WARNING | E_PARSE);

ini_set('memory_limit', '256M');

require 'vendor/autoload.php';

use Medoo\Medoo;

$uflizer = new URLFriendlyizer(array(
		'default_refresh'   =>  3600*6
	));

if (isset($argv) && $argv[1]=='update') {
	$pnids = array_keys($uflizer->config["properties"]);
	if ($argv[2] && $uflizer->config["properties"][$argv[2]]) {
		echo "Actualizando pnid=".$argv[2]." (".$uflizer->config["properties"][$argv[2]]["name"].")...";
		$uflizer->pnidUpdate($argv[2]);
		echo "OK\n";
	} else {
		for ($i=0;$i<count($pnids);$i++) {
			echo "Actualizando pnid=".$pnids[$i]." (".$uflizer->config["properties"][$pnids[$i]]["name"].")...";
			$uflizer->pnidUpdate($pnids[$i]);
			echo "OK\n";
		}
	}
	exit();
}

$uflizer->start();


class URLFriendlyizer {
	var $config = array();
	var $default_refresh = 86400; // 1 dia
	var $db; 
	var $cacheURIaid = array();
	var $cacheURIpnid = array();
	var $cacheURIpvid = array();

	function __construct($opts) {
		if (isset($opts["default_refresh"])) {
			$this->default_refresh = $opts["default_refresh"];
		}
		$this->readConfig();
		if (!isset($this->config["datadir"])) {
			$this->config["datadir"]=dirname(__FILE__)."/ufdata/";
		} elseif (preg_match('/^\.\/(.*)$/',$this->config["datadir"],$matches)) {
			$this->config["datadir"]=dirname(__FILE__)."/".$matches[1];
		}
		if (!isset($this->config["db_file"])) {
			$this->config["db_file"]="urlfriendly.db";
		}

		$this->db = new Medoo([
				"type"=>"sqlite",
				"database"=>$this->config["datadir"].$this->config["db_file"]
			]);

	}

	function start() {
		$req; //requested uri
		$ruri; //requested friendly uri part
		$nuri; //normalized requested friendly uri part
		$alt="w3-";
		$alt_suffix=!empty($this->config["alt_suffix"])?$this->config["alt_suffix"]:"alt";

		$req = $_SERVER['REQUEST_URI'];
		if (!isset($req)) {
			$req = $_SERVER['argv'][1];
		}
		$req = preg_replace('/[\?].*/','',$req);
		$req = preg_replace('/["<>]/','',$req);

		$ruri = $req;
		if (!isset($ruri)) {
			$ruri = "/";
		} else {
			$ruri = substr($ruri,strlen($this->config["abase"]));
		}
		$ruri = preg_replace("/^\/+|\/+$/","",$ruri);

		if (preg_match('/^_info\/php$/',$ruri,$mref)) {
			phpinfo();
			return;
		}

		$this->checkPNUpdates();

		if (preg_match('/^(.*)\!('.$alt_suffix.')$/',$ruri,$mref)) {
			$ruri=$mref[1];
			$alt='alt-';
		}

		$nuri = URLFriendlyizer::normalize($ruri);

		if (preg_match('/^_redirect\/([0-9]+)\/(.*)$/',$ruri,$mref)) {
			$this->redirectToCanonicalURI($mref[1],$mref[2]);
		} else if (preg_match('/^_resolve\/([0-9]+)\/(.*)$/',$ruri,$mref)) {
			$this->jsonCanonicalURI($mref[1],$mref[2],1);
		}

		$source = "";

		if ($nuri == '') {
			$source = $alt.'channel.html';
		} else if (preg_match('/^mapa-del-sitio$/i',$ruri)) {
			$source = $alt.'contents.html';
		} else if (preg_match('/^ingreso$/i',$ruri)) {
			$source = $alt.'issues.html';
		} else if (preg_match('/^(.*\/)?([0-9]+):[^\/]*$/',$ruri,$mref)) {
			$source = $alt.'article-'.$mref[2].'.html';
		} else if (preg_match('/^([^;]+);([^;]+)$/',$ruri,$mref)) {
			$prop1 = $this->db->get("ufproperties",array("pvid"),array("ufname"=>URLFriendlyizer::normalize($mref[1])));
			$prop2 = $this->db->get("ufproperties",array("pvid"),array("ufname"=>URLFriendlyizer::normalize($mref[2])));
			if ($prop1["pvid"] < $prop2["pvid"]) {
				$source = $alt.'multipropertyvalues-'.$prop1["pvid"].'-'.$prop2["pvid"].'.html';
			} else {
				$source = $alt.'multipropertyvalues-'.$prop2["pvid"].'-'.$prop1["pvid"].'.html';
			}
		} else {
			$prop = $this->db->get("ufproperties",array("pvid","pnid","name"),array("ufname"=>$nuri));
			if ($prop["pvid"]>0) {
				$source = $alt.'propertyvalue-'.$prop["pvid"].'.html';
			} else if ($prop["pnid"]>0) {
				$source = $alt.'propertyname-'.$prop["pnid"].'.html';
			}
		}

		//echo "<!-- db error : ".print_r($this->db->error(),1)." -->";

		$text = "";

		if ($source && file_exists($this->config["idir"].$source)) {
			$charsetdecl = '';
			if ($this->config["charset"]) {
				$charsetdecl = ';charset='.$this->config["charset"];
			}
			header('Content-type:text/html'.$charsetdecl);
			$text =  file_get_contents($this->config["idir"].$source);

			// los enlaces se convierten en amistosos
                        $abspre = $this->config["ubase"].$this->config["ibase"];
                        $abspre = str_replace('/','\/',$abspre);
                        $abspre = str_replace('.','\.',$abspre);
                        $text = preg_replace_callback(
						'/( (?:href|content|action)=["\'](?:\s*[0-9]+\s*;\s*url=\s*)?)(?:'.$abspre.')?(?:w3|(alt))-article-([0-9]+).html(["\'])/i',
                                                array(&$this,'replace_article_callback'),
                                                $text);
                        $text = preg_replace_callback(
                                                '/( (?:href|content|action)=["\'](?:\s*[0-9]+\s*;\s*url=\s*)?)(?:'.$abspre.')?(?:w3|(alt))-propertyname-([0-9]+).html(["\'])/i',
                                                array(&$this,'replace_pn_callback'),
                                                $text);
                        $text = preg_replace_callback(
                                                '/( (?:href|content|action)=["\'](?:\s*[0-9]+\s*;\s*url=\s*)?)(?:'.$abspre.')?(?:w3|(alt))-propertyvalue-([0-9]+).html(["\'])/i',
                                                array(&$this,'replace_pv_callback'),
                                                $text);
                        $text = preg_replace_callback(
                                                '/( (?:href|content|action)=["\'](?:\s*[0-9]+\s*;\s*url=\s*)?)(?:'.$abspre.')?(?:w3|(alt))-multipropertyvalues-([0-9]+)-([0-9]+).html(["\'])/i',
                                                array(&$this,'replace_mpv_callback'),
                                                $text);
			//$text = preg_replace('/<a ([^>]* )?href=["\']w3-channel.html["\']/i','<a ${1}href="'.$this->config["abase"].'"',$text);
			$text = preg_replace('/( (?:href|content|action)=["\'](?:\s*[0-9]+\s*;\s*url=\s*)?)(?:'.$abspre.')?w3-channel.html(["\'])/i','${1}'.$this->config["abase"].'${2}',$text);
			$text = preg_replace('/( (?:href|content|action)=["\'](?:\s*[0-9]+\s*;\s*url=\s*)?)(?:'.$abspre.')?alt-channel.html(["\'])/i','${1}'.$this->config["abase"].'!'.$alt_suffix.'${2}',$text);
			//$text = preg_replace('/<a ([^>]* )?href=["\']('.$abspre.')?w3-contents.html["\']/i','<a ${1}href="'.$this->config["abase"].'Mapa-del-sitio"',$text);
			$text = preg_replace('/( (?:href|content|action)=["\'](?:\s*[0-9]+\s*;\s*url=\s*)?)(?:'.$abspre.')?w3-contents.html(["\'])/i','${1}'.$this->config["abase"].'Mapa-del-sitio${2}',$text);
			$text = preg_replace('/( (?:href|content|action)=["\'](?:\s*[0-9]+\s*;\s*url=\s*)?)(?:'.$abspre.')?alt-contents.html(["\'])/i','${1}'.$this->config["abase"].'Mapa-del-sitio!'.$alt_suffix.'${2}',$text);
			$text = preg_replace('/( (?:href|content|action)=["\'](?:\s*[0-9]+\s*;\s*url=\s*)?)(?:'.$abspre.')?w3-issues.html(["\'])/i','${1}'.$this->config["abase"].'Ingreso${2}',$text);
			$text = preg_replace('/( (?:href|content|action)=["\'](?:\s*[0-9]+\s*;\s*url=\s*)?)(?:'.$abspre.')?alt-issues.html(["\'])/i','${1}'.$this->config["abase"].'Ingreso!'.$alt_suffix.'${2}',$text);
			$text = preg_replace('/<a ([^>]* )?href=["\']#([\w\d\-\_\.\%]*)["\']/i','<a ${1}href="'.$this->config["ubase"].$req.'#${2}"',$text);
			$text = preg_replace('/@import ["\']([\w\d\_\.\%\-][\w\d\_\.\%\/\-]*)["\']/i','@import "'.$this->config["ibase"].'${1}"',$text);

                        $abspreenc = urlencode($this->config["ubase"].$this->config["ibase"]);
                        $abspreenc = str_replace('%','\%',$abspreenc);
                        $text = preg_replace_callback(
                                        '/(.)(?:'.$abspreenc.')(?:w3|(alt))-article-([0-9]+).html(.)/i',
                                                array(&$this,'replace_article_callback_absenc'),
                                                $text);
                        $text = preg_replace_callback(
                                        '/(.)(?:'.$abspreenc.')(?:w3|(alt))-propertyname-([0-9]+).html(.)/i',
                                                array(&$this,'replace_pn_callback_absenc'),
                                                $text);
                        $text = preg_replace_callback(
                                        '/(.)(?:'.$abspreenc.')(?:w3|(alt))-propertyvalue-([0-9]+).html(.)/i',
                                                array(&$this,'replace_pv_callback_absenc'),
                                                $text);
                        $text = preg_replace_callback(
                                        '/(.)(?:'.$abspreenc.')(?:w3|(alt))-multipropertyvalues-([0-9]+)-([0-9]+).html(.)/i',
                                                array(&$this,'replace_mpv_callback_absenc'),
                                                $text);

			// se reemplaza o agrega <base>
			//header('Content-Base:'.$this->config["ibase"].$source);
			if (preg_match('/<base href=/i',$text)) {
				$text = preg_replace('/<base href=[^>]*>/i','<base href="'.$this->config["ibase"].$source.'">',$text);
			} else {
				$text = preg_replace('/<head>/i','<head><base href="'.$this->config["ibase"].$source.'">',$text);
			}
			$text = preg_replace('/<\/head>/i','<link rel="canonical" href="'.$this->config["ubase"].$this->config["ibase"].$source.'"></head>',$text);
		} else {
			header($_SERVER["SERVER_PROTOCOL"]." 404 Not Found");
			$text ="<html><head><title>Error 404 : No encontrado</title></head><body><h1>Error 404 : No encontrado</h1></body></html>";
			$errorfile = $this->config["error_file"];
			if ($errorfile != "" and is_readable($errorfile)) {
				$text = file_get_contents($errorfile);
			}
		}
		if (isset($this->config["cache-control"])) {
			header("Cache-control: max-age=".$this->config["cache-control"]);
		} else {
			header("Cache-control: max-age=".$this->config["refresh"]);
		}
		echo $text;
	}

	function replace_article_callback($matches) {
		$preurl = $matches[1];
		$alt = $matches[2];
		$alt_suffix=!empty($this->config["alt_suffix"])?$this->config["alt_suffix"]:"alt";
		$aid = $matches[3];
		$posturl = $matches[4];
		$auri = !empty($this->cacheURIaid[''.$aid])?$this->cacheURIaid[''.$aid]:null;
		if (is_null($auri)) {
			$artinfo = $this->articleInfo($aid);
			if (!$this->articleIsUpdated($aid,$artinfo)) {
				$this->articleUpdate($aid);
				$artinfo = $this->articleInfo($aid);
			}
			$artinfo["pname"] = preg_replace('/\/+$/','',$artinfo["pname"]);
			$auri = $this->getArticleURI($aid,$artinfo); 
			$cacheURIaid[''.$aid]=$auri;
		}
		return $preurl.$auri.($alt?"!".$alt_suffix:"").$posturl;
	}

	function replace_article_callback_absenc($matches) {
		$matches[1] .= urlencode($this->config['ubase']);
		return $this->replace_article_callback($matches);
	}

	function replace_pn_callback($matches) {
		$preurl = $matches[1];
		$alt = $matches[2];
		$alt_suffix=!empty($this->config["alt_suffix"])?$this->config["alt_suffix"]:"alt";
		$pnid = $matches[3];
		$posturl = $matches[4];
		$pnuri = !empty($this->cacheURIpnid[''.$pnid])?$this->cacheURIpnid[''.$pnid]:null;
		if (is_null($pnuri)) {
			$prop = $this->db->get("ufproperties",array("name"),array('AND'=>array("pnid"=>$pnid,"pvid"=>0)));
			if (!empty($prop["name"])) {
				$pnuri = URLFriendlyizer::amistosiza($this->config["abase"].$prop["name"]).'/';
				$cacheURIpnid[''.$pnid] = $pnuri;
			}
		}
		if (is_null($pnuri)) {
			if ($alt) {
				$pnuri = preg_replace('/\/$/','',$pnuri);
				$pnuri.="!".$alt_suffix."/";
			}
			return $preurl.$pnuri.$posturl;
		}
		return $matches[0];
	}

	function replace_pn_callback_absenc($matches) {
		$matches[1] .= urlencode($this->config['ubase']);
		return $this->replace_pn_callback($matches);
	}

	function replace_pv_callback($matches) {
		$preurl = $matches[1];
		$alt = $matches[2];
		$alt_suffix=!empty($this->config["alt_suffix"])?$this->config["alt_suffix"]:"alt";
		$pvid = $matches[3];
		$posturl = $matches[4];
		$pvuri = !empty($this->cacheURIpvid[''.$pvid])?$this->cacheURIpvid[''.$pvid]:null;
		if (is_null($pvuri)) {
			$prop = $this->db->get("ufproperties",array("name"),array("pvid"=>$pvid));
			if (!empty($prop["name"])) {
				$pvuri = URLFriendlyizer::amistosiza($this->config["abase"].$prop["name"]).'/';
				$this->cacheURIpvid[''.$pvid] = $pvuri;
			}
		}
		if (!empty($pvuri)) {
			if ($alt) {
				$pvuri = preg_replace('/\/$/','',$pvuri);
				$pvuri.="!".$alt_suffix."/";
			}
			return $preurl.$pvuri.$posturl;
		}
		return $matches[0];
	}

	function replace_pv_callback_absenc($matches) {
		$matches[1] .= urlencode($this->config['ubase']);
		return $this->replace_pv_callback($matches);
	}

	function replace_mpv_callback($matches) {
		$preurl = $matches[1];
		$alt = $matches[2];
		$alt_suffix=$this->config["alt_suffix"]?$this->config["alt_suffix"]:"alt";
		$pvid1 = $matches[3];
		$pvid2 = $matches[4];
		$posturl = $matches[5];
		$pv1uri = $this->cacheURIpvid[''.$pvid1];
		$pv1pnid = $this->cachePNpvid[''.$pvid1];
		$pv2uri = $this->cacheURIpvid[''.$pvid2];
		$pv2pnid = $this->cachePNpvid[''.$pvid2];
		if (!isset($pv1uri) || !isset($pv1pnid)) {
			$prop = $this->db->get("ufproperties",array("name","pnid"),array("pvid"=>$pvid1));
			if ($prop["name"] && $prop["pnid"]) {
				$pv1uri = URLFriendlyizer::amistosiza($prop["name"]);
				$this->cacheURIpvid[''.$pvid1] = $pv1uri;
				$pv1pnid = $prop["pnid"];
				$this->cachePNpvid[''.$pvid1] = $pv1pnid;
			}
		}
		if (!isset($pv2uri) || !isset($pv2pnid)) {
			$prop = $this->db->get("ufproperties",array("name","pnid"),array("pvid"=>$pvid2));
			if ($prop["name"] && $prop["pnid"]) {
				$pv2uri = URLFriendlyizer::amistosiza($prop["name"]);
				$this->cacheURIpvid[''.$pvid2] = $pv2uri;
				$pv2pnid = $prop["pnid"];
				$this->cachePNpvid[''.$pvid2] = $pv2pnid;
			}
		}
		if (isset($pv1uri) && isset($pv2uri) && isset($pv1pnid) && isset($pv2pnid)) {
			$mpvuri="";
			if ($this->config["properties"][$pv1pnid]["priority"] <= $this->config["properties"][$pv2pnid]["priority"]) {
				$mpvuri = $this->config["abase"].$pv1uri.';'.$pv2uri.($alt?"!".$alt_suffix:"").'/';
			} else {
				$mpvuri = $this->config["abase"].$pv2uri.';'.$pv1uri.($alt?"!".$alt_suffix:"").'/';
			}
			return $preurl.$mpvuri.$posturl;
		}
		return $matches[0];
	}

	function replace_mpv_callback_absenc($matches) {
                $matches[1] .= urlencode($this->config['ubase']);
                return $this->replace_mpv_callback($matches);
        }

	function checkPNUpdates() {
		$pnids = array_keys($this->config["properties"]);
		for ($i=0;$i<count($pnids);$i++) {
			if (!$this->pnidIsUpdated($pnids[$i])) {
				$this->pnidUpdate($pnids[$i]);
			}
		}
	}

	function readConfig() {
		$this->config = json_decode(file_get_contents(dirname(__FILE__).'/urlfriendly.conf.json'),true);
	}

	function normalize($txt) {
		$string = strtolower($txt);
		$string = trim($string);
		$string = strtr($string,"àáâãäåæçèéêëìíîïðñòóôõöøùúûÁÉÍÓÚÑ","aaaaaaaceeeeiiiidnoooooouuuaeioun");
		$string = preg_replace("/[^a-z0-9\_\/]/","_",$string);
		$string = preg_replace("/\_\_+/","_",$string);
		$string = trim($string,"_");
		return $string;
	}

	function amistosiza($txt) {
		$string = trim($txt);
		$string = strtr($string,"àáâãäåæçèéêëìíîïðñòóôõöøùúûÁÉÍÓÚÑ","aaaaaaaceeeeiiiidnoooooouuuAEIOUN");
		$string = preg_replace("/[^a-zA-Z0-9\-\+\/]/","-",$string);
		$string = preg_replace("/\-\-+/","-",$string);
		$string = trim($string,"-");
		return $string;
	}

	function getArticleURI($aid,$artinfo) {
		$auri = $this->config["abase"].$aid.":";
		if (isset($artinfo) && isset($artinfo["aname"])) {
			//echo "<!-- $aid info ".print_r($artinfo,1)." -->";
			$artinfo["pname"] = preg_replace('/\/+$/','',$artinfo["pname"]);
			$auri = $this->config["abase"].URLFriendlyizer::amistosiza($artinfo["pname"])."/".$aid.":".URLFriendlyizer::amistosiza($artinfo["aname"]);
		} else {
			$src = $this->config["idir"]."w3-article-".$aid.".html";
			//echo "<!-- $aid src $src -->";
			$fh = @fopen($src,"r");
			$title = "";
			if ($fh) {
				while (($line = fgets($fh)) !== false) {
					if (preg_match('/<meta name="DC.title" content="/i',$line)) {
						$title = preg_replace('/^.*<meta name="DC.title" content="([^"]*)".*/i','${1}',$line);
						break;
					}
				}
				fclose($fh);
			}
			$auri = $this->config["abase"].$aid.":".URLFriendlyizer::amistosiza($title);
		}
		return $auri;
	}

	function pnidIsUpdated($pnid) {
		if ($pnid && isset($this->config["properties"]["".$pnid])) {
			$pnupdated = $this->db->get("ufproperties","updated",array('AND'=>array('pnid'=>$pnid,'pvid'=>0))) + 0;
			$pnw3file = $this->config["idir"]."w3-propertyname-".$pnid.".html";
			$pnpublished = 0;
			if (is_readable($pnw3file)) {
				$pnpublished = filemtime($pnw3file);
			}
			$refresh  = !empty($this->config["properties"]["".$pnid]["refresh"])?$this->config["properties"]["".$pnid]["refresh"]:(!empty($this->config["refresh"])?$this->config["refresh"]:$this->default_refresh);
			//echo "\n<!-- pnid=$pnid refresh=$refresh time=".time()." pnupd=$pnupdated pnpublished=$pnpublished -->\n";
			return $pnpublished <= $pnupdated && time() - $pnupdated <= $refresh ;
		}
		return FALSE;
	}

	function pnidUpdate($pnid) {
		$lockfile = $this->config["datadir"].$this->config["id"]."-pn-".$pnid.".lock";
		//echo "<!-- updating $pnid -->";
		if (! $lh = fopen($lockfile,'w')) {
			//Si no puedo abrir archivo de lock continuo con data antigua
			//echo "<!-- lock error $pnid -->";
			return;
		}
		if (flock($lh,LOCK_EX | LOCK_NB)) {
			$contents = FALSE;
			$resturl = $this->config["restbase"]."PropertyValue/".$this->config["cid"]."/pnid/".$pnid."/list/json?require-body-content=0&require-propertybinaries=0&donotrequire-binaries=1";
			if (isset($this->config["restcookies"])) {
				$restcontext = stream_context_create(['http' => ['header'=> 'Cookie: ' . $this->config["restcookies"] ."\r\n",'timeout' => 900], "ssl"=>["verify_peer"=>false,"verify_peer_name"=>false]]);
				$contents = file_get_contents( $resturl, false, $restcontext);
			} else {
				$contents = file_get_contents( $resturl );
			}

			if ($contents === FALSE) {
				// Fallo la consulta WS a Engine, mantengo info anterior
				//echo "<!-- Error al actualizar clasificando $pnid -->";
				return;
			}

			$newpnupdated = time();

			if (!preg_match('/utf-?8/i',$this->config["engine-charset"])) {
				$contents=utf8_encode($contents);
			}
			$pndata = json_decode($contents,true);

			$pnconfig = $this->config["properties"]["".$pnid];
			$pnbase = $pnconfig["base"];
			$pnbase = preg_replace("/^\/+|\/+$/","",$pnbase);
			$pnname = utf8_decode($pnconfig["base"]!=""?$pnconfig["base"]:$pnconfig["name"]);
			$pnname = preg_replace("/^\/|\/$/","",$pnname);
			$pnfields = array();
			$pnfields[] = array(
					"pvid"=>0,
					"pnid"=>(int)$pnid,
					"name"=>$pnname,
					"ufname"=>URLFriendlyizer::normalize($pnname),
					"priority"=>(int)$pnconfig["priority"],
					"updated"=>$newpnupdated
				);
			if (!empty($pndata["propertyvalue"]) && is_array($pndata["propertyvalue"]) && empty($pndata["propertyvalue"][0]) && !empty($pndata["propertyvalue"]["name"])) {
				$pndata["propertyvalue"] = [$pndata["propertyvalue"]];
			}
			for ($j=0;is_array($pndata["propertyvalue"]) && $j<count($pndata["propertyvalue"]);$j++) {
				$pv = $pndata["propertyvalue"][$j];
				$pv["name"] = str_replace("/","-",$pv["name"]);
				//$wpath = "";
				$npath = $pnbase;
				if (!empty($pv["expanded"]) && is_array($pv["expanded"]) && count($pv["expanded"])) {
					$pepv = $pv["expanded"];
					if (!empty($pepv["propertyvalue"][0])) {
						$epv = $pepv["propertyvalue"];
					} else {
						$epv = array($pepv["propertyvalue"]);
					}
					if (count($epv)) {
						for($k=0;$k<count($epv);$k++) {
							//if ($epv[$k]["webname"] != "") {
							//	$wpath .= "/".utf8_decode($epv[$k]["webname"]);
							//}
							if ($epv[$k]["name"] != "") {
								$epv[$k]["name"] = str_replace("/","-",$epv[$k]["name"]);
								$npath .= "/".utf8_decode($epv[$k]["name"]);
							}
						}
					}
				}
				//$wpath .= "/".utf8_decode($pv["webname"]);
				//if (preg_match('/\/\s*(\/|$)/',$wpath)) {
				//	$wpath="";
				//}
				$npath .= "/".utf8_decode($pv["name"]);
				//$wpath = preg_replace('/^\s*\/\s*/','',$wpath);
				$npath = preg_replace('/^\s*\/\s*/','',$npath);
				//$pnuri = URLFriendlyizer::normalize($pnbase);
				//if ($pnuri == "") {
				//	$pnuri.="/";
				//}
				//$pnuri.=URLFriendlyizer::normalize($npath);
				$pnfields[] = array(
					"pvid"=>(int)$pv["pvid"],
					"pnid"=>(int)$pnid,
					"name"=>$npath,
					"ufname"=>URLFriendlyizer::normalize($npath),
					"priority"=>(int)$pnconfig["priority"],
					"updated"=>$newpnupdated
				);
			}
			$this->db->delete("ufproperties",array("pnid"=>(int)$pnid));
			//echo "<!-- pnfields : ".print_r($pnfields,1)." -->";
			$this->db->insert("ufproperties",$pnfields);
			//echo "<!-- db error : ".print_r($this->db->error(),1)." -->";
			flock($lh,LOCK_UN);
			fclose($lh);
			unlink($lockfile);
		} else {
			//No pude obtener lock, hay otro proceso actualizando.
			//continuo con data antigua mientras el otro proceso sigue actualizando
			fclose($lh);
		}
	}

	function articleInfo($aid) {
		$artinfo = $this->db->select("ufarticles",array("[>]ufproperties"=>"pvid"),array("ufarticles.name(aname)","ufproperties.name(pname)","ufproperties.pvid(pvid)","ufproperties.pnid(pnid)","ufarticles.updated(aupdated)"),array("ufarticles.aid"=>$aid));
		return $artinfo[0];
	}

	function articleIsUpdated($aid,$artinfo) {
		$aw3file = $this->config["idir"]."w3-article-".$aid.".html";
		$apublished = 0;
		if (is_readable($aw3file)) {
			$apublished = filemtime($aw3file);
		}
		$refresh  = $this->config["refresh"];
		if (!isset($refresh)) {
			$refresh = $this->default_refresh;
		}
		return isset($artinfo) && (intval($artinfo["aupdated"]) >= $apublished) && (time() - intval($artinfo["aupdated"]) <=  $refresh);
	}

	function articleUpdate($aid) {
		$lockfile = $this->config["datadir"].$this->config["id"]."-a-".$aid.".lock";
		if (! $lh = fopen($lockfile,'w')) {
			//Si no puedo abrir archivo de lock continuo con data antigua
			return;
		}
		if (flock($lh,LOCK_EX | LOCK_NB)) {
			//echo "<!-- Article($aid) update -->";
			$contents = FALSE;
			$resturl = $this->config["restbase"]."Article/".$this->config["cid"]."/id/".$aid."/json?require-body-content=0&require-propertybinaries=0&donotrequire-binaries=1";
			if (isset($this->config["restcookies"])) {
				$restcontext = stream_context_create(['http' => ['header'=> 'Cookie: ' . $this->config["restcookies"] ."\r\n",'timeout' => 900], "ssl"=>["verify_peer"=>false,"verify_peer_name"=>false]]);
				$contents = file_get_contents( $resturl, false, $restcontext);
			} else {
				$contents = file_get_contents( $resturl );
			}

			if ($contents === FALSE) {
				// Fallo la consulta WS a Engine, mantengo info anterior
				return;
			}

			$newaupdated = time();

			if (!preg_match('/utf-?8/i',$this->config["engine-charset"])) {
				$contents=utf8_encode($contents);
			}
			$adata = json_decode($contents,true);

			$aprops = $adata["properties"]["property"];
			if (isset($aprops["property-value"])) {
				$aprops = array($aprops);
			}
			if (!is_array($aprops)) {
				$aprops=[];
			}
			$pvidcand = 0;
			//$pvidncand = "";
			$pvidpcand = 999999;
			$pnidcand = 0;
			$pnidpcand = 999999;
			for($i=0;$i<count($aprops);$i++) {
				$ipnid = $aprops[$i]["pnid"];
				if (isset($this->config["properties"][$ipnid]) && $this->config["properties"][$ipnid]["priority"]<=$pnidpcand) {
					$ppnid = $this->config["properties"][$ipnid]["priority"];
					if (isset($aprops[$i]["property-value"]["pvid"])) {
						$aprops[$i]["property-value"] = array($aprops[$i]["property-value"]);
					}
					//echo "values:".print_r($aprops[$i]["property-value"],true)."\n";
					for($k=0;$k<count($aprops[$i]["property-value"]);$k++) {
						$kpv = $aprops[$i]["property-value"][$k];
						if ($kpv["priority"]<$pvidpcand || $ipnid != $pnidcand) {
							$pvidcand = $kpv["pvid"];
							//$pvidncand = $kpv["name"];
							$pvidpcand = $kpv["priority"];
							$pnidcand = $ipnid;
							$pnidpcand = $ppnid;
						}
					}
				}
			}
			$this->db->delete("ufarticles",array("aid"=>$aid));
			$adata["name"] = str_replace("/","-",$adata["name"]);
			$this->db->insert("ufarticles",array("aid"=>$aid,"name"=>utf8_decode($adata["name"]),"pvid"=>$pvidcand,"updated"=>$newaupdated));
			//echo "<!-- db error : ".print_r($this->db->error(),1)." -->";
			flock($lh,LOCK_UN);
			fclose($lh);
			unlink($lockfile);
		} else {
			//No pude obtener lock, hay otro proceso actualizando.
			//continuo con data antigua mientras el otro proceso sigue actualizando
			fclose($lh);
		}
	}

	function resolveCanonicalURI ($iid,$file) {
		$uri;
		#$rreq = "/".$iid."/".$file;
		$rreq = $file;
		$alt_suffix="!".(!empty($this->config["alt_suffix"])?$this->config["alt_suffix"]:"alt");

		if (!$file || !file_exists($this->config["idir"].$file)) {
			$uri = $this->config["ibase"].$file."?__noredirect=1";
		} else if (preg_match("/(w3|alt).article.([0-9]+).html/",$rreq,$matches)) {
			$artinfo = $this->articleInfo($matches[2]);
			if (!$this->articleIsUpdated($matches[2],$artinfo)) {
				$this->articleUpdate($matches[2]);
				$artinfo = $this->articleInfo($matches[2]);
			}
			$uri = $this->getArticleURI($matches[2],$artinfo).($matches[1]=="alt"?$alt_suffix:"");
		} else if (preg_match("/(w3|alt).propertyvalue.([0-9]+).html/",$rreq,$matches)) {
			$prop = $this->db->get("ufproperties",array("name"),array("pvid"=>$matches[2]));
			if ($prop["name"]) {
				$uri = URLFriendlyizer::amistosiza($this->config["abase"].$prop["name"]).($matches[1]=="alt"?$alt_suffix:"").'/';
			}
		} else if (preg_match("/(w3|alt).propertyname.([0-9]+).html/",$rreq,$matches)) {
			$prop = $this->db->get("ufproperties",array("name"),array('AND'=>array("pnid"=>$matches[2],"pvid"=>0)));
			if ($prop["name"]) {
				$uri = URLFriendlyizer::amistosiza($this->config["abase"].$prop["name"]).($matches[1]=="alt"?$alt_suffix:"").'/';
			}
		} else if (preg_match("/(w3|alt).multipropertyvalues.([0-9]+)-([0-9]+).html/",$rreq,$matches)) {
			$pv1uri = "";
			$pv1pnid = 0;
			$pv2uri = "";
			$pv2pnid = 0;
			$prop = $this->db->get("ufproperties",array("name","pnid"),array("pvid"=>$matches[2]));
			if ($prop["name"] && $prop["pnid"]) {
				$pv1uri = URLFriendlyizer::amistosiza($prop["name"]);
				$pv1pnid = $prop["pnid"];
			}
			$prop = $this->db->get("ufproperties",array("name","pnid"),array("pvid"=>$matches[3]));
			if ($prop["name"] && $prop["pnid"]) {
				$pv2uri = URLFriendlyizer::amistosiza($prop["name"]);
				$pv2pnid = $prop["pnid"];
			}
			if ($pv1uri != "" && $pv2uri != "" && $pv1pnid>0 && $pv2pnid>0) {
				if ($this->config["properties"][$pv1pnid]["priority"] <= $this->config["properties"][$pv2pnid]["priority"]) {
					$uri = $this->config["abase"].$pv1uri.';'.$pv2uri.($matches[1]=="alt"?$alt_suffix:"").'/';
				} else {
					$uri = $this->config["abase"].$pv2uri.';'.$pv1uri.($matches[1]=="alt"?$alt_suffix:"").'/';
				}
			}
		} else if (preg_match("/(w3|alt).channel.html/",$rreq,$matches)) {
			$uri = $this->config["abase"].($matches[1]=="alt"?$alt_suffix:"");
		} else if (preg_match("/(w3|alt).contents.html/",$rreq,$matches)) {
			$uri = $this->config["abase"]."Mapa-del-sitio".($matches[1]=="alt"?$alt_suffix:"");
		} else if (preg_match("/(w3|alt).issues.html/",$rreq,$matches)) {
			$uri = $this->config["abase"]."Ingreso".($matches[1]=="alt"?$alt_suffix:"");
		}
		if (!isset($uri)) {
			$uri = $this->config["ibase"].$file."?__noredirect=1";
		}
		return $uri;
	}

	function redirectToCanonicalURI($iid,$file) {
		$uri = $this->resolveCanonicalURI($iid,$file); 
		#header("Access-Control-Allow-Origin: ".preg_replace('/\/mod\/rest.*/','',$this->config["restbase"]));
		header("Location: ".$uri);
		exit;
	}

	function jsonCanonicalURI($iid,$file) {
		$uri = $this->resolveCanonicalURI($iid,$file);
		header('Content-type:application/json');
		header("Access-Control-Allow-Origin: ".preg_replace('/\/mod\/rest.*/','',$this->config["restbase"]));
		echo json_encode(array("redirect"=>$uri));
		exit;
	}
}

?>
