mark: A photo of Mark kneeling on top of the Taal Volcano in the Philippines. It was a long hike. (Default)
Mark Smith ([staff profile] mark) wrote in [site community profile] changelog2009-03-22 12:39 am

[dw-free] Allow S2 properties to be grouped visually in the s2 editor

[commit: http://hg.dwscoalition.org/dw-free/rev/cab281adf877]

http://bugs.dwscoalition.org/show_bug.cgi?id=536

Initial support for grouped S2 properties in the wizard.

Patch by [personal profile] afuna.

Files modified:
  • bin/upgrading/s2layers/core2.s2
  • cgi-bin/LJ/Customize.pm
  • cgi-bin/LJ/Widget/S2PropGroup.pm
  • htdocs/stc/widgets/s2propgroup.css
--------------------------------------------------------------------------------
diff -r 68412c5257d5 -r cab281adf877 bin/upgrading/s2layers/core2.s2
--- a/bin/upgrading/s2layers/core2.s2	Sun Mar 22 00:31:38 2009 +0000
+++ b/bin/upgrading/s2layers/core2.s2	Sun Mar 22 00:39:27 2009 +0000
@@ -950,6 +950,7 @@ propgroup text = "Text";
 propgroup text = "Text";
 propgroup background = "Background";
 propgroup sidebar = "Sidebar";
+propgroup modules = "Modules";
 propgroup appearance = "Appearance";
 propgroup options = "Options";
 propgroup images = "Images";
@@ -997,6 +998,7 @@ property builtin string PALIMGROOT {
     doc_flags = "[sys]";
     des = "The base URL of palimg files, without a trailing slash.  Example: \"http://www.dreamwidth.com/palimg\".";
 }
+
 
 ##===============================
 ## Display settings - general
@@ -1017,28 +1019,83 @@ set num_items_recent = 20;
 set num_items_recent = 20;
 set num_items_reading = 20;
 
+property string[] reverse_sortorder_group {
+    des = "Display entries in reverse-chronological order on my:";
+    grouptype = "viewslist";
+}
+set reverse_sortorder_group = [ "reverse_sortorder_day", "reverse_sortorder_year" ];
 #property bool reverse_sortorder_recent    { des = "Display entries in reverse-chronological order on my recent entries page"; }
-property bool reverse_sortorder_day        { des = "Display entries in reverse-chronological order on my day page"; }
-property bool reverse_sortorder_year       { des = "Display entries in reverse-chronological order on my year archive"; }
+property bool reverse_sortorder_day { 
+    des = "Display entries in reverse-chronological order on my day page";  
+    label = "Day Page";    
+    grouped = 1;
+}
+property bool reverse_sortorder_year { 
+    des = "Display entries in reverse-chronological order on my year archive"; 
+    label = "Year Archive";
+    grouped = 1;
+}
 #property bool reverse_sortorder_reading   { des = "Display entries in reverse-chronological order on my reading page"; }
 #set reverse_sortorder_recent = true;
 set reverse_sortorder_day = false;
 set reverse_sortorder_year = false;
 #set reverse_sortorder_reading = true;
 
-property bool show_userpics_recent      { des = "Display userpics on my recent entries"; }
-property bool show_userpics_reading     { des = "Display userpics on my reading page"; }
-property bool show_userpics_entry       { des = "Display userpics on my entry pages"; }
-property bool show_userpics_comments    { des = "Display userpics on my comments"; }
+property string[] show_userpics_group {
+    des = "Display userpics on my:";
+    grouptype = "viewslist";
+}
+set show_userpics_group = [ "show_userpics_recent", "show_userpics_reading", "show_userpics_entry", "show_userpics_comments" ];
+property bool show_userpics_recent {
+    des = "Display userpics on my recent entries";
+    label = "Recent Entries";
+    grouped = 1;
+}
+property bool show_userpics_reading {
+    des = "Display userpics on my reading page"; 
+    label = "Reading Page";
+    grouped = 1;
+}
+property bool show_userpics_entry {
+    des = "Display userpics on my entry pages";
+    label = "Entry Pages";
+    grouped = 1;
+}
+property bool show_userpics_comments {
+    des = "Display userpics on my comments";
+    label = "Comments";
+    grouped = 1;
+}
 set show_userpics_recent = true;
 set show_userpics_reading = true;
 set show_userpics_entry = true;
 set show_userpics_comments = true;
 
-property bool show_sidebar_recent       { des = "Display my sidebar on my recent entries"; }
-property bool show_sidebar_reading      { des = "Display my sidebar on my reading page"; }
-property bool show_sidebar_entry        { des = "Display my sidebar on my entry pages"; }
-property bool show_sidebar_others       { des = "Display my sidebar on other pages"; }
+property string[] show_sidebar_group {
+    des = "Display my sidebar on my:";
+    grouptype = "viewslist";
+}
+set show_sidebar_group = [ "show_sidebar_recent", "show_sidebar_reading", "show_sidebar_entry", "show_sidebar_others" ];
+property bool show_sidebar_recent {
+    des = "Display my sidebar on my recent entries";
+    label = "Recent Entries";
+    grouped = 1;
+}
+property bool show_sidebar_reading {
+    des = "Display my sidebar on my reading page";
+    label = "Reading Page";
+    grouped = 1;
+}
+property bool show_sidebar_entry {
+    des = "Display my sidebar on my entry pages";
+    label = "Entry Pages";
+    grouped = 1;
+}
+property bool show_sidebar_others {
+    des = "Display my sidebar on other pages";
+    label = "Other Pages";
+    grouped = 1;
+}
 set show_sidebar_recent = true;
 set show_sidebar_reading = true;
 set show_sidebar_entry = true;
@@ -1051,21 +1108,38 @@ property bool use_custom_friend_colors  
 property bool use_custom_friend_colors  { des = "Use my custom friend colors"; }
 set use_custom_friend_colors = false;
 
+
+property string[] entry_datetime_format_group {
+    des = "Entry date/time format";
+    grouptype = "datetime";
+}
+set entry_datetime_format_group = ["entry_date_format", "entry_time_format"];
 property string entry_date_format { 
     des = "Entry date format";
     values = "short|2/5/80|med|Feb. 5th, 1980|med_day|Tue, Feb. 5th, 1980|long|February 5th, 1980|long_day|Tuesday, February 5th, 1980";
+    grouped = 1;
 }
 property string entry_time_format {
     des = "Entry time format.";
     values = "short|12:34am";
-}
+    grouped = 1;
+}
+
+
+property string[] comment_datetime_format_group {
+    des = "Comment date/time format";
+    grouptype = "datetime";
+}
+set comment_datetime_format_group = ["comment_date_format", "comment_time_format"];
 property string comment_date_format { 
     des = "Comment date format";
     values = "short|2/5/80|med|Feb. 5th, 1980|med_day|Tue, Feb. 5th, 1980|long|February 5th, 1980|long_day|Tuesday, February 5th, 1980";
+    grouped = 1;
 }
 property string comment_time_format {
     des = "Comment time format.";
     values = "short|12:34am";
+    grouped = 1;
 }
 set entry_date_format = "short";
 set entry_time_format = "short";
