Foswiki on GitHub is open for business! Next release meeting: Monday September 15, 1300Z

Item11397: Write EXPORTEDPREFERENCES as JSON instead of meta tags

Priority: CurrentState: AppliesTo: Component: WaitingFor:
Normal Closed Extension JQueryPlugin  
In html5 custom meta tags are no longer allowed. With a custom tag, the validator will give an error like:
Bad value foswiki.WIKIUSERNAME for attribute name on element meta: Keyword foswiki.wikiusername is not registered.

These values could as well be written as a piece of JSON text:
foswiki.data.meta = {
   TOPIC: "WebHome",
   WEB: "Main",
   etc.
}

Affected files:
lib/Foswiki/Plugins/JQueryPlugin/FOSWIKI.pm (writes)
pub/System/JQueryPlugin/plugins/foswiki/jquery.foswiki.js (reads)
pub/System/JavascriptFiles/foswikilib (reads)

As fallback (for the updated plugin working on older Foswiki installations) the javascript code could check for the existence of foswiki.data.meta and use the old meta parsing if not found.

-- ArthurClemens - 30 Dec 2011

+1. I did something similar (use JSON instead of <meta> tag) with TinyMCEPlugin ages ago, actually to cure some I18N/encoding/unicode issues. Keeping everything as JSON keeps it simple(r).

-- PaulHarvey - 30 Dec 2011

By now jquery.foswiki caches all data to be moved from perl to javascript in the foswiki.preferences object. I don't see a need to rename foswiki.preferences to foswiki.data. We should keep it like this.

The access to this object is guarded by the foswiki.getPreference() method. It takes care of reading foswiki.preferences properly as well as loading additional data not included in EXPORTEDPREFERENCES using an ajax call to the foswiki server and cache it in foswiki.preferences.

That said, I don't see any problem in generating the foswiki.preferences right away using a json snipped as part of the html page instead of a set of meta tags.

The only problem is extensibility!

For now plugins could just add more meta tags to the head using a normal ADDTOZONE{"head" text="meta tag"}. In contrast, writing out a foswiki.preferences json object needs more care taking, maybe by defining a preferences zone in itself that anybody could write to using an appropriate ADDTOZONE{"preferences=" text="some json property"} call. These are all collected and rendered using some RENDERZONE{"preferences" head="foswiki.preferences = {" separator="," footer="}"}.

-- MichaelDaum - 30 Dec 2011

So we have a couple of objectives:
  • plugins call "add to zone: preference", containing a key/value pair
  • all zone preferences are collected in a list
  • at writeCompletePage the preferences are rendered as JSON: foswiki.preferences = {}, surrounded by script tags

So instead of the addToZone providing the script tags, the rendering is delegated to renderZone.

Instead of making the rendering of preferences a special case, this principle could be reused by adding a type attribute to addToZone.

Each type will be rendered specifically. For instance:
  • type="css" will create surrounding style tags
  • type="cssfile" will create style src="" tags
  • type="json" will create style src="" tags
  • type="jsonvalue" will create a JSON property for each key/value

Likewise
  • type="preference" will create a JSON property for each key/value, and surrounding foswiki.preferences={...} code

That also introduces the concept of lists. Currently a zone with a previously used id will overwrite the older zone. It could be useful instead to create a list of zone elements with the same id.

In that case it would be possible to use id as the Javascript variable name:
addToZone( 'preference', 'foswiki.preferences', { name='SYSTEMWEB', value='System' } )

  • For 'preferences', type='jsonvalue' would be assumed
  • The id foswiki.preferences could also be assumed, but not for other uses of type="jsonvalue"
  • The data property for addToZone is overloaded with a hash

-- ArthurClemens - 02 Jan 2012

I'm wary of doing something like that, because the transition will be painful (ZonePlugin all over again)...

Something like
%ADDTOZONE{"jsonpreferences", text="key: \"value\""}%

... isn't as elegant, but still workable.

%RENDERZONE{
  "jsonpreferences"
  header="<script>foswiki.preferences = {$n"
  format="  $item"
  separator=",$n"
  footer="};</script>"
}%

Or, can we use the LIBJS approach, i.e. %TMPL:P{"foswiki.preferences" name="SYSTEMWEB" value="System"}% approach again - at least then we could ship the necessary templates with JQueryPlugin, to ease the upgrade pain.

-- PaulHarvey - 02 Jan 2012

That leaves a lot of specifics open to the programmer or template author. Currently he just has to write %RENDERZONE{"script"}%.

The preferences are currently used by JQueryPlugin, so rendering should be done by the plugin as well. But the plugin does not provide any templates to do that.

In all it would be ideal if the preferences are written to head ('script' zone) automatically.

Why would it be an transition problem if Foswiki 2.0 ships with a type parameter? We can do a version check in the perl code.

Ok, it is nasty to get this right.

I am now opting to provide default %TMPL:DEF{"renderzone"}% variants:
%TMPL:DEF{"renderzone" zone="head"}%%RENDERZONE{"%zone%"}%%TMPL:END%

%TMPL:DEF{"renderzone:script"}%%RENDERZONE{"script"}%%TMPL:END%

%TMPL:DEF{"renderzone:head"}%%RENDERZONE{"head"}%%TMPL:END%

%TMPL:DEF{"renderzone:jsonpreference"}%%RENDERZONE{
   "jsonpreference"
   header="<script type='text/javascript'>
foswiki.preferences = {
"
   footer="$n};$n</script>"
   type="string"
   format="   $item"
   separator=",$n"
}%%TMPL:END%

This should be included in the lowest level template foswiki.tmpl, or provided in new template zoneformats.tmpl (and TMPL:INCLUDEd in foswiki.tmpl).

Change to FOSWIKI.pm:
    if ( $Foswiki::Plugins::VERSION < 2.1 ) {

        # add exported preferences to head using META tags
        my $text = '';
        foreach my $pref ( split( /\s*,\s*/, $prefs ) ) {
            $text .=
                '<meta name="foswiki.' 
              . $pref
              . '" content="%ENCODE{"%'
              . $pref
              . '%"}%" />'
              . " <!-- $pref -->\n";
        }

        Foswiki::Func::addToZone( "head", "JQUERYPLUGIN::FOSWIKI::META",
            $text );
    }
    else {
    
        # add exported preferences to head using a JSON object
        foreach my $pref ( split( /\s*,\s*/, $prefs ) ) {
            my $jsonPreference =
              '"' . $pref . '": "' . '%ENCODE{"%' . $pref . '%"}%"';
            Foswiki::Func::addToZone( "jsonpreference", undef,
                $jsonPreference );
        }
    }

-- ArthurClemens - 03 Jan 2012

Why still produce meta tags for older foswikis? Both styles can be mixed freely. There isn't even a hurry to change plugins to make use of the new preference zone.

-- MichaelDaum - 04 Jan 2012

Because the jsonpreferences need to get rendered, and that won't be done automatically in older FW.

-- ArthurClemens - 04 Jan 2012

There's no need to render all preferences that are exported from foswiki to javascript using a single json object. Instead, plugins can use $.extend() to incrementally add to the foswiki.preferences object.

The initial stub is created like this:

<script>
jQuery.extend(foswiki, {
 "preferences": {
 ...
  }
});
</script>

Plugins can then add something like this to the script zone:

<script>
jQuery(function($) {
  $.extend(foswiki.preferences, {
   ...
  });
});
</script>

A positive sideeffect of moving from meta to JSON is that we don't need to encapsulate preferences into an %ENCODE anymore, which in turn breaks utf8 encoded values ... a bug in its own.

-- MichaelDaum - 10 Apr 2012

But how are the preferences fed to the js foswiki object if we don't use meta tags?

Oh, I see, it is now done automatically by the plugin.

-- ArthurClemens - 10 Apr 2012
 

ItemTemplate edit

Summary Write EXPORTEDPREFERENCES as JSON instead of meta tags
ReportedBy ArthurClemens
Codebase
SVN Range
AppliesTo Extension
Component JQueryPlugin
Priority Normal
CurrentState Closed
WaitingFor
Checkins Foswikirev:14589 Foswikirev:15191
TargetRelease patch
ReleasedIn 1.1.6
CheckinsOnBranches Release01x01 trunk
trunkCheckins Foswikirev:14589
Release01x01Checkins Foswikirev:15191
Topic revision: r18 - 02 Dec 2012, GeorgeClark
 
The copyright of the content on this website is held by the contributing authors, except where stated elsewhere. see CopyrightStatement. Creative Commons License