{"id":23,"date":"2013-04-06T17:29:09","date_gmt":"2013-04-06T17:29:09","guid":{"rendered":"http:\/\/www.aibistin.com\/?p=23"},"modified":"2017-03-12T15:29:18","modified_gmt":"2017-03-12T19:29:18","slug":"upload-and-email-a-photo-using-dancer2-and-twitter-bootstrap","status":"publish","type":"post","link":"https:\/\/www.aibistin.com\/?p=23","title":{"rendered":"Upload and Email a photo using Dancer2 with Twitter Bootstrap"},"content":{"rendered":"<p>This is just a short demonstration on uploading a photograph, previewing the photo, and then emailing the photo to a user defined email address.<\/p>\n<p>I will use the <a href=\"http:\/\/www.perl.org\">Perl<\/a>, <a href=\"https:\/\/metacpan.org\/module\/Dancer2\" target=\"_blank\">Dancer2<\/a> a lightweight web application framework, <a href=\"https:\/\/metacpan.org\/module\/ABW\/Template-Toolkit-2.24\/lib\/inlineTemplate\/Toolkit.pod\" target=\"_blank\">Template Toolkit<\/a>, and <a href=\"http:\/\/jasny.github.io\/bootstrap\/index.html\" target=\"_blank\">Bootstrap<\/a>.<\/p>\n<p>You can set up Gmail or Hotmail to act as your mail server. See <a href=\"&quot;http:\/\/www.havetheknowhow.com\/Configure-the-server\/Install-ssmtp.html\">here<\/a> or <a href=\"&quot;http:\/\/tombuntu.com\/index.php\/2008\/10\/21\/sending-email-from-your-system-with-ssmtp\/\">here<\/a> for useful guides on setting up sSMTP on your linux laptop.<\/p>\n<p class=\"code-heading\">Configuration:<\/p>\n<p>First, set up the Dancer2 configuration file. The original Dancer comes with a helper script which will set up libraries, config files etc. I used this and converted what I needed to Dancer2(All is explained <a href=\"https:\/\/metacpan.org\/module\/SUKRIA\/Dancer2-0.03\/lib\/Dancer2\/Manual.pod\">here<\/a>. I prefer <a href=\"https:\/\/metacpan.org\/module\/Config::General\">Config::General<\/a> format to YMAL so I changed the config file to Config::General format and the file suffix from &#8220;.yml&#8221; to &#8220;.conf&#8221;.<\/p>\n<p><span class=\"code-description\">Main Configuration File.<\/span><\/p>\n<p>This config file, written in <a href=\"https:\/\/metacpan.org\/module\/Config::General\">Config::General<\/a> format, provides the application with the main view layout( or &#8220;wrapper&#8221; ) name, &#8220;main.tt&#8221;. It also notifies the application that we intend to use Template::Toolkit for our views as opposed to the Dancer2::Template::Simple. I specify that the Template will use &#8220;[%&#8221; style tags as opposed to the Dancer default of &#8220;&lt;%&#8221; tags. This may no longer be necessary with Dancer2.<br \/>\nWe also include UTF-8 encoding for good measure.<\/p>\n<pre class=\"brush: css; auto-links: false; collapse: false; first-line: 1; gutter: true; html-script: false; light: false; pad-line-numbers: false; smart-tabs: true; tab-size: 4; title: config.conf; toolbar: true; notranslate\" title=\"config.conf\">\r\nappname = &quot;PhotoUp&quot;\r\nlayout = &quot;main&quot;\r\ncharset = &quot;UTF-8&quot;\r\n&lt;engines&gt;\r\n  &lt;template_toolkit&gt;\r\n    ENCODING = utf8\r\n    start_tag = '&#x5B;%'\r\n    end_tag = '%]'\r\n &lt;\/template_toolkit&gt;\r\n&lt;\/engines&gt;\r\n<\/pre>\n<p><span class=\"code-description\">Development Configuration File<\/span><\/p>\n<p>Most of this, except the Input File and Email sections are set up automatically for you using the Dancer helper script. I changed the logging to output to a file (logs\/development.log). I also added some configuration for sending the email via sSMTP.<br \/>\nThe input file max size and other information can be specified here too. The more constraints that are added here, the less hard coding of data will be needed in the Perl Module.<\/p>\n<pre class=\"brush: css; auto-links: false; collapse: false; first-line: 1; gutter: true; html-script: false; light: false; pad-line-numbers: false; smart-tabs: true; tab-size: 4; title: development.conf; toolbar: true; notranslate\" title=\"development.conf\">\r\n\r\nlogger = &quot;file&quot;\r\nlog = &quot;debug&quot;\r\nwarnings = 1\r\nshow_errors = 1\r\n&lt;InputFile&gt;\r\n  max_file_size = 1000000 # Kbs\r\n  file_suffix = .jpeg\r\n&lt;\/InputFile&gt;\r\n\r\n&lt;Email&gt;\r\n  transport = SMTP-TLS\r\n &lt;SMP-TLS&gt;\r\n    host = smtp.live.com # hotmail\r\n    username = xxxxxxxn@xxxxxxxxx.com\r\n    password = &quot;password&quot;\r\n    port = 587\r\n  &lt;\/SMP-TLS&gt;\r\n  from = xxxxxxxxxxxx@hotmail.com\r\n  to = xxxxxx@xxxxxxxxx.com\r\n  cc = xxx@xxxxxxxx.com\r\n  subject = Here are your photos.\r\n  signed = The Boss\r\n&lt;\/Email&gt;\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>&nbsp;<\/p>\n<p class=\"code-heading\">Views:<\/p>\n<p>I use Template Toolkit for my templating. Dancer2 uses a &#8220;layout&#8221; as a type of wrapper for your template. Here is mine located at views\/layouts\/main.tt.<\/p>\n<p><span class=\"code-description\">Layout.<\/span><\/p>\n<pre class=\"brush: css; auto-links: false; collapse: false; first-line: 1; gutter: true; highlight: [15,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65]; html-script: true; light: false; pad-line-numbers: false; smart-tabs: true; tab-size: 4; title: main.tt; toolbar: true; notranslate\" title=\"main.tt\">\r\n\r\n&lt;!DOCTYPE html&gt;\r\n&lt;html lang=&quot;en&quot;&gt;    \r\n&lt;meta charset=&quot;utf-8&quot;&gt;\r\n&lt;head&gt;\r\n&#x5B;% title %]\r\n&lt;meta name=&quot;viewport&quot; content=&quot;width=device-width, initial-scale=1.0&quot; \/&gt;\r\n&lt;meta name=&quot;description&quot; content=&quot;Photo Sender&quot; \/&gt;\r\n&lt;meta name=&quot;author&quot; content=&quot;austin kenny&quot; \/&gt;\r\n  &lt;!-- Bootstrap File Upload CSS --&gt;\r\n  &lt;link href=&quot;&#x5B;% request.uri_base %]\/css\/bootstrap\/bootstrap.css&quot; rel=&quot;stylesheet&quot; \/&gt; \r\n  &lt;link href=&quot;&#x5B;% request.uri_base %]\/css\/bootstrap\/bootstrap-responsive.css&quot; rel=&quot;stylesheet&quot; \/&gt; \r\n  &lt;link href=&quot;&#x5B;% request.uri_base %]\/css\/bootstrap\/bootstrap-fileupload.css&quot; rel=&quot;stylesheet&quot; \/&gt; \r\n&lt;\/head&gt;\r\n&lt;body&gt;\r\n  &#x5B;% content %]\r\n&lt;div class=&quot;text-center&quot; id=&quot;footer&quot;&gt;\r\n &lt;small&gt; Powered by &lt;a href=&quot;http:\/\/perl.org\/&quot;&gt;Perl&lt;\/a&gt;\r\n &lt;a href=&quot;https:\/\/metacpan.org\/module\/Dancer2&quot;&gt;Dancer2&lt;\/a&gt;\r\n &lt;a href=&quot;http:\/\/jasny.github.io\/bootstrap\/index.html&quot;&gt;Bootstrap&lt;\/a&gt; &lt;\/small&gt;&lt;\/div&gt;\r\n&lt;\/body&gt;\r\n&lt;!-- Grab Google CDN's jQuery. fall back to local if necessary --&gt;&lt;script type=&quot;text\/javascript&quot; src=&quot;\/\/ajax.googleapis.com\/ajax\/libs\/jquery\/1.9.1\/jquery.min.js&quot;&gt;&lt;\/script&gt;&lt;script type=&quot;text\/javascript&quot;&gt;\/\/ &lt;!&#x5B;CDATA&#x5B;\r\n!window.jQuery &amp;#038;&amp; document.write('&lt;script type=&quot;text\/javascript&quot; src=&quot;&#x5B;% request.uri_base %]\/javascripts\/jquery.js&quot;&gt;&lt;\\\/script&gt;')\r\n\/\/ ]]&gt;&lt;\/script&gt;&lt;!-- Bootstrap File Upload Js --&gt;&lt;script type=&quot;text\/javascript&quot; src=&quot;&#x5B;% request.uri_base %]\/javascripts\/bootstrap-fileupload.js&quot;&gt;&lt;\/script&gt;\r\n<\/pre>\n<p>I have included all the Bootstrap CSS links along with the Bootstrap JavaScript to provide the fancy photo\/image preview etc. It is also necessary to provide links to JQuery in order for the Bootstrap code to work. Notice the <code>[% content %]<\/code> placeholder on line 14. This is where the page specific content(photo_upload.tt) goes.<\/p>\n<p><span class=\"code-description\">Upload Page View.<\/span><\/p>\n<p>This template view (photo_upload.tt), it the first page that is rendered when the application is called.It contains the &#8220;Browse&#8221; button for searching for the file\/image to be uploaded on the client&#8217;s device. It also has an email to field, along with the necessary send button.<br \/>\nMost of the initial heavy lifting will be done by the jQuery upload and preview script, provided by Twitter Bootstrap. When the file\/image is previewed, it can be emailed, or the user can opt to discard it in favour of another file\/image.<\/p>\n<pre class=\"brush: css; auto-links: false; collapse: false; first-line: 1; html-script: true; light: false; pad-line-numbers: false; smart-tabs: true; tab-size: 4; title: photo_upload.tt; toolbar: true; notranslate\" title=\"photo_upload.tt\">\r\n\r\n&lt;div class=&quot;container&quot;&gt;\r\n    &lt;!-- Browse and upload file form: --&gt;\r\n    &lt;form class=&quot;form-file-upload file-upload &quot; enctype=&quot;multipart\/form-data&quot;\r\n    name=&quot;photoUploadForm&quot;  action=&quot;\/photo_upload&quot; method=&quot;POST&quot;&gt;\r\n    &lt;fieldset&gt;\r\n\r\n        &lt;legend class=&quot;form-file-upload-heading&quot;&gt;Email Your Photos&lt;\/legend&gt;\r\n        &lt;!-- Start Bootstrap Template --&gt;\r\n        &lt;div class=&quot;fileupload fileupload-new&quot; data-provides=&quot;fileupload&quot;&gt;\r\n            &lt;div class=&quot;fileupload-new thumbnail&quot; style=&quot;width: 200px; height: 150px;&quot;&gt;\r\n              &lt;img src=&quot;http:\/\/www.placehold.it\/200x150\/EFEFEF\/AAAAAA&amp;text=no+image&quot; \/&gt;\r\n            &lt;\/div&gt;\r\n            &lt;div class=&quot;fileupload-preview fileupload-exists thumbnail&quot;\r\n             style=&quot;max-width: 200px; max-height: 150px; line-height: 20px;&quot;&gt;\r\n            &lt;\/div&gt;\r\n            &lt;div&gt;\r\n               &lt;span class=&quot;btn btn-file&quot;&gt;&lt;span class=&quot;fileupload-new&quot;&gt;Select image&lt;\/span&gt;\r\n               &lt;span class=&quot;fileupload-exists&quot;&gt;Change&lt;\/span&gt;\r\n               &lt;input type=&quot;file&quot;  name=&quot;inPhoto&quot;\/&gt;\r\n               &lt;\/span&gt;\r\n               &lt;a href=&quot;in_photo&quot; class=&quot;btn fileupload-exists&quot; data-dismiss=&quot;fileupload&quot;&gt;Remove&lt;\/a&gt; \r\n                &lt;span class=&quot;text-error&quot;&gt;&#x5B;%- upload_error -%]&lt;\/span&gt;\r\n            &lt;\/div&gt; \r\n        &lt;\/div&gt;\r\n        &lt;!-- End Bootstrap Template --&gt;\r\n        &lt;div class=&quot;input-append&quot;&gt;\r\n          &lt;!-- Mail-to Email address --&gt;\r\n          &lt;input class=&quot;input-large&quot; type=&quot;email&quot; name=&quot;emailTo&quot;\r\n          placeholder=&quot;Email to&quot; maxlength=&quot;60&quot; \/&gt;\r\n          &lt;button class=&quot;btn btn&quot; type=&quot;submit&quot; &gt;Email Photo&lt;\/button&gt;\r\n        &lt;\/div&gt;\r\n        &lt;div&gt;\r\n           &lt;span class=&quot;text-error&quot;&gt;&#x5B;%- email_error -%]&lt;\/span&gt;\r\n        &lt;\/div&gt;\r\n\r\n    &lt;\/fieldset&gt;\r\n    &lt;\/form&gt;\r\n    &lt;!--   &lt;div id=&quot;progress&quot;&gt;&lt;\/div&gt; --&gt;\r\n    &lt;div id=&quot;messages&quot;&gt;\r\n    &lt;p class=&quot;text-success&quot;&gt;&#x5B;%- success_message -%]&lt;\/p&gt;\r\n    &lt;p class=&quot;text-info&quot;&gt;&#x5B;%- info_message -%]&lt;\/p&gt;\r\n    &lt;p class=&quot;text-warning&quot;&gt;&#x5B;%- warning_message -%]&lt;\/p&gt;\r\n    &lt;p class=&quot;text-error&quot;&gt;&#x5B;%- error_message -%]&lt;\/p&gt;\r\n    &lt;\/div&gt;\r\n\r\n&lt;\/div&gt; &lt;!-- \/container --&gt;\r\n<\/pre>\n<p>&nbsp;<\/p>\n<p>Most of this comes straight from the <a title=\"Bootstrap File Upload\" href=\"http:\/\/jasny.github.io\/bootstrap\/javascript.html#fileupload\">Jasny Bootstrap documentation.<\/a><\/p>\n<p>Now it is just a matter of writing the\u00a0 Dancer2 script.<\/p>\n<p>&nbsp;<\/p>\n<p class=\"code-heading\">Dancer2 Module<\/p>\n<pre class=\"brush: css; auto-links: false; collapse: false; first-line: 1; gutter: true; light: false; pad-line-numbers: false; smart-tabs: false; tab-size: 4; title: PhotoUp.pm; toolbar: true; notranslate\" title=\"PhotoUp.pm\">\r\n\r\npackage PhotoUp;\r\n# ABSTRACT: Upload and email photo(s) demo using ssmpt and Hotmail\r\nuse Dancer2;\r\nuse Dancer2::Core::Error;\r\n\r\nour $VERSION = '0.1';\r\n\r\nuse Data::Dump qw\/dump\/;\r\nuse Carp qw\/croak\/;\r\nuse Try::Tiny;\r\nuse IO::All;\r\nuse File::LibMagic;\r\n\r\n#------Email\r\nuse Email::Valid;\r\nuse Email::Sender::Simple qw(sendmail);\r\nuse MIME::Entity;\r\nuse Email::Sender::Transport::SMTP::TLS;\r\n\r\n#------ Globals\r\nmy $file_upload_route    = '\/photo_upload';\r\nmy $uploaded_file        = 'inPhoto';\r\nmy $email_to             = 'emailTo';\r\nmy $image_file_suffix_rx = qr\/\\.(gif|png|jpe?g)$\/;\r\nmy $image_file_type_rx   = qr\/image\\\/(jpeg|gif|png)\/;\r\n\r\n=head2 get $file_upload_route\r\n  Render the file upload form.\r\n=cut\r\n\r\n<\/pre>\n<p>The first part of the module took care of incorporating all the necessary modules, frameworks and package &#8220;global&#8221; variables. The most important modules being Dancer2 and Template modules.<br \/>\nThe next method is the &#8220;GET&#8221; &#8216;\/photo_upload&#8217; route method. It will render the photo\/image upload page with the image viewer. As you can see, there if very little coding here. Just specifying the template file and in this case, populating one template parameter, the page &#8220;title&#8221;. Dancer takes care of the rest.<\/p>\n<pre class=\"brush: css; auto-links: false; collapse: false; first-line: 31; gutter: true; light: false; pad-line-numbers: false; smart-tabs: false; tab-size: 4; title: Photo Upload Page; toolbar: true; notranslate\" title=\"Photo Upload Page\">\r\n\r\nget $file_upload_route =&gt; sub {\r\n    debug 'Got to GET photo_upload page';\r\n\r\n    #------ Render the upload page.\r\n    template 'photo_upload.tt', { title =&gt; 'Upload Photos', };\r\n};\r\n<\/pre>\n<p>The next method uses the same HTTP route, this time using &#8220;POST&#8221;. It is called after the file has been uploaded and the &#8216;Email Photo&#8217; button has been clicked. This method takes care of passing the input parameters(uploaded file object and email address) to a validation method. The CPAN module Email::Valid is used to validate the email address while File::LibMagic has the necessary power to validate the uploaded image. From here, the Email Message building and sending methods are called. And finally, a results page is rendered with the details as to the success or failure of the operation.<\/p>\n<pre class=\"brush: css; auto-links: false; collapse: false; first-line: 37; gutter: true; light: false; pad-line-numbers: false; smart-tabs: false; tab-size: 4; title: Photo Upload Processing; toolbar: true; notranslate\" title=\"Photo Upload Processing\">\r\n=head2 post $file_upload_route\r\n  Upload the photograph or image file. Will accept files of type .jpg, .jpeg, \r\n  .png, .gif.\r\n  Validates the file type and size.\r\n  Will email them to the user provided email address.\r\n=cut\r\n\r\npost $file_upload_route =&gt; sub {\r\n    debug 'Got to POST photo_upload page';\r\n\r\n    debug &quot;Params are : &quot; . dump( request-&gt;params );\r\n    my $validation_results = {\r\n        in_photo =&gt; upload($uploaded_file) \/\/ undef,\r\n        email_to =&gt; params-&gt;{$email_to},\r\n    };\r\n\r\n    _validate_user_input($validation_results);\r\n    debug 'These are the validation results: ' . dump($validation_results);\r\n\r\n    #------ Display errors\r\n    if (   ( exists $validation_results-&gt;{email_error} )\r\n        or ( exists $validation_results-&gt;{upload_error} ) )\r\n    {\r\n        debug 'Got to display some Errors.';\r\n        return template 'photo_upload.tt', {\r\n            title =&gt; 'Upload Photo Errors',\r\n            %$validation_results,\r\n            warning_message =&gt; 'You must upload the file again and enter a\r\n            correct email address!',\r\n        };\r\n    }\r\n\r\n    debug 'Good, passed the error checks.';\r\n    my $in_photo = $validation_results-&gt;{in_photo};\r\n\r\n    #------ Rename the Base of the temporary file to the original File Base\r\n    my $photo_fq_name = _rename_uploaded_file($in_photo);\r\n\r\n    my $message = _build_email_message(\r\n        {\r\n            email_to =&gt; $validation_results-&gt;{email_to},\r\n            type     =&gt; $in_photo-&gt;type,\r\n            path     =&gt; $photo_fq_name,\r\n            encoding =&gt; 'base64',\r\n        }\r\n    );\r\n\r\n    return _process_error('Unable to build MIME::Entity')\r\n      unless $message;\r\n\r\n    my $transport = _build_email_transport();\r\n\r\n    return _process_error('Unable to build Email transport!')\r\n      unless $transport;\r\n\r\n    _send_email_msg( $message, $transport );\r\n\r\n    template 'photo_sent.tt',\r\n      {\r\n        title              =&gt; 'Emailed Photos',\r\n        sent_files_heading =&gt; 'Emailed Photo(s)',\r\n        success_message    =&gt; $in_photo-&gt;basename\r\n          . ' was emailed to '\r\n          . $validation_results-&gt;{email_to},\r\n        in_photos   =&gt; &#x5B;$in_photo],\r\n        have_photos =&gt; ( $in_photo ? 1 : 0 ),\r\n        return_to   =&gt; uri_for($file_upload_route),\r\n      };\r\n\r\n};\r\n<\/pre>\n<p>The input validation method using Email::Valid and File::Lib::Magic to do the heavy lifting here.<\/p>\n<pre class=\"brush: css; auto-links: false; collapse: false; first-line: 107; gutter: true; light: false; pad-line-numbers: false; smart-tabs: false; tab-size: 4; title: Validation Methods; toolbar: true; notranslate\" title=\"Validation Methods\">\r\n=head _validate_user_input\r\n Validate the uploaded file and the 'to' Email address.\r\n Pass a HashRef with the Dancer Request Upload Object and\r\n the email address.\r\n Populates the HashRef with validation information.\r\n {\r\n    in_photo  =&gt;  # the Dancer Request Upload object or not exists\r\n    email_to =&gt; email@emails.com or not exists if invalid\r\n    upload_error =&gt;  'Error Msg....'  or not exists if ok\r\n    email_error =&gt;  'Error Msg....'  or not exists if ok\r\n }\r\n=cut\r\n\r\nsub _validate_user_input {\r\n    my $validation_report = shift;\r\n\r\n    if ( defined $validation_report-&gt;{in_photo} ) {\r\n        debug 'At least there is a file uploaded.';\r\n\r\n        #------ Check that the file is a valid Image or Photo\r\n        $validation_report-&gt;{upload_error} = 'Not a valid photo or image type!'\r\n          unless _validate_file( $validation_report-&gt;{in_photo} );\r\n    }\r\n    else {\r\n        $validation_report-&gt;{upload_error} = 'No photo uploaded!';\r\n    }\r\n\r\n    #------ Validate the 'to' Email Address\r\n    $validation_report-&gt;{email_error} = 'Invalid or no email address entered!'\r\n      unless ( $validation_report-&gt;{email_to} =\r\n        _validate_email_address( $validation_report-&gt;{email_to} ) );\r\n}\r\n\r\n=head2 _validate_email_address\r\n  Validates a given Email Address.\r\n  Uses Email::Valid\r\n  Returns undef if not valid.\r\n=cut\r\n\r\nsub _validate_email_address {\r\n    my $email_address_in = shift;\r\n    my $valid_email_addr;\r\n    try {\r\n        $valid_email_addr = Email::Valid-&gt;address($email_address_in);\r\n    }\r\n    catch {\r\n        error 'Problems with Email::Valid!: ' . $_;\r\n    };\r\n    return $valid_email_addr;\r\n}\r\n\r\n=head2 _validate_file \r\n  Validate the file type by first checking the file suffix,\r\n  then validating the file type with File::Lib::Magic\r\n  Also checks that the file is smaller than the maximum allowed \r\n  size from the config file.\r\n  Returns the validated file or undef.\r\n=cut\r\n\r\nsub _validate_file {\r\n    my $in_file = shift;\r\n    return\r\n      unless ( $in_file\r\n        &amp;&amp; ( lc( $in_file-&gt;basename ) =~ \/$image_file_suffix_rx\/ )\r\n        &amp;&amp; ( $in_file-&gt;size &lt;= config-&gt;{InputFile}{max_file_size} ) );\r\n    my $FileMagic;\r\n    try {\r\n        $FileMagic = File::LibMagic-&gt;new();\r\n    }\r\n    catch {\r\n        error $_;\r\n    };\r\n    return $in_file\r\n      if ( $FileMagic-&gt;checktype_filename( $in_file-&gt;tempname ) =~\r\n        \/$image_file_type_rx\/ );\r\n}\r\n\r\n=head2 _rename_uploaded_file\r\n Renames the temporary file basename back to its original name.\r\n=cut\r\n\r\nsub _rename_uploaded_file {\r\n    my $file_upload = shift;\r\n    my $io_photo    = io( $file_upload-&gt;tempname );\r\n    my $filepath    = $io_photo-&gt;filepath;\r\n    return $io_photo-&gt;rename( $filepath . $file_upload-&gt;basename );\r\n}\r\n<\/pre>\n<p>The Email Message is build using various CPAN modules.<br \/>\nMIME::Entity; # To build the Email Message<br \/>\nEmail::Sender::Transport::SMTP::TLS; # To set up the Email transport<br \/>\nEmail::Sender::Simple qw(sendmail); # Sends the Email<br \/>\nYou can check out their documentation on <a title=\"Meta::Cpan\" href=\"http:\/\/www.metacpan.org\" target=\"_blank\">meta::cpan<\/a> if you are not already familiar with them.<\/p>\n<pre class=\"brush: css; auto-links: false; collapse: false; first-line: 194; gutter: true; light: false; pad-line-numbers: false; smart-tabs: false; tab-size: 4; title: Email Sender Methods; toolbar: true; notranslate\" title=\"Email Sender Methods\">\r\n=head2 _build_email_message\r\n Build the Email message\r\n Uses data from the config file to populate most of these fields.\r\n=cut\r\n\r\nsub _build_email_message {\r\n    my $in_photo_details = shift;\r\n    my $data_message     = 'Files are attached!';\r\n\r\n    #----- Follow this link for mime-types-list\r\n    #      http:\/\/www.freeformatter.com\/mime-types-list.html\r\n    my %headers = (\r\n        type    =&gt; &quot;multipart\/mixed&quot;,\r\n        From    =&gt; config-&gt;{'Email'}{from},\r\n        To      =&gt; $in_photo_details-&gt;{email_to},\r\n        Subject =&gt; config-&gt;{'Email'}{subject} \/\/ 'Here is your photo',\r\n    );\r\n\r\n    my $message = try {\r\n        MIME::Entity-&gt;build(\r\n            Charset  =&gt; 'utf-8',\r\n            Encoding =&gt; 'quoted-printable',\r\n            %headers,\r\n            Data =&gt; $data_message,\r\n        );\r\n    }\r\n    catch {\r\n        error 'Unable to build MIME::Entity! ' . $_;\r\n        undef;\r\n    };\r\n\r\n    return unless $message;\r\n\r\n    #--- Attach the photo\r\n    $message-&gt;attach(\r\n        Disposition =&gt; &quot;attachment&quot;,\r\n        type        =&gt; $in_photo_details-&gt;{type},\r\n        Path        =&gt; $in_photo_details-&gt;{path},\r\n        Encoding    =&gt; $in_photo_details-&gt;{encoding}\r\n    ) if $message;\r\n\r\n    return $message;\r\n}\r\n\r\n=head2 _build_email_transport\r\n  Build the Email Transport. Config file specifies SMTP-TLS for this project.\r\n  My laptop is configured to use sSmtp.\r\n  My development configuration file has set smtp.live.com(hotmail) as the\r\n  Email host for convenience.\r\n=cut\r\n\r\nsub _build_email_transport {\r\n    my $transport;\r\n    my $error_msg;\r\n\r\n    if ( config-&gt;{Email}{transport} eq q\/SMTP-TLS\/ ) {\r\n\r\n        $transport = try {\r\n            Email::Sender::Transport::SMTP::TLS-&gt;new(\r\n                host     =&gt; config-&gt;{'Email'}{'SMTP-TLS'}{host},\r\n                port     =&gt; config-&gt;{'Email'}{'SMTP-TLS'}{port},\r\n                username =&gt; config-&gt;{'Email'}{'SMTP-TLS'}{username},\r\n                password =&gt; config-&gt;{'Email'}{'SMTP-TLS'}{password}\r\n            );\r\n        }\r\n        catch {\r\n            error 'Unable to create a transport method! ' . $_;\r\n            undef;\r\n        };\r\n    }\r\n    else {\r\n        error\r\n&quot;No Email transport method specified.\\n Did you plan to hand deliver it?&quot;;\r\n    }\r\n\r\n    return $transport;\r\n}\r\n\r\n=head2 _send_email_msg\r\n  Send the Email with the photo attached\r\n  Pass the message and the transport.\r\n=cut\r\n\r\nsub _send_email_msg {\r\n    my $message   = shift;\r\n    my $transport = shift;\r\n\r\n    try {\r\n        sendmail( $message, { transport =&gt; $transport } );\r\n    }\r\n    catch {\r\n        error 'Unable to email the files!' . $_;\r\n    };\r\n}\r\n<\/pre>\n<p>And then there is the Error processing. Just in case &#8230;&#8230;.<\/p>\n<pre class=\"brush: css; auto-links: false; collapse: false; first-line: 288; gutter: true; light: false; pad-line-numbers: false; smart-tabs: false; tab-size: 4; title: Error Processing; toolbar: true; notranslate\" title=\"Error Processing\">\r\n#-------------------------------------------------------------------------------\r\n#  Render Error Page\r\n#  Pass a message and a URL to return to.\r\n#-------------------------------------------------------------------------------\r\nsub _process_error {\r\n    my $error_msg = shift \/\/ 'Something really bad must have happened.';\r\n    error $error_msg;\r\n    return Dancer2::Core::Error-&gt;new(\r\n        response =&gt; response(),\r\n        status   =&gt; 500,\r\n        message  =&gt; $error_msg,\r\n    )-&gt;throw;\r\n}\r\n\r\n#-------------------------------------------------------------------------------\r\n1\r\n__END__\r\n\r\n=pod\r\n \r\n=head1 NAME\r\n \r\n PhotoUp - Upload and Email Photo(s) Demo\r\n  \r\n=head1 VERSION\r\n \r\n version 0.1\r\n  \r\n=head1 SYNOPSIS\r\n        Just a demo.                                         \r\n=head1 DESCRIPTION\r\n  This is just a short demo to Upload a use inputted photograph or image. The\r\n  photo will be emailed to a user inputted email address using Dancer2. It will take\r\n  advantage or Bootstrap's photo upload and preview JavaScript\/jQuery\r\n  component. It will also use Template::Toolkit.\r\n=cut\r\n<\/pre>\n<p>This pretty much takes care of uploading the photograph, validating that it is in fact a photo or some other image type and validating the &#8216;to&#8217; email address. It also checks to make sure that the file is not larger than the maximum size specified in the configuration file. It then emails the photo to the requested email address. Along the way it also logs some debug and error messages. If everything runs as planned, a page will be rendered notifying the user of the operations success.<\/p>\n<p class=\"code-heading\">Views (cont):<\/p>\n<p><span class=\"code-description\">Display the results of our actions in photo_sent.tt.<\/span><\/p>\n<p>This is the final template view rendered. Only a few things to note here for those of you who are not familiar with Template::Toolkit. Template::Toolkit allows lots of data processing and manipulation. It has many Plugins that can be installed to filter data or to use various CPAN modules like DateTime.pm right inside the template.<br \/>\nI just use a little manipulation of the uploaded file size data so as to figure out the best way to represent the size units of the file. I also threw in some error and status message fields at the foot of the view, which I haven&#8217;t fully utilized yet.<\/p>\n<pre class=\"brush: css; auto-links: false; collapse: false; first-line: 1; gutter: true; html-script: true; light: false; pad-line-numbers: false; smart-tabs: true; tab-size: 4; title: photo_sent.tt; toolbar: true; notranslate\" title=\"photo_sent.tt\">\r\n\r\n&#x5B;% USE two_dec = format('%.2f') -%]\r\n\r\n&lt;div class=&quot;container&quot;&gt;\r\n&lt;!-- Sent Photos Form --&gt;\r\n    &lt;p class=&quot;lead&quot;&gt;&#x5B;%- sent_files_heading -%] &lt;\/p&gt;\r\n    &lt;table  class=&quot;table table-hover&quot;&gt;\r\n    &lt;thead&gt;\r\n       &lt;tr&gt;\r\n          &lt;th&gt;File Name&lt;\/th&gt; &lt;th&gt;Type&lt;\/th&gt; &lt;th&gt;Size&lt;\/th&gt;\r\n        &lt;\/tr&gt;\r\n    &lt;\/thead&gt;\r\n    &#x5B;% FOREACH file IN in_photos %]\r\n    &lt;tbody&gt;&lt;tr&gt;\r\n            &lt;td&gt;&#x5B;%- file.basename -%]&lt;\/td&gt;\r\n            &lt;td&gt;&#x5B;%- file.type -%]&lt;\/td&gt;\r\n            &#x5B;%# 'Display the size in most appropriate unit size.' %]\r\n            &#x5B;% IF file.size &lt; 1024 %]\r\n               &lt;td&gt;\r\n                &#x5B;% file.size _ &quot;Bytes&quot; %] \r\n               &lt;\/td&gt;\r\n            &#x5B;% ELSIF file.size &lt; 1048576 %]\r\n               &lt;td&gt;\r\n                &#x5B;% two_dec(file.size \/ 1024) _ 'K' %]\r\n               &lt;\/td&gt;\r\n            &#x5B;% ELSIF file.size &lt; 1073741824 %]\r\n               &lt;td&gt;\r\n                &#x5B;% two_dec(file.size \/ 1048576 ) _ 'M'  %] \r\n               &lt;\/td&gt;\r\n            &#x5B;% ELSE %]\r\n               &lt;td&gt;\r\n                &#x5B;% two_dec(file.size \/ 1073741824 ) _ 'G' %]\r\n               &lt;\/td&gt;\r\n             &#x5B;% END %]\r\n\r\n\r\n    &lt;\/tr&gt;&lt;\/tbody&gt;\r\n    &lt;\/table&gt;\r\n    &#x5B;% END %]\r\n\r\n    &lt;a  href=&quot;&#x5B;% return_to %]&quot;&gt;\r\n        &lt;button class=&quot;btn btn-large&quot; &gt;Back&lt;\/button&gt;&lt;\/a&gt;\r\n    &lt;div id=&quot;messages&quot;&gt;\r\n    &lt;p class=&quot;text-success&quot;&gt;&#x5B;%- success_message -%]&lt;\/p&gt;\r\n    &lt;p class=&quot;text-info&quot;&gt;&#x5B;%- info_message -%]&lt;\/p&gt;\r\n    &lt;p class=&quot;text-warning&quot;&gt;&#x5B;%- warning_message -%]&lt;\/p&gt;\r\n    &lt;p class=&quot;text-error&quot;&gt;&#x5B;%- error_message -%]&lt;\/p&gt;\r\n    &lt;\/div&gt;\r\n&lt;\/div&gt; &lt;!-- \/container --&gt;\r\n<\/pre>\n<h3>Sample Web Pages<\/h3>\n<p><span class=\"code-description\">Photo Upload Page<\/span><br \/>\nThis is the initial photo upload page.<\/p>\n<figure id=\"attachment_309\" aria-describedby=\"caption-attachment-309\" style=\"width: 640px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/www.aibistin.com\/wp-content\/uploads\/2013\/04\/upload_turtles.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-large wp-image-309\" src=\"http:\/\/www.aibistin.com\/wp-content\/uploads\/2013\/04\/upload_turtles-1024x507.png\" alt=\"Initial Upload Photos Page\" width=\"640\" height=\"316\" srcset=\"https:\/\/www.aibistin.com\/wp-content\/uploads\/2013\/04\/upload_turtles-1024x507.png 1024w, https:\/\/www.aibistin.com\/wp-content\/uploads\/2013\/04\/upload_turtles-300x148.png 300w, https:\/\/www.aibistin.com\/wp-content\/uploads\/2013\/04\/upload_turtles.png 1280w\" sizes=\"auto, (max-width: 640px) 100vw, 640px\" \/><\/a><figcaption id=\"caption-attachment-309\" class=\"wp-caption-text\">Upload Photos Page<\/figcaption><\/figure>\n<p><span class=\"code-description\">View Uploaded Photo<\/span><br \/>\nThis is what the web page looks like when a the photograph has been uploaded.<\/p>\n<figure id=\"attachment_299\" aria-describedby=\"caption-attachment-299\" style=\"width: 640px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/www.aibistin.com\/wp-content\/uploads\/2013\/04\/turtles.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-large wp-image-299\" src=\"http:\/\/www.aibistin.com\/wp-content\/uploads\/2013\/04\/turtles-1024x507.png\" alt=\"Photo preview page\" width=\"640\" height=\"316\" srcset=\"https:\/\/www.aibistin.com\/wp-content\/uploads\/2013\/04\/turtles-1024x507.png 1024w, https:\/\/www.aibistin.com\/wp-content\/uploads\/2013\/04\/turtles-300x148.png 300w, https:\/\/www.aibistin.com\/wp-content\/uploads\/2013\/04\/turtles.png 1280w\" sizes=\"auto, (max-width: 640px) 100vw, 640px\" \/><\/a><figcaption id=\"caption-attachment-299\" class=\"wp-caption-text\">Photo preview page<\/figcaption><\/figure>\n<p><span class=\"code-description\">Results Page<\/span><br \/>\nThis page is displayed after the photo is emailed.<\/p>\n<figure id=\"attachment_300\" aria-describedby=\"caption-attachment-300\" style=\"width: 640px\" class=\"wp-caption alignnone\"><a href=\"http:\/\/www.aibistin.com\/wp-content\/uploads\/2013\/04\/emailed_turtles_small.png\"><img loading=\"lazy\" decoding=\"async\" class=\"size-large wp-image-300\" src=\"http:\/\/www.aibistin.com\/wp-content\/uploads\/2013\/04\/emailed_turtles_small-1024x507.png\" alt=\"Emailed photo results page\" width=\"640\" height=\"316\" srcset=\"https:\/\/www.aibistin.com\/wp-content\/uploads\/2013\/04\/emailed_turtles_small-1024x507.png 1024w, https:\/\/www.aibistin.com\/wp-content\/uploads\/2013\/04\/emailed_turtles_small-300x148.png 300w, https:\/\/www.aibistin.com\/wp-content\/uploads\/2013\/04\/emailed_turtles_small.png 1280w\" sizes=\"auto, (max-width: 640px) 100vw, 640px\" \/><\/a><figcaption id=\"caption-attachment-300\" class=\"wp-caption-text\">Emailed photo results page<\/figcaption><\/figure>\n<h3>Todo<\/h3>\n<p>Ok, thats it for this one for now. There are a few other things that I could do here. Test scripts need to be setup to really test this one out. Also I could add some more client side validation to the existing validation provided by Bootstrap. I have used <a href=\"http:\/\/bassistance.de\/jquery-plugins\/jquery-plugin-validation\/\">this<\/a> in the past and found it a very useful client side validation library. Also I plan to check out this more elaborate <a href=\"http:\/\/blueimp.github.io\/jQuery-File-Upload\/\"> jQuery file upload<\/a> library in the future.<\/p>\n<h3>Summary<\/h3>\n<p>Dancer2 provides a nice framework for building small to medium sized Web based applications. It is not as powerful, or as well documented as Catalyst. On the other hand, the learning curve is somewhat less steep than Catalyst. I still think that Catalyst is a much better framework, but I do plan to use Dancer2 for some more small applications. Plack::Builder is also another option worth checking out for very small web apps.<\/p>\n<p>The code for this can be found in my <a href=\"https:\/\/github.com\/aibistin\/PhotoUp\">GitHub Repo<\/a>.<\/p>\n<p><!--?php get_HitsMechanic();?--><\/p>\n<p><a class=\"twitter-follow-button\" href=\"https:\/\/twitter.com\/aibistin\" data-show-count=\"false\">Follow @aibistin<\/a><\/p>\n","protected":false},"excerpt":{"rendered":"<p>This is just a short demonstration on uploading a photograph, previewing the photo, and then emailing the photo to a user defined email address. I will use the Perl, Dancer2 a lightweight web application framework, Template Toolkit, and Bootstrap. You can set up Gmail or Hotmail to act as your mail server. See here or [&hellip;]<\/p>\n","protected":false},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"_jetpack_memberships_contains_paid_content":false,"footnotes":""},"categories":[5],"tags":[9,7,21,8,20,10,22],"class_list":["post-23","post","type-post","status-publish","format-standard","hentry","category-home","tag-bootstrap","tag-dancer","tag-dancer2","tag-file-upload","tag-perl","tag-ssmtp","tag-templatetoolkit"],"jetpack_featured_media_url":"","jetpack_sharing_enabled":true,"_links":{"self":[{"href":"https:\/\/www.aibistin.com\/index.php?rest_route=\/wp\/v2\/posts\/23","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/www.aibistin.com\/index.php?rest_route=\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/www.aibistin.com\/index.php?rest_route=\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/www.aibistin.com\/index.php?rest_route=\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/www.aibistin.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcomments&post=23"}],"version-history":[{"count":216,"href":"https:\/\/www.aibistin.com\/index.php?rest_route=\/wp\/v2\/posts\/23\/revisions"}],"predecessor-version":[{"id":1047,"href":"https:\/\/www.aibistin.com\/index.php?rest_route=\/wp\/v2\/posts\/23\/revisions\/1047"}],"wp:attachment":[{"href":"https:\/\/www.aibistin.com\/index.php?rest_route=%2Fwp%2Fv2%2Fmedia&parent=23"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/www.aibistin.com\/index.php?rest_route=%2Fwp%2Fv2%2Fcategories&post=23"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/www.aibistin.com\/index.php?rest_route=%2Fwp%2Fv2%2Ftags&post=23"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}