aboutsummaryrefslogtreecommitdiffstats
path: root/perllib/FixMyStreet/App.pm
diff options
context:
space:
mode:
Diffstat (limited to 'perllib/FixMyStreet/App.pm')
-rw-r--r--perllib/FixMyStreet/App.pm407
1 files changed, 407 insertions, 0 deletions
diff --git a/perllib/FixMyStreet/App.pm b/perllib/FixMyStreet/App.pm
new file mode 100644
index 000000000..68bfc728b
--- /dev/null
+++ b/perllib/FixMyStreet/App.pm
@@ -0,0 +1,407 @@
+package FixMyStreet::App;
+use Moose;
+use namespace::autoclean;
+
+use Catalyst::Runtime 5.80;
+use FixMyStreet;
+use FixMyStreet::Cobrand;
+use Memcached;
+use mySociety::Email;
+use mySociety::EmailUtil;
+use mySociety::Random qw(random_bytes);
+use FixMyStreet::Map;
+
+use URI;
+use URI::QueryParam;
+
+use Catalyst (
+ 'Static::Simple', #
+ 'Unicode::Encoding',
+ 'Session',
+ 'Session::Store::DBIC',
+ 'Session::State::Cookie', # FIXME - we're using our own override atm
+ 'Authentication',
+ 'SmartURI',
+);
+
+extends 'Catalyst';
+
+our $VERSION = '0.01';
+
+__PACKAGE__->config(
+
+ # get the config from the core object
+ %{ FixMyStreet->config() },
+
+ name => 'FixMyStreet::App',
+
+ encoding => 'UTF-8',
+
+ # Disable deprecated behavior needed by old applications
+ disable_component_resolution_regex_fallback => 1,
+
+ # Some generic stuff
+ default_view => 'Web',
+
+ # Serve anything in web dir that is not a .cgi script
+ static => { #
+ include_path => [ FixMyStreet->path_to("web") . "" ],
+ ignore_extensions => ['cgi'],
+ },
+
+ 'Plugin::Session' => { # Catalyst::Plugin::Session::Store::DBIC
+ dbic_class => 'DB::Session',
+ expires => 3600 * 24 * 7 * 4, # 4 weeks
+ },
+
+ 'Plugin::Authentication' => {
+ default_realm => 'default',
+ default => {
+ credential => { # Catalyst::Authentication::Credential::Password
+ class => 'Password',
+ password_field => 'password',
+ password_type => 'self_check',
+ },
+ store => { # Catalyst::Authentication::Store::DBIx::Class
+ class => 'DBIx::Class',
+ user_model => 'DB::User',
+ },
+ },
+ no_password => { # use post confirm etc
+ credential => { # Catalyst::Authentication::Credential::Password
+ class => 'Password',
+ password_type => 'none',
+ },
+ store => { # Catalyst::Authentication::Store::DBIx::Class
+ class => 'DBIx::Class',
+ user_model => 'DB::User',
+ },
+ },
+ },
+);
+
+# Start the application
+__PACKAGE__->setup();
+
+# set up DB handle for old code
+FixMyStreet->configure_mysociety_dbhandle;
+
+# disable debug logging unless in debug mode
+__PACKAGE__->log->disable('debug') #
+ unless __PACKAGE__->debug;
+
+=head1 NAME
+
+FixMyStreet::App - Catalyst based application
+
+=head1 SYNOPSIS
+
+ script/fixmystreet_app_server.pl
+
+=head1 DESCRIPTION
+
+FixMyStreet.com codebase
+
+=head1 METHODS
+
+=head2 cobrand
+
+ $cobrand = $c->cobrand();
+
+Returns the cobrand object. If not already determined this request finds it and
+caches it to the stash.
+
+=cut
+
+sub cobrand {
+ my $c = shift;
+ return $c->stash->{cobrand} ||= $c->_get_cobrand();
+}
+
+sub _get_cobrand {
+ my $c = shift;
+
+ my $host = $c->req->uri->host;
+ my $override_moniker = $c->get_override('cobrand_moniker');
+
+ my $cobrand_class =
+ $override_moniker
+ ? FixMyStreet::Cobrand->get_class_for_moniker($override_moniker)
+ : FixMyStreet::Cobrand->get_class_for_host($host);
+
+ my $cobrand = $cobrand_class->new( { c => $c } );
+
+ return $cobrand;
+}
+
+=head2 setup_request
+
+ $cobrand = $c->setup_request();
+
+Work out which cobrand we should be using. Set the environment correctly - eg
+template paths, maps, languages etc, etc.
+
+=cut
+
+sub setup_request {
+ my $c = shift;
+
+ $c->setup_dev_overrides();
+
+ my $cobrand = $c->cobrand;
+
+ # append the cobrand templates to the include path
+ $c->stash->{additional_template_paths} =
+ [ $cobrand->path_to_web_templates->stringify ]
+ unless $cobrand->is_default;
+
+ # work out which language to use
+ my $lang_override = $c->get_override('lang');
+ my $host = $c->req->uri->host;
+ my $lang =
+ $lang_override ? $lang_override
+ : $host =~ /^en\./ ? 'en-gb'
+ : $host =~ /cy/ ? 'cy'
+ : undef;
+
+ # set the language and the translation file to use - store it on stash
+ my $set_lang = $cobrand->set_lang_and_domain(
+ $lang, # language
+ 1, # return unicode
+ FixMyStreet->path_to('locale')->stringify # use locale directory
+ );
+ $c->stash->{lang_code} = $set_lang;
+
+ # debug
+ $c->log->debug( sprintf "Set lang to '%s' and cobrand to '%s'",
+ $set_lang, $cobrand->moniker );
+
+ $c->model('DB::Problem')->set_restriction( $cobrand->site_restriction() );
+
+ Memcached::set_namespace( FixMyStreet->config('BCI_DB_NAME') . ":" );
+
+ my $map = $host =~ /^osm\./ ? 'OSM' : $c->req->param('map_override');
+ #if ($c->sessionid) {
+ # $map = $c->session->{map};
+ # $map = undef unless $map eq 'OSM';
+ #}
+ FixMyStreet::Map::set_map_class( $map );
+
+ return $c;
+}
+
+=head2 setup_dev_overrides
+
+ $c->setup_dev_overrides();
+
+This is only run if STAGING_SITE is true.
+
+It is intended as an easy way to change the cobrand, language, map etc etc etc
+without having to muck around with domain names and so on. The overrides are set
+by passing _override_xxx parameters in the query. The values and stored in the
+session and are used in preference to the defaults.
+
+All overrides can be easily cleared by setting the _override_clear_all parameter
+to true.
+
+=cut
+
+sub setup_dev_overrides {
+ my $c = shift;
+
+ # If not on STAGING_SITE bail out
+ return unless $c->config->{STAGING_SITE};
+
+ # Extract all the _override_xxx parameters
+ my %params = %{ $c->req->parameters };
+ delete $params{$_} for grep { !m{^_override_} } keys %params;
+
+ # stop if there is nothing to add
+ return 1 unless scalar keys %params;
+
+ # Check to see if we should clear all
+ if ( $params{_override_clear_all} ) {
+ delete $c->session->{overrides};
+ return;
+ }
+
+ # check for all the other _override params and set their values
+ my $overrides = $c->session->{overrides} ||= {};
+ foreach my $raw_key ( keys %params ) {
+ my ($key) = $raw_key =~ m{^_override_(.*)$};
+ $overrides->{$key} = $params{$raw_key};
+ }
+
+ return $overrides;
+}
+
+=head2 get_override
+
+ $value = $c->get_override( 'cobrand_moniker' );
+
+Checks the overrides for the value given and returns it if found, undef if not.
+
+Always returns undef unless on a staging site (avoids autovivifying overrides
+hash in session and so creating a session for all users).
+
+=cut
+
+sub get_override {
+ my ( $c, $key ) = @_;
+ return unless $c->config->{STAGING_SITE};
+ return $c->session->{overrides}->{$key};
+}
+
+=head2 send_email
+
+ $email_sent = $c->send_email( 'email_template.txt', $extra_stash_values );
+
+Send an email by filling in the given template with values in the stash.
+
+You can specify extra values to those already in the stash by passing a hashref
+as the second argument.
+
+The stash (or extra_stash_values) keys 'to', 'from' and 'subject' are used to
+set those fields in the email if they are present.
+
+If a 'from' is not specified then the default from the config is used.
+
+=cut
+
+sub send_email {
+ my $c = shift;
+ my $template = shift;
+ my $extra_stash_values = shift || {};
+
+ my $sender = $c->cobrand->contact_email;
+ my $sender_name = $c->cobrand->contact_name;
+ $sender =~ s/team/fms-DO-NOT-REPLY/;
+
+ # create the vars to pass to the email template
+ my $vars = {
+ from => [ $sender, _($sender_name) ],
+ %{ $c->stash },
+ %$extra_stash_values,
+ additional_template_paths => [
+ FixMyStreet->path_to( 'templates', 'email', $c->cobrand->moniker, $c->stash->{lang_code} )->stringify,
+ FixMyStreet->path_to( 'templates', 'email', $c->cobrand->moniker )->stringify,
+ ]
+ };
+
+ # render the template
+ my $content = $c->view('Email')->render( $c, $template, $vars );
+
+ # create an email - will parse headers out of content
+ my $email = Email::Simple->new($content);
+ $email->header_set( ucfirst($_), $vars->{$_} )
+ for grep { $vars->{$_} } qw( to from subject);
+
+ # pass the email into mySociety::Email to construct the on the wire 7bit
+ # format - this should probably happen in the transport instead but hohum.
+ my $email_text = mySociety::Locale::in_gb_locale { mySociety::Email::construct_email(
+ {
+ _template_ => $email->body, # will get line wrapped
+ _parameters_ => {},
+ $email->header_pairs
+ }
+ ) };
+
+ # send the email
+ $c->model('EmailSend')->send($email_text);
+
+ return $email;
+}
+
+sub send_email_cron {
+ my ( $c, $params, $env_from, $env_to, $nomail ) = @_;
+
+ $params->{'Message-ID'} = sprintf('<fms-cron-%s-%s@mysociety.org>', time(),
+ unpack('h*', random_bytes(5, 1))
+ );
+
+ my $email = mySociety::Locale::in_gb_locale { mySociety::Email::construct_email($params) };
+
+ if ( FixMyStreet->test_mode ) {
+ my $sender = Email::Send->new({ mailer => 'Test' });
+ $sender->send( $email );
+ return 0;
+ } elsif (!$nomail) {
+ return mySociety::EmailUtil::send_email( $email, $env_from, @$env_to );
+ } else {
+ print $email;
+ return 1; # Failure
+ }
+}
+
+=head2 uri_with
+
+ $uri = $c->uri_with( ... );
+
+Simply forwards on to $c->req->uri_with - this is a common typo I make!
+
+=cut
+
+sub uri_with {
+ my $c = shift;
+ return $c->req->uri_with(@_);
+}
+
+=head2 uri_for
+
+ $uri = $c->uri_for( ... );
+
+Like C<uri_for> except that it passes the uri to the cobrand to be altered if
+needed.
+
+=cut
+
+sub uri_for {
+ my $c = shift;
+ my @args = @_;
+
+ my $uri = $c->next::method(@args);
+
+ my $cobranded_uri = $c->cobrand->uri($uri);
+
+ # note that the returned uri may be a string not an object (eg cities)
+ return $cobranded_uri;
+}
+
+=head2 uri_for_email
+
+ $uri = $c->uri_for_email( ... );
+
+Like C<uri_for> except that it checks the cobrand for an email specific url base
+and uses that.
+
+=cut
+
+sub uri_for_email {
+ my $c = shift;
+ my @args = @_;
+
+ my $normal_uri = $c->uri_for(@_)->absolute;
+ my $base = $c->cobrand->base_url_with_lang( 1 );
+
+ my $email_uri = $base . $normal_uri->path_query;
+
+ return URI->new($email_uri);
+}
+
+sub finalize {
+ my $c = shift;
+ $c->next::method(@_);
+
+ # cobrand holds on to a reference to $c so we want to
+ # get git rid of this to stop circular references and
+ # memory leaks
+ delete $c->stash->{cobrand};
+}
+
+=head1 SEE ALSO
+
+L<FixMyStreet::App::Controller::Root>, L<Catalyst>
+
+=cut
+
+1;