From b165a1a42da8614ffc583069e10b367cc786233b Mon Sep 17 00:00:00 2001 From: Frank Brehm Date: Tue, 18 Mar 2008 10:04:24 +0000 Subject: [PATCH] Grundstruktur importiert --- Changes | 4 + Makefile.PL | 16 + README | 1 + frbr_books.yml | 30 ++ lib/FrBr/Books.pm | 120 +++++ lib/FrBr/Books/Controller/Books.pm | 148 ++++++ lib/FrBr/Books/Controller/Root.pm | 64 +++ lib/FrBr/Books/Db.pm | 19 + lib/FrBr/Books/Db/Autor2buch.pm | 28 ++ lib/FrBr/Books/Db/Autoren.pm | 27 ++ lib/FrBr/Books/Db/Buch2kategorie.pm | 27 ++ lib/FrBr/Books/Db/Buch2serie.pm | 28 ++ lib/FrBr/Books/Db/Buecher.pm | 36 ++ lib/FrBr/Books/Db/Kategorien.pm | 26 ++ lib/FrBr/Books/Db/Serien.pm | 27 ++ lib/FrBr/Books/Db/Session.pm | 26 ++ lib/FrBr/Books/Db/Users.pm | 36 ++ lib/FrBr/Books/Db/Verlage.pm | 27 ++ lib/FrBr/Books/Db/Waehrungen.pm | 28 ++ lib/FrBr/Books/Model/Schema.pm | 44 ++ lib/FrBr/Books/Plugin/ConfigLoader.pm | 102 +++++ lib/FrBr/Books/View/TtDefault.pm | 47 ++ lib/FrBr/Common.pm | 432 ++++++++++++++++++ root/lib/config/colors.tt2 | 84 ++++ root/lib/config/main.tt2 | 26 ++ root/lib/config/url.tt2 | 18 + root/lib/site/footer.tt2 | 7 + root/lib/site/header.tt2 | 8 + root/lib/site/html.tt2 | 27 ++ root/lib/site/jscript.tt2 | 14 + root/lib/site/jsfiles.tt2 | 11 + root/lib/site/layout.tt2 | 15 + root/lib/site/statusbar.tt2 | 9 + root/lib/site/styles.css | 173 +++++++ root/lib/site/wrapper.tt2 | 17 + root/src/books/list.tt2 | 32 ++ root/src/books/styles.css | 10 + root/src/error.tt2 | 14 + root/src/welcome.tt2 | 15 + root/static/images/btn_120x50_built.png | Bin 0 -> 3826 bytes .../static/images/btn_120x50_built_shadow.png | Bin 0 -> 3681 bytes root/static/images/btn_120x50_powered.png | Bin 0 -> 3862 bytes .../images/btn_120x50_powered_shadow.png | Bin 0 -> 3673 bytes root/static/images/btn_88x31_built.png | Bin 0 -> 2517 bytes root/static/images/btn_88x31_built_shadow.png | Bin 0 -> 2274 bytes root/static/images/btn_88x31_powered.png | Bin 0 -> 2542 bytes .../images/btn_88x31_powered_shadow.png | Bin 0 -> 2304 bytes root/static/images/catalyst_logo.png | Bin 0 -> 13710 bytes root/static/pic/fbr.ico | Bin 0 -> 626 bytes script/frbr_books_cgi.pl | 37 ++ script/frbr_books_create.pl | 75 +++ script/frbr_books_fastcgi.pl | 80 ++++ script/frbr_books_server.pl | 111 +++++ script/frbr_books_test.pl | 54 +++ t/01app.t | 7 + t/02pod.t | 9 + t/03podcoverage.t | 9 + t/controller_Books.t | 10 + t/model_Schema.t | 6 + t/view_TtDefault.t | 6 + 60 files changed, 2227 insertions(+) create mode 100644 Changes create mode 100644 Makefile.PL create mode 100644 README create mode 100644 frbr_books.yml create mode 100644 lib/FrBr/Books.pm create mode 100644 lib/FrBr/Books/Controller/Books.pm create mode 100644 lib/FrBr/Books/Controller/Root.pm create mode 100644 lib/FrBr/Books/Db.pm create mode 100644 lib/FrBr/Books/Db/Autor2buch.pm create mode 100644 lib/FrBr/Books/Db/Autoren.pm create mode 100644 lib/FrBr/Books/Db/Buch2kategorie.pm create mode 100644 lib/FrBr/Books/Db/Buch2serie.pm create mode 100644 lib/FrBr/Books/Db/Buecher.pm create mode 100644 lib/FrBr/Books/Db/Kategorien.pm create mode 100644 lib/FrBr/Books/Db/Serien.pm create mode 100644 lib/FrBr/Books/Db/Session.pm create mode 100644 lib/FrBr/Books/Db/Users.pm create mode 100644 lib/FrBr/Books/Db/Verlage.pm create mode 100644 lib/FrBr/Books/Db/Waehrungen.pm create mode 100644 lib/FrBr/Books/Model/Schema.pm create mode 100644 lib/FrBr/Books/Plugin/ConfigLoader.pm create mode 100644 lib/FrBr/Books/View/TtDefault.pm create mode 100644 lib/FrBr/Common.pm create mode 100644 root/lib/config/colors.tt2 create mode 100644 root/lib/config/main.tt2 create mode 100644 root/lib/config/url.tt2 create mode 100644 root/lib/site/footer.tt2 create mode 100644 root/lib/site/header.tt2 create mode 100644 root/lib/site/html.tt2 create mode 100644 root/lib/site/jscript.tt2 create mode 100644 root/lib/site/jsfiles.tt2 create mode 100644 root/lib/site/layout.tt2 create mode 100644 root/lib/site/statusbar.tt2 create mode 100644 root/lib/site/styles.css create mode 100644 root/lib/site/wrapper.tt2 create mode 100644 root/src/books/list.tt2 create mode 100644 root/src/books/styles.css create mode 100644 root/src/error.tt2 create mode 100644 root/src/welcome.tt2 create mode 100644 root/static/images/btn_120x50_built.png create mode 100644 root/static/images/btn_120x50_built_shadow.png create mode 100644 root/static/images/btn_120x50_powered.png create mode 100644 root/static/images/btn_120x50_powered_shadow.png create mode 100644 root/static/images/btn_88x31_built.png create mode 100644 root/static/images/btn_88x31_built_shadow.png create mode 100644 root/static/images/btn_88x31_powered.png create mode 100644 root/static/images/btn_88x31_powered_shadow.png create mode 100644 root/static/images/catalyst_logo.png create mode 100644 root/static/pic/fbr.ico create mode 100755 script/frbr_books_cgi.pl create mode 100755 script/frbr_books_create.pl create mode 100755 script/frbr_books_fastcgi.pl create mode 100755 script/frbr_books_server.pl create mode 100755 script/frbr_books_test.pl create mode 100644 t/01app.t create mode 100644 t/02pod.t create mode 100644 t/03podcoverage.t create mode 100644 t/controller_Books.t create mode 100644 t/model_Schema.t create mode 100644 t/view_TtDefault.t diff --git a/Changes b/Changes new file mode 100644 index 0000000..d4c8205 --- /dev/null +++ b/Changes @@ -0,0 +1,4 @@ +This file documents the revision history for Perl extension FrBr::Books. + +0.01 2008-03-17 17:36:11 + - initial revision, generated by Catalyst diff --git a/Makefile.PL b/Makefile.PL new file mode 100644 index 0000000..86ef15a --- /dev/null +++ b/Makefile.PL @@ -0,0 +1,16 @@ +use inc::Module::Install; + +name 'FrBr-Books'; +all_from 'lib/FrBr/Books.pm'; + +requires 'Catalyst::Runtime' => '5.7012'; +requires 'Catalyst::Plugin::ConfigLoader'; +requires 'Catalyst::Plugin::Static::Simple'; +requires 'Catalyst::Action::RenderView'; +requires 'YAML'; # This should reflect the config file format you've chosen + # See Catalyst::Plugin::ConfigLoader for supported formats +catalyst; + +install_script glob('script/*.pl'); +auto_install; +WriteAll; diff --git a/README b/README new file mode 100644 index 0000000..e8e8fb3 --- /dev/null +++ b/README @@ -0,0 +1 @@ +Run script/frbr_books_server.pl to test the application. diff --git a/frbr_books.yml b/frbr_books.yml new file mode 100644 index 0000000..ca5378f --- /dev/null +++ b/frbr_books.yml @@ -0,0 +1,30 @@ +--- +name: FrBr::Books +database: + host: localhost + port: 3306 + schema: books + user: books + # passwd: +authentication: + dbic: + # Note this first definition would be the same as setting + # __PACKAGE__->config->{authentication}->{dbic}->{user_class} = ’MyAppDB::User’ + # in lib/MyApp.pm (IOW, each hash key becomes a "name:" in the YAML file). + # + # This is the model object created by Catalyst::Model::DBIC from your + # schema (you created ’MyAppDB::User’ but as the Catalyst startup + # debug messages show, it was loaded as ’MyApp::Model::MyAppDB::User’). + # NOTE: Omit ’MyApp::Model’ to avoid a component lookup issue in Catalyst 5.66 + user_class: Schema::Users + # + # This is the name of the field in your ’users’ table that contains the user’s name + user_field: login + # + # This is the name of the field in your ’users’ table that contains the password + password_field: password + # + # Other options can go here for hashed passwords + password_type: salted_hash + password_salt_len: 4 + diff --git a/lib/FrBr/Books.pm b/lib/FrBr/Books.pm new file mode 100644 index 0000000..f423d9b --- /dev/null +++ b/lib/FrBr/Books.pm @@ -0,0 +1,120 @@ +package FrBr::Books; + +# $Id: CookBook.pm 15 2007-08-09 14:39:47Z frank $ +# $URL$ + +use strict; +use warnings; + +use Catalyst::Runtime '5.70'; + +use FrBr::Common; + +# Set flags and add plugins for the application +# +# -Debug: activates the debug mode for very useful log messages +# ConfigLoader: will load the configuration from a YAML file in the +# application's home directory +# Static::Simple: will serve static files from the application's root +# directory + +use Catalyst qw/ + + +FrBr::Books::Plugin::ConfigLoader + + Static::Simple + +/; + +our $VERSION = '0.01'; + +my %LangsToUse = ( + 'de' => 'deutsch', + 'en' => 'english', +); + +my %LangsToUseInDates = ( + 'de' => 'Deutsch', + 'en' => 'English', +); + +# Configure the application. +# +# Note that settings in frbr_books.yml (or other external +# configuration file that you set up manually) take precedence +# over this when using ConfigLoader. Thus configuration +# details given here can function as a default configuration, +# with a external configuration file acting as an override for +# local deployment. + +__PACKAGE__->config( name => 'FrBr::Books' ); + +# Start the application +__PACKAGE__->setup; + +#------------------------------------------------------- + +sub auto : Private { + + my ( $self, $c ) = @_; + my $K = __PACKAGE__ . "::auto(): "; + + $c->config->{'debug_level'} = 0 unless defined $c->config->{'debug_level'}; + $c->log->debug( get_output_string( $K, "Aktuelle Konfiguration: ", $c->config ) ) if $c->config->{'debug_level'} >= 3; + + $c->stash->{'debug_level'} = to_int( $c->config->{'debug_level'} ) || 0; + my $env_debug_level = to_int( $ENV{'FRBR_BOOKS_DEBUG'} || $ENV{'CATALYST_DEBUG'} ); + $c->stash->{'debug_level'} = $env_debug_level if $env_debug_level and $env_debug_level > $c->stash->{'debug_level'}; + $c->log->debug( $K . "Aktuelles Debug-Level: " . $c->stash->{'debug_level'} ) if $c->stash->{'debug_level'}; + + + ######################## + # Sprachabhaengige Dinge + + my $lang = 'de'; + + # Neue Sprache festlegen: + if ( $c->req->param('new_lang_to_use') and $LangsToUse{ $c->req->param('new_lang_to_use') } ) { + $lang = $c->req->param('new_lang_to_use'); + #$c->session->{'lang_to_use'} = $lang; + } + else { + #$lang = $c->session->{'lang_to_use'} if $c->session->{'lang_to_use'}; + } + #$c->languages( [$lang] ); + #$c->stash->{'lang_to_use'} = $lang; + #$c->log->debug( $K . "Neue Sprache: '" . $lang . "' (" . $LangsToUseInDates{$lang} . ")." ) if $c->stash->{'debug_level'} >= 2; + + 1; + +} ## end sub auto : + + +=head1 NAME + +FrBr::Books - Catalyst based application + +=head1 SYNOPSIS + + script/frbr_books_server.pl + +=head1 DESCRIPTION + +[enter your description here] + +=head1 SEE ALSO + +L, L + +=head1 AUTHOR + +Frank Brehm + +=head1 LICENSE + +This library is free software, you can redistribute it and/or modify +it under the same terms as Perl itself. + +=cut + +1; diff --git a/lib/FrBr/Books/Controller/Books.pm b/lib/FrBr/Books/Controller/Books.pm new file mode 100644 index 0000000..bd114fb --- /dev/null +++ b/lib/FrBr/Books/Controller/Books.pm @@ -0,0 +1,148 @@ +package FrBr::Books::Controller::Books; + +# $Id: Absence.pm 305 2008-03-14 13:53:44Z fbrehm $ +# $URL$ + +use strict; +use warnings; +use base 'Catalyst::Controller'; + +use FrBr::Common; + +=head1 NAME + +FrBr::Books::Controller::Books - Catalyst Controller + +=head1 DESCRIPTION + +Catalyst Controller. + +=head1 METHODS + +=cut + + +=head2 index + +=cut + +sub index : Private { + my ( $self, $c ) = @_; + + $c->response->body('Matched FrBr::Books::Controller::Books in Books.'); +} + +=head2 list + +Fetch all book objects and pass to books/list.tt2 in stash to be displayed + +=cut + +sub list : Local { + + # Retrieve the usual perl OO '$self' for this object. $c is the Catalyst + # 'Context' that's used to 'glue together' the various components + # that make up the application + my ($self, $c) = @_; + my $K = __PACKAGE__ . "::list(): "; + $c->log->debug( $K . "aufgerufen." ) if $c->stash->{'debug_level'} > 2; + + $c->stash->{'cssfiles'} = [] unless $c->stash->{'cssfiles'}; + push @{$c->stash->{'cssfiles'}}, 'books/styles.css'; + + my $search_params = undef; + my $other_params = {}; + $other_params->{'order_by'} = [ 'title' ]; + $other_params->{'join'} = [ 'waehrung', 'verlag' ]; + $other_params->{'select'} = [ + 'me.id', + 'me.title', + 'me.title_original', + 'me.verlags_id', + 'me.isbn', + 'me.buch_nr', + 'me.ausgabejahr', + 'me.druckjahr', + 'me.preis', + 'me.waehrungs_id', + 'waehrung.waehrungs_kuerzel', + 'waehrung.waehrungs_name', + 'waehrung.umrechnung_in_euro', + 'verlag.name_short AS `verlagsname_short`', + 'verlag.name_long AS `verlagsname_long`', + ]; + $other_params->{'as'} = [ + 'id', + 'title', + 'title_original', + 'verlags_id', + 'isbn', + 'buch_nr', + 'ausgabejahr', + 'druckjahr', + 'preis', + 'waehrungs_id', + 'waehrungs_kuerzel', + 'waehrungs_name', + 'umrechnung_in_euro', + 'verlagsname_short', + 'verlagsname_long', + ]; + $c->stash->{'books'} = []; + for my $book ( $c->model('Schema::Buecher')->search( $search_params, $other_params )->all() ) { + my $buch = {}; + $buch->{'id'} = $book->id(); + $buch->{'title'} = $book->title(); + $buch->{'title_original'} = $book->title_original(); + $buch->{'verlags_id'} = $book->verlags_id(); + $buch->{'isbn'} = $book->isbn(); + $buch->{'buch_nr'} = $book->buch_nr(); + $buch->{'ausgabejahr'} = $book->ausgabejahr(); + $buch->{'druckjahr'} = $book->druckjahr(); + $buch->{'preis'} = $book->preis(); + $buch->{'waehrungs_id'} = $book->waehrungs_id(); + $buch->{'waehrungs_kuerzel'} = $book->get_column('waehrungs_kuerzel'); + $buch->{'waehrungs_name'} = $book->get_column('waehrungs_name'); + $buch->{'umrechnung_in_euro'} = $book->get_column('umrechnung_in_euro'); + $buch->{'verlagsname_short'} = $book->get_column('verlagsname_short'); + $buch->{'verlagsname_long'} = $book->get_column('verlagsname_long'); + push @{$c->stash->{'books'}}, $buch; + } + + $c->stash->{'autoren'} = {}; + for my $autor ( $c->model('Schema::Autoren')->all() ) { + my $au = {}; + my $id = $autor->id; + $au->{'id'} = $id; + $au->{'autor_name'} = $autor->autor_name; + $au->{'autor_descr'} = $autor->autor_descr; + $c->log->debug( $K . get_output_string( "Autor gefunden: ", $au ) ) if $c->stash->{'debug_level'} > 3; + $c->stash->{'autoren'}{$id} = $au; + } + $c->stash->{'buch2autor'} = {}; + for my $ref ( $c->model('Schema::Autor2buch')->search(undef, { 'order_by' => 'ord_num' } )->all() ) { + my $bid = $ref->buch_id; + my $aid = $ref->autor_id; + $c->stash->{'buch2autor'}{$bid} = [] unless $c->stash->{'buch2autor'}{$bid}; + push @{$c->stash->{'buch2autor'}{$bid}}, $c->stash->{'autoren'}{$aid}{'autor_name'}; + } + + # Set the TT template to use. You will almost always want to do this + # in your action methods (action methods respond to user input in + # your controllers). + $c->stash->{'template'} = 'books/list.tt2'; +} + + +=head1 AUTHOR + +Frank Brehm + +=head1 LICENSE + +This library is free software, you can redistribute it and/or modify +it under the same terms as Perl itself. + +=cut + +1; diff --git a/lib/FrBr/Books/Controller/Root.pm b/lib/FrBr/Books/Controller/Root.pm new file mode 100644 index 0000000..db62e57 --- /dev/null +++ b/lib/FrBr/Books/Controller/Root.pm @@ -0,0 +1,64 @@ +package FrBr::Books::Controller::Root; + +# $Id: Absence.pm 305 2008-03-14 13:53:44Z fbrehm $ +# $URL$ + +use strict; +use warnings; +use base 'Catalyst::Controller'; + +# +# Sets the actions in this controller to be registered with no prefix +# so they function identically to actions created in MyApp.pm +# +__PACKAGE__->config->{'namespace'} = ''; + +=head1 NAME + +FrBr::Books::Controller::Root - Root Controller for FrBr::Books + +=head1 DESCRIPTION + +[enter your description here] + +=head1 METHODS + +=cut + +=head2 default + +=cut + +sub default : Private { + + my ( $self, $c ) = @_; + + #$c->stash->{'site_title'} = sprintf( $c->localize( "%s's Cookbook" ) , 'Frank Brehm' ); + $c->stash->{'site_title'} = "Franks Bucharchiv"; + $c->stash->{'message'} = 'Willkommen!'; + $c->stash->{'template'} = 'welcome.tt2'; + + # Hello World + #$c->response->body( $c->welcome_message ); +} + +=head2 end + +Attempt to render a view, if needed. + +=cut + +sub end : ActionClass('RenderView') {} + +=head1 AUTHOR + +Frank Brehm + +=head1 LICENSE + +This library is free software, you can redistribute it and/or modify +it under the same terms as Perl itself. + +=cut + +1; diff --git a/lib/FrBr/Books/Db.pm b/lib/FrBr/Books/Db.pm new file mode 100644 index 0000000..f6c5101 --- /dev/null +++ b/lib/FrBr/Books/Db.pm @@ -0,0 +1,19 @@ +package FrBr::Books::Db; + +# $Id: Absence.pm 305 2008-03-14 13:53:44Z fbrehm $ +# $URL$ + +use strict; +use warnings; + +use base 'DBIx::Class::Schema'; + +__PACKAGE__->load_classes; + + +# Created by DBIx::Class::Schema::Loader v0.04004 @ 2008-03-17 17:53:52 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:yMIjCo2VODRuy9VqS1AeQA + + +# You can replace this text with custom content, and it will be preserved on regeneration +1; diff --git a/lib/FrBr/Books/Db/Autor2buch.pm b/lib/FrBr/Books/Db/Autor2buch.pm new file mode 100644 index 0000000..84d83c4 --- /dev/null +++ b/lib/FrBr/Books/Db/Autor2buch.pm @@ -0,0 +1,28 @@ +package FrBr::Books::Db::Autor2buch; + +# $Id: Absence.pm 305 2008-03-14 13:53:44Z fbrehm $ +# $URL$ + +use strict; +use warnings; + +use base 'DBIx::Class'; + +__PACKAGE__->load_components("Core"); +__PACKAGE__->table("autor2buch"); +__PACKAGE__->add_columns( + "id" => { data_type => "INT", default_value => undef, is_nullable => 0, size => 10 }, + "buch_id" => { data_type => "INT", default_value => undef, is_nullable => 0, size => 10 }, + "autor_id" => { data_type => "INT", default_value => undef, is_nullable => 0, size => 10 }, + "ord_num" => { data_type => "INT", default_value => 0, is_nullable => 0, size => 10 }, +); +__PACKAGE__->set_primary_key("id"); +__PACKAGE__->add_unique_constraint("buch_id", ["buch_id", "autor_id"]); + + +# Created by DBIx::Class::Schema::Loader v0.04004 @ 2008-03-17 17:53:52 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ndVfBKXzEtRRzVPU1qAspg + + +# You can replace this text with custom content, and it will be preserved on regeneration +1; diff --git a/lib/FrBr/Books/Db/Autoren.pm b/lib/FrBr/Books/Db/Autoren.pm new file mode 100644 index 0000000..1c4cd1e --- /dev/null +++ b/lib/FrBr/Books/Db/Autoren.pm @@ -0,0 +1,27 @@ +package FrBr::Books::Db::Autoren; + +# $Id: Absence.pm 305 2008-03-14 13:53:44Z fbrehm $ +# $URL$ + +use strict; +use warnings; + +use base 'DBIx::Class'; + +__PACKAGE__->load_components("Core"); +__PACKAGE__->table("autoren"); +__PACKAGE__->add_columns( + "id" => { data_type => "INT", default_value => undef, is_nullable => 0, size => 10 }, + "autor_name" => { data_type => "VARCHAR", default_value => undef, is_nullable => 0, size => 250, }, + "autor_descr" => { data_type => "TEXT", default_value => undef, is_nullable => 0, size => 65535, }, +); +__PACKAGE__->set_primary_key("id"); +__PACKAGE__->add_unique_constraint("autor_name", ["autor_name"]); + + +# Created by DBIx::Class::Schema::Loader v0.04004 @ 2008-03-17 17:53:52 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:QwPjYKNwUnR5b418xxulDg + + +# You can replace this text with custom content, and it will be preserved on regeneration +1; diff --git a/lib/FrBr/Books/Db/Buch2kategorie.pm b/lib/FrBr/Books/Db/Buch2kategorie.pm new file mode 100644 index 0000000..e61f62b --- /dev/null +++ b/lib/FrBr/Books/Db/Buch2kategorie.pm @@ -0,0 +1,27 @@ +package FrBr::Books::Db::Buch2kategorie; + +# $Id: Absence.pm 305 2008-03-14 13:53:44Z fbrehm $ +# $URL$ + +use strict; +use warnings; + +use base 'DBIx::Class'; + +__PACKAGE__->load_components("Core"); +__PACKAGE__->table("buch2kategorie"); +__PACKAGE__->add_columns( + "id" => { data_type => "INT", default_value => undef, is_nullable => 0, size => 10 }, + "buch_id" => { data_type => "INT", default_value => undef, is_nullable => 0, size => 10 }, + "kategorie_id" => { data_type => "INT", default_value => undef, is_nullable => 0, size => 10 }, +); +__PACKAGE__->set_primary_key("id"); +__PACKAGE__->add_unique_constraint("buch_id", ["buch_id", "kategorie_id"]); + + +# Created by DBIx::Class::Schema::Loader v0.04004 @ 2008-03-17 17:53:52 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:rYT7qQgF4WvVJQgCDUurIw + + +# You can replace this text with custom content, and it will be preserved on regeneration +1; diff --git a/lib/FrBr/Books/Db/Buch2serie.pm b/lib/FrBr/Books/Db/Buch2serie.pm new file mode 100644 index 0000000..0220903 --- /dev/null +++ b/lib/FrBr/Books/Db/Buch2serie.pm @@ -0,0 +1,28 @@ +package FrBr::Books::Db::Buch2serie; + +# $Id: Absence.pm 305 2008-03-14 13:53:44Z fbrehm $ +# $URL$ + +use strict; +use warnings; + +use base 'DBIx::Class'; + +__PACKAGE__->load_components("Core"); +__PACKAGE__->table("buch2serie"); +__PACKAGE__->add_columns( + "id" => { data_type => "INT", default_value => undef, is_nullable => 0, size => 10 }, + "buch_id" => { data_type => "INT", default_value => undef, is_nullable => 0, size => 10 }, + "serien_id" => { data_type => "INT", default_value => undef, is_nullable => 0, size => 10 }, + "ord_num" => { data_type => "INT", default_value => undef, is_nullable => 0, size => 10 }, +); +__PACKAGE__->set_primary_key("id"); +__PACKAGE__->add_unique_constraint("buch_id", ["buch_id", "serien_id"]); + + +# Created by DBIx::Class::Schema::Loader v0.04004 @ 2008-03-17 17:53:52 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:0ZVowzPrhFa/hKjsW/G9RQ + + +# You can replace this text with custom content, and it will be preserved on regeneration +1; diff --git a/lib/FrBr/Books/Db/Buecher.pm b/lib/FrBr/Books/Db/Buecher.pm new file mode 100644 index 0000000..e0d16e5 --- /dev/null +++ b/lib/FrBr/Books/Db/Buecher.pm @@ -0,0 +1,36 @@ +package FrBr::Books::Db::Buecher; + +# $Id: Absence.pm 305 2008-03-14 13:53:44Z fbrehm $ +# $URL$ + +use strict; +use warnings; + +use base 'DBIx::Class'; + +__PACKAGE__->load_components("Core"); +__PACKAGE__->table("buecher"); +__PACKAGE__->add_columns( + "id" => { data_type => "INT", default_value => undef, is_nullable => 0, size => 10 }, + "title" => { data_type => "VARCHAR", default_value => undef, is_nullable => 0, size => 250, }, + "title_original" => { data_type => "VARCHAR", default_value => undef, is_nullable => 1, size => 250, }, + "verlags_id" => { data_type => "INT", default_value => undef, is_nullable => 0, size => 10 }, + "isbn" => { data_type => "VARCHAR", default_value => undef, is_nullable => 1, size => 20, }, + "buch_nr" => { data_type => "VARCHAR", default_value => undef, is_nullable => 1, size => 100, }, + "ausgabejahr" => { data_type => "MEDIUMINT", default_value => undef, is_nullable => 1, size => 8, }, + "druckjahr" => { data_type => "MEDIUMINT", default_value => undef, is_nullable => 1, size => 8, }, + "preis" => { data_type => "FLOAT", default_value => undef, is_nullable => 1, size => 32 }, + "waehrungs_id" => { data_type => "INT", default_value => undef, is_nullable => 1, size => 10 }, +); + +__PACKAGE__->set_primary_key("id"); + +__PACKAGE__->might_have( 'waehrung' => 'FrBr::Books::Db::Waehrungen', { 'foreign.id' => 'self.waehrungs_id' } ); +__PACKAGE__->might_have( 'verlag' => 'FrBr::Books::Db::Verlage', { 'foreign.id' => 'self.verlags_id' } ); + +# Created by DBIx::Class::Schema::Loader v0.04004 @ 2008-03-17 17:53:52 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:GjY80NXBYER1XlWgPukp+A + + +# You can replace this text with custom content, and it will be preserved on regeneration +1; diff --git a/lib/FrBr/Books/Db/Kategorien.pm b/lib/FrBr/Books/Db/Kategorien.pm new file mode 100644 index 0000000..142a4a6 --- /dev/null +++ b/lib/FrBr/Books/Db/Kategorien.pm @@ -0,0 +1,26 @@ +package FrBr::Books::Db::Kategorien; + +# $Id: Absence.pm 305 2008-03-14 13:53:44Z fbrehm $ +# $URL$ + +use strict; +use warnings; + +use base 'DBIx::Class'; + +__PACKAGE__->load_components("Core"); +__PACKAGE__->table("kategorien"); +__PACKAGE__->add_columns( + "id" => { data_type => "INT", default_value => undef, is_nullable => 0, size => 10 }, + "kategorie_name" => { data_type => "VARCHAR", default_value => undef, is_nullable => 0, size => 100, }, +); +__PACKAGE__->set_primary_key("id"); +__PACKAGE__->add_unique_constraint("kategorie_name", ["kategorie_name"]); + + +# Created by DBIx::Class::Schema::Loader v0.04004 @ 2008-03-17 17:53:52 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:ZEMEahjPuJByMUE/OvebIg + + +# You can replace this text with custom content, and it will be preserved on regeneration +1; diff --git a/lib/FrBr/Books/Db/Serien.pm b/lib/FrBr/Books/Db/Serien.pm new file mode 100644 index 0000000..21b3b19 --- /dev/null +++ b/lib/FrBr/Books/Db/Serien.pm @@ -0,0 +1,27 @@ +package FrBr::Books::Db::Serien; + +# $Id: Absence.pm 305 2008-03-14 13:53:44Z fbrehm $ +# $URL$ + +use strict; +use warnings; + +use base 'DBIx::Class'; + +__PACKAGE__->load_components("Core"); +__PACKAGE__->table("serien"); +__PACKAGE__->add_columns( + "id" => { data_type => "INT", default_value => undef, is_nullable => 0, size => 10 }, + "serien_name" => { data_type => "VARCHAR", default_value => undef, is_nullable => 0, size => 100, }, + "descr" => { data_type => "TEXT", default_value => undef, is_nullable => 0, size => 65535, }, +); +__PACKAGE__->set_primary_key("id"); +__PACKAGE__->add_unique_constraint("serien_name", ["serien_name"]); + + +# Created by DBIx::Class::Schema::Loader v0.04004 @ 2008-03-17 17:53:52 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:/IzPoWKb7jK+P2HCTO+bCQ + + +# You can replace this text with custom content, and it will be preserved on regeneration +1; diff --git a/lib/FrBr/Books/Db/Session.pm b/lib/FrBr/Books/Db/Session.pm new file mode 100644 index 0000000..04ff3a5 --- /dev/null +++ b/lib/FrBr/Books/Db/Session.pm @@ -0,0 +1,26 @@ +package FrBr::Books::Db::Session; + +# $Id: Absence.pm 305 2008-03-14 13:53:44Z fbrehm $ +# $URL$ + +use strict; +use warnings; + +use base 'DBIx::Class'; + +__PACKAGE__->load_components("Core"); +__PACKAGE__->table("session"); +__PACKAGE__->add_columns( + "id" => { data_type => "VARCHAR", default_value => undef, is_nullable => 0, size => 72, }, + "session_data" => { data_type => "TEXT", default_value => undef, is_nullable => 0, size => 65535, }, + "expires" => { data_type => "INT", default_value => undef, is_nullable => 0, size => 10 }, +); +__PACKAGE__->set_primary_key("id"); + + +# Created by DBIx::Class::Schema::Loader v0.04004 @ 2008-03-17 17:53:52 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:iRa1VUPKms1GfHni1F3KoA + + +# You can replace this text with custom content, and it will be preserved on regeneration +1; diff --git a/lib/FrBr/Books/Db/Users.pm b/lib/FrBr/Books/Db/Users.pm new file mode 100644 index 0000000..d137f96 --- /dev/null +++ b/lib/FrBr/Books/Db/Users.pm @@ -0,0 +1,36 @@ +package FrBr::Books::Db::Users; + +# $Id: Absence.pm 305 2008-03-14 13:53:44Z fbrehm $ +# $URL$ + +use strict; +use warnings; + +use base 'DBIx::Class'; + +__PACKAGE__->load_components("Core"); +__PACKAGE__->table("users"); +__PACKAGE__->add_columns( + "user_id" => { data_type => "INT", default_value => undef, is_nullable => 0, size => 10 }, + "login" => { data_type => "VARCHAR", default_value => undef, is_nullable => 0, size => 50, }, + "vorname" => { data_type => "VARCHAR", default_value => undef, is_nullable => 0, size => 100, }, + "nachname" => { data_type => "VARCHAR", default_value => undef, is_nullable => 0, size => 100, }, + "password" => { data_type => "VARCHAR", default_value => undef, is_nullable => 0, size => 250, }, + "date_created" => { data_type => "DATETIME", default_value => undef, is_nullable => 0, size => 19, }, + "date_changed" => { data_type => "DATETIME", default_value => undef, is_nullable => 0, size => 19, }, + "email" => { data_type => "VARCHAR", default_value => undef, is_nullable => 0, size => 250, }, + "deleted" => { data_type => "ENUM", default_value => "n", is_nullable => 0, size => 1 }, + "enabled" => { data_type => "ENUM", default_value => "y", is_nullable => 0, size => 1 }, + "admin_status" => { data_type => "ENUM", default_value => "n", is_nullable => 0, size => 1 }, + "comments" => { data_type => "TEXT", default_value => undef, is_nullable => 0, size => 65535, }, +); +__PACKAGE__->set_primary_key("user_id"); +__PACKAGE__->add_unique_constraint("login", ["login"]); + + +# Created by DBIx::Class::Schema::Loader v0.04004 @ 2008-03-17 17:53:52 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:dE3Et+cb9hDWcDvAu7CS+A + + +# You can replace this text with custom content, and it will be preserved on regeneration +1; diff --git a/lib/FrBr/Books/Db/Verlage.pm b/lib/FrBr/Books/Db/Verlage.pm new file mode 100644 index 0000000..f525289 --- /dev/null +++ b/lib/FrBr/Books/Db/Verlage.pm @@ -0,0 +1,27 @@ +package FrBr::Books::Db::Verlage; + +# $Id: Absence.pm 305 2008-03-14 13:53:44Z fbrehm $ +# $URL$ + +use strict; +use warnings; + +use base 'DBIx::Class'; + +__PACKAGE__->load_components("Core"); +__PACKAGE__->table("verlage"); +__PACKAGE__->add_columns( + "id" => { data_type => "INT", default_value => undef, is_nullable => 0, size => 10 }, + "name_short" => { data_type => "VARCHAR", default_value => undef, is_nullable => 0, size => 50, }, + "name_long" => { data_type => "VARCHAR", default_value => undef, is_nullable => 0, size => 250, }, +); +__PACKAGE__->set_primary_key("id"); +__PACKAGE__->add_unique_constraint("name_short", ["name_short"]); + + +# Created by DBIx::Class::Schema::Loader v0.04004 @ 2008-03-17 17:53:52 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:DAa/d3XMLm6o69u4vod7zA + + +# You can replace this text with custom content, and it will be preserved on regeneration +1; diff --git a/lib/FrBr/Books/Db/Waehrungen.pm b/lib/FrBr/Books/Db/Waehrungen.pm new file mode 100644 index 0000000..61136d9 --- /dev/null +++ b/lib/FrBr/Books/Db/Waehrungen.pm @@ -0,0 +1,28 @@ +package FrBr::Books::Db::Waehrungen; + +# $Id: Absence.pm 305 2008-03-14 13:53:44Z fbrehm $ +# $URL$ + +use strict; +use warnings; + +use base 'DBIx::Class'; + +__PACKAGE__->load_components("Core"); +__PACKAGE__->table("waehrungen"); +__PACKAGE__->add_columns( + "id" => { data_type => "INT", default_value => undef, is_nullable => 0, size => 10 }, + "waehrungs_kuerzel" => { data_type => "VARCHAR", default_value => undef, is_nullable => 0, size => 10, }, + "waehrungs_name" => { data_type => "VARCHAR", default_value => undef, is_nullable => 0, size => 100, }, + "umrechnung_in_euro" => { data_type => "FLOAT", default_value => undef, is_nullable => 1, size => 32 }, +); +__PACKAGE__->set_primary_key("id"); +__PACKAGE__->add_unique_constraint("waehrungs_kuerzel", ["waehrungs_kuerzel"]); + + +# Created by DBIx::Class::Schema::Loader v0.04004 @ 2008-03-17 17:53:52 +# DO NOT MODIFY THIS OR ANYTHING ABOVE! md5sum:WM0GK+E0U+7khRuX5LN3XA + + +# You can replace this text with custom content, and it will be preserved on regeneration +1; diff --git a/lib/FrBr/Books/Model/Schema.pm b/lib/FrBr/Books/Model/Schema.pm new file mode 100644 index 0000000..da5fcbd --- /dev/null +++ b/lib/FrBr/Books/Model/Schema.pm @@ -0,0 +1,44 @@ +package FrBr::Books::Model::Schema; + +# $Id: Absence.pm 305 2008-03-14 13:53:44Z fbrehm $ +# $URL$ + +use strict; +use base 'Catalyst::Model::DBIC::Schema'; + +__PACKAGE__->config( + schema_class => 'FrBr::Books::Db', + 'connect_info' => [ + $ENV{'FRBR_BOOKS_DSN'} || 'dbi:mysql:database=books;host=localhost', + $ENV{'FRBR_BOOKS_USER'} || 'books', + $ENV{'FRBR_BOOKS_PWD'} || '', + { 'AutoCommit' => 1, + 'PrintError' => 0, + 'RaiseError' => 0, + } + ], +); + +=head1 NAME + +FrBr::Books::Model::Schema - Catalyst DBIC Schema Model +=head1 SYNOPSIS + +See L + +=head1 DESCRIPTION + +L Model using schema L + +=head1 AUTHOR + +Frank Brehm + +=head1 LICENSE + +This library is free software, you can redistribute it and/or modify +it under the same terms as Perl itself. + +=cut + +1; diff --git a/lib/FrBr/Books/Plugin/ConfigLoader.pm b/lib/FrBr/Books/Plugin/ConfigLoader.pm new file mode 100644 index 0000000..faa6c0a --- /dev/null +++ b/lib/FrBr/Books/Plugin/ConfigLoader.pm @@ -0,0 +1,102 @@ +package FrBr::Books::Plugin::ConfigLoader; + +# $Id: ConfigLoader.pm 16 2007-08-09 14:47:12Z frank $ +# $URL$ + +use base "Catalyst::Plugin::ConfigLoader"; +use FrBr::Common; +use Catalyst::Log::Log4perl; + +my %LogLevels = ( + 'FATAL' => 1, + 'ERROR' => 1, + 'WARN' => 1, + 'INFO' => 1, + 'DEBUG' => 1, +); + +#--------------------------------------------------------------------------- + +=head2 finalize_config + +This method is called after the config file is loaded. It can be +used to implement tuning of config values that can only be done +at runtime. If you need to do this to properly configure any +plugins, it's important to load ConfigLoader before them. +ConfigLoader provides a default finalize_config method which +walks through the loaded config hash and replaces any strings +beginning containing C<__HOME__> with the full path to +app's home directory (i.e. C<$c-Epath_to('')> ). +You can also use C<__path_to(foo/bar)__> which translates to +C<$c-Epath_to('foo', 'bar')> + +=cut + +sub finalize_config { + + my $c = shift; + my $K = __PACKAGE__ . "::finalize_config(): "; + + #$c->log->debug( get_output_string( $K, "aufgerufen." ) ) if $c->config->{'debug_level'} >= 2; + + $c->SUPER::finalize_config(); + + $c->config->{'debug_level'} = to_int( $c->config->{'debug_level'} ) ? to_int( $c->config->{'debug_level'} ) : 0; + + # Vorgabe-View festlegen (kann mit $c->stash->{'current_view_instance'} oder $c->stash->{'current_view'} ueberschrieben werden.) + $c->config()->{'default_view'} = 'TtDefault' unless $c->config()->{'default_view'}; + + my $log4perlconf = ""; + my $log_level = uc( $c->config()->{'log_level'} || 'info' ); + $log_level = 'INFO' unless $LogLevels{$log_level}; + + if ( $ENV{'CATALYST_DEBUG'} or $ENV{'FRBR_BOOKS_DEBUG'} ) { + $log_level = 'DEBUG'; + } + else { + $log_level = 'INFO' if $log_level eq 'DEBUG'; + } + + $log4perlconf .= 'log4perl.rootLogger=' . $log_level . ", A1\n"; + if ( $c->config()->{'colored_log'} ) { + $log4perlconf .= "log4perl.appender.A1=Log::Log4perl::Appender::ScreenColoredLevels\n"; + } + else { + $log4perlconf .= "log4perl.appender.A1=Log::Log4perl::Appender::Screen\n"; + } + $log4perlconf .= "log4perl.appender.A1.layout=PatternLayout\n"; + $log4perlconf .= "log4perl.appender.A1.layout.ConversionPattern=[%d] [CookBook] [%p] %m%n\n"; + + $c->log->debug( $K . "Log4perl-Konfiguration:\n" . $log4perlconf ) if $c->config->{'debug_level'} >= 1; + $c->log( Catalyst::Log::Log4perl->new( \$log4perlconf ) ); + + my $dsn = 'dbi:mysql:host=%s;database=%s'; + + $dsn = sprintf( $dsn, ( $c->config()->{'database'}->{'host'} || 'localhost' ), + ( $c->config()->{'database'}->{'schema'} || 'books' ) ); + + my $port = 3306; + if ( $c->config()->{'database'}->{'port'} and to_int( $c->config()->{'database'}->{'port'} ) ) { + $port = $c->config()->{'database'}->{'port'}; + } + $port = $port == 3306 ? '' : ';port=' . $port; + $dsn .= $port; + + $c->config()->{'dsn'} = $dsn; + $c->config()->{'db_user'} = $c->config()->{'database'}->{'user'} || 'cookbook'; + $c->config()->{'db_passwd'} = $c->config()->{'database'}->{'passwd'} || ''; + + $ENV{'FRBR_BOOKS_DSN'} = $dsn; + $ENV{'FRBR_BOOKS_USER'} = $c->config()->{'db_user'}; + $ENV{'FRBR_BOOKS_PWD'} = $c->config()->{'db_passwd'}; + + $c->log->debug( get_output_string( $K, "Aktuelle Konfiguration: ", $c->config ) ) if $c->config->{'debug_level'} >= 3; + +} ## end sub finalize_config + +#--------------------------------------------------------------------------- + +1; + +#--------------------------------------------------------------------------- + diff --git a/lib/FrBr/Books/View/TtDefault.pm b/lib/FrBr/Books/View/TtDefault.pm new file mode 100644 index 0000000..b184a8e --- /dev/null +++ b/lib/FrBr/Books/View/TtDefault.pm @@ -0,0 +1,47 @@ +package FrBr::Books::View::TtDefault; + +# $Id: Absence.pm 305 2008-03-14 13:53:44Z fbrehm $ +# $URL$ + +use strict; +use base 'Catalyst::View::TT'; + +__PACKAGE__->config({ + CATALYST_VAR => 'Catalyst', + INCLUDE_PATH => [ + FrBr::Books->path_to( 'root', 'src' ), + FrBr::Books->path_to( 'root', 'lib' ) + ], + PRE_PROCESS => 'config/main.tt2', + WRAPPER => 'site/wrapper.tt2', + ERROR => 'error.tt2', + TIMER => 0, + PRE_CHOMP => 0, + POST_CHOMP => 0, + TEMPLATE_EXTENSION => '.tt2', +}); + +=head1 NAME + +FrBr::Books::View::TtDefault - TT View for FrBr::Books + +=head1 DESCRIPTION + +TT View for FrBr::Books. + +=head1 AUTHOR + +=head1 SEE ALSO + +L + +Frank Brehm + +=head1 LICENSE + +This library is free software, you can redistribute it and/or modify +it under the same terms as Perl itself. + +=cut + +1; diff --git a/lib/FrBr/Common.pm b/lib/FrBr/Common.pm new file mode 100644 index 0000000..cc47329 --- /dev/null +++ b/lib/FrBr/Common.pm @@ -0,0 +1,432 @@ +package FrBr::Common; + +# $Id: Common.pm 5 2007-08-07 17:27:37Z frank $ +# $URL$ + +=head1 NAME + +FrBr::Common + +=head1 DESCRIPTION + +Modul fuer allgemeine Aufgaben, zum Beispiel Verbose-Level usw. + +Dieses Modul sollte von allen Scripten und Modulen verwendet werden. + +=cut + +#--------------------------------------------------------------------------- + +use strict; +use warnings; +use Exporter; +use Data::Dumper; +use Cwd; +use File::Spec; + +use Carp qw(:DEFAULT cluck); + +our @ISA = qw(Exporter); +our @EXPORT = qw( + &verbose + &canon_filename + &common_debug + &common_error + &common_notice + &get_output_string + &escape_html + &home_dir + &to_bool + &to_float + &to_int +); + +$Data::Dumper::Indent = 1; +$Data::Dumper::Sortkeys = 1; + +our $FRBR_VERSION = "1.1"; +our $AUTHOR = 'Frank Brehm '; + +our $env_home_name = 'FRBR_HOME'; + +my $Revis = <<'ENDE'; + $Revision$ +ENDE +$Revis =~ s/^.*:\s*(\S+)\s*\$.*/$1/s; +our $VERSION = $FRBR_VERSION . "." . $Revis; + +my $verbose = 0; +my $mark = 'CookBook'; + +$verbose = to_int( $ENV{'FRBR_BOOKS_DEBUG'} || $ENV{'CATALYST_DEBUG'} ) || 0; + +#------------------------------------------------------------------------------------------ + +=head1 Funktionen + +Alle hier definierten Funktionen werden exportiert. + +#------------------------------------------------------------------------------------------ + +=head2 verbose( $new_verbose_level ) + +Setzt bzw. gibt den Verbose-Level des aktuellen Scripts zurueck. + +Typische Werte: + +=over 4 + +=item I<0>: + +Keinerlei Ausgaben. + +=item I<1>: + +Ausgabe der wichtigsten Aktionen des aktuellen Scripts (sollte eigentlich Standard sein). + +. +. +. + +=item I<6>: + +Ausfuehrlichstes Geplapper bis zum Gehtnichtmehr. + +=back + +=cut + +sub verbose { + + my $new_verbose_level = shift; + + if ( defined $new_verbose_level and $new_verbose_level =~ /^\d+$/ ) { + $verbose = $new_verbose_level; + } + + return $verbose; + +} ## end sub verbose + +##------------------------------------------------------------------------------------------ + +=head2 mark( [$new_mark] ) + +Gibt den aktuellen Log-Marker zurueck bzw. setzt einen neuen, wenn er übergeben wurde. + +=cut + +sub mark { + + my $new_mark = shift; + + if ( $new_mark and $new_mark !~ /^\s*$/ ) { + $new_mark =~ s/^\s+//; + $new_mark =~ s/\s+$//s; + $mark = $new_mark; + } + + return $mark; + +} ## end sub mark + +#------------------------------------------------------------------------------------------ + +=head2 home_dir() + +Gibt den Namen des Verzeichnisses der Software-Installation zurueck, +die aus der Umgebungsvariablen C entnommen wird. + +Wenn die Umgebungsvariable nicht gesetzt ist bzw. auf ein nicht vorhandenes Verzeichnis +zeigt, wird undef zurueckgegeben. + +=cut + +sub home_dir { + + my $K = __PACKAGE__ . "::home_dir(): "; + + unless ( $ENV{$env_home_name} ) { + common_notice( $K . "Umgebungsvariable '" . $env_home_name . "' nicht gesetzt." ); + return undef; + } + + unless ( File::Spec->file_name_is_absolute( $ENV{$env_home_name} ) ) { + common_notice( $K . "Umgebungsvariable '" . $env_home_name . "' (" . $ENV{$env_home_name} . ") ist keine absolute Pfadangabe." ); + return undef; + } + + unless ( -d $ENV{$env_home_name} ) { + common_notice( + $K . "Umgebungsvariable '" . $env_home_name . "' zeigt auf nicht vorhandenes Verzeichnis '" . $ENV{$env_home_name} . "'." ); + return undef; + } + + return File::Spec->canonpath( $ENV{$env_home_name} ); + +} ## end sub home_dir + +#------------------------------------------------------------------------------------------ + +=head2 canon_filename( @directories, $filename ) + +Kettet die uebergebenen Verzeichnisnamen und Dateinamen aneinander. +Wenn der resultierende Pfad nicht absolut ist, wird home_dir() davorgehaengt. + +=cut + +sub canon_filename { + + my @path = @_; + my $K = __PACKAGE__ . "::canon_filename(): "; + + unless ( scalar(@path) ) { + common_notice( $K . "Keine Argumente uebergeben." ); + return undef; + } + + my $file = File::Spec->catfile(@path); + unless ( File::Spec->file_name_is_absolute($file) ) { + my $home = home_dir(); + return undef unless $home; + $file = File::Spec->catfile( $home, @path ); + } + + return $file; + +} ## end sub canon_filename + +#--------------------------------------------------------------------------- + +=head2 common_debug( $debug_level, @messages ) + +Allgemeine Debug-Funktion (wenn das Log-Objekt noch nicht existieren sollte) + +=cut + +sub common_debug { + + my $debug_level = shift; + + $debug_level = to_int($debug_level); + $debug_level = 1 unless defined $debug_level; + + return if $debug_level > $verbose; + + my $text = get_output_string(@_); + return if $text eq ''; + + print $mark . " (debug" . $debug_level . "): " . $text . "\n"; + + return; + +} ## end sub common_debug + +#--------------------------------------------------------------------------- + +sub get_output_string { + + my $text = ''; + for (@_) { + next unless defined $_; + my $t = ref($_) ? Dumper($_) : $_; + next if $t eq ''; + $text .= $t; + } + + $text =~ s/^\s+//; + $text =~ s/\s+$//s; + return $text; + +} ## end sub get_output_string + +#--------------------------------------------------------------------------- + +=head2 common_error( $error_level, @messages ) + +Allgemeine Error-Warn-Funktion (wenn das Log-Objekt noch nicht existieren sollte) + +=cut + +sub common_error { + + my $error_level = shift; + + $error_level ||= 'error'; + + my $text = get_output_string(@_); + + $text = "unbekannter Fehler" if $text eq ''; + + warn $mark . "(" . $error_level . "): " . $text . "\n"; + + return; + +} ## end sub common_error + +#--------------------------------------------------------------------------- + +=head2 common_notice( @messages ) + +Allgemeine Warnmeldung. + +=cut + +sub common_notice { + + my $text = get_output_string(@_); + $text = "unbekannter Fehler" if $text eq ''; + + warn $mark . ": " . $text . "\n"; + + return; + +} ## end sub common_notice + +#------------------------------------------------------------------------------------------ + +=head2 to_bool( $wert ) + +Wandelt den uebergebenen Scalar sicher in einen Wahrheitswert (0 oder 1) um. + +=cut + +sub to_bool { + + my $val = shift; + + return 0 unless defined $val; + return 0 if $val =~ /^\s*$/; + + if ( $val =~ /^\s*y(?:es?)?/i + or $val =~ /^\s*ja?/i + or $val =~ /^\s*[wt]\s*$/i + or $val =~ /^\s*on\s*$/i + or $val =~ /^\s*wahr|true/i ) + { + return 1; + } + + if ( $val =~ /^\s*no?/i + or $val =~ /^\s*ne(?:in?)?/i + or $val =~ /^\s*f\s*$/i + or $val =~ /^\s*off\s*$/i + or $val =~ /^\s*falsch|false/i ) + { + return 0; + } + + my $intval = to_int($val); + if ( defined $intval ) { + return $intval ? 1 : 0; + } + + return $val ? 1 : 0; + +} ## end sub to_bool + +#------------------------------------------------------------------------------------------ + +=head2 to_float( $wert ) + +Wandelt den uebergebenen Scalar sicher in eine Float-Zahl um. + +Falls der uebergebene Wert keine gueltige Zahl ist, wird undef zurueckgegeben. + +=cut + +sub to_float { + + my $val = shift; + + return undef unless defined($val) and $val =~ /\d/; + + my $ts = ","; + my $ds = "."; + + if ( ( $val =~ /\d,/ and $val !~ /\d\./ ) + or ( $val =~ /\d\.\d\d\d\./ ) + or ( $val =~ /\d\.\d\d\d,/ ) ) + { + $ds = ","; + $ts = "."; + } + + $val =~ s/\Q$ts\E//g; + $val =~ s/\Q$ds\E/\./g; + + return ( $val + 0 ); + +} ## end sub to_float + +#------------------------------------------------------------------------------------------ + +=head2 to_int( $wert, $signed ) + +Wandelt den uebergebenen Wert sicher in eine Integer-Zahl um. + +Dabei legt der optionale logische Parameter $signed fest, ob auch +vorzeichenbehaftete Werte zulaessig sind. + +Wenn keine gueltige Zahl uebergeben wird, wird undef zurueckgegeben. + +=cut + +sub to_int { + + my $val = shift; + my $signed = shift; + + return undef unless defined $val; + unless ( $val =~ /\d/ ) { + return undef; + } + + if ($signed) { + $val =~ /^[^\d-]*(?:(-)\s*)?(\d+)/; + $val = ( defined $1 ? $1 : '' ) . $2; + } + else { + $val =~ /^\D*(\d+)/; + $val = $1; + } + + return $val + 0; + +} ## end sub to_int + +#------------------------------------------------------------------------------------------ + +=head2 escape_html( $text ) + +Maskiert alle '&', '<', '>' und '"' im uebergebenen Text durch entsprechende +HTML-Entities. + +Entnommen dem Modul L. + +=cut + +sub escape_html { + + return unless defined( my $toencode = shift ); + + $toencode =~ s{&}{&}gso; + $toencode =~ s{<}{<}gso; + $toencode =~ s{>}{>}gso; + $toencode =~ s{\"}{"}gso; + + # Doesn't work. Can't work. forget it. + # $toencode =~ s{\x8b}{‹}gso; + # $toencode =~ s{\x9b}{›}gso; + + $toencode; + +} ## end sub escape_html + +#------------------------------------------------------------------------------------------ + +1; + +#------------------------------------------------------------------------------------------ + +__END__ diff --git a/root/lib/config/colors.tt2 b/root/lib/config/colors.tt2 new file mode 100644 index 0000000..0c16999 --- /dev/null +++ b/root/lib/config/colors.tt2 @@ -0,0 +1,84 @@ +[%# config/col + + alle Farbdefinitionen + + $Id: colors.tt2 1998 2008-03-06 11:34:59Z fbrehm $ + $URL$ + +-%] + +[%- + # Definition von Farbnamen + + site.rgb = { + black = '#000000' + white = '#ffffff' + grey1 = '#46494c' + grey2 = '#c6c9cc' + grey3 = '#e3e6ea' + hellgrau = '#d2d2d2' + mittelgrau = '#b4b4b4' + dunkelgrau = '#969696' + ganzdunkelgrau = '#404040' + silbergrau = '#cccccc' + hellsilbergrau = '#e0e0e0' + fastweiss = '#f0f0f0' + graugelb = '#e1e49a' + rotbraun = '#9b2b2a' + dunkelbraun = '#782828' + cyan = '#8de0e0' + mgrey = '#707070' + lgrey = '#c0c0c0' + red = '#cc4444' + hellrot = '#f00000' + rosa = '#ffc7c7' + hellrosa = '#ffe2e2' + green = '#66aa66' + darkgreen = '#3d663d' + blue = '#89b8df' + darkblue = '#000080' + orange = '#f08900' + graublau = '#acbdcd' + hellgraublau = '#d6ebff' + hhellgraublau = '#c1d4e6' + dunkelgraublau = '#808d99' + hdunkelgraublau = '#96a5b2' + dunkelblau = '#1010c0' + hellmagenta = '#d7cfec' + mittelmgenta = '#babadd' + dunkelmagenta = '#666699' + dunkelgraugelb = '#404822' + }; + + site.col = { + page = site.rgb.hellmagenta + text = site.rgb.black + head = site.rgb.dunkelmagenta + head_text = site.rgb.white + line = site.rgb.dunkelgraugelb + message = site.rgb.green + error = site.rgb.red + warn = site.rgb.darkgreen + alink = site.rgb.darkblue + statusbarbg = site.rgb.mittelmgenta + statusbartext = site.rgb.dunkelgraugelb + statusbarborder = site.rgb.dunkelgraugelb + footertext = site.rgb.dunkelgraublau + border = site.rgb.ganzdunkelgrau + table_bg = site.rgb.white + list_head = site.rgb.silbergrau + list_row = site.rgb.hellsilbergrau + list_row_bold = site.rgb.fastweiss + text_free_day = site.rgb.hellrot + text_hfree_day = site.rgb.dunkelbraun + bg_free_day = site.rgb.rosa + bg_hfree_day = site.rgb.hellrosa + border_hell = site.rgb.hellgraublau + border_dunkel = site.rgb.dunkelgraublau + bg_table = site.rgb.graublau + bg_table_head = site.rgb.hdunkelgraublau + link_table = site.rgb.dunkelblau + }; + +-%] + diff --git a/root/lib/config/main.tt2 b/root/lib/config/main.tt2 new file mode 100644 index 0000000..6f57bd3 --- /dev/null +++ b/root/lib/config/main.tt2 @@ -0,0 +1,26 @@ +[%# config/main + + $Id: main.tt2 1986 2008-03-04 14:25:18Z fbrehm $ + $URL$ + + This is the main configuration template which is processed before + any other page, by virtue of it being defined as a PRE_PROCESS + template. This is the place to define any extra template variables, + macros, load plugins, and perform any other template setup. + +-%] +[%- + + # define a data structure to hold sitewide data + site = { + title => 'Inhalt', + copyright => '2008, Frank Brehm, Berlin', + }; + + # load up any other configuration items + PROCESS config/colors.tt2 + config/url.tt2; + + # set defaults for variables, etc. + DEFAULT message = 'Es gibt nichts darzustellen.'; + +-%] diff --git a/root/lib/config/url.tt2 b/root/lib/config/url.tt2 new file mode 100644 index 0000000..8941f2b --- /dev/null +++ b/root/lib/config/url.tt2 @@ -0,0 +1,18 @@ +[%# + + config/url.tt2 + + # alle URL-Definitionen + # + # $Id: url.tt2 1986 2008-03-04 14:25:18Z fbrehm $ + # $URL$ + # +-%] +[%- base = Catalyst.req.base; + + site.url = { + base = base + home = "${base}welcome" + message = "${base}message" + } +-%] diff --git a/root/lib/site/footer.tt2 b/root/lib/site/footer.tt2 new file mode 100644 index 0000000..0eab06b --- /dev/null +++ b/root/lib/site/footer.tt2 @@ -0,0 +1,7 @@ +[%# + ** Template fuer Fusszeile + ** + ** $Id: styles.css 1986 2008-03-04 14:25:18Z fbrehm $ + ** $URL$ + ** -%] + diff --git a/root/lib/site/header.tt2 b/root/lib/site/header.tt2 new file mode 100644 index 0000000..305ce13 --- /dev/null +++ b/root/lib/site/header.tt2 @@ -0,0 +1,8 @@ +[%# + ** Template fuer Seitenkopf + ** + ** $Id: styles.css 1986 2008-03-04 14:25:18Z fbrehm $ + ** $URL$ + ** + ** -%] +

