Thursday, June 18, 2015

JQuery UI tooltip with a close link for mobile

Here is the JQuery code I use to to set up JQuery UI tool tips with a close link that only appears on mobile platforms. This is useful because on a desktop, the tool tip will close when you mouseout from the target element, but on a touch platform it sticks around.

// Set up tool tips for images and anchors.
$( document ).tooltip({
   items: "a[title], img[alt], .toolTip[title], :not(.noToolTip)",
  track: true,
  position: { my: "left+15 center", at: "right center" },
   content: function() {
      var element = $( this );
      var closer = closerLink = '';
      if (isMobile()) {
         closer = ' <br><div onClick="$(this).parent().parent().remove();" style="color: blue; text-decoration: underline; text-align: right;">Close</div>';
         closerLink = ' <br>Tap link again to open it.<br><div onClick="$(this).parent().parent().remove();" style="color: blue; text-decoration: underline; text-align: right;">Close</div>';
      }
      // noToolTip means NO TOOL TIP.
      if ( element.is( ".noToolTip" ) ) {
         return null;
      }
      // Anchor - use title.
      if ( element.is( "a[title]" ) ) {
         return element.attr( "title" ) + closerLink;
      }
      // Image - use alt.
      if ( element.is( "img[alt]" ) ) {
         return element.attr( "alt" ) + closer;
      }
      // Any element with toolTip class - use title.
      if ( element.is( ".toolTip[title]" ) ) {
         return element.attr( "title" ) + closer;
      }
   }
});

function isMobile() {
   return (/iPhone|iPod|iPad|Android|BlackBerry/).test(navigator.userAgent);
}

I am targeting three types of things here:

  • Anchor tags (a) with a title attribute.
  • Image tags (img) with a title attribute.
  • Any element with class toolTip.
  • And specifically exclude any element with class noToolTip.

You can see this in action on my newly refreshed site: Chihuahua Rescue Victoria.

Wednesday, June 17, 2015

Run main class in test dir from maven with Java agent and Log4J configuration

It took a bit of help from Michal (on this StackOverflow post: Running main method from test class via maven) before I got this right. But here it is:

<profile>
   <id>run-importer</id>
   <properties>
      <loadTimeWeaverArg>-javaagent:"${settings.localRepository}/org/springframework/spring-agent/2.5.6/spring-agent-2.5.6.jar"</loadTimeWeaverArg>
      <log4JConfigArg>-Dlog4j.configuration=file:${project.build.directory}/path/to/log4j.properties</log4JConfigArg>
      <mainClassArg>com.myorg.MyClass</mainClassArg>
      <arg1>foo</arg1>
   </properties>
   <build>
      <plugins>
         <plugin>
            <groupId>org.codehaus.mojo</groupId>
            <artifactId>exec-maven-plugin</artifactId>
            <version>1.2.1</version>
            <goals>
               <goal>exec</goal>
            </goals>
            <configuration>
               <executable>java</executable>
               <classpathScope>test</classpathScope>
               <arguments>
                  <argument>${log4JConfigArg}</argument>
                  <argument>${loadTimeWeaverArg}</argument>
                  <argument>-classpath</argument>
                  <classpath />
                  <argument>${mainClassArg}</argument>
                  <argument>${arg1}</argument>
               </arguments>
            </configuration>
         </plugin>
      </plugins>
   </build>
</profile>

And I run it with the command line: mvn -e exec:exec -Prun-importer

The advantages of this approach:

  • The whole purpose of this profile is to run "special code" that should never be deployed but needs to make use of code in src and test src.
  • It leaves room in case this ever needs to be duplicated, so there is no "competition" regarding what gets run with mvn -e exec:exec.
  • I can specify java agent, log4j and lots of other config using variables that already exist in the pom.
  • I can override any of these arguments on the command line with -Darg1="bar"

Sunday, April 26, 2015

Generating getters setters toString hashCode and equals

Generating code is a good way to remove so much boilerplate in Java that you would otherwise write by hand. Getters and setters is a big one. Eclipse can generate those, but Eclipse generation clashes with Checkstule rules that I use. The toString(), equals() and hashCode() methods are important to get right too. Previously I set up Eclipse templates but I still had to fill in the details by hand. Most of the time it's better to start with a default implementation for each that uses every non-static field in the class.

This post demonstrates some code I have written to automate the generation of these things.

