Merge branch 'newui' into 0.5.0--docker
1
.gitignore
vendored
@@ -2,6 +2,7 @@ books/*
|
||||
*.epub
|
||||
*.idea
|
||||
*.pyc
|
||||
.svn/
|
||||
app/content.opf
|
||||
.vscode
|
||||
fontend/db.sqlite3
|
||||
|
||||
78
README.md
vendored
@@ -1,16 +1,18 @@
|
||||
# pyShelf 0.5.0
|
||||
# pyShelf 0.6.0
|
||||
|
||||
<p align="center"><b>Terminal based ebook server. Open source & Lightweight.</b></p>
|
||||
<p align="center">Having used Calibre for hosting my eBook collection in the past, I found myself frustrated having to install X on my server, or manage my library externally, Thus I have decided to spin up my own.</p>
|
||||
<p align="center"><a href="https://pyshelf.com">https://pyshelf.com</a></p>
|
||||
|
||||

|
||||

|
||||

|
||||
|
||||
<p align="center"><b>Discord [https://discord.gg/H9TbNJS](https://discord.gg/H9TbNJS) | IRC freenode.net @ #pyshelf</b></p>
|
||||
|
||||
### You dont need a X server to host a website, or your Movie & Tv collection, so why should you need one to host ebooks?
|
||||
<i>Other solutiions require you to have access to an X server to at the very least generate your book database, pyShelf doesnt.We aim to provide a fully featured ebook server with minimal requirements, and no reliance on X whatsoever.</i>
|
||||
|
||||
Follow or influence development @ <p align="center"><b> <a href="https://discord.gg/H9TbNJS">Discord</a> | <a href="https://webchat.freenode.net/#pyshelf">IRC</a> freenode.net @ #pyshelf</b></p>
|
||||
## Current Features
|
||||
* Custom Installer -- pre-req installs work on Arch Based Distros Only
|
||||
* Custom Installer works only on Arch Based Distros
|
||||
* Recursive Scanning
|
||||
* Fast database access
|
||||
* Django based frontend
|
||||
@@ -20,36 +22,64 @@
|
||||
|
||||
## Currently Supported Formats
|
||||
* epub
|
||||
* mobi
|
||||
|
||||
|
||||
## 0.6.0 Patch Notes.
|
||||
# New Features
|
||||
* .mobi Yep mobis are now a thing!
|
||||
* Result set ordering
|
||||
* You can now choose to order your results:
|
||||
* Title
|
||||
* Author
|
||||
* Categories
|
||||
* & Tags
|
||||
* Reworked UI/UX
|
||||
* More intuitive, less intrusive, & stays out of the way. <i>caveat: I need to rework the placement of the next & previous page controls. While they do remain usable, I intend to have them follow the users</i>
|
||||
position on the page in future releases.
|
||||
|
||||

|
||||
|
||||
* New controls
|
||||
* Sort
|
||||
* Ascending / Descending result set
|
||||
* Display of the result set count, and your current position in the set.
|
||||
* A pop over layer to hold things like
|
||||
* [ ] User login
|
||||
* [ ] Control panel
|
||||
* [ ] Book details
|
||||
* Whatever else :)
|
||||
## Installation Example
|
||||
<a href="https://vimeo.com/382292764" target="_blank">pyShelf Installation Video</a>
|
||||
|
||||
## Further Installation & Support Information
|
||||
* [SUPPORT.md](https://github.com/th3r00t/pyShelf/blob/development/.github/SUPPORT.md)
|
||||
|
||||
## 0.5.0 Patch Notes.
|
||||
|
||||
### Pre-req Dependencies
|
||||
* gcc -- This will be installed by the new pre-installer script if its binary is not detected at /usr/bin/gcc
|
||||
Users on distros other then Arch should install gcc via their systems package manager prior to
|
||||
running the installer.
|
||||
* Python3
|
||||
* pip
|
||||
### New Features
|
||||
* Collections
|
||||
We are now categorizing your ebooks into collections based on the folder
|
||||
structure used to store them. Any folder after the root book folder is now
|
||||
considered as a collection.
|
||||
#### books/forgotten realms/ -> Forgotten Realms Collection.
|
||||
#### books/Dune/Prelude To Dune -> Dune, & Preluse To Dune Collections.
|
||||
|
||||
In addition to the work on the collection system, a good deal of time was spent
|
||||
on the installer, and the concept of having an installer in the first place.
|
||||
|
||||
I mainly wanted to make this project for Network Administrators, and other home
|
||||
# Installation
|
||||
This project is currently targeted towards Network Administrators, and other home
|
||||
enthusiasts whom I assume will know how to setup a Django app, and a
|
||||
Postgres server. Beyond that theres nothing the user has to do to make the
|
||||
system work...
|
||||
Postgres server.
|
||||
|
||||
Once your environment is ready very little is required to get the system up and running
|
||||
* From the main directory
|
||||
* setup configurations as discussed in [SUPPORT.md](https://github.com/th3r00t/pyShelf/blob/development/.github/SUPPORT.md)
|
||||
* `pip install -r requirments.txt`
|
||||
* `cd src`
|
||||
* `python manage.py migrate`
|
||||
* `cd ..`
|
||||
* `./importbooks`
|
||||
* `./makecollections`
|
||||
* Browse to the site as defined in your apache | nginx config
|
||||
|
||||
## Included installer
|
||||
<a href="https://vimeo.com/382292764" target="_blank">pyShelf Installation Video</a>
|
||||
|
||||
The installer will only run correctly on arch based distros. This could be
|
||||
easily rectified to include other package managers, Members of the community
|
||||
@@ -61,6 +91,12 @@ installation already present in the source now, however it is not complete and
|
||||
should not be relied upon to be present in future releases unless completed by
|
||||
a member of the community,
|
||||
|
||||
The installer will walk you through all the configurations required by pyShelf to
|
||||
run if you are running on Arch linux.
|
||||
|
||||
## Further Installation & Support Information
|
||||
* [SUPPORT.md](https://github.com/th3r00t/pyShelf/blob/development/.github/SUPPORT.md)
|
||||
|
||||
## Development
|
||||
|
||||
* [`pre-commit`](https://pre-commit.com/)
|
||||
@@ -118,7 +154,7 @@ Once the .epub files are in the directory specified in [docker/.env](docker/.env
|
||||
#### Improved cover image storage, and acquisition.
|
||||
#### OPDS Support
|
||||
#### Support for other formats
|
||||
- [ ] .mobi
|
||||
- [x] .mobi
|
||||
- [ ] .pdf
|
||||
- [ ] .cbz
|
||||
- [ ] .zip (Zipped book folders, is this a new idea? (Consider storing your library folders zipped and retrieving a book on demand))
|
||||
|
||||
2
config.json
vendored
@@ -1 +1 @@
|
||||
{"TITLE": "pyShelf E-Book Server", "VERSION": "0.5.0", "BOOKPATH": "/srv/Books", "DB_HOST": "localhost", "DB_PORT": "5432", "DATABASE": "pyshelf", "USER": "pyshelf", "PASSWORD": "pyshelf", "BOOKSHELF": "data/shelf.json", "ALLOWED_HOSTS": "*", "hostname": "localhost", "webport": "8000", "wsgiport": "8001"}
|
||||
{"TITLE": "pyShelf E-Book Server", "VERSION": "0.6.0", "BOOKPATH": "/home/raelon/Books", "DB_HOST": "localhost", "DB_PORT": "5432", "DATABASE": "pyshelf", "USER": "pyshelf", "PASSWORD": "pyshelf", "BOOKSHELF": "data/shelf.json", "ALLOWED_HOSTS": "*", "hostname": "localhost", "webport": "8000", "wsgiport": "8001"}
|
||||
2
importBooks
vendored
@@ -1,4 +1,4 @@
|
||||
#!python
|
||||
#!/usr/bin/env python
|
||||
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
0
importBooks.pstat
vendored
Normal file
11
installer
vendored
@@ -1,4 +1,4 @@
|
||||
#!python
|
||||
#!/usr/bin/ env python
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
@@ -304,8 +304,8 @@ if RequiredServices().db_server_found(req) is False:
|
||||
if r["name"] == "PASSWORD":
|
||||
sql_pass = r["answer"]
|
||||
|
||||
# sql_user = config["USER"]
|
||||
sql_user = "pyshelf"
|
||||
sql_user = config["USER"]
|
||||
# sql_user = "pyshelf"
|
||||
db_name = "pyshelf"
|
||||
psql_cmd = (
|
||||
"CREATE DATABASE %s; CREATE USER %s WITH PASSWORD '%s'; \
|
||||
@@ -333,6 +333,11 @@ if RequiredServices().db_server_found(req) is False:
|
||||
psql_cmd,
|
||||
]
|
||||
else:
|
||||
for r in install_answers:
|
||||
if r["name"] == "PASSWORD":
|
||||
sql_pass = r["answer"]
|
||||
sql_user = config["USER"]
|
||||
db_name = "pyshelf"
|
||||
psql_cmd = (
|
||||
"CREATE DATABASE %s; CREATE USER %s WITH PASSWORD '%s'; \
|
||||
GRANT ALL PRIVILEGES ON DATABASE %s TO %s;"
|
||||
|
||||
2
makeCollections
vendored
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
|
||||
import pathlib
|
||||
import sys
|
||||
|
||||
BIN
preview_050.png
vendored
|
Before Width: | Height: | Size: 1.7 MiB |
BIN
preview_1_050.png
vendored
|
Before Width: | Height: | Size: 2.4 MiB |
2
pyproject.toml
vendored
@@ -7,4 +7,4 @@ use_parentheses = true
|
||||
# NOTE: the known_third_party setting is managed by
|
||||
# seed-isort-config and should not be modified directly.
|
||||
# Any changes made to this setting will be overwritten.
|
||||
known_third_party = ["backend", "bs4", "django", "interface", "prompt_toolkit", "psycopg2", "pyfiglet", "requests"]
|
||||
known_third_party = ["backend", "bs4", "django", "interface", "mobi", "prompt_toolkit", "psycopg2", "pyfiglet", "requests"]
|
||||
|
||||
6
requirements.txt
vendored
@@ -16,4 +16,8 @@ django-debug-toolbar
|
||||
psycopg2-binary
|
||||
prompt_toolkit
|
||||
psutil
|
||||
pyfiglet
|
||||
pyfiglet
|
||||
mobi-python
|
||||
pudb
|
||||
jsonpickle
|
||||
django-widget-tweaks
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
import json
|
||||
import os
|
||||
import pathlib
|
||||
@@ -7,6 +7,8 @@ import zipfile
|
||||
|
||||
from bs4 import BeautifulSoup
|
||||
|
||||
from mobi import Mobi
|
||||
|
||||
from .api_hooks import DuckDuckGo
|
||||
from .config import Config
|
||||
from .storage import Storage
|
||||
@@ -24,10 +26,11 @@ class Catalogue:
|
||||
self.opf_regx = re.compile(r"\.opf")
|
||||
self.cover_regx = re.compile(r"\.jpg|\.jpeg|\.png|\.bmp|\.gif")
|
||||
self.html_regx = re.compile(r"\.html")
|
||||
self.title_sanitization_regx = re.compile(r"^(Book )+[0-9]*")
|
||||
self.title_sanitization_lvl2_regx = re.compile(r"^(Book )+[0-9]*\W+(-)")
|
||||
self.title_sanitization_dirs_regx = re.compile(r"/")
|
||||
self.root_dir = config.root
|
||||
self.book_folder = config.book_path
|
||||
self.book_shelf = config.book_shelf
|
||||
self._book_list_expanded = None
|
||||
self.books = None
|
||||
self.db_pointer = config.catalogue_db
|
||||
self.config = config
|
||||
@@ -56,24 +59,31 @@ class Catalogue:
|
||||
|
||||
:returns self._book_list_expanded: json string containing all book metadata
|
||||
"""
|
||||
self.scan_folder() # Populate file list
|
||||
regx = re.compile(r"\.epub")
|
||||
self.scan_folder() # Populate file list
|
||||
regx = re.compile(r"\.epub|\.mobi")
|
||||
try:
|
||||
self.books = list(filter(regx.search, filter(None, self.file_list)))
|
||||
except TypeError as e:
|
||||
print(e)
|
||||
self._book_list_expanded = {}
|
||||
with open(self.book_shelf, "w") as f:
|
||||
for book in self.books:
|
||||
self._book_list_expanded[book] = self.process_book(book)
|
||||
json.dump(self._book_list_expanded, f)
|
||||
"""
|
||||
for book in self.books:
|
||||
self._book_list_expanded[book] = self.process_by_filetype(book)
|
||||
return self._book_list_expanded
|
||||
"""
|
||||
|
||||
def process_by_filetype(self, book):
|
||||
print(str(book), end='\r', flush=True)
|
||||
if book.endswith(".epub"):
|
||||
epub = self.process_epub(book)
|
||||
return self.extract_metadata_epub(epub)
|
||||
elif book.endswith(".mobi"):
|
||||
return self.extract_metadata_mobi(book)
|
||||
|
||||
@staticmethod
|
||||
def process_book(book):
|
||||
def process_epub(book):
|
||||
"""Return dictionary of epub file contents"""
|
||||
book = zipfile.ZipFile(book, "r")
|
||||
details = {}
|
||||
book = zipfile.ZipFile(book, "r")
|
||||
with book as book_zip:
|
||||
details["files"] = []
|
||||
details["path"] = book.filename
|
||||
@@ -86,7 +96,7 @@ class Catalogue:
|
||||
details["files"].append(match.string)
|
||||
return details
|
||||
|
||||
def extract_metadata(self, book):
|
||||
def extract_metadata_epub(self, book):
|
||||
"""
|
||||
Return extracted metadata and cover picture
|
||||
book['path'] == Full path to ebook file
|
||||
@@ -94,24 +104,123 @@ class Catalogue:
|
||||
"""
|
||||
book_zip = zipfile.ZipFile(book["path"], "r")
|
||||
with book_zip as f:
|
||||
content = self.extract_content(book_zip, book)
|
||||
content = self.extract_content(f, book)
|
||||
soup = BeautifulSoup(content, "lxml")
|
||||
title = soup.find("dc:title")
|
||||
if title is None:
|
||||
title = book["path"].split("/")[-1].rsplit(".", 1)[0]
|
||||
else:
|
||||
title = title.contents[0]
|
||||
if re.match(self.title_sanitization_regx, title):
|
||||
if re.match(self.title_sanitization_lvl2_regx, title):
|
||||
title = re.split(r"-+\W", title)[1]
|
||||
else: title = re.split(self.title_sanitization_regx, title)[2]
|
||||
|
||||
author = soup.find("dc:creator")
|
||||
if author is not None:
|
||||
author = author.contents[0]
|
||||
try:
|
||||
cover = self.extract_cover_image(book_zip, book)
|
||||
cover = self.extract_cover_image(f, book)
|
||||
except IndexError:
|
||||
# cover = self.extract_cover_html(book_zip, book)
|
||||
cover = DuckDuckGo().image_result(title)
|
||||
book_details = [title, author, cover, book["path"]]
|
||||
try:
|
||||
description = self.stripTags(soup.find("dc:description").text)
|
||||
except AttributeError:
|
||||
description = None
|
||||
try:
|
||||
identifier = self.stripTags(soup.find("dc:identifier").text)
|
||||
except AttributeError:
|
||||
identifier = None
|
||||
try:
|
||||
publisher = self.stripTags(soup.find("dc:publisher").text)
|
||||
except AttributeError:
|
||||
publisher = None
|
||||
try:
|
||||
date = self.stripTags(soup.find("dc:date").text)
|
||||
except AttributeError:
|
||||
date = None
|
||||
try:
|
||||
rights = self.stripTags(soup.find("dc:rights").text)
|
||||
except AttributeError:
|
||||
rights = None
|
||||
try:
|
||||
tags = soup.find_all("dc:subject")
|
||||
except AttributeError:
|
||||
tags = None
|
||||
ftags = None
|
||||
if tags is not None:
|
||||
for tag in tags:
|
||||
if ftags is None:
|
||||
ftags = tag.text
|
||||
else:
|
||||
ftags = ftags + "," + tag.text
|
||||
book_details = [
|
||||
title,
|
||||
author,
|
||||
cover,
|
||||
book["path"],
|
||||
description,
|
||||
identifier,
|
||||
publisher,
|
||||
date,
|
||||
rights,
|
||||
ftags,
|
||||
]
|
||||
return book_details
|
||||
|
||||
@staticmethod
|
||||
def stripTags(source):
|
||||
p = re.compile(r"<.*?>")
|
||||
return p.sub("", source)
|
||||
|
||||
def extract_metadata_mobi(self, book):
|
||||
book = Mobi(book)
|
||||
book.parse()
|
||||
try:
|
||||
cover_image = book.readImageRecord(0)
|
||||
except KeyError:
|
||||
cover_image = None
|
||||
title = book.title().decode("utf-8")
|
||||
author = book.author().decode("utf-8")
|
||||
book_config = book.config
|
||||
try:
|
||||
description = self.stripTags(book_config['exth']['records'][103].decode("utf-8"))
|
||||
except KeyError:
|
||||
description = None
|
||||
try:
|
||||
identifier = book_config['exth']['records'][104].decode("utf-8")
|
||||
except KeyError:
|
||||
identifier = None
|
||||
try:
|
||||
publisher = book_config['exth']['records'][101].decode("utf-8")
|
||||
except KeyError:
|
||||
publisher = None
|
||||
date = None
|
||||
rights = None
|
||||
try:
|
||||
ftags = book_config['exth']['records'][105].decode("utf-8")
|
||||
if ":" in ftags:
|
||||
ftags = ftags.replace(":", ",")
|
||||
elif ";" in ftags:
|
||||
ftags = ftags.replace(";", ",")
|
||||
# elif re.search(r"\s", ftags): # Must be final assignment to avoid spliting on multiple delimeters
|
||||
# ftags = ftags.replace(" ", ",")
|
||||
except KeyError:
|
||||
ftags = None
|
||||
return [
|
||||
title,
|
||||
author,
|
||||
cover_image,
|
||||
book.f.name,
|
||||
description,
|
||||
identifier,
|
||||
publisher,
|
||||
date,
|
||||
rights,
|
||||
ftags,
|
||||
]
|
||||
|
||||
def extract_content(self, book_zip, book):
|
||||
"""
|
||||
Opens epub as zip file filters then stores as list any files matching opf_regx
|
||||
@@ -161,12 +270,13 @@ class Catalogue:
|
||||
Gets a list of new files via compare_shelf_current.
|
||||
Iterates over list and inserts new books into database.
|
||||
"""
|
||||
# TODO Refactor metadata extraction into process_book \
|
||||
# call to more easily handle additional formats
|
||||
book_list = self.compare_shelf_current()
|
||||
db = Storage(self.config)
|
||||
for book in book_list:
|
||||
book = self.process_book(book)
|
||||
extracted = self.extract_metadata(book)
|
||||
db.insert_book(extracted)
|
||||
book = self.process_by_filetype(book)
|
||||
db.insert_book(book)
|
||||
inserted = db.commit()
|
||||
if inserted is not True:
|
||||
print(inserted)
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
#!/usr/bin/python
|
||||
import re
|
||||
|
||||
import datetime
|
||||
import psycopg2
|
||||
|
||||
|
||||
@@ -53,7 +53,7 @@ class Storage:
|
||||
Insert book in database
|
||||
:returns: True if succeeds False if not
|
||||
"""
|
||||
q = "INSERT INTO books (title, author, cover, progress, file_name, pages) values (%s, %s, %s, 0, %s, 0);"
|
||||
q = "INSERT INTO books (title, author, cover, progress, file_name, pages, description, identifier, publisher, date, rights, tags) values (%s, %s, %s, 0, %s, 0, %s, %s, %s, %s, %s, %s);"
|
||||
try:
|
||||
try:
|
||||
cover_image = book[2].data
|
||||
@@ -61,11 +61,27 @@ class Storage:
|
||||
cover_image = book[2]
|
||||
if not book[2]: # If cover image is missing unset entry
|
||||
cover_image = None
|
||||
self.cursor.execute(q, (book[0], book[1], cover_image, book[3]))
|
||||
self.cursor.execute(
|
||||
q,
|
||||
(
|
||||
book[0], # title
|
||||
book[1], # author
|
||||
cover_image,
|
||||
book[3], # file
|
||||
book[4], # descr
|
||||
book[5], # ident
|
||||
book[6], # publisher
|
||||
datetime.datetime.now(),
|
||||
book[8], # rights
|
||||
book[9], # tags
|
||||
),
|
||||
)
|
||||
return True
|
||||
except Exception as e:
|
||||
print(e)
|
||||
return False
|
||||
if e.pgcode == '22007': # psycopg2's error code for invalid date
|
||||
book[7] = psycopg2.Date(int(book[7]), 1, 1)
|
||||
self.insert_book(book)
|
||||
raise e
|
||||
|
||||
def book_paths_list(self):
|
||||
"""
|
||||
@@ -106,8 +122,11 @@ class Storage:
|
||||
path = self.config.book_path + "/"
|
||||
_collections = []
|
||||
_pathing = book[1].split(path)[1].split("/")
|
||||
_pathing.pop(0)
|
||||
_pathing.pop(-1)
|
||||
try:
|
||||
_pathing.pop(0)
|
||||
_pathing.pop(-1)
|
||||
except IndexError:
|
||||
continue
|
||||
for _p in _pathing:
|
||||
_s = _p.replace("'", "")
|
||||
_x = re.sub(_title_regx, "", _s)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
@@ -16,7 +16,7 @@ def MakeCollections(root):
|
||||
config = Config(root) # Get configuration settings
|
||||
# InitFiles(config.file_array) # Initialize file system
|
||||
_storage = Storage(config)
|
||||
_storage.make_collections()
|
||||
_storage.make_collections()
|
||||
_t2 = time.time()
|
||||
scan_time = round(_t2 - _t1)
|
||||
print("Collections Made.")
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
#!/usr/bin/python
|
||||
#!/usr/bin/env python
|
||||
import os
|
||||
import sys
|
||||
import time
|
||||
|
||||
@@ -51,8 +51,9 @@ INSTALLED_APPS = [
|
||||
"interface",
|
||||
"interface.templatetags",
|
||||
"debug_toolbar",
|
||||
"widget_tweaks"
|
||||
]
|
||||
|
||||
AUTH_USER_MODEL = "interface.User"
|
||||
MIDDLEWARE = [
|
||||
"django.middleware.security.SecurityMiddleware",
|
||||
"django.contrib.sessions.middleware.SessionMiddleware",
|
||||
@@ -103,6 +104,10 @@ DATABASES = {
|
||||
"PORT": CONFIG.db_port,
|
||||
}
|
||||
}
|
||||
# Session
|
||||
# Uncomment below to enable sessions management by a memcache server
|
||||
# https://docs.djangoproject.com/en/3.0/topics/http/sessions/
|
||||
# SESSION_ENGINE = "django.contrib.sessions.backends.cached_db"
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/2.2/ref/settings/#auth-password-validators
|
||||
@@ -133,6 +138,6 @@ USE_TZ = True
|
||||
|
||||
# Static files (CSS, JavaScript, Images)
|
||||
# https://docs.djangoproject.com/en/2.2/howto/static-files/
|
||||
|
||||
LOGIN_REDIRECT_URL = 'home'
|
||||
STATIC_URL = "/static/"
|
||||
STATIC_ROOT = os.path.join(BASE_DIR, "interface/static/")
|
||||
|
||||
@@ -16,21 +16,56 @@ Including another URLconf
|
||||
from django.conf import settings
|
||||
from django.contrib import admin
|
||||
from django.urls import include, path, re_path
|
||||
from django.contrib.auth import views as auth_views
|
||||
from django.contrib.auth.models import User
|
||||
from django.shortcuts import HttpResponse
|
||||
from interface import views
|
||||
|
||||
urlpatterns = [
|
||||
path("admin/", admin.site.urls),
|
||||
path("", views.index, name="index"),
|
||||
path("home", views.home, name="home"),
|
||||
re_path("^live", views.live, name="liverequest"),
|
||||
path("sort/<_order>", views.index, name="index"),
|
||||
path("flip_sort/<_order>", views.flip_sort, name="index"),
|
||||
path("download/<pk>", views.download, name="download"),
|
||||
path("share/<pk>", views.share, name="share"),
|
||||
path("share/<pk>", views.info, name="info"),
|
||||
path("prev_page/<bookset>", views.prev_page, name="prev_page"),
|
||||
path("next_page/<bookset>", views.next_page, name="next_page"),
|
||||
path("search/", views.search, name="search"),
|
||||
path("search/<query>", views.search, name="search"),
|
||||
path("search/<query>/<_set>", views.search, name="search"),
|
||||
path("prev_page/<bookset>/<_order>", views.prev_page, name="prev_page"),
|
||||
path("next_page/<bookset>/<_order>", views.next_page, name="next_page"),
|
||||
path("search/", views.index, name="search"),
|
||||
path("search/<query>", views.index, name="search"),
|
||||
path("search/<query>/<_set>", views.index, name="search"),
|
||||
path("collections", views.collectionspage, name="collections"),
|
||||
path("show_collection/<query>/<_set>", views.show_collection, name="show_collection"),
|
||||
path("signup", views.signup, name="signup"),
|
||||
path("login", views.userlogin, name="login"),
|
||||
path('logout', views.userlogout, name='logout'),
|
||||
path('favorite/<pk>', views.favorite, name='favorite'),
|
||||
path('favorites', views.favorites, name='favorites'),
|
||||
path('favorites/<bookset>', views.favorites, name='favorites'),
|
||||
path('favorites/<bookset>/<query>', views.favorites, name='favorites'),
|
||||
path(
|
||||
"show_collection/<_collection>/<_colset>",
|
||||
views.show_collection,
|
||||
name="show_collection",
|
||||
'admin/password_reset/',
|
||||
auth_views.PasswordResetView.as_view(),
|
||||
name='admin_password_reset',
|
||||
),
|
||||
path(
|
||||
'admin/password_reset/done/',
|
||||
auth_views.PasswordResetDoneView.as_view(),
|
||||
name='password_reset_done',
|
||||
),
|
||||
path(
|
||||
'reset/<uidb64>/<token>/',
|
||||
auth_views.PasswordResetConfirmView.as_view(),
|
||||
name='password_reset_confirm',
|
||||
),
|
||||
path(
|
||||
'reset/done/',
|
||||
auth_views.PasswordResetCompleteView.as_view(),
|
||||
name='password_reset_complete',
|
||||
),
|
||||
]
|
||||
if settings.DEBUG:
|
||||
|
||||
@@ -1,6 +1,30 @@
|
||||
from django.contrib import admin
|
||||
from django.contrib.auth.admin import UserAdmin
|
||||
|
||||
from .models import Books, Collections, Favorites, Navigation, User
|
||||
from .forms import CustomUserCreationForm, CustomUserChangeForm
|
||||
|
||||
|
||||
class CustomUserAdmin(UserAdmin):
|
||||
model = User
|
||||
add_form = CustomUserCreationForm
|
||||
form = CustomUserChangeForm
|
||||
list_display = ["email", "username", "facebook", "twitter", "sponsorid", "matrixid"]
|
||||
fieldsets = UserAdmin.fieldsets + (
|
||||
(None, {"fields": ()}),
|
||||
("Personal info", {"fields": ("facebook", "twitter", "matrixid")}),
|
||||
("Permissions", {"fields": ("sponsorid",)}),
|
||||
)
|
||||
# add_fieldsets is not a standard ModelAdmin attribute. UserAdmin
|
||||
# overrides get_fieldsets to use this attribute when creating a user.
|
||||
add_fieldsets = UserAdmin.add_fieldsets + (
|
||||
(None, {"classes": ("wide",), "fields": ("facebook", "twitter", "sponsorid", "matrixid")},),
|
||||
)
|
||||
|
||||
from .models import Books
|
||||
|
||||
# Register your models here.
|
||||
admin.site.register(Books)
|
||||
admin.site.register(Collections)
|
||||
admin.site.register(Favorites)
|
||||
admin.site.register(Navigation)
|
||||
admin.site.register(User, CustomUserAdmin)
|
||||
|
||||
64
src/interface/forms.py
Normal file
@@ -0,0 +1,64 @@
|
||||
from django import forms
|
||||
from django.contrib.auth.forms import UserCreationForm, UserChangeForm, AuthenticationForm
|
||||
from .models import User
|
||||
|
||||
|
||||
class CustomUserCreationForm(UserCreationForm):
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ("username", "email", "facebook", "twitter", "sponsorid", "matrixid")
|
||||
|
||||
|
||||
class CustomUserChangeForm(UserChangeForm):
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ("username", "email", "facebook", "twitter", "sponsorid", "matrixid")
|
||||
|
||||
|
||||
class CustomUserLoginForm(AuthenticationForm):
|
||||
class Meta:
|
||||
Model = User
|
||||
fields = ("username", "password")
|
||||
|
||||
|
||||
class SignUpForm(CustomUserCreationForm):
|
||||
def __init__(self, *args, **kwargs):
|
||||
super(CustomUserCreationForm, self).__init__(*args, **kwargs)
|
||||
for fieldname in ['password1']:
|
||||
self.fields[fieldname].help_text = 'At least 8 chars.'
|
||||
self.fields[fieldname].initial = 'Password'
|
||||
for fieldname in ['password2']:
|
||||
self.fields[fieldname].help_text = ''
|
||||
self.fields[fieldname].initial = 'Confirm Pass'
|
||||
|
||||
username = forms.CharField(
|
||||
max_length=30,
|
||||
required=True,
|
||||
help_text='Required',
|
||||
initial="",
|
||||
label="Username",
|
||||
|
||||
)
|
||||
email = forms.EmailField(
|
||||
max_length=254,
|
||||
help_text='Required',
|
||||
initial="",
|
||||
label="Email"
|
||||
)
|
||||
matrixid = forms.CharField(
|
||||
max_length=30,
|
||||
required=False,
|
||||
help_text='Optional',
|
||||
initial="",
|
||||
label="Matrix Id"
|
||||
)
|
||||
password1 = forms.PasswordInput()
|
||||
class Meta:
|
||||
model = User
|
||||
fields = ("username", "email", "matrixid")
|
||||
|
||||
|
||||
class UserLoginForm(CustomUserLoginForm):
|
||||
|
||||
class Meta:
|
||||
model = User
|
||||
116
src/interface/migrations/0001_initial.py
Executable file → Normal file
@@ -1,35 +1,111 @@
|
||||
# Generated by Django 2.2.7 on 2019-11-28 19:24
|
||||
# Generated by Django 3.0.7 on 2020-07-23 16:01
|
||||
|
||||
from django.conf import settings
|
||||
import django.contrib.auth.models
|
||||
import django.contrib.auth.validators
|
||||
from django.db import migrations, models
|
||||
import django.db.models.deletion
|
||||
import django.utils.timezone
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
initial = True
|
||||
|
||||
dependencies = []
|
||||
dependencies = [
|
||||
('auth', '0011_update_proxy_permissions'),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="Books",
|
||||
name='User',
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("title", models.CharField(max_length=255)),
|
||||
("author", models.CharField(max_length=255, null=True)),
|
||||
("categories", models.CharField(max_length=255, null=True)),
|
||||
("cover", models.BinaryField(editable=True, null=True)),
|
||||
("pages", models.IntegerField(null=True)),
|
||||
("progress", models.IntegerField(null=True)),
|
||||
("file_name", models.CharField(max_length=255)),
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('password', models.CharField(max_length=128, verbose_name='password')),
|
||||
('last_login', models.DateTimeField(blank=True, null=True, verbose_name='last login')),
|
||||
('is_superuser', models.BooleanField(default=False, help_text='Designates that this user has all permissions without explicitly assigning them.', verbose_name='superuser status')),
|
||||
('username', models.CharField(error_messages={'unique': 'A user with that username already exists.'}, help_text='Required. 150 characters or fewer. Letters, digits and @/./+/-/_ only.', max_length=150, unique=True, validators=[django.contrib.auth.validators.UnicodeUsernameValidator()], verbose_name='username')),
|
||||
('first_name', models.CharField(blank=True, max_length=30, verbose_name='first name')),
|
||||
('last_name', models.CharField(blank=True, max_length=150, verbose_name='last name')),
|
||||
('email', models.EmailField(blank=True, max_length=254, verbose_name='email address')),
|
||||
('is_staff', models.BooleanField(default=False, help_text='Designates whether the user can log into this admin site.', verbose_name='staff status')),
|
||||
('is_active', models.BooleanField(default=True, help_text='Designates whether this user should be treated as active. Unselect this instead of deleting accounts.', verbose_name='active')),
|
||||
('date_joined', models.DateTimeField(default=django.utils.timezone.now, verbose_name='date joined')),
|
||||
('facebook', models.CharField(max_length=255, null=True)),
|
||||
('twitter', models.CharField(max_length=255, null=True)),
|
||||
('ulvl', models.IntegerField(default=1)),
|
||||
('sponsorid', models.IntegerField(null=True)),
|
||||
('matrixid', models.CharField(max_length=255, null=True)),
|
||||
('groups', models.ManyToManyField(blank=True, help_text='The groups this user belongs to. A user will get all permissions granted to each of their groups.', related_name='user_set', related_query_name='user', to='auth.Group', verbose_name='groups')),
|
||||
('user_permissions', models.ManyToManyField(blank=True, help_text='Specific permissions for this user.', related_name='user_set', related_query_name='user', to='auth.Permission', verbose_name='user permissions')),
|
||||
],
|
||||
options={"db_table": "books",},
|
||||
options={
|
||||
'verbose_name': 'user',
|
||||
'verbose_name_plural': 'users',
|
||||
'abstract': False,
|
||||
},
|
||||
managers=[
|
||||
('objects', django.contrib.auth.models.UserManager()),
|
||||
],
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Books',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('title', models.TextField()),
|
||||
('author', models.CharField(max_length=255, null=True)),
|
||||
('categories', models.TextField(null=True)),
|
||||
('cover', models.BinaryField(editable=True, null=True)),
|
||||
('pages', models.IntegerField(null=True)),
|
||||
('progress', models.IntegerField(null=True)),
|
||||
('file_name', models.TextField()),
|
||||
('description', models.TextField(null=True)),
|
||||
('identifier', models.CharField(max_length=255, null=True)),
|
||||
('publisher', models.TextField(null=True)),
|
||||
('date', models.DateField(null=True)),
|
||||
('rights', models.CharField(max_length=255, null=True)),
|
||||
('tags', models.TextField(null=True)),
|
||||
],
|
||||
options={
|
||||
'db_table': 'books',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Navigation',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('title', models.CharField(max_length=255)),
|
||||
('link', models.CharField(max_length=255, null=True)),
|
||||
('category', models.CharField(max_length=255, null=True)),
|
||||
('parent_id', models.IntegerField(null=True)),
|
||||
('alt', models.CharField(max_length=255, null=True)),
|
||||
('type', models.IntegerField(null=True)),
|
||||
('socket', models.CharField(max_length=255)),
|
||||
],
|
||||
options={
|
||||
'db_table': 'navigation',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Favorites',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('book', models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to='interface.Books')),
|
||||
('user', models.ForeignKey(default=None, on_delete=django.db.models.deletion.CASCADE, to=settings.AUTH_USER_MODEL)),
|
||||
],
|
||||
options={
|
||||
'db_table': 'favorites',
|
||||
},
|
||||
),
|
||||
migrations.CreateModel(
|
||||
name='Collections',
|
||||
fields=[
|
||||
('id', models.AutoField(auto_created=True, primary_key=True, serialize=False, verbose_name='ID')),
|
||||
('collection', models.CharField(max_length=255)),
|
||||
('book_id', models.ForeignKey(on_delete=django.db.models.deletion.PROTECT, to='interface.Books')),
|
||||
],
|
||||
options={
|
||||
'db_table': 'collections',
|
||||
},
|
||||
),
|
||||
]
|
||||
|
||||
@@ -1,14 +0,0 @@
|
||||
# Generated by Django 2.2.7 on 2020-01-01 04:45
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("interface", "0001_initial"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(name="books", options={"managed": False},),
|
||||
]
|
||||
@@ -1,14 +0,0 @@
|
||||
# Generated by Django 2.2.7 on 2020-01-01 04:47
|
||||
|
||||
from django.db import migrations
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("interface", "0002_auto_20200101_0445"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.AlterModelOptions(name="books", options={},),
|
||||
]
|
||||
@@ -1,37 +0,0 @@
|
||||
# Generated by Django 3.0.2 on 2020-02-04 20:22
|
||||
|
||||
import django.db.models.deletion
|
||||
from django.db import migrations, models
|
||||
|
||||
|
||||
class Migration(migrations.Migration):
|
||||
|
||||
dependencies = [
|
||||
("interface", "0003_auto_20200101_0447"),
|
||||
]
|
||||
|
||||
operations = [
|
||||
migrations.CreateModel(
|
||||
name="Collections",
|
||||
fields=[
|
||||
(
|
||||
"id",
|
||||
models.AutoField(
|
||||
auto_created=True,
|
||||
primary_key=True,
|
||||
serialize=False,
|
||||
verbose_name="ID",
|
||||
),
|
||||
),
|
||||
("collection", models.CharField(max_length=255)),
|
||||
(
|
||||
"book_id",
|
||||
models.ForeignKey(
|
||||
on_delete=django.db.models.deletion.PROTECT,
|
||||
to="interface.Books",
|
||||
),
|
||||
),
|
||||
],
|
||||
options={"db_table": "collections",},
|
||||
),
|
||||
]
|
||||
0
src/interface/migrations/__init__.py
Executable file → Normal file
@@ -1,5 +1,8 @@
|
||||
from django.contrib.postgres.search import SearchVector
|
||||
from django.db import models
|
||||
from django.conf import settings
|
||||
from django.contrib.auth.models import AbstractUser, User
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
# Create your models here.
|
||||
|
||||
@@ -22,30 +25,43 @@ class Books(models.Model):
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
title = models.CharField(max_length=255)
|
||||
title = models.TextField(max_length=None)
|
||||
author = models.CharField(max_length=255, null=True)
|
||||
categories = models.CharField(max_length=255, null=True)
|
||||
categories = models.TextField(max_length=None, null=True)
|
||||
cover = models.BinaryField(null=True, editable=True)
|
||||
pages = models.IntegerField(null=True)
|
||||
progress = models.IntegerField(null=True)
|
||||
file_name = models.CharField(max_length=255, null=False)
|
||||
file_name = models.TextField(max_length=None, null=False)
|
||||
date = models.DateTimeField(auto_now_add=True)
|
||||
description = models.TextField(null=True)
|
||||
identifier = models.CharField(max_length=255, null=True)
|
||||
publisher = models.TextField(max_length=None, null=True)
|
||||
date = models.DateField(null=True)
|
||||
rights = models.CharField(max_length=255, null=True)
|
||||
tags = models.TextField(max_length=None, null=True)
|
||||
|
||||
def generic_search(self, query):
|
||||
try:
|
||||
results = Books.objects.annotate(
|
||||
search=SearchVector("title", "file_name", "author"),
|
||||
search=SearchVector("title", "file_name", "author", "tags"),
|
||||
).filter(search=query)
|
||||
except Exception as e:
|
||||
raise
|
||||
return results
|
||||
|
||||
def search_by_collection(self, query):
|
||||
try:
|
||||
return Books.objects.filter(categories=query)
|
||||
except Exception as e:
|
||||
raise
|
||||
|
||||
|
||||
class Collections(models.Model):
|
||||
class Meta:
|
||||
db_table = "collections"
|
||||
|
||||
def __str__(self):
|
||||
return self.collection
|
||||
return self.collection.__str__()
|
||||
|
||||
collection = models.CharField(max_length=255)
|
||||
book_id = models.ForeignKey(Books, on_delete=models.PROTECT)
|
||||
@@ -55,10 +71,109 @@ class Collections(models.Model):
|
||||
return reverse("model-detail-view", args=[str(self.id)])
|
||||
|
||||
def generic_search(self, query):
|
||||
books =[]
|
||||
try:
|
||||
results = Books.objects.annotate(search=SearchVector("collection"),).filter(
|
||||
#results = Collections.objects.all().filter(
|
||||
# collection=query
|
||||
#)
|
||||
results = Collections.objects.prefetch_related('book_id').annotate(search=SearchVector("collection"), ).filter(
|
||||
search=query
|
||||
)
|
||||
except Exception as e:
|
||||
raise
|
||||
_results = results.values('book_id')
|
||||
for r in results:
|
||||
books.append(
|
||||
{
|
||||
"pk": r.book_id.id,
|
||||
"title": r.book_id.title,
|
||||
"author": r.book_id.author,
|
||||
"categories": r.book_id.categories,
|
||||
"cover": r.book_id.cover,
|
||||
"pages": r.book_id.pages,
|
||||
"progress": r.book_id.progress,
|
||||
"file_name": r.book_id.file_name,
|
||||
"date": r.book_id.date,
|
||||
"description": r.book_id.description,
|
||||
"identifier": r.book_id.identifier,
|
||||
"publisher": r.book_id.publisher,
|
||||
"date": r.book_id.date,
|
||||
"rights": r.book_id.rights,
|
||||
"tags": r.book_id.tags
|
||||
}
|
||||
)
|
||||
return books
|
||||
|
||||
|
||||
class Navigation(models.Model):
|
||||
"""
|
||||
pyShelfs Navigation Database class
|
||||
:param title: Link Text
|
||||
:param link: Link link :)
|
||||
:param category: Where in the nav tree do I belong
|
||||
:param parent_id: This link is a sub link of link with id of me
|
||||
:param alt: Alternate text of link
|
||||
:param type: Web link, or Socket link which will be expected to act on \
|
||||
the link, and the action defined in socket
|
||||
:param socket: if a Socket link define socket here
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
db_table = "navigation"
|
||||
|
||||
def __str__(self):
|
||||
return self.title
|
||||
|
||||
title = models.CharField(max_length=255)
|
||||
link = models.CharField(max_length=255, null=True)
|
||||
category = models.CharField(max_length=255, null=True)
|
||||
parent_id = models.IntegerField(null=True, editable=True)
|
||||
alt = models.CharField(max_length=255, null=True)
|
||||
type = models.IntegerField(null=True)
|
||||
socket = models.CharField(max_length=255, null=False)
|
||||
|
||||
def generic_search(self, query):
|
||||
try:
|
||||
results = Navigation.objects.annotate(
|
||||
search=SearchVector("title", "parent_id", "category"),
|
||||
).filter(search=query)
|
||||
except Exception as e:
|
||||
raise
|
||||
return results
|
||||
|
||||
|
||||
class User(AbstractUser):
|
||||
facebook = models.CharField(max_length=255, null=True)
|
||||
twitter = models.CharField(max_length=255, null=True)
|
||||
ulvl = models.IntegerField(default=1)
|
||||
sponsorid = models.IntegerField(null=True)
|
||||
matrixid = models.CharField(max_length=255, null=True)
|
||||
|
||||
def __str__(self):
|
||||
return self.username
|
||||
|
||||
|
||||
class Favorites(models.Model):
|
||||
"""
|
||||
Favorites Database class
|
||||
:param book: book foreign key
|
||||
:param user: user foreign key
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
db_table = "favorites"
|
||||
|
||||
def __str__(self):
|
||||
return str(self.book)
|
||||
|
||||
book = models.ForeignKey(Books, on_delete=models.CASCADE, default=None)
|
||||
user = models.ForeignKey(User, on_delete=models.CASCADE, default=None)
|
||||
|
||||
def generic_search(self, query):
|
||||
try:
|
||||
results = Favorites.objects.annotate(search=SearchVector("user"),).filter(search=query)
|
||||
except Exception as e:
|
||||
raise
|
||||
return results
|
||||
|
||||
|
||||
|
||||
0
src/interface/static/admin/css/autocomplete.css
vendored
Executable file → Normal file
0
src/interface/static/admin/css/base.css
vendored
Executable file → Normal file
0
src/interface/static/admin/css/changelists.css
vendored
Executable file → Normal file
0
src/interface/static/admin/css/dashboard.css
vendored
Executable file → Normal file
0
src/interface/static/admin/css/fonts.css
vendored
Executable file → Normal file
0
src/interface/static/admin/css/forms.css
vendored
Executable file → Normal file
0
src/interface/static/admin/css/login.css
vendored
Executable file → Normal file
0
src/interface/static/admin/css/responsive.css
vendored
Executable file → Normal file
0
src/interface/static/admin/css/responsive_rtl.css
vendored
Executable file → Normal file
0
src/interface/static/admin/css/rtl.css
vendored
Executable file → Normal file
0
src/interface/static/admin/css/vendor/select2/LICENSE-SELECT2.md
vendored
Executable file → Normal file
0
src/interface/static/admin/css/vendor/select2/select2.css
vendored
Executable file → Normal file
0
src/interface/static/admin/css/vendor/select2/select2.min.css
vendored
Executable file → Normal file
0
src/interface/static/admin/css/widgets.css
vendored
Executable file → Normal file
0
src/interface/static/admin/fonts/LICENSE.txt
vendored
Executable file → Normal file
0
src/interface/static/admin/fonts/README.txt
vendored
Executable file → Normal file
0
src/interface/static/admin/fonts/Roboto-Bold-webfont.woff
vendored
Executable file → Normal file
0
src/interface/static/admin/fonts/Roboto-Light-webfont.woff
vendored
Executable file → Normal file
0
src/interface/static/admin/fonts/Roboto-Regular-webfont.woff
vendored
Executable file → Normal file
0
src/interface/static/admin/img/LICENSE
vendored
Executable file → Normal file
0
src/interface/static/admin/img/README.txt
vendored
Executable file → Normal file
0
src/interface/static/admin/img/calendar-icons.svg
vendored
Executable file → Normal file
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
2
src/interface/static/admin/img/gis/move_vertex_off.svg
vendored
Executable file → Normal file
@@ -1 +1 @@
|
||||
<svg width="24" height="22" viewBox="0 0 847 779" xmlns="http://www.w3.org/2000/svg"><g><path fill="#EBECE6" d="M120 1h607c66 0 120 54 120 120v536c0 66-54 120-120 120h-607c-66 0-120-54-120-120v-536c0-66 54-120 120-120z"/><path fill="#9E9E93" d="M120 1h607c66 0 120 54 120 120v536c0 66-54 120-120 120h-607c-66 0-120-54-120-120v-536c0-66 54-120 120-120zm607 25h-607c-26 0-50 11-67 28-17 18-28 41-28 67v536c0 27 11 50 28 68 17 17 41 27 67 27h607c26 0 49-10 67-27 17-18 28-41 28-68v-536c0-26-11-49-28-67-18-17-41-28-67-28z"/><path stroke="#A9A8A4" stroke-width="20" d="M706 295l-68 281"/><path stroke="#E47474" stroke-width="20" d="M316 648l390-353M141 435l175 213"/><path stroke="#C9C9C9" stroke-width="20" d="M319 151l-178 284M706 295l-387-144"/><g fill="#040405"><path d="M319 111c22 0 40 18 40 40s-18 40-40 40-40-18-40-40 18-40 40-40zM141 395c22 0 40 18 40 40s-18 40-40 40c-23 0-41-18-41-40s18-40 41-40zM316 608c22 0 40 18 40 40 0 23-18 41-40 41s-40-18-40-41c0-22 18-40 40-40zM706 254c22 0 40 18 40 41 0 22-18 40-40 40s-40-18-40-40c0-23 18-41 40-41zM638 536c22 0 40 18 40 40s-18 40-40 40-40-18-40-40 18-40 40-40z"/></g></g></svg>
|
||||
<svg width="24" height="22" viewBox="0 0 847 779" xmlns="http://www.w3.org/2000/svg"><g><path fill="#EBECE6" d="M120 1h607c66 0 120 54 120 120v536c0 66-54 120-120 120h-607c-66 0-120-54-120-120v-536c0-66 54-120 120-120z"/><path fill="#9E9E93" d="M120 1h607c66 0 120 54 120 120v536c0 66-54 120-120 120h-607c-66 0-120-54-120-120v-536c0-66 54-120 120-120zm607 25h-607c-26 0-50 11-67 28-17 18-28 41-28 67v536c0 27 11 50 28 68 17 17 41 27 67 27h607c26 0 49-10 67-27 17-18 28-41 28-68v-536c0-26-11-49-28-67-18-17-41-28-67-28z"/><path stroke="#A9A8A4" stroke-width="20" d="M706 295l-68 281"/><path stroke="#E47474" stroke-width="20" d="M316 648l390-353M141 435l175 213"/><path stroke="#C9C9C9" stroke-width="20" d="M319 151l-178 284M706 295l-387-144"/><g fill="#040405"><path d="M319 111c22 0 40 18 40 40s-18 40-40 40-40-18-40-40 18-40 40-40zM141 395c22 0 40 18 40 40s-18 40-40 40c-23 0-41-18-41-40s18-40 41-40zM316 608c22 0 40 18 40 40 0 23-18 41-40 41s-40-18-40-41c0-22 18-40 40-40zM706 254c22 0 40 18 40 41 0 22-18 40-40 40s-40-18-40-40c0-23 18-41 40-41zM638 536c22 0 40 18 40 40s-18 40-40 40-40-18-40-40 18-40 40-40z"/></g></g></svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
2
src/interface/static/admin/img/gis/move_vertex_on.svg
vendored
Executable file → Normal file
@@ -1 +1 @@
|
||||
<svg width="24" height="22" viewBox="0 0 847 779" xmlns="http://www.w3.org/2000/svg"><g><path fill="#F1C02A" d="M120 1h607c66 0 120 54 120 120v536c0 66-54 120-120 120h-607c-66 0-120-54-120-120v-536c0-66 54-120 120-120z"/><path fill="#9E9E93" d="M120 1h607c66 0 120 54 120 120v536c0 66-54 120-120 120h-607c-66 0-120-54-120-120v-536c0-66 54-120 120-120zm607 25h-607c-26 0-50 11-67 28-17 18-28 41-28 67v536c0 27 11 50 28 68 17 17 41 27 67 27h607c26 0 49-10 67-27 17-18 28-41 28-68v-536c0-26-11-49-28-67-18-17-41-28-67-28z"/><path stroke="#A9A8A4" stroke-width="20" d="M706 295l-68 281"/><path stroke="#E47474" stroke-width="20" d="M316 648l390-353M141 435l175 213"/><path stroke="#C9A741" stroke-width="20" d="M319 151l-178 284M706 295l-387-144"/><g fill="#040405"><path d="M319 111c22 0 40 18 40 40s-18 40-40 40-40-18-40-40 18-40 40-40zM141 395c22 0 40 18 40 40s-18 40-40 40c-23 0-41-18-41-40s18-40 41-40zM316 608c22 0 40 18 40 40 0 23-18 41-40 41s-40-18-40-41c0-22 18-40 40-40zM706 254c22 0 40 18 40 41 0 22-18 40-40 40s-40-18-40-40c0-23 18-41 40-41zM638 536c22 0 40 18 40 40s-18 40-40 40-40-18-40-40 18-40 40-40z"/></g></g></svg>
|
||||
<svg width="24" height="22" viewBox="0 0 847 779" xmlns="http://www.w3.org/2000/svg"><g><path fill="#F1C02A" d="M120 1h607c66 0 120 54 120 120v536c0 66-54 120-120 120h-607c-66 0-120-54-120-120v-536c0-66 54-120 120-120z"/><path fill="#9E9E93" d="M120 1h607c66 0 120 54 120 120v536c0 66-54 120-120 120h-607c-66 0-120-54-120-120v-536c0-66 54-120 120-120zm607 25h-607c-26 0-50 11-67 28-17 18-28 41-28 67v536c0 27 11 50 28 68 17 17 41 27 67 27h607c26 0 49-10 67-27 17-18 28-41 28-68v-536c0-26-11-49-28-67-18-17-41-28-67-28z"/><path stroke="#A9A8A4" stroke-width="20" d="M706 295l-68 281"/><path stroke="#E47474" stroke-width="20" d="M316 648l390-353M141 435l175 213"/><path stroke="#C9A741" stroke-width="20" d="M319 151l-178 284M706 295l-387-144"/><g fill="#040405"><path d="M319 111c22 0 40 18 40 40s-18 40-40 40-40-18-40-40 18-40 40-40zM141 395c22 0 40 18 40 40s-18 40-40 40c-23 0-41-18-41-40s18-40 41-40zM316 608c22 0 40 18 40 40 0 23-18 41-40 41s-40-18-40-41c0-22 18-40 40-40zM706 254c22 0 40 18 40 41 0 22-18 40-40 40s-40-18-40-40c0-23 18-41 40-41zM638 536c22 0 40 18 40 40s-18 40-40 40-40-18-40-40 18-40 40-40z"/></g></g></svg>
|
||||
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
0
src/interface/static/admin/img/icon-addlink.svg
vendored
Executable file → Normal file
|
Before Width: | Height: | Size: 331 B After Width: | Height: | Size: 331 B |
0
src/interface/static/admin/img/icon-alert.svg
vendored
Executable file → Normal file
|
Before Width: | Height: | Size: 504 B After Width: | Height: | Size: 504 B |
0
src/interface/static/admin/img/icon-calendar.svg
vendored
Executable file → Normal file
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
0
src/interface/static/admin/img/icon-changelink.svg
vendored
Executable file → Normal file
|
Before Width: | Height: | Size: 380 B After Width: | Height: | Size: 380 B |
0
src/interface/static/admin/img/icon-clock.svg
vendored
Executable file → Normal file
|
Before Width: | Height: | Size: 677 B After Width: | Height: | Size: 677 B |
0
src/interface/static/admin/img/icon-deletelink.svg
vendored
Executable file → Normal file
|
Before Width: | Height: | Size: 392 B After Width: | Height: | Size: 392 B |
0
src/interface/static/admin/img/icon-no.svg
vendored
Executable file → Normal file
|
Before Width: | Height: | Size: 560 B After Width: | Height: | Size: 560 B |
0
src/interface/static/admin/img/icon-unknown-alt.svg
vendored
Executable file → Normal file
|
Before Width: | Height: | Size: 655 B After Width: | Height: | Size: 655 B |
0
src/interface/static/admin/img/icon-unknown.svg
vendored
Executable file → Normal file
|
Before Width: | Height: | Size: 655 B After Width: | Height: | Size: 655 B |
0
src/interface/static/admin/img/icon-viewlink.svg
vendored
Executable file → Normal file
|
Before Width: | Height: | Size: 581 B After Width: | Height: | Size: 581 B |
0
src/interface/static/admin/img/icon-yes.svg
vendored
Executable file → Normal file
|
Before Width: | Height: | Size: 436 B After Width: | Height: | Size: 436 B |
0
src/interface/static/admin/img/inline-delete.svg
vendored
Executable file → Normal file
|
Before Width: | Height: | Size: 560 B After Width: | Height: | Size: 560 B |
0
src/interface/static/admin/img/search.svg
vendored
Executable file → Normal file
|
Before Width: | Height: | Size: 458 B After Width: | Height: | Size: 458 B |
0
src/interface/static/admin/img/selector-icons.svg
vendored
Executable file → Normal file
|
Before Width: | Height: | Size: 3.2 KiB After Width: | Height: | Size: 3.2 KiB |
0
src/interface/static/admin/img/sorting-icons.svg
vendored
Executable file → Normal file
|
Before Width: | Height: | Size: 1.1 KiB After Width: | Height: | Size: 1.1 KiB |
0
src/interface/static/admin/img/tooltag-add.svg
vendored
Executable file → Normal file
|
Before Width: | Height: | Size: 331 B After Width: | Height: | Size: 331 B |
0
src/interface/static/admin/img/tooltag-arrowright.svg
vendored
Executable file → Normal file
|
Before Width: | Height: | Size: 280 B After Width: | Height: | Size: 280 B |
0
src/interface/static/admin/js/SelectBox.js
vendored
Executable file → Normal file
0
src/interface/static/admin/js/SelectFilter2.js
vendored
Executable file → Normal file
0
src/interface/static/admin/js/actions.js
vendored
Executable file → Normal file
0
src/interface/static/admin/js/actions.min.js
vendored
Executable file → Normal file
0
src/interface/static/admin/js/admin/DateTimeShortcuts.js
vendored
Executable file → Normal file
0
src/interface/static/admin/js/admin/RelatedObjectLookups.js
vendored
Executable file → Normal file
0
src/interface/static/admin/js/autocomplete.js
vendored
Executable file → Normal file
0
src/interface/static/admin/js/calendar.js
vendored
Executable file → Normal file
0
src/interface/static/admin/js/cancel.js
vendored
Executable file → Normal file
0
src/interface/static/admin/js/change_form.js
vendored
Executable file → Normal file
0
src/interface/static/admin/js/collapse.js
vendored
Executable file → Normal file
0
src/interface/static/admin/js/collapse.min.js
vendored
Executable file → Normal file
0
src/interface/static/admin/js/core.js
vendored
Executable file → Normal file
0
src/interface/static/admin/js/inlines.js
vendored
Executable file → Normal file
0
src/interface/static/admin/js/inlines.min.js
vendored
Executable file → Normal file
0
src/interface/static/admin/js/jquery.init.js
vendored
Executable file → Normal file
0
src/interface/static/admin/js/popup_response.js
vendored
Executable file → Normal file
0
src/interface/static/admin/js/prepopulate.js
vendored
Executable file → Normal file
0
src/interface/static/admin/js/prepopulate.min.js
vendored
Executable file → Normal file
0
src/interface/static/admin/js/prepopulate_init.js
vendored
Executable file → Normal file
0
src/interface/static/admin/js/urlify.js
vendored
Executable file → Normal file
0
src/interface/static/admin/js/vendor/jquery/LICENSE.txt
vendored
Executable file → Normal file
1238
src/interface/static/admin/js/vendor/jquery/jquery.js
vendored
Executable file → Normal file
4
src/interface/static/admin/js/vendor/jquery/jquery.min.js
vendored
Executable file → Normal file
0
src/interface/static/admin/js/vendor/select2/LICENSE.md
vendored
Executable file → Normal file
2
src/interface/static/admin/js/vendor/select2/i18n/af.js
vendored
Executable file → Normal file
@@ -1,3 +1,3 @@
|
||||
/*! Select2 4.0.7 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||
|
||||
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/af",[],function(){return{errorLoading:function(){return"Die resultate kon nie gelaai word nie."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Verwyders asseblief "+t+" character";return t!=1&&(n+="s"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Voer asseblief "+t+" of meer karakters";return n},loadingMore:function(){return"Meer resultate word gelaai…"},maximumSelected:function(e){var t="Kies asseblief net "+e.maximum+" item";return e.maximum!=1&&(t+="s"),t},noResults:function(){return"Geen resultate gevind"},searching:function(){return"Besig…"},removeAllItems:function(){return"Verwyder alle items"}}}),{define:e.define,require:e.require}})();
|
||||
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/af",[],function(){return{errorLoading:function(){return"Die resultate kon nie gelaai word nie."},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Verwyders asseblief "+t+" character";return t!=1&&(n+="s"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Voer asseblief "+t+" of meer karakters";return n},loadingMore:function(){return"Meer resultate word gelaai…"},maximumSelected:function(e){var t="Kies asseblief net "+e.maximum+" item";return e.maximum!=1&&(t+="s"),t},noResults:function(){return"Geen resultate gevind"},searching:function(){return"Besig…"},removeAllItems:function(){return"Verwyder alle items"}}}),{define:e.define,require:e.require}})();
|
||||
2
src/interface/static/admin/js/vendor/select2/i18n/ar.js
vendored
Executable file → Normal file
@@ -1,3 +1,3 @@
|
||||
/*! Select2 4.0.7 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||
|
||||
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ar",[],function(){return{errorLoading:function(){return"لا يمكن تحميل النتائج"},inputTooLong:function(e){var t=e.input.length-e.maximum;return"الرجاء حذف "+t+" عناصر"},inputTooShort:function(e){var t=e.minimum-e.input.length;return"الرجاء إضافة "+t+" عناصر"},loadingMore:function(){return"جاري تحميل نتائج إضافية..."},maximumSelected:function(e){return"تستطيع إختيار "+e.maximum+" بنود فقط"},noResults:function(){return"لم يتم العثور على أي نتائج"},searching:function(){return"جاري البحث…"},removeAllItems:function(){return"قم بإزالة كل العناصر"}}}),{define:e.define,require:e.require}})();
|
||||
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ar",[],function(){return{errorLoading:function(){return"لا يمكن تحميل النتائج"},inputTooLong:function(e){var t=e.input.length-e.maximum;return"الرجاء حذف "+t+" عناصر"},inputTooShort:function(e){var t=e.minimum-e.input.length;return"الرجاء إضافة "+t+" عناصر"},loadingMore:function(){return"جاري تحميل نتائج إضافية..."},maximumSelected:function(e){return"تستطيع إختيار "+e.maximum+" بنود فقط"},noResults:function(){return"لم يتم العثور على أي نتائج"},searching:function(){return"جاري البحث…"},removeAllItems:function(){return"قم بإزالة كل العناصر"}}}),{define:e.define,require:e.require}})();
|
||||
2
src/interface/static/admin/js/vendor/select2/i18n/az.js
vendored
Executable file → Normal file
@@ -1,3 +1,3 @@
|
||||
/*! Select2 4.0.7 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||
|
||||
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/az",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum;return t+" simvol silin"},inputTooShort:function(e){var t=e.minimum-e.input.length;return t+" simvol daxil edin"},loadingMore:function(){return"Daha çox nəticə yüklənir…"},maximumSelected:function(e){return"Sadəcə "+e.maximum+" element seçə bilərsiniz"},noResults:function(){return"Nəticə tapılmadı"},searching:function(){return"Axtarılır…"},removeAllItems:function(){return"Bütün elementləri sil"}}}),{define:e.define,require:e.require}})();
|
||||
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/az",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum;return t+" simvol silin"},inputTooShort:function(e){var t=e.minimum-e.input.length;return t+" simvol daxil edin"},loadingMore:function(){return"Daha çox nəticə yüklənir…"},maximumSelected:function(e){return"Sadəcə "+e.maximum+" element seçə bilərsiniz"},noResults:function(){return"Nəticə tapılmadı"},searching:function(){return"Axtarılır…"},removeAllItems:function(){return"Bütün elementləri sil"}}}),{define:e.define,require:e.require}})();
|
||||
2
src/interface/static/admin/js/vendor/select2/i18n/bg.js
vendored
Executable file → Normal file
@@ -1,3 +1,3 @@
|
||||
/*! Select2 4.0.7 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||
|
||||
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/bg",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Моля въведете с "+t+" по-малко символ";return t>1&&(n+="a"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Моля въведете още "+t+" символ";return t>1&&(n+="a"),n},loadingMore:function(){return"Зареждат се още…"},maximumSelected:function(e){var t="Можете да направите до "+e.maximum+" ";return e.maximum>1?t+="избора":t+="избор",t},noResults:function(){return"Няма намерени съвпадения"},searching:function(){return"Търсене…"},removeAllItems:function(){return"Премахнете всички елементи"}}}),{define:e.define,require:e.require}})();
|
||||
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/bg",[],function(){return{inputTooLong:function(e){var t=e.input.length-e.maximum,n="Моля въведете с "+t+" по-малко символ";return t>1&&(n+="a"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Моля въведете още "+t+" символ";return t>1&&(n+="a"),n},loadingMore:function(){return"Зареждат се още…"},maximumSelected:function(e){var t="Можете да направите до "+e.maximum+" ";return e.maximum>1?t+="избора":t+="избор",t},noResults:function(){return"Няма намерени съвпадения"},searching:function(){return"Търсене…"},removeAllItems:function(){return"Премахнете всички елементи"}}}),{define:e.define,require:e.require}})();
|
||||
2
src/interface/static/admin/js/vendor/select2/i18n/bn.js
vendored
Executable file → Normal file
@@ -1,3 +1,3 @@
|
||||
/*! Select2 4.0.7 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||
|
||||
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/bn",[],function(){return{errorLoading:function(){return"ফলাফলগুলি লোড করা যায়নি।"},inputTooLong:function(e){var t=e.input.length-e.maximum,n="অনুগ্রহ করে "+t+" টি অক্ষর মুছে দিন।";return t!=1&&(n="অনুগ্রহ করে "+t+" টি অক্ষর মুছে দিন।"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n=t+" টি অক্ষর অথবা অধিক অক্ষর লিখুন।";return n},loadingMore:function(){return"আরো ফলাফল লোড হচ্ছে ..."},maximumSelected:function(e){var t=e.maximum+" টি আইটেম নির্বাচন করতে পারবেন।";return e.maximum!=1&&(t=e.maximum+" টি আইটেম নির্বাচন করতে পারবেন।"),t},noResults:function(){return"কোন ফলাফল পাওয়া যায়নি।"},searching:function(){return"অনুসন্ধান করা হচ্ছে ..."}}}),{define:e.define,require:e.require}})();
|
||||
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/bn",[],function(){return{errorLoading:function(){return"ফলাফলগুলি লোড করা যায়নি।"},inputTooLong:function(e){var t=e.input.length-e.maximum,n="অনুগ্রহ করে "+t+" টি অক্ষর মুছে দিন।";return t!=1&&(n="অনুগ্রহ করে "+t+" টি অক্ষর মুছে দিন।"),n},inputTooShort:function(e){var t=e.minimum-e.input.length,n=t+" টি অক্ষর অথবা অধিক অক্ষর লিখুন।";return n},loadingMore:function(){return"আরো ফলাফল লোড হচ্ছে ..."},maximumSelected:function(e){var t=e.maximum+" টি আইটেম নির্বাচন করতে পারবেন।";return e.maximum!=1&&(t=e.maximum+" টি আইটেম নির্বাচন করতে পারবেন।"),t},noResults:function(){return"কোন ফলাফল পাওয়া যায়নি।"},searching:function(){return"অনুসন্ধান করা হচ্ছে ..."}}}),{define:e.define,require:e.require}})();
|
||||
2
src/interface/static/admin/js/vendor/select2/i18n/bs.js
vendored
Executable file → Normal file
@@ -1,3 +1,3 @@
|
||||
/*! Select2 4.0.7 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||
|
||||
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/bs",[],function(){function e(e,t,n,r){return e%10==1&&e%100!=11?t:e%10>=2&&e%10<=4&&(e%100<12||e%100>14)?n:r}return{errorLoading:function(){return"Preuzimanje nije uspijelo."},inputTooLong:function(t){var n=t.input.length-t.maximum,r="Obrišite "+n+" simbol";return r+=e(n,"","a","a"),r},inputTooShort:function(t){var n=t.minimum-t.input.length,r="Ukucajte bar još "+n+" simbol";return r+=e(n,"","a","a"),r},loadingMore:function(){return"Preuzimanje još rezultata…"},maximumSelected:function(t){var n="Možete izabrati samo "+t.maximum+" stavk";return n+=e(t.maximum,"u","e","i"),n},noResults:function(){return"Ništa nije pronađeno"},searching:function(){return"Pretraga…"},removeAllItems:function(){return"Uklonite sve stavke"}}}),{define:e.define,require:e.require}})();
|
||||
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/bs",[],function(){function e(e,t,n,r){return e%10==1&&e%100!=11?t:e%10>=2&&e%10<=4&&(e%100<12||e%100>14)?n:r}return{errorLoading:function(){return"Preuzimanje nije uspijelo."},inputTooLong:function(t){var n=t.input.length-t.maximum,r="Obrišite "+n+" simbol";return r+=e(n,"","a","a"),r},inputTooShort:function(t){var n=t.minimum-t.input.length,r="Ukucajte bar još "+n+" simbol";return r+=e(n,"","a","a"),r},loadingMore:function(){return"Preuzimanje još rezultata…"},maximumSelected:function(t){var n="Možete izabrati samo "+t.maximum+" stavk";return n+=e(t.maximum,"u","e","i"),n},noResults:function(){return"Ništa nije pronađeno"},searching:function(){return"Pretraga…"},removeAllItems:function(){return"Uklonite sve stavke"}}}),{define:e.define,require:e.require}})();
|
||||
2
src/interface/static/admin/js/vendor/select2/i18n/ca.js
vendored
Executable file → Normal file
@@ -1,3 +1,3 @@
|
||||
/*! Select2 4.0.7 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||
|
||||
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ca",[],function(){return{errorLoading:function(){return"La càrrega ha fallat"},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Si us plau, elimina "+t+" car";return t==1?n+="àcter":n+="àcters",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Si us plau, introdueix "+t+" car";return t==1?n+="àcter":n+="àcters",n},loadingMore:function(){return"Carregant més resultats…"},maximumSelected:function(e){var t="Només es pot seleccionar "+e.maximum+" element";return e.maximum!=1&&(t+="s"),t},noResults:function(){return"No s'han trobat resultats"},searching:function(){return"Cercant…"},removeAllItems:function(){return"Treu tots els elements"}}}),{define:e.define,require:e.require}})();
|
||||
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/ca",[],function(){return{errorLoading:function(){return"La càrrega ha fallat"},inputTooLong:function(e){var t=e.input.length-e.maximum,n="Si us plau, elimina "+t+" car";return t==1?n+="àcter":n+="àcters",n},inputTooShort:function(e){var t=e.minimum-e.input.length,n="Si us plau, introdueix "+t+" car";return t==1?n+="àcter":n+="àcters",n},loadingMore:function(){return"Carregant més resultats…"},maximumSelected:function(e){var t="Només es pot seleccionar "+e.maximum+" element";return e.maximum!=1&&(t+="s"),t},noResults:function(){return"No s'han trobat resultats"},searching:function(){return"Cercant…"},removeAllItems:function(){return"Treu tots els elements"}}}),{define:e.define,require:e.require}})();
|
||||
2
src/interface/static/admin/js/vendor/select2/i18n/cs.js
vendored
Executable file → Normal file
@@ -1,3 +1,3 @@
|
||||
/*! Select2 4.0.7 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||
|
||||
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/cs",[],function(){function e(e,t){switch(e){case 2:return t?"dva":"dvě";case 3:return"tři";case 4:return"čtyři"}return""}return{errorLoading:function(){return"Výsledky nemohly být načteny."},inputTooLong:function(t){var n=t.input.length-t.maximum;return n==1?"Prosím, zadejte o jeden znak méně.":n<=4?"Prosím, zadejte o "+e(n,!0)+" znaky méně.":"Prosím, zadejte o "+n+" znaků méně."},inputTooShort:function(t){var n=t.minimum-t.input.length;return n==1?"Prosím, zadejte ještě jeden znak.":n<=4?"Prosím, zadejte ještě další "+e(n,!0)+" znaky.":"Prosím, zadejte ještě dalších "+n+" znaků."},loadingMore:function(){return"Načítají se další výsledky…"},maximumSelected:function(t){var n=t.maximum;return n==1?"Můžete zvolit jen jednu položku.":n<=4?"Můžete zvolit maximálně "+e(n,!1)+" položky.":"Můžete zvolit maximálně "+n+" položek."},noResults:function(){return"Nenalezeny žádné položky."},searching:function(){return"Vyhledávání…"},removeAllItems:function(){return"Odstraňte všechny položky"}}}),{define:e.define,require:e.require}})();
|
||||
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/cs",[],function(){function e(e,t){switch(e){case 2:return t?"dva":"dvě";case 3:return"tři";case 4:return"čtyři"}return""}return{errorLoading:function(){return"Výsledky nemohly být načteny."},inputTooLong:function(t){var n=t.input.length-t.maximum;return n==1?"Prosím, zadejte o jeden znak méně.":n<=4?"Prosím, zadejte o "+e(n,!0)+" znaky méně.":"Prosím, zadejte o "+n+" znaků méně."},inputTooShort:function(t){var n=t.minimum-t.input.length;return n==1?"Prosím, zadejte ještě jeden znak.":n<=4?"Prosím, zadejte ještě další "+e(n,!0)+" znaky.":"Prosím, zadejte ještě dalších "+n+" znaků."},loadingMore:function(){return"Načítají se další výsledky…"},maximumSelected:function(t){var n=t.maximum;return n==1?"Můžete zvolit jen jednu položku.":n<=4?"Můžete zvolit maximálně "+e(n,!1)+" položky.":"Můžete zvolit maximálně "+n+" položek."},noResults:function(){return"Nenalezeny žádné položky."},searching:function(){return"Vyhledávání…"},removeAllItems:function(){return"Odstraňte všechny položky"}}}),{define:e.define,require:e.require}})();
|
||||
2
src/interface/static/admin/js/vendor/select2/i18n/da.js
vendored
Executable file → Normal file
@@ -1,3 +1,3 @@
|
||||
/*! Select2 4.0.7 | https://github.com/select2/select2/blob/master/LICENSE.md */
|
||||
|
||||
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/da",[],function(){return{errorLoading:function(){return"Resultaterne kunne ikke indlæses."},inputTooLong:function(e){var t=e.input.length-e.maximum;return"Angiv venligst "+t+" tegn mindre"},inputTooShort:function(e){var t=e.minimum-e.input.length;return"Angiv venligst "+t+" tegn mere"},loadingMore:function(){return"Indlæser flere resultater…"},maximumSelected:function(e){var t="Du kan kun vælge "+e.maximum+" emne";return e.maximum!=1&&(t+="r"),t},noResults:function(){return"Ingen resultater fundet"},searching:function(){return"Søger…"},removeAllItems:function(){return"Fjern alle elementer"}}}),{define:e.define,require:e.require}})();
|
||||
(function(){if(jQuery&&jQuery.fn&&jQuery.fn.select2&&jQuery.fn.select2.amd)var e=jQuery.fn.select2.amd;return e.define("select2/i18n/da",[],function(){return{errorLoading:function(){return"Resultaterne kunne ikke indlæses."},inputTooLong:function(e){var t=e.input.length-e.maximum;return"Angiv venligst "+t+" tegn mindre"},inputTooShort:function(e){var t=e.minimum-e.input.length;return"Angiv venligst "+t+" tegn mere"},loadingMore:function(){return"Indlæser flere resultater…"},maximumSelected:function(e){var t="Du kan kun vælge "+e.maximum+" emne";return e.maximum!=1&&(t+="r"),t},noResults:function(){return"Ingen resultater fundet"},searching:function(){return"Søger…"},removeAllItems:function(){return"Fjern alle elementer"}}}),{define:e.define,require:e.require}})();
|
||||