2019-06-14 16:14:24 +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.Generic ;
using System.Data ;
using System.Linq ;
using ASC.Common.Data ;
using ASC.Common.Data.Sql ;
using ASC.Common.Data.Sql.Expressions ;
using ASC.Core ;
using ASC.Core.Tenants ;
using Newtonsoft.Json ;
namespace ASC.Feed.Data
{
public class FeedAggregateDataProvider
{
public static DateTime GetLastTimeAggregate ( string key )
{
var q = new SqlQuery ( "feed_last" )
. Select ( "last_date" )
. Where ( "last_key" , key ) ;
2019-08-15 15:08:40 +00:00
using var db = new DbManager ( Constants . FeedDbId ) ;
var value = db . ExecuteScalar < DateTime > ( q ) ;
return value ! = default ? value . AddSeconds ( 1 ) : value ;
2019-06-14 16:14:24 +00:00
}
public static void SaveFeeds ( IEnumerable < FeedRow > feeds , string key , DateTime value )
{
using ( var db = new DbManager ( Constants . FeedDbId ) )
{
db . ExecuteNonQuery ( new SqlInsert ( "feed_last" , true ) . InColumnValue ( "last_key" , key ) . InColumnValue ( "last_date" , value ) ) ;
}
const int feedsPortionSize = 1000 ;
var aggregatedDate = DateTime . UtcNow ;
var feedsPortion = new List < FeedRow > ( ) ;
foreach ( var feed in feeds )
{
feedsPortion . Add ( feed ) ;
if ( feedsPortion . Sum ( f = > f . Users . Count ) < = feedsPortionSize ) continue ;
SaveFeedsPortion ( feedsPortion , aggregatedDate ) ;
feedsPortion . Clear ( ) ;
}
if ( feedsPortion . Any ( ) )
{
SaveFeedsPortion ( feedsPortion , aggregatedDate ) ;
}
}
private static void SaveFeedsPortion ( IEnumerable < FeedRow > feeds , DateTime aggregatedDate )
{
2019-08-15 15:08:40 +00:00
using var db = new DbManager ( Constants . FeedDbId ) ;
using var tx = db . BeginTransaction ( ) ;
var i = new SqlInsert ( "feed_aggregate" , true )
. InColumns ( "id" , "tenant" , "product" , "module" , "author" , "modified_by" , "group_id" , "created_date" ,
"modified_date" , "json" , "keywords" , "aggregated_date" ) ;
var i2 = new SqlInsert ( "feed_users" , true ) . InColumns ( "feed_id" , "user_id" ) ;
foreach ( var f in feeds )
2019-06-14 16:14:24 +00:00
{
2019-08-15 15:08:40 +00:00
if ( 0 > = f . Users . Count ) continue ;
2019-06-14 16:14:24 +00:00
2019-08-15 15:08:40 +00:00
i . Values ( f . Id , f . Tenant , f . ProductId , f . ModuleId , f . AuthorId , f . ModifiedById , f . GroupId , f . CreatedDate , f . ModifiedDate , f . Json , f . Keywords , aggregatedDate ) ;
2019-06-14 16:14:24 +00:00
2019-08-15 15:08:40 +00:00
if ( f . ClearRightsBeforeInsert )
{
db . ExecuteNonQuery (
new SqlDelete ( "feed_users" )
. Where ( "feed_id" , f . Id )
) ;
}
2019-06-14 16:14:24 +00:00
2019-08-15 15:08:40 +00:00
foreach ( var u in f . Users )
{
i2 . Values ( f . Id , u . ToString ( ) ) ;
2019-06-14 16:14:24 +00:00
}
2019-08-15 15:08:40 +00:00
}
2019-06-14 16:14:24 +00:00
2019-08-15 15:08:40 +00:00
db . ExecuteNonQuery ( i ) ;
db . ExecuteNonQuery ( i2 ) ;
2019-06-14 16:14:24 +00:00
2019-08-15 15:08:40 +00:00
tx . Commit ( ) ;
2019-06-14 16:14:24 +00:00
}
public static void RemoveFeedAggregate ( DateTime fromTime )
{
2019-08-15 15:08:40 +00:00
using var db = new DbManager ( Constants . FeedDbId ) ;
using var command = db . Connection . CreateCommand ( ) ;
using var tx = db . Connection . BeginTransaction ( IsolationLevel . ReadUncommitted ) ;
command . Transaction = tx ;
command . CommandTimeout = 60 * 60 ; // a hour
var dialect = DbRegistry . GetSqlDialect ( Constants . FeedDbId ) ;
if ( dialect . SupportMultiTableUpdate )
2019-06-14 16:14:24 +00:00
{
2019-08-15 15:08:40 +00:00
command . ExecuteNonQuery ( "delete from feed_aggregate, feed_users using feed_aggregate, feed_users where id = feed_id and aggregated_date < @date" , new { date = fromTime } ) ;
2019-06-14 16:14:24 +00:00
}
2019-08-15 15:08:40 +00:00
else
{
command . ExecuteNonQuery ( new SqlDelete ( "feed_users" ) . Where ( Exp . In ( "feed_id" , new SqlQuery ( "feed_aggregate" ) . Select ( "id" ) . Where ( Exp . Lt ( "aggregated_date" , fromTime ) ) ) ) , dialect ) ;
command . ExecuteNonQuery ( new SqlDelete ( "feed_aggregate" ) . Where ( Exp . Lt ( "aggregated_date" , fromTime ) ) , dialect ) ;
}
tx . Commit ( ) ;
2019-06-14 16:14:24 +00:00
}
public static List < FeedResultItem > GetFeeds ( FeedApiFilter filter )
{
var filterOffset = filter . Offset ;
var filterLimit = filter . Max > 0 & & filter . Max < 1000 ? filter . Max : 1000 ;
var feeds = new Dictionary < string , List < FeedResultItem > > ( ) ;
var tryCount = 0 ;
List < FeedResultItem > feedsIteration ;
do
{
feedsIteration = GetFeedsInternal ( filter ) ;
foreach ( var feed in feedsIteration )
{
if ( feeds . ContainsKey ( feed . GroupId ) )
{
feeds [ feed . GroupId ] . Add ( feed ) ;
}
else
{
feeds [ feed . GroupId ] = new List < FeedResultItem > { feed } ;
}
}
filter . Offset + = feedsIteration . Count ;
} while ( feeds . Count < filterLimit
& & feedsIteration . Count = = filterLimit
& & tryCount + + < 5 ) ;
filter . Offset = filterOffset ;
return feeds . Take ( filterLimit ) . SelectMany ( group = > group . Value ) . ToList ( ) ;
}
private static List < FeedResultItem > GetFeedsInternal ( FeedApiFilter filter )
{
var query = new SqlQuery ( "feed_aggregate a" )
. InnerJoin ( "feed_users u" , Exp . EqColumns ( "a.id" , "u.feed_id" ) )
. Select ( "a.json, a.module, a.author, a.modified_by, a.group_id, a.created_date, a.modified_date, a.aggregated_date" )
. Where ( "a.tenant" , CoreContext . TenantManager . GetCurrentTenant ( ) . TenantId )
. Where (
! Exp . Eq ( "a.modified_by" , SecurityContext . CurrentAccount . ID ) &
Exp . Eq ( "u.user_id" , SecurityContext . CurrentAccount . ID )
)
. OrderBy ( "a.modified_date" , false )
. SetFirstResult ( filter . Offset )
. SetMaxResults ( filter . Max ) ;
if ( filter . OnlyNew )
{
query . Where ( Exp . Ge ( "a.aggregated_date" , filter . From ) ) ;
}
else
{
if ( 1 < filter . From . Year )
{
query . Where ( Exp . Ge ( "a.modified_date" , filter . From ) ) ;
}
if ( filter . To . Year < 9999 )
{
query . Where ( Exp . Le ( "a.modified_date" , filter . To ) ) ;
}
}
if ( ! string . IsNullOrEmpty ( filter . Product ) )
{
query . Where ( "a.product" , filter . Product ) ;
}
if ( filter . Author ! = Guid . Empty )
{
query . Where ( "a.modified_by" , filter . Author ) ;
}
if ( filter . SearchKeys ! = null & & filter . SearchKeys . Length > 0 )
{
var exp = filter . SearchKeys
. Where ( s = > ! string . IsNullOrEmpty ( s ) )
. Select ( s = > s . Replace ( "\\" , "\\\\" ) . Replace ( "%" , "\\%" ) . Replace ( "_" , "\\_" ) )
. Aggregate ( Exp . False , ( cur , s ) = > cur | Exp . Like ( "a.keywords" , s , SqlLike . AnyWhere ) ) ;
query . Where ( exp ) ;
}
2019-08-15 15:08:40 +00:00
using var db = new DbManager ( Constants . FeedDbId ) ;
var news = db
. ExecuteList ( query )
. ConvertAll ( r = > new FeedResultItem (
Convert . ToString ( r [ 0 ] ) ,
Convert . ToString ( r [ 1 ] ) ,
new Guid ( Convert . ToString ( r [ 2 ] ) ) ,
new Guid ( Convert . ToString ( r [ 3 ] ) ) ,
Convert . ToString ( r [ 4 ] ) ,
TenantUtil . DateTimeFromUtc ( Convert . ToDateTime ( r [ 5 ] ) ) ,
TenantUtil . DateTimeFromUtc ( Convert . ToDateTime ( r [ 6 ] ) ) ,
TenantUtil . DateTimeFromUtc ( Convert . ToDateTime ( r [ 7 ] ) ) ) ) ;
return news ;
2019-06-14 16:14:24 +00:00
}
public static int GetNewFeedsCount ( DateTime lastReadedTime )
{
var q = new SqlQuery ( "feed_aggregate a" )
. Select ( "id" )
. Where ( "a.tenant" , CoreContext . TenantManager . GetCurrentTenant ( ) . TenantId )
. Where ( ! Exp . Eq ( "a.modified_by" , SecurityContext . CurrentAccount . ID ) )
. InnerJoin ( "feed_users u" , Exp . EqColumns ( "a.id" , "u.feed_id" ) )
. Where ( "u.user_id" , SecurityContext . CurrentAccount . ID )
. SetMaxResults ( 1001 ) ;
2019-08-15 12:04:42 +00:00
2019-06-14 16:14:24 +00:00
if ( 1 < lastReadedTime . Year )
{
q . Where ( Exp . Ge ( "a.aggregated_date" , lastReadedTime ) ) ;
}
2019-08-15 15:08:40 +00:00
using var db = new DbManager ( Constants . FeedDbId ) ;
return db . ExecuteList ( q ) . Count ( ) ;
2019-06-14 16:14:24 +00:00
}
public IEnumerable < int > GetTenants ( TimeInterval interval )
{
2019-08-15 15:08:40 +00:00
using var db = new DbManager ( Constants . FeedDbId ) ;
var q = new SqlQuery ( "feed_aggregate" )
. Select ( "tenant" )
. Where ( Exp . Between ( "aggregated_date" , interval . From , interval . To ) )
. GroupBy ( 1 ) ;
return db . ExecuteList ( q ) . ConvertAll ( r = > Convert . ToInt32 ( r [ 0 ] ) ) ;
2019-06-14 16:14:24 +00:00
}
public static FeedResultItem GetFeedItem ( string id )
{
var query = new SqlQuery ( "feed_aggregate a" )
. Select ( "a.json, a.module, a.author, a.modified_by, a.group_id, a.created_date, a.modified_date, a.aggregated_date" )
. Where ( "a.id" , id ) ;
2019-08-15 15:08:40 +00:00
using var db = new DbManager ( Constants . FeedDbId ) ;
var news = db
. ExecuteList ( query )
. ConvertAll ( r = > new FeedResultItem (
Convert . ToString ( r [ 0 ] ) ,
Convert . ToString ( r [ 1 ] ) ,
new Guid ( Convert . ToString ( r [ 2 ] ) ) ,
new Guid ( Convert . ToString ( r [ 3 ] ) ) ,
Convert . ToString ( r [ 4 ] ) ,
TenantUtil . DateTimeFromUtc ( Convert . ToDateTime ( r [ 5 ] ) ) ,
TenantUtil . DateTimeFromUtc ( Convert . ToDateTime ( r [ 6 ] ) ) ,
TenantUtil . DateTimeFromUtc ( Convert . ToDateTime ( r [ 7 ] ) ) ) ) ;
return news . FirstOrDefault ( ) ;
2019-06-14 16:14:24 +00:00
}
public static void RemoveFeedItem ( string id )
{
2019-08-15 15:08:40 +00:00
using var db = new DbManager ( Constants . FeedDbId ) ;
using var command = db . Connection . CreateCommand ( ) ;
using var tx = db . Connection . BeginTransaction ( IsolationLevel . ReadUncommitted ) ;
command . Transaction = tx ;
command . CommandTimeout = 60 * 60 ; // a hour
2019-06-14 16:14:24 +00:00
2019-08-15 15:08:40 +00:00
var dialect = DbRegistry . GetSqlDialect ( Constants . FeedDbId ) ;
2019-06-14 16:14:24 +00:00
2019-08-15 15:08:40 +00:00
command . ExecuteNonQuery ( new SqlDelete ( "feed_users" ) . Where ( "feed_id" , id ) , dialect ) ;
command . ExecuteNonQuery ( new SqlDelete ( "feed_aggregate" ) . Where ( "id" , id ) , dialect ) ;
2019-06-14 16:14:24 +00:00
2019-08-15 15:08:40 +00:00
tx . Commit ( ) ;
2019-06-14 16:14:24 +00:00
}
}
public class FeedResultItem
{
public FeedResultItem (
2019-08-15 12:04:42 +00:00
string json ,
string module ,
Guid authorId ,
2019-06-14 16:14:24 +00:00
Guid modifiedById ,
2019-08-15 12:04:42 +00:00
string groupId ,
2019-06-14 16:14:24 +00:00
DateTime createdDate ,
2019-08-15 12:04:42 +00:00
DateTime modifiedDate ,
2019-06-14 16:14:24 +00:00
DateTime aggregatedDate )
{
var now = TenantUtil . DateTimeFromUtc ( DateTime . UtcNow ) ;
Json = json ;
Module = module ;
AuthorId = authorId ;
ModifiedById = modifiedById ;
2019-08-15 12:04:42 +00:00
2019-06-14 16:14:24 +00:00
GroupId = groupId ;
if ( now . Year = = createdDate . Year & & now . Date = = createdDate . Date )
{
IsToday = true ;
}
else if ( now . Year = = createdDate . Year & & now . Date = = createdDate . Date . AddDays ( 1 ) )
{
IsYesterday = true ;
}
CreatedDate = createdDate ;
ModifiedDate = modifiedDate ;
AggregatedDate = aggregatedDate ;
}
public string Json { get ; private set ; }
public string Module { get ; private set ; }
public Guid AuthorId { get ; private set ; }
public Guid ModifiedById { get ; private set ; }
public string GroupId { get ; private set ; }
public bool IsToday { get ; private set ; }
public bool IsYesterday { get ; private set ; }
public DateTime CreatedDate { get ; private set ; }
public DateTime ModifiedDate { get ; private set ; }
public DateTime AggregatedDate { get ; private set ; }
public FeedMin ToFeedMin ( )
{
var feedMin = JsonConvert . DeserializeObject < FeedMin > ( Json ) ;
feedMin . Author = new FeedMinUser { UserInfo = CoreContext . UserManager . GetUsers ( feedMin . AuthorId ) } ;
feedMin . CreatedDate = CreatedDate ;
if ( feedMin . Comments = = null ) return feedMin ;
foreach ( var comment in feedMin . Comments )
{
comment . Author = new FeedMinUser { UserInfo = CoreContext . UserManager . GetUsers ( comment . AuthorId ) } ;
}
return feedMin ;
}
}
}