graph_starter 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +3 -0
- data/Rakefile +38 -0
- data/app/assets/images/missing.png +0 -0
- data/app/assets/javascripts/graph_starter/application.coffee.erb +25 -0
- data/app/assets/javascripts/graph_starter/ember-template-compiler.js +22130 -0
- data/app/assets/javascripts/graph_starter/ember.js +52794 -0
- data/app/assets/javascripts/graph_starter/ember_apps/permissions_modal.coffee +146 -0
- data/app/assets/javascripts/graph_starter/ember_apps/user_list_dropdown.coffee +22 -0
- data/app/assets/javascripts/graph_starter/jquery-ui.min.js +7 -0
- data/app/assets/javascripts/graph_starter/underscore.js +1548 -0
- data/app/assets/stylesheets/graph_starter/application.scss +9 -0
- data/app/controllers/graph_starter/application_controller.rb +11 -0
- data/app/controllers/graph_starter/assets_controller.rb +76 -0
- data/app/controllers/graph_starter/authorizables_controller.rb +47 -0
- data/app/controllers/graph_starter/categories_controller.rb +79 -0
- data/app/controllers/graph_starter/groups_controller.rb +85 -0
- data/app/controllers/graph_starter/models_controller.rb +11 -0
- data/app/controllers/graph_starter/properties_controller.rb +11 -0
- data/app/helpers/graph_starter/application_helper.rb +11 -0
- data/app/models/concerns/graph_starter/authorizable.rb +30 -0
- data/app/models/graph_starter/asset.rb +161 -0
- data/app/models/graph_starter/can_access.rb +14 -0
- data/app/models/graph_starter/category.rb +24 -0
- data/app/models/graph_starter/group.rb +36 -0
- data/app/models/graph_starter/image.rb +16 -0
- data/app/models/graph_starter/model.rb +23 -0
- data/app/models/graph_starter/property.rb +19 -0
- data/app/models/graph_starter/view.rb +30 -0
- data/app/views/graph_starter/assets/TODO.md +7 -0
- data/app/views/graph_starter/assets/_cards.html.slim +34 -0
- data/app/views/graph_starter/assets/edit.html.slim +24 -0
- data/app/views/graph_starter/assets/home.html.slim +1 -0
- data/app/views/graph_starter/assets/index.html.slim +28 -0
- data/app/views/graph_starter/assets/show.html.slim +98 -0
- data/app/views/graph_starter/authorizables/show.json.jbuilder +20 -0
- data/app/views/graph_starter/authorizables/user_and_group_search.json.jbuilder +8 -0
- data/app/views/graph_starter/categories/show.html.slim +9 -0
- data/app/views/graph_starter/groups/_form.html.slim +30 -0
- data/app/views/graph_starter/groups/_list.html.slim +19 -0
- data/app/views/graph_starter/groups/edit.html.slim +8 -0
- data/app/views/graph_starter/groups/index.html.slim +18 -0
- data/app/views/graph_starter/groups/index.json.jbuilder +4 -0
- data/app/views/graph_starter/groups/new.html.slim +5 -0
- data/app/views/graph_starter/groups/show.html.slim +16 -0
- data/app/views/graph_starter/groups/show.json.jbuilder +1 -0
- data/app/views/graph_starter/models/index.html.slim +10 -0
- data/app/views/graph_starter/properties/_property.html.slim +34 -0
- data/app/views/layouts/graph_starter/_change_permissions_modal.html.slim +90 -0
- data/app/views/layouts/graph_starter/_custom_menu.html.slim +0 -0
- data/app/views/layouts/graph_starter/_menu.html.slim +7 -0
- data/app/views/layouts/graph_starter/_twitter_meta_tags.html.slim +17 -0
- data/app/views/layouts/graph_starter/application.html.slim +45 -0
- data/app/views/layouts/graph_starter/custom_menu.html.slim +0 -0
- data/config/routes.rb +24 -0
- data/lib/graph_starter.rb +4 -0
- data/lib/graph_starter/engine.rb +25 -0
- data/lib/graph_starter/query_authorizer.rb +81 -0
- data/lib/graph_starter/version.rb +3 -0
- data/lib/tasks/graph_starter_tasks.rake +4 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/controllers/application_controller.rb +5 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/bin/setup +29 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +26 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +41 -0
- data/test/dummy/config/environments/production.rb +79 -0
- data/test/dummy/config/environments/test.rb +42 -0
- data/test/dummy/config/initializers/assets.rb +11 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +4 -0
- data/test/dummy/config/secrets.yml +22 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/graph_starter_test.rb +7 -0
- data/test/integration/navigation_test.rb +10 -0
- data/test/test_helper.rb +20 -0
- metadata +262 -0
@@ -0,0 +1,19 @@
|
|
1
|
+
- groups.each do |group|
|
2
|
+
tr
|
3
|
+
td style="padding-left: #{indentation * 10}px"
|
4
|
+
i.caret.right.icon
|
5
|
+
= group.name
|
6
|
+
td = link_to action: :new, parent_id: group do
|
7
|
+
i.sitemap.large.icon
|
8
|
+
| Create Subgroup
|
9
|
+
td = link_to group do
|
10
|
+
i.unhide.large.icon
|
11
|
+
| Show
|
12
|
+
td = link_to edit_group_path(group) do
|
13
|
+
i.edit.large.icon
|
14
|
+
| Edit
|
15
|
+
td = link_to group, data: {:confirm => 'Are you sure?'}, :method => :delete do
|
16
|
+
i.remove.large.icon
|
17
|
+
| Delete
|
18
|
+
|
19
|
+
= render partial: 'list', locals: {groups: group.sub_groups, indentation: indentation + 1}
|
@@ -0,0 +1,16 @@
|
|
1
|
+
p#notice = notice
|
2
|
+
|
3
|
+
h1 Viewing group:
|
4
|
+
|
5
|
+
h2 = @group.name
|
6
|
+
|
7
|
+
h3 Members
|
8
|
+
|
9
|
+
.ui.segments
|
10
|
+
- @group.members.each do |member|
|
11
|
+
.ui.segment
|
12
|
+
= link_to member.name, member
|
13
|
+
|
14
|
+
= link_to 'Edit', edit_group_path(@group)
|
15
|
+
'|
|
16
|
+
= link_to 'Back', groups_path
|
@@ -0,0 +1 @@
|
|
1
|
+
json.extract! @group, :id, :created_at, :updated_at
|
@@ -0,0 +1,34 @@
|
|
1
|
+
- can_write = (level == 'write') && !form.nil?
|
2
|
+
|
3
|
+
- ruby_type = property.ruby_type.to_s
|
4
|
+
- case ruby_type
|
5
|
+
- when 'Integer'
|
6
|
+
- if can_write
|
7
|
+
= form.number_field property.name
|
8
|
+
- else
|
9
|
+
= asset.read_attribute(property.name)
|
10
|
+
- when 'DateTime', 'Date'
|
11
|
+
- if can_write
|
12
|
+
i.calendar.icon
|
13
|
+
- if ruby_type == 'DateTime'
|
14
|
+
i.wait.icon
|
15
|
+
- strftime_format = {'DateTime' => '%Y-%m-%d %H:%M', 'Date' => '%Y-%m-%d'}[ruby_type]
|
16
|
+
|
17
|
+
- id = SecureRandom.uuid
|
18
|
+
- value = asset.read_attribute(property.name)
|
19
|
+
|
20
|
+
= form.text_field property.name, value: value && value.strftime(strftime_format), id: id
|
21
|
+
|
22
|
+
- js_options = {'DateTime' => "format:'Y-m-d H:i'", 'Date' => "timepicker:false,format:'Y-m-d'"}[ruby_type]
|
23
|
+
javascript:
|
24
|
+
$(function(){
|
25
|
+
$('##{id}').datetimepicker({#{js_options.html_safe}});
|
26
|
+
});
|
27
|
+
- else
|
28
|
+
= asset.read_attribute(property.name)
|
29
|
+
|
30
|
+
- else
|
31
|
+
- if can_write
|
32
|
+
= form.text_field property.name
|
33
|
+
- else
|
34
|
+
= asset.read_attribute(property.name)
|
@@ -0,0 +1,90 @@
|
|
1
|
+
script type="text/x-handlebars"
|
2
|
+
.ui.modal
|
3
|
+
i.close.icon
|
4
|
+
|
5
|
+
.header Change permissions for {{object.name}}
|
6
|
+
.image.content
|
7
|
+
| {{#if object.image_url}}
|
8
|
+
.ui.medium.image
|
9
|
+
img src="{{object.image_url}}"
|
10
|
+
| {{/if}}
|
11
|
+
|
12
|
+
.description
|
13
|
+
|
14
|
+
<div class="ui search {{if searching 'loading' ''}}">
|
15
|
+
.ui.icon.input
|
16
|
+
| {{input type="text" placeholder="User or group" class="prompt" value=user_and_group_search}}
|
17
|
+
i.add.circle.icon
|
18
|
+
|
19
|
+
<div id="modal-search-results" class="results transition {{if user_and_group_results_present 'visible' ''}}">
|
20
|
+
.ui.two.column.grid
|
21
|
+
- %w(user group).each do |result_type|
|
22
|
+
| {{#if #{result_type}_results.length}}
|
23
|
+
.eight.wide.column
|
24
|
+
table.ui.celled.striped.table
|
25
|
+
tr
|
26
|
+
th colspan="3"
|
27
|
+
h2
|
28
|
+
i class="#{result_type == 'user' ? 'user' : 'users'} icon"
|
29
|
+
= result_type.humanize.pluralize
|
30
|
+
| {{#each #{result_type}_results as |result|}}
|
31
|
+
tr
|
32
|
+
td
|
33
|
+
| {{result.name}}
|
34
|
+
td
|
35
|
+
.ui.large.buttons
|
36
|
+
<button {{action "add_#{result_type}" result 'read'}} class="ui button green icon">Read</button>
|
37
|
+
.or
|
38
|
+
<button {{action "add_#{result_type}" result 'write'}} class="ui button red icon">Write</button>
|
39
|
+
| {{/each}}
|
40
|
+
| {{/if}}
|
41
|
+
|
42
|
+
</div>
|
43
|
+
</div>
|
44
|
+
|
45
|
+
.inline.field
|
46
|
+
.ui.checkbox
|
47
|
+
| {{input type="checkbox" name="private" checked=object.private}}
|
48
|
+
label Private?
|
49
|
+
- %w(user group).each do |entity_type|
|
50
|
+
| {{#if #{entity_type}_permissions}}
|
51
|
+
.ui.segment.padded
|
52
|
+
.ui.form
|
53
|
+
.ui.header
|
54
|
+
i class="#{entity_type == 'user' ? 'user' : 'users'} icon"
|
55
|
+
| #{entity_type.humanize.pluralize} allowed access
|
56
|
+
table.ui.definition.table
|
57
|
+
| {{#each object.#{entity_type}_permissions as |#{entity_type}_permission|}}
|
58
|
+
tr
|
59
|
+
td
|
60
|
+
label
|
61
|
+
| {{#{entity_type}_permission.#{entity_type}.name}}
|
62
|
+
td
|
63
|
+
.field
|
64
|
+
.ui.radio.checkbox
|
65
|
+
| {{ radio-button name=#{entity_type}_permission.#{entity_type}.id value='read' groupValue=#{entity_type}_permission.level }}
|
66
|
+
label Read
|
67
|
+
td
|
68
|
+
.field
|
69
|
+
.ui.radio.checkbox
|
70
|
+
| {{ radio-button name=#{entity_type}_permission.#{entity_type}.id value='write' groupValue=#{entity_type}_permission.level }}
|
71
|
+
label Write
|
72
|
+
td
|
73
|
+
<button {{action "remove_#{entity_type}_permission" #{entity_type}_permission}} class="ui circular red button"><i class="remove user icon"/></button>
|
74
|
+
| {{/each}}
|
75
|
+
|
76
|
+
| {{/if}}
|
77
|
+
|
78
|
+
<button {{action "update_permissions" target="controller"}} class="ui button positive icon {{if saving 'loading' ''}}"><i class="write icon"/>Update Permissions</button>
|
79
|
+
|
80
|
+
.actions
|
81
|
+
.ui.positive.right.labeled.icon.button.done
|
82
|
+
| Done
|
83
|
+
i.checkmark.icon
|
84
|
+
|
85
|
+
scss:
|
86
|
+
.ui.search > #modal-search-results {
|
87
|
+
width: 600px;
|
88
|
+
padding: 1em;
|
89
|
+
}
|
90
|
+
|
File without changes
|
@@ -0,0 +1,7 @@
|
|
1
|
+
#menu.ui.secondary.pointing.menu
|
2
|
+
- GraphStarter::Asset.descendants.each do |model_class|
|
3
|
+
- path = graph_starter.assets_path(model_slug: model_class.model_slug)
|
4
|
+
= link_to model_class.name.pluralize.humanize, path, class: "item #{'active' if request.path.match(/^#{path}/)}"
|
5
|
+
|
6
|
+
- engine_view do
|
7
|
+
= render 'layouts/graph_starter/custom_menu'
|
@@ -0,0 +1,17 @@
|
|
1
|
+
meta name="twitter:site" content="@neo4j"
|
2
|
+
meta name="twitter:domain" content="http://neo4j.org"
|
3
|
+
|
4
|
+
- if @asset
|
5
|
+
/- if @asset.creators.count > 0
|
6
|
+
/ - # Is this the right one? Difference between uploader and author
|
7
|
+
/ meta name="twitter:creator" content="@#{@asset.creators[0].twitter_username}"
|
8
|
+
|
9
|
+
- if @asset.title
|
10
|
+
meta name="twitter:title" content="#{@asset.title}"
|
11
|
+
|
12
|
+
meta name="twitter:description" content="#{@asset.summary}"
|
13
|
+
|
14
|
+
- if @asset.class.has_images?
|
15
|
+
meta name="twitter:card" content="#{(@asset.first_image_source ? 'summary_large_image' : 'summary')}"
|
16
|
+
|
17
|
+
meta name="twitter:image:src" content="#{@asset.first_image_source ? @asset.first_image_source.url : '/assets/img/neo4j/neo4j-logo.png'}"
|
@@ -0,0 +1,45 @@
|
|
1
|
+
doctype html
|
2
|
+
|
3
|
+
html
|
4
|
+
|
5
|
+
head
|
6
|
+
title AssetPortal
|
7
|
+
= stylesheet_link_tag 'graph_starter/application', media: 'all', 'data-turbolinks-track' => true
|
8
|
+
|
9
|
+
= render partial: 'layouts/graph_starter/twitter_meta_tags'
|
10
|
+
|
11
|
+
- if defined?(current_user)
|
12
|
+
javascript:
|
13
|
+
var current_user = #{current_user.to_json.html_safe}.user;
|
14
|
+
|
15
|
+
= javascript_include_tag 'graph_starter/application', 'data-turbolinks-track' => true
|
16
|
+
|
17
|
+
- controller = params[:controller].to_sym
|
18
|
+
|
19
|
+
- if ![:users, :groups].include?(controller)
|
20
|
+
= javascript_include_tag 'ember_apps/permissions_modal', 'data-turbolinks-track' => true
|
21
|
+
|
22
|
+
= csrf_meta_tags
|
23
|
+
|
24
|
+
body
|
25
|
+
|
26
|
+
= render partial: 'layouts/graph_starter/menu'
|
27
|
+
|
28
|
+
- if notice.present?
|
29
|
+
p.ui.green.message
|
30
|
+
i.close.icon
|
31
|
+
= notice
|
32
|
+
- if alert.present?
|
33
|
+
p.ui.yellow.message
|
34
|
+
i.close.icon
|
35
|
+
= alert
|
36
|
+
|
37
|
+
.ui.container
|
38
|
+
|
39
|
+
= yield
|
40
|
+
|
41
|
+
- if @current_user_is_admin
|
42
|
+
#change-object-permissions
|
43
|
+
|
44
|
+
- if !request.env['HTTP_X_XHR_REFERER']
|
45
|
+
= render partial: 'layouts/graph_starter/change_permissions_modal', locals: {asset: @asset}
|
File without changes
|
data/config/routes.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
GraphStarter::Engine.routes.draw do
|
2
|
+
resources :groups
|
3
|
+
root 'assets#home'
|
4
|
+
|
5
|
+
resources :categories
|
6
|
+
resources :groups do
|
7
|
+
member do
|
8
|
+
get :users_to_add
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
get 'models' => 'models#index', as: :models
|
13
|
+
get 'models/:name' => 'models#show', as: :model
|
14
|
+
|
15
|
+
get 'authorizables/user_and_group_search.json' => 'authorizables#user_and_group_search'
|
16
|
+
get 'authorizables/:model_slug/:id.:format' => 'authorizables#show'
|
17
|
+
put 'authorizables/:model_slug/:id.:format' => 'authorizables#update'
|
18
|
+
|
19
|
+
get ':model_slug' => 'assets#index', as: :assets
|
20
|
+
get ':model_slug/:id' => 'assets#show', as: :asset
|
21
|
+
get ':model_slug/:id/edit' => 'assets#edit', as: :edit_asset
|
22
|
+
get ':model_slug/search/:query.json' => 'assets#search', as: :search_assets
|
23
|
+
patch ':model_slug/:id' => 'assets#update'
|
24
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'neo4jrb_paperclip'
|
2
|
+
require 'semantic-ui-sass'
|
3
|
+
|
4
|
+
require 'neo4j-core'
|
5
|
+
require 'neo4j'
|
6
|
+
require 'neo4j/railtie'
|
7
|
+
require 'neo4j/rake_tasks'
|
8
|
+
require 'slim-rails'
|
9
|
+
|
10
|
+
module GraphStarter
|
11
|
+
class Engine < ::Rails::Engine
|
12
|
+
isolate_namespace GraphStarter
|
13
|
+
|
14
|
+
config.autoload_paths << File.expand_path("../../", __FILE__)
|
15
|
+
|
16
|
+
config.neo4j._active_record_destroyed_behavior = true
|
17
|
+
|
18
|
+
config.assets.precompile += %w(
|
19
|
+
missing.png
|
20
|
+
|
21
|
+
ember_apps/permissions_modal.js
|
22
|
+
ember_apps/user_list_dropdown.js
|
23
|
+
)
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module GraphStarter
|
2
|
+
class QueryAuthorizer
|
3
|
+
# Can take:
|
4
|
+
# * a Query
|
5
|
+
# * a Proxy object
|
6
|
+
# * Anything that responds to #query where a `Query` is returned
|
7
|
+
def initialize(query_object)
|
8
|
+
validate_query_object!(query_object)
|
9
|
+
|
10
|
+
@query_object = query_object
|
11
|
+
end
|
12
|
+
|
13
|
+
def authorized_pluck(variable, user)
|
14
|
+
authorized_query(variable, user).pluck(variable)
|
15
|
+
end
|
16
|
+
|
17
|
+
def authorized_query(variables, user)
|
18
|
+
variables = Array(variables)
|
19
|
+
|
20
|
+
result_query = query.with(*variables)
|
21
|
+
|
22
|
+
result_query = authorized_user_query(result_query, user, variables)
|
23
|
+
|
24
|
+
# Collapse 2D array of all possible levels into one column of levels
|
25
|
+
result_query
|
26
|
+
.unwind(level_collection: :level_collections)
|
27
|
+
.unwind(level: :level_collection).break
|
28
|
+
.with(:level, *variables).where_not(level: nil)
|
29
|
+
.with('collect(level) AS levels', *variables)
|
30
|
+
.with("CASE WHEN 'write' IN levels THEN 'write' ELSE 'read' END AS level", *variables)
|
31
|
+
end
|
32
|
+
|
33
|
+
private
|
34
|
+
|
35
|
+
def validate_query_object!(query_object)
|
36
|
+
return if self.class.queryish?(query_object)
|
37
|
+
|
38
|
+
fail ArgumentError, "Expected query_object to be queryish. Was: #{query_object.inspect}"
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.queryish?(query_object)
|
42
|
+
query_object.is_a?(::Neo4j::Core::Query) ||
|
43
|
+
# Working around these two classes for new. They should return `true`
|
44
|
+
# for `respond_to(:query)`
|
45
|
+
query_object.is_a?(::Neo4j::ActiveNode::HasN::AssociationProxy) ||
|
46
|
+
query_object.is_a?(::Neo4j::ActiveNode::Query::QueryProxy) ||
|
47
|
+
query_object.respond_to?(:query)
|
48
|
+
end
|
49
|
+
|
50
|
+
def query
|
51
|
+
if @query_object.is_a?(::Neo4j::Core::Query)
|
52
|
+
@query_object
|
53
|
+
else
|
54
|
+
@query_object.query
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def authorized_user_query(query, user, variables, user_variable = :user)
|
59
|
+
collect_levels_string = variables.flat_map do |variable|
|
60
|
+
["CASE WHEN (user.admin OR #{variable}_created_rel IS NOT NULL) THEN 'write' WHEN NOT(#{variable}.private) THEN 'read' END",
|
61
|
+
"#{variable}_direct_access_rel.level",
|
62
|
+
"#{variable}_indirect_can_access_rel.level"]
|
63
|
+
end.compact.join(', ')
|
64
|
+
|
65
|
+
result_query = variables.flat_map { |v| user_authorization_paths(v, user_variable) }
|
66
|
+
.inject(query.optional_match_nodes(user_variable => user).break) do |result, clause|
|
67
|
+
result.optional_match(clause).break
|
68
|
+
end.with('*')
|
69
|
+
|
70
|
+
result_query
|
71
|
+
.with("collect([#{collect_levels_string}]) AS level_collections", *variables)
|
72
|
+
end
|
73
|
+
|
74
|
+
def user_authorization_paths(variable, user_variable = :user)
|
75
|
+
["#{variable}<-[#{variable}_created_rel:CREATED]-#{user_variable}",
|
76
|
+
"#{variable}<-[#{variable}_direct_access_rel:CAN_ACCESS]-#{user_variable}",
|
77
|
+
"#{variable}<-[#{variable}_indirect_can_access_rel:CAN_ACCESS]-(:Group)<-[:HAS_SUBGROUP*0..5]-(:Group)<-[:BELONGS_TO]-#{user_variable}"]
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
|