adminterface 0.2.3 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (29) hide show
  1. checksums.yaml +4 -4
  2. data/LICENSE.md +19 -109
  3. data/README.md +19 -21
  4. data/app/assets/stylesheets/adminterface/components/_form.scss +1 -0
  5. data/app/assets/stylesheets/adminterface/mixins/_utilities.scss +4 -0
  6. data/app/assets/stylesheets/adminterface/vendors/bootstrap/_all.scss +1 -0
  7. data/app/assets/stylesheets/adminterface/vendors/bootstrap/components/_breadcrumb.scss +1 -0
  8. data/app/assets/stylesheets/adminterface/vendors/flatpickr/{main.scss → _main.scss} +6 -0
  9. data/app/javascript/adminterface/lib/__tests__/detached_dropdown.spec.js +69 -69
  10. data/app/javascript/adminterface/lib/__tests__/has_many.html +13 -13
  11. data/app/javascript/adminterface/lib/__tests__/header_toggler.html +2 -0
  12. data/app/javascript/adminterface/lib/__tests__/header_toggler.spec.js +212 -0
  13. data/app/javascript/adminterface/lib/__tests__/per_page.spec.js +75 -0
  14. data/app/javascript/adminterface/lib/header_toggler.js +10 -8
  15. data/app/views/layouts/active_admin_logged_out.html.erb +1 -1
  16. data/lib/adminterface/configs.rb +5 -1
  17. data/lib/adminterface/engine.rb +0 -7
  18. data/lib/adminterface/extensions/namespace_settings.rb +3 -0
  19. data/lib/adminterface/extensions/views/pages/base.rb +2 -0
  20. data/lib/adminterface/version.rb +1 -1
  21. data/lib/adminterface.rb +0 -31
  22. metadata +9 -12
  23. data/lib/adminterface/encryption/encryptor.rb +0 -26
  24. data/lib/adminterface/license.rb +0 -18
  25. data/lib/adminterface/licensing/base.rb +0 -99
  26. data/lib/adminterface/licensing/commercial.rb +0 -7
  27. data/lib/adminterface/licensing/notice.rb +0 -54
  28. data/lib/adminterface/licensing/personal.rb +0 -13
  29. data/lib/adminterface/public.pem +0 -9
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 55f6123dc817294d2566d26a1bf0537e4872ac7f21f614302d0c03a7eb2b5d02
4
- data.tar.gz: bcf8d3977bfecbf02325ebb7c54eed28b1b28bf6858ce5e740a5ea7ad1a82536
3
+ metadata.gz: 18beaba0b474654bab684cc576d19be2c8a7d4c1c956733196fd428ff127381c
4
+ data.tar.gz: 5fa18f21332acb4d428670e70923fdab7880172bfedcce2faf4f1a742b6426a0
5
5
  SHA512:
6
- metadata.gz: d4e1d39c819aa2b33ba261343fd69f99db3af0a99a7b9a5bd7d5db236a2a041989d0a074de689bf0be058fec7ae5c8169ef6add875c231e98d00f1af132bec79
7
- data.tar.gz: 0b47892df0a7b40cff4f99038228e2cc00cb9be1654517ed0297fb8dde4daa1c2a000c4d9228f839d042222775a602300165fd47a47eaf4f7870a583f52d7d92
6
+ metadata.gz: 9c48cbd82014ac58317f0d8a6c6d70cc2c05b24ff977deccfe4fe0cd23d42680e7ae5c7d009a1b27b5eab08f28a7123e087522fd608874a41e64e766e77ebfb7
7
+ data.tar.gz: 86f4704e4c64c1f62a66e70c4924183f558bf1f4f84550e94a48d04df9598f23e589764dd330cbdf7d029d007caa7d56f51b7dee7c263cf5c355eb72d491a42b
data/LICENSE.md CHANGED
@@ -1,109 +1,19 @@
1
- # Terms of Service
2
- This license is a legal agreement between you and CMDBrew Studio Inc. for the use of Active Admin Bootstrap (the “Software”). By downloading any Active Admin Bootstrap files, add-ons, or resources, you agree to be bound by the terms and conditions of this license. CMDBrew Studio Inc. reserves the right to alter this agreement at any time, for any reason, without notice.
3
-
4
- ## Definitions
5
- - **"Effective Date"** means the date and time you first download, copy, install, access, deploy or otherwise make use of the Software.
6
-
7
- - **"Entity"** means a sole proprietorship, corporation, partnership, joint venture, limited-liability company, governmental authority or agency, unincorporated organization, trust, or other legally-recognized association of two or more individuals and/or Entities.
8
-
9
- - **"Intellectual Property Rights"** means all rights in and to U.S., Canada, and/or foreign (a) copyrights or works of authorship (whether copyrightable or not), including computer programs and rights in and to data and databases, (b) trademarks, service marks, trade dress, trade names, logos, corporate names, or domain names, or other similar designations of source or origin, together with the goodwill symbolized by any of the foregoing, (c) patents, patent disclosures, or inventions (whether patentable or not), (d) trade secrets, know-how, or other confidential information, and (e) all other intellectual property rights, in each case whether registered or unregistered, and including all registrations and applications for, and renewals or extensions of, such rights, and all similar or equivalent rights or forms of protection in any part of the world.
10
-
11
- - **"Law"** means any statute, law, ordinance, regulation, rule, code, order, constitution, treaty, common law, judgment, decree, or other requirement of any federal, state, provincial, local, or foreign government or political subdivision thereof, or any court, arbitrator, or tribunal of competent jurisdiction.
12
-
13
- - **"Personal Purpose"** Personal use for research, experiment, and testing for the benefit of public
14
- knowledge, personal study, private entertainment, hobby projects, amateur
15
- pursuits, or religious observance, without any anticipated commercial
16
- application, doesn't count as use for a commercial purpose.
17
-
18
- - **"Non-Personal Purpose"** Use for purposes that are outside of Personal Purpose.
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
- ## Quick Start
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
- Adminterface is free for personal and non-commercial use and with a yearly subscription for commercial purposes. See our [Terms of Service in full text](LICENSE.md) for more information.
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).
@@ -119,6 +119,7 @@
119
119
  -webkit-box-align: end;
120
120
  -webkit-box-direction: reverse;
121
121
  flex-direction: row-reverse;
122
+ justify-content: flex-start;
122
123
  }
123
124
 
124
125
  li {
@@ -37,3 +37,7 @@
37
37
  -webkit-box-shadow: #{$value};
38
38
  -moz-box-shadow: #{$value};
39
39
  }
40
+
41
+ @mixin lang-dir-rtl {
42
+ @at-root { html[dir="rtl"] & { @content; } }
43
+ }
@@ -1,3 +1,4 @@
1
+ @import './components/breadcrumb';
1
2
  @import './components/buttons';
2
3
  @import './components/dropdowns';
3
4
  @import './components/form/labels';
@@ -0,0 +1 @@
1
+ ol.breadcrumb { padding-left: 0; }
@@ -6,6 +6,12 @@
6
6
  .flatpickr-next-month {
7
7
  padding: 5px 10px;
8
8
 
9
+ svg {
10
+ @include lang-dir-rtl {
11
+ @include transform(rotate(180deg));
12
+ }
13
+ }
14
+
9
15
  &:hover svg {
10
16
  fill: $flatpickr-ctrl-hover-bg;
11
17
  }
@@ -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,2 @@
1
+ <div class="breakpoint-up-helper">
2
+ </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', function (e) {
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>
@@ -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 || {})
@@ -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
@@ -6,6 +6,9 @@ module Adminterface
6
6
  CONFIGS = Adminterface::Configs::DEFAULTS
7
7
 
8
8
  included do
9
+ # Language direction ltr or rtl
10
+ register :lang_dir, "ltr", :string_symbol_or_proc
11
+
9
12
  # The default component configs
10
13
  register :components, CONFIGS[:components]
11
14
 
@@ -5,6 +5,8 @@ module Adminterface
5
5
  module Base
6
6
  def build(*args)
7
7
  super
8
+ set_attribute :dir,
9
+ render_or_call_method_or_proc_on(self, active_admin_config.lang_dir(self))
8
10
  build_breakpoint_helpers
9
11
  build_meta_tags_for_js
10
12
  end
@@ -1,3 +1,3 @@
1
1
  module Adminterface
2
- VERSION = "0.2.3"
2
+ VERSION = "0.3.0"
3
3
  end
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.2.3
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-09 00:00:00.000000000 Z
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
- - See LICENSE in LICENSE.md
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.22
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
@@ -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,7 +0,0 @@
1
- require_relative "./base"
2
-
3
- module Adminterface
4
- module Licensing
5
- class Commercial < Base; end
6
- end
7
- 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
@@ -1,13 +0,0 @@
1
- require_relative "./base"
2
-
3
- module Adminterface
4
- module Licensing
5
- class Personal < Base
6
- private
7
-
8
- def request?
9
- false
10
- end
11
- end
12
- end
13
- end
@@ -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-----