Adding Skin and Settings menus to your JDeveloper ADF application

Posted on Updated on

Ever want to display menus in your Oracle JDeveloper 11g ADF Faces rich client application so users can easily change skins or other settings on the fly?  Perhaps you don’t want to expose these menus to your end users but at least want some easy way for you to test these different options while you develop or debug your application.  This post will show you how to get it done similar to what you see on the hosted ADF Faces Rich Client demos.

Screen shot of skin and settings menus
Example skin and settings menus

Before we begin with the changes, let’s start with some background:

Skins

A skin is an Apache MyFaces Trinidad construct that is also used in ADF Faces.  A skin is very much like a standard CSS style sheet that you use to define styles used by an HTML page.  Skins abstract implementation details in the same manner that components in JavaServer Faces (JSF) abstract you from underlying HTML implementation details.  The syntax of a skin follows that of the CSS 3 spec.  Skins can extend other skins, have blocks for user agent/browser-specific definitions, accessibility-specific definitions, and other properties that would normally not be available in a CSS file.

If you are interested in creating your own skin then I would recommend reviewing these pages:

Settings

The ADF Faces framework uses settings defined in the WEB-INF/trinidad-config.xml file of your application.  This file allows you to control settings such as:

  • Which skin used by your application
  • Whether a screen reader is used
  • Whether the page should be optimized for high contrast colors
  • Whether the page should be optimized for large fonts
  • Whether animation should be enabled in component interactions

See the configuration documentation for more details on this file.

Now here are the details for how to create these menus:

The Configuration File

If your application does not already have one, create a new WEB-INF/trinidad-config.xml file.  We want to make these settings dynamic so let’s use EL expressions for the values like this:

<?xml version="1.0" encoding="UTF-8"?>
<trinidad-config xmlns="http://myfaces.apache.org/trinidad/config">
 <skin-family>#{skins.skinFamily}</skin-family>
 <accessibility-mode>#{accessibility.accessibilityMode}</accessibility-mode>
 <accessibility-profile>#{accessibility.accessibilityProfile}</accessibility-profile>
 <animation-enabled>#{animation.animationEnabled}</animation-enabled>
</trinidad-config>

I am using separate managed beans here primarily for organization but you could just use a single bean.  The skin-family specifies what the active skin is.  The accessibility-mode specifies whether the page should be targeted for a screen reader.  The accessibility-profile specifies whether high contrast colors and whether large fonts should be targeted for the page.  The animation-enabled specifies whether the user will see animations in components that provide them.

The Menu Components

Some af:menuBar (most likely one that exists in your application’s page template) will need some af:menu components.  In this example, we have one menu that lists all of the skins that we want to expose and another menu that lists the settings.

ADF Faces comes with some built-in skins that have an inheritance structure like this:

  • simple — plain, nearly void of color, basic appearance
    • blafplus-medium — rectangular, solid blue colors
      • blafplus-rich — rounded, solid blue colors
    • fusion — blue colors with a rich three-dimensional appearance
      • fusion-projector — similar to the fusion skin but with colors suited for poor quality screen projectors

We will make our skin menu use radio menu items to display which skin is active:

 <af:menu text="#{bundle.MENU_SKIN}" id="skins">
   <af:commandMenuItem
     id="skin1"
     text="blafplus-rich"
     type="radio"
     actionListener="#{skins.skinMenuAction}"
     selected="#{skins.skinFamily=='blafplus-rich'}"/>
   <af:commandMenuItem
     id="skin2"
     text="blafplus-medium"
     type="radio"
     actionListener="#{skins.skinMenuAction}"
     selected="#{skins.skinFamily=='blafplus-medium'}"/>
   <af:commandMenuItem
     id="skin3"
     text="fusion"
     type="radio"
     actionListener="#{skins.skinMenuAction}"
     selected="#{skins.skinFamily=='fusion'}"/>
   <af:commandMenuItem
     id="skin4"
     text="fusion-projector"
     type="radio"
     actionListener="#{skins.skinMenuAction}"
     selected="#{skins.skinFamily=='fusion-projector'}"/>
   <af:commandMenuItem
     id="skin5"
     text="simple"
     type="radio"
     actionListener="#{skins.skinMenuAction}"
     selected="#{skins.skinFamily=='simple'}"/>
 </af:menu>

