Linkchecker - Vermeidung von deadlinks

Fragen zur Installation von CONTENIDO 4.9? Probleme bei der Konfiguration? Hinweise oder Fragen zur Entwicklung des Systemes oder zur Sicherheit?
Antworten
jkv
Beiträge: 6
Registriert: So 8. Mai 2005, 14:55
Kontaktdaten:

Linkchecker - Vermeidung von deadlinks

Beitrag von jkv »

Hallo zusammen,
ich bin noch ziemlich neu mit Contenido, aber ganz gut unterwegs mit PHP & MySQL.

Das CMS ist super, sehr übersichtlich und leicht zu erweitern. Großes Lob an 4fb. Mir fehlt jedoch eine Linkchecker-Funktionalität, die mich warnt, wenn ich eine manuell verlinkte Seite löschen will. timo hat vor langer Zeit schon in einem Thread geschrieben, dass es nicht so einfach sei.
Ich würde es aber trotzdem gerne versuchen und würde mich sehr freuen, wenn jemand noch Anregungen zur Umsetzung bringt. Oder vielleicht auch schreibt, dass es Quatsch ist was ich vorhabe (Hab nämlich keine Ahnung, wie andere CMS dies umsetzen).

Ich sehe zwei Möglichkeiten, zunächst nur für den Typ Text/HTML; beide kommen erst zum Zug, wenn ein Artikel gelöscht werden soll. Dem Redakteur wird dann mitgeteilt auf welchen Seiten auf die zu löschende Seite verlinkt wird. Er muss dann selber entscheiden, wie er vorgeht.

1) An der Datenbankstruktur wird nichts verändert. Mit einer Mysql-Abfrage (z.B. "select * from con_content where idtype=2 and value like '%idcat\%3D10%'" für den Artikel mit der Nummer 10) suche ich die Seiten raus, die auf die zu löschende verlinken und liste sie auf. Ich denke mal das ist wegen dem Rechenaufwand für größere Sites aber nicht praktikabel.

2) Die Datenbank bekommt eine neue Tabelle. Beim Speichern von einem Artikel wird dieser direkt nach intenen Links durchgesucht. In die Tabelle kommen dann Werte für die beiden Seiten rein: idcat_von und idcat_nach oder so. Beim Löschen muss die Tabelle dann nur ausgelesen werden. Auch für größere Sites geeignet.

Was denkt ihr? Ich hoffe es schreibt keiner, dass es sowas schon gibt :), denn ich hab länger gesucht und nichts gefunden!!

Viele Grüße,
jkv
emergence
Beiträge: 10653
Registriert: Mo 28. Jul 2003, 12:49
Wohnort: Austria
Kontaktdaten:

Beitrag von emergence »

also geben tuts das soweit ich weiss für contenido noch nicht...
ob es jemand anderes schreibt ???

na wie auch immer...

wenn du auf den typ CMS_HTML diese prüfung vornimmst kannst du an sich CMS_HTMLHEAD ebenso gleich mit einbinden. die beiden können nämlich gleich behandelt werden...

nur nach idcat zu suchen wird es aber nicht sein..
eine überprüfung auf idart bzw auf idcatart würde für interne links ebenso benötigt werden...

ich würde nach front_content.php? suchen lassen und alle gefundenen matches entsprechend auswerten...

die einzig andere sache die bezüglich links dann noch offen wäre -> CMS_LINK -> dieser speichert entweder die idcatart als ziffer oder einen string -> wenn string muss wieder geprüft werden ob front_content.php dort gespeichert ist...
*** make your own tools (wishlist :: thx)
jkv
Beiträge: 6
Registriert: So 8. Mai 2005, 14:55
Kontaktdaten:

Beitrag von jkv »

Super, vielen Dank für die Tipps emergence, ich werde darauf aufbauen und die Ergebnisse hier mitteilen (kann aber ein bisschen dauern).

Viele Grüße,
jkv
jkv
Beiträge: 6
Registriert: So 8. Mai 2005, 14:55
Kontaktdaten:

Beitrag von jkv »

