Merge branch 'feature/ip-security' of github.com:ONLYOFFICE/AppServer into feature/admin-messages

This commit is contained in:
Viktor Fomin 2022-04-07 17:32:15 +03:00
commit ab0404e1dc
72 changed files with 2068 additions and 644 deletions

View File

@ -22,6 +22,10 @@
<ROW Property="ARPURLUPDATEINFO" Value="http://www.onlyoffice.com/download.aspx"/>
<ROW Property="DATABASE_MIGRATION" Value="true"/>
<ROW Property="DATABASE_PROP" Value="onlyoffice"/>
<ROW Property="ELASTICSEARCH_HOST" Value="localhost" ValueLocId="-"/>
<ROW Property="ELASTICSEARCH_MSG" Value="Unable to connect to remote Elasticsearch server at "/>
<ROW Property="ELASTICSEARCH_PORT" Value="9200" ValueLocId="-"/>
<ROW Property="ELASTICSEARCH_SCHEME" Value="http" ValueLocId="-"/>
<ROW Property="ENVIRONMENT" Value="test"/>
<ROW Property="INSTALL_ROOT_FOLDER_NAME" Value="[|Manufacturer]"/>
<ROW Property="MSIFASTINSTALL" MultiBuildValue="DefaultBuild:3"/>
@ -231,6 +235,7 @@
<ROW Property="AI_SETUPEXEPATH" Signature_="AI_EXE_PATH_CU" Builds="DefaultBuild"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiBinaryComponent">
<ROW Name="PowerShellScriptLauncher.dll" SourcePath="&lt;AI_CUSTACTS&gt;PowerShellScriptLauncher.dll"/>
<ROW Name="Prereq.dll" SourcePath="&lt;AI_CUSTACTS&gt;Prereq.dll"/>
<ROW Name="SoftwareDetector.dll" SourcePath="&lt;AI_CUSTACTS&gt;SoftwareDetector.dll"/>
<ROW Name="TxtUpdater.dll" SourcePath="&lt;AI_CUSTACTS&gt;TxtUpdater.dll"/>
@ -243,6 +248,21 @@
<ROW Dialog_="AdminInstallPointDlg" Control="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Attributes="3" Text="[ButtonText_Back]" Order="600" TextLocId="-" MsiKey="AdminInstallPointDlg#Back" Options="1"/>
<ROW Dialog_="AdminWelcomeDlg" Control="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Attributes="1" Text="[ButtonText_Back]" Order="400" TextLocId="-" MsiKey="AdminWelcomeDlg#Back" Options="1"/>
<ROW Dialog_="CustomizeDlg" Control="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Attributes="3" Text="[ButtonText_Back]" Order="700" TextLocId="-" MsiKey="CustomizeDlg#Back" Options="1"/>
<ROW Dialog_="ELKConnectionDlg" Control="Next" Type="PushButton" X="236" Y="243" Width="56" Height="17" Attributes="3" Text="[ButtonText_Next]" Order="100" Options="1"/>
<ROW Dialog_="ELKConnectionDlg" Control="Cancel" Type="PushButton" X="304" Y="243" Width="56" Height="17" Attributes="3" Text="[ButtonText_Cancel]" Order="200" Options="1"/>
<ROW Dialog_="ELKConnectionDlg" Control="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Attributes="3" Text="[ButtonText_Back]" Order="300" Options="1"/>
<ROW Dialog_="ELKConnectionDlg" Control="BannerBitmap" Type="Bitmap" X="0" Y="0" Width="370" Height="44" Attributes="1048577" Text="[BannerBitmap]" Order="400"/>
<ROW Dialog_="ELKConnectionDlg" Control="BannerLine" Type="Line" X="0" Y="44" Width="372" Height="0" Attributes="1" Order="500"/>
<ROW Dialog_="ELKConnectionDlg" Control="BottomLine" Type="Line" X="5" Y="234" Width="368" Height="0" Attributes="1" Order="600"/>
<ROW Dialog_="ELKConnectionDlg" Control="Description" Type="Text" X="25" Y="23" Width="280" Height="15" Attributes="196611" Text="Описание нового диалогового окна..." Order="700"/>
<ROW Dialog_="ELKConnectionDlg" Control="Logo" Type="Text" X="4" Y="228" Width="70" Height="12" Attributes="1" Text="Advanced Installer" Order="800"/>
<ROW Dialog_="ELKConnectionDlg" Control="Title" Type="Text" X="15" Y="6" Width="200" Height="15" Attributes="196611" Text="Новое диалоговое окно" TextStyle="[DlgTitleFont]" Order="900"/>
<ROW Dialog_="ELKConnectionDlg" Control="ProtocolLabel" Type="Text" X="25" Y="65" Width="67" Height="11" Attributes="65539" Text="Protocol" Order="1000"/>
<ROW Dialog_="ELKConnectionDlg" Control="ServerLabel" Type="Text" X="25" Y="92" Width="67" Height="11" Attributes="65539" Text="Server:" Order="1100"/>
<ROW Dialog_="ELKConnectionDlg" Control="PortLabel" Type="Text" X="25" Y="119" Width="67" Height="11" Attributes="65539" Text="Port:" Order="1200"/>
<ROW Dialog_="ELKConnectionDlg" Control="ProtocolEdit" Type="Edit" X="98" Y="61" Width="253" Height="18" Attributes="3" Property="ELASTICSEARCH_SCHEME" Text="{5}" Order="1300"/>
<ROW Dialog_="ELKConnectionDlg" Control="ServerEdit" Type="Edit" X="98" Y="89" Width="253" Height="18" Attributes="3" Property="ELASTICSEARCH_HOST" Text="[ComputerName]" Order="1400"/>
<ROW Dialog_="ELKConnectionDlg" Control="PortEdit" Type="Edit" X="98" Y="116" Width="253" Height="18" Attributes="19" Property="ELASTICSEARCH_PORT" Text="{7}" Order="1500"/>
<ROW Dialog_="ExitDialog" Control="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Attributes="1" Text="[ButtonText_Back]" Order="400" TextLocId="-" MsiKey="ExitDialog#Back" Options="1"/>
<ROW Dialog_="FatalError" Control="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Attributes="1" Text="[ButtonText_Back]" Order="400" TextLocId="-" MsiKey="FatalError#Back" Options="1"/>
<ROW Dialog_="FolderDlg" Control="Back" Type="PushButton" X="180" Y="243" Width="56" Height="17" Attributes="3" Text="[ButtonText_Back]" Order="800" TextLocId="-" MsiKey="FolderDlg#Back" Options="1"/>
@ -280,11 +300,11 @@
<ATTRIBUTE name="DeletedRows" value="SQLConnectionDlg#OdbcResourceEdit@SQLConnectionDlg#OdbcResourceLabel@SQLConnectionDlg.aip"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiControlEventComponent">
<ROW Dialog_="WelcomeDlg" Control_="Next" Event="NewDialog" Argument="FolderDlg" Condition="AI_INSTALL AND ( OLDPRODUCTS = &quot;&quot; )" Ordering="4"/>
<ROW Dialog_="FolderDlg" Control_="Next" Event="NewDialog" Argument="SQLConnectionDlg" Condition="AI_INSTALL AND ( SqlConnectionError &lt;&gt; &quot;&quot; )" Ordering="203"/>
<ROW Dialog_="WelcomeDlg" Control_="Next" Event="NewDialog" Argument="FolderDlg" Condition="AI_INSTALL AND ( OLDPRODUCTS = &quot;&quot; )" Ordering="7"/>
<ROW Dialog_="FolderDlg" Control_="Next" Event="NewDialog" Argument="SQLConnectionDlg" Condition="AI_INSTALL AND ( SqlConnectionError &lt;&gt; &quot;&quot; )" Ordering="205"/>
<ROW Dialog_="FolderDlg" Control_="Back" Event="NewDialog" Argument="WelcomeDlg" Condition="AI_INSTALL" Ordering="1"/>
<ROW Dialog_="VerifyReadyDlg" Control_="Install" Event="EndDialog" Argument="Return" Condition="AI_INSTALL" Ordering="197"/>
<ROW Dialog_="VerifyReadyDlg" Control_="Back" Event="NewDialog" Argument="SQLConnectionDlg" Condition="AI_INSTALL AND ( SqlConnectionError &lt;&gt; &quot;&quot; )" Ordering="202"/>
<ROW Dialog_="VerifyReadyDlg" Control_="Back" Event="NewDialog" Argument="ELKConnectionDlg" Condition="AI_INSTALL AND ( SqlConnectionError &lt;&gt; &quot;&quot; AND ELK_CONNECTION = 0 )" Ordering="203"/>
<ROW Dialog_="MaintenanceWelcomeDlg" Control_="Next" Event="NewDialog" Argument="MaintenanceTypeDlg" Condition="AI_MAINT" Ordering="99"/>
<ROW Dialog_="CustomizeDlg" Control_="Next" Event="NewDialog" Argument="VerifyReadyDlg" Condition="AI_MAINT" Ordering="101"/>
<ROW Dialog_="CustomizeDlg" Control_="Back" Event="NewDialog" Argument="MaintenanceTypeDlg" Condition="AI_MAINT" Ordering="1"/>
@ -305,23 +325,40 @@
<ROW Dialog_="VerifyReadyDlg" Control_="Install" Event="EndDialog" Argument="Return" Condition="AI_PATCH" Ordering="199"/>
<ROW Dialog_="VerifyReadyDlg" Control_="Back" Event="NewDialog" Argument="PatchWelcomeDlg" Condition="AI_PATCH" Ordering="205"/>
<ROW Dialog_="PrerequisitesDlg" Control_="Back" Event="NewDialog" Argument="WelcomePrereqDlg" Condition="AI_BOOTSTRAPPER" Ordering="1"/>
<ROW Dialog_="SQLConnectionDlg" Control_="Next" Event="DoAction" Argument="AI_DATA_SETTER_5" Condition="AI_INSTALL AND ( SqlConnectionError &lt;&gt; &quot;&quot; )" Ordering="5"/>
<ROW Dialog_="SQLConnectionDlg" Control_="Next" Event="DoAction" Argument="TestSQLConnectionMsgBox" Condition="AI_INSTALL AND ( SqlConnectionError &lt;&gt; &quot;&quot; )" Ordering="6"/>
<ROW Dialog_="SQLConnectionDlg" Control_="Next" Event="DoAction" Argument="AI_DATA_SETTER_5" Condition="AI_INSTALL AND ( SqlConnectionError &lt;&gt; &quot;&quot; )" Ordering="4"/>
<ROW Dialog_="SQLConnectionDlg" Control_="Next" Event="DoAction" Argument="TestSQLConnectionMsgBox" Condition="AI_INSTALL AND ( SqlConnectionError &lt;&gt; &quot;&quot; )" Ordering="5"/>
<ROW Dialog_="SQLConnectionDlg" Control_="SQLConnectionDlgDialogInitializer" Event="[PASSWORD_PROP]" Argument="{}" Condition="AI_INSTALL AND ( OLDPRODUCTS=&quot;&quot; AND SQLConnectionDlg_Cond )" Ordering="4"/>
<ROW Dialog_="SQLConnectionDlg" Control_="SQLConnectionDlgDialogInitializer" Event="[AI_ButtonText_Next_Orig]" Argument="[ButtonText_Next]" Condition="AI_INSTALL AND ( OLDPRODUCTS=&quot;&quot; AND SQLConnectionDlg_Cond )" Ordering="3"/>
<ROW Dialog_="SQLConnectionDlg" Control_="SQLConnectionDlgDialogInitializer" Event="[ButtonText_Next]" Argument="[[AI_CommitButton]]" Condition="AI_INSTALL AND ( OLDPRODUCTS=&quot;&quot; AND SQLConnectionDlg_Cond )" Ordering="2"/>
<ROW Dialog_="SQLConnectionDlg" Control_="SQLConnectionDlgDialogInitializer" Event="[AI_Text_Next_Orig]" Argument="[Text_Next]" Condition="AI_INSTALL AND ( OLDPRODUCTS=&quot;&quot; AND SQLConnectionDlg_Cond )" Ordering="1"/>
<ROW Dialog_="SQLConnectionDlg" Control_="SQLConnectionDlgDialogInitializer" Event="[Text_Next]" Argument="[Text_Install]" Condition="AI_INSTALL AND ( OLDPRODUCTS=&quot;&quot; AND SQLConnectionDlg_Cond )" Ordering="0"/>
<ROW Dialog_="SQLConnectionDlg" Control_="Next" Event="NewDialog" Argument="VerifyReadyDlg" Condition="AI_INSTALL AND ( SqlConnectionError = &quot;&quot; )" Ordering="7"/>
<ROW Dialog_="SQLConnectionDlg" Control_="Back" Event="NewDialog" Argument="FolderDlg" Condition="AI_INSTALL" Ordering="1"/>
<ROW Dialog_="SQLConnectionDlg" Control_="Next" Event="NewDialog" Argument="ELKConnectionDlg" Condition="AI_INSTALL AND ( SqlConnectionError = &quot;&quot; AND ELK_CONNECTION = 0 )" Ordering="7"/>
<ROW Dialog_="SQLConnectionDlg" Control_="Back" Event="NewDialog" Argument="FolderDlg" Condition="AI_INSTALL AND ( OLDPRODUCTS = &quot;&quot; )" Ordering="2"/>
<ROW Dialog_="WelcomeDlg" Control_="Next" Event="DoAction" Argument="TestSqlConnection" Condition="AI_INSTALL" Ordering="1"/>
<ROW Dialog_="WelcomeDlg" Control_="Next" Event="NewDialog" Argument="SQLConnectionDlg" Condition="AI_INSTALL AND ( SqlConnectionError &lt;&gt; &quot;&quot; )" Ordering="2"/>
<ROW Dialog_="WelcomeDlg" Control_="Next" Event="NewDialog" Argument="VerifyReadyDlg" Condition="AI_INSTALL AND ( SqlConnectionError=&quot;&quot; )" Ordering="3"/>
<ROW Dialog_="WelcomeDlg" Control_="Next" Event="NewDialog" Argument="SQLConnectionDlg" Condition="AI_INSTALL AND ( SqlConnectionError &lt;&gt; &quot;&quot; )" Ordering="4"/>
<ROW Dialog_="WelcomeDlg" Control_="Next" Event="NewDialog" Argument="VerifyReadyDlg" Condition="AI_INSTALL AND ( SqlConnectionError=&quot;&quot; AND ELK_CONNECTION = 1 )" Ordering="6"/>
<ROW Dialog_="FolderDlg" Control_="Next" Event="DoAction" Argument="TestSqlConnection" Condition="AI_INSTALL" Ordering="201"/>
<ROW Dialog_="FolderDlg" Control_="Next" Event="NewDialog" Argument="VerifyReadyDlg" Condition="AI_INSTALL AND ( SqlConnectionError = &quot;&quot; )" Ordering="204"/>
<ROW Dialog_="SQLConnectionDlg" Control_="Next" Event="DoAction" Argument="TestSqlConnection" Condition="AI_INSTALL" Ordering="4"/>
<ROW Dialog_="VerifyReadyDlg" Control_="Back" Event="NewDialog" Argument="FolderDlg" Condition="AI_INSTALL AND ( OLDPRODUCTS = &quot;&quot; AND SqlConnectionError = &quot;&quot; )" Ordering="201"/>
<ROW Dialog_="VerifyReadyDlg" Control_="Back" Event="NewDialog" Argument="WelcomeDlg" Condition="AI_INSTALL" Ordering="203"/>
<ROW Dialog_="FolderDlg" Control_="Next" Event="NewDialog" Argument="VerifyReadyDlg" Condition="AI_INSTALL AND ( SqlConnectionError = &quot;&quot; AND ELK_CONNECTION = 1 )" Ordering="207"/>
<ROW Dialog_="SQLConnectionDlg" Control_="Next" Event="DoAction" Argument="TestSqlConnection" Condition="AI_INSTALL" Ordering="3"/>
<ROW Dialog_="VerifyReadyDlg" Control_="Back" Event="NewDialog" Argument="FolderDlg" Condition="AI_INSTALL AND ( OLDPRODUCTS = &quot;&quot; AND SqlConnectionError = &quot;&quot; AND ELK_CONNECTION = 1 )" Ordering="201"/>
<ROW Dialog_="VerifyReadyDlg" Control_="Back" Event="NewDialog" Argument="WelcomeDlg" Condition="AI_INSTALL" Ordering="202"/>
<ROW Dialog_="ELKConnectionDlg" Control_="Cancel" Event="SpawnDialog" Argument="CancelDlg" Condition="1" Ordering="100"/>
<ROW Dialog_="ELKConnectionDlg" Control_="Next" Event="NewDialog" Argument="VerifyReadyDlg" Condition="AI_INSTALL AND ( ELK_CONNECTION = 1 AND SqlConnectionError = &quot;&quot; )" Ordering="7"/>
<ROW Dialog_="ELKConnectionDlg" Control_="Back" Event="NewDialog" Argument="FolderDlg" Condition="AI_INSTALL AND ( OLDPRODUCTS = &quot;&quot; )" Ordering="3"/>
<ROW Dialog_="ELKConnectionDlg" Control_="Back" Event="NewDialog" Argument="SQLConnectionDlg" Condition="AI_INSTALL AND ( SqlConnectionError &lt;&gt; &quot;&quot; )" Ordering="5"/>
<ROW Dialog_="ELKConnectionDlg" Control_="Next" Event="DoAction" Argument="TestElasticsearchConnection" Condition="AI_INSTALL" Ordering="3"/>
<ROW Dialog_="ELKConnectionDlg" Control_="Next" Event="DoAction" Argument="AI_DATA_SETTER_1" Condition="AI_INSTALL" Ordering="2"/>
<ROW Dialog_="ELKConnectionDlg" Control_="Next" Event="DoAction" Argument="TestElasticsearchConnectionMsgBox" Condition="AI_INSTALL AND ( ELK_CONNECTION = 0 )" Ordering="6"/>
<ROW Dialog_="ELKConnectionDlg" Control_="Next" Event="DoAction" Argument="AI_DATA_SETTER" Condition="AI_INSTALL AND ( ELK_CONNECTION = 0 )" Ordering="5"/>
<ROW Dialog_="SQLConnectionDlg" Control_="Back" Event="NewDialog" Argument="WelcomeDlg" Condition="AI_INSTALL AND ( OLDPRODUCTS &lt;&gt; &quot;&quot; )" Ordering="3"/>
<ROW Dialog_="ELKConnectionDlg" Control_="Back" Event="NewDialog" Argument="WelcomeDlg" Condition="AI_INSTALL AND ( OLDPRODUCTS &lt;&gt; &quot;&quot; )" Ordering="4"/>
<ROW Dialog_="WelcomeDlg" Control_="Next" Event="DoAction" Argument="TestElasticsearchConnection" Condition="AI_INSTALL AND ( SqlConnectionError = &quot;&quot; )" Ordering="3"/>
<ROW Dialog_="WelcomeDlg" Control_="Next" Event="DoAction" Argument="AI_DATA_SETTER_1" Condition="AI_INSTALL AND ( SqlConnectionError = &quot;&quot; )" Ordering="2"/>
<ROW Dialog_="WelcomeDlg" Control_="Next" Event="NewDialog" Argument="ELKConnectionDlg" Condition="AI_INSTALL AND ( SqlConnectionError = &quot;&quot; AND ELK_CONNECTION = 0 )" Ordering="5"/>
<ROW Dialog_="SQLConnectionDlg" Control_="Next" Event="SpawnDialog" Argument="VerifyReadyDlg" Condition="AI_INSTALL AND ( SqlConnectionError = &quot;&quot; AND ELK_CONNECTION = 1 )" Ordering="8"/>
<ROW Dialog_="FolderDlg" Control_="Next" Event="DoAction" Argument="TestElasticsearchConnection" Condition="AI_INSTALL" Ordering="204"/>
<ROW Dialog_="FolderDlg" Control_="Next" Event="DoAction" Argument="AI_DATA_SETTER_1" Condition="AI_INSTALL" Ordering="203"/>
<ROW Dialog_="FolderDlg" Control_="Next" Event="NewDialog" Argument="ELKConnectionDlg" Condition="AI_INSTALL AND ( SqlConnectionError = &quot;&quot; AND ELK_CONNECTION = 0 )" Ordering="206"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiCreateFolderComponent">
<ROW Directory_="APPDIR" Component_="APPDIR" ManualDelete="true"/>
@ -331,6 +368,8 @@
<ROW Action="AI_AppSearchEx" Type="1" Source="Prereq.dll" Target="DoAppSearchEx"/>
<ROW Action="AI_BACKUP_AI_SETUPEXEPATH" Type="51" Source="AI_SETUPEXEPATH_ORIGINAL" Target="[AI_SETUPEXEPATH]"/>
<ROW Action="AI_ConfigFailActions" Type="11265" Source="aicustact.dll" Target="ConfigureServFailActions" WithoutSeq="true"/>
<ROW Action="AI_DATA_SETTER" Type="51" Source="CustomActionData" Target="[ELASTICSEARCH_MSG]&#13;\n[ELASTICSEARCH_SCHEME]://[ELASTICSEARCH_HOST]:[ELASTICSEARCH_PORT] |[ProductName] Setup |MB_OK,MB_ICONWARNING,MB_DEFBUTTON1||[CLIENTPROCESSID]"/>
<ROW Action="AI_DATA_SETTER_1" Type="51" Source="CustomActionData" Target="AEQAaQBnAGkAdABhAGwAbAB5AFMAaQBnAG4AUwBjAHIAaQBwAHQAAgABAEYAbABhAGcAcwACADYAAQBQAGEAcgBhAG0AcwACAAEAUwBjAHIAaQBwAHQAAgAjAFIAZQBxAHUAaQByAGUAcwAgAC0AdgBlAHIAcwBpAG8AbgAgADMADQAKAFAAYQByAGEAbQAoACkADQAKAA0ACgBmAHUAbgBjAHQAaQBvAG4AIABDAGgAZQBjAGsAVABDAFAAUABvAHIAdABBAHYAYQBpAGwAYQBiAGkAbABpAHQAeQAgAFsAXAB7AF0ADQAKAA0ACgAgACAAcABhAHIAYQBtACAAKAAgACQAdABjAHAAXwBoAG8AcwB0ACwAIAAkAHAAbwByAHQAIAApAA0ACgANAAoAIAAgACQAdABpAG0AZQBfAHQAbwBfAHcAYQBpAHQAIAA9ACAAMQAwADAAMAANAAoAIAAgACQAdABjAHAAbwBiAGoAZQBjAHQAIAA9ACAATgBlAHcALQBPAGIAagBlAGMAdAAgAHMAeQBzAHQAZQBtAC4ATgBlAHQALgBTAG8AYwBrAGUAdABzAC4AVABjAHAAQwBsAGkAZQBuAHQADQAKACAAIAAkAGMAbwBuAG4AZQBjAHQAIAA9ACAAJAB0AGMAcABvAGIAagBlAGMAdAAuAEIAZQBnAGkAbgBDAG8AbgBuAGUAYwB0ACgAJAB0AGMAcABfAGgAbwBzAHQALAAgACQAcABvAHIAdAAsACAAJABuAHUAbABsACwAIAAkAG4AdQBsAGwAKQANAAoAIAAgACQAdwBhAGkAdAAgAD0AIAAkAGMAbwBuAG4AZQBjAHQALgBBAHMAeQBuAGMAVwBhAGkAdABIAGEAbgBkAGwAZQAuAFcAYQBpAHQATwBuAGUAKAAkAHQAaQBtAGUAXwB0AG8AXwB3AGEAaQB0ACwAIAAkAGYAYQBsAHMAZQApAA0ACgAgACAAaQBmACAAKAAtAE4AbwB0ACAAJAB3AGEAaQB0ACkAIABbAFwAewBdAA0ACgAgACAAIAAgAHIAZQB0AHUAcgBuACAAMAANAAoAIAAgAFsAXAB9AF0AIABlAGwAcwBlACAAWwBcAHsAXQANAAoAIAAgACAAIAAkAEUAcgByAG8AcgAuAGMAbABlAGEAcgAoACkADQAKACAAIAAgACAAJAB0AGMAcABvAGIAagBlAGMAdAAuAEUAbgBkAEMAbwBuAG4AZQBjAHQAKAAkAGMAbwBuAG4AZQBjAHQAKQAgAHwAIABPAHUAdAAtAE4AdQBsAGwADQAKACAAIAAgACAAaQBmACAAKAAkAEUAcgByAG8AcgBbAFwAWwBdADAAWwBcAF0AXQApACAAWwBcAHsAXQANAAoAIAAgACAAIAAgACAAIAAgAFcAcgBpAHQAZQAtAFcAYQByAG4AaQBuAGcAIAAoACIAWwBcAHsAXQAwAFsAXAB9AF0AIgAgAC0AZgAgACQARQByAHIAbwByAFsAXABbAF0AMABbAFwAXQBdAC4ARQB4AGMAZQBwAHQAaQBvAG4ALgBNAGUAcwBzAGEAZwBlACkADQAKACAAIAAgACAAWwBcAH0AXQAgAGUAbABzAGUAIABbAFwAewBdAA0ACgAgACAAIAAgACAAIAAgACAAcgBlAHQAdQByAG4AIAAxAA0ACgAgACAAIAAgAFsAXAB9AF0ADQAKACAAIABbAFwAfQBdAA0ACgBbAFwAfQBdAA0ACgANAAoAJABFAEwASwBfAEgATwBTAFQAIAA9ACAAQQBJAF8ARwBlAHQATQBzAGkAUAByAG8AcABlAHIAdAB5ACAARQBMAEEAUwBUAEkAQwBTAEUAQQBSAEMASABfAEgATwBTAFQADQAKACQARQBMAEsAXwBQAE8AUgBUACAAPQAgAEEASQBfAEcAZQB0AE0AcwBpAFAAcgBvAHAAZQByAHQAeQAgAEUATABBAFMAVABJAEMAUwBFAEEAUgBDAEgAXwBQAE8AUgBUAA0ACgANAAoAJABFAEwASwBfAEMATwBOAE4ARQBDAFQASQBPAE4AIAA9ACAAQwBoAGUAYwBrAFQAQwBQAFAAbwByAHQAQQB2AGEAaQBsAGEAYgBpAGwAaQB0AHkAIAAkAEUATABLAF8ASABPAFMAVAAgACQARQBMAEsAXwBQAE8AUgBUAA0ACgANAAoAQQBJAF8AUwBlAHQATQBzAGkAUAByAG8AcABlAHIAdAB5ACAARQBMAEsAXwBDAE8ATgBOAEUAQwBUAEkATwBOACAAJABFAEwASwBfAEMATwBOAE4ARQBDAFQASQBPAE4="/>
<ROW Action="AI_DATA_SETTER_10" Type="51" Source="CustomActionData" Target="MySQL80"/>
<ROW Action="AI_DATA_SETTER_11" Type="51" Source="CustomActionData" Target="MySQL80"/>
<ROW Action="AI_DATA_SETTER_12" Type="51" Source="CustomActionData" Target="Elasticsearch"/>
@ -388,9 +427,14 @@
<ROW Action="StartMySQLService" Type="1" Source="aicustact.dll" Target="StartWinService" Options="1" AdditionalSeq="AI_DATA_SETTER_11"/>
<ROW Action="StopElasticSearchService" Type="1" Source="aicustact.dll" Target="StopWinService" Options="1" AdditionalSeq="AI_DATA_SETTER_12"/>
<ROW Action="StopMySQLService" Type="1" Source="aicustact.dll" Target="StopWinService" Options="1" AdditionalSeq="AI_DATA_SETTER_10"/>
<ROW Action="TestElasticsearchConnection" Type="1" Source="PowerShellScriptLauncher.dll" Target="RunPowerShellScript" WithoutSeq="true" Options="1" AdditionalSeq="AI_DATA_SETTER_1"/>
<ROW Action="TestElasticsearchConnectionMsgBox" Type="1" Source="aicustact.dll" Target="MsgBox" WithoutSeq="true" Options="1" AdditionalSeq="AI_DATA_SETTER"/>
<ROW Action="TestSQLConnectionMsgBox" Type="1" Source="aicustact.dll" Target="MsgBox" WithoutSeq="true" Options="1" AdditionalSeq="AI_DATA_SETTER_5"/>
<ROW Action="TestSqlConnection" Type="6" Source="utils.vbs" Target="TestSqlConnection" WithoutSeq="true"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiDialogComponent">
<ROW Dialog="ELKConnectionDlg" HCentering="50" VCentering="50" Width="370" Height="270" Attributes="3" Title="[ProductName] [Setup]" Control_Default="Next" Control_Cancel="Cancel"/>
</COMPONENT>
<COMPONENT cid="caphyon.advinst.msicomp.MsiEnvComponent">
<ROW Environment="JAVA_HOME" Name="=-*JAVA_HOME" Value="[WindowsVolume]Progra~1\Elastic\Elasticsearch\7.13.1\jdk" Component_="AI_ExePath"/>
<ROW Environment="Path" Name="=*Path" Value="[~];%JAVA_HOME%\bin" Component_="AI_ExePath"/>