[% site_title or template.title or site.title %]

diff --git a/root/lib/site/html.tt2 b/root/lib/site/html.tt2 new file mode 100644 index 0000000..2b361f9 --- /dev/null +++ b/root/lib/site/html.tt2 @@ -0,0 +1,27 @@ +[%# + + Template fuer HTML-Seite als Ganzes + + $Id: html.tt2 1986 2008-03-04 14:25:18Z fbrehm $ + $URL$ + +-%] + + + + + + +[% PROCESS site/jsfiles.tt2 -%] + + [% site_title or template.title or site.title %] + +[% PROCESS site/jscript.tt2 %] + + +[% content %] + + diff --git a/root/lib/site/jscript.tt2 b/root/lib/site/jscript.tt2 new file mode 100644 index 0000000..24eb6e8 --- /dev/null +++ b/root/lib/site/jscript.tt2 @@ -0,0 +1,14 @@ +[%# + + Template fuer Einbindung reines JavaScripts + + $Id: jscript.tt2 1986 2008-03-04 14:25:18Z fbrehm $ + $URL$ + +-%] + diff --git a/root/lib/site/jsfiles.tt2 b/root/lib/site/jsfiles.tt2 new file mode 100644 index 0000000..d99929d --- /dev/null +++ b/root/lib/site/jsfiles.tt2 @@ -0,0 +1,11 @@ +[%# + + Template fuer HTML-Seite als Ganzes + + $Id: jsfiles.tt2 1986 2008-03-04 14:25:18Z fbrehm $ + $URL$ + +-%] +[%- FOREACH jsfile IN jsfiles %] + +[%- END %] diff --git a/root/lib/site/layout.tt2 b/root/lib/site/layout.tt2 new file mode 100644 index 0000000..d525e0c --- /dev/null +++ b/root/lib/site/layout.tt2 @@ -0,0 +1,15 @@ +[%# + ** Template fuer Allgemeines Seiten-Layout + ** + ** $Id: styles.css 1986 2008-03-04 14:25:18Z fbrehm $ + ** $URL$ + ** + ** -%] + +
[% PROCESS site/statusbar.tt2 %]
+ +
+[% content %] +
+ + diff --git a/root/lib/site/statusbar.tt2 b/root/lib/site/statusbar.tt2 new file mode 100644 index 0000000..34154b8 --- /dev/null +++ b/root/lib/site/statusbar.tt2 @@ -0,0 +1,9 @@ +[%# site/statusbar.tt2 + + Template fuer den Statusbalken / Menuezeile + + $Id: statusbar.tt2 1986 2008-03-04 14:25:18Z fbrehm $ + $URL$ + +-%] +  diff --git a/root/lib/site/styles.css b/root/lib/site/styles.css new file mode 100644 index 0000000..ffddbe7 --- /dev/null +++ b/root/lib/site/styles.css @@ -0,0 +1,173 @@ +[%# + ** Allgemeine Stylesheets + ** + ** $Id: styles.css 1986 2008-03-04 14:25:18Z fbrehm $ + ** $URL$ + ** %] + +/* --- site/styles.css ----------------- */ + +html { + /* height: 100%; */ +} + +body { + background-color: [% site.col.page %]; + color: [% site.col.text %]; + margin: 0px; + padding: 0px; + height: 100%; + font-family: Verdana,Helvetica,Arial; + font-size: 10pt; +} + +A { + color: [% site.col.alink %]; + text-decoration: none; +} + +A:link { + color: [% site.col.alink %]; +} + +A:hover { + color: [% site.col.text %]; + text-decoration: underline; +} + +A:active { + color: [% site.col.text %]; + text-decoration: none; +} + +A:focus { + color: [% site.col.text %]; + text-decoration: none; +} + +#header { + background-color: [% site.col.head %]; + color: [% site.col.head_text %]; + margin: 0; + padding: 5px; +} + +DIV#header H1 { + margin: 0px; + font-weight: normal; + font-size: 2em; + letter-spacing: 1pt; + text-align: right; +} + + + +#statusbar { + background-color: [% site.col.statusbarbg %]; + color: [% site.col.statusbartext %]; + border-bottom: 1px solid [% site.col.statusbarborder %]; + border-top: 1px solid [% site.col.statusbarborder %]; + margin: 0; + padding: 0; + padding: 3px 0px 3px 2%; + font-weight: bold; +} + +#statusbar TABLE { + color: [% site.col.statusbartext %]; + font-size: 0.9em; +} + +#statusbar TABLE TD { + padding: 2px; + vertical-align: middle; +} + +#statusbar TABLE TD SPAN.login { + font-weight: bolder; +} + +#statusbar A { + color: [% site.col.statusbartext %]; +} + +#statusbar A:link { + color: [% site.col.statusbartext %]; +} + +#statusbar A:hover { + color: [% site.col.statusbartext %]; +} + +#statusbar A:active { + color: [% site.col.statusbartext %]; +} + +/* Message-Box */ +DIV#message_box { + font-family: monospace; + font-size: 9pt; + text-align: left; + margin: 0; + white-space: pre; + padding: 0.5em 2em; +} + +#footer { + background-color: [% site.col.page %]; + border-top: 1px solid [% site.col.statusbarborder %]; + position: fixed; + width: 100%; + margin: 0; + padding: 0; + bottom: 0; + font-size: 0.8em; + color: [% site.col.footertext %]; +} + +TABLE { + border-collapse: collapse; +} + +#content { + padding: 10px; +} + +H1.title { + padding: 4px; + margin: 0px; +} + +H2 { + text-align: center; +} + +H2.footer_title { + text-align: center; + padding: 4px; + margin: 0px; +} + +H3 { + text-align: center; +} + +DIV#copyright { + text-align: center; + padding: 4px; + margin: 0px; +} + +.message { + color: [% site.col.message %]; +} + +.error { + color: [% site.col.error %]; +} + +[%- FOREACH cssfile IN cssfiles %] +/* --- [% cssfile %] ----------------- */ + [% PROCESS $cssfile -%] +[%- END %] + diff --git a/root/lib/site/wrapper.tt2 b/root/lib/site/wrapper.tt2 new file mode 100644 index 0000000..23a2ac2 --- /dev/null +++ b/root/lib/site/wrapper.tt2 @@ -0,0 +1,17 @@ +[%# + + Template fuer HTML-Seite als Ganzes + + $Id: wrapper.tt2 1986 2008-03-04 14:25:18Z fbrehm $ + $URL$ + +-%] +[%- + IF template.name.match('\.(css|js|txt)'); + debug("Passing page through as text: $template.name"); + content; + ELSE; + debug("Applying HTML page layout wrappers to $template.name\n"); + content WRAPPER site/html.tt2 + site/layout.tt2; + END; +-%] diff --git a/root/src/books/list.tt2 b/root/src/books/list.tt2 new file mode 100644 index 0000000..92f909e --- /dev/null +++ b/root/src/books/list.tt2 @@ -0,0 +1,32 @@ +[%# + ** Template fuer Buecherliste + ** + ** $Id: styles.css 1986 2008-03-04 14:25:18Z fbrehm $ + ** $URL$ + ** -%] + +[% META title = 'Bücherliste' -%] + + + + + + + + + + + +[% # Display each book in a table row %] +[% FOREACH book IN books -%][% buch_id = book.id -%] + + + + + + + + + +[% END -%] +
Autor (-en)TitelVerlagISBNBuch-NummerAusgabejahrPreis
[% tt_authors = [ ]; tt_authors.push(author) FOREACH author = buch2autor.$buch_id %][% tt_authors.join(', ') %][% book.title %][% IF book.title_original %] ([% book.title_original %])[% END %][% book.verlagsname_short %][% book.isbn %][% book.buch_nr %][% book.ausgabejahr %][% book.preis | format '%0.2f' | replace('\.', ',') %][% IF book.waehrungs_kuerzel %] [% book.waehrungs_kuerzel %][% END %]
diff --git a/root/src/books/styles.css b/root/src/books/styles.css new file mode 100644 index 0000000..4ec7514 --- /dev/null +++ b/root/src/books/styles.css @@ -0,0 +1,10 @@ +[%# + # Template fuer Stylesheets Buecherlisten + # + # $Id: styles.css 305 2008-03-14 13:53:44Z fbrehm $ + # $URL$ + # +-%] +/* Stylesheets Buecherlisten */ + + diff --git a/root/src/error.tt2 b/root/src/error.tt2 new file mode 100644 index 0000000..7b5f3b9 --- /dev/null +++ b/root/src/error.tt2 @@ -0,0 +1,14 @@ +[%# + + error.tt2 - Template zur Darstellung eines Template-Fehlers + + #$Id: error.tt2 1996 2008-03-05 14:06:03Z fbrehm $ + #$URL$ + +-%] +[% META title = 'Fehler' %] +

Ein Fehler ist aufgetreten.

+

Wir sind untröstlich darüber, aber es halt eines der Dinge, + die von Zeit zu Zeit auftreten können.

+

Hier die Fehlermeldung in der Hoffnung, daß sie Ihnen etwas sagen möge: + [% error %]

diff --git a/root/src/welcome.tt2 b/root/src/welcome.tt2 new file mode 100644 index 0000000..7c359b6 --- /dev/null +++ b/root/src/welcome.tt2 @@ -0,0 +1,15 @@ +[%# + + message.tt2 - Universal-Template + + #$Id: message.tt2 1986 2008-03-04 14:25:18Z fbrehm $ + #$URL$ + +-%] +[%- META title = 'Standard-Seite' %] +

Das ist die Standard-Seite.

+

Wir haben folgende Botschaft für Sie: [% message %].

+ +Verfügbare Sprachen:
    [% FOR lang IN avail_languages %] +
  • '[% lang %]'
  • +[% END %]
diff --git a/root/static/images/btn_120x50_built.png b/root/static/images/btn_120x50_built.png new file mode 100644 index 0000000000000000000000000000000000000000..c709fd67191037b89c6cfcdd821b9afb3b0447e7 GIT binary patch literal 3826 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 U;!MG3z7_z~G1jho3XU%Q9~)wzf&c&j literal 0 HcmV?d00001 diff --git a/root/static/images/btn_120x50_powered_shadow.png b/root/static/images/btn_120x50_powered_shadow.png new file mode 100644 index 0000000000000000000000000000000000000000..e6876c08fc78dcad5e4fd5a2e895f3522ad63a13 GIT binary patch literal 3673 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 UbTFT1G^xE3}C@ABzvgNF-{S6H` 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 a0|4eoa7M-5CQbkV002ovPDHLkV1l4dqzaP& literal 0 HcmV?d00001 diff --git a/root/static/images/catalyst_logo.png b/root/static/images/catalyst_logo.png new file mode 100644 index 0000000000000000000000000000000000000000..21f1cac7a9e12236982adf3f548dcaee560cbabb GIT binary patch literal 13710 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 literal 0 HcmV?d00001 diff --git a/root/static/pic/fbr.ico b/root/static/pic/fbr.ico new file mode 100644 index 0000000000000000000000000000000000000000..5a0537a4b4286ed90eb2a55a8f6f218cc2d488d3 GIT binary patch literal 626 zcmb_XF%H5o40HggW77qRts^rp;2}Js4?tz?%9l_EzEO*zBk9ykgtJ43O0C4uiyYa$ zM-;{(w1!aH4B`U# yt&2DV`cTfXX*fO|NB2N*|90%Vm-AVh`HpixunRZlQ1@-9t2SIK_H^FP$i**SYG<1O literal 0 HcmV?d00001 diff --git a/script/frbr_books_cgi.pl b/script/frbr_books_cgi.pl new file mode 100755 index 0000000..82fb25b --- /dev/null +++ b/script/frbr_books_cgi.pl @@ -0,0 +1,37 @@ +#!/usr/bin/perl -w + +BEGIN { $ENV{CATALYST_ENGINE} ||= 'CGI' } + +use strict; +use warnings; +use FindBin; +use lib "$FindBin::Bin/../lib"; +use FrBr::Books; + +FrBr::Books->run; + +1; + +=head1 NAME + +frbr_books_cgi.pl - Catalyst CGI + +=head1 SYNOPSIS + +See L + +=head1 DESCRIPTION + +Run a Catalyst application as a cgi script. + +=head1 AUTHOR + +Sebastian Riedel, C + +=head1 COPYRIGHT + + +This library is free software, you can redistribute it and/or modify +it under the same terms as Perl itself. + +=cut diff --git a/script/frbr_books_create.pl b/script/frbr_books_create.pl new file mode 100755 index 0000000..51a1e3d --- /dev/null +++ b/script/frbr_books_create.pl @@ -0,0 +1,75 @@ +#!/usr/bin/perl -w + +use strict; +use warnings; +use Getopt::Long; +use Pod::Usage; +use Catalyst::Helper; + +my $force = 0; +my $mech = 0; +my $help = 0; + +GetOptions( + 'nonew|force' => \$force, + 'mech|mechanize' => \$mech, + 'help|?' => \$help + ); + +pod2usage(1) if ( $help || !$ARGV[0] ); + +my $helper = Catalyst::Helper->new( { '.newfiles' => !$force, mech => $mech } ); + +pod2usage(1) unless $helper->mk_component( 'FrBr::Books', @ARGV ); + +1; + +=head1 NAME + +frbr_books_create.pl - Create a new Catalyst Component + +=head1 SYNOPSIS + +frbr_books_create.pl [options] model|view|controller name [helper] [options] + + Options: + -force don't create a .new file where a file to be created exists + -mechanize use Test::WWW::Mechanize::Catalyst for tests if available + -help display this help and exits + + Examples: + frbr_books_create.pl controller My::Controller + frbr_books_create.pl controller My::Controller BindLex + frbr_books_create.pl -mechanize controller My::Controller + frbr_books_create.pl view My::View + frbr_books_create.pl view MyView TT + frbr_books_create.pl view TT TT + frbr_books_create.pl model My::Model + frbr_books_create.pl model SomeDB DBIC::Schema MyApp::Schema create=dynamic\ + dbi:SQLite:/tmp/my.db + frbr_books_create.pl model AnotherDB DBIC::Schema MyApp::Schema create=static\ + dbi:Pg:dbname=foo root 4321 + + See also: + perldoc Catalyst::Manual + perldoc Catalyst::Manual::Intro + +=head1 DESCRIPTION + +Create a new Catalyst Component. + +Existing component files are not overwritten. If any of the component files +to be created already exist the file will be written with a '.new' suffix. +This behavior can be suppressed with the C<-force> option. + +=head1 AUTHOR + +Sebastian Riedel, C +Maintained by the Catalyst Core Team. + +=head1 COPYRIGHT + +This library is free software, you can redistribute it and/or modify +it under the same terms as Perl itself. + +=cut diff --git a/script/frbr_books_fastcgi.pl b/script/frbr_books_fastcgi.pl new file mode 100755 index 0000000..205800e --- /dev/null +++ b/script/frbr_books_fastcgi.pl @@ -0,0 +1,80 @@ +#!/usr/bin/perl -w + +BEGIN { $ENV{CATALYST_ENGINE} ||= 'FastCGI' } + +use strict; +use warnings; +use Getopt::Long; +use Pod::Usage; +use FindBin; +use lib "$FindBin::Bin/../lib"; +use FrBr::Books; + +my $help = 0; +my ( $listen, $nproc, $pidfile, $manager, $detach, $keep_stderr ); + +GetOptions( + 'help|?' => \$help, + 'listen|l=s' => \$listen, + 'nproc|n=i' => \$nproc, + 'pidfile|p=s' => \$pidfile, + 'manager|M=s' => \$manager, + 'daemon|d' => \$detach, + 'keeperr|e' => \$keep_stderr, +); + +pod2usage(1) if $help; + +FrBr::Books->run( + $listen, + { nproc => $nproc, + pidfile => $pidfile, + manager => $manager, + detach => $detach, + keep_stderr => $keep_stderr, + } +); + +1; + +=head1 NAME + +frbr_books_fastcgi.pl - Catalyst FastCGI + +=head1 SYNOPSIS + +frbr_books_fastcgi.pl [options] + + Options: + -? -help display this help and exits + -l -listen Socket path to listen on + (defaults to standard input) + can be HOST:PORT, :PORT or a + filesystem path + -n -nproc specify number of processes to keep + to serve requests (defaults to 1, + requires -listen) + -p -pidfile specify filename for pid file + (requires -listen) + -d -daemon daemonize (requires -listen) + -M -manager specify alternate process manager + (FCGI::ProcManager sub-class) + or empty string to disable + -e -keeperr send error messages to STDOUT, not + to the webserver + +=head1 DESCRIPTION + +Run a Catalyst application as fastcgi. + +=head1 AUTHOR + +Sebastian Riedel, C +Maintained by the Catalyst Core Team. + +=head1 COPYRIGHT + +This library is free software, you can redistribute it and/or modify +it under the same terms as Perl itself. + +=cut diff --git a/script/frbr_books_server.pl b/script/frbr_books_server.pl new file mode 100755 index 0000000..f51db84 --- /dev/null +++ b/script/frbr_books_server.pl @@ -0,0 +1,111 @@ +#!/usr/bin/perl -w + +BEGIN { + $ENV{CATALYST_ENGINE} ||= 'HTTP'; + $ENV{CATALYST_SCRIPT_GEN} = 30; + require Catalyst::Engine::HTTP; +} + +use strict; +use warnings; +use Getopt::Long; +use Pod::Usage; +use FindBin; +use lib "$FindBin::Bin/../lib"; + +my $debug = 0; +my $fork = 0; +my $help = 0; +my $host = undef; +my $port = $ENV{FRBR_BOOKS_PORT} || $ENV{CATALYST_PORT} || 3000; +my $keepalive = 0; +my $restart = $ENV{FRBR_BOOKS_RELOAD} || $ENV{CATALYST_RELOAD} || 0; +my $restart_delay = 1; +my $restart_regex = '\.yml$|\.yaml$|\.pm$'; +my $restart_directory = undef; + +my @argv = @ARGV; + +GetOptions( + 'debug|d' => \$debug, + 'fork' => \$fork, + 'help|?' => \$help, + 'host=s' => \$host, + 'port=s' => \$port, + 'keepalive|k' => \$keepalive, + 'restart|r' => \$restart, + 'restartdelay|rd=s' => \$restart_delay, + 'restartregex|rr=s' => \$restart_regex, + 'restartdirectory=s' => \$restart_directory, +); + +pod2usage(1) if $help; + +if ( $restart && $ENV{CATALYST_ENGINE} eq 'HTTP' ) { + $ENV{CATALYST_ENGINE} = 'HTTP::Restarter'; +} +if ( $debug ) { + $ENV{CATALYST_DEBUG} = 1; +} + +# This is require instead of use so that the above environment +# variables can be set at runtime. +require FrBr::Books; + +FrBr::Books->run( $port, $host, { + argv => \@argv, + 'fork' => $fork, + keepalive => $keepalive, + restart => $restart, + restart_delay => $restart_delay, + restart_regex => qr/$restart_regex/, + restart_directory => $restart_directory, +} ); + +1; + +=head1 NAME + +frbr_books_server.pl - Catalyst Testserver + +=head1 SYNOPSIS + +frbr_books_server.pl [options] + + Options: + -d -debug force debug mode + -f -fork handle each request in a new process + (defaults to false) + -? -help display this help and exits + -host host (defaults to all) + -p -port port (defaults to 3000) + -k -keepalive enable keep-alive connections + -r -restart restart when files get modified + (defaults to false) + -rd -restartdelay delay between file checks + -rr -restartregex regex match files that trigger + a restart when modified + (defaults to '\.yml$|\.yaml$|\.pm$') + -restartdirectory the directory to search for + modified files + (defaults to '../') + + See also: + perldoc Catalyst::Manual + perldoc Catalyst::Manual::Intro + +=head1 DESCRIPTION + +Run a Catalyst Testserver for this application. + +=head1 AUTHOR + +Sebastian Riedel, C +Maintained by the Catalyst Core Team. + +=head1 COPYRIGHT + +This library is free software, you can redistribute it and/or modify +it under the same terms as Perl itself. + +=cut diff --git a/script/frbr_books_test.pl b/script/frbr_books_test.pl new file mode 100755 index 0000000..16e6f12 --- /dev/null +++ b/script/frbr_books_test.pl @@ -0,0 +1,54 @@ +#!/usr/bin/perl -w + +use strict; +use warnings; +use Getopt::Long; +use Pod::Usage; +use FindBin; +use lib "$FindBin::Bin/../lib"; +use Catalyst::Test 'FrBr::Books'; + +my $help = 0; + +GetOptions( 'help|?' => \$help ); + +pod2usage(1) if ( $help || !$ARGV[0] ); + +print request($ARGV[0])->content . "\n"; + +1; + +=head1 NAME + +frbr_books_test.pl - Catalyst Test + +=head1 SYNOPSIS + +frbr_books_test.pl [options] uri + + Options: + -help display this help and exits + + Examples: + frbr_books_test.pl http://localhost/some_action + frbr_books_test.pl /some_action + + See also: + perldoc Catalyst::Manual + perldoc Catalyst::Manual::Intro + +=head1 DESCRIPTION + +Run a Catalyst action from the command line. + +=head1 AUTHOR + +Sebastian Riedel, C +Maintained by the Catalyst Core Team. + +=head1 COPYRIGHT + +This library is free software, you can redistribute it and/or modify +it under the same terms as Perl itself. + +=cut diff --git a/t/01app.t b/t/01app.t new file mode 100644 index 0000000..c9c341b --- /dev/null +++ b/t/01app.t @@ -0,0 +1,7 @@ +use strict; +use warnings; +use Test::More tests => 2; + +BEGIN { use_ok 'Catalyst::Test', 'FrBr::Books' } + +ok( request('/')->is_success, 'Request should succeed' ); diff --git a/t/02pod.t b/t/02pod.t new file mode 100644 index 0000000..251640d --- /dev/null +++ b/t/02pod.t @@ -0,0 +1,9 @@ +use strict; +use warnings; +use Test::More; + +eval "use Test::Pod 1.14"; +plan skip_all => 'Test::Pod 1.14 required' if $@; +plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD}; + +all_pod_files_ok(); diff --git a/t/03podcoverage.t b/t/03podcoverage.t new file mode 100644 index 0000000..ae59d4c --- /dev/null +++ b/t/03podcoverage.t @@ -0,0 +1,9 @@ +use strict; +use warnings; +use Test::More; + +eval "use Test::Pod::Coverage 1.04"; +plan skip_all => 'Test::Pod::Coverage 1.04 required' if $@; +plan skip_all => 'set TEST_POD to enable this test' unless $ENV{TEST_POD}; + +all_pod_coverage_ok(); diff --git a/t/controller_Books.t b/t/controller_Books.t new file mode 100644 index 0000000..e8de2c9 --- /dev/null +++ b/t/controller_Books.t @@ -0,0 +1,10 @@ +use strict; +use warnings; +use Test::More tests => 3; + +BEGIN { use_ok 'Catalyst::Test', 'FrBr::Books' } +BEGIN { use_ok 'FrBr::Books::Controller::Books' } + +ok( request('/books')->is_success, 'Request should succeed' ); + + diff --git a/t/model_Schema.t b/t/model_Schema.t new file mode 100644 index 0000000..3b50119 --- /dev/null +++ b/t/model_Schema.t @@ -0,0 +1,6 @@ +use strict; +use warnings; +use Test::More tests => 1; + +BEGIN { use_ok 'FrBr::Books::Model::Schema' } + diff --git a/t/view_TtDefault.t b/t/view_TtDefault.t new file mode 100644 index 0000000..9815fcf --- /dev/null +++ b/t/view_TtDefault.t @@ -0,0 +1,6 @@ +use strict; +use warnings; +use Test::More tests => 1; + +BEGIN { use_ok 'FrBr::Books::View::TtDefault' } + -- 2.39.5