collavre 0.1.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
- data/README.md +221 -0
- data/Rakefile +8 -0
- data/app/assets/stylesheets/collavre/actiontext.css +577 -0
- data/app/assets/stylesheets/collavre/activity_logs.css +99 -0
- data/app/assets/stylesheets/collavre/comments_popup.css +692 -0
- data/app/assets/stylesheets/collavre/creatives.css +559 -0
- data/app/assets/stylesheets/collavre/dark_mode.css +118 -0
- data/app/assets/stylesheets/collavre/mention_menu.css +43 -0
- data/app/assets/stylesheets/collavre/popup.css +160 -0
- data/app/assets/stylesheets/collavre/print.css +37 -0
- data/app/assets/stylesheets/collavre/slide_view.css +79 -0
- data/app/assets/stylesheets/collavre/user_menu.css +34 -0
- data/app/channels/collavre/comments_presence_channel.rb +54 -0
- data/app/channels/collavre/slide_view_channel.rb +11 -0
- data/app/channels/collavre/topics_channel.rb +12 -0
- data/app/components/collavre/avatar_component.html.erb +15 -0
- data/app/components/collavre/avatar_component.rb +59 -0
- data/app/components/collavre/inbox/badge_component.html.erb +6 -0
- data/app/components/collavre/inbox/badge_component.rb +18 -0
- data/app/components/collavre/plans_timeline_component.html.erb +14 -0
- data/app/components/collavre/plans_timeline_component.rb +56 -0
- data/app/components/collavre/popup_menu_component.html.erb +6 -0
- data/app/components/collavre/popup_menu_component.rb +30 -0
- data/app/components/collavre/progress_filter_component.html.erb +5 -0
- data/app/components/collavre/progress_filter_component.rb +10 -0
- data/app/components/collavre/user_mention_menu_component.html.erb +3 -0
- data/app/components/collavre/user_mention_menu_component.rb +8 -0
- data/app/controllers/collavre/application_controller.rb +15 -0
- data/app/controllers/collavre/attachments_controller.rb +44 -0
- data/app/controllers/collavre/calendar_events_controller.rb +15 -0
- data/app/controllers/collavre/comment_read_pointers_controller.rb +80 -0
- data/app/controllers/collavre/comments/activity_logs_controller.rb +26 -0
- data/app/controllers/collavre/comments/reactions_controller.rb +82 -0
- data/app/controllers/collavre/comments_controller.rb +464 -0
- data/app/controllers/collavre/contacts_controller.rb +10 -0
- data/app/controllers/collavre/creative_expanded_states_controller.rb +27 -0
- data/app/controllers/collavre/creative_imports_controller.rb +24 -0
- data/app/controllers/collavre/creative_plans_controller.rb +69 -0
- data/app/controllers/collavre/creative_shares_controller.rb +79 -0
- data/app/controllers/collavre/creatives_controller.rb +535 -0
- data/app/controllers/collavre/devices_controller.rb +19 -0
- data/app/controllers/collavre/email_verifications_controller.rb +16 -0
- data/app/controllers/collavre/emails_controller.rb +11 -0
- data/app/controllers/collavre/github_auth_controller.rb +25 -0
- data/app/controllers/collavre/google_auth_controller.rb +43 -0
- data/app/controllers/collavre/inbox_items_controller.rb +64 -0
- data/app/controllers/collavre/invites_controller.rb +27 -0
- data/app/controllers/collavre/notion_auth_controller.rb +25 -0
- data/app/controllers/collavre/passwords_controller.rb +37 -0
- data/app/controllers/collavre/plans_controller.rb +110 -0
- data/app/controllers/collavre/sessions_controller.rb +57 -0
- data/app/controllers/collavre/topics_controller.rb +58 -0
- data/app/controllers/collavre/user_themes_controller.rb +58 -0
- data/app/controllers/collavre/users_controller.rb +390 -0
- data/app/helpers/collavre/application_helper.rb +4 -0
- data/app/helpers/collavre/comments_helper.rb +9 -0
- data/app/helpers/collavre/creatives_helper.rb +343 -0
- data/app/helpers/collavre/navigation_helper.rb +163 -0
- data/app/helpers/collavre/user_themes_helper.rb +4 -0
- data/app/javascript/collavre.js +26 -0
- data/app/javascript/components/InlineLexicalEditor.jsx +889 -0
- data/app/javascript/components/LinkPopup.jsx +112 -0
- data/app/javascript/components/creative_tree_row.js +503 -0
- data/app/javascript/components/plugins/attachment_cleanup_plugin.jsx +95 -0
- data/app/javascript/components/plugins/image_upload_plugin.jsx +162 -0
- data/app/javascript/controllers/click_target_controller.js +13 -0
- data/app/javascript/controllers/comment_controller.js +162 -0
- data/app/javascript/controllers/comments/__tests__/popup_controller.test.js +68 -0
- data/app/javascript/controllers/comments/form_controller.js +530 -0
- data/app/javascript/controllers/comments/list_controller.js +715 -0
- data/app/javascript/controllers/comments/mention_menu_controller.js +41 -0
- data/app/javascript/controllers/comments/popup_controller.js +385 -0
- data/app/javascript/controllers/comments/presence_controller.js +311 -0
- data/app/javascript/controllers/comments/topics_controller.js +338 -0
- data/app/javascript/controllers/common_popup_controller.js +55 -0
- data/app/javascript/controllers/creatives/drag_drop_controller.js +45 -0
- data/app/javascript/controllers/creatives/expansion_controller.js +222 -0
- data/app/javascript/controllers/creatives/import_controller.js +116 -0
- data/app/javascript/controllers/creatives/row_editor_controller.js +8 -0
- data/app/javascript/controllers/creatives/select_mode_controller.js +231 -0
- data/app/javascript/controllers/creatives/set_plan_modal_controller.js +107 -0
- data/app/javascript/controllers/creatives/tree_controller.js +218 -0
- data/app/javascript/controllers/index.js +79 -0
- data/app/javascript/controllers/link_creative_controller.js +91 -0
- data/app/javascript/controllers/popup_menu_controller.js +82 -0
- data/app/javascript/controllers/progress_filter_controller.js +35 -0
- data/app/javascript/controllers/reaction_picker_controller.js +107 -0
- data/app/javascript/controllers/share_invite_controller.js +15 -0
- data/app/javascript/controllers/share_user_search_controller.js +121 -0
- data/app/javascript/controllers/tabs_controller.js +43 -0
- data/app/javascript/creatives/drag_drop/dom.js +170 -0
- data/app/javascript/creatives/drag_drop/event_handlers.js +846 -0
- data/app/javascript/creatives/drag_drop/indicator.js +35 -0
- data/app/javascript/creatives/drag_drop/operations.js +116 -0
- data/app/javascript/creatives/drag_drop/state.js +31 -0
- data/app/javascript/creatives/tree_renderer.js +248 -0
- data/app/javascript/lib/api/__tests__/queue_manager.test.js +153 -0
- data/app/javascript/lib/api/creatives.js +79 -0
- data/app/javascript/lib/api/csrf_fetch.js +22 -0
- data/app/javascript/lib/api/drag_drop.js +31 -0
- data/app/javascript/lib/api/queue_manager.js +423 -0
- data/app/javascript/lib/apply_lexical_styles.js +15 -0
- data/app/javascript/lib/common_popup.js +195 -0
- data/app/javascript/lib/lexical/__tests__/action_text_attachment_node.test.jsx +91 -0
- data/app/javascript/lib/lexical/__tests__/attachment_payload.test.js +194 -0
- data/app/javascript/lib/lexical/action_text_attachment_node.js +459 -0
- data/app/javascript/lib/lexical/attachment_node.jsx +170 -0
- data/app/javascript/lib/lexical/attachment_payload.js +293 -0
- data/app/javascript/lib/lexical/dom_attachment_utils.js +66 -0
- data/app/javascript/lib/lexical/image_node.jsx +159 -0
- data/app/javascript/lib/lexical/style_attributes.js +40 -0
- data/app/javascript/lib/responsive_images.js +54 -0
- data/app/javascript/lib/turbo_stream_actions.js +33 -0
- data/app/javascript/lib/utils/markdown.js +23 -0
- data/app/javascript/modules/creative_guide.js +53 -0
- data/app/javascript/modules/creative_row_editor.js +1841 -0
- data/app/javascript/modules/creative_row_swipe.js +43 -0
- data/app/javascript/modules/creatives.js +15 -0
- data/app/javascript/modules/export_to_markdown.js +34 -0
- data/app/javascript/modules/inbox_panel.js +226 -0
- data/app/javascript/modules/lexical_inline_editor.jsx +133 -0
- data/app/javascript/modules/mention_menu.js +77 -0
- data/app/javascript/modules/plans_menu.js +39 -0
- data/app/javascript/modules/plans_timeline.js +397 -0
- data/app/javascript/modules/share_modal.js +73 -0
- data/app/javascript/modules/share_user_popup.js +77 -0
- data/app/javascript/modules/slide_view.js +163 -0
- data/app/javascript/services/cable.js +32 -0
- data/app/javascript/slide_view.js +2 -0
- data/app/javascript/utils/caret_position.js +42 -0
- data/app/javascript/utils/clipboard.js +40 -0
- data/app/jobs/collavre/ai_agent_job.rb +27 -0
- data/app/jobs/collavre/inbox_summary_job.rb +24 -0
- data/app/jobs/collavre/notion_export_job.rb +30 -0
- data/app/jobs/collavre/notion_sync_job.rb +48 -0
- data/app/jobs/collavre/permission_cache_cleanup_job.rb +36 -0
- data/app/jobs/collavre/permission_cache_job.rb +71 -0
- data/app/jobs/collavre/push_notification_job.rb +86 -0
- data/app/mailers/collavre/application_mailer.rb +17 -0
- data/app/mailers/collavre/creative_mailer.rb +9 -0
- data/app/mailers/collavre/email_verification_mailer.rb +20 -0
- data/app/mailers/collavre/inbox_mailer.rb +19 -0
- data/app/mailers/collavre/invitation_mailer.rb +16 -0
- data/app/mailers/collavre/passwords_mailer.rb +10 -0
- data/app/models/collavre/activity_log.rb +13 -0
- data/app/models/collavre/application_record.rb +5 -0
- data/app/models/collavre/calendar_event.rb +20 -0
- data/app/models/collavre/comment.rb +307 -0
- data/app/models/collavre/comment_presence_store.rb +30 -0
- data/app/models/collavre/comment_reaction.rb +11 -0
- data/app/models/collavre/comment_read_pointer.rb +26 -0
- data/app/models/collavre/contact.rb +23 -0
- data/app/models/collavre/creative.rb +413 -0
- data/app/models/collavre/creative_expanded_state.rb +11 -0
- data/app/models/collavre/creative_share.rb +122 -0
- data/app/models/collavre/creative_shares_cache.rb +18 -0
- data/app/models/collavre/current.rb +14 -0
- data/app/models/collavre/device.rb +13 -0
- data/app/models/collavre/email.rb +14 -0
- data/app/models/collavre/github_account.rb +10 -0
- data/app/models/collavre/github_repository_link.rb +19 -0
- data/app/models/collavre/inbox_item.rb +95 -0
- data/app/models/collavre/invitation.rb +22 -0
- data/app/models/collavre/label.rb +47 -0
- data/app/models/collavre/mcp_tool.rb +30 -0
- data/app/models/collavre/notion_account.rb +17 -0
- data/app/models/collavre/notion_block_link.rb +10 -0
- data/app/models/collavre/notion_page_link.rb +19 -0
- data/app/models/collavre/plan.rb +20 -0
- data/app/models/collavre/session.rb +24 -0
- data/app/models/collavre/system_setting.rb +144 -0
- data/app/models/collavre/tag.rb +10 -0
- data/app/models/collavre/task.rb +10 -0
- data/app/models/collavre/task_action.rb +10 -0
- data/app/models/collavre/topic.rb +12 -0
- data/app/models/collavre/user.rb +174 -0
- data/app/models/collavre/user_theme.rb +10 -0
- data/app/models/collavre/variation.rb +5 -0
- data/app/models/collavre/webauthn_credential.rb +11 -0
- data/app/services/collavre/ai_agent_service.rb +193 -0
- data/app/services/collavre/ai_client.rb +183 -0
- data/app/services/collavre/ai_system_prompt_renderer.rb +38 -0
- data/app/services/collavre/auto_theme_generator.rb +198 -0
- data/app/services/collavre/comment_link_formatter.rb +60 -0
- data/app/services/collavre/comments/action_executor.rb +262 -0
- data/app/services/collavre/comments/action_validator.rb +58 -0
- data/app/services/collavre/comments/calendar_command.rb +97 -0
- data/app/services/collavre/comments/command_processor.rb +37 -0
- data/app/services/collavre/comments/mcp_command.rb +109 -0
- data/app/services/collavre/comments/mcp_command_builder.rb +32 -0
- data/app/services/collavre/creatives/filter_pipeline.rb +196 -0
- data/app/services/collavre/creatives/filters/assignee_filter.rb +30 -0
- data/app/services/collavre/creatives/filters/base_filter.rb +24 -0
- data/app/services/collavre/creatives/filters/comment_filter.rb +21 -0
- data/app/services/collavre/creatives/filters/date_filter.rb +58 -0
- data/app/services/collavre/creatives/filters/progress_filter.rb +25 -0
- data/app/services/collavre/creatives/filters/search_filter.rb +28 -0
- data/app/services/collavre/creatives/filters/tag_filter.rb +16 -0
- data/app/services/collavre/creatives/importer.rb +47 -0
- data/app/services/collavre/creatives/index_query.rb +191 -0
- data/app/services/collavre/creatives/path_exporter.rb +131 -0
- data/app/services/collavre/creatives/permission_cache_builder.rb +194 -0
- data/app/services/collavre/creatives/permission_checker.rb +42 -0
- data/app/services/collavre/creatives/plan_tagger.rb +53 -0
- data/app/services/collavre/creatives/progress_service.rb +89 -0
- data/app/services/collavre/creatives/reorderer.rb +187 -0
- data/app/services/collavre/creatives/tree_builder.rb +231 -0
- data/app/services/collavre/creatives/tree_formatter.rb +36 -0
- data/app/services/collavre/gemini_parent_recommender.rb +77 -0
- data/app/services/collavre/github/client.rb +112 -0
- data/app/services/collavre/github/pull_request_analyzer.rb +280 -0
- data/app/services/collavre/github/pull_request_processor.rb +181 -0
- data/app/services/collavre/github/webhook_provisioner.rb +130 -0
- data/app/services/collavre/google_calendar_service.rb +149 -0
- data/app/services/collavre/link_preview_fetcher.rb +230 -0
- data/app/services/collavre/markdown_importer.rb +202 -0
- data/app/services/collavre/mcp_service.rb +217 -0
- data/app/services/collavre/notion_client.rb +231 -0
- data/app/services/collavre/notion_creative_exporter.rb +296 -0
- data/app/services/collavre/notion_service.rb +249 -0
- data/app/services/collavre/ppt_importer.rb +76 -0
- data/app/services/collavre/ruby_llm_interaction_logger.rb +34 -0
- data/app/services/collavre/system_events/context_builder.rb +41 -0
- data/app/services/collavre/system_events/dispatcher.rb +19 -0
- data/app/services/collavre/system_events/router.rb +72 -0
- data/app/services/collavre/tools/creative_retrieval_service.rb +138 -0
- data/app/views/admin/shared/_tabs.html.erb +4 -0
- data/app/views/collavre/comments/_activity_log_details.html.erb +23 -0
- data/app/views/collavre/comments/_comment.html.erb +147 -0
- data/app/views/collavre/comments/_comments_popup.html.erb +46 -0
- data/app/views/collavre/comments/_list.html.erb +10 -0
- data/app/views/collavre/comments/_presence_avatars.html.erb +8 -0
- data/app/views/collavre/comments/_reaction_picker.html.erb +15 -0
- data/app/views/collavre/comments/_read_receipts.html.erb +19 -0
- data/app/views/collavre/creatives/_add_button.html.erb +20 -0
- data/app/views/collavre/creatives/_delete_button.html.erb +12 -0
- data/app/views/collavre/creatives/_github_integration_modal.html.erb +77 -0
- data/app/views/collavre/creatives/_import_upload_zone.html.erb +10 -0
- data/app/views/collavre/creatives/_inline_edit_form.html.erb +89 -0
- data/app/views/collavre/creatives/_mobile_actions_menu.html.erb +24 -0
- data/app/views/collavre/creatives/_notion_integration_modal.html.erb +90 -0
- data/app/views/collavre/creatives/_set_plan_modal.html.erb +25 -0
- data/app/views/collavre/creatives/_share_button.html.erb +55 -0
- data/app/views/collavre/creatives/edit.html.erb +4 -0
- data/app/views/collavre/creatives/index.html.erb +192 -0
- data/app/views/collavre/creatives/new.html.erb +4 -0
- data/app/views/collavre/creatives/show.html.erb +29 -0
- data/app/views/collavre/creatives/slide_view.html.erb +20 -0
- data/app/views/collavre/email_verification_mailer/verify.html.erb +4 -0
- data/app/views/collavre/email_verification_mailer/verify.text.erb +2 -0
- data/app/views/collavre/emails/index.html.erb +19 -0
- data/app/views/collavre/emails/show.html.erb +5 -0
- data/app/views/collavre/inbox_items/_item.html.erb +18 -0
- data/app/views/collavre/inbox_items/_items.html.erb +3 -0
- data/app/views/collavre/inbox_items/_list.html.erb +7 -0
- data/app/views/collavre/inbox_items/index.html.erb +1 -0
- data/app/views/collavre/inbox_mailer/daily_summary.html.erb +11 -0
- data/app/views/collavre/inbox_mailer/daily_summary.text.erb +8 -0
- data/app/views/collavre/invitation_mailer/invite.html.erb +5 -0
- data/app/views/collavre/invitation_mailer/invite.text.erb +3 -0
- data/app/views/collavre/invites/show.html.erb +8 -0
- data/app/views/collavre/passwords/edit.html.erb +12 -0
- data/app/views/collavre/passwords/new.html.erb +17 -0
- data/app/views/collavre/passwords_mailer/reset.html.erb +4 -0
- data/app/views/collavre/passwords_mailer/reset.text.erb +2 -0
- data/app/views/collavre/sessions/new.html.erb +27 -0
- data/app/views/collavre/sessions/providers/_google.html.erb +7 -0
- data/app/views/collavre/sessions/providers/_passkey.html.erb +3 -0
- data/app/views/collavre/shared/_link_creative_modal.html.erb +6 -0
- data/app/views/collavre/shared/_navigation.html.erb +37 -0
- data/app/views/collavre/shared/navigation/_help_button.html.erb +1 -0
- data/app/views/collavre/shared/navigation/_inbox_button.html.erb +5 -0
- data/app/views/collavre/shared/navigation/_mobile_inbox_button.html.erb +3 -0
- data/app/views/collavre/shared/navigation/_mobile_plans_button.html.erb +1 -0
- data/app/views/collavre/shared/navigation/_panels.html.erb +18 -0
- data/app/views/collavre/shared/navigation/_plans_button.html.erb +3 -0
- data/app/views/collavre/shared/navigation/_search_form.html.erb +6 -0
- data/app/views/collavre/user_themes/index.html.erb +75 -0
- data/app/views/collavre/users/_app_header.html.erb +5 -0
- data/app/views/collavre/users/_contact_management.html.erb +77 -0
- data/app/views/collavre/users/_id_pwd_fields.html.erb +27 -0
- data/app/views/collavre/users/edit_ai.html.erb +79 -0
- data/app/views/collavre/users/edit_password.html.erb +27 -0
- data/app/views/collavre/users/index.html.erb +80 -0
- data/app/views/collavre/users/new.html.erb +33 -0
- data/app/views/collavre/users/new_ai.html.erb +87 -0
- data/app/views/collavre/users/passkeys.html.erb +43 -0
- data/app/views/collavre/users/show.html.erb +143 -0
- data/app/views/inbox/badge_component/_count.html.erb +2 -0
- data/app/views/layouts/collavre/slide.html.erb +29 -0
- data/config/locales/comments.en.yml +114 -0
- data/config/locales/comments.ko.yml +110 -0
- data/config/locales/contacts.en.yml +16 -0
- data/config/locales/contacts.ko.yml +16 -0
- data/config/locales/creatives.en.yml +183 -0
- data/config/locales/creatives.ko.yml +164 -0
- data/config/locales/invites.en.yml +19 -0
- data/config/locales/invites.ko.yml +17 -0
- data/config/locales/notifications.en.yml +8 -0
- data/config/locales/notifications.ko.yml +8 -0
- data/config/locales/plans.en.yml +12 -0
- data/config/locales/plans.ko.yml +19 -0
- data/config/locales/themes.en.yml +29 -0
- data/config/locales/themes.ko.yml +27 -0
- data/config/locales/users.en.yml +151 -0
- data/config/locales/users.ko.yml +146 -0
- data/config/routes.rb +92 -0
- data/db/migrate/20241201000000_create_notion_integrations.rb +29 -0
- data/db/migrate/20250128110017_create_creatives.rb +11 -0
- data/db/migrate/20250128120122_create_users.rb +11 -0
- data/db/migrate/20250128120123_create_sessions.rb +11 -0
- data/db/migrate/20250128123633_create_subscribers.rb +10 -0
- data/db/migrate/20250312000000_create_notion_block_links.rb +16 -0
- data/db/migrate/20250312010000_allow_multiple_notion_blocks_per_creative.rb +5 -0
- data/db/migrate/20250522115048_remove_name_from_creatives.rb +5 -0
- data/db/migrate/20250522190651_add_parent_id_to_creatives.rb +5 -0
- data/db/migrate/20250523133100_rename_inventory_count_to_progress.rb +13 -0
- data/db/migrate/20250523133101_add_sequence_to_creatives.rb +5 -0
- data/db/migrate/20250525205100_add_user_id_to_creatives.rb +10 -0
- data/db/migrate/20250527014217_create_creative_shares.rb +12 -0
- data/db/migrate/20250528142349_add_origin_id_to_creatives.rb +5 -0
- data/db/migrate/20250530060200_create_tags.rb +9 -0
- data/db/migrate/20250531105150_add_value_to_tags.rb +5 -0
- data/db/migrate/20250531140142_create_labels.rb +12 -0
- data/db/migrate/20250531140145_change_tag_to_label_reference.rb +6 -0
- data/db/migrate/20250601000000_create_comments.rb +10 -0
- data/db/migrate/20250601061830_drop_plans_and_variations.rb +6 -0
- data/db/migrate/20250604122600_add_owner_to_labels.rb +5 -0
- data/db/migrate/20250606000000_rename_email_address_to_email_in_users.rb +7 -0
- data/db/migrate/20250606150329_create_creative_hierarchies.rb +16 -0
- data/db/migrate/20250610142000_create_creative_expanded_states.rb +11 -0
- data/db/migrate/20250611030138_create_invitations.rb +15 -0
- data/db/migrate/20250611105524_create_inbox_items.rb +14 -0
- data/db/migrate/20250612150000_update_permissions.rb +43 -0
- data/db/migrate/20250612232913_add_email_verified_at_to_users.rb +5 -0
- data/db/migrate/20250616065905_add_avatar_to_users.rb +5 -0
- data/db/migrate/20250617092111_add_display_level_to_users.rb +5 -0
- data/db/migrate/20250620004558_create_emails.rb +13 -0
- data/db/migrate/20250621000000_create_comment_read_pointers.rb +11 -0
- data/db/migrate/20250622000000_add_completion_mark_to_users.rb +5 -0
- data/db/migrate/20250623000000_add_theme_to_users.rb +5 -0
- data/db/migrate/20250624000000_add_name_to_users.rb +18 -0
- data/db/migrate/20250714190000_create_devices.rb +17 -0
- data/db/migrate/20250715120000_add_notifications_enabled_to_users.rb +5 -0
- data/db/migrate/20250823000000_add_no_access_permission.rb +25 -0
- data/db/migrate/20250826000000_add_calendar_id_to_users.rb +5 -0
- data/db/migrate/20250827000000_add_google_oauth_tokens_to_users.rb +8 -0
- data/db/migrate/20250827061238_add_timezone_to_users.rb +5 -0
- data/db/migrate/20250828000000_create_calendar_events.rb +16 -0
- data/db/migrate/20250828060000_add_creative_to_calendar_events.rb +5 -0
- data/db/migrate/20250830141052_add_locale_to_users.rb +5 -0
- data/db/migrate/20250830141101_add_message_key_to_inbox_items.rb +6 -0
- data/db/migrate/20250902025423_remove_description_and_featured_image_from_creatives.rb +6 -0
- data/db/migrate/20250910000000_add_private_to_comments.rb +5 -0
- data/db/migrate/20250910105640_migrate_write_permissions_to_admin.rb +17 -0
- data/db/migrate/20250911084338_backfill_creative_in_comment_inbox_items.rb +27 -0
- data/db/migrate/20250923002959_deduplicate_device_fcm_tokens.rb +25 -0
- data/db/migrate/20250924000000_add_system_admin_to_users.rb +6 -0
- data/db/migrate/20250925000000_create_github_integrations.rb +26 -0
- data/db/migrate/20250927000000_add_webhook_secret_to_github_repository_links.rb +29 -0
- data/db/migrate/20250928000000_add_action_and_approver_to_comments.rb +6 -0
- data/db/migrate/20250928010000_add_action_execution_tracking_to_comments.rb +6 -0
- data/db/migrate/20250928105957_add_github_gemini_prompt_to_creatives.rb +5 -0
- data/db/migrate/20250929000000_add_comment_and_creative_refs_to_inbox_items.rb +6 -0
- data/db/migrate/20251001000001_create_contacts.rb +71 -0
- data/db/migrate/20251002000000_add_shared_by_to_creative_shares.rb +14 -0
- data/db/migrate/20251124120902_add_ai_fields_to_users.rb +7 -0
- data/db/migrate/20251124122218_add_created_by_id_to_users.rb +5 -0
- data/db/migrate/20251124124521_add_llm_api_key_to_users.rb +5 -0
- data/db/migrate/20251124130000_add_searchable_to_users.rb +6 -0
- data/db/migrate/20251125072705_migrate_linked_creative_children_to_origin.rb +28 -0
- data/db/migrate/20251126040752_add_description_to_creatives.rb +75 -0
- data/db/migrate/20251127000000_move_comments_from_linked_creatives_to_origins.rb +18 -0
- data/db/migrate/20251201073823_add_tools_to_users.rb +5 -0
- data/db/migrate/20251202062715_create_mcp_tools.rb +16 -0
- data/db/migrate/20251204125754_create_tasks_and_task_actions.rb +23 -0
- data/db/migrate/20251204125756_add_routing_expression_to_users.rb +5 -0
- data/db/migrate/20251204161133_set_default_routing_expression_for_ai_agents.rb +13 -0
- data/db/migrate/20251211033025_nullify_self_referencing_origins.rb +9 -0
- data/db/migrate/20251211080040_create_topics_and_add_topic_to_comments.rb +15 -0
- data/db/migrate/20251215143500_create_activity_logs.rb +16 -0
- data/db/migrate/20251222125727_create_webauthn_credentials.rb +14 -0
- data/db/migrate/20251222125839_add_webauthn_id_to_users.rb +5 -0
- data/db/migrate/20251223022315_create_comment_reactions.rb +13 -0
- data/db/migrate/20251223072625_create_user_themes.rb +11 -0
- data/db/migrate/20251230074456_add_creative_id_to_labels.rb +5 -0
- data/db/migrate/20251230113607_refactor_labels.rb +38 -0
- data/db/migrate/20251231010012_backfill_tags_for_labels.rb +15 -0
- data/db/migrate/20251231013234_drop_subscribers.rb +9 -0
- data/db/migrate/20260106090544_allow_null_user_id_in_creative_shares.rb +6 -0
- data/db/migrate/20260106160643_create_system_settings.rb +11 -0
- data/db/migrate/20260116000000_create_creative_shares_cache.rb +15 -0
- data/db/migrate/20260116000001_populate_creative_shares_cache.rb +23 -0
- data/db/migrate/20260119022933_make_source_share_id_nullable_in_creative_shares_caches.rb +7 -0
- data/db/migrate/20260119023446_populate_owner_cache_entries.rb +25 -0
- data/db/migrate/20260119100000_add_account_lockout_to_users.rb +6 -0
- data/db/migrate/20260119110000_add_last_active_at_to_sessions.rb +6 -0
- data/db/migrate/20260120045354_encrypt_oauth_tokens.rb +60 -0
- data/db/migrate/20260120162259_remove_fk_from_creative_shares_caches.rb +7 -0
- data/db/migrate/20260120163856_remove_timestamps_from_creative_shares_caches.rb +6 -0
- data/lib/collavre/configuration.rb +14 -0
- data/lib/collavre/engine.rb +77 -0
- data/lib/collavre/user_extensions.rb +29 -0
- data/lib/collavre/version.rb +3 -0
- data/lib/collavre.rb +26 -0
- data/lib/generators/collavre/install/install_generator.rb +105 -0
- data/lib/generators/collavre/install/templates/build.cjs.tt +100 -0
- data/lib/tasks/collavre_assets.rake +15 -0
- metadata +591 -0
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class CreateActivityLogs < ActiveRecord::Migration[7.1]
|
|
4
|
+
def change
|
|
5
|
+
create_table :activity_logs do |t|
|
|
6
|
+
t.string :activity, null: false
|
|
7
|
+
t.json :log, default: {}, null: false
|
|
8
|
+
t.references :creative, foreign_key: true, null: true
|
|
9
|
+
t.references :user, foreign_key: true, null: true
|
|
10
|
+
t.references :comment, foreign_key: true, null: true
|
|
11
|
+
t.datetime :created_at, null: false
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
add_index :activity_logs, :created_at
|
|
15
|
+
end
|
|
16
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
class CreateWebauthnCredentials < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
create_table :webauthn_credentials do |t|
|
|
4
|
+
t.references :user, null: false, foreign_key: true
|
|
5
|
+
t.string :webauthn_id, null: false
|
|
6
|
+
t.string :public_key, null: false
|
|
7
|
+
t.integer :sign_count, default: 0, null: false
|
|
8
|
+
t.string :nickname
|
|
9
|
+
|
|
10
|
+
t.timestamps
|
|
11
|
+
end
|
|
12
|
+
add_index :webauthn_credentials, :webauthn_id, unique: true
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
class CreateCommentReactions < ActiveRecord::Migration[7.1]
|
|
2
|
+
def change
|
|
3
|
+
create_table :comment_reactions do |t|
|
|
4
|
+
t.references :comment, null: false, foreign_key: true
|
|
5
|
+
t.references :user, null: false, foreign_key: true
|
|
6
|
+
t.string :emoji, null: false
|
|
7
|
+
|
|
8
|
+
t.timestamps
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
add_index :comment_reactions, [ :comment_id, :user_id, :emoji ], unique: true
|
|
12
|
+
end
|
|
13
|
+
end
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
class RefactorLabels < ActiveRecord::Migration[8.1]
|
|
2
|
+
def up
|
|
3
|
+
puts "Start refactoring labels"
|
|
4
|
+
# Data migration: Create Creatives for Labels that don't have one
|
|
5
|
+
Label.where(creative_id: nil).find_each do |label|
|
|
6
|
+
# Use label[:name] to bypass alias_method :name, :description in Label model
|
|
7
|
+
# which returns nil when creative_id is nil
|
|
8
|
+
actual_name = label[:name]
|
|
9
|
+
puts "Processing label #{actual_name}"
|
|
10
|
+
next if actual_name.blank?
|
|
11
|
+
|
|
12
|
+
# Create a new Creative using the Label's name and owner
|
|
13
|
+
creative = Creative.create!(
|
|
14
|
+
description: actual_name,
|
|
15
|
+
user_id: label.owner_id,
|
|
16
|
+
progress: 0.0 # Default value
|
|
17
|
+
)
|
|
18
|
+
|
|
19
|
+
puts "Update label #{label.id} with creative #{creative.id}"
|
|
20
|
+
label.update!(creative_id: creative.id)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
# Schema changes
|
|
24
|
+
change_column_null :labels, :creative_id, false
|
|
25
|
+
remove_column :labels, :name
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def down
|
|
29
|
+
add_column :labels, :name, :string
|
|
30
|
+
change_column_null :labels, :creative_id, true
|
|
31
|
+
|
|
32
|
+
# Optional: We could attempt to restore names from creatives, but
|
|
33
|
+
# since we can't be sure which creative was created specifically for the label migration vs manual,
|
|
34
|
+
# strictly speaking the down migration might just restore schema.
|
|
35
|
+
# But for completeness let's try to put creative description back to name if it matches?
|
|
36
|
+
# For now, just restoring schema is safer.
|
|
37
|
+
end
|
|
38
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
class BackfillTagsForLabels < ActiveRecord::Migration[8.1]
|
|
2
|
+
def up
|
|
3
|
+
Label.where.not(creative_id: nil).find_each do |label|
|
|
4
|
+
next if Tag.exists?(label_id: label.id, creative_id: label.creative_id)
|
|
5
|
+
|
|
6
|
+
Tag.create!(label_id: label.id, creative_id: label.creative_id)
|
|
7
|
+
end
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def down
|
|
11
|
+
# No-op or potentially delete tags created by this migration
|
|
12
|
+
# Since tags might have been created manually later, it's safer to avoid bulk deletion
|
|
13
|
+
# unless we track which ones were created here.
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
class AllowNullUserIdInCreativeShares < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
change_column_null :creative_shares, :user_id, true
|
|
4
|
+
add_index :creative_shares, :creative_id, unique: true, where: "user_id IS NULL", name: "index_creative_shares_on_creative_id_public"
|
|
5
|
+
end
|
|
6
|
+
end
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
class CreateCreativeSharesCache < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
create_table :creative_shares_caches do |t|
|
|
4
|
+
t.references :creative, null: false, foreign_key: true
|
|
5
|
+
t.references :user, null: true, foreign_key: true
|
|
6
|
+
t.integer :permission, null: false
|
|
7
|
+
t.references :source_share, null: false, foreign_key: { to_table: :creative_shares }
|
|
8
|
+
t.timestamps
|
|
9
|
+
end
|
|
10
|
+
|
|
11
|
+
add_index :creative_shares_caches, [ :creative_id, :user_id ], unique: true
|
|
12
|
+
add_index :creative_shares_caches, [ :user_id, :permission ]
|
|
13
|
+
# source_share_id index is already created by t.references
|
|
14
|
+
end
|
|
15
|
+
end
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
class PopulateCreativeSharesCache < ActiveRecord::Migration[8.1]
|
|
2
|
+
def up
|
|
3
|
+
say_with_time "Populating creative_shares_cache" do
|
|
4
|
+
# Ensure the service is available before running
|
|
5
|
+
unless defined?(Creatives::PermissionCacheBuilder)
|
|
6
|
+
Rails.logger.warn "PermissionCacheBuilder not available, skipping cache population"
|
|
7
|
+
return 0
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
count = 0
|
|
11
|
+
# Include all shares including no_access (needed to override public shares)
|
|
12
|
+
CreativeShare.find_each do |share|
|
|
13
|
+
Creatives::PermissionCacheBuilder.propagate_share(share)
|
|
14
|
+
count += 1
|
|
15
|
+
end
|
|
16
|
+
count
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
|
|
20
|
+
def down
|
|
21
|
+
execute "DELETE FROM creative_shares_caches"
|
|
22
|
+
end
|
|
23
|
+
end
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
class MakeSourceShareIdNullableInCreativeSharesCaches < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
change_column_null :creative_shares_caches, :source_share_id, true
|
|
4
|
+
remove_foreign_key :creative_shares_caches, :creative_shares
|
|
5
|
+
add_foreign_key :creative_shares_caches, :creative_shares, column: :source_share_id, on_delete: :cascade, validate: false
|
|
6
|
+
end
|
|
7
|
+
end
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
class PopulateOwnerCacheEntries < ActiveRecord::Migration[8.1]
|
|
2
|
+
def up
|
|
3
|
+
say_with_time "Populating owner cache entries" do
|
|
4
|
+
# Insert owner entries for all creatives (admin = 4)
|
|
5
|
+
# Use INSERT ... ON CONFLICT to avoid duplicates
|
|
6
|
+
# datetime('now') for SQLite compatibility
|
|
7
|
+
execute <<~SQL
|
|
8
|
+
INSERT INTO creative_shares_caches (creative_id, user_id, permission, source_share_id, created_at, updated_at)
|
|
9
|
+
SELECT id, user_id, 4, NULL, datetime('now'), datetime('now')
|
|
10
|
+
FROM creatives
|
|
11
|
+
WHERE user_id IS NOT NULL
|
|
12
|
+
ON CONFLICT (creative_id, user_id) DO NOTHING
|
|
13
|
+
SQL
|
|
14
|
+
end
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def down
|
|
18
|
+
say_with_time "Removing owner cache entries" do
|
|
19
|
+
execute <<~SQL
|
|
20
|
+
DELETE FROM creative_shares_caches
|
|
21
|
+
WHERE source_share_id IS NULL
|
|
22
|
+
SQL
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
end
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
class EncryptOauthTokens < ActiveRecord::Migration[8.1]
|
|
4
|
+
def up
|
|
5
|
+
say_with_time "Encrypting User Google tokens" do
|
|
6
|
+
encrypt_column(User, :google_access_token)
|
|
7
|
+
encrypt_column(User, :google_refresh_token)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
say_with_time "Encrypting GithubAccount tokens" do
|
|
11
|
+
encrypt_column(GithubAccount, :token)
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
say_with_time "Encrypting NotionAccount tokens" do
|
|
15
|
+
encrypt_column(NotionAccount, :token)
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def down
|
|
20
|
+
# No-op: Decryption happens automatically when reading encrypted values
|
|
21
|
+
# The `encrypts` declaration in the model handles transparent decryption
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
private
|
|
25
|
+
|
|
26
|
+
def encrypt_column(model, attribute)
|
|
27
|
+
table_name = model.table_name
|
|
28
|
+
|
|
29
|
+
# Read plaintext values directly from database using raw SQL
|
|
30
|
+
records_to_encrypt = ActiveRecord::Base.connection.select_all(
|
|
31
|
+
"SELECT id, #{attribute} FROM #{table_name} WHERE #{attribute} IS NOT NULL"
|
|
32
|
+
).to_a
|
|
33
|
+
|
|
34
|
+
records_to_encrypt.each do |row|
|
|
35
|
+
record_id = row["id"]
|
|
36
|
+
plaintext_value = row[attribute.to_s]
|
|
37
|
+
|
|
38
|
+
# Skip if already encrypted (value looks like JSON with encryption markers)
|
|
39
|
+
next if plaintext_value.nil? || encrypted?(plaintext_value)
|
|
40
|
+
|
|
41
|
+
# Encrypt the plaintext value using the model's encryptor
|
|
42
|
+
encryptor = model.type_for_attribute(attribute)
|
|
43
|
+
encrypted_value = encryptor.serialize(plaintext_value)
|
|
44
|
+
|
|
45
|
+
# Write encrypted value directly to database
|
|
46
|
+
ActiveRecord::Base.connection.execute(
|
|
47
|
+
"UPDATE #{table_name} SET #{attribute} = #{ActiveRecord::Base.connection.quote(encrypted_value)} WHERE id = #{record_id}"
|
|
48
|
+
)
|
|
49
|
+
end
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def encrypted?(value)
|
|
53
|
+
# Active Record Encryption uses JSON format starting with specific markers
|
|
54
|
+
return false unless value.is_a?(String)
|
|
55
|
+
|
|
56
|
+
value.start_with?("{") && value.include?('"p":')
|
|
57
|
+
rescue StandardError
|
|
58
|
+
false
|
|
59
|
+
end
|
|
60
|
+
end
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
class RemoveFkFromCreativeSharesCaches < ActiveRecord::Migration[8.1]
|
|
2
|
+
def change
|
|
3
|
+
remove_foreign_key :creative_shares_caches, :creatives, if_exists: true
|
|
4
|
+
remove_foreign_key :creative_shares_caches, :users, if_exists: true
|
|
5
|
+
remove_foreign_key :creative_shares_caches, column: :source_share_id, if_exists: true
|
|
6
|
+
end
|
|
7
|
+
end
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
module Collavre
|
|
2
|
+
class Configuration
|
|
3
|
+
attr_accessor :user_class_name, :current_user_method
|
|
4
|
+
|
|
5
|
+
def initialize
|
|
6
|
+
@user_class_name = "User"
|
|
7
|
+
@current_user_method = -> { Current.user }
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def user_class
|
|
11
|
+
@user_class_name.constantize
|
|
12
|
+
end
|
|
13
|
+
end
|
|
14
|
+
end
|
|
@@ -0,0 +1,77 @@
|
|
|
1
|
+
module Collavre
|
|
2
|
+
class Engine < ::Rails::Engine
|
|
3
|
+
isolate_namespace Collavre
|
|
4
|
+
|
|
5
|
+
config.generators do |g|
|
|
6
|
+
g.test_framework :minitest
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# Path to engine's JavaScript sources for jsbundling-rails integration
|
|
10
|
+
def self.javascript_path
|
|
11
|
+
root.join("app/javascript")
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
# Path to engine's stylesheet sources
|
|
15
|
+
def self.stylesheet_path
|
|
16
|
+
root.join("app/assets/stylesheets")
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
# Add engine migrations to main app's migration path
|
|
20
|
+
# This allows migrations to live in the engine but be run from the host app
|
|
21
|
+
initializer "collavre.migrations" do |app|
|
|
22
|
+
unless app.root.to_s.match?(root.to_s)
|
|
23
|
+
config.paths["db/migrate"].expanded.each do |expanded_path|
|
|
24
|
+
app.config.paths["db/migrate"] << expanded_path
|
|
25
|
+
end
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
initializer "collavre.assets" do |app|
|
|
30
|
+
app.config.assets.precompile += %w[collavre.js] if app.config.respond_to?(:assets)
|
|
31
|
+
|
|
32
|
+
# Add engine stylesheets to asset paths for Propshaft
|
|
33
|
+
if app.config.respond_to?(:assets) && app.config.assets.respond_to?(:paths)
|
|
34
|
+
app.config.assets.paths << root.join("app/assets/stylesheets")
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
initializer "collavre.importmap", before: "importmap" do |app|
|
|
39
|
+
if app.config.respond_to?(:importmap)
|
|
40
|
+
app.config.importmap.paths << Engine.root.join("config/importmap.rb")
|
|
41
|
+
end
|
|
42
|
+
end
|
|
43
|
+
|
|
44
|
+
# Add engine locales to I18n load path
|
|
45
|
+
# Host app can override these translations by defining the same keys
|
|
46
|
+
initializer "collavre.i18n" do |app|
|
|
47
|
+
config.i18n.load_path += Dir[root.join("config/locales/**/*.yml")]
|
|
48
|
+
end
|
|
49
|
+
|
|
50
|
+
# Allow engine controllers to fall back to host app views during migration
|
|
51
|
+
# This enables gradual view migration - views can stay in host app until moved to engine
|
|
52
|
+
initializer "collavre.view_paths" do
|
|
53
|
+
ActiveSupport.on_load(:action_controller) do
|
|
54
|
+
append_view_path Rails.root.join("app/views")
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
|
|
58
|
+
# Make engine URL helpers available to controllers and views via the `collavre` method
|
|
59
|
+
# This avoids conflicts with main app routes while still providing access to engine routes
|
|
60
|
+
initializer "collavre.url_helpers" do
|
|
61
|
+
ActiveSupport.on_load(:action_controller_base) do
|
|
62
|
+
# Add to controllers
|
|
63
|
+
define_method :collavre do
|
|
64
|
+
Collavre::Engine.routes.url_helpers
|
|
65
|
+
end
|
|
66
|
+
private :collavre
|
|
67
|
+
|
|
68
|
+
# Add to views via helper
|
|
69
|
+
helper do
|
|
70
|
+
def collavre
|
|
71
|
+
Collavre::Engine.routes.url_helpers
|
|
72
|
+
end
|
|
73
|
+
end
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
end
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module Collavre
|
|
2
|
+
module UserExtensions
|
|
3
|
+
extend ActiveSupport::Concern
|
|
4
|
+
|
|
5
|
+
included do
|
|
6
|
+
# Core Collavre associations
|
|
7
|
+
has_many :collavre_creatives, class_name: "Collavre::Creative", dependent: :destroy
|
|
8
|
+
has_many :collavre_comments, class_name: "Collavre::Comment", dependent: :destroy
|
|
9
|
+
has_many :collavre_creative_shares, class_name: "Collavre::CreativeShare", dependent: :destroy
|
|
10
|
+
has_many :collavre_shared_creative_shares, class_name: "Collavre::CreativeShare",
|
|
11
|
+
foreign_key: :shared_by_id, dependent: :nullify, inverse_of: :shared_by
|
|
12
|
+
has_many :collavre_topics, class_name: "Collavre::Topic", dependent: :destroy
|
|
13
|
+
has_many :collavre_inbox_items, class_name: "Collavre::InboxItem",
|
|
14
|
+
foreign_key: :owner_id, dependent: :destroy, inverse_of: :owner
|
|
15
|
+
has_many :collavre_invitations, class_name: "Collavre::Invitation",
|
|
16
|
+
foreign_key: :inviter_id, dependent: :destroy, inverse_of: :inviter
|
|
17
|
+
has_many :collavre_contacts, class_name: "Collavre::Contact", dependent: :destroy
|
|
18
|
+
has_many :collavre_contact_memberships, class_name: "Collavre::Contact",
|
|
19
|
+
foreign_key: :contact_user_id, dependent: :destroy, inverse_of: :contact_user
|
|
20
|
+
has_many :collavre_user_themes, class_name: "Collavre::UserTheme", dependent: :destroy
|
|
21
|
+
has_many :collavre_devices, class_name: "Collavre::Device", dependent: :destroy
|
|
22
|
+
has_many :collavre_calendar_events, class_name: "Collavre::CalendarEvent", dependent: :destroy
|
|
23
|
+
has_many :collavre_labels, class_name: "Collavre::Label", foreign_key: :owner_id, dependent: :destroy
|
|
24
|
+
has_many :collavre_activity_logs, class_name: "Collavre::ActivityLog", dependent: :destroy
|
|
25
|
+
has_many :collavre_comment_read_pointers, class_name: "Collavre::CommentReadPointer", dependent: :destroy
|
|
26
|
+
has_many :collavre_creative_expanded_states, class_name: "Collavre::CreativeExpandedState", dependent: :destroy
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
data/lib/collavre.rb
ADDED
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
require "collavre/version"
|
|
2
|
+
require "collavre/configuration"
|
|
3
|
+
require "collavre/engine"
|
|
4
|
+
require "collavre/user_extensions"
|
|
5
|
+
|
|
6
|
+
module Collavre
|
|
7
|
+
class << self
|
|
8
|
+
attr_writer :configuration
|
|
9
|
+
|
|
10
|
+
def configuration
|
|
11
|
+
@configuration ||= Configuration.new
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def configure
|
|
15
|
+
yield(configuration)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def user_class
|
|
19
|
+
configuration.user_class
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def current_user
|
|
23
|
+
configuration.current_user_method.call
|
|
24
|
+
end
|
|
25
|
+
end
|
|
26
|
+
end
|
|
@@ -0,0 +1,105 @@
|
|
|
1
|
+
# frozen_string_literal: true
|
|
2
|
+
|
|
3
|
+
module Collavre
|
|
4
|
+
module Generators
|
|
5
|
+
class InstallGenerator < Rails::Generators::Base
|
|
6
|
+
source_root File.expand_path("templates", __dir__)
|
|
7
|
+
|
|
8
|
+
desc "Install Collavre engine assets for jsbundling-rails"
|
|
9
|
+
|
|
10
|
+
class_option :replace_build_script, type: :boolean, default: false,
|
|
11
|
+
desc: "Replace script/build.cjs entirely (recommended for new projects)"
|
|
12
|
+
|
|
13
|
+
def install_build_script
|
|
14
|
+
build_script = Rails.root.join("script/build.cjs")
|
|
15
|
+
|
|
16
|
+
if options[:replace_build_script] || !File.exist?(build_script)
|
|
17
|
+
template "build.cjs.tt", "script/build.cjs"
|
|
18
|
+
say_status :create, "script/build.cjs (with Collavre support)", :green
|
|
19
|
+
else
|
|
20
|
+
update_existing_build_script(build_script)
|
|
21
|
+
end
|
|
22
|
+
end
|
|
23
|
+
|
|
24
|
+
def show_post_install
|
|
25
|
+
say ""
|
|
26
|
+
say "Collavre assets installed!", :green
|
|
27
|
+
say ""
|
|
28
|
+
say "The build script now automatically finds Collavre gem and includes its assets."
|
|
29
|
+
say ""
|
|
30
|
+
say "Stylesheets are automatically available via Propshaft."
|
|
31
|
+
say "Add to your application.css if needed:"
|
|
32
|
+
say " @import 'collavre/creatives';"
|
|
33
|
+
say " @import 'collavre/comments_popup';"
|
|
34
|
+
say " @import 'collavre/dark_mode';"
|
|
35
|
+
say ""
|
|
36
|
+
say "Run 'npm run build' to build assets."
|
|
37
|
+
say ""
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
private
|
|
41
|
+
|
|
42
|
+
def update_existing_build_script(build_script)
|
|
43
|
+
content = File.read(build_script)
|
|
44
|
+
|
|
45
|
+
# Check if already installed
|
|
46
|
+
if content.include?("collavre") || content.include?("COLLAVRE_GEM_PATH")
|
|
47
|
+
say_status :skip, "Collavre already configured in build script", :yellow
|
|
48
|
+
return
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
# Add gem path detection function after requires
|
|
52
|
+
gem_path_function = <<~JS
|
|
53
|
+
|
|
54
|
+
// Find Collavre gem path using bundler
|
|
55
|
+
const { execSync } = require('child_process');
|
|
56
|
+
function getCollavreGemPath() {
|
|
57
|
+
if (process.env.COLLAVRE_GEM_PATH) return process.env.COLLAVRE_GEM_PATH;
|
|
58
|
+
try {
|
|
59
|
+
const gemPath = execSync('bundle show collavre 2>/dev/null', { encoding: 'utf8' }).trim();
|
|
60
|
+
if (gemPath && !gemPath.includes('Could not find')) return gemPath;
|
|
61
|
+
} catch (e) {}
|
|
62
|
+
const localPath = path.join(process.cwd(), 'engines/collavre');
|
|
63
|
+
if (require('fs').existsSync(localPath)) return localPath;
|
|
64
|
+
return null;
|
|
65
|
+
}
|
|
66
|
+
JS
|
|
67
|
+
|
|
68
|
+
# Add gem entry point discovery
|
|
69
|
+
gem_discovery_code = <<~JS
|
|
70
|
+
|
|
71
|
+
// Add Collavre gem entry points
|
|
72
|
+
const collavreGemPath = getCollavreGemPath();
|
|
73
|
+
if (collavreGemPath) {
|
|
74
|
+
console.log(`[INFO] Including Collavre assets from: ${collavreGemPath}`);
|
|
75
|
+
const collavreEntries = glob.sync(path.join(collavreGemPath, 'app/javascript/*.*'));
|
|
76
|
+
collavreEntries.forEach(entry => {
|
|
77
|
+
const name = path.parse(entry).name;
|
|
78
|
+
if (!entryPoints[name]) entryPoints[name] = entry;
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
JS
|
|
82
|
+
|
|
83
|
+
# Insert function after requires
|
|
84
|
+
if content.include?("const path = require('path');")
|
|
85
|
+
content = content.sub(
|
|
86
|
+
"const path = require('path');",
|
|
87
|
+
"const path = require('path');#{gem_path_function}"
|
|
88
|
+
)
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
# Insert discovery before the empty check
|
|
92
|
+
if content.include?("if (Object.keys(entryPoints).length === 0)")
|
|
93
|
+
content = content.sub(
|
|
94
|
+
"if (Object.keys(entryPoints).length === 0)",
|
|
95
|
+
"#{gem_discovery_code}\nif (Object.keys(entryPoints).length === 0)"
|
|
96
|
+
)
|
|
97
|
+
File.write(build_script, content)
|
|
98
|
+
say_status :update, "script/build.cjs", :green
|
|
99
|
+
else
|
|
100
|
+
say_status :error, "Could not find insertion point in build script. Use --replace-build-script option.", :red
|
|
101
|
+
end
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
end
|