☀️ 🌙
Laden

NützlichesNützliches

Hilfreiche Lösungen

Bei der täglichen Arbeit mit WordPress, Joomla, Typo3 und Co, treten in Abständen immer wieder die gleichen Probleme auf. Nachfolgend notiere ich die wichtigsten und gebe gleich die passende Lösung dafür.

Diese Lösung muss nicht immer sauber sein, dies bedeutet das es auch bessere Lösungen für das jeweilige Problem geben kann. Hierbei ist jedoch immer eine Gesamtbetrachtung nötig. Bei den hier präsentierten Lösungen war der entsprechende Lösungsansatz autonom.

Die Verwendung der hier vorgestellten Lösungen, geschieht natürlich auf eigene Gefahr. Ich rate dringend dazu, eine vorherige Sicherung anzulegen.

WordPress

Nachfolgend nützliche Erweiterungen für die wp-config.php Datei.

define('WP_HOME','https://ihredomain.de'); //WordPress-Adresse (Backend)
define('WP_SITEURL','https://ihredomain.de'); //Webseiten-Adresse (Startseite)

define('WP_POST_REVISIONS', 5); //Anzahl maximal gespeicherter Revisionen
define('WP_POST_REVISIONS', false); //Revisionen werden nicht gespeichert
define('AUTOSAVE_INTERVAL', 30); //Alle 30 Sekunden wird automatisch gespeichert (Standard 60)
define('EMPTY_TRASH_DAYS', 60); //Alle 60 Tage wir der Papierkorb geleert
define('EMPTY_TRASH_DAYS', 0); //Der Papierkorb wird sofort geleert

define('WP_CACHE', true); //Den internen Cache von WordPress aktivieren
define('WP_MEMORY_LIMIT', '128M'); //Hauptspeicher in MByte festlegen, der für WordPress zur Verfügung steht
define('WP_MEMORY_LIMIT', '8M'); //Upload-Limit auf zum Beispiel 8, 16, 32, 64, 128 MB erhöhen

define( 'WP_EMOICONS', false ); //Emojis deaktivieren
define( 'IMAGE_EDIT_OVERWRITE', true ); //Überschreibt vorhandene gleiche Bilder

define('DISALLOW_FILE_EDIT', TRUE); //Editieren der Dateien im Editor verhindern
define('FORCE_SSL_LOGIN', true); //SSL-Login erzwingen

define('AUTOMATIC_UPDATER_DISABLED', true); //Automatische Updates deaktivieren
define( 'WP_AUTO_UPDATE_CORE', false ); //Deaktivieren aller automatischen WordPress-Updates

Mit folgenden Code Snippet, der in der function.php des Themes eingefügt werden muss, entfernt man die Angaben zum entsprechenden Meta Generator.

/* Kommentar Feed entfernen */
add_filter( 'feed_links_show_comments_feed', '__return_false' );

/* WordPress Generator entfernen */
remove_action('wp_head', 'wp_generator');

/* Slider Revolution Meta Generator entfernen */
function remove_revslider_meta_tag() { return ""; }
add_filter( 'revslider_meta_generator', 'remove_revslider_meta_tag' );

Generell ist es möglich innerhalb eines WordPress Verzeichnisses mehrere weitere WordPress Webseiten zu integrieren. Ob dies immer der beste Weg ist sei dahingestellt.

Nennt man das Unterverzeichnis für die weitere WordPress Installation z.B. /intern/, dann darf in der übergeordneten WordPress Installation dieser Name nicht für eine Unterseite verwendet werden, damit es nicht zu Konflikten kommt.

Zudem muss die .htaccess Datei in dem Unterverzeichnis wie nachfolgend erklärt angepasst werden:

# BEGIN WordPress
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /intern/
RewriteRule ^index.php$ - [L]
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule . /intern/index.php [L]
</IfModule>
# END WordPress

In einigen Fällen kann es hilfreich sein, wenn anstatt einer Fehlerseite, eine direkte Weiterleitung zur Startseite erfolgen soll. Aus SEO Sicht ist dies eher nicht zu empfehlen, dennoch ist die Lösung für dieses Szenario recht einfach.

Lösung: Man erstellt sich im Theme Ordner eine eigene 404.php Datei und fügt den nachfolgenden Code ein. Zur Sicherheit sollte die (sofern vorhanden) original 404.php gesichert werden, falls man diese päter doch noch verwenden möchte.

header("HTTP/1.1 301 Moved Permanently"); 
header("Location:".get_bloginfo('url')); 
exit();

Eine generelle .htaccess Lösung ist unter Server/Hoster zu finden.

Ein Menülink lässt sich in WordPress nur mit bestimmten Elementen verlinken bzw. mit externen Seiten. Wenn ein Menülink z.B. ein Modal öffnen soll, ist dies meist komplizierter. Abhilfe schafft hier ein jQuery Skript.

Lösung: Man tauscht hier lediglich die ID des gewünschten Menülinks und definiert den Code, der beim Klick auf diesen Menüpunkt ausgeführt werden soll.

jQuery(document).ready(function(){
    jQuery("#menu-item-50").bind( "click", function() {
        //Hier der gewünschte Code
    });
});

Nicht erst seit der DSGVO bietet es sich an, Web Schriftarten selbst zu hosten. Je nachdem welches Theme verwendet wird, geht dies mal einfacher, mal schwerer, da die Schriften unterschiedlichen eingebunden sind.

Abhilfe schafft hier ein Plugin, das automatisch die verwendeten Schriftarten erkennt und lokal auf dem Server abspeichert.

Lösung: https://wordpress.org/plugins/selfhost-google-fonts/

Bietet das verwendete Theme keine Möglichkeiten, den Bild Titel darzustellen, lässt sich mit ein wenig PHP, dieser Umstand ändern. Dies funktioniert in dem hier beschriebenen Fall auch nur, wenn die ID des Bildes bzw. Anhangs zuvor abgefragt wird.

Lösung:

$attachment_title = get_the_title($attach_id)

Mit folgenden Code in der functions.php, lässt sich ein eigenes Logo oberhalb der Login Box erstellen.

function geek_login_logo() {
?>
 <style type="text/css">
 #login h1 a, .login h1 a {
 background-image: url(https://www.cms-geek.de/wp-content/uploads/logo.png);
 margin-bottom: 0;
 background-size: 200px;
 height: 200px;
 width: 200px;
 margin-left: auto;
 margin-right: auto;
 border-radius: 50%;
 }
 body.login {background-color: #fff;} .login #backtoblog a, .login #nav a {color: #ff0000 !important}
 </style>
<?php }
add_action( 'login_enqueue_scripts', 'geek_login_logo' );

Folgender Code kann als neue Template Datei im Theme Ordner verwendet werden:

<?php
/*
Template Name: Password Reset Template
*/
global $wpdb, $user_ID;

function tg_validate_url() {
    global $post;
    $page_url = esc_url( get_permalink( $post->ID ));
    $urlget    = strpos( $page_url, "?" );
    if ($urlget === false) {
        $concate = "?";
    } else {
        $concate = "&";
    }
    return $page_url.$concate;
}

if ( !$user_ID ) { //block logged in users
    if(isset( $_POST['action']) && $_POST['action']=='save_pw_reset' ){
    if ( !wp_verify_nonce( $_POST['save_pwd_nonce'], "save_reset_password" ) ) {
      exit("No trick please");
    }
        $user_login = base64_decode( $_POST['user_token'] );
        $user_data  = $wpdb->get_row( "SELECT ID, user_login, user_email FROM $wpdb->users WHERE ID = '". $user_login ."'" );
        if( $user_data )
        {
            wp_set_password( $_POST['new_password'], $user_login );
            $wpdb->update( $wpdb->users,array('user_activation_key'=>'' ), array( 'ID'=>$user_login ) );
            echo json_encode( array( 'status' => 'success', 'msg' => 'Password Reset successfully' ) );
        }else{
            echo json_encode( array( 'status' => 'error', 'msg' => 'please try again' ) );
        }
        exit();
    }

    if(isset( $_GET['key'] ) && $_GET['action'] == "reset_pwd" ) {
        $reset_key  = $_GET['key'];
        $user_login = base64_decode($_GET['login']);
        $user_data  = $wpdb->get_row( $wpdb->prepare( "SELECT ID, user_login, user_email FROM $wpdb->users WHERE user_activation_key = %s AND (user_login = %s OR user_email = %s )" , $reset_key, $user_login,$user_login ));
       
        $user_login = $user_data->user_login;
        $user_email = $user_data->user_email;
       
        if( !empty( $reset_key ) && !empty( $user_data ) ) {           
      get_header();
?>
      <div data-height="200" class="title_outer title_without_animation">
        <div style="height:100px;" class="title title_size_small  position_left ">
          <div class="image not_responsive"></div>
          <div style="height:100px;" class="title_holder">
            <div class="container">
              <div class="container_inner clearfix">
                <div class="title_subtitle_holder">
                  <h1><span><?php echo the_title();  ?></span></h1>
                </div>
              </div>
            </div>
          </div>
        </div>
      </div>
      <div class="container">
        <?php if( isset( $qode_options_proya['overlapping_content'] ) && $qode_options_proya['overlapping_content'] == 'yes' ) { ?>
          <div class="overlapping_content"><div class="overlapping_content_inner">
        <?php } ?>
        <div class="container_inner forgot_password default_template_holder clearfix">
          <div class="forgot_box">               
            <?php if ( have_posts() ) : ?>   
              <?php while ( have_posts() ) : the_post(); ?>
                <div class="pwd_res_container">
                  <p>Please enter your new password.</p>
                  <form method="post" action=""  id="saveresetpwdfrm">
                    <label>New password</label>
                    <br />           
                    <input type="password" class="text" name="new_password" id="new_password"  value="" style="width:200px" /><br /><br />
                    <label>Repeat new password</label>
                    <br />       
                    <input type="hidden" name="user_email" value="<?php echo $user_email; ?>" />
                    <input type="password" class="text" name="repeat_new_password" id="repeat_new_password" value="" style="width:200px" /><br />
                    <input type="hidden" name="action" value="save_pw_reset" />
                    <input type="hidden" name="save_pwd_nonce" value="<?php echo wp_create_nonce("save_reset_password"); ?>" />
                    <input type="hidden" name="user_token" value="<?php echo base64_encode($user_data->ID); ?>" />
                    <input type="submit" id="savesubmitbtn" class="reset_password" name="submit" value="Save" />       
                  </form>
                  <div id="result"></div> <!-- To hold validation results -->
                </div>
                <script type="text/javascript">                         
                jQuery( "#saveresetpwdfrm" ).submit( function() {
                  if( jQuery( '#new_password' ).val() != '' ){
                    if( jQuery( '#repeat_new_password' ).val() != '' ){
                      if( jQuery( '#new_password' ).val() == jQuery( '#repeat_new_password' ).val() ){
                          jQuery( '#result' ).html( '<span class="loading">Validating...</span>' ).fadeIn();
                          var input_data = jQuery( '#saveresetpwdfrm' ).serialize();
                          jQuery.ajax({
                            type: "POST",
                            url:  "<?php echo get_permalink( $post->ID ); ?>",
                            data: input_data,
                            success: function( msg ){
                              change_data = JSON.parse(msg);
                              if(change_data.status=='success'){
                                jQuery( '.pwd_res_container' ).html( '<div><p>Your password reset successfully.</p><p><a href="<?php echo home_url('/login'); ?>">Click here </a> to logged in.</p></div>' ).fadeIn();
                              }else{
                                jQuery( '#result' ).html( change_data.msg ).fadeIn();
                              }
                            }
                          });
                      }else{
                        jQuery( '#result' ).html( '<span class="loading">Password and repeat password not matched</span>' ).fadeIn();
                      }
                    }else{
                      jQuery( '#result' ).html( '<span class="loading">Please enter repeat password</span>' ).fadeIn();
                    }
                  }else{
                    jQuery( '#result' ).html( '<span class="loading">Please enter password</span>' ).fadeIn();
                  }
                  return false;
                });
                </script>           
              <?php endwhile; ?>
            <?php else : ?>       
            <h2><?php _e('Not Found'); ?></h1>           
          <?php endif; ?>           
          </div>
        </div>
        <?php if(isset($qode_options_proya['overlapping_content']) && $qode_options_proya['overlapping_content'] == 'yes') {?>
          </div></div>
        <?php } ?>
        <style>
        .container_inner.forgot_password.default_template_holder.clearfix > div {
          margin-bottom: 35px;
        }
        #savesubmitbtn {
          background: #ff9900 none repeat scroll 0 0;
          border: medium none;
          border-radius: 3px;
          color: #fff !important;
          font-size: 15px;
          margin-top: 15px;
          padding: 10px;
          text-transform: capitalize;
          cursor: pointer;
        }
        .forgot_box {
          border: 2px solid rgb(0, 128, 0);
          padding: 20px;
          width: 50%;
        }
        .pwd_res_container  div#result {
          font-style: italic;
          font-weight: bold;
          margin-top: 20px;
        }
        </style>
      </div>
<?php
      get_footer();          
        }else{
            $redirect_to = get_permalink( $post->ID );
            wp_safe_redirect($redirect_to);
            exit();
        }       
    }
    //exit();

    if( $_POST['action'] == "tg_pwd_reset" ){
        if ( !wp_verify_nonce( $_POST['tg_pwd_nonce'], "tg_pwd_nonce" )) {
          exit( "No trick please" );
       } 
        if( empty( $_POST['user_input'] ) ) {
            echo "<div class='error'>Please enter your Username or E-mail address</div>";
            exit();
        }
        //We shall SQL escape the input
        $user_input = $wpdb->escape( trim( $_POST['user_input'] ) );
       
        if ( strpos( $user_input, '@' ) ) {
            $user_data = get_user_by_email( $user_input );
            if( empty( $user_data )  ) { //delete the condition $user_data->caps[administrator] == 1, if you want to allow password reset for admins also
                echo "<div class='error'>Invalid E-mail address!</div>";
                exit();
            }
        }else {
            $user_data = get_userdatabylogin($user_input);
            if(empty( $user_data ) ) { //delete the condition $user_data->caps[administrator] == 1, if you want to allow password reset for admins also
                echo "<div class='error'>Invalid Username!</div>";
                exit();
            }
        }
       
        $user_login = $user_data->user_login;
        $user_email = $user_data->user_email;
       
        $key = $wpdb->get_var( $wpdb->prepare( "SELECT user_activation_key FROM $wpdb->users WHERE user_login = %s OR user_email = %s ", $user_login ) );       
        //generate reset key
        $key = wp_generate_password( 20, false );
          
        $key = sha1( $key . $user_email . uniqid( time(), true ) );
        $wpdb->update( $wpdb->users, array( 'user_activation_key' => $key), array( 'user_login' => $user_login ) );   
       
        //mailing reset details to the user
        $message = __( 'Someone requested that the password be reset for the following account:' ) . "\r\n\r\n";
        $message .= get_option('siteurl') . "\r\n\r\n";
        $message .= sprintf( __( 'Username: %s' ), $user_login ) . "\r\n\r\n";
        $message .= __( 'If this was a mistake, just ignore this email and nothing will happen.' ) . "\r\n\r\n";
        $message .= __( 'To reset your password, visit the following address:' ) . "\r\n\r\n";
        $message .= tg_validate_url() . "action=reset_pwd&key=$key&login=" . base64_encode($user_login) . "\r\n";
        $headers[] = 'From: Max Muster < test@test.de >';
        if ( $message && !wp_mail( $user_email, 'Password Reset Request', $message, $headers ) ) {
            echo "<div class='error'>Email failed to send for some unknown reason.</div>";
            exit();
        }else {
            echo "<div class='success'>We have just sent you an email with Password reset instructions.</div>";
            exit();
        }       
    } else if( $_REQUEST['action']!='reset_pwd' ) {
    get_header(); 
?>
    <div data-height="200" class="title_outer title_without_animation">
      <div style="height:100px;" class="title title_size_small  position_left ">
        <div class="image not_responsive"></div>
        <div style="height:100px;" class="title_holder">
          <div class="container">
            <div class="container_inner clearfix">
              <div class="title_subtitle_holder">
                <h1><span><?php echo the_title();  ?></span></h1>
              </div>
            </div>
          </div>
        </div>
      </div>
    </div>
    <div class="container">
            <?php if( isset($qode_options_proya['overlapping_content'] ) && $qode_options_proya['overlapping_content'] == 'yes' ){ ?>
                <div class="overlapping_content"><div class="overlapping_content_inner">
            <?php } ?>
            <div class="container_inner forgot_password default_template_holder clearfix">
        <div class="forgot_box">               
          <?php if ( have_posts() ) : ?>   
            <?php while ( have_posts() ) : the_post(); ?>
              <p>Please enter your username or email address. <br/>You will receive a link to create a new password via email.</p>
              <form class="user_form" id="wp_pass_reset" action="" method="post">   
              <label>Username or Email:</label>
              <br />       
              <input type="text" class="text" name="user_input" value="" style="width:200px" /><br />
              <input type="hidden" name="action" value="tg_pwd_reset" />
              <input type="hidden" name="tg_pwd_nonce" value="<?php echo wp_create_nonce("tg_pwd_nonce"); ?>" />
              <input type="submit" id="submitbtn" class="reset_password" name="submit" value="Reset Password" />                   
              </form>
              <div id="result"></div> <!-- To hold validation results -->
              <script type="text/javascript">                         
                jQuery( '#wp_pass_reset' ).submit(function() {           
                  jQuery( '#result' ).html( '<span class="loading">Validating...</span>' ).fadeIn();
                  var input_data = jQuery( '#wp_pass_reset' ).serialize();
                  jQuery.ajax({
                    type: "POST",
                    url:  "<?php echo get_permalink( $post->ID ); ?>",
                    data: input_data,
                    success: function( msg ){
                    jQuery( '.loading' ).remove();
                    jQuery( '<div>' ).html( msg ).appendTo( 'div#result' ).hide().fadeIn( 'slow' );
                  }
                });
                return false;                 
              });
              </script>               
            <?php endwhile; ?>       
          <?php else : ?>       
          <h2><?php _e('Not Found'); ?></h1>           
        <?php endif; ?>
        </div>
      </div>
            <?php if( isset($qode_options_proya['overlapping_content'] ) && $qode_options_proya['overlapping_content'] == 'yes' ){ ?>
                </div></div>
            <?php } ?>
        </div>
        <style>
        .container_inner.forgot_password.default_template_holder.clearfix > div {
      margin-bottom: 35px;
    }
    #submitbtn.reset_password, #savesubmitbtn {
      background: #ff9900 none repeat scroll 0 0;
      border: medium none;
      border-radius: 3px;
      color: #fff !important;
      font-size: 15px;
      margin-top: 15px;
      padding: 10px;
      text-transform: capitalize;
    }
    .forgot_box {
      border: 2px solid rgb(0, 128, 0);
      padding: 20px;
      width: 50%;
    }
    .forgot_box > div#result {
      font-style: italic;
      font-weight: bold;
      margin-top: 20px;
    }
        </style>
<?php
    get_footer();
    }
}
else {
    wp_redirect( home_url() ); exit;
    //redirect logged in user to home page
}
?>

In manchen Fällen ist es sinnvoll bestimmte Beiträge zwar auf der Blog Seite anzuzeigen, jedoch nicht im Archiv.

Lösung: Zuerst sollten die betreffenden Beiträge einer bestimmten Kategorie zugeordnet werden. Mit dem nachfolgenden Skript wird dann diese Kategorie aus dem Archiv entfernt. Die ID in dem Skript muss dann durch die erstellte Kategorie ID ersetzt werden.

function exclude_stuff($query) {
if ( $query->is_date) {
$query->set('cat', '-6');
}
return $query;
}
add_filter('pre_get_posts', 'exclude_stuff');

In manchen Fällen ist es ratsam eigne Custom Post Types zu erstellen. Hierbei handelt es sich um eine Erweiterung für Inhalte, wobei diese Inhalte nicht auf eine bestimmte Art hin festgelegt sind. Das nachfolgende Code Snippet erstellt einen Custom Post Type für Ansprechpartner und sorgt zugleich dazu, dass hier ein eigener Kategoriebaum zu erstellt wird.

function ansprechpartner_post_type() {
$labels = array(
'name' => 'Ansprechpartner Einträge',
'singular_name' => 'Ansprechpartner',
'menu_name' => 'Ansprechpartner',
'parent_item_colon' => '',
'all_items' => 'Alle Einträge',
'view_item' => 'Eintrag ansehen',
'add_new_item' => 'Neuer Eintrag',
'add_new' => 'Hinzufügen',
'edit_item' => 'Eintrag bearbeiten',
'update_item' => 'Update Eintrag',
'search_items' => '',
'not_found' => '',
'not_found_in_trash' => '',
);
$rewrite = array(
'slug' => 'neuer_ansprechpartner',
'with_front' => true,
'pages' => true,
'feeds' => true,
);
$args = array(
'labels' => $labels,
'supports' => array( 'title', 'editor', 'excerpt', 'thumbnail', 'comments', 'trackbacks', ),
'taxonomies' => array('post_tag'),
'hierarchical' => true,
'public' => true,
'show_ui' => true,
'show_in_menu' => true,
'show_in_nav_menus' => true,
'show_in_admin_bar' => true,
'menu_position' => 5,
'can_export' => false,
'has_archive' => true,
'exclude_from_search' => false,
'publicly_queryable' => true,
'rewrite' => $rewrite,
'capability_type' => 'page',
);
register_post_type( 'neuer_ansprechpartner', $args );
}
add_action( 'init', 'ansprechpartner_post_type', 0 );

add_action( 'init', 'ansprechpartner_post_type_custom_taxonomy', 0 );
function ansprechpartner_post_type_custom_taxonomy() {
  $labels = array(
    'name' => _x( 'Abteilungen', 'taxonomy general name' ),
    'singular_name' => _x( 'Abteilung', 'taxonomy singular name' ),
    'search_items' =>  __( 'Abteilung suchen' ),
    'all_items' => __( 'Alle Abteilungen' ),
    'parent_item' => __( 'Kategorie' ),
    'parent_item_colon' => __( 'Parent Type:' ),
    'edit_item' => __( 'Abteilung bearbeiten' ), 
    'update_item' => __( 'Aktualisieren' ),
    'add_new_item' => __( 'Neu hinzufügen' ),
    'new_item_name' => __( 'New Type Name' ),
    'menu_name' => __( 'Abteilungen' ),
  ); 	
  register_taxonomy('abteilungen',array('neuer_ansprechpartner'), array(
    'hierarchical' => true,
    'labels' => $labels,
    'show_ui' => true,
    'show_admin_column' => true,
    'query_var' => true,
    'rewrite' => array( 'slug' => 'abteilung' ),
  ));
}

Von Haus aus können SVG Dateien nicht in die Mediathek geladen werden. Dies geschieht aus Sicherheitsgründen und soll vor manipulierten SVG Dateien schützen. Wurde diese Datei von einem selbst erstellt, ist das Risiko sicher gering.

Lösung: Die einfachste Variante ist das verwenden eines Plugins, das den Upload ermöglicht. Befindet sich die Datei einmal in der Mediathek, kann das Plugin natürlich auch wieder deinstalliert werden. Gute Erfahrungen habe ich mit SVG Support gemacht.

Wird die SVG Grafik in manchen Browsern nicht gerendert hilft möglicherweise folgendes:
Die entsprechende SV Datei mit einem Texteditor.

xlink:href="data:img/png;base64,

ersetzen durch

xlink:href="data:image/png;base64,

Aus SEO Sicht kann es ratsam sein, bestimmte Unterseiten statisch anzulegen. So lassen sich auf dieser statischen Unterseite alle Skripte entfernen, die nicht benötigt werden, sowie das für diese Seite verwendete Keyword, für sämtliche Bilder, etc. verwenden. Generell sollte einem natürlich bewusst sein, dass diese statische Seite bei zukünftigen Änderungen schwieriger zu bearbeiten ist, da hier direkt im Quelltext gearbeitet werden muss.

Lösung: Auch hier gibt es verschiedene Plugins, wie u.a. WP2Static. In meinem Fall funktioniert keines der beworbenen Plugins, wobei dies auch an der Zusammensetzung der verwendeten Plugins gelegen haben könnte.  Die Verwendung von HTTrack funktionierte hingegen problemlos. Hier ließ ich nur die Unterseite tracken, die statisch umgewandelt werden sollte. Im Nachgang fügte ich alle benötigten CSS und Skripte in einen Ordner und wandelte die erzeugte index.html in eine php Datei um. Im Quelltext muss diese Datei wie folgt aufgebaut sein:

<?php
/*
 * Template Name: Gewünschter Template Name
 */
?>

Anschließend wird diese php Datei und der Dateien Ordner, in das Hauptverzeichnis des verwendeten Themes eingefügt. Erstellt man nun in WordPress eine neue Unterseite, wird in der Template Auswahlliste, die hinzugefügte PHP Datei angezeigt. Damit greift diese Unterseite dann genau auf diese php Datei zu. Diese kann dann noch beliebig modifiziert werden.

Folgende Anpassungen sorgen für die richtige responsive Ansicht beim einbetten von Videos in der Webseite:

function evolution_wrap_oembed( $html ){
    $html = preg_replace( '/(width|height)="\d*"\s/', "", $html );
    return'<div class="embed-responsive embed-responsive-16by9">'.$html.'</div>';
}
add_filter( 'embed_oembed_html','evolution_wrap_oembed',10,1);
.embed-responsive.embed-responsive-16by9 {
    position: relative;
    padding-bottom: 56.25%; /* 16:9 */
    padding-top: 25px;
    height: 0;
}
.embed-responsive.embed-responsive-16by9 iframe {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
}
video {
    width: 100% !important;
    height: auto !important;
}

Wie es der Titel schon beschreibt, wird mit dem folgenden Code ein ACF Field, das zuvor natürlich angelegt sein muss, in den Textauszug von Beiträgen integriert.

function testexcerpt() {
    $t_field1 = get_post_meta( get_the_ID(), 'testfeld1', true );
    $t_field2 = get_post_meta( get_the_ID(), 'testfeld2', true );
    $original = get_the_excerpt();
  
    //Verändert nur Beiträge die auf einer bestimmten Seite dargestellt werden
    if ( is_page( 12204 ) ) {
  $excerpt = '<div class="new-meta"><img src="beispiel1.png"><br>'. $t_field1 .' </div><div class="new-meta"><img src="beispiel2.png"><br> '.       
        $t_field2 .' </div> ';
    echo $excerpt;
    } else {
    echo $original;
    }
};
add_filter( 'the_excerpt', 'testexcerpt' );

Integriert man ein Skript / Funktion / Element in der Header oder Footer Datei, wird dieses global auf allen Seiten verwendet. Mit dem nachfolgenden Skript lässt sich dieses nur auf bestimmten Seiten begrenzen.

<?php if ( is_page('ID DER GEWÜNSCHTEN SEITE') ) { ?>
    <!-- Hier kommt das Skript hinein -->
<?php } ?>

Bei Linkedin ist es möglich, Webseite als Referenzen zu integrieren. Dabei wird automatisch ein Bild, Titel und Beschreibungstext von der Webseite übernommen. Titel ist hierbei der Seitenname und die Beschreibung die Meta-Description. Das Bild wiederrum lässt sich durch das Plugin Yoast SEO ändern. Hierzu auf der Startseite in den Bearbeitungsmodus gehen und in den Yoast Einstellungen dieser den Reiter „Social“ > „Facebook Image“ wählen und ein entsprechendes Bild hochladen.

Wurde im Vorfeld schon einmal versucht die Webseite bei Linkedin zu übernehmen, muss eine Aktualisierung dieser Seite vorgenommen werden. Dafür gibt man die gewünschte Domain auf https://www.linkedin.com/post-inspector/inspect/ ein und versucht es anschließend noch einmal.

Seit einigen Jahren benutze ich zur Sicherung von WordPress Webseiten das Plugin „Duplicator“. Die Wiederherstellung ist dank einer .zip Datei und php Datei zum ansprechen kinderleicht von der Hand. In wenigen Fällen funktioniert das Plugin jedoch nicht auf allen Servern bzw. das Entpacken der .zip Datei wird nicht vollständig durchgeführt. In diesen Fällen lässt sich das Archiv auch von Hand entpacken und auf den Server hochladen.

Bei manuellen Wiederherstellen werden oft die zuvor gemachten Theme Einstellungen nicht übernommen. Zudem bietet nicht jedes Theme eine Import/Export Funktion. Hilfreich kann hier das Plugin „Customizer Export/Import“ sein, dass eine Export Datei auf der Ursprungsseite generiert. Diese Datei lässt sich dann auf der Wiederhergestellten Webseite importieren.

Angezeigte Fehler in WordPress sollte man schnellstmöglich beheben, da andernfalls irgendwann genau diese Fehler zu größeren Problemen führen. Beeinträchtigen diese Fehler jedoch das Frontend so sehr, dass eine kurzzeitige Lösung her muss, lassen sich die Fehler (meistens) mit folgenden Code, der in der wp-config hinterlegt werden muß.

ini_set('display_errors','Off');
ini_set('error_reporting', E_ALL );
define('WP_DEBUG', false);
define('WP_DEBUG_DISPLAY', false);

In den meisten Fällen reicht bereits der erste Code:

ini_set('display_errors','Off');

Wenn WordPress nicht mehr richtig läuft, kommen meist die typischen Kandidaten als mögliche Fehlerursache in Frage: Veralteter Core, veraltetes Theme oder Plugin, Probleme aufgrund von geänderter PHP Version, Plugin Konflikte untereinander oder zu geringer Speicher.

Nicht alle Fehler lassen sich auf den ersten Blick feststellen. Bevor man also zu viel Zeit investiert, solle erst einmal der Fehler Log ausgelesen werden. Hierfür muss eine entsprechende Ergänzung in der wp-config.php vorgenommen werden (je nach Hoster müssen dort eventuell zusätzliche Einstellungen vorgenommen werden).

define( 'WP_DEBUG', true );
define( 'WP_DEBUG_LOG', true );
define( 'WP_DEBUG_DISPLAY', false );

Die entsprechende Log Datei sollte dann auf dem Server unter /wp-content/debug.log zu finden sein.

Die folgende Fehlermeldung „Fehler: Cookies sind gesperrt oder werden von Deinem Browser nicht unterstützt. Du musst Cookies aktivieren, um WordPress verwenden zu können.“ lässt sich mit einigen wenigen Anpassungen, meist schnell beheben.

Lösung: Sofern die wp-config.php fehlerfrei ist, können folgenden Ergänzungen darin, das Problem lösen.

define('WP_HOME','http://www.beispielseite.de');
define('WP_SITEURL','http://www.beispielseite.de');
define('COOKIE_DOMAIN','http://www.beispielseite.de');

Und abermals ein Fehler der in Kombination mit einer PHP Umstellung vorkommen kann. Dieser tritt jedoch nicht explizit nur bei WordPress auf, auch Joomla und Drupal sind hiervon betroffen. Generell ist hier eine fehlende Initialisierung eines Arrays Ursprung dieses Fehlers.

Lösung: Nachfolgend mehrere Variante und dessen Lösungen.

$data[] = '<script type="text/javascript">' . NL;

ändern in

$data = [];
$data[] = '<script type="text/javascript">' . NL;

Alternativ auch

$data = array();
$data[] = '<script type="text/javascript">' . NL;

Fehlt die Definition für

$section_style         = '';

muß diese geändert werden in

$section_style         = [];

Tritt der Fehler in dieser Kombination auf

self::$arrMetaBoxes[] = $box;

muss auch hier die Variable definiert werden

self::$arrMetaBoxes = [];
self::$arrMetaBoxes[] = $box;

Der TypeError … is not a function spricht in den meisten Fällen für ein fehlendes jQuery Skript. Das Skript fehlt entweder generell, wurde also nicht im Header integriert, oder wird zu spät geladen, also erst nachdem das eigentliche Skript bereits aufgerufen wurde.

Im genannten Beispiel ist es hingegen das fehlende Bootstrap Skript, das nachfolgend hinzugefügt werden kann.

<script type="text/javascript" src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.5/js/bootstrap.min.js"></script>

Die Kombination aus veralteten Theme und einer PHP Umstellung, kann folgende Fehlermeldung zu Tage führen:

Strict Standards: Declaration of TGM_Bulk_Installer_Skin::before() should be compatible with Bulk_Upgrader_Skin::before($title = '') in /wp-content/themes/xxx/inc/class-tgm-plugin-activation.php on line 1893
Strict Standards: Declaration of TGM_Bulk_Installer_Skin::after() should be compatible with Bulk_Upgrader_Skin::after($title = '') in /wp-content/themes/xxx/inc/class-tgm-plugin-activation.php on line 1893


Lösung:
Hierzu müssen zwei Codezeilen geändert werden, die Zeilennummer kann dabei jedoch abweichen, hier sollte man die Suchfunktion verwenden.

In Zeile 1977 ändert man den Code

public function before() {

in

public function before( $title = '' ) {

Und in Zeile 1999:

public function after() {

in

public function after( $title = '' ) {

Der Aufruf $.browser() wird von neuren jQuery Versionen nicht mehr unterstützt. Fügt man den nachfolgenden Code in die fehlerhafte .js Datei ein, lässt sich die Funktion wiederherstellen.

var matched, browser;

jQuery.uaMatch = function( ua ) {
    ua = ua.toLowerCase();

    var match = /(chrome)[ \/]([\w.]+)/.exec( ua ) ||
        /(webkit)[ \/]([\w.]+)/.exec( ua ) ||
        /(opera)(?:.*version|)[ \/]([\w.]+)/.exec( ua ) ||
        /(msie) ([\w.]+)/.exec( ua ) ||
        ua.indexOf("compatible") < 0 && /(mozilla)(?:.*? rv:([\w.]+)|)/.exec( ua ) ||
        [];

    return {
        browser: match[ 1 ] || "",
        version: match[ 2 ] || "0"
    };
};

matched = jQuery.uaMatch( navigator.userAgent );
browser = {};

if ( matched.browser ) {
    browser[ matched.browser ] = true;
    browser.version = matched.version;
}

// Chrome is Webkit, but Webkit is also Safari.
if ( browser.chrome ) {
    browser.webkit = true;
} else if ( browser.webkit ) {
    browser.safari = true;
}

jQuery.browser = browser;

Tritt der Fehler 500 auf, wird dieser Fehler im Browser direkt angezeigt, jedoch ist auch eine komplett weiße WordPress Seite möglich. In beiden Fällen ist ein Login ins Backend nicht mehr möglich. Tritt der Fehler kurz nach einer PHP Umstellung auf, kann die nachstehende Lösung hilfreich sein.

Lösung: Meist sind es Plugins, die aufgrund der PHP Umstellung einen Fehler produzieren, der über eine einfache Fehlermeldung hinaus geht und somit das gesamte System stört.

Im ersten Schritt sollte man den Ordner /plugins/ umbenennen, um den Fehler einzugrenzen. Hierzu muss mit einem FTP Programm Zugriff auf dem Server erfolgen. Ist dies der Grund sollte man die Plugins nach der Reihe durchgehen, um nach dem Ausschlusskriterium das fehlerhafte Plugin zu lokalisieren.

Leider müssen Fehler nicht immer zwangsläufig durch das WordPress System selbst entstehen, eine nicht leichte Lösung ist es, wenn der Server ein Problem verursacht.

Der Hoster Strato bietet für seine PHP Auswahl z.B. eine Art Boost Funktion an. Im aktivierten Zustand kann diese zu einer gelegentlichen Fehler 500 Meldung führen, wenn man versucht eine Seite oder einen Beitrag zu bearbeiten. Deaktiviert man diese Funktion innerhalb seines Hosting Pakets, läuft die Seite wieder fehlerfrei.

Liegt dieses Problem vor, ist oftmals ein Plugin Konflikt der Grund. In der Regel deutet auch ein Fehler in der Konsole auf eventuelle gründe hin. Kandidaten sind hierfür gerne das Übersetzungsplugin WPML in der Kombination mit dem SEO Plugin YOAST.

Lösung: Aktualisiert man beide Plugins, dürfte auch der Fehler verschwinden und der Editor ist wieder nutzbar.

Der Grund kann vielfältig sein, die Lösung dafür ist hingegen recht einfach.

Lösung: Mit einem FTP Programm verschafft man sich Zugang ins Hauptverzeichnis der WordPress Installation auf dem Server. Hier sollte sich die Datei .maintenance befinden. Wird diese nun gelöscht, sollte auch das System wieder fehlerfrei funktionieren.

Verwendet man html Code im Elementor und nutzt gleichzeitig das Plugin Wordfence, kann es zu Problemen bei speichern kommen.

Lösung: Wenn man Wordfence kurzzeitig deaktiviert, funktioniert auch das Speichern wieder.

Abhängig von beeinflussenden CSS-Eigenschaften, kann es beim Aktivieren von Animationen in Elementor, zu kurzzeitigen auftauchen von horizontalen Scrollbalken kommen, wenn z.B. ein seitliches einfliegen von Elementen eingestellt ist.

Die nachfolgenden CSS-Einstellungen sollten dieses Problem beheben.

body {
  margin: 0;
  overflow-x: hidden;
}

 

Klickt man auf eine Seite um diese mit Elmentor zu bearbeiten, kommt es in Ausnahmefällen zu einem endlosen Laden oder der Fehlermeldung das Elementor nicht geladen werden kann.

Die Fehlerquellen können hier vielfältig sein. Eine Möglichkeit ist jedoch unter „Einstellungen“ > „Permalinks“, diese einmal neu zu speichern.

Zur Fehlerbehebung muss folgender Eintrag in der .htaccess Datei vorgenommen werden:

<IfModule mod_headers.c>
Header always set X-Frame-Options SAMEORIGIN
</IfModule>

Diese Fehlermeldung ist vielfältig und kann von tatsächlichen fehlenden Benutzerrechten, über fehlende Dateirechte bis hin zu nicht übernommenen Prefixes verursacht werden.
In meinem Fall hatte ich Admin Rechte, konnte mich einloggen, so dass die Admin Leiste sichtbar war, aber ich kam nicht auf dem Dashboard bzw. andere Backend Seiten.

Lösung: Die ID und der Name des Admin musste geändert werden, zu ID 1 und Administrator. Zusätzlich der Eintrag unter wp_capabilities korrigiert werden zu:

a:1:{s:13:"administrator";b:1;}

In WordPress lassen sich die Seiten bereits in einer Hierarchie anlegen, was der Übersichtlichkeit aber auch dem URL Aufbau entgegenkommen kann. Soll die übergeordnete Webseite jedoch nicht mit Inhalt gefüllt werden und direkt auf das Kindelement verweisen, muss ein wenig gecodet werden.

Lösung: Hierzu erstellt man idealerweise in einem Child-Theme, damit bei einem Update des Themes diese Datei nicht überschrieben wird, eine leere php Datei (z.B. blank.php). Abschließend fügt man den nachfolgenden Code ein.

<?php
/*
 * Template Name: Parent Menu
 * Description: Redirects empty parent page to first child page
 */

$child_page = get_pages( "child_of=" . $post->ID . "&sort_column=menu_order" );
if ( $child_page ) {
    $parent_page = $child_page[0];
    wp_redirect( get_permalink( $parent_page->ID ) );
}

Im letzten Schritt muss im Backend auf der gewünschten übergeordneten Seite, das Template geändert werden. Im genannten Fall auf „Parent Menu“.

Wird im WordPress Backend die Widget Funktion nicht angezeigt unterstützt aller Voraussicht nach das Theme diese nicht. Dies bedeutet wiederrum nicht, dass man es dem jeweiligen Theme nicht doch beibringen kann.

Lösung: Hierzu wird folgender Code in die functions.php eingefügt.

function ourWidgetsInit(){
  register_sidebar( array (
    'name' => 'Sidebar',
    'id' => 'sidebar1'
  ));
}
add_action('widgets_init', 'ourWidgetsInit');

Leider gibt es bis heute noch keine simple Möglichkeit den Button Text in den Beiträgen von Avada abzuändern.

Mit dem folgenden Code in der Theme functions.php kann der Text angepasst werden:

add_filter( 'avada_blog_read_more_link', 'my_read_more_link' );
function my_read_more_link( $read_more ) {
  $read_more = "Anderer Text";
  return $read_more;
}

Es gibt zahlreiche Tipps wie man seine WordPress Webseite sicherer machen kann. Regelmäßige Updates, komplizierte Passwörter, Dateirechte richtig einstellen, Sicherungen, sind nur ein kleiner Teil dieser Liste. Nachfolgend eine Liste von Möglichkeiten, die seltener genannt werden.

Automatische CORE-Updates aktivieren

define( 'WP_AUTO_UPDATE_CORE', true );

Dateiänderungen über Backend-Editor verhindern

define('DISALLOW_FILE_EDIT', true);

Benutzernamen verbergen (wird in der functions.php eingefügt)

add_action('template_redirect', 'bwp_template_redirect');
function bwp_template_redirect()
{
  if (is_author())
  {
    wp_redirect( home_url() ); exit;
  }
}

wp-includes Änderungen verhindert (wird in der .htaccess eingefügt)

<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^wp-admin/includes/ - [F,L]
RewriteRule !^wp-includes/ - [S=3]
RewriteRule ^wp-includes/[^/]+\.php$ - [F,L]
RewriteRule ^wp-includes/js/tinymce/langs/.+\.php - [F,L]
RewriteRule ^wp-includes/theme-compat/ - [F,L]
</IfModule>

readme.html und license.txt schützen

<Files readme.html>
order allow,deny
deny from all
</Files>

<Files license.txt>
order allow,deny
deny from all
</Files>

Hinter WP-VCD steckt eine Malware, die sich in verschiedenen WordPress Dateien einnistet. Wird diese nicht vollständig entfernt, kann sich der Code immer wieder neu ausführen.

Verwendetet man sogenannten Nulled Premium Plugins oder Themes, dabei handelt es sich um modifizierte Dateien die im Internet frei zu finden sind und im Gegensatz zur originalen Hersteller Seite kostenlos zur Verfügung stehen. Der Nachteil ist jedoch, dass diese meist auch Schadcode erhalten, der sobald die Dateien auf dem Server aktiviert wurde, große Teile der Webseite beschädigen können.

Lösung: Generell gilt es zu überlegen, ob man Programme aus unbekannten Quellen laden möchte. Ist dies bereits geschehen, sollte man folgende Punkte beachten.

1.) Die gesamte Webseite sichern.

2.) Folgende Dateien löschen:

/wp-includes/wp-vcd.php
/wp-includes/class.wp.php
/wp-includes/wp-cd.php
/wp-includes/wp-feed.php
/wp-includes/wp-tmp.php
/wp-includes/class.theme-modules.php
/wp-includes/class.plugin-modules.php

3.) Den Quellcode in folgender Datei säubern:

/wp-includes/class.wp.php
/wp-content/themes/_verwendetesTheme/functions.php

4.) Den Server nach dem Ursprung durchsuchen, in dem man folgenden Dateien ausfindig macht:

class.theme-modules.php
class.plugin-modules.php

Ein einfachsten findet man die Dateien, in dem man per Shell auf den Webserver zugreift und mit dem nachfolgenden Befehl, alle Dateien auf dem Server danach durchsuchen lässt und anschließend entfernt.

find / -name gesuchteDatei.php

Der Fall dürfte zwar nur sehr selten auftreten, völlig abwegig ist dieser aber nicht: Man kommt nicht mehr ins Backend von WordPress.

Lösung: Sofern noch Zugang zur Datenbank besteht, idealerweise über phpMyAdmin, lässt sich ein neuer Admin Zugang schnell einrichten. Hierzu verwendet man einfach die nachfolgenden SQL Befehle. Zuvor muß noch das Wort ‚databasename‘, durch den tatsächlichen Datenbanknamen abgeändert werden.

INSERT INTO `databasename`.`wp_users` (`ID`, `user_login`, `user_pass`, `user_nicename`, `user_email`, `user_url`, `user_registered`, `user_activation_key`, `user_status`, `display_name`) VALUES ('4', 'demo', MD5('demo'), 'Your Name', 'test@yourdomain.de', 'http://www.test.de/', '2011-06-07 00:00:00', '', '0', 'Your Name');
 
INSERT INTO `databasename`.`wp_usermeta` (`umeta_id`, `user_id`, `meta_key`, `meta_value`) VALUES (NULL, '4', 'wp_capabilities', 'a:1:{s:13:"administrator";s:1:"1";}');
 
INSERT INTO `databasename`.`wp_usermeta` (`umeta_id`, `user_id`, `meta_key`, `meta_value`) VALUES (NULL, '4', 'wp_user_level', '10');

Anschließend kann man sich mit dem Benutzername und Passwort ‚demo‘ einloggen. Diese Zugangsdaten sollten natürlich schnellstmöglich geändert werden!

Die Art des Hacks kann unterschiedlich ausfallen. Es gibt Variante die einem sofort auffallen und andere die im Hintergrund ablaufen, so dass man diese nur selten mitbekommt, sofern man nicht regelmäßig alle Unterseiten einer Webseite prüft.

Wie: Die Vorgehensweise ist oft dieselben, Schwachstellen in veralteten WordPress Version oder ein veraltetes Plugin können dafür sorgen das Hacker auf die Webseite gelangen. Auch veraltete Skripte in gekauften Themes können eine Ursache sein. Oftmals ist ein Hacker auch nicht gezielt auf eine Webseite aufmerksam geworden, sondern nutzt Tools die gängige Sicherheitslücken in Webseiten versucht auszuspionieren und bei einem Treffer automatisch diese Webseite zu infiltrieren. Hier kommt es oft zu Script Injections, also das Einfügen von Skripten das entweder Nutzerdaten ausliest oder Besucher der Webseite zu anderen Webseiten weiterleitet. Im schlimmsten Fall wird Malware auf der Webseite hinterlegt, die der Besucher versehentlich herunterlädt.

Generell: Wurde eine Webseite gehackt, sollte im Idealfall eine Sicherung eingespielt werden und anschließend das System aktualisiert werden. Das Ändern alles Zugangsdaten (Backend, Datenbank, etc.) ist ebenfalls wichtig.

Suche: Möchte man sich auf die Suche nach dem Problem machen, ist eine allgemeine Anleitung nicht möglich, da diese Skripte bzw. Schadcode überall hinterlegt sein kann. In einer vorhandenen Datei, in einer neuen Datei oder direkt in der Datenbank. Hilfreich ist es, wenn man den problematischen Code z.B. im Quellcode gefunden hat, so dass man schon einmal weiß wonach man suchen muss. Eine Möglichkeit ist auf dem Server nach Dateien zu suchen, deren Änderungsdatum mit dem Hackdatum übereinstimmen, sofern man den genauen Zeitraum des Hacks halbwegs sicher bestimmen kann.

Hilfreich bei der Suche kann https://sitecheck.sucuri.net/ sein, zusätzlich gibt es hierzu auch noch ein WordPress Plugin. Ebenfalls gute Erfahrungen konnte ich mit dem Quttera Web Malware Scanner https://wordpress.org/plugins/quttera-web-malware-scanner/ machen. Oftmals können Sie die problematische Datei bzw. Dateien bestimmen oder zumindest einen ungefähren Hinweis dazu preisgeben.

Um den nervigen und zugleich überflüssigen Kommentar aus dem Quelltext zu entfernen, fügt man einfach den folgenden Code in die functions.php ein:

if (defined('WPSEO_VERSION')) {
 add_action('wp_head',function() { ob_start(function($o) {
 return preg_replace('/^\n?\n?$/mi','',$o);
 }); },~PHP_INT_MAX);
}

Leider kommt es vor, dass beim Aufruf der von Yoast generierten XMP Sitemap diese endlos lädt, aber nie dargestellt wird.

Lösung: Klickt man unter Einstellungen -> Permalinks einmal auf Speichern, lässt sich das Problem damit oftmals beheben.

Die sogenannte Acceptance Box in Contact Form 7, dient als Checkbox innerhalb des Formulars. Von Haus aus wird diese beim nicht anklicken, trotz Pflichtfeld, mit einer Fehlermeldung bedacht.

Lösung: Mit der nachfolgenden Anweisung, die innerhalb des Tabs Additional Settings eingefügt wird, folgt die Behebung dieses Problems.

acceptance_as_validation: on

Sofern die empfangenen E-Mails, die über Contact Form 7 versendet werden, mit hoher Priorität angezeigt werden sollen, lässt sich unter „Additional Headers“ folgender Eintrag vornehmen:

X-Priority: 1
X-MSMail-Priority: High

Kleine Einstellung, große Wirkung. Wird das „Von“ Feld in Contact Form 7 nicht korrekt ausgefüllt, steht WordPress als Sender in der E-Mail. Passt man dies wie nachfolgend erklärt an, gehört dieses Problem der Vergangenheit an.

Sendername <senderemail@test.de>

Bisher fehlt immer noch eine Plugin eigene Lösung, um das Formular zweispaltig umzubauen. Die folgenden Anpassungen ermöglichen eine händische Lösung.

Anpassungen im Formular selbst:

<div class="wps-form">
    <!-- Zeile 1 -->
    <div class="wps-form-row">
        <!-- Spalte 1 -->
        <div class="wps-form-column">
            [text* your-name placeholder "Name*"]
        </div>
        <!-- Spalte 2 -->
        <div class="wps-form-column">
            [email* your-email placeholder "E-Mail*"]
        </div>
    </div>
    <!-- Ende der Zeile 1 -->
</div>

Ergänzungen für die CSS Datei:

.wps-form {
    width: 100%;
    margin: 0 auto;
}
.wps-form-row {
    display: flex;
    flex-direction: column;
    width: 100%;
}
.wps-form-row .wpcf7-form-control {
    width: 100%;
}
.wps-form-column {
    flex: 1;
    padding: 0.5rem 0;
    width: 100%;
}
@media only screen and ( min-width: 48em ) { 
    .wps-form-row {
        flex-direction: row;
    }
    .wps-form-column {
        padding: 0.5rem 1rem;
    }
}

Wenn Contact Form 7 beim Senden einer Nachricht eine Fehlermeldung ausgibt, dass die E-Mail nicht verschickt werden konnte, liegt es eventuell an einem Fehler mit Google reCAPTCHA ver 3. In Firefox lässt sich mit der Konsole eventuell dann auch ein API Fehler feststellen.

Lösung: Im Allgemeinen liegt es an einem falsch hinterlegten Domain Namen. Unter google.com/recaptcha muß die Einstellung der Domain z.B. cms-geek.de lauten. Wenn man hingegen ein www oder sogar ein https:// voranstellt, wird ein Fehler ausgelöst, der sich dann zeigt, wenn versucht wird das Kontaktformular abzusenden.

Leider bietet das Plugin von Haus aus keine eigene Länderliste an, außer man erstellt sich diese von Hand, was aufwendig und zudem viel Ressourcen beim Laden frisst.

Lösung: Zuerst wird das Plugin Listo benötigt, was trotz Meldung auch mit der aktuellen WordPress Version läuft. Ist dieses installiert und aktiviert, lässt sich ein entsprechendes Feld in Contact Form 7 erstellen.

[select land data:countries default:60]

Der Wert default:60 steht für Deutschland.

Der genannte Fehler tritt immer dann auf, wenn die VON Adresse, nicht zur Webseiten Domain passt. Dies ist oftmals dann der Fall, wenn man in dem VON Feld den Inhalt des [your-email] Felds hinterlegt.

Lösung: Hier sollte ebenfalls die Adresse hinterlegt werden, die auch im AN Feld angegeben wird. Damit man beim Antworten auf diese E-Mail jedoch bereits den richtigen Empfänger hat und nicht sich selbst antwortet, kann im Additional Headers folgendes hinterlegt werden: „Reply-To: [your-email]“.

Das Plugin Ninja Forms mit der Erweiterung Multi Part Forms lässt sich so erweitern, dass der „Nächste Schritt“ Button teilweise überflüssig wird. Befindet sich im Formular ein Feld das mit einem normalen Klick aktiviert wird, lässt sich dies mit dem nachfolgenden Code realisieren. Bei Mehrfachauswahl ist dies auch möglich, ohne dass hier das Skript bereits beim ersten Klick auf den nächsten Schritt springt.

$(document).ready(function () { 
         $(document).on('click', '.clicky', function(event) {
          event.preventDefault;
          $(".nf-next").click();
      function greet(){
        $(".nf-next").click();
      }
      setTimeout(greet, 500);  
});
jQuery(document).on( 'nfFormReady', function( e, layoutView ) {
    // Your code goes here...
});

Dieser Fehler tritt gern bei älteren Versionen des Plugins auf, wenn hingegen auf eine aktuelle PHP Version gesetzt wird. Beides verträgt sich nicht und sorgt unter Umständen für eine komplett weiße Webseite.

Lösung: Im Plugin Ordner sucht bearbeitet man folgende Datei revslider/includes/framework/base-admin.class.php

Man sucht nun die folgende Codezeile:

private static $arrMetaBoxes = '';

und ersetzt diese durch:

private static $arrMetaBoxes = array();

Anschließend sollte man den Webseiten Cache und Browser Cache einmal löschen.

Folgendes Script scrollt automatisch nach dem letzten Slide unterhalb des Sliders zum nachfolgenden Content. Die ID des Sliders (hier: revapi1) muss entsprechend angepasst werden.

Lösung:

// Scroll-Geschwindigkeit in ms
var scrollTime = 500;
 
// Zeit bevor das Scrollen beginnt in ms
var scrollDelay = 2000;
 
var api = revapi1.on('revolution.slide.onchange', function(e, data) {
 
    if(data.slideIndex === api.revmaxslide()) {
 
        setTimeout(function() {
 
            var bounds = api[0].getBoundingClientRect();
            jQuery('html, body').animate({
                scrollTop: window.pageYOffset + bounds.top + bounds.height
            }, scrollTime);
 
        }, scrollDelay);
    }
});

Wenn man auf Slides von außerhalb des Plugins zugreifen möchte, z.B. das über einen bestimmten Link / Button ein bestimmter Slide angezeigt werden soll, kann sich wie folgt behelfen:

1. Zuerst erstellt man einen entsprechenden Link, samt Klasse.

<a href="#" class="slider-link">Show Slide Number Two</a>

2. Folgender Code wird in der Custom JavaScript section des Sliders selbst hinterlegt.

jQuery('body').on('click', '.slider-link', function() {
  // revapi5 equals the API identified for the slider
  // revshowslide(2) will load the second slide
  revapi5.revshowslide(2);
  return false;
});

In einigen Fällen kann es recht sinnvoll sein, dass sich ein Element außerhalb des Sliders, je nach angezeigten Slide anders verhält. In dem nachfolgenden Fall geht es darum, das Element farblich anzupassen.

Im gewünschten Slide fügt man unter Link & SEO, im Feld Custom Fields den folgenden Wert ein (in der neueren Version unter Tags & Link , im Feld HTML Data):

data-color="light"

Anschließend folgt ein kurzer CSS Code, bei dem sr- dem ganzen vorangestellt wird.

body.sr-light {background-color: #fff;}

Zu guter Letzt muss dann noch folgender JavaScript Code hinterlegt werden:

var api = revapi1.on('revolution.slide.onchange', function(e, data) {
 
  jQuery('body').removeClass('sr-light').addClass(
    'sr-' + api.find('rs-slide').eq(data.slideIndex - 1).attr('data-color')
  );
});

revapi1 muss hierbei, durch den API Namen des verwendeten Sliders ersetzt werden.

Durch diese Anpassungen wird im body eine Klasse mit dem Namen sr-light sichtbar, wenn der Slide angezeigt wird.

Zu guter Letzt muss jetzt das gewünschte Element angepasst werden, was z.B. mit dem nachfolgenden Code möglich ist:

body.sr-light .entry-content {color: #fff;}

Die Lösung funktioniert beim Theme Avada, für andere Themes muß hier eventuell die Klasse geändert werden.

function add_slider_rev_to_post() {
  if ( is_single() && 'post' == get_post_type() ) {
    print do_shortcode('[rev_slider alias="aktuelles"][/rev_slider]');
  }
}
add_filter( 'avada_after_header_wrapper', 'add_slider_rev_to_post' );

Wurde ein Domainumzug durchgeführt, sind meist die eingefügten Bilder im Slider nicht mehr zu erkennen, da diese noch falsch verlinken. In den neuen Versionen des Plugins ist eine kostenpflichtige Erweiterung nötig um die Anpassungen automatisch vornehmen zu können.

Lösung: Hat man Zugriff auf die Datenbank können in der Tabelle „wp_revslider_slides“ die entsprechenden Links händisch angepasst werden.

Im Zusammenhang mit einer PHP Umstellung zu 7.1 oder 7.2 kann es vorkommen, dass der All-in-One Event Calendar mit folgender Fehlermeldung den Dienst quittiert: CSS-Kompilierung schlug fehl, da nicht genügend freier Speicher verfügbar war (mindestens 24M wird benötigt). Dein Kalender wird ohne CSS nicht angezeigt werden oder richtig funktionieren.

Lösung: Zum einen sollte geprüft werden, ob eventuell wirklich ein Problem mit dem Memory Limit des Servers besteht. Ist dies der Fall, kann mit einer php.ini Datei, die Erhöhung des Limits erzeugt werden (mehr dazu unter Server & Hoster).
Ist dieses Problem behoben, sollte man im Plugin selbst unter „Veranstaltungen“ > „Theme-Optionen“, auf „Optionen speichern“ klicken, den Webseiten Cache (falls vorhanden) und den Browser Cache leeren.

Das Backup Plugin Duplicator erzeugt seit der neueren Version ein DUP Archiv. Mit herkömmlichen Archiv Tools lässt sich dieses leider nicht entpacken.

Der eigenen Dup Archive Extractor von Snapcreek löst dieses Problem und kann hier heruntergeladen werden.

Einen Sticky Header in Elementor umzusetzen geht relativ einfach.

1. Das komplette Header Element über „Bewegeungseffekte“ auf Sticky „Oben“ setzen und als Offset Wert 10 einstellen.
2. Damit sich das Logo mittverkleinert, muss dieses die Klasse „logo“ zugewiesen bekommen.
3. Das Header Element bekommt einen „Eigenes CSS“ Code:

/* Hintergrundfarbe wird weiß wenn scroll */
.elementor-sticky--effects {
      background-color:rgba(255,255,255,1);
      transition: all 0.5s ease;
}
/* Menü Farbe ändern on scroll */
.elementor-sticky--effects .elementor-nav-menu a{
      color: #333!important;
      transition: all 0.5s ease;
}
/* Logo größe normal */
.logo img {
      width: auto;
      max-height: 120px;
      transition: all 0.5s ease;
}
/* Logo größe on scroll */
.elementor-sticky--effects .logo img {
      max-height: 60px;
      width: auto;
}

Wahlweise lässt sich das Menü selbst ebenfalls noch etwas anpassen, damit die komplette Header Höhe reduziert wird.

Aktuell fehlt es Elementor noch an einer Funktion, dass man mehreren Boxen nebeneinander dieselbe Höhe geben kann bzw. das sich an der höchsten Box orientiert wird.

Lösung: Mit etwas CSS kann man dieses Problem jedoch beheben. Zuerst muss innerhalb einer Section eine weitere Section erstellt werden. Diese kann dann z.B. drei oder vier Spalten besitzen. in jeder Spalte positioniert man dann den gewünschten Content. Der inneren Section muss dann die CSS Klasse zugewiesen werden.

.equal-height-content {
    height: 100%;
    display: flex;
}

Möchte man das automatische nachladen der Google Fonts verhindern, da man diese z.B. lokal auf dem Server gespeichert hat, sind folgende Schritte notwendig:
1.) Mit dem Plugin OMGF | Host Google Fonts Locally lässt sich die bestehende Webseite nach entsprechenden Google Fonts durchsuchen, herunterladen und lokal speichern.

2.) Unter Elementor -> Einstellungen -> Standardschriftarten deaktivieren anklicken. Anschließend unter Elementor -> Werkzeuge -> CSS erneuern anklicken.

3.) Werden anschließend noch immer Google Schriften geladen, lässt sich folgendes Script in der functions.php Datei hinterlegen:

add_filter( 'elementor/frontend/print_google_fonts', '__return_false' );

4.) Setzt man parallel auch das Plugin Slider Revolution ein, gibt es hier unter Einstellungen ebenfalls noch eine Möglichkeit, dass keine Schriften mehr vom Google Server geladen werden.

Die Popup Funktion von Elementor scheint noch einige kleinere Schwierigkeiten zu haben, so dass man sich hier und da noch etwas behelfen muss. Möchte man gerne einen einfachen Audio Player im Popup integrieren, funktioniert der WordPress eigene Embed Code nicht richtig. Alternativ lässt sich dies mit dem nachfolgenden Skript lösen.

<audio controls autoplay>
<source src="datei.mp3" type="audio/mpeg">
Ihr Browser unterstützt den Audio Player leider nicht.
</audio>

Bei mehrzeiligen Texten in der Elementor Icon List wird das Icon immer mittig vor dem Text zentriert und leider nicht oben. Der nachfolgende Code ändert dies.

.elementor-icon-list-item {
  align-items: baseline !important;
}	
.elementor-icon-list-item .elementor-icon-list-icon {
  margin-top:6px;
}

Möchte man von einer Unterseite auf eine andere Unterseite die mit Tabs ausgestattet ist, einen bestimmten Tab öffnen lassen, lässt sich dies mit dem nachfolgenden Skript realisieren.

Lösung Tab:

<script>
document.addEventListener('DOMContentLoaded', function() {
setTimeout(function() {

jQuery(function($){

let desktoptitles = $('.elementor-tab-desktop-title');
let mobiletitles = $('.elementor-tab-mobile-title');

let strings = ['?tab1','?tab2','?tab3'];

strings.forEach( (string,i) => {
if (window.location.href.indexOf(string) > -1) {
desktoptitles.eq(i).click();
mobiletitles.eq(i).click();
$('html, body').animate({
scrollTop: desktoptitles.eq(i).offset().top - 100
},'slow');
} } );
}); }, 1200); });
</script>

Lösung Akkordeon:

<script>
document.addEventListener('DOMContentLoaded', function() {
setTimeout(function() {

jQuery(function($){
let accordiontitles = $('.elementor-accordion-item .elementor-tab-title');

let strings = ['?akkordeon1','?akkordeon2','?akkordeon3'];

strings.forEach( (string,i) => {
if (window.location.href.indexOf(string) > -1) {
accordiontitles.eq(i).click();
$('html, body').animate({
scrollTop: accordiontitles.eq(i).offset().top - 100
},'slow');
} } );
}); }, 1200); });
</script>

Lösung Toggle:

<script>
document.addEventListener('DOMContentLoaded', function() {
setTimeout(function() {

jQuery(function($){
let toggletitles = $('.elementor-toggle-item .elementor-tab-title');

let strings = ['?toggle1','?toggle2','?toggle3'];

strings.forEach( (string,i) => {
if (window.location.href.indexOf(string) > -1) {
toggletitles.eq(i).click();
$('html, body').animate({
scrollTop: toggletitles.eq(i).offset().top - 100
},'slow');
} } );
}); }, 1200); });
</script>

Fehlt beim automatischen Scrollen zum Ankerpunkt ein nötiger Offset Wert, lässt sich dieser mit dem nachfolgenden Code in der functions.php Datei hinzufügen.

add_action( 'wp_footer', function() {
  if ( ! defined( 'ELEMENTOR_VERSION' ) ) {
    return;
  }
  ?>
  <script>
    jQuery( function( $ ) {
      // Add space for Elementor Menu Anchor link
      $( window ).on( 'elementor/frontend/init', function() {
        elementorFrontend.hooks.addFilter( 'frontend/handlers/menu_anchor/scroll_top_distance', function( scrollTop ) {
          return scrollTop - 30;
        } );
      } );
    } );
  </script>
  <?php
} );

Bis zum jetzigen Zeitpunkt bietet Elementor einen Filter nur für das Portfolio, nicht aber für das Blog Widget an.

Lösung: Auf Github findet sich das Elementor Super Cat Plugin, das u.a. genau dafür entwickelt wurde.

In Elementor lässt sich zwar die Länge des Textauszuges festlegen, aber nicht wie dieses Ende sollen. z.B. mit …

Lösung: Mit dem folgenden Code in der functions.php lässt sich dies beliebig anpassen.

function custom_post_excerpt_length($post_excerpt, $post)
{
//Maximale Buschstaben
$length = 70;
//Endet mit ...
$excerpt = strlen($post_excerpt) > $length ? mb_substr($post_excerpt, 0, $length - 3, "utf-8") . "..." : $post_excerpt;
return $excerpt;
}
add_filter('get_the_excerpt', 'custom_post_excerpt_length', 20, 2);

Dies funktioniert mit folgenden Code in der functions.php Datei.

function close_elementor_accordion() { ?>
   <script>
    jQuery(document).ready(function($) { 
    var delay = 100; setTimeout(function() { 
    $('.elementor-tab-title').removeClass('elementor-active');
      $('.elementor-tab-content').css('display', 'none'); }, delay); 
    });
  </script>
<?php }
add_action('wp_footer', 'close_elementor_accordion');

Mit relativ einfachem Mittel lässt sich ein beliebiger Button erstellen, der beim Klick ein Elementor Popup öffnet und nach erneutem Klick diesen Popup auch wieder schließt. Nachfolgend ist nur die Grundstruktur hinterlegt.

Einstellungen im Popup unter „Open By Selector“:

a[href="#bmenue"]

Skript zum „Schließen“ des Popups:

jQuery("#burger-menue").click(function(){
   if (jQuery("#burger-menue").hasClass("open")) {
       jQuery("#burger-menue").removeClass("open");
       jQuery(".dialog-close-button").click();
   }
   else {
      jQuery("#burger-menue").addClass("open");
   }
});

Leider gibt es bisher keine integrierte Funktion in Elementor die es ermöglich, das Spalten anklickbar und mit einem Link hinterlegt werden. Neben der Plugin Lösung gibt es auch einen manuellen Weg.

Lösungsschritte:
1.) Man erstellt einen Button oder Link innerhalb dieser Spalte und fügt ein Linkziel ein.
2.) Anschließend weißt man diesem Element oder Link eine Klasse zu, z.B. „clickable“.
3.) Zum Schluss wird folgender CSS Code hinterlegt:

.elementor-column.clickable a:after {
   content: "";
   display: block; 
   position: absolute;
   top: 0;
   left: 0;
   right: 0;
   bottom: 0;
   z-index: 2; 
}
.clickable .elementor-widget, .clickable .elementor-widget-wrap {
   position: static;
}

Das Caching Plugin WP Rocket erzeugt einen automatischen Eintrag am Ende des Webseiten Quelltextes. Dieser lässt sich jedoch auch mit wenig Aufwand entfernen.

Lösung: Hierzu wird einfach in der wp-config.php Datei, der nachfolgende Eintrag hinzugefügt.

define('WP_ROCKET_WHITE_LABEL_FOOTPRINT', true);

Die meisten Caching Plugins bieten die Funktion zum Zusammenfassen von CSS und JavaScript Dateien, um damit eine Reduzierung der Dateien zu erreichen und somit die Ladegeschwindigkeit zu reduzieren. Vor allem bei der Zusammenfassung von JavaScript Dateien kommt es (je nach Menge der verwendeten JavaScript Dateien) oftmals zu Fehlern in der Darstellung.

Lösung: Ein guter Weg ist es, alle verwendeten JavaScript Dateien zur Ausnahme hinzuzufügen und im zweiten Schritt, diese einzeln wieder aus der Ausnahme zu nehmen, um festzustellen welche Datei bzw. Dateien eventuell zu den Fehlern führen. Um eine einfache Zusammenfassung alle JavaScript Dateien zu bekommen, lässt sich über das kostenlose Crawler Programm Xenu’s Link Sleuth, eine Liste exportieren.

<?php
add_filter('body_class', 'append_language_class');
function append_language_class($classes){
  $classes[] = ICL_LANGUAGE_CODE;
  return $classes;
}
?>

Als WordPress Plugin Entwickler erhält man Zugang zum Repository, von wo aus das Plugin in das WordPress Verzeichnis ausgegeben wird. Im ersten Moment stellt sich jedoch die Frage, wie man seine Datei in das Repository übertragen kann.

Lösung: Das kostenlose TortoiseSVN ermöglicht genau dieses. Gibt man hier seine Repository URL ein, erhält man Zugriff zu seinem Verzeichnis. Erstmalig müssen auch die WordPress.com Zugangsdaten hinterlegt werden.

Nicht alle Skripte und CSS-Dateien die Themes und Plugins verwenden, werden auch benötigt. Hier gibt es also Optimierungs-Potenzial die vor allem beim Laden der Webseite von Vorteil ist.

1. Im ersten Schritt muss die ID herausgefunden werden. Die geht am besten mit dem Inspektor.

<link rel='stylesheet' id='default_styles-css' href='https://example.com/wp/wp-content/themes/example/style.css' type='text/css' media='all' />

2. Wird die ID nicht angezeigt, kann man sich mit dem folgenden Skript in der functions.php behelfen und anschließend die ID mit dem Inspektor im Head Bereich auslesen.

/*
  Get Script and Style IDs
  Adds inline comment to your frontend pages
  View source code near the <head> section
  Lists only properly registered scripts
  @ https://digwp.com/2018/08/disable-script-style-added-plugins/
*/
function shapeSpace_inspect_script_style() {
  
  global $wp_scripts, $wp_styles;
  
  echo "\n" .'<!--'. "\n\n";
  
  echo 'SCRIPT IDs:'. "\n";
  
  foreach($wp_scripts->queue as $handle) echo $handle . "\n";
  
  echo "\n" .'STYLE IDs:'. "\n";
  
  foreach($wp_styles->queue as $handle) echo $handle . "\n";
  
  echo "\n" .'-->'. "\n\n";
  
}
add_action('wp_print_scripts', 'shapeSpace_inspect_script_style');

3. Sind die IDs der gewünschten Elemente bekannt, kommt der eigentliche Teil in dem diese entfernt werden.

function disable_scripts_styles() {
  if (!is_page('checkout') && !is_page('purchase-confirmation') && !is_page('purchase-history') && !is_page('transaction-failed')) {
    wp_dequeue_script('beispiel01');
    wp_dequeue_style('beispiel02');
  }
  
  wp_dequeue_style('beispiel03');
}
add_action('wp_enqueue_scripts', 'disable_scripts_styles', 100);

Woocommerce

Standartmäßig wird die Produktübersichtsseite von Woocommerce im Meta Title als „Produkte Archiv“ dargestellt.

Lösung: Wär das SEO Plugin Yoast im Einsatz hat, kann dort unter der Rubrik „Titel & Metas“, die Darstellung von Archive von Artikeltypen entsprechend anpassen.

Die Bezeichnung der Versandmethode wird im Warenkorb, sowie Checkout und in den E-Mails vor den Versandkosten dargestellt. Sofern es nur eine Versandmethode gibt, ist diese vorgestellte Bezeichnung eventuell überflüssig.

Lösung: Mit dem folgen Code Snippet, der in der function.php des Themes eingefügt werden muss, wird diese Bezeichnung entfernt.

add_filter( 'woocommerce_cart_shipping_method_full_label', 'bbloomer_remove_shipping_label', 10, 2 );

function bbloomer_remove_shipping_label($label, $method) {
$new_label = preg_replace( '/^.+:/', '', $label );
return $new_label;
}

Nicht jedes Theme zeigt in der Produktübersicht möglicherweise einen „Produktdetails“-Button an, der aus Benutzersicht dennoch sehr hilfreich sein kann, da nicht jedem bewusst ist, dass z.B. der Produkttitel die entsprechende Detailseite nach dem anklicken anzeigt.

Der nachfolgende Code in der functions.php löst dieses Problem:

