#!/usr/bin/env perl

# This chunk of stuff was generated by App::FatPacker. To find the original
# file's code, look for the end of this BEGIN block or the string 'FATPACK'
BEGIN {
my %fatpacked;

$fatpacked{"WGDev.pm"} = <<'WGDEV';
  package WGDev;
  {
    $WGDev::VERSION = '0.1207160';
  }
  # ABSTRACT: WebGUI Developer Utilities
  use strict;
  use warnings;
  use 5.008008;
  
  use File::Spec ();
  use Cwd        ();
  use WGDev::X   ();
  use Try::Tiny;
  
  sub new {
      my $class = shift;
      my $self = bless {}, $class;
      my $root;
      my $config;
      if ( $_[0] && -d $_[0] ) {
          ( $root, $config ) = @_;
      }
      else {
          ( $config, $root ) = @_;
      }
      if ($root) {
          $self->root($root);
      }
      if ($config) {
          $self->config_file($config);
      }
      return $self;
  }
  
  sub set_environment {
      my $self = shift;
      my %options = @_;
      require Config;
      WGDev::X::NoWebGUIRoot->throw
          if !$self->root;
      WGDev::X::NoWebGUIConfig->throw
          if !$self->config_file;
      if (! $options{localized}) {
          $self->{orig_env}
              ||= { map { $_ => $ENV{$_} } qw(WEBGUI_ROOT WEBGUI_CONFIG PERL5LIB) };
      }
      ##no critic (RequireLocalizedPunctuationVars)
      $ENV{WEBGUI_ROOT}   = $self->root;
      $ENV{WEBGUI_CONFIG} = $self->config_file;
      $ENV{PERL5LIB}      = join $Config::Config{path_sep}, $self->lib,
          $ENV{PERL5LIB} || ();
      return 1;
  }
  
  sub reset_environment {
      my $self     = shift;
      my $orig_env = delete $self->{orig_env};
      return
          if !$orig_env;
      ##no critic (RequireLocalizedPunctuationVars)
      @ENV{ keys %{$orig_env} } = values %{$orig_env};
      return 1;
  }
  
  sub root {
      my $self = shift;
      if (@_) {
          my $path = shift;
          if (   -d $path
              && -e File::Spec->catfile( $path, 'lib', 'WebGUI.pm' )
              )
          {
              $self->{root} = File::Spec->rel2abs($path);
              $self->{lib} = File::Spec->catdir( $self->{root}, 'lib' );
              unshift @INC, $self->lib;
          }
          else {
              WGDev::X::BadParameter->throw(
                  'parameter' => 'WebGUI root directory',
                  'value'     => $path,
              );
          }
      }
      return $self->{root};
  }
  
  sub config_file {
      my $self = shift;
      if (@_) {
          my $path = shift;
          require Config::JSON;
          if ( -e $path ) {
          }
          elsif (
              $self->root
              && -e (
                  my $fullpath
                      = File::Spec->catfile( $self->root, 'etc', $path ) ) )
          {
              $path = $fullpath;
          }
          else {
              WGDev::X::BadParameter->throw(
                  'parameter' => 'WebGUI config file',
                  'value'     => $path,
              );
          }
          if ( !$self->root ) {
              try {
                  $self->root(
                      File::Spec->catpath(
                          ( File::Spec->splitpath($path) )[ 0, 1 ],
                          File::Spec->updir
                      ) );
              };
          }
          my $path_abs = File::Spec->rel2abs($path);
          my $config;
          if ( ! try { $config = Config::JSON->new($path_abs) } ) {
              WGDev::X::BadParameter->throw(
                  'parameter' => 'WebGUI config file',
                  'value'     => $path,
              );
          }
          $self->close_session;
          $self->close_config;
          $self->{config_file} = $path_abs;
          $self->{config}      = $config;
          delete $self->{config_file_relative};
      }
      return $self->{config_file};
  }
  
  sub lib {
      my $self = shift;
      WGDev::X::NoWebGUIRoot->throw
          if !$self->root;
      if ( !wantarray ) {
          return $self->{lib};
      }
      my @lib = $self->{lib};
      if ( !$self->{custom_lib} ) {
          my @custom_lib;
          $self->{custom_lib} = \@custom_lib;
          my $custom
              = File::Spec->catfile( $self->root, 'sbin', 'preload.custom' );
          if ( -e $custom && open my $fh, '<', $custom ) {
              while ( my $line = <$fh> ) {
                  $line =~ s/[#].*//msx;
                  $line =~ s/\A\s+//msx;
                  $line =~ s/\s+\z//msx;
                  if ( -d $line ) {
                      unshift @custom_lib, $line;
                  }
              }
              close $fh or WGDev::X::IO::Read->throw( path => $custom );
          }
      }
      unshift @lib, @{ $self->{custom_lib} };
      return @lib;
  }
  
  sub config {
      my $self = shift;
      WGDev::X::NoWebGUIConfig->throw
          if !$self->config_file;
      return $self->{config} ||= do {
          require Config::JSON;
          Config::JSON->new( $self->config_file );
      };
  }
  
  sub close_config {
      my $self = shift;
      delete $self->{config};
  
      # if we're closing the config, we probably want new sessions to pick up
      # changes to the file
      if ( WebGUI::Config->can('clearCache') ) {
          WebGUI::Config->clearCache;
      }
      return 1;
  }
  
  sub config_file_relative {
      my $self = shift;
      WGDev::X::NoWebGUIConfig->throw
          if !$self->config_file;
      return $self->{config_file_relative} ||= do {
          my $config_dir
              = Cwd::realpath( File::Spec->catdir( $self->root, 'etc' ) );
          File::Spec->abs2rel( $self->config_file, $config_dir );
      };
  }
  
  sub db {
      my $self = shift;
      require WGDev::Database;
      return $self->{db} ||= WGDev::Database->new( $self->config );
  }
  
  sub session {
      my $self = shift;
      WGDev::X::NoWebGUIConfig->throw
          if !$self->config_file;
      require WebGUI::Session;
      if ( $self->{session} ) {
          my $dbh = $self->{session}->db->dbh;
  
          # if the database handle died, close the session
          if ( !$dbh->ping ) {
              delete $self->{asset};
              ( delete $self->{session} )->close;
          }
      }
      return $self->{session} ||= do {
          my $session = $self->create_session($self->{session_id});
          $self->{session_id} = $session->getId;
          $session;
      };
  }
  
  sub create_session {
      my $self = shift;
      my $session_id = shift;
      my $session;
      if ( $self->version->module =~ /^8[.]/msx ) {
          $session
              = WebGUI::Session->open( $self->config_file,
              undef, undef, $session_id );
      }
      else {
          $session
              = WebGUI::Session->open( $self->root, $self->config_file_relative,
              undef, undef, $session_id );
      }
      return $session;
  }
  
  sub close_session {
      my $self = shift;
      if ( $self->{session} ) {    # if we have a cached session
          my $session = $self->session;  # get the session, recreating if needed
          $session->var->end;            # close the session
          $session->close;
          delete $self->{asset};
          delete $self->{session};
      }
      return 1;
  }
  
  sub list_site_configs {
      my $self = shift;
      my $root = $self->root;
      WGDev::X::NoWebGUIRoot->throw
          if !$root;
  
      if ( opendir my $dh, File::Spec->catdir( $root, 'etc' ) ) {
          my @configs = readdir $dh;
          closedir $dh
              or WGDev::X::IO::Read->throw('Unable to close directory handle');
          @configs = map { File::Spec->catdir( $root, 'etc', $_ ) }
              grep { /\Q.conf\E$/msx && !/^(?:spectre|log)\Q.conf\E$/msx }
              @configs;
          return @configs;
      }
      return;
  }
  
  sub asset {
      my $self = shift;
      require WGDev::Asset;
      return $self->{asset} ||= WGDev::Asset->new( $self->session );
  }
  
  sub version {
      my $self = shift;
      WGDev::X::NoWebGUIRoot->throw
          if !$self->root;
      require WGDev::Version;
      return $self->{version} ||= WGDev::Version->new( $self->root );
  }
  
  sub wgd_config {    ##no critic (ProhibitExcessComplexity)
      my ( $self, $key_list, $value ) = @_;
      my $config = \( $self->{wgd_config} );
      if ( !${$config} ) {
          $config = \( $self->read_wgd_config );
      }
      my @keys;
      if ( ref $key_list && ref $key_list eq 'ARRAY' ) {
          @keys = @{$key_list};
      }
      else {
          @keys = split /[.]/msx, $key_list;
      }
  
      if ( !${$config} ) {
          $config = \( $self->{wgd_config} = {} );
      }
      while (@keys) {
          my $key     = shift @keys;
          my $numeric = $key ne q{} && $key =~ /^[+]?-?\d*$/msx;
          my $type    = ref ${$config};
          if (   ( !$type && !defined $value )
              || $type eq 'SCALAR'
              || ( $type eq 'ARRAY' && !$numeric ) )
          {
              return;
          }
          elsif ( $type eq 'ARRAY' or ( !$type && $numeric ) ) {
              if ( !$type ) {
                  ${$config} = [];
              }
              my ($insert) = $key =~ s/^([+])//msx;
              if ( !defined $value
                  && ( $insert || !defined ${$config}->[$key] ) )
              {
                  return;
              }
              if ($insert) {
                  if ( $key ne q{} ) {
                      if ( $key < 0 ) {
                          $key += @{ ${$config} };
                      }
                      splice @{ ${$config} }, $key, 0, undef;
                  }
                  else {
                      $key = @{ ${$config} };
                  }
              }
              $config = \( ${$config}->[$key] );
          }
          else {
              if ( !$type ) {
                  ${$config} = {};
              }
              if ( !defined ${$config}->{$key} && !defined $value ) {
                  return;
              }
              $config = \( ${$config}->{$key} );
          }
          if (@keys) {
              next;
          }
          if ($value) {
              return ${$config} = $value;
          }
          return ${$config};
      }
      return;
  }
  
  my $json;
  
  sub read_wgd_config {
      my $self = shift;
      for my $config_file ( "$ENV{HOME}/.wgdevcfg", '/etc/wgdevcfg' ) {
          if ( -e $config_file ) {
              my $config;
              open my $fh, '<', $config_file or next;
              my $content = do { local $/; <$fh> };
              close $fh or next;
              $self->{wgd_config_path} = Cwd::realpath($config_file);
              if ( $content eq q{} ) {
                  $config = {};
              }
              else {
                  if ( !$json ) {
                      require JSON;
                      $json = JSON->new;
                      $json->utf8;
                      $json->relaxed;
                      $json->canonical;
                      $json->pretty;
                  }
                  $config = try { $json->decode($content) } || {};
              }
              return $self->{wgd_config} = $config;
          }
      }
      return $self->{wgd_config} = {};
  }
  
  sub write_wgd_config {
      my $self        = shift;
      my $config_path = $self->{wgd_config_path};
      if ( !$self->{wgd_config_path} ) {
          $config_path = $self->{wgd_config_path} = $ENV{HOME} . '/.wgdevcfg';
      }
      my $config = $self->{wgd_config} || {};
      if ( !$json ) {
          require JSON;
          $json = JSON->new;
          $json->utf8;
          $json->relaxed;
          $json->canonical;
          $json->pretty;
      }
      my $encoded = $json->encode($config);
      $encoded =~ s/\n?\z/\n/msx;
      open my $fh, '>', $config_path
          or WGDev::X::IO::Write->throw(
          message => 'Unable to write config file',
          path    => $config_path,
          );
      print {$fh} $encoded;
      close $fh
          or WGDev::X::IO::Write->throw(
          message => 'Unable to write config file',
          path    => $config_path,
          );
      return 1;
  }
  
  sub my_config {
      my $self = shift;
      my $key  = shift;
      my @keys;
      if ( ref $key && ref $key eq 'ARRAY' ) {
          @keys = @{$key};
      }
      else {
          @keys = split /[.]/msx, $key;
      }
      my $caller = caller;
      my $remove = ( ref $self ) . q{::};
      $caller =~ s/^\Q$remove//msx;
      unshift @keys, map { lcfirst $_ } split /::/msx, $caller;
      return $self->wgd_config( \@keys, @_ );
  }
  
  sub yaml_decode {
      _load_yaml_lib();
      goto &yaml_decode;
  }
  
  sub yaml_encode {
      _load_yaml_lib();
      goto &yaml_encode;
  }
  
  sub _load_yaml_lib {
      ## no critic (ProhibitCascadingIfElse)
      no warnings 'redefine';
      if ( try { require YAML::XS } ) {
          *yaml_encode = \&YAML::XS::Dump;
          *yaml_decode = \&YAML::XS::Load;
      }
      elsif ( try { require YAML::Syck } ) {
          *yaml_encode = \&YAML::Syck::Dump;
          *yaml_decode = \&YAML::Syck::Load;
      }
      elsif ( try { require YAML } ) {
          *yaml_encode = \&YAML::Dump;
          *yaml_decode = \&YAML::Load;
      }
      elsif ( try { require YAML::Tiny } ) {
          *yaml_encode = \&YAML::Tiny::Dump;
          *yaml_decode = \&YAML::Tiny::Load;
      }
      else {
          *yaml_encode = *yaml_decode = sub {
              WGDev::X->throw('No YAML library available!');
          };
      }
      return;
  }
  
  sub DESTROY {
      my $self = shift;
      local $@;
      try {
          $self->close_session;
      };
      return;
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev - WebGUI Developer Utilities
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      use WGDev;
  
      my $wgd = WGDev->new( $webgui_root, $config_file );
  
      my $webgui_session = $wgd->session;
      my $webgui_version = $wgd->version->module;
  
  =head1 DESCRIPTION
  
  Performs common actions needed by WebGUI developers, such as recreating their
  site from defaults, checking version numbers, exporting packages, and more.
  
  =head1 METHODS
  
  =head2 C<new ( [ $root ], [ $config ] )>
  
  Creates a new WGDev object.  Optionally accepts a WebGUI root path and config
  file.  These will be passed on to the C<root> and C<config_file> methods.
  
  =head2 C<root ( [ $webgui_root ] )>
  
  Sets or returns the WebGUI root path the object will be interacting with.  If
  the path can't be recognized as a WebGUI root, an error will be thrown.  The
  return value will always be an absolute path to the WebGUI root.
  
  =head2 C<config_file ( [ $webgui_config ] )>
  
  Sets or returns the site config file path.  The given path can be relative to
  the current directory or to the etc directory in the WebGUI root.  If the
  config file is found and the WebGUI root is not yet set, it will set the root
  based on the config file path.  If the specified config file can't be found,
  an error will be thrown.
  
  =head2 C<config_file_relative>
  
  Returns the config file path relative to the WebGUI config directory.  Useful
  for initializing WebGUI sessions, which require the config path to be relative
  to that directory.
  
  =head2 C<lib>
  
  In scalar context, returns the WebGUI library path based on the WebGUI root.
  In array context, it also includes the library paths specified in the
  F<preload.custom> file.
  
  =head2 C<list_site_configs>
  
  Returns a list of the available site configuration files in the
  C<etc> directory of the specified WebGUI root path.  The returned
  paths will include the full file path.
  
  =head2 C<config>
  
  Returns a Config::JSON object based on the file set using C<config_file>.
  
  =head2 C<session>
  
  Returns a WebGUI session initialized using the WebGUI root and config file.
  
  =head2 C<asset>
  
  Returns a L<WGDev::Asset> object for simple asset operations.
  
  =head2 C<db>
  
  Returns a L<WGDev::Database> object for database interaction without starting
  a WebGUI session.
  
  =head2 C<version>
  
  Returns a L<WGDev::Version> object for checking the WebGUI version number in
  several different places.
  
  =head2 C<close_config>
  
  Closes the link to the WebGUI config file.  Future calls to C<config> will
  load a new object based on the file.
  
  =head2 C<close_session>
  
  Closes the WebGUI session.  If the session object has expired or is no longer
  valid, it will first be re-opened, then closed properly.
  
  =head2 C<set_environment>
  
  Sets the C<WEBGUI_ROOT>, C<WEBGUI_CONFIG>, and C<PERL5LIB> environment variables
  based on C<root>, C<config_file>, and C<lib>.
  
  =head2 C<reset_environment>
  
  Resets the C<WEBGUI_ROOT>, C<WEBGUI_CONFIG>, and C<PERL5LIB> based to what they
  were prior to set_environment being called.
  
  =head2 C<wgd_config ( [ $config_param [, $value ] ] )>
  
  Get or set WGDev config file parameters.  Accepts two parameters, the config
  directive and optionally the value to set it to.  The config directive is the
  path in a data structure specified either as an array reference of keys or a
  period separated string of keys.
  
  =head2 C<my_config ( [ $config_param [, $value ] ] )>
  
  Similar to wgd_config, but prefixes the specified path with keys based on the
  caller's package.  For example, a package of C<WGDev::Command::Reset> becomes
  C<command.reset>.
  
  =head2 C<read_wgd_config>
  
  Reads and parses the WGDev config file into memory.  Will be automatically
  called by C<wgd_config> as needed.
  
  =head2 C<write_wgd_config>
  
  Saves the current configuration back to the WGDev config file.
  
  =head1 FUNCTIONS
  
  =head2 C<yaml_encode ( $structure )>
  
  Loads a YAML module if needed and encodes a data structure with it.
  
  =head2 C<yaml_decode ( $yaml_string )>
  
  Loads a YAML module if needed and decodes a data structure with it.
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV

$fatpacked{"WGDev/Asset.pm"} = <<'WGDEV_ASSET';
  package WGDev::Asset;
  {
    $WGDev::Asset::VERSION = '0.1207160';
  }
  # ABSTRACT: Asset utility functions
  use strict;
  use warnings;
  use 5.008008;
  
  use constant LINE_LENGTH => 78;
  
  use WGDev;
  use WGDev::X;
  use Try::Tiny;
  
  sub new {
      my $class   = shift;
      my $session = shift;
      my $self    = bless { session => $session, }, $class;
      require WebGUI::Asset;
      return $self;
  }
  
  sub root {
      my $self = shift;
      return WebGUI::Asset->getRoot( $self->{session} );
  }
  
  sub import_node {
      my $self = shift;
      return WebGUI::Asset->getImportNode( $self->{session} );
  }
  
  sub default_asset { goto &home }
  
  sub home {
      my $self = shift;
      return WebGUI::Asset->getDefault( $self->{session} );
  }
  
  sub by_url {
      my $self = shift;
      my $asset = WebGUI::Asset->newByUrl( $self->{session}, @_ );
      if (! defined $asset) {
          WGDev::X::AssetNotFound->throw(asset => $_[0]);
      }
      return $asset;
  }
  
  sub by_id {
      my $self = shift;
      my ($asset_id, $revision) = @_;
      my $asset;
      if (WebGUI::Asset->can('newById')) {
          $asset = WebGUI::Asset->newById( $self->{session}, $asset_id, $revision );
      }
      else {
          $asset = WebGUI::Asset->new( $self->{session}, $asset_id, undef, $revision );
      }
      if (! defined $asset) {
          WGDev::X::AssetNotFound->throw(asset => $_[0]);
      }
      return $asset;
  }
  
  sub find {
      my ( $self, $asset_spec ) = @_;
      my $session = $self->{session};
      my $asset;
      my $e;
      if ( $session->id->valid($asset_spec) ) {
          try {
              $asset = $self->by_id($asset_spec);
          }
          catch {
              $e = $_;
          };
      }
      if ( !$asset ) {
          try {
              $asset = WebGUI::Asset->newByUrl( $session, $asset_spec );
          }
          catch {
              $e ||= $_;
          };
      }
      if ( $asset && ref $asset && $asset->isa('WebGUI::Asset') ) {
          return $asset;
      }
      if ($e) {
          WGDev::X->inflate($e);
      }
      WGDev::X::AssetNotFound->throw( asset => $asset_spec );
  }
  
  my $package_re = qr{
      [[:upper:]]\w+
      (?: ::[[:upper:]]\w+ )*
  }msx;
  
  sub validate_class {
      my $self = shift;
      my $in_class = my $class = shift;
      if (
          $class =~ s{\A
              # optionally starting with WebGUI::Asset:: or ::
              (?:(?:WebGUI::Asset)?::)?
              ( $package_re )
              \z
          }{WebGUI::Asset::$1}msx
          )
      {
          my $short_class = $1;
          return wantarray ? ( $class, $short_class ) : $class;
      }
      WGDev::X::BadAssetClass->throw( class => $in_class );
  }
  
  sub _gen_serialize_header {
      my $self        = shift;
      my $header_text = shift;
      my $header      = "==== $header_text ";
      $header .= ( q{=} x ( LINE_LENGTH - length $header ) ) . "\n";
      return $header;
  }
  
  sub serialize {
      my ( $self, $asset, $properties ) = @_;
      my $class = ref $asset || $asset;
      WGDev::X::BadParameter->throw('No asset or class specified')
          if not defined $class;
      if ( !ref $asset ) {
          ( my $module = $class . '.pm' ) =~ s{::}{/}msxg;
          require $module;
      }
      my $short_class = $class;
      $short_class =~ s/^WebGUI::Asset:://xms;
  
      my ( $asset_properties, $meta, $text )
          = $self->_asset_properties( $asset, $properties );
  
      my $basic_yaml = WGDev::yaml_encode( {
              'Asset ID'   => $asset_properties->{assetId},
              'Title'      => $asset_properties->{title},
              'Menu Title' => $asset_properties->{menuTitle},
              'URL'        => $asset_properties->{url},
              'Parent'     => (
                  ref $asset
                  ? $asset->getParent->get('url')
                  : $self->import_node->get('url')
              ),
          } );
  
      # filter out unneeded YAML syntax
      $basic_yaml =~ s/\A---(?:\Q {}\E)?\s*//msx;
      $basic_yaml =~ s/\r?\n/\n/msxg;
      $basic_yaml =~ s/[ ]+$//msxg;
  
      # line up colons
      $basic_yaml =~ s/^([^:]+):/sprintf("%-12s:", $1)/msxeg;
      my $output = $self->_gen_serialize_header($short_class) . $basic_yaml;
  
      for my $field ( sort keys %{$text} ) {
          my $value = $text->{$field};
          if ( !defined $value ) {
              $value = q{~};
          }
          $value =~ s/\r\n?/\n/msxg;
          $output .= $self->_gen_serialize_header($field) . $value . "\n";
      }
  
      my $meta_yaml = WGDev::yaml_encode($meta);
      $meta_yaml =~ s/\A---(?:\Q {}\E)?\s*//msx;
      $meta_yaml =~ s/\r?\n/\n/msxg;
      $meta_yaml =~ s/[ ]+$//msxg;
      $output .= $self->_gen_serialize_header('Properties') . $meta_yaml . "\n";
  
      return $output;
  }
  
  sub _asset_properties {
      my $self       = shift;
      my $class      = shift;
      my $properties = shift;
      my $asset;
      if ( ref $class ) {
          $asset = $class;
          $class = ref $asset;
      }
      @_ = ($self, $class, $asset, $properties);
      if ($class->can('definition')) {
          goto &_asset_properties_definition;
      }
      goto &_asset_properties_meta;
  }
  
  sub _asset_properties_definition {
      my $self = shift;
      my ($class, $asset, $properties) = @_;
  
      my $definition = $class->definition( $self->{session} );
      my %text;
      my %meta;
  
      my $asset_properties
          = { $asset ? %{ $asset->get } : (), $properties ? %{$properties} : (),
          };
      for my $def ( @{$definition} ) {
          while ( my ( $property, $property_def )
              = each %{ $def->{properties} } )
          {
              if (  !defined $asset_properties->{$property}
                  && defined $property_def->{defaultValue} )
              {
                  $asset_properties->{$property}
                      = $self->_get_property_default($property_def);
              }
  
              $self->_filter_property(
                  $property,
                  $asset_properties->{$property},
                  ucfirst ( $property_def->{fieldType} || q{} ),
                  $property_def->{tab},
                  \%text,
                  \%meta,
              );
          }
      }
      return ( $asset_properties, \%meta, \%text );
  }
  
  sub _asset_properties_meta {
      my $self = shift;
      my ($class, $asset, $properties) = @_;
  
      my %text;
      my %meta;
  
      my $asset_properties
          = { $asset ? %{ $asset->get } : (), $properties ? %{$properties} : (),
          };
  
      for my $property ( $class->meta->get_all_property_list ) {
          my $attr = $class->meta->find_attribute_by_name($property);
          if (  !defined $asset_properties->{$property} ) {
              $asset_properties->{$property} = $attr->default;
          }
          my $field_type = ucfirst $attr->fieldType;
          $self->_filter_property(
              $property,
              $asset_properties->{$property},
              ucfirst $attr->fieldType,
              $attr->form->{tab},
              \%text,
              \%meta,
          );
      }
      return ( $asset_properties, \%meta, \%text );
  }
  
  sub _filter_property { ##no critic (ProhibitManyArgs)
      my $self = shift;
      my ( $property, $value, $field_type, $tab, $text, $meta ) = @_;
      if (   $property eq 'title'
          || $property eq 'menuTitle'
          || $property eq 'url' )
      {
          return;
      }
      elsif ($field_type eq 'HTMLArea'
          || $field_type eq 'Textarea'
          || $field_type eq 'Codearea' )
      {
          $text->{$property} = $value;
      }
      elsif ( $field_type eq 'Hidden' ) {
          return;
      }
      else {
          $meta->{ $tab || 'properties' }{$property} = $value;
      }
      return;
  }
  
  my %basic_translation = (
      'Title'      => 'title',
      'Asset ID'   => 'assetId',
      'Menu Title' => 'menuTitle',
      'URL'        => 'url',
      'Parent'     => 'parent',
  );
  
  sub deserialize {
      my $self          = shift;
      my $asset_data    = shift;
      my @text_sections = split m{
          ^====[ ]    # line start, plus equal signs
          ((?:\w|:)+) # word chars or colons (Perl namespace)
          [ ]=+       # space + equals
          (?:\n|\z)   # end of line or end of string
      }msx, $asset_data;
  
      # due to split, there is an extra empty entry at the beginning
      shift @text_sections;
      my $class      = $self->validate_class( shift @text_sections );
      my $basic_data = shift @text_sections;
      my %sections;
      my %properties;
  
      while ( my $section = shift @text_sections ) {
          my $section_data = shift @text_sections;
          chomp $section_data;
          if ( $section_data eq q{~} ) {
              $section_data = undef;
          }
          $sections{$section} = $section_data;
      }
      if ( my $prop_data = delete $sections{Properties} ) {
          my $tabs = WGDev::yaml_decode($prop_data);
          %properties = map { %{$_} } values %{$tabs};
      }
  
      @properties{ keys %sections } = values %sections;
  
      my $basic_untrans = WGDev::yaml_decode($basic_data);
      for my $property ( keys %{$basic_untrans} ) {
          if ( $basic_translation{$property} ) {
              $properties{ $basic_translation{$property} }
                  = $basic_untrans->{$property};
          }
      }
  
      $properties{className} = $class;
  
      return \%properties;
  }
  
  sub _get_property_default {
      my $self         = shift;
      my $property_def = shift;
      my $default      = $property_def->{defaultValue};
      my $form_class   = $property_def->{fieldType};
      if ($form_class) {
          $form_class = "WebGUI::Form::\u$form_class";
          my $form_module = join q{/}, ( split /::/msx, $form_class . '.pm' );
          if ( eval { require $form_module; 1 } ) {
              my $form = $form_class->new( $self->{session},
                  { defaultValue => $default } );
              $default = $form->getDefaultValue;
          }
      }
      return $default;
  }
  
  sub export_extension {
      my $self  = shift;
      my $asset = shift;
      my $class = ref $asset || $asset;
      return
          if !defined $class;
      my $short_class = $class;
      $short_class =~ s/.*:://msx;
      my $extension = lc $short_class;
      $extension =~ s/(?<!^)[aeiouy]//msxg;
      $extension =~ tr/a-z//s;
      return $extension;
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Asset - Asset utility functions
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      my $root_node = $wgd->asset->root;
  
  =head1 DESCRIPTION
  
  Performs common actions on assets.
  
  =head1 METHODS
  
  =head2 C<new ( $session )>
  
  Creates a new object.  Requires a single parameter of the WebGUI session to use.
  
  =head2 C<by_id ( $asset_id )>
  
  Finds an asset based on an asset ID.
  
  =head2 C<by_url ( $asset_url )>
  
  Finds an asset based on a URL.
  
  =head2 C<find ( $asset_id_or_url )>
  
  Finds an asset based on either an asset ID or a URL based on the format of
  the input.
  
  =head2 C<home>
  
  An alias for the C<default_asset> method.
  
  =head2 C<default_asset>
  
  Returns the default WebGUI asset, as will be shown for the URL of C</>.
  
  =head2 C<root>
  
  Returns the root WebGUI asset.
  
  =head2 C<import_node>
  
  Returns the Import Node asset.
  
  =head2 C<serialize ( $asset_or_class )>
  
  Serializes an asset into a string that can be written out to a file.
  
  =head2 C<deserialize ( $asset_data_text )>
  
  Deserializes a string as generated by C<serialize> into either a hash
  reference of properties that can be used to create or update an asset.
  
  =head2 C<validate_class ( [ $class_name ] )>
  
  Accepts a class name of an asset in either full (C<WebGUI::Asset::Template>) or
  short (C<Template>) form.  In scalar context, returns the full class name.  In
  array context, returns an array of the full and the short class name.  Will
  throw an error if the provided class is not valid.
  
  =head2 C<export_extension ( $asset_or_class )>
  
  Returns a file extension to use for exporting the given asset or
  class.  The extension will be the last segment of the class name,
  lower cased, with repeated letters and vowels (except for an initial
  vowel) removed.
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_ASSET

$fatpacked{"WGDev/Command.pm"} = <<'WGDEV_COMMAND';
  package WGDev::Command;
  {
    $WGDev::Command::VERSION = '0.1207160';
  }
  # ABSTRACT: Run WGDev commands
  use strict;
  use warnings;
  use 5.008008;
  
  use Getopt::Long ();
  use File::Spec   ();
  use Cwd          ();
  use WGDev::X     ();
  
  sub run {
      my $class = shift;
      local @ARGV = @_;
      Getopt::Long::Configure(
          qw(default gnu_getopt pass_through no_auto_abbrev));
      Getopt::Long::GetOptions(
          'h|?|help'      => \( my $opt_help ),
          'V|ver|version' => \( my $opt_version ),
  
          'F|config-file=s' => \( my $opt_config ),
          'R|webgui-root=s' => \( my $opt_root ),
          'S|sitename=s'    => \( my $opt_sitename ),
      ) || WGDev::X::CommandLine->throw( usage => $class->usage(0) );
      my @params = @ARGV;
  
      my $command_name = shift @params;
  
      my $command_module = eval { $class->get_command_module($command_name) };
      if ( $command_name && !$command_module ) {
          my $command_exec = $class->_find_cmd_exec($command_name);
          if ($command_exec) {
              require WGDev::Command::Run;
              $command_module = 'WGDev::Command::Run';
              unshift @params, $command_exec, $opt_help ? '--help' : (),
                  $opt_version ? '--version' : ();
              undef $opt_help;
              undef $opt_version;
          }
          else {
              WGDev::X::CommandLine::BadCommand->throw(
                  command_name => $command_name,
                  usage        => $class->usage(0),
              );
          }
      }
  
      if ($opt_version) {
          $class->report_version( $command_name, $command_module );
      }
      elsif ($opt_help) {
          $class->report_help( $command_name, $command_module );
      }
      elsif ( !$command_name ) {
          print $class->usage(0);
          require WGDev::Command::Commands;
          return WGDev::Command::Commands->help;
      }
      else {
          require WGDev;
          my $wgd = WGDev->new;
          $class->guess_webgui_paths(
              wgd         => $wgd,
              root        => $opt_root,
              config_file => $opt_config,
              sitename    => $opt_sitename,
          );
          my $command = $command_module->new($wgd);
          return $command->run(@params);
      }
      return 1;
  }
  
  sub get_params_or_defaults {
      my $class  = shift;
      my %params = @_;
      my $wgd    = $params{wgd};
  
      if ( $params{config_file} && $params{sitename} ) {
          WGDev::X::BadParameter->throw(
              q{Can't specify both a config file and a sitename});
      }
  
  ##no tidy
      my $webgui_root
          = $params{root}
          || $ENV{WEBGUI_ROOT}
          || $wgd->my_config('webgui_root');
  ##tidy
      my $webgui_config;
      my $webgui_sitename;
  
      # avoid buggy critic module
      ##no critic (ProhibitCallsToUndeclaredSubs)
      FIND_CONFIG: {
          ( $webgui_config = $params{config_file} )
              && last FIND_CONFIG;
          ( $webgui_sitename = $params{sitename} )
              && last FIND_CONFIG;
          ( $webgui_config = $ENV{WEBGUI_CONFIG} )
              && last FIND_CONFIG;
          ( $webgui_sitename = $ENV{WEBGUI_SITENAME} )
              && last FIND_CONFIG;
          ( $webgui_config = $wgd->my_config('webgui_config') )
              && last FIND_CONFIG;
          ( $webgui_sitename = $wgd->my_config('webgui_sitename') )
              && last FIND_CONFIG;
      }
  
      $params{root}        = $webgui_root;
      $params{config_file} = $webgui_config;
      $params{sitename}    = $webgui_sitename;
      return %params;
  }
  
  sub guess_webgui_paths {
      my $class  = shift;
      my %params = $class->get_params_or_defaults(@_);
      my $wgd    = $params{wgd};
  
      my $webgui_root     = $params{root};
      my $webgui_config   = $params{config_file};
      my $webgui_sitename = $params{sitename};
  
      my $e;
  
      # first we need to find the webgui root
      if ($webgui_root) {
          $wgd->root($webgui_root);
      }
  
      # if that didn't set the root and we have a config, try to set it.
      # if it is absolute, it will give us a root as well
      if ( !$wgd->root && $webgui_config ) {
          if ( eval { $class->set_config_by_input( $wgd, $webgui_config ); } ) {
              return $wgd
                  if $wgd->root;
          }
          else {
              $e = WGDev::X->caught || WGDev::X->new($@);
          }
      }
  
      if ( !$wgd->root ) {
          if ( !eval { $class->set_root_relative($wgd); 1 } ) {
  
              # throw error from previous try to set the config
              $e->rethrow
                  if $e;
              return $wgd;
          }
      }
  
      if ($webgui_sitename) {
          $class->set_config_by_sitename( $wgd, $webgui_sitename );
      }
      elsif ($webgui_config) {
          $class->set_config_by_input( $wgd, $webgui_config );
      }
      return $wgd;
  }
  
  sub set_root_relative {
      my ( $class, $wgd ) = @_;
      my $dir = Cwd::getcwd();
      while (1) {
          if ( -e File::Spec->catfile( $dir, 'lib', 'WebGUI.pm' ) ) {
              $wgd->root($dir);
              last;
          }
          my $parent
              = Cwd::realpath( File::Spec->catdir( $dir, File::Spec->updir ) );
          WGDev::X::NoWebGUIRoot->throw
              if $dir eq $parent;
          $dir = $parent;
      }
      return $wgd;
  }
  
  sub set_config_by_input {
      my ( $class, $wgd, $webgui_config ) = @_;
  
      # first, try the specified config file
      if ( eval { $wgd->config_file($webgui_config) } ) {
          return $wgd;
      }
      my $e = WGDev::X->caught;
  
      # if that didn't work, try it with .conf appended
      if ( $webgui_config !~ /\Q.conf\E$/msx ) {
          if ( eval { $wgd->config_file( $webgui_config . '.conf' ) } ) {
              return $wgd;
          }
      }
  
      # if neither normal or alternate config files worked, die
      $e->rethrow;
  }
  
  sub set_config_by_sitename {
      my ( $class, $wgd, $sitename ) = @_;
      require Config::JSON;
      my @configs = $wgd->list_site_configs;
      my $found_config;
      my $sitename_regex = qr/ (?:^|[.])  \Q$sitename\E $ /msx;
      for my $config_file (@configs) {
          my $config = eval { Config::JSON->new($config_file) };
          next
              if !$config;
          for my $config_sitename ( @{ $config->get('sitename') } ) {
              if ( $config_sitename =~ m/$sitename_regex/msx ) {
                  if ($found_config) {
                      WGDev::X->throw("Ambigious site name: $sitename");
                  }
                  $found_config = $config_file;
              }
          }
      }
      if ($found_config) {
          $wgd->config_file($found_config);
          return $wgd;
      }
      WGDev::X->throw("Unable to find config file for site: $sitename");
  }
  
  sub report_version {
      my ( $class, $name, $module ) = @_;
      if ( ref $class ) {
          $class = ref $class;
      }
      print "$class version " . $class->VERSION;
      if ($module) {
          print " - $module version " . $module->VERSION;
      }
      print "\n";
      return 1;
  }
  
  sub report_help {
      my ( $class, $name, $module ) = @_;
      if ( ref $class ) {
          $class = ref $class;
      }
      if ($module) {
          if ( $module->can('usage') ) {
              print $module->usage(1);
          }
          else {
              warn "No documentation for $name command.\n";
          }
      }
      else {
          print $class->usage(1);
      }
      return 1;
  }
  
  sub get_command_module {
      my ( $class, $command_name ) = @_;
      if ( $command_name && $command_name =~ /^\w+(?:-\w+)*$/mxs ) {
          my $module = $class->command_to_module($command_name);
          ( my $module_file = "$module.pm" ) =~ s{::}{/}mxsg;
          if (   eval { require $module_file; 1 }
              && $module->can('run')
              && $module->can('is_runnable')
              && $module->is_runnable )
          {
              return $module;
          }
      }
      WGDev::X::BadCommand->throw( 'command_name' => $command_name );
  }
  
  sub command_to_module {
      my ( $class, $command ) = @_;
      my $module = join q{::}, __PACKAGE__, map {ucfirst} split /-/msx,
          $command;
      return $module;
  }
  
  sub _find_cmd_exec {
      my ( $class, $command_name, $root, $config ) = @_;
      if ($command_name) {
          for my $path ( File::Spec->path ) {
              my $execpath = File::Spec->catfile( $path, "wgd-$command_name" );
              if ( -x $execpath ) {
                  return $execpath;
              }
          }
      }
      return;
  }
  
  sub usage {
      my $class = shift;
      require WGDev::Help;
      return WGDev::Help::package_usage( $class, @_ );
  }
  
  sub command_list {
      my $class = shift;
      my %commands;
      ( my $fn_prefix = $class ) =~ s{::}{/}msxg;
  
      require File::Find;
      my %lib_check;
      for my $inc_path (@INC) {
          ##no critic (ProhibitParensWithBuiltins)
          my $command_root
              = File::Spec->catdir( $inc_path, split( /::/msx, $class ) );
          next
              if !-d $command_root;
          my $find_callback = sub {
              return
                  if !/\Q.pm\E$/msx;
  
              no warnings 'once';
              my $lib_path
                  = File::Spec->abs2rel( $File::Find::name, $inc_path );
              $lib_check{$lib_path} = 1;
          };
          File::Find::find( { no_chdir => 1, wanted => $find_callback },
              $command_root );
      }
      no warnings 'once';
      for my $module ( grep {m{^\Q$fn_prefix\E/}msx} ( keys %INC, @App::WGDev::PACKED ) ) {
          $lib_check{$module} = 1;
      }
      for my $module ( keys %lib_check ) {
          my $package = $module;
          $package =~ s/\Q.pm\E$//msx;
          $package = join q{::}, File::Spec->splitdir($package);
          ##no critic (RequireCheckingReturnValueOfEval)
          eval {
              require $module;
              if ( $package->can('run')
                  && $package->can('is_runnable')
                  && $package->is_runnable
              ) {
                  ( my $command = $package ) =~ s/^\Q$class\E:://msx;
                  $command = join q{-}, map {lcfirst} split m{::}msx, $command;
                  $commands{$command} = 1;
              }
          };
      }
  
      for my $command ( map { glob File::Spec->catfile( $_, 'wgd-*' ) }
          File::Spec->path )
      {
          next
              if !-x $command;
          my $file = ( File::Spec->splitpath($command) )[2];
          $file =~ s/^wgd-//msx;
          $commands{$file} = 1;
      }
      my @commands = sort keys %commands;
      return @commands;
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Command - Run WGDev commands
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      wgd [arguments] <subcommand> [subcommand arguments]
  
  =head1 DESCRIPTION
  
  Runs sub-commands from the C<WGDev::Command> namespace, or standalone
  scripts starting with F<wgd->
  
  =head1 METHODS
  
  =head2 C<run ( @arguments )>
  
  Runs C<wgd>, processing the arguments specified and running a sub-command if possible.
  
  =head2 C<usage ( [$verbosity] )>
  
  Returns usage information for C<wgd>.  The verbosity level is passed on
  to L<WGDev::Help::package_usage|WGDev::Help/package_usage>.
  
  =head2 C<command_list>
  
  Searches for available sub-commands and returns them as an array.
  This list includes available Perl modules that pass the
  L</get_command_module> check and executable files beginning with
  F<wgd->.
  
  =head2 C<command_to_module ( $command )>
  
  Converts a command into the module that would implement it.  Returns
  that module name.
  
  =head2 C<get_command_module ( $command )>
  
  Converts the command to a module, then attempts to load that module.
  If the module loads successfully, implements the C<run> and
  C<is_runnable> methods, and C<is_runnable> returns true, returns
  the module.  If not, returns C<undef>.
  
  =head2 C<< get_params_or_defaults ( wgd => $wgd, %params ) >>
  
  Finds the specified WebGUI root, config file, and C<sitename>.  Uses
  environment variables and configuration file if not specified
  directly.  Returns C<%params> with C<root>, C<config_file>, and
  C<sitename> options updated.
  
  =head2 C<< guess_webgui_paths ( wgd => $wgd, [root => $webgui_root], [config_file => $webgui_config] ) >>
  
  Attempts to detect the paths to use for the WebGUI root and config
  file.  Initializes the specified C<$wgd> object.  If specified, attempts
  to use the specified paths first.  If not specified, first checks
  the environment variables C<WEBGUI_ROOT> and C<WEBGUI_CONFIG>.
  Next, attempts to search upward from the current path to find the
  WebGUI root.  If a WebGUI root has been found but not a config file,
  checks for available config files.  If only one is available, it
  is used as the config file.
  
  =head2 C<set_root_relative ( $wgd )>
  
  Attempts to set the root WebGUI directory based on the current
  directory.  Searches upward from the current path for a valid WebGUI
  root directory, and sets it in the C<$wgd> object if found.  If no
  valid root is found, throws an error.
  
  =head2 C<set_config_by_input ( $wgd, $config )>
  
  Sets the config file in the C<$wgd> object based on the specified
  WebGUI config file.  If the specified file isn't found, but a file
  with the same name with the C<.conf> extension added to it does
  exist, that file will be used.  If a config file can't be found,
  throws an error.
  
  =head2 C<set_config_by_sitename ( $wgd, $sitename )>
  
  Sets the config file in the C<$wgd> object based on the specified
  site name.  All of the available config files will be checked and
  if one of the sites lists the site name, its config file will be
  used.
  
  =head2 C<report_help ( [$command, $module] )>
  
  Shows help information for C<wgd> or a sub-command.  If a command
  and module is specified, attempts to call C<usage> on the module
  or displays an error.  Otherwise, displays help information for
  C<wgd>.
  
  =head2 C<report_version ( [$command, $module] )>
  
  Reports version information about C<wgd>.  If specified, also
  includes version information about a sub-command.
  
  =head1 OPTIONS
  
  =over 8
  
  =item C<-h> C<-?> C<--help>
  
  Display usage summary for any command.
  
  =item C<-V> C<--version>
  
  Display version information
  
  =item C<-F> C<--config-file>
  
  Specify WebGUI config file to use.  Can be absolute, relative to
  the current directory, or relative to WebGUI's config directory.
  If not specified, it will try to use the C<WEBGUI_CONFIG> environment
  variable or the C<command.webgui_config> option from the configuration
  file.
  
  =item C<-S> C<--sitename>
  
  Specify the name of a WebGUI site to operate on.  This will check
  all of the config files in WebGUI's config directory for a single
  site using the specified C<sitename>.  If not specified, the
  C<WEBGUI_SITENAME> environment variable and C<command.webgui_sitename>
  option will be used if available.
  
  =item C<-R> C<--webgui-root>
  
  Specify WebGUI's root directory.  Can be absolute or relative.  If
  not specified, first the C<WEBGUI_ROOT> environment variable and
  C<command.webgui_root> option from the configuration file will be
  checked, then will search upward from the current path for a WebGUI
  installation.
  
  =item C<< <subcommand> >>
  
  The sub-command to run or get help for.
  
  =back
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_COMMAND

$fatpacked{"WGDev/Command/Base.pm"} = <<'WGDEV_COMMAND_BASE';
  package WGDev::Command::Base;
  {
    $WGDev::Command::Base::VERSION = '0.1207160';
  }
  # ABSTRACT: Super-class for implementing WGDev commands
  use strict;
  use warnings;
  use 5.008008;
  
  use WGDev::X ();
  
  sub is_runnable {
      my $class = shift;
      return $class->can('process');
  }
  
  sub new {
      my ( $class, $wgd ) = @_;
      my $self = bless {
          wgd       => $wgd,
          options   => {},
          arguments => [],
      }, $class;
      return $self;
  }
  
  sub wgd { return $_[0]->{wgd} }
  
  sub parse_params {
      my $self = shift;
      local @ARGV = @_;
  
      require Getopt::Long;
      Getopt::Long::Configure( 'default', $self->config_parse_options );
  
      my %getopt_params = (
          '<>' => sub {
              $self->argument( map {"$_"} @_ );
          },
      );
  
      for my $option ( $self->config_options ) {
  
          # for complex options, name is first word segment
          ( my $option_name ) = ( $option =~ /([\w-]+)/msx );
          my $method = 'option_' . $option_name;
          $method =~ tr/-/_/;
          if ( $self->can($method) ) {
              $getopt_params{$option} = sub {
                  $self->$method( @_[ 1 .. $#_ ] );
              };
          }
          else {
              $getopt_params{$option} = \( $self->{options}{$option_name} );
          }
      }
      my $result = Getopt::Long::GetOptions(%getopt_params);
      push @{ $self->{arguments} }, @ARGV;
      return $result;
  }
  
  sub parse_params_string {
      my $self         = shift;
      my $param_string = shift;
      require Text::ParseWords;
      return $self->parse_params( Text::ParseWords::shellwords($param_string) );
  }
  
  sub config_parse_options { return qw(gnu_getopt) }
  sub config_options       { }
  
  sub option {
      my $self = shift;
      my $option = shift || return;
      if (@_) {
          return $self->{options}{$option} = shift;
      }
      return $self->{options}{$option};
  }
  
  sub set_option_default {
      my $self = shift;
      my $option = shift || return;
      if ( !defined $self->option($option) ) {
          return $self->option( $option, @_ );
      }
      return;
  }
  
  sub argument {
      my $self = shift;
      if (@_) {
          push @{ $self->{arguments} }, @_;
          return wantarray ? @_ : $_[-1];
      }
      return;
  }
  
  sub arguments {
      my $self = shift;
      if ( @_ && ref $_[0] eq 'ARRAY' ) {
          my $arguments = shift;
          @{ $self->{arguments} } = @{$arguments};
      }
      return @{ $self->{arguments} };
  }
  
  sub run {
      my $self = shift;
      WGDev::X::NoWebGUIRoot->throw
          if $self->needs_root && !$self->wgd->root;
      WGDev::X::NoWebGUIConfig->throw
          if $self->needs_config && !$self->wgd->config_file;
      my @params = ( @_ == 1 && ref $_[0] eq 'ARRAY' ) ? @{ +shift } : @_;
      local $| = 1;
      if ( !$self->parse_params(@params) ) {
          my $usage = $self->usage(0);
          WGDev::X::CommandLine::BadParams->throw( usage => $usage );
      }
      return $self->process;
  }
  
  sub usage {
      my $class     = shift;
      my $verbosity = shift;
      if ( ref $class ) {
          $class = ref $class;
      }
      require WGDev::Help;
      my $usage = WGDev::Help::package_usage( $class, $verbosity );
      return $usage;
  }
  
  sub help {
      my $class = shift;
      if ( ref $class ) {
          $class = ref $class;
      }
      require WGDev::Help;
      WGDev::Help::package_perldoc( $class,
          '!AUTHOR|LICENSE|METHODS|SUBROUTINES' );
      return 1;
  }
  
  sub needs_root {
      return 1;
  }
  
  sub needs_config {
      my $class = shift;
      return $class->needs_root;
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Command::Base - Super-class for implementing WGDev commands
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      package WGDev::Command::Mine;
      use parent qw(WGDev::Command::Base);
  
      sub process {
          my $self = shift;
          print "Running my command\n";
          return 1;
      }
  
  =head1 DESCRIPTION
  
  A super-class useful for implementing L<WGDev> command modules.  Includes
  simple methods to override for parameter parsing and provides help text via
  Pod::Usage.
  
  While using WGDev::Command::Base is not required to write a command module,
  it is the recommended way to do so.
  
  =head1 METHODS
  
  =head2 C<is_runnable>
  
  This is a class method that must be implemented and return true for all
  command modules.  This method will return true for any subclass that
  implements the C<process> method.
  
  =head2 C<new ( $wgd )>
  
  Instantiate a new command object.  Requires a L<WGDev> object as the first
  parameter.
  
  =head2 C<wgd>
  
  Returns the L<WGDev> object used to instantiate the object.
  
  =head2 C<config_parse_options>
  
  Returns an array of parameters used to configure command line parsing.  These
  options are passed directly to L<Getopt::Long>.  See
  L<Getopt::Long/Configuring_Getopt::Long> for details on the available options.
  By default, returns C<gnu_getopt> and can be overridden to return others.
  
  =head2 C<config_options>
  
  Returns an array of command line options to be parsed.  Should be overridden
  to set which options will be parsed.  Should be specified in the syntax
  accepted by L<Getopt::Long>.  Each option will be saved as the the first
  group of word characters in the option definition.  Alternately, if a method
  with the name C<< option_<name> >> exists, it will be called to set the
  option instead.
  
  =head2 C<option ( $option [, $value] )>
  
  Sets or returns a command line option.  Accepts the option name as the first
  parameter.  If specified, the option will be set the the value of the second
  parameter.
  
  =head2 C<argument ( $argument )>
  
  Adds an argument to the argument list.  Any parameters specified will be added
  to the argument list.  Can be overridden to provide alternate behavior.
  
  =head2 C<arguments ( [ \@arguments ] )>
  
  Sets or returns the arguments list.  If specified, the first parameter
  must be an array reference whose values will be set as the arguments list.
  
  =head2 C<parse_params ( @parameters )>
  
  Sets options based on an array of command line parameters.
  
  =head2 C<parse_params_string ( $parameters )>
  
  Sets options based on a string of command line parameters.  The string will be
  processed with L<Text::ParseWords> C<shellwords> sub then passed on to
  C<parse_params>.
  
  =head2 C<set_option_default ( $option, $value )>
  
  Sets an option only if it is not currently defined.  First parameter is the
  option to set, second parameter is the value to set it to.
  
  =head2 C<needs_root>
  
  Should be overridden in subclasses to set whether a command needs a WebGUI root directory to run.  Returns true if not overridden.
  
  =head2 C<needs_config>
  
  Should be overridden in subclasses to set whether a command needs a WebGUI config file directory to run.  Returns the same value as L</needs_root> if not overridden.
  
  =head2 C<usage ( [ $verbosity ] )>
  
  Returns the usage information for the command.  The optional first parameter
  is the verbosity to use.
  
  =head2 C<help>
  
  Display help information for this command using L<perldoc>.  Excludes AUTHOR
  and LICENSE sections.
  
  =head2 C<run ( @arguments )>
  
  Runs the command.  Parameters should be the command line parameters
  to use for running the command.  This sub should return a true value
  on success and either die or return a false value on failure.  The
  default method will first call C<process_params> with the given
  parameters, call C<usage> if there was a problem with parsing the
  parameters, or call C<process> if there was not.  It will return
  C<process>'s return value to the caller.
  
  =head2 C<process>
  
  Needs to be subclasses to provide the main functionality of the command.  This
  method will be called as part of the run method.  Should return a true value
  on success.
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_COMMAND_BASE

$fatpacked{"WGDev/Command/Base/Verbosity.pm"} = <<'WGDEV_COMMAND_BASE_VERBOSITY';
  package WGDev::Command::Base::Verbosity;
  {
    $WGDev::Command::Base::Verbosity::VERSION = '0.1207160';
  }
  # ABSTRACT: Super-class for implementing WGDev commands with verbosity levels
  use strict;
  use warnings;
  use 5.008008;
  
  use parent qw(WGDev::Command::Base);
  
  sub new {
      my $class = shift;
      my $self  = $class->SUPER::new(@_);
      $self->{verbosity} = 1;
      $self->{tab_level} = 0;
      return $self;
  }
  
  sub config_options {
      return qw(
          verbose|v
          quiet|q
      );
  }
  
  sub option_verbose {
      my $self = shift;
      $self->{verbosity}++;
      return;
  }
  
  sub option_quiet {
      my $self = shift;
      $self->{verbosity}--;
      return;
  }
  
  sub verbosity {
      my $self = shift;
      if (@_) {
          return $self->{verbosity} = shift;
      }
      return $self->{verbosity};
  }
  
  sub report {
      my $self          = shift;
      my $message       = pop;
      my $verbose_limit = shift;
      if ( !defined $verbose_limit ) {
          $verbose_limit = 1;
      }
      return
          if $verbose_limit > $self->verbosity;
      my $tabs = "\t" x $self->tab_level;
      print $tabs . $message;
      return 1;
  }
  
  sub tab_level {
      my $self = shift;
      if (@_) {
          $self->{tab_level} += shift;
      }
      return $self->{tab_level};
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Command::Base::Verbosity - Super-class for implementing WGDev commands with verbosity levels
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      package WGDev::Command::Mine;
      use WGDev::Command::Base::Verbosity;
      @ISA = qw(WGDev::Command::Base::Verbosity);
  
      sub process {
          my $self = shift;
          $self->report("Running my command\n");
          return 1;
      }
  
  =head1 DESCRIPTION
  
  A super-class useful for implementing WGDev command modules.  Parses the
  C<--verbose> and C<--quiet> command line options.
  
  =head1 METHODS
  
  =head2 C<verbosity ( [ $verbosity ] )>
  
  Sets or returns the verbosity.  This is modified when parsing parameters.  Defaults to 1.
  
  =head2 C<report ( [ $verbosity, ] $message )>
  
  Prints messages based on the current verbosity level.  If given two
  parameters, the first must be the verbosity level to start printing the
  message at.  The second parameter is the message to print.  Will also accept
  a single parameter of a message to print starting at verbosity level 1.
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_COMMAND_BASE_VERBOSITY

$fatpacked{"WGDev/Command/Batchedit.pm"} = <<'WGDEV_COMMAND_BATCHEDIT';
  package WGDev::Command::Batchedit;
  {
    $WGDev::Command::Batchedit::VERSION = '0.1207160';
  }
  # ABSTRACT: Edits assets by URL or asset ID with a pattern and a string
  #           so it can be used in a shell script / batch file
  use strict;
  use warnings;
  use 5.008008;
  
  use parent qw(WGDev::Command::Base);
  
  use WGDev ();
  
  sub config_options {
      return qw(
          command=s
          tree=s@
          class=s@
          pattern=s
          string=s
      );
  }
  
  sub process {
      my $self = shift;
      my $wgd  = $self->wgd;
  
      my @assets_to_edit = $self->get_assets_data;
  
      if ( !@assets_to_edit ) {
          WGDev::X->throw('No assets to edit!');
      }
  
      # get pattern to match
      my $pattern = $self->{options}->{pattern};
  
      # get replacement string
      my $string  = $self->{options}->{string};
  
      my $output_format = "%-8s: %-30s (%22s) %s\n";
  
      my $version_tag;
      for my $asset_to_edit (@assets_to_edit) {
          my $asset_text = $asset_to_edit->{text};
          my $old_asset_text = $asset_text;
          $asset_text =~ s/$pattern/$string/xmsg;
          if ( $asset_text eq $old_asset_text ) {
              printf $output_format,
                  'Skipping', ( $asset_to_edit->{url} || $asset_to_edit->{title} ),
                  ( $asset_to_edit->{asset_id} || q{} ), $asset_to_edit->{title};
              next;
          }
          $version_tag ||= do {
              require WebGUI::VersionTag;
              my $vt = WebGUI::VersionTag->getWorking( $wgd->session );
              $vt->set( { name => 'WGDev Asset Editor' } );
              $vt;
          };
          my $asset_data = $wgd->asset->deserialize($asset_text);
          my $asset;
          my $parent;
          if ( $asset_data->{parent} ) {
              $parent = eval { $wgd->asset->find( $asset_data->{parent} ) };
          }
          if ( $asset_to_edit->{asset_id} ) {
              $asset = $wgd->asset->by_id( $asset_to_edit->{asset_id}, undef,
                  $asset_to_edit->{revision} );
              $asset = $asset->addRevision(
                  $asset_data,
                  undef,
                  {
                      skipAutoCommitWorkflows => 1,
                      skipNotification        => 1,
                  } );
              if ($parent) {
                  $asset->setParent($parent);
              }
          }
          else {
              $parent ||= $wgd->asset->import_node;
              my $asset_id = $asset_data->{assetId};
              $asset = $parent->addChild(
                  $asset_data,
                  $asset_id,
                  undef,
                  {
                      skipAutoCommitWorkflows => 1,
                      skipNotification        => 1,
                  } );
          }
          printf $output_format, ( $asset_to_edit->{asset_id} ? 'Updating' : 'Adding' ),
              $asset->get('url'), $asset->getId, $asset->get('title');
      }
  
      if ($version_tag) {
          $version_tag->commit;
      }
      return 1;
  }
  
  sub get_assets_data {
      my $self = shift;
      my $wgd  = $self->wgd;
      my @assets_data;
      for my $asset_spec ( $self->arguments ) {
          my $asset_data = eval { $self->get_asset_data($asset_spec) };
          if ( !$asset_data ) {
              warn $@;
              next;
          }
          push @assets_data, $asset_data;
      }
      if ( !$self->option('tree') ) {
          return @assets_data;
      }
      for my $parent_spec ( @{ $self->option('tree') } ) {
          my $parent = $wgd->asset->find($parent_spec) || do {
              warn "$parent_spec is not a valid asset!\n";
              next;
          };
          my $options = {};
          if ( $self->option('class') ) {
              my @classes = @{ $self->option('class') };
              for (@classes) {
                  s/^(?:(?:WebGUI::Asset)?::)?/WebGUI::Asset::/msx;
              }
              $options->{includeOnlyClasses} = \@classes;
          }
          my $assets
              = $parent->getLineage( [qw(self descendants)], $options );
          for my $asset_id ( @{$assets} ) {
              my $asset_data = $self->get_asset_data($asset_id);
              if ( !$asset_data ) {
                  next;
              }
              push @assets_data, $asset_data;
          }
      }
      return @assets_data;
  }
  
  sub get_asset_data {
      my $self  = shift;
      my $asset = shift;
  
      my $wgd_asset = $self->wgd->asset;
      if ( !ref $asset ) {
          $asset = eval { $wgd_asset->find($asset) };
          if ( !$asset ) {
              die $@;
          }
      }
  
      my $asset_text = $self->wgd->asset->serialize($asset);
      my $short_class = ref $asset || $asset;
      $short_class =~ s/^WebGUI::Asset:://msx;
  
      return {
          text     => $asset_text,
          class    => ref $asset || $asset,
          asset_id => $asset->getId,
          url      => $asset->get('url'),
          title    => $asset->get('title'),
      };
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Command::Batchedit - Edits assets by URL or asset ID with a pattern and a string
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      wgd batchedit --pattern=<pattern> --string=<string> <asset> [<asset> ...]
      wgd batchedit --tree=<asset> --pattern=<pattern> --string=<string> [--tree=<asset> ...] [--class=<class> ...]
  
  =head1 DESCRIPTION
  
  Edits assets in-place by replacing all matching 'pattern's with 'string'.
  If modifications are made, the assets are updated.
  
  =head1 METHODS
  
  =head2 C<get_assets_data>
  
  Creates and returns an array of hash references with information about
  the assets and exported files. Also follows the C<--tree> option.
  
  =head2 C<get_asset_data ( $asset_or_class )>
  
  Accepts an asset, returning a hash reference of information about the
  asset.
  
  =head1 OPTIONS
  
  =over 8
  
  =item C<--pattern=>
  
  Pattern to match against for replacing.
  
  =item C<--string=>
  
  Replacement string for the matched pattern.
  
  =item C<< <asset> >>
  
  Either an asset URL or ID.  As many as desired can be specified.
  Prepending with a slash will force it to be interpreted as a URL.
  
  =item C<--tree=>
  
  Will open specified asset and all descendants in editor.  Can be specified
  multiple times.
  
  =item C<--class=>
  
  Only used with --tree option.  Limits exported assets to specified classes.
  Can be specified as a full (C<WebGUI::Asset::Template>) or abbreviated
  (C<Template>) class name.
  
  =back
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_COMMAND_BATCHEDIT

$fatpacked{"WGDev/Command/Build.pm"} = <<'WGDEV_COMMAND_BUILD';
  package WGDev::Command::Build;
  {
    $WGDev::Command::Build::VERSION = '0.1207160';
  }
  # ABSTRACT: Builds an SQL script and uploads for site creation
  use strict;
  use warnings;
  use 5.008008;
  
  use parent qw(WGDev::Command::Base::Verbosity);
  
  use File::Spec ();
  use WGDev::X   ();
  use WGDev::File;
  
  sub config_options {
      return (
          shift->SUPER::config_options, qw(
              sql|s
              uploads|u
              ) );
  }
  
  sub parse_params {
      my $self   = shift;
      my $result = $self->SUPER::parse_params(@_);
      if ( !defined $self->option('sql') && !defined $self->option('uploads') )
      {
          $self->option( 'sql',     1 );
          $self->option( 'uploads', 1 );
      }
      return $result;
  }
  
  sub process {
      my $self = shift;
  
      if ( $self->option('sql') ) {
          $self->create_db_script;
      }
  
      if ( $self->option('uploads') ) {
          $self->update_local_uploads;
      }
      return 1;
  }
  
  sub create_db_script {
      my $self = shift;
      my $wgd  = $self->wgd;
  
      my $version = $wgd->version->database( $wgd->db->connect );
      $self->report("WebGUI version: $version\n");
  
      $self->report('Creating database dump... ');
      my $wg8 = $wgd->version->module =~ /^8[.]/msx;
      my $db_file = $wg8 ? do {
          require WebGUI::Paths;
          WebGUI::Paths->defaultCreateSQL;
      } : File::Spec->catfile( $wgd->root, 'docs', 'create.sql' );
      open my $out, q{>}, $db_file
          or WGDev::X::IO::Write->throw( path => $db_file );
  
      $self->write_db_header($out);
      $self->write_db_structure($out);
      $self->write_db_data($out);
      $self->write_db_footer($out);
  
      close $out
          or WGDev::X::IO::Write->throw( path => $db_file );
      $self->report("Done.\n");
      return 1;
  }
  
  sub write_db_header {
      my $self = shift;
      my $out  = shift;
      print {$out} <<'END_SQL';
  SET @OLD_CHARACTER_SET_CLIENT       = @@CHARACTER_SET_CLIENT;
  SET @OLD_CHARACTER_SET_RESULTS      = @@CHARACTER_SET_RESULTS;
  SET @OLD_CHARACTER_SET_CONNECTION   = @@CHARACTER_SET_CONNECTION;
  SET @OLD_COLLATION_CONNECTION       = @@COLLATION_CONNECTION;
  SET @OLD_TIME_ZONE                  = @@TIME_ZONE;
  SET @OLD_UNIQUE_CHECKS              = @@UNIQUE_CHECKS;
  SET @OLD_FOREIGN_KEY_CHECKS         = @@FOREIGN_KEY_CHECKS;
  SET @OLD_SQL_MODE                   = @@SQL_MODE;
  SET @OLD_SQL_NOTES                  = @@SQL_NOTES;
  
  SET CHARACTER_SET_CLIENT            = 'utf8';
  SET CHARACTER_SET_RESULTS           = 'utf8';
  SET CHARACTER_SET_CONNECTION        = 'utf8';
  SET TIME_ZONE                       = '+00:00';
  SET UNIQUE_CHECKS                   = 0;
  SET FOREIGN_KEY_CHECKS              = 0;
  SET SQL_MODE                        = 'NO_AUTO_VALUE_ON_ZERO';
  SET SQL_NOTES                       = 0;
  END_SQL
      return;
  }
  
  sub write_db_footer {
      my $self = shift;
      my $out  = shift;
      print {$out} <<'END_SQL';
  SET CHARACTER_SET_CLIENT        = @OLD_CHARACTER_SET_CLIENT;
  SET CHARACTER_SET_RESULTS       = @OLD_CHARACTER_SET_RESULTS;
  SET CHARACTER_SET_CONNECTION    = @OLD_CHARACTER_SET_CONNECTION;
  SET COLLATION_CONNECTION        = @OLD_COLLATION_CONNECTION;
  SET TIME_ZONE                   = @OLD_TIME_ZONE;
  SET UNIQUE_CHECKS               = @OLD_UNIQUE_CHECKS;
  SET FOREIGN_KEY_CHECKS          = @OLD_FOREIGN_KEY_CHECKS;
  SET SQL_MODE                    = @OLD_SQL_MODE;
  SET SQL_NOTES                   = @OLD_SQL_NOTES;
  END_SQL
      return;
  }
  
  sub write_db_structure {
      my $self = shift;
      my $out  = shift;
      my $wgd  = $self->wgd;
  
      open my $in, q{-|}, 'mysqldump',
          $wgd->db->command_line( '--compact', '--no-data',
          '--compatible=mysql40' )
          or WGDev::X::System->throw('Unable to run mysqldump');
      my $statement;
      while ( my $line = <$in> ) {
          next
              if $line =~ /\bSET[^=]+=\s*[@][@]character_set_client;/msxi
                  || $line =~ /\bSET\s+character_set_client\b/msxi;
          if ( !$statement && $line =~ /\A(CREATE[ ]TABLE)/msx ) {
              $statement = $1;
          }
          if ( $statement && $line =~ /;\n?\z/msx ) {
              if ( $statement eq 'CREATE TABLE' ) {
                  $line =~ s/TYPE=(InnoDB|MyISAM)/ENGINE=$1/;
                  $line =~ s/;(\n?)\z/ CHARSET=utf8;$1/msx;
              }
              undef $statement;
          }
          print {$out} $line;
      }
      close $in
          or WGDev::X::System->throw('Unable to run mysqldump');
      return 1;
  }
  
  sub write_db_data {
      my $self = shift;
      my $out  = shift;
      my $wgd  = $self->wgd;
  
      my $dbh     = $wgd->db->connect;
      my $version = $wgd->version->database($dbh);
  
      my %skip_data_tables = map { $_ => 1 } qw(
          userSession     userSessionScratch
          webguiVersion   userLoginLog
          assetHistory    cache
      );
  
      my @tables;
  
      my $sth = $dbh->table_info( undef, undef, q{%}, undef );
      while ( ( undef, undef, my $table ) = $sth->fetchrow_array ) {
          next
              if $skip_data_tables{$table};
          my ($count)
              = $dbh->selectrow_array(
              'SELECT COUNT(*) FROM ' . $dbh->quote_identifier($table) );
          next
              if !$count;
          push @tables, $table;
      }
  
      open my $in, q{-|}, 'mysqldump',
          $wgd->db->command_line( '--no-create-info', '--compact',
          '--disable-keys', sort @tables, )
          or WGDev::X::System->throw('Unable to run mysqldump');
      while ( my $line = <$in> ) {
          $line =~ s{ /[*] !\d+ \s+ ([^*]+?) \s* [*]/; }{$1;}msx;
          print {$out} $line;
      }
      close $in
          or WGDev::X::System->throw('Unable to run mysqldump');
  
      print {$out} 'INSERT INTO webguiVersion '
          . '(webguiVersion,versionType,dateApplied) '
          . "VALUES ('$version','Initial Install',UNIX_TIMESTAMP());\n";
  
      return 1;
  }
  
  sub update_local_uploads {
      my $self = shift;
      my $wgd  = $self->wgd;
  
      $self->report('Loading uploads from site... ');
  
      my $wg_uploads = File::Spec->catdir( $wgd->root, 'www', 'uploads' );
      my $site_uploads    = $wgd->config->get('uploadsPath');
      WGDev::File->sync_dirs($site_uploads, $wg_uploads);
  
      $self->report("Done\n");
      return 1;
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Command::Build - Builds an SQL script and uploads for site creation
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      wgd build [-s] [-u]
  
  =head1 DESCRIPTION
  
  Uses the current database and uploads to build a new F<create.sql> and update
  the local uploads directory.  With no options, builds both the database
  script and the uploads directory.
  
  =head1 METHODS
  
  =head2 C<create_db_script>
  
  Builds the F<create.sql> database script.  This is done as a dump of the current
  database structure and data, excluding the data from some tables.
  
  =head2 C<update_local_uploads>
  
  Updates the working directory's uploads from the current site.  Files will be
  deleted or created so the two match.
  
  =head1 OPTIONS
  
  =over 8
  
  =item C<-s> C<--sql>
  
  Make F<create.sql> based on current database contents
  
  =item C<-u> C<--uploads>
  
  Make uploads based on current site's uploads
  
  =back
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_COMMAND_BUILD

$fatpacked{"WGDev/Command/Commands.pm"} = <<'WGDEV_COMMAND_COMMANDS';
  package WGDev::Command::Commands;
  {
    $WGDev::Command::Commands::VERSION = '0.1207160';
  }
  # ABSTRACT: List WGDev sub-commands
  use strict;
  use warnings;
  use 5.008008;
  
  use parent qw(WGDev::Command::Base);
  
  use WGDev::Command;
  use WGDev::Help;
  use WGDev::X ();
  
  sub needs_root {
      return;
  }
  
  sub process {
      my $self = shift;
      return $self->help;
  }
  
  sub help {
      my $class = shift;
      print "Sub-commands available:\n";
      my %abstracts = $class->command_abstracts;
      my @commands  = sort keys %abstracts;
      @commands = (
          'intro',
          'commands',
          'help',
          undef,
          grep { $_ ne 'intro' && $_ ne 'commands' && $_ ne 'help' } @commands,
      );
      for my $command (@commands) {
          if ( !defined $command ) {
              print "\n";
              next;
          }
          my $command_abstract = $abstracts{$command} || '(external command)';
          printf "    %-15s - %s\n", $command, $command_abstract;
      }
      return 1;
  }
  
  sub command_abstracts {
      my $class = shift;
      my %abstracts = map { $_ => undef } WGDev::Command->command_list;
      require Pod::PlainText;
      my $parser = Pod::PlainText->new( indent => 0, width => 1000 );
      $parser->select('NAME');
      for my $command ( keys %abstracts ) {
          my $command_module
              = eval { WGDev::Command->get_command_module($command) };
          next
              if !$command_module;
          my $pod           = WGDev::Help::package_pod($command_module);
          my $formatted_pod = q{};
          open my $pod_in, '<', \$pod
              or WGDev::X::IO->throw;
          open my $pod_out, '>', \$formatted_pod
              or WGDev::X::IO->throw;
          $parser->parse_from_filehandle( $pod_in, $pod_out );
          close $pod_in  or WGDev::X::IO->throw;
          close $pod_out or WGDev::X::IO->throw;
  
          if ( $formatted_pod =~ /^ [:\w]+ \s* - \s* (.+?) \s* $/msx ) {
              $abstracts{$command} = $1;
          }
      }
      return %abstracts;
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Command::Commands - List WGDev sub-commands
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      wgd commands
  
  =head1 DESCRIPTION
  
  Provides an overview of the available WGDev commands.
  
  =head1 METHODS
  
  =head2 C<command_abstracts>
  
  A class method which returns a hash with keys of the available
  commands and values of the module abstract extracted from POD.
  
  =head1 OPTIONS
  
  None
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_COMMAND_COMMANDS

$fatpacked{"WGDev/Command/Config.pm"} = <<'WGDEV_COMMAND_CONFIG';
  package WGDev::Command::Config;
  {
    $WGDev::Command::Config::VERSION = '0.1207160';
  }
  # ABSTRACT: Report or set WGDev configuration parameters
  use strict;
  use warnings;
  use 5.008008;
  
  use parent qw(WGDev::Command::Base);
  
  use WGDev          ();
  use WGDev::X       ();
  use WGDev::Command ();
  
  sub needs_root {
      return;
  }
  
  sub config_options {
      return qw(
          struct|s
      );
  }
  
  sub config_parse_options { return qw(gnu_getopt pass_through) }
  
  sub process {
      my $self = shift;
      my $wgd  = $self->wgd;
      my @args = $self->arguments;
  
      if ( !@args ) {
          my $usage = $self->usage(0);
          warn $usage;
          return;
      }
  
      my ( $config_param, $value ) = @args;
      my @config_path = split /[.]/msx, $config_param;
  
      if ( defined $value ) {
          if ( $value =~ s/\A@//msx ) {
              my $file = $value;
              my $fh;
              if ( $file eq q{-} ) {
                  open $fh, '<&=', \*STDIN
                      or WGDev::X::IO::Read->throw;
              }
              else {
                  open $fh, '<', $file
                      or WGDev::X::IO::Read->throw( path => $file );
              }
              $value = do { local $/; <$fh> };
              close $fh
                  or WGDev::X::IO::Read->throw( path => $file );
          }
          if ( $self->option('struct') ) {
              $value =~ s/\A \s* ( [[{] ) /--- $1/msx;
              $value .= "\n";
              eval {
                  $value = WGDev::yaml_decode($value);
                  1;
              } or WGDev::X->throw('Invalid or unsupported format.');
          }
      }
      my $param
          = $wgd->wgd_config( \@config_path, defined $value ? $value : () );
      if ( defined $value && defined $param ) {
          $wgd->write_wgd_config;
          return 1;
      }
      if ( ref $param ) {
          $param = WGDev::yaml_encode($param);
          $param =~ s/\A---(?:\Q {}\E)?\n?//msx;
      }
      elsif ( !defined $param ) {
          return 0;
      }
      $param =~ s/\n?\z/\n/msx;
      print $param;
      return 1;
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Command::Config - Report or set WGDev configuration parameters
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      wgd config [--struct] <config path> [<value>]
  
  =head1 DESCRIPTION
  
  Report or set WGDev configuration parameters.
  
  =head1 OPTIONS
  
  =over 8
  
  =item C<-s> C<--struct>
  
  When setting a config value, specifies that the value should be treated as a
  data structure formatted as YAML or JSON.
  
  =item C<< <config path> >>
  
  Path of the the config variable to retrieve.  Sub-level options are specified
  as a period separated list of keys.  Complex options will be returned formatted
  as YAML.
  
  =item C<< <value> >>
  
  The value to set the config option to.
  
  =back
  
  =head1 CONFIGURATION
  
  The WGDev config file is a JSON formatted file existing as either
  F</etc/wgdevcfg> or F<.wgdevcfg> in the current user's home directory.
  
  A simple config file looks like:
  
   {
      "command" : {
         "webgui_root" : "/data/WebGUI",
         "webgui_config" : "dev.localhost.localdomain.conf"
      }
   }
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_COMMAND_CONFIG

$fatpacked{"WGDev/Command/Db.pm"} = <<'WGDEV_COMMAND_DB';
  package WGDev::Command::Db;
  {
    $WGDev::Command::Db::VERSION = '0.1207160';
  }
  # ABSTRACT: Connect to database with the MySQL client
  use strict;
  use warnings;
  use 5.008008;
  
  use parent qw(WGDev::Command::Base);
  
  use WGDev::X ();
  
  sub config_options {
      return qw(
          print|p
          dump|d:s
          load|l=s
          clear|c
          show
      );
  }
  
  sub config_parse_options { return qw(gnu_getopt pass_through) }
  
  sub process {
      my $self         = shift;
      my $db           = $self->wgd->db;
      my @command_line = $db->command_line( $self->arguments );
      if (  ( defined $self->option('print') || 0 )
          + ( defined $self->option('dump')  || 0 )
          + ( defined $self->option('load')  || 0 )
          + ( defined $self->option('clear') || 0 ) > 1 )
      {
          WGDev::X->throw('Multiple database operations specified!');
      }
  
      if ( $self->option('print') ) {
          print join q{ }, map {"'$_'"} @command_line;
          return 1;
      }
      if ( $self->option('clear') ) {
          $db->clear;
          return 1;
      }
      if ( defined $self->option('load') ) {
          if ( $self->option('load') && $self->option('load') ne q{-} ) {
              $db->clear;
              $db->load( $self->option('load') );
              return 1;
          }
      }
      if ( defined $self->option('dump') ) {
          if ( $self->option('dump') && $self->option('dump') ne q{-} ) {
              $db->dump( $self->option('dump') );
              return 1;
          }
          else {
              my $return = system {'mysqldump'} 'mysqldump', @command_line;
              return $return ? 0 : 1;
          }
      }
      if ( defined $self->option('show') ) {
          my $return = system {'mysqlshow'} 'mysqlshow', @command_line;
          return $return ? 0 : 1;
      }
      my $return = system {'mysql'} 'mysql', @command_line;
      return $return ? 0 : 1;
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Command::Db - Connect to database with the MySQL client
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      wgd db [-p | -d | -l | -c | --show] [mysql options]
  
  =head1 DESCRIPTION
  
  Opens the C<mysql> client to your WebGUI database, loads or dumps a database
  script, or displays database information, or clears a database's contents.
  
  =head1 OPTIONS
  
  Any arguments not recognized will be passed through to the C<mysql> or
  C<mysqldump> commands as applicable.
  
  =over 8
  
  =item C<-p> C<--print>
  
  Prints out the command options that would be passed to C<mysql>
  
  =item C<-d> C<--dump=>
  
  Dumps the database as an SQL script.  If a file is specified, dumps to that
  file.  Otherwise, dumps to standard out.
  
  =item C<-l> C<--load=>
  
  Loads a database script into the database.  Database script must be specified.
  
  =item C<-c> C<--clear>
  
  Clears the database, removing all tables.
  
  =item C<--show>
  
  Shows database information via C<mysqlshow>.
  
  For example, to display a summary of the number of columns and rows in each table,
  use C<mysqlshow>'s C<--count> option:
  
      wgd db --show --count
  
  =back
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_COMMAND_DB

$fatpacked{"WGDev/Command/Dist.pm"} = <<'WGDEV_COMMAND_DIST';
  package WGDev::Command::Dist;
  {
    $WGDev::Command::Dist::VERSION = '0.1207160';
  }
  # ABSTRACT: Create a distribution file for WebGUI
  use strict;
  use warnings;
  use 5.008008;
  
  use parent qw(WGDev::Command::Base);
  
  use File::Spec ();
  
  sub config_options {
      return (
          shift->SUPER::config_options, qw(
              buildDir|b=s
              ) );
  }
  
  sub needs_config {
      return;
  }
  
  sub process {
      my $self = shift;
      my $wgd  = $self->wgd;
      require File::Temp;
      File::Temp->VERSION(0.19); ##no critic (ProhibitMagicNumbers)
      require File::Copy;
      require Cwd;
  
      my ( $version, $status ) = $wgd->version->module;
      my $build_dir = $self->option('buildDir');
      my $build_root;
      if ($build_dir) {
          $build_root = $build_dir;
          mkdir $build_root;
      }
      if ( $build_root && !-e $build_root ) {
          $build_root = File::Temp->newdir;
      }
      my $build_webgui = File::Spec->catdir( $build_root, 'WebGUI' );
      my $build_docs   = File::Spec->catdir( $build_root, 'api' );
      my $cwd          = Cwd::cwd();
  
      mkdir $build_webgui;
      $self->export_files($build_webgui);
      my $inst_dir = $build_dir || $cwd;
      if ( !fork ) {
          chdir $build_root;
          exec 'tar', 'czf',
              File::Spec->catfile( $inst_dir,
              "webgui-$version-$status.tar.gz" ),
              'WebGUI';
      }
      wait;
  
      mkdir $build_docs;
      $self->generate_docs($build_docs);
      if ( !fork ) {
          chdir $build_root;
          exec 'tar', 'czf',
              File::Spec->catfile(
              $inst_dir, "webgui-api-$version-$status.tar.gz"
              ),
              'api';
      }
      wait;
      return 1;
  }
  
  sub export_files {
      my $self    = shift;
      my $to_root = shift;
      my $from    = $self->wgd->root;
  
      if ( -e File::Spec->catdir( $from, '.git' ) ) {
          system 'git', '--git-dir=' . File::Spec->catdir( $from, '.git' ),
              'checkout-index', '-a', '--prefix=' . $to_root . q{/};
      }
      elsif ( -e File::Spec->catdir( $from, '.svn' ) ) {
          system 'svn', 'export', $from, $to_root;
      }
      else {
          system 'cp', '-r', $from, $to_root;
      }
  
      for my $file (
          [ 'docs', 'previousVersion.sql' ],
          [ 'etc',  '*.conf' ],
          [ 'sbin', 'preload.custom' ],
          [ 'sbin', 'preload.exclude' ] )
      {
          my $file_path = File::Spec->catfile( $to_root, @{$file} );
          for my $file ( glob $file_path ) {
              unlink $file;
          }
      }
      return $to_root;
  }
  
  sub generate_docs {
      my $self    = shift;
      my $to_root = shift;
      my $from    = $self->wgd->root;
      require File::Find;
      require File::Path;
      require Pod::Html;
      require File::Temp;
      File::Temp->VERSION(0.19); ##no critic (ProhibitMagicNumbers)
      my $code_dir = File::Spec->catdir( $from, 'lib', 'WebGUI' );
      my $temp_dir = File::Temp->newdir;
      File::Find::find( {
              no_chdir => 1,
              wanted   => sub {
                  no warnings 'once';
                  my $code_file = $File::Find::name;
                  return
                      if -d $code_file;
                  my $doc_file = $code_file;
                  return
                      if $doc_file =~ /\b\QOperation.pm\E$/msx;
                  return
                      if $doc_file !~ s/\Q.pm\E$/.html/msx;
                  $doc_file = File::Spec->rel2abs(
                      File::Spec->abs2rel( $doc_file, $code_dir ), $to_root );
                  my $directory = File::Spec->catpath(
                      ( File::Spec->splitpath($doc_file) )[ 0, 1 ] );
                  File::Path::mkpath($directory);
                  Pod::Html::pod2html(
                      '--quiet',
                      '--noindex',
                      '--infile=' . $code_file,
                      '--outfile=' . $doc_file,
                      '--cachedir=' . $temp_dir,
                  );
              },
          },
          $code_dir
      );
      return $to_root;
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Command::Dist - Create a distribution file for WebGUI
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      wgd dist [-c] [-d] [-b /data/builds]
  
  =head1 DESCRIPTION
  
  Generates distribution files containing WebGUI or the WebGUI API.
  
  =head1 METHODS
  
  =head2 C<export_files ( $directory )>
  
  Exports the WebGUI root directory, excluding common site specific files, to
  the specified directory.
  
  =head2 C<generate_docs ( $directory )>
  
  Generate API documentation for WebGUI using Pod::Html in the specified
  directory.
  
  =head1 OPTIONS
  
  By default, generates both a code and API documentation package.
  
  =over 8
  
  =item C<-c> C<--code>
  
  Generates a code distribution
  
  =item C<-d> C<--documentation>
  
  Generates an API documentation distribution
  
  =item C<-b> C<--buildDir>
  
  Install the directories and tarballs in a different location.  If no build directory
  is specified, it will create a temp file.
  
  =back
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_COMMAND_DIST

$fatpacked{"WGDev/Command/Edit.pm"} = <<'WGDEV_COMMAND_EDIT';
  package WGDev::Command::Edit;
  {
    $WGDev::Command::Edit::VERSION = '0.1207160';
  }
  # ABSTRACT: Edits assets by URL
  use strict;
  use warnings;
  use 5.008008;
  
  use parent qw(WGDev::Command::Base);
  
  use WGDev ();
  
  sub config_options {
      return qw(
          command=s
          tree=s@
          class=s@
      );
  }
  
  sub process {
      my $self = shift;
      my $wgd  = $self->wgd;
  
      my @files = $self->export_asset_data;
  
      if ( !@files ) {
          WGDev::X->throw('No assets to edit!');
      }
  
      ## no critic (ProhibitParensWithBuiltins)
      my $command = $self->option('command') || $ENV{EDITOR} || 'vi';
      system join( q{ }, $command, map { $_->{filename} } @files );
  
      my $output_format = "%-8s: %-30s (%22s) %s\n";
  
      my $version_tag;
      for my $file (@files) {
          open my $fh, '<:encoding(UTF-8)', $file->{filename} or next;
          my $asset_text = do { local $/; <$fh> };
          close $fh or next;
          unlink $file->{filename};
          if ( $asset_text eq $file->{text} ) {
              printf $output_format,
                  'Skipping', ( $file->{url} || $file->{title} ),
                  ( $file->{asset_id} || q{} ), $file->{title};
              next;
          }
          $version_tag ||= do {
              require WebGUI::VersionTag;
              my $vt = WebGUI::VersionTag->getWorking( $wgd->session );
              $vt->set( { name => 'WGDev Asset Editor' } );
              $vt;
          };
          my $asset_data = $wgd->asset->deserialize($asset_text);
          my $asset;
          my $parent;
          if ( $asset_data->{parent} ) {
              $parent = eval { $wgd->asset->find( $asset_data->{parent} ) };
          }
          if ( $file->{asset_id} ) {
              $asset = $wgd->asset->by_id( $file->{asset_id}, undef,
                  $file->{revision} );
              $asset = $asset->addRevision(
                  $asset_data,
                  undef,
                  {
                      skipAutoCommitWorkflows => 1,
                      skipNotification        => 1,
                  } );
              if ($parent) {
                  $asset->setParent($parent);
              }
          }
          else {
              $parent ||= $wgd->asset->import_node;
              my $asset_id = $asset_data->{assetId};
              $asset = $parent->addChild(
                  $asset_data,
                  $asset_id,
                  undef,
                  {
                      skipAutoCommitWorkflows => 1,
                      skipNotification        => 1,
                  } );
          }
          printf $output_format, ( $file->{asset_id} ? 'Updating' : 'Adding' ),
              $asset->get('url'), $asset->getId, $asset->get('title');
      }
  
      if ($version_tag) {
          $version_tag->commit;
      }
      return 1;
  }
  
  sub export_asset_data {
      my $self = shift;
      my $wgd  = $self->wgd;
      my @files;
      for my $asset_spec ( $self->arguments ) {
          my $file_data = eval { $self->write_temp($asset_spec) };
          if ( !$file_data ) {
              warn $@;
              next;
          }
          push @files, $file_data;
      }
      if ( $self->option('tree') ) {
          for my $parent_spec ( @{ $self->option('tree') } ) {
              my $parent = $wgd->asset->find($parent_spec) || do {
                  warn "$parent_spec is not a valid asset!\n";
                  next;
              };
              my $options = {};
              if ( $self->option('class') ) {
                  my @classes = @{ $self->option('class') };
                  for (@classes) {
                      s/^(?:(?:WebGUI::Asset)?::)?/WebGUI::Asset::/msx;
                  }
                  $options->{includeOnlyClasses} = \@classes;
              }
              my $assets
                  = $parent->getLineage( [qw(self descendants)], $options );
              for my $asset_id ( @{$assets} ) {
                  my $file_data = $self->write_temp($asset_id);
                  if ( !$file_data ) {
                      next;
                  }
                  push @files, $file_data;
              }
          }
      }
      return @files;
  }
  
  sub write_temp {
      my $self  = shift;
      my $asset = shift;
      require File::Temp;
  
      my $wgd_asset = $self->wgd->asset;
      if ( !ref $asset ) {
          $asset = eval { $wgd_asset->find($asset) }
              || eval { scalar $wgd_asset->validate_class($asset) };
          if ( !$asset ) {
              die $@;
          }
      }
  
      my $short_class = ref $asset || $asset;
      $short_class =~ s/^WebGUI::Asset:://msx;
  
      my ( $fh, $filename ) = File::Temp::tempfile();
      binmode $fh, ':encoding(UTF-8)';
      my $asset_text = $self->wgd->asset->serialize($asset);
  
      print {$fh} $asset_text;
      close $fh or return;
      return {
          filename => $filename,
          text     => $asset_text,
          class    => ref $asset || $asset,
          ref $asset
          ? (
              asset_id => $asset->getId,
              url      => $asset->get('url'),
              title    => $asset->get('title'),
              )
          : ( title => 'New ' . $short_class, ),
      };
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Command::Edit - Edits assets by URL
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      wgd edit [--command=<command>] <asset> [<asset> ...]
      wgd edit --tree=<asset> [--tree=<asset> ...] [--class=<class> ...]
  
  =head1 DESCRIPTION
  
  Exports asset to temporary files, then opens them in your prefered editor.
  If modifications are made, the assets are updated.
  
  =head1 METHODS
  
  =head2 C<export_asset_data>
  
  For each item in C<arguments>, exports the asset serialized to text to a
  temporary file.  Also follows the C<--tree> option.  Returns an array of
  hash references with information about the assets and exported files.
  
  =head2 C<write_temp ( $asset_or_class )>
  
  Accepts an asset or a class name and exports it serialized as a text file.
  Returns a hash reference of information about the file ans asset.
  
  =head1 OPTIONS
  
  =over 8
  
  =item C<--command=>
  
  Command to be executed.  If not specified, uses the EDITOR environment
  variable.  If that is not specified, uses C<$EDITOR> or C<vi>.
  
  =item C<< <asset> >>
  
  Either an asset URL, ID, or class name.  As many can be specified as desired.
  Prepending with a slash will force it to be interpreted as a URL.  Class names
  specified will be opened with a skeleton for the asset type.
  
  =item C<--tree=>
  
  Will open specified asset and all descendants in editor.  Can be specified
  multiple times.
  
  =item C<--class=>
  
  Only used with --tree option.  Limits exported assets to specified classes.
  Can be specified as a full (C<WebGUI::Asset::Template>) or abbreviated
  (C<Template>) class name.
  
  =back
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_COMMAND_EDIT

$fatpacked{"WGDev/Command/Export.pm"} = <<'WGDEV_COMMAND_EXPORT';
  package WGDev::Command::Export;
  {
    $WGDev::Command::Export::VERSION = '0.1207160';
  }
  # ABSTRACT: Exports assets to files
  use strict;
  use warnings;
  use 5.008008;
  
  use parent qw(WGDev::Command::Base);
  
  use WGDev::X ();
  
  sub config_options {
      return qw(
          stdout
      );
  }
  
  sub process {
      my $self = shift;
  
      my $wgd_asset = $self->wgd->asset;
      for my $asset_spec ( $self->arguments ) {
          my $asset = eval { $wgd_asset->find($asset_spec) }
              || eval { $wgd_asset->validate_class($asset_spec) };
          if ( !$asset ) {
              warn $@;
              next;
          }
          my $asset_text = $self->wgd->asset->serialize($asset);
          if ( $self->option('stdout') ) {
              print $asset_text;
          }
          else {
              my $filename = $self->export_filename($asset);
              print "Writing $filename...\n";
              open my $fh, '>', $filename
                  or WGDev::X::IO::Write->throw( path => $filename );
              print {$fh} $asset_text;
              close $fh
                  or WGDev::X::IO::Write->throw( path => $filename );
          }
      }
      return 1;
  }
  
  sub export_filename {
      my $self        = shift;
      my $asset       = shift;
      my $class       = ref $asset || $asset;
      my $short_class = $class;
      $short_class =~ s/.*:://msx;
      my $extension = lc $short_class;
      $extension =~ tr/aeiouy//d;
      $extension =~ tr/a-z//s;
      my $filename;
  
      if ( ref $asset ) {
          $filename = ( split m{/}msx, $asset->get('url') )[-1];
      }
      else {
          $filename = 'new-' . lc $short_class;
      }
      $filename .= ".$extension";
      return $filename;
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Command::Export - Exports assets to files
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      wgd export [--stdout] <asset> [<asset> ...]
  
  =head1 DESCRIPTION
  
  Exports asset to files.
  
  =head1 METHODS
  
  =head2 C<export_filename ( $asset_or_class )>
  
  Calculates the file name to export an asset as.  Accepts a parameter of the
  asset object or an asset class name.  The file name will be the last portion
  of the asset's URL, with an extension based on the asset's class name.  If
  provided only a class name, the file name will also be based on the class
  name.
  
  =head1 OPTIONS
  
  =over 8
  
  =item C<--stdout>
  
  Exports to standard out instead of a file.  This only makes sense with a single asset specified.
  
  =item C<< <asset> >>
  
  Either an asset URL, ID, class name.  As many can be specified as desired.
  Prepending with a slash will force it to be interpreted as a URL.  Asset
  classes will generate skeletons of export files for the given class.
  
  =back
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_COMMAND_EXPORT

$fatpacked{"WGDev/Command/Export/Branch.pm"} = <<'WGDEV_COMMAND_EXPORT_BRANCH';
  package WGDev::Command::Export::Branch;
  {
    $WGDev::Command::Export::Branch::VERSION = '0.1207160';
  }
  # ABSTRACT: Export a branch of assets
  use strict;
  use warnings;
  use 5.008008;
  
  use parent qw(WGDev::Command::Base::Verbosity);
  
  use File::Spec ();
  use Cwd        ();
  use constant LINEAGE_LEVEL_LENGTH => 6;
  
  sub config_options {
      return (
          shift->SUPER::config_options, qw(
              to|t=s
              hier!
              ) );
  }
  
  sub parse_params {
      my $self   = shift;
      my $result = $self->SUPER::parse_params(@_);
      $self->set_option_default( 'hier', 1 );
      return $result;
  }
  
  sub process {
      my $self = shift;
      require File::Path;
  
      my $wgd_asset = $self->wgd->asset;
  
      my $base_dir = $self->option('to') || Cwd::cwd;
      my $heir = $self->option('hier');
  
      for my $asset_spec ( $self->arguments ) {
          my $base_asset = eval { $wgd_asset->find($asset_spec) };
          if ( !$base_asset ) {
              warn $@;
              next;
          }
          $self->report( 'Exporting "' . $base_asset->get('title') . "...\n" );
          if ( $self->verbosity ) {
              $self->tab_level(1);
          }
          my $iter
              = $base_asset->getLineageIterator( [ 'self', 'descendants' ] );
          my $base_depth
              = length( $base_asset->get('lineage') ) / LINEAGE_LEVEL_LENGTH;
          while ( my $asset = $iter->() ) {
              my @url_segments;
              if ($heir) {
                  my $parent = $asset;
                  my $depth
                      = length( $asset->get('lineage') ) / LINEAGE_LEVEL_LENGTH;
                  while (1) {
                      my $url_part = $parent->get('url');
                      $url_part =~ s{.*/}{}msx;
                      unshift @url_segments, $url_part;
                      last
                          if --$depth < $base_depth;
                      $parent = $parent->getParent;
                  }
              }
              else {
                  @url_segments = split m{/}msx, $asset->get('url');
              }
              my $extension = $wgd_asset->export_extension($asset);
              my $filename  = ( pop @url_segments ) . ".$extension";
              $self->report( 0,
                  File::Spec->catfile( @url_segments, $filename ) . "\n" );
              my $dir = File::Spec->catdir( $base_dir, @url_segments );
              my $full_path = File::Spec->catfile( $dir, $filename );
              File::Path::mkpath($dir);
              my $asset_text = $wgd_asset->serialize($asset);
              open my $fh, '>', $full_path
                  or WGDev::X::IO::Write->throw( path => $full_path );
              print {$fh} $asset_text;
              close $fh
                  or WGDev::X::IO::Write->throw( path => $full_path );
          }
          if ( $self->verbosity ) {
              $self->tab_level(-1);
          }
          $self->report("Done.\n");
      }
      return 1;
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Command::Export::Branch - Export a branch of assets
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      wgd export-branch [--no-hier] [--to=<output dir>] <asset> [<asset> ...]
  
  =head1 DESCRIPTION
  
  Exports a branch of assets as serialized files.
  
  =head1 OPTIONS
  
  =over 8
  
  =item C<--[no-]hier>
  
  Exports assets in a directories based on their hierarchy in the
  asset tree.  If not enabled, the serialized assets' location is
  based directly on their URLs. Enabled by default.
  
  =item C<-t> C<--to=>
  
  Output directory to place the exported files in.  If not specified,
  files are placed in the current directory.
  
  =back
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_COMMAND_EXPORT_BRANCH

$fatpacked{"WGDev/Command/For/Each.pm"} = <<'WGDEV_COMMAND_FOR_EACH';
  package WGDev::Command::For::Each;
  {
    $WGDev::Command::For::Each::VERSION = '0.1207160';
  }
  # ABSTRACT: Run command for each available config file
  use strict;
  use warnings;
  use 5.008008;
  
  use parent qw(WGDev::Command::Base);
  
  sub new {
      my $class = shift;
      my $self = $class->SUPER::new(@_);
      $self->{commands} = [];
      return $self;
  }
  
  sub needs_config {
      return;
  }
  
  sub needs_root {
      return 1;
  }
  
  sub config_options {
      return (
          shift->SUPER::config_options, qw(
              exec|e=s
              print|p:s
              print0|0:s
              wgd|c|w=s
  
              force|f
              ) );
  }
  
  sub option_exec {
      my $self = shift;
      my ($command) = @_;
      push @{ $self->{commands} },
          command_exec => [$command];
  }
  
  sub option_print {
      my $self = shift;
      my $format = shift;
      push @{ $self->{commands} },
          command_print => [
              format => $format,
          ];
  }
  
  sub option_print0 {
      my $self = shift;
      my $format = shift;
      push @{ $self->{commands} },
          command_print => [
              separator => "\0",
              format => $format,
          ];
  }
  
  sub option_wgd {
      my $self = shift;
      my ($command) = @_;
      push @{ $self->{commands} },
          command_wgd => [$command];
  }
  
  sub command_print {
      my $self = shift;
      my %options = @_;
      my $separator = $options{separator} || "\n";
      my $format = $options{format} || '%s';
      $format .= $separator;
      printf $format, $self->wgd->config_file;
      return 1;
  }
  
  sub command_exec {
      my $self = shift;
      my $command = shift;
      local %ENV = %ENV;
      $self->wgd->set_environment(localized => 1);
      system $command
          and WGDev::X::System->throw('Error running shell command.');
      return 1;
  }
  
  sub command_wgd {
      my $self = shift;
      my $command = shift;
      my $wgd = $self->wgd;
      require Text::ParseWords;
  
      my @command_line = (
          '-R' . $wgd->root,
          '-F' . $wgd->config_file,
          Text::ParseWords::shellwords($command),
      );
  
      return WGDev::Command->run(@command_line);
  }
  
  sub process {
      my $self = shift;
  
      my @commands = @{ $self->{commands} };
      my $force = $self->option('force');
      if (! @commands ) {
          @commands = (command_print => []);
      }
  
      my $root = $self->wgd->root;
      SITES: for my $config ( $self->wgd->list_site_configs ) {
          my $wgd = eval { WGDev->new( $root, $config ) };
          if ( $wgd ) {
              ##no critic (ProhibitCStyleForLoops ProhibitLocalVars)
              local $self->{wgd} = $wgd;
              COMMANDS: for (my $i = 0; $i <= $#commands; $i += 2) {
                  my $command = $commands[$i];
                  my @params = @{ $commands[$i + 1] };
                  my $success = eval {
                      $self->$command(@params) || 1;
                  };
                  if ( $success ) {
                      # nothing
                  }
                  elsif ( $force ) {
                      warn $@;
                  }
                  else {
                      WGDev::X->inflate($@);
                  }
              }
          }
          elsif ($force) {
              warn $@;
          }
          else {
              WGDev::X->inflate($@);
          }
      }
  
      return 1;
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Command::For::Each - Run command for each available config file
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      wgd for-each [ --print0 | --exec=command ] [ -f ]
  
  =head1 DESCRIPTION
  
  Runs a command for each available WebGUI config file.  By default,
  the names of the config files will be output.
  
  =head1 OPTIONS
  
  =over 8
  
  =item C<-f> C<--force>
  
  Continue processing config files if there is an error
  
  =item C<-0> C<--print0[=format]>
  
  Prints the config file name followed by an ASCII C<NUL> character
  instead of a carriage return.
  
  An optional L<perlfunc/sprintf> formatting string can be specified.
  
  =item C<-p> C<--print[=format]>
  
  Prints the config file name.  This is the default option if no other
  options are specified.
  
  An optional L<perlfunc/sprintf> formatting string can be specified.
  
  =item C<-e> C<--exec=>
  
  Runs the given command using the shell for each config file.  The
  WEBGUI_ROOT and WEBGUI_CONFIG environment variables will be set
  while this command is run.
  
  =item C<-w> C<-c> C<--wgd=>
  
  Runs the given WGDev command for each config file.
  
  =back
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_COMMAND_FOR_EACH

$fatpacked{"WGDev/Command/Group.pm"} = <<'WGDEV_COMMAND_GROUP';
  package WGDev::Command::Group;
  {
    $WGDev::Command::Group::VERSION = '0.1207160';
  }
  # ABSTRACT: Utilities for manipulating WebGUI Groups
  use strict;
  use warnings;
  use 5.008008;
  
  use parent qw(WGDev::Command::Base);
  
  use WGDev::X ();
  
  sub config_options {
      return qw(
          list|l
          format|f=s
          long
          hidden
      );
  }
  
  sub process {
      my $self = shift;
      my $wgd  = $self->wgd;
  
      my $session = $wgd->session();
  
      if ( $self->option('list') ) {
          my $format = $self->option('format');
          if ( $self->option('long') ) {
              $format
                  = 'Name: %groupName% %%n Id: %groupId% %%n Description: %description% %%n';
          }
          elsif ( !$format ) {
              $format = '%groupName%';
          }
          my $show_in_forms = $self->option('hidden');
          my $group_ids     = $session->db->buildArrayRef(
              'select groupId from groups order by groupName');
          for my $group_id ( @{$group_ids} ) {
              my $group = WebGUI::Group->new( $session, $group_id );
              if ( !$group ) {
                  warn "Unable to instantiate group via groupId: $group_id";
                  next;
              }
              next if !$show_in_forms && !$group->showInForms;
  
              my $output = $self->format_output( $format, $group );
              print $output . "\n";
          }
      }
  }
  
  sub format_output {
      my ( $self, $format, $group ) = @_;
      $format =~ s/%%n/\n/msxg;
      {
          no warnings 'uninitialized';
          $format =~ s{% (?: (\w+) (?: :(-?\d+) )? )? %}{
              my $replace;
              if ($1) {
                  $replace = $group->get($1);
                  if ($2) {
                      $replace = sprintf('%*2$s', $replace, $2);
                  }
              }
              else {
                  $replace = '%';
              }
              $replace;
          }msxeg;
      }
      return $format;
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Command::Group - Utilities for manipulating WebGUI Groups
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      wgd group [--list [--long] [--hidden]]
  
  =head1 DESCRIPTION
  
  Utilities for manipulating WebGUI Groups
  
  =head1 METHODS
  
  =head2 C<format_output ( $format, $group )>
  
  Returns the formatted information about a group.  C<$format> is
  the format to output as specified in the L<format option|/-f>.
  
  =head1 OPTIONS
  
  =over 8
  
  =item C<-l> C<--list>
  
  List groups. This is currently the only supported action.
  
  =item C<--long>
  
  Use long list format, which includes group name, ID, and description.
  
  =item C<-f> C<--format=>
  
  Use arbitrary formatting.  Format looks like C<%description:30%>, where 'C<description>' is
  the field to display, and 30 is the length to left pad/cut to.  Negative
  lengths can be specified for right padding.  Percent signs can be included by
  using C<%%>. Newlines can be included by using C<%%n>
  
  =item C<--hidden>
  
  Include groups that are normally hidden from WebGUI forms.
  
  =back
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_COMMAND_GROUP

$fatpacked{"WGDev/Command/Guid.pm"} = <<'WGDEV_COMMAND_GUID';
  package WGDev::Command::Guid;
  {
    $WGDev::Command::Guid::VERSION = '0.1207160';
  }
  # ABSTRACT: Generates GUIDs via WebGUI's $session->id->generate API
  use strict;
  use warnings;
  use 5.008008;
  
  use parent qw(WGDev::Command::Base);
  
  sub config_options {
      return qw(
          number|n=i
          dashes!
          toHex
      );
  }
  
  sub process {
      my $self = shift;
      my $wgd  = $self->wgd;
  
      my $session = $wgd->session();
      my $id      = $session->id;
  
      if ( $self->option('toHex') ) {
          foreach my $guid ( $self->arguments ) {
              printf "%s : %s\n", $guid, $id->toHex($guid);
          }
          return;
      }
  
      my $number = $self->option('number') || 1;
      $self->set_option_default( dashes => 1 );
  
      for ( 1 .. $number ) {
          my $guid = $id->generate();
          if ( !$self->option('dashes') && $guid =~ /[-_]/msx ) {
              redo;
          }
          print "$guid\n";
      }
      return 1;
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Command::Guid - Generates GUIDs via WebGUI's $session->id->generate API
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      wgd guid [-n <quantity>] [--no-dashes]
  
  =head1 DESCRIPTION
  
  Generates GUIDs via WebGUI's C<$session->id->generate> API. Optionally
  excludes GUIDs with dashes (for easy double-click copy/pasting).
  
  =head1 OPTIONS
  
  =over 8
  
  =item C<-n> C<--number>
  
  Number of GUIDs to generate. Defaults to 1.
  
  =item C<--[no-]dashes>
  
  Whether or not to filter GUIDs containing dashes (for easy double-click copy/pasting)
  
  =back
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_COMMAND_GUID

$fatpacked{"WGDev/Command/Help.pm"} = <<'WGDEV_COMMAND_HELP';
  package WGDev::Command::Help;
  {
    $WGDev::Command::Help::VERSION = '0.1207160';
  }
  # ABSTRACT: Displays C<perldoc> help for WGDev command
  use strict;
  use warnings;
  use 5.008008;
  
  use parent qw(WGDev::Command::Base);
  
  use WGDev::Command ();
  use WGDev::X       ();
  
  sub needs_root {
      return;
  }
  
  sub process {
      my $self = shift;
      my $wgd  = $self->wgd;
  
      my ($command) = $self->arguments;
      if ( !defined $command ) {
          print WGDev::Command->usage(1);
          return 1;
      }
  
      my $command_module;
      if ( $command eq 'wgd' ) {
          $command_module = 'WGDev::Command';
      }
      else {
          $command_module = WGDev::Command->get_command_module($command);
      }
  
      if ( !$command_module ) {
          WGDev::X::CommandLine::BadCommand->throw(
              usage        => $self->usage,
              command_name => $command,
          );
      }
  
      if ( $command_module->can('help') ) {
          return $command_module->help;
      }
  
      require WGDev::Help;
      WGDev::Help::package_perldoc( $command_module,
          '!AUTHOR|LICENSE|METHODS|SUBROUTINES' );
      return 1;
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Command::Help - Displays C<perldoc> help for WGDev command
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      wgd help <command>
  
  =head1 DESCRIPTION
  
  Displays C<perldoc> page for WGDev command.
  
  More or less equivalent to running
  
       wgd command --help
  
  Except that the help message is displayed via Pod::Perldoc
  
  =head1 OPTIONS
  
  =over 8
  
  =item C<< <command> >>
  
  The sub-command to display help information about.
  
  =back
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_COMMAND_HELP

$fatpacked{"WGDev/Command/Import.pm"} = <<'WGDEV_COMMAND_IMPORT';
  package WGDev::Command::Import;
  {
    $WGDev::Command::Import::VERSION = '0.1207160';
  }
  # ABSTRACT: Import assets from files
  use strict;
  use warnings;
  use 5.008008;
  
  use parent qw(WGDev::Command::Base);
  
  sub process {
      my $self = shift;
  
      my $wgd_asset = $self->wgd->asset;
      my $version_tag;
      for my $asset_file ( $self->arguments ) {
          open my $fh, '<:encoding(UTF-8)', $asset_file or next;
          my $asset_text = do { local $/; <$fh> };
          close $fh or next;
          $version_tag ||= do {
              require WebGUI::VersionTag;
              my $vt = WebGUI::VersionTag->getWorking( $self->wgd->session );
              $vt->set( { name => 'WGDev Asset Import' } );
              $vt;
          };
          my $asset_data = $wgd_asset->deserialize($asset_text);
          my $parent;
          if ( $asset_data->{parent} ) {
              $parent = eval { $wgd_asset->find( $asset_data->{parent} ) };
          }
          my $asset;
          my $mode;
  
          if ( eval { $asset = $wgd_asset->by_id( $asset_data->{assetId} ) } ) {
              $mode = 'Updating';
              $asset->addRevision( $asset_data, undef,
                  { skipAutoCommitWorkflows => 1, skipNotification => 1 } );
              if ( $asset_data->{parent} ) {
                  if ($parent) {
                      $asset->setParent($parent);
                  }
              }
          }
          else {
              $mode = 'Adding';
              $parent ||= $wgd_asset->import_node;
              $asset = $parent->addChild( $asset_data, $asset_data->{assetId},
                  undef,
                  { skipAutoCommitWorkflows => 1, skipNotification => 1 } );
          }
          printf "%8s: %-30s (%22s) %s\n", $mode,
              $asset->get('url'), $asset->getId, $asset->get('title');
      }
      if ($version_tag) {
          $version_tag->commit;
      }
      return 1;
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Command::Import - Import assets from files
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      wgd import <asset file> [<asset file> ...]
  
  =head1 DESCRIPTION
  
  Imports asset from files.
  
  =head1 OPTIONS
  
  =over 8
  
  =item C<< <asset file> >>
  
  File to import.
  
  =back
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_COMMAND_IMPORT

$fatpacked{"WGDev/Command/Intro.pm"} = <<'WGDEV_COMMAND_INTRO';
  package WGDev::Command::Intro;
  {
    $WGDev::Command::Intro::VERSION = '0.1207160';
  }
  # ABSTRACT: Introduction to WGDev
  use strict;
  use warnings;
  use 5.008008;
  
  use parent qw(WGDev::Command::Base);
  
  sub needs_root {
      return;
  }
  
  sub process {
      my $self = shift;
      return $self->help;
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Command::Intro - Introduction to WGDev
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      wgd edit default_article
      wgd package home
      wgd reset --build
      wgd reset --dev
      wgd db
  
  =head1 DESCRIPTION
  
  WGDev provides a variety of commands useful for WebGUI developers.
  
  =head1 GETTING STARTED
  
  The first step in using WGDev is getting it to find your WebGUI
  root directory and site config file.  For this, you can either use
  the C<WEBGUI_ROOT> and C<WEBGUI_CONFIG>/C<WEBGUI_SITENAME> environment
  variables, setting the C<command.webgui_root> and
  C<command.webgui_config>/C<command.webgui_sitename> options via the
  L<config command|WGDev::Command::Config>, using command line
  parameters (see C<wgd help>), or (for the root path) relying on
  auto-detection.
  
  Auto-detection works by searching upward from the current directory
  for a valid WebGUI directory.  The config file cannot be detected
  and must be specified.
  
  The WebGUI config file can be specified relative to the current
  directory, relative to WebGUI's etc directory, or as an absolute
  path.
  
  Once you have the root and config file set or otherwise specified,
  you can use any of the WGDev commands.
  
  =head1 GETTING HELP
  
  A summary of a command's options is available by running the command
  with the C<--help> option.  Full documentation is available using
  the C<wgd help> command.  A full list of available commands is
  available by running C<wgd commands>.
  
  =head1 SPECIFYING ASSETS
  
  When specifying assets as parameters to commands, either an asset
  URL or an asset ID can be specified.  Some commands will also accept
  a class name, treating it as an new asset of that type.
  
  =head1 COMMON COMMANDS
  
  =head2 C<< wgd edit <asset> >>
  
  Edits the specified asset in your prefered text editor.  When you
  exit the editor, the asset on the WebGUI site will be updated with
  the new data.  Multiple assets can be specified.
  
  =head2 C<< wgd package <asset> >>
  
  The package command will generate a package for asset specified.
  Additionally, the --import option allows you to import package
  files, and --upgrade will export a package and put it into the correct
  package directory for the next WebGUI release.  Multiple assets can
  be specified.
  
  =head2 C<wgd reset --dev>
  
  Resets a site to its defaults and sets it up for development.  The
  site started is disabled, leaving the admin login with the default
  password of C<123qwe>.  Additionally, all of the default example
  content is cleared from the site giving you a blank slate to work
  from.
  
  =head2 C<wgd reset --build>
  
  Resets a site it its defaults and prepares it to generate a site
  creation script.  The site starter is enabled, and old version tags
  and revisions of content are cleaned up.
  
  =head2 C<wgd db>
  
  Starts the C<mysql> client in the site's database, using the login
  information from the site config file.
  
  =head2 C<< wgd export <asset> >>
  
  Exports assets to files. You can export to standard out by using
  the C<--stdout> option.  Multiple assets can be specified.
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_COMMAND_INTRO

$fatpacked{"WGDev/Command/Ls.pm"} = <<'WGDEV_COMMAND_LS';
  package WGDev::Command::Ls;
  {
    $WGDev::Command::Ls::VERSION = '0.1207160';
  }
  # ABSTRACT: List WebGUI assets
  use strict;
  use warnings;
  use 5.008008;
  
  use parent qw(WGDev::Command::Base);
  
  sub config_options {
      return qw(
          directory|d
          format|f=s
          long|l
          recursive|r
          excludeClass=s@
          includeOnlyClass=s@
          limit=n
          isa=s
          filter=s
      );
  }
  
  sub option_filter {
      my $self   = shift;
      my $filter = shift;
  
      my ( $filter_prop, $filter_sense, $filter_match )
          = $filter =~ m{%(\w+)% \s* ([~!])~ \s* (.*)}msx;
      if (   !defined $filter_prop
          || !defined $filter_sense
          || !defined $filter_match )
      {
          WGDev::X->throw("Invalid filter specified: $filter");
      }
      if ( $filter_match =~ m{\A/(.*)/\Z}msx ) {
          eval { $filter_match = qr/$1/msx; }
              || WGDev::X->throw(
              "Specified filter is not a valid regular expression: $1");
      }
      else {
          $filter_match = qr/\A\Q$filter_match\E\z/msx;
      }
      $self->{filter_property} = $filter_prop;
      $self->{filter_sense}    = $filter_sense eq q{~};
      $self->{filter_match}    = $filter_match;
      return;
  }
  
  sub process {
      my $self = shift;
      my $wgd  = $self->wgd;
  
      my $format = $self->option('format');
      if ( $self->option('long') ) {
          $format = '%assetId% %url:-35% %title%';
      }
      elsif ( !$format ) {
          $format = '%url%';
      }
  
      my $relatives   = $self->option('recursive') ? 'descendants' : 'children';
      my @parents     = $self->arguments;
      my $show_header = @parents > 1;
      my $exclude_classes      = $self->option('excludeClass');
      my $include_only_classes = $self->option('includeOnlyClass');
      my $limit                = $self->option('limit');
      my $isa                  = $self->option('isa');
  
      my $error;
      PARENT:
      while ( my $parent = shift @parents ) {
          my $asset;
          if ( !eval { $asset = $wgd->asset->find($parent) } ) {
              warn "wgd ls: $parent: No such asset\n";
              $error++;
              next;
          }
          if ($self->option('directory')) {
             my $parent_asset = $wgd->asset->find($parent);
             print $self->format_output( $format, $parent_asset ), "\n";
          }
          else {
             if ($show_header) {
                 print "$parent:\n";
             }
             my $child_iter = $asset->getLineageIterator(
                 [$relatives],
                 {
                     $exclude_classes ? ( excludeClasses => $exclude_classes )
                     : (),
                     $include_only_classes
                     ? ( includeOnlyClasses => $include_only_classes )
                     : (),
                     defined $limit
                         && !defined $self->{filter_match} ? ( limit => $limit )
                     : (),
                     $isa ? ( isa => $isa ) : (),
                 } );
             while ( my $child = $child_iter->() ) {
                 next
                     if !$self->pass_filter($child);
     
                 # Handle limit ourselves because smartmatch filtering happens
                 # *after* getLineage returns its results
                 last PARENT
                     if defined $limit && $limit-- <= 0;
     
                 my $output = $self->format_output( $format, $child );
                 print $output . "\n";
             }
             if (@parents) {
                 print "\n";
             }
          }
      }
      return (! $error);
  }
  
  sub pass_filter {
      my ( $self, $asset ) = @_;
      my $filter_prop  = $self->{filter_property};
      my $filter_sense = $self->{filter_sense};
      my $filter_match = $self->{filter_match};
  
      return 1
          if !defined $filter_match;
  
      {
          no warnings 'uninitialized';
          if ($filter_sense) {
              return $asset->get($filter_prop) =~ $filter_match;
          }
          else {
              return $asset->get($filter_prop) !~ $filter_match;
          }
      }
  }
  
  sub format_output {
      my ( $self, $format, $asset ) = @_;
      {
          no warnings 'uninitialized';
          $format =~ s{% (?: (\w+) (?: :(-?\d+) )? )? %}{
              my $replace;
              if ($1) {
                  $replace = $asset->get($1);
                  if ($2) {
                      $replace = sprintf('%*2$s', $replace, $2);
                  }
              }
              else {
                  $replace = '%';
              }
              $replace;
          }msxeg;
      }
      return $format;
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Command::Ls - List WebGUI assets
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      wgd ls [-d] [-l] [--format=<format>] [-r] <asset> [<asset> ...]
  
  =head1 DESCRIPTION
  
  Lists children of WebGUI asset(s), or just the asset(s) themselves
  
  =head1 METHODS
  
  =head2 C<format_output ( $format, $asset )>
  
  Returns the formatted information about an asset.  C<$format> is
  the format to output as specified in the L<format option|/-f>.
  
  =head2 C<option_filter ( $filter )>
  
  Takes a filter specification, verifies that it is specified properly, and saves it.
  Ignored when '-d' is set.
  
  =head2 C<pass_filter ( $asset )>
  
  Checks if a given asset passes the saved filter.  Returns true or false.
  Ignored when '-d' is set.
  
  =head1 OPTIONS
  
  =over 8
  
  =item C<-d> C<--directory>
  
  Print only the specified asset (similar to '-d' in ls(1)).
  
  =item C<-l> C<--long>
  
  Use long list format, which includes asset ID, URL, and title.
  
  =item C<-f> C<--format=>
  
  Use arbitrary formatting.  Format looks like C<%url:30%>, where 'C<url>' is
  the field to display, and 30 is the length to left pad/cut to.  Negative
  lengths can be specified for right padding.  Percent signs can be included by
  using C<%%>.
  
  =item C<-r> C<--recursive>
  
  Recursively list all descendants (by default we only list children).
  Ignored when '-d' is set.
  
  =item C<--includeOnlyClass=>
  
  Specify one or more times to limit the results to a certain set of asset classes.
  Ignored when '-d' is set.
  
  =item C<--excludeClass=>
  
  Specify one or more times to filter out certain asset class(es) from the results.
  Ignored when '-d' is set.
  
  =item C<--limit=>
  
  The maximum amount of entries to return.
  Ignored when '-d' is set.
  
  =item C<--isa=>
  
  A class name where you can look for classes of a similar base class.
  For example, if you're looking for Donations, Subscriptions, Products
  and other subclasses of L<WebGUI::Asset::Sku>, then specify the
  parameter C<--isa=WebGUI::Asset::Sku>.
  Ignored when '-d' is set.
  
  =item C<--filter=>
  
  Apply smart match filtering against the results. Format looks like
  C<%url% ~~ smartmatch>, where C<url> is the field to filter against,
  and C<smartmatch> is either a Perl regular expression such as
  C</(?i:partial_match)/> or a string such as C<my_exact_match>.
  Ignored when '-d' is set.
  
  =back
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_COMMAND_LS

$fatpacked{"WGDev/Command/Mail.pm"} = <<'WGDEV_COMMAND_MAIL';
  package WGDev::Command::Mail;
  {
    $WGDev::Command::Mail::VERSION = '0.1207160';
  }
  # ABSTRACT: Sends emails via the WebGUI::Mail::Send API
  use strict;
  use warnings;
  use 5.008008;
  use Carp;
  
  use parent qw(WGDev::Command::Base);
  
  use WGDev::X ();
  
  sub config_options {
      return qw(
          list|l
          delete
          processQueue
          queue|q
          toUser=s
          toGroup=s
          subject|s=s
          from=s
          cc=s
          bcc=s
          replyTo=s
          returnPath=s
          contentType=s
          messageId=s
          inReplyTo=s
          isInbox
          verbose|v
      );
  }
  
  sub process {
      my $self = shift;
      my $wgd  = $self->wgd;
  
      my $verbose = $self->option('verbose');
  
      # Handle special cases
      if ( !$self->arguments ) {
          my $dbh   = $wgd->db->connect;
          my $count = $dbh->selectrow_array('SELECT COUNT(*) FROM mailQueue');
          print "Mail queue has @{[ $count || 'no' ]} message(s).\n";
  
          if ( $self->option('list') ) {
              for my $message (
                  @{ $dbh->selectcol_arrayref('SELECT message FROM mailQueue') }
                  )
              {
                  print $message . "\n";
              }
          }
          elsif ( $self->option('delete') ) {
              $dbh->do('DELETE FROM mailQueue');
              print "Deleted all messages from mail queue.\n";
          }
          elsif ( $self->option('processQueue') ) {
              my $WORKFLOW_ID = 'pbworkflow000000000007';
              my $found
                  = $dbh->selectrow_array(
                  'SELECT count(*) FROM Workflow WHERE workflowId = ?',
                  {}, $WORKFLOW_ID, );
              if ( !$found ) {
                  WGDev::X->throw(
                      q{The default "Send Queued Email Messages" Workflow was not found,}
                          . q{ unable to run.} );
              }
              require WebGUI::Workflow::Instance;
              my $session = $wgd->session;
              WebGUI::Workflow::Instance->create( $session,
                  { workflowId => $WORKFLOW_ID, } )->start;
              print
                  qq{Triggered Workflow, the mail queue should be being processed as we speak.\n};
          }
          return 1;
      }
  
      my $session = $wgd->session;
      my $to = join q{,}, $self->arguments;
      my $body;
      while ( my $line = <STDIN> ) {
          last if $line eq ".\n";
          $body .= $line;
      }
  
  # We are going to pass pretty much all options into WebGUI::Mail::Send::create
      my $options = $self->{options};
      $options->{to} = $to;
  
      # Pull out the non-api options (or short-hands)
      if ( my $s = delete $options->{s} ) {
          $options->{subject} = $s;
      }
      my $queue = delete $options->{q} || delete $options->{queue};
  
      if ($verbose) {
          print $queue ? 'Queueing' : 'Sending', " message:\n";
          print $body;
          print "Using the following options:\n";
          print Data::Dumper::Dumper( $self->{options} );
          print 'SMTP Server: ' . $session->setting->get('smtpServer') . "\n";
          print "emailToLog: 1\n" if $session->config->get('emailToLog');
      }
      require WebGUI::Mail::Send;
      my $msg = WebGUI::Mail::Send->create( $session, $options );
      WGDev::X->throw('Unable to instantiate message') unless $msg;
  
      $msg->addText($body);
  
      my $status;
      if ($queue) {
          $status = $msg->queue;
      }
      else {
          $status = $msg->send;
      }
      print "Status: $status\n" if $verbose;
      return 1;
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Command::Mail - Sends emails via the WebGUI::Mail::Send API
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      wgd mail
      wgd mail -s test pat@patspam.com
  
  =head1 DESCRIPTION
  
  Sends emails via the L<WebGUI::Mail::Send> API
  
  If run with no arguments, displays the number of messages currently
  in the mail queue.
  
  Accepts all options supported by
  L<WebGUI::Mail::Send::create|WebGUI::Mail::Send/create>, plus the
  following additional items:
  
  =head1 OPTIONS
  
  =over 8
  
  =item C<-l> C<--list>
  
  List (print) the raw contents of the mail queue.
  
  =item C<--delete>
  
  Delete the contents of the mail queue.
  
  =item C<--processQueue>
  
  Trigger the default "Send Queued Email Messages" Workflow.  This
  will send all of the messages in the mail queue.
  
  =item C<-q> C<--queue>
  
  Add the message to the queue rather than sending it immediately.
  
  =item C<-s>
  
  Short-hand for C<--subject>.
  
  =back
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_COMMAND_MAIL

$fatpacked{"WGDev/Command/Optimize.pm"} = <<'WGDEV_COMMAND_OPTIMIZE';
  package WGDev::Command::Optimize;
  {
    $WGDev::Command::Optimize::VERSION = '0.1207160';
  }
  # ABSTRACT: Scans your site and suggests various optimizations
  use strict;
  use warnings;
  use 5.008008;
  
  use parent qw(WGDev::Command::Base::Verbosity);
  
  sub config_options {
      return qw(
          assets
          macros
          db
      );
  }
  
  sub process {
      my $self = shift;
      my $wgd  = $self->wgd;
  
      if ( $self->option('assets') ) {
          $self->optimise_assets();
      }
  
      if ( $self->option('macros') ) {
          $self->optimise_macros();
      }
  
      if ( $self->option('db') ) {
          $self->optimise_db();
      }
      return 1;
  }
  
  sub optimise_assets {
      my $self    = shift;
      my $wgd     = $self->wgd;
      my $session = $wgd->session();
  
      my @assets;
      for my $asset ( sort keys %{ $session->config->get('assets') } ) {
          if (
              !$session->db->quickScalar(
                  'select count(*) from asset where className = ?', [$asset] ) )
          {
              push @assets, $asset;
          }
      }
  
      if (@assets) {
          $self->report(
              "The following Assets do not appear in your Asset table:\n");
          for my $asset (@assets) {
              $self->report("\t$asset\n");
          }
          my $config  = $wgd->config_file();
          my $message = <<"END_MESSAGE";
  If you are sure any of these Assets are not being used on your site,
  you can reduce memory usage by removing them from the "assets" section of
  your site config file, which is located at:
  \t$config
  
  Keep in mind:
  *) Some assets such as FilePile will not appear in your Assets table but
     are still used to provide funcitonality (in the case of FilePile 
     providing a way for users to upload multiple Files).
  END_MESSAGE
          $self->report("$message");
      }
  
      return 1;
  }
  
  sub optimise_macros {
      my $self    = shift;
      my $wgd     = $self->wgd;
      my $session = $wgd->session();
  
      my @macros;
      for my $macro ( sort keys %{ $session->config->get('macros') } ) {
          if (
              !$session->db->quickScalar(
                  'select count(*) from template where template like ? or template like ?',
                  [ "%^$macro;%", "%^$macro(%" ] ) )
          {
              push @macros, $macro;
          }
      }
  
      if (@macros) {
          my $macros  = join q{}, map {"\t$_\n"} @macros;
          my $config  = $wgd->config_file();
          my $message = <<"END_MESSAGE";
  The following Macros do not appear in the template field of the template table:
  $macros
  
  If you are sure any of these Macros are not being used on your site,
  you can reduce memory usage by removing them from the "macros" section of
  your site config file, which is located at:
  \t$config
  
  Keep in mind:
  *) Macros can be references from lots of places other then just Templates, 
     for example the mailFooter setting in the Settings table 
  END_MESSAGE
          $self->report($message);
      }
  
      return 1;
  }
  
  use constant OPTIMIZE_TABLES_LIMIT => 10;
  
  sub optimise_db {
      my $self    = shift;
      my $wgd     = $self->wgd;
      my $session = $wgd->session();
  
      my $sth = $session->db->read('show table status');
  
      my @tables;
      while ( my $r = $sth->hashRef ) {
          push @tables, [ $r->{Name}, $r->{Data_length}, $r->{Rows} ];
      }
  
      $self->report("Top 10 Tables, sorted by Data_length\n");
      my $ctr;
      for my $table ( sort { $b->[1] <=> $a->[1] } @tables ) {
          ## no critic (ProhibitParensWithBuiltins)
          $self->report( sprintf( "%10d\t%s\n", $table->[1], $table->[0] ) );
          last
              if ++$ctr == OPTIMIZE_TABLES_LIMIT;
      }
      $self->report("\n");
  
      $self->report("Top 10 Tables, sorted by Rows\n");
      $ctr = 0;
      for my $table ( sort { $b->[2] <=> $a->[2] } @tables ) {
          ## no critic (ProhibitParensWithBuiltins)
          $self->report( sprintf( "%10d\t%s\n", $table->[2], $table->[0] ) );
          last
              if ++$ctr == OPTIMIZE_TABLES_LIMIT;
      }
      $self->report("\n");
  
      $self->report(<<'END_ADVICE');
  To reduce row count, you may want to investigate deleting old/unused data.
  To reduce row size, apart from deleting rows, you might want to investigate mysql's "optimize table" command.
  END_ADVICE
  
      return 1;
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Command::Optimize - Scans your site and suggests various optimizations
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      wgd optimize [--assets] [--macros]
  
  =head1 DESCRIPTION
  
  Scans your site and suggests various optimizations
  
  =head1 METHODS
  
  =head2 C<optimise_assets>
  
  Suggests Assets that you might be able to disable to reduce memory consumption
  
  =head2 C<optimise_macros>
  
  Suggests Macros that you might be able to disable to reduce memory consumption
  
  =head2 C<optimise_db>
  
  Suggests database tables that may be able to be adjusted to increase speed.
  
  =head1 OPTIONS
  
  =over 8
  
  =item C<--assets>
  
  Suggests Assets that you might be able to disable to reduce memory consumption
  
  =item C<--macros>
  
  Suggests Macros that you might be able to disable to reduce memory consumption
  
  =item C<--db>
  
  Suggests database tables that may be able to be adjusted to increase speed.
  
  =back
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_COMMAND_OPTIMIZE

$fatpacked{"WGDev/Command/Package.pm"} = <<'WGDEV_COMMAND_PACKAGE';
  package WGDev::Command::Package;
  {
    $WGDev::Command::Package::VERSION = '0.1207160';
  }
  # ABSTRACT: Export assets for upgrade
  use strict;
  use warnings;
  use 5.008008;
  
  use parent qw(WGDev::Command::Base);
  
  use File::Spec ();
  use WGDev::X   ();
  
  sub config_options {
      return qw(
          import|i=s@
          parent=s
          overwrite
  
          upgrade|u
          to=s
      );
  }
  
  sub process {
      my $self = shift;
      my $wgd  = $self->wgd;
      require File::Copy;
      if ( $self->arguments ) {
          my $package_dir = $self->option('to') || q{.};
          if ( $self->option('upgrade') ) {
              my $version = $wgd->version->module;
              my $wg8 = $version =~ /^8[.]/msx;
              if ($wg8) {
                  require WebGUI::Paths;
                  my $old_version = $wgd->version->db_script;
                  $package_dir = File::Spec->catdir( WebGUI::Paths->upgrades,
                      $old_version . q{-} . $version );
              }
              else {
                  $package_dir = File::Spec->catdir( $wgd->root, 'docs', 'upgrades',
                      'packages-' . $wgd->version->module );
              }
              if ( !-d $package_dir ) {
                  mkdir $package_dir;
              }
          }
          if ( !-d $package_dir ) {
              WGDev::X::IO->throw(
                  error => 'Directory does not exist',
                  path  => $package_dir
              );
          }
          for my $asset_spec ( $self->arguments ) {
              my $asset = eval { $wgd->asset->find($asset_spec) } || do {
                  warn "Unable to find asset $asset_spec!\n";
                  next;
              };
  
              my $storage  = $asset->exportPackage;
              my $filename = $storage->getFiles->[0];
              my $filepath = $storage->getPath($filename);
              File::Copy::copy( $filepath,
                  File::Spec->catfile( $package_dir, $filename ) );
              printf "Building package %27s for %27s.\n", $filename,
                  $asset->get('title');
          }
      }
      if ( $self->option('import') ) {
          my $parent
              = $self->option('parent')
              ? eval { $wgd->asset->find( $self->option('parent') ) }
              : $wgd->asset->import_node;
          if ( !$parent ) {
              warn "Unable to find parent node!\n";
              return 0;
          }
          require WebGUI::Storage;
          require WebGUI::VersionTag;
  
          my $version_tag = WebGUI::VersionTag->getWorking( $wgd->session );
          $version_tag->set( { name => 'WGDev package import' } );
          my $import_options = {};
          if ($self->option('overwrite')) {
              $import_options->{'overwriteLatest'} = 1;
          }
          for my $package ( @{ $self->option('import') } ) {
              my $storage = WebGUI::Storage->createTemp( $wgd->session );
              $storage->addFileFromFilesystem($package);
              my $asset = $parent->importPackage($storage, $import_options);
              if ( ! ref $asset ) {
                  # importPackage returns a string for errors (ugh)
                  WGDev::X::BadPackage->throw(
                      package => $package,
                      message => $asset,
                  );
              }
              elsif ( ! eval { $asset->isa('WebGUI::Asset') } ) {
                  # not an asset or an error?  this shouldn't ever happen.
                  WGDev::X::BadPackage->throw(
                      package => $package,
                      message => 'Strange result from package import: '
                          . ref($asset),
                  );
              }
              print "Imported '$package' to " . $asset->get('url') . "\n";
          }
          $version_tag->commit;
      }
      return 1;
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Command::Package - Export assets for upgrade
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      wgd package [--to=<dir>] [--upgrade] [<asset> ...]
      wgd package [--parent=<asset>] [--import=<package file>]
  
  =head1 DESCRIPTION
  
  Exports or imports assets as packages, optionally placing them in the current
  upgrade path.
  
  =head1 OPTIONS
  
  Assets specified as standalone arguments are exported as packages.
  
  =over 8
  
  =item C<-i> C<--import=>
  
  Package file (or files) to import.  Will be imported to the import node if no
  other parent is specified.
  
  =item C<--overwrite>
  
  Forces the assets in this package to be the latest version on the
  site.  This option only works in conjunction with C<--import> and
  requires WebGUI 7.8.1 or higher.
  
  =item C<--parent=>
  
  Specify the parent asset to import packages into.
  
  =item C<-u> C<--upgrade>
  
  If specified, packages will be exported to the directory for the upgrade to
  the current local version.
  
  =item C<--to=>
  
  Specify a directory to output the package files to.  If neither C<--upgrade>
  or C<--to> is specified, packages will be output to the current directory.
  
  =item C<< <asset> >>
  
  Either an asset ID or an asset URL to specify an asset.
  
  =back
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_COMMAND_PACKAGE

$fatpacked{"WGDev/Command/Reset.pm"} = <<'WGDEV_COMMAND_RESET';
  package WGDev::Command::Reset;
  {
    $WGDev::Command::Reset::VERSION = '0.1207160';
  }
  # ABSTRACT: Reset a site to defaults
  use strict;
  use warnings;
  use 5.008008;
  
  use parent qw(WGDev::Command::Base::Verbosity);
  
  use WGDev::X   ();
  use File::Spec ();
  use constant STAT_MODE => 2;
  use constant STAT_UID  => 4;
  use constant STAT_GID  => 5;
  
  sub config_options {
      return (
          shift->SUPER::config_options, qw(
              fast|f
  
              backup!
              delcache!
              import:s
              no-import|noimport
  
              test|t
              dev|d
              build|b
  
              uploads!
              upgrade!
  
              debug!
              starter!
              clear!
              config!
              purge!
              cleantags!
              emptytrash!
              index!
              runwf!
              autologon!
              util=s@
  
              delusers
  
              profile|pro|p=s@
              ) );
  }
  
  sub parse_params {
      my ( $self, @args ) = @_;
      $self->option( 'delcache' => 1 );
      return $self->SUPER::parse_params(@args);
  }
  
  sub option_no_import {
      my $self = shift;
      $self->option( 'import', undef );
      return;
  }
  
  sub option_profile {
      my $self           = shift;
      my $profile        = shift;
      my $profile_string = $self->wgd->my_config( [ 'profiles', $profile ] );
      if ( !defined $profile_string ) {
          warn "Profile '$profile' does not exist!\n";
          return;
      }
      $self->parse_params_string($profile_string);
      return;
  }
  
  sub option_fast {
      my $self = shift;
      $self->option( uploads   => 0 );
      $self->option( backup    => 0 );
      $self->option( delcache  => 0 );
      $self->option( purge     => 0 );
      $self->option( cleantags => 0 );
      $self->option( index     => 0 );
      $self->option( runwf     => 0 );
      return;
  }
  
  sub option_dev {
      my $self = shift;
      $self->option( backup  => 1 );
      $self->option( import  => q{} );
      $self->option( uploads => 1 );
      $self->option( upgrade => 1 );
      $self->option( starter => 0 );
      $self->option( debug   => 1 );
      $self->option( clear   => 1 );
      return;
  }
  
  sub option_build {
      my $self = shift;
      $self->verbosity( $self->verbosity + 1 );
      $self->option( backup     => 1 );
      $self->option( uploads    => 1 );
      $self->option( import     => q{} );
      $self->option( starter    => 1 );
      $self->option( debug      => 0 );
      $self->option( upgrade    => 1 );
      $self->option( purge      => 1 );
      $self->option( emptytrash => 1 );
      $self->option( cleantags  => 1 );
      $self->option( index      => 1 );
      $self->option( runwf      => 1 );
      return;
  }
  
  sub process {
      my $self = shift;
      my $wgd  = $self->wgd;
  
      if ( $self->option('backup') ) {
          $self->backup;
      }
  
      # Clear cache
      if ( $self->option('delcache') ) {
          $self->clear_cache;
      }
  
      # Delete non-system users
      if ( $self->option('delusers') ) {
          $self->delete_users;
      }
  
      # Clear and recreate uploads
      if ( $self->option('uploads') ) {
          $self->reset_uploads;
      }
  
      if ( defined $self->option('import') ) {
          $self->import_db_script;
      }
  
      # Run the upgrade in a fork
      if ( $self->option('upgrade') ) {
          $self->upgrade;
      }
  
      if ( $self->option('config') ) {
          $self->reset_config;
      }
  
      if ( defined $self->option('debug') || defined $self->option('starter') )
      {
          $self->set_settings;
      }
  
      if ( $self->option('clear') ) {
          $self->clear_default_content;
      }
  
      if ( $self->option('purge') ) {
          $self->purge_old_revisions;
      }
  
      if ( $self->option('emptytrash') ) {
          $self->empty_trash;
      }
  
      if ( $self->option('cleantags') ) {
          $self->clean_version_tags;
      }
  
      if ( $self->option('runwf') ) {
          $self->run_all_workflows;
      }
  
      if ( $self->option('index') ) {
          $self->rebuild_lineage;
          $self->rebuild_index;
      }
  
      if ( $self->option('autologon') ) {
          $self->autologon;
      }
  
      if ( $self->option('util') ) {
          require WGDev::Command::Util;
          for my $util ( @{ $self->option('util') } ) {
              $self->report("Running utility script '$util'... ");
              $self->report( 2, "\n" );
              my $util_command = WGDev::Command::Util->new($wgd);
              $util_command->parse_params_string($util);
              $util_command->verbosity( $self->verbosity - 1 );
              if ( !$util_command->process ) {
                  WGDev::X->throw('Error running util script!');
              }
              $self->report("Done.\n");
          }
      }
  
      return 1;
  }
  
  sub backup {
      my $self = shift;
      $self->report('Backing up current database... ');
      $self->wgd->db->dump(
          File::Spec->catfile( File::Spec->tmpdir, 'WebGUI-reset-backup.sql' )
      );
      $self->report("Done.\n");
      return 1;
  }
  
  sub clear_cache {
      my $self       = shift;
      my $wgd        = $self->wgd;
      my $cache_type = $wgd->config->get('cacheType');
      $self->report('Clearing cache... ');
      if ( $cache_type && $cache_type eq 'WebGUI::Cache::FileCache' ) {
          my $cache_dir = $wgd->config->get('fileCacheRoot')
              || '/tmp/WebGUICache';
          require File::Path;
          File::Path::rmtree( $cache_dir, { keep_root => 1 } );
      }
      elsif ( $cache_type && $cache_type eq 'WebGUI::Cache::Database' ) {
          # Don't clear the DB cache if we are importing, as that
          # will wipe it anyway
          if ( !defined $self->option('import') ) {
              my $dsn = $wgd->db->connect;
              $dsn->do('DELETE FROM cache');
          }
      }
      # Assume WebGUI 8 / CHI if no cache type, but with cache config
      elsif ( ( ! $cache_type || $cache_type eq 'WebGUI::Cache::CHI' )
          && ( my $cache_config = $wgd->config->get('cache') ) ) {
          # If there is a root dir, just wipe it so we don't have to load CHI
          if ( my $cache_dir = $cache_config->{root_dir} ) {
              require File::Path;
              File::Path::rmtree( $cache_dir, { keep_root => 1 } );
          }
          else {
              require CHI;
              my $cache = CHI->new(%{$cache_config});
              $cache->clear;
          }
      }
      # Don't do anything for unknown cache types
      $self->report("Done.\n");
      return 1;
  }
  
  sub delete_users {
      my $self = shift;
      my $wgd  = $self->wgd;
  
      my $session = $wgd->session;
      my @user_ids = grep { $_ ne '1' && $_ ne '3' }
          map { @{$_} }
          @{ $wgd->db->fetchall_arrayref('SELECT userId FROM users') };
      my $n_users = @user_ids;
      $self->report("Deleting $n_users non-system users... ");
      $self->report( 2, "\n" );
      require WebGUI::User;
  
      for my $user_id (@user_ids) {
          my $user = WebGUI::User->new( $session, $user_id );
          $self->report( 2, "\tDeleting user '" . $user->username . "'.\n" );
          $user->delete;
      }
      $self->report("Done.\n");
      return 1;
  }
  
  # Clear and recreate uploads
  sub reset_uploads {
      my $self = shift;
      my $wgd  = $self->wgd;
      require File::Copy;
      require File::Find;
      require File::Path;
      $self->report('Recreating uploads... ');
  
      my $wg_uploads = File::Spec->catdir( $wgd->root, 'www', 'uploads' );
      my $site_uploads = $wgd->config->get('uploadsPath');
  
      my ( $uploads_mode, $uploads_uid, $uploads_gid )
          = ( stat $site_uploads )[ STAT_MODE, STAT_UID, STAT_GID ];
  
      # if uploads doesn't exist, use reasonable defaults
      if ( ! defined $uploads_mode ) {
          $uploads_mode = oct 755;
          $uploads_uid = $>;
          $uploads_gid = $);
      }
  
      # make umask as permissive as required to match existing uploads folder
      # including sticky bits
      my $permissive_umask = oct 7777 & ~$uploads_mode;
      my $initial_umask = umask $permissive_umask;
  
      # set effective UID and GID, fail silently
      local ( $>, $) ) = ( $uploads_uid, $uploads_gid );
  
      File::Path::rmtree( $site_uploads, { keep_root => 1 } );
  
      File::Find::find( {
              no_chdir => 1,
              wanted   => sub {
                  no warnings 'once';
                  my $wg_path = $File::Find::name;
                  my $site_path
                      = File::Spec->rel2abs(
                      File::Spec->abs2rel( $wg_path, $wg_uploads ),
                      $site_uploads );
                  if ( -d $wg_path ) {
                      my $dir = ( File::Spec->splitdir($wg_path) )[-1];
                      if ( $dir =~ /^[.]/msx ) {
                          $File::Find::prune = 1;
                          return;
                      }
                      File::Path::mkpath( $site_path, 0, $uploads_mode );
                  }
                  else {
                      File::Copy::copy( $wg_path, $site_path );
                  }
              },
          },
          $wg_uploads
      );
  
      umask $initial_umask;
  
      $self->report("Done.\n");
      return 1;
  }
  
  sub import_db_script {
      my $self = shift;
      my $wgd  = $self->wgd;
      $self->report('Clearing old database information... ');
      $wgd->db->clear;
      $self->report("Done.\n");
      my $wg8 = $self->wgd->version->module =~ /^8[.]/msx;
  
      $self->report('Importing clean database dump... ');
  
      my $db_file = $self->option('import');
      if ( defined $db_file && $db_file eq q{} ) {
          if ($wg8) {
              require WebGUI::Paths;
              $db_file = WebGUI::Paths->defaultCreateSQL;
          }
          else {
              # Use previousVersion.sql for upgrades if it is available
              if ($self->option('upgrade')) {
                  $db_file = File::Spec->catfile( $wgd->root, 'docs', 'previousVersion.sql' );
              }
              if (! $db_file || ! -f $db_file ) {
                  $db_file = File::Spec->catfile( $wgd->root, 'docs', 'create.sql' );
              }
          }
      }
      $wgd->db->load($db_file);
      $self->report("Done.\n");
      return 1;
  }
  
  # Run the upgrade in a fork
  sub upgrade {
      my $self = shift;
      my $wgd  = $self->wgd;
      require File::Spec;
      my $wg8 = $self->wgd->version->module =~ /^8[.]/msx;
      $self->report('Running upgrade script... ');
  
      my $pid = fork;
      if ( !$pid ) {
  
          # replace sub in WebGUI::Config to only return a single config file
          my $config_filename
              = ( File::Spec->splitpath( $wgd->config_file ) )[2];
          my $config_hash = { $config_filename => $wgd->config };
          require WebGUI::Config;
          no warnings qw(once redefine);
          local *WebGUI::Config::readAllConfigs = sub { return $config_hash };
  
          my @args = qw(--doit --override --skipBackup --skipDelete);
          if ( $self->verbosity < 2 ) {
              push @args, '--quiet';
          }
          if ($wg8 && -f File::Spec->catfile($wgd->root, 'sbin', 'webgui.pl')) {
              $self->_run_script( 'webgui.pl', 'upgrade', @args );
          }
          else {
              $self->_run_script( 'upgrade.pl', @args );
          }
      }
      waitpid $pid, 0;
  
      # error status of subprocess
      if ($?) {
          WGDev::X->throw('Upgrade failed!');
      }
      $self->report("Done.\n");
      return 1;
  }
  
  sub reset_config {
      my $self = shift;
      my $wgd  = $self->wgd;
      require File::Copy;
  
      $self->report('Resetting config file... ');
      my $reset_config = $wgd->my_config('config');
  
      # new config file will include any explicit overrides
      my %set_config;
      if ( exists $reset_config->{override} ) {
          %set_config = %{ $reset_config->{override} };
      }
  
      # will also include specified values copied from old config
      my @copy_keys = qw(
          dsn dbuser dbpass uploadsPath uploadsURL
          exportPath extrasPath extrasURL cacheType
          sitename spectreIp spectrePort spectreSubnets
          fileCacheRoot
          );    # Update POD docs if these change
      if ( exists $reset_config->{copy} ) {
          unshift @copy_keys, @{ $reset_config->{copy} };
      }
      for my $key (@copy_keys) {
          $set_config{$key} = $wgd->config->get($key);
      }
  
      $wgd->close_config;
      open my $fh, '>', $wgd->config_file
          or WGDev::X::IO::Write->throw( path => $wgd->config_file );
      File::Copy::copy(
          File::Spec->catfile( $wgd->root, 'etc', 'WebGUI.conf.original' ),
          $fh );
      close $fh
          or WGDev::X::IO::Write->throw( path => $wgd->config_file );
  
      my $config = $wgd->config;
      while ( my ( $key, $value ) = each %set_config ) {
          $config->set( $key, $value );
      }
  
      $self->report("Done.\n");
      return 1;
  }
  
  sub set_settings {
      my $self = shift;
      my $wgd  = $self->wgd;
      $self->report('Setting WebGUI settings... ');
      my $dbh = $wgd->db->connect;
      my $sth = $dbh->prepare(
          q{REPLACE INTO `settings` (`name`, `value`) VALUES (?,?)});
      if ( $self->option('debug') ) {
          $sth->execute( 'showDebug', 1 );
  
          # for debug we set this to 1 year
          $sth->execute( 'sessionTimeout', 365 * 24 * 60 * 60 );
      }
      elsif ( defined $self->option('debug') ) {
          $sth->execute( 'showDebug', 0 );
  
          # default time is 2 hours
          $sth->execute( 'sessionTimeout', 2 * 60 * 60 );
      }
      if ( $self->option('starter') ) {
          $sth->execute( 'specialState', 'init' );
      }
      elsif ( defined $self->option('starter') ) {
          $dbh->do(q{DELETE FROM `settings` WHERE `name`='specialState'});
      }
      $self->report("Done.\n");
      return 1;
  }
  
  sub clear_default_content {
      my $self = shift;
      my $wgd  = $self->wgd;
      $self->report('Clearing example assets... ');
      $self->report( 2, "\n" );
      my $home     = $wgd->asset->home;
      my $children = $home->getLineage(
          ['descendants'],
          {
              statesToInclude => [
                  'published',       'clipboard',
                  'clipboard-limbo', 'trash',
                  'trash-limbo'
              ],
              statusToInclude => [ 'approved', 'pending', 'archive' ],
              excludeClasses => ['WebGUI::Asset::Wobject::Layout'],
              returnObjects  => 1,
              invertTree     => 1,
          } );
      for my $child ( @{$children} ) {
          $self->report( 2, sprintf "\tRemoving %-35s '%s'\n",
              $child->getName, $child->get('title') );
          $child->purge;
      }
      $self->report("Done.\n");
      return 1;
  }
  
  sub purge_old_revisions {
      my $self = shift;
      my $wgd  = $self->wgd;
      require WebGUI::Asset;
      $self->report('Purging old Asset revisions... ');
      $self->report( 2, "\n" );
      my $sth = $wgd->db->connect->prepare(<<'END_SQL');
      SELECT `assetData`.`assetId`, `asset`.`className`, `assetData`.`revisionDate`
      FROM `asset`
          LEFT JOIN `assetData` ON `asset`.`assetId` = `assetData`.`assetId`
      ORDER BY `assetData`.`revisionDate` ASC
  END_SQL
      $sth->execute;
      while ( my ( $id, $class, $revision ) = $sth->fetchrow_array ) {
          my $current_revision
              = WebGUI::Asset->getCurrentRevisionDate( $wgd->session, $id );
          if ( !defined $current_revision || $current_revision == $revision ) {
              next;
          }
          my $asset = eval { $wgd->asset->by_id($id, $revision) };
          next
              unless $asset;
  
          if ( $asset->getRevisionCount('approved') > 1 ) {
              $self->report( 2, sprintf "\tPurging %-35s %s '%s'\n",
                  $asset->getName, $revision, $asset->get('title') );
              $asset->purgeRevision;
          }
      }
      $self->report("Done.\n");
      return 1;
  }
  
  sub empty_trash {
      my $self = shift;
      my $wgd  = $self->wgd;
      $self->report('Emptying trash... ');
      $self->report( 2, "\n" );
      my $assets = $wgd->asset->root->getLineage(
          ['descendants'],
          {
              statesToInclude => [qw(trash)],
              statusToInclude => [qw(approved archived pending)],
          } );
      for my $asset_id ( @{$assets} ) {
          my $asset = $wgd->asset->by_id($asset_id);
          $self->report( 2, sprintf "\tPurging %-35s '%s'\n",
              $asset->getName, $asset->get('title') );
          $asset->purge;
      }
      $self->report("Done.\n");
      return 1;
  }
  
  sub clean_version_tags {
      my $self = shift;
      my $wgd  = $self->wgd;
  
      $self->report('Finding current version number... ');
      my $version = $wgd->version->database( $wgd->db->connect );
      $self->report("$version. Done.\n");
  
      $self->report('Cleaning out versions Tags... ');
      my $tag_id = 'pbversion0000000000001';
      my $dbh    = $wgd->db->connect;
      my $sth    = $dbh->prepare(q{UPDATE `assetData` SET `tagId` = ?});
      $sth->execute($tag_id);
      $sth = $dbh->prepare(q{DELETE FROM `assetVersionTag`});
      $sth->execute;
      $sth = $dbh->prepare(<<'END_SQL');
          INSERT INTO `assetVersionTag`
              ( `tagId`, `name`, `isCommitted`, `creationDate`, `createdBy`, `commitDate`,
                  `committedBy`, `isLocked`, `lockedBy`, `groupToUse`, `workflowId` )
          VALUES (?,?,?,?,?,?,?,?,?,?,?)
  END_SQL
      my $now = time;
      $sth->execute( $tag_id, "Base $version Install",
          1, $now, '3', $now, '3', 0, q{}, '3', q{} );
      $self->report("Done.\n");
      return 1;
  }
  
  sub run_all_workflows {
      my $self = shift;
      my $wgd  = $self->wgd;
      $self->report('Running all pending workflows... ');
      $self->report( 2, "\n" );
      require WebGUI::Workflow::Instance;
      my $sth = $wgd->db->connect->prepare(
          q{SELECT `instanceId` FROM `WorkflowInstance`});
      $sth->execute;
      while ( my ($instance_id) = $sth->fetchrow_array ) {
          my $instance
              = WebGUI::Workflow::Instance->new( $wgd->session, $instance_id );
          my $waiting_count = 0;
          while ( my $result = $instance->run ) {
              if ( $result eq 'complete' ) {
                  $waiting_count = 0;
                  next;
              }
              if ( $result eq 'waiting' ) {
                  ##no critic (ProhibitMagicNumbers)
                  if ( $waiting_count++ > 10 ) {
                      warn
                          "Unable to finish workflow $instance_id, too many iterations!\n";
                      last;
                  }
                  next;
              }
              if ( $result eq 'done' ) {
              }
              elsif ( $result eq 'error' ) {
                  warn "Unable to finish workflow $instance_id.  Error!\n";
              }
              else {
                  warn
                      "Unable to finish workflow $instance_id.  Unknown status $result!\n";
              }
              last;
          }
      }
      $self->report("Done.\n");
      return 1;
  }
  
  sub rebuild_lineage {
      my $self = shift;
      my $wgd  = $self->wgd;
      $self->report('Rebuilding lineage... ');
      my $pid = fork;
      if ( !$pid ) {
  
          # silence output of rebuildLineage unless we're at max verbosity
          if ( $self->verbosity < 3 ) {    ##no critic (ProhibitMagicNumbers)
              ##no critic (RequireCheckedOpen)
              open STDIN,  '<', File::Spec->devnull;
              open STDOUT, '>', File::Spec->devnull;
              open STDERR, '>', File::Spec->devnull;
          }
          $self->report("\n\n");
          $self->_run_script( 'rebuildLineage.pl',
              '--configFile=' . $wgd->config_file_relative );
      }
      waitpid $pid, 0;
      $self->report("Done.\n");
      return 1;
  }
  
  sub rebuild_index {
      my $self = shift;
      my $wgd  = $self->wgd;
      $self->report('Rebuilding search index... ');
      my $pid = fork;
      if ( !$pid ) {
  
          # silence output of searhc indexer unless we're at max verbosity
          if ( $self->verbosity < 3 ) {    ##no critic (ProhibitMagicNumbers)
              ##no critic (RequireCheckedOpen)
              open STDIN,  '<', File::Spec->devnull;
              open STDOUT, '>', File::Spec->devnull;
              open STDERR, '>', File::Spec->devnull;
          }
          print "\n\n";
          $self->_run_script( 'search.pl', '--indexsite',
              '--configFile=' . $wgd->config_file_relative );
      }
      waitpid $pid, 0;
      $self->report("Done.\n");
      return 1;
  }
  
  sub autologon {
      my $self = shift;
      my $wgd  = $self->wgd;
      my @session_ids;
      $self->report('Getting active browser site sessions... ');
      my $success;
      if ( eval { push @session_ids, $self->get_firefox_sessions; 1 } ) {
          $success = 1;
      }
      if ( eval { push @session_ids, $self->get_safari_sessions; 1 } ) {
          $success = 1;
      }
      if ($success) {
          $self->report("Done.\n");
      }
      else {
          $self->report("Failed!\n");
      }
  
      if (@session_ids) {
          $self->report('Creating sessions on site... ');
          for my $session_id (@session_ids) {
              my $session = $wgd->create_session($session_id);
              $session->user( { userId => 3 } );
              $session->var->switchAdminOn;
              $session->close;
          }
          $self->report("Done.\n");
      }
  }
  
  sub get_firefox_sessions {
      my $self = shift;
      my $wgd  = $self->wgd;
  
      require DBI;
      require DBD::SQLite;
  
      my $cookies_file = $self->get_firefox_cookiedb;
      my $dbh          = DBI->connect(
          'dbi:SQLite:dbname=' . $cookies_file,
          q{}, q{},
          {
              PrintError => 0,
              RaiseError => 1,
              ##no critic (ProhibitMagicNumbers)
              eval { DBD::SQLite->VERSION(1.27) }
              ? ( sqlite_unicode => 1 )
              : ( unicode => 1 ),
          } );
      my @sitenames         = @{ $wgd->config->get('sitename') };
      my $cookie_name       = $wgd->config->get('cookieName');
      my $gateway           = $wgd->config->get('gateway');
      my $site_placeholders = join q{,}, (q{?}) x @sitenames;
      my $sth               = $dbh->prepare(<<"END_SQL");
          SELECT
              value
          FROM
              moz_cookies
          WHERE
              name = ?
              AND host IN ($site_placeholders)
              AND path = ?
              AND expiry > ?
  END_SQL
      $sth->execute( $cookie_name, @sitenames, $gateway, time );
      my @session_ids;
  
      while ( my ($session_id) = $sth->fetchrow_array ) {
          push @session_ids, $session_id;
      }
      $sth->finish;
      $dbh->disconnect;
      return @session_ids;
  }
  
  sub get_firefox_cookiedb {
      my $self = shift;
  
      require File::HomeDir;
      require Config::INI::Reader;
      require File::Temp;
      File::Temp->VERSION(0.19); ##no critic (ProhibitMagicNumbers)
      require File::Copy;
  
      my $firefox_subdir
          = $^O eq 'darwin' ? 'Firefox'
          : $^O eq 'linux'  ? '.firefox'
          :                   WGDev::X->throw('Unsupported operating system');
  
      my $firefox_data_dir
          = File::Spec->catdir( File::HomeDir->my_data, $firefox_subdir );
      my $profile_path;
  
      my $profile_config = Config::INI::Reader->read_file(
          File::Spec->catfile( $firefox_data_dir, 'profiles.ini' ) );
      my @profiles = grep {/^Profile/msx} keys %{$profile_config};
      if ( @profiles == 1 ) {
          $profile_path = $profile_config->{ $profiles[0] }{Path};
      }
      else {
          for my $key (@profiles) {
              next
                  if !$profile_config->{$key}{Default};
              $profile_path = $profile_config->{$key}{Path};
          }
          WGDev::X->throw('Unable to find a profile')
              if !$profile_path;
      }
  
      $profile_path = File::Spec->rel2abs( $profile_path, $firefox_data_dir );
  
      # database is locked so we have to make a copy to check it
      my $cookies_file = File::Spec->catfile( $profile_path, 'cookies.sqlite' );
      my $tmpdir       = File::Temp::tempdir();
      my $cookies_copy = File::Spec->catfile( $tmpdir, 'cookies.sqlite' );
      File::Copy::copy( $cookies_file, $cookies_copy );
      return $cookies_copy;
  }
  
  sub get_safari_sessions {
      my $self = shift;
      my $wgd  = $self->wgd;
  
      if ( $^O ne 'darwin' ) {
          WGDev::X->throw('Safari cookies only available in Mac OS X.');
      }
  
      require HTTP::Cookies::Safari;
      require File::HomeDir;
  
      my %sitename    = map { $_ => 1 } @{ $wgd->config->get('sitename') };
      my $cookie_name = $wgd->config->get('cookieName');
      my $gateway     = $wgd->config->get('gateway');
  
      my $cookies_file
          = File::Spec->catfile( File::HomeDir->home, 'Library', 'Cookies',
          'Cookies.plist', );
      my $cookie_jar = HTTP::Cookies::Safari->new( file => $cookies_file );
      $cookie_jar->load;
  
      my @session_ids;
      $cookie_jar->scan(
          sub {
              my ( undef, $key, $value, $path, $domain ) = @_;
              if (   $key eq $cookie_name
                  && $path eq $gateway
                  && $sitename{$domain} )
              {
                  push @session_ids, $value;
              }
          } );
  
      return @session_ids;
  }
  
  sub _run_script {
      my $self   = shift;
      my $script = shift;
      my @args   = @_;
  
      # child process, don't need to worry about restoring anything
      chdir File::Spec->catdir( $self->wgd->root, 'sbin' );
  
      local @ARGV = @args;
      local $0    = q{./} . $script;
  
      ##no critic (ProhibitMultiplePackages)
      package main;
  {
    $main::VERSION = '0.1207160';
  }
      do $0;
      die $@
          if $@;
      exit;
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Command::Reset - Reset a site to defaults
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      wgd reset [-v] [-q] [-f] [-d | -b | -t]
  
  =head1 DESCRIPTION
  
  Resets a site to defaults, including multiple cleanup options for setting up
  a site for development or for a build.  Can also perform the cleanup functions
  without resetting a site.
  
  =head1 METHODS
  
  =head2 C<option_build>
  
  Enables options for creating a release build.  Equivalent to
  
      $reset->verbosity( $reset->verbosity + 1 );
      $reset->option( backup     => 1 );
      $reset->option( uploads    => 1 );
      $reset->option( import     => 1 );
      $reset->option( starter    => 1 );
      $reset->option( debug      => 0 );
      $reset->option( upgrade    => 1 );
      $reset->option( emptytrash => 1 );
      $reset->option( purge      => 1 );
      $reset->option( cleantags  => 1 );
      $reset->option( index      => 1 );
      $reset->option( runwf      => 1 );
  
  =head2 C<option_dev>
  
  Enables options for doing development work.  Equivalent to
  
      $reset->option( backup  => 1 );
      $reset->option( import  => 1 );
      $reset->option( uploads => 1 );
      $reset->option( upgrade => 1 );
      $reset->option( starter => 0 );
      $reset->option( debug   => 1 );
      $reset->option( clear   => 1 );
  
  =head2 C<option_fast>
  
  Enables options for doing a faster reset, usually used along with
  other group options or profiles.  Equivalent to
  
      $reset->option( uploads   => 0 );
      $reset->option( backup    => 0 );
      $reset->option( delcache  => 0 );
      $reset->option( purge     => 0 );
      $reset->option( cleantags => 0 );
      $reset->option( index     => 0 );
      $reset->option( runwf     => 0 );
  
  =head2 C<option_profile>
  
  Reads a profile from the config section C<command.reset.profiles>
  and processes it as a string of command line options.
  
  =head2 C<clear_cache>
  
  Clears the site's cache.
  
  =head2 C<backup>
  
  Creates a backup of the site database in the system's temp directory.
  
  =head2 C<reset_uploads>
  
  Clears and recreates the uploads location for a site.
  
  =head2 C<import_db_script>
  
  Imports a base database script for the site.  If
  C<< $reset->option('upgrade') >> is set, F<previousVersion.sql> is
  used.  Otherwise, F<create.sql> is used.
  
  =head2 C<upgrade>
  
  Performs an upgrade on the site
  
  =head2 C<set_settings>
  
  Enabled/disables debug mode and extended/standard session timeout
  based on C<< $reset->option('debug') >> and enables/disables the
  site starter based on C<< $reset->option('starter') >>.
  
  =head2 C<reset_config>
  
  Resets the site's config file based on the rules listed in
  L</WebGUI Config File Reset>.
  
  =head2 C<empty_trash>
  
  Purges all items from the trash.
  
  =head2 C<purge_old_revisions>
  
  Purges all asset revisions aside from the most recent.
  
  =head2 C<clean_version_tags>
  
  Collapses all version tags into a single tag labeled based on the
  current WebGUI version.
  
  =head2 C<clear_default_content>
  
  Removes all content descending from the default asset, excluding
  Page Layout assets.
  
  =head2 C<delete_users>
  
  Removes all users from the site, excepting the default users of
  Admin and Visitor.
  
  =head2 C<rebuild_index>
  
  Rebuilds the search index of the site using the F<search.pl> script.
  
  =head2 C<rebuild_lineage>
  
  Rebuilds the lineage of the site using the F<rebuildLineage.pl> script.
  
  =head2 C<run_all_workflows>
  
  Attempts to finish processing all active workflows.  Waiting workflows
  will be run up to 10 times to complete them.
  
  =head2 C<autologon>
  
  Attempts to create sessions on the site matching browser cookies.
  
  =head2 C<get_firefox_sessions>
  
  Returns a list of session IDs that Firefox has set in cookies for the site.
  
  =head2 C<get_firefox_cookiedb>
  
  Returns the name of a copy of Firefox's cookie database.
  
  =head2 C<get_safari_sessions>
  
  Returns a list of session IDs that Safari has set in cookies for the site.
  
  =head1 OPTIONS
  
  =over 8
  
  =item C<-v> C<--verbose>
  
  Output more information
  
  =item C<-q> C<--quiet>
  
  Output less information
  
  =item C<-f> C<--fast>
  
  Fast mode - equivalent to:
  C<--no-upload --no-backup --no-delcache --no-purge --no-cleantags --no-index --no-runwf>
  
  =item C<-t> C<--test>
  
  Test mode - equivalent to:
  C<--backup --import>
  
  =item C<-d> C<--dev>
  
  Developer mode - equivalent to:
  C<--backup --import --no-starter --debug --clear --upgrade --uploads>
  
  =item C<-b> C<--build>
  
  Build mode - equivalent to:
  C<--verbose --backup --import --starter --no-debug --upgrade --purge --cleantags --index --runwf>
  
  =item C<--[no-]backup>
  
  Backup database before doing any other operations.
  
  =item C<--[no-]delcache>
  
  Delete the site's cache.  Defaults to on.
  
  =item C<--[no-]import=>
  
  Import a database script.  If no database file is specified,
  F<docs/create.sql> or F<docs/previousVersion.sql> will be used
  depending on if the upgrade option has been specified.
  
  =item C<--[no-]uploads>
  
  Recreate uploads directory
  
  =item C<--[no-]upgrade>
  
  Perform an upgrade - also controls which database script to import
  
  =item C<--[no-]debug>
  
  Enable debug mode and increase session timeout
  
  =item C<--[no-]starter>
  
  Enable the site starter
  
  =item C<--[no-]clear>
  
  Clear the content off the home page and its children
  
  =item C<--[no-]config>
  
  Resets the site's config file.  Some values like database information will be
  preserved.  Additional options can be set in the WGDev config file.
  
  =item C<--[no-]emptytrash>
  
  Purges all items from the trash
  
  =item C<--[no-]purge>
  
  Purge all old revisions
  
  =item C<--[no-]cleantags>
  
  Removes all version tags and sets all asset revisions to be
  under a new version tag marked with the current version number
  
  =item C<--[no-]index>
  
  Rebuild the site lineage and reindex all of the content
  
  =item C<--[no-]runwf>
  
  Attempt to finish any running workflows
  
  =item C<--[no-]autologon>
  
  Attempt to create sessions on the site logged in as Admin, with
  admin mode enabled based on browser cookies.
  
  =item C<--util=>
  
  Run a utility script.  Script will be run last, being passed to the
  L<C<util> command|WGDev::Command::Util>.  Parameter can be specified multiple
  times to run additional scripts.
  
  =item C<-p> C<--pro[file]=>
  
  Specify a profile of options to use for resetting.  Profiles are specified in
  the WGDev config file under the C<command.reset.profiles> section.  A profile
  is defined as a string to be used as additional command line options.
  
  =back
  
  =head1 CONFIGURATION
  
  =over 8
  
  =item C<< profiles.<profile name> >>
  
  Creates a profile to use with the C<--profile> option.  The value of the config
  parameter is a string with the command line parameters to apply when this
  profile is used.
  
  =item C<config.overide>
  
  Overrides to apply when resetting config file.
  
  =item C<config.copy>
  
  Parameters to copy from existing config file when resetting it.
  
  =back
  
  =head2 WebGUI Config File Reset
  
  The config file is reset by taking the currently specified WebGUI config file,
  the F<WebGUI.conf.orig> file that WGDev finds in the F<etc> directory, and
  instructions in the WGDev config file (see L<WGDev::Command::Config>).
  
  The reset config file contains in order of priority: options copied from the
  existing config file, override options, and options in the F<WebGUI.conf.orig>
  file.
  
  Overrides are specified in the config parameter C<command.reset.config.overide>
  as a hash of options to apply.  Copied parameters are specified in
  C<command.reset.config.copy> as a list of entries to copy.  In addition to the
  configured list, a set of parameters is always copied:
  
      dsn         dbuser          dbpass
      uploadsPath uploadsURL
      extrasPath  extrasURL
      exportPath
      cacheType   fileCacheRoot
      sitename
      spectreIp   spectrePort     spectreSubnets
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_COMMAND_RESET

$fatpacked{"WGDev/Command/Run.pm"} = <<'WGDEV_COMMAND_RUN';
  package WGDev::Command::Run;
  {
    $WGDev::Command::Run::VERSION = '0.1207160';
  }
  # ABSTRACT: Run arbitrary shell command
  use strict;
  use warnings;
  use 5.008008;
  
  use parent qw(WGDev::Command::Base);
  
  sub process {
      my $self = shift;
      $self->wgd->set_environment;
      my @arguments = $self->arguments;
      my $command   = shift @arguments;
      my $result    = system {$command} $command, @arguments;
      return $result ? 0 : 1;
  }
  
  sub parse_params {
      my ( $self, @args ) = @_;
      $self->arguments( \@args );
      return 1;
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Command::Run - Run arbitrary shell command
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      wgd run <command>
  
  =head1 DESCRIPTION
  
  Runs an arbitrary command, but sets the C<WEBGUI_CONFIG>, C<WEBGUI_ROOT>, and
  C<PERL5LIB> environment variables first.
  
  =head1 OPTIONS
  
  Has no options of its own.  All options are passed on to specified command.
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_COMMAND_RUN

$fatpacked{"WGDev/Command/Self/Upgrade.pm"} = <<'WGDEV_COMMAND_SELF_UPGRADE';
  package WGDev::Command::Self::Upgrade;
  {
    $WGDev::Command::Self::Upgrade::VERSION = '0.1207160';
  }
  # ABSTRACT: Upgrade WGDev script
  use strict;
  use warnings;
  use 5.008008;
  
  use parent qw(WGDev::Command::Base);
  use WGDev::X;
  use WGDev::Command;
  
  sub needs_root { return }
  sub config_options { () }
  
  sub is_runnable {
      # use the presence of fatpacker to detect single script install
      # this command is not meant for upgrading module install
      return $App::WGDev::PACKED;
  }
  
  sub process {
      my $self = shift;
      my $file = $0;
      require File::Temp;
      require LWP::UserAgent;
      if (! -w $file) {
          WGDev::X::IO::Write->throw( path => $file );
      }
      my $our_version = WGDev::Command->VERSION;
      print "Current version: $our_version\n";
      my $ua = LWP::UserAgent->new;
      my $res = $ua->get('http://haarg.org/wgd');
      if (! $res->is_success) {
          WGDev::X->throw('Unable to download new version');
      }
      my $content = $res->decoded_content;
      my $new_version = do {
          my $temp_script = File::Temp->new;
          $temp_script->autoflush(1);
          print { $temp_script } $content;
          open my $fh, q{-|}, $^X, q{--}, $temp_script->filename, '-V'
              or WGDev::X::IO->throw;
          my $output = do { local $/; <$fh> };
          close $fh
              or WGDev::X::IO->throw;
          my ($script_version) = ($output =~ /(\d[\d.]+)/msx);
          $script_version;
      };
      print "New version: $new_version\n";
      if ($our_version eq $new_version) {
          print "Already up to date.\n";
          return 1;
      }
      print "Upgrading.\n";
      open my $fh, '>', $file
          or WGDev::X::IO->throw;
      print { $fh } $content;
      close $fh
          or WGDev::X::IO->throw;
      exec $^X, $file, '-V';
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Command::Self::Upgrade - Upgrade WGDev script
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      wgd self-upgrade
  
  =head1 DESCRIPTION
  
  Upgrades the WGDev script.
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_COMMAND_SELF_UPGRADE

$fatpacked{"WGDev/Command/Setting.pm"} = <<'WGDEV_COMMAND_SETTING';
  package WGDev::Command::Setting;
  {
    $WGDev::Command::Setting::VERSION = '0.1207160';
  }
  # ABSTRACT: Returns WebGUI settings from the database.
  use strict;
  use warnings;
  use 5.008008;
  
  use parent qw(WGDev::Command::Base);
  
  sub config_options {
      return qw();
  }
  
  sub process {
      my $self = shift;
      my $wgd  = $self->wgd;
      my $dbh  = $wgd->db->connect;
      my $sth
          = $dbh->prepare('SELECT name, value FROM settings WHERE name LIKE ?');
      my $sth_set;
      foreach my $argument ( $self->arguments ) {
          my $new_value;
          if ( $argument =~ s/=(.*)//msx ) {
              $new_value = $1;
          }
          $sth->execute($argument);
          while ( my ( $setting, $value ) = $sth->fetchrow_array ) {
              if ( !defined $value ) {
                  $value = '(NULL)';
              }
              if ( defined $new_value ) {
                  $sth_set ||= $dbh->prepare(
                      'UPDATE settings SET value = ? WHERE name = ?');
                  $sth_set->execute( $new_value, $setting );
                  $sth_set->finish;
                  printf "%-39s %s => %s\n", $setting, $value, $new_value;
              }
              else {
                  printf "%-39s %s\n", $setting, $value;
              }
          }
      }
  
      return 1;
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Command::Setting - Returns WebGUI settings from the database.
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      wgd setting <setting>[=<value>] [<setting> ...]
  
  =head1 DESCRIPTION
  
  Prints settings from the WebGUI settings table.  This is handy for doing quick lookups,
  or for using as part of other C<wgd> commands.  Can also the the value of settings.
  
  =head1 OPTIONS
  
  =over 8
  
  =item C<< <setting> >>
  
  The name of the setting to display.  Can also contain SQL wildcards
  to show multiple settings.  Using a setting of C<%> will display
  all settings.
  
  =item C<< <value> >>
  
  The value to set the setting to.  If specified, the old value and
  new value will be included in the output.
  
  =back
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_COMMAND_SETTING

$fatpacked{"WGDev/Command/Test.pm"} = <<'WGDEV_COMMAND_TEST';
  package WGDev::Command::Test;
  {
    $WGDev::Command::Test::VERSION = '0.1207160';
  }
  # ABSTRACT: Run WebGUI tests
  use strict;
  use warnings;
  use 5.008008;
  
  use parent qw(WGDev::Command::Base);
  
  use File::Spec ();
  
  sub config_parse_options { return qw(gnu_getopt pass_through) }
  
  sub config_options {
      return qw(
          all|A
          slow
          live|L
          debug
          reset:s
          cover|C:s
          coverOptions:s
      );
  }
  
  sub process {
      my $self = shift;
      require Cwd;
      require App::Prove;
  
      my $wgd = $self->wgd;
      $wgd->set_environment;
  
      if ( defined $self->option('reset') ) {
          my $reset_options = $self->option('reset');
          if ( $reset_options eq q{} ) {
              $reset_options = '--quiet --backup --delcache --import --upgrade';
          }
          require WGDev::Command::Reset;
          my $reset = WGDev::Command::Reset->new($wgd);
          $reset->parse_params_string($reset_options);
          $reset->process;
      }
  
      ##no critic (RequireLocalizedPunctuationVars)
      local $ENV{CODE_COP} = 1
          if $self->option('slow');
      local $ENV{TEST_SYNTAX} = 1
          if $self->option('slow');
      local $ENV{TEST_POD} = 1
          if $self->option('slow');
  
      local $ENV{WEBGUI_LIVE} = 1
          if $self->option('live');
  
      local $ENV{WEBGUI_TEST_DEBUG} = 1
          if $self->option('debug');
  
      local $ENV{HARNESS_PERL_SWITCHES} = $ENV{HARNESS_PERL_SWITCHES};
      my $cover_dir;
      if ( defined $self->option('cover') ) {
          $cover_dir = $self->option('cover') || 'cover_db';
          if ( -e $cover_dir ) {
              system 'cover', '-silent', '-delete', $cover_dir;
          }
          my $cover_options = $self->option('coverOptions')
              || '-select,WebGUI,+ignore,^t';
          if ( $ENV{HARNESS_PERL_SWITCHES} ) {
              $ENV{HARNESS_PERL_SWITCHES} .= q{ };
          }
          else {
              $ENV{HARNESS_PERL_SWITCHES} = q{};
          }
          $ENV{HARNESS_PERL_SWITCHES} .= '-MDevel::Cover=' . join q{,},
              -silent => 1,
              $cover_options, -db => $cover_dir;
      }
  
      my $prove = App::Prove->new;
      my @args  = $self->arguments;
      @args = ( '-r', grep { $_ ne '-r' } @args );
      my $orig_dir;
      if ( $self->option('all') ) {
          $orig_dir = Cwd::cwd();
          chdir $wgd->root;
          unshift @args, 't';
      }
      $prove->process_args(@args);
      my $result = $prove->run;
      if ($orig_dir) {
          chdir $orig_dir;
      }
      if ( defined $cover_dir ) {
          system 'cover', '-silent', $cover_dir;
      }
      return $result;
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Command::Test - Run WebGUI tests
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      wgd test [-ASCL] [--debug] [<prove options>]
  
  =head1 DESCRIPTION
  
  Runs WebGUI tests, setting the needed environment variables beforehand.
  Includes quick options for running all tests, and including slow tests.
  
  =head1 OPTIONS
  
  Unrecognized options will be passed through to prove.
  
  =over 8
  
  =item C<-A> C<--all>
  
  Run all tests recursively.  Otherwise, tests will need to be specified.
  
  =item C<--slow>
  
  Includes slow tests by defining CODE_COP, TEST_SYNTAX, and TEST_POD.
  
  =item C<-L> C<--live>
  
  Includes live tests by defining WEBGUI_LIVE.
  
  =item C<--debug>
  
  After a test, output the number of assets, version tags, users, groups, sessions
  and session scratch variables, to determine when tests leak objects that can interfere
  with downstream tests.
  
  This option is really only useful when passing the --verbose switch through to prove.
  
  =item C<--reset=>
  
  Perform a site reset before running the tests.  The value specified is used
  as the command line parameters for the L<C<reset> command|WGDev::Command::Reset>.
  With no value, will use the options C<--delcache --backup --import --upgrade> to do a
  fast site reset.
  
  =item C<-C> C<--cover=>
  
  Run coverage using Devel::Cover. The value specified is used as the directory to 
  put the coverage data and defaults to C<cover_db>.
  
  =item C<--coverOptions=>
  
  Options to pass to L<Devel::Cover>. Defaults to C<-select,WebGUI,+ignore,^t>.
  
  =back
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_COMMAND_TEST

$fatpacked{"WGDev/Command/Trash.pm"} = <<'WGDEV_COMMAND_TRASH';
  package WGDev::Command::Trash;
  {
    $WGDev::Command::Trash::VERSION = '0.1207160';
  }
  # ABSTRACT: Trash assets by URL/assetId
  use strict;
  use warnings;
  use WGDev::Command::Ls;
  use 5.008008;
  
  use parent qw(WGDev::Command::Base);
  
  use WGDev ();
  
  sub config_options {
      return qw(
          purge
          restore
          list
      );
  }
  
  sub process {
      my $self = shift;
      my $wgd  = $self->wgd;
      if ($self->option('list')) {
          return $self->list_trash;
      }
      else {
          return $self->trash;
      }
  }
  
  sub trash {
      my $self = shift;
      my $wgd  = $self->wgd;
      my $error;
      my $method  = $self->option('purge')   ? 'purge'
                  : $self->option('restore') ? 'restore'
                  : 'trash';
      my @asset_specs = $self->arguments;
      ASSET:
      while ( my $asset_spec = shift @asset_specs ) {
          my $asset;
          if ( !eval { $asset = $wgd->asset->find($asset_spec) } ) {
              warn "wgd trash: $asset_spec: No such asset\n";
              $error++;
              next ASSET;
          }
          my $success = $asset->$method;
          if ($method ne 'restore' && ! $success) {
              warn "wgd trash: unable to $method $asset_spec\n";
              ++$error;
          }
      }
  
      return (! $error);
  }
  
  sub list_trash {
      my $self = shift;
      my $wgd  = $self->wgd;
      my $root = $wgd->asset->root;
      my $trashed_assets = $root->getAssetsInTrash();
      my $ls = WGDev::Command::Ls->new($wgd);
      my $format = '%assetId% %url:-35% %title%';
      ASSET:
      foreach my $asset ( @{ $trashed_assets } ) {
          next ASSET unless $asset;
          my $output = $ls->format_output( $format, $asset );
          print $output . "\n";
      }
  
      return 1;
  }
  
  1;
  
  
  1;
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Command::Trash - Trash assets by URL/assetId
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      wgd trash [--purge] [--restore] <asset> [<asset> ...]
      wgd trash [--list]
  
  =head1 DESCRIPTION
  
  Methods for working with assets in the trash.
  
  =head1 OPTIONS
  
  =over 8
  
  =item C<--purge>
  
  Purges the assets from the system instead of putting it into the trash.
  
  =item C<--restore>
  
  Restores the assets that have been trashed to the regular, published state.
  
  =item C<--list>
  
  Lists all assets in the trash.
  
  =item C<< <asset> >>
  
  Either an asset URL or an ID.  As many can be specified as desired.
  Prepending with a slash will force it to be interpreted as a URL.
  
  =back
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_COMMAND_TRASH

$fatpacked{"WGDev/Command/User.pm"} = <<'WGDEV_COMMAND_USER';
  package WGDev::Command::User;
  {
    $WGDev::Command::User::VERSION = '0.1207160';
  }
  # ABSTRACT: Utilities for manipulating WebGUI Users
  use strict;
  use warnings;
  use 5.008008;
  
  use parent qw(WGDev::Command::Base);
  
  use WGDev::X ();
  
  sub config_options {
      return qw(
          delete
          findByPassword=s
          findByDictionary=s
      );
  }
  
  sub process {
      my $self = shift;
      my $wgd  = $self->wgd;
  
      my $session = $wgd->session();
  
      if ( my $password = $self->option('findByPassword') ) {
          return $self->find_by_password($password);
      }
  
      if ( my $dictionary = $self->option('findByDictionary') ) {
          return $self->find_by_dictionary($dictionary);
      }
  
      if ( $self->option('delete') ) {
          return $self->delete_user();
      }
  }
  
  # this is named 'delete_user()' because 'delete' is a built-in function in Perl
  sub delete_user {
      my $self    = shift;
      my $user    = undef;
      my $wgd     = $self->wgd;
      my $session = $wgd->session();
  
      if ( !$self->arguments ) {
          WGDev::X->throw("No user to delete!\n");
      }
  
      foreach my $userId ( $self->arguments ) {
          eval {
              $user = new WebGUI::User($session, $userId);
          };
          if ($@ || !$user->validUserId($session, $userId)) {
              WGDev::X::UserNotFound->throw(userId => $userId);
          }
  
          $user->delete();
      }
  }
  
  sub find_by_password {
      my $self     = shift;
      my $password = shift;
      my $session  = $self->wgd->session();
  
      require Digest::MD5;
      require Encode;
      my $hash = Digest::MD5::md5_base64( Encode::encode_utf8($password) );
      print "Password:\t$password\n";
      print "Hashes to:\t$hash\n";
      my @user_ids = $session->db->buildArray(
          'select userId from authentication where fieldName = ? and fieldData = ?',
          [ 'identifier', $hash ] );
      print "Matching users:\t";
      print @user_ids ? "\n" : "None\n";
  
      for my $user_id (@user_ids) {
          my $user = WebGUI::User->new( $session, $user_id );
          my $username = $user->username;
          print " * $user_id ($username)\n";
      }
      return;
  }
  
  sub find_by_dictionary {
      my $self    = shift;
      my $dict    = shift;
      my $session = $self->wgd->session();
  
      my @hashed_passwords
          = $session->db->buildArray(
          'select fieldData from authentication where fieldName = ?',
          ['identifier'] );
      my %hashed_passwords = map { $_ => 1 } @hashed_passwords;
      open my $d, '<', $dict
          or WGDev::X::IO::Read->throw(
          path    => $dict,
          message => 'Unable to open dictionary file',
          );
      while ( my $word = <$d> ) {
          chomp $word;
          my $hash = Digest::MD5::md5_base64( Encode::encode_utf8($word) );
          if ( $hashed_passwords{$hash} ) {
              print "\n*** HIT ***\n";
              $self->find_by_password($word);
          }
      }
      close $d
          or WGDev::X::IO::Read->throw(
          path    => $dict,
          message => 'Unable to open dictionary file',
          );
      return;
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Command::User - Utilities for manipulating WebGUI Users
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      wgd user [--delete userId [userId ...]] [--findByPassword <password>] [--findByDictionary <dictionary>]
  
  =head1 DESCRIPTION
  
  Utilities for manipulating WebGUI Users
  
  =head1 METHODS
  
  =head2 delete_user
  
  Deletes the specified user(s), given a list of userIds on the command line.
  
  =head2 find_by_password
  
  Hashes the given password and sees if any user IDs in the C<authentication> table
  match. This check will become less efficient once WebGUI implements password salting.
  
  =head2 find_by_dictionary
  
  Search through the given dictionary file, hashing words one by one and
  checking them against all known hashed passwords.
  
  Does not try to be efficient or clever - for common dictionary files it's
  plenty fast enough.
  
  =head1 OPTIONS
  
  =over 8
  
  =item C<--delete>
  
  Delete the specified user(s) by their userId.
  
  =item C<--findByPassword>
  
  Return a list of users that are using the given password (assumes
  WebGUI authentication module).
  
  =item C<--findByDictionary>
  
  Use a dictionary file to do a brute-force search for users using
  any password in the dictionary (assumes WebGUI authentication
  module). For example, Linux distributions typically have a dictionary
  file in C</usr/share/dict/> or C</var/lib/dict/>
  
  =back
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_COMMAND_USER

$fatpacked{"WGDev/Command/Util.pm"} = <<'WGDEV_COMMAND_UTIL';
  package WGDev::Command::Util;
  {
    $WGDev::Command::Util::VERSION = '0.1207160';
  }
  # ABSTRACT: Run a utility script
  use strict;
  use warnings;
  use 5.008008;
  
  use parent qw(WGDev::Command::Base::Verbosity);
  
  use File::Spec ();
  
  sub config_parse_options { return qw(gnu_getopt pass_through) }
  
  sub process {
      my $self = shift;
      my $wgd  = $self->wgd;
  
      $wgd->set_environment;
  
      my @args    = $self->arguments;
      my $command = shift @args;
  
      unshift @args, '--configFile=' . $wgd->config_file_relative;
  
      my $sbin_path = File::Spec->catdir( $wgd->root, 'sbin' );
  
      if ( -e $command ) {
          $command = File::Spec->rel2abs($command);
      }
      elsif ( -e File::Spec->rel2abs( $command, $sbin_path ) ) {
          $command = File::Spec->rel2abs( $command, $sbin_path );
      }
      else {
          WGDev::X->throw("Unable to find $command.");
      }
  
      if ( !-x $command ) {
          unshift @args, $command;
  
          # $^X is the name of the current perl executable
          $command = $^X;
      }
  
      my $pid = fork;
      if ( !$pid ) {
          if ( $self->verbosity < 1 ) {
              ##no critic (RequireCheckedOpen)
              open STDIN,  '<', File::Spec->devnull;
              open STDOUT, '>', File::Spec->devnull;
              open STDERR, '>', File::Spec->devnull;
          }
          chdir $sbin_path;
          exec {$command} $command, @args;
      }
      waitpid $pid, 0;
  
      # $? is the child's exit value
      return $? ? 0 : 1;
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Command::Util - Run a utility script
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      wgd util [-q] <command>
  
  =head1 DESCRIPTION
  
  Runs a utility script.  The script will be run from WebGUI's F<sbin>
  directory, and will be passed a C<--configFile> option.
  
  =head1 OPTIONS
  
  Any options not handled by this command are passed to the utility script.
  
  =over 8
  
  =item C<-q> C<--quiet>
  
  If specified, will silence all output from the utility script.
  
  =back
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_COMMAND_UTIL

$fatpacked{"WGDev/Command/Version.pm"} = <<'WGDEV_COMMAND_VERSION';
  package WGDev::Command::Version;
  {
    $WGDev::Command::Version::VERSION = '0.1207160';
  }
  # ABSTRACT: Reports and updates version numbers
  use strict;
  use warnings;
  use 5.008008;
  
  use parent qw(WGDev::Command::Base);
  
  use WGDev::X   ();
  use File::Spec ();
  
  sub needs_config {
      return;
  }
  
  sub config_parse_options { return qw(no_getopt_compat) }
  
  sub config_options {
      return qw(
          create|c
          bare|b
          dist|d
      );
  }
  
  sub process {
      my $self = shift;
      my $wgd  = $self->wgd;
  
      my ($ver) = $self->arguments;
  
      if ( $self->option('create') ) {
          $ver = $self->update_version($ver);
      }
  
      my $wgv = $wgd->version;
      my ( $perl_version, $perl_status ) = $wgv->module;
      if ( $self->option('dist') ) {
          print $perl_version, q{-}, $perl_status, "\n";
          return 1;
      }
      if ( $self->option('bare') ) {
          print $perl_version, "\n";
          return 1;
      }
  
      my $db_version = $wgv->database_script;
      my ( $change_file, $change_version ) = $wgv->changelog;
      my ( $up_file, undef, $up_file_ver, $up_version ) = $wgv->upgrade;
      my $db_live_version = eval { $wgv->database( $wgd->db->connect ) };
  
      my $err_count = 0;
      my $expect_ver = $ver || $perl_version;
      if ( $perl_version ne $expect_ver ) {
          $err_count++;
          $perl_version = _colored( $perl_version, 'red' );
      }
      if ( $db_version ne $expect_ver ) {
          $err_count++;
          $db_version = _colored( $db_version, 'magenta' );
      }
      if ( $change_version ne $expect_ver ) {
          $err_count++;
          $change_version = _colored( $change_version, 'red' );
      }
      if ( $up_version ne $expect_ver ) {
          $err_count++;
          $up_version = _colored( $up_version, 'red' );
      }
      if ( $up_file_ver ne $expect_ver ) {
          $err_count++;
          $up_file = _colored( $up_file, 'red' );
      }
      if ( !defined $db_live_version ) {
          $err_count++;
          $db_live_version = _colored( 'Not available', 'magenta' );
      }
      elsif ( $db_live_version ne $expect_ver ) {
          $err_count++;
          $db_live_version = _colored( $db_live_version, 'red' );
      }
  
      print <<"END_REPORT";
    Perl version:             $perl_version - $perl_status
    Database version:         $db_live_version
    Database script version:  $db_version
    Changelog version:        $change_version
    Upgrade script version:   $up_version
    Upgrade script filename:  $up_file
  END_REPORT
  
      if ($err_count) {
          return 0;
      }
      return 1;
  }
  
  sub update_version {
      my $self        = shift;
      my $ver         = shift;
      my $wgd         = $self->wgd;
      my $root        = $wgd->root;
      my $wgv         = $wgd->version;
      my $old_version = $wgv->module;
      my $new_version = $ver || do {
          my @parts = split /[.]/msx, $old_version;
          $parts[-1]++;
          join q{.}, @parts;
      };
  
      open my $fh, '<', File::Spec->catfile( $root, 'lib', 'WebGUI.pm' )
          or WGDev::X::IO::Read->throw( path => 'WebGUI.pm' );
      my @pm_content = do { local $/; <$fh> };
      close $fh
          or WGDev::X::IO::Read->throw( path => 'WebGUI.pm' );
      open $fh, '>', File::Spec->catfile( $root, 'lib', 'WebGUI.pm' )
          or WGDev::X::IO::Write->throw( path => 'WebGUI.pm' );
      for my $line (@pm_content) {
          $line =~ s/(\$VERSION\s*=)[^\n]*;/$1 '$new_version';/msx;
          print {$fh} $line;
      }
      close $fh
          or WGDev::X::IO::Write->throw( path => 'WebGUI.pm' );
  
      my ($change_file) = $wgv->changelog;
      open $fh, '<',
          File::Spec->catfile( $root, 'docs', 'changelog', $change_file )
          or WGDev::X::IO::Read->throw( path => $change_file );
      my $change_content = do { local $/; <$fh> };
      close $fh
          or WGDev::X::IO::Read->throw( path => $change_file );
  
      open $fh, '>',
          File::Spec->catfile( $root, 'docs', 'changelog', $change_file )
          or WGDev::X::IO::Write->throw( path => $change_file );
      print {$fh} $new_version . "\n\n" . $change_content;
      close $fh
          or WGDev::X::IO::Write->throw( path => $change_file );
  
      open my $in, '<',
          File::Spec->catfile( $root, 'docs', 'upgrades', '_upgrade.skeleton' )
          or WGDev::X::IO::Read->throw( path => '_upgrade.skeleton' );
      open my $out, '>',
          File::Spec->catfile( $root, 'docs', 'upgrades',
          "upgrade_$old_version-$new_version.pl" )
          or WGDev::X::IO::Write->throw(
          path => "upgrade_$old_version-$new_version.pl" );
      while ( my $line = <$in> ) {
          $line =~ s/(\$toVersion\s*=)[^\n]*$/$1 '$new_version';/xms;
          print {$out} $line;
      }
      close $out
          or WGDev::X::IO::Write->throw(
          path => "upgrade_$old_version-$new_version.pl" );
      close $in
          or WGDev::X::IO::Read->throw( path => '_upgrade.skeleton' );
      return $new_version;
  }
  
  sub _colored {
      no warnings 'redefine';
      if ( eval { require Term::ANSIColor; 1 } ) {
          *_colored = \&Term::ANSIColor::colored;
      }
      else {
          *_colored = sub { $_[0] };
      }
      goto &_colored;
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Command::Version - Reports and updates version numbers
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      wgd version [-b | -c | -d] [<version>]
  
  =head1 DESCRIPTION
  
  Reports the current versions of the F<WebGUI.pm> module, F<create.sql> database
  script, change log, and upgrade file.  Non-matching versions will be noted
  in red if possible.
  
  =head1 METHODS
  
  =head2 C<update_version ( $new_version )>
  
  Updates WebGUI's version number to the specified version.  If not provided,
  the patch level of the version number is incremented.  The version number in
  F<WebGUI.pm> is changed, a new upgrade script is created, and a heading is
  added to the change log.
  
  =head1 OPTIONS
  
  =over 8
  
  =item C<-c> C<--create>
  
  Adds a new section to the change log for the new version, updates the version
  number in F<WebGUI.pm>, and creates a new upgrade script.  The version number
  to update to can be specified on the command line.  If not specified, defaults
  to incrementing the patch level by one.
  
  =item C<-d> C<--dist>
  
  Output the version number and status of the current WebGUI, joined by a dash.
  If the version is passed as well, it will be ignored.
  
  =item C<-b> C<--bare>
  
  Outputs the version number taken from F<WebGUI.pm> only
  
  =item C<< <version> >>
  
  The version number to compare against or create
  
  =back
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_COMMAND_VERSION

$fatpacked{"WGDev/Database.pm"} = <<'WGDEV_DATABASE';
  package WGDev::Database;
  {
    $WGDev::Database::VERSION = '0.1207160';
  }
  # ABSTRACT: Database connectivity and DSN parsing for WGDev
  use strict;
  use warnings;
  use 5.008008;
  
  use WGDev::X ();
  
  sub username { return shift->{username} }
  sub password { return shift->{password} }
  sub database { return shift->{database} }
  sub hostname { return shift->{hostname} }
  sub port     { return shift->{port} }
  sub dsn      { return shift->{dsn} }
  
  sub user { goto &username }
  sub pass { goto &password }
  sub host { goto &hostname }
  sub name { goto &database }
  
  sub new {
      my $class  = shift;
      my $config = shift;
      my $self   = bless {}, $class;
  
      my $dsn = $self->{dsn} = $config->get('dsn');
      $self->{username} = $config->get('dbuser');
      $self->{password} = $config->get('dbpass');
      $self->{database} = ( split /[:;]/msx, $dsn )[2];
      $self->{hostname} = 'localhost';
      $self->{port}     = '3306';
      while ( $dsn =~ /([^=;:]+)=([^;:]+)/msxg ) {
          if ( $1 eq 'host' || $1 eq 'hostname' ) {
              $self->{hostname} = $2;
          }
          elsif ( $1 eq 'db' || $1 eq 'database' || $1 eq 'dbname' ) {
              $self->{database} = $2;
          }
          elsif ( $1 eq 'port' ) {
              $self->{port} = $2;
          }
      }
      return $self;
  }
  
  sub command_line {
      my $self   = shift;
      my @params = (
          '-h' . $self->hostname,
          '-P' . $self->port,
          $self->database,
          '-u' . $self->username,
          ( $self->password ? '-p' . $self->password : () ),
          '--default-character-set=utf8',
          @_,
      );
      return wantarray ? @params : join q{ }, map {"'$_'"} @params;
  }
  
  sub connect {    ## no critic (ProhibitBuiltinHomonyms)
      my $self = shift;
      require DBI;
      if ( $self->{dbh} && !$self->{dbh}->ping ) {
          delete $self->{dbh};
      }
      return $self->{dbh} ||= DBI->connect(
          $self->dsn,
          $self->username,
          $self->password,
          {
              RaiseError        => 1,
              PrintWarn         => 0,
              PrintError        => 0,
              mysql_enable_utf8 => 1,
          } );
  }
  sub dbh  { return shift->{dbh} }
  sub open { goto &connect }         ## no critic (ProhibitBuiltinHomonyms)
  
  sub disconnect {
      my $self = shift;
      if ( my $dbh = delete $self->{dbh} ) {
          $dbh->disconnect;
      }
      return;
  }
  
  sub close {    ## no critic (ProhibitBuiltinHomonyms ProhibitAmbiguousNames)
      goto &disconnect;
  }
  
  sub clear {
      my $self   = shift;
      my $dbh    = $self->connect;
      my $sth    = $dbh->table_info( undef, undef, q{%} );
      my @tables = map { @{$_} } @{ $sth->fetchall_arrayref( [2] ) };
      $dbh->do('SET FOREIGN_KEY_CHECKS = 0');
      for my $table (@tables) {
          $dbh->do( 'DROP TABLE ' . $dbh->quote_identifier($table) );
      }
      $dbh->do('SET FOREIGN_KEY_CHECKS = 1');
      return 1;
  }
  
  sub load {
      my $self     = shift;
      my $dumpfile = shift;
      system 'mysql', $self->command_line( '-e' . 'source ' . $dumpfile )
          and WGDev::X::System->throw('Error running mysql');
      return 1;
  }
  
  sub dump {    ## no critic (ProhibitBuiltinHomonyms)
      my $self     = shift;
      my $dumpfile = shift;
      system 'mysqldump', $self->command_line( '-r' . $dumpfile )
          and WGDev::X::System->throw('Error running mysqldump');
      return 1;
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Database - Database connectivity and DSN parsing for WGDev
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      my $dsn = $wgd->database->connect;
      my $username = $wgd->database->username;
  
  =head1 DESCRIPTION
  
  Has methods to access various parts of the DSN that can be used for other
  programs such as command line C<mysql>.  Also has methods to easily connect
  and reuse a database connection.
  
  =head1 METHODS
  
  =head2 C<new ( $wgd )>
  
  Creates a new WGDev::Database object.
  
  =over 4
  
  =item C<$wgd>
  
  An instantiated WGDev object.
  
  =back
  
  =head2 C<dsn>
  
  Returns the DSN for the database.
  
  =head2 C<database>
  
  Returns the name of the database.
  
  =head2 C<name>
  
  Alias for the L</database> method.
  
  =head2 C<hostname>
  
  Returns the host name for the database connection.
  
  =head2 C<host>
  
  Alias for the L</hostname> method.
  
  =head2 C<password>
  
  Returns the password for the database connection.
  
  =head2 C<pass>
  
  Alias for the L</password> method.
  
  =head2 C<port>
  
  Returns the port for the database connection.
  
  =head2 C<username>
  
  Returns the user name for the database connection.
  
  =head2 C<user>
  
  Alias for the L</username> method.
  
  =head2 C<command_line>
  
  Returns command line options suitable for passing to the F<mysql>
  or F<mysqldump> command line programs to connect to the database.
  
  =head2 C<connect>
  
  Connects to the database if it hasn't been connected to yet and
  returns the database handle for the connection.
  
  =head2 C<open>
  
  Alias for the L</connect> method.
  
  =head2 C<dbh>
  
  Returns the database handle of the current connection, or C<undef> if
  there is no active connection.
  
  =head2 C<disconnect>
  
  Closes the active database connection.  If there is no active
  connection, does nothing.
  
  =head2 C<close>
  
  Alias for the L</disconnect> method.
  
  =head2 C<clear>
  
  Removes all tables from the database, leaving it empty.
  
  =head2 C<dump ( $dumpfile )>
  
  Dumps the database content to the specified file.
  
  =head2 C<load ( $dumpfile )>
  
  Loads the specified database script into the database.
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_DATABASE

$fatpacked{"WGDev/File.pm"} = <<'WGDEV_FILE';
  package WGDev::File;
  {
    $WGDev::File::VERSION = '0.1207160';
  }
  # ABSTRACT: File utility functions
  use strict;
  use warnings;
  use 5.008008;
  
  use constant STAT_FILESIZE => 7;
  
  use WGDev::X;
  use File::Spec ();
  
  sub sync_dirs {
      my $class = shift;
      my $from_dir = shift;
      my $to_dir = shift;
  
      require File::Copy;
      require File::Path;
  
      File::Path::mkpath($to_dir);
  
      # recurse through destination and delete files that don't exist in source
      $class->matched_find($to_dir, $from_dir, sub {
          my ($to_path, $from_path) = @_;
          return
              if -e $from_path;
          if ( -d $to_path ) {
              File::Path::rmtree($to_path);
          }
          else {
              unlink $to_path;
          }
      });
  
      # copy files that don't exist or are different
      $class->matched_find($from_dir, $to_dir, sub {
          my ($from_path, $to_path) = @_;
          return
              if -d $from_path;
          my $from_size = ( stat _ )[STAT_FILESIZE];
          return
              if -e $to_path && ( stat _ )[STAT_FILESIZE] == $from_size;
  
          my $to_parent = File::Spec->catpath(
              ( File::Spec->splitpath($to_path) )[ 0, 1 ] );
          File::Path::mkpath($to_parent);
          File::Copy::copy( $from_path, $to_path );
      });
  }
  
  sub matched_find {
      my $class = shift;
      my $from_dir = shift;
      my $to_dir = shift;
      my $cb = shift;
  
      require File::Find;
  
      my $matched_cb = sub {
          no warnings 'once';
          my $from_path = $File::Find::name;
          my ( undef, undef, $filename ) = File::Spec->splitpath($from_path);
          if ( $filename eq '.svn' || $filename eq 'temp' ) {
              $File::Find::prune = 1;
              return;
          }
          my $rel_path = File::Spec->abs2rel( $from_path, $from_dir );
          my $to_path = File::Spec->rel2abs( $rel_path, $to_dir );
          $cb->($from_path, $to_path);
      };
      File::Find::find( { no_chdir => 1, wanted => $matched_cb }, $from_dir );
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::File - File utility functions
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      WGDev::File->sync_dirs($from, $to);
  
  =head1 DESCRIPTION
  
  Performs common actions on files.
  
  =head1 METHODS
  
  =head2 C<sync_dirs ( $from_dir, $to_dir )>
  
  Synchronizes two directories.  Deletes any additional files in the
  destination that don't exist in the source.  Checks for file
  differences by size before copying.
  
  =head2 C<matched_find ( $from_dir, $to_dir, $callback )>
  
  Recurses through C<$from_dir>, calling C<$callback> for each file
  or directory found.  The callback is passed two parameters, the
  file found, and a file name relative to C<$to_dir> based on the found
  file's path relative to C<$from_dir>.
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_FILE

$fatpacked{"WGDev/Help.pm"} = <<'WGDEV_HELP';
  package WGDev::Help;
  {
    $WGDev::Help::VERSION = '0.1207160';
  }
  # ABSTRACT: Generate help text for WGDev
  use strict;
  use warnings;
  use 5.008008;
  
  use WGDev::X   ();
  use File::Spec ();
  use Try::Tiny;
  
  sub package_usage {
      my $package   = shift;
      my $verbosity = shift;
      require WGDev::Pod::Usage;
      if ( !defined $verbosity ) {
          $verbosity = 1;
      }
      my $parser = WGDev::Pod::Usage->new;
      $parser->verbosity($verbosity);
      my $pod = package_pod($package);
      return $parser->parse_from_string($pod);
  }
  
  sub package_perldoc {
      my $package  = shift;
      my $sections = shift;
      require Pod::Perldoc;
      require File::Temp;
      File::Temp->VERSION(0.19); ##no critic (ProhibitMagicNumbers)
      require File::Path;
      my $pod = package_pod( $package, $sections );
  
      my $pid = fork;
      if ( !$pid ) {
          # perldoc will try to drop privs anyway, so do it ourselves so the
          # temp file has the correct owner
          Pod::Perldoc->new->drop_privs_maybe;
  
          # Make a path that plays nice with Perldoc internals.  Will format nicer.
          my $tmpdir = File::Temp->newdir( TMPDIR => 1 );
  
          # construct a path that Perldoc will interperet as a package name
          my @path_parts = split /::/msx, $package;
          my $filename   = pop @path_parts;
          my $path       = File::Spec->catdir( $tmpdir->dirname, 'perl', @path_parts );
          File::Path::mkpath($path);
          my $out_file = File::Spec->catfile( $path, $filename );
  
          open my $out, '>', $out_file
              or WGDev::X::IO->throw('Unable to create temp file');
          print {$out} $pod;
          close $out or return q{};
  
          # perldoc doesn't understand darwin's stty output.
          # copy and paste but it seems to work
          my @extra_args;
          if ($^O eq 'darwin') {
              ##no critic (ProhibitBacktick ProhibitMagicNumbers)
              if (`stty -a` =~ /(\d+)[ ]columns;/msx) {
                  my $cols = $1;
                  my $c = $cols * 39 / 40;
                  $cols = $c > $cols - 2 ? $c : $cols -2;
                  if ( $cols > 80 ) {
                      push @extra_args, '-n', 'nroff -rLL=' . (int $c) . 'n';
                  }
              }
          }
  
          local @ARGV = ( @extra_args, '-w', 'section:3', '-F', $out_file );
          exit Pod::Perldoc->run;
      }
      waitpid $pid, 0;
  
      # error status of subprocess
      if ($?) {
          WGDev::X->throw('Error displaying help!');
      }
      return;
  }
  
  my %pod;
  
  sub package_pod {
      my $package  = shift;
      my $sections = shift;
      my $raw_pod = $pod{$package};
      if ( !$raw_pod ) {
          $raw_pod = read_lib($package);
          $pod{$package} = $raw_pod;
      }
  
      return $raw_pod
          if !$sections;
  
      open my $pod_in, '<', \$raw_pod
          or WGDev::X::IO->throw;
      my @sections = ref $sections ? @{$sections} : $sections;
      require Pod::Select;
      my $parser = Pod::Select->new;
      $parser->select(@sections);
      my $pod = q{};
      open my $pod_out, '>', \$pod
          or WGDev::X::IO->throw;
      $parser->parse_from_filehandle( $pod_in, $pod_out );
      close $pod_out
          or WGDev::X::IO->throw;
      close $pod_in
          or WGDev::X::IO->throw;
      return $pod;
  }
  
  sub read_lib {
      my $module = shift;
      if ($module =~ /\A\w+(?:::\w+)*\z/msx) {
          $module .= '.pm';
          $module =~ s{::}{/}msxg;
      }
      my $data;
      if ($INC{$module}) {
          $data = _read_file($module, $INC{$module});
      }
      else {
          for my $inc (@INC) {
              if (! ref $inc) {
                  my $filename = $inc . q{/} . $module;
                  if (-f $filename) {
                      $data = _read_file($module, $filename);
                  }
              }
              else {
                  $data = _read_file($module, $inc);
              }
              last
                  if defined $data;
          }
      }
      return $data;
  }
  
  sub _read_file {
      my ($module, $inc) = @_;
      my ($fh, $cb, $state);
      ##no critic (ProhibitCascadingIfElse)
      if (! ref $inc) {
          open $fh, '<', $inc
              or return;
      }
      elsif (ref $inc eq 'CODE') {
          ($fh, $cb, $state) = $inc->($inc, $module);
      }
      elsif (ref $inc eq 'ARRAY') {
          ($fh, $cb, $state) = $inc->[0]->($inc, $module);
      }
      elsif ($inc->can('INC')) {
          ($fh, $cb, $state) = $inc->INC($module);
      }
      my $data;
      if ($cb || $fh) {
          local $_;
          $data = q{};
          while (1) {
              last
                  if ($fh && !defined ($_ = <$fh>));
              last
                  if ($cb && !$cb->($cb, $state));
              $data .= $_;
          }
          if ($fh) {
              close $fh
                  or WGDev::X::IO->throw;
          }
      }
      return $data;
  }
  
  1;
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Help - Generate help text for WGDev
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      use WGDev::Help;
  
      my $usage = WGDev::Help::package_usage( 'My::Class' );
  
  =head1 DESCRIPTION
  
  Reads help information from modules but filters to only pick relevant
  sections when multiple POD documents exist in a single file.
  
  =head1 FUNCTIONS
  
  =head2 C<package_usage ( $package [, $verbosity] )>
  
  Returns usage information for a package, using L<Pod::Usage>.  Can be used on
  packages that have been combined into a single file.
  
  =head2 C<package_perldoc ( $package [, $sections] )>
  
  Displays documentation for a package using L<Pod::Perldoc>.  Can be used on
  packages that have been combined into a single file.
  
  =over 4
  
  =item C<$sections>
  
  Passed on to L</package_pod> to limit the sections output.
  
  =back
  
  =head2 C<package_pod ( $package [, $sections] )>
  
  Filters out the POD for a specific package from the module file for the package.
  
  =over 4
  
  =item C<$sections>
  
  =back
  
  Limits sections to include based on L<Pod::Select/SECTION SPECIFICATIONS|Pod::Select's rules>.
  Can be either a scalar value or an array reference.
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_HELP

$fatpacked{"WGDev/Pod/Usage.pm"} = <<'WGDEV_POD_USAGE';
  package WGDev::Pod::Usage;
  {
    $WGDev::Pod::Usage::VERSION = '0.1207160';
  }
  # ABSTRACT: Produce usage documentation for WGDev commands
  use strict;
  use warnings;
  use 5.008008;
  
  use constant OPTION_INDENT      => 4;
  use constant OPTION_TEXT_INDENT => 24;
  
  use parent qw(Pod::PlainText Pod::Select);
  
  use WGDev::X ();
  
  sub new {
      my $proto = shift;
  
      my $self = $proto->SUPER::new( indent => 0 );
      $self->verbosity(1);
      return $self;
  }
  
  sub verbosity {
      my $self      = shift;
      my $verbosity = shift;
      if ($verbosity) {
          $self->select(qw(NAME SYNOPSIS OPTIONS/!.+));
      }
      else {
          $self->select(qw(NAME SYNOPSIS));
      }
      return;
  }
  
  sub command {
      my $self    = shift;
      my $command = shift;
      $self->{_last_command} = $command;
      return $self->SUPER::command( $command, @_ );
  }
  
  sub cmd_head1 {
      my $self = shift;
      my $head = shift;
      my $para = shift;
      $head =~ s/\s+$//msx;
      $self->{_last_head1} = $head;
      if ( $head eq 'NAME' ) {
          return;
      }
      elsif ( $head eq 'SYNOPSIS' ) {
          $head = 'USAGE';
      }
      $head = lc $head;
      $head =~ s/\b(.)/uc($1)/msxe;
      $head .= q{:};
      my $output = $self->interpolate( $head, $para );
      $self->output( $output . "\n" );
      return;
  }
  
  sub textblock {
      my $self = shift;
      my $text = shift;
      my $para = shift;
      if ( $self->{_last_head1} eq 'NAME' ) {
          $text =~ s/^[\w:]+\Q - //msx;
      }
      if ( $self->{_last_command} eq 'item' && !$self->{ITEM} ) {
          return;
      }
      return $self->SUPER::textblock( $text, $para );
  }
  
  sub verbatim {
      my $self = shift;
      if ( $self->{_last_command} eq 'item' && !$self->{ITEM} ) {
          return;
      }
      return $self->SUPER::verbatim(@_);
  }
  
  sub item {
      my $self   = shift;
      my $item   = shift;
      my $tag    = delete $self->{ITEM};
      my $margin = $self->{MARGIN};
      local $self->{MARGIN} = 0;    ## no critic (ProhibitLocalVars)
  
      $tag = $self->reformat($tag);
      $tag =~ s/\n*\z//msx;
  
      $item =~ s/[.].*//msx;
      {
          ## no critic (ProhibitLocalVars)
          local $self->{width} = $self->{width} - OPTION_TEXT_INDENT;
          $item = $self->reformat($item);
      }
      $item =~ s/\n*\z//msx;
      my $option_indent_string = q{ } x OPTION_TEXT_INDENT;
      $item =~ s/\n/\n$option_indent_string/msxg;
  
      my $indent_string = q{ } x OPTION_INDENT;
      if ( $item eq q{} ) {
          $self->output( $indent_string . $tag . "\n" );
      }
      else {
          my $option_name_length = OPTION_TEXT_INDENT - OPTION_INDENT - 1;
          $self->output( $indent_string . sprintf "%-*s %s\n",
              $option_name_length, $tag, $item );
      }
      return;
  }
  
  sub seq_c {
      return $_[1];
  }
  
  sub parse_from_string {
      my $self   = shift;
      my $pod    = shift;
      my $output = q{};
      open my $out_fh, '>', \$output
          or WGDev::X::IO->throw;
      open my $in_fh, '<', \$pod
          or WGDev::X::IO->throw;
      $self->parse_from_filehandle( $in_fh, $out_fh );
      close $in_fh
          or WGDev::X::IO->throw;
      close $out_fh
          or WGDev::X::IO->throw;
      return $output;
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Pod::Usage - Produce usage documentation for WGDev commands
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      use WGDev::Pod::Usage;
      my $parser = WGDev::Pod::Usage->new;
      my $usage = $parser->parse_from_string($pod);
  
  =head1 DESCRIPTION
  
  Formats POD documentation into a format suitable for showing as
  usage text.  WGDev::Pod::Usage is a subclass of L<Pod::Select>.
  
  =for Pod::Coverage cmd_head1
      new
      parse_from_string
      seq_c
      textblock
      verbosity
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_POD_USAGE

$fatpacked{"WGDev/Version.pm"} = <<'WGDEV_VERSION';
  package WGDev::Version;
  {
    $WGDev::Version::VERSION = '0.1207160';
  }
  # ABSTRACT: Extract version information from WebGUI
  use strict;
  use warnings;
  use 5.008008;
  
  use File::Spec;
  use WGDev::X ();
  
  sub new {
      my $class = shift;
      my $dir   = shift || WGDev::X::NoWebGUIRoot->throw;
      my $self  = bless \$dir, $class;
      return $self;
  }
  
  sub pm { goto &module }
  
  sub module {
      my $dir = ${ +shift };
      my $version;
      my $status;
      open my $fh, '<', File::Spec->catfile( $dir, 'lib', 'WebGUI.pm' )
          or WGDev::X::IO::Read->throw( path => 'WebGUI.pm' );
      while ( my $line = <$fh> ) {
          ##no critic (ProhibitStringyEval)
          if ( $line =~ /\$VERSION\s*=(.*)$/msx ) {
              $version = eval $1;
          }
          if ( $line =~ /\$STATUS\s*=(.*)$/msx ) {
              $status = eval $1;
          }
          last
              if $version && $status;
      }
      close $fh
          or WGDev::X::IO::Read->throw( path => 'WebGUI.pm' );
      return wantarray ? ( $version, $status ) : $version;
  }
  
  sub db_script { goto &database_script }
  
  sub database_script {
      my $self = shift;
      my $dir = ${ $self };
      my $wg8 = $self->module =~ /^8[.]/msx;
      my $version;
      my $db_file = $wg8 ? do {
          require WebGUI::Paths;
          WebGUI::Paths->defaultCreateSQL;
      } : File::Spec->catfile( $dir, 'docs', 'create.sql' );
      open my $fh, '<', $db_file
          or WGDev::X::IO::Read->throw( path => $db_file );
      while ( my $line = <$fh> ) {
          if (
              ##no critic (ProhibitComplexRegexes);
              $line =~ m{
                  (?:(?i)\QINSERT INTO\E) \s+
                  (`?)webguiVersion\1     \s+
                  .+?                     \s+?
                  (?i)VALUES              \s+
                  \Q('\E ( [^']+ )        [']
              }msx
          ) {
              $version = $2;
              last;
          }
      }
      close $fh
          or WGDev::X::IO::Read->throw( path => $db_file );
      return $version;
  }
  
  sub db { goto &database }
  
  sub database {
      my $dir = ${ +shift };
      my $dbh = shift;
      require version;
      my $sth = $dbh->prepare('SELECT webguiVersion FROM webguiVersion');
      $sth->execute;
      my @versions = map { $_->[0] }
          sort { $a->[1] <=> $b->[1] }
          map { [ $_, version->new($_) ] }
          map { @{$_} } @{ $sth->fetchall_arrayref( [0] ) };
      $sth->finish;
      my $version = pop @versions;
      return $version;
  }
  
  sub changelog {
      my $dir = ${ +shift };
      require version;
      my @changelogs;
      opendir my $dh, File::Spec->catdir( $dir, 'docs', 'changelog' )
          or WGDev::X::IO::Read->throw( path => 'docs/changelog' );
      while ( my $file = readdir $dh ) {
          if ( $file =~ /^( [x\d]+ [.] [x\d]+ [.] [x\d]+ ) \Q.txt\E $/msx ) {
              ( my $v = $1 ) =~ tr/x/0/;
              push @changelogs, [ $file, version->new($v) ];
          }
      }
      closedir $dh
          or WGDev::X::IO::Read->throw( path => 'docs/changelog' );
      @changelogs = sort { $a->[1] <=> $b->[1] } @changelogs;
      my $latest = pop @changelogs;
      open my $fh, '<',
          File::Spec->catfile( $dir, 'docs', 'changelog', $latest->[0] )
          or WGDev::X::IO::Read->throw( path => "docs/changelog/$latest->[0]" );
      while ( my $line = <$fh> ) {
          if ( $line =~ /^(\d+[.]\d+[.]\d+)$/msx ) {
              $latest->[1] = $1;
              last;
          }
      }
      close $fh
          or WGDev::X::IO::Read->throw( path => "docs/changelog/$latest->[0]" );
      return @{$latest};
  }
  
  # returns ($upgrade_file, $from_version, $to_version, $to_version_file)
  sub upgrade {
      my $dir = ${ +shift };
      require version;
      my @upgrades;
      opendir my $dh, File::Spec->catdir( $dir, 'docs', 'upgrades' )
          or WGDev::X::IO::Read->throw( path => 'docs/upgrades' );
      while ( my $file = readdir $dh ) {
          if ( $file =~ /^upgrade_ ([.\d]+) - ([.\d]+) \Q.pl\E$/msx ) {
              push @upgrades, [ $file, version->new($1), version->new($2) ];
          }
      }
      closedir $dh
          or WGDev::X::IO::Read->throw( path => 'docs/upgrades' );
      @upgrades = sort { $a->[2] <=> $b->[2] } @upgrades;
      my $latest = pop @upgrades;
      open my $fh, '<',
          File::Spec->catfile( $dir, 'docs', 'upgrades', $latest->[0] )
          or
          WGDev::X::IO::Read->throw( path => 'docs/upgrades/' . $latest->[0] );
      while ( my $line = <$fh> ) {
          if ( $line =~ /\$toVersion\s*=(.*)$/msx ) {
              ##no critic (ProhibitStringyEval RequireCheckingReturnValueOfEval)
              push @{$latest}, eval $1;
              last;
          }
      }
      close $fh
          or
          WGDev::X::IO::Read->throw( path => 'docs/upgrades/' . $latest->[0] );
      return @{$latest};
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::Version - Extract version information from WebGUI
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      my $wgv = WGDev::Version->new('/data/WebGUI');
      print "You have WebGUI " . $wgv->module . "\n";
  
  =head1 DESCRIPTION
  
  Extracts version information from various places in WebGUI: the change log,
  the upgrade script, the WebGUI module, the database creation script, or a
  live database.
  
  =head1 METHODS
  
  =head2 C<new ( $webgui_root )>
  
  Creates a new WGDev::Version object.  Needs a WebGUI directory to be specified.
  
  =over 4
  
  =item C<$webgui_root>
  
  The root of the WebGUI directory to use for finding each file.
  
  =back
  
  =head2 C<module>
  
  In scalar context, returns the version number from the F<lib/WebGUI.pm>
  module.  In array context, returns the version number and the status
  (beta/stable).
  
  =head2 C<pm>
  
  An alias for the L</module> method.
  
  =head2 C<changelog>
  
  Returns the most recent version number noted in the change log.
  
  =head2 C<upgrade>
  
      my ($upgrade_file, $from_version, $to_version, $to_version_file) = $wgv->upgrade;
  
  Finds the most recent upgrade script and returns an array of
  information about it.  The array contains the script's file name,
  the version number it will upgrade from and to based on its file name,
  and the version it will upgrade to noted in the script itself.
  
  =head2 C<database_script>
  
  Returns the version noted in the F<create.sql> database script.
  
  =head2 C<db_script>
  
  An alias for the L</database_script> method.
  
  =head2 C<database ( $dbh )>
  
  Accepts a database handle, and returns the latest version from the
  C<webguiVersion> table.
  
  =head2 C<db>
  
  An alias for the L</database> method.
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_VERSION

$fatpacked{"WGDev/X.pm"} = <<'WGDEV_X';
  package WGDev::X;
  {
    $WGDev::X::VERSION = '0.1207160';
  }
  # ABSTRACT: WGDev Exceptions
  use strict;
  use warnings;
  use 5.008008;
  
  use Exception::Class (
      'WGDev::X'              => { description => 'A general WGDev error', },
      'WGDev::X::CommandLine' => {
          isa         => 'WGDev::X',
          description => 'An error with the command line.',
          fields      => ['usage'],
      },
      'WGDev::X::CommandLine::BadCommand' => {
          isa         => 'WGDev::X::CommandLine',
          description => 'An invalid command was requested.',
          fields      => ['command_name'],
      },
      'WGDev::X::BadCommand' => {
          isa         => 'WGDev::X',
          description => 'An invalid command was requested.',
          fields      => ['command_name'],
      },
      'WGDev::X::CommandLine::BadParams' => {
          isa         => 'WGDev::X::CommandLine',
          description => 'Invalid parameters were passed to a command.',
      },
      'WGDev::X::System' => {
          isa         => 'WGDev::X',
          description => 'System error',
          fields      => ['errno_string'],
      },
      'WGDev::X::IO' => {
          isa         => 'WGDev::X::System',
          description => 'IO error',
          fields      => ['path'],
      },
      'WGDev::X::IO::Read' => {
          isa         => 'WGDev::X::IO',
          description => 'Read error',
      },
      'WGDev::X::IO::Write' => {
          isa         => 'WGDev::X::IO',
          description => 'Write error',
      },
      'WGDev::X::NoWebGUIConfig' => {
          isa         => 'WGDev::X',
          description => 'No WebGUI config file available.',
      },
      'WGDev::X::NoWebGUIRoot' => {
          isa         => 'WGDev::X',
          description => 'No WebGUI root directory available.',
      },
      'WGDev::X::BadParameter' => {
          isa         => 'WGDev::X',
          description => 'Bad parameter provided.',
          fields      => [ 'parameter', 'value' ],
      },
      'WGDev::X::AssetNotFound' => {
          isa         => 'WGDev::X',
          description => 'Specified asset not found',
          fields      => ['asset'],
      },
      'WGDev::X::BadAssetClass' => {
          isa         => 'WGDev::X',
          description => 'Bad asset class specified',
          fields      => ['class'],
      },
      'WGDev::X::UserNotFound' => {
          isa         => 'WGDev::X',
          description => 'Specified user not found',
          fields      => ['userId'],
      },
      'WGDev::X::Module' => {
          isa         => 'WGDev::X',
          description => 'Error loading module',
          fields      => ['module', 'using_module'],
      },
      'WGDev::X::Module::Find' => {
          isa         => 'WGDev::X::Module',
          description => q{Can't find module},
      },
      'WGDev::X::Module::Parse' => {
          isa         => 'WGDev::X::Module',
          description => q{Error compiling module},
      },
      'WGDev::X::BadPackage' => {
          isa         => 'WGDev::X',
          description => q{Error importing a package},
          fields      => ['message', 'package'],
      },
  );
  
  BEGIN {
      if ( $ENV{WGDEV_DEBUG} ) {
          WGDev::X->Trace(1);
      }
      ##no critic (ProhibitMagicNumbers)
      if ( !eval { Exception::Class->VERSION(1.27) } ) {
  
          # work around bad behavior of Exception::Class < 1.27
          # where it defaults the message to $!
          no warnings 'once';
          *WGDev::X::new = sub {
              my $errno = qq{$!};
              my $class = shift;
              my $self  = $class->SUPER::new(@_);
              if ( $self->{message} eq $errno ) {
                  $self->{message} = q{};
              }
              return $self;
          };
      }
  }
  
  ##no critic (ProhibitQualifiedSubDeclarations)
  
  sub _format_file_as_module {
      my $file = shift;
      if ($file =~ s/[.]pm$//msx) {
          $file =~ s{/}{::}msxg;
      }
      return $file;
  }
  
  sub WGDev::X::inflate {
      my $class = shift;
      if (@_ == 1 && ref $_[0] && $_[0]->can('throw')) {
          $_[0]->throw;
      }
      if (@_ == 1 && !ref $_[0]) {
          my $e = shift;
          ##no critic (ProhibitComplexRegexes);
          if ($e =~ m{
              \ACan't[ ]locate[ ](.*?)[ ]in[ ][@]INC[ ]
              .*[ ]at[ ](.*?)[ ]line[ ]\d+[.]
          }msx) {
              my $module = $1;
              my $using_module = $2;
              $module = _format_file_as_module($module);
              $using_module = _format_file_as_module($using_module);
              WGDev::X::Module::Find->throw(message => $e, module => $module, using_module => $using_module);
          }
          elsif ( $e =~ s{
              (at[ ](.*?)[.]pm[ ]line[ ]\d+[.])
              \s+Compilation[ ]failed[ ]in[ ]require[ ]at[ ]
              (.*?)[ ]line[ ]\d+[.].*?\z
          }{$1}msx ) {
              my $module = $2;
              my $using_module = $3;
              $module = _format_file_as_module($module);
              $using_module = _format_file_as_module($using_module);
              WGDev::X::Module::Parse->throw(message => $e, module => $module, using_module => $using_module);
          }
      }
      $class->throw(@_);
  }
  
  sub WGDev::X::full_message {
      my $self = shift;
      return $self->message || $self->description;
  }
  
  sub WGDev::X::CommandLine::full_message {
      my $self    = shift;
      my $message = $self->message;
      if ( defined $self->usage ) {
          if ($message) {
              $message =~ s/[\n\r]*\z/\n\n/msx;
          }
          $message .= $self->usage;
      }
      $message =~ s/[\n\r]*\z/\n\n/msx;
      return $message;
  }
  
  sub WGDev::X::BadParameter::full_message {
      my $self = shift;
      my $message = $self->SUPER::message || $self->description;
      if ( $self->parameter ) {
          $message .= q{ } . $self->parameter;
      }
      if ( $self->value ) {
          $message .= q{: } . $self->value;
      }
      return $message;
  }
  
  sub WGDev::X::CommandLine::BadCommand::full_message {
      my $self = shift;
      my $message
          = defined $self->command_name
          ? "Can't find command @{[ $self->command_name ]}!\n"
          : "No command specified!\n";
      if ( defined $self->usage ) {
          $message .= "\n" . $self->usage;
      }
      $message =~ s/[\n\r]*\z/\n\n/msx;
      $message
          .= "Try the running 'wgd commands' for a list of available commands.\n\n";
      return $message;
  }
  
  sub WGDev::X::System::new {
      my $class        = shift;
      my $errno_string = qq{$!};
      my $self         = $class->SUPER::new(@_);
      if ( !defined $self->errno_string ) {
          $self->{errno_string} = $errno_string;
      }
      return $self;
  }
  
  sub WGDev::X::System::full_message {
      my $self    = shift;
      my $message = $self->SUPER::full_message;
      $message .= ' - ' . $self->errno_string;
      return $message;
  }
  
  sub WGDev::X::IO::full_message {
      my $self = shift;
      my $message = $self->SUPER::message || $self->description;
      if ( $self->path ) {
          $message .= ' Path: ' . $self->path;
      }
      $message .= ' - ' . $self->errno_string;
      return $message;
  }
  
  sub WGDev::X::AssetNotFound::full_message {
      my $self = shift;
      my $message = $self->SUPER::full_message;
      if ( $self->asset ) {
          $message .= ' - ' . $self->asset;
      }
      return $message;
  }
  
  sub WGDev::X::UserNotFound::full_message {
      my $self = shift;
      my $message = $self->SUPER::full_message;
      if ( $self->userId ) {
          $message .= ' FATAL ERROR: User not found - ' . $self->userId;
      }
      return $message;
  }
  
  sub WGDev::X::Module::full_message {
      my $self = shift;
      my $message = $self->description . q{ } . $self->module
          . q{ for } . $self->using_module . ":\n" . $self->SUPER::message;
      $message =~ s/[\n\r]*\z/\n\n/msx;
      return $message;
  }
  
  sub WGDev::X::Module::Find::full_message {
      my $self = shift;
      my $message = $self->description . q{ } . $self->module
          . q{ for } . $self->using_module;
      $message =~ s/[\n\r]*\z/\n\n/msx;
      return $message;
  }
  
  1;
  
  
  
  __END__
  =pod
  
  =head1 NAME
  
  WGDev::X - WGDev Exceptions
  
  =head1 VERSION
  
  version 0.1207160
  
  =head1 SYNOPSIS
  
      use WGDev::X;
      WGDev::X->throw();
  
  =head1 DESCRIPTION
  
  Exceptions for WGDev
  
  =head1 AUTHOR
  
  Graham Knop <haarg@haarg.org>
  
  =head1 COPYRIGHT AND LICENSE
  
  This software is copyright (c) 2012 by Graham Knop.
  
  This is free software; you can redistribute it and/or modify it under
  the same terms as the Perl 5 programming language system itself.
  
  =cut
  
WGDEV_X

s/^  //mg for values %fatpacked;

unshift @INC, sub {
  if (my $fat = $fatpacked{$_[1]}) {
    open my $fh, '<', \$fat
      or die "FatPacker error loading $_[1] (could be a perl installation issue?)";
    return $fh;
  }
  return
};

} # END OF FATPACK CODE

#!perl
package
    App::WGDev;
# ABSTRACT: WebGUI Developer Utilities
use strict;
use warnings;
use 5.008008;

use WGDev::Command;

our $PACKED = 1;
our @PACKED = qw(
WGDev.pm
WGDev/Asset.pm
WGDev/Command.pm
WGDev/Database.pm
WGDev/File.pm
WGDev/Help.pm
WGDev/Version.pm
WGDev/X.pm
WGDev/Command/Base.pm
WGDev/Command/Batchedit.pm
WGDev/Command/Build.pm
WGDev/Command/Commands.pm
WGDev/Command/Config.pm
WGDev/Command/Db.pm
WGDev/Command/Dist.pm
WGDev/Command/Edit.pm
WGDev/Command/Export.pm
WGDev/Command/Group.pm
WGDev/Command/Guid.pm
WGDev/Command/Help.pm
WGDev/Command/Import.pm
WGDev/Command/Intro.pm
WGDev/Command/Ls.pm
WGDev/Command/Mail.pm
WGDev/Command/Optimize.pm
WGDev/Command/Package.pm
WGDev/Command/Reset.pm
WGDev/Command/Run.pm
WGDev/Command/Setting.pm
WGDev/Command/Test.pm
WGDev/Command/Trash.pm
WGDev/Command/User.pm
WGDev/Command/Util.pm
WGDev/Command/Version.pm
WGDev/Command/Base/Verbosity.pm
WGDev/Command/Export/Branch.pm
WGDev/Command/For/Each.pm
WGDev/Command/Self/Upgrade.pm
WGDev/Pod/Usage.pm
);

my $return = eval { WGDev::Command->run(@ARGV) };
if ( my $message = $@ ) {
    $message =~ s/\n?\z/\n/msx;
    die $message;
}
exit( $return ? 0 : 1 );

__END__
=pod

=head1 NAME

App::WGDev - WebGUI Developer Utilities

=head1 VERSION

version 0.1207160

=head1 AUTHOR

Graham Knop <haarg@haarg.org>

=head1 COPYRIGHT AND LICENSE

This software is copyright (c) 2012 by Graham Knop.

This is free software; you can redistribute it and/or modify it under
the same terms as the Perl 5 programming language system itself.

=cut