View File

@ -75,6 +75,7 @@ namespace ASC.IPSecurity
});
TenantDbContext.TenantIpRestrictions.AddRange(ipsList);
TenantDbContext.SaveChanges();
tx.Commit();
return ips.ToList();

View File

@ -159,7 +159,7 @@
"enabled": "true"
},
"thumbnail": {
"thumbnaillHeight": 156,
"thumbnaillWidth": 216
"thumbnaillHeight": 260,
"thumbnaillWidth": 360
}
}

View File

@ -1,5 +1,5 @@
{
"kafka": {
"BootstrapServers": ""
}
}
{
"kafka": {
"BootstrapServers": "localhost:9092"
}
}

View File

@ -13,103 +13,105 @@ import SortButton from "./sub-components/SortButton";
import { StyledFilterInput, StyledSearchInput } from "./StyledFilterInput";
const FilterInput = ({
t,
sectionWidth,
getFilterData,
getSortData,
getViewSettingsData,
getSelectedFilterData,
onFilter,
onSearch,
onSort,
onChangeViewAs,
viewAs,
placeholder,
contextMenuHeader,
headerLabel,
viewSelectorVisible,
isRecentFolder,
isFavoritesFolder,
...props
}) => {
const [viewSettings, setViewSettings] = React.useState([]);
const [selectedFilterData, setSelectedFilterData] = React.useState([]);
const FilterInput = React.memo(
({
t,
sectionWidth,
getFilterData,
getSortData,
getViewSettingsData,
getSelectedFilterData,
onFilter,
onSearch,
onSort,
onChangeViewAs,
viewAs,
placeholder,
contextMenuHeader,
headerLabel,
viewSelectorVisible,
isRecentFolder,
isFavoritesFolder,
...props
}) => {
const [viewSettings, setViewSettings] = React.useState([]);
const [selectedFilterData, setSelectedFilterData] = React.useState([]);
const [inputValue, setInputValue] = React.useState("");
const [inputValue, setInputValue] = React.useState("");
const getSelectedFilterDataAction = React.useCallback(async () => {
const data = await getSelectedFilterData();
const getSelectedFilterDataAction = React.useCallback(async () => {
const data = await getSelectedFilterData();
setSelectedFilterData(data);
setInputValue(!!data.inputValue ? data.inputValue : "");
}, [getSelectedFilterData]);
setSelectedFilterData(data);
setInputValue(!!data.inputValue ? data.inputValue : "");
}, [getSelectedFilterData]);
React.useEffect(() => {
getSelectedFilterDataAction();
}, [getSelectedFilterData]);
React.useEffect(() => {
getSelectedFilterDataAction();
}, [getSelectedFilterData]);
React.useEffect(() => {
getViewSettingsData && setViewSettings(getViewSettingsData());
}, [getViewSettingsData]);
React.useEffect(() => {
getViewSettingsData && setViewSettings(getViewSettingsData());
}, [getViewSettingsData]);
const onClearSearch = () => {
onSearch && onSearch();
};
const onClearSearch = () => {
onSearch && onSearch();
};
return (
<StyledFilterInput {...props} sectionWidth={sectionWidth}>
<StyledSearchInput
placeholder={placeholder}
value={inputValue}
onChange={onSearch}
onClearSearch={onClearSearch}
/>
<FilterButton
t={t}
selectedFilterData={selectedFilterData}
contextMenuHeader={contextMenuHeader}
getFilterData={getFilterData}
onFilter={onFilter}
headerLabel={headerLabel}
/>
{viewSettings &&
!isMobile &&
viewSelectorVisible &&
!isMobileUtils() &&
!isTabletUtils() ? (
<ViewSelector
style={{ marginLeft: "8px" }}
onChangeView={onChangeViewAs}
viewAs={viewAs === "table" ? "row" : viewAs}
viewSettings={viewSettings}
return (
<StyledFilterInput {...props} sectionWidth={sectionWidth}>
<StyledSearchInput
placeholder={placeholder}
value={inputValue}
onChange={onSearch}
onClearSearch={onClearSearch}
/>
) : (
<>
{(isMobile || isTabletUtils() || isMobileUtils()) && (
<SortButton
t={t}
selectedFilterData={selectedFilterData}
getSortData={getSortData}
onChangeViewAs={onChangeViewAs}
viewAs={viewAs === "table" ? "row" : viewAs}
viewSettings={viewSettings}
onSort={onSort}
viewSelectorVisible={viewSelectorVisible}
isRecentFolder={isRecentFolder}
isFavoritesFolder={isFavoritesFolder}
/>
)}
</>
)}
</StyledFilterInput>
);
};
<FilterButton
t={t}
selectedFilterData={selectedFilterData}
contextMenuHeader={contextMenuHeader}
getFilterData={getFilterData}
onFilter={onFilter}
headerLabel={headerLabel}
/>
{viewSettings &&
!isMobile &&
viewSelectorVisible &&
!isMobileUtils() &&
!isTabletUtils() ? (
<ViewSelector
style={{ marginLeft: "8px" }}
onChangeView={onChangeViewAs}
viewAs={viewAs === "table" ? "row" : viewAs}
viewSettings={viewSettings}
/>
) : (
<>
{(isMobile || isTabletUtils() || isMobileUtils()) && (
<SortButton
t={t}
selectedFilterData={selectedFilterData}
getSortData={getSortData}
onChangeViewAs={onChangeViewAs}
viewAs={viewAs === "table" ? "row" : viewAs}
viewSettings={viewSettings}
onSort={onSort}
viewSelectorVisible={viewSelectorVisible}
isRecentFolder={isRecentFolder}
isFavoritesFolder={isFavoritesFolder}
/>
)}
</>
)}
</StyledFilterInput>
);
}
);
FilterInput.defaultProps = {
viewSelectorVisible: false,
};
export default React.memo(FilterInput);
export default FilterInput;

View File