add_action('woocommerce_after_shop_loop_item', 'add_a_custom_button', 5 );
function add_a_custom_button() {
    global $product;

    if( $product->is_type('variable') || $product->is_type('grouped') ) return;

    echo '<div style="margin-bottom:10px;">
        <a class="button custom-button" href="' . esc_attr( $product->get_permalink() ) . '">' . __('Produktdetails') . '</a>
    </div>';
}

Im Folgenden wird das Buttonziel des „Zurück zum Shop“ Buttons so geändert, das dieses zur letzte Seite zurückgeführt wird. Dafür muss folgende Änderung in der cart-empty.php vorgenommen werden.

if ( wc_get_page_id( 'shop' ) > 0 ) : ?>
  <p class="return-to-shop">
    <a href="#" onclick="window.history.go(-1); return false;">
      <?php _e( 'Zurück', 'woocommerce' ) ?>
    </a>
  </p>
<?php endif; ?>

 

Möchte ein Kunden im Warenkorb die Mengenanzahl noch einmal korrigieren, muss die Änderung anschließend durch den Button „Warenkorb aktualisieren“ übernommen werden.

Das kostenlose Plugin WooCommerce AJAX Cart bietet hier eine automatische Lösung, so das ein manuelle bestätigen des Buttons vermieden wird.

Leider fehlen bis heute standartmäßig in Woocommerce noch einige Funktionen, die bei anderen Shopsystemen als Standard dazugehören. Durch die Skalierbarkeit des Systems, lassen sich jedoch Wege finden, diese Änderungen nachträglich vorzunehmen.

Der nachfolgende Code sorgt für mehrere Dinge gleichzeitig. Zum einen lässt sich damit auf der Mein Konto Loginseite, sowie auf der Bestellübersichtsseite, eine Registrierung für den Kunden umsetzen. Zudem werden innerhalb der Registrierung noch zwei Extrafelder erzeugt, die zum einen auch für den Admin im Backend sichtbar und auch bei jeder Bestellemail dem Admin mit angezeigt werden. Der Code muss in der function.php des verwendeten Themes, am besten am Ende der Datei, angefügt werden.

  
// Extrafelder definieren
function woo_get_account_fields() {
      return apply_filters( 'woo_account_fields', array(
        'user_extrafeld1' => array(
          'type'        => 'text',
          'label'       => __( 'Extrafeld1', 'woocommerce' ),
          'placeholder' => __( '', 'woocommerce' ),
          'required'    => true,
          'sanitize'    => 'wc_clean',
          'description'       => 'Beschreibung Beispieltext',
          'hide_in_account'      => false,
          'hide_in_admin'        => false,
          'hide_in_checkout'     => false,
          'hide_in_registration' => false,
        ),
      
        'user_extrafeld2' => array(
          'type'        => 'text',
          'label'       => __( 'Extrafeld2', 'woocommerce' ),
          'placeholder' => __( '', 'woocommerce' ),
          'required'    => true,
          'sanitize' => 'wc_clean',
          'description'       => 'Beschreibung Beispieltext',
          'hide_in_account'      => false,
          'hide_in_admin'        => false,
          'hide_in_checkout'     => false,
          'hide_in_registration' => false,
        ),
      ) );
  }


  function woo_print_user_frontend_fields() {
      $fields            = woo_get_account_fields();
      $is_user_logged_in = is_user_logged_in();
   
      foreach ( $fields as $key => $field_args ) {
          $value = null;
   
          if ( $is_user_logged_in && ! empty( $field_args['hide_in_account'] ) ) {
              continue;
          }
   
          if ( ! $is_user_logged_in && ! empty( $field_args['hide_in_registration'] ) ) {
              continue;
          }
   
          if ( $is_user_logged_in ) {
              $user_id = woo_get_edit_user_id();
              $value   = woo_get_userdata( $user_id, $key );
          }
   
          $value = isset( $field_args['value'] ) ? $field_args['value'] : $value;
   
          woocommerce_form_field( $key, $field_args, $value );
      }
  }
  add_action( 'woocommerce_register_form', 'woo_print_user_frontend_fields', 10 );
  add_action( 'woocommerce_edit_account_form', 'woo_print_user_frontend_fields', 10 );


  function woo_checkout_fields( $checkout_fields ) {
      $fields = woo_get_account_fields();
   
      foreach ( $fields as $key => $field_args ) {
          if ( ! empty( $field_args['hide_in_checkout'] ) ) {
              continue;
          }
   
          $checkout_fields['account'][ $key ] = $field_args;
      }
   
      return $checkout_fields;
  }
  add_filter( 'woocommerce_checkout_fields', 'woo_checkout_fields', 10, 1 );


  function woo_print_user_admin_fields() {
      $fields = woo_get_account_fields();
      ?>
        <table class="form-table" id="woo-additional-information">
          <tbody>
          <?php foreach ( $fields as $key => $field_args ) { ?>
              <?php
              if ( ! empty( $field_args['hide_in_admin'] ) ) {
                  continue;
              }
   
              $user_id = woo_get_edit_user_id();
              $value   = woo_get_userdata( $user_id, $key );
              ?>
              <tr>
                  <th>
                      <label for="<?php echo $key; ?>"><?php echo $field_args['label']; ?></label>
                  </th>
                  <td>
                      <?php $field_args['label'] = false; ?>
                      <?php woocommerce_form_field( $key, $field_args, $value ); ?>
                  </td>
              </tr>
          <?php } ?>
          </tbody>
      </table>
      <?php
  }
  add_action( 'show_user_profile', 'woo_print_user_admin_fields', 30 );
  add_action( 'edit_user_profile', 'woo_print_user_admin_fields', 30 );


  function woo_get_edit_user_id() {
      return isset( $_GET['user_id'] ) ? (int) $_GET['user_id'] : get_current_user_id();
  }


  function woo_save_account_fields( $customer_id ) {
      $fields = woo_get_account_fields();
      $sanitized_data = array();
   
      foreach ( $fields as $key => $field_args ) {
          if ( ! woo_is_field_visible( $field_args ) ) {
              continue;
          }
   
          $sanitize = isset( $field_args['sanitize'] ) ? $field_args['sanitize'] : 'wc_clean';
          $value    = isset( $_POST[ $key ] ) ? call_user_func( $sanitize, $_POST[ $key ] ) : '';
   
          update_user_meta( $customer_id, $key, $value );
      }
   
      if ( ! empty( $sanitized_data ) ) {
          $sanitized_data['ID'] = $customer_id;
          wp_update_user( $sanitized_data );
      }
  }
  add_action( 'woocommerce_created_customer', 'woo_save_account_fields' );
  add_action( 'personal_options_update', 'woo_save_account_fields' );
  add_action( 'edit_user_profile_update', 'woo_save_account_fields' );
  add_action( 'woocommerce_save_account_details', 'woo_save_account_fields' );


  function woo_is_field_visible( $field_args ) {
      $visible = true;
      $action = filter_input( INPUT_POST, 'action' );
   
      if ( is_admin() && ! empty( $field_args['hide_in_admin'] ) ) {
          $visible = false;
      } elseif ( ( is_account_page() || $action === 'save_account_details' ) && is_user_logged_in() && ! empty( $field_args['hide_in_account'] ) ) {
          $visible = false;
      } elseif ( ( is_account_page() || $action === 'save_account_details' ) && ! is_user_logged_in() && ! empty( $field_args['hide_in_registration'] ) ) {
          $visible = false;
      } elseif ( is_checkout() && ! empty( $field_args['hide_in_checkout'] ) ) {
          $visible = false;
      }
   
      return $visible;
  }


  function woo_is_userdata( $key ) {
      $userdata = array(
          'user_pass',
          'user_login',
          'user_nicename',
          'user_email',
          'display_name',
          'nickname',
          'first_name',
          'last_name',
          'description',
          'rich_editing',
          'user_registered',
          'role',
          'jabber',
          'aim',
          'yim',
          'show_admin_bar_front',
    'user_extrafeld1',
    'user_extrafeld2',
      );
   
      return in_array( $key, $userdata );
  }


  function woo_validate_user_frontend_fields( $errors ) {
      $fields = woo_get_account_fields();
   
      foreach ( $fields as $key => $field_args ) {
          if ( empty( $field_args['required'] ) ) {
              continue;
          }
   
          if ( ! isset( $_POST['register'] ) && ! empty( $field_args['hide_in_account'] ) ) {
              continue;
          }
   
          if ( isset( $_POST['register'] ) && ! empty( $field_args['hide_in_registration'] ) ) {
              continue;
          }
   
          if ( empty( $_POST[ $key ] ) ) {
              $message = sprintf( __( '%s muss angegeben werden.', 'woo' ), '<strong>' . $field_args['label'] . '</strong>' );
              $errors->add( $key, $message );
          }
      }
   
      return $errors;
  }
  add_filter( 'woocommerce_registration_errors', 'woo_validate_user_frontend_fields', 10 );
  add_filter( 'woocommerce_save_account_details_errors', 'woo_validate_user_frontend_fields', 10 );


  function woo_add_post_data_to_account_fields( $fields ) {
      if ( empty( $_POST ) ) {
          return $fields;
      }
   
      foreach ( $fields as $key => $field_args ) {
          if ( empty( $_POST[ $key ] ) ) {
              $fields[ $key ]['value'] = '';
              continue;
          }
   
          $fields[ $key ]['value'] = $_POST[ $key ];
      }
   
      return $fields;
  }
  add_filter( 'woo_account_fields', 'woo_add_post_data_to_account_fields', 10, 1 );


  function woo_get_userdata( $user_id, $key ) {
      if ( ! woo_is_userdata( $key ) ) {
          return get_user_meta( $user_id, $key );
      }
   
      $userdata = get_userdata( $user_id );
   
      if ( ! $userdata || ! isset( $userdata->{$key} ) ) {
          return '';
      }
   
      return $userdata->{$key};
  }
  add_filter( 'default_checkout_billing_country', 'change_default_checkout_country' );
  add_filter( 'default_checkout_billing_state', 'change_default_checkout_state' );


  function change_default_checkout_country() {
    return 'DE';
  }
  function change_default_checkout_state() {
    return 'DE';
  }


  add_action( 'woocommerce_register_form_start', 'woo_add_name_woo_account_registration' );
  function woo_add_name_woo_account_registration() {
      ?>
   
      <p class="form-row form-row-first">
      <label for="reg_billing_first_name"><?php _e( 'First name', 'woocommerce' ); ?> <span class="required">*</span></label>
      <input type="text" class="input-text" name="billing_first_name" id="reg_billing_first_name" value="<?php if ( ! empty( $_POST['billing_first_name'] ) ) esc_attr_e( $_POST['billing_first_name'] ); ?>" />
      </p>
   
      <p class="form-row form-row-last">
      <label for="reg_billing_last_name"><?php _e( 'Last name', 'woocommerce' ); ?> <span class="required">*</span></label>
      <input type="text" class="input-text" name="billing_last_name" id="reg_billing_last_name" value="<?php if ( ! empty( $_POST['billing_last_name'] ) ) esc_attr_e( $_POST['billing_last_name'] ); ?>" />
      </p>
    <?php
      $countries_obj   = new WC_Countries();
      $countries   = $countries_obj->get_allowed_countries();
      woocommerce_form_field('billing_country', array(
      'type'       => 'select',
      'class'      => array( 'chzn-drop' ),
      'label'      => __('Land'),
      'placeholder'    => __(''),
      'options'    => $countries,
      'required' => 1
      )
    );?>
    
    <p class="form-row">
      <label for="reg_billing_address_1"><?php _e( 'Straße & Hausnummer', 'woocommerce' ); ?> <span class="required">*</span></label>
      <input type="text" class="input-text" name="billing_address_1" id="reg_billing_address_1" value="<?php if ( ! empty( $_POST['billing_address_1'] ) ) esc_attr_e( $_POST['billing_address_1'] ); ?>" />
      </p>
    
    <p class="form-row">
      <label for="reg_billing_postcode"><?php _e( 'Postleitzahl', 'woocommerce' ); ?> <span class="required">*</span></label>
      <input type="text" class="input-text" name="billing_postcode" id="reg_billing_postcode" value="<?php if ( ! empty( $_POST['billing_postcode'] ) ) esc_attr_e( $_POST['billing_postcode'] ); ?>" />
      </p>
    
    <p class="form-row">
      <label for="reg_billing_city"><?php _e( 'Ort / Stadt', 'woocommerce' ); ?> <span class="required">*</span></label>
      <input type="text" class="input-text" name="billing_city" id="reg_billing_city" value="<?php if ( ! empty( $_POST['billing_city'] ) ) esc_attr_e( $_POST['billing_city'] ); ?>" />
      </p>
    
    <p class="form-row">
      <label for="reg_billing_phone"><?php _e( 'Telefon', 'woocommerce' ); ?> <span class="required">*</span></label>
      <input type="text" class="input-text" name="billing_phone" id="reg_billing_phone" value="<?php if ( ! empty( $_POST['billing_phone'] ) ) esc_attr_e( $_POST['billing_phone'] ); ?>" />
      </p>
   
      <div class="clear"></div>
   
      <?php
  }
   

  add_filter( 'woocommerce_registration_errors', 'woo_validate_name_fields', 10, 3 );
  function woo_validate_name_fields( $errors, $username, $email ) {
      if ( isset( $_POST['billing_first_name'] ) && empty( $_POST['billing_first_name'] ) ) {
          $errors->add( 'billing_first_name_error', __( 'Vorname muss angegeben werden.', 'woocommerce' ) );
      }
      if ( isset( $_POST['billing_last_name'] ) && empty( $_POST['billing_last_name'] ) ) {
          $errors->add( 'billing_last_name_error', __( 'Nachname muss angegeben werden.', 'woocommerce' ) );
      }
    
     if ( isset( $_POST['billing_country'] ) && empty( $_POST['billing_country'] ) ) {
          $errors->add( 'billing_country_error', __( 'Land muss angegeben werden.', 'woocommerce' ) );
      }
    
     if ( isset( $_POST['billing_address_1'] ) && empty( $_POST['billing_address_1'] ) ) {
          $errors->add( 'billing_address_1_name_error', __( 'Straße & Hausnummer muss angegeben werden.', 'woocommerce' ) );
      }
    
     if ( isset( $_POST['billing_postcode'] ) && empty( $_POST['billing_postcode'] ) ) {
          $errors->add( 'billing_postcode_name_error', __( 'Postleitzahl muss angegeben werden.', 'woocommerce' ) );
      }
    
     if ( isset( $_POST['billing_city'] ) && empty( $_POST['billing_city'] ) ) {
          $errors->add( 'billing_city_name_error', __( 'Ort / Stadt muss angegeben werden.', 'woocommerce' ) );
      }
    
     if ( isset( $_POST['billing_phone'] ) && empty( $_POST['billing_phone'] ) ) {
          $errors->add( 'billing_phone_error', __( 'Telefon muss angegeben werden.', 'woocommerce' ) );
      }
    
      return $errors;
  }


  add_action( 'woocommerce_created_customer', 'woo_save_name_fields' );
  function woo_save_name_fields( $customer_id ) {
      if ( isset( $_POST['billing_first_name'] ) ) {
          update_user_meta( $customer_id, 'billing_first_name', sanitize_text_field( $_POST['billing_first_name'] ) );
          update_user_meta( $customer_id, 'first_name', sanitize_text_field($_POST['billing_first_name']) );
      }
      if ( isset( $_POST['billing_last_name'] ) ) {
          update_user_meta( $customer_id, 'billing_last_name', sanitize_text_field( $_POST['billing_last_name'] ) );
          update_user_meta( $customer_id, 'last_name', sanitize_text_field($_POST['billing_last_name']) );
      }
    if ( isset( $_POST['billing_country'] ) ) {
          update_user_meta( $customer_id, 'billing_country', sanitize_text_field( $_POST['billing_country'] ) );
      }
    if ( isset( $_POST['billing_address_1'] ) ) {
          update_user_meta( $customer_id, 'billing_address_1', sanitize_text_field( $_POST['billing_address_1'] ) );
      }
    if ( isset( $_POST['billing_postcode'] ) ) {
          update_user_meta( $customer_id, 'billing_postcode', sanitize_text_field( $_POST['billing_postcode'] ) );
      }
    if ( isset( $_POST['billing_city'] ) ) {
          update_user_meta( $customer_id, 'billing_city', sanitize_text_field( $_POST['billing_city'] ) );
      }
    if ( isset( $_POST['billing_phone'] ) ) {
          update_user_meta( $customer_id, 'billing_phone', sanitize_text_field( $_POST['billing_phone'] ) );
      }
   
  }


  function woo_add_customer_to_email_subject( $subject, $order ) {
    $subject = $order->billing_first_name . ' ' . $order->billing_last_name . ' - ' . $order->get_order_number() . ' - ' . $order->get_date_created()->format ('d.m.Y');
    return $subject;
  }
  add_filter( 'woocommerce_email_subject_new_order', 'woo_add_customer_to_email_subject', 1, 2 );


  add_filter('woocommerce_email_order_meta_keys', 'my_woocommerce_email_order_meta_keys');
  function my_woocommerce_email_order_meta_keys( $keys ) {
      $keys['Extrafeld111'] = '_user_extrafeld1';
      return $keys;    
  }


  add_filter( 'woocommerce_email_order_meta_fields', 'woocommerce_email_order_meta_fields_func', 10, 3 );
  function woocommerce_email_order_meta_fields_func( $fields, $sent_to_admin, $order ) {
    $fields['user_extrafeld1'] = array(
      'label' => __( 'Extrafeld1', 'woocommerce' ),
      'value' => wptexturize( get_post_meta( $order->id, 'user_extrafeld1', true ) )
    );
    $fields['user_extrafeld2'] = array(
      'label' => __( 'Extrafeld2', 'woocommerce' ),
      'value' => wptexturize( get_post_meta( $order->id, 'user_extrafeld2', true ) )
    );
    return $fields;
  }


  add_action( 'woocommerce_email_order_details', 'woo_add_transaction_id', 10, 4);
  function woo_add_transaction_id( $order, $sent_to_admin, $plain_text, $email ){
      $_output = null;
      
      if ($sent_to_admin) {
          $_user = $order->get_user();
          
          if($_user) {
              $_userId = $_user->ID;
              $_extrafeld1 = get_user_meta( $_userId, 'user_extrafeld1', true );
              $_extrafeld2 = get_user_meta( $_userId, 'user_extrafeld2', true ); 
              
              if($_extrafeld1 != '')
                  $_output = 'Extrafeld: ' . $_extrafeld1;
                  
              if($_extrafeld2 != '')
                  $_output .= '<br />Extrafeld: ' . $_extrafeld2;
          }
      }
      
      echo $_output;
  }

Von Haus aus sendet WordPress alle E-Mails, auch die verschiedenen Woocommerce Varianten, per Skript. Hier wird vor allem beim Betreiben eines Online-Shops deutlich, dass manche E-Mail Anbieter Probleme haben, diese E-Mails zuzustellen. Das Resultat sind Bestellbestätigungen, etc. die beim Kunden nie ankommen und an den Shop Betreiber als Mailer Demon Nachricht zurückkommen.

Lösung: Das kostenlose Plugin Post SMTP leistet hier Abhilfe. Damit werden die eigenen Mailaccount Daten in WordPress hinterlegt, so dass die E-Mails in Zukunft nicht mehr per eigenen Skript, sondern über den Mail Anbieter versendet werden. Die Fehlerquote sinkt damit auf ein Minimum.

Bindet man PayPal in seinem Shop als Zahlungsanbieter mit ein, sollte innerhalb von PayPal selbst, der Rückkehrlink zur Webseite ebenfalls hinterlegt werden. Dies sorgt dafür das nach erfolgter Bezahlung, der Nutzer von der PayPal Webseite zurück zum eigenen Shop geleitet wird. Hier erhält der Nutzer dann noch eine Bestellübersicht.

Lösung: Der Link https://www.eigenewebseite.de/checkout/order-received/ führt zur Bestellzusammenfassung, sofern man die Übersetzung bzw. Bezeichnung der Seite im WordPress Backend nicht geändert hat.

Da die Ansprache im englischen nicht zwischen Du und Sie unterscheidet, ist in den Grundeinstellungen des Shops auch die Du Variante voreingestellt. Nun möchte nicht jeder seine Kunde mit Du ansprechen.

Lösung: Im WordPress Backend findet man unter Einstellungen > Allgemein > Sprache der Website > Deutsch (Sie), die passende Einstellung. Leider werden nicht immer alle Woocommerce Elemente auch in Sie Form übersetzt.

In diesem Fall muß man die Sprachdatei von Hand anpassen. Hierzu eignet sich das kostenlose Programm Poedit, in der sich die Sprachdatei bearbeiten lässt. Diese befindet sich unter/wp-content/languages/plugins/woocommerce-de_DE.po

Die Dankes- bzw. Bestellbestätigungsseite lässt sich auf unterschiedliche Weise anpassen, sofern man gänzlich auf Plugins verzichten möchte.

1. Variante: Per Weiterleitung zu einer eigenen Unterseite

add_action( 'template_redirect', 'woo_custom_redirect_after_purchase' );
function woo_custom_redirect_after_purchase() {
  global $wp;
  if ( is_checkout() && !empty( $wp->query_vars['order-received'] ) ) {
    wp_redirect( 'https://www.zurgewuenschtenseite.de' );
    exit;
  }
}

2. Variante: Anpassen der Woocommerce Seite

// Ändern der Überschrift

add_filter( 'the_title', 'woo_title_order_received', 10, 2 );
function woo_title_order_received( $title, $id ) {
  if ( function_exists( 'is_order_received_page' ) && 
       is_order_received_page() && get_the_ID() === $id ) {
    $title = "Vielen Dank für Ihre Bestellung und alles andere";
  }
  return $title;
}
// Ändern der zweite Überschrift

add_filter('woocommerce_thankyou_order_received_text', 'woo_change_order_received_text', 10, 2 );

function woo_change_order_received_text( $str, $order ) {
    $new_str = 'Dies steht nun in der zweiten Zeile';
    return $new_str;
}
// Weiterleiten zu einer eigenen Unterseite bei einem bestimmten Produkt (Grundlage ist die Produkt ID)

add_action( 'woocommerce_thankyou', 'redirect_product_based', 1 ); 
function redirect_product_based ( $order_id ){
            $order = wc_get_order( $order_id );
            foreach( $order->get_items() as $item ) {
                if ( $item['product_id'] == 643 ) {
                     wp_redirect( 'https://www.weiterleitungzurunterseite' );
                
                }
            }        
}

Diese Woocommerce Fehlermeldung ist etwas irreführend, da sie in unterschiedlichen Zusammenhängen auftreten kann.

Lösung: Wenn man sich sicher ist, dass die Versandklassen und Methoden richtig eingestellt und aktiviert wurden, sollte einmal die eingesetzten Plugins testen. Oftmals kann es passieren, dass sich Shipping Plugins gegenseitig stören.

Woocommerce bietet dank WordPress die Möglichkeit verschiedene Nutzergruppen zu erstellen. Der Standartbenutzer als Wocommerce Kunde ist Customer. Nun lässt sich z.B. eine weitere Benutzergruppe mit denselben Rechten erstellen, die dann z.B. als Wiederverkäufer tätig sind. Soll diese bestimmte Nutzergruppe eine zusätzliche Zahlungsmöglichkeit besitzen, die „normale“ Kunden nicht sehen können, lässt sich das wie folgt lösen.

Lösung: Folgendes Skript wird in der function.php des Theme am Ende eingefügt.

function customer_disable_manager( $available_gateways ) 
{global $woocommerce;
if ( isset( $available_gateways['hierdiezahlungsart'] ) && current_user_can('customer') ) {
   unset( $available_gateways['hierdiezahlungsart'] );
}
return $available_gateways;
}
add_filter( 'woocommerce_available_payment_gateways','customer_disable_manager' );

Wenn auf der Checkout Seite die Bereiche Zahlungsart und Artikelübersicht endlos Laden, liegt ein größerer Fehler vor. Leider ist dieser Fehler nie eindeutig, da es durchaus auch ein Konflikt mit einem anderen Skript geben kann.

Lösung: Sofern nicht unbedingt benötigt, sollte man einmal überprüfen, ob ein abschalten des Geo-Lokalisierung in Woocommerce Abhilfe schafft.