The settings menu items will be presented as individual check mark options.  I am using a group to separate the animation setting from the others that are more accessibility-related:

 <af:menu text="#{bundle.MENU_SETTINGS}" id="config">
   <af:group id="acc">
     <af:commandMenuItem
       id="accMode"
       text="#{bundle.ACCESSIBILITY_LABEL_SCREEN_READER}"
       shortDesc="#{bundle.ACCESSIBILITY_SHORT_DESC_SCREEN_READER}"
       selected="#{accessibility.screenReader}"
       type="check"
       actionListener="#{accessibility.modeMenuAction}"/>
     <af:commandMenuItem
       id="contrast"
       text="#{bundle.ACCESSIBILITY_LABEL_HIGH_CONTRAST}"
       shortDesc="#{bundle.ACCESSIBILITY_SHORT_DESC_HIGH_CONTRAST}"
       selected="#{accessibility.highContrast}"
       type="check"
       actionListener="#{accessibility.colorContrastMenuAction}"/>
     <af:commandMenuItem
       id="fonts"
       text="#{bundle.ACCESSIBILITY_LABEL_LARGE_FONTS}"
       shortDesc="#{bundle.ACCESSIBILITY_SHORT_DESC_LARGE_FONTS}"
       selected="#{accessibility.largeFonts}"
       type="check"
       actionListener="#{accessibility.fontSizeMenuAction}"/>
   </af:group>
   <af:commandMenuItem
     id="animate"
     text="#{bundle.MENU_ITEM_ENABLE_ANIMATIONS}"
     type="check"
     selected="#{animation.animationEnabled=='true'}"
     actionListener="#{animation.animationMenuAction}"/>
 </af:menu>

Managed Beans

We could have hard-coded the values we were after in the configuration file but then you’d have to restart your application when you make changes.  Since we want these settings to be dynamic, we use EL to point these values to managed beans.  These same beans are used to handle the menu item actions as well as convey their selected states.

The managed beans can be defined in the faces-config.xml like this (using session scope to preserve the settings for the span of the user’s session):

<!-- A managed bean that we use to track the user's skin preference. -->
<managed-bean>
  <managed-bean-name>skins</managed-bean-name>
  <managed-bean-class>foo.SkinPreferences</managed-bean-class>
  <managed-bean-scope>session</managed-bean-scope>
</managed-bean>

<!-- A managed bean that we use to track the user's accessibility preference. -->
<managed-bean>
  <managed-bean-name>accessibility</managed-bean-name>
  <managed-bean-class>foo.AccessibilityPreferences</managed-bean-class>
  <managed-bean-scope>session</managed-bean-scope>
</managed-bean>

<!-- A managed bean that we use to track the user's animation preference. -->
<managed-bean>
  <managed-bean-name>animation</managed-bean-name>
  <managed-bean-class>foo.AnimationPreferences</managed-bean-class>
  <managed-bean-scope>session</managed-bean-scope>
</managed-bean>

The session-scoped SkinPreferences managed bean defines your default skin and looks like this:

public class SkinPreferences implements Serializable
{
 public String getSkinFamily()
 {
   return _skinFamily;
 }

 public void setSkinFamily(String family)
 {
   _skinFamily = family;
 }

 public void skinMenuAction(ActionEvent ae)
 {
   RichCommandMenuItem menuItem = (RichCommandMenuItem)ae.getComponent();
   setSkinFamily(menuItem.getText());
   reloadThePage();
 }

 public static void reloadThePage()
 {
   // Since the page might be using PPR navigation,
   // we need to issue a redirect to ensure the entire page redraws:
   FacesContext fContext =
     FacesContext.getCurrentInstance();
   String viewId =
     fContext.getViewRoot().getViewId();
   String actionUrl =
     fContext.getApplication().getViewHandler().getActionURL(fContext, viewId);
   try
   {
     ExternalContext eContext = fContext.getExternalContext();
     String resourceUrl = actionUrl;
     eContext.redirect(resourceUrl);
   }
   catch (IOException ioe)
   {
     System.err.println("Problem trying to reload the page:");
     ioe.printStackTrace();
   }
 }

 private String _cachedTabBarHeight = null;
 private String _skinFamily = _DEFAULT_SKIN;
 private static final String _DEFAULT_SKIN = "fusion";
 private static final long serialVersionUID = 1L;
}

The session-scoped AccessibilityPreferences managed bean looks like this:

