OpenProject is the leading open source project management software.
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
openproject/lib/api/v3/utilities/path_helper.rb

527 lines
14 KiB

#-- encoding: UTF-8
#-- copyright
# OpenProject is an open source project management software.
# Copyright (C) 2012-2021 the OpenProject GmbH
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License version 3.
#
# OpenProject is a fork of ChiliProject, which is a fork of Redmine. The copyright follows:
# Copyright (C) 2006-2013 Jean-Philippe Lang
# Copyright (C) 2010-2013 the ChiliProject Team
#
# This program is free software; you can redistribute it and/or
# modify it under the terms of the GNU General Public License
# as published by the Free Software Foundation; either version 2
# of the License, or (at your option) any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
#
# See docs/COPYRIGHT.rdoc for more details.
#++
module API
module V3
module Utilities
module PathHelper
include API::Utilities::UrlHelper
class ApiV3Path
extend API::Utilities::UrlHelper
def self.index(name, path = nil)
plural_name = name.to_s.pluralize
path ||= plural_name
define_singleton_method(plural_name) do
"#{root}/#{path}"
end
end
private_class_method :index
def self.show(name)
define_singleton_method(name) { |id| build_path(name, id) }
end
private_class_method :show
def self.create_form(name)
define_singleton_method(:"create_#{name}_form") { build_path(name, "form") }
end
private_class_method :create_form
def self.update_form(name)
define_singleton_method(:"#{name}_form") { |id| build_path(name, id, "form") }
end
private_class_method :update_form
def self.schema(name)
define_singleton_method(:"#{name}_schema") { build_path(name, "schema") }
end
private_class_method :schema
def self.build_path(name, *kwargs)
[root, name.to_s.pluralize, *kwargs].compact.join("/")
end
private_class_method :build_path
def self.resources(name,
except: [],
only: %i[index show create_form update_form schema])
(Array(only) - Array(except)).each do |method|
send(method, name)
end
end
private_class_method :resources
# Determining the root_path on every url we want to render is
# expensive. As the root_path will not change within a
# request, we can cache the first response on each request.
def self.root_path
RequestStore.store[:cached_root_path] ||= super
end
def self.root
"#{root_path}api/v3"
end
index :action
show :action
index :activity
show :activity
index :attachment
show :attachment
def self.attachment_content(id)
"#{root}/attachments/#{id}/content"
end
def self.attachments_by_post(id)
"#{post(id)}/attachments"
end
def self.attachments_by_work_package(id)
"#{work_package(id)}/attachments"
end
def self.attachments_by_help_text(id)
"#{help_text(id)}/attachments"
end
def self.attachments_by_wiki_page(id)
"#{wiki_page(id)}/attachments"
end
def self.prepare_new_attachment_upload
"#{root}/attachments/prepare"
end
index :attachment_upload
show :attachment_upload
def self.attachment_uploaded(attachment_id)
"#{root}/attachments/#{attachment_id}/uploaded"
end
def self.available_assignees(project_id)
"#{project(project_id)}/available_assignees"
end
def self.available_responsibles(project_id)
"#{project(project_id)}/available_responsibles"
end
def self.available_watchers(work_package_id)
"#{work_package(work_package_id)}/available_watchers"
end
def self.available_projects_on_edit(work_package_id)
"#{work_package(work_package_id)}/available_projects"
end
def self.available_projects_on_create(type_id)
if type_id.to_i.zero?
"#{work_packages}/available_projects"
else
"#{work_packages}/available_projects?for_type=#{type_id}"
end
end
8 years ago
def self.available_relation_candidates(work_package_id)
"#{work_package(work_package_id)}/available_relation_candidates"
end
index :capability
show :capability
def self.capabilities_contexts_global
"#{capabilities}/contexts/global"
end
index :backup
index :category
show :category
def self.categories_by_project(id)
"#{project(id)}/categories"
end
def self.configuration
"#{root}/configuration"
end
def self.create_project_work_package_form(project_id)
"#{work_packages_by_project(project_id)}/form"
end
def self.custom_action(id)
"#{root}/custom_actions/#{id}"
end
def self.custom_action_execute(id)
"#{custom_action(id)}/execute"
end
def self.custom_option(id)
"#{root}/custom_options/#{id}"
end
index :help_text
show :help_text
resources :grid
resources :membership
def self.memberships_available_projects
"#{memberships}/available_projects"
end
index :message
show :message
index :newses, :news
def self.news(id)
"#{newses}/#{id}"
end
[26688] In-app notifications (#9399) * Add bell icon to icon font * Add in app notification in top menu * Add fullscreen modal * Add notification modal and items * Style items * Toggle details of item * Mark all read * Add no results box * wip specification for event api * Add events table, query and index * Send out events from WP notification mailer job There we have the recipients present * Add cleanup job for older events with a setting * Hide bell notification when not logged * Add specs for events API index/show * Fix setting yml key * remove pry in event creation * Fix before hook in events API to after_validation * Fix polymorphic association raising exception for aggregated journals * Fix typo in read_ian * Fix yml entry for mentioned * Add read/unread post actions to event API and add specs * Wire up API to frontend * Fix order on events * Switch to unread in notification * Add event query * rename WPEventService * route wp mail sending over events * rename spec methods * author becomes watcher * correct message call signature * rename events to notifications * renname parameter to reflect notification nature * create author watcher for existing work packages * Merge unreadCount from store * Take a stab at polymorphic representers * Fix link generation in polymorphic resources For journals, no title is being generated however * Fix frontend model for context * Use timer for polling * add notification_setting data layer * Fix show resource spec * Fix duplicate class in notification bell item * Add minimal feature spec for notification * API for notification settings * Persist notifications * adapt work package notification creation to notification settings * extract notified_on_all * consolidate wp#recipients * concentrate wp notification in journal service * simplify methods * Remove unused patch endpoint * Add specs for rendering and parsing notification settings * Contract spec * Update service spec * adapt specs * Angular notifications frontend commit e29dced64699eb5f2443b9307c78343c9a58d1ee Author: Wieland Lindenthal <w.lindenthal@forkmerge.com> Date: Mon Jun 21 17:34:50 2021 +0200 Create Akita store and query for notification settings commit 1a45c26c1a0c147d15393e49d2625aca4851a64d Author: Wieland Lindenthal <w.lindenthal@forkmerge.com> Date: Mon Jun 21 11:09:25 2021 +0200 Remove tabs from notificaition settings page commit 0ea21e90c13a197f8bf2cfba1b60ddcff4e5e827 Author: Oliver Günther <mail@oliverguenther.de> Date: Sun Jun 20 21:55:48 2021 +0200 WIP in app settings * migrate notification data * add project visible filter to project query * Add inline-create and table display grouped by project * Add notifications under admin/users * Remove notifications partial * Rename notififcations store to user preferences store * Add setting for self_notified and hook that up to the backend * Add aria-label to table checkboxes * Restyle table and toolbar * replace remains of mail_notifications attribute * initialize notification settings for new user * adapt my_preferences references * reenable no self notified for documents * adapt specs * Avoid has_many :notifcation_settings Rails magically autosaves the user's preferences when the user gets saved, which somehow also tries to save the notfifications even when unchanged. This breaks some specs such as the avatar upload spec. As we can't update the assocation through rails anyway, just delegate to the user for reading instead. * Restore update method of notification settings * Restore update spec * fix spec syntax * lint scss * linting * Fix content_tag for bell icon * Add feature specs for notification settings * Disable ContentTag cop * use visible filter to get projects for notification The visible filter will reduce the project list down to the set of projects visible to the user provided as a parameter. This includes public projects. * test for actual mail sending * adapt me resource path this.apiV3Service.users.me changed its type in 0d6c0b6bc7620de94e00e72b36d6cbc1ec4c8db4 * Implement changed migration * Linting * Add actor to notification representer * Fix factory creating a duplicate WP journal * Add work packages loading and journal details to notification entry component * IAN basic facets, keep and expanded states. * Fix notification bell spec * Render body separately and add auto updating relative time * Add fixedTime title * Add actor to notification entry * Fix clicking links on work package and project * Tiny styling changes on entry row * Disable count in notification if larger than 99 (wont fit) * Introduce virtual scrolling to entry table * allow delaying & prevent mail sending if ain read Introduces a setting to delay mail sending after a journal aggregation time has expired. That way, users can confirm a notification in app. If they do before the delay expires, no mail is sent out additionally for that user. * consolidate notifications (in&out) into shared admin menu Co-authored-by: ulferts <jens.ulferts@googlemail.com> Co-authored-by: Wieland Lindenthal <w.lindenthal@forkmerge.com>
3 years ago
index :notification
show :notification
def self.notification_read_ian(id)
"#{notification(id)}/readIAN"
end
def self.notification_unread_ian(id)
"#{notification(id)}/unreadIAN"
end
def self.notification_read_email(id)
"#{notification(id)}/readEmail"
end
def self.notification_unread_email(id)
"#{notification(id)}/unreadEmail"
end
index :placeholder_user
show :placeholder_user
index :post
show :post
index :principal
index :priorities
show :priority
class << self
alias :issue_priorities :priorities
alias :issue_priority :priority
end
resources :project
show :project_status
def self.projects_available_parents
"#{projects}/available_parent_projects"
end
[31023] Preparation for project edit fields: Generalize edit and display fields currently work package specific (#7726) * Check edit fields for Work package dependencies * Make EditForm, NotificationService, EditContext indepenedent of Work Packages * Make EventsService independent of WorkPackages * Start renaming WpNotificationService (WiP) * Fix more references * Fix typescript errors * Add basic halEditingService * Rename to global halResourceEditingService (WiP) * Move typing from class to methods (WiP) * Fix typescript errors * Remove space in type * Add test project widget && generalize the editFieldGroup * Rename editing portal service [ci skip] * Rename WpEditFieldComponent * Rename WpDisplayFields * Rename display-XX-field to XX-display-field [ci skip] * Add WP specific ID field to distinguish between resources * Re-add state in work package resource * Generalize display field renderer * Rename spent-time to wp-spent-time and fix highlight specifics [ci skip] * Actually load the project schema and make field editable * Make edit-field-group.component an edit-form.component and subclass EditForm * Remove edit context in favor of specialized EditForm * Add special cases for work package editing * Fix edit actions bar * Fix codeclimate issues * Use WorkPackageNotificationService if necessary * Override NotificationService for WPs to allow WP specififc notifications (WiP) * Correctly provide wpNotification service Because the ui-router doesn't seem to correctly use the parent element's injector, we need to provide the wpNotification service not in the wp-base, but rather the wp-list component as well as in the isolated query space. * Allow to filter halEvents for specific resourceTypes (e.g. WorkPackage) * Remove superfluous cell class constant * Start renaming selectors for wp-edit-field into generics * Remove wp-table--cell-span in favor of display field selector * Consolidate other display and edit field styles * Provide specialized service for transitions in active edit forms * Remove superfluous overflowSelector * Accept that date field contains some work package specifics * Ignore unreadable files * Provide the changeset for work packages as a hook Since hal resource editing service is provided per query space, we cannot register them once (would only work globally) * Fix dangerfile * Remove another todo in halResourceNotification service * Fix npm TestBed for changed dependencies * Show inplace edit field in project details widget * Fix highlighting in single view * Provide HalResourceEditingService outside of project context * Used typedState for single-view * Also provide wpNotification service in split view * Correct check for resource type in eventsService * Fix getSchemaName in display field renderer * Fix passing ids into `halEditing.stopEditing` * Do not globally inject the halResourceEditingService There's a bug(?) in ui-router that gives you the global service before the parent injected service for a ui-view * Fix wpCreate service on copying and parallel creation * Remove test project widget * Revert changes for project details widget
5 years ago
def self.projects_schema
"#{projects}/schema"
end
[34444] Projects copy APIv3 (#9149) * Create copy project endpoint * Add representers * Add _meta representer for copy module information * Add Meta payload representer * Extract parsing of copy attributes into service * Extract enqueue job for projects copy * Keep request object available in the bodied endpoint this allows us to access grape (e.g., for redirecting) * Add DelayedModify endpoint that redirects to job status * Use DelayedModify endpoint for copying projects * Add api paths for form/copy * Disable cache on ProjectCopyPayload * Add spec * Add resource spec * Extend schema for meta * Extend docs * Don't pass service result, but state as meta to forms * Use copy dependencies for naming copy options in API * Add description property for counting * Remove unused action attribute * Skip writable checks on meta property for payloads * Use prepend to allow create(...) usage with meta * Extend copy spec with custom fields usage * Add spec for copy flags * Add todos * Pass errors correctly to copy settings * Remove invalid parameters to copy that are now caught through project service * Remove duplicated validation * Linting * Make count a human readable, formattable description string * Make source count string readable * Provide the source project for generating the counts * Extract copying of attachments into sepearate dependent services This will allow the API to dynamically generate a copy association schema from each dependent service * Add spec for copy schema representer * Add payload representer spec * Also validate the model to pass the validations when copying * Make description optional and add note to schemas.apib * Add send_notifications to representer * Allow to configure sendNotifications * Allow bodied to receive a state process callback That allows us to manage the meta state on the API level, not on the service level * Make other services compatible with BaseCallable * Fix params passing from BaseCallable Wrapping a single hash into kwargs obviously loses their key indifference and we can't expect all services to use a single params object. Older services use kwargs * Copy wiki page with parent_id This will save some memory instead of memoizing the entire wiki page * Fix typo * Default to true for all copy associations That means if only a true value is passed, all other options will still be true * Do not try to copy attachments if the base dependency wasnt copied * Ensure null identifier gets rendered * Fix paths to the form and commit * Change redirect to an URL * Extend documentation on meta properties as table * Ensure we pass a default params if incoming params are nil Now that SetAttributes needs to splat params, they do no longer correctly get the no-param-default behavior of Ruby, so we need to explictly assign a default params hash * Fix expect for trailing path
4 years ago
def self.project_copy(id)
"#{project(id)}/copy"
end
def self.project_copy_form(id)
"#{project(id)}/copy/form"
end
resources :query
def self.query_default
"#{queries}/default"
end
def self.query_project_default(id)
"#{project(id)}/queries/default"
end
def self.query_star(id)
"#{query(id)}/star"
end
def self.query_unstar(id)
"#{query(id)}/unstar"
end
def self.query_order(id)
"#{query(id)}/order"
end
def self.query_column(name)
"#{queries}/columns/#{name}"
end
8 years ago
def self.query_group_by(name)
"#{queries}/group_bys/#{name}"
end
8 years ago
def self.query_sort_by(name, direction)
"#{queries}/sort_bys/#{name}-#{direction}"
end
def self.query_filter(name)
"#{queries}/filters/#{name}"
end
def self.query_filter_instance_schemas
"#{queries}/filter_instance_schemas"
end
def self.query_filter_instance_schema(id)
"#{queries}/filter_instance_schemas/#{id}"
end
def self.query_project_form(id)
"#{project(id)}/queries/form"
end
def self.query_project_filter_instance_schemas(id)
"#{project(id)}/queries/filter_instance_schemas"
end
def self.query_operator(name)
"#{queries}/operators/#{name}"
end
def self.query_project_schema(id)
"#{project(id)}/queries/schema"
end
def self.query_available_projects
"#{queries}/available_projects"
end
index :relations
show :relation
index :revision
show :revision
6 years ago
def self.render_markup(link: nil, plain: false)
format = if plain
OpenProject::TextFormatting::Formats.plain_format
else
OpenProject::TextFormatting::Formats.rich_format
end
path = "#{root}/render/#{format}"
path += "?context=#{link}" if link
path
end
index :role
show :role
def self.show_revision(project_id, identifier)
show_revision_project_repository_path(project_id, identifier)
end
def self.show_user(user_id)
user_path(user_id)
end
index :status
show :status
def self.string_object(value)
val = ::ERB::Util::url_encode(value)
"#{root}/string_objects?value=#{val}"
end
resources :time_entry
def self.time_entries_activity(activity_id)
"#{root}/time_entries/activities/#{activity_id}"
end
def self.time_entries_available_projects
"#{time_entries}/available_projects"
end
def self.time_entries_available_work_packages_on_create
"#{time_entries}/available_work_packages"
end
def self.time_entries_available_work_packages_on_edit(time_entry_id)
"#{time_entry(time_entry_id)}/available_work_packages"
end
index :type
show :type
def self.types_by_project(project_id)
"#{project(project_id)}/types"
end
resources :user
def self.user_lock(id)
"#{user(id)}/lock"
end
[26688] In-app notifications (#9399) * Add bell icon to icon font * Add in app notification in top menu * Add fullscreen modal * Add notification modal and items * Style items * Toggle details of item * Mark all read * Add no results box * wip specification for event api * Add events table, query and index * Send out events from WP notification mailer job There we have the recipients present * Add cleanup job for older events with a setting * Hide bell notification when not logged * Add specs for events API index/show * Fix setting yml key * remove pry in event creation * Fix before hook in events API to after_validation * Fix polymorphic association raising exception for aggregated journals * Fix typo in read_ian * Fix yml entry for mentioned * Add read/unread post actions to event API and add specs * Wire up API to frontend * Fix order on events * Switch to unread in notification * Add event query * rename WPEventService * route wp mail sending over events * rename spec methods * author becomes watcher * correct message call signature * rename events to notifications * renname parameter to reflect notification nature * create author watcher for existing work packages * Merge unreadCount from store * Take a stab at polymorphic representers * Fix link generation in polymorphic resources For journals, no title is being generated however * Fix frontend model for context * Use timer for polling * add notification_setting data layer * Fix show resource spec * Fix duplicate class in notification bell item * Add minimal feature spec for notification * API for notification settings * Persist notifications * adapt work package notification creation to notification settings * extract notified_on_all * consolidate wp#recipients * concentrate wp notification in journal service * simplify methods * Remove unused patch endpoint * Add specs for rendering and parsing notification settings * Contract spec * Update service spec * adapt specs * Angular notifications frontend commit e29dced64699eb5f2443b9307c78343c9a58d1ee Author: Wieland Lindenthal <w.lindenthal@forkmerge.com> Date: Mon Jun 21 17:34:50 2021 +0200 Create Akita store and query for notification settings commit 1a45c26c1a0c147d15393e49d2625aca4851a64d Author: Wieland Lindenthal <w.lindenthal@forkmerge.com> Date: Mon Jun 21 11:09:25 2021 +0200 Remove tabs from notificaition settings page commit 0ea21e90c13a197f8bf2cfba1b60ddcff4e5e827 Author: Oliver Günther <mail@oliverguenther.de> Date: Sun Jun 20 21:55:48 2021 +0200 WIP in app settings * migrate notification data * add project visible filter to project query * Add inline-create and table display grouped by project * Add notifications under admin/users * Remove notifications partial * Rename notififcations store to user preferences store * Add setting for self_notified and hook that up to the backend * Add aria-label to table checkboxes * Restyle table and toolbar * replace remains of mail_notifications attribute * initialize notification settings for new user * adapt my_preferences references * reenable no self notified for documents * adapt specs * Avoid has_many :notifcation_settings Rails magically autosaves the user's preferences when the user gets saved, which somehow also tries to save the notfifications even when unchanged. This breaks some specs such as the avatar upload spec. As we can't update the assocation through rails anyway, just delegate to the user for reading instead. * Restore update method of notification settings * Restore update spec * fix spec syntax * lint scss * linting * Fix content_tag for bell icon * Add feature specs for notification settings * Disable ContentTag cop * use visible filter to get projects for notification The visible filter will reduce the project list down to the set of projects visible to the user provided as a parameter. This includes public projects. * test for actual mail sending * adapt me resource path this.apiV3Service.users.me changed its type in 0d6c0b6bc7620de94e00e72b36d6cbc1ec4c8db4 * Implement changed migration * Linting * Add actor to notification representer * Fix factory creating a duplicate WP journal * Add work packages loading and journal details to notification entry component * IAN basic facets, keep and expanded states. * Fix notification bell spec * Render body separately and add auto updating relative time * Add fixedTime title * Add actor to notification entry * Fix clicking links on work package and project * Tiny styling changes on entry row * Disable count in notification if larger than 99 (wont fit) * Introduce virtual scrolling to entry table * allow delaying & prevent mail sending if ain read Introduces a setting to delay mail sending after a journal aggregation time has expired. That way, users can confirm a notification in app. If they do before the delay expires, no mail is sent out additionally for that user. * consolidate notifications (in&out) into shared admin menu Co-authored-by: ulferts <jens.ulferts@googlemail.com> Co-authored-by: Wieland Lindenthal <w.lindenthal@forkmerge.com>
3 years ago
def self.user_preferences(id)
"#{user(id)}/preferences"
end
index :group
show :group
resources :version
def self.versions_available_projects
"#{versions}/available_projects"
end
def self.versions_by_project(project_id)
"#{project(project_id)}/versions"
end
def self.projects_by_version(version_id)
"#{version(version_id)}/projects"
end
def self.watcher(id, work_package_id)
"#{work_package_watchers(work_package_id)}/#{id}"
end
def self.wiki_page(id)
"#{root}/wiki_pages/#{id}"
end
resources :work_package, except: :schema
def self.work_package_schema(project_id, type_id)
"#{root}/work_packages/schemas/#{project_id}-#{type_id}"
end
def self.work_package_activities(id)
"#{work_package(id)}/activities"
end
def self.work_package_relations(id)
"#{work_package(id)}/relations"
end
def self.work_package_relation(id, work_package_id)
"#{work_package_relations(work_package_id)}/#{id}"
end
def self.work_package_available_relation_candidates(id)
"#{work_package(id)}/available_relation_candidates"
end
def self.work_package_revisions(id)
"#{work_package(id)}/revisions"
end
def self.work_package_schemas(*args)
path = "#{root}/work_packages/schemas"
if args.empty?
path
else
values = args.map do |project_id, type_id|
"#{project_id}-#{type_id}"
end
filter = [{ id: { operator: '=', values: values } }]
path + "?filters=#{CGI.escape(filter.to_s)}"
end
end
def self.work_package_sums_schema
"#{root}/work_packages/schemas/sums"
end
def self.work_package_watchers(id)
"#{work_package(id)}/watchers"
end
def self.work_packages_by_project(project_id)
"#{project(project_id)}/work_packages"
end
def self.path_for(path, filters: nil, sort_by: nil, page_size: nil)
query_params = {
filters: filters&.to_json,
sortBy: sort_by&.to_json,
pageSize: page_size
}.reject { |_, v| v.blank? }
if query_params.any?
"#{send(path)}?#{query_params.to_query}"
else
send(path)
end
end
def self.url_for(path, arguments = nil)
duplicate_regexp = if OpenProject::Configuration.rails_relative_url_root
Regexp.new("#{OpenProject::Configuration.rails_relative_url_root}/$")
else
Regexp.new("/$")
end
root_url = OpenProject::StaticRouting::StaticUrlHelpers.new.root_url
root_url.gsub(duplicate_regexp, '') + send(path, arguments)
end
end
def api_v3_paths
::API::V3::Utilities::PathHelper::ApiV3Path
end
end
end
end
end