2019-05-15 14:56:09 +00:00
/ *
*
* ( c ) Copyright Ascensio System Limited 2010 - 2018
*
* This program is freeware . You can redistribute it and / or modify it under the terms of the GNU
* General Public License ( GPL ) version 3 as published by the Free Software Foundation ( https : //www.gnu.org/copyleft/gpl.html).
* In accordance with Section 7 ( a ) of the GNU GPL 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 more details , see GNU GPL at https : //www.gnu.org/copyleft/gpl.html
*
* You can contact Ascensio System SIA by email at sales @onlyoffice . com
*
* The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display
* Appropriate Legal Notices , as required under Section 5 of the GNU GPL version 3.
*
* Pursuant to Section 7 § 3 ( b ) of the GNU GPL you must retain the original ONLYOFFICE logo which contains
* relevant author attributions when distributing the software . If the display of the logo in its graphic
* form is not reasonably feasible for technical reasons , you must include the words "Powered by ONLYOFFICE"
* in every copy of the program you distribute .
* Pursuant to Section 7 § 3 ( e ) we decline to grant you any rights under trademark law for use of our trademarks .
*
* /
using System ;
using System.Collections ;
using System.Collections.Concurrent ;
using System.Collections.Generic ;
using System.Globalization ;
using System.Linq ;
using System.Reflection ;
using System.Resources ;
using System.Runtime.Caching ;
using System.Text.RegularExpressions ;
2020-02-17 08:58:14 +00:00
using System.Web ;
2020-07-14 10:03:53 +00:00
2020-02-17 08:58:14 +00:00
using ASC.Common ;
2019-05-15 14:56:09 +00:00
using ASC.Common.Logging ;
2020-02-17 08:58:14 +00:00
using ASC.Core ;
using ASC.Core.Common.EF ;
using ASC.Core.Common.EF.Context ;
2019-11-06 15:03:09 +00:00
using Microsoft.AspNetCore.Http ;
using Microsoft.Extensions.Configuration ;
using Microsoft.Extensions.Options ;
2019-05-15 14:56:09 +00:00
namespace TMResourceData
{
public class DBResourceManager : ResourceManager
{
public static bool WhiteLableEnabled = false ;
private readonly ConcurrentDictionary < string , ResourceSet > resourceSets = new ConcurrentDictionary < string , ResourceSet > ( ) ;
public DBResourceManager ( string filename , Assembly assembly )
: base ( filename , assembly )
{
2020-02-17 08:58:14 +00:00
}
2019-12-16 09:30:01 +00:00
2020-02-17 08:58:14 +00:00
public DBResourceManager (
IConfiguration configuration ,
IOptionsMonitor < ILog > option ,
DbContextManager < ResourceDbContext > dbContext ,
string filename ,
2019-12-16 09:30:01 +00:00
Assembly assembly )
2019-10-09 10:09:54 +00:00
: base ( filename , assembly )
2019-11-06 15:03:09 +00:00
{
Configuration = configuration ;
2020-02-17 08:58:14 +00:00
Option = option ;
DbContext = dbContext ;
2019-10-09 10:09:54 +00:00
}
2019-05-15 14:56:09 +00:00
2019-11-06 15:03:09 +00:00
public static void PatchAssemblies ( IOptionsMonitor < ILog > option )
2019-05-15 14:56:09 +00:00
{
2019-10-22 11:21:44 +00:00
AppDomain . CurrentDomain . AssemblyLoad + = ( _ , a ) = > PatchAssembly ( option , a . LoadedAssembly ) ;
Array . ForEach ( AppDomain . CurrentDomain . GetAssemblies ( ) , a = > PatchAssembly ( option , a ) ) ;
2019-05-15 14:56:09 +00:00
}
2019-11-06 15:03:09 +00:00
public static void PatchAssembly ( IOptionsMonitor < ILog > option , Assembly a , bool onlyAsc = true )
{
var log = option . CurrentValue ;
2019-10-22 11:21:44 +00:00
2019-05-15 14:56:09 +00:00
if ( ! onlyAsc | | Accept ( a ) )
{
var types = new Type [ 0 ] ;
try
{
types = a . GetTypes ( ) ;
}
catch ( ReflectionTypeLoadException rtle )
{
log . WarnFormat ( "Can not GetTypes() from assembly {0}, try GetExportedTypes(), error: {1}" , a . FullName , rtle . Message ) ;
foreach ( var e in rtle . LoaderExceptions )
{
log . Info ( e . Message ) ;
}
try
{
types = a . GetExportedTypes ( ) ;
}
catch ( Exception err )
{
log . ErrorFormat ( "Can not GetExportedTypes() from assembly {0}: {1}" , a . FullName , err . Message ) ;
}
}
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 ) ;
if ( ! ( rm is DBResourceManager ) )
{
var dbrm = new DBResourceManager ( rm . BaseName , a ) ;
2020-10-12 19:39:23 +00:00
type . InvokeMember ( "resourceMan" , BindingFlags . Static | BindingFlags . NonPublic | BindingFlags . SetField , null , type , new object [ ] { dbrm } ) ;
2019-05-15 14:56:09 +00:00
}
}
}
}
}
private static bool Accept ( Assembly a )
{
var n = a . GetName ( ) . Name ;
return ( n . StartsWith ( "ASC." ) | | n . StartsWith ( "App_GlobalResources" ) ) & & a . GetManifestResourceNames ( ) . Any ( ) ;
}
public override Type ResourceSetType
{
get { return typeof ( DBResourceSet ) ; }
2019-11-06 15:03:09 +00:00
}
2020-08-12 09:58:08 +00:00
private IConfiguration Configuration { get ; }
private IOptionsMonitor < ILog > Option { get ; }
private DbContextManager < ResourceDbContext > DbContext { get ; }
2020-02-17 08:58:14 +00:00
2019-05-15 14:56:09 +00:00
protected override ResourceSet InternalGetResourceSet ( CultureInfo culture , bool createIfNotExists , bool tryParents )
{
2020-10-12 19:39:23 +00:00
resourceSets . TryGetValue ( culture . Name , out var set ) ;
2019-05-15 14:56:09 +00:00
if ( set = = null )
{
var invariant = culture = = CultureInfo . InvariantCulture ? base . InternalGetResourceSet ( CultureInfo . InvariantCulture , true , true ) : null ;
2019-12-16 09:30:01 +00:00
set = new DBResourceSet ( Configuration , Option , DbContext , invariant , culture , BaseName ) ;
2020-10-12 19:39:23 +00:00
resourceSets . AddOrUpdate ( culture . Name , set , ( k , v ) = > set ) ;
2019-05-15 14:56:09 +00:00
}
return set ;
}
class DBResourceSet : ResourceSet
{
private const string NEUTRAL_CULTURE = "Neutral" ;
2019-10-09 10:09:54 +00:00
private readonly TimeSpan cacheTimeout = TimeSpan . FromMinutes ( 120 ) ; // for performance
2019-05-15 14:56:09 +00:00
private readonly object locker = new object ( ) ;
private readonly MemoryCache cache ;
private readonly ResourceSet invariant ;
private readonly string culture ;
2019-11-06 15:03:09 +00:00
private readonly string filename ;
private readonly ILog log ;
2020-08-12 09:58:08 +00:00
private DbContextManager < ResourceDbContext > DbContext { get ; }
2020-02-17 08:58:14 +00:00
public DBResourceSet (
IConfiguration configuration ,
IOptionsMonitor < ILog > option ,
DbContextManager < ResourceDbContext > dbContext ,
ResourceSet invariant ,
CultureInfo culture ,
2019-12-16 09:30:01 +00:00
string filename )
2019-05-15 14:56:09 +00:00
{
if ( culture = = null )
{
throw new ArgumentNullException ( "culture" ) ;
}
if ( string . IsNullOrEmpty ( filename ) )
{
throw new ArgumentNullException ( "filename" ) ;
2019-11-06 15:03:09 +00:00
}
2020-02-17 08:58:14 +00:00
DbContext = dbContext ;
2019-11-06 15:03:09 +00:00
log = option . CurrentValue ;
2019-10-09 10:09:54 +00:00
try
{
var defaultValue = ( ( int ) cacheTimeout . TotalMinutes ) . ToString ( ) ;
cacheTimeout = TimeSpan . FromMinutes ( Convert . ToInt32 ( configuration [ "resources:cache-timeout" ] ? ? defaultValue ) ) ;
2019-05-15 14:56:09 +00:00
}
2019-10-09 10:09:54 +00:00
catch ( Exception err )
{
log . Error ( err ) ;
2019-11-06 15:03:09 +00:00
}
2019-05-15 14:56:09 +00:00
this . invariant = invariant ;
this . culture = invariant ! = null ? NEUTRAL_CULTURE : culture . Name ;
this . filename = filename . Split ( '.' ) . Last ( ) + ".resx" ;
cache = MemoryCache . Default ;
}
public override string GetString ( string name , bool ignoreCase )
{
var result = ( string ) null ;
try
{
var dic = GetResources ( ) ;
2020-10-12 19:39:23 +00:00
dic . TryGetValue ( name , out result ) ;
2019-05-15 14:56:09 +00:00
}
catch ( Exception err )
{
log . ErrorFormat ( "Can not get resource from {0} for {1}: GetString({2}), {3}" , filename , culture , name , err ) ;
}
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 )
{
log . Error ( err ) ;
}
return result . GetEnumerator ( ) ;
}
private Dictionary < string , string > GetResources ( )
{
var key = string . Format ( "{0}/{1}" , filename , culture ) ;
2019-08-15 13:41:38 +00:00
if ( ! ( cache . Get ( key ) is Dictionary < string , string > dic ) )
2019-05-15 14:56:09 +00:00
{
lock ( locker )
{
dic = cache . Get ( key ) as Dictionary < string , string > ;
if ( dic = = null )
{
var policy = cacheTimeout = = TimeSpan . Zero ? null : new CacheItemPolicy ( ) { AbsoluteExpiration = DateTimeOffset . Now . Add ( cacheTimeout ) } ;
cache . Set ( key , dic = LoadResourceSet ( filename , culture ) , policy ) ;
}
}
}
return dic ;
}
2019-10-10 08:52:21 +00:00
private Dictionary < string , string > LoadResourceSet ( string filename , string culture )
2020-02-17 08:58:14 +00:00
{
2019-12-16 09:30:01 +00:00
using var context = DbContext . Get ( "tmresource" ) ;
2020-02-17 08:58:14 +00:00
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 ) ;
2019-12-16 09:30:01 +00:00
return q
. ToDictionary ( r = > r . data . Title , r = > r . data . TextValue , StringComparer . InvariantCultureIgnoreCase ) ;
2019-05-15 14:56:09 +00:00
}
}
}
2020-10-19 15:53:15 +00:00
[Singletone]
2019-10-22 11:21:44 +00:00
public class WhiteLabelHelper
2019-05-15 14:56:09 +00:00
{
2019-10-22 11:21:44 +00:00
private readonly ILog log ;
private readonly ConcurrentDictionary < int , string > whiteLabelDictionary ;
2019-11-06 15:03:09 +00:00
public string DefaultLogoText ;
2020-08-12 09:58:08 +00:00
private IConfiguration Configuration { get ; }
2019-11-06 15:03:09 +00:00
public WhiteLabelHelper ( IConfiguration configuration , IOptionsMonitor < ILog > option )
{
log = option . Get ( "ASC.Resources" ) ;
whiteLabelDictionary = new ConcurrentDictionary < int , string > ( ) ;
DefaultLogoText = "" ;
Configuration = configuration ;
2019-10-22 11:21:44 +00:00
}
2019-05-15 14:56:09 +00:00
2019-10-22 11:21:44 +00:00
public void SetNewText ( int tenantId , string newText )
2019-05-15 14:56:09 +00:00
{
try
{
2020-10-12 19:39:23 +00:00
whiteLabelDictionary . AddOrUpdate ( tenantId , r = > newText , ( i , s ) = > newText ) ;
2019-05-15 14:56:09 +00:00
}
catch ( Exception e )
{
log . Error ( "SetNewText" , e ) ;
}
}
2019-10-22 11:21:44 +00:00
public void RestoreOldText ( int tenantId )
2019-05-15 14:56:09 +00:00
{
try
{
2020-10-12 19:39:23 +00:00
whiteLabelDictionary . TryRemove ( tenantId , out var text ) ;
2019-05-15 14:56:09 +00:00
}
catch ( Exception e )
{
log . Error ( "RestoreOldText" , e ) ;
}
}
2019-10-22 11:21:44 +00:00
internal string ReplaceLogo ( TenantManager tenantManager , IHttpContextAccessor httpContextAccessor , string resourceName , string resourceValue )
2019-05-15 14:56:09 +00:00
{
if ( string . IsNullOrEmpty ( resourceValue ) )
{
return resourceValue ;
2019-11-06 15:03:09 +00:00
}
if ( ! DBResourceManager . WhiteLableEnabled )
{
return resourceValue ;
}
2019-05-15 14:56:09 +00:00
2019-10-18 08:48:27 +00:00
if ( httpContextAccessor . HttpContext ! = null ) //if in Notify Service or other process without HttpContext
2019-05-15 14:56:09 +00:00
{
try
{
2019-10-11 08:57:35 +00:00
var tenant = tenantManager . GetCurrentTenant ( false ) ;
2019-05-15 14:56:09 +00:00
if ( tenant = = null ) return resourceValue ;
2019-08-15 13:05:50 +00:00
if ( whiteLabelDictionary . TryGetValue ( tenant . TenantId , out var newText ) )
2019-05-15 14:56:09 +00:00
{
var newTextReplacement = newText ;
if ( resourceValue . Contains ( "<" ) & & resourceValue . Contains ( ">" ) | | resourceName . StartsWith ( "pattern_" ) )
{
newTextReplacement = HttpUtility . HtmlEncode ( newTextReplacement ) ;
}
if ( resourceValue . Contains ( "{0" ) )
{
//Hack for string which used in string.Format
newTextReplacement = newTextReplacement . Replace ( "{" , "{{" ) . Replace ( "}" , "}}" ) ;
2019-11-06 15:03:09 +00:00
}
var replPattern = Configuration [ "resources:whitelabel-text.replacement.pattern" ] ? ? "(?<=[^@/\\\\]|^)({0})(?!\\.com)" ;
2019-05-15 14:56:09 +00:00
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 )
{
log . Error ( "ReplaceLogo" , e ) ;
}
}
return resourceValue ;
}
2019-11-06 15:03:09 +00:00
}
2019-05-15 14:56:09 +00:00
}