accomodate plugins system (prism, mermaid)

This commit is contained in:
Kaczanowski Mateusz 2020-10-27 14:14:42 +01:00
parent 1265305bac
commit a8e8476c9c
9 changed files with 541 additions and 215 deletions

35
src/formatter.rs Normal file
View file

@ -0,0 +1,35 @@
use handlebars::{Handlebars, JsonRender};
pub fn new<'r>() -> Handlebars<'r> {
let mut handlebars = Handlebars::new();
handlebars.register_helper("format_url", Box::new(format_helper));
handlebars
}
fn format_helper(
h: &handlebars::Helper,
_: &Handlebars,
_: &handlebars::Context,
_: &mut handlebars::RenderContext,
out: &mut dyn handlebars::Output,
) -> Result<(), handlebars::RenderError> {
let prefix_val = h.param(0).ok_or(handlebars::RenderError::new(
"Param 0 is required for format helper.",
))?;
let uri_val = h.param(1).ok_or(handlebars::RenderError::new(
"Param 1 is required for format helper.",
))?;
let prefix = prefix_val.value().render();
let uri = uri_val.value().render();
let rendered = match uri.starts_with("/") {
true => format!("{}{}", prefix, uri),
false => uri,
};
out.write(rendered.as_ref())?;
Ok(())
}

View file

@ -12,18 +12,18 @@ mod api_generated;
use crate::api_generated::api::{finish_entry_buffer, get_root_as_entry, Entry, EntryArgs};
#[macro_export]
macro_rules! load_static_resources {
( $( $x:expr ),* ) => {
macro_rules! load_static_resources(
{ $($key:expr => $value:expr),+ } => {
{
let mut resources: HashMap<&str, &[u8]> = HashMap::new();
let mut resources: HashMap<&'static str, &'static [u8]> = HashMap::new();
$(
resources.insert($x, include_bytes!($x));
resources.insert($key, include_bytes!($value));
)*
resources
}
};
}
};
);
pub fn compaction_filter_expired_entries(
_: u32,

View file

@ -5,23 +5,27 @@
extern crate rocket;
#[macro_use]
extern crate structopt_derive;
extern crate chrono;
extern crate flatbuffers;
extern crate handlebars;
extern crate nanoid;
extern crate num_cpus;
extern crate regex;
extern crate speculate;
extern crate structopt;
extern crate chrono;
extern crate regex;
mod formatter;
#[macro_use]
mod lib;
use lib::{compaction_filter_expired_entries, get_entry_data, get_extension, new_entry};
mod plugins;
use plugins::plugin::{Plugin, PluginManager};
mod api_generated;
use api_generated::api::get_root_as_entry;
use std::collections::HashMap;
use std::collections::BTreeMap;
use std::io;
use std::io::Cursor;
@ -32,15 +36,15 @@ use rocket::http::{ContentType, Status};
use rocket::response::{Redirect, Response};
use rocket::{Data, State};
use humantime::parse_duration;
use handlebars::Handlebars;
use rocksdb::{Options, DB};
use structopt::StructOpt;
use chrono::NaiveDateTime;
use speculate::speculate;
use serde_json::json;
use handlebars::Handlebars;
use humantime::parse_duration;
use nanoid::nanoid;
use regex::Regex;
use rocksdb::{Options, DB};
use serde_json::json;
use speculate::speculate;
use structopt::StructOpt;
speculate! {
use super::rocket;
@ -247,10 +251,7 @@ struct PastebinConfig {
)]
tls_key: Option<String>,
#[structopt(
long = "uri",
help = "Override default URI",
)]
#[structopt(long = "uri", help = "Override default URI")]
uri: Option<String>,
#[structopt(
@ -267,11 +268,7 @@ struct PastebinConfig {
)]
slug_charset: String,
#[structopt(
long = "slug-len",
help = "Length of URL slug",
default_value = "21"
)]
#[structopt(long = "slug-len", help = "Length of URL slug", default_value = "21")]
slug_len: usize,
#[structopt(
@ -280,6 +277,13 @@ struct PastebinConfig {
default_value = "5 minutes, 10 minutes, 1 hour, 1 day, 1 week, 1 month, 1 year, Never"
)]
ui_expiry_times: Vec<String>,
#[structopt(
long = "plugins",
help = "Enable additional functionalities (ie. prism, mermaid)",
default_value = "prism"
)]
plugins: Vec<String>,
}
fn get_url(cfg: &PastebinConfig) -> String {
@ -307,6 +311,7 @@ fn get_url(cfg: &PastebinConfig) -> String {
}
fn get_error_response<'r>(
handlebars: &Handlebars<'r>,
uri_prefix: String,
html: String,
status: Status,
@ -317,9 +322,7 @@ fn get_error_response<'r>(
"uri_prefix": uri_prefix,
});
let content = Handlebars::new()
.render_template(html.as_str(), &map)
.unwrap();
let content = handlebars.render_template(html.as_str(), &map).unwrap();
Response::build()
.status(status)
@ -367,12 +370,14 @@ fn get<'r>(
id: String,
lang: Option<String>,
state: State<'r, DB>,
resources: State<'r, HashMap<&str, &[u8]>>,
handlebars: State<'r, Handlebars>,
plugin_manager: State<PluginManager>,
ui_expiry_times: State<'r, BTreeMap<String, u64>>,
ui_expiry_default: State<'r, String>,
cfg: State<PastebinConfig>,
) -> Response<'r> {
let html = String::from_utf8_lossy(resources.get("../static/index.html").unwrap()).to_string();
let resources = plugin_manager.static_resources();
let html = String::from_utf8_lossy(resources.get("/static/index.html").unwrap()).to_string();
// handle missing entry
let root = match get_entry_data(&id, &state) {
@ -386,12 +391,13 @@ fn get<'r>(
let map = json!({
"version": VERSION,
"is_error": "true",
"uri_prefix": cfg.uri_prefix
"uri_prefix": cfg.uri_prefix,
"js_imports": plugin_manager.js_imports(),
"css_imports": plugin_manager.css_imports(),
"js_init": plugin_manager.js_init(),
});
let content = Handlebars::new()
.render_template(html.as_str(), &map)
.unwrap();
let content = handlebars.render_template(html.as_str(), &map).unwrap();
return Response::build()
.status(err_kind)
@ -415,6 +421,9 @@ fn get<'r>(
"uri_prefix": cfg.uri_prefix,
"ui_expiry_times": ui_expiry_times.inner(),
"ui_expiry_default": ui_expiry_default.inner(),
"js_imports": plugin_manager.js_imports(),
"css_imports": plugin_manager.css_imports(),
"js_init": plugin_manager.js_init(),
});
if entry.burn() {
@ -423,7 +432,8 @@ fn get<'r>(
map["is_burned"] = json!("true");
map["glyph"] = json!("fa fa-fire");
} else if entry.expiry_timestamp() != 0 {
let time = NaiveDateTime::from_timestamp(entry.expiry_timestamp() as i64, 0).format("%Y-%m-%d %H:%M:%S");
let time = NaiveDateTime::from_timestamp(entry.expiry_timestamp() as i64, 0)
.format("%Y-%m-%d %H:%M:%S");
map["msg"] = json!(format!("This paste will expire on {}.", time));
map["level"] = json!("info");
map["glyph"] = json!("far fa-clock");
@ -433,9 +443,7 @@ fn get<'r>(
map["is_encrypted"] = json!("true");
}
let content = Handlebars::new()
.render_template(html.as_str(), &map)
.unwrap();
let content = handlebars.render_template(html.as_str(), &map).unwrap();
Response::build()
.status(Status::Ok)
@ -447,8 +455,9 @@ fn get<'r>(
#[get("/new?<id>&<level>&<msg>&<glyph>&<url>")]
fn get_new<'r>(
state: State<'r, DB>,
handlebars: State<Handlebars>,
cfg: State<PastebinConfig>,
resources: State<'r, HashMap<&str, &[u8]>>,
plugin_manager: State<PluginManager>,
ui_expiry_times: State<'r, BTreeMap<String, u64>>,
ui_expiry_default: State<'r, String>,
id: Option<String>,
@ -457,7 +466,8 @@ fn get_new<'r>(
msg: Option<String>,
url: Option<String>,
) -> Response<'r> {
let html = String::from_utf8_lossy(resources.get("../static/index.html").unwrap()).to_string();
let resources = plugin_manager.static_resources();
let html = String::from_utf8_lossy(resources.get("/static/index.html").unwrap()).to_string();
let msg = msg.unwrap_or_else(|| String::from(""));
let level = level.unwrap_or_else(|| String::from("secondary"));
let glyph = glyph.unwrap_or_else(|| String::from(""));
@ -474,6 +484,9 @@ fn get_new<'r>(
"uri_prefix": cfg.uri_prefix,
"ui_expiry_times": ui_expiry_times.inner(),
"ui_expiry_default": ui_expiry_default.inner(),
"js_imports": plugin_manager.js_imports(),
"css_imports": plugin_manager.css_imports(),
"js_init": plugin_manager.js_init(),
});
if let Some(id) = id {
@ -484,14 +497,10 @@ fn get_new<'r>(
map["is_encrypted"] = json!("true");
}
map["pastebin_code"] = json!(
std::str::from_utf8(entry.data().unwrap()).unwrap()
);
map["pastebin_code"] = json!(std::str::from_utf8(entry.data().unwrap()).unwrap());
}
let content = Handlebars::new()
.render_template(html.as_str(), &map)
.unwrap();
let content = handlebars.render_template(html.as_str(), &map).unwrap();
Response::build()
.status(Status::Ok)
@ -538,18 +547,26 @@ fn get_binary(id: String, state: State<DB>) -> Response {
#[get("/static/<resource>")]
fn get_static<'r>(
resource: String,
resources: State<'r, HashMap<&str, &[u8]>>,
handlebars: State<Handlebars>,
plugin_manager: State<PluginManager>,
cfg: State<PastebinConfig>,
) -> Response<'r> {
let pth = format!("../static/{}", resource);
let resources = plugin_manager.static_resources();
let pth = format!("/static/{}", resource);
let ext = get_extension(resource.as_str()).replace(".", "");
let content = match resources.get(pth.as_str()) {
Some(data) => data,
None => {
let html =
String::from_utf8_lossy(resources.get("../static/index.html").unwrap()).to_string();
return get_error_response(cfg.uri_prefix.clone(), html, Status::NotFound);
String::from_utf8_lossy(resources.get("/static/index.html").unwrap()).to_string();
return get_error_response(
handlebars.inner(),
cfg.uri_prefix.clone(),
html,
Status::NotFound,
);
}
};
let content_type = ContentType::from_extension(ext.as_str()).unwrap();
@ -563,7 +580,12 @@ fn get_static<'r>(
#[get("/")]
fn index(cfg: State<PastebinConfig>) -> Redirect {
let url = String::from(Path::new(cfg.uri_prefix.as_str()).join("new").to_str().unwrap());
let url = String::from(
Path::new(cfg.uri_prefix.as_str())
.join("new")
.to_str()
.unwrap(),
);
Redirect::to(url)
}
@ -610,7 +632,7 @@ fn rocket(pastebin_config: PastebinConfig) -> rocket::Rocket {
let mut alphabet: Vec<char> = vec![];
// match all printable ASCII characters
for i in 0x20 .. 0x7e as u8 {
for i in 0x20..0x7e as u8 {
let c = i as char;
if re.is_match(c.encode_utf8(&mut tmp)) {
@ -629,17 +651,26 @@ fn rocket(pastebin_config: PastebinConfig) -> rocket::Rocket {
if sub_elem.trim().to_lowercase() == "never" {
all.insert(sub_elem.trim().to_string(), 0);
} else {
all.insert(sub_elem.trim().to_string(), parse_duration(sub_elem).unwrap().as_secs());
all.insert(
sub_elem.trim().to_string(),
parse_duration(sub_elem).unwrap().as_secs(),
);
}
}
}
}
}
all
};
let ui_expiry_default: String = ui_expiry_times
.iter()
.filter_map(|(key, &val)| if val == pastebin_config.ttl { Some(key.clone()) } else { None })
.filter_map(|(key, &val)| {
if val == pastebin_config.ttl {
Some(key.clone())
} else {
None
}
})
.collect();
if ui_expiry_default.is_empty() {
@ -654,26 +685,35 @@ fn rocket(pastebin_config: PastebinConfig) -> rocket::Rocket {
panic!("selected slug alphabet is empty, please check if slug_charset is a valid regex");
}
let plugins: Vec<Box<dyn Plugin>> = pastebin_config
.plugins
.iter()
.map(|t| match t.as_str() {
"prism" => Box::new(plugins::prism::new()),
"mermaid" => Box::new(plugins::mermaid::new()),
_ => panic!("unknown plugin provided"),
})
.map(|x| x as Box<dyn plugins::plugin::Plugin>)
.collect();
let plugin_manager = plugins::new(plugins);
let uri_prefix = pastebin_config.uri_prefix.clone();
let resources = load_static_resources!(
"../static/index.html",
"../static/custom.js",
"../static/custom.css",
"../static/prism.js",
"../static/prism.css",
"../static/favicon.ico"
);
// run rocket
rocket::custom(rocket_config)
.manage(pastebin_config)
.manage(db)
.manage(resources)
.manage(formatter::new())
.manage(plugin_manager)
.manage(alphabet)
.manage(ui_expiry_times)
.manage(ui_expiry_default)
.mount(
if uri_prefix == "" { "/" } else { uri_prefix.as_str() },
if uri_prefix == "" {
"/"
} else {
uri_prefix.as_str()
},
routes![index, create, remove, get, get_new, get_raw, get_binary, get_static],
)
}

37
src/plugins.rs Normal file
View file

@ -0,0 +1,37 @@
pub mod mermaid;
pub mod plugin;
pub mod prism;
use std::collections::HashMap;
pub fn new<'r>(plugins: Vec<Box<dyn plugin::Plugin<'r>>>) -> plugin::PluginManager<'r> {
let base_static_resources = load_static_resources!(
"/static/index.html" => "../static/index.html",
"/static/custom.js" => "../static/custom.js",
"/static/custom.css" => "../static/custom.css",
"/static/favicon.ico" => "../static/favicon.ico"
);
let base_css_imports = vec![
"https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/css/bootstrap.min.css",
"https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.0/css/all.min.css",
"/static/custom.css",
];
let base_js_imports = vec![
"https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js",
"https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js",
"https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js",
"https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/js/bootstrap.min.js",
"https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.4/clipboard.min.js",
"https://cdnjs.cloudflare.com/ajax/libs/bootstrap-notify/0.2.0/js/bootstrap-notify.min.js",
"/static/custom.js",
];
plugin::PluginManager::build()
.plugins(plugins)
.static_resources(base_static_resources)
.css_imports(base_css_imports)
.js_imports(base_js_imports)
.finalize()
}

12
src/plugins/mermaid.rs Normal file
View file

@ -0,0 +1,12 @@
use std::collections::HashMap;
use crate::plugins::plugin::PastebinPlugin;
pub fn new<'r>() -> PastebinPlugin<'r> {
PastebinPlugin {
css_imports: vec![],
js_imports: vec!["https://cdnjs.cloudflare.com/ajax/libs/mermaid/8.8.2/mermaid.min.js"],
js_init: Some("mermaid.init(undefined, '.language-mermaid');"),
static_resources: HashMap::new(),
}
}

181
src/plugins/plugin.rs Normal file
View file

@ -0,0 +1,181 @@
use std::collections::HashMap;
pub trait Plugin<'r>: Sync + Send {
fn css_imports(&self) -> &Vec<&'r str>;
fn js_imports(&self) -> &Vec<&'r str>;
fn js_init(&self) -> Option<&'r str>;
fn static_resources(&self) -> &HashMap<&'r str, &'r [u8]>;
}
#[derive(Debug)]
pub struct PastebinPlugin<'r> {
pub css_imports: Vec<&'r str>,
pub js_imports: Vec<&'r str>,
pub js_init: Option<&'r str>,
pub static_resources: HashMap<&'r str, &'r [u8]>,
}
impl<'r> Plugin<'r> for PastebinPlugin<'r> {
fn css_imports(&self) -> &Vec<&'r str> {
&self.css_imports
}
fn js_imports(&self) -> &Vec<&'r str> {
&self.js_imports
}
fn js_init(&self) -> Option<&'r str> {
self.js_init
}
fn static_resources(&self) -> &HashMap<&'r str, &'r [u8]> {
&self.static_resources
}
}
pub struct PluginManagerBuilder<'r> {
manager: PluginManager<'r>,
}
impl<'r> PluginManagerBuilder<'r> {
pub fn plugins(&mut self, plugins: Vec<Box<dyn Plugin<'r>>>) -> &mut PluginManagerBuilder<'r> {
self.manager.set_plugins(plugins);
self
}
pub fn css_imports(&mut self, css_imports: Vec<&'r str>) -> &mut PluginManagerBuilder<'r> {
self.manager.set_css_imports(css_imports);
self
}
pub fn js_imports(&mut self, js_imports: Vec<&'r str>) -> &mut PluginManagerBuilder<'r> {
self.manager.set_js_imports(js_imports);
self
}
pub fn static_resources(
&mut self,
static_resources: HashMap<&'r str, &'r [u8]>,
) -> &mut PluginManagerBuilder<'r> {
self.manager.set_static_resources(static_resources);
self
}
pub fn finalize(&mut self) -> PluginManager<'r> {
self.manager.build_css_imports();
self.manager.build_js_imports();
self.manager.build_js_init();
self.manager.build_static_resources();
std::mem::replace(&mut self.manager, PluginManager::new())
}
}
pub struct PluginManager<'r> {
// plugins are used to build up the static members of the struct, for instance:
// * js_imports (ie. "{{uri_prefix}}/static/prism.js")
// * static_resources (files under static/ directory - compiled with the binary)
plugins: Vec<Box<dyn Plugin<'r>>>,
css_imports: Vec<&'r str>,
js_imports: Vec<&'r str>,
js_init: Vec<&'r str>,
static_resources: HashMap<&'r str, &'r [u8]>,
}
impl<'r> PluginManager<'r> {
pub fn new() -> PluginManager<'r> {
PluginManager {
plugins: vec![],
css_imports: vec![],
js_imports: vec![],
js_init: vec![],
static_resources: HashMap::new(),
}
}
pub fn build() -> PluginManagerBuilder<'r> {
PluginManagerBuilder {
manager: PluginManager::new(),
}
}
pub fn set_plugins(&mut self, plugins: Vec<Box<dyn Plugin<'r>>>) {
self.plugins = plugins;
}
pub fn set_css_imports(&mut self, css_imports: Vec<&'r str>) {
self.css_imports = css_imports;
}
pub fn css_imports(&self) -> Vec<&'r str> {
self.css_imports.clone()
}
pub fn set_js_imports(&mut self, js_imports: Vec<&'r str>) {
self.js_imports = js_imports;
}
pub fn js_imports(&self) -> Vec<&'r str> {
self.js_imports.clone()
}
pub fn set_js_init(&mut self, js_init: Vec<&'r str>) {
self.js_init = js_init;
}
pub fn js_init(&self) -> Vec<&'r str> {
self.js_init.clone()
}
pub fn set_static_resources(&mut self, static_resources: HashMap<&'r str, &'r [u8]>) {
self.static_resources = static_resources;
}
pub fn static_resources(&self) -> HashMap<&'r str, &'r [u8]> {
self.static_resources.clone()
}
fn build_css_imports(&mut self) {
self.set_css_imports(
self.plugins
.iter()
.flat_map(|p| p.css_imports().into_iter())
.chain((&self.css_imports).into_iter())
.map(|&val| val)
.collect(),
)
}
fn build_js_imports(&mut self) {
self.set_js_imports(
self.plugins
.iter()
.flat_map(|p| p.js_imports().into_iter())
.chain((&self.js_imports).into_iter())
.map(|&val| val)
.collect(),
)
}
fn build_js_init(&mut self) {
self.set_js_init(
self.plugins
.iter()
.flat_map(|p| p.js_init().into_iter())
.chain((&self.js_init).into_iter().map(|&val| val))
.collect(),
)
}
fn build_static_resources(&mut self) {
self.set_static_resources(
self.plugins
.iter()
.flat_map(|p| p.static_resources().into_iter())
.chain((&self.static_resources).into_iter())
.map(|(&key, &val)| (key, val))
.collect(),
)
}
}

18
src/plugins/prism.rs Normal file
View file

@ -0,0 +1,18 @@
use std::collections::HashMap;
use crate::plugins::plugin::PastebinPlugin;
pub fn new<'r>() -> PastebinPlugin<'r> {
PastebinPlugin {
css_imports: vec!["/static/prism.css"],
js_imports: vec!["/static/prism.js"],
js_init: Some(
"var holder = $('#pastebin-code-block:first').get(0); \
if (holder) { Prism.highlightElement(holder); }",
),
static_resources: load_static_resources! {
"/static/prism.js" => "../../static/prism.js",
"/static/prism.css" =>"../../static/prism.css"
},
}
}

View file

@ -1,161 +1,162 @@
$(document).ready(function() {
function replaceUrlParam(url, param, value) {
if (value == null) {
value = '';
}
function replaceUrlParam(url, param, value) {
if (value == null) {
value = '';
}
var pattern = new RegExp('\\b('+param+'=).*?(&|#|$)');
if (url.search(pattern)>=0) {
return url.replace(pattern,'$1' + value + '$2');
}
var pattern = new RegExp('\\b('+param+'=).*?(&|#|$)');
if (url.search(pattern)>=0) {
return url.replace(pattern,'$1' + value + '$2');
}
url = url.replace(/[?#]$/,'');
return url + (url.indexOf('?')>0 ? '&' : '?') + param + '=' + value;
}
url = url.replace(/[?#]$/,'');
return url + (url.indexOf('?')>0 ? '&' : '?') + param + '=' + value;
}
function resetLanguageSelector() {
var url = new URL(document.location);
var params = url.searchParams;
var lang = params.get("lang");
function resetLanguageSelector() {
var url = new URL(document.location);
var params = url.searchParams;
var lang = params.get("lang");
if (lang != null) {
$("#language-selector").val(lang);
} else {
if($("#pastebin-code-block").length) {
$("#language-selector").val(
$("#pastebin-code-block").prop("class").trim().split('-')[1]
);
}
}
}
if (lang != null) {
$("#language-selector").val(lang);
} else {
if($("#pastebin-code-block").length) {
$("#language-selector").val(
$("#pastebin-code-block").prop("class").trim().split('-')[1]
);
}
}
}
function getDefaultExpiryTime() {
var expiry = $("#expiry-dropdown-btn").text().split("Expires: ")[1];
return $("#expiry-dropdown a:contains('"+ expiry +"')").attr('href');
}
function checkPasswordModal() {
if ($("#password-modal").length) {
$('#password-modal').modal('toggle');
}
}
function checkPasswordModal() {
if ($("#password-modal").length) {
$('#password-modal').modal('toggle');
}
}
resetLanguageSelector();
checkPasswordModal();
resetLanguageSelector();
checkPasswordModal();
init_plugins();
var state = {
expiry: getDefaultExpiryTime(),
burn: 0,
};
var state = {
expiry: getDefaultExpiryTime(),
burn: 0,
};
$("#language-selector").change(function() {
if ($("#pastebin-code-block").length) {
$('#pastebin-code-block').attr('class', 'language-' + $("#language-selector").val());
Prism.highlightElement($('#pastebin-code-block')[0]);
}
});
$("#language-selector").change(function() {
if ($("#pastebin-code-block").length) {
$('#pastebin-code-block').attr('class', 'language-' + $("#language-selector").val());
init_plugins();
}
});
$("#remove-btn").on("click", function(event) {
event.preventDefault();
$("#remove-btn").on("click", function(event) {
event.preventDefault();
$.ajax({
url: window.location.pathname,
type: 'DELETE',
success: function(result) {
uri = uri_prefix + "/new";
uri = replaceUrlParam(uri, 'level', "info");
uri = replaceUrlParam(uri, 'glyph', "fas fa-info-circle");
uri = replaceUrlParam(uri, 'msg', "The paste has been successfully removed.");
window.location.href = encodeURI(uri);
}
});
});
$.ajax({
url: window.location.pathname,
type: 'DELETE',
success: function(result) {
uri = uri_prefix + "/new";
uri = replaceUrlParam(uri, 'level', "info");
uri = replaceUrlParam(uri, 'glyph', "fas fa-info-circle");
uri = replaceUrlParam(uri, 'msg', "The paste has been successfully removed.");
window.location.href = encodeURI(uri);
}
});
});
$("#copy-btn").on("click", function(event) {
event.preventDefault();
$("#copy-btn").on("click", function(event) {
event.preventDefault();
$(".toolbar-item button").get(0).click();
$(".toolbar-item button").get(0).click();
var $this = $(this);
$this.text("Copied!");
$this.attr("disabled", "disabled");
var $this = $(this);
$this.text("Copied!");
$this.attr("disabled", "disabled");
setTimeout(function() {
$this.text("Copy");
$this.removeAttr("disabled");
}, 800);
setTimeout(function() {
$this.text("Copy");
$this.removeAttr("disabled");
}, 800);
});
});
$("#send-btn").on("click", function(event) {
event.preventDefault();
$("#send-btn").on("click", function(event) {
event.preventDefault();
uri = uri_prefix == "" ? "/" : uri_prefix;
uri = replaceUrlParam(uri, 'lang', $("#language-selector").val());
uri = replaceUrlParam(uri, 'ttl', state.expiry);
uri = replaceUrlParam(uri, 'burn', state.burn);
uri = uri_prefix == "" ? "/" : uri_prefix;
uri = replaceUrlParam(uri, 'lang', $("#language-selector").val());
uri = replaceUrlParam(uri, 'ttl', state.expiry);
uri = replaceUrlParam(uri, 'burn', state.burn);
var data = $("#content-textarea").val();
var pass = $("#pastebin-password").val();
var data = $("#content-textarea").val();
var pass = $("#pastebin-password").val();
if ($("#pastebin-password").val().length > 0) {
data = CryptoJS.AES.encrypt(data, pass).toString();
uri = replaceUrlParam(uri, 'encrypted', true);
}
if ($("#pastebin-password").val().length > 0) {
data = CryptoJS.AES.encrypt(data, pass).toString();
uri = replaceUrlParam(uri, 'encrypted', true);
}
$.ajax({
url: uri,
type: 'POST',
data: data,
success: function(result) {
uri = uri_prefix + "/new";
uri = replaceUrlParam(uri, 'level', "success");
uri = replaceUrlParam(uri, 'glyph', "fas fa-check");
uri = replaceUrlParam(uri, 'msg', "The paste has been successfully created:");
uri = replaceUrlParam(uri, 'url', result);
$.ajax({
url: uri,
type: 'POST',
data: data,
success: function(result) {
uri = uri_prefix + "/new";
uri = replaceUrlParam(uri, 'level', "success");
uri = replaceUrlParam(uri, 'glyph', "fas fa-check");
uri = replaceUrlParam(uri, 'msg', "The paste has been successfully created:");
uri = replaceUrlParam(uri, 'url', result);
window.location.href = encodeURI(uri);
}
});
});
window.location.href = encodeURI(uri);
}
});
});
$('#expiry-dropdown a').click(function(event){
event.preventDefault();
$('#expiry-dropdown a').click(function(event){
event.preventDefault();
state.expiry = $(this).attr("href");
$('#expiry-dropdown-btn').text("Expires: " + this.innerHTML);
});
state.expiry = $(this).attr("href");
$('#expiry-dropdown-btn').text("Expires: " + this.innerHTML);
});
$('#burn-dropdown a').click(function(event){
event.preventDefault();
$('#burn-dropdown a').click(function(event){
event.preventDefault();
state.burn = $(this).attr("href");
$('#burn-dropdown-btn').text("Burn: " + this.innerHTML);
});
state.burn = $(this).attr("href");
$('#burn-dropdown-btn').text("Burn: " + this.innerHTML);
});
$('#decrypt-btn').click(function(event) {
var pass = $("#modal-password").val();
$('#decrypt-btn').click(function(event) {
var pass = $("#modal-password").val();
var data = "";
if ($("#pastebin-code-block").length) {
data = $("#pastebin-code-block").text();
data = $("#pastebin-code-block").text();
} else {
data = $("#content-textarea").text();
data = $("#content-textarea").text();
}
var decrypted = CryptoJS.AES.decrypt(data, pass).toString(CryptoJS.enc.Utf8);
if (decrypted.length == 0) {
$("#modal-alert").removeClass("collapse");
} else {
var decrypted = CryptoJS.AES.decrypt(data, pass).toString(CryptoJS.enc.Utf8);
if (decrypted.length == 0) {
$("#modal-alert").removeClass("collapse");
} else {
if ($("#pastebin-code-block").length) {
$("#pastebin-code-block").text(decrypted);
Prism.highlightElement($('#pastebin-code-block')[0]);
$("#pastebin-code-block").text(decrypted);
init_plugins();
} else {
$("#content-textarea").text(decrypted);
$("#content-textarea").text(decrypted);
}
$("#modal-close-btn").click();
$("#modal-alert").alert('close');
}
});
$("#modal-close-btn").click();
$("#modal-alert").alert('close');
}
});
});

View file

@ -9,11 +9,10 @@
<title>Pastebin</title>
<link rel="icon" href="/static/favicon.ico">
<link href="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/css/bootstrap.min.css" rel="stylesheet">
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/font-awesome/5.13.0/css/all.min.css" crossorigin="anonymous">
<link href="{{uri_prefix}}/static/prism.css" rel="stylesheet" />
<link href="{{uri_prefix}}/static/custom.css" rel="stylesheet" />
{{#each css_imports as |url|}}
<link href="{{format_url ../uri_prefix url}}" rel="stylesheet" />
{{/each}}
</head>
<body>
@ -27,16 +26,16 @@
<li class="nav-item active">
<a class="nav-link" href="{{uri_prefix}}/new">New <span class="sr-only">(current)</span></a>
</li>
{{#if is_created}}
{{#if (not is_burned)}}
<li class="nav-item">
<a class="nav-link" href="#" id="remove-btn">Remove</a>
</li>
<li class="nav-item">
{{#if is_created}}
{{#if (not is_burned)}}
<li class="nav-item">
<a class="nav-link" href="#" id="remove-btn">Remove</a>
</li>
<li class="nav-item">
<a class="nav-link" href="{{uri_prefix}}/new?id={{pastebin_id}}" id="clone-btn">Clone</a>
</li>
{{/if}}
{{/if}}
</li>
{{/if}}
{{/if}}
{{#if is_editable}}
<li class="nav-item dropdown">
@ -242,8 +241,8 @@
</div>
</div>
</div>
{{ else }}
{{#if msg}}
{{ else }}
{{#if msg}}
<div class="mt-3 alert alert-{{level}} alert-dismissible fade show" role="alert">
{{#if glyph}}<i class="{{glyph}}"></i>{{/if}}
{{#if url}}{{msg}} <a href="{{url}}">{{url}}</a>{{else}}{{msg}}{{/if}}
@ -251,16 +250,16 @@
<span aria-hidden="true">&times;</span>
</button>
</div>
{{/if}}
{{/if}}
{{#if is_editable}}
{{#if is_editable}}
<div class="form-group">
<textarea class="form-control" id="content-textarea" rows="25">{{pastebin_code}}</textarea>
</div>
{{/if}}
{{#if is_created or is_clone}}
<pre><code id="pastebin-code-block" class="language-{{pastebin_language}}">{{pastebin_code}}</code></pre>
{{/if}}
{{/if}}
{{#if is_created or is_clone}}
<pre><code id="pastebin-code-block" class="language-{{pastebin_language}}">{{pastebin_code}}</code></pre>
{{/if}}
{{/if}}
</main>
@ -268,7 +267,7 @@
<span class="text-muted ml-4"><a href="https://github.com/mkaczanowski/pastebin"><i class="fab fa-github"></i></a> - pastebin v{{version}}</span>
</footer>
{{#if is_encrypted}}
{{#if is_encrypted}}
<div class="modal fade" id="password-modal" tabindex="-1" role="dialog" aria-labelledby="password-modal-label" aria-hidden="true">
<div class="modal-dialog" role="document">
<div class="modal-content">
@ -299,16 +298,19 @@
</div>
</div>
</div>
{{/if}}
{{/if}}
<script>var uri_prefix="{{uri_prefix}}"</script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/jquery/3.2.1/jquery.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/crypto-js/4.0.0/crypto-js.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/popper.js/1.12.9/umd/popper.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/twitter-bootstrap/4.0.0/js/bootstrap.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/clipboard.js/2.0.4/clipboard.min.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/bootstrap-notify/0.2.0/js/bootstrap-notify.min.js"></script>
<script src="{{uri_prefix}}/static/prism.js"></script>
<script src="{{uri_prefix}}/static/custom.js"></script>
<script>
var uri_prefix="{{uri_prefix}}";
function init_plugins() {
{{#each js_init as |fn|}}{{fn}}
{{/each}}
}
</script>
{{#each js_imports as |url|}}
<script src="{{format_url ../uri_prefix url}}"></script>
{{/each}}
</body>
</html>