Merge branch 'master' of github.com:ONLYOFFICE/CommunityServer-AspNetCore

This commit is contained in:
Alexey Safronov 2019-08-05 18:13:12 +03:00
commit a2288468ff
12 changed files with 368 additions and 60 deletions

View File

@ -89,7 +89,12 @@ namespace ASC.Core.Caching
{
return (from == default(DateTime) ? users.Values : users.Values.Where(u => u.LastModified >= from)).ToDictionary(u => u.ID);
}
}
}
public IDictionary<Guid, UserInfo> GetUsers(int tenant, bool isAdmin, EmployeeStatus? employeeStatus, List<Guid> includeGroups, List<Guid> excludeGroups, EmployeeActivationStatus? activationStatus, string text, string sortBy, bool sortOrderAsc, long limit, long offset, out int total)
{
return service.GetUsers(tenant, isAdmin, employeeStatus, includeGroups, excludeGroups, activationStatus, text, sortBy, sortOrderAsc, limit, offset, out total);
}
public UserInfo GetUser(int tenant, Guid id)
{
@ -411,7 +416,6 @@ namespace ASC.Core.Caching
return tenant.ToString() + USERS + userId;
}
[Serializable]
class UserPhoto
{

View File

@ -85,7 +85,13 @@ namespace ASC.Core
break;
}
return users.ToArray();
}
}
public IEnumerable<UserInfo> GetUsers(bool isAdmin, EmployeeStatus? employeeStatus, List<Guid> includeGroups, List<Guid> excludeGroups, EmployeeActivationStatus? activationStatus, string text, string sortBy, bool sortOrderAsc, long limit, long offset, out int total)
{
var tenantId = CoreContext.TenantManager.GetCurrentTenant().TenantId;
return userService.GetUsers(tenantId, isAdmin, employeeStatus, includeGroups, excludeGroups, activationStatus, text, sortBy, sortOrderAsc, limit, offset, out total).Values;
}
public DateTime GetMaxUsersLastModified()
{

View File

@ -33,6 +33,18 @@ namespace ASC.Core
public interface IUserService
{
IDictionary<Guid, UserInfo> GetUsers(int tenant, DateTime from);
IDictionary<Guid, UserInfo> GetUsers(int tenant, bool isAdmin,
EmployeeStatus? employeeStatus,
List<Guid> includeGroups,
List<Guid> excludeGroups,
EmployeeActivationStatus? activationStatus,
string text,
string sortBy,
bool sortOrderAsc,
long limit,
long offset,
out int total);
UserInfo GetUser(int tenant, Guid id);

View File

@ -24,16 +24,16 @@
*/
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using ASC.Common.Data.Sql;
using ASC.Common.Data.Sql.Expressions;
using ASC.Core.Tenants;
using ASC.Core.Users;
using ASC.Security.Cryptography;
using System;
using System.Collections.Generic;
using System.Configuration;
using System.Linq;
using System.Text.RegularExpressions;
namespace ASC.Core.Data
{
@ -50,6 +50,112 @@ namespace ASC.Core.Data
return ExecList(q).ConvertAll(ToUser).ToDictionary(u => u.ID);
}
public IDictionary<Guid, UserInfo> GetUsers(int tenant, bool isAdmin,
EmployeeStatus? employeeStatus,
List<Guid> includeGroups,
List<Guid> excludeGroups,
EmployeeActivationStatus? activationStatus,
string text,
string sortBy,
bool sortOrderAsc,
long limit,
long offset,
out int total)
{
var totalQuery = new SqlQuery("core_user u").Where("u.tenant", tenant).SelectCount();
GetUserQueryForFilter(totalQuery, isAdmin, employeeStatus, includeGroups, excludeGroups, activationStatus, text);
total = ExecScalar<int>(totalQuery);
var q = GetUserQuery(tenant, default);
q = GetUserQueryForFilter(q, isAdmin, employeeStatus, includeGroups, excludeGroups, activationStatus, text);
if (!string.IsNullOrEmpty(sortBy))
{
q.OrderBy(sortBy, sortOrderAsc);
}
if(limit != 0)
{
q.SetMaxResults((int)limit);
}
if(offset != 0)
{
q.SetFirstResult((int)offset);
}
return ExecList(q).ConvertAll(ToUser).ToDictionary(u => u.ID);
}
private SqlQuery GetUserQueryForFilter(SqlQuery q,bool isAdmin,
EmployeeStatus? employeeStatus,
List<Guid> includeGroups,
List<Guid> excludeGroups,
EmployeeActivationStatus? activationStatus,
string text)
{
q.Where("u.removed", false);
if (includeGroups != null && includeGroups.Any())
{
foreach (var g in includeGroups)
{
var groupQuery = new SqlQuery("core_usergroup cug")
.Where(Exp.EqColumns("cug.tenant", "cu.tenant"))
.Where(Exp.EqColumns("cu.id", "cug.userid"))
.Where(Exp.Eq("cug.groupid", g));
q.Where(Exp.Exists(groupQuery));
}
}
if (excludeGroups != null && excludeGroups.Any())
{
foreach (var g in excludeGroups)
{
var groupQuery = new SqlQuery("core_usergroup cug")
.Where(Exp.EqColumns("cug.tenant", "cu.tenant"))
.Where(Exp.EqColumns("cu.id", "cug.userid"))
.Where(Exp.Eq("cug.groupid", g));
q.Where(!Exp.Exists(groupQuery));
}
}
if (employeeStatus != null)
{
switch (employeeStatus)
{
case EmployeeStatus.LeaveOfAbsence:
case EmployeeStatus.Terminated:
if (!isAdmin) q.Where("u.status", EmployeeStatus.Terminated);
break;
case EmployeeStatus.All:
if (!isAdmin) q.Where("u.status", EmployeeStatus.Active);
break;
case EmployeeStatus.Default:
case EmployeeStatus.Active:
q.Where("u.status", EmployeeStatus.Active);
break;
}
}
if (activationStatus != null)
{
q.Where("u.activation_status", activationStatus.Value);
}
if (!string.IsNullOrEmpty(text))
{
q.Where(Exp.Like("u.firstname", text, SqlLike.AnyWhere) |
Exp.Like("u.lastname", text, SqlLike.AnyWhere) |
Exp.Like("u.title", text, SqlLike.AnyWhere) |
Exp.Like("u.location", text, SqlLike.AnyWhere) |
Exp.Like("u.email", text, SqlLike.AnyWhere));
}
return q;
}
public UserInfo GetUser(int tenant, Guid id)
{
var q = GetUserQuery(tenant, default(DateTime)).Where("id", id);
@ -329,11 +435,11 @@ namespace ASC.Core.Data
var where = Exp.Empty;
if (tenant != Tenant.DEFAULT_TENANT)
{
where &= Exp.Eq("tenant", tenant);
where &= Exp.Eq("u.tenant", tenant);
}
if (from != default(DateTime))
{
where &= Exp.Ge("last_modified", from);
where &= Exp.Ge("u.last_modified", from);
}
if (where != Exp.Empty)
{

View File

@ -271,75 +271,40 @@ namespace ASC.Employee.Core.Controllers
if (CoreContext.Configuration.Personal) throw new MethodAccessException("Method not available");
var isAdmin = CoreContext.UserManager.GetUsers(SecurityContext.CurrentAccount.ID).IsAdmin() ||
WebItemSecurity.IsProductAdministrator(WebItemManager.PeopleProductID, SecurityContext.CurrentAccount.ID);
var status = isAdmin ? EmployeeStatus.All : EmployeeStatus.Default;
if (employeeStatus != null)
var includeGroups = new List<Guid>();
if (groupId.HasValue)
{
switch (employeeStatus)
{
case EmployeeStatus.Terminated:
case EmployeeStatus.All:
status = isAdmin ? (EmployeeStatus)employeeStatus : EmployeeStatus.Default;
break;
default:
status = (EmployeeStatus)employeeStatus;
break;
}
includeGroups.Add(groupId.Value);
}
var users = string.IsNullOrEmpty(ApiContext.FilterValue) ?
CoreContext.UserManager.GetUsers(status).AsEnumerable() :
CoreContext.UserManager.Search(ApiContext.FilterValue, status).AsEnumerable();
var excludeGroups = new List<Guid>();
if (groupId != null && !groupId.Equals(Guid.Empty))
{
users = users.Where(x => CoreContext.UserManager.IsUserInGroup(x.ID, (Guid)groupId));
}
if (activationStatus != null)
{
users = activationStatus == EmployeeActivationStatus.Activated ?
users.Where(x => x.ActivationStatus.HasFlag(EmployeeActivationStatus.Activated)) :
users.Where(x => x.ActivationStatus == EmployeeActivationStatus.NotActivated ||
x.ActivationStatus == EmployeeActivationStatus.Pending ||
x.ActivationStatus == EmployeeActivationStatus.AutoGenerated);
}
if (employeeType != null)
{
switch (employeeType)
{
case EmployeeType.User:
users = users.Where(x => !x.IsVisitor());
excludeGroups.Add(Constants.GroupVisitor.ID);
break;
case EmployeeType.Visitor:
users = users.Where(x => x.IsVisitor());
includeGroups.Add(Constants.GroupVisitor.ID);
break;
}
}
if (isAdministrator.HasValue && isAdministrator.Value)
{
users = users.Where(x => x.IsAdmin() || x.GetListAdminModules().Any());
includeGroups.Add(Constants.GroupAdmin.ID);
var products = WebItemManager.Instance.GetItemsAll().Where(i => i is IProduct || i.ID == WebItemManager.MailProductID);
includeGroups.AddRange(products.Select(r=> r.ID));
}
ApiContext.TotalCount = users.Count();
var users = CoreContext.UserManager.GetUsers(isAdmin, employeeStatus, includeGroups, excludeGroups, activationStatus, ApiContext.FilterValue, ApiContext.SortBy, !ApiContext.SortDescending, ApiContext.Count - 1, ApiContext.StartIndex, out int total);
switch (ApiContext.SortBy)
{
case "firstname":
users = ApiContext.SortDescending ? users.OrderByDescending(r => r, UserInfoComparer.FirstName) : users.OrderBy(r => r, UserInfoComparer.FirstName);
break;
case "lastname":
users = ApiContext.SortDescending ? users.OrderByDescending(r => r, UserInfoComparer.LastName) : users.OrderBy(r => r, UserInfoComparer.LastName);
break;
default:
users = ApiContext.SortDescending ? users.OrderByDescending(r => r, UserInfoComparer.Default) : users.OrderBy(r => r, UserInfoComparer.Default);
break;
}
users = users.Skip((int)ApiContext.StartIndex).Take((int)ApiContext.Count - 1);
//ApiContext.SetDataSorted();
//ApiContext.SetDataPaginated();
ApiContext.SetTotalCount(total);
return users;
}

View File

@ -141,6 +141,7 @@ import OrigRadiobuttonHoverCheckedIcon from './radiobutton.hover.checked.react.s
import OrigToggleButtonCheckedIcon from './toggle.button.checked.react.svg';
import OrigToggleButtonIcon from './toggle.button.react.svg';
import OrigTabsIcon from './tabs.react.svg';
export const AZSortingIcon = createStyledIcon(
OrigAZSortingIcon,
@ -698,4 +699,10 @@ export const ToggleButtonIcon = createStyledIcon(
OrigToggleButtonIcon,
'ToggleButtonIcon',
"rect"
);
export const TabsIcon = createStyledIcon(
OrigTabsIcon,
'TabsIcon',
"rect"
);

View File

@ -0,0 +1,3 @@
<svg width="80" height="32" viewBox="0 0 80 32" fill="none" xmlns="http://www.w3.org/2000/svg">
<rect width="80" height="32" rx="16" fill="#265A8F"/>
</svg>

After

Width:  |  Height:  |  Size: 157 B

View File

@ -0,0 +1,95 @@
import React, { Component } from 'react';
import PropTypes from 'prop-types';
import styled, { css } from 'styled-components';
import { Icons } from '../icons';
import { getCssFromSvg } from '../icons/get-css-from-svg';
import ReactDOMServer from 'react-dom/server';
var tabsIcon/*, tabsIcon2*/;
(function () { tabsIcon = getCssFromSvg(ReactDOMServer.renderToString(<Icons.TabsIcon />)); }());
//tabsIcon2 = getCssFromSvg(ReactDOMServer.renderToString(<Icons.TabsIcon />));
// Основной контейнер
const TabsContainer = styled.div``;
// Шапка
const NavItem = styled.div`
position: relative;
box-shadow: 0px 5px 20px rgba(0,0,0,0.13);
`;
//Исправить!!!
const hoverCss = css`
width: 80px;
height: 32px;
background-color: #F8F9F9;
border-radius: 16px;
`;
// Заголовки шапки
const Label = styled.label`
margin:0;
min-width: 80px;
position: relative;
background-repeat: no-repeat;
p {text-align: center; margin-top: 6px;}
margin-right: 5px;
label {margin:0}
${props => props.selected ?
`background-image: url("data:image/svg+xml,${tabsIcon}"); cursor: default; p {color: #fff}` :
`background-image: none;
&:hover{
cursor: pointer;
${hoverCss}
}`
}
`;
// Контенn в зависимости от шапки
const BodyContainer = styled.div``;
class Tabs extends Component {
constructor(props) {
super(props);
this.state = {
activeTab: '0'
};
}
labelClick = (tab) => {
if (this.state.activeTab !== tab.id) {
this.setState({ activeTab: tab.id });
console.log('My update setState()');
}
};
render() {
//console.log(this.props.selected);
return (
<TabsContainer>
<NavItem>
{this.props.children.map((item) =>
<Label
selected={item.id === this.state.activeTab}
key={item.id}
onClick={() => {
this.labelClick(item);
}}>
{item.title}
</Label>
)}
</NavItem>
<BodyContainer> {this.props.children[this.state.activeTab].body} </BodyContainer>
</TabsContainer>
);
}
}
export default Tabs;
Tabs.propTypes = {
children: PropTypes.object
};
Tabs.defaultProps = {/*isChecked: false*/ };

View File

@ -31,7 +31,7 @@ const HiddenInput = styled.input`
z-index: -1;
`;
const CheckboxIcon = ({ isChecked }) => {
const ToggleIcon = ({ isChecked }) => {
const iconName = isChecked ? "ToggleButtonCheckedIcon" : "ToggleButtonIcon";
return <>{React.createElement(Icons[iconName])}</>;
};
@ -65,7 +65,7 @@ class ToggleButton extends Component {
onChange={this.onInputChange}
{...this.props}
/>
<CheckboxIcon {...this.props} />
<ToggleIcon {...this.props} />
{this.props.label && (
<Text.Body
tag="span"

View File

@ -40,3 +40,4 @@ export { default as RadioButton } from './components/radio-button'
export { default as TextArea } from './components/text-area'
export { default as ToggleButton } from './components/toggle-button'
export { default as LinkWithDropdown } from './components/link-with-dropdown'
export { default as Tabs } from './components/tabs'

View File

@ -0,0 +1,19 @@
# Scrollbar
#### Description
Scrollbar is used for displaying custom scroollbar
#### Usage
```js
import { Scrollbar } from 'asc-web-components';
<Scrollbar>Some content</Scrollbar>
```
#### Properties
| Props | Type | Required | Values | Default | Description |
| ---------- | ----------- | :------: | ----------------------------------------- | ------------ | --------------------- |
| `stype` | `string` | | `smallWhite`, `smallBlack`, `preMediumBlack`, `mediumBlack` | `smallBlack` | Scroollbar style type |

View File

@ -0,0 +1,90 @@
import React from 'react';
import { storiesOf } from '@storybook/react';
import { withKnobs, boolean, text, select } from '@storybook/addon-knobs/react';
import { action } from '@storybook/addon-actions';
import withReadme from 'storybook-readme/with-readme';
import Readme from './README.md';
import { Tabs, Text } from 'asc-web-components';
import Section from '../../.storybook/decorators/section';
import { BooleanValue } from 'react-values';
//<Text.Body tag="span">{text("Body text", "Try again later")}</Text.Body>
const something_items = [
{
id: "0",
title: <Text.Body> Title1 </Text.Body>,
body:
<div >
<div> <button>BUTTON</button> <button>BUTTON</button> <button>BUTTON</button> </div>
<div> <button>BUTTON</button> <button>BUTTON</button> <button>BUTTON</button> </div>
<div> <button>BUTTON</button> <button>BUTTON</button> <button>BUTTON</button> </div>
</div>
},
{
id: "1",
title: <Text.Body> Title2 </Text.Body>,
body:
<div >
<div> <label>LABEL</label> <label>LABEL</label> <label>LABEL</label> </div>
<div> <label>LABEL</label> <label>LABEL</label> <label>LABEL</label> </div>
<div> <label>LABEL</label> <label>LABEL</label> <label>LABEL</label> </div>
</div>
},
{
id: "2",
title: <Text.Body> Title3 </Text.Body>,
body:
<div>
<div> <input></input> <input></input> <input></input> </div>
<div> <input></input> <input></input> <input></input> </div>
<div> <input></input> <input></input> <input></input> </div>
</div>
},
{
id: "3",
title: <Text.Body> Title4 </Text.Body>,
body:
<div>
<div> <button>BUTTON</button> <button>BUTTON</button> <button>BUTTON</button> </div>
<div> <button>BUTTON</button> <button>BUTTON</button> <button>BUTTON</button> </div>
<div> <button>BUTTON</button> <button>BUTTON</button> <button>BUTTON</button> </div>
</div>
},
{
id: "4",
title: <Text.Body> Title5 </Text.Body>,
body:
<div>
<div> <label>LABEL</label> <label>LABEL</label> <label>LABEL</label> </div>
<div> <label>LABEL</label> <label>LABEL</label> <label>LABEL</label> </div>
<div> <label>LABEL</label> <label>LABEL</label> <label>LABEL</label> </div>
</div>
}
];
/*
const item = [{
id: "0",
something_title: <Text.Body>{text("Title text", "Title1")} </Text.Body>,
something_body:
<div >
<div> <button>BUTTON</button> <button>BUTTON</button> <button>BUTTON</button> </div>
<div> <button>BUTTON</button> <button>BUTTON</button> <button>BUTTON</button> </div>
<div> <button>BUTTON</button> <button>BUTTON</button> <button>BUTTON</button> </div>
</div>
}];
*/
storiesOf('Components|Tabs', module)
.addDecorator(withKnobs)
.addDecorator(withReadme(Readme))
.add('base', () => {
return (
<BooleanValue>
<Tabs>
{something_items}
</Tabs>
</BooleanValue>
);
});