public class AccessibilityPreferences implements Serializable
{
  public String getAccessibilityMode()
  {
    return _screenReader ? "screenReader" : "default";
  }

  public AccessibilityProfile getAccessibilityProfile()
  {
    // Use safe double-check locking just in case we have multiple threads coming
    // through.
    if (_profile == null)
    {
      synchronized (this)
      {
        if (_profile == null)
          _profile = _createAccessibilityProfile();
      }
    }
    return _profile;
  }

  public boolean isScreenReader()
  {
    return _screenReader;
  }

  public boolean isHighContrast()
  {
    return _highContrast;
  }

  public boolean isLargeFonts()
  {
    return _largeFonts;
  }

  public void setScreenReader(boolean screenReader)
  {
    _screenReader = screenReader;
  }

  public synchronized void setHighContrast(boolean highContrast)
  {
    _highContrast = highContrast;

    // We need to re-create the AccessibilityProfile instance if any of the profile
    // properties change.
    // Null out our old cached copy.
    // It will be re-created the next time that getAccessibilityProfile() is called.
    _profile = null;
  }

  public synchronized void setLargeFonts(boolean largeFonts)
  {
    _largeFonts = largeFonts;

    // We need to re-create the AccessibilityProfile instance if any of the profile
    // properties change.
    // Null out our old cached copy.
    // It will be re-created the next time that getAccessibilityProfile() is called.
    _profile = null;
  }

  public void modeMenuAction(ActionEvent ae)
  {
    setScreenReader(!isScreenReader());
    SkinPreferences.reloadThePage();
  }

  public void colorContrastMenuAction(ActionEvent ae)
  {
    setHighContrast(!isHighContrast());
    SkinPreferences.reloadThePage();
  }

  public void fontSizeMenuAction(ActionEvent ae)
  {
    setLargeFonts(!isLargeFonts());
    SkinPreferences.reloadThePage();
  }

  // Creates the AccessibilityProfile instance based on the user's current
  // accessibility preferences.
  private AccessibilityProfile _createAccessibilityProfile()
  {
    AccessibilityProfile.ColorContrast colorContrast = isHighContrast() ?
      AccessibilityProfile.ColorContrast.HIGH :
      AccessibilityProfile.ColorContrast.STANDARD;

    AccessibilityProfile.FontSize fontSize = isLargeFonts() ?
      AccessibilityProfile.FontSize.LARGE :
      AccessibilityProfile.FontSize.MEDIUM;

    return AccessibilityProfile.getInstance(colorContrast, fontSize);
  }

  // We hold a reference to a cached copy of the AccessibilityProfile so that we do
  // not need to re-create this object each time getAccessibilityProfile() is
  // called.
  // Note that we mark this as volatile so that we can safely perform double-checked
  // locking on this variable.
  private volatile AccessibilityProfile _profile;

  private boolean _screenReader;
  private boolean _highContrast;
  private boolean _largeFonts;

  private static final long serialVersionUID = 1L;
}

The session-scoped AnimationPreferences managed bean looks like this:

public class AnimationPreferences implements Serializable
{
  public boolean isAnimationEnabled()
  {
    return _animationEnabled;
  }

  public void setAnimationEnabled(boolean enabled)
  {
    _animationEnabled = enabled;
  }

  public void animationMenuAction(ActionEvent ae)
  {
    setAnimationEnabled(!isAnimationEnabled());
    SkinPreferences.reloadThePage();
  }

  private boolean _animationEnabled = true;
  private static final long serialVersionUID = 1L;
}

Adding an Accessibility Page

If you wanted to take this a step further (and do not have built-in, predefined user-specific settings after a user sign in screen), you should provide an “Accessibility” link.  This link should be exposed as early as possible in your page so that screen reader users can easily identify that you are providing them options.  This page should then provide a reduced user interface for simple settings configuration.

Screen shot of the accessibility preferences page
The accessibility preferences page

Extra faces-config.xml changes:

<!--
  A managed bean that holds onto the user's accessibility preferences while
  editing these value on the Accessibility Preferences page.  Note that unlike
  the session-scoped AccessibilityPreferences bean, this bean only requires
  request-scope, since it is only holding the values for a short time (for
  the life of one request).
  -->
