diff --git a/app/assets/stylesheets/openproject/_generic.sass b/app/assets/stylesheets/openproject/_generic.sass
index 32cb206992..5582932d30 100644
--- a/app/assets/stylesheets/openproject/_generic.sass
+++ b/app/assets/stylesheets/openproject/_generic.sass
@@ -92,3 +92,11 @@
.-required-highlighting
border: 1px solid red
+
+.-no-width
+ display: block
+ width: 0
+
+.-no-height
+ display: block
+ height: 0
diff --git a/app/helpers/work_packages_helper.rb b/app/helpers/work_packages_helper.rb
index 0ece0f99ff..ed83dfe5f6 100644
--- a/app/helpers/work_packages_helper.rb
+++ b/app/helpers/work_packages_helper.rb
@@ -151,65 +151,6 @@ module WorkPackagesHelper
end.html_safe
end
- def work_package_quick_info(work_package, only_path: true)
- changed_dates = {}
-
- journals = work_package.journals.where(['created_at >= ?', Date.today.to_time - 7.day])
- .order(Arel.sql('created_at desc'))
-
- journals.each do |journal|
- break if changed_dates['start_date'] && changed_dates['due_date']
-
- ['start_date', 'due_date'].each do |date|
- if changed_dates[date].nil? &&
- journal.details[date] &&
- journal.details[date].first
- changed_dates[date] = " (#{journal.details[date].first})".html_safe
- end
- end
- end
-
-
- link = link_to_work_package(work_package, status: true, only_path: only_path)
-
- # Don't print dates if neither start nor due set
- start = work_package.start_date&.to_s
- due = work_package.due_date&.to_s
-
- if start.nil? && due.nil?
- return link
- end
-
- # Otherwise, print concise
- # (2018-01-01 -)
- # (- 2018-01-01)
- # (2018-01-01 - 2018-01-01)
- link <<
- if start.nil?
- " (- #{due})"
- elsif due.nil?
- " (#{start} -)"
- else
- " (#{start} - #{due})"
- end
-
- link
- end
-
- def work_package_quick_info_with_description(work_package, lines = 3, only_path: true)
- description = truncated_work_package_description(work_package, lines)
-
- link = work_package_quick_info(work_package, only_path: only_path)
-
- attributes = info_user_attributes(work_package)
-
- link += content_tag(:div, attributes, class: 'indent quick_info attributes')
-
- link += content_tag(:div, description, class: 'indent quick_info description')
-
- link
- end
-
def work_package_list(work_packages, &_block)
ancestors = []
work_packages.each do |work_package|
@@ -238,7 +179,7 @@ module WorkPackagesHelper
# Returns a string of css classes that apply to the issue
def work_package_css_classes(work_package)
# TODO: remove issue once css is cleaned of it
- s = 'issue work_package'.html_safe
+ s = 'issue work_package preview-trigger'.html_safe
s << " status-#{work_package.status.position}" if work_package.status
s << " priority-#{work_package.priority.position}" if work_package.priority
s << ' closed' if work_package.closed?
diff --git a/frontend/src/app/angular4-modules.ts b/frontend/src/app/angular4-modules.ts
index 056aa716f0..e8236c3a72 100644
--- a/frontend/src/app/angular4-modules.ts
+++ b/frontend/src/app/angular4-modules.ts
@@ -89,6 +89,8 @@ import {FormsCacheService} from "core-components/forms/forms-cache.service";
import {OpenprojectAdminModule} from "core-app/modules/admin/openproject-admin.module";
import {OpenprojectDashboardsModule} from "core-app/modules/dashboards/openproject-dashboards.module";
import {OpenprojectWorkPackageGraphsModule} from "core-app/modules/work-package-graphs/openproject-work-package-graphs.module";
+import {WpPreviewModal} from "core-components/modals/preview-modal/wp-preview-modal/wp-preview.modal";
+import {PreviewTriggerService} from "core-app/globals/global-listeners/preview-trigger.service";
@NgModule({
imports: [
@@ -172,6 +174,7 @@ import {OpenprojectWorkPackageGraphsModule} from "core-app/modules/work-package-
// Augmenting Rails
ModalWrapperAugmentService,
+ PreviewTriggerService,
],
declarations: [
OpContextMenuTrigger,
@@ -180,6 +183,7 @@ import {OpenprojectWorkPackageGraphsModule} from "core-app/modules/work-package-
ConfirmDialogModal,
DynamicContentModal,
PasswordConfirmationModal,
+ WpPreviewModal,
// Main menu
MainMenuResizerComponent,
@@ -202,6 +206,7 @@ import {OpenprojectWorkPackageGraphsModule} from "core-app/modules/work-package-
ConfirmDialogModal,
PasswordConfirmationModal,
AttributeHelpTextModal,
+ WpPreviewModal,
// Main menu
MainMenuResizerComponent,
@@ -233,6 +238,7 @@ export function initializeServices(injector:Injector) {
const ExternalQueryConfiguration = injector.get(ExternalQueryConfigurationService);
const ExternalRelationQueryConfiguration = injector.get(ExternalRelationQueryConfigurationService);
const ModalWrapper = injector.get(ModalWrapperAugmentService);
+ const PreviewTrigger = injector.get(PreviewTriggerService);
const EditorMacros = injector.get(EditorMacrosService);
const mainMenuNavigationService = injector.get(MainMenuNavigationService);
@@ -241,6 +247,8 @@ export function initializeServices(injector:Injector) {
// Setup modal wrapping
ModalWrapper.setupListener();
+ PreviewTrigger.setupListener();
+
// Setup query configuration listener
ExternalQueryConfiguration.setupListener();
ExternalRelationQueryConfiguration.setupListener();
diff --git a/frontend/src/app/components/modals/preview-modal/wp-preview-modal/wp-preview.modal.html b/frontend/src/app/components/modals/preview-modal/wp-preview-modal/wp-preview.modal.html
new file mode 100644
index 0000000000..553ca424dd
--- /dev/null
+++ b/frontend/src/app/components/modals/preview-modal/wp-preview-modal/wp-preview.modal.html
@@ -0,0 +1,24 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/frontend/src/app/components/modals/preview-modal/wp-preview-modal/wp-preview.modal.sass b/frontend/src/app/components/modals/preview-modal/wp-preview-modal/wp-preview.modal.sass
new file mode 100644
index 0000000000..69d97caba6
--- /dev/null
+++ b/frontend/src/app/components/modals/preview-modal/wp-preview-modal/wp-preview.modal.sass
@@ -0,0 +1,36 @@
+@import "helpers"
+
+.preview-modal--container
+ position: absolute
+ z-index: 5000
+ min-width: 200px
+ max-width: 400px
+ min-height: 100px
+ max-height: 300px
+ background: white
+ padding: 15px
+ border: 1px solid lightgrey
+ border-radius: 3px
+ box-shadow: 0px 0px 5px 0px rgba(0, 0, 0, 0.5)
+
+ display: grid
+ grid-template: auto auto auto / auto auto 1fr 1fr
+ grid-column-gap: 5px
+ grid-row-gap: 10px
+ grid-template-areas: "type subject subject subject" "author author author author" "status status . assignee"
+
+.status
+ grid-area: status
+ padding: 0px 10px
+
+.type
+ grid-area: type
+
+.author
+ grid-area: author
+
+.subject
+ grid-area: subject
+
+.assignee
+ grid-area: assignee
diff --git a/frontend/src/app/components/modals/preview-modal/wp-preview-modal/wp-preview.modal.ts b/frontend/src/app/components/modals/preview-modal/wp-preview-modal/wp-preview.modal.ts
new file mode 100644
index 0000000000..db287ad5dc
--- /dev/null
+++ b/frontend/src/app/components/modals/preview-modal/wp-preview-modal/wp-preview.modal.ts
@@ -0,0 +1,80 @@
+// -- copyright
+// OpenProject is a project management system.
+// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
+//
+// 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 doc/COPYRIGHT.rdoc for more details.
+// ++
+
+import {ChangeDetectionStrategy, ChangeDetectorRef, Component, ElementRef, Inject, OnInit} from "@angular/core";
+import {OpModalComponent} from "core-components/op-modals/op-modal.component";
+import {OpModalLocalsToken, OpModalService} from "core-components/op-modals/op-modal.service";
+import {OpModalLocalsMap} from "core-components/op-modals/op-modal.types";
+import {I18nService} from "core-app/modules/common/i18n/i18n.service";
+import {WorkPackageResource} from "core-app/modules/hal/resources/work-package-resource";
+import {WorkPackageDmService} from "core-app/modules/hal/dm-services/work-package-dm.service";
+import {HalResource} from "core-app/modules/hal/resources/hal-resource";
+import {Highlighting} from "core-components/wp-fast-table/builders/highlighting/highlighting.functions";
+
+@Component({
+ templateUrl: './wp-preview.modal.html',
+ styleUrls: ['./wp-preview.modal.sass'],
+ changeDetection: ChangeDetectionStrategy.OnPush,
+})
+export class WpPreviewModal extends OpModalComponent implements OnInit {
+ public workPackage:WorkPackageResource;
+
+ public text = {
+ created_by: this.i18n.t('js.label_created_by'),
+ };
+
+ constructor(readonly elementRef:ElementRef,
+ @Inject(OpModalLocalsToken) readonly locals:OpModalLocalsMap,
+ readonly cdRef:ChangeDetectorRef,
+ readonly i18n:I18nService,
+ readonly workPackageDmService:WorkPackageDmService,
+ readonly opModalService:OpModalService) {
+ super(locals, cdRef, elementRef);
+ }
+
+
+ ngOnInit() {
+ super.ngOnInit();
+ const workPackageLink = this.locals.workPackageLink;
+ const workPackageId = HalResource.idFromLink(workPackageLink);
+
+ this.workPackageDmService.loadWorkPackageById(workPackageId)
+ .then((workPackage:WorkPackageResource) => {
+ this.workPackage = workPackage;
+ this.cdRef.detectChanges();
+ });
+ }
+
+ highlightingColor(resource:HalResource, background:boolean = false) {
+ if (background) {
+ return Highlighting.backgroundClass(resource.$halType.toLowerCase(), resource.id!);
+ } else {
+ return Highlighting.inlineClass(resource.$halType.toLowerCase(), resource.id!);
+ }
+ }
+}
diff --git a/frontend/src/app/globals/global-listeners/preview-trigger.service.ts b/frontend/src/app/globals/global-listeners/preview-trigger.service.ts
new file mode 100644
index 0000000000..d173b07d05
--- /dev/null
+++ b/frontend/src/app/globals/global-listeners/preview-trigger.service.ts
@@ -0,0 +1,88 @@
+// -- copyright
+// OpenProject is a project management system.
+// Copyright (C) 2012-2015 the OpenProject Foundation (OPF)
+//
+// 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 doc/COPYRIGHT.rdoc for more details.
+// ++
+
+
+import {Injectable, Injector} from "@angular/core";
+import {OpModalService} from "core-components/op-modals/op-modal.service";
+import {WpPreviewModal} from "core-components/modals/preview-modal/wp-preview-modal/wp-preview.modal";
+import {OpModalComponent} from "core-components/op-modals/op-modal.component";
+
+@Injectable()
+export class PreviewTriggerService {
+ private previewModal:OpModalComponent;
+ private modalElement:HTMLElement;
+
+ constructor(readonly opModalService:OpModalService,
+ readonly injector:Injector) {
+ }
+
+ setupListener() {
+ jQuery(document.body).on('mouseenter', '.preview-trigger', (e) => {
+ e.preventDefault();
+ e.stopPropagation();
+ const el = jQuery(e.target);
+
+ this.previewModal = this.opModalService.show(WpPreviewModal, this.injector, { workPackageLink: el.attr("href") });
+ this.modalElement = this.previewModal.elementRef.nativeElement;
+ jQuery(this.modalElement).position({
+ my: 'left top',
+ at: 'left bottom',
+ of: el,
+ collision: 'flipfit'
+ });
+
+ jQuery(this.modalElement).addClass('-no-width -no-height');
+ });
+
+ jQuery(document.body).on('mouseleave', '.preview-trigger', (e:JQueryEventObject) => {
+ e.preventDefault();
+ e.stopPropagation();
+
+ if (this.isMouseOverPreview(e)) {
+ jQuery(this.modalElement).on('mouseleave', () => {
+ this.opModalService.close();
+ });
+ } else {
+ this.opModalService.close();
+ }
+ });
+ }
+
+ private isMouseOverPreview(evt:JQueryEventObject) {
+ const previewElement = jQuery(this.modalElement.children[0]);
+ if (previewElement && previewElement.offset()) {
+ let horizontalHover = evt.pageX >= Math.floor(previewElement.offset()!.left) &&
+ evt.pageX < previewElement.offset()!.left + previewElement.width()!;
+ let verticalHover = evt.pageY >= Math.floor(previewElement.offset()!.top) &&
+ evt.pageY < previewElement.offset()!.top + previewElement.height()!;
+ return horizontalHover && verticalHover;
+ }
+ return false;
+ }
+
+}
diff --git a/lib/open_project/text_formatting/matchers/link_handlers/colon_separator.rb b/lib/open_project/text_formatting/matchers/link_handlers/colon_separator.rb
index 3363b931f7..b196c83748 100644
--- a/lib/open_project/text_formatting/matchers/link_handlers/colon_separator.rb
+++ b/lib/open_project/text_formatting/matchers/link_handlers/colon_separator.rb
@@ -82,7 +82,7 @@ module OpenProject::TextFormatting::Matchers
private
def render_version
- if project && (version = project.versions.visible.find_by(name: oid))
+ if project && (version = project.versions.find_by(name: oid))
link_to h(version.name),
{ only_path: context[:only_path], controller: '/versions', action: 'show', id: version },
class: 'version'
@@ -90,8 +90,9 @@ module OpenProject::TextFormatting::Matchers
end
def render_commit
- if project&.repository && (changeset = Changeset.visible.where(['repository_id = ? AND scmid LIKE ?', project.repository.id, "#{oid}%"]).first)
- link_to h("#{project_prefix}#{name}"),
+ if project&.repository &&
+ (changeset = Changeset.where(['repository_id = ? AND scmid LIKE ?', project.repository.id, "#{oid}%"]).first)
+ link_to h("#{matcher.project_prefix}#{matcher.identifier}"),
{ only_path: context[:only_path], controller: '/repositories', action: 'revision', project_id: project, rev: changeset.identifier },
class: 'changeset',
title: truncate_single_line(changeset.comments, length: 100)
@@ -99,7 +100,7 @@ module OpenProject::TextFormatting::Matchers
end
def render_source
- if project&.repository && User.current.allowed_to?(:browse_repository, project)
+ if project&.repository
matcher.identifier =~ %r{\A[/\\]*(.*?)(@([0-9a-f]+))?(#(L\d+))?\z}
path = $1
rev = $3
@@ -128,7 +129,6 @@ module OpenProject::TextFormatting::Matchers
def render_project
p = Project
- .visible
.where(['projects.identifier = :s OR LOWER(projects.name) = :s', { s: oid.downcase }])
.first
if p
@@ -137,7 +137,7 @@ module OpenProject::TextFormatting::Matchers
end
def render_user
- if (user = User.in_visible_project.find_by(login: oid))
+ if (user = User.find_by(login: oid))
link_to_user(user, only_path: context[:only_path], class: 'user-mention')
end
end
diff --git a/lib/open_project/text_formatting/matchers/link_handlers/hash_separator.rb b/lib/open_project/text_formatting/matchers/link_handlers/hash_separator.rb
index 27f0c35f8a..d82ec3ac93 100644
--- a/lib/open_project/text_formatting/matchers/link_handlers/hash_separator.rb
+++ b/lib/open_project/text_formatting/matchers/link_handlers/hash_separator.rb
@@ -60,7 +60,7 @@ module OpenProject::TextFormatting::Matchers
private
def render_version
- version = Version.visible.find_by(id: oid)
+ version = Version.find_by(id: oid)
if version
link_to h(version.name),
{ only_path: context[:only_path], controller: '/versions', action: 'show', id: version },
@@ -69,21 +69,21 @@ module OpenProject::TextFormatting::Matchers
end
def render_message
- message = Message.visible.includes(:parent).find_by(id: oid)
+ message = Message.includes(:parent).find_by(id: oid)
if message
link_to_message(message, { only_path: context[:only_path] }, class: 'message')
end
end
def render_project
- p = Project.visible.find_by(id: oid)
+ p = Project.find_by(id: oid)
if p
link_to_project(p, { only_path: context[:only_path] }, class: 'project')
end
end
def render_user
- user = User.in_visible_project.find_by(id: oid)
+ user = User.find_by(id: oid)
if user
link_to_user(user, only_path: context[:only_path], class: 'user-mention')
end
diff --git a/lib/open_project/text_formatting/matchers/link_handlers/revisions.rb b/lib/open_project/text_formatting/matchers/link_handlers/revisions.rb
index f2fb082419..09df28602f 100644
--- a/lib/open_project/text_formatting/matchers/link_handlers/revisions.rb
+++ b/lib/open_project/text_formatting/matchers/link_handlers/revisions.rb
@@ -32,8 +32,8 @@ module OpenProject::TextFormatting::Matchers
module LinkHandlers
class Revisions < Base
##
- # Match work package links.
- # Condition: Separator is #|##|###
+ # Match revision links.
+ # Condition: Separator is r
# Condition: Prefix is nil
def applicable?
matcher.prefix.nil? && matcher.sep == 'r'
@@ -42,14 +42,13 @@ module OpenProject::TextFormatting::Matchers
#
# Examples:
#
- # #1234, ##1234, ###1234
+ # r11, r13
def call
# don't handle link unless repository exists
return nil unless project && project.repository
- changeset = Changeset.visible.find_by(repository_id: project.repository.id, revision: matcher.identifier)
+ changeset = project.repository.find_changeset_by_name(matcher.identifier)
- # don't handle link unless changeset can be seen
if changeset
link_to(h("#{matcher.project_prefix}r#{matcher.identifier}"),
{ only_path: context[:only_path], controller: '/repositories', action: 'revision', project_id: project, rev: changeset.revision },
diff --git a/lib/open_project/text_formatting/matchers/link_handlers/work_packages.rb b/lib/open_project/text_formatting/matchers/link_handlers/work_packages.rb
index 935b8ab1a5..e791a52799 100644
--- a/lib/open_project/text_formatting/matchers/link_handlers/work_packages.rb
+++ b/lib/open_project/text_formatting/matchers/link_handlers/work_packages.rb
@@ -65,23 +65,14 @@ module OpenProject::TextFormatting::Matchers
end
def render_work_package_link(work_package)
- if matcher.sep == '##'
- return work_package_quick_info(work_package, only_path: context[:only_path])
- elsif matcher.sep == '###' && !context[:no_nesting]
- return work_package_quick_info_with_description(work_package, only_path: context[:only_path])
- end
-
- if matcher.sep == '#' || (matcher.sep == '###' && context[:no_nesting])
- link_to("#{matcher.sep}#{work_package.id}",
- work_package_path_or_url(id: work_package.id, only_path: context[:only_path]),
- class: work_package_css_classes(work_package),
- title: "#{truncate(work_package.subject, escape: false, length: 100)} (#{work_package.status.try(:name)})")
- end
+ link_to("##{work_package.id}",
+ work_package_path_or_url(id: work_package.id, only_path: context[:only_path]),
+ class: work_package_css_classes(work_package),
+ title: "#{truncate(work_package.subject, escape: false, length: 100)} (#{work_package.status.try(:name)})")
end
def find_work_package(oid)
WorkPackage
- .visible
.includes(:status)
.references(:statuses)
.find_by(id: oid)
diff --git a/spec/lib/api/v3/repositories/revision_representer_spec.rb b/spec/lib/api/v3/repositories/revision_representer_spec.rb
index a9f1da8909..77fffc5414 100644
--- a/spec/lib/api/v3/repositories/revision_representer_spec.rb
+++ b/spec/lib/api/v3/repositories/revision_representer_spec.rb
@@ -95,7 +95,7 @@ describe ::API::V3::Repositories::RevisionRepresenter do
id = work_package.id
str = 'Totally references "
str << "##{id}"
@@ -104,7 +104,7 @@ describe ::API::V3::Repositories::RevisionRepresenter do
before do
allow(User).to receive(:current).and_return(FactoryBot.build_stubbed(:admin))
allow(WorkPackage)
- .to receive_message_chain('visible.includes.references.find_by')
+ .to receive_message_chain('includes.references.find_by')
.and_return(work_package)
end
diff --git a/spec/lib/open_project/text_formatting/markdown/markdown_spec.rb b/spec/lib/open_project/text_formatting/markdown/markdown_spec.rb
index 35439dc90a..ea4ad07400 100644
--- a/spec/lib/open_project/text_formatting/markdown/markdown_spec.rb
+++ b/spec/lib/open_project/text_formatting/markdown/markdown_spec.rb
@@ -101,16 +101,8 @@ describe OpenProject::TextFormatting,
before do
allow(project).to receive(:repository).and_return(repository)
- changesets = [changeset1, changeset2]
-
- allow(Changeset).to receive(:visible).and_return(changesets)
-
- changesets.each do |changeset|
- allow(changesets)
- .to receive(:find_by)
- .with(repository_id: project.repository.id, revision: changeset.revision)
- .and_return(changeset)
- end
+ allow(repository).to receive(:find_changeset_by_name).with(changeset1.revision).and_return(changeset1)
+ allow(repository).to receive(:find_changeset_by_name).with(changeset2.revision).and_return(changeset2)
end
context 'Single link' do
@@ -222,7 +214,7 @@ describe OpenProject::TextFormatting,
let(:issue_link) do
link_to("##{issue.id}",
work_package_path(issue),
- class: 'issue work_package status-3 priority-1 created-by-me', title: "#{issue.subject} (#{issue.status})")
+ class: 'issue work_package preview-trigger status-3 priority-1 created-by-me', title: "#{issue.subject} (#{issue.status})")
end
context 'Plain issue link' do
@@ -231,55 +223,6 @@ describe OpenProject::TextFormatting,
it { is_expected.to be_html_eql("#{issue_link}, [#{issue_link}], (#{issue_link}) and #{issue_link}.
") }
end
- describe 'quickinfo' do
- subject { format_text("###{issue.id}") }
-
- let(:issue) do
- FactoryBot.create :work_package,
- project: project,
- author: project_member,
- type: project.types.first,
- start_date: start_date,
- due_date: due_date
- end
-
- context 'no dates' do
- let(:start_date) { nil }
- let(:due_date) { nil }
-
- it 'prints no quickinfo with dates' do
- puts subject
- end
- end
-
- context 'start date' do
- let(:start_date) { Date.today }
- let(:due_date) { nil }
-
- it 'prints no quickinfo with dates' do
- expect(subject).to include "(#{start_date.to_s} -)"
- end
- end
-
- context 'due date' do
- let(:start_date) { nil }
- let(:due_date) { Date.today }
-
- it 'prints quickinfo with start date' do
- expect(subject).to include "(- #{due_date.to_s})"
- end
- end
-
- context 'both date' do
- let(:start_date) { Date.today }
- let(:due_date) { Date.today + 1.day }
-
- it 'prints quickinfo with dates' do
- expect(subject).to include "(#{start_date.to_s} - #{due_date.to_s})"
- end
- end
- end
-
context 'Plain issue link with braces' do
subject { format_text("foo (bar ##{issue.id})") }
@@ -312,7 +255,7 @@ describe OpenProject::TextFormatting,
let(:issue_link) do
link_to("##{issue.id}",
work_package_path(issue),
- class: 'issue work_package status-3 priority-1 created-by-me',
+ class: 'issue work_package preview-trigger status-3 priority-1 created-by-me',
title: "#{issue.subject} (#{issue.status})")
end
@@ -320,28 +263,6 @@ describe OpenProject::TextFormatting,
it { is_expected.to be_html_eql("#{issue_link}
") }
end
- context 'Cyclic Description Links' do
- let(:issue2) do
- FactoryBot.create :work_package,
- project: project,
- author: project_member,
- type: project.types.first
- end
-
- before do
- issue2.description = "####{issue.id}"
- issue2.save!
- issue.description = "####{issue2.id}"
- issue.save!
- end
-
- subject { format_text issue, :description }
-
- it "doesn't replace description links with a cycle" do
- expect(subject).to match("###{issue.id}")
- end
- end
-
context 'Description links' do
subject { format_text issue, :description }
@@ -402,7 +323,7 @@ describe OpenProject::TextFormatting,
subject { format_text("user##{linked_project_member.id}") }
it {
- is_expected.to be_html_eql("user##{linked_project_member.id}
")
+ is_expected.to be_html_eql("#{link_to(linked_project_member.name, { controller: :users, action: :show, id: linked_project_member.id }, title: "User #{linked_project_member.name}", class: 'user-mention')}
")
}
end
end
@@ -434,7 +355,7 @@ describe OpenProject::TextFormatting,
subject { format_text("user:\"#{linked_project_member.login}\"") }
it {
- is_expected.to be_html_eql("user:\"#{linked_project_member.login}\"
")
+ is_expected.to be_html_eql("#{link_to(linked_project_member.name, { controller: :users, action: :show, id: linked_project_member.id }, title: "User #{linked_project_member.name}", class: 'user-mention')}
")
}
end
end
@@ -706,7 +627,7 @@ describe OpenProject::TextFormatting,
let(:expected) do
<<~EXPECTED
CookBook documentation
- ##{issue.id}
+ ##{issue.id}
[[CookBook documentation]]
diff --git a/spec/requests/api/v3/render_resource_spec.rb b/spec/requests/api/v3/render_resource_spec.rb
index 7a4b4c2034..4de97d87fc 100644
--- a/spec/requests/api/v3/render_resource_spec.rb
+++ b/spec/requests/api/v3/render_resource_spec.rb
@@ -83,7 +83,7 @@ describe 'API v3 Render resource', type: :request do
let(:title) { "#{work_package.subject} (#{work_package.status})" }
let(:text) {
'Hello World! Have a look at ##{id}
"
}