mirror of
https://github.com/Neetpone/ponepaste.git
synced 2025-03-12 06:30:07 +01:00
Various front and backend code improvements
This commit is contained in:
parent
92814b33b2
commit
985bc39a60
18 changed files with 165 additions and 528 deletions
|
@ -11,7 +11,16 @@ $pastes = Paste::with([
|
||||||
'tags' => function($query) {
|
'tags' => function($query) {
|
||||||
$query->select('tags.id', 'name', 'slug');
|
$query->select('tags.id', 'name', 'slug');
|
||||||
}
|
}
|
||||||
])->select(['id', 'user_id', 'title'])->get();
|
])->select(['id', 'user_id', 'title']);
|
||||||
|
|
||||||
|
if (!empty($_GET['q']) && is_string($_GET['q'])) {
|
||||||
|
$tags = explode(',', $_GET['q']);
|
||||||
|
$pastes = $pastes->whereHas('tags', function($query) use ($tags) {
|
||||||
|
$query->where('name', $tags);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
$pastes = $pastes->get();
|
||||||
|
|
||||||
header('Content-Type: application/json; charset=UTF-8');
|
header('Content-Type: application/json; charset=UTF-8');
|
||||||
|
|
||||||
|
|
|
@ -15,4 +15,5 @@ updatePageViews($conn);
|
||||||
// Theme
|
// Theme
|
||||||
$page_template = 'archive';
|
$page_template = 'archive';
|
||||||
$page_title = 'Pastes Archive';
|
$page_title = 'Pastes Archive';
|
||||||
|
array_push($script_bundles, 'archive');
|
||||||
require_once('theme/' . $default_theme . '/common.php');
|
require_once('theme/' . $default_theme . '/common.php');
|
||||||
|
|
|
@ -1,354 +0,0 @@
|
||||||
const $ = function(selector) {
|
|
||||||
return document.querySelector(selector);
|
|
||||||
};
|
|
||||||
|
|
||||||
const $$ = function(selector) {
|
|
||||||
return document.querySelectorAll(selector) || [];
|
|
||||||
};
|
|
||||||
|
|
||||||
const makeEl = function(html) {
|
|
||||||
const template = document.createElement('template');
|
|
||||||
|
|
||||||
template.innerHTML = html.trim();
|
|
||||||
|
|
||||||
return template.content.firstChild;
|
|
||||||
};
|
|
||||||
|
|
||||||
const clearEl = function(el) {
|
|
||||||
while (el.firstChild) {
|
|
||||||
el.removeChild(el.firstChild);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const toggleEl = function(el) {
|
|
||||||
if (el.classList.contains('is-hidden')) {
|
|
||||||
el.classList.remove('is-hidden');
|
|
||||||
} else {
|
|
||||||
el.classList.add('is-hidden');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
const escape = function(unsafe) {
|
|
||||||
return unsafe
|
|
||||||
.replace(/&/g, "&")
|
|
||||||
.replace(/</g, "<")
|
|
||||||
.replace(/>/g, ">")
|
|
||||||
.replace(/"/g, """)
|
|
||||||
.replace(/'/g, "'");
|
|
||||||
};
|
|
||||||
|
|
||||||
class TagsInput {
|
|
||||||
constructor(element, options = {}) {
|
|
||||||
this.element = element;
|
|
||||||
this.tags = [];
|
|
||||||
this.options = options;
|
|
||||||
|
|
||||||
this.maxTags = options.maxTags || 10;
|
|
||||||
this.inputNode = null;
|
|
||||||
this.containerNode = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
attach() {
|
|
||||||
this.element.style.display = 'none';
|
|
||||||
|
|
||||||
this.containerNode = makeEl('<div class="tags-input"></div>');
|
|
||||||
this.inputNode = makeEl('<input class="input" type="text" placeholder="10 tags maximum" value="" />');
|
|
||||||
this.containerNode.appendChild(this.inputNode);
|
|
||||||
|
|
||||||
this.element.parentNode.insertBefore(this.containerNode, this.element.nextSibling);
|
|
||||||
|
|
||||||
/* Load existing tags from input */
|
|
||||||
if (this.element.value) {
|
|
||||||
for (const tag of this.element.value.split(',')) {
|
|
||||||
this.addTag(tag);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Handle addition and removal of tags via key-presses */
|
|
||||||
this.containerNode.addEventListener('keydown', this._handleInputKeyUp.bind(this));
|
|
||||||
|
|
||||||
/* Handle deletions by clicking the delete button */
|
|
||||||
this.containerNode.addEventListener('click', this._handleContainerClick.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
detach() {
|
|
||||||
this.tags.clear();
|
|
||||||
this.containerNode.remove();
|
|
||||||
this.element.style.display = 'inline-block';
|
|
||||||
}
|
|
||||||
|
|
||||||
updateHiddenInputValue() {
|
|
||||||
this.element.value = this.tags.join(',');
|
|
||||||
}
|
|
||||||
|
|
||||||
deleteTagNode(node) {
|
|
||||||
this.tags.splice(this.tags.indexOf(node.dataset.value.toLowerCase()), 1);
|
|
||||||
node.remove();
|
|
||||||
|
|
||||||
/* Below the limit? Make sure the input is enabled. */
|
|
||||||
if (this.tags.length < this.maxTags) {
|
|
||||||
this.inputNode.disabled = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
addTag(tagValue) {
|
|
||||||
tagValue = tagValue.trim();
|
|
||||||
|
|
||||||
/* Tag value is probably not empty and we don't already have the same tag. */
|
|
||||||
if (tagValue !== '' && this.tags.indexOf(tagValue.toLowerCase()) === -1) {
|
|
||||||
this.tags.push(tagValue.toLowerCase());
|
|
||||||
|
|
||||||
this.inputNode.parentNode.insertBefore(
|
|
||||||
makeEl('<span class="tag is-info" data-value="' + escape(tagValue) + '">' + escape(tagValue) + '<span class="delete is-small" /></span>'),
|
|
||||||
this.inputNode
|
|
||||||
);
|
|
||||||
|
|
||||||
/* Too many tags, disable the input for now. */
|
|
||||||
if (this.tags.length >= this.maxTags) {
|
|
||||||
this.inputNode.disabled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_handleInputKeyUp(evt) {
|
|
||||||
let tagValue = this.inputNode.value;
|
|
||||||
|
|
||||||
if (evt.key === 'Backspace' && tagValue === '') {
|
|
||||||
// Remove the child
|
|
||||||
if (this.inputNode.previousSibling) {
|
|
||||||
this.deleteTagNode(this.inputNode.previousSibling);
|
|
||||||
|
|
||||||
this.updateHiddenInputValue();
|
|
||||||
}
|
|
||||||
} else if (evt.key === ',') {
|
|
||||||
this.addTag(tagValue);
|
|
||||||
|
|
||||||
this.inputNode.value = '';
|
|
||||||
this.updateHiddenInputValue();
|
|
||||||
|
|
||||||
evt.preventDefault();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
_handleContainerClick(evt) {
|
|
||||||
if (evt.target && evt.target.classList.contains('delete')) {
|
|
||||||
this.deleteTagNode(evt.target.closest('.tag'));
|
|
||||||
this.updateHiddenInputValue();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class SimplePaginator {
|
|
||||||
constructor(element) {
|
|
||||||
this.element = element;
|
|
||||||
}
|
|
||||||
|
|
||||||
attach(pageCallback) {
|
|
||||||
this.element.addEventListener('click', evt => {
|
|
||||||
if (evt.target && evt.target.classList.contains('paginator__button')) {
|
|
||||||
pageCallback(+evt.target.dataset.page);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
update(totalRecords, perPage, currentPage) {
|
|
||||||
clearEl(this.element);
|
|
||||||
|
|
||||||
/* First and last page in existence */
|
|
||||||
const firstPage = 0;
|
|
||||||
const lastPage = Math.floor(totalRecords / perPage); // ish?
|
|
||||||
const numPagesToShow = 2;
|
|
||||||
|
|
||||||
/* First and last page the main paginator will show */
|
|
||||||
const firstPageShow = (currentPage - firstPage) < numPagesToShow ? firstPage : ((currentPage - numPagesToShow < 0) ? currentPage : currentPage - numPagesToShow);
|
|
||||||
const lastPageShow = (firstPageShow + numPagesToShow) > lastPage ? lastPage : (firstPageShow + numPagesToShow + numPagesToShow);
|
|
||||||
|
|
||||||
/* Whether to show the first and last pages in existence at the ends of the paginator */
|
|
||||||
const showFirstPage = (Math.abs(firstPage - currentPage)) > (numPagesToShow);
|
|
||||||
const showLastPage = (Math.abs(lastPage - currentPage)) > (numPagesToShow);
|
|
||||||
|
|
||||||
|
|
||||||
const prevButtonDisabled = currentPage === firstPage ? 'disabled' : '';
|
|
||||||
|
|
||||||
/* Previous button */
|
|
||||||
this.element.appendChild(makeEl(
|
|
||||||
`<a class="paginator__button previous ${prevButtonDisabled}" data-page="${currentPage - 1}">Previous</a>`
|
|
||||||
));
|
|
||||||
|
|
||||||
/* First page button */
|
|
||||||
if (showFirstPage) {
|
|
||||||
this.element.appendChild(makeEl(
|
|
||||||
`<a class="paginator__button" data-page="${firstPage}">${firstPage}</a>`
|
|
||||||
));
|
|
||||||
this.element.appendChild(makeEl(`<span class="ellipsis">…</span>`));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* "window" buttons */
|
|
||||||
for (let i = firstPageShow; i <= lastPageShow; i++) {
|
|
||||||
const selected = (i === currentPage ? 'paginator__button--selected' : '');
|
|
||||||
this.element.appendChild(makeEl(
|
|
||||||
`<a class="paginator__button ${selected}" data-page="${i}">${i}</a>`
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Last page button */
|
|
||||||
if (showLastPage) {
|
|
||||||
this.element.appendChild(makeEl(`<span class="ellipsis">…</span>`));
|
|
||||||
this.element.appendChild(makeEl(
|
|
||||||
`<a class="paginator__button" data-page="${lastPage}">${lastPage}</a>`
|
|
||||||
));
|
|
||||||
}
|
|
||||||
|
|
||||||
const nextButtonDisabled = currentPage === lastPage ? 'disabled' : '';
|
|
||||||
/* Next button */
|
|
||||||
this.element.appendChild(makeEl(
|
|
||||||
`<a class="paginator__button next ${nextButtonDisabled}" data-page="${currentPage + 1}">Next</a>`
|
|
||||||
));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
class DataTable {
|
|
||||||
constructor(element, options) {
|
|
||||||
this.element = element;
|
|
||||||
this.container = element.parentElement;
|
|
||||||
this.options = options;
|
|
||||||
|
|
||||||
this.ajaxCallback = options.ajaxCallback;
|
|
||||||
this.data = [];
|
|
||||||
|
|
||||||
this.totalRecords = -1;
|
|
||||||
this.perPage = 20;
|
|
||||||
this.currentPage = 0;
|
|
||||||
|
|
||||||
this.paginator = new SimplePaginator(this.container.querySelector('.paginator'));
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
attach() {
|
|
||||||
this.paginator.attach(this._updatePage.bind(this));
|
|
||||||
this._loadEntries();
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Load the requested data from the server, and when done, update the DOM. */
|
|
||||||
_loadEntries() {
|
|
||||||
new Promise(this.ajaxCallback)
|
|
||||||
.then(this._updateEntries.bind(this));
|
|
||||||
}
|
|
||||||
|
|
||||||
/* Update the DOM to reflect the current state of the data we have loaded */
|
|
||||||
_updateEntries(data) {
|
|
||||||
this.data = data.data;
|
|
||||||
this.totalRecords = this.data.length;
|
|
||||||
|
|
||||||
const bodyElement = this.element.querySelector('tbody');
|
|
||||||
clearEl(bodyElement);
|
|
||||||
|
|
||||||
const firstIndex = (this.perPage * this.currentPage);
|
|
||||||
const lastIndex = (firstIndex + this.perPage) > this.totalRecords ? this.totalRecords : (firstIndex + this.perPage);
|
|
||||||
|
|
||||||
|
|
||||||
for (let i = firstIndex; i < lastIndex; i++) {
|
|
||||||
const rowElem = makeEl(this.options.rowCallback(this.data[i]));
|
|
||||||
rowElem.classList.add(i % 2 === 0 ? 'odd' : 'even');
|
|
||||||
|
|
||||||
bodyElement.appendChild(rowElem);
|
|
||||||
}
|
|
||||||
|
|
||||||
this.paginator.update(this.totalRecords, this.perPage, this.currentPage);
|
|
||||||
}
|
|
||||||
|
|
||||||
_updatePage(n) {
|
|
||||||
this.currentPage = n;
|
|
||||||
this.paginator.update(this.totalRecords, this.perPage, this.currentPage);
|
|
||||||
this._updateEntries({data: this.data});
|
|
||||||
}
|
|
||||||
|
|
||||||
_updateSort(field, direction) {
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const setupSite = function() {
|
|
||||||
Array.prototype.forEach.call($$('.js-tag-input'), (el) => {
|
|
||||||
new TagsInput(el).attach();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (document.querySelector('#archive')) {
|
|
||||||
const table = new DataTable(document.querySelector('#archive'), {
|
|
||||||
ajaxCallback: (resolve) => {
|
|
||||||
fetch('/api/ajax_pastes.php')
|
|
||||||
.then(r => r.json())
|
|
||||||
.then(resolve);
|
|
||||||
},
|
|
||||||
rowCallback: (rowData) => {
|
|
||||||
const tags = rowData.tags.map((tagData) => {
|
|
||||||
let tagColorClass;
|
|
||||||
if (tagData.name.indexOf('nsfw') !== -1) {
|
|
||||||
tagColorClass = 'is-danger';
|
|
||||||
} else if (tagData.name.indexOf('safe') !== -1) {
|
|
||||||
tagColorClass = 'is-success';
|
|
||||||
} else if (tagData.name.indexOf('/') !== -1) {
|
|
||||||
tagColorClass = 'is-primary';
|
|
||||||
} else {
|
|
||||||
tagColorClass = 'is-info';
|
|
||||||
}
|
|
||||||
|
|
||||||
return `<a href="/tags/${tagData.slug}">
|
|
||||||
<span class="tag ${tagColorClass}">${escape(tagData.name)}</span>
|
|
||||||
</a>`;
|
|
||||||
}).join('');
|
|
||||||
|
|
||||||
return `<tr>
|
|
||||||
<td><a href="/${rowData.id}">${escape(rowData.title)}</a></td>
|
|
||||||
<td><a href="/user/${escape(rowData.author)}">${escape(rowData.author)}</a></td>
|
|
||||||
<td>${tags}</td>
|
|
||||||
</tr>`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
table.attach();
|
|
||||||
}
|
|
||||||
|
|
||||||
const signupButton = $('[data-target~="#signin"],[data-target~="#signup"]');
|
|
||||||
|
|
||||||
if (signupButton) {
|
|
||||||
signupButton.addEventListener('click', () => {
|
|
||||||
$('.modal').classList.add('is-active');
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.modal-button-close').addEventListener('click', () => {
|
|
||||||
$('.modal').classList.remove('is-active');
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const embedButton = $('.panel-tools .embed-tool');
|
|
||||||
|
|
||||||
if (embedButton){
|
|
||||||
embedButton.addEventListener('click', (evt) => {
|
|
||||||
if (evt.target && evt.target.closest('.panel-tools')) {
|
|
||||||
toggleEl(evt.target.closest('.panel-tools').querySelector('.panel-embed'));
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const expandButton = $('.expand-tool');
|
|
||||||
|
|
||||||
if (expandButton) {
|
|
||||||
expandButton.addEventListener('click', (evt) => {
|
|
||||||
if (evt.target && evt.target.closest('.panel')) {
|
|
||||||
const panel = evt.target.closest('.panel');
|
|
||||||
|
|
||||||
if (panel.classList.contains('panel-fullsize')) {
|
|
||||||
panel.classList.remove('panel-fullsize');
|
|
||||||
} else {
|
|
||||||
panel.classList.add('panel-fullsize');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
if (document.readyState !== 'loading') {
|
|
||||||
setupSite();
|
|
||||||
} else {
|
|
||||||
document.addEventListener('DOMContentLoaded', setupSite);
|
|
||||||
}
|
|
2
assets/bundle/bundle.min.js
vendored
2
assets/bundle/bundle.min.js
vendored
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
|
@ -15,6 +15,14 @@ use PonePaste\Models\Paste;
|
||||||
use PonePaste\Models\User;
|
use PonePaste\Models\User;
|
||||||
|
|
||||||
/* View functions */
|
/* View functions */
|
||||||
|
function javascriptIncludeTag(string $name) : string {
|
||||||
|
if (PP_DEBUG) {
|
||||||
|
return "<script src=\"/assets/bundle/${name}.js\"></script>";
|
||||||
|
}
|
||||||
|
|
||||||
|
return "<script src=\"/assets/bundle/${name}.min.js\"></script>";
|
||||||
|
}
|
||||||
|
|
||||||
function urlForPage($page = '') : string {
|
function urlForPage($page = '') : string {
|
||||||
if (!PP_MOD_REWRITE) {
|
if (!PP_MOD_REWRITE) {
|
||||||
$page .= '.php';
|
$page .= '.php';
|
||||||
|
@ -204,6 +212,8 @@ $total_unique_views = PageView::select('tvisit')->orderBy('id', 'desc')->first()
|
||||||
|
|
||||||
$current_user = SessionHelper::currentUser();
|
$current_user = SessionHelper::currentUser();
|
||||||
|
|
||||||
|
$script_bundles = [];
|
||||||
|
|
||||||
/* Security headers */
|
/* Security headers */
|
||||||
header('X-Frame-Options: SAMEORIGIN');
|
header('X-Frame-Options: SAMEORIGIN');
|
||||||
header('X-Content-Type-Options: nosniff');
|
header('X-Content-Type-Options: nosniff');
|
||||||
|
|
|
@ -126,7 +126,7 @@ if ($_SERVER['REQUEST_METHOD'] === 'POST') {
|
||||||
if ($editing) {
|
if ($editing) {
|
||||||
$paste = Paste::find($_POST['paste_id']);
|
$paste = Paste::find($_POST['paste_id']);
|
||||||
if ($current_user &&
|
if ($current_user &&
|
||||||
$current_user->user_id === $paste->user_id) {
|
$current_user->id === $paste->user_id) {
|
||||||
$paste_id = $paste->id;
|
$paste_id = $paste->id;
|
||||||
$paste->update([
|
$paste->update([
|
||||||
'title' => $paste_title,
|
'title' => $paste_title,
|
||||||
|
|
|
@ -1,3 +1,44 @@
|
||||||
import { $ } from './dom';
|
import { escape, whenReady } from './dom';
|
||||||
import { DataTable } from './data_tables';
|
import { DataTable } from './data_tables';
|
||||||
|
import { globalSetup } from './main';
|
||||||
|
|
||||||
|
whenReady(() => {
|
||||||
|
globalSetup();
|
||||||
|
|
||||||
|
const urlParams = new URLSearchParams(window.location.search);
|
||||||
|
const myParam = urlParams.get('q');
|
||||||
|
const apiUrl = myParam !== null ? '/api/ajax_pastes.php?q=' + myParam : '/api/ajax_pastes.php';
|
||||||
|
|
||||||
|
const table = new DataTable(document.getElementById('archive'), {
|
||||||
|
ajaxCallback: (resolve) => {
|
||||||
|
fetch(apiUrl)
|
||||||
|
.then(r => r.json())
|
||||||
|
.then(resolve);
|
||||||
|
},
|
||||||
|
rowCallback: (rowData) => {
|
||||||
|
const tags = rowData.tags.map((tagData) => {
|
||||||
|
let tagColorClass;
|
||||||
|
if (tagData.name.indexOf('nsfw') !== -1) {
|
||||||
|
tagColorClass = 'is-danger';
|
||||||
|
} else if (tagData.name.indexOf('safe') !== -1) {
|
||||||
|
tagColorClass = 'is-success';
|
||||||
|
} else if (tagData.name.indexOf('/') !== -1) {
|
||||||
|
tagColorClass = 'is-primary';
|
||||||
|
} else {
|
||||||
|
tagColorClass = 'is-info';
|
||||||
|
}
|
||||||
|
|
||||||
|
return `<a href="/tags/${tagData.slug}">
|
||||||
|
<span class="tag ${tagColorClass}">${escape(tagData.name)}</span>
|
||||||
|
</a>`;
|
||||||
|
}).join('');
|
||||||
|
|
||||||
|
return `<tr>
|
||||||
|
<td><a href="/${rowData.id}">${escape(rowData.title)}</a></td>
|
||||||
|
<td><a href="/user/${escape(rowData.author)}">${escape(rowData.author)}</a></td>
|
||||||
|
<td>${tags}</td>
|
||||||
|
</tr>`;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
table.attach();
|
||||||
|
});
|
|
@ -37,5 +37,12 @@ const escape = function(unsafe) {
|
||||||
.replace(/'/g, "'");
|
.replace(/'/g, "'");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const whenReady = function(funcp) {
|
||||||
|
if (document.readyState !== 'loading') {
|
||||||
|
funcp();
|
||||||
|
} else {
|
||||||
|
document.addEventListener('DOMContentLoaded', funcp);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
export { $, $$, makeEl, clearEl, toggleEl, escape };
|
export { whenReady, $, $$, makeEl, clearEl, toggleEl, escape };
|
4
js/generic.js
Normal file
4
js/generic.js
Normal file
|
@ -0,0 +1,4 @@
|
||||||
|
import { whenReady } from "./dom";
|
||||||
|
import { globalSetup } from "./main";
|
||||||
|
|
||||||
|
whenReady(globalSetup);
|
88
js/main.js
88
js/main.js
|
@ -1,47 +1,7 @@
|
||||||
import { $, $$, escape, toggleEl } from './dom';
|
import { $, $$, toggleEl } from './dom';
|
||||||
import { TagsInput } from "./tag_input";
|
import { TagsInput } from "./tag_input";
|
||||||
import { DataTable } from "./data_tables";
|
|
||||||
|
|
||||||
const setupSite = function() {
|
|
||||||
Array.prototype.forEach.call($$('.js-tag-input'), (el) => {
|
|
||||||
new TagsInput(el).attach();
|
|
||||||
});
|
|
||||||
|
|
||||||
if (document.querySelector('#archive')) {
|
|
||||||
const table = new DataTable(document.querySelector('#archive'), {
|
|
||||||
ajaxCallback: (resolve) => {
|
|
||||||
fetch('/api/ajax_pastes.php')
|
|
||||||
.then(r => r.json())
|
|
||||||
.then(resolve);
|
|
||||||
},
|
|
||||||
rowCallback: (rowData) => {
|
|
||||||
const tags = rowData.tags.map((tagData) => {
|
|
||||||
let tagColorClass;
|
|
||||||
if (tagData.name.indexOf('nsfw') !== -1) {
|
|
||||||
tagColorClass = 'is-danger';
|
|
||||||
} else if (tagData.name.indexOf('safe') !== -1) {
|
|
||||||
tagColorClass = 'is-success';
|
|
||||||
} else if (tagData.name.indexOf('/') !== -1) {
|
|
||||||
tagColorClass = 'is-primary';
|
|
||||||
} else {
|
|
||||||
tagColorClass = 'is-info';
|
|
||||||
}
|
|
||||||
|
|
||||||
return `<a href="/tags/${tagData.slug}">
|
|
||||||
<span class="tag ${tagColorClass}">${escape(tagData.name)}</span>
|
|
||||||
</a>`;
|
|
||||||
}).join('');
|
|
||||||
|
|
||||||
return `<tr>
|
|
||||||
<td><a href="/${rowData.id}">${escape(rowData.title)}</a></td>
|
|
||||||
<td><a href="/user/${escape(rowData.author)}">${escape(rowData.author)}</a></td>
|
|
||||||
<td>${tags}</td>
|
|
||||||
</tr>`;
|
|
||||||
}
|
|
||||||
});
|
|
||||||
table.attach();
|
|
||||||
}
|
|
||||||
|
|
||||||
|
const setupSignupModal = () => {
|
||||||
const signupButton = $('[data-target~="#signin"],[data-target~="#signup"]');
|
const signupButton = $('[data-target~="#signin"],[data-target~="#signup"]');
|
||||||
|
|
||||||
if (signupButton) {
|
if (signupButton) {
|
||||||
|
@ -53,6 +13,14 @@ const setupSite = function() {
|
||||||
$('.modal').classList.remove('is-active');
|
$('.modal').classList.remove('is-active');
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const globalSetup = () => {
|
||||||
|
Array.prototype.forEach.call($$('.js-tag-input'), (el) => {
|
||||||
|
new TagsInput(el).attach();
|
||||||
|
});
|
||||||
|
|
||||||
|
setupSignupModal();
|
||||||
|
|
||||||
const embedButton = $('.panel-tools .embed-tool');
|
const embedButton = $('.panel-tools .embed-tool');
|
||||||
|
|
||||||
|
@ -79,10 +47,36 @@ const setupSite = function() {
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
|
||||||
if (document.readyState !== 'loading') {
|
// Notifications
|
||||||
setupSite();
|
(document.querySelectorAll('.notification .delete') || []).forEach(($delete) => {
|
||||||
} else {
|
const $notification = $delete.parentNode;
|
||||||
document.addEventListener('DOMContentLoaded', setupSite);
|
|
||||||
|
$delete.addEventListener('click', () => {
|
||||||
|
$notification.parentNode.removeChild($notification);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Hamburger menu
|
||||||
|
const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0);
|
||||||
|
if ($navbarBurgers.length > 0) {
|
||||||
|
$navbarBurgers.forEach(el => {
|
||||||
|
el.addEventListener('click', () => {
|
||||||
|
const target = el.dataset.target;
|
||||||
|
const $target = document.getElementById(target);
|
||||||
|
el.classList.toggle('is-active');
|
||||||
|
$target.classList.toggle('is-active');
|
||||||
|
});
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const preloader = $('.preloader');
|
||||||
|
const main = $('main');
|
||||||
|
|
||||||
|
if (preloader && main) {
|
||||||
|
preloader.remove();
|
||||||
|
main.id = '';
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export { globalSetup };
|
|
@ -8,6 +8,10 @@ use Highlight\Highlighter;
|
||||||
use PonePaste\Models\Paste;
|
use PonePaste\Models\Paste;
|
||||||
use PonePaste\Models\User;
|
use PonePaste\Models\User;
|
||||||
|
|
||||||
|
function isRequesterLikelyBot() : bool {
|
||||||
|
return str_contains(strtolower($_SERVER['HTTP_USER_AGENT']), 'bot');
|
||||||
|
}
|
||||||
|
|
||||||
function rawView($content, $p_code) {
|
function rawView($content, $p_code) {
|
||||||
if ($p_code) {
|
if ($p_code) {
|
||||||
header('Content-Type: text/plain');
|
header('Content-Type: text/plain');
|
||||||
|
@ -147,7 +151,7 @@ if (isset($_GET['raw'])) {
|
||||||
|
|
||||||
// Deletion
|
// Deletion
|
||||||
if (isset($_POST['delete'])) {
|
if (isset($_POST['delete'])) {
|
||||||
if (!$current_user || ($paste_owner_id !== $current_user->user_id)) {
|
if (!$current_user || ($paste_owner_id !== $current_user->id)) {
|
||||||
flashError('You must be logged in and own this paste to delete it.');
|
flashError('You must be logged in and own this paste to delete it.');
|
||||||
} else {
|
} else {
|
||||||
$paste->delete();
|
$paste->delete();
|
||||||
|
@ -210,7 +214,7 @@ if ($password_required && $password_valid) {
|
||||||
}
|
}
|
||||||
|
|
||||||
// View counter
|
// View counter
|
||||||
if (@$_SESSION['not_unique'] !== $paste_id) {
|
if (!isRequesterLikelyBot() && @$_SESSION['not_unique'] !== $paste_id) {
|
||||||
$_SESSION['not_unique'] = $paste_id;
|
$_SESSION['not_unique'] = $paste_id;
|
||||||
$paste->views += 1;
|
$paste->views += 1;
|
||||||
$paste->save();
|
$paste->save();
|
||||||
|
|
|
@ -1,20 +1,27 @@
|
||||||
// noinspection JSUnusedGlobalSymbols
|
// noinspection JSUnusedGlobalSymbols,JSCheckFunctionSignatures
|
||||||
|
|
||||||
import {getBabelOutputPlugin} from '@rollup/plugin-babel';
|
import {getBabelOutputPlugin} from '@rollup/plugin-babel';
|
||||||
import {terser} from 'rollup-plugin-terser';
|
import {terser} from 'rollup-plugin-terser';
|
||||||
|
|
||||||
export default {
|
const output = (name) => {
|
||||||
input: 'js/main.js',
|
return {
|
||||||
|
input: `js/${name}.js`,
|
||||||
output: [
|
output: [
|
||||||
{
|
{
|
||||||
file: 'assets/bundle/bundle.js',
|
file: `assets/bundle/${name}.js`,
|
||||||
format: 'esm'
|
format: 'esm'
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
file: 'assets/bundle/bundle.min.js',
|
file: `assets/bundle/${name}.min.js`,
|
||||||
format: 'esm',
|
format: 'esm',
|
||||||
plugins: [getBabelOutputPlugin({ presets: ['@babel/preset-env'] }), terser()],
|
plugins: [getBabelOutputPlugin({ presets: ['@babel/preset-env'] }), terser()],
|
||||||
sourcemap: true
|
sourcemap: true
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
export default [
|
||||||
|
output('generic'),
|
||||||
|
output('archive')
|
||||||
|
];
|
|
@ -4,7 +4,7 @@
|
||||||
rowReorder: {selector: 'td:nth-child(2)'},
|
rowReorder: {selector: 'td:nth-child(2)'},
|
||||||
responsive: true,
|
responsive: true,
|
||||||
processing: true,
|
processing: true,
|
||||||
language: {processing: '<i class="fa fa-spinner fa-spin fa-3x fa-fw"></i><span class="sr-only">Loading...</span> '},
|
language: {processing: ''},
|
||||||
autoWidth: false,
|
autoWidth: false,
|
||||||
ajax: "api/ajax_pastes.php",
|
ajax: "api/ajax_pastes.php",
|
||||||
initComplete: function () {
|
initComplete: function () {
|
||||||
|
@ -57,6 +57,7 @@
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
<tbody>
|
<tbody>
|
||||||
|
<!-- <i class="fa fa-spinner fa-spin fa-3x fa-fw"></i><span class="sr-only">Loading...</span>-->
|
||||||
<!-- Filled by DataTables -->
|
<!-- Filled by DataTables -->
|
||||||
</tbody>
|
</tbody>
|
||||||
<tfoot>
|
<tfoot>
|
||||||
|
|
|
@ -32,9 +32,6 @@ $flashes = getFlashes();
|
||||||
<link href="//<?= $baseurl ?>/theme/bulma/css/paste.css" rel="stylesheet"/>
|
<link href="//<?= $baseurl ?>/theme/bulma/css/paste.css" rel="stylesheet"/>
|
||||||
<link href="//<?= $baseurl ?>/theme/bulma/css/table-responsive.css" rel="stylesheet"/>
|
<link href="//<?= $baseurl ?>/theme/bulma/css/table-responsive.css" rel="stylesheet"/>
|
||||||
<link href="//<?= $baseurl ?>/theme/bulma/css/table-row-orders.css" rel="stylesheet"/>
|
<link href="//<?= $baseurl ?>/theme/bulma/css/table-row-orders.css" rel="stylesheet"/>
|
||||||
<script src="//<?= $baseurl ?>/theme/bulma/js/jquery.min.js"></script>
|
|
||||||
<script src="//<?= $baseurl ?>/theme/bulma/js/paste.js"></script>
|
|
||||||
<script src="//<?= $baseurl ?>/assets/bundle/<?= PP_DEBUG ? 'bundle.js' : 'bundle.min.js' ?>"></script>
|
|
||||||
</head>
|
</head>
|
||||||
|
|
||||||
<body>
|
<body>
|
||||||
|
@ -288,16 +285,15 @@ $flashes = getFlashes();
|
||||||
</div>
|
</div>
|
||||||
</footer>
|
</footer>
|
||||||
|
|
||||||
|
<script src="//<?= $baseurl ?>/theme/bulma/js/paste.js"></script>
|
||||||
|
<?php if (empty($script_bundles)): ?>
|
||||||
|
<?= javascriptIncludeTag('generic') ?>
|
||||||
|
<?php else: ?>
|
||||||
|
<?php foreach ($script_bundles as $bundle): ?>
|
||||||
|
<?= javascriptIncludeTag($bundle) ?>
|
||||||
|
<?php endforeach; ?>
|
||||||
|
<?php endif; ?>
|
||||||
<script>
|
<script>
|
||||||
const whenReady = (callback) => {
|
|
||||||
if (document.readyState !== 'loading') {
|
|
||||||
callback();
|
|
||||||
} else {
|
|
||||||
document.addEventListener('DOMContentLoaded', callback);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// Tabs function for popup login
|
// Tabs function for popup login
|
||||||
function openTab(evt, tabName) {
|
function openTab(evt, tabName) {
|
||||||
const x = document.getElementsByClassName("content-tab");
|
const x = document.getElementsByClassName("content-tab");
|
||||||
|
@ -313,30 +309,6 @@ $flashes = getFlashes();
|
||||||
document.getElementById(tabName).style.display = "block";
|
document.getElementById(tabName).style.display = "block";
|
||||||
evt.currentTarget.className += " is-active";
|
evt.currentTarget.className += " is-active";
|
||||||
}
|
}
|
||||||
|
|
||||||
whenReady(() => {
|
|
||||||
// Notifications
|
|
||||||
(document.querySelectorAll('.notification .delete') || []).forEach(($delete) => {
|
|
||||||
$notification = $delete.parentNode;
|
|
||||||
|
|
||||||
$delete.addEventListener('click', () => {
|
|
||||||
$notification.parentNode.removeChild($notification);
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
// Hamburger menu
|
|
||||||
const $navbarBurgers = Array.prototype.slice.call(document.querySelectorAll('.navbar-burger'), 0);
|
|
||||||
if ($navbarBurgers.length > 0) {
|
|
||||||
$navbarBurgers.forEach(el => {
|
|
||||||
el.addEventListener('click', () => {
|
|
||||||
const target = el.dataset.target;
|
|
||||||
const $target = document.getElementById(target);
|
|
||||||
el.classList.toggle('is-active');
|
|
||||||
$target.classList.toggle('is-active');
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
});
|
|
||||||
</script>
|
</script>
|
||||||
<script nonce="D4rkm0d3">
|
<script nonce="D4rkm0d3">
|
||||||
const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]');
|
const toggleSwitch = document.querySelector('.theme-switch input[type="checkbox"]');
|
||||||
|
@ -364,8 +336,5 @@ $flashes = getFlashes();
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
|
|
||||||
<!-- Additional Scripts -->
|
|
||||||
<?php /* echo $additional_scripts; */ ?>
|
|
||||||
|
|
||||||
</body>
|
</body>
|
||||||
</html>
|
</html>
|
|
@ -4,10 +4,8 @@
|
||||||
|
|
||||||
/* Bulma */
|
/* Bulma */
|
||||||
@import url("bulma.min.css");
|
@import url("bulma.min.css");
|
||||||
@import url("bulma.modal-fx.min.css");
|
|
||||||
@import url("bulma.tooltip.min.css");
|
@import url("bulma.tooltip.min.css");
|
||||||
@import url("bulma.checkradio.min.css");
|
@import url("bulma.checkradio.min.css");
|
||||||
@import url("bulma.social.all.min.css");
|
|
||||||
|
|
||||||
/* DataTables.js */
|
/* DataTables.js */
|
||||||
@import url("datatables.min.css");
|
@import url("datatables.min.css");
|
||||||
|
|
|
@ -1,40 +1,5 @@
|
||||||
<link rel="stylesheet" href="theme/bulma/css/bulma-tagsinput.min.css"/>
|
<link rel="stylesheet" href="theme/bulma/css/bulma-tagsinput.min.css"/>
|
||||||
<script>
|
<script>
|
||||||
function setupTagsInput() {
|
|
||||||
const tagsInput = document.getElementById('tags-with-source');
|
|
||||||
|
|
||||||
if (tagsInput) {
|
|
||||||
new BulmaTagsInput(tagsInput, {
|
|
||||||
allowDuplicates: false,
|
|
||||||
caseSensitive: false,
|
|
||||||
clearSelectionOnTyping: false,
|
|
||||||
closeDropdownOnItemSelect: true,
|
|
||||||
delimiter: ',',
|
|
||||||
freeInput: true,
|
|
||||||
highlightDuplicate: true,
|
|
||||||
highlightMatchesString: true,
|
|
||||||
itemText: 'name',
|
|
||||||
maxTags: 10,
|
|
||||||
maxChars: 40,
|
|
||||||
minChars: 1,
|
|
||||||
noResultsLabel: 'No results found',
|
|
||||||
placeholder: '10 Tags Maximum"',
|
|
||||||
removable: true,
|
|
||||||
searchMinChars: 1,
|
|
||||||
searchOn: 'text',
|
|
||||||
selectable: true,
|
|
||||||
tagClass: 'is-info',
|
|
||||||
trim: true,
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (document.readyState !== 'loading') {
|
|
||||||
setupTagsInput();
|
|
||||||
} else {
|
|
||||||
document.addEventListener('DOMContentLoaded', setupTagsInput);
|
|
||||||
}
|
|
||||||
|
|
||||||
function openreport() {
|
function openreport() {
|
||||||
var x = document.getElementById("panel");
|
var x = document.getElementById("panel");
|
||||||
if (x.style.display === "none") {
|
if (x.style.display === "none") {
|
||||||
|
@ -53,19 +18,6 @@
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
<script>
|
|
||||||
function preloaderFadeOutInit() {
|
|
||||||
$('.preloader').fadeOut('slow');
|
|
||||||
$('main').attr('id', '');
|
|
||||||
}
|
|
||||||
|
|
||||||
// Window load function
|
|
||||||
jQuery(window).on('load', function () {
|
|
||||||
(function ($) {
|
|
||||||
// preloaderFadeOutInit();
|
|
||||||
})(jQuery);
|
|
||||||
});
|
|
||||||
</script>
|
|
||||||
<?php if ($using_highlighter): ?>
|
<?php if ($using_highlighter): ?>
|
||||||
<link rel="stylesheet" href="/vendor/scrivo/highlight.php/styles/default.css"/>
|
<link rel="stylesheet" href="/vendor/scrivo/highlight.php/styles/default.css"/>
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
|
@ -242,7 +194,7 @@ $selectedloader = "$bg[$i]"; // set variable equal to which random filename was
|
||||||
<?php endif; ?>
|
<?php endif; ?>
|
||||||
</div>
|
</div>
|
||||||
<!-- Guests -->
|
<!-- Guests -->
|
||||||
<?php if ($totalpastes !== 0 && ($current_user === null || $current_user->user_id !== $paste['user_id'])) { ?>
|
<?php if ($totalpastes !== 0 && ($current_user === null || $current_user->id !== $paste_owner_id)) { ?>
|
||||||
<hr>
|
<hr>
|
||||||
<label class="label">More from this Author </label>
|
<label class="label">More from this Author </label>
|
||||||
<?php
|
<?php
|
||||||
|
@ -313,19 +265,16 @@ $selectedloader = "$bg[$i]"; // set variable equal to which random filename was
|
||||||
<p id="charNum"><b>File Size: </b><span style="color: green;">1000/1000Kb</span></p>
|
<p id="charNum"><b>File Size: </b><span style="color: green;">1000/1000Kb</span></p>
|
||||||
<br>
|
<br>
|
||||||
|
|
||||||
|
|
||||||
<div class="columns">
|
<div class="columns">
|
||||||
<div class="column">
|
<div class="column">
|
||||||
<div class="field">
|
<div class="field">
|
||||||
<label class="label">Tags</label>
|
<label class="label">Tags</label>
|
||||||
<div class="control">
|
<div class="control">
|
||||||
<input id="tags-with-source" name="tag_input" class="input"
|
<label class="label" for="field_tags">Tags</label>
|
||||||
value="<?php
|
<div class="control">
|
||||||
$inputtags = $paste['tags'];
|
<input name="tag_input" class="input js-tag-input" id="field_tags"
|
||||||
foreach ($inputtags as $tag) {
|
value="<?= pp_html_escape($paste->tags->map(function($t) { return $t->name; })->join(',')) ?>" />
|
||||||
$tagsName = ucfirst(pp_html_escape($tag['name']));
|
</div>
|
||||||
echo ',' . $tagsName . '';
|
|
||||||
} ?>">
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
@ -418,7 +367,7 @@ $selectedloader = "$bg[$i]"; // set variable equal to which random filename was
|
||||||
Encrypt on Server
|
Encrypt on Server
|
||||||
</label>
|
</label>
|
||||||
<?php
|
<?php
|
||||||
if ($current_user->user_id == $paste['user_id']) {
|
if ($current_user->id == $paste['user_id']) {
|
||||||
?>
|
?>
|
||||||
<input class="button is-info" type="submit" name="edit" id="edit"
|
<input class="button is-info" type="submit" name="edit" id="edit"
|
||||||
value="Edit"/>
|
value="Edit"/>
|
||||||
|
|
2
user.php
2
user.php
|
@ -57,7 +57,7 @@ if (isset($_GET['del'])) {
|
||||||
$paste_id = intval(trim($_GET['id']));
|
$paste_id = intval(trim($_GET['id']));
|
||||||
$paste = Paste::find($paste_id);
|
$paste = Paste::find($paste_id);
|
||||||
|
|
||||||
if (!$paste || $paste->user_id !== $current_user->user_id) {
|
if (!$paste || $paste->user_id !== $current_user->id) {
|
||||||
$error = 'That paste does not exist, or you are not the owner of it.';
|
$error = 'That paste does not exist, or you are not the owner of it.';
|
||||||
} else {
|
} else {
|
||||||
$paste->delete();
|
$paste->delete();
|
||||||
|
|
Loading…
Add table
Reference in a new issue