In Woocommerce fehlt standartmäßig eine Anzeige des gesparten Preises, wenn ein Aktionspreis hinterlegt wurde. Mit dem nachfolgenden Code der in der functions.php hinterlegt wird, lässt sich der gesparte preis, samt Prozentsatz in der Kategorieansicht, sowie den Produktdetails anzeigen.

function ts_you_save() {
  
  global $product;
  
   if( $product->is_type('simple') || $product->is_type('external') || $product->is_type('grouped') ) {
      
     	$regular_price 	= get_post_meta( $product->get_id(), '_regular_price', true ); 
        $sale_price 	= get_post_meta( $product->get_id(), '_sale_price', true );
     
     	if( !empty($sale_price) ) {
  
              $amount_saved = $regular_price - $sale_price;
              $currency_symbol = get_woocommerce_currency_symbol();

              $percentage = round( ( ( $regular_price - $sale_price ) / $regular_price ) * 100 );
              ?>
              <p class="you_save_price">Du sparst <?php echo number_format($amount_saved,2, '.', '')."€  (". number_format($percentage,0, '', '')."%)"; ?></p>						
              <?php		
        }
   }
}

add_action( 'woocommerce_single_product_summary', 'ts_you_save', 15 );
add_action( 'woocommerce_after_shop_loop_item', 'ts_you_save', 5 );

Mit einem einfachen Hook lassen sich Advanced Custom Fields (ACF) auch in Woocommerce integrieren.

add_action( 'woocommerce_before_add_to_cart_form', 'acf_field_woo', 30 ); 
 
function acf_field_woo() { 
echo get_field('feldname'); 
}

Im Woocommerce Bestellabschluss gibt es nur ein Feld für die Straße & Hausnummer. Leider kommt es oft vor das der Nutzer genau diese Hausnummer vergisst. Mit dem nachfolgenden Snippet wird geprüft ob das genannte Feld auch eine Hausnummer enthält.

add_action('woocommerce_checkout_process', 'custom_validation_process');
function custom_validation_process()
{
    global $woocommerce;
 
    if(isset($_POST['billing_address_1']) and $_POST['billing_address_1'] != '')
    {
        if (!preg_match('/([0-9]+)/Uis', $_POST['billing_address_1']))
        {
            if(function_exists('wc_add_notice'))
                wc_add_notice( __('Haben Sie die Hausnummer bei der Stra&szlig;e vergessen?'), 'error' );
            else
                $woocommerce->add_error( __('Haben Sie die Hausnummer bei der Stra&szlig;e vergessen?') );
        }
    }
   
    if(isset($_POST['ship_to_different_address']))
    {
        if(isset($_POST['shipping_address_1']) and $_POST['shipping_address_1'] != '')
        {
            if (!preg_match('/([0-9]+)/Uis', $_POST['shipping_address_1']))
            {
                if(function_exists('wc_add_notice'))
                    wc_add_notice( __('Haben Sie die Hausnummer bei der Stra&szlig;e vergessen?'), 'error' );
                else
                    $woocommerce->add_error( __('Haben Sie die Hausnummer bei der Stra&szlig;e vergessen?') );
            }
        }
    }
}

Um für ein bestimmtes Land im Shop einen Mindestbestellbetrag zu hinterlegen, hilft der nachfolgen Code Schnipsel. Zusätzlich sollte man noch einen textlichen Hinweis einfügen, um möglichen Käufern schon im Vorfeld über den Mindestbestellbetrag zu informieren.

add_action( 'woocommerce_check_cart_items', 'cw_min_num_products' );
function cw_min_num_products() {
  if( is_cart() || is_checkout() ) {
    global $woocommerce;
                $minimum = 20;
    	$county = array('DE');   
                $cart_tot_order = WC()->cart->total;
    if( $cart_tot_order < $minimum && in_array( WC()->customer->get_shipping_country(), $county )  ) {
          wc_add_notice( sprintf( '<strong>A Minimum order of $%s is required before checking out.</strong>' 
          	. '<br />Current order: $%s.',
          	$minimum,
                $cart_tot_order	),
          'error' );
    }
  }
}
add_action( 'woocommerce_proceed_to_checkout', 'rabatt_text_eins', 1 );
add_action( 'woocommerce_review_order_before_submit', 'rabatt_text_eins', 1 );
function rabatt_text_eins() {
  $text = "Inhaltstext";
   echo $text;
}

Der Text lässt sich zum einen im Template bzw. mit einem Template Designer anpassen oder auch in der Übersetzungsdatei. Eine Möglichkeit dies über die functions.php zu lösen, ist mit dem nachfolgenden Code möglich.

add_action( 'woocommerce_email_before_order_table', 'rabatt_text_hinweismail', 1 );
function rabatt_text_hinweismail() {
  $text = "Beispieltext";
   echo $text;
}

In manchen Fällen ist die automatische Download Tabelle, die Woocommerce in der Bestellmail integriert, nicht sinnvoll. Mit dem nachfolgenden Code in der functions.php Datei wird diese entfernt.

add_action( 'woocommerce_email', 'remove_order_downloads_from_emails', 10, 1 );
function remove_order_downloads_from_emails( $emails ){
    remove_action( 'woocommerce_email_order_details', array( $emails, 'order_downloads' ), 10 );
}
add_action( 'woocommerce_cart_calculate_fees', 'custom_discount_for_pickup_shipping_method', 10, 1 );
function custom_discount_for_pickup_shipping_method( $cart ) {
    if ( is_admin() && ! defined( 'DOING_AJAX' ) )
        return;

    $percentage = 5; // Rabatt in %

    $chosen_shipping_method_id = WC()->session->get( 'chosen_shipping_methods' )[0];
    $chosen_shipping_method    = explode(':', $chosen_shipping_method_id)[0];

    if ( strpos( $chosen_shipping_method_id, 'local_pickup' ) !== false ) {
        $discount = $cart->get_subtotal() * $percentage / 100;
        $cart->add_fee( __('Rabatt') . ' (' . $percentage . '%)', -$discount );
    }
}

Bietet man seinen Kunden ab einem bestimmten Warenwert die Möglichkeit des kostenlosen Versands an, ist es eventuell sinnvoll den noch benötigten Restbetrag, der nötig ist, anzuzeigen. Dies wird durch den folgenden Codeschnipsel, der in der functions.php Datei hinterlegt werden muss, ermöglicht.

add_action( 'woocommerce_before_cart', 'balance_free_shipping_cart_notice' );
  
function balance_free_shipping_cart_notice() {
   $min_amount = 49; //Betrag, ab dem kostenloser Versand angeboten wird
   $current = WC()->cart->subtotal;
   if ( $current < $min_amount ) {
      $added_text = 'Zum kostenlosen Versand fehlt noch ein Restbetrag von ' . wc_price( $min_amount - $current ) . ' !';
      $return_to = wc_get_page_permalink( 'shop' );
      $notice = sprintf( '<a href="%s" class="button wc-forward">%s</a> %s', esc_url( $return_to ), 'Einkauf fortsetzen', $added_text );
      wc_print_notice( $notice, 'notice' );
   }
}

Beim Einrichten der AGB Schnittstelle sollte auf den Einsatz eines Caching Plugins vorübergehend verzichtet werden, hier kann es sonst zu Fehlern kommen. Zudem müssen die genauen Seitennamen eingehalten werden:

Bestehende URL-Titelform

URL-Titelform Deutsch

URL-Titelform Englisch

Zahlungsartenbezahlmoeglichkeitenpayment-methods (en)
Widerrufsbelehrung digitale Warenwiderrufsbelehrung-digitale-warenrevocation_digital (en)
Widerrufsbelehrung Dienstleistungenwiderrufsbelehrung-dienstleistungenrevocation_repair (en)
Widerrufsbelehrungwiderrufsbelehrungrevocation (en)
Versandartenversandmethodenshipping-methods (en)
Impressumimpressumimprint (en)
Datenschutzbelehrungdatenschutzbelehrungdata-security (en)
Batteriehinweisbatteriehinweisbattery_note (en)
AGB / Kundeninformationenagbterms (en)

Eigentlich sollte durch das ändern der Adresse in der Kasse eine automatische Anpassung der Versandkosten (sofern diese unterschiedlich angelegt sind) erfolgen. Ist dies nicht der Fall, kann das nachfolgende Skript das Problem beheben.

add_action('wp_footer', 'billing_country_update_checkout', 50);
function billing_country_update_checkout() {
    if ( ! is_checkout() ) return;
    ?>
    <script type="text/javascript">
    jQuery(function($){
        $('select#billing_country, select#shipping_country').on( 'change', function (){
            var t = { updateTimer: !1,  dirtyInput: !1,
                reset_update_checkout_timer: function() {
                    clearTimeout(t.updateTimer)
                },
                trigger_update_checkout: function() {
                    t.reset_update_checkout_timer(), t.dirtyInput = !1,
                    $(document.body).trigger("update_checkout")
                }
            };
            $(document.body).trigger('update_checkout');
            console.log('Event: update_checkout');
        });
    });
    </script>
    <?php
}

Damit die AGB Checkbox kurz über dem finalen Bestellung abschließen Button landet, ist folgender Code nötig:

add_action( 'init', 'my_child_move_legal_checkboxes', 50 );
function my_child_move_legal_checkboxes() {
remove_action( 'woocommerce_review_order_after_payment', 'woocommerce_gzd_template_render_checkout_checkboxes', 10 );
add_action( 'woocommerce_gzd_review_order_before_submit', 'woocommerce_gzd_template_render_checkout_checkboxes', 10 );
}

Standartmäßig finden man in der Kasse im Adressfeld nur die Bezeichnung „Straße“ und als Standartwert „Straßenname und Hausnummer“. Vor allem wenn man sonst keine Standardwerte in den Feldern verwendet, wirkt das ganze hier sehr unsauber. Zudem müsste als Bezeichnung auch noch die Hausnummer stehen.

Mit dem folgenden Code in der functions.php, löst man das Problem:

add_filter('woocommerce_default_address_fields', function ($address_fields) {
    unset($address_fields['address_1']['placeholder'], $address_fields['address_2']['placeholder']);
    $address_fields['address_1']['label'] = __('Straße & Hausnummer');
    return $address_fields;
});

Für CSS Anpassungen kann es wichtig sein, dass die jeweilige Kategorie im Body angezeigt wird. Dies funktioniert mit Hilfe folgenden Codes in der functions.php:

add_filter( 'body_class', 'wc_product_cats_css_body_class' );
  
function wc_product_cats_css_body_class( $classes ){
  if ( is_singular( 'product' ) ) {
    $current_product = wc_get_product();
    $custom_terms = get_the_terms( $current_product->get_id(), 'product_cat' );
    if ( $custom_terms ) {
      foreach ( $custom_terms as $custom_term ) {
        $classes[] = 'product_cat_' . $custom_term->slug;
      }
    }
  }
  return $classes;
}

Joomla

Bei Aktualisierungen des Joomla Core´s kommt es oft zu der Fehlermeldung: Ajax Loading Error: Forbidden. Verursacht wird dieser Fehler durch den .htaccess Schutz in den entsprechenden Ordnern. Die Folge ist ein Fehlschlagen der Aktualisierung.

Lösung: Deaktiviert man für die Dauer der Aktualisierung die htaccess Dateien, z.B. durch umbenennen in .#htacess /administrator/components, läuft des Update problemlos durch.

Alte Joomla Installationen können für große Probleme sorgen, wenn diese aktualisiert werden sollen, nachdem über Jahre hinweg, keine Updates mehr vorgenommen wurden. Auch alte Joomla Templates sind da keine Ausnahme.

Lösung: Unter folgenden Link findet sich eine Schritt für Schritt Anleitung: http://go-lux.de/go-lux-stellt-sich-vor/webtec/45-joomla-template-migrieren-1-5-nach-3-x

Werden händisch Scripte in einem Beitrag hinterlegt, werden diese nach dem Speichern vom TinyMCE Editor automatisch wieder gelöscht.

Lösung: Zum einen lässt sich der Editor komplett abschalten, wesentlich benutzerfreundlicher ist jedoch folgende Anpassung:

– Unter Extensions > Plugins > Editor
– Hier das TinyMCE Plugin auswählen.
– Unter Use Joomla Text Filter Option, diese auf On schalten.
– Anschließend speichern.

Tritt in vielen Fällen u.a. dann auf, wenn die Weiterleitung nicht richtig gesetzt ist. Dies kann an einer falschen oder fehlerhaften http zu https Weiterleitung liegen oder auch an eine Weiterleitung per JavaScript. Im letzteren Fall sollte man mit einer sauberen Redirect 301 Weiterleitung arbeiten.

HTML, CSS & JavaScript

<center> 
 <embed src="https://www.cms-geek.de" style="border:4px solid #ff0000;" /> 
</center> 
<input list="countries" placeholder="Start typing...">
<datalist id="countries">
  <option>Russia</option>
  <option>Germany</option>
  <option>United Kingdom</option>
  <option>France</option>
</datalist>

Manchmal kann es etwas umständlich sein, wenn man nach einer Verlinkung sucht, die entfernt werden soll. Wenn gar nichts mehr geht, kann man sich mit folgenden CSS Snippet behelfen. Dieser löscht zwar nicht die Verlinkung, sorgt aber dafür, dass sich dieser nicht mehr anklicken lässt.

.beispielelement { pointer-events: none; }

Dies gehört eigentlich zum Grundwissen, kennt aber eventuell dennoch nicht jeder. Damit CSS Anpassungen nur auf Elternelemente angewendet werden, wird hier mit dem > gearbeitet.

#hauptmenue > ul:hover {
    /* style */
}

Die CSS Methode object-fit bietet ideale Lösungen, um Bilder an ein festen Seitenformat anzupassen. Leider ist in alten IE Version diese Methode nicht präsent. Ein Workaround bietet:

https://github.com/bfred-it/object-fit-images

Ein kleines Script, sorgt dafür das der IE mitspielt.

Dafür muss neben der normalen Anweisung, ein zusätzlicher Eintrag gemacht werden:

object-fit: contain;
font-family: 'object-fit: contain;'

Haben Kinder-Elemente denselben Klassennamen wie das Eltern-Element, wird eine CSS Eigenschaft für das Elternelement unweigerlich auch auf alle Kinder übertragen. Mit einer simplen Lösung, lässt sich dies verhindern.

#beispielklasse > div.elternelement {
    color: ff0000;
}

Die Überschrift klingt vielleicht etwas verwirrend, letztlich geht es aber um eine Problemstellung, die immer mal wieder auftreten kann. Man benötigt einen Full Width Container genau an der Stelle, wo sich jedoch ein übergeordneter Container befindet, dessen breiten deutlich geringer ist.

Lösung: Mit folgenden Code Snippet lässt sich dieses Problem beheben.

html

<div class="container">
<div class="row-full">--- Full width container ---</div>
</div>

css

.row-full{
 width: 100vw;
 position: relative;
 margin-left: -50vw;
 height: 100px;
 margin-top: 100px;
 left: 50%;
}

Sofern man auf entsprechende Grafiken verzichten möchte und eine CSS Lösung vorzieht, ist folgendes Snippet recht hilfreich:

.element { transform: skewY(-12deg); }

Mit einer kuzzen Codezeile ist dieses Problem schnell gelöst.

.klassenname { height: 100vh; }

Wenn nur eine Anpassung mit CSS möglich ist um eine Bild an eine bestimmte Stelle zu platzieren, bietet sich die Lösung per :after Pseudoelement an.

.dasbild:after {
    background-image: url('/images/bild.png');
    background-size: 10px 20px;
    display: inline-block;
    width: 10px; 
    height: 20px;
    content:"";
}
#element:before {
    background-image: url('Pfad zum gewünschten Bild');
    background-size: 150px 200px;
    display: block;
    width: 150px; 
    height: 200px;
    content:"";
    position: absolute;
    z-index: 1;
    top: 50%;
    -webkit-transform: translateY(-50%);
    -moz-transform: translateY(-50%);
    -ms-transform: translateY(-50%);
    transform: translateY(-50%);
    /* Hälfte der Element Breite nehmen, in diesem Fall 200px / 2 */
    left: calc(50% - 100px);
}

Möchte man auf die typischen Bullet Points verzichten und alternative html Symbole einer Liste voranstellen, bringen diese Codezeilen den gewünschten Erfolg.

ul {
  list-style: none;
}

ul li:before {
  content: '✓';
}

Alternativ können auch direkt ASCII Symbole verwendet werden. Mit dem nachfolgenden CSS Code, lässt sich der Doppelfeil darstellen.

/* \0020 sorgt hier für Abstand zwischen dem Symbol und nachfolgenden Listentext */
ul li:before {
     content: "\00BB \0020";
}

Generell lassen sich per CSS auch alle ASCII Zeichen darstellen, problematischer ist, das hierfür nicht immer gleich der richtige Wert zu finden ist, damit das Symbol auch per content eingefügt werden kann.

#main .element:before {
   content:  "\2713 ";
}

Die meisten Symbole lassen sich u.a. auf der Webseite von key-shortcut.com finden. Liegt der richtige Code für das Symbol nicht vor, kann dieser mit dem Entity Conversion Calculator entsprechend umgewandelt werden.

Oftmals lassen sich html Anker nicht richtig positionieren, so dass es wünschenswert wäre, wenn der Anker bereits einige Pixel oberhalb der eigentlichen Position beginnen würde.

Nachfolgend finden sich eine einfache Lösung per CSS, sowie eine Variante mit jQuery, die ein langsameres sliden zum Anker ermöglicht.

Lösung: In beiden Fällen sorgt der top-Wert (-250px) für die gewünschte Positionierung.

a.anchorPos {
    display: block;
    position: relative;
    top: -250px;
    visibility: hidden;
}
jQuery(function() {
                jQuery('a[href*="#"]:not([href="#"])').click(function() {
                  if (location.pathname.replace(/^\//,'') == this.pathname.replace(/^\//,'') && location.hostname == this.hostname) {
                    var target = jQuery(this.hash);
                    target = target.length ? target : jQuery('[name=' + this.hash.slice(1) +']');
                    if (target.length) {
                      jQuery('html, body').animate({
                        scrollTop: target.offset().top -250
                      }, 1100); //scrolling speed
                      return false;
                    }
                  }
                });
              });

Eine simple Lösung ist folgende CSS Anpassung, die am entsprechenden Anker hinterlegt wird:

scroll-margin-top: 1em;

Vor allem in Safari scheint die Formatierung eines select Feldes nicht mit den Standard Einstellungen zu funktionieren. Nachfolgend eine einfache Lösung womit der Abstand zum linken Rand erhöht werden kann.

padding-left:17px; 
-webkit-padding-start:17px;

Nachfolgend ein einfaches Hamburger Menü (Toggle Menü) das ohne JavaScript auskommt.

html

<nav role="navigation">
  <div id="menuToggle">
    <input type="checkbox" />
    <span></span>
    <span></span>
    <span></span>
    <ul id="menu">
      <a href="#"><li>Home</li></a>
      <a href="#"><li>About</li></a>
      <a href="#"><li>Info</li></a>
      <a href="#"><li>Contact</li></a>
    </ul>
  </div>
</nav>

CSS

#menuToggle
{
  display: block;
  position: relative;
  top: 50px;
  left: 50px;
  z-index: 1;
  -webkit-user-select: none;
  user-select: none;
}

#menuToggle input
{
  display: block;
  width: 40px;
  height: 32px;
  position: absolute;
  top: -7px;
  left: -5px;
  cursor: pointer;
  opacity: 0;
  z-index: 2;
  -webkit-touch-callout: none;
}

#menuToggle span
{
  display: block;
  width: 33px;
  height: 4px;
  margin-bottom: 5px;
  position: relative;
  background: #cdcdcd;
  border-radius: 3px;
  z-index: 1;
  transform-origin: 4px 0px;
  transition: transform 0.5s cubic-bezier(0.77,0.2,0.05,1.0),
              background 0.5s cubic-bezier(0.77,0.2,0.05,1.0),
              opacity 0.55s ease;
}

#menuToggle span:first-child
{
  transform-origin: 0% 0%;
}

#menuToggle span:nth-last-child(2)
{
  transform-origin: 0% 100%;
}

#menuToggle input:checked ~ span
{
  opacity: 1;
  transform: rotate(45deg) translate(-2px, -1px);
  background: #232323;
}

#menuToggle input:checked ~ span:nth-last-child(3)
{
  opacity: 0;
  transform: rotate(0deg) scale(0.2, 0.2);
}

#menuToggle input:checked ~ span:nth-last-child(2)
{
  transform: rotate(-45deg) translate(0, -1px);
}

#menu
{
  position: absolute;
  width: 300px;
  margin: -100px 0 0 -50px;
  padding: 50px;
  padding-top: 125px;
  background: #ededed;
  list-style-type: none;
  -webkit-font-smoothing: antialiased;
  transform-origin: 0% 0%;
  transform: translate(-100%, 0);
  transition: transform 0.5s cubic-bezier(0.77,0.2,0.05,1.0);
}

#menu li
{
  padding: 10px 0;
  font-size: 22px;
}

#menuToggle input:checked ~ ul
{
  transform: none;
}

 

#dasmenue .menu-item:not(:last-child) a {
   border-right: 1px solid #ededed;
   padding:5px 10px 5px 5px;
}
#dasmenue .menu-item:last-child a {
   padding:5px 3px 5px 5px;
}

Schön und einfach ist es z.B. den YouTube Einbettungscode einzufügen. Jedoch müssen dafür feste Werte eingegeben werden, das auf Mobilen Geräte dann immer wieder zu Darstellungsproblemen führt.

Lösung: Mit folgenden Code lässt sich das Problem umgehen.

<div class="embed-container">//Hier wird das iframe eingefügt.</div>
.embed-container {
  position: relative;
  padding-bottom: 56.25%; /* ratio 16x9 */
  height: 0;
  overflow: hidden;
  width: 100%;
  height: auto;
}

.embed-container iframe {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 100%;
}

/* ratio 4x3 */
.embed-container.ratio4x3 {
  padding-bottom: 75%;
}

Mit CSS erzeugte Kreise sehen oftmals an den Rändern unsauber aus, mit der box-shadow Eigenschaft, lässt sich dies ändern.

-mox-box-shadow: 0 0 1px white;
-webkit-box-shadow: 0 0 1px white;
box-shadow: 0 0 1px white;

Mit ein wenig CSS lassen sich problemlos farbliche Overlays über Bilder erstellen. Dafür muss das entsprechende Bild lediglich innerhalb eines <div> liegen.

.card-overlay{
  position: relative;
}
.card-overlay:after {
  content:'';
  position:absolute;
  left:0px;
  top:0px;
  width:100%;
  height:100%;
  background: rgba(0, 0, 0, 0.5);
}

Um Textblöcke schöner aussehen zu lassen, ist eine Möglichkeit diese als Blocksatz umzusetzen.

p { 
    text-align: justify; 
};

Wer er doch lieber linkbündig mag, kann auch die Silbentrennung nutzen.

p { 
    hyphens: auto; 
}

Damit diese funktioniert muss die Sprache der Webseite gesetzt sein.

<html lang="de">

In der Tat ist diese Anpassung schon etwas extravagant, aber es gibt sicherlich Gründe in der diese Sinn ergeben und zwar die Hintergrundfarbe bei Textmarkierungen. Diese werden meist vom verwendeten Browser gesteuert, jedoch lässt sich per CSS die Textfarbe sowie der Hintergrund bei Markierungen ebenfalls steuern.

::-moz-selection { 
   color:#fff; 
   background:#000; 
}
::selection { 
   color:#fff; 
   background:#000; 
}
.button {
  width: 130px;
  height: 130px;
  text-transform: uppercase;
  letter-spacing: 1px;
  color: #fff;
  text-align: center;
  background: rgba(30, 144, 255, 0.6);
  border-radius: 50%;
  animation: shadow-pulse 1s infinite;
}

@keyframes shadow-pulse
{
  0% {
    box-shadow: 0 0 0 0px rgba(0, 0, 0, 0.2);
  }
  100% {
    box-shadow: 0 0 0 35px rgba(0, 0, 0, 0);
  }
}

