Skip to content
Snippets Groups Projects
Verified Commit 136b16b3 authored by Andrej Ramašeuski's avatar Andrej Ramašeuski
Browse files

Verze s podporou editace

parent 6f1c5f87
Branches
No related tags found
No related merge requests found
0.2.0
1.0.0
......@@ -100,6 +100,8 @@ sub startup {
$r->get('/')->to(cb => sub { shift->render('index'); });
$r->post('/')->to('Shortcut#create');
$r->get('/shortcuts')->to(cb => sub { shift->render('shortcuts'); });
$r->get('/login')->to('OIDC#callback');
$r->get('/logout')->to('OIDC#do_logout');
......
......@@ -4,7 +4,22 @@ use Mojo::Base 'Mojolicious::Controller', -signatures;
use Data::Validate::URI qw(is_uri);
use Image::PNG::QRCode 'qrpng';
# This action will render a template
sub redirect ($c) {
my $shortcut = $c->schema->resultset('Shortcut')->search({
shortcut => $c->stash->{shortcut},
is_active => 1,
deleted => undef,
})->first;
if ( ! $shortcut ) {
$c->render( status => 404, text => 'not found' );
return;
}
$c->res->code($shortcut->code);
$c->redirect_to($shortcut->url);
}
sub create ($c) {
my $url = $c->param('url');
......@@ -34,39 +49,24 @@ sub create ($c) {
}
my %data = (
user_id => $c->current_user->{id},
deleted => undef,
url => $url,
);
my $shortcut = $c->schema->resultset('Shortcut')->search(\%data)->first;
$shortcut ||= $c->schema->resultset('Shortcut')->create({
%data,
shortcut => $custom // $c->schema->resultset('Shortcut')->generate(),
});
$url = 'https://' . $c->config->{domain} . '/' . $shortcut->shortcut;
$c->render('shortcut/created', url => $url, shortcut => $shortcut );
}
sub redirect ($c) {
my $shortcut = $c->schema->resultset('Shortcut')->search({
shortcut => $c->stash->{shortcut},
is_active => 1,
deleted => undef,
})->first;
my $shortcut = $c->current_user->shortcuts(\%data)->first;
if ( ! $shortcut ) {
$c->render( status => 404, text => 'not found' );
return;
$data{shortcut} = $custom || $c->schema->resultset('Shortcut')->generate();
$shortcut = $c->current_user->add_to_shortcuts(\%data);
}
$c->res->code($shortcut->code);
$c->redirect_to($shortcut->url);
$c->render('shortcut/created',
url => 'https://' . $c->config->{domain} . '/' . $shortcut->shortcut,
shortcut => $shortcut
);
}
sub qr ($c) {
my $url = 'https://' . $c->config->{domain} . '/' . $c->stash->{shortcut};
my $png = qrpng (text => $url, level => 4);
......@@ -79,13 +79,50 @@ sub list ($c) {
my @shortcuts = ();
SHORTCUT:
foreach my $shortcut ( $c->stash->{user}->shortcuts ) {
foreach my $shortcut ( $c->stash->{user}->shortcuts({ deleted => undef }) ) {
push @shortcuts, $c->spec_filter(
{ $shortcut->get_columns }, 'Shortcut'
);
}
$c->render(json => \@shortcuts );
$c->render(json => \@shortcuts, );
}
sub update ($c) {
my $shortcut = $c->stash->{user}->shortcuts({
id => $c->stash->{id}
})->first;
if ( ! $shortcut ) {
$c->render( status => 404, text => 'not found' );
return;
}
$shortcut->update({
code => $c->req->json->{code},
url => $c->req->json->{url},
});
$c->render(status => 204, text => '' );
}
sub delete ($c) {
my $shortcut = $c->stash->{user}->shortcuts({
id => $c->stash->{id}
})->first;
if ( ! $shortcut ) {
$c->render( status => 404, text => 'not found' );
return;
}
$shortcut->update({
deleted => \'now()'
});
$c->render(status => 204, text => '' );
}
1;
......@@ -3,7 +3,7 @@ openapi: 3.0.3
info:
title: Piratský zkracovač
description: Piratský zkracovač API
version: 1.15.1
version: 1.0.0
license:
name: Artistic License 2.0
url: https://www.perlfoundation.org/artistic-license-20.html
......@@ -25,15 +25,19 @@ components:
properties:
id:
type: integer
readOnly: true
maxLength: 6
shortcut:
type: string
description: Zkratka
url:
type: string
description: URL pro přesměrování
maxLength: 1024
code:
type: integer
description: Kód přesměrování
enum: [301, 302]
securitySchemes:
Token:
type: apiKey
......@@ -42,6 +46,23 @@ components:
paths:
/shortcuts:
post:
tags:
- shortcuts
security:
- Token: []
summary: "Pridat zkratku"
operationId: ShortcutCreate
x-mojo-to: shortcut#create1
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Shortcut'
responses:
201:
description: Shortcut created
get:
tags:
- shortcuts
......@@ -60,3 +81,32 @@ paths:
items:
$ref: '#/components/schemas/Shortcut'
/shortcuts/{id}:
put:
tags:
- shortcuts
security:
- Token: []
summary: "Aktualizovat zkratku"
operationId: ShortcutUpdate
x-mojo-to: shortcut#update
requestBody:
required: true
content:
application/json:
schema:
$ref: '#/components/schemas/Shortcut'
responses:
204:
description: Shortcut updated
delete:
tags:
- shortcuts
security:
- Token: []
summary: "Smazat zkratku"
operationId: ShortcutDelete
x-mojo-to: shortcut#delete
responses:
204:
description: Shortcut deleted
.jsgrid-edit-row > .jsgrid-cell, .content-block table tr:nth-child(2n).jsgrid-edit-row .jsgrid-cell, .table--striped tr:nth-child(2n).jsgrid-edit-row .jsgrid-cell {
background: #92c6ab;
}
.jsgrid-edit-row input, .jsgrid-edit-row select, .jsgrid-edit-row textarea, .jsgrid-filter-row input, .jsgrid-filter-row select, .jsgrid-filter-row textarea, .jsgrid-insert-row input, .jsgrid-insert-row select, .jsgrid-insert-row textarea {
width: 100%;
padding: 0;
}
This diff is collapsed.
......@@ -39,3 +39,8 @@ create table "log" (
primary key("id"),
foreign key("shortcut_id") references "shortcuts" ("id") on update cascade on delete restrict
);
-- 2 up
alter table "shortcuts" alter column "code" set default 302;
alter table "shortcuts" drop constraint "shortcuts_shortcut_key";
create index "shortcuts_shortcut_idx" on "shortcuts" ("shortcut");
......@@ -16,10 +16,6 @@ Pro použiti Pirátského zkracovače musíte se přihliásit <strong><a href="<
% if ( $c->is_user_authenticated ) {
<div class="content-block">
%= include 'includes/form';
% if ( $c->current_user->shortcuts({ deleted => undef })->count() ) {
%= include 'shortcut/list';
% }
%= include 'shortcut/form';
</div>
% }
......@@ -23,11 +23,13 @@
<link rel="manifest" href="/manifest.json"/>
<link rel="stylesheet" href="<%= $c->config->{styleguide} %>css/styles.css"/>
<script src="https://cdn-unpkg.pirati.cz/jquery@3.6.0/dist/jquery.min.js" integrity="sha256-/xUj+3OJU5yExlq6GSYGSHk7tPXikynS7ogEvDej/m4=" crossorigin="anonymous"></script>
<!-- <link href="https://cdn-unpkg.pirati.cz/bootstrap@5.2.0-beta1/dist/css/bootstrap.min.css" rel="stylesheet" integrity="sha384-0evHe/X+R7YkIZDRvuzKMRqM+OrBnVFBL6DOitfPri4tjfHxaWutUpFmBp4vmVor" crossorigin="anonymous">-->
<script src="https://cdn-unpkg.pirati.cz/bootstrap@5.2.0-beta1/dist/js/bootstrap.bundle.min.js" integrity="sha384-pprn3073KE6tl6bjs2QrFaJGz5/SUsLqktiwsUTF55Jfv3qYSDhgCecCxMW52nD2" crossorigin="anonymous"></script>
<script src="https://cdn-unpkg.pirati.cz/htmx.org@1.7.0" integrity="sha384-EzBXYPt0/T6gxNp0nuPtLkmRpmDBbjg6WmCUZRLXBBwYYmwAUxzlSGej0ARHX0Bo" crossorigin="anonymous"></script>
<!-- <link rel="stylesheet" href="https://cdn-unpkg.pirati.cz/bootstrap-table@1.20.2/dist/bootstrap-table.min.css">-->
<script src="https://cdn-unpkg.pirati.cz/bootstrap-table@1.20.2/dist/bootstrap-table.min.js"></script>
<link type="text/css" rel="stylesheet" href="https://cdn-cdnjs-cloudflare.pirati.cz/ajax/libs/jsgrid/1.5.3/jsgrid.min.css" />
<link type="text/css" rel="stylesheet" href="https://cdn-cdnjs-cloudflare.pirati.cz/ajax/libs/jsgrid/1.5.3/jsgrid-theme.min.css" />
<script type="text/javascript" src="https://cdn-cdnjs-cloudflare.pirati.cz/ajax/libs/jsgrid/1.5.3/jsgrid.min.js"></script>
<link rel="stylesheet" href="/custom.css"/>
</head>
<body>
......@@ -49,11 +51,21 @@
</div>
<div v-if="show || isLgScreenSize" class="navbar__main navbar__section navbar__section--expandable container-padding--zero lg:container-padding--auto flex items-center">
<div class="flex-grow">
<!-- MENU -->
% if ( $c->is_user_authenticated ) {
<ul class="navbar-menu text-white">
<li class="navbar-menu__item">
<a href="/" class="navbar-menu__link">Nová zkratka</a>
</li>
<li class="navbar-menu__item">
<a href="/shortcuts" class="navbar-menu__link">Moje zkratky</a>
</li>
</ul>
% }
</div>
<div class="flex items-center space-x-4">
% if ( $c->is_user_authenticated ) {
<div class="flex items-center space-x-4">
<span class="head-heavy-2xs"><%= $c->current_user->displayname %></span>
<div class="avatar avatar--2xs">
......@@ -61,7 +73,6 @@
</div>
<a href="/logout"><button class="text-grey-200 hover:text-white"><i class="ico--log-out"></i></button></a>
</div>
% } else {
<a href="<%= $c->oidc->authorize %>">
<button class="btn btn--icon btn--grey-125 btn--hoveractive">
......@@ -82,7 +93,7 @@
</ui-app>
</nav>
<div class="container container--default py-8 lg:py-24">
<div class="container container--default py-8">
<section>
<main>
<%= content %>
......
......@@ -8,7 +8,6 @@
<label class="form-field__label" for="url">URL</label>
<div class="form-field__wrapper form-field__wrapper--shadowed">
<input type="text" name="url" class="text-input form-field__control" value="" placeholder="https://www.pirati.cz/program/dlouhodoby/psychotropni-latky/" required="required" />
</div>
</div>
......@@ -16,7 +15,6 @@
<label class="form-field__label" for="shortcut">Zkratka</label>
<div class="form-field__wrapper form-field__wrapper--shadowed">
<input type="text" name="shortcut" class="text-input form-field__control w-40" size="8" maxlength="8" value="" placeholder="thc" />
<button class="btn btn--grey-125 btn--hoveractive ml-4">
<div class="btn__body">Zkrátit</div>
</button>
......
<div class="card elevation-4 space-y-4 mt-2">
<div class="card__body">
<h2>Moje zkratky</h2>
<table id="Shortcuts"
class="table table--bordered"
data-row-style="rowStyle"
data-unique-id="id"
data-url="/api/shortcuts" >
<thead>
<tr>
<th data-field="shortcut" data-width="10" data-width-unit="%" data-sortable="true">Zkratka</th>
<th data-field="url" data-width="90" data-width-unit="%" data-sortable="true">URL</th>
</tr>
</thead>
</table>
</div>
</div>
<script>
$('#Shortcuts').bootstrapTable({
ajaxOptions: {
headers: {
'X-Auth-Token': '<%= $c->current_user->token %>'
}
},
sortable: true,
pagination: true,
pageSize: 50,
paginationParts: ['pageInfo', 'pageList'],
sidePagination: 'server',
// onClickCell: function (field, value, row, $element) {
// window.location.href ='/archive/' + row.id;
// },
});
function rowStyle(row, index) {
return {
css: {
cursor: 'pointer'
}
}
}
</script>
% layout 'default';
<h1 class="head-alt-md md:head-alt-lg max-w-5xl mb-8">Moje zkratky</h1>
<div id="jsGrid"></div>
<script>
jsGrid.validators.url = {
message: 'Zadejte platný URL',
validator: function(value, item) {
let url;
try {
url = new URL(value);
} catch (_) {
return false;
}
return url.protocol === "http:" || url.protocol === "https:";
}
}
$(function() {
$("#jsGrid").jsGrid({
width: "100%",
height: "auto",
inserting: false,
editing: true,
sorting: true,
paging: true,
selecting: false,
autoload: true,
invalidMessage: 'Neplatná data',
deleteConfirm: 'Opravdu chcete smazat zkratku?',
controller: {
loadData: function(filter) {
return $.ajax({
url: "/api/shortcuts",
dataType: "json",
headers: { 'X-Auth-Token': '<%= $c->current_user->token %>' },
data: filter,
});
},
insertItem: function(item) {
return $.ajax({
type: "POST",
dataType: "json",
headers: { 'X-Auth-Token': '<%= $c->current_user->token %>' },
url: "/api/shortcuts",
data: JSON.stringify(item)
});
},
updateItem: function(item) {
return $.ajax({
type: "PUT",
dataType: "json",
headers: { 'X-Auth-Token': '<%= $c->current_user->token %>' },
url: "/api/shortcuts/" + item.id,
data: JSON.stringify(item)
});
},
deleteItem: function(item) {
return $.ajax({
type: "DELETE",
headers: { 'X-Auth-Token': '<%= $c->current_user->token %>' },
url: "/api/shortcuts/" + item.id,
});
},
},
fields: [
{ name: "shortcut", title: "Zkratka", type: "text", width: 50, editing: false },
{ name: "url", title: "URL", type: "text", width: 250, validate: "url" },
{ name: "code", title: "Týp přesměrování", type: "select", width: 70, items: [
{ name: "302 dočasné", code: 302 },
{ name: "301 trvalé", code: 301 },
],
valueField: "code",
textField: "name"
},
{ type: "control", editButton: false, width: 30 }
]
});
});
</script>
<button class="btn <%= $classes %>">
<div class="btn__body <%= $bodyClasses %>"><%= $cta %></div>
</button>
<footer class="border-t border-grey-125 pt-4 flex flex-col md:flex-row md:justify-between space-y-4">
<img src="<%= $c->config->{styleguide} %>images/logo-full-black.svg" class="w-32" alt="Pirátská strana" />
<p>Piráti, 2021. Všechna práva vyhlazena.</p>
</footer>
<a href="<%= $url %>">
<button class="btn btn--icon <%= $classes %>">
<div class="btn__body-wrap">
<div class="btn__body <%= $bodyClasses %>"><%= $cta %></div>
<div class="btn__icon <%= $iconClasses %>">
<i class="ico--<%= $icon %>"></i>
</div>
</div>
</button>
</a>
<nav class="navbar navbar--simple __js-root">
<ui-app inline-template>
<ui-navbar inline-template>
<div>
<div class="container container--wide navbar__content" :class="{'navbar__content--initialized': true}">
<div class="navbar__brand my-4 flex items-center lg:pr-8 lg:my-0">
<a href="#">
<img src="<%= $c->config->{styleguide} %>/images/logo-round-white.svg" class="w-8" />
</a>
<span class="pl-4 font-bold text-xl lg:border-r lg:border-grey-300 lg:pr-8"><%= $c->config->{name} %></span>
</div>
<div class="navbar__menutoggle my-4 flex justify-end lg:hidden">
<a href="#" @click="show = !show" class="no-underline hover:no-underline">
<i class="ico--menu text-3xl"></i>
</a>
</div>
<div v-if="show || isLgScreenSize" class="navbar__main navbar__section navbar__section--expandable container-padding--zero lg:container-padding--auto flex items-center">
<div class="flex-grow">
<!-- MENU -->
</div>
<div class="flex items-center space-x-4">
% if ( $c->is_user_authenticated ) {
<div class="flex items-center space-x-4">
<span class="head-heavy-2xs"><%= $c->current_user->{displayname} %></span>
<div class="avatar avatar--2xs">
<img src="<%= $c->config->{piratar}%><%= $c->current_user->{username} %>.jpg" alt="<%= $c->current_user->{displayname} %>" />
</div>
<a href="/logout"><button class="text-grey-200 hover:text-white"><i class="ico--log-out"></i></button></a>
</div>
% } else {
%= include 'styleguide/icon-button', classes => "btn--grey-125 btn--hoveractive", bodyClasses=>'', iconClasses=>'', icon => 'pirati', cta => "Přihlásit se", url => $c->oidc->authorize ;
% }
</div>
</div>
</div>
</div>
</ui-navbar>
</ui-app>
</nav>
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment