<?xml version="1.0" encoding="UTF-8"?>
<rss version="2.0"
	xmlns:content="http://purl.org/rss/1.0/modules/content/"
	xmlns:wfw="http://wellformedweb.org/CommentAPI/"
	xmlns:dc="http://purl.org/dc/elements/1.1/"
	xmlns:atom="http://www.w3.org/2005/Atom"
	xmlns:sy="http://purl.org/rss/1.0/modules/syndication/"
	xmlns:slash="http://purl.org/rss/1.0/modules/slash/"
	>

<channel>
	<title>Webeo Webagentur</title>
	<atom:link href="http://www.webeo.ch/feed" rel="self" type="application/rss+xml" />
	<link>https://www.webeo.ch</link>
	<description>kreativ und modern</description>
	<lastBuildDate>Thu, 18 Aug 2011 15:12:25 +0000</lastBuildDate>
	<language>en</language>
	<sy:updatePeriod>hourly</sy:updatePeriod>
	<sy:updateFrequency>1</sy:updateFrequency>
	<generator>http://wordpress.org/?v=3.2.1</generator>
		<item>
		<title>AJAX Suche in WordPress mit Autovervollständigung</title>
		<link>https://www.webeo.ch/journal/ajax-suche-in-wordpress-mit-autovervollstandigung</link>
		<comments>https://www.webeo.ch/journal/ajax-suche-in-wordpress-mit-autovervollstandigung#comments</comments>
		<pubDate>Mon, 15 Aug 2011 21:43:32 +0000</pubDate>
		<dc:creator>Roman</dc:creator>
				<category><![CDATA[Anleitung]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">https://www.webeo.ch/?p=1083</guid>
		<description><![CDATA[Das Laden von Inhalt ohne die Seite selbst neu laden zu müssen, ist und bleibt eines der Elemente um dem Benutzer mehr Komfort zu bieten. Die grossen Suchmaschinen machen es vor. Die Rede ist von AJAX (Asynchronous JavaScript And XML). Hierbei wird die Anfrage (Request) statt über eine serverseitige Scriptsprache mittels JavaScript an den Server [...]]]></description>
			<content:encoded><![CDATA[<p>Das Laden von Inhalt ohne die Seite selbst neu laden zu müssen, ist und bleibt eines der Elemente um dem Benutzer mehr Komfort zu bieten. Die grossen Suchmaschinen machen es vor. Die Rede ist von <b>AJAX (Asynchronous JavaScript And XML)</b>.</p>
<p>Hierbei wird die Anfrage (Request) statt über eine serverseitige Scriptsprache mittels JavaScript an den Server gestellt. Der Vorteil liegt darin begründet, dass JavaScript auf der Clientseite ausgeführt wird. Der Webbrowser muss die aktuelle Seite daher nicht Neu laden sondern kann die DOM-Elemente direkt manipulieren.</p>
<p>In diesem Artikel soll gezeigt werden, wie man ohne Plugin mit Hilfe von <b>jQuery</b>, <b>jQuery.UI</b> und einigen WordPress-Tricks eine AJAX Suche in WordPress realisiert.</p>
<p><span id="more-1083"></span></p>
<h2>Hintergrund</h2>
<p>Bevor wir mit der Umsetzung beginnen, werfen wir einen Blick auf das Endergebnis. Ziel ist es, ein Suchformular wie in untenstehender Abbildung zu erstellen.</p>
<ol>
<li>Mittels <code>jQuery.ui.autocomplete</code> wird dem Suchfeld eine Event zugeordnet, welcher ausgelöst wird sobald etwas in das Textfeld eingegeben wurde.</li>
<li>Wurde ein Event registriert, wird durch jQuery ein AJAX-Request an WordPress gestellt. Als Wert wird der eingegebene Text übermittelt.</li>
<li>WordPress prüft zunächst, ob die angegebene Aktion existiert.</li>
<li>Und führt anschliessend den an die Aktion registrierten Callback aus.</li>
<li>Der Callback selbst erledigt nun die eigentliche Arbeit. Anhand des mitgegebenen Wertes werden die Daten aus der Datenbank ausgelesen und zurück an den Client gesendet.</li>
<li>Als letzten Schritt erfolgt die Aufbereitung und Anzeige der Daten.</li>
</ol>
<p>Schritt 1,2 und 6 werden hierbei auf Clientseite abgehandelt und müssen daher in JavaScript erfolgen. Schritt 3,4 und 5 sind serverseitige Aufgaben und werden in unserem Fall mittels PHP durchgeführt.</p>
<p><a href="https://www.webeo.ch/wp/wp-content/uploads/2011/08/ajax_sheet.png" rel="prettyPhoto[1083]"><img class="alignnone size-full wp-image-1084" title="AJAX Request/Response Verfahren" src="https://www.webeo.ch/wp/wp-content/uploads/2011/08/ajax_sheet.png" alt="" width="600" height="1000" /></a></p>
<h2>Schritt 1: HTML Gerüst</h2>
<p>In diesem Tutorial wird das Standard Suchformular von WordPress ersetzt. Falls du dies nicht willst, kannst du das Ganze auch in ein eigenes Page-Template speichern und einer Seite deiner Wahl zuweisen. Wie das geht, erfährst du <a href="http://codex.wordpress.org/Pages#Creating_Your_Own_Page_Templates" target="_blank">hier</a>.</p>
<p>Öffne nun die Datei <code>searchform.php</code> innerhalb deines Themeverzeichnisses. Sollte die Datei nicht existieren, erstelle Sie. Passe den Inhalt nachfolgendem Beispiel an:</p>
<pre class="brush: php; title: ; notranslate">
&lt;?php
/**
 * Template for displaying the search form
 */
?&gt;
&lt;form id=&quot;searchForm&quot; action=&quot;&lt;?php echo home_url(); ?&gt;&quot; method=&quot;get&quot;&gt;
    &lt;input id=&quot;searchKeyword&quot; title=&quot;&lt;?php __('Search'); ?&gt;&quot; type=&quot;text&quot; name=&quot;s&quot; value=&quot;&quot; /&gt;
    &lt;button id=&quot;searchSubmit&quot; type=&quot;submit&quot;&gt;&lt;?php _e('Search'); ?&gt;&lt;/button&gt;
&lt;/form&gt;
</pre>
<p>Wichtig hierbei ist, dass die Datei im Root-Verzeichnis deines Themes liegt und der Name der Datei <code>searchform.php</code> trägt. Somit gehen wir sicher, dass WordPress die Datei korrekt lädt und über die internen Funktionen wie bspw. <code>get_search_form()</code> aufgerufen werden kann.</p>
<p>Ebenfalls zu beachten ist die ID des Textfeldes. Diese muss mit der ID in der späteren JavaScript-Datei übereinstimmen.</p>
<h2>Schritt 2: CSS Styling</h2>
<p>Da jedes Theme unterschiedlich daher kommt, verzichte ich an dieser Stelle auf ein Stylesheet. Einzig die HTMl-Liste, in welche später die Vorschläge aus der Datenbank anzeigen werden, muss Design technisch etwas angepasst werden. Da diese durch <code>jQuery.ui.autocomplete</code> generiert wird, sind die CSS-Klassen bereits vorgegeben. Eine genaue Auflistung findest du auf der <a href="http://docs.jquery.com/UI/Autocomplete#theming" target="_blank">jQuery.ui.autocomplete API Seite</a>.</p>
<p>Binde nun nachfolgendes CSS-Snippet in das CSS-Stylesheet deines Themes ein (bspw. in <code>style.css</code>).</p>
<pre class="brush: css; title: ; notranslate">
.ui-autocomplete {
    width: 10em;
    padding: 10px;
    background: #f4f4f4;
    border: 1px solid #ccc;
}

.ui-autocomplete li {
    background: none;
    padding-left: 0;
    border-bottom: 1px solid #ccc;
}
</pre>
<h2>Schritt 3: Serverseitige Logik (PHP/WordPress)</h2>
<p>Obwohl abweichend vom eigentlichen AJAX-Ablauf, fangen wir nun als Erstes mit der serverseitigen Logik an. Hierbei werden wir nun eine Klassen namens <code>Webeo_Searchform</code> erstellen.</p>
<p>Dabei ist es dir freigestellt, ob du die Klasse als Plugin oder innerhalb des Themes einbindest. Zur vereinfachten Darstellung verwende ich hier die übliche <code>functions.php</code> innerhalb des Themeverzeichnisses.</p>
<pre class="brush: php; title: ; notranslate">
// functions.php

/**
 * Webeo_Searchform class
 *
 * @package     Webeo
 * @author      Roman Wünsche
 * @copyright   2011 Webeo GmbH. All rights reserved.
 */
class Webeo_Searchform {

    /**
     * Constructor
     *
     * @return void
     */
    public function __construct() {
        // Attach init method to init action hook
        add_action('init', array($this, 'init'));
    }

    /**
     * Init - Fires on init action hook
     *
     * @return void
     */
	public function init() {
	    // Attach AJAX method to privileged and non-privileged action hook
	    add_action('wp_ajax_nopriv_webeo_searchform', array($this, 'getAjaxData'));
	    add_action('wp_ajax_webeo_searchform', array($this, 'getAjaxData'));

	    // Register and enqueue JS
	    $this-&gt;enqueueScripts();

	    // Print additional JS var to sourcecode
	    $this-&gt;printAjaxConfig();
	}

	/**
	 * Deregister/Register and enqueue custom JavaScripts
	 *
	 * @return void
	 */
	public function enqueueScripts() {
	    // Deregister builtin jQuery
	    wp_deregister_script('jquery');

	    // Register custom searchform JS and some jQuery libraries from the Google CDN
	    wp_register_script('jquery', 'https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js', array(), '1.6.2', false);
	    wp_register_script('jqueryui', 'https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.15/jquery-ui.min.js', array('jquery'), '1.8.15', false);
	    wp_register_script('webeo.searchform', get_stylesheet_directory_uri() . '/js/webeo.searchform.js', array('jquery', 'jqueryui'), '1.0', false);

	    // Enqueue custom searchform JS
	    wp_enqueue_script('webeo.searchform');
	}

	/**
	 * Print additional JavaScript vars to sourcecode, which we can use
	 * on the client side JavaScript file.
	 *
	 * @return void
	 */
	public function printAjaxConfig() {
	    // Check for SSL
	    $protocol = (is_ssl()) ? 'https://' : 'http://';

	    // Generate data for the client side JavaScript file
	    $params = array(
	        'ajaxurl' =&gt; admin_url('admin-ajax.php', $protocol),
	        'ajaximg' =&gt; get_stylesheet_directory_uri() . '/images/ajaxloader.gif',
	        'ajaxaction' =&gt; 'webeo_searchform',
	        'ajaxnonce' =&gt; wp_create_nonce('webeo_searchform_nonce')
	    );

	    // Print data to sourcecode
	    wp_localize_script('webeo.searchform', 'webeo_searchform', $params);
	}

	/**
	 * AJAX processing
	 *
	 * @return string
	 */
	public function getAjaxData() {
	    // Check nonce
	    check_ajax_referer('webeo_searchform_nonce');

	    if(isset($_REQUEST['action'])) {
	        // Filter $_REQUEST vars
	        $value = esc_attr($_REQUEST['value']);

	        // Retreieve data from database
	        global $wpdb;
	        $posts = $wpdb-&gt;get_results($wpdb-&gt;prepare(&quot;SELECT `ID`, `post_title` FROM $wpdb-&gt;posts WHERE `post_status` = 'publish' AND `post_title` REGEXP %s LIMIT 10&quot;, $value));

	        if(!empty($posts)) {
	            // Add the permalink to the postdata
	            foreach($posts as $post) {
	                $post-&gt;post_permalink = get_permalink($post-&gt;ID);
	            }

	            // Save post array in response var
	            $response = $posts;
	        } else {
	            // Save error message in response var, if no post was found
	            $response = __('No items found.', 'webeo-searchform');
	        }

	        // Encode the response var and print the result
	        echo json_encode($response);
	    }

	    // Close AJAX request
	    die();
	}
}

// Initialize Webeo_Searchform
new Webeo_Searchform();
</pre>
<p>Schauen wir uns den Code etwas genauer an:</p>
<h3>Definieren der AJAX-Callback Action-Hooks</h3>
<pre class="brush: php; title: ; notranslate">
public function init() {
    // Attach AJAX method to privileged and non-privileged action hook
    add_action('wp_ajax_nopriv_webeo_searchform', array($this, 'getAjaxData'));
    add_action('wp_ajax_webeo_searchform', array($this, 'getAjaxData'));

    // Register and enqueue JS
    $this-&gt;enqueueScripts();

    // Print additional JS var to sourcecode
    $this-&gt;printAjaxConfig();
}
</pre>
<p>Innerhalb der <code>init</code>-Methode fügen wir zwei neue WordPress-Actions hinzu, registrieren unsere noch nicht existierende JavaScript-Datei und geben zusätzlich einige Parameter für das spätere Script aus.</p>
<pre class="brush: php; title: ; notranslate">
add_action('wp_ajax_nopriv_webeo_searchform', array($this, 'getAjaxData'));
add_action('wp_ajax_webeo_searchform', array($this, 'getAjaxData'));
</pre>
<p>Die Namen der Action-Hooks sind keineswegs frei gewählt, zumindest nicht gänzlich. Über <code>wp_ajax_</code> teilen wir WordPress mit, dass es sich hierbei um eine AJAX-Action handelt. Damit der Action-Hook auch ausgeführt wird wenn der Besucher über keine Rechte verfügt, definieren wir zusätzlich noch die Action beginnend mit <code>wp_ajax_nopriv_</code>.</p>
<p>Alles was danach folgt, kannst du frei wählen. Den Bezeichner musst du dir jedoch merken, da wir ihn später noch für den AJAX-Request benötigen.</p>
<h3>Registrieren und Einbinden der JavaScript-Dateien</h3>
<pre class="brush: php; title: ; notranslate">
public function enqueueScripts() {
    // Deregister builtin jQuery
    wp_deregister_script('jquery');

    // Register custom searchform JS and some jQuery libraries from the Google CDN
    wp_register_script('jquery', 'https://ajax.googleapis.com/ajax/libs/jquery/1.6.2/jquery.min.js', array(), '1.6.2', false);
    wp_register_script('jqueryui', 'https://ajax.googleapis.com/ajax/libs/jqueryui/1.8.15/jquery-ui.min.js', array('jquery'), '1.8.15', false);
    wp_register_script('webeo.searchform', get_stylesheet_directory_uri() . '/js/webeo.searchform.js', array('jquery', 'jqueryui'), '1.0', false);

    // Enqueue custom searchform JS
    wp_enqueue_script('webeo.searchform');
}
</pre>
<p>Als nächsten Schritt registrieren und laden wir nun die benötigten JavaScript-Dateien. WordPress kommt bereits mit einer jQuery und jQuery-Core Version daher. Da diese von WordPress Version zu WordPress Version unterschiedlich sind, verwenden wir fürs Tutorial die zum Zeitpunkt dieses Artikels aktuellsten Versionen der beiden Bibliotheken direkt von der Google Cloud.</p>
<pre class="brush: php; title: ; notranslate">
wp_register_script('webeo.searchform', get_stylesheet_directory_uri() . '/js/webeo.searchform.js', array('jquery', 'jqueryui'), '1.0', false);
</pre>
<p>Als letztes Script registrieren wir unter dem Bezeichner <code>webeo.searchform</code> unsere eigene JavaScript-Datei, in welchem wir später unser AJAX-Request erstellen. Hierbei ist es wichtig, dass du die Abhängigkeiten als dritten Parameter mitgibst. In unserem Fall wird unser eigenes Script später jQuery und jQuery.UI benötigen. Daher geben wir als dritten Parameter folgendes Array mit: <code>array('jquery', 'jqueryui')</code>.</p>
<pre class="brush: php; title: ; notranslate">

// Enqueue custom searchform JS
wp_enqueue_script('webeo.searchform');
</pre>
<p>Nun können wir das Script mittels <code>wp_enqueue_script()</code> einbinden. Wurde alles korrekt durchgeführt, wird WordPress die Abhängigkeiten nun von alleine auflösen und jQuery sowie jQuery.UI ebenfalls einbinden.</p>
<h3>Zusätzliche Parameter für die JavaScript-Datei</h3>
<pre class="brush: php; title: ; notranslate">
public function printAjaxConfig() {
    // Check for SSL
    $protocol = (is_ssl()) ? 'https://' : 'http://';

    // Generate data for the client side JavaScript file
    $params = array(
        'ajaxurl' =&gt; admin_url('admin-ajax.php', $protocol),
        'ajaximg' =&gt; get_stylesheet_directory_uri() . '/images/ajaxloader.gif',
        'ajaxaction' =&gt; 'webeo_searchform',
        'ajaxnonce' =&gt; wp_create_nonce('webeo_searchform_nonce')
    );

    // Print data to sourcecode
    wp_localize_script('webeo.searchform', 'webeo_searchform', $params);
}
</pre>
<p>Was nun folgt, wird in vielen Tutorials oftmals von Hand in der späteren JavaScript-Datei niedergeschrieben. Die Rede ist von den Parametern, welche während des AJAX-Requests benötigt werden. Da jedoch nicht jede WordPress Version gleich ist, sollte dies generell über PHP abgehandelt werden. Die Parameter werden nun serverseitig erstellt und über die Funktion <code>wp_localize_script()</code> in den Sourcecode ausgegeben. Somit kann später in der JavaScript-Datei darauf zugegriffen werden.</p>
<ul>
<li><b><code>ajaxurl</code></b>: Gibt den URL zum Serverscript an, welches den AJAX-Request entgegen nimmt und verarbeitet.</li>
<li><b><code>ajaxaction</code></b>: Gibt den registrierten AJAX-Action-Hook an. Hier muss nun der oben gewählte Bezeichner angefügt werden.</li>
<li><b><code>ajaximg</code></b>: Gibt den URL zu einem Loading-Bild aus, welches später als Platzhalter angezeigt werden kann.</li>
<li><b><code>ajaxnone</code></b>: Generiert einen Nonce-Token, um die Sicherheit zu erhöhen.</li>
</ul>
<div class="notification warning">
<strong>Wichtig!</strong></p>
<p>Oftmals kommt es zu Verwechslungen bei der Angabe des Script-Bezeichners und der AJAX-Action. Als Folge davon wird die JavaScript-Datei nicht eingebunden. Schau dir die in der Funktion übergebenen Parameter an.</p>
<pre class="brush: php; title: ; notranslate">
wp_localize_script('webeo.searchform', 'webeo_searchform', $params);&lt;/pre&gt;
</pre>
<p>Der erste Parameter ist der Bezeichner der eingebundenen JavaScript-Datei. In unserem Fall <code>webeo.searchform</code> wie weiter oben beim Registrieren der Scripts angegeben. Der zweite Parameter hingegen ist der Name der Variable, welche im Sourcecode ausgegeben und später in der JavaScript-Datei verwendet wird.</p>
</div>
<h3>Der eigentliche AJAX-Callback: <code>getAjaxData</code></h3>
<pre class="brush: php; title: ; notranslate">
public function getAjaxData() {
    // Check nonce
    check_ajax_referer('webeo_searchform_nonce');

    if(isset($_REQUEST['action'])) {
        // Filter $_REQUEST vars
        $value = esc_attr($_REQUEST['value']);

        // Retreieve data from database
        global $wpdb;
        $posts = $wpdb-&gt;get_results($wpdb-&gt;prepare(&quot;SELECT `ID`, `post_title` FROM $wpdb-&gt;posts WHERE `post_status` = 'publish' AND `post_title` REGEXP %s LIMIT 10&quot;, $value));

        if(!empty($posts)) {
            // Add the permalink to the postdata
            foreach($posts as $post) {
                $post-&gt;post_permalink = get_permalink($post-&gt;ID);
            }

            // Save post array in response var
            $response = $posts;
        } else {
            // Save error message in response var, if no post was found
            $response = __('No items found.', 'webeo-searchform');
        }

        // Encode the response var and print the result
        echo json_encode($response);
    }

    // Close AJAX request
    die();
}
</pre>
<p>Bisher haben wir lediglich über das korrekte Laden und einbinden der JavaScript Dateien gesprochen. Nun folgt die Definition der Methode, welche wir bei den beiden <code>wp_ajax_</code> Actions als Callback angegebenen haben. Diese soll den AJAX-Request verarbeiten.</p>
<pre class="brush: php; title: ; notranslate">
check_ajax_referer('webeo_searchform_nonce');
</pre>
<p>Zu aller erst prüfen wir, ob der übermittelte Nonce-Token identisch ist. Für eine besser Sicherheit, könnte man den Token selbst immer neu generieren lassen. Dies ist jedoch nicht Teil des Tutorials.</p>
<pre class="brush: php; title: ; notranslate">
global $wpdb;
$posts = $wpdb-&gt;get_results($wpdb-&gt;prepare(&quot;SELECT `ID`, `post_title` FROM $wpdb-&gt;posts WHERE `post_status` = 'publish' AND `post_title` REGEXP %s LIMIT 10&quot;, $value));
</pre>
<p>Wurde im <code>$_REQUEST</code> Array eine <code>action</code> angegeben, filtern wir die übergebenen Daten und senden ein SQL-Query an die Datenbank. Hierbei verwenden wir die globale Varibale <code>$wpdb</code>, welche bereits eine Instanz der Klasse <code>WP_Query</code> enthält. Üblicherweise sollten Benutzereingaben nie blindlinks vertraut werden, weshalb wird das Query mittels der Methode <code>prepare</code> zuerst vorbereiten. Hierbei verhält sich die Methode ähnlich der eingebauten PHP-Funktion <code>sprinft()</code>. Mittels  <code>%d</code> (Decimal) oder <code>%s</code> (String) wird im Query ein Platzhalter definiert, welcher später mit dem angefügten Parameter ersetzt wird.</p>
<p class="info">
<strong>Tipp:</strong><br />
Da das Prefix der Datenbank-Tabellen bei der Installation durch den Benutzer frei gewählt werden kann, sollte auf die direkte Angabe (bspw. <code>FROM wp_posts</code>) verzichtet werden. Stattdessen kann das <code>WP_Query</code> Objekt verwendet werden, welches das Prefix korrekt ausgibt. Obiger Aufruf wird daher durch <code>FROM $wpdb-&gt;posts</code> ersetzt.
</p>
<p>Wurde das Query mittels <code>$wpdb-&gt;get_results()</code> ausgeführt und enthält Daten, wird zusätzlich noch der Permalink anhand der ID generiert und an das Array angehängt. Sollten keine Posts vorhanden sein, so wird eine entsprechende Fehlermeldung zurückgegeben.</p>
<pre class="brush: php; title: ; notranslate">
echo json_encode($response);
</pre>
<p>Als letzten Schritt muss die Antwort noch encoded und ausgegeben werden. Vergewissere dich, dass du am Ende den AJAX-Request mittels einem <code>die()</code> beendest.</p>
<h2>Schritt 4: Clientseitige Logik (JavaScript)</h2>
<pre class="brush: jscript; title: ; notranslate">

/**
 * Webeo Searchform JavaScript
 *
 * @package     Webeo
 * @author      Roman Wünsche
 * @copyright   2011 Webeo GmbH. All rights reserved.
 */

(function($) {
	$(document).ready(function() {
		webeoAjaxSearch();
	});

	/**
	 * AJAX Search
	 */
	function webeoAjaxSearch() {
		var formConfig = webeo_searchform;
		var $searchField = $('#searchKeyword');

		$searchField.autocomplete({
			source: function(request, response) {
				$.ajax({
					url: formConfig.ajaxurl,
					type: 'post',
					data: {
						action: formConfig.ajaxaction,
						value: $searchField.val(),
						_ajax_nonce: formConfig.ajaxnonce
					},
					timeout: 1500,
					beforeSend: function() {
						$('#ajaxmessage').hide();
						$searchField.after('&lt;img id=&quot;ajaxloading&quot; src=&quot;' + formConfig.ajaximg + '&quot; alt=&quot;&quot; /&gt;');
					},
					success: function(data) {
						$('#ajaxloading').hide();

						data = $.parseJSON(data);

						if(data instanceof Array) {
							response($.map(data, function(item) {
								return {
									label: item.post_title,
									id: item.ID,
									permalink: item.post_permalink
								}
							}));
						} else {
							$searchField.after('&lt;span id=&quot;ajaxmessage&quot;&gt;' + data + '&lt;/span&gt;');
						}
					},
					error: function() {
						$('#ajaxloading').hide();
						$searchField.after('&lt;span id=&quot;ajaxmessage&quot;&gt;Request timed out. Please try again.&lt;/span&gt;');
					}
				});
			},
			select: function(event, ui) {
				$(location).attr('href', ui.item.permalink);
			},
			minLength: 1
		});
	}
})(jQuery);
</pre>
<p>Kommen wir nun zum clientseitigen JavaScript, der letzten Komponente für die AJAX Suche.</p>
<pre class="brush: jscript; title: ; notranslate">
(function($) {
	$(document).ready(function() {
		webeoAjaxSearch();
	});
})(jQuery);
</pre>
<p>Da unter WordPress jQuery standardmässig im noConflict-Modus läuft, erstellen wir uns als aller Erstes einen noConflict-Wrapper. Somit können wir trotz noConflict-Modus weiterhin das Dollarzeichen <code>$</code> anstelle des sonst üblichen <code>jQuery</code> Bezeichners verwenden. Das Prinzip wird von Rob Cherny auf seinem Blog <a href="http://blog.navigationarts.com/avoiding-jquerys-noconflict-mode-with-prototype-and-sitecore/" target="_blank">näher beschrieben</a>.</p>
<p>Danach folgt die übliche <code>$(document).ready()</code> Anweisung, welche die Funktion <code>webeoAjaxSearch()</code> aufruft.</p>
<h3>Laden der zusätzlichen Parameter aus dem Serverscript</h3>
<p>Hat das Laden der JavaScript-Dateien geklappt, müsste im Sourcecode nachfolgender <code>CDATA</code>-Block ersichtlich sein:</p>
<pre class="brush: xml; title: ; notranslate">
&lt;script type='text/javascript'&gt;
/* &lt;![CDATA[ */
var webeo_searchform = {&quot;ajaxurl&quot;:&quot;http:\/\/example.com\/wp-admin\/admin-ajax.php&quot;,&quot;ajaximg&quot;:&quot;http:\/\/example.com\/wp-content\/themes\/webeo\/images\/ajaxloader.gif&quot;,&quot;ajaxaction&quot;:&quot;webeo_searchform&quot;,&quot;ajaxnonce&quot;:&quot;8a8ae165d6&quot;};
/* ]]&gt; */
&lt;/script&gt;
</pre>
<p>In der Funktion <code>webeoAjaxSearch()</code> definieren wir zuerst einige Variablen.</p>
<pre class="brush: jscript; title: ; notranslate">
var formConfig = webeo_searchform;
var $searchField = $('#searchKeyword');
</pre>
<p>Entscheidend hierbei ist die Variable <code>formConfig</code>. Diese erhält ihre Daten nun vom obigen <code>CDATA</code>-Block bzw. der Variable <code>webeo_searchform</code> welche durch den Aufruf von <code>wp_localize_script()</code> generiert wurde. Daneben setzen wir noch eine Variable für das Suchfeld. Da es sich hierbei um ein jQuery-Element handelt, wird absichtlich ein Dollarzeichen davor gesetzt. </p>
<pre class="brush: jscript; title: ; notranslate">
$searchField.autocomplete({
    source: function(request, response) {
        // ...
    }
    select: function(event, ui) {
        // ...
    }
    minLength: 1
});
</pre>
<p>Als nächstes wird dem Suchfeld der <code>autocomplete</code> Event von <code>jQuery.UI</code> angefügt, womit die eigentliche Autovervollständigung auf diesem Feld aktiv wird. Nachfolgend eine kurze Auflistung der verwendeten Optionen. Eine abschliessende Liste kannst du der <a href="http://docs.jquery.com/UI/Autocomplete#options" target="_blank">jQuery.ui.autocomplete API Seite</a> entnehmen.</p>
<ul>
<li><b><code>source</code></b>: Gibt an, woher die Daten für die Autovervollständigung kommen sollen. In unserem Fall wird hier der eigentliche AJAX-Request gestellt.</li>
<li><b><code>select</code></b>: Gibt an, was passieren soll wenn ein Item aus der Liste ausgewählt wurde. Mittels <code>$(location).attr('href', url)</code> wird automatisch auf den erhaltenen Permalink weitergeleitet.</li>
<li><b><code>minLength</code></b>: Gibt an, ab wievielen Zeichen der Event ausgelöst werden soll.</li>
</ul>
<h3>Der eigentliche AJAX-Request</h3>
<pre class="brush: jscript; title: ; notranslate">
$.ajax({
	url: formConfig.ajaxurl,
	type: 'post',
	data: {
		action: formConfig.ajaxaction,
		value: $searchField.val(),
		_ajax_nonce: formConfig.ajaxnonce
	},
	timeout: 1500,
	beforeSend: function() {
		$('#ajaxmessage').hide();
		$searchField.after('&lt;img id=&quot;ajaxloading&quot; src=&quot;' + formConfig.ajaximg + '&quot; alt=&quot;&quot; /&gt;');
	},
	success: function(data) {
		$('#ajaxloading').hide();
		data = $.parseJSON(data);

		if(data instanceof Array) {
			response($.map(data, function(item) {
				return {
					label: item.post_title,
					id: item.ID,
					permalink: item.post_permalink
				}
			}));
		} else {
			$searchField.after('&lt;span id=&quot;ajaxmessage&quot;&gt;' + data + '&lt;/span&gt;');
		}
	},
	error: function() {
		$('#ajaxloading').hide();
		$searchField.after('&lt;span id=&quot;ajaxmessage&quot;&gt;Request timed out. Please try again.&lt;/span&gt;');
	}
});
</pre>
<p>Über die jQuery Funktion <code>ajax()</code> wird der eigentliche AJAX-Request erzeugt. Auch hier wieder eine kurze Auflistung der verwendeten Optionen. Alle Weiteren findest auf der <a href="http://api.jquery.com/jQuery.ajax/" target="_blank">jQuery API Seite</a>.</p>
<ul>
<li><b><code>url</code></b>: Den URL zum Script, welches den Request verarbeitet. In unserem Fall der Pfad zur <code>admin-ajax.php</code> von WordPress innerhalb der <code>formConfig</code> Variable.</li>
<li><b><code>type</code></b>: Die Transport-Methode GET oder POST.</li>
<li><b><code>data</code></b>: Ein Array an Daten, welche an den Server geschickt werden soll. Hierbei muss die <code>action</code> zwingend mit dem Bezeichner innerhab des registrierten AJAX-Action-Hooks (<code>wp_ajax_</code>) übereinstimmen. Ebenfalls übergeben wir hier den eingegebenen Suchwert <code>value</code> sowie den generierten Nonce-Token <code>_ajax_nonce</code>.</li>
<li><b><code>timeout</code></b>: Das Timeout in Millisekunden, bevor der Request abbricht.</li>
<li><b><code>beforeSend</code></b>: Dieser Event wird ausgeführt, bevor der eigentliche AJAX-Request gestartet wird. Ideal um das Loading-Bild anzeigen zu lassen.</li>
<li><b><code>success</code></b>: Wurde der AJAX-Request erfolgreich abgeschlossen, wird dieser Event ausgelöst. Die Daten müssen als aller erstes dekodiert und anschliessend an das <code>response</code> Objekt von <code>jQuery.ui.autocomplete</code> zurückgegeben werden. Da wir im Normalfall ein Array als Antwort erhalten, erstellen wir mittels <code>map()</code> ein Array Objekt mit unseren eigenen Identifiern. Wurde als Antwort kein Array empfangen, so handelt es sich um eine Fehlermeldung und wird in einem Span-Tag ausgegeben.</li>
<li><b><code>error</code></b>: Ging während des AJAX-Requests etwas schief oder lief dieser in ein Timeout, wird der Error-Event ausgeführt. In unserem Fall wird eine Span-Tag mit dazugehöriger Fehlermeldung an das Suchfeld angefügt.</li>
</ul>
<h2>Debugging</h2>
<p>Um JavaScript zu debuggen, lohnt es sich Firefox oder Safari mit der <b>Firebug Extension</b> zu installieren. Im Konsolen-Fenster siehst du dann direkt, ob ein Parse-Fehler vorliegt. Daneben hast du die Möglichkeit, über den Aufruf der Funktion <code>console.log()</code> eine Ausgaben direkt in der Konsole zu tätigen. Vorbei sind die Tage, wo du eine Ausgabe mittels <code>alert()</code> überprüfen musstest.</p>
<h2>Download</h2>
<p>Damit du schnell zum Ziel kommst, habe ich dir sämtliche Dateien als <a href="https://www.webeo.ch/wp/wp-content/uploads/2011/08/ajax_search_in_wordpress_with_autocompletion.zip" title="Download" target="_blank">ZIP-Datei</a> angefügt. Test es aus!</p>
<h2>Nun bist du an der Reihe</h2>
<p>Hat alles geklappt? Ist irgend etwas unklar? Melde dich über die Kommentarfunktion und lass uns Wissen was du von der Lösung hälst. Auch Verbesserungen oder Erweiterungen sind gerne gesehen.</p>
]]></content:encoded>
			<wfw:commentRss>https://www.webeo.ch/journal/ajax-suche-in-wordpress-mit-autovervollstandigung/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Magento Benutzerhandbuch</title>
		<link>https://www.webeo.ch/journal/magento-benutzerhandbuch</link>
		<comments>https://www.webeo.ch/journal/magento-benutzerhandbuch#comments</comments>
		<pubDate>Thu, 28 Apr 2011 08:42:48 +0000</pubDate>
		<dc:creator>Roman</dc:creator>
				<category><![CDATA[Anleitung]]></category>
		<category><![CDATA[Magento]]></category>

		<guid isPermaLink="false">http://www.webeo.ch/?p=1052</guid>
		<description><![CDATA[Magento ist schon länger ein ernstzunehmender Konkurrent im E-Commerce Business. Der grosse Vorteil der Anpassungsfähigkeit ist zugleich auch einer der grössten Hürden für Betreiber und Administrator. Das System ist relativ komplex und erfordert einiges an Einarbeitungszeit. Leider gibt es von Seiten Varien, der Hersteller von Magento, nur eine kostenpflichtige Anleitung in Englisch. Für deutschsprachige Nutzer [...]]]></description>
			<content:encoded><![CDATA[<p>Magento ist schon länger ein ernstzunehmender Konkurrent im E-Commerce Business. Der grosse Vorteil der Anpassungsfähigkeit ist zugleich auch einer der grössten Hürden für Betreiber und Administrator.</p>
<p>Das System ist relativ komplex und erfordert einiges an Einarbeitungszeit. Leider gibt es von Seiten Varien, der Hersteller von Magento, nur eine kostenpflichtige Anleitung in Englisch. Für deutschsprachige Nutzer existieren einige mehr oder weniger gute Alternativen. Zu den guten gehört sicherlich das <a href="http://www.techdivision.com/shop/magento-handbuch-1" target="_blank">E-Book</a> von Techdivision mit über 350-Seiten.</p>
<p>Meistens ist ein Kunde jedoch nicht bereit, ein dickes Handbuch zu wälzen und hierfür noch Geld auszugeben. Aus diesem Grund habe ich mich entschlossen unser Einstiegs-Benutzerhandbuch für Magento der Öffentlichkeit zur Verfügung zu stellen.</p>
<p style="text-align: center;"><a title="Magento-Benutzerhandbuch herunterladen" href="/wp/wp-content/uploads/2011/04/Magento-Benutzerhandbuch.pdf"><img class="aligncenter" src="/wp/wp-content/uploads/2011/04/webeo_magento_manual.png" alt="" width="220" height="304" /></a><br />
<a title="Jetzt herunterladen" href="https://www.webeo.ch/wp/wp-content/uploads/2011/04/Magento-Benutzerhandbuch.pdf" target="_blank">Jetzt herunterladen (11.5 MB)</a></p>
<p>Das Handbuch beschreibt auf knapp 50 Seiten die notwendigsten Schritte um Kategorien, Attribute und Artikel zu verwalten. Hinzu kommt die Erklärung des Bestellprozederes aus Kunden- und Betreiberssicht. Damit auch wirklich jeder zum Ziel kommt, wurde grossen Wert auf das visuelle Element gelegt. Mittels detallierten Screenshots wird versucht, die Administrationsoberfläche auch für wenig versierte Benutzer verständlich zu erläutern.</p>
<p>Das Handbuch kann frei weitergegeben werden, sofern ein entsprechender Vermerk angegeben wird. Ebenfalls würde es mich freuen wenn die Anleitung gemeinsam mit der Community wachsen würde.</p>
]]></content:encoded>
			<wfw:commentRss>https://www.webeo.ch/journal/magento-benutzerhandbuch/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>WordPress fernsteuern mittels XML-RPC</title>
		<link>https://www.webeo.ch/journal/wordpress-fernsteuern-mittels-xml-rpc</link>
		<comments>https://www.webeo.ch/journal/wordpress-fernsteuern-mittels-xml-rpc#comments</comments>
		<pubDate>Wed, 30 Mar 2011 08:50:58 +0000</pubDate>
		<dc:creator>Roman</dc:creator>
				<category><![CDATA[Anleitung]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[WordPress]]></category>

		<guid isPermaLink="false">http://www.webeo.ch/?p=1018</guid>
		<description><![CDATA[Neben der allseits bekanntem Möglichkeit WordPress über die Administrationsoberfläche zu verwalten, können Anfragen auch über diverse andere Kanäle gestellt werden. Neben dem SMTP-Interface, welches es ermöglicht einen Artikel per Mail zu erstellen (Siehe hierzu das Plugin Postie), soll in diesem Artikel der XML-RPC Dienst näher vorgestellt werden. Hierbei wird neben einem generellen Überblick auch Schritt [...]]]></description>
			<content:encoded><![CDATA[<p>Neben der allseits bekanntem Möglichkeit WordPress über die Administrationsoberfläche zu verwalten, können Anfragen auch über diverse andere Kanäle gestellt werden. Neben dem SMTP-Interface, welches es ermöglicht einen Artikel per Mail zu erstellen (Siehe hierzu das Plugin <a href="http://wordpress.org/extend/plugins/postie/">Postie</a>), soll in diesem Artikel der XML-RPC Dienst näher vorgestellt werden.</p>
<p>Hierbei wird neben einem generellen Überblick auch Schritt für Schritt aufgezeigt werden, wie man den Dienst für seine eigenen Bedürfnisse nutzt und so bspw. das fehlende Feature für Custom Post Types hinzufügt.</p>
<p><span id="more-1018"></span></p>
<h2>Hintergrund</h2>
<p><a href="http://de.wikipedia.org/wiki/XML-RPC">XML-RPC</a> (Extensible Markup Language Remote Procedure Call) definiert hierbei, wie die Anfrage (Request) und die Antwort (Response) auszusehen hat. Da somit das Ergebnis immer das Gleiche ist, kann man ein System per XML-RPC von nahezu allen Programmiersprachen anzapfen, welche diesen Standard implementieren.</p>
<p>Das Request&#038;Response-Verfahren (Transport) wird hierbei über das HTTP-Protokoll abgewickelt. Bei der Darstellung der Daten kommt, wer hätte es gedacht, XML zum Einsatz.</p>
<h2>Funktionsweise</h2>
<p>Die Idee dahinter ist simpel. Der Dienst oder genauer gesagt der XML-RPC-Server wird von WordPress initialisiert und nimmt nun an ihn gerichtete Anfragen entgegen.</p>
<p>Via XML-RPC-Client wird nun eine Anfrage (<code>methodCall</code>) an den Server gestartet. Dieser prüft, ob die übergebene Methode existiert, führt diese aus und sendet das Ergebnis (<code>methodResponse</code>) zurück.</p>
<p style="text-align: center;"><img class="aligncenter" src="http://www.webeo.ch/wp/wp-content/uploads/2011/03/xmlrpc.png" alt="XML-RPC Diagram" width="600" height="500" /></p>
<h2>WordPress Unterstützung</h2>
<p>WordPress unterstützt XML-RPC bereits seit seinen Anfängen. Über die Jahre wurde das System jedoch immer wieder ausgebaut und aus Abwärtskompatibilität viel Redundanz in den Code eingeschleust. Die Dokumentation ist ebenfalls sehr minimal ausgefallen.</p>
<p>Die Prioritäten scheinen wohl definitiv wo anders zu liegen. Dies zeigt sich auch in der fehlenden Unterstützung von Custom Post Types oder den Taxonomies.</p>
<h2>Schritt für Schritt</h2>
<h3>Schritt 1: Server Initalisierung <code>xmlrpc.php</code></h3>
<p>Über die Datei <code>xmlrpc.php</code>, welche sich im Root-Verzeichnis von WordPress befindet, wird der eigentliche Server initialisiert. Hierzu muss dieser zuerst über die Administrationsoberfläche aktiviert werden.</p>
<p>Setze hierzu die Checkbox bei der Option:<br />
<code>Settings &gt; Writing &gt; XML-RPC &gt; Enable the WordPress, Movable Type, MetaWeblog and Blogger XML-RPC publishing protocols.</code></p>
<h3>Schritt 2: Client Initialisierung, Testen der Verbindung</h3>
<p>Um nun Anfragen an den Server zu stellen, fehlt noch der eigentliche Client. Glücklicherweise ist unter WordPress bereits eine Client-Komponente in der Server-Klasse enthalten, welche auch unabhängig von WordPress funktioniert. Alternativ wäre hier noch das <code>PHPXMLRPC</code> Projekt zu nennen, welches ebenfalls ein einfaches Interface zur Verfügung stellt.</p>
<ol>
<li>Kopiere nun die Datei <code>class-IXR.php</code> aus dem Verzeichnis <code>/wp-includes/</code> in ein eigenes, lokales Verzeichnis deines Webservers (bspw. <code>/var/www/rpc/</code>). Das Ganze könnte man auch direkt in der <code>functions.php</code> von WordPress testen, doch dann wäre der eigentliche Sinn dahinter verfehlt. Immerhin sollen später die Anfragen an WordPress auch von unterschiedlichen Quellen aus erfolgen können.</li>
<li>Im eben erzeugten Verzeichnis erstellst du nun eine <code>index.php</code> mit nachfolgendem Inhalt. Bitte passe den Pfad entsprechend deiner Installation an.</li>
</ol>
<pre class="brush: php; title: ; notranslate">
require_once 'class-IXR.php';
$rpc = new IXR_Client('http://hostname.tld/path/to/wordpress/xmlrpc.php');

$result = $rpc-&gt;query('demo.sayHello');

if(!$result) {
    echo 'Error [' . $rpc-&gt;getErrorCode() . ']: ' . $rpc-&gt;getErrorMessage();
    exit();
}

print_r($rpc-&gt;getResponse());
</pre>
<p>Nach Ausführen des obigen Scripts, müsste der XML-RPC Server nun eine kleine "Hello"-Willkommensmeldung ausgeben. Schauen wir uns das Script genauer an:</p>
<pre class="brush: php; title: ; notranslate">
require_once 'class-IXR.php';
$rpc = new IXR_Client('http://hostname.tld/path/to/wordpress/xmlrpc.php');
</pre>
<p>Zuerst wird die XML-RPC Klasse eingebunden und ein neues Objekt erzeugt. Hier muss als Parameter der relative Pfad zur <code>xmlrpc.php</code> Datei im Root-Verzeichnis von WordPress angegeben werden.</p>
<pre class="brush: php; title: ; notranslate">
$result = $rpc-&gt;query('demo.sayHello');
</pre>
<p>Als nächstes wird ein Query an den XML-RPC Server geschickt. Als ersten Parameter wird hier der Identifier <code>demo.sayHello</code> mitgegeben. Hierbei handelt es sich um eine registrierte Methode innerhalb des Servers.</p>
<pre class="brush: php; title: ; notranslate">
if(!$result) {
    echo 'Error [' . $rpc-&gt;getErrorCode() . ']: ' . $rpc-&gt;getErrorMessage();
    exit();
}

print_r($rpc-&gt;getResponse());
</pre>
<p>Als Letztes erfolgt die Prüfung und Ausgabe der Antwort.</p>
<h3>Schritt 3: Alle verfügbaren Methoden ausgeben</h3>
<p>Um nun mit dem Dienst arbeiten zu können, müssen wir wissen welche Methoden existieren. Hierzu passen wir das Query wie folgt an:</p>
<pre class="brush: php; title: ; notranslate">
$result = $rpc-&gt;query('system.listMethods');
</pre>
<p>Als Ausgabe präsentiert sich uns nun eine Liste aller registrierten Methoden.</p>
<h3>Schritt 4: Authentifizierung</h3>
<p>Bisher war es uns möglich, den Dienst ohne Authentifizierung anzusprechen. Da wir nun jedoch alle verfügbaren Seiten abrufen möchten, müssen wir uns vorher bei WordPress anmelden. Hierzu definieren wir uns nun einige Parameter, welche anschliessend ans Query übergeben werden.</p>
<pre class="brush: php; title: ; notranslate">
$params = array(
    1,
    'admin',
    'P@ssW0rd',
    10
);

$result = $rpc-&gt;query(
    'wp.getPages',
    $params
);
</pre>
<p>Die Parameter sehen wie folgt aus:</p>
<ul>
<li> <b>BlogID</b>: Angabe der BlogID. Wird momentan Intern noch nirgends verwendet. Später soll hier vermutlich das Multisite-Feature unterstützt werden. Da ein Parameter erwartet wird, wird hier eine <code>1</code> mitgegeben.</li>
<li> <b>Benutzername</b>: Angabe des Benutzernamens, welche die notwendigen Rechte mitbringen muss.</li>
<li> <b>Kennwort</b>: Das zum Benutzer dazugehörige Kennwort.</li>
<li> <b>Limit</b>: Die SQL-Limite für das spätere Query. <code><i>Optional. Defaultwert: 10, Unbegrenzt: -1</i></code></li>
</ul>
<p>Als Methode wird nun <code>wp.getPages()</code> aufgerufen, welche uns ein Array mit den letzten 10 Seiten ausgibt.</p>
<h2>Methoden anpassen und erweitern</h2>
<p>Kommen wir nun zum interessanten Teil, dem Anpassen und Erweitern der Server Methoden. WordPress verwaltet alle eigenen Methoden und die der unterstützen Webservices (Blogger, MetaWeblog, etc.) in der Klasse <code>wp-xmlrpc-server</code>, zu finden unter <code>/wp-includes/class-wp-xmlrpc-server.php</code>.</p>
<pre class="brush: php; title: ; notranslate">
class wp_xmlrpc_server extends IXR_Server {
	function wp_xmlrpc_server() {
		$this-&gt;methods = array(
			'wp.getUsersBlogs'		=&gt; 'this:wp_getUsersBlogs',
			'wp.getPage'			=&gt; 'this:wp_getPage',
			'wp.getPages'			=&gt; 'this:wp_getPages',
			// ...
			'demo.sayHello' =&gt; 'this:sayHello',
			'demo.addTwoNumbers' =&gt; 'this:addTwoNumbers'
		);

		$this-&gt;initialise_blog_option_info( );
		$this-&gt;methods = apply_filters('xmlrpc_methods', $this-&gt;methods);
	}

	function serve_request() {
		$this-&gt;IXR_Server($this-&gt;methods);
	}

	function wp_getPage($args) {
	    // do the magic
	}
}
</pre>
<p>Sehen wir uns obigen Auszug der Klasse genauer an, merken wir auch gleich dass es sich hierbei um eine abgeleitete Klasse von <code>IXR_Server</code> handelt. Mit anderen Worten: Der eigentliche XML-RPC Server Kram ist in seiner eigenen Klasse gekapselt. In der <code>wp-xmlrpc-server</code> Klasse sind lediglich die eigenen Methoden enthalten. Der Konstruktor setzt hierbei die Klassenvariable <code>$this->methods</code>, welche über die Methode <code>serve_request</code> an die Vaterklasse weitergegeben wird.</p>
<pre class="brush: php; title: ; notranslate">
$this-&gt;methods = array(
    'wp.getPages' =&gt; 'this:wp_getPage',
)
</pre>
<p>Über das Array in der Klassenvariable <code>$this->methods</code> werden nun also die eigenen Methoden registriert. Findinge Programmierer könnten nun auf die Idee kommen, dieses Array direkt zu erweitern. Somit würden wir aber eine WordPress-Core Datei anpassen, welche beim nächsten Update wieder überschrieben würde.</p>
<p>Wer genau hinschaut, sieht im Konstruktor den Aufruf eines Filter-Hooks:</p>
<pre class="brush: php; title: ; notranslate">
$this-&gt;methods = apply_filters('xmlrpc_methods', $this-&gt;methods);
</pre>
<p>Dieser ermöglicht es uns, eigene Methoden in das Array hinzuzufügen. Hierzu muss man bspw. in der <code>functions.php</code> einen eigenen Filter schreiben und diesen an WordPress übergeben.</p>
<pre class="brush: php; title: ; notranslate">
// functions.php
function addWebeoXMLRPCMethods($methods) {
    $methods['webeo.getPost'] = 'webeo_getPost';
    $methods['webeo.getPosts'] = 'webeo_getPosts';
    return $methods;
}
add_filter('xmlrpc_methods', 'addWebeoXMLRPCMethods');

function webeo_getPost($args) {
    // do the magic
}

function webeo_getPosts($args) {
    // do the magic
}
</pre>
<p>Wer mich kennt weiss, dass ich all meinen Code in Klassen auslagere. Bei WordPress stosst man hier leider immer wieder auf Hindernisse. So auch bei der Übergabe der obigen Methoden. Denn im Array kann als Parameter lediglich ein Funktionsname mitgegeben werden.</p>
<p>Moment! In der <code>wp-xmlrpc-server</code> Klasse wurde doch auch auf eine Klassenmethode verwiesen? Richtig, lieber Leser, doch leider wird man mal wieder eines besseren belehrt wenn man die <code>call</code> Methode der <code>IXR_Server</code> Klasse näher betrachtet. Diese verarbeitet später das Array und führt die darin enthaltenen Methoden aus. Wer genau hinsieht, merkt bald wie hier der String <code>this:methodName</code> verarbeitet wird. Via <code>substr</code> werden hierbei einfach die ersten 5 Zeichen des Strings (<code>this:</code>) entfernt und der Rest an die <code>$this</code>-Referenz der Klasse angefügt. Urghs. Nicht sonderlich hübsch.</p>
<h2>Eigene Server-Klasse erstellen und ableiten</h2>
<p>Nun denn, eine Möglichkeit besteht allerdings noch. Und zwar kann man sich eine eigene Klasse schreiben, welche von <code>IXR_Server</code> oder <code>wp_xmlrpc_server</code> ableitet. Glücklicherweise bietet WordPress hier einen anderen Filter-Hook namens <code>wp_xmlrpc_server_class</code> an. Zu finden in der <code>xmlrpc.php</code> Datei, welche zu Beginn des Artikels kurz erwähnt wurde.</p>
<p>Mit diesem Filter ersetzen wir nun die von WordPress verwendete <code>wp_xmlrpc_server</code> Klasse durch unsere eigene. Da somit auch die Methoden von WordPress nicht mehr funktionieren würden, ist es ratsam nicht von <code>IXR_Server</code> sondern von <code>wp_xmlrpc_server</code> abzuleiten. Ein Beispiel:</p>
<pre class="brush: php; title: ; notranslate">
// Webeo_XMLRPC.php
class Webeo_XMLRPC extends wp_xmlrpc_server {
    public function __construct() {
        parent::__construct();

        $methods = array(
            'webeo.getPost' =&gt; 'this:webeo_getPost',
            'webeo.getPosts' =&gt; 'this:webeo_getPosts'
        );

        $this-&gt;methods = array_merge($this-&gt;methods, $methods);
    }

    public static function webeo_getName() {
        return __CLASS__;
    }

    public function sayHello($args) {
        return 'Hello Commander!';
    }

    public function webeo_getPost($args) {
        // do the magic
    }

    public function webeo_getPosts($args) {
        // do the magic
    }
}
</pre>
<p>Obige Klasse erbt nun sämtliche Methoden von <code>wp_xmlrpc_server</code> und kann frei erweitert werden.</p>
<p>Nun müssen wir WordPress noch mitteilen, dass wir für alle XML-RPC Anfragen unsere eigene Klasse verwenden möchten. In der <code>functions.php</code> oder im eigenen Pluginloader wird daher folgender Filter registriert:</p>
<pre class="brush: php; title: ; notranslate">
add_filter('wp_xmlrpc_server_class', array('Webeo_XMLRPC', 'webeo_getName'));
</pre>
<p>Damit der Filter eine Instanz der Klasse <code>Webeo_XMLRPC</code> erstellen kann, müssen wir hier den eigenen Klassennamen zurückgeben. Denn der Filter führt nicht wie sonst üblich die PHP interne <code>call_user_func()</code> Funktion aus, sondern erzeugt direkt über <code>new $filterContent</code> eine neue Instanz der Klasse. Ebenfalls unschön gelöst. </p>
<p>Wir behelfen uns hier mit der PHP internen Konstante <code>__CLASS__</code> welche uns in der Methode <code>webeo_getName</code> den Namen der eigenen Klasse zurück gibt.</p>
<p>Führen wir nun als Test das erste Hello-Query nochmals aus...</p>
<pre class="brush: php; title: ; notranslate">
$result = $rpc-&gt;query('demo.sayHello');
</pre>
<p>... erscheint nun anstelle von "Hello" eine "Hello Commander!". Das Überschreiben der <code>sayHello</code> Methode hat also ohne Probleme geklappt.</p>
<p>Nun können wir eigene Methoden schreiben, diese über das Array <code>$methods</code> im Konstruktor "registrieren" und via XML-RPC abfragen.</p>
<h2>Jetzt bist du an der Reihe</h2>
<p>Hat dir der Artikel weitergeholfen? Hast du selber einen besseren und elegateren Weg gefunden das Problem zu lösen? Für Feedback aller Art haben wir immer ein offenes Ohr. Nutzer hierzu unsere Kommentarfunktion.</p>
]]></content:encoded>
			<wfw:commentRss>https://www.webeo.ch/journal/wordpress-fernsteuern-mittels-xml-rpc/feed</wfw:commentRss>
		<slash:comments>6</slash:comments>
		</item>
		<item>
		<title>Quicktipp: WordPress Hooks suchen und finden</title>
		<link>https://www.webeo.ch/journal/quicktipp-wordpress-hooks-suchen-und-finden</link>
		<comments>https://www.webeo.ch/journal/quicktipp-wordpress-hooks-suchen-und-finden#comments</comments>
		<pubDate>Thu, 03 Feb 2011 13:59:24 +0000</pubDate>
		<dc:creator>Roman</dc:creator>
				<category><![CDATA[Anleitung]]></category>
		<category><![CDATA[WordPress]]></category>
		<category><![CDATA[action]]></category>
		<category><![CDATA[apply_filters]]></category>
		<category><![CDATA[do_action]]></category>
		<category><![CDATA[filter]]></category>
		<category><![CDATA[find]]></category>
		<category><![CDATA[grep]]></category>
		<category><![CDATA[hooks]]></category>

		<guid isPermaLink="false">http://www.webeo.ch/?p=955</guid>
		<description><![CDATA[Um dem Entwickler das Leben etwas zu vereinfachen, nutzt WordPress an diversen Stellen so genannte Hooks. Kurz gesagt, kann man hiermit auf ein internes Verhalten von WordPress reagieren, bevor dieses selbst ausgeführt wird. Bspw. wenn man den Inhalt vor der Ausgabe lieber nochmals filtern möchte. Oftmals hat man jedoch das Problem, dass man zwar über [...]]]></description>
			<content:encoded><![CDATA[<p>Um dem Entwickler das Leben etwas zu vereinfachen, nutzt WordPress an diversen Stellen so genannte <a href="http://de.wikipedia.org/wiki/Hook_%28EDV%29" target="_blank">Hooks</a>. Kurz gesagt, kann man hiermit auf ein internes Verhalten von WordPress reagieren, bevor dieses selbst ausgeführt wird. Bspw. wenn man den Inhalt vor der Ausgabe lieber nochmals filtern möchte.</p>
<p>Oftmals hat man jedoch das Problem, dass man zwar über den WordPress Codex den richtigen Filter- oder Action-Hook gefunden hat, dieser aber gewisse Parameter verlangt. Wenn diese nicht direkt in der Dokumentation angegeben sind, bleibt einem nichts anderes übrig als den PHP-Code zu durchkämmen.</p>
<p>Einfacher gehts mit nachfolgendem Shell-Befehl.</p>
<p><span id="more-955"></span></p>
<pre class="brush: bash; title: ; notranslate">
$ cd /path/to/wordpress/
$ find . -type f -exec grep -n &quot;do_action('save_post&quot; {} \; -print
</pre>
<p>Obiges Beispiel durchsucht im aktuellen Verzeichnis (.) alle Dateien (-type f) und filtert hierbei den Inhalt (-exec grep -n) nach dem Vorkommen von "do_action('save_post" und gibt die jeweilige Zeile auf der Konsole aus. Über die "-print" Anweisung wird hierbei noch zusätzlich der Pfad zur PHP-Datei ausgegeben.</p>
<p>Dies Ausgabe sieht dann wie folgt aus:</p>
<pre class="brush: bash; title: ; notranslate">
2389:    do_action('save_post', $post_ID, $post);
2481:    do_action('save_post', $post_id, $post);
./wp-includes/post.php
</pre>
<p>Die erste Spalte liefert die Zeilenummer im Dokument gefolgt vom Action-Hook. Nun sieht man die gesuchten Paramter, welchem dem Hook übergeben werden müssen.</p>
<p>Möchte man sich nun alle verfügbaren Action-Hooks anzeigen lassen, so gibt man folgendes ein:</p>
<pre class="brush: bash; title: ; notranslate">
$ cd /path/to/wordpress/
$ find . -type f -exec grep -n &quot;do_action(&quot; {} \; -print
</pre>
<p>Für alle Filter-Hooks entsprechend:</p>
<pre class="brush: bash; title: ; notranslate">
$ cd /path/to/wordpress/
$ find . -type f -exec grep -n &quot;apply_filters(&quot; {} \; -print
</pre>
]]></content:encoded>
			<wfw:commentRss>https://www.webeo.ch/journal/quicktipp-wordpress-hooks-suchen-und-finden/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Zuwachs und neue Portfolioeinträge</title>
		<link>https://www.webeo.ch/journal/zuwachs-und-neue-portfolioeintrage</link>
		<comments>https://www.webeo.ch/journal/zuwachs-und-neue-portfolioeintrage#comments</comments>
		<pubDate>Thu, 02 Dec 2010 10:31:13 +0000</pubDate>
		<dc:creator>Roman</dc:creator>
				<category><![CDATA[Webeo]]></category>

		<guid isPermaLink="false">http://www.webeo.ch/?p=905</guid>
		<description><![CDATA[Webeo erhält Zuwachs und feiert daher bereits jetzt Weihnachten. Ralph Henggeler wird uns in Zukunft als freier Mitarbeiter zu Themen wie Web und Security unterstützen. Nach seiner Ausbildung zum eidg. Fachinformatiker Richtung Systemtechnik, konnte er seine Kenntnisse bei einem grossen Schweizer Internet Service Provider vertiefen. Dank seinem tiefen Know-How im Webbereich mit Fokus auf Security [...]]]></description>
			<content:encoded><![CDATA[<p>Webeo erhält Zuwachs und feiert daher bereits jetzt Weihnachten. Ralph Henggeler wird uns in Zukunft als freier Mitarbeiter zu Themen wie Web und Security unterstützen. Nach seiner Ausbildung zum eidg. Fachinformatiker Richtung Systemtechnik, konnte er seine Kenntnisse bei einem grossen Schweizer Internet Service Provider vertiefen. Dank seinem tiefen Know-How im Webbereich mit Fokus auf Security ist er ein idealer Zusatz für Webeo. Herzlich Willkommen!</p>
<p>Natürlich waren wir in der Zwischenzeit nicht untätig. Neu in die Referenzliste hinzugekommen sind folgende Unternehmen:</p>
<ul>
<li><a href="http://tutti.ch" target="_blank">Tutti.ch</a>, der Schweizer Inseratemarkt. Diverse Werbebanner wurden in verschiedenen Formanten nach Google- und iAB-Richtlinien umgesetzt.</li>
<li><a href="http://absolvententag.ch" target="_blank">Absolvententag ZHAW</a>. Ganz nach dem Motto "campus meets business", können Unternehmen nach geeigneten Fachkräften suchen oder Absolventen den Schritt in Richtung Business wagen. Um dem neuen Corporate Identity gerecht zu werden, wurde die Webseite einem Facelift unterzogen. Als CMS wird Typo3 eingesetzt. Die Umsetzung erfolgte hierbei durch die Firma luginbuehl.com.</li>
</ul>
]]></content:encoded>
			<wfw:commentRss>https://www.webeo.ch/journal/zuwachs-und-neue-portfolioeintrage/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Quicktipp: User Homes unter Zend Server CE</title>
		<link>https://www.webeo.ch/journal/quicktipp-user-homes-unter-zend-server-ce</link>
		<comments>https://www.webeo.ch/journal/quicktipp-user-homes-unter-zend-server-ce#comments</comments>
		<pubDate>Tue, 21 Sep 2010 08:45:29 +0000</pubDate>
		<dc:creator>Roman</dc:creator>
				<category><![CDATA[Anleitung]]></category>
		<category><![CDATA[Mac OS X]]></category>
		<category><![CDATA[Zend]]></category>
		<category><![CDATA[apache]]></category>
		<category><![CDATA[user homes]]></category>
		<category><![CDATA[zend server ce]]></category>

		<guid isPermaLink="false">http://www.webeo.ch/?p=873</guid>
		<description><![CDATA[<p>Seit der Umstellung von XAMPP auf Zend Server CE kommen täglich immer wieder neue kleine Anpassungen an der Konfiguration hinzu. Dies, da der Zend Server im Vergleich zu XAMPP etwas restriktiver eingestellt ist.</p>
<p>Dies muss nicht schlecht sein. Im Gegenteil. Auch in einer Entwicklungsumgebung kann etwas mehr Sicherheit sicherlich nicht verkehrt sein. Dennoch gibt es einige Punkte, die die Usabillity etwas einschränken. Aus diesem Grund gibts hier nun immer wieder einige Quicktipps zu finden.</p>]]></description>
			<content:encoded><![CDATA[<p>Seit der Umstellung von XAMPP auf Zend Server CE kommen täglich immer wieder neue kleine Anpassungen an der Konfiguration hinzu. Dies, da der Zend Server im Vergleich zu XAMPP etwas restriktiver eingestellt ist.</p>
<p>Dies muss nicht schlecht sein. Im Gegenteil. Auch in einer Entwicklungsumgebung kann etwas mehr Sicherheit sicherlich nicht verkehrt sein. Dennoch gibt es einige Punkte, die die Usabillity etwas einschränken. Aus diesem Grund gibts hier nun immer wieder einige Quicktipps zu finden.</p>
<p>Da ich gewisse persönliche Seiten in meinem eigenen Benutzerverzeichnis unter "/Users/webeo/Sites/" lagere, ist es nur naheliegend diese direkt mit dem Apache zu verknüpfen. So können anschliessend alle Seiten über http://localhost/~webeo/ aufgerufen werden. Damit dies unter Zend Server CE möglich ist, müssen drei Schritte unternommen werden.</p>
<p><span id="more-873"></span></p>
<h2>Schritt 1: Anpassen der httpd.conf</h2>
<p>Als erstes navigiert man in das Konfigurations-Verzeichnis des Apaches. Unter Mac OS X ist dies:</p>
<pre>/usr/local/zend/apache/conf</pre>
<p>Anschliessend müsst ihr in der httpd.conf den Include-Befehl der User-Home Konfiguration wieder einbinden:</p>
<pre>Apfel:~ webeo$ cd /usr/local/zend/apache2/conf
Apfel:conf webeo$ sudo vi httpd.conf
Password: ********
</pre>
<pre class="brush: plain; title: ; notranslate">
// httpd.conf
# User home directories
Include conf/extra/httpd-userdir.conf
</pre>
<h2>Schritt 2: Anpassen der httpd-userdir.conf</h2>
<p>Nachdem ihr die Datei gespeichert habt, erfolgt nun die Anpassung der User-Home Direktive in der httpd-userdir.conf. Diese ist im Unterverzeichnis "extra" zu finden:</p>
<pre>Apfel:conf webeo$ sudo vi extra/httpd-userdir.conf
</pre>
<p>Passt diese anschliessend wie folgt an:</p>
<pre class="brush: plain; title: ; notranslate">
// extra/httpd-userdir.conf

#
# UserDir: The name of the directory that is appended onto a user's home
# directory if a ~user request is received.  Note that you must also set
# the default access control for these directories, as in the example below.
#
UserDir Sites

#
# Control access to UserDir directories.  The following is an example
# for a site where these directories are restricted to read-only.
#
&lt;Directory &quot;/Users/*/Sites&quot;&gt;
    AllowOverride FileInfo AuthConfig Limit Indexes
    Options MultiViews Indexes SymLinksIfOwnerMatch IncludesNoExec
    &lt;Limit GET POST OPTIONS&gt;
        Order allow,deny
        Allow from all
    &lt;/Limit&gt;
    &lt;LimitExcept GET POST OPTIONS&gt;
        Order deny,allow
        Deny from all
    &lt;/LimitExcept&gt;
&lt;/Directory&gt;
</pre>
<h2>Schritt 3: Neustarten von Zend Server CE</h2>
<p>Als letzten Schritt müsst ihr den Apache2 noch neustarten. Dies geschiet über den Aufruf des "zendctl.sh" Shell-Skripts im bin-Verzeichnis des Zend Servers CE.</p>
<pre>Apfel:conf webeo$ sudo /usr/local/zend/bin/zendctl.sh restart-apache
/usr/local/zend/bin/apachectl stop [OK]
/usr/local/zend/bin/apachectl start [OK]
</pre>
<h2>Nun seit ihr an der Reihe</h2>
<p>Hat bei euch alles wie gewünscht funktioniert oder habt ihr sonstige Tipps und Tricks rund um den Zend Server CE? Lasst es uns wissen.</p>
]]></content:encoded>
			<wfw:commentRss>https://www.webeo.ch/journal/quicktipp-user-homes-unter-zend-server-ce/feed</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Mehrere VirtualHosts mit Zend Server CE</title>
		<link>https://www.webeo.ch/journal/mehrere-virtualhosts-mit-zend-server-ce</link>
		<comments>https://www.webeo.ch/journal/mehrere-virtualhosts-mit-zend-server-ce#comments</comments>
		<pubDate>Sun, 29 Aug 2010 18:19:32 +0000</pubDate>
		<dc:creator>Roman</dc:creator>
				<category><![CDATA[Anleitung]]></category>
		<category><![CDATA[Mac OS X]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Zend]]></category>
		<category><![CDATA[apache]]></category>
		<category><![CDATA[vhosts]]></category>
		<category><![CDATA[webserver]]></category>
		<category><![CDATA[zend]]></category>

		<guid isPermaLink="false">http://www.webeo.ch/?p=821</guid>
		<description><![CDATA[<p>Seit Neuem verwende ich anstelle von XAMPP das <a href="http://www.zend.com/products/server-ce/" target="_blank">Zend Server Community Edition</a> Paket als Entwicklungswebserver.</p>
<p>Hauptgrund war, dass wir nun vermehrt mit dem Zend Framework arbeiten. Generell hat der Webserver dank dem optimierten PHP Stack aber auch eine spürbar bessere Performance. Hinzu kommt, dass der Webserver bereits nahtlos mit dem Zend Studio arbeitet. So wird bspw. der Server automatisch gestartet wenn die IDE läuft.</p>]]></description>
			<content:encoded><![CDATA[<p>Seit Neuem verwende ich anstelle von XAMPP das <a href="http://www.zend.com/products/server-ce/" target="_blank">Zend Server Community Edition</a> Paket als Entwicklungswebserver.</p>
<p>Hauptgrund war, dass wir nun vermehrt mit dem Zend Framework arbeiten. Generell hat der Webserver dank dem optimierten PHP Stack aber auch eine spürbar bessere Performance. Hinzu kommt, dass der Webserver bereits nahtlos mit dem Zend Studio arbeitet. So wird bspw. der Server automatisch gestartet wenn die IDE läuft.</p>
<p><span id="more-821"></span></p>
<p>Zend Server CE verwendet hierbei für die Administrations-Oberfläche den Port 10081. Das htdocs-Verzeichnis liegt bei Mac OS X unter</p>
<pre>/usr/local/zend/apache2/htdocs</pre>
<p>und ist über den Port 10088 erreichbar. Wer hier gerne normal "http://localhost" ohne den zusätzlichen Port aufruft, kann dies über die httpd.conf ändern. Ebenso können dort zusätzliche VirtualHosts eingerichtet werden, was insbesondere bei der Entwicklung mehrere ZF-Projekten das Leben erleichtert.</p>
<h2>Beispiel</h2>
<p>Ich arbeite mit drei Projekten, welche ich mit der Standard-Installation wie folgt aufrufen muss:</p>
<pre>http://localhost:10088 (Htdocs-Index)
http://localhost:10088/webeo/public (Projekt Webeo)
http://localhost:10088/shift/public (Projekt Shift)
http://localhost:10088/shift/public (Projekt Berta)
</pre>
<p>Neu soll das Ganze wie folgt aufgerufen werden:</p>
<pre>http://localhost (Htdocs Index)
http://webeo.local (Projekt Webeo)
http://shift.local (Projekt Shift)
http://berta.local (Projekt Berta)
</pre>
<p>Der schöne Nebeneffekt dabei, bei der Entwicklung von Anwendungen mit dem Zend Framework kann ich nun relativ vom Root-Verzeichnis ausgehen und muss nicht zwingend das Basis-Verzeichnis über den Front-Controller auslesen:</p>
<p>
<pre class="brush: php; title: ; notranslate">// Angabe der Basis-URL
foreach ($stylesheets as $stylesheet) {
$this-&gt;view-&gt;headLink()-&gt;appendStylesheet(Zend_Controller_Front::getInstance() -&gt;getBaseUrl() . '/skins/' . $skin . '/css/' . $stylesheet);
}

// Relativ zum Root-Verzeichnis
foreach ($stylesheets as $stylesheet) {
$this-&gt;view-&gt;headLink()-&gt;appendStylesheet('/skins/' . $skin . '/css/' . $stylesheet);
}
</pre>
</p>
<p>Wobei man hier erwähnen muss, dass erstere Variante prinzipiell schönerer Programmierstil ist. Immerhin funktioniert das Projekt dann auch bei einer lokalen Installation ohne VirtualHosts.</p>
<h2>Schritt 1: httpd.conf anpassen</h2>
<p>Ich verwende hier das eingebaute Terminal von Mac OS X. Zu finden unter:</p>
<pre>Programme -> Dienstprogramme -> Terminal.app</pre>
<p>Die httpd.conf liegt im Verzeichnis "/usr/local/zend/apache2/conf". Da diese dem Webbenutzer gehört, muss der Befehl "sudo" vorangestellt werden um daran Änderungen als Superuser vornehmen zu können.</p>
<pre>Apfel:~ rofflox$ cd /usr/local/zend/apache2/conf
Apfel:conf rofflox$ sudo vi httpd.conf
Password:
</pre>
<p>Anschliessend sucht man nach dem ersten Vorkommen von "Listen" und fügt einen zweiten Eintrag hinzu. Mittels "Listen" gibt man dem Apache-Webserver an, auf welchem Port er laufen soll. Bei Zend Server CE ist dies standardmässig der Port 10088. Da wir den Webserver ebenfalls unter http://localhost ansprechen wollen, fügen wir hier zusätzlich den HTTP-Standardport 80 mittels "Listen 80" hinzu.</p>
<blockquote><p><strong>Tipp:</strong><br />
 Um im vi-Editor nach einem Wort zu suchen, kann im Terminal ein / gefolgt von dem Suchwort eingegeben werden. Um weitere Vorkommen zu finden, drückt man anschliessend lediglich die Taste "n" um weiter zu springen.</p>
</blockquote>
<p>Vorher:</p>
<pre>// httpd.conf
#
# Listen: Allows you to bind Apache to specific IP addresses and/or
# ports, instead of the default. See also the
# directive.
#
# Change this to Listen on specific IP addresses as shown below to
# prevent Apache from glomming onto all bound IP addresses.
#
#Listen 12.34.56.78:80
Listen 10088
</pre>
<p>Nachher:</p>
<pre>
// httpd.conf
#
# Listen: Allows you to bind Apache to specific IP addresses and/or
# ports, instead of the default. See also the
# directive.
#
# Change this to Listen on specific IP addresses as shown below to
# prevent Apache from glomming onto all bound IP addresses.
#
#Listen 12.34.56.78:80
Listen 10088
Listen 80
</pre>
<p>Die virtuellen Hosts werden bei Zend Server CE in einer separaten Konfigurations-Datei abgelegt. Diese gilt es nun noch zu aktivieren. Hierfür entfernt man das Kommentar-Zeichen in der httpd.conf.</p>
<p>Vorher:</p>
<pre>// httpd.conf
# Virtual hosts
# Include conf/extra/httpd-vhosts.conf
</pre>
<p>Nachher:</p>
<pre>// httpd.conf
# Virtual hosts
Include conf/extra/httpd-vhosts.conf
</pre>
<h2>Schritt 2: httpd-vhosts.conf anpassen</h2>
<p>Kommen wir nun zum eigentlich erfassen unserer virtuellen Hosts. Diese werden in der Datei "httpd-vhosts.conf" hinzugefügt. Zu finden unter:</p>
<pre>/usr/local/zend/apache2/conf/extra/</pre>
<pre>Apfel:conf rofflox$ cd extra/
Apfel:extra rofflox$ sudo vi httpd-vhosts.conf
</pre>
<p>Die Datei enthält bereits einige vorkonfigurierte virtuelle Hosts. Diese kann man getrost löschen, da ansonsten beim Starten vom Apache unnötige Fehlermeldungen erscheinen. Am besten ersetzt ihr die Datei mit der von mir angehängten.</p>
<p>
<pre class="brush: plain; title: ; notranslate">// httpd-vhosts.conf
#
# Virtual Hosts
#
# If you want to maintain multiple domains/hostnames on your
# machine you can setup VirtualHost containers for them. Most configurations
# use only name-based virtual hosts so the server doesn't need to worry about
# IP addresses. This is indicated by the asterisks in the directives below.
#
# Please see the documentation at
# &lt;URL:http://httpd.apache.org/docs/2.2/vhosts/&gt;
# for further details before you try to setup virtual hosts.
#
# You may use the command line option '-S' to verify your virtual host
# configuration.

#
# Use name-based virtual hosting.
#
NameVirtualHost *:80

#
# VirtualHost example:
# Almost any Apache directive may go into a VirtualHost container.
# The first VirtualHost section is used for all requests that do not
# match a ServerName or ServerAlias in any &lt;VirtualHost&gt; block.

&lt;VirtualHost *:80&gt;
    DocumentRoot &quot;/usr/local/zend/apache2/htdocs&quot;
&lt;/VirtualHost&gt;

# webeo.local VirtualHost
&lt;VirtualHost *:80&gt;
    DocumentRoot &quot;/usr/local/zend/apache2/htdocs/zf_webeo/public&quot;
    ServerName webeo.local
    &lt;Directory &quot;/usr/local/zend/apache2/htdocs/zf_webeo/public&quot;&gt;
        Options FollowSymLinks
        AllowOverride All
        Order deny,allow
        Allow from all
    &lt;/Directory&gt;
&lt;/VirtualHost&gt;

# shift.local VirtualHost
&lt;VirtualHost *:80&gt;
    DocumentRoot &quot;/usr/local/zend/apache2/htdocs/zf_shift/public&quot;
    ServerName shift.local
    &lt;Directory &quot;/usr/local/zend/apache2/htdocs/zf_shift/public&quot;&gt;
        Options FollowSymLinks
        AllowOverride All
        Order deny,allow
        Allow from all
    &lt;/Directory&gt;
&lt;/VirtualHost&gt;

# berta.local VirtualHost
&lt;VirtualHost *:80&gt;
    DocumentRoot &quot;/usr/local/zend/apache2/htdocs/zf_berta/public&quot;
    ServerName berta.local
    &lt;Directory &quot;/usr/local/zend/apache2/htdocs/zf_berta/public&quot;&gt;
        Options FollowSymLinks
        AllowOverride All
        Order deny,allow
        Allow from all
    &lt;/Directory&gt;
&lt;/VirtualHost&gt;
</pre>
</p>
<p>Wichtig hierbei: Der erste VirtualHost muss zwingend auf das eigentliche htdocs-Verzeichnis zeigen. Ansonsten erhält man beim Aufruf über http://localhost nicht die Standard-Indexseite von Zend Server CE angezeigt sondern die des ersten VirtualHosts den man definiert hat.</p>
<p>In den eigenen VirtualHosts müsst ihr anschliessend nur noch den DocumentRoot sowie die Directory-Direktive auf euer gewünschtes Verzeichnis setzen. Als ServerName gibt ihr zudem den Namen ein, über welchen ihr das Verzeichnis anschliessend im Browser ansprechen wollt.</p>
<h2>Schritt 3: Hinzufügen der ServerNamen in der hosts Datei</h2>
<p>Nun müssen wir die vorher definierten Servernamen noch der lokalen hosts-Datei bekannt machen. Zu finden unter:</p>
<pre>/etc/hosts</pre>
<p>Beim Aufruf eines Domainnamens im Browser, fragt der Computer normalerweise den DNS-Server eures Providers ab um den Domainnamen in eine IP-Adresse aufzulösen. Bevor dies jedoch geschieht, überprüft er ob in seiner lokale /etc/hosts Datei bereits ein Eintrag vorhanden ist. Dies machen wir uns nun zunutze und fügen dort unsere Servernamen ein:</p>
<pre>Apfel:extra rofflox$ vi /etc/hosts</pre>
<pre>// hosts
##
# Host Database
#
# localhost is used to configure the loopback interface
# when the system is booting.  Do not change this entry.
##
127.0.0.1       localhost
127.0.0.1       apfel
127.0.0.1       localhost.local
255.255.255.255 broadcasthost

# Apache VHosts
127.0.0.1       webeo.local
127.0.0.1       shift.local
127.0.0.1       berta.local
</pre>
<h2>Schritt 4: Neustarten des Apache-Webservers</h2>
<p>Als letzten Schritt müssen wir nur noch den Webserver neustarten. Gebt hierzu folgenden Befehl im Terminal ein:</p>
<pre>Apfel:extra rofflox$ sudo /usr/local/zend/apache2/bin/apachectl restart
/usr/local/zend/apache2/bin/apachectl restart [OK]
</pre>
<h2>Testen</h2>
<p>Wenn ihr nun eine der oben definierten Namen in euren Browser eingebt, müsste dort eure Projektseite geladen werden. Falls nicht, läuft entweder ein anderer Webserver auf Port 80 (Insbesondere wenn ihr Websharing unter Mac OS X aktiviert habt) oder aber ihr habt in einer Datei irgend einen Tippfehler drin. Normalerweise sollte dann jedoch bereits beim Neustart des Apache-Webservers eine entsprechende Meldung ausgegeben werden.</p>
<h2>Nun seid ihr dran</h2>
<p>Hat bei euch alles auf Anhieb geklappt? Benutzt ihr ebenfalls eigene VirtualHosts für eure Projekte oder habt ihr gar eine andere, einfachere Variante?</p>
]]></content:encoded>
			<wfw:commentRss>https://www.webeo.ch/journal/mehrere-virtualhosts-mit-zend-server-ce/feed</wfw:commentRss>
		<slash:comments>7</slash:comments>
		</item>
		<item>
		<title>Redesign der Webeo Webseite ist online!</title>
		<link>https://www.webeo.ch/journal/redesign-der-webeo-webseite-ist-online</link>
		<comments>https://www.webeo.ch/journal/redesign-der-webeo-webseite-ist-online#comments</comments>
		<pubDate>Mon, 12 Jul 2010 15:26:14 +0000</pubDate>
		<dc:creator>Roman</dc:creator>
				<category><![CDATA[Allgemein]]></category>
		<category><![CDATA[PHP]]></category>
		<category><![CDATA[Webeo]]></category>
		<category><![CDATA[framework]]></category>
		<category><![CDATA[php]]></category>
		<category><![CDATA[redesign]]></category>
		<category><![CDATA[webeo]]></category>
		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://webeo.ch/wp/?p=743</guid>
		<description><![CDATA[<p>Der Sommer wird noch heisser! Webeo präsentiert sich in einem überarbeitetem Design.</p>]]></description>
			<content:encoded><![CDATA[<p>Der Sommer wird noch heisser! Webeo präsentiert sich in einem überarbeitetem Design. Heller, Schlichter und schicker soll es sein. Besonderes Augenmerk wurde diesmal auf den Service-Bereich sowie unser Portfolio gelegt. Neu können dort nicht nur die Arbeiten detaillierter präsentiert werden, ebenso ist es dem Besucher möglich nach speziellen Kriterien zu filtern.</p>
<p>Möglich macht dies ein eigenes entwickeltes Themeframework auf Basis von WordPress 3.0 und PHP5. Für den Besucher nicht zu sehen, die zahlreichen administrativen Möglichkeiten für den Seitenbetreiber.</p>
<p>Wir freuen uns auf Euer Feedback!</p>
]]></content:encoded>
			<wfw:commentRss>https://www.webeo.ch/journal/redesign-der-webeo-webseite-ist-online/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Gustav Keller AG mit neuem Webauftritt</title>
		<link>https://www.webeo.ch/journal/gustav-keller-ag-mit-neuem-webauftritt</link>
		<comments>https://www.webeo.ch/journal/gustav-keller-ag-mit-neuem-webauftritt#comments</comments>
		<pubDate>Sun, 09 May 2010 16:59:22 +0000</pubDate>
		<dc:creator>Roman</dc:creator>
				<category><![CDATA[Design]]></category>
		<category><![CDATA[Webeo]]></category>
		<category><![CDATA[kunde]]></category>
		<category><![CDATA[redesign]]></category>
		<category><![CDATA[webauftritt]]></category>
		<category><![CDATA[wordpress]]></category>

		<guid isPermaLink="false">http://www.webeo.ch/?p=394</guid>
		<description><![CDATA[<p>Pünktlich zum Sommerbeginn präsentiert sich die Gustav Keller AG in überarbeitetem Design. Doch nicht nur für das Auge hat sich viel getan, im Hintergrund wurde WordPress als CMS integriert.</p>]]></description>
			<content:encoded><![CDATA[<p>Pünktlich zum Sommerbeginn präsentiert sich die Gustav Keller AG in  überarbeitetem Design. Doch nicht nur für das Auge hat sich viel getan, im Hintergrund wurde WordPress als CMS integriert.</p>
<p><a href="http://www.gustavkeller.ch" target="_blank" class="button-bluedark"><span>Seite besuchen</span></a></p>
<div class="clear">Besonders hervorzuheben sind dabei:</p>
<ul>
<li>Modernes Layout und Design, angepasst ans Katalogdesign.</li>
<li>Grössere Schriften, mehr Weissraum und generell mehr Platz zollen den  immer grösser werdenden Monitorauflösungen Tribut.</li>
<li>Moderne Landingpage mit aktuellen Informationen für gerade laufende  Aktionen, Ankündigungen und Übersicht über aktuelle  Unternehmensneuigkeiten.</li>
<li>Komplette Verwaltung aller Inhalte über ein Content Management System.</li>
<li>Multilinguale Umsetzung in Deutsch und Französisch.</li>
</ul>
</div>
]]></content:encoded>
			<wfw:commentRss>https://www.webeo.ch/journal/gustav-keller-ag-mit-neuem-webauftritt/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
		<item>
		<title>Casestudie: Neue Schweizer Banknoten</title>
		<link>https://www.webeo.ch/journal/casestudie-neue-schweizer-banknoten</link>
		<comments>https://www.webeo.ch/journal/casestudie-neue-schweizer-banknoten#comments</comments>
		<pubDate>Sun, 03 Jan 2010 15:56:34 +0000</pubDate>
		<dc:creator>Roman</dc:creator>
				<category><![CDATA[Design]]></category>
		<category><![CDATA[Webeo]]></category>
		<category><![CDATA[banknote]]></category>
		<category><![CDATA[casestudie]]></category>
		<category><![CDATA[design]]></category>
		<category><![CDATA[schweiz]]></category>

		<guid isPermaLink="false">http://www.webeo.ch/?p=346</guid>
		<description><![CDATA[Webeo startet aus aktuellem Anlass eine eigene Casestudie zum Design einer neuen 100 Franken Banknote. Thema und Design soll die globale Vernetzung darstellen.]]></description>
			<content:encoded><![CDATA[<p>Die Schweizerische Nationalbank hatte im Jahre 2005 einige Grafiker <a href="http://www.snb.ch/de/iabout/cash/newcash/id/cash_new" target="_blank">zu einem Wettbewerb</a> geladen um die neuen Schweizer Banknoten zu entwerfen. Bisher hat man nur wenig Neues erfahren. Bspw. dass der von der Jury beworbene 2. Platz dennoch das Rennen machte, da sich die Motive anscheinend besser zur Implementierung der Sicherheitselemente eigneten. Ob dies der wirkliche Grund war oder ob sich die Jury im nachhinein dennoch aufgrund der doch sehr gewagten Motive von Manuel Krebs umentscheiden musste, sei mal dahin gestellt.</p>
<p>Fakt ist jedoch, dass Manuela Pfrunder den Auftrag zugesprochen bekam und nun bis Mitte 2010 Zeit hat, die anfänglichen Entwürfe näher auszuarbeiten. Man darf also gespannt sein. Mir persönlich gefiehl bereits der <a href="http://www.snb.ch/de/iabout/cash/newcash/id/cash_new_result/3" target="_blank">Roh-Entwurf</a>, wenn mans denn so nennen kann. Generell bin ich über die meisten Vorschläge positiv überrascht.</p>
<p>Obwohl ich mit den bisherigen Banknoten von Jörg Zintzmeyer mehr als zufrieden bin, gibts einen Punkt der mir nie wirklich gefiel. Auf jeder Banknotenserie waren Portraits von mehr oder weniger bekannte Personen abgebildet. Die neuen Entwürfe gehen hier endlich einen Schritt weiter und zeigen (natur)wissenschaftliche "Wunder" und Phänomene (Embryo, DNA, Gehirnsynapsen usw.).</p>
<p>Leider fehlte mir als Informatiker auch hier ein Zeichen der vernetzten, modernen Welt, weshalb ich mir einen kleinen Exkurs ins Banknotendesign gegönnt habe.</p>
<p style="text-align: center;"><a href="http://www.webeo.ch/wp/wp-content/uploads/2010/07/banknoten_entwurf_grid.jpg" rel="prettyPhoto[346]"><img class="aligncenter size-medium wp-image-595" title="Schweizer 100er Banknote" src="http://www.webeo.ch/wp/wp-content/uploads/2010/07/banknoten_entwurf_grid.jpg" alt="" width="596" height="614" /></a></p>
<p><a href="http://www.webeo.ch/wp-content/uploads/2010/01/banknoten_entwurf_overview.jpg"><br />
</a></p>
<p>Das Ganze soll die globale Vernetzung verdeutlichen, welche in der modernen Welt tagtäglich Einzug hält. Für die Einen mag es einen Fluch sein, für die Anderen ist es eine neue Art der Kommunikation und des Geschäftens. Auf jedenfall betrifft es heutzutage jede Person und ist in meinen Augen daher perfekt für ein Banknoten-Motiv geeignet. Hinzu kamen gewisse Sicherheitsrelevante Aspekte wie Wasserzeichen oder der RFID-Chip, welcher breits im biometrischen Pass zu finden ist. Schlussendlich soll mit zwei Schweizerkreuzen die Herkunft symbolisiert werden.</p>
]]></content:encoded>
			<wfw:commentRss>https://www.webeo.ch/journal/casestudie-neue-schweizer-banknoten/feed</wfw:commentRss>
		<slash:comments>0</slash:comments>
		</item>
	</channel>
</rss>

<!-- Performance optimized by W3 Total Cache. Learn more: http://www.w3-edge.com/wordpress-plugins/

Minified using disk: basic
Page Caching using disk: enhanced

Served from: www.webeo.ch @ 2012-02-06 08:54:35 -->
