faalis 0.24.4 → 0.25.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/app/assets/javascripts/faalis/dashboard/modules/auth/group.js +7 -1
- data/app/assets/javascripts/faalis/dashboard/modules/fields/relation.js +50 -3
- data/app/assets/stylesheets/faalis/base.css.scss +16 -1
- data/app/assets/stylesheets/faalis/ltr/foundation_and_overrides.scss +4 -0
- data/app/assets/stylesheets/faalis/rtl/foundation_and_overrides.css.scss +1 -1
- data/app/assets/stylesheets/faalis/variables.css.scss +1 -1
- data/app/controllers/faalis/#api_controller.rb# +144 -0
- data/app/controllers/faalis/api/v1/#conversations_controller.rb# +120 -0
- data/app/controllers/faalis/api/v1/workflows_controller.rb +18 -0
- data/app/controllers/faalis/api_controller.rb +3 -3
- data/app/controllers/faalis/application_controller.rb +0 -1
- data/app/models/faalis/workflow.rb +4 -0
- data/app/views/angularjs_templates/auth/groups/new.html +47 -25
- data/app/views/angularjs_templates/fields/relation/relation.html +9 -2
- data/app/views/devise/sessions/new.html.erb +52 -88
- data/app/views/faalis/api/v1/workflows/index.json.jbuilder +5 -0
- data/app/views/faalis/home/index.html.erb +14 -11
- data/app/views/layouts/faalis/application.html.erb +21 -30
- data/app/views/layouts/faalis/simple.html.erb +37 -0
- data/app/workflows/faalis/administration_workflow.rb +7 -0
- data/config/routes.rb +15 -13
- data/db/migrate/20140413180202_create_faalis_workflows.rb +9 -0
- data/db/seeds.rb +17 -13
- data/lib/faalis.rb +12 -10
- data/lib/faalis/engine.rb +9 -8
- data/lib/faalis/generators/concerns.rb +15 -13
- data/lib/faalis/generators/concerns/bulk.rb +2 -5
- data/lib/faalis/generators/concerns/fieldset.rb +48 -0
- data/lib/faalis/generators/concerns/resource_fields.rb +43 -45
- data/lib/faalis/generators/concerns/where.rb +46 -0
- data/lib/faalis/generators/dashboard_scaffold.rb +6 -5
- data/lib/faalis/version.rb +1 -1
- data/lib/faalis/workflows.rb +7 -0
- data/lib/faalis/workflows/base.rb +69 -0
- data/lib/faalis/workflows/discovery.rb +42 -0
- data/lib/generators/faalis/templates/js/list_view/new.html.erb +7 -5
- data/lib/tasks/sync.rake +10 -0
- data/spec/factories/faalis_workflows.rb +7 -0
- data/spec/models/faalis/workflow_spec.rb +7 -0
- metadata +36 -4
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 57235f4273f0784e21d8a157f8dd9f11b0209f83
|
4
|
+
data.tar.gz: a232ef417944fe7e7573d36cb9c554a510146762
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a5593499cdf0803d93dfe23ea248856ad355d0b05833c31708ec4c81fcbd9914a3d92e80b6c44ada4c32f30bea3eacf3240f10bfb829bae8f6c7a773a6c68802
|
7
|
+
data.tar.gz: a6500a1f72c9fcca90f7936d148c6ea0bb1bdea0c31ab8a7548014297788cac1803438a2d89145d23cf9cc613940c662c374be6266e2f8585b5a35822e789d92
|
@@ -64,6 +64,7 @@ Group.controller("AddGroupController", ["Restangular", "$scope", "$location", "$
|
|
64
64
|
$scope.permissions = [];
|
65
65
|
$scope.editing = false;
|
66
66
|
|
67
|
+
|
67
68
|
$scope.$on("update_perms", function(event) {
|
68
69
|
var sel_perms = [];
|
69
70
|
event.targetScope.selected_perms.forEach(function(perm){
|
@@ -93,8 +94,13 @@ Group.controller("AddGroupController", ["Restangular", "$scope", "$location", "$
|
|
93
94
|
.catch(catch_error);
|
94
95
|
}
|
95
96
|
|
97
|
+
API.all('workflows').getList()
|
98
|
+
.then(function(data){
|
99
|
+
$scope.workflows = data;
|
100
|
+
})
|
101
|
+
.catch(catch_error);
|
96
102
|
|
97
|
-
|
103
|
+
API.all('permissions').getList()
|
98
104
|
.then(function(data){
|
99
105
|
$scope.permissions = data;
|
100
106
|
$scope.$emit("update_perms");
|
@@ -8,6 +8,7 @@ Relation.directive('relationField', ["$filter", "gettext", "Restangular", "catch
|
|
8
8
|
|
9
9
|
function link(scope, element, attrs){
|
10
10
|
var ltr = is_ltr();
|
11
|
+
|
11
12
|
scope.element_id = "id_" + scope.field.name;
|
12
13
|
scope.msg_element_id = "id_" + scope.field.name + "_msg";
|
13
14
|
scope.show_help_btn = false;
|
@@ -34,9 +35,27 @@ Relation.directive('relationField', ["$filter", "gettext", "Restangular", "catch
|
|
34
35
|
return false;
|
35
36
|
}
|
36
37
|
};
|
38
|
+
scope.show_buttons = function() {
|
39
|
+
if ("hide_buttons" in scope.field) {
|
40
|
+
return !scope.field.hide_buttons;
|
41
|
+
}
|
42
|
+
return true;
|
43
|
+
};
|
37
44
|
|
38
|
-
|
39
|
-
|
45
|
+
// Update current field collection list by execute a query on remote end.
|
46
|
+
// if `force` param had non undefined value this method will skip `parent_id`
|
47
|
+
// check
|
48
|
+
scope.update_collection = function(force){
|
49
|
+
if ("parent_id" in scope.field) {
|
50
|
+
if ((scope.field.parent_id === undefined) && (force === undefined)) {
|
51
|
+
return;
|
52
|
+
}
|
53
|
+
}
|
54
|
+
var to = scope.field.to;
|
55
|
+
if (typeof(scope.field.to) === "function") {
|
56
|
+
to = scope.field.to();
|
57
|
+
}
|
58
|
+
var list_object = API.all(to);
|
40
59
|
if ("list_object" in scope.options) {
|
41
60
|
list_object = scope.options.list_object;
|
42
61
|
}
|
@@ -52,7 +71,17 @@ Relation.directive('relationField', ["$filter", "gettext", "Restangular", "catch
|
|
52
71
|
|
53
72
|
scope.update_collection();
|
54
73
|
}
|
55
|
-
|
74
|
+
else {
|
75
|
+
// Add support for `multiple` key in fields with `in` type
|
76
|
+
scope.multiple = function(){
|
77
|
+
if ("multiple" in scope.field) {
|
78
|
+
if (scope.field.multiple !== undefined) {
|
79
|
+
return scope.field.multiple;
|
80
|
+
}
|
81
|
+
}
|
82
|
+
return false;
|
83
|
+
};
|
84
|
+
}
|
56
85
|
// Populate model with new data
|
57
86
|
function update_model_data(){
|
58
87
|
var new_val = $("#" + scope.element_id).val();
|
@@ -75,6 +104,20 @@ Relation.directive('relationField', ["$filter", "gettext", "Restangular", "catch
|
|
75
104
|
}
|
76
105
|
};
|
77
106
|
update_model_data();
|
107
|
+
|
108
|
+
// User can provide a variable to watch on. In case of
|
109
|
+
// any change below function will run. for example:
|
110
|
+
// suppose your select box should update by using value
|
111
|
+
// of other fields. you can add `update-on-change="OtherVar"
|
112
|
+
// to your relation-field.
|
113
|
+
// Remember that in that case you should specify a function
|
114
|
+
// for `to` field of `field-data` which should return a url
|
115
|
+
// of destination resource.
|
116
|
+
scope.$watch('update_on_change', function(newv) {
|
117
|
+
if (newv !== undefined) {
|
118
|
+
scope.update_collection(true);
|
119
|
+
}
|
120
|
+
});
|
78
121
|
}
|
79
122
|
// Actual object of <relation-field> directive
|
80
123
|
return {
|
@@ -108,6 +151,10 @@ Relation.directive('relationField', ["$filter", "gettext", "Restangular", "catch
|
|
108
151
|
// relation field data
|
109
152
|
field: '=fieldData',
|
110
153
|
|
154
|
+
// A variable to watch. in case of change current field
|
155
|
+
// collection will update.
|
156
|
+
update_on_change: '=updateOnChange',
|
157
|
+
|
111
158
|
// Actual Angularjs ng-model
|
112
159
|
model: '='
|
113
160
|
},
|
@@ -49,7 +49,14 @@ footer {
|
|
49
49
|
min-height:60px;
|
50
50
|
position:relative;
|
51
51
|
margin: 0 auto;
|
52
|
-
color
|
52
|
+
color: $lightgray;
|
53
|
+
|
54
|
+
&.plain {
|
55
|
+
background: none;
|
56
|
+
div {
|
57
|
+
padding: 5px;
|
58
|
+
}
|
59
|
+
}
|
53
60
|
|
54
61
|
div{
|
55
62
|
padding:20px 60px;
|
@@ -316,3 +323,11 @@ small.error {
|
|
316
323
|
font-size: 0.6em;
|
317
324
|
color: $lightgray;
|
318
325
|
}
|
326
|
+
|
327
|
+
.single-form {
|
328
|
+
padding-top: 5em;
|
329
|
+
min-height: 35em;
|
330
|
+
fieldset {
|
331
|
+
height: 20em;
|
332
|
+
}
|
333
|
+
}
|
@@ -1,3 +1,5 @@
|
|
1
|
+
@import "faalis/variables";
|
2
|
+
|
1
3
|
// Foundation by ZURB
|
2
4
|
// foundation.zurb.com
|
3
5
|
// Licensed under MIT Open Source
|
@@ -1250,5 +1252,7 @@ $section-title-bg: #f5f5f5;
|
|
1250
1252
|
$section-border-color: #dfdfdf;
|
1251
1253
|
$topbar-margin-bottom: 0;
|
1252
1254
|
$topbar-breakpoint: emCalc(768px); // Change to 9999px for always mobile layout
|
1255
|
+
$topbar-bg: $headerbg;
|
1256
|
+
$topbar-dropdown-toggle-alpha: 0.4;
|
1253
1257
|
|
1254
1258
|
@import 'foundation';
|
@@ -1255,7 +1255,7 @@ $alert-close-position: emCalc(12px);
|
|
1255
1255
|
$section-function-factor: 5%;
|
1256
1256
|
$section-title-bg: #f5f5f5;
|
1257
1257
|
$section-border-color: #dfdfdf;
|
1258
|
-
$topbar-bg: $
|
1258
|
+
$topbar-bg: $headerbg;
|
1259
1259
|
$topbar-margin-bottom: emCalc(20px);
|
1260
1260
|
$topbar-input-height: emCalc(60px);
|
1261
1261
|
$topbar-transition-speed: 300ms;
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# -----------------------------------------------------------------------------
|
2
|
+
# Faalis - Basic website skel engine
|
3
|
+
# Copyright (C) 2012-2013 Yellowen
|
4
|
+
#
|
5
|
+
# This program is free software; you can redistribute it and/or modify
|
6
|
+
# it under the terms of the GNU General Public License as published by
|
7
|
+
# the Free Software Foundation; either version 2 of the License, or
|
8
|
+
# (at your option) any later version.
|
9
|
+
#
|
10
|
+
# This program is distributed in the hope that it will be useful,
|
11
|
+
# but WITHOUT ANY WARRANTY; without even the implied warranty of
|
12
|
+
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
13
|
+
# GNU General Public License for more details.
|
14
|
+
#
|
15
|
+
# You should have received a copy of the GNU General Public License along
|
16
|
+
# with this program; if not, write to the Free Software Foundation, Inc.,
|
17
|
+
# 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
18
|
+
# -----------------------------------------------------------------------------
|
19
|
+
require_dependency "faalis/api_controller"
|
20
|
+
|
21
|
+
|
22
|
+
# This class is the base class of all API controllers in any **Faalis**
|
23
|
+
# host applications. Each host Rails application should have an `APIController`
|
24
|
+
# which inherit from this class.
|
25
|
+
class Faalis::APIController < Faalis::ApplicationController
|
26
|
+
|
27
|
+
@@allowed_fields = []
|
28
|
+
|
29
|
+
# Only support `json` format
|
30
|
+
respond_to :json
|
31
|
+
|
32
|
+
# Authenticate user before any action take place
|
33
|
+
before_filter :authenticate_filter
|
34
|
+
|
35
|
+
# Check for any presence of filtering query, In querystring and load
|
36
|
+
# resource using them
|
37
|
+
before_filter :load_resource_by_query, :only => [:index]
|
38
|
+
|
39
|
+
|
40
|
+
protect_from_forgery
|
41
|
+
|
42
|
+
# Set csrf cookie after any action
|
43
|
+
after_filter :set_csrf_cookie_for_ng
|
44
|
+
|
45
|
+
# Rescue from any access denied exception raised from cancan and
|
46
|
+
# returns a useful error message in json
|
47
|
+
rescue_from CanCan::AccessDenied do |exception|
|
48
|
+
|
49
|
+
render :status => 403, :json => {
|
50
|
+
:error => _("You don't have access to this page"),
|
51
|
+
:orig_msg => exception.message,
|
52
|
+
:action => exception.action,
|
53
|
+
}
|
54
|
+
end
|
55
|
+
|
56
|
+
def set_csrf_cookie_for_ng
|
57
|
+
cookies['XSRF-TOKEN'] = form_authenticity_token if protect_against_forgery?
|
58
|
+
end
|
59
|
+
|
60
|
+
# User authentication for API services take place here. By default
|
61
|
+
# **Faalis** uses the authentication method of **Devise** to authenticate
|
62
|
+
# access to API service.
|
63
|
+
#
|
64
|
+
# If you want to change authentication method ? just override this method
|
65
|
+
# in you **APIController**
|
66
|
+
def authenticate_filter
|
67
|
+
authenticate_user!
|
68
|
+
end
|
69
|
+
|
70
|
+
# Load resource by using parameters specified in querystring.
|
71
|
+
def load_resource_by_query
|
72
|
+
# If any query string parameter provided and allow fields specified
|
73
|
+
if not request.query_parameters.empty? and not allowed_fields.empty?
|
74
|
+
|
75
|
+
logger.info ("Load resource by query parameters")
|
76
|
+
# Iterate over parameters in query string
|
77
|
+
request.query_parameters.each do |key, value|
|
78
|
+
# each key can be like filename[__querytype]=value
|
79
|
+
# which `querytype` is string that specify the query type scope
|
80
|
+
# to use in model. For example these is a query type scope called
|
81
|
+
# `gt` which mean the mentioned field should be greater than the
|
82
|
+
# value
|
83
|
+
field, query_type = key.split("__")
|
84
|
+
|
85
|
+
if allowed_fields.include? field
|
86
|
+
# If field name is in the allowed list
|
87
|
+
# If no query type specified we will use assignment scope.
|
88
|
+
if query_type.nil?
|
89
|
+
query_type = "assignment"
|
90
|
+
end
|
91
|
+
|
92
|
+
# If model have an scope with the "#{query_type}_query" name.
|
93
|
+
# Otherwise skip
|
94
|
+
if model_class.respond_to? "#{query_type}_query"
|
95
|
+
|
96
|
+
# If resource already loaded. If there was a instnace variable
|
97
|
+
# with the plural name of the resource exists then resource
|
98
|
+
# already loaded and we should chain new conditions
|
99
|
+
if instance_variable_defined? "@#{controller_name}"
|
100
|
+
instance_variable_get("@#{controller_name}").send("#{query_type}_query".to_sym, field, value)
|
101
|
+
else
|
102
|
+
# Resource did not loaded we make first query
|
103
|
+
# (without touching database) and set the corresponding
|
104
|
+
# instance variables
|
105
|
+
relation_object = model_class.send("#{query_type}_query".to_sym, field, value)
|
106
|
+
instance_variable_set("@#{controller_name}", relation_object)
|
107
|
+
end
|
108
|
+
|
109
|
+
else
|
110
|
+
logger.info "There is no `#{query_type}_query` in `#{model_class.to_s}` model."
|
111
|
+
end
|
112
|
+
else
|
113
|
+
logger.warn "`#{field}` in not in allowed list for `#{self.class.to_s}`."
|
114
|
+
end
|
115
|
+
end
|
116
|
+
else
|
117
|
+
logger.info("Load resource using `load_resource`")
|
118
|
+
#self.class.load_resource
|
119
|
+
end
|
120
|
+
end
|
121
|
+
|
122
|
+
# An array of allowed fields for query loading
|
123
|
+
def allowed_fields
|
124
|
+
@@allowed_fields
|
125
|
+
end
|
126
|
+
|
127
|
+
# Using this query you can activate the query loading system
|
128
|
+
# and specify fields which you want to use in query loading
|
129
|
+
def self.allow_query_on(*args)
|
130
|
+
@@allowed_fields = args.to_a.collect { |x| x.to_s }
|
131
|
+
end
|
132
|
+
|
133
|
+
protected
|
134
|
+
|
135
|
+
# Model class related to this controller.
|
136
|
+
def model_class
|
137
|
+
controller_name.singularize.classify.constantize
|
138
|
+
end
|
139
|
+
|
140
|
+
def verified_request?
|
141
|
+
super || form_authenticity_token == request.headers['X-XSRF-TOKEN']
|
142
|
+
end
|
143
|
+
|
144
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require_dependency 'faalis/application_controller'
|
2
|
+
|
3
|
+
module Faalis
|
4
|
+
class API::V1::ConversationsController < ::APIController
|
5
|
+
before_filter :authenticate_user!
|
6
|
+
helper_method :mailbox, :conversation
|
7
|
+
|
8
|
+
def create
|
9
|
+
#binding.pry
|
10
|
+
recipient_emails = message_params(:recipients).split(',')
|
11
|
+
recipients = User.where(email: recipient_emails).all
|
12
|
+
|
13
|
+
@conversation = current_user.
|
14
|
+
send_message(recipients, *message_params(:body, :subject)).conversation
|
15
|
+
|
16
|
+
respond_with(@conversation)
|
17
|
+
end
|
18
|
+
|
19
|
+
def reply
|
20
|
+
@conversation = current_user.reply_to_conversation(conversation, *message_params(:body, :subject))
|
21
|
+
respond_with(@conversation)
|
22
|
+
end
|
23
|
+
|
24
|
+
def trash
|
25
|
+
ids = params[:id].split(",")
|
26
|
+
ids.each do |id|
|
27
|
+
conversation = current_user.mailbox.conversations.find(params[:id])
|
28
|
+
conversation.move_to_trash(current_user)
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
def untrash
|
33
|
+
ids = params[:id].split(",")
|
34
|
+
ids.each do |id|
|
35
|
+
conversation = current_user.mailbox.conversations.find(params[:id])
|
36
|
+
conversation.untrash(current_user)
|
37
|
+
end
|
38
|
+
|
39
|
+
end
|
40
|
+
|
41
|
+
def destroy
|
42
|
+
ids = params[:id].split(",")
|
43
|
+
ids.each do |id|
|
44
|
+
conversation = current_user.mailbox.conversations.find(params[:id])
|
45
|
+
current_user.mark_as_deleted conversation
|
46
|
+
#conversation.mark_as_deleted(current_user)
|
47
|
+
end
|
48
|
+
respond_with
|
49
|
+
end
|
50
|
+
|
51
|
+
def index
|
52
|
+
if params[:box] == "inbox"
|
53
|
+
box = "inbox"
|
54
|
+
elsif params[:box] == "sentbox"
|
55
|
+
box = "sentbox"
|
56
|
+
elsif params[:box] == "trash"
|
57
|
+
box = "trash"
|
58
|
+
else
|
59
|
+
respond_to do |f|
|
60
|
+
f.any { head :not_found }
|
61
|
+
end
|
62
|
+
returng
|
63
|
+
end
|
64
|
+
# puts current_user.mailbox.sent.to_json
|
65
|
+
@mailbox ||= current_user.mailbox.send(box.to_sym)
|
66
|
+
respond_with @mailbox
|
67
|
+
end
|
68
|
+
|
69
|
+
|
70
|
+
def show
|
71
|
+
#def conversation
|
72
|
+
@conversations ||= current_user.mailbox.conversations.find(params[:id])
|
73
|
+
@current_user = current_user
|
74
|
+
# @conversation = {}
|
75
|
+
# conversations.each do |conversation|
|
76
|
+
# tmp = {
|
77
|
+
# :receipts => conversation.receipts,
|
78
|
+
# :body => conversation.messages.body,
|
79
|
+
# :is_read => conversation.is_read,
|
80
|
+
# :trashed => conversation.trashed,
|
81
|
+
# :deleted => conversation.deleted,
|
82
|
+
# :recipients => conversation.recipients
|
83
|
+
# }
|
84
|
+
#unless @conversation.include? conversation.id
|
85
|
+
# @conversation[conversation.id] = tmp
|
86
|
+
#end
|
87
|
+
#end
|
88
|
+
|
89
|
+
end
|
90
|
+
private
|
91
|
+
|
92
|
+
def mailbox
|
93
|
+
@mailbox ||= current_user.mailbox
|
94
|
+
end
|
95
|
+
|
96
|
+
|
97
|
+
def conversation
|
98
|
+
@conversation ||= mailbox.conversations.find(params[:id])
|
99
|
+
end
|
100
|
+
|
101
|
+
def conversation_params(*keys)
|
102
|
+
fetch_params(:conversation, *keys)
|
103
|
+
end
|
104
|
+
|
105
|
+
def message_params(*keys)
|
106
|
+
fetch_params(:message, *keys)
|
107
|
+
end
|
108
|
+
|
109
|
+
def fetch_params(key, *subkeys)
|
110
|
+
params[key].instance_eval do
|
111
|
+
case subkeys.size
|
112
|
+
when 0 then self
|
113
|
+
when 1 then
|
114
|
+
self[subkeys.first]
|
115
|
+
else subkeys.map{|k| self[k] }
|
116
|
+
end
|
117
|
+
end
|
118
|
+
end
|
119
|
+
end
|
120
|
+
end
|