mirror of
https://github.com/inventree/InvenTree.git
synced 2025-06-13 02:25:38 +00:00
0.6.0 stable (#2655)
* fix spelling Thanks @Stephano120 Good catch! * add migration * I18n merge (#2582) * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Adds a warning if no build outputs are created * Throw validation error if no build outputs have been started * Prevent AttributeError from being thrown Ref: https://github.com/inventree/InvenTree/issues/2587 * fix: use default storage backend for Maint Mode * Experimenting with children models permissions * Prevent build outputs being created with zero quantity * PEP style fixes * Reload build output table when an active build output is deleted * Reload completed output table * Fixes issue with BOM export - Cascading BOM export was broken * Boolean settings are now directly clickable * Layout changes * Display error if setting update failes * Skips some specific steps when importing data - We need to prevent certain operations from running when we are importing data - This is to prevent unique database constraints from being violated - Do not register plugins during data import - Do not launch notification events * PEP fixes * Adds new setting to optionally display or hide part pricing information * Hide pricing history tab if not enabled * Only calculate pricing data if required * Disable multi-level BOM requests * Adds serializer for uploading a BOM file and extracting fields * Fix existing bug with BomExport functionality - could not select BOM format * POST request now returns extracted data rows (as an array of dicts) * Attempt to auto-extract part information based on provided data * Basic javascript function to construct BOM table from extracted data * Initialize related field for "part" selection * Add callback for "remove row" button * Construct required form fields - required some additional functionality in forms.js * Add "clear input" callback function * Add optional part lookup by "part" field * Allow decimal values for BOM overage * Adds a BomUpload endpoint to handle upload of complete BOM * Check for duplicate BOM items as part of serializer validation * bug fix * Adds options to clear existing BOM data when uploading * Update upload file template * Remove old templates * PEP fixes * Handle errors when connecting to currency exchange - Also adds timeout when connecting * JS linting * Only update rates on server launch if there are no rates available * PEP fixes * unit test fixes * Do not hide the "submit order" button * Remove incorrect validation routine * Refactored and added permission check for children models * Reverted print statement to logger * Improved approach to permission check at runtime * Fix logic for enabling "place order" button * Update README.md Add "follow on twitter" button * Allow POST of files for unit testing * Update README.md Reorder sections * Catch potential file processing errors * Add unit testing for uploading invalid BOM files * Raise error if imported dataset contains no data rows * Return per-row error messages when extracting data * PEP fixes * Adds check for duplicate parts when importing * Increased error checking when uploading BOM data * Catch potential error when posting invalid numbers via REST API * Improve part "guess" algorithm * Display initial errors when importing data * Add button to display original row data * Disable "submit" button to prevent multiple simultaneous uploads * Add more unit testing for BOM file upload - Test "levels" functionality - Test part guessing / introspection * Adds API endpoint to delete build outputs * Remove old form code which is no longer used * Cleanup * js linting * Update base django version * fix quotes * ignore the django import check * ignore import error * ignore migration * ignore branches * remove coverage from parts migrations * fix migration coverage for orders * fix migration coverage for company * fix migration coverage for build * simpler coverage ignore * run test paralell * ignore coverage on ruleset checks * remove dead code * move up comment so unneeded functions are not not covered * remove dead code * ignore database not ready * imports are not tested * no test for malformed paths * ignore exception ref * only run sqlite paralell * fix import * remove dead code * PEP fix * ignore wrong control view safeties * ignore controls that should not be reached in coverage * test wrong setting defaults * remove paralell coverage * fix setting coverage * Remove settings mods * Pep * Allow BOM file to be "re-uploaded" * ignore ci render_test * add comment about function * ignore debug toolbar * app not ready can not be simulated by tests * use same style for AppNotReady Exception * database not ready events are hard to reproduce consistently * remove dead test * ignore not testable condition * ignore coverage in exsisting migrations * will never be true in testing * add test for system healt checks * test test mode * test Isimporting * ignore whole file * ignore system exit conditions in coverage * ignore testing coditions in coverage * do not cover secret key * ignore db optm in coverage * ignore some default in coverage * ignore currently dead code in coverage * ignore wsgi * remove dead code * should not be reached - ignore in cov * ignore sanity checks for coverage * update system health check * fix label tests * omit coverage via setup.cfg * fix reporting emition * catch more explicit * fix coverage * except import errors * add coverage for labels * PEP fix * do not count unreachable code * ignore unreachable things * user api tests * PEP fix * remove coverage that is not reachable * remove cov from not used feature * remove dead code * make git log call simpler * return cov from feature only used for debug * should not be reached * add more plugin coverage * PEP fixes * spellcheck * add test for non existing token * remove dead code -> permission class does that already * add more user api tests * disable broken test * Enforce proper formatting for 'quantity' field when importing BOM data * Adds unit tests for index page Some fairly simple unit tests to ensure that the index page is being correctly loaded. * Adds a new API endpoint for creating build outputs * Adds query function to Part model to return trackable parts in the BOM * Extract serial numbers from submitted form data * Optionally auto-allocate stock items when creating a new build output * remove code which is now unused * PEP style fixes * Form improvements * Automatically select Bom Items with matching serial numbers when allocating stock to a build order output * Adds generic API endpoint for extracting data from a tabulated file * Adds model mixin for generically determining which fields can be imported on any particular model * Adds functionality to map file columns to model fiels * Refactoring API endpoints - Improved URL naming scheme * Adds generic javascript function for mapping file columns to model fields * Adds a button to quickly "pass" a test * js linting * Fix field name * unit test fixes * Fix breadcrumb tree for stock item page * Create FUNDING.yml Add sponsor file * Update FUNDING.yml Add ko_fi username * Spelling fix * Implement unit test for missing columns * Improve unit testing * Further improvements to unit tests * Adds information on test result being deleted * Adds "refresh" button for stock test table * Ensure unit tests are more resilient * Adds API endpoint for installing stock items into other stock items - Requires more filtering for the Part API - Adds more BOM related functionality for Part model - Removes old server-side form * PEP fixes * Critical bug fix: Check if serial numbers already exist when creating new StockItem * Allow processing of "null" cells (caused by xls / xlsx import) * Reintroduce option to clear (delete) BOM before uploading new data * When uploading a report template, keep the existing filename (if it is the same report!) * Improved error messages when report templates (or snippets) are missing! * Delete template files from cache as they are uploaded * Set default error message visibility in modal options * remove unused code * remove unneeded assignment * merge satement * merge statments * remove unneeded continue * PEP fix * I18n merge (#2647) * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * fix caps * fix string concat * use f-string annotation * I18n release merge (#2654) * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix: New translations django.po from Crowdin * Fix conflict * Removes outdated templates Co-authored-by: Matthias Mair <66015116+matmair@users.noreply.github.com> Co-authored-by: Matthias <matthias.mair@oewf.org> Co-authored-by: Nigel <nigel.w@nosun.ca> Co-authored-by: eeintech <eeintech@eeinte.ch>
This commit is contained in:
2
.github/FUNDING.yml
vendored
Normal file
2
.github/FUNDING.yml
vendored
Normal file
@ -0,0 +1,2 @@
|
||||
patreon: inventree
|
||||
ko_fi: inventree
|
30
.github/ISSUE_TEMPLATE/app_issue.md
vendored
30
.github/ISSUE_TEMPLATE/app_issue.md
vendored
@ -1,30 +0,0 @@
|
||||
---
|
||||
name: App issue
|
||||
about: Report a bug or issue with the InvenTree app
|
||||
title: "[APP] Enter bug description"
|
||||
labels: bug, app
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of the bug or issue
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
|
||||
1. Go to ...
|
||||
2. Select ...
|
||||
3. ...
|
||||
|
||||
**Expected Behavior**
|
||||
A clear and concise description of what you expected to happen
|
||||
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem
|
||||
|
||||
**Version Information**
|
||||
|
||||
- App platform: *Select iOS or Android*
|
||||
- App version: *Enter app version*
|
||||
- Server version: *Enter server version*
|
32
.github/ISSUE_TEMPLATE/bug_report.md
vendored
32
.github/ISSUE_TEMPLATE/bug_report.md
vendored
@ -1,31 +1,47 @@
|
||||
---
|
||||
name: Bug report
|
||||
about: Create a bug report to help us improve InvenTree
|
||||
name: Bug
|
||||
about: Create a bug report to help us improve InvenTree!
|
||||
title: "[BUG] Enter bug description"
|
||||
labels: bug, question
|
||||
assignees: ''
|
||||
|
||||
---
|
||||
|
||||
**Describe the bug**
|
||||
A clear and concise description of what the bug is.
|
||||
<!---
|
||||
Everything inside these brackets is hidden - please remove them where you fill out information.
|
||||
--->
|
||||
|
||||
|
||||
**Describe the bug**
|
||||
<!---
|
||||
A clear and concise description of what the bug is.
|
||||
--->
|
||||
|
||||
**Steps to Reproduce**
|
||||
|
||||
**To Reproduce**
|
||||
Steps to reproduce the behavior:
|
||||
<!---
|
||||
1. Go to '...'
|
||||
2. Click on '....'
|
||||
3. Scroll down to '....'
|
||||
4. See error
|
||||
--->
|
||||
|
||||
**Expected behavior**
|
||||
<!---
|
||||
A clear and concise description of what you expected to happen.
|
||||
--->
|
||||
|
||||
<!---
|
||||
**Screenshots**
|
||||
If applicable, add screenshots to help explain your problem.
|
||||
--->
|
||||
|
||||
**Deployment Method**
|
||||
Docker
|
||||
Bare Metal
|
||||
- [ ] Docker
|
||||
- [ ] Bare Metal
|
||||
|
||||
**Version Information**
|
||||
You can get this by going to the "About InvenTree" section in the upper right corner and cicking on to the "copy version information"
|
||||
<!---
|
||||
You can get this by going to the "About InvenTree" section in the upper right corner and clicking on to the "copy version information"
|
||||
--->
|
||||
|
37
.github/workflows/check_translations.yaml
vendored
Normal file
37
.github/workflows/check_translations.yaml
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
name: Check Translations
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- l10
|
||||
pull_request:
|
||||
branches:
|
||||
- l10
|
||||
|
||||
jobs:
|
||||
|
||||
check:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
INVENTREE_DB_NAME: './test_db.sqlite'
|
||||
INVENTREE_DB_ENGINE: django.db.backends.sqlite3
|
||||
INVENTREE_DEBUG: info
|
||||
INVENTREE_MEDIA_ROOT: ./media
|
||||
INVENTREE_STATIC_ROOT: ./static
|
||||
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install gettext
|
||||
pip3 install invoke
|
||||
invoke install
|
||||
- name: Test Translations
|
||||
run: invoke translate
|
||||
- name: Check Migration Files
|
||||
run: python3 ci/check_migration_files.py
|
60
.github/workflows/coverage.yaml
vendored
60
.github/workflows/coverage.yaml
vendored
@ -1,60 +0,0 @@
|
||||
# Perform CI checks, and calculate code coverage
|
||||
|
||||
name: SQLite
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- l10*
|
||||
|
||||
pull_request:
|
||||
branches-ignore:
|
||||
- l10*
|
||||
|
||||
jobs:
|
||||
|
||||
# Run tests on SQLite database
|
||||
# These tests are used for code coverage analysis
|
||||
coverage:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
INVENTREE_DB_NAME: './test_db.sqlite'
|
||||
INVENTREE_DB_ENGINE: django.db.backends.sqlite3
|
||||
INVENTREE_DEBUG: info
|
||||
INVENTREE_MEDIA_ROOT: ./media
|
||||
INVENTREE_STATIC_ROOT: ./static
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.7
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install gettext
|
||||
pip3 install invoke
|
||||
invoke install
|
||||
invoke static
|
||||
- name: Coverage Tests
|
||||
run: |
|
||||
invoke coverage
|
||||
- name: Data Import Export
|
||||
run: |
|
||||
invoke migrate
|
||||
invoke import-fixtures
|
||||
invoke export-records -f data.json
|
||||
rm test_db.sqlite
|
||||
invoke migrate
|
||||
invoke import-records -f data.json
|
||||
invoke import-records -f data.json
|
||||
- name: Test Translations
|
||||
run: invoke translate
|
||||
- name: Check Migration Files
|
||||
run: python3 ci/check_migration_files.py
|
||||
- name: Upload Coverage Report
|
||||
run: coveralls
|
1
.github/workflows/docker_latest.yaml
vendored
1
.github/workflows/docker_latest.yaml
vendored
@ -34,7 +34,6 @@ jobs:
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||
push: true
|
||||
target: production
|
||||
repository: inventree/inventree
|
||||
tags: inventree/inventree:latest
|
||||
- name: Image Digest
|
||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
||||
|
8
.github/workflows/docker_stable.yaml
vendored
8
.github/workflows/docker_stable.yaml
vendored
@ -1,4 +1,5 @@
|
||||
# Build and push latest docker image on push to master branch
|
||||
# Build and push docker image on push to 'stable' branch
|
||||
# Docker build will be uploaded to dockerhub with the 'inventree:stable' tag
|
||||
|
||||
name: Docker Build
|
||||
|
||||
@ -34,9 +35,8 @@ jobs:
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||
push: true
|
||||
target: production
|
||||
build-args: |
|
||||
branch=stable
|
||||
repository: inventree/inventree
|
||||
build-args:
|
||||
branch: stable
|
||||
tags: inventree/inventree:stable
|
||||
- name: Image Digest
|
||||
run: echo ${{ steps.docker_build.outputs.digest }}
|
||||
|
8
.github/workflows/docker_tag.yaml
vendored
8
.github/workflows/docker_tag.yaml
vendored
@ -1,4 +1,5 @@
|
||||
# Publish docker images to dockerhub
|
||||
# Publish docker images to dockerhub on a tagged release
|
||||
# Docker build will be uploaded to dockerhub with the 'invetree:<tag>' tag
|
||||
|
||||
name: Docker Publish
|
||||
|
||||
@ -32,7 +33,6 @@ jobs:
|
||||
platforms: linux/amd64,linux/arm64,linux/arm/v7
|
||||
push: true
|
||||
target: production
|
||||
build-args: |
|
||||
tag=${{ github.event.release.tag_name }}
|
||||
repository: inventree/inventree
|
||||
build-args:
|
||||
tag: ${{ github.event.release.tag_name }}
|
||||
tags: inventree/inventree:${{ github.event.release.tag_name }}
|
||||
|
37
.github/workflows/docker_test.yaml
vendored
Normal file
37
.github/workflows/docker_test.yaml
vendored
Normal file
@ -0,0 +1,37 @@
|
||||
# Test that the InvenTree docker image compiles correctly
|
||||
|
||||
# This CI action runs on pushes to either the master or stable branches
|
||||
|
||||
# 1. Build the development docker image (as per the documentation)
|
||||
# 2. Install requied python libs into the docker container
|
||||
# 3. Launch the container
|
||||
# 4. Check that the API endpoint is available
|
||||
|
||||
name: Docker Test
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- 'master'
|
||||
- 'stable'
|
||||
|
||||
jobs:
|
||||
|
||||
docker:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
- name: Build Docker Image
|
||||
run: |
|
||||
cd docker
|
||||
docker-compose -f docker-compose.sqlite.yml build
|
||||
docker-compose -f docker-compose.sqlite.yml run inventree-dev-server invoke update
|
||||
docker-compose -f docker-compose.sqlite.yml up -d
|
||||
- name: Sleepy Time
|
||||
run: sleep 60
|
||||
- name: Test API
|
||||
run: |
|
||||
pip install requests
|
||||
python3 ci/check_api_endpoint.py
|
54
.github/workflows/html.yaml
vendored
54
.github/workflows/html.yaml
vendored
@ -1,54 +0,0 @@
|
||||
# Check javascript template files
|
||||
|
||||
name: HTML Templates
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
pull_request:
|
||||
branches-ignore:
|
||||
- l10*
|
||||
|
||||
jobs:
|
||||
|
||||
html:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
INVENTREE_DB_ENGINE: sqlite3
|
||||
INVENTREE_DB_NAME: inventree
|
||||
INVENTREE_MEDIA_ROOT: ./media
|
||||
INVENTREE_STATIC_ROOT: ./static
|
||||
steps:
|
||||
- name: Install node.js
|
||||
uses: actions/setup-node@v2
|
||||
- run: npm install
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.7
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install gettext
|
||||
pip3 install invoke
|
||||
invoke install
|
||||
invoke static
|
||||
- name: Check HTML Files
|
||||
run: |
|
||||
npm install markuplint
|
||||
npx markuplint InvenTree/build/templates/build/*.html
|
||||
npx markuplint InvenTree/common/templates/common/*.html
|
||||
npx markuplint InvenTree/company/templates/company/*.html
|
||||
npx markuplint InvenTree/order/templates/order/*.html
|
||||
npx markuplint InvenTree/part/templates/part/*.html
|
||||
npx markuplint InvenTree/stock/templates/stock/*.html
|
||||
npx markuplint InvenTree/templates/*.html
|
||||
npx markuplint InvenTree/templates/InvenTree/*.html
|
||||
npx markuplint InvenTree/templates/InvenTree/settings/*.html
|
||||
|
50
.github/workflows/javascript.yaml
vendored
50
.github/workflows/javascript.yaml
vendored
@ -1,50 +0,0 @@
|
||||
# Check javascript template files
|
||||
|
||||
name: Javascript Templates
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
pull_request:
|
||||
branches-ignore:
|
||||
- l10*
|
||||
|
||||
jobs:
|
||||
|
||||
javascript:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
INVENTREE_DB_ENGINE: sqlite3
|
||||
INVENTREE_DB_NAME: inventree
|
||||
INVENTREE_MEDIA_ROOT: ./media
|
||||
INVENTREE_STATIC_ROOT: ./static
|
||||
steps:
|
||||
- name: Install node.js
|
||||
uses: actions/setup-node@v2
|
||||
- run: npm install
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.7
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install gettext
|
||||
pip3 install invoke
|
||||
invoke install
|
||||
invoke static
|
||||
- name: Check Templated Files
|
||||
run: |
|
||||
cd ci
|
||||
python check_js_templates.py
|
||||
- name: Lint Javascript Files
|
||||
run: |
|
||||
npm install eslint eslint-config-google
|
||||
invoke render-js-files
|
||||
npx eslint js_tmp/*.js
|
67
.github/workflows/mysql.yaml
vendored
67
.github/workflows/mysql.yaml
vendored
@ -1,67 +0,0 @@
|
||||
# MySQL Unit Testing
|
||||
|
||||
name: MySQL
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- l10*
|
||||
|
||||
pull_request:
|
||||
branches-ignore:
|
||||
- l10*
|
||||
|
||||
jobs:
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
# Database backend configuration
|
||||
INVENTREE_DB_ENGINE: django.db.backends.mysql
|
||||
INVENTREE_DB_NAME: inventree
|
||||
INVENTREE_DB_USER: root
|
||||
INVENTREE_DB_PASSWORD: password
|
||||
INVENTREE_DB_HOST: '127.0.0.1'
|
||||
INVENTREE_DB_PORT: 3306
|
||||
INVENTREE_DEBUG: info
|
||||
INVENTREE_MEDIA_ROOT: ./media
|
||||
INVENTREE_STATIC_ROOT: ./static
|
||||
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:latest
|
||||
env:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: yes
|
||||
MYSQL_DATABASE: inventree
|
||||
MYSQL_USER: inventree
|
||||
MYSQL_PASSWORD: password
|
||||
MYSQL_ROOT_PASSWORD: password
|
||||
options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
|
||||
ports:
|
||||
- 3306:3306
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.7
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
sudo apt-get install mysql-server libmysqlclient-dev
|
||||
pip3 install invoke
|
||||
pip3 install mysqlclient
|
||||
invoke install
|
||||
- name: Run Tests
|
||||
run: invoke test
|
||||
- name: Data Import Export
|
||||
run: |
|
||||
invoke migrate
|
||||
python3 ./InvenTree/manage.py flush --noinput
|
||||
invoke import-fixtures
|
||||
invoke export-records -f data.json
|
||||
python3 ./InvenTree/manage.py flush --noinput
|
||||
invoke import-records -f data.json
|
||||
invoke import-records -f data.json
|
63
.github/workflows/postgresql.yaml
vendored
63
.github/workflows/postgresql.yaml
vendored
@ -1,63 +0,0 @@
|
||||
# PostgreSQL Unit Testing
|
||||
|
||||
name: PostgreSQL
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- l10*
|
||||
|
||||
pull_request:
|
||||
branches-ignore:
|
||||
- l10*
|
||||
|
||||
jobs:
|
||||
|
||||
test:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
# Database backend configuration
|
||||
INVENTREE_DB_ENGINE: django.db.backends.postgresql
|
||||
INVENTREE_DB_NAME: inventree
|
||||
INVENTREE_DB_USER: inventree
|
||||
INVENTREE_DB_PASSWORD: password
|
||||
INVENTREE_DB_HOST: '127.0.0.1'
|
||||
INVENTREE_DB_PORT: 5432
|
||||
INVENTREE_DEBUG: info
|
||||
INVENTREE_MEDIA_ROOT: ./media
|
||||
INVENTREE_STATIC_ROOT: ./static
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres
|
||||
env:
|
||||
POSTGRES_USER: inventree
|
||||
POSTGRES_PASSWORD: password
|
||||
ports:
|
||||
- 5432:5432
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: 3.7
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
sudo apt-get install libpq-dev
|
||||
pip3 install invoke
|
||||
pip3 install psycopg2
|
||||
invoke install
|
||||
- name: Run Tests
|
||||
run: invoke test
|
||||
- name: Data Import Export
|
||||
run: |
|
||||
invoke migrate
|
||||
python3 ./InvenTree/manage.py flush --noinput
|
||||
invoke import-fixtures
|
||||
invoke export-records -f data.json
|
||||
python3 ./InvenTree/manage.py flush --noinput
|
||||
invoke import-records -f data.json
|
||||
invoke import-records -f data.json
|
49
.github/workflows/python.yaml
vendored
49
.github/workflows/python.yaml
vendored
@ -1,49 +0,0 @@
|
||||
# Run python library tests whenever code is pushed to master
|
||||
|
||||
name: Python Bindings
|
||||
|
||||
on:
|
||||
push:
|
||||
branches:
|
||||
- master
|
||||
|
||||
pull_request:
|
||||
branches-ignore:
|
||||
- l10*
|
||||
|
||||
jobs:
|
||||
|
||||
python:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
INVENTREE_DB_NAME: './test_db.sqlite'
|
||||
INVENTREE_DB_ENGINE: 'sqlite3'
|
||||
INVENTREE_DEBUG: info
|
||||
INVENTREE_MEDIA_ROOT: ./media
|
||||
INVENTREE_STATIC_ROOT: ./static
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
- name: Install InvenTree
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install python3-dev python3-pip python3-venv
|
||||
pip3 install invoke
|
||||
invoke install
|
||||
invoke migrate
|
||||
- name: Download Python Code
|
||||
run: |
|
||||
git clone --depth 1 https://github.com/inventree/inventree-python ./inventree-python
|
||||
- name: Start Server
|
||||
run: |
|
||||
invoke import-records -f ./inventree-python/test/test_data.json
|
||||
invoke server -a 127.0.0.1:8000 &
|
||||
sleep 60
|
||||
- name: Run Tests
|
||||
run: |
|
||||
cd inventree-python
|
||||
invoke test
|
||||
|
304
.github/workflows/qc_checks.yaml
vendored
Normal file
304
.github/workflows/qc_checks.yaml
vendored
Normal file
@ -0,0 +1,304 @@
|
||||
# Checks for each PR / push
|
||||
|
||||
name: QC checks
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- l10*
|
||||
|
||||
pull_request:
|
||||
branches-ignore:
|
||||
- l10*
|
||||
|
||||
env:
|
||||
python_version: 3.8
|
||||
node_version: 16
|
||||
|
||||
server_start_sleep: 60
|
||||
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
INVENTREE_DB_ENGINE: sqlite3
|
||||
INVENTREE_DB_NAME: inventree
|
||||
INVENTREE_MEDIA_ROOT: ./media
|
||||
INVENTREE_STATIC_ROOT: ./static
|
||||
|
||||
|
||||
jobs:
|
||||
pep_style:
|
||||
name: PEP style (python)
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ env.python_version }}
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ env.python_version }}
|
||||
cache: 'pip'
|
||||
- name: Install deps
|
||||
run: |
|
||||
pip install flake8==3.8.3
|
||||
pip install pep8-naming==0.11.1
|
||||
- name: flake8
|
||||
run: |
|
||||
flake8 InvenTree
|
||||
|
||||
javascript:
|
||||
name: javascript template files
|
||||
needs: pep_style
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
- name: Install node.js ${{ env.node_version }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ env.node_version }}
|
||||
cache: 'npm'
|
||||
- run: npm install
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ env.python_version }}
|
||||
cache: 'pip'
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install gettext
|
||||
pip3 install invoke
|
||||
invoke install
|
||||
invoke static
|
||||
- name: Check Templated Files
|
||||
run: |
|
||||
cd ci
|
||||
python check_js_templates.py
|
||||
- name: Lint Javascript Files
|
||||
run: |
|
||||
invoke render-js-files
|
||||
npx eslint js_tmp/*.js
|
||||
|
||||
html:
|
||||
name: html template files
|
||||
needs: pep_style
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
- name: Install node.js ${{ env.node_version }}
|
||||
uses: actions/setup-node@v2
|
||||
with:
|
||||
node-version: ${{ env.node_version }}
|
||||
cache: 'npm'
|
||||
- run: npm install
|
||||
- name: Setup Python
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ env.python_version }}
|
||||
cache: 'pip'
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install gettext
|
||||
pip3 install invoke
|
||||
invoke install
|
||||
invoke static
|
||||
- name: Check HTML Files
|
||||
run: |
|
||||
npx markuplint InvenTree/build/templates/build/*.html
|
||||
npx markuplint InvenTree/company/templates/company/*.html
|
||||
npx markuplint InvenTree/order/templates/order/*.html
|
||||
npx markuplint InvenTree/part/templates/part/*.html
|
||||
npx markuplint InvenTree/stock/templates/stock/*.html
|
||||
npx markuplint InvenTree/templates/*.html
|
||||
npx markuplint InvenTree/templates/InvenTree/*.html
|
||||
npx markuplint InvenTree/templates/InvenTree/settings/*.html
|
||||
|
||||
python:
|
||||
name: python bindings
|
||||
needs: pep_style
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
wrapper_name: inventree-python
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
- name: Install InvenTree
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install python3-dev python3-pip python3-venv
|
||||
pip3 install invoke
|
||||
invoke install
|
||||
invoke migrate
|
||||
- name: Download Python Code
|
||||
run: |
|
||||
git clone --depth 1 https://github.com/inventree/${{ env.wrapper_name }} ./${{ env.wrapper_name }}
|
||||
- name: Start Server
|
||||
run: |
|
||||
invoke import-records -f ./${{ env.wrapper_name }}/test/test_data.json
|
||||
invoke server -a 127.0.0.1:8000 &
|
||||
sleep ${{ env.server_start_sleep }}
|
||||
- name: Run Tests
|
||||
run: |
|
||||
cd ${{ env.wrapper_name }}
|
||||
invoke test
|
||||
|
||||
coverage:
|
||||
name: Sqlite / coverage
|
||||
needs: ['javascript', 'html']
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
INVENTREE_DB_NAME: ./inventree.sqlite
|
||||
INVENTREE_DB_ENGINE: sqlite3
|
||||
INVENTREE_PLUGINS_ENABLED: true
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Python ${{ env.python_version }}
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ env.python_version }}
|
||||
cache: 'pip'
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install gettext
|
||||
pip3 install invoke
|
||||
invoke install
|
||||
invoke static
|
||||
- name: Coverage Tests
|
||||
run: |
|
||||
invoke coverage
|
||||
- name: Data Import Export
|
||||
run: |
|
||||
invoke migrate
|
||||
invoke import-fixtures
|
||||
invoke export-records -f data.json
|
||||
rm inventree.sqlite
|
||||
invoke migrate
|
||||
invoke import-records -f data.json
|
||||
invoke import-records -f data.json
|
||||
- name: Test Translations
|
||||
run: invoke translate
|
||||
- name: Check Migration Files
|
||||
run: python3 ci/check_migration_files.py
|
||||
- name: Upload Coverage Report
|
||||
run: coveralls
|
||||
|
||||
postgres:
|
||||
name: Postgres
|
||||
needs: ['javascript', 'html']
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
env:
|
||||
INVENTREE_DB_ENGINE: django.db.backends.postgresql
|
||||
INVENTREE_DB_USER: inventree
|
||||
INVENTREE_DB_PASSWORD: password
|
||||
INVENTREE_DB_HOST: '127.0.0.1'
|
||||
INVENTREE_DB_PORT: 5432
|
||||
INVENTREE_DEBUG: info
|
||||
INVENTREE_CACHE_HOST: localhost
|
||||
INVENTREE_PLUGINS_ENABLED: true
|
||||
|
||||
services:
|
||||
postgres:
|
||||
image: postgres
|
||||
env:
|
||||
POSTGRES_USER: inventree
|
||||
POSTGRES_PASSWORD: password
|
||||
ports:
|
||||
- 5432:5432
|
||||
|
||||
redis:
|
||||
image: redis
|
||||
ports:
|
||||
- 6379:6379
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Python ${{ env.python_version }}
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ env.python_version }}
|
||||
cache: 'pip'
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install libpq-dev
|
||||
pip3 install invoke
|
||||
pip3 install psycopg2
|
||||
pip3 install django-redis>=5.0.0
|
||||
invoke install
|
||||
- name: Run Tests
|
||||
run: invoke test
|
||||
- name: Data Import Export
|
||||
run: |
|
||||
invoke migrate
|
||||
python3 ./InvenTree/manage.py flush --noinput
|
||||
invoke import-fixtures
|
||||
invoke export-records -f data.json
|
||||
python3 ./InvenTree/manage.py flush --noinput
|
||||
invoke import-records -f data.json
|
||||
invoke import-records -f data.json
|
||||
|
||||
mysql:
|
||||
name: MySql
|
||||
needs: ['javascript', 'html']
|
||||
runs-on: ubuntu-latest
|
||||
env:
|
||||
# Database backend configuration
|
||||
INVENTREE_DB_ENGINE: django.db.backends.mysql
|
||||
INVENTREE_DB_USER: root
|
||||
INVENTREE_DB_PASSWORD: password
|
||||
INVENTREE_DB_HOST: '127.0.0.1'
|
||||
INVENTREE_DB_PORT: 3306
|
||||
INVENTREE_DEBUG: info
|
||||
INVENTREE_PLUGINS_ENABLED: true
|
||||
|
||||
services:
|
||||
mysql:
|
||||
image: mysql:latest
|
||||
env:
|
||||
MYSQL_ALLOW_EMPTY_PASSWORD: yes
|
||||
MYSQL_DATABASE: ${{ env.INVENTREE_DB_NAME }}
|
||||
MYSQL_USER: inventree
|
||||
MYSQL_PASSWORD: password
|
||||
MYSQL_ROOT_PASSWORD: password
|
||||
options: --health-cmd="mysqladmin ping" --health-interval=5s --health-timeout=2s --health-retries=3
|
||||
ports:
|
||||
- 3306:3306
|
||||
|
||||
steps:
|
||||
- name: Checkout Code
|
||||
uses: actions/checkout@v2
|
||||
- name: Setup Python ${{ env.python_version }}
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ env.python_version }}
|
||||
cache: 'pip'
|
||||
- name: Install Dependencies
|
||||
run: |
|
||||
sudo apt-get update
|
||||
sudo apt-get install libmysqlclient-dev
|
||||
pip3 install invoke
|
||||
pip3 install mysqlclient
|
||||
invoke install
|
||||
- name: Run Tests
|
||||
run: invoke test
|
||||
- name: Data Import Export
|
||||
run: |
|
||||
invoke migrate
|
||||
python3 ./InvenTree/manage.py flush --noinput
|
||||
invoke import-fixtures
|
||||
invoke export-records -f data.json
|
||||
python3 ./InvenTree/manage.py flush --noinput
|
||||
invoke import-records -f data.json
|
||||
invoke import-records -f data.json
|
25
.github/workflows/stale.yml
vendored
Normal file
25
.github/workflows/stale.yml
vendored
Normal file
@ -0,0 +1,25 @@
|
||||
# Marks all issues that do not receive activity stale starting 2022
|
||||
name: Mark stale issues and pull requests
|
||||
|
||||
on:
|
||||
schedule:
|
||||
- cron: '24 11 * * *'
|
||||
|
||||
jobs:
|
||||
stale:
|
||||
|
||||
runs-on: ubuntu-latest
|
||||
permissions:
|
||||
issues: write
|
||||
pull-requests: write
|
||||
|
||||
steps:
|
||||
- uses: actions/stale@v3
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
stale-issue-message: 'This issue seems stale. Please react to show this is still important.'
|
||||
stale-pr-message: 'This PR seems stale. Please react to show this is still important.'
|
||||
stale-issue-label: 'no-activity'
|
||||
stale-pr-label: 'no-activity'
|
||||
start-date: '2022-01-01'
|
||||
exempt-all-milestones: true
|
34
.github/workflows/style.yaml
vendored
34
.github/workflows/style.yaml
vendored
@ -1,34 +0,0 @@
|
||||
name: Style Checks
|
||||
|
||||
on:
|
||||
push:
|
||||
branches-ignore:
|
||||
- l10*
|
||||
|
||||
pull_request:
|
||||
branches-ignore:
|
||||
- l10*
|
||||
|
||||
jobs:
|
||||
style:
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
strategy:
|
||||
max-parallel: 4
|
||||
matrix:
|
||||
python-version: [3.7]
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v2
|
||||
- name: Set up Python ${{ matrix.python-version }}
|
||||
uses: actions/setup-python@v2
|
||||
with:
|
||||
python-version: ${{ matrix.python-version }}
|
||||
- name: Install deps
|
||||
run: |
|
||||
pip install flake8==3.8.3
|
||||
pip install pep8-naming==0.11.1
|
||||
- name: flake8
|
||||
run: |
|
||||
flake8 InvenTree
|
@ -1,15 +1,16 @@
|
||||
# Check that the version number format matches the current branch
|
||||
|
||||
name: Version Numbering
|
||||
# Checks version number
|
||||
name: version number
|
||||
|
||||
on:
|
||||
pull_request:
|
||||
branches-ignore:
|
||||
- l10*
|
||||
|
||||
|
||||
jobs:
|
||||
|
||||
check:
|
||||
check_version:
|
||||
name: version number
|
||||
runs-on: ubuntu-latest
|
||||
|
||||
steps:
|
17
.github/workflows/welcome.yml
vendored
Normal file
17
.github/workflows/welcome.yml
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
# welcome new contributers
|
||||
name: Welcome
|
||||
on:
|
||||
pull_request:
|
||||
types: [opened]
|
||||
issues:
|
||||
types: [opened]
|
||||
|
||||
jobs:
|
||||
run:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- uses: actions/first-interaction@v1
|
||||
with:
|
||||
repo-token: ${{ secrets.GITHUB_TOKEN }}
|
||||
issue-message: 'Welcome to InvenTree! Please check the [contributing docs](https://inventree.readthedocs.io/en/latest/contribute/) on how to help.\nIf you experience setup / install issues please read all [install docs]( https://inventree.readthedocs.io/en/latest/start/intro/).'
|
||||
pr-message: 'This is your first PR, welcome!\nPlease check [Contributing](https://github.com/inventree/InvenTree/blob/master/CONTRIBUTING.md) to make sure your submission fits our general code-style and workflow.\nMake sure to document why this PR is needed and to link connected issues so we can review it faster.'
|
11
.gitignore
vendored
11
.gitignore
vendored
@ -49,6 +49,7 @@ static_i18n
|
||||
|
||||
# Local config file
|
||||
config.yaml
|
||||
plugins.txt
|
||||
|
||||
# Default data file
|
||||
data.json
|
||||
@ -77,6 +78,10 @@ dev/
|
||||
locale_stats.json
|
||||
|
||||
# node.js
|
||||
package-lock.json
|
||||
package.json
|
||||
node_modules/
|
||||
node_modules/
|
||||
|
||||
# maintenance locker
|
||||
maintenance_mode_state.txt
|
||||
|
||||
# plugin dev directory
|
||||
plugins/
|
||||
|
47
.gitpod.yml
Normal file
47
.gitpod.yml
Normal file
@ -0,0 +1,47 @@
|
||||
tasks:
|
||||
- name: Setup django
|
||||
before: |
|
||||
export INVENTREE_DB_ENGINE='sqlite3'
|
||||
export INVENTREE_DB_NAME='/workspace/InvenTree/dev/database.sqlite3'
|
||||
export INVENTREE_MEDIA_ROOT='/workspace/InvenTree/inventree-data/media'
|
||||
export INVENTREE_STATIC_ROOT='/workspace/InvenTree/dev/static'
|
||||
export PIP_USER='no'
|
||||
|
||||
python3 -m venv venv
|
||||
source venv/bin/activate
|
||||
pip install invoke
|
||||
inv install
|
||||
mkdir dev
|
||||
inv update
|
||||
gp sync-done setup_server
|
||||
|
||||
- name: Start server
|
||||
init: gp sync-await setup_server
|
||||
command: |
|
||||
gp sync-await setup_server
|
||||
export INVENTREE_DB_ENGINE='sqlite3'
|
||||
export INVENTREE_DB_NAME='/workspace/InvenTree/dev/database.sqlite3'
|
||||
export INVENTREE_MEDIA_ROOT='/workspace/InvenTree/inventree-data/media'
|
||||
export INVENTREE_STATIC_ROOT='/workspace/InvenTree/dev/static'
|
||||
|
||||
source venv/bin/activate
|
||||
rm /workspace/InvenTree/inventree-data -r
|
||||
git clone https://github.com/inventree/demo-dataset /workspace/InvenTree/inventree-data
|
||||
invoke delete-data -f
|
||||
invoke import-records -f /workspace/InvenTree/inventree-data/inventree_data.json
|
||||
|
||||
inv server
|
||||
|
||||
# List the ports to expose. Learn more https://www.gitpod.io/docs/config-ports/
|
||||
ports:
|
||||
- port: 8000
|
||||
onOpen: open-preview
|
||||
|
||||
github:
|
||||
prebuilds:
|
||||
master: true
|
||||
pullRequests: false
|
||||
pullRequestsFromForks: true
|
||||
addBadge: true
|
||||
addLabel: gitpod-ready
|
||||
addCheck: false
|
@ -5,8 +5,6 @@ Main JSON interface views
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import logging
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.http import JsonResponse
|
||||
|
||||
@ -21,14 +19,7 @@ from .views import AjaxView
|
||||
from .version import inventreeVersion, inventreeApiVersion, inventreeInstanceName
|
||||
from .status import is_worker_running
|
||||
|
||||
from plugins import plugins as inventree_plugins
|
||||
|
||||
|
||||
logger = logging.getLogger("inventree")
|
||||
|
||||
|
||||
logger.info("Loading action plugins...")
|
||||
action_plugins = inventree_plugins.load_action_plugins()
|
||||
from plugin import registry
|
||||
|
||||
|
||||
class InfoView(AjaxView):
|
||||
@ -110,10 +101,11 @@ class ActionPluginView(APIView):
|
||||
'error': _("No action specified")
|
||||
})
|
||||
|
||||
for plugin_class in action_plugins:
|
||||
if plugin_class.action_name() == action:
|
||||
|
||||
plugin = plugin_class(request.user, data=data)
|
||||
action_plugins = registry.with_mixin('action')
|
||||
for plugin in action_plugins:
|
||||
if plugin.action_name() == action:
|
||||
# TODO @matmair use easier syntax once InvenTree 0.7.0 is released
|
||||
plugin.init(request.user, data=data)
|
||||
|
||||
plugin.perform_action()
|
||||
|
||||
|
@ -46,7 +46,7 @@ class InvenTreeAPITestCase(APITestCase):
|
||||
self.user.is_staff = True
|
||||
|
||||
self.user.save()
|
||||
|
||||
|
||||
for role in self.roles:
|
||||
self.assignRole(role)
|
||||
|
||||
@ -106,12 +106,12 @@ class InvenTreeAPITestCase(APITestCase):
|
||||
|
||||
return response
|
||||
|
||||
def post(self, url, data, expected_code=None):
|
||||
def post(self, url, data, expected_code=None, format='json'):
|
||||
"""
|
||||
Issue a POST request
|
||||
"""
|
||||
|
||||
response = self.client.post(url, data=data, format='json')
|
||||
response = self.client.post(url, data=data, format=format)
|
||||
|
||||
if expected_code is not None:
|
||||
self.assertEqual(response.status_code, expected_code)
|
||||
@ -130,12 +130,24 @@ class InvenTreeAPITestCase(APITestCase):
|
||||
|
||||
return response
|
||||
|
||||
def patch(self, url, data, files=None, expected_code=None):
|
||||
def patch(self, url, data, expected_code=None, format='json'):
|
||||
"""
|
||||
Issue a PATCH request
|
||||
"""
|
||||
|
||||
response = self.client.patch(url, data=data, files=files, format='json')
|
||||
response = self.client.patch(url, data=data, format=format)
|
||||
|
||||
if expected_code is not None:
|
||||
self.assertEqual(response.status_code, expected_code)
|
||||
|
||||
return response
|
||||
|
||||
def options(self, url, expected_code=None):
|
||||
"""
|
||||
Issue an OPTIONS request
|
||||
"""
|
||||
|
||||
response = self.client.options(url, format='json')
|
||||
|
||||
if expected_code is not None:
|
||||
self.assertEqual(response.status_code, expected_code)
|
||||
|
@ -18,16 +18,37 @@ class InvenTreeConfig(AppConfig):
|
||||
def ready(self):
|
||||
|
||||
if canAppAccessDatabase():
|
||||
|
||||
self.remove_obsolete_tasks()
|
||||
|
||||
self.start_background_tasks()
|
||||
|
||||
if not isInTestMode():
|
||||
self.update_exchange_rates()
|
||||
|
||||
def remove_obsolete_tasks(self):
|
||||
"""
|
||||
Delete any obsolete scheduled tasks in the database
|
||||
"""
|
||||
|
||||
obsolete = [
|
||||
'InvenTree.tasks.delete_expired_sessions',
|
||||
'stock.tasks.delete_old_stock_items',
|
||||
]
|
||||
|
||||
try:
|
||||
from django_q.models import Schedule
|
||||
except AppRegistryNotReady: # pragma: no cover
|
||||
return
|
||||
|
||||
# Remove any existing obsolete tasks
|
||||
Schedule.objects.filter(func__in=obsolete).delete()
|
||||
|
||||
def start_background_tasks(self):
|
||||
|
||||
try:
|
||||
from django_q.models import Schedule
|
||||
except (AppRegistryNotReady):
|
||||
except AppRegistryNotReady: # pragma: no cover
|
||||
return
|
||||
|
||||
logger.info("Starting background tasks...")
|
||||
@ -57,17 +78,16 @@ class InvenTreeConfig(AppConfig):
|
||||
schedule_type=Schedule.DAILY,
|
||||
)
|
||||
|
||||
# Remove expired sessions
|
||||
# Delete old error messages
|
||||
InvenTree.tasks.schedule_task(
|
||||
'InvenTree.tasks.delete_expired_sessions',
|
||||
'InvenTree.tasks.delete_old_error_logs',
|
||||
schedule_type=Schedule.DAILY,
|
||||
)
|
||||
|
||||
# Delete "old" stock items
|
||||
# Delete old notification records
|
||||
InvenTree.tasks.schedule_task(
|
||||
'stock.tasks.delete_old_stock_items',
|
||||
schedule_type=Schedule.MINUTES,
|
||||
minutes=30,
|
||||
'common.tasks.delete_old_notifications',
|
||||
schedule_type=Schedule.DAILY,
|
||||
)
|
||||
|
||||
def update_exchange_rates(self):
|
||||
@ -80,10 +100,10 @@ class InvenTreeConfig(AppConfig):
|
||||
|
||||
try:
|
||||
from djmoney.contrib.exchange.models import ExchangeBackend
|
||||
from datetime import datetime, timedelta
|
||||
|
||||
from InvenTree.tasks import update_exchange_rates
|
||||
from common.settings import currency_code_default
|
||||
except AppRegistryNotReady:
|
||||
except AppRegistryNotReady: # pragma: no cover
|
||||
pass
|
||||
|
||||
base_currency = currency_code_default()
|
||||
@ -95,23 +115,18 @@ class InvenTreeConfig(AppConfig):
|
||||
|
||||
last_update = backend.last_update
|
||||
|
||||
if last_update is not None:
|
||||
delta = datetime.now().date() - last_update.date()
|
||||
if delta > timedelta(days=1):
|
||||
print(f"Last update was {last_update}")
|
||||
update = True
|
||||
else:
|
||||
if last_update is None:
|
||||
# Never been updated
|
||||
print("Exchange backend has never been updated")
|
||||
logger.info("Exchange backend has never been updated")
|
||||
update = True
|
||||
|
||||
# Backend currency has changed?
|
||||
if not base_currency == backend.base_currency:
|
||||
print(f"Base currency changed from {backend.base_currency} to {base_currency}")
|
||||
logger.info(f"Base currency changed from {backend.base_currency} to {base_currency}")
|
||||
update = True
|
||||
|
||||
except (ExchangeBackend.DoesNotExist):
|
||||
print("Exchange backend not found - updating")
|
||||
logger.info("Exchange backend not found - updating")
|
||||
update = True
|
||||
|
||||
except:
|
||||
@ -119,4 +134,7 @@ class InvenTreeConfig(AppConfig):
|
||||
return
|
||||
|
||||
if update:
|
||||
update_exchange_rates()
|
||||
try:
|
||||
update_exchange_rates()
|
||||
except Exception as e:
|
||||
logger.error(f"Error updating exchange rates: {e}")
|
||||
|
@ -1,9 +1,9 @@
|
||||
"""
|
||||
Pull rendered copies of the templated
|
||||
Pull rendered copies of the templated
|
||||
only used for testing the js files! - This file is omited from coverage
|
||||
"""
|
||||
|
||||
from django.http import response
|
||||
from django.test import TestCase, testcases
|
||||
from django.test import TestCase
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
import os
|
||||
|
90
InvenTree/InvenTree/config.py
Normal file
90
InvenTree/InvenTree/config.py
Normal file
@ -0,0 +1,90 @@
|
||||
"""
|
||||
Helper functions for loading InvenTree configuration options
|
||||
"""
|
||||
|
||||
import os
|
||||
import shutil
|
||||
import logging
|
||||
|
||||
|
||||
logger = logging.getLogger('inventree')
|
||||
|
||||
|
||||
def get_base_dir():
|
||||
""" Returns the base (top-level) InvenTree directory """
|
||||
return os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
|
||||
|
||||
def get_config_file():
|
||||
"""
|
||||
Returns the path of the InvenTree configuration file.
|
||||
|
||||
Note: It will be created it if does not already exist!
|
||||
"""
|
||||
|
||||
base_dir = get_base_dir()
|
||||
|
||||
cfg_filename = os.getenv('INVENTREE_CONFIG_FILE')
|
||||
|
||||
if cfg_filename:
|
||||
cfg_filename = cfg_filename.strip()
|
||||
cfg_filename = os.path.abspath(cfg_filename)
|
||||
else:
|
||||
# Config file is *not* specified - use the default
|
||||
cfg_filename = os.path.join(base_dir, 'config.yaml')
|
||||
|
||||
if not os.path.exists(cfg_filename):
|
||||
print("InvenTree configuration file 'config.yaml' not found - creating default file")
|
||||
|
||||
cfg_template = os.path.join(base_dir, "config_template.yaml")
|
||||
shutil.copyfile(cfg_template, cfg_filename)
|
||||
print(f"Created config file {cfg_filename}")
|
||||
|
||||
return cfg_filename
|
||||
|
||||
|
||||
def get_plugin_file():
|
||||
"""
|
||||
Returns the path of the InvenTree plugins specification file.
|
||||
|
||||
Note: It will be created if it does not already exist!
|
||||
"""
|
||||
# Check if the plugin.txt file (specifying required plugins) is specified
|
||||
PLUGIN_FILE = os.getenv('INVENTREE_PLUGIN_FILE')
|
||||
|
||||
if not PLUGIN_FILE:
|
||||
# If not specified, look in the same directory as the configuration file
|
||||
|
||||
config_dir = os.path.dirname(get_config_file())
|
||||
|
||||
PLUGIN_FILE = os.path.join(config_dir, 'plugins.txt')
|
||||
|
||||
if not os.path.exists(PLUGIN_FILE):
|
||||
logger.warning("Plugin configuration file does not exist")
|
||||
logger.info(f"Creating plugin file at '{PLUGIN_FILE}'")
|
||||
|
||||
# If opening the file fails (no write permission, for example), then this will throw an error
|
||||
with open(PLUGIN_FILE, 'w') as plugin_file:
|
||||
plugin_file.write("# InvenTree Plugins (uses PIP framework to install)\n\n")
|
||||
|
||||
return PLUGIN_FILE
|
||||
|
||||
|
||||
def get_setting(environment_var, backup_val, default_value=None):
|
||||
"""
|
||||
Helper function for retrieving a configuration setting value
|
||||
|
||||
- First preference is to look for the environment variable
|
||||
- Second preference is to look for the value of the settings file
|
||||
- Third preference is the default value
|
||||
"""
|
||||
|
||||
val = os.getenv(environment_var)
|
||||
|
||||
if val is not None:
|
||||
return val
|
||||
|
||||
if backup_val is not None:
|
||||
return backup_val
|
||||
|
||||
return default_value
|
@ -23,7 +23,7 @@ def health_status(request):
|
||||
|
||||
if request.path.endswith('.js'):
|
||||
# Do not provide to script requests
|
||||
return {}
|
||||
return {} # pragma: no cover
|
||||
|
||||
if hasattr(request, '_inventree_health_status'):
|
||||
# Do not duplicate efforts
|
||||
|
@ -1,7 +1,12 @@
|
||||
import certifi
|
||||
import ssl
|
||||
from urllib.request import urlopen
|
||||
|
||||
from common.settings import currency_code_default, currency_codes
|
||||
from urllib.error import HTTPError, URLError
|
||||
from urllib.error import URLError
|
||||
|
||||
from djmoney.contrib.exchange.backends.base import SimpleExchangeBackend
|
||||
from django.db.utils import OperationalError
|
||||
|
||||
|
||||
class InvenTreeExchange(SimpleExchangeBackend):
|
||||
@ -23,6 +28,22 @@ class InvenTreeExchange(SimpleExchangeBackend):
|
||||
return {
|
||||
}
|
||||
|
||||
def get_response(self, **kwargs):
|
||||
"""
|
||||
Custom code to get response from server.
|
||||
Note: Adds a 5-second timeout
|
||||
"""
|
||||
|
||||
url = self.get_url(**kwargs)
|
||||
|
||||
try:
|
||||
context = ssl.create_default_context(cafile=certifi.where())
|
||||
response = urlopen(url, timeout=5, context=context)
|
||||
return response.read()
|
||||
except:
|
||||
# Returning None here will raise an error upstream
|
||||
return None
|
||||
|
||||
def update_rates(self, base_currency=currency_code_default()):
|
||||
|
||||
symbols = ','.join(currency_codes())
|
||||
@ -30,5 +51,14 @@ class InvenTreeExchange(SimpleExchangeBackend):
|
||||
try:
|
||||
super().update_rates(base=base_currency, symbols=symbols)
|
||||
# catch connection errors
|
||||
except (HTTPError, URLError):
|
||||
except URLError:
|
||||
print('Encountered connection error while updating')
|
||||
except OperationalError as e:
|
||||
if 'SerializationFailure' in e.__cause__.__class__.__name__:
|
||||
print('Serialization Failure while updating exchange rates')
|
||||
# We are just going to swallow this exception because the
|
||||
# exchange rates will be updated later by the scheduled task
|
||||
else:
|
||||
# Other operational errors probably are still show stoppers
|
||||
# so reraise them so that the log contains the stacktrace
|
||||
raise
|
||||
|
@ -53,7 +53,7 @@ class InvenTreeModelMoneyField(ModelMoneyField):
|
||||
"""
|
||||
Custom MoneyField for clean migrations while using dynamic currency settings
|
||||
"""
|
||||
|
||||
|
||||
def __init__(self, **kwargs):
|
||||
# detect if creating migration
|
||||
if 'migrate' in sys.argv or 'makemigrations' in sys.argv:
|
||||
|
@ -34,18 +34,47 @@ class InvenTreeOrderingFilter(OrderingFilter):
|
||||
Ordering fields should be mapped to separate fields
|
||||
"""
|
||||
|
||||
for idx, field in enumerate(ordering):
|
||||
ordering_initial = ordering
|
||||
ordering = []
|
||||
|
||||
reverse = False
|
||||
for field in ordering_initial:
|
||||
|
||||
if field.startswith('-'):
|
||||
reverse = field.startswith('-')
|
||||
|
||||
if reverse:
|
||||
field = field[1:]
|
||||
reverse = True
|
||||
|
||||
# Are aliases defined for this field?
|
||||
if field in aliases:
|
||||
ordering[idx] = aliases[field]
|
||||
alias = aliases[field]
|
||||
else:
|
||||
alias = field
|
||||
|
||||
"""
|
||||
Potentially, a single field could be "aliased" to multiple field,
|
||||
|
||||
(For example to enforce a particular ordering sequence)
|
||||
|
||||
e.g. to filter first by the integer value...
|
||||
|
||||
ordering_field_aliases = {
|
||||
"reference": ["integer_ref", "reference"]
|
||||
}
|
||||
|
||||
"""
|
||||
|
||||
if type(alias) is str:
|
||||
alias = [alias]
|
||||
elif type(alias) in [list, tuple]:
|
||||
pass
|
||||
else:
|
||||
# Unsupported alias type
|
||||
continue
|
||||
|
||||
for a in alias:
|
||||
if reverse:
|
||||
ordering[idx] = '-' + ordering[idx]
|
||||
a = '-' + a
|
||||
|
||||
ordering.append(a)
|
||||
|
||||
return ordering
|
||||
|
@ -4,16 +4,31 @@ Helper forms which subclass Django forms to provide additional functionality
|
||||
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
from urllib.parse import urlencode
|
||||
import logging
|
||||
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django import forms
|
||||
from django.contrib.auth.models import User
|
||||
from django.contrib.auth.models import User, Group
|
||||
from django.conf import settings
|
||||
from django.http import HttpResponseRedirect
|
||||
from django.urls import reverse
|
||||
|
||||
from crispy_forms.helper import FormHelper
|
||||
from crispy_forms.layout import Layout, Field
|
||||
from crispy_forms.bootstrap import PrependedText, AppendedText, PrependedAppendedText, StrictButton, Div
|
||||
|
||||
from allauth.account.forms import SignupForm, set_form_field_order
|
||||
from allauth.account.adapter import DefaultAccountAdapter
|
||||
from allauth.socialaccount.adapter import DefaultSocialAccountAdapter
|
||||
from allauth.exceptions import ImmediateHttpResponse
|
||||
from allauth_2fa.adapter import OTPAdapter
|
||||
from allauth_2fa.utils import user_has_valid_totp_device
|
||||
|
||||
from part.models import PartCategory
|
||||
from common.models import InvenTreeSetting
|
||||
|
||||
logger = logging.getLogger('inventree')
|
||||
|
||||
|
||||
class HelperForm(forms.ModelForm):
|
||||
@ -144,7 +159,6 @@ class EditUserForm(HelperForm):
|
||||
'username',
|
||||
'first_name',
|
||||
'last_name',
|
||||
'email'
|
||||
]
|
||||
|
||||
|
||||
@ -204,3 +218,112 @@ class SettingCategorySelectForm(forms.ModelForm):
|
||||
css_class='row',
|
||||
),
|
||||
)
|
||||
|
||||
|
||||
# override allauth
|
||||
class CustomSignupForm(SignupForm):
|
||||
"""
|
||||
Override to use dynamic settings
|
||||
"""
|
||||
def __init__(self, *args, **kwargs):
|
||||
kwargs['email_required'] = InvenTreeSetting.get_setting('LOGIN_MAIL_REQUIRED')
|
||||
|
||||
super().__init__(*args, **kwargs)
|
||||
|
||||
# check for two mail fields
|
||||
if InvenTreeSetting.get_setting('LOGIN_SIGNUP_MAIL_TWICE'):
|
||||
self.fields["email2"] = forms.EmailField(
|
||||
label=_("Email (again)"),
|
||||
widget=forms.TextInput(
|
||||
attrs={
|
||||
"type": "email",
|
||||
"placeholder": _("Email address confirmation"),
|
||||
}
|
||||
),
|
||||
)
|
||||
|
||||
# check for two password fields
|
||||
if not InvenTreeSetting.get_setting('LOGIN_SIGNUP_PWD_TWICE'):
|
||||
self.fields.pop("password2")
|
||||
|
||||
# reorder fields
|
||||
set_form_field_order(self, ["username", "email", "email2", "password1", "password2", ])
|
||||
|
||||
def clean(self):
|
||||
cleaned_data = super().clean()
|
||||
|
||||
# check for two mail fields
|
||||
if InvenTreeSetting.get_setting('LOGIN_SIGNUP_MAIL_TWICE'):
|
||||
email = cleaned_data.get("email")
|
||||
email2 = cleaned_data.get("email2")
|
||||
if (email and email2) and email != email2:
|
||||
self.add_error("email2", _("You must type the same email each time."))
|
||||
|
||||
return cleaned_data
|
||||
|
||||
|
||||
class RegistratonMixin:
|
||||
"""
|
||||
Mixin to check if registration should be enabled
|
||||
"""
|
||||
def is_open_for_signup(self, request, *args, **kwargs):
|
||||
if settings.EMAIL_HOST and InvenTreeSetting.get_setting('LOGIN_ENABLE_REG', True):
|
||||
return super().is_open_for_signup(request, *args, **kwargs)
|
||||
return False
|
||||
|
||||
def save_user(self, request, user, form, commit=True):
|
||||
user = super().save_user(request, user, form)
|
||||
start_group = InvenTreeSetting.get_setting('SIGNUP_GROUP')
|
||||
if start_group:
|
||||
try:
|
||||
group = Group.objects.get(id=start_group)
|
||||
user.groups.add(group)
|
||||
except Group.DoesNotExist:
|
||||
logger.error('The setting `SIGNUP_GROUP` contains an non existant group', start_group)
|
||||
user.save()
|
||||
return user
|
||||
|
||||
|
||||
class CustomAccountAdapter(RegistratonMixin, OTPAdapter, DefaultAccountAdapter):
|
||||
"""
|
||||
Override of adapter to use dynamic settings
|
||||
"""
|
||||
def send_mail(self, template_prefix, email, context):
|
||||
"""only send mail if backend configured"""
|
||||
if settings.EMAIL_HOST:
|
||||
return super().send_mail(template_prefix, email, context)
|
||||
return False
|
||||
|
||||
|
||||
class CustomSocialAccountAdapter(RegistratonMixin, DefaultSocialAccountAdapter):
|
||||
"""
|
||||
Override of adapter to use dynamic settings
|
||||
"""
|
||||
def is_auto_signup_allowed(self, request, sociallogin):
|
||||
if InvenTreeSetting.get_setting('LOGIN_SIGNUP_SSO_AUTO', True):
|
||||
return super().is_auto_signup_allowed(request, sociallogin)
|
||||
return False
|
||||
|
||||
# from OTPAdapter
|
||||
def has_2fa_enabled(self, user):
|
||||
"""Returns True if the user has 2FA configured."""
|
||||
return user_has_valid_totp_device(user)
|
||||
|
||||
def login(self, request, user):
|
||||
# Require two-factor authentication if it has been configured.
|
||||
if self.has_2fa_enabled(user):
|
||||
# Cast to string for the case when this is not a JSON serializable
|
||||
# object, e.g. a UUID.
|
||||
request.session['allauth_2fa_user_id'] = str(user.id)
|
||||
|
||||
redirect_url = reverse('two-factor-authenticate')
|
||||
# Add GET parameters to the URL if they exist.
|
||||
if request.GET:
|
||||
redirect_url += u'?' + urlencode(request.GET)
|
||||
|
||||
raise ImmediateHttpResponse(
|
||||
response=HttpResponseRedirect(redirect_url)
|
||||
)
|
||||
|
||||
# Otherwise defer to the original allauth adapter.
|
||||
return super().login(request, user)
|
||||
|
@ -69,6 +69,35 @@ def getStaticUrl(filename):
|
||||
return os.path.join(STATIC_URL, str(filename))
|
||||
|
||||
|
||||
def construct_absolute_url(*arg):
|
||||
"""
|
||||
Construct (or attempt to construct) an absolute URL from a relative URL.
|
||||
|
||||
This is useful when (for example) sending an email to a user with a link
|
||||
to something in the InvenTree web framework.
|
||||
|
||||
This requires the BASE_URL configuration option to be set!
|
||||
"""
|
||||
|
||||
base = str(InvenTreeSetting.get_setting('INVENTREE_BASE_URL'))
|
||||
|
||||
url = '/'.join(arg)
|
||||
|
||||
if not base:
|
||||
return url
|
||||
|
||||
# Strip trailing slash from base url
|
||||
if base.endswith('/'):
|
||||
base = base[:-1]
|
||||
|
||||
if url.startswith('/'):
|
||||
url = url[1:]
|
||||
|
||||
url = f"{base}/{url}"
|
||||
|
||||
return url
|
||||
|
||||
|
||||
def getBlankImage():
|
||||
"""
|
||||
Return the qualified path for the 'blank image' placeholder.
|
||||
@ -286,7 +315,7 @@ def WrapWithQuotes(text, quote='"'):
|
||||
return text
|
||||
|
||||
|
||||
def MakeBarcode(object_name, object_pk, object_data={}, **kwargs):
|
||||
def MakeBarcode(object_name, object_pk, object_data=None, **kwargs):
|
||||
""" Generate a string for a barcode. Adds some global InvenTree parameters.
|
||||
|
||||
Args:
|
||||
@ -298,6 +327,8 @@ def MakeBarcode(object_name, object_pk, object_data={}, **kwargs):
|
||||
Returns:
|
||||
json string of the supplied data plus some other data
|
||||
"""
|
||||
if object_data is None:
|
||||
object_data = {}
|
||||
|
||||
url = kwargs.get('url', False)
|
||||
brief = kwargs.get('brief', True)
|
||||
@ -375,21 +406,28 @@ def DownloadFile(data, filename, content_type='application/text', inline=False):
|
||||
return response
|
||||
|
||||
|
||||
def extract_serial_numbers(serials, expected_quantity):
|
||||
def extract_serial_numbers(serials, expected_quantity, next_number: int):
|
||||
""" Attempt to extract serial numbers from an input string.
|
||||
- Serial numbers must be integer values
|
||||
- Serial numbers must be positive
|
||||
- Serial numbers can be split by whitespace / newline / commma chars
|
||||
- Serial numbers can be supplied as an inclusive range using hyphen char e.g. 10-20
|
||||
- Serial numbers can be defined as ~ for getting the next available serial number
|
||||
- Serial numbers can be supplied as <start>+ for getting all expecteded numbers starting from <start>
|
||||
- Serial numbers can be supplied as <start>+<length> for getting <length> numbers starting from <start>
|
||||
|
||||
Args:
|
||||
serials: input string with patterns
|
||||
expected_quantity: The number of (unique) serial numbers we expect
|
||||
next_number(int): the next possible serial number
|
||||
"""
|
||||
|
||||
serials = serials.strip()
|
||||
|
||||
# fill in the next serial number into the serial
|
||||
if '~' in serials:
|
||||
serials = serials.replace('~', str(next_number))
|
||||
|
||||
groups = re.split("[\s,]+", serials)
|
||||
|
||||
numbers = []
|
||||
@ -437,7 +475,6 @@ def extract_serial_numbers(serials, expected_quantity):
|
||||
continue
|
||||
else:
|
||||
errors.append(_("Invalid group: {g}").format(g=group))
|
||||
continue
|
||||
|
||||
# plus signals either
|
||||
# 1: 'start+': expected number of serials, starting at start
|
||||
@ -462,13 +499,21 @@ def extract_serial_numbers(serials, expected_quantity):
|
||||
# no case
|
||||
else:
|
||||
errors.append(_("Invalid group: {g}").format(g=group))
|
||||
continue
|
||||
|
||||
# Group should be a number
|
||||
elif group:
|
||||
# try conversion
|
||||
try:
|
||||
number = int(group)
|
||||
except:
|
||||
# seem like it is not a number
|
||||
raise ValidationError(_(f"Invalid group {group}"))
|
||||
|
||||
number_add(number)
|
||||
|
||||
# No valid input group detected
|
||||
else:
|
||||
if group in numbers:
|
||||
errors.append(_("Duplicate serial: {g}".format(g=group)))
|
||||
else:
|
||||
numbers.append(group)
|
||||
raise ValidationError(_(f"Invalid/no group {group}"))
|
||||
|
||||
if len(errors) > 0:
|
||||
raise ValidationError(errors)
|
||||
@ -667,3 +712,18 @@ def clean_decimal(number):
|
||||
return Decimal(0)
|
||||
|
||||
return clean_number.quantize(Decimal(1)) if clean_number == clean_number.to_integral() else clean_number.normalize()
|
||||
|
||||
|
||||
def inheritors(cls):
|
||||
"""
|
||||
Return all classes that are subclasses from the supplied cls
|
||||
"""
|
||||
subcls = set()
|
||||
work = [cls]
|
||||
while work:
|
||||
parent = work.pop()
|
||||
for child in parent.__subclasses__():
|
||||
if child not in subcls:
|
||||
subcls.add(child)
|
||||
work.append(child)
|
||||
return subcls
|
||||
|
@ -1 +0,0 @@
|
||||
{"de": 95, "el": 0, "en": 0, "es": 4, "fr": 6, "he": 0, "id": 0, "it": 0, "ja": 4, "ko": 0, "nl": 0, "no": 0, "pl": 27, "ru": 6, "sv": 0, "th": 0, "tr": 32, "vi": 0, "zh": 1}
|
@ -2,9 +2,14 @@
|
||||
Custom management command to cleanup old settings that are not defined anymore
|
||||
"""
|
||||
|
||||
import logging
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
|
||||
|
||||
logger = logging.getLogger('inventree')
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""
|
||||
Cleanup old (undefined) settings in the database
|
||||
@ -12,27 +17,27 @@ class Command(BaseCommand):
|
||||
|
||||
def handle(self, *args, **kwargs):
|
||||
|
||||
print("Collecting settings")
|
||||
logger.info("Collecting settings")
|
||||
from common.models import InvenTreeSetting, InvenTreeUserSetting
|
||||
|
||||
# general settings
|
||||
db_settings = InvenTreeSetting.objects.all()
|
||||
model_settings = InvenTreeSetting.GLOBAL_SETTINGS
|
||||
model_settings = InvenTreeSetting.SETTINGS
|
||||
|
||||
# check if key exist and delete if not
|
||||
for setting in db_settings:
|
||||
if setting.key not in model_settings:
|
||||
setting.delete()
|
||||
print(f"deleted setting '{setting.key}'")
|
||||
logger.info(f"deleted setting '{setting.key}'")
|
||||
|
||||
# user settings
|
||||
db_settings = InvenTreeUserSetting.objects.all()
|
||||
model_settings = InvenTreeUserSetting.GLOBAL_SETTINGS
|
||||
model_settings = InvenTreeUserSetting.SETTINGS
|
||||
|
||||
# check if key exist and delete if not
|
||||
for setting in db_settings:
|
||||
if setting.key not in model_settings:
|
||||
setting.delete()
|
||||
print(f"deleted user setting '{setting.key}'")
|
||||
logger.info(f"deleted user setting '{setting.key}'")
|
||||
|
||||
print("checked all settings")
|
||||
logger.info("checked all settings")
|
||||
|
@ -0,0 +1,70 @@
|
||||
"""
|
||||
Custom management command to rebuild thumbnail images
|
||||
|
||||
- May be required after importing a new dataset, for example
|
||||
"""
|
||||
|
||||
import os
|
||||
import logging
|
||||
|
||||
from PIL import UnidentifiedImageError
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.conf import settings
|
||||
from django.db.utils import OperationalError, ProgrammingError
|
||||
|
||||
from company.models import Company
|
||||
from part.models import Part
|
||||
|
||||
|
||||
logger = logging.getLogger('inventree')
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""
|
||||
Rebuild all thumbnail images
|
||||
"""
|
||||
|
||||
def rebuild_thumbnail(self, model):
|
||||
"""
|
||||
Rebuild the thumbnail specified by the "image" field of the provided model
|
||||
"""
|
||||
|
||||
if not model.image:
|
||||
return
|
||||
|
||||
img = model.image
|
||||
url = img.thumbnail.name
|
||||
loc = os.path.join(settings.MEDIA_ROOT, url)
|
||||
|
||||
if not os.path.exists(loc):
|
||||
logger.info(f"Generating thumbnail image for '{img}'")
|
||||
|
||||
try:
|
||||
model.image.render_variations(replace=False)
|
||||
except FileNotFoundError:
|
||||
logger.error(f"ERROR: Image file '{img}' is missing")
|
||||
except UnidentifiedImageError:
|
||||
logger.error(f"ERROR: Image file '{img}' is not a valid image")
|
||||
|
||||
def handle(self, *args, **kwargs):
|
||||
|
||||
logger.setLevel(logging.INFO)
|
||||
|
||||
logger.info("Rebuilding Part thumbnails")
|
||||
|
||||
for part in Part.objects.exclude(image=None):
|
||||
try:
|
||||
self.rebuild_thumbnail(part)
|
||||
except (OperationalError, ProgrammingError):
|
||||
logger.error("ERROR: Database read error.")
|
||||
break
|
||||
|
||||
logger.info("Rebuilding Company thumbnails")
|
||||
|
||||
for company in Company.objects.exclude(image=None):
|
||||
try:
|
||||
self.rebuild_thumbnail(company)
|
||||
except (OperationalError, ProgrammingError):
|
||||
logger.error("ERROR: abase read error.")
|
||||
break
|
36
InvenTree/InvenTree/management/commands/remove_mfa.py
Normal file
36
InvenTree/InvenTree/management/commands/remove_mfa.py
Normal file
@ -0,0 +1,36 @@
|
||||
"""
|
||||
Custom management command to remove MFA for a user
|
||||
"""
|
||||
|
||||
from django.core.management.base import BaseCommand
|
||||
from django.contrib.auth import get_user_model
|
||||
|
||||
|
||||
class Command(BaseCommand):
|
||||
"""
|
||||
Remove MFA for a user
|
||||
"""
|
||||
|
||||
def add_arguments(self, parser):
|
||||
parser.add_argument('mail', type=str)
|
||||
|
||||
def handle(self, *args, **kwargs):
|
||||
|
||||
# general settings
|
||||
mail = kwargs.get('mail')
|
||||
if not mail:
|
||||
raise KeyError('A mail is required')
|
||||
user = get_user_model()
|
||||
mfa_user = [*set(user.objects.filter(email=mail) | user.objects.filter(emailaddress__email=mail))]
|
||||
|
||||
if len(mfa_user) == 0:
|
||||
print('No user with this mail associated')
|
||||
elif len(mfa_user) > 1:
|
||||
print('More than one user found with this mail')
|
||||
else:
|
||||
# and clean out all MFA methods
|
||||
# backup codes
|
||||
mfa_user[0].staticdevice_set.all().delete()
|
||||
# TOTP tokens
|
||||
mfa_user[0].totpdevice_set.all().delete()
|
||||
print(f'Removed all MFA methods for user {str(mfa_user[0])}')
|
@ -31,7 +31,7 @@ class InvenTreeMetadata(SimpleMetadata):
|
||||
"""
|
||||
|
||||
def determine_metadata(self, request, view):
|
||||
|
||||
|
||||
self.request = request
|
||||
self.view = view
|
||||
|
||||
@ -72,7 +72,10 @@ class InvenTreeMetadata(SimpleMetadata):
|
||||
|
||||
# Remove any HTTP methods that the user does not have permission for
|
||||
for method, permission in rolemap.items():
|
||||
if method in actions and not check(user, table, permission):
|
||||
|
||||
result = check(user, table, permission)
|
||||
|
||||
if method in actions and not result:
|
||||
del actions[method]
|
||||
|
||||
# Add a 'DELETE' action if we are allowed to delete
|
||||
@ -105,20 +108,41 @@ class InvenTreeMetadata(SimpleMetadata):
|
||||
|
||||
model_fields = model_meta.get_field_info(model_class)
|
||||
|
||||
model_default_func = getattr(model_class, 'api_defaults', None)
|
||||
|
||||
if model_default_func:
|
||||
model_default_values = model_class.api_defaults(self.request)
|
||||
else:
|
||||
model_default_values = {}
|
||||
|
||||
# Iterate through simple fields
|
||||
for name, field in model_fields.fields.items():
|
||||
|
||||
if field.has_default() and name in serializer_info.keys():
|
||||
if name in serializer_info.keys():
|
||||
|
||||
default = field.default
|
||||
if field.has_default():
|
||||
|
||||
if callable(default):
|
||||
try:
|
||||
default = default()
|
||||
except:
|
||||
continue
|
||||
default = field.default
|
||||
|
||||
serializer_info[name]['default'] = default
|
||||
if callable(default):
|
||||
try:
|
||||
default = default()
|
||||
except:
|
||||
continue
|
||||
|
||||
serializer_info[name]['default'] = default
|
||||
|
||||
elif name in model_default_values:
|
||||
serializer_info[name]['default'] = model_default_values[name]
|
||||
|
||||
# Attributes to copy from the model to the field (if they don't exist)
|
||||
attributes = ['help_text']
|
||||
|
||||
for attr in attributes:
|
||||
if attr not in serializer_info[name]:
|
||||
|
||||
if hasattr(field, attr):
|
||||
serializer_info[name][attr] = getattr(field, attr)
|
||||
|
||||
# Iterate through relations
|
||||
for name, relation in model_fields.relations.items():
|
||||
@ -138,6 +162,9 @@ class InvenTreeMetadata(SimpleMetadata):
|
||||
if 'help_text' not in serializer_info[name] and hasattr(relation.model_field, 'help_text'):
|
||||
serializer_info[name]['help_text'] = relation.model_field.help_text
|
||||
|
||||
if name in model_default_values:
|
||||
serializer_info[name]['default'] = model_default_values[name]
|
||||
|
||||
except AttributeError:
|
||||
pass
|
||||
|
||||
@ -147,7 +174,7 @@ class InvenTreeMetadata(SimpleMetadata):
|
||||
# Extract extra information if an instance is available
|
||||
if hasattr(serializer, 'instance'):
|
||||
instance = serializer.instance
|
||||
|
||||
|
||||
if instance is None and model_class is not None:
|
||||
# Attempt to find the instance based on kwargs lookup
|
||||
kwargs = getattr(self.view, 'kwargs', None)
|
||||
@ -213,7 +240,7 @@ class InvenTreeMetadata(SimpleMetadata):
|
||||
|
||||
# Introspect writable related fields
|
||||
if field_info['type'] == 'field' and not field_info['read_only']:
|
||||
|
||||
|
||||
# If the field is a PrimaryKeyRelatedField, we can extract the model from the queryset
|
||||
if isinstance(field, serializers.PrimaryKeyRelatedField):
|
||||
model = field.queryset.model
|
||||
|
@ -1,12 +1,18 @@
|
||||
from django.shortcuts import HttpResponseRedirect
|
||||
from django.urls import reverse_lazy
|
||||
from django.urls import reverse_lazy, Resolver404
|
||||
from django.db import connection
|
||||
from django.shortcuts import redirect
|
||||
from django.conf.urls import include, url
|
||||
import logging
|
||||
import time
|
||||
import operator
|
||||
|
||||
from rest_framework.authtoken.models import Token
|
||||
from allauth_2fa.middleware import BaseRequire2FAMiddleware, AllauthTwoFactorMiddleware
|
||||
|
||||
from InvenTree.urls import frontendpatterns
|
||||
from common.models import InvenTreeSetting
|
||||
|
||||
|
||||
logger = logging.getLogger("inventree")
|
||||
|
||||
@ -59,20 +65,19 @@ class AuthRequiredMiddleware(object):
|
||||
|
||||
except Token.DoesNotExist:
|
||||
logger.warning(f"Access denied for unknown token {token_key}")
|
||||
pass
|
||||
|
||||
# No authorization was found for the request
|
||||
if not authorized:
|
||||
# A logout request will redirect the user to the login screen
|
||||
if request.path_info == reverse_lazy('logout'):
|
||||
return HttpResponseRedirect(reverse_lazy('login'))
|
||||
if request.path_info == reverse_lazy('account_logout'):
|
||||
return HttpResponseRedirect(reverse_lazy('account_login'))
|
||||
|
||||
path = request.path_info
|
||||
|
||||
# List of URL endpoints we *do not* want to redirect to
|
||||
urls = [
|
||||
reverse_lazy('login'),
|
||||
reverse_lazy('logout'),
|
||||
reverse_lazy('account_login'),
|
||||
reverse_lazy('account_logout'),
|
||||
reverse_lazy('admin:login'),
|
||||
reverse_lazy('admin:logout'),
|
||||
]
|
||||
@ -80,7 +85,7 @@ class AuthRequiredMiddleware(object):
|
||||
if path not in urls and not path.startswith('/api/'):
|
||||
# Save the 'next' parameter to pass through to the login view
|
||||
|
||||
return redirect('%s?next=%s' % (reverse_lazy('login'), request.path))
|
||||
return redirect('%s?next=%s' % (reverse_lazy('account_login'), request.path))
|
||||
|
||||
response = self.get_response(request)
|
||||
|
||||
@ -146,3 +151,28 @@ class QueryCountMiddleware(object):
|
||||
print(x[0], ':', x[1])
|
||||
|
||||
return response
|
||||
|
||||
|
||||
url_matcher = url('', include(frontendpatterns))
|
||||
|
||||
|
||||
class Check2FAMiddleware(BaseRequire2FAMiddleware):
|
||||
"""check if user is required to have MFA enabled"""
|
||||
def require_2fa(self, request):
|
||||
# Superusers are require to have 2FA.
|
||||
try:
|
||||
if url_matcher.resolve(request.path[1:]):
|
||||
return InvenTreeSetting.get_setting('LOGIN_ENFORCE_MFA')
|
||||
except Resolver404:
|
||||
pass
|
||||
return False
|
||||
|
||||
|
||||
class CustomAllauthTwoFactorMiddleware(AllauthTwoFactorMiddleware):
|
||||
"""This function ensures only frontend code triggers the MFA auth cycle"""
|
||||
def process_request(self, request):
|
||||
try:
|
||||
if not url_matcher.resolve(request.path[1:]):
|
||||
super().process_request(request)
|
||||
except Resolver404:
|
||||
pass
|
||||
|
@ -4,6 +4,7 @@ Generic models which provide extra functionality over base Django model types.
|
||||
|
||||
from __future__ import unicode_literals
|
||||
|
||||
import re
|
||||
import os
|
||||
import logging
|
||||
|
||||
@ -20,7 +21,8 @@ from django.dispatch import receiver
|
||||
from mptt.models import MPTTModel, TreeForeignKey
|
||||
from mptt.exceptions import InvalidMove
|
||||
|
||||
from .validators import validate_tree_name
|
||||
from InvenTree.fields import InvenTreeURLField
|
||||
from InvenTree.validators import validate_tree_name
|
||||
|
||||
|
||||
logger = logging.getLogger('inventree')
|
||||
@ -43,15 +45,122 @@ def rename_attachment(instance, filename):
|
||||
return os.path.join(instance.getSubdir(), filename)
|
||||
|
||||
|
||||
class DataImportMixin(object):
|
||||
"""
|
||||
Model mixin class which provides support for 'data import' functionality.
|
||||
|
||||
Models which implement this mixin should provide information on the fields available for import
|
||||
"""
|
||||
|
||||
# Define a map of fields avaialble for import
|
||||
IMPORT_FIELDS = {}
|
||||
|
||||
@classmethod
|
||||
def get_import_fields(cls):
|
||||
"""
|
||||
Return all available import fields
|
||||
|
||||
Where information on a particular field is not explicitly provided,
|
||||
introspect the base model to (attempt to) find that information.
|
||||
|
||||
"""
|
||||
fields = cls.IMPORT_FIELDS
|
||||
|
||||
for name, field in fields.items():
|
||||
|
||||
# Attempt to extract base field information from the model
|
||||
base_field = None
|
||||
|
||||
for f in cls._meta.fields:
|
||||
if f.name == name:
|
||||
base_field = f
|
||||
break
|
||||
|
||||
if base_field:
|
||||
if 'label' not in field:
|
||||
field['label'] = base_field.verbose_name
|
||||
|
||||
if 'help_text' not in field:
|
||||
field['help_text'] = base_field.help_text
|
||||
|
||||
fields[name] = field
|
||||
|
||||
return fields
|
||||
|
||||
@classmethod
|
||||
def get_required_import_fields(cls):
|
||||
""" Return all *required* import fields """
|
||||
fields = {}
|
||||
|
||||
for name, field in cls.get_import_fields().items():
|
||||
required = field.get('required', False)
|
||||
|
||||
if required:
|
||||
fields[name] = field
|
||||
|
||||
return fields
|
||||
|
||||
|
||||
class ReferenceIndexingMixin(models.Model):
|
||||
"""
|
||||
A mixin for keeping track of numerical copies of the "reference" field.
|
||||
|
||||
!!DANGER!! always add `ReferenceIndexingSerializerMixin`to all your models serializers to
|
||||
ensure the reference field is not too big
|
||||
|
||||
Here, we attempt to convert a "reference" field value (char) to an integer,
|
||||
for performing fast natural sorting.
|
||||
|
||||
This requires extra database space (due to the extra table column),
|
||||
but is required as not all supported database backends provide equivalent casting.
|
||||
|
||||
This mixin adds a field named 'reference_int'.
|
||||
|
||||
- If the 'reference' field can be cast to an integer, it is stored here
|
||||
- If the 'reference' field *starts* with an integer, it is stored here
|
||||
- Otherwise, we store zero
|
||||
"""
|
||||
|
||||
class Meta:
|
||||
abstract = True
|
||||
|
||||
def rebuild_reference_field(self):
|
||||
|
||||
reference = getattr(self, 'reference', '')
|
||||
|
||||
self.reference_int = extract_int(reference)
|
||||
|
||||
reference_int = models.BigIntegerField(default=0)
|
||||
|
||||
|
||||
def extract_int(reference):
|
||||
# Default value if we cannot convert to an integer
|
||||
ref_int = 0
|
||||
|
||||
# Look at the start of the string - can it be "integerized"?
|
||||
result = re.match(r"^(\d+)", reference)
|
||||
|
||||
if result and len(result.groups()) == 1:
|
||||
ref = result.groups()[0]
|
||||
try:
|
||||
ref_int = int(ref)
|
||||
except:
|
||||
ref_int = 0
|
||||
return ref_int
|
||||
|
||||
|
||||
class InvenTreeAttachment(models.Model):
|
||||
""" Provides an abstracted class for managing file attachments.
|
||||
|
||||
An attachment can be either an uploaded file, or an external URL
|
||||
|
||||
Attributes:
|
||||
attachment: File
|
||||
comment: String descriptor for the attachment
|
||||
user: User associated with file upload
|
||||
upload_date: Date the file was uploaded
|
||||
"""
|
||||
|
||||
def getSubdir(self):
|
||||
"""
|
||||
Return the subdirectory under which attachments should be stored.
|
||||
@ -60,11 +169,32 @@ class InvenTreeAttachment(models.Model):
|
||||
|
||||
return "attachments"
|
||||
|
||||
def save(self, *args, **kwargs):
|
||||
# Either 'attachment' or 'link' must be specified!
|
||||
if not self.attachment and not self.link:
|
||||
raise ValidationError({
|
||||
'attachment': _('Missing file'),
|
||||
'link': _('Missing external link'),
|
||||
})
|
||||
|
||||
super().save(*args, **kwargs)
|
||||
|
||||
def __str__(self):
|
||||
return os.path.basename(self.attachment.name)
|
||||
if self.attachment is not None:
|
||||
return os.path.basename(self.attachment.name)
|
||||
else:
|
||||
return str(self.link)
|
||||
|
||||
attachment = models.FileField(upload_to=rename_attachment, verbose_name=_('Attachment'),
|
||||
help_text=_('Select file to attach'))
|
||||
help_text=_('Select file to attach'),
|
||||
blank=True, null=True
|
||||
)
|
||||
|
||||
link = InvenTreeURLField(
|
||||
blank=True, null=True,
|
||||
verbose_name=_('Link'),
|
||||
help_text=_('Link to external URL')
|
||||
)
|
||||
|
||||
comment = models.CharField(blank=True, max_length=100, verbose_name=_('Comment'), help_text=_('File comment'))
|
||||
|
||||
@ -80,7 +210,10 @@ class InvenTreeAttachment(models.Model):
|
||||
|
||||
@property
|
||||
def basename(self):
|
||||
return os.path.basename(self.attachment.name)
|
||||
if self.attachment:
|
||||
return os.path.basename(self.attachment.name)
|
||||
else:
|
||||
return None
|
||||
|
||||
@basename.setter
|
||||
def basename(self, fn):
|
||||
|
@ -1,43 +0,0 @@
|
||||
# -*- coding: utf-8 -*-
|
||||
|
||||
import inspect
|
||||
import importlib
|
||||
import pkgutil
|
||||
|
||||
|
||||
def iter_namespace(pkg):
|
||||
|
||||
return pkgutil.iter_modules(pkg.__path__, pkg.__name__ + ".")
|
||||
|
||||
|
||||
def get_modules(pkg):
|
||||
# Return all modules in a given package
|
||||
return [importlib.import_module(name) for finder, name, ispkg in iter_namespace(pkg)]
|
||||
|
||||
|
||||
def get_classes(module):
|
||||
# Return all classes in a given module
|
||||
return inspect.getmembers(module, inspect.isclass)
|
||||
|
||||
|
||||
def get_plugins(pkg, baseclass):
|
||||
"""
|
||||
Return a list of all modules under a given package.
|
||||
|
||||
- Modules must be a subclass of the provided 'baseclass'
|
||||
- Modules must have a non-empty PLUGIN_NAME parameter
|
||||
"""
|
||||
|
||||
plugins = []
|
||||
|
||||
modules = get_modules(pkg)
|
||||
|
||||
# Iterate through each module in the package
|
||||
for mod in modules:
|
||||
# Iterate through each class in the module
|
||||
for item in get_classes(mod):
|
||||
plugin = item[1]
|
||||
if issubclass(plugin, baseclass) and plugin.PLUGIN_NAME:
|
||||
plugins.append(plugin)
|
||||
|
||||
return plugins
|
@ -6,10 +6,16 @@ def isInTestMode():
|
||||
Returns True if the database is in testing mode
|
||||
"""
|
||||
|
||||
if 'test' in sys.argv:
|
||||
return True
|
||||
return 'test' in sys.argv
|
||||
|
||||
return False
|
||||
|
||||
def isImportingData():
|
||||
"""
|
||||
Returns True if the database is currently importing data,
|
||||
e.g. 'loaddata' command is performed
|
||||
"""
|
||||
|
||||
return 'loaddata' in sys.argv
|
||||
|
||||
|
||||
def canAppAccessDatabase(allow_test=False):
|
||||
|
@ -5,8 +5,8 @@ Serializers used in various InvenTree apps
|
||||
# -*- coding: utf-8 -*-
|
||||
from __future__ import unicode_literals
|
||||
|
||||
|
||||
import os
|
||||
import tablib
|
||||
|
||||
from decimal import Decimal
|
||||
|
||||
@ -16,6 +16,7 @@ from django.conf import settings
|
||||
from django.contrib.auth.models import User
|
||||
from django.core.exceptions import ValidationError as DjangoValidationError
|
||||
from django.utils.translation import ugettext_lazy as _
|
||||
from django.db import models
|
||||
|
||||
from djmoney.contrib.django_rest_framework.fields import MoneyField
|
||||
from djmoney.money import Money
|
||||
@ -27,6 +28,8 @@ from rest_framework.fields import empty
|
||||
from rest_framework.exceptions import ValidationError
|
||||
from rest_framework.serializers import DecimalField
|
||||
|
||||
from .models import extract_int
|
||||
|
||||
|
||||
class InvenTreeMoneySerializer(MoneyField):
|
||||
"""
|
||||
@ -66,7 +69,7 @@ class InvenTreeMoneySerializer(MoneyField):
|
||||
|
||||
if currency and amount is not None and not isinstance(amount, MONEY_CLASSES) and amount is not empty:
|
||||
return Money(amount, currency)
|
||||
|
||||
|
||||
return amount
|
||||
|
||||
|
||||
@ -239,20 +242,15 @@ class InvenTreeModelSerializer(serializers.ModelSerializer):
|
||||
return data
|
||||
|
||||
|
||||
class InvenTreeAttachmentSerializer(InvenTreeModelSerializer):
|
||||
class ReferenceIndexingSerializerMixin():
|
||||
"""
|
||||
Special case of an InvenTreeModelSerializer, which handles an "attachment" model.
|
||||
|
||||
The only real addition here is that we support "renaming" of the attachment file.
|
||||
This serializer mixin ensures the the reference is not to big / small
|
||||
for the BigIntegerField
|
||||
"""
|
||||
|
||||
# The 'filename' field must be present in the serializer
|
||||
filename = serializers.CharField(
|
||||
label=_('Filename'),
|
||||
required=False,
|
||||
source='basename',
|
||||
allow_blank=False,
|
||||
)
|
||||
def validate_reference(self, value):
|
||||
if extract_int(value) > models.BigIntegerField.MAX_BIGINT:
|
||||
raise serializers.ValidationError('reference is to to big')
|
||||
return value
|
||||
|
||||
|
||||
class InvenTreeAttachmentSerializerField(serializers.FileField):
|
||||
@ -284,6 +282,27 @@ class InvenTreeAttachmentSerializerField(serializers.FileField):
|
||||
return os.path.join(str(settings.MEDIA_URL), str(value))
|
||||
|
||||
|
||||
class InvenTreeAttachmentSerializer(InvenTreeModelSerializer):
|
||||
"""
|
||||
Special case of an InvenTreeModelSerializer, which handles an "attachment" model.
|
||||
|
||||
The only real addition here is that we support "renaming" of the attachment file.
|
||||
"""
|
||||
|
||||
attachment = InvenTreeAttachmentSerializerField(
|
||||
required=False,
|
||||
allow_null=False,
|
||||
)
|
||||
|
||||
# The 'filename' field must be present in the serializer
|
||||
filename = serializers.CharField(
|
||||
label=_('Filename'),
|
||||
required=False,
|
||||
source='basename',
|
||||
allow_blank=False,
|
||||
)
|
||||
|
||||
|
||||
class InvenTreeImageSerializerField(serializers.ImageField):
|
||||
"""
|
||||
Custom image serializer.
|
||||
@ -296,3 +315,326 @@ class InvenTreeImageSerializerField(serializers.ImageField):
|
||||
return None
|
||||
|
||||
return os.path.join(str(settings.MEDIA_URL), str(value))
|
||||
|
||||
|
||||
class InvenTreeDecimalField(serializers.FloatField):
|
||||
"""
|
||||
Custom serializer for decimal fields. Solves the following issues:
|
||||
|
||||
- The normal DRF DecimalField renders values with trailing zeros
|
||||
- Using a FloatField can result in rounding issues: https://code.djangoproject.com/ticket/30290
|
||||
"""
|
||||
|
||||
def to_internal_value(self, data):
|
||||
|
||||
# Convert the value to a string, and then a decimal
|
||||
try:
|
||||
return Decimal(str(data))
|
||||
except:
|
||||
raise serializers.ValidationError(_("Invalid value"))
|
||||
|
||||
|
||||
class DataFileUploadSerializer(serializers.Serializer):
|
||||
"""
|
||||
Generic serializer for uploading a data file, and extracting a dataset.
|
||||
|
||||
- Validates uploaded file
|
||||
- Extracts column names
|
||||
- Extracts data rows
|
||||
"""
|
||||
|
||||
# Implementing class should register a target model (database model) to be used for import
|
||||
TARGET_MODEL = None
|
||||
|
||||
class Meta:
|
||||
fields = [
|
||||
'data_file',
|
||||
]
|
||||
|
||||
data_file = serializers.FileField(
|
||||
label=_("Data File"),
|
||||
help_text=_("Select data file for upload"),
|
||||
required=True,
|
||||
allow_empty_file=False,
|
||||
)
|
||||
|
||||
def validate_data_file(self, data_file):
|
||||
"""
|
||||
Perform validation checks on the uploaded data file.
|
||||
"""
|
||||
|
||||
self.filename = data_file.name
|
||||
|
||||
name, ext = os.path.splitext(data_file.name)
|
||||
|
||||
# Remove the leading . from the extension
|
||||
ext = ext[1:]
|
||||
|
||||
accepted_file_types = [
|
||||
'xls', 'xlsx',
|
||||
'csv', 'tsv',
|
||||
'xml',
|
||||
]
|
||||
|
||||
if ext not in accepted_file_types:
|
||||
raise serializers.ValidationError(_("Unsupported file type"))
|
||||
|
||||
# Impose a 50MB limit on uploaded BOM files
|
||||
max_upload_file_size = 50 * 1024 * 1024
|
||||
|
||||
if data_file.size > max_upload_file_size:
|
||||
raise serializers.ValidationError(_("File is too large"))
|
||||
|
||||
# Read file data into memory (bytes object)
|
||||
try:
|
||||
data = data_file.read()
|
||||
except Exception as e:
|
||||
raise serializers.ValidationError(str(e))
|
||||
|
||||
if ext in ['csv', 'tsv', 'xml']:
|
||||
try:
|
||||
data = data.decode()
|
||||
except Exception as e:
|
||||
raise serializers.ValidationError(str(e))
|
||||
|
||||
# Convert to a tablib dataset (we expect headers)
|
||||
try:
|
||||
self.dataset = tablib.Dataset().load(data, ext, headers=True)
|
||||
except Exception as e:
|
||||
raise serializers.ValidationError(str(e))
|
||||
|
||||
if len(self.dataset.headers) == 0:
|
||||
raise serializers.ValidationError(_("No columns found in file"))
|
||||
|
||||
if len(self.dataset) == 0:
|
||||
raise serializers.ValidationError(_("No data rows found in file"))
|
||||
|
||||
return data_file
|
||||
|
||||
def match_column(self, column_name, field_names, exact=False):
|
||||
"""
|
||||
Attempt to match a column name (from the file) to a field (defined in the model)
|
||||
|
||||
Order of matching is:
|
||||
- Direct match
|
||||
- Case insensitive match
|
||||
- Fuzzy match
|
||||
"""
|
||||
|
||||
column_name = column_name.strip()
|
||||
|
||||
column_name_lower = column_name.lower()
|
||||
|
||||
if column_name in field_names:
|
||||
return column_name
|
||||
|
||||
for field_name in field_names:
|
||||
if field_name.lower() == column_name_lower:
|
||||
return field_name
|
||||
|
||||
if exact:
|
||||
# Finished available 'exact' matches
|
||||
return None
|
||||
|
||||
# TODO: Fuzzy pattern matching for column names
|
||||
|
||||
# No matches found
|
||||
return None
|
||||
|
||||
def extract_data(self):
|
||||
"""
|
||||
Returns dataset extracted from the file
|
||||
"""
|
||||
|
||||
# Provide a dict of available import fields for the model
|
||||
model_fields = {}
|
||||
|
||||
# Keep track of columns we have already extracted
|
||||
matched_columns = set()
|
||||
|
||||
if self.TARGET_MODEL:
|
||||
try:
|
||||
model_fields = self.TARGET_MODEL.get_import_fields()
|
||||
except:
|
||||
pass
|
||||
|
||||
# Extract a list of valid model field names
|
||||
model_field_names = [key for key in model_fields.keys()]
|
||||
|
||||
# Provide a dict of available columns from the dataset
|
||||
file_columns = {}
|
||||
|
||||
for header in self.dataset.headers:
|
||||
column = {}
|
||||
|
||||
# Attempt to "match" file columns to model fields
|
||||
match = self.match_column(header, model_field_names, exact=True)
|
||||
|
||||
if match is not None and match not in matched_columns:
|
||||
matched_columns.add(match)
|
||||
column['value'] = match
|
||||
else:
|
||||
column['value'] = None
|
||||
|
||||
file_columns[header] = column
|
||||
|
||||
return {
|
||||
'file_fields': file_columns,
|
||||
'model_fields': model_fields,
|
||||
'rows': [row.values() for row in self.dataset.dict],
|
||||
'filename': self.filename,
|
||||
}
|
||||
|
||||
def save(self):
|
||||
...
|
||||
|
||||
|
||||
class DataFileExtractSerializer(serializers.Serializer):
|
||||
"""
|
||||
Generic serializer for extracting data from an imported dataset.
|
||||
|
||||
- User provides an array of matched headers
|
||||
- User provides an array of raw data rows
|
||||
"""
|
||||
|
||||
# Implementing class should register a target model (database model) to be used for import
|
||||
TARGET_MODEL = None
|
||||
|
||||
class Meta:
|
||||
fields = [
|
||||
'columns',
|
||||
'rows',
|
||||
]
|
||||
|
||||
# Mapping of columns
|
||||
columns = serializers.ListField(
|
||||
child=serializers.CharField(
|
||||
allow_blank=True,
|
||||
),
|
||||
)
|
||||
|
||||
rows = serializers.ListField(
|
||||
child=serializers.ListField(
|
||||
child=serializers.CharField(
|
||||
allow_blank=True,
|
||||
allow_null=True,
|
||||
),
|
||||
)
|
||||
)
|
||||
|
||||
def validate(self, data):
|
||||
|
||||
data = super().validate(data)
|
||||
|
||||
self.columns = data.get('columns', [])
|
||||
self.rows = data.get('rows', [])
|
||||
|
||||
if len(self.rows) == 0:
|
||||
raise serializers.ValidationError(_("No data rows provided"))
|
||||
|
||||
if len(self.columns) == 0:
|
||||
raise serializers.ValidationError(_("No data columns supplied"))
|
||||
|
||||
self.validate_extracted_columns()
|
||||
|
||||
return data
|
||||
|
||||
@property
|
||||
def data(self):
|
||||
|
||||
if self.TARGET_MODEL:
|
||||
try:
|
||||
model_fields = self.TARGET_MODEL.get_import_fields()
|
||||
except:
|
||||
model_fields = {}
|
||||
|
||||
rows = []
|
||||
|
||||
for row in self.rows:
|
||||
"""
|
||||
Optionally pre-process each row, before sending back to the client
|
||||
"""
|
||||
|
||||
processed_row = self.process_row(self.row_to_dict(row))
|
||||
|
||||
if processed_row:
|
||||
rows.append({
|
||||
"original": row,
|
||||
"data": processed_row,
|
||||
})
|
||||
|
||||
return {
|
||||
'fields': model_fields,
|
||||
'columns': self.columns,
|
||||
'rows': rows,
|
||||
}
|
||||
|
||||
def process_row(self, row):
|
||||
"""
|
||||
Process a 'row' of data, which is a mapped column:value dict
|
||||
|
||||
Returns either a mapped column:value dict, or None.
|
||||
|
||||
If the function returns None, the column is ignored!
|
||||
"""
|
||||
|
||||
# Default implementation simply returns the original row data
|
||||
return row
|
||||
|
||||
def row_to_dict(self, row):
|
||||
"""
|
||||
Convert a "row" to a named data dict
|
||||
"""
|
||||
|
||||
row_dict = {
|
||||
'errors': {},
|
||||
}
|
||||
|
||||
for idx, value in enumerate(row):
|
||||
|
||||
if idx < len(self.columns):
|
||||
col = self.columns[idx]
|
||||
|
||||
if col:
|
||||
row_dict[col] = value
|
||||
|
||||
return row_dict
|
||||
|
||||
def validate_extracted_columns(self):
|
||||
"""
|
||||
Perform custom validation of header mapping.
|
||||
"""
|
||||
|
||||
if self.TARGET_MODEL:
|
||||
try:
|
||||
model_fields = self.TARGET_MODEL.get_import_fields()
|
||||
except:
|
||||
model_fields = {}
|
||||
|
||||
cols_seen = set()
|
||||
|
||||
for name, field in model_fields.items():
|
||||
|
||||
required = field.get('required', False)
|
||||
|
||||
# Check for missing required columns
|
||||
if required:
|
||||
if name not in self.columns:
|
||||
raise serializers.ValidationError(_(f"Missing required column: '{name}'"))
|
||||
|
||||
for col in self.columns:
|
||||
|
||||
if not col:
|
||||
continue
|
||||
|
||||
# Check for duplicated columns
|
||||
if col in cols_seen:
|
||||
raise serializers.ValidationError(_(f"Duplicate column: '{col}'"))
|
||||
|
||||
cols_seen.add(col)
|
||||
|
||||
def save(self):
|
||||
"""
|
||||
No "save" action for this serializer
|
||||
"""
|
||||
...
|
||||
|
@ -15,8 +15,8 @@ import logging
|
||||
|
||||
import os
|
||||
import random
|
||||
import socket
|
||||
import string
|
||||
import shutil
|
||||
import sys
|
||||
from datetime import datetime
|
||||
|
||||
@ -25,31 +25,14 @@ import moneyed
|
||||
import yaml
|
||||
from django.utils.translation import gettext_lazy as _
|
||||
from django.contrib.messages import constants as messages
|
||||
import django.conf.locale
|
||||
|
||||
from .config import get_base_dir, get_config_file, get_plugin_file, get_setting
|
||||
|
||||
|
||||
def _is_true(x):
|
||||
# Shortcut function to determine if a value "looks" like a boolean
|
||||
return str(x).lower() in ['1', 'y', 'yes', 't', 'true']
|
||||
|
||||
|
||||
def get_setting(environment_var, backup_val, default_value=None):
|
||||
"""
|
||||
Helper function for retrieving a configuration setting value
|
||||
|
||||
- First preference is to look for the environment variable
|
||||
- Second preference is to look for the value of the settings file
|
||||
- Third preference is the default value
|
||||
"""
|
||||
|
||||
val = os.getenv(environment_var)
|
||||
|
||||
if val is not None:
|
||||
return val
|
||||
|
||||
if backup_val is not None:
|
||||
return backup_val
|
||||
|
||||
return default_value
|
||||
return str(x).strip().lower() in ['1', 'y', 'yes', 't', 'true']
|
||||
|
||||
|
||||
# Determine if we are running in "test" mode e.g. "manage.py test"
|
||||
@ -59,31 +42,16 @@ TESTING = 'test' in sys.argv
|
||||
DEFAULT_AUTO_FIELD = 'django.db.models.AutoField'
|
||||
|
||||
# Build paths inside the project like this: os.path.join(BASE_DIR, ...)
|
||||
BASE_DIR = os.path.dirname(os.path.dirname(os.path.abspath(__file__)))
|
||||
BASE_DIR = get_base_dir()
|
||||
|
||||
# Specify where the "config file" is located.
|
||||
# By default, this is 'config.yaml'
|
||||
|
||||
cfg_filename = os.getenv('INVENTREE_CONFIG_FILE')
|
||||
|
||||
if cfg_filename:
|
||||
cfg_filename = cfg_filename.strip()
|
||||
cfg_filename = os.path.abspath(cfg_filename)
|
||||
|
||||
else:
|
||||
# Config file is *not* specified - use the default
|
||||
cfg_filename = os.path.join(BASE_DIR, 'config.yaml')
|
||||
|
||||
if not os.path.exists(cfg_filename):
|
||||
print("InvenTree configuration file 'config.yaml' not found - creating default file")
|
||||
|
||||
cfg_template = os.path.join(BASE_DIR, "config_template.yaml")
|
||||
shutil.copyfile(cfg_template, cfg_filename)
|
||||
print(f"Created config file {cfg_filename}")
|
||||
cfg_filename = get_config_file()
|
||||
|
||||
with open(cfg_filename, 'r') as cfg:
|
||||
CONFIG = yaml.safe_load(cfg)
|
||||
|
||||
# We will place any config files in the same directory as the config file
|
||||
config_dir = os.path.dirname(cfg_filename)
|
||||
|
||||
# Default action is to run the system in Debug mode
|
||||
# SECURITY WARNING: don't run with debug turned on in production!
|
||||
DEBUG = _is_true(get_setting(
|
||||
@ -91,6 +59,12 @@ DEBUG = _is_true(get_setting(
|
||||
CONFIG.get('debug', True)
|
||||
))
|
||||
|
||||
# Determine if we are running in "demo mode"
|
||||
DEMO_MODE = _is_true(get_setting(
|
||||
'INVENTREE_DEMO',
|
||||
CONFIG.get('demo', False)
|
||||
))
|
||||
|
||||
DOCKER = _is_true(get_setting(
|
||||
'INVENTREE_DOCKER',
|
||||
False
|
||||
@ -108,7 +82,7 @@ logging.basicConfig(
|
||||
)
|
||||
|
||||
if log_level not in ['DEBUG', 'INFO', 'WARNING', 'ERROR', 'CRITICAL']:
|
||||
log_level = 'WARNING'
|
||||
log_level = 'WARNING' # pragma: no cover
|
||||
|
||||
LOGGING = {
|
||||
'version': 1,
|
||||
@ -122,6 +96,11 @@ LOGGING = {
|
||||
'handlers': ['console'],
|
||||
'level': log_level,
|
||||
},
|
||||
'filters': {
|
||||
'require_not_maintenance_mode_503': {
|
||||
'()': 'maintenance_mode.logging.RequireNotMaintenanceMode503',
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
# Get a logger instance for this setup file
|
||||
@ -140,20 +119,20 @@ d) Create "secret_key.txt" if it does not exist
|
||||
|
||||
if os.getenv("INVENTREE_SECRET_KEY"):
|
||||
# Secret key passed in directly
|
||||
SECRET_KEY = os.getenv("INVENTREE_SECRET_KEY").strip()
|
||||
logger.info("SECRET_KEY loaded by INVENTREE_SECRET_KEY")
|
||||
SECRET_KEY = os.getenv("INVENTREE_SECRET_KEY").strip() # pragma: no cover
|
||||
logger.info("SECRET_KEY loaded by INVENTREE_SECRET_KEY") # pragma: no cover
|
||||
else:
|
||||
# Secret key passed in by file location
|
||||
key_file = os.getenv("INVENTREE_SECRET_KEY_FILE")
|
||||
|
||||
if key_file:
|
||||
key_file = os.path.abspath(key_file)
|
||||
key_file = os.path.abspath(key_file) # pragma: no cover
|
||||
else:
|
||||
# default secret key location
|
||||
key_file = os.path.join(BASE_DIR, "secret_key.txt")
|
||||
key_file = os.path.abspath(key_file)
|
||||
|
||||
if not os.path.exists(key_file):
|
||||
if not os.path.exists(key_file): # pragma: no cover
|
||||
logger.info(f"Generating random key file at '{key_file}'")
|
||||
# Create a random key file
|
||||
with open(key_file, 'w') as f:
|
||||
@ -165,7 +144,7 @@ else:
|
||||
|
||||
try:
|
||||
SECRET_KEY = open(key_file, "r").read().strip()
|
||||
except Exception:
|
||||
except Exception: # pragma: no cover
|
||||
logger.exception(f"Couldn't load keyfile {key_file}")
|
||||
sys.exit(-1)
|
||||
|
||||
@ -177,7 +156,7 @@ STATIC_ROOT = os.path.abspath(
|
||||
)
|
||||
)
|
||||
|
||||
if STATIC_ROOT is None:
|
||||
if STATIC_ROOT is None: # pragma: no cover
|
||||
print("ERROR: INVENTREE_STATIC_ROOT directory not defined")
|
||||
sys.exit(1)
|
||||
|
||||
@ -189,7 +168,7 @@ MEDIA_ROOT = os.path.abspath(
|
||||
)
|
||||
)
|
||||
|
||||
if MEDIA_ROOT is None:
|
||||
if MEDIA_ROOT is None: # pragma: no cover
|
||||
print("ERROR: INVENTREE_MEDIA_ROOT directory is not defined")
|
||||
sys.exit(1)
|
||||
|
||||
@ -208,7 +187,7 @@ if cors_opt:
|
||||
CORS_ORIGIN_ALLOW_ALL = cors_opt.get('allow_all', False)
|
||||
|
||||
if not CORS_ORIGIN_ALLOW_ALL:
|
||||
CORS_ORIGIN_WHITELIST = cors_opt.get('whitelist', [])
|
||||
CORS_ORIGIN_WHITELIST = cors_opt.get('whitelist', []) # pragma: no cover
|
||||
|
||||
# Web URL endpoint for served static files
|
||||
STATIC_URL = '/static/'
|
||||
@ -233,7 +212,10 @@ STATIC_COLOR_THEMES_DIR = os.path.join(STATIC_ROOT, 'css', 'color-themes')
|
||||
MEDIA_URL = '/media/'
|
||||
|
||||
if DEBUG:
|
||||
logger.info("InvenTree running in DEBUG mode")
|
||||
logger.info("InvenTree running with DEBUG enabled")
|
||||
|
||||
if DEMO_MODE:
|
||||
logger.warning("InvenTree running in DEMO mode") # pragma: no cover
|
||||
|
||||
logger.debug(f"MEDIA_ROOT: '{MEDIA_ROOT}'")
|
||||
logger.debug(f"STATIC_ROOT: '{STATIC_ROOT}'")
|
||||
@ -246,9 +228,13 @@ INSTALLED_APPS = [
|
||||
'django.contrib.admin',
|
||||
'django.contrib.auth',
|
||||
'django.contrib.contenttypes',
|
||||
'django.contrib.sessions',
|
||||
'user_sessions', # db user sessions
|
||||
'django.contrib.messages',
|
||||
'django.contrib.staticfiles',
|
||||
'django.contrib.sites',
|
||||
|
||||
# Maintenance
|
||||
'maintenance_mode',
|
||||
|
||||
# InvenTree apps
|
||||
'build.apps.BuildConfig',
|
||||
@ -260,6 +246,7 @@ INSTALLED_APPS = [
|
||||
'report.apps.ReportConfig',
|
||||
'stock.apps.StockConfig',
|
||||
'users.apps.UsersConfig',
|
||||
'plugin.apps.PluginAppConfig',
|
||||
'InvenTree.apps.InvenTreeConfig', # InvenTree app runs last
|
||||
|
||||
# Third part add-ons
|
||||
@ -279,30 +266,45 @@ INSTALLED_APPS = [
|
||||
'error_report', # Error reporting in the admin interface
|
||||
'django_q',
|
||||
'formtools', # Form wizard tools
|
||||
|
||||
'allauth', # Base app for SSO
|
||||
'allauth.account', # Extend user with accounts
|
||||
'allauth.socialaccount', # Use 'social' providers
|
||||
|
||||
'django_otp', # OTP is needed for MFA - base package
|
||||
'django_otp.plugins.otp_totp', # Time based OTP
|
||||
'django_otp.plugins.otp_static', # Backup codes
|
||||
|
||||
'allauth_2fa', # MFA flow for allauth
|
||||
]
|
||||
|
||||
MIDDLEWARE = CONFIG.get('middleware', [
|
||||
'django.middleware.security.SecurityMiddleware',
|
||||
'django.contrib.sessions.middleware.SessionMiddleware',
|
||||
'user_sessions.middleware.SessionMiddleware', # db user sessions
|
||||
'django.middleware.locale.LocaleMiddleware',
|
||||
'django.middleware.common.CommonMiddleware',
|
||||
'django.middleware.csrf.CsrfViewMiddleware',
|
||||
'corsheaders.middleware.CorsMiddleware',
|
||||
'django.contrib.auth.middleware.AuthenticationMiddleware',
|
||||
'django_otp.middleware.OTPMiddleware', # MFA support
|
||||
'InvenTree.middleware.CustomAllauthTwoFactorMiddleware', # Flow control for allauth
|
||||
'django.contrib.messages.middleware.MessageMiddleware',
|
||||
'django.middleware.clickjacking.XFrameOptionsMiddleware',
|
||||
'InvenTree.middleware.AuthRequiredMiddleware'
|
||||
'InvenTree.middleware.AuthRequiredMiddleware',
|
||||
'InvenTree.middleware.Check2FAMiddleware', # Check if the user should be forced to use MFA
|
||||
'maintenance_mode.middleware.MaintenanceModeMiddleware',
|
||||
])
|
||||
|
||||
# Error reporting middleware
|
||||
MIDDLEWARE.append('error_report.middleware.ExceptionProcessor')
|
||||
|
||||
AUTHENTICATION_BACKENDS = CONFIG.get('authentication_backends', [
|
||||
'django.contrib.auth.backends.ModelBackend'
|
||||
'django.contrib.auth.backends.ModelBackend',
|
||||
'allauth.account.auth_backends.AuthenticationBackend', # SSO login via external providers
|
||||
])
|
||||
|
||||
# If the debug toolbar is enabled, add the modules
|
||||
if DEBUG and CONFIG.get('debug_toolbar', False):
|
||||
if DEBUG and CONFIG.get('debug_toolbar', False): # pragma: no cover
|
||||
logger.info("Running with DEBUG_TOOLBAR enabled")
|
||||
INSTALLED_APPS.append('debug_toolbar')
|
||||
MIDDLEWARE.append('debug_toolbar.middleware.DebugToolbarMiddleware')
|
||||
@ -318,7 +320,6 @@ TEMPLATES = [
|
||||
os.path.join(MEDIA_ROOT, 'report'),
|
||||
os.path.join(MEDIA_ROOT, 'label'),
|
||||
],
|
||||
'APP_DIRS': True,
|
||||
'OPTIONS': {
|
||||
'context_processors': [
|
||||
'django.template.context_processors.debug',
|
||||
@ -331,6 +332,13 @@ TEMPLATES = [
|
||||
'InvenTree.context.status_codes',
|
||||
'InvenTree.context.user_roles',
|
||||
],
|
||||
'loaders': [(
|
||||
'django.template.loaders.cached.Loader', [
|
||||
'plugin.loader.PluginTemplateLoader',
|
||||
'django.template.loaders.filesystem.Loader',
|
||||
'django.template.loaders.app_directories.Loader',
|
||||
])
|
||||
],
|
||||
},
|
||||
},
|
||||
]
|
||||
@ -355,63 +363,6 @@ REST_FRAMEWORK = {
|
||||
|
||||
WSGI_APPLICATION = 'InvenTree.wsgi.application'
|
||||
|
||||
background_workers = os.environ.get('INVENTREE_BACKGROUND_WORKERS', None)
|
||||
|
||||
if background_workers is not None:
|
||||
try:
|
||||
background_workers = int(background_workers)
|
||||
except ValueError:
|
||||
background_workers = None
|
||||
|
||||
if background_workers is None:
|
||||
# Sensible default?
|
||||
background_workers = 4
|
||||
|
||||
# django-q configuration
|
||||
Q_CLUSTER = {
|
||||
'name': 'InvenTree',
|
||||
'workers': background_workers,
|
||||
'timeout': 90,
|
||||
'retry': 120,
|
||||
'queue_limit': 50,
|
||||
'bulk': 10,
|
||||
'orm': 'default',
|
||||
'sync': False,
|
||||
}
|
||||
|
||||
# Markdownx configuration
|
||||
# Ref: https://neutronx.github.io/django-markdownx/customization/
|
||||
MARKDOWNX_MEDIA_PATH = datetime.now().strftime('markdownx/%Y/%m/%d')
|
||||
|
||||
# Markdownify configuration
|
||||
# Ref: https://django-markdownify.readthedocs.io/en/latest/settings.html
|
||||
|
||||
MARKDOWNIFY_WHITELIST_TAGS = [
|
||||
'a',
|
||||
'abbr',
|
||||
'b',
|
||||
'blockquote',
|
||||
'em',
|
||||
'h1', 'h2', 'h3',
|
||||
'i',
|
||||
'img',
|
||||
'li',
|
||||
'ol',
|
||||
'p',
|
||||
'strong',
|
||||
'ul'
|
||||
]
|
||||
|
||||
MARKDOWNIFY_WHITELIST_ATTRS = [
|
||||
'href',
|
||||
'src',
|
||||
'alt',
|
||||
]
|
||||
|
||||
MARKDOWNIFY_BLEACH = False
|
||||
|
||||
DATABASES = {}
|
||||
|
||||
"""
|
||||
Configure the database backend based on the user-specified values.
|
||||
|
||||
@ -445,7 +396,7 @@ for key in db_keys:
|
||||
reqiured_keys = ['ENGINE', 'NAME']
|
||||
|
||||
for key in reqiured_keys:
|
||||
if key not in db_config:
|
||||
if key not in db_config: # pragma: no cover
|
||||
error_msg = f'Missing required database configuration value {key}'
|
||||
logger.error(error_msg)
|
||||
|
||||
@ -464,7 +415,7 @@ db_engine = db_config['ENGINE'].lower()
|
||||
|
||||
# Correct common misspelling
|
||||
if db_engine == 'sqlite':
|
||||
db_engine = 'sqlite3'
|
||||
db_engine = 'sqlite3' # pragma: no cover
|
||||
|
||||
if db_engine in ['sqlite3', 'postgresql', 'mysql']:
|
||||
# Prepend the required python module string
|
||||
@ -478,14 +429,195 @@ logger.info(f"DB_ENGINE: {db_engine}")
|
||||
logger.info(f"DB_NAME: {db_name}")
|
||||
logger.info(f"DB_HOST: {db_host}")
|
||||
|
||||
DATABASES['default'] = db_config
|
||||
"""
|
||||
In addition to base-level database configuration, we may wish to specify specific options to the database backend
|
||||
Ref: https://docs.djangoproject.com/en/3.2/ref/settings/#std:setting-OPTIONS
|
||||
"""
|
||||
|
||||
CACHES = {
|
||||
'default': {
|
||||
'BACKEND': 'django.core.cache.backends.locmem.LocMemCache',
|
||||
},
|
||||
# 'OPTIONS' or 'options' can be specified in config.yaml
|
||||
# Set useful sensible timeouts for a transactional webserver to communicate
|
||||
# with its database server, that is, if the webserver is having issues
|
||||
# connecting to the database server (such as a replica failover) don't sit and
|
||||
# wait for possibly an hour or more, just tell the client something went wrong
|
||||
# and let the client retry when they want to.
|
||||
db_options = db_config.get("OPTIONS", db_config.get("options", {}))
|
||||
|
||||
# Specific options for postgres backend
|
||||
if "postgres" in db_engine: # pragma: no cover
|
||||
from psycopg2.extensions import (
|
||||
ISOLATION_LEVEL_READ_COMMITTED,
|
||||
ISOLATION_LEVEL_SERIALIZABLE,
|
||||
)
|
||||
|
||||
# Connection timeout
|
||||
if "connect_timeout" not in db_options:
|
||||
# The DB server is in the same data center, it should not take very
|
||||
# long to connect to the database server
|
||||
# # seconds, 2 is minium allowed by libpq
|
||||
db_options["connect_timeout"] = int(
|
||||
os.getenv("INVENTREE_DB_TIMEOUT", 2)
|
||||
)
|
||||
|
||||
# Setup TCP keepalive
|
||||
# DB server is in the same DC, it should not become unresponsive for
|
||||
# very long. With the defaults below we wait 5 seconds for the network
|
||||
# issue to resolve itself. It it that doesn't happen whatever happened
|
||||
# is probably fatal and no amount of waiting is going to fix it.
|
||||
# # 0 - TCP Keepalives disabled; 1 - enabled
|
||||
if "keepalives" not in db_options:
|
||||
db_options["keepalives"] = int(
|
||||
os.getenv("INVENTREE_DB_TCP_KEEPALIVES", "1")
|
||||
)
|
||||
# # Seconds after connection is idle to send keep alive
|
||||
if "keepalives_idle" not in db_options:
|
||||
db_options["keepalives_idle"] = int(
|
||||
os.getenv("INVENTREE_DB_TCP_KEEPALIVES_IDLE", "1")
|
||||
)
|
||||
# # Seconds after missing ACK to send another keep alive
|
||||
if "keepalives_interval" not in db_options:
|
||||
db_options["keepalives_interval"] = int(
|
||||
os.getenv("INVENTREE_DB_TCP_KEEPALIVES_INTERVAL", "1")
|
||||
)
|
||||
# # Number of missing ACKs before we close the connection
|
||||
if "keepalives_count" not in db_options:
|
||||
db_options["keepalives_count"] = int(
|
||||
os.getenv("INVENTREE_DB_TCP_KEEPALIVES_COUNT", "5")
|
||||
)
|
||||
# # Milliseconds for how long pending data should remain unacked
|
||||
# by the remote server
|
||||
# TODO: Supported starting in PSQL 11
|
||||
# "tcp_user_timeout": int(os.getenv("PGTCP_USER_TIMEOUT", "1000"),
|
||||
|
||||
# Postgres's default isolation level is Read Committed which is
|
||||
# normally fine, but most developers think the database server is
|
||||
# actually going to do Serializable type checks on the queries to
|
||||
# protect against simultaneous changes.
|
||||
# https://www.postgresql.org/docs/devel/transaction-iso.html
|
||||
# https://docs.djangoproject.com/en/3.2/ref/databases/#isolation-level
|
||||
if "isolation_level" not in db_options:
|
||||
serializable = _is_true(
|
||||
os.getenv("INVENTREE_DB_ISOLATION_SERIALIZABLE", "true")
|
||||
)
|
||||
db_options["isolation_level"] = (
|
||||
ISOLATION_LEVEL_SERIALIZABLE
|
||||
if serializable
|
||||
else ISOLATION_LEVEL_READ_COMMITTED
|
||||
)
|
||||
|
||||
# Specific options for MySql / MariaDB backend
|
||||
if "mysql" in db_engine: # pragma: no cover
|
||||
# TODO TCP time outs and keepalives
|
||||
|
||||
# MariaDB's default isolation level is Repeatable Read which is
|
||||
# normally fine, but most developers think the database server is
|
||||
# actually going to Serializable type checks on the queries to
|
||||
# protect against siumltaneous changes.
|
||||
# https://mariadb.com/kb/en/mariadb-transactions-and-isolation-levels-for-sql-server-users/#changing-the-isolation-level
|
||||
# https://docs.djangoproject.com/en/3.2/ref/databases/#mysql-isolation-level
|
||||
if "isolation_level" not in db_options:
|
||||
serializable = _is_true(
|
||||
os.getenv("INVENTREE_DB_ISOLATION_SERIALIZABLE", "true")
|
||||
)
|
||||
db_options["isolation_level"] = (
|
||||
"serializable" if serializable else "read committed"
|
||||
)
|
||||
|
||||
# Specific options for sqlite backend
|
||||
if "sqlite" in db_engine:
|
||||
# TODO: Verify timeouts are not an issue because no network is involved for SQLite
|
||||
|
||||
# SQLite's default isolation level is Serializable due to SQLite's
|
||||
# single writer implementation. Presumably as a result of this, it is
|
||||
# not possible to implement any lower isolation levels in SQLite.
|
||||
# https://www.sqlite.org/isolation.html
|
||||
pass
|
||||
|
||||
# Provide OPTIONS dict back to the database configuration dict
|
||||
db_config['OPTIONS'] = db_options
|
||||
|
||||
DATABASES = {
|
||||
'default': db_config
|
||||
}
|
||||
|
||||
|
||||
_cache_config = CONFIG.get("cache", {})
|
||||
_cache_host = _cache_config.get("host", os.getenv("INVENTREE_CACHE_HOST"))
|
||||
_cache_port = _cache_config.get(
|
||||
"port", os.getenv("INVENTREE_CACHE_PORT", "6379")
|
||||
)
|
||||
|
||||
if _cache_host: # pragma: no cover
|
||||
# We are going to rely upon a possibly non-localhost for our cache,
|
||||
# so don't wait too long for the cache as nothing in the cache should be
|
||||
# irreplacable.
|
||||
_cache_options = {
|
||||
"CLIENT_CLASS": "django_redis.client.DefaultClient",
|
||||
"SOCKET_CONNECT_TIMEOUT": int(os.getenv("CACHE_CONNECT_TIMEOUT", "2")),
|
||||
"SOCKET_TIMEOUT": int(os.getenv("CACHE_SOCKET_TIMEOUT", "2")),
|
||||
"CONNECTION_POOL_KWARGS": {
|
||||
"socket_keepalive": _is_true(
|
||||
os.getenv("CACHE_TCP_KEEPALIVE", "1")
|
||||
),
|
||||
"socket_keepalive_options": {
|
||||
socket.TCP_KEEPCNT: int(
|
||||
os.getenv("CACHE_KEEPALIVES_COUNT", "5")
|
||||
),
|
||||
socket.TCP_KEEPIDLE: int(
|
||||
os.getenv("CACHE_KEEPALIVES_IDLE", "1")
|
||||
),
|
||||
socket.TCP_KEEPINTVL: int(
|
||||
os.getenv("CACHE_KEEPALIVES_INTERVAL", "1")
|
||||
),
|
||||
socket.TCP_USER_TIMEOUT: int(
|
||||
os.getenv("CACHE_TCP_USER_TIMEOUT", "1000")
|
||||
),
|
||||
},
|
||||
},
|
||||
}
|
||||
CACHES = {
|
||||
"default": {
|
||||
"BACKEND": "django_redis.cache.RedisCache",
|
||||
"LOCATION": f"redis://{_cache_host}:{_cache_port}/0",
|
||||
"OPTIONS": _cache_options,
|
||||
},
|
||||
}
|
||||
else:
|
||||
CACHES = {
|
||||
"default": {
|
||||
"BACKEND": "django.core.cache.backends.locmem.LocMemCache",
|
||||
},
|
||||
}
|
||||
|
||||
try:
|
||||
# 4 background workers seems like a sensible default
|
||||
background_workers = int(os.environ.get('INVENTREE_BACKGROUND_WORKERS', 4))
|
||||
except ValueError: # pragma: no cover
|
||||
background_workers = 4
|
||||
|
||||
# django-q configuration
|
||||
Q_CLUSTER = {
|
||||
'name': 'InvenTree',
|
||||
'workers': background_workers,
|
||||
'timeout': 90,
|
||||
'retry': 120,
|
||||
'queue_limit': 50,
|
||||
'bulk': 10,
|
||||
'orm': 'default',
|
||||
'sync': False,
|
||||
}
|
||||
|
||||
if _cache_host: # pragma: no cover
|
||||
# If using external redis cache, make the cache the broker for Django Q
|
||||
# as well
|
||||
Q_CLUSTER["django_redis"] = "worker"
|
||||
|
||||
# database user sessions
|
||||
SESSION_ENGINE = 'user_sessions.backends.db'
|
||||
LOGOUT_REDIRECT_URL = 'index'
|
||||
SILENCED_SYSTEM_CHECKS = [
|
||||
'admin.E410',
|
||||
]
|
||||
|
||||
# Password validation
|
||||
# https://docs.djangoproject.com/en/1.10/ref/settings/#auth-password-validators
|
||||
|
||||
@ -509,7 +641,7 @@ AUTH_PASSWORD_VALIDATORS = [
|
||||
|
||||
EXTRA_URL_SCHEMES = CONFIG.get('extra_url_schemes', [])
|
||||
|
||||
if not type(EXTRA_URL_SCHEMES) in [list]:
|
||||
if not type(EXTRA_URL_SCHEMES) in [list]: # pragma: no cover
|
||||
logger.warning("extra_url_schemes not correctly formatted")
|
||||
EXTRA_URL_SCHEMES = []
|
||||
|
||||
@ -524,6 +656,7 @@ LANGUAGES = [
|
||||
('el', _('Greek')),
|
||||
('en', _('English')),
|
||||
('es', _('Spanish')),
|
||||
('es-mx', _('Spanish (Mexican)')),
|
||||
('fr', _('French')),
|
||||
('he', _('Hebrew')),
|
||||
('it', _('Italian')),
|
||||
@ -532,6 +665,7 @@ LANGUAGES = [
|
||||
('nl', _('Dutch')),
|
||||
('no', _('Norwegian')),
|
||||
('pl', _('Polish')),
|
||||
('pt', _('Portugese')),
|
||||
('ru', _('Russian')),
|
||||
('sv', _('Swedish')),
|
||||
('th', _('Thai')),
|
||||
@ -540,6 +674,25 @@ LANGUAGES = [
|
||||
('zh-cn', _('Chinese')),
|
||||
]
|
||||
|
||||
# Testing interface translations
|
||||
if get_setting('TEST_TRANSLATIONS', False): # pragma: no cover
|
||||
# Set default language
|
||||
LANGUAGE_CODE = 'xx'
|
||||
|
||||
# Add to language catalog
|
||||
LANGUAGES.append(('xx', 'Test'))
|
||||
|
||||
# Add custom languages not provided by Django
|
||||
EXTRA_LANG_INFO = {
|
||||
'xx': {
|
||||
'code': 'xx',
|
||||
'name': 'Test',
|
||||
'name_local': 'Test'
|
||||
},
|
||||
}
|
||||
LANG_INFO = dict(django.conf.locale.LANG_INFO, **EXTRA_LANG_INFO)
|
||||
django.conf.locale.LANG_INFO = LANG_INFO
|
||||
|
||||
# Currencies available for use
|
||||
CURRENCIES = CONFIG.get(
|
||||
'currencies',
|
||||
@ -550,7 +703,7 @@ CURRENCIES = CONFIG.get(
|
||||
|
||||
# Check that each provided currency is supported
|
||||
for currency in CURRENCIES:
|
||||
if currency not in moneyed.CURRENCIES:
|
||||
if currency not in moneyed.CURRENCIES: # pragma: no cover
|
||||
print(f"Currency code '{currency}' is not supported")
|
||||
sys.exit(1)
|
||||
|
||||
@ -624,14 +777,14 @@ USE_L10N = True
|
||||
# Do not use native timezone support in "test" mode
|
||||
# It generates a *lot* of cruft in the logs
|
||||
if not TESTING:
|
||||
USE_TZ = True
|
||||
USE_TZ = True # pragma: no cover
|
||||
|
||||
DATE_INPUT_FORMATS = [
|
||||
"%Y-%m-%d",
|
||||
]
|
||||
|
||||
# crispy forms use the bootstrap templates
|
||||
CRISPY_TEMPLATE_PACK = 'bootstrap3'
|
||||
CRISPY_TEMPLATE_PACK = 'bootstrap4'
|
||||
|
||||
# Use database transactions when importing / exporting data
|
||||
IMPORT_EXPORT_USE_TRANSACTIONS = True
|
||||
@ -646,3 +799,93 @@ MESSAGE_TAGS = {
|
||||
messages.ERROR: 'alert alert-block alert-danger',
|
||||
messages.INFO: 'alert alert-block alert-info',
|
||||
}
|
||||
|
||||
SITE_ID = 1
|
||||
|
||||
# Load the allauth social backends
|
||||
SOCIAL_BACKENDS = CONFIG.get('social_backends', [])
|
||||
for app in SOCIAL_BACKENDS:
|
||||
INSTALLED_APPS.append(app) # pragma: no cover
|
||||
|
||||
SOCIALACCOUNT_PROVIDERS = CONFIG.get('social_providers', [])
|
||||
|
||||
# settings for allauth
|
||||
ACCOUNT_EMAIL_CONFIRMATION_EXPIRE_DAYS = get_setting('INVENTREE_LOGIN_CONFIRM_DAYS', CONFIG.get('login_confirm_days', 3))
|
||||
|
||||
ACCOUNT_LOGIN_ATTEMPTS_LIMIT = get_setting('INVENTREE_LOGIN_ATTEMPTS', CONFIG.get('login_attempts', 5))
|
||||
|
||||
ACCOUNT_LOGOUT_ON_PASSWORD_CHANGE = True
|
||||
|
||||
# override forms / adapters
|
||||
ACCOUNT_FORMS = {
|
||||
'login': 'allauth.account.forms.LoginForm',
|
||||
'signup': 'InvenTree.forms.CustomSignupForm',
|
||||
'add_email': 'allauth.account.forms.AddEmailForm',
|
||||
'change_password': 'allauth.account.forms.ChangePasswordForm',
|
||||
'set_password': 'allauth.account.forms.SetPasswordForm',
|
||||
'reset_password': 'allauth.account.forms.ResetPasswordForm',
|
||||
'reset_password_from_key': 'allauth.account.forms.ResetPasswordKeyForm',
|
||||
'disconnect': 'allauth.socialaccount.forms.DisconnectForm',
|
||||
}
|
||||
|
||||
SOCIALACCOUNT_ADAPTER = 'InvenTree.forms.CustomSocialAccountAdapter'
|
||||
ACCOUNT_ADAPTER = 'InvenTree.forms.CustomAccountAdapter'
|
||||
|
||||
# Markdownx configuration
|
||||
# Ref: https://neutronx.github.io/django-markdownx/customization/
|
||||
MARKDOWNX_MEDIA_PATH = datetime.now().strftime('markdownx/%Y/%m/%d')
|
||||
|
||||
# Markdownify configuration
|
||||
# Ref: https://django-markdownify.readthedocs.io/en/latest/settings.html
|
||||
|
||||
MARKDOWNIFY_WHITELIST_TAGS = [
|
||||
'a',
|
||||
'abbr',
|
||||
'b',
|
||||
'blockquote',
|
||||
'em',
|
||||
'h1', 'h2', 'h3',
|
||||
'i',
|
||||
'img',
|
||||
'li',
|
||||
'ol',
|
||||
'p',
|
||||
'strong',
|
||||
'ul'
|
||||
]
|
||||
|
||||
MARKDOWNIFY_WHITELIST_ATTRS = [
|
||||
'href',
|
||||
'src',
|
||||
'alt',
|
||||
]
|
||||
|
||||
MARKDOWNIFY_BLEACH = False
|
||||
|
||||
# Maintenance mode
|
||||
MAINTENANCE_MODE_RETRY_AFTER = 60
|
||||
MAINTENANCE_MODE_STATE_BACKEND = 'maintenance_mode.backends.DefaultStorageBackend'
|
||||
|
||||
# Are plugins enabled?
|
||||
PLUGINS_ENABLED = _is_true(get_setting(
|
||||
'INVENTREE_PLUGINS_ENABLED',
|
||||
CONFIG.get('plugins_enabled', False),
|
||||
))
|
||||
|
||||
PLUGIN_FILE = get_plugin_file()
|
||||
|
||||
# Plugin Directories (local plugins will be loaded from these directories)
|
||||
PLUGIN_DIRS = ['plugin.builtin', 'barcodes.plugins', ]
|
||||
|
||||
if not TESTING:
|
||||
# load local deploy directory in prod
|
||||
PLUGIN_DIRS.append('plugins') # pragma: no cover
|
||||
|
||||
if DEBUG or TESTING:
|
||||
# load samples in debug mode
|
||||
PLUGIN_DIRS.append('plugin.samples')
|
||||
|
||||
# Plugin test settings
|
||||
PLUGIN_TESTING = get_setting('PLUGIN_TESTING', TESTING) # are plugins beeing tested?
|
||||
PLUGIN_TESTING_SETUP = get_setting('PLUGIN_TESTING_SETUP', False) # load plugins from setup hooks in testing?
|
||||
PLUGIN_RETRY = get_setting('PLUGIN_RETRY', 5) # how often should plugin loading be tried?
|
||||
|
File diff suppressed because one or more lines are too long
@ -1,170 +0,0 @@
|
||||
/**
|
||||
* @author: Dennis Hernández
|
||||
* @webSite: http://djhvscf.github.io/Blog
|
||||
* @update: zhixin wen <wenzhixin2010@gmail.com>
|
||||
*/
|
||||
|
||||
!($ => {
|
||||
const diacriticsMap = {}
|
||||
const defaultAccentsDiacritics = [
|
||||
{base: 'A', letters: '\u0041\u24B6\uFF21\u00C0\u00C1\u00C2\u1EA6\u1EA4\u1EAA\u1EA8\u00C3\u0100\u0102\u1EB0\u1EAE\u1EB4\u1EB2\u0226\u01E0\u00C4\u01DE\u1EA2\u00C5\u01FA\u01CD\u0200\u0202\u1EA0\u1EAC\u1EB6\u1E00\u0104\u023A\u2C6F'},
|
||||
{base: 'AA',letters: '\uA732'},
|
||||
{base: 'AE',letters: '\u00C6\u01FC\u01E2'},
|
||||
{base: 'AO',letters: '\uA734'},
|
||||
{base: 'AU',letters: '\uA736'},
|
||||
{base: 'AV',letters: '\uA738\uA73A'},
|
||||
{base: 'AY',letters: '\uA73C'},
|
||||
{base: 'B', letters: '\u0042\u24B7\uFF22\u1E02\u1E04\u1E06\u0243\u0182\u0181'},
|
||||
{base: 'C', letters: '\u0043\u24B8\uFF23\u0106\u0108\u010A\u010C\u00C7\u1E08\u0187\u023B\uA73E'},
|
||||
{base: 'D', letters: '\u0044\u24B9\uFF24\u1E0A\u010E\u1E0C\u1E10\u1E12\u1E0E\u0110\u018B\u018A\u0189\uA779'},
|
||||
{base: 'DZ',letters: '\u01F1\u01C4'},
|
||||
{base: 'Dz',letters: '\u01F2\u01C5'},
|
||||
{base: 'E', letters: '\u0045\u24BA\uFF25\u00C8\u00C9\u00CA\u1EC0\u1EBE\u1EC4\u1EC2\u1EBC\u0112\u1E14\u1E16\u0114\u0116\u00CB\u1EBA\u011A\u0204\u0206\u1EB8\u1EC6\u0228\u1E1C\u0118\u1E18\u1E1A\u0190\u018E'},
|
||||
{base: 'F', letters: '\u0046\u24BB\uFF26\u1E1E\u0191\uA77B'},
|
||||
{base: 'G', letters: '\u0047\u24BC\uFF27\u01F4\u011C\u1E20\u011E\u0120\u01E6\u0122\u01E4\u0193\uA7A0\uA77D\uA77E'},
|
||||
{base: 'H', letters: '\u0048\u24BD\uFF28\u0124\u1E22\u1E26\u021E\u1E24\u1E28\u1E2A\u0126\u2C67\u2C75\uA78D'},
|
||||
{base: 'I', letters: '\u0049\u24BE\uFF29\u00CC\u00CD\u00CE\u0128\u012A\u012C\u0130\u00CF\u1E2E\u1EC8\u01CF\u0208\u020A\u1ECA\u012E\u1E2C\u0197'},
|
||||
{base: 'J', letters: '\u004A\u24BF\uFF2A\u0134\u0248'},
|
||||
{base: 'K', letters: '\u004B\u24C0\uFF2B\u1E30\u01E8\u1E32\u0136\u1E34\u0198\u2C69\uA740\uA742\uA744\uA7A2'},
|
||||
{base: 'L', letters: '\u004C\u24C1\uFF2C\u013F\u0139\u013D\u1E36\u1E38\u013B\u1E3C\u1E3A\u0141\u023D\u2C62\u2C60\uA748\uA746\uA780'},
|
||||
{base: 'LJ',letters: '\u01C7'},
|
||||
{base: 'Lj',letters: '\u01C8'},
|
||||
{base: 'M', letters: '\u004D\u24C2\uFF2D\u1E3E\u1E40\u1E42\u2C6E\u019C'},
|
||||
{base: 'N', letters: '\u004E\u24C3\uFF2E\u01F8\u0143\u00D1\u1E44\u0147\u1E46\u0145\u1E4A\u1E48\u0220\u019D\uA790\uA7A4'},
|
||||
{base: 'NJ',letters: '\u01CA'},
|
||||
{base: 'Nj',letters: '\u01CB'},
|
||||
{base: 'O', letters: '\u004F\u24C4\uFF2F\u00D2\u00D3\u00D4\u1ED2\u1ED0\u1ED6\u1ED4\u00D5\u1E4C\u022C\u1E4E\u014C\u1E50\u1E52\u014E\u022E\u0230\u00D6\u022A\u1ECE\u0150\u01D1\u020C\u020E\u01A0\u1EDC\u1EDA\u1EE0\u1EDE\u1EE2\u1ECC\u1ED8\u01EA\u01EC\u00D8\u01FE\u0186\u019F\uA74A\uA74C'},
|
||||
{base: 'OI',letters: '\u01A2'},
|
||||
{base: 'OO',letters: '\uA74E'},
|
||||
{base: 'OU',letters: '\u0222'},
|
||||
{base: 'OE',letters: '\u008C\u0152'},
|
||||
{base: 'oe',letters: '\u009C\u0153'},
|
||||
{base: 'P', letters: '\u0050\u24C5\uFF30\u1E54\u1E56\u01A4\u2C63\uA750\uA752\uA754'},
|
||||
{base: 'Q', letters: '\u0051\u24C6\uFF31\uA756\uA758\u024A'},
|
||||
{base: 'R', letters: '\u0052\u24C7\uFF32\u0154\u1E58\u0158\u0210\u0212\u1E5A\u1E5C\u0156\u1E5E\u024C\u2C64\uA75A\uA7A6\uA782'},
|
||||
{base: 'S', letters: '\u0053\u24C8\uFF33\u1E9E\u015A\u1E64\u015C\u1E60\u0160\u1E66\u1E62\u1E68\u0218\u015E\u2C7E\uA7A8\uA784'},
|
||||
{base: 'T', letters: '\u0054\u24C9\uFF34\u1E6A\u0164\u1E6C\u021A\u0162\u1E70\u1E6E\u0166\u01AC\u01AE\u023E\uA786'},
|
||||
{base: 'TZ',letters: '\uA728'},
|
||||
{base: 'U', letters: '\u0055\u24CA\uFF35\u00D9\u00DA\u00DB\u0168\u1E78\u016A\u1E7A\u016C\u00DC\u01DB\u01D7\u01D5\u01D9\u1EE6\u016E\u0170\u01D3\u0214\u0216\u01AF\u1EEA\u1EE8\u1EEE\u1EEC\u1EF0\u1EE4\u1E72\u0172\u1E76\u1E74\u0244'},
|
||||
{base: 'V', letters: '\u0056\u24CB\uFF36\u1E7C\u1E7E\u01B2\uA75E\u0245'},
|
||||
{base: 'VY',letters: '\uA760'},
|
||||
{base: 'W', letters: '\u0057\u24CC\uFF37\u1E80\u1E82\u0174\u1E86\u1E84\u1E88\u2C72'},
|
||||
{base: 'X', letters: '\u0058\u24CD\uFF38\u1E8A\u1E8C'},
|
||||
{base: 'Y', letters: '\u0059\u24CE\uFF39\u1EF2\u00DD\u0176\u1EF8\u0232\u1E8E\u0178\u1EF6\u1EF4\u01B3\u024E\u1EFE'},
|
||||
{base: 'Z', letters: '\u005A\u24CF\uFF3A\u0179\u1E90\u017B\u017D\u1E92\u1E94\u01B5\u0224\u2C7F\u2C6B\uA762'},
|
||||
{base: 'a', letters: '\u0061\u24D0\uFF41\u1E9A\u00E0\u00E1\u00E2\u1EA7\u1EA5\u1EAB\u1EA9\u00E3\u0101\u0103\u1EB1\u1EAF\u1EB5\u1EB3\u0227\u01E1\u00E4\u01DF\u1EA3\u00E5\u01FB\u01CE\u0201\u0203\u1EA1\u1EAD\u1EB7\u1E01\u0105\u2C65\u0250'},
|
||||
{base: 'aa',letters: '\uA733'},
|
||||
{base: 'ae',letters: '\u00E6\u01FD\u01E3'},
|
||||
{base: 'ao',letters: '\uA735'},
|
||||
{base: 'au',letters: '\uA737'},
|
||||
{base: 'av',letters: '\uA739\uA73B'},
|
||||
{base: 'ay',letters: '\uA73D'},
|
||||
{base: 'b', letters: '\u0062\u24D1\uFF42\u1E03\u1E05\u1E07\u0180\u0183\u0253'},
|
||||
{base: 'c', letters: '\u0063\u24D2\uFF43\u0107\u0109\u010B\u010D\u00E7\u1E09\u0188\u023C\uA73F\u2184'},
|
||||
{base: 'd', letters: '\u0064\u24D3\uFF44\u1E0B\u010F\u1E0D\u1E11\u1E13\u1E0F\u0111\u018C\u0256\u0257\uA77A'},
|
||||
{base: 'dz',letters: '\u01F3\u01C6'},
|
||||
{base: 'e', letters: '\u0065\u24D4\uFF45\u00E8\u00E9\u00EA\u1EC1\u1EBF\u1EC5\u1EC3\u1EBD\u0113\u1E15\u1E17\u0115\u0117\u00EB\u1EBB\u011B\u0205\u0207\u1EB9\u1EC7\u0229\u1E1D\u0119\u1E19\u1E1B\u0247\u025B\u01DD'},
|
||||
{base: 'f', letters: '\u0066\u24D5\uFF46\u1E1F\u0192\uA77C'},
|
||||
{base: 'g', letters: '\u0067\u24D6\uFF47\u01F5\u011D\u1E21\u011F\u0121\u01E7\u0123\u01E5\u0260\uA7A1\u1D79\uA77F'},
|
||||
{base: 'h', letters: '\u0068\u24D7\uFF48\u0125\u1E23\u1E27\u021F\u1E25\u1E29\u1E2B\u1E96\u0127\u2C68\u2C76\u0265'},
|
||||
{base: 'hv',letters: '\u0195'},
|
||||
{base: 'i', letters: '\u0069\u24D8\uFF49\u00EC\u00ED\u00EE\u0129\u012B\u012D\u00EF\u1E2F\u1EC9\u01D0\u0209\u020B\u1ECB\u012F\u1E2D\u0268\u0131'},
|
||||
{base: 'j', letters: '\u006A\u24D9\uFF4A\u0135\u01F0\u0249'},
|
||||
{base: 'k', letters: '\u006B\u24DA\uFF4B\u1E31\u01E9\u1E33\u0137\u1E35\u0199\u2C6A\uA741\uA743\uA745\uA7A3'},
|
||||
{base: 'l', letters: '\u006C\u24DB\uFF4C\u0140\u013A\u013E\u1E37\u1E39\u013C\u1E3D\u1E3B\u017F\u0142\u019A\u026B\u2C61\uA749\uA781\uA747'},
|
||||
{base: 'lj',letters: '\u01C9'},
|
||||
{base: 'm', letters: '\u006D\u24DC\uFF4D\u1E3F\u1E41\u1E43\u0271\u026F'},
|
||||
{base: 'n', letters: '\u006E\u24DD\uFF4E\u01F9\u0144\u00F1\u1E45\u0148\u1E47\u0146\u1E4B\u1E49\u019E\u0272\u0149\uA791\uA7A5'},
|
||||
{base: 'nj',letters: '\u01CC'},
|
||||
{base: 'o', letters: '\u006F\u24DE\uFF4F\u00F2\u00F3\u00F4\u1ED3\u1ED1\u1ED7\u1ED5\u00F5\u1E4D\u022D\u1E4F\u014D\u1E51\u1E53\u014F\u022F\u0231\u00F6\u022B\u1ECF\u0151\u01D2\u020D\u020F\u01A1\u1EDD\u1EDB\u1EE1\u1EDF\u1EE3\u1ECD\u1ED9\u01EB\u01ED\u00F8\u01FF\u0254\uA74B\uA74D\u0275'},
|
||||
{base: 'oi',letters: '\u01A3'},
|
||||
{base: 'ou',letters: '\u0223'},
|
||||
{base: 'oo',letters: '\uA74F'},
|
||||
{base: 'p',letters: '\u0070\u24DF\uFF50\u1E55\u1E57\u01A5\u1D7D\uA751\uA753\uA755'},
|
||||
{base: 'q',letters: '\u0071\u24E0\uFF51\u024B\uA757\uA759'},
|
||||
{base: 'r',letters: '\u0072\u24E1\uFF52\u0155\u1E59\u0159\u0211\u0213\u1E5B\u1E5D\u0157\u1E5F\u024D\u027D\uA75B\uA7A7\uA783'},
|
||||
{base: 's',letters: '\u0073\u24E2\uFF53\u00DF\u015B\u1E65\u015D\u1E61\u0161\u1E67\u1E63\u1E69\u0219\u015F\u023F\uA7A9\uA785\u1E9B'},
|
||||
{base: 't',letters: '\u0074\u24E3\uFF54\u1E6B\u1E97\u0165\u1E6D\u021B\u0163\u1E71\u1E6F\u0167\u01AD\u0288\u2C66\uA787'},
|
||||
{base: 'tz',letters: '\uA729'},
|
||||
{base: 'u',letters: '\u0075\u24E4\uFF55\u00F9\u00FA\u00FB\u0169\u1E79\u016B\u1E7B\u016D\u00FC\u01DC\u01D8\u01D6\u01DA\u1EE7\u016F\u0171\u01D4\u0215\u0217\u01B0\u1EEB\u1EE9\u1EEF\u1EED\u1EF1\u1EE5\u1E73\u0173\u1E77\u1E75\u0289'},
|
||||
{base: 'v',letters: '\u0076\u24E5\uFF56\u1E7D\u1E7F\u028B\uA75F\u028C'},
|
||||
{base: 'vy',letters: '\uA761'},
|
||||
{base: 'w',letters: '\u0077\u24E6\uFF57\u1E81\u1E83\u0175\u1E87\u1E85\u1E98\u1E89\u2C73'},
|
||||
{base: 'x',letters: '\u0078\u24E7\uFF58\u1E8B\u1E8D'},
|
||||
{base: 'y',letters: '\u0079\u24E8\uFF59\u1EF3\u00FD\u0177\u1EF9\u0233\u1E8F\u00FF\u1EF7\u1E99\u1EF5\u01B4\u024F\u1EFF'},
|
||||
{base: 'z',letters: '\u007A\u24E9\uFF5A\u017A\u1E91\u017C\u017E\u1E93\u1E95\u01B6\u0225\u0240\u2C6C\uA763'}
|
||||
]
|
||||
|
||||
const initNeutraliser = () => {
|
||||
for (const diacritic of defaultAccentsDiacritics) {
|
||||
const letters = diacritic.letters
|
||||
for (let i = 0; i < letters.length; i++) {
|
||||
diacriticsMap[letters[i]] = diacritic.base
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* eslint-disable no-control-regex */
|
||||
const removeDiacritics = str => str.replace(/[^\u0000-\u007E]/g, a => diacriticsMap[a] || a)
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
searchAccentNeutralise: false
|
||||
})
|
||||
|
||||
$.BootstrapTable = class extends $.BootstrapTable {
|
||||
init () {
|
||||
if (this.options.searchAccentNeutralise) {
|
||||
initNeutraliser()
|
||||
}
|
||||
super.init()
|
||||
}
|
||||
|
||||
initSearch () {
|
||||
if (this.options.sidePagination !== 'server') {
|
||||
let s = this.searchText && this.searchText.toLowerCase()
|
||||
const f = $.isEmptyObject(this.filterColumns) ? null : this.filterColumns
|
||||
|
||||
// Check filter
|
||||
this.data = f ? this.options.data.filter((item, i) => {
|
||||
for (const key in f) {
|
||||
if (item[key] !== f[key]) {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}) : this.options.data
|
||||
|
||||
this.data = s ? this.options.data.filter((item, i) => {
|
||||
for (let [key, value] of Object.entries(item)) {
|
||||
key = $.isNumeric(key) ? parseInt(key, 10) : key
|
||||
const column = this.columns[this.fieldsColumnsIndex[key]]
|
||||
const j = this.header.fields.indexOf(key)
|
||||
|
||||
if (column && column.searchFormatter) {
|
||||
value = $.fn.bootstrapTable.utils.calculateObjectValue(column,
|
||||
this.header.formatters[j], [value, item, i], value)
|
||||
}
|
||||
|
||||
const index = this.header.fields.indexOf(key)
|
||||
if (index !== -1 && this.header.searchables[index] && typeof value === 'string') {
|
||||
if (this.options.searchAccentNeutralise) {
|
||||
value = removeDiacritics(value)
|
||||
s = removeDiacritics(s)
|
||||
}
|
||||
if (this.options.strictSearch) {
|
||||
if ((`${value}`).toLowerCase() === s) {
|
||||
return true
|
||||
}
|
||||
} else {
|
||||
if ((`${value}`).toLowerCase().includes(s)) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}) : this.data
|
||||
}
|
||||
}
|
||||
}
|
||||
})(jQuery)
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "Accent Neutralise",
|
||||
"version": "1.0.0",
|
||||
"description": "Plugin to neutralise the words.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/accent-neutralise",
|
||||
"example": "#",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-accent-neutralise",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/accent-neutralise"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "djhvscf",
|
||||
"image": "https://avatars1.githubusercontent.com/u/4496763"
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "Auto Refresh",
|
||||
"version": "1.0.0",
|
||||
"description": "Plugin to automatically refresh the table on an interval.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/auto-refresh",
|
||||
"example": "#",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-auto-refresh",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/auto-refresh"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "fenichaler",
|
||||
"image": "https://avatars.githubusercontent.com/u/3437075"
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "Cookie",
|
||||
"version": "1.2.1",
|
||||
"description": "Plugin to use the cookie of the browser.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/cookie",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/cookie.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-cookie",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/cookie"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "djhvscf",
|
||||
"image": "https://avatars1.githubusercontent.com/u/4496763"
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "Copy Rows",
|
||||
"version": "1.0.0",
|
||||
"description": "Allows pushing of selected column data to the clipboard.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/copy-rows",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/copy-rows.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "copy-rows",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/copy-rows"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "Homer Glascock",
|
||||
"image": "https://avatars1.githubusercontent.com/u/5546710"
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "DeferURL",
|
||||
"version": "1.0.0",
|
||||
"description": "Plugin to defer server side processing.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/defer-url",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/defer-url.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-defer-url",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/defer-url"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "rubensa",
|
||||
"image": "https://avatars1.githubusercontent.com/u/1469340"
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "Table Editable",
|
||||
"version": "1.1.0",
|
||||
"description": "Use the x-editable to in-place editing your table.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/editable",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/editable.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "x-editable",
|
||||
"url": "https://github.com/vitalets/x-editable"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "wenzhixin",
|
||||
"image": "https://avatars1.githubusercontent.com/u/2117018"
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "Table Export",
|
||||
"version": "1.1.0",
|
||||
"description": "Export your table data to JSON, XML, CSV, TXT, SQL, Word, Excel, PNG, PDF.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/export",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/export.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "tableExport.jquery.plugin",
|
||||
"url": "https://github.com/hhurz/tableExport.jquery.plugin"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "wenzhixin",
|
||||
"image": "https://avatars1.githubusercontent.com/u/2117018"
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "Filter Control",
|
||||
"version": "2.1.0",
|
||||
"description": "Plugin to add input/select element on the top of the columns in order to filter the data.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/filter-control",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/filter-control.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-filter-control",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/filter-control"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "djhvscf",
|
||||
"image": "https://avatars1.githubusercontent.com/u/4496763"
|
||||
}
|
||||
}
|
@ -1,12 +0,0 @@
|
||||
{
|
||||
"name": "Group By V2",
|
||||
"version": "1.0.0",
|
||||
"description": "Group the data by field",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/group-by-v2",
|
||||
"example": "",
|
||||
"plugins": [],
|
||||
"author": {
|
||||
"name": "Knoxvillekm",
|
||||
"image": "https://avatars3.githubusercontent.com/u/11072464"
|
||||
}
|
||||
}
|
@ -1,53 +0,0 @@
|
||||
table.treetable tbody tr td {
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
table.treetable span {
|
||||
background-position: center left;
|
||||
background-repeat: no-repeat;
|
||||
padding: .2em 0 .2em 1.5em;
|
||||
}
|
||||
|
||||
table.treetable tr.collapsed span.indenter a {
|
||||
background-image: url(data:image/png;base64,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);
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
table.treetable tr.expanded span.indenter a {
|
||||
background-image: url(data:image/png;base64,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);
|
||||
padding-right: 12px;
|
||||
}
|
||||
|
||||
table.treetable tr.branch {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
table.treetable tr.selected {
|
||||
background-color: #3875d7;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
table.treetable tr span.indenter a {
|
||||
outline: none; /* Expander shows outline after upgrading to 3.0 (#141) */
|
||||
}
|
||||
|
||||
table.treetable tr.collapsed.selected span.indenter a {
|
||||
background-image: url(data:image/png;base64,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);
|
||||
}
|
||||
|
||||
table.treetable tr.expanded.selected span.indenter a {
|
||||
background-image: url(data:image/png;base64,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);
|
||||
}
|
||||
|
||||
table.treetable tr.accept {
|
||||
background-color: #a3bce4;
|
||||
color: #fff
|
||||
}
|
||||
|
||||
table.treetable tr.collapsed.accept td span.indenter a {
|
||||
background-image: url(data:image/x-png;base64,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);
|
||||
}
|
||||
|
||||
table.treetable tr.expanded.accept td span.indenter a {
|
||||
background-image: url(data:image/x-png;base64,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);
|
||||
}
|
@ -1,243 +0,0 @@
|
||||
/**
|
||||
* @author: Dennis Hernández
|
||||
* @webSite: http://djhvscf.github.io/Blog
|
||||
* @version: v1.1.0
|
||||
*/
|
||||
|
||||
!function ($) {
|
||||
|
||||
'use strict';
|
||||
|
||||
var originalRowAttr,
|
||||
dataTTId = 'data-tt-id',
|
||||
dataTTParentId = 'data-tt-parent-id',
|
||||
obj = {},
|
||||
parentId = undefined;
|
||||
|
||||
var getParentRowId = function (that, id) {
|
||||
var parentRows = that.$body.find('tr').not('[' + 'data-tt-parent-id]');
|
||||
|
||||
for (var i = 0; i < parentRows.length; i++) {
|
||||
if (i === id) {
|
||||
return $(parentRows[i]).attr('data-tt-id');
|
||||
}
|
||||
}
|
||||
|
||||
return undefined;
|
||||
};
|
||||
|
||||
var sumData = function (that, data) {
|
||||
var sumRow = {};
|
||||
$.each(data, function (i, row) {
|
||||
if (!row.IsParent) {
|
||||
for (var prop in row) {
|
||||
if (!isNaN(parseFloat(row[prop]))) {
|
||||
if (that.columns[that.fieldsColumnsIndex[prop]].groupBySumGroup) {
|
||||
if (sumRow[prop] === undefined) {
|
||||
sumRow[prop] = 0;
|
||||
}
|
||||
sumRow[prop] += +row[prop];
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
return sumRow;
|
||||
};
|
||||
|
||||
var rowAttr = function (row, index) {
|
||||
//Call the User Defined Function
|
||||
originalRowAttr.apply([row, index]);
|
||||
|
||||
obj[dataTTId.toString()] = index;
|
||||
|
||||
if (!row.IsParent) {
|
||||
obj[dataTTParentId.toString()] = parentId === undefined ? index : parentId;
|
||||
} else {
|
||||
parentId = index;
|
||||
delete obj[dataTTParentId.toString()];
|
||||
}
|
||||
|
||||
return obj;
|
||||
};
|
||||
|
||||
var setObjectKeys = function () {
|
||||
// From https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Object/keys
|
||||
Object.keys = function (o) {
|
||||
if (o !== Object(o)) {
|
||||
throw new TypeError('Object.keys called on a non-object');
|
||||
}
|
||||
var k = [],
|
||||
p;
|
||||
for (p in o) {
|
||||
if (Object.prototype.hasOwnProperty.call(o, p)) {
|
||||
k.push(p);
|
||||
}
|
||||
}
|
||||
return k;
|
||||
}
|
||||
};
|
||||
|
||||
var getDataArrayFromItem = function (that, item) {
|
||||
var itemDataArray = [];
|
||||
for (var i = 0; i < that.options.groupByField.length; i++) {
|
||||
itemDataArray.push(item[that.options.groupByField[i]]);
|
||||
}
|
||||
|
||||
return itemDataArray;
|
||||
};
|
||||
|
||||
var getNewRow = function (that, result, index) {
|
||||
var newRow = {};
|
||||
for (var i = 0; i < that.options.groupByField.length; i++) {
|
||||
newRow[that.options.groupByField[i].toString()] = result[index][0][that.options.groupByField[i]];
|
||||
}
|
||||
|
||||
newRow.IsParent = true;
|
||||
|
||||
return newRow;
|
||||
};
|
||||
|
||||
var groupBy = function (array, f) {
|
||||
var groups = {};
|
||||
$.each(array, function (i, o) {
|
||||
var group = JSON.stringify(f(o));
|
||||
groups[group] = groups[group] || [];
|
||||
groups[group].push(o);
|
||||
});
|
||||
return Object.keys(groups).map(function (group) {
|
||||
return groups[group];
|
||||
});
|
||||
};
|
||||
|
||||
var makeGrouped = function (that, data) {
|
||||
var newData = [],
|
||||
sumRow = {};
|
||||
|
||||
var result = groupBy(data, function (item) {
|
||||
return getDataArrayFromItem(that, item);
|
||||
});
|
||||
|
||||
for (var i = 0; i < result.length; i++) {
|
||||
result[i].unshift(getNewRow(that, result, i));
|
||||
if (that.options.groupBySumGroup) {
|
||||
sumRow = sumData(that, result[i]);
|
||||
if (!$.isEmptyObject(sumRow)) {
|
||||
result[i].push(sumRow);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
newData = newData.concat.apply(newData, result);
|
||||
|
||||
if (!that.options.loaded && newData.length > 0) {
|
||||
that.options.loaded = true;
|
||||
that.options.originalData = that.options.data;
|
||||
that.options.data = newData;
|
||||
}
|
||||
|
||||
return newData;
|
||||
};
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
groupBy: false,
|
||||
groupByField: [],
|
||||
groupBySumGroup: false,
|
||||
groupByInitExpanded: undefined, //node, 'all'
|
||||
//internal variables
|
||||
loaded: false,
|
||||
originalData: undefined
|
||||
});
|
||||
|
||||
$.fn.bootstrapTable.methods.push('collapseAll', 'expandAll', 'refreshGroupByField');
|
||||
|
||||
$.extend($.fn.bootstrapTable.COLUMN_DEFAULTS, {
|
||||
groupBySumGroup: false
|
||||
});
|
||||
|
||||
var BootstrapTable = $.fn.bootstrapTable.Constructor,
|
||||
_init = BootstrapTable.prototype.init,
|
||||
_initData = BootstrapTable.prototype.initData;
|
||||
|
||||
BootstrapTable.prototype.init = function () {
|
||||
//Temporal validation
|
||||
if (!this.options.sortName) {
|
||||
if ((this.options.groupBy) && (this.options.groupByField.length > 0)) {
|
||||
var that = this;
|
||||
|
||||
// Compatibility: IE < 9 and old browsers
|
||||
if (!Object.keys) {
|
||||
$.fn.bootstrapTable.utils.objectKeys();
|
||||
}
|
||||
|
||||
//Make sure that the internal variables are set correctly
|
||||
this.options.loaded = false;
|
||||
this.options.originalData = undefined;
|
||||
|
||||
originalRowAttr = this.options.rowAttributes;
|
||||
this.options.rowAttributes = rowAttr;
|
||||
this.$el.off('post-body.bs.table').on('post-body.bs.table', function () {
|
||||
that.$el.treetable({
|
||||
expandable: true,
|
||||
onNodeExpand: function () {
|
||||
if (that.options.height) {
|
||||
that.resetHeader();
|
||||
}
|
||||
},
|
||||
onNodeCollapse: function () {
|
||||
if (that.options.height) {
|
||||
that.resetHeader();
|
||||
}
|
||||
}
|
||||
}, true);
|
||||
|
||||
if (that.options.groupByInitExpanded !== undefined) {
|
||||
if (typeof that.options.groupByInitExpanded === 'number') {
|
||||
that.expandNode(that.options.groupByInitExpanded);
|
||||
} else if (that.options.groupByInitExpanded.toLowerCase() === 'all') {
|
||||
that.expandAll();
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
_init.apply(this, Array.prototype.slice.apply(arguments));
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.initData = function (data, type) {
|
||||
//Temporal validation
|
||||
if (!this.options.sortName) {
|
||||
if ((this.options.groupBy) && (this.options.groupByField.length > 0)) {
|
||||
|
||||
this.options.groupByField = typeof this.options.groupByField === 'string' ?
|
||||
this.options.groupByField.replace('[', '').replace(']', '')
|
||||
.replace(/ /g, '').toLowerCase().split(',') : this.options.groupByField;
|
||||
|
||||
data = makeGrouped(this, data ? data : this.options.data);
|
||||
}
|
||||
}
|
||||
_initData.apply(this, [data, type]);
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.expandAll = function () {
|
||||
this.$el.treetable('expandAll');
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.collapseAll = function () {
|
||||
this.$el.treetable('collapseAll');
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.expandNode = function (id) {
|
||||
id = getParentRowId(this, id);
|
||||
if (id !== undefined) {
|
||||
this.$el.treetable('expandNode', id);
|
||||
}
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.refreshGroupByField = function (groupByFields) {
|
||||
if (!$.fn.bootstrapTable.utils.compareObjects(this.options.groupByField, groupByFields)) {
|
||||
this.options.groupByField = groupByFields;
|
||||
this.load(this.options.originalData);
|
||||
}
|
||||
};
|
||||
}(jQuery);
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "Group By",
|
||||
"version": "1.1.0",
|
||||
"description": "Plugin to group the data by fields.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/group-by",
|
||||
"example": "#",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-group-by",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/group-by"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "djhvscf",
|
||||
"image": "https://avatars1.githubusercontent.com/u/4496763"
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "i18n Enhance",
|
||||
"version": "1.0.0",
|
||||
"description": "Plugin to add i18n API in order to change column's title and table locale.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/i18n-enhance",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/i18n-enhance.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-i18n-enhance",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/i18n-enhance"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "Jewway",
|
||||
"image": "https://avatars0.githubusercontent.com/u/3501899"
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "Key Events",
|
||||
"version": "1.0.0",
|
||||
"description": "Plugin to support the key events in the bootstrap table.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/key-events",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/key-events.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-key-events",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/key-events"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "djhvscf",
|
||||
"image": "https://avatars1.githubusercontent.com/u/4496763"
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "Mobile",
|
||||
"version": "1.1.0",
|
||||
"description": "Plugin to support the responsive feature.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/mobile",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/mobile.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-mobile",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/mobile"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "djhvscf",
|
||||
"image": "https://avatars1.githubusercontent.com/u/4496763"
|
||||
}
|
||||
}
|
@ -1,88 +0,0 @@
|
||||
/**
|
||||
* @author Homer Glascock <HopGlascock@gmail.com>
|
||||
* @version: v1.0.0
|
||||
*/
|
||||
|
||||
!function ($) {
|
||||
"use strict";
|
||||
|
||||
var sprintf = $.fn.bootstrapTable.utils.sprintf;
|
||||
|
||||
var reInit = function (self) {
|
||||
self.initHeader();
|
||||
self.initSearch();
|
||||
self.initPagination();
|
||||
self.initBody();
|
||||
};
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
showToggleBtn: false,
|
||||
multiToggleDefaults: [], //column names go here
|
||||
});
|
||||
|
||||
$.fn.bootstrapTable.methods.push('hideAllColumns', 'showAllColumns');
|
||||
|
||||
var BootstrapTable = $.fn.bootstrapTable.Constructor,
|
||||
_initToolbar = BootstrapTable.prototype.initToolbar;
|
||||
|
||||
BootstrapTable.prototype.initToolbar = function () {
|
||||
|
||||
_initToolbar.apply(this, Array.prototype.slice.apply(arguments));
|
||||
|
||||
var that = this,
|
||||
$btnGroup = this.$toolbar.find('>.btn-group');
|
||||
|
||||
if (typeof this.options.multiToggleDefaults === 'string') {
|
||||
this.options.multiToggleDefaults = JSON.parse(this.options.multiToggleDefaults);
|
||||
}
|
||||
|
||||
if (this.options.showToggleBtn && this.options.showColumns) {
|
||||
var showbtn = "<button class='btn btn-default hidden' id='showAllBtn'><span class='glyphicon glyphicon-resize-full icon-zoom-in'></span></button>",
|
||||
hidebtn = "<button class='btn btn-default' id='hideAllBtn'><span class='glyphicon glyphicon-resize-small icon-zoom-out'></span></button>";
|
||||
|
||||
$btnGroup.append(showbtn + hidebtn);
|
||||
|
||||
$btnGroup.find('#showAllBtn').click(function () { that.showAllColumns();
|
||||
$btnGroup.find('#hideAllBtn').toggleClass('hidden');
|
||||
$btnGroup.find('#showAllBtn').toggleClass('hidden');
|
||||
});
|
||||
$btnGroup.find('#hideAllBtn').click(function () { that.hideAllColumns();
|
||||
$btnGroup.find('#hideAllBtn').toggleClass('hidden');
|
||||
$btnGroup.find('#showAllBtn').toggleClass('hidden');
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.hideAllColumns = function () {
|
||||
var that = this,
|
||||
defaults = that.options.multiToggleDefaults;
|
||||
|
||||
$.each(this.columns, function (index, column) {
|
||||
//if its one of the defaults dont touch it
|
||||
if (defaults.indexOf(column.field) == -1 && column.switchable) {
|
||||
column.visible = false;
|
||||
var $items = that.$toolbar.find('.keep-open input').prop('disabled', false);
|
||||
$items.filter(sprintf('[value="%s"]', index)).prop('checked', false);
|
||||
}
|
||||
});
|
||||
|
||||
reInit(that);
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.showAllColumns = function () {
|
||||
var that = this;
|
||||
$.each(this.columns, function (index, column) {
|
||||
if (column.switchable) {
|
||||
column.visible = true;
|
||||
}
|
||||
|
||||
var $items = that.$toolbar.find('.keep-open input').prop('disabled', false);
|
||||
$items.filter(sprintf('[value="%s"]', index)).prop('checked', true);
|
||||
});
|
||||
|
||||
reInit(that);
|
||||
|
||||
that.toggleColumn(0, that.columns[0].visible, false);
|
||||
};
|
||||
|
||||
}(jQuery);
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "Multi Column Toggle",
|
||||
"version": "1.0.0",
|
||||
"description": "Allows hiding and showing of multiple columns at once.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/multi-column-toggle",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/multi-column-toggle.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "multi-column-toggle",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/multi-column-toggle"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "Homer Glascock",
|
||||
"image": "https://avatars1.githubusercontent.com/u/5546710"
|
||||
}
|
||||
}
|
@ -1,71 +0,0 @@
|
||||
/**
|
||||
* @author: Dennis Hernández
|
||||
* @webSite: http://djhvscf.github.io/Blog
|
||||
* @version: v1.0.0
|
||||
*/
|
||||
|
||||
!function ($) {
|
||||
|
||||
'use strict';
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
multipleSearch: false,
|
||||
delimeter: " "
|
||||
});
|
||||
|
||||
var BootstrapTable = $.fn.bootstrapTable.Constructor,
|
||||
_initSearch = BootstrapTable.prototype.initSearch;
|
||||
|
||||
BootstrapTable.prototype.initSearch = function () {
|
||||
if (this.options.multipleSearch) {
|
||||
if (this.searchText === undefined) {
|
||||
return;
|
||||
}
|
||||
var strArray = this.searchText.split(this.options.delimeter),
|
||||
that = this,
|
||||
f = $.isEmptyObject(this.filterColumns) ? null : this.filterColumns,
|
||||
dataFiltered = [];
|
||||
|
||||
if (strArray.length === 1) {
|
||||
_initSearch.apply(this, Array.prototype.slice.apply(arguments));
|
||||
} else {
|
||||
for (var i = 0; i < strArray.length; i++) {
|
||||
var str = strArray[i].trim();
|
||||
dataFiltered = str ? $.grep(dataFiltered.length === 0 ? this.options.data : dataFiltered, function (item, i) {
|
||||
for (var key in item) {
|
||||
key = $.isNumeric(key) ? parseInt(key, 10) : key;
|
||||
var value = item[key],
|
||||
column = that.columns[that.fieldsColumnsIndex[key]],
|
||||
j = $.inArray(key, that.header.fields);
|
||||
|
||||
// Fix #142: search use formated data
|
||||
if (column && column.searchFormatter) {
|
||||
value = $.fn.bootstrapTable.utils.calculateObjectValue(column,
|
||||
that.header.formatters[j], [value, item, i], value);
|
||||
}
|
||||
|
||||
var index = $.inArray(key, that.header.fields);
|
||||
if (index !== -1 && that.header.searchables[index] && (typeof value === 'string' || typeof value === 'number')) {
|
||||
if (that.options.strictSearch) {
|
||||
if ((value + '').toLowerCase() === str) {
|
||||
return true;
|
||||
}
|
||||
} else {
|
||||
if ((value + '').toLowerCase().indexOf(str) !== -1) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}) : this.data;
|
||||
}
|
||||
|
||||
this.data = dataFiltered;
|
||||
}
|
||||
} else {
|
||||
_initSearch.apply(this, Array.prototype.slice.apply(arguments));
|
||||
}
|
||||
};
|
||||
|
||||
}(jQuery);
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "Multiple Search",
|
||||
"version": "1.0.0",
|
||||
"description": "Plugin to support the multiple search.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/multiple-search",
|
||||
"example": "#",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-multiple-search",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/multiple-search"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "djhvscf",
|
||||
"image": "https://avatars1.githubusercontent.com/u/4496763"
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
.multiple-select-row-selected {
|
||||
background: lightBlue
|
||||
}
|
||||
|
||||
.table tbody tr:hover td,
|
||||
.table tbody tr:hover th {
|
||||
background-color: transparent;
|
||||
}
|
||||
|
||||
|
||||
.table-striped tbody tr:nth-child(odd):hover td {
|
||||
background-color: #F9F9F9;
|
||||
}
|
||||
|
||||
.fixed-table-container tbody .selected td {
|
||||
background: lightBlue;
|
||||
}
|
@ -1,127 +0,0 @@
|
||||
/**
|
||||
* @author: Dennis Hernández
|
||||
* @webSite: http://djhvscf.github.io/Blog
|
||||
* @version: v1.0.0
|
||||
*/
|
||||
|
||||
!function ($) {
|
||||
|
||||
'use strict';
|
||||
|
||||
document.onselectstart = function() {
|
||||
return false;
|
||||
};
|
||||
|
||||
var getTableObjectFromCurrentTarget = function (currentTarget) {
|
||||
currentTarget = $(currentTarget);
|
||||
return currentTarget.is("table") ? currentTarget : currentTarget.parents().find(".table");
|
||||
};
|
||||
|
||||
var getRow = function (target) {
|
||||
target = $(target);
|
||||
return target.parent().parent();
|
||||
};
|
||||
|
||||
var onRowClick = function (e) {
|
||||
var that = getTableObjectFromCurrentTarget(e.currentTarget);
|
||||
|
||||
if (window.event.ctrlKey) {
|
||||
toggleRow(e.currentTarget, that, false, false);
|
||||
}
|
||||
|
||||
if (window.event.button === 0) {
|
||||
if (!window.event.ctrlKey && !window.event.shiftKey) {
|
||||
clearAll(that);
|
||||
toggleRow(e.currentTarget, that, false, false);
|
||||
}
|
||||
|
||||
if (window.event.shiftKey) {
|
||||
selectRowsBetweenIndexes([that.bootstrapTable("getOptions").multipleSelectRowLastSelectedRow.rowIndex, e.currentTarget.rowIndex], that)
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var onCheckboxChange = function (e) {
|
||||
var that = getTableObjectFromCurrentTarget(e.currentTarget);
|
||||
clearAll(that);
|
||||
toggleRow(getRow(e.currentTarget), that, false, false);
|
||||
};
|
||||
|
||||
var toggleRow = function (row, that, clearAll, useShift) {
|
||||
if (clearAll) {
|
||||
row = $(row);
|
||||
that.bootstrapTable("getOptions").multipleSelectRowLastSelectedRow = undefined;
|
||||
row.removeClass(that.bootstrapTable("getOptions").multipleSelectRowCssClass);
|
||||
that.bootstrapTable("uncheck", row.data("index"));
|
||||
} else {
|
||||
that.bootstrapTable("getOptions").multipleSelectRowLastSelectedRow = row;
|
||||
row = $(row);
|
||||
if (useShift) {
|
||||
row.addClass(that.bootstrapTable("getOptions").multipleSelectRowCssClass);
|
||||
that.bootstrapTable("check", row.data("index"));
|
||||
} else {
|
||||
if(row.hasClass(that.bootstrapTable("getOptions").multipleSelectRowCssClass)) {
|
||||
row.removeClass(that.bootstrapTable("getOptions").multipleSelectRowCssClass)
|
||||
that.bootstrapTable("uncheck", row.data("index"));
|
||||
} else {
|
||||
row.addClass(that.bootstrapTable("getOptions").multipleSelectRowCssClass);
|
||||
that.bootstrapTable("check", row.data("index"));
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
var selectRowsBetweenIndexes = function (indexes, that) {
|
||||
indexes.sort(function(a, b) {
|
||||
return a - b;
|
||||
});
|
||||
|
||||
for (var i = indexes[0]; i <= indexes[1]; i++) {
|
||||
toggleRow(that.bootstrapTable("getOptions").multipleSelectRowRows[i-1], that, false, true);
|
||||
}
|
||||
};
|
||||
|
||||
var clearAll = function (that) {
|
||||
for (var i = 0; i < that.bootstrapTable("getOptions").multipleSelectRowRows.length; i++) {
|
||||
toggleRow(that.bootstrapTable("getOptions").multipleSelectRowRows[i], that, true, false);
|
||||
}
|
||||
};
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
multipleSelectRow: false,
|
||||
multipleSelectRowCssClass: 'multiple-select-row-selected',
|
||||
//internal variables used by the extension
|
||||
multipleSelectRowLastSelectedRow: undefined,
|
||||
multipleSelectRowRows: []
|
||||
});
|
||||
|
||||
var BootstrapTable = $.fn.bootstrapTable.Constructor,
|
||||
_init = BootstrapTable.prototype.init,
|
||||
_initBody = BootstrapTable.prototype.initBody;
|
||||
|
||||
BootstrapTable.prototype.init = function () {
|
||||
if (this.options.multipleSelectRow) {
|
||||
var that = this;
|
||||
|
||||
//Make sure that the internal variables have the correct value
|
||||
this.options.multipleSelectRowLastSelectedRow = undefined;
|
||||
this.options.multipleSelectRowRows = [];
|
||||
|
||||
this.$el.on("post-body.bs.table", function (e) {
|
||||
setTimeout(function () {
|
||||
that.options.multipleSelectRowRows = that.$body.children();
|
||||
that.options.multipleSelectRowRows.click(onRowClick);
|
||||
that.options.multipleSelectRowRows.find("input[type=checkbox]").change(onCheckboxChange);
|
||||
}, 1);
|
||||
});
|
||||
}
|
||||
|
||||
_init.apply(this, Array.prototype.slice.apply(arguments));
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.clearAllMultipleSelectionRow = function () {
|
||||
clearAll(this);
|
||||
};
|
||||
|
||||
$.fn.bootstrapTable.methods.push('clearAllMultipleSelectionRow');
|
||||
}(jQuery);
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "Multiple Selection Row",
|
||||
"version": "1.0.0",
|
||||
"description": "Plugin to enable the multiple selection row. You can use the ctrl+click to select one row or use ctrl+shift+click to select a range of rows.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/multiple-selection-row",
|
||||
"example": "",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-multiple-selection-row",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/multiple-selection-row"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "djhvscf",
|
||||
"image": "https://avatars1.githubusercontent.com/u/4496763"
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "Multiple Sort",
|
||||
"version": "1.1.0",
|
||||
"description": "Plugin to support the multiple sort.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/multiple-sort",
|
||||
"example": "#",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-multiple-sort",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/multiple-sort"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "dimbslmh",
|
||||
"image": "https://avatars1.githubusercontent.com/u/745635"
|
||||
}
|
||||
}
|
@ -1,67 +0,0 @@
|
||||
/**
|
||||
* @author: Brian Huisman
|
||||
* @webSite: http://www.greywyvern.com
|
||||
* @version: v1.0.0
|
||||
* JS functions to allow natural sorting on bootstrap-table columns
|
||||
* add data-sorter="alphanum" or data-sorter="numericOnly" to any th
|
||||
*
|
||||
* @update Dennis Hernández <http://djhvscf.github.io/Blog>
|
||||
* @update Duane May
|
||||
*/
|
||||
|
||||
function alphanum(a, b) {
|
||||
function chunkify(t) {
|
||||
var tz = [],
|
||||
x = 0,
|
||||
y = -1,
|
||||
n = 0,
|
||||
i,
|
||||
j;
|
||||
|
||||
while (i = (j = t.charAt(x++)).charCodeAt(0)) {
|
||||
var m = (i === 46 || (i >= 48 && i <= 57));
|
||||
if (m !== n) {
|
||||
tz[++y] = "";
|
||||
n = m;
|
||||
}
|
||||
tz[y] += j;
|
||||
}
|
||||
return tz;
|
||||
}
|
||||
|
||||
function stringfy(v) {
|
||||
if (typeof(v) === "number") {
|
||||
v = "" + v;
|
||||
}
|
||||
if (!v) {
|
||||
v = "";
|
||||
}
|
||||
return v;
|
||||
}
|
||||
|
||||
var aa = chunkify(stringfy(a));
|
||||
var bb = chunkify(stringfy(b));
|
||||
|
||||
for (x = 0; aa[x] && bb[x]; x++) {
|
||||
if (aa[x] !== bb[x]) {
|
||||
var c = Number(aa[x]),
|
||||
d = Number(bb[x]);
|
||||
|
||||
if (c == aa[x] && d == bb[x]) {
|
||||
return c - d;
|
||||
} else {
|
||||
return (aa[x] > bb[x]) ? 1 : -1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return aa.length - bb.length;
|
||||
}
|
||||
|
||||
function numericOnly(a, b) {
|
||||
function stripNonNumber(s) {
|
||||
s = s.replace(new RegExp(/[^0-9]/g), "");
|
||||
return parseInt(s, 10);
|
||||
}
|
||||
|
||||
return stripNonNumber(a) - stripNonNumber(b);
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "Natural Sorting",
|
||||
"version": "1.0.0",
|
||||
"description": "Plugin to support the natural sorting.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/natural-sorting",
|
||||
"example": "#",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-natural-sorting",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/natural-sorting"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "GreyWyvern",
|
||||
"image": "https://avatars1.githubusercontent.com/u/137631"
|
||||
}
|
||||
}
|
@ -1,21 +0,0 @@
|
||||
(The MIT License)
|
||||
|
||||
Copyright (c) 2019 doug-the-guy <badlydrawnsun@yahoo.com>
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
in the Software without restriction, including without limitation the rights
|
||||
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
||||
copies of the Software, and to permit persons to whom the Software is
|
||||
furnished to do so, subject to the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be included in
|
||||
all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
||||
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
||||
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
||||
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
|
||||
THE SOFTWARE.
|
@ -1,92 +0,0 @@
|
||||
# Bootstrap Table Pipelining
|
||||
|
||||
Use Plugin: [bootstrap-table-pipeline]
|
||||
|
||||
This plugin enables client side data caching for server side requests which will
|
||||
eliminate the need to issue a new request every page change. This will allow
|
||||
for a performance balance for a large data set between returning all data at once
|
||||
(client side paging) and a new server side request (server side paging).
|
||||
|
||||
There are two new options:
|
||||
- usePipeline: enables this feature
|
||||
- pipelineSize: the size of each cache window
|
||||
|
||||
The size of the pipeline must be evenly divisible by the current page size. This is
|
||||
assured by rounding up to the nearest evenly divisible value. For example, if
|
||||
the pipeline size is 4990 and the current page size is 25, then pipeline size will
|
||||
be dynamically set to 5000.
|
||||
|
||||
The cache windows are computed based on the pipeline size and the total number of rows
|
||||
returned by the server side query. For example, with pipeline size 500 and total rows
|
||||
1300, the cache windows will be:
|
||||
|
||||
[{'lower': 0, 'upper': 499}, {'lower': 500, 'upper': 999}, {'lower': 1000, 'upper': 1499}]
|
||||
|
||||
Using the limit (i.e. the pipelineSize) and offset parameters, the server side request
|
||||
**MUST** return only the data in the requested cache window **AND** the total number of rows.
|
||||
To wit, the server side code must use the offset and limit parameters to prepare the response
|
||||
data.
|
||||
|
||||
On a page change, the new offset is checked if it is within the current cache window. If so,
|
||||
the requested page data is returned from the cached data set. Otherwise, a new server side
|
||||
request will be issued for the new cache window.
|
||||
|
||||
The current cached data is only invalidated on these events:
|
||||
- sorting
|
||||
- searching
|
||||
- page size change
|
||||
- page change moves into a new cache window
|
||||
|
||||
There are two new events:
|
||||
- cached-data-hit.bs.table: issued when cached data is used on a page change
|
||||
- cached-data-reset.bs.table: issued when the cached data is invalidated and new server side request is issued
|
||||
|
||||
## Features
|
||||
|
||||
* Created with Bootstrap 4
|
||||
|
||||
## Usage
|
||||
|
||||
```
|
||||
# assumed import of bootstrap and bootstrap-table assets
|
||||
<script src="/path/to/bootstrap-table-pipeline.js"></script>
|
||||
...
|
||||
<table id="pipeline_table"
|
||||
class="table table-striped"
|
||||
data-method='post'
|
||||
data-use-pipeline="true"
|
||||
data-pipeline-size="5000"
|
||||
data-pagination="true"
|
||||
data-side-pagination="server"
|
||||
data-page-size="50">
|
||||
<thead><tr>
|
||||
<th data-field="type" data-sortable="true">Type</th>
|
||||
<th data-field="value" data-sortable="true">Value</th>
|
||||
<th data-field="date" data-sortable="true">Date</th>
|
||||
</tr></thead>
|
||||
</table>
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
### usePipeline
|
||||
|
||||
* type: Boolean
|
||||
* description: Set true to enable pipelining
|
||||
* default: `false`
|
||||
|
||||
## pipelineSize
|
||||
|
||||
* type: Integer
|
||||
* description: Size of each cache window. Must be greater than 0
|
||||
* default: `1000`
|
||||
|
||||
## Events
|
||||
|
||||
### onCachedDataHit(cached-data-hit.bs.table)
|
||||
|
||||
* Fires when paging was able to use the locally cached data.
|
||||
|
||||
### onCachedDataReset(cached-data-reset.bs.table)
|
||||
|
||||
* Fires when the locally cached data needed to be reset (i.e. on sorting, searching, page size change or paged out of current cache window)
|
@ -1,18 +0,0 @@
|
||||
{
|
||||
"name": "Pipeline",
|
||||
"version": "1.0.0",
|
||||
"description": "Plugin to support a hybrid approach to server/client side paging.",
|
||||
"url": "",
|
||||
"example": "#",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-pipeline",
|
||||
"url": ""
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "doug-the-guy",
|
||||
"image": ""
|
||||
}
|
||||
}
|
||||
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "Reorder Columns",
|
||||
"version": "1.1.0",
|
||||
"description": "Plugin to support the reordering columns feature.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/reorder-columns",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/reorder-columns.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-reorder-columns",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/reorder-columns"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "djhvscf",
|
||||
"image": "https://avatars1.githubusercontent.com/u/4496763"
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "Reorder Rows",
|
||||
"version": "1.0.0",
|
||||
"description": "Plugin to support the reordering rows feature.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/reorder-rows",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/reorder-rows.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-reorder-rows",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/reorder-rows"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "djhvscf",
|
||||
"image": "https://avatars1.githubusercontent.com/u/4496763"
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "Resizable",
|
||||
"version": "1.1.0",
|
||||
"description": "Plugin to support the resizable feature.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/resizable",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/resizable.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-resizable",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/resizable"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "djhvscf",
|
||||
"image": "https://avatars1.githubusercontent.com/u/4496763"
|
||||
}
|
||||
}
|
@ -1,332 +0,0 @@
|
||||
/**
|
||||
* @author: Jewway
|
||||
* @version: v1.1.1
|
||||
*/
|
||||
|
||||
! function ($) {
|
||||
'use strict';
|
||||
|
||||
function getCurrentHeader(that) {
|
||||
var header = that.$header;
|
||||
if (that.options.height) {
|
||||
header = that.$tableHeader;
|
||||
}
|
||||
|
||||
return header;
|
||||
}
|
||||
|
||||
function initFilterValues(that) {
|
||||
if (!$.isEmptyObject(that.filterColumnsPartial)) {
|
||||
var $header = getCurrentHeader(that);
|
||||
|
||||
$.each(that.columns, function (idx, column) {
|
||||
var value = that.filterColumnsPartial[column.field];
|
||||
|
||||
if (column.filter) {
|
||||
if (column.filter.setFilterValue) {
|
||||
var $filter = $header.find('[data-field=' + column.field + '] .filter');
|
||||
column.filter.setFilterValue($filter, column.field, value);
|
||||
} else {
|
||||
var $ele = $header.find('[data-filter-field=' + column.field + ']');
|
||||
switch (column.filter.type) {
|
||||
case 'input':
|
||||
$ele.val(value);
|
||||
case 'select':
|
||||
$ele.val(value).trigger('change');
|
||||
}
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
function createFilter(that, header) {
|
||||
var enableFilter = false,
|
||||
isVisible,
|
||||
html,
|
||||
timeoutId = 0;
|
||||
|
||||
$.each(that.columns, function (i, column) {
|
||||
isVisible = 'hidden';
|
||||
html = null;
|
||||
|
||||
if (!column.visible) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!column.filter) {
|
||||
html = $('<div class="no-filter"></div>');
|
||||
} else {
|
||||
var filterClass = column.filter.class ? ' ' + column.filter.class : '';
|
||||
html = $('<div style="margin: 0px 2px 2px 2px;" class="filter' + filterClass + '">');
|
||||
|
||||
if (column.searchable) {
|
||||
enableFilter = true;
|
||||
isVisible = 'visible'
|
||||
}
|
||||
|
||||
if (column.filter.template) {
|
||||
html.append(column.filter.template(that, column, isVisible));
|
||||
} else {
|
||||
var $filter = $(that.options.filterTemplate[column.filter.type.toLowerCase()](that, column, isVisible));
|
||||
|
||||
switch (column.filter.type) {
|
||||
case 'input':
|
||||
var cpLock = true;
|
||||
$filter.off('compositionstart').on('compositionstart', function (event) {
|
||||
cpLock = false;
|
||||
});
|
||||
|
||||
$filter.off('compositionend').on('compositionend', function (event) {
|
||||
cpLock = true;
|
||||
var $input = $(this);
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = setTimeout(function () {
|
||||
that.onColumnSearch(event, column.field, $input.val());
|
||||
}, that.options.searchTimeOut);
|
||||
});
|
||||
|
||||
$filter.off('keyup').on('keyup', function (event) {
|
||||
if (cpLock) {
|
||||
var $input = $(this);
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = setTimeout(function () {
|
||||
that.onColumnSearch(event, column.field, $input.val());
|
||||
}, that.options.searchTimeOut);
|
||||
}
|
||||
});
|
||||
|
||||
$filter.off('mouseup').on('mouseup', function (event) {
|
||||
var $input = $(this),
|
||||
oldValue = $input.val();
|
||||
|
||||
if (oldValue === "") {
|
||||
return;
|
||||
}
|
||||
|
||||
setTimeout(function () {
|
||||
var newValue = $input.val();
|
||||
|
||||
if (newValue === "") {
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = setTimeout(function () {
|
||||
that.onColumnSearch(event, column.field, newValue);
|
||||
}, that.options.searchTimeOut);
|
||||
}
|
||||
}, 1);
|
||||
});
|
||||
break;
|
||||
case 'select':
|
||||
$filter.on('select2:select', function (event) {
|
||||
that.onColumnSearch(event, column.field, $(this).val());
|
||||
});
|
||||
|
||||
$filter.on("select2:unselecting", function (event) {
|
||||
var $select2 = $(this);
|
||||
event.preventDefault();
|
||||
$select2.val(null).trigger('change');
|
||||
that.searchText = undefined;
|
||||
that.onColumnSearch(event, column.field, $select2.val());
|
||||
});
|
||||
break;
|
||||
}
|
||||
|
||||
html.append($filter);
|
||||
}
|
||||
}
|
||||
|
||||
$.each(header.children().children(), function (i, tr) {
|
||||
tr = $(tr);
|
||||
if (tr.data('field') === column.field) {
|
||||
tr.find('.fht-cell').append(html);
|
||||
return false;
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
if (!enableFilter) {
|
||||
header.find('.filter').hide();
|
||||
}
|
||||
}
|
||||
|
||||
function initSelect2(that) {
|
||||
var $header = getCurrentHeader(that);
|
||||
|
||||
$.each(that.columns, function (idx, column) {
|
||||
if (column.filter && column.filter.type === 'select') {
|
||||
var $selectEle = $header.find('select[data-filter-field="' + column.field + '"]');
|
||||
|
||||
if ($selectEle.length > 0 && !$selectEle.data().select2) {
|
||||
var select2Opts = {
|
||||
placeholder: "",
|
||||
allowClear: true,
|
||||
data: column.filter.data,
|
||||
dropdownParent: that.$el.closest(".bootstrap-table")
|
||||
};
|
||||
|
||||
$selectEle.select2(select2Opts);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
filter: false,
|
||||
filterValues: {},
|
||||
filterTemplate: {
|
||||
input: function (instance, column, isVisible) {
|
||||
return '<input type="text" class="form-control" data-filter-field="' + column.field + '" style="width: 100%; visibility:' + isVisible + '">';
|
||||
},
|
||||
select: function (instance, column, isVisible) {
|
||||
return '<select data-filter-field="' + column.field + '" style="width: 100%; visibility:' + isVisible + '"></select>';
|
||||
}
|
||||
},
|
||||
onColumnSearch: function (field, text) {
|
||||
return false;
|
||||
}
|
||||
});
|
||||
|
||||
$.extend($.fn.bootstrapTable.COLUMN_DEFAULTS, {
|
||||
filter: undefined
|
||||
});
|
||||
|
||||
$.extend($.fn.bootstrapTable.Constructor.EVENTS, {
|
||||
'column-search.bs.table': 'onColumnSearch'
|
||||
});
|
||||
|
||||
var BootstrapTable = $.fn.bootstrapTable.Constructor,
|
||||
_init = BootstrapTable.prototype.init,
|
||||
_initHeader = BootstrapTable.prototype.initHeader,
|
||||
_initSearch = BootstrapTable.prototype.initSearch;
|
||||
|
||||
BootstrapTable.prototype.init = function () {
|
||||
//Make sure that the filtercontrol option is set
|
||||
if (this.options.filter) {
|
||||
var that = this;
|
||||
|
||||
if (that.options.filterTemplate) {
|
||||
that.options.filterTemplate = $.extend({}, $.fn.bootstrapTable.defaults.filterTemplate, that.options.filterTemplate);
|
||||
}
|
||||
|
||||
if (!$.isEmptyObject(that.options.filterValues)) {
|
||||
that.filterColumnsPartial = that.options.filterValues;
|
||||
that.options.filterValues = {};
|
||||
}
|
||||
|
||||
this.$el.on('reset-view.bs.table', function () {
|
||||
//Create controls on $tableHeader if the height is set
|
||||
if (!that.options.height) {
|
||||
return;
|
||||
}
|
||||
|
||||
//Avoid recreate the controls
|
||||
if (that.$tableHeader.find('select').length > 0 || that.$tableHeader.find('input').length > 0) {
|
||||
return;
|
||||
}
|
||||
|
||||
createFilter(that, that.$tableHeader);
|
||||
}).on('post-header.bs.table', function () {
|
||||
var timeoutId = 0;
|
||||
|
||||
initSelect2(that);
|
||||
clearTimeout(timeoutId);
|
||||
timeoutId = setTimeout(function () {
|
||||
initFilterValues(that);
|
||||
}, that.options.searchTimeOut - 1000);
|
||||
}).on('column-switch.bs.table', function (field, checked) {
|
||||
initFilterValues(that);
|
||||
});
|
||||
}
|
||||
|
||||
_init.apply(this, Array.prototype.slice.apply(arguments));
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.initHeader = function () {
|
||||
_initHeader.apply(this, Array.prototype.slice.apply(arguments));
|
||||
if (this.options.filter) {
|
||||
createFilter(this, this.$header);
|
||||
}
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.initSearch = function () {
|
||||
var that = this,
|
||||
filterValues = that.filterColumnsPartial;
|
||||
|
||||
// Filter for client
|
||||
if (that.options.sidePagination === 'client') {
|
||||
this.data = $.grep(this.data, function (row, idx) {
|
||||
for (var field in filterValues) {
|
||||
var column = that.columns[that.fieldsColumnsIndex[field]],
|
||||
filterValue = filterValues[field].toLowerCase(),
|
||||
rowValue = row[field];
|
||||
|
||||
rowValue = $.fn.bootstrapTable.utils.calculateObjectValue(
|
||||
that.header,
|
||||
that.header.formatters[$.inArray(field, that.header.fields)], [rowValue, row, idx], rowValue);
|
||||
|
||||
if (column.filterStrictSearch) {
|
||||
if (!($.inArray(field, that.header.fields) !== -1 &&
|
||||
(typeof rowValue === 'string' || typeof rowValue === 'number') &&
|
||||
rowValue.toString().toLowerCase() === filterValue.toString().toLowerCase())) {
|
||||
return false;
|
||||
}
|
||||
} else {
|
||||
if (!($.inArray(field, that.header.fields) !== -1 &&
|
||||
(typeof rowValue === 'string' || typeof rowValue === 'number') &&
|
||||
(rowValue + '').toLowerCase().indexOf(filterValue) !== -1)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
});
|
||||
}
|
||||
|
||||
_initSearch.apply(this, Array.prototype.slice.apply(arguments));
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.onColumnSearch = function (event, field, value) {
|
||||
if ($.isEmptyObject(this.filterColumnsPartial)) {
|
||||
this.filterColumnsPartial = {};
|
||||
}
|
||||
|
||||
if (value) {
|
||||
this.filterColumnsPartial[field] = value;
|
||||
} else {
|
||||
delete this.filterColumnsPartial[field];
|
||||
}
|
||||
|
||||
this.options.pageNumber = 1;
|
||||
this.onSearch(event);
|
||||
this.trigger('column-search', field, value);
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.setSelect2Data = function (field, data) {
|
||||
var that = this,
|
||||
$header = getCurrentHeader(that),
|
||||
$selectEle = $header.find('select[data-filter-field=\"' + field + '\"]');
|
||||
$selectEle.empty();
|
||||
$selectEle.select2({
|
||||
data: data,
|
||||
placeholder: "",
|
||||
allowClear: true,
|
||||
dropdownParent: that.$el.closest(".bootstrap-table")
|
||||
});
|
||||
|
||||
$.each(this.columns, function (idx, column) {
|
||||
if (column.field === field) {
|
||||
column.filter.data = data;
|
||||
return false;
|
||||
}
|
||||
});
|
||||
};
|
||||
|
||||
BootstrapTable.prototype.setFilterValues = function (values) {
|
||||
this.filterColumnsPartial = values;
|
||||
};
|
||||
|
||||
$.fn.bootstrapTable.methods.push('setSelect2Data');
|
||||
$.fn.bootstrapTable.methods.push('setFilterValues');
|
||||
|
||||
}(jQuery);
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "Select2 Filter",
|
||||
"version": "1.1.0",
|
||||
"description": "Plugin to add select2 filter on the top of the columns in order to filter the data.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/select2-filter",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/select2-filter.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-select2-filter",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/select2-filter"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "Jewway",
|
||||
"image": "https://avatars0.githubusercontent.com/u/3501899"
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "Sticky Header",
|
||||
"version": "1.0.0",
|
||||
"description": "An extension which provides a sticky header for table columns when scrolling on a long page and / or table. Works for tables with many columns and narrow width with horizontal scrollbars too.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/sticky-header",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/sticky-header.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-sticky-header",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/sticky-header"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "vinzloh",
|
||||
"image": "https://avatars0.githubusercontent.com/u/5501845"
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "Toolbar",
|
||||
"version": "2.0.0",
|
||||
"description": "Plugin to support the advanced search.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/toolbar",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/toolbar.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-toolbar",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/toolbar"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "djhvscf",
|
||||
"image": "https://avatars1.githubusercontent.com/u/4496763"
|
||||
}
|
||||
}
|
@ -1 +0,0 @@
|
||||
.table:not(.table-condensed)>tbody>tr>td.treenode{padding-top:0;padding-bottom:0;border-bottom:solid #fff 1px}.table:not(.table-condensed)>tbody>tr:last-child>td.treenode{border-bottom:none}.treenode .text{float:left;display:block;padding-top:6px;padding-bottom:6px}.treenode .vertical,.treenode .vertical.last{float:left;display:block;width:1px;border-left:dashed silver 1px;height:38px;margin-left:8px}.treenode .vertical.last{height:15px}.treenode .space,.treenode .node{float:left;display:block;width:15px;height:5px;margin-top:15px}.treenode .node{border-top:dashed silver 1px}
|
@ -1,130 +0,0 @@
|
||||
/**
|
||||
* @author: KingYang
|
||||
* @webSite: https://github.com/kingyang
|
||||
* @version: v1.0.0
|
||||
*/
|
||||
|
||||
! function ($) {
|
||||
|
||||
'use strict';
|
||||
|
||||
$.extend($.fn.bootstrapTable.defaults, {
|
||||
treeShowField: null,
|
||||
idField: 'id',
|
||||
parentIdField: 'pid',
|
||||
treeVerticalcls: 'vertical',
|
||||
treeVerticalLastcls: 'vertical last',
|
||||
treeSpacecls: 'space',
|
||||
treeNodecls: 'node',
|
||||
treeCellcls: 'treenode',
|
||||
treeTextcls: 'text',
|
||||
onTreeFormatter: function (row) {
|
||||
var that = this,
|
||||
options = that.options,
|
||||
level = row._level || 0,
|
||||
plevel = row._parent && row._parent._level || 0,
|
||||
paddings = [];
|
||||
for (var i = 0; i < plevel; i++) {
|
||||
paddings.push('<i class="' + options.treeVerticalcls + '"></i>');
|
||||
paddings.push('<i class="' + options.treeSpacecls + '"></i>');
|
||||
}
|
||||
|
||||
for (var i = plevel; i < level; i++) {
|
||||
if (row._last && i === (level - 1)) {
|
||||
paddings.push('<i class="' + options.treeVerticalLastcls + '"></i>');
|
||||
} else {
|
||||
paddings.push('<i class="' + options.treeVerticalcls + '"></i>');
|
||||
}
|
||||
paddings.push('<i class="' + options.treeNodecls + '"></i>');
|
||||
}
|
||||
return paddings.join('');
|
||||
}, onGetNodes: function (row, data) {
|
||||
var that = this;
|
||||
var nodes = [];
|
||||
$.each(data, function (i, item) {
|
||||
if (row[that.options.idField] === item[that.options.parentIdField]) {
|
||||
nodes.push(item);
|
||||
}
|
||||
});
|
||||
return nodes;
|
||||
},
|
||||
onCheckLeaf: function (row, data) {
|
||||
if (row.isLeaf !== undefined) {
|
||||
return row.isLeaf;
|
||||
}
|
||||
return !row._nodes || !row._nodes.length;
|
||||
}, onCheckRoot: function (row, data) {
|
||||
var that = this;
|
||||
return !row[that.options.parentIdField];
|
||||
}
|
||||
});
|
||||
|
||||
var BootstrapTable = $.fn.bootstrapTable.Constructor,
|
||||
_initRow = BootstrapTable.prototype.initRow,
|
||||
_initHeader = BootstrapTable.prototype.initHeader;
|
||||
|
||||
BootstrapTable.prototype.initHeader = function () {
|
||||
var that = this;
|
||||
_initHeader.apply(that, Array.prototype.slice.apply(arguments));
|
||||
var treeShowField = that.options.treeShowField;
|
||||
if (treeShowField) {
|
||||
$.each(this.header.fields, function (i, field) {
|
||||
if (treeShowField === field) {
|
||||
that.treeEnable = true;
|
||||
var _formatter = that.header.formatters[i];
|
||||
var _class = [that.options.treeCellcls];
|
||||
if (that.header.classes[i]) {
|
||||
_class.push(that.header.classes[i].split('"')[1] || '');
|
||||
}
|
||||
that.header.classes[i] = ' class="' + _class.join(' ') + '"';
|
||||
that.header.formatters[i] = function (value, row, index) {
|
||||
var colTree = [that.options.onTreeFormatter.apply(that, [row])];
|
||||
colTree.push('<span class="' + that.options.treeTextcls + '">');
|
||||
if (_formatter) {
|
||||
colTree.push(_formatter.apply(this, Array.prototype.slice.apply(arguments)));
|
||||
} else {
|
||||
colTree.push(value);
|
||||
}
|
||||
colTree.push('</span>');
|
||||
return colTree.join('');
|
||||
};
|
||||
return false;
|
||||
}
|
||||
});
|
||||
}
|
||||
};
|
||||
|
||||
var initNode = function (item, idx, data, parentDom) {
|
||||
var that = this;
|
||||
var nodes = that.options.onGetNodes.apply(that, [item, data]);
|
||||
item._nodes = nodes;
|
||||
parentDom.append(_initRow.apply(that, [item, idx, data, parentDom]));
|
||||
var len = nodes.length - 1;
|
||||
for (var i = 0; i <= len; i++) {
|
||||
var node = nodes[i];
|
||||
node._level = item._level + 1;
|
||||
node._parent = item;
|
||||
if (i === len)
|
||||
node._last = 1;
|
||||
initNode.apply(that, [node, $.inArray(node, data), data, parentDom]);
|
||||
}
|
||||
};
|
||||
|
||||
|
||||
BootstrapTable.prototype.initRow = function (item, idx, data, parentDom) {
|
||||
var that = this;
|
||||
if (that.treeEnable) {
|
||||
if (that.options.onCheckRoot.apply(that, [item, data])) {
|
||||
if (item._level === undefined) {
|
||||
item._level = 0;
|
||||
}
|
||||
initNode.apply(that, [item, idx, data, parentDom]);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
|
||||
}
|
||||
return _initRow.apply(that, Array.prototype.slice.apply(arguments));
|
||||
};
|
||||
|
||||
} (jQuery);
|
@ -1,43 +0,0 @@
|
||||
.table:not(.table-condensed) > tbody > tr > td.treenode {
|
||||
padding-top: 0;
|
||||
padding-bottom: 0;
|
||||
border-bottom: solid #fff 1px;
|
||||
}
|
||||
|
||||
.table:not(.table-condensed) > tbody > tr:last-child > td.treenode {
|
||||
border-bottom: none;
|
||||
}
|
||||
|
||||
.treenode {
|
||||
.text {
|
||||
float: left;
|
||||
display: block;
|
||||
padding-top: 6px;
|
||||
padding-bottom: 6px;
|
||||
}
|
||||
.vertical,
|
||||
.vertical.last {
|
||||
float: left;
|
||||
display: block;
|
||||
width: 1px;
|
||||
border-left: dashed silver 1px;
|
||||
height: 38px;
|
||||
margin-left: 8px;
|
||||
}
|
||||
.vertical.last {
|
||||
height: 15px;
|
||||
}
|
||||
|
||||
.space,
|
||||
.node {
|
||||
float: left;
|
||||
display: block;
|
||||
width: 15px;
|
||||
height: 5px;
|
||||
margin-top: 15px;
|
||||
}
|
||||
|
||||
.node {
|
||||
border-top: dashed silver 1px;
|
||||
}
|
||||
}
|
@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "Tree column",
|
||||
"version": "1.0.0",
|
||||
"description": "Plugin to support display tree data column.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/tree-column",
|
||||
"example": "http://issues.wenzhixin.net.cn/bootstrap-table/#extensions/tree-column.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-reorder-rows",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/tree-column"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "KingYang",
|
||||
"image": "https://avatars3.githubusercontent.com/u/1540211"
|
||||
}
|
||||
}
|
Binary file not shown.
Before Width: | Height: | Size: 346 B |
@ -1,17 +0,0 @@
|
||||
{
|
||||
"name": "treegrid",
|
||||
"version": "1.0.0",
|
||||
"description": "Plugin to support the jquery treegrid.",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/treegrid",
|
||||
"example": "https://github.com/wenzhixin/bootstrap-table-examples/blob/master/extensions/treegrid.html",
|
||||
|
||||
"plugins": [{
|
||||
"name": "bootstrap-table-treegrid",
|
||||
"url": "https://github.com/wenzhixin/bootstrap-table/tree/master/src/extensions/treegrid"
|
||||
}],
|
||||
|
||||
"author": {
|
||||
"name": "foreveryang321",
|
||||
"image": "https://avatars0.githubusercontent.com/u/5868190"
|
||||
}
|
||||
}
|
790
InvenTree/InvenTree/static/bootstrap-table/themes/bootstrap-table/bootstrap-table.css
vendored
Normal file
790
InvenTree/InvenTree/static/bootstrap-table/themes/bootstrap-table/bootstrap-table.css
vendored
Normal file
@ -0,0 +1,790 @@
|
||||
/**
|
||||
* @author Dustin Utecht
|
||||
* https://github.com/wenzhixin/bootstrap-table/
|
||||
*/
|
||||
.bootstrap-table .fixed-table-toolbar::after {
|
||||
content: "";
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .bs-bars,
|
||||
.bootstrap-table .fixed-table-toolbar .search,
|
||||
.bootstrap-table .fixed-table-toolbar .columns {
|
||||
position: relative;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns .btn-group > .btn-group {
|
||||
display: inline-block;
|
||||
margin-left: -1px !important;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns .btn-group > .btn-group > .btn {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns .btn-group > .btn-group:first-child > .btn {
|
||||
border-top-left-radius: 4px;
|
||||
border-bottom-left-radius: 4px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns .btn-group > .btn-group:last-child > .btn {
|
||||
border-top-right-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns .dropdown-menu {
|
||||
text-align: left;
|
||||
max-height: 300px;
|
||||
overflow: auto;
|
||||
-ms-overflow-style: scrollbar;
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns label {
|
||||
display: block;
|
||||
padding: 3px 20px;
|
||||
clear: both;
|
||||
font-weight: normal;
|
||||
line-height: 1.428571429;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns-left {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns-right {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .pull-right .dropdown-menu {
|
||||
right: 0;
|
||||
left: auto;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container {
|
||||
position: relative;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table {
|
||||
width: 100%;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table th,
|
||||
.bootstrap-table .fixed-table-container .table td {
|
||||
vertical-align: middle;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table thead th {
|
||||
vertical-align: bottom;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table thead th:focus {
|
||||
outline: 0 solid transparent;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table thead th.detail {
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table thead th .th-inner {
|
||||
padding: 0.75rem;
|
||||
vertical-align: bottom;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table thead th .sortable {
|
||||
cursor: pointer;
|
||||
background-position: right;
|
||||
background-repeat: no-repeat;
|
||||
padding-right: 30px !important;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table thead th .both {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAQAAADYWf5HAAAAkElEQVQoz7X QMQ5AQBCF4dWQSJxC5wwax1Cq1e7BAdxD5SL+Tq/QCM1oNiJidwox0355mXnG/DrEtIQ6azioNZQxI0ykPhTQIwhCR+BmBYtlK7kLJYwWCcJA9M4qdrZrd8pPjZWPtOqdRQy320YSV17OatFC4euts6z39GYMKRPCTKY9UnPQ6P+GtMRfGtPnBCiqhAeJPmkqAAAAAElFTkSuQmCC");
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table thead th .asc {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZ0lEQVQ4y2NgGLKgquEuFxBPAGI2ahhWCsS/gDibUoO0gPgxEP8H4ttArEyuQYxAPBdqEAxPBImTY5gjEL9DM+wTENuQahAvEO9DMwiGdwAxOymGJQLxTyD+jgWDxCMZRsEoGAVoAADeemwtPcZI2wAAAABJRU5ErkJggg==");
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table thead th .desc {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZUlEQVQ4y2NgGAWjYBSggaqGu5FA/BOIv2PBIPFEUgxjB+IdQPwfC94HxLykus4GiD+hGfQOiB3J8SojEE9EM2wuSJzcsFMG4ttQgx4DsRalkZENxL+AuJQaMcsGxBOAmGvopk8AVz1sLZgg0bsAAAAASUVORK5CYII= ");
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table tbody tr.selected td {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table tbody tr.no-records-found td {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table tbody tr .card-view {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table tbody tr .card-view .card-view-title {
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
min-width: 30%;
|
||||
width: auto !important;
|
||||
text-align: left !important;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table tbody tr .card-view .card-view-value {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table .bs-checkbox {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table .bs-checkbox label {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table .bs-checkbox label input[type="radio"],
|
||||
.bootstrap-table .fixed-table-container .table .bs-checkbox label input[type="checkbox"] {
|
||||
margin: 0 auto !important;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table.table-sm .th-inner {
|
||||
padding: 0.3rem;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container.fixed-height:not(.has-footer) {
|
||||
border-bottom: 1px solid #dbdbdb;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container.fixed-height.has-card-view {
|
||||
border-top: 1px solid #dbdbdb;
|
||||
border-bottom: 1px solid #dbdbdb;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container.fixed-height .fixed-table-border {
|
||||
border-left: 1px solid #dbdbdb;
|
||||
border-right: 1px solid #dbdbdb;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container.fixed-height .table thead th {
|
||||
border-bottom: 1px solid #dbdbdb;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container.fixed-height .table-dark thead th {
|
||||
border-bottom: 1px solid #32383e;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-header {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body {
|
||||
overflow-x: auto;
|
||||
overflow-y: auto;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading {
|
||||
align-items: center;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
z-index: 1000;
|
||||
transition: visibility 0s, opacity 0.15s ease-in-out;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.open {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap {
|
||||
align-items: baseline;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .loading-text {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-dot,
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::after,
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::before {
|
||||
content: "";
|
||||
animation-duration: 1.5s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-name: LOADING;
|
||||
background: #363636;
|
||||
border-radius: 50%;
|
||||
display: block;
|
||||
height: 5px;
|
||||
margin: 0 4px;
|
||||
opacity: 0;
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-dot {
|
||||
animation-delay: 0.3s;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::after {
|
||||
animation-delay: 0.6s;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark {
|
||||
background: #363636;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-dot,
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-wrap::after,
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-wrap::before {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-footer {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination::after {
|
||||
content: "";
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination-detail,
|
||||
.bootstrap-table .fixed-table-pagination > .pagination {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination-detail .pagination-info {
|
||||
line-height: 34px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination-detail .page-list {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination-detail .page-list .btn-group {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination-detail .page-list .btn-group .dropdown-menu {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination ul.pagination {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination ul.pagination li.page-intermediate a {
|
||||
color: #c8c8c8;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination ul.pagination li.page-intermediate a::before {
|
||||
content: '\2B05';
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination ul.pagination li.page-intermediate a::after {
|
||||
content: '\27A1';
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination ul.pagination li.disabled a {
|
||||
pointer-events: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.bootstrap-table.fullscreen {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1050;
|
||||
width: 100% !important;
|
||||
background: #fff;
|
||||
height: calc(100vh);
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.bootstrap-table.bootstrap4 .pagination-lg .page-link, .bootstrap-table.bootstrap5 .pagination-lg .page-link {
|
||||
padding: .5rem 1rem;
|
||||
}
|
||||
|
||||
.bootstrap-table.bootstrap5 .float-left {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.bootstrap-table.bootstrap5 .float-right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
/* calculate scrollbar width */
|
||||
div.fixed-table-scroll-inner {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
div.fixed-table-scroll-outer {
|
||||
top: 0;
|
||||
left: 0;
|
||||
visibility: hidden;
|
||||
width: 200px;
|
||||
height: 150px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@keyframes LOADING {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
@font-face {
|
||||
font-family: 'bootstrap-table';
|
||||
src: url("fonts/bootstrap-table.eot?gmdfsp");
|
||||
src: url("fonts/bootstrap-table.eot") format("embedded-opentype"), url("fonts/bootstrap-table.ttf") format("truetype"), url("fonts/bootstrap-table.woff") format("woff"), url("fonts/bootstrap-table.svg") format("svg");
|
||||
font-weight: normal;
|
||||
font-style: normal;
|
||||
font-display: block;
|
||||
}
|
||||
|
||||
[class^="icon-"],
|
||||
[class*=" icon-"] {
|
||||
/* use !important to prevent issues with browser extensions that change fonts */
|
||||
font-family: 'bootstrap-table', sans-serif !important;
|
||||
speak: none;
|
||||
font-style: normal;
|
||||
font-weight: normal;
|
||||
font-variant: normal;
|
||||
text-transform: none;
|
||||
line-height: 1;
|
||||
/* Better Font Rendering =========== */
|
||||
-webkit-font-smoothing: antialiased;
|
||||
-moz-osx-font-smoothing: grayscale;
|
||||
}
|
||||
|
||||
.icon-arrow-down-circle:before {
|
||||
content: "\e907";
|
||||
}
|
||||
|
||||
.icon-arrow-up-circle:before {
|
||||
content: "\e908";
|
||||
}
|
||||
|
||||
.icon-chevron-left:before {
|
||||
content: "\e900";
|
||||
}
|
||||
|
||||
.icon-chevron-right:before {
|
||||
content: "\e901";
|
||||
}
|
||||
|
||||
.icon-clock:before {
|
||||
content: "\e90c";
|
||||
}
|
||||
|
||||
.icon-copy:before {
|
||||
content: "\e909";
|
||||
}
|
||||
|
||||
.icon-download:before {
|
||||
content: "\e90d";
|
||||
}
|
||||
|
||||
.icon-list:before {
|
||||
content: "\e902";
|
||||
}
|
||||
|
||||
.icon-maximize:before {
|
||||
content: "\1f5ce";
|
||||
}
|
||||
|
||||
.icon-minus:before {
|
||||
content: "\e90f";
|
||||
}
|
||||
|
||||
.icon-move:before {
|
||||
content: "\e903";
|
||||
}
|
||||
|
||||
.icon-plus:before {
|
||||
content: "\e90e";
|
||||
}
|
||||
|
||||
.icon-printer:before {
|
||||
content: "\e90b";
|
||||
}
|
||||
|
||||
.icon-refresh-cw:before {
|
||||
content: "\e904";
|
||||
}
|
||||
|
||||
.icon-search:before {
|
||||
content: "\e90a";
|
||||
}
|
||||
|
||||
.icon-toggle-right:before {
|
||||
content: "\e905";
|
||||
}
|
||||
|
||||
.icon-trash-2:before {
|
||||
content: "\e906";
|
||||
}
|
||||
|
||||
.icon-sort-amount-asc:before {
|
||||
content: "\ea4c";
|
||||
}
|
||||
|
||||
.bootstrap-table * {
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.bootstrap-table input.form-control,
|
||||
.bootstrap-table select.form-control,
|
||||
.bootstrap-table .btn {
|
||||
border-radius: 4px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #ccc;
|
||||
padding: 9px 12px;
|
||||
}
|
||||
|
||||
.bootstrap-table select.form-control {
|
||||
height: 35px;
|
||||
}
|
||||
|
||||
.bootstrap-table .btn {
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.bootstrap-table .btn.active {
|
||||
background-color: #ebebeb;
|
||||
}
|
||||
|
||||
.bootstrap-table .btn:focus, .bootstrap-table .btn:hover {
|
||||
background-color: whitesmoke;
|
||||
}
|
||||
|
||||
.bootstrap-table .caret {
|
||||
display: inline-block;
|
||||
width: 0;
|
||||
height: 0;
|
||||
margin-left: 2px;
|
||||
vertical-align: middle;
|
||||
border-top: 4px dashed;
|
||||
border-top: 4px solid;
|
||||
border-right: 4px solid transparent;
|
||||
border-left: 4px solid transparent;
|
||||
}
|
||||
|
||||
.bootstrap-table .detail-icon {
|
||||
text-decoration: none;
|
||||
color: #3679e4;
|
||||
}
|
||||
|
||||
.bootstrap-table .detail-icon:hover {
|
||||
color: #154a9f;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns,
|
||||
.bootstrap-table .fixed-table-toolbar .columns .btn-group {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns > .btn:not(:first-child):not(:last-child),
|
||||
.bootstrap-table .fixed-table-toolbar .columns > .btn:not(:first-child):not(:last-child) > .btn,
|
||||
.bootstrap-table .fixed-table-toolbar .columns > .btn-group:not(:first-child):not(:last-child),
|
||||
.bootstrap-table .fixed-table-toolbar .columns > .btn-group:not(:first-child):not(:last-child) > .btn {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns > .btn:not(:last-child):not(.dropdown-toggle),
|
||||
.bootstrap-table .fixed-table-toolbar .columns > .btn:not(:last-child) > .btn,
|
||||
.bootstrap-table .fixed-table-toolbar .columns > .btn-group:not(:last-child):not(.dropdown-toggle),
|
||||
.bootstrap-table .fixed-table-toolbar .columns > .btn-group:not(:last-child) > .btn {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns > .btn:not(:first-child):not(.dropdown-toggle),
|
||||
.bootstrap-table .fixed-table-toolbar .columns > .btn:not(:first-child) > .btn,
|
||||
.bootstrap-table .fixed-table-toolbar .columns > .btn-group:not(:first-child):not(.dropdown-toggle),
|
||||
.bootstrap-table .fixed-table-toolbar .columns > .btn-group:not(:first-child) > .btn {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns label {
|
||||
padding: 5px 12px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns input[type="checkbox"] {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns .dropdown-divider {
|
||||
border-bottom: 1px solid #dbdbdb;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .search .input-group .search-input {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .search .input-group button[name="search"],
|
||||
.bootstrap-table .fixed-table-toolbar .search .input-group button[name="clearSearch"] {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .search .input-group button[name="search"]:not(:last-child),
|
||||
.bootstrap-table .fixed-table-toolbar .search .input-group button[name="clearSearch"]:not(:last-child) {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
border-right: none;
|
||||
}
|
||||
|
||||
.bootstrap-table .open.dropdown-menu {
|
||||
display: block;
|
||||
}
|
||||
|
||||
.bootstrap-table .dropdown-menu-up .dropdown-menu {
|
||||
top: auto;
|
||||
bottom: 100%;
|
||||
}
|
||||
|
||||
.bootstrap-table .dropdown-menu {
|
||||
display: none;
|
||||
background-color: #fff;
|
||||
position: absolute;
|
||||
right: 0;
|
||||
min-width: 120px;
|
||||
margin-top: 2px;
|
||||
border: 1px solid #ccc;
|
||||
border-radius: 4px;
|
||||
-webkit-box-shadow: 0 3px 12px rgba(0, 0, 0, 0.175);
|
||||
box-shadow: 0 3px 12px rgba(0, 0, 0, 0.175);
|
||||
}
|
||||
|
||||
.bootstrap-table .dropdown-menu .dropdown-item {
|
||||
color: #363636;
|
||||
text-decoration: none;
|
||||
display: block;
|
||||
padding: 5px 12px;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.bootstrap-table .dropdown-menu .dropdown-item:hover {
|
||||
background-color: whitesmoke;
|
||||
}
|
||||
|
||||
.bootstrap-table .dropdown-menu .dropdown-item.active {
|
||||
background-color: #3679e4;
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.bootstrap-table .dropdown-menu .dropdown-item.active:hover {
|
||||
background-color: #1b5fcc;
|
||||
}
|
||||
|
||||
.bootstrap-table .columns-left .dropdown-menu {
|
||||
left: 0;
|
||||
right: auto;
|
||||
}
|
||||
|
||||
.bootstrap-table .pagination-detail {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.bootstrap-table .pagination-detail .dropdown-item {
|
||||
min-width: 45px;
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.bootstrap-table table {
|
||||
border-collapse: collapse;
|
||||
}
|
||||
|
||||
.bootstrap-table table th {
|
||||
text-align: inherit;
|
||||
}
|
||||
|
||||
.bootstrap-table table.table-bordered thead tr th,
|
||||
.bootstrap-table table.table-bordered tbody tr td {
|
||||
border: 1px solid #dbdbdb;
|
||||
}
|
||||
|
||||
.bootstrap-table table.table-bordered tbody tr td {
|
||||
padding: 0.75rem;
|
||||
}
|
||||
|
||||
.bootstrap-table table.table-hover tbody tr:hover {
|
||||
background: #fafafa;
|
||||
}
|
||||
|
||||
.bootstrap-table .float-left {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.bootstrap-table .float-right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.bootstrap-table .pagination {
|
||||
padding: 0;
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
text-align: center;
|
||||
list-style: none;
|
||||
}
|
||||
|
||||
.bootstrap-table .pagination .page-item {
|
||||
border: 1px solid #dbdbdb;
|
||||
background-color: #fff;
|
||||
border-radius: 4px;
|
||||
margin: 2px;
|
||||
padding: 5px 2px 5px 2px;
|
||||
}
|
||||
|
||||
.bootstrap-table .pagination .page-item:hover {
|
||||
background-color: whitesmoke;
|
||||
}
|
||||
|
||||
.bootstrap-table .pagination .page-item .page-link {
|
||||
padding: 6px 12px;
|
||||
line-height: 1.428571429;
|
||||
color: #363636;
|
||||
text-decoration: none;
|
||||
outline: none;
|
||||
}
|
||||
|
||||
.bootstrap-table .pagination .page-item.active {
|
||||
background-color: #3679e4;
|
||||
border: 1px solid #206ae1;
|
||||
}
|
||||
|
||||
.bootstrap-table .pagination .page-item.active .page-link {
|
||||
color: #fff;
|
||||
}
|
||||
|
||||
.bootstrap-table .pagination .page-item.active:hover {
|
||||
background-color: #1b5fcc;
|
||||
}
|
||||
|
||||
.bootstrap-table .pagination .btn-group {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.bootstrap-table .pagination .btn-group .btn:not(:first-child):not(:last-child),
|
||||
.bootstrap-table .pagination .btn-group input:not(:first-child):not(:last-child) {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .pagination .btn-group .btn:first-child:not(:last-child):not(.dropdown-toggle),
|
||||
.bootstrap-table .pagination .btn-group input:first-child:not(:last-child):not(.dropdown-toggle) {
|
||||
border-top-right-radius: 0;
|
||||
border-bottom-right-radius: 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .pagination .btn-group .btn:last-child:not(:first-child),
|
||||
.bootstrap-table .pagination .btn-group input:last-child:not(:first-child) {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .pagination .btn-group > .btn-group:last-child:not(:first-child) > .btn:first-child {
|
||||
border-top-left-radius: 0;
|
||||
border-bottom-left-radius: 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .filter-control {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.bootstrap-table .page-jump-to input,
|
||||
.bootstrap-table .page-jump-to .btn {
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
.modal {
|
||||
position: fixed;
|
||||
display: none;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
}
|
||||
|
||||
.modal.show {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.modal .btn {
|
||||
border-radius: 4px;
|
||||
background-color: #fff;
|
||||
border: 1px solid #ccc;
|
||||
padding: 6px 12px;
|
||||
outline: none;
|
||||
cursor: pointer;
|
||||
}
|
||||
|
||||
.modal .btn.active {
|
||||
border-color: black;
|
||||
}
|
||||
|
||||
.modal .modal-background {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
bottom: 0;
|
||||
right: 0;
|
||||
z-index: 998;
|
||||
background-color: rgba(10, 10, 10, 0.86);
|
||||
}
|
||||
|
||||
.modal .modal-content {
|
||||
position: relative;
|
||||
width: 600px;
|
||||
margin: 30px auto;
|
||||
z-index: 999;
|
||||
}
|
||||
|
||||
.modal .modal-content .box {
|
||||
background-color: #fff;
|
||||
border-radius: 6px;
|
||||
display: block;
|
||||
padding: 1.25rem;
|
||||
}
|
1124
InvenTree/InvenTree/static/bootstrap-table/themes/bootstrap-table/bootstrap-table.js
vendored
Normal file
1124
InvenTree/InvenTree/static/bootstrap-table/themes/bootstrap-table/bootstrap-table.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
10
InvenTree/InvenTree/static/bootstrap-table/themes/bootstrap-table/bootstrap-table.min.css
vendored
Normal file
10
InvenTree/InvenTree/static/bootstrap-table/themes/bootstrap-table/bootstrap-table.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
10
InvenTree/InvenTree/static/bootstrap-table/themes/bootstrap-table/bootstrap-table.min.js
vendored
Normal file
10
InvenTree/InvenTree/static/bootstrap-table/themes/bootstrap-table/bootstrap-table.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
Binary file not shown.
@ -0,0 +1,28 @@
|
||||
<?xml version="1.0" standalone="no"?>
|
||||
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd" >
|
||||
<svg xmlns="http://www.w3.org/2000/svg">
|
||||
<metadata>Generated by IcoMoon</metadata>
|
||||
<defs>
|
||||
<font id="bootstrap-table" horiz-adv-x="1024">
|
||||
<font-face units-per-em="1024" ascent="960" descent="-64" />
|
||||
<missing-glyph horiz-adv-x="1024" />
|
||||
<glyph unicode=" " horiz-adv-x="512" d="" />
|
||||
<glyph unicode="" glyph-name="chevron-left" d="M670.165 200.832l-225.835 225.835 225.835 225.835c16.683 16.683 16.683 43.691 0 60.331s-43.691 16.683-60.331 0l-256-256c-16.683-16.683-16.683-43.691 0-60.331l256-256c16.683-16.683 43.691-16.683 60.331 0s16.683 43.691 0 60.331z" />
|
||||
<glyph unicode="" glyph-name="chevron-right" d="M414.165 140.502l256 256c16.683 16.683 16.683 43.691 0 60.331l-256 256c-16.683 16.683-43.691 16.683-60.331 0s-16.683-43.691 0-60.331l225.835-225.835-225.835-225.835c-16.683-16.683-16.683-43.691 0-60.331s43.691-16.683 60.331 0z" />
|
||||
<glyph unicode="" glyph-name="list" d="M341.333 640h554.667c23.552 0 42.667 19.115 42.667 42.667s-19.115 42.667-42.667 42.667h-554.667c-23.552 0-42.667-19.115-42.667-42.667s19.115-42.667 42.667-42.667zM341.333 384h554.667c23.552 0 42.667 19.115 42.667 42.667s-19.115 42.667-42.667 42.667h-554.667c-23.552 0-42.667-19.115-42.667-42.667s19.115-42.667 42.667-42.667zM341.333 128h554.667c23.552 0 42.667 19.115 42.667 42.667s-19.115 42.667-42.667 42.667h-554.667c-23.552 0-42.667-19.115-42.667-42.667s19.115-42.667 42.667-42.667zM170.667 682.667c0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667 19.115-42.667 42.667-42.667 42.667 19.115 42.667 42.667zM170.667 426.667c0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667 19.115-42.667 42.667-42.667 42.667 19.115 42.667 42.667zM170.667 170.667c0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667 19.115-42.667 42.667-42.667 42.667 19.115 42.667 42.667z" />
|
||||
<glyph unicode="" glyph-name="move" d="M469.333 750.336v-281.003h-281.003l55.168 55.168c16.683 16.683 16.683 43.691 0 60.331s-43.691 16.683-60.331 0l-128-128c-0.043-0.043-0.085-0.128-0.171-0.171-4.011-4.053-7.040-8.704-9.088-13.653-4.309-10.453-4.309-22.229 0-32.683 2.048-4.949 5.077-9.643 9.088-13.653 0.043-0.043 0.085-0.128 0.171-0.171l128-128c16.683-16.683 43.691-16.683 60.331 0s16.683 43.691 0 60.331l-55.168 55.168h281.003v-281.003l-55.168 55.168c-16.683 16.683-43.691 16.683-60.331 0s-16.683-43.691 0-60.331l128-128c4.096-4.096 8.832-7.168 13.867-9.259 5.12-2.091 10.539-3.2 15.957-3.243 5.675-0.043 11.349 1.024 16.64 3.243 5.035 2.091 9.771 5.163 13.867 9.259l128 128c16.683 16.683 16.683 43.691 0 60.331s-43.691 16.683-60.331 0l-55.168-55.168v281.003h281.003l-55.168-55.168c-16.683-16.683-16.683-43.691 0-60.331s43.691-16.683 60.331 0l128 128c4.096 4.096 7.168 8.832 9.259 13.867 2.091 5.12 3.2 10.539 3.243 15.957 0.043 5.675-1.024 11.349-3.243 16.64-2.091 5.035-5.163 9.771-9.259 13.867l-128 128c-16.683 16.683-43.691 16.683-60.331 0s-16.683-43.691 0-60.331l55.168-55.168h-281.003v281.003l55.168-55.168c16.683-16.683 43.691-16.683 60.331 0s16.683 43.691 0 60.331l-128 128c-0.043 0.043-0.128 0.085-0.171 0.171-4.053 4.011-8.704 7.040-13.653 9.088-10.453 4.309-22.229 4.309-32.683 0-4.949-2.048-9.643-5.077-13.653-9.088-0.043-0.043-0.128-0.085-0.171-0.171l-128-128c-16.683-16.683-16.683-43.691 0-60.331s43.691-16.683 60.331 0z" />
|
||||
<glyph unicode="" glyph-name="refresh-cw" d="M189.995 540.416c15.445 43.648 38.827 82.133 67.883 114.432 30.208 33.579 66.645 60.587 106.88 79.744s84.096 30.549 129.237 32.939c43.392 2.304 88.021-3.755 131.669-19.2 50.603-17.92 94.123-46.421 127.275-80.213l120.704-113.451h-148.309c-23.552 0-42.667-19.115-42.667-42.667s19.115-42.667 42.667-42.667h256c0.171 0 0.299 0 0.469 0 5.845 0.043 11.435 1.323 16.469 3.499 5.205 2.261 9.856 5.504 13.739 9.515 0.555 0.597 1.152 1.195 1.664 1.835 3.072 3.541 5.547 7.637 7.296 12.032 1.749 4.352 2.773 9.045 2.944 13.952 0.085 0.64 0.085 1.237 0.085 1.835v256c0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667v-157.397l-124.843 117.291c-42.325 43.093-96.896 78.635-159.701 100.864-54.4 19.243-110.208 26.837-164.608 23.979-56.491-2.987-111.317-17.195-161.493-41.131s-95.701-57.643-133.547-99.669c-36.437-40.491-65.664-88.619-84.907-143.061-7.851-22.229 3.755-46.592 25.984-54.443s46.592 3.755 54.443 25.984zM85.333 242.688l126.080-118.485c39.851-39.893 86.955-70.784 137.259-91.648 52.224-21.632 107.861-32.469 163.456-32.469s111.232 10.795 163.456 32.384c50.347 20.821 97.451 51.669 138.283 92.501 47.104 47.104 81.109 102.699 100.736 159.787 7.68 22.272-4.181 46.549-26.496 54.229s-46.549-4.181-54.229-26.496c-15.403-44.8-42.368-89.216-80.341-127.189-32.768-32.725-70.4-57.387-110.592-73.984-41.728-17.28-86.272-25.941-130.816-25.899s-89.045 8.704-130.773 25.984c-40.149 16.64-77.781 41.301-111.488 74.965l-119.467 112.299h148.267c23.552 0 42.667 19.115 42.667 42.667s-19.115 42.667-42.667 42.667h-256c-0.171 0-0.299 0-0.469 0-5.845-0.043-11.435-1.323-16.469-3.499-5.205-2.261-9.899-5.547-13.781-9.6-0.555-0.555-1.067-1.152-1.579-1.749-3.072-3.584-5.589-7.68-7.339-12.117-1.707-4.352-2.731-9.003-2.944-13.909-0.085-0.597-0.085-1.195-0.085-1.792v-256c0-23.552 19.115-42.667 42.667-42.667s42.667 19.115 42.667 42.667z" />
|
||||
<glyph unicode="" glyph-name="toggle-right" d="M341.333 768c-46.165 0-90.283-9.173-130.56-25.856-41.813-17.323-79.36-42.667-110.805-74.112s-56.789-69.035-74.112-110.805c-16.683-40.277-25.856-84.395-25.856-130.56s9.173-90.283 25.856-130.56c17.323-41.813 42.667-79.36 74.112-110.805s69.035-56.789 110.805-74.112c40.277-16.683 84.395-25.856 130.56-25.856h341.333c46.165 0 90.283 9.173 130.56 25.856 41.813 17.323 79.36 42.667 110.805 74.112s56.789 69.035 74.112 110.805c16.683 40.277 25.856 84.395 25.856 130.56s-9.173 90.283-25.856 130.56c-17.323 41.813-42.667 79.36-74.112 110.805s-69.035 56.789-110.805 74.112c-40.277 16.683-84.395 25.856-130.56 25.856zM341.333 682.667h341.333c34.773 0 67.797-6.912 97.877-19.371 31.275-12.971 59.477-31.957 83.115-55.595s42.667-51.84 55.595-83.115c12.501-30.123 19.413-63.147 19.413-97.92s-6.912-67.797-19.371-97.877c-12.971-31.275-31.957-59.477-55.595-83.115s-51.84-42.667-83.115-55.595c-30.123-12.501-63.147-19.413-97.92-19.413h-341.333c-34.773 0-67.797 6.912-97.877 19.371-31.275 12.971-59.477 31.957-83.115 55.595s-42.667 51.84-55.595 83.115c-12.501 30.123-19.413 63.147-19.413 97.92s6.912 67.797 19.371 97.877c12.971 31.275 31.957 59.477 55.595 83.115s51.84 42.667 83.115 55.595c30.123 12.501 63.147 19.413 97.92 19.413zM853.333 426.667c0 23.040-4.608 45.099-12.928 65.28-8.661 20.907-21.333 39.68-37.035 55.381s-34.475 28.373-55.381 37.035c-20.224 8.363-42.283 12.971-65.323 12.971s-45.099-4.608-65.28-12.928c-20.907-8.661-39.68-21.333-55.381-37.035s-28.373-34.475-37.035-55.381c-8.363-20.224-12.971-42.283-12.971-65.323s4.608-45.099 12.928-65.28c8.661-20.907 21.333-39.68 37.035-55.381s34.475-28.373 55.381-37.035c20.224-8.363 42.283-12.971 65.323-12.971s45.099 4.608 65.28 12.928c20.907 8.661 39.68 21.333 55.381 37.035s28.373 34.475 37.035 55.381c8.363 20.224 12.971 42.283 12.971 65.323zM768 426.667c0-11.648-2.304-22.613-6.443-32.64-4.309-10.411-10.667-19.797-18.56-27.733-7.893-7.893-17.323-14.251-27.733-18.56-9.984-4.096-20.949-6.4-32.597-6.4s-22.613 2.304-32.64 6.443c-10.411 4.309-19.797 10.667-27.733 18.56s-14.251 17.323-18.56 27.733c-4.096 9.984-6.4 20.949-6.4 32.597s2.304 22.613 6.443 32.64c4.309 10.411 10.667 19.797 18.56 27.733s17.323 14.251 27.733 18.56c9.984 4.096 20.949 6.4 32.597 6.4s22.613-2.304 32.64-6.443c10.411-4.309 19.797-10.667 27.733-18.56 7.893-7.893 14.251-17.323 18.56-27.733 4.096-9.984 6.4-20.949 6.4-32.597z" />
|
||||
<glyph unicode="" glyph-name="trash-2" d="M768 640v-554.667c0-5.845-1.152-11.349-3.2-16.299-2.133-5.205-5.333-9.899-9.301-13.867s-8.661-7.125-13.867-9.301c-4.949-2.048-10.453-3.2-16.299-3.2h-426.667c-5.845 0-11.349 1.152-16.299 3.2-5.205 2.133-9.899 5.333-13.867 9.301s-7.125 8.661-9.301 13.867c-2.048 4.949-3.2 10.453-3.2 16.299v554.667zM725.333 725.334v42.667c0 17.28-3.456 33.835-9.728 48.981-6.485 15.701-16 29.781-27.776 41.557s-25.856 21.291-41.557 27.776c-15.104 6.229-31.659 9.685-48.939 9.685h-170.667c-17.28 0-33.835-3.456-48.981-9.728-15.659-6.485-29.739-16-41.515-27.776s-21.291-25.856-27.776-41.515c-6.272-15.147-9.728-31.701-9.728-48.981v-42.667h-170.667c-23.552 0-42.667-19.115-42.667-42.667s19.115-42.667 42.667-42.667h42.667v-554.667c0-17.28 3.456-33.835 9.728-48.981 6.485-15.701 16-29.781 27.776-41.557s25.856-21.291 41.557-27.776c15.104-6.229 31.659-9.685 48.939-9.685h426.667c17.28 0 33.835 3.456 48.981 9.728 15.701 6.485 29.781 16 41.557 27.776s21.291 25.856 27.776 41.557c6.229 15.104 9.685 31.659 9.685 48.939v554.667h42.667c23.552 0 42.667 19.115 42.667 42.667s-19.115 42.667-42.667 42.667zM384 725.334v42.667c0 5.845 1.152 11.349 3.2 16.299 2.133 5.205 5.333 9.899 9.301 13.867s8.661 7.125 13.867 9.301c4.949 2.048 10.453 3.2 16.299 3.2h170.667c5.845 0 11.349-1.152 16.299-3.2 5.205-2.133 9.899-5.333 13.867-9.301s7.125-8.661 9.301-13.867c2.048-4.949 3.2-10.453 3.2-16.299v-42.667zM384 469.334v-256c0-23.552 19.115-42.667 42.667-42.667s42.667 19.115 42.667 42.667v256c0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667zM554.667 469.334v-256c0-23.552 19.115-42.667 42.667-42.667s42.667 19.115 42.667 42.667v256c0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667z" />
|
||||
<glyph unicode="" glyph-name="arrow-down-circle" d="M981.333 426.667c0 63.488-12.629 124.16-35.541 179.499-23.808 57.472-58.667 109.141-101.888 152.363s-94.891 78.123-152.363 101.888c-55.381 22.955-116.053 35.584-179.541 35.584s-124.16-12.629-179.499-35.541c-57.472-23.808-109.141-58.667-152.363-101.931s-78.123-94.891-101.931-152.363c-22.912-55.339-35.541-116.011-35.541-179.499s12.629-124.16 35.541-179.499c23.808-57.472 58.667-109.141 101.888-152.363s94.891-78.123 152.363-101.888c55.381-22.955 116.053-35.584 179.541-35.584s124.16 12.629 179.499 35.541c57.472 23.808 109.141 58.667 152.363 101.888s78.123 94.891 101.888 152.363c22.955 55.381 35.584 116.053 35.584 179.541zM896 426.667c0-52.096-10.368-101.675-29.056-146.859-19.456-46.976-47.957-89.259-83.413-124.672s-77.739-63.957-124.672-83.413c-45.184-18.688-94.763-29.056-146.859-29.056s-101.675 10.368-146.859 29.056c-46.976 19.456-89.259 47.957-124.672 83.413-35.456 35.456-63.957 77.739-83.413 124.672-18.688 45.184-29.056 94.763-29.056 146.859s10.368 101.675 29.056 146.859c19.456 46.976 47.957 89.259 83.413 124.672s77.739 63.957 124.672 83.413c45.184 18.688 94.763 29.056 146.859 29.056s101.675-10.368 146.859-29.056c46.976-19.456 89.259-47.957 124.672-83.413 35.456-35.456 63.957-77.739 83.413-124.672 18.688-45.184 29.056-94.763 29.056-146.859zM469.333 597.334v-238.336l-97.835 97.835c-16.683 16.683-43.691 16.683-60.331 0s-16.683-43.691 0-60.331l170.667-170.667c4.096-4.096 8.832-7.168 13.867-9.259 5.12-2.091 10.539-3.2 15.957-3.243 0.213 0 0.469 0 0.683 0 5.419 0.043 10.88 1.109 15.957 3.243 5.035 2.091 9.771 5.163 13.867 9.259l170.667 170.667c16.683 16.683 16.683 43.691 0 60.331s-43.691 16.683-60.331 0l-97.835-97.835v238.336c0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667z" />
|
||||
<glyph unicode="" glyph-name="arrow-up-circle" d="M981.333 426.667c0 63.488-12.629 124.16-35.541 179.499-23.808 57.472-58.667 109.141-101.888 152.363s-94.891 78.123-152.363 101.888c-55.381 22.955-116.053 35.584-179.541 35.584s-124.16-12.629-179.499-35.541c-57.472-23.808-109.141-58.667-152.363-101.931s-78.123-94.891-101.931-152.363c-22.912-55.339-35.541-116.011-35.541-179.499s12.629-124.16 35.541-179.499c23.808-57.472 58.667-109.141 101.888-152.363s94.891-78.123 152.363-101.888c55.381-22.955 116.053-35.584 179.541-35.584s124.16 12.629 179.499 35.541c57.472 23.808 109.141 58.667 152.363 101.888s78.123 94.891 101.888 152.363c22.955 55.381 35.584 116.053 35.584 179.541zM896 426.667c0-52.096-10.368-101.675-29.056-146.859-19.456-46.976-47.957-89.259-83.413-124.672s-77.739-63.957-124.672-83.413c-45.184-18.688-94.763-29.056-146.859-29.056s-101.675 10.368-146.859 29.056c-46.976 19.456-89.259 47.957-124.672 83.413-35.456 35.456-63.957 77.739-83.413 124.672-18.688 45.184-29.056 94.763-29.056 146.859s10.368 101.675 29.056 146.859c19.456 46.976 47.957 89.259 83.413 124.672s77.739 63.957 124.672 83.413c45.184 18.688 94.763 29.056 146.859 29.056s101.675-10.368 146.859-29.056c46.976-19.456 89.259-47.957 124.672-83.413 35.456-35.456 63.957-77.739 83.413-124.672 18.688-45.184 29.056-94.763 29.056-146.859zM554.667 256v238.336l97.835-97.835c16.683-16.683 43.691-16.683 60.331 0s16.683 43.691 0 60.331l-170.667 170.667c-0.085 0.085-0.128 0.128-0.213 0.213-4.053 3.968-8.661 6.997-13.611 9.045-10.453 4.309-22.229 4.309-32.683 0-4.949-2.048-9.557-5.077-13.611-9.045-0.085-0.085-0.128-0.128-0.213-0.213l-170.667-170.667c-16.683-16.683-16.683-43.691 0-60.331s43.691-16.683 60.331 0l97.835 97.835v-238.336c0-23.552 19.115-42.667 42.667-42.667s42.667 19.115 42.667 42.667z" />
|
||||
<glyph unicode="" glyph-name="copy" d="M469.333 597.334c-17.28 0-33.835-3.456-48.981-9.728-15.659-6.485-29.739-16-41.515-27.776s-21.291-25.856-27.776-41.515c-6.272-15.147-9.728-31.701-9.728-48.981v-384c0-17.28 3.456-33.835 9.728-48.981 6.485-15.701 16-29.781 27.776-41.557s25.856-21.291 41.557-27.776c15.104-6.229 31.659-9.685 48.939-9.685h384c17.28 0 33.835 3.456 48.981 9.728 15.701 6.485 29.781 16 41.557 27.776s21.291 25.856 27.776 41.557c6.229 15.104 9.685 31.659 9.685 48.939v384c0 17.28-3.456 33.835-9.728 48.981-6.485 15.701-16 29.781-27.776 41.557s-25.856 21.291-41.557 27.776c-15.104 6.229-31.659 9.685-48.939 9.685zM469.333 512h384c5.845 0 11.349-1.152 16.299-3.2 5.205-2.133 9.899-5.333 13.867-9.301s7.125-8.661 9.301-13.867c2.048-4.949 3.2-10.453 3.2-16.299v-384c0-5.845-1.152-11.349-3.2-16.299-2.133-5.205-5.333-9.899-9.301-13.867s-8.661-7.125-13.867-9.301c-4.949-2.048-10.453-3.2-16.299-3.2h-384c-5.845 0-11.349 1.152-16.299 3.2-5.205 2.133-9.899 5.333-13.867 9.301s-7.125 8.661-9.301 13.867c-2.048 4.949-3.2 10.453-3.2 16.299v384c0 5.845 1.152 11.349 3.2 16.299 2.133 5.205 5.333 9.899 9.301 13.867s8.661 7.125 13.867 9.301c4.949 2.048 10.453 3.2 16.299 3.2zM213.333 341.334h-42.667c-5.845 0-11.349 1.152-16.299 3.2-5.205 2.133-9.899 5.333-13.867 9.301s-7.125 8.661-9.301 13.867c-2.048 4.949-3.2 10.453-3.2 16.299v384c0 5.845 1.152 11.349 3.2 16.299 2.133 5.205 5.333 9.899 9.301 13.867s8.661 7.125 13.867 9.301c4.949 2.048 10.453 3.2 16.299 3.2h384c5.845 0 11.349-1.152 16.299-3.2 5.205-2.133 9.899-5.333 13.867-9.301s7.125-8.661 9.301-13.867c2.048-4.949 3.2-10.453 3.2-16.299v-42.667c0-23.552 19.115-42.667 42.667-42.667s42.667 19.115 42.667 42.667v42.667c0 17.28-3.456 33.835-9.728 48.981-6.485 15.701-16 29.781-27.776 41.557s-25.856 21.291-41.557 27.776c-15.104 6.229-31.659 9.685-48.939 9.685h-384c-17.28 0-33.835-3.456-48.981-9.728-15.659-6.485-29.739-16-41.515-27.776s-21.291-25.856-27.776-41.515c-6.272-15.147-9.728-31.701-9.728-48.981v-384c0-17.28 3.456-33.835 9.728-48.981 6.485-15.701 16-29.781 27.776-41.557s25.856-21.291 41.557-27.776c15.104-6.229 31.659-9.685 48.939-9.685h42.667c23.552 0 42.667 19.115 42.667 42.667s-19.115 42.667-42.667 42.667z" />
|
||||
<glyph unicode="" glyph-name="search" d="M684.416 262.144c-1.451-1.109-2.859-2.347-4.224-3.712s-2.56-2.731-3.712-4.224c-26.752-25.771-58.24-46.549-93.013-60.971-35.072-14.507-73.6-22.571-114.133-22.571s-79.061 8.064-114.219 22.613c-36.523 15.104-69.419 37.291-96.981 64.896s-49.749 60.459-64.896 96.981c-14.507 35.115-22.571 73.643-22.571 114.176s8.064 79.061 22.613 114.219c15.104 36.48 37.291 69.419 64.853 96.981s60.501 49.749 96.981 64.853c35.157 14.549 73.685 22.613 114.219 22.613s79.061-8.064 114.219-22.613c36.523-15.104 69.419-37.291 96.981-64.896s49.749-60.459 64.896-96.981c14.507-35.115 22.571-73.643 22.571-114.176s-8.064-79.061-22.613-114.219c-14.421-34.773-35.2-66.261-60.971-93.013zM926.165 72.832l-156.8 156.8c22.4 27.989 40.96 59.179 54.869 92.843 18.773 45.312 29.099 94.933 29.099 146.859s-10.325 101.547-29.099 146.859c-19.456 47.019-48 89.301-83.371 124.672s-77.653 63.915-124.672 83.371c-45.312 18.773-94.933 29.099-146.859 29.099s-101.547-10.325-146.859-29.099c-47.019-19.456-89.301-48-124.672-83.371s-63.915-77.653-83.371-124.672c-18.773-45.312-29.099-94.933-29.099-146.859s10.325-101.547 29.099-146.859c19.456-47.019 48-89.301 83.371-124.672s77.653-63.915 124.672-83.371c45.312-18.773 94.933-29.099 146.859-29.099s101.547 10.325 146.859 29.099c33.621 13.952 64.853 32.512 92.843 54.869l156.8-156.8c16.683-16.683 43.691-16.683 60.331 0s16.683 43.691 0 60.331z" />
|
||||
<glyph unicode="" glyph-name="printer" d="M725.333 597.334h-426.667v213.333h426.667zM213.333 128v-128c0-23.552 19.115-42.667 42.667-42.667h512c23.552 0 42.667 19.115 42.667 42.667v128h42.667c17.28 0 33.835 3.456 48.981 9.728 15.701 6.485 29.781 16 41.557 27.776s21.291 25.856 27.776 41.557c6.229 15.104 9.685 31.659 9.685 48.939v213.333c0 17.28-3.456 33.835-9.728 48.981-6.485 15.701-16 29.781-27.776 41.557s-25.856 21.291-41.557 27.776c-15.104 6.229-31.659 9.685-48.939 9.685h-42.667v256c0 23.552-19.115 42.667-42.667 42.667h-512c-23.552 0-42.667-19.115-42.667-42.667v-256h-42.667c-17.28 0-33.835-3.456-48.981-9.728-15.659-6.485-29.739-16-41.515-27.776s-21.291-25.856-27.776-41.515c-6.272-15.147-9.728-31.701-9.728-48.981v-213.333c0-17.28 3.456-33.835 9.728-48.981 6.485-15.701 16-29.781 27.776-41.557s25.856-21.291 41.557-27.776c15.104-6.229 31.659-9.685 48.939-9.685zM256 384c-23.552 0-42.667-19.115-42.667-42.667v-128h-42.667c-5.845 0-11.349 1.152-16.299 3.2-5.205 2.133-9.899 5.333-13.867 9.301s-7.125 8.661-9.301 13.867c-2.048 4.949-3.2 10.453-3.2 16.299v213.333c0 5.845 1.152 11.349 3.2 16.299 2.133 5.205 5.333 9.899 9.301 13.867s8.661 7.125 13.867 9.301c4.949 2.048 10.453 3.2 16.299 3.2h682.667c5.845 0 11.349-1.152 16.299-3.2 5.205-2.133 9.899-5.333 13.867-9.301s7.125-8.661 9.301-13.867c2.048-4.949 3.2-10.453 3.2-16.299v-213.333c0-5.845-1.152-11.349-3.2-16.299-2.133-5.205-5.333-9.899-9.301-13.867s-8.661-7.125-13.867-9.301c-4.949-2.048-10.453-3.2-16.299-3.2h-42.667v128c0 23.552-19.115 42.667-42.667 42.667zM298.667 298.667h426.667v-256h-426.667z" />
|
||||
<glyph unicode="" glyph-name="clock" d="M981.333 426.667c0 63.488-12.629 124.16-35.541 179.499-23.808 57.472-58.667 109.141-101.888 152.363s-94.891 78.123-152.363 101.888c-55.381 22.955-116.053 35.584-179.541 35.584s-124.16-12.629-179.499-35.541c-57.472-23.808-109.141-58.667-152.363-101.931s-78.123-94.891-101.931-152.363c-22.912-55.339-35.541-116.011-35.541-179.499s12.629-124.16 35.541-179.499c23.808-57.472 58.667-109.141 101.888-152.363s94.891-78.123 152.363-101.888c55.381-22.955 116.053-35.584 179.541-35.584s124.16 12.629 179.499 35.541c57.472 23.808 109.141 58.667 152.363 101.888s78.123 94.891 101.888 152.363c22.955 55.381 35.584 116.053 35.584 179.541zM896 426.667c0-52.096-10.368-101.675-29.056-146.859-19.456-46.976-47.957-89.259-83.413-124.672s-77.739-63.957-124.672-83.413c-45.184-18.688-94.763-29.056-146.859-29.056s-101.675 10.368-146.859 29.056c-46.976 19.456-89.259 47.957-124.672 83.413-35.456 35.456-63.957 77.739-83.413 124.672-18.688 45.184-29.056 94.763-29.056 146.859s10.368 101.675 29.056 146.859c19.456 46.976 47.957 89.259 83.413 124.672s77.739 63.957 124.672 83.413c45.184 18.688 94.763 29.056 146.859 29.056s101.675-10.368 146.859-29.056c46.976-19.456 89.259-47.957 124.672-83.413 35.456-35.456 63.957-77.739 83.413-124.672 18.688-45.184 29.056-94.763 29.056-146.859zM469.333 682.667v-256c0-16.597 9.472-31.019 23.595-38.144l170.667-85.333c21.077-10.539 46.72-2.005 57.259 19.072s2.005 46.72-19.072 57.259l-147.115 73.515v229.632c0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667z" />
|
||||
<glyph unicode="" glyph-name="download" d="M853.333 298.667v-170.667c0-5.845-1.152-11.349-3.2-16.299-2.133-5.205-5.333-9.899-9.301-13.867s-8.661-7.125-13.867-9.301c-4.949-2.048-10.453-3.2-16.299-3.2h-597.333c-5.845 0-11.349 1.152-16.299 3.2-5.205 2.133-9.899 5.333-13.867 9.301s-7.125 8.661-9.301 13.867c-2.048 4.949-3.2 10.453-3.2 16.299v170.667c0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667v-170.667c0-17.28 3.456-33.835 9.728-48.981 6.485-15.701 16-29.781 27.776-41.557s25.856-21.291 41.557-27.776c15.104-6.229 31.659-9.685 48.939-9.685h597.333c17.28 0 33.835 3.456 48.981 9.728 15.701 6.485 29.781 16 41.557 27.776s21.291 25.856 27.776 41.557c6.229 15.104 9.685 31.659 9.685 48.939v170.667c0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667zM554.667 401.664v409.003c0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667v-409.003l-140.501 140.501c-16.683 16.683-43.691 16.683-60.331 0s-16.683-43.691 0-60.331l213.333-213.333c0.085-0.085 0.171-0.171 0.256-0.256 4.053-3.968 8.661-6.955 13.568-9.003 5.12-2.133 10.624-3.2 16.085-3.243 0.171 0 0.341 0 0.469 0 5.461 0.043 10.965 1.109 16.085 3.243 5.035 2.091 9.728 5.163 13.824 9.259l213.333 213.333c16.683 16.683 16.683 43.691 0 60.331s-43.691 16.683-60.331 0z" />
|
||||
<glyph unicode="" glyph-name="plus" d="M213.333 384h256v-256c0-23.552 19.115-42.667 42.667-42.667s42.667 19.115 42.667 42.667v256h256c23.552 0 42.667 19.115 42.667 42.667s-19.115 42.667-42.667 42.667h-256v256c0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667v-256h-256c-23.552 0-42.667-19.115-42.667-42.667s19.115-42.667 42.667-42.667z" />
|
||||
<glyph unicode="" glyph-name="minus" d="M213.333 384h597.333c23.552 0 42.667 19.115 42.667 42.667s-19.115 42.667-42.667 42.667h-597.333c-23.552 0-42.667-19.115-42.667-42.667s19.115-42.667 42.667-42.667z" />
|
||||
<glyph unicode="" glyph-name="sort-amount-asc" d="M320 192v768h-128v-768h-160l224-224 224 224h-160zM448 384h576v-128h-576v128zM448 576h448v-128h-448v128zM448 768h320v-128h-320v128zM448 960h192v-128h-192v128z" />
|
||||
<glyph unicode="🗎" glyph-name="maximize" d="M341.333 853.334h-128c-17.28 0-33.835-3.456-48.981-9.728-15.659-6.485-29.739-16-41.515-27.776s-21.291-25.856-27.776-41.515c-6.272-15.147-9.728-31.701-9.728-48.981v-128c0-23.552 19.115-42.667 42.667-42.667s42.667 19.115 42.667 42.667v128c0 5.845 1.152 11.349 3.2 16.299 2.133 5.205 5.333 9.899 9.301 13.867s8.661 7.125 13.867 9.301c4.949 2.048 10.453 3.2 16.299 3.2h128c23.552 0 42.667 19.115 42.667 42.667s-19.115 42.667-42.667 42.667zM938.667 597.334v128c0 17.28-3.456 33.835-9.728 48.981-6.485 15.701-16 29.781-27.776 41.557s-25.856 21.291-41.557 27.776c-15.104 6.229-31.659 9.685-48.939 9.685h-128c-23.552 0-42.667-19.115-42.667-42.667s19.115-42.667 42.667-42.667h128c5.845 0 11.349-1.152 16.299-3.2 5.205-2.133 9.899-5.333 13.867-9.301s7.125-8.661 9.301-13.867c2.048-4.949 3.2-10.453 3.2-16.299v-128c0-23.552 19.115-42.667 42.667-42.667s42.667 19.115 42.667 42.667zM682.667-0h128c17.28 0 33.835 3.456 48.981 9.728 15.701 6.485 29.781 16 41.557 27.776s21.291 25.856 27.776 41.557c6.229 15.104 9.685 31.659 9.685 48.939v128c0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667v-128c0-5.845-1.152-11.349-3.2-16.299-2.133-5.205-5.333-9.899-9.301-13.867s-8.661-7.125-13.867-9.301c-4.949-2.048-10.453-3.2-16.299-3.2h-128c-23.552 0-42.667-19.115-42.667-42.667s19.115-42.667 42.667-42.667zM85.333 256v-128c0-17.28 3.456-33.835 9.728-48.981 6.485-15.701 16-29.781 27.776-41.557s25.856-21.291 41.557-27.776c15.104-6.229 31.659-9.685 48.939-9.685h128c23.552 0 42.667 19.115 42.667 42.667s-19.115 42.667-42.667 42.667h-128c-5.845 0-11.349 1.152-16.299 3.2-5.205 2.133-9.899 5.333-13.867 9.301s-7.125 8.661-9.301 13.867c-2.048 4.949-3.2 10.453-3.2 16.299v128c0 23.552-19.115 42.667-42.667 42.667s-42.667-19.115-42.667-42.667z" />
|
||||
</font></defs></svg>
|
After Width: | Height: | Size: 23 KiB |
Binary file not shown.
Binary file not shown.
440
InvenTree/InvenTree/static/bootstrap-table/themes/bulma/bootstrap-table-bulma.css
vendored
Normal file
440
InvenTree/InvenTree/static/bootstrap-table/themes/bulma/bootstrap-table-bulma.css
vendored
Normal file
@ -0,0 +1,440 @@
|
||||
/**
|
||||
* @author zhixin wen <wenzhixin2010@gmail.com>
|
||||
* https://github.com/wenzhixin/bootstrap-table/
|
||||
* theme: https://github.com/jgthms/bulma/
|
||||
*/
|
||||
.bootstrap-table .fixed-table-toolbar::after {
|
||||
content: "";
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .bs-bars,
|
||||
.bootstrap-table .fixed-table-toolbar .search,
|
||||
.bootstrap-table .fixed-table-toolbar .columns {
|
||||
position: relative;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns .btn-group > .btn-group {
|
||||
display: inline-block;
|
||||
margin-left: -1px !important;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns .btn-group > .btn-group > .btn {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns .btn-group > .btn-group:first-child > .btn {
|
||||
border-top-left-radius: 4px;
|
||||
border-bottom-left-radius: 4px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns .btn-group > .btn-group:last-child > .btn {
|
||||
border-top-right-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns .dropdown-menu {
|
||||
text-align: left;
|
||||
max-height: 300px;
|
||||
overflow: auto;
|
||||
-ms-overflow-style: scrollbar;
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns label {
|
||||
display: block;
|
||||
padding: 3px 20px;
|
||||
clear: both;
|
||||
font-weight: normal;
|
||||
line-height: 1.428571429;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns-left {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns-right {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .pull-right .dropdown-menu {
|
||||
right: 0;
|
||||
left: auto;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container {
|
||||
position: relative;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table {
|
||||
width: 100%;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table th,
|
||||
.bootstrap-table .fixed-table-container .table td {
|
||||
vertical-align: middle;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table thead th {
|
||||
vertical-align: bottom;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table thead th:focus {
|
||||
outline: 0 solid transparent;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table thead th.detail {
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table thead th .th-inner {
|
||||
padding: 0.75rem;
|
||||
vertical-align: bottom;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table thead th .sortable {
|
||||
cursor: pointer;
|
||||
background-position: right;
|
||||
background-repeat: no-repeat;
|
||||
padding-right: 30px !important;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table thead th .both {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAQAAADYWf5HAAAAkElEQVQoz7X QMQ5AQBCF4dWQSJxC5wwax1Cq1e7BAdxD5SL+Tq/QCM1oNiJidwox0355mXnG/DrEtIQ6azioNZQxI0ykPhTQIwhCR+BmBYtlK7kLJYwWCcJA9M4qdrZrd8pPjZWPtOqdRQy320YSV17OatFC4euts6z39GYMKRPCTKY9UnPQ6P+GtMRfGtPnBCiqhAeJPmkqAAAAAElFTkSuQmCC");
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table thead th .asc {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZ0lEQVQ4y2NgGLKgquEuFxBPAGI2ahhWCsS/gDibUoO0gPgxEP8H4ttArEyuQYxAPBdqEAxPBImTY5gjEL9DM+wTENuQahAvEO9DMwiGdwAxOymGJQLxTyD+jgWDxCMZRsEoGAVoAADeemwtPcZI2wAAAABJRU5ErkJggg==");
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table thead th .desc {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZUlEQVQ4y2NgGAWjYBSggaqGu5FA/BOIv2PBIPFEUgxjB+IdQPwfC94HxLykus4GiD+hGfQOiB3J8SojEE9EM2wuSJzcsFMG4ttQgx4DsRalkZENxL+AuJQaMcsGxBOAmGvopk8AVz1sLZgg0bsAAAAASUVORK5CYII= ");
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table tbody tr.selected td {
|
||||
background-color: #fafafa;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table tbody tr.no-records-found td {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table tbody tr .card-view {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table tbody tr .card-view .card-view-title {
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
min-width: 30%;
|
||||
width: auto !important;
|
||||
text-align: left !important;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table tbody tr .card-view .card-view-value {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table .bs-checkbox {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table .bs-checkbox label {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table .bs-checkbox label input[type="radio"],
|
||||
.bootstrap-table .fixed-table-container .table .bs-checkbox label input[type="checkbox"] {
|
||||
margin: 0 auto !important;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table.table-sm .th-inner {
|
||||
padding: 0.3rem;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container.fixed-height:not(.has-footer) {
|
||||
border-bottom: 1px solid #dbdbdb;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container.fixed-height.has-card-view {
|
||||
border-top: 1px solid #dbdbdb;
|
||||
border-bottom: 1px solid #dbdbdb;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container.fixed-height .fixed-table-border {
|
||||
border-left: 1px solid #dbdbdb;
|
||||
border-right: 1px solid #dbdbdb;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container.fixed-height .table thead th {
|
||||
border-bottom: 1px solid #dbdbdb;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container.fixed-height .table-dark thead th {
|
||||
border-bottom: 1px solid #32383e;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-header {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body {
|
||||
overflow-x: auto;
|
||||
overflow-y: auto;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading {
|
||||
align-items: center;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
z-index: 1000;
|
||||
transition: visibility 0s, opacity 0.15s ease-in-out;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.open {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap {
|
||||
align-items: baseline;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .loading-text {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-dot,
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::after,
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::before {
|
||||
content: "";
|
||||
animation-duration: 1.5s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-name: LOADING;
|
||||
background: #363636;
|
||||
border-radius: 50%;
|
||||
display: block;
|
||||
height: 5px;
|
||||
margin: 0 4px;
|
||||
opacity: 0;
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-dot {
|
||||
animation-delay: 0.3s;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::after {
|
||||
animation-delay: 0.6s;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark {
|
||||
background: #363636;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-dot,
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-wrap::after,
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-wrap::before {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-footer {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination::after {
|
||||
content: "";
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination-detail,
|
||||
.bootstrap-table .fixed-table-pagination > .pagination {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination-detail .pagination-info {
|
||||
line-height: 34px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination-detail .page-list {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination-detail .page-list .btn-group {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination-detail .page-list .btn-group .dropdown-menu {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination ul.pagination {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination ul.pagination li.page-intermediate a {
|
||||
color: #c8c8c8;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination ul.pagination li.page-intermediate a::before {
|
||||
content: '\2B05';
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination ul.pagination li.page-intermediate a::after {
|
||||
content: '\27A1';
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination ul.pagination li.disabled a {
|
||||
pointer-events: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.bootstrap-table.fullscreen {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1050;
|
||||
width: 100% !important;
|
||||
background: #fff;
|
||||
height: calc(100vh);
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.bootstrap-table.bootstrap4 .pagination-lg .page-link, .bootstrap-table.bootstrap5 .pagination-lg .page-link {
|
||||
padding: .5rem 1rem;
|
||||
}
|
||||
|
||||
.bootstrap-table.bootstrap5 .float-left {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.bootstrap-table.bootstrap5 .float-right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
/* calculate scrollbar width */
|
||||
div.fixed-table-scroll-inner {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
div.fixed-table-scroll-outer {
|
||||
top: 0;
|
||||
left: 0;
|
||||
visibility: hidden;
|
||||
width: 200px;
|
||||
height: 150px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@keyframes LOADING {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.box {
|
||||
background-color: #fff;
|
||||
border-radius: 6px;
|
||||
color: #4a4a4a;
|
||||
display: block;
|
||||
padding: 1.25rem;
|
||||
}
|
||||
|
||||
.bootstrap-table .float-left {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.bootstrap-table .float-right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .search input {
|
||||
width: auto;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns {
|
||||
margin-right: 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .button.dropdown {
|
||||
padding: 0;
|
||||
border: 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .button.dropdown .button {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .button.dropdown:not(:first-child) .button {
|
||||
border-bottom-left-radius: 0;
|
||||
border-top-left-radius: 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .button.dropdown:last-child .button {
|
||||
border-bottom-right-radius: 4px;
|
||||
border-top-right-radius: 4px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .button.dropdown .dropdown-content {
|
||||
box-shadow: none;
|
||||
border: 1px solid #dbdbdb;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .button.dropdown label.dropdown-item {
|
||||
padding: 5px 20px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination .ui.dropdown {
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination .is-up .fa-angle-down:before {
|
||||
content: "\f106";
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination .pagination-link.disabled {
|
||||
background-color: #dbdbdb;
|
||||
border-color: #dbdbdb;
|
||||
box-shadow: none;
|
||||
color: #7a7a7a;
|
||||
opacity: .5;
|
||||
cursor: not-allowed;
|
||||
}
|
1116
InvenTree/InvenTree/static/bootstrap-table/themes/bulma/bootstrap-table-bulma.js
vendored
Normal file
1116
InvenTree/InvenTree/static/bootstrap-table/themes/bulma/bootstrap-table-bulma.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
10
InvenTree/InvenTree/static/bootstrap-table/themes/bulma/bootstrap-table-bulma.min.css
vendored
Normal file
10
InvenTree/InvenTree/static/bootstrap-table/themes/bulma/bootstrap-table-bulma.min.css
vendored
Normal file
File diff suppressed because one or more lines are too long
10
InvenTree/InvenTree/static/bootstrap-table/themes/bulma/bootstrap-table-bulma.min.js
vendored
Normal file
10
InvenTree/InvenTree/static/bootstrap-table/themes/bulma/bootstrap-table-bulma.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
423
InvenTree/InvenTree/static/bootstrap-table/themes/foundation/bootstrap-table-foundation.css
vendored
Normal file
423
InvenTree/InvenTree/static/bootstrap-table/themes/foundation/bootstrap-table-foundation.css
vendored
Normal file
@ -0,0 +1,423 @@
|
||||
/**
|
||||
* @author zhixin wen <wenzhixin2010@gmail.com>
|
||||
* https://github.com/wenzhixin/bootstrap-table/
|
||||
* theme: https://github.com/jgthms/bulma/
|
||||
*/
|
||||
.bootstrap-table .fixed-table-toolbar::after {
|
||||
content: "";
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .bs-bars,
|
||||
.bootstrap-table .fixed-table-toolbar .search,
|
||||
.bootstrap-table .fixed-table-toolbar .columns {
|
||||
position: relative;
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns .btn-group > .btn-group {
|
||||
display: inline-block;
|
||||
margin-left: -1px !important;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns .btn-group > .btn-group > .btn {
|
||||
border-radius: 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns .btn-group > .btn-group:first-child > .btn {
|
||||
border-top-left-radius: 4px;
|
||||
border-bottom-left-radius: 4px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns .btn-group > .btn-group:last-child > .btn {
|
||||
border-top-right-radius: 4px;
|
||||
border-bottom-right-radius: 4px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns .dropdown-menu {
|
||||
text-align: left;
|
||||
max-height: 300px;
|
||||
overflow: auto;
|
||||
-ms-overflow-style: scrollbar;
|
||||
z-index: 1001;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns label {
|
||||
display: block;
|
||||
padding: 3px 20px;
|
||||
clear: both;
|
||||
font-weight: normal;
|
||||
line-height: 1.428571429;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns-left {
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .columns-right {
|
||||
margin-left: 5px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .pull-right .dropdown-menu {
|
||||
right: 0;
|
||||
left: auto;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container {
|
||||
position: relative;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table {
|
||||
width: 100%;
|
||||
margin-bottom: 0 !important;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table th,
|
||||
.bootstrap-table .fixed-table-container .table td {
|
||||
vertical-align: middle;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table thead th {
|
||||
vertical-align: bottom;
|
||||
padding: 0;
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table thead th:focus {
|
||||
outline: 0 solid transparent;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table thead th.detail {
|
||||
width: 30px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table thead th .th-inner {
|
||||
padding: 0.75rem;
|
||||
vertical-align: bottom;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table thead th .sortable {
|
||||
cursor: pointer;
|
||||
background-position: right;
|
||||
background-repeat: no-repeat;
|
||||
padding-right: 30px !important;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table thead th .both {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAQAAADYWf5HAAAAkElEQVQoz7X QMQ5AQBCF4dWQSJxC5wwax1Cq1e7BAdxD5SL+Tq/QCM1oNiJidwox0355mXnG/DrEtIQ6azioNZQxI0ykPhTQIwhCR+BmBYtlK7kLJYwWCcJA9M4qdrZrd8pPjZWPtOqdRQy320YSV17OatFC4euts6z39GYMKRPCTKY9UnPQ6P+GtMRfGtPnBCiqhAeJPmkqAAAAAElFTkSuQmCC");
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table thead th .asc {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZ0lEQVQ4y2NgGLKgquEuFxBPAGI2ahhWCsS/gDibUoO0gPgxEP8H4ttArEyuQYxAPBdqEAxPBImTY5gjEL9DM+wTENuQahAvEO9DMwiGdwAxOymGJQLxTyD+jgWDxCMZRsEoGAVoAADeemwtPcZI2wAAAABJRU5ErkJggg==");
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table thead th .desc {
|
||||
background-image: url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABMAAAATCAYAAAByUDbMAAAAZUlEQVQ4y2NgGAWjYBSggaqGu5FA/BOIv2PBIPFEUgxjB+IdQPwfC94HxLykus4GiD+hGfQOiB3J8SojEE9EM2wuSJzcsFMG4ttQgx4DsRalkZENxL+AuJQaMcsGxBOAmGvopk8AVz1sLZgg0bsAAAAASUVORK5CYII= ");
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table tbody tr.selected td {
|
||||
background-color: #f9f9f9;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table tbody tr.no-records-found td {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table tbody tr .card-view {
|
||||
display: flex;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table tbody tr .card-view .card-view-title {
|
||||
font-weight: bold;
|
||||
display: inline-block;
|
||||
min-width: 30%;
|
||||
width: auto !important;
|
||||
text-align: left !important;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table tbody tr .card-view .card-view-value {
|
||||
width: 100% !important;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table .bs-checkbox {
|
||||
text-align: center;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table .bs-checkbox label {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table .bs-checkbox label input[type="radio"],
|
||||
.bootstrap-table .fixed-table-container .table .bs-checkbox label input[type="checkbox"] {
|
||||
margin: 0 auto !important;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .table.table-sm .th-inner {
|
||||
padding: 0.3rem;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container.fixed-height:not(.has-footer) {
|
||||
border-bottom: 1px solid #f1f1f1;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container.fixed-height.has-card-view {
|
||||
border-top: 1px solid #f1f1f1;
|
||||
border-bottom: 1px solid #f1f1f1;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container.fixed-height .fixed-table-border {
|
||||
border-left: 1px solid #f1f1f1;
|
||||
border-right: 1px solid #f1f1f1;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container.fixed-height .table thead th {
|
||||
border-bottom: 1px solid #f1f1f1;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container.fixed-height .table-dark thead th {
|
||||
border-bottom: 1px solid #32383e;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-header {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body {
|
||||
overflow-x: auto;
|
||||
overflow-y: auto;
|
||||
height: 100%;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading {
|
||||
align-items: center;
|
||||
background: #fff;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
position: absolute;
|
||||
bottom: 0;
|
||||
width: 100%;
|
||||
z-index: 1000;
|
||||
transition: visibility 0s, opacity 0.15s ease-in-out;
|
||||
opacity: 0;
|
||||
visibility: hidden;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.open {
|
||||
visibility: visible;
|
||||
opacity: 1;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap {
|
||||
align-items: baseline;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .loading-text {
|
||||
margin-right: 6px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap {
|
||||
align-items: center;
|
||||
display: flex;
|
||||
justify-content: center;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-dot,
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::after,
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::before {
|
||||
content: "";
|
||||
animation-duration: 1.5s;
|
||||
animation-iteration-count: infinite;
|
||||
animation-name: LOADING;
|
||||
background: rgba(0, 0, 0, 0.87);
|
||||
border-radius: 50%;
|
||||
display: block;
|
||||
height: 5px;
|
||||
margin: 0 4px;
|
||||
opacity: 0;
|
||||
width: 5px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-dot {
|
||||
animation-delay: 0.3s;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading .loading-wrap .animation-wrap::after {
|
||||
animation-delay: 0.6s;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark {
|
||||
background: rgba(0, 0, 0, 0.87);
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-dot,
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-wrap::after,
|
||||
.bootstrap-table .fixed-table-container .fixed-table-body .fixed-table-loading.table-dark .animation-wrap::before {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-container .fixed-table-footer {
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination::after {
|
||||
content: "";
|
||||
display: block;
|
||||
clear: both;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination-detail,
|
||||
.bootstrap-table .fixed-table-pagination > .pagination {
|
||||
margin-top: 10px;
|
||||
margin-bottom: 10px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination-detail .pagination-info {
|
||||
line-height: 34px;
|
||||
margin-right: 5px;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination-detail .page-list {
|
||||
display: inline-block;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination-detail .page-list .btn-group {
|
||||
position: relative;
|
||||
display: inline-block;
|
||||
vertical-align: middle;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination-detail .page-list .btn-group .dropdown-menu {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination ul.pagination {
|
||||
margin: 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination ul.pagination li.page-intermediate a {
|
||||
color: #c8c8c8;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination ul.pagination li.page-intermediate a::before {
|
||||
content: '\2B05';
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination ul.pagination li.page-intermediate a::after {
|
||||
content: '\27A1';
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination > .pagination ul.pagination li.disabled a {
|
||||
pointer-events: none;
|
||||
cursor: default;
|
||||
}
|
||||
|
||||
.bootstrap-table.fullscreen {
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
z-index: 1050;
|
||||
width: 100% !important;
|
||||
background: #fff;
|
||||
height: calc(100vh);
|
||||
overflow-y: scroll;
|
||||
}
|
||||
|
||||
.bootstrap-table.bootstrap4 .pagination-lg .page-link, .bootstrap-table.bootstrap5 .pagination-lg .page-link {
|
||||
padding: .5rem 1rem;
|
||||
}
|
||||
|
||||
.bootstrap-table.bootstrap5 .float-left {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.bootstrap-table.bootstrap5 .float-right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
/* calculate scrollbar width */
|
||||
div.fixed-table-scroll-inner {
|
||||
width: 100%;
|
||||
height: 200px;
|
||||
}
|
||||
|
||||
div.fixed-table-scroll-outer {
|
||||
top: 0;
|
||||
left: 0;
|
||||
visibility: hidden;
|
||||
width: 200px;
|
||||
height: 150px;
|
||||
overflow: hidden;
|
||||
}
|
||||
|
||||
@keyframes LOADING {
|
||||
0% {
|
||||
opacity: 0;
|
||||
}
|
||||
50% {
|
||||
opacity: 1;
|
||||
}
|
||||
to {
|
||||
opacity: 0;
|
||||
}
|
||||
}
|
||||
|
||||
.bootstrap-table .float-left {
|
||||
float: left;
|
||||
}
|
||||
|
||||
.bootstrap-table .float-right {
|
||||
float: right;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .search input {
|
||||
height: 2.5293rem;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .keep-open.dropdown-container .button:hover .menu {
|
||||
background: #fff;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .keep-open.dropdown-container .menu li {
|
||||
padding: 5px 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar .keep-open.dropdown-container .menu li label {
|
||||
white-space: nowrap;
|
||||
text-align: left;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-toolbar input,
|
||||
.bootstrap-table .fixed-table-toolbar .button {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination .page-list > div {
|
||||
display: inline;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination .button {
|
||||
margin-bottom: 0;
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination .dropup .fa-angle-down:before {
|
||||
content: "\f106";
|
||||
}
|
||||
|
||||
.bootstrap-table .fixed-table-pagination .page-item {
|
||||
padding: 6px 12px;
|
||||
line-height: 1.428571429;
|
||||
}
|
||||
|
||||
.bootstrap-table .dropdown-pane {
|
||||
width: auto;
|
||||
padding: 0.5rem;
|
||||
}
|
1132
InvenTree/InvenTree/static/bootstrap-table/themes/foundation/bootstrap-table-foundation.js
vendored
Normal file
1132
InvenTree/InvenTree/static/bootstrap-table/themes/foundation/bootstrap-table-foundation.js
vendored
Normal file
File diff suppressed because it is too large
Load Diff
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user