@ -30,6 +30,10 @@ const Navigation = ({
isRecycleBinFolder,
isEmptyFilesList,
clearTrash,
showFolderInfo,
isCurrentFolderInfo,
toggleInfoPanel,
isInfoPanelVisible,
...rest
}) => {
const [isOpen, setIsOpen] = React.useState(false);
@ -133,6 +137,8 @@ const Navigation = ({
isRecycleBinFolder={isRecycleBinFolder}
isEmptyFilesList={isEmptyFilesList}
clearTrash={clearTrash}
toggleInfoPanel={toggleInfoPanel}
isInfoPanelVisible={isInfoPanelVisible}
/>
</StyledContainer>
</>

View File

@ -3,16 +3,11 @@ import { isMobile, isMobileOnly } from "react-device-detect";
import { tablet, desktop, mobile } from "@appserver/components/utils/device";
const StyledContainer = styled.div`
padding: ${(props) => (props.isDropBox ? "14px 0 3px" : "14px 0 0px")};
width: fit-content;
width: 100% !important;
display: grid;
grid-template-columns: ${(props) =>
props.isRootFolder ? "1fr auto" : "29px 1fr auto"};
align-items: center;
grid-template-columns: ${(props) =>
props.isRootFolder ? "auto 1fr" : "29px auto 1fr"};
.arrow-button {
width: 17px;
@ -31,8 +26,7 @@ const StyledContainer = styled.div`
`}
@media ${mobile} {
width: 100%;
padding: ${(props) => (props.isDropBox ? "12px 0 5px" : "12px 0 0")};
height: 53px;
}
${isMobileOnly &&

View File

@ -5,6 +5,7 @@ import ContextMenuButton from "@appserver/components/context-menu-button";
import IconButton from "@appserver/components/icon-button";
import { isMobile } from "react-device-detect";
import { tablet } from "@appserver/components/utils/device";
import { Base } from "@appserver/components/themes";
const StyledContainer = styled.div`
margin-left: 20px;
@ -30,7 +31,8 @@ const StyledContainer = styled.div`
}
.option-button {
margin-right: 8px;
margin-left: auto;
margin-right: 15px;
min-width: 17px;
}
@ -39,6 +41,35 @@ const StyledContainer = styled.div`
}
`;
const StyledInfoPanelToggleWrapper = styled.div`
display: flex;
align-items: center;
align-self: center;
justify-content: center;
margin-left: ${({ isRootFolder }) => (isRootFolder ? "auto" : "none")};
.info-panel-toggle-bg {
height: 32px;
width: 32px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background-color: ${(props) =>
props.isInfoPanelVisible
? props.theme.infoPanel.sectionHeaderToggleBgActive
: props.theme.infoPanel.sectionHeaderToggleBg};
path {
fill: ${(props) =>
props.isInfoPanelVisible
? props.theme.infoPanel.sectionHeaderToggleIconActive
: props.theme.infoPanel.sectionHeaderToggleIcon};
}
}
`;
StyledInfoPanelToggleWrapper.defaultProps = { theme: Base };
const ControlButtons = ({
personal,
isDropBox,
@ -49,6 +80,8 @@ const ControlButtons = ({
isRecycleBinFolder,
isEmptyFilesList,
clearTrash,
isInfoPanelVisible,
toggleInfoPanel,
}) => {
return (
<StyledContainer isDropBox={isDropBox}>
@ -96,6 +129,20 @@ const ControlButtons = ({
) : (
<></>
)}
<StyledInfoPanelToggleWrapper
isRootFolder={isRootFolder}
isInfoPanelVisible={isInfoPanelVisible}
>
<div className="info-panel-toggle-bg">
<IconButton
className="info-panel-toggle"
iconName="images/panel.svg"
size="16"
isFill={true}
onClick={toggleInfoPanel}
/>
</div>
</StyledInfoPanelToggleWrapper>
</StyledContainer>
);
};

View File

@ -19,6 +19,10 @@ import SubSectionBody from "./sub-components/section-body";
import SubSectionBodyContent from "./sub-components/section-body-content";
import SubSectionBar from "./sub-components/section-bar";
import SubSectionPaging from "./sub-components/section-paging";
//import SectionToggler from "./sub-components/section-toggler";
import InfoPanel from "./sub-components/info-panel";
import SubInfoPanelBody from "./sub-components/info-panel-body";
import SubInfoPanelHeader from "./sub-components/info-panel-header";
import ReactResizeDetector from "react-resize-detector";
import FloatingButton from "../FloatingButton";
@ -36,8 +40,13 @@ const StyledMainBar = styled.div`
box-sizing: border-box;
margin-left: -20px;
width: calc(100vw - 256px);
max-width: calc(100vw - 256px);
/* width: calc(100vw - 256px);
max-width: calc(100vw - 256px); */
width: ${(props) =>
props.infoPanelIsVisible ? "calc(100vw - 657px)" : "calc(100vw - 256px)"};
max-width: ${(props) =>
props.infoPanelIsVisible ? "calc(100vw - 657px)" : "calc(100vw - 256px)"};
#bar-banner {
margin-bottom: -3px;
@ -123,12 +132,24 @@ function SectionPaging() {
}
SectionPaging.displayName = "SectionPaging";
function InfoPanelBody() {
return null;
}
InfoPanelBody.displayName = "InfoPanelBody";
function InfoPanelHeader() {
return null;
}
InfoPanelHeader.displayName = "InfoPanelHeader";
class Section extends React.Component {
static SectionHeader = SectionHeader;
static SectionFilter = SectionFilter;
static SectionBody = SectionBody;
static SectionBar = SectionBar;
static SectionPaging = SectionPaging;
static InfoPanelBody = InfoPanelBody;
static InfoPanelHeader = InfoPanelHeader;
constructor(props) {
super(props);
@ -208,6 +229,7 @@ class Section extends React.Component {
setMaintenanceExist,
snackbarExist,
showText,
infoPanelIsVisible,
} = this.props;
let sectionHeaderContent = null;
@ -215,6 +237,9 @@ class Section extends React.Component {
let sectionFilterContent = null;
let sectionPagingContent = null;
let sectionBodyContent = null;
let infoPanelBodyContent = null;
let infoPanelHeaderContent = null;
React.Children.forEach(children, (child) => {
const childType =
child && child.type && (child.type.displayName || child.type.name);
@ -235,6 +260,12 @@ class Section extends React.Component {
case SectionBody.displayName:
sectionBodyContent = child;
break;
case InfoPanelBody.displayName:
infoPanelBodyContent = child;
break;
case InfoPanelHeader.displayName:
infoPanelHeaderContent = child;
break;
default:
break;
}
@ -277,6 +308,7 @@ class Section extends React.Component {
maintenanceExist={maintenanceExist}
isSectionBarAvailable={isSectionBarAvailable}
isSectionHeaderAvailable={isSectionHeaderAvailable}
infoPanelIsVisible={infoPanelIsVisible}
>
{!isMobile && (
<StyledMainBar
@ -285,6 +317,7 @@ class Section extends React.Component {
className={"main-bar"}
showText={showText}
isSectionHeaderAvailable={isSectionHeaderAvailable}
infoPanelIsVisible={infoPanelIsVisible}
>
<SubSectionBar
setMaintenanceExist={setMaintenanceExist}
@ -302,6 +335,7 @@ class Section extends React.Component {
snackbarExist={snackbarExist}
className="section-header_header"
isHeaderVisible={isHeaderVisible}
infoPanelIsVisible={infoPanelIsVisible}
viewAs={viewAs}
showText={showText}
>
@ -322,6 +356,7 @@ class Section extends React.Component {
</SubSectionFilter>
</>
)}
{isSectionBodyAvailable && (
<>
<SubSectionBody
@ -341,6 +376,7 @@ class Section extends React.Component {
isSectionHeaderAvailable={
isSectionHeaderAvailable
}
infoPanelIsVisible={infoPanelIsVisible}
>
<SubSectionBar
setMaintenanceExist={setMaintenanceExist}
@ -358,6 +394,7 @@ class Section extends React.Component {
isHeaderVisible={isHeaderVisible}
viewAs={viewAs}
showText={showText}
infoPanelIsVisible={infoPanelIsVisible}
>
{sectionHeaderContent
? sectionHeaderContent.props.children
@ -372,11 +409,13 @@ class Section extends React.Component {
: null}
</SubSectionFilter>
)}
<SubSectionBodyContent>
{sectionBodyContent
? sectionBodyContent.props.children
: null}
</SubSectionBodyContent>
{isSectionPagingAvailable && (
<SubSectionPaging>
{sectionPagingContent
@ -387,6 +426,7 @@ class Section extends React.Component {
</SubSectionBody>
</>
)}
{!(isMobile || isMobileUtils() || isTabletUtils()) ? (
showPrimaryProgressBar && showSecondaryProgressBar ? (
<>
@ -428,6 +468,12 @@ class Section extends React.Component {
<></>
)}
</SectionContainer>
<InfoPanel>
<SubInfoPanelHeader>
{infoPanelHeaderContent}
</SubInfoPanelHeader>
<SubInfoPanelBody>{infoPanelBodyContent}</SubInfoPanelBody>
</InfoPanel>
</Provider>
)}
</ReactResizeDetector>
@ -501,12 +547,14 @@ Section.defaultProps = {
withBodyAutoFocus: false,
};
Section.InfoPanelHeader = InfoPanelHeader;
Section.InfoPanelBody = InfoPanelBody;
Section.SectionHeader = SectionHeader;
Section.SectionFilter = SectionFilter;
Section.SectionBody = SectionBody;
Section.SectionPaging = SectionPaging;
export default inject(({ auth }) => {
export default inject(({ auth, infoPanelStore }) => {
const { isLoaded, settingsStore } = auth;
const {
isHeaderVisible,
@ -523,6 +571,9 @@ export default inject(({ auth }) => {
showText,
} = settingsStore;
let infoPanelIsVisible = false;
if (infoPanelStore) infoPanelIsVisible = infoPanelStore.isVisible;
return {
isLoaded,
isTabletView,
@ -536,5 +587,7 @@ export default inject(({ auth }) => {
isDesktop: isDesktopClient,
showText,
infoPanelIsVisible: infoPanelIsVisible,
};
})(observer(Section));

View File

@ -0,0 +1,16 @@
import Scrollbar from "@appserver/components/scrollbar";
import React from "react";
const SubInfoPanelBody = ({ children }) => {
const content = children?.props?.children;
return (
<Scrollbar scrollclass="section-scroll" stype="mediumBlack">
{content}
</Scrollbar>
);
};
SubInfoPanelBody.displayName = "SubInfoPanelBody";
export default SubInfoPanelBody;

View File

@ -0,0 +1,58 @@
import IconButton from "@appserver/components/icon-button";
import Text from "@appserver/components/text";
import { Base } from "@appserver/components/themes";
import { tablet } from "@appserver/components/utils/device";
import { inject, observer } from "mobx-react";
import PropTypes from "prop-types";
import React from "react";
import styled from "styled-components";
const StyledInfoPanelHeader = styled.div`
width: 100%;
max-width: 100%;
height: 54px;
min-height: 54px;
box-sizing: border-box;
display: flex;
justify-content: space-between;
align-items: center;
border-bottom: ${(props) => `1px solid ${props.theme.infoPanel.borderColor}`};
.header-text {
margin-left: 20px;
}
`;
const SubInfoPanelHeader = ({ children, onHeaderCrossClick }) => {
const content = children?.props?.children;
return (
<StyledInfoPanelHeader>
<Text className="header-text" fontSize="21px" fontWeight="700">
{content}
</Text>
</StyledInfoPanelHeader>
);
};
SubInfoPanelHeader.propTypes = {
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node,
PropTypes.any,
]),
toggleIsVisible: PropTypes.func,
};
StyledInfoPanelHeader.defaultProps = { theme: Base };
SubInfoPanelHeader.defaultProps = { theme: Base };
SubInfoPanelHeader.displayName = "SubInfoPanelHeader";
export default inject(({ infoPanelStore }) => {
let onHeaderCrossClick = () => {};
if (infoPanelStore) {
onHeaderCrossClick = infoPanelStore.onHeaderCrossClick;
}
return { onHeaderCrossClick };
})(observer(SubInfoPanelHeader));

View File

@ -0,0 +1,140 @@
import IconButton from "@appserver/components/icon-button";
import { Base } from "@appserver/components/themes";
import { isTablet, mobile, tablet } from "@appserver/components/utils/device";
import { inject } from "mobx-react";
import PropTypes from "prop-types";
import React, { useEffect } from "react";
import styled from "styled-components";
const StyledInfoPanelWrapper = styled.div.attrs(({ id }) => ({
id: id,
}))`
height: auto;
width: auto;
background: rgba(6, 22, 38, 0.2);
backdrop-filter: blur(18px);
@media ${tablet} {
z-index: 309;
position: absolute;
top: 0;
bottom: 0;
left: 0;
right: 0;
}
`;
const StyledInfoPanel = styled.div`
height: 100%;
width: 400px;
background-color: ${(props) => props.theme.infoPanel.backgroundColor};
border-left: ${(props) => `1px solid ${props.theme.infoPanel.borderColor}`};
display: flex;
flex-direction: column;
@media ${tablet} {
position: absolute;
border: none;
right: 0;
width: 480px;
max-width: calc(100vw - 69px);
}
@media ${mobile} {
bottom: 0;
height: 80%;
width: 100vw;
max-width: 100vw;
}
`;
const StyledCloseButtonWrapper = styled.div`
position: absolute;
display: none;
background-color: ${(props) => props.theme.infoPanel.closeButtonBg};
padding: ${(props) => props.theme.infoPanel.closeButtonWrapperPadding};
border-radius: 50%;
.info-panel-button {
svg {
width: ${(props) => props.theme.infoPanel.closeButtonSize};
height: ${(props) => props.theme.infoPanel.closeButtonSize};
}
path {
fill: ${(props) => props.theme.infoPanel.closeButtonIcon};
}
}
@media ${tablet} {
display: block;
top: 0;
left: 0;
margin-top: 18px;
margin-left: -34px;
}
@media ${mobile} {
right: 0;
left: auto;
margin-top: -34px;
margin-right: 10px;
}
`;
const InfoPanel = ({ children, isVisible, setIsVisible }) => {
if (!isVisible) return null;
const closeInfoPanel = () => setIsVisible(false);
useEffect(() => {
const onMouseDown = (e) => {
if (e.target.id === "InfoPanelWrapper") closeInfoPanel();
};
if (isTablet()) document.addEventListener("mousedown", onMouseDown);
return () => document.removeEventListener("mousedown", onMouseDown);
}, []);
return (
<StyledInfoPanelWrapper className="info-panel" id="InfoPanelWrapper">
<StyledInfoPanel>
<StyledCloseButtonWrapper>
<IconButton
onClick={closeInfoPanel}
iconName="/static/images/cross.react.svg"
className="info-panel-button"
/>
</StyledCloseButtonWrapper>
{children}
</StyledInfoPanel>
</StyledInfoPanelWrapper>
);
};
InfoPanel.propTypes = {
children: PropTypes.oneOfType([
PropTypes.arrayOf(PropTypes.node),
PropTypes.node,
PropTypes.any,
]),
isVisible: PropTypes.bool,
};
StyledInfoPanelWrapper.defaultProps = { theme: Base };
StyledCloseButtonWrapper.defaultProps = { theme: Base };
StyledInfoPanel.defaultProps = { theme: Base };
InfoPanel.defaultProps = { theme: Base };
export default inject(({ infoPanelStore }) => {
let isVisible = false;
let setIsVisible = () => {};
if (infoPanelStore) {
isVisible = infoPanelStore.isVisible;
setIsVisible = infoPanelStore.setIsVisible;
}
return {
isVisible,
setIsVisible,
};
})(InfoPanel);

View File

@ -38,8 +38,10 @@ const StyledSectionContainer = styled.section`
display: flex;
flex-direction: column;
width: 100%;
max-width: 100vw;
width: ${(props) =>
props.infoPanelIsVisible ? "calc(100% - 677px)" : "100%"};
max-width: ${(props) =>
props.infoPanelIsVisible ? "calc(100vw - 677px)" : "100vw"};
@media ${tablet} {
width: 100%;

View File

@ -16,8 +16,16 @@ const StyledSectionHeader = styled.div`
margin-right: 20px;
${NoUserSelect}
width: calc(100vw - 296px);
max-width: calc(100vw - 296px);
display: grid;
align-items: center;
/* width: calc(100vw - 296px);
max-width: calc(100vw - 296px); */
width: ${(props) =>
props.infoPanelIsVisible ? "calc(100vw - 696px)" : "calc(100vw - 296px)"};
max-width: ${(props) =>
props.infoPanelIsVisible ? "calc(100vw - 696px)" : "calc(100vw - 296px)"};
@media ${tablet} {
width: ${(props) =>

View File

@ -11,7 +11,6 @@ export const EmployeeActivationStatus = Object.freeze({
Pending: 2,
AutoGenerated: 4,
});
/**
* Enum for employee status.
* @readonly
@ -20,7 +19,6 @@ export const EmployeeStatus = Object.freeze({
Active: 1,
Disabled: 2,
});
/**
* Enum for employee type.
* @readonly
@ -29,7 +27,6 @@ export const EmployeeType = Object.freeze({
User: 1,
Guest: 2,
});
/**
* Enum for filter type.
* @readonly
@ -48,7 +45,6 @@ export const FilterType = Object.freeze({
ByExtension: 11,
MediaOnly: 12,
});
/**
* Enum for file type.
* @readonly
@ -63,7 +59,6 @@ export const FileType = Object.freeze({
Presentation: 6,
Document: 7,
});
/**
* Enum for file action.
* @readonly
@ -72,7 +67,6 @@ export const FileAction = Object.freeze({
Create: 0,
Rename: 1,
});
/**
* Enum for root folders type.
* @readonly
@ -90,7 +84,6 @@ export const FolderType = Object.freeze({
Templates: 12,
Privacy: 13,
});
export const ShareAccessRights = Object.freeze({
None: 0,
FullAccess: 1,
@ -102,7 +95,6 @@ export const ShareAccessRights = Object.freeze({
FormFilling: 7,
CustomFilter: 8,
});
export const ConflictResolveType = Object.freeze({
Skip: 0,
Overwrite: 1,
@ -127,7 +119,6 @@ export const providersData = Object.freeze({
icon: "/static/images/share.linkedin.react.svg",
},
});
export const LoaderStyle = {
title: "",
width: "100%",
@ -183,7 +174,6 @@ export const TenantTrustedDomainsType = Object.freeze({
Custom: 1,
All: 2,
});
export const PasswordLimitSpecialCharacters = "!@#$%^&*";
/**

View File

@ -7,11 +7,9 @@ html,
body {
height: 100%;
}
#root {
min-height: 100%;
position: relative;
.pageLoader {
position: fixed;
left: calc(50% - 20px);
@ -40,17 +38,14 @@ body {
body {
margin: 0;
}
body.loading * {
cursor: wait !important;
}
body.drag-cursor * {
cursor: url('data:image/svg+xml;utf8,<svg width="20" height="23" viewBox="0 0 20 23" fill="none" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"><rect width="20" height="23" fill="url(%23pattern0)"/><defs><pattern id="pattern0" patternContentUnits="objectBoundingBox" width="1" height="1"><use xlink:href="%23image0" transform="scale(0.05 0.0434783)"/></pattern><image id="image0" width="20" height="23" xlink:href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAABQAAAAXCAYAAAALHW+jAAABS2lUWHRYTUw6Y29tLmFkb2JlLnhtcAAAAAAAPD94cGFja2V0IGJlZ2luPSLvu78iIGlkPSJXNU0wTXBDZWhpSHpyZVN6TlRjemtjOWQiPz4KPHg6eG1wbWV0YSB4bWxuczp4PSJhZG9iZTpuczptZXRhLyIgeDp4bXB0az0iQWRvYmUgWE1QIENvcmUgNS42LWMxMzggNzkuMTU5ODI0LCAyMDE2LzA5LzE0LTAxOjA5OjAxICAgICAgICAiPgogPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4KICA8cmRmOkRlc2NyaXB0aW9uIHJkZjphYm91dD0iIi8+CiA8L3JkZjpSREY+CjwveDp4bXBtZXRhPgo8P3hwYWNrZXQgZW5kPSJyIj8+IEmuOgAAAzhJREFUOI2VlT1PI1cUhp/58DgZbMAOi2AcQZMFCSHREK3EKsoWNDRYCIkum7+Qf0BP4Y4K8QfSEtEgpUoatLuRiyDxUcSI2JKF8JCZAeyZe08Kj5GxTNgc6ejcO3Puc99zr86M4XkeqRmA05u4rvvGsqxfAEZGRt7Ytl3XWsv09JSampqODMNARNjb26PfjFKpBICIvLJt+1OhUMg+PDzI0tKSu7+/nxMR1tfXI9/3FcDd3d3p2dnZW0AAnUbpAc0+uOV53tfHx8evKpXK5P39fS6Xy5HP54njeOTg4GD06OhoNAzDb7LZ7DiQBay0MoYBtWVZHcdxGB8fJ45jAIIgwDRNSqUSExMTOI4zOjk5+cfY2NjrYVDTskyM7rSptRYA3/cJw/BxpyAIaLV8zs/PmZubsw4PD0srKys/z8/PfwtkUmEGgKm1RmtZAFZFuoqz2SzFYvERWCwWMc3uro7jsLi4yO7u7usgCH4F3CdKZ2dnv9vY2EjW1tZkZeWtxHEsnU5HfN8XERGttbRaLVFKSZIkcnNzIyIiFxcX4nleAswCXwFfACYLCwsiIhJFkbx7973c3t7K51i1WhXP80JgHigBecA2RaQDEIYhImAYTy7tWUvzjPQMM72Sba11W0ScKIqo1WokSYKI/BcLABHp5Rl9ji2iieMY13Upl8vYto3W+kVg9zKf5AmAnSQqD1AoFNjZ2QFAKfUiMJfLoZRyB5+blmX9Vq1WlVKK/+OXl5dxkiR/DQINIDMzM9M5OTl5UVXParUam5ubf56env4oIg9ACPjAnWlZlrRard8/fPj42eoymQyNRiMnIgpQQJJGbSqldBAEq+/f//D31dWVvASL45hms6kdx6mnkBhop1AxK5WKBpJ6vb66tbWVDIFI6iilCMOQcrlcv76+/qkPGKdjsbe3twG01rrRaNQz7XZ72Dl3B4ZBs9nE9303bYgeMCH9NtpRFGGapga+DMPoZnl52emDPGkbwzBQSiEi1ykg6VOnBxf0fgEuMJpG+5mLlhR0B/yTxpiBBdJXwn06t54Bkqp7SPMfW2ZQgQY6Kaw9WPKQ3F7Jj80/uKDX5OaQd89Bpc/5F/DGMQusl3V8AAAAAElFTkSuQmCC"/></defs></svg>')
6 6,
auto !important;
}
body.desktop {
user-select: none;
-moz-user-select: none;
@ -59,7 +54,6 @@ body.desktop {
-o-user-select: none;
mozuserselect: none;
}
#snackbar {
display: flex;
justify-content: center;

View File

@ -0,0 +1,3 @@
<svg width="17" height="15" viewBox="0 0 17 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.5 2.5H13.5C14.0523 2.5 14.5 2.94772 14.5 3.5V11.5C14.5 12.0523 14.0523 12.5 13.5 12.5H11.5V2.5ZM9 2.5H3.5C2.94772 2.5 2.5 2.94772 2.5 3.5V11.5C2.5 12.0523 2.94772 12.5 3.5 12.5H9V2.5ZM17 1.5C17 0.671573 16.3284 0 15.5 0H14.5H2.5H1.5C0.671573 0 0 0.671573 0 1.5V2.88462V12.1154V13.5C0 14.3284 0.671573 15 1.5 15H2.5H14.5H15.5C16.3284 15 17 14.3284 17 13.5V12.1154V2.88462V1.5Z" fill="#A3A9AE"/>
</svg>

After

Width:  |  Height:  |  Size: 550 B

View File

@ -37,10 +37,6 @@ export const StyledSubmenuItems = styled.div`
display: flex;
flex-direction: row;
gap: 4px;
padding: 0 20px;
@media ${tablet} {
padding: 0 16px;
}
overflow: hidden;
&::-webkit-scrollbar {

View File

@ -45,11 +45,19 @@ const StyledButton = styled(Button)`
padding-right: 8px;
}
.button-content {
@media ${tablet} {
flex-direction: column;
gap: 0px;
}
}
@media ${tablet} {
display: flex;
justify-content: center;
flex-direction: column;
height: 60px;
padding: 22px 12px 0 12px;
padding: 0px 12px;
.btnIcon {
padding: 0;
margin: 0 auto;
@ -57,7 +65,7 @@ const StyledButton = styled(Button)`
}
@media ${mobile} {
padding: 18px 16px 0 16px;
padding: 0 16px;
height: 50px;
font-size: 0;
line-height: 0;

View File

@ -7,7 +7,7 @@ import { isMobile } from "react-device-detect";
const StyledTableContainer = styled.div`
-moz-user-select: none;
width: calc(100% - 5px);
width: 100%;
max-width: 100%;
margin-top: -19px;
@ -79,7 +79,7 @@ const StyledTableGroupMenu = styled.div`
align-items: center;
width: 100%;
z-index: 199;
height: 52px;
height: 53px;
box-shadow: ${(props) => props.theme.tableContainer.groupMenu.boxShadow};
border-radius: 0px 0px 6px 6px;
margin: 0;
@ -125,6 +125,42 @@ const StyledTableGroupMenu = styled.div`
StyledTableGroupMenu.defaultProps = { theme: Base };
const StyledInfoPanelToggleWrapper = styled.div`
display: flex;
align-items: center;
align-self: center;
justify-content: center;
margin: 0 20px 0 auto;
height: 100%;
width: auto;
padding-left: 20px;
@media ${tablet} {
margin: 0 16px 0 auto;
}
.info-panel-toggle-bg {
height: 32px;
width: 32px;
display: flex;
align-items: center;
justify-content: center;
border-radius: 50%;
background-color: ${(props) =>
props.isInfoPanelVisible
? props.theme.infoPanel.sectionHeaderToggleBgActive
: props.theme.infoPanel.sectionHeaderToggleBg};
path {
fill: ${(props) =>
props.isInfoPanelVisible
? props.theme.infoPanel.sectionHeaderToggleIconActive
: props.theme.infoPanel.sectionHeaderToggleIcon};
}
}
`;
StyledInfoPanelToggleWrapper.defaultProps = { theme: Base };
const StyledTableHeader = styled.div`
position: fixed;
background: ${(props) => props.theme.tableContainer.header.background};
@ -302,6 +338,9 @@ const StyledScrollbar = styled(Scrollbar)`
.scroll-body {
display: flex;
}
.nav-thumb-vertical {
display: none !important;
}
.nav-thumb-horizontal {
${isMobile && "display: none !important"};
}
@ -318,6 +357,7 @@ export {
StyledTableCell,
StyledTableSettings,
StyledTableGroupMenu,
StyledInfoPanelToggleWrapper,
StyledEmptyTableContainer,
StyledScrollbar,
};

View File

@ -1,10 +1,15 @@
import React from "react";
import PropTypes from "prop-types";
import Checkbox from "../checkbox";
import { StyledTableGroupMenu, StyledScrollbar } from "./StyledTableContainer";
import {
StyledTableGroupMenu,
StyledScrollbar,
StyledInfoPanelToggleWrapper,
} from "./StyledTableContainer";
import ComboBox from "../combobox";
import GroupMenuItem from "./GroupMenuItem";
import { useTranslation } from "react-i18next";
import IconButton from "../icon-button";
const TableGroupMenu = (props) => {
const {
@ -14,15 +19,14 @@ const TableGroupMenu = (props) => {
onChange,
checkboxOptions,
checkboxMargin,
isInfoPanelVisible,
toggleInfoPanel,
...rest
} = props;
const onCheckboxChange = (e) => {
onChange && onChange(e.target && e.target.checked);
};
const { t } = useTranslation("Common");
return (
<>
<StyledTableGroupMenu
@ -54,11 +58,21 @@ const TableGroupMenu = (props) => {
<GroupMenuItem key={index} item={item} />
))}
</StyledScrollbar>
<StyledInfoPanelToggleWrapper isInfoPanelVisible={isInfoPanelVisible}>
<div className="info-panel-toggle-bg">
<IconButton
className="info-panel-toggle"
iconName="images/panel.svg"
size="16"
isFill={true}
onClick={toggleInfoPanel}
/>
</div>
</StyledInfoPanelToggleWrapper>
</StyledTableGroupMenu>
</>
);
};
TableGroupMenu.propTypes = {
isChecked: PropTypes.bool,
isIndeterminate: PropTypes.bool,
@ -68,5 +82,4 @@ TableGroupMenu.propTypes = {
onChange: PropTypes.func,
checkboxMargin: PropTypes.string,
};
export default TableGroupMenu;

View File

@ -117,7 +117,7 @@ class TableHeader extends React.Component {
const column2Width = this.getSubstring(widths[colIndex]);
const defaultColumn = document.getElementById("column_" + colIndex);
if (defaultColumn.dataset.defaultSize) return;
if (!defaultColumn || defaultColumn.dataset.defaultSize) return;
if (column2Width + offset >= defaultMinColumnSize) {
widths[+columnIndex] = newWidth + "px";
@ -236,8 +236,8 @@ class TableHeader extends React.Component {
const storageSize =
!resetColumnsSize && localStorage.getItem(columnStorageName);
const defaultSize = this.props.columns.find((col) => col.defaultSize)
?.defaultSize;
const defaultSize =
this.props.columns.find((col) => col.defaultSize)?.defaultSize || 0;
//TODO: Fixed columns size if something went wrong
if (storageSize) {
@ -262,9 +262,12 @@ class TableHeader extends React.Component {
const containerWidth = +container.clientWidth;
const oldWidth = tableContainer
.map((column) => this.getSubstring(column))
.reduce((x, y) => x + y);
const oldWidth =
tableContainer
.map((column) => this.getSubstring(column))
.reduce((x, y) => x + y) -
defaultSize -
settingsSize;
let str = "";
@ -278,7 +281,7 @@ class TableHeader extends React.Component {
const enable =
index == tableContainer.length - 1 ||
(column ? column.dataset.enable === "true" : item !== "0px");
const defaultSize = column && column.dataset.defaultSize;
const defaultColumnSize = column && column.dataset.defaultSize;
const isActiveNow = item === "0px" && enable;
if (isActiveNow && column) activeColumnIndex = index;
@ -301,18 +304,14 @@ class TableHeader extends React.Component {
} else if (item !== `${settingsSize}px`) {
const percent = (this.getSubstring(item) / oldWidth) * 100;
if (index == 1) {
const newItemWidth = (containerWidth * percent) / 100 + "px";
gridTemplateColumns.push(newItemWidth);
} else {
const newItemWidth = defaultSize
? `${defaultSize}px`
: percent === 0
? `${minColumnSize}px`
: (containerWidth * percent) / 100 + "px";
const newItemWidth = defaultColumnSize
? `${defaultColumnSize}px`
: percent === 0
? `${minColumnSize}px`
: ((containerWidth - defaultSize - settingsSize) * percent) / 100 +
"px";
gridTemplateColumns.push(newItemWidth);
}
gridTemplateColumns.push(newItemWidth);
} else {
gridTemplateColumns.push(item);
}
@ -351,7 +350,8 @@ class TableHeader extends React.Component {
const enableColumns = this.props.columns
.filter((x) => x.enable)
.filter((x) => !x.defaultSize);
.filter((x) => !x.defaultSize)
.filter((x) => !x.default);
const container = containerRef.current
? containerRef.current

View File

@ -1952,6 +1952,29 @@ const Base = {
},
},
infoPanel: {
sectionHeaderToggleIcon: gray,
sectionHeaderToggleIconActive: "#3B72A7",
sectionHeaderToggleBg: "transparent",
sectionHeaderToggleBgActive: grayLight,
backgroundColor: white,
borderColor: grayLightMid,
thumbnailBorderColor: grayLightMid,
textColor: black,
closeButtonWrapperPadding: "0px",
closeButtonIcon: white,
closeButtonSize: "17px",
closeButtonBg: "transparent",
accessGroupBg: grayLightMid,
accessGroupText: black,
showAccessUsersTextColor: gray,
showAccessPanelTextColor: "#3b72a7",
},
filesArticleBody: {
background: lightGrayishStrongBlue,
panelBackground: lightGrayishStrongBlue,

View File

@ -1954,6 +1954,29 @@ const Dark = {
},
},
infoPanel: {
sectionHeaderToggleIcon: "#858585",
sectionHeaderToggleIconActive: "#c4c4c4",
sectionHeaderToggleBg: "transparent",
sectionHeaderToggleBgActive: "#292929",
backgroundColor: black,
borderColor: "#292929",
thumbnailBorderColor: grayLightMid,
textColor: white,
closeButtonWrapperPadding: "6px",
closeButtonIcon: black,
closeButtonSize: "12px",
closeButtonBg: "#a2a2a2",
accessGroupBg: "#242424",
accessGroupText: white,
showAccessUsersTextColor: gray,
showAccessPanelTextColor: "#E06A1B",
},
filesArticleBody: {
background: black,
panelBackground: "#474747",

Binary file not shown.

After

Width:  |  Height:  |  Size: 11 KiB

View File

@ -0,0 +1,3 @@
<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8 15C11.866 15 15 11.866 15 8C15 4.13401 11.866 1 8 1C4.13401 1 1 4.13401 1 8C1 11.866 4.13401 15 8 15ZM7 6V4H9V6H7ZM7 12V7H9V12H7Z" fill="black"/>
</svg>

After

Width:  |  Height:  |  Size: 301 B

View File

@ -0,0 +1,3 @@
<svg width="17" height="15" viewBox="0 0 17 15" fill="none" xmlns="http://www.w3.org/2000/svg">
<path fill-rule="evenodd" clip-rule="evenodd" d="M11.5 2.5H13.5C14.0523 2.5 14.5 2.94772 14.5 3.5V11.5C14.5 12.0523 14.0523 12.5 13.5 12.5H11.5V2.5ZM9 2.5H3.5C2.94772 2.5 2.5 2.94772 2.5 3.5V11.5C2.5 12.0523 2.94772 12.5 3.5 12.5H9V2.5ZM17 1.5C17 0.671573 16.3284 0 15.5 0H14.5H2.5H1.5C0.671573 0 0 0.671573 0 1.5V2.88462V12.1154V13.5C0 14.3284 0.671573 15 1.5 15H2.5H14.5H15.5C16.3284 15 17 14.3284 17 13.5V12.1154V2.88462V1.5Z" fill="#A3A9AE"/>
</svg>

After

Width:  |  Height:  |  Size: 550 B

View File

@ -0,0 +1,15 @@
{
"Info": "Info",
"ViewDetails": "View Details",
"ItemsSelected": "Items selected",
"SystemProperties": "System properties",
"WhoHasAccess": "Who has access",
"Members": "members",
"OpenSharingSettings": "Open sharing settings",
"Location": "Location",
"FileExtension": "File extension",
"LastModifiedBy": "Last modified by",
"Versions": "Versions",
"Comments": "Comments"
}

View File

@ -0,0 +1,15 @@
{
"Info": "Информация",
"ViewDetails": "Просмотреть подробную информацию",
"ItemsSelected": "Выбрано элементов",
"SystemProperties": "Системные свойства",
"WhoHasAccess": "У кого есть доступ",
"Members": "участников",
"OpenSharingSettings": "Открыть настройки общего доступа",
"Location": "Местоположение",
"FileExtension": "Расширение файла",
"LastModifiedBy": "Автор последнего корректива",
"Versions": "Версии",
"Comments": "Комментарии"
}

View File

@ -82,6 +82,7 @@ export default function withFileActions(WrappedFileItem) {
if (mouseButton || e.currentTarget.tagName !== "DIV" || label) {
return e;
}
e.preventDefault();
setTooltipPosition(e.pageX, e.pageY);
setStartDrag(true);
@ -92,13 +93,14 @@ export default function withFileActions(WrappedFileItem) {
const { viewAs } = this.props;
if (
e.target.closest(".checkbox") ||
e.target.tagName === "INPUT" ||
e.target.tagName === "SPAN" ||
e.target.tagName === "A" ||
e.target.closest(".expandButton") ||
e.target.closest(".badges") ||
e.target.closest(".checkbox") ||
e.button !== 0 ||
e.target.closest(".expandButton") ||
e.target.querySelector(".expandButton") ||
e.target.closest(".badges") ||
e.target.closest(".not-selectable")
)
return;
@ -106,12 +108,10 @@ export default function withFileActions(WrappedFileItem) {
if (viewAs === "tile") {
if (e.target.closest(".edit-button") || e.target.tagName === "IMG")
return;
if (e.detail === 1) this.fileContextClick();
} else {
this.fileContextClick();
}
} else this.fileContextClick();
};
onFilesClick = (e) => {
const { item, openFileAction } = this.props;
if (
@ -247,6 +247,7 @@ export default function withFileActions(WrappedFileItem) {
activeFiles,
activeFolders,
} = filesStore;
const { startUpload } = uploadDataStore;
const { type, extension, id } = fileActionStore;

View File

@ -0,0 +1,32 @@
import Text from "@appserver/components/text";
import React from "react";
import { withTranslation } from "react-i18next";
import { ReactSVG } from "react-svg";
import { StyledTitle } from "./styles/styles.js";
const SeveralItems = (props) => {
const { t, selectedItems, getIcon, getFolderInfo } = props;
const itemsIcon = getIcon(24, ".file");
return (
<>
<StyledTitle>
<ReactSVG className="icon" src={itemsIcon} />
<Text className="text" fontWeight={600} fontSize="16px">
{`${t("ItemsSelected")}: ${selectedItems.length}`}
</Text>
</StyledTitle>
<div className="no-thumbnail-img-wrapper">
<img
size="96px"
className="no-thumbnail-img"
src="images/empty_screen.png"
/>
</div>
</>
);
};
export default withTranslation(["InfoPanel"])(SeveralItems);

View File

@ -0,0 +1,415 @@
import { FileType } from "@appserver/common/constants";
import { LANGUAGE } from "@appserver/common/constants";
import Link from "@appserver/components/link";
import Text from "@appserver/components/text";
import Tooltip from "@appserver/components/tooltip";
import React, { useEffect, useState } from "react";
import { ReactSVG } from "react-svg";
import {
StyledAccess,
StyledAccessItem,
StyledOpenSharingPanel,
StyledProperties,
StyledSubtitle,
StyledThumbnail,
StyledTitle,
} from "./styles/styles.js";
const moment = require("moment");
const SingleItem = (props) => {
const {
t,
selectedItem,
onSelectItem,
setSharingPanelVisible,
getFolderInfo,
getIcon,
getFolderIcon,
getShareUsers,
dontShowSize,
dontShowLocation,
dontShowAccess,
} = props;
let updateSubscription = true;
const [item, setItem] = useState({
id: "",
isFolder: false,
title: "",
iconUrl: "",
thumbnailUrl: "",
properties: [],
access: {
owner: {
img: "",
link: "",
},
others: [],
},
});
const updateItemsInfo = async (selectedItem) => {
const getItemIcon = (item, size) => {
return item.isFolder
? getFolderIcon(item.providerKey, size)
: getIcon(size, item.fileExst || ".file");
};
const getSingleItemProperties = (item) => {
const styledLink = (text, href) => (
<Link className="property-content" href={href} isHovered={true}>
{text}
</Link>
);
const styledText = (text) => (
<Text className="property-content">{text}</Text>
);
const parseAndFormatDate = (date) => {
return moment(date)
.locale(localStorage.getItem(LANGUAGE))
.format("DD.MM.YY hh:mm A");
};
const getItemType = (fileType) => {
switch (fileType) {
case FileType.Unknown:
return t("Common:Unknown");
case FileType.Archive:
return t("Common:Archive");
case FileType.Video:
return t("Common:Video");
case FileType.Audio:
return t("Common:Audio");
case FileType.Image:
return t("Common:Image");
case FileType.Spreadsheet:
return t("Home:Spreadsheet");
case FileType.Presentation:
return t("Home:Presentation");
case FileType.Document:
return t("Home:Document");
default:
return t("Home:Folder");
}
};
const itemSize = item.isFolder
? `${t("Translations:Folders")}: ${item.foldersCount} | ${t(
"Translations:Files"
)}: ${item.filesCount}`
: item.contentLength;
const itemType = getItemType(item.fileType);
let result = [
{
id: "Owner",
title: t("Common:Owner"),
content: styledLink(
item.createdBy?.displayName,
item.createdBy?.profileUrl
),
},
// {
// id: "Location",
// title: t("InfoPanel:Location"),
// content: styledText("..."),
// },
{
id: "Type",
title: t("Common:Type"),
content: styledText(itemType),
},
{
id: "Size",
title: t("Common:Size"),
content: styledText(itemSize),
},
{
id: "ByLastModifiedDate",
title: t("Home:ByLastModifiedDate"),
content: styledText(parseAndFormatDate(item.updated)),
},
{
id: "LastModifiedBy",
title: t("LastModifiedBy"),
content: styledLink(
item.updatedBy?.displayName,
item.updatedBy?.profileUrl
),
},
{
id: "ByCreationDate",
title: t("Home:ByCreationDate"),
content: styledText(parseAndFormatDate(item.created)),
},
];
if (item.isFolder) return result;
result.splice(3, 0, {
id: "FileExtension",
title: t("FileExtension"),
content: styledText(
item.fileExst ? item.fileExst.split(".")[1].toUpperCase() : "-"
),
});
result.push(
{
id: "Versions",
title: t("Versions"),
content: styledText(item.version),
},
{
id: "Comments",
title: t("Comments"),
content: styledText(item.comment),
}
);
return result;
};
const displayedItem = {
id: selectedItem.id,
isFolder: selectedItem.isFolder,
title: selectedItem.title,
iconUrl: getItemIcon(selectedItem, 32),
thumbnailUrl: selectedItem.thumbnailUrl || getItemIcon(selectedItem, 96),
properties: getSingleItemProperties(selectedItem),
access: {
owner: {
img: selectedItem.createdBy?.avatarSmall,
link: selectedItem.createdBy?.profileUrl,
},
others: [],
},
};
setItem(displayedItem);
await loadAsyncData(displayedItem, selectedItem);
};
const loadAsyncData = async (displayedItem, selectedItem) => {
if (!updateSubscription) return;
const updateLoadedItemProperties = async (displayedItem, selectedItem) => {
const parentFolderId = selectedItem.isFolder
? selectedItem.parentId
: selectedItem.folderId;
const noLocationProperties = [...displayedItem.properties].filter(
(dip) => dip.id !== "Location"
);
let result;
await getFolderInfo(parentFolderId)
.catch(() => {
result = noLocationProperties;
})
.then((data) => {
if (!data) {
result = noLocationProperties;
return;
}
result = [...displayedItem.properties].map((dip) =>
dip.id === "Location"
? {
id: "Location",
title: t("Location"),
content: (
<Link
className="property-content"
href={`/products/files/filter?folder=${parentFolderId}`}
isHovered={true}
>
{data.title}
</Link>
),
}
: dip
);
});
return result;
};
const updateLoadedItemAccess = async (selectedItem) => {
const accesses = await getShareUsers(
[selectedItem.isFolder ? selectedItem.parentId : selectedItem.folderId],
[selectedItem.id]
);
const result = {
owner: {},
others: [],
};
accesses.forEach((access) => {
let key = access.sharedTo.id,
img = access.sharedTo.avatarSmall,
link = access.sharedTo.profileUrl,
name = access.sharedTo.displayName || access.sharedTo.name,
{ manager } = access.sharedTo;
if (access.isOwner) result.owner = { key, img, link, name };
else {
if (access.sharedTo.email)
result.others.push({ key, type: "user", img, link, name });
else if (access.sharedTo.manager)
result.others.push({ key, type: "group", name, manager });
}
});
result.others = result.others.sort((a) => (a.type === "group" ? -1 : 1));
return result;
};
// const properties = await updateLoadedItemProperties(
// displayedItem,
// selectedItem
// );
if (dontShowAccess) {
setItem({
...displayedItem,
//properties: properties,
});
return;
}
const access = await updateLoadedItemAccess(selectedItem);
setItem({
...displayedItem,
// properties: properties,
access: access,
});
};
const openSharingPanel = () => {
const { id, isFolder } = item;
onSelectItem({ id, isFolder });
setSharingPanelVisible(true);
};
useEffect(() => {
if (selectedItem.id !== item.id && updateSubscription)
updateItemsInfo(selectedItem);
return () => (updateSubscription = false);
}, [selectedItem]);
return (
<>
<StyledTitle>
<ReactSVG className="icon" src={item.iconUrl} />
<Text className="text">{item.title}</Text>
</StyledTitle>
{selectedItem.thumbnailUrl ? (
<StyledThumbnail>
<img src={item.thumbnailUrl} alt="" />
</StyledThumbnail>
) : (
<div className="no-thumbnail-img-wrapper">
<ReactSVG className="no-thumbnail-img" src={item.thumbnailUrl} />
</div>
)}
<StyledSubtitle>
<Text fontWeight="600" fontSize="14px">
{t("SystemProperties")}
</Text>
</StyledSubtitle>
<StyledProperties>
{item.properties.map((p) => {
if (dontShowSize && p.id === "Size") return;
if (dontShowLocation && p.id === "Location") return;
return (
<div key={p.title} className="property">
<Text className="property-title">{p.title}</Text>
{p.content}
</div>
);
})}
</StyledProperties>
{!dontShowAccess && item.access && (
<>
<StyledSubtitle>
<Text fontWeight="600" fontSize="14px">
{t("WhoHasAccess")}
</Text>
</StyledSubtitle>
<StyledAccess>
<Tooltip
id="access-item-tooltip"
getContent={(dataTip) =>
dataTip ? <Text fontSize="13px">{dataTip}</Text> : null
}
/>
<StyledAccessItem>
<div
data-for="access-item-tooltip"
className="access-item-tooltip"
data-tip={item.access.owner.name}
>
<div className="item-user">
<a href={item.access.owner.link}>
<img src={item.access.owner.img} />
</a>
</div>
</div>
</StyledAccessItem>
{item.access.others.length > 0 && <div className="divider"></div>}
{item.access.others.map((item, i) => {
if (i < 3)
return (
<div key={item.key}>
<StyledAccessItem>
<div
data-for="access-item-tooltip"
data-tip={item.name}
className="access-item-tooltip"
>
{item.type === "user" ? (
<div className="item-user">
<a href={item.link}>
<img src={item.img} />
</a>
</div>
) : (
<div className="item-group">
<span>{item.name.substr(0, 2).toUpperCase()}</span>
</div>
)}
</div>
</StyledAccessItem>
</div>
);
})}
{item.access.others.length > 3 && (
<div className="show-more-users" onClick={openSharingPanel}>
{`+ ${item.access.others.length - 3} ${t("Members")}`}
</div>
)}
</StyledAccess>
<StyledOpenSharingPanel onClick={openSharingPanel}>
{t("OpenSharingSettings")}
</StyledOpenSharingPanel>
</>
)}
</>
);
};
export default SingleItem;

View File

@ -0,0 +1,109 @@
import { inject, observer } from "mobx-react";
import React, { useEffect, useState } from "react";
import { withTranslation } from "react-i18next";
import { withRouter } from "react-router";
import SeveralItems from "./SeveralItems";
import SingleItem from "./SingleItem";
import { StyledInfoRoomBody } from "./styles/styles.js";
import { Base } from "@appserver/components/themes";
const InfoPanelBodyContent = ({
t,
selectedFolder,
selectedItems,
getFolderInfo,
getIcon,
getFolderIcon,
getShareUsers,
onSelectItem,
setSharingPanelVisible,
isRecycleBinFolder,
isRecentFolder,
isFavoritesFolder,
}) => {
const singleItem = (item) => {
const dontShowLocation = item.isFolder && item.parentId === 0;
const dontShowSize = item.isFolder && (isFavoritesFolder || isRecentFolder);
const dontShowAccess =
isRecycleBinFolder ||
(item.isFolder && item.parentId === 0) ||
item.rootFolderId === 7 ||
(item.isFolder && item.pathParts && item.pathParts[0] === 7);
return (
<SingleItem
t={t}
selectedItem={item}
onSelectItem={onSelectItem}
setSharingPanelVisible={setSharingPanelVisible}
getFolderInfo={getFolderInfo}
getIcon={getIcon}
getFolderIcon={getFolderIcon}
getShareUsers={getShareUsers}
dontShowLocation={dontShowLocation}
dontShowSize={dontShowSize}
dontShowAccess={dontShowAccess}
/>
);
};
return (
<StyledInfoRoomBody>
<>
{selectedItems.length === 0 ? (
singleItem({
...selectedFolder,
isFolder: true,
})
) : selectedItems.length === 1 ? (
singleItem(selectedItems[0])
) : (
<SeveralItems selectedItems={selectedItems} getIcon={getIcon} />
)}
</>
</StyledInfoRoomBody>
);
};
InfoPanelBodyContent.defaultProps = { theme: Base };
export default inject(
({
filesStore,
settingsStore,
filesActionsStore,
dialogsStore,
treeFoldersStore,
selectedFolderStore,
}) => {
const { selection, getFolderInfo, getShareUsers } = filesStore;
const { getIcon, getFolderIcon } = settingsStore;
const { onSelectItem } = filesActionsStore;
const { setSharingPanelVisible } = dialogsStore;
const {
isRecycleBinFolder,
isRecentFolder,
isFavoritesFolder,
} = treeFoldersStore;
return {
selectedFolder: { ...selectedFolderStore },
selectedItems: [...selection],
getFolderInfo,
getShareUsers,
getIcon,
getFolderIcon,
onSelectItem,
setSharingPanelVisible,
isRecycleBinFolder,
isRecentFolder,
isFavoritesFolder,
};
}
)(
withRouter(
withTranslation(["InfoPanel", "Home", "Common", "Translations"])(
observer(InfoPanelBodyContent)
)
)
);

View File

@ -0,0 +1,234 @@
import styled from "styled-components";
import { Base } from "@appserver/components/themes";
const StyledInfoRoomBody = styled.div`
padding: 0px 0px 0 16px;
height: auto;
background-color: ${(props) => props.theme.infoPanel.backgroundColor};
color: ${(props) => props.theme.infoPanel.textColor};
.no-item {
text-align: center;
}
.no-thumbnail-img-wrapper {
height: auto;
width: 100%;
display: flex;
justify-content: center;
.no-thumbnail-img {
height: 96px;
width: 96px;
}
}
.current-folder-loader-wrapper {
width: 100%;
display: flex;
justify-content: center;
height: 96px;
margin-top: 116.56px;
}
`;
const StyledTitle = styled.div`
display: flex;
flex-wrap: no-wrap;
flex-direction: row;
align-items: center;
width: 100%;
height: 44px;
padding: 23px 0;
.icon {
display: flex;
align-items: center;
svg {
height: 32px;
width: 32px;
}
}
.text {
font-weight: 600;
font-size: 16px;
line-height: 22px;
max-height: 44px;
margin: 0 8px;
overflow: hidden;
text-overflow: ellipsis;
display: -webkit-box;
-webkit-box-orient: vertical;
-webkit-line-clamp: 2;
}
`;
const StyledThumbnail = styled.div`
display: flex;
justify-content: center;
align-items: center;
width: 100%;
height: auto;
img {
border: ${(props) => `solid 1px ${props.theme.infoPanel.borderColor}`};
border-radius: 6px;
//width: 100%;
width: auto;
max-width: 100%;
height: auto;
}
`;
const StyledSubtitle = styled.div`
display: flex;
flex-direction: row;
align-items: center;
width: 100%;
padding: 24px 0;
`;
const StyledProperties = styled.div`
display: flex;
flex-direction: column;
width: 100%;
gap: 8px;
.property {
width: 100%;
display: grid;
grid-template-columns: 150px 1fr;
grid-column-gap: 24px;
.property-title {
font-size: 13px;
}
.property-content {
display: flex;
align-items: center;
font-weight: 600;
font-size: 13px;
}
}
`;
const StyledAccess = styled.div`
display: flex;
flex-wrap: wrap;
flex-direction: row;
gap: 8px;
align-items: center;
.divider {
background: ${(props) => props.theme.infoPanel.borderColor};
margin: 2px 4px;
width: 1px;
height: 28px;
}
.show-more-users {
position: static;
width: 101px;
height: 16px;
left: 120px;
top: 8px;
padding-left: 1px;
font-family: "Open Sans";
font-style: normal;
font-weight: normal;
font-size: 12px;
line-height: 16px;
text-align: left;
color: ${(props) => props.theme.infoPanel.showAccessUsersTextColor};
flex: none;
order: 3;
flex-grow: 0;
cursor: pointer;
&:hover {
text-decoration: underline;
}
}
`;
const StyledAccessItem = styled.div`
width: 32px;
height: 32px;
border-radius: 50%;
.access-item-tooltip {
cursor: pointer;
width: 100%;
height: 100%;
.item-group {
border-radius: 50%;
background-color: ${(props) => props.theme.infoPanel.accessGroupBg};
width: 100%;
height: 100%;
display: flex;
align-items: center;
justify-content: center;
span {
font-family: "Open Sans";
font-weight: 700;
font-size: 12px;
color: ${(props) => props.theme.infoPanel.accessGroupText};
line-height: 16px;
}
}
.item-user {
img {
border-radius: 50%;
width: 100%;
height: 100%;
}
}
}
`;
const StyledOpenSharingPanel = styled.div`
position: static;
width: auto;
height: 15px;
left: 0px;
top: 2px;
font-family: "Open Sans";
font-style: normal;
font-weight: 600;
font-size: 13px;
line-height: 15px;
color: ${(props) => props.theme.infoPanel.showAccessPanelTextColor};
display: flex;
margin: 16px 0px;
cursor: pointer;
text-decoration: underline;
text-decoration-style: dashed;
`;
StyledInfoRoomBody.defaultProps = { theme: Base };
StyledThumbnail.defaultProps = { theme: Base };
StyledAccess.defaultProps = { theme: Base };
StyledAccessItem.defaultProps = { theme: Base };
StyledOpenSharingPanel.defaultProps = { theme: Base };
export {
StyledInfoRoomBody,
StyledTitle,
StyledThumbnail,
StyledSubtitle,
StyledProperties,
StyledAccess,
StyledAccessItem,
StyledOpenSharingPanel,
};

View File

@ -0,0 +1,8 @@
import React from "react";
import { withTranslation } from "react-i18next";
const InfoPanelHeaderContent = ({ t }) => {
return <>{t("Info")}</>;
};
export default withTranslation(["InfoPanel"])(InfoPanelHeaderContent);

View File

@ -0,0 +1,2 @@
export { default as InfoPanelHeaderContent } from "./Header";
export { default as InfoPanelBodyContent } from "./Body";

View File

@ -97,8 +97,8 @@ const StyledSimpleFilesRow = styled(Row)`
}
.row_content {
${(props) => props.sectionWidth > 500 && `max-width: fit-content;`}
min-width: auto;
${(props) =>
props.sectionWidth > 500 && `max-width: fit-content;`}//min-width: auto;;
}
.badges {

View File

@ -146,15 +146,17 @@ const StyledFileTileTop = styled.div`
align-items: baseline;
height: 156px;
position: relative;
border-radius: 6px 6px 0 0;
.thumbnail-image {
pointer-events: none;
position: absolute;
height: 100%;
width: 100%;
object-fit: ${(props) => (props.isMedia ? "cover" : "none")};
object-fit: cover;
object-position: top;
z-index: 0;
border-radius: 6px 6px 0 0;
}
.temporary-icon > .injected-svg {

View File

@ -71,6 +71,7 @@ const SectionBodyContent = (props) => {
(e.target.closest(".scroll-body") &&
!e.target.closest(".files-item") &&
!e.target.closest(".not-selectable") &&
!e.target.closest(".info-panel") &&
!e.target.closest(".table-container_group-menu")) ||
e.target.closest(".files-main-button") ||
e.target.closest(".add-button") ||

View File

@ -18,7 +18,7 @@ import TableGroupMenu from "@appserver/components/table-container/TableGroupMenu
import Navigation from "@appserver/common/components/Navigation";
const StyledContainer = styled.div`
padding: 0 0 15px;
/* padding: 0 0 15px;
@media ${tablet} {
padding: 0 0 17px;
@ -36,7 +36,7 @@ const StyledContainer = styled.div`
${isMobileOnly &&
css`
padding: 0 0 13px;
`}
`} */
.table-container_group-menu {
${(props) =>
@ -317,6 +317,7 @@ class SectionHeaderContent extends React.Component {
const {
t,
tReady,
isInfoPanelVisible,
isRootFolder,
title,
canCreate,
@ -344,6 +345,8 @@ class SectionHeaderContent extends React.Component {
width={context.sectionWidth}
isRootFolder={isRootFolder}
canCreate={canCreate}
isRecycleBinFolder={isRecycleBinFolder}
title={title}
isTitle={title}
isDesktop={isDesktop}
isTabletView={isTabletView}
@ -357,6 +360,8 @@ class SectionHeaderContent extends React.Component {
isChecked={isHeaderChecked}
isIndeterminate={isHeaderIndeterminate}
headerMenu={headerMenu}
isInfoPanelVisible={this.props.isInfoPanelVisible}
toggleInfoPanel={this.props.toggleInfoPanel}
/>
) : (
<div className="header-container">
@ -383,6 +388,8 @@ class SectionHeaderContent extends React.Component {
isEmptyFilesList={isEmptyFilesList}
clearTrash={this.onEmptyTrashAction}
onBackToParentFolder={this.onBackToParentFolder}
toggleInfoPanel={this.props.toggleInfoPanel}
isInfoPanelVisible={this.props.isInfoPanelVisible}
/>
)}
</div>
@ -403,6 +410,7 @@ export default inject(
treeFoldersStore,
filesActionsStore,
settingsStore,
infoPanelStore,
}) => {
const {
setSelected,
@ -441,9 +449,10 @@ export default inject(
backToParentFolder,
} = filesActionsStore;
const { toggleIsVisible, isVisible } = infoPanelStore;
return {
showText: auth.settingsStore.showText,
isDesktop: auth.settingsStore.isDesktopClient,
isRootFolder: selectedFolderStore.parentId === 0,
title: selectedFolderStore.title,
@ -451,6 +460,8 @@ export default inject(
pathParts: selectedFolderStore.pathParts,
navigationPath: selectedFolderStore.navigationPath,
canCreate,
toggleInfoPanel: toggleIsVisible,
isInfoPanelVisible: isVisible,
isHeaderVisible,
isHeaderIndeterminate,
isHeaderChecked,

View File

@ -22,6 +22,7 @@ import {
SectionPagingContent,
Bar,
} from "./Section";
import { InfoPanelBodyContent, InfoPanelHeaderContent } from "./InfoPanel";
import { ArticleMainButtonContent } from "../../components/Article";
@ -287,6 +288,7 @@ class PureHome extends React.Component {
setMaintenanceExist,
snackbarExist,
} = this.props;
return (
<>
<MediaViewer />
@ -345,6 +347,14 @@ class PureHome extends React.Component {
</Consumer>
</Section.SectionBody>
<Section.InfoPanelHeader>
<InfoPanelHeaderContent />
</Section.InfoPanelHeader>
<Section.InfoPanelBody>
<InfoPanelBodyContent />
</Section.InfoPanelBody>
<Section.SectionPaging>
<SectionPagingContent tReady={tReady} />
</Section.SectionPaging>

View File

@ -22,6 +22,7 @@ class ContextOptionsStore {
uploadDataStore;
versionHistoryStore;
settingsStore;
infoPanelStore;
constructor(
authStore,
@ -32,7 +33,8 @@ class ContextOptionsStore {
treeFoldersStore,
uploadDataStore,
versionHistoryStore,
settingsStore
settingsStore,
infoPanelStore
) {
makeAutoObservable(this);
this.authStore = authStore;
@ -44,6 +46,7 @@ class ContextOptionsStore {
this.uploadDataStore = uploadDataStore;
this.versionHistoryStore = versionHistoryStore;
this.settingsStore = settingsStore;
this.infoPanelStore = infoPanelStore;
}
onOpenFolder = (item) => {
@ -335,6 +338,11 @@ class ContextOptionsStore {
return options;
};
onShowInfoPanel = () => {
const { setIsVisible } = this.infoPanelStore;
setIsVisible(true);
};
getFilesContextOptions = (item, t) => {
const { contextOptions } = item;
const isRootThirdPartyFolder =
@ -510,6 +518,13 @@ class ContextOptionsStore {
disabled: true,
},
...versionActions,
{
key: "show-info",
label: t("InfoPanel:ViewDetails"),
icon: "/static/images/info.outline.react.svg",
onClick: this.onShowInfoPanel,
disabled: false,
},
{
key: "block-unblock-version",
label: t("UnblockVersion"),

View File

@ -1,26 +1,26 @@
import { makeAutoObservable } from "mobx";
import {
removeFiles,
checkFileConflicts,
deleteFile,
deleteFolder,
finalizeVersion,
lockFile,
downloadFiles,
markAsRead,
checkFileConflicts,
removeShareFiles,
getSubfolders,
emptyTrash,
finalizeVersion,
getSubfolders,
lockFile,
markAsRead,
removeFiles,
removeShareFiles,
} from "@appserver/common/api/files";
import {
ConflictResolveType,
FileAction,
FileStatus,
} from "@appserver/common/constants";
import { makeAutoObservable } from "mobx";
import toastr from "studio/toastr";
import { TIMEOUT } from "../helpers/constants";
import { loopTreeFolders, checkProtocol } from "../helpers/files-helpers";
import toastr from "studio/toastr";
import { combineUrl } from "@appserver/common/utils";
import { AppServerConfig } from "@appserver/common/constants";
import config from "../../package.json";
@ -34,6 +34,7 @@ class FilesActionStore {
settingsStore;
dialogsStore;
mediaViewerDataStore;
infoPanelStore;
constructor(
authStore,
@ -43,7 +44,8 @@ class FilesActionStore {
selectedFolderStore,
settingsStore,
dialogsStore,
mediaViewerDataStore
mediaViewerDataStore,
infoPanelStore
) {
makeAutoObservable(this);
this.authStore = authStore;
@ -53,6 +55,7 @@ class FilesActionStore {
this.selectedFolderStore = selectedFolderStore;
this.settingsStore = settingsStore;
this.dialogsStore = dialogsStore;
this.infoPanelStore = infoPanelStore;
this.mediaViewerDataStore = mediaViewerDataStore;
}
@ -786,6 +789,7 @@ class FilesActionStore {
switch (option) {
case "share":
return isAccessedSelected && !personal; //isFavoritesFolder ||isRecentFolder
case "showInfo":
case "copy":
case "download":
return hasSelection;
@ -831,6 +835,8 @@ class FilesActionStore {
setDeleteDialogVisible,
} = this.dialogsStore;
const { toggleIsVisible } = this.infoPanelStore;
switch (option) {
case "share":
if (!this.isAvailableOption("share")) return null;
@ -915,6 +921,7 @@ class FilesActionStore {
const moveTo = this.getOption("moveTo", t);
const copy = this.getOption("copy", t);
const deleteOption = this.getOption("delete", t);
const showInfo = this.getOption("showInfo", t);
itemsCollection
.set("share", share)
@ -922,7 +929,8 @@ class FilesActionStore {
.set("downloadAs", downloadAs)
.set("moveTo", moveTo)
.set("copy", copy)
.set("delete", deleteOption);
.set("delete", deleteOption)
.set("showInfo", showInfo);
return this.convertToArray(itemsCollection);
};
@ -932,12 +940,15 @@ class FilesActionStore {
const download = this.getOption("download", t);
const downloadAs = this.getOption("downloadAs", t);
const copy = this.getOption("copy", t);
const showInfo = this.getOption("showInfo", t);
itemsCollection
.set("share", share)
.set("download", download)
.set("downloadAs", downloadAs)
.set("copy", copy);
.set("copy", copy)
.set("showInfo", showInfo);
return this.convertToArray(itemsCollection);
};
@ -948,6 +959,7 @@ class FilesActionStore {
const download = this.getOption("download", t);
const downloadAs = this.getOption("downloadAs", t);
const copy = this.getOption("copy", t);
const showInfo = this.getOption("showInfo", t);
itemsCollection
.set("share", share)
@ -960,7 +972,9 @@ class FilesActionStore {
setUnsubscribe(true);
setDeleteDialogVisible(true);
},
});
})
.set("showInfo", showInfo);
return this.convertToArray(itemsCollection);
};
@ -968,12 +982,15 @@ class FilesActionStore {
const moveTo = this.getOption("moveTo", t);
const deleteOption = this.getOption("delete", t);
const download = this.getOption("download", t);
const showInfo = this.getOption("showInfo", t);
itemsCollection
.set("download", download)
.set("moveTo", moveTo)
.set("delete", deleteOption);
.set("delete", deleteOption)
.set("showInfo", showInfo);
return this.convertToArray(itemsCollection);
};
@ -984,6 +1001,7 @@ class FilesActionStore {
const download = this.getOption("download", t);
const downloadAs = this.getOption("downloadAs", t);
const copy = this.getOption("copy", t);
const showInfo = this.getOption("showInfo", t);
itemsCollection
.set("share", share)
@ -999,7 +1017,9 @@ class FilesActionStore {
.then(() => toastr.success(t("RemovedFromFavorites")))
.catch((err) => toastr.error(err));
},
});
})
.set("showInfo", showInfo);
return this.convertToArray(itemsCollection);
};
@ -1012,6 +1032,7 @@ class FilesActionStore {
const download = this.getOption("download", t);
const downloadAs = this.getOption("downloadAs", t);
const deleteOption = this.getOption("delete", t);
const showInfo = this.getOption("showInfo", t);
itemsCollection
.set("download", download)
@ -1021,9 +1042,12 @@ class FilesActionStore {
onClick: () => setMoveToPanelVisible(true),
iconUrl: "/static/images/move.react.svg",
})
.set("delete", deleteOption);
.set("delete", deleteOption)
.set("showInfo", showInfo);
return this.convertToArray(itemsCollection);
};
getHeaderMenu = (t) => {
const {
isFavoritesFolder,

View File

@ -1,23 +1,24 @@
import { makeAutoObservable, runInAction } from "mobx";
import api from "@appserver/common/api";
import {
FolderType,
FilterType,
FileType,
FileAction,
AppServerConfig,
FileAction,
FileType,
FilterType,
FolderType,
FileStatus,
} from "@appserver/common/constants";
import history from "@appserver/common/history";
import { loopTreeFolders } from "../helpers/files-helpers";
import config from "../../package.json";
import { combineUrl } from "@appserver/common/utils";
import { updateTempContent } from "@appserver/common/utils";
import { thumbnailStatuses } from "../helpers/constants";
import { isMobile } from "react-device-detect";
import { openDocEditor as openEditor } from "../helpers/utils";
import toastr from "studio/toastr";
import config from "../../package.json";
import { thumbnailStatuses } from "../helpers/constants";
import { loopTreeFolders } from "../helpers/files-helpers";
import { openDocEditor as openEditor } from "../helpers/utils";
const { FilesFilter } = api;
const storageViewAs = localStorage.getItem("viewAs");
@ -717,6 +718,7 @@ class FilesStore {
"version", //category
"finalize-version",
"show-version-history",
"show-info",
"block-unblock-version", //need split
"separator1",
"open-location",
@ -1004,6 +1006,7 @@ class FilesStore {
"separator0",
"sharing-settings",
"owner-change",
"show-info",
"link-for-portal-users",
"separator1",
"open-location",

View File

@ -0,0 +1,27 @@
import { makeAutoObservable } from "mobx";
class InfoPanelStore {
isVisible = false;
constructor() {
makeAutoObservable(this);
}
onHeaderCrossClick = () => {
this.isVisible = false;
};
toggleIsVisible = () => {
this.isVisible = !this.isVisible;
};
setVisible = () => {
this.isVisible = true;
};
setIsVisible = (bool) => {
this.isVisible = bool;
};
}
export default InfoPanelStore;

View File

@ -1,11 +1,11 @@
import { makeAutoObservable } from "mobx";
import api from "@appserver/common/api";
import axios from "axios";
import {
setFavoritesSetting,
setRecentSetting,
} from "@appserver/common/api/files";
import { FolderType } from "@appserver/common/constants";
import axios from "axios";
import { makeAutoObservable } from "mobx";
import { presentInArray } from "../helpers/files-helpers";
class SettingsStore {
@ -62,6 +62,10 @@ class SettingsStore {
this.treeFoldersStore = treeFoldersStore;
}
get infoPanelIsVisible() {
return this.infoPanelIsVisible;
}
setIsLoaded = (isLoaded) => {
this.settingsIsLoaded = isLoaded;
};

View File

@ -16,13 +16,12 @@ import selectedFilesStore from "./SelectedFilesStore";
import ContextOptionsStore from "./ContextOptionsStore";
import HotkeyStore from "./HotkeyStore";
import store from "studio/store";
import InfoPanelStore from "./InfoPanelStore";
const selectedFolderStore = new SelectedFolderStore(store.auth.settingsStore);
const treeFoldersStore = new TreeFoldersStore(selectedFolderStore);
const settingsStore = new SettingsStore(thirdPartyStore, treeFoldersStore);
const filesStore = new FilesStore(
store.auth,
store.auth.settingsStore,
@ -37,10 +36,8 @@ const mediaViewerDataStore = new MediaViewerDataStore(
filesStore,
settingsStore
);
const secondaryProgressDataStore = new SecondaryProgressDataStore();
const primaryProgressDataStore = new PrimaryProgressDataStore();
const dialogsStore = new DialogsStore(
store.auth,
treeFoldersStore,
@ -57,6 +54,8 @@ const uploadDataStore = new UploadDataStore(
settingsStore
);
const infoPanelStore = new InfoPanelStore();
const filesActionsStore = new FilesActionsStore(
store.auth,
uploadDataStore,
@ -65,11 +64,11 @@ const filesActionsStore = new FilesActionsStore(
selectedFolderStore,
settingsStore,
dialogsStore,
mediaViewerDataStore
mediaViewerDataStore,
infoPanelStore
);
const versionHistoryStore = new VersionHistoryStore(filesStore);
const contextOptionsStore = new ContextOptionsStore(
store.auth,
dialogsStore,
@ -79,7 +78,8 @@ const contextOptionsStore = new ContextOptionsStore(
treeFoldersStore,
uploadDataStore,
versionHistoryStore,
settingsStore
settingsStore,
infoPanelStore
);
const hotkeyStore = new HotkeyStore(
@ -105,6 +105,7 @@ const stores = {
selectedFilesStore,
contextOptionsStore,
hotkeyStore,
infoPanelStore,
};
export default stores;

View File

@ -0,0 +1,12 @@
<svg width="17" height="16" viewBox="0 0 17 16" fill="none" xmlns="http://www.w3.org/2000/svg">
<g clip-path="url(#clip0_21120_56765)">
<path fill-rule="evenodd" clip-rule="evenodd" d="M8.5 2C5.18629 2 2.5 4.68629 2.5 8C2.5 11.3137 5.18629 14 8.5 14C11.8137 14 14.5 11.3137 14.5 8C14.5 4.68629 11.8137 2 8.5 2ZM0.5 8C0.5 3.58172 4.08172 0 8.5 0C12.9183 0 16.5 3.58172 16.5 8C16.5 12.4183 12.9183 16 8.5 16C4.08172 16 0.5 12.4183 0.5 8Z" fill="#333333"/>
<circle cx="8.5" cy="5" r="1" fill="#333333"/>
<rect x="7.5" y="7" width="2" height="5" rx="1" fill="#333333"/>
</g>
<defs>
<clipPath id="clip0_21120_56765">
<rect width="16" height="16" fill="white" transform="translate(0.5)"/>
</clipPath>
</defs>
</svg>

After

Width:  |  Height:  |  Size: 710 B

View File

@ -100,12 +100,20 @@
"NotFoundTitle": "Nothing found",
"PasswordMinLenght": "Minimal password length",
"Path": "Path",
"PleaseNote": "Please note",
"PleaseNoteDescription": " <0>{{pleaseNote}}</0>: your old portal address will become available to new ©linney users once you click the <2>{{save}}</2> button.",
"PeopleAdministratorsCan": "People module admins can create profiles and groups, import people, and invite users.",
"PortalAccess": "Portal access",
"PortalAccessSubTitle": "This section allows you to provide users with safe and convenient ways to access the portal.",
"PortalAdmins": "Portal admins",
"PortalAdminsDescription": "Have the same access rights as the portal owner, except the right to: change portal owner; deactivate or delete portal.",
"PortalOwner": "Portal Owner",
"PortalRenaming": "Portal Renaming",
"PortalRenamingDescription": "Here you can change your portal address.",
"PortalRenamingDescriptionTooltip": "Enter the part that will appear next to the onlyoffice.com/onlyoffice.eu portal address.",
"PortalRenamingLabelText": "New portal name",
"PortalRenamingMobile": "Enter the part that will appear next to the onlyoffice.com/onlyoffice.eu portal address. Please note: your old portal address will become available to new ©linney users once you click the Save button.",
"PortalRenamingSettingsTooltip": "<0>{{text}}</0> Enter the part that will appear next to the onlyoffice.com/onlyoffice.eu portal address.",
"ProductUserOpportunities": "View profiles and groups",
"RegistrationDate": "Registration date",
"RestoreBackup": "Data Restore",
@ -147,7 +155,6 @@
"TrustedMail": "Trusted mail domain settings",
"TrustedMailDescription": "Trusted Mail Domain Settings is a way to specify the mail servers used for user self-registration.",
"TrustedMailHelper": "You can either check the Custom domains option and enter the trusted mail server in the field below so that a person who has an account at it will be able to register him(her)self by clicking the Join link on the Sign In page or disable this option.",
"TrustedMailWarningHelper": "Users with trusted email domains will automatically be listed in the Waiting Room section of the Address book.",
"TwoFactorAuth": "Two-factor authentication",
"TwoFactorAuthDescription": "Two-factor authentication provides a more secure way to log in. After entering the credentials, the user will have to enter a code from an SMS or the authentication app. ",
"TwoFactorAuthHelper": "Note: SMS messages can be sent if you have a positive balance only. You can always check your current balance in your SMS provider account. Do not forget to replenish your balance in good time. ",

View File

@ -147,7 +147,6 @@
"TrustedMail": "Настройки доверенных почтовых доменов",
"TrustedMailDescription": "Настройки доверенных почтовых доменов позволяют указать почтовые серверы, которые могут использовать пользователи при самостоятельной регистрации в ONLYOFFICE.",
"TrustedMailHelper": "Можно отметить опцию Пользовательские домены и ввести доверенный почтовый сервер в поле ниже, чтобы любой сотрудник вашей компании, имеющий учетную запись на указанном почтовом сервере, смог зарегистрироваться самостоятельно, нажав ссылку Присоединиться на странице входа и введя адрес электронной почты с именем доверенного домена, который Вы добавили.",
"TrustedMailWarningHelper": "Пользователи с доверенными почтовыми доменами будут автоматически попадать в Waiting Room в разделе Address book.",
"TwoFactorAuth": "Двухфакторная аутентификация",
"TwoFactorAuthDescription": "Двухфакторная аутентификация обеспечивает более безопасный способ входа на портал. После ввода учетных данных пользователь должен будет ввести код из SMS или приложения для аутентификации.",
"TwoFactorAuthHelper": "Обратите внимание: отправка SMS-сообщений осуществляется только при положительном балансе. Вы всегда можете проверить текущий баланс в учетной записи вашего SMS-провайдера. Не забывайте своевременно пополнять баланс. ",

View File

@ -110,7 +110,7 @@ class SectionHeaderContent extends React.Component {
const resultPath = locationPathname.slice(fullSettingsUrlLength + 1);
const arrayOfParams = resultPath.split("/");
const key = getKeyByLink(arrayOfParams, settingsTree);
const key = getKeyByLink(arrayOfParams, settingsTree)[0];
const header = getTKeyByKey(key, settingsTree);
const isCategory = checkPropertyByLink(
arrayOfParams,
@ -133,7 +133,7 @@ class SectionHeaderContent extends React.Component {
componentDidUpdate() {
const arrayOfParams = this.getArrayOfParams();
const key = getKeyByLink(arrayOfParams, settingsTree);
const key = getKeyByLink(arrayOfParams, settingsTree)[0];
const header = getTKeyByKey(key, settingsTree);
const isCategory = checkPropertyByLink(
arrayOfParams,

View File

@ -1,76 +0,0 @@
import React, { useEffect, useState } from "react";
import styled from "styled-components";
import { Base } from "@appserver/components/themes";
import { isSmallTablet } from "@appserver/components/utils/device";
const StyledComponent = styled.div`
.combo-button-label {
max-width: 100%;
}
.settings-block {
margin-bottom: 24px;
}
.category-description {
line-height: 20px;
color: #657077;
margin-bottom: 20px;
}
/* .category-item-wrapper:not(:last-child) {
border-bottom: 1px solid #eceef1;
margin-bottom: 24px;
} */
.category-item-description {
color: ${(props) => props.theme.studio.settings.common.descriptionColor};
font-size: 12px;
max-width: 1024px;
}
.category-item-heading {
display: flex;
align-items: center;
padding-bottom: 16px;
}
.category-item-title {
font-weight: bold;
font-size: 16px;
line-height: 22px;
margin-right: 4px;
}
@media (min-width: 600px) {
.settings-block {
max-width: 350px;
height: auto;
}
}
`;
StyledComponent.defaultProps = { theme: Base };
const SettingsPageLayout = ({ children }) => {
const [mobileView, setMobileView] = useState(true);
const checkInnerWidth = () => {
if (isSmallTablet()) {
setMobileView(true);
} else {
setMobileView(false);
}
};
useEffect(() => {
window.addEventListener("resize", checkInnerWidth);
return () => window.removeEventListener("resize", checkInnerWidth);
}, []);
const isMobile = !!(isSmallTablet() && mobileView);
return <>{children(isMobile)}</>;
};
export default SettingsPageLayout;

View File

@ -1,40 +0,0 @@
import React from "react";
import styled from "styled-components";
import { Base } from "@appserver/components/themes";
const StyledComponent = styled.div`
.combo-button-label {
max-width: 100%;
}
.category-item-wrapper {
padding-top: 20px;
.category-item-heading {
padding-bottom: 8px;
svg {
padding-bottom: 5px;
}
}
.category-item-description {
color: #657077;
font-size: 13px;
max-width: 1024px;
}
.inherit-title-link {
margin-right: 4px;
font-size: 16px;
font-weight: 700;
}
}
`;
StyledComponent.defaultProps = { theme: Base };
const SettingsPageMobileView = ({ children }) => {
return <StyledComponent>{children}</StyledComponent>;
};
export default SettingsPageMobileView;

View File

@ -0,0 +1,137 @@
import React from "react";
import { withTranslation } from "react-i18next";
import styled from "styled-components";
import Text from "@appserver/components/text";
import Box from "@appserver/components/box";
import Link from "@appserver/components/link";
import { combineUrl } from "@appserver/common/utils";
import { inject, observer } from "mobx-react";
import { AppServerConfig } from "@appserver/common/constants";
import withCultureNames from "@appserver/common/hoc/withCultureNames";
import history from "@appserver/common/history";
import { Base } from "@appserver/components/themes";
import { StyledArrowRightIcon } from "../common/settingsCustomization/StyledSettings";
const StyledComponent = styled.div`
padding-top: 13px;
.combo-button-label {
max-width: 100%;
}
.category-item-wrapper {
padding-bottom: 20px;
.category-item-heading {
padding-bottom: 8px;
svg {
padding-bottom: 5px;
}
}
.category-item-description {
color: #657077;
font-size: 13px;
max-width: 1024px;
}
.inherit-title-link {
margin-right: 4px;
font-size: 16px;
font-weight: 700;
}
}
`;
StyledComponent.defaultProps = { theme: Base };
const CustomizationNavbar = ({ t, theme, helpUrlCommonSettings }) => {
const onClickLink = (e) => {
e.preventDefault();
history.push(e.target.pathname);
};
return (
<StyledComponent>
<div className="category-item-wrapper">
<div className="category-item-heading">
<Link
className="inherit-title-link header"
onClick={onClickLink}
truncate={true}
href={combineUrl(
AppServerConfig.proxyURL,
"/settings/common/customization/language-and-time-zone"
)}
>
{t("StudioTimeLanguageSettings")}
</Link>
<StyledArrowRightIcon size="small" color="#333333" />
</div>
<Text className="category-item-description">
{t("LanguageAndTimeZoneSettingsDescription")}
</Text>
<Box paddingProp="10px 0 3px 0">
<Link
color={theme.studio.settings.common.linkColorHelp}
target="_blank"
isHovered={true}
href={helpUrlCommonSettings}
>
{t("Common:LearnMore")}
</Link>
</Box>
</div>
<div className="category-item-wrapper">
<div className="category-item-heading">
<Link
truncate={true}
className="inherit-title-link header"
onClick={onClickLink}
href={combineUrl(
AppServerConfig.proxyURL,
"/settings/common/customization/welcome-page-settings"
)}
>
{t("CustomTitlesWelcome")}
</Link>
<StyledArrowRightIcon size="small" color="#333333" />
</div>
<Text className="category-item-description">
{t("CustomTitlesSettingsDescription")}
</Text>
</div>
<div className="category-item-wrapper">
<div className="category-item-heading">
<Link
truncate={true}
className="inherit-title-link header"
onClick={onClickLink}
href={combineUrl(
AppServerConfig.proxyURL,
"/settings/common/customization/portal-renaming"
)}
>
{t("PortalRenaming")}
</Link>
<StyledArrowRightIcon size="small" color="#333333" />
</div>
<Text className="category-item-description">
{t("PortalRenamingDescription")}
</Text>
</div>
</StyledComponent>
);
};
export default inject(({ auth }) => {
const { helpUrlCommonSettings, theme } = auth.settingsStore;
return {
theme,
helpUrlCommonSettings,
};
})(
withCultureNames(
observer(withTranslation(["Settings", "Common"])(CustomizationNavbar))
)
);

View File

@ -1,15 +1,14 @@
import React from "react";
import React, { useEffect, useState } from "react";
import { withTranslation } from "react-i18next";
import styled from "styled-components";
import withCultureNames from "@appserver/common/hoc/withCultureNames";
import LanguageAndTimeZone from "./settingsCustomization/language-and-time-zone";
import WelcomePageSettings from "./settingsCustomization/welcome-page-settings";
import PortalRenaming from "./settingsCustomization/portal-renaming";
import SettingsPageLayout from "./SettingsPageLayout";
import SettingsPageMobileView from "./SettingsPageMobileView";
import { isSmallTablet } from "@appserver/components/utils/device";
import CustomizationNavbar from "./customization-navbar";
import { Base } from "@appserver/components/themes";
import { setDocumentTitle } from "../../../../../helpers/utils";
const StyledComponent = styled.div`
.combo-button-label {
@ -24,12 +23,12 @@ const StyledComponent = styled.div`
line-height: 20px;
color: #657077;
margin-bottom: 20px;
max-width: 700px;
}
.category-item-wrapper:not(:last-child) {
border-bottom: 1px solid #eceef1;
margin-bottom: 24px;
// Add
padding-bottom: 24px;
}
@ -63,28 +62,42 @@ const StyledComponent = styled.div`
StyledComponent.defaultProps = { theme: Base };
const Customization = ({ t }) => {
return (
<SettingsPageLayout>
{(isMobile) =>
isMobile ? (
<SettingsPageMobileView>
<LanguageAndTimeZone isMobileView={isMobile} />
<WelcomePageSettings isMobileView={isMobile} />
<PortalRenaming isMobileView={isMobile} />
</SettingsPageMobileView>
) : (
<StyledComponent>
<div className="category-description">{`${t(
"Settings:CustomizationDescription"
)}`}</div>
{/* TODO: Add isMobile = false in component */}
<LanguageAndTimeZone isMobileView={isMobile} />
<WelcomePageSettings isMobileView={isMobile} />
<PortalRenaming isMobileView={isMobile} />
</StyledComponent>
)
}
</SettingsPageLayout>
const [mobileView, setMobileView] = useState(true);
const [isLoadingCustomization, setIsLoadingCustomization] = useState(true);
const checkInnerWidth = () => {
if (isSmallTablet()) {
setMobileView(true);
} else {
setMobileView(false);
}
};
useEffect(() => {
setDocumentTitle(t("Customization"));
//TODO: add method to get the portal name
setIsLoadingCustomization(false);
window.addEventListener("resize", checkInnerWidth);
return () => window.removeEventListener("resize", checkInnerWidth);
}, []);
const isMobile = !!(isSmallTablet() && mobileView);
return isMobile ? (
<CustomizationNavbar />
) : (
<StyledComponent>
<div className="category-description">{`${t(
"Settings:CustomizationDescription"
)}`}</div>
<LanguageAndTimeZone
isLoadingCustomization={isLoadingCustomization}
isMobileView={isMobile}
/>
<WelcomePageSettings isMobileView={isMobile} />
<PortalRenaming isMobileView={isMobile} />
</StyledComponent>
);
};

View File

@ -54,6 +54,18 @@ const StyledSettingsComponent = styled.div`
line-height: 20px;
}
.errorText {
position: absolute;
font-size: 10px;
color: #f21c0e;
}
.settings-block-description {
line-height: 20px;
color: #657077;
padding-bottom: 12px;
}
@media (max-width: 599px) {
${(props) =>
props.hasScroll &&
@ -75,6 +87,10 @@ const StyledSettingsComponent = styled.div`
max-width: 350px;
height: auto;
}
.settings-block-description {
display: none;
}
}
@media (orientation: landscape) and (max-width: 600px) {

View File

@ -19,17 +19,10 @@ import { AppServerConfig } from "@appserver/common/constants";
import config from "../../../../../../../package.json";
import history from "@appserver/common/history";
import { isMobileOnly } from "react-device-detect";
import Text from "@appserver/components/text";
import Box from "@appserver/components/box";
import Link from "@appserver/components/link";
import { isSmallTablet } from "@appserver/components/utils/device";
import checkScrollSettingsBlock from "../utils";
import {
StyledSettingsComponent,
StyledScrollbar,
StyledArrowRightIcon,
} from "./StyledSettings";
import { StyledSettingsComponent, StyledScrollbar } from "./StyledSettings";
import LoaderLngTZSettings from "../sub-components/loaderLngTZSettings";
const mapTimezonesToArray = (timezones) => {
return timezones.map((timezone) => {
return { key: timezone.id, label: timezone.displayName };
@ -80,7 +73,7 @@ class LanguageAndTimeZone extends React.Component {
"timezoneDefault"
);
setDocumentTitle(t("Customization"));
setDocumentTitle(t("StudioTimeLanguageSettings"));
this.state = {
isLoadedData: false,
@ -90,8 +83,6 @@ class LanguageAndTimeZone extends React.Component {
timezoneDefault: timezoneDefaultFromSessionStorage || timezone,
language: languageFromSessionStorage || language,
languageDefault: languageDefaultFromSessionStorage || language,
isLoadingGreetingSave: false,
isLoadingGreetingRestore: false,
hasChanged: false,
showReminder: false,
hasScroll: false,
@ -343,8 +334,9 @@ class LanguageAndTimeZone extends React.Component {
theme,
cultureNames,
isMobileView,
helpUrlCommonSettings,
isLoadingCustomization,
} = this.props;
const {
isLoadedData,
language,
@ -359,37 +351,6 @@ class LanguageAndTimeZone extends React.Component {
<LanguageTimeSettingsTooltip theme={theme} t={t} />
);
const isMobileViewLanguageTimeSettings = (
<div className="category-item-wrapper">
<div className="category-item-heading">
<Link
className="inherit-title-link header"
onClick={this.onClickLink}
truncate={true}
href={combineUrl(
AppServerConfig.proxyURL,
"/settings/common/customization/language-and-time-zone"
)}
>
{t("StudioTimeLanguageSettings")}
</Link>
<StyledArrowRightIcon size="small" color="#333333" />
</div>
<Text className="category-item-description">
{t("LanguageAndTimeZoneSettingsDescription")}
</Text>
<Box paddingProp="10px 0 3px 0">
<Link
color={theme.studio.settings.common.linkColorHelp}
target="_blank"
isHovered={true}
href={helpUrlCommonSettings}
>
{t("Common:LearnMore")}
</Link>
</Box>
</div>
);
const settingsBlock = (
<div className="settings-block">
<FieldContainer
@ -440,8 +401,6 @@ class LanguageAndTimeZone extends React.Component {
return !isLoadedData ? (
<Loader className="pageLoader" type="rombs" size="40px" />
) : isMobileView ? (
isMobileViewLanguageTimeSettings
) : (
<StyledSettingsComponent
hasScroll={hasScroll}
@ -492,7 +451,6 @@ export default inject(({ auth, setup }) => {
//getPortalCultures,
getPortalTimezones,
getCurrentCustomSchema,
helpUrlCommonSettings,
} = auth.settingsStore;
const { user } = auth.userStore;
@ -514,7 +472,6 @@ export default inject(({ auth, setup }) => {
setLanguageAndTime,
getCurrentCustomSchema,
getPortalTimezones,
helpUrlCommonSettings,
};
})(
withCultureNames(

View File

@ -1,6 +1,5 @@
import React, { useState, useEffect } from "react";
import { withTranslation } from "react-i18next";
import styled from "styled-components";
import Loader from "@appserver/components/loader";
import toastr from "@appserver/components/toast/toastr";
import HelpButton from "@appserver/components/help-button";
@ -13,37 +12,12 @@ import { AppServerConfig } from "@appserver/common/constants";
import config from "../../../../../../../package.json";
import history from "@appserver/common/history";
import { isMobileOnly } from "react-device-detect";
import Text from "@appserver/components/text";
import Link from "@appserver/components/link";
import { isSmallTablet } from "@appserver/components/utils/device";
import checkScrollSettingsBlock from "../utils";
import {
StyledSettingsComponent,
StyledScrollbar,
StyledArrowRightIcon,
} from "./StyledSettings";
import { PortalRenamingTooltip } from "../sub-components/common-tooltips";
import { StyledSettingsComponent, StyledScrollbar } from "./StyledSettings";
import { saveToSessionStorage, getFromSessionStorage } from "../../../utils";
const StyledComponent = styled.div`
.settings-block {
margin-bottom: 70px;
}
.settings-block {
max-width: 350px;
}
.combo-button-label {
max-width: 100%;
}
.error {
font-weight: 400;
font-size: 10px;
line-height: 14px;
color: #f21c0e;
}
`;
import { setDocumentTitle } from "../../../../../../helpers/utils";
const PortalRenaming = ({ t, setPortalRename, isMobileView }) => {
// TODO: Change false
@ -72,6 +46,8 @@ const PortalRenaming = ({ t, setPortalRename, isMobileView }) => {
"The account name must be between 6 and 50 characters long";
useEffect(() => {
setDocumentTitle(t("PortalRenaming"));
const checkScroll = checkScrollSettingsBlock();
window.addEventListener("resize", checkInnerWidth);
@ -83,6 +59,15 @@ const PortalRenaming = ({ t, setPortalRename, isMobileView }) => {
setHasScroll(scrollPortalName);
}
// TODO: Remove div with height 64 and remove settings-mobile class
const settingsMobile = document.getElementsByClassName(
"settings-mobile"
)[0];
if (settingsMobile) {
settingsMobile.style.display = "none";
}
return () =>
window.removeEventListener(
"resize",
@ -135,7 +120,8 @@ const PortalRenaming = ({ t, setPortalRename, isMobileView }) => {
saveToSessionStorage("errorValue", lengthNameError);
break;
default:
setErrorValue("");
saveToSessionStorage("errorValue", null);
setErrorValue(null);
}
};
@ -174,94 +160,66 @@ const PortalRenaming = ({ t, setPortalRename, isMobileView }) => {
}
};
// TODO: Move to a file
const onClickLink = (e) => {
e.preventDefault();
history.push(e.target.pathname);
};
const isMobileViewPortalRenaming = (
<div className="category-item-wrapper">
<div className="category-item-heading">
<Link
truncate={true}
className="inherit-title-link header"
onClick={onClickLink}
href={combineUrl(
AppServerConfig.proxyURL,
"/settings/common/customization/portal-renaming"
)}
>
Portal Renaming
{/* {t("CustomTitlesWelcome")} */}
</Link>
<StyledArrowRightIcon size="small" color="#333333" />
</div>
<Text className="category-item-description">
{/* {t("CustomTitlesSettingsDescription")} */}
Here you can change your portal address.
</Text>
</div>
);
const tooltipPortalRenamingTooltip = <PortalRenamingTooltip t={t} />;
const hasError = errorValue === null ? false : true;
const settingsBlock = (
<div className="settings-block">
<div className="settings-block-description">
{t("PortalRenamingMobile")}
</div>
<FieldContainer
id="fieldContainerWelcomePage"
className="field-container-width"
labelText={`${t("New portal name")}:`}
labelText={`${t("PortalRenamingLabelText")}:`}
isVertical={true}
>
<TextInput
scale={true}
value={portalName}
onChange={onChangePortalName}
// isDisabled={isLoadingGreetingSave || isLoadingGreetingRestore}
placeholder={`${t("room")}`}
isDisabled={isLoadingPortalNameSave}
hasError={hasError}
/>
<div className="error">{errorValue}</div>
<div className="errorText">{errorValue}</div>
</FieldContainer>
</div>
);
return !isLoadedData ? (
<Loader className="pageLoader" type="rombs" size="40px" />
) : isMobileView ? (
isMobileViewPortalRenaming
) : (
<>
<StyledComponent hasScroll={hasScroll} className="category-item-wrapper">
{checkInnerWidth() && !isMobileView && (
<div className="category-item-heading">
<div className="category-item-title">{t("Portal Renaming")}</div>
<HelpButton
iconName="static/images/combined.shape.svg"
size={12}
// tooltipContent={tooltipCustomTitlesTooltip}
/>
</div>
)}
{(isMobileOnly && isSmallTablet()) || isSmallTablet() ? (
<StyledScrollbar stype="smallBlack">{settingsBlock}</StyledScrollbar>
) : (
<> {settingsBlock}</>
)}
<SaveCancelButtons
className="save-cancel-buttons"
onSaveClick={onSavePortalRename}
onCancelClick={onCancelPortalName}
saveButtonLabel={t("Common:SaveButton")}
cancelButtonLabel={t("Common:CancelButton")}
showReminder={showReminder}
reminderTest={t("YouHaveUnsavedChanges")}
displaySettings={true}
// hasScroll={hasScroll}
/>
</StyledComponent>
</>
<StyledSettingsComponent
hasScroll={hasScroll}
className="category-item-wrapper"
>
{checkInnerWidth() && !isMobileView && (
<div className="category-item-heading">
<div className="category-item-title">{t("PortalRenaming")}</div>
<HelpButton
iconName="static/images/combined.shape.svg"
size={12}
tooltipContent={tooltipPortalRenamingTooltip}
/>
</div>
)}
{(isMobileOnly && isSmallTablet()) || isSmallTablet() ? (
<StyledScrollbar stype="smallBlack">{settingsBlock}</StyledScrollbar>
) : (
<> {settingsBlock}</>
)}
<SaveCancelButtons
className="save-cancel-buttons"
onSaveClick={onSavePortalRename}
onCancelClick={onCancelPortalName}
saveButtonLabel={t("Common:SaveButton")}
cancelButtonLabel={t("Common:CancelButton")}
showReminder={showReminder}
reminderTest={t("YouHaveUnsavedChanges")}
displaySettings={true}
hasScroll={hasScroll}
/>
</StyledSettingsComponent>
);
};

View File

@ -16,15 +16,10 @@ import { AppServerConfig } from "@appserver/common/constants";
import config from "../../../../../../../package.json";
import history from "@appserver/common/history";
import { isMobileOnly } from "react-device-detect";
import Text from "@appserver/components/text";
import Link from "@appserver/components/link";
import { isSmallTablet } from "@appserver/components/utils/device";
import checkScrollSettingsBlock from "../utils";
import {
StyledSettingsComponent,
StyledScrollbar,
StyledArrowRightIcon,
} from "./StyledSettings";
import { StyledSettingsComponent, StyledScrollbar } from "./StyledSettings";
import LoaderLngTZSettings from "../sub-components/loaderLngTZSettings";
let greetingTitleFromSessionStorage = "";
let greetingTitleDefaultFromSessionStorage = "";
@ -47,7 +42,7 @@ class WelcomePageSettings extends React.Component {
"isFirstWelcomePageSettings"
);
setDocumentTitle(t("Customization"));
setDocumentTitle(t("CustomTitlesWelcome"));
this.state = {
isLoadedData: false,
@ -235,29 +230,6 @@ class WelcomePageSettings extends React.Component {
const tooltipCustomTitlesTooltip = <CustomTitlesTooltip t={t} />;
// TODO: Move to a file
const isMobileViewWelcomePageSettings = (
<div className="category-item-wrapper">
<div className="category-item-heading">
<Link
truncate={true}
className="inherit-title-link header"
onClick={this.onClickLink}
href={combineUrl(
AppServerConfig.proxyURL,
"/settings/common/customization/welcome-page-settings"
)}
>
{t("CustomTitlesWelcome")}
</Link>
<StyledArrowRightIcon size="small" color="#333333" />
</div>
<Text className="category-item-description">
{t("CustomTitlesSettingsDescription")}
</Text>
</div>
);
const settingsBlock = (
<div className="settings-block">
<FieldContainer
@ -280,8 +252,6 @@ class WelcomePageSettings extends React.Component {
return !isLoadedData ? (
<Loader className="pageLoader" type="rombs" size="40px" />
) : isMobileView ? (
isMobileViewWelcomePageSettings
) : (
<StyledSettingsComponent
hasScroll={hasScroll}

View File

@ -11,10 +11,10 @@ const StyledTooltip = styled.div`
.bold {
font-weight: 600;
}
.display {
.display-inline {
display: inline;
}
.display-link {
.display-block {
display: block;
}
`;
@ -28,7 +28,7 @@ export const LanguageTimeSettingsTooltip = ({ t, theme }) => {
<StyledTooltip>
<Text className="font-size">
<Trans ns="Settings" i18nKey="LanguageTimeSettingsTooltip" text={text}>
<Text className="bold display font-size">{{ text }}</Text>{" "}
<Text className="bold display-inline font-size">{{ text }}</Text>{" "}
</Trans>
</Text>
<Text className="font-size">
@ -39,10 +39,13 @@ export const LanguageTimeSettingsTooltip = ({ t, theme }) => {
save={save}
>
{" "}
<Text className="bold display font-size"> {{ save }}</Text>{" "}
<Text className="bold display-inline font-size">
{" "}
{{ save }}
</Text>{" "}
<Link
color={theme.studio.settings.common.linkColorHelp}
className="display-link font-size"
className="display-block font-size"
isHovered={true}
href="https://helpcenter.onlyoffice.com/administration/configuration.aspx#CustomizingPortal_block"
>
@ -69,9 +72,12 @@ export const CustomTitlesTooltip = ({ t }) => {
text={text}
from={from}
>
<Text className="bold display font-size"> {{ welcomeText }}</Text>{" "}
<Text className="bold display font-size"> {{ text }}</Text>{" "}
<Text className="bold display font-size"> {{ from }}</Text>{" "}
<Text className="bold display-inline font-size">
{" "}
{{ welcomeText }}
</Text>{" "}
<Text className="bold display-inline font-size"> {{ text }}</Text>{" "}
<Text className="bold display-inline font-size"> {{ from }}</Text>{" "}
</Trans>
</Text>
<Text className="font-size">
@ -81,7 +87,44 @@ export const CustomTitlesTooltip = ({ t }) => {
header={header}
>
{" "}
<Text className="bold display font-size"> {{ header }}</Text>{" "}
<Text className="bold display-inline font-size">
{" "}
{{ header }}
</Text>{" "}
</Trans>
</Text>
</StyledTooltip>
);
};
export const PortalRenamingTooltip = ({ t }) => {
const text = t("Settings:PortalRenamingDescription");
const pleaseNote = t("Settings:PleaseNote");
const save = t("Common:SaveButton");
return (
<StyledTooltip>
<Text className="font-size">
<Trans
ns="Settings"
i18nKey="PortalRenamingSettingsTooltip"
text={text}
>
<Text className="display-inline font-size"> {{ text }}</Text>{" "}
</Trans>
</Text>
<Text className="font-size">
<Trans
ns="Settings"
i18nKey="PleaseNoteDescription"
pleaseNote={pleaseNote}
save={save}
>
<Text className="bold display-inline font-size">
{" "}
{{ pleaseNote }}
</Text>{" "}
<Text className="bold display-inline font-size"> {{ save }}</Text>{" "}
</Trans>
</Text>
</StyledTooltip>

View File

@ -0,0 +1,15 @@
import React from "react";
import styled from "styled-components";
import Loaders from "@appserver/common/components/Loaders";
const StyledLoader = styled.div``;
const LoaderLngTZSettings = () => {
return (
<StyledLoader>
<Loaders.Rectangle />
</StyledLoader>
);
};
export default LoaderLngTZSettings;

View File

@ -302,7 +302,7 @@ class WhiteLabel extends React.Component {
} = this.state;
console.log("WhiteLabelSettings render");
return <> В разработке</>;
return <>In development</>;
return !isLoadedData ? (
<Loader className="pageLoader" type="rombs" size="40px" />

View File

@ -45,6 +45,12 @@ export const StyledCategoryWrapper = styled.div`
align-items: center;
`;
export const StyledTooltip = styled.div`
.subtitle {
margin-bottom: 10px;
}
`;
export const StyledMobileCategoryWrapper = styled.div`
margin-bottom: 20px;
@ -115,4 +121,8 @@ export const LearnMoreWrapper = styled.div`
flex-direction: column;
margin-bottom: 20px;
}
.learn-subtitle {
margin-bottom: 10px;
}
`;

View File

@ -12,6 +12,7 @@ import AdminMessageSection from "./adminMessage";
import MobileView from "./mobileView";
import CategoryWrapper from "../sub-components/category-wrapper";
import { size } from "@appserver/components/utils/device";
import { getLanguage } from "@appserver/common/utils";
const AccessPortal = (props) => {
const { t } = props;
@ -30,31 +31,41 @@ const AccessPortal = (props) => {
: setIsMobileView(false);
};
const lng = getLanguage(localStorage.getItem("language") || "en");
if (isMobileView) return <MobileView />;
return (
<MainContainer className="desktop-view">
<Text className="subtitle">{t("PortalAccessSubTitle")}</Text>
<CategoryWrapper
t={t}
title={t("SettingPasswordStrength")}
tooltipContent={t("SettingPasswordStrengthDescription")}
tooltipTitle={t("SettingPasswordStrengthDescription")}
tooltipUrl={`https://helpcenter.onlyoffice.com/${lng}/administration/configuration.aspx#ChangingSecuritySettings_block`}
/>
<PasswordStrengthSection />
<hr />
<CategoryWrapper
t={t}
title={t("TwoFactorAuth")}
tooltipContent={t("TwoFactorAuthDescription")}
tooltipTitle={t("TwoFactorAuthDescription")}
tooltipUrl={`https://helpcenter.onlyoffice.com/${lng}/administration/two-factor-authentication.aspx`}
/>
<TfaSection />
<hr />
<CategoryWrapper
t={t}
title={t("TrustedMail")}
tooltipContent={t("TrustedMailDescription")}
tooltipTitle={t("TrustedMailDescription")}
tooltipUrl={`https://helpcenter.onlyoffice.com/${lng}/administration/configuration.aspx#ChangingSecuritySettings_block`}
/>
<TrustedMailSection />
<hr />
<CategoryWrapper
t={t}
title={t("IPSecurity")}
tooltipContent={t("IPSecurityDescription")}
tooltipTitle={t("IPSecurityDescription")}
/>
<IpSecuritySection />
<hr />

View File

@ -8,7 +8,6 @@ import Text from "@appserver/components/text";
import Link from "@appserver/components/link";
import Slider from "@appserver/components/slider";
import Checkbox from "@appserver/components/checkbox";
import SectionLoader from "../sub-components/section-loader";
import { getLanguage } from "@appserver/common/utils";
import { LearnMoreWrapper } from "../StyledSecurity";
import toastr from "@appserver/components/toast/toastr";
@ -20,10 +19,6 @@ import isEqual from "lodash/isEqual";
const MainContainer = styled.div`
width: 100%;
.page-subtitle {
margin-bottom: 10px;
}
.password-slider {
width: 160px;
height: 8px;
@ -162,11 +157,11 @@ const PasswordStrength = (props) => {
};
const lng = getLanguage(localStorage.getItem("language") || "en");
if (!isLoading) return <SectionLoader />;
return (
<MainContainer>
<LearnMoreWrapper>
<Text className="page-subtitle">
<Text className="learn-subtitle">
{t("SettingPasswordStrengthHelper")}
</Text>
<Link

View File

@ -7,23 +7,14 @@ import RadioButtonGroup from "@appserver/components/radio-button-group";
import Text from "@appserver/components/text";
import Link from "@appserver/components/link";
import toastr from "@appserver/components/toast/toastr";
import SectionLoader from "../sub-components/section-loader";
import { getLanguage } from "@appserver/common/utils";
import Buttons from "../sub-components/buttons";
import { LearnMoreWrapper } from "../StyledSecurity";
import { size } from "@appserver/components/utils/device";
import { saveToSessionStorage, getFromSessionStorage } from "../../../utils";
import isEqual from "lodash/isEqual";
const MainContainer = styled.div`
width: 100%;
.page-subtitle {
margin-bottom: 10px;
}
.box {
}
`;
const TwoFactorAuth = (props) => {
@ -113,11 +104,10 @@ const TwoFactorAuth = (props) => {
};
const lng = getLanguage(localStorage.getItem("language") || "en");
if (!isLoading) return <SectionLoader />;
return (
<MainContainer>
<LearnMoreWrapper>
<Text className="page-subtitle">{t("TwoFactorAuthHelper")}</Text>
<Text className="learn-subtitle">{t("TwoFactorAuthHelper")}</Text>
<Link
color="#316DAA"
target="_blank"

View File

@ -18,10 +18,6 @@ import isEqual from "lodash/isEqual";
const MainContainer = styled.div`
width: 100%;
.page-subtitle {
margin-bottom: 10px;
}
.user-fields {
margin-bottom: 18px;
}
@ -130,7 +126,7 @@ const TrustedMail = (props) => {
const onSaveClick = () => {
const valid = domains.map((domain) => regexp.test(domain));
if (valid.includes(false)) {
if (type === "1" && valid.includes(false)) {
toastr.error(t("Common:IncorrectDomain"));
return;
}
@ -160,7 +156,7 @@ const TrustedMail = (props) => {
return (
<MainContainer>
<LearnMoreWrapper>
<Text className="page-subtitle">{t("TrustedMailHelper")}</Text>
<Text className="learn-subtitle">{t("TrustedMailHelper")}</Text>
<Link
color="#316DAA"
target="_blank"
@ -208,16 +204,6 @@ const TrustedMail = (props) => {
/>
)}
<Text
color="#F21C0E"
fontSize="16px"
fontWeight="700"
className="warning-text"
>
{t("Common:Warning")}!
</Text>
<Text>{t("TrustedMailWarningHelper")}</Text>
<Buttons
t={t}
showReminder={showReminder}
@ -228,7 +214,7 @@ const TrustedMail = (props) => {
);
};
export default inject(({ auth, setup }) => {
export default inject(({ auth }) => {
const {
trustedDomainsType,
trustedDomains,

View File

@ -1,11 +1,23 @@
import React from "react";
import Text from "@appserver/components/text";
import HelpButton from "@appserver/components/help-button";
import Link from "@appserver/components/link";
import { Base } from "@appserver/components/themes";
import { StyledCategoryWrapper } from "../StyledSecurity";
import { StyledCategoryWrapper, StyledTooltip } from "../StyledSecurity";
const CategoryWrapper = (props) => {
const { title, tooltipContent, theme } = props;
const { t, title, tooltipTitle, tooltipUrl, theme } = props;
const tooltip = () => (
<StyledTooltip>
<Text className={tooltipUrl ? "subtitle" : ""}>{tooltipTitle}</Text>
{tooltipUrl && (
<Link target="_blank" isHovered href={tooltipUrl}>
{t("Common:LearnMore")}
</Link>
)}
</StyledTooltip>
);
return (
<StyledCategoryWrapper>
@ -17,7 +29,7 @@ const CategoryWrapper = (props) => {
displayType="dropdown"
place="right"
offsetRight={0}
tooltipContent={<Text>{tooltipContent}</Text>}
getContent={tooltip}
tooltipColor={theme.studio.settings.security.owner.tooltipColor}
/>
</StyledCategoryWrapper>

View File

@ -1,26 +0,0 @@
import React from "react";
import styled from "styled-components";
import Loaders from "@appserver/common/components/Loaders";
const StyledLoader = styled.div`
display: flex;
flex-direction: column;
gap: 8px;
width: 300px;
@media (max-width: 375px) {
width: 100%;
}
`;
const SectionLoader = () => {
return (
<StyledLoader>
<Loaders.Rectangle />
<Loaders.Rectangle />
<Loaders.Rectangle />
</StyledLoader>
);
};
export default SectionLoader;

View File

@ -1,4 +1,4 @@
import React, { useState } from "react";
import React, { useState, useEffect, useRef } from "react";
import styled from "styled-components";
import commonIconsStyles from "@appserver/components/utils/common-icons-style";
import { PlusIcon, TrashIcon } from "./svg";
@ -38,6 +38,14 @@ const StyledAddWrapper = styled.div`
cursor: pointer;
`;
const usePrevious = (value) => {
const ref = useRef();
useEffect(() => {
ref.current = value;
}, [value]);
return ref.current;
};
const UserFields = (props) => {
const {
className,
@ -49,6 +57,33 @@ const UserFields = (props) => {
regexp,
} = props;
const [errors, setErrors] = useState(new Array(inputs.length).fill(false));
const prevInputs = usePrevious(inputs.length);
useEffect(() => {
if (inputs.length > prevInputs) setErrors([...errors, false]);
}, [inputs]);
const onBlur = (index) => {
let newErrors = Array.from(errors);
newErrors[index] = true;
setErrors(newErrors);
};
const onFocus = (index) => {
let newErrors = Array.from(errors);
newErrors[index] = false;
setErrors(newErrors);
};
const onDelete = (index) => {
let newErrors = Array.from(errors);
newErrors.splice(index, 1);
setErrors(newErrors);
onDeleteInput(index);
};
return (
<div className={className}>
{inputs ? (
@ -59,14 +94,14 @@ const UserFields = (props) => {
<StyledInputWrapper key={`domain-input-${index}`}>
<TextInput
id={`domain-input-${index}`}
isAutoFocussed={true}
value={input}
onChange={(e) => onChangeInput(e, index)}
hasError={error}
/>
<StyledTrashIcon
size="medium"
onClick={() => onDeleteInput(index)}
onBlur={() => onBlur(index)}
onFocus={() => onFocus(index)}
hasError={errors[index] && error}
/>
<StyledTrashIcon size="medium" onClick={() => onDelete(index)} />
</StyledInputWrapper>
);
})

View File

@ -32,8 +32,8 @@ export const settingsTree = [
{
key: "0-0-2",
icon: "",
link: "team-template",
tKey: "TeamTemplate",
link: "portal-renaming",
tKey: "PortalRenaming",
},
],
},

View File

@ -238,7 +238,6 @@ const Editor = () => {
data: "backup-restore",
});
socketHelper.on("restore-backup", () => {
setPreparationPortalDialogVisible(true);
});
} catch (e) {
@ -625,20 +624,23 @@ const Editor = () => {
const onSDKAppReady = () => {
console.log("ONLYOFFICE Document Editor is ready");
const index = url.indexOf("#message/");
if (index > -1) {
const splitUrl = url.split("#message/");
const message = decodeURIComponent(splitUrl[1]).replaceAll("+", " ");
history.pushState({}, null, url.substring(0, index));
docEditor.showMessage(message);
} else {
if (config?.Error) docEditor.showMessage(config.Error);
}
const tempElm = document.getElementById("loader");
if (tempElm) {
tempElm.outerHTML = "";
}
const index = url.indexOf("#message/");
if (index > -1) {
const splitUrl = url.split("#message/");
if (splitUrl.length === 2) {
let raw = splitUrl[1]?.trim();
const message = decodeURIComponent(raw).replace(/\+/g, " ");
docEditor.showMessage(message);
history.pushState({}, null, url.substring(0, index));
}
} else {
if (config?.Error) docEditor.showMessage(config.Error);
}
};
const onSDKInfo = (event) => {
@ -961,7 +963,9 @@ const Editor = () => {
)}
{preparationPortalDialogVisible && (
<PreparationPortalDialog visible={preparationPortalDialogVisible} />
<PreparationPortalDialog
visible={preparationPortalDialogVisible}
/>
)}
</>
) : (