Merge branch 'release/v1.0.0' of https://github.com/ONLYOFFICE/DocSpace into release/v1.0.0

This commit is contained in:
Maria Sukhova 2023-04-12 14:21:00 +03:00
commit b21a47829e
21 changed files with 249 additions and 53 deletions

40
.github/workflows/build-ffvideo.yml vendored Normal file
View File

@ -0,0 +1,40 @@
name: ffvideo build
on:
push:
branches:
- release/v1.0.0
paths:
- 'build/install/docker/**.ffvideo'
jobs:
build:
runs-on: ubuntu-latest
strategy:
matrix:
platform: [linux/amd64]
steps:
- name: Checkout
uses: actions/checkout@v3
- name: Set up QEMU
uses: docker/setup-qemu-action@v2
- name: Set up Docker Buildx
id: buildx
uses: docker/setup-buildx-action@v2
- name: Login to DockerHub
uses: docker/login-action@v2
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}
- name: Build and push
uses: docker/build-push-action@v4
with:
context: .
file: ./build/install/docker/Dockerfile.ffvideo
push: true
tags: onlyoffice/ffvideo:6.0

View File

@ -227,14 +227,12 @@ CMD ["ASC.Files.dll", "ASC.Files"]
## ASC.Files.Service ##
FROM dotnetrun AS files_services
RUN apt-get -y update && \
apt-get install -yq ffmpeg &&\
rm -rf /var/lib/apt/lists/*
ENV LD_LIBRARY_PATH=/usr/local/lib:/usr/local/lib64
WORKDIR ${BUILD_PATH}/products/ASC.Files/service/
COPY --chown=onlyoffice:onlyoffice docker-entrypoint.py ./docker-entrypoint.py
COPY --from=base --chown=onlyoffice:onlyoffice ${BUILD_PATH}/services/ASC.Files.Service/service/ .
COPY --from=onlyoffice/ffvideo:6.0 --chown=onlyoffice:onlyoffice /usr/local /usr/local/
CMD ["ASC.Files.Service.dll", "ASC.Files.Service", "core:eventBus:subscriptionClientName=asc_event_bus_files_service_queue"]

View File

@ -223,15 +223,14 @@ CMD ["ASC.Files.dll", "ASC.Files"]
## ASC.Files.Service ##
FROM dotnetrun AS files_services
RUN apt-get -y update && \
apt-get install -yq ffmpeg
ENV LD_LIBRARY_PATH=/usr/local/lib:/usr/local/lib64
WORKDIR ${BUILD_PATH}/products/ASC.Files/service/
COPY --chown=onlyoffice:onlyoffice docker-entrypoint.py ./docker-entrypoint.py
COPY --from=base --chown=onlyoffice:onlyoffice ${BUILD_PATH}/services/ASC.Files.Service/service/ .
COPY --from=onlyoffice/ffvideo:6.0 --chown=onlyoffice:onlyoffice /usr/local /usr/local/
CMD ["ASC.Files.Service.dll", "ASC.Files.Service"]
CMD ["ASC.Files.Service.dll", "ASC.Files.Service", "core:eventBus:subscriptionClientName=asc_event_bus_files_service_queue"]
## ASC.Notify ##
# FROM dotnetrun AS notify

View File

@ -0,0 +1,107 @@
FROM ubuntu:20.04 AS base
ENV DEBIAN_FRONTEND="noninteractive"
ENV TZ="Etc/UTC"
WORKDIR /tmp/workdir
RUN apt-get -yqq update && \
apt-get install -yq --no-install-recommends ca-certificates expat libgomp1 && \
apt-get autoremove -y && \
apt-get clean -y
FROM base as build
ENV FFMPEG_VERSION=6.0 \
X264_VERSION=20170226-2245-stable \
X265_VERSION=3.4 \
VPX_VERSION=1.8.0 \
SRC=/usr/local
ARG LD_LIBRARY_PATH=/opt/ffmpeg/lib
ARG MAKEFLAGS="-j2"
ARG PKG_CONFIG_PATH="/opt/ffmpeg/share/pkgconfig:/opt/ffmpeg/lib/pkgconfig:/opt/ffmpeg/lib64/pkgconfig"
ARG PREFIX=/opt/ffmpeg
ARG LD_LIBRARY_PATH="/opt/ffmpeg/lib:/opt/ffmpeg/lib64"
RUN buildDeps="autoconf \
automake \
cmake \
curl \
bzip2 \
libexpat1-dev \
g++ \
gcc \
git \
gperf \
libtool \
make \
meson \
nasm \
perl \
pkg-config \
python \
libssl-dev \
yasm \
zlib1g-dev" && \
apt-get -yqq update && \
apt-get install -yq --no-install-recommends ${buildDeps}
### libvpx
RUN \
DIR=/tmp/vpx && \
mkdir -p ${DIR} && \
cd ${DIR} && \
curl -sL https://codeload.github.com/webmproject/libvpx/tar.gz/v${VPX_VERSION} | \
tar -zx --strip-components=1 && \
./configure --prefix="${PREFIX}" --enable-vp8 --enable-vp9 --enable-vp9-highbitdepth --enable-pic --enable-shared \
--disable-debug --disable-examples --disable-docs --disable-install-bins && \
make && \
make install && \
rm -rf ${DIR}
## Download ffmpeg
RUN \
DIR=/tmp/ffmpeg && mkdir -p ${DIR} && cd ${DIR} && \
curl -sLO https://ffmpeg.org/releases/ffmpeg-${FFMPEG_VERSION}.tar.bz2 && \
tar -jx --strip-components=1 -f ffmpeg-${FFMPEG_VERSION}.tar.bz2 && \
./configure --disable-debug --disable-doc --disable-ffplay --enable-shared --enable-gpl --extra-libs=-ldl && \
make ; make install
## Build ffmpeg
RUN \
DIR=/tmp/ffmpeg && cd ${DIR} && \
./configure \
--disable-debug \
--disable-doc \
--disable-ffplay \
--enable-gpl \
--enable-libvpx \
--extra-cflags="-I${PREFIX}/include" \
--extra-ldflags="-L${PREFIX}/lib" \
--extra-libs=-ldl \
--extra-libs=-lpthread \
--prefix="${PREFIX}" && \
make clean && \
make && \
make install && \
make distclean && \
hash -r && \
cd tools && \
make qt-faststart && cp qt-faststart ${PREFIX}/bin/
## cleanup
RUN \
ldd ${PREFIX}/bin/ffmpeg | grep opt/ffmpeg | cut -d ' ' -f 3 | xargs -i cp {} /usr/local/lib/ && \
for lib in /usr/local/lib/*.so.*; do ln -s "${lib##*/}" "${lib%%.so.*}".so; done && \
cp ${PREFIX}/bin/* /usr/local/bin/ && \
cp -r ${PREFIX}/share/ffmpeg /usr/local/share/ && \
LD_LIBRARY_PATH=/usr/local/lib ffmpeg -buildconf && \
cp -r ${PREFIX}/include/libav* ${PREFIX}/include/libpostproc ${PREFIX}/include/libsw* /usr/local/include && \
mkdir -p /usr/local/lib/pkgconfig && \
for pc in ${PREFIX}/lib/pkgconfig/libav*.pc ${PREFIX}/lib/pkgconfig/libpostproc.pc ${PREFIX}/lib/pkgconfig/libsw*.pc; do \
sed "s:${PREFIX}:/usr/local:g" <"$pc" >/usr/local/lib/pkgconfig/"${pc##*/}"; \
done
FROM base AS release
ENV LD_LIBRARY_PATH=/usr/local/lib:/usr/local/lib64
COPY --from=build /usr/local /usr/local/
ENTRYPOINT ["bash"]

View File

@ -40,7 +40,7 @@ public class WarmupServicesStartupTask : IStartupTask
}
public Task ExecuteAsync(CancellationToken cancellationToken)
{
{
var processedFailed = 0;
var processedSuccessed = 0;
var startTime = DateTime.UtcNow;
@ -52,13 +52,13 @@ public class WarmupServicesStartupTask : IStartupTask
logger.TraceWarmupStarted();
tenantManager.SetCurrentTenant("localhost");
tenantManager.SetCurrentTenant("localhost");
foreach (var service in GetServices(_services))
{
try
{
scope.ServiceProvider.GetServices(service);
{
scope.ServiceProvider.GetService(service);
processedSuccessed++;
}
@ -72,9 +72,9 @@ public class WarmupServicesStartupTask : IStartupTask
var processed = processedSuccessed + processedFailed;
logger.TraceWarmupFinished(processed,
processedSuccessed,
processedFailed,
logger.TraceWarmupFinished(processed,
processedSuccessed,
processedFailed,
(DateTime.UtcNow - startTime).TotalMilliseconds);
}
@ -86,7 +86,7 @@ public class WarmupServicesStartupTask : IStartupTask
return services
.Where(descriptor => descriptor.ImplementationType != typeof(WarmupServicesStartupTask))
.Where(descriptor => descriptor.ServiceType.ContainsGenericParameters == false)
.Select(descriptor => descriptor.ServiceType)
.Select(descriptor => descriptor.ServiceType)
.Distinct();
}
}

View File

@ -146,7 +146,7 @@ public static class ServiceCollectionExtension
var logger = sp.GetRequiredService<ILogger<DefaultActiveMQPersistentConnection>>();
var factory = new Apache.NMS.NMSConnectionFactory(activeMQConfiguration.Uri);
var retryCount = 5;
if (!string.IsNullOrEmpty(cfg["core:eventBus:connectRetryCount"]))
@ -195,10 +195,8 @@ public static class ServiceCollectionExtension
/// Add a IHostedService for given type.
/// Only one copy of this instance type will active in multi process architecture.
/// </remarks>
public static void AddActivePassiveHostedService<T>(this IServiceCollection services) where T : class, IHostedService
public static void AddActivePassiveHostedService<T>(this IServiceCollection services, DIHelper diHelper) where T : class, IHostedService
{
var diHelper = new DIHelper(services);
diHelper.TryAdd<IRegisterInstanceDao<T>, RegisterInstanceDao<T>>();
diHelper.TryAdd<IRegisterInstanceManager<T>, RegisterInstanceManager<T>>();

View File

@ -61,6 +61,7 @@ public class LocalesTest
public List<JavaScriptFile> JavaScriptFiles { get; set; }
public List<ModuleFolder> ModuleFolders { get; set; }
public List<KeyValuePair<string, string>> NotTranslatedToasts { get; set; }
public List<KeyValuePair<string, string>> NotTranslatedProps { get; set; }
public List<LanguageItem> CommonTranslations { get; set; }
public List<ParseJsonError> ParseJsonErrors { get; set; }
public static string ConvertPathToOS { get; private set; }
@ -201,7 +202,10 @@ public class LocalesTest
"|(?<=toastr.success\\([\"`\'])(.*)(?=[\"\'`])" +
"|(?<=toastr.warn\\([\"`\'])(.*)(?=[\"\'`])", RegexOptions.Multiline | RegexOptions.ECMAScript);
var notTranslatedPropsRegex = new Regex("<[\\w\\n][^>]* (title|placeholder|label|text)={?[\\\"\\'](.*)[\\\"\\']}?", RegexOptions.Multiline | RegexOptions.ECMAScript);
NotTranslatedToasts = new List<KeyValuePair<string, string>>();
NotTranslatedProps = new List<KeyValuePair<string, string>>();
foreach (var path in javascriptFiles)
{
@ -219,6 +223,18 @@ public class LocalesTest
}
}
var propsMatches = notTranslatedPropsRegex.Matches(jsFileText).ToList();
if (propsMatches.Any())
{
foreach (var propsMatch in propsMatches)
{
var found = propsMatch.Value;
if (!string.IsNullOrEmpty(found) && !NotTranslatedProps.Exists(t => t.Value == found))
NotTranslatedProps.Add(new KeyValuePair<string, string>(path, found));
}
}
var matches = regexp.Matches(jsFileText);
var translationKeys = matches
@ -1079,6 +1095,29 @@ public class LocalesTest
Assert.AreEqual(0, NotTranslatedToasts.Count, message);
}
[Test]
[Category("Locales")]
public void NotTranslatedPropsTest()
{
var message = $"Next text not translated props (title, placeholder, label, text):\r\n\r\n";
var i = 0;
NotTranslatedProps.GroupBy(t => t.Key)
.Select(g => new
{
FilePath = g.Key,
Values = g.ToList()
})
.ToList()
.ForEach(t =>
{
message += $"{++i}. Path='{t.FilePath}'\r\n\r\n{string.Join("\r\n", t.Values.Select(v => v.Value))}\r\n\r\n";
});
Assert.AreEqual(0, NotTranslatedProps.Count, message);
}
[Test]
[Category("Locales")]
public void WrongTranslationVariablesTest()

View File

@ -102,15 +102,15 @@ public class PortalController : ControllerBase
[HttpPost("register")]
[AllowCrossSiteJson]
[Authorize(AuthenticationSchemes = "auth:allowskip:registerportal")]
public Task<IActionResult> RegisterAsync(TenantModel model)
public async Task<IActionResult> RegisterAsync(TenantModel model)
{
if (model == null)
{
return Task.FromResult<IActionResult>(BadRequest(new
return BadRequest(new
{
error = "portalNameEmpty",
message = "PortalName is required"
}));
});
}
if (!ModelState.IsValid)
@ -122,11 +122,11 @@ public class PortalController : ControllerBase
message.Add(ModelState[k].Errors.FirstOrDefault().ErrorMessage);
}
return Task.FromResult<IActionResult>(BadRequest(new
return BadRequest(new
{
error = "params",
message
}));
});
}
var sw = Stopwatch.StartNew();
@ -136,7 +136,7 @@ public class PortalController : ControllerBase
if (!CheckPasswordPolicy(model.Password, out var error1))
{
sw.Stop();
return Task.FromResult<IActionResult>(BadRequest(error1));
return BadRequest(error1);
}
if (!string.IsNullOrEmpty(model.Password))
@ -152,16 +152,11 @@ public class PortalController : ControllerBase
{
sw.Stop();
return Task.FromResult<IActionResult>(BadRequest(error));
return BadRequest(error);
}
return InternalRegisterAsync(model, error, sw);
}
private async Task<IActionResult> InternalRegisterAsync(TenantModel model, object error, Stopwatch sw)
{
model.PortalName = (model.PortalName ?? "").Trim();
var (exists, _) = await CheckExistingNamePortalAsync(model.PortalName);
(var exists, error) = await CheckExistingNamePortalAsync(model.PortalName);
if (!exists)
{

View File

@ -66,8 +66,8 @@ public class Startup : BaseStartup
services.AddHostedService<BackupCleanerTempFileService>();
services.AddHostedService<BackupWorkerService>();
services.AddActivePassiveHostedService<BackupCleanerService>();
services.AddActivePassiveHostedService<BackupSchedulerService>();
services.AddActivePassiveHostedService<BackupCleanerService>(DIHelper);
services.AddActivePassiveHostedService<BackupSchedulerService>(DIHelper);
services.AddBaseDbContextPool<BackupsContext>();
services.AddBaseDbContextPool<FilesDbContext>();

View File

@ -53,8 +53,8 @@ public class Startup : BaseWorkerStartup
DIHelper.TryAdd<NotifyInvokeSendMethodRequestedIntegrationEventHandler>();
DIHelper.TryAdd<NotifySendMessageRequestedIntegrationEventHandler>();
services.AddActivePassiveHostedService<NotifySenderService>();
services.AddActivePassiveHostedService<NotifyCleanerService>();
services.AddActivePassiveHostedService<NotifySenderService>(DIHelper);
services.AddActivePassiveHostedService<NotifyCleanerService>(DIHelper);
services.AddBaseDbContextPool<NotifyDbContext>();
}

View File

@ -37,7 +37,6 @@ const FilesListRow = ({
fontSize="13px"
fontWeight="400"
name={`${index}`}
label=""
isChecked={isChecked}
onClick={onFileClick}
value=""

View File

@ -9,15 +9,17 @@ const SizeCell = ({ t, item, sideColor }) => {
filesCount,
foldersCount,
} = item;
const date = fileExst || contentLength ? contentLength : "—";
return (
<StyledText
color={sideColor}
fontSize="12px"
fontWeight={600}
title=""
title={date}
truncate
>
{fileExst || contentLength ? contentLength : "—"}
{date}
</StyledText>
);
};

View File

@ -49,10 +49,17 @@ const TypeCell = ({ t, item, sideColor }) => {
const type = item.isRoom ? getRoomType() : getItemType();
const Exst = fileExst ? fileExst.slice(1).toUpperCase() : "";
const data = `${type} ${Exst}`;
return (
<StyledText fontSize="12px" fontWeight="600" color={sideColor} truncate>
{type} {Exst}
<StyledText
fontSize="12px"
fontWeight="600"
color={sideColor}
truncate
title={data}
>
{data}
</StyledText>
);
};

View File

@ -48,7 +48,6 @@ const BackupListBody = ({
fontSize="13px"
fontWeight="400"
value=""
label=""
isChecked={isChecked}
onClick={onSelectFile}
name={`${index}_${fileId}`}

View File

@ -1,5 +1,6 @@
import styled, { css } from "styled-components";
import Base from "../themes/base";
import { isMobileOnly } from "react-device-detect";
const StyledDropdown = styled.div`
@media (orientation: landscape) {
@ -58,6 +59,14 @@ const StyledDropdown = styled.div`
box-shadow: ${(props) => props.theme.dropDown.boxShadow};
-moz-box-shadow: ${(props) => props.theme.dropDown.boxShadow};
-webkit-box-shadow: ${(props) => props.theme.dropDown.boxShadow};
${(props) =>
(props.isMobileView || isMobileOnly) &&
css`
box-shadow: ${(props) => props.theme.dropDown.boxShadowMobile};
-moz-box-shadow: ${(props) => props.theme.dropDown.boxShadowMobile};
-webkit-box-shadow: ${(props) => props.theme.dropDown.boxShadowMobile};
`}
padding: ${(props) => !props.maxHeight && props.itemCount > 1 && `4px 0px`};
${(props) =>
props.columnCount &&

View File

@ -162,7 +162,7 @@ class GroupButton extends React.Component {
{label}
</StyledDropdownToggle>
)}
{isSeparator && <Separator title="" />}
{isSeparator && <Separator />}
</StyledGroupButton>
);
}

View File

@ -84,7 +84,9 @@ const Caret = styled.div`
`}
`;
const Separator = styled.div`
const Separator = styled.div.attrs(() => ({
title: "",
}))`
vertical-align: middle;
border: ${(props) => props.theme.groupButton.separator.border};
width: ${(props) => props.theme.groupButton.separator.width};

View File

@ -14,6 +14,8 @@ import DropDown from "@docspace/components/drop-down";
import DropDownItem from "@docspace/components/drop-down-item";
import Text from "@docspace/components/text";
const PLUS = "+";
const InputPhone = ({
defaultCountry,
onChange,
@ -138,7 +140,7 @@ const InputPhone = ({
fillIcon={true}
selectedOption={country}
/>
<Label text="+" className="prefix" />
<Label text={PLUS} className="prefix" />
<TextInput
type="tel"
className="input-phone"

View File

@ -116,6 +116,7 @@ RadioButton.propTypes = {
RadioButton.defaultProps = {
isChecked: false,
isDisabled: false,
label: "",
};
export default RadioButton;

View File

@ -1376,6 +1376,7 @@ const Dark = {
borderRadius: "6px",
boxShadow:
"0px 16px 16px rgba(0, 0, 0, 0.16), 0px 8.1px 6.975px rgba(0, 0, 0, 0.108), 0px 3.2px 2.6px rgba(0, 0, 0, 0.08), 0px 0.7px 0.925px rgba(0, 0, 0, 0.052)",
boxShadowMobile: "0px -4px 60px rgba(0, 0, 0, 0.25)",
border: "1px solid #474747",
},

View File

@ -24,8 +24,6 @@
// 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
using System.Threading.Channels;
namespace ASC.Files.Service;
public class Startup : BaseWorkerStartup
{
@ -66,12 +64,12 @@ public class Startup : BaseWorkerStartup
DIHelper.TryAdd<FeedAggregatorService>();
services.AddHostedService<FeedCleanerService>();
DIHelper.TryAdd<FeedCleanerService>();
services.AddActivePassiveHostedService<FileConverterService<int>>();
DIHelper.TryAdd<FeedCleanerService>();
services.AddActivePassiveHostedService<FileConverterService<int>>(DIHelper);
DIHelper.TryAdd<FileConverterService<int>>();
services.AddActivePassiveHostedService<FileConverterService<string>>();
services.AddActivePassiveHostedService<FileConverterService<string>>(DIHelper);
DIHelper.TryAdd<FileConverterService<string>>();
services.AddHostedService<ThumbnailBuilderService>();