view release on metacpan or search on metacpan
lib/Ado/Manual/Contributing.pod view on Meta::CPAN
=head2 MODEL
Put your database related code under the L<Model> namespace.
Use L<DBIx::Simple::Class>.
Generate your classes from tables using C<dsc_dump_schema.pl> utility
that comes with DBIx::Simple::Class.
Feel free to contribute to L<DBIx::Simple::Class>.
Minimize SQL complexity. Write ANSI SQL if you need to write SQL.
Write your SQL in a way to be compatible with
L<SQLite|http://sqlite.org/>, L<MySQL|http://dev.mysql.com/>
and L<PostgreSQL|http://www.postgresql.org/>.
Use the L<DBIx::Simple::Class/SQL> feature to achieve this.
See L<DBIx::Simple::Class/SQL> and its source to figure out how to do it.
To guess the current driver:
use DBI::Const::GetInfoType;
#...
my $dbms = $self->dbh->get_info( $GetInfoType{SQL_DBMS_NAME} );
if($dbms eq 'SQLite'){
...
}
public/vendor/pagedown/Markdown.Converter.js view on Meta::CPAN
Markdown.Converter = function () {
var pluginHooks = this.hooks = new HookCollection();
// given a URL that was encountered by itself (without markup), should return the link text that's to be given to this link
pluginHooks.addNoop("plainLinkText");
// called with the orignal text as given to makeHtml. The result of this plugin hook is the actual markdown source that will be cooked
pluginHooks.addNoop("preConversion");
// called with the text once all normalizations have been completed (tabs to spaces, line endings, etc.), but before any conversions have
pluginHooks.addNoop("postNormalization");
// Called with the text before / after creating block elements like code blocks and lists. Note that this is called recursively
// with inner content, e.g. it's called with the full text, and then only with the content of a blockquote. The inner
// call will receive outdented text.
pluginHooks.addNoop("preBlockGamut");
pluginHooks.addNoop("postBlockGamut");
// called with the text of a single block element before / after the span-level conversions (bold, code spans, etc.) have been made
pluginHooks.addNoop("preSpanGamut");
pluginHooks.addNoop("postSpanGamut");
// called with the final cooked HTML code. The result of this plugin hook is the actual output of makeHtml
pluginHooks.addNoop("postConversion");
//
// Private state of the converter instance:
//
// Global hashes, used by various utility routines
var g_urls;
var g_titles;
var g_html_blocks;
public/vendor/pagedown/Markdown.Converter.js view on Meta::CPAN
// Convert all tabs to spaces.
text = _Detab(text);
// Strip any lines consisting only of spaces and tabs.
// This makes subsequent regexen easier to write, because we can
// match consecutive blank lines with /\n+/ instead of something
// contorted like /[ \t]*\n+/ .
text = text.replace(/^[ \t]+$/mg, "");
text = pluginHooks.postNormalization(text);
// Turn block-level HTML blocks into hash entries
text = _HashHTMLBlocks(text);
// Strip link definitions, store in hashes.
text = _StripLinkDefinitions(text);
text = _RunBlockGamut(text);
text = _UnescapeSpecialChars(text);
// attacklab: Restore dollar signs
text = text.replace(/~D/g, "$$");
// attacklab: Restore tildes
text = text.replace(/~T/g, "~");
text = pluginHooks.postConversion(text);
g_html_blocks = g_titles = g_urls = null;
return text;
};
function _StripLinkDefinitions(text) {
//
// Strips link definitions from text, stores the URLs and titles in
// hash references.
public/vendor/pagedown/Markdown.Converter.js view on Meta::CPAN
// Do Horizontal Rules:
var replacement = "<hr />\n";
text = text.replace(/^[ ]{0,2}([ ]?\*[ ]?){3,}[ \t]*$/gm, replacement);
text = text.replace(/^[ ]{0,2}([ ]?-[ ]?){3,}[ \t]*$/gm, replacement);
text = text.replace(/^[ ]{0,2}([ ]?_[ ]?){3,}[ \t]*$/gm, replacement);
text = _DoLists(text);
text = _DoCodeBlocks(text);
text = _DoBlockQuotes(text);
text = pluginHooks.postBlockGamut(text, blockGamutHookCallback);
// We already ran _HashHTMLBlocks() before, in Markdown(), but that
// was to escape raw HTML in the original Markdown source. This time,
// we're escaping the markup we've just created, so that we don't wrap
// <p> tags around block-level tags.
text = _HashHTMLBlocks(text);
text = _FormParagraphs(text, doNotUnhash);
return text;
}
public/vendor/pagedown/Markdown.Converter.js view on Meta::CPAN
text = _DoAutoLinks(text);
text = text.replace(/~P/g, "://"); // put in place to prevent autolinking; reset now
text = _EncodeAmpsAndAngles(text);
text = _DoItalicsAndBold(text);
// Do hard breaks:
text = text.replace(/ +\n/g, " <br>\n");
text = pluginHooks.postSpanGamut(text);
return text;
}
function _EscapeSpecialCharsWithinTagAttributes(text) {
//
// Within tags -- meaning between < and > -- encode [\ ` * _] so they
// don't conflict with their use in Markdown for code, italics and strong.
//
public/vendor/pagedown/Markdown.Editor.js view on Meta::CPAN
options.strings = options.strings || {};
if (options.helpButton) {
options.strings.help = options.strings.help || options.helpButton.title;
}
var getString = function (identifier) { return options.strings[identifier] || defaultsStrings[identifier]; }
idPostfix = idPostfix || "";
var hooks = this.hooks = new Markdown.HookCollection();
hooks.addNoop("onPreviewRefresh"); // called with no arguments after the preview has been refreshed
hooks.addNoop("postBlockquoteCreation"); // called with the user's selection *after* the blockquote was created; should return the actual to-be-inserted text
hooks.addFalse("insertImageDialog"); /* called with one parameter: a callback to be called with the URL of the image. If the application creates
* its own image insertion dialog, this hook should return true, and the callback should be called with the chosen
* image url (or null if the user cancelled). If this hook returns false, the default dialog will be used.
*/
this.getConverter = function () { return markdownConverter; }
var that = this,
panels;
public/vendor/pagedown/Markdown.Editor.js view on Meta::CPAN
// select the non-existent link text and discard the selection in the
// textarea. The solution to this is to cache the textarea selection
// on the button's mousedown event and set a flag. In the part of the
// code where we need to grab the selection, we check for the flag
// and, if it's set, use the cached area instead of querying the
// textarea.
//
// This ONLY affects Internet Explorer (tested on versions 6, 7
// and 8) and ONLY on button clicks. Keyboard shortcuts work
// normally since the focus never leaves the textarea.
function PanelCollection(postfix) {
this.buttonBar = doc.getElementById("wmd-button-bar" + postfix);
this.preview = doc.getElementById("wmd-preview" + postfix);
this.input = doc.getElementById("wmd-input" + postfix);
};
// Returns true if the DOM element is visible, false if it's hidden.
// Checks if display is anything other than none.
util.isVisible = function (elem) {
if (window.getComputedStyle) {
// Most browsers
return window.getComputedStyle(elem, null).getPropertyValue("display") !== "none";
}
public/vendor/pagedown/Markdown.Editor.js view on Meta::CPAN
};
// Converts \r\n and \r to \n.
util.fixEolChars = function (text) {
text = text.replace(/\r\n/g, "\n");
text = text.replace(/\r/g, "\n");
return text;
};
// Extends a regular expression. Returns a new RegExp
// using pre + regex + post as the expression.
// Used in a few functions where we have a base
// expression and we want to pre- or append some
// conditions to it (e.g. adding "$" to the end).
// The flags are unchanged.
//
// regex is a RegExp, pre and post are strings.
util.extendRegExp = function (regex, pre, post) {
if (pre === null || pre === undefined) {
pre = "";
}
if (post === null || post === undefined) {
post = "";
}
var pattern = regex.toString();
var flags;
// Replace the flags with empty space and store them.
pattern = pattern.replace(/\/([gim]*)$/, function (wholeMatch, flagsPart) {
flags = flagsPart;
return "";
});
// Remove the slash delimiters on the regular expression.
pattern = pattern.replace(/(^\/|\/$)/g, "");
pattern = pre + pattern + post;
return new re(pattern, flags);
}
// UNFINISHED
// The assignment in the while loop makes jslint cranky.
// I'll change it to a better loop later.
position.getTop = function (elem, isInner) {
var result = elem.offsetTop;
if (!isInner) {
public/vendor/pagedown/Markdown.Editor.js view on Meta::CPAN
range.collapse(false);
range.moveStart("character", -defTextLen);
range.moveEnd("character", defTextLen);
range.select();
}
input.focus();
}, 0);
};
function UIManager(postfix, panels, undoManager, previewManager, commandManager, helpOptions, getString) {
var inputBox = panels.input,
buttons = {}; // buttons.undo, buttons.link, etc. The actual DOM elements.
makeSpritedButtonRow();
var keyEvent = "keydown";
if (uaSniffed.isOpera) {
keyEvent = "keypress";
}
public/vendor/pagedown/Markdown.Editor.js view on Meta::CPAN
function makeSpritedButtonRow() {
var buttonBar = panels.buttonBar;
var normalYShift = "0px";
var disabledYShift = "-20px";
var highlightYShift = "-40px";
var buttonRow = document.createElement("ul");
buttonRow.id = "wmd-button-row" + postfix;
buttonRow.className = 'wmd-button-row';
buttonRow = buttonBar.appendChild(buttonRow);
var xPosition = 0;
var makeButton = function (id, title, XShift, textOp) {
var button = document.createElement("li");
button.className = "wmd-button";
button.style.left = xPosition + "px";
xPosition += 25;
var buttonImage = document.createElement("span");
button.id = id + postfix;
button.appendChild(buttonImage);
button.title = title;
button.XShift = XShift;
if (textOp)
button.textOp = textOp;
setupButton(button, true);
buttonRow.appendChild(button);
return button;
};
var makeSpacer = function (num) {
var spacer = document.createElement("li");
spacer.className = "wmd-spacer wmd-spacer" + num;
spacer.id = "wmd-spacer" + num + postfix;
buttonRow.appendChild(spacer);
xPosition += 25;
}
buttons.bold = makeButton("wmd-bold-button", getString("bold"), "0px", bindCommand("doBold"));
buttons.italic = makeButton("wmd-italic-button", getString("italic"), "-20px", bindCommand("doItalic"));
makeSpacer(1);
buttons.link = makeButton("wmd-link-button", getString("link"), "-40px", bindCommand(function (chunk, postProcessing) {
return this.doLinkOrImage(chunk, postProcessing, false);
}));
buttons.quote = makeButton("wmd-quote-button", getString("quote"), "-60px", bindCommand("doBlockquote"));
buttons.code = makeButton("wmd-code-button", getString("code"), "-80px", bindCommand("doCode"));
buttons.image = makeButton("wmd-image-button", getString("image"), "-100px", bindCommand(function (chunk, postProcessing) {
return this.doLinkOrImage(chunk, postProcessing, true);
}));
makeSpacer(2);
buttons.olist = makeButton("wmd-olist-button", getString("olist"), "-120px", bindCommand(function (chunk, postProcessing) {
this.doList(chunk, postProcessing, true);
}));
buttons.ulist = makeButton("wmd-ulist-button", getString("ulist"), "-140px", bindCommand(function (chunk, postProcessing) {
this.doList(chunk, postProcessing, false);
}));
buttons.heading = makeButton("wmd-heading-button", getString("heading"), "-160px", bindCommand("doHeading"));
buttons.hr = makeButton("wmd-hr-button", getString("hr"), "-180px", bindCommand("doHorizontalRule"));
makeSpacer(3);
buttons.undo = makeButton("wmd-undo-button", getString("undo"), "-200px", null);
buttons.undo.execute = function (manager) { if (manager) manager.undo(); };
var redoTitle = /win/.test(nav.platform.toLowerCase()) ?
getString("redo") :
getString("redomac"); // mac and other non-Windows platforms
buttons.redo = makeButton("wmd-redo-button", redoTitle, "-220px", null);
buttons.redo.execute = function (manager) { if (manager) manager.redo(); };
if (helpOptions) {
var helpButton = document.createElement("li");
var helpButtonImage = document.createElement("span");
helpButton.appendChild(helpButtonImage);
helpButton.className = "wmd-button wmd-help-button";
helpButton.id = "wmd-help-button" + postfix;
helpButton.XShift = "-240px";
helpButton.isHelp = true;
helpButton.style.right = "0px";
helpButton.title = getString("help");
helpButton.onclick = helpOptions.handler;
setupButton(helpButton, true);
buttonRow.appendChild(helpButton);
buttons.help = helpButton;
}
public/vendor/pagedown/Markdown.Editor.js view on Meta::CPAN
chunk.selection = chunk.selection.replace(regex, function (line, marked) {
if (new re("^" + that.prefixes, "").test(line)) {
return line;
}
return marked + "\n";
});
chunk.selection = chunk.selection.replace(/\s+$/, "");
};
commandProto.doBold = function (chunk, postProcessing) {
return this.doBorI(chunk, postProcessing, 2, this.getString("boldexample"));
};
commandProto.doItalic = function (chunk, postProcessing) {
return this.doBorI(chunk, postProcessing, 1, this.getString("italicexample"));
};
// chunk: The selected region that will be enclosed with */**
// nStars: 1 for italics, 2 for bold
// insertText: If you just click the button without highlighting text, this gets inserted
commandProto.doBorI = function (chunk, postProcessing, nStars, insertText) {
// Get rid of whitespace and fixup newlines.
chunk.trimWhitespace();
chunk.selection = chunk.selection.replace(/\n{2,}/g, "\n");
// Look for stars before and after. Is the chunk already marked up?
// note that these regex matches cannot fail
var starsBefore = /(\**$)/.exec(chunk.before)[0];
var starsAfter = /(^\**)/.exec(chunk.after)[0];
public/vendor/pagedown/Markdown.Editor.js view on Meta::CPAN
return querypart.replace(/\+/g, "%2b"); // since we replaced plus with spaces in the query part, all pluses that now appear where originally encoded
});
if (title) {
title = title.trim ? title.trim() : title.replace(/^\s*/, "").replace(/\s*$/, "");
title = title.replace(/"/g, "quot;").replace(/\(/g, "(").replace(/\)/g, ")").replace(/</g, "<").replace(/>/g, ">");
}
return title ? link + ' "' + title + '"' : link;
});
}
commandProto.doLinkOrImage = function (chunk, postProcessing, isImage) {
chunk.trimWhitespace();
chunk.findTags(/\s*!?\[/, /\][ ]?(?:\n[ ]*)?(\[.*?\])?/);
var background;
if (chunk.endTag.length > 1 && chunk.startTag.length > 0) {
chunk.startTag = chunk.startTag.replace(/!?\[/, "");
chunk.endTag = "";
this.addLinkDef(chunk, null);
public/vendor/pagedown/Markdown.Editor.js view on Meta::CPAN
if (!chunk.selection) {
if (isImage) {
chunk.selection = that.getString("imagedescription");
}
else {
chunk.selection = that.getString("linkdescription");
}
}
}
postProcessing();
};
background = ui.createBackground();
if (isImage) {
if (!this.hooks.insertImageDialog(linkEnteredCallback))
ui.prompt(this.getString("imagedialog"), imageDefaultText, linkEnteredCallback);
}
else {
ui.prompt(this.getString("linkdialog"), linkDefaultText, linkEnteredCallback);
}
return true;
}
};
// When making a list, hitting shift-enter will put your cursor on the next line
// at the current indent level.
commandProto.doAutoindent = function (chunk, postProcessing) {
var commandMgr = this,
fakeSelection = false;
chunk.before = chunk.before.replace(/(\n|^)[ ]{0,3}([*+-]|\d+[.])[ \t]*\n$/, "\n\n");
chunk.before = chunk.before.replace(/(\n|^)[ ]{0,3}>[ \t]*\n$/, "\n\n");
chunk.before = chunk.before.replace(/(\n|^)[ \t]+\n$/, "\n\n");
// There's no selection, end the cursor wasn't at the end of the line:
// The user wants to split the current list item / code line / blockquote line
public/vendor/pagedown/Markdown.Editor.js view on Meta::CPAN
commandMgr.doCode(chunk);
}
}
if (fakeSelection) {
chunk.after = chunk.selection + chunk.after;
chunk.selection = "";
}
};
commandProto.doBlockquote = function (chunk, postProcessing) {
chunk.selection = chunk.selection.replace(/^(\n*)([^\r]+?)(\n*)$/,
function (totalMatch, newlinesBefore, text, newlinesAfter) {
chunk.before += newlinesBefore;
chunk.after = newlinesAfter + chunk.after;
return text;
});
chunk.before = chunk.before.replace(/(>[ \t]*)$/,
function (totalMatch, blankLine) {
public/vendor/pagedown/Markdown.Editor.js view on Meta::CPAN
if (!/^(\n|^)[ ]{0,3}>/.test(chunk.selection) && chunk.startTag) {
chunk.startTag = chunk.startTag.replace(/\n{0,2}$/, "\n\n");
}
if (!/(\n|^)[ ]{0,3}>.*$/.test(chunk.selection) && chunk.endTag) {
chunk.endTag = chunk.endTag.replace(/^\n{0,2}/, "\n\n");
}
}
chunk.selection = this.hooks.postBlockquoteCreation(chunk.selection);
if (!/\n/.test(chunk.selection)) {
chunk.selection = chunk.selection.replace(/^(> *)/,
function (wholeMatch, blanks) {
chunk.startTag += blanks;
return "";
});
}
};
commandProto.doCode = function (chunk, postProcessing) {
var hasTextBefore = /\S[ ]*$/.test(chunk.before);
var hasTextAfter = /^[ ]*\S/.test(chunk.after);
// Use 'four space' markdown if the selection is on its own
// line or is multiline.
if ((!hasTextAfter && !hasTextBefore) || /\n/.test(chunk.selection)) {
chunk.before = chunk.before.replace(/[ ]{4}$/,
function (totalMatch) {
public/vendor/pagedown/Markdown.Editor.js view on Meta::CPAN
else if (chunk.endTag && !chunk.startTag) {
chunk.before += chunk.endTag;
chunk.endTag = "";
}
else {
chunk.startTag = chunk.endTag = "";
}
}
};
commandProto.doList = function (chunk, postProcessing, isNumberedList) {
// These are identical except at the very beginning and end.
// Should probably use the regex extension function to make this clearer.
var previousItemsRegex = /(\n|^)(([ ]{0,3}([*+-]|\d+[.])[ \t]+.*)(\n.+|\n{2,}([*+-].*|\d+[.])[ \t]+.*|\n{2,}[ \t]+\S.*)*)\n*$/;
var nextItemsRegex = /^\n*(([ ]{0,3}([*+-]|\d+[.])[ \t]+.*)(\n.+|\n{2,}([*+-].*|\d+[.])[ \t]+.*|\n{2,}[ \t]+\S.*)*)\n*/;
// The default bullet is a dash but others are possible.
// This has nothing to do with the particular HTML bullet,
// it's just a markdown bullet.
var bullet = "-";
public/vendor/pagedown/Markdown.Editor.js view on Meta::CPAN
chunk.trimWhitespace(true);
chunk.skipLines(nLinesUp, nLinesDown, true);
chunk.startTag = prefix;
var spaces = prefix.replace(/./g, " ");
this.wrap(chunk, SETTINGS.lineLength - spaces.length);
chunk.selection = chunk.selection.replace(/\n/g, "\n" + spaces);
};
commandProto.doHeading = function (chunk, postProcessing) {
// Remove leading/trailing whitespace and reduce internal spaces to single spaces.
chunk.selection = chunk.selection.replace(/\s+/g, " ");
chunk.selection = chunk.selection.replace(/(^\s+|\s+$)/g, "");
// If we clicked the button with no selected text, we just
// make a level 2 hash header around some default text.
if (!chunk.selection) {
chunk.startTag = "## ";
chunk.selection = this.getString("headingexample");
public/vendor/pagedown/Markdown.Editor.js view on Meta::CPAN
if (len > SETTINGS.lineLength) {
len = SETTINGS.lineLength;
}
chunk.endTag = "\n";
while (len--) {
chunk.endTag += headerChar;
}
}
};
commandProto.doHorizontalRule = function (chunk, postProcessing) {
chunk.startTag = "----------\n";
chunk.selection = "";
chunk.skipLines(2, 1, true);
}
})();
public/vendor/pagedown/Markdown.Sanitizer.js view on Meta::CPAN
if (typeof exports === "object" && typeof require === "function") { // we're in a CommonJS (e.g. Node.js) module
output = exports;
Converter = require("./Markdown.Converter").Converter;
} else {
output = window.Markdown;
Converter = output.Converter;
}
output.getSanitizingConverter = function () {
var converter = new Converter();
converter.hooks.chain("postConversion", sanitizeHtml);
converter.hooks.chain("postConversion", balanceTags);
return converter;
}
function sanitizeHtml(html) {
return html.replace(/<[^>]*>?/gi, sanitizeTag);
}
// (tags that can be opened/closed) | (tags that stand alone)
var basic_tag_whitelist = /^(<\/?(b|blockquote|code|del|dd|dl|dt|em|h1|h2|h3|i|kbd|li|ol|p|pre|s|sup|sub|strong|strike|ul)>|<(br|hr)\s?\/?>)$/i;
// <a href="url..." optional title>|</a>
t/command/adoplugin-01.t view on Meta::CPAN
$ado->routes->find('controlleraction')->remove();
$ado->routes->find('controlleractionid')->remove();
# $ado->start("routes");
# Test generated routes
$t->get_ok('/testatii.json')->status_is(200);
$t->get_ok('/testatii/list?format=html')->status_is(200)
->content_like(qr|table.+id</th>.+permissions</th.+Hello</td.+Hello2|smx);
# needs login
$t->post_ok(
'/testatii/create.html' => form => {
title => 'Hello3',
body =>
'Ðла, бала, ниÑа ÑÑÑÑка паниÑа, Хей гиди ÐанÑо, Ð½Ð°Ñ ÐºÐ°Ð¿Ð¸ÑанÑо...'
}
)->status_is(302) #requires authentication
->header_is('Location' => '/login', 'redirected to /login');
my $login_url = $t->tx->res->headers->header('Location');
# Login after following the Location header (redirect)
t/command/adoplugin-01.t view on Meta::CPAN
#get the csrf fields
my $form = $t->tx->res->dom->at('#login_form');
my $csrf_token = $form->at('[name="csrf_token"]')->{value};
my $form_hash = {
_method => 'login/ado',
login_name => 'test1',
login_password => '',
csrf_token => $csrf_token,
digest => Mojo::Util::sha1_hex($csrf_token . Mojo::Util::sha1_hex('test1test1')),
};
$t->post_ok($login_url => {} => form => $form_hash)->status_is(302)
->header_is('Location' => '/testatii/create.html', 'redirected back to /testatii/create');
my $create_url = $t->tx->res->headers->header('Location');
#now we can create a new resouce after authenticating
$t->post_ok(
$create_url => {Accept => 'text/html'} => form => {
title => 'Hello3',
body =>
'Ðла, бала, ниÑа ÑÑÑÑка паниÑа, Хей гиди ÐанÑо, Ð½Ð°Ñ ÐºÐ°Ð¿Ð¸ÑанÑо...'
}
);
$t->get_ok('/testatii/read/3.html')->status_is(200)
->content_like(qr|Hello3|smx, 'reading created content - ok');
=pod
t/command/crud.t view on Meta::CPAN
#Run tests on the generated code?!?!
unshift @{$ado->renderer->paths}, catdir($tempdir, 'site_templates');
$t->get_ok('/testatii/list', {Accept => 'text/plain'})->status_is(415)
->content_like(qr|Unsupported.+Please.+list\.json|x, 'Unsupported Media Type - ok');
$t->get_ok('/testatii/list.json')->status_is(200);
$t->get_ok('/testatii/list.html')->status_is(200)
->content_like(qr|table.+id</th>.+permissions</th.+Hello</td.+Hello2|smx);
$t->post_ok(
'/testatii/create.html' => form => {
title => 'Hello3',
body =>
'Ðла, бала, ниÑа ÑÑÑÑка паниÑа, Хей гиди ÐанÑо, Ð½Ð°Ñ ÐºÐ°Ð¿Ð¸ÑанÑо...'
}
);
$t->get_ok('/testatii/read/3.html')->status_is(200)
->content_like(qr|Hello3|smx, 'reading content - ok');
$t->put_ok(
'/testatii/update/3.html' => form => {
t/plugin/auth-01.t view on Meta::CPAN
my $test_auth_url = $t->ua->server->url->path('/test/authenticateduser');
$t->get_ok('/login/ado', {Referer => $test_auth_url})->status_is(200)
->element_exists('section.ui.login_form form#login_form')
->text_like('form#login_form .ui.header:nth-child(1)' => qr'Sign in')
->element_exists('#login_name')->element_exists('#login_password');
#try unexisting login method
my $help_url = $t->ua->server->url->path('/help');
$t->get_ok('/login/alabala', {Referer => $help_url})->status_is(401)
->element_exists_not('input[name="_method"][checked="checked"]');
$t->post_ok('/login/alabala', {Referer => $help_url})->status_is(401)
->text_like('.ui.error.message' => qr/of the supported login methods/);
#try condition (redirects to /login url)
my $login_url =
$t->get_ok('/test/authenticateduser')->status_is(302)->header_like('Location' => qr|/login$|)
->tx->res->headers->header('Location');
#login after following the Location header (redirect)
$t->get_ok($login_url);
#get the csrf fields
my $form = $t->tx->res->dom->at('#login_form');
my $csrf_token = $form->at('[name="csrf_token"]')->{value};
my $form_hash = {
_method => 'login/ado',
login_name => 'test1',
login_password => '',
csrf_token => $csrf_token,
digest => Mojo::Util::sha1_hex($csrf_token . Mojo::Util::sha1_hex('test1' . 'wrong_pass')),
};
$t->post_ok($login_url => {} => form => $form_hash)->status_is(401);
#try with wrong (unchanged) csrf token
$t->post_ok($login_url => {DNT => 1} => form => $form_hash)->status_is(403, 'Wrong csrf_token')
; #403 Forbidden
#try with no user passed
$t->get_ok($login_url);
delete $form_hash->{login_name};
$form_hash->{csrf_token} = $t->tx->res->dom->at('#login_form [name="csrf_token"]')->{value};
$form_hash->{digest} =
Mojo::Util::sha1_hex($form_hash->{csrf_token} . Mojo::Util::sha1_hex('' . 'wrong_pass'));
$t->post_ok($login_url => {} => form => $form_hash)->status_is(401, 'No login_name');
#try with no data
$t->post_ok($login_url)->status_is(401, 'No $val->has_data');
#try with unexisting user
$t->get_ok($login_url);
$form_hash->{login_name} = 'alabala';
$form_hash->{csrf_token} = $t->tx->res->dom->at('#login_form [name="csrf_token"]')->{value};
$form_hash->{digest} =
Mojo::Util::sha1_hex($form_hash->{csrf_token} . Mojo::Util::sha1_hex('' . 'wrong_pass'));
$t->post_ok($login_url => {} => form => $form_hash)->status_is(401, 'No such user $login_name')
->text_like('#error_login', qr'Wrong credentials! Please try again!')
->text_like('#error_login_name', qr"No such user '$form_hash->{login_name}'!");
done_testing;
t/plugin/auth-02.t view on Meta::CPAN
# It is expected all plugins to load their routes by the time the generic routes load.
# This means it is ok to keep them in ado.conf
#http://localhost:3000/perldoc/Mojolicious/Guides/Routing#Rearranging-routes
$ado->routes->find('controller')->remove();
$ado->routes->find('controlleraction')->remove();
$ado->routes->find('controlleractionid')->remove();
$ado->routes->get('/test/ingroup')->over(ingroup => 'test1')->to('test#ingroup');
my $csrf_token =
$t->get_ok('/login')->tx->res->dom->at('#login_form [name="csrf_token"]')->{value};
$t->post_ok(
'/login' => {} => form => {
_method => 'login/ado',
csrf_token => $csrf_token,
}
)->status_is(401 => 'user submitted empty form 401 ok');
my $login_url =
$t->get_ok('/test/authenticateduser')->status_is(302)->header_like('Location' => qr|/login$|)
->tx->res->headers->header('Location');
$t->get_ok('/login/ado');
#try again with the right password this time
my $form = $t->tx->res->dom->at('#login_form');
my $new_csrf_token = $form->at('[name="csrf_token"]')->{value};
ok($new_csrf_token ne $csrf_token, '$new_csrf_token is different');
$t->post_ok(
$login_url => {},
form => {
_method => 'login/ado',
login_name => 'test1',
login_password => '',
csrf_token => $new_csrf_token,
digest => Mojo::Util::sha1_hex($new_csrf_token . Mojo::Util::sha1_hex('test1' . 'test1')),
}
#redirect back to the $c->session('over_route')