Skip to content

Commit

Permalink
Merge release/11.3 into dev
Browse files Browse the repository at this point in the history
  • Loading branch information
oliverguenther committed Jun 9, 2021
1 parent b210113 commit dcfa053
Show file tree
Hide file tree
Showing 13 changed files with 61 additions and 63 deletions.
2 changes: 1 addition & 1 deletion config/locales/crowdin/js-ru.yml
Expand Up @@ -289,7 +289,7 @@ ru:
bim:
learn_about_link: https://www.openproject.org/openproject-11-3-release
current_new_feature_html: >
The release contains various new features and improvements: <br> <ul class="%{list_styling_class}"> <li>BIM: Improved performance of the IFC model viewer.</li> <li>BIM: The IFC viewer now supports double precision models, which is great for large construction sites such as bridges, roads etc.</li> <li>BIM: Toggle button for switching between perspective and orthogonal mode.</li> <li>BIM: A menu for editing, deleting or flipping section planes.</li> <li>New button in header navigation to create projects, users and work packages.</li> <li>A new invite modal for users, groups and placeholder users allows to easily add or invite new users and assign them to work packages.</li> <li>API v3 extensions for groups and more, i.e. create, read, update and delete groups and group members through the API.</li> <li>Multi-selection for project custom fields of type list.</li> <li>Creation of backup from the web interface.</li> </ul>
Релиз содержит различные новые функции и улучшения: <br> <ul class="%{list_styling_class}"> <li>BIM: Улучшенная производительность просмотра моделей IFC.</li> <li>BIM: Средство просмотра IFC теперь поддерживает двойную точность моделей, что отлично подходит для больших участков строительства, таких как мосты, дороги и т.д.</li> <li>BIM: Переключить кнопку для переключения между перспективным и ортогональным режимом.</li> <li>BIM: Меню для редактирования, удаления или отражения плоскостей разделов.</li> <li>Новая кнопка в навигации по заголовкам для создания проектов, пользователей и пакетов работ.</li> <li>Новое приглашение для пользователей, группы и заполнители позволяют легко добавлять или приглашать новых пользователей и назначать их к рабочим пакетам.</li> <li>расширения API v3 для групп и более, то есть создание, чтение, обновление и удаление групп и членов групп через API.</li> <li>Мульти-выбор пользовательских полей проекта списка типов.</li> <li>Создание резервной копии из веб-интерфейса.</li> </ul>
label_activate: "Активировать"
label_add_column_after: "Добавить столбец после"
label_add_column_before: "Добавить столбец перед"
Expand Down
2 changes: 1 addition & 1 deletion config/puma.rb
Expand Up @@ -21,7 +21,7 @@
# Workers do not work on JRuby or Windows (both of which do not support
# processes).
#
workers ENV.fetch("OPENPROJECT_WEB_WORKERS") { 1 }.to_i
workers ENV.fetch("OPENPROJECT_WEB_WORKERS") { 0 }.to_i

# Use the `preload_app!` method when specifying a `workers` number.
# This directive tells Puma to first boot the application and load code
Expand Down
Expand Up @@ -31,6 +31,7 @@ import { APIv3UserPaths } from "core-app/core/apiv3/endpoints/users/apiv3-user-p
import { Observable } from "rxjs";
import { UserResource } from "core-app/features/hal/resources/user-resource";
import { APIV3Service } from "core-app/core/apiv3/api-v3.service";
import { APIv3FormResource } from "core-app/core/apiv3/forms/apiv3-form-resource";

