mautic 0.1.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (39) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +97 -0
  4. data/Rakefile +33 -0
  5. data/app/assets/config/mautic_manifest.js +2 -0
  6. data/app/assets/javascripts/mautic/application.js +16 -0
  7. data/app/assets/javascripts/mautic/mautic_connections.js +2 -0
  8. data/app/assets/stylesheets/mautic/application.css +15 -0
  9. data/app/assets/stylesheets/mautic/mautic_connections.css +4 -0
  10. data/app/assets/stylesheets/scaffold.css +80 -0
  11. data/app/controllers/mautic/application_controller.rb +5 -0
  12. data/app/controllers/mautic/connections_controller.rb +109 -0
  13. data/app/helpers/mautic/application_helper.rb +4 -0
  14. data/app/jobs/mautic/application_job.rb +4 -0
  15. data/app/mailers/mautic/application_mailer.rb +6 -0
  16. data/app/models/application_record.rb +3 -0
  17. data/app/models/mautic/application_record.rb +5 -0
  18. data/app/models/mautic/connection.rb +43 -0
  19. data/app/models/mautic/connections/oauth2.rb +61 -0
  20. data/app/models/mautic/form.rb +9 -0
  21. data/app/views/layouts/mautic/application.html.erb +18 -0
  22. data/app/views/mautic/connections/_form.html.erb +38 -0
  23. data/app/views/mautic/connections/edit.html.erb +3 -0
  24. data/app/views/mautic/connections/index.html.erb +30 -0
  25. data/app/views/mautic/connections/new.html.erb +3 -0
  26. data/app/views/mautic/connections/show.html.erb +18 -0
  27. data/config/locales/en.yml +17 -0
  28. data/config/routes.rb +9 -0
  29. data/db/migrate/20171028082047_create_mautic_mautic_connections.rb +16 -0
  30. data/lib/mautic.rb +33 -0
  31. data/lib/mautic/engine.rb +18 -0
  32. data/lib/mautic/form_helper.rb +49 -0
  33. data/lib/mautic/model.rb +98 -0
  34. data/lib/mautic/proxy.rb +43 -0
  35. data/lib/mautic/version.rb +3 -0
  36. data/lib/tasks/mautic_tasks.rake +4 -0
  37. data/spec/rails_helper.rb +6 -0
  38. data/spec/spec_helper.rb +85 -0
  39. metadata +243 -0
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ba8f8a30689a990362470a3af955dec9806de43e
4
+ data.tar.gz: 6bfa9b65f2f37fb3d04ca4b796fbca69d6adb59b
5
+ SHA512:
6
+ metadata.gz: 574a39ff9fa73874b6d80d0781cccf2528aff12fc7c9acc6cb94febc3b3859fccbafd7b094e96add0b8f685fa2c45e45bfc9e724f3c2a8ef782b7b20391f9cd5
7
+ data.tar.gz: bd728bc2be94076c71abb072fa899dd401b58e3e371cad41b0c25fbf74c2e2589d7fa47311814fdda0b7f56c623a087be44cd702bf1fab4f3619fa0ff9bfc735
@@ -0,0 +1,20 @@
1
+ Copyright 2017 Lukáš Pokorný
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,97 @@
1
+ # Mautic rails
2
+ RoR helper / wrapper for Mautic API and forms
3
+
4
+ *Rails 4 compatible*
5
+ ## Usage
6
+ ### Gem provides API connection to your Mautic(s)
7
+ 1. Create mautic connection
8
+ 2. Authorize it
9
+
10
+ Find connection which you want to use:
11
+ ```ruby
12
+ m = Mautic::Connection.last
13
+ ```
14
+ Get specify contact:
15
+ ```ruby
16
+ contact = m.contact.find(1) # => #<Mautic::Contact id=1 ...>
17
+ ```
18
+ Collections of contacts:
19
+ ```ruby
20
+ m.contacts.where("gmail").each do |contact|
21
+ #<Mautic::Contact id=12 ...>
22
+ #<Mautic::Contact id=21 ...>
23
+ #<Mautic::Contact id=99 ...>
24
+ end
25
+ ```
26
+ New instance of contacts:
27
+ ```ruby
28
+ contact = m.contacts.new({ email: "newcontactmail@fake.info"} )
29
+ contact.save # => true
30
+ ```
31
+ Update contact
32
+ ```ruby
33
+ contact.email = ""
34
+ contact.save # => false
35
+ contact.errors # => [{"code"=>400, "message"=>"email: This field is required.", "details"=>{"email"=>["This field is required."]}}]
36
+ ```
37
+ Of course you can use more than contact: `assets`, `emails`, `companies`, `forms`, `points` ...
38
+ ### Gem provides simple Mautic form submit
39
+ There are two options of usage:
40
+ 1. Use default mautic url from configuration and shortcut class method:
41
+ ```ruby
42
+ # form: ID of form in Mautic *required*
43
+ # url: Mautic URL - default is from configuration
44
+ # request: request object (for domain, and forward IP...) *optional*
45
+ Mautic::FormHelper.submit(form: "mautic form ID") do |i|
46
+ i.form_field1 = "value1"
47
+ i.form_field2 = "value2"
48
+ end
49
+ ```
50
+ 2. Or create instance
51
+ ```ruby
52
+ # request is *optional*
53
+ m = Mautic::FormHelper.new("https://mymautic.com", request)
54
+ m.data = {} # hash of attributes
55
+ m.push # push data to mautic
56
+ ```
57
+
58
+ ## Installation
59
+ Add this line to your application's Gemfile:
60
+
61
+ ```ruby
62
+ gem 'mautic', '~>0.1'
63
+ ```
64
+
65
+ And then execute:
66
+ ```bash
67
+ $ bundle
68
+ ```
69
+
70
+ Or install it yourself as:
71
+ ```bash
72
+ $ gem install mautic
73
+ ```
74
+
75
+ ## Configuration
76
+
77
+ add to `config/initializers/mautic.rb`:
78
+ ```ruby
79
+ Mautic.configure do |config|
80
+ # This is for oauth handshake token url. I need to know where your app listen
81
+ config.base_url = "https://my-rails-app.com"
82
+ # *optional* This is your default mautic URL - used in form helper
83
+ config.mautic_url = "https://mautic.my.app"
84
+ end
85
+ ```
86
+
87
+ add to `config/routes.rb`
88
+ ```ruby
89
+ mount Mautic::Engine => "/mautic"
90
+
91
+ ```
92
+
93
+ ## Contributing
94
+ Ideas and pull requests are welcome!
95
+
96
+ ## License
97
+ The gem is available as open source under the terms of the [MIT License](http://opensource.org/licenses/MIT).
@@ -0,0 +1,33 @@
1
+ begin
2
+ require 'bundler/setup'
3
+ rescue LoadError
4
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
5
+ end
6
+
7
+ require 'rdoc/task'
8
+
9
+ RDoc::Task.new(:rdoc) do |rdoc|
10
+ rdoc.rdoc_dir = 'rdoc'
11
+ rdoc.title = 'Mautic'
12
+ rdoc.options << '--line-numbers'
13
+ rdoc.rdoc_files.include('README.md')
14
+ rdoc.rdoc_files.include('lib/**/*.rb')
15
+ end
16
+
17
+ APP_RAKEFILE = File.expand_path("../spec/dummy/Rakefile", __FILE__)
18
+ load 'rails/tasks/engine.rake'
19
+
20
+
21
+ load 'rails/tasks/statistics.rake'
22
+
23
+
24
+
25
+ require 'bundler/gem_tasks'
26
+
27
+ # require 'rspec/core'
28
+ # require 'rspec/core/rake_task'
29
+ #
30
+ # desc 'Run all specs in spec directory (excluding plugin specs)'
31
+ # RSpec::Core::RakeTask.new(spec: 'app:db:test:prepare')
32
+ #
33
+ # task default: :spec
@@ -0,0 +1,2 @@
1
+ //= link_directory ../javascripts/mautic .js
2
+ //= link_directory ../stylesheets/mautic .css
@@ -0,0 +1,16 @@
1
+ // This is a manifest file that'll be compiled into application.js, which will include all the files
2
+ // listed below.
3
+ //
4
+ // Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
5
+ // or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
6
+ //
7
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
8
+ // compiled file. JavaScript code in this file should be added after the last require_* statement.
9
+ //
10
+ // Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
11
+ // about supported directives.
12
+ //
13
+ // require rails-ujs
14
+ //= require jquery
15
+ //= require jquery_ujs
16
+ //= require_tree .
@@ -0,0 +1,2 @@
1
+ // Place all the behaviors and hooks related to the matching controller here.
2
+ // All this logic will automatically be available in application.js.
@@ -0,0 +1,15 @@
1
+ /*
2
+ * This is a manifest file that'll be compiled into application.css, which will include all the files
3
+ * listed below.
4
+ *
5
+ * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
6
+ * or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
7
+ *
8
+ * You're free to add application-wide styles to this file and they'll appear at the bottom of the
9
+ * compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
10
+ * files in this directory. Styles in this file should be added after the last require_* statement.
11
+ * It is generally better to create a new file per style scope.
12
+ *
13
+ *= require_tree .
14
+ *= require_self
15
+ */
@@ -0,0 +1,4 @@
1
+ /*
2
+ Place all the styles related to the matching controller here.
3
+ They will automatically be included in application.css.
4
+ */
@@ -0,0 +1,80 @@
1
+ body {
2
+ background-color: #fff;
3
+ color: #333;
4
+ margin: 33px;
5
+ }
6
+
7
+ body, p, ol, ul, td {
8
+ font-family: verdana, arial, helvetica, sans-serif;
9
+ font-size: 13px;
10
+ line-height: 18px;
11
+ }
12
+
13
+ pre {
14
+ background-color: #eee;
15
+ padding: 10px;
16
+ font-size: 11px;
17
+ }
18
+
19
+ a {
20
+ color: #000;
21
+ }
22
+
23
+ a:visited {
24
+ color: #666;
25
+ }
26
+
27
+ a:hover {
28
+ color: #fff;
29
+ background-color: #000;
30
+ }
31
+
32
+ th {
33
+ padding-bottom: 5px;
34
+ }
35
+
36
+ td {
37
+ padding: 0 5px 7px;
38
+ }
39
+
40
+ div.field,
41
+ div.actions {
42
+ margin-bottom: 10px;
43
+ }
44
+
45
+ #notice {
46
+ color: green;
47
+ }
48
+
49
+ .field_with_errors {
50
+ padding: 2px;
51
+ background-color: red;
52
+ display: table;
53
+ }
54
+
55
+ #error_explanation {
56
+ width: 450px;
57
+ border: 2px solid red;
58
+ padding: 7px 7px 0;
59
+ margin-bottom: 20px;
60
+ background-color: #f0f0f0;
61
+ }
62
+
63
+ #error_explanation h2 {
64
+ text-align: left;
65
+ font-weight: bold;
66
+ padding: 5px 5px 5px 15px;
67
+ font-size: 12px;
68
+ margin: -7px -7px 0;
69
+ background-color: #c00;
70
+ color: #fff;
71
+ }
72
+
73
+ #error_explanation ul li {
74
+ font-size: 12px;
75
+ list-style: square;
76
+ }
77
+
78
+ label {
79
+ display: block;
80
+ }
@@ -0,0 +1,5 @@
1
+ module Mautic
2
+ class ApplicationController < ActionController::Base
3
+ protect_from_forgery with: :exception
4
+ end
5
+ end
@@ -0,0 +1,109 @@
1
+ module Mautic
2
+ class ConnectionsController < ApplicationController
3
+ before_action :set_mautic_connection, only: [:show, :edit, :update, :destroy, :oauth2, :authorize]
4
+
5
+ # GET /mautic_connections
6
+ def index
7
+ @mautic_connections = Connection.order(:url)
8
+ respond_to do |format|
9
+ format.html { render layout: !request.xhr? }
10
+ format.json { render json: @mautic_connections }
11
+ end
12
+ end
13
+
14
+ # GET /mautic_connections/1
15
+ def show
16
+ respond_to do |format|
17
+ format.html { render layout: !request.xhr? }
18
+ end
19
+ end
20
+
21
+ # GET /mautic_connections/new
22
+ def new
23
+ @mautic_connection = Connection.new
24
+ respond_to do |format|
25
+ format.html { render layout: !request.xhr? }
26
+ end
27
+ end
28
+
29
+ # GET /mautic_connections/1/edit
30
+ def edit
31
+ respond_to do |format|
32
+ format.html { render layout: !request.xhr? }
33
+ end
34
+ end
35
+
36
+ # POST /mautic_connections
37
+ def create
38
+ @mautic_connection = Connection.new(mautic_connection_params)
39
+
40
+ respond_to do |format|
41
+ if @mautic_connection.save
42
+ format.html { redirect_to mautic.connection_path(@mautic_connection), notice: t('mautic.text_mautic_connection_created') }
43
+ format.js { head :no_content }
44
+ format.json { render json: @mautic_connection }
45
+ else
46
+ format.html { render :new }
47
+ format.js { head :unprocessable_entity }
48
+ format.json { render json: @mautic_connection.errors, status: :unprocessable_entity }
49
+ end
50
+ end
51
+ end
52
+
53
+ # PATCH/PUT /mautic_connections/1
54
+ def update
55
+ respond_to do |format|
56
+ if @mautic_connection.update(mautic_connection_params)
57
+ format.html { redirect_to mautic.connection_path(@mautic_connection), notice: t('mautic.text_mautic_connection_updated') }
58
+ format.js { head :no_content }
59
+ format.json { render json: @mautic_connection }
60
+ else
61
+ format.html { render :edit }
62
+ format.js { head :unprocessable_entity }
63
+ format.json { render json: @mautic_connection.errors, status: :unprocessable_entity }
64
+ end
65
+ end
66
+ end
67
+
68
+ # DELETE /mautic_connections/1
69
+ def destroy
70
+ @mautic_connection.destroy
71
+ respond_to do |format|
72
+ format.html { redirect_to :connections, notice: t('mautic.text_mautic_connection_destroyed') }
73
+ format.js { render js: "document.getElementById('#{view_context.dom_id(@mautic_connection)}').remove()" }
74
+ format.json { render json: @mautic_connection }
75
+ end
76
+ end
77
+
78
+ # ==--==--==--==--
79
+
80
+ def authorize
81
+ redirect_to @mautic_connection.authorize
82
+ end
83
+
84
+ def oauth2
85
+ begin
86
+ response = @mautic_connection.get_code(params.require(:code))
87
+ @mautic_connection.update(token: response.token, refresh_token: response.refresh_token)
88
+ return render plain: t('mautic.text_mautic_authorize_successfully')
89
+ rescue OAuth2::Error => e
90
+ flash[:error] = e.message
91
+ end
92
+
93
+ render :show
94
+ end
95
+
96
+ private
97
+ # Use callbacks to share common setup or constraints between actions.
98
+ def set_mautic_connection
99
+ @mautic_connection = Connection.find(params[:id])
100
+ rescue ActiveRecord::RecordNotFound => e
101
+ return render head: 404, plain: e.message
102
+ end
103
+
104
+ # Only allow a trusted parameter "white list" through.
105
+ def mautic_connection_params
106
+ params.require(:connection).permit(:url, :client_id, :secret, :type)
107
+ end
108
+ end
109
+ end
@@ -0,0 +1,4 @@
1
+ module Mautic
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,4 @@
1
+ module Mautic
2
+ class ApplicationJob < ActiveJob::Base
3
+ end
4
+ end
@@ -0,0 +1,6 @@
1
+ module Mautic
2
+ class ApplicationMailer < ActionMailer::Base
3
+ default from: 'from@example.com'
4
+ layout 'mailer'
5
+ end
6
+ end
@@ -0,0 +1,3 @@
1
+ class ApplicationRecord < ActiveRecord::Base
2
+ self.abstract_class = true
3
+ end
@@ -0,0 +1,5 @@
1
+ module Mautic
2
+ class ApplicationRecord < ActiveRecord::Base
3
+ self.abstract_class = true
4
+ end
5
+ end
@@ -0,0 +1,43 @@
1
+ module Mautic
2
+ class Connection < ApplicationRecord
3
+
4
+ self.table_name = 'mautic_connections'
5
+
6
+ validates :url, :client_id, :secret, presence: true
7
+ validates :url, format: URI::regexp(%w(http https))
8
+
9
+ alias_attribute :access_token, :token
10
+
11
+ def client
12
+ raise NotImplementedError
13
+ end
14
+
15
+ def authorize
16
+ raise NotImplementedError
17
+ end
18
+
19
+ def get_code(code)
20
+ raise NotImplementedError
21
+ end
22
+
23
+ def connection
24
+ raise NotImplementedError
25
+ end
26
+
27
+ def refresh!
28
+ raise NotImplementedError
29
+ end
30
+
31
+ %w(assets campaigns categories companies contacts emails forms messages notes notifications pages points roles stats users).each do |entity|
32
+ define_method entity do
33
+ Proxy.new(self, entity)
34
+ end
35
+ end
36
+
37
+ def request(type, path, params = {})
38
+ raise NotImplementedError
39
+ end
40
+
41
+
42
+ end
43
+ end
@@ -0,0 +1,61 @@
1
+ module Mautic
2
+ module Connections
3
+ class Oauth2 < Mautic::Connection
4
+
5
+ def client
6
+ @client ||= OAuth2::Client.new(client_id, secret, {
7
+ site: url,
8
+ authorize_url: '/oauth/v2/authorize',
9
+ token_url: '/oauth/v2/token',
10
+ raise_errors: false
11
+ })
12
+ end
13
+
14
+ def authorize
15
+ client.auth_code.authorize_url(redirect_uri: callback_url)
16
+ end
17
+
18
+ def get_code(code)
19
+ client.auth_code.get_token(code, redirect_uri: callback_url)
20
+ end
21
+
22
+ def connection
23
+ @connection ||= OAuth2::AccessToken.new(client, token, { refresh_token: refresh_token })
24
+ end
25
+
26
+ def refresh!
27
+ @connection = connection.refresh!
28
+ update(token: @connection.token, refresh_token: @connection.refresh_token)
29
+ @connection
30
+ end
31
+
32
+ def request(type, path, params = {})
33
+ json = JSON.parse connection.request(type, path, params).body
34
+ Array(json['errors']).each do |error|
35
+ case error['code']
36
+ when 400
37
+ # Validation error
38
+ when 401
39
+ raise Mautic::TokenExpiredError.new(error['message']) if @try_to_refresh
40
+ @try_to_refresh = true
41
+ refresh!
42
+ json = request(type, path, params)
43
+ else
44
+ raise Mautic::AuthorizeError.new("#{error['code']} - #{error['message']}")
45
+ end
46
+ end
47
+ json
48
+ end
49
+
50
+ private
51
+
52
+ def callback_url
53
+ # return Mautic.config.oauth2_callback_url if Mautic.config.oauth2_callback_url
54
+ uri = URI.parse(Mautic.config.base_url)
55
+ uri.path = Mautic::Engine.routes.url_helpers.oauth2_connection_path(self)
56
+ uri.to_s
57
+ end
58
+
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,9 @@
1
+ module Mautic
2
+ class Form < Model
3
+
4
+ def assign_attributes(source = nil)
5
+ self.attributes = {name: source['name'], fields: source['fields']} if source.is_a? Hash
6
+ end
7
+
8
+ end
9
+ end
@@ -0,0 +1,18 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Mautic</title>
5
+ <%= stylesheet_link_tag "mautic/application", media: "all" %>
6
+ <%= javascript_include_tag "mautic/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+ <% Array(flash).each do |type, message| %>
11
+ <div class="alert <%= type %>">
12
+ <%= message %>
13
+ </div>
14
+ <% end %>
15
+ <%= yield %>
16
+
17
+ </body>
18
+ </html>
@@ -0,0 +1,38 @@
1
+ <%#= form_with(model: mautic_connection.becomes(Mautic::Connection), local: true) do |form| %>
2
+ <%= form_for(mautic_connection.becomes(Mautic::Connection)) do |form| %>
3
+ <% if mautic_connection.errors.any? %>
4
+ <div id="error_explanation">
5
+ <h2><%= pluralize(mautic_connection.errors.count, "error") %> prohibited this mautic_connection from being saved:</h2>
6
+
7
+ <ul>
8
+ <% mautic_connection.errors.full_messages.each do |message| %>
9
+ <li><%= message %></li>
10
+ <% end %>
11
+ </ul>
12
+ </div>
13
+ <% end %>
14
+
15
+ <div class="field">
16
+ <%= form.label :type %>
17
+ <%= form.select :type, %w(Mautic::Connections::Oauth2) %>
18
+ </div>
19
+
20
+ <div class="field">
21
+ <%= form.label :url %>
22
+ <%= form.text_field :url, id: :mautic_connection_url %>
23
+ </div>
24
+
25
+ <div class="field">
26
+ <%= form.label :client_id %>
27
+ <%= form.text_field :client_id, id: :mautic_connection_client_id %>
28
+ </div>
29
+
30
+ <div class="field">
31
+ <%= form.label :secret %>
32
+ <%= form.text_field :secret, id: :mautic_connection_secret %>
33
+ </div>
34
+
35
+ <div class="actions">
36
+ <%= form.submit %>
37
+ </div>
38
+ <% end %>
@@ -0,0 +1,3 @@
1
+ <h1>Editing Mautic Connection</h1>
2
+
3
+ <%= render 'form', mautic_connection: @mautic_connection %>
@@ -0,0 +1,30 @@
1
+ <p id="notice"><%= notice %></p>
2
+
3
+ <h1>Mautic Connections</h1>
4
+
5
+ <table class="table table-stripped">
6
+ <thead>
7
+ <tr>
8
+ <th>Url</th>
9
+ <th>Client</th>
10
+ <th>Secret</th>
11
+ <th colspan="2"></th>
12
+ </tr>
13
+ </thead>
14
+
15
+ <tbody>
16
+ <% @mautic_connections.each do |mautic_connection| %>
17
+ <tr>
18
+ <td><%= link_to(mautic_connection.url, mautic.connection_path(mautic_connection)) %></td>
19
+ <td><%= mautic_connection.client_id %></td>
20
+ <td><%= mautic_connection.secret %></td>
21
+ <td><%= link_to 'Edit', mautic.edit_connection_path(mautic_connection) %></td>
22
+ <td><%= link_to 'Destroy', mautic.connection_path(mautic_connection), method: :delete, data: { confirm: 'Are you sure?' } %></td>
23
+ </tr>
24
+ <% end %>
25
+ </tbody>
26
+ </table>
27
+
28
+ <br>
29
+
30
+ <%= link_to 'New Mautic Connection', mautic.new_connection_path %>
@@ -0,0 +1,3 @@
1
+ <h1>New Mautic Connection</h1>
2
+
3
+ <%= render 'form', mautic_connection: @mautic_connection %>
@@ -0,0 +1,18 @@
1
+ <p>
2
+ <strong>Url:</strong>
3
+ <%= @mautic_connection.url %>
4
+ </p>
5
+
6
+ <p>
7
+ <strong>Client:</strong>
8
+ <%= @mautic_connection.client_id %>
9
+ </p>
10
+
11
+ <p>
12
+ <strong>Secret:</strong>
13
+ <%= @mautic_connection.secret %>
14
+ </p>
15
+
16
+ <%= link_to 'Authorize', mautic.authorize_connection_path(@mautic_connection) %> |
17
+ <%= link_to 'Edit', mautic.edit_connection_path(@mautic_connection) %> |
18
+ <%= link_to 'Back', mautic.connections_path %>
@@ -0,0 +1,17 @@
1
+ en:
2
+ attributes:
3
+ activerecord:
4
+ models:
5
+ mautic_connection: Mautic
6
+ attributes:
7
+ mautic_connection:
8
+ url: Address
9
+ client_id: Public key
10
+ secret: Secret key
11
+ token: Code
12
+
13
+ mautic:
14
+ text_mautic_connection_created: Mautic connection created
15
+ text_mautic_connection_updated: Mautic connection updated
16
+ text_mautic_connection_destroyed: Mautic connection destroyed
17
+ text_mautic_authorize_successfully: Connection authorized!
@@ -0,0 +1,9 @@
1
+ Mautic::Engine.routes.draw do
2
+ resources :connections do
3
+ member do
4
+ get :authorize
5
+ get :oauth2
6
+
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,16 @@
1
+ class CreateMauticMauticConnections < ActiveRecord::Migration
2
+ def change
3
+ create_table :mautic_connections do |t|
4
+ t.string :type
5
+
6
+ t.string :url
7
+ t.string :client_id
8
+ t.string :secret
9
+
10
+ t.string :token
11
+ t.string :refresh_token
12
+
13
+ t.timestamps null: false
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,33 @@
1
+ require "oauth2"
2
+ require "jquery-rails"
3
+ require "mautic/engine"
4
+
5
+ module Mautic
6
+ include ::ActiveSupport::Configurable
7
+
8
+ autoload :FormHelper, 'mautic/form_helper'
9
+ autoload :Proxy, 'mautic/proxy'
10
+ autoload :Model, 'mautic/model'
11
+
12
+ class TokenExpiredError < StandardError
13
+ end
14
+
15
+ class ValidationError < StandardError
16
+
17
+ end
18
+
19
+ class AuthorizeError < StandardError
20
+ end
21
+
22
+ configure do |config|
23
+ # This is URL your application - its for oauth callbacks
24
+ config.base_url = "http://localhost:3000"
25
+ # *optional* This is your default mautic URL - used in form helper
26
+ config.mautic_url = "https://mautic.my.app"
27
+ end
28
+ # Your code goes here...
29
+
30
+ class Contact < Model
31
+
32
+ end
33
+ end
@@ -0,0 +1,18 @@
1
+ module Mautic
2
+ class Engine < ::Rails::Engine
3
+ isolate_namespace Mautic
4
+
5
+ config.generators do |g|
6
+ g.test_framework :rspec, fixture: false
7
+ end
8
+
9
+ initializer :append_migrations do |app|
10
+ unless app.root.to_s.match root.to_s
11
+ config.paths['db/migrate'].expanded.each do |expanded_path|
12
+ app.config.paths['db/migrate'] << expanded_path
13
+ end
14
+ end
15
+ end
16
+
17
+ end
18
+ end
@@ -0,0 +1,49 @@
1
+ require 'rest-client'
2
+ module Mautic
3
+ class FormHelper
4
+
5
+ # shortcut
6
+ def self.submit(url: nil, form: nil, request: nil, &block)
7
+ m = new(url || Mautic.config.mautic_url, request)
8
+ m.send_data form, &block
9
+ end
10
+
11
+ attr_reader :url, :host
12
+ attr_accessor :forward_ip
13
+ attr_writer :data
14
+
15
+ def initialize(url, request = nil)
16
+ @url = url
17
+
18
+ @host = request&.host
19
+ @forward_ip = request&.remote_ip
20
+ end
21
+
22
+ def send_data(form_id, &block)
23
+ @collector = OpenStruct.new(formId: form_id)
24
+ yield @collector
25
+ self.data = @collector.to_h
26
+
27
+ push
28
+ end
29
+
30
+ def data
31
+ raise ArgumentError if @data.nil?
32
+ defaults = {
33
+ 'submit' => '1',
34
+ 'domain' => host
35
+ }
36
+ defaults.merge(@data.to_h).inject({}){|mem, (name, value)| mem["mauticform[#{name}]"] = value; mem}
37
+ end
38
+
39
+ def submit
40
+ uri = URI.parse(url)
41
+ uri.path = '/form/submit'
42
+ headers = {}
43
+ headers.store 'X-Forwarded-For', forward_ip if forward_ip
44
+ RestClient.post uri.to_s, data, headers
45
+ end
46
+ alias_method :push, :submit
47
+ end
48
+
49
+ end
@@ -0,0 +1,98 @@
1
+ module Mautic
2
+ class Model < OpenStruct
3
+
4
+ class MauticHash < Hash
5
+
6
+ def []=(name, value)
7
+ @changes ||= {}
8
+ @changes[name] = value
9
+ super
10
+ end
11
+
12
+ def changes
13
+ @changes || {}
14
+ end
15
+
16
+ end
17
+
18
+ class << self
19
+
20
+ def endpoint
21
+ name.demodulize.underscore.pluralize
22
+ end
23
+
24
+ def in(connection)
25
+ Proxy.new(connection, endpoint)
26
+ end
27
+
28
+ end
29
+
30
+ def initialize(connection, hash=nil)
31
+ @connection = connection
32
+ @table = MauticHash.new
33
+ self.attributes = { created_at: hash['dateAdded']&.to_time, updated_at: hash['dateModified']&.to_time } if hash
34
+ assign_attributes(hash)
35
+ end
36
+
37
+ def save(force = false)
38
+ id.present? ? update(force) : create
39
+ end
40
+
41
+ def update(force = false)
42
+ return false if changes.blank?
43
+ json = @connection.request((force && :put || :patch), "api/#{endpoint}/#{id}/edit", { body: to_h })
44
+ if json['errors']
45
+ self.errors = json['errors']
46
+ else
47
+ self.attributes = json[endpoint.singularize]
48
+ end
49
+ json['errors'].blank?
50
+ end
51
+
52
+ def create
53
+ json = @connection.request(:post, "api/#{endpoint}/#{id}/new", { body: to_h })
54
+ if json['errors']
55
+ self.errors = json['errors']
56
+ else
57
+ self.attributes = json[endpoint.singularize]
58
+ end
59
+ json['errors'].blank?
60
+ end
61
+
62
+ def destroy
63
+ json = @connection.request(:delete, "api/#{endpoint}/#{id}/delete")
64
+ self.errors = json['errors'] if json['errors']
65
+ json['errors'].blank?
66
+ end
67
+
68
+ def changes
69
+ @table.changes
70
+ end
71
+
72
+ private
73
+
74
+ def endpoint
75
+ self.class.endpoint
76
+ end
77
+
78
+ def attributes=(hash)
79
+ hash.each_pair do |k, v|
80
+ k = k.to_sym
81
+ @table[k] = v
82
+ end
83
+ @table.instance_variable_set(:@changes, nil)
84
+ end
85
+
86
+ def assign_attributes(source = nil)
87
+ source ||= {}
88
+ data = {}
89
+ if (fields = source['fields'])
90
+ data.merge!(fields['all']) if fields['all']
91
+ elsif source
92
+ data = source
93
+ end
94
+ self.attributes = data
95
+ end
96
+
97
+ end
98
+ end
@@ -0,0 +1,43 @@
1
+ module Mautic
2
+ class Proxy
3
+
4
+ def initialize(connection, endpoint)
5
+ @connection = connection
6
+ klass = "Mautic::#{endpoint.classify}"
7
+ @target = klass.safe_constantize || Mautic.const_set(endpoint.classify, Class.new(Mautic::Model))
8
+ @endpoint = endpoint
9
+ end
10
+
11
+ def new(attributes = {})
12
+ @target.new(@connection, attributes)
13
+ end
14
+
15
+ def all(options={})
16
+ where(options)
17
+ end
18
+
19
+ def where(arg = '')
20
+ params = case arg
21
+ when ::String
22
+ { search: arg } if arg.present?
23
+ when ::Hash
24
+ arg
25
+ end
26
+ json = @connection.request(:get, "api/#{@endpoint}", { params: params })
27
+ json[@endpoint].collect do |id, attributes|
28
+ @target.new(@connection, attributes || id)
29
+ end
30
+ end
31
+
32
+ def first
33
+ where(limit: 1).first
34
+ end
35
+
36
+ def find(id)
37
+ json = @connection.request(:get, "api/#{@endpoint}/#{id}")
38
+ @target.new(@connection, json[@endpoint.singularize])
39
+ end
40
+
41
+
42
+ end
43
+ end
@@ -0,0 +1,3 @@
1
+ module Mautic
2
+ VERSION = '0.1.1'
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :mautic do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,6 @@
1
+ ENV['RAILS_ENV'] ||= 'test'
2
+ require File.expand_path("../dummy/config/environment.rb", __FILE__)
3
+ # Prevent database truncation if the environment is production
4
+ abort('The Rails environment is running in production mode!') if Rails.env.production?
5
+
6
+ require 'spec_helper'
@@ -0,0 +1,85 @@
1
+ require 'rspec/rails'
2
+ require 'factory_bot_rails'
3
+ require 'database_cleaner'
4
+ require 'faker'
5
+ require 'webmock/rspec'
6
+ # require 'capybara/rspec'
7
+
8
+ ActiveRecord::Migration.maintain_test_schema!
9
+
10
+ # See http://rubydoc.info/gems/rspec-core/RSpec/Core/Configuration
11
+ RSpec.configure do |config|
12
+ # config.infer_spec_type_from_file_location!
13
+ # rspec-expectations config goes here. You can use an alternate
14
+ # assertion/expectation library such as wrong or the stdlib/minitest
15
+ # assertions if you prefer.
16
+ config.expect_with :rspec do |expectations|
17
+ # This option will default to `true` in RSpec 4. It makes the `description`
18
+ # and `failure_message` of custom matchers include text for helper methods
19
+ # defined using `chain`, e.g.:
20
+ # be_bigger_than(2).and_smaller_than(4).description
21
+ # # => "be bigger than 2 and smaller than 4"
22
+ # ...rather than:
23
+ # # => "be bigger than 2"
24
+ expectations.include_chain_clauses_in_custom_matcher_descriptions = true
25
+ end
26
+
27
+ # rspec-mocks config goes here. You can use an alternate test double
28
+ # library (such as bogus or mocha) by changing the `mock_with` option here.
29
+ config.mock_with :rspec do |mocks|
30
+ # Prevents you from mocking or stubbing a method that does not exist on
31
+ # a real object. This is generally recommended, and will default to
32
+ # `true` in RSpec 4.
33
+ mocks.verify_partial_doubles = true
34
+ end
35
+
36
+ config.include FactoryBot::Syntax::Methods
37
+ # DatabaseCleaner.clean_with(
38
+ # :truncation,
39
+ # except: %w(ar_internal_metadata)
40
+ # )
41
+
42
+ config.before(:suite) do
43
+ DatabaseCleaner.clean_with(:truncation, except: %w(ar_internal_metadata))
44
+ end
45
+
46
+ config.before(:each) do
47
+ DatabaseCleaner.strategy = :transaction
48
+ end
49
+
50
+ config.before(:each, js: true) do
51
+ DatabaseCleaner.strategy = :truncation
52
+ end
53
+
54
+ config.before(:each) do
55
+ DatabaseCleaner.start
56
+ end
57
+
58
+ config.append_after(:each) do
59
+ DatabaseCleaner.clean
60
+ end
61
+
62
+ config.use_transactional_fixtures = false
63
+
64
+ config.infer_spec_type_from_file_location!
65
+
66
+ config.filter_rails_from_backtrace!
67
+
68
+ # config.before(:each) do |ex|
69
+ # meta = ex.metadata
70
+ # unless meta[:null]
71
+ # case meta[:logged]
72
+ # when :admin
73
+ # logged(Symphonia::User, FactoryBot.create(:admin_user))
74
+ # when true
75
+ # logged(Symphonia::User, FactoryBot.create(:user))
76
+ # end
77
+ # end
78
+ # end
79
+
80
+ end
81
+
82
+
83
+ def logged(model, user)
84
+ allow(model).to receive(:current).and_return(user)
85
+ end
metadata ADDED
@@ -0,0 +1,243 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: mautic
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Lukáš Pokorný
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2017-11-01 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rails
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '4.2'
20
+ - - ">="
21
+ - !ruby/object:Gem::Version
22
+ version: 4.2.8
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ requirements:
27
+ - - "~>"
28
+ - !ruby/object:Gem::Version
29
+ version: '4.2'
30
+ - - ">="
31
+ - !ruby/object:Gem::Version
32
+ version: 4.2.8
33
+ - !ruby/object:Gem::Dependency
34
+ name: jquery-rails
35
+ requirement: !ruby/object:Gem::Requirement
36
+ requirements:
37
+ - - "~>"
38
+ - !ruby/object:Gem::Version
39
+ version: '3.1'
40
+ type: :runtime
41
+ prerelease: false
42
+ version_requirements: !ruby/object:Gem::Requirement
43
+ requirements:
44
+ - - "~>"
45
+ - !ruby/object:Gem::Version
46
+ version: '3.1'
47
+ - !ruby/object:Gem::Dependency
48
+ name: oauth2
49
+ requirement: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - "~>"
52
+ - !ruby/object:Gem::Version
53
+ version: '1.4'
54
+ type: :runtime
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ requirements:
58
+ - - "~>"
59
+ - !ruby/object:Gem::Version
60
+ version: '1.4'
61
+ - !ruby/object:Gem::Dependency
62
+ name: rest-client
63
+ requirement: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - "~>"
66
+ - !ruby/object:Gem::Version
67
+ version: '2.0'
68
+ type: :runtime
69
+ prerelease: false
70
+ version_requirements: !ruby/object:Gem::Requirement
71
+ requirements:
72
+ - - "~>"
73
+ - !ruby/object:Gem::Version
74
+ version: '2.0'
75
+ - !ruby/object:Gem::Dependency
76
+ name: sqlite3
77
+ requirement: !ruby/object:Gem::Requirement
78
+ requirements:
79
+ - - "~>"
80
+ - !ruby/object:Gem::Version
81
+ version: '1.3'
82
+ type: :development
83
+ prerelease: false
84
+ version_requirements: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - "~>"
87
+ - !ruby/object:Gem::Version
88
+ version: '1.3'
89
+ - !ruby/object:Gem::Dependency
90
+ name: rspec-rails
91
+ requirement: !ruby/object:Gem::Requirement
92
+ requirements:
93
+ - - "~>"
94
+ - !ruby/object:Gem::Version
95
+ version: '3.6'
96
+ type: :development
97
+ prerelease: false
98
+ version_requirements: !ruby/object:Gem::Requirement
99
+ requirements:
100
+ - - "~>"
101
+ - !ruby/object:Gem::Version
102
+ version: '3.6'
103
+ - !ruby/object:Gem::Dependency
104
+ name: factory_bot_rails
105
+ requirement: !ruby/object:Gem::Requirement
106
+ requirements:
107
+ - - "~>"
108
+ - !ruby/object:Gem::Version
109
+ version: '4.8'
110
+ type: :development
111
+ prerelease: false
112
+ version_requirements: !ruby/object:Gem::Requirement
113
+ requirements:
114
+ - - "~>"
115
+ - !ruby/object:Gem::Version
116
+ version: '4.8'
117
+ - !ruby/object:Gem::Dependency
118
+ name: database_cleaner
119
+ requirement: !ruby/object:Gem::Requirement
120
+ requirements:
121
+ - - "~>"
122
+ - !ruby/object:Gem::Version
123
+ version: '1.6'
124
+ type: :development
125
+ prerelease: false
126
+ version_requirements: !ruby/object:Gem::Requirement
127
+ requirements:
128
+ - - "~>"
129
+ - !ruby/object:Gem::Version
130
+ version: '1.6'
131
+ - !ruby/object:Gem::Dependency
132
+ name: faker
133
+ requirement: !ruby/object:Gem::Requirement
134
+ requirements:
135
+ - - "~>"
136
+ - !ruby/object:Gem::Version
137
+ version: '1.8'
138
+ type: :development
139
+ prerelease: false
140
+ version_requirements: !ruby/object:Gem::Requirement
141
+ requirements:
142
+ - - "~>"
143
+ - !ruby/object:Gem::Version
144
+ version: '1.8'
145
+ - !ruby/object:Gem::Dependency
146
+ name: webmock
147
+ requirement: !ruby/object:Gem::Requirement
148
+ requirements:
149
+ - - "~>"
150
+ - !ruby/object:Gem::Version
151
+ version: '3.1'
152
+ type: :development
153
+ prerelease: false
154
+ version_requirements: !ruby/object:Gem::Requirement
155
+ requirements:
156
+ - - "~>"
157
+ - !ruby/object:Gem::Version
158
+ version: '3.1'
159
+ - !ruby/object:Gem::Dependency
160
+ name: pry-rails
161
+ requirement: !ruby/object:Gem::Requirement
162
+ requirements:
163
+ - - "~>"
164
+ - !ruby/object:Gem::Version
165
+ version: '0.3'
166
+ type: :development
167
+ prerelease: false
168
+ version_requirements: !ruby/object:Gem::Requirement
169
+ requirements:
170
+ - - "~>"
171
+ - !ruby/object:Gem::Version
172
+ version: '0.3'
173
+ description: Rails client for Mautic API. Provide wrapper for push to mautic form
174
+ email:
175
+ - pokorny@luk4s.cz
176
+ executables: []
177
+ extensions: []
178
+ extra_rdoc_files: []
179
+ files:
180
+ - MIT-LICENSE
181
+ - README.md
182
+ - Rakefile
183
+ - app/assets/config/mautic_manifest.js
184
+ - app/assets/javascripts/mautic/application.js
185
+ - app/assets/javascripts/mautic/mautic_connections.js
186
+ - app/assets/stylesheets/mautic/application.css
187
+ - app/assets/stylesheets/mautic/mautic_connections.css
188
+ - app/assets/stylesheets/scaffold.css
189
+ - app/controllers/mautic/application_controller.rb
190
+ - app/controllers/mautic/connections_controller.rb
191
+ - app/helpers/mautic/application_helper.rb
192
+ - app/jobs/mautic/application_job.rb
193
+ - app/mailers/mautic/application_mailer.rb
194
+ - app/models/application_record.rb
195
+ - app/models/mautic/application_record.rb
196
+ - app/models/mautic/connection.rb
197
+ - app/models/mautic/connections/oauth2.rb
198
+ - app/models/mautic/form.rb
199
+ - app/views/layouts/mautic/application.html.erb
200
+ - app/views/mautic/connections/_form.html.erb
201
+ - app/views/mautic/connections/edit.html.erb
202
+ - app/views/mautic/connections/index.html.erb
203
+ - app/views/mautic/connections/new.html.erb
204
+ - app/views/mautic/connections/show.html.erb
205
+ - config/locales/en.yml
206
+ - config/routes.rb
207
+ - db/migrate/20171028082047_create_mautic_mautic_connections.rb
208
+ - lib/mautic.rb
209
+ - lib/mautic/engine.rb
210
+ - lib/mautic/form_helper.rb
211
+ - lib/mautic/model.rb
212
+ - lib/mautic/proxy.rb
213
+ - lib/mautic/version.rb
214
+ - lib/tasks/mautic_tasks.rake
215
+ - spec/rails_helper.rb
216
+ - spec/spec_helper.rb
217
+ homepage: https://github.com/luk4s/mautic-rails
218
+ licenses:
219
+ - MIT
220
+ metadata: {}
221
+ post_install_message:
222
+ rdoc_options: []
223
+ require_paths:
224
+ - lib
225
+ required_ruby_version: !ruby/object:Gem::Requirement
226
+ requirements:
227
+ - - ">="
228
+ - !ruby/object:Gem::Version
229
+ version: '2.3'
230
+ required_rubygems_version: !ruby/object:Gem::Requirement
231
+ requirements:
232
+ - - ">="
233
+ - !ruby/object:Gem::Version
234
+ version: '0'
235
+ requirements: []
236
+ rubyforge_project:
237
+ rubygems_version: 2.6.13
238
+ signing_key:
239
+ specification_version: 4
240
+ summary: Ruby on Rails Mautic integration
241
+ test_files:
242
+ - spec/rails_helper.rb
243
+ - spec/spec_helper.rb