<managed-bean>
  <managed-bean-name>accessibilityHolder</managed-bean-name>
  <managed-bean-class>foo.AccessibilityPreferencesHolder</managed-bean-class>
  <managed-bean-scope>request</managed-bean-scope>
  <!--
    We use a managed-property to inject the AccessibilityPreferences
    instance into our holder.  This allows the holder both to determine
    its initial values as well as apply back any changes.
    -->
  <managed-property>
    <property-name>accessibilityPreferences</property-name>
    <value>#{accessibility}</value>
  </managed-property>
</managed-bean>

The accessibility page should contain content like this:

<af:panelHeader id="ph1" text="#{bundle.ACCESSIBILITY_TITLE}">
  <af:panelFormLayout id="pfl1">
    <f:facet name="footer">
      <af:panelGroupLayout id="pgl2" layout="horizontal">
        <f:facet name="separator">
          <af:spacer id="s1" width="4px"/>
        </f:facet>
        <af:commandButton
          text="#{bundle.OK}"
          id="ok"
          action="#{accessibilityHolder.ok}"/>
        <af:commandButton
          text="#{bundle.CANCEL}"
          id="cancel"
          action="#{accessibilityHolder.cancel}"/>
      </af:panelGroupLayout>
    </f:facet>
    <af:selectBooleanCheckbox
      value="#{accessibilityHolder.screenReader}"
      id="sbc1"
      text="#{bundle.ACCESSIBILITY_LABEL_SCREEN_READER}"
      shortDesc="#{bundle.ACCESSIBILITY_SHORT_DESC_SCREEN_READER}"/>
    <af:selectBooleanCheckbox
      value="#{accessibilityHolder.highContrast}"
      id="sbc2"
      text="#{bundle.ACCESSIBILITY_LABEL_HIGH_CONTRAST}"
      shortDesc="#{bundle.ACCESSIBILITY_SHORT_DESC_HIGH_CONTRAST}"/>
    <af:selectBooleanCheckbox
      value="#{accessibilityHolder.largeFonts}"
      id="sbc3"
      text="#{bundle.ACCESSIBILITY_LABEL_LARGE_FONTS}"
      shortDesc="#{bundle.ACCESSIBILITY_SHORT_DESC_LARGE_FONTS}"/>
  </af:panelFormLayout>
</af:panelHeader>

The request-scoped AccessibilityPreferencesHolder managed bean looks like this:

/**
 * AccessibilityPreferencesHolder is a request-scope managed bean that temporarily
 * holds on to accessibility preferences while the end user is editing them in the
 * accessibility preferences page.  While interacting with the accessibility page,
 * we avoid pushing values directly into the AccessibilityPreferences instance,
 * since it is possible that the end user may leave the page without accepting these
 * changes.
 *
 * The values stored in this temporary object are only pushed into the
 * AccessibilityPreferences instance when the user commits the changes by clicking
 * the "OK" button
 * on the accessibility preferences page.  When leaving the page via any other means
 * (Cancel button or other command components), any changes stored in the
 * AccessibilityPreferencesHolder are discarded.
 *
 * This object is exposed as a rqeuest-scope managed bean with the name
 * "accessibilityHolder" (see faces-config.xml).  It can be accessed via the EL
 * expression "#{accessibilityHolder}".
 */
public class AccessibilityPreferencesHolder
{
  /**
   * Tests whether the user prefers screen reader-optimized content.
   */
  public boolean isScreenReader()
  {
    return _screenReader;
  }

  /**
   * Tests whether the user prefers high contrast-optimized content.
   */
  public boolean isHighContrast()
  {
    return _highContrast;
  }

  /**
   * Tests whether the user prefers content optimized for large fonts.
   */
  public boolean isLargeFonts()
  {
    return _largeFonts;
  }

  /**
   * Stores the screen reader preference.
   */
  public void setScreenReader(boolean screenReader)
  {
    _screenReader = screenReader;
  }

  /**
   * Stores the high contrast preference.
   */
  public void setHighContrast(boolean highContrast)
  {
    _highContrast = highContrast;
  }

  /**
   * Stores the large fonts preference.
   */
  public void setLargeFonts(boolean largeFonts)
  {
    _largeFonts = largeFonts;
  }

