Commit 2f15a532 authored by Patricio Bruna's avatar Patricio Bruna

Merge pull request #75 from ZBoxApp/issues_resolved

Esperemos que no explote
parents c82ac91a 58b41f50
module.exports = {"main":{"js":"/244695bundle.js"}}
\ No newline at end of file
module.exports = {"main":{"js":"/887398bundle.js"}}
\ No newline at end of file
......@@ -20,8 +20,8 @@ export default class AntiSpam extends React.Component {
this.handleSave = this.handleSave.bind(this);
this.alterDomain = this.alterDomain.bind(this);
this.domain = DomainStore.getCurrent();
this.blackList = null;
this.whiteList = null;
this.blackList = [];
this.whiteList = [];
if (this.domain.attrs.amavisBlacklistSender) {
this.blackList = Array.isArray(this.domain.attrs.amavisBlacklistSender) ? this.domain.attrs.amavisBlacklistSender : this.domain.attrs.amavisBlacklistSender.trim().split(' ');
......@@ -39,11 +39,11 @@ export default class AntiSpam extends React.Component {
switch (action) {
case 'black':
this.searchItemToRemove(this.blackList, item);
attrs.amavisBlacklistSender = this.blackList;
attrs.amavisBlacklistSender = this.blackList.length > 0 ? this.blackList : null;
break;
case 'white':
this.searchItemToRemove(this.whiteList, item);
attrs.amavisWhitelistSender = this.whiteList;
attrs.amavisWhitelistSender = this.whiteList.length > 0 ? this.whiteList : null;
break;
}
......
......@@ -111,6 +111,7 @@ export default class DomainMailboxPlans extends React.Component {
);
}
} else {
console.log(totalLimit);
totalLimit += plan.limit;
}
......
......@@ -17,6 +17,7 @@ import * as Client from '../../utils/client.jsx';
import * as Utils from '../../utils/utils.jsx';
import * as GlobalActions from '../../action_creators/global_actions.jsx';
import Constants from '../../utils/constants.jsx';
import UserStore from '../../stores/user_store.jsx';
const QueryOptions = Constants.QueryOptions;
const messageType = Constants.MessageType;
......@@ -28,12 +29,14 @@ export default class Domains extends React.Component {
this.getDomains = this.getDomains.bind(this);
const page = parseInt(this.props.location.query.page, 10) || 1;
this.isGlobalAdmin = UserStore.isGlobalAdmin();
this.state = {
page,
offset: ((page - 1) * QueryOptions.DEFAULT_LIMIT)
};
}
getDomains() {
const self = this;
Client.getAllDomains(
......@@ -72,6 +75,7 @@ export default class Domains extends React.Component {
}
);
}
getPlans(domains) {
const names = domains.map((d) => {
return d.name;
......@@ -110,6 +114,7 @@ export default class Domains extends React.Component {
$('#sidebar-domains').addClass('active');
this.getDomains();
}
componentWillUnmount() {
$('#sidebar-domains').removeClass('active');
}
......@@ -117,6 +122,7 @@ export default class Domains extends React.Component {
render() {
const error = this.state.error;
let message;
let addDomainButton = null;
if (error) {
message = (
<MessageBar
......@@ -127,15 +133,17 @@ export default class Domains extends React.Component {
);
}
const addDomainButton = [{
label: 'Agregar Dominio',
props: {
className: 'btn btn-success',
onClick: (e) => {
Utils.handleLink(e, '/domains/new');
if (this.isGlobalAdmin) {
addDomainButton = [{
label: 'Agregar Dominio',
props: {
className: 'btn btn-success',
onClick: (e) => {
Utils.handleLink(e, '/domains/new');
}
}
}
}];
}];
}
let tableResults;
if (this.state.data) {
......@@ -233,7 +241,7 @@ export default class Domains extends React.Component {
<thead>
<tr>
<th>{'Nombre'}</th>
<th className='text-center'>{'Casillas Usadas'}</th>
<th className='text-center'>{'Casillas Compradas'}</th>
<th className='text-center'>{'Descripción'}</th>
<th className='text-center'>{'Estado'}</th>
</tr>
......
......@@ -8,6 +8,7 @@ import DateTimeField from 'react-bootstrap-datetimepicker';
import * as Client from '../../utils/client.jsx';
import Promise from 'bluebird';
import * as GlobalActions from '../../action_creators/global_actions.jsx';
import * as Utils from '../../utils/utils.jsx';
export default class MultipleTaskModal extends React.Component {
constructor(props) {
......@@ -16,25 +17,58 @@ export default class MultipleTaskModal extends React.Component {
this.onSubmit = this.onSubmit.bind(this);
this.getOwnAccounts = this.getOwnAccounts.bind(this);
this.handleChangeDate = this.handleChangeDate.bind(this);
this.allAccounts = [];
this.loop = 0;
this.range = 200;
this.initialDate = Utils.setInitialDate();
this.dateStart = this.initialDate.formatted;
this.dateEnd = this.initialDate.formatted;
this.timestampStart = this.initialDate.timestamp;
this.timestampEnd = this.initialDate.timestamp;
this.state = {
loaded: false
};
}
handleChangeDate() {
const timeLapse = 100;
setTimeout(() => {
const formated = document.getElementById('zimbraPrefOutOfOfficeUntilDate').value.split('/').reverse().join('') + '000000Z';
this.refs.zimbraPrefOutOfOfficeUntilDate.value = formated;
}, timeLapse);
handleChangeDate(x, from) {
const ref = this.refs[from];
const timestamp = Utils.getInitialDateFromTimestamp(x);
ref.value = timestamp;
}
onSubmit() {
const accounts = this.state.accounts.account;
const accounts = this.allAccounts;
const domain = this.props.data;
const total = accounts.length;
const collection = [];
const refs = this.refs;
let message = null;
let error = false;
const isEnabled = refs.zimbraPrefOutOfOfficeReplyEnabled.checked;
const start = refs.zimbraPrefOutOfOfficeFromDate.value;
const end = refs.zimbraPrefOutOfOfficeUntilDate.value;
if ((start > end) && isEnabled) {
message = 'La fecha en la que termina su respuesta automática, debe ser mayor que en la que comienza.';
error = true;
} else if ((start === end) && isEnabled) {
message = 'La fecha en la que comienza su respuesta automática no puede ser la misma fecha en la que termina.';
error = true;
}
if (error) {
this.setState({
error: true,
message,
typeError: 'text-danger'
});
return false;
}
GlobalActions.emitStartTask({
origin: 'Dominio - Tareas Masivas',
......@@ -42,57 +76,117 @@ export default class MultipleTaskModal extends React.Component {
action: `Asignando mensaje masivo fuera de oficina a ${total}`
});
this.setState({
error: false
});
const attrs = {};
const isEnabled = this.refs.zimbraPrefOutOfOfficeReplyEnabled.checked;
if (isEnabled) {
attrs.zimbraPrefOutOfOfficeReplyEnabled = isEnabled.toString().toUpperCase();
attrs.zimbraPrefOutOfOfficeReply = this.refs.zimbraPrefOutOfOfficeReply.value;
attrs.zimbraPrefOutOfOfficeUntilDate = this.refs.zimbraPrefOutOfOfficeUntilDate.value;
attrs.zimbraPrefOutOfOfficeReply = refs.zimbraPrefOutOfOfficeReply.value;
attrs.zimbraPrefOutOfOfficeUntilDate = refs.zimbraPrefOutOfOfficeUntilDate.value;
attrs.zimbraPrefOutOfOfficeFromDate = refs.zimbraPrefOutOfOfficeFromDate.value;
} else {
attrs.zimbraPrefOutOfOfficeReplyEnabled = isEnabled.toString().toUpperCase();
}
accounts.forEach((account) => {
const initial = (this.loop * this.range);
const limitLoop = ((initial + this.range) > total) ? total : (initial + this.range);
for (let i = initial; i < limitLoop; i++) {
const account = accounts[i];
collection.push(Client.modifyAccountByBatch(account.id, attrs));
});
}
Client.batchRequest(collection, () => {
GlobalActions.emitEndTask({
id: 'dominio-massive-task',
toast: {
message: `Se han agregado el mensaje fuera de oficina con exito a las ${total} casillas del dominio: ${domain.name}`,
title: 'Dominio - Mensaje Masivo'
if (limitLoop === total) {
GlobalActions.emitEndTask({
id: 'dominio-massive-task',
toast: {
message: `Se han agregado el mensaje fuera de oficina con exito a las ${total} casillas del dominio: ${domain.name}`,
title: 'Dominio - Mensaje Masivo'
}
});
if (this.props.show) {
this.props.onHide();
}
});
if (this.props.show) {
this.props.onHide();
this.loop = 0;
this.allAccounts = [];
return true;
}
}, () => {
setTimeout(() => {
this.onSubmit();
}, 200);
return null;
}, (error) => {
console.log(error);
if (this.props.show) {
this.props.onHide();
}
});
}
getOwnAccounts() {
getOwnAccounts(attrs) {
const attributes = attrs || {};
const domain = this.props.data;
attributes.domain = domain.name;
attributes.maxResults = window.manager_config.maxResultOnRequestZimbra;
new Promise((resolve, reject) => {
Client.getAllAccounts({
domain: domain.name
}, (success) => {
Client.getAllAccounts(attributes, (success) => {
return resolve(success);
}, (err) => {
return reject(err);
});
}).then((res) => {
this.setState({
loaded: true,
accounts: res
if (res.more) {
const result = res.account;
Array.prototype.push.apply(this.allAccounts, result);
if (res.total > this.allAccounts.length) {
this.loop++;
attributes.offset = (this.loop * attributes.limit);
setTimeout(() => {
this.getOwnAccounts(attributes);
}, 250);
}
}
if (!res.more) {
const finalResult = res.account;
Array.prototype.push.apply(this.allAccounts, finalResult);
this.loop = 0;
return this.setState({
loaded: true,
loading: false
});
}
const total = res.total;
const loaded = this.allAccounts.length;
return this.setState({
loading: true,
messageLoading: `Cargando ${loaded} de ${total} casillas...`
});
}).catch((error) => {
return error;
//return error;
if (error.code === 'account.TOO_MANY_SEARCH_RESULTS') {
const newMaxResultOnRequest = window.manager_config.maxResultOnRequestZimbra + window.manager_config.autoincrementOnFailRequestZimbra;
window.manager_config.maxResultOnRequestZimbra = newMaxResultOnRequest;
attributes.maxResults = newMaxResultOnRequest;
attributes.limit = window.manager_config.autoincrementOnFailRequestZimbra;
attributes.offset = this.loop;
setTimeout(() => {
this.getOwnAccounts(attributes);
}, 250);
}
});
}
......@@ -102,6 +196,19 @@ export default class MultipleTaskModal extends React.Component {
render() {
let content = null;
let messageLoading = 'Cargando...';
let labelError = null;
if (this.state.loading) {
const message = this.state.messageLoading;
messageLoading = message;
}
if (this.state.error) {
labelError = (
<label className={this.state.typeError}>{this.state.message}</label>
);
}
if (this.state.loaded) {
content = (
......@@ -128,6 +235,34 @@ export default class MultipleTaskModal extends React.Component {
</div>
</div>
<div className='form-group string'>
<label className='string required col-sm-3 control-label'>
{'Empieza el'}
</label>
<div className='col-sm-8'>
<DateTimeField
inputFormat='DD/MM/YYYY'
inputProps={
{
id: 'zimbraPrefOutOfOfficeFromDate',
readOnly: 'readOnly'
}
}
onChange={(x) => {
this.handleChangeDate(x, 'zimbraPrefOutOfOfficeFromDate');
}}
defaultText={this.dateStart}
mode={'date'}
/>
<input
type='hidden'
ref='zimbraPrefOutOfOfficeFromDate'
value={this.timestampStart}
/>
</div>
</div>
<div className='form-group string'>
<label className='string required col-sm-3 control-label'>
{'Termina el'}
......@@ -142,12 +277,16 @@ export default class MultipleTaskModal extends React.Component {
readOnly: 'readOnly'
}
}
onChange={this.handleChangeDate}
defaultText={this.dateEnd}
onChange={(x) => {
this.handleChangeDate(x, 'zimbraPrefOutOfOfficeUntilDate');
}}
mode={'date'}
/>
<input
type='hidden'
ref='zimbraPrefOutOfOfficeUntilDate'
value={this.timestampEnd}
/>
</div>
</div>
......@@ -174,7 +313,7 @@ export default class MultipleTaskModal extends React.Component {
content = (
<div className='text-center'>
<i className='fa fa-spinner fa-spin fa-3x fa-fw'></i>
<p>Cargando...</p>
<p>{messageLoading}</p>
</div>
);
}
......@@ -195,6 +334,10 @@ export default class MultipleTaskModal extends React.Component {
<div>
{content}
</div>
<div className='text-center'>
<br/>
{labelError}
</div>
</Modal.Body>
<Modal.Footer>
<button
......
......@@ -7,6 +7,7 @@ import ReactDOM from 'react-dom';
import * as GlobalActions from '../action_creators/global_actions.jsx';
import {browserHistory, Link} from 'react-router';
import * as Utils from '../utils/utils.jsx';
import logo from '../images/logo.png';
export default class Header extends React.Component {
......@@ -15,6 +16,7 @@ export default class Header extends React.Component {
this.handleSearch = this.handleSearch.bind(this);
this.toggleSidebar = this.toggleSidebar.bind(this);
}
handleSearch(e) {
e.preventDefault();
const search = ReactDOM.findDOMNode(this.refs.query);
......@@ -22,11 +24,14 @@ export default class Header extends React.Component {
const utf8 = ReactDOM.findDOMNode(this.refs.utf8).value.trim();
search.value = '';
GlobalActions.emitStartLoading();
browserHistory.push(`search/global?utf8=${utf8}&query=${encodeURIComponent(term)}`);
Utils.handleLink(e, `/search/${encodeURIComponent(term)}`);
//browserHistory.push(`search/?utf8=${utf8}&query=${encodeURIComponent(term)}`);
}
toggleSidebar() {
$('body').toggleClass('hide-sidebar').toggleClass('show-sidebar');
}
render() {
return (
<div id='header'>
......
......@@ -34,7 +34,7 @@ export default class Login extends React.Component {
componentDidMount() {
Client.isLoggedIn((data) => {
if (data && data.logged_in) {
browserHistory.push('/companies');
browserHistory.push('/mailboxes');
} else {
$('body').addClass('blank');
}
......@@ -46,7 +46,7 @@ export default class Login extends React.Component {
this.setState({user});
if (user) {
browserHistory.push('/companies');
browserHistory.push('/mailboxes');
}
}
submit(email, password) {
......@@ -71,7 +71,7 @@ export default class Login extends React.Component {
return Client.getAllCos(
(cosData) => {
ZimbraStore.setAllCos(cosData);
browserHistory.push('/companies');
browserHistory.push('/mailboxes');
}
);
},
......
......@@ -2,17 +2,19 @@
// See LICENSE.txt for license information.
import React from 'react';
import {browserHistory} from 'react-router';
import {Modal} from 'react-bootstrap';
import UserStore from '../../stores/user_store.jsx';
import PasswordStrengthMeter from 'react-password-strength-meter';
import * as GlobalActions from '../../action_creators/global_actions.jsx';
import Constants from '../../utils/constants.jsx';
const MessageTypes = Constants.MessageType;
export default class ConfirmDeleteModal extends React.Component {
constructor(props) {
super(props);
this.handleChangePasswd = this.handleChangePasswd.bind(this);
this.forceLogout = this.forceLogout.bind(this);
this.handlePasswd = this.handlePasswd.bind(this);
this.restart = this.restart.bind(this);
......@@ -36,12 +38,6 @@ export default class ConfirmDeleteModal extends React.Component {
return {currentUser};
}
forceLogout(path) {
setTimeout(() => {
browserHistory.push(path);
}, 3000);
}
handleChangePasswd() {
if (this.refs.passwdfield.value && this.refs.passwdfield.value.length > 0) {
if (this.refs.passwdfield.value.length < 9) {
......@@ -53,15 +49,22 @@ export default class ConfirmDeleteModal extends React.Component {
}
this.props.data.setPassword(this.refs.passwdfield.value, () => {
this.setState({
alert: true,
message: 'Su contraseña se ha sido cambiada éxitosamente.',
typeError: 'text-success'
});
const message = {
error: 'Su contraseña se ha sido cambiada éxitosamente.',
typeError: MessageTypes.SUCCESS
};
if (this.props.show) {
this.props.onHide();
}
if (this.props.data.name === this.state.currentUser.name) {
this.forceLogout('/logout');
const alterMessage = `${message.error} Deberá iniciar sesión de nuevo.`;
message.error = alterMessage;
message.logout = true;
}
GlobalActions.emitMessage(message);
}, (error) => {
this.setState({
alert: true,
......
......@@ -14,46 +14,103 @@ export default class FormVacacionesMailbox extends React.Component {
super(props);
this.handleSaveAutoResp = this.handleSaveAutoResp.bind(this);
this.dateStart = null;
this.dateEnd = null;
this.initialDate = Utils.setInitialDate();
}
handleChangeDate(x, from) {
const ref = this.refs[from];
const timestamp = Utils.getInitialDateFromTimestamp(x);
ref.value = timestamp;
}
handleSaveAutoResp() {
const data = this.props.data;
const refs = this.refs;
let enabled = refs.zimbraPrefOutOfOfficeReplyEnabled.checked.toString().toUpperCase();
let formated = document.getElementById('zimbraPrefOutOfOfficeUntilDate').value.split('/').reverse().join('') + '000000Z';
const attrs = {};
const isEnabled = refs.zimbraPrefOutOfOfficeReplyEnabled.checked;
const start = refs.zimbraPrefOutOfOfficeFromDate.value;
const end = refs.zimbraPrefOutOfOfficeUntilDate.value;
let formatedStart = document.getElementById('zimbraPrefOutOfOfficeFromDate').value.split('/').reverse().join('') + '000000Z';
let formatedEnd = document.getElementById('zimbraPrefOutOfOfficeUntilDate').value.split('/').reverse().join('') + '000000Z';
if ((start > end) && isEnabled) {
GlobalActions.emitMessage({
error: 'La fecha en la que termina su respuesta automática, debe ser mayor que en la que comienza.',
typeError: messageType.ERROR
});
let attrs = {
zimbraPrefOutOfOfficeReplyEnabled: enabled,
zimbraPrefOutOfOfficeUntilDate: formated,
zimbraPrefOutOfOfficeReply: refs.zimbraPrefOutOfOfficeReply.value
};
return false;
} else if ((start === end) && isEnabled) {
GlobalActions.emitMessage({
error: 'La fecha en la que comienza su respuesta automática no puede ser la misma fecha en la que termina.',
typeError: messageType.ERROR
});
return false;
}
if (isEnabled) {
attrs.zimbraPrefOutOfOfficeReplyEnabled = isEnabled.toString().toUpperCase();
attrs.zimbraPrefOutOfOfficeReply = refs.zimbraPrefOutOfOfficeReply.value;
attrs.zimbraPrefOutOfOfficeUntilDate = formatedEnd;
attrs.zimbraPrefOutOfOfficeFromDate = formatedStart;
} else {
attrs.zimbraPrefOutOfOfficeReplyEnabled = isEnabled.toString().toUpperCase();
}
Client.modifyAccount(data.id, attrs, () => {
GlobalActions.emitMessage({
error: 'Se ha modificado su respuesta de vacaciones con éxito.',
type: messageType.SUCCESS
typeError: messageType.SUCCESS
});
}, (error) => {
GlobalActions.emitMessage({
error: error.message,
type: messageType.ERROR
typeError: messageType.ERROR
});
});
return null;
}
componentDidMount() {
const data = this.props.data.attrs;
if (data.hasOwnProperty('zimbraPrefOutOfOfficeReplyEnabled')) {
this.refs.zimbraPrefOutOfOfficeReplyEnabled.checked = data.zimbraPrefOutOfOfficeReplyEnabled.toString().toLowerCase() === 'true';
}
if (data.hasOwnProperty('zimbraPrefOutOfOfficeUntilDate')) {
document.getElementById('zimbraPrefOutOfOfficeUntilDate').value = Utils.dateFormatted(data.zimbraPrefOutOfOfficeUntilDate, true, '/');
}
if (data.hasOwnProperty('zimbraPrefOutOfOfficeReply')) {
this.refs.zimbraPrefOutOfOfficeReply.value = data.zimbraPrefOutOfOfficeReply;
}
if (this.dateStart) {
this.refs.zimbraPrefOutOfOfficeFromDate.value = Utils.forceTimestampFromHumanDate(this.dateStart);
}
if (this.dateEnd) {
this.refs.zimbraPrefOutOfOfficeUntilDate.value = Utils.forceTimestampFromHumanDate(this.dateEnd);
}
}
componentWillMount() {
const data = this.props.data.attrs;
if (data.hasOwnProperty('zimbraPrefOutOfOfficeFromDate')) {
this.dateStart = Utils.dateFormatted(data.zimbraPrefOutOfOfficeFromDate, true, '/');
} else {
this.dateStart = this.initialDate.formatted;
}
if (data.hasOwnProperty('zimbraPrefOutOfOfficeUntilDate')) {
this.dateEnd = Utils.dateFormatted(data.zimbraPrefOutOfOfficeUntilDate, true, '/');
} else {
this.dateEnd = this.initialDate.formatted;
}
}
render() {
......@@ -84,6 +141,33 @@ export default class FormVacacionesMailbox extends React.Component {
</div>
</div>
<div className='form-group string'>
<label className='string required col-sm-3 control-label'>
{'Empieza el'}
</label>
<div className='col-sm-8'>
<DateTimeField
inputFormat='DD/MM/YYYY'
inputProps={
{
id: 'zimbraPrefOutOfOfficeFromDate',
readOnly: 'readOnly'
}
}
onChange={(x) => {
this.handleChangeDate(x, 'zimbraPrefOutOfOfficeFromDate');
}}
defaultText={this.dateStart}
mode={'date'}
/>
<input
type='hidden'
ref='zimbraPrefOutOfOfficeFromDate'
/>
</div>
</div>
<div className='form-group string'>
<label className='string required col-sm-3 control-label'>
{'Termina el'}
......@@ -98,6 +182,16 @@ export default class FormVacacionesMailbox extends React.Component {
readOnly: 'readOnly'
}
}
onChange={(x) => {
this.handleChangeDate(x, 'zimbraPrefOutOfOfficeUntilDate');
}}
defaultText={this.dateEnd}
mode={'date'}
/>
<input
type='hidden'
ref='zimbraPrefOutOfOfficeUntilDate'
/>
</div>
</div>
......
......@@ -46,6 +46,8 @@ export default class BlockGeneralInfoMailbox extends React.Component {
render() {
let blockInfo = null;
let statusCos = null;
console.log(ZimbraStore.getAllCos());
console.log(this.data);
const cosID = Utils.getEnabledPlansObjectByCos(ZimbraStore.getAllCos(), this.props.data.attrs.zimbraCOSId);
let cosName = null;
......
......@@ -41,7 +41,9 @@ export default class Mailboxes extends React.Component {
const page = parseInt(this.props.location.query.page, 10) || 1;
this.mailboxes = null;
this.status = '';
this.cos = null;
this.cos = Utils.getEnabledPlansByCos(ZimbraStore.getAllCos());
this.cosById = Utils.getEnabledPlansByCosId(ZimbraStore.getAllCos());
this.isRefreshing = true;
this.state = {
page,
......@@ -146,7 +148,7 @@ export default class Mailboxes extends React.Component {
domainId = this.props.params.domain_id;
this.getAllMailboxes(domainId);
this.getAllMailboxes(domainId, window.manager_config.maxResultOnRequestZimbra);
} else {
GlobalActions.emitStartLoading();
......@@ -154,7 +156,7 @@ export default class Mailboxes extends React.Component {
domainId = newProps.params.domain_id;
}
this.getAllMailboxes(domainId);
this.getAllMailboxes(domainId, window.manager_config.maxResultOnRequestZimbra);
}
}
......@@ -180,8 +182,13 @@ export default class Mailboxes extends React.Component {
);
}
getAccounts(domainName) {
const attrs = {};
getAccounts(domainName, maxResult) {
const attrs = {
limit: QueryOptions.DEFAULT_LIMIT,
maxResults: maxResult,
offset: this.state.offset
};
if (domainName) {
attrs.domain = domainName;
}
......@@ -195,19 +202,23 @@ export default class Mailboxes extends React.Component {
});
}
if (MailboxStore.hasMailboxes()) {
resolve(MailboxStore.getMailboxes());
if (MailboxStore.hasMailboxes() && MailboxStore.hasThisPage(this.state.page)) {
console.log('has page with data');
return resolve(MailboxStore.getMailboxByPage(this.state.page));
}
return Client.getAllAccounts(attrs, (success) => {
MailboxStore.setMailboxes(success);
this.mailboxes = this.mailboxes = MailboxStore.getMailboxes();
MailboxStore.setMailboxes(success, this.state.page);
this.mailboxes = MailboxStore.getMailboxes();
return resolve(success);
}, (error) => {
return reject(error);
});
}).then((data) => {
if (data.account) {
this.isRefreshing = false;
const tables = this.buildTableFromData(data, ['Todas', 'Bloqueadas']);
if (tables.lockedAlert) {
......@@ -227,9 +238,18 @@ export default class Mailboxes extends React.Component {
domain: domainName
});
}).catch((error) => {
return error;
if (error.code === 'account.TOO_MANY_SEARCH_RESULTS') {
this.isRefreshing = true;
const newMaxResult = (parseInt(maxResult, 10) + window.manager_config.autoincrementOnFailRequestZimbra);
window.manager_config.maxResultOnRequestZimbra = newMaxResult;
setTimeout(() => {
this.getAccounts(domainName, newMaxResult);
}, 250);
}
}).finally(() => {
GlobalActions.emitEndLoading();
if (!this.isRefreshing) {
GlobalActions.emitEndLoading();
}
});
}
......@@ -237,11 +257,11 @@ export default class Mailboxes extends React.Component {
if (domainId) {
return this.domainInfo(domainId).then(() => {
const domain = DomainStore.getCurrent();
this.getAccounts(domain.name);
this.getAccounts(domain.name, window.manager_config.maxResultOnRequestZimbra);
});
}
return this.getAccounts();
return this.getAccounts(null, window.manager_config.maxResultOnRequestZimbra);
}
refreshAllAccounts() {
......@@ -276,6 +296,29 @@ export default class Mailboxes extends React.Component {
buildRow(row, classes, status) {
const id = row.id;
const attrs = row.attrs;
let displayName = 'No definido';
let tipo = attrs.zimbraCOSId || null;
if (tipo) {
tipo = this.cosById[tipo] || null;
if (tipo) {
tipo = Utils.titleCase(tipo);
}
}
if (!tipo) {
tipo = 'Desconocido';
}
if (attrs.displayName) {
displayName = attrs.displayName.trim();
} else if (attrs.cn || attrs.sn) {
const cn = attrs.cn || '';
const sn = attrs.sn || '';
displayName = `${cn.trim()} ${sn.trim()}`;
}
return (
<tr
key={id}
......@@ -297,14 +340,14 @@ export default class Mailboxes extends React.Component {
</td>
<td className={'mailbox-displayname'}>
{row.name}
{displayName}
</td>
<td className={'mailbox-cos-plan'}>
<statusLabel className={'label-plan label-unknown'}>{'unknown'}</statusLabel>
<statusLabel className={'label-plan label-unknown'}>{tipo}</statusLabel>
</td>
<td>
<td className='text-center'>
<Button
btnAttrs={
{
......@@ -360,8 +403,8 @@ export default class Mailboxes extends React.Component {
<tr>
<th>{'Email'}</th>
<th className='td-mbxs text-left'>{'Nombre'}</th>
<th className='text-center text-center'>{'Tipo'}</th>
<th className='text-center text-center'>{'Acciones'}</th>
<th className='text-left'>{'Tipo'}</th>
<th className='text-center'>{'Acciones'}</th>
</tr>
</thead>
<tbody>
......@@ -416,7 +459,7 @@ export default class Mailboxes extends React.Component {
}
const response = {};
const all = `${arrayTabNames.shift()} (${activeAccounts.length})`;
const all = `${arrayTabNames.shift()} (${totalAccounts})`;
const locked = `${arrayTabNames.shift()} (${lockedAccounts.length})`;
// create structure html for all accountsç
......@@ -461,7 +504,9 @@ export default class Mailboxes extends React.Component {
}
];
let activePagination = null;
let activePagination = {
total: totalAccounts
};
const totalPage = Math.ceil(totalAccounts / QueryOptions.DEFAULT_LIMIT);
if (activeAccounts.length > QueryOptions.DEFAULT_LIMIT) {
activeAccounts = activeAccounts.slice(this.state.offset, (this.state.page * QueryOptions.DEFAULT_LIMIT));
......
......@@ -3,6 +3,7 @@
import $ from 'jquery';
import React from 'react';
import {browserHistory} from 'react-router';
import EventStore from '../../stores/event_store.jsx';
......@@ -52,6 +53,12 @@ export default class MailboxDetails extends React.Component {
error: attrs.error,
type: attrs.typeError
});
if (attrs.logout) {
setTimeout(() => {
browserHistory.push('/logout');
}, 3000);
}
}
onRemoveAlias(alias) {
......
......@@ -19,7 +19,7 @@ export default class PageInfo extends React.Component {
PageInfo.propTypes = {
titlePage: React.PropTypes.any.isRequired,
descriptionPage: React.PropTypes.string
descriptionPage: React.PropTypes.any
};
PageInfo.defaultProps = {
......
......@@ -60,7 +60,8 @@ export default class Pagination extends React.Component {
let prev;
let next;
let last;
let i = 1;
let console;
//let i = 1;
if (current > 1 && current <= total) {
first = (
......@@ -96,9 +97,43 @@ export default class Pagination extends React.Component {
>{'Última'}</a>
</li>
);
console = (
<li key='console-page'>
<span>{`${current} de ${this.props.totalPages}`}</span>
</li>
);
}
for (; i <= total; i++) {
const rangeBack = current - this.props.range;
const rangeForward = ((current + this.props.range) + 1);
for (let p = rangeBack; p < rangeForward; p++) {
if ((p > 0) && (p <= total)) {
if (p === current) {
pages.push(
<li
key={`page-${p}`}
className='active'
>
<a remote='false'>{p.toString()}</a>
</li>
);
} else {
pages.push(
<li key={`page-${p}`}>
<a
onClick={this.handleChange}
>
{p.toString()}
</a>
</li>
);
}
}
}
/*for (; i <= total; i++) {
if (current === i) {
pages.push(
<li
......@@ -119,7 +154,7 @@ export default class Pagination extends React.Component {
</li>
);
}
}
}*/
return (
<div id='pagination'>
......@@ -129,6 +164,7 @@ export default class Pagination extends React.Component {
{pages}
{next}
{last}
{console}
</ul>
</div>
);
......@@ -138,5 +174,10 @@ export default class Pagination extends React.Component {
Pagination.propTypes = {
url: React.PropTypes.string.isRequired,
currentPage: React.PropTypes.number.isRequired,
totalPages: React.PropTypes.number.isRequired
totalPages: React.PropTypes.number.isRequired,
range: React.PropTypes.number
};
Pagination.defaultProps = {
range: 2
}
......@@ -58,10 +58,26 @@ export default class ProgressTask extends React.Component {
});
}
isTaskDuplicated(params) {
const id = params.id;
for (let i = 0; i < this.tasks.length; i++) {
const currentTask = this.tasks[i];
if (id === currentTask.id) {
return true;
}
}
return false;
}
showTasks(params) {
if (this.isTaskDuplicated(params)) {
return null;
}
this.tasks.push(params);
this.setState({
return this.setState({
show: true,
tasks: this.tasks,
total: this.tasks.length
......
import React from 'react';
import PageInfo from '../page_info.jsx';
import * as GlobalActions from '../../action_creators/global_actions.jsx';
import * as Client from '../../utils/client.jsx';
import Button from '../button.jsx';
import * as Utils from '../../utils/utils.jsx';
import Panel from '../panel.jsx';
export default class SearchView extends React.Component {
constructor(props) {
super(props);
this.makeSearch = this.makeSearch.bind(this);
this.state = {};
}
componentDidMount() {
GlobalActions.emitEndLoading();
const query = this.props.params.query;
this.makeSearch(query);
}
componentWillReceiveProps(newProps) {
//const condition = (newProps.params.query !== this.props.params.query);
GlobalActions.emitEndLoading();
const query = newProps.params.query;
this.makeSearch(query);
};
makeSearch(query) {
Client.search({
query: `(|(mail=*${query}*)(cn=*${query}*)(sn=*${query}*)(gn=*${query}*)(displayName=*${query}*)(zimbraMailDeliveryAddress=*${query}*)(zimbraDomainName=*${query}*)(uid=*${query}*)(zimbraMailAlias=*${query}*)(uid=*${query}*)(zimbraDomainName=*${query}*)(cn=*${query}*))`,
types: 'accounts,distributionlists,domains'
}, (success) => {
const result = [];
for (const key in success) {
if (success.hasOwnProperty(key)) {
if (key === 'dl' || key === 'domain' || key === 'account') {
Array.prototype.push.apply(result, success[key]);
}
}
}
if (success.total <= 0) {
return this.setState({
notfound: true
});
}
return this.setState({
result
});
}, (error) => {
console.log(error);
});
}
render() {
const query = this.props.params.query;
let content = null;
const pagelInfo = (
<PageInfo
titlePage='Búsqueda'
descriptionPage={`Resultados para su búsqueda: ${query}`}
/>
);
if (this.state.result) {
const data = this.state.result;
const objectDomain = {};
const rows = data.map((item) => {
const type = item.constructor.name.toString().toLowerCase();
let tipo = 'Desconocido';
let url = null;
const id = item.id;
switch(type) {
case 'domain':
tipo = (
<div>
<i className='fa fa-globe fa-lg'></i>
<span className='marginLeft'>{'Dominio'}</span>
</div>
);
url = `/domains/${id}`;
objectDomain[item.name] = id;
break;
case 'account':
tipo = (
<div>
<i className='fa fa-user fa-lg'></i>
<span className='marginLeft'>{'Casilla'}</span>
</div>
);
url = `/mailboxes/${id}`;
break;
case 'distributionlist':
tipo = (
<div>
<i className='fa fa-users fa-lg'></i>
<span className='marginLeft'>{'Lista de Distribución'}</span>
</div>
);
const dlName = item.name.indexOf('@') > -1 ? item.name.split('@').pop() : item.name;
let domainId = null;
if (objectDomain[dlName]) {
domainId = objectDomain[dlName];
} else {
domainId = Utils.getDomainIdFromDL(item.name, data);
}
url = `/domains/${domainId}/distribution_lists/${id}`;
break;
}
return (
<tr
key={id}
className={'mailbox-row'}
>
<td className={'mailbox-name'}>
{tipo}
</td>
<td className={'mailbox-displayname'}>
{item.name}
</td>
<td className={'text-right'}>
<Button
btnAttrs={
{
className: 'btn btn-xs btn-info',
onClick: (e) => Utils.handleLink(e, url)
}
}
>
{'Ver'}
</Button>
</td>
</tr>
);
});
const table = (
<div
key='mailbox'
id='index-mailboxes-table'
className='table-responsive'
>
<table
id='index-domains'
cellPadding='1'
cellSpacing='1'
className='table table-condensed table-striped vertical-align index-mailbox-table'
>
<thead>
<tr>
<th className='text-left'>{'Tipo'}</th>
<th className='td-mbxs text-left'>{'Nombre'}</th>
<th className='text-right'>{'Acciones'}</th>
</tr>
</thead>
<tbody>
{rows}
</tbody>
</table>
</div>
);
content = (
<Panel
children={table}
hasHeader={false}
/>
);
}
if (this.state.notfound) {
content = (
<div className='text-center'>
<h4>
No existen resultados para su búsqueda
</h4>
</div>
);
}
return (
<div>
{pagelInfo}
<div className='content animate-panel'>
<div className='row'>
<div className='col-md-12 panel-with-tabs'>
{content}
</div>
</div>
</div>
</div>
);
}
}
SearchView.propTypes = {
params: React.PropTypes.object
};
......@@ -24,14 +24,6 @@ export default class SidebarMenu extends React.Component {
className='nav'
id='side-menu'
>
<li id='sidebar-dashboards'>
<a
href='#'
onClick={(e) => this.handleLink(e, '/dashboards')}
>
<span className='nav-label'>{'dashboards'}</span>
</a>
</li>
<li id='sidebar-companies'>
<a
href='#'
......
......@@ -8,6 +8,8 @@
"url": "http://192.168.1.8:8081",
"token": "otto"
},
"maxResultOnRequestZimbra": 4000,
"autoincrementOnFailRequestZimbra": 500,
"plans": {
"basic": true,
"premium": true,
......
......@@ -22,6 +22,7 @@ import CreateMailBox from './components/mailbox/create_mailbox.jsx';
import EditMailBox from './components/mailbox/edit_mailbox.jsx';
import DistributionLists from './components/distribution/distribution_lists.jsx';
import EditDistributionList from './components/distribution/edit_distribution_lists.jsx';
import SearchView from './components/search/search.jsx';
import * as Client from './utils/client.jsx';
import * as Utils from './utils/utils.jsx';
......@@ -156,18 +157,18 @@ function renderRootComponent() {
component={EditDistributionList}
/>
<Route
path='companies'
component={Companies}
/>
<Route
path='companies/:id'
component={Company}
/>
<Route
path='companies/:id/domains/new'
component={CreateDomains}
/>
<Route
path='companies'
component={Companies}
/>
<Route
path='companies/:id'
component={Company}
/>
<Route
path='companies/:id/domains/new'
component={CreateDomains}
/>
<Route
path='mailboxes'
......@@ -193,6 +194,11 @@ function renderRootComponent() {
path='logout'
onEnter={onLoggedOut}
/>
<Route
path='search/:query'
component={SearchView}
/>
</Route>
<Route component={NotLoggedIn}>
<IndexRedirect to='login'/>
......
......@@ -3,3 +3,4 @@
@import 'domain';
@import 'companies';
@import 'mailbox';
@import 'search';
.marginLeft {
margin-left: 5px;
}
......@@ -7,11 +7,13 @@ import Constants from '../utils/constants.jsx';
const eventTypes = Constants.EventTypes;
let mailboxesArray = null;
let mailboxexInstances = [];
class MailboxStoreClass extends EventEmitter {
constructor() {
super();
this.current = null;
this.currentPage = {};
}
getMailboxById(id) {
......@@ -38,6 +40,27 @@ class MailboxStoreClass extends EventEmitter {
}
}
hasThisPage(page) {
if (page && this.currentPage[page]) {
return this.currentPage[page];
}
return false;
}
setCurrentPage(page) {
this.currentPage[page] = true;
}
getMailboxByPage(page) {
if (page && this.currentPage[page]) {
console.log(this.currentPage);
return this.currentPage[page];
}
return false;
}
setCurrent(account) {
this.current = account;
}
......@@ -54,12 +77,33 @@ class MailboxStoreClass extends EventEmitter {
return false;
}
getPages() {
return this.currentPage;
}
getMailboxes() {
return mailboxesArray;
}
setMailboxes(mailboxes) {
setMailboxes(mailboxes, page) {
if (mailboxesArray) {
Array.prototype.push.apply(mailboxexInstances, mailboxes.account);
mailboxesArray.account = mailboxexInstances;
console.log('mailbox', mailboxes);
if (page) {
this.currentPage[page] = mailboxes;
console.log(this.currentPage[page]);
}
return true;
}
mailboxesArray = mailboxes;
mailboxexInstances = mailboxes.account;
if (page) {
this.currentPage[page] = mailboxes;
console.log(this.currentPage[page]);
}
return true;
}
changeAccount(newAccount) {
......
......@@ -29,6 +29,7 @@ function handleError(methodName, err) {
if (err) {
error.message = err.extra.reason;
error.code = err.extra.code || null;
} else {
error.message = 'Ocurrio un error general';
}
......@@ -111,7 +112,7 @@ export function login(user, password, success, error) {
}
Utils.setCookie('token', zimbra.client.token);
ZimbraStore.setCurrent(zimbra);
window.manager_config.dns.token = Utils.getCookie('token');
return getMe(success, error);
});
}
......@@ -524,9 +525,7 @@ export function search(query, success, error) {
initZimbra().then(
(zimbra) => {
zimbra.directorySearch(
{
query
},
query,
(err, data) => {
if (err) {
const e = handleError('search', err);
......
......@@ -544,3 +544,59 @@ export function getOwners(owners) {
throw Error('Owners array no es un arreglo :' + typeof owners);
}
export function getDomainIdFromDL(dlName, arr) {
const domainName = dlName.indexOf('@') > -1 ? dlName.split('@').pop() : dlName;
for (const domain in arr) {
if (arr.hasOwnProperty(domain)) {
if (domain.constructor.name.toString().toLowerCase() === 'domain') {
if (arr[domain].name === domainName) {
return arr[domain];
}
}
}
}
}
export function getInitialDateFromTimestamp(timestamp) {
const time = parseInt(timestamp, 10);
const date = new Date(time).toLocaleString();
let dateParts = date.split('/');
const lastPosition = dateParts.pop();
const formattedLastPosition = lastPosition.substring(0, 4);
dateParts.push(formattedLastPosition);
const timestampReseted = new Date(dateParts[2], (parseInt(dateParts[1], 10) - 1).toString(), dateParts[0], '00', '00', '00').getTime();
return timestampReseted;
}
export function forceTimestampFromHumanDate(date) {
const arrDate = date.split('/').reverse();
const newDateArr = arrDate.map((pos, i) => {
let item = parseInt(pos, 10);
if (i === 1) {
const tmp = item - 1;
item = tmp;
}
return item;
});
const formattedTimeStamp = new Date(newDateArr[0], newDateArr[1], newDateArr[2], '00', '00', '00').getTime();
return formattedTimeStamp;
}
export function setInitialDate(){
const dateInstance = new Date();
const day = dateInstance.getDate().toString().length < 2 ? '0' + dateInstance.getDate().toString() : dateInstance.getDate();
const month = (dateInstance.getMonth() + 1).toString().length < 2 ? '0' + (dateInstance.getMonth() + 1).toString() : (dateInstance.getMonth() + 1);
const formatted = `${day}/${month}/${dateInstance.getFullYear()}`;
const date = new Date(dateInstance.getFullYear(), dateInstance.getMonth(), dateInstance.getDate(), '00','00','00').getTime();
const dateObject = {
timestamp: date,
formatted
};
return dateObject;
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment