adminterface 0.2.3 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/LICENSE.md +19 -109
- data/README.md +19 -21
- data/app/assets/stylesheets/adminterface/components/_form.scss +1 -0
- data/app/assets/stylesheets/adminterface/mixins/_utilities.scss +4 -0
- data/app/assets/stylesheets/adminterface/vendors/bootstrap/_all.scss +1 -0
- data/app/assets/stylesheets/adminterface/vendors/bootstrap/components/_breadcrumb.scss +1 -0
- data/app/assets/stylesheets/adminterface/vendors/flatpickr/{main.scss → _main.scss} +6 -0
- data/app/javascript/adminterface/lib/__tests__/detached_dropdown.spec.js +69 -69
- data/app/javascript/adminterface/lib/__tests__/has_many.html +13 -13
- data/app/javascript/adminterface/lib/__tests__/header_toggler.html +2 -0
- data/app/javascript/adminterface/lib/__tests__/header_toggler.spec.js +212 -0
- data/app/javascript/adminterface/lib/__tests__/per_page.spec.js +75 -0
- data/app/javascript/adminterface/lib/header_toggler.js +10 -8
- data/app/views/layouts/active_admin_logged_out.html.erb +1 -1
- data/lib/adminterface/configs.rb +5 -1
- data/lib/adminterface/engine.rb +0 -7
- data/lib/adminterface/extensions/namespace_settings.rb +3 -0
- data/lib/adminterface/extensions/views/pages/base.rb +2 -0
- data/lib/adminterface/version.rb +1 -1
- data/lib/adminterface.rb +0 -31
- metadata +9 -12
- data/lib/adminterface/encryption/encryptor.rb +0 -26
- data/lib/adminterface/license.rb +0 -18
- data/lib/adminterface/licensing/base.rb +0 -99
- data/lib/adminterface/licensing/commercial.rb +0 -7
- data/lib/adminterface/licensing/notice.rb +0 -54
- data/lib/adminterface/licensing/personal.rb +0 -13
- data/lib/adminterface/public.pem +0 -9
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 18beaba0b474654bab684cc576d19be2c8a7d4c1c956733196fd428ff127381c
|
4
|
+
data.tar.gz: 5fa18f21332acb4d428670e70923fdab7880172bfedcce2faf4f1a742b6426a0
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 9c48cbd82014ac58317f0d8a6c6d70cc2c05b24ff977deccfe4fe0cd23d42680e7ae5c7d009a1b27b5eab08f28a7123e087522fd608874a41e64e766e77ebfb7
|
7
|
+
data.tar.gz: 86f4704e4c64c1f62a66e70c4924183f558bf1f4f84550e94a48d04df9598f23e589764dd330cbdf7d029d007caa7d56f51b7dee7c263cf5c355eb72d491a42b
|
data/LICENSE.md
CHANGED
@@ -1,109 +1,19 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
- **"Repository"** means the GitHub repository located at https://github.com/CMDBrew/adminterface
|
21
|
-
|
22
|
-
- **"Software"** means source code, object code, API's, and all works incorporated into each of the foregoing.
|
23
|
-
|
24
|
-
- **"Territory"** means all countries not subject to U.S. and Canada export restrictions.
|
25
|
-
|
26
|
-
- **"Third-Party Materials"** means items not owned by us, which may include Software (such as open-source software or third-party add-ons or extensions), content, data, information, or other materials.
|
27
|
-
|
28
|
-
- **"Update"** means any change, patch, fix, upgrade, adaptation, alteration, translation, correction, revision, supplement, enhancement, improvement, or other modification of the Software that we may, in our sole discretion, release from time to time. An Update may consist, among other things, of modifications to the Software's features, functionality, or interfaces.
|
29
|
-
|
30
|
-
## Permitted Use
|
31
|
-
One single project license grants the right to use the Software on one domain and unlimited development sites. Each additional domain using the Software requires an additional purchased license. Contact contact@cmdbrew.com for other types of project licenses.
|
32
|
-
|
33
|
-
Provided that you remain at all times in compliance with the terms and conditions of this Agreement, we hereby grant to you a limited, non-exclusive, non-sub-licensable, non-transferable license to use the Software within the Territory for any purpose not prohibited by this Agreement or applicable Law (the "License"). All rights not expressly granted herein are reserved. You acknowledge that the License is adequate consideration for all your responsibilities, representations, and warranties hereunder.
|
34
|
-
|
35
|
-
**If you're developing a product for a client, you should purchase a license on behalf of the customer and transfer ownership of the license to them.**
|
36
|
-
|
37
|
-
## Restrictions
|
38
|
-
Unless you have been granted prior, written consent from CMDBrew Studio Inc., you may not:
|
39
|
-
* Reproduce, distribute, or transfer the Software, or portions thereof, to any third party.
|
40
|
-
* Sell, rent, lease, assign, or sublet the Software or portions thereof.
|
41
|
-
* Bypass, block, or disable the Software's licensing-verification mechanisms.
|
42
|
-
* Grant rights to any other person.
|
43
|
-
* Dispute our ownership of the Software or any Intellectual Property Right therein.
|
44
|
-
* Use the Software in violation of any U.S., Canada, and international law or regulation.
|
45
|
-
* Develop or provide a competing product or service.
|
46
|
-
* Use the Software for any purpose that is to our detriment or commercial disadvantage.
|
47
|
-
|
48
|
-
## Requirements
|
49
|
-
You will:
|
50
|
-
|
51
|
-
* Comply with all applicable Laws.
|
52
|
-
* Promptly notify us if you become aware of any actual or suspected infringement or misuse of the Software or any claim that the Software infringes the rights of any Person.
|
53
|
-
* Cooperate with and assist us in all reasonable ways to stop or prevent any actual or threatened infringement or misuse of the Software.
|
54
|
-
* Retain (and not alter or obscure) this Agreement and all trademarks, copyright notices, and other notices of Intellectual Property Rights in connection with the Software; and for any Non-Personal Purpose that you control or operate directly or indirectly, including by way of a contractor or intermediary.
|
55
|
-
* Notify us of the domain name utilized by the Software and the name and email of the individual(s) or organization that purchased the license.
|
56
|
-
* Pay the fee set forth in Section "Fees".
|
57
|
-
|
58
|
-
## Fees and License
|
59
|
-
Please refer to [Pricing](https://adminterface.io/pricing) for our license fees.
|
60
|
-
|
61
|
-
* **Personal:** Free forever. The Software is free of charge for Personal Purposes.
|
62
|
-
|
63
|
-
* **Commercial:** The software is being used for Non-Personal Purposes. Requires a yearly license subscription fee.
|
64
|
-
|
65
|
-
* **Payment Terms:** The yearly license subscription fee, as paid at time of purchase or license renewal, (the "Subscription") for each domain operating a Non-Personal Purpose app, is due at the time of Licence purchase until your subscription expires.
|
66
|
-
* All fees are exclusive of taxes. You are responsible for any taxes, duties, or other charges imposed by any governmental authority on amounts paid by you pursuant hereto (except for any taxes imposed on our income).
|
67
|
-
* Payment must be made in U.S. dollars by Credit Card.
|
68
|
-
* Interest will accrue on any past-due amount at the rate of 1.8% per month, compounded monthly, or the highest rate permitted by applicable Law, whichever is less. You will reimburse us immediately for all costs we incur in relation to any overdue amount, including attorney fees and collection-agency fees, whether or not a formal complaint is filed, and no notice of default is required.
|
69
|
-
* In the event that any amount becomes overdue, we may, in our sole discretion, and in addition to any other remedy available at law, in equity, or hereunder: (i) suspend or revoke your License, (ii) withhold new licenses, or (iii) terminate this Agreement. You will pay all amounts due hereunder in full, without any setoff, deduction, or withholding for any reason, except as may be permitted by applicable Law. No refunds will be granted or paid.
|
70
|
-
|
71
|
-
## Making Copies
|
72
|
-
You may make copies of the Software for back-up purposes, provided that you reproduce the Software in its original form and with all proprietary notices on the back-up copy.
|
73
|
-
|
74
|
-
## Software Modification
|
75
|
-
You may alter, modify, or extend the Software for your own use, or commission a third-party to perform modifications for you, but you may not resell, redistribute or transfer the modified or derivative version without prior written consent from CMDBrew Studio Inc.. Components from the Software may not be extracted and used in other programs without prior written consent from CMDBrew Studio Inc..
|
76
|
-
|
77
|
-
## Technical Support
|
78
|
-
CMDBrew Studio Inc. does not provide direct phone support at this time. No representations or guarantees are made regarding the response time in which e-mail support questions are answered, but we will do our best to respond quickly.
|
79
|
-
|
80
|
-
## Open-Source Components
|
81
|
-
The Software makes use of certain [Open-Source Components or other licensing](https://adminterface.io/docs/start/dependencies/) that does not restrict commercial use. Your use of Open-Source Components, including those on which the Software relies, is subject to the terms and conditions of the applicable open-source licenses. A copy of each license can be found at https://choosealicense.com/licenses/.
|
82
|
-
|
83
|
-
## Ownership
|
84
|
-
CMDBrew Studio Inc. is the sole and exclusive owner of all right, title, and interest in and to the Software, all Updates thereto, all Official Extensions based thereon, and all Intellectual Property Rights in each of the foregoing, subject only to the rights of third parties in Open-Source Components and the License granted hereunder. You acknowledge that you have no ownership interest in the Software and that, except for the License granted hereunder, nothing herein or elsewhere grants to you or any third party (by implication, waiver, estoppel or otherwise) any right, title, or interest (including any Intellectual Property Right) in or to the Software or any portion thereof.
|
85
|
-
|
86
|
-
## Contributions
|
87
|
-
If you make a Contribution to the Software, you hereby automatically, immediately, unconditionally, and irrevocably assign the entirety of such Contribution to CMDBrew Studio Inc. for no additional consideration beyond the License granted hereunder. Similarly, in the event that you acquire (by any means) or have (at any time) any right, title, or interest (including any Intellectual Property Right) in or to the Software beyond the License granted hereunder, you hereby automatically, immediately, unconditionally, and irrevocably assign the entirety of such right, title, and interest to CMDBrew Studio Inc. for no additional consideration beyond the License granted hereunder. In the event of any assignment by you to us under this Section "Contributions", you hereby irrevocably waive, to the extent permitted by applicable law, any and all claims you may now or hereafter have in any jurisdiction to all rights of paternity, integrity, disclosure, and withdrawal and any other rights (which may be known as "moral rights") with respect to the subject matter of such assignment. For further information regarding how to contribute, please visit the the Software Repository.
|
88
|
-
|
89
|
-
## Refunds
|
90
|
-
CMDBrew Studio Inc. offers limited refunds on software within 14 days of purchase. Contact contact@cmdbrew.com for assistance.
|
91
|
-
|
92
|
-
## Indemnity
|
93
|
-
You agree to indemnify and hold harmless CMDBrew Studio Inc. for any third-party claims, actions or suits, as well as any related expenses, liabilities, damages, settlements or fees arising from your use or misuse of the Software, or a violation of any terms of this license.
|
94
|
-
|
95
|
-
## Term and Termination
|
96
|
-
* **Term:** This Agreement takes effect as of the Effective Date and continues in effect until terminated as set forth below in Section "Term and Termination".
|
97
|
-
|
98
|
-
* **Termination:** This Agreement may be terminated:
|
99
|
-
* By us, at anytime, effective on written notice to you, if we, in our sole discretion, believe that you have breached this Agreement;
|
100
|
-
|
101
|
-
* By you, at any time, effective immediately, when you cease to use the Software and permanently delete all copies;
|
102
|
-
|
103
|
-
* Automatically, effective immediately and without notice, if you are a company, corporation, or other legal entity and you (A) are dissolved or liquidated or take any corporate action for such purpose; (B) become insolvent or are generally unable to pay your debts as they become due; (C) become the subject of any voluntary or involuntary bankruptcy proceeding under any domestic or foreign bankruptcy or insolvency Law; (D) make or seek to make a general assignment for the benefit of creditors; or (E) apply for, or consent to, the appointment of a trustee, receiver or custodian for a substantial part of your property.
|
104
|
-
|
105
|
-
## Disclaimer of Warranty
|
106
|
-
THE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESSED OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, WARRANTIES OF QUALITY, PERFORMANCE, NON-INFRINGEMENT, MERCHANTABILITY, OR FITNESS FOR A PARTICULAR PURPOSE. FURTHER, CMDBREW STUDIO INC. DOES NOT WARRANT THAT THE SOFTWARE OR ANY RELATED SERVICE WILL ALWAYS BE AVAILABLE.
|
107
|
-
|
108
|
-
## Limitations of Liability
|
109
|
-
YOU ASSUME ALL RISK ASSOCIATED WITH THE INSTALLATION AND USE OF THE SOFTWARE. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS OF THE SOFTWARE BE LIABLE FOR CLAIMS, DAMAGES OR OTHER LIABILITY ARISING FROM, OUT OF, OR IN CONNECTION WITH THE SOFTWARE. LICENSE HOLDERS ARE SOLELY RESPONSIBLE FOR DETERMINING THE APPROPRIATENESS OF USE AND ASSUME ALL RISKS ASSOCIATED WITH ITS USE, INCLUDING BUT NOT LIMITED TO THE RISKS OF PROGRAM ERRORS, DAMAGE TO EQUIPMENT, LOSS OF DATA OR SOFTWARE PROGRAMS, OR UNAVAILABILITY OR INTERRUPTION OF OPERATIONS.
|
1
|
+
Copyright (c) 2021 I-Lung Lee, Richard Wang, Adminterface.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
4
|
+
of this software and associated documentation files (the "Software"), to deal
|
5
|
+
in the Software without restriction, including without limitation the rights
|
6
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
7
|
+
copies of the Software, and to permit persons to whom the Software is
|
8
|
+
furnished to do so, subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
14
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
15
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
16
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
|
17
|
+
DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
|
18
|
+
OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE
|
19
|
+
OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
CHANGED
@@ -1,3 +1,14 @@
|
|
1
|
+
> ## ⛔️ DEPRECATED: THIS GEM IS NO LONGER BEING MAINTAINED
|
2
|
+
> Due to personal reasons, we can no longer continue to maintain this gem. As a result, we are no longer accepting or merging pull requests.
|
3
|
+
>
|
4
|
+
> To help with the transition, we've changed the license to MIT and removed the license verification for commercial uses. For alternatives, please check out other excellent solutions listed in the [ActiveAdmin Wiki](https://github.com/activeadmin/activeadmin/wiki).
|
5
|
+
>
|
6
|
+
> Thank you to all who contributed and who used Adminterface in their projects. We couldn't have made it this far without your support.
|
7
|
+
>
|
8
|
+
> Until next time.
|
9
|
+
>
|
10
|
+
> — I-Lung, Richard Wang, and the Adminterface team
|
11
|
+
|
1
12
|
# Adminterface <!-- omit in toc -->
|
2
13
|
[![CI](https://github.com/CMDBrew/adminterface/actions/workflows/ci.yml/badge.svg?branch=main)](https://github.com/CMDBrew/adminterface/actions/workflows/ci.yml)
|
3
14
|
[![Maintainability](https://api.codeclimate.com/v1/badges/4dbedfdf21a2d675d2ca/maintainability)](https://codeclimate.com/github/CMDBrew/adminterface/maintainability)
|
@@ -16,7 +27,13 @@ A gem that brings Bootstrap 5, advanced customizability, and other goodies into
|
|
16
27
|
- [Bootstrap](https://getbootstrap.com/) ~> 5.1.3
|
17
28
|
- [Webpacker](https://github.com/rails/webpacker) ~> 5.0
|
18
29
|
|
19
|
-
##
|
30
|
+
## Get Started
|
31
|
+
- [Documentation](https://adminterface.io/docs/intro)
|
32
|
+
- [Demo](https://demo.adminterface.io)
|
33
|
+
- [Issue tracker](https://github.com/CMDBrew/adminterface/issues)
|
34
|
+
- [Discussions and feature requests](https://github.com/CMDBrew/adminterface/discussions)
|
35
|
+
|
36
|
+
## Installation
|
20
37
|
1. Add the following to your application's Gemfile:
|
21
38
|
```ruby
|
22
39
|
gem "adminterface"
|
@@ -43,27 +60,8 @@ A gem that brings Bootstrap 5, advanced customizability, and other goodies into
|
|
43
60
|
$ rails server
|
44
61
|
```
|
45
62
|
|
46
|
-
## Documentation
|
47
|
-
Check our [website](https://adminterface.io) to find in-depth documentation for everything that Adminterface offers.
|
48
|
-
|
49
63
|
## Contributing
|
50
64
|
See [CONTRIBUTING.md](CONTRIBUTING.md).
|
51
65
|
|
52
66
|
## License
|
53
|
-
|
54
|
-
|
55
|
-
### For Commerical License
|
56
|
-
Get a license at https://adminterface.io/pricing, then create and add the `license_key` into the following file in your project:
|
57
|
-
```ruby
|
58
|
-
# config/initializers/adminterface.rb
|
59
|
-
Adminterface.setup do |config|
|
60
|
-
config.license_key = "**********************"
|
61
|
-
|
62
|
-
## Use Rails credentials:
|
63
|
-
## doc: https://edgeguides.rubyonrails.org/security.html#custom-credentials
|
64
|
-
# config.license_key = Rails.application.credentials.adminterface[:license_key]
|
65
|
-
|
66
|
-
## Or, use environment variables:
|
67
|
-
# config.license_key = ENV["ADMINTERFACE_LICENSE_KEY"]
|
68
|
-
end
|
69
|
-
```
|
67
|
+
The gem is available as open source under the terms of the [MIT License](LICENSE.md).
|
@@ -0,0 +1 @@
|
|
1
|
+
ol.breadcrumb { padding-left: 0; }
|
@@ -1,69 +1,69 @@
|
|
1
|
-
import DetachedDropdownClass from '../detached_dropdown'
|
2
|
-
|
3
|
-
describe('DetachedDropdown', () => {
|
4
|
-
const element = document.createElement('div')
|
5
|
-
const options = {
|
6
|
-
title: '.title-menu'
|
7
|
-
}
|
8
|
-
|
9
|
-
beforeEach(() => {
|
10
|
-
global.adminterface = {
|
11
|
-
addObserver: jest.fn()
|
12
|
-
}
|
13
|
-
})
|
14
|
-
|
15
|
-
test('constructor initiates properties and call bind correctly', () => {
|
16
|
-
const spyOnBind = jest.spyOn(DetachedDropdownClass.prototype, '_bind')
|
17
|
-
spyOnBind.mockImplementation(() => {})
|
18
|
-
const DetachedDropdown = new DetachedDropdownClass(element, options)
|
19
|
-
|
20
|
-
expect(DetachedDropdown.element).toEqual(element)
|
21
|
-
expect(DetachedDropdown.options).toEqual({
|
22
|
-
menu: '.dropdown-menu',
|
23
|
-
...options
|
24
|
-
})
|
25
|
-
expect(spyOnBind).toHaveBeenCalledTimes(1)
|
26
|
-
spyOnBind.mockRestore()
|
27
|
-
})
|
28
|
-
|
29
|
-
test('bind method calls adminterface correctly', () => {
|
30
|
-
const DetachedDropdown = new DetachedDropdownClass(element, options)
|
31
|
-
|
32
|
-
expect(global.adminterface.addObserver).toHaveBeenCalledWith(
|
33
|
-
element,
|
34
|
-
DetachedDropdown,
|
35
|
-
DetachedDropdown.constructor.name
|
36
|
-
)
|
37
|
-
})
|
38
|
-
|
39
|
-
test('bind method add event listener correctly', () => {
|
40
|
-
const DetachedDropdown = new DetachedDropdownClass(element, options)
|
41
|
-
const spyOnAddEventListener = jest.spyOn(
|
42
|
-
DetachedDropdown.element,
|
43
|
-
'addEventListener'
|
44
|
-
)
|
45
|
-
DetachedDropdown._bind()
|
46
|
-
|
47
|
-
expect(spyOnAddEventListener).toHaveBeenCalledTimes(2)
|
48
|
-
expect(spyOnAddEventListener.mock.calls).toEqual([
|
49
|
-
['show.bs.dropdown', expect.any(Function)],
|
50
|
-
['hidden.bs.dropdown', expect.any(Function)]
|
51
|
-
])
|
52
|
-
})
|
53
|
-
|
54
|
-
test('append method calls native WEB API append correctly', () => {
|
55
|
-
const testDiv = document.createElement('div')
|
56
|
-
const testP = document.createElement('p')
|
57
|
-
testDiv.append('Some text', testP)
|
58
|
-
|
59
|
-
const DetachedDropdown = new DetachedDropdownClass(element, options)
|
60
|
-
const spyOnAppend = jest.spyOn(document.body, 'append')
|
61
|
-
|
62
|
-
DetachedDropdown._append(document.body, testP)
|
63
|
-
|
64
|
-
expect(spyOnAppend).toHaveBeenCalledTimes(1)
|
65
|
-
expect(spyOnAppend).toHaveBeenCalledWith(
|
66
|
-
testP.parentElement.removeChild(testP)
|
67
|
-
)
|
68
|
-
})
|
69
|
-
})
|
1
|
+
import DetachedDropdownClass from '../detached_dropdown'
|
2
|
+
|
3
|
+
describe('DetachedDropdown', () => {
|
4
|
+
const element = document.createElement('div')
|
5
|
+
const options = {
|
6
|
+
title: '.title-menu'
|
7
|
+
}
|
8
|
+
|
9
|
+
beforeEach(() => {
|
10
|
+
global.adminterface = {
|
11
|
+
addObserver: jest.fn()
|
12
|
+
}
|
13
|
+
})
|
14
|
+
|
15
|
+
test('constructor initiates properties and call bind correctly', () => {
|
16
|
+
const spyOnBind = jest.spyOn(DetachedDropdownClass.prototype, '_bind')
|
17
|
+
spyOnBind.mockImplementation(() => {})
|
18
|
+
const DetachedDropdown = new DetachedDropdownClass(element, options)
|
19
|
+
|
20
|
+
expect(DetachedDropdown.element).toEqual(element)
|
21
|
+
expect(DetachedDropdown.options).toEqual({
|
22
|
+
menu: '.dropdown-menu',
|
23
|
+
...options
|
24
|
+
})
|
25
|
+
expect(spyOnBind).toHaveBeenCalledTimes(1)
|
26
|
+
spyOnBind.mockRestore()
|
27
|
+
})
|
28
|
+
|
29
|
+
test('bind method calls adminterface correctly', () => {
|
30
|
+
const DetachedDropdown = new DetachedDropdownClass(element, options)
|
31
|
+
|
32
|
+
expect(global.adminterface.addObserver).toHaveBeenCalledWith(
|
33
|
+
element,
|
34
|
+
DetachedDropdown,
|
35
|
+
DetachedDropdown.constructor.name
|
36
|
+
)
|
37
|
+
})
|
38
|
+
|
39
|
+
test('bind method add event listener correctly', () => {
|
40
|
+
const DetachedDropdown = new DetachedDropdownClass(element, options)
|
41
|
+
const spyOnAddEventListener = jest.spyOn(
|
42
|
+
DetachedDropdown.element,
|
43
|
+
'addEventListener'
|
44
|
+
)
|
45
|
+
DetachedDropdown._bind()
|
46
|
+
|
47
|
+
expect(spyOnAddEventListener).toHaveBeenCalledTimes(2)
|
48
|
+
expect(spyOnAddEventListener.mock.calls).toEqual([
|
49
|
+
['show.bs.dropdown', expect.any(Function)],
|
50
|
+
['hidden.bs.dropdown', expect.any(Function)]
|
51
|
+
])
|
52
|
+
})
|
53
|
+
|
54
|
+
test('append method calls native WEB API append correctly', () => {
|
55
|
+
const testDiv = document.createElement('div')
|
56
|
+
const testP = document.createElement('p')
|
57
|
+
testDiv.append('Some text', testP)
|
58
|
+
|
59
|
+
const DetachedDropdown = new DetachedDropdownClass(element, options)
|
60
|
+
const spyOnAppend = jest.spyOn(document.body, 'append')
|
61
|
+
|
62
|
+
DetachedDropdown._append(document.body, testP)
|
63
|
+
|
64
|
+
expect(spyOnAppend).toHaveBeenCalledTimes(1)
|
65
|
+
expect(spyOnAppend).toHaveBeenCalledWith(
|
66
|
+
testP.parentElement.removeChild(testP)
|
67
|
+
)
|
68
|
+
})
|
69
|
+
})
|
@@ -1,14 +1,14 @@
|
|
1
|
-
<div data-sortable="address">
|
2
|
-
<form class="has-many-list">
|
3
|
-
<fieldset class="has_many_fields">
|
4
|
-
<input type="checkbox" name="[_destroy]" checked>
|
5
|
-
<input type="text" name="[address]" value="Tokyo">
|
6
|
-
<a class="button has_many_remove"></a>
|
7
|
-
<a
|
8
|
-
class="button has_many_add"
|
9
|
-
data-html="<div></div>"
|
10
|
-
data-placeholder="script"
|
11
|
-
></a>
|
12
|
-
</fieldset>
|
13
|
-
</form>
|
1
|
+
<div data-sortable="address">
|
2
|
+
<form class="has-many-list">
|
3
|
+
<fieldset class="has_many_fields">
|
4
|
+
<input type="checkbox" name="[_destroy]" checked>
|
5
|
+
<input type="text" name="[address]" value="Tokyo">
|
6
|
+
<a class="button has_many_remove"></a>
|
7
|
+
<a
|
8
|
+
class="button has_many_add"
|
9
|
+
data-html="<div></div>"
|
10
|
+
data-placeholder="script"
|
11
|
+
></a>
|
12
|
+
</fieldset>
|
13
|
+
</form>
|
14
14
|
</div>
|
@@ -0,0 +1,212 @@
|
|
1
|
+
import fs from 'fs'
|
2
|
+
import path from 'path'
|
3
|
+
import HeaderTogglerClass from '../header_toggler'
|
4
|
+
import * as utilModule from '../utils'
|
5
|
+
|
6
|
+
describe('CheckboxToggler', () => {
|
7
|
+
const html = fs.readFileSync(path.resolve(__dirname, './header_toggler.html'))
|
8
|
+
const options = { item: 1 }
|
9
|
+
const defaults = {
|
10
|
+
container: 'body',
|
11
|
+
activeClass: 'header-active',
|
12
|
+
cookieName: 'header-state',
|
13
|
+
cookieExpireSec: 30 * 24 * 60 * 60
|
14
|
+
}
|
15
|
+
const mockEvent = {
|
16
|
+
preventDefault: jest.fn()
|
17
|
+
}
|
18
|
+
|
19
|
+
let element
|
20
|
+
let $container
|
21
|
+
let windowSpy
|
22
|
+
let spyOnAdd
|
23
|
+
let spyOnRemove
|
24
|
+
let spyOnCookieGet
|
25
|
+
let spyOnCookieSet
|
26
|
+
|
27
|
+
beforeEach(() => {
|
28
|
+
document.body.innerHTML = html
|
29
|
+
document.body.classList.remove(defaults.activeClass)
|
30
|
+
element = document.body.firstChild
|
31
|
+
$container = document.querySelector(defaults.container)
|
32
|
+
|
33
|
+
windowSpy = jest.spyOn(window, 'window', 'get')
|
34
|
+
spyOnCookieGet = jest.spyOn(utilModule, 'cookieGet')
|
35
|
+
spyOnCookieSet = jest.spyOn(utilModule, 'cookieSet')
|
36
|
+
spyOnCookieSet.mockImplementation(() => {})
|
37
|
+
spyOnAdd = jest.spyOn(HeaderTogglerClass.prototype, '_add')
|
38
|
+
spyOnRemove = jest.spyOn(HeaderTogglerClass.prototype, '_remove')
|
39
|
+
|
40
|
+
global.adminterface = {
|
41
|
+
addObserver: jest.fn()
|
42
|
+
}
|
43
|
+
})
|
44
|
+
|
45
|
+
afterEach(() => {
|
46
|
+
jest.restoreAllMocks()
|
47
|
+
})
|
48
|
+
|
49
|
+
test('constructor initiates correctly', () => {
|
50
|
+
const spyOnBind = jest.spyOn(HeaderTogglerClass.prototype, '_bind')
|
51
|
+
spyOnBind.mockImplementation(() => {})
|
52
|
+
|
53
|
+
const HeaderToggler = new HeaderTogglerClass(element, options)
|
54
|
+
|
55
|
+
expect(HeaderToggler.element).toStrictEqual(element)
|
56
|
+
expect(HeaderToggler.options).toStrictEqual({ ...defaults, ...options })
|
57
|
+
|
58
|
+
expect(spyOnBind).toHaveBeenCalledTimes(1)
|
59
|
+
})
|
60
|
+
|
61
|
+
test('bind calls cookieGet once with correct argument', () => {
|
62
|
+
const HeaderToggler = new HeaderTogglerClass(element, options)
|
63
|
+
|
64
|
+
expect(spyOnCookieGet).toHaveBeenCalledTimes(1)
|
65
|
+
expect(spyOnCookieGet).toHaveBeenCalledWith(HeaderToggler.options.cookieName)
|
66
|
+
})
|
67
|
+
|
68
|
+
test('bind calls add when cookie is activeClass and breakpointHelper is visible', () => {
|
69
|
+
windowSpy.mockImplementation(() => ({
|
70
|
+
getComputedStyle: () => ({
|
71
|
+
visibility: 'visible'
|
72
|
+
})
|
73
|
+
}))
|
74
|
+
spyOnCookieGet.mockImplementation(() => 'header-active')
|
75
|
+
|
76
|
+
new HeaderTogglerClass(element, options) // eslint-disable-line no-new
|
77
|
+
|
78
|
+
expect(spyOnAdd).toHaveBeenCalledTimes(1)
|
79
|
+
})
|
80
|
+
|
81
|
+
test('bind does not call add when cookie is not activeClass and breakpoint is visible', () => {
|
82
|
+
windowSpy.mockImplementation(() => ({
|
83
|
+
getComputedStyle: () => ({
|
84
|
+
visibility: 'visible'
|
85
|
+
})
|
86
|
+
}))
|
87
|
+
spyOnCookieGet.mockImplementation(() => 'header-not-active')
|
88
|
+
|
89
|
+
new HeaderTogglerClass(element, options) // eslint-disable-line no-new
|
90
|
+
|
91
|
+
expect(spyOnAdd).not.toHaveBeenCalled()
|
92
|
+
})
|
93
|
+
|
94
|
+
test('bind does not call add when cookie is activeClass and breakpointHelper is not visible', () => {
|
95
|
+
windowSpy.mockImplementation(() => ({
|
96
|
+
getComputedStyle: () => ({
|
97
|
+
visibility: 'not-visible'
|
98
|
+
})
|
99
|
+
}))
|
100
|
+
spyOnCookieGet.mockImplementation(() => 'header-active')
|
101
|
+
|
102
|
+
new HeaderTogglerClass(element, options) // eslint-disable-line no-new
|
103
|
+
|
104
|
+
expect(spyOnAdd).not.toHaveBeenCalled()
|
105
|
+
})
|
106
|
+
|
107
|
+
test('bind calls addEventListener on element once with click', () => {
|
108
|
+
const HeaderToggler = new HeaderTogglerClass(element, options)
|
109
|
+
const spyOnElementAddEventListener = jest.spyOn(HeaderToggler.element, 'addEventListener')
|
110
|
+
|
111
|
+
HeaderToggler._bind()
|
112
|
+
|
113
|
+
expect(spyOnElementAddEventListener).toHaveBeenCalledTimes(1)
|
114
|
+
expect(spyOnElementAddEventListener).toHaveBeenCalledWith(
|
115
|
+
'click',
|
116
|
+
expect.any(Function)
|
117
|
+
)
|
118
|
+
})
|
119
|
+
|
120
|
+
test('bind calls adminterface addObserver once with correct argument', () => {
|
121
|
+
const spyOnAddObserver = jest.spyOn(global.adminterface, 'addObserver')
|
122
|
+
|
123
|
+
const HeaderToggler = new HeaderTogglerClass(element, options)
|
124
|
+
|
125
|
+
expect(spyOnAddObserver).toHaveBeenCalledTimes(1)
|
126
|
+
expect(spyOnAddObserver).toHaveBeenCalledWith(
|
127
|
+
HeaderToggler.element,
|
128
|
+
HeaderToggler,
|
129
|
+
HeaderToggler.constructor.name
|
130
|
+
)
|
131
|
+
})
|
132
|
+
|
133
|
+
test('clickCallback calls preventDefault', () => {
|
134
|
+
spyOnAdd.mockImplementation(() => {})
|
135
|
+
spyOnRemove.mockImplementation(() => {})
|
136
|
+
|
137
|
+
const HeaderToggler = new HeaderTogglerClass(element, options)
|
138
|
+
|
139
|
+
HeaderToggler._clickCallback(mockEvent, $container, HeaderToggler)
|
140
|
+
|
141
|
+
expect(mockEvent.preventDefault).toHaveBeenCalledTimes(1)
|
142
|
+
})
|
143
|
+
|
144
|
+
test('clickCallback calls remove when $container class contains activeClass', () => {
|
145
|
+
spyOnAdd.mockImplementation(() => {})
|
146
|
+
spyOnRemove.mockImplementation(() => {})
|
147
|
+
|
148
|
+
const HeaderToggler = new HeaderTogglerClass(element, options)
|
149
|
+
|
150
|
+
document.body.classList.add(HeaderToggler.options.activeClass)
|
151
|
+
|
152
|
+
spyOnAdd.mockClear()
|
153
|
+
HeaderToggler._clickCallback(mockEvent, $container, HeaderToggler)
|
154
|
+
|
155
|
+
expect(spyOnRemove).toHaveBeenCalledTimes(1)
|
156
|
+
expect(spyOnAdd).not.toHaveBeenCalled()
|
157
|
+
})
|
158
|
+
|
159
|
+
test('clickCallback calls add when $container class does not contain activeClass', () => {
|
160
|
+
spyOnAdd.mockImplementation(() => {})
|
161
|
+
spyOnRemove.mockImplementation(() => {})
|
162
|
+
|
163
|
+
const HeaderToggler = new HeaderTogglerClass(element, options)
|
164
|
+
|
165
|
+
spyOnAdd.mockClear()
|
166
|
+
HeaderToggler._clickCallback(mockEvent, $container, HeaderToggler)
|
167
|
+
|
168
|
+
expect(spyOnRemove).not.toHaveBeenCalled()
|
169
|
+
expect(spyOnAdd).toHaveBeenCalledTimes(1)
|
170
|
+
})
|
171
|
+
|
172
|
+
test('add calls $container.classList.add and cookieSet', () => {
|
173
|
+
const spyOnContainerClassListAdd = jest.spyOn($container.classList, 'add')
|
174
|
+
|
175
|
+
const HeaderToggler = new HeaderTogglerClass(element, options)
|
176
|
+
spyOnContainerClassListAdd.mockClear()
|
177
|
+
spyOnCookieSet.mockClear()
|
178
|
+
HeaderToggler._add()
|
179
|
+
|
180
|
+
expect(spyOnContainerClassListAdd).toHaveBeenCalledTimes(1)
|
181
|
+
expect(spyOnContainerClassListAdd).toHaveBeenCalledWith(
|
182
|
+
HeaderToggler.options.activeClass
|
183
|
+
)
|
184
|
+
|
185
|
+
expect(spyOnCookieSet).toHaveBeenCalledTimes(1)
|
186
|
+
expect(spyOnCookieSet).toHaveBeenCalledWith(
|
187
|
+
HeaderToggler.options.cookieName,
|
188
|
+
HeaderToggler.options.activeClass,
|
189
|
+
HeaderToggler.options.cookieExpireSec
|
190
|
+
)
|
191
|
+
})
|
192
|
+
|
193
|
+
test('remove calls $container.classList.remove and cookieSet', () => {
|
194
|
+
const spyOnContainerClassListRemove = jest.spyOn($container.classList, 'remove')
|
195
|
+
|
196
|
+
const HeaderToggler = new HeaderTogglerClass(element, options)
|
197
|
+
spyOnCookieSet.mockClear()
|
198
|
+
HeaderToggler._remove()
|
199
|
+
|
200
|
+
expect(spyOnContainerClassListRemove).toHaveBeenCalledTimes(1)
|
201
|
+
expect(spyOnContainerClassListRemove).toHaveBeenCalledWith(
|
202
|
+
HeaderToggler.options.activeClass
|
203
|
+
)
|
204
|
+
|
205
|
+
expect(spyOnCookieSet).toHaveBeenCalledTimes(1)
|
206
|
+
expect(spyOnCookieSet).toHaveBeenCalledWith(
|
207
|
+
HeaderToggler.options.cookieName,
|
208
|
+
null,
|
209
|
+
0
|
210
|
+
)
|
211
|
+
})
|
212
|
+
})
|
@@ -0,0 +1,75 @@
|
|
1
|
+
import PerPage from '../per_page'
|
2
|
+
import * as utilModule from '../utils'
|
3
|
+
|
4
|
+
describe('CheckboxToggler', () => {
|
5
|
+
const mockEvent = {
|
6
|
+
preventDefault: jest.fn()
|
7
|
+
}
|
8
|
+
const mockQueryString = '?email=someone@example.com'
|
9
|
+
const value = 1
|
10
|
+
|
11
|
+
let spyOnQueryStringToParams
|
12
|
+
let spyOnTurbolinks
|
13
|
+
let spyOnTurbolinksVisit
|
14
|
+
let spyOnToQueryString
|
15
|
+
let params
|
16
|
+
|
17
|
+
beforeEach(() => {
|
18
|
+
Object.defineProperty(window, 'location', {
|
19
|
+
value: {
|
20
|
+
search: mockQueryString
|
21
|
+
},
|
22
|
+
writable: true
|
23
|
+
})
|
24
|
+
|
25
|
+
params = utilModule.queryStringToParams().filter(({ name }) => name !== 'per_page' && name !== 'page')
|
26
|
+
params.push({ name: 'per_page', value })
|
27
|
+
|
28
|
+
spyOnQueryStringToParams = jest.spyOn(utilModule, 'queryStringToParams')
|
29
|
+
spyOnTurbolinks = jest.spyOn(utilModule, 'hasTurbolinks')
|
30
|
+
spyOnTurbolinksVisit = jest.spyOn(utilModule, 'turbolinksVisit')
|
31
|
+
spyOnToQueryString = jest.spyOn(utilModule, 'toQueryString')
|
32
|
+
|
33
|
+
PerPage.value = value
|
34
|
+
})
|
35
|
+
|
36
|
+
afterEach(() => {
|
37
|
+
jest.restoreAllMocks()
|
38
|
+
mockEvent.preventDefault.mockClear()
|
39
|
+
})
|
40
|
+
|
41
|
+
test('update triggers queryStringToParams', () => {
|
42
|
+
PerPage._update(mockEvent)
|
43
|
+
|
44
|
+
expect(spyOnQueryStringToParams).toHaveBeenCalledTimes(1)
|
45
|
+
})
|
46
|
+
|
47
|
+
test('hasTurbolinks: true, update triggers preventDefault and turbolinksVisit', () => {
|
48
|
+
spyOnTurbolinks.mockImplementation(() => true)
|
49
|
+
spyOnTurbolinksVisit.mockImplementation(() => {})
|
50
|
+
|
51
|
+
PerPage._update(mockEvent)
|
52
|
+
|
53
|
+
expect(mockEvent.preventDefault).toHaveBeenCalledTimes(1)
|
54
|
+
|
55
|
+
expect(spyOnTurbolinksVisit).toHaveBeenCalledTimes(1)
|
56
|
+
expect(spyOnTurbolinksVisit).toHaveBeenCalledWith(params)
|
57
|
+
|
58
|
+
expect(spyOnToQueryString).not.toHaveBeenCalled()
|
59
|
+
})
|
60
|
+
|
61
|
+
test('hasTurbolinks: false, sets window.location.search to toQueryString output', () => {
|
62
|
+
spyOnTurbolinks.mockImplementation(() => false)
|
63
|
+
spyOnToQueryString.mockImplementation(params => params)
|
64
|
+
|
65
|
+
PerPage._update(mockEvent)
|
66
|
+
|
67
|
+
expect(mockEvent.preventDefault).not.toHaveBeenCalled()
|
68
|
+
expect(spyOnTurbolinksVisit).not.toHaveBeenCalled()
|
69
|
+
|
70
|
+
expect(spyOnToQueryString).toHaveBeenCalledTimes(1)
|
71
|
+
expect(spyOnToQueryString).toHaveBeenCalledWith(params)
|
72
|
+
|
73
|
+
expect(window.location.search).toStrictEqual(params)
|
74
|
+
})
|
75
|
+
})
|
@@ -38,17 +38,19 @@ class HeaderToggler {
|
|
38
38
|
this._add()
|
39
39
|
}
|
40
40
|
|
41
|
-
this.element.addEventListener('click',
|
42
|
-
e.preventDefault()
|
43
|
-
if ($container.classList.contains(_self.options.activeClass)) {
|
44
|
-
_self._remove()
|
45
|
-
} else {
|
46
|
-
_self._add()
|
47
|
-
}
|
48
|
-
})
|
41
|
+
this.element.addEventListener('click', (e) => this._clickCallback(e, $container, _self))
|
49
42
|
|
50
43
|
adminterface.addObserver(this.element, this, this.constructor.name)
|
51
44
|
}
|
45
|
+
|
46
|
+
_clickCallback (e, $container, _self) {
|
47
|
+
e.preventDefault()
|
48
|
+
if ($container.classList.contains(_self.options.activeClass)) {
|
49
|
+
_self._remove()
|
50
|
+
} else {
|
51
|
+
_self._add()
|
52
|
+
}
|
53
|
+
}
|
52
54
|
}
|
53
55
|
|
54
56
|
export default HeaderToggler
|
@@ -1,5 +1,5 @@
|
|
1
1
|
<!DOCTYPE html>
|
2
|
-
<html lang="<%=I18n.locale%>">
|
2
|
+
<html lang="<%=I18n.locale%>" dir="<%= ActiveAdmin.application.lang_dir(self) %>">
|
3
3
|
<head>
|
4
4
|
<meta http-equiv="Content-type" content="text/html; charset=utf-8">
|
5
5
|
<title><%= [@page_title, ActiveAdmin.application.site_title(self)].compact.join(" | ") %></title>
|
data/lib/adminterface/configs.rb
CHANGED
@@ -10,7 +10,11 @@ module Adminterface
|
|
10
10
|
}.call.freeze
|
11
11
|
|
12
12
|
module Configurable
|
13
|
-
attr_writer :components, :css_classes, :comments_pager, :comments_per_page, :pager, :comments_input
|
13
|
+
attr_writer :lang_dir, :components, :css_classes, :comments_pager, :comments_per_page, :pager, :comments_input
|
14
|
+
|
15
|
+
def lang_dir(...)
|
16
|
+
@lang_dir || namespace.lang_dir(...)
|
17
|
+
end
|
14
18
|
|
15
19
|
def components
|
16
20
|
namespace.components.deep_merge(@components || {})
|
data/lib/adminterface/engine.rb
CHANGED
@@ -11,12 +11,5 @@ module Adminterface
|
|
11
11
|
include ::Adminterface::Initializers::ViewHelpers
|
12
12
|
include ::Adminterface::Initializers::Views
|
13
13
|
include ::Adminterface::Initializers::Comments
|
14
|
-
|
15
|
-
config.after_initialize do
|
16
|
-
unless defined?(::Rails::Generators) || defined?(::Rails::Console) ||
|
17
|
-
File.basename($0).eql?("rake")
|
18
|
-
::Adminterface::License.verify!
|
19
|
-
end
|
20
|
-
end
|
21
14
|
end
|
22
15
|
end
|
data/lib/adminterface/version.rb
CHANGED
data/lib/adminterface.rb
CHANGED
@@ -7,7 +7,6 @@ module Adminterface
|
|
7
7
|
autoload :VERSION, "adminterface/version"
|
8
8
|
autoload :Configs, "adminterface/configs"
|
9
9
|
autoload :Data, "adminterface/data"
|
10
|
-
autoload :License, "adminterface/license"
|
11
10
|
autoload :Callable, "adminterface/callable"
|
12
11
|
|
13
12
|
# Load Initializers
|
@@ -20,36 +19,6 @@ module Adminterface
|
|
20
19
|
autoload :ViewHelpers, "adminterface/initializers/view_helpers"
|
21
20
|
autoload :Views, "adminterface/initializers/views"
|
22
21
|
end
|
23
|
-
|
24
|
-
module Licensing
|
25
|
-
autoload :Notice, "adminterface/licensing/notice"
|
26
|
-
autoload :Commercial, "adminterface/licensing/commercial"
|
27
|
-
autoload :Personal, "adminterface/licensing/personal"
|
28
|
-
end
|
29
|
-
|
30
|
-
module Encryption
|
31
|
-
autoload :Encryptor, "adminterface/encryption/encryptor"
|
32
|
-
end
|
33
|
-
|
34
|
-
# License key
|
35
|
-
mattr_accessor :license_key
|
36
|
-
@@license_key = nil
|
37
|
-
|
38
|
-
def self.setup
|
39
|
-
yield self
|
40
|
-
end
|
41
|
-
|
42
|
-
def self.rails6_and_up?
|
43
|
-
Rails::VERSION::MAJOR >= 6
|
44
|
-
end
|
45
|
-
|
46
|
-
def self.cache_store
|
47
|
-
if Rails.cache.instance_of?(ActiveSupport::Cache::NullStore)
|
48
|
-
ActiveSupport::Cache::MemoryStore.new
|
49
|
-
else
|
50
|
-
Rails.cache
|
51
|
-
end
|
52
|
-
end
|
53
22
|
end
|
54
23
|
|
55
24
|
require "adminterface/engine"
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: adminterface
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.3.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- I-Lung Lee
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2021-11-
|
11
|
+
date: 2021-11-26 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activeadmin
|
@@ -144,6 +144,7 @@ files:
|
|
144
144
|
- app/assets/stylesheets/adminterface/vendors/_all.scss
|
145
145
|
- app/assets/stylesheets/adminterface/vendors/bootstrap/_all.scss
|
146
146
|
- app/assets/stylesheets/adminterface/vendors/bootstrap/_variables.scss
|
147
|
+
- app/assets/stylesheets/adminterface/vendors/bootstrap/components/_breadcrumb.scss
|
147
148
|
- app/assets/stylesheets/adminterface/vendors/bootstrap/components/_buttons.scss
|
148
149
|
- app/assets/stylesheets/adminterface/vendors/bootstrap/components/_dropdowns.scss
|
149
150
|
- app/assets/stylesheets/adminterface/vendors/bootstrap/components/_navs.scss
|
@@ -151,8 +152,8 @@ files:
|
|
151
152
|
- app/assets/stylesheets/adminterface/vendors/bootstrap/content/_tables.scss
|
152
153
|
- app/assets/stylesheets/adminterface/vendors/bootstrap/content/_typography.scss
|
153
154
|
- app/assets/stylesheets/adminterface/vendors/flatpickr/_all.scss
|
155
|
+
- app/assets/stylesheets/adminterface/vendors/flatpickr/_main.scss
|
154
156
|
- app/assets/stylesheets/adminterface/vendors/flatpickr/_variables.scss
|
155
|
-
- app/assets/stylesheets/adminterface/vendors/flatpickr/main.scss
|
156
157
|
- app/assets/stylesheets/adminterface/vendors/tom_select/_all.scss
|
157
158
|
- app/assets/stylesheets/adminterface/vendors/tom_select/_main.scss
|
158
159
|
- app/assets/stylesheets/adminterface/vendors/trix/_actiontext.scss
|
@@ -181,6 +182,9 @@ files:
|
|
181
182
|
- app/javascript/adminterface/lib/__tests__/filters.spec.js
|
182
183
|
- app/javascript/adminterface/lib/__tests__/has_many.html
|
183
184
|
- app/javascript/adminterface/lib/__tests__/has_many.spec.js
|
185
|
+
- app/javascript/adminterface/lib/__tests__/header_toggler.html
|
186
|
+
- app/javascript/adminterface/lib/__tests__/header_toggler.spec.js
|
187
|
+
- app/javascript/adminterface/lib/__tests__/per_page.spec.js
|
184
188
|
- app/javascript/adminterface/lib/__tests__/utils.spec.js
|
185
189
|
- app/javascript/adminterface/lib/batch_actions.js
|
186
190
|
- app/javascript/adminterface/lib/checkbox_toggler.js
|
@@ -239,7 +243,6 @@ files:
|
|
239
243
|
- lib/adminterface/data/base.rb
|
240
244
|
- lib/adminterface/data/countries.rb
|
241
245
|
- lib/adminterface/data/time_zones.rb
|
242
|
-
- lib/adminterface/encryption/encryptor.rb
|
243
246
|
- lib/adminterface/engine.rb
|
244
247
|
- lib/adminterface/extensions/base_controller.rb
|
245
248
|
- lib/adminterface/extensions/batch_actions/resource_extension.rb
|
@@ -334,12 +337,6 @@ files:
|
|
334
337
|
- lib/adminterface/initializers/resource.rb
|
335
338
|
- lib/adminterface/initializers/view_helpers.rb
|
336
339
|
- lib/adminterface/initializers/views.rb
|
337
|
-
- lib/adminterface/license.rb
|
338
|
-
- lib/adminterface/licensing/base.rb
|
339
|
-
- lib/adminterface/licensing/commercial.rb
|
340
|
-
- lib/adminterface/licensing/notice.rb
|
341
|
-
- lib/adminterface/licensing/personal.rb
|
342
|
-
- lib/adminterface/public.pem
|
343
340
|
- lib/adminterface/version.rb
|
344
341
|
- lib/generators/adminterface/comments/comments_generator.rb
|
345
342
|
- lib/generators/adminterface/comments/templates/README
|
@@ -358,7 +355,7 @@ files:
|
|
358
355
|
- lib/tasks/adminterface_tasks.rake
|
359
356
|
homepage: https://adminterface.io
|
360
357
|
licenses:
|
361
|
-
-
|
358
|
+
- MIT
|
362
359
|
metadata:
|
363
360
|
homepage_uri: https://adminterface.io
|
364
361
|
source_code_uri: https://github.com/cmdbrew/adminterface
|
@@ -379,7 +376,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
379
376
|
- !ruby/object:Gem::Version
|
380
377
|
version: '0'
|
381
378
|
requirements: []
|
382
|
-
rubygems_version: 3.2.
|
379
|
+
rubygems_version: 3.2.32
|
383
380
|
signing_key:
|
384
381
|
specification_version: 4
|
385
382
|
summary: Build functional and beautiful web apps faster with ActiveAdmin
|
@@ -1,26 +0,0 @@
|
|
1
|
-
module Adminterface
|
2
|
-
module Encryption
|
3
|
-
class Encryptor
|
4
|
-
extend Callable
|
5
|
-
attr_reader :message
|
6
|
-
|
7
|
-
def initialize(message)
|
8
|
-
@message = message
|
9
|
-
end
|
10
|
-
|
11
|
-
def call
|
12
|
-
Base64.encode64(public_key.public_encrypt(message))
|
13
|
-
end
|
14
|
-
|
15
|
-
private
|
16
|
-
|
17
|
-
def public_key_file
|
18
|
-
@public_key_file ||= File.read(Engine.root.join("lib/adminterface/public.pem"))
|
19
|
-
end
|
20
|
-
|
21
|
-
def public_key
|
22
|
-
@public_key ||= OpenSSL::PKey::RSA.new(public_key_file)
|
23
|
-
end
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
data/lib/adminterface/license.rb
DELETED
@@ -1,18 +0,0 @@
|
|
1
|
-
module Adminterface
|
2
|
-
class License
|
3
|
-
class_attribute :license, default: {}
|
4
|
-
|
5
|
-
class << self
|
6
|
-
def verify!
|
7
|
-
return if Rails.env.test?
|
8
|
-
|
9
|
-
self.license =
|
10
|
-
if ::Adminterface.license_key.present?
|
11
|
-
::Adminterface::Licensing::Commercial
|
12
|
-
else
|
13
|
-
::Adminterface::Licensing::Personal
|
14
|
-
end.call
|
15
|
-
end
|
16
|
-
end
|
17
|
-
end
|
18
|
-
end
|
@@ -1,99 +0,0 @@
|
|
1
|
-
module Adminterface
|
2
|
-
module Licensing
|
3
|
-
class Base
|
4
|
-
extend Adminterface::Callable
|
5
|
-
|
6
|
-
ENDPOINT = "https://app.adminterface.io/api/v1/licenses/verify".freeze
|
7
|
-
CACHE_KEY = "adminterface.licensing.response".freeze
|
8
|
-
TIMEOUT = 5
|
9
|
-
|
10
|
-
attr_reader :license_key
|
11
|
-
delegate :cache_store, :rails6_and_up?, to: :Adminterface
|
12
|
-
|
13
|
-
def initialize(license_key = Adminterface.license_key)
|
14
|
-
@license_key = license_key
|
15
|
-
end
|
16
|
-
|
17
|
-
def call
|
18
|
-
response = send_and_cache_request.with_indifferent_access
|
19
|
-
Adminterface::Licensing::Notice
|
20
|
-
.call(response, license_key, **{external: request?, endpoint: ENDPOINT})
|
21
|
-
response
|
22
|
-
end
|
23
|
-
|
24
|
-
def payload
|
25
|
-
{
|
26
|
-
license_key: encrypted_license_key,
|
27
|
-
license: license,
|
28
|
-
adminterface_version: Adminterface::VERSION,
|
29
|
-
rails_version: Rails::VERSION::STRING,
|
30
|
-
ruby_version: RUBY_VERSION,
|
31
|
-
environment: Rails.env,
|
32
|
-
app_name: app_name,
|
33
|
-
verified: false
|
34
|
-
}
|
35
|
-
end
|
36
|
-
|
37
|
-
private
|
38
|
-
|
39
|
-
def encrypted_license_key
|
40
|
-
@encrypted_license_key ||=
|
41
|
-
license_key && Adminterface::Encryption::Encryptor.call(license_key)
|
42
|
-
end
|
43
|
-
|
44
|
-
def send_and_cache_request
|
45
|
-
return cached_response if has_cached_response?
|
46
|
-
return local_response unless request?
|
47
|
-
|
48
|
-
response = HTTParty.post(
|
49
|
-
ENDPOINT,
|
50
|
-
body: payload.to_json,
|
51
|
-
headers: {'Content-type': "application/json"},
|
52
|
-
timeout: TIMEOUT
|
53
|
-
)
|
54
|
-
|
55
|
-
case response.code
|
56
|
-
when 200 then cache_response response.code, response.parsed_response
|
57
|
-
else return_error response.code, response.body
|
58
|
-
end
|
59
|
-
rescue => e
|
60
|
-
return_error e.class, e.message
|
61
|
-
end
|
62
|
-
|
63
|
-
def local_response
|
64
|
-
payload.merge(verified: true)
|
65
|
-
end
|
66
|
-
|
67
|
-
def request?
|
68
|
-
true
|
69
|
-
end
|
70
|
-
|
71
|
-
def return_error(status, exception_message = "")
|
72
|
-
payload.merge(status: status, message: exception_message).stringify_keys!
|
73
|
-
end
|
74
|
-
|
75
|
-
def cache_response(status, response, time: 1.hour.to_i)
|
76
|
-
response = payload.merge!(status: status, expiry: time, **response).stringify_keys!
|
77
|
-
cache_store.write(CACHE_KEY, response, expires_in: time)
|
78
|
-
response
|
79
|
-
end
|
80
|
-
|
81
|
-
def license
|
82
|
-
self.class.name.split("::").last.underscore
|
83
|
-
end
|
84
|
-
|
85
|
-
def app_name
|
86
|
-
klass = Rails.application.class
|
87
|
-
rails6_and_up? ? klass.module_parent.name : klass.parent.name
|
88
|
-
end
|
89
|
-
|
90
|
-
def has_cached_response?
|
91
|
-
cache_store.exist? CACHE_KEY
|
92
|
-
end
|
93
|
-
|
94
|
-
def cached_response
|
95
|
-
cache_store.read CACHE_KEY
|
96
|
-
end
|
97
|
-
end
|
98
|
-
end
|
99
|
-
end
|
@@ -1,54 +0,0 @@
|
|
1
|
-
module Adminterface
|
2
|
-
module Licensing
|
3
|
-
class Notice
|
4
|
-
extend Adminterface::Callable
|
5
|
-
attr_reader :response, :license_key, :external, :endpoint
|
6
|
-
|
7
|
-
def initialize(response, license_key, external: true, endpoint: nil)
|
8
|
-
@response = response
|
9
|
-
@license_key = license_key
|
10
|
-
@external = external
|
11
|
-
@endpoint = endpoint
|
12
|
-
end
|
13
|
-
|
14
|
-
def call
|
15
|
-
return unless Rails.env.development?
|
16
|
-
|
17
|
-
log("========================================")
|
18
|
-
log(messages)
|
19
|
-
log("========================================")
|
20
|
-
end
|
21
|
-
|
22
|
-
private
|
23
|
-
|
24
|
-
def log(*msgs)
|
25
|
-
msgs.each { |x| puts(x) }
|
26
|
-
end
|
27
|
-
|
28
|
-
def external?
|
29
|
-
!!external
|
30
|
-
end
|
31
|
-
|
32
|
-
def title
|
33
|
-
"Adminterface #{response[:adminterface_version]} (\"#{response[:license]}\")"
|
34
|
-
end
|
35
|
-
|
36
|
-
def messages
|
37
|
-
return title unless external?
|
38
|
-
|
39
|
-
[title, "=> Verifying license on #{endpoint}", verified].flatten
|
40
|
-
end
|
41
|
-
|
42
|
-
def verified
|
43
|
-
if !!response[:verified]
|
44
|
-
Rainbow("=> [#{response[:status]}] #{response[:message]}").green
|
45
|
-
else
|
46
|
-
[
|
47
|
-
Rainbow("=> [#{response[:status]}] #{response[:message]}").red,
|
48
|
-
Rainbow("=> license_key: #{license_key}").red
|
49
|
-
]
|
50
|
-
end
|
51
|
-
end
|
52
|
-
end
|
53
|
-
end
|
54
|
-
end
|
data/lib/adminterface/public.pem
DELETED
@@ -1,9 +0,0 @@
|
|
1
|
-
-----BEGIN PUBLIC KEY-----
|
2
|
-
MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAuH2AdyyQycs7DhFQo1f/
|
3
|
-
9ssIqc5ek7OYZHlfwSYSFuWaTfq46HK66QZ1ebkUva/U33QzNyaxcMxYpTpRY2dF
|
4
|
-
IPLtS1QNktC4U0T3Nz0sY3tZp/IwnWcE9b8MpS2FDFtMRiHu+/dtthWoG6kP6w5y
|
5
|
-
P8pWDLwQuYPbPnqlLbp6yWaJB1G1qHoLmgKD1VPUxjk0gB0tGPPvaiciESObbGC3
|
6
|
-
IgzeUKtz/+dhR4UBOZc1pmrftwacUfvNHrSuot0jaYCNrb98Oq8kkBpp79MgRghK
|
7
|
-
SkPdTRidSlIokdxlEOrcRhoJsVka5w9fYLShJdv1yhmGh3VZdKkvSqM7MhwE+zML
|
8
|
-
0QIDAQAB
|
9
|
-
-----END PUBLIC KEY-----
|