Hallo miteinander,
ich hab hier die erste Lösung parat. Ich habs nur in der Version 4.4.5r1 getestet, aber ich denke, das funktioniert genauso in anderen.
Das Ergebnis sieht momentan so aus:
Bild
Nach dem Löschen eines Artikels wird dem Redakteur mitgeteilt, welche Container noch auf den gelöschten Artikel verweisen.

Um das zu realisieren sind folgende zwei Codeänderungen/-ergänzungen notwendig:
In der MySQL Tabelle actions (con_actions) muss in der Zeile "name='con_deleteart'" das Feld "code" folgenden Inhalt erhalten:

Code: Alles auswählen

$linkcheck=conLinkcheck($idart);
$lm="";
foreach ($linkcheck as $lc) {
 $lm.="<BR />".$lc."\n";
}
if ($lm!="") $lm="<BR /><BR />Folgende Artikel verweisen noch auf den eben gel&oumlschten Artikel:".$lm;
conDeleteArt ($idart);
$tmp_notification = $notification->returnNotification("info", i18n("Artikel gel&ouml;scht".$lm));
conLinkcheck liefert ein Array mit den Clickstreams zu den Artikeln, die einen Link auf den gelöschten Artikel beinhalten.

Die Funktion conLinkcheck habe ich in contenido/includes/functions.con.php eingefügt. Diese sieht so aus:

Code: Alles auswählen

function conLinkcheck($idart) {
    global $db, $cfg;
	// einige Werte initialisieren
    $ClickStreamSeperator="&nbsp;>&nbsp;";
    $linkerArts = array(); // wird returned
    $idcat=array();
    $idcatart=array();

    // alle idcat und idcatart rausfischen, die bei verlinkung auf idart führen
    $sql = "SELECT * FROM ".$cfg["tab"]["cat_art"]." WHERE idart = '".$idart."'";
    $db->query($sql);

    while ( $db->next_record() ) {
        $idcatart[] = $db->f("idcatart");
    	// alle idcat mit speichern, die idart als startartikel haben
    	if ($db->f("is_start")==1) $idcat[] = $db->f("idcat");
    }

    
    // hier beginnt die suche. alle CMS_HTMLHEAD und CMS_HTML typen nach front_content.php durchsuchen
    $sql = "SELECT
                a.idcontent AS idart_linker,
                a.value AS value,
                b.idcat AS idcat_linker,
                c.title AS arttitle
            FROM
                ".$cfg["tab"]["content"]." AS a,
                ".$cfg["tab"]["cat_art"]." AS b,
                ".$cfg["tab"]["art_lang"]." AS c
            WHERE
                (a.idtype = 1 OR a.idtype = 2) AND
            	a.value LIKE '%front_content.php%' AND
                a.idcontent = b.idart AND
                a.idcontent = c.idart";
    
    $db->query($sql);

    // jetzt prüfen, ob auf die idart seite verlinkt wird
    while ( $db->next_record() ) {
		$htmlDecoded=urldecode($db->f("value"));
    	$tmpoffset=0;
    	$HitsOnThisPage=0;
    	while ($tmpoffset=strpos($htmlDecoded,"front_content.php?", $tmpoffset)) {
    	 	// strlen("front_content.php?") == 18; hol dir nur die parameter; alles zwischen ? und "
    		$toScan=substr($htmlDecoded, $tmpoffset+18, strpos($htmlDecoded,"\"", $tmpoffset)-$tmpoffset-18);
			$URLParameter = explode("&", $toScan);
			foreach ($URLParameter as $UP) {
				list($tmpname, $tmpvalue) = explode("=", $UP);
				$TrifftZu=false;
				switch ($tmpname) {
					case "idcat":
						if (in_array($tmpvalue, $idcat)) $TrifftZu=true;
						break;		
					case "idcatart":
						if (in_array($tmpvalue, $idcatart)) $TrifftZu=true;
						break;		
					case "idart":
						if ($tmpvalue==$idart) $TrifftZu=true;
						break;		
				}
				if ($TrifftZu) $HitsOnThisPage++;
			}
    	 	$tmpoffset++; // damit er nicht immer die selbe stelle findet
    	}
    	if ($HitsOnThisPage!=0) {
    		$clickstream="";
    		conCreateLocationString($db->f("idcat_linker"), $ClickStreamSeperator, $clickstream);
    		$linkerArts[] = $HitsOnThisPage."x ".$clickstream.$ClickStreamSeperator.$db->f("arttitle");
    	}
    }
    return $linkerArts;
}
Kurze Erläuterung:
Ich hole mir zuerst alle idcat und idcatart über die der zu löschende Artikel erreichbar ist. Dann fische ich alle CMS_HTML und CMS_HTMLHEAD (haben die immer idtype 1 und 2?) Container, wo im "value" der String "front_content.php?" vorkommt. Gleichzeitig besorge ich mir den Titel des Artikels von diesem Container, sowie seine Kategorie, die beide für den Clickstream benötigt werden. Dann wird nur noch geguckt, ob der Link auf die zu löschende Seite verweist. Wenn ja, wird dieser Artikel in das Array mit aufgenommen, welches die Funktion zurückliefert.

Funktioniert bei mir einwandfrei. Ich habe noch einige Fragen, auch diese Funktion betreffend, vielleicht kann mir da einer helfen:
In der Datenbank: Ist das Verhältnis con_content.idcontent=con_art.idart richtig?
Ein Link auf eine Contenido Seite hat doch immer nur idart, idcat oder idcatart als Parameter, aber niemals eine Mischung, oder? Würde doch auch keinen Sinn machen?
Und die wichtigste Frage: Momentan (wie im Screenshot zu sehen) werden die gefundenen Artikel in dem Notification Feld ausgegeben. Was nicht gerade benutzerfreundlich ist. Schöner wäre es, wenn ein Popup mit dieser Artikelliste aufspringen würde und der Redakteur, wenn er sich an die Änderungen begibt, immer zu dem Popupfenster zurückkehren kann um sich die Liste nochmal anzuschauen. Contenido hat ja dafür die Javascript Funktion messageBox.notify. Hat Contenido eine Funktion um die Ausführung von Javascript Code zu veranlassen? Ich könnte auch einen Javascript Block in das Notification Feld reingeben, aber das wäre leicht unsauber.

Für Kritik und/oder Verbesserungsvorschäge wäre ich sehr dankbar.

MfG,
jkv
emergence
Beiträge: 10653
Registriert: Mo 28. Jul 2003, 12:49
Wohnort: Austria
Kontaktdaten:

Beitrag von emergence »

Ein Link auf eine Contenido Seite hat doch immer nur idart, idcat oder idcatart als Parameter, aber niemals eine Mischung, oder?
doch eine kombination von zwei werte ist möglich und zwar bei

front_content.php?idcat=X&idart=Y (kategorieid - artikelid)

---

die anderen varianten

front_content.php?idcatart=Z (artikelid in einer bestimmten kategorie)

front_content.php?idcat=X (kategorie id)

front_content.php?idart=Y (artikelid - funktioniert glaube ich erst ab > 4.5.x)
*** make your own tools (wishlist :: thx)
jkv
Beiträge: 6
Registriert: So 8. Mai 2005, 14:55
Kontaktdaten:

Beitrag von jkv »

Hallo,
hab noch einige Änderungen vorgenommen:
- front_content.php?idcat=X&idart=Y (kategorieid - artikelid) wird korrekt verarbeitet
- Wenn der gelöschte Artikel als Startartikel eingestellt war und andere Artikel auf die Kategorie des gelöschten verweisen, wird das jetzt nicht mehr erfasst (also ein Link wie front_content.php?idcat=X verweist ja nicht direkt auf den Artikel. Das kann zur Verwirrung führen.)
- Die Liste mit den verweisenden Artikel öffnet man jetzt in einem neuen Fenster.

Drei Änderungen sind nötig (in der Version 4.4.5 getestet):

Datenbanktabelle con_actions in der Zeile name=con_deleteart:

Code: Alles auswählen

$linkcheck=conLinkcheck($idart);
$lm="";
foreach ($linkcheck as $l=>$lc) {
 $lm.="<br>$lc x $l";
}
if ($lm!="") $lm="<BR /><BR /><A HREF=\"javascript://\" onClick=\"boxLC.notify('Artikelliste', 'Artikel mit Links auf den gel&ouml;schten Artikel:<br><br>$lm')\">Auf den gel&ouml;schten Artikel existieren noch Links. Hier klicken f&uuml;r die Liste.</A>";
conDeleteArt ($idart);
$tmp_notification = $notification->returnNotification("info", i18n("Artikel gel&ouml;scht".$lm));
In contenido/templates/standard/template.con_art_overview.html, unter box = new messageBox("", "", "", 0, 0); noch

Code: Alles auswählen

// Box für den Linkchecker
boxLC = new messageBox("", "", "", 450, 200);
einfügen.

Und zuletzt die Funktion selbst. Die hab ich ganz unten in contenido/includes/functions.con.php eingefügt:

Code: Alles auswählen

function conLinkcheck($idart) {
	global $db, $cfg;
	// einige Werte initialisieren
	$ClickStreamSeperator="&nbsp;>&nbsp;";
	$linkerArts = array(); // wird returned
	$idcat=array();
	$idcatart=array();

	// alle idcat und idcatart rausfischen, die bei verlinkung auf idart führen
	$sql = "SELECT * FROM ".$cfg["tab"]["cat_art"]." WHERE idart = '".$idart."'";
	$db->query($sql);

	while ( $db->next_record() ) {
		$idcatart[] = $db->f("idcatart");
		$idcat[] = $db->f("idcat");
	}


	// hier beginnt die suche. alle CMS_HTMLHEAD und CMS_HTML typen nach front_content.php durchsuchen
	$sql = "SELECT
                a.idcontent AS idart_linker,
                a.value AS value,
                b.idcat AS idcat_linker,
                c.title AS arttitle
            FROM
                ".$cfg["tab"]["content"]." AS a,
                ".$cfg["tab"]["cat_art"]." AS b,
                ".$cfg["tab"]["art_lang"]." AS c
            WHERE
                (a.idtype = 1 OR a.idtype = 2) AND
            	a.value LIKE '%front_content.php%' AND
                a.idcontent = b.idart AND
                a.idcontent = c.idart";

	$db->query($sql);

	// jetzt prüfen, ob auf die idart seite verlinkt wird
	while ( $db->next_record() ) {
		$htmlDecoded=html_entity_decode(urldecode($db->f("value")));
		$tmpoffset=0;
		$HitsOnThisPage=0;
		while ($tmpoffset=strpos($htmlDecoded,"front_content.php?", $tmpoffset)) {
			// strlen("front_content.php?") == 18; hol dir nur die parameter; alles zwischen ? und "
			$toScan=substr($htmlDecoded, $tmpoffset+18, strpos($htmlDecoded,"\"", $tmpoffset)-$tmpoffset-18);
			$URLParameter = explode("&", $toScan);
			$TrifftZu=array();
			foreach ($URLParameter as $UP) {
				list($tmpname, $tmpvalue) = explode("=", $UP);
				switch ($tmpname) {
					case "idcat":
						if (in_array($tmpvalue, $idcat)) $TrifftZu['idcat']=true;
						break;
					case "idcatart":
						if (in_array($tmpvalue, $idcatart)) $TrifftZu['idcatart']=true;
						break;
					case "idart":
						if ($tmpvalue==$idart) $TrifftZu['idart']=true;
						break;
				}
			}
			if (@$TrifftZu['idcatart']) {
				$HitsOnThisPage++;
			} elseif (@$TrifftZu['idcat'] && @$TrifftZu['idart']) {
				$HitsOnThisPage++;
			} elseif (@$TrifftZu['idart']) {
				$HitsOnThisPage++;
			}
			$tmpoffset++; // damit er nicht immer die selbe stelle findet
		}
		if ($HitsOnThisPage!=0) {
			$clickstream="";
			conCreateLocationString($db->f("idcat_linker"), $ClickStreamSeperator, $clickstream);
			$clickstream = $clickstream.$ClickStreamSeperator.$db->f("arttitle");
			$linkerArts[$clickstream] += $HitsOnThisPage;
		}
	}
	return $linkerArts;
}
Ich hoffe jemand kann damit was anfangen.
Gruss
jkv
Antworten