Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 2 additions & 1 deletion lib/MetaCPAN/Web/Controller/Lab.pm
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,8 @@ __PACKAGE__->config(

sub lab : Path : Args(0) {
my ( $self, $c ) = @_;
$c->stash( template => 'lab.html' );
$c->res->redirect( '/tools', 301 );
$c->detach;
}

sub dependencies : Local : Args(0) : Does('Sortable') {
Expand Down
65 changes: 64 additions & 1 deletion lib/MetaCPAN/Web/Controller/Pod.pm
Original file line number Diff line number Diff line change
@@ -1,11 +1,13 @@
package MetaCPAN::Web::Controller::Pod;

use HTML::Restrict;
use HTML::TokeParser;
use Moose;
use Try::Tiny;
use URI;
use HTML::Escape qw(escape_html);
use Future;
use Encode qw( encode decode DIE_ON_ERR LEAVE_SRC );

use namespace::autoclean;

Expand Down Expand Up @@ -170,6 +172,64 @@ sub view : Private {
}
}

sub pod2html : Path('/pod2html') {
my ( $self, $c ) = @_;
my $pod;
if ( my $pod_file = $c->req->upload('pod_file') ) {
my $raw_pod = $pod_file->slurp;
eval {
$pod = decode( 'UTF-8', $raw_pod, DIE_ON_ERR | LEAVE_SRC );
1;
} or $pod = decode( 'cp1252', $raw_pod );
}
elsif ( $pod = $c->req->parameters->{pod} ) {
}
else {
return;
}

$c->stash( { pod => $pod } );

my $html
= $c->model('API')
->request( 'pod_render', undef, { pod => encode( 'UTF-8', $pod ) },
'POST' )->get->{raw};

$html = $self->filter_html($html);

if ( $c->req->parameters->{raw} ) {
$c->res->content_type('text/html');
$c->res->body($html);
$c->detach;
}
else {
my ( $pod_name, $abstract );
my $p = HTML::TokeParser->new( \$html );
while ( my $t = $p->get_token ) {
my ( $type, $tag, $attr ) = @$t;
if ( $type eq 'S'
&& $tag eq 'h1'
&& $attr->{id}
&& $attr->{id} eq 'NAME' )
{
my $name_section = $p->get_trimmed_text('h1');
if ($name_section) {
( $pod_name, $abstract )
= $name_section =~ /(?:NAME\s+)?([^-]+)\s*-\s*(.*)/s;
}
last;
}
}
$c->stash(
{
pod_rendered => $html,
( $pod_name ? ( pod_name => $pod_name ) : () ),
( $abstract ? ( abstract => $abstract ) : () ),
}
);
}
}

sub filter_html {
my ( $self, $html, $data ) = @_;

Expand Down Expand Up @@ -236,7 +296,7 @@ sub filter_html {
# bad protocol
return '';
}
else {
elsif ($data) {
my $base = "https://st.aticpan.org/source/";
if ( $val =~ s{^/}{} ) {
$base .= "$data->{author}/$data->{release}/";
Expand All @@ -247,6 +307,9 @@ sub filter_html {
}
$val = URI->new_abs( $val, $base )->as_string;
}
else {
$val = '/static/images/gray.png';
}
}
$tag .= qq{ $attr="} . escape_html($val) . qq{"};
}
Expand Down
14 changes: 14 additions & 0 deletions lib/MetaCPAN/Web/Controller/Tools.pm
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package MetaCPAN::Web::Controller::Tools;

use Moose;
use namespace::autoclean;

BEGIN { extends 'MetaCPAN::Web::Controller' }

sub tools : Path : Args(0) {
my ( $self, $c ) = @_;
$c->stash( template => 'tools.html' );
}

__PACKAGE__->meta->make_immutable;
1;
39 changes: 23 additions & 16 deletions lib/MetaCPAN/Web/Model/API.pm
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ use URI;
use URI::QueryParam;
use MetaCPAN::Web::Types qw( Uri );
use Try::Tiny qw( catch try );
use HTTP::Request;
use HTTP::Request::Common ();

my $loop;

Expand Down Expand Up @@ -86,33 +88,38 @@ sub request {

my $url = $self->api_secure->clone;

$method ||= $search ? 'POST' : 'GET';

# the order of the following 2 lines matters
# `path_query` is destructive
$url->path_query($path);
for my $param ( keys %{ $params || {} } ) {
$url->query_param( $param => $params->{$param} );
}

my $current_url = $self->request_uri;
my $request_id = $self->request_id;
if ( $method eq 'GET' || $search ) {
for my $param ( keys %{ $params || {} } ) {
$url->query_param( $param => $params->{$param} );
}
}

my $request = HTTP::Request->new(
my $request = HTTP::Request::Common->can($method)->(
$url,
(
$method ? $method
: $search ? 'POST'
: 'GET',
$search
? (
'Content-Type' => 'application/json',
'Content' => encode_json($search),
)
: $method eq 'POST' && $params ? (
'Content_Type' => 'multipart/form-data',
'Content' => $params,
)
: ()
),
$url,
[
( $search ? ( 'Content-Type' => 'application/json' ) : () ),
( $current_url ? ( 'Referer' => $current_url->as_string ) : () ),
( $request_id ? ( 'X-MetaCPAN-Request-ID' => $request_id ) : () ),
],
( $current_url ? ( 'Referer' => $current_url->as_string ) : () ),
( $request_id ? ( 'X-MetaCPAN-Request-ID' => $request_id ) : () ),
);

# encode_json returns an octet string
$request->add_content( encode_json($search) ) if $search;

$self->client->do_request( request => $request )->transform(
done => sub {
my $response = shift;
Expand Down
11 changes: 7 additions & 4 deletions root/lab/list.html → root/inc/tools-bar.html
Original file line number Diff line number Diff line change
@@ -1,12 +1,15 @@
<div class="tabbable tabs-left about-bar slidepanel">
<ul class="nav nav-pills nav-stacked" data-spy="affix" data-offset-top="105" id="left-nav">
<li<% IF req.action == 'lab' %> class="active"<% END %>>
<a href="/lab">Lab</a>
<li<% IF req.action == 'tools' %> class="active"<% END %>>
<a href="/tools">Tools</a>
</li>
<li<% IF req.action == 'pod2html' %> class="active"<% END %>>
<a href="/pod2html">Pod Renderer</a>
</li>
<li<% IF req.action == '/lab/dashboard' %> class="active"<% END %>>
<a href="/lab/dashboard">Dashboard</a>
<a href="/lab/dashboard">Lab: Dashboard</a>
</li>
<li<% IF req.action == '/lab/dependencies' %> class="active"<% END %>>
<a href="/lab/dependencies">Dependencies</a>
<a href="/lab/dependencies">Lab: Dependencies</a>
</li>
</div>
18 changes: 0 additions & 18 deletions root/lab.html

This file was deleted.

2 changes: 1 addition & 1 deletion root/lab/dashboard.html
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
<% PROCESS "lab/list.html" %>
<% PROCESS "inc/tools-bar.html" %>

<div class="content">

Expand Down
33 changes: 33 additions & 0 deletions root/pod/pod2html.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
<%
title = 'Pod Renderer' _ (pod_name ? ' - ' _ pod_name : '')
%>
<% PROCESS "inc/tools-bar.html" %>
<div class="content metacpan-pod-renderer">
<div class="panel panel-default">
<div class="panel-heading" role="tab" data-toggle="collapse" data-target="#metacpan-pod-renderer-collapse">
<h3 class="panel-title">Pod Renderer<span class="pull-right"><i class="fa fa-chevron-down" aria-hidden="true"></i><i class="fa fa-chevron-up" aria-hidden="true"></i></span></h3>
</div>
<div class="panel-collapse collapse in" id="metacpan-pod-renderer-collapse">
<div class="panel-body">
<form method="POST" enctype="multipart/form-data" id="metacpan-pod-renderer-form">
<input id="metacpan-pod-renderer-file" type="file" name="pod_file" accept=".pl, .pod, .pm, text/plain, text/x-perl-file, text/x-pod-file"/>
<div class="form-group">
<label for="metacpan-pod-renderer-pod">Source:</label>
<textarea id="metacpan-pod-renderer-pod" name="pod" class="form-control"><% pod %></textarea>
</div>
<div class="form-group button-line">
<label for="metacpan-pod-renderer-file" class="btn btn-default">Browse for file...</label>
<input type="submit" value="Render" class="btn btn-primary" />
</div>
</form>
<div id="metacpan-pod-renderer-loading" class="alert alert-info" style="display: none"><i class="fa fa-spinner fa-spin"></i> Loading...</div>
<div id="metacpan-pod-renderer-error" class="alert alert-danger" <% IF !pod_error %>style="display: none"<% END %>>
Error rendering POD<% IF pod_error; ' - ' _ pod_error; END %>
</div>
</div>
</div>
</div>
</div>
<div class="content">
<div id="metacpan-pod-renderer-output" class="pod anchors" <% IF !pod_rendered %>style="display: none"<% END %>><% pod_rendered | none %></div>
</div>
Binary file added root/static/images/gray.png
Loading
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
96 changes: 87 additions & 9 deletions root/static/js/cpan.js
Original file line number Diff line number Diff line change
Expand Up @@ -262,13 +262,16 @@ $(document).ready(function() {
}
}

$('.anchors').find('h1,h2,h3,h4,h5,h6,dt').each(function() {
if (this.id) {
$(document.createElement('a')).attr('href', '#' + this.id).addClass('anchor').append(
$(document.createElement('span')).addClass('fa fa-bookmark black')
).prependTo(this);
}
});
function create_anchors(top) {
top.find('h1,h2,h3,h4,h5,h6,dt').each(function() {
if (this.id) {
$(document.createElement('a')).attr('href', '#' + this.id).addClass('anchor').append(
$(document.createElement('span')).addClass('fa fa-bookmark black')
).prependTo(this);
}
});
}
create_anchors($('.anchors'));

var module_source_href = $('#source-link').attr('href');
if (module_source_href) {
Expand Down Expand Up @@ -302,8 +305,7 @@ $(document).ready(function() {

$('.dropdown-toggle').dropdown();

var index = $("#index");
if (index) {
function format_index(index) {
index.wrap('<div id="index-container"><div class="index-border"></div></div>');
var container = index.parent().parent();

Expand All @@ -328,6 +330,10 @@ $(document).ready(function() {
container.addClass("pull-right");
}
}
var index = $("#index");
if (index.length) {
format_index(index);
}

['right'].forEach(function(side) {
var panel = $('#' + side + "-panel");
Expand Down Expand Up @@ -358,6 +364,78 @@ $(document).ready(function() {
if (changes.prop('scrollHeight') > changes.height()) {
$("#last-changes-toggle").show();
}

var pod2html_form = $('#metacpan-pod-renderer-form');
var pod2html_text = $('[name="pod"]', pod2html_form);
var pod2html_update = function(pod) {
if (!pod) {
pod = pod2html_text.get(0).value;
}
var submit = pod2html_form.find('input[type="submit"]');
submit.attr("disabled", "disabled");
var rendered = $('#metacpan-pod-renderer-output');
var loading = $('#metacpan-pod-renderer-loading');
var error = $('#metacpan-pod-renderer-error');
rendered.hide();
rendered.html('');
loading.show();
error.hide();
document.title = "Pod Renderer - metacpan.org";
$.ajax({
url: '/pod2html',
method: 'POST',
data: {
pod: pod,
raw: true
},
success: function(data, stat, req) {
rendered.html(data);
loading.hide();
error.hide();
var res = $('#NAME + p').text().match(/^([^-]+?)\s*-\s*(.*)/);
if (res) {
var title = res[0];
var abstract = res[1];
document.title = "Pod Renderer - " + title + " - metacpan.org";
}
var index = $("#index", rendered);
if (index.length) {
format_index(index);
}
create_anchors(rendered);
rendered.show();
submit.removeAttr("disabled");
},
error: function(data, stat) {
rendered.hide();
loading.hide();
error.html('Error rendering POD' +
(data && data.length ? ' - ' + data : ''));
error.show();
submit.removeAttr("disabled");
}
});
};
if (window.FileReader) {
$('input[type="file"]', pod2html_form).on('change', function(e) {
var files = this.files;
for (var i = 0; i < files.length; i++) {
var file = files[i];
var reader = new FileReader();
reader.onload = function(e) {
pod2html_text.get(0).value = e.target.result;
pod2html_update(pod2html_text.get(0).value);
};
reader.readAsText(file);
}
this.value = null;
});
}
pod2html_form.on('submit', function(e) {
e.preventDefault();
e.stopPropagation();
pod2html_update();
});
});

function set_page_size(selector, storage_name) {
Expand Down
Loading