#?SuikaWikiConfig/2.0

Plugin:
  @Name: PropEdit
  @Description:
    @@@: WikiPage Property Editor
    @@lang:en
  @License: %%Perl%%
  @Author:
    @@Name:
      @@@@: Wakaba
      @@@lang:ja
      @@@script:Latn
    @@Mail[list]: w@suika.fam.cx
  @Date.RCS:
    $Date: 2004/07/25 06:54:29 $
  @RequiredPlugin[list]:
    WikiEdit
    WikiFormCore
  @Namespace:
    @@media-type: 
      http://suika.fam.cx/~wakaba/-temp/2004/04/24/mt#
    @@pe:
      http://suika.fam.cx/~wakaba/archive/2004/7/20/sw-propedit#

PluginConst:
  @NS_XHTML1:
    http://www.w3.org/1999/xhtml

ViewDefinition:
  @Mode: pe--propedit
  @Condition:
    @@http-method[list]:
      GET
      HEAD
  @Description:
    @@@: Editing WikiPage Properties
    @@lang: en
  @template:
    @@http-status-code: 200
    @@media-type: text/html
    @@use-media-type-charset: 1
    @@expires: %%edit%%
    @@body:
	%html-document (
          title => {%res(name=>{PropEdit:WebPageTitle});}p,
          link-meta => {%template (name => links);
                        %html-meta(name => ROBOTS, content => NOINDEX);}p,
          content => {
            %template (
              name => ws--page,
              -content => {
                %section (
                  title => {%res (name => {PropEdit:Title});}p, heading,
                  content => {
                    %pe--propedit (template => {
                      %resource-as-plain-text (name => {PropEdit:Template});
                    }p);
                  }p,
                );
              },
            );
          }p,
        );

ViewDefinition:
  @Mode: -pe--conflict
  @Condition:
    @@http-method[list]:
      GET
  @Description:
    @@@: Editing WikiPage Properties -- Report Confliction
    @@lang: en
  @template:
    @@http-status-code: 409
    @@media-type: text/html
    @@use-media-type-charset: 1
    @@expires: %%error%%
    @@body:
	%html-document (
          title => {%res(name=>{PropEdit:Conflict:WebPageTitle});}p,
          link-meta => {%template (name => links);
                        %html-meta(name => ROBOTS, content => NOINDEX);}p,
          content => {
            %template (
              name => ws--page,
              -content => {
                %section (
                  title => {%res (name => {PropEdit:Conflict:Title});}p, heading,
                  content => {
                    %paragraph (content => {
                      %res (name => {PropEdit:Conflict:Description});
                    }p);
                    %pe--propedit (template => {
                    %resource-as-plain-text (name => {PropEdit:Template});
                  }p,
                );
              }p);},
            );
          }p,
        );

FormattingRule:
  @Category[list]: view
  @Name: pe--propedit
  @Description:
    @@@: A property editor form
    @@lang: en
  @Parameter:
    @@Name: page
    @@Type: WikiName
    @@Default: (auto)
    @@Description:
      @@@@: WikiPage to be edited
      @@@lang:en
  @Parameter:
    @@Name: template
    @@Type: template
    @@Default: (required)
    @@Description:
      @@@@: Template of the content
      @@@lang: en
  @Formatting:
    __ATTRTEXT:%page__;__ATTRTEXT:%template__;
    my $page = $o->{wiki}->name ($p->{page} || $o->{wiki}->{var}->{page});
    my $template = $p->{template};
    my $content = __FUNCPACK{WikiEdit}__->get_content ($o, $page);
    local $o->{var}->{content} = $content->{content};
    local $o->{var}->{content_prop} = $content->{content_prop};
    local $o->{var}->{source}->{prop} = sub {
      my %opt = @_;
      my $key = $opt{p}->{id};
      my $uri = $opt{o}->{wiki}->{config}->{<Q:pe:prop>}->{$key}->{uri};
      my $rv = {};
      if ($uri) {
        if (my $nv = $opt{o}->{wiki}->{input}->parameter ('PE--CTRL--'.$key)) {
          $rv->{value} = $nv;
          $rv->{class} = [qw/pe--from-parameter/];
        } else {
          $rv->{value} = $opt{o}->{var}->{content_prop}
                        ->get_attribute_value ($uri,
                                               default => $opt{p}->{default});
        }
      } else {
        $rv->{value} = $opt{p}->{default};
      }
      $rv;
    };
    __FUNCPACK{WikiFormCore}__->make_form_in_html
                         ($p->{-parent}, $template,
                          wiki => $o->{wiki},
                          o => $o,
                          index => -1,
                          local_id_prefix => 'PE--CTRL--',
                          output => {
                            mode => 'pe--write',
                            page => $page,
                            submit_button => 1,
                            hidden => sub {
    my ($hidden, $o) = @_;
    ## Embed hidden sequencial number to detect confliction
    for ($hidden->append_new_node (type => '#element',
                                   namespace_uri => $NS_XHTML1,
                                   local_name => 'input')) {
      $_->set_attribute (type => 'hidden');
      $_->set_attribute (name => 'pe--seq');
      $_->set_attribute (value => $o->{var}->{content_prop}
	                            ->get_attribute_value (<Q:pe:seq>,
                                                           default => 0));
      $_->option (use_EmptyElemTag => 1);
    }
                            },
                          });

ViewFragment:
  @Name: links
  @Description:
    @@@: Link to property edit mode of the WikiPage
    @@lang:en
  @Formatting:
    %link-wiki(mode => pe--propedit, up-to-date,
               rel => edit, class => wiki-cmd,
               description => {%res(name=>{PropEdit:Link:Description});}p);

ViewDefinition:
  @Mode: pe--write
  @Condition:
    @@http-method[list]:
      POST
  @Description:
    @@@: Saving modified (new) WikiPage properties
    @@lang: en
  @Use:
    use Message::Util::Error;
  @method:
    @@Name: main
    @@@:
      my $wiki = $self->{view}->{wiki};
      $wiki->{var}->{db}->{read_only}->{'content'} = 1;
      $wiki->{var}->{db}->{read_only}->{'content_prop'} = 0;
      $wiki->{var}->{db}->{read_only}->{'lastmodified'} = 1;
      $wiki->{var}->{db}->{read_only}->{'referer'} = 0;
      $wiki->init_db;

      my $page = $wiki->{var}->{page};
      my $old_content = $wiki->{db}->get (content => $page);
      my $prop = $wiki->{db}->get (content_prop => $page);

      ## Check confliction
      my $seq = $wiki->{input}->parameter ('pe--seq') || 0;
      if ($seq != $prop->get_attribute_value (<Q:pe:seq>, default => 0)) {
        $self->{view}->{wiki}->view_in_mode (mode => '-pe--conflict');
        throw SuikaWiki::DB::Util::Error -type => 'ERROR_REPORTED';
      }

      MODIFICATION: {
      
      my $modified = 0;
      my $old_mt;
      my $new_mt;
      my %depend;
      KEY: for my $key (grep s/^PE--CTRL--//, $wiki->{input}->parameter_names) {
        my $propinfo = $wiki->{config}->{<Q:pe:prop>}->{$key};
        next KEY unless $propinfo->{uri};

        ## Really modified?
        my $new_value = [$wiki->{input}->parameter ('PE--CTRL--'.$key)];
        if (@$new_value == 1) {
          if ($propinfo->{is_list}) {
            $new_value = [split /\x0A|\x0D\x0A?/, $new_value->[0]];
          }
          if ($propinfo->{type} eq <Q:media-type:media-type>) {
            next KEY if $new_value->[0] eq '#asis';
          }
        }
        
        my $old_value = $prop->get_attribute_value ($propinfo->{uri});
        if (ref $old_value and @$new_value == @$old_value) {
          CHK: {
            for my $i (0..$#$new_value) {
              last CHK if $old_value->[$i] ne $new_value->[$i];
            }
            next KEY;
          }
        } elsif (not ref $old_value and @$new_value == 1) {
          next KEY if $old_value eq $new_value->[0];
        }
        if ($propinfo->{uri} eq <Q:media-type:media-type>) {
          $old_mt = $old_value;
          $new_mt = $new_value->[0];
        }
        if (@$new_value) {
          if ($propinfo->{is_list}) {
            $prop->set_attribute ($propinfo->{uri} => $new_value);
          } elsif (length $new_value->[0]) {
            $prop->set_attribute ($propinfo->{uri} => $new_value->[0]);
          } else {
            $prop->remove_attribute ($propinfo->{uri});
          }
        } else {
          $prop->remove_attribute ($propinfo->{uri});
        }
        $modified = 1;
        $depend{$key} = $propinfo->{depend} if $propinfo->{depend};
      }
      DEPEND: for my $sub (keys %depend) {
        for my $main (@{$depend{$sub}}) {
          unless ($prop->get_attribute ($wiki->{config}->{<Q:pe:prop>}
                                             ->{$main}->{uri})) {
            $prop->remove_attribute ($wiki->{config}->{<Q:pe:prop>}
                                          ->{$sub}->{uri});
            next DEPEND;
          }
        }
      }
      last MODIFICATION unless $modified;

      $prop->set_attribute (<Q:pe:seq> => $seq + 1);

      ## Update content
      try {
        $self->{view}->{wiki}->{db}->set (content_prop => $page => $prop);
      } catch SuikaWiki::DB::Util::Error with {
        my $err = shift;
        if ($err->{-type} eq 'ERROR_REPORTED') {
          $err->throw;
        } else {
          $self->{view}->{wiki}->view_in_mode (mode => '-wdb--fatal-error');
          throw SuikaWiki::DB::Util::Error -type => 'ERROR_REPORTED';
        }
      };

      ## Post-write events
      if ($new_mt) {
        my $format = __FUNCPACK{WikiFormat}__
                              ->handler (\$old_content,
                                         serialized_media_type => $old_mt,
                                         content_prop => $prop,
                                         wiki => $wiki);
        $format->content_type_changed_from (wiki => $wiki,
                                      page => $page,
                                      old_content => \$old_content,
                                      new_content => \$old_content,
                                      new_serialized_media_type => $new_mt,
                                      content_prop => $prop);
      }
      my $format = __FUNCPACK{WikiFormat}__
                              ->handler (\$old_content,
                                         serialized_media_type => $new_mt,
                                         content_prop => $prop,
                                         wiki => $wiki);
      $format->content_prop_modified (wiki => $wiki,
                                      page => $page,
                                      content_prop => $prop);

      } # MODIFICATION

      ## Redirect appropriate mode
      my $new_mode = $self->{view}->{wiki}->{input}->parameter
                         ('we--mode-modified');
      $new_mode =~ s/[^0-9A-Za-z._-]+//g;
        my $uri = $self->{view}->{wiki}->uri_reference
                       (page => $page,
                        mode => $new_mode,
                        up_to_date => 1,
                        param => {we__mode_modified => $new_mode});
        
      require SuikaWiki::Output::HTTP;
      my $output = SuikaWiki::Output::HTTP->new
            (wiki => $self->{view}->{wiki});
      $output->set_redirect (uri => $uri, status_code => 303);
      $output->output (output => 'http-cgi');

FormattingRule:
  @Category[list]:
    form-input
  @Name: pe--texts
  @Description:
    @@@@: Multiple texts input
    @@@lang:en
  @Formatting:
    __ATTRTEXT:%id__;__ATTRNODE:%label__;
    my $id = __FUNCPACK{WikiFormCore}__
                              ->control_id ($o,
                                            local_id => $p->{id},
                                            require_local_id => 1);
    my $has_label = 0;
    if ($p->{label}->count) {
      $has_label = 1;
      for ($p->{-parent}->append_new_node
                            (type => '#element',
                             namespace_uri => $NS_XHTML1,
                             local_name => 'label')) {
        $_->set_attribute (for => $id->{global_id});
        $_->append_node ($p->{label});
      }
      $p->{-parent}->append_new_node
                            (type => '#element',
                             namespace_uri => $NS_XHTML1,
                             local_name => 'br')
                   ->option (use_EmptyElemTag => 1);
    }
    
    for ($p->{-parent}->append_new_node (type => '#element',
                                         namespace_uri => $NS_XHTML1,
                                         local_name => 'textarea')) {
      $_->set_attribute (name => $id->{local_id});
      $_->set_attribute (id => $id->{global_id}) if $has_label;
      __ATTRTEXT:%size__;__ATTRTEXT:%lines__;__ATTRTEXT:%description__;
      $_->set_attribute (cols => ($p->{size}||20)*2);	## Length
      $_->set_attribute (title => $p->{description}) if $p->{description};
      $_->set_attribute (disabled => 'disabled') if $o->{form}->{disabled};
      __ATTRTEXT:%class__;
      my @class = split /\s+/, $p->{class};
      push @class, 'require' if $o->{form}->{require}->{id}->{$p->{id}};
      __ATTRTEXT:%source__;
      my $items = 5;
      if ($p->{source}) {
        if (ref $o->{var}->{source}->{$p->{source}}) {
          my $d = $o->{var}->{source}->{$p->{source}}
                    ->(p=>$p, o=>$o, type => <Q:pe:texts>);
          $_->append_text (ref $d->{value} eq 'ARRAY' ?
                           join "\n", @{$d->{value}} : $d->{value});
          push @class, @{$d->{class}||[]};
          $items = @{$d->{value}} + 1 if ref $d->{value} eq 'ARRAY';
        } else {
          $_->append_text (ref $o->{var}->{$p->{source}} eq 'ARRAY' ?
                           join "\n", @{$o->{var}->{$p->{source}}} :
                           $o->{var}->{$p->{source}});
          $items = @{$o->{var}->{$p->{source}}} + 1
                 if ref $o->{var}->{$p->{source}} eq 'ARRAY';
        }
      } else {
        __ATTRTEXT:%default__;
        $_->append_text ($p->{default});	## Default value
      }
      $_->set_attribute (rows => ($p->{lines} || $items));	## Lines
      $_->set_attribute (class => join ' ', @class) if @class;
    }

Resource:
  @Edit:SaveAnd:pe--propedit:Description:
    @@lang: en
    @@@:
      Save modified property values and show this property-editing
      mode again.
  @Edit:SaveAnd:pe--propedit:Label:
    @@lang: en
    @@@:
      Editing Properties
  @PropEdit:Conflict:Description:
    @@lang: en
    @@@:
    Properties for WikiPage %link-to-wikipage (label => {%link-to-it (
    label => {%page-name;}p);});
    has been modified during you are editing them.
    Please ensure that third-party's modification does not
    affect your submitting changes. 
  @PropEdit:Conflict:Title:
    @@lang: en
    @@@:
      Confliction - Editing Content Properties
  @PropEdit:Conflict:WebPageTitle:
    @@lang: en
    @@@:
      CONFLICTION - %page-name; (Editing Content Properties)
  @PropEdit:Link:Description:
    @@lang: en
    @@@: Edit properties of this WikiPage
  @PropEdit:Template:
    
  ## Should be defined in resource WikiPage
  @PropEdit:Title:
    @@lang: en
    @@@:
      Editing Content Properties
  @PropEdit:WebPageTitle:
    @@lang: en
    @@@:
      %page-name; (Editing Content Properties)