Unfortunately, your browser isn't able to display this book. "+
- "If possible, try again in another browser or on another device.
";
-Monocle.Reader.LOAD_FAILURE_INFO =
- "Sorry, parts of the book could not be retrieved.
"+
- "Please check your connection and refresh to try again.
";
-/* BOOK */
-
-/* The Book handles movement through the content by the reader page elements.
- *
- * It's responsible for instantiating components as they are required,
- * and for calculating which component and page number to move to (based on
- * requests from the Reader).
- *
- */
-
-Monocle.Book = function (dataSource, preloadWindow) {
-
- var API = { constructor: Monocle.Book }
- var k = API.constants = API.constructor;
- var p = API.properties = {
- dataSource: dataSource,
- preloadWindow: preloadWindow,
- cmptLoadQueue: {},
- components: [],
- chapters: {} // flat arrays of chapters per component
- }
-
-
- function initialize() {
- p.componentIds = dataSource.getComponents();
- p.contents = dataSource.getContents();
- p.lastCIndex = p.componentIds.length - 1;
- }
-
-
- // Adjusts the given locus object to provide the page number within the
- // current component.
- //
- // If the locus implies movement to another component, the locus
- // 'componentId' property will be updated to point to this component, and
- // the 'load' property will be set to true, which should be taken as a
- // sign to call loadPageAt with a callback.
- //
- // The locus argument is an object that has one of the following properties:
- //
- // - page: positive integer. Counting up from the start of component.
- // - pagesBack: negative integer. Counting back from the end of component.
- // - percent: float indicating percentage through the component
- // - direction: integer relative to the current page number for this pageDiv
- // - position: string, one of "start" or "end", moves to corresponding point
- // in the given component
- // - anchor: an element id within the component
- // - xpath: the node at this XPath within the component
- // - selector: the first node at this CSS selector within the component
- //
- // The locus object can also specify a componentId. If it is not provided
- // we default to the currently active component, and if that doesn't exist,
- // we default to the very first component.
- //
- // The locus result will be an object with the following properties:
- //
- // - load: boolean, true if loading component required, false otherwise
- // - componentId: component to load (current componentId if load is false)
- // - if load is false:
- // - page
- // - if load is true:
- // - one of page / pagesBack / percent / direction / position / anchor
- //
- function pageNumberAt(pageDiv, locus) {
- locus.load = false;
- var currComponent = pageDiv.m.activeFrame ?
- pageDiv.m.activeFrame.m.component :
- null;
- var component = null;
- var cIndex = p.componentIds.indexOf(locus.componentId);
- if (cIndex < 0 && !currComponent) {
- // No specified component, no current component. Load first component.
- locus.load = true;
- locus.componentId = p.componentIds[0];
- return locus;
- } else if (
- cIndex < 0 &&
- locus.componentId &&
- currComponent.properties.id != locus.componentId
- ) {
- // Invalid component, say not found.
- pageDiv.m.reader.dispatchEvent(
- "monocle:notfound",
- { href: locus.componentId }
- );
- return null;
- } else if (cIndex < 0) {
- // No specified (or invalid) component, use current component.
- component = currComponent;
- locus.componentId = pageDiv.m.activeFrame.m.component.properties.id;
- cIndex = p.componentIds.indexOf(locus.componentId);
- } else if (!p.components[cIndex] || p.components[cIndex] != currComponent) {
- // Specified component differs from current component. Load specified.
- locus.load = true;
- return locus;
- } else {
- component = currComponent;
- }
-
- // If we're here, then the locus is based on the current component.
- var result = { load: false, componentId: locus.componentId, page: 1 }
-
- // Get the current last page.
- lastPageNum = component.lastPageNumber();
-
- // Deduce the page number for the given locus.
- if (typeof(locus.page) == "number") {
- result.page = locus.page;
- } else if (typeof(locus.pagesBack) == "number") {
- result.page = lastPageNum + locus.pagesBack;
- } else if (typeof(locus.percent) == "number") {
- var place = new Monocle.Place();
- place.setPlace(component, 1);
- result.page = place.pageAtPercentageThrough(locus.percent);
- } else if (typeof(locus.direction) == "number") {
- if (!pageDiv.m.place) {
- console.warn("Can't move in a direction if pageDiv has no place.");
- }
- result.page = pageDiv.m.place.pageNumber();
- result.page += locus.direction;
- } else if (typeof(locus.anchor) == "string") {
- result.page = component.pageForChapter(locus.anchor, pageDiv);
- } else if (typeof(locus.xpath) == "string") {
- result.page = component.pageForXPath(locus.xpath, pageDiv);
- } else if (typeof(locus.selector) == "string") {
- result.page = component.pageForSelector(locus.selector, pageDiv);
- } else if (typeof(locus.position) == "string") {
- if (locus.position == "start") {
- result.page = 1;
- } else if (locus.position == "end") {
- result.page = lastPageNum['new'];
- }
- } else {
- console.warn("Unrecognised locus: " + locus);
- }
-
- if (result.page < 1) {
- if (cIndex == 0) {
- // On first page of book.
- result.page = 1;
- result.boundarystart = true;
- } else {
- // Moving backwards from current component.
- result.load = true;
- result.componentId = p.componentIds[cIndex - 1];
- result.pagesBack = result.page;
- result.page = null;
- }
- } else if (result.page > lastPageNum) {
- if (cIndex == p.lastCIndex) {
- // On last page of book.
- result.page = lastPageNum;
- result.boundaryend = true;
- } else {
- // Moving forwards from current component.
- result.load = true;
- result.componentId = p.componentIds[cIndex + 1];
- result.page -= lastPageNum;
- }
- }
-
- return result;
- }
-
-
- // Same as pageNumberAt, but if a load is not flagged, this will
- // automatically update the pageDiv's place to the given pageNumber.
- //
- // If you call this (ie, from a flipper), you are effectively entering into
- // a contract to move the frame offset to the given page returned in the
- // locus if load is false.
- //
- function setPageAt(pageDiv, locus) {
- locus = pageNumberAt(pageDiv, locus);
- if (locus && !locus.load) {
- var evtData = { locus: locus, page: pageDiv }
- if (locus.boundarystart) {
- pageDiv.m.reader.dispatchEvent('monocle:boundarystart', evtData);
- } else if (locus.boundaryend) {
- pageDiv.m.reader.dispatchEvent('monocle:boundaryend', evtData);
- } else {
- var component = p.components[p.componentIds.indexOf(locus.componentId)];
- pageDiv.m.place = pageDiv.m.place || new Monocle.Place();
- pageDiv.m.place.setPlace(component, locus.page);
-
- var evtData = {
- page: pageDiv,
- locus: locus,
- pageNumber: pageDiv.m.place.pageNumber(),
- componentId: locus.componentId
- }
- pageDiv.m.reader.dispatchEvent("monocle:pagechange", evtData);
- }
- }
- return locus;
- }
-
-
- // Will load the given component into the pageDiv's frame, then invoke the
- // callback with resulting locus (provided by pageNumberAt).
- //
- // If the resulting page number is outside the bounds of the new component,
- // (ie, pageNumberAt again requests a load), this will recurse into further
- // components until non-loading locus is returned by pageNumberAt. Then the
- // callback will fire with that locus.
- //
- // As with setPageAt, if you call this you're obliged to move the frame
- // offset to the given page in the locus passed to the callback.
- //
- function loadPageAt(pageDiv, locus, onLoad, onFail) {
- var cIndex = p.componentIds.indexOf(locus.componentId);
- if (!locus.load || cIndex < 0) {
- locus = pageNumberAt(pageDiv, locus);
- }
-
- if (!locus) {
- return onFail ? onFail() : null;
- }
-
- if (!locus.load) {
- return onLoad(locus);
- }
-
- var findPageNumber = function () {
- locus = setPageAt(pageDiv, locus);
- if (!locus) {
- return onFail ? onFail() : null;
- } else if (locus.load) {
- loadPageAt(pageDiv, locus, onLoad, onFail)
- } else {
- onLoad(locus);
- }
- }
-
- var applyComponent = function (component) {
- component.applyTo(pageDiv, findPageNumber);
- for (var l = 1; l <= p.preloadWindow; ++l) {
- deferredPreloadComponent(cIndex+l, l*k.PRELOAD_INTERVAL);
- }
- }
-
- loadComponent(cIndex, applyComponent, onFail, pageDiv);
- }
-
-
- // If your flipper doesn't care whether a component needs to be
- // loaded before the page can be set, you can use this shortcut.
- //
- function setOrLoadPageAt(pageDiv, locus, onLoad, onFail) {
- locus = setPageAt(pageDiv, locus);
- if (!locus) {
- if (onFail) { onFail(); }
- } else if (locus.load) {
- loadPageAt(pageDiv, locus, onLoad, onFail);
- } else {
- onLoad(locus);
- }
- }
-
-
- // Fetches the component source from the dataSource.
- //
- // 'index' is the index of the component in the
- // dataSource.getComponents array.
- //
- // 'onLoad' is invoked when the source is received.
- //
- // 'onFail' is optional, and is invoked if the source could not be fetched.
- //
- // 'pageDiv' is optional, and simply allows firing events on
- // the reader object that has requested this component, ONLY if
- // the source has not already been received.
- //
- function loadComponent(index, onLoad, onFail, pageDiv) {
- if (p.components[index]) {
- return onLoad(p.components[index]);
- }
-
- var cmptId = p.components[index];
- var evtData = { 'page': pageDiv, 'component': cmptId, 'index': index };
- pageDiv.m.reader.dispatchEvent('monocle:componentloading', evtData);
-
- var onCmptLoad = function (cmpt) {
- evtData['component'] = cmpt;
- pageDiv.m.reader.dispatchEvent('monocle:componentloaded', evtData);
- onLoad(cmpt);
- }
-
- var onCmptFail = function (cmptId) {
- console.warn("Failed to load component: "+cmptId);
- pageDiv.m.reader.dispatchEvent('monocle:componentfailed', evtData);
- if (onFail) { onFail(); }
- }
-
- _loadComponent(index, onCmptLoad, onCmptFail);
- }
-
-
- function preloadComponent(index) {
- if (p.components[index]) { return; }
- var cmptId = p.componentIds[index];
- if (!cmptId) { return; }
- if (p.cmptLoadQueue[cmptId]) { return; }
- _loadComponent(index);
- }
-
-
- function deferredPreloadComponent(index, delay) {
- Monocle.defer(function () { preloadComponent(index); }, delay);
- }
-
-
- function _loadComponent(index, successCallback, failureCallback) {
- var cmptId = p.componentIds[index];
- var queueItem = { success: successCallback, failure: failureCallback };
- if (p.cmptLoadQueue[cmptId]) {
- return p.cmptLoadQueue[cmptId] = queueItem;
- } else {
- p.cmptLoadQueue[cmptId] = queueItem;
- }
-
- var onCmptFail = function () {
- fireLoadQueue(cmptId, 'failure', cmptId);
- }
-
- var onCmptLoad = function (cmptSource) {
- if (cmptSource === false) { return onCmptFail(); }
- p.components[index] = new Monocle.Component(
- API,
- cmptId,
- index,
- chaptersForComponent(cmptId),
- cmptSource
- );
- fireLoadQueue(cmptId, 'success', p.components[index]);
- }
-
- var cmptSource = p.dataSource.getComponent(cmptId, onCmptLoad);
- if (cmptSource && !p.components[index]) {
- onCmptLoad(cmptSource);
- } else if (cmptSource === false) {
- onCmptFail();
- }
- }
-
-
- function fireLoadQueue(cmptId, cbName, args) {
- if (typeof p.cmptLoadQueue[cmptId][cbName] == 'function') {
- p.cmptLoadQueue[cmptId][cbName](args);
- }
- p.cmptLoadQueue[cmptId] = null;
- }
-
-
- // Returns an array of chapter objects that are found in the given component.
- //
- // A chapter object has this format:
- //
- // {
- // title: "Chapter 1",
- // fragment: null
- // }
- //
- // The fragment property of a chapter object is either null (the chapter
- // starts at the head of the component) or the fragment part of the URL
- // (eg, "foo" in "index.html#foo").
- //
- function chaptersForComponent(cmptId) {
- if (p.chapters[cmptId]) {
- return p.chapters[cmptId];
- }
- p.chapters[cmptId] = [];
- var matcher = new RegExp('^'+decodeURIComponent(cmptId)+"(\#(.+)|$)");
- var matches;
- var recurser = function (chp) {
- if (matches = decodeURIComponent(chp.src).match(matcher)) {
- p.chapters[cmptId].push({
- title: chp.title,
- fragment: matches[2] || null
- });
- }
- if (chp.children) {
- for (var i = 0; i < chp.children.length; ++i) {
- recurser(chp.children[i]);
- }
- }
- }
-
- for (var i = 0; i < p.contents.length; ++i) {
- recurser(p.contents[i]);
- }
- return p.chapters[cmptId];
- }
-
-
- // Returns a locus for the chapter that has the URL given in the
- // 'src' argument.
- //
- // See the comments at pageNumberAt for an explanation of locus objects.
- //
- function locusOfChapter(src) {
- var matcher = new RegExp('^(.+?)(#(.*))?$');
- var matches = src.match(matcher);
- if (!matches) { return null; }
- var cmptId = componentIdMatching(matches[1]);
- if (!cmptId) { return null; }
- var locus = { componentId: cmptId }
- matches[3] ? locus.anchor = matches[3] : locus.position = "start";
- return locus;
- }
-
-
- function isValidLocus(locus) {
- if (!locus) { return false; }
- if (locus.componentId && !componentIdMatching(locus.componentId)) {
- return false;
- }
- return true;
- }
-
-
- function componentIdMatching(str) {
- str = decodeURIComponent(str);
- for (var i = 0, ii = p.componentIds.length; i < ii; ++i) {
- if (decodeURIComponent(p.componentIds[i]) == str) { return str; }
- }
- return null;
- }
-
-
- function componentWeights() {
- if (!p.weights) {
- p.weights = dataSource.getMetaData('componentWeights') || [];
- if (!p.weights.length) {
- var cmptSize = 1.0 / p.componentIds.length;
- for (var i = 0, ii = p.componentIds.length; i < ii; ++i) {
- p.weights.push(cmptSize);
- }
- }
- }
- return p.weights;
- }
-
-
- API.getMetaData = dataSource.getMetaData;
- API.pageNumberAt = pageNumberAt;
- API.setPageAt = setPageAt;
- API.loadPageAt = loadPageAt;
- API.setOrLoadPageAt = setOrLoadPageAt;
- API.chaptersForComponent = chaptersForComponent;
- API.locusOfChapter = locusOfChapter;
- API.isValidLocus = isValidLocus;
- API.componentWeights = componentWeights;
-
- initialize();
-
- return API;
-}
-
-
-// Legacy function. Deprecated.
-//
-Monocle.Book.fromNodes = function (nodes) {
- console.deprecation("Book.fromNodes() will soon be removed.");
- return new Monocle.Book(Monocle.bookDataFromNodes(nodes));
-}
-
-Monocle.Book.PRELOAD_INTERVAL = 1000;
-// PLACE
-
-Monocle.Place = function () {
-
- var API = { constructor: Monocle.Place }
- var k = API.constants = API.constructor;
- var p = API.properties = {
- component: null,
- percent: null
- }
-
-
- function setPlace(cmpt, pageN) {
- p.component = cmpt;
- p.percent = pageN / cmpt.lastPageNumber();
- p.chapter = null;
- }
-
-
- function setPercentageThrough(cmpt, percent) {
- p.component = cmpt;
- p.percent = percent;
- p.chapter = null;
- }
-
-
- function componentId() {
- return p.component.properties.id;
- }
-
-
- // How far we are through the component at the "top of the page".
- //
- // 0 - start of book. 1.0 - end of book.
- //
- function percentAtTopOfPage() {
- if (k.PAGE_ANCHOR == 'bottom') {
- return p.percent - 1.0 / p.component.lastPageNumber();
- } else {
- return p.percent;
- }
- }
-
-
- function percentOnPage() {
- return percentAtTopOfPage() + k.PAGE_ANCHOR_OFFSET / pagesInComponent();
- }
-
-
- // How far we are through the component at the "bottom of the page".
- //
- function percentAtBottomOfPage() {
- if (k.PAGE_ANCHOR == 'bottom') {
- return p.percent;
- } else {
- return p.percent + 1.0 / p.component.lastPageNumber();
- }
- }
-
-
- // The page number at a given point (0: start, 1: end) within the component.
- //
- function pageAtPercentageThrough(percent) {
- var pages = pagesInComponent();
- if (typeof percent != 'number') { percent = 0; }
- return Math.max(Math.round(pages * percent), 1);
- }
-
-
- // The page number of this point within the component.
- //
- function pageNumber() {
- return pageAtPercentageThrough(p.percent);
- }
-
-
- function pagesInComponent() {
- return p.component.lastPageNumber();
- }
-
-
- function chapterInfo() {
- if (p.chapter) {
- return p.chapter;
- }
- return p.chapter = p.component.chapterForPage(pageNumber()+1);
- }
-
-
- function chapterTitle() {
- var chp = chapterInfo();
- return chp ? chp.title : null;
- }
-
-
- function chapterSrc() {
- var src = componentId();
- var cinfo = chapterInfo();
- if (cinfo && cinfo.fragment) {
- src += "#" + cinfo.fragment;
- }
- return src;
- }
-
-
- function getLocus(options) {
- options = options || {};
- var locus = {
- page: pageNumber(),
- componentId: componentId()
- }
- if (options.direction) {
- locus.page += options.direction;
- } else {
- locus.percent = percentAtBottomOfPage();
- }
- return locus;
- }
-
-
- // Returns how far this place is in the entire book (0 - start, 1.0 - end).
- //
- function percentageOfBook() {
- var book = p.component.properties.book;
- var componentIds = book.properties.componentIds;
- var weights = book.componentWeights();
- var cmptIndex = p.component.properties.index;
- var pc = weights[cmptIndex] * p.percent;
- for (var i = 0, ii = cmptIndex; i < ii; ++i) { pc += weights[i]; }
-
- // Note: This is a decent estimation of current page number and total
- // number of pages, but it's very approximate. Could be improved by storing
- // the page counts of all components accessed (since the dimensions of the
- // reader last changed), and averaging the result across them. (You
- // probably want to ignore calcs for components < 2 or 3 pages long, too.
- // The bigger the component, the more accurate the calculation.)
- //
- // var bkPages = p.component.lastPageNumber() / weights[cmptIndex];
- // console.log('Page: '+ Math.floor(pc*bkPages)+ ' of '+ Math.floor(bkPages));
-
- return pc;
- }
-
-
- function onFirstPageOfBook() {
- return p.component.properties.index == 0 && pageNumber() == 1;
- }
-
-
- function onLastPageOfBook() {
- return (
- p.component.properties.index ==
- p.component.properties.book.properties.lastCIndex &&
- pageNumber() == p.component.lastPageNumber()
- );
- }
-
-
- API.setPlace = setPlace;
- API.setPercentageThrough = setPercentageThrough;
- API.componentId = componentId;
- API.percentAtTopOfPage = percentAtTopOfPage;
- API.percentOnPage = percentOnPage;
- API.percentAtBottomOfPage = percentAtBottomOfPage;
- API.pageAtPercentageThrough = pageAtPercentageThrough;
- API.pageNumber = pageNumber;
- API.pagesInComponent = pagesInComponent;
- API.chapterInfo = chapterInfo;
- API.chapterTitle = chapterTitle;
- API.chapterSrc = chapterSrc;
- API.getLocus = getLocus;
- API.percentageOfBook = percentageOfBook;
- API.onFirstPageOfBook = onFirstPageOfBook;
- API.onLastPageOfBook = onLastPageOfBook;
-
- API.percentageThrough = k.PAGE_ANCHOR == 'bottom' ? percentAtBottomOfPage :
- k.PAGE_ANCHOR == 'offset' ? percentOnPage :
- percentAtTopOfPage;
-
- return API;
-}
-
-
-// Can set this to 'top', 'offset' or 'bottom'. Old Monocle behaviour is 'bottom'.
-//
-Monocle.Place.PAGE_ANCHOR = 'offset';
-Monocle.Place.PAGE_ANCHOR_OFFSET = 0.1;
-
-
-Monocle.Place.FromPageNumber = function (component, pageNumber) {
- var place = new Monocle.Place();
- place.setPlace(component, pageNumber);
- return place;
-}
-
-
-Monocle.Place.FromPercentageThrough = function (component, percent) {
- var place = new Monocle.Place();
- place.setPercentageThrough(component, percent);
- return place;
-}
-
-
-// We can't create a place from a percentage of the book, because the
-// component may not have been loaded yet. But we can get a locus.
-//
-Monocle.Place.percentOfBookToLocus = function (reader, percent) {
- var book = reader.getBook();
- var componentIds = book.properties.componentIds;
- var weights = book.componentWeights();
- var cmptIndex = 0, cmptWeight = 0;
- percent = Math.min(percent, 0.99999);
- while (percent >= 0) {
- cmptWeight = weights[cmptIndex];
- percent -= weights[cmptIndex];
- if (percent >= 0) {
- cmptIndex += 1;
- if (cmptIndex >= weights.length) {
- console.error('Unable to calculate locus from percentage: '+percent);
- return;
- }
- }
- }
- var cmptPercent = (percent + cmptWeight) / cmptWeight;
- return { componentId: componentIds[cmptIndex], percent: cmptPercent }
-}
-;
-/* COMPONENT */
-
-// See the properties declaration for details of constructor arguments.
-//
-Monocle.Component = function (book, id, index, chapters, source) {
-
- var API = { constructor: Monocle.Component }
- var k = API.constants = API.constructor;
- var p = API.properties = {
- // a back-reference to the public API of the book that owns this component
- book: book,
-
- // the string that represents this component in the book's component array
- id: id,
-
- // the position in the book's components array of this component
- index: index,
-
- // The chapters argument is an array of objects that list the chapters that
- // can be found in this component. A chapter object is defined as:
- //
- // {
- // title: str,
- // fragment: str, // optional anchor id
- // percent: n // how far into the component the chapter begins
- // }
- //
- // NOTE: the percent property is calculated by the component - you only need
- // to pass in the title and the optional id string.
- //
- chapters: chapters,
-
- // the frame provided by dataSource.getComponent() for this component
- source: source
- }
-
-
- // Makes this component the active component for the pageDiv. There are
- // several strategies for this (see loadFrame).
- //
- // When the component has been loaded into the pageDiv's frame, the callback
- // will be invoked with the pageDiv and this component as arguments.
- //
- function applyTo(pageDiv, callback) {
- prepareSource(pageDiv.m.reader);
-
- var evtData = { 'page': pageDiv, 'source': p.source };
- pageDiv.m.reader.dispatchEvent('monocle:componentchanging', evtData);
-
- var onLoaded = function () {
- setupFrame(
- pageDiv,
- pageDiv.m.activeFrame,
- function () { callback(pageDiv, API) }
- );
- }
-
- Monocle.defer(function () { loadFrame(pageDiv, onLoaded); });
- }
-
-
- // Loads this component into the given frame, using one of the following
- // strategies:
- //
- // * HTML - a HTML string
- // * URL - a URL string
- // * Nodes - an array of DOM body nodes (NB: no way to populate head)
- // * Document - a DOM DocumentElement object
- //
- function loadFrame(pageDiv, callback) {
- var frame = pageDiv.m.activeFrame;
-
- // We own this frame now.
- frame.m.component = API;
-
- // Hide the frame while we're changing it.
- frame.style.visibility = "hidden";
-
- frame.whenDocumentReady = function () {
- var doc = frame.contentDocument;
- var evtData = { 'page': pageDiv, 'document': doc, 'component': API };
- pageDiv.m.reader.dispatchEvent('monocle:componentmodify', evtData);
- frame.whenDocumentReady = null;
- }
-
- if (p.source.html) {
- return loadFrameFromHTML(p.source.html || p.source, frame, callback);
- } else if (p.source.url) {
- return loadFrameFromURL(p.source.url, frame, callback);
- } else if (p.source.doc) {
- return loadFrameFromDocument(p.source.doc, frame, callback);
- }
- }
-
-
- // LOAD STRATEGY: HTML
- // Loads a HTML string into the given frame, invokes the callback once loaded.
- //
- function loadFrameFromHTML(src, frame, callback) {
- var fn = function () {
- Monocle.Events.deafen(frame, 'load', fn);
- frame.whenDocumentReady();
- Monocle.defer(callback);
- }
- Monocle.Events.listen(frame, 'load', fn);
- if (Monocle.Browser.env.loadHTMLWithDocWrite) {
- frame.contentDocument.open('text/html', 'replace');
- frame.contentDocument.write(src);
- frame.contentDocument.close();
- } else {
- frame.contentWindow['monCmptData'] = src;
- frame.src = "javascript:window['monCmptData'];"
- }
- }
-
-
- // LOAD STRATEGY: URL
- // Loads the URL into the given frame, invokes callback once loaded.
- //
- function loadFrameFromURL(url, frame, callback) {
- // If it's a relative path, we need to make it absolute.
- if (!url.match(/^\//)) {
- url = absoluteURL(url);
- }
- var onDocumentReady = function () {
- Monocle.Events.deafen(frame, 'load', onDocumentReady);
- frame.whenDocumentReady();
- }
- var onDocumentLoad = function () {
- Monocle.Events.deafen(frame, 'load', onDocumentLoad);
- Monocle.defer(callback);
- }
- Monocle.Events.listen(frame, 'load', onDocumentReady);
- Monocle.Events.listen(frame, 'load', onDocumentLoad);
- frame.contentWindow.location.replace(url);
- }
-
-
- // LOAD STRATEGY: DOCUMENT
- // Replaces the DocumentElement of the given frame with the given srcDoc.
- // Invokes the callback when loaded.
- //
- function loadFrameFromDocument(srcDoc, frame, callback) {
- var doc = frame.contentDocument;
-
- // WebKit has an interesting quirk. The ) will be sorted instead
-* of the itself.
-*/
-jQuery.fn.sortElements = (function(){
-
- var sort = [].sort;
-
- return function(comparator, getSortable) {
-
- getSortable = getSortable || function(){return this;};
-
- var placements = this.map(function(){
-
- var sortElement = getSortable.call(this),
- parentNode = sortElement.parentNode,
-
- // Since the element itself will change position, we have
- // to have some way of storing it's original position in
- // the DOM. The easiest way is to have a 'flag' node:
- nextSibling = parentNode.insertBefore(
- document.createTextNode(''),
- sortElement.nextSibling
- );
-
- return function() {
-
- if (parentNode === this) {
- throw new Error(
- "You can't sort elements if any one is a descendant of another."
- );
- }
-
- // Insert before flag:
- parentNode.insertBefore(this, nextSibling);
- // Remove flag:
- parentNode.removeChild(nextSibling);
-
- };
-
- });
-
- return sort.call(this, comparator).each(function(i){
- placements[i].call(getSortable.call(this));
- });
-
- };
-
-})();
\ No newline at end of file
diff --git a/COPS/cops-3.1.3/restapi.php b/COPS/cops-3.1.3/restapi.php
deleted file mode 100644
index 4fb73874..00000000
--- a/COPS/cops-3.1.3/restapi.php
+++ /dev/null
@@ -1,13 +0,0 @@
-
- * @author mikespub
- * @deprecated 3.1.0 use index.php/restapi instead
- */
-
-$link = str_replace('restapi.php', 'index.php/restapi', $_SERVER['REQUEST_URI'] ?? '');
-header('Location: ' . $link);
diff --git a/COPS/cops-3.1.3/router.php b/COPS/cops-3.1.3/router.php
deleted file mode 100644
index 5bd97fa0..00000000
--- a/COPS/cops-3.1.3/router.php
+++ /dev/null
@@ -1,34 +0,0 @@
-
- * @author mikespub
- */
-
-if (php_sapi_name() !== 'cli-server') {
- echo 'This router is for the php development server only';
- return;
-}
-
-// check if the requested path actually exists
-$path = parse_url((string) $_SERVER['REQUEST_URI'], PHP_URL_PATH);
-if (!empty($path) && file_exists(__DIR__ . $path) && !is_dir(__DIR__ . $path)) {
- return false;
-}
-// route to the right PHP endpoint if needed
-$script = urldecode((string) $_SERVER['SCRIPT_NAME']);
-if (str_contains($path, $script . '/') && file_exists(__DIR__ . $script)) {
- return false;
-}
-
-// set environment vars for the front controller
-$_SERVER['SCRIPT_NAME'] = '/index.php';
-$_SERVER['PATH_INFO'] ??= parse_url((string) $_SERVER['REQUEST_URI'], PHP_URL_PATH);
-
-// use index.php as front controller
-include __DIR__ . '/index.php';
diff --git a/COPS/cops-3.1.3/schema.graphql b/COPS/cops-3.1.3/schema.graphql
deleted file mode 100644
index a8be025d..00000000
--- a/COPS/cops-3.1.3/schema.graphql
+++ /dev/null
@@ -1,269 +0,0 @@
-"""
-Adapted from https://github.com/mikespub-org/acdibble-tuql
-Goal: create GraphQL interface to Calibre database (maybe)
-"""
-type Query {
- authors(
- limit: Int
- order: String
-
- """
- A JSON object conforming the the shape specified in http://docs.sequelizejs.com/en/latest/docs/querying/
- """
- where: SequelizeJSON
- offset: Int
- ): [Entry]
- author(
- id: ID
-
- """
- A JSON object conforming the the shape specified in http://docs.sequelizejs.com/en/latest/docs/querying/
- """
- where: SequelizeJSON
- ): Entry
- books(
- limit: Int
- order: String
-
- """
- A JSON object conforming the the shape specified in http://docs.sequelizejs.com/en/latest/docs/querying/
- """
- where: SequelizeJSON
- offset: Int
- ): [EntryBook]
- book(
- id: ID
-
- """
- A JSON object conforming the the shape specified in http://docs.sequelizejs.com/en/latest/docs/querying/
- """
- where: SequelizeJSON
- ): EntryBook
- customColumns(
- limit: Int
- order: String
-
- """
- A JSON object conforming the the shape specified in http://docs.sequelizejs.com/en/latest/docs/querying/
- """
- where: SequelizeJSON
- offset: Int
- ): [Entry]
- customColumn(
- id: ID
-
- """
- A JSON object conforming the the shape specified in http://docs.sequelizejs.com/en/latest/docs/querying/
- """
- where: SequelizeJSON
- ): Entry
- datas(
- bookId: ID
-
- """
- A JSON object conforming the the shape specified in http://docs.sequelizejs.com/en/latest/docs/querying/
- """
- where: SequelizeJSON
- ): [Data]
- data(
- id: ID
-
- """
- A JSON object conforming the the shape specified in http://docs.sequelizejs.com/en/latest/docs/querying/
- """
- where: SequelizeJSON
- ): Data
- feeds(
- limit: Int
- order: String
-
- """
- A JSON object conforming the the shape specified in http://docs.sequelizejs.com/en/latest/docs/querying/
- """
- where: SequelizeJSON
- offset: Int
- ): [Entry]
- feed(
- id: ID
-
- """
- A JSON object conforming the the shape specified in http://docs.sequelizejs.com/en/latest/docs/querying/
- """
- where: SequelizeJSON
- ): Entry
- identifiers(
- limit: Int
- order: String
-
- """
- A JSON object conforming the the shape specified in http://docs.sequelizejs.com/en/latest/docs/querying/
- """
- where: SequelizeJSON
- offset: Int
- ): [Entry]
- identifier(
- id: ID
-
- """
- A JSON object conforming the the shape specified in http://docs.sequelizejs.com/en/latest/docs/querying/
- """
- where: SequelizeJSON
- ): Entry
- languages(
- limit: Int
- order: String
-
- """
- A JSON object conforming the the shape specified in http://docs.sequelizejs.com/en/latest/docs/querying/
- """
- where: SequelizeJSON
- offset: Int
- ): [Entry]
- language(
- id: ID
-
- """
- A JSON object conforming the the shape specified in http://docs.sequelizejs.com/en/latest/docs/querying/
- """
- where: SequelizeJSON
- ): Entry
- preferences(
- limit: Int
- order: String
-
- """
- A JSON object conforming the the shape specified in http://docs.sequelizejs.com/en/latest/docs/querying/
- """
- where: SequelizeJSON
- offset: Int
- ): [Entry]
- preference(
- id: ID
-
- """
- A JSON object conforming the the shape specified in http://docs.sequelizejs.com/en/latest/docs/querying/
- """
- where: SequelizeJSON
- ): Entry
- publishers(
- limit: Int
- order: String
-
- """
- A JSON object conforming the the shape specified in http://docs.sequelizejs.com/en/latest/docs/querying/
- """
- where: SequelizeJSON
- offset: Int
- ): [Entry]
- publisher(
- id: ID
-
- """
- A JSON object conforming the the shape specified in http://docs.sequelizejs.com/en/latest/docs/querying/
- """
- where: SequelizeJSON
- ): Entry
- ratings(
- limit: Int
- order: String
-
- """
- A JSON object conforming the the shape specified in http://docs.sequelizejs.com/en/latest/docs/querying/
- """
- where: SequelizeJSON
- offset: Int
- ): [Entry]
- rating(
- id: ID
-
- """
- A JSON object conforming the the shape specified in http://docs.sequelizejs.com/en/latest/docs/querying/
- """
- where: SequelizeJSON
- ): Entry
- series(
- limit: Int
- order: String
-
- """
- A JSON object conforming the the shape specified in http://docs.sequelizejs.com/en/latest/docs/querying/
- """
- where: SequelizeJSON
- offset: Int
- ): [Entry]
- serie(
- id: ID
-
- """
- A JSON object conforming the the shape specified in http://docs.sequelizejs.com/en/latest/docs/querying/
- """
- where: SequelizeJSON
- ): Entry
- tags(
- limit: Int
- order: String
-
- """
- A JSON object conforming the the shape specified in http://docs.sequelizejs.com/en/latest/docs/querying/
- """
- where: SequelizeJSON
- offset: Int
- ): [Entry]
- tag(
- id: ID
-
- """
- A JSON object conforming the the shape specified in http://docs.sequelizejs.com/en/latest/docs/querying/
- """
- where: SequelizeJSON
- ): Entry
-}
-
-"""The `JSON` scalar type represents raw JSON as values."""
-scalar SequelizeJSON
-
-type Entry {
- id: ID!
- title: String!
- content: String
- contentType: String
- linkArray: [Link]
- className: String
- numberOfElement: String
- books: [EntryBook]
-}
-
-type EntryBook {
- id: ID!
- title: String!
- content: String
- contentType: String
- linkArray: [Link]
- className: String
- numberOfElement: String
- path: String
- authors: [Entry]
- customColumns: [Entry]
- datas: [Data]
- identifiers: [Entry]
- languages: String
- publisher: Entry
- rating: String
- serie: Entry
- tags: [Entry]
-}
-
-type Link {
- href: String!
- type: String!
- rel: String
- title: String
-}
-
-type Data {
- id: ID!
- book: EntryBook
- format: String
- uncompressedSize: Int
- name: String
-}
diff --git a/COPS/cops-3.1.3/sendtomail.php b/COPS/cops-3.1.3/sendtomail.php
deleted file mode 100644
index bf4b2b0c..00000000
--- a/COPS/cops-3.1.3/sendtomail.php
+++ /dev/null
@@ -1,13 +0,0 @@
-
- * @author mikespub
- * @deprecated 3.1.0 use index.php/mail instead
- */
-
-$link = str_replace('sendtomail.php', 'index.php/mail', $_SERVER['REQUEST_URI'] ?? '');
-header('Location: ' . $link);
diff --git a/COPS/cops-3.1.3/src/Calibre/Annotation.php b/COPS/cops-3.1.3/src/Calibre/Annotation.php
deleted file mode 100644
index 8316a865..00000000
--- a/COPS/cops-3.1.3/src/Calibre/Annotation.php
+++ /dev/null
@@ -1,211 +0,0 @@
-
- */
-
-namespace SebLucas\Cops\Calibre;
-
-use SebLucas\Cops\Input\Config;
-use SebLucas\Cops\Input\Route;
-use SebLucas\Cops\Pages\PageId;
-use JsonException;
-
-class Annotation extends Base
-{
- public const PAGE_ID = PageId::ALL_ANNOTATIONS_ID;
- public const PAGE_ALL = PageId::ALL_ANNOTATIONS;
- public const PAGE_BOOK = PageId::ANNOTATIONS_BOOK;
- public const PAGE_DETAIL = PageId::ANNOTATION_DETAIL;
- public const SQL_TABLE = "annotations";
- public const SQL_LINK_TABLE = "annotations";
- public const SQL_LINK_COLUMN = "id";
- public const SQL_SORT = "id";
- public const SQL_COLUMNS = "id, book, format, user_type, user, timestamp, annot_id, annot_type, annot_data";
- public const SQL_ALL_ROWS = "select {0} from annotations where 1=1 {1}";
-
- public int $book;
- public string $format;
- public string $userType;
- public string $user;
- public float $timestamp;
- public string $type;
- /** @var array */
- public array $data;
-
- /**
- * Summary of __construct
- * @param object $post
- * @param ?int $database
- */
- public function __construct($post, $database = null)
- {
- $this->id = $post->id;
- $this->book = $post->book;
- $this->format = $post->format;
- $this->userType = $post->user_type;
- $this->user = $post->user;
- $this->timestamp = $post->timestamp;
- $this->name = $post->annot_id;
- $this->type = $post->annot_type;
- try {
- $this->data = json_decode($post->annot_data, true, 512, JSON_THROW_ON_ERROR);
- } catch (JsonException) {
- $this->data = [ $post->annot_data ];
- }
- $this->databaseId = $database;
- }
-
- /**
- * Summary of getUri
- * @param array $params
- * @return string
- */
- public function getUri($params = [])
- {
- // @todo let restapi build route url, or change route to use page here?
- if (Config::get('use_route_urls')) {
- return Route::link($this->handler) . '/annotations/' . $this->book . '/' . $this->id;
- }
- $params['bookId'] = $this->book;
- $params['id'] = $this->id;
- // we need databaseId here because we use Route::link with $handler
- $params['db'] = $this->getDatabaseId();
- return Route::link($this->handler, static::PAGE_DETAIL, $params);
- }
-
- /**
- * Summary of getTitle
- * @return string
- */
- public function getTitle()
- {
- return '(' . strval($this->book) . ') ' . ucfirst($this->type) . ' ' . $this->name;
- }
-
- /** Use inherited class methods to query static SQL_TABLE for this class */
-
- /**
- * Summary of getCountByBookId
- * @param ?int $database
- * @return array
- */
- public static function getCountByBookId($database = null)
- {
- $entries = [];
- $query = 'select book, count(*) as count from annotations group by book order by book';
- $result = Database::query($query, [], $database);
- while ($post = $result->fetchObject()) {
- $entries[$post->book] = $post->count;
- }
- return $entries;
- }
-
- /**
- * Summary of getInstancesByBookId
- * @param int $bookId
- * @param ?int $database
- * @return array
- */
- public static function getInstancesByBookId($bookId, $database = null)
- {
- // @todo filter by format, user, annotType etc.
- $query = 'select ' . static::getInstanceColumns($database) . '
-from annotations
-where book = ?';
- $result = Database::query($query, [$bookId], $database);
- $annotationArray = [];
- while ($post = $result->fetchObject()) {
- array_push($annotationArray, new Annotation($post, $database));
- }
- return $annotationArray;
- }
-
- /**
- * Forget about converting annotations
- *
- * Epub CFIs are *not* compatible between Calibre, Kobo, epub.js, readium.js etc.
- * See https://github.com/futurepress/epub.js/issues/1358 for comments and links
- *
- * @return void
- */
- private static function forgetAboutConvertAnnotations()
- {
- /**
- From Calibre annotations:
- {
- "id": 2,
- "book": 17,
- "format": "EPUB",
- "userType": "local",
- "user": "viewer",
- "timestamp": 1710158035.583,
- "type": "highlight",
- "data": {
- "end_cfi": "/2/4/2/2/6/1:24",
- "highlighted_text": "Charles Lutwidge Dodgson",
- "notes": "Full author name",
- "spine_index": 2,
- "spine_name": "OPS/about.xml",
- "start_cfi": "/2/4/2/2/6/1:0",
- "style": {
- "kind": "color",
- "type": "builtin",
- "which": "yellow"
- },
- "timestamp": "2024-03-11T11:53:55.583Z",
- "toc_family_titles": [
- "About"
- ],
- "type": "highlight",
- "uuid": "5HHGuoCOtpA-umaIbBuc0Q"
- }
- }
- From epub.js local storage in Chrome:
- {
- "restore": true,
- "bookPath": "/cops/zipfs.php/0/20/",
- "flow": "paginated",
- "history": true,
- "reload": false,
- "bookmarks": [
- "epubcfi(/6/8!/4/2/2[chapter_458]/2/2/2/1:0)",
- "epubcfi(/6/10!/4/2/2[chapter_460]/4/26/1:372)"
- ],
- "annotations": [
- {
- "cfi": "epubcfi(/6/6!/4/2/2/2,/1:0,/1:13)",
- "date": "2024-03-13T10:11:48.731Z",
- "text": "About author",
- "uuid": "60c92408-81b9-47d9-f705-66f26317f2ee"
- },
- {
- "cfi": "epubcfi(/6/6!/4/2/2/6,/1:0,/1:24)",
- "date": "2024-03-13T10:19:09.443Z",
- "text": "Actual person",
- "uuid": "71aad015-28f9-4088-abce-48762492b52e"
- },
- {
- "cfi": "epubcfi(/6/6!/4/2/2/6,/1:93,/1:106)",
- "date": "2024-03-13T10:19:31.777Z",
- "text": "Pen name",
- "uuid": "66f559ae-f8a5-4b39-b3cc-673a08d67f00"
- }
- ],
- "sectionId": "level1-about",
- "spread": {
- "mod": "auto",
- "min": 800
- },
- "styles": {
- "fontSize": 100
- },
- "pagination": false,
- "language": "en",
- "previousLocationCfi": "epubcfi(/6/6!/4/2/2/2/1:0)"
- }
- */
- }
-}
diff --git a/COPS/cops-3.1.3/src/Calibre/Author.php b/COPS/cops-3.1.3/src/Calibre/Author.php
deleted file mode 100644
index 68cf4b9f..00000000
--- a/COPS/cops-3.1.3/src/Calibre/Author.php
+++ /dev/null
@@ -1,88 +0,0 @@
-
- */
-
-namespace SebLucas\Cops\Calibre;
-
-use SebLucas\Cops\Pages\PageId;
-
-class Author extends Base
-{
- public const PAGE_ID = PageId::ALL_AUTHORS_ID;
- public const PAGE_ALL = PageId::ALL_AUTHORS;
- public const PAGE_LETTER = PageId::AUTHORS_FIRST_LETTER;
- public const PAGE_DETAIL = PageId::AUTHOR_DETAIL;
- public const SQL_TABLE = "authors";
- public const SQL_LINK_TABLE = "books_authors_link";
- public const SQL_LINK_COLUMN = "author";
- public const SQL_SORT = "sort";
- public const SQL_COLUMNS = "authors.id as id, authors.name as name, authors.sort as sort, authors.link as link";
- public const SQL_ROWS_BY_FIRST_LETTER = "select {0} from authors, books_authors_link where author = authors.id and upper (authors.sort) like ? {1} group by authors.id, authors.name, authors.sort order by sort";
- public const SQL_ROWS_FOR_SEARCH = "select {0} from authors, books_authors_link where author = authors.id and (upper (authors.sort) like ? or upper (authors.name) like ?) {1} group by authors.id, authors.name, authors.sort order by sort";
- public const SQL_ALL_ROWS = "select {0} from authors, books_authors_link where author = authors.id {1} group by authors.id, authors.name, authors.sort order by sort";
- public const SQL_BOOKLIST = 'select {0} from books_authors_link, books ' . Book::SQL_BOOKS_LEFT_JOIN . '
- left outer join books_series_link on books_series_link.book = books.id
- where books_authors_link.book = books.id and author = ? {1} order by series desc, series_index asc, pubdate asc';
- public const URL_PARAM = "a";
-
- /** @var string */
- public $sort;
-
- /**
- * Summary of __construct
- * @param object $post
- * @param ?int $database
- */
- public function __construct($post, $database = null)
- {
- $this->id = $post->id;
- $this->name = str_replace("|", ",", $post->name);
- $this->sort = $post->sort;
- $this->link = property_exists($post, 'link') ? $post->link : null;
- $this->databaseId = $database;
- }
-
- /**
- * Summary of getTitle
- * @return string
- */
- public function getTitle()
- {
- return $this->name;
- }
-
- /**
- * Summary of getParentTitle
- * @return string
- */
- public function getParentTitle()
- {
- return localize("authors.title");
- }
-
- /** Use inherited class methods to query static SQL_TABLE for this class */
-
- /**
- * Summary of getInstancesByBookId
- * @param int $bookId
- * @param ?int $database
- * @return array
- */
- public static function getInstancesByBookId($bookId, $database = null)
- {
- $query = 'select ' . static::getInstanceColumns($database) . '
-from authors, books_authors_link
-where author = authors.id
-and book = ? order by books_authors_link.id';
- $result = Database::query($query, [$bookId], $database);
- $authorArray = [];
- while ($post = $result->fetchObject()) {
- array_push($authorArray, new Author($post, $database));
- }
- return $authorArray;
- }
-}
diff --git a/COPS/cops-3.1.3/src/Calibre/Base.php b/COPS/cops-3.1.3/src/Calibre/Base.php
deleted file mode 100644
index 38ad0078..00000000
--- a/COPS/cops-3.1.3/src/Calibre/Base.php
+++ /dev/null
@@ -1,576 +0,0 @@
-
- */
-
-namespace SebLucas\Cops\Calibre;
-
-use SebLucas\Cops\Input\Config;
-use SebLucas\Cops\Input\Request;
-use SebLucas\Cops\Input\Route;
-use SebLucas\Cops\Model\Entry;
-use SebLucas\Cops\Model\EntryBook;
-use SebLucas\Cops\Model\LinkFeed;
-use SebLucas\Cops\Model\LinkNavigation;
-use SebLucas\Cops\Pages\PageId;
-use SebLucas\Cops\Pages\Page;
-
-abstract class Base
-{
- public const PAGE_ID = PageId::ALL_BASES_ID;
- public const PAGE_ALL = 0;
- public const PAGE_DETAIL = 0;
- public const PAGE_LETTER = 0;
- public const SQL_TABLE = "bases";
- public const SQL_LINK_TABLE = "books_bases_link";
- public const SQL_LINK_COLUMN = "base";
- public const SQL_SORT = "sort";
- public const SQL_COLUMNS = "bases.id as id, bases.name as name, bases.sort as sort, bases.link as link";
- public const SQL_ALL_ROWS = "select {0} from bases, books_bases_link where base = bases.id {1} group by bases.id, bases.name, bases.sort order by sort";
- public const SQL_ROWS_FOR_SEARCH = "select {0} from bases, books_bases_link where base = bases.id and (upper (bases.sort) like ? or upper (bases.name) like ?) {1} group by bases.id, bases.name, bases.sort order by sort";
- public const SQL_ROWS_BY_FIRST_LETTER = "select {0} from bases, books_bases_link where base = bases.id and upper (bases.sort) like ? {1} group by bases.id, bases.name, bases.sort order by sort";
- public const SQL_BOOKLIST = 'select {0} from books_bases_link, books ' . Book::SQL_BOOKS_LEFT_JOIN . '
- where books_bases_link.book = books.id and base = ? {1} order by books.sort';
- public const COMPATIBILITY_XML_ALDIKO = "aldiko";
- public const URL_PARAM = "b";
-
- /** @var ?int */
- public $id;
- /** @var ?string */
- public $name;
- /** @var ?string */
- public $link;
- public bool $limitSelf = true;
- /** @var ?int */
- protected $databaseId = null;
- /** @var ?int */
- protected $filterLimit = null;
- /** @var array */
- protected $filterParams = [];
- protected string $handler = '';
-
- /**
- * Summary of __construct
- * @param object $post
- * @param ?int $database
- */
- public function __construct($post, $database = null)
- {
- $this->id = $post->id;
- $this->name = $post->name;
- $this->link = property_exists($post, 'link') ? $post->link : null;
- $this->databaseId = $database;
- }
-
- /**
- * Summary of getDatabaseId
- * @return ?int
- */
- public function getDatabaseId()
- {
- return $this->databaseId;
- }
-
- /**
- * Summary of getUri
- * @param array $params
- * @return string
- */
- public function getUri($params = [])
- {
- $params['id'] = $this->id;
- // we need databaseId here because we use Route::link with $handler
- $params['db'] = $this->getDatabaseId();
- if (Config::get('use_route_urls')) {
- $params['title'] = $this->getTitle();
- }
- return Route::link($this->handler, static::PAGE_DETAIL, $params);
- }
-
- /**
- * Summary of getParentUri
- * @param array $params
- * @return string
- */
- public function getParentUri($params = [])
- {
- // we need databaseId here because we use Route::link with $handler
- $params['db'] = $this->getDatabaseId();
- return Route::link($this->handler, static::PAGE_ALL, $params);
- }
-
- /**
- * Summary of getEntryId
- * @return string
- */
- public function getEntryId()
- {
- return static::PAGE_ID . ":" . $this->id;
- }
-
- /**
- * Summary of getEntryIdByLetter
- * @param string $startingLetter
- * @return string
- */
- public static function getEntryIdByLetter($startingLetter)
- {
- return static::PAGE_ID . ":letter:" . $startingLetter;
- }
-
- /**
- * Summary of getTitle
- * @return string
- */
- public function getTitle()
- {
- return $this->name;
- }
-
- /**
- * Summary of getContent
- * @param int $count
- * @return string
- */
- public function getContent($count = 0)
- {
- return str_format(localize("bookword", $count), $count);
- }
-
- /**
- * Summary of getContentType
- * @return string
- */
- public function getContentType()
- {
- return "text";
- }
-
- /**
- * Summary of getLinkArray
- * @param array $params
- * @return array
- */
- public function getLinkArray($params = [])
- {
- // remove for Filter::getEntryArray() - see filterTest
- unset($params[static::URL_PARAM]);
- return [ new LinkFeed($this->getUri($params), "subsection") ];
- }
-
- /**
- * Summary of setHandler
- * @param string $handler
- * @return void
- */
- public function setHandler($handler)
- {
- $this->handler = $handler;
- }
-
- /**
- * Summary of getHandler
- * @return string
- */
- public function getHandler()
- {
- return $this->handler;
- }
-
- /**
- * Summary of getClassName
- * @param ?string $className
- * @return string
- */
- public function getClassName($className = null)
- {
- $className ??= static::class;
- $classParts = explode('\\', $className);
- return end($classParts);
- }
-
- /**
- * Summary of getEntry
- * @param int $count
- * @param array $params
- * @return Entry
- */
- public function getEntry($count = 0, $params = [])
- {
- $entry = new Entry(
- $this->getTitle(),
- $this->getEntryId(),
- $this->getContent($count),
- $this->getContentType(),
- $this->getLinkArray($params),
- $this->getDatabaseId(),
- $this->getClassName(),
- $count
- );
- $entry->instance = $this;
- return $entry;
- }
-
- /**
- * Summary of getParentTitle
- * @return string
- */
- public function getParentTitle()
- {
- return localize("title.title");
- }
-
- /**
- * Summary of getPage
- * @param int $count
- * @param array $params
- * @todo investigate potential use as alternative to getEntry()
- * @return Page
- */
- public function getPage($count = 0, $params = [])
- {
- $params['id'] = $this->id;
- // we need databaseId here because we use Route::link with $handler
- $params['db'] = $this->getDatabaseId();
- if (Config::get('use_route_urls')) {
- $params['title'] = $this->getTitle();
- }
- $request = Request::build($params, $this->handler);
- $page = PageId::getPage(static::PAGE_DETAIL, $request, $this);
- if (!empty($count)) {
- $page->totalNumber = $count;
- }
- return $page;
- }
-
- /** Use inherited class methods to get entries from by instance (linked via books) */
-
- /**
- * Get the query to find all books with this value
- * the returning array has two values:
- * - first the query (string)
- * - second an array of all PreparedStatement parameters
- * @return array{0: string, 1: array}
- */
- public function getQuery()
- {
- return [ static::SQL_BOOKLIST, [ $this->id ]];
- }
-
- /**
- * Summary of getLinkTable
- * @return string
- */
- public function getLinkTable()
- {
- return static::SQL_LINK_TABLE;
- }
-
- /**
- * Summary of getLinkColumn
- * @return string
- */
- public function getLinkColumn()
- {
- return static::SQL_LINK_COLUMN;
- }
-
- /**
- * Summary of getBooks
- * @param int $n
- * @param ?string $sort
- * @return array
- */
- public function getBooks($n = 1, $sort = null)
- {
- // @todo see if we want to do something special for books, and deal with static:: inheritance
- //return $this->getEntriesByInstance(Book::class, $n, $sort, $this->databaseId);
- $booklist = new BookList(null, $this->databaseId);
- $booklist->orderBy = $sort;
- [$entryArray, ] = $booklist->getBooksByInstance($this, $n);
- return $entryArray;
- }
-
- /**
- * Summary of getEntriesByInstance
- * @param class-string $className
- * @param int $n
- * @param ?string $sort
- * @param ?int $database
- * @param ?int $numberPerPage
- * @return array
- */
- public function getEntriesByInstance($className, $n = 1, $sort = null, $database = null, $numberPerPage = null)
- {
- $database ??= $this->databaseId;
- $numberPerPage ??= $this->filterLimit;
- // @todo get rid of extraParams in JsonRenderer and OpdsRenderer as filters should be included in navlink now
- $params = $this->getExtraParams();
- $request = Request::build($params, $this->handler);
- $baselist = new BaseList($className, $request, $database, $numberPerPage);
- $baselist->orderBy = $sort;
- return $baselist->getEntriesByInstance($this, $n, $this->filterParams);
- }
-
- /**
- * Summary of getAuthors
- * @param int $n
- * @param ?string $sort
- * @return array
- */
- public function getAuthors($n = 1, $sort = null)
- {
- return $this->getEntriesByInstance(Author::class, $n, $sort);
- }
-
- /**
- * Summary of getLanguages
- * @param int $n
- * @param ?string $sort
- * @return array
- */
- public function getLanguages($n = 1, $sort = null)
- {
- return $this->getEntriesByInstance(Language::class, $n, $sort);
- }
-
- /**
- * Summary of getPublishers
- * @param int $n
- * @param ?string $sort
- * @return array
- */
- public function getPublishers($n = 1, $sort = null)
- {
- return $this->getEntriesByInstance(Publisher::class, $n, $sort);
- }
-
- /**
- * Summary of getRatings
- * @param int $n
- * @param ?string $sort
- * @return array
- */
- public function getRatings($n = 1, $sort = null)
- {
- return $this->getEntriesByInstance(Rating::class, $n, $sort);
- }
-
- /**
- * Summary of getSeries
- * @param int $n
- * @param ?string $sort
- * @return array
- */
- public function getSeries($n = 1, $sort = null)
- {
- return $this->getEntriesByInstance(Serie::class, $n, $sort);
- }
-
- /**
- * Summary of getTags
- * @param int $n
- * @param ?string $sort
- * @return array
- */
- public function getTags($n = 1, $sort = null)
- {
- return $this->getEntriesByInstance(Tag::class, $n, $sort);
- }
-
- /**
- * Summary of getIdentifiers
- * @param int $n
- * @param ?string $sort
- * @return array
- */
- public function getIdentifiers($n = 1, $sort = null)
- {
- return $this->getEntriesByInstance(Identifier::class, $n, $sort);
- }
-
- /**
- * Summary of getCustomValues
- * @param CustomColumnType $customType
- * @return array
- */
- public function getCustomValues($customType)
- {
- // we'd need to apply getEntriesById from $instance on $customType instance here - too messy
- return [];
- }
-
- /**
- * Summary of setFilterLimit
- * @param ?int $filterLimit
- * @return void
- */
- public function setFilterLimit($filterLimit)
- {
- $this->filterLimit = $filterLimit;
- }
-
- /**
- * Summary of getFilterLimit
- * @return ?int
- */
- public function getFilterLimit()
- {
- if (empty($this->filterLimit) || $this->filterLimit < 1) {
- return 999999;
- }
- return $this->filterLimit;
- }
-
- /**
- * Summary of setFilterParams if we want to filter by virtual library etc.
- * @see Page::getFilters()
- * @param array $filterParams
- * @return void
- */
- public function setFilterParams($filterParams)
- {
- $this->filterParams = $filterParams;
- }
-
- /**
- * Summary of getFilterParams
- * @return array
- */
- public function getFilterParams()
- {
- return $this->filterParams;
- }
-
- /**
- * Summary of getExtraParams if we want to add extra params to entry links etc.
- * @return array
- */
- public function getExtraParams()
- {
- return array_merge([static::URL_PARAM => $this->id], $this->filterParams);
- }
-
- /**
- * Summary of getNote
- * @return Note|null
- */
- public function getNote()
- {
- $className = static::class;
- $tableName = $className::SQL_TABLE;
- return Note::getInstanceByTypeId($tableName, $this->id, $this->databaseId);
- }
-
- /** Generic methods inherited by Author, Language, Publisher, Rating, Series, Tag classes */
-
- /**
- * Summary of getInstanceById
- * @param string|int|null $id
- * @param ?int $database
- * @return object
- */
- public static function getInstanceById($id, $database = null)
- {
- $className = static::class;
- if (isset($id)) {
- $query = 'select ' . static::getInstanceColumns($database) . ' from ' . $className::SQL_TABLE . ' where id = ?';
- $result = Database::query($query, [$id], $database);
- if ($post = $result->fetchObject()) {
- return new $className($post, $database);
- }
- }
- $default = static::getDefaultName();
- // use id = 0 to support route urls
- return new $className((object) ['id' => 0, 'name' => $default, 'sort' => $default], $database);
- }
-
- /**
- * Summary of getInstanceByName
- * @param string $name
- * @param ?int $database
- * @return object|null
- */
- public static function getInstanceByName($name, $database = null)
- {
- $className = static::class;
- $query = 'select ' . static::getInstanceColumns($database) . ' from ' . $className::SQL_TABLE . ' where name = ?';
- $result = Database::query($query, [$name], $database);
- if ($post = $result->fetchObject()) {
- return new $className($post, $database);
- }
- return null;
- }
-
- /**
- * Summary of getInstanceColumns
- * @param ?int $database
- * @return string
- */
- public static function getInstanceColumns($database = null)
- {
- $className = static::class;
- // add link field for database user_version 26 = Calibre version 6.15.0 and later (Apr 7, 2023)
- if (in_array($className::SQL_TABLE, ['languages', 'publishers', 'ratings', 'series', 'tags']) && Database::getUserVersion($database) > 25) {
- return $className::SQL_COLUMNS . ', ' . $className::SQL_TABLE . '.link as link';
- }
- return $className::SQL_COLUMNS;
- }
-
- /**
- * Summary of getDefaultName
- * @return ?string
- */
- public static function getDefaultName()
- {
- return null;
- }
-
- /**
- * Summary of getCount
- * @param ?int $database
- * @param ?string $handler
- * @return ?Entry
- */
- public static function getCount($database = null, $handler = null)
- {
- $count = Database::querySingle('select count(*) from ' . static::SQL_TABLE, $database);
- return static::getCountEntry($count, $database, null, $handler);
- }
-
- /**
- * Summary of getCountEntry
- * @param int $count
- * @param ?int $database
- * @param ?string $numberOfString
- * @param ?string $handler
- * @param array $params
- * @return ?Entry
- */
- public static function getCountEntry($count, $database = null, $numberOfString = null, $handler = null, $params = [])
- {
- if ($count == 0) {
- return null;
- }
- if (!$numberOfString) {
- $numberOfString = static::SQL_TABLE . ".alphabetical";
- }
- $params["db"] ??= $database;
- $href = Route::link($handler, static::PAGE_ALL, $params);
- $entry = new Entry(
- localize(static::SQL_TABLE . ".title"),
- static::PAGE_ID,
- str_format(localize($numberOfString, $count), $count),
- "text",
- // issue #26 for koreader: section is not supported
- [ new LinkNavigation($href, "subsection") ],
- $database,
- "",
- $count
- );
- return $entry;
- }
-}
diff --git a/COPS/cops-3.1.3/src/Calibre/BaseList.php b/COPS/cops-3.1.3/src/Calibre/BaseList.php
deleted file mode 100644
index ecce38dd..00000000
--- a/COPS/cops-3.1.3/src/Calibre/BaseList.php
+++ /dev/null
@@ -1,635 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops\Calibre;
-
-use SebLucas\Cops\Input\Config;
-use SebLucas\Cops\Input\Route;
-use SebLucas\Cops\Input\Request;
-use SebLucas\Cops\Model\Entry;
-use SebLucas\Cops\Model\LinkFeed;
-use SebLucas\Cops\Model\LinkNavigation;
-
-class BaseList
-{
- public Request $request;
- public string $className;
- /** @var ?int */
- protected $databaseId = null;
- /** @var ?int */
- protected $numberPerPage = null;
- /** @var array */
- //protected $ignoredCategories = [];
- /** @var ?string */
- public $orderBy = null;
- protected string $handler = '';
-
- /**
- * @param class-string $className
- * @param ?Request $request
- * @param ?int $database
- * @param ?int $numberPerPage
- */
- public function __construct($className, $request, $database = null, $numberPerPage = null)
- {
- $this->className = $className;
- $this->request = $request ?? new Request();
- $this->databaseId = $database ?? $this->request->database();
- $this->numberPerPage = $numberPerPage ?? $this->request->option("max_item_per_page");
- //$this->ignoredCategories = $this->request->option('ignored_categories');
- $this->setOrderBy();
- // get handler based on $this->request
- $this->handler = $this->request->getHandler();
- }
-
- /**
- * Summary of setOrderBy
- * @return void
- */
- protected function setOrderBy()
- {
- $this->orderBy = $this->request->getSorted($this->getSort());
- //$this->orderBy ??= $this->request->option('sort');
- }
-
- /**
- * Summary of getOrderBy
- * @return ?string
- */
- protected function getOrderBy()
- {
- return match ($this->orderBy) {
- 'title' => 'sort',
- 'count' => 'count desc, name',
- default => $this->orderBy,
- };
- }
-
- /**
- * Summary of getDatabaseId
- * @return ?int
- */
- public function getDatabaseId()
- {
- return $this->databaseId;
- }
-
- /** Use inherited class methods to get entries from by instance (linked via books) */
-
- /**
- * Summary of getTable
- * @return string
- */
- public function getTable()
- {
- return $this->className::SQL_TABLE;
- }
-
- /**
- * Summary of getSort
- * @return string
- */
- public function getSort()
- {
- return $this->className::SQL_SORT;
- }
-
- /**
- * Summary of getCountColumns
- * @return string
- */
- public function getCountColumns()
- {
- return $this->className::SQL_COLUMNS . ", count(*) as count";
- }
-
- /**
- * Summary of getLinkTable
- * @return string
- */
- public function getLinkTable()
- {
- return $this->className::SQL_LINK_TABLE;
- }
-
- /**
- * Summary of getLinkColumn
- * @return string
- */
- public function getLinkColumn()
- {
- return $this->className::SQL_LINK_COLUMN;
- }
-
- /** Generic methods inherited by Author, Language, Publisher, Rating, Series, Tag classes */
-
- /**
- * Summary of getInstanceById
- * @param ?int $id
- * @return mixed
- */
- public function getInstanceById($id)
- {
- return $this->className::getInstanceById($id, $this->databaseId);
- }
-
- /**
- * Summary of getWithoutEntry
- * @return ?Entry
- */
- public function getWithoutEntry()
- {
- $count = $this->countWithoutEntries();
- $instance = $this->getInstanceById(null);
- $instance->setHandler($this->handler);
- return $instance->getEntry($count);
- }
-
- /**
- * Summary of getEntryCount
- * @return ?Entry
- */
- public function getEntryCount()
- {
- return $this->className::getCount($this->databaseId);
- }
-
- /**
- * Summary of countRequestEntries
- * @return int
- */
- public function countRequestEntries()
- {
- if ($this->request->hasFilter()) {
- return $this->countEntriesByFilter();
- }
- return $this->countAllEntries();
- }
-
- /**
- * Summary of countAllEntries
- * @return int
- */
- public function countAllEntries()
- {
- return Database::querySingle('select count(*) from ' . $this->getTable(), $this->databaseId);
- }
-
- /**
- * Summary of countDistinctEntries
- * @param ?string $column
- * @return int
- */
- public function countDistinctEntries($column = null)
- {
- $column ??= $this->getSort();
- return Database::querySingle('select count(distinct ' . $column . ') from ' . $this->getTable(), $this->databaseId);
- }
-
- /**
- * Summary of countEntriesByFirstLetter
- * @param string $letter
- * @return int
- */
- public function countEntriesByFirstLetter($letter)
- {
- $filterString = 'upper(' . $this->getTable() . '.' . $this->getSort() . ') like ?';
- $param = $letter . "%";
- $filter = new Filter($this->request, [], $this->getLinkTable(), $this->databaseId);
- $filter->addFilter($filterString, $param);
- return $this->countFilteredEntries($filter);
- }
-
- /**
- * Summary of countEntriesByFilter
- * @return int
- */
- public function countEntriesByFilter()
- {
- $filter = new Filter($this->request, [], $this->getLinkTable(), $this->databaseId);
- return $this->countFilteredEntries($filter);
- }
-
- /**
- * Summary of countEntriesByInstance
- * @param Base|Category $instance
- * @param array $filterParams if we want to filter by virtual library etc.
- * @return int
- */
- public function countEntriesByInstance($instance, $filterParams = [])
- {
- $filter = new Filter($filterParams, [], $this->getLinkTable(), $this->databaseId);
- $filter->addInstanceFilter($instance);
- return $this->countFilteredEntries($filter);
- }
-
- /**
- * Summary of countFilteredEntries
- * @param Filter $filter
- * @return int
- */
- public function countFilteredEntries($filter)
- {
- // select {0} from series, books_series_link where series.id = books_series_link.series {1}
- $query = 'select {0} from ' . $this->getTable() . ', ' . $this->getLinkTable() . ' where ' . $this->getTable() . '.id = ' . $this->getLinkTable() . '.' . $this->getLinkColumn() . ' {1}';
- // count(distinct series.id)
- $columns = 'count(distinct ' . $this->getTable() . '.id)';
- // and (exists (select null from books_authors_link, books where books_series_link.book = books.id and books_authors_link.book = books.id and books_authors_link.author = ?))
- $filterString = $filter->getFilterString();
- // [1]
- $params = $filter->getQueryParams();
- return Database::countFilter($query, $columns, $filterString, $params, $this->databaseId);
- }
-
- /**
- * Summary of countWithoutEntries
- * @return int
- */
- public function countWithoutEntries()
- {
- // @todo see BookList::getBooksWithoutCustom() to support CustomColumn
- if (!in_array($this->className, [Rating::class, Serie::class, Tag::class, Identifier::class])) {
- return 0;
- }
- $query = $this->className::SQL_BOOKLIST_NULL;
- $columns = 'count(distinct books.id)';
- return Database::countFilter($query, $columns, "", [], $this->databaseId);
- }
-
- /**
- * Summary of getRequestEntries
- * @param int $n
- * @return array
- */
- public function getRequestEntries($n = 1)
- {
- if ($this->request->hasFilter()) {
- return $this->getEntriesByFilter($n);
- }
- return $this->getAllEntries($n);
- }
-
- /**
- * Summary of getAllEntries = same as getAll() in child class
- * @param int $n
- * @return array
- */
- public function getAllEntries($n = 1)
- {
- $query = $this->className::SQL_ALL_ROWS;
- if (!empty($this->orderBy) && $this->orderBy != $this->getSort() && str_contains($this->getCountColumns(), ' as ' . $this->orderBy)) {
- if (str_contains($query, 'order by')) {
- $query = preg_replace('/\s+order\s+by\s+[\w.]+(\s+(asc|desc)|)\s*/i', ' order by ' . $this->getOrderBy() . ' ', $query);
- } else {
- $query .= ' order by ' . $this->getOrderBy() . ' ';
- }
- }
- $columns = $this->getCountColumns();
- return $this->getEntryArrayWithBookNumber($query, $columns, "", [], $n);
- }
-
- /**
- * Summary of getAllEntriesByQuery
- * @param string $find
- * @param int $n
- * @param int $repeat for SCOPE_AUTHOR we need to repeat the query x 2 because Author checks both name and sort fields
- * @return array
- */
- public function getAllEntriesByQuery($find, $n = 1, $repeat = 1)
- {
- $query = $this->className::SQL_ROWS_FOR_SEARCH;
- $columns = $this->getCountColumns();
- // Author has 2 params, the rest 1
- $params = array_fill(0, $repeat, '%' . $find . '%');
- if ($this->request->hasFilter()) {
- $filter = new Filter($this->request, $params, $this->getLinkTable(), $this->databaseId);
- $filterString = $filter->getFilterString();
- $params = $filter->getQueryParams();
- return $this->getEntryArrayWithBookNumber($query, $columns, $filterString, $params, $n);
- }
- return $this->getEntryArrayWithBookNumber($query, $columns, "", $params, $n);
- }
-
- /**
- * Summary of getCountByFirstLetter
- * @return array
- */
- public function getCountByFirstLetter()
- {
- // substr(upper(authors.sort), 1, 1)
- $groupField = 'substr(upper(' . $this->getTable() . '.' . $this->getSort() . '), 1, 1)';
- return $this->getCountByGroup($groupField, $this->className::PAGE_LETTER, 'letter');
- }
-
- /**
- * Summary of getCountByGroup
- * @param string $groupField
- * @param string $page
- * @param string $label
- * @return array
- */
- public function getCountByGroup($groupField, $page, $label)
- {
- $filter = new Filter($this->request, [], $this->getLinkTable(), $this->databaseId);
- $filterString = $filter->getFilterString();
- $params = $filter->getQueryParams();
-
- if (!in_array($this->orderBy, ['groupid', 'count'])) {
- $this->orderBy = 'groupid';
- }
- $sortBy = $this->getOrderBy();
- // select {0} from authors, books_authors_link where authors.id = books_authors_link.author {1}
- $query = 'select {0} from ' . $this->getTable() . ', ' . $this->getLinkTable() . ' where ' . $this->getTable() . '.id = ' . $this->getLinkTable() . '.' . $this->getLinkColumn() . ' {1}';
- // group by groupid
- $query .= ' group by groupid';
- // order by $sortBy
- $query .= ' order by ' . $sortBy;
- // $groupField as groupid, count(distinct authors.id) as count
- $columns = $groupField . ' as groupid, count(distinct ' . $this->getTable() . '.id) as count';
- $result = Database::queryFilter($query, $columns, $filterString, $params, -1, $this->databaseId);
-
- $entryArray = [];
- $params = $this->request->getFilterParams();
- $params["db"] ??= $this->databaseId;
- while ($post = $result->fetchObject()) {
- $params["id"] = $post->groupid;
- $href = Route::link($this->handler, $page, $params);
- array_push($entryArray, new Entry(
- $post->groupid,
- $this->className::PAGE_ID . ':' . $label . ':' . $post->groupid,
- str_format(localize('bookword', $post->count), $post->count),
- 'text',
- [ new LinkNavigation($href, "subsection") ],
- $this->databaseId,
- ucfirst($label),
- $post->count
- ));
- }
- return $entryArray;
- }
-
- /**
- * Summary of getEntriesByFirstLetter
- * @param string $letter
- * @param int $n
- * @return array
- */
- public function getEntriesByFirstLetter($letter, $n = 1)
- {
- $query = $this->className::SQL_ROWS_BY_FIRST_LETTER;
- $columns = $this->getCountColumns();
- $filter = new Filter($this->request, [$letter . "%"], $this->getLinkTable(), $this->databaseId);
- $filterString = $filter->getFilterString();
- $params = $filter->getQueryParams();
- return $this->getEntryArrayWithBookNumber($query, $columns, $filterString, $params, $n);
- }
-
- /**
- * Summary of getEntriesByFilter
- * @param int $n
- * @return array
- */
- public function getEntriesByFilter($n = 1)
- {
- $filter = new Filter($this->request, [], $this->getLinkTable(), $this->databaseId);
- return $this->getFilteredEntries($filter, $n);
- }
-
- /**
- * Summary of getEntriesByInstance
- * @param Base|Category $instance
- * @param int $n
- * @param array $filterParams if we want to filter by virtual library etc.
- * @return array
- */
- public function getEntriesByInstance($instance, $n = 1, $filterParams = [])
- {
- $filter = new Filter($filterParams, [], $this->getLinkTable(), $this->databaseId);
- $filter->addInstanceFilter($instance);
- $entries = $this->getFilteredEntries($filter, $n);
- $limit = $instance->getFilterLimit();
- // are we at the filter limit for this instance?
- if ($n == 1 && count($entries) < $limit) {
- return $entries;
- }
- // if so, let's see how many entries we're missing
- $total = $this->countEntriesByInstance($instance, $filterParams);
- $count = $total - count($entries);
- if ($count < 1) {
- return $entries;
- }
- // @todo let the caller know there are more entries available
- // @todo we can't use facetGroups here, or OPDS reader thinks we're drilling down :-()
- $className = $instance->getClassName($this->className);
- $title = strtolower($className);
- $title = localize($title . 's.title');
- if ($n > 1) {
- $paging = '&filter=1';
- if ($n > 2) {
- $paging .= '&g[' . $this->className::URL_PARAM . ']=' . strval($n - 1);
- }
- $entry = new Entry(
- localize("paging.previous.alternate") . " " . $title,
- $instance->getEntryId() . ':filter:',
- $instance->getContent($count),
- "text",
- [ new LinkFeed($instance->getUri() . $paging) ],
- $this->databaseId,
- $className,
- $count
- );
- array_push($entries, $entry);
- }
- if ($n < ceil($total / $limit)) {
- $paging = '&filter=1';
- $paging .= '&g[' . $this->className::URL_PARAM . ']=' . strval($n + 1);
- $entry = new Entry(
- localize("paging.next.alternate") . " " . $title,
- $instance->getEntryId() . ':filter:',
- $instance->getContent($count),
- "text",
- [ new LinkFeed($instance->getUri() . $paging) ],
- $this->databaseId,
- $className,
- $count
- );
- array_push($entries, $entry);
- }
- return $entries;
- }
-
- /**
- * Summary of getEntriesByCustomValueId
- * @param CustomColumnType $customType
- * @param mixed $valueId
- * @param int $n
- * @return array
- */
- public function getEntriesByCustomValueId($customType, $valueId, $n = 1)
- {
- $filter = new Filter([], [], $this->getLinkTable(), $this->databaseId);
- $filter->addCustomIdFilter($customType, $valueId);
- return $this->getFilteredEntries($filter, $n);
- }
-
- /**
- * Summary of getFilteredEntries
- * @param Filter $filter
- * @param int $n
- * @return array
- */
- public function getFilteredEntries($filter, $n = 1)
- {
- $query = $this->className::SQL_ALL_ROWS;
- if (!empty($this->orderBy) && $this->orderBy != $this->getSort() && str_contains($this->getCountColumns(), ' as ' . $this->orderBy)) {
- if (str_contains($query, 'order by')) {
- $query = preg_replace('/\s+order\s+by\s+[\w.]+(\s+(asc|desc)|)\s*/i', ' order by ' . $this->getOrderBy() . ' ', $query);
- } else {
- $query .= ' order by ' . $this->getOrderBy() . ' ';
- }
- }
- $columns = $this->getCountColumns();
- $filterString = $filter->getFilterString();
- $params = $filter->getQueryParams();
- return $this->getEntryArrayWithBookNumber($query, $columns, $filterString, $params, $n);
- }
-
- /**
- * Summary of getEntryArrayWithBookNumber
- * @param string $query
- * @param string $columns
- * @param string $filter
- * @param array $params
- * @param int $n
- * @return array
- */
- public function getEntryArrayWithBookNumber($query, $columns, $filter, $params, $n)
- {
- $result = Database::queryFilter($query, $columns, $filter, $params, $n, $this->databaseId, $this->numberPerPage);
- $entryArray = [];
- $params = [];
- if ($this->request->hasFilter()) {
- $params = $this->request->getFilterParams();
- //$params["db"] ??= $this->databaseId;
- }
- while ($post = $result->fetchObject()) {
- /** @var Author|Tag|Serie|Publisher|Language|Rating|Book $instance */
- if ($this->className == Book::class) {
- $post->count = 1;
- }
-
- $instance = new $this->className($post, $this->databaseId);
- $instance->setHandler($this->handler);
- array_push($entryArray, $instance->getEntry($post->count, $params));
- }
- return $entryArray;
- }
-
- /**
- * Summary of hasChildCategories
- * @return bool
- */
- public function hasChildCategories()
- {
- if (empty(Config::get('calibre_categories_using_hierarchy')) || !in_array($this->className::CATEGORY, Config::get('calibre_categories_using_hierarchy'))) {
- return false;
- }
- return true;
- }
-
- /**
- * Use the Calibre tag browser view to retrieve all tags or series with count
- * Format: tag_browser_tags(id,name,count,avg_rating,sort)
- * @param int $n
- * @return array
- */
- public function browseAllEntries($n = 1)
- {
- if (!$this->hasChildCategories()) {
- return [];
- }
- $tableName = 'tag_browser_' . $this->className::CATEGORY;
- $queryFormat = "SELECT id, name, count FROM {0} ORDER BY {1}";
- if (!in_array($this->orderBy, ['id', 'name', 'count', 'sort'])) {
- $this->orderBy = "sort";
- }
- $query = str_format($queryFormat, $tableName, $this->getOrderBy());
-
- $result = Database::queryFilter($query, "", "", [], $n, $this->databaseId, $this->numberPerPage);
- $entryArray = [];
- while ($post = $result->fetchObject()) {
- $instance = new $this->className($post, $this->databaseId);
- $instance->setHandler($this->handler);
- array_push($entryArray, $instance->getEntry($post->count));
- }
- return $entryArray;
- }
-
- /**
- * Summary of getInstanceIdsByBookIds
- * @param array $bookIds
- * @return array>
- */
- public function getInstanceIdsByBookIds($bookIds)
- {
- if (count($bookIds) < 1) {
- return [];
- }
- $queryFormat = 'SELECT book, {1} as instanceId FROM {0} WHERE book IN (' . str_repeat('?,', count($bookIds) - 1) . '?)';
- $query = str_format($queryFormat, $this->getLinkTable(), $this->getLinkColumn());
- $result = Database::query($query, $bookIds, $this->databaseId);
-
- $instanceIds = [];
- while ($post = $result->fetchObject()) {
- $instanceIds[$post->book] ??= [];
- array_push($instanceIds[$post->book], $post->instanceId);
- }
- return $instanceIds;
- }
-
- /**
- * Summary of getInstancesByIds
- * @param array> $instanceIds
- * @return array
- */
- public function getInstancesByIds($instanceIds)
- {
- $uniqueIds = static::getUniqueInstanceIds($instanceIds);
- if (count($uniqueIds) < 1) {
- return [];
- }
- $query = 'select ' . $this->className::SQL_COLUMNS . ' from ' . $this->className::SQL_TABLE . ' where id IN (' . str_repeat('?,', count($uniqueIds) - 1) . '?)';
- $result = Database::query($query, $uniqueIds, $this->databaseId);
- $instances = [];
- while ($post = $result->fetchObject()) {
- if ($this->className == Data::class) {
- // we don't have the book available here, set later
- $instances[$post->id] = new $this->className($post);
- } else {
- $instances[$post->id] = new $this->className($post, $this->databaseId);
- $instances[$post->id]->setHandler($this->handler);
- }
- }
- return $instances;
- }
-
- /**
- * Summary of getUniqueInstanceIds
- * @param array> $instanceIds
- * @return array
- */
- public static function getUniqueInstanceIds($instanceIds)
- {
- $uniqueIds = [];
- foreach ($instanceIds as $bookId => $instanceIdList) {
- $uniqueIds = array_values(array_unique(array_merge($uniqueIds, $instanceIdList)));
- }
- return $uniqueIds;
- }
-}
diff --git a/COPS/cops-3.1.3/src/Calibre/Book.php b/COPS/cops-3.1.3/src/Calibre/Book.php
deleted file mode 100644
index b3317ed2..00000000
--- a/COPS/cops-3.1.3/src/Calibre/Book.php
+++ /dev/null
@@ -1,879 +0,0 @@
-
- */
-
-namespace SebLucas\Cops\Calibre;
-
-use SebLucas\Cops\Input\Config;
-use SebLucas\Cops\Input\Route;
-use SebLucas\Cops\Model\EntryBook;
-use SebLucas\Cops\Model\LinkEntry;
-use SebLucas\Cops\Model\LinkFeed;
-use SebLucas\Cops\Output\FileResponse;
-use SebLucas\Cops\Output\Format;
-use SebLucas\Cops\Output\Response;
-use SebLucas\Cops\Pages\PageId;
-use SebLucas\EPubMeta\EPub;
-use SebLucas\EPubMeta\Tools\ZipEdit;
-use Exception;
-
-//class Book extends Base
-class Book
-{
- public const PAGE_ID = PageId::ALL_BOOKS_ID;
- public const PAGE_ALL = PageId::ALL_BOOKS;
- public const PAGE_LETTER = PageId::ALL_BOOKS_LETTER;
- public const PAGE_YEAR = PageId::ALL_BOOKS_YEAR;
- public const PAGE_DETAIL = PageId::BOOK_DETAIL;
- public const SQL_TABLE = "books";
- public const SQL_LINK_TABLE = "books";
- public const SQL_LINK_COLUMN = "id";
- public const SQL_SORT = "sort";
- public const SQL_COLUMNS = 'books.id as id, books.title as title, text as comment, path, timestamp, pubdate, series_index, uuid, has_cover, ratings.rating';
- public const SQL_ALL_ROWS = BookList::SQL_BOOKS_ALL;
-
- public const SQL_BOOKS_LEFT_JOIN = 'left outer join comments on comments.book = books.id
- left outer join books_ratings_link on books_ratings_link.book = books.id
- left outer join ratings on books_ratings_link.rating = ratings.id ';
-
- public const BAD_SEARCH = 'QQQQQ';
- public const DATA_DIR_NAME = 'data';
-
- /** @var int */
- public $id;
- /** @var string */
- public $title;
- /** @var mixed */
- public $timestamp;
- /** @var mixed */
- public $pubdate;
- /** @var string */
- public $path;
- /** @var string */
- public $uuid;
- /** @var bool */
- public $hasCover;
- /** @var string */
- public $relativePath;
- /** @var ?float */
- public $seriesIndex;
- /** @var string */
- public $comment;
- /** @var ?int */
- public $rating;
- /** @var ?int */
- protected $databaseId = null;
- /** @var ?array */
- public $datas = null;
- /** @var ?array */
- public $extraFiles = null;
- /** @var ?array */
- public $authors = null;
- /** @var Publisher|false|null */
- public $publisher = null;
- /** @var Serie|false|null */
- public $serie = null;
- /** @var ?array */
- public $tags = null;
- /** @var ?array */
- public $identifiers = null;
- /** @var ?string */
- public $languages = null;
- /** @var ?array */
- public $annotations = null;
- /** @var array */
- public $format = [];
- /** @var ?string */
- protected $coverFileName = null;
- public bool $updateForKepub = false;
- protected string $handler = '';
-
- /**
- * Summary of __construct
- * @param object $line
- * @param ?int $database
- */
- public function __construct($line, $database = null)
- {
- $this->id = $line->id;
- $this->title = $line->title;
- $this->timestamp = strtotime($line->timestamp);
- $this->pubdate = $line->pubdate;
- //$this->path = Database::getDbDirectory() . $line->path;
- //$this->relativePath = $line->path;
- // -DC- Init relative or full path
- if (!empty(Config::get('calibre_external_storage'))) {
- $this->path = Config::get('calibre_external_storage') . str_replace('%2F', '/', rawurlencode($line->path));
- } else {
- $this->path = $line->path;
- if (!is_dir($this->path)) {
- $this->path = Database::getDbDirectory($database) . $line->path;
- }
- }
- $this->seriesIndex = $line->series_index;
- $this->comment = $line->comment ?? '';
- $this->uuid = $line->uuid;
- $this->hasCover = $line->has_cover;
- $this->rating = $line->rating;
- $this->databaseId = $database;
- // do this at the end when all properties are set
- if ($this->hasCover) {
- $this->coverFileName = Cover::findCoverFileName($this, $line);
- if (empty($this->coverFileName)) {
- $this->hasCover = false;
- }
- }
- }
-
- /**
- * Summary of getDatabaseId
- * @return ?int
- */
- public function getDatabaseId()
- {
- return $this->databaseId;
- }
-
- /**
- * Summary of getCoverFileName
- * @return ?string
- */
- public function getCoverFileName()
- {
- if ($this->hasCover) {
- return $this->coverFileName;
- }
- return null;
- }
-
- /**
- * Summary of getEntryId
- * @return string
- */
- public function getEntryId()
- {
- return PageId::ALL_BOOKS_UUID . ':' . $this->uuid;
- }
-
- /**
- * Summary of getEntryIdByLetter
- * @param string $startingLetter
- * @return string
- */
- public static function getEntryIdByLetter($startingLetter)
- {
- return static::PAGE_ID . ':letter:' . $startingLetter;
- }
-
- /**
- * Summary of getEntryIdByYear
- * @param string|int $year
- * @return string
- */
- public static function getEntryIdByYear($year)
- {
- return static::PAGE_ID . ':year:' . $year;
- }
-
- /**
- * Summary of getUri
- * @param array $params
- * @return string
- */
- public function getUri($params = [])
- {
- $params['id'] = $this->id;
- // we need databaseId here because we use Route::link with $handler
- $params['db'] = $this->databaseId;
- if (Config::get('use_route_urls')) {
- $params['author'] = $this->getAuthorsName();
- $params['title'] = $this->getTitle();
- }
- return Route::link($this->handler, static::PAGE_DETAIL, $params);
- }
-
- /**
- * Summary of getDetailUrl
- * @param string|null $handler
- * @param array $params
- * @return string
- */
- public function getDetailUrl($handler = null, $params = [])
- {
- $handler ??= $this->handler;
- $params['id'] = $this->id;
- // we need databaseId here because we use Route::link with $handler
- $params['db'] = $this->databaseId;
- if (Config::get('use_route_urls')) {
- $params['author'] = $this->getAuthorsName();
- $params['title'] = $this->getTitle();
- }
- return Route::link($handler, static::PAGE_DETAIL, $params);
- }
-
- /**
- * Summary of getTitle
- * @return string
- */
- public function getTitle()
- {
- return $this->title;
- }
-
- /* Other class (author, series, tag, ...) initialization and accessors */
-
- /**
- * @param int $n
- * @param ?string $sort
- * @return ?array
- */
- public function getAuthors($n = -1, $sort = null)
- {
- if (is_null($this->authors)) {
- $this->authors = Author::getInstancesByBookId($this->id, $this->databaseId);
- }
- return $this->authors;
- }
-
- /**
- * Summary of getAuthorsName
- * @return string
- */
- public function getAuthorsName()
- {
- return implode(', ', array_map(function ($author) {
- return $author->name;
- }, $this->getAuthors()));
- }
-
- /**
- * Summary of getAuthorsSort
- * @return string
- */
- public function getAuthorsSort()
- {
- return implode(', ', array_map(function ($author) {
- return $author->sort;
- }, $this->getAuthors()));
- }
-
- /**
- * Summary of getPublisher
- * @return Publisher|false
- */
- public function getPublisher()
- {
- if (is_null($this->publisher)) {
- $this->publisher = Publisher::getInstanceByBookId($this->id, $this->databaseId);
- }
- return $this->publisher;
- }
-
- /**
- * @return Serie|false
- */
- public function getSerie()
- {
- if (is_null($this->serie)) {
- $this->serie = Serie::getInstanceByBookId($this->id, $this->databaseId);
- }
- return $this->serie;
- }
-
- /**
- * @param int $n
- * @param ?string $sort
- * @return string
- */
- public function getLanguages($n = -1, $sort = null)
- {
- if (is_null($this->languages)) {
- $this->languages = Language::getLanguagesByBookId($this->id, $this->databaseId);
- }
- return $this->languages;
- }
-
- /**
- * @param int $n
- * @param ?string $sort
- * @return array
- */
- public function getTags($n = -1, $sort = null)
- {
- if (is_null($this->tags)) {
- $this->tags = Tag::getInstancesByBookId($this->id, $this->databaseId);
- }
- return $this->tags;
- }
-
- /**
- * Summary of getTagsName
- * @return string
- */
- public function getTagsName()
- {
- return implode(', ', array_map(function ($tag) {
- return $tag->name;
- }, $this->getTags()));
- }
-
- /**
- * @return array
- */
- public function getIdentifiers()
- {
- if (is_null($this->identifiers)) {
- $this->identifiers = Identifier::getInstancesByBookId($this->id, $this->databaseId);
- }
- return $this->identifiers;
- }
-
- /**
- * @return array
- */
- public function getAnnotations()
- {
- if (is_null($this->annotations)) {
- $this->annotations = Annotation::getInstancesByBookId($this->id, $this->databaseId);
- }
- return $this->annotations;
- }
-
- /**
- * @param string $source from metadata.opf file (default)
- * @return Metadata|false
- */
- public function getMetadata($source = 'file')
- {
- $file = realpath($this->path . '/metadata.opf');
- if (empty($file) || !file_exists($file)) {
- return false;
- }
- $content = file_get_contents($file);
- return Metadata::parseData($content);
- }
-
- /**
- * @return array
- */
- public function getDatas()
- {
- if (is_null($this->datas)) {
- $this->datas = static::getDataByBook($this);
- }
- return $this->datas;
- }
-
- /**
- * Get extra data files associated with this book
- * @see https://manual.calibre-ebook.com/metadata.html#data-files
- * @return array
- */
- public function getExtraFiles()
- {
- if (is_null($this->extraFiles)) {
- $this->extraFiles = [];
- $dataPath = $this->path . '/' . static::DATA_DIR_NAME . '/';
- if (empty(Config::get('calibre_external_storage')) && is_dir($dataPath)) {
- $iterator = new \RecursiveIteratorIterator(new \RecursiveDirectoryIterator($dataPath));
- foreach ($iterator as $file) {
- if ($file->isDir()) {
- continue;
- }
- if (!str_starts_with((string) $file->getPathname(), $dataPath)) {
- continue;
- }
- array_push($this->extraFiles, substr((string) $file->getPathname(), strlen($dataPath)));
- }
- }
- }
- return $this->extraFiles;
- }
-
- /**
- * Summary of getExtraFileLink
- * @param string $fileName
- * @return LinkEntry
- */
- public function getExtraFileLink($fileName)
- {
- $filePath = $this->path . '/' . static::DATA_DIR_NAME . '/' . $fileName;
- $mimetype = Response::getMimeType($filePath) ?? 'application/octet-stream';
- if (Database::useAbsolutePath($this->databaseId)) {
- $params = ['id' => $this->id, 'db' => $this->databaseId];
- if (Config::get('use_route_urls') && is_null($params['db'])) {
- $params['db'] = 0;
- }
- $params['file'] = $fileName;
- $url = Route::link("fetch", null, $params);
- } else {
- $url = Route::path(str_replace('%2F', '/', rawurlencode($filePath)));
- }
- $linkEntry = new LinkEntry(
- $url,
- $mimetype,
- 'related',
- $fileName
- );
- $linkEntry->addFileInfo($filePath);
- return $linkEntry;
- }
-
- /**
- * Summary of GetMostInterestingDataToSendToKindle
- * @return ?Data
- */
- public function GetMostInterestingDataToSendToKindle()
- {
- $bestFormatForKindle = ['PDF', 'AZW3', 'MOBI', 'EPUB'];
- $bestRank = -1;
- $bestData = null;
- foreach ($this->getDatas() as $data) {
- $key = array_search($data->format, $bestFormatForKindle);
- if ($key !== false && $key > $bestRank) {
- $bestRank = $key;
- $bestData = $data;
- }
- }
- return $bestData;
- }
-
- /**
- * Summary of getDataById
- * @param int $idData
- * @return Data|false
- */
- public function getDataById($idData)
- {
- $reduced = array_filter($this->getDatas(), function ($data) use ($idData) {
- return $data->id == $idData;
- });
- return reset($reduced);
- }
-
- /**
- * Summary of getRating
- * @return string
- */
- public function getRating()
- {
- if (is_null($this->rating) || $this->rating == 0) {
- return '';
- }
- $retour = '';
- for ($i = 0; $i < $this->rating / 2; $i++) {
- $retour .= '★'; // full star
- }
- for ($i = 0; $i < 5 - $this->rating / 2; $i++) {
- $retour .= '☆'; // empty star
- }
- return $retour;
- }
-
- /**
- * Summary of getPubDate
- * @return string
- */
- public function getPubDate()
- {
- if (empty($this->pubdate)) {
- return '';
- }
- $dateY = (int) substr((string) $this->pubdate, 0, 4);
- if ($dateY > 102) {
- return str_pad(strval($dateY), 4, '0', STR_PAD_LEFT);
- }
- return '';
- }
-
- /**
- * Summary of getComment
- * @param bool $withSerie
- * @return string
- */
- public function getComment($withSerie = true)
- {
- $addition = '';
- $se = $this->getSerie();
- if (!empty($se) && $withSerie) {
- $addition = $addition . '' . localize('content.series') . '' . str_format(localize('content.series.data'), $this->seriesIndex, htmlspecialchars($se->name)) . " \n";
- }
- //if (preg_match('/<\/(div|p|a|span)>/', $this->comment)) {
- return $addition . Format::html2xhtml($this->comment);
- //} else {
- // return $addition . htmlspecialchars($this->comment);
- //}
- }
-
- /**
- * Summary of getDataFormat
- * @param string $format
- * @return Data|false
- */
- public function getDataFormat($format)
- {
- $reduced = array_filter($this->getDatas(), function ($data) use ($format) {
- return $data->format == $format;
- });
- return reset($reduced);
- }
-
- /**
- * @checkme always returns absolute path for single DB in PHP app here - cfr. internal dir for X-Accel-Redirect with Nginx
- * @param string $extension
- * @param int $idData
- * @param bool $encoded url encode filename
- * @return string|false|null string for file path, false for missing cover, null for missing data
- */
- public function getFilePath($extension, $idData = null, $encoded = false)
- {
- if ($extension == "jpg" || $extension == "png") {
- return $this->getCoverFilePath($extension);
- }
- $data = $this->getDataById($idData);
- if (!$data) {
- return null;
- }
- $file = $data->name . "." . strtolower($data->format);
- if ($encoded) {
- return $this->path . '/' . rawurlencode($file);
- }
- return $this->path . '/' . $file;
- }
-
- /**
- * @checkme always returns absolute path for single DB in PHP app here - cfr. internal dir for X-Accel-Redirect with Nginx
- * @param string $extension
- * @return string|false string for file path, false for missing cover
- */
- public function getCoverFilePath($extension)
- {
- if (empty($this->coverFileName)) {
- return $this->path . '/cover.' . $extension;
- } else {
- $ext = strtolower(pathinfo($this->coverFileName, PATHINFO_EXTENSION));
- if ($ext == $extension) {
- return $this->coverFileName;
- }
- }
- return false;
- }
-
- /**
- * Summary of sendUpdatedEpub
- * @param int $idData
- * @param FileResponse $response
- * @return FileResponse
- */
- public function sendUpdatedEpub($idData, $response)
- {
- $data = $this->getDataById($idData);
-
- // if we want to update metadata and then use kepubify, we need to save the updated Epub first
- if ($this->updateForKepub && !empty(Config::get('kepubify_path'))) {
- // make a temp copy for the updated Epub file
- $tmpfile = FileResponse::getTempFile('epub');
- if (!copy($data->getLocalPath(), $tmpfile)) {
- // this will call exit()
- Response::sendError(null, 'Error: unable to copy epub file');
- }
- $filePath = $tmpfile;
- } else {
- $filePath = $data->getLocalPath();
- }
-
- try {
- $epub = new EPub($filePath, ZipEdit::class);
-
- $epub->setTitle($this->title);
- $authorArray = [];
- foreach ($this->getAuthors() as $author) {
- $authorArray[$author->sort] = $author->name;
- }
- $epub->setAuthors($authorArray);
- $epub->setLanguage($this->getLanguages());
- $epub->setDescription($this->getComment(false));
- $epub->setSubjects($this->getTagsName());
- // -DC- Use cover file name
- // $epub->Cover2($this->getCoverFilePath('jpg'), 'image/jpeg');
- $epub->setCoverFile($this->coverFileName, 'image/jpeg');
- $epub->setCalibre($this->uuid);
- $se = $this->getSerie();
- if (!empty($se)) {
- $epub->setSeries($se->name);
- $epub->setSeriesIndex(strval($this->seriesIndex));
- }
- $filename = $data->getUpdatedFilenameEpub();
- // @checkme this is set in fetch.php now
- if ($this->updateForKepub) {
- $filename = $data->getUpdatedFilenameKepub();
- // @todo no cache control here!?
- $response->setHeaders($data->getMimeType(), null, basename($filename));
- // save updated Epub file and convert to kepub
- if (!empty(Config::get('kepubify_path'))) {
- $epub->save();
-
- // run kepubify on updated Epub file and send converted tmpfile
- $result = $this->runKepubify($filePath, $response);
- if (empty($result)) {
- // this will call exit()
- Response::sendError(null, 'Error: failed to convert epub file');
- }
- return $result;
- }
- $epub->updateForKepub();
- }
- // @todo no cache control here!?
- //$response->setHeaders($data->getMimeType(), null, basename($filename));
- $sendHeaders = headers_sent() ? false : true;
- $epub->download($filename, $sendHeaders);
- return $response;
- } catch (Exception $e) {
- // this will call exit()
- Response::sendError(null, 'Exception: ' . $e->getMessage());
- }
- }
-
- /**
- * Summary of runKepubify
- * @param string $filepath
- * @param ?FileResponse $response
- * @return FileResponse|string|null
- */
- public function runKepubify($filepath, $response = null)
- {
- if (empty(Config::get('kepubify_path'))) {
- return null;
- }
- $tmpfile = FileResponse::getTempFile('kepub.epub');
- $cmd = escapeshellarg((string) Config::get('kepubify_path'));
- $cmd .= ' -o ' . escapeshellarg($tmpfile);
- $cmd .= ' ' . escapeshellarg($filepath);
- exec($cmd, $output, $return);
- if ($return == 0 && file_exists($tmpfile)) {
- if (!empty($response)) {
- return $response->sendFile($tmpfile, true);
- }
- return $tmpfile;
- }
- return null;
- }
-
- /**
- * The values of all the specified columns
- *
- * @param string[] $columns
- * @param bool $asArray
- * @return array
- */
- public function getCustomColumnValues($columns, $asArray = false)
- {
- $result = [];
- $database = $this->databaseId;
-
- $columns = CustomColumnType::checkCustomColumnList($columns, $database);
-
- foreach ($columns as $lookup) {
- $col = CustomColumnType::createByLookup($lookup, $database);
- if (!is_null($col)) {
- $cust = $col->getCustomByBook($this);
- if ($asArray) {
- array_push($result, $cust->toArray());
- } else {
- array_push($result, $cust);
- }
- }
- }
-
- return $result;
- }
-
- /**
- * Summary of getLinkArray
- * @param array $params @todo is this useful here?
- * @return array
- */
- public function getLinkArray($params = [])
- {
- $database = $this->databaseId;
- $linkArray = [];
-
- $cover = new Cover($this);
- $coverLink = $cover->getCoverLink();
- if ($coverLink) {
- array_push($linkArray, $coverLink);
- }
- // @todo set height for thumbnail here depending on opds vs. html
- $thumb = 'html';
- $thumbnailLink = $cover->getThumbnailLink($thumb);
- if ($thumbnailLink) {
- array_push($linkArray, $thumbnailLink);
- }
-
- foreach ($this->getDatas() as $data) {
- if ($data->isKnownType()) {
- $linkEntry = $data->getDataLink(LinkEntry::OPDS_ACQUISITION_TYPE, $data->format);
- if (empty(Config::get('calibre_external_storage'))) {
- $linkEntry->addFileInfo($data->getLocalPath());
- }
- array_push($linkArray, $linkEntry);
- }
- }
-
- // don't use collection here, or OPDS reader will group all entries together - messes up recent books
- foreach ($this->getAuthors() as $author) {
- /** @var Author $author */
- $author->setHandler($this->handler);
- array_push(
- $linkArray,
- new LinkFeed(
- $author->getUri(),
- 'related',
- str_format(localize('bookentry.author'), localize('splitByLetter.book.other'), $author->name)
- )
- );
- }
-
- // don't use collection here, or OPDS reader will group all entries together - messes up recent books
- $serie = $this->getSerie();
- if (!empty($serie)) {
- $serie->setHandler($this->handler);
- array_push(
- $linkArray,
- new LinkFeed(
- $serie->getUri(),
- 'related',
- str_format(localize('content.series.data'), $this->seriesIndex, $serie->name)
- )
- );
- }
-
- return $linkArray;
- }
-
- /**
- * Summary of setHandler
- * @param string $handler
- * @return void
- */
- public function setHandler($handler)
- {
- $this->handler = $handler;
- }
-
- /**
- * Summary of getHandler
- * @return string
- */
- public function getHandler()
- {
- return $this->handler;
- }
-
- /**
- * Summary of getEntry
- * @param int $count
- * @param array $params
- * @return EntryBook
- */
- public function getEntry($count = 0, $params = [])
- {
- return new EntryBook(
- $this->getTitle(),
- $this->getEntryId(),
- $this->getComment(),
- 'text/html',
- $this->getLinkArray($params),
- $this
- );
- }
-
- /* End of other class (author, series, tag, ...) initialization and accessors */
-
- // -DC- Get customisable book columns
- /**
- * Summary of getBookColumns
- * @return string
- */
- public static function getBookColumns()
- {
- $res = static::SQL_COLUMNS;
- if (!empty(Config::get('calibre_database_field_cover'))) {
- $res = str_replace('has_cover,', 'has_cover, ' . Config::get('calibre_database_field_cover') . ',', $res);
- }
-
- return $res;
- }
-
- /**
- * Summary of getBookById
- * @param int $bookId
- * @param ?int $database
- * @return ?Book
- */
- public static function getBookById($bookId, $database = null)
- {
- $query = 'select ' . static::getBookColumns() . '
-from books ' . static::SQL_BOOKS_LEFT_JOIN . '
-where books.id = ?';
- $result = Database::query($query, [$bookId], $database);
- while ($post = $result->fetchObject()) {
- $book = new Book($post, $database);
- return $book;
- }
- return null;
- }
-
- /**
- * Summary of getBookByDataId
- * @param int $dataId
- * @param ?int $database
- * @return ?Book
- */
- public static function getBookByDataId($dataId, $database = null)
- {
- $query = 'select ' . static::getBookColumns() . ', data.name, data.format
-from data, books ' . static::SQL_BOOKS_LEFT_JOIN . '
-where data.book = books.id and data.id = ?';
- $result = Database::query($query, [$dataId], $database);
- while ($post = $result->fetchObject()) {
- $book = new Book($post, $database);
- $data = new Data($post, $book);
- $data->id = $dataId;
- $book->datas = [$data];
- return $book;
- }
- return null;
- }
-
- /**
- * Summary of getDataByBook
- * @param Book $book
- * @return array
- */
- public static function getDataByBook($book)
- {
- $out = [];
-
- $sql = 'select id, format, name from data where book = ?';
-
- $ignored_formats = Config::get('ignored_formats');
- if (count($ignored_formats) > 0) {
- $sql .= " and format not in ('"
- . implode("','", $ignored_formats)
- . "')";
- }
-
- $database = $book->getDatabaseId();
- $result = Database::query($sql, [$book->id], $database);
-
- while ($post = $result->fetchObject()) {
- array_push($out, new Data($post, $book));
- }
- return $out;
- }
-}
diff --git a/COPS/cops-3.1.3/src/Calibre/BookList.php b/COPS/cops-3.1.3/src/Calibre/BookList.php
deleted file mode 100644
index b3b287d8..00000000
--- a/COPS/cops-3.1.3/src/Calibre/BookList.php
+++ /dev/null
@@ -1,622 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops\Calibre;
-
-use SebLucas\Cops\Input\Config;
-use SebLucas\Cops\Input\Route;
-use SebLucas\Cops\Input\Request;
-use SebLucas\Cops\Model\Entry;
-use SebLucas\Cops\Model\EntryBook;
-use SebLucas\Cops\Model\LinkFeed;
-use SebLucas\Cops\Model\LinkNavigation;
-use SebLucas\Cops\Pages\PageId;
-use SebLucas\Cops\Pages\PageQueryResult;
-use Exception;
-
-class BookList
-{
- public const SQL_BOOKS_ALL = 'select {0} from books ' . Book::SQL_BOOKS_LEFT_JOIN . ' where 1=1 {1} order by books.sort ';
- public const SQL_BOOKS_BY_FIRST_LETTER = 'select {0} from books ' . Book::SQL_BOOKS_LEFT_JOIN . '
- where upper (books.sort) like ? {1} order by books.sort';
- public const SQL_BOOKS_BY_PUB_YEAR = 'select {0} from books ' . Book::SQL_BOOKS_LEFT_JOIN . '
- where substr(date(books.pubdate), 1, 4) = ? {1} order by books.sort';
- public const SQL_BOOKS_QUERY = 'select {0} from books ' . Book::SQL_BOOKS_LEFT_JOIN . '
- where (
- exists (select null from authors, books_authors_link where book = books.id and author = authors.id and authors.name like ?) or
- exists (select null from tags, books_tags_link where book = books.id and tag = tags.id and tags.name like ?) or
- exists (select null from series, books_series_link on book = books.id and books_series_link.series = series.id and series.name like ?) or
- exists (select null from publishers, books_publishers_link where book = books.id and books_publishers_link.publisher = publishers.id and publishers.name like ?) or
- title like ?) {1} order by books.sort';
- public const SQL_BOOKS_RECENT = 'select {0} from books ' . Book::SQL_BOOKS_LEFT_JOIN . '
- where 1=1 {1} order by books.timestamp desc limit ';
- public const URL_PARAM_FIRST = "f";
- public const URL_PARAM_YEAR = "y";
-
- public const BAD_SEARCH = 'QQQQQ';
- public const BATCH_QUERY = false;
-
- public Request $request;
- /** @var ?int */
- protected $databaseId = null;
- /** @var ?int */
- protected $numberPerPage = null;
- /** @var array */
- //protected $ignoredCategories = [];
- /** @var ?string */
- public $orderBy = null;
- /** @var array */
- public $bookList = [];
- protected string $handler = '';
-
- /**
- * @param ?Request $request
- * @param ?int $database
- * @param ?int $numberPerPage
- */
- public function __construct($request, $database = null, $numberPerPage = null)
- {
- $this->request = $request ?? new Request();
- $this->databaseId = $database ?? $this->request->database();
- $this->numberPerPage = $numberPerPage ?? $this->request->option("max_item_per_page");
- //$this->ignoredCategories = $this->request->option('ignored_categories');
- $this->setOrderBy();
- // get handler based on $this->request
- $this->handler = $this->request->getHandler();
- }
-
- /**
- * Summary of setOrderBy
- * @return void
- */
- protected function setOrderBy()
- {
- $this->orderBy = $this->request->getSorted();
- //$this->orderBy ??= $this->request->option('sort');
- }
-
- /**
- * Summary of getOrderBy
- * @return ?string
- */
- protected function getOrderBy()
- {
- return match ($this->orderBy) {
- 'title asc', 'title' => 'books.sort asc',
- 'title desc' => 'books.sort desc',
- 'author asc', 'author' => 'books.author_sort asc',
- 'author desc' => 'books.author_sort desc',
- 'pubdate desc', 'pubdate' => 'books.pubdate desc',
- 'pubdate asc' => 'books.pubdate asc',
- 'rating desc', 'rating' => 'ratings.rating desc',
- 'rating asc' => 'ratings.rating asc',
- 'timestamp desc', 'timestamp' => 'books.timestamp desc',
- 'timestamp asc' => 'books.timestamp asc',
- 'count desc', 'count' => 'count desc',
- 'count asc' => 'count asc',
- default => $this->orderBy,
- };
- }
-
- /**
- * Summary of getBookCount
- * @return int
- */
- public function getBookCount()
- {
- if ($this->request->hasFilter()) {
- return $this->getFilterBookCount();
- }
- return Database::querySingle('select count(*) from books', $this->databaseId);
- }
-
- /**
- * Summary of getFilterBookCount
- * @return int
- */
- public function getFilterBookCount()
- {
- $filter = new Filter($this->request, [], "books", $this->databaseId);
- $filterString = $filter->getFilterString();
- $params = $filter->getQueryParams();
- return Database::countFilter(static::SQL_BOOKS_ALL, 'count(*)', $filterString, $params, $this->databaseId);
- }
-
- /**
- * Summary of getCount
- * @return array
- */
- public function getCount()
- {
- $nBooks = $this->getBookCount();
- $result = [];
- $params = $this->request->getFilterParams();
- $params["db"] ??= $this->databaseId;
- $href = Route::link($this->handler, Book::PAGE_ALL, $params);
- // issue #26 for koreader: section is not supported
- if (!empty(Config::get('titles_split_first_letter'))) {
- $linkArray = [ new LinkNavigation($href, "subsection") ];
- } elseif (!empty(Config::get('titles_split_publication_year'))) {
- $linkArray = [ new LinkNavigation($href, "subsection") ];
- } else {
- $linkArray = [ new LinkFeed($href, null) ];
- }
- $entry = new Entry(
- localize('allbooks.title'),
- Book::PAGE_ID,
- str_format(localize('allbooks.alphabetical', $nBooks), $nBooks),
- 'text',
- $linkArray,
- $this->databaseId,
- '',
- $nBooks
- );
- array_push($result, $entry);
- if (Config::get('recentbooks_limit') > 0) {
- $href = Route::link($this->handler, PageId::ALL_RECENT_BOOKS, $params);
- $count = ($nBooks > Config::get('recentbooks_limit')) ? Config::get('recentbooks_limit') : $nBooks;
- $entry = new Entry(
- localize('recent.title'),
- PageId::ALL_RECENT_BOOKS_ID,
- str_format(localize('recent.list'), $count),
- 'text',
- [ new LinkFeed($href, 'http://opds-spec.org/sort/new')],
- $this->databaseId,
- '',
- $count
- );
- array_push($result, $entry);
- }
- return $result;
- }
-
- /**
- * Summary of getBooksByInstance
- * @param Base|Author|Identifier|Language|Publisher|Rating|Serie|Tag|CustomColumn $instance
- * @param int $n
- * @return array{0: EntryBook[], 1: integer}
- */
- public function getBooksByInstance($instance, $n)
- {
- if (empty($instance->id) && in_array($instance::class, [Rating::class, Serie::class, Tag::class, Identifier::class])) {
- return $this->getBooksWithoutInstance($instance, $n);
- }
- [$query, $params] = $instance->getQuery();
- return $this->getEntryArray($query, $params, $n);
- }
-
- /**
- * Summary of getBooksByInstanceOrChildren
- * @param Category $instance
- * @param int $n
- * @return array{0: EntryBook[], 1: integer}
- */
- public function getBooksByInstanceOrChildren($instance, $n)
- {
- [$query, $params] = $instance->getQuery();
- $children = $instance->getChildCategories();
- if (!empty($children)) {
- $childIds = [];
- foreach ($children as $child) {
- array_push($childIds, $child->id);
- }
- $params = array_merge($params, $childIds);
- $query = str_replace(' = ? ', ' IN (' . str_repeat('?,', count($params) - 1) . '?)', $query);
- // use distinct here in case books belong to several child categories
- $query = str_ireplace('select ', 'select distinct ', $query);
- }
- return $this->getEntryArray($query, $params, $n);
- }
-
- /**
- * Summary of getBooksWithoutInstance
- * @param mixed $instance
- * @param int $n
- * @return array{0: EntryBook[], 1: integer}
- */
- public function getBooksWithoutInstance($instance, $n)
- {
- // in_array("series", Config::get('show_not_set_filter'))
- if ($instance instanceof CustomColumn) {
- return $this->getBooksWithoutCustom($instance->customColumnType, $n);
- }
- return $this->getEntryArray($instance::SQL_BOOKLIST_NULL, [], $n);
- }
-
- /**
- * Summary of getBooksByCustomYear
- * @param CustomColumnTypeDate $columnType
- * @param string|int $year
- * @param int $n
- * @return array{0: EntryBook[], 1: integer}
- */
- public function getBooksByCustomYear($columnType, $year, $n)
- {
- [$query, $params] = $columnType->getQueryByYear($year);
-
- return $this->getEntryArray($query, $params, $n);
- }
-
- /**
- * Summary of getBooksByCustomRange
- * @param CustomColumnTypeInteger $columnType
- * @param string $range
- * @param int $n
- * @return array{0: EntryBook[], 1: integer}
- */
- public function getBooksByCustomRange($columnType, $range, $n)
- {
- [$query, $params] = $columnType->getQueryByRange($range);
-
- return $this->getEntryArray($query, $params, $n);
- }
-
- /**
- * Summary of getBooksWithoutCustom
- * @param CustomColumnType $columnType
- * @param int $n
- * @return array{0: EntryBook[], 1: integer}
- */
- public function getBooksWithoutCustom($columnType, $n)
- {
- // use null here to reduce conflict with bool and int custom columns
- [$query, $params] = $columnType->getQuery(null);
- return $this->getEntryArray($query, $params, $n);
- }
-
- /**
- * Summary of getBooksByQueryScope
- * @param array $queryScope
- * @param int $n
- * @param array $ignoredCategories
- * @return array{0: EntryBook[], 1: integer}
- */
- public function getBooksByQueryScope($queryScope, $n, $ignoredCategories = [])
- {
- $i = 0;
- $critArray = [];
- foreach ([PageQueryResult::SCOPE_AUTHOR,
- PageQueryResult::SCOPE_TAG,
- PageQueryResult::SCOPE_SERIES,
- PageQueryResult::SCOPE_PUBLISHER,
- PageQueryResult::SCOPE_BOOK] as $key) {
- if (in_array($key, $ignoredCategories) ||
- (!array_key_exists($key, $queryScope) && !array_key_exists('all', $queryScope))) {
- $critArray[$i] = static::BAD_SEARCH;
- } else {
- if (array_key_exists($key, $queryScope)) {
- $critArray[$i] = $queryScope[$key];
- } else {
- $critArray[$i] = $queryScope["all"];
- }
- }
- $i++;
- }
- return $this->getEntryArray(static::SQL_BOOKS_QUERY, $critArray, $n);
- }
-
- /**
- * Summary of getAllBooks
- * @param int $n
- * @return array{0: EntryBook[], 1: integer}
- */
- public function getAllBooks($n = 1)
- {
- [$entryArray, $totalNumber] = $this->getEntryArray(static::SQL_BOOKS_ALL, [], $n);
- return [$entryArray, $totalNumber];
- }
-
- /**
- * Summary of getCountByFirstLetter
- * @return array
- */
- public function getCountByFirstLetter()
- {
- return $this->getCountByGroup('substr(upper(books.sort), 1, 1)', Book::PAGE_LETTER, 'letter');
- }
-
- /**
- * Summary of getCountByPubYear
- * @return array
- */
- public function getCountByPubYear()
- {
- return $this->getCountByGroup('substr(date(books.pubdate), 1, 4)', Book::PAGE_YEAR, 'year');
- }
-
- /**
- * Summary of getCountByGroup
- * @param string $groupField
- * @param string $page
- * @param string $label
- * @return array
- */
- public function getCountByGroup($groupField, $page, $label)
- {
- $filter = new Filter($this->request, [], "books", $this->databaseId);
- $filterString = $filter->getFilterString();
- $params = $filter->getQueryParams();
-
- // check orderBy to sort by count
- if (!in_array($this->orderBy, ['groupid', 'count'])) {
- $this->orderBy = 'groupid';
- }
- $sortBy = $this->getOrderBy();
- $result = Database::queryFilter('select {0}
-from books
-where 1=1 {1}
-group by groupid
-order by ' . $sortBy, $groupField . ' as groupid, count(*) as count', $filterString, $params, -1, $this->databaseId);
-
- $entryArray = [];
- while ($post = $result->fetchObject()) {
- $params = ['id' => $post->groupid, 'db' => $this->databaseId];
- $href = Route::link($this->handler, $page, $params);
- array_push($entryArray, new Entry(
- $post->groupid,
- Book::PAGE_ID . ':' . $label . ':' . $post->groupid,
- str_format(localize('bookword', $post->count), $post->count),
- 'text',
- [ new LinkFeed($href, "subsection") ],
- $this->databaseId,
- ucfirst($label),
- $post->count
- ));
- }
- return $entryArray;
- }
-
- /**
- * Summary of getBooksByFirstLetter
- * @param string $letter
- * @param int $n
- * @return array{0: EntryBook[], 1: integer}
- */
- public function getBooksByFirstLetter($letter, $n)
- {
- return $this->getEntryArray(static::SQL_BOOKS_BY_FIRST_LETTER, [$letter . '%'], $n);
- }
-
- /**
- * Summary of getBooksByPubYear
- * @param string|int $year
- * @param int $n
- * @return array{0: EntryBook[], 1: integer}
- */
- public function getBooksByPubYear($year, $n)
- {
- return $this->getEntryArray(static::SQL_BOOKS_BY_PUB_YEAR, [$year], $n);
- }
-
- /**
- * Summary of getAllRecentBooks
- * @return array
- */
- public function getAllRecentBooks()
- {
- [$entryArray, ] = $this->getEntryArray(static::SQL_BOOKS_RECENT . Config::get('recentbooks_limit'), [], -1);
- return $entryArray;
- }
-
- /**
- * Summary of getEntryArray
- * @param string $query
- * @param array $params
- * @param int $n
- * @return array{0: EntryBook[], 1: integer}
- */
- public function getEntryArray($query, $params, $n)
- {
- $filter = new Filter($this->request, $params, "books", $this->databaseId);
- $filterString = $filter->getFilterString();
- $params = $filter->getQueryParams();
-
- if (isset($this->orderBy) && $this->orderBy !== Book::SQL_SORT) {
- if (str_contains($query, 'order by')) {
- $query = preg_replace('/\s+order\s+by\s+[\w.]+(\s+(asc|desc)|)\s*/i', ' order by ' . $this->getOrderBy() . ' ', $query);
- } else {
- $query .= ' order by ' . $this->getOrderBy() . ' ';
- }
- }
-
- /** @var integer $totalNumber */
- /** @var \PDOStatement $result */
- [$totalNumber, $result] = Database::queryTotal($query, Book::getBookColumns(), $filterString, $params, $n, $this->databaseId, $this->numberPerPage);
-
- if (static::BATCH_QUERY) {
- return $this->batchQuery($totalNumber, $result);
- }
- $entryArray = [];
- while ($post = $result->fetchObject()) {
- $book = new Book($post, $this->databaseId);
- $book->setHandler($this->handler);
- array_push($entryArray, $book->getEntry());
- }
- return [$entryArray, $totalNumber];
- }
-
- /**
- * Summary of batchQuery
- * @param int $totalNumber
- * @param \PDOStatement $result
- * @throws \Exception
- * @return array{0: EntryBook[], 1: integer}
- */
- public function batchQuery($totalNumber, $result)
- {
- $this->bookList = [];
- while ($post = $result->fetchObject()) {
- $book = new Book($post, $this->databaseId);
- $book->setHandler($this->handler);
- $this->bookList[$book->id] = $book;
- }
- $entryArray = [];
- if (count($this->bookList) < 1) {
- return [$entryArray, $totalNumber];
- }
- $this->setAuthors();
- $this->setSerie();
- $this->setPublisher();
- $this->setTags();
- $this->setLanguages();
- $this->setDatas();
- foreach ($this->bookList as $bookId => $book) {
- array_push($entryArray, $book->getEntry());
- }
- $this->bookList = [];
- return [$entryArray, $totalNumber];
- }
-
- /**
- * Summary of setAuthors
- * @throws \Exception
- * @return void
- */
- public function setAuthors()
- {
- $bookIds = array_keys($this->bookList);
- $baselist = new BaseList(Author::class, $this->request, $this->databaseId);
- $authorIds = $baselist->getInstanceIdsByBookIds($bookIds);
- $authors = $baselist->getInstancesByIds($authorIds);
- foreach ($bookIds as $bookId) {
- $this->bookList[$bookId]->authors = [];
- $authorIds[$bookId] ??= [];
- foreach ($authorIds[$bookId] as $authorId) {
- if (!array_key_exists($authorId, $authors)) {
- throw new Exception('Unknown author ' . $authorId . ' in ' . var_export($authors, true));
- }
- array_push($this->bookList[$bookId]->authors, $authors[$authorId]);
- }
- }
- }
-
- /**
- * Summary of setSerie
- * @throws \Exception
- * @return void
- */
- public function setSerie()
- {
- $bookIds = array_keys($this->bookList);
- $baselist = new BaseList(Serie::class, $this->request, $this->databaseId);
- $seriesIds = $baselist->getInstanceIdsByBookIds($bookIds);
- $series = $baselist->getInstancesByIds($seriesIds);
- foreach ($bookIds as $bookId) {
- $this->bookList[$bookId]->serie = false;
- $seriesIds[$bookId] ??= [];
- foreach ($seriesIds[$bookId] as $seriesId) {
- if (!array_key_exists($seriesId, $series)) {
- throw new Exception('Unknown series ' . $seriesId . ' in ' . var_export($series, true));
- }
- $this->bookList[$bookId]->serie = $series[$seriesId];
- break;
- }
- }
- }
-
- /**
- * Summary of setPublisher
- * @throws \Exception
- * @return void
- */
- public function setPublisher()
- {
- $bookIds = array_keys($this->bookList);
- $baselist = new BaseList(Publisher::class, $this->request, $this->databaseId);
- $publisherIds = $baselist->getInstanceIdsByBookIds($bookIds);
- $publishers = $baselist->getInstancesByIds($publisherIds);
- foreach ($bookIds as $bookId) {
- $this->bookList[$bookId]->publisher = false;
- $publisherIds[$bookId] ??= [];
- foreach ($publisherIds[$bookId] as $publisherId) {
- if (!array_key_exists($publisherId, $publishers)) {
- throw new Exception('Unknown publisher ' . $publisherId . ' in ' . var_export($publishers, true));
- }
- $this->bookList[$bookId]->publisher = $publishers[$publisherId];
- break;
- }
- }
- }
-
- /**
- * Summary of setTags
- * @throws \Exception
- * @return void
- */
- public function setTags()
- {
- $bookIds = array_keys($this->bookList);
- $baselist = new BaseList(Tag::class, $this->request, $this->databaseId);
- $tagIds = $baselist->getInstanceIdsByBookIds($bookIds);
- $tags = $baselist->getInstancesByIds($tagIds);
- foreach ($bookIds as $bookId) {
- $this->bookList[$bookId]->tags = [];
- $tagIds[$bookId] ??= [];
- foreach ($tagIds[$bookId] as $tagId) {
- if (!array_key_exists($tagId, $tags)) {
- throw new Exception('Unknown tag ' . $tagId . ' in ' . var_export($tags, true));
- }
- array_push($this->bookList[$bookId]->tags, $tags[$tagId]);
- }
- }
- }
-
- /**
- * Summary of setLanguages
- * @throws \Exception
- * @return void
- */
- public function setLanguages()
- {
- $bookIds = array_keys($this->bookList);
- $baselist = new BaseList(Language::class, $this->request, $this->databaseId);
- $languageIds = $baselist->getInstanceIdsByBookIds($bookIds);
- $languages = $baselist->getInstancesByIds($languageIds);
- foreach ($bookIds as $bookId) {
- $langCodes = [];
- $languageIds[$bookId] ??= [];
- foreach ($languageIds[$bookId] as $languageId) {
- if (!array_key_exists($languageId, $languages)) {
- throw new Exception('Unknown language ' . $languageId . ' in ' . var_export($languages, true));
- }
- array_push($langCodes, $languages[$languageId]->getTitle());
- }
- $this->bookList[$bookId]->languages = implode(', ', $langCodes);
- }
- }
-
- /**
- * Summary of setDatas
- * @throws \Exception
- * @return void
- */
- public function setDatas()
- {
- $bookIds = array_keys($this->bookList);
- $baselist = new BaseList(Data::class, $this->request, $this->databaseId);
- $dataIds = $baselist->getInstanceIdsByBookIds($bookIds);
- $datas = $baselist->getInstancesByIds($dataIds);
- $ignored_formats = Config::get('ignored_formats');
- foreach ($bookIds as $bookId) {
- $this->bookList[$bookId]->datas = [];
- $dataIds[$bookId] ??= [];
- foreach ($dataIds[$bookId] as $dataId) {
- if (!array_key_exists($dataId, $datas)) {
- throw new Exception('Unknown data ' . $dataId . ' in ' . var_export($datas, true));
- }
- if (!empty($ignored_formats) && in_array($datas[$dataId]->format, $ignored_formats)) {
- continue;
- }
- // we need to set the book here, since we didn't do it above
- $datas[$dataId]->setBook($this->bookList[$bookId]);
- array_push($this->bookList[$bookId]->datas, $datas[$dataId]);
- }
- }
- }
-}
diff --git a/COPS/cops-3.1.3/src/Calibre/Category.php b/COPS/cops-3.1.3/src/Calibre/Category.php
deleted file mode 100644
index 49a81606..00000000
--- a/COPS/cops-3.1.3/src/Calibre/Category.php
+++ /dev/null
@@ -1,181 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops\Calibre;
-
-use SebLucas\Cops\Input\Config;
-use SebLucas\Cops\Model\Entry;
-
-abstract class Category extends Base
-{
- public const CATEGORY = "categories";
- /** @var ?int */
- public $count;
- /** @var ?array */
- protected $children = null;
- /** @var Category|false|null */
- protected $parent = null;
-
- /**
- * Summary of hasChildCategories
- * @return bool
- */
- public function hasChildCategories()
- {
- if (empty(Config::get('calibre_categories_using_hierarchy')) || !in_array(static::CATEGORY, Config::get('calibre_categories_using_hierarchy'))) {
- return false;
- }
- return true;
- }
-
- /**
- * Get child instances for hierarchical tags or custom columns
- * @return array
- */
- public function getChildCategories()
- {
- if (!is_null($this->children)) {
- return $this->children;
- }
- // Fiction -> Fiction.% matching Fiction.Historical and Fiction.Romance
- $find = $this->getTitle() . '.%';
- $this->children = $this->getRelatedCategories($find);
- return $this->children;
- }
-
- /**
- * Get child entries for hierarchical tags or custom columns
- * @param int|bool|null $expand include all child categories at all levels or only direct children
- * @return array
- */
- public function getChildEntries($expand = false)
- {
- $entryArray = [];
- foreach ($this->getChildCategories() as $child) {
- // check if this is an immediate child or not, like Fiction matches Fiction.Historical but not Fiction.Historical.Romance
- if (empty($expand) && !preg_match('/^' . $this->getTitle() . '\.[^.]+$/', $child->getTitle())) {
- continue;
- }
- array_push($entryArray, $child->getEntry($child->count));
- }
- return $entryArray;
- }
-
- /**
- * Get sibling entries for hierarchical tags or custom columns
- * @return array
- */
- public function getSiblingEntries()
- {
- // Fiction.Historical -> Fiction.% matching Fiction.Historical and Fiction.Romance
- $parentName = static::findParentName($this->getTitle());
- if (empty($parentName)) {
- return [];
- }
- // pattern match here
- $find = $parentName . '.%';
- $siblings = $this->getRelatedCategories($find);
- $entryArray = [];
- foreach ($siblings as $sibling) {
- array_push($entryArray, $sibling->getEntry($sibling->count));
- }
- return $entryArray;
- }
-
- /**
- * Find parent name of hierarchical name
- * @param string $name
- * @return string
- */
- protected static function findParentName($name)
- {
- $parts = explode('.', $name);
- $child = array_pop($parts);
- if (empty($parts)) {
- return '';
- }
- $parent = implode('.', $parts);
- return $parent;
- }
-
- /**
- * Get parent instance for hierarchical tags or custom columns
- * @return Category|false
- */
- public function getParentCategory()
- {
- if (!is_null($this->parent)) {
- return $this->parent;
- }
- $this->parent = false;
- // Fiction.Historical -> Fiction
- $parentName = static::findParentName($this->getTitle());
- if (empty($parentName)) {
- return $this->parent;
- }
- // exact match here
- $find = $parentName;
- $parents = $this->getRelatedCategories($find);
- if (count($parents) == 1) {
- $this->parent = $parents[0];
- }
- return $this->parent;
- }
-
- /**
- * Get parent entry for hierarchical tags or custom columns
- * @return ?Entry
- */
- public function getParentEntry()
- {
- $parent = $this->getParentCategory();
- if (!empty($parent)) {
- return $parent->getEntry($parent->count);
- }
- return null;
- }
-
- /**
- * Find related categories for hierarchical tags or series - @todo needs title_sort function in sqlite for series
- * Format: tag_browser_tags(id,name,count,avg_rating,sort)
- * @param string|array $find pattern match or exact match for name, or array of child ids
- * @return array
- */
- public function getRelatedCategories($find)
- {
- if (!$this->hasChildCategories()) {
- return [];
- }
- $className = static::class;
- $tableName = 'tag_browser_' . static::CATEGORY;
- if (is_array($find)) {
- $queryFormat = "SELECT id, name, count FROM {0} WHERE id IN (" . str_repeat("?,", count($find) - 1) . "?) ORDER BY sort";
- $params = $find;
- } elseif (!str_contains($find, '%')) {
- $queryFormat = "SELECT id, name, count FROM {0} WHERE name = ? ORDER BY sort";
- $params = [$find];
- } else {
- $queryFormat = "SELECT id, name, count FROM {0} WHERE name LIKE ? ORDER BY sort";
- $params = [$find];
- }
- $query = str_format($queryFormat, $tableName);
- $result = Database::query($query, $params, $this->databaseId);
-
- $instances = [];
- while ($post = $result->fetchObject()) {
- /** @var Category $instance */
- $instance = new $className($post, $this->databaseId);
- $instance->count = $post->count;
- array_push($instances, $instance);
- }
- return $instances;
- }
-}
diff --git a/COPS/cops-3.1.3/src/Calibre/Cover.php b/COPS/cops-3.1.3/src/Calibre/Cover.php
deleted file mode 100644
index c438a055..00000000
--- a/COPS/cops-3.1.3/src/Calibre/Cover.php
+++ /dev/null
@@ -1,437 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops\Calibre;
-
-use SebLucas\Cops\Input\Config;
-use SebLucas\Cops\Input\Request;
-use SebLucas\Cops\Input\Route;
-use SebLucas\Cops\Model\LinkEntry;
-use SebLucas\Cops\Output\FileResponse;
-
-class Cover
-{
- public static string $handler = "fetch";
- /** @var Book */
- public $book;
- /** @var ?int */
- protected $databaseId;
- /** @var ?string */
- public $coverFileName = null;
- /** @var ?FileResponse */
- protected $response = null;
-
- /**
- * Summary of __construct
- * @param Book $book
- * @param ?int $database
- */
- public function __construct($book, $database = null)
- {
- $this->book = $book;
- if ($book->hasCover) {
- $this->coverFileName = $book->getCoverFileName();
- }
- $this->databaseId = $database ?? $book->getDatabaseId();
- }
-
- /**
- * Summary of checkDatabaseFieldCover
- * @param string $fileName
- * @return ?string
- */
- public function checkDatabaseFieldCover($fileName)
- {
- $imgDirectory = Database::getImgDirectory($this->databaseId);
- $this->coverFileName = $fileName;
- if (!file_exists($this->coverFileName)) {
- $this->coverFileName = null;
- }
- if (empty($this->coverFileName)) {
- $this->coverFileName = sprintf('%s%s', $imgDirectory, $fileName);
- if (!file_exists($this->coverFileName)) {
- $this->coverFileName = null;
- }
- }
- if (empty($this->coverFileName)) {
- // Try with the epub file name
- $data = $this->book->getDataFormat('EPUB');
- if ($data) {
- // this won't work for Calibre directories due to missing (book->id) in path here
- $this->coverFileName = sprintf('%s%s/%s', $imgDirectory, $data->name, $fileName);
- if (!file_exists($this->coverFileName)) {
- $this->coverFileName = null;
- }
- if (empty($this->coverFileName)) {
- $this->coverFileName = sprintf('%s%s.jpg', $imgDirectory, $data->name);
- if (!file_exists($this->coverFileName)) {
- $this->coverFileName = null;
- }
- }
- }
- }
- return $this->coverFileName;
- }
-
- /**
- * Summary of checkCoverFilePath
- * @return ?string
- */
- public function checkCoverFilePath()
- {
- $cover = $this->book->getCoverFilePath("jpg");
- if (!empty($cover) && !empty(Config::get('calibre_external_storage'))) {
- $this->coverFileName = $cover;
- return $this->coverFileName;
- }
- if ($cover === false || !file_exists($cover)) {
- $cover = $this->book->getCoverFilePath("png");
- }
- if ($cover === false || !file_exists($cover)) {
- $this->coverFileName = null;
- } else {
- $this->coverFileName = $cover;
- }
- return $this->coverFileName;
- }
-
- /**
- * Summary of getThumbnailCachePath
- * @param ?int $width
- * @param ?int $height
- * @param string $type
- * @return ?string
- */
- public function getThumbnailCachePath($width, $height, $type = 'jpg')
- {
- // moved some of the thumbnail cache from fetch.php to Cover
- //by default, we don't cache
- $cachePath = null;
- if (empty(Config::get('thumbnail_cache_directory'))) {
- return $cachePath;
- }
-
- $uuid = $this->book->uuid;
- $database = $this->databaseId;
-
- $cachePath = Config::get('thumbnail_cache_directory');
- //if multiple databases, add a subfolder with the database ID
- $cachePath .= !is_null($database) ? 'db-' . $database . DIRECTORY_SEPARATOR : '';
- //when there are lots of thumbnails, it's better to save files in subfolders, so if the book's uuid is
- //"01234567-89ab-cdef-0123-456789abcdef", we will save the thumbnail in .../0/12/34567-89ab-cdef-0123-456789abcdef-...
- $cachePath .= substr($uuid, 0, 1) . DIRECTORY_SEPARATOR . substr($uuid, 1, 2) . DIRECTORY_SEPARATOR;
- //check if cache folder exists or create it
- if (file_exists($cachePath) || mkdir($cachePath, 0o700, true)) {
- //we name the thumbnail from the book's uuid and it's dimensions (width and/or height)
- $thumbnailCacheName = substr($uuid, 3) . '-' . strval($width) . 'x' . strval($height) . '.' . $type;
- $cachePath = $cachePath . $thumbnailCacheName;
- } else {
- //error creating the folder, so we don't cache
- $cachePath = null;
- }
-
- return $cachePath;
- }
-
- /**
- * Summary of getThumbnail
- * @param ?int $width
- * @param ?int $height
- * @param ?string $outputfile
- * @param string $inType
- * @return bool
- */
- public function getThumbnail($width, $height, $outputfile = null, $inType = 'jpg')
- {
- if (is_null($width) && is_null($height)) {
- return false;
- }
- if (empty($this->coverFileName) || !is_file($this->coverFileName) || !is_readable($this->coverFileName)) {
- return false;
- }
- // @todo support creating (and caching) thumbnails for external cover images someday
-
- // -DC- Use cover file name
- //$file = $this->getCoverFilePath('jpg');
- $file = $this->coverFileName;
- // get image size
- if ($size = GetImageSize($file)) {
- $w = $size[0];
- $h = $size[1];
- //set new size
- if (!is_null($width)) {
- $nw = $width;
- if ($nw >= $w) {
- return false;
- }
- $nh = intval(($nw * $h) / $w);
- } else {
- $nh = $height;
- if ($nh >= $h) {
- return false;
- }
- $nw = intval(($nh * $w) / $h);
- }
- } else {
- return false;
- }
-
- // Draw the image
- if ($inType == 'png') {
- $src_img = imagecreatefrompng($file);
- } else {
- $src_img = imagecreatefromjpeg($file);
- }
- $dst_img = imagecreatetruecolor($nw, $nh);
- if (!imagecopyresampled($dst_img, $src_img, 0, 0, 0, 0, $nw, $nh, $w, $h)) {
- return false;
- }
- //if we don't cache the thumbnail, this already returns the image data
- if (is_null($outputfile)) {
- $mimetype = ($inType == 'png') ? 'image/png' : 'image/jpeg';
- // use cache control here
- $this->response->setHeaders($mimetype, 0);
- $this->response->sendHeaders();
- }
- if ($inType == 'png') {
- if (!imagepng($dst_img, $outputfile, 9)) {
- return false;
- }
- } else {
- if (!imagejpeg($dst_img, $outputfile, 80)) {
- return false;
- }
- }
- imagedestroy($src_img);
- imagedestroy($dst_img);
-
- return true;
- }
-
- /**
- * Summary of sendThumbnail
- * @param Request $request
- * @param FileResponse $response
- * @return FileResponse
- */
- public function sendThumbnail($request, $response)
- {
- $type = $request->get('type', 'jpg');
- $width = $request->get('width');
- $height = $request->get('height');
- $thumb = $request->get('thumb');
- if (!empty($thumb) && empty($height)) {
- $height = match ($thumb) {
- "opds2" => intval(Config::get('opds_thumbnail_height')) * 2,
- "opds" => intval(Config::get('opds_thumbnail_height')),
- "html2" => intval(Config::get('html_thumbnail_height')) * 2,
- "html" => intval(Config::get('html_thumbnail_height')),
- default => intval($thumb),
- };
- }
- $mime = ($type == 'jpg') ? 'image/jpeg' : 'image/png';
- $file = $this->coverFileName;
-
- $cachePath = $this->getThumbnailCachePath($width, $height, $type);
-
- if ($cachePath !== null && file_exists($cachePath)) {
- //return the already cached thumbnail
- $response->setHeaders($mime, 0);
- return $response->sendFile($cachePath, true);
- }
-
- $this->response = $response;
- if ($this->getThumbnail($width, $height, $cachePath, $type)) {
- //if we don't cache the thumbnail, imagejpeg() in $cover->getThumbnail() already return the image data
- if ($cachePath === null) {
- // The cover had to be resized
- return $this->response;
- }
- //return the just cached thumbnail
- $response->setHeaders($mime, 0);
- return $response->sendFile($cachePath, true);
- }
-
- $response->setHeaders($mime, 0);
- return $response->sendFile($file);
- }
-
- /**
- * Summary of getCoverUri
- * @return ?string
- */
- public function getCoverUri()
- {
- $link = $this->getCoverLink();
- if ($link) {
- return $link->hrefXhtml();
- }
- return null;
- }
-
- /**
- * Summary of getCoverLink
- * @return ?LinkEntry
- */
- public function getCoverLink()
- {
- if ($this->coverFileName) {
- // -DC- Use cover file name
- //array_push($linkArray, Data::getLink($this, 'jpg', 'image/jpeg', LinkEntry::OPDS_IMAGE_TYPE, 'cover.jpg', NULL));
- $ext = strtolower(pathinfo($this->coverFileName, PATHINFO_EXTENSION));
- $mime = ($ext == 'jpg') ? 'image/jpeg' : 'image/png';
- if (!empty(Config::get('calibre_external_storage')) && str_starts_with($this->coverFileName, (string) Config::get('calibre_external_storage'))) {
- return new LinkEntry(
- $this->coverFileName,
- $mime,
- LinkEntry::OPDS_IMAGE_TYPE
- );
- }
- $file = 'cover.' . $ext;
- // moved image-specific code from Data to Cover
- if (!Database::useAbsolutePath($this->databaseId)) {
- return new LinkEntry(
- Route::path(str_replace('%2F', '/', rawurlencode($this->book->path . "/" . $file))),
- $mime,
- LinkEntry::OPDS_IMAGE_TYPE
- );
- }
- $params = ['id' => $this->book->id, 'db' => $this->databaseId];
- if (Config::get('use_route_urls') && is_null($params['db'])) {
- $params['db'] = 0;
- }
- if ($ext != 'jpg') {
- $params['type'] = $ext;
- }
- return new LinkEntry(
- Route::link(static::$handler, null, $params),
- $mime,
- LinkEntry::OPDS_IMAGE_TYPE
- );
- }
-
- return null;
- }
-
- /**
- * Summary of getThumbnailUri
- * @param string $thumb
- * @param bool $useDefault
- * @return ?string
- */
- public function getThumbnailUri($thumb, $useDefault = true)
- {
- $link = $this->getThumbnailLink($thumb, $useDefault);
- if ($link) {
- return $link->hrefXhtml();
- }
- return null;
- }
-
- /**
- * Summary of getThumbnailLink
- * @param string $thumb
- * @param bool $useDefault
- * @return ?LinkEntry
- */
- public function getThumbnailLink($thumb, $useDefault = true)
- {
- if (Config::get('thumbnail_handling') != "1" &&
- !empty(Config::get('thumbnail_handling'))) {
- $fileName = Config::get('thumbnail_handling');
- $ext = strtolower(pathinfo((string) $fileName, PATHINFO_EXTENSION));
- $mime = ($ext == 'jpg') ? 'image/jpeg' : 'image/png';
- return new LinkEntry(
- Route::path($fileName),
- $mime,
- LinkEntry::OPDS_THUMBNAIL_TYPE
- );
- }
-
- if ($this->coverFileName) {
- // -DC- Use cover file name
- //array_push($linkArray, Data::getLink($this, 'jpg', 'image/jpeg', LinkEntry::OPDS_THUMBNAIL_TYPE, 'cover.jpg', NULL));
- $ext = strtolower(pathinfo($this->coverFileName, PATHINFO_EXTENSION));
- $mime = ($ext == 'jpg') ? 'image/jpeg' : 'image/png';
- // @todo support creating (and caching) thumbnails for external cover images someday
- if (!empty(Config::get('calibre_external_storage')) && str_starts_with($this->coverFileName, (string) Config::get('calibre_external_storage'))) {
- return new LinkEntry(
- $this->coverFileName,
- $mime,
- LinkEntry::OPDS_THUMBNAIL_TYPE
- );
- }
- //$file = 'cover.' . $ext;
- // moved image-specific code from Data to Cover
- $params = ['id' => $this->book->id, 'db' => $this->databaseId];
- if (Config::get('use_route_urls') && is_null($params['db'])) {
- $params['db'] = 0;
- }
- if ($ext != 'jpg') {
- $params['type'] = $ext;
- }
- if (Config::get('thumbnail_handling') != "1") {
- $params['thumb'] = $thumb;
- }
- return new LinkEntry(
- Route::link(static::$handler, null, $params),
- $mime,
- LinkEntry::OPDS_THUMBNAIL_TYPE
- );
- }
-
- if ($useDefault) {
- return $this->getDefaultLink();
- }
- return null;
- }
-
- /**
- * Summary of getDefaultLink
- * @return ?LinkEntry
- */
- public function getDefaultLink()
- {
- if (!empty(Config::get('thumbnail_default'))) {
- $ext = strtolower(pathinfo((string) Config::get('thumbnail_default'), PATHINFO_EXTENSION));
- $mime = ($ext == 'jpg') ? 'image/jpeg' : 'image/png';
- return new LinkEntry(
- Route::path(Config::get('thumbnail_default')),
- $mime,
- LinkEntry::OPDS_THUMBNAIL_TYPE
- );
- }
- return null;
- }
-
- /**
- * Summary of findCoverFileName
- * @param Book $book
- * @param object $line
- * @return ?string
- */
- public static function findCoverFileName($book, $line)
- {
- // -DC- Use cover file name
- //if (!file_exists($this->getCoverFilePath('jpg'))) {
- // // double check
- // $this->hasCover = 0;
- //}
- $coverFileName = null;
- $cover = new Cover($book);
- if (!empty(Config::get('calibre_database_field_cover'))) {
- $coverFileName = $cover->checkDatabaseFieldCover($line->cover);
- }
- // Else try with default cover file name
- if (empty($coverFileName)) {
- $coverFileName = $cover->checkCoverFilePath();
- }
- return $coverFileName;
- }
-}
diff --git a/COPS/cops-3.1.3/src/Calibre/CustomColumn.php b/COPS/cops-3.1.3/src/Calibre/CustomColumn.php
deleted file mode 100644
index dc5945a8..00000000
--- a/COPS/cops-3.1.3/src/Calibre/CustomColumn.php
+++ /dev/null
@@ -1,216 +0,0 @@
-
- */
-
-namespace SebLucas\Cops\Calibre;
-
-use SebLucas\Cops\Input\Config;
-use SebLucas\Cops\Input\Route;
-use SebLucas\Cops\Model\Entry;
-use SebLucas\Cops\Pages\PageId;
-
-/**
- * A CustomColumn with an value
- */
-class CustomColumn extends Category
-{
- public const PAGE_ID = PageId::ALL_CUSTOMS_ID;
- public const PAGE_ALL = PageId::ALL_CUSTOMS;
- public const PAGE_DETAIL = PageId::CUSTOM_DETAIL;
- public const URL_PARAM = "c";
-
- /** @var string the (string) representation of the value */
- public $value;
- /** @var CustomColumnType the custom column that contains the value */
- public $customColumnType;
- /** @var string the value encoded for HTML displaying */
- public $htmlvalue;
-
- /**
- * CustomColumn constructor.
- *
- * @param integer|string|null $id id of the chosen value
- * @param string $value string representation of the value
- * @param CustomColumnType $customColumnType the CustomColumn this value lives in
- */
- public function __construct($id, $value, $customColumnType)
- {
- $this->id = $id;
- $this->value = $value;
- $this->customColumnType = $customColumnType;
- $this->htmlvalue = $this->customColumnType->encodeHTMLValue($this->value ?? '');
- $this->databaseId = $this->customColumnType->getDatabaseId();
- $this->handler = $this->customColumnType->getHandler();
- }
-
- /**
- * Summary of getCustomId
- * @return int
- */
- public function getCustomId()
- {
- return $this->customColumnType->customId;
- }
-
- /**
- * Get the URI to show all books with this value
- *
- * @param array $params
- * @return string
- */
- public function getUri($params = [])
- {
- $params['custom'] = $this->getCustomId();
- $params['id'] = $this->id;
- // we need databaseId here because we use Route::link with $handler
- $params['db'] = $this->getDatabaseId();
- return Route::link($this->handler, static::PAGE_DETAIL, $params);
- }
-
- /**
- * Summary of getParentUri
- * @param array $params
- * @return string
- */
- public function getParentUri($params = [])
- {
- return $this->customColumnType->getUri($params);
- }
-
- /**
- * Get the EntryID to show all books with this value
- *
- * @return string
- */
- public function getEntryId()
- {
- return static::PAGE_ID . ":" . strval($this->getCustomId()) . ":" . $this->id;
- }
-
- /**
- * Summary of getTitle
- * @return string
- */
- public function getTitle()
- {
- return strval($this->value);
- }
-
- /**
- * Summary of getParentTitle
- * @return string
- */
- public function getParentTitle()
- {
- return $this->customColumnType->getTitle();
- }
-
- /**
- * Summary of getClassName
- * @param ?string $className
- * @return string
- */
- public function getClassName($className = null)
- {
- return $this->customColumnType->getTitle();
- }
-
- /**
- * Summary of getCustomCount
- * @return Entry
- */
- public function getCustomCount()
- {
- [$query, $params] = $this->getQuery();
- $columns = 'count(*)';
- $count = Database::countFilter($query, $columns, "", $params, $this->databaseId);
- return $this->getEntry($count);
- }
-
- /**
- * Get the query to find all books with this value
- * the returning array has two values:
- * - first the query (string)
- * - second an array of all PreparedStatement parameters
- *
- * @return array{0: string, 1: array}
- */
- public function getQuery()
- {
- return $this->customColumnType->getQuery($this->id);
- }
-
- /**
- * Summary of getFilter
- * @param ?string $parentTable
- * @return array{0: string, 1: array}
- */
- public function getFilter($parentTable = null)
- {
- return $this->customColumnType->getFilter($this->id, $parentTable);
- }
-
- /**
- * Return the value of this column as an HTML snippet
- *
- * @return string
- */
- public function getHTMLEncodedValue()
- {
- return $this->htmlvalue;
- }
-
- /**
- * Summary of hasChildCategories
- * @return bool
- */
- public function hasChildCategories()
- {
- return $this->customColumnType->hasChildCategories();
- }
-
- /**
- * Find related categories for hierarchical custom columns
- * Format: tag_browser_custom_column_2(id,value,count,avg_rating,sort)
- * @param string|array $find
- * @return array
- */
- public function getRelatedCategories($find)
- {
- return $this->customColumnType->getRelatedCategories($find);
- }
-
- /**
- * Create an CustomColumn by CustomColumnID and ValueID
- *
- * @param int $customId the id of the customColumn
- * @param string|int $id the id of the chosen value
- * @param ?int $database
- * @return ?CustomColumn
- */
- public static function createCustom($customId, $id, $database = null)
- {
- $columnType = CustomColumnType::createByCustomID($customId, $database);
-
- return $columnType->getCustom($id);
- }
-
- /**
- * Return this object as an array
- *
- * @return array
- */
- public function toArray()
- {
- return [
- 'valueID' => $this->id,
- 'value' => $this->value,
- 'customColumnType' => $this->customColumnType->toArray(),
- 'htmlvalue' => $this->htmlvalue,
- ];
- }
-}
diff --git a/COPS/cops-3.1.3/src/Calibre/CustomColumnType.php b/COPS/cops-3.1.3/src/Calibre/CustomColumnType.php
deleted file mode 100644
index ad132225..00000000
--- a/COPS/cops-3.1.3/src/Calibre/CustomColumnType.php
+++ /dev/null
@@ -1,572 +0,0 @@
-
- */
-
-namespace SebLucas\Cops\Calibre;
-
-use SebLucas\Cops\Input\Config;
-use SebLucas\Cops\Input\Route;
-use SebLucas\Cops\Model\Entry;
-use SebLucas\Cops\Model\LinkNavigation;
-use SebLucas\Cops\Pages\PageId;
-use Exception;
-
-/**
- * A single calibre custom column
- */
-abstract class CustomColumnType
-{
- public const PAGE_ID = PageId::ALL_CUSTOMS_ID;
- public const PAGE_ALL = PageId::ALL_CUSTOMS;
- public const PAGE_DETAIL = PageId::CUSTOM_DETAIL;
- public const SQL_TABLE = "custom_columns";
- public const SQL_BOOKLIST_LINK = 'select {0} from {2}, books ' . Book::SQL_BOOKS_LEFT_JOIN . '
- where {2}.book = books.id and {2}.{3} = ? {1} order by books.sort';
- public const SQL_BOOKLIST_ID = 'select {0} from {2}, books ' . Book::SQL_BOOKS_LEFT_JOIN . '
- where {2}.book = books.id and {2}.id = ? {1} order by books.sort';
- public const SQL_BOOKLIST_VALUE = 'select {0} from {2}, books ' . Book::SQL_BOOKS_LEFT_JOIN . '
- where {2}.book = books.id and {2}.value = ? {1} order by books.sort';
- public const SQL_BOOKLIST_RANGE = 'select {0} from {2}, books ' . Book::SQL_BOOKS_LEFT_JOIN . '
- where {2}.book = books.id and {2}.value >= ? and {2}.value <= ? {1} order by {2}.value';
- public const SQL_BOOKLIST_NULL = 'select {0} from books ' . Book::SQL_BOOKS_LEFT_JOIN . '
- where books.id not in (select book from {2}) {1} order by books.sort';
- public const ALL_WILDCARD = ["*"];
-
- public const TYPE_TEXT = "text"; // type 1 + 2 (calibre)
- public const TYPE_CSV = "csv"; // type 2 (internal)
- public const TYPE_COMMENT = "comments"; // type 3
- public const TYPE_SERIES = "series"; // type 4
- public const TYPE_ENUM = "enumeration"; // type 5
- public const TYPE_DATE = "datetime"; // type 6
- public const TYPE_FLOAT = "float"; // type 7
- public const TYPE_INT = "int"; // type 8
- public const TYPE_RATING = "rating"; // type 9
- public const TYPE_BOOL = "bool"; // type 10
- public const TYPE_COMPOSITE = "composite"; // type 11 + 12
-
- /** @var array */
- protected static $customColumnCacheID = [];
-
- /** @var array */
- protected static $customColumnCacheLookup = [];
-
- /** @var integer the id of this column */
- public $customId;
- /** @var string name/title of this column */
- public $columnTitle;
- /** @var string the datatype of this column (one of the TYPE_* constant values) */
- public $datatype;
- /** @var null|Entry[] */
- protected $customValues = null;
- /** @var ?int */
- protected $databaseId = null;
- /** @var ?int */
- protected $numberPerPage = -1;
- /** @var array */
- protected $displaySettings = [];
- protected string $handler = '';
-
- /**
- * Summary of __construct
- * @param int $customId
- * @param string $datatype
- * @param ?int $database
- * @param array $displaySettings
- */
- protected function __construct($customId, $datatype, $database = null, $displaySettings = [])
- {
- $this->columnTitle = static::getTitleByCustomID($customId, $database);
- $this->customId = $customId;
- $this->datatype = $datatype;
- $this->customValues = null;
- $this->databaseId = $database;
- $this->numberPerPage = Config::get('max_item_per_page');
- $this->displaySettings = $displaySettings;
- }
-
- /**
- * Summary of getDatabaseId
- * @return mixed
- */
- public function getDatabaseId()
- {
- return $this->databaseId;
- }
-
- /**
- * Get the name of the sqlite table for this column
- *
- * @return string
- */
- protected function getTableName()
- {
- return "custom_column_{$this->customId}";
- }
-
- /**
- * The URI to show all the values of this column
- *
- * @param array $params
- * @return string
- */
- public function getUri($params = [])
- {
- $params['custom'] = $this->customId;
- // we need databaseId here because we use Route::link with $handler
- $params['db'] = $this->getDatabaseId();
- return Route::link($this->handler, static::PAGE_ALL, $params);
- }
-
- /**
- * The EntryID to show all the values of this column
- *
- * @return string
- */
- public function getEntryId()
- {
- return static::PAGE_ID . ":" . $this->customId;
- }
-
- /**
- * The title of this column
- *
- * @return string
- */
- public function getTitle()
- {
- return $this->columnTitle;
- }
-
- /**
- * Summary of getContentType
- * @return mixed|string
- */
- public function getContentType()
- {
- // @checkme convert "csv" back to "text" here?
- return $this->datatype;
- }
-
- /**
- * Summary of getLinkArray
- * @param array $params
- * @return array
- */
- public function getLinkArray($params = [])
- {
- // issue #26 for koreader: section is not supported
- return [ new LinkNavigation($this->getUri($params), "subsection") ];
- }
-
- /**
- * Summary of setHandler
- * @param string $handler
- * @return void
- */
- public function setHandler($handler)
- {
- $this->handler = $handler;
- }
-
- /**
- * Summary of getHandler
- * @return string
- */
- public function getHandler()
- {
- return $this->handler;
- }
-
- /**
- * The description used in the index page
- *
- * @param int $count
- * @return string
- */
- public function getContent($count = 0)
- {
- $desc = $this->getDatabaseDescription();
- if ($desc === null || empty($desc)) {
- $desc = str_format(localize("customcolumn.description"), $this->getTitle());
- }
- return $desc;
- }
-
- /**
- * The description of this column as it is definied in the database
- *
- * @return ?string
- */
- public function getDatabaseDescription()
- {
- $query = 'SELECT display FROM custom_columns WHERE id = ?';
- $result = Database::query($query, [$this->customId], $this->databaseId);
- if ($post = $result->fetchObject()) {
- $json = json_decode($post->display);
- return (isset($json->description) && !empty($json->description)) ? $json->description : null;
- }
- return null;
- }
-
- /**
- * Get the Entry for this column
- * This is used in the initializeContent method to display e.g. the index page
- *
- * @param array $params
- * @return Entry
- */
- public function getCount($params = [])
- {
- // @todo do we want to filter by virtual library etc. here?
- $pcount = $this->getDistinctValueCount();
- $ptitle = $this->getTitle();
- $pid = $this->getEntryId();
- $pcontent = $this->getContent($pcount);
- // @checkme convert "csv" back to "text" here?
- $pcontentType = $this->getContentType();
- $database = $this->getDatabaseId();
- $plinkArray = $this->getLinkArray($params);
- $pclass = "";
-
- return new Entry($ptitle, $pid, $pcontent, $pcontentType, $plinkArray, $database, $pclass, $pcount);
- }
-
- /**
- * Return an entry array for all possible (in the DB used) values of this column
- * These are the values used in the PageAllCustoms() page
- *
- * @param int $n
- * @param ?string $sort
- * @return array
- */
- public function getAllCustomValues($n = -1, $sort = null)
- {
- // lazy loading
- if ($this->customValues == null) {
- $this->customValues = $this->getAllCustomValuesFromDatabase($n, $sort);
- }
-
- return $this->customValues;
- }
-
- /**
- * Summary of getPaginatedResult
- * @param string $query
- * @param array $params
- * @param int $n
- * @return \PDOStatement
- */
- public function getPaginatedResult($query, $params = [], $n = 1)
- {
- if ($this->numberPerPage != -1 && $n != -1) {
- $query .= " LIMIT ?, ?";
- array_push($params, ($n - 1) * $this->numberPerPage, $this->numberPerPage);
- }
- $result = Database::query($query, $params, $this->databaseId);
-
- return $result;
- }
-
- /**
- * Get the amount of distinct values for this column
- *
- * @return int
- */
- public function getDistinctValueCount()
- {
- $queryFormat = "SELECT COUNT(DISTINCT value) AS count FROM {0}";
- $query = str_format($queryFormat, $this->getTableName());
- // @todo do we want to filter by virtual library etc. here?
- return Database::querySingle($query, $this->databaseId);
- }
-
- /**
- * Use the Calibre tag browser view to retrieve all custom values with count
- * Format: tag_browser_custom_column_2(id,value,count,avg_rating,sort)
- * @param int $n
- * @param ?string $sort
- * @return array
- */
- public function browseAllCustomValues($n = -1, $sort = null)
- {
- if (!$this->hasChildCategories()) {
- return [];
- }
- $tableName = 'tag_browser_' . $this->getTableName();
- $queryFormat = "SELECT id, value, count FROM {0} ORDER BY {1}";
- if (!in_array($sort, ['id', 'value', 'count', 'sort'])) {
- $sort = "sort";
- }
- if ($sort == 'count') {
- $sort .= ' desc, value';
- }
- $query = str_format($queryFormat, $tableName, $sort);
-
- $result = $this->getPaginatedResult($query, [], $n);
- $entryArray = [];
- while ($post = $result->fetchObject()) {
- $customcolumn = new CustomColumn($post->id, $post->value, $this);
- array_push($entryArray, $customcolumn->getEntry($post->count));
- }
- return $entryArray;
- }
-
- /**
- * Summary of hasChildCategories
- * @return bool
- */
- public function hasChildCategories()
- {
- // @todo this only works with column titles/names, not the lookup names used elsewhere
- if (empty(Config::get('calibre_categories_using_hierarchy')) || !in_array($this->columnTitle, Config::get('calibre_categories_using_hierarchy'))) {
- return false;
- }
- return true;
- }
-
- /**
- * Find related categories for hierarchical custom columns
- * Format: tag_browser_custom_column_2(id,value,count,avg_rating,sort)
- * @param string|array $find pattern match or exact match for name, or array of child ids
- * @return array
- */
- public function getRelatedCategories($find)
- {
- if (!$this->hasChildCategories()) {
- return [];
- }
- $tableName = 'tag_browser_' . $this->getTableName();
- if (is_array($find)) {
- $queryFormat = "SELECT id, value, count FROM {0} WHERE id IN (" . str_repeat("?,", count($find) - 1) . "?) ORDER BY sort";
- $params = $find;
- } elseif (!str_contains($find, '%')) {
- $queryFormat = "SELECT id, value, count FROM {0} WHERE value = ? ORDER BY sort";
- $params = [$find];
- } else {
- $queryFormat = "SELECT id, value, count FROM {0} WHERE value LIKE ? ORDER BY sort";
- $params = [$find];
- }
- $query = str_format($queryFormat, $tableName);
- $result = Database::query($query, $params, $this->databaseId);
-
- $instances = [];
- while ($post = $result->fetchObject()) {
- $customcolumn = new CustomColumn($post->id, $post->value, $this);
- $customcolumn->count = $post->count;
- array_push($instances, $customcolumn);
- }
- return $instances;
- }
-
- /**
- * Encode a value of this column ready to be displayed in an HTML document
- *
- * @param integer|string $value
- * @return string
- */
- public function encodeHTMLValue($value)
- {
- return htmlspecialchars($value);
- }
-
- /**
- * Return this object as an array
- *
- * @return array
- */
- public function toArray()
- {
- return [
- 'customId' => $this->customId,
- 'columnTitle' => $this->columnTitle,
- 'datatype' => $this->datatype,
- 'displaySettings' => $this->displaySettings,
- //'customValues' => $this->customValues,
- ];
- }
-
- /**
- * Get the datatype & display-settings of a CustomColumn by its customID
- *
- * @param int $customId
- * @param ?int $database
- * @return array
- */
- protected static function getDatatypeAndDisplaySettingsByCustomID($customId, $database = null)
- {
- $query = 'SELECT datatype, is_multiple, display FROM custom_columns WHERE id = ?';
- $result = Database::query($query, [$customId], $database);
- if ($post = $result->fetchObject()) {
-
- $settings = $post->display ? json_decode($post->display, true) : [];
-
- // handle case where we have several values, e.g. array of text for type 2 (csv)
- if ($post->datatype === "text" && $post->is_multiple === 1) {
- return ["csv", $settings];
- }
- return [$post->datatype, $settings];
- }
- return [null, []];
- }
-
- /**
- * Create a CustomColumnType by CustomID
- *
- * @param int $customId the id of the custom column
- * @param ?int $database
- * @return ?CustomColumnType
- * @throws Exception If the $customId is not found or the datatype is unknown
- */
- public static function createByCustomID($customId, $database = null)
- {
- // Reuse already created CustomColumns for performance
- if (array_key_exists($customId, static::$customColumnCacheID)) {
- return static::$customColumnCacheID[$customId];
- }
-
- [$datatype, $displaySettings] = static::getDatatypeAndDisplaySettingsByCustomID($customId, $database);
-
- static::$customColumnCacheID[$customId] = match ($datatype) {
- static::TYPE_TEXT => new CustomColumnTypeText($customId, static::TYPE_TEXT, $database, $displaySettings),
- static::TYPE_CSV => new CustomColumnTypeText($customId, static::TYPE_CSV, $database, $displaySettings),
- static::TYPE_SERIES => new CustomColumnTypeSeries($customId, $database, $displaySettings),
- static::TYPE_ENUM => new CustomColumnTypeEnumeration($customId, $database, $displaySettings),
- static::TYPE_COMMENT => new CustomColumnTypeComment($customId, $database, $displaySettings),
- static::TYPE_DATE => new CustomColumnTypeDate($customId, $database, $displaySettings),
- static::TYPE_FLOAT => new CustomColumnTypeFloat($customId, $database, $displaySettings),
- static::TYPE_INT => new CustomColumnTypeInteger($customId, static::TYPE_INT, $database, $displaySettings),
- static::TYPE_RATING => new CustomColumnTypeRating($customId, $database, $displaySettings),
- static::TYPE_BOOL => new CustomColumnTypeBool($customId, $database, $displaySettings),
- static::TYPE_COMPOSITE => null,
- default => throw new Exception("Unkown column type: " . $datatype),
- };
- return static::$customColumnCacheID[$customId];
- }
-
- /**
- * Create a CustomColumnType by its lookup name
- *
- * @param string $lookup the lookup-name of the custom column
- * @param ?int $database
- * @return ?CustomColumnType
- */
- public static function createByLookup($lookup, $database = null)
- {
- // Reuse already created CustomColumns for performance
- if (array_key_exists($lookup, static::$customColumnCacheLookup)) {
- return static::$customColumnCacheLookup[$lookup];
- }
-
- $query = 'SELECT id FROM custom_columns WHERE label = ?';
- $result = Database::query($query, [$lookup], $database);
- if ($post = $result->fetchObject()) {
- return static::$customColumnCacheLookup[$lookup] = static::createByCustomID($post->id, $database);
- }
- return static::$customColumnCacheLookup[$lookup] = null;
- }
-
- /**
- * Get the title of a CustomColumn by its customID
- *
- * @param int $customId
- * @param ?int $database
- * @return string
- */
- protected static function getTitleByCustomID($customId, $database = null)
- {
- $query = 'SELECT name FROM custom_columns WHERE id = ?';
- $result = Database::query($query, [$customId], $database);
- if ($post = $result->fetchObject()) {
- return $post->name;
- }
- return "";
- }
-
- /**
- * Check the list of custom columns requested (and expand the wildcard if needed)
- *
- * @param array $columnList
- * @param ?int $database
- * @return array
- */
- public static function checkCustomColumnList($columnList, $database = null)
- {
- if ($columnList === static::ALL_WILDCARD) {
- $columnList = array_keys(static::getAllCustomColumns($database));
- }
- return $columnList;
- }
-
- /**
- * Get all defined custom columns from the database
- *
- * @param ?int $database
- * @return array>
- */
- public static function getAllCustomColumns($database = null)
- {
- $query = 'SELECT id, label, name, datatype, display, is_multiple, normalized FROM custom_columns';
- $result = Database::query($query, [], $database);
- $columns = [];
- while ($post = $result->fetchObject()) {
- $columns[$post->label] = (array) $post;
- }
- return $columns;
- }
-
- /**
- * Get the query to find all books with a specific value of this column
- * the returning array has two values:
- * - first the query (string)
- * - second an array of all PreparedStatement parameters
- *
- * @param string|integer|null $id the id of the searched value
- * @return ?array{0: string, 1: array}
- */
- abstract public function getQuery($id);
-
- /**
- * Summary of getFilter
- * @param string|int|null $id
- * @param ?string $parentTable
- * @return ?array{0: string, 1: array}
- */
- abstract public function getFilter($id, $parentTable = null);
-
- /**
- * Get a CustomColumn for a specified (by ID) value
- *
- * @param string|int|null $id the id of the searched value
- * @return ?CustomColumn
- */
- abstract public function getCustom($id);
-
- /**
- * Return an entry array for all possible (in the DB used) values of this column by querying the database
- *
- * @param int $n
- * @param ?string $sort
- * @return ?array
- */
- abstract protected function getAllCustomValuesFromDatabase($n = -1, $sort = null);
-
- /**
- * Find the value of this column for a specific book
- *
- * @param Book $book
- * @return CustomColumn
- */
- abstract public function getCustomByBook($book);
-
- /**
- * Is this column searchable by value
- * only searchable columns can be displayed on the index page
- *
- * @return bool
- */
- abstract public function isSearchable();
-}
diff --git a/COPS/cops-3.1.3/src/Calibre/CustomColumnTypeBool.php b/COPS/cops-3.1.3/src/Calibre/CustomColumnTypeBool.php
deleted file mode 100644
index aea2d3ae..00000000
--- a/COPS/cops-3.1.3/src/Calibre/CustomColumnTypeBool.php
+++ /dev/null
@@ -1,162 +0,0 @@
-
- */
-
-namespace SebLucas\Cops\Calibre;
-
-use SebLucas\Cops\Model\Entry;
-
-class CustomColumnTypeBool extends CustomColumnType
-{
- public const SQL_BOOKLIST_TRUE = 'select {0} from {2}, books ' . Book::SQL_BOOKS_LEFT_JOIN . '
- where {2}.book = books.id and {2}.value = 1 {1} order by books.sort';
- public const SQL_BOOKLIST_FALSE = 'select {0} from {2}, books ' . Book::SQL_BOOKS_LEFT_JOIN . '
- where {2}.book = books.id and {2}.value = 0 {1} order by books.sort';
-
- // PHP pre 5.6 does not support const arrays
- /** @var array */
- protected $BOOLEAN_NAMES = [
- -1 => "customcolumn.boolean.unknown", // localize("customcolumn.boolean.unknown")
- 0 => "customcolumn.boolean.no", // localize("customcolumn.boolean.no")
- +1 => "customcolumn.boolean.yes", // localize("customcolumn.boolean.yes")
- ];
-
- /**
- * Summary of __construct
- * @param int $customId
- * @param ?int $database
- * @param array $displaySettings
- */
- protected function __construct($customId, $database = null, $displaySettings = [])
- {
- parent::__construct($customId, static::TYPE_BOOL, $database, $displaySettings);
- }
-
- /**
- * Summary of getQuery
- * @param string|int|null $id
- * @return ?array{0: string, 1: array}
- */
- public function getQuery($id)
- {
- if ($id == -1 || $id === '') {
- $query = str_format(static::SQL_BOOKLIST_NULL, "{0}", "{1}", $this->getTableName());
- return [$query, []];
- } elseif ($id == 0) {
- $query = str_format(static::SQL_BOOKLIST_FALSE, "{0}", "{1}", $this->getTableName());
- return [$query, []];
- } elseif ($id == 1) {
- $query = str_format(static::SQL_BOOKLIST_TRUE, "{0}", "{1}", $this->getTableName());
- return [$query, []];
- } else {
- return null;
- }
- }
-
- /**
- * Summary of getFilter
- * @param string|int|null $id
- * @param ?string $parentTable
- * @return ?array{0: string, 1: array}
- */
- public function getFilter($id, $parentTable = null)
- {
- $linkTable = $this->getTableName();
- $linkColumn = "value";
- // @todo support $parentTable if relevant
- if ($id == -1 || $id === '') {
- // @todo is this the right way when filtering?
- $filter = "not exists (select null from {$linkTable} where {$linkTable}.book = books.id)";
- return [$filter, []];
- } elseif ($id == 0) {
- $filter = "exists (select null from {$linkTable} where {$linkTable}.book = books.id and {$linkTable}.{$linkColumn} = 0)";
- return [$filter, []];
- } elseif ($id == 1) {
- $filter = "exists (select null from {$linkTable} where {$linkTable}.book = books.id and {$linkTable}.{$linkColumn} = 1)";
- return [$filter, []];
- } else {
- return ["", []];
- }
- }
-
- /**
- * Summary of getCustom
- * @param string|int|null $id
- * @return CustomColumn
- */
- public function getCustom($id)
- {
- return new CustomColumn($id, localize($this->BOOLEAN_NAMES[$id]), $this);
- }
-
- /**
- * Summary of getAllCustomValuesFromDatabase
- * @param int $n
- * @param ?string $sort
- * @return array
- */
- protected function getAllCustomValuesFromDatabase($n = -1, $sort = null)
- {
- $queryFormat = "SELECT coalesce({0}.value, -1) AS id, count(*) AS count FROM books LEFT JOIN {0} ON books.id = {0}.book GROUP BY {0}.value ORDER BY {0}.value";
- $query = str_format($queryFormat, $this->getTableName());
- $result = Database::query($query, [], $this->databaseId);
-
- $entryArray = [];
- while ($post = $result->fetchObject()) {
- $name = localize($this->BOOLEAN_NAMES[$post->id]);
- $customcolumn = new CustomColumn($post->id, $name, $this);
- array_push($entryArray, $customcolumn->getEntry($post->count));
- }
- return $entryArray;
- }
-
- /**
- * Summary of getDistinctValueCount
- * @return int
- */
- public function getDistinctValueCount()
- {
- return count($this->BOOLEAN_NAMES);
- }
-
- /**
- * Summary of getContent
- * @param int $count
- * @return string
- */
- public function getContent($count = 0)
- {
- return localize("customcolumn.description.bool");
- }
-
- /**
- * Summary of getCustomByBook
- * @param Book $book
- * @return CustomColumn
- */
- public function getCustomByBook($book)
- {
- $queryFormat = "SELECT {0}.value AS boolvalue FROM {0} WHERE {0}.book = ?";
- $query = str_format($queryFormat, $this->getTableName());
-
- $result = Database::query($query, [$book->id], $this->databaseId);
- if ($post = $result->fetchObject()) {
- return new CustomColumn($post->boolvalue, localize($this->BOOLEAN_NAMES[$post->boolvalue]), $this);
- } else {
- return new CustomColumn(-1, localize($this->BOOLEAN_NAMES[-1]), $this);
- }
- }
-
- /**
- * Summary of isSearchable
- * @return bool
- */
- public function isSearchable()
- {
- return true;
- }
-}
diff --git a/COPS/cops-3.1.3/src/Calibre/CustomColumnTypeComment.php b/COPS/cops-3.1.3/src/Calibre/CustomColumnTypeComment.php
deleted file mode 100644
index 69229d40..00000000
--- a/COPS/cops-3.1.3/src/Calibre/CustomColumnTypeComment.php
+++ /dev/null
@@ -1,125 +0,0 @@
-
- */
-
-namespace SebLucas\Cops\Calibre;
-
-use SebLucas\Cops\Input\Config;
-use SebLucas\Cops\Model\Entry;
-
-class CustomColumnTypeComment extends CustomColumnType
-{
- /**
- * Summary of __construct
- * @param int $customId
- * @param ?int $database
- * @param array $displaySettings
- */
- protected function __construct($customId, $database = null, $displaySettings = [])
- {
- parent::__construct($customId, static::TYPE_COMMENT, $database, $displaySettings);
- }
-
- /**
- * Summary of getQuery
- * @param string|int|null $id
- * @return ?array{0: string, 1: array}
- */
- public function getQuery($id)
- {
- if (empty($id) && in_array("custom", Config::get('show_not_set_filter'))) {
- $query = str_format(static::SQL_BOOKLIST_NULL, "{0}", "{1}", $this->getTableName());
- return [$query, []];
- }
- $query = str_format(static::SQL_BOOKLIST_ID, "{0}", "{1}", $this->getTableName());
- return [$query, [$id]];
- }
-
- /**
- * Summary of getFilter
- * @param string|int|null $id
- * @param ?string $parentTable
- * @return ?array{0: string, 1: array}
- */
- public function getFilter($id, $parentTable = null)
- {
- $linkTable = $this->getTableName();
- $linkColumn = "id";
- if (!empty($parentTable) && $parentTable != "books") {
- $filter = "exists (select null from {$linkTable}, books where {$parentTable}.book = books.id and {$linkTable}.book = books.id and {$linkTable}.{$linkColumn} = ?)";
- } else {
- $filter = "exists (select null from {$linkTable} where {$linkTable}.book = books.id and {$linkTable}.{$linkColumn} = ?)";
- }
- return [$filter, [$id]];
- }
-
- /**
- * Summary of getCustom
- * @param string|int|null $id
- * @return CustomColumn
- */
- public function getCustom($id)
- {
- return new CustomColumn($id, $id, $this);
- }
-
- /**
- * Summary of encodeHTMLValue
- * @param string $value
- * @return string
- */
- public function encodeHTMLValue($value)
- {
- return "" . $value . " "; // no htmlspecialchars, this is already HTML
- }
-
- /**
- * Summary of getAllCustomValuesFromDatabase
- * @param int $n
- * @param ?string $sort
- * @return null
- */
- protected function getAllCustomValuesFromDatabase($n = -1, $sort = null)
- {
- return null;
- }
-
- /**
- * Summary of getDistinctValueCount
- * @return int
- */
- public function getDistinctValueCount()
- {
- return 0;
- }
-
- /**
- * Summary of getCustomByBook
- * @param Book $book
- * @return CustomColumn
- */
- public function getCustomByBook($book)
- {
- $queryFormat = "SELECT {0}.id AS id, {0}.value AS value FROM {0} WHERE {0}.book = ?";
- $query = str_format($queryFormat, $this->getTableName());
-
- $result = Database::query($query, [$book->id], $this->databaseId);
- if ($post = $result->fetchObject()) {
- return new CustomColumn($post->id, $post->value, $this);
- }
- return new CustomColumn(null, localize("customcolumn.float.unknown"), $this);
- }
-
- /**
- * Summary of isSearchable
- * @return bool
- */
- public function isSearchable()
- {
- return false;
- }
-}
diff --git a/COPS/cops-3.1.3/src/Calibre/CustomColumnTypeDate.php b/COPS/cops-3.1.3/src/Calibre/CustomColumnTypeDate.php
deleted file mode 100644
index 8b304c50..00000000
--- a/COPS/cops-3.1.3/src/Calibre/CustomColumnTypeDate.php
+++ /dev/null
@@ -1,241 +0,0 @@
-
- */
-
-namespace SebLucas\Cops\Calibre;
-
-use SebLucas\Cops\Input\Config;
-use SebLucas\Cops\Input\Route;
-use SebLucas\Cops\Model\Entry;
-use SebLucas\Cops\Model\LinkNavigation;
-use DateTime;
-use UnexpectedValueException;
-
-class CustomColumnTypeDate extends CustomColumnType
-{
- public const SQL_BOOKLIST = 'select {0} from {2}, books ' . Book::SQL_BOOKS_LEFT_JOIN . '
- where {2}.book = books.id and date({2}.value) = ? {1} order by books.sort';
- public const SQL_BOOKLIST_YEAR = 'select {0} from {2}, books ' . Book::SQL_BOOKS_LEFT_JOIN . '
- where {2}.book = books.id and substr(date({2}.value), 1, 4) = ? {1} order by {2}.value';
- public const GET_PATTERN = '/^(\d+)$/';
-
- /**
- * Summary of __construct
- * @param int $customId
- * @param ?int $database
- * @param array $displaySettings
- */
- protected function __construct($customId, $database = null, $displaySettings = [])
- {
- parent::__construct($customId, static::TYPE_DATE, $database, $displaySettings);
- }
-
- /**
- * Summary of getQuery
- * @param string|int|null $id
- * @return ?array{0: string, 1: array}
- */
- public function getQuery($id)
- {
- if (empty($id) && in_array("custom", Config::get('show_not_set_filter'))) {
- $query = str_format(static::SQL_BOOKLIST_NULL, "{0}", "{1}", $this->getTableName());
- return [$query, []];
- }
- $date = new DateTime($id);
- $query = str_format(static::SQL_BOOKLIST, "{0}", "{1}", $this->getTableName());
- return [$query, [$date->format("Y-m-d")]];
- }
-
- /**
- * Summary of getQueryByYear
- * @param mixed $year
- * @throws \UnexpectedValueException
- * @return ?array{0: string, 1: array}
- */
- public function getQueryByYear($year)
- {
- if (!preg_match(static::GET_PATTERN, (string) $year)) {
- throw new UnexpectedValueException();
- }
- $query = str_format(static::SQL_BOOKLIST_YEAR, "{0}", "{1}", $this->getTableName());
- return [$query, [$year]];
- }
-
- /**
- * Summary of getFilter
- * @param string|int|null $id
- * @param ?string $parentTable
- * @return ?array{0: string, 1: array}
- */
- public function getFilter($id, $parentTable = null)
- {
- $date = new DateTime($id);
- $linkTable = $this->getTableName();
- $linkColumn = "value";
- if (!empty($parentTable) && $parentTable != "books") {
- $filter = "exists (select null from {$linkTable}, books where {$parentTable}.book = books.id and {$linkTable}.book = books.id and {$linkTable}.{$linkColumn} = ?)";
- } else {
- $filter = "exists (select null from {$linkTable} where {$linkTable}.book = books.id and date({$linkTable}.{$linkColumn}) = ?)";
- }
- return [$filter, [$date->format("Y-m-d")]];
- }
-
- /**
- * Summary of getCustom
- * @param string|int|null $id
- * @return CustomColumn
- */
- public function getCustom($id)
- {
- if (empty($id)) {
- return new CustomColumn(null, localize("customcolumn.date.unknown"), $this);
- }
- $date = new DateTime($id);
-
- return new CustomColumn($id, $date->format(localize("customcolumn.date.format")), $this);
- }
-
- /**
- * Summary of getAllCustomValuesFromDatabase
- * @param int $n
- * @param ?string $sort
- * @return array
- */
- protected function getAllCustomValuesFromDatabase($n = -1, $sort = null)
- {
- $queryFormat = "SELECT date(value) AS datevalue, count(*) AS count FROM {0} GROUP BY datevalue";
- if (!empty($sort) && $sort == 'count') {
- $queryFormat .= ' ORDER BY count desc, datevalue';
- } else {
- $queryFormat .= ' ORDER BY datevalue';
- }
- $query = str_format($queryFormat, $this->getTableName());
-
- $result = $this->getPaginatedResult($query, [], $n);
- $entryArray = [];
- while ($post = $result->fetchObject()) {
- $date = new DateTime($post->datevalue);
- $id = $date->format("Y-m-d");
- $name = $date->format(localize("customcolumn.date.format"));
-
- $customcolumn = new CustomColumn($id, $name, $this);
- array_push($entryArray, $customcolumn->getEntry($post->count));
- }
-
- return $entryArray;
- }
-
- /**
- * Summary of getDistinctValueCount
- * @return mixed
- */
- public function getDistinctValueCount()
- {
- $queryFormat = "SELECT COUNT(DISTINCT date(value)) AS count FROM {0}";
- $query = str_format($queryFormat, $this->getTableName());
- return Database::querySingle($query, $this->databaseId);
- }
-
- /**
- * Summary of getCountByYear
- * @param mixed $page can be $columnType::PAGE_ALL or $columnType::PAGE_DETAIL
- * @param ?string $sort
- * @return array
- */
- public function getCountByYear($page, $sort = null)
- {
- $queryFormat = "SELECT substr(date(value), 1, 4) AS groupid, count(*) AS count FROM {0} GROUP BY groupid";
- if (!empty($sort) && $sort == 'count') {
- $queryFormat .= ' ORDER BY count desc, groupid';
- } else {
- $queryFormat .= ' ORDER BY groupid';
- }
- $query = str_format($queryFormat, $this->getTableName());
- $result = Database::query($query, [], $this->databaseId);
-
- $entryArray = [];
- $label = 'year';
- while ($post = $result->fetchObject()) {
- $params = ['custom' => $this->customId, 'year' => $post->groupid, 'db' => $this->databaseId];
- $href = Route::link($this->handler, $page, $params);
- array_push($entryArray, new Entry(
- $post->groupid,
- $this->getEntryId() . ':' . $label . ':' . $post->groupid,
- str_format(localize('bookword', $post->count), $post->count),
- 'text',
- [ new LinkNavigation($href, null, null) ],
- $this->databaseId,
- ucfirst($label),
- $post->count
- ));
- }
-
- return $entryArray;
- }
-
- /**
- * Summary of getCustomValuesByYear
- * @param mixed $year
- * @param ?string $sort
- * @return array
- */
- public function getCustomValuesByYear($year, $sort = null)
- {
- if (!preg_match(static::GET_PATTERN, (string) $year)) {
- throw new UnexpectedValueException();
- }
- $queryFormat = "SELECT date(value) AS datevalue, count(*) AS count FROM {0} WHERE substr(date(value), 1, 4) = ? GROUP BY datevalue";
- if (!empty($sort) && $sort == 'count') {
- $queryFormat .= ' ORDER BY count desc, datevalue';
- } else {
- $queryFormat .= ' ORDER BY datevalue';
- }
- $query = str_format($queryFormat, $this->getTableName());
- $params = [ $year ];
- $result = Database::query($query, $params, $this->databaseId);
-
- $entryArray = [];
- while ($post = $result->fetchObject()) {
- $date = new DateTime($post->datevalue);
- $id = $date->format("Y-m-d");
- $name = $date->format(localize("customcolumn.date.format"));
-
- $customcolumn = new CustomColumn($id, $name, $this);
- array_push($entryArray, $customcolumn->getEntry($post->count));
- }
-
- return $entryArray;
- }
-
- /**
- * Summary of getCustomByBook
- * @param Book $book
- * @return CustomColumn
- */
- public function getCustomByBook($book)
- {
- $queryFormat = "SELECT date({0}.value) AS datevalue FROM {0} WHERE {0}.book = ?";
- $query = str_format($queryFormat, $this->getTableName());
-
- $result = Database::query($query, [$book->id], $this->databaseId);
- if ($post = $result->fetchObject()) {
- $date = new DateTime($post->datevalue);
-
- return new CustomColumn($date->format("Y-m-d"), $date->format(localize("customcolumn.date.format")), $this);
- }
- return new CustomColumn(null, localize("customcolumn.date.unknown"), $this);
- }
-
- /**
- * Summary of isSearchable
- * @return bool
- */
- public function isSearchable()
- {
- return true;
- }
-}
diff --git a/COPS/cops-3.1.3/src/Calibre/CustomColumnTypeEnumeration.php b/COPS/cops-3.1.3/src/Calibre/CustomColumnTypeEnumeration.php
deleted file mode 100644
index 09d3e2c4..00000000
--- a/COPS/cops-3.1.3/src/Calibre/CustomColumnTypeEnumeration.php
+++ /dev/null
@@ -1,151 +0,0 @@
-
- */
-
-namespace SebLucas\Cops\Calibre;
-
-use SebLucas\Cops\Input\Config;
-use SebLucas\Cops\Model\Entry;
-
-class CustomColumnTypeEnumeration extends CustomColumnType
-{
- /**
- * Summary of __construct
- * @param int $customId
- * @param ?int $database
- * @param array $displaySettings
- */
- protected function __construct($customId, $database = null, $displaySettings = [])
- {
- parent::__construct($customId, static::TYPE_ENUM, $database, $displaySettings);
- }
-
- /**
- * Get the name of the linking sqlite table for this column
- * (or NULL if there is no linktable)
- *
- * @return string
- */
- protected function getTableLinkName()
- {
- return "books_custom_column_{$this->customId}_link";
- }
-
- /**
- * Get the name of the linking column in the linktable
- *
- * @return string
- */
- protected function getTableLinkColumn()
- {
- return "value";
- }
-
- /**
- * Summary of getQuery
- * @param string|int|null $id
- * @return ?array{0: string, 1: array}
- */
- public function getQuery($id)
- {
- if (empty($id) && in_array("custom", Config::get('show_not_set_filter'))) {
- $query = str_format(static::SQL_BOOKLIST_NULL, "{0}", "{1}", $this->getTableLinkName());
- return [$query, []];
- }
- $query = str_format(static::SQL_BOOKLIST_LINK, "{0}", "{1}", $this->getTableLinkName(), $this->getTableLinkColumn());
- return [$query, [$id]];
- }
-
- /**
- * Summary of getFilter
- * @param string|int|null $id
- * @param ?string $parentTable
- * @return ?array{0: string, 1: array}
- */
- public function getFilter($id, $parentTable = null)
- {
- $linkTable = $this->getTableLinkName();
- $linkColumn = $this->getTableLinkColumn();
- if (!empty($parentTable) && $parentTable != "books") {
- $filter = "exists (select null from {$linkTable}, books where {$parentTable}.book = books.id and {$linkTable}.book = books.id and {$linkTable}.{$linkColumn} = ?)";
- } else {
- $filter = "exists (select null from {$linkTable} where {$linkTable}.book = books.id and {$linkTable}.{$linkColumn} = ?)";
- }
- return [$filter, [$id]];
- }
-
- /**
- * Summary of getCustom
- * @param string|int|null $id
- * @return CustomColumn
- */
- public function getCustom($id)
- {
- $query = str_format("SELECT id, value AS name FROM {0} WHERE id = ?", $this->getTableName());
- $result = Database::query($query, [$id], $this->databaseId);
- if ($post = $result->fetchObject()) {
- return new CustomColumn($id, $post->name, $this);
- }
- return new CustomColumn(null, localize("customcolumn.enum.unknown"), $this);
- }
-
- /**
- * Summary of getAllCustomValuesFromDatabase
- * @param int $n
- * @param ?string $sort
- * @return array
- */
- protected function getAllCustomValuesFromDatabase($n = -1, $sort = null)
- {
- $queryFormat = "SELECT {0}.id AS id, {0}.value AS name, count(*) AS count FROM {0}, {1} WHERE {0}.id = {1}.{2} GROUP BY {0}.id, {0}.value ORDER BY {0}.value";
- $query = str_format($queryFormat, $this->getTableName(), $this->getTableLinkName(), $this->getTableLinkColumn());
-
- $result = $this->getPaginatedResult($query, [], $n);
- $entryArray = [];
- while ($post = $result->fetchObject()) {
- $customcolumn = new CustomColumn($post->id, $post->name, $this);
- array_push($entryArray, $customcolumn->getEntry($post->count));
- }
- return $entryArray;
- }
-
- /**
- * Summary of getContent
- * @param int $count
- * @return string
- */
- public function getContent($count = 0)
- {
- return str_format(localize("customcolumn.description.enum", $count), $count);
- }
-
- /**
- * Summary of getCustomByBook
- * @param Book $book
- * @return CustomColumn
- */
- public function getCustomByBook($book)
- {
- $queryFormat = "SELECT {0}.id AS id, {0}.{2} AS name FROM {0}, {1} WHERE {0}.id = {1}.{2} AND {1}.book = ?";
- $query = str_format($queryFormat, $this->getTableName(), $this->getTableLinkName(), $this->getTableLinkColumn());
-
- $result = Database::query($query, [$book->id], $this->databaseId);
- if ($post = $result->fetchObject()) {
- return new CustomColumn($post->id, $post->name, $this);
- }
- return new CustomColumn(null, localize("customcolumn.enum.unknown"), $this);
- }
-
- /**
- * Summary of isSearchable
- * @return bool
- */
- public function isSearchable()
- {
- return true;
- }
-}
diff --git a/COPS/cops-3.1.3/src/Calibre/CustomColumnTypeFloat.php b/COPS/cops-3.1.3/src/Calibre/CustomColumnTypeFloat.php
deleted file mode 100644
index a2ab69e3..00000000
--- a/COPS/cops-3.1.3/src/Calibre/CustomColumnTypeFloat.php
+++ /dev/null
@@ -1,137 +0,0 @@
-
- */
-
-namespace SebLucas\Cops\Calibre;
-
-use SebLucas\Cops\Input\Config;
-use SebLucas\Cops\Model\Entry;
-use UnexpectedValueException;
-
-class CustomColumnTypeFloat extends CustomColumnType
-{
- public const GET_PATTERN = '/^(-?[0-9.]+)-(-?[0-9.]+)$/';
-
- /**
- * Summary of __construct
- * @param int $customId
- * @param ?int $database
- * @param array $displaySettings
- */
- protected function __construct($customId, $database = null, $displaySettings = [])
- {
- parent::__construct($customId, static::TYPE_FLOAT, $database, $displaySettings);
- }
-
- /**
- * Summary of getQuery
- * @param string|int|null $id
- * @return ?array{0: string, 1: array}
- */
- public function getQuery($id)
- {
- if (empty($id) && strval($id) !== '0.0' && in_array("custom", Config::get('show_not_set_filter'))) {
- $query = str_format(static::SQL_BOOKLIST_NULL, "{0}", "{1}", $this->getTableName());
- return [$query, []];
- }
- $query = str_format(static::SQL_BOOKLIST_VALUE, "{0}", "{1}", $this->getTableName());
- return [$query, [$id]];
- }
-
- /**
- * Summary of getQueryByRange
- * @param string $range
- * @throws \UnexpectedValueException
- * @return ?array{0: string, 1: array}
- */
- public function getQueryByRange($range)
- {
- $matches = [];
- if (!preg_match(static::GET_PATTERN, $range, $matches)) {
- throw new UnexpectedValueException();
- }
- $lower = $matches[1];
- $upper = $matches[2];
- $query = str_format(static::SQL_BOOKLIST_RANGE, "{0}", "{1}", $this->getTableName());
- return [$query, [$lower, $upper]];
- }
-
- /**
- * Summary of getFilter
- * @param string|int|null $id
- * @param ?string $parentTable
- * @return ?array{0: string, 1: array}
- */
- public function getFilter($id, $parentTable = null)
- {
- $linkTable = $this->getTableName();
- $linkColumn = "value";
- if (!empty($parentTable) && $parentTable != "books") {
- $filter = "exists (select null from {$linkTable}, books where {$parentTable}.book = books.id and {$linkTable}.book = books.id and {$linkTable}.{$linkColumn} = ?)";
- } else {
- $filter = "exists (select null from {$linkTable} where {$linkTable}.book = books.id and {$linkTable}.{$linkColumn} = ?)";
- }
- return [$filter, [$id]];
- }
-
- /**
- * Summary of getCustom
- * @param string|int|null $id
- * @return CustomColumn
- */
- public function getCustom($id)
- {
- return new CustomColumn($id, $id, $this);
- }
-
- /**
- * Summary of getAllCustomValuesFromDatabase
- * @param int $n
- * @param ?string $sort
- * @return array
- */
- protected function getAllCustomValuesFromDatabase($n = -1, $sort = null)
- {
- $queryFormat = "SELECT value AS id, count(*) AS count FROM {0} GROUP BY value";
- $query = str_format($queryFormat, $this->getTableName());
-
- $result = $this->getPaginatedResult($query, [], $n);
- $entryArray = [];
- while ($post = $result->fetchObject()) {
- $name = $post->id;
- $customcolumn = new CustomColumn($post->id, $name, $this);
- array_push($entryArray, $customcolumn->getEntry($post->count));
- }
- return $entryArray;
- }
-
- /**
- * Summary of getCustomByBook
- * @param Book $book
- * @return CustomColumn
- */
- public function getCustomByBook($book)
- {
- $queryFormat = "SELECT {0}.value AS value FROM {0} WHERE {0}.book = ?";
- $query = str_format($queryFormat, $this->getTableName());
-
- $result = Database::query($query, [$book->id], $this->databaseId);
- if ($post = $result->fetchObject()) {
- return new CustomColumn($post->value, $post->value, $this);
- }
- return new CustomColumn(null, localize("customcolumn.float.unknown"), $this);
- }
-
- /**
- * Summary of isSearchable
- * @return bool
- */
- public function isSearchable()
- {
- return true;
- }
-}
diff --git a/COPS/cops-3.1.3/src/Calibre/CustomColumnTypeInteger.php b/COPS/cops-3.1.3/src/Calibre/CustomColumnTypeInteger.php
deleted file mode 100644
index 7e0ae8f2..00000000
--- a/COPS/cops-3.1.3/src/Calibre/CustomColumnTypeInteger.php
+++ /dev/null
@@ -1,231 +0,0 @@
-
- */
-
-namespace SebLucas\Cops\Calibre;
-
-use SebLucas\Cops\Input\Config;
-use SebLucas\Cops\Input\Route;
-use SebLucas\Cops\Model\Entry;
-use SebLucas\Cops\Model\LinkNavigation;
-use UnexpectedValueException;
-
-class CustomColumnTypeInteger extends CustomColumnType
-{
- public const GET_PATTERN = '/^(-?[0-9]+)-(-?[0-9]+)$/';
-
- /**
- * Summary of __construct
- * @param int $customId
- * @param string $datatype
- * @param ?int $database
- * @param array $displaySettings
- * @throws \UnexpectedValueException
- */
- protected function __construct($customId, $datatype = self::TYPE_INT, $database = null, $displaySettings = [])
- {
- match ($datatype) {
- static::TYPE_INT => parent::__construct($customId, static::TYPE_INT, $database, $displaySettings),
- static::TYPE_FLOAT => parent::__construct($customId, static::TYPE_FLOAT, $database, $displaySettings),
- default => throw new UnexpectedValueException(),
- };
- }
-
- /**
- * Summary of getQuery
- * @param string|int|null $id
- * @return ?array{0: string, 1: array}
- */
- public function getQuery($id)
- {
- if (empty($id) && strval($id) !== '0' && in_array("custom", Config::get('show_not_set_filter'))) {
- $query = str_format(static::SQL_BOOKLIST_NULL, "{0}", "{1}", $this->getTableName());
- return [$query, []];
- }
- $query = str_format(static::SQL_BOOKLIST_VALUE, "{0}", "{1}", $this->getTableName());
- return [$query, [$id]];
- }
-
- /**
- * Summary of getQueryByRange
- * @param string $range
- * @throws \UnexpectedValueException
- * @return ?array{0: string, 1: array}
- */
- public function getQueryByRange($range)
- {
- $matches = [];
- if (!preg_match(static::GET_PATTERN, $range, $matches)) {
- throw new UnexpectedValueException();
- }
- $lower = $matches[1];
- $upper = $matches[2];
- $query = str_format(static::SQL_BOOKLIST_RANGE, "{0}", "{1}", $this->getTableName());
- return [$query, [$lower, $upper]];
- }
-
- /**
- * Summary of getFilter
- * @param string|int|null $id
- * @param ?string $parentTable
- * @return ?array{0: string, 1: array}
- */
- public function getFilter($id, $parentTable = null)
- {
- $linkTable = $this->getTableName();
- $linkColumn = "value";
- if (!empty($parentTable) && $parentTable != "books") {
- $filter = "exists (select null from {$linkTable}, books where {$parentTable}.book = books.id and {$linkTable}.book = books.id and {$linkTable}.{$linkColumn} = ?)";
- } else {
- $filter = "exists (select null from {$linkTable} where {$linkTable}.book = books.id and {$linkTable}.{$linkColumn} = ?)";
- }
- return [$filter, [$id]];
- }
-
- /**
- * Summary of getCustom
- * @param string|int|null $id
- * @return CustomColumn
- */
- public function getCustom($id)
- {
- return new CustomColumn($id, $id, $this);
- }
-
- /**
- * Summary of getAllCustomValuesFromDatabase
- * @param int $n
- * @param ?string $sort
- * @return array
- */
- protected function getAllCustomValuesFromDatabase($n = -1, $sort = null)
- {
- $queryFormat = "SELECT value AS id, count(*) AS count FROM {0} GROUP BY value";
- if (!empty($sort) && $sort == 'count') {
- $queryFormat .= ' ORDER BY count desc, value';
- } else {
- $queryFormat .= ' ORDER BY value';
- }
- $query = str_format($queryFormat, $this->getTableName());
-
- $result = $this->getPaginatedResult($query, [], $n);
- $entryArray = [];
- while ($post = $result->fetchObject()) {
- $name = $post->id;
- $customcolumn = new CustomColumn($post->id, $name, $this);
- array_push($entryArray, $customcolumn->getEntry($post->count));
- }
- return $entryArray;
- }
-
- /**
- * Summary of getCountByRange
- * @param mixed $page can be $columnType::PAGE_ALL or $columnType::PAGE_DETAIL
- * @param ?string $sort
- * @return array
- */
- public function getCountByRange($page, $sort = null)
- {
- $numtiles = Config::get('custom_integer_split_range');
- if ($numtiles <= 1) {
- $numtiles = Config::get('max_item_per_page');
- }
- if ($numtiles < 1) {
- $numtiles = 1;
- }
- // Equal height distribution using NTILE() has problem with overlapping range
- //$queryFormat = "SELECT groupid, MIN(value) AS min_value, MAX(value) AS max_value, COUNT(*) AS count FROM (SELECT value, NTILE({$numtiles}) OVER (ORDER BY value) AS groupid FROM {0}) x GROUP BY groupid";
- // Semi-equal height distribution using CUME_DIST()
- $queryFormat = "SELECT CAST(ROUND(dist * ({$numtiles} - 1), 0) AS INTEGER) AS groupid, MIN(value) AS min_value, MAX(value) AS max_value, COUNT(*) AS count FROM (SELECT value, CUME_DIST() OVER (ORDER BY value) dist FROM {0}) GROUP BY groupid";
- if (!empty($sort) && $sort == 'count') {
- $queryFormat .= ' ORDER BY count desc, groupid';
- } else {
- $queryFormat .= ' ORDER BY groupid';
- }
- $query = str_format($queryFormat, $this->getTableName());
- $result = Database::query($query, [], $this->databaseId);
-
- $entryArray = [];
- $label = 'range';
- while ($post = $result->fetchObject()) {
- $range = $post->min_value . "-" . $post->max_value;
- $params = ['custom' => $this->customId, 'range' => $range, 'db' => $this->databaseId];
- $href = Route::link($this->handler, $page, $params);
- array_push($entryArray, new Entry(
- $range,
- $this->getEntryId() . ':' . $label . ':' . $range,
- str_format(localize('bookword', $post->count), $post->count),
- 'text',
- [ new LinkNavigation($href, null, null) ],
- $this->databaseId,
- ucfirst($label),
- $post->count
- ));
- }
-
- return $entryArray;
- }
-
- /**
- * Summary of getCustomValuesByRange
- * @param string $range
- * @param ?string $sort
- * @return array
- */
- public function getCustomValuesByRange($range, $sort = null)
- {
- $matches = [];
- if (!preg_match(static::GET_PATTERN, $range, $matches)) {
- throw new UnexpectedValueException();
- }
- $lower = $matches[1];
- $upper = $matches[2];
- $queryFormat = "SELECT value AS id, count(*) AS count FROM {0} WHERE value >= ? AND value <= ? GROUP BY value";
- if (!empty($sort) && $sort == 'count') {
- $queryFormat .= ' ORDER BY count desc, value';
- } else {
- $queryFormat .= ' ORDER BY value';
- }
- $query = str_format($queryFormat, $this->getTableName());
- $result = Database::query($query, [$lower, $upper], $this->databaseId);
-
- $entryArray = [];
- while ($post = $result->fetchObject()) {
- $name = $post->id;
- $customcolumn = new CustomColumn($post->id, $name, $this);
- array_push($entryArray, $customcolumn->getEntry($post->count));
- }
-
- return $entryArray;
- }
-
- /**
- * Summary of getCustomByBook
- * @param Book $book
- * @return CustomColumn
- */
- public function getCustomByBook($book)
- {
- $queryFormat = "SELECT {0}.value AS value FROM {0} WHERE {0}.book = ?";
- $query = str_format($queryFormat, $this->getTableName());
-
- $result = Database::query($query, [$book->id], $this->databaseId);
- if ($post = $result->fetchObject()) {
- return new CustomColumn($post->value, $post->value, $this);
- }
- return new CustomColumn(null, localize("customcolumn.int.unknown"), $this);
- }
-
- /**
- * Summary of isSearchable
- * @return bool
- */
- public function isSearchable()
- {
- return true;
- }
-}
diff --git a/COPS/cops-3.1.3/src/Calibre/CustomColumnTypeRating.php b/COPS/cops-3.1.3/src/Calibre/CustomColumnTypeRating.php
deleted file mode 100644
index 82e40986..00000000
--- a/COPS/cops-3.1.3/src/Calibre/CustomColumnTypeRating.php
+++ /dev/null
@@ -1,169 +0,0 @@
-
- */
-
-namespace SebLucas\Cops\Calibre;
-
-use SebLucas\Cops\Model\Entry;
-
-class CustomColumnTypeRating extends CustomColumnType
-{
- public const SQL_BOOKLIST = 'select {0} from books ' . Book::SQL_BOOKS_LEFT_JOIN . '
- left join {2} on {2}.book = books.id
- left join {3} on {3}.id = {2}.{4}
- where {3}.value = ? order by books.sort';
- public const SQL_BOOKLIST_NULL = 'select {0} from books ' . Book::SQL_BOOKS_LEFT_JOIN . '
- left join {2} on {2}.book = books.id
- left join {3} on {3}.id = {2}.{4}
- where ((books.id not in (select {2}.book from {2})) or ({3}.value = 0)) {1} order by books.sort';
-
- /**
- * Summary of __construct
- * @param int $customId
- * @param ?int $database
- * @param array $displaySettings
- */
- protected function __construct($customId, $database = null, $displaySettings = [])
- {
- parent::__construct($customId, static::TYPE_RATING, $database, $displaySettings);
- }
-
- /**
- * Get the name of the linking sqlite table for this column
- * (or NULL if there is no linktable)
- *
- * @return string
- */
- protected function getTableLinkName()
- {
- return "books_custom_column_{$this->customId}_link";
- }
-
- /**
- * Get the name of the linking column in the linktable
- *
- * @return string
- */
- protected function getTableLinkColumn()
- {
- return "value";
- }
-
- /**
- * Summary of getQuery
- * @param string|int|null $id
- * @return ?array{0: string, 1: array}
- */
- public function getQuery($id)
- {
- if (empty($id)) {
- $query = str_format(static::SQL_BOOKLIST_NULL, "{0}", "{1}", $this->getTableLinkName(), $this->getTableName(), $this->getTableLinkColumn());
- return [$query, []];
- } else {
- $query = str_format(static::SQL_BOOKLIST, "{0}", "{1}", $this->getTableLinkName(), $this->getTableName(), $this->getTableLinkColumn());
- return [$query, [$id]];
- }
- }
-
- /**
- * Summary of getFilter
- * @param string|int|null $id
- * @param ?string $parentTable
- * @return ?array{0: string, 1: array}
- */
- public function getFilter($id, $parentTable = null)
- {
- // @todo do we want to filter on ratings Id or Value here
- return ["", []];
- }
-
- /**
- * Summary of getCustom
- * @param string|int|null $id
- * @return CustomColumn
- */
- public function getCustom($id)
- {
- return new CustomColumn($id, str_format(localize("customcolumn.stars", $id / 2), $id / 2), $this);
- }
-
- /**
- * Summary of getAllCustomValuesFromDatabase
- * @param int $n
- * @param ?string $sort
- * @return array
- */
- protected function getAllCustomValuesFromDatabase($n = -1, $sort = null)
- {
- $queryFormat = "SELECT coalesce({0}.value, 0) AS value, count(*) AS count FROM books LEFT JOIN {1} ON books.id = {1}.book LEFT JOIN {0} ON {0}.id = {1}.value GROUP BY coalesce({0}.value, -1)";
- $query = str_format($queryFormat, $this->getTableName(), $this->getTableLinkName());
- $result = Database::query($query, [], $this->databaseId);
-
- $countArray = [0 => 0, 2 => 0, 4 => 0, 6 => 0, 8 => 0, 10 => 0];
- while ($row = $result->fetchObject()) {
- $countArray[$row->value] = $row->count;
- }
-
- $entryArray = [];
-
- // @todo align with other custom columns
- for ($i = 0; $i <= 5; $i++) {
- $id = $i * 2;
- $count = $countArray[$id];
- $name = str_format(localize("customcolumn.stars", $i), $i);
- $customcolumn = new CustomColumn($id, $name, $this);
- array_push($entryArray, $customcolumn->getEntry($count));
- }
-
- return $entryArray;
- }
-
- /**
- * Summary of getDistinctValueCount
- * @return int
- */
- public function getDistinctValueCount()
- {
- return count($this->getAllCustomValues());
- }
-
- /**
- * Summary of getContent
- * @param int $count
- * @return string
- */
- public function getContent($count = 0)
- {
- return localize("customcolumn.description.rating");
- }
-
- /**
- * Summary of getCustomByBook
- * @param mixed $book
- * @return CustomColumn
- */
- public function getCustomByBook($book)
- {
- $queryFormat = "SELECT {0}.value AS value FROM {0}, {1} WHERE {0}.id = {1}.{2} AND {1}.book = ?";
- $query = str_format($queryFormat, $this->getTableName(), $this->getTableLinkName(), $this->getTableLinkColumn());
-
- $result = Database::query($query, [$book->id], $this->databaseId);
- if ($post = $result->fetchObject()) {
- return new CustomColumn($post->value, str_format(localize("customcolumn.stars", $post->value / 2), $post->value / 2), $this);
- }
- return new CustomColumn(null, localize("customcolumn.rating.unknown"), $this);
- }
-
- /**
- * Summary of isSearchable
- * @return bool
- */
- public function isSearchable()
- {
- return true;
- }
-}
diff --git a/COPS/cops-3.1.3/src/Calibre/CustomColumnTypeSeries.php b/COPS/cops-3.1.3/src/Calibre/CustomColumnTypeSeries.php
deleted file mode 100644
index ca7e54b7..00000000
--- a/COPS/cops-3.1.3/src/Calibre/CustomColumnTypeSeries.php
+++ /dev/null
@@ -1,151 +0,0 @@
-
- */
-
-namespace SebLucas\Cops\Calibre;
-
-use SebLucas\Cops\Input\Config;
-use SebLucas\Cops\Model\Entry;
-
-class CustomColumnTypeSeries extends CustomColumnType
-{
- /**
- * Summary of __construct
- * @param int $customId
- * @param ?int $database
- * @param array $displaySettings
- */
- protected function __construct($customId, $database = null, $displaySettings = [])
- {
- parent::__construct($customId, static::TYPE_SERIES, $database, $displaySettings);
- }
-
- /**
- * Get the name of the linking sqlite table for this column
- * (or NULL if there is no linktable)
- *
- * @return string
- */
- protected function getTableLinkName()
- {
- return "books_custom_column_{$this->customId}_link";
- }
-
- /**
- * Get the name of the linking column in the linktable
- *
- * @return string
- */
- protected function getTableLinkColumn()
- {
- return "value";
- }
-
- /**
- * Summary of getQuery
- * @param string|int|null $id
- * @return ?array{0: string, 1: array}
- */
- public function getQuery($id)
- {
- if (empty($id) && in_array("custom", Config::get('show_not_set_filter'))) {
- $query = str_format(static::SQL_BOOKLIST_NULL, "{0}", "{1}", $this->getTableLinkName());
- return [$query, []];
- }
- $query = str_format(static::SQL_BOOKLIST_LINK, "{0}", "{1}", $this->getTableLinkName(), $this->getTableLinkColumn());
- return [$query, [$id]];
- }
-
- /**
- * Summary of getFilter
- * @param string|int|null $id
- * @param ?string $parentTable
- * @return ?array{0: string, 1: array}
- */
- public function getFilter($id, $parentTable = null)
- {
- $linkTable = $this->getTableLinkName();
- $linkColumn = $this->getTableLinkColumn();
- if (!empty($parentTable) && $parentTable != "books") {
- $filter = "exists (select null from {$linkTable}, books where {$parentTable}.book = books.id and {$linkTable}.book = books.id and {$linkTable}.{$linkColumn} = ?)";
- } else {
- $filter = "exists (select null from {$linkTable} where {$linkTable}.book = books.id and {$linkTable}.{$linkColumn} = ?)";
- }
- return [$filter, [$id]];
- }
-
- /**
- * Summary of getCustom
- * @param string|int|null $id
- * @return CustomColumn
- */
- public function getCustom($id)
- {
- $query = str_format("SELECT id, value AS name FROM {0} WHERE id = ?", $this->getTableName());
- $result = Database::query($query, [$id], $this->databaseId);
- if ($post = $result->fetchObject()) {
- return new CustomColumn($id, $post->name, $this);
- }
- return new CustomColumn(null, localize("customcolumn.boolean.unknown"), $this);
- }
-
- /**
- * Summary of getAllCustomValuesFromDatabase
- * @param int $n
- * @param ?string $sort
- * @return array
- */
- protected function getAllCustomValuesFromDatabase($n = -1, $sort = null)
- {
- $queryFormat = "SELECT {0}.id AS id, {0}.value AS name, count(*) AS count FROM {0}, {1} WHERE {0}.id = {1}.{2} GROUP BY {0}.id, {0}.value ORDER BY {0}.value";
- $query = str_format($queryFormat, $this->getTableName(), $this->getTableLinkName(), $this->getTableLinkColumn());
-
- $result = $this->getPaginatedResult($query, [], $n);
- $entryArray = [];
- while ($post = $result->fetchObject()) {
- $customcolumn = new CustomColumn($post->id, $post->name, $this);
- array_push($entryArray, $customcolumn->getEntry($post->count));
- }
- return $entryArray;
- }
-
- /**
- * Summary of getContent
- * @param int $count
- * @return string
- */
- public function getContent($count = 0)
- {
- return str_format(localize("customcolumn.description.series", $count), $count);
- }
-
- /**
- * Summary of getCustomByBook
- * @param mixed $book
- * @return CustomColumn
- */
- public function getCustomByBook($book)
- {
- $queryFormat = "SELECT {0}.id AS id, {0}.{2} AS value, {1}.{2} AS name, {1}.extra AS extra FROM {0}, {1} WHERE {0}.id = {1}.{2} AND {1}.book = ?";
- $query = str_format($queryFormat, $this->getTableName(), $this->getTableLinkName(), $this->getTableLinkColumn());
-
- $result = Database::query($query, [$book->id], $this->databaseId);
- if ($post = $result->fetchObject()) {
- return new CustomColumn($post->id, $post->value . " [" . $post->extra . "]", $this);
- }
- return new CustomColumn(null, "", $this);
- }
-
- /**
- * Summary of isSearchable
- * @return bool
- */
- public function isSearchable()
- {
- return true;
- }
-}
diff --git a/COPS/cops-3.1.3/src/Calibre/CustomColumnTypeText.php b/COPS/cops-3.1.3/src/Calibre/CustomColumnTypeText.php
deleted file mode 100644
index 89c0cdc8..00000000
--- a/COPS/cops-3.1.3/src/Calibre/CustomColumnTypeText.php
+++ /dev/null
@@ -1,177 +0,0 @@
-
- */
-
-namespace SebLucas\Cops\Calibre;
-
-use SebLucas\Cops\Input\Config;
-use SebLucas\Cops\Model\Entry;
-use UnexpectedValueException;
-
-class CustomColumnTypeText extends CustomColumnType
-{
- /**
- * Summary of __construct
- * @param int $customId
- * @param string $datatype
- * @param ?int $database
- * @param array $displaySettings
- * @return void
- * @throws \UnexpectedValueException
- */
- protected function __construct($customId, $datatype = self::TYPE_TEXT, $database = null, $displaySettings = [])
- {
- switch ($datatype) {
- case static::TYPE_TEXT:
- parent::__construct($customId, static::TYPE_TEXT, $database, $displaySettings);
- return;
- case static::TYPE_CSV:
- parent::__construct($customId, static::TYPE_CSV, $database, $displaySettings);
- return;
- case static::TYPE_ENUM:
- parent::__construct($customId, static::TYPE_ENUM, $database, $displaySettings);
- return;
- case static::TYPE_SERIES:
- parent::__construct($customId, static::TYPE_SERIES, $database, $displaySettings);
- return;
- default:
- throw new UnexpectedValueException();
- }
- }
-
- /**
- * Get the name of the linking sqlite table for this column
- * (or NULL if there is no linktable)
- *
- * @return string
- */
- protected function getTableLinkName()
- {
- return "books_custom_column_{$this->customId}_link";
- }
-
- /**
- * Get the name of the linking column in the linktable
- *
- * @return string
- */
- protected function getTableLinkColumn()
- {
- return "value";
- }
-
- /**
- * Summary of getQuery
- * @param string|int|null $id
- * @return ?array{0: string, 1: array}
- */
- public function getQuery($id)
- {
- if (empty($id) && in_array("custom", Config::get('show_not_set_filter'))) {
- $query = str_format(static::SQL_BOOKLIST_NULL, "{0}", "{1}", $this->getTableLinkName());
- return [$query, []];
- }
- $query = str_format(static::SQL_BOOKLIST_LINK, "{0}", "{1}", $this->getTableLinkName(), $this->getTableLinkColumn());
- return [$query, [$id]];
- }
-
- /**
- * Summary of getFilter
- * @param string|int|null $id
- * @param ?string $parentTable
- * @return ?array{0: string, 1: array}
- */
- public function getFilter($id, $parentTable = null)
- {
- $linkTable = $this->getTableLinkName();
- $linkColumn = $this->getTableLinkColumn();
- if (!empty($parentTable) && $parentTable != "books") {
- $filter = "exists (select null from {$linkTable}, books where {$parentTable}.book = books.id and {$linkTable}.book = books.id and {$linkTable}.{$linkColumn} = ?)";
- } else {
- $filter = "exists (select null from {$linkTable} where {$linkTable}.book = books.id and {$linkTable}.{$linkColumn} = ?)";
- }
- return [$filter, [$id]];
- }
-
- /**
- * Summary of getCustom
- * @param string|int|null $id
- * @return CustomColumn
- */
- public function getCustom($id)
- {
- $query = str_format("SELECT id, value AS name FROM {0} WHERE id = ?", $this->getTableName());
- $result = Database::query($query, [$id], $this->databaseId);
- if ($post = $result->fetchObject()) {
- return new CustomColumn($id, $post->name, $this);
- }
- return new CustomColumn(null, localize("customcolumn.boolean.unknown"), $this);
- }
-
- /**
- * Summary of getAllCustomValuesFromDatabase
- * @param int $n
- * @param ?string $sort
- * @return array
- */
- protected function getAllCustomValuesFromDatabase($n = -1, $sort = null)
- {
- $queryFormat = "SELECT {0}.id AS id, {0}.value AS name, count(*) AS count FROM {0}, {1} WHERE {0}.id = {1}.{2} GROUP BY {0}.id, {0}.value ORDER BY {0}.value";
- $query = str_format($queryFormat, $this->getTableName(), $this->getTableLinkName(), $this->getTableLinkColumn());
-
- $result = $this->getPaginatedResult($query, [], $n);
- $entryArray = [];
- while ($post = $result->fetchObject()) {
- $customcolumn = new CustomColumn($post->id, $post->name, $this);
- array_push($entryArray, $customcolumn->getEntry($post->count));
- }
- return $entryArray;
- }
-
- /**
- * Summary of getCustomByBook
- * @param mixed $book
- * @throws \UnexpectedValueException
- * @return CustomColumn
- */
- public function getCustomByBook($book)
- {
- $queryFormat = match ($this->datatype) {
- static::TYPE_TEXT => "SELECT {0}.id AS id, {0}.{2} AS name FROM {0}, {1} WHERE {0}.id = {1}.{2} AND {1}.book = ? ORDER BY {0}.value",
- static::TYPE_CSV => "SELECT {0}.id AS id, {0}.{2} AS name FROM {0}, {1} WHERE {0}.id = {1}.{2} AND {1}.book = ? ORDER BY {0}.value",
- static::TYPE_ENUM => "SELECT {0}.id AS id, {0}.{2} AS name FROM {0}, {1} WHERE {0}.id = {1}.{2} AND {1}.book = ?",
- static::TYPE_SERIES => "SELECT {0}.id AS id, {1}.{2} AS name, {1}.extra AS extra FROM {0}, {1} WHERE {0}.id = {1}.{2} AND {1}.book = ?",
- default => throw new UnexpectedValueException(),
- };
- $query = str_format($queryFormat, $this->getTableName(), $this->getTableLinkName(), $this->getTableLinkColumn());
-
- $result = Database::query($query, [$book->id], $this->databaseId);
- // handle case where we have several values, e.g. array of text for type 2 (csv)
- if ($this->datatype === static::TYPE_CSV) {
- $idArray = [];
- $nameArray = [];
- while ($post = $result->fetchObject()) {
- array_push($idArray, $post->id);
- array_push($nameArray, $post->name);
- }
- return new CustomColumn(implode(",", $idArray), implode(",", $nameArray), $this);
- }
- if ($post = $result->fetchObject()) {
- return new CustomColumn($post->id, $post->name, $this);
- }
- return new CustomColumn(null, "", $this);
- }
-
- /**
- * Summary of isSearchable
- * @return bool
- */
- public function isSearchable()
- {
- return true;
- }
-}
diff --git a/COPS/cops-3.1.3/src/Calibre/Data.php b/COPS/cops-3.1.3/src/Calibre/Data.php
deleted file mode 100644
index a7e3f3f3..00000000
--- a/COPS/cops-3.1.3/src/Calibre/Data.php
+++ /dev/null
@@ -1,326 +0,0 @@
-
- */
-
-namespace SebLucas\Cops\Calibre;
-
-use SebLucas\Cops\Input\Config;
-use SebLucas\Cops\Input\Route;
-use SebLucas\Cops\Model\LinkEntry;
-use SebLucas\Cops\Output\Response;
-
-class Data
-{
- public const SQL_TABLE = "data";
- public const SQL_COLUMNS = "id, name, format";
- public const SQL_LINK_TABLE = "data";
- public const SQL_LINK_COLUMN = "id";
- public const SQL_SORT = "name";
- public static string $handler = "fetch";
- /** @var int */
- public $id;
- public string $name;
- public string $format;
- public string $realFormat;
- public string $extension;
- /** @var ?Book */
- public $book;
- /** @var ?int */
- protected $databaseId;
- public bool $updateForKepub = false;
-
- /** @var array */
- public static $mimetypes = [
- 'aac' => 'audio/aac',
- 'azw' => 'application/x-mobipocket-ebook',
- 'azw1' => 'application/x-topaz-ebook',
- 'azw2' => 'application/x-kindle-application',
- 'azw3' => 'application/x-mobi8-ebook',
- 'cbz' => 'application/x-cbz',
- 'cbr' => 'application/x-cbr',
- 'css' => 'text/css',
- 'djv' => 'image/vnd.djvu',
- 'djvu' => 'image/vnd.djvu',
- 'doc' => 'application/msword',
- 'epub' => 'application/epub+zip',
- 'fb2' => 'text/fb2+xml',
- 'gif' => 'image/gif',
- 'ibooks' => 'application/x-ibooks+zip',
- 'jpeg' => 'image/jpeg',
- 'jpg' => 'image/jpeg',
- 'kepub' => 'application/epub+zip',
- 'kobo' => 'application/x-koboreader-ebook',
- 'm4a' => 'audio/mp4',
- 'm4b' => 'audio/mp4',
- 'mobi' => 'application/x-mobipocket-ebook',
- 'mp3' => 'audio/mpeg',
- 'lit' => 'application/x-ms-reader',
- 'lrs' => 'text/x-sony-bbeb+xml',
- 'lrf' => 'application/x-sony-bbeb',
- 'lrx' => 'application/x-sony-bbeb',
- 'ncx' => 'application/x-dtbncx+xml',
- 'opf' => 'application/oebps-package+xml',
- 'otf' => 'font/otf',
- 'pdb' => 'application/vnd.palm',
- 'pdf' => 'application/pdf',
- 'png' => 'image/png',
- 'prc' => 'application/x-mobipocket-ebook',
- 'rtf' => 'application/rtf',
- 'svg' => 'image/svg+xml',
- 'ttf' => 'font/ttf',
- 'tpz' => 'application/x-topaz-ebook',
- 'txt' => 'text/plain',
- 'wav' => 'audio/wav',
- 'webp' => 'image/webp',
- 'wmf' => 'image/wmf',
- 'woff' => 'font/woff',
- 'woff2' => 'font/woff2',
- 'xhtml' => 'application/xhtml+xml',
- 'xml' => 'application/xhtml+xml',
- 'xpgt' => 'application/adobe-page-template+xml',
- 'zip' => 'application/zip',
- ];
-
- /**
- * Summary of __construct
- * @param object $post
- * @param ?Book $book
- */
- public function __construct($post, $book = null)
- {
- $this->id = $post->id;
- $this->name = $post->name;
- $this->format = $post->format;
- $this->realFormat = str_replace("ORIGINAL_", "", $post->format);
- $this->extension = strtolower($this->realFormat);
- $this->setBook($book);
- }
-
- /**
- * Summary of setBook
- * @param ?Book $book
- * @return void
- */
- public function setBook($book)
- {
- $this->book = $book;
- $this->databaseId = ($nullsafeVariable1 = $book) ? $nullsafeVariable1->getDatabaseId() : null;
- // this is set on book in JsonRenderer now
- if (!is_null($book) && $book->updateForKepub && $this->isEpubValidOnKobo()) {
- $this->updateForKepub = true;
- }
- }
-
- /**
- * Summary of isKnownType
- * @return bool
- */
- public function isKnownType()
- {
- return array_key_exists($this->extension, static::$mimetypes);
- }
-
- /**
- * Summary of getMimeType
- * @return string
- */
- public function getMimeType()
- {
- if ($this->isKnownType()) {
- return static::$mimetypes[$this->extension];
- }
- $default = "application/octet-stream";
- return Response::getMimeType($this->getLocalPath()) ?? $default;
- }
-
- /**
- * Summary of isEpubValidOnKobo
- * @return bool
- */
- public function isEpubValidOnKobo()
- {
- return $this->format == "EPUB" || $this->format == "KEPUB";
- }
-
- /**
- * Summary of getFilename
- * @return string
- */
- public function getFilename()
- {
- return $this->name . "." . strtolower($this->format);
- }
-
- /**
- * Summary of getUpdatedFilename
- * @return string
- */
- public function getUpdatedFilename()
- {
- return $this->book->getAuthorsSort() . " - " . $this->book->title;
- }
-
- /**
- * Summary of getUpdatedFilenameEpub
- * @return string
- */
- public function getUpdatedFilenameEpub()
- {
- return $this->getUpdatedFilename() . ".epub";
- }
-
- /**
- * Summary of getUpdatedFilenameKepub
- * @return string
- */
- public function getUpdatedFilenameKepub()
- {
- $str = $this->getUpdatedFilename() . ".kepub.epub";
- return str_replace(
- [':', '#', '&'],
- ['-', '-', ' '],
- $str
- );
- }
-
- /**
- * Summary of getDataLink
- * @param string $rel
- * @param ?string $title
- * @param bool $view
- * @return LinkEntry
- */
- public function getDataLink($rel, $title = null, $view = false)
- {
- if ($rel == LinkEntry::OPDS_ACQUISITION_TYPE && Config::get('use_url_rewriting') == "1") {
- return $this->getHtmlLinkWithRewriting($title, $view);
- }
-
- return static::getLink($this->book, $this->extension, $this->getMimeType(), $rel, $this->getFilename(), $this->id, $title, $view);
- }
-
- /**
- * Summary of getHtmlLink
- * @return string
- */
- public function getHtmlLink()
- {
- return $this->getDataLink(LinkEntry::OPDS_ACQUISITION_TYPE)->href;
- }
-
- /**
- * Summary of getViewHtmlLink
- * @return string
- */
- public function getViewHtmlLink()
- {
- return $this->getDataLink(LinkEntry::OPDS_ACQUISITION_TYPE, null, true)->href;
- }
-
- /**
- * Summary of getLocalPath
- * @return string
- */
- public function getLocalPath()
- {
- return $this->book->path . "/" . $this->getFilename();
- }
-
- /**
- * Summary of getHtmlLinkWithRewriting
- * @param ?string $title
- * @param bool $view
- * @deprecated 3.1.0 use route urls instead
- * @return LinkEntry
- */
- public function getHtmlLinkWithRewriting($title = null, $view = false)
- {
- $database = "";
- if (!is_null($this->databaseId)) {
- $database = $this->databaseId . "/";
- }
-
- $prefix = "download";
- if ($view) {
- $prefix = "view";
- }
- $href = $prefix . "/" . $this->id . "/" . $database;
-
- // this is set on book in JsonRenderer now
- if ($this->updateForKepub) {
- $href .= rawurlencode($this->getUpdatedFilenameKepub());
- } else {
- $href .= rawurlencode($this->getFilename());
- }
- return new LinkEntry(
- Route::path($href),
- $this->getMimeType(),
- LinkEntry::OPDS_ACQUISITION_TYPE,
- $title
- );
- }
-
- /**
- * Summary of getDataByBook
- * @param mixed $book
- * @return array
- */
- public static function getDataByBook($book)
- {
- return Book::getDataByBook($book);
- }
-
- /**
- * Summary of getLink
- * @param Book $book
- * @param string $type
- * @param string $mime
- * @param string $rel
- * @param string $filename
- * @param ?int $idData
- * @param ?string $title
- * @param bool $view
- * @return LinkEntry
- */
- public static function getLink($book, $type, $mime, $rel, $filename, $idData, $title = null, $view = false)
- {
- if (!empty(Config::get('calibre_external_storage')) && str_starts_with($book->path, (string) Config::get('calibre_external_storage'))) {
- return new LinkEntry(
- $book->path . "/" . rawurlencode($filename),
- $mime,
- $rel,
- $title
- );
- }
- // moved image-specific code from Data to Cover
- if (Database::useAbsolutePath($book->getDatabaseId()) ||
- ($type == "epub" && Config::get('update_epub-metadata'))) {
- $params = ['db' => $book->getDatabaseId()];
- if (Config::get('use_route_urls') && is_null($params['db'])) {
- $params['db'] = 0;
- }
- $params['type'] = $type;
- $params['data'] = $idData;
- if ($view) {
- $params['view'] = 1;
- }
- return new LinkEntry(
- Route::link(static::$handler, null, $params),
- $mime,
- $rel,
- $title
- );
- }
-
- return new LinkEntry(
- Route::path(str_replace('%2F', '/', rawurlencode($book->path . "/" . $filename))),
- $mime,
- $rel,
- $title
- );
- }
-}
diff --git a/COPS/cops-3.1.3/src/Calibre/Database.php b/COPS/cops-3.1.3/src/Calibre/Database.php
deleted file mode 100644
index a8836a97..00000000
--- a/COPS/cops-3.1.3/src/Calibre/Database.php
+++ /dev/null
@@ -1,440 +0,0 @@
-
- */
-
-namespace SebLucas\Cops\Calibre;
-
-use SebLucas\Cops\Input\Config;
-use SebLucas\Cops\Input\Route;
-use SebLucas\Cops\Language\Translation;
-use SebLucas\Cops\Output\Response;
-use Exception;
-use PDO;
-
-class Database
-{
- public const KEEP_STATS = false;
- public const CALIBRE_DB_NAME = 'metadata.db';
- public const NOTES_DIR_NAME = '.calnotes';
- public const NOTES_DB_NAME = 'notes.db';
- /** @var ?PDO */
- protected static $db = null;
- protected static ?string $dbFileName = null;
- /** @var ?PDO */
- protected static $notesDb = null;
- protected static int $count = 0;
- /** @var array */
- protected static $queries = [];
-
- /**
- * Summary of getDbStatistics
- * @return array
- */
- public static function getDbStatistics()
- {
- return ['count' => static::$count, 'queries' => static::$queries];
- }
-
- /**
- * Summary of isMultipleDatabaseEnabled
- * @return bool
- */
- public static function isMultipleDatabaseEnabled()
- {
- return is_array(Config::get('calibre_directory'));
- }
-
- /**
- * Summary of useAbsolutePath
- * @param ?int $database
- * @return bool
- */
- public static function useAbsolutePath($database)
- {
- $path = static::getDbDirectory($database);
- return preg_match('/^\//', $path) || // Linux /
- preg_match('/^\w\:/', $path); // Windows X:
- }
-
- /**
- * Summary of noDatabaseSelected
- * @param ?int $database
- * @return bool
- */
- public static function noDatabaseSelected($database)
- {
- return static::isMultipleDatabaseEnabled() && is_null($database);
- }
-
- /**
- * Summary of getDbList
- * @return array
- */
- public static function getDbList()
- {
- if (static::isMultipleDatabaseEnabled()) {
- return Config::get('calibre_directory');
- } else {
- return ["" => Config::get('calibre_directory')];
- }
- }
-
- /**
- * Summary of getDbNameList
- * @return array
- */
- public static function getDbNameList()
- {
- if (static::isMultipleDatabaseEnabled()) {
- return array_keys(Config::get('calibre_directory'));
- } else {
- return [""];
- }
- }
-
- /**
- * Summary of getDbName
- * @param ?int $database
- * @return string
- */
- public static function getDbName($database)
- {
- if (static::isMultipleDatabaseEnabled()) {
- if (is_null($database)) {
- $database = 0;
- }
- $array = array_keys(Config::get('calibre_directory'));
- return $array[$database];
- }
- return "";
- }
-
- /**
- * Summary of getDbDirectory
- * @param ?int $database
- * @return string
- */
- public static function getDbDirectory($database)
- {
- if (static::isMultipleDatabaseEnabled()) {
- if (is_null($database)) {
- $database = 0;
- }
- $array = array_values(Config::get('calibre_directory'));
- return $array[$database];
- }
- return Config::get('calibre_directory');
- }
-
- // -DC- Add image directory
- /**
- * Summary of getImgDirectory
- * @param ?int $database
- * @return string
- */
- public static function getImgDirectory($database)
- {
- if (static::isMultipleDatabaseEnabled()) {
- if (is_null($database)) {
- $database = 0;
- }
- $array = array_values(Config::get('image_directory'));
- return $array[$database];
- }
- return Config::get('image_directory');
- }
-
- /**
- * Summary of getDbFileName
- * @param ?int $database
- * @return string
- */
- public static function getDbFileName($database)
- {
- return static::getDbDirectory($database) . 'metadata.db';
- }
-
- /**
- * Summary of error
- * @param ?int $database
- * @throws \Exception
- * @return never
- */
- protected static function error($database)
- {
- if (php_sapi_name() != "cli") {
- Response::redirect(Route::link("check") . "?err=1");
- }
- throw new Exception("Database <{$database}> not found.");
- }
-
- /**
- * Summary of getDb
- * @param ?int $database
- * @return \PDO
- */
- public static function getDb($database = null)
- {
- if (static::KEEP_STATS) {
- static::$count += 1;
- }
- if (is_null(static::$db)) {
- try {
- if (is_readable(static::getDbFileName($database))) {
- static::$db = new PDO('sqlite:' . static::getDbFileName($database));
- if (Translation::useNormAndUp()) {
- static::$db->sqliteCreateFunction('normAndUp', function ($s) {
- return Translation::normAndUp($s);
- }, 1);
- }
- static::$dbFileName = static::getDbFileName($database);
- } else {
- static::error($database);
- }
- } catch (Exception) {
- static::error($database);
- }
- }
- return static::$db;
- }
-
- /**
- * Summary of checkDatabaseAvailability
- * @param ?int $database
- * @return bool
- */
- public static function checkDatabaseAvailability($database)
- {
- if (static::noDatabaseSelected($database)) {
- for ($i = 0; $i < count(static::getDbList()); $i++) {
- static::getDb($i);
- static::clearDb();
- }
- } else {
- static::getDb($database);
- }
- return true;
- }
-
- /**
- * Summary of clearDb
- * @return void
- */
- public static function clearDb()
- {
- static::$db = null;
- static::$notesDb = null;
- }
-
- /**
- * Summary of querySingle
- * @param string $query
- * @param ?int $database
- * @return mixed
- */
- public static function querySingle($query, $database = null)
- {
- if (static::KEEP_STATS) {
- array_push(static::$queries, $query);
- }
- return static::getDb($database)->query($query)->fetchColumn();
- }
-
-
- /**
- * Summary of query
- * @param string $query
- * @param array $params
- * @param ?int $database
- * @return \PDOStatement
- */
- public static function query($query, $params = [], $database = null)
- {
- if (static::KEEP_STATS) {
- array_push(static::$queries, $query);
- }
- if (count($params) > 0) {
- $result = static::getDb($database)->prepare($query);
- $result->execute($params);
- } else {
- $result = static::getDb($database)->query($query);
- }
- return $result;
- }
-
- /**
- * Summary of queryTotal
- * @param string $query
- * @param string $columns
- * @param string $filter
- * @param array $params
- * @param int $n
- * @param ?int $database
- * @param ?int $numberPerPage
- * @return array{0: integer, 1: \PDOStatement}
- */
- public static function queryTotal($query, $columns, $filter, $params, $n, $database = null, $numberPerPage = null)
- {
- if (static::KEEP_STATS) {
- array_push(static::$queries, $query);
- }
- $totalResult = -1;
-
- if (Translation::useNormAndUp()) {
- $query = preg_replace("/upper/", "normAndUp", $query);
- $columns = preg_replace("/upper/", "normAndUp", $columns);
- }
-
- if (is_null($numberPerPage)) {
- $numberPerPage = Config::get('max_item_per_page');
- }
-
- if ($numberPerPage != -1 && $n != -1) {
- // First check total number of results
- $totalResult = static::countFilter($query, 'count(*)', $filter, $params, $database);
-
- // Next modify the query and params
- $query .= " limit ?, ?";
- array_push($params, ($n - 1) * $numberPerPage, $numberPerPage);
- }
- $result = static::getDb($database)->prepare(str_format($query, $columns, $filter));
- $result->execute($params);
- return [$totalResult, $result];
- }
-
- /**
- * Summary of queryFilter
- * @param string $query
- * @param string $columns
- * @param string $filter
- * @param array $params
- * @param int $n
- * @param ?int $database
- * @param ?int $numberPerPage
- * @return \PDOStatement
- */
- public static function queryFilter($query, $columns, $filter, $params, $n, $database = null, $numberPerPage = null)
- {
- if (static::KEEP_STATS) {
- array_push(static::$queries, $query);
- }
- if (Translation::useNormAndUp()) {
- $query = preg_replace("/upper/", "normAndUp", $query);
- $columns = preg_replace("/upper/", "normAndUp", $columns);
- }
-
- if (is_null($numberPerPage)) {
- $numberPerPage = Config::get('max_item_per_page');
- }
-
- if ($numberPerPage != -1 && $n != -1) {
- // Next modify the query and params
- $query .= " limit ?, ?";
- array_push($params, ($n - 1) * $numberPerPage, $numberPerPage);
- }
-
- $result = static::getDb($database)->prepare(str_format($query, $columns, $filter));
- $result->execute($params);
- return $result;
- }
-
- /**
- * Summary of countFilter
- * @param string $query
- * @param string $columns
- * @param string $filter
- * @param array $params
- * @param ?int $database
- * @return integer
- */
- public static function countFilter($query, $columns = 'count(*)', $filter = '', $params = [], $database = null)
- {
- if (static::KEEP_STATS) {
- array_push(static::$queries, $query);
- }
- // assuming order by ... is at the end of the query here
- $query = preg_replace('/\s+order\s+by\s+[\w.]+(\s+(asc|desc)|).*$/i', '', $query);
- $result = static::getDb($database)->prepare(str_format($query, $columns, $filter));
- $result->execute($params);
- $totalResult = $result->fetchColumn();
- return $totalResult;
- }
-
- /**
- * Summary of getDbSchema
- * @param ?int $database
- * @param ?string $type get table or view entries
- * @return array
- */
- public static function getDbSchema($database = null, $type = null)
- {
- $query = 'SELECT type, name, tbl_name, rootpage, sql FROM sqlite_schema';
- $params = [];
- if (!empty($type)) {
- $query .= ' WHERE type = ?';
- $params[] = $type;
- }
- $entries = [];
- $result = static::query($query, $params, $database);
- while ($post = $result->fetchObject()) {
- $entry = (array) $post;
- array_push($entries, $entry);
- }
- return $entries;
- }
-
- /**
- * Summary of getTableInfo
- * @param ?int $database
- * @param string $name table or view name
- * @return array
- */
- public static function getTableInfo($database = null, $name = 'books')
- {
- $query = "PRAGMA table_info({$name})";
- $params = [];
- $result = static::query($query, $params, $database);
- $entries = [];
- while ($post = $result->fetchObject()) {
- $entry = (array) $post;
- array_push($entries, $entry);
- }
- return $entries;
- }
-
- /**
- * Summary of getUserVersion
- * @param ?int $database
- * @return int
- */
- public static function getUserVersion($database = null)
- {
- $query = "PRAGMA user_version";
- $result = static::querySingle($query, $database);
- return $result;
- }
-
- /**
- * Summary of getNotesDb
- * @param ?int $database
- * @return PDO|null
- */
- public static function getNotesDb($database = null)
- {
- if (!is_null(static::$notesDb)) {
- return static::$notesDb;
- }
- static::getDb($database);
- // calibre_dir/.calnotes/notes.db
- $dbFileName = dirname((string) static::$dbFileName) . '/' . static::NOTES_DIR_NAME . '/' . static::NOTES_DB_NAME;
- if (!file_exists($dbFileName) || !is_readable($dbFileName)) {
- return null;
- }
- static::$notesDb = new PDO('sqlite:' . $dbFileName);
- return static::$notesDb;
- }
-}
diff --git a/COPS/cops-3.1.3/src/Calibre/Filter.php b/COPS/cops-3.1.3/src/Calibre/Filter.php
deleted file mode 100644
index b4546795..00000000
--- a/COPS/cops-3.1.3/src/Calibre/Filter.php
+++ /dev/null
@@ -1,491 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops\Calibre;
-
-use SebLucas\Cops\Input\Request;
-use SebLucas\Cops\Model\Entry;
-use SebLucas\Cops\Pages\PageId;
-use UnexpectedValueException;
-
-class Filter
-{
- public const PAGE_ID = PageId::FILTER_ID;
- public const PAGE_DETAIL = PageId::FILTER;
- public const URL_PARAMS = [
- Author::URL_PARAM => Author::class,
- Language::URL_PARAM => Language::class,
- Publisher::URL_PARAM => Publisher::class,
- Rating::URL_PARAM => Rating::class,
- Serie::URL_PARAM => Serie::class,
- Tag::URL_PARAM => Tag::class,
- Identifier::URL_PARAM => Identifier::class,
- CustomColumn::URL_PARAM => CustomColumn::class,
- BookList::URL_PARAM_FIRST => BookList::class,
- BookList::URL_PARAM_YEAR => BookList::class,
- VirtualLibrary::URL_PARAM => VirtualLibrary::class,
- ];
- public const SEARCH_FIELDS = [
- 'authors' => Author::class,
- //'formats' => Format::class,
- 'languages' => Language::class,
- 'publishers' => Publisher::class,
- 'ratings' => Rating::class,
- 'series' => Serie::class,
- 'tags' => Tag::class,
- ];
-
- protected Request $request;
- /** @var array */
- protected $params = [];
- protected string $parentTable = "books";
- protected string $queryString = "";
- /** @var ?int */
- protected $databaseId;
-
- /**
- * Summary of __construct
- * @param Request|array $request current request or urlParams array
- * @param array $params initial query params
- * @param string $parent optional parent link table if we need to link books, e.g. books_series_link
- * @param ?int $database current database in multiple database setup
- */
- public function __construct($request, array $params = [], string $parent = "books", $database = null)
- {
- if (is_array($request)) {
- $request = Request::build($request);
- }
- $this->request = $request;
- $this->params = $params;
- $this->parentTable = $parent;
- $this->queryString = "";
- $this->databaseId = $database;
-
- $this->checkForFilters();
- }
-
- /**
- * Summary of getFilterString
- * @return string filters to append to query string
- */
- public function getFilterString()
- {
- return $this->queryString;
- }
-
- /**
- * Summary of getQueryParams
- * @return array updated query params including filters
- */
- public function getQueryParams()
- {
- return $this->params;
- }
-
- /**
- * Summary of checkForFilters
- * @return void
- */
- public function checkForFilters()
- {
- if (empty($this->request->urlParams)) {
- return;
- }
-
- // See $config['cops_books_filter']
- $tagName = $this->request->get('tag', null);
- if (!empty($tagName)) {
- $this->addTagNameFilter($tagName);
- }
-
- // See $config['cops_calibre_virtual_libraries']
- $libraryId = $this->request->get(VirtualLibrary::URL_PARAM, null);
- if (!empty($libraryId)) {
- $this->addVirtualLibraryFilter($libraryId);
- }
-
- $authorId = $this->request->get(Author::URL_PARAM, null, '/^!?\d+$/');
- if (!empty($authorId)) {
- $this->addAuthorIdFilter($authorId);
- }
-
- $languageId = $this->request->get(Language::URL_PARAM, null, '/^!?\d+$/');
- if (!empty($languageId)) {
- $this->addLanguageIdFilter($languageId);
- }
-
- $publisherId = $this->request->get(Publisher::URL_PARAM, null, '/^!?\d+$/');
- if (!empty($publisherId)) {
- $this->addPublisherIdFilter($publisherId);
- }
-
- $ratingId = $this->request->get(Rating::URL_PARAM, null, '/^!?\d+$/');
- if (!empty($ratingId)) {
- $this->addRatingIdFilter($ratingId);
- }
-
- $seriesId = $this->request->get(Serie::URL_PARAM, null, '/^!?\d+$/');
- if (!empty($seriesId)) {
- $this->addSeriesIdFilter($seriesId);
- }
-
- $tagId = $this->request->get(Tag::URL_PARAM, null, '/^!?\d+$/');
- if (!empty($tagId)) {
- $this->addTagIdFilter($tagId);
- }
-
- $identifierType = $this->request->get(Identifier::URL_PARAM, null, '/^!?\w+$/');
- if (!empty($identifierType)) {
- $this->addIdentifierTypeFilter($identifierType);
- }
-
- $letter = $this->request->get(BookList::URL_PARAM_FIRST, null, '/^\w$/');
- if (!empty($letter)) {
- $this->addFirstLetterFilter($letter);
- }
-
- $year = $this->request->get(BookList::URL_PARAM_YEAR, null, '/^\d+$/');
- if (!empty($year)) {
- $this->addPubYearFilter($year);
- }
-
- // URL format: ...&c[2]=3&c[3]=other to filter on column 2 = 3 and column 3 = other
- $customIdArray = $this->request->get(CustomColumn::URL_PARAM, null);
- if (!empty($customIdArray) && is_array($customIdArray)) {
- $this->addCustomIdArrayFilters($customIdArray);
- }
- }
-
- /**
- * Summary of addFilter
- * @param string $filter
- * @param mixed $param
- * @return void
- */
- public function addFilter($filter, $param)
- {
- $this->queryString .= ' and (' . $filter . ')';
- array_push($this->params, $param);
- }
-
- /**
- * Summary of addTagNameFilter
- * @param string $tagName
- * @return void
- */
- public function addTagNameFilter($tagName)
- {
- $exists = true;
- if (preg_match("/^!(.*)$/", $tagName, $matches)) {
- $exists = false;
- $tagName = $matches[1];
- }
-
- $filter = 'exists (select null from books_tags_link, tags where books_tags_link.book = books.id and books_tags_link.tag = tags.id and tags.name = ?)';
-
- if (!$exists) {
- $filter = 'not ' . $filter;
- }
-
- $this->addFilter($filter, $tagName);
- }
-
- /**
- * Summary of addVirtualLibraryFilter
- * @param string|int $libraryId
- * @throws \UnexpectedValueException
- * @return void
- */
- public function addVirtualLibraryFilter($libraryId)
- {
- // URL format: ...&vl=2.Short_Stories_in_English
- if (str_contains($libraryId, '.')) {
- [$libraryId, $slug] = explode('.', $libraryId);
- }
- /** @var VirtualLibrary $instance */
- $instance = VirtualLibrary::getInstanceById($libraryId);
- if (empty($instance->id)) {
- return;
- }
-
- $search = $instance->value;
- $replace = $search;
- $params = [];
- $matches = [];
- // See https://github.com/seblucas/cops/pull/233 by @Broele
- preg_match_all('/(?P#?\w+)\:(?P\w+|"(?P[^"]*)")/', $search, $matches, PREG_SET_ORDER);
- foreach ($matches as $match) {
- // get search field
- if (!array_key_exists($match['attr'], static::SEARCH_FIELDS)) {
- $match['attr'] .= 's';
- if (!array_key_exists($match['attr'], static::SEARCH_FIELDS)) {
- throw new UnexpectedValueException('Unsupported search field: ' . $match['attr']);
- }
- }
- // find exact match
- if (isset($match['quoted']) && str_starts_with($match['quoted'], '=')) {
- $value = substr($match['quoted'], 1);
- $className = static::SEARCH_FIELDS[$match['attr']];
- $instance = $className::getInstanceByName($value, $this->databaseId);
- $filterString = $this->getLinkedIdFilter($instance->getLinkTable(), $instance->getLinkColumn(), $instance->limitSelf);
- $replace = str_replace($match[0], $filterString, $replace);
- array_push($params, $instance->id);
- } else {
- throw new UnexpectedValueException('Unsupported search criteria: ' . $match['attr'] . '=' . $match['value']);
- }
- }
-
- if (!empty($replace)) {
- $this->queryString .= ' and (' . $replace . ')';
- foreach ($params as $param) {
- array_push($this->params, $param);
- }
- }
- }
-
- /**
- * Summary of addInstanceFilter
- * @param Base|Author|Language|Publisher|Rating|Serie|Tag|CustomColumn $instance
- * @return void
- */
- public function addInstanceFilter($instance)
- {
- if ($instance instanceof CustomColumn) {
- $this->addCustomIdFilter($instance->customColumnType, $instance->id);
- return;
- }
- $this->addLinkedIdFilter($instance->id, $instance->getLinkTable(), $instance->getLinkColumn(), $instance->limitSelf);
- }
-
- /**
- * Summary of addAuthorIdFilter
- * @param string|int $authorId
- * @return void
- */
- public function addAuthorIdFilter($authorId)
- {
- $this->addLinkedIdFilter($authorId, Author::SQL_LINK_TABLE, Author::SQL_LINK_COLUMN);
- }
-
- /**
- * Summary of addLanguageIdFilter
- * @param string|int $languageId
- * @return void
- */
- public function addLanguageIdFilter($languageId)
- {
- $this->addLinkedIdFilter($languageId, Language::SQL_LINK_TABLE, Language::SQL_LINK_COLUMN);
- }
-
- /**
- * Summary of addPublisherIdFilter
- * @param string|int $publisherId
- * @return void
- */
- public function addPublisherIdFilter($publisherId)
- {
- $this->addLinkedIdFilter($publisherId, Publisher::SQL_LINK_TABLE, Publisher::SQL_LINK_COLUMN);
- }
-
- /**
- * Summary of addRatingIdFilter
- * @param string|int $ratingId
- * @return void
- */
- public function addRatingIdFilter($ratingId)
- {
- $this->addLinkedIdFilter($ratingId, Rating::SQL_LINK_TABLE, Rating::SQL_LINK_COLUMN);
- }
-
- /**
- * Summary of addSeriesIdFilter
- * @param string|int $seriesId
- * @return void
- */
- public function addSeriesIdFilter($seriesId)
- {
- $this->addLinkedIdFilter($seriesId, Serie::SQL_LINK_TABLE, Serie::SQL_LINK_COLUMN);
- }
-
- /**
- * Summary of addTagIdFilter
- * @param string|int $tagId
- * @return void
- */
- public function addTagIdFilter($tagId)
- {
- $this->addLinkedIdFilter($tagId, Tag::SQL_LINK_TABLE, Tag::SQL_LINK_COLUMN);
- }
-
- /**
- * Summary of addIdentifierTypeFilter
- * @param string $identifierType
- * @return void
- */
- public function addIdentifierTypeFilter($identifierType)
- {
- $this->addLinkedIdFilter($identifierType, Identifier::SQL_LINK_TABLE, Identifier::SQL_LINK_COLUMN);
- }
-
- /**
- * Summary of addFirstLetterFilter
- * @param mixed $letter
- * @return void
- */
- public function addFirstLetterFilter($letter)
- {
- $filter = 'substr(upper(books.sort), 1, 1) = ?';
- $this->addFilter($filter, $letter);
- }
-
- /**
- * Summary of addPubYearFilter
- * @param mixed $year
- * @return void
- */
- public function addPubYearFilter($year)
- {
- $filter = 'substr(date(books.pubdate), 1, 4) = ?';
- $this->addFilter($filter, $year);
- }
-
- /**
- * Summary of addCustomIdArrayFilters
- * @param array $customIdArray
- * @return void
- */
- public function addCustomIdArrayFilters($customIdArray)
- {
- foreach ($customIdArray as $customId => $valueId) {
- if (!preg_match('/^\d+$/', $customId)) {
- continue;
- }
- $customType = CustomColumnType::createByCustomID($customId, $this->databaseId);
- $this->addCustomIdFilter($customType, $valueId);
- }
- }
-
- /**
- * Summary of addCustomIdFilter
- * @param CustomColumnType $customType
- * @param mixed $valueId
- * @return void
- */
- public function addCustomIdFilter($customType, $valueId)
- {
- [$filter, $params] = $customType->getFilter($valueId, $this->parentTable);
- if (!empty($filter)) {
- $this->queryString .= ' and (' . $filter . ')';
- foreach ($params as $param) {
- array_push($this->params, $param);
- }
- }
- }
-
- /**
- * Summary of addLinkedIdFilter
- * @param string|int $linkId
- * @param string $linkTable
- * @param string $linkColumn
- * @param bool $limitSelf if filtering on the same table as the parent, limit results to self (or not for tags)
- * @return void
- */
- public function addLinkedIdFilter($linkId, $linkTable, $linkColumn, $limitSelf = true)
- {
- $exists = true;
- $matches = [];
- if (preg_match("/^!(.*)$/", $linkId, $matches)) {
- $exists = false;
- $linkId = $matches[1];
- }
-
- $filter = $this->getLinkedIdFilter($linkTable, $linkColumn, $limitSelf);
-
- if (!$exists) {
- $filter = 'not ' . $filter;
- }
-
- $this->addFilter($filter, $linkId);
- }
-
- /**
- * Summary of getLinkedIdFilter
- * @param string $linkTable
- * @param string $linkColumn
- * @param bool $limitSelf if filtering on the same table as the parent, limit results to self (or not for tags)
- * @return string
- */
- public function getLinkedIdFilter($linkTable, $linkColumn, $limitSelf = true)
- {
- if ($this->parentTable == $linkTable) {
- if ($limitSelf) {
- $filter = "{$linkTable}.{$linkColumn} = ?";
- } else {
- // find other tags/identifiers applied to books where this tag/identifier applies
- $filter = "exists (select null from {$linkTable} as filterself, books where {$this->parentTable}.book = books.id and {$this->parentTable}.{$linkColumn} != filterself.{$linkColumn} and filterself.book = books.id and filterself.{$linkColumn} = ?)";
- }
- } elseif ($this->parentTable == "books") {
- $filter = "exists (select null from {$linkTable} where {$linkTable}.book = books.id and {$linkTable}.{$linkColumn} = ?)";
- } else {
- $filter = "exists (select null from {$linkTable}, books where {$this->parentTable}.book = books.id and {$linkTable}.book = books.id and {$linkTable}.{$linkColumn} = ?)";
- }
-
- return $filter;
- }
-
- /**
- * Summary of getEntryArray
- * @param Request $request
- * @param ?int $database
- * @return array
- */
- public static function getEntryArray($request, $database = null)
- {
- $handler = $request->getHandler();
- $libraryId = $request->getVirtualLibrary();
- $entryArray = [];
- foreach (static::URL_PARAMS as $paramName => $className) {
- if ($className == VirtualLibrary::class) {
- continue;
- }
- $paramValue = $request->get($paramName, null);
- if (!isset($paramValue)) {
- continue;
- }
- // @todo do we want to filter by virtual library etc. here?
- if ($className == BookList::class) {
- $booklist = new BookList(Request::build([$paramName => $paramValue], $handler), $database);
- $groupFunc = ($paramName == 'f') ? 'getCountByFirstLetter' : 'getCountByPubYear';
- $entryArray = array_merge($entryArray, $booklist->$groupFunc());
- continue;
- }
- if ($className == CustomColumn::class) {
- foreach ($paramValue as $customId => $valueId) {
- $custom = CustomColumn::createCustom($customId, $valueId, $database);
- $custom->setHandler($handler);
- $entryArray = array_merge($entryArray, [ $custom->getCustomCount() ]);
- }
- continue;
- }
- // remove negative flag for filter entry here
- if (preg_match('/^!\d+$/', (string) $paramValue)) {
- $paramValue = substr((string) $paramValue, 1);
- }
- if (!empty($libraryId)) {
- $req = Request::build([$paramName => $paramValue, VirtualLibrary::URL_PARAM => $libraryId], $handler);
- } else {
- $req = Request::build([$paramName => $paramValue], $handler);
- }
- $baselist = new BaseList($className, $req, $database);
- $entries = $baselist->getEntriesByFilter();
- $entryArray = array_merge($entryArray, $entries);
- }
- return $entryArray;
- }
-}
diff --git a/COPS/cops-3.1.3/src/Calibre/Identifier.php b/COPS/cops-3.1.3/src/Calibre/Identifier.php
deleted file mode 100644
index 732287fc..00000000
--- a/COPS/cops-3.1.3/src/Calibre/Identifier.php
+++ /dev/null
@@ -1,191 +0,0 @@
-
- */
-
-namespace SebLucas\Cops\Calibre;
-
-use SebLucas\Cops\Pages\PageId;
-
-class Identifier extends Base
-{
- public const PAGE_ID = PageId::ALL_IDENTIFIERS_ID;
- public const PAGE_ALL = PageId::ALL_IDENTIFIERS;
- public const PAGE_DETAIL = PageId::IDENTIFIER_DETAIL;
- public const SQL_TABLE = "identifiers";
- public const SQL_LINK_TABLE = "identifiers";
- public const SQL_LINK_COLUMN = "type";
- public const SQL_SORT = "type";
- public const SQL_COLUMNS = "identifiers.type as id, identifiers.type as type, '' as val";
- public const SQL_ALL_ROWS = "select {0} from identifiers where 1=1 {1} group by identifiers.type order by identifiers.type";
- public const SQL_ROWS_FOR_SEARCH = ""; // "select {0} from tags, books_tags_link where tags.id = tag and upper (tags.name) like ? {1} group by tags.id, tags.name order by tags.name";
- public const SQL_BOOKLIST = 'select {0} from identifiers, books ' . Book::SQL_BOOKS_LEFT_JOIN . '
- where identifiers.book = books.id and identifiers.type = ? {1} order by books.sort';
- public const SQL_BOOKLIST_NULL = 'select {0} from books ' . Book::SQL_BOOKS_LEFT_JOIN . '
- where books.id not in (select book from identifiers) {1} order by books.sort';
- public const URL_PARAM = "i";
-
- /** @var ?int */
- public $id;
- /** @var string */
- public $type;
- public string $formattedType;
- /** @var string */
- public $val;
- public string $uri;
- /** @var ?int */
- protected $databaseId;
-
- /**
- * Summary of __construct
- * @param object $post
- * @param ?int $database
- */
- public function __construct($post, $database = null)
- {
- $this->id = $post->id;
- $this->type = strtolower($post->type);
- $this->val = $post->val;
- $this->formatType();
- $this->databaseId = $database;
- }
-
- /**
- * Summary of formatType
- * @return void
- */
- public function formatType()
- {
- if ($this->type == 'amazon') {
- $this->formattedType = "Amazon";
- $this->uri = sprintf("https://amazon.com/dp/%s", $this->val);
- } elseif ($this->type == "asin") {
- $this->formattedType = $this->type;
- $this->uri = sprintf("https://amazon.com/dp/%s", $this->val);
- } elseif (str_starts_with($this->type, "amazon_")) {
- $this->formattedType = sprintf("Amazon.co.%s", substr($this->type, 7));
- $this->uri = sprintf("https://amazon.co.%s/dp/%s", substr($this->type, 7), $this->val);
- } elseif ($this->type == "isbn") {
- $this->formattedType = "ISBN";
- $this->uri = sprintf("https://www.worldcat.org/isbn/%s", $this->val);
- } elseif ($this->type == "doi") {
- $this->formattedType = "DOI";
- $this->uri = sprintf("https://dx.doi.org/%s", $this->val);
- } elseif ($this->type == "douban") {
- $this->formattedType = "Douban";
- $this->uri = sprintf("https://book.douban.com/subject/%s", $this->val);
- } elseif ($this->type == "goodreads") {
- $this->formattedType = "Goodreads";
- $this->uri = sprintf("https://www.goodreads.com/book/show/%s", $this->val);
- } elseif ($this->type == "google") {
- $this->formattedType = "Google Books";
- $this->uri = sprintf("https://books.google.com/books?id=%s", $this->val);
- } elseif ($this->type == "kobo") {
- $this->formattedType = "Kobo";
- $this->uri = sprintf("https://www.kobo.com/ebook/%s", $this->val);
- } elseif ($this->type == "litres") {
- $this->formattedType = "ЛитРес";
- $this->uri = sprintf("https://www.litres.ru/%s", $this->val);
- } elseif ($this->type == "issn") {
- $this->formattedType = "ISSN";
- $this->uri = sprintf("https://portal.issn.org/resource/ISSN/%s", $this->val);
- } elseif ($this->type == "isfdb") {
- $this->formattedType = "ISFDB";
- $this->uri = sprintf("http://www.isfdb.org/cgi-bin/pl.cgi?%s", $this->val);
- } elseif ($this->type == "lubimyczytac") {
- $this->formattedType = "Lubimyczytac";
- $this->uri = sprintf("https://lubimyczytac.pl/ksiazka/%s/ksiazka", $this->val);
- } elseif ($this->type == "wd") {
- $this->formattedType = "Wikidata";
- $this->uri = sprintf("https://www.wikidata.org/entity/%s", $this->val);
- } elseif ($this->type == "ltid") {
- $this->formattedType = "LibraryThing";
- $this->uri = sprintf("https://www.librarything.com/work/book/%s", $this->val);
- } elseif ($this->type == "olid") {
- $this->formattedType = "OpenLibrary";
- $this->uri = sprintf("https://openlibrary.org/works/%s", $this->val);
- } elseif ($this->type == "url") {
- $this->formattedType = $this->type;
- $this->uri = $this->val;
- } else {
- $this->formattedType = $this->type;
- $this->uri = '';
- }
- }
-
- /**
- * Summary of getTitle
- * @return mixed
- */
- public function getTitle()
- {
- return $this->formattedType;
- }
-
- /**
- * Summary of getParentTitle
- * @return string
- */
- public function getParentTitle()
- {
- return localize("identifiers.title");
- }
-
- /**
- * Summary of getLink
- * @return string
- */
- public function getLink()
- {
- return $this->uri;
- }
-
- /**
- * Summary of getInstanceById
- * @param string|int|null $id used for the type of identifier here
- * @param ?int $database
- * @return object
- */
- public static function getInstanceById($id, $database = null)
- {
- if (isset($id)) {
- return new Identifier((object) ['id' => $id, 'type' => $id, 'val' => ''], $database);
- }
- $default = static::getDefaultName();
- return new Identifier((object) ['id' => null, 'type' => $default, 'val' => ''], $database);
- }
-
- /**
- * Summary of getDefaultName
- * @return string
- */
- public static function getDefaultName()
- {
- return localize("identifierword.none");
- }
-
- /**
- * Summary of getInstancesByBookId
- * @param int $bookId
- * @param ?int $database
- * @return array
- */
- public static function getInstancesByBookId($bookId, $database = null)
- {
- $identifiers = [];
-
- $query = 'select type, val, id
- from identifiers
- where book = ?
- order by type';
- $result = Database::query($query, [$bookId], $database);
- while ($post = $result->fetchObject()) {
- array_push($identifiers, new Identifier($post, $database));
- }
- return $identifiers;
- }
-}
diff --git a/COPS/cops-3.1.3/src/Calibre/Language.php b/COPS/cops-3.1.3/src/Calibre/Language.php
deleted file mode 100644
index ffa7e5b8..00000000
--- a/COPS/cops-3.1.3/src/Calibre/Language.php
+++ /dev/null
@@ -1,91 +0,0 @@
-
- */
-
-namespace SebLucas\Cops\Calibre;
-
-use SebLucas\Cops\Pages\PageId;
-
-class Language extends Base
-{
- public const PAGE_ID = PageId::ALL_LANGUAGES_ID;
- public const PAGE_ALL = PageId::ALL_LANGUAGES;
- public const PAGE_DETAIL = PageId::LANGUAGE_DETAIL;
- public const SQL_TABLE = "languages";
- public const SQL_LINK_TABLE = "books_languages_link";
- public const SQL_LINK_COLUMN = "lang_code";
- public const SQL_SORT = "lang_code";
- public const SQL_COLUMNS = "languages.id as id, languages.lang_code as name";
- public const SQL_ALL_ROWS = "select {0} from languages, books_languages_link where languages.id = books_languages_link.lang_code {1} group by languages.id, books_languages_link.lang_code order by languages.lang_code";
- public const SQL_BOOKLIST = 'select {0} from books_languages_link, books ' . Book::SQL_BOOKS_LEFT_JOIN . '
- where books_languages_link.book = books.id and lang_code = ? {1} order by books.sort';
- public const URL_PARAM = "l";
-
- /**
- * Summary of getTitle
- * @return string
- */
- public function getTitle()
- {
- return static::getLanguageString($this->name);
- }
-
- /**
- * Summary of getParentTitle
- * @return string
- */
- public function getParentTitle()
- {
- return localize("languages.title");
- }
-
- /** Use inherited class methods to query static SQL_TABLE for this class */
-
- /**
- * Summary of getLanguageString
- * @param string $code
- * @return string
- */
- public static function getLanguageString($code)
- {
- $string = localize("languages." . $code);
- if (preg_match("/^languages/", $string)) {
- return $code;
- }
- return $string;
- }
-
- /**
- * Summary of getDefaultName
- * @return string
- */
- public static function getDefaultName()
- {
- return localize("language.title");
- }
-
- /**
- * Summary of getLanguagesByBookId
- * @param int $bookId
- * @param ?int $database
- * @return string
- */
- public static function getLanguagesByBookId($bookId, $database = null)
- {
- $lang = [];
- $query = 'select ' . static::getInstanceColumns($database) . '
- from books_languages_link, languages
- where books_languages_link.lang_code = languages.id
- and book = ?
- order by item_order';
- $result = Database::query($query, [$bookId], $database);
- while ($post = $result->fetchObject()) {
- array_push($lang, static::getLanguageString($post->name));
- }
- return implode(', ', $lang);
- }
-}
diff --git a/COPS/cops-3.1.3/src/Calibre/Metadata.php b/COPS/cops-3.1.3/src/Calibre/Metadata.php
deleted file mode 100644
index 9192c85a..00000000
--- a/COPS/cops-3.1.3/src/Calibre/Metadata.php
+++ /dev/null
@@ -1,197 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops\Calibre;
-
-use DOMDocument;
-use DOMNode;
-use DOMText;
-use JsonException;
-
-/**
- * Calibre metadata.opf files are based on EPUB 2.0 ,
- * not EPUB 3.x
- */
-class Metadata
-{
- public string $uniqueIdentifier;
- public string $version;
- /** @var array */
- public array $metadata;
- /** @var array */
- public array $guide;
-
- /**
- * Summary of getElement
- * @param string $element like dc:identifier
- * @return array
- */
- public function getElement($element)
- {
- $elements = [];
- foreach ($this->metadata as $item) {
- if (empty($item[$element])) {
- continue;
- }
- $elements[] = $item[$element];
- }
- return $elements;
- }
-
- /**
- * Summary of getIdentifiers
- * @return array
- */
- public function getIdentifiers()
- {
- return $this->getElement('dc:identifier');
- }
-
- /**
- * Summary of getElementName
- * @param string $element like meta
- * @param string $name like calibre:annotation
- * @return array
- */
- public function getElementName($element, $name)
- {
- $elements = [];
- foreach ($this->metadata as $item) {
- if (empty($item[$element])) {
- continue;
- }
- if (empty($item[$element]['name']) || $item[$element]['name'] != $name) {
- continue;
- }
- $elements[] = $item[$element]['content'];
- }
- return $elements;
- }
-
- /**
- * Summary of getAnnotations
- * @return array
- */
- public function getAnnotations()
- {
- return $this->getElementName('meta', 'calibre:annotation');
- }
-
- /**
- * Summary of getInstanceByBookId
- * @param int $bookId
- * @param ?int $database
- * @return Metadata|false
- */
- public static function getInstanceByBookId($bookId, $database = null)
- {
- $book = Book::getBookById($bookId, $database);
- if (empty($book)) {
- return false;
- }
- $file = realpath($book->path . '/metadata.opf');
- if (empty($file) || !file_exists($file)) {
- return false;
- }
- $content = file_get_contents($file);
- return static::parseData($content);
- }
-
- /**
- * @param string $data
- * @return self
- */
- public static function parseData($data)
- {
- $doc = new DOMDocument();
- $doc->loadXML($data);
- $root = static::getNode($doc, 'package');
-
- $package = new self();
- $package->uniqueIdentifier = static::getAttr($root, 'unique-identifier');
- $package->version = static::getAttr($root, 'version');
- $package->metadata = static::addNode(static::getNode($root, 'metadata'));
- $package->guide = static::addNode(static::getNode($root, 'guide'));
- return $package;
- }
-
- /**
- * @param DOMNode $node
- * @param string $name
- * @return mixed
- */
- public static function getAttr($node, $name)
- {
- return $node->getAttribute($name);
- }
-
- /**
- * @param DOMNode $node
- * @param string $name
- * @return DOMNode
- */
- public static function getNode($node, $name)
- {
- return $node->getElementsByTagName($name)->item(0);
- }
-
- /**
- * @param DOMNode $node
- * @return string|array
- */
- public static function addNode($node)
- {
- if (!$node->hasAttributes() && !$node->hasChildNodes()) {
- return trim($node->nodeValue);
- }
- $children = null;
- if ($node->hasChildNodes()) {
- if ($node->childNodes->length == 1 && $node->firstChild instanceof DOMText) {
- $children = trim((string) $node->firstChild->nodeValue);
- } else {
- $children = [];
- foreach ($node->childNodes as $child) {
- if ($child instanceof DOMText) {
- continue;
- }
- $children[] = [$child->nodeName => self::addNode($child)];
- }
- }
- }
- if (!$node->hasAttributes()) {
- return $children;
- }
- $info = [];
- foreach ($node->attributes as $attr) {
- if ($node->nodeName == 'meta' && $attr->name == 'content' && !empty($attr->value)) {
- try {
- $info[$attr->name] = json_decode($attr->value, true, 512, JSON_THROW_ON_ERROR);
- } catch (JsonException) {
- $info[$attr->name] = $attr->value;
- }
- } elseif ($attr->name == 'value') {
- $info['@value'] = $attr->value;
- } else {
- $info[$attr->name] = $attr->value;
- }
- }
- //$value = trim($node->nodeValue);
- //if ($value !== '') {
- // $info['value'] = $value;
- //}
- if (!empty($children)) {
- if (is_array($children)) {
- $info['children'] = $children;
- } else {
- $info['value'] = $children;
- }
- }
- return $info;
- }
-}
diff --git a/COPS/cops-3.1.3/src/Calibre/Note.php b/COPS/cops-3.1.3/src/Calibre/Note.php
deleted file mode 100644
index bcf853d9..00000000
--- a/COPS/cops-3.1.3/src/Calibre/Note.php
+++ /dev/null
@@ -1,176 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops\Calibre;
-
-use SebLucas\Cops\Input\Route;
-use SebLucas\Cops\Pages\PageId;
-
-class Note
-{
- public const PAGE_ID = PageId::ALL_NOTES_ID;
- public const PAGE_ALL = PageId::ALL_NOTES;
- public const PAGE_TYPE = PageId::ALL_NOTES_TYPE;
- public const PAGE_DETAIL = PageId::NOTE_DETAIL;
- public const ALLOWED_FIELDS = [
- 'authors' => Author::class,
- //'languages' => Language::class,
- 'publishers' => Publisher::class,
- 'series' => Serie::class,
- 'tags' => Tag::class,
- ];
- public int $id;
- public int $item;
- public string $colname;
- public string $doc;
- public float $mtime;
- public ?int $databaseId = null;
- protected string $handler = '';
-
- /**
- * Summary of __construct
- * @param object $post
- * @param ?int $database
- */
- public function __construct($post, $database = null)
- {
- $this->id = $post->id;
- $this->item = $post->item;
- $this->colname = $post->colname;
- $this->doc = $post->doc;
- $this->mtime = $post->mtime;
- $this->databaseId = $database;
- }
-
- /**
- * Summary of getUri
- * @param array $params
- * @return string
- */
- public function getUri($params = [])
- {
- // @todo get handler from somewhere
- return Route::link($this->handler) . '/notes/' . $this->colname . '/' . $this->item;
- }
-
- /**
- * Summary of getTitle
- * @return string|null
- */
- public function getTitle()
- {
- // @todo get corresponding title from item instance
- return '';
- }
-
- /**
- * Summary of getResources
- * @return array
- */
- public function getResources()
- {
- $notesDb = Database::getNotesDb($this->databaseId);
- if (is_null($notesDb)) {
- return [];
- }
- $resources = [];
- $query = 'select hash, name from resources, notes_resources_link where resources.hash = resource and note = ?';
- $params = [$this->id];
- $result = $notesDb->prepare($query);
- $result->execute($params);
- while ($post = $result->fetchObject()) {
- $resources[$post->hash] = new Resource($post, $this->databaseId);
- }
- return $resources;
- }
-
- /**
- * Summary of getCountByType
- * @param ?int $database
- * @return array
- */
- public static function getCountByType($database = null)
- {
- $notesDb = Database::getNotesDb($database);
- if (is_null($notesDb)) {
- return [];
- }
- $entries = [];
- $query = 'select colname as type, count(*) as count from notes group by colname order by colname';
- $result = $notesDb->prepare($query);
- $result->execute();
- while ($post = $result->fetchObject()) {
- $entries[$post->type] = $post->count;
- }
- return $entries;
- }
-
- /**
- * Summary of getEntriesByType
- * @param string $type
- * @param ?int $database
- * @return array
- */
- public static function getEntriesByType($type, $database = null)
- {
- if (!array_key_exists($type, static::ALLOWED_FIELDS)) {
- return [];
- }
- $notesDb = Database::getNotesDb($database);
- if (is_null($notesDb)) {
- return [];
- }
- $entries = [];
- $query = 'select item, length(doc) as size, mtime from notes where colname = ? order by item';
- $params = [$type];
- $result = $notesDb->prepare($query);
- $result->execute($params);
- while ($post = $result->fetchObject()) {
- $entries[$post->item] = (array) $post;
- }
- $itemIdList = array_keys($entries);
- if (empty($itemIdList)) {
- return $entries;
- }
- $query = "select id, name from {$type} where id in (" . str_repeat('?,', count($itemIdList) - 1) . '?)';
- $result = Database::query($query, $itemIdList, $database);
- while ($post = $result->fetchObject()) {
- if (array_key_exists($post->id, $entries)) {
- $entries[$post->id]["title"] = $post->name;
- }
- }
- return $entries;
- }
-
- /**
- * Summary of getInstanceByTypeId
- * @param string $type
- * @param int $id
- * @param ?int $database
- * @return self|null
- */
- public static function getInstanceByTypeId($type, $id, $database = null)
- {
- if (!array_key_exists($type, static::ALLOWED_FIELDS)) {
- return null;
- }
- $notesDb = Database::getNotesDb($database);
- if (is_null($notesDb)) {
- return null;
- }
- $query = 'select id, item, colname, doc, mtime from notes where item = ? and colname = ?';
- $params = [$id, $type];
- $result = $notesDb->prepare($query);
- $result->execute($params);
- if ($post = $result->fetchObject()) {
- return new self($post, $database);
- }
- return null;
- }
-}
diff --git a/COPS/cops-3.1.3/src/Calibre/Preference.php b/COPS/cops-3.1.3/src/Calibre/Preference.php
deleted file mode 100644
index d711f2d8..00000000
--- a/COPS/cops-3.1.3/src/Calibre/Preference.php
+++ /dev/null
@@ -1,118 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops\Calibre;
-
-use SebLucas\Cops\Pages\PageId;
-use JsonException;
-
-class Preference
-{
- public const PAGE_ID = PageId::ALL_PREFERENCES_ID;
- public const PAGE_ALL = PageId::ALL_PREFERENCES;
- public const PAGE_DETAIL = PageId::PREFERENCE_DETAIL;
- public const SQL_TABLE = "preferences";
- public const SQL_COLUMNS = "id, key, val";
-
- public int $id;
- public string $key;
- public mixed $val;
- public ?int $databaseId = null;
-
- /**
- * Summary of __construct
- * @param object $post
- * @param ?int $database
- */
- public function __construct($post, $database = null)
- {
- $this->id = $post->id;
- $this->key = $post->key;
- try {
- $this->val = json_decode($post->val, true, 512, JSON_THROW_ON_ERROR);
- } catch (JsonException) {
- $this->val = $post->val;
- }
- $this->databaseId = $database;
- }
-
- /**
- * Summary of getInstances
- * @param ?int $database
- * @return array
- */
- public static function getInstances($database = null)
- {
- $preferences = [];
- $query = 'select ' . static::SQL_COLUMNS . ' from ' . static::SQL_TABLE . ' order by key';
- $result = Database::query($query, [], $database);
- while ($post = $result->fetchObject()) {
- $preferences[$post->key] = new self($post, $database);
- }
- return $preferences;
- }
-
- /**
- * Summary of getInstanceByKey
- * @param string $key
- * @param ?int $database
- * @return self|null
- */
- public static function getInstanceByKey($key, $database = null)
- {
- $query = 'select ' . static::SQL_COLUMNS . ' from ' . static::SQL_TABLE . ' where key = ?';
- $params = [$key];
- $result = Database::query($query, $params, $database);
- if ($post = $result->fetchObject()) {
- return new self($post, $database);
- }
- return null;
- }
-
- /**
- * Summary of getVirtualLibraries
- * {
- * "Both Authors": "authors:\"=Author Two\" and authors:\"=Author One\"",
- * "Kindle 2": "tags:\"=Kindle_Mike\" or tags:\"=Kindle_Luca\"",
- * "No Device": "not tags:\"=Kindle_Mike\" and not tags:\"=Kindle_Luca\" and not tags:\"=Kindle_Lydia\""
- * }
- * See https://github.com/seblucas/cops/pull/233
- * @param ?int $database
- * @return self|null
- */
- public static function getVirtualLibraries($database = null)
- {
- return static::getInstanceByKey('virtual_libraries', $database);
- }
-
- /**
- * Summary of getUserCategories
- * @param ?int $database
- * @return self|null
- */
- public static function getUserCategories($database = null)
- {
- // @todo investigate format
- return static::getInstanceByKey('user_categories', $database);
- }
-
- /**
- * Summary of getSavedSearches
- * {
- * "Author One": "authors:one and not authors:two"
- * }
- * @param ?int $database
- * @return self|null
- */
- public static function getSavedSearches($database = null)
- {
- // @todo map search string from saved search to filters
- return static::getInstanceByKey('saved_searches', $database);
- }
-}
diff --git a/COPS/cops-3.1.3/src/Calibre/Publisher.php b/COPS/cops-3.1.3/src/Calibre/Publisher.php
deleted file mode 100644
index a9ae7dea..00000000
--- a/COPS/cops-3.1.3/src/Calibre/Publisher.php
+++ /dev/null
@@ -1,66 +0,0 @@
-
- */
-
-namespace SebLucas\Cops\Calibre;
-
-use SebLucas\Cops\Pages\PageId;
-
-class Publisher extends Base
-{
- public const PAGE_ID = PageId::ALL_PUBLISHERS_ID;
- public const PAGE_ALL = PageId::ALL_PUBLISHERS;
- public const PAGE_DETAIL = PageId::PUBLISHER_DETAIL;
- public const SQL_TABLE = "publishers";
- public const SQL_LINK_TABLE = "books_publishers_link";
- public const SQL_LINK_COLUMN = "publisher";
- public const SQL_SORT = "name";
- public const SQL_COLUMNS = "publishers.id as id, publishers.name as name";
- public const SQL_ALL_ROWS = "select {0} from publishers, books_publishers_link where publishers.id = publisher {1} group by publishers.id, publishers.name order by publishers.name";
- public const SQL_ROWS_FOR_SEARCH = "select {0} from publishers, books_publishers_link where publishers.id = publisher and upper (publishers.name) like ? {1} group by publishers.id, publishers.name order by publishers.name";
- public const SQL_BOOKLIST = 'select {0} from books_publishers_link, books ' . Book::SQL_BOOKS_LEFT_JOIN . '
- where books_publishers_link.book = books.id and publisher = ? {1} order by books.sort';
- public const URL_PARAM = "p";
-
- /**
- * Summary of getParentTitle
- * @return string
- */
- public function getParentTitle()
- {
- return localize("publishers.title");
- }
-
- /** Use inherited class methods to query static SQL_TABLE for this class */
-
- /**
- * Summary of getInstanceByBookId
- * @param int $bookId
- * @param ?int $database
- * @return Publisher|false
- */
- public static function getInstanceByBookId($bookId, $database = null)
- {
- $query = 'select ' . static::getInstanceColumns($database) . '
-from books_publishers_link, publishers
-where publishers.id = publisher and book = ?';
- $result = Database::query($query, [$bookId], $database);
- if ($post = $result->fetchObject()) {
- return new Publisher($post, $database);
- }
- return false;
- }
-
- /**
- * Summary of getDefaultName
- * @return string
- */
- public static function getDefaultName()
- {
- return localize("publisherword.none");
- }
-}
diff --git a/COPS/cops-3.1.3/src/Calibre/Rating.php b/COPS/cops-3.1.3/src/Calibre/Rating.php
deleted file mode 100644
index 22e382b3..00000000
--- a/COPS/cops-3.1.3/src/Calibre/Rating.php
+++ /dev/null
@@ -1,72 +0,0 @@
-name) / 2), intval($this->name) / 2);
- }
-
- /**
- * Summary of getParentTitle
- * @return string
- */
- public function getParentTitle()
- {
- return localize("ratings.title");
- }
-
- /** Use inherited class methods to query static SQL_TABLE for this class */
-
- /**
- * Summary of getCount
- * @param ?int $database
- * @param ?string $handler
- * @return ?Entry
- */
- public static function getCount($database = null, $handler = null)
- {
- $count = Database::querySingle('select count(*) from ' . static::SQL_TABLE, $database);
- // str_format (localize("ratings", count(array))
- return static::getCountEntry($count, $database, "ratings", $handler);
- }
-
- /**
- * Summary of getDefaultName
- * @return int
- */
- public static function getDefaultName()
- {
- return 0;
- }
-}
diff --git a/COPS/cops-3.1.3/src/Calibre/Resource.php b/COPS/cops-3.1.3/src/Calibre/Resource.php
deleted file mode 100644
index 145ff2e5..00000000
--- a/COPS/cops-3.1.3/src/Calibre/Resource.php
+++ /dev/null
@@ -1,138 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops\Calibre;
-
-use SebLucas\Cops\Input\Config;
-use SebLucas\Cops\Input\Route;
-use SebLucas\Cops\Output\FileResponse;
-
-class Resource
-{
- // https://github.com/kovidgoyal/calibre/blob/master/src/calibre/gui2/dialogs/edit_category_notes.py
- public const IMAGE_EXTENSIONS = [
- 'png' => 'image/png',
- 'jpeg' => 'image/jpeg',
- 'jpg' => 'image/jpeg',
- 'gif' => 'image/gif',
- 'svg' => 'image/svg+xml',
- 'webp' => 'image/webp',
- ];
- public const RESOURCE_URL_SCHEME = 'calres';
- public static string $handler = "calres";
- public string $hash;
- public string $name;
- public ?int $databaseId = null;
-
- /**
- * Summary of __construct
- * @param object $post
- * @param ?int $database
- */
- public function __construct($post, $database = null)
- {
- $this->hash = $post->hash;
- $this->name = $post->name;
- $this->databaseId = $database;
- }
-
- /**
- * Summary of getUri
- * @param array $params
- * @return string
- */
- public function getUri($params = [])
- {
- $database = $this->databaseId ?? 0;
- if (Config::get('use_route_urls')) {
- [$alg, $digest] = explode(':', $this->hash);
- $params['db'] = $database;
- $params['alg'] = $alg;
- $params['digest'] = $digest;
- return Route::link(static::$handler, null, $params);
- }
- // @todo remove /handler once link() is fixed
- $baseurl = Route::link(static::$handler) . '/' . static::$handler;
- return $baseurl . '/' . $database . '/' . str_replace(':', '/', $this->hash);
- }
-
- /**
- * Summary of fixResourceLinks
- * @param string $doc
- * @param ?int $database
- * @return string
- */
- public static function fixResourceLinks($doc, $database = null)
- {
- $database ??= 0;
- if (Config::get('use_route_urls')) {
- // create link to resource with dummy alg & digest
- $baseurl = Route::link(static::$handler, null, ['db' => $database, 'alg' => '{alg}', 'digest' => '{digest}']);
- // remove dummy alg & digest
- $baseurl = str_replace(['/{alg}', '/{digest}'], [], $baseurl);
- return str_replace(static::RESOURCE_URL_SCHEME . '://', $baseurl . '/', $doc);
- }
- // @todo remove /handler once link() is fixed
- $baseurl = Route::link(static::$handler) . '/' . static::$handler;
- return str_replace(static::RESOURCE_URL_SCHEME . '://', $baseurl . '/' . $database . '/', $doc);
- }
-
- /**
- * Summary of getResourcePath
- * @param string $hash
- * @param ?int $database
- * @return string|null
- */
- public static function getResourcePath($hash, $database = null)
- {
- [$alg, $digest] = explode(':', $hash);
- $resourcesDir = dirname(Database::getDbFileName($database)) . '/' . Database::NOTES_DIR_NAME . '/resources';
- $resourcePath = $resourcesDir . '/' . substr($digest, 0, 2) . '/' . $alg . '-' . $digest;
- if (file_exists($resourcePath)) {
- return $resourcePath;
- }
- return null;
- }
-
- /**
- * Summary of sendImageResource
- * @param string $hash
- * @param FileResponse $response
- * @param ?string $name
- * @param ?int $database
- * @return FileResponse|null
- */
- public static function sendImageResource($hash, $response, $name = null, $database = null)
- {
- $path = static::getResourcePath($hash, $database);
- if (empty($path)) {
- return null;
- }
- if (empty($name)) {
- $content = file_get_contents($path . '.metadata');
- $info = json_decode($content, true);
- $name = $info['name'];
- }
- $ext = strtolower(pathinfo((string) $name, PATHINFO_EXTENSION));
- if (!array_key_exists($ext, static::IMAGE_EXTENSIONS)) {
- return null;
- }
- $mime = static::IMAGE_EXTENSIONS[$ext];
-
- $response->setHeaders($mime, 0);
- return $response->sendFile($path, true);
- }
-}
diff --git a/COPS/cops-3.1.3/src/Calibre/Serie.php b/COPS/cops-3.1.3/src/Calibre/Serie.php
deleted file mode 100644
index 127c4705..00000000
--- a/COPS/cops-3.1.3/src/Calibre/Serie.php
+++ /dev/null
@@ -1,69 +0,0 @@
-
- */
-
-namespace SebLucas\Cops\Calibre;
-
-use SebLucas\Cops\Pages\PageId;
-
-class Serie extends Category
-{
- public const PAGE_ID = PageId::ALL_SERIES_ID;
- public const PAGE_ALL = PageId::ALL_SERIES;
- public const PAGE_DETAIL = PageId::SERIE_DETAIL;
- public const SQL_TABLE = "series";
- public const SQL_LINK_TABLE = "books_series_link";
- public const SQL_LINK_COLUMN = "series";
- public const SQL_SORT = "sort";
- public const SQL_COLUMNS = "series.id as id, series.name as name, series.sort as sort";
- public const SQL_ALL_ROWS = "select {0} from series, books_series_link where series.id = series {1} group by series.id, series.name, series.sort order by series.sort";
- public const SQL_ROWS_FOR_SEARCH = "select {0} from series, books_series_link where series.id = series and upper (series.name) like ? {1} group by series.id, series.name, series.sort order by series.sort";
- public const SQL_BOOKLIST = 'select {0} from books_series_link, books ' . Book::SQL_BOOKS_LEFT_JOIN . '
- where books_series_link.book = books.id and series = ? {1} order by series_index';
- public const SQL_BOOKLIST_NULL = 'select {0} from books ' . Book::SQL_BOOKS_LEFT_JOIN . '
- where books.id not in (select book from books_series_link) {1} order by books.sort';
- public const URL_PARAM = "s";
- public const CATEGORY = "series";
-
- /**
- * Summary of getParentTitle
- * @return string
- */
- public function getParentTitle()
- {
- return localize("series.title");
- }
-
- /** Use inherited class methods to query static SQL_TABLE for this class */
-
- /**
- * Summary of getInstanceByBookId
- * @param int $bookId
- * @param ?int $database
- * @return Serie|false
- */
- public static function getInstanceByBookId($bookId, $database = null)
- {
- $query = 'select ' . static::getInstanceColumns($database) . '
-from books_series_link, series
-where series.id = series and book = ?';
- $result = Database::query($query, [$bookId], $database);
- if ($post = $result->fetchObject()) {
- return new Serie($post, $database);
- }
- return false;
- }
-
- /**
- * Summary of getDefaultName
- * @return string
- */
- public static function getDefaultName()
- {
- return localize("seriesword.none");
- }
-}
diff --git a/COPS/cops-3.1.3/src/Calibre/Tag.php b/COPS/cops-3.1.3/src/Calibre/Tag.php
deleted file mode 100644
index 82165ff6..00000000
--- a/COPS/cops-3.1.3/src/Calibre/Tag.php
+++ /dev/null
@@ -1,72 +0,0 @@
-
- */
-
-namespace SebLucas\Cops\Calibre;
-
-use SebLucas\Cops\Pages\PageId;
-
-class Tag extends Category
-{
- public const PAGE_ID = PageId::ALL_TAGS_ID;
- public const PAGE_ALL = PageId::ALL_TAGS;
- public const PAGE_DETAIL = PageId::TAG_DETAIL;
- public const SQL_TABLE = "tags";
- public const SQL_LINK_TABLE = "books_tags_link";
- public const SQL_LINK_COLUMN = "tag";
- public const SQL_SORT = "name";
- public const SQL_COLUMNS = "tags.id as id, tags.name as name";
- public const SQL_ALL_ROWS = "select {0} from tags, books_tags_link where tags.id = tag {1} group by tags.id, tags.name order by tags.name";
- public const SQL_ROWS_FOR_SEARCH = "select {0} from tags, books_tags_link where tags.id = tag and upper (tags.name) like ? {1} group by tags.id, tags.name order by tags.name";
- public const SQL_BOOKLIST = 'select {0} from books_tags_link, books ' . Book::SQL_BOOKS_LEFT_JOIN . '
- where books_tags_link.book = books.id and tag = ? {1} order by books.sort';
- public const SQL_BOOKLIST_NULL = 'select {0} from books ' . Book::SQL_BOOKS_LEFT_JOIN . '
- where books.id not in (select book from books_tags_link) {1} order by books.sort';
- public const URL_PARAM = "t";
- public const CATEGORY = "tags";
-
- /**
- * Summary of getParentTitle
- * @return string
- */
- public function getParentTitle()
- {
- return localize("tags.title");
- }
-
- /** Use inherited class methods to query static SQL_TABLE for this class */
-
- /**
- * Summary of getDefaultName
- * @return string
- */
- public static function getDefaultName()
- {
- return localize("tagword.none");
- }
-
- /**
- * Summary of getInstancesByBookId
- * @param int $bookId
- * @param ?int $database
- * @return array
- */
- public static function getInstancesByBookId($bookId, $database = null)
- {
- $tags = [];
- $query = 'select ' . static::getInstanceColumns($database) . '
- from books_tags_link, tags
- where tag = tags.id
- and book = ?
- order by name';
- $result = Database::query($query, [$bookId], $database);
- while ($post = $result->fetchObject()) {
- array_push($tags, new Tag($post, $database));
- }
- return $tags;
- }
-}
diff --git a/COPS/cops-3.1.3/src/Calibre/User.php b/COPS/cops-3.1.3/src/Calibre/User.php
deleted file mode 100644
index ac5826d8..00000000
--- a/COPS/cops-3.1.3/src/Calibre/User.php
+++ /dev/null
@@ -1,152 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops\Calibre;
-
-use SebLucas\Cops\Input\Config;
-use PDO;
-use Exception;
-use JsonException;
-
-/**
- * Summary of User
- */
-class User
-{
- public const SQL_TABLE = "users";
- public const SQL_COLUMNS = "id, name, timestamp, session_data, restriction, readonly, misc_data";
-
- public int $id;
- public string $name;
- public mixed $restriction;
- public ?string $userDbFile = null;
-
- /**
- * Summary of __construct
- * @param object $post
- * @param ?string $userDbFile
- */
- public function __construct($post, $userDbFile = null)
- {
- $this->id = $post->id;
- $this->name = $post->name;
- // @todo use restriction etc. from Calibre user database
- // ['library_restrictions' => []]
- try {
- $this->restriction = json_decode($post->restriction, true, 512, JSON_THROW_ON_ERROR);
- } catch (JsonException) {
- $this->restriction = $post->restriction;
- }
- $this->userDbFile = $userDbFile;
- }
-
- /**
- * Summary of getUserDb
- * @param string|null $userDbFile
- * @return \PDO
- */
- public static function getUserDb($userDbFile = null)
- {
- $userDbFile ??= Config::get('calibre_user_database');
- if (!is_string($userDbFile) || !is_readable($userDbFile)) {
- throw new Exception('Invalid users database ' . $userDbFile);
- }
- return new PDO('sqlite:' . $userDbFile);
- }
-
- /**
- * Summary of getInstanceByName
- * @param string $name
- * @param ?string $userDbFile
- * @return self|null
- */
- public static function getInstanceByName($name, $userDbFile = null)
- {
- try {
- $db = static::getUserDb($userDbFile);
- } catch (Exception $e) {
- error_log($e->getMessage());
- return null;
- }
- $query = 'select ' . static::SQL_COLUMNS . ' from ' . static::SQL_TABLE . ' where name = ?';
- $params = [$name];
- $result = $db->prepare($query);
- $result->execute($params);
- if ($post = $result->fetchObject()) {
- return new self($post, $userDbFile);
- }
- return null;
- }
-
- /**
- * Summary of verifyLogin
- * @param array $serverVars
- * @return bool
- */
- public static function verifyLogin($serverVars = null)
- {
- $basicAuth = Config::get('basic_authentication');
- if (empty($basicAuth)) {
- return true;
- }
- $serverVars ??= $_SERVER;
- if (empty($serverVars['PHP_AUTH_USER']) || empty($serverVars['PHP_AUTH_PW'])) {
- return false;
- }
- // array( "username" => "xxx", "password" => "secret")
- if (is_array($basicAuth)) {
- return static::checkBasicAuthArray($basicAuth, $serverVars);
- }
- // /config/.config/calibre/server-users.sqlite
- if (is_string($basicAuth)) {
- return static::checkBasicAuthDatabase($basicAuth, $serverVars);
- }
- return false;
- }
-
- /**
- * Summary of checkBasicAuthArray
- * @param array $authArray
- * @param array $serverVars
- * @return bool
- */
- public static function checkBasicAuthArray($authArray, $serverVars)
- {
- if (($serverVars['PHP_AUTH_USER'] != $authArray['username'] ||
- $serverVars['PHP_AUTH_PW'] != $authArray['password'])) {
- return false;
- }
- return true;
- }
-
- /**
- * Summary of checkBasicAuthDatabase
- * @param string $userDbFile
- * @param array $serverVars
- * @return bool
- */
- public static function checkBasicAuthDatabase($userDbFile, $serverVars)
- {
- try {
- $db = static::getUserDb($userDbFile);
- } catch (Exception $e) {
- error_log($e->getMessage());
- return false;
- }
- $query = 'select ' . static::SQL_COLUMNS . ' from ' . static::SQL_TABLE . ' where name = ? and pw = ?';
- $stmt = $db->prepare($query);
- $params = [ $serverVars['PHP_AUTH_USER'], $serverVars['PHP_AUTH_PW'] ];
- $stmt->execute($params);
- $result = $stmt->fetchObject();
- if (empty($result) || $result->name != $serverVars['PHP_AUTH_USER']) {
- return false;
- }
- return true;
- }
-}
diff --git a/COPS/cops-3.1.3/src/Calibre/VirtualLibrary.php b/COPS/cops-3.1.3/src/Calibre/VirtualLibrary.php
deleted file mode 100644
index d31240b3..00000000
--- a/COPS/cops-3.1.3/src/Calibre/VirtualLibrary.php
+++ /dev/null
@@ -1,204 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops\Calibre;
-
-use SebLucas\Cops\Input\Config;
-use SebLucas\Cops\Input\Route;
-use SebLucas\Cops\Model\Entry;
-use SebLucas\Cops\Pages\PageId;
-
-class VirtualLibrary extends Base
-{
- public const PAGE_ID = PageId::ALL_LIBRARIES_ID;
- public const PAGE_ALL = PageId::ALL_LIBRARIES;
- public const PAGE_DETAIL = PageId::LIBRARY_DETAIL;
- public const SQL_TABLE = "libraries";
- public const URL_PARAM = "vl";
-
- /** @var array */
- protected static array $libraries = [];
- public string $value;
-
- /**
- * Summary of __construct
- * @param object $post
- * @param ?int $database
- */
- public function __construct($post, $database = null)
- {
- $this->id = $post->id;
- $this->name = $post->name;
- $this->value = $post->value;
- $this->databaseId = $database;
- }
-
- /**
- * Summary of getUri
- * @param array $params
- * @return string
- */
- public function getUri($params = [])
- {
- // get home page from Config
- $homepage = PageId::getHomePage();
- // we need databaseId here because we use Route::link with $handler
- $params['db'] = $this->getDatabaseId();
- if (!empty($this->id)) {
- // URL format: ...&vl=2.Short_Stories_in_English
- $params[static::URL_PARAM] = static::formatParameter($this->id, $this->getTitle());
- }
- return Route::link($this->handler, $homepage, $params);
- }
-
- /**
- * Summary of getParentTitle
- * @return string
- */
- public function getParentTitle()
- {
- return localize("libraries.title");
- }
-
- /**
- * Summary of formatParameter
- * @param string|int $id
- * @param string $title
- * @return string
- */
- public static function formatParameter($id, $title)
- {
- // URL format: ...&vl=2.Short_Stories_in_English
- return strval($id) . '.' . Route::slugify($title);
- }
-
- /**
- * Summary of getLibraries
- * @param ?int $database
- * @return array
- */
- public static function getLibraries($database = null)
- {
- $db = $database ?? 0;
- if (array_key_exists($db, self::$libraries)) {
- return self::$libraries[$db];
- }
- $preference = Preference::getVirtualLibraries($database);
- self::$libraries[$db] = $preference->val ?? [];
- return self::$libraries[$db];
- }
-
- /**
- * Summary of countEntries
- * @param ?int $database
- * @return int
- */
- public static function countEntries($database = null)
- {
- $libraries = self::getLibraries($database);
- return count($libraries);
- }
-
- /**
- * Summary of getEntries
- * @param ?int $database
- * @param ?string $handler
- * @return array
- */
- public static function getEntries($database = null, $handler = null)
- {
- $libraries = self::getLibraries($database);
- $entryArray = [];
- $id = 1;
- foreach ($libraries as $name => $value) {
- // @todo get book count filtered by value
- $post = (object) ['id' => $id, 'name' => $name, 'value' => $value, 'count' => 0];
- $instance = new self($post, $database);
- $instance->setHandler($handler);
- array_push($entryArray, $instance->getEntry($post->count));
- $id += 1;
- }
- return $entryArray;
- }
-
- /**
- * Summary of getWithoutEntry
- * @param ?int $database
- * @param ?string $handler
- * @return ?Entry
- */
- public static function getWithoutEntry($database = null, $handler = null)
- {
- $booklist = new BookList(null, $database);
- $count = $booklist->getBookCount();
- $instance = self::getInstanceById(null, $database);
- $instance->setHandler($handler);
- return $instance->getEntry($count);
- }
-
- /**
- * Summary of getDefaultName
- * @return ?string
- */
- public static function getDefaultName()
- {
- return localize("libraries.none");
- }
-
- /**
- * Summary of getCount
- * @param ?int $database
- * @param ?string $handler
- * @return ?Entry
- */
- public static function getCount($database = null, $handler = null)
- {
- $libraries = self::getLibraries($database);
- $count = count($libraries);
- return static::getCountEntry($count, $database, "libraries", $handler);
- }
-
- /**
- * Summary of getInstanceById
- * @param string|int|null $id
- * @param ?int $database
- * @return object
- */
- public static function getInstanceById($id, $database = null)
- {
- $libraries = self::getLibraries($database);
- if (isset($id)) {
- // id = key position in array + 1
- $id = intval($id) - 1;
- $name = array_keys($libraries)[$id];
- return static::getInstanceByName($name);
- }
- $default = static::getDefaultName();
- $post = (object) ['id' => null, 'name' => $default, 'value' => ''];
- return new self($post, $database);
- }
-
- /**
- * Summary of getInstanceByName
- * @param string $name
- * @param ?int $database
- * @return self|null
- */
- public static function getInstanceByName($name, $database = null)
- {
- $libraries = self::getLibraries($database);
- if (!empty($libraries) && array_key_exists($name, $libraries)) {
- // id = key position in array + 1
- $id = array_search($name, array_keys($libraries)) + 1;
- $post = (object) ['id' => $id, 'name' => $name, 'value' => $libraries[$name]];
- return new self($post, $database);
- }
- return null;
- }
-}
diff --git a/COPS/cops-3.1.3/src/Framework.php b/COPS/cops-3.1.3/src/Framework.php
deleted file mode 100644
index ee5a0fe1..00000000
--- a/COPS/cops-3.1.3/src/Framework.php
+++ /dev/null
@@ -1,132 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops;
-
-/**
- * Minimal Framework
- */
-class Framework
-{
- /** @var array */
- protected static $handlers = [
- "index" => Handlers\HtmlHandler::class,
- "feed" => Handlers\FeedHandler::class,
- "json" => Handlers\JsonHandler::class,
- "fetch" => Handlers\FetchHandler::class,
- "read" => Handlers\ReadHandler::class,
- "epubfs" => Handlers\EpubFsHandler::class,
- "restapi" => Handlers\RestApiHandler::class,
- "check" => Handlers\CheckHandler::class,
- "opds" => Handlers\OpdsHandler::class,
- "loader" => Handlers\LoaderHandler::class,
- "zipper" => Handlers\ZipperHandler::class,
- "calres" => Handlers\CalResHandler::class,
- "zipfs" => Handlers\ZipFsHandler::class,
- "mail" => Handlers\MailHandler::class,
- "graphql" => Handlers\GraphQLHandler::class,
- ];
- /** @var array */
- protected static $middlewares = [];
-
- /**
- * Single request runner with optional handler name
- * @param string $name
- * @return void
- */
- public static function run($name = '')
- {
- $request = static::getRequest($name);
-
- // @todo route to the right handler if needed
- if (empty($name)) {
- $name = $request->getHandler();
- } elseif (Input\Config::get('use_route_urls')) {
- $name = $request->getHandler($name);
- }
- // special case for json requests here
- if ($name == 'index' && $request->isJson()) {
- $name = 'json';
- }
- $handler = Framework::getHandler($name);
- if (empty(static::$middlewares)) {
- $handler->handle($request);
- return;
- }
- // @see https://www.php-fig.org/psr/psr-15/meta/#queue-based-request-handler
- $queue = new Handlers\QueueBasedHandler($handler);
- foreach (static::$middlewares as $middleware) {
- $queue->add(new $middleware());
- }
- $queue->handle($request);
- }
-
- /**
- * Get request instance
- * @param string $name
- * @param bool $parse
- * @return Input\Request
- */
- public static function getRequest($name = '', $parse = true)
- {
- // initialize routes if needed
- static::init();
- // when using Apache .htaccess redirect
- if (empty($_SERVER['PATH_INFO']) && !empty($_SERVER['REDIRECT_PATH_INFO'])) {
- $_SERVER['PATH_INFO'] = $_SERVER['REDIRECT_PATH_INFO'];
- }
- // @deprecated 3.1.0 use index.php/$name instead
- // fix PATH_INFO when accessed via traditional endpoint scripts
- if (!empty($name) && Input\Route::addPrefix($name)) {
- if (empty($_SERVER['PATH_INFO']) || $_SERVER['PATH_INFO'] == '/') {
- $_SERVER['PATH_INFO'] = '/' . $name;
- } elseif (!str_starts_with((string) $_SERVER['PATH_INFO'], '/' . $name . '/')) {
- $_SERVER['PATH_INFO'] = '/' . $name . $_SERVER['PATH_INFO'];
- // @todo force parsing route urls here?
- Input\Config::set('use_route_urls', 1);
- }
- }
- // @todo special case for restapi
- return new Input\Request($parse);
- }
-
- /**
- * Initialize framework
- * @return void
- */
- public static function init()
- {
- static::addRoutes();
- }
-
- /**
- * Add routes for all handlers
- * @return void
- */
- public static function addRoutes()
- {
- if (Input\Route::count() > 0) {
- return;
- }
- foreach (static::$handlers as $name => $handler) {
- Input\Route::addRoutes($handler::getRoutes());
- }
- }
-
- /**
- * Get handler instance based on name
- * @param string $name
- * @param mixed $args
- * @return mixed
- */
- public static function getHandler($name, ...$args)
- {
- return new static::$handlers[$name](...$args);
- }
-}
diff --git a/COPS/cops-3.1.3/src/Handlers/BaseHandler.php b/COPS/cops-3.1.3/src/Handlers/BaseHandler.php
deleted file mode 100644
index 57a29244..00000000
--- a/COPS/cops-3.1.3/src/Handlers/BaseHandler.php
+++ /dev/null
@@ -1,42 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops\Handlers;
-
-use SebLucas\Cops\Input\Request;
-use SebLucas\Cops\Input\Route;
-use SebLucas\Cops\Output\Response;
-
-/**
- * Summary of BaseHandler
- */
-abstract class BaseHandler
-{
- public const PARAM = Route::HANDLER_PARAM;
- public const HANDLER = "";
-
- /**
- * @return array
- */
- public static function getRoutes()
- {
- return [];
- }
-
- public function __construct()
- {
- // ...
- }
-
- /**
- * @param Request $request
- * @return Response|void
- */
- abstract public function handle($request);
-}
diff --git a/COPS/cops-3.1.3/src/Handlers/CalResHandler.php b/COPS/cops-3.1.3/src/Handlers/CalResHandler.php
deleted file mode 100644
index 16c33668..00000000
--- a/COPS/cops-3.1.3/src/Handlers/CalResHandler.php
+++ /dev/null
@@ -1,48 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops\Handlers;
-
-use SebLucas\Cops\Calibre\Resource;
-use SebLucas\Cops\Output\FileResponse;
-use SebLucas\Cops\Output\Response;
-
-/**
- * Handle calres:// resources for Calibre notes
- * URL format: index.php/calres/{db}/{alg}/{digest} with {hash} = {alg}:{digest}
- */
-class CalResHandler extends BaseHandler
-{
- public const HANDLER = "calres";
-
- public static function getRoutes()
- {
- // extra routes supported by other endpoints (path starts with endpoint param)
- return [
- "/calres/{db:\d+}/{alg}/{digest}" => [static::PARAM => static::HANDLER],
- ];
- }
-
- public function handle($request)
- {
- $database = $request->getId('db');
- $alg = $request->get('alg');
- $digest = $request->get('digest');
-
- $hash = $alg . ':' . $digest;
-
- // create empty file response to start with!?
- $response = new FileResponse();
-
- $result = Resource::sendImageResource($hash, $response, null, intval($database));
- if (is_null($result)) {
- Response::notFound($request);
- }
- }
-}
diff --git a/COPS/cops-3.1.3/src/Handlers/CheckHandler.php b/COPS/cops-3.1.3/src/Handlers/CheckHandler.php
deleted file mode 100644
index cbbcb165..00000000
--- a/COPS/cops-3.1.3/src/Handlers/CheckHandler.php
+++ /dev/null
@@ -1,344 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops\Handlers;
-
-use SebLucas\Cops\Calibre\Database;
-use SebLucas\Cops\Input\Config;
-use SebLucas\Cops\Input\Request;
-use SebLucas\Cops\Input\Route;
-use SebLucas\Cops\Output\Format;
-use SebLucas\Cops\Output\Response;
-use Exception;
-use PDO;
-
-/**
- * Summary of CheckHandler
- */
-class CheckHandler extends BaseHandler
-{
- public const HANDLER = "check";
-
- public static function getRoutes()
- {
- return [
- "/check/{more:.*}" => [static::PARAM => static::HANDLER],
- "/check" => [static::PARAM => static::HANDLER],
- ];
- }
-
- public function handle($request)
- {
- $more = $request->get('more');
- if ($more) {
- $this->handleMore($request);
- return;
- }
-
- try {
- $this->checkConfig($request);
- } catch (Exception $e) {
- echo "Error in normal handling:";
- echo "" . $e->getMessage() . " ";
- include 'checkconfig.php';
- }
- }
-
- /**
- * Summary of checkConfig
- * @param Request $request
- * @return void
- */
- public function checkConfig($request)
- {
- $data = [];
- $data['style'] = Route::path($request->style());
- $data['err'] = $request->get('err', -1);
- $data['error'] = $this->getError($data['err']);
- $data['phpversion'] = $this->getPhpVersion();
- foreach ($this->getExtensions() as $extension => $message) {
- $data['extension_' . $extension] = $message;
- }
- $data['baseurl'] = $this->getBaseURL($request);
- $data['render'] = $this->getRender($request);
- $data['agent'] = $this->getAgent($request);
- $data['full'] = $request->get('full');
- $data['databases'] = $this->getDatabases($data['full']);
-
- $template = dirname(__DIR__, 2) . '/templates/checkconfig.html';
-
- $response = new Response('text/html;charset=utf-8');
- $response->sendData(Format::template($data, $template));
- }
-
- /**
- * Summary of getMessage
- * @param string $title
- * @param string $message
- * @return string
- */
- public function getMessage($title, $message)
- {
- return '
-
- ' . $title . '
- ' . $message . '
- ';
- }
-
- /**
- * Summary of getError
- * @param mixed $err
- * @return string
- */
- public function getError($err)
- {
- switch ($err) {
- case 1:
- $title = 'You\'ve been redirected because COPS is not configured properly';
- $message = 'Database error';
- return $this->getMessage($title, $message);
- }
- return '';
- }
-
- /**
- * Summary of getPhpVersion
- * @return string
- */
- public function getPhpVersion()
- {
- if (defined('PHP_VERSION_ID')) {
- if (PHP_VERSION_ID >= 80200) {
- return 'OK (' . PHP_VERSION . ')';
- }
- return 'Please install PHP >= 8.2 (' . PHP_VERSION . ')';
- }
- return 'Please install PHP >= 8.2';
- }
-
- /**
- * Summary of getExtensions
- * @return array
- */
- public function getExtensions()
- {
- $extensions = [];
- if (extension_loaded('gd') && function_exists('gd_info')) {
- $extensions['gd'] = 'OK';
- } else {
- $extensions['gd'] = 'Please install the php-gd extension and make sure it\'s enabled';
- }
- if (extension_loaded('pdo_sqlite')) {
- $extensions['sqlite'] = 'OK';
- } else {
- $extensions['sqlite'] = 'Please install the php-sqlite / php-sqlite3 extension and make sure it\'s enabled';
- }
- if (extension_loaded('libxml')) {
- $extensions['libxml'] = 'OK';
- } else {
- $extensions['libxml'] = 'Please make sure libxml is enabled';
- }
- if (extension_loaded('dom')) {
- $extensions['dom'] = 'OK';
- } else {
- $extensions['dom'] = 'Please install the php-xml extension and make sure DOM is enabled';
- }
- if (extension_loaded('xmlwriter')) {
- $extensions['xmlwriter'] = 'OK';
- } else {
- $extensions['xmlwriter'] = 'Please install the php-xml extension and make sure XMLWriter is enabled';
- }
- if (extension_loaded('json')) {
- $extensions['json'] = 'OK';
- } else {
- $extensions['json'] = 'Please install the php-json extension and make sure it\'s enabled';
- }
- if (extension_loaded('mbstring')) {
- $extensions['mbstring'] = 'OK';
- } else {
- $extensions['mbstring'] = 'Please install the php-mbstring extension and make sure it\'s enabled';
- }
- if (extension_loaded('intl')) {
- $extensions['intl'] = 'OK';
- } else {
- $extensions['intl'] = 'Please install the php-intl extension and make sure it\'s enabled';
- }
- if (class_exists('Normalizer', $autoload = false)) {
- $extensions['Normalizer'] = 'OK';
- } else {
- $extensions['Normalizer'] = 'Please make sure intl is enabled in your php.ini';
- }
- if (extension_loaded('zlib')) {
- $extensions['zlib'] = 'OK';
- } else {
- $extensions['zlib'] = 'Please make sure zlib is enabled';
- }
- return $extensions;
- }
-
- /**
- * Summary of getBaseURL
- * @param Request $request
- * @return string
- */
- public function getBaseURL($request)
- {
- $base = dirname((string) $request->script());
- if (!str_ends_with($base, '/')) {
- $base .= '/';
- }
- $result = '';
- $result .= 'Base URL detected by the script: ' . $base . ' ';
- $result .= 'Full URL specified in $config[\'cops_full_url\']: ' . Config::get('full_url') . ' ';
- if (Route::hasTrustedProxies()) {
- $result .= 'Trusted proxies configured: ' . Config::get('trusted_proxies') . ' ';
- $result .= 'Trusted headers configured: ' . json_encode(Config::get('trusted_headers')) . ' ';
- foreach (Config::get('trusted_headers') as $name) {
- $header = 'HTTP_' . strtoupper(str_replace('-', '_', $name));
- $result .= $header . ': ' . ($request->server($header) ?? '') . ' ';
- }
- $result .= 'Base URL via trusted proxies: ' . Route::base() . ' ';
- }
- $result .= 'REMOTE_ADDR: ' . ($request->server('REMOTE_ADDR') ?? '') . ' ';
- $result .= ' ';
- $result .= 'SCRIPT_NAME: ' . ($request->server('SCRIPT_NAME') ?? '') . ' ';
- $result .= 'HTTP_HOST: ' . ($request->server('HTTP_HOST') ?? '') . ' ';
- $result .= 'SERVER_NAME: ' . ($request->server('SERVER_NAME') ?? '') . ' ';
- $result .= 'SERVER_ADDR: ' . ($request->server('SERVER_ADDR') ?? '') . ' ';
- $result .= 'SERVER_PORT: ' . ($request->server('SERVER_PORT') ?? '') . ' ';
- $result .= 'REQUEST_SCHEME: ' . ($request->server('REQUEST_SCHEME') ?? '') . ' ';
- $result .= 'REQUEST_URI: ' . ($request->server('REQUEST_URI') ?? '') . ' ';
- return $result;
- }
-
- /**
- * Summary of getRender
- * @param Request $request
- * @return string
- */
- public function getRender($request)
- {
- if ($request->render()) {
- return 'Server side rendering';
- }
- return 'Client side rendering';
- }
-
- /**
- * Summary of getAgent
- * @param Request $request
- * @return string
- */
- public function getAgent($request)
- {
- return $request->agent();
- }
-
- /**
- * Summary of getDatabases
- * @param mixed $full
- * @return string
- */
- public function getDatabases($full)
- {
- $i = 0;
- $result = '';
- foreach (Database::getDbList() as $name => $database) {
- $title = 'Check if Calibre database path is not an URL';
- if (!preg_match('#^http#', $database)) {
- $message = $name . ' OK';
- } else {
- $message = $name . ' Calibre path has to be local (no URL allowed)';
- }
- $result .= $this->getMessage($title, $message);
-
- $title = 'Check if Calibre database file exists and is readable';
- if (is_readable(Database::getDbFileName($i))) {
- $message = $name . ' OK';
- } else {
- $message = $name . ' File ' . Database::getDbFileName($i) . ' not found,
-Please check
-
-- Value of $config[\'calibre_directory\'] in config/local.php (Does it end with a \'/\'?)
-- Value of open_basedir in your php.ini
-- The access rights of the Calibre Database
-- Synology users please read this
-- Note that hosting your Calibre Library in /home is almost impossible due to access rights restriction
- ';
- }
- $result .= $this->getMessage($title, $message);
-
- if (!is_readable(Database::getDbFileName($i))) {
- $i++;
- continue;
- }
- $title = 'Check if Calibre database file can be opened with PHP';
- try {
- $db = new PDO('sqlite:' . Database::getDbFileName($i));
- $message = $name . ' OK';
- } catch (Exception $e) {
- $message = $name . ' If the file is readable, check your php configuration. Exception detail : ' . $e;
- }
- $result .= $this->getMessage($title, $message);
-
- $title = 'Check if Calibre database file contains at least some of the needed tables';
- try {
- $db = new PDO('sqlite:' . Database::getDbFileName($i));
- $count = $db->query('select count(*) FROM sqlite_master WHERE type="table" AND name in ("books", "authors", "tags", "series")')->fetchColumn();
- if ($count == 4) {
- $message = $name . ' OK';
- } else {
- $message = $name . ' Not all Calibre tables were found. Are you sure you\'re using the correct database.';
- }
- } catch (Exception $e) {
- $message = $name . ' If the file is readable, check your php configuration. Exception detail : ' . $e;
- }
- $result .= $this->getMessage($title, $message);
-
- if (!$full) {
- $i++;
- continue;
- }
- $title = 'Check if all Calibre books are found';
- $message = "This option has been disabled by default - uncomment if you are sure you want to do this...";
- /**
- try {
- $db = new PDO('sqlite:' . Database::getDbFileName($i));
- $result = $db->prepare('select books.path || "/" || data.name || "." || lower (format) as fullpath from data join books on data.book = books.id');
- $result->execute();
- while ($post = $result->fetchObject()) {
- if (!is_file(Database::getDbDirectory($i) . $post->fullpath)) {
- echo '' . Database::getDbDirectory($i) . $post->fullpath . ' ';
- }
- }
- } catch (Exception $e) {
- echo $name . ' If the file is readable, check your php configuration. Exception detail : ' . $e;
- }
- */
- $result .= $this->getMessage($title, $message);
- $i++;
- }
- return $result;
- }
-
- /**
- * Summary of handleMore
- * @param Request $request
- * @return void
- */
- public function handleMore($request)
- {
- $message = date(DATE_COOKIE) . "\n\n";
- $message .= var_export($request, true);
-
- $response = new Response('text/plain;charset=utf-8');
- $response->sendData($message);
- }
-}
diff --git a/COPS/cops-3.1.3/src/Handlers/EpubFsHandler.php b/COPS/cops-3.1.3/src/Handlers/EpubFsHandler.php
deleted file mode 100644
index 6a781b8b..00000000
--- a/COPS/cops-3.1.3/src/Handlers/EpubFsHandler.php
+++ /dev/null
@@ -1,64 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops\Handlers;
-
-use SebLucas\Cops\Output\EPubReader;
-use SebLucas\Cops\Output\Response;
-use Exception;
-
-/**
- * Handle Epub filesystem for monocle epub reader
- * URL format: index.php/epubfs?data={idData}&comp={component}
- */
-class EpubFsHandler extends BaseHandler
-{
- public const HANDLER = "epubfs";
-
- public static function getRoutes()
- {
- // support custom pattern for route placeholders - see nikic/fast-route
- return [
- "/epubfs/{db:\d+}/{data:\d+}/{comp:.+}" => [static::PARAM => static::HANDLER],
- ];
- }
-
- public function handle($request)
- {
- if (php_sapi_name() === 'cli' && $request->getHandler() !== 'phpunit') {
- return;
- }
-
- //$database = $request->getId('db');
- $idData = $request->getId('data');
- if (empty($idData)) {
- // this will call exit()
- Response::notFound($request);
- }
- $component = $request->get('comp', null);
- if (empty($component)) {
- // this will call exit()
- Response::notFound($request);
- }
- $database = $request->database();
-
- // create empty response to start with!?
- $response = new Response();
-
- $reader = new EPubReader($request, $response);
-
- try {
- $reader->sendContent($idData, $component, $database);
-
- } catch (Exception $e) {
- error_log($e);
- Response::sendError($request, $e->getMessage());
- }
- }
-}
diff --git a/COPS/cops-3.1.3/src/Handlers/FeedHandler.php b/COPS/cops-3.1.3/src/Handlers/FeedHandler.php
deleted file mode 100644
index c55be787..00000000
--- a/COPS/cops-3.1.3/src/Handlers/FeedHandler.php
+++ /dev/null
@@ -1,66 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops\Handlers;
-
-use SebLucas\Cops\Input\Config;
-use SebLucas\Cops\Output\OpdsRenderer;
-use SebLucas\Cops\Output\Response;
-use SebLucas\Cops\Pages\PageId;
-
-/**
- * Handle OPDS 1.2 feed
- * URL format: index.php/feed?page={page}&query={query}&...
- */
-class FeedHandler extends BaseHandler
-{
- public const HANDLER = "feed";
-
- public static function getRoutes()
- {
- return [
- "/feed/{page}/{id}" => [static::PARAM => static::HANDLER],
- "/feed/{page}" => [static::PARAM => static::HANDLER],
- "/feed" => [static::PARAM => static::HANDLER],
- ];
- }
-
- public function handle($request)
- {
- $page = $request->get('page', PageId::INDEX);
- $query = $request->get('query');
- if ($query) {
- $page = PageId::OPENSEARCH_QUERY;
- }
- // @todo handle special case of OPDS not expecting filter while HTML does better
- $request->set('filter', null);
-
- if (Config::get('fetch_protect') == '1') {
- session_start();
- if (!isset($_SESSION['connected'])) {
- $_SESSION['connected'] = 0;
- }
- }
-
- $response = new Response('application/xml;charset=utf-8');
-
- $OPDSRender = new OpdsRenderer($request, $response);
-
- switch ($page) {
- case PageId::OPENSEARCH :
- case PageId::SEARCH :
- $response->sendData($OPDSRender->getOpenSearch($request));
- return;
- default:
- $currentPage = PageId::getPage($page, $request);
- $response->sendData($OPDSRender->render($currentPage, $request));
- return;
- }
- }
-}
diff --git a/COPS/cops-3.1.3/src/Handlers/FetchHandler.php b/COPS/cops-3.1.3/src/Handlers/FetchHandler.php
deleted file mode 100644
index 7315a690..00000000
--- a/COPS/cops-3.1.3/src/Handlers/FetchHandler.php
+++ /dev/null
@@ -1,235 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops\Handlers;
-
-use SebLucas\Cops\Input\Config;
-use SebLucas\Cops\Input\Request;
-use SebLucas\Cops\Calibre\Book;
-use SebLucas\Cops\Calibre\Cover;
-use SebLucas\Cops\Calibre\Data;
-use SebLucas\Cops\Output\FileResponse;
-use SebLucas\Cops\Output\Response;
-use SebLucas\Cops\Output\Zipper;
-
-/**
- * Fetch book covers or files
- * URL format: index.php/fetch?id={bookId}&type={type}&data={idData}&view={viewOnly}
- * or index.php/fetch?id={bookId}&thumb={thumb} for book cover thumbnails
- * or index.php/fetch?id={bookId}&file={file} for extra data file for this book
- */
-class FetchHandler extends BaseHandler
-{
- public const HANDLER = "fetch";
-
- public static function getRoutes()
- {
- // check if the path starts with the endpoint param or not here
- return [
- // support custom pattern for route placeholders - see nikic/fast-route
- "/files/{db:\d+}/{id:\d+}/{file:.+}" => [static::PARAM => static::HANDLER],
- "/thumbs/{thumb}/{db:\d+}/{id:\d+}.jpg" => [static::PARAM => static::HANDLER],
- "/covers/{db:\d+}/{id:\d+}.jpg" => [static::PARAM => static::HANDLER],
- "/inline/{db:\d+}/{data:\d+}/{ignore}.{type}" => [static::PARAM => static::HANDLER, "view" => 1],
- "/fetch/{db:\d+}/{data:\d+}/{ignore}.{type}" => [static::PARAM => static::HANDLER],
- // @todo handle url rewriting if enabled separately - path parameters are different
- "/view/{data}/{db}/{ignore}.{type}" => [static::PARAM => static::HANDLER, "view" => 1],
- "/view/{data}/{ignore}.{type}" => [static::PARAM => static::HANDLER, "view" => 1],
- "/download/{data}/{db}/{ignore}.{type}" => [static::PARAM => static::HANDLER],
- "/download/{data}/{ignore}.{type}" => [static::PARAM => static::HANDLER],
- ];
- }
-
- /**
- * Summary of handle
- * @param Request $request
- * @return void
- */
- public function handle($request)
- {
- if (Config::get('fetch_protect') == '1') {
- session_start();
- if (!isset($_SESSION['connected'])) {
- // this will call exit()
- Response::notFound($request);
- }
- }
- // clean output buffers before sending the ebook data do avoid high memory usage on big ebooks (ie. comic books)
- if (ob_get_length() !== false && $request->getHandler() !== 'phpunit') {
- ob_end_clean();
- }
-
- $bookId = $request->getId();
- $type = $request->get('type', 'jpg');
- $idData = $request->getId('data');
- $viewOnly = $request->get('view', false);
- $database = $request->database();
- $file = $request->get('file');
-
- if (is_null($bookId)) {
- $book = Book::getBookByDataId($idData, $database);
- } else {
- $book = Book::getBookById($bookId, $database);
- }
-
- if (!$book) {
- // this will call exit()
- Response::notFound($request);
- }
-
- if (!empty($file)) {
- $this->sendExtraFile($request, $book, $file);
- return;
- }
-
- // -DC- Add png type
- if (in_array($type, ['jpg', 'png'])) {
- $this->sendThumbnail($request, $book, $type);
- return;
- }
-
- if (!$viewOnly && $type == 'epub' && Config::get('update_epub-metadata')) {
- $this->sendUpdatedEpub($request, $book, $idData);
- return;
- }
-
- $data = $book->getDataById($idData);
- // absolute path for single DB in PHP app here - cfr. internal dir for X-Accel-Redirect with Nginx
- $file = $book->getFilePath($type, $idData);
-
- if ($viewOnly) {
- // disposition inline here
- $response = new FileResponse($data->getMimeType(), 0, '');
- $response->sendFile($file);
- return;
- }
-
- if ($type == 'epub' && Config::get('provide_kepub') == '1' && preg_match('/Kobo/', $request->agent())) {
- $this->sendConvertedKepub($book, $file, $data);
- return;
- }
-
- $response = new FileResponse($data->getMimeType(), 0, basename($file));
- $response->sendFile($file);
- }
-
- /**
- * Summary of sendExtraFile
- * @param Request $request
- * @param Book $book
- * @param string $file
- * @return FileResponse|Zipper
- */
- public function sendExtraFile($request, $book, $file)
- {
- if ($file == 'zipped') {
- // zip all extra files and send back
- return $this->zipExtraFiles($request, $book);
- }
- $extraFiles = $book->getExtraFiles();
- if (!in_array($file, $extraFiles)) {
- // this will call exit()
- Response::notFound($request);
- }
- // send back extra file
- $filepath = $book->path . '/' . Book::DATA_DIR_NAME . '/' . $file;
- if (!file_exists($filepath)) {
- // this will call exit()
- Response::notFound($request);
- }
- $mimetype = Response::getMimeType($filepath);
- $response = new FileResponse($mimetype, 0, basename($filepath));
- return $response->sendFile($filepath);
- }
-
- /**
- * Summary of zipExtraFiles
- * @param Request $request
- * @param Book $book
- * @return Zipper
- */
- public function zipExtraFiles($request, $book)
- {
- $zipper = new Zipper($request);
-
- if ($zipper->isValidForExtraFiles($book)) {
- $sendHeaders = headers_sent() ? false : true;
- // disable nginx buffering by default
- if ($sendHeaders) {
- header('X-Accel-Buffering: no');
- }
- return $zipper->download(null, $sendHeaders);
- } else {
- Response::sendError($request, "Invalid zipped: " . $zipper->getMessage());
- }
- }
-
- /**
- * Summary of sendThumbnail
- * @param Request $request
- * @param Book $book
- * @param string $type
- * @return FileResponse
- */
- public function sendThumbnail($request, $book, $type)
- {
- $file = $book->getCoverFilePath($type);
- if (empty($file) || !file_exists($file)) {
- // this will call exit()
- Response::notFound($request);
- }
- $cover = new Cover($book);
- // create empty file response to start with!?
- $response = new FileResponse();
- return $cover->sendThumbnail($request, $response);
- }
-
- /**
- * Summary of sendUpdatedEpub
- * @param Request $request
- * @param Book $book
- * @param mixed $idData
- * @return FileResponse
- */
- public function sendUpdatedEpub($request, $book, $idData)
- {
- // update epub metadata + provide kepub if needed (with update of opf properties for cover-image in EPub)
- if (Config::get('provide_kepub') == '1' && preg_match('/Kobo/', $request->agent())) {
- $book->updateForKepub = true;
- }
- // create empty response to start with!?
- $response = new FileResponse();
- // this will also use kepubify_path internally if defined
- return $book->sendUpdatedEpub($idData, $response);
- }
-
- /**
- * Summary of sendConvertedKepub
- * @param Book $book
- * @param string $file
- * @param Data $data
- * @return FileResponse
- */
- public function sendConvertedKepub($book, $file, $data)
- {
- // run kepubify on original Epub file and send converted tmpfile
- if (!empty(Config::get('kepubify_path'))) {
- // @todo no cache control here!?
- $response = new FileResponse($data->getMimeType(), null, basename($data->getUpdatedFilenameKepub()));
- $result = $book->runKepubify($file, $response);
- if (empty($result)) {
- Response::sendError(null, 'Error: failed to convert epub file');
- }
- return $result;
- }
- // provide kepub in name only (without update of opf properties for cover-image in Epub)
- $response = new FileResponse($data->getMimeType(), 0, basename($data->getUpdatedFilenameKepub()));
- return $response->sendFile($file);
- }
-}
diff --git a/COPS/cops-3.1.3/src/Handlers/GraphQLHandler.php b/COPS/cops-3.1.3/src/Handlers/GraphQLHandler.php
deleted file mode 100644
index c0640476..00000000
--- a/COPS/cops-3.1.3/src/Handlers/GraphQLHandler.php
+++ /dev/null
@@ -1,345 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops\Handlers;
-
-use SebLucas\Cops\Calibre\Author;
-use SebLucas\Cops\Calibre\BaseList;
-use SebLucas\Cops\Calibre\Book;
-use SebLucas\Cops\Calibre\BookList;
-use SebLucas\Cops\Calibre\Identifier;
-use SebLucas\Cops\Calibre\Language;
-use SebLucas\Cops\Calibre\Publisher;
-use SebLucas\Cops\Calibre\Rating;
-use SebLucas\Cops\Calibre\Serie;
-use SebLucas\Cops\Calibre\Tag;
-use SebLucas\Cops\Input\Config;
-use SebLucas\Cops\Input\Context;
-use SebLucas\Cops\Input\Request;
-use SebLucas\Cops\Input\Route;
-use SebLucas\Cops\Output\Format;
-use SebLucas\Cops\Output\Response;
-use GraphQL\GraphQL;
-use GraphQL\Utils\BuildSchema;
-use GraphQL\Language\AST\TypeDefinitionNode;
-use GraphQL\Type\Definition\ResolveInfo;
-use GraphQL\Executor\Executor;
-use GraphQL\Error\DebugFlag;
-
-/**
- * Summary of GraphQLHandler
- */
-class GraphQLHandler extends BaseHandler
-{
- public const HANDLER = "graphql";
- public const DEBUG = DebugFlag::INCLUDE_DEBUG_MESSAGE;
-
- public static function getRoutes()
- {
- return [
- "/graphql" => [static::PARAM => static::HANDLER],
- ];
- }
-
- /**
- * Summary of handle
- * @param Request $request
- * @return void
- */
- public function handle($request)
- {
- if ($request->method() !== 'POST') {
- $this->renderPlayground();
- return;
- }
-
- // override splitting authors and books by first letter here?
- Config::set('author_split_first_letter', '0');
- Config::set('titles_split_first_letter', '0');
- //Config::set('titles_split_publication_year', '0');
- // @todo override pagination
- Config::set('max_item_per_page', 100);
-
- $result = $this->runQuery($request);
-
- $response = new Response('application/json;charset=utf-8');
- $response->sendData(json_encode($result));
- }
-
- /**
- * Summary of runQuery
- * @param Request $request
- * @return array
- */
- public function runQuery($request)
- {
- $input = json_decode((string) $request->content(), true);
-
- $schema = $this->getSchema($request);
-
- $queryString = $input['query'];
- $rootValue = 'query';
- // @see https://github.com/webonyx/graphql-php/blob/master/examples/02-schema-definition-language/graphql.php
- // use $rootValue to resolve query fields
- //$rootValue = $this->getFieldResolvers($request);
- $context = new Context($request);
- $variableValues = $input['variables'] ?? null;
- $operationName = $input['operationName'] ?? null;
- //$fieldResolver = $this->getFieldResolver($request, $resolvers);
- //$validationRules = [];
-
- $result = GraphQL::executeQuery(
- $schema,
- $queryString,
- $rootValue,
- $context,
- $variableValues,
- $operationName,
- $fieldResolver = null,
- $validationRules = null
- );
- //$result = array_merge($result->toArray(), ['input' => $input]);
-
- return $result->toArray(static::DEBUG);
- }
-
- /**
- * Summary of getSchema
- * @phpstan-import-type TypeConfigDecorator from \GraphQL\Utils\ASTDefinitionBuilder
- * @param Request $request
- * @return \GraphQL\Type\Schema
- */
- public function getSchema($request)
- {
- $resolvers = $this->mapTypeFieldResolvers();
-
- $typeConfigDecorator = function (array $typeConfig, TypeDefinitionNode $typeDefinitionNode) use ($resolvers, $request) {
- $name = $typeConfig['name'];
- // ... add missing options to $typeConfig based on type $name
- if (empty($typeConfig['resolveField']) && !empty($resolvers[$name])) {
- $typeConfig['resolveField'] = $resolvers[$name]($request);
- }
- return $typeConfig;
- };
-
- $contents = file_get_contents(dirname(__DIR__, 2) . '/schema.graphql');
- //$schema = BuildSchema::build($contents);
- $schema = BuildSchema::build($contents, $typeConfigDecorator);
-
- return $schema;
- }
-
- /**
- * Summary of mapTypeFieldResolvers
- * @return array
- */
- public function mapTypeFieldResolvers()
- {
- return [
- 'Query' => $this->getQueryFieldResolver(...),
- 'Entry' => $this->getEntryFieldResolver(...),
- 'EntryBook' => $this->getEntryBookFieldResolver(...),
- ];
- }
-
- /**
- * Summary of getQueryFieldResolver
- * @param Request $request
- * @return callable
- */
- public function getQueryFieldResolver($request)
- {
- $resolver = static function ($objectValue, array $args, $context, ResolveInfo $info) use ($request) {
- $fieldName = $info->fieldName;
- switch ($fieldName) {
- case 'books':
- $booklist = new BookList($request);
- [$entryArray, $totalNumber] = $booklist->getAllBooks();
- return $entryArray;
- case 'book':
- $book = Book::getBookById($args['id'], $request->database());
- if (is_null($book)) {
- return $book;
- }
- $book->setHandler("index");
- return $book->getEntry();
- case 'datas':
- $book = Book::getBookById($args['bookId'], $request->database());
- if (is_null($book)) {
- return $book;
- }
- $book->setHandler("index");
- return $book->getDatas();
- case 'data':
- $book = Book::getBookByDataId($args['id'], $request->database());
- if (is_null($book)) {
- return $book;
- }
- $data = $book->datas[0];
- return $data;
- case 'authors':
- $baselist = new BaseList(Author::class, $request);
- $entryArray = $baselist->getRequestEntries();
- return $entryArray;
- case 'author':
- $instance = Author::getInstanceById($args['id'], $request->database());
- $instance->setHandler("index");
- return $instance->getEntry();
- case 'identifiers':
- $baselist = new BaseList(Identifier::class, $request);
- $entryArray = $baselist->getRequestEntries();
- return $entryArray;
- case 'identifier':
- $instance = Identifier::getInstanceById($args['id'], $request->database());
- $instance->setHandler("index");
- return $instance->getEntry();
- case 'languages':
- $baselist = new BaseList(Language::class, $request);
- $entryArray = $baselist->getRequestEntries();
- return $entryArray;
- case 'language':
- $instance = Language::getInstanceById($args['id'], $request->database());
- $instance->setHandler("index");
- return $instance->getEntry();
- case 'publishers':
- $baselist = new BaseList(Publisher::class, $request);
- $entryArray = $baselist->getRequestEntries();
- return $entryArray;
- case 'publisher':
- $instance = Publisher::getInstanceById($args['id'], $request->database());
- $instance->setHandler("index");
- return $instance->getEntry();
- case 'ratings':
- $baselist = new BaseList(Rating::class, $request);
- $entryArray = $baselist->getRequestEntries();
- return $entryArray;
- case 'rating':
- $instance = Rating::getInstanceById($args['id'], $request->database());
- $instance->setHandler("index");
- return $instance->getEntry();
- case 'series':
- $baselist = new BaseList(Serie::class, $request);
- $entryArray = $baselist->getRequestEntries();
- return $entryArray;
- case 'serie':
- $instance = Serie::getInstanceById($args['id'], $request->database());
- $instance->setHandler("index");
- return $instance->getEntry();
- case 'tags':
- $baselist = new BaseList(Tag::class, $request);
- $entryArray = $baselist->getRequestEntries();
- return $entryArray;
- case 'tag':
- $instance = Tag::getInstanceById($args['id'], $request->database());
- $instance->setHandler("index");
- return $instance->getEntry();
- }
- return Executor::defaultFieldResolver($objectValue, $args, $context, $info);
- };
- return $resolver;
- }
-
- /**
- * Summary of getEntryFieldResolver
- * @param Request $request
- * @return callable
- */
- public function getEntryFieldResolver($request)
- {
- $resolver = static function ($objectValue, array $args, $context, ResolveInfo $info) use ($request) {
- $fieldName = $info->fieldName;
- switch ($fieldName) {
- case 'books':
- // @todo get books for parent instance(s)
- $instance = $objectValue->instance;
- $booklist = new BookList($request);
- [$entryArray, $totalNumber] = $booklist->getBooksByInstance($instance, 1);
- return $entryArray;
- }
- return Executor::defaultFieldResolver($objectValue, $args, $context, $info);
- };
- return $resolver;
- }
-
- /**
- * Summary of getEntryBookFieldResolver
- * @param Request $request
- * @return callable
- */
- public function getEntryBookFieldResolver($request)
- {
- $resolver = static function ($objectValue, array $args, $context, ResolveInfo $info) use ($request) {
- $fieldName = $info->fieldName;
- //if (is_object($objectValue) && isset($objectValue->{$fieldName})) {
- // return $objectValue->{$fieldName};
- //}
- // coming from Data
- if ($objectValue instanceof Book) {
- $objectValue = $objectValue->getEntry();
- }
- /** @var Book $book */
- $book = $objectValue->book;
- switch ($fieldName) {
- case 'path':
- return $book->path;
- case 'authors':
- $authors = $book->getAuthors();
- $entryArray = [];
- foreach ($authors as $author) {
- array_push($entryArray, $author->getEntry());
- }
- return $entryArray;
- case 'datas':
- $datas = $book->getDatas();
- return $datas;
- case 'identifiers':
- $identifiers = $book->getIdentifiers();
- $entryArray = [];
- foreach ($identifiers as $identifier) {
- array_push($entryArray, $identifier->getEntry());
- }
- return $entryArray;
- case 'languages':
- $languages = $book->getLanguages();
- return $languages;
- case 'publisher':
- $publisher = $book->getPublisher();
- return $publisher->getEntry();
- case 'rating':
- $rating = $book->getRating();
- return $rating;
- case 'serie':
- $serie = $book->getSerie();
- return $serie->getEntry();
- case 'tags':
- $tags = $book->getTags();
- $entryArray = [];
- foreach ($tags as $tag) {
- array_push($entryArray, $tag->getEntry());
- }
- return $entryArray;
- }
- return Executor::defaultFieldResolver($objectValue, $args, $context, $info);
- };
- return $resolver;
- }
-
- /**
- * Render GraphQL Playground
- * @return void
- */
- public function renderPlayground()
- {
- $data = ['link' => Route::link(static::HANDLER)];
- $template = dirname(__DIR__, 2) . '/templates/graphql.html';
-
- $response = new Response('text/html;charset=utf-8');
- $response->sendData(Format::template($data, $template));
- }
-}
diff --git a/COPS/cops-3.1.3/src/Handlers/HtmlHandler.php b/COPS/cops-3.1.3/src/Handlers/HtmlHandler.php
deleted file mode 100644
index 225723b1..00000000
--- a/COPS/cops-3.1.3/src/Handlers/HtmlHandler.php
+++ /dev/null
@@ -1,72 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops\Handlers;
-
-use SebLucas\Cops\Calibre\Database;
-use SebLucas\Cops\Input\Config;
-use SebLucas\Cops\Input\Route;
-use SebLucas\Cops\Output\HtmlRenderer;
-use SebLucas\Cops\Output\Response;
-use SebLucas\Cops\Pages\PageId;
-use Throwable;
-
-/**
- * HTML main handler
- * URL format: index.php?page={page}&...
- */
-class HtmlHandler extends PageHandler
-{
- public const HANDLER = "index";
-
- public static function getRoutes()
- {
- return parent::getRoutes();
- }
-
- public function handle($request)
- {
- // If we detect that an OPDS reader try to connect try to redirect to feed.php
- if (preg_match('/(Librera|MantanoReader|FBReader|Stanza|Marvin|Aldiko|Moon\+ Reader|Chunky|AlReader|EBookDroid|BookReader|CoolReader|PageTurner|books\.ebook\.pdf\.reader|com\.hiwapps\.ebookreader|OpenBook)/', $request->agent())) {
- Response::redirect(Route::link("feed", null, ["db" => $request->database()]));
- return;
- }
-
- $page = $request->get('page');
- $database = $request->database();
-
- // Use the configured home page if needed
- if (!isset($page)) {
- $page = PageId::getHomePage();
- $request->set('page', $page);
- }
-
- // Access the database ASAP to be sure it's readable, redirect if that's not the case.
- // It has to be done before any header is sent.
- Database::checkDatabaseAvailability($database);
-
- if (Config::get('fetch_protect') == '1') {
- session_start();
- if (!isset($_SESSION['connected'])) {
- $_SESSION['connected'] = 0;
- }
- }
-
- $response = new Response('text/html;charset=utf-8');
-
- $html = new HtmlRenderer($request, $response);
-
- try {
- $response->sendData($html->render($request));
- } catch (Throwable $e) {
- error_log($e);
- Response::sendError($request, $e->getMessage());
- }
- }
-}
diff --git a/COPS/cops-3.1.3/src/Handlers/JsonHandler.php b/COPS/cops-3.1.3/src/Handlers/JsonHandler.php
deleted file mode 100644
index 6f2fc61c..00000000
--- a/COPS/cops-3.1.3/src/Handlers/JsonHandler.php
+++ /dev/null
@@ -1,45 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops\Handlers;
-
-use SebLucas\Cops\Output\JsonRenderer;
-use SebLucas\Cops\Output\Response;
-use Throwable;
-
-/**
- * Handle JSON ajax requests
- * URL format: index.php?page={page}&...
- * with Accept: application/json or X-Requested-With: XMLHttpRequest
- */
-class JsonHandler extends PageHandler
-{
- public const HANDLER = "json";
-
- public static function getRoutes()
- {
- // same routes as HtmlHandler - see util.js
- //return parent::getRoutes();
- return [];
- }
-
- public function handle($request)
- {
- $response = new Response('application/json;charset=utf-8');
-
- $json = new JsonRenderer($request, $response);
-
- try {
- $response->sendData(json_encode($json->getJson($request)));
- } catch (Throwable $e) {
- error_log($e);
- Response::sendError($request, $e->getMessage());
- }
- }
-}
diff --git a/COPS/cops-3.1.3/src/Handlers/LoaderHandler.php b/COPS/cops-3.1.3/src/Handlers/LoaderHandler.php
deleted file mode 100644
index 7c2b9a72..00000000
--- a/COPS/cops-3.1.3/src/Handlers/LoaderHandler.php
+++ /dev/null
@@ -1,93 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops\Handlers;
-
-use SebLucas\Cops\Input\Config;
-use SebLucas\Cops\Input\Route;
-use SebLucas\Cops\Output\Response;
-use Marsender\EPubLoader\RequestHandler;
-use Marsender\EPubLoader\App\ExtraActions;
-
-/**
- * Summary of LoaderHandler
- */
-class LoaderHandler extends BaseHandler
-{
- public const HANDLER = "loader";
-
- public static function getRoutes()
- {
- return [
- "/loader/{action}/{dbNum:\d+}/{authorId:\d+}" => [static::PARAM => static::HANDLER],
- "/loader/{action}/{dbNum:\d+}" => [static::PARAM => static::HANDLER],
- "/loader/{action}/" => [static::PARAM => static::HANDLER],
- "/loader/{action}" => [static::PARAM => static::HANDLER],
- "/loader" => [static::PARAM => static::HANDLER],
- ];
- }
-
- public function handle($request)
- {
- // get the global config for epub-loader from config/loader.php
- $gConfig = require dirname(__DIR__, 2) . '/config/loader.php';
- // adapt for use with COPS
- $gConfig['endpoint'] = Route::link('loader');
- $gConfig['app_name'] = 'COPS Loader';
- $gConfig['version'] = Config::VERSION;
- $gConfig['admin_email'] = '';
- $gConfig['create_db'] = false;
- $gConfig['databases'] = [];
-
- // specify a cache directory for any Google or Wikidata lookup
- $cacheDir = $gConfig['cache_dir'] ?? 'tests/cache';
- if (!is_dir($cacheDir) && !mkdir($cacheDir, 0o777, true)) {
- echo 'Please make sure the cache directory can be created';
- return;
- }
- if (!is_writable($cacheDir)) {
- echo 'Please make sure the cache directory is writeable';
- return;
- }
-
- // get the current COPS calibre directories
- $calibreDir = Config::get('calibre_directory');
- if (!is_array($calibreDir)) {
- $calibreDir = ['COPS Database' => $calibreDir];
- }
- foreach ($calibreDir as $name => $path) {
- $gConfig['databases'][] = ['name' => $name, 'db_path' => rtrim((string) $path, '/'), 'epub_path' => '.'];
- }
-
- $action = $request->get('action');
- $dbNum = $request->getId('dbNum');
- $itemId = $request->getId('authorId');
-
- $urlParams = $request->urlParams;
-
- // you can define extra actions for your app - see example.php
- $handler = new RequestHandler($gConfig, ExtraActions::class, $cacheDir);
- $result = $handler->request($action, $dbNum, $urlParams);
-
- if (method_exists($handler, 'isDone')) {
- if ($handler->isDone()) {
- return;
- }
- }
-
- // handle the result yourself or let epub-loader generate the output
- $result = array_merge($gConfig, $result);
- //$templateDir = 'templates/twigged/loader'; // if you want to use custom templates
- $templateDir = null;
- $template = null;
-
- $response = new Response('text/html;charset=utf-8');
- $response->sendData($handler->output($result, $templateDir, $template));
- }
-}
diff --git a/COPS/cops-3.1.3/src/Handlers/MailHandler.php b/COPS/cops-3.1.3/src/Handlers/MailHandler.php
deleted file mode 100644
index 6b933df1..00000000
--- a/COPS/cops-3.1.3/src/Handlers/MailHandler.php
+++ /dev/null
@@ -1,58 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops\Handlers;
-
-use SebLucas\Cops\Output\Mail;
-use SebLucas\Cops\Output\Response;
-
-/**
- * Send books by email
- * URL format: index.php/mail (POST data and email)
- */
-class MailHandler extends BaseHandler
-{
- public const HANDLER = "mail";
-
- public static function getRoutes()
- {
- return [
- "/mail" => [static::PARAM => static::HANDLER],
- ];
- }
-
- public function handle($request)
- {
- // set request handler to 'phpunit' to run preSend() but not actually Send()
- $dryRun = ($request->getHandler() === 'phpunit') ? true : false;
-
- $mailer = new Mail();
-
- if ($error = $mailer->checkConfiguration()) {
- // this will call exit()
- Response::sendError($request, $error);
- }
-
- $idData = (int) $request->post("data");
- $emailDest = $request->post("email");
- if ($error = $mailer->checkRequest($idData, $emailDest)) {
- // this will call exit()
- Response::sendError($request, $error);
- }
-
- if ($error = $mailer->sendMail($idData, $emailDest, $request, $dryRun)) {
- $response = new Response('text/plain');
- $response->sendData(localize("mail.messagenotsent") . $error);
- return;
- }
-
- $response = new Response('text/plain');
- $response->sendData(localize("mail.messagesent"));
- }
-}
diff --git a/COPS/cops-3.1.3/src/Handlers/OpdsHandler.php b/COPS/cops-3.1.3/src/Handlers/OpdsHandler.php
deleted file mode 100644
index a334135a..00000000
--- a/COPS/cops-3.1.3/src/Handlers/OpdsHandler.php
+++ /dev/null
@@ -1,68 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops\Handlers;
-
-use SebLucas\Cops\Input\Config;
-//use SebLucas\Cops\Output\OpdsRenderer;
-use SebLucas\Cops\Output\KiwilanOPDS as OpdsRenderer;
-use SebLucas\Cops\Pages\PageId;
-
-/**
- * Handle OPDS 2.0 feed (dev only)
- * URL format: index.php/opds{/route}?query={query} etc.
- */
-class OpdsHandler extends BaseHandler
-{
- public const HANDLER = "opds";
-
- public static function getRoutes()
- {
- return [
- "/opds/{page}/{id}" => [static::PARAM => static::HANDLER],
- "/opds/{page}" => [static::PARAM => static::HANDLER],
- "/opds" => [static::PARAM => static::HANDLER],
- ];
- }
-
- public function handle($request)
- {
- $page = $request->get('page', PageId::INDEX);
- $query = $request->get('query'); // 'q' by default for php-opds
- if ($query) {
- $page = PageId::OPENSEARCH_QUERY;
- }
-
- if (Config::get('fetch_protect') == '1') {
- session_start();
- if (!isset($_SESSION['connected'])) {
- $_SESSION['connected'] = 0;
- }
- }
-
- $OPDSRender = new OpdsRenderer();
-
- switch ($page) {
- case PageId::OPENSEARCH :
- case PageId::SEARCH :
- $response = $OPDSRender->getOpenSearch($request);
- break;
- default:
- $currentPage = PageId::getPage($page, $request);
- $response = $OPDSRender->render($currentPage, $request);
- }
-
- foreach ($response->getHeaders() as $type => $value) {
- header($type . ': ' . $value);
- }
- http_response_code($response->getStatus());
-
- echo $response->getContents();
- }
-}
diff --git a/COPS/cops-3.1.3/src/Handlers/PageHandler.php b/COPS/cops-3.1.3/src/Handlers/PageHandler.php
deleted file mode 100644
index 2d38319f..00000000
--- a/COPS/cops-3.1.3/src/Handlers/PageHandler.php
+++ /dev/null
@@ -1,73 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops\Handlers;
-
-use SebLucas\Cops\Pages\PageId;
-
-/**
- * Generic page handler extended by HtmlHandler and JsonHandler
- * URL format: ...?page={page}&...
- */
-class PageHandler extends BaseHandler
-{
- public static function getRoutes()
- {
- // Format: route => page, or route => [page => page, fixed => 1, ...] with fixed params
- return [
- "/index" => PageId::INDEX,
- "/authors/letter/{id}" => PageId::AUTHORS_FIRST_LETTER,
- "/authors/letter" => ["page" => PageId::ALL_AUTHORS, "letter" => 1],
- "/authors/{id}/{title}" => PageId::AUTHOR_DETAIL,
- "/authors/{id}" => PageId::AUTHOR_DETAIL,
- "/authors" => PageId::ALL_AUTHORS,
- "/books/letter/{id}" => PageId::ALL_BOOKS_LETTER,
- "/books/letter" => ["page" => PageId::ALL_BOOKS, "letter" => 1],
- "/books/year/{id}" => PageId::ALL_BOOKS_YEAR,
- "/books/year" => ["page" => PageId::ALL_BOOKS, "year" => 1],
- "/books/{id}/{author}/{title}" => PageId::BOOK_DETAIL,
- "/books/{id}" => PageId::BOOK_DETAIL,
- "/books" => PageId::ALL_BOOKS,
- "/series/{id}/{title}" => PageId::SERIE_DETAIL,
- "/series/{id}" => PageId::SERIE_DETAIL,
- "/series" => PageId::ALL_SERIES,
- "/query/{query}/{scope}" => ["page" => PageId::OPENSEARCH_QUERY, "search" => 1],
- "/query/{query}" => ["page" => PageId::OPENSEARCH_QUERY, "search" => 1],
- "/search/{query}/{scope}" => PageId::OPENSEARCH_QUERY,
- "/search/{query}" => PageId::OPENSEARCH_QUERY,
- "/search" => PageId::OPENSEARCH,
- "/recent" => PageId::ALL_RECENT_BOOKS,
- "/tags/{id}/{title}" => PageId::TAG_DETAIL,
- "/tags/{id}" => PageId::TAG_DETAIL,
- "/tags" => PageId::ALL_TAGS,
- "/custom/{custom}/{id}" => PageId::CUSTOM_DETAIL,
- "/custom/{custom}" => PageId::ALL_CUSTOMS,
- "/about" => PageId::ABOUT,
- "/languages/{id}/{title}" => PageId::LANGUAGE_DETAIL,
- "/languages/{id}" => PageId::LANGUAGE_DETAIL,
- "/languages" => PageId::ALL_LANGUAGES,
- "/customize" => PageId::CUSTOMIZE,
- "/publishers/{id}/{title}" => PageId::PUBLISHER_DETAIL,
- "/publishers/{id}" => PageId::PUBLISHER_DETAIL,
- "/publishers" => PageId::ALL_PUBLISHERS,
- "/ratings/{id}/{title}" => PageId::RATING_DETAIL,
- "/ratings/{id}" => PageId::RATING_DETAIL,
- "/ratings" => PageId::ALL_RATINGS,
- "/identifiers/{id}/{title}" => PageId::IDENTIFIER_DETAIL,
- "/identifiers/{id}" => PageId::IDENTIFIER_DETAIL,
- "/identifiers" => PageId::ALL_IDENTIFIERS,
- "/libraries" => PageId::ALL_LIBRARIES,
- ];
- }
-
- public function handle($request)
- {
- // ...
- }
-}
diff --git a/COPS/cops-3.1.3/src/Handlers/QueueBasedHandler.php b/COPS/cops-3.1.3/src/Handlers/QueueBasedHandler.php
deleted file mode 100644
index 4dfa5241..00000000
--- a/COPS/cops-3.1.3/src/Handlers/QueueBasedHandler.php
+++ /dev/null
@@ -1,62 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops\Handlers;
-
-use SebLucas\Cops\Input\Request;
-use SebLucas\Cops\Output\Response;
-
-/**
- * Basic middleware dispatcher (FIFO queue)
- * This is mainly useful for request middleware, not response middleware,
- * since most COPS request handlers already finish sending the response
- * @see https://www.php-fig.org/psr/psr-15/meta/#queue-based-request-handler
- */
-class QueueBasedHandler extends BaseHandler
-{
- /** @var BaseHandler */
- protected $handler;
- /** @var array */
- protected $queue = [];
-
- /**
- * Set final request handler
- * @param BaseHandler $handler
- */
- public function __construct($handler)
- {
- $this->handler = $handler;
- }
-
- /**
- * Add middleware to queue (FIFO)
- * @param mixed $middleware
- * @return void
- */
- public function add($middleware)
- {
- $this->queue[] = $middleware;
- }
-
- /**
- * Process next middleware in queue or call final handler
- * @param Request $request
- * @return Response
- */
- public function handle($request)
- {
- if (empty($this->queue)) {
- // @todo most handlers already finish sending response
- return $this->handler->handle($request);
- }
- // @todo support other __invoke, callable etc. middleware
- $middleware = array_shift($this->queue);
- return $middleware->process($request, $this);
- }
-}
diff --git a/COPS/cops-3.1.3/src/Handlers/ReadHandler.php b/COPS/cops-3.1.3/src/Handlers/ReadHandler.php
deleted file mode 100644
index a73177f5..00000000
--- a/COPS/cops-3.1.3/src/Handlers/ReadHandler.php
+++ /dev/null
@@ -1,54 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops\Handlers;
-
-use SebLucas\Cops\Input\Config;
-use SebLucas\Cops\Output\EPubReader;
-use SebLucas\Cops\Output\Response;
-use Exception;
-
-/**
- * Handle epub reader with monocle
- * URL format: index.php/read?data={idData}&version={version}
- */
-class ReadHandler extends BaseHandler
-{
- public const HANDLER = "read";
-
- public static function getRoutes()
- {
- return [
- "/read/{db:\d+}/{data:\d+}/{title}" => [static::PARAM => static::HANDLER],
- "/read/{db:\d+}/{data:\d+}" => [static::PARAM => static::HANDLER],
- ];
- }
-
- public function handle($request)
- {
- $idData = $request->getId('data');
- if (empty($idData)) {
- // this will call exit()
- Response::notFound($request);
- }
- $version = $request->get('version', Config::get('epub_reader', 'monocle'));
- $database = $request->database();
-
- $response = new Response('text/html;charset=utf-8');
-
- $reader = new EPubReader($request, $response);
-
- try {
- $response->sendData($reader->getReader($idData, $version, $database));
- } catch (Exception $e) {
- error_log($e);
- Response::sendError($request, $e->getMessage());
- }
- }
-}
diff --git a/COPS/cops-3.1.3/src/Handlers/RestApiHandler.php b/COPS/cops-3.1.3/src/Handlers/RestApiHandler.php
deleted file mode 100644
index c821a65a..00000000
--- a/COPS/cops-3.1.3/src/Handlers/RestApiHandler.php
+++ /dev/null
@@ -1,86 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops\Handlers;
-
-use SebLucas\Cops\Input\Config;
-use SebLucas\Cops\Input\Route;
-use SebLucas\Cops\Output\Format;
-use SebLucas\Cops\Output\Response;
-use SebLucas\Cops\Output\RestApi;
-use Exception;
-
-/**
- * Handle REST API
- * URL format: index.php/restapi{/route}?db={db} etc.
- */
-class RestApiHandler extends BaseHandler
-{
- public const HANDLER = "restapi";
-
- public static function getRoutes()
- {
- // Note: this supports all other routes with /restapi prefix
- // extra routes supported by REST API
- return [
- // add default routes for handler to generate links
- "/restapi/{route:.*}" => [static::PARAM => static::HANDLER],
- "/restapi" => [static::PARAM => static::HANDLER],
- "/custom" => [static::PARAM => static::HANDLER],
- "/databases/{db}/{name}" => [static::PARAM => static::HANDLER],
- "/databases/{db}" => [static::PARAM => static::HANDLER],
- "/databases" => [static::PARAM => static::HANDLER],
- "/openapi" => [static::PARAM => static::HANDLER],
- "/routes" => [static::PARAM => static::HANDLER],
- "/pages" => [static::PARAM => static::HANDLER],
- "/notes/{type}/{id}/{title}" => [static::PARAM => static::HANDLER],
- "/notes/{type}/{id}" => [static::PARAM => static::HANDLER],
- "/notes/{type}" => [static::PARAM => static::HANDLER],
- "/notes" => [static::PARAM => static::HANDLER],
- "/preferences/{key}" => [static::PARAM => static::HANDLER],
- "/preferences" => [static::PARAM => static::HANDLER],
- "/annotations/{bookId}/{id}" => [static::PARAM => static::HANDLER],
- "/annotations/{bookId}" => [static::PARAM => static::HANDLER],
- "/annotations" => [static::PARAM => static::HANDLER],
- "/metadata/{bookId}/{element}/{name}" => [static::PARAM => static::HANDLER],
- "/metadata/{bookId}/{element}" => [static::PARAM => static::HANDLER],
- "/metadata/{bookId}" => [static::PARAM => static::HANDLER],
- "/user/details" => [static::PARAM => static::HANDLER],
- "/user" => [static::PARAM => static::HANDLER],
- ];
- }
-
- public function handle($request)
- {
- // override splitting authors and books by first letter here?
- Config::set('author_split_first_letter', '0');
- Config::set('titles_split_first_letter', '0');
- //Config::set('titles_split_publication_year', '0');
-
- $path = $request->path();
- if (empty($path) || $path == '/restapi/') {
- $data = ['link' => Route::link(static::HANDLER, null, ['route' => 'openapi'])];
- $template = dirname(__DIR__, 2) . '/templates/restapi.html';
-
- $response = new Response('text/html;charset=utf-8');
- $response->sendData(Format::template($data, $template));
- return;
- }
-
- $response = new Response('application/json;charset=utf-8');
-
- $apiHandler = new RestApi($request, $response);
-
- try {
- $response->sendData($apiHandler->getOutput());
- } catch (Exception $e) {
- $response->sendData(json_encode(["Exception" => $e->getMessage()]));
- }
- }
-}
diff --git a/COPS/cops-3.1.3/src/Handlers/ZipFsHandler.php b/COPS/cops-3.1.3/src/Handlers/ZipFsHandler.php
deleted file mode 100644
index e145cca3..00000000
--- a/COPS/cops-3.1.3/src/Handlers/ZipFsHandler.php
+++ /dev/null
@@ -1,64 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops\Handlers;
-
-use SebLucas\Cops\Output\EPubReader;
-use SebLucas\Cops\Output\Response;
-use Exception;
-
-/**
- * Handle Epub filesystem for epubjs-reader
- * URL format: index.php/zipfs/{db}/{data}/{comp}
- */
-class ZipFsHandler extends BaseHandler
-{
- public const HANDLER = "zipfs";
-
- public static function getRoutes()
- {
- // support custom pattern for route placeholders - see nikic/fast-route
- return [
- "/zipfs/{db:\d+}/{data:\d+}/{comp:.+}" => [static::PARAM => static::HANDLER],
- ];
- }
-
- public function handle($request)
- {
- if (php_sapi_name() === 'cli' && $request->getHandler() !== 'phpunit') {
- return;
- }
-
- //$database = $request->getId('db');
- $idData = $request->getId('data');
- if (empty($idData)) {
- // this will call exit()
- Response::notFound($request);
- }
- $component = $request->get('comp');
- if (empty($component)) {
- // this will call exit()
- Response::notFound($request);
- }
- $database = $request->database();
-
- // create empty response to start with!?
- $response = new Response();
-
- $reader = new EPubReader($request, $response);
-
- try {
- $reader->sendZipContent($idData, $component, $database);
-
- } catch (Exception $e) {
- error_log($e);
- Response::sendError($request, $e->getMessage());
- }
- }
-}
diff --git a/COPS/cops-3.1.3/src/Handlers/ZipperHandler.php b/COPS/cops-3.1.3/src/Handlers/ZipperHandler.php
deleted file mode 100644
index 65a73adc..00000000
--- a/COPS/cops-3.1.3/src/Handlers/ZipperHandler.php
+++ /dev/null
@@ -1,64 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops\Handlers;
-
-use SebLucas\Cops\Input\Config;
-use SebLucas\Cops\Output\Response;
-use SebLucas\Cops\Output\Zipper;
-
-/**
- * Download all books for a page, series or author by format (epub, mobi, any, ...)
- * URL format: index.php/zipper?page={page}&type={type}&id={id}
- */
-class ZipperHandler extends BaseHandler
-{
- public const HANDLER = "zipper";
-
- public static function getRoutes()
- {
- // handle endpoint with page param
- return [
- "/zipper/{page}/{type}/{id}" => [static::PARAM => static::HANDLER],
- "/zipper/{page}/{type}" => [static::PARAM => static::HANDLER],
- "/zipper/{page}" => [static::PARAM => static::HANDLER],
- ];
- }
-
- public function handle($request)
- {
- if (empty(Config::get('download_page')) &&
- empty(Config::get('download_series')) &&
- empty(Config::get('download_author'))
- ) {
- // this will call exit()
- Response::sendError($request, 'Downloads by page, series or author are disabled in config');
- }
- if (Config::get('fetch_protect') == '1') {
- session_start();
- if (!isset($_SESSION['connected'])) {
- // this will call exit()
- Response::notFound($request);
- }
- }
-
- $zipper = new Zipper($request);
-
- if ($zipper->isValidForDownload()) {
- $sendHeaders = headers_sent() ? false : true;
- // disable nginx buffering by default
- if ($sendHeaders) {
- header('X-Accel-Buffering: no');
- }
- $zipper->download(null, $sendHeaders);
- } else {
- Response::sendError($request, "Invalid download: " . $zipper->getMessage());
- }
- }
-}
diff --git a/COPS/cops-3.1.3/src/Input/Config.php b/COPS/cops-3.1.3/src/Input/Config.php
deleted file mode 100644
index 75113ed1..00000000
--- a/COPS/cops-3.1.3/src/Input/Config.php
+++ /dev/null
@@ -1,93 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops\Input;
-
-use Exception;
-
-/**
- * Summary of Config
- */
-class Config
-{
- public const VERSION = '3.1.3';
- public const ENDPOINT = [
- "index" => "index.php",
- "feed" => "feed.php",
- "json" => "getJSON.php",
- "fetch" => "fetch.php",
- "read" => "epubreader.php",
- "epubfs" => "epubfs.php",
- "restapi" => "restapi.php",
- "check" => "checkconfig.php",
- "opds" => "opds.php",
- "loader" => "loader.php",
- "zipper" => "zipper.php",
- "calres" => "calres.php",
- "zipfs" => "zipfs.php",
- "mail" => "sendtomail.php",
- "graphql" => "graphql.php",
- ];
- protected const PREFIX = 'cops_';
-
- /**
- * Summary of values
- * @var array
- */
- protected static $values = [];
-
- /**
- * Summary of load
- * @param array $values
- * @return void
- */
- public static function load($values)
- {
- // some phpunit tests re-load the config so we merge here
- static::$values = array_merge(static::$values, $values);
- }
-
- /**
- * Summary of get
- * @param string $name
- * @param mixed $default
- * @throws \Exception
- * @return mixed
- */
- public static function get($name, $default = null)
- {
- if (empty(static::$values)) {
- throw new Exception('Config was not loaded correctly in config/config.php or config/test.php');
- }
- if (array_key_exists(static::PREFIX . $name, static::$values)) {
- return static::$values[static::PREFIX . $name];
- }
- return static::$values[$name] ?? $default;
- }
-
- /**
- * Summary of set
- * @param string $name
- * @param mixed $value
- * @return void
- */
- public static function set($name, $value)
- {
- static::$values[static::PREFIX . $name] = $value;
- }
-
- /**
- * Summary of dump
- * @return array
- */
- public static function dump()
- {
- return static::$values;
- }
-}
diff --git a/COPS/cops-3.1.3/src/Input/Context.php b/COPS/cops-3.1.3/src/Input/Context.php
deleted file mode 100644
index 756da5f8..00000000
--- a/COPS/cops-3.1.3/src/Input/Context.php
+++ /dev/null
@@ -1,40 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops\Input;
-
-/**
- * Summary of Context
- */
-class Context
-{
- /**
- * Summary of request
- * @var Request
- */
- protected Request $request;
-
- /**
- * Summary of __construct
- * @param Request $request
- */
- public function __construct(Request $request)
- {
- $this->request = $request;
- }
-
- /**
- * Summary of getRequest
- * @return Request
- */
- public function getRequest()
- {
- return $this->request;
- }
-}
diff --git a/COPS/cops-3.1.3/src/Input/Request.php b/COPS/cops-3.1.3/src/Input/Request.php
deleted file mode 100644
index ed2c9482..00000000
--- a/COPS/cops-3.1.3/src/Input/Request.php
+++ /dev/null
@@ -1,578 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops\Input;
-
-use SebLucas\Cops\Calibre\Filter;
-use SebLucas\Cops\Output\Response;
-
-/**
- * Summary of Request
- */
-class Request
-{
- /** @var array */
- public $urlParams = [];
- protected string $queryString = '';
- protected string $pathInfo = '';
- protected bool $parsed = true;
- public ?string $content = null;
-
- /**
- * Summary of __construct
- * @param bool $parse
- */
- public function __construct($parse = true)
- {
- $this->parseParams($parse);
- }
-
- /**
- * Summary of useServerSideRendering
- * @return bool|int
- */
- public function render()
- {
- return preg_match('/' . Config::get('server_side_render') . '/', $this->agent());
- }
-
- /**
- * Summary of query
- * @return string
- */
- public function query()
- {
- return $this->queryString;
- }
-
- /**
- * Summary of method
- * @return ?string
- */
- public function method()
- {
- return $this->server('REQUEST_METHOD');
- }
-
- /**
- * Summary of agent
- * @return string
- */
- public function agent()
- {
- return $this->server('HTTP_USER_AGENT') ?? "";
- }
-
- /**
- * Summary of language
- * @return ?string
- */
- public function language()
- {
- return $this->server('HTTP_ACCEPT_LANGUAGE');
- }
-
- /**
- * Summary of path
- * @param string $default
- * @return string
- */
- public function path($default = '')
- {
- return $this->server('PATH_INFO') ?? $default;
- }
-
- /**
- * Summary of script
- * @return ?string
- */
- public function script()
- {
- return $this->server('SCRIPT_NAME');
- }
-
- /**
- * Summary of uri
- * @return ?string
- */
- public function uri()
- {
- return $this->server('REQUEST_URI');
- }
-
- /**
- * Summary of parseParams
- * @param bool $parse
- * @return void
- */
- public function parseParams($parse = true)
- {
- $this->parsed = $parse;
- $this->urlParams = [];
- $this->queryString = '';
- if (!$this->parsed) {
- return;
- }
- $path = $this->path();
- if (!empty(Config::get('use_route_urls'))) {
- $params = Route::match($path);
- if (is_null($params)) {
- // this will call exit()
- //Response::sendError($this, "Invalid request path '$path'");
- error_log("Invalid COPS request path '$path'");
- $params = [];
- }
- // JsonHandler uses same routes as HtmlHandler - see util.js
- if (empty($params[Route::HANDLER_PARAM]) && $this->isAjax()) {
- $params[Route::HANDLER_PARAM] = 'json';
- }
- foreach ($params as $name => $value) {
- $this->urlParams[$name] = $value;
- }
- //} elseif (empty($this->urlParams[Route::HANDLER_PARAM]) && $this->isAjax()) {
- // $this->urlParams[Route::HANDLER_PARAM] = 'json';
- }
- if (!empty($_GET)) {
- foreach ($_GET as $name => $value) {
- // remove ajax timestamp for jQuery cache = false
- if ($name == '_') {
- continue;
- }
- $this->urlParams[$name] = $_GET[$name];
- }
- }
- // get virtual library from option (see customize)
- if (!isset($this->urlParams['vl']) && !empty($this->option('virtual_library'))) {
- $this->urlParams['vl'] = $this->option('virtual_library');
- }
- if (!empty(Config::get('calibre_user_database'))) {
- $user = $this->getUserName();
- // @todo use restriction etc. from Calibre user database
- }
- $this->queryString = $_SERVER['QUERY_STRING'] ?? '';
- $this->pathInfo = $_SERVER['PATH_INFO'] ?? '';
- }
-
- /**
- * Summary of hasFilter
- * @return bool
- */
- public function hasFilter()
- {
- // see list of acceptable filter params in Filter.php
- $find = Filter::URL_PARAMS;
- return !empty(array_intersect_key($find, $this->urlParams));
- }
-
- /**
- * Summary of getFilterParams
- * @return array
- */
- public function getFilterParams()
- {
- // see list of acceptable filter params in Filter.php
- $find = Filter::URL_PARAMS;
- return array_intersect_key($this->urlParams, $find);
- }
-
- /**
- * Summary of get
- * @param string $name
- * @param mixed $default
- * @param ?string $pattern
- * @return mixed
- */
- public function get($name, $default = null, $pattern = null)
- {
- if (!empty($this->urlParams) && isset($this->urlParams[$name]) && $this->urlParams[$name] != '') {
- if (!isset($pattern) || preg_match($pattern, (string) $this->urlParams[$name])) {
- return $this->urlParams[$name];
- }
- }
- return $default;
- }
-
- /**
- * Summary of set
- * @param string $name
- * @param mixed $value
- * @return void
- */
- public function set($name, $value)
- {
- $this->urlParams[$name] = $value;
- $this->queryString = http_build_query($this->urlParams);
- }
-
- /**
- * Summary of post
- * @param string $name
- * @return mixed
- */
- public function post($name)
- {
- return $_POST[$name] ?? null;
- }
-
- /**
- * Summary of request
- * @param string $name
- * @return mixed
- */
- public function request($name)
- {
- return $_REQUEST[$name] ?? null;
- }
-
- /**
- * Summary of server
- * @param string $name
- * @return mixed
- */
- public function server($name)
- {
- return $_SERVER[$name] ?? null;
- }
-
- /**
- * Summary of session
- * @param string $name
- * @return mixed
- */
- public function session($name)
- {
- return $_SESSION[$name] ?? null;
- }
-
- /**
- * Summary of cookie
- * @param string $name
- * @return mixed
- */
- public function cookie($name)
- {
- return $_COOKIE[$name] ?? null;
- }
-
- /**
- * Summary of files
- * @param string $name
- * @return mixed
- */
- public function files($name)
- {
- return $_FILES[$name] ?? null;
- }
-
- /**
- * Summary of content
- * @return mixed
- */
- public function content()
- {
- if (!isset($this->content)) {
- $this->content = file_get_contents('php://input');
- }
- return $this->content;
- }
-
- /**
- * Summary of option
- * @param string $option
- * @return mixed
- */
- public function option($option)
- {
- if (!is_null($this->cookie($option))) {
- if (!is_null(Config::get($option)) && is_array(Config::get($option))) {
- return explode(',', (string) $this->cookie($option));
- } elseif (!preg_match('/[^A-Za-z0-9\-_.@()]/', (string) $this->cookie($option))) {
- return $this->cookie($option);
- }
- }
- if (!is_null(Config::get($option))) {
- return Config::get($option);
- }
-
- return '';
- }
-
- /**
- * Summary of style
- * @return string
- */
- public function style()
- {
- $style = $this->option('style');
- if (!preg_match('/[^A-Za-z0-9\-_]/', (string) $style)) {
- return 'templates/' . $this->template() . '/styles/style-' . $this->option('style') . '.css';
- }
- return 'templates/' . Config::get('template') . '/styles/style-' . Config::get('style') . '.css';
- }
-
- /**
- * Summary of template
- * @return string
- */
- public function template()
- {
- $template = $this->option('template');
- if (!preg_match('/[^A-Za-z0-9\-_]/', (string) $template) && is_dir("templates/{$template}/")) {
- return $template;
- }
- return Config::get('template');
- }
-
- /**
- * Summary of getIntOrNull
- * @param string $name
- * @return ?int
- */
- protected function getIntOrNull($name)
- {
- $value = $this->get($name, null, '/^\d+$/');
- if (!is_null($value)) {
- return (int) $value;
- }
- return null;
- }
-
- /**
- * Summary of getId
- * @param string $name
- * @return ?int
- */
- public function getId($name = 'id')
- {
- return $this->getIntOrNull($name);
- }
-
- /**
- * Summary of database
- * @return ?int
- */
- public function database()
- {
- return $this->getIntOrNull('db');
- }
-
- /**
- * Summary of getVirtualLibrary
- * @param bool $strip
- * @return int|string|null
- */
- public function getVirtualLibrary($strip = false)
- {
- $libraryId = $this->get('vl', null);
- if (empty($libraryId)) {
- return null;
- }
- // URL format: ...&vl=2.Short_Stories_in_English
- if ($strip && str_contains((string) $libraryId, '.')) {
- [$libraryId, $slug] = explode('.', (string) $libraryId);
- }
- return $libraryId;
- }
-
- /**
- * Summary of getUserName
- * @param ?string $name
- * @return string|null
- */
- public function getUserName($name = null)
- {
- $http_auth_user = Config::get('http_auth_user', 'PHP_AUTH_USER');
- $name ??= $this->server($http_auth_user);
- return $name;
- }
-
- /**
- * Summary of getSorted
- * @param ?string $default
- * @return ?string
- */
- public function getSorted($default = null)
- {
- return $this->get('sort', $default, '/^\w+(\s+(asc|desc)|)$/i');
- // ?? $this->option('sort');
- }
-
- /**
- * Summary of getCurrentUrl
- * @param ?string $handler
- * @return string
- */
- public function getCurrentUrl($handler = null)
- {
- $handler ??= $this->getHandler();
- $pathInfo = $this->path();
- $queryString = $this->query();
- if (empty($queryString)) {
- return Route::link($handler) . $pathInfo;
- }
- return Route::link($handler) . $pathInfo . '?' . $queryString;
- }
-
- /**
- * Summary of getEndpoint
- * @param string $default
- * @deprecated 3.1.0 use getHandler() instead
- * @return string
- */
- public function getEndpoint($default)
- {
- $script = explode("/", $this->script() ?? "/" . $default);
- $link = array_pop($script);
- // see former LinkNavigation
- if (preg_match("/(bookdetail|getJSON).php/", $link)) {
- return $default;
- }
- return $link;
- }
-
- /**
- * Summary of getHandler
- * @param string $default
- * @return string
- */
- public function getHandler($default = 'index')
- {
- // we have a handler already
- if (!empty($this->urlParams[Route::HANDLER_PARAM])) {
- return $this->urlParams[Route::HANDLER_PARAM];
- }
- // @deprecated 3.1.0 use index.php endpoint
- // try to find handler via endpoint
- $endpoint = $this->getEndpoint(Config::ENDPOINT[$default]);
- if ($endpoint == Config::ENDPOINT['index']) {
- return 'index';
- }
- $flipped = array_flip(Config::ENDPOINT);
- if (!empty($flipped[$endpoint])) {
- return $flipped[$endpoint];
- }
- // for phpunit tests
- if ($endpoint == 'phpunit' || $endpoint == 'Standard input code') {
- return $default;
- }
- // how did we end up here?
- throw new \Exception('Unknown handler for endpoint ' . htmlspecialchars($endpoint));
- //return $default;
- }
-
- /**
- * Summary of getCleanParams
- * @return array
- */
- public function getCleanParams()
- {
- $params = $this->urlParams;
- unset($params['title']);
- unset($params['_']);
- unset($params['n']);
- unset($params['complete']);
- //unset($params[Route::HANDLER_PARAM]);
- return $params;
- }
-
- /**
- * Summary of getApiKey
- * @return string
- */
- public function getApiKey()
- {
- // If you send header X-Api-Key from your client, the web server will turn this into HTTP_X_API_KEY
- // For Nginx proxy configurations you may need to add something like this:
- // proxy_set_header X-Api-Key $http_x_api_key;
- return $this->server('HTTP_X_API_KEY') ?? '';
- }
-
- /**
- * Summary of hasValidApiKey
- * @return bool
- */
- public function hasValidApiKey()
- {
- if (empty(Config::get('api_key')) || Config::get('api_key') !== $this->getApiKey()) {
- return false;
- }
- return true;
- }
-
- /**
- * Summary of isAjax
- * @return bool
- */
- public function isAjax()
- {
- // for jquery etc. if passed along by proxy
- if ($this->server('HTTP_X_REQUESTED_WITH') == 'XMLHttpRequest') {
- return true;
- }
- // for fetch etc. if Accept header is specified
- if (str_contains($this->server('HTTP_ACCEPT') ?? '', 'application/json')) {
- return true;
- }
- // @todo https://developer.mozilla.org/en-US/docs/Web/HTTP/Headers/Sec-Fetch-Mode
- return false;
- }
-
- /**
- * Summary of isJson
- * @return bool
- */
- public function isJson()
- {
- // @deprecated 3.1.0 use index.php endpoint
- // using actual getJSON.php endpoint, or
- // set in parseParams() based on isAjax()
- $handler = $this->getHandler();
- if ($handler == 'json') {
- return true;
- }
- return false;
- }
-
- /**
- * Summary of isFeed
- * @return bool
- */
- public function isFeed()
- {
- // @deprecated 3.1.0 use index.php endpoint
- // using actual feed.php or opds.php endpoint, or
- // set in parseParams() based on Route::match()
- $handler = $this->getHandler();
- if (in_array($handler, ['feed', 'opds'])) {
- return true;
- }
- return false;
- }
-
- /**
- * Summary of build
- * @param array $params ['db' => $db, 'page' => $pageId, 'id' => $id, 'query' => $query, 'n' => $n]
- * @param string $handler
- * @param ?array $server
- * @param ?array $cookie
- * @param ?array $config
- * @return Request
- */
- public static function build($params = [], $handler = '', $server = null, $cookie = null, $config = null)
- {
- // ['db' => $db, 'page' => $pageId, 'id' => $id, 'query' => $query, 'n' => $n]
- if (!empty($handler)) {
- $params[Route::HANDLER_PARAM] ??= $handler;
- }
- $request = new self(false);
- $request->urlParams = $params;
- $request->queryString = http_build_query($request->urlParams);
- return $request;
- }
-}
diff --git a/COPS/cops-3.1.3/src/Input/Route.php b/COPS/cops-3.1.3/src/Input/Route.php
deleted file mode 100644
index 0040f768..00000000
--- a/COPS/cops-3.1.3/src/Input/Route.php
+++ /dev/null
@@ -1,685 +0,0 @@
-
- * @author mikespub
- */
-
-namespace SebLucas\Cops\Input;
-
-use FastRoute\Dispatcher;
-use FastRoute\RouteCollector;
-use SebLucas\Cops\Input\Config;
-use SebLucas\Cops\Language\Translation;
-
-use function FastRoute\simpleDispatcher;
-
-/**
- * Summary of Route
- */
-class Route
-{
- public const HANDLER_PARAM = "_handler";
- public const SYMFONY_REQUEST = '\Symfony\Component\HttpFoundation\Request';
-
- /** @var ?\Symfony\Component\HttpFoundation\Request */
- protected static $proxyRequest = null;
- /** @var ?string */
- protected static $baseUrl = null;
- /** @var array */
- protected static $routes = [];
- /** @var string[] */
- protected static $skipPrefix = ['index', 'json', 'fetch', 'restapi', 'graphql', 'phpunit'];
- /** @var Dispatcher|null */
- protected static $dispatcher = null;
- /** @var array */
- protected static $pages = [];
- // with use_url_rewriting = 1 - basic rewrites only
- /** @var array */
- protected static $rewrites = [
- // Format: route => handler, or route => [handler, [fixed => 1, ...]] with fixed params
- "/view/{data}/{db}/{ignore}.{type}" => ["fetch", ["view" => 1]],
- "/view/{data}/{ignore}.{type}" => ["fetch", ["view" => 1]],
- "/download/{data}/{db}/{ignore}.{type}" => ["fetch"],
- "/download/{data}/{ignore}.{type}" => ["fetch"],
- ];
- /** @var array */
- protected static $handlers = [];
-
- /**
- * Match pathinfo against routes and return query params
- * @param string $path
- * @return ?array array of query params or null if not found
- */
- public static function match($path)
- {
- if (empty($path) || $path == '/') {
- return [];
- }
-
- // match exact path
- if (static::has($path)) {
- return static::get($path);
- }
-
- // match pattern
- $fixed = [];
- $params = [];
- $method = 'GET';
-
- $dispatcher = static::getSimpleDispatcher();
- $routeInfo = $dispatcher->dispatch($method, $path);
- switch ($routeInfo[0]) {
- case Dispatcher::NOT_FOUND:
- // ... 404 Not Found
- //http_response_code(404);
- //throw new Exception("Invalid route " . htmlspecialchars($path));
- return null;
- case Dispatcher::METHOD_NOT_ALLOWED:
- //$allowedMethods = $routeInfo[1];
- // ... 405 Method Not Allowed
- //header('Allow: ' . implode(', ', $allowedMethods));
- //http_response_code(405);
- //throw new Exception("Invalid method " . htmlspecialchars($method) . " for route " . htmlspecialchars($path));
- return null;
- case Dispatcher::FOUND:
- $fixed = $routeInfo[1];
- $params = $routeInfo[2];
- }
- // for normal routes, put fixed params at the start
- $params = array_merge($fixed, $params);
- unset($params['ignore']);
- return $params;
- }
-
- /**
- * Check if static route exists
- * @param string $route
- * @return bool
- */
- public static function has($route)
- {
- return array_key_exists($route, static::$routes);
- }
-
- /**
- * Get query params for static route
- * @param string $route
- * @return array
- */
- public static function get($route)
- {
- $page = static::$routes[$route];
- if (is_array($page)) {
- return $page;
- }
- return ["page" => $page];
- }
-
- /**
- * Set route to page with optional static params
- * @param string $route
- * @param string $page
- * @param array $params
- * @return void
- */
- public static function set($route, $page, $params = [])
- {
- if (empty($params)) {
- static::$routes[$route] = $page;
- return;
- }
- $params["page"] = $page;
- static::$routes[$route] = $params;
- }
-
- /**
- * Add prefix for paths with this endpoint
- * @param string $name
- * @return bool
- */
- public static function addPrefix($name)
- {
- return !in_array($name, static::$skipPrefix);
- }
-
- /**
- * Summary of getSimpleDispatcher
- * @return Dispatcher
- */
- public static function getSimpleDispatcher()
- {
- static::$dispatcher ??= simpleDispatcher(function (RouteCollector $r) {
- static::addRouteCollection($r);
- });
- return static::$dispatcher;
- }
-
- /**
- * Summary of addRouteCollection
- * @param RouteCollector $r
- * @return void
- */
- public static function addRouteCollection($r)
- {
- foreach (static::getRoutes() as $route => $queryParams) {
- $r->addRoute('GET', $route, $queryParams);
- }
- }
-
- /**
- * Get routes and query params
- * @return array>
- */
- public static function getRoutes()
- {
- $routeMap = [];
- foreach (array_keys(static::$routes) as $route) {
- $routeMap[$route] = static::get($route);
- }
- return $routeMap;
- }
-
- /**
- * Add routes and query params
- * @param array> $routes
- * @return void
- */
- public static function addRoutes($routes)
- {
- static::$routes = array_merge(static::$routes, $routes);
- }
-
- /**
- * Set routes
- * @param array
|