@@ -1132,6 +1206,82 @@ set sidebar_primary = []; # Set in prop_
 set sidebar_primary = []; # Set in prop_init()
 set sidebar_secondary = []; # Set in prop_init()
 
+##===============================
+## Display settings - modules
+##===============================
+property string[] module_userprofile_group {
+    des = "Basic Journal Info";
+    grouptype = "module";
+}
+set module_userprofile_group = ["module_userprofile_show", "module_userprofile_order", "module_userprofile_opts_group"];
+property bool module_userprofile_show   { grouped = 1; }
+property int  module_userprofile_order  { grouped = 1; }
+
+property string[] module_userprofile_opts_group {
+    grouptype = "moduleopts";
+    grouped = 1;
+}
+set module_userprofile_opts_group = ["module_userprofile_opts_userpic", "module_userprofile_opts_name", "module_userprofile_opts_website"];
+property bool module_userprofile_opts_userpic   { grouped = 1; label = "Default Userpic"; }
+property bool module_userprofile_opts_name      { grouped = 1; label = "Display Name"; }
+property bool module_userprofile_opts_website   { grouped = 1; label = "Website"; }
+
+property string[] module_navlinks_group {
+    des = "Navigation";
+    grouptype = "module";
+}
+set module_navlinks_group = ["module_navlinks_show", "module_navlinks_order"];
+property bool module_navlinks_show    { grouped = 1; }
+property int  module_navlinks_order   { grouped = 1; }
+
+property string[] module_calendar_group {
+    des = "Calendar";
+    grouptype = "module";
+}
+set module_calendar_group = ["module_calendar_show", "module_calendar_order"];
+property bool module_calendar_show    { grouped = 1; }
+property int  module_calendar_order   { grouped = 1; }
+
+property string[] module_pagesummary_group {
+    des = "Page Summary";
+    grouptype = "module";
+}
+set module_pagesummary_group = ["module_pagesummary_show", "module_pagesummary_order"];
+property bool module_pagesummary_show   { grouped = 1; }
+property int  module_pagesummary_order  { grouped = 1; }
+
+property string[] module_tags_group {
+    des = "Tags";
+    grouptype = "module";
+}
+set module_tags_group = ["module_tags_show", "module_tags_order", "module_tags_opts_group"];
+property bool module_tags_show  { grouped = 1; }
+property int  module_tags_order { grouped = 1; }
+
+property string[] module_tags_opts_group {
+    grouptype = "moduleopts";
+    grouped = 1;
+}
+set module_tags_opts_group = ["module_tags_opts_cloud", "module_tags_opts_list", "module_tags_opts_multilevel"];
+property bool module_tags_opts_cloud        { grouped = 1; label = "Cloud"; }
+property bool module_tags_opts_list         { grouped = 1; label = "List"; }
+property bool module_tags_opts_multilevel   { grouped = 1; label = "Multilevel"; }
+
+property string[] module_links_group {
+    des = "Link List";
+    grouptype = "module";
+}
+set module_links_group = ["module_links_show", "module_links_order"];
+property bool module_links_show     { grouped = 1; }
+property int  module_links_order    { grouped = 1; }
+
+property string[] module_syndicate_group {
+    des = "Syndication";
+    grouptype = "module";
+}
+set module_syndicate_group = ["module_syndicate_show", "module_syndicate_order"];
+property bool module_syndicate_show    { grouped = 1; }
+property int  module_syndicate_order   { grouped = 1; }
 
 ##===============================
 ## Journal style - fonts
