underworld 1.0.0
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.
- checksums.yaml +7 -0
- checksums.yaml.gz.sig +0 -0
- data.tar.gz.sig +0 -0
- data/LICENSE +339 -0
- data/Rakefile +48 -0
- data/app/assets/fonts/DroidNaskh-Bold.ttf +0 -0
- data/app/assets/fonts/DroidNaskh-Regular.ttf +0 -0
- data/app/assets/fonts/OpenSans-Bold.ttf +0 -0
- data/app/assets/fonts/OpenSans-BoldItalic.ttf +0 -0
- data/app/assets/fonts/OpenSans-ExtraBold.ttf +0 -0
- data/app/assets/fonts/OpenSans-ExtraBoldItalic.ttf +0 -0
- data/app/assets/fonts/OpenSans-Italic.ttf +0 -0
- data/app/assets/fonts/OpenSans-Light.ttf +0 -0
- data/app/assets/fonts/OpenSans-LightItalic.ttf +0 -0
- data/app/assets/fonts/OpenSans-Regular.ttf +0 -0
- data/app/assets/fonts/OpenSans-Semibold.ttf +0 -0
- data/app/assets/fonts/OpenSans-SemiboldItalic.ttf +0 -0
- data/app/assets/images/underworld/select2-spinner.gif +0 -0
- data/app/assets/images/underworld/select2.png +0 -0
- data/app/assets/images/underworld/select2x2.png +0 -0
- data/app/assets/javascripts/underworld/application.js +16 -0
- data/app/assets/javascripts/underworld/dashboard/application.js.erb +30 -0
- data/app/assets/javascripts/underworld/dashboard/functions.js.erb +50 -0
- data/app/assets/javascripts/underworld/dashboard/init.js.coffee +33 -0
- data/app/assets/javascripts/underworld/dashboard/lib/actions.js +7 -0
- data/app/assets/javascripts/underworld/dashboard/lib/amd.js.coffee.erb +0 -0
- data/app/assets/javascripts/underworld/dashboard/lib/flash_message.js +71 -0
- data/app/assets/javascripts/underworld/dashboard/lib/locale.js +14 -0
- data/app/assets/javascripts/underworld/dashboard/lib/misc.js +5 -0
- data/app/assets/javascripts/underworld/dashboard/lib/string.js +3 -0
- data/app/assets/javascripts/underworld/dashboard/lib/ujs_patches.js.coffee +28 -0
- data/app/assets/javascripts/underworld/dashboard/manifest.js +5 -0
- data/app/assets/javascripts/underworld/dashboard/variables.js.erb +6 -0
- data/app/assets/javascripts/underworld/groups.js +2 -0
- data/app/assets/javascripts/underworld/home.js +2 -0
- data/app/assets/javascripts/underworld/i18n.js +13 -0
- data/app/assets/javascripts/underworld/simple.js +20 -0
- data/app/assets/javascripts/underworld/users.js +2 -0
- data/app/assets/stylesheets/underworld/dashboard/loadindicator.scss +17 -0
- data/app/assets/stylesheets/underworld/dashboard/ltr/application.css +38 -0
- data/app/assets/stylesheets/underworld/dashboard/ltr/base.css.scss +0 -0
- data/app/assets/stylesheets/underworld/dashboard/rtl/application.css +11 -0
- data/app/assets/stylesheets/underworld/dashboard/rtl/base.css.scss +0 -0
- data/app/assets/stylesheets/underworld/dashboard/share.scss +62 -0
- data/app/assets/stylesheets/underworld/dashboard/variables.scss +9 -0
- data/app/assets/stylesheets/underworld/ltr/application.css +0 -0
- data/app/assets/stylesheets/underworld/rtl/application.css +0 -0
- data/app/assets/stylesheets/underworld/simple/ltr/application.css +2 -0
- data/app/assets/stylesheets/underworld/simple/rtl/application.css +2 -0
- data/app/assets/stylesheets/underworld/simple/rtl/rtl.scss +16 -0
- data/app/assets/stylesheets/underworld/simple/share.scss +11 -0
- data/app/controllers/underworld/api_controller.rb +133 -0
- data/app/controllers/underworld/application_controller.rb +16 -0
- data/app/controllers/underworld/assets_controller.rb +6 -0
- data/app/controllers/underworld/dashboard/application_controller.rb +43 -0
- data/app/controllers/underworld/dashboard/groups_controller.rb +85 -0
- data/app/controllers/underworld/dashboard/profile_controller.rb +49 -0
- data/app/controllers/underworld/dashboard/user_messages_controller.rb +27 -0
- data/app/controllers/underworld/dashboard/users_controller.rb +116 -0
- data/app/controllers/underworld/dashboard_controller.rb +16 -0
- data/app/controllers/underworld/omniauth/callbacks_controller.rb +24 -0
- data/app/helpers/underworld/application_helper.rb +4 -0
- data/app/helpers/underworld/dashboard_helper.rb +107 -0
- data/app/helpers/underworld/groups_helper.rb +4 -0
- data/app/helpers/underworld/home_helper.rb +4 -0
- data/app/helpers/underworld/users_helper.rb +8 -0
- data/app/models/application_record.rb +3 -0
- data/app/models/underworld/concerns/assignment.rb +16 -0
- data/app/models/underworld/concerns/user/auth_definitions.rb +87 -0
- data/app/models/underworld/concerns/user/gravatar.rb +11 -0
- data/app/models/underworld/concerns/user/mongoid_fields.rb +81 -0
- data/app/models/underworld/concerns/user/permission.rb +47 -0
- data/app/models/underworld/concerns/user/user_roles.rb +24 -0
- data/app/models/underworld/group.rb +57 -0
- data/app/models/underworld/permission.rb +43 -0
- data/app/models/underworld/user.rb +81 -0
- data/app/models/underworld/user_message.rb +6 -0
- data/app/policies/underworld/admin_only_policy.rb +66 -0
- data/app/policies/underworld/application_policy.rb +55 -0
- data/app/policies/underworld/group_policy.rb +8 -0
- data/app/policies/underworld/user_message_policy.rb +9 -0
- data/app/policies/underworld/user_policy.rb +9 -0
- data/app/views/angular/auth/groups/details.html +2 -0
- data/app/views/angular/auth/groups/index.html +2 -0
- data/app/views/angular/auth/groups/new.html +2 -0
- data/app/views/angular/auth/index.html +30 -0
- data/app/views/angular/auth/profile/edit.html +54 -0
- data/app/views/angular/auth/users/details.html +28 -0
- data/app/views/angular/auth/users/index.html +6 -0
- data/app/views/angular/auth/users/new.html +57 -0
- data/app/views/angular/conversations/details.html +19 -0
- data/app/views/angular/conversations/index.html +13 -0
- data/app/views/angular/conversations/new.html +39 -0
- data/app/views/angular/conversations/show.html +6 -0
- data/app/views/angular/conversations/show_details.html +15 -0
- data/app/views/angular/fields/boolean/boolean.html +4 -0
- data/app/views/angular/fields/control-combo/control-list.html +19 -0
- data/app/views/angular/fields/datetime/datetime.html +10 -0
- data/app/views/angular/fields/datetime/time.html +25 -0
- data/app/views/angular/fields/file/file.html.slim +0 -0
- data/app/views/angular/fields/float/float.html +4 -0
- data/app/views/angular/fields/image/image.html +1 -0
- data/app/views/angular/fields/integer/integer.html +4 -0
- data/app/views/angular/fields/relation/relation.html +44 -0
- data/app/views/angular/fields/string/string.html +22 -0
- data/app/views/angular/fields/tag/tag.html +1 -0
- data/app/views/angular/fields/text/text.html +4 -0
- data/app/views/angular/filter/index.html +15 -0
- data/app/views/angular/index.html +3 -0
- data/app/views/angular/locale/fa.po +25 -0
- data/app/views/angular/locale/templates.pot +24 -0
- data/app/views/angular/logs/index.html +6 -0
- data/app/views/angular/modules.html +0 -0
- data/app/views/angular/nav.html.erb +39 -0
- data/app/views/angular/underworld/views/generic_add_view.html.slim +5 -0
- data/app/views/devise/registrations/edit.html.erb +29 -0
- data/app/views/devise/registrations/new.html.erb +38 -0
- data/app/views/devise/sessions/new.html.erb +37 -0
- data/app/views/devise/shared/_links.erb +19 -0
- data/app/views/devise/shared/_omni_link.erb +33 -0
- data/app/views/kaminari/_first_page.html.slim +3 -0
- data/app/views/kaminari/_gap.html.slim +2 -0
- data/app/views/kaminari/_last_page.html.slim +3 -0
- data/app/views/kaminari/_next_page.html.slim +3 -0
- data/app/views/kaminari/_page.html.slim +3 -0
- data/app/views/kaminari/_paginator.html.slim +13 -0
- data/app/views/kaminari/_prev_page.html.slim +3 -0
- data/app/views/layouts/underworld/application.html.erb +79 -0
- data/app/views/layouts/underworld/dashboard.html.erb +52 -0
- data/app/views/layouts/underworld/simple.html.erb +27 -0
- data/app/views/underworld/api/v1/conversations/create.json.jbuilder +1 -0
- data/app/views/underworld/api/v1/conversations/destroy.json.jbuilder +1 -0
- data/app/views/underworld/api/v1/conversations/index.json.jbuilder +1 -0
- data/app/views/underworld/api/v1/conversations/reply.json.jbuilder +1 -0
- data/app/views/underworld/api/v1/conversations/show.json.jbuilder +14 -0
- data/app/views/underworld/api/v1/conversations/trash.json.jbuilder +1 -0
- data/app/views/underworld/api/v1/groups/create.json.jbuilder +4 -0
- data/app/views/underworld/api/v1/groups/destroy.html.erb +2 -0
- data/app/views/underworld/api/v1/groups/destroy.json.jbuilder +1 -0
- data/app/views/underworld/api/v1/groups/edit.html.erb +2 -0
- data/app/views/underworld/api/v1/groups/index.html.erb +2 -0
- data/app/views/underworld/api/v1/groups/index.json.jbuilder +6 -0
- data/app/views/underworld/api/v1/groups/new.html.erb +2 -0
- data/app/views/underworld/api/v1/groups/show.html.erb +2 -0
- data/app/views/underworld/api/v1/groups/show.json.jbuilder +5 -0
- data/app/views/underworld/api/v1/groups/update.json.jbuilder +4 -0
- data/app/views/underworld/api/v1/logs/index.json.jbuilder +1 -0
- data/app/views/underworld/api/v1/permissions/index.json.jbuilder +1 -0
- data/app/views/underworld/api/v1/permissions/user_permissions.json.jbuilder +1 -0
- data/app/views/underworld/api/v1/users/create.html.erb +2 -0
- data/app/views/underworld/api/v1/users/create.json.jbuilder +1 -0
- data/app/views/underworld/api/v1/users/destroy.json.jbuilder +1 -0
- data/app/views/underworld/api/v1/users/distroy.html.erb +2 -0
- data/app/views/underworld/api/v1/users/edit.html.erb +2 -0
- data/app/views/underworld/api/v1/users/index.html.erb +2 -0
- data/app/views/underworld/api/v1/users/index.json.jbuilder +4 -0
- data/app/views/underworld/api/v1/users/show.html.erb +2 -0
- data/app/views/underworld/api/v1/users/show.json.jbuilder +2 -0
- data/app/views/underworld/api/v1/users/update.json.jbuilder +1 -0
- data/app/views/underworld/dashboard/groups/_form.html.slim +68 -0
- data/app/views/underworld/dashboard/groups/create.js.erb +3 -0
- data/app/views/underworld/dashboard/groups/edit.html.slim +3 -0
- data/app/views/underworld/dashboard/groups/errors.js.erb +3 -0
- data/app/views/underworld/dashboard/groups/index.html.slim +47 -0
- data/app/views/underworld/dashboard/groups/new.html.slim +2 -0
- data/app/views/underworld/dashboard/groups/update.js.erb +3 -0
- data/app/views/underworld/dashboard/index.html.erb +0 -0
- data/app/views/underworld/dashboard/login_required_page.html.erb +5 -0
- data/app/views/underworld/dashboard/not_found.html.slim +9 -0
- data/app/views/underworld/dashboard/not_found.js.erb +1 -0
- data/app/views/underworld/dashboard/profile/_form.html.erb +33 -0
- data/app/views/underworld/dashboard/profile/_profile_form.html.erb +33 -0
- data/app/views/underworld/dashboard/profile/create.js.erb +3 -0
- data/app/views/underworld/dashboard/profile/edit.html.erb +2 -0
- data/app/views/underworld/dashboard/profile/edit_password.html.erb +2 -0
- data/app/views/underworld/dashboard/profile/errors.js.erb +3 -0
- data/app/views/underworld/dashboard/resource/_form.html.slim +33 -0
- data/app/views/underworld/dashboard/resource/_input.html.slim +3 -0
- data/app/views/underworld/dashboard/resource/create.js.erb +3 -0
- data/app/views/underworld/dashboard/resource/destroy.js.erb +2 -0
- data/app/views/underworld/dashboard/resource/edit.html.slim +3 -0
- data/app/views/underworld/dashboard/resource/index.html.slim +60 -0
- data/app/views/underworld/dashboard/resource/new.html.slim +2 -0
- data/app/views/underworld/dashboard/resource/show.html.slim +22 -0
- data/app/views/underworld/dashboard/resource/update.js.erb +3 -0
- data/app/views/underworld/dashboard/shared/_content_header.html.slim +15 -0
- data/app/views/underworld/dashboard/shared/_control_sidebar.html.slim +50 -0
- data/app/views/underworld/dashboard/shared/_footer.html.slim +7 -0
- data/app/views/underworld/dashboard/shared/_header.html.erb +98 -0
- data/app/views/underworld/dashboard/shared/_sidebar.html.erb +69 -0
- data/app/views/underworld/dashboard/shared/_toolbar.html.slim +0 -0
- data/app/views/underworld/dashboard/shared/errors.js.erb +3 -0
- data/app/views/underworld/dashboard/user_messages/_form.html.slim +0 -0
- data/app/views/underworld/dashboard/user_messages/create.js.erb +0 -0
- data/app/views/underworld/dashboard/user_messages/destroy.js.erb +0 -0
- data/app/views/underworld/dashboard/user_messages/new.html.slim +72 -0
- data/app/views/underworld/dashboard/users/_form.html.slim +73 -0
- data/app/views/underworld/dashboard/users/_password_form.html.slim +37 -0
- data/app/views/underworld/dashboard/users/create.js.erb +3 -0
- data/app/views/underworld/dashboard/users/destroy.js.erb +2 -0
- data/app/views/underworld/dashboard/users/edit.html.slim +3 -0
- data/app/views/underworld/dashboard/users/edit_password.html.slim +1 -0
- data/app/views/underworld/dashboard/users/errors.js.erb +3 -0
- data/app/views/underworld/dashboard/users/index.html.slim +66 -0
- data/app/views/underworld/dashboard/users/new.html.slim +2 -0
- data/app/views/underworld/dashboard/users/show.html.slim +6 -0
- data/app/views/underworld/dashboard/users/update.js.erb +3 -0
- data/app/views/underworld/dashboard/users/update_password.js.erb +3 -0
- data/app/views/underworld/relations/_belongs_to.json.jbuilder +11 -0
- data/config/initializers/devise.rb +24 -0
- data/config/locales/devise.en.yml +59 -0
- data/config/locales/devise.fa.yml +58 -0
- data/config/locales/underworld.en.yml +96 -0
- data/config/locales/underworld.fa.yml +108 -0
- data/config/routes.rb +43 -0
- data/config/spring.rb +1 -0
- data/db/migrate/20131013091000_devise_create_underworld_users.rb +65 -0
- data/db/migrate/20131020124701_create_underworld_groups.rb +15 -0
- data/db/migrate/20131021170923_create_underworld_permissions.rb +15 -0
- data/db/migrate/20131123120422_add_permissions_groups_table.rb +16 -0
- data/db/migrate/20140613120923_add_users_groups_table.rb +13 -0
- data/db/migrate/20151122115129_add_uniqueness_index_to_users_groups.rb +11 -0
- data/db/migrate/20160310105736_create_underworld_user_messages.rb +25 -0
- data/db/seeds.rb +49 -0
- data/lib/generators/underworld/install_generator.rb +108 -0
- data/lib/generators/underworld/resource_generator.rb +70 -0
- data/lib/generators/underworld/resources_generator.rb +20 -0
- data/lib/generators/underworld/templates/README +21 -0
- data/lib/generators/underworld/templates/SPECS +13 -0
- data/lib/generators/underworld/templates/api/controller.rb.erb +60 -0
- data/lib/generators/underworld/templates/api_controller.rb +2 -0
- data/lib/generators/underworld/templates/dashboard/controller.rb.erb +2 -0
- data/lib/generators/underworld/templates/dashboard_controller.rb +18 -0
- data/lib/generators/underworld/templates/devise.rb +251 -0
- data/lib/generators/underworld/templates/features/api.feature +92 -0
- data/lib/generators/underworld/templates/features/api.step.rb +7 -0
- data/lib/generators/underworld/templates/features/step_definitions/email_steps.rb +206 -0
- data/lib/generators/underworld/templates/features/support/email_spec.rb +1 -0
- data/lib/generators/underworld/templates/features/support/env.rb +79 -0
- data/lib/generators/underworld/templates/javascripts/application.js +19 -0
- data/lib/generators/underworld/templates/policy/application_policy.rb +2 -0
- data/lib/generators/underworld/templates/policy/policy.rb.erb +2 -0
- data/lib/generators/underworld/templates/seeds.rb +1 -0
- data/lib/generators/underworld/templates/spec/factories/groups.rb +5 -0
- data/lib/generators/underworld/templates/spec/factories/users.rb +10 -0
- data/lib/generators/underworld/templates/spec/spec_helper.rb +69 -0
- data/lib/generators/underworld/templates/spec/support/devise.rb +3 -0
- data/lib/generators/underworld/templates/stylesheets/dashboard/ltr/application.css +3 -0
- data/lib/generators/underworld/templates/stylesheets/dashboard/rtl/application.css +3 -0
- data/lib/generators/underworld/templates/stylesheets/ltr/application.css +3 -0
- data/lib/generators/underworld/templates/stylesheets/rtl/application.css +3 -0
- data/lib/generators/underworld/templates/underworld.rb +12 -0
- data/lib/generators/underworld/templates/views/create.json.jbuilder.erb +3 -0
- data/lib/generators/underworld/templates/views/destroy.json.jbuilder.erb +1 -0
- data/lib/generators/underworld/templates/views/index.json.jbuilder.erb +13 -0
- data/lib/generators/underworld/templates/views/show.json.jbuilder.erb +6 -0
- data/lib/generators/underworld/templates/views/update.json.jbuilder.erb +6 -0
- data/lib/generators/underworld/views_generator.rb +57 -0
- data/lib/tasks/docs.rake +6 -0
- data/lib/tasks/sync.rake +9 -0
- data/lib/tasks/underworld_tasks.rake +38 -0
- data/lib/underworld.rb +35 -0
- data/lib/underworld/action_dispatch.rb +3 -0
- data/lib/underworld/api.rb +42 -0
- data/lib/underworld/api/groups_api.rb +41 -0
- data/lib/underworld/api/permissions_api.rb +24 -0
- data/lib/underworld/api/root.rb +15 -0
- data/lib/underworld/api/users_api.rb +20 -0
- data/lib/underworld/concerns.rb +7 -0
- data/lib/underworld/concerns/authorizable.rb +81 -0
- data/lib/underworld/configuration.rb +97 -0
- data/lib/underworld/dashboard.rb +11 -0
- data/lib/underworld/dashboard/dsl.rb +41 -0
- data/lib/underworld/dashboard/dsl/base.rb +133 -0
- data/lib/underworld/dashboard/dsl/create.rb +116 -0
- data/lib/underworld/dashboard/dsl/form_fields_properties.rb +62 -0
- data/lib/underworld/dashboard/dsl/index.rb +45 -0
- data/lib/underworld/dashboard/dsl/show.rb +6 -0
- data/lib/underworld/dashboard/dsl/update.rb +6 -0
- data/lib/underworld/dashboard/helpers/box_helpers.rb +129 -0
- data/lib/underworld/dashboard/models.rb +2 -0
- data/lib/underworld/dashboard/models/sidebar.rb +139 -0
- data/lib/underworld/dashboard/sections.rb +2 -0
- data/lib/underworld/dashboard/sections/resource.rb +229 -0
- data/lib/underworld/dashboard/sections/resource_create.rb +263 -0
- data/lib/underworld/dashboard/sections/resource_destroy.rb +27 -0
- data/lib/underworld/dashboard/sections/resource_show.rb +96 -0
- data/lib/underworld/dashboard/sections/resources_index.rb +113 -0
- data/lib/underworld/dashboard/sections/sidebar.rb +41 -0
- data/lib/underworld/discovery.rb +8 -0
- data/lib/underworld/discovery/permissions.rb +49 -0
- data/lib/underworld/engine.rb +53 -0
- data/lib/underworld/exceptions.rb +7 -0
- data/lib/underworld/extension.rb +18 -0
- data/lib/underworld/extension/base.rb +29 -0
- data/lib/underworld/fake_assets.rb +5 -0
- data/lib/underworld/i18n.rb +101 -0
- data/lib/underworld/initialize.rb +0 -0
- data/lib/underworld/liquid.rb +7 -0
- data/lib/underworld/liquid/block.rb +14 -0
- data/lib/underworld/liquid/render_tag.rb +50 -0
- data/lib/underworld/liquid/tag.rb +51 -0
- data/lib/underworld/middlewares.rb +1 -0
- data/lib/underworld/middlewares/locale.rb +29 -0
- data/lib/underworld/omniauth.rb +8 -0
- data/lib/underworld/omniauth/callbacks.rb +39 -0
- data/lib/underworld/orm.rb +30 -0
- data/lib/underworld/routes.rb +117 -0
- data/lib/underworld/version.rb +3 -0
- data/test/controllers/underworld/dashboard/resource_test.rb +118 -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/javascripts/dashboard/application.js +19 -0
- data/test/dummy/app/assets/stylesheets/application.css +13 -0
- data/test/dummy/app/assets/stylesheets/dashboard/ltr/application.css +3 -0
- data/test/dummy/app/assets/stylesheets/dashboard/rtl/application.css +3 -0
- data/test/dummy/app/assets/stylesheets/ltr/application.css +3 -0
- data/test/dummy/app/assets/stylesheets/rtl/application.css +3 -0
- data/test/dummy/app/controllers/api_controller.rb +2 -0
- data/test/dummy/app/controllers/application_controller.rb +6 -0
- data/test/dummy/app/controllers/dashboard/application_controller.rb +18 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/policies/application_policy.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/config.ru +4 -0
- data/test/dummy/config/application.rb +23 -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 +29 -0
- data/test/dummy/config/environments/production.rb +80 -0
- data/test/dummy/config/environments/test.rb +38 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/devise.rb +251 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/formstatic.rb +76 -0
- data/test/dummy/config/initializers/formtastic.rb +110 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/kaminari_config.rb +10 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/secret_token.rb +12 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/underworld.rb +11 -0
- data/test/dummy/config/initializers/underworld_assets.rb +1 -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 +13 -0
- data/test/dummy/lib/templates/slim/scaffold/_form.html.slim +8 -0
- data/test/dummy/public/404.html +58 -0
- data/test/dummy/public/422.html +58 -0
- data/test/dummy/public/500.html +57 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/fabricators/underworld/groups.rb +27 -0
- data/test/fabricators/underworld/permissions.rb +14 -0
- data/test/fabricators/underworld/users.rb +17 -0
- data/test/generators/install_generator_test.rb +84 -0
- data/test/integration/underworld/authentication_test.rb +36 -0
- data/test/integration/underworld/dashboard_test.rb +64 -0
- data/test/test_helper.rb +72 -0
- metadata +1045 -0
- metadata.gz.sig +0 -0
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
#require_dependency 'underworld/dashboard/dsl'
|
|
2
|
+
|
|
3
|
+
module Underworld::Dashboard
|
|
4
|
+
autoload :DSL, 'underworld/dashboard/dsl'
|
|
5
|
+
|
|
6
|
+
module Helpers
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
autoload :Models, 'underworld/dashboard/models'
|
|
10
|
+
autoload :Sections, 'underworld/dashboard/sections.rb'
|
|
11
|
+
end
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
require_dependency 'underworld/dashboard/dsl/form_fields_properties'
|
|
2
|
+
require_dependency 'underworld/dashboard/sections/resource'
|
|
3
|
+
require_dependency 'underworld/dashboard/sections/resources_index'
|
|
4
|
+
require_dependency 'underworld/dashboard/sections/resource_create'
|
|
5
|
+
require_dependency 'underworld/dashboard/sections/resource_show'
|
|
6
|
+
require_dependency 'underworld/dashboard/sections/resource_destroy'
|
|
7
|
+
require_dependency 'underworld/dashboard/sections/sidebar'
|
|
8
|
+
|
|
9
|
+
module Underworld::Dashboard
|
|
10
|
+
module DSL
|
|
11
|
+
|
|
12
|
+
extend ActiveSupport::Concern
|
|
13
|
+
|
|
14
|
+
include Underworld::Dashboard::Sections::Resource
|
|
15
|
+
include Underworld::Dashboard::Sections::ResourcesIndex
|
|
16
|
+
include Underworld::Dashboard::Sections::ResourceCreate
|
|
17
|
+
include Underworld::Dashboard::Sections::ResourceShow
|
|
18
|
+
include Underworld::Dashboard::Sections::ResourceDestroy
|
|
19
|
+
include Underworld::Dashboard::Sections::Sidebar
|
|
20
|
+
|
|
21
|
+
attr_accessor :_override_views
|
|
22
|
+
|
|
23
|
+
def _override_views
|
|
24
|
+
@_override_views ||= []
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
module ClassMethods
|
|
28
|
+
# override the default view for given views or the result
|
|
29
|
+
# of the given block, by the one from the application
|
|
30
|
+
def override_views(*views, &block)
|
|
31
|
+
|
|
32
|
+
define_method(:_override_views) do
|
|
33
|
+
result = views || []
|
|
34
|
+
result.concat(block.call) if block_given?
|
|
35
|
+
result
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,133 @@
|
|
|
1
|
+
require 'set'
|
|
2
|
+
|
|
3
|
+
module Underworld::Dashboard::DSL
|
|
4
|
+
class Base
|
|
5
|
+
attr_reader(:action_buttons, :fields, :model, :default_scope,
|
|
6
|
+
:action_buttons)
|
|
7
|
+
|
|
8
|
+
# Base class for all the DSL property classes to be
|
|
9
|
+
# used as the yielded object inside each section DSL
|
|
10
|
+
# scope.
|
|
11
|
+
#
|
|
12
|
+
# For example the below code will yield an instance of
|
|
13
|
+
# one of this class children.
|
|
14
|
+
#
|
|
15
|
+
# in_index do
|
|
16
|
+
# # This will yield an instance of this class child
|
|
17
|
+
# end
|
|
18
|
+
#
|
|
19
|
+
# The most important note in this class is that all the public
|
|
20
|
+
# methods that their name starts with an underscore (_) not meant to
|
|
21
|
+
# be used as DSL.
|
|
22
|
+
def initialize(model)
|
|
23
|
+
@action_buttons = []
|
|
24
|
+
@model = model
|
|
25
|
+
@fields = resolve_model_reflections
|
|
26
|
+
@fields_type = {}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
# Allow user to specify an array of model attributes to be used
|
|
30
|
+
# in respected section. For example attributes to show as header
|
|
31
|
+
# columns in index section
|
|
32
|
+
def attributes(*fields_name, **options, &block)
|
|
33
|
+
if options.include? :except
|
|
34
|
+
@fields = resolve_model_reflections.reject do |field|
|
|
35
|
+
options[:except].include? field.to_sym
|
|
36
|
+
end
|
|
37
|
+
elsif options.include? :append
|
|
38
|
+
|
|
39
|
+
@fields += options[:append]
|
|
40
|
+
else
|
|
41
|
+
|
|
42
|
+
# Check for valid field names
|
|
43
|
+
fields_name.each do |name|
|
|
44
|
+
unless @fields.include? name.to_s
|
|
45
|
+
raise ArgumentError, "can't find '#{name}' field for model '#{model}'."
|
|
46
|
+
end
|
|
47
|
+
end
|
|
48
|
+
# set new value for fields
|
|
49
|
+
@fields = fields_name.map(&:to_s) unless fields_name.empty?
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
@fields.concat(block.call.map(&:to_s)) if block_given?
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
# Return the default scope for current properties object.
|
|
56
|
+
# scope will be used to fetch all the records for the given
|
|
57
|
+
# section. The Default implementation belongs to index section
|
|
58
|
+
# and return a page aware list of all the objects.
|
|
59
|
+
#
|
|
60
|
+
# You can easily change it via the corresponding section dsl.
|
|
61
|
+
# For example in case of `index` section:
|
|
62
|
+
#
|
|
63
|
+
# in_index do |index|
|
|
64
|
+
# index.scope do |params|
|
|
65
|
+
# YourModel.all
|
|
66
|
+
# end
|
|
67
|
+
#
|
|
68
|
+
# # or ...
|
|
69
|
+
#
|
|
70
|
+
# scope :my_scope
|
|
71
|
+
# end
|
|
72
|
+
#
|
|
73
|
+
# private
|
|
74
|
+
# def my_scope
|
|
75
|
+
# YourModel.where(...)
|
|
76
|
+
#
|
|
77
|
+
# end
|
|
78
|
+
#
|
|
79
|
+
# Arguments:
|
|
80
|
+
# * **params**: Is the same params passed to controller action.
|
|
81
|
+
def scope(name = nil, &block)
|
|
82
|
+
return @default_scope = block if block_given?
|
|
83
|
+
@default_scope = name.to_sym
|
|
84
|
+
end
|
|
85
|
+
|
|
86
|
+
# Define a new action on the `action` place of the current section
|
|
87
|
+
# **options**: Is a hash which contains the action button properties.
|
|
88
|
+
#
|
|
89
|
+
# `name`: Label to use with the button.
|
|
90
|
+
# `href`: The link href to use in the `a` tag
|
|
91
|
+
# `class`: classes of the button.
|
|
92
|
+
# `icon_class`: font awesome icon to use in button.
|
|
93
|
+
def action_button(**options)
|
|
94
|
+
@action_buttons ||= []
|
|
95
|
+
@action_buttons << options
|
|
96
|
+
@action_buttons.uniq!
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
private
|
|
100
|
+
|
|
101
|
+
# Replace foreign key names with their reflection names
|
|
102
|
+
# and create an array of model fields
|
|
103
|
+
def resolve_model_reflections
|
|
104
|
+
# TODO: cach the result using and instance_var or something
|
|
105
|
+
reflections = model.reflections
|
|
106
|
+
columns = Set.new model.column_names
|
|
107
|
+
new_fields = Set.new
|
|
108
|
+
fields_to_remove = Set.new
|
|
109
|
+
|
|
110
|
+
reflections.each do |name, column|
|
|
111
|
+
has_many = ActiveRecord::Reflection::HasManyReflection
|
|
112
|
+
has_and_belongs_to_many = ActiveRecord::Reflection::HasAndBelongsToManyReflection
|
|
113
|
+
|
|
114
|
+
if !column.is_a?(has_many) && !column.is_a?(has_and_belongs_to_many)
|
|
115
|
+
new_fields << name.to_s
|
|
116
|
+
fields_to_remove << column.foreign_key.to_s
|
|
117
|
+
end
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
if model.respond_to? :attachment_definitions
|
|
121
|
+
# Paperclip is installed and model contains attachments
|
|
122
|
+
model.attachment_definitions.each do |name, _|
|
|
123
|
+
new_fields << name.to_s
|
|
124
|
+
["file_name", "content_type", "file_size", "updated_at"].each do |x|
|
|
125
|
+
fields_to_remove << "#{name}_#{x}"
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
columns.union(new_fields) - fields_to_remove
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
@@ -0,0 +1,116 @@
|
|
|
1
|
+
require_dependency 'underworld/dashboard/dsl/base'
|
|
2
|
+
|
|
3
|
+
module Underworld::Dashboard::DSL
|
|
4
|
+
class Create < Base
|
|
5
|
+
|
|
6
|
+
def initialize(model)
|
|
7
|
+
super
|
|
8
|
+
@field_details = {}
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
# Specify attributes details, for example if you want to change a datetime
|
|
12
|
+
# field type to string you need to do like this:
|
|
13
|
+
#
|
|
14
|
+
# in_form do
|
|
15
|
+
# attributes_properties date: { as: :string }
|
|
16
|
+
# end
|
|
17
|
+
def attributes_properties(**options)
|
|
18
|
+
options_set = Set.new options.keys.map(&:to_s)
|
|
19
|
+
|
|
20
|
+
unless options_set.subset? Set.new(@fields)
|
|
21
|
+
fail "You have to provide correct attribute names in" +
|
|
22
|
+
"'attributes_properties' for '#{@model}'."
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# TODO: Check for valid value for each key
|
|
26
|
+
@field_details = options || {}
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def _field_details
|
|
30
|
+
details = @field_details.dup
|
|
31
|
+
|
|
32
|
+
fields_for_form.each do |field|
|
|
33
|
+
if !details.include? field.to_sym
|
|
34
|
+
|
|
35
|
+
type = guess_field_type(field)
|
|
36
|
+
details[field.to_sym] = initialize_field(field.to_sym, type)
|
|
37
|
+
end
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
details
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def fields_for_form
|
|
44
|
+
fields_name = @fields.dup
|
|
45
|
+
|
|
46
|
+
fields_name.delete('id')
|
|
47
|
+
fields_name.delete('created_at')
|
|
48
|
+
fields_name.delete('updated_at')
|
|
49
|
+
|
|
50
|
+
fields_name
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
private
|
|
54
|
+
|
|
55
|
+
def guess_field_type(field)
|
|
56
|
+
column = model.columns_hash[field.to_s]
|
|
57
|
+
return column.type.to_sym unless column.nil?
|
|
58
|
+
|
|
59
|
+
return :file if has_attachment? field.to_sym
|
|
60
|
+
|
|
61
|
+
has_many = ActiveRecord::Reflection::HasManyReflection
|
|
62
|
+
has_and_belongs_to_many = ActiveRecord::Reflection::HasAndBelongsToManyReflection
|
|
63
|
+
reflection = model.reflections[field.to_s]
|
|
64
|
+
|
|
65
|
+
unless reflection.nil?
|
|
66
|
+
return nil if reflection.is_a? has_many
|
|
67
|
+
return :multiple_select if reflection.is_a? has_and_belongs_to_many
|
|
68
|
+
:select
|
|
69
|
+
end
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
def has_attachment?(name)
|
|
73
|
+
if model.respond_to? :attachment_definitions
|
|
74
|
+
return true if model.attachment_definitions.keys.include? name
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
false
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def initialize_field(name, type)
|
|
81
|
+
if respond_to?("setup_#{type}".to_sym, true)
|
|
82
|
+
return send("setup_#{type}", name)
|
|
83
|
+
end
|
|
84
|
+
{ as: type.to_sym }
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
def setup_datetime(name)
|
|
88
|
+
{ as: :string,
|
|
89
|
+
input_html: { class: 'datetimepicker' }
|
|
90
|
+
}
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
def setup_time(name)
|
|
94
|
+
{ as: :string,
|
|
95
|
+
input_html: { class: 'timepicker' }
|
|
96
|
+
}
|
|
97
|
+
end
|
|
98
|
+
|
|
99
|
+
def setup_date(name)
|
|
100
|
+
{ as: :string,
|
|
101
|
+
input_html: { class: 'datepicker1' }
|
|
102
|
+
}
|
|
103
|
+
end
|
|
104
|
+
|
|
105
|
+
def setup_multiple_select(name)
|
|
106
|
+
{ as: :select,
|
|
107
|
+
input_html: { class: 'multiple select' }
|
|
108
|
+
}
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
def setup_integer(name)
|
|
112
|
+
{ as: nil }
|
|
113
|
+
end
|
|
114
|
+
|
|
115
|
+
end
|
|
116
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module Underworld::Dashboard
|
|
2
|
+
class FormFieldsProperties
|
|
3
|
+
|
|
4
|
+
def initializer
|
|
5
|
+
@fields = {}
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def fields=(all_fields)
|
|
9
|
+
fields.each do |name, field|
|
|
10
|
+
unless all_fields.include? name.to_s
|
|
11
|
+
raise ArgumentError, "'#{name}' is not a valid field"
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
all_fields.each do |name, field|
|
|
16
|
+
unless fields.include?(name.to_sym)
|
|
17
|
+
|
|
18
|
+
Rails.logger.debug("FIELD NAME: #{name}")
|
|
19
|
+
Rails.logger.debug("FIELD TYPE: #{field.type}")
|
|
20
|
+
|
|
21
|
+
if self.respond_to?("setup_#{field.type}".to_sym, true)
|
|
22
|
+
puts "sdasda" * 100
|
|
23
|
+
send("setup_#{field.type}", name)
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def fields
|
|
30
|
+
@fields || {}
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def []=(name, detail)
|
|
34
|
+
@fields ||= {}
|
|
35
|
+
@fields[name.to_sym] = detail
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def [](index)
|
|
39
|
+
unless @fields.nil?
|
|
40
|
+
return @fields[index] || {}
|
|
41
|
+
end
|
|
42
|
+
{}
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def method_missing(m, *args, &block)
|
|
46
|
+
@fields ||= {}
|
|
47
|
+
if self.respond_to? m
|
|
48
|
+
return self.send(m, *args, &block)
|
|
49
|
+
else
|
|
50
|
+
# FIXME: This probably is a bad idea. We should get the whole
|
|
51
|
+
# args
|
|
52
|
+
@fields[m] = args[0]
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def to_hash
|
|
57
|
+
fields
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
end
|
|
62
|
+
end
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
require_dependency 'underworld/dashboard/dsl/base'
|
|
2
|
+
|
|
3
|
+
module Underworld::Dashboard::DSL
|
|
4
|
+
class Index < Base
|
|
5
|
+
attr_reader :tool_buttons
|
|
6
|
+
|
|
7
|
+
# Allow user to specify an array of model attributes to be used
|
|
8
|
+
# in respected section. For example attributes to show as header
|
|
9
|
+
# columns in index section
|
|
10
|
+
def attributes(*fields_name, **options, &block)
|
|
11
|
+
if options.include? :except
|
|
12
|
+
@fields = resolve_model_reflections.reject do |field|
|
|
13
|
+
options[:except].include? field.to_sym
|
|
14
|
+
end
|
|
15
|
+
elsif options.include? :append
|
|
16
|
+
@fields += options[:append]
|
|
17
|
+
else
|
|
18
|
+
# set new value for fields
|
|
19
|
+
@fields = fields_name.map(&:to_s) unless fields_name.empty?
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
@fields.concat(block.call.map(&:to_s)) if block_given?
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
# Define a new tool on the `tool` place of the index section
|
|
26
|
+
# **options**: Is a hash which contains the tool button properties.
|
|
27
|
+
#
|
|
28
|
+
# `name`: name of the button. used as identifier
|
|
29
|
+
# `class`: classes of the button.
|
|
30
|
+
# `icon_class`: font awesome icon to use in button.
|
|
31
|
+
# `remote`: whether
|
|
32
|
+
#
|
|
33
|
+
# You have to provide a block for this method which returns
|
|
34
|
+
# a string to be used as the **href** for the link
|
|
35
|
+
def tool_button(**options, &block)
|
|
36
|
+
fail 'You have to provide a block for `tool_button`' if !block_given?
|
|
37
|
+
|
|
38
|
+
options[:block] = block
|
|
39
|
+
@tool_buttons ||= {}
|
|
40
|
+
@tool_buttons[options[:name]] = options
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
alias_method :table_fields, :attributes
|
|
44
|
+
end
|
|
45
|
+
end
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
module Underworld::Dashboard
|
|
2
|
+
module Helpers
|
|
3
|
+
module BoxHelpers
|
|
4
|
+
|
|
5
|
+
class Box
|
|
6
|
+
def footer(&block)
|
|
7
|
+
return @footer = nil unless block_given?
|
|
8
|
+
@footer = block
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
def body(&block)
|
|
12
|
+
@body = block
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def footer_content
|
|
16
|
+
@footer
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def body_content
|
|
20
|
+
@body
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
class Tabs
|
|
25
|
+
|
|
26
|
+
class Tab
|
|
27
|
+
attr_reader :title, :title_icon
|
|
28
|
+
attr_accessor :content, :id
|
|
29
|
+
|
|
30
|
+
def initialize(title, **options)
|
|
31
|
+
@title = title
|
|
32
|
+
@active = false
|
|
33
|
+
@active = true if options[:active] == true
|
|
34
|
+
@title_icon = options[:title_icon]
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def active?
|
|
38
|
+
@active
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
attr_reader :title_icon
|
|
43
|
+
|
|
44
|
+
def initialize(**options)
|
|
45
|
+
@tabs = []
|
|
46
|
+
@title_icon = options[:title_icon]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def tab(title, **options, &block)
|
|
51
|
+
id = @tabs.length
|
|
52
|
+
tab = Tab.new(title, **options)
|
|
53
|
+
tab.content = block
|
|
54
|
+
tab.id = id
|
|
55
|
+
@tabs << tab
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
def tabs
|
|
59
|
+
@tabs
|
|
60
|
+
end
|
|
61
|
+
end
|
|
62
|
+
|
|
63
|
+
def box(title = nil, **options)
|
|
64
|
+
content = Box.new
|
|
65
|
+
|
|
66
|
+
yield content
|
|
67
|
+
|
|
68
|
+
result = "<div class='box #{options[:box_class] || 'box-default'}'>" +
|
|
69
|
+
"<div class='box-header with-border'>" +
|
|
70
|
+
"<h3 class='box-title'>" +
|
|
71
|
+
title +
|
|
72
|
+
'</h3>'
|
|
73
|
+
|
|
74
|
+
unless options[:show_tools].nil?
|
|
75
|
+
result += "<div class='box-tools pull-right'>" +
|
|
76
|
+
"<button class='btn btn-box-tool' data-widget='collapse'>" +
|
|
77
|
+
"<i class='fa fa-minus'></i></button>" +
|
|
78
|
+
"</div><!-- /.box-tools -->"
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
result += "</div><!-- /.box-header -->" +
|
|
82
|
+
"<div class='box-body'>" +
|
|
83
|
+
capture(&content.body_content) +
|
|
84
|
+
"</div><!-- /.box-body -->"
|
|
85
|
+
|
|
86
|
+
unless content.footer_content.nil?
|
|
87
|
+
result += '<div class="box-footer">' +
|
|
88
|
+
content.footer_content +
|
|
89
|
+
"</div><!-- /.box-footer-->"
|
|
90
|
+
end
|
|
91
|
+
|
|
92
|
+
result += "</div><!-- /.box -->"
|
|
93
|
+
|
|
94
|
+
result.html_safe
|
|
95
|
+
end
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def tabbed_box(title = nil, **options)
|
|
99
|
+
content = Tabs.new
|
|
100
|
+
|
|
101
|
+
yield content
|
|
102
|
+
|
|
103
|
+
result = "<div class='nav-tabs-custom'>" +
|
|
104
|
+
"<ul class='nav nav-tabs pull-right'>"
|
|
105
|
+
|
|
106
|
+
content.tabs.each do |tab|
|
|
107
|
+
active = nil
|
|
108
|
+
active = "class='active'" if tab.active?
|
|
109
|
+
|
|
110
|
+
result += "<li #{active}><a href='#tab_#{tab.id}-#{tab.id}' data-toggle='tab'>" +
|
|
111
|
+
"<i class='fa #{tab.title_icon}'></i> #{tab.title}</a></li>"
|
|
112
|
+
end
|
|
113
|
+
|
|
114
|
+
result += "<li class='pull-left header'><i class='fa #{options[:title_icon]}'></i>#{title}</li>" +
|
|
115
|
+
"</ul>" +
|
|
116
|
+
"<div class='tab-content'>"
|
|
117
|
+
|
|
118
|
+
content.tabs.each do |tab|
|
|
119
|
+
result += "<div class='tab-pane #{'active' if tab.active?}' id='tab_#{tab.id}-#{tab.id}'>" +
|
|
120
|
+
capture(&tab.content) +
|
|
121
|
+
"</div><!-- /.tab-pane -->"
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
result += "</div><!-- /.tab-content --></div>"
|
|
125
|
+
result.html_safe
|
|
126
|
+
end
|
|
127
|
+
end
|
|
128
|
+
end
|
|
129
|
+
end
|