  /**
   * Setter for injecting the AccessibilityPreferences managed-property.
   * The managed-bean definition specifies that the AccessibilityPreferences
   * instance should be injected into this object via the
   * setAccessibilityPreferences() method. (See faces-config.xml.)
   */
  public void setAccessibilityPreferences(AccessibilityPreferences preferences)
  {
    // Use the session-scoped AccessibilityPreferences to initialize our
    // values to the user's current preferences.
    _largeFonts = preferences.isLargeFonts();
    _highContrast = preferences.isHighContrast();
    _screenReader = preferences.isScreenReader();

    // Keep a reference to the AccessibilityPreferences instance.  We'll need
    // access to this object in order to commit the user's new preferences.
    _preferences = preferences;
  }

  /**
   * Handles OK button clicks.
   */
  public String ok()
  {
    // Store the user's new preferences, making them active.
    _preferences.setScreenReader(isScreenReader());
    _preferences.setHighContrast(isHighContrast());
    _preferences.setLargeFonts(isLargeFonts());

    // Specify where to navigate to next.
    return "home"; // change this for your app's needs
  }

  /**
   * Handles cancel button clicks.
   */
  public String cancel()
  {
    // Don't save any new values - just return.
    return "home"; // change this for your app's needs
  }

  // User's current preferences.
  private boolean _largeFonts;
  private boolean _highContrast;
  private boolean _screenReader;

  // Injected AccessbilityPreferences instance.
  private AccessibilityPreferences _preferences;
}
Advertisements

Keeping your iTunes Library Fresh

Posted on Updated on

Are you getting bored with shuffle? Are your 5-star songs getting overplayed? Do you want to hear some of the old songs from time to time but not too much? The remedy is to create a “Good Mix” smart playlist and here’s how!

You don’t need iTunes 9 to do this but the steps are much simpler in iTunes 9. It introduces a new grouping feature for smart playlist rules.  Unfortunately, even with these new grouping abilities, there are still things that cannot be achieve without nesting playlists. You’ll need to create a total of 6 smart playlists–but don’t worry, it will be worth it!

Step 1 – Create the “Mix Prerequisites” playlist

This smart playlist will be the basis for all of the other smart playlists that we will be creating.  The goal of this playlist is to filter out files and genres that won’t be everyday music files.  For my tastes, this means “Classical” and “Holiday” music won’t be on my normal listening schedule but feel free adjust this as applicable to your tastes.

  1. Match = all
  2. Name – does not contain – “Digital Booklet -“
  3. Genre – is not – “Classical”
  4. Genre – is not – “Holiday”
  5. Genre – is not – “Podcast”
  6. Kind – is not – “MPEG audio stream”
  7. Kind – contains – “audio”
  8. Genre – is not – “Audiobooks”
  9. Limit to – unchecked
  10. Match only checked items – unchecked
  11. Live updating – checked

Mix-Prerequisites

Step 2 – Create the “Mix 4 Star Fill” playlist

This smart playlist is also a building-block playlist that will be consumed by another smart playlist.

  1. Match = all
  2. Playlist – is – Mix Prerequisites
  3. Rating – is – 4 stars
  4. Last Played – is not in the last – 14 days
  5. Limit to – unchecked
  6. Match only checked items – unchecked
  7. Live updating – checked

Mix 4 Star Fill

Step 3 – Create the “Mix 3 Star Fill” playlist

This smart playlist is also a building-block playlist that will be consumed by another smart playlist.

  1. Match = all
  2. Playlist – is – Mix Prerequisites
  3. Rating – is – 3 stars
  4. Last Played – is not in the last – 2 months
  5. Limit to – checked – 40 – items – selected by random
  6. Match only checked items – unchecked
  7. Live updating – checked

Mix 3 Star Fill

Step 4 – Create the “Mix 2 Star Fill” playlist

This smart playlist is also a building-block playlist that will be consumed by another smart playlist.

  1. Match = all
  2. Playlist – is – Mix Prerequisites
  3. Rating – is – 2 stars
  4. Last Played – is not in the last – 20 months
  5. Limit to – checked – 20 – items – selected by random
  6. Match only checked items – unchecked
  7. Live updating – checked

Mix 2 Star Fill

Step 5 – Create the “Mix 1 Star Fill” playlist

This smart playlist is also a building-block playlist that will be consumed by another smart playlist.

  1. Match = all
  2. Playlist – is – Mix Prerequisites
  3. Rating – is – 1 star
  4. Last Played – is not in the last – 30 months
  5. Limit to – checked – 5 – items – selected by random
  6. Match only checked items – unchecked
  7. Live updating – checked

Mix 1 Star Fill

Step 6 – Create the “Good Mix” playlist

This smart playlist is the final playlist and the one that you will be listening to.

  1. Match = all
  2. Playlist – is – Mix Prerequisites
  3. Click the “…” button to create a match grouping and set its match type to “all of the following”
  4. Rating – is – 5 stars
  5. Rating – is – no stars (will encourage you to rate songs you just purchased)
  6. Playlist – is – Mix 4 Star Fill
  7. Playlist – is – Mix 3 Star Fill
  8. Playlist – is – Mix 2 Star Fill
  9. Playlist – is – Mix 1 Star Fill
  10. Limit to – unchecked
  11. Match only checked items – checked
  12. Live updating – checked

Good-Mix

Step 7 – Shuffle and Enjoy

Now that all of the playlists have been created, you are almost ready to play music.

  1. Select the “Good Mix” playlist
  2. Select the “turn shuffle on” icon at the bottom-left of iTunes to make sure that the songs that get compiled by this playlist will be shuffled
  3. Play!

Maintenance

If your music tastes change, be sure to change the rating of your songs!

  • If a song feels overplayed to you?  Knock it down a star.
  • Is an old gem coming back to you?  Give it some extra stars.

If you notice that you have too many or not enough songs of a particular rating, you can change the corresponding “fill” playlist by adjusting:

  • The “limit to” setting so that more or less are included into the final compilation
  • The “Last Played – is not in the last” so that songs will be repeated more or less frequently

Holiday Variation

  1. Edit the “Mix Prerequisites” smart playlist
  2. Take out the “Genre – is not – Holiday” rule
  3. Edit the “Good Mix” smart playlist
  4. Add a “Genre – is not – Holiday” rule at the top of the list of rules
  5. Duplicate the “Good Mix” smart playlist and name the new one “Holiday Mix”
  6. Edit the “Holiday Mix” smart playlist
  7. Change the “Genre – is not – Holiday” rule to “Genre – is – Holiday”

What’s New in JSF 2?

Posted on Updated on

Andy Schwartz has created a fantastic introduction to the new features of JavaServer Faces 2:

Your Bank and Employer are Putting Your Security at Risk

Posted on Updated on

It is becoming an epidemic. More banks and employers are making changes to their web applications that they think are protecting both your and their interests but in practice, this is actually doing quite the opposite.

If your bank or employer is a well-known business, it is likely a frequent target for phishing attacks.  More people are relying on Google searches and other websites that generate “tiny” URLs (long URLs are redirected through a third party with a short URL that can easily be typed in a chat client, email, etc.).  This means that people aren’t using bookmarks to access their banks or companies which means if a website looks familiar to you, you aren’t going to pay close attention to the URL in your browser’s address bar.  On the same token, it is easy for fat fingers to slightly mistype a URL that you know by heart so you can easily go to a website different than what you intended and will fall victim to a phishing attack.  More and more banks and employers are becoming paperless so any interaction that is requested of the user comes from email.  An email has many ways to fool the user into believing that it and the links provided in the email are authentic.

How are my banks and my employer putting my security (and theirs) at risk?

The reason is that they are implementing various mechanisms to prevent your browser from saving and recalling stored user names and passwords.  This can be achieved by either adding autocomplete=”off” to the input fields or to the form that wraps these inputs.  Another mechanism that is becoming more prevalent is the technique of asking for your user name in one screen and then your password in a second screen after the first screen is submitted.  These mechanisms break the password storage and retrieval features of your browser.

Why does denying password saving put us at risk?

There are two reasons why this is very bad.  Both reasons come from human error:

  1. Requiring that a human type in the user name and password means that the human must concentrate on typing in the data rather than analyzing whether the website is authentic.  If the browser populated the user name and password fields, you know immediately that the website is the correct one.  If the fields are blank and the user knows that the password should have been recalled by the browser’s saved password mechanism, the user becomes surprised and starts to question if they are visiting the correct page.  If the browser never saves the password, the user no longer has a fast and accurate way to know that the website is authentic.
  2. Requiring that a human type in the user name and password encourages the user to have a password that is as easily memorized and typed as possible.  This makes it an easier password to guess; fewer iterations to attempt before success.  It may also force the user into reusing the same password for every website.  Reusing passwords is bad because if one website is compromised, all other websites are wide open.  If the user is conscientious about using unique passwords, they are then forced to write down the passwords and post them in an easily-accessible but also vulnerable location in their cubicle at work or desk at home where everyone in your office or everyone who visits or breaks into your home can access it.