@@ -1179,19 +1329,56 @@ property string font_module_text {
 ## Journal style - images
 ##===============================
 
-# FIXME: Want URL, tiling, alignment. Width and height?
-property string image_background_url {
+property string[] background_image_group {
     des = "Background";
-}
-
-property string image_header_url {
+    grouptype = "image";
+}
+set background_image_group = [ "background_image_url", "background_image_repeat", "background_image_position" ];
+property string background_image_url {
+    grouped = 1;
+}
+property string background_image_repeat {
+    values = "repeat|tile image|no-repeat|don't tile|repeat-x|tile horizontally|repeat-y|tile vertically";
+    grouped = 1;
+}
+property string background_image_position {
+    values = "top left|top left|center|center|top right|top right| bottom left|bottom left|bottom right|bottom right";
+    grouped = 1;
+}
+
+property string[] header_image_group {
     des = "Header";
-}
-
-property string image_separator_url {
+    grouptype = "image";
+}
+set header_image_group = [ "header_image_url", "header_image_repeat", "header_image_position" ];
+property string header_image_url {
+    grouped = 1;
+}
+property string header_image_repeat {
+    values = "repeat|tile image|no-repeat|don't tile|repeat-x|tile horizontally|repeat-y|tile vertically";
+    grouped = 1;
+}
+property string header_image_position {
+    values = "top left|top left|center|center|top right|top right| bottom left|bottom left|bottom right|bottom right";
+    grouped = 1;
+}
+
+property string[] separator_image_group {
     des = "Separator";
-}
-
+    grouptype = "image";
+}
+set separator_image_group = [ "separator_image_url", "separator_image_repeat", "separator_image_position" ];
+property string separator_image_url {
+    grouped = 1;
+}
+property string separator_image_repeat {
+    values = "repeat|tile image|no-repeat|don't tile|repeat-x|tile horizontally|repeat-y|tile vertically";
+    grouped = 1;
+}
+property string separator_image_position {
+    values = "top left|top left|center|center|top right|top right| bottom left|bottom left|bottom right|bottom right";
+    grouped = 1;
+}
 
 ##===============================
 ## Journal style - colors
diff -r 68412c5257d5 -r cab281adf877 cgi-bin/LJ/Customize.pm
--- a/cgi-bin/LJ/Customize.pm	Sun Mar 22 00:31:38 2009 +0000
+++ b/cgi-bin/LJ/Customize.pm	Sun Mar 22 00:39:27 2009 +0000
@@ -460,8 +460,11 @@ sub save_s2_props {
     $lyr_layout->{'uniq'} = $dbh->selectrow_array("SELECT value FROM s2info WHERE s2lid=? AND infokey=?",
                                               undef, $lyr_layout->{'s2lid'}, "redist_uniq");
 
+    my @grouped_properties = S2::get_properties( $lyr_core->{s2lid} );
+    @grouped_properties = grep { $_->{grouped} == 1 } @grouped_properties;
+
     my %override;
-    foreach my $prop (S2::get_properties($lyr_layout->{'s2lid'}))
+    foreach my $prop ( S2::get_properties( $lyr_layout->{s2lid} ), @grouped_properties )
     {
         $prop = S2::get_property($lyr_core->{'s2lid'}, $prop)
             unless ref $prop;
@@ -542,7 +545,11 @@ sub get_propgroups {
 
     my %prop;  # name hashref, deleted when added to a category
     my @propnames;
-    foreach my $prop (S2::get_properties($lyr_layout->{'s2lid'})) {
+    
+    my @grouped_properties = S2::get_properties( $lyr_core->{s2lid} );
+    @grouped_properties = grep { $_->{grouped} == 1 } @grouped_properties;
+
+    foreach my $prop ( S2::get_properties( $lyr_layout->{s2lid} ), @grouped_properties ) {
         unless (ref $prop) {
             $prop = S2::get_property($lyr_core->{'s2lid'}, $prop);
             next unless ref $prop;
diff -r 68412c5257d5 -r cab281adf877 cgi-bin/LJ/Widget/S2PropGroup.pm
--- a/cgi-bin/LJ/Widget/S2PropGroup.pm	Sun Mar 22 00:31:38 2009 +0000
+++ b/cgi-bin/LJ/Widget/S2PropGroup.pm	Sun Mar 22 00:39:27 2009 +0000
@@ -52,7 +52,7 @@ sub render_body {
             } else {
                 $row_class = $count % 2 == 0 ? " graybg" : "";
             }
-            $ret .= $class->output_prop($props->{$prop_name}, $prop_name, $row_class, $u, $style, $theme);
+            $ret .= $class->output_prop( $props->{$prop_name}, $prop_name, $row_class, $u, $style, $theme, $props );
             $count++;
         }
         $ret .= "</table>";
@@ -70,7 +70,7 @@ sub render_body {
             }
             $header_printed = 1;
             $row_class = $count % 2 == 0 ? " graybg" : "";
-            $ret .= $class->output_prop($props->{$prop_name}, $prop_name, $row_class, $u, $style, $theme);
+            $ret .= $class->output_prop( $props->{$prop_name}, $prop_name, $row_class, $u, $style, $theme, $props );
             $count++;
         }
         $ret .= "</table>" if $header_printed;
@@ -117,7 +117,7 @@ sub render_body {
                 }
 
                 $row_class = $count % 2 == 0 ? " graybg" : "";
-                $ret .= $class->output_prop($props->{$prop_name}, $prop_name, $row_class, $u, $style, $theme);
+                $ret .= $class->output_prop( $props->{$prop_name}, $prop_name, $row_class, $u, $style, $theme, $props );
                 $count++;
             }
             $ret .= "</table>" if $header_printed;
@@ -206,12 +206,45 @@ sub output_prop {
     my $u = shift;
     my $style = shift;
     my $theme = shift;
-
+    my $props = shift;
+    
     # for themes that don't use the linklist_support prop
     my $linklist_tab;
     if (!$prop && $prop_name eq "linklist_support") {
         $linklist_tab = $theme->linklist_support_tab;
     }
+    
+    my $ret;
+    $ret .= "<tr class='prop-row$row_class' width='100%'>";
+
+    if ($linklist_tab) {
+        $ret .= "<td colspan='100%'>" . $class->ml( 'widget.s2propgroup.linkslisttab', {'name' => $linklist_tab} ) . "</td>";
+        $ret .= "</tr>";
+        return $ret;
+    }
+
+    $ret .= "<td class='prop-header'>" . LJ::eall( $prop->{des} ) . " " . LJ::help_icon( "s2opt_$prop->{name}" ) . "</td>"
+        unless $prop->{type} eq "Color" || $prop->{type} eq "string[]";
+
+   $ret .= $class->output_prop_element( $prop, $prop_name, $u, $style, $theme, $props );
+
+    my $note = "";
+    $note .= LJ::eall( $prop->{note} ) if $prop->{note};
+    $ret .= "</tr><tr class='prop-row-note$row_class'><td colspan='100%' class='prop-note'>$note</td>" if $note;
+
+    $ret .= "</tr>";
+    return $ret;
+}
+
+sub output_prop_element {
+    my $class = shift;
+    my $prop = shift;
+    my $prop_name = shift;
+    my $u = shift;
+    my $style = shift;
+    my $theme = shift;
+    my $props = shift;
+    my $is_group = shift;
 
     my $name = $prop->{name};
     my $type = $prop->{type};
@@ -227,29 +260,56 @@ sub output_prop {
 
     $existing_display = LJ::eall($existing_display);
 
+    return if $prop->{grouped} && ! $is_group;
+
     my $ret;
-    $ret .= "<tr class='prop-row$row_class' width='100%'>";
+    # visually grouped properties. Allow nesting to only two levels
+    if ( $type eq "string[]" && $is_group < 2 ) {    
 
-    if ($linklist_tab) {
-        $ret .= "<td colspan='100%'>" . $class->ml('widget.s2propgroup.linkslisttab', {'name' => $linklist_tab}) . "</td>";
-        $ret .= "</tr>";
-        return $ret;
-    }
+        if ( $prop->{grouptype} eq "module" ) {
+            my $has_opts;
+            $ret .= "<td class='prop-grouped prop-$prop->{grouptype}' colspan=2>";
+            foreach my $prop_in_group ( @$override ) {
+                if ( $prop_in_group =~ /opts_group$/ ) {
+                    $has_opts = 1;
+                    next;
+                }
+                $ret .= $class->output_prop_element( $props->{$prop_in_group}, $prop_in_group, $u, $style, $theme, $props, $is_group + 1 );
+            }
+            
+            my $modulename = $prop->{name};
+            $modulename =~ s/_group$//;
+            
+            $ret .= "<label for='${modulename}_show'>" . LJ::eall( $prop->{des} )  ."</label>";
+            
+            $ret .= $class->output_prop_element( $props->{"${modulename}_opts_group"}, "${modulename}_opts_group", $u, $style, $theme, $props, $is_group + 1 ) if $has_opts;
 
-    $ret .= "<td class='prop-header'>" . LJ::eall($prop->{des}) . " " . LJ::help_icon("s2opt_$name") . "</td>"
-        unless $type eq "Color";
-
-    if ($prop->{values}) {
-        $ret .= "<td class='prop-input'>";
+            $ret .= "</td>";
+        } elsif ( $prop->{grouptype} eq "moduleopts" ) {
+            $ret .= "<ul class='prop-moduleopts'>";
+            foreach my $prop_in_group ( @$override ) {
+                $ret .= "<li>" . $class->output_prop_element( $props->{$prop_in_group}, $prop_in_group, $u, $style, $theme, $props, $is_group + 1 );
+            }
+            $ret .= "</ul>";
+        } else {
+            $ret .= "<td class='prop-grouped prop-$prop->{grouptype}' colspan=2><label class='prop-header'>" . LJ::eall( $prop->{des} ) . " " . LJ::help_icon( "s2opt_$prop->{name}" ) . "</label>";
+    
+            foreach my $prop_in_group ( @$override ) {
+                $ret .= $class->output_prop_element( $props->{$prop_in_group}, $prop_in_group, $u, $style, $theme, $props, $is_group + 1 );
+            }
+            $ret .= "</td>";
+        }
+    } elsif ( $prop->{values} ) {
+        $ret .= "<td class='prop-input'>" unless $is_group;
         $ret .= $class->html_select(
             { name => $name,
               disabled => ! $can_use,
               selected => $override, },
             split(/\|/, $prop->{values})
         );
-        $ret .= "</td>";
+        $ret .= "</td>" unless $is_group;
     } elsif ($type eq "int") {
-        $ret .= "<td class='prop-input'>";
+        $ret .= "<td class='prop-input'>" unless $is_group;
         $ret .= $class->html_text(
             name => $name,
             disabled => ! $can_use,
@@ -257,14 +317,15 @@ sub output_prop {
             maxlength => 5,
             size => 7,
         );
-        $ret .= "</td>";
+        $ret .= "</td>" unless $is_group;
     } elsif ($type eq "bool") {
-        $ret .= "<td class='prop-check'>";
+        $ret .= "<td class='prop-check'>" unless $is_group;
         $ret .= $class->html_check(
             name => $name,
             disabled => ! $can_use,
             selected => $override,
-            
+            label => $prop->{label},
+            id => $name,
         );
         
         # force the checkbox to be submitted, if the user unchecked it
@@ -275,13 +336,13 @@ sub output_prop {
             { disabled => ! $can_use }
         );
 
-        $ret .= "</td>";
+        $ret .= "</td>" unless $is_group;
     } elsif ($type eq "string") {
         my ($rows, $cols, $full) = ($prop->{rows}+0,
                                     $prop->{cols}+0,
                                     $prop->{full}+0);
 
-        $ret .= "<td class='prop-input'>";
+        $ret .= "<td class='prop-input'>" unless $is_group;
         if ($full > 0) {
             $ret .= $class->html_textarea(
                 name => $name,
@@ -311,9 +372,9 @@ sub output_prop {
                 size => $size,
             );
         }
-        $ret .= "</td>";
+        $ret .= "</td>" unless $is_group;
     } elsif ($type eq "Color") {
-        $ret .= "<td class='prop-color'>";
+        $ret .= "<td class='prop-color'>" unless $is_group;
         $ret .= $class->html_color(
             name => $name,
             disabled => ! $can_use,
@@ -322,18 +383,13 @@ sub output_prop {
             onchange => "Customize.CustomizeTheme.form_change();",
             no_btn => 1,
         );
-        $ret .= "</td>";
+        $ret .= "</td>" unless $is_group;
         $ret .= "<td>" . LJ::eall($prop->{des}) . " " . LJ::help_icon("s2opt_$name") . "</td>";
     }
-
+    
     my $offhelp = ! $can_use ? LJ::help_icon('s2propoff', ' ') : "";
     $ret .= " $offhelp";
 
-    my $note = "";
-    $note .= LJ::eall($prop->{note}) if $prop->{note};
-    $ret .= "</tr><tr class='prop-row-note$row_class'><td colspan='100%' class='prop-note'>$note</td>" if $note;
-
-    $ret .= "</tr>";
     return $ret;
 }
 
diff -r 68412c5257d5 -r cab281adf877 htdocs/stc/widgets/s2propgroup.css
--- a/htdocs/stc/widgets/s2propgroup.css	Sun Mar 22 00:31:38 2009 +0000
+++ b/htdocs/stc/widgets/s2propgroup.css	Sun Mar 22 00:39:27 2009 +0000
@@ -32,3 +32,23 @@
 .s2propgroup-outer-expandcollapse {
     display: none;
 }
+
+.prop-grouped label.prop-header {
+    float: left;
+    clear: both;
+    width: 20em;
+}
+
+.prop-viewslist label {
+    float: none; 
+}
+
+.prop-viewslist label.prop-header {
+    float: none;
+    display: block;
+    width: auto;
+}
+
+.prop-moduleopts {
+    margin-left: 10em;
+}
--------------------------------------------------------------------------------