App-mojopaste

 view release on metacpan or  search on metacpan

script/mojopaste  view on Meta::CPAN

#!perl
package App::mojopaste::Backend::File;
use Mojo::Base 'Mojolicious::Plugin';

use Mojo::File 'path';
use Mojo::Util qw(encode decode);
use Text::CSV;

my $ID = 0;

sub register {
  my ($self, $app, $config) = @_;
  my $dir = $app->config('paste_dir');
  path($dir)->make_path unless -d $dir;
  $app->helper('paste.load_p' => sub { _load_p($dir, @_) });
  $app->helper('paste.save_p' => sub { _save_p($dir, @_) });
}

sub _load_p {
  my ($dir, $c, $id) = @_;
  my @res = ('', '');

  eval {
    die "Hacking attempt! paste_id=($id)" if !$id or $id =~ m!\W!;
    return Mojo::Promise->new->resolve(decode 'UTF-8', path($dir, $id)->slurp);
  } or do {
    return Mojo::Promise->new->reject($@ || 'Paste not found');
  };
}

sub _save_p {
  my ($dir, $c, $text) = @_;
  my $id  = substr Mojo::Util::md5_sum($$ . time . $ID++), 0, 12;
  my @res = ('', '');

  eval {
    path($dir, $id)->spurt(encode 'UTF-8', $text);
    return Mojo::Promise->new->resolve($id);
  } or do {
    return Mojo::Promise->new->reject($@ || 'Unknown error');
  };
}

package main;
use Mojolicious::Lite;

use Mojo::JSON 'true';

plugin 'config' if $ENV{MOJO_CONFIG};
app->config->{backend}   ||= $ENV{PASTE_BACKEND} || 'File';
app->config->{paste_dir} ||= $ENV{PASTE_DIR}     || 'paste';

app->defaults(
  brand_link    => app->config('brand_link') || $ENV{PASTE_BRAND_LINK} || 'index',
  brand_logo    => app->config('brand_logo') // $ENV{PASTE_BRAND_LOGO} // '/images/logo.png',
  brand_name    => app->config('brand_name') // $ENV{PASTE_BRAND_NAME} // 'Mojopaste',
  enable_charts => app->config('enable_charts') // $ENV{PASTE_ENABLE_CHARTS},
  embed         => 'description,graph,heading,nav',
  error         => '',
  paste         => '',
  placeholder   => 'Enter your text here and then press the "Save" button above.',
  title         => 'Mojopaste',
);

my $backend = app->config('backend');
plugin $backend =~ /::/ ? $backend : "App::mojopaste::Backend::$backend";

helper no_such_paste => sub {
  my ($c, $err) = @_;
  $c->app->log->debug("no_such_paste: $err");
  $c->stash($_ => 'Could not find paste') for qw(error heading title);
  $c->render(description => '', layout => 'mojopaste', status => 404);
};

helper set_title => sub {
  my ($c, $prefix, $suffix) = @_;
  my $brand_name = $c->stash('brand_name') || 'Mojopaste';
  $suffix = $suffix ? "$brand_name $suffix" : $brand_name;
  $prefix =~ s![\n\r]+! !g;
  $prefix =~ s!^\W+!!g;
  $prefix = substr $prefix, 0, 56 - length $suffix;
  return $c->stash(title => "$prefix - $suffix");
};

get(
  '/' => {layout => 'mojopaste'} => sub {
    my $c = shift;

    return $c->set_title("Create new paste") unless my $id = $c->param('edit');
    return $c->render_later->paste->load_p($id)->then(sub {
      return $c->no_such_paste('Could not find paste') unless my $paste = shift;
      $c->set_title(substr($paste, 0, 80), 'edit');
      $c->param(paste => $paste)->render;
    })->catch(sub { $c->no_such_paste(shift) });
  },
  'index'
);

post(
  '/' => {layout => 'mojopaste'},
  sub {
    my $c     = shift;
    my $paste = $c->param('paste') || '';

    return $c->render('index', placeholder => 'You neeed to enter some characters!', status => 400)
      unless $paste =~ /\w/;
    return $c->render_later->paste->save_p($paste)->then(sub {
      $c->redirect_to('show', paste_id => shift);
    })->catch(sub { $c->reply->exception(shift) });
  }
);

get(
  '/:paste_id',
  [format => ['html', 'txt']],

script/mojopaste  view on Meta::CPAN

% end
@@ chart.html.ep
%= include 'nav' if $embed =~ /nav/;
% if ($heading and $embed =~ /heading/) {
<h2><%= $heading %></h2>
% }
% if ($description and $embed =~ /description/) {
<p><%= $description %></p>
% }
% if ($embed =~ /graph/) {
<div id="chart"><%= $error %></div>
<script src="//ajax.googleapis.com/ajax/libs/jquery/1.9.0/jquery.min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/raphael/2.1.0/raphael-min.js"></script>
<script src="//cdnjs.cloudflare.com/ajax/libs/morris.js/0.5.1/morris.min.js"></script>
  % unless ($error) {
<script>
try {
  var chart = document.getElementById('chart');
  var tid, resize = function() { chart.style.height = (window.innerHeight - chart.offsetTop) + 'px' };
  resize();
  window.onresize = function() { if (tid) clearTimeout(tid); var tid = setTimeout(resize, 10); }
  new Morris.Line(<%== Mojo::JSON::encode_json(stash 'chart') %>);
} catch(e) {
  document.getElementById('chart').innerHTML = 'Invalid chart data. ' + e;
}
</script>
  % }
% }
@@ show.html.ep
%= include 'nav' if $embed =~ /nav/;
<pre class="prettyprint linenums"><%= $error || $paste %></pre>
@@ nav.html.ep
% my $paste_id = stash 'paste_id';
<nav>
  %= link_to $brand_link, class => 'brand', begin
    % if ($brand_logo) {
      %= image $brand_logo, alt => ''
    % }
    % if ($brand_name) {
      <span><%= $brand_name %></span>
    % }
  % end

  % if ($paste_id) {
    %= link_to 'New', 'index', class => 'btn'
    %= link_to 'Edit', url_for('index')->query(edit => $paste_id), class => 'btn'

    % if (exists stash->{chart}) {
      %= link_to 'Data', url_for('show', paste_id => $paste_id), class => 'btn'
    % } else {
      %= link_to 'Raw', url_for('show', paste_id => $paste_id, format => 'txt'), class => 'btn'
      %= link_to 'Graph', url_for('chart'), class => 'btn' if $enable_charts
    % }

  % } else {
    <button class="btn save" tabindex="2">Save</button>
  % }
</nav>
@@ show.txt.ep
Paste not found: <%= $paste_id %>
@@ images/logo.png (base64)
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



( run in 0.979 second using v1.01-cache-2.11-cpan-788537b7465 )