Stretchable Tiled Layout

Posted on Updated on

With the Oracle ADF Faces Rich Client, have you ever wanted to divide a stretchable box into tiles?

Divided in Half

Let’s say you just wanted to divide a stretchable area in half, the top half gets 50% of the space and the bottom half gets the other 50%.  If your stretchable area is one created by a component that stretches its children (please read Layout Basics if this term doesn’t mean anything to you), you achieve this half-and-half layout by using a panelStretchLayout:

<af:panelStretchLayout id="half" topHeight="50%"
  dimensionsFrom="parent">
  <f:facet name="top">
    <!-- top half component rooted here -->
  </f:facet>
  <f:facet name="center">
    <!-- bottom half component rooted here
         (the center facet gets the remaining space) -->
  </f:facet>
</af:panelStretchLayout>

Remember, avoid inlineStyle if at all possible; do you see any inlineStyle values here?

Divided into Thirds

What if you wanted it to be divided into thirds?  Just add one more facet to the panelStretchLayout like this:

<af:panelStretchLayout id="thirds" topHeight="33%" bottomHeight="33%"
  dimensionsFrom="parent">
  <f:facet name="top">
    <!-- top third component rooted here -->
  </f:facet>
  <f:facet name="center">
    <!-- middle third component rooted here
         (the center facet gets the remaining space) -->
  </f:facet>
  <f:facet name="bottom">
    <!-- bottom third component rooted here -->
  </f:facet>
</af:panelStretchLayout>

Divided into Quarters

Okay so now what are you gonna do–the panelStretchLayout doesn’t have any more facets to choose from that would appear below “bottom”?!

Well, now you need to nest panelStretchLayouts where the inner panelStretchLayout uses 3 facets (space divided into thirds) and an outer panelStretchLayout that uses 2 facets (space divided into 25% and 75%) like this:

<af:panelStretchLayout id="outer" topHeight="25%"
  dimensionsFrom="parent">
  <f:facet name="top">
    <!-- first quarter component rooted here -->
  </f:facet>
  <f:facet name="center">
    <af:panelStretchLayout id="inner" topHeight="33%" bottomHeight="33%"
      dimensionsFrom="parent">
      <f:facet name="top">
        <!-- second quarter component rooted here -->
      </f:facet>
      <f:facet name="center">
        <!-- third quarter component rooted here
             (the center facet gets the remaining space) -->
      </f:facet>
      <f:facet name="bottom">
        <!-- fourth quarter component rooted here -->
      </f:facet>
    </af:panelStretchLayout>
  </f:facet>
</af:panelStretchLayout>

These all line up vertically, what if I wanted horizontal tiles?

Instead of using “top” and “bottom” facets, use “start” and “end”.

What if I don’t have a stretchable area?

Without being stretchable, you cannot evenly distribute space.  You would then use something like an Apache MyFaces Trinidad trh:tableLayout (as seen in the Tiled Flowing demo or even a layout=”vertical” af:panelGroupLayout.

Are there any other tiled layout solutions?

Yes, for the stretchable area case, you could:

  • Use panelSplitters instead of panelStretchLayouts though splitter panels don’t use percentage units–only pixels, or
  • Use panelGridLayout (don’t forget halign=”stretch” and valign=”stretch” on your gridCell components).
  • Use grid structure to generate the tiles as seen in the Tiled Stretching demo.

For more info, refer to the demos, documentation, forums, news, etc. on the Oracle ADF Faces Rich Client Components page on the Oracle Technology Network:

CSS Spec Proposals for Transforms, Transitions, and Animation

Posted on Updated on

The folks at webkit.org are adopting animation support that has been present in mobile Safari since iPhone 2.0. They are also proposing specs for CSS-based transforms, transitions, and animations:

See some animations in action using the latest nightly WebKit at:

Firefox 3.1 is also adopting WebKit’s proposal for supporting transforms too:

And of course there is also the cryptic filters that IE supports for transforms:

It will be nice to have all of these varying implementations conform to a standard.

Woodstock Migration to ADF Faces Rich Client

Posted on

There is now a tutorial for migrating from Sun’s Woodstock JSF component into Oracle ADF Faces Rich Client components:

http://www.oracle.com/technology/products/adf/adffaces/woodstock2adfMatix.html