Merge branch 'develop' into feature/oauth2-client
This commit is contained in:
commit
ad047bc372
@ -7,7 +7,7 @@ BUILD_PATH=${BUILD_PATH:-${SRC_PATH}/publish}
|
||||
BUILD_DOTNET_CORE_ARGS=${BUILD_DOTNET_CORE_ARGS:-"false"}
|
||||
PROPERTY_BUILD=${PROPERTY_BUILD:-"all"}
|
||||
|
||||
BACKEND_NODEJS_SERVICES=${BACKEND_NODEJS_SERVICES:-"ASC.Socket.IO, ASC.SsoAuth"}
|
||||
BACKEND_NODEJS_SERVICES=${BACKEND_NODEJS_SERVICES:-"ASC.Socket.IO, ASC.SsoAuth, ASC.TelegramReports"}
|
||||
BACKEND_DOTNETCORE_SERVICES=${BACKEND_DOTNETCORE_SERVICES:-"ASC.Files, ASC.People, ASC.Data.Backup, ASC.Files.Service, ASC.Notify, \
|
||||
ASC.Studio.Notify, ASC.Web.Api, ASC.Web.Studio, ASC.Data.Backup.BackgroundTasks, ASC.ClearEvents, ASC.ApiSystem, ASC.Web.HealthChecks.UI"}
|
||||
SELF_CONTAINED=${SELF_CONTAINED:-"false"}
|
||||
@ -179,6 +179,12 @@ function backend-nodejs-publish {
|
||||
yarn install --cwd ${SRC_PATH}/common/${ARRAY_NAME_SERVICES[$i]} --frozen-lockfile && \
|
||||
mkdir -p ${BUILD_PATH}/services/${ARRAY_NAME_SERVICES[$i]}/service/ && \
|
||||
cp -rfv ${SRC_PATH}/common/${ARRAY_NAME_SERVICES[$i]}/* ${BUILD_PATH}/services/${ARRAY_NAME_SERVICES[$i]}/service/
|
||||
|
||||
if [[ ${ARRAY_NAME_SERVICES[$i]} == "ASC.TelegramReports" ]]
|
||||
then
|
||||
# build before run
|
||||
yarn --cwd ${BUILD_PATH}/services/ASC.TelegramReports/service/ build
|
||||
fi
|
||||
if [[ ${DOCKER_ENTRYPOINT} != "false" ]]
|
||||
then
|
||||
echo "== ADD ${DOCKER_ENTRYPOINT} to ${ARRAY_NAME_SERVICES[$i]} =="
|
||||
|
@ -101,6 +101,7 @@ done
|
||||
services_name_backend_nodejs=()
|
||||
services_name_backend_nodejs+=(ASC.Socket.IO)
|
||||
services_name_backend_nodejs+=(ASC.SsoAuth)
|
||||
services_name_backend_nodejs+=(ASC.TelegramReports)
|
||||
|
||||
# Publish backend services (Nodejs)
|
||||
for i in ${!services_name_backend_nodejs[@]}; do
|
||||
|
@ -114,6 +114,7 @@
|
||||
API_HOST=${CONTAINER_PREFIX}api
|
||||
STUDIO_HOST=${CONTAINER_PREFIX}studio
|
||||
SSOAUTH_HOST=${CONTAINER_PREFIX}ssoauth
|
||||
TELEGRAMREPORTS_HOST=${CONTAINER_PREFIX}telegramreports
|
||||
MIGRATION_RUNNER_HOST=${CONTAINER_PREFIX}migration-runner
|
||||
PROXY_HOST=${CONTAINER_PREFIX}proxy
|
||||
ROUTER_HOST=${CONTAINER_PREFIX}router
|
||||
@ -138,6 +139,7 @@
|
||||
SERVICE_API=${API_HOST}:${SERVICE_PORT}
|
||||
SERVICE_STUDIO=${STUDIO_HOST}:${SERVICE_PORT}
|
||||
SERVICE_SSOAUTH=${SSOAUTH_HOST}:${SERVICE_PORT}
|
||||
SERVICE_TELEGRAMREPORTS=${TELEGRAMREPORTS_HOST}:${SERVICE_PORT}
|
||||
SERVICE_DOCEDITOR=${DOCEDITOR_HOST}:5013
|
||||
SERVICE_LOGIN=${LOGIN_HOST}:5011
|
||||
SERVICE_HELTHCHECKS=${HELTHCHECKS_HOST}:${SERVICE_PORT}
|
||||
|
@ -269,6 +269,7 @@ services:
|
||||
- SERVICE_API_SYSTEM=${SERVICE_API_SYSTEM}
|
||||
- SERVICE_STUDIO=${SERVICE_STUDIO}
|
||||
- SERVICE_SSOAUTH=${SERVICE_SSOAUTH}
|
||||
- SERVICE_TELEGRAMREPORTS=${SERVICE_TELEGRAMREPORTS}
|
||||
- SERVICE_DOCEDITOR=${SERVICE_DOCEDITOR}
|
||||
- SERVICE_LOGIN=${SERVICE_LOGIN}
|
||||
- SERVICE_HELTHCHECKS=${SERVICE_HELTHCHECKS}
|
||||
|
@ -1,10 +0,0 @@
|
||||
<service>
|
||||
<id>OnlyofficeWebPlugins</id>
|
||||
<name>ONLYOFFICE WebPlugins Server</name>
|
||||
<startmode>manual</startmode>
|
||||
<executable>node</executable>
|
||||
<arguments>../../common/ASC.WebPlugins/dist/src/main.js</arguments>
|
||||
<log mode="none"/>
|
||||
<delayedAutoStart>true</delayedAutoStart>
|
||||
<onfailure action="restart" delay="5 sec" />
|
||||
</service>
|
@ -1,9 +0,0 @@
|
||||
PUSHD %~dp0..
|
||||
|
||||
cd %~dp0../../common/ASC.WebPlugins/
|
||||
|
||||
call yarn install --immutable
|
||||
|
||||
call yarn build
|
||||
|
||||
POPD
|
@ -215,7 +215,8 @@ public abstract class BaseStartup
|
||||
.AddBaseDbContextPool<IntegrationEventLogContext>()
|
||||
.AddBaseDbContextPool<FeedDbContext>()
|
||||
.AddBaseDbContextPool<MessagesContext>()
|
||||
.AddBaseDbContextPool<WebhooksDbContext>();
|
||||
.AddBaseDbContextPool<WebhooksDbContext>()
|
||||
.AddBaseDbContextPool<WebPluginDbContext>();
|
||||
|
||||
if (AddAndUseSession)
|
||||
{
|
||||
|
@ -59,6 +59,7 @@ public class BaseWorkerStartup
|
||||
services.AddBaseDbContextPool<FeedDbContext>();
|
||||
services.AddBaseDbContextPool<MessagesContext>();
|
||||
services.AddBaseDbContextPool<WebhooksDbContext>();
|
||||
services.AddBaseDbContextPool<WebPluginDbContext>();
|
||||
|
||||
services.RegisterFeature();
|
||||
|
||||
|
131
common/ASC.Core.Common/Data/DbWebPluginService.cs
Normal file
131
common/ASC.Core.Common/Data/DbWebPluginService.cs
Normal file
@ -0,0 +1,131 @@
|
||||
// (c) Copyright Ascensio System SIA 2010-2022
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
namespace ASC.Core.Data;
|
||||
|
||||
[Scope]
|
||||
public class DbWebPluginService
|
||||
{
|
||||
private readonly IDbContextFactory<WebPluginDbContext> _dbContextFactory;
|
||||
|
||||
public DbWebPluginService(IDbContextFactory<WebPluginDbContext> dbContextFactory)
|
||||
{
|
||||
_dbContextFactory = dbContextFactory;
|
||||
}
|
||||
|
||||
public async Task<DbWebPlugin> SaveAsync(DbWebPlugin webPlugin)
|
||||
{
|
||||
ArgumentNullException.ThrowIfNull(webPlugin);
|
||||
|
||||
await using var dbContext = _dbContextFactory.CreateDbContext();
|
||||
|
||||
await dbContext.AddOrUpdateAsync(q => q.WebPlugins, webPlugin);
|
||||
await dbContext.SaveChangesAsync();
|
||||
|
||||
return webPlugin;
|
||||
}
|
||||
|
||||
public async Task<DbWebPlugin> GetByIdAsync(int tenantId, int id)
|
||||
{
|
||||
await using var dbContext = _dbContextFactory.CreateDbContext();
|
||||
|
||||
return await Queries.WebPluginByIdAsync(dbContext, tenantId, id);
|
||||
}
|
||||
|
||||
public async Task<DbWebPlugin> GetByNameAsync(int tenantId, string name)
|
||||
{
|
||||
await using var dbContext = _dbContextFactory.CreateDbContext();
|
||||
|
||||
return await Queries.WebPluginByNameAsync(dbContext, tenantId, name);
|
||||
}
|
||||
|
||||
public async Task<List<DbWebPlugin>> GetAsync(int tenantId)
|
||||
{
|
||||
await using var dbContext = _dbContextFactory.CreateDbContext();
|
||||
|
||||
return await Queries.WebPluginsAsync(dbContext, tenantId).ToListAsync();
|
||||
}
|
||||
|
||||
public async Task UpdateAsync(int tenantId, int id, bool enabled)
|
||||
{
|
||||
using var dbContext = _dbContextFactory.CreateDbContext();
|
||||
|
||||
await Queries.UpdateWebPluginStatusAsync(dbContext, tenantId, id, enabled);
|
||||
}
|
||||
|
||||
public async Task DeleteAsync(int tenantId, int id)
|
||||
{
|
||||
await using var dbContext = _dbContextFactory.CreateDbContext();
|
||||
|
||||
await Queries.DeleteWebPluginByIdAsync(dbContext, tenantId, id);
|
||||
}
|
||||
}
|
||||
|
||||
static file class Queries
|
||||
{
|
||||
public static readonly Func<WebPluginDbContext, int, IAsyncEnumerable<DbWebPlugin>>
|
||||
WebPluginsAsync = EF.CompileAsyncQuery(
|
||||
(WebPluginDbContext ctx, int tenantId) =>
|
||||
ctx.WebPlugins
|
||||
.AsNoTracking()
|
||||
.Where(r => r.TenantId == tenantId));
|
||||
|
||||
public static readonly Func<WebPluginDbContext, int, int, Task<DbWebPlugin>>
|
||||
WebPluginByIdAsync = EF.CompileAsyncQuery(
|
||||
(WebPluginDbContext ctx, int tenantId, int id) =>
|
||||
ctx.WebPlugins
|
||||
.AsNoTracking()
|
||||
.Where(r => r.TenantId == tenantId)
|
||||
.Where(r => r.Id == id)
|
||||
.FirstOrDefault());
|
||||
|
||||
public static readonly Func<WebPluginDbContext, int, string, Task<DbWebPlugin>>
|
||||
WebPluginByNameAsync = EF.CompileAsyncQuery(
|
||||
(WebPluginDbContext ctx, int tenantId, string name) =>
|
||||
ctx.WebPlugins
|
||||
.AsNoTracking()
|
||||
.Where(r => r.TenantId == tenantId)
|
||||
.Where(r => r.Name == name)
|
||||
.FirstOrDefault());
|
||||
|
||||
public static readonly Func<WebPluginDbContext, int, int, bool, Task<int>>
|
||||
UpdateWebPluginStatusAsync = EF.CompileAsyncQuery(
|
||||
(WebPluginDbContext ctx, int tenantId, int id, bool enabled) =>
|
||||
ctx.WebPlugins
|
||||
.Where(r => r.TenantId == tenantId)
|
||||
.Where(r => r.Id == id)
|
||||
.ExecuteUpdate(toUpdate => toUpdate
|
||||
.SetProperty(p => p.Enabled, enabled)
|
||||
));
|
||||
|
||||
public static readonly Func<WebPluginDbContext, int, int, Task<int>>
|
||||
DeleteWebPluginByIdAsync = EF.CompileAsyncQuery(
|
||||
(WebPluginDbContext ctx, int tenantId, int id) =>
|
||||
ctx.WebPlugins
|
||||
.Where(r => r.TenantId == tenantId)
|
||||
.Where(r => r.Id == id)
|
||||
.ExecuteDelete());
|
||||
}
|
45
common/ASC.Core.Common/EF/Context/WebPluginDbContext.cs
Normal file
45
common/ASC.Core.Common/EF/Context/WebPluginDbContext.cs
Normal file
@ -0,0 +1,45 @@
|
||||
// (c) Copyright Ascensio System SIA 2010-2022
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
namespace ASC.Core.Common.EF.Context;
|
||||
|
||||
public class WebPluginDbContext : DbContext
|
||||
{
|
||||
public DbSet<DbWebPlugin> WebPlugins { get; set; }
|
||||
|
||||
public WebPluginDbContext(DbContextOptions<WebPluginDbContext> options) : base(options)
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
protected override void OnModelCreating(ModelBuilder modelBuilder)
|
||||
{
|
||||
ModelBuilderWrapper
|
||||
.From(modelBuilder, Database)
|
||||
.AddDbWebPlugins()
|
||||
.AddDbTenant();
|
||||
}
|
||||
}
|
212
common/ASC.Core.Common/EF/Model/DbWebPlugin.cs
Normal file
212
common/ASC.Core.Common/EF/Model/DbWebPlugin.cs
Normal file
@ -0,0 +1,212 @@
|
||||
// (c) Copyright Ascensio System SIA 2010-2022
|
||||
//
|
||||
// This program is a free software product.
|
||||
// You can redistribute it and/or modify it under the terms
|
||||
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
|
||||
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
|
||||
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
|
||||
// any third-party rights.
|
||||
//
|
||||
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
|
||||
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
|
||||
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
|
||||
//
|
||||
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
|
||||
//
|
||||
// The interactive user interfaces in modified source and object code versions of the Program must
|
||||
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
|
||||
//
|
||||
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
|
||||
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
|
||||
// trademark law for use of our trademarks.
|
||||
//
|
||||
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
|
||||
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
|
||||
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
|
||||
|
||||
namespace ASC.Core.Common.EF.Model;
|
||||
|
||||
public class DbWebPlugin : BaseEntity
|
||||
{
|
||||
public int Id { get; set; }
|
||||
public int TenantId { get; set; }
|
||||
|
||||
public string Name { get; set; }
|
||||
public string Version { get; set; }
|
||||
public string Description { get; set; }
|
||||
public string License { get; set; }
|
||||
public string Author { get; set; }
|
||||
public string HomePage { get; set; }
|
||||
public string PluginName { get; set; }
|
||||
public string Scopes { get; set; }
|
||||
public string Image { get; set; }
|
||||
|
||||
public Guid CreateBy { get; set; }
|
||||
public DateTime CreateOn { get; set; }
|
||||
|
||||
public bool Enabled { get; set; }
|
||||
public bool System { get; set; }
|
||||
|
||||
public DbTenant Tenant { get; set; }
|
||||
|
||||
public override object[] GetKeys()
|
||||
{
|
||||
return new object[] { Id };
|
||||
}
|
||||
}
|
||||
|
||||
public static class WebPluginExtension
|
||||
{
|
||||
public static ModelBuilderWrapper AddDbWebPlugins(this ModelBuilderWrapper modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<DbWebPlugin>().Navigation(e => e.Tenant).AutoInclude(false);
|
||||
|
||||
modelBuilder
|
||||
.Add(MySqlAddDbWebPlugins, Provider.MySql)
|
||||
.Add(PgSqlAddDbWebPlugins, Provider.PostgreSql);
|
||||
|
||||
return modelBuilder;
|
||||
}
|
||||
|
||||
public static void MySqlAddDbWebPlugins(this ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<DbWebPlugin>(entity =>
|
||||
{
|
||||
entity.HasKey(e => new { e.Id })
|
||||
.HasName("PRIMARY");
|
||||
|
||||
entity.ToTable("webplugins")
|
||||
.HasCharSet("utf8mb4");
|
||||
|
||||
entity.HasIndex(e => e.TenantId)
|
||||
.HasDatabaseName("tenant");
|
||||
|
||||
entity.Property(e => e.TenantId)
|
||||
.HasColumnName("tenant_id");
|
||||
|
||||
entity.Property(e => e.Name)
|
||||
.IsRequired()
|
||||
.HasColumnName("name")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
entity.Property(e => e.Version)
|
||||
.HasColumnName("version")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
entity.Property(e => e.Description)
|
||||
.HasColumnName("description")
|
||||
.HasColumnType("text");
|
||||
|
||||
entity.Property(e => e.License)
|
||||
.HasColumnName("license")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
entity.Property(e => e.Author)
|
||||
.HasColumnName("author")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
entity.Property(e => e.HomePage)
|
||||
.HasColumnName("home_page")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
entity.Property(e => e.PluginName)
|
||||
.IsRequired()
|
||||
.HasColumnName("plugin_name")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
entity.Property(e => e.Scopes)
|
||||
.HasColumnName("scopes")
|
||||
.HasColumnType("text");
|
||||
|
||||
entity.Property(e => e.Image)
|
||||
.HasColumnName("image")
|
||||
.HasColumnType("varchar(255)");
|
||||
|
||||
entity.Property(e => e.CreateBy)
|
||||
.IsRequired()
|
||||
.HasColumnName("create_by")
|
||||
.HasColumnType("char(36)");
|
||||
|
||||
entity.Property(e => e.CreateOn)
|
||||
.HasColumnName("create_on")
|
||||
.HasColumnType("datetime");
|
||||
|
||||
entity.Property(e => e.Enabled)
|
||||
.HasColumnName("enabled")
|
||||
.HasColumnType("tinyint(1)")
|
||||
.HasDefaultValueSql("'0'");
|
||||
|
||||
entity.Property(e => e.System)
|
||||
.HasColumnName("system")
|
||||
.HasColumnType("tinyint(1)")
|
||||
.HasDefaultValueSql("'0'");
|
||||
});
|
||||
}
|
||||
public static void PgSqlAddDbWebPlugins(this ModelBuilder modelBuilder)
|
||||
{
|
||||
modelBuilder.Entity<DbWebPlugin>(entity =>
|
||||
{
|
||||
entity.HasKey(e => new { e.Id })
|
||||
.HasName("webplugins_pkey");
|
||||
|
||||
entity.ToTable("webplugins", "onlyoffice");
|
||||
|
||||
entity.HasIndex(e => e.TenantId)
|
||||
.HasDatabaseName("tenant_webplugins");
|
||||
|
||||
entity.Property(e => e.TenantId)
|
||||
.HasColumnName("tenant_id");
|
||||
|
||||
entity.Property(e => e.Name)
|
||||
.IsRequired()
|
||||
.HasColumnName("name")
|
||||
.HasMaxLength(255);
|
||||
|
||||
entity.Property(e => e.Version)
|
||||
.HasColumnName("version")
|
||||
.HasMaxLength(255);
|
||||
|
||||
entity.Property(e => e.Description)
|
||||
.HasColumnName("description");
|
||||
|
||||
entity.Property(e => e.License)
|
||||
.HasColumnName("license")
|
||||
.HasMaxLength(255);
|
||||
|
||||
entity.Property(e => e.Author)
|
||||
.HasColumnName("author")
|
||||
.HasMaxLength(255);
|
||||
|
||||
entity.Property(e => e.HomePage)
|
||||
.HasColumnName("home_page")
|
||||
.HasMaxLength(255);
|
||||
|
||||
entity.Property(e => e.PluginName)
|
||||
.IsRequired()
|
||||
.HasColumnName("plugin_name")
|
||||
.HasMaxLength(255);
|
||||
|
||||
entity.Property(e => e.Scopes)
|
||||
.HasColumnName("scopes");
|
||||
|
||||
entity.Property(e => e.Image)
|
||||
.HasColumnName("image")
|
||||
.HasMaxLength(255);
|
||||
|
||||
entity.Property(e => e.CreateBy)
|
||||
.IsRequired()
|
||||
.HasColumnName("create_by")
|
||||
.HasMaxLength(36)
|
||||
.IsFixedLength();
|
||||
|
||||
entity.Property(e => e.CreateOn)
|
||||
.HasColumnName("create_on");
|
||||
|
||||
entity.Property(e => e.Enabled)
|
||||
.HasColumnName("enabled");
|
||||
|
||||
entity.Property(e => e.System)
|
||||
.HasColumnName("system");
|
||||
});
|
||||
}
|
||||
}
|
31
common/ASC.TelegramReports/.eslintrc.js
Executable file
31
common/ASC.TelegramReports/.eslintrc.js
Executable file
@ -0,0 +1,31 @@
|
||||
module.exports = {
|
||||
parser: "@typescript-eslint/parser",
|
||||
parserOptions: {
|
||||
project: "tsconfig.json",
|
||||
tsconfigRootDir: __dirname,
|
||||
sourceType: "module",
|
||||
},
|
||||
plugins: ["@typescript-eslint/eslint-plugin"],
|
||||
extends: [
|
||||
"plugin:@typescript-eslint/recommended",
|
||||
"plugin:prettier/recommended",
|
||||
],
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
jest: true,
|
||||
},
|
||||
ignorePatterns: [".eslintrc.js"],
|
||||
"prettier/prettier": [
|
||||
"error",
|
||||
{
|
||||
endOfLine: "auto",
|
||||
},
|
||||
],
|
||||
rules: {
|
||||
"@typescript-eslint/interface-name-prefix": "off",
|
||||
"@typescript-eslint/explicit-function-return-type": "off",
|
||||
"@typescript-eslint/explicit-module-boundary-types": "off",
|
||||
"@typescript-eslint/no-explicit-any": "off",
|
||||
},
|
||||
};
|
0
common/ASC.WebPlugins/.gitignore → common/ASC.TelegramReports/.gitignore
vendored
Normal file → Executable file
0
common/ASC.WebPlugins/.gitignore → common/ASC.TelegramReports/.gitignore
vendored
Normal file → Executable file
14
common/ASC.TelegramReports/README.md
Executable file
14
common/ASC.TelegramReports/README.md
Executable file
@ -0,0 +1,14 @@
|
||||
# ASC Telegram Reports
|
||||
|
||||
ASC.TelegramReports
|
||||
|
||||
### Installation and usage
|
||||
|
||||
```bash
|
||||
$ npm install
|
||||
$ npm start
|
||||
```
|
||||
|
||||
### License
|
||||
|
||||
[GNU GPL V3](LICENSE)
|
2
common/ASC.WebPlugins/config/config.json → common/ASC.TelegramReports/config/config.json
Normal file → Executable file
2
common/ASC.WebPlugins/config/config.json → common/ASC.TelegramReports/config/config.json
Normal file → Executable file
@ -1,6 +1,6 @@
|
||||
{
|
||||
"app": {
|
||||
"port": 5014,
|
||||
"port": 5016,
|
||||
"appsettings": "../../../../config",
|
||||
"environment": "Development"
|
||||
}
|
26
common/ASC.TelegramReports/config/index.ts
Executable file
26
common/ASC.TelegramReports/config/index.ts
Executable file
@ -0,0 +1,26 @@
|
||||
import * as nconf from "nconf";
|
||||
import * as path from "path";
|
||||
import * as fs from "fs";
|
||||
|
||||
import * as conf from "./config.json";
|
||||
|
||||
nconf.argv().env().file("config", path.join(__dirname, "config.json"));
|
||||
|
||||
getAndSaveAppsettings();
|
||||
|
||||
export default nconf;
|
||||
|
||||
function getAndSaveAppsettings() {
|
||||
var appsettings = nconf.get("app").appsettings;
|
||||
|
||||
if (!path.isAbsolute(appsettings)) {
|
||||
appsettings = path.join(__dirname, appsettings);
|
||||
}
|
||||
|
||||
var env = nconf.get("app").environment;
|
||||
console.log('environment: ' + env);
|
||||
|
||||
nconf.file("appsettingsWithEnv", path.join(appsettings, 'appsettings.' + env + '.json'));
|
||||
nconf.file("appsettings", path.join(appsettings, 'appsettings.json'));
|
||||
nconf.file("telegramConf", path.join(appsettings, "telegram.json"));
|
||||
}
|
31
common/ASC.TelegramReports/index.ts
Executable file
31
common/ASC.TelegramReports/index.ts
Executable file
@ -0,0 +1,31 @@
|
||||
import firebase from "firebase/compat/app";
|
||||
import "firebase/compat/database";
|
||||
|
||||
import { NestFactory } from "@nestjs/core";
|
||||
import { AppModule } from "./src/app/app.module";
|
||||
import { AppService } from "./src/app/app.service";
|
||||
|
||||
import * as config from "./config";
|
||||
|
||||
const winston = require("./src/log.js");
|
||||
|
||||
const firebaseConfig = config.default.get("firebase");
|
||||
firebase.initializeApp(firebaseConfig);
|
||||
|
||||
async function bootstrap() {
|
||||
try {
|
||||
const app = await NestFactory.create(AppModule);
|
||||
const appService = app.get(AppService);
|
||||
|
||||
winston.info(`Start TelegramReports Service listening`);
|
||||
|
||||
const ref = firebase.database().ref("reports").limitToLast(1);
|
||||
|
||||
ref.on("child_added", (data) => {
|
||||
appService.sendMessage(data.val())
|
||||
});
|
||||
} catch (e) {
|
||||
winston.error(e);
|
||||
}
|
||||
}
|
||||
bootstrap();
|
5
common/ASC.WebPlugins/nest-cli.json → common/ASC.TelegramReports/nest-cli.json
Normal file → Executable file
5
common/ASC.WebPlugins/nest-cli.json → common/ASC.TelegramReports/nest-cli.json
Normal file → Executable file
@ -2,5 +2,8 @@
|
||||
"$schema": "https://json.schemastore.org/nest-cli",
|
||||
"collection": "@nestjs/schematics",
|
||||
"sourceRoot": "src",
|
||||
"entryFile": "src/main"
|
||||
"entryFile": "index.js",
|
||||
"compilerOptions": {
|
||||
"deleteOutDir": true
|
||||
}
|
||||
}
|
8
common/ASC.WebPlugins/package.json → common/ASC.TelegramReports/package.json
Normal file → Executable file
8
common/ASC.WebPlugins/package.json → common/ASC.TelegramReports/package.json
Normal file → Executable file
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "plugin-system",
|
||||
"name": "telegram-reports",
|
||||
"version": "1.0.0",
|
||||
"description": "Server for docspace-plugins",
|
||||
"description": "Server for telegram-reports",
|
||||
"private": true,
|
||||
"scripts": {
|
||||
"prebuild": "rimraf dist",
|
||||
@ -9,7 +9,7 @@
|
||||
"start": "rimraf dist & nest start",
|
||||
"start:dev": "rimraf dist & nest start --watch",
|
||||
"start:debug": "nest start --debug --watch",
|
||||
"start:prod": "node dist/main",
|
||||
"start:prod": "node dist/index",
|
||||
"lint": "eslint \"{src,apps,libs,test}/**/*.ts\" --fix"
|
||||
},
|
||||
"dependencies": {
|
||||
@ -19,11 +19,13 @@
|
||||
"@nestjs/platform-express": "^9.0.0",
|
||||
"@nestjs/typeorm": "^9.0.0",
|
||||
"date-and-time": "^2.4.1",
|
||||
"firebase": "^10.4.0",
|
||||
"mysql2": "^2.3.3",
|
||||
"nconf": "^0.12.0",
|
||||
"reflect-metadata": "^0.1.13",
|
||||
"rimraf": "^3.0.2",
|
||||
"rxjs": "^7.2.0",
|
||||
"telegraf": "^4.13.1",
|
||||
"typeorm": "^0.3.7",
|
||||
"winston": "^3.8.2",
|
||||
"winston-cloudwatch": "^6.1.1",
|
9
common/ASC.TelegramReports/src/app/app.module.ts
Executable file
9
common/ASC.TelegramReports/src/app/app.module.ts
Executable file
@ -0,0 +1,9 @@
|
||||
import { Module } from "@nestjs/common";
|
||||
import { AppService } from "./app.service";
|
||||
|
||||
@Module({
|
||||
imports: [],
|
||||
controllers: [],
|
||||
providers: [AppService],
|
||||
})
|
||||
export class AppModule { }
|
43
common/ASC.TelegramReports/src/app/app.service.ts
Executable file
43
common/ASC.TelegramReports/src/app/app.service.ts
Executable file
@ -0,0 +1,43 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { Telegraf } from "telegraf";
|
||||
|
||||
import * as config from "../../config";
|
||||
|
||||
const winston = require("../log.js");
|
||||
|
||||
const { botKey, chatId } = config.default.get("telegramConf");
|
||||
|
||||
const MAX_LENGTH = 4096; //TG Limit
|
||||
|
||||
@Injectable()
|
||||
export class AppService {
|
||||
bot = new Telegraf(botKey);
|
||||
|
||||
chunkMessage = (str: string, size: number): Array<string> =>
|
||||
Array.from({ length: Math.ceil(str.length / size) }, (_, i) =>
|
||||
str.slice(i * size, i * size + size),
|
||||
);
|
||||
|
||||
async sendMessage(report): Promise<string> {
|
||||
if (!botKey) throw new Error("Empty bot key");
|
||||
if (!chatId) throw new Error("Empty chat ID");
|
||||
|
||||
const message = "New bug report:\n" + JSON.stringify(report);
|
||||
|
||||
try {
|
||||
if (message.length > MAX_LENGTH) {
|
||||
for (const part of this.chunkMessage(message, MAX_LENGTH)) {
|
||||
await this.bot.telegram.sendMessage(chatId, part);
|
||||
}
|
||||
} else {
|
||||
await this.bot.telegram.sendMessage(chatId, message);
|
||||
}
|
||||
|
||||
winston.info(`Report sent successfully`, message);
|
||||
return "Report sent successfully"
|
||||
} catch (e) {
|
||||
winston.error(e);
|
||||
return e;
|
||||
}
|
||||
}
|
||||
}
|
103
common/ASC.TelegramReports/src/log.ts
Executable file
103
common/ASC.TelegramReports/src/log.ts
Executable file
@ -0,0 +1,103 @@
|
||||
import * as winston from "winston";
|
||||
import * as WinstonCloudWatch from "winston-cloudwatch";
|
||||
import * as date from "date-and-time";
|
||||
import * as os from "os";
|
||||
import * as config from "../config";
|
||||
import { randomUUID } from "crypto";
|
||||
import "winston-daily-rotate-file";
|
||||
import * as path from "path";
|
||||
import * as fs from "fs";
|
||||
|
||||
let logpath = process.env.logpath || null;
|
||||
|
||||
if (logpath != null) {
|
||||
if (!path.isAbsolute(logpath)) {
|
||||
logpath = path.join(__dirname, "..", logpath);
|
||||
}
|
||||
}
|
||||
|
||||
const fileName = logpath
|
||||
? path.join(logpath, "telegramreports.%DATE%.log")
|
||||
: path.join(__dirname, "..", "..", "..", "..", "Logs", "telegramreports.%DATE%.log");
|
||||
|
||||
const dirName = path.dirname(fileName);
|
||||
|
||||
if (!fs.existsSync(dirName)) {
|
||||
fs.mkdirSync(dirName);
|
||||
}
|
||||
|
||||
const aws = config.default.get("aws").cloudWatch;
|
||||
|
||||
const accessKeyId = aws.accessKeyId;
|
||||
const secretAccessKey = aws.secretAccessKey;
|
||||
const awsRegion = aws.region;
|
||||
const logGroupName = aws.logGroupName;
|
||||
const logStreamName = aws.logStreamName.replace("${hostname}", os.hostname())
|
||||
.replace("${applicationContext}", "TelegramReports")
|
||||
.replace("${guid}", randomUUID())
|
||||
.replace("${date}", date.format(new Date(), 'YYYY/MM/DDTHH.mm.ss'));
|
||||
|
||||
const options = {
|
||||
file: {
|
||||
filename: fileName,
|
||||
datePattern: "MM-DD",
|
||||
handleExceptions: true,
|
||||
humanReadableUnhandledException: true,
|
||||
zippedArchive: true,
|
||||
maxSize: "50m",
|
||||
maxFiles: "30d",
|
||||
json: true,
|
||||
},
|
||||
console: {
|
||||
level: "debug",
|
||||
handleExceptions: true,
|
||||
json: false,
|
||||
colorize: true,
|
||||
},
|
||||
cloudWatch: {
|
||||
name: 'aws',
|
||||
level: "debug",
|
||||
logStreamName: logStreamName,
|
||||
logGroupName: logGroupName,
|
||||
awsRegion: awsRegion,
|
||||
jsonMessage: true,
|
||||
awsOptions: {
|
||||
credentials: {
|
||||
accessKeyId: accessKeyId,
|
||||
secretAccessKey: secretAccessKey
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const transports: winston.transport[] = [
|
||||
new winston.transports.Console(options.console),
|
||||
new winston.transports.DailyRotateFile(options.file)
|
||||
];
|
||||
|
||||
if (aws != null && aws.accessKeyId !== '') {
|
||||
transports.push(new WinstonCloudWatch(options.cloudWatch));
|
||||
}
|
||||
|
||||
const customFormat = winston.format(info => {
|
||||
const now = new Date();
|
||||
|
||||
info.date = date.format(now, 'YYYY-MM-DD HH:mm:ss');
|
||||
info.applicationContext = "TelegramReports";
|
||||
info.level = info.level.toUpperCase();
|
||||
|
||||
const hostname = os.hostname();
|
||||
|
||||
info["instance-id"] = hostname;
|
||||
|
||||
return info;
|
||||
})();
|
||||
|
||||
module.exports = winston.createLogger({
|
||||
format: winston.format.combine(
|
||||
customFormat,
|
||||
winston.format.json()
|
||||
),
|
||||
transports: transports,
|
||||
exitOnError: false,
|
||||
});
|
0
common/ASC.WebPlugins/tsconfig.build.json → common/ASC.TelegramReports/tsconfig.build.json
Normal file → Executable file
0
common/ASC.WebPlugins/tsconfig.build.json → common/ASC.TelegramReports/tsconfig.build.json
Normal file → Executable file
0
common/ASC.WebPlugins/tsconfig.json → common/ASC.TelegramReports/tsconfig.json
Normal file → Executable file
0
common/ASC.WebPlugins/tsconfig.json → common/ASC.TelegramReports/tsconfig.json
Normal file → Executable file
5466
common/ASC.WebPlugins/yarn.lock → common/ASC.TelegramReports/yarn.lock
Normal file → Executable file
5466
common/ASC.WebPlugins/yarn.lock → common/ASC.TelegramReports/yarn.lock
Normal file → Executable file
File diff suppressed because it is too large
Load Diff
@ -1,31 +0,0 @@
|
||||
module.exports = {
|
||||
parser: '@typescript-eslint/parser',
|
||||
parserOptions: {
|
||||
project: 'tsconfig.json',
|
||||
tsconfigRootDir : __dirname,
|
||||
sourceType: 'module',
|
||||
},
|
||||
plugins: ['@typescript-eslint/eslint-plugin'],
|
||||
extends: [
|
||||
'plugin:@typescript-eslint/recommended',
|
||||
'plugin:prettier/recommended',
|
||||
],
|
||||
root: true,
|
||||
env: {
|
||||
node: true,
|
||||
jest: true,
|
||||
},
|
||||
ignorePatterns: ['.eslintrc.js'],
|
||||
'prettier/prettier': [
|
||||
'error',
|
||||
{
|
||||
'endOfLine': 'auto',
|
||||
}
|
||||
],
|
||||
rules: {
|
||||
'@typescript-eslint/interface-name-prefix': 'off',
|
||||
'@typescript-eslint/explicit-function-return-type': 'off',
|
||||
'@typescript-eslint/explicit-module-boundary-types': 'off',
|
||||
'@typescript-eslint/no-explicit-any': 'off',
|
||||
},
|
||||
};
|
File diff suppressed because one or more lines are too long
783
common/ASC.WebPlugins/.yarn/releases/yarn-3.2.2.cjs
vendored
783
common/ASC.WebPlugins/.yarn/releases/yarn-3.2.2.cjs
vendored
File diff suppressed because one or more lines are too long
@ -1,26 +0,0 @@
|
||||
import * as nconf from "nconf";
|
||||
import * as path from "path";
|
||||
import * as fs from "fs";
|
||||
|
||||
import * as conf from "./config.json";
|
||||
|
||||
nconf.argv().env().file("config", path.join(__dirname, "config.json"));
|
||||
|
||||
getAndSaveAppsettings();
|
||||
|
||||
export default nconf;
|
||||
|
||||
function getAndSaveAppsettings() {
|
||||
var appsettings = nconf.get("app").appsettings;
|
||||
|
||||
if (!path.isAbsolute(appsettings)) {
|
||||
appsettings = path.join(__dirname, appsettings);
|
||||
}
|
||||
|
||||
var env = nconf.get("app").environment;
|
||||
console.log('environment: ' + env);
|
||||
|
||||
nconf.file("appsettingsWithEnv", path.join(appsettings, 'appsettings.' + env + '.json'));
|
||||
nconf.file("appsettings", path.join(appsettings, 'appsettings.json'));
|
||||
nconf.file("pluginsConf", path.join(appsettings, "plugins.json"));
|
||||
}
|
@ -1,7 +0,0 @@
|
||||
CREATE TABLE IF NOT EXISTS `plugin` (
|
||||
`id` varchar(200) NOT NULL,
|
||||
`name` text NOT NULL,
|
||||
`filename` text NOT NULL,
|
||||
`isActive` int(11) NOT NULL DEFAULT '1',
|
||||
PRIMARY KEY (`id`)
|
||||
) DEFAULT CHARSET=utf8;
|
@ -1,23 +0,0 @@
|
||||
import { Module } from "@nestjs/common";
|
||||
import { TypeOrmModule } from "@nestjs/typeorm";
|
||||
|
||||
import { PluginsModule } from "./plugins/plugins.module";
|
||||
|
||||
import { Plugin } from "./entities/plugin.entity";
|
||||
|
||||
@Module({
|
||||
imports: [
|
||||
TypeOrmModule.forRoot({
|
||||
type: "mysql",
|
||||
host: "localhost",
|
||||
port: 3306,
|
||||
username: "root",
|
||||
password: "root",
|
||||
database: "onlyoffice",
|
||||
entities: [Plugin],
|
||||
synchronize: true,
|
||||
}),
|
||||
PluginsModule,
|
||||
],
|
||||
})
|
||||
export class AppModule {}
|
@ -1,16 +0,0 @@
|
||||
import { Entity, Column, PrimaryGeneratedColumn } from "typeorm";
|
||||
|
||||
@Entity()
|
||||
export class Plugin {
|
||||
@PrimaryGeneratedColumn()
|
||||
id: number;
|
||||
|
||||
@Column()
|
||||
name: string;
|
||||
|
||||
@Column()
|
||||
filename: string;
|
||||
|
||||
@Column({ default: true })
|
||||
isActive: boolean;
|
||||
}
|
@ -1,33 +0,0 @@
|
||||
import { Injectable, CanActivate, ExecutionContext } from "@nestjs/common";
|
||||
import { Observable } from "rxjs";
|
||||
|
||||
import * as config from "../../config";
|
||||
|
||||
const { enabled, allow } = config.default.get("plugins");
|
||||
|
||||
@Injectable()
|
||||
export class PluginGuard implements CanActivate {
|
||||
canActivate(
|
||||
context: ExecutionContext
|
||||
): boolean | Promise<boolean> | Observable<boolean> {
|
||||
return enabled === "true";
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class PluginUploadGuard implements CanActivate {
|
||||
canActivate(
|
||||
context: ExecutionContext
|
||||
): boolean | Promise<boolean> | Observable<boolean> {
|
||||
return enabled === "true" && allow.includes("upload");
|
||||
}
|
||||
}
|
||||
|
||||
@Injectable()
|
||||
export class PluginDeleteGuard implements CanActivate {
|
||||
canActivate(
|
||||
context: ExecutionContext
|
||||
): boolean | Promise<boolean> | Observable<boolean> {
|
||||
return enabled === "true" && allow.includes("delete");
|
||||
}
|
||||
}
|
@ -1,104 +0,0 @@
|
||||
import * as winston from "winston";
|
||||
import * as WinstonCloudWatch from "winston-cloudwatch";
|
||||
import * as date from "date-and-time";
|
||||
import * as os from "os";
|
||||
import * as config from "../config";
|
||||
import { randomUUID } from "crypto";
|
||||
import "winston-daily-rotate-file";
|
||||
import * as path from "path";
|
||||
import * as fs from "fs";
|
||||
|
||||
let logpath = process.env.logpath || null;
|
||||
|
||||
if (logpath != null) {
|
||||
if (!path.isAbsolute(logpath)) {
|
||||
logpath = path.join(__dirname, "..", logpath);
|
||||
}
|
||||
}
|
||||
|
||||
const fileName = logpath
|
||||
? path.join(logpath, "plugins.%DATE%.log")
|
||||
: path.join(__dirname, "..", "..", "..", "..", "Logs", "plugins.%DATE%.log");
|
||||
|
||||
const dirName = path.dirname(fileName);
|
||||
|
||||
if (!fs.existsSync(dirName)) {
|
||||
fs.mkdirSync(dirName);
|
||||
}
|
||||
|
||||
const aws = config.default.get("aws").cloudWatch;
|
||||
|
||||
const accessKeyId = aws.accessKeyId;
|
||||
const secretAccessKey = aws.secretAccessKey;
|
||||
const awsRegion = aws.region;
|
||||
const logGroupName = aws.logGroupName;
|
||||
const logStreamName = aws.logStreamName.replace("${hostname}", os.hostname())
|
||||
.replace("${applicationContext}", "WebPlugins")
|
||||
.replace("${guid}", randomUUID())
|
||||
.replace("${date}", date.format(new Date(), 'YYYY/MM/DDTHH.mm.ss'));
|
||||
|
||||
const options = {
|
||||
file: {
|
||||
filename: fileName,
|
||||
datePattern: "MM-DD",
|
||||
handleExceptions: true,
|
||||
humanReadableUnhandledException: true,
|
||||
zippedArchive: true,
|
||||
maxSize: "50m",
|
||||
maxFiles: "30d",
|
||||
json: true,
|
||||
},
|
||||
console: {
|
||||
level: "debug",
|
||||
handleExceptions: true,
|
||||
json: false,
|
||||
colorize: true,
|
||||
},
|
||||
cloudWatch: {
|
||||
name: 'aws',
|
||||
level: "debug",
|
||||
logStreamName: logStreamName,
|
||||
logGroupName: logGroupName,
|
||||
awsRegion: awsRegion,
|
||||
jsonMessage: true,
|
||||
awsOptions: {
|
||||
credentials: {
|
||||
accessKeyId: accessKeyId,
|
||||
secretAccessKey: secretAccessKey
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const transports: winston.transport[] = [
|
||||
new winston.transports.Console(options.console),
|
||||
new winston.transports.DailyRotateFile(options.file)
|
||||
];
|
||||
|
||||
if (aws != null && aws.accessKeyId !== '')
|
||||
{
|
||||
transports.push(new WinstonCloudWatch(options.cloudWatch));
|
||||
}
|
||||
|
||||
const customFormat = winston.format(info => {
|
||||
const now = new Date();
|
||||
|
||||
info.date = date.format(now, 'YYYY-MM-DD HH:mm:ss');
|
||||
info.applicationContext = "WebPlugins";
|
||||
info.level = info.level.toUpperCase();
|
||||
|
||||
const hostname = os.hostname();
|
||||
|
||||
info["instance-id"] = hostname;
|
||||
|
||||
return info;
|
||||
})();
|
||||
|
||||
module.exports = winston.createLogger({
|
||||
format: winston.format.combine(
|
||||
customFormat,
|
||||
winston.format.json()
|
||||
),
|
||||
transports: transports,
|
||||
exitOnError: false,
|
||||
});
|
@ -1,21 +0,0 @@
|
||||
import { NestFactory } from "@nestjs/core";
|
||||
import { AppModule } from "./app.module";
|
||||
|
||||
import * as config from "../config";
|
||||
|
||||
const winston = require("./log.js");
|
||||
|
||||
const port = config.default.get("app").port || 5014;
|
||||
|
||||
async function bootstrap() {
|
||||
try {
|
||||
const app = await NestFactory.create(AppModule, { cors: false });
|
||||
// app.enableCors();
|
||||
await app.listen(port, () => {
|
||||
winston.info(`Start WebPlugins Service listening on port ${port} for http`);
|
||||
});
|
||||
} catch (e) {
|
||||
winston.error(e);
|
||||
}
|
||||
}
|
||||
bootstrap();
|
@ -1,85 +0,0 @@
|
||||
import {
|
||||
Controller,
|
||||
Param,
|
||||
Body,
|
||||
Get,
|
||||
Post,
|
||||
UploadedFiles,
|
||||
UseInterceptors,
|
||||
Put,
|
||||
Delete,
|
||||
UseGuards,
|
||||
} from "@nestjs/common";
|
||||
|
||||
import { AnyFilesInterceptor } from "@nestjs/platform-express";
|
||||
|
||||
import { storage } from "src/utils";
|
||||
|
||||
import { Plugin } from "src/entities/plugin.entity";
|
||||
|
||||
import {
|
||||
PluginGuard,
|
||||
PluginUploadGuard,
|
||||
PluginDeleteGuard,
|
||||
} from "src/guards/plugin.guard";
|
||||
|
||||
import { PluginsService } from "./plugins.service";
|
||||
import fileFilter from "src/utils/file-filter";
|
||||
|
||||
@Controller("/api/2.0/plugins")
|
||||
@UseGuards(PluginGuard)
|
||||
export class PluginsController {
|
||||
constructor(private pluginsService: PluginsService) { }
|
||||
|
||||
@Get()
|
||||
async findAll(): Promise<{ response: Plugin[] }> {
|
||||
const plugins: Plugin[] = await this.pluginsService.findAll();
|
||||
return { response: plugins };
|
||||
}
|
||||
|
||||
@Put("activate/:id")
|
||||
async activate(@Param("id") id: number): Promise<{ response: Plugin }> {
|
||||
const plugin: Plugin = await this.pluginsService.activate(id);
|
||||
return { response: plugin };
|
||||
}
|
||||
|
||||
@Post("upload")
|
||||
@UseGuards(PluginUploadGuard)
|
||||
@UseInterceptors(
|
||||
AnyFilesInterceptor({
|
||||
storage: storage,
|
||||
fileFilter: fileFilter,
|
||||
})
|
||||
)
|
||||
async upload(
|
||||
@UploadedFiles() files: Express.Multer.File[]
|
||||
): Promise<{ response: Plugin | { error: string } }> {
|
||||
try {
|
||||
if (files[0]) {
|
||||
const plugin = await this.pluginsService.upload(
|
||||
files[0].originalname,
|
||||
files[0].filename
|
||||
);
|
||||
|
||||
return { response: plugin };
|
||||
} else {
|
||||
return {
|
||||
response: { error: "Invalid file format or file already exists" },
|
||||
};
|
||||
}
|
||||
} catch (e) {
|
||||
console.log(e);
|
||||
return {
|
||||
response: { error: "Invalid file format or file already exists" },
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
@Delete("delete/:id")
|
||||
@UseGuards(PluginDeleteGuard)
|
||||
async delete(@Param("id") id: number) {
|
||||
await this.pluginsService.delete(id);
|
||||
}
|
||||
}
|
||||
|
||||
export default PluginsController;
|
@ -1,14 +0,0 @@
|
||||
import { Module } from "@nestjs/common";
|
||||
import { TypeOrmModule } from "@nestjs/typeorm";
|
||||
|
||||
import { PluginsController } from "./plugins.controller";
|
||||
import { PluginsService } from "./plugins.service";
|
||||
|
||||
import { Plugin } from "src/entities/plugin.entity";
|
||||
|
||||
@Module({
|
||||
imports: [TypeOrmModule.forFeature([Plugin])],
|
||||
controllers: [PluginsController],
|
||||
providers: [PluginsService],
|
||||
})
|
||||
export class PluginsModule { }
|
@ -1,100 +0,0 @@
|
||||
import { Injectable } from "@nestjs/common";
|
||||
import { InjectRepository } from "@nestjs/typeorm";
|
||||
import { Repository } from "typeorm";
|
||||
import * as path from "path";
|
||||
import * as fs from "fs";
|
||||
|
||||
import * as config from "../../config";
|
||||
|
||||
const { path: pathToPlugins } = config.default.get("pluginsConf");
|
||||
|
||||
import { Plugin } from "src/entities/plugin.entity";
|
||||
|
||||
@Injectable()
|
||||
export class PluginsService {
|
||||
constructor(
|
||||
@InjectRepository(Plugin)
|
||||
private pluginsRepository: Repository<Plugin>
|
||||
) { }
|
||||
|
||||
findAll(): Promise<Plugin[]> {
|
||||
return this.pluginsRepository.find();
|
||||
}
|
||||
|
||||
findOne(id: number): Promise<Plugin> {
|
||||
return this.pluginsRepository.findOneBy({ id });
|
||||
}
|
||||
|
||||
async remove(id: string): Promise<void> {
|
||||
await this.pluginsRepository.delete(id);
|
||||
}
|
||||
|
||||
async activate(id: number): Promise<Plugin> {
|
||||
const plugin: Plugin = await this.pluginsRepository.findOneBy({ id });
|
||||
|
||||
plugin.isActive = !plugin.isActive;
|
||||
|
||||
await this.pluginsRepository.save(plugin);
|
||||
|
||||
return plugin;
|
||||
}
|
||||
|
||||
async upload(
|
||||
originalname: string,
|
||||
filename: string
|
||||
): Promise<Plugin | { error: string }> {
|
||||
const plugin: Plugin = new Plugin();
|
||||
|
||||
const dir = path.join(__dirname, pathToPlugins, `${filename}`);
|
||||
|
||||
const name = originalname.split(".")[0];
|
||||
|
||||
const contents = fs.readFileSync(dir, "utf8");
|
||||
|
||||
let isPlugin = true;
|
||||
|
||||
isPlugin = contents.includes(`"dependencies":{"docspace-plugin"`);
|
||||
|
||||
const splitName = name.split("");
|
||||
|
||||
splitName[0].toUpperCase();
|
||||
|
||||
isPlugin = contents.includes(`window.Plugins.${splitName.join("")}`);
|
||||
|
||||
isPlugin = contents.includes(".prototype.getPluginName=function()");
|
||||
|
||||
isPlugin = contents.includes(".prototype.getPluginVersion=function()");
|
||||
|
||||
isPlugin = contents.includes(".prototype.activate=function()");
|
||||
|
||||
isPlugin = contents.includes(".prototype.deactivate=function()");
|
||||
|
||||
plugin.name = name;
|
||||
plugin.filename = filename;
|
||||
|
||||
plugin.isActive = true;
|
||||
|
||||
if (!isPlugin) {
|
||||
fs.unlink(dir, (err) => {
|
||||
err && console.log(err);
|
||||
});
|
||||
return { error: "It is not a plugin" };
|
||||
}
|
||||
|
||||
return plugin;
|
||||
}
|
||||
|
||||
async delete(id: number) {
|
||||
const plugin: Plugin = await this.pluginsRepository.findOneBy({ id });
|
||||
|
||||
const fileName = plugin.filename;
|
||||
|
||||
const dir = path.join(__dirname, pathToPlugins, `${fileName}`);
|
||||
|
||||
fs.unlink(dir, (err) => {
|
||||
err && console.log(err);
|
||||
});
|
||||
|
||||
await this.pluginsRepository.delete(id);
|
||||
}
|
||||
}
|
@ -1,25 +0,0 @@
|
||||
import * as fs from "fs";
|
||||
import * as path from "path";
|
||||
|
||||
import * as config from "../../config";
|
||||
|
||||
const { path: pathToPlugins } = config.default.get("pluginsConf");
|
||||
|
||||
const fileFilter = (req, file, cb) => {
|
||||
const pluginsDir = path.join(__dirname, pathToPlugins);
|
||||
|
||||
let files = null;
|
||||
let isUniqName = true;
|
||||
|
||||
if (fs.existsSync(pluginsDir)) {
|
||||
files = fs.readdirSync(pluginsDir);
|
||||
|
||||
isUniqName = !files?.includes(file.originalname);
|
||||
}
|
||||
|
||||
if (file.mimetype === "text/javascript" && isUniqName) return cb(null, true);
|
||||
|
||||
return cb(null, false);
|
||||
};
|
||||
|
||||
export default fileFilter;
|
@ -1,3 +0,0 @@
|
||||
import storage from "./storage";
|
||||
|
||||
export { storage };
|
@ -1,24 +0,0 @@
|
||||
import { diskStorage } from "multer";
|
||||
import * as path from "path";
|
||||
import * as fs from "fs";
|
||||
|
||||
import * as config from "../../config";
|
||||
|
||||
const { path: pathToPlugins } = config.default.get("pluginsConf");
|
||||
|
||||
const storage = diskStorage({
|
||||
destination: function (req, file, cb) {
|
||||
const dir = path.join(__dirname, pathToPlugins);
|
||||
|
||||
if (!fs.existsSync(dir)) {
|
||||
fs.mkdirSync(dir);
|
||||
}
|
||||
|
||||
cb(null, dir);
|
||||
},
|
||||
filename: function (req, file, cb) {
|
||||
cb(null, file.originalname);
|
||||
},
|
||||
});
|
||||
|
||||
export default storage;
|
@ -37,6 +37,8 @@ public class MigrationContext : DbContext
|
||||
|
||||
public DbSet<MobileAppInstall> MobileAppInstall { get; set; }
|
||||
public DbSet<DbIPLookup> DbIPLookup { get; set; }
|
||||
public DbSet<DbWebPlugin> WebPlugins { get; set; }
|
||||
|
||||
public DbSet<Regions> Regions { get; set; }
|
||||
|
||||
public DbSet<FireBaseUser> FireBaseUsers { get; set; }
|
||||
@ -112,6 +114,7 @@ public class MigrationContext : DbContext
|
||||
.AddDbTariffRow()
|
||||
.AddMobileAppInstall()
|
||||
.AddDbIPLookup()
|
||||
.AddDbWebPlugins()
|
||||
.AddRegions()
|
||||
.AddFireBaseUsers()
|
||||
.AddNotifyInfo()
|
||||
|
@ -409,48 +409,51 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"csp":{
|
||||
"default": {
|
||||
"def": ["'self'", "data:"],
|
||||
"script": ["'self'", "'unsafe-inline'", "'unsafe-eval'"],
|
||||
"style": ["'self'", "'unsafe-inline'"],
|
||||
"img": ["'self'", "data:", "blob:"],
|
||||
"frame": ["'self'"]
|
||||
},
|
||||
"zendesk":{
|
||||
"def": ["*.zdassets.com", "*.zopim.com", "*.zendesk.com", "wss:" ],
|
||||
"script": ["*.zdassets.com", "*.zopim.com","'unsafe-eval'"],
|
||||
"img": ["*.zopim.io" ]
|
||||
},
|
||||
"firebase":{
|
||||
"script": ["*.googleapis.com"],
|
||||
"def": ["*.googleapis.com" ]
|
||||
},
|
||||
"oform":{
|
||||
"img": ["*.onlyoffice.com"],
|
||||
"def": ["*.onlyoffice.com" ]
|
||||
}
|
||||
"csp": {
|
||||
"default": {
|
||||
"def": ["'self'", "data:"],
|
||||
"script": ["'self'", "'unsafe-inline'", "'unsafe-eval'"],
|
||||
"style": ["'self'", "'unsafe-inline'"],
|
||||
"img": ["'self'", "data:", "blob:"],
|
||||
"frame": ["'self'"]
|
||||
},
|
||||
"zendesk": {
|
||||
"def": ["*.zdassets.com", "*.zopim.com", "*.zendesk.com", "wss:"],
|
||||
"script": ["*.zdassets.com", "*.zopim.com", "'unsafe-eval'"],
|
||||
"img": ["*.zopim.io"]
|
||||
},
|
||||
"firebase": {
|
||||
"script": ["*.googleapis.com"],
|
||||
"def": ["*.googleapis.com"]
|
||||
},
|
||||
"oform": {
|
||||
"img": ["*.onlyoffice.com"],
|
||||
"def": ["*.onlyoffice.com"]
|
||||
}
|
||||
},
|
||||
"logocolors":[
|
||||
{"r":255, "g":102, "b":128},
|
||||
{"r":255, "g":143, "b":64},
|
||||
{"r":242, "g":210, "b":48},
|
||||
{"r":97, "g":192, "b":89},
|
||||
{"r":112, "g":224, "b":150},
|
||||
{"r":31, "g":206, "b":203},
|
||||
{"r":92, "g":195, "b":247},
|
||||
{"r":97, "g":145, "b":242},
|
||||
{"r":119, "g":87, "b":217},
|
||||
{"r":182, "g":121, "b":242},
|
||||
{"r":255, "g":127, "b":212}
|
||||
"logocolors": [
|
||||
{ "r": 255, "g": 102, "b": 128 },
|
||||
{ "r": 255, "g": 143, "b": 64 },
|
||||
{ "r": 242, "g": 210, "b": 48 },
|
||||
{ "r": 97, "g": 192, "b": 89 },
|
||||
{ "r": 112, "g": 224, "b": 150 },
|
||||
{ "r": 31, "g": 206, "b": 203 },
|
||||
{ "r": 92, "g": 195, "b": 247 },
|
||||
{ "r": 97, "g": 145, "b": 242 },
|
||||
{ "r": 119, "g": 87, "b": 217 },
|
||||
{ "r": 182, "g": 121, "b": 242 },
|
||||
{ "r": 255, "g": 127, "b": 212 }
|
||||
],
|
||||
"radicale": {
|
||||
"admin": "",
|
||||
"path": ""
|
||||
},
|
||||
"plugins": {
|
||||
"enabled": "false",
|
||||
"allow": ["upload", "delete"]
|
||||
"enabled": "true",
|
||||
"extension": ".zip",
|
||||
"maxSize": 5242880,
|
||||
"allow": [],
|
||||
"assetExtensions": [".jpg", ".jpeg", ".png", ".svg"]
|
||||
},
|
||||
"aws": {
|
||||
"cloudWatch": {
|
||||
|
@ -98,17 +98,27 @@ server {
|
||||
local red = redis:new()
|
||||
local redis_host = "127.0.0.1"
|
||||
local redis_port = 6379
|
||||
local redis_pass = ""
|
||||
|
||||
red:set_timeout(1000) -- 1 second
|
||||
|
||||
local ok, err = red:connect(redis_host, redis_port)
|
||||
if not ok then
|
||||
ngx.log(ngx.ERR, "failed to connect to redis: ", err)
|
||||
return
|
||||
end
|
||||
|
||||
|
||||
if redis_pass ~= "" then
|
||||
local res, err = red:auth(redis_pass)
|
||||
if not res then
|
||||
ngx.log(ngx.ERR, "failed to authenticate: ", err)
|
||||
return
|
||||
end
|
||||
end
|
||||
|
||||
local csp, err = red:hget(key, "data")
|
||||
|
||||
if csp == ngx.null then
|
||||
if csp == ngx.null or not csp then
|
||||
ngx.log(ngx.ERR, "failed to get redis key: ", err)
|
||||
else
|
||||
ngx.header.Content_Security_Policy = csp
|
||||
@ -255,11 +265,7 @@ server {
|
||||
location ~* /backup {
|
||||
proxy_pass http://127.0.0.1:5012;
|
||||
}
|
||||
|
||||
location ~* /plugins {
|
||||
proxy_pass http://127.0.0.1:5014;
|
||||
}
|
||||
|
||||
|
||||
location ~* /migration {
|
||||
proxy_pass http://127.0.0.1:5034;
|
||||
}
|
||||
|
@ -127,6 +127,23 @@
|
||||
"virtualpath": "~/studio/{0}/userphotos/temp"
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"name": "webplugins",
|
||||
"data": "00000000-0000-0000-0000-000000000000",
|
||||
"type": "disc",
|
||||
"path": "$STORAGE_ROOT\\Studio\\{0}\\webplugins",
|
||||
"virtualpath": "~/studio/{0}/webplugins"
|
||||
},
|
||||
{
|
||||
"name": "systemwebplugins",
|
||||
"data": "00000000-0000-0000-0000-000000000000",
|
||||
"type": "disc",
|
||||
"path": "$STORAGE_ROOT\\Studio\\webplugins",
|
||||
"virtualpath": "~/studio/webplugins",
|
||||
"appendTenantId": false,
|
||||
"disableMigrate": true,
|
||||
"disableEncryption": true
|
||||
}
|
||||
]
|
||||
}
|
||||
|
6
config/telegram.json
Normal file
6
config/telegram.json
Normal file
@ -0,0 +1,6 @@
|
||||
{
|
||||
"telegramConf": {
|
||||
"botKey": "",
|
||||
"chatId": ""
|
||||
}
|
||||
}
|
1325
i18next/client.babel
1325
i18next/client.babel
File diff suppressed because it is too large
Load Diff
7113
migrations/mysql/MigrationContext/20230717114523_MigrationContext_Upgrade2.Designer.cs
generated
Normal file
7113
migrations/mysql/MigrationContext/20230717114523_MigrationContext_Upgrade2.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,70 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Metadata;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace ASC.Migrations.MySql.Migrations.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class MigrationContextUpgrade2 : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "webplugins",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
tenantid = table.Column<int>(name: "tenant_id", type: "int", nullable: false),
|
||||
name = table.Column<string>(type: "varchar(255)", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
version = table.Column<string>(type: "varchar(255)", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
description = table.Column<string>(type: "text", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
license = table.Column<string>(type: "varchar(255)", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
author = table.Column<string>(type: "varchar(255)", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
homepage = table.Column<string>(name: "home_page", type: "varchar(255)", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
pluginname = table.Column<string>(name: "plugin_name", type: "varchar(255)", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
scopes = table.Column<string>(type: "text", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
image = table.Column<string>(type: "varchar(255)", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
createby = table.Column<Guid>(name: "create_by", type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||
createon = table.Column<DateTime>(name: "create_on", type: "datetime", nullable: false),
|
||||
enabled = table.Column<bool>(type: "tinyint(1)", nullable: false, defaultValueSql: "'0'"),
|
||||
system = table.Column<bool>(type: "tinyint(1)", nullable: false, defaultValueSql: "'0'")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PRIMARY", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_webplugins_tenants_tenants_tenant_id",
|
||||
column: x => x.tenantid,
|
||||
principalTable: "tenants_tenants",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "tenant",
|
||||
table: "webplugins",
|
||||
column: "tenant_id");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "webplugins");
|
||||
}
|
||||
}
|
||||
}
|
@ -1521,6 +1521,48 @@ namespace ASC.Migrations.MySql.SaaS.Migrations
|
||||
.Annotation("MySql:CharSet", "utf8")
|
||||
.Annotation("Relational:Collation", "utf8_general_ci");
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "webplugins",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "int", nullable: false)
|
||||
.Annotation("MySql:ValueGenerationStrategy", MySqlValueGenerationStrategy.IdentityColumn),
|
||||
tenantid = table.Column<int>(name: "tenant_id", type: "int", nullable: false),
|
||||
name = table.Column<string>(type: "varchar(255)", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
version = table.Column<string>(type: "varchar(255)", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
description = table.Column<string>(type: "text", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
license = table.Column<string>(type: "varchar(255)", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
author = table.Column<string>(type: "varchar(255)", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
homepage = table.Column<string>(name: "home_page", type: "varchar(255)", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
pluginname = table.Column<string>(name: "plugin_name", type: "varchar(255)", nullable: false)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
scopes = table.Column<string>(type: "text", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
image = table.Column<string>(type: "varchar(255)", nullable: true)
|
||||
.Annotation("MySql:CharSet", "utf8mb4"),
|
||||
createby = table.Column<Guid>(name: "create_by", type: "char(36)", nullable: false, collation: "ascii_general_ci"),
|
||||
createon = table.Column<DateTime>(name: "create_on", type: "datetime", nullable: false),
|
||||
enabled = table.Column<bool>(type: "tinyint(1)", nullable: false, defaultValueSql: "'0'"),
|
||||
system = table.Column<bool>(type: "tinyint(1)", nullable: false, defaultValueSql: "'0'")
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("PRIMARY", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_webplugins_tenants_tenants_tenant_id",
|
||||
column: x => x.tenantid,
|
||||
principalTable: "tenants_tenants",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
})
|
||||
.Annotation("MySql:CharSet", "utf8mb4");
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
table: "files_converts",
|
||||
columns: new[] { "input", "output" },
|
||||
@ -2591,6 +2633,11 @@ namespace ASC.Migrations.MySql.SaaS.Migrations
|
||||
table: "short_links",
|
||||
column: "short",
|
||||
unique: true);
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "tenant",
|
||||
table: "webplugins",
|
||||
column: "tenant_id");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -2766,6 +2813,9 @@ namespace ASC.Migrations.MySql.SaaS.Migrations
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "short_links");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "webplugins");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1653,6 +1653,85 @@ namespace ASC.Migrations.MySql.SaaS.Migrations
|
||||
b.HasAnnotation("MySql:CharSet", "utf8");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ASC.Core.Common.EF.Model.DbWebPlugin", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("int");
|
||||
|
||||
b.Property<string>("Author")
|
||||
.HasColumnType("varchar(255)")
|
||||
.HasColumnName("author");
|
||||
|
||||
b.Property<Guid>("CreateBy")
|
||||
.HasColumnType("char(36)")
|
||||
.HasColumnName("create_by");
|
||||
|
||||
b.Property<DateTime>("CreateOn")
|
||||
.HasColumnType("datetime")
|
||||
.HasColumnName("create_on");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("description");
|
||||
|
||||
b.Property<bool>("Enabled")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("tinyint(1)")
|
||||
.HasColumnName("enabled")
|
||||
.HasDefaultValueSql("'0'");
|
||||
|
||||
b.Property<string>("HomePage")
|
||||
.HasColumnType("varchar(255)")
|
||||
.HasColumnName("home_page");
|
||||
|
||||
b.Property<string>("Image")
|
||||
.HasColumnType("varchar(255)")
|
||||
.HasColumnName("image");
|
||||
|
||||
b.Property<string>("License")
|
||||
.HasColumnType("varchar(255)")
|
||||
.HasColumnName("license");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.Property<string>("PluginName")
|
||||
.IsRequired()
|
||||
.HasColumnType("varchar(255)")
|
||||
.HasColumnName("plugin_name");
|
||||
|
||||
b.Property<string>("Scopes")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("scopes");
|
||||
|
||||
b.Property<bool>("System")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("tinyint(1)")
|
||||
.HasColumnName("system")
|
||||
.HasDefaultValueSql("'0'");
|
||||
|
||||
b.Property<int>("TenantId")
|
||||
.HasColumnType("int")
|
||||
.HasColumnName("tenant_id");
|
||||
|
||||
b.Property<string>("Version")
|
||||
.HasColumnType("varchar(255)")
|
||||
.HasColumnName("version");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("PRIMARY");
|
||||
|
||||
b.HasIndex("TenantId")
|
||||
.HasDatabaseName("tenant");
|
||||
|
||||
b.ToTable("webplugins", (string)null);
|
||||
|
||||
b.HasAnnotation("MySql:CharSet", "utf8mb4");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ASC.Core.Common.EF.Model.DbWebstudioIndex", b =>
|
||||
{
|
||||
b.Property<string>("IndexName")
|
||||
@ -6784,6 +6863,17 @@ namespace ASC.Migrations.MySql.SaaS.Migrations
|
||||
b.Navigation("Tenant");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ASC.Core.Common.EF.Model.DbWebPlugin", b =>
|
||||
{
|
||||
b.HasOne("ASC.Core.Common.EF.Model.DbTenant", "Tenant")
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Tenant");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ASC.Core.Common.EF.Model.DbTenantPartner", b =>
|
||||
{
|
||||
b.HasOne("ASC.Core.Common.EF.Model.DbTenant", "Tenant")
|
||||
|
6943
migrations/postgre/MigrationContext/20230717114525_MigrationContext_Upgrade2.Designer.cs
generated
Normal file
6943
migrations/postgre/MigrationContext/20230717114525_MigrationContext_Upgrade2.Designer.cs
generated
Normal file
File diff suppressed because it is too large
Load Diff
@ -0,0 +1,64 @@
|
||||
using System;
|
||||
using Microsoft.EntityFrameworkCore.Migrations;
|
||||
using Npgsql.EntityFrameworkCore.PostgreSQL.Metadata;
|
||||
|
||||
#nullable disable
|
||||
|
||||
namespace ASC.Migrations.PostgreSql.Migrations
|
||||
{
|
||||
/// <inheritdoc />
|
||||
public partial class MigrationContextUpgrade2 : Migration
|
||||
{
|
||||
/// <inheritdoc />
|
||||
protected override void Up(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.CreateTable(
|
||||
name: "webplugins",
|
||||
schema: "onlyoffice",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
tenantid = table.Column<int>(name: "tenant_id", type: "integer", nullable: false),
|
||||
name = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: false),
|
||||
version = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: true),
|
||||
description = table.Column<string>(type: "text", nullable: true),
|
||||
license = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: true),
|
||||
author = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: true),
|
||||
homepage = table.Column<string>(name: "home_page", type: "character varying(255)", maxLength: 255, nullable: true),
|
||||
pluginname = table.Column<string>(name: "plugin_name", type: "character varying(255)", maxLength: 255, nullable: false),
|
||||
scopes = table.Column<string>(type: "text", nullable: true),
|
||||
image = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: true),
|
||||
createby = table.Column<Guid>(name: "create_by", type: "uuid", fixedLength: true, maxLength: 36, nullable: false),
|
||||
createon = table.Column<DateTime>(name: "create_on", type: "timestamp with time zone", nullable: false),
|
||||
enabled = table.Column<bool>(type: "boolean", nullable: false),
|
||||
system = table.Column<bool>(type: "boolean", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("webplugins_pkey", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_webplugins_tenants_tenants_tenant_id",
|
||||
column: x => x.tenantid,
|
||||
principalSchema: "onlyoffice",
|
||||
principalTable: "tenants_tenants",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "tenant_webplugins",
|
||||
schema: "onlyoffice",
|
||||
table: "webplugins",
|
||||
column: "tenant_id");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
protected override void Down(MigrationBuilder migrationBuilder)
|
||||
{
|
||||
migrationBuilder.DropTable(
|
||||
name: "webplugins",
|
||||
schema: "onlyoffice");
|
||||
}
|
||||
}
|
||||
}
|
@ -1296,6 +1296,40 @@ namespace ASC.Migrations.PostgreSql.SaaS.Migrations
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.CreateTable(
|
||||
name: "webplugins",
|
||||
schema: "onlyoffice",
|
||||
columns: table => new
|
||||
{
|
||||
Id = table.Column<int>(type: "integer", nullable: false)
|
||||
.Annotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn),
|
||||
tenantid = table.Column<int>(name: "tenant_id", type: "integer", nullable: false),
|
||||
name = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: false),
|
||||
version = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: true),
|
||||
description = table.Column<string>(type: "text", nullable: true),
|
||||
license = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: true),
|
||||
author = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: true),
|
||||
homepage = table.Column<string>(name: "home_page", type: "character varying(255)", maxLength: 255, nullable: true),
|
||||
pluginname = table.Column<string>(name: "plugin_name", type: "character varying(255)", maxLength: 255, nullable: false),
|
||||
scopes = table.Column<string>(type: "text", nullable: true),
|
||||
image = table.Column<string>(type: "character varying(255)", maxLength: 255, nullable: true),
|
||||
createby = table.Column<Guid>(name: "create_by", type: "uuid", fixedLength: true, maxLength: 36, nullable: false),
|
||||
createon = table.Column<DateTime>(name: "create_on", type: "timestamp with time zone", nullable: false),
|
||||
enabled = table.Column<bool>(type: "boolean", nullable: false),
|
||||
system = table.Column<bool>(type: "boolean", nullable: false)
|
||||
},
|
||||
constraints: table =>
|
||||
{
|
||||
table.PrimaryKey("webplugins_pkey", x => x.Id);
|
||||
table.ForeignKey(
|
||||
name: "FK_webplugins_tenants_tenants_tenant_id",
|
||||
column: x => x.tenantid,
|
||||
principalSchema: "onlyoffice",
|
||||
principalTable: "tenants_tenants",
|
||||
principalColumn: "id",
|
||||
onDelete: ReferentialAction.Cascade);
|
||||
});
|
||||
|
||||
migrationBuilder.InsertData(
|
||||
schema: "onlyoffice",
|
||||
table: "files_converts",
|
||||
@ -2420,6 +2454,12 @@ namespace ASC.Migrations.PostgreSql.SaaS.Migrations
|
||||
schema: "onlyoffice",
|
||||
table: "webstudio_uservisit",
|
||||
column: "visitdate");
|
||||
|
||||
migrationBuilder.CreateIndex(
|
||||
name: "tenant_webplugins",
|
||||
schema: "onlyoffice",
|
||||
table: "webplugins",
|
||||
column: "tenant_id");
|
||||
}
|
||||
|
||||
/// <inheritdoc />
|
||||
@ -2634,6 +2674,10 @@ namespace ASC.Migrations.PostgreSql.SaaS.Migrations
|
||||
migrationBuilder.DropTable(
|
||||
name: "tenants_tenants",
|
||||
schema: "onlyoffice");
|
||||
|
||||
migrationBuilder.DropTable(
|
||||
name: "webplugins",
|
||||
schema: "onlyoffice");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1554,6 +1554,89 @@ namespace ASC.Migrations.PostgreSql.SaaS.Migrations
|
||||
b.ToTable("tenants_version", "onlyoffice");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ASC.Core.Common.EF.Model.DbWebPlugin", b =>
|
||||
{
|
||||
b.Property<int>("Id")
|
||||
.ValueGeneratedOnAdd()
|
||||
.HasColumnType("integer")
|
||||
.HasAnnotation("Npgsql:ValueGenerationStrategy", NpgsqlValueGenerationStrategy.IdentityByDefaultColumn);
|
||||
|
||||
b.Property<string>("Author")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("character varying(255)")
|
||||
.HasColumnName("author");
|
||||
|
||||
b.Property<Guid>("CreateBy")
|
||||
.HasMaxLength(36)
|
||||
.HasColumnType("uuid")
|
||||
.HasColumnName("create_by")
|
||||
.IsFixedLength();
|
||||
|
||||
b.Property<DateTime>("CreateOn")
|
||||
.HasColumnType("timestamp with time zone")
|
||||
.HasColumnName("create_on");
|
||||
|
||||
b.Property<string>("Description")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("description");
|
||||
|
||||
b.Property<bool>("Enabled")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("enabled");
|
||||
|
||||
b.Property<string>("HomePage")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("character varying(255)")
|
||||
.HasColumnName("home_page");
|
||||
|
||||
b.Property<string>("Image")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("character varying(255)")
|
||||
.HasColumnName("image");
|
||||
|
||||
b.Property<string>("License")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("character varying(255)")
|
||||
.HasColumnName("license");
|
||||
|
||||
b.Property<string>("Name")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("character varying(255)")
|
||||
.HasColumnName("name");
|
||||
|
||||
b.Property<string>("PluginName")
|
||||
.IsRequired()
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("character varying(255)")
|
||||
.HasColumnName("plugin_name");
|
||||
|
||||
b.Property<string>("Scopes")
|
||||
.HasColumnType("text")
|
||||
.HasColumnName("scopes");
|
||||
|
||||
b.Property<bool>("System")
|
||||
.HasColumnType("boolean")
|
||||
.HasColumnName("system");
|
||||
|
||||
b.Property<int>("TenantId")
|
||||
.HasColumnType("integer")
|
||||
.HasColumnName("tenant_id");
|
||||
|
||||
b.Property<string>("Version")
|
||||
.HasMaxLength(255)
|
||||
.HasColumnType("character varying(255)")
|
||||
.HasColumnName("version");
|
||||
|
||||
b.HasKey("Id")
|
||||
.HasName("webplugins_pkey");
|
||||
|
||||
b.HasIndex("TenantId")
|
||||
.HasDatabaseName("tenant_webplugins");
|
||||
|
||||
b.ToTable("webplugins", "onlyoffice");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ASC.Core.Common.EF.Model.DbWebstudioIndex", b =>
|
||||
{
|
||||
b.Property<string>("IndexName")
|
||||
@ -6515,6 +6598,17 @@ namespace ASC.Migrations.PostgreSql.SaaS.Migrations
|
||||
b.Navigation("Tenant");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ASC.Core.Common.EF.Model.DbWebPlugin", b =>
|
||||
{
|
||||
b.HasOne("ASC.Core.Common.EF.Model.DbTenant", "Tenant")
|
||||
.WithMany()
|
||||
.HasForeignKey("TenantId")
|
||||
.OnDelete(DeleteBehavior.Cascade)
|
||||
.IsRequired();
|
||||
|
||||
b.Navigation("Tenant");
|
||||
});
|
||||
|
||||
modelBuilder.Entity("ASC.Core.Common.EF.Model.DbWebstudioSettings", b =>
|
||||
{
|
||||
b.HasOne("ASC.Core.Common.EF.Model.DbTenant", "Tenant")
|
||||
|
1
packages/client/public/locales/ar-SA/WebPlugins.json
Normal file
1
packages/client/public/locales/ar-SA/WebPlugins.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
1
packages/client/public/locales/az/WebPlugins.json
Normal file
1
packages/client/public/locales/az/WebPlugins.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
1
packages/client/public/locales/bg/WebPlugins.json
Normal file
1
packages/client/public/locales/bg/WebPlugins.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
1
packages/client/public/locales/cs/WebPlugins.json
Normal file
1
packages/client/public/locales/cs/WebPlugins.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
1
packages/client/public/locales/de/WebPlugins.json
Normal file
1
packages/client/public/locales/de/WebPlugins.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
1
packages/client/public/locales/el-GR/WebPlugins.json
Normal file
1
packages/client/public/locales/el-GR/WebPlugins.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
13
packages/client/public/locales/en/WebPlugins.json
Normal file
13
packages/client/public/locales/en/WebPlugins.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"DeletePlugin": "Delete plugin",
|
||||
"DeletePluginDescription": "This plugin will no longer be available to DocSpace users. Are you sure you want to continue?",
|
||||
"DeletePluginTitle": "Delete plugin?",
|
||||
"Description": "You can add plugins to extend the functionality of DocSpace with extra features. Click Upload plugin and select the plugin file in the ZIP archive.",
|
||||
"Metadata": "Metadata",
|
||||
"NeedSettings": "Need enter settings",
|
||||
"NoPlugins": "No plugins added",
|
||||
"NotNeedSettings": "Not need enter settings",
|
||||
"Plugins": "Plugins",
|
||||
"PluginsHelp": "Plugins allow you to extend the functionality of DocSpace",
|
||||
"UploadPlugin": "Upload plugin"
|
||||
}
|
1
packages/client/public/locales/es/WebPlugins.json
Normal file
1
packages/client/public/locales/es/WebPlugins.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
1
packages/client/public/locales/fi/WebPlugins.json
Normal file
1
packages/client/public/locales/fi/WebPlugins.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
1
packages/client/public/locales/fr/WebPlugins.json
Normal file
1
packages/client/public/locales/fr/WebPlugins.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
1
packages/client/public/locales/hy-AM/WebPlugins.json
Normal file
1
packages/client/public/locales/hy-AM/WebPlugins.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
1
packages/client/public/locales/it/WebPlugins.json
Normal file
1
packages/client/public/locales/it/WebPlugins.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
1
packages/client/public/locales/ja-JP/WebPlugins.json
Normal file
1
packages/client/public/locales/ja-JP/WebPlugins.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
1
packages/client/public/locales/ko-KR/WebPlugins.json
Normal file
1
packages/client/public/locales/ko-KR/WebPlugins.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
1
packages/client/public/locales/lo-LA/WebPlugins.json
Normal file
1
packages/client/public/locales/lo-LA/WebPlugins.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
1
packages/client/public/locales/lv/WebPlugins.json
Normal file
1
packages/client/public/locales/lv/WebPlugins.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
1
packages/client/public/locales/nl/WebPlugins.json
Normal file
1
packages/client/public/locales/nl/WebPlugins.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
1
packages/client/public/locales/pl/WebPlugins.json
Normal file
1
packages/client/public/locales/pl/WebPlugins.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
1
packages/client/public/locales/pt-BR/WebPlugins.json
Normal file
1
packages/client/public/locales/pt-BR/WebPlugins.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
1
packages/client/public/locales/pt/WebPlugins.json
Normal file
1
packages/client/public/locales/pt/WebPlugins.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
1
packages/client/public/locales/ro/WebPlugins.json
Normal file
1
packages/client/public/locales/ro/WebPlugins.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
13
packages/client/public/locales/ru/WebPlugins.json
Normal file
13
packages/client/public/locales/ru/WebPlugins.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"DeletePlugin": "Удалить плагин",
|
||||
"DeletePluginDescription": "Этот плагин больше не будет доступен пользователям DocSpace. Вы уверены что хотите продолжить?",
|
||||
"DeletePluginTitle": "Удалить плагин?",
|
||||
"Description": "Вы можете добавлять плагины, чтобы расширить функциональность DocSpace дополнительными функциями. Нажмите «Загрузить плагин» и выберите файл плагина в ZIP-архиве.",
|
||||
"Metadata": "Метаданные",
|
||||
"NeedSettings": "Нужно ввести настройки",
|
||||
"NoPlugins": "Плагины не добавлены",
|
||||
"NotNeedSettings": "Не нужно вводить настройки",
|
||||
"Plugins": "Плагины",
|
||||
"PluginsHelp": "Плагины позволяют расширить функциональность DocSpace.",
|
||||
"UploadPlugin": "Загрузить плагин"
|
||||
}
|
1
packages/client/public/locales/sk/WebPlugins.json
Normal file
1
packages/client/public/locales/sk/WebPlugins.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
1
packages/client/public/locales/sl/WebPlugins.json
Normal file
1
packages/client/public/locales/sl/WebPlugins.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
1
packages/client/public/locales/tr/WebPlugins.json
Normal file
1
packages/client/public/locales/tr/WebPlugins.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
1
packages/client/public/locales/uk-UA/WebPlugins.json
Normal file
1
packages/client/public/locales/uk-UA/WebPlugins.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
1
packages/client/public/locales/vi/WebPlugins.json
Normal file
1
packages/client/public/locales/vi/WebPlugins.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
1
packages/client/public/locales/zh-CN/WebPlugins.json
Normal file
1
packages/client/public/locales/zh-CN/WebPlugins.json
Normal file
@ -0,0 +1 @@
|
||||
{}
|
@ -133,8 +133,8 @@ const ClientContent = (props) => {
|
||||
|
||||
return (
|
||||
<>
|
||||
<GlobalEvents />
|
||||
<FilesPanels />
|
||||
<GlobalEvents />
|
||||
{!isFormGallery ? (
|
||||
isFrame ? (
|
||||
showMenu && <ClientArticle />
|
||||
@ -154,7 +154,7 @@ const ClientContent = (props) => {
|
||||
};
|
||||
|
||||
const Client = inject(
|
||||
({ auth, clientLoadingStore, filesStore, peopleStore }) => {
|
||||
({ auth, clientLoadingStore, filesStore, peopleStore, pluginStore }) => {
|
||||
const {
|
||||
frameConfig,
|
||||
isFrame,
|
||||
@ -162,6 +162,7 @@ const Client = inject(
|
||||
encryptionKeys,
|
||||
setEncryptionKeys,
|
||||
isEncryptionSupport,
|
||||
enablePlugins,
|
||||
isDesktopClientInit,
|
||||
setIsDesktopClientInit,
|
||||
} = auth.settingsStore;
|
||||
@ -170,14 +171,13 @@ const Client = inject(
|
||||
|
||||
const { isVisitor } = auth.userStore.user;
|
||||
|
||||
const {
|
||||
isLoading,
|
||||
setIsSectionFilterLoading,
|
||||
setIsSectionHeaderLoading,
|
||||
} = clientLoadingStore;
|
||||
const { isLoading, setIsSectionFilterLoading, setIsSectionHeaderLoading } =
|
||||
clientLoadingStore;
|
||||
|
||||
const withMainButton = !isVisitor;
|
||||
|
||||
const { isInit: isInitPlugins, initPlugins } = pluginStore;
|
||||
|
||||
return {
|
||||
isDesktop: isDesktopClient,
|
||||
isDesktopClientInit,
|
||||
@ -199,6 +199,8 @@ const Client = inject(
|
||||
const actions = [];
|
||||
actions.push(filesStore.initFiles());
|
||||
actions.push(peopleStore.init());
|
||||
|
||||
if (enablePlugins && !isInitPlugins) actions.push(initPlugins());
|
||||
await Promise.all(actions);
|
||||
},
|
||||
};
|
||||
|
@ -34,13 +34,8 @@ export default function withFileActions(WrappedFileItem) {
|
||||
};
|
||||
|
||||
onDropZoneUpload = (files, uploadToFolder) => {
|
||||
const {
|
||||
t,
|
||||
dragging,
|
||||
setDragging,
|
||||
startUpload,
|
||||
uploadEmptyFolders,
|
||||
} = this.props;
|
||||
const { t, dragging, setDragging, startUpload, uploadEmptyFolders } =
|
||||
this.props;
|
||||
|
||||
dragging && setDragging(false);
|
||||
|
||||
@ -93,6 +88,9 @@ export default function withFileActions(WrappedFileItem) {
|
||||
e.target.classList.contains("item-file-name") ||
|
||||
e.target.classList.contains("row-content-link");
|
||||
|
||||
if ((isRoomsFolder || isArchiveFolder) && isFileName && !isSelected)
|
||||
setBufferSelection(item);
|
||||
|
||||
if (
|
||||
isPrivacy ||
|
||||
isTrashFolder ||
|
||||
|
@ -375,6 +375,7 @@ const ShellWrapper = inject(({ auth, backup }) => {
|
||||
whiteLabelLogoUrls,
|
||||
standalone,
|
||||
} = settingsStore;
|
||||
|
||||
const isBase = settingsStore.theme.isBase;
|
||||
const { setPreparationPortalDialogVisible } = backup;
|
||||
|
||||
|
@ -183,6 +183,7 @@ const Items = ({
|
||||
|
||||
activeItemId,
|
||||
emptyTrashInProgress,
|
||||
|
||||
isCommunity,
|
||||
isPaymentPageAvailable,
|
||||
}) => {
|
||||
|
@ -12,6 +12,7 @@ import PersonManagerReactSvgUrl from "PUBLIC_DIR/images/person.manager.react.svg
|
||||
import PersonReactSvgUrl from "PUBLIC_DIR/images/person.react.svg?url";
|
||||
import PersonUserReactSvgUrl from "PUBLIC_DIR/images/person.user.react.svg?url";
|
||||
import InviteAgainReactSvgUrl from "PUBLIC_DIR/images/invite.again.react.svg?url";
|
||||
import PluginMoreReactSvgUrl from "PUBLIC_DIR/images/plugin.more.react.svg?url";
|
||||
import React from "react";
|
||||
|
||||
import { isMobileOnly } from "react-device-detect";
|
||||
@ -26,8 +27,6 @@ import { useNavigate, useLocation } from "react-router-dom";
|
||||
import MobileView from "./MobileView";
|
||||
|
||||
import { Events, EmployeeType } from "@docspace/common/constants";
|
||||
import { getMainButtonItems } from "SRC_DIR/helpers/plugins";
|
||||
|
||||
import toastr from "@docspace/components/toast/toastr";
|
||||
import styled, { css } from "styled-components";
|
||||
import Button from "@docspace/components/button";
|
||||
@ -110,6 +109,7 @@ const ArticleMainButtonContent = (props) => {
|
||||
isArchiveFolder,
|
||||
|
||||
enablePlugins,
|
||||
mainButtonItemsList,
|
||||
|
||||
currentColorScheme,
|
||||
|
||||
@ -254,6 +254,17 @@ const ArticleMainButtonContent = (props) => {
|
||||
React.useEffect(() => {
|
||||
if (isRoomsFolder || isSettingsPage) return;
|
||||
|
||||
const pluginItems = [];
|
||||
|
||||
if (mainButtonItemsList && enablePlugins && !isAccountsPage) {
|
||||
mainButtonItemsList.forEach((option) => {
|
||||
pluginItems.push({
|
||||
key: option.key,
|
||||
...option.value,
|
||||
});
|
||||
});
|
||||
}
|
||||
|
||||
const formActions = [
|
||||
{
|
||||
id: "actions_template",
|
||||
@ -411,6 +422,18 @@ const ArticleMainButtonContent = (props) => {
|
||||
|
||||
const menuModel = [...actions];
|
||||
|
||||
if (pluginItems.length > 0) {
|
||||
menuModel.push({
|
||||
id: "actions_more-plugins",
|
||||
className: "main-button_drop-down",
|
||||
icon: PluginMoreReactSvgUrl,
|
||||
label: t("Common:More"),
|
||||
disabled: false,
|
||||
key: "more-plugins",
|
||||
items: pluginItems,
|
||||
});
|
||||
}
|
||||
|
||||
menuModel.push({
|
||||
isSeparator: true,
|
||||
key: "separator",
|
||||
@ -419,19 +442,6 @@ const ArticleMainButtonContent = (props) => {
|
||||
menuModel.push(...uploadActions);
|
||||
setUploadActions(uploadActions);
|
||||
|
||||
if (enablePlugins) {
|
||||
const pluginOptions = getMainButtonItems();
|
||||
|
||||
if (pluginOptions) {
|
||||
pluginOptions.forEach((option) => {
|
||||
menuModel.splice(option.value.position, 0, {
|
||||
key: option.key,
|
||||
...option.value,
|
||||
});
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
setModel(menuModel);
|
||||
setActions(actions);
|
||||
}, [
|
||||
@ -441,9 +451,11 @@ const ArticleMainButtonContent = (props) => {
|
||||
isAccountsPage,
|
||||
isSettingsPage,
|
||||
enablePlugins,
|
||||
mainButtonItemsList,
|
||||
isRoomsFolder,
|
||||
isOwner,
|
||||
isAdmin,
|
||||
|
||||
onCreate,
|
||||
onCreateRoom,
|
||||
onInvite,
|
||||
@ -557,6 +569,7 @@ export default inject(
|
||||
treeFoldersStore,
|
||||
selectedFolderStore,
|
||||
clientLoadingStore,
|
||||
pluginStore,
|
||||
}) => {
|
||||
const { showArticleLoader } = clientLoadingStore;
|
||||
const { mainButtonMobileVisible } = filesStore;
|
||||
@ -588,6 +601,8 @@ export default inject(
|
||||
const { isAdmin, isOwner } = auth.userStore.user;
|
||||
const { isGracePeriod } = auth.currentTariffStatusStore;
|
||||
|
||||
const { mainButtonItemsList } = pluginStore;
|
||||
|
||||
return {
|
||||
isGracePeriod,
|
||||
setInviteUsersWarningDialogVisible,
|
||||
@ -613,6 +628,8 @@ export default inject(
|
||||
currentFolderId,
|
||||
|
||||
enablePlugins,
|
||||
mainButtonItemsList,
|
||||
|
||||
currentColorScheme,
|
||||
|
||||
isAdmin,
|
||||
|
@ -29,6 +29,9 @@ import {
|
||||
DeleteLinkDialog,
|
||||
RoomSharingDialog,
|
||||
MoveToPublicRoom,
|
||||
SettingsPluginDialog,
|
||||
PluginDialog,
|
||||
DeletePluginDialog,
|
||||
} from "../dialogs";
|
||||
import ConvertPasswordDialog from "../dialogs/ConvertPasswordDialog";
|
||||
import ArchiveDialog from "../dialogs/ArchiveDialog";
|
||||
@ -78,8 +81,11 @@ const Panels = (props) => {
|
||||
embeddingPanelIsVisible,
|
||||
roomSharingPanelVisible,
|
||||
moveToPublicRoomVisible,
|
||||
settingsPluginDialogVisible,
|
||||
pluginDialogVisible,
|
||||
leaveRoomDialogVisible,
|
||||
changeRoomOwnerIsVisible,
|
||||
deletePluginDialogVisible,
|
||||
} = props;
|
||||
|
||||
const { t } = useTranslation(["Translations", "Common"]);
|
||||
@ -89,6 +95,21 @@ const Panels = (props) => {
|
||||
};
|
||||
|
||||
return [
|
||||
settingsPluginDialogVisible && (
|
||||
<SettingsPluginDialog
|
||||
isVisible={settingsPluginDialogVisible}
|
||||
key={"settings-plugin-dialog"}
|
||||
/>
|
||||
),
|
||||
deletePluginDialogVisible && (
|
||||
<DeletePluginDialog
|
||||
isVisible={deletePluginDialogVisible}
|
||||
key={"delete-plugin-dialog"}
|
||||
/>
|
||||
),
|
||||
pluginDialogVisible && (
|
||||
<PluginDialog isVisible={pluginDialogVisible} key={"plugin-dialog"} />
|
||||
),
|
||||
uploadPanelVisible && <UploadPanel key="upload-panel" />,
|
||||
sharingPanelVisible && (
|
||||
<SharingPanel
|
||||
@ -179,6 +200,7 @@ export default inject(
|
||||
versionHistoryStore,
|
||||
backup,
|
||||
createEditRoomStore,
|
||||
pluginStore,
|
||||
}) => {
|
||||
const {
|
||||
sharingPanelVisible,
|
||||
@ -227,6 +249,12 @@ export default inject(
|
||||
const { hotkeyPanelVisible } = auth.settingsStore;
|
||||
const { confirmDialogIsLoading } = createEditRoomStore;
|
||||
|
||||
const {
|
||||
settingsPluginDialogVisible,
|
||||
deletePluginDialogVisible,
|
||||
pluginDialogVisible,
|
||||
} = pluginStore;
|
||||
|
||||
return {
|
||||
preparationPortalDialogVisible,
|
||||
sharingPanelVisible,
|
||||
@ -265,8 +293,11 @@ export default inject(
|
||||
embeddingPanelIsVisible,
|
||||
roomSharingPanelVisible,
|
||||
moveToPublicRoomVisible,
|
||||
settingsPluginDialogVisible,
|
||||
pluginDialogVisible,
|
||||
leaveRoomDialogVisible,
|
||||
changeRoomOwnerIsVisible,
|
||||
deletePluginDialogVisible,
|
||||
};
|
||||
}
|
||||
)(observer(Panels));
|
||||
|
@ -0,0 +1,151 @@
|
||||
import React from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import { messageActions } from "SRC_DIR/helpers/plugins/utils";
|
||||
|
||||
import Dialog from "./sub-components/Dialog";
|
||||
|
||||
const CreatePluginFile = ({
|
||||
visible,
|
||||
title,
|
||||
startValue,
|
||||
onSave,
|
||||
onCancel,
|
||||
onClose,
|
||||
isCreateDialog,
|
||||
options,
|
||||
selectedOption,
|
||||
onSelect,
|
||||
extension,
|
||||
|
||||
pluginId,
|
||||
pluginName,
|
||||
pluginSystem,
|
||||
|
||||
updatePluginStatus,
|
||||
setCurrentSettingsDialogPlugin,
|
||||
setSettingsPluginDialogVisible,
|
||||
setPluginDialogVisible,
|
||||
setPluginDialogProps,
|
||||
|
||||
updateContextMenuItems,
|
||||
updateInfoPanelItems,
|
||||
updateMainButtonItems,
|
||||
updateProfileMenuItems,
|
||||
updateEventListenerItems,
|
||||
updateFileItems,
|
||||
}) => {
|
||||
const { t } = useTranslation(["Translations", "Common", "Files"]);
|
||||
|
||||
const onSaveAction = async (e, value) => {
|
||||
if (!onSave) return onCloseAction();
|
||||
|
||||
const message = await onSave(e, value);
|
||||
|
||||
messageActions(
|
||||
message,
|
||||
null,
|
||||
|
||||
pluginId,
|
||||
pluginName,
|
||||
pluginSystem,
|
||||
|
||||
setSettingsPluginDialogVisible,
|
||||
setCurrentSettingsDialogPlugin,
|
||||
updatePluginStatus,
|
||||
null,
|
||||
setPluginDialogVisible,
|
||||
setPluginDialogProps,
|
||||
|
||||
updateContextMenuItems,
|
||||
updateInfoPanelItems,
|
||||
updateMainButtonItems,
|
||||
updateProfileMenuItems,
|
||||
updateEventListenerItems,
|
||||
updateFileItems
|
||||
);
|
||||
onCloseAction();
|
||||
};
|
||||
|
||||
const onCloseAction = (e) => {
|
||||
onCancel && onCancel();
|
||||
onClose && onClose();
|
||||
};
|
||||
|
||||
const onSelectAction = (option) => {
|
||||
if (!onSelect) return;
|
||||
const message = onSelect(option);
|
||||
|
||||
messageActions(
|
||||
message,
|
||||
null,
|
||||
|
||||
pluginId,
|
||||
pluginName,
|
||||
pluginSystem,
|
||||
|
||||
setSettingsPluginDialogVisible,
|
||||
setCurrentSettingsDialogPlugin,
|
||||
updatePluginStatus,
|
||||
null,
|
||||
setPluginDialogVisible,
|
||||
setPluginDialogProps,
|
||||
|
||||
updateContextMenuItems,
|
||||
updateInfoPanelItems,
|
||||
updateMainButtonItems,
|
||||
updateProfileMenuItems,
|
||||
updateEventListenerItems,
|
||||
updateFileItems
|
||||
);
|
||||
};
|
||||
|
||||
return (
|
||||
<Dialog
|
||||
t={t}
|
||||
visible={visible}
|
||||
title={title}
|
||||
startValue={startValue}
|
||||
onSave={onSaveAction}
|
||||
onCancel={onCloseAction}
|
||||
onClose={onCloseAction}
|
||||
isCreateDialog={isCreateDialog}
|
||||
extension={extension}
|
||||
options={options}
|
||||
selectedOption={selectedOption}
|
||||
onSelect={onSelectAction}
|
||||
/>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ pluginStore }) => {
|
||||
const {
|
||||
updatePluginStatus,
|
||||
setCurrentSettingsDialogPlugin,
|
||||
setSettingsPluginDialogVisible,
|
||||
setPluginDialogVisible,
|
||||
setPluginDialogProps,
|
||||
|
||||
updateContextMenuItems,
|
||||
updateInfoPanelItems,
|
||||
updateMainButtonItems,
|
||||
updateProfileMenuItems,
|
||||
updateEventListenerItems,
|
||||
updateFileItems,
|
||||
} = pluginStore;
|
||||
return {
|
||||
updatePluginStatus,
|
||||
setCurrentSettingsDialogPlugin,
|
||||
setSettingsPluginDialogVisible,
|
||||
setPluginDialogVisible,
|
||||
setPluginDialogProps,
|
||||
|
||||
updateContextMenuItems,
|
||||
updateInfoPanelItems,
|
||||
updateMainButtonItems,
|
||||
updateProfileMenuItems,
|
||||
updateEventListenerItems,
|
||||
updateFileItems,
|
||||
};
|
||||
})(observer(CreatePluginFile));
|
@ -1,4 +1,6 @@
|
||||
import React, { useState, useEffect, useCallback, memo } from "react";
|
||||
import { useState, useEffect, useCallback, useRef, memo } from "react";
|
||||
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import { FileAction } from "@docspace/common/constants";
|
||||
import { Events } from "@docspace/common/constants";
|
||||
@ -8,8 +10,9 @@ import RenameEvent from "./RenameEvent";
|
||||
import CreateRoomEvent from "./CreateRoomEvent";
|
||||
import EditRoomEvent from "./EditRoomEvent";
|
||||
import ChangeUserTypeEvent from "./ChangeUserTypeEvent";
|
||||
import CreatePluginFile from "./CreatePluginFileEvent";
|
||||
|
||||
const GlobalEvents = () => {
|
||||
const GlobalEvents = ({ enablePlugins, eventListenerItemsList }) => {
|
||||
const [createDialogProps, setCreateDialogProps] = useState({
|
||||
visible: false,
|
||||
id: null,
|
||||
@ -43,6 +46,14 @@ const GlobalEvents = () => {
|
||||
onClose: null,
|
||||
});
|
||||
|
||||
const [createPluginFileDialog, setCreatePluginFileProps] = useState({
|
||||
visible: false,
|
||||
props: null,
|
||||
onClose: null,
|
||||
});
|
||||
|
||||
const eventHandlersList = useRef([]);
|
||||
|
||||
const onCreate = useCallback((e) => {
|
||||
const { payload } = e;
|
||||
|
||||
@ -115,11 +126,29 @@ const GlobalEvents = () => {
|
||||
const onChangeUserType = useCallback((e) => {
|
||||
setChangeUserTypeDialogProps({
|
||||
visible: true,
|
||||
onClose: () =>
|
||||
setChangeUserTypeDialogProps({ visible: false, onClose: null }),
|
||||
onClose: () => {
|
||||
setChangeUserTypeDialogProps({ visible: false, onClose: null });
|
||||
},
|
||||
});
|
||||
}, []);
|
||||
|
||||
const onCreatePluginFileDialog = useCallback(
|
||||
(e) => {
|
||||
if (!enablePlugins) return;
|
||||
|
||||
const { payload } = e;
|
||||
setCreatePluginFileProps({
|
||||
...payload,
|
||||
visible: true,
|
||||
onClose: () => {
|
||||
payload.onClose && payload.onClose();
|
||||
setCreatePluginFileProps({ visible: false, onClose: null });
|
||||
},
|
||||
});
|
||||
},
|
||||
[enablePlugins]
|
||||
);
|
||||
|
||||
useEffect(() => {
|
||||
window.addEventListener(Events.CREATE, onCreate);
|
||||
window.addEventListener(Events.RENAME, onRename);
|
||||
@ -127,14 +156,57 @@ const GlobalEvents = () => {
|
||||
window.addEventListener(Events.ROOM_EDIT, onEditRoom);
|
||||
window.addEventListener(Events.CHANGE_USER_TYPE, onChangeUserType);
|
||||
|
||||
if (enablePlugins) {
|
||||
window.addEventListener(
|
||||
Events.CREATE_PLUGIN_FILE,
|
||||
onCreatePluginFileDialog
|
||||
);
|
||||
|
||||
if (eventListenerItemsList) {
|
||||
eventListenerItemsList.forEach((item) => {
|
||||
const eventHandler = (e) => {
|
||||
item.eventHandler(e);
|
||||
};
|
||||
|
||||
eventHandlersList.current.push(eventHandler);
|
||||
|
||||
window.addEventListener(item.eventType, eventHandler);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
return () => {
|
||||
window.removeEventListener(Events.CREATE, onCreate);
|
||||
window.removeEventListener(Events.RENAME, onRename);
|
||||
window.removeEventListener(Events.ROOM_CREATE, onCreateRoom);
|
||||
window.removeEventListener(Events.ROOM_EDIT, onEditRoom);
|
||||
window.removeEventListener(Events.CHANGE_USER_TYPE, onChangeUserType);
|
||||
|
||||
if (enablePlugins) {
|
||||
window.removeEventListener(
|
||||
Events.CREATE_PLUGIN_FILE,
|
||||
onCreatePluginFileDialog
|
||||
);
|
||||
|
||||
if (eventListenerItemsList) {
|
||||
eventListenerItemsList.forEach((item, index) => {
|
||||
window.removeEventListener(
|
||||
item.eventType,
|
||||
eventHandlersList.current[index]
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
};
|
||||
}, [onRename, onCreate, onCreateRoom, onEditRoom, onChangeUserType]);
|
||||
}, [
|
||||
onRename,
|
||||
onCreate,
|
||||
onCreateRoom,
|
||||
onEditRoom,
|
||||
onChangeUserType,
|
||||
onCreatePluginFileDialog,
|
||||
enablePlugins,
|
||||
]);
|
||||
|
||||
return [
|
||||
createDialogProps.visible && (
|
||||
@ -155,7 +227,19 @@ const GlobalEvents = () => {
|
||||
{...changeUserTypeDialog}
|
||||
/>
|
||||
),
|
||||
createPluginFileDialog.visible && (
|
||||
<CreatePluginFile
|
||||
key={Events.CREATE_PLUGIN_FILE}
|
||||
{...createPluginFileDialog}
|
||||
/>
|
||||
),
|
||||
];
|
||||
};
|
||||
|
||||
export default memo(GlobalEvents);
|
||||
export default inject(({ auth, pluginStore }) => {
|
||||
const { enablePlugins } = auth.settingsStore;
|
||||
|
||||
const { eventListenerItemsList } = pluginStore;
|
||||
|
||||
return { enablePlugins, eventListenerItemsList };
|
||||
})(observer(GlobalEvents));
|
||||
|
@ -0,0 +1,84 @@
|
||||
import React from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import ModalDialog from "@docspace/components/modal-dialog";
|
||||
import Button from "@docspace/components/button";
|
||||
import toastr from "@docspace/components/toast/toastr";
|
||||
|
||||
import ModalDialogContainer from "../ModalDialogContainer";
|
||||
|
||||
const DeletePluginDialog = (props) => {
|
||||
const { t, ready } = useTranslation(["WebPlugins", "Common"]);
|
||||
const { isVisible, onClose, onDelete } = props;
|
||||
|
||||
const [isRequestRunning, setIsRequestRunning] = React.useState(false);
|
||||
|
||||
const onDeleteClick = async () => {
|
||||
try {
|
||||
setIsRequestRunning(true);
|
||||
await onDelete();
|
||||
|
||||
setIsRequestRunning(true);
|
||||
onClose();
|
||||
} catch (error) {
|
||||
toastr.error(error);
|
||||
onClose();
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalDialogContainer
|
||||
isLoading={!ready}
|
||||
visible={isVisible}
|
||||
onClose={onClose}
|
||||
displayType="modal"
|
||||
>
|
||||
<ModalDialog.Header>{t("DeletePluginTitle")}</ModalDialog.Header>
|
||||
<ModalDialog.Body>{t("DeletePluginDescription")}</ModalDialog.Body>
|
||||
<ModalDialog.Footer>
|
||||
<Button
|
||||
className="delete-button"
|
||||
key="DeletePortalBtn"
|
||||
label={t("Common:OkButton")}
|
||||
size="normal"
|
||||
scale
|
||||
primary={true}
|
||||
isLoading={isRequestRunning}
|
||||
onClick={onDeleteClick}
|
||||
/>
|
||||
<Button
|
||||
className="cancel-button"
|
||||
key="CancelDeleteBtn"
|
||||
label={t("Common:CancelButton")}
|
||||
size="normal"
|
||||
scale
|
||||
isDisabled={isRequestRunning}
|
||||
onClick={onClose}
|
||||
/>
|
||||
</ModalDialog.Footer>
|
||||
</ModalDialogContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ pluginStore }) => {
|
||||
const {
|
||||
deletePluginDialogProps,
|
||||
setDeletePluginDialogVisible,
|
||||
setDeletePluginDialogProps,
|
||||
uninstallPlugin,
|
||||
} = pluginStore;
|
||||
|
||||
const onClose = () => {
|
||||
setDeletePluginDialogVisible(false);
|
||||
setDeletePluginDialogProps(null);
|
||||
};
|
||||
|
||||
const { pluginId, pluginName, pluginSystem } = deletePluginDialogProps;
|
||||
|
||||
const onDelete = async () => {
|
||||
await uninstallPlugin(pluginId, pluginName, pluginSystem);
|
||||
};
|
||||
|
||||
return { onClose, onDelete };
|
||||
})(observer(DeletePluginDialog));
|
201
packages/client/src/components/dialogs/PluginDialog/index.js
Normal file
201
packages/client/src/components/dialogs/PluginDialog/index.js
Normal file
@ -0,0 +1,201 @@
|
||||
import React from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import ModalDialog from "@docspace/components/modal-dialog";
|
||||
|
||||
import WrappedComponent from "SRC_DIR/helpers/plugins/WrappedComponent";
|
||||
import { PluginComponents } from "SRC_DIR/helpers/plugins/constants";
|
||||
import { messageActions } from "SRC_DIR/helpers/plugins/utils";
|
||||
|
||||
const PluginDialog = ({
|
||||
isVisible,
|
||||
dialogHeader,
|
||||
dialogBody,
|
||||
dialogFooter,
|
||||
onClose,
|
||||
onLoad,
|
||||
eventListeners,
|
||||
|
||||
pluginId,
|
||||
pluginName,
|
||||
pluginSystem,
|
||||
|
||||
setSettingsPluginDialogVisible,
|
||||
setCurrentSettingsDialogPlugin,
|
||||
updatePluginStatus,
|
||||
|
||||
setPluginDialogVisible,
|
||||
setPluginDialogProps,
|
||||
|
||||
updateContextMenuItems,
|
||||
updateInfoPanelItems,
|
||||
updateMainButtonItems,
|
||||
updateProfileMenuItems,
|
||||
updateEventListenerItems,
|
||||
updateFileItems,
|
||||
...rest
|
||||
}) => {
|
||||
const [dialogHeaderProps, setDialogHeaderProps] =
|
||||
React.useState(dialogHeader);
|
||||
const [dialogBodyProps, setDialogBodyProps] = React.useState(dialogBody);
|
||||
const [dialogFooterProps, setDialogFooterProps] =
|
||||
React.useState(dialogFooter);
|
||||
|
||||
const [modalRequestRunning, setModalRequestRunning] = React.useState(false);
|
||||
|
||||
const functionsRef = React.useRef([]);
|
||||
|
||||
const onCloseAction = async () => {
|
||||
if (modalRequestRunning) return;
|
||||
const message = await onClose();
|
||||
|
||||
messageActions(
|
||||
message,
|
||||
null,
|
||||
|
||||
pluginId,
|
||||
pluginName,
|
||||
pluginSystem,
|
||||
|
||||
setSettingsPluginDialogVisible,
|
||||
setCurrentSettingsDialogPlugin,
|
||||
updatePluginStatus,
|
||||
null,
|
||||
setPluginDialogVisible,
|
||||
setPluginDialogProps,
|
||||
|
||||
updateContextMenuItems,
|
||||
updateInfoPanelItems,
|
||||
updateMainButtonItems,
|
||||
updateProfileMenuItems,
|
||||
updateEventListenerItems,
|
||||
updateFileItems
|
||||
);
|
||||
};
|
||||
|
||||
React.useEffect(() => {
|
||||
if (eventListeners) {
|
||||
eventListeners.forEach((e) => {
|
||||
const onAction = async (evt) => {
|
||||
const message = await e.onAction(evt);
|
||||
|
||||
messageActions(
|
||||
message,
|
||||
null,
|
||||
|
||||
pluginId,
|
||||
pluginName,
|
||||
pluginSystem,
|
||||
|
||||
setSettingsPluginDialogVisible,
|
||||
setCurrentSettingsDialogPlugin,
|
||||
updatePluginStatus,
|
||||
null,
|
||||
setPluginDialogVisible,
|
||||
setPluginDialogProps,
|
||||
|
||||
updateContextMenuItems,
|
||||
updateInfoPanelItems,
|
||||
updateMainButtonItems,
|
||||
updateProfileMenuItems,
|
||||
updateEventListenerItems,
|
||||
updateFileItems
|
||||
);
|
||||
};
|
||||
|
||||
functionsRef.current.push(onAction);
|
||||
|
||||
window.addEventListener(e.name, onAction);
|
||||
});
|
||||
}
|
||||
|
||||
return () => {
|
||||
if (eventListeners) {
|
||||
eventListeners.forEach((e, index) => {
|
||||
window.removeEventListener(e.name, functionsRef.current[index]);
|
||||
});
|
||||
}
|
||||
};
|
||||
}, []);
|
||||
|
||||
const onLoadAction = React.useCallback(async () => {
|
||||
// if (onLoad) {
|
||||
// const res = await onLoad();
|
||||
// setDialogHeaderProps(res.newDialogHeader);
|
||||
// setDialogBodyProps(res.newDialogBody);
|
||||
// setDialogFooterProps(res.newDialogFooter);
|
||||
// }
|
||||
}, [onLoad]);
|
||||
|
||||
React.useEffect(() => {
|
||||
onLoadAction();
|
||||
}, [onLoadAction]);
|
||||
|
||||
return (
|
||||
<ModalDialog visible={isVisible} onClose={onCloseAction} {...rest}>
|
||||
<ModalDialog.Header>{dialogHeaderProps}</ModalDialog.Header>
|
||||
<ModalDialog.Body>
|
||||
<WrappedComponent
|
||||
pluginId={pluginId}
|
||||
pluginName={pluginName}
|
||||
pluginSystem={pluginSystem}
|
||||
component={{
|
||||
component: PluginComponents.box,
|
||||
props: dialogBodyProps,
|
||||
}}
|
||||
setModalRequestRunning={setModalRequestRunning}
|
||||
/>
|
||||
</ModalDialog.Body>
|
||||
{dialogFooterProps && (
|
||||
<ModalDialog.Footer>
|
||||
<WrappedComponent
|
||||
pluginId={pluginId}
|
||||
pluginName={pluginName}
|
||||
pluginSystem={pluginSystem}
|
||||
component={{
|
||||
component: PluginComponents.box,
|
||||
props: dialogFooterProps,
|
||||
}}
|
||||
setModalRequestRunning={setModalRequestRunning}
|
||||
/>
|
||||
</ModalDialog.Footer>
|
||||
)}
|
||||
</ModalDialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ pluginStore }) => {
|
||||
const {
|
||||
pluginDialogProps,
|
||||
setSettingsPluginDialogVisible,
|
||||
setCurrentSettingsDialogPlugin,
|
||||
updatePluginStatus,
|
||||
|
||||
setPluginDialogVisible,
|
||||
setPluginDialogProps,
|
||||
|
||||
updateContextMenuItems,
|
||||
updateInfoPanelItems,
|
||||
updateMainButtonItems,
|
||||
updateProfileMenuItems,
|
||||
updateEventListenerItems,
|
||||
updateFileItems,
|
||||
} = pluginStore;
|
||||
|
||||
return {
|
||||
...pluginDialogProps,
|
||||
setSettingsPluginDialogVisible,
|
||||
setCurrentSettingsDialogPlugin,
|
||||
updatePluginStatus,
|
||||
|
||||
setPluginDialogVisible,
|
||||
setPluginDialogProps,
|
||||
|
||||
updateContextMenuItems,
|
||||
updateInfoPanelItems,
|
||||
updateMainButtonItems,
|
||||
updateProfileMenuItems,
|
||||
updateEventListenerItems,
|
||||
updateFileItems,
|
||||
};
|
||||
})(observer(PluginDialog));
|
@ -0,0 +1,159 @@
|
||||
import React from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
import { useTranslation } from "react-i18next";
|
||||
|
||||
import ModalDialog from "@docspace/components/modal-dialog";
|
||||
|
||||
import { PluginComponents } from "SRC_DIR/helpers/plugins/constants";
|
||||
import WrappedComponent from "SRC_DIR/helpers/plugins/WrappedComponent";
|
||||
|
||||
import Header from "./sub-components/Header";
|
||||
import Info from "./sub-components/Info";
|
||||
import Footer from "./sub-components/Footer";
|
||||
import Button from "@docspace/components/button";
|
||||
|
||||
const SettingsPluginDialog = ({
|
||||
plugin,
|
||||
withDelete,
|
||||
onLoad,
|
||||
|
||||
settings,
|
||||
saveButton,
|
||||
|
||||
settingsPluginDialogVisible,
|
||||
|
||||
onClose,
|
||||
onDelete,
|
||||
|
||||
...rest
|
||||
}) => {
|
||||
const { t } = useTranslation(["WebPlugins", "Common", "Files", "People"]);
|
||||
|
||||
const [customSettingsProps, setCustomSettingsProps] =
|
||||
React.useState(settings);
|
||||
|
||||
const [saveButtonProps, setSaveButtonProps] = React.useState(saveButton);
|
||||
|
||||
const [modalRequestRunning, setModalRequestRunning] = React.useState(false);
|
||||
|
||||
const onLoadAction = React.useCallback(async () => {
|
||||
if (!onLoad) return;
|
||||
const res = await onLoad();
|
||||
|
||||
setCustomSettingsProps(res.settings);
|
||||
if (res.saveButton)
|
||||
setSaveButtonProps({
|
||||
...res.saveButton,
|
||||
props: { ...res.saveButton, scale: true },
|
||||
});
|
||||
}, [onLoad]);
|
||||
|
||||
React.useEffect(() => {
|
||||
onLoadAction();
|
||||
}, [onLoadAction]);
|
||||
|
||||
const onCloseAction = () => {
|
||||
if (modalRequestRunning) return;
|
||||
onClose();
|
||||
};
|
||||
|
||||
const onDeleteAction = () => {
|
||||
if (modalRequestRunning) return;
|
||||
onClose();
|
||||
|
||||
onDelete();
|
||||
};
|
||||
|
||||
return (
|
||||
<ModalDialog
|
||||
visible={settingsPluginDialogVisible}
|
||||
displayType="aside"
|
||||
onClose={onCloseAction}
|
||||
withBodyScroll
|
||||
withFooterBorder
|
||||
>
|
||||
<ModalDialog.Header>
|
||||
<Header t={t} name={plugin?.name} />
|
||||
</ModalDialog.Header>
|
||||
<ModalDialog.Body>
|
||||
<WrappedComponent
|
||||
pluginId={plugin.id}
|
||||
pluginName={plugin.name}
|
||||
pluginSystem={plugin.system}
|
||||
component={{
|
||||
component: PluginComponents.box,
|
||||
props: customSettingsProps,
|
||||
}}
|
||||
saveButton={saveButton}
|
||||
setSaveButtonProps={setSaveButtonProps}
|
||||
setModalRequestRunning={setModalRequestRunning}
|
||||
/>
|
||||
<Info t={t} plugin={plugin} withDelete={withDelete} />
|
||||
{withDelete && (
|
||||
<Button
|
||||
label={t("DeletePlugin")}
|
||||
onClick={onDeleteAction}
|
||||
scale
|
||||
size={"normal"}
|
||||
/>
|
||||
)}
|
||||
</ModalDialog.Body>
|
||||
<ModalDialog.Footer>
|
||||
<Footer
|
||||
t={t}
|
||||
id={plugin?.id}
|
||||
pluginName={plugin.name}
|
||||
pluginSystem={plugin.system}
|
||||
saveButtonProps={saveButtonProps}
|
||||
setModalRequestRunning={setModalRequestRunning}
|
||||
onCloseAction={onCloseAction}
|
||||
modalRequestRunning={modalRequestRunning}
|
||||
/>
|
||||
</ModalDialog.Footer>
|
||||
</ModalDialog>
|
||||
);
|
||||
};
|
||||
|
||||
export default inject(({ auth, pluginStore }) => {
|
||||
const {
|
||||
pluginList,
|
||||
settingsPluginDialogVisible,
|
||||
setSettingsPluginDialogVisible,
|
||||
currentSettingsDialogPlugin,
|
||||
setCurrentSettingsDialogPlugin,
|
||||
setDeletePluginDialogVisible,
|
||||
setDeletePluginDialogProps,
|
||||
} = pluginStore;
|
||||
|
||||
const { pluginOptions } = auth.settingsStore;
|
||||
|
||||
const { pluginId, pluginSystem, pluginName } = currentSettingsDialogPlugin;
|
||||
|
||||
const plugin = pluginSystem
|
||||
? pluginList.find((p) => p.name === pluginName)
|
||||
: pluginList.find((p) => p.id === pluginId);
|
||||
|
||||
const withDelete = pluginOptions.includes("delete") && !pluginSystem;
|
||||
|
||||
const pluginSettings = plugin?.getAdminPluginSettings();
|
||||
|
||||
const onClose = () => {
|
||||
setSettingsPluginDialogVisible(false);
|
||||
setCurrentSettingsDialogPlugin(null);
|
||||
};
|
||||
|
||||
const onDelete = () => {
|
||||
setDeletePluginDialogVisible(true);
|
||||
setDeletePluginDialogProps({ pluginId, pluginSystem, pluginName });
|
||||
};
|
||||
|
||||
return {
|
||||
plugin,
|
||||
withDelete,
|
||||
...pluginSettings,
|
||||
settingsPluginDialogVisible,
|
||||
|
||||
onClose,
|
||||
onDelete,
|
||||
};
|
||||
})(observer(SettingsPluginDialog));
|
@ -0,0 +1,57 @@
|
||||
import React from "react";
|
||||
import styled from "styled-components";
|
||||
|
||||
import Button from "@docspace/components/button";
|
||||
|
||||
import { PluginComponent } from "SRC_DIR/helpers/plugins/WrappedComponent";
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
width: 100%;
|
||||
|
||||
display: flex;
|
||||
align-items: space-between;
|
||||
gap: 10px;
|
||||
`;
|
||||
|
||||
const Footer = ({
|
||||
t,
|
||||
id,
|
||||
pluginName,
|
||||
pluginSystem,
|
||||
saveButtonProps,
|
||||
modalRequestRunning,
|
||||
setModalRequestRunning,
|
||||
onCloseAction,
|
||||
}) => {
|
||||
return (
|
||||
<StyledContainer>
|
||||
<PluginComponent
|
||||
component={{
|
||||
...saveButtonProps,
|
||||
props: {
|
||||
...saveButtonProps?.props,
|
||||
scale: true,
|
||||
isSaveButton: true,
|
||||
primary: true,
|
||||
size: "normal",
|
||||
label: t("Common:SaveButton"),
|
||||
modalRequestRunning,
|
||||
setSettingsModalRequestRunning: setModalRequestRunning,
|
||||
onCloseAction,
|
||||
},
|
||||
}}
|
||||
pluginId={id}
|
||||
pluginName={pluginName}
|
||||
pluginSystem={pluginSystem}
|
||||
/>
|
||||
<Button
|
||||
scale={true}
|
||||
size={"normal"}
|
||||
onClick={onCloseAction}
|
||||
label={t("Common:CancelButton")}
|
||||
/>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default Footer;
|
@ -0,0 +1,25 @@
|
||||
import styled from "styled-components";
|
||||
|
||||
import { Base } from "@docspace/components/themes";
|
||||
|
||||
const StyledHeader = styled.div`
|
||||
display: flex;
|
||||
align-items: center;
|
||||
|
||||
div {
|
||||
color: ${(props) => props.theme.plugins.pluginName};
|
||||
}
|
||||
`;
|
||||
|
||||
StyledHeader.defaultProps = { theme: Base };
|
||||
|
||||
const Header = ({ t, name }) => {
|
||||
return (
|
||||
<StyledHeader>
|
||||
{t("Common:Settings")}
|
||||
<div>({name})</div>
|
||||
</StyledHeader>
|
||||
);
|
||||
};
|
||||
|
||||
export default Header;
|
@ -0,0 +1,216 @@
|
||||
import React from "react";
|
||||
import styled, { css } from "styled-components";
|
||||
|
||||
import { getCookie } from "@docspace/common/utils";
|
||||
import { LANGUAGE } from "@docspace/common/constants";
|
||||
|
||||
import Text from "@docspace/components/text";
|
||||
import Link from "@docspace/components/link";
|
||||
import getCorrectDate from "@docspace/components/utils/getCorrectDate";
|
||||
|
||||
import { PluginStatus } from "SRC_DIR/helpers/plugins/constants";
|
||||
import { Base } from "@docspace/components/themes";
|
||||
|
||||
const StyledContainer = styled.div`
|
||||
width: 100%;
|
||||
|
||||
${(props) =>
|
||||
props.withDelete &&
|
||||
css`
|
||||
margin-bottom: 24px;
|
||||
`}
|
||||
`;
|
||||
|
||||
const StyledSeparator = styled.div`
|
||||
width: 100%;
|
||||
height: 1px;
|
||||
|
||||
margin: 24px 0;
|
||||
|
||||
background-color: ${(props) => props.theme.plugins.borderColor};
|
||||
`;
|
||||
|
||||
StyledSeparator.defaultProps = { theme: Base };
|
||||
|
||||
const StyledInfo = styled.div`
|
||||
margin-top: 24px;
|
||||
|
||||
width: 100%;
|
||||
height: auto;
|
||||
|
||||
display: grid;
|
||||
|
||||
grid-template-columns: max-content 1fr;
|
||||
|
||||
gap: 8px 24px;
|
||||
`;
|
||||
|
||||
const Info = ({ t, plugin, withDelete }) => {
|
||||
const locale = getCookie(LANGUAGE) || "en";
|
||||
const uploadDate = plugin.createOn && getCorrectDate(locale, plugin.createOn);
|
||||
|
||||
const pluginStatus =
|
||||
plugin.status === PluginStatus.active
|
||||
? t("NotNeedSettings")
|
||||
: t("NeedSettings");
|
||||
|
||||
return (
|
||||
<StyledContainer withDelete={withDelete}>
|
||||
<StyledSeparator />
|
||||
<Text fontSize={"14px"} fontWeight={600} lineHeight={"16px"} noSelect>
|
||||
{t("Metadata")}
|
||||
</Text>
|
||||
<StyledInfo>
|
||||
{plugin.author && (
|
||||
<>
|
||||
<Text
|
||||
fontSize={"13px"}
|
||||
fontWeight={400}
|
||||
lineHeight={"20px"}
|
||||
noSelect
|
||||
truncate
|
||||
>
|
||||
{t("Files:ByAuthor")}
|
||||
</Text>
|
||||
<Text
|
||||
fontSize={"13px"}
|
||||
fontWeight={600}
|
||||
lineHeight={"20px"}
|
||||
noSelect
|
||||
>
|
||||
{plugin.author}
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
|
||||
{plugin.version && (
|
||||
<>
|
||||
<Text
|
||||
fontSize={"13px"}
|
||||
fontWeight={400}
|
||||
lineHeight={"20px"}
|
||||
noSelect
|
||||
truncate
|
||||
>
|
||||
{t("Common:Version")}
|
||||
</Text>
|
||||
<Text
|
||||
fontSize={"13px"}
|
||||
fontWeight={600}
|
||||
lineHeight={"20px"}
|
||||
noSelect
|
||||
>
|
||||
{plugin.version}
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
|
||||
{!plugin.system && (
|
||||
<>
|
||||
<Text
|
||||
fontSize={"13px"}
|
||||
fontWeight={400}
|
||||
lineHeight={"20px"}
|
||||
noSelect
|
||||
truncate
|
||||
>
|
||||
{t("Common:Uploader")}
|
||||
</Text>
|
||||
<Text
|
||||
fontSize={"13px"}
|
||||
fontWeight={600}
|
||||
lineHeight={"20px"}
|
||||
noSelect
|
||||
>
|
||||
{plugin.createBy}
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
|
||||
{!plugin.system && uploadDate && (
|
||||
<>
|
||||
<Text
|
||||
fontSize={"13px"}
|
||||
fontWeight={400}
|
||||
lineHeight={"20px"}
|
||||
noSelect
|
||||
truncate
|
||||
>
|
||||
{t("Common:UploadDate")}
|
||||
</Text>
|
||||
<Text
|
||||
fontSize={"13px"}
|
||||
fontWeight={600}
|
||||
lineHeight={"20px"}
|
||||
noSelect
|
||||
>
|
||||
{uploadDate}
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
|
||||
<Text
|
||||
fontSize={"13px"}
|
||||
fontWeight={400}
|
||||
lineHeight={"20px"}
|
||||
noSelect
|
||||
truncate
|
||||
>
|
||||
{t("People:UserStatus")}
|
||||
</Text>
|
||||
<Text fontSize={"13px"} fontWeight={600} lineHeight={"20px"} noSelect>
|
||||
{pluginStatus}
|
||||
</Text>
|
||||
|
||||
{plugin.homePage && (
|
||||
<>
|
||||
<Text
|
||||
fontSize={"13px"}
|
||||
fontWeight={400}
|
||||
lineHeight={"20px"}
|
||||
noSelect
|
||||
truncate
|
||||
>
|
||||
{t("Common:Homepage")}
|
||||
</Text>
|
||||
<Link
|
||||
fontSize={"13px"}
|
||||
fontWeight={600}
|
||||
lineHeight={"20px"}
|
||||
type={"page"}
|
||||
href={plugin?.homePage}
|
||||
target={"_blank"}
|
||||
noSelect
|
||||
isHovered
|
||||
>
|
||||
{plugin.homePage}
|
||||
</Link>
|
||||
</>
|
||||
)}
|
||||
{plugin.description && (
|
||||
<>
|
||||
<Text
|
||||
fontSize={"13px"}
|
||||
fontWeight={400}
|
||||
lineHeight={"20px"}
|
||||
noSelect
|
||||
truncate
|
||||
>
|
||||
{t("Common:Description")}
|
||||
</Text>
|
||||
<Text
|
||||
fontSize={"13px"}
|
||||
fontWeight={600}
|
||||
lineHeight={"20px"}
|
||||
noSelect
|
||||
>
|
||||
{plugin.description}
|
||||
</Text>
|
||||
</>
|
||||
)}
|
||||
</StyledInfo>
|
||||
</StyledContainer>
|
||||
);
|
||||
};
|
||||
|
||||
export default Info;
|
@ -34,7 +34,10 @@ import UnsavedChangesDialog from "./UnsavedChangesDialog";
|
||||
import DeleteLinkDialog from "./DeleteLinkDialog";
|
||||
import RoomSharingDialog from "./RoomSharingDialog";
|
||||
import MoveToPublicRoom from "./MoveToPublicRoom";
|
||||
import SettingsPluginDialog from "./SettingsPluginDialog";
|
||||
import ReportDialog from "./ReportDialog";
|
||||
import PluginDialog from "./PluginDialog";
|
||||
import DeletePluginDialog from "./DeletePluginDialog";
|
||||
|
||||
export {
|
||||
EmptyTrashDialog,
|
||||
@ -73,5 +76,8 @@ export {
|
||||
DeleteLinkDialog,
|
||||
RoomSharingDialog,
|
||||
MoveToPublicRoom,
|
||||
SettingsPluginDialog,
|
||||
ReportDialog,
|
||||
PluginDialog,
|
||||
DeletePluginDialog,
|
||||
};
|
||||
|
@ -1,116 +0,0 @@
|
||||
import { PluginType } from "./constants";
|
||||
|
||||
class PluginStore {
|
||||
plugins = null;
|
||||
contextMenuItems = null;
|
||||
mainButtonItems = null;
|
||||
profileMenuItems = null;
|
||||
|
||||
constructor() {
|
||||
this.plugins = new Map();
|
||||
this.contextMenuItems = new Map();
|
||||
this.mainButtonItems = new Map();
|
||||
this.profileMenuItems = new Map();
|
||||
}
|
||||
|
||||
installPlugin(id, plugin) {
|
||||
this.plugins.set(+id, plugin);
|
||||
|
||||
if (plugin.isActive) {
|
||||
this.activatePlugin(+id);
|
||||
}
|
||||
}
|
||||
|
||||
uninstallPlugin(id) {
|
||||
this.deactivatePlugin(id);
|
||||
this.plugins.delete(+id);
|
||||
}
|
||||
|
||||
getInstalledPlugins() {
|
||||
return this.plugins;
|
||||
}
|
||||
|
||||
activatePlugin(id) {
|
||||
this.plugins.get(+id).isActive = true;
|
||||
const pluginItems = this.plugins.get(+id).activate();
|
||||
|
||||
if (!pluginItems) return;
|
||||
|
||||
Array.from(pluginItems, ([key, value]) => {
|
||||
switch (key) {
|
||||
case PluginType.CONTEXT_MENU:
|
||||
Array.from(value, ([key, item]) =>
|
||||
this.addContextMenuItem(key, item)
|
||||
);
|
||||
return;
|
||||
case PluginType.ACTION_BUTTON:
|
||||
Array.from(value, ([key, item]) => this.addMainButtonItem(key, item));
|
||||
return;
|
||||
case PluginType.PROFILE_MENU:
|
||||
Array.from(value, ([key, item]) =>
|
||||
this.addProfileMenuItem(key, item)
|
||||
);
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
deactivatePlugin(id) {
|
||||
this.plugins.get(+id).isActive = false;
|
||||
const pluginItems = this.plugins.get(+id).deactivate();
|
||||
|
||||
if (!pluginItems) return;
|
||||
|
||||
Array.from(pluginItems, ([key, value]) => {
|
||||
switch (key) {
|
||||
case PluginType.CONTEXT_MENU:
|
||||
value.map((key) => this.deleteContextMenuItem(key));
|
||||
return;
|
||||
case PluginType.ACTION_BUTTON:
|
||||
value.map((key) => this.deleteMainButtonItem(key));
|
||||
return;
|
||||
case PluginType.PROFILE_MENU:
|
||||
value.map((key) => this.deleteProfileMenuItem(key));
|
||||
return;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
addContextMenuItem(key, contextMenuItem) {
|
||||
this.contextMenuItems.set(key, contextMenuItem);
|
||||
}
|
||||
|
||||
deleteContextMenuItem(key) {
|
||||
this.contextMenuItems.delete(key);
|
||||
}
|
||||
|
||||
getContextMenuItems() {
|
||||
return this.contextMenuItems;
|
||||
}
|
||||
|
||||
addMainButtonItem(key, mainButtonItem) {
|
||||
this.mainButtonItems.set(key, mainButtonItem);
|
||||
}
|
||||
|
||||
deleteMainButtonItem(key) {
|
||||
this.mainButtonItems.delete(key);
|
||||
}
|
||||
|
||||
getMainButtonItems() {
|
||||
return this.mainButtonItems;
|
||||
}
|
||||
|
||||
addProfileMenuItem(key, profileMenuItem) {
|
||||
this.profileMenuItems.set(key, profileMenuItem);
|
||||
}
|
||||
|
||||
deleteProfileMenuItem(key) {
|
||||
this.profileMenuItems.delete(key);
|
||||
}
|
||||
|
||||
getProfileMenuItems() {
|
||||
return this.profileMenuItems;
|
||||
}
|
||||
}
|
||||
|
||||
export default PluginStore;
|
426
packages/client/src/helpers/plugins/WrappedComponent.js
Normal file
426
packages/client/src/helpers/plugins/WrappedComponent.js
Normal file
@ -0,0 +1,426 @@
|
||||
import React from "react";
|
||||
import { inject, observer } from "mobx-react";
|
||||
|
||||
import RectangleLoader from "@docspace/common/components/Loaders/RectangleLoader";
|
||||
|
||||
import Box from "@docspace/components/box";
|
||||
import Text from "@docspace/components/text";
|
||||
import Checkbox from "@docspace/components/checkbox";
|
||||
import TextArea from "@docspace/components/textarea";
|
||||
import TextInput from "@docspace/components/text-input";
|
||||
import Label from "@docspace/components/label";
|
||||
import Button from "@docspace/components/button";
|
||||
import ToggleButton from "@docspace/components/toggle-button";
|
||||
import ComboBox from "@docspace/components/combobox";
|
||||
|
||||
import { PluginComponents } from "./constants";
|
||||
|
||||
import { messageActions } from "./utils";
|
||||
|
||||
const PropsContext = React.createContext({});
|
||||
|
||||
const ComponentPure = ({
|
||||
component,
|
||||
pluginId,
|
||||
pluginName,
|
||||
pluginSystem,
|
||||
setSettingsPluginDialogVisible,
|
||||
setCurrentSettingsDialogPlugin,
|
||||
updatePluginStatus,
|
||||
setPluginDialogVisible,
|
||||
setPluginDialogProps,
|
||||
|
||||
updateContextMenuItems,
|
||||
updateInfoPanelItems,
|
||||
updateMainButtonItems,
|
||||
updateProfileMenuItems,
|
||||
updateEventListenerItems,
|
||||
updateFileItems,
|
||||
}) => {
|
||||
const [elementProps, setElementProps] = React.useState(component.props);
|
||||
|
||||
const {
|
||||
contextProps,
|
||||
updatePropsContext,
|
||||
isRequestRunning,
|
||||
setIsRequestRunning,
|
||||
setModalRequestRunning,
|
||||
} = React.useContext(PropsContext);
|
||||
|
||||
React.useEffect(() => {
|
||||
if (
|
||||
!component.contextName ||
|
||||
(contextProps && !contextProps[component.contextName])
|
||||
)
|
||||
return;
|
||||
|
||||
contextProps && setElementProps(contextProps[component.contextName]);
|
||||
}, [contextProps && contextProps[component.contextName]]);
|
||||
|
||||
React.useEffect(() => {
|
||||
setElementProps(component.props);
|
||||
}, [component.props]);
|
||||
|
||||
const getElement = () => {
|
||||
const componentName = component.component;
|
||||
|
||||
switch (componentName) {
|
||||
case PluginComponents.box: {
|
||||
const childrenComponents = elementProps?.children?.map(
|
||||
(item, index) => (
|
||||
<PluginComponent
|
||||
key={`box-${index}-${item.component}`}
|
||||
component={item}
|
||||
pluginId={pluginId}
|
||||
pluginName={pluginName}
|
||||
pluginSystem={pluginSystem}
|
||||
/>
|
||||
)
|
||||
);
|
||||
|
||||
return <Box {...elementProps}>{childrenComponents}</Box>;
|
||||
}
|
||||
|
||||
case PluginComponents.text: {
|
||||
return <Text {...elementProps}>{elementProps.text}</Text>;
|
||||
}
|
||||
|
||||
case PluginComponents.label: {
|
||||
return <Label {...elementProps} />;
|
||||
}
|
||||
|
||||
case PluginComponents.checkbox: {
|
||||
const onChangeAction = () => {
|
||||
const message = elementProps.onChange();
|
||||
|
||||
messageActions(
|
||||
message,
|
||||
setElementProps,
|
||||
|
||||
pluginId,
|
||||
pluginName,
|
||||
pluginSystem,
|
||||
|
||||
setSettingsPluginDialogVisible,
|
||||
setCurrentSettingsDialogPlugin,
|
||||
updatePluginStatus,
|
||||
updatePropsContext,
|
||||
setPluginDialogVisible,
|
||||
setPluginDialogProps,
|
||||
|
||||
updateContextMenuItems,
|
||||
updateInfoPanelItems,
|
||||
updateMainButtonItems,
|
||||
updateProfileMenuItems,
|
||||
updateEventListenerItems,
|
||||
updateFileItems
|
||||
);
|
||||
};
|
||||
|
||||
return <Checkbox {...elementProps} onChange={onChangeAction} />;
|
||||
}
|
||||
|
||||
case PluginComponents.toggleButton: {
|
||||
const onChangeAction = () => {
|
||||
const message = elementProps.onChange();
|
||||
|
||||
messageActions(
|
||||
message,
|
||||
setElementProps,
|
||||
|
||||
pluginId,
|
||||
pluginName,
|
||||
pluginSystem,
|
||||
|
||||
setSettingsPluginDialogVisible,
|
||||
setCurrentSettingsDialogPlugin,
|
||||
updatePluginStatus,
|
||||
updatePropsContext,
|
||||
setPluginDialogVisible,
|
||||
setPluginDialogProps,
|
||||
|
||||
updateContextMenuItems,
|
||||
updateInfoPanelItems,
|
||||
updateMainButtonItems,
|
||||
updateProfileMenuItems,
|
||||
updateEventListenerItems,
|
||||
updateFileItems
|
||||
);
|
||||
};
|
||||
|
||||
return <ToggleButton {...elementProps} onChange={onChangeAction} />;
|
||||
}
|
||||
|
||||
case PluginComponents.textArea: {
|
||||
const onChangeAction = (e) => {
|
||||
const message = elementProps.onChange(e.target.value);
|
||||
|
||||
messageActions(
|
||||
message,
|
||||
setElementProps,
|
||||
|
||||
pluginId,
|
||||
pluginName,
|
||||
pluginSystem,
|
||||
|
||||
setSettingsPluginDialogVisible,
|
||||
setCurrentSettingsDialogPlugin,
|
||||
updatePluginStatus,
|
||||
updatePropsContext,
|
||||
setPluginDialogVisible,
|
||||
setPluginDialogProps,
|
||||
|
||||
updateContextMenuItems,
|
||||
updateInfoPanelItems,
|
||||
updateMainButtonItems,
|
||||
updateProfileMenuItems,
|
||||
updateEventListenerItems,
|
||||
updateFileItems
|
||||
);
|
||||
};
|
||||
|
||||
return <TextArea {...elementProps} onChange={onChangeAction} />;
|
||||
}
|
||||
|
||||
case PluginComponents.input: {
|
||||
const onChangeAction = (e) => {
|
||||
const message = elementProps.onChange(e.target.value);
|
||||
|
||||
messageActions(
|
||||
message,
|
||||
setElementProps,
|
||||
|
||||
pluginId,
|
||||
pluginName,
|
||||
pluginSystem,
|
||||
|
||||
setSettingsPluginDialogVisible,
|
||||
setCurrentSettingsDialogPlugin,
|
||||
updatePluginStatus,
|
||||
updatePropsContext,
|
||||
setPluginDialogVisible,
|
||||
setPluginDialogProps,
|
||||
|
||||
updateContextMenuItems,
|
||||
updateInfoPanelItems,
|
||||
updateMainButtonItems,
|
||||
updateProfileMenuItems,
|
||||
updateEventListenerItems,
|
||||
updateFileItems
|
||||
);
|
||||
};
|
||||
|
||||
return <TextInput {...elementProps} onChange={onChangeAction} />;
|
||||
}
|
||||
|
||||
case PluginComponents.button: {
|
||||
const {
|
||||
withLoadingAfterClick,
|
||||
disableWhileRequestRunning,
|
||||
isSaveButton,
|
||||
modalRequestRunning,
|
||||
setSettingsModalRequestRunning,
|
||||
onCloseAction,
|
||||
...rest
|
||||
} = elementProps;
|
||||
|
||||
const onClickAction = async () => {
|
||||
if (withLoadingAfterClick) {
|
||||
setIsRequestRunning && setIsRequestRunning(true);
|
||||
setModalRequestRunning && setModalRequestRunning(true);
|
||||
if (isSaveButton) {
|
||||
setSettingsModalRequestRunning &&
|
||||
setSettingsModalRequestRunning(true);
|
||||
}
|
||||
}
|
||||
|
||||
const message = await elementProps.onClick();
|
||||
|
||||
messageActions(
|
||||
message,
|
||||
setElementProps,
|
||||
|
||||
pluginId,
|
||||
pluginName,
|
||||
pluginSystem,
|
||||
|
||||
setSettingsPluginDialogVisible,
|
||||
setCurrentSettingsDialogPlugin,
|
||||
updatePluginStatus,
|
||||
updatePropsContext,
|
||||
setPluginDialogVisible,
|
||||
setPluginDialogProps,
|
||||
|
||||
updateContextMenuItems,
|
||||
updateInfoPanelItems,
|
||||
updateMainButtonItems,
|
||||
updateProfileMenuItems,
|
||||
updateEventListenerItems,
|
||||
updateFileItems
|
||||
);
|
||||
|
||||
setIsRequestRunning && setIsRequestRunning(false);
|
||||
|
||||
if (isSaveButton) {
|
||||
setSettingsModalRequestRunning &&
|
||||
setSettingsModalRequestRunning(false);
|
||||
onCloseAction && onCloseAction();
|
||||
}
|
||||
};
|
||||
|
||||
const isLoading = withLoadingAfterClick
|
||||
? isSaveButton
|
||||
? modalRequestRunning
|
||||
: isRequestRunning
|
||||
? isRequestRunning
|
||||
: rest.isLoading
|
||||
: rest.isLoading;
|
||||
const isDisabled = disableWhileRequestRunning
|
||||
? isSaveButton
|
||||
? modalRequestRunning
|
||||
: isRequestRunning
|
||||
? isRequestRunning
|
||||
: rest.isDisabled
|
||||
: rest.isDisabled;
|
||||
|
||||
return (
|
||||
<Button
|
||||
{...rest}
|
||||
isLoading={isLoading}
|
||||
isDisabled={isDisabled}
|
||||
onClick={onClickAction}
|
||||
/>
|
||||
);
|
||||
}
|
||||
|
||||
case PluginComponents.comboBox: {
|
||||
const onSelectAction = (option) => {
|
||||
const message = elementProps.onSelect(option);
|
||||
|
||||
messageActions(
|
||||
message,
|
||||
setElementProps,
|
||||
|
||||
pluginId,
|
||||
pluginName,
|
||||
pluginSystem,
|
||||
|
||||
setSettingsPluginDialogVisible,
|
||||
setCurrentSettingsDialogPlugin,
|
||||
updatePluginStatus,
|
||||
updatePropsContext,
|
||||
setPluginDialogVisible,
|
||||
setPluginDialogProps,
|
||||
|
||||
updateContextMenuItems,
|
||||
updateInfoPanelItems,
|
||||
updateMainButtonItems,
|
||||
updateProfileMenuItems,
|
||||
updateEventListenerItems,
|
||||
updateFileItems
|
||||
);
|
||||
};
|
||||
|
||||
return <ComboBox {...elementProps} onSelect={onSelectAction} />;
|
||||
}
|
||||
|
||||
case PluginComponents.iFrame: {
|
||||
return (
|
||||
<iframe {...elementProps} style={{ minHeight: "100%" }}></iframe>
|
||||
);
|
||||
}
|
||||
|
||||
case PluginComponents.img: {
|
||||
return <img {...elementProps}></img>;
|
||||
}
|
||||
|
||||
case PluginComponents.skeleton: {
|
||||
return <RectangleLoader {...elementProps} />;
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
const element = getElement();
|
||||
|
||||
return element;
|
||||
};
|
||||
|
||||
export const PluginComponent = inject(({ pluginStore }) => {
|
||||
const {
|
||||
updatePluginStatus,
|
||||
setCurrentSettingsDialogPlugin,
|
||||
setSettingsPluginDialogVisible,
|
||||
setPluginDialogVisible,
|
||||
setPluginDialogProps,
|
||||
|
||||
updateContextMenuItems,
|
||||
updateInfoPanelItems,
|
||||
updateMainButtonItems,
|
||||
updateProfileMenuItems,
|
||||
updateEventListenerItems,
|
||||
updateFileItems,
|
||||
} = pluginStore;
|
||||
return {
|
||||
updatePluginStatus,
|
||||
setCurrentSettingsDialogPlugin,
|
||||
setSettingsPluginDialogVisible,
|
||||
setPluginDialogVisible,
|
||||
setPluginDialogProps,
|
||||
|
||||
updateContextMenuItems,
|
||||
updateInfoPanelItems,
|
||||
updateMainButtonItems,
|
||||
updateProfileMenuItems,
|
||||
updateEventListenerItems,
|
||||
updateFileItems,
|
||||
};
|
||||
})(observer(ComponentPure));
|
||||
|
||||
const WrappedComponent = ({
|
||||
pluginId,
|
||||
pluginName,
|
||||
pluginSystem,
|
||||
|
||||
component,
|
||||
|
||||
saveButton,
|
||||
setSaveButtonProps,
|
||||
|
||||
setModalRequestRunning,
|
||||
}) => {
|
||||
const [contextProps, setContextProps] = React.useState({});
|
||||
|
||||
const [isRequestRunning, setIsRequestRunning] = React.useState(false);
|
||||
|
||||
const updatePropsContext = (name, props) => {
|
||||
if (saveButton && name === saveButton.contextName) {
|
||||
setSaveButtonProps && setSaveButtonProps((val) => ({ ...val, props }));
|
||||
} else {
|
||||
const newProps = { ...contextProps };
|
||||
newProps[name] = props;
|
||||
|
||||
setContextProps(newProps);
|
||||
}
|
||||
};
|
||||
|
||||
return (
|
||||
<PropsContext.Provider
|
||||
value={{
|
||||
contextProps,
|
||||
updatePropsContext,
|
||||
isRequestRunning,
|
||||
setIsRequestRunning,
|
||||
setModalRequestRunning,
|
||||
}}
|
||||
>
|
||||
<PluginComponent
|
||||
component={component}
|
||||
pluginId={pluginId}
|
||||
pluginName={pluginName}
|
||||
pluginSystem={pluginSystem}
|
||||
/>
|
||||
</PropsContext.Provider>
|
||||
);
|
||||
};
|
||||
|
||||
export default WrappedComponent;
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
Reference in New Issue
Block a user