export class Apiv3UsersPaths extends APIv3ResourceCollection<UserResource, APIv3UserPaths> {
constructor(protected apiRoot:APIV3Service,
Expand All @@ -43,6 +44,9 @@ export class Apiv3UsersPaths extends APIv3ResourceCollection<UserResource, APIv3
// /api/v3/users/me
public readonly me = this.path + '/me';

// /api/v3/users/form
public readonly form = this.subResource('form', APIv3FormResource);

/**
* Create a new UserResource
*
Expand Down
4 changes: 2 additions & 2 deletions frontend/src/app/core/current-user/current-user.service.ts
Expand Up @@ -153,7 +153,7 @@ export class CurrentUserService {
return this.capabilitiesForContext$(contextId).pipe(
map((capabilities) => actions.reduce(
(acc, action) => {
return acc && !!capabilities.find(cap => cap.action.href === `/api/v3/actions/${action}`);
return acc && !!capabilities.find(cap => cap.action.href.endsWith(`/api/v3/actions/${action}`));
},
capabilities.length > 0,
)),
Expand All @@ -168,7 +168,7 @@ export class CurrentUserService {
const actionsToFilter = _.castArray(actions);
return this.capabilitiesForContext$(contextId).pipe(
map((capabilities) => capabilities.reduce(
(acc, cap) => acc || !!actionsToFilter.find(action => cap.action.href === `/api/v3/actions/${action}`),
(acc, cap) => acc || !!actionsToFilter.find(action => cap.action.href.endsWith(`/api/v3/actions/${action}`)),
false,
)),
distinctUntilChanged(),
Expand Down
Expand Up @@ -25,7 +25,7 @@ export function initializeServices(injector:Injector) {
return function () {
const inviteUserAugmentService = injector.get(OpInviteUserModalAugmentService);
inviteUserAugmentService.setupListener();
}
};
}

@NgModule({
Expand Down
Expand Up @@ -55,7 +55,7 @@
*ngIf="isNewPrincipal && type === PrincipalType.User && userDynamicFieldConfig.schema"
[dynamicFormGroup]="dynamicFieldsControl"
[settings]="userDynamicFieldConfig"
[resourcePath]="pathHelper.usersPath()"
[formUrl]="apiV3Service.users.form.path"
[handleSubmit]="false"
></op-dynamic-form>
</div>
Expand Down
@@ -1,27 +1,17 @@
import {
Component,
OnInit,
Input,
Output,
EventEmitter,
ViewChild,
ChangeDetectorRef,
} from '@angular/core';
import { ChangeDetectorRef, Component, EventEmitter, Input, OnInit, Output, ViewChild, } from '@angular/core';
import { HttpClient } from "@angular/common/http";
import {
FormGroup,
FormControl,
Validators,
} from '@angular/forms';
import { PathHelperService } from "core-app/core/path-helper/path-helper.service";
import { FormControl, FormGroup, Validators, } from '@angular/forms';
import { I18nService } from "core-app/core/i18n/i18n.service";
import { HalResource } from "core-app/features/hal/resources/hal-resource";
import { PrincipalData, PrincipalLike } from "core-app/shared/components/principal/principal-types";
import { ProjectResource } from "core-app/features/hal/resources/project-resource";
import { DynamicFormComponent } from "core-app/shared/components/dynamic-forms/components/dynamic-form/dynamic-form.component"
import { PrincipalType } from '../invite-user.component';
import { take } from 'rxjs/internal/operators/take';
import { map } from 'rxjs/operators';
import { APIV3Service } from "core-app/core/apiv3/api-v3.service";

function extractCustomFieldsFromSchema(schema: IOPFormSettings['_embedded']['schema']) {
function extractCustomFieldsFromSchema(schema:IOPFormSettings['_embedded']['schema']) {
return Object.keys(schema)
.reduce((fields, name) => {
if (name.startsWith('customField') && schema[name].required) {
Expand Down Expand Up @@ -49,7 +39,7 @@ export class PrincipalComponent implements OnInit {
@Output() save = new EventEmitter<{ principalData:PrincipalData, isAlreadyMember:boolean }>();
@Output() back = new EventEmitter();

@ViewChild(DynamicFormComponent) dynamicForm: DynamicFormComponent;
@ViewChild(DynamicFormComponent) dynamicForm:DynamicFormComponent;

public PrincipalType = PrincipalType;

Expand Down Expand Up @@ -77,13 +67,13 @@ export class PrincipalComponent implements OnInit {
};

public principalForm = new FormGroup({
principal: new FormControl(null, [ Validators.required ]),
principal: new FormControl(null, [Validators.required]),
userDynamicFields: new FormGroup({}),
});

public userDynamicFieldConfig: {
payload: IOPFormSettings['_embedded']['payload']|null,
schema: IOPFormSettings['_embedded']['schema']|null,
public userDynamicFieldConfig:{
payload:IOPFormSettings['_embedded']['payload']|null,
schema:IOPFormSettings['_embedded']['schema']|null,
} = {
payload: null,
schema: null,
Expand All @@ -101,7 +91,7 @@ export class PrincipalComponent implements OnInit {
return this.principalForm.get('userDynamicFields');
}

get customFields():{[key:string]:any} {
get customFields():{ [key:string]:any } {
return this.dynamicFieldsControl?.value;
}

Expand All @@ -128,17 +118,27 @@ export class PrincipalComponent implements OnInit {
constructor(
readonly I18n:I18nService,
readonly httpClient:HttpClient,
readonly pathHelper:PathHelperService,
readonly cdRef: ChangeDetectorRef,
) {}
readonly apiV3Service:APIV3Service,
readonly cdRef:ChangeDetectorRef,
) {
}

ngOnInit() {
this.principalControl?.setValue(this.principalData.principal);

if (this.type === PrincipalType.User) {
const payload = this.isNewPrincipal ? this.principalData.customFields : {};
this.httpClient
.post<IOPFormSettings>('/api/v3/users/form', payload, { withCredentials: true, responseType: 'json' })
this
.apiV3Service
.users
.form
.post(payload)
.pipe(
take(1),
// The subsequent code expects to not work with a HalResource but rather with the raw
// api response.
map(formResource => formResource.$source)
)
.subscribe((formConfig) => {
this.userDynamicFieldConfig.schema = extractCustomFieldsFromSchema(formConfig._embedded?.schema);
this.userDynamicFieldConfig.payload = formConfig._embedded?.payload;
Expand Down Expand Up @@ -177,7 +177,7 @@ export class PrincipalComponent implements OnInit {
_links: Object.keys(links).reduce((cfs, name) => ({
...cfs,
[name]: Array.isArray(links[name])
? links[name].map((opt: any) => opt._links ? opt._links.self : opt)
? links[name].map((opt:any) => opt._links ? opt._links.self : opt)
: (links[name]._links ? links[name]._links.self : links[name])
}), {}),
};
Expand Down
@@ -1,17 +1,18 @@
import {Component, OnInit} from '@angular/core';
import {StateService, UIRouterGlobals} from "@uirouter/core";
import {UntilDestroyedMixin} from "core-app/shared/helpers/angular/until-destroyed.mixin";
import {PathHelperService} from "core-app/core/path-helper/path-helper.service";
import {HalSource} from "core-app/features/hal/resources/hal-resource";
import { Component, OnInit } from '@angular/core';
import { StateService } from "@uirouter/core";
import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin";
import { PathHelperService } from "core-app/core/path-helper/path-helper.service";
import { HalSource } from "core-app/features/hal/resources/hal-resource";
import {
IDynamicFieldGroupConfig,
IOPFormlyFieldSettings,
IOPFormlyTemplateOptions,
} from "core-app/shared/components/dynamic-forms/typings";
import {I18nService} from "core-app/core/i18n/i18n.service";
import {APIV3Service} from "core-app/core/apiv3/api-v3.service";
import {JobStatusModal} from "core-app/features/job-status/job-status-modal/job-status.modal";
import {OpModalService} from "core-app/shared/components/modal/modal.service";
import { I18nService } from "core-app/core/i18n/i18n.service";
import { APIV3Service } from "core-app/core/apiv3/api-v3.service";
import { JobStatusModal } from "core-app/features/job-status/job-status-modal/job-status.modal";
import { OpModalService } from "core-app/shared/components/modal/modal.service";
import { CurrentProjectService } from "core-app/core/current-project/current-project.service";

@Component({
selector: 'op-copy-project',
Expand All @@ -31,11 +32,11 @@ export class CopyProjectComponent extends UntilDestroyedMixin implements OnInit
text = {
advancedSettingsLabel: this.I18n.t("js.forms.advanced_settings"),
copySettingsLabel: this.I18n.t("js.project.copy.copy_options"),
}
};

constructor(
private apiV3Service:APIV3Service,
private uIRouterGlobals:UIRouterGlobals,
private currentProjectService:CurrentProjectService,
private pathHelperService:PathHelperService,
private modalService:OpModalService,
private $state:StateService,
Expand All @@ -45,7 +46,7 @@ export class CopyProjectComponent extends UntilDestroyedMixin implements OnInit
}

ngOnInit():void {
this.formUrl = this.apiV3Service.projects.id(this.uIRouterGlobals.params.projectPath).copy.form.path;
this.formUrl = this.apiV3Service.projects.id(this.currentProjectService.id!).copy.form.path;
this.fieldGroups = [
{
name: this.text.advancedSettingsLabel,
Expand Down
Expand Up @@ -80,7 +80,7 @@ export class NewProjectComponent extends UntilDestroyedMixin implements OnInit {
}

ngOnInit():void {
this.resourcePath = this.pathHelperService.projectsPath();
this.resourcePath = this.apiV3Service.projects.path;
this.fieldGroups = [{
name: this.text.advancedSettingsLabel,
fieldsFilter: (field) => !['name', 'parent'].includes(field.templateOptions?.property!) &&
Expand Down
@@ -1,6 +1,6 @@
<op-dynamic-form
[resourceId]="resourceId"
[resourcePath]="projectsPath"
[formHttpMethod]="formMethod"
[fieldsSettingsPipe]="dynamicFieldsSettingsPipe"
helpTextAttributeScope="Project"
></op-dynamic-form>
@@ -1,32 +1,32 @@
import { Component, OnInit } from '@angular/core';
import { StateService, UIRouterGlobals } from "@uirouter/core";
import { StateService } from "@uirouter/core";
import { UntilDestroyedMixin } from "core-app/shared/helpers/angular/until-destroyed.mixin";
import { PathHelperService } from "core-app/core/path-helper/path-helper.service";
import { IOPFormlyFieldSettings } from "core-app/shared/components/dynamic-forms/typings";
import { CurrentProjectService } from "core-app/core/current-project/current-project.service";

@Component({
selector: 'app-projects',
templateUrl: './projects.component.html',
styleUrls: ['./projects.component.scss']
})
export class ProjectsComponent extends UntilDestroyedMixin implements OnInit {
resourceId:string;
projectsPath:string;
formMethod = 'patch';
text:{ [key:string]:string };
dynamicFieldsSettingsPipe:(dynamicFieldsSettings:IOPFormlyFieldSettings[]) => IOPFormlyFieldSettings[];
hiddenFields = ['identifier', 'active'];

constructor(
private _uIRouterGlobals:UIRouterGlobals,
private _pathHelperService:PathHelperService,
private _$state:StateService,
private _currentProjectService:CurrentProjectService,
) {
super();
}

ngOnInit():void {
this.projectsPath = this._pathHelperService.projectsPath();
this.resourceId = this._uIRouterGlobals.params.projectPath;
this.projectsPath = this._currentProjectService.apiv3Path!;
this.dynamicFieldsSettingsPipe = (dynamicFieldsSettings) => {
return dynamicFieldsSettings
.reduce((formattedDynamicFieldsSettings:IOPFormlyFieldSettings[], dynamicFormField) => {
Expand All @@ -42,13 +42,6 @@ export class ProjectsComponent extends UntilDestroyedMixin implements OnInit {
}
}

onSubmitted(formResource:HalSource) {
// TODO: Filter out if this.resourceId === 'new'?
if (!this.resourceId) {
this._$state.go('.', { ...this._$state.params, projectPath: formResource.identifier });
}
}

private isFieldHidden(name:string|undefined) {
return this.hiddenFields.includes(name || '');
}
Expand Down
Expand Up @@ -295,7 +295,7 @@ export class DynamicFormComponent extends UntilDestroyedMixin implements OnChang
}

if (resourcePath) {
return `${this._pathHelperService.api.v3.apiV3Base}${resourcePath}`;
return resourcePath;
}

return;
Expand Down Expand Up @@ -349,7 +349,7 @@ export class DynamicFormComponent extends UntilDestroyedMixin implements OnChang

submit_message = `${title || ''} ${this.text.job_started}`;
} else {
submit_message = this.resourceId ? this.text.successful_update : this.text.successful_create;
submit_message = this.formHttpMethod === 'patch' ? this.text.successful_update : this.text.successful_create;
}

this._notificationsService.addSuccess(submit_message);
Expand Down
2 changes: 1 addition & 1 deletion lib/open_project/logging/sentry_logger.rb
Expand Up @@ -6,7 +6,7 @@ class << self
# Capture a message to sentry
def log(message, log_context = {})
Sentry.configure_scope do |sentry_scope|
build_sentry_context(sentry_scope, log_context)
build_sentry_context(sentry_scope, log_context.to_h)
Sentry.capture_message(message, level: sentry_level(log_context[:level]))
end
end
Expand Down

0 comments on commit dcfa053

Please sign in to comment.