Skip to content
Okt 9 14

Drupal: Exposed Filter Block zum richtigen Panel umleiten

by dennis

Kleiner Schnipsel, große Wirkung:

/*
 * implements hook_form_alter
*/
function THEME_form_alter(&$form, &$form_state, $form_id) {
    if ($form['#id'] == 'id-of-exposed-form') {
        $form['#action'] = '/new/path';
        return $form;
    }
}
Sep 3 14

Drupal: Datum als Mikroformat

by dennis
/**
 * Implements hook_theme_registry_alter().
 * @see: http://drupal.stackexchange.com/questions/76972/override-theme-for-date-field-output
 */
function hook_theme_registry_alter(&$theme_registry) {
 
    if (isset($theme_registry['date_display_range'])) {
        $theme_registry['date_display_range']['function'] = 'theme_custom_date_display_range';
    }
    if (isset($theme_registry['date_display_single'])) {
        $theme_registry['date_display_single']['function'] = 'theme_custom_date_display_single';
    }
}
 
/**
 * Returns HTML for a date element formatted as a range.
 * @see: http://drupal.stackexchange.com/questions/76972/override-theme-for-date-field-output
 */
function theme_custom_date_display_range($variables) {
 
    return t('!start-date - !end-date', array(
        '!start-date' => '<span class="date-display-start dtstart"' . drupal_attributes($variables['attributes_start']) . '>'.str_replace(' ','&nbsp;',$variables['date1']).'</span>',
        '!end-date' => '<span class="date-display-end dtend"' . drupal_attributes($variables['attributes_end']) . '>'.str_replace(' ','&nbsp;',$variables['date2'].$variables['timezone']).'</span>',
    ));
}
 
/**
 * Returns HTML for a date element formatted as single.
 */
function theme_custom_date_display_single($variables) {
 
    return '<span class="date-display-single dtstart"'.drupal_attributes($variables['attributes']) . '>'.str_replace(' ','&nbsp;',$variables['date'].$variables['timezone']).'</span>';
}
Jul 10 14

Eine E-Mail mit PHP versenden

by dennis

Der minimale Header:

$to = 'Vorname Nachname <e.mail@domain.de>';
$from = 'Vorname Nachname <do-not-reply@domain.de>';
$subject = 'Betreff der E-Mail';
 
/*
ACHTET DARAUF, dass die Datei als UTF-8 ohne BOM konvertiert ist.
In den Variablen $to, $from & $subject sind Umlaute und Sonderzeichen erlaubt!
Bitte NICHT, NIEMALS, NIE htmlspecialchars im Head der E-Mail verwenden!
Das führt zu einem NOTICE und die E-Mail geht nicht raus.
*/
 
$header  = "From: ".$from.PHP_EOL;
$header .= "Message-Id: <".sha1(microtime())."@domain.de>".PHP_EOL;
$header .= "X-Mailer: PHP ".phpversion().PHP_EOL;
$header .= "MIME-Version: 1.0".PHP_EOL;
$header .= "X-Priority: 3".PHP_EOL;
$header .= "Importance: Normal".PHP_EOL;
$header .= "Content-Type: text/plain; charset=UTF-8".PHP_EOL;
$header .= "Content-Transfer-Encoding: 8bit".PHP_EOL;

E-Mail Absenden dann immer so:

mail($to, $subject, $body, $header);
/*
Bitte nicht so: mail('', $subject, $body, $header);
... oder so: mail($to, $subject, $body, "From e.mail@domain.de\nX-Priority: 3\n usw …");
*/

Um zu prüfen ob alles gut gegangen ist…

if ($mail($to, $subject, $body, $header) === true) {} // liefert TRUE oder FALSE

Eine E-Mail mit Anhang versenden siehe hier

Jun 19 13

PHP: Screenshoot von einer Website erstellen

by dennis

Einen Screenshoot von einer Website zu erstellen ist mit PHP nativ nicht möglich. Abhilfe schafft die Library wkhtmltoimage:

http://code.google.com/p/wkhtmltopdf/

Da diese nur aus einer einzelnen Datei besteht genügt es, diese in ein Verzeichnis des Webservers zu legen. Der Aufruf erfolgt dann per PHP wie folgt:

$cmd = "/aboluter/pfad/wkhtmltoimage-i386 --load-error-handling abort --disable-javascript http://meine-website /absoluter/pfad/zieldatei.jpg";
shell_exec($cmd);

zu beachten ist dabei, dass es vom Webhoster aus erlaubt ist, Shell Befehle mit PHP zu starten. Beim Webhoster All-Inkl erreicht man dies z.B. in dem man eine .htaccess Datei mit folgendem Inhalt in den gleichen Ordner wie das PHP Script legt:

AddHandler php5-cgi .php

Noch ein Tipp:

Mit html2canvas kann das Problem auch auf der Client-Seite per Javascript gelöst werden:
http://html2canvas.hertzen.com/

Dez 13 12

Geschenktipp: nerddicted – club capable geekware

by dennis

So kurz vor Weihnachten ist man auch als Programmierer mal wieder damit beschäftigt, Weihnachtsgeschenke im Netz zu shoppen.
Für die Schaar an Nerds in meinem Freundeskreis habe ich in den letzten Jahren schon die üblichen Geekshops durchgeshoppt: dieses Jahr habe ich aber was neues gefunden – nerdige Shirts für Leute mit Stil, das passt doch!

Auf www.nerddicted.de ist das ganze zu sehen und die ersten drei Shirts schon zu haben.

Wer sich oder andere Nerds beschenken will, sollte dieses coole Projekt unterstützen!

Dez 13 12

CSS step navi

by dennis

>>> Demo (new window)

HTML:

        <ul id="steps">                
            <li class="first-child"><a class="active" href="#">first step</a></li>
            <li><a href="#">second step</a></li>
            <li><a href="#">[...]</a></li>
            <li class="last-child"><a href="#">last step</a></li>            
        </ul>

CSS:

        #steps { list-style:none; float:left; height:40px; margin:0; padding:0; }
        #steps li { margin-right:-17px; float:left; }
        #steps li a { background:url(img/steps.png) top left no-repeat; display:block; width:142px; height:32px; padding-top:8px; text-decoration:none; color:#eee; font-size:14px; text-align:center; }
        #steps li a:hover, #steps li a.active { background-position: 0px -50px; color:#252525 !important; }
        #steps li.first-child a { background-position: 0px -100px; }
        #steps li.first-child a:hover { background-position: 0px -150px; }
        #steps li.first-child a.active { background-position: 0px -150px; }
        #steps li.last-child { margin-right:0; }
        #steps li.last-child a { width:135px; background-position: 0px -200px; }
        #steps li.last-child a:hover { background-position: 0px -250px; }
        #steps li.last-child a.active {background-position: 0px -250px; }

Background-Image:
Sprite

Nov 15 12

Get-Parameter mit jQuery auslesen

by dennis

Ein kleines Plugin, gefunden hier: http://jquery-howto.blogspot.de/2009/09/get-url-parameters-values-with-jquery.html

$.extend({
  getUrlVars: function(){
    var vars = [], hash;
    var hashes = window.location.href.slice(window.location.href.indexOf('?') + 1).split('&');
    for(var i = 0; i < hashes.length; i++)
    {
      hash = hashes[i].split('=');
      vars.push(hash[0]);
      vars[hash[0]] = hash[1];
    }
    return vars;
  },
  getUrlVar: function(name){
    return $.getUrlVars()[name];
  }
});

Und so funktionierts:

// Objekt mit allen Parametern:
var allVars = $.getUrlVars();
 
// Ein einzelner Parameter:
var byName = $.getUrlVar('name');
Nov 15 12

JavaScript Micro-Templating

by dennis

Dazu gibt’s ein wirklich gutes Script von John Resig, gefunden in seinem Blog:

http://ejohn.org/blog/javascript-micro-templating/

dort, und in seinem Buch gibt es auch weitere Informationen.

// Simple JavaScript Templating
// John Resig - http://ejohn.org/ - MIT Licensed
(function(){
  var cache = {};
 
  this.tmpl = function tmpl(str, data){
    // Figure out if we're getting a template, or if we need to
    // load the template - and be sure to cache the result.
    var fn = !/\W/.test(str) ?
      cache[str] = cache[str] ||
        tmpl(document.getElementById(str).innerHTML) :
 
      // Generate a reusable function that will serve as a template
      // generator (and which will be cached).
      new Function("obj",
        "var p=[],print=function(){p.push.apply(p,arguments);};" +
 
        // Introduce the data as local variables using with(){}
        "with(obj){p.push('" +
 
        // Convert the template into pure JavaScript
        str
          .replace(/[\r\t\n]/g, " ")
          .split("<%").join("\t")
          .replace(/((^|%>)[^\t]*)'/g, "$1\r")
          .replace(/\t=(.*?)%>/g, "',$1,'")
          .split("\t").join("');")
          .split("%>").join("p.push('")
          .split("\r").join("\\'")
      + "');}return p.join('');");
 
    // Provide some basic currying to the user
    return data ? fn( data ) : fn;
  };
})();

Ein Template würde dann so aussehen:

 
<script type="text/html" id="item_tmpl">
  <div id="<%=id%>" class="<%=(i % 2 == 1 ? " even" : "")%>">
    <div class="grid_1 alpha right">
      <img class="righted" src="<%=profile_image_url%>"/>
    </div>
    <div class="grid_6 omega contents">
      <p><b><a href="/<%=from_user%>"><%=from_user%></a>:</b> <%=text%></p>
    </div>
  </div>
</script>

außerdem ist es möglich inline-Script zu verwenden:

<script type="text/html" id="user_tmpl">
  <% for ( var i = 0; i < users.length; i++ ) { %>
    <li><a href="<%=users[i].url%>"><%=users[i].name%></a></li>
  <% } %>
</script>

Verwendet wird die Funktion dann so:

var results = document.getElementById("results");
results.innerHTML = tmpl("item_tmpl", dataObject);

oder so:

var show_user = tmpl("item_tmpl"), html = "";
for ( var i = 0; i < users.length; i++ ) {
  html += show_user( users[i] );
}
Nov 9 12

Input-Felder auf dem iPad / iPhone

by dennis

Beim mobilen Safari werden Textfelder und Buttons abgerundet, mit einem Schatten versehen und etwas breiter dargestellt als z.B. im Firefox. Wer das nicht möchte, kann dies mit folgendem Schnipsel unterbinden:

input {
    -webkit-appearance:none; /* schatten etc entfernen */
    -webkit-border-radius:0; /* runde ecken entfernen */
    -webkit-box-sizing: border-box; /* css-box modell umstellen */
}

Einen interessanten Artikel zum box-sizing Attribut gib es hier:

http://www.peterkroener.de/schoenes-neues-css-box-sizing/

Feb 17 12

jquery.mSlide.js – jQuery plugin

by dennis

simple and easy to use slider-gallery with touch support

source:

/*
 * jquery.mSlide.js - jQuery plugin
 * simple and easy to use slider-gallery with touch support
 *
 * written by: M.Eckebrecht, D.Lehmann
 * examples and documentation at: www.quirks-modus.de/_mSlide/
 * 
 * version: 1.0 (16/02/2012)
 * requirements: jQuery, jQueryUI
 * 
 * licensed under the GPL license:
 *   http://www.gnu.org/licenses/gpl.html
 */
$(function() {
    $.widget( "od.mSlide", {
 
        options: {
            slideMarginLeft: 50,
            change: null /* callback function for change event */
        },
 
        _create: function() {
            var e = this.element;
            var self = this;
 
            if(typeof e[0].addEventListener == 'function') {
                e[0].addEventListener("touchstart", this._touchHandler, false);
            	e[0].addEventListener("touchmove", this._touchHandler, false);
            	e[0].addEventListener("touchend", this._touchHandler, false);
            	e[0].addEventListener("touchcancel", this._touchHandler, false);
            }
 
            e.data('allSlidesWidth',0).css({
                'position':'relative',
                'overflow':'hidden'
            });
 
            e.find('div.slide').each(function(){
                $(this).css('float','left').data({
                    'pos_x' : e.data('allSlidesWidth'),
                    'slide_num' : $(this).prevAll('.slide').size() 
                });
                e.data('allSlidesWidth',e.data('allSlidesWidth')+$(this).width());
            });
 
            e.wrapInner($('<div class="mSlide_innerWrap" />').css('width',e.data('allSlidesWidth'))); 
            e.append($('<div class="mSlide_btnLeft" />').hide());
            e.append($('<div class="mSlide_btnRight" />'));
 
            i = e.find('.mSlide_innerWrap'); // inner
 
            e.find('.mSlide_btnRight').click(function(){
                self.slideNext();                
            });
 
            e.find('.mSlide_btnLeft').click(function(){
                self.slidePrev();                
            });
 
            i.draggable({
                axis: 'x',
                cursor: 'pointer',
                scroll: true,
                stop: function(event, ui) {
                    if($(this).position().left > 0) {
                        $(this).animate({
                          left:0
                        }, 500, 'easeOutBounce', function(){
                            self._refresh(true);
                        });
                    } else if(e.width() > (i.width() + i.position().left)){
                        $(this).animate({
                          left: (i.width()-e.width())*-1
                        }, 500, 'easeOutBounce', function(){
                            self._refresh(true);
                        });
                    } else {
                        self._refresh(true);
                    } 
                }
            });
            this._refresh(false);
        },
 
        _touchHandler: function(event) {
            var touches = event.changedTouches;
            var first = touches[0];
            var type = '';
            switch(event.type) {
                case "touchstart":
                    type="mousedown";
                    break;
                case "touchmove":
                    type="mousemove";
                    event.preventDefault();
                    break;
                case "touchend":
                    type="mouseup";
                    break;
                default: return;
            }
            var simulatedEvent = document.createEvent("MouseEvent");
            simulatedEvent.initMouseEvent(type, true, true, window, 1, first.screenX, first.screenY, first.clientX, first.clientY, false, false, false, false, 0/*left*/, null);
            first.target.dispatchEvent(simulatedEvent);
        },
 
        slideTo: function(slide_num){
            var e = this.element;
            var i = e.find('.mSlide_innerWrap');
            var self = this;
            switch(slide_num) {
                case 'first' : slide_num = 0; break;
                case 'last'  : slide_num = e.find('div.slide').size()-1; break; 
            }
            e.find('div.slide').each(function() {
                if ($(this).data('slide_num') == slide_num  ) {
                    if ((i.width()-($(this).data('pos_x'))) > e.width()) {
                        var slideTo = $(this).data('pos_x')*-1 + self.options.slideMarginLeft;
                    } else {
                        var slideTo = -1*(i.width()-e.width());
                    }
                    i.animate({
                        'left' : ((slideTo >0)?0:slideTo)
                    },500, null, function(){
                        self._refresh();
                    });
                    return false;
                }
            });
        },
 
        slideNext: function(){
            var e = this.element;
            var i = e.find('.mSlide_innerWrap');
            var self = this;
            var current_pos = -1*i.position().left;
            e.find('div.slide').each(function(){
                if (($(this).data('pos_x')) > current_pos+self.options.slideMarginLeft) {
                    if ((i.width()-($(this).data('pos_x'))) > e.width()) {
                        var slideTo = -1*($(this).data('pos_x')-self.options.slideMarginLeft);
                    } else {
                        var slideTo = -1*(i.width()-e.width());
                    }
                    i.animate({
                        'left' : slideTo
                    },500, null, function(){
                        self._refresh(true);
                    });
                    return false;  
                }                
            });
        },
 
        slidePrev: function(){
            var e = this.element;
            var i = e.find('.mSlide_innerWrap');
            var self = this;
            var current_pos = -1*i.position().left;  
            e.find('div.slide').each(function(){
                if (current_pos <= $(this).data('pos_x')) {
                    var slideTo = -1*($(this).prev('.slide').data('pos_x')-self.options.slideMarginLeft);
                    i.animate({
                        'left' : ((slideTo >0)?0:slideTo)
                    },500, null, function(){
                        self._refresh(true);
                    });
                    return false;
                } 
            });                
        },
 
        // called when created, and later when changing options or slide stops
        _refresh: function(triggerChange) {
            var e = this.element;
            var i = e.find('.mSlide_innerWrap');
            var posLeft = i.position().left;
            // hide left button if position is first slide
            if(posLeft >= 0){
                e.find('.mSlide_btnLeft').fadeOut();
            } else{
                e.find('.mSlide_btnLeft').fadeIn();
            }
            // hide right button if position is last slide
            if(e.width() >= i.width() + posLeft){
                e.find('.mSlide_btnRight').fadeOut();
            } else{
                e.find('.mSlide_btnRight').fadeIn();
            }
            if (triggerChange) {
                this._trigger("change", null, -1*posLeft);
            }
        },
 
        _destroy: function() {
        },
 
        _setOptions: function() {
            $.Widget.prototype._setOptions.apply( this, arguments );
            this._refresh(true);
        },
 
        _setOption: function( key, value ) {
            $.Widget.prototype._setOption.call( this, key, value );
            this._refresh(true);
        }
    });
});

stylesheet:

div.mSlide_btnLeft { width:5%; height:100%; position:absolute; left:0; top:0; background:#000; z-index:100; opacity: .5; }
div.mSlide_btnRight { width:5%; height:100%; position:absolute; right:0; top:0; background:#000; z-index:100; opacity: .5; }

example markup:

    <div id="gallery" style="width:800px; height:280px;">
        <div class="slide" style="width:400px; height:280px; background:#fa0;">
            Slide 1
        </div>
        <div class="slide" style="width:250px; height:280px; background:#f0a;">
            Slide 2
        </div>
        <div class="slide" style="width:350px; height:280px; background:#fa0;">
            Slide 3            
        </div>
        <div class="slide" style="width:310px; height:280px; background:#f0a;">
            Slide 4
        </div>
        <div class="slide" style="width:370px; height:280px; background:#fa0;">
            Slide 5
        </div>
        <div class="slide" style="width:370px; height:280px; background:#f0a;">
            Slide 6
        </div>
    </div>
 
    <a id="first">first</a> || <a id="prev">prev</a> || <a id="next">next</a> || <a id="last">last</a>

simple:

$('#gallery').mSlide();

all options:

        $('#gallery').mSlide({
            'change': function(e, pos_x) {
                // onChange - callback: 
                // e = event // pos_x = current slide position 
            },
            'slideMarginLeft' : 50 /* 50px from left (used for left control) */
        });

use of external controls:

        $('#next').click(function(){
            $('#gallery').mSlide('slideNext');
        });
 
        $('#prev').click(function(){
            $('#gallery').mSlide('slidePrev');
        });
 
        $('#last').click(function(){
            $('#gallery').mSlide('slideTo','last');
        });
 
        $('#first').click(function(){
            $('#gallery').mSlide('slideTo','first');
        });
 
        $('#slide_num').click(function(){
            $('#gallery').mSlide('slideTo',3);
        });

download and demonstration: /_mSlide/