Checkstyle vs Getters and Setters

I use Checkstyle wherever I can. It encourages me to write code with a consistent style and, like FindBugs, helps me to avoid common errors. Here are some Checkstyle rules that I use:

  1. Ensure that I have a jadvadoc comment on fields and methods. This includes checking that I have a @return tag for non-void methods and a @param tag for each parameter. Javadoc comments let me impart important context that cannot always be achieved by self documentating code.
  2. Final parameters on methods. Avoid accidentally re-assigning a parameter during method execution by making sure that all parameters are delcared as final. If I need a primitive (or wrapper or other immutable object like String) whose value will change, it is clearer to define a new local variable and assign it an initial value from the corresponding final parameter if needed.
  3. Avoid hidden fields. Don't have a parameter with the same name as an instance variable, because it is too easy to accidentally modify the wrong one. Using this on the instance variable will avoid these errors, but it is easy to forget.

In my opinion, these are very good rules to live by when coding and as I write new methods, I find myself automatically implementing these rules: they have become muscle memory for me.

However these rules have a cost when dealing with getters and setters. Let's say I create a new class, add ten fields, and in Eclipse select Source > Generate getters and setters > select all > OK and bam, I have ten corresponding accessor and mutator methods for my ten fields. Now I would have to edit twenty methods to correct the comments and edit ten methods to make parameters final and give them a different name. Here is an example of the getters and setters generated by Eclipse.

private String name;


private int id;


/**
 * @return the name
 */
public final String getName() {
   return name;
}


/**
 * @param name the name to set
 */
public final void setName(String name) {
   this.name = name;
}


/**
 * @return the id
 */
public final int getId() {
   return id;
}


/**
 * @param id the id to set
 */
public final void setId(int id) {
   this.id = id;
}

Eclipse generation cannot, as yet, make parameters final or give them different names, and the comments are very basic and only use the parameter name to make them different. It is less likely that following the Checkstyle rules will create as much value on getters and setters as on other methods. Getters and setters are very simple methods, usually one-liners. Because they are so simple that they can be auto-generated, we don't generally modify them after creation - so there is less need to give them the protection of checkstyle rules like final parameters and avoiding hidden fields. Thus, the cost of editing so many methods is rarely worth the value it gives.

Having said all that, there a couple of reasons why I still want to follow these rules, even on getters and setters if I can cut the cost significantly.

  • Comments are still valuable for fields/getters/setters that cannot be truly un-ambiguously named, which happens often enough no matter how much thought you can put into it. For the simple ones, simple comments are fine.
  • If my Eclipse Problems or Markers view is filled up with dozens of Checkstyle warnings from getters and setters, it is harder for me to spot the warnings I really should take care of. So I would prefer to keep my Problem/Markers view clean and automatically implement the Checkstyle rules if possible.

Generate Boilerplate

So here is how I have attempted to fix this issue and make it as painless as possible to generate getters and setters and implement the Checkstyle rules I want. At the same time, I will generate decent default toString(), equals() and hashCode() methods to copy and paste too.

As a pre-requisite, I have checked out my utils project from GitHub into Eclipse: https://github.com/robertmarkbram/RobUtils so that I can use three classes in particular:

  • OutputFieldList.java which has some utility functions that use reflection to inspect a class and do things I sometimes find useful such as
    • output a list of fields or getters and setters that are present in a class.
    • output sample equals(), hashCode() and toString() impelementations using the commons lang builders.
    • outputs sample getters and setters from fields defined in a class.
  • FieldComment.java which is an annotation that stores a field's comment that OutputFieldList will read and output as javadoc comments for getter and setter fields. It's based on a solution Abhi wrote about in this StackOverflow answer to the question: How to get a JavaDoc of a method at run time?
  • FieldCommentSampleClass.java which is just a place I copy the field declarations into so that I can use the FieldComments annotation in the same project as the other things in RobUtils.

I begin by writing out the fields in the actual class they belong to. I write out comments for each.

public class Foo {
   /** User friendly label for display. */
   private String name;

   /** Unique ID within datastore. */
   private int id;

}

I then copy these comments into FieldCommentSampleClass.java, which is just a scratch pad that has the advantage of being in the same project as the @FieldComment annotation. Within FieldCommentSampleClass.java, I do a couple of find and replace operations to change the javadoc comments to @FieldComment annotations.

  1. Replace /**  with @FieldComment(comment=".
  2. Replace */ with ").

So that now have fields with the @FieldComment annotation:

public class FieldCommentSampleClass {
   @FieldComment(comment="User friendly label for display. ")
   private String name;

   @FieldComment(comment="Unique ID within datastore. ")
   private int id;
}

I then run the main method in OutputFieldList.java with these two calls. This will generate getters and setters, plus decent toString(), hashCode() and equals() methods for me.

public static void main(final String[] args) throws Exception {
   generateGetAndSetMethods(FieldCommentSampleClass.class, //
      OutputFields.NO_FIELDS, //
      OutputGetters.OUTPUT_GETTERS, //
      OutputSetters.OUTPUT_SETTERS);
   outputToStringHashAndEquals(Foo.class);
}

I now have the following output that I copy and paste into my original class.

/** @return user friendly label for display.  */
public String getName() {
   return name;
}

/** @return unique ID within datastore.  */
public int getId() {
   return id;
}


/** @param theName user friendly label for display.  */
public void setName(final String theName) {
   this.name = theName;
}


/** @param theId unique ID within datastore.  */
public void setId(final int theId) {
   this.id = theId;
}


@Override
public boolean equals(final Object obj) {
   if (obj == this) {
      return true; // test for reference equality
   }
   if (obj == null) {
      return false; // test for null
   }
   if (obj instanceof FieldCommentSampleClass) {
      final FieldCommentSampleClass other = (FieldCommentSampleClass) obj;
      // @formatter:off
      return new org.apache.commons.lang.builder.EqualsBuilder()
            .append(name, other.name)
            .append(id, other.id)
            .isEquals();
      // @formatter:on
   } else {
      return false;
   }
}


@Override
public int hashCode() {
   // @formatter:off
   return new org.apache.commons.lang.builder.HashCodeBuilder()
         .append(name)
         .append(id)
         .toHashCode();
   // @formatter:on
}


@Override
public String toString() {
   org.apache.commons.lang.builder.ToStringBuilder.setDefaultStyle(org.apache.commons.lang.builder.ToStringStyle.SHORT_PREFIX_STYLE);
   // @formatter:off
   return new org.apache.commons.lang.builder.ToStringBuilder(this)
      .append("name", name)
      .append("id", id)
      .toString();
   // @formatter:on
}

Final notes

A few notes to keep in mind.

  • OutputFieldList needs JDK 8 because it uses the Stream API with lambdas.
  • The toString(), equals() and hashCode() methods generated use the fully qualified class names for the apache commons lang builders. I them use Source > Add Import (or control+shift+m) to replace the fully qualified class names with just the class names.
  • If the code I am generating boiler plate for relies on custom code (e.g. field types are not standard java types), I may have to modify my RobUtils project so that it can reference those types, which is easy: Project > Properties > Java Build Path > Projects tab > Add and select the projects I need to reference.
  • I am pragmatic about following these Checkstyle rules.
    • When editing code written by someone else that has Checkstyle warnings, should I fix them? Perhaps. If there are just a few and I have time, sure. If there are many then only fix the ones in the actual lines of code I need to edit.
    • I don't force my own practices on other developers in a team. If the team has a policy already in place around the use of tools like Checkstyle or Findbugs, that takes precedence. Otherwise I practice what makes sense to me. If other developers are interested, explain how I use the tooling to help me write good code, but don't proselytise.
    • As I mentioned above, there is a cost to taking on processes like Checkstyle rules. If the cost is too high (i.e. there is little benefit to be gained), or the rules doesn't make sense to you, don't do it. Checkstyle warnings can be suppressed for a class, method or code block and rules can be removed entirely from a project specific configuration if the rule is too burdensome.

Sunday, April 19, 2015

PHP iterating over Blogger posts from Atom Feed XML

Thanks to some decent PHP APIs, it is really easy to read XML from a URL and process the results.

Use case: I have written an expression of interest form that people can use to raise an application to adopt a rescued Chihuahua. Part of the form lists the dogs available - and this list comes from a Blogger feed. The blog is used as a list of all the Chihuahuas that have been rescued (one dog per post) and the ones that are currently available all have a specific label: Available now. The expression of interest form reads the Atom feed for this label and displays a list of the available now dogs on the form.

Retrieve XML from URL and access child elements