Die Überschrift sagt alles und die Umsetzung ist ebenfalls recht einfach.

#eltern-container {
    position: relative;
    float: none;
    margin: 0 auto;
    width: 100%;

    /* Die nachfolgenden drei Anweisungen sind entscheident! */
    display: flex;
    justify-content: center;
    align-items: center;
}
margin-left: auto; 
margin-right: 0;
a {
  -moz-box-sizing: border-box;
  display:flex;
  align-items:center;
  justify-content:center;
}

Bei Pseudo-Elementen :before und :after ist es etwas schwieriger, einer Linie bzw. Border eine entsprechende Länge zuweisen zu können. Nachfolgend eine mögliche Variante.

.linie-unten:after {
    background-color: #fff; 
    bottom: 0;
    content: '';
    display: block;
    height: 6px;
    left: 50%;
    position: absolute;
    transform: translate(-50%,0);
    width: 575px;
}

Dieser Effekt beschreibt das langsamen reinzoomen in ein Foto, was oft bei Slidern verwendet wird, um hier ein wenig mehr Bewegung auf einer sonst statischen Webseite entstehen zu lassen.

Dieser Effekt lässt sich auch mit etwas CSS nachstellen:

<div class="image-wrap">
  <img src="https://www.cms-geek.de/wp-content/uploads/cms-geek-schreibtisch.jpg">
</div>
.image-wrap {
  width: 100%;
  height: 50vw;
  margin: 0 auto;
  overflow: hidden;
  position: relative;
}

.image-wrap img {
  width: 100%;
  animation: move 40s ease;
  -ms-animation: move 40s ease;
  -webkit-animation: move 40s ease;
  -0-animation: move 40s ease;
  -moz-animation: move 40s ease;
  position: absolute;
}

@-webkit-keyframes move {
  0% {
    -webkit-transform-origin: bottom left;
    -moz-transform-origin: bottom left;
    -ms-transform-origin: bottom left;
    -o-transform-origin: bottom left;
    transform-origin: bottom left;
    transform: scale(1.0);
    -ms-transform: scale(1.0);
    /* IE 9 */
    
    -webkit-transform: scale(1.0);
    /* Safari and Chrome */
    
    -o-transform: scale(1.0);
    /* Opera */
    
    -moz-transform: scale(1.0);
    /* Firefox */
  }
  100% {
    transform: scale(1.2);
    -ms-transform: scale(1.2);
    /* IE 9 */
    
    -webkit-transform: scale(1.2);
    /* Safari and Chrome */
    
    -o-transform: scale(1.2);
    /* Opera */
    
    -moz-transform: scale(1.2);
    /* Firefox */
  }
}

Damit z.B. eine Box responsive quadratisch dargestellt werden kann, sind die folgenden CSS Anpassungen nötig.

.square {
  position: relative;
  width: 50%;
}

.square:after {
  content: "";
  display: block;
  padding-bottom: 100%;
}

.content {
  position: absolute;
  width: 100%;
  height: 100%;
}
.container {
  display: flex;
  justify-content: center;
}
h2 {
  display: flex;
  align-items: center;
}

h2::after {
  content: '';
  flex: 1;
  margin-left: 1rem;
  height: 1px;
  background-color: #000;
}
Wort1 <span style="width: 100px">&nbsp;</span> Wort2
span {
  display:inline-block;
  border-bottom: 1px solid black;
  line-height:0
}
#line h2 {
  position: relative;
}

#line h2::before {
  content: '';
  height: 2px;
  background: #2a0717;
  position: absolute;
  top: 8px;
  right: 101%;
  width: 999%;
}

Einzelne Elemente / Ebenen in SVG Dateien lassen sich per CSS manipulieren, wenn diese zuvor richtig angelegt wurden.

Dafür sollte die gewünschte Ebene oder eben mehrere jeweils in einem separaten Ordner hinterlegt werden und dieser Ordner eindeutig benannt werden. Wird die Grafik nun gespeichert, hier bietet sich Adobe Illustrator an, sind folgende Exporteinstellungen einzuhalten:

.eicon-chevron-right::before {
   font-family: "Font Awesome 5 Free";
   content: "" !important;
   width: 35px;
   height: 35px;
   border-color: #000;
   display: inline-block;
   border-bottom: 2px solid;
   border-left: 2px solid;
   -webkit-transition: all 0.5s ease;
   -moz-transition: all 0.5s ease;
   -o-transition: all 0.5s ease;
   -ms-transition: all 0.5s ease;
   transition: all 0.5s ease;
   -ms-transform: rotate(225deg);
   -webkit-transform: rotate(225deg);
   margin-right:10px;
}
.eicon-chevron-left::before {
   font-family: "Font Awesome 5 Free";
   content: "" !important;
   width: 35px;
   height: 35px;
   border-color: #000;
   border-bottom-color: rgb(0, 0, 0);
   border-left-color: rgb(0, 0, 0);
   display: inline-block;
   border-bottom: 2px solid;
   border-left: 2px solid;
   -webkit-transition: all 0.5s ease;
   -moz-transition: all 0.5s ease;
   -o-transition: all 0.5s ease;
   -ms-transition: all 0.5s ease;
   transition: all 0.5s ease;
   -ms-transform: rotate(45deg);
   -webkit-transform: rotate(45deg);
   transform: rotate(45deg);
   margin-left:10px;
}

Im body muss die Klasse „animate-in“ eingefügt werden.

.animate-in {
    -webkit-animation: fadeIn .1s ease-in;
    animation: fadeIn .1s ease-in;
}
.animate-out {
    -webkit-transition: opacity .1s;
    transition: opacity .1s;
    opacity: 0;
}
@-webkit-keyframes fadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
}
@keyframes fadeIn {
    from { opacity: 0; }
    to { opacity: 1; }
}
window.addEventListener("beforeunload", function () {
  document.body.classList.add("animate-out");
});

Nicht immer sollen Radio Buttons so aussehen wie sie eben aussehen und manchmal können diese auch nicht durch andere Formelemente ersetzt werden, somit muss eine Lösung her.

#form1 input[type="radio"] {
  opacity: 0 !important;
  position: fixed;
  width: 0px;
}
#form1 label:before,
#form1 label:after {
  opacity: 0 !important;
}
#form1 label {
    display: table-cell;
    background-color: #ff0000;
    padding: 10px 20px;
    border: 1px solid #ff0000;
    border-radius: 15px;
    color: #fff;
    cursor: pointer;
    width: 110px;
    height: 110px;
    text-align: center;
    vertical-align: middle;
    font-weight: 400;
    font-size: 1.1em;
}
#form1 input[type="radio"]:checked + label,
#form1 input[type="checkbox"]:checked + label {
    background-color:#fff;
    color: #ff0000;
}
#bewerbungen label:hover {
    background-color: #fff;
    color: #ff0000;
}

Es gibt viele Möglichkeiten eine Weiterleitung, sei es intern oder extern, umzusetzen. Wenn hingegen weder das Einbinden eines Codes, in den Header, Footer oder als body onload möglich ist und darüber hinaus auch auf jQuery verzichtet werden muss, werden die Lösungswege weniger.

Nachfolgend ein einfaches Skript, was innerhalb des body eingefügt werden muss.

<script language="javascript" type="text/javascript">
          <!--
  setTimeout(function()
  {window.open('https://www.hiergehteszurgewuenschtenwebseite.de', '_blank');
             } , 1000);
  
          // –>
</script>

Ein einfaches Skript das eine Weiterleitung anhand der eingestellten Sprache des verwendeten Browsers durchführt.

<script type="text/javascript">
var langcodes=new Array("en", "de", "default")

var langredirects=new Array("/englischeseite.html", "/deutscheseite.html","/englischeseite.htmln")

var languageinfo=navigator.language? navigator.language : navigator.userLanguage
var gotodefault=1

function redirectpage(dest){
if (window.location.replace)
   window.location.replace(dest)
else
   window.location=dest
}

for (i=0;i<langcodes.length-1;i++){
if (languageinfo.substr(0,2)==langcodes[i]){
   redirectpage(langredirects[i])
   gotodefault=0
break
}
}

if (gotodefault)
   redirectpage(langredirects[langcodes.length-1])
</script>
var url_string = "http://www.example.com/t.html?a=1&b=3&c=m2-m3-m4"; //window.location.href
var url = new URL(url_string);
var c = url.searchParams.get("c");
console.log(c);

In manchen Fällen ist es nötig, dass Text mit einer bestimmten Zeichenlänge gekürzt wird. Der nachfolgende Code ermöglicht genau dieses. Ist der Text länger als 53 Zeichen, wird dieser gekürzt und ein … angefügt.

jQuery(function($) { 
$(".page-id-1 .title a").text(function(index, currentText) {
  if (currentText.length > 53) {
    return currentText.substr(0, 53)+'...';
  }
});
});

Nachträglich jQuery Code einzufügen ist keine Seltenheit, in Kombination mit dem Revolution Slider kann es hier aber zu Konflikten kommen, so das entweder der Code oder der Slider nicht reagieren.

Lösung: jQuery Bibliotheken nutzen jQuery() und $(), man sollte hier jQuery() verwenden, um Konflikte zu vermeiden.

jQuery ( function($) {
//Hier den Code einfügen
} );

Nicht immer kann dem body händisch eine Klasse hinzugefügt werden. In diesen Fällen hilft die folgende Lösung.

Lösung:

add_filter( 'body_class','my_body_classes' );
function my_body_classes( $classes ) {
    $classes[] = 'klassenname';
    return $classes; 
}

Ein nützlicher Effekt kann es sein, CSS Elemente abhängig von der Tageszeit zu ändern. Das nachfolgende Beispiel ändert ein Hintergrundbild.

var d = new Date();
var n = d.getHours();
if (n > 19 || n < 6)
   $("#header").css("background", "url('nacht.jpg')");
else
   $("#header").css("background", "url('tag.jpg')");
<script type="text/javascript">
$(".news-btn").click(function() {
    $('html, body').animate({
        scrollTop: parseInt($("#newsletter").offset().top - 200)
    }, 2000);
});
</script>

Es kann Webseiten Elemente geben (Buttons, Popups) die erst nach etwas scrollen auf der gewünschten Seite erscheinen sollen.

Lösung: Folgendes Skript sorgt genau für diesen Effekt. Dieses sollte im Header integriert werden.

$(document).scroll(function () {
    var y = $(this).scrollTop();
    if (y > 900) {
        $('.meinButton').fadeIn();
    } else {
        $('.meinButton').fadeOut();
    }
});

Der Wert 900 dient hier als Abstand. Das Element wird also sichtbar, wenn man mindestens 900 Pixel nach unten gescrollt hat.

Alternative:

jQuery(window).scroll(function(){
  if(jQuery(document).scrollTop() > 200){
    jQuery('.mehr-pfeil').hide();
  } else {
    jQuery('.mehr-pfeil').show();  
  }
});

Folgender Code im Footer oder als eigenständige js Datei löst dieses Problem.

<script>
jQuery ( function($) {
  $(document).ready(function() {
    $(".fcb-link-button").delay(7000).fadeIn(500);
  });
});
</script>

Der Wert 7000 steht für Sekunden, hier also 7s.

In der CSS Datei muss noch folgendes eingefügt werden, damit das Element beim Aufruf der Webseite unsichtbar ist:

.fcb-link-button {display:none;}

Die Problemstellung ist recht einfach. Wir haben zu viel Text auf einer Seite und wollen einen Teil dieses Textes ausblenden und nur bei Bedarf einblenden lassen. Diese Funktion soll über einen Button geschehen. Dieser ist mit „Mehr“ beschriftet und soll sich in „Weniger“ ändern, wenn der restliche Text eingeblendet wird.

Lösung: Folgendes Code Snippet jetzt diese Bedingung um.

// #btn-mehr ist die ID des Buttons, #mehr-text die ID des versteckten Contents

jQuery( document ).on( 'click',  '#btn-mehr', function( event ) {
   event.preventDefault();
   jQuery('#mehr-text').fadeToggle("slow");
   if(jQuery('#btn-mehr .button-text').text() == "Mehr"){
       jQuery('#btn-mehr .button-text').text("Weniger");
    }else{
       jQuery('#btn-mehr .button-text').text("Mehr");
    }
});

Und damit der entsprechende Content auch im Vorfeld ausgeblendet ist, muss noch etwas CSS Code verwendet werden.

#mehr-text {
    display: none;
}

Die Vorgabe ist relativ simpel, nach Klick auf einen Button, wir ein neues Element eingeblendet, das ebenfalls einen Button beinhaltet. Dieser unterscheidet sich zwar von der Funktion, trägt aber dieselbe Bezeichnung wie der erste Button und sorgt daher eher für Irritationen.

Das nachfolgende Skript löst genau dieses Problem.

<script type="text/javascript">
    jQuery( document ).on( 'click', '.KlasseDesErstenButtons', function( event ) {
  event.preventDefault();
  if (jQuery('KlasseDesElementsDasSichtbarWird').css('display') == 'block') { 
    jQuery("KlasseDesButtonsDerGeändertWerdenSoll").val('Anfrage absenden');
  }
    });
</script>

Das nachfolgende Beispiel lässt eine CSS Animation erst dann ablaufen, wenn diese im Sichtfeld des Nutzers ist, was z.B. durch scrollen erreicht wird. Dabei wird ein Bild sichtbar, das eine gezeichnete Linie ergibt.

Die gezeichnete Linie liegt als Bild bereits vor. Per CSS wird diese zu Anfang komplett verdeckt und dann langsam sichtbar, so dass der Effekt einer von oben nach unten gezeichneten Linie entsteht.

html

<div class="contain-element">
<img class="prints" src="/line.png" alt="Linie">
<span class="cover"></span>
</div>

CSS

.contain-element {
     position: relative;
     text-align: center;
}
.cover {
     position: absolute;
     left: 0;
     background: #fff;
     height: 1000px;
     width: 1420px;
}
@keyframes walk {
     to {
           transform: translateY(675px);
    }
}

jQuery

<script>
  jQuery ( function($) {
  $(document).ready(function(){
        $(window).scroll(function(){
        if ($('.contain-element').isOnScreen()) {
      $(".cover").css("animation","walk 4s steps(28, end) forwards");
        } else {
            // The element is NOT visible, do something else
        }
    });
});

$.fn.isOnScreen = function(){
    var win = $(window);
    var viewport = {
        top : win.scrollTop(),
        left : win.scrollLeft()
    };
    viewport.right = viewport.left + win.width();
    viewport.bottom = viewport.top + win.height();
    
    var bounds = this.offset();
    bounds.right = bounds.left + this.outerWidth();
    bounds.bottom = bounds.top + this.outerHeight();
    
    return (!(viewport.right < bounds.left || viewport.left > bounds.right || viewport.bottom < bounds.top || viewport.top > bounds.bottom)); 
};
} );
</script>

Soll ein bestimmtes Element erst sichtbar werden, wenn während des scrollen ein anderes Element erscheint, kann nachfolgender Code hilfreich sein.

var p = $( ".passedMe" ); //Element das erscheint
var offset = p.offset();
offset = offset.top;

$(window).scroll(function () {  
    if ($(window).scrollTop()   >  offset ) {
       $('.showHide').fadeIn(); //Element das sichtbar werden soll
    }
  else { $('.showHide').fadeOut(); }
});
<ul id="navlist">
    <li id="home"><a class="nav" href="home">Home</a></li>
    <li id="about"><a class="nav" href="about-us">About Us</a></li>
</ul>
$('#navlist a').click(function(e) {
    e.preventDefault();
    $('#navlist a').removeClass('selected');
    $(this).addClass('selected');
});

Der nachfolgende Code fügt einen Hinweis auf allen Unterseiten einer Domain ein, wenn eine bestimmte Zeichenfolge innerhalb der URL vorhanden ist.

function add_code_to_body() {
   if (strpos($_SERVER['REQUEST_URI'], 'zeichenfolge-in-der-url') !== false){ ?>
   <div style="background-color:#000;color:#fff;font-size: 17px;padding: 8px;text-align: center;">Hier kommt der Hinweis.</div>
   <?php }
}
add_action( 'wp_body_open', 'add_code_to_body' );

Die Fehlermeldung tritt oftmals dann auf, wenn veraltete Befehle Verwendung finden, die in aktuellen jQuery Versionen nicht mehr unterstützt werden.

Lösung: Wenn möglich programmiert man den Code um, damit dieser auf dem aktuellen Stand ist. Andernfalls hilft das jQuery Migrate Plugin, das die Verwendung älteren Codes erlaubt.

<script src="https://code.jquery.com/jquery-3.3.1.min.js"></script>
<script src="https://code.jquery.com/jquery-migrate-1.4.1.min.js"></script>

Die Timeout Funktion kann eventuell auch weggelassen werden, war in meinem Fall aber nötig.

$(document).ready(function() {
    
setTimeout(function() {
    
var text = $('.slp_result_citystatezip').html().split(' ');
$('.slp_result_citystatezip').text(text[1] + ' ' + text[0]);
  
}, 1000);
  
});

Hierzu muss das gewünschte Element lediglich eine eindeutige ID oder Klasse besitzen.

$('.meineklasse').attr('onclick', 'myFunction()');

Google & Co

Nachfolgend die wichtigsten Begriffe die innerhalb von Google Analytics auftauchen, samt ihrer Bedeutung:

Absprungsrate: Wie viele Personen gehen nach dem Aufruf einer einzelnen Seite wieder. Diese besuchen somit keine anderen Seiten, sondern gehen gleich wieder.

Ausstiegsrate: Die Ausstiegsrate ist nicht zu verwechseln mit der Absprungrate, denn sie gibt den Prozentsatz der Seitenaufrufe an, die die letzten während einer Sitzung waren. Es wurden also vorher mindestens noch eine weitere Seite während der Sitzung aufgerufen.

Conversion-Rate: Die Conversion-Rate gibt den prozentualen Anteil an Webseiten Besuchen wieder, die ein zuvor definiertes Ziel auf der Webseite erreicht haben.

Ereignisse (Events): Unter Ereignissen versteht man in Google Analytics Nutzeraktionen, die nicht als Seitenaufrufe erfasst werden können, beispielsweise einen Download oder einen Klick auf einen Button.

Sitzungen: Ist die Anzahl der Besuche auf der Webseite, auch wenn derselbe Nutzer mehrfach die Webseite besucht, wird jedes Mal dies als Sitzung gewertet.

Neue Sitzungen: Wie viele Nutzer/Sitzungen sind „neu“ oder „wiederkehrend“.

Durchschnittliche Sitzungsdauer: Die durchschnittliche Dauer einer Sitzung.

Nutzer: Die Anzahl der Besuche eines Nutzers an einem Tag auf der Webseite.

Seitenaufrufe: Wie viele einzelne Seiten wurden während einer Sitzung aufgerufen.

Seiten/Sitzung: Wie viele Seiten werden pro Sitzung aufgerufen.

Trichter: Bei der Einrichtung von Zielen kann ein Trichter erstellt werden. Er steht für eine Abfolge von Webseiten, die aufgerufen werden müssen, damit das Ziel als erreicht gilt.

Mit wenigen Schritten lässt sich ein mailto:, sowie tel: Link mit Google Analytics tracken. Hierbei sind zwei Schritte nötig.

Aufbau tel:

<a href="tel:+310123456789" onclick="gtag('event', 'Click', { 'event_category': 'Call' });">Linkname</a>

Aufbau mailto:

<a href="mailto:info@example.com" onclick="gtag('event', 'Click', { 'event_category': 'Email' });">Linkname</a>

Skript Alternative:

<script src="https://code.jquery.com/jquery-3.1.0.js"></script>
<script>
$("[href*='tel:'], [href*='mailto:']").click(function(e) {

  e.preventDefault();
  var href = $(this).attr('href');

  // tel:
  if (href.toLowerCase().indexOf("tel:") >= 0) {
    eventCategory = 'Call';
    eventLabel = href.replace('tel:', '');

  }

  // mailto:
  if (href.toLowerCase().indexOf("mailto:") >= 0) {
    eventCategory = 'Email';
    eventLabel = href.replace('mailto:', '');
  }

  gtag('event', 'Click', {
    'event_category': eventCategory,
    'event_label': eventLabel
  });

  setTimeout(function() {
    window.location = href;
  }, 500);

});
</script>

Das Skript sollte dann eingesetzt werden, wenn generell alle mailto und tel Links auf einer Webseite getrackt werden sollen.

Einstellungen in Analytics:
Damit Analytics die Klicks nun auch verarbeiten kann, muß unter Verwaltung -> Zielvorhaben ein neues Ereignis angelegt werden.

analytics-ereignis

Eine Variante um ein Kontaktformular mit Google Analytics tracken zu können ist es, nicht das Formular selbst, sondern eine anschließende Erfolgsseite zu tracken.

Dafür stellt man das Kontaktformular so ein, das es nach erfolgten senden zu einer Seite weiterleitet, die dem Nutzer das erfolgreiche Absenden noch einmal bestätigt. Diese Seite ist es dann, die als neues Zielvorhaben in Analytics hinterlegt wird.

analytics-weiterleitung

Um Google Analytics rechtssicher einbinden zu können, muss u.a. die IP Adresse des Nutzers anonymisiert werden. Welche Punkte darüber gehend noch beachtet werden müssen, erkläre ich u.a. in diesem Video Tutorial.

ga('set', 'anonymizeIp', true);

Startet man eine Kampagne mit Facebook Ads, z.B. um eine Landingpage zu bewerben, glaubt man vermutlich an einen Fehler, wenn man die Facebook Insights Daten, mit den Google Analytics Werten von der Landingpage vergleicht. Hier kommt es zu teilweise extremen Differenzen.

Hintergrund: Analytics und Facebook haben grundlegend verschiedene Ansätze, wie dessen Werte gemessen werden. Aus diesem Grund ist es nicht ungewöhnlich, das Facebook zwar 500 Klicks gezählt hat, auf der Landingpage laut Analytics aber nur 30 Personen waren.

Mehr Infos: Auf ANALYTICSkiste geht Michaela Linhart ins Detail und erklärt die genauen Hintergründe und mögliche Lösungsansetze.

Wer einen Google My Business Eintrag sein eigenen nennt und hier seine Bewertungen erhöhen möchte, ist mit einer Verlinkung von der eigenen Webseite zu genau diesen Rezesionen gut beraten. So können Webseiten Besucher direkt eine Rezension auf Google schreiben.

Der Link zur Bewertung sieht folgendermaßen aus:

https://search.google.com/local/writereview?placeid=XXXXXXXXXXXXXXXXXXXXXXXXXXX

Die mit X gekennzeichnete PlaceID, ist die eindeutige Adresse zum eigenen Google My Business Eintrag. Nutzen Sie den Google eigenen Dienst PlaceID Lookup Tool um die entsprechende Adresse zu finden.

Mit Kritik muss man umgehen können, sofern Sie sachlich gehalten wird und den rechtlichen Bestimmungen unterliegen. Verleumdungen und das Behaupten falscher Tatsachen, muss hingegen niemand hinnehmen.

Neben dem Melden-Button neben der Rezension selbst, kann über das folgenden Formular direkt mit Google kommuniziert werden.

Wenn auch diese Variante ohne Erfolg bleibt, leider ist Google hier meist nicht hilfreich, sollte man sich an einen Anwalt wenden, der ein entsprechendes Schreiben an Google aufsetzt, dessen Erfolgschance deutlich höher sind.

Wär fremde Plugins benutzt, um Google Map auf der Webseite zu integrieren, sieht oft anstatt der gewünschte Karte den Hinweis „Hoppla, ein Fehler ist aufgetreten.“

Lösung: Zu allererst muss ein Google API Key erstellt werden, dies ist unter https://developers.google.com/maps/documentation/javascript/ möglich, wenn man dort auf Schlüssel anfordern klickt (ein Google Konto ist Voraussetzung). Der anschließend erzeugte Schlüssel wird in das entsprechende Plugin integriert. Mittlerweile muss im Google Konto eine Kredidkarte hinterlegt sein, damit die Karte auch wirklich aktiv geschaltet wird.

Funktioniert dies nicht so ohne weiteres, kann man dies auch manuell erledigen. Dafür sucht man in den Plugin Dateien nach dem Link zur Google Map und ergänzt diesen, wie in dem folgenden Beispiel:

<script type='text/javascript' src='https://maps.googleapis.com/maps/api/js?key=AIzaSyD_6SsSIeIwhkHkHVfvM_gj4nVjtOqkBQw&#038;language=de&#038;ver=1'></script>

Server / Hoster / Datenbanken

Verwendet man ein Plugin wie Contact Form 7 um ein Kontaktformular auf seiner Webseite zu verwenden, besitzt HostEurope eine besondere Sicherheitseinstellung. Unter Produktverwaltung > WebHosting > Skripte&Datenbanken > Skript-Einstellungen muss die Absender E-Mail Adresse eingetragen sein, die identisch mit der im Plugin hinterlegten ist. Andernfalls werden diese Nachrichten, trotz erfolgreichen Versenden, nie zugestellt.

Unabhängig welches CMS eingesetzt wird, die Dateien- und Verzeichnisrechte sollten immer richtig gesetzt sein, da diese zur Sicherheit einer Webseite wesentlich beitragen.

Die einfachste Variante, um diese Rechte für eine Vielzahl an Ordnern, Unterordnern und Dateien setzen zu können, ist der Einsatz per SSH Verbindung. Für Windows Systeme gibt es das kostenlose Programm PuTTY, das eine solche verschlüsselte Verbindung zum Server ermöglicht.

WordPress nennt für Verzeichnisse (Ordner) die Rechte 755, für Dateien 644.
Ersetzt man die beiden in kursiv gesetzten Pfade durch die Server eigenen  absoluten Pfade (Document Root), werden alle darin befindlichen Ordner und Dateien automatisch angepasst.

Für Verzeichnisse:
find /path/to/your/wordpress/install/ -type d -exec chmod 755 {} \;

Für Dateien:
find /path/to/your/wordpress/install/ -type f -exec chmod 644 {} \;

Wird eine bereits in Suchmaschinen indexierte Webseite überarbeitet oder erst einmal generell abgeschaltete, macht es eventuell Sinn, alle bisherigen Seiten auf eine Startseite oder Wartungsseite weiterzuleiten. Durch das anpassen der .htaccess Datei im Hauptverzeichnis der Webseite, ist dies recht einfach möglich.

Aus SEO Sicht ist dies Variante nicht zu empfehlen, da es dadurch automatisch zu Ranking Verlusten kommen kann. In Fällen wo dies jedoch vernachlässigt werden kann, ist dieser Code Schnipsel recht hilfreich.

Lösung:

RewriteEngine on
RewriteBase /

RewriteCond %{REQUEST_URI} !^/(index\.html|bg\.jpg)?$ [NC]
RewriteCond %{REQUEST_URI} !^/img(/|$) [NC]
RewriteRule ^.*$ /index.html [L,R=301]

Wurde ein Link versehentlich mit Leerzeichen veröffentlicht worden, wird der Nutzer diesen nicht öffnen können. Hier kann eine Weiterleitung von der fehlerhaften Domain sinnvoll sein.

Lösung:

Redirect 301 "/pfadmit leerzeichen.php" https://www.beispieldomain.de/pfadohneleerzeichen.php

Alternative, falls die obere variante auf dem Server nicht funktioniert:

Redirect 301 /pfadmit[\s]leerzeichen.php http://www.beispieldomain.de/pfadohneleerzeichen.php

Möchte man eine Weiterleitung vornehmen, bei der das Elternelement weitergeleitet werden soll, das Kindelement jedoch nicht, ist folgender .htaccess Eintrag nötig:

Redirect 301 /products/?$ http://www.domain.com/404/

Bei einigen Hostern ist das erhöhen des Memory Limits nur per php.ini Datei möglich. Hierzu erstellt man eine entsprechende leere Datei mit einem Editor und fügt zusätzlich noch die nachfolgende Zeile ein.

memory_limit = 256M

In WordPress muss sich diese Datei z.B. im wp-admin Ordner befinden, damit diese aktiv genutzt werden kann.

Auf der Webseite wurde erfolgreich ein SSL Zertifikat hinterlegt, doch die Webseite wird immer noch als unsicher betrachtet. Hier liegt es meist an Mixed Content, also eine Vermischung von sicheren und unsicheren Inhalten.

Lösung: Generell gilt es alle bestehenden Links von http auf https zu ändern. Lässt sich die fehlerhafte Verlinkung aber nicht finden, hilft die Verwendung von Google Chrome. Hier klickt man auf der entsprechenden Seite auf die rechte Maustaste, anschließend auf Untersuchen -> Console. Hier wird dann das genaue Element angezeigt und eine schnelle Fehlerbehebung ist möglich.

Gelegentlich kann es wichtig sein, Datenbank-Einträge direkt in dieser zu bearbeiten. Um einen bestimmten Datensatz zu finden und diesen zu ersetzen, ist folgende SQL-Anweisung möglich:

UPDATE tabellenName SET tabellenWert = replace(tabellenWert,"alterEintrag","neuerEintrag");

Hin und wieder tauchen Datenbank Dateien auf, die mehrere hundert MB groß sein können. Versucht man diese durch phpMyAdmin hochzuladen, kann es aufgrund der Dateigrößen-Beschränkung zu Problemen kommen.

Lösung: Mit dem Tool „SQL Dump Splitter“ kann die Datenbank, erst einmal in kleine handliche Dateien aufgeteilt werden. Durch das php Skript „Big Dump“ was man in einem eigenen Verzeichnis auf dem Server ablegt und darin die aufgeteilten Datenbanken hoch lädt, funktioniert das Importieren schnell und unkompliziert.

Bei Umzügen von Datenbanken auf andere Server ist in Einzelfällen mit Fehlern beim Importieren der Datenbank zu rechnen. Dies liegt oft an veraltete Software, hier ist dann die MySQL Version auf einem älteren Stand.

Lösung: In einigen Fällen kann die zu importierenden SQL-Datei mit einem Editor bearbeitet werden.

ENGINE=InnoDB DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_unicode_520_ci;
ersetzen mit:
ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_general_ci;
utf8mb4_unicode_520_ci 
ersetzen mit:
utf8mb4_unicode_ci

In einigen Fällen reicht es auch die MySQL Version zu erhöhen.

Registriert man eine Domain bei IONOS und verwendet diese nicht direkt, wird standartmäßig SEDO Werbung auf der entsprechenden leeren Domain ausgegeben. Dieses lässt sich zum Glück beheben.

Lösung: Hierzu klickt man im IONOS Kundenbackend auf „Domains und SSL“ > unten rechts auf „Schnellzugriff“, anschließend den Punkt „Domain Parking“ auswählen. Dort lässt sich die Werbung ausschalten.

Tools & Links

Wer schnell eine kleine CSS Animation erstellen möchte, ohne direkt alle Befehle griffbereit zu haben, kann sich einem Online Tool wie cssanimate.com behelfen.

Postet man einen Beitrag samt Webseiten Link, generiert Facebook aus den vorhandenen Link eine Auswahl an möglichen Vorschaubildern. Ist man mit der Auswahl nicht zufrieden bzw. wird nicht das richtige Bild angezeigt, gibt es zwei Lösungswege.

1. Wurde das Bild auf der Webseite schon korrigiert, aber Facebook hat dieses noch nicht übernommen, lässt sich über den Facebook Debugger, der entsprechende Webseiten Link neu einlesen.
Gibt es eventuell danach immer noch Probleme, lässt sich z.B. über das WordPress Plugin Yoast, ein Facebook Bild, für die entsprechende Unterseite bereits im Vorfeld festlegen.

2. Wurde der Beitrag bereits gepostet, muss dieser nicht unbedingt gelöscht werden. Klickt man den Beitrag oben rechts über den Pfeil an, gibt es hier die Möglichkeit, das Vorschaubild ebenfalls neu einlesen zu lassen.

Analog zu Facebook, kann auch bei linkedin.com ein zuvor eingelesener Link, wenn dieser Inhaltlich geändert wurde, neu eingelesen werden.

Dies geschieht unter: https://www.linkedin.com/post-inspector/

Mit dem kostenlos Programm MailStore lassen sich E-Mails und Accounts problemlos sichern, unabhängig welche Anbieter oder Client man verwendet.

Neben der generellen Sicherung ist dies ein einfach Art, vor einem Domain Umzug eine Sicherung seiner bisherigen E-Mails vorzunehmen, vor allem wenn diese per IMAP Protokoll abgerufen wurden und nach einem Umzug automatisch gelöscht werden würden.

Aufgrund verschiedenster E-Mail Clients und Anbieter ist das Gestalten einer E-Mail recht umständlich. Wenn Logos als Signatur mitgesendet werden sollen, kommen diese beim Empfänger schon einmal anders an als gedacht.

Eine Möglichkeit ist das Logo im Inline Embedding Verfahren mit der E-Mail zu verschmelzen. Dafür muss im Vorfeld das Bild bzw. Logo in das Format Base64 umgewandelt werden.

Unter base64-image.de ist dies kostenlos möglich. Anschließend wird das Bild als <img> Tag eingefügt und wahlweise dann auch noch mit einem <a> Tag versehen, um das Bild mit der Webseite zu verlinken.

WordPress bietet für fast alle Bedürfnisse unterschiedlichste Plugins, kostenlose sowie kostenpflichtige und diese lassen sich in der Regel auch schnell finden. Nachfolgend eine Liste von hilfreichen Plugins, für die ich doch einmal länger suchen musste, dafür genau dies erfüllten, wofür sie benötigt wurden. Zudem sind alle aufgezählten Plugins kostenlos.

Phoenix Media Rename – Hiermit lassen sich nachträglich Dateinamen in der Mediathek anpassen. Auch bereits eingefügte Dateien, können automatisch umbenannt werden ohne diese neu verlinken zu müssen.

Enable Media Replace – geht noch einen Schritt weiter, hier lassen sich Medien komplett auswechseln ohne das diese neu im Content eingefügt werden müssen.

Media Cleaner – Clean & Optimize Space – löscht nicht mehr verwendete Bilder aus der Mediathek und dem Server. Zuvor jedoch unbedingt eine Sicherung vornehmen.

Visual Portfolio – Hilfreiche Portfolio Galerie mit allen Funktionen und Filtern, die man sonst nur bei kostenpflichtigen Plugins findet.

Fatal Error Notify – Sendet dem Seitenbetreiber eine E-Mail zu, wenn die Webseite z.B. auf Grund eines schweren Fehlers nicht mehr dargestellt werden kann.

Contact Form 7 – Repeatable Fields – Ist ein Addon für das Contact Form 7 Plugin und sorgt anhand von Gruppen dafür, das gewünschte Felder beliebig oft wiederholt werden können.

GamiPress – Belohnungssystem für Nutzer.

Image Source Control – Plugin zum Hinterlegen von Copyright-Rechten eines Bildes direkt am bzw. im Bild.

Shortcode Cleaner Lite – Säubert den Quellcode, z.B. wenn der Editor gewechselt wurde und dadurch noch alter Code / Shortcodes auf der Webseite hinterlegt sind.

Canvas Image Resize – Erlaubt das definieren der maximalen Höhe und Breite eines Bildes, bevor dieses hochgeladen wird.

Filter Posts By Category – Schöne Filterfunktion mit Ajax die auch mit Elementor und Custom Post Types zusammenarbeitet.

Make Column Clickable Elementor – Wie der Name schon verrät, können hiermit Spalteninhalte auch klickbar gemacht werden.

Tuxedo Big File Uploads – Sorgt dafür das auch sehr große Dateien in der Mediathek hochgeladen werden können, auch wenn es hier eigentlich eine Uploadgrenze gibt.

Products Suggestions for WooCommerce – Bietet ausgewählte Artikel dem Nutzer zum Beispiel auf der Warenkorb oder auf der Kassenseite an.

Go Live Update Urls – Gute Alternative zu Better Search Replace, um alle URLs einer Webseite, samt Datenbank zu ändern.

Complianz – GDPR/CCPA Cookie Consent – Selbst in der kostenlosen Version eine sehr gute Alternative zu Borland Cookies und Co.

CF7 Form Submission Limit – Beeinflusst wie oft ein Formular gesendet werden kann.

curtains.js – Einfaches WebGL-Tool zum Animieren von Bildern und Videos.

turn.js – ist eine JavaScript-Bibliothek, die Inhalte wie ein echtes Buch oder Magazin aussehen lässt und dabei alle Vorteile von HTML5 nutzt.

Dank Video DownloadHelper lässt sich so gut wie jedes Video herunterladen. Die Erweiterung wird in Firefox installiert, zusätzlich muß noch die Video DownloadHelper Companion App 1.2.4 , als zusätzliches Programm ebenfalls installiert werden.

So einfach geht es:

1.) Strg + H (Ersetzen)
2.) Wählen Extended aus SearchMode
3.) In Finden: \r\n\r\n einsetzen
4.) In Ersetzen mit: \r\n einfügen

Suche nach

^([^,]*,[^,]*),.*$

und ersetze dies mit

\1

Vor jeder Zeile einfügen
Suche nach

^

und ersetze dies mit

dem gewünschten Text


Nach jeder Zeile einfügen

Suche nach

$

und ersetze dies mit

dem gewünschten Text

Die Überschrift passt vielleicht nicht ganz, denn Jekyll ist ein Generator für statische Webseiten basierend auf Ruby. Dabei arbeitet es mit Plain-Text HTML und kommt ohne Datenbank aus, so dass generierte Seiten vom Webserver sehr schnell bereitgestellt werden können.

Hier werden neue Inhalte per Editor hinterlegt oder per GitHub. Wir erhalten so eine schnelle statische Webseite, jedoch keine durchdesignte Webseite mit einfacher Benutzeroberfläche.

Sofern man nicht über GitHub arbeiten möchte, sondern lokal auf dem Rechner, um anschließend die Dateien auf dem Webserver zu übertragen, muß die Grundlage geschaffen werden.

Installiere Ruby ( www.ruby-lang.org )

Installiere Ruby Installer ( rubyinstaller.org )

Anschließend sind folgende Befehle in der Konsole nötig:

gem install jekyll bundler

und danach

bundle exec jekyll serve

Die Webseite sollte dann unter 127.0.0.1:4000 im Browser zu finden sein.

Problemlösung
Fehler:
Could not find gem ‚jekyll-sitemap‘ in any of the gem sources listed in your Gemfile or available on this machine.
Lösung:

$ sudo gem install jekyll-sitemap
$ sudo gem install pygments.rb
$ gem install bundler
$ bundle install


Fehler:
Unable to load the EventMachine C extension; To use the pure-ruby reactor, require ‚em/pure_ruby‘
Lösung:

gem uninstall eventmachine
gem install eventmachine --platform ruby

 

Lösung:

IMGonline.com.ua – Überträgt Metadata des Originalbildes auf ein anderes.

Rauschen entfernen – Hilfreiche Infos wie man das Bildrauschen nachträglich etwas reduzieren kann.

Omatsuri.app – Webdesigner Tools

Mobilefish.com – vcards Generator

Wenn es mal vorkommt, dass sich eine Datei oder Ordner gar nicht löschen lassen und alle Möglichkeiten ausgeschöpft sind, dann hilft eventuell LockHunter.

Manche Addons, etc. benötigen die englische Version des jeweiligen Adobe Programmes, damit diese fehlerfrei funktionieren. In den Vorsteinstellungen gibt es die Möglichkeit die Programm-Sprache zu ändern, sofern diese Version installiert wurde.

Alternative: Im Photoshop-Verzeichnis unter „Locales > de_DE > Support Files“ findet man die Übersetzungsdatei „tw10428.dat“. Wenn man diese Datei vorübergehend aus dem Ordner entfernt und woanders sichert (um diese später wieder zu verwenden), anschließend das Programm neu startet, wird die englische Version geöffnet.

Um Daten auf einer Festplatte sicher löschen zu können reicht eine normale Formatierung meist nicht auf. Datenfragmete lassen sich danach oft wiederherstellen.

Besser dafür geeignet sind Programme wie Darik’s Boot and Nuke (kurz DBAN), das in der normalen Version auch kostenlos ist. Hier lässt sich einstellen, wie oft ein Datenträger überschrieben werden soll (je mehr Durchgänge, he länger dauert logischerweise der Vorgang).

Das Programm lässt sich also ISO Datei herunterladen, die dann auf einem bootfähigen Datenträger z.B. USB Stick aufgespielt wird. Sehr einfach ist dies mit dem kostenlosen Programm RUFUS möglich. Dieses erstellt nicht nur einen bootfähigen Datenträger, sondern bindet auch die entsprechende ISO Datei ein.

Floating Contact ButtonFloating Contact Button

Plugin

Auf dieser Seite kann die Funktion des Floating Contact Buttons im Live Betrieb betrachtet werden.

Dafür muss lediglich auf den Button in der rechten unteren Bildschirmecke geklickt werden. Bei diesem Beispiel wird ein Contact Form 7 Formular verwendet.

Bei Fragen oder Hilfe zu diesem Plugin, kann das Support Forum zum Plugin genutzt werden.

On this page you can see the function of the Floating Contact Button in live mode.

All you have to do is click on the button in the lower right corner of the screen. In this example a Contact Form 7 form is used.

If you have questions or need help with this plugin, you can use the Support Forum for the plugin.

Über michÜber mich

CMS ist mein Leben

Als Webentwickler in einer Agentur, liegt es in der Natur der Sache sich täglich mit Content Management Systemen (kurz CMS) zu beschäftigen. Hierbei werden alle Bereiche von Design, Umsetzung, Sicherheit und Problemlösung abgedeckt.

Da viele Fragen bei Schulungen immer wieder auftreten, möchte ich auf dieser Webseite, die passenden Lösungen anbieten. Kostenlose Video Tutorials zeigen Schritt für Schritt den Weg zum Ziel und dieses nicht nur oberflächlich, sondern im Detail. Der Schwerpunkt liegt hier auf dem CMS WordPress.

Also, viel Spaß.

  • 0%
    WORDPRESS
  • 0%
    WOOCOMMERCE (auch wenn im eigentlichen Sinne kein CMS)
  • 0%
    JOOMLA
  • 0%
    TYPO3
  • 0%
    DRUPAL

WordPress bietet neben vielen kostenlosen Plugins, eine extrem gute und motivierte Community. Hier bleiben die wenigsten Fragen lange unbeantwortet. Wer Zeit und Lust hat sich hier zu engagieren, ist gerne gesehen und kann viele Bereiche näher kennenlernen.

Der auf dieser Webseite befindliche Kontakt Button (Floating Contact Button) ist von mir entwickelt worden und kann kostenlos als Plugin für die eigene Webseite genutzt werden.

Klicken Sie auf den unteren Button, um den Inhalt von de.wordpress.org zu laden.

Inhalt laden

Für andere Dinge muss auch mal Zeit sein, weshalb dann nicht einfach mal einen guten Film schauen. Nachfolgend meine TOP 100 der Filme, die man gesehen haben sollte. Die Sortierung ist zufällig.

Der Blade Runner (1982) 117min | Action, Sci-Fi, Thriller | 14 October 1982 (West Germany) Summary: A blade runner must pursue and terminate four replicants who stole a ship in space, and have returned to Earth to find their creator.
Countries: USALanguages: English, German, Cantonese, Japanese, Hungarian, Arabic

Zwei glorreiche Halunken (1966) 161min | Western | 15 December 1967 (West Germany) Summary: A bounty hunting scam joins two men in an uneasy alliance against a third in a race to find a fortune in gold buried in a remote cemetery.
Countries: Italy, Spain, West Germany, USALanguages: Italian

Ghostbusters - Die Geisterjäger (1984) 105min | Action, Comedy, Fantasy | 25 January 1985 (West Germany) Summary: Three former parapsychology professors set up shop as a unique ghost removal service.
Countries: USALanguages: English

Indiana Jones und der letzte Kreuzzug (1989) 127min | Action, Adventure | 14 September 1989 (West Germany) Summary: In 1938, after his father Professor Henry Jones, Sr. goes missing while pursuing the Holy Grail, Professor Henry "Indiana" Jones, Jr. finds himself up against Adolf Hitler's Nazis again to stop them from obtaining its powers.
Countries: USA, UKLanguages: English, German, Greek, Arabic

Das Schweigen der Lämmer (1991) 118min | Crime, Drama, Thriller | 11 April 1991 (Germany) Summary: A young F.B.I. cadet must receive the help of an incarcerated and manipulative cannibal killer to help catch another serial killer, a madman who skins his victims.
Countries: USALanguages: English, Latin

Alien - Das unheimliche Wesen aus einer fremden Welt (1979) 117min | Horror, Sci-Fi | 25 October 1979 (West Germany) Summary: After a space merchant vessel receives an unknown transmission as a distress call, one of the crew is attacked by a mysterious life form and they soon realize that its life cycle has merely begun.
Countries: UK, USALanguages: English

Domino (2005) 127min | Action, Biography, Crime | 29 December 2005 (Germany) Summary: A recounting of Domino Harvey's life story. The daughter of actor Laurence Harvey turned away from her career as a Ford model to become a bounty hunter.
Countries: France, USA, UKLanguages: English

Mann unter Feuer (2004) 146min | Action, Crime, Drama | 30 September 2004 (Germany) Summary: In Mexico City, a former CIA operative swears vengeance on those who committed an unspeakable act against the family he was hired to protect.
Countries: USA, UK, Mexico, SwitzerlandLanguages: English, Spanish

Die Unbestechlichen (1987) 119min | Crime, Drama, Thriller | 15 October 1987 (West Germany) Summary: During the era of Prohibition in the United States, Federal Agent Eliot Ness sets out to stop ruthless Chicago gangster Al Capone and, because of rampant corruption, assembles a small, hand-picked team to help him.
Countries: USALanguages: English

Die Unbestechlichen (1976) 138min | Biography, Drama, History | 30 September 1976 (West Germany) Summary: "The Washington Post" reporters Bob Woodward and Carl Bernstein uncover the details of the Watergate scandal that leads to President Richard Nixon's resignation.
Countries: USALanguages: English, Spanish

Die Verurteilten (1994) 142min | Drama | 9 March 1995 (Germany) Summary: Two imprisoned men bond over a number of years, finding solace and eventual redemption through acts of common decency.
Countries: USALanguages: English

Zurück in die Zukunft (1985) 116min | Adventure, Comedy, Sci-Fi | 3 October 1985 (West Germany) Summary: Marty McFly, a 17-year-old high school student, is accidentally sent thirty years into the past in a time-traveling DeLorean invented by his close friend, the eccentric scientist Doc Brown.
Countries: USALanguages: English

Der Pate (1972) 175min | Crime, Drama | 24 August 1972 (West Germany) Summary: An organized crime dynasty's aging patriarch transfers control of his clandestine empire to his reluctant son.
Countries: USALanguages: English, Italian, Latin

Apocalypse Now (1979) 147min | Drama, Mystery, War | 4 October 1979 (West Germany) Summary: A U.S. Army officer serving in Vietnam is tasked with assassinating a renegade Special Forces Colonel who sees himself as a god.
Countries: USALanguages: English, French, Vietnamese

James Bond 007: Casino Royale (2006) 144min | Action, Adventure, Thriller | 23 November 2006 (Germany) Summary: After earning 00 status and a licence to kill, Secret Agent James Bond sets out on his first mission as 007. Bond must defeat a private banker funding terrorists in a high-stakes game of poker at Casino Royale, Montenegro.
Countries: UK, Czech Republic, USA, Germany, BahamasLanguages: English, Serbian, German, Italian, French

Rambo (1982) 93min | Action, Adventure | 6 January 1983 (West Germany) Summary: A veteran Green Beret is forced by a cruel Sheriff and his deputies to flee into the mountains and wage an escalating one-man war against his pursuers.
Countries: USALanguages: English

The Dark Knight (2008) 152min | Action, Crime, Drama | 21 August 2008 (Germany) Summary: When the menace known as the Joker wreaks havoc and chaos on the people of Gotham, Batman must accept one of the greatest psychological and physical tests of his ability to fight injustice.
Countries: USA, UKLanguages: English, Mandarin

