Category: HTML CSS PHP

  • Display embedded YouTube videos on iPhone

    Open the Settings app, under General find Restrictions. Set the YouTube app to On.
    Unless you did a “jailbreak” that interferes with YouTube, your embedded video should display.

    Embedded YouTube videos not working on your iPhone? Try this fix

    For embedding videos on YouTube, use HTML like this: (more…)

  • Using PHP Packages without Composer

    Composer http://getcomposer.org/ is a “Dependency Manager for PHP”.

    If you do not have Apache command line access, like most people using a shared hosting account, instructions for installing a PHP package with Composer won’t work (at least not using the instructions given):

    The preferred method of installation is [composer](http://getcomposer.org/). In your project root (not web root), create a minimum [composer.json](http://packagist.org/) file:
    {
    "require": {
    "VerticalTab/Pillow": "x.x.x"
    }
    }
    Replace "x.x.x" above with the tag number you want to use.

    Next, get composer and use it to install (again, in your project root)
    $ wget http://getcomposer.org/composer.phar
    $ php composer.phar install
    This will put the library into your vendors directory.

    Then you would use this line of code in your program to access the package:
    require 'vendor/autoload.php';

    Well, you don’t need Composer to install this package, you can insert some standard PHP code that works, into your program. (But you won’t have all the version checking that Composer does.)

    This PHP code replaces the autoload.php:
    function __autoload($class_name) {
    require_once("./$class_name.php");
    }

    The package I am using as an example is a Zillow API package, in the VerticalTab folder, inside the current folder, and contains these files:
    /VerticalTab/Pillow/Chart.php
    /VerticalTab/Pillow/Comps.php
    /VerticalTab/Pillow/Date.php
    /VerticalTab/Pillow/HttpClient.php
    /VerticalTab/Pillow/Links.php
    /VerticalTab/Pillow/Property.php
    /VerticalTab/Pillow/Proxy.php
    /VerticalTab/Pillow/Range.php
    /VerticalTab/Pillow/ResultSet.php
    /VerticalTab/Pillow/SearchResults.php
    /VerticalTab/Pillow/Service.php
    /VerticalTab/Pillow/Xml.php
    /VerticalTab/Pillow/Zestimate.php

    List all the package’s .PHP files using a special syntax. Convert the names like below. The backslashes are required; forward slashes are incorrect syntax. Drop the .php extension

    use VerticalTab\Pillow\Chart;
    use VerticalTab\Pillow\Comps;
    use VerticalTab\Pillow\Date;
    use VerticalTab\Pillow\HttpClient;
    use VerticalTab\Pillow\Links;
    use VerticalTab\Pillow\Property;
    use VerticalTab\Pillow\Proxy;
    use VerticalTab\Pillow\Range;
    use VerticalTab\Pillow\ResultSet;
    use VerticalTab\Pillow\SearchResults;
    use VerticalTab\Pillow\Service;
    use VerticalTab\Pillow\Xml;
    use VerticalTab\Pillow\Zestimate;

    Now all the package code is accessible to your program.

    Inside the package are namespace commands, e.g. VerticalTab/Pillow/SearchResults.php has namespace VerticalTab\Pillow;

  • PHP Unzip bug makes WordPress Updates hang

    I have been having problems with WordPress Updates for a while. No error messages, just the WordPress Update, or WordPress Plugin Update, hangs.

    Then I found out that the WordPress backup plugin I am using, BackWPUp, failed if I configured it to use any compression.

    Then I found out that the problems I had been having with random-seeming 500 Server Errors was from the WP-Super-Cache plugin configured with, hmm, compression.

    WordPress Updates would create new folders, but no files in them.

    I wrote a PHP program, outside of WordPress, to test if the problem was in WordPress, or in the version of PHP used on my server.

    The problem is in PHP. The PHP unzip routines lock up. No error message on screen, in the PHP log, or in the system log.

    WordPress 3.6.0 and 3.6.1, Multi-Site, Apache, PHP Version 5.2.17, [HTTP_ACCEPT_ENCODING] => gzip, deflate

    PHP itself has a bug in Zip/Unzip in old versions. Hangs without any error message displayed or in logs. Affects WordPress Update, and Plugin/Theme Updates, and any plugins that use compression (e.g. WP-Super-Cache or BackWPup)

    PHP Change Log http://www.php.net/ChangeLog-5.php#5.3.4 says
    PHP Version 5.3.4 released 09-Dec-2010 – Fixed crash in zip extract method

    Note: PHP versions are numbered so version 5.3.10 is higher than 5.3.9 (5.3.1 is lower)

    (more…)

  • PHP Unzip bug makes WordPress Updates hang

    I have been having problems with WordPress Updates for a while. No error messages, just the WordPress Update, or WordPress Plugin Update, hangs.

    Then I found out that the WordPress backup plugin I am using, BackWPUp, failed if I configured it to use any compression.

    Then I found out that the problems I had been having with random-seeming 500 Server Errors was from the WP-Super-Cache plugin configured with, hmm, compression.

    WordPress Updates would create new folders, but no files in them.

    I wrote a PHP program, outside of WordPress, to test if the problem was in WordPress, or in the version of PHP used on my server.

    The problem is in PHP. The PHP unzip routines lock up. No error message on screen, in the PHP log, or in the system log.

    WordPress 3.6.0 and 3.6.1, Multi-Site, Apache, PHP Version 5.2.17, [HTTP_ACCEPT_ENCODING] => gzip, deflate

    PHP itself has a bug in Zip/Unzip in old versions. Hangs without any error message displayed or in logs. Affects WordPress Update, and Plugin/Theme Updates, and any plugins that use compression (e.g. WP-Super-Cache or BackWPup)

    PHP Change Log http://www.php.net/ChangeLog-5.php#5.3.4 says
    PHP Version 5.3.4 released 09-Dec-2010 – Fixed crash in zip extract method

    Note: PHP versions are numbered so version 5.3.10 is higher than 5.3.9 (5.3.1 is lower)

    (more…)

  • Server alert you of File Not Found and other Errors

    I want to know when there are errors on my site.

    “404: File Not Found” errors can be from forgetting to upload an image, or from typing a URL wrong for a link to another page on my site. They can also be from people typing a file name wrong (some words are commonly mis-spelled). I can correct all of these, if I know about them.

    Most people have to remember to check their site logs. (But they don’t.) I want the option of having the server send me an email.

    Yes, I get emails about made-up names search engines look for, and made-up names spammers search for. I just delete these emails, part of web site maintenance (or I turn off emails about errors that didn’t start from my site, e.g. hackers searching). A few search bots are so bad, I have my site not send me emails about their “missing files”; the Bing-bot is drunk.

    I also want to know about some categories of 403: Access Denied requests, to make sure that none are for files that should be permitted. I’ve had some WordPress plugins, for example, that produced file names with double slashes in them, which my security system was blocking; I needed to know that was going on, and what exactly was making those legitimate file requests get blocked. I needed full disclosure from my server.

    Here is how I have the server give me the reports I need. (Another post will cover the security system I use, and the modifications to get the full disclosure from it.)

    What you Need to Use This

    I’m writing to experienced PHP programmers. You have to be able to understand, install, test PHP programs. This works on my server, LunarPages hosting and an IIS server, but accesses server variables that might be different on your server; there is no way I can know the details of your server (unless you hire me to install this for you). This might not fully work on your server, or might need modification! If you can’t debug PHP, for server-level functions, don’t try this; have me or another programmer do this for you.

    This works best on Apache web servers. This also works on IIS servers, though they will probably give less detailed reporting, and may block writing the error log entries. (I’m not going to give IIS instructions here, if you have an IIS server you probably already know how to adapt these instructions. In another post I’ll show my web.config file changes for IIS.)

    How to Call This

    I have my server error pages, 403.php 404.php 500.php (specified in .htaccess) include errortowebmaster.php. I have all these files in /public_html/shared (and have symbolic links defined so the shared folder literally does get shared by all my web sites).

    Initializing errortowebmaster.php

    
    

    So far, I am doing some standard initializing variables.

    LinkChecker is a good free program to check for broken links on your site; I want to not get an email for each link it finds, since it produces good reports itself. I simplify the http agent field (and have these defined in one place should the agent ever change).

    What Caused 403:Access Denied Errors?

    I am using the 5G Blacklist/Firewall from http://perishablepress.com/5g-blacklist-2013/ to block access by hackers and spammers. It works on commonly used patterns in several server fields, not on constantly-updated IP addresses or constantly-spoofed User Agent fields. I highly recommend reading their pages, so you understand how it works.

    However, the 5G Blacklist needs testing, to make sure nothing on your server gets blocked inadvertently. Another post will show how I customized the 5G Blacklist script; this post shows how I use server variables I set, to give me reports — what phrase triggered what section of the Blacklist.

    if ( (getenv("REDIRECT_noRewrite") !== FALSE) || (getenv("noRewrite") !== FALSE) ){
    	$htaccessErrors[] = 'Module mod_rewrite.c not installed or not enabled';
    }
    if (getenv("REDIRECT_dotHTLocation") !== FALSE) {
    	/* for testing only, which .htaccess file is being triggered? The root or a subdomain? Testing showed that on LunarPages hosting, only the QueryString section of 5G Blacklist needs to be in a subdomain's .htaccess file -- easier to maintain updates with most of the 5G Blacklist in only one place */
    	$htaccessErrors[] = '.htaccess location:' . getenv("REDIRECT_dotHTLocation");
    } elseif (getenv("dotHTLocation") !== FALSE) {
    	$htaccessErrors[] = '.htaccess location:' . getenv("dotHTLocation");
    }
    
    if (isset($_SERVER['REDIRECT_noUserAgent']) || isset($_SERVER['noUserAgent']) ) {
    	$htaccessErrors[] = 'NoUserAgent';
    }
    
    /* root .htacess sets variable, subdomain .htaccess sets REDIRECT_variable, since the root checks first then redirects to the subdomain */
    
    /* variable names must exactly match what shows up in $_SERVER, is case sensitive (e.g. badQueryString not BadQueryString) */
    if (getenv("REDIRECT_badQueryString") !== FALSE) {
    	$htaccessErrors[] = 'badQueryString:'.getenv("REDIRECT_badQueryString");
    	$sendMail = $sendMail ? $emailQueryString : false;
    } elseif (getenv("REDIRECT_REDIRECT_badQueryString") !== FALSE) {
    	$htaccessErrors[] = 'badQueryString:'.getenv("REDIRECT_REDIRECT_badQueryString");
    	$sendMail = $sendMail ? $emailQueryString : false;
    } elseif ($querystring=='QUERY_STRING') {	/* from IIS web.config */
    	$htaccessErrors[] = 'badQueryString';
    	$sendMail = $sendMail ? $emailQueryString : false;
    }
    
    if (getenv("REDIRECT_badRequestString") !== FALSE) {
    	$htaccessErrors[] = 'badRequestString:' . getenv("REDIRECT_badRequestString");
    	$sendMail = $sendMail ? $emailRequestString : false;
    } elseif (getenv("REDIRECT_REDIRECT_badRequestString") !== FALSE) {
    	$htaccessErrors[] = 'badRequestString:' . getenv("REDIRECT_REDIRECT_badRequestString");
    	$sendMail = $sendMail ? $emailRequestString : false;
    } elseif ($querystring=='URL_REQUEST_STRING') {	/* from IIS web.config */
    	$htaccessErrors[] = 'badRequestString';
    	$sendMail = $sendMail ? $emailRequestString : false;
    }
    
    if (getenv("REDIRECT_badUserAgent") !== FALSE) {
    	$htaccessErrors[] = 'badUserAgent:' . getenv("REDIRECT_badUserAgent");
    	$sendMail = $sendMail ? $emailUserAgent : false;
    } elseif (getenv("REDIRECT_REDIRECT_badUserAgent") !== FALSE) {
    	$htaccessErrors[] = 'badUserAgent:' . getenv("REDIRECT_REDIRECT_badUserAgent");
    	$sendMail = $sendMail ? $emailUserAgent : false;
    } elseif ($querystring=='USER_AGENT') {	/* from IIS web.config */
    	$htaccessErrors[] = 'badUserAgent';
    	$sendMail = $sendMail ? $emailUserAgent : false;
    }
    
    if ( getenv("REDIRECT_badRequestMethod") !== FALSE ) {
    	$htaccessErrors[] = 'badRequestMethod:' . getenv("REDIRECT_badRequestMethod");
    	$sendMail = $sendMail ? $emailRequestMethod : false;
    } elseif ( getenv("REDIRECT_REDIRECT_badRequestMethod") !== FALSE ) {
    	$htaccessErrors[] = 'badRequestMethod:' . getenv("REDIRECT_REDIRECT_badRequestMethod");
    	$sendMail = $sendMail ? $emailRequestMethod : false;
    }
    
    if ( (getenv("REDIRECT_badCharsInRequest") !== FALSE) || (getenv("REDIRECT_REDIRECT_badCharsInRequest") !== FALSE) ) {
    	$htaccessErrors[] = 'badCharsInRequest (e.g. CRLF)';
    	$sendMail = $sendMail ? $emailBadChars : false;
    }
    /* testing, don't know yet which .htaccess catches badCharsInRequest problems */
    if ( (getenv("REDIRECT_badCharsInRequestLC") !== FALSE) || (getenv("REDIRECT_REDIRECT_badCharsInRequestLC") !== FALSE) ) {
    	$htaccessErrors[] = 'badCharsInRequestLC (e.g. CRLF)';
    	$sendMail = $sendMail ? $emailBadChars : false;
    }
    
    if ( (getenv("REDIRECT_badCookie") !== FALSE) || (getenv("REDIRECT_REDIRECT_badCookie") !== FALSE) ) {
    	$htaccessErrors[] = 'badCookie';
    	$sendMail = $sendMail ? $emailCookie : false;
    }
    /* testing, don't know yet which .htaccess catches cookie problems */
    if ( (getenv("REDIRECT_badCookieLC") !== FALSE) || (getenv("REDIRECT_REDIRECT_badCookieLC") !== FALSE) ) {
    	$htaccessErrors[] = 'badCookieLC';
    	$sendMail = $sendMail ? $emailCookie : false;
    }
    
    if (isset($_SERVER['REDIRECT_ERROR_NOTES'])) {
    	$htaccessErrors[] = 'Error Notes:'.$_SERVER['REDIRECT_ERROR_NOTES'];
    } elseif (isset($_SERVER['REDIRECT_REDIRECT_ERROR_NOTES'])) {
    	$htaccessErrors[] = 'Error Notes:'.$_SERVER['REDIRECT_REDIRECT_ERROR_NOTES'];
    }
    
    /* badNuisance should be sent directly to bad-webbot.php, display minimal page. Nuisance in QUERY_STRING would arrive here */
    if (isset($_SERVER['REDIRECT_badNuisance'])) {
    	$htaccessErrors[] = 'Nuisance:'.$_SERVER['REDIRECT_badNuisance'];
    	$sendMail = false;
    } elseif ($querystring=='NUISANCE') {
    	/* from IIS web.config */
    	$htaccessErrors[] = 'Nuisance';
    	$sendMail = false;
    } 
    
    if (isset($_SERVER['REDIRECT_badBot']) || isset($_SERVER['REDIRECT_badBadHacker']) ) {
    	/* badBot set in /shared/blackhole/.htaccess, for the George Lerner modified version of http://perishablepress.com/blackhole-bad-bots/ which makes lines to cut/paste into .htaccess */
    	if (isset($_SERVER['REDIRECT_badBot']) ) {
    		$htaccessErrors[] = 'badBot '. $_SERVER['REDIRECT_badBot'];
    		$sendMail = $sendMail ? $emailBot : false;
    	}
    	/* If an IP address gives too many 403 errors, add them to the badBadHacker list in .htaccess. Future enhancement, have this file keep count, make a log file with lines to cut/paste into .htaccess */
    	if (isset($_SERVER['REDIRECT_badBadHacker']) ) {
    		$htaccessErrors[] = 'blacklisted by IP';
    		$sendMail = $sendMail ? $emailHacker : false;
    	}
    } elseif ($querystring=='IP_ADDRESS') {
    	/* from IIS web.config */
    	$htaccessErrors[] = 'blacklisted by IP';
    	$sendMail = $sendMail ? $emailHacker : false;
    }
    
    

    On my server, custom server variables get prefixed with 'REDIRECT_' and I tested both with and without the prefix; this is definitely something that your server could do differently. Check the $_SERVER and $_ENV variables for the actual variable names on your server.

    Very Detailed Error Display for My Computer

    Put your IP addresses (e.g. home computer, work computer, and smart phone) in $myipaddress, set in your secret configuration file (secret because it contains your IP address, which will let baddies see your server configuration). Not "secure" but probably "secure enough"; someone who sees your screen can get anything they want from you; someone who has access to your configuration file has full access to your site. Sometimes is Very useful for debugging to see the server variables, especially when I am configuring a new client's account. Just browse to any non-existent file name on their web site from my computer, and my 404.php shows me (and only me) $_SERVER and $_ENV.

    For any IP address I know, I also display my name for it. For example, don't put the local coffee shop in the "My IP" list, but it makes sense to identify the coffee shop so you know "that was me testing things" or "hmm, I was at the coffee shop, but didn't access That file..." (Pay attention to who can see your screen, even from a distance!) I also put names to the IP address of web site clients, since they will have a lot more errors as they develop new pages on their site, (those few times I notice an ongoing problem and give them the solution, they love it).

    Displaying the Detailed Troubleshooting & Error Information

    Note: For getting this to display well in WordPress, I changed <pre> to <code> (open and close tags) in the next section. You'll probably like the display better if you use pre in your code.

    if (in_array($ip, $myipaddress)) {
    	echo "
    .htaccess Errors:
    "; print_r($htaccessErrors); echo "
    "; echo "
    _SERVER:
    "; print_r($_SERVER); echo "

    _ENV:
    "; print_r($_ENV); echo "

    "; if (isset($_GET)) { foreach ($_GET as $arg) { $clean = filter_input(INPUT_GET, $arg, FILTER_SANITIZE_STRING); // new PHP function print "Get Parameter: $clean
    "; } } if (isset($argv)) { // IIS (Cart32) leaves in $_SERVER['QUERY_STRING'], or $_SERVER['argv'], both unparsed, on error pages. foreach ($argv as $arg) { $clean = filter_input(INPUT_GET, $arg, FILTER_SANITIZE_STRING); print "Argv Parameter: $clean
    "; } } else if (isset($_SERVER['argv'])) { foreach ($_SERVER['argv'] as $arg) { $clean = filter_input(INPUT_GET, $arg, FILTER_SANITIZE_STRING); print "_SERVER argv Parameter: $clean
    "; } } } $ipText = $ip; switch ($ip) { case "12.345.67.89" : case "123.45.678.90" : $ipText .= " (My Home)"; break; case "98.76.54.32" : $ipText .= " (My Work)"; break; default: // $ipText .= " (unknown)"; } $combine = $ipText . " tried to load \n";

    Writing an Error Log

    After some checking for uninitialized variables, write a brief error result to log files. I deliberately keep 403 errors out of the system PHP error log, as they require different attention. (Tempting to move 404 errors to their own file, so many idiot search engines/bots, but then I wouldn't see when I forgot to upload an image... I do, however, note when a request originated from a file on my server, since this is much more likely something I need to fix.)

    if (strpos($requri, $servname) !== false) {
    	/* MVC's IIS server gives full URL and status in requri */
    	if ($status !== '') {
    		$subject = $status.' '.$requri; 
    	} else {
    		$subject = $requri;
    	}
    	$combine .= " $requri";
    } else {
    	$subject = "$status $servname $requri";
    	$combine .= " $servname$requri"; 
    }
    
    $mycookie = "";
    foreach ($_COOKIE as $cookie_name => $cookie_text)
    {
    	$mycookie .= "\t" . $cookie_name . ": " . $cookie_text . " \n";
    }
    
    if (!isset($browser_name)) { $browser_name = "(unknown) "; }	
    // set by script from http://techpatterns.com/downloads/php_browser_detection.php
    if (!isset($browser_ver)) { $browser_ver = "(unknown) "; }
    

    Display Info On Web Browser

    $message = "

    $note

    \n

    $combine

    \n

    Remote Host = $remoteHost

    \n

    Query string = $querystring

    \n

    HTTP Referer = $httpref

    User Agent = $httpagent

    \n

    $today

    \n

    Cookies =

    $mycookie
    \n
    "; $message .= "\nBrowser detected as: " . $browser_name . "version " . $browser_ver; $message .= "\nIP Address: " . $ip; echo $message;

    Sending an Email to Site Administrator

    The email will be sent to the address in $adminemail, defined in your secret configuration file.

    function implode_r($glue,$arr){
            $ret_str = "";
            foreach($arr as $a){
                if ($ret_str === "") {
    				$ret_str .= (is_array($a)) ? implode_r($glue,$a) : $a;
    			} else {
    				$ret_str .= (is_array($a)) ? implode_r($glue,$a) : $glue . $a;
    			}
            }
            return $ret_str;
    }
    if (!isset($logfile)) { 
    	$logfile = getenv("DOCUMENT_ROOT") . "/error_log";
    }	
    if (!isset($deniedLogfile)) { 
    	$deniedLogfile = $logfile.'403';
    }
    if (!file_exists($logfile)) {
    	$fh = @fopen($logfile, 'w'); 
    	if ($fh !== FALSE) { fwrite($fh, "ErrorToWebMaster New Error File\n"); fclose($fh); }
    }
    if (!file_exists($deniedLogfile)) {
    	/* some servers prohibit fopen, so @ before it to prevent error messages */
    	$fh = @fopen($deniedLogfile, 'w'); 
    	if ($fh !== FALSE) { fwrite($fh, "ErrorToWebMaster New 403 Error File\n"); fclose($fh); }
    }
    if (isset($htaccessErrors)) {
    	$htaccessErrorsStr = implode_r(" | ",$htaccessErrors);
    	/* Cart32 wimpy IIS hosting doesn't allow error_log ("permission denied") so @ before it */
    	@error_log("htaccess Errors = $htaccessErrorsStr | $ipText Accessing: $servname, $requri $now \n",3,$deniedLogfile);
    } else {
    	@error_log("$ipText Accessing: $servname, $requri $now \n",3,$logfile);
    	$htaccessErrorsStr = '';
    }
    
    /* $message was for on screen, $message2 is for emailing */
    $message2 = "htaccess Errors = $htaccessErrorsStr\n
    $combine \n
    Remote Host = $remoteHost\n
    Query string = $querystring \n
    HTTP Referer = $httpref\n
    User Agent = $httpagent \n
    $today \n
    Cookies = \n$mycookie \n";
    
    $message2 .= "\nBrowser detected as: " . $browser_name . "version " . $browser_ver;
    
    $fh = @fopen($logfile, 'r');
    if (filesize($logfile) == 0) {
    	$theData = "";
    } else {
    	$theData = fread($fh, filesize($logfile));
    }
    fclose($fh);
    $message2 .= "\n\n[Error Log]: $logfile\n" . $theData;
    $message2 .= "\n[End of Error Log]\n";
    $message2 .= print_r($_SERVER,true);
    $message2 .= print_r($_ENV,true);
    
    $to = $adminemail;
    $from = "From: " . $adminemail . "\r\n";
    
    if (isset($_SERVER['REDIRECT_ERROR_NOTES']) ) {
    	$sendMail = false;
    } else $sendMail = true;
    
    if ( (stristr($docroot,"xampp") == FALSE) && ($requri != "/wp-login.php") && ($httpagent != 'LinkChecker/') && ($httpagent != 'LinksManager.com_bot') ) {
    	if ($sendMail !== false) {
    		mail($to, $subject, $message2, $from);
    	}
    }
    ?>
    

    Questions?

    Post your comments or questions here, and of course if you would rather I set this up for you and get it working on your web site, let me know. Contact Me.

  • HTML on-page links not working

    HTML bookmarks, or on-page link targets, are used often so viewers can easily jump to another section on a page. The link would be in this form: <a href="#hello">. Then define the bookmark with code like this: <a name="hello">

    These links have stopped working if you use the wrong HTML code. Used to be you could use <a name="hello"></a> or <a id="hello"></a> but you can now only use the id attribute for this.

    name= is deprecated for link targets, and invalid for links in HTML5. It no longer works at least in latest Firefox (v13). Change <a name="hello"> to <a id="hello">

    The target does not need to be an <a> tag, it can be <p id="hello"> or <h2 id="hello"> etc. which is often cleaner code.

    name= is still used (needed) in forms. It is also still needed in META tags.

  • Favorite FireFox Add-Ons

    Here are the FireFox add-ons that I use

    NoScript

    Essential security for your web browser! Allow active content to run only from sites you trust, and protect yourself against XSS and Clickjacking attacks.

    Firebug

    Firebug integrates with Firefox to put a wealth of development tools at your fingertips while you browse. You can edit, debug, and monitor CSS, HTML, and JavaScript live in any web page.

    CodeBurner for Firebug

    CodeBurner is a Firefox add-on that integrates with Firebug, to extend it with reference material for HTML and CSS.

    Firefinder for Firebug

    Finds HTML elements matching chosen CSS selector(s) or XPath expression

    CoLT

    CoLT makes it easy to copy either a hyperlink’s text or both the link and the link’s text (in a format you specify). Two handy context-menu items make this possible, and don’t add clutter; the items are only visible when right-clicking a link! Using CoLT to make this list, right click on the link to the Add-On in the search page, press o (for “cOpy link text and location as”), press Enter (HTML is the default, plain text and BBCode are some of the other options).

    ColorZilla

    Advanced Eyedropper, ColorPicker, Page Zoomer and other colorful goodies…

    Live HTTP Headers

    View HTTP headers of a page while browsing.

    TwitterBar

    Post to Twitter from your address bar.

    BetterPrivacy

    Ever wondered why you are still tracked though you tried everything to prevent it?
    BetterPrivacy is a safeguard which protects from usually not deletable LSO’s on Google, YouTube, Ebay…

    Clipmarks

    Clipmarks lets you clip and share specific parts of articles, blog posts or anything else you read on the web. With our newly integrated Amplify service, you can post clips directly to Twitter or Facebook – or share them on clipmarks.com and more.

    Download Statusbar

    View and manage downloads from a tidy statusbar – without the download window getting in the way of your web browsing.

    Pixel Perfect

    Pixel Perfect is a Firefox/Firebug extension that allows web developers and designers to easily overlay a web composition over top of the developed HTML.

    CSS Usage

    Just found this one, I’ve been wanting to clean out my CSS! Firebug extension to view which CSS rules are actually used.

    FireFTP

    FireFTP is a free, secure, cross-platform FTP client for Mozilla Firefox which provides easy and intuitive access to FTP servers. (I use FileZilla instead, easier for multiple projects; but FireFTP runs inside Firefox.)

    FlashGot

    Download all the links, movies and audio clips of a page at the maximum speed with a single click, using the most popular, lightweight and reliable external download managers.

    IE View Lite

    This is a cut down version of IE View by Paul Roub, which is 47.4 KB. All the same UI and features are there. It has a right click menu item to open a page in IE and a list of sites to always open in IE. It can close tabs automatically and send.

    iMacros for Firefox

    Automate Firefox. Record and replay repetitious work. If you love the Firefox web browser, but are tired of repetitive tasks like visiting the same sites every days, filling out forms, and remembering passwords, then iMacros for Firefox is the solution you’ve been dreaming of! Whatever you do with Firefox, iMacros can automate it.

    I have automated reading ClickBank data into a database form; and reading real estate listings, home values and rental prices, into a database.

    Combined with MySQL PHP Generator – Free PHP MySQL Generator by SQL Maestro Group you have a relatively easy to use (compared to coding in PHP) data scraping to database system.

    InFormEnter

    InFormEnter adds a small, clickable icon next to every input field in a web form, from where you can select the item to be inserted – no typing required (but I press Insert, quicker). You can configure it to display your frequently used information such as name, email, address, library card number.

    Minimap Sidebar

    The Minimap Addon gives you a suite of built-in maps and mapping tools for your web browser. Drag and Drop addresses or address links you find on web pages to view their location in the Map Sidebar, or by manually adding locations.

    Operator

    Operator leverages microformats and other semantic data that are already available on many web pages to provide new ways to interact with web services.

    Save Complete

    Saves a webpage completely, providing a better alternative to the built-in save functionality of Firefox.

    Screengrab

    Screengrab! saves webpages as images…

    It will capture what you can see in the window, the entire page, just a selection, a particular frame… basically it saves webpages as images – either to a file, or to the clipboard. It captures Flash too!

    SenSEO

    SenSEO is an extension checking the most important on-page-SEO-criteria and calculating a grade of how good your site fulfills these criteria.

    Add-Ons I Sometimes Use

    DOM Inspector

    DOM Inspector is a tool that can be used to inspect and edit the live DOM of any web document or XUL application. The DOM can be navigated using a two-paned window displaying a variety of different views on the document and all nodes within.

  • Using Google Web Fonts (Needs Some Quotation Marks)

    http://www.google.com/webfonts has a great collection of web-licensed fonts to use freely in your web pages. It also has a very easy interface for picking the fonts you want and adding them to your web pages.

    One small glitch: In Firefox (at least version 14.0.1) you have to enclose CSS src url(‘site’) (and @import url(‘site’) in single quotes. The Google-supplied code doesn’t (currently) do that. Without the single quotes, the font doesn’t load. http://www.w3schools.com/cssref/css3_pr_font-face_rule.asp shows the proper syntax has the single quotes.

    Google says in your CSS file, put this:
    @import url(http://fonts.googleapis.com/css?family=Uncial+Antiqua);
    but should be this (notice the single quotes):
    @import url('http://fonts.googleapis.com/css?family=Uncial+Antiqua');

    Browse to the Google API URL (e.g. http://fonts.googleapis.com/css?family=Uncial+Antiqua). Copy the CSS from that page.

    It should output this:

    @font-face {
    font-family: 'Uncial Antiqua';
    font-style: normal;
    font-weight: 400;
    src: local('Uncial Antiqua'), local('UncialAntiqua-Regular'), url('http://themes.googleusercontent.com/static/fonts/uncialantiqua/v1/F-leefDiFwQXsyd6eaSlltK4x0DJI-T8q5ozNuxBQHo.woff') format('woff');

    [instead of url(http://themes…woff), it should have single quotes around it, url(‘http://themes…woff’) ]

    Paste the Google URL into your browser address bar, copy the result into your CSS file, and add the proper quotes.

  • iMacros for Retrieving Information from Multiple Web Pages

    iMacros is a great tool for grabbing information from web sites. People often use it for grabbing the latest stock prices, or getting the latest prices on many products. Putting the data into a spreadsheet-compatible file is easy.

    You can also use iMacros to fill out forms from a spreadsheet (.csv) text file.

    iMacros is a free plugin for Firefox (or IE), or there is a more powerful paid version.

    You specify the information to grab using a simple scripting language, that can search a web page for HTML tags or attributes. So, you could search for the the paragraph with an ID, and grab the text. Or look for a link that starts with a particular domain, and grab either the link or the text.

    iMacros automatically handles looping through all the occurrences on a page (for example, each row of a table), the most common need.

    The Firefox version lets you control switching tabs, to grab the data from one tab and write it to a form on another tab. (The form could process the data and save it to a database, or anything else you could program a form processing routine to do.)

    But it doesn’t have a full programming language, with testing variables and calling subroutines based on the test. It doesn’t have FOR loops, or WHILE loops. So, you can grab all the prices on one page, or keep grabbing one value from pages as long as there is a “Next” link. But you can’t easily do both. You can grab and save All the values, but not save only the ones that meet your conditions.

    Well, iMacros for Firefox has a Javascript interface. With that, you can have Javascript control the looping, testing for special conditions, massaging the data, checking for errors. You can pass values from Javascript to iMacros, and return extracted information to Javascript.

    I’m going to show some code snippets for using loops to pass values to iMacros scripts and back to Javascript, and testing for errors.

    So you have some idea what the script does, in my task, one web page lists the MapID of each Map in a section of the County. Another web page lists the Parcels contained in a specific Map. Yet another web page shows what I want, details about a Parcel. I then have iMacros put the details in a custom form, and submit the form data to a PHP program that writes it to a MySQL database.

    Since each section of the county could have an unknown number of Map IDs, and each Map would have an unknown number of Parcels, and each Parcel has information on several web pages, and since there is so much information to collect it couldn’t be done in one day (probably non-stop gathering for 4 months!) and therefore I needed to have the scripts keep track of what had been completed, it was too complex for any iMacros script. But with the Javascript interface controlling 6 scripts, it works!

    mapidnum = 1
    /* Javascript uses labeled loops so you can break out of multiple loops */
    loopm:
    do {
    iimSet("mapidnum", mapidnum)
    /* get the nth map id */
    iret = iimPlay("macro-that-finds-each-map-id.iim")
    if (iret == -101) {
    /* user pressed Stop button */
    break loopm;
    }
    mapid = iimGetLastExtract() /* 849/849010100 */
    if (mapid == "#EANF#") {
    continue;
    }
    iimDisplay("TownshipRange "+t+"("+l+") SectionCount:"+s+" Section "+sections[s]+" Township "+tr[0]+" Range "+tr[1]+" MapIDnum: "+mapidnum+" MapID: "+mapid+" TotAPN="+totalapnsfound);
    mapid = mapid.slice(4,13) /* 849010100 */
    iimSet("mapid", mapid)
    /* read number of parcel IDs for this map ID */
    iret = iimPlay("macro-that-finds-how-many-detail-records-for-map-id.iim")
    apnsfoundstr = iimGetLastExtract()
    if (iret == -101) {
    /* user pressed Stop button */
    break loopm;
    }
    n = apnsfoundstr.indexOf(" APN(s) Found",0)
    if (n != -1) {
    apnsfound = parseInt(apnsfoundstr.slice(0,n))
    } else apnsfound = 0
    loopn:
    for (n=1; n<= apnsfound; n++) { iimSet("parcelloop", n) iimDisplay("TownshipRange "+t+"("+l+") SectionCount:"+s+" Section "+sections[s]+" Township "+tr[0]+" Range "+tr[1]+" MapIDnum: "+mapidnum+" MapID: "+mapid+" TotAPN="+totalapnsfound+" APN "+n+"("+apnsfound+")") iret = iimPlay("macro-that-reads-detail-record.iim") if (iret == -101) { /* user pressed Stop button, break out of labeled loop (both FOR loop and DO...WHILE) */ break loopm; } if (iret == -933) { /* when disconnect WiFi get: -933 Error Loading Page */ /* do something to retry, or run a script PROMPT "Error Loading Page (WiFi Error?)" */ continue; } parceldash = iimGetLastExtract(1) /* have single parcel number (with dashes, maybe with split letter), next step needs parcel number without dashes */ parcel = parceldash.replace(/-/g,''); /* take all values so far, pass to iMacros to put into Form for writing to Database */ iimSet("county", county) iimSet("parcel", parcel) iimSet("parceldash", parceldash) iimSet("mapid", mapid) iimSet("township", tr[0]) iimSet("range", tr[1]) iimSet("section", sections[s]) iimDisplay("TownshipRange "+t+"("+l+") SectionCount:"+s+" Section "+sections[s]+" Township "+tr[0]+" Range "+tr[1]+" MapIDnum: "+mapidnum+" MapID: "+mapid+" TotAPN="+totalapnsfound+" APN "+n+"("+apnsfound+") Parcel:"+parcel); iret = iimPlay("macro-that-reads-parcel-data-puts-it-in-form-that-writes-to-MySQL.iim") } /* end for APNs found */ mapidnum++ } while (mapid != "#EANF#")

    iMacros web site has full documentation on all the commands, including iimSet (sets a variable for use in an iMacros script), iimPlay (play an iMacros script), iimGetLastExtract (bring extracted data into Javascript), and iimDisplay (display text in a status area of the screen).

    In iMacros scripts you would access the variables (set with iimSet).

    ' macro-that-finds-each-map-id.iim uses the parameter like this:
    ' Find the nth Map ID. The page displays a link to a PDF of the map; the Map ID is the text of the link.
    TAG POS=R{{mapidnum}} TYPE=A ATTR=HREF:http://156.42.37.20* EXTRACT=TXT

    ' macro-that-reads-detail-record.iim uses the parameter like this:
    ' Find the nth Parcel Number, displayed as a link to the first page of the parcel's information (the County Assessor's page has links to the County Treasurer's info for this parcel).
    TAG POS=R{{parcelloop}} TYPE=A ATTR=HREF:/Assessor/ParcelApplication/Detail.aspx* EXTRACT=TXT

    Want Me to Help?

    If this is giving you ideas for grabbing information you could use, I can probably write iMacros scripts to get what you need, into a database or web page. Contact Me so we can talk.

  • Form Processing in PHP

    Best I have found for processing forms is
    Tectite Form Mail.

    Tectite is Secure (spammers can’t use your form to send other emails), written from scratch by professional PHP programmers who understand email security. Your email is hidden in a configuration file (not in the form) all the form has is a name like “Me” or “Sales” (optional, but so important of course you’d use that feature).

    Has required fields, value checking, custom error pages (with informative messages, telling the visitor which field they didn’t fill in right).

    Send results to text file and/or the email(s) you specify in your configuration file.

    Completely free. Written in PHP. Thorough documentation.

    Even has a PHP interface. I wrote a form for saving results of online research, used the Tectite PHP interface to save the entire form’s results into a WordPress post.

  • WordPress 3 (or 4) Multi-Site Installation, on Shared Hosting, Even with Existing Web Sites

    WordPress 3 (or 4) Multi-Site Installation, on Shared Hosting, Even with Existing Web Sites

    Here’s how to Install WordPress 3 with the New Multi-Site Capabilities — Even if you Already Have a Website in your Hosting Account. [Updated, works on WordPress 4 through at least WP 4.5)

    WordPress multi-site features let you very easily create blogs (now officially called “sites”).

    You can now have multiple blogs in WordPress with much less administration of WordPress updates, plug-in updates and settings, theme updates, etc. Multiple blogs, for each domain you are hosting, can be managed by a single installation of WordPress. (more…)

  • KeyCaps Font for Computer Documentation

    Font Designers: I need a KeyCaps Font!

    I’ve searched the web for a web-licensed keyboard font that has the Windows Flag, Macintosh Clover, all the special keys that are needed for computer software instructional pages. Instructions like “Type your name, press <Tab>, type your email, press <Ctrl>-<Alt>-E (on OS-X, press <Clover>-E), and <Left-Click> OK.”

    Don’t need all the alphabetic, number, punctuation characters, just the special keys, that are legible and look good next to normal sized text, on web pages.

    Prefer it be a separate font, so can use simple commands like <KBD>e</KBD> to display <Enter> or <KBD>c</KBD> to display <Ctrl>, or <KBD>4</KBD> to display the Clover. None of those awkward <#65432;> character codes, that don’t even show in most character maps! (more…)