The function below reads an XML document from a URL (an Atom feed) and will return an array of the child elements that represent specific posts with a given label from the blog.

function retrieveAvailableNowPosts() {
   // Set URL to XML we want to read - Available now.
   $file="http://chihuahuarescue.blogspot.com.au/feeds/posts/default/-/Available%20now";
   // Load specified XML file or report failure
   $xml = simplexml_load_file($file);
   if (!$xml) {
      return false;
   }
   // Load blog entries
   $posts =  $xml -> entry;
   if (sizeOf($posts) > 0) {
      return $posts;
   } else {
      return null;
   }
}

Notes about this function.

  • $xml = simplexml_load_file($file);
      • Loading the contents of a URL and then parsing the XML it contains is done with simplexml_load_file. The return from this function is either an SimpleXMLElement object or a boolean false if there was an error reading XML from the file (URL in this case).
      • The parameter to simplexml_load_file can be a file or URL.
      • Blogger supports feeds either from RSS 2.0 or Atom 1.0, and you can switch between them simply with a different URL.
      • The URL I am using (http://chihuahuarescue.blogspot.com.au/feeds/posts/default/-/Available%20now) is for an Atom feed. The label is the part after the last forward slash after it has been URL encoded i.e. Available now is the label, which becomes Available%20now after URL encoding. This was easy in my case because I only had to swap the space with %20. If you have more complicated labels (or perhaps need to URL encode dynamic labels), you can use the PHP function urlencode to do this.
  • $posts = $xml -> entry;
    • $xml is the variable containing the XML read from simplexml_load_file.
    • The Atom feed XML returned from the URL has feed as the root element and it contains a variable number of entry elements, each of which is a blog post that was made against the target label. The skeleton is shown below.
      <feed ...>
         ...
         <entry>
            ...
         </entry>
         <entry>
            ...
         </entry>
         <entry>
            ...
         </entry>
      </feed>
    • We access the array of the entry elements on the feed using the "arrow" operator (T_OBJECT_OPERATOR for objects).
    • $posts should therefore be an array which might be empty.
  • if (sizeOf($posts) > 0)
    • If the array has 1 or more elements, return it.
    • Otherwise, return null.

Iterate through XML elements

The function below accepts the XML elements read from the earlier function and iterates through them to output HTML.

function createDogList($posts) {
   $list = '<ul class="availableNowList">';
   // Check if posts is undefined, null, false or empty.
   if (!$posts || sizeOf($posts) == 0) {
      $list .= '<li>Unfortunately there are no dogs available at this time.</li>';
   } else {
      // Go over each entry.
      foreach($posts as $post) {
         // Publish time
         $dateTime = date("l jS F, Y", strtotime(strtok($post->published, 'T')));
         // Link.
         $link = $post->link[4]["href"];
         // Title.
         $title = $post->title;
         // List the entry.
         $list .=
            '<li>
               <a href="' . $link . '" target="_blank">'
                     . $title . '</a> <em><small>(published ' . $dateTime . ')</small></em>.
            </li>';
      }
   }
   $list .= '</ul>';
   return $list;
}

Notes about this function.

  • This function builds up an un-ordered list (ul) of blog posts, with each list item having
    • A link to the blog post - the link text being the blog entry title.
    • The date on which the post was published. Example list:
      • RUDI (published Sunday 29th March, 2015).
      • MIMI (published Sunday 29th March, 2015).
      • BAXTER (published Sunday 29th March, 2015).
  • If there are no posts against this label, output only one list item with an explanation that there are no matches at this time.
  • The parameter to this function is a list of blog posts against a certain label, retrieved by the previous function: retrieveAvailableNowPosts().
  • if (!$posts || sizeOf($posts) == 0) { .. }
    • This is more than just a null-check: !$posts will be true if the variable $posts is null, not set (undefined, has no value) or false.
    • Here is a quick overview, showing that an IF is a good test for all three things.
      <?php
         echo '<pre>';
         $foo1;        if($foo1) { ?>foo1 is set and not null/false.<?php } else { ?>foo1 is not set/null/false.<br><?php }
         $foo2=null;   if($foo2) { ?>foo2 is set and not null/false.<?php } else { ?>foo2 is not set/null/false.<br><?php }
         $foo3=false;  if($foo3) { ?>foo3 is set and not null/false.<?php } else { ?>foo3 is not set/null/false.<br><?php }
         echo 'foo1 - ';
         var_dump(isset($foo1));
         echo 'foo2 - ';
         var_dump(isset($foo2));
         echo 'foo3 - ';
         var_dump(isset($foo3));
         echo '</pre>';
      ?>
      
      The output of the above is:
      foo1 is not set/null/false.
      foo2 is not set/null/false.
      foo3 is not set/null/false.
      foo1 - bool(false)
      foo2 - bool(false)
      foo3 - bool(true)
    • This allows us to respond to two sad cases from retrieveAvailableNowPosts() at once.
      • retrieveAvailableNowPosts() returns false if it couldn't read the Atom feed XML from the blog.
      • retrieveAvailableNowPosts() returns null if the list of posts is empty.
      • The call to sizeof (sizeOf($posts) == 0) is actually not needed, because retrieveAvailableNowPosts() returns null if the list of posts is empty, but I left it here in case I ever call this function from a different place and neglect to included the same rule.
  • $dateTime = date("l jS F, Y", strtotime(strtok($post->published, 'T')));
    • $post->published
      • The value of this looks like 2015-03-29T03:10:00.003-07:00.
      • I only want the year, month and date only: I want to discard all the time information and just output the date. I will use a string tokenizer to do this in the next step.
    • strtok($post->published, 'T')
      • I use a string tokenizer with the letter "T" as the delimiter. Note that the first call strtok will return the first token, and since I only need the first token, I don't store reference to the tokenizer. Here is how you would use the tokenizer in another situation to go over all tokens:
        $string = "String to split";
        delimiter = " \n\t";  // Split string on spaces, newlines and tabs.
        $token = strtok($string, $delimiter);
        while ($token !== false) {
            echo "Next token: $token <br />";
            $token = strtok($delimiter);
        }
        
      • The result of strtok($post->published, 'T') will be something like 2015-03-29 (note that it does not include the delimiter itself).
    • date("l jS F, Y", strtotime(strtok($post->published, 'T')))
      • I use the date function to parse the date (from text like 2015-03-29) and output it in a different format (like Sunday 29th March, 2015).
      • See the PHP page for date function to find the full list of date format options, but here is what my format uses.
        • l - A full textual representation of the day of the week: Sunday through Saturday.
        • j - Day of the month without leading zeros: 1 to 31.
        • S - English ordinal suffix for the day of the month, 2 characters: st, nd, rd or th.
        • F - A full textual representation of a month, such as January or March: January through December.
        • Y - A full numeric representation of a year, 4 digits: 1999 or 2003.
  • $link = $post->link[4][href]
    • In a given entry element, get the href attribute of the fifth link element (using a zero based index).
    • The fifth link element holds a direct URL to the post, such as: <link rel="alternate" type="text/html" href="http://chihuahuarescue.blogspot.com/2015/03/mimi.html" title="MIMI"/>.
  • Google's description of what is in each post element shows you what things you can access this way for each post:
    • posts: A list of all posts for this page. Each post contains the following:
      • dateHeader: The date of this post, only present if this is the first post in the list that was posted on this day.
      • id: The numeric post ID.
      • title: The post's title.
      • body: The content of the post.
      • author: The display name of the post author.
      • url: The permalink of this post.
      • timestamp: The post's timestamp. Unlike dateHeader, this exists for every post.
      • labels: The list of the post's labels. Each label contains the following:
        • name: The label text.
        • url: The URL of the page that lists all posts in this blog with this label.
        • isLast: True or false. Whether this label is the last one in the list (useful for placing commas).

Error handling

The primary error condition is from the call to simplexml_load_file, which returns false if there was a failure reading XML from the URL. The secondary error condition occurs if we read the XML okay, but found it contained none of the elements we are interested in. On the page that uses these functions, both error conditions are treated as normal outputs from the retrieveAvailableNowPosts function and dealt with nicely, as you can see below. We output error messages if either error occurs, and display the "normal" content of the page otherwise.

$posts = retrieveAvailableNowPosts();
// If a boolean false is returned there was an error.
if ($posts === false) {
?>
   <p style="text-align: center; color: red;">Unable to load list of Available Now dogs from Chihuahua Rescue Victoria!</p>
<?php
// And null means there is nothing present.
} else if ($posts === null) {
?>
   <p style="text-align: center;">Unfortunately there are no dogs available at this time. Please try again later.</p>
<?php
// Otherwise, all good. Carry on.
} else {
?>
<?php
 ... normal page content goes here.
} // end else
?>

Just die!

We could have handled the error from simplexml_load_file in this way:

$xml = simplexml_load_file($file) or die("<p>An error message.</p>");

This offers a very poor user experience, especially on a web page because it will cause PHP to immediately exit and no further code on the page will be processed. This will most likely result in an ugly page with broken HTML.

Resources that helped me.

 

Saturday, April 18, 2015

UltraEdit macro to select HTML/XML tag

In a previous post from 2010, UltraEdit macro to select HTML/XML tag, I detailed two UltraEdit macros to select HTML/XML tags backwards and forwards. It had a couple of problems, such as not being able to distinguish between PRE and P when you start select P tags, so this version fixes that.

Here are the macros. The first is used to select the previous tag: I have it mapped to control+shift+,.

InsertMode
ColumnModeOff
HexOff
UltraEditReOn
Clipboard 2
IfSel
Find RegExp Up Select "</++^c^{>^}^{[ ^p^r^n^t]+[~>]++>^}"
Else
Find Up "<"
Find RegExp "[A-Za-z]"
SelectWord
Copy
Find Up "<"
Key LEFT ARROW
Find RegExp Select "</++^c^{>^}^{[ ^p^r^n^t]+[~>]++>^}"
EndIf
Clipboard 0

The second is used to select the next tag: I have it mapped to control+shift+..

InsertMode
ColumnModeOff
HexOff
UltraEditReOn
Clipboard 2
IfSel
Find RegExp Select "</++^c^{>^}^{[ ^p^r^n^t]+[~>]++>^}"
Else
Find "<"
Find RegExp "[A-Za-z]"
SelectWord
Copy
Find Up "<"
Key LEFT ARROW
Find RegExp Select "</++^c^{>^}^{[ ^p^r^n^t]+[~>]++>^}"
EndIf
Clipboard 0

A few notes about the macros.

  • Select previous tag.
    1. Use it by leaving the cursor within an opening tag (<p>) or closing tag (</p> or unary tag (<br>) or within the text content of a tag. Do not select any text.
    2. Press the shortcut (control+shift+,).
    3. The macro will begin running Else part of the IfSel condition (because no text was selected).
      1. Find "<"
        • Looks for the first left angle bracket before the cursor.
      2. Find RegExp "[A-Za-z]"
        • Find the next letter after the left angle bracket - which will be the start of the tag name.
      3. SelectWord
        • Select the tag name.
      4. Copy
        • Copy it - to the second clipboard, which was selected earlier in the macro by the command Clipboard 2.
      5. Find Up "<"
        • Select the first left angle bracket before the cursor (again).
      6. Key LEFT ARROW
        • Make sure cursor is to the left of that angle bracket so the next command (a Find) will have that character in scope.
      7. Find RegExp Select "</++^c^{>^}^{[ ^p^r^n^t]+[~>]++>^}"
        1. Select the entire open/close/unary tag.
        2. Find - because we had previously moved to the left of the opening left angle bracket of the tag, the search will take this tag into account.
        3. RegExp - use regular expressions. An earlier macro command (UltraEditReOn) specified that UltraEdit regular expressions are turned on (as opposed to Perl or Unix ones).
        4. Select - whatever we find with the next expression will be selected in UltraEdit.
        5. A breakdown of the expression: </++^c^{>^}^{[ ^p^r^n^t]+[~>]++>^}
          1. </++
            • Find left angle bracket and zero or more forward slashes: matches < or </.
          2. ^c
            • Find text in clipboard 2 (which we selected previously).
          3. ^{>^}^{[ ^p^r^n^t]+[~>]++>^}
            • This is an OR expression. ^{A^}^{B^} says find A or B. So this expression says to find either one of:
              • >
                • The right angle bracket that closes a tag. This covers the simple cases, e.g. <p>.
              • [ ^p^r^n^t]+[~>]++>
                • [ ^p^r^n^t]+ one or zero of: space or newline (DOS, Mac or Unix) or tab.
                • [~>]++ zero or more of any character other than the right angle bracket.
                • > the right angle bracket.
                • This covers tags with attributes, e.g. <p style=""> which may or may not be spread across multiple lines.
    4. Run the macro again with shortcut (control+shift+,).
    5. The macro will run the IfSel condition because now there is text selected from the previous run.
    6. It will run the exact same Find as was described above except for one difference.
      1. Find RegExp Up Select "</++^c^{>^}^{[ ^p^r^n^t]+[~>]++>^}"
      2. The Up part means that we will look for the next complete tag to the left of what we already have selected from the previous run.

Here is a sample of HTML that I used to test this on.

<html>
   <head>
      <title>Some Title</title>
   </head>
   <body>
      <div>
         <div style="color: red;"
               id="divWithId">Nested <span>div</span>.
            <pre>
               monospaced
            </pre>
         </div>
      </div>
   </body>
</html>

For the first run, I place the cursor as indicated below by | (either within the DIV open tag or within the DIV content.

<html>
   <head>
      <title>Some Title</title>
   </head>
   <body>
      <div>
         <div style="color: red;"
               id="divWit|hId">Nested| <span>div</span>.
            <pre>
               monospaced
            </pre>
         </div>
      </div>
   </body>
</html>

Run the select previous tag macro (I have it mapped to control+shift+,) and text will be selected as indicated below.

<html>
   <head>
      <title>Some Title</title>
   </head>
   <body>
      <div>
         <div style="color: red;"
            id="divWithId">Nested <span>div</span>.
            <pre>
               monospaced
            </pre>
         </div>
      </div>
   </body>
</html>

Now run the select next tag macro (I have it mapped to control+shift+.) and text will be selected as indicated below.

<html>
   <head>
      <title>Some Title</title>
   </head>
   <body>
      <div>
         <div style="color: red;"
            id="divWithId">Nested <span>div</span>.
            <pre>
               monospaced
            </pre>
         </div>
      </div>
   </body>
</html>

Run the select previous tag macro again and text will be selected as indicated below.

<html>
   <head>
      <title>Some Title</title>
   </head>
   <body>
      <div>
         <div style="color: red;"
            id="divWithId">Nested <span>div</span>.
            <pre>
               monospaced
            </pre>
         </div>
      </div>
   </body>
</html>

Run the select next tag macro again and text will be selected as indicated below.

<html>
   <head>
      <title>Some Title</title>
   </head>
   <body>
      <div>
         <div style="color: red;"
            id="divWithId">Nested <span>div</span>.
            <pre>
               monospaced
            </pre>
         </div>
      </div>
   </body>
</html>

If you use either macro again at this point, nothing will happen because there are no more DIV elements in the document not already selected.

Importantly, these macros work correctly on similarly named tags such as zip zipfileset (which I have used in XML for Ant build files). If I am select zip tags, it skips nested zipfileset elements.

Two final notes.

  1. Something this macro cannot do is to select an entire element that contains nested elements of the same tag. For example, consider the HTML below.
    <div id="outer">
       <div id="inner">
          Inner DIV.
       </div>
    </div>
    
    Macros in UltraEdit cannot be used to select the entire outer DIV because you can't store state in a macro, which you would need to do in order to count nested elements to make sure you select the entire outer one. My workaround for this situation is to just make it easier to keep selecting next/previous DIV tag so that you can achieve the same effect with a bit of repetition.
  2. UltraEdit Find commands in macros can use Perl regular expressions, which are very powerful too. One thing they can do much more easily is to treat newline characters as part of the wildcard. In a Perl regex, (?s) tells the regular expression to include newline characters when matching a . wildcard. You can also use backreferences in Find and Replace expressions. However, backreferences don't persist between macro calls. So, these two macros store the tag name in clipboard 2 so that each time you call one of the macros afterwards, it "remembers" what tag you were searching for by looking at clipboard 2.

    I wrote this up on the UltraEdit forum here: macro to select HTML tag v2.

    Tuesday, April 14, 2015

    Add content to Blogger posts specific to a label

    Mum uses her Blogger to post about dogs she has rescued and uses labels to mark which rescued Chihuahuas that are available to good homes, or have been happily re-homed etc. I created an expression of interest form for people to apply for the dogs that are available, and I wanted a link to the form to appear below the blog entries labelled with "Available now" only. Here is how to do it.

    You need to edit your Blogger XML template by going to the dashboard, selecting your blog, going to the Template section and clicking on the "Edit HTML" button. Take a backup of this content before you change it so that you can always go back! I haven't found a single comprehensive reference on the XML grammar and really only worked this out by experimentation and reading examples.

    In the template, search for <data:post.body/>. If you use the template for your mobile version as well, you will see two instances of that tag. I put my content under both instances because I wanted the same thing to appear on the desktop and mobile versions.

    <div class='post-body entry-content' expr:id='"post-body-" + data:post.id' itemprop='articleBody'>
       <data:post.body/>
       <!-- If the post has labels. -->
       <b:if cond='data:post.labels'>
          <!-- Go through all the labels attached to the post. -->
          <b:loop values='data:post.labels' var='label'>
             <!-- If current label is our target one. -->
             <b:if cond='data:label.name == "Available now"'>
                <!-- Display content I want to appear after the post. -->
                <div style="text-align: center;">
                   <p><em>If you would like to adopt this dog, or any of our Chihuahua Rescue Victoria dogs, please fill out the <a href="http://www.chihuahuarescuevictoria.org/forms/adoption/Express-interest-in-adopting-from-Chihuahua-Rescue-Victoria.php">expression of interest form</a>.</em></p>
                </div>
             </b:if>
          </b:loop>
       </b:if>
       <div style='clear: both;'/> <!-- clear for photos floats -->
    </div>
    

    Elements to note here.

    • <data:post.body/>
      • Indicates the body of each blog post.
    • <b:if cond='data:post.labels'>
      • Tests if the blog post has labels attached to it.
    • <b:loop values='data:post.labels' var='label'>
      • Loop through every label attached to this single post. The loop will place the current label in a variable named "label".
    • <b:if cond='data:label.name == "Available now"'>
      • Test if the current label's name is the label I am targeting.
      • I place my label-specific content within this tag.

    So essentially what I am doing is looping through post.labels with each label in a variable called label, and for each one I examine label.name.

    Look carefully at Google's description of what is in each post element to see what else you can access for each post:

    • posts: A list of all posts for this page. Each post contains the following:
      • dateHeader: The date of this post, only present if this is the first post in the list that was posted on this day.
      • id: The numeric post ID.
      • title: The post's title.
      • body: The content of the post.
      • author: The display name of the post author.
      • url: The permalink of this post.
      • timestamp: The post's timestamp. Unlike dateHeader, this exists for every post.
      • labels: The list of the post's labels. Each label contains the following:
        • name: The label text.
        • url: The URL of the page that lists all posts in this blog with this label.
        • isLast: True or false. Whether this label is the last one in the list (useful for placing commas).

    References I found useful.

     

     

    Sunday, April 12, 2015

    Validation and error handling points

    I have been writing a form today to send off emails with a PHP back-end. All day I have been dealing with validation and trying to cover all the bases (so all your bases remain belonging to YOU).

    Things to keep in mind when validating data.

    1. Presence - if the field is mandatory, validate that it has a value.
      1. Always give some visual way of marking a field as mandatory.
    2. Size - check the size of the input i.e. that it is less than a certain number of characters.
      1. Do this for all values, whether they are coming from an input, text area, checkbox or radio button. One way a site can be attacked is for a malicious sender to ignore your HTML form and POST their own massive values.
    3. Escape and sanitise data before you use it to prevent cross site scripting or SQL injection attacks. Do this before you:
      1. Save to a DB.
      2. Output back to HMTL.
      3. Write to a file.
      4. Send to an email.
      5. Send it to another part of your back-end for further processing.
    4. Type - check that an input is an integer or double or boolean as required.
    5. Format - check that input matches a required format, like a phone number or email etc.
      1. Can be helped by using input masks, but you still need to validate that the data you received matches the mask on the server side.
    6. Value - check input against your own business logic. For example:
      1. Is a number within a given range.
      2. Does the string match an element in a known list of choices.
    7. Related - apply any validation that requires examining multiple fields. For example, if country, state and postcode are given, make sure that they are a valid combination.
    8. Server side first.
      1. Client side validation is often easier, but server side validation is more important because javascript can be disabled or ignored completely if a malicious sender simply POSTS their own requests.
      2. Consider how to return errors in a such a way that they can be easily communicated back to the user on the interface.
    9. Client side second
      1. While server side is more important, client side validation makes for a faster and more responsive user experience because you can point out errors before the user ever hits SEND.
      2. Consider things such as how to present errors to users and how mark things like dynamic business rules (where field A is only mandatory if field B is given a value).