Mad Max - Jenseits der Donnerkuppel (1985) 107min | Action, Adventure, Sci-Fi | 26 September 1985 (West Germany) Summary: After being exiled from the most advanced town in post apocalyptic Australia, a drifter travels with a group of abandoned children to rebel against the town's queen.
Countries: AustraliaLanguages: English

Matrix (1999) 136min | Action, Sci-Fi | 17 June 1999 (Germany) Summary: When a beautiful stranger leads computer hacker Neo to a forbidding underworld, he discovers the shocking truth--the life he knows is the elaborate deception of an evil cyber-intelligence.
Countries: USALanguages: English

2001: Odyssee im Weltraum (1968) 149min | Adventure, Sci-Fi | 11 September 1968 (West Germany) Summary: After discovering a mysterious artifact buried beneath the Lunar surface, mankind sets off on a quest to find its origins with help from intelligent supercomputer H.A.L. 9000.
Countries: UK, USALanguages: English, Russian

The Big Lebowski (1998) 117min | Comedy, Crime, Sport | 19 March 1998 (Germany) Summary: Jeff "The Dude" Lebowski, mistaken for a millionaire of the same name, seeks restitution for his ruined rug and enlists his bowling buddies to help get it.
Countries: USA, UKLanguages: English, German, Hebrew, Spanish

Angst und Schrecken in Las Vegas (1998) 118min | Adventure, Comedy, Drama | 24 September 1998 (Germany) Summary: An oddball journalist and his psychopathic lawyer travel to Las Vegas for a series of psychedelic escapades.
Countries: USALanguages: English

Auf der Flucht (1993) 130min | Action, Crime, Drama | 16 September 1993 (Germany) Summary: Dr. Richard Kimble, unjustly accused of murdering his wife, must find the real killer while being the target of a nationwide manhunt led by a seasoned U.S. Marshal.
Countries: USALanguages: English, Polish, Spanish

Sicario (2015) 121min | Action, Crime, Drama | 1 October 2015 (Germany) Summary: An idealistic FBI agent is enlisted by a government task force to aid in the escalating war against drugs at the border area between the U.S. and Mexico.
Countries: USA, Mexico, Hong KongLanguages: English, Spanish

Angel Heart (1987) 113min | Horror, Mystery, Thriller | 3 September 1987 (West Germany) Summary: A private investigator is hired by a man who calls himself Louis Cyphre to track down a singer named Johnny Favorite. But the investigation takes an unexpected and somber turn.
Countries: UK, Canada, USALanguages: English, French

Big Fish - Der Zauber, der ein Leben zur Legende macht (2003) 125min | Adventure, Drama, Fantasy | 8 April 2004 (Germany) Summary: A frustrated son tries to determine the fact from fiction in his dying father's life.
Countries: USALanguages: English, Cantonese

Heat (1995) 170min | Crime, Drama, Thriller | 29 February 1996 (Germany) Summary: A group of professional bank robbers start to feel the heat from police when they unknowingly leave a clue at their latest heist.
Countries: USALanguages: English, Spanish

Insomnia - Schlaflos (2002) 118min | Drama, Mystery, Thriller | 10 October 2002 (Germany) Summary: Two Los Angeles homicide detectives are dispatched to a northern town where the sun doesn't set to investigate the methodical murder of a local teen.
Countries: USA, UKLanguages: English

Scarface (1983) 170min | Crime, Drama | 9 March 1984 (West Germany) Summary: In 1980 Miami, a determined Cuban immigrant takes over a drug cartel and succumbs to greed.
Countries: USALanguages: English, Spanish

Dirty Harry (1971) 102min | Action, Crime, Thriller | 10 March 1972 (West Germany) Summary: When a madman calling himself "the Scorpio Killer" menaces the city, tough-as-nails San Francisco Police Inspector "Dirty" Harry Callahan is assigned to track down and ferret out the crazed psychopath.
Countries: USALanguages: English

Dark City (1998) 100min | Mystery, Sci-Fi, Thriller | 27 August 1998 (Germany) Summary: A man struggles with memories of his past, which include a wife he cannot remember and a nightmarish world no one else ever seems to wake up from.
Countries: AustraliaLanguages: English

Bullitt (1968) 114min | Action, Crime, Thriller | 3 January 1969 (West Germany) Summary: An all guts, no glory San Francisco cop becomes determined to find the underworld kingpin that killed the witness in his protection.
Countries: USALanguages: English

Out of Time - Sein Gegner ist die Zeit (2003) 105min | Crime, Drama, Mystery | 11 March 2004 (Germany) Summary: A Florida police chief must solve a vicious double homicide before he himself falls under suspicion.
Countries: USALanguages: English

Déjà Vu - Wettlauf gegen die Zeit (2006) 126min | Action, Crime, Sci-Fi | 22 December 2006 (Germany) Summary: After a ferry is bombed in New Orleans, an A.T.F. agent joins a unique investigation using experimental surveillance technology to find the bomber, but soon finds himself becoming obsessed with one of the victims.
Countries: USA, UKLanguages: English

Casino (1995) 178min | Crime, Drama | 14 March 1996 (Germany) Summary: A tale of greed, deception, money, power, and murder occur between two best friends: a mafia enforcer and a casino executive compete against each other over a gambling empire, and over a fast-living and fast-loving socialite.
Countries: USA, FranceLanguages: English

Wehrlos - Die Tochter des Generals (1999) 116min | Crime, Drama, Mystery | 18 November 1999 (Germany) Summary: When the daughter of a well-known and well-respected base commander is murdered, an undercover detective is summoned to look into the matter and finds a slew of cover-ups at West Point.
Countries: USA, GermanyLanguages: English

Pakt der Wölfe (2001) 142min | Action, Adventure, Drama | 14 February 2002 (Germany) Summary: In 18th-century France, the Chevalier de Fronsac and his Native American friend Mani are sent to the Gevaudan province at the king's behest to investigate the killings of hundreds by a mysterious beast.
Countries: FranceLanguages: French, German, Italian

Zombieland (2009) 88min | Adventure, Comedy, Fantasy | 10 December 2009 (Germany) Summary: A shy student trying to reach his family in Ohio, a gun-toting tough guy trying to find the last Twinkie, and a pair of sisters trying to get to an amusement park join forces to travel across a zombie-filled America.
Countries: USALanguages: English, Spanish, French

Fight Club (1999) 139min | Drama | 11 November 1999 (Germany) Summary: An insomniac office worker and a devil-may-care soapmaker form an underground fight club that evolves into something much, much more.
Countries: USA, Germany, ItalyLanguages: English

Platoon (1986) 120min | Drama, War | 30 April 1987 (West Germany) Summary: Chris Taylor, a neophyte recruit in Vietnam, finds himself caught in a battle of wills between two sergeants, one good and the other evil. A shrewd examination of the brutality of war and the duality of man in conflict.
Countries: USA, UKLanguages: English, Vietnamese

Im Jahr des Drachen (1985) 134min | Action, Crime, Drama | 24 October 1985 (West Germany) Summary: A police detective cracks down on organized crime in Chinatown after the murders of Triad and Mafia leaders.
Countries: USALanguages: Mandarin, English, Cantonese, Vietnamese, Polish

An jedem verdammten Sonntag (1999) 162min | Drama, Sport | 9 March 2000 (Germany) Summary: A behind-the-scenes look at the life-and-death struggles of modern-day gladiators and those who lead them.
Countries: USALanguages: English

Stirb langsam (1988) 132min | Action, Thriller | 10 November 1988 (West Germany) Summary: An NYPD officer tries to save his wife and several others taken hostage by German terrorists during a Christmas party at the Nakatomi Plaza in Los Angeles.
Countries: USALanguages: English, German, Italian, Japanese

L.A. Confidential (1997) 138min | Crime, Drama, Mystery | 4 December 1997 (Germany) Summary: As corruption grows in 1950s Los Angeles, three policemen - one strait-laced, one brutal, and one sleazy - investigate a series of murders with their own brand of justice.
Countries: USALanguages: English

In the Line of Fire: Die zweite Chance (1993) 128min | Action, Crime, Drama | 28 October 1993 (Germany) Summary: Secret Service agent Frank Horrigan (Clint Eastwood) couldn't save Kennedy, but he's determined not to let a clever assassin take out this president.
Countries: USALanguages: English

Sieben (1995) 127min | Crime, Drama, Mystery | 23 November 1995 (Germany) Summary: Two detectives, a rookie and a veteran, hunt a serial killer who uses the seven deadly sins as his motives.
Countries: USALanguages: English

Gladiator (2000) 155min | Action, Adventure, Drama | 25 May 2000 (Germany) Summary: A former Roman General sets out to exact vengeance against the corrupt emperor who murdered his family and sent him into slavery.
Countries: USA, UK, Malta, MoroccoLanguages: English

Black Rain (1989) 125min | Action, Crime, Thriller | 14 December 1989 (West Germany) Summary: Two NYC cops arrest a Yakuza member and must escort him when he's extradited to Japan.
Countries: USALanguages: English, Japanese

Spy Game - Der finale Countdown (2001) 126min | Action, Crime, Thriller | 14 March 2002 (Germany) Summary: Retiring CIA agent Nathan Muir recalls his training of Tom Bishop while working against agency politics to free him from his Chinese captors.
Countries: USA, Germany, Japan, France, UKLanguages: English, German, Arabic, French, Cantonese

Krieg der Sterne (1977) 121min | Action, Adventure, Fantasy | 9 February 1978 (West Germany) Summary: Luke Skywalker joins forces with a Jedi Knight, a cocky pilot, a Wookiee and two droids to save the galaxy from the Empire's world-destroying battle station, while also attempting to rescue Princess Leia from the mysterious Darth Vader.
Countries: USA, UKLanguages: English

Braveheart (1995) 178min | Biography, Drama, History | 5 October 1995 (Germany) Summary: Scottish warrior William Wallace leads his countrymen in a rebellion to free his homeland from the tyranny of King Edward I of England.
Countries: USALanguages: English, French, Latin, Scottish Gaelic, Italian

Sin City (2005) 124min | Crime, Thriller | 11 August 2005 (Germany) Summary: A movie that explores the dark and miserable town, Basin City, tells the story of three different people, all caught up in violent corruption.
Countries: USALanguages: English

Mississippi Burning - Die Wurzel des Hasses (1988) 128min | Crime, Drama, History | 6 April 1989 (West Germany) Summary: Two F.B.I. Agents with wildly different styles arrive in Mississippi to investigate the disappearance of some civil rights activists.
Countries: USALanguages: English

Chinatown (1974) 130min | Drama, Mystery, Thriller | 19 December 1974 (West Germany) Summary: A private detective hired to expose an adulterer finds himself caught up in a web of deceit, corruption, and murder.
Countries: USALanguages: English, Cantonese, Spanish

Der weiße Hai (1975) 124min | Adventure, Thriller | 18 December 1975 (West Germany) Summary: When a killer shark unleashes chaos on a beach community, it's up to a local sheriff, a marine biologist, and an old seafarer to hunt the beast down.
Countries: USALanguages: English

Eine Frage der Ehre (1992) 138min | Drama, Thriller | 14 January 1993 (Germany) Summary: Military lawyer Lieutenant Daniel Kaffee defends Marines accused of murder. They contend they were acting under orders.
Countries: USALanguages: English, French

96 Hours (2008) 90min | Action, Thriller | 19 February 2009 (Germany) Summary: A retired CIA agent travels across Europe and relies on his old skills to save his estranged daughter, who has been kidnapped while on a trip to Paris.
Countries: France, USA, UKLanguages: English, French, Albanian, Arabic

Shutter Island (2010) 138min | Mystery, Thriller | 25 February 2010 (Germany) Summary: In 1954, a U.S. Marshal investigates the disappearance of a murderer who escaped from a hospital for the criminally insane.
Countries: USALanguages: English, German

Bad Boys II (2003) 147min | Action, Comedy, Crime | 9 October 2003 (Germany) Summary: Two loose-cannon narcotics cops investigate the flow of Ecstasy into Florida from a Cuban drug cartel.
Countries: USALanguages: English, Spanish, Russian

Lethal Weapon - Zwei stahlharte Profis (1987) 109min | Action, Crime, Thriller | 10 September 1987 (West Germany) Summary: Two newly paired cops who are complete opposites must put aside their differences in order to catch a gang of drug smugglers.
Countries: USALanguages: English

Drive (2011) 100min | Crime, Drama | 26 January 2012 (Germany) Summary: A mysterious Hollywood stuntman and mechanic moonlights as a getaway driver and finds himself in trouble when he helps out his neighbor.
Countries: USALanguages: English, Spanish

Der schmale Grat (1998) 170min | Drama, War | 25 February 1999 (Germany) Summary: Adaptation of James Jones' autobiographical 1962 novel, focusing on the conflict at Guadalcanal during the second World War.
Countries: USA, AustraliaLanguages: English, Tok Pisin, Japanese, Greek

JFK: Tatort Dallas (1991) 189min | Drama, History, Thriller | 23 January 1992 (Germany) Summary: New Orleans District Attorney Jim Garrison discovers there's more to the Kennedy assassination than the official story.
Countries: USA, FranceLanguages: English, Spanish

The Game - Das Geschenk seines Lebens (1997) 129min | Action, Drama, Mystery | 20 November 1997 (Germany) Summary: After a wealthy banker is given an opportunity to participate in a mysterious game, his life is turned upside down when he becomes unable to distinguish between the game and reality.
Countries: USALanguages: English, Cantonese, German

Walk the Line (2005) 136min | Biography, Drama, Music | 2 February 2006 (Germany) Summary: A chronicle of country music legend Johnny Cash's life, from his early days on an Arkansas cotton farm to his rise to fame with Sun Records in Memphis, where he recorded alongside Elvis Presley, Jerry Lee Lewis, and Carl Perkins.
Countries: USA, GermanyLanguages: English, Russian

Aus Mangel an Beweisen (1990) 127min | Mystery, Thriller | 13 December 1990 (Germany) Summary: As a lawyer investigates the murder of a colleague, he finds himself more connected to the crime than anyone else.
Countries: USALanguages: English

Die unglaubliche Reise in einem verrückten Flugzeug (1980) 88min | Comedy | 14 November 1980 (West Germany) Summary: A man afraid to fly must ensure that a plane lands safely after the pilots become sick.
Countries: USALanguages: English

Blues Brothers (1980) 133min | Action, Adventure, Comedy | 16 October 1980 (West Germany) Summary: Jake Blues, just released from prison, puts together his old band to save the Catholic home where he and his brother Elwood were raised.
Countries: USALanguages: English

Im Auftrag des Teufels (1997) 144min | Drama, Mystery, Thriller | 22 January 1998 (Germany) Summary: An exceptionally adept Florida lawyer is offered a job at a high-end New York City law firm with a high-end boss - the biggest opportunity of his career to date.
Countries: USA, GermanyLanguages: English, German, Italian, Mandarin, Spanish

Road to Perdition (2002) 117min | Crime, Drama, Thriller | 5 September 2002 (Germany) Summary: A mob enforcer's son witnesses a murder, forcing him and his father to take to the road, and his father down a path of redemption and revenge.
Countries: USALanguages: English

Der Name der Rose (1986) 130min | Crime, Drama, Mystery | 16 October 1986 (West Germany) Summary: An intellectually nonconformist friar investigates a series of mysterious deaths in an isolated abbey.
Countries: West Germany, Italy, FranceLanguages: Italian, English, Latin, German

Interview mit einem Vampir (1994) 123min | Drama, Horror | 1 December 1994 (Germany) Summary: A vampire tells his epic life story: love, betrayal, loneliness, and hunger.
Countries: USALanguages: English, French

Zwei Himmelhunde auf dem Weg zur Hölle (1972) 120min | Action, Adventure, Comedy | 6 March 1973 (West Germany) Summary: A bush-flying duo crash-land in the heart of the Peruvian jungle where an unscrupulous speculator controls a precious emerald source and an entire mining community. Can they right the wrongs, and in the process, manage to make a profit?
Countries: ItalyLanguages: Italian

Die rechte und die linke Hand des Teufels (1970) 115min | Comedy, Western | 2 March 1971 (West Germany) Summary: A lazy, unorthodox gunfighter and his portly, horse-thieving brother defend a Mormon settlement from a land-grabbing Major, a Mexican bandit, and their henchmen.
Countries: ItalyLanguages: Italian, English, Spanish

Last Boy Scout - Das Ziel ist Überleben (1991) 105min | Action, Comedy, Crime | 12 March 1992 (Germany) Summary: A private detective's protected female witness is murdered, prompting him and the victim's boyfriend to investigate the crime that leads to a corrupt politician and a crooked football team owner.
Countries: USALanguages: English

Lost in Translation: Zwischen den Welten (2003) 102min | Comedy, Drama | 8 January 2004 (Germany) Summary: A faded movie star and a neglected young woman form an unlikely bond after crossing paths in Tokyo.
Countries: USA, JapanLanguages: English, Japanese, German, French

Kevin - Allein zu Haus (1990) 103min | Comedy, Family | 17 January 1991 (Germany) Summary: An eight-year-old troublemaker must protect his house from a pair of burglars when he is accidentally left home alone by his family during Christmas vacation.
Countries: USALanguages: English

Wyatt Earp - Das Leben einer Legende (1994) 191min | Adventure, Biography, Crime | 1 September 1994 (Germany) Summary: From Wichita to Dodge City to the O.K. Corral in Tombstone, a man becomes a myth in this thrilling journey of romance, adventure and desperate, heroic action.
Countries: USALanguages: English, Spanish

Zodiac: Die Spur des Killers (2007) 157min | Crime, Drama, Mystery | 31 May 2007 (Germany) Summary: In the late 1960s/early 1970s, a San Francisco cartoonist becomes an amateur detective obsessed with tracking down the Zodiac Killer, an unidentified individual who terrorizes Northern California with a killing spree.
Countries: USALanguages: English

Per Anhalter durch die Galaxis (2005) 109min | Adventure, Comedy, Sci-Fi | 9 June 2005 (Germany) Summary: Mere seconds before the Earth is to be demolished by an alien construction crew, journeyman Arthur Dent is swept off the planet by his friend Ford Prefect, a researcher penning a new edition of "The Hitchhiker's Guide to the Galaxy."
Countries: UK, USALanguages: English

Shining (1980) 146min | Drama, Horror | 16 October 1980 (West Germany) Summary: A family heads to an isolated hotel for the winter where a sinister presence influences the father into violence, while his psychic son sees horrific forebodings from both past and future.
Countries: UK, USALanguages: English

Forrest Gump (1994) 142min | Drama, Romance | 13 October 1994 (Germany) Summary: The presidencies of Kennedy and Johnson, the events of Vietnam, Watergate and other historical events unfold through the perspective of an Alabama man with an IQ of 75, whose only desire is to be reunited with his childhood sweetheart.
Countries: USALanguages: English

Selma (2014) 128min | Biography, Drama, History | 19 February 2015 (Germany) Summary: A chronicle of Dr. Martin Luther King, Jr.'s campaign to secure equal voting rights via an epic march from Selma to Montgomery, Alabama, in 1965.
Countries: UK, USA, FranceLanguages: English

Terminator 2: Tag der Abrechnung (1991) 137min | Action, Sci-Fi | 24 October 1991 (Germany) Summary: A cyborg, identical to the one who failed to kill Sarah Connor, must now protect her teenage son, John Connor, from a more advanced and powerful cyborg.
Countries: USA, FranceLanguages: English, Spanish

Street Kings (2008) 109min | Action, Crime, Drama | 17 April 2008 (Germany) Summary: An undercover cop, disillusioned by the death of his wife, is implicated in the murder of an officer and must struggle to clear himself.
Countries: USALanguages: English, Turkish

Shaun of the Dead (2004) 99min | Comedy, Horror | 30 December 2004 (Germany) Summary: A man's uneventful life is disrupted by the zombie apocalypse.
Countries: UK, France, USALanguages: English

Unter Feuer (1983) 128min | Drama, War | 11 November 1983 (West Germany) Summary: Three journalists in a romantic triangle are involved in political intrigue during the last days of the corrupt Somozoa regime in Nicaragua before it falls to a popular revolution in 1979.
Countries: USA, MexicoLanguages: English

French Connection - Brennpunkt Brooklyn (1971) 104min | Action, Crime, Drama | 14 January 1972 (West Germany) Summary: A pair of NYC cops in the Narcotics Bureau stumble onto a drug smuggling job with a French connection.
Countries: USALanguages: English, French

Erbarmungslos (1992) 130min | Drama, Western | 24 September 1992 (Germany) Summary: Retired Old West gunslinger William Munny reluctantly takes on one last job, with the help of his old partner Ned Logan and a young man, The "Schofield Kid."
Countries: USALanguages: English

Flight (2012) 138min | Drama, Thriller | 24 January 2013 (Germany) Summary: An airline pilot saves almost all his passengers on his malfunctioning airliner which eventually crashed, but an investigation into the accident reveals something troubling.
Countries: USA, United Arab EmiratesLanguages: English

Frantic (1988) 120min | Crime, Drama, Mystery | 25 August 1988 (West Germany) Summary: In a hotel room in Paris, a doctor comes out of the shower and finds that his wife has disappeared. He soon finds himself caught up in a world of intrigue, espionage, gangsters, drugs and murder.
Countries: USA, FranceLanguages: English, French

Auf dem Highway ist die Hölle los (1981) 95min | Action, Comedy, Sport | 2 October 1981 (West Germany) Summary: A wide variety of eccentric competitors participate in a wild and illegal cross-country road race. However, the eccentric entrants will do anything to win the road race, including low-down, dirty tricks.
Countries: USA, Hong KongLanguages: English, Cantonese, Japanese, Arabic

GoodFellas - Drei Jahrzehnte in der Mafia (1990) 146min | Biography, Crime, Drama | 11 October 1990 (Germany) Summary: The story of Henry Hill and his life in the mob, covering his relationship with his wife Karen Hill and his mob partners Jimmy Conway and Tommy DeVito in the Italian-American crime syndicate.
Countries: USALanguages: English, Italian

Das Boot (1981) 149min | Adventure, Drama, Thriller | 17 September 1981 (West Germany) Summary: The claustrophobic world of a WWII German U-boat; boredom, filth and sheer terror.
Countries: West GermanyLanguages: German

Der Untergang (2004) 156min | Biography, Drama, History | 16 September 2004 (Germany) Summary: Traudl Junge, the final secretary for Adolf Hitler, tells of the Nazi dictator's final days in his Berlin bunker at the end of WWII.
Countries: Germany, Austria, ItalyLanguages: German, Russian, French, English

Schtonk (1992) 115min | Comedy | 12 March 1992 (Germany) Summary: The slightly fictionalized story of an art forger, a journalist desperate for a big story, and the biggest press scandal in German history: the Hitler Diaries.
Countries: GermanyLanguages: German

Ödipussi (1988) 88min | Comedy, Romance | 10 March 1988 (East Germany) Summary: Gray-haired furniture retailer Paul Winkelmann still has his dinner cooked and his laundry done by his mother. He spends his evenings playing Scrabble with Mama's friends and discussing the... See full summary »
Countries: West GermanyLanguages: German, Italian

Memento (2000) 113min | Mystery, Thriller | 13 December 2001 (Germany) Summary: A man with short-term memory loss attempts to track down his wife's murderer.
Countries: USALanguages: English

Der unsichtbare Dritte (1959) 136min | Adventure, Mystery, Thriller | 18 December 1959 (West Germany) Summary: A New York City advertising executive goes on the run after being mistaken for a government agent by a group of foreign spies.
Countries: USALanguages: English, French

2010 - Das Jahr, in dem wir Kontakt aufnehmen (1984) 116min | Adventure, Mystery, Sci-Fi | 22 February 1985 (West Germany) Summary: A joint U.S.-Soviet expedition is sent to Jupiter to learn what happened to the Discovery, and H.A.L.
Countries: USALanguages: English, Russian

Wer sich die Mühe gemacht hat, alle Filmtitel bis hierhin anzusehen, findet auf der Webseite „the edit room floor“ einige spannende behind the scenes Fotos zu meist älteren Filmen.

X

    Nehmen über das nachfolgende Formular mit mir Kontakt auf.