2022-03-15 18:00:53 +00:00
// (c) Copyright Ascensio System SIA 2010-2022
//
// This program is a free software product.
// You can redistribute it and/or modify it under the terms
// of the GNU Affero General Public License (AGPL) version 3 as published by the Free Software
// Foundation. In accordance with Section 7(a) of the GNU AGPL its Section 15 shall be amended
// to the effect that Ascensio System SIA expressly excludes the warranty of non-infringement of
// any third-party rights.
//
// This program is distributed WITHOUT ANY WARRANTY, without even the implied warranty
// of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. For details, see
// the GNU AGPL at: http://www.gnu.org/licenses/agpl-3.0.html
//
// You can contact Ascensio System SIA at Lubanas st. 125a-25, Riga, Latvia, EU, LV-1021.
//
// The interactive user interfaces in modified source and object code versions of the Program must
// display Appropriate Legal Notices, as required under Section 5 of the GNU AGPL version 3.
//
// Pursuant to Section 7(b) of the License you must retain the original Product logo when
// distributing the program. Pursuant to Section 7(e) we decline to grant you any rights under
// trademark law for use of our trademarks.
//
// All the Product's GUI elements, including illustrations and icon sets, as well as technical writing
// content are licensed under the terms of the Creative Commons Attribution-ShareAlike 4.0
// International. See the License terms at http://creativecommons.org/licenses/by-sa/4.0/legalcode
2022-02-15 11:52:43 +00:00
namespace TMResourceData ;
2022-02-14 21:02:57 +00:00
2022-02-15 11:52:43 +00:00
public class DBResourceManager : ResourceManager
{
2022-03-17 14:33:16 +00:00
public static readonly bool WhiteLableEnabled ;
2022-02-15 11:52:43 +00:00
private readonly ConcurrentDictionary < string , ResourceSet > _resourceSets = new ConcurrentDictionary < string , ResourceSet > ( ) ;
2022-05-18 16:07:09 +00:00
private readonly IMemoryCache _memoryCache ;
2022-02-15 11:52:43 +00:00
public DBResourceManager ( string filename , Assembly assembly )
: base ( filename , assembly )
{
}
public DBResourceManager (
2022-05-18 16:07:09 +00:00
IMemoryCache memoryCache ,
2022-02-15 11:52:43 +00:00
IConfiguration configuration ,
2022-04-26 17:53:48 +00:00
ILogger < DBResourceManager > option ,
2022-02-15 11:52:43 +00:00
DbContextManager < ResourceDbContext > dbContext ,
string filename ,
Assembly assembly )
: base ( filename , assembly )
{
2022-05-18 16:07:09 +00:00
_memoryCache = memoryCache ;
2022-02-15 11:52:43 +00:00
_configuration = configuration ;
_option = option ;
_dbContext = dbContext ;
}
2022-05-26 09:01:54 +00:00
public static void PatchAssemblies ( ILogger < DBResourceManager > option )
2022-02-15 11:52:43 +00:00
{
AppDomain . CurrentDomain . AssemblyLoad + = ( _ , a ) = > PatchAssembly ( option , a . LoadedAssembly ) ;
Array . ForEach ( AppDomain . CurrentDomain . GetAssemblies ( ) , a = > PatchAssembly ( option , a ) ) ;
}
2022-05-26 09:01:54 +00:00
public static void PatchAssembly ( ILogger < DBResourceManager > logger , Assembly a , bool onlyAsc = true )
2022-02-15 11:52:43 +00:00
{
2022-04-26 17:53:48 +00:00
var log = logger ;
2022-02-15 11:52:43 +00:00
if ( ! onlyAsc | | Accept ( a ) )
{
var types = Array . Empty < Type > ( ) ;
try
{
types = a . GetTypes ( ) ;
2022-02-14 21:02:57 +00:00
}
2022-02-15 11:52:43 +00:00
catch ( ReflectionTypeLoadException rtle )
{
2022-05-26 09:01:54 +00:00
log . CanNotGetType ( nameof ( a . GetType ) , a . FullName , nameof ( a . GetExportedTypes ) , rtle ) ;
2022-02-15 11:52:43 +00:00
foreach ( var e in rtle . LoaderExceptions )
{
2022-05-05 14:13:47 +00:00
log . Information ( e . Message ) ;
2022-02-14 21:02:57 +00:00
}
2022-02-15 11:52:43 +00:00
try
{
types = a . GetExportedTypes ( ) ;
2022-02-14 21:02:57 +00:00
}
2022-02-15 11:52:43 +00:00
catch ( Exception err )
{
2022-05-06 07:58:15 +00:00
log . CanNotGetExportedTypes ( a . FullName , nameof ( a . GetExportedTypes ) , err ) ;
2022-02-15 11:52:43 +00:00
}
}
foreach ( var type in types )
{
var prop = type . GetProperty ( "ResourceManager" , BindingFlags . Static | BindingFlags . NonPublic | BindingFlags . Public ) ;
if ( prop ! = null )
{
var rm = ( ResourceManager ) prop . GetValue ( type ) ;
2022-04-14 19:23:57 +00:00
if ( rm is not DBResourceManager )
2022-02-14 21:02:57 +00:00
{
2022-02-15 11:52:43 +00:00
var dbrm = new DBResourceManager ( rm . BaseName , a ) ;
type . InvokeMember ( "resourceMan" , BindingFlags . Static | BindingFlags . NonPublic | BindingFlags . SetField , null , type , new object [ ] { dbrm } ) ;
2022-02-14 21:02:57 +00:00
}
2022-02-15 11:52:43 +00:00
}
}
}
}
2022-02-14 21:02:57 +00:00
2022-02-15 11:52:43 +00:00
private static bool Accept ( Assembly a )
{
var n = a . GetName ( ) . Name ;
return ( n . StartsWith ( "ASC." ) | | n . StartsWith ( "App_GlobalResources" ) ) & & a . GetManifestResourceNames ( ) . Length > 0 ;
}
public override Type ResourceSetType = > typeof ( DBResourceSet ) ;
private readonly IConfiguration _configuration ;
2022-05-26 09:01:54 +00:00
private readonly ILogger < DBResourceManager > _option ;
2022-02-15 11:52:43 +00:00
private readonly DbContextManager < ResourceDbContext > _dbContext ;
protected override ResourceSet InternalGetResourceSet ( CultureInfo culture , bool createIfNotExists , bool tryParents )
{
_resourceSets . TryGetValue ( culture . Name , out var set ) ;
if ( set = = null )
{
var invariant = culture = = CultureInfo . InvariantCulture ? base . InternalGetResourceSet ( CultureInfo . InvariantCulture , true , true ) : null ;
2022-05-18 16:07:09 +00:00
set = new DBResourceSet ( _memoryCache , _configuration , _option , _dbContext , invariant , culture , BaseName ) ;
2022-02-15 11:52:43 +00:00
_resourceSets . AddOrUpdate ( culture . Name , set , ( k , v ) = > set ) ;
}
return set ;
}
class DBResourceSet : ResourceSet
{
2022-03-25 16:26:06 +00:00
private const string NeutralCulture = "Neutral" ;
2022-02-15 11:52:43 +00:00
private readonly TimeSpan _cacheTimeout = TimeSpan . FromMinutes ( 120 ) ; // for performance
private readonly object _locker = new object ( ) ;
2022-05-18 16:07:09 +00:00
private readonly IMemoryCache _cache ;
2022-02-15 11:52:43 +00:00
private readonly ResourceSet _invariant ;
private readonly string _culture ;
private readonly string _fileName ;
2022-05-26 09:01:54 +00:00
private readonly ILogger < DBResourceManager > _logger ;
2022-02-15 11:52:43 +00:00
private readonly DbContextManager < ResourceDbContext > _dbContext ;
public DBResourceSet (
2022-05-18 16:07:09 +00:00
IMemoryCache memoryCache ,
2022-02-15 11:52:43 +00:00
IConfiguration configuration ,
2022-05-26 09:01:54 +00:00
ILogger < DBResourceManager > logger ,
2022-02-15 11:52:43 +00:00
DbContextManager < ResourceDbContext > dbContext ,
ResourceSet invariant ,
CultureInfo culture ,
string filename )
{
2022-03-09 17:15:51 +00:00
ArgumentNullException . ThrowIfNull ( culture ) ;
ArgumentNullOrEmptyException . ThrowIfNullOrEmpty ( filename ) ;
2022-02-15 11:52:43 +00:00
_dbContext = dbContext ;
2022-04-26 17:53:48 +00:00
_logger = logger ;
2022-02-15 11:52:43 +00:00
try
{
var defaultValue = ( ( int ) _cacheTimeout . TotalMinutes ) . ToString ( ) ;
_cacheTimeout = TimeSpan . FromMinutes ( Convert . ToInt32 ( configuration [ "resources:cache-timeout" ] ? ? defaultValue ) ) ;
}
catch ( Exception err )
{
2022-05-05 14:13:47 +00:00
_logger . ErrorDBResourceSet ( err ) ;
2022-02-15 11:52:43 +00:00
}
_invariant = invariant ;
2022-03-25 16:26:06 +00:00
_culture = invariant ! = null ? NeutralCulture : culture . Name ;
2022-02-15 11:52:43 +00:00
_fileName = filename . Split ( '.' ) . Last ( ) + ".resx" ;
2022-05-18 16:07:09 +00:00
_cache = memoryCache ;
2022-02-15 11:52:43 +00:00
}
public override string GetString ( string name , bool ignoreCase )
{
var result = ( string ) null ;
try
{
var dic = GetResources ( ) ;
dic . TryGetValue ( name , out result ) ;
}
catch ( Exception err )
{
2022-05-05 14:13:47 +00:00
_logger . CanNotGetResource ( _fileName , _culture , name , err ) ;
2022-02-15 11:52:43 +00:00
}
if ( _invariant ! = null & & result = = null )
{
result = _invariant . GetString ( name , ignoreCase ) ;
}
return result ;
}
public override IDictionaryEnumerator GetEnumerator ( )
{
var result = new Hashtable ( ) ;
if ( _invariant ! = null )
{
foreach ( DictionaryEntry e in _invariant )
{
result [ e . Key ] = e . Value ;
}
}
try
{
var dic = GetResources ( ) ;
foreach ( var e in dic )
{
result [ e . Key ] = e . Value ;
}
}
catch ( Exception err )
{
2022-05-05 14:13:47 +00:00
_logger . ErrorDBResourceSet ( err ) ;
2022-02-15 11:52:43 +00:00
}
return result . GetEnumerator ( ) ;
}
private Dictionary < string , string > GetResources ( )
{
var key = $"{_fileName}/{_culture}" ;
2022-04-14 19:23:57 +00:00
if ( _cache . Get ( key ) is not Dictionary < string , string > dic )
2022-02-15 11:52:43 +00:00
{
lock ( _locker )
{
dic = _cache . Get ( key ) as Dictionary < string , string > ;
2022-05-26 09:01:54 +00:00
2022-02-15 11:52:43 +00:00
if ( dic = = null )
{
2022-05-26 09:01:54 +00:00
2022-02-15 11:52:43 +00:00
dic = LoadResourceSet ( _fileName , _culture ) ;
2022-05-26 09:01:54 +00:00
2022-05-18 16:07:09 +00:00
if ( _cacheTimeout = = TimeSpan . Zero )
{
_cache . Set ( key , dic ) ;
}
else
{
_cache . Set ( key , dic , DateTimeOffset . Now . Add ( _cacheTimeout ) ) ;
}
2022-02-15 11:52:43 +00:00
}
}
}
return dic ;
}
private Dictionary < string , string > LoadResourceSet ( string filename , string culture )
{
using var context = _dbContext . Get ( "tmresource" ) ;
var q = context . ResData
. Where ( r = > r . CultureTitle = = culture )
. Join ( context . ResFiles , r = > r . FileId , a = > a . Id , ( d , f ) = > new { data = d , files = f } )
. Where ( r = > r . files . ResName = = filename ) ;
return q
. ToDictionary ( r = > r . data . Title , r = > r . data . TextValue , StringComparer . InvariantCultureIgnoreCase ) ;
}
}
}
[Singletone]
public class WhiteLabelHelper
{
2022-04-26 17:53:48 +00:00
private readonly ILogger _logger ;
2022-02-15 11:52:43 +00:00
private readonly ConcurrentDictionary < int , string > _whiteLabelDictionary ;
public string DefaultLogoText { get ; set ; }
private readonly IConfiguration _configuration ;
2022-04-26 17:53:48 +00:00
public WhiteLabelHelper ( IConfiguration configuration , ILoggerProvider option )
2022-02-15 11:52:43 +00:00
{
2022-04-26 17:53:48 +00:00
_logger = option . CreateLogger ( "ASC.Resources" ) ;
2022-02-15 11:52:43 +00:00
_whiteLabelDictionary = new ConcurrentDictionary < int , string > ( ) ;
DefaultLogoText = string . Empty ;
_configuration = configuration ;
}
public void SetNewText ( int tenantId , string newText )
{
try
{
_whiteLabelDictionary . AddOrUpdate ( tenantId , r = > newText , ( i , s ) = > newText ) ;
}
catch ( Exception e )
{
2022-05-05 14:13:47 +00:00
_logger . ErrorSetNewText ( e ) ;
2022-02-15 11:52:43 +00:00
}
}
public void RestoreOldText ( int tenantId )
{
try
{
_whiteLabelDictionary . TryRemove ( tenantId , out var text ) ;
}
catch ( Exception e )
{
2022-05-06 07:58:15 +00:00
_logger . ErrorRestoreOldText ( e ) ;
2022-02-15 11:52:43 +00:00
}
}
internal string ReplaceLogo ( TenantManager tenantManager , IHttpContextAccessor httpContextAccessor , string resourceName , string resourceValue )
{
if ( string . IsNullOrEmpty ( resourceValue ) )
{
return resourceValue ;
}
if ( ! DBResourceManager . WhiteLableEnabled )
{
return resourceValue ;
}
if ( httpContextAccessor . HttpContext ! = null ) //if in Notify Service or other process without HttpContext
{
try
{
var tenant = tenantManager . GetCurrentTenant ( false ) ;
if ( tenant = = null )
{
return resourceValue ;
}
if ( _whiteLabelDictionary . TryGetValue ( tenant . Id , out var newText ) )
{
var newTextReplacement = newText ;
if ( resourceValue . IndexOf ( '<' ) > = 0 & & resourceValue . IndexOf ( '>' ) > = 0 | | resourceName . StartsWith ( "pattern_" ) )
{
newTextReplacement = HttpUtility . HtmlEncode ( newTextReplacement ) ;
}
if ( resourceValue . Contains ( "{0" ) )
{
//Hack for string which used in string.Format
newTextReplacement = newTextReplacement . Replace ( "{" , "{{" ) . Replace ( "}" , "}}" ) ;
}
var replPattern = _configuration [ "resources:whitelabel-text.replacement.pattern" ] ? ? "(?<=[^@/\\\\]|^)({0})(?!\\.com)" ;
var pattern = string . Format ( replPattern , DefaultLogoText ) ;
//Hack for resource strings with mails looked like ...@onlyoffice... or with website http://www.onlyoffice.com link or with the https://www.facebook.com/pages/OnlyOffice/833032526736775
return Regex . Replace ( resourceValue , pattern , newTextReplacement , RegexOptions . IgnoreCase ) . Replace ( "™" , "" ) ;
}
}
catch ( Exception e )
{
2022-05-05 14:13:47 +00:00
_logger . ErrorReplaceLogo ( e ) ;
2022-02-15 11:52:43 +00:00
}
}
return resourceValue ;
}
2019-05-15 14:56:09 +00:00
}