diff --git a/src/messenger/build.xml b/src/messenger/build.xml index 1c606544..23e7a9f3 100644 --- a/src/messenger/build.xml +++ b/src/messenger/build.xml @@ -16,6 +16,7 @@ default_app_js - Build JavaScript files related to default application chat_app_js - Build JavaScript files related to chat application + users_app_js - Build JavaScript files related to users application core_handlebars - Compile Handlebars templates of the Core @@ -263,6 +264,14 @@ Chat JavaScript application built. + + + + + + Users JavaScript application built. + + Compile Handlebars templates of the Core @@ -292,7 +301,7 @@ - + Mibew Messenger built. diff --git a/src/messenger/webim/default.css b/src/messenger/webim/default.css index c4fbf130..f35bc028 100644 --- a/src/messenger/webim/default.css +++ b/src/messenger/webim/default.css @@ -86,6 +86,10 @@ a { height:40px; } +.inline-block { + display: inline-block; +} + #footer { background: white url(images/footer.gif) bottom repeat-x; font-size:11px; @@ -557,8 +561,11 @@ table.awaiting td.visitor { border-bottom: 1px solid #ccc; padding: 10px 8px; margin: 0px; + text-align: center; +} +table.awaiting .no-threads, table.awaiting .no-visitors { + height: 30px; } - .awaiting .visitor a { color: #296685; } .awaiting tr:hover .visitor, .awaiting tr:hover .visitor a { color: #1D485E; } @@ -571,27 +578,26 @@ table.awaiting td.visitor { .awaiting tr.inchat:hover .visitor, .awaiting tr.inchat:hover .visitor a { color: #444; } .awaiting tr.inchat a { text-decoration: none; } -.firstmessage { +.first-message { text-align: right; font-size: 0.8em; padding-right: 10px; } -.firstmessage a { +.first-message a { text-decoration: none; } -.firstmessage a:hover { +.first-message a:hover { text-decoration: underline; } +#status-panel-region { + margin: 10px; +} + #connstatus { float:right; - margin: 10px 10px; -} - -#connlinks { - margin: 10px 10px; } #connlinks a { @@ -603,12 +609,64 @@ table.awaiting td.visitor { text-decoration: underline; } +.default-thread-controls { + width: 100px; +} + +.default-visitor-controls { + width: 20px; +} + +.default-thread-controls .control, +.default-visitor-controls .control { + height: 15px; + width: 15px; + margin: 0 2px; + border: none; + cursor: pointer; +} + +.open-control { + background: no-repeat top left url('images/tbliclspeak.gif'); +} + +.view-control { + background: no-repeat top left url('images/tbliclread.gif'); +} + +.track-control { + background: no-repeat top left url('images/tblictrack.gif'); +} + +.ban-control { + background: no-repeat top left url('images/ban.gif'); +} + +#sound-region { + display: none; +} + /* online operators */ -#onlineoperators { +#agents-region { padding-right: 10px; float: right; } +.agent-status-away, .agent-status-online { + display: inline-block; + height: 12px; + width: 12px; + border: none; + background-repeat: no-repeat; + margin-left: 5px; + margin-right: 2px; +} +.agent-status-away { + background-image: url("images/opaway.gif"); +} +.agent-status-online { + background-image: url("images/oponline.gif"); +} /* search */ diff --git a/src/messenger/webim/default_ie.css b/src/messenger/webim/default_ie.css new file mode 100644 index 00000000..3270865c --- /dev/null +++ b/src/messenger/webim/default_ie.css @@ -0,0 +1,21 @@ +/* + This file is part of Mibew Messenger project. + + Copyright (c) 2005-2011 Mibew Messenger Community + All rights reserved. The contents of this file are subject to the terms of + the Eclipse Public License v1.0 which accompanies this distribution, and + is available at http://www.eclipse.org/legal/epl-v10.html + + Alternatively, the contents of this file may be used under the terms of + the GNU General Public License Version 2 or later (the "GPL"), in which case + the provisions of the GPL are applicable instead of those above. If you wish + to allow use of your version of this file only under the terms of the GPL, and + not to allow others to use your version of this file under the terms of the + EPL, indicate your decision by deleting the provisions above and replace them + with the notice and other provisions required by the GPL. +*/ + +.inline-block { + display: inline; + zoom: 1; +} diff --git a/src/messenger/webim/js/compiled/users.js b/src/messenger/webim/js/compiled/users.js deleted file mode 100644 index 5563f4b4..00000000 --- a/src/messenger/webim/js/compiled/users.js +++ /dev/null @@ -1,35 +0,0 @@ -/* - This file is part of Mibew Messenger project. - http://mibew.org - - Copyright (c) 2005-2011 Mibew Messenger Community - License: http://mibew.org/license.php -*/ -Ajax.PeriodicalUpdater=Class.create(); -Class.inherit(Ajax.PeriodicalUpdater,Ajax.Base,{initialize:function(a){this.setOptions(a);this._options.onComplete=this.requestComplete.bind(this);this._options.onException=this.handleException.bind(this);this._options.onTimeout=this.handleTimeout.bind(this);this._options.timeout=5E3;this.frequency=this._options.frequency||2;this.updater={};this.update()},handleException:function(){this._options.handleError&&this._options.handleError("offline, reconnecting");this.stopUpdate();this.timer=setTimeout(this.update.bind(this), -1E3)},handleTimeout:function(){this._options.handleError&&this._options.handleError("timeout, reconnecting");this.stopUpdate();this.timer=setTimeout(this.update.bind(this),1E3)},stopUpdate:function(){this.updater._options&&(this.updater._options.onComplete=void 0);clearTimeout(this.timer)},update:function(){this._options.updateParams&&(this._options.parameters=this._options.updateParams());this.updater=new Ajax.Request(this._options.url,this._options)},requestComplete:function(a){try{var b=Ajax.getXml(a); -b?(this._options.updateContent||Ajax.emptyFunction)(b):this._options.handleError&&this._options.handleError("reconnecting")}catch(c){}this.timer=setTimeout(this.update.bind(this),1E3*this.frequency)}}); -var HtmlGenerationUtils={popupLink:function(a,b,c,d,h,f,e){return'"+d+""},generateOneRowTable:function(a){return''+a+"
"},viewOpenCell:function(a,b,c,d,h,f,e,m){b= -b+"?thread="+c;f="";f=h||d?f+HtmlGenerationUtils.popupLink(m||!d?b:b+"&viewonly=true",localized[h?0:1],"ImCenter"+c,a,640,480,null):f+(''+a+"");f+="";""!=e&&(f=f+('')+(30");return HtmlGenerationUtils.generateOneRowTable(f)},viewActionsCell:function(a,b,c,d,h,f){a=a+"?thread="+b;var e="";d&&(e=e+ -''+HtmlGenerationUtils.popupLink(a,localized[0],"ImCenter"+b,''+localized[0]+'',640,480,null),e+="");c&&(e+='',e+=HtmlGenerationUtils.popupLink(a+"&viewonly=true",localized[1],"ImCenter"+b,''+localized[1]+'',640,480,null),e+="");h&&(e+='',e+=HtmlGenerationUtils.popupLink(f+ -"?thread="+b,localized[6],"ImTracked"+b,''+localized[6]+'',640,480,null),e+="");return e},banCell:function(a,b){return''+HtmlGenerationUtils.popupLink(webimRoot+"/operator/ban.php?"+(b?"id="+b:"thread="+a),localized[2],"ban"+a,''+localized[2]+'',720,480,null)+""},viewVisOpenCell:function(a,b,c,d,h){var f= -"",f=h?f+HtmlGenerationUtils.popupLink(b+"?visitor="+c,localized[7],"ImCenter"+c,a,640,480,null):f+(''+a+""),f=f+'';a=HtmlGenerationUtils.popupLink(d+"?visitor="+c,localized[6],"ImTracked"+c,''+localized[6]+'',640,480,null);a=a.replace("scrollbars=0","scrollbars=1");f+=a;f+="";return HtmlGenerationUtils.generateOneRowTable(f)}};Ajax.ThreadListUpdater=Class.create(); -Class.inherit(Ajax.ThreadListUpdater,Ajax.Base,{initialize:function(a){this.setOptions(a);this._options.updateParams=this.updateParams.bind(this);this._options.handleError=this.handleError.bind(this);this._options.updateContent=this.updateContent.bind(this);this._options.lastrevision=0;this.threadTimers={};this.delta=0;this.t=this._options.table;this.t2=this._options.visitors_table;this.periodicalUpdater=new Ajax.PeriodicalUpdater(this._options);this.old_visitors={};this.visitors={};this.visitorTimers= -{}},updateParams:function(){return"since="+this._options.lastrevision+"&status="+this._options.istatus+(this._options.showonline?"&showonline=1":"")+(this._options.showvisitors?"&showvisitors=1":"")},setStatus:function(a){this._options.status.innerHTML=a},handleError:function(a){this.setStatus(a)},updateThread:function(a){function b(a,b,d,c){if(a=CommonUtils.getCell(d,b,a))a.innerHTML=c}for(var c,d,h,f=!1,e=!1,m=!1,n=null,p=null,g=0;g"+NodeUtils.getNodeValue(a,"useragent")+"";null!=n&&(q=""+NodeUtils.getNodeValue(a,"reason")+"");m&&(k+=HtmlGenerationUtils.banCell(c,p));k=HtmlGenerationUtils.generateOneRowTable(k);q=HtmlGenerationUtils.generateOneRowTable(q);a=CommonUtils.getRow("t"+d,this.t);m=CommonUtils.getRow("t"+d+"end",this.t); -if(null!=g&&(g.rowIndex<=a.rowIndex||g.rowIndex>=m.rowIndex))this.t.deleteRow(g.rowIndex),g=this.threadTimers[c]=null;if(null==g){if(g=this.t.insertRow(a.rowIndex+1),g.className="blocked"==n&&"chat"!=d?"ban":"in"+d,g.id="thr"+c,this.threadTimers[c]=[r,s,d],CommonUtils.insertCell(g,"name","visitor",null,null,HtmlGenerationUtils.viewOpenCell(j,this._options.agentservl,c,f,e,n,u,"chat"!=d,this._options.showvisitors,this._options.trackedservl)),CommonUtils.insertCell(g,"actions","visitor","center",null, -k),CommonUtils.insertCell(g,"contid","visitor","center",null,l),CommonUtils.insertCell(g,"state","visitor","center",null,h),CommonUtils.insertCell(g,"op","visitor","center",null,t),CommonUtils.insertCell(g,"time","visitor","center",null,this.getTimeSince(r)),CommonUtils.insertCell(g,"wait","visitor","center",null,"chat"!=d?this.getTimeSince(s):"-"),CommonUtils.insertCell(g,"etc","visitor","center",null,q),"wait"==d||"prio"==d)return!0}else this.threadTimers[c]=[r,s,d],g.className="blocked"==n&&"chat"!= -d?"ban":"in"+d,b(this.t,g,"name",HtmlGenerationUtils.viewOpenCell(j,this._options.agentservl,c,f,e,n,u,"chat"!=d,this._options.showvisitors,this._options.trackedservl)),b(this.t,g,"actions",k),b(this.t,g,"contid",l),b(this.t,g,"state",h),b(this.t,g,"op",t),b(this.t,g,"time",this.getTimeSince(r)),b(this.t,g,"wait","chat"!=d?this.getTimeSince(s):"-"),b(this.t,g,"etc",q);return!1}},updateQueueMessages:function(){function a(a,b){var c=$(b),e=$(b+"end");return null==c||null==e?!1:c.rowIndex+1a&&(a="0"+a);60<=b&&(c=Math.floor(b/60),b%=60,10>b&&(b="0"+b),c+=":");return c+b+":"+a},updateTimers:function(){for(var a in this.threadTimers)if(null!=this.threadTimers[a]){var b=this.threadTimers[a],c=CommonUtils.getRow("thr"+a,this.t); -if(null!=c){var d=this.getTimeSince(b[0]),h=CommonUtils.getCell("time",c,this.t);h&&(h.innerHTML=d);b="chat"!=b[2]?this.getTimeSince(b[1]):"-";if(c=CommonUtils.getCell("wait",c,this.t))c.innerHTML=b}}},updateThreads:function(a){var b=!1,c=NodeUtils.getAttrValue(a,"time"),d=NodeUtils.getAttrValue(a,"revision");c&&(this.delta=(new Date).getTime()-c);d&&(this._options.lastrevision=d);for(c=0;c '+f}}b.innerHTML=c.join(", ")}},updateVisitorsTimers:function(){for(var a in this.visitorTimers)if(null!=this.visitorTimers[a]){var b=this.visitorTimers[a],c=CommonUtils.getRow("vis"+a,this.t2);if(null!=c){var d=function(a,b,c,d){if(a=CommonUtils.getCell(c,b,a))a.innerHTML=d};d(this.t2,c,"time",this.getTimeSince(b[0]));d(this.t2,c,"modified",this.getTimeSince(b[1]));null!=b[2]&&d(this.t2,c,"invitationtime",this.getTimeSince(b[2]))}}},updateVisitor:function(a){function b(a,b,c,d){if(a= -CommonUtils.getCell(c,b,a))a.innerHTML=d}for(var c,d=0;d=l.rowIndex))this.t2.deleteRow(d.rowIndex),d=this.visitorTimers[c]=null;null==d?(d=this.t2.insertRow(a.rowIndex+1),d.id="vis"+c,this.visitorTimers[c]=[m,n,k],CommonUtils.insertCell(d,"username", -"visitor",null,null,HtmlGenerationUtils.viewVisOpenCell(f,this._options.inviteservl,c,this._options.trackedservl,null==j)),CommonUtils.insertCell(d,"addr","visitor","center",null,h),CommonUtils.insertCell(d,"time","visitor","center",null,this.getTimeSince(m)),CommonUtils.insertCell(d,"modified","visitor","center",null,this.getTimeSince(n)),CommonUtils.insertCell(d,"operator","visitor","center",null,null!=j?j:"-"),CommonUtils.insertCell(d,"invitationtime","visitor","center",null,null!=j?this.getTimeSince(k): -"-"),CommonUtils.insertCell(d,"invitations","visitor","center",null,p+" / "+g),CommonUtils.insertCell(d,"useragent","visitor","center",null,e)):(this.visitorTimers[c]=[m,n,k],b(this.t2,d,"username",HtmlGenerationUtils.viewVisOpenCell(f,this._options.inviteservl,c,this._options.trackedservl,null==j)),b(this.t2,d,"addr",h),b(this.t2,d,"operator",null!=j?j:"-"),b(this.t2,d,"time",this.getTimeSince(m)),b(this.t2,d,"modified",this.getTimeSince(n)),b(this.t2,d,"invitationtime",null!=j?this.getTimeSince(k): -"-"),b(this.t2,d,"invitations",p+" / "+g),b(this.t2,d,"useragent",e));this.visitors[c]=1;return!1},removeOldVisitors:function(){for(id in this.old_visitors)if(void 0===this.visitors[id]){var a=CommonUtils.getRow("vis"+id,this.t2);a&&this.t2.deleteRow(a.rowIndex);this.visitorTimers[id]=null}},updateVisitorsList:function(a){var b=$("visstatustd");b&&(b.innerHTML=0d?"0"+d:d);c.push(10>b?"0"+b:b);return c.join(":")})})(Handlebars); diff --git a/src/messenger/webim/js/compiled/users/init.js b/src/messenger/webim/js/compiled/users/init.js new file mode 100644 index 00000000..205315cc --- /dev/null +++ b/src/messenger/webim/js/compiled/users/init.js @@ -0,0 +1,8 @@ +/* + This file is part of Mibew Messenger project. + http://mibew.org + + Copyright (c) 2005-2011 Mibew Messenger Community + License: http://mibew.org/license.php +*/ +(function(a){a.Regions={};a.Popup={};a.Popup.open=function(b,a,c){b=window.open(b,a,c);b.focus();b.opener=window}})(Mibew); diff --git a/src/messenger/webim/js/compiled/users/mibewapi_users_interaction.js b/src/messenger/webim/js/compiled/users/mibewapi_users_interaction.js new file mode 100644 index 00000000..ee5b7aca --- /dev/null +++ b/src/messenger/webim/js/compiled/users/mibewapi_users_interaction.js @@ -0,0 +1,8 @@ +/* + This file is part of Mibew Messenger project. + http://mibew.org + + Copyright (c) 2005-2011 Mibew Messenger Community + License: http://mibew.org/license.php +*/ +MibewAPIUsersInteraction=function(){this.obligatoryArguments={"*":{agentId:null,"return":{},references:{}},result:{errorCode:0}};this.reservedFunctionNames=["result"]};MibewAPIUsersInteraction.prototype=new MibewAPIInteraction; diff --git a/src/messenger/webim/js/compiled/users/model_views/agent.js b/src/messenger/webim/js/compiled/users/model_views/agent.js new file mode 100644 index 00000000..c8a12a30 --- /dev/null +++ b/src/messenger/webim/js/compiled/users/model_views/agent.js @@ -0,0 +1,8 @@ +/* + This file is part of Mibew Messenger project. + http://mibew.org + + Copyright (c) 2005-2011 Mibew Messenger Community + License: http://mibew.org/license.php +*/ +(function(b,c,d){b.Views.Agent=c.Marionette.ItemView.extend({template:d.templates.agent,tagName:"span",className:"agent",modelEvents:{change:"render"},initialize:function(){this.isModelLast=this.isModelFirst=!1},serializeData:function(){var a=this.model.toJSON();a.isFirst=this.isModelFirst;a.isLast=this.isModelLast;return a}})})(Mibew,Backbone,Handlebars); diff --git a/src/messenger/webim/js/compiled/users/model_views/no_threads.js b/src/messenger/webim/js/compiled/users/model_views/no_threads.js new file mode 100644 index 00000000..53049710 --- /dev/null +++ b/src/messenger/webim/js/compiled/users/model_views/no_threads.js @@ -0,0 +1,8 @@ +/* + This file is part of Mibew Messenger project. + http://mibew.org + + Copyright (c) 2005-2011 Mibew Messenger Community + License: http://mibew.org/license.php +*/ +(function(a,b,c){a.Views.NoThreads=b.Marionette.ItemView.extend({template:c.templates.no_threads,initialize:function(a){this.tagName=a.tagName}})})(Mibew,Backbone,Handlebars); diff --git a/src/messenger/webim/js/compiled/users/model_views/no_visitors.js b/src/messenger/webim/js/compiled/users/model_views/no_visitors.js new file mode 100644 index 00000000..84aa8a2e --- /dev/null +++ b/src/messenger/webim/js/compiled/users/model_views/no_visitors.js @@ -0,0 +1,8 @@ +/* + This file is part of Mibew Messenger project. + http://mibew.org + + Copyright (c) 2005-2011 Mibew Messenger Community + License: http://mibew.org/license.php +*/ +(function(a,b,c){a.Views.NoVisitors=b.Marionette.ItemView.extend({template:c.templates.no_visitors,initialize:function(a){this.tagName=a.tagName}})})(Mibew,Backbone,Handlebars); diff --git a/src/messenger/webim/js/compiled/users/model_views/queued_thread.js b/src/messenger/webim/js/compiled/users/model_views/queued_thread.js new file mode 100644 index 00000000..3f76310f --- /dev/null +++ b/src/messenger/webim/js/compiled/users/model_views/queued_thread.js @@ -0,0 +1,12 @@ +/* + This file is part of Mibew Messenger project. + http://mibew.org + + Copyright (c) 2005-2011 Mibew Messenger Community + License: http://mibew.org/license.php +*/ +(function(d,e){d.Views.QueuedThread=d.Views.CompositeBase.extend({template:e.templates.queued_thread,itemView:d.Views.Control,itemViewContainer:".thread-controls",className:"thread",modelEvents:{change:"render"},events:{"click .open-dialog":"openDialog","click .view-control":"viewDialog","click .track-control":"showTrack","click .ban-control":"showBan","click .geo-link":"showGeoInfo","click .first-message a":"showFirstMessage"},initialize:function(){this.lastStyles=[]},serializeData:function(){var a= +this.model,b=d.Objects.Models.page,c=a.toJSON();c.stateDesc=this.stateToDesc(a.get("state"));c.chatting=a.get("state")==a.STATE_CHATTING;c.tracked=b.get("showVisitors");c.firstMessage&&(c.firstMessagePreview=30',h=c.name,h?g=h.call(b,{hash:{}}):(g=b.name,g=typeof g===l?g():g),f+=j(g),g=b.isLast,g=c.unless.call(b,g,{hash:{},inverse:k.noop,fn:k.program(9,q,e)});if(g||g===0)f+=g;return f}),b.threads_collection=a(function(a,b,c,d,e){c=c||a.helpers;var f="",g,h,i=c.helperMissing,j=this.escapeExpression;return f+='\n\n\n \n \n \n \n \n \n \n \n\n\n\n\n\n
',h=c.L10n,g=h?h.call(b,"pending.table.head.name",{hash:{}}):i.call(b,"L10n","pending.table.head.name",{hash:{}}),f+=j(g)+"",h=c.L10n,g=h?h.call(b,"pending.table.head.actions",{hash:{}}):i.call(b,"L10n","pending.table.head.actions",{hash:{}}),f+=j(g)+"",h=c.L10n,g=h?h.call(b,"pending.table.head.contactid",{hash:{}}):i.call(b,"L10n","pending.table.head.contactid",{hash:{}}),f+=j(g)+"",h=c.L10n,g=h?h.call(b,"pending.table.head.state",{hash:{}}):i.call(b,"L10n","pending.table.head.state",{hash:{}}),f+=j(g)+"",h=c.L10n,g=h?h.call(b,"pending.table.head.operator",{hash:{}}):i.call(b,"L10n","pending.table.head.operator",{hash:{}}),f+=j(g)+"",h=c.L10n,g=h?h.call(b,"pending.table.head.total",{hash:{}}):i.call(b,"L10n","pending.table.head.total",{hash:{}}),f+=j(g)+"",h=c.L10n,g=h?h.call(b,"pending.table.head.waittime",{hash:{}}):i.call(b,"L10n","pending.table.head.waittime",{hash:{}}),f+=j(g)+"",h=c.L10n,g=h?h.call(b,"pending.table.head.etc",{hash:{}}):i.call(b,"L10n","pending.table.head.etc",{hash:{}}),f+=j(g)+'
',f}),b.no_visitors=a(function(a,b,c,d,e){c=c||a.helpers;var f="",g,h,i=c.helperMissing,j=this.escapeExpression;return f+='',h=c.L10n,g=h?h.call(b,"visitors.no_visitors",{hash:{}}):i.call(b,"L10n","visitors.no_visitors",{hash:{}}),f+=j(g)+"",f}),b.visitor=a(function(a,b,c,d,e){function m(a,b){var d="",e,f;return d+='',f=c.userName,f?e=f.call(a,{hash:{}}):(e=a.userName,e=typeof e===k?e():e),d+=j(e)+"",d}function n(a,b){var d,e;return e=c.userName,e?d=e.call(a,{hash:{}}):(d=a.userName,d=typeof d===k?d():d),j(d)}function o(a,b){var d="",e,f;return d+='',f=c.remote,f?e=f.call(a,{hash:{}}):(e=a.remote,e=typeof e===k?e():e),d+=j(e)+"",d}function p(a,b){var d,e;return e=c.remote,e?d=e.call(a,{hash:{}}):(d=a.remote,d=typeof d===k?d():d),j(d)}function q(a,b){var c;return c=a.invitationInfo,c=c==null||c===!1?c:c.agentName,c=typeof c===k?c():c,j(c)}function r(a,b){return"-"}function s(a,b){var d,e;return d=a.invitationInfo,d=d==null||d===!1?d:d.time,e=c.formatTimeSince,d=e?e.call(a,d,{hash:{}}):i.call(a,"formatTimeSince",d,{hash:{}}),j(d)}function t(a,b){return"-"}c=c||a.helpers;var f="",g,h,i=c.helperMissing,j=this.escapeExpression,k="function",l=this;f+='\n ',g=b.invitationInfo,g=c.unless.call(b,g,{hash:{},inverse:l.program(3,n,e),fn:l.program(1,m,e)});if(g||g===0)f+=g;f+='\n\n\n
\n
\n
\n
\n\n',g=b.userIp,g=c["if"].call(b,g,{hash:{},inverse:l.program(7,p,e),fn:l.program(5,o,e)});if(g||g===0)f+=g;f+='\n',g=b.firstTime,h=c.formatTimeSince,g=h?h.call(b,g,{hash:{}}):i.call(b,"formatTimeSince",g,{hash:{}}),f+=j(g)+'\n',g=b.lastTime,h=c.formatTimeSince,g=h?h.call(b,g,{hash:{}}):i.call(b,"formatTimeSince",g,{hash:{}}),f+=j(g)+'\n',g=b.invitationInfo,g=c["if"].call(b,g,{hash:{},inverse:l.program(11,r,e),fn:l.program(9,q,e)});if(g||g===0)f+=g;f+='\n',g=b.invitationInfo,g=c["if"].call(b,g,{hash:{},inverse:l.program(15,t,e),fn:l.program(13,s,e)});if(g||g===0)f+=g;return f+='\n',h=c.invitations,h?g=h.call(b,{hash:{}}):(g=b.invitations,g=typeof g===k?g():g),f+=j(g)+" / ",h=c.chats,h?g=h.call(b,{hash:{}}):(g=b.chats,g=typeof g===k?g():g),f+=j(g)+'\n',h=c.userAgent,h?g=h.call(b,{hash:{}}):(g=b.userAgent,g=typeof g===k?g():g),f+=j(g)+"",f}),b.status_panel=a(function(a,b,c,d,e){function m(a,b){var d,e;return e=c.L10n,d=e?e.call(a,"pending.status.away",{hash:{}}):i.call(a,"L10n","pending.status.away",{hash:{}}),j(d)}function n(a,b){var d,e;return e=c.L10n,d=e?e.call(a,"pending.status.online",{hash:{}}):i.call(a,"L10n","pending.status.online",{hash:{}}),j(d)}function o(a,b){var d,e;return e=c.L10n,d=e?e.call(a,"pending.status.setonline",{hash:{}}):i.call(a,"L10n","pending.status.setonline",{hash:{}}),j(d)}function p(a,b){var d,e;return e=c.L10n,d=e?e.call(a,"pending.status.setaway",{hash:{}}):i.call(a,"L10n","pending.status.setaway",{hash:{}}),j(d)}c=c||a.helpers;var f="",g,h,i=c.helperMissing,j=this.escapeExpression,k="function",l=this;f+='
',h=c.message,h?g=h.call(b,{hash:{}}):(g=b.message,g=typeof g===k?g():g),f+=j(g),g=b.agent,g=g==null||g===!1?g:g.away,g=c["if"].call(b,g,{hash:{},inverse:l.program(3,n,e),fn:l.program(1,m,e)});if(g||g===0)f+=g;f+='
",f}),b.queued_thread=a(function(a,b,c,d,e){function m(a,b){var d,e;return e=c.L10n,d=e?e.call(a,"pending.table.speak",{hash:{}}):i.call(a,"L10n","pending.table.speak",{hash:{}}),j(d)}function n(a,b){var d,e;return e=c.L10n,d=e?e.call(a,"pending.table.view",{hash:{}}):i.call(a,"L10n","pending.table.view",{hash:{}}),j(d)}function o(a,b){var d="",e,f;return f=c.L10n,e=f?f.call(a,"chat.client.spam.prefix",{hash:{}}):i.call(a,"L10n","chat.client.spam.prefix",{hash:{}}),d+=j(e)+" ",d}function p(a,b){var d="",e,f;return d+='",d}function q(a,b){var d="",e,f;return d+='\n
\n ',d}function r(a,b){var d="",e,f;return d+='\n
\n ',d}function s(a,b){var d="",e,f;return d+='\n
\n ',d}function t(a,b){var d="",e,f;return d+='\n
\n ',d}function u(a,b){var d="",e,f;return d+='',f=c.remote,f?e=f.call(a,{hash:{}}):(e=a.remote,e=typeof e===k?e():e),d+=j(e)+"",d}function v(a,b){var d,e;return e=c.remote,e?d=e.call(a,{hash:{}}):(d=a.remote,d=typeof d===k?d():d),j(d)}function w(a,b){var d,e;return d=a.waitingTime,e=c.formatTimeSince,d=e?e.call(a,d,{hash:{}}):i.call(a,"formatTimeSince",d,{hash:{}}),j(d)}function x(a,b){return"-"}function y(a,b){var c;return c=a.ban,c=c==null||c===!1?c:c.reason,c=typeof c===k?c():c,j(c)}function z(a,b){var d,e;return e=c.userAgent,e?d=e.call(a,{hash:{}}):(d=a.userAgent,d=typeof d===k?d():d),j(d)}c=c||a.helpers;var f="",g,h,i=c.helperMissing,j=this.escapeExpression,k="function",l=this;f+='\n \n ",g=b.firstMessage,g=c["if"].call(b,g,{hash:{},inverse:l.noop,fn:l.program(7,p,e)});if(g||g===0)f+=g;f+='\n\n\n
\n ',g=b.canOpen,g=c["if"].call(b,g,{hash:{},inverse:l.noop,fn:l.program(9,q,e)});if(g||g===0)f+=g;f+="\n ",g=b.canView,g=c["if"].call(b,g,{hash:{},inverse:l.noop,fn:l.program(11,r,e)});if(g||g===0)f+=g;f+="\n ",g=b.tracked,g=c["if"].call(b,g,{hash:{},inverse:l.noop,fn:l.program(13,s,e)});if(g||g===0)f+=g;f+="\n ",g=b.canBan,g=c["if"].call(b,g,{hash:{},inverse:l.noop,fn:l.program(15,t,e)});if(g||g===0)f+=g;f+='\n
\n
\n\n',g=b.userIp,g=c["if"].call(b,g,{hash:{},inverse:l.program(19,v,e),fn:l.program(17,u,e)});if(g||g===0)f+=g;f+='\n',h=c.stateDesc,h?g=h.call(b,{hash:{}}):(g=b.stateDesc,g=typeof g===k?g():g),f+=j(g)+'\n',h=c.agentName,h?g=h.call(b,{hash:{}}):(g=b.agentName,g=typeof g===k?g():g),f+=j(g)+'\n',g=b.totalTime,h=c.formatTimeSince,g=h?h.call(b,g,{hash:{}}):i.call(b,"formatTimeSince",g,{hash:{}}),f+=j(g)+'\n',g=b.chatting,g=c.unless.call(b,g,{hash:{},inverse:l.program(23,x,e),fn:l.program(21,w,e)});if(g||g===0)f+=g;f+='\n',g=b.ban,g=c["if"].call(b,g,{hash:{},inverse:l.program(27,z,e),fn:l.program(25,y,e)});if(g||g===0)f+=g;return f+="",f}),b.no_threads=a(function(a,b,c,d,e){c=c||a.helpers;var f="",g,h,i=c.helperMissing,j=this.escapeExpression;return f+='',h=c.L10n,g=h?h.call(b,"clients.no_clients",{hash:{}}):i.call(b,"L10n","clients.no_clients",{hash:{}}),f+=j(g)+"",f}),b.visitors_collection=a(function(a,b,c,d,e){c=c||a.helpers;var f="",g,h,i=c.helperMissing,j=this.escapeExpression;return f+='\n\n\n \n \n \n \n \n \n \n \n \n\n\n\n\n
',h=c.L10n,g=h?h.call(b,"visitors.table.head.name",{hash:{}}):i.call(b,"L10n","visitors.table.head.name",{hash:{}}),f+=j(g)+"",h=c.L10n,g=h?h.call(b,"visitors.table.head.actions",{hash:{}}):i.call(b,"L10n","visitors.table.head.actions",{hash:{}}),f+=j(g)+"",h=c.L10n,g=h?h.call(b,"visitors.table.head.contactid",{hash:{}}):i.call(b,"L10n","visitors.table.head.contactid",{hash:{}}),f+=j(g)+"",h=c.L10n,g=h?h.call(b,"visitors.table.head.firsttimeonsite",{hash:{}}):i.call(b,"L10n","visitors.table.head.firsttimeonsite",{hash:{}}),f+=j(g)+"",h=c.L10n,g=h?h.call(b,"visitors.table.head.lasttimeonsite",{hash:{}}):i.call(b,"L10n","visitors.table.head.lasttimeonsite",{hash:{}}),f+=j(g)+"",h=c.L10n,g=h?h.call(b,"visitors.table.head.invited.by",{hash:{}}):i.call(b,"L10n","visitors.table.head.invited.by",{hash:{}}),f+=j(g)+"",h=c.L10n,g=h?h.call(b,"visitors.table.head.invitationtime",{hash:{}}):i.call(b,"L10n","visitors.table.head.invitationtime",{hash:{}}),f+=j(g)+"",h=c.L10n,g=h?h.call(b,"visitors.table.head.invitations",{hash:{}}):i.call(b,"L10n","visitors.table.head.invitations",{hash:{}}),f+=j(g)+"",h=c.L10n,g=h?h.call(b,"visitors.table.head.etc",{hash:{}}):i.call(b,"L10n","visitors.table.head.etc",{hash:{}}),f+=j(g)+'
',f})})();(function(e){e.registerHelper("formatTimeSince",function(b){var a=Math.round((new Date).getTime()/1E3)-b;b=a%60;var d=Math.floor(a/60)%60,a=Math.floor(a/3600),c=[];0d?"0"+d:d);c.push(10>b?"0"+b:b);return c.join(":")})})(Handlebars); +MibewAPIUsersInteraction=function(){this.obligatoryArguments={"*":{agentId:null,"return":{},references:{}},result:{errorCode:0}};this.reservedFunctionNames=["result"]};MibewAPIUsersInteraction.prototype=new MibewAPIInteraction; +(function(a,b){a.Models.Agent=a.Models.User.extend({defaults:b.extend({},a.Models.User.prototype.defaults,{id:null,isAgent:!0,away:!1}),away:function(){this.setAvailability(!1)},available:function(){this.setAvailability(!0)},setAvailability:function(c){var b=this;a.Objects.server.callFunctions([{"function":c?"available":"away",arguments:{agentId:this.id,references:{},"return":{}}}],function(a){0==a.errorCode&&b.set({away:!c})},!0)}})})(Mibew,_); +(function(a,c){var b=[],f=a.Models.QueuedThread=a.Models.Thread.extend({defaults:c.extend({},a.Models.Thread.prototype.defaults,{controls:null,userName:"",userIp:"",remote:"",userAgent:"",agentName:"",canOpen:!1,canView:!1,canBan:!1,ban:!1,totalTime:0,waitingTime:0,firstMessage:null}),initialize:function(){for(var e=[],b=f.getControls(),d=0,c=b.length;d'+ - inner+''; - }, - - generateOneRowTable: function(content) { - return '' + content + '
'; - }, - - viewOpenCell: function(username,servlet,id,canview,canopen,ban,message,cantakenow,tracked,trackedlink) { - var link = servlet+"?thread="+id; - var gen = ''; - if(canopen || canview ) { - gen += HtmlGenerationUtils.popupLink( (cantakenow||!canview) ? link : link+"&viewonly=true", localized[canopen ? 0 : 1], "ImCenter"+id, username, 640, 480, null); - } else { - gen += '' + username + ''; - } - gen += ''; - if( message != "" ) { - gen += ''; - gen += message.length > 30 ? message.substring(0,30) + '...' : message; - gen += ''; - } - - return HtmlGenerationUtils.generateOneRowTable(gen); - }, - viewActionsCell: function(servlet,id,canview,canopen,tracked,trackedlink) { - var link = servlet+"?thread="+id; - var gen = ''; - if( canopen ) { - gen += ''; - gen += HtmlGenerationUtils.popupLink( link, localized[0], "ImCenter"+id, ''+localized[0]+'', 640, 480, null); - gen += ''; - } - if( canview ) { - gen += ''; - gen += HtmlGenerationUtils.popupLink( link+"&viewonly=true", localized[1], "ImCenter"+id, ''+localized[1]+'', 640, 480, null); - gen += ''; - } - if ( tracked ) { - gen += ''; - gen += HtmlGenerationUtils.popupLink( trackedlink+"?thread="+id, localized[6], "ImTracked"+id, ''+localized[6]+'', 640, 480, null); - gen += ''; - } - return gen; - }, - banCell: function(id,banid){ - return ''+ - HtmlGenerationUtils.popupLink( webimRoot+'/operator/ban.php?'+(banid ? 'id='+banid : 'thread='+id), localized[2], "ban"+id, ''+localized[2]+'', 720, 480, null)+ - ''; - }, - viewVisOpenCell: function(username, inviteservlet, userid, trackedservlet, caninvite) { - var cellsCount = 2; - var gen = ''; - if(caninvite) { - gen += HtmlGenerationUtils.popupLink( inviteservlet+"?visitor="+userid, localized[7], "ImCenter"+userid, username, 640, 480, null); - } else { - gen += '' + username + ''; - } - gen += ''; - gen += ''; - var tr_link = HtmlGenerationUtils.popupLink( trackedservlet+"?visitor="+userid, localized[6], "ImTracked"+userid, ''+localized[6]+'', 640, 480, null); - tr_link = tr_link.replace("scrollbars=0","scrollbars=1"); - gen += tr_link; - gen += ''; - return HtmlGenerationUtils.generateOneRowTable(gen); - } -}; - -Ajax.ThreadListUpdater = Class.create(); -Class.inherit( Ajax.ThreadListUpdater, Ajax.Base, { - - initialize: function(_options) { - this.setOptions(_options); - this._options.updateParams = this.updateParams.bind(this); - this._options.handleError = this.handleError.bind(this); - this._options.updateContent = this.updateContent.bind(this); - this._options.lastrevision = 0; - this.threadTimers = new Object(); - this.delta = 0; - this.t = this._options.table; - this.t2 = this._options.visitors_table; - this.periodicalUpdater = new Ajax.PeriodicalUpdater(this._options); - this.old_visitors = new Object(); - this.visitors = new Object(); - this.visitorTimers = new Object(); - }, - - updateParams: function() { - return "since=" + this._options.lastrevision + "&status=" + this._options.istatus + (this._options.showonline ? "&showonline=1" : "") + (this._options.showvisitors ? "&showvisitors=1" : ""); - }, - - setStatus: function(msg) { - this._options.status.innerHTML = msg; - }, - - handleError: function(s) { - this.setStatus( s ); - }, - - updateThread: function(node) { - var id, stateid, vstate, canview = false, canopen = false, canban = false, ban = null, banid = null; - - for( var i = 0; i < node.attributes.length; i++ ) { - var attr = node.attributes[i]; - if( attr.nodeName == "id" ) - id = attr.nodeValue; - else if( attr.nodeName == "stateid" ) - stateid = attr.nodeValue; - else if( attr.nodeName == "state" ) - vstate = attr.nodeValue; - else if( attr.nodeName == "canopen" ) - canopen = true; - else if( attr.nodeName == "canview" ) - canview = true; - else if( attr.nodeName == "canban" ) - canban = true; - else if( attr.nodeName == "ban" ) - ban = attr.nodeValue; - else if( attr.nodeName == "banid" ) - banid = attr.nodeValue; - } - - function setcell(_table, row,id,pcontent) { - var cell = CommonUtils.getCell( id, row, _table ); - if( cell ) - cell.innerHTML = pcontent; - } - - var row = CommonUtils.getRow("thr"+id, this.t); - if( stateid == "closed" ) { - if( row ) { - this.t.deleteRow(row.rowIndex); - } - this.threadTimers[id] = null; - return; - } - - var vname = NodeUtils.getNodeValue(node,"name"); - var actions = HtmlGenerationUtils.viewActionsCell(this._options.agentservl,id,canview,canopen,this._options.showvisitors, this._options.trackedservl); - var vaddr = NodeUtils.getNodeValue(node,"addr"); - var vtime = NodeUtils.getNodeValue(node,"time"); - var agent = NodeUtils.getNodeValue(node,"agent"); - var modified = NodeUtils.getNodeValue(node,"modified"); - var message = NodeUtils.getNodeValue(node,"message"); - var etc = ''+NodeUtils.getNodeValue(node,"useragent")+''; - - if(ban != null) { - etc = ''+NodeUtils.getNodeValue(node,"reason")+''; - } - - if(canban) { - actions += HtmlGenerationUtils.banCell(id,banid); - } - actions = HtmlGenerationUtils.generateOneRowTable(actions); - etc = HtmlGenerationUtils.generateOneRowTable(etc); - - var startRow = CommonUtils.getRow("t"+stateid, this.t); - var endRow = CommonUtils.getRow("t"+stateid+"end", this.t); - - if( row != null && (row.rowIndex <= startRow.rowIndex || row.rowIndex >= endRow.rowIndex ) ) { - this.t.deleteRow(row.rowIndex); - this.threadTimers[id] = null; - row = null; - } - if( row == null ) { - row = this.t.insertRow(startRow.rowIndex+1); - row.className = (ban == "blocked" && stateid != "chat") ? "ban" : "in"+stateid; - row.id = "thr"+id; - this.threadTimers[id] = new Array(vtime,modified,stateid); - CommonUtils.insertCell(row, "name", "visitor", null, null, HtmlGenerationUtils.viewOpenCell(vname,this._options.agentservl,id,canview,canopen,ban,message,stateid!='chat',this._options.showvisitors, this._options.trackedservl)); - CommonUtils.insertCell(row, "actions", "visitor", "center", null, actions); - CommonUtils.insertCell(row, "contid", "visitor", "center", null, vaddr ); - CommonUtils.insertCell(row, "state", "visitor", "center", null, vstate ); - CommonUtils.insertCell(row, "op", "visitor", "center", null, agent ); - CommonUtils.insertCell(row, "time", "visitor", "center", null, this.getTimeSince(vtime) ); - CommonUtils.insertCell(row, "wait", "visitor", "center", null, (stateid!='chat' ? this.getTimeSince(modified) : '-') ); - CommonUtils.insertCell(row, "etc", "visitor", "center", null, etc ); - - if( stateid == 'wait' || stateid == 'prio' ) - return true; - } else { - this.threadTimers[id] = new Array(vtime,modified,stateid); - row.className = (ban == "blocked" && stateid != "chat") ? "ban" : "in"+stateid; - setcell(this.t, row,"name",HtmlGenerationUtils.viewOpenCell(vname,this._options.agentservl,id,canview,canopen,ban,message,stateid!='chat',this._options.showvisitors, this._options.trackedservl)); - setcell(this.t, row, "actions", actions); - setcell(this.t, row,"contid",vaddr); - setcell(this.t, row,"state",vstate); - setcell(this.t, row,"op",agent); - setcell(this.t, row,"time",this.getTimeSince(vtime)); - setcell(this.t, row,"wait",(stateid!='chat' ? this.getTimeSince(modified) : '-')); - setcell(this.t, row,"etc",etc); - } - return false; - }, - - updateQueueMessages: function() { - function queueNotEmpty(t,id) { - var startRow = $(id); - var endRow = $(id+"end"); - if( startRow == null || endRow == null ) { - return false; - } - return startRow.rowIndex+1 < endRow.rowIndex; - } - var _status = $("statustd"); - if( _status) { - var notempty = queueNotEmpty(this.t, "twait") || queueNotEmpty(this.t, "tprio") || queueNotEmpty(this.t, "tchat"); - _status.innerHTML = notempty ? "" : this._options.noclients; - _status.height = notempty ? 5 : 30; - } - }, - - getTimeSince: function(srvtime) { - var secs = Math.floor(((new Date()).getTime()-srvtime-this.delta)/1000); - var minutes = Math.floor(secs/60); - var prefix = ""; - secs = secs % 60; - if( secs < 10 ) - secs = "0" + secs; - if( minutes >= 60 ) { - var hours = Math.floor(minutes/60); - minutes = minutes % 60; - if( minutes < 10 ) - minutes = "0" + minutes; - prefix = hours + ":"; - } - - return prefix + minutes+":"+secs; - }, - - updateTimers: function() { - for (var i in this.threadTimers) { - if (this.threadTimers[i] != null) { - var value = this.threadTimers[i]; - var row = CommonUtils.getRow("thr"+i, this.t); - if( row != null ) { - function setcell(_table, row,id,pcontent) { - var cell = CommonUtils.getCell( id, row, _table ); - if( cell ) - cell.innerHTML = pcontent; - } - setcell(this.t, row,"time",this.getTimeSince(value[0])); - setcell(this.t, row,"wait",(value[2]!='chat' ? this.getTimeSince(value[1]) : '-')); - } - } - } - }, - - updateThreads: function(root) { - var newAdded = false; - var _time = NodeUtils.getAttrValue(root, "time"); - var _revision = NodeUtils.getAttrValue(root, "revision" ); - - if( _time ) - this.delta = (new Date()).getTime() - _time; - if( _revision ) - this._options.lastrevision = _revision; - - for( var i = 0; i < root.childNodes.length; i++ ) { - var node = root.childNodes[i]; - if( node.tagName == 'thread' ) - if( this.updateThread(node) ) - newAdded = true; - } - this.updateQueueMessages(); - this.updateTimers(); - this.setStatus(this._options.istatus ? localized[8] : localized[9]); - if( newAdded ) { - playSound(webimRoot+'/sounds/new_user.wav'); - window.focus(); - if(updaterOptions.showpopup) { - alert(localized[5]); - } - } - }, - - updateOperators: function(root) { - var div = $('onlineoperators'); - if (!div) - return; - - var names = []; - - for( var i = 0; i < root.childNodes.length; i++ ) { - var node = root.childNodes[i]; - if(node.tagName != 'operator') - continue; - - var name = NodeUtils.getAttrValue(node, 'name'); - var isAway = NodeUtils.getAttrValue(node, 'away') != null; - - names[names.length] = - ''+localized[1]+' '+ name; - } - - div.innerHTML = names.join(', '); - }, - - updateVisitorsTimers: function() { - for (var i in this.visitorTimers) { - if (this.visitorTimers[i] != null) { - var value = this.visitorTimers[i]; - var row = CommonUtils.getRow("vis"+i, this.t2); - if( row != null ) { - function setcell(_table, row,id,pcontent) { - var cell = CommonUtils.getCell( id, row, _table ); - if( cell ) - cell.innerHTML = pcontent; - } - setcell(this.t2, row,"time",this.getTimeSince(value[0])); - setcell(this.t2, row,"modified",this.getTimeSince(value[1])); - if (value[2] != null) - setcell(this.t2, row,"invitationtime",this.getTimeSince(value[2])); - } - } - } - }, - - updateVisitor: function(node) { - var id, invited = false; - - for( var i = 0; i < node.attributes.length; i++ ) { - var attr = node.attributes[i]; - if( attr.nodeName == "id" ) - id = attr.nodeValue; - } - - function setcell(_table, row,id,pcontent) { - var cell = CommonUtils.getCell( id, row, _table ); - if( cell ) - cell.innerHTML = pcontent; - } - - var addr = NodeUtils.getNodeValue(node,"addr"); - var username = NodeUtils.getNodeValue(node,"username"); - var useragent = NodeUtils.getNodeValue(node,"useragent"); - var time = NodeUtils.getNodeValue(node,"time"); - var modified = NodeUtils.getNodeValue(node,"modified"); - - var invitations = NodeUtils.getNodeValue(node,"invitations"); - var chats = NodeUtils.getNodeValue(node,"chats"); - - var operator = null; - var invitationtime = null; - var invitation = node.getElementsByTagName("invitation")[0]; - for( var i = 0; i < invitation.childNodes.length; i++ ) { - var childnode = invitation.childNodes[i]; - if( childnode.tagName == 'operator' ) { - operator = childnode.firstChild.nodeValue; - } - else if ( childnode.tagName == 'invitationtime' ) { - invitationtime = childnode.firstChild.nodeValue; - } - } - var state = (operator == null) ? 'free' : 'invited'; - - var row = CommonUtils.getRow("vis"+id, this.t2); - - var startRow = CommonUtils.getRow("vis" + state, this.t2); - var endRow = CommonUtils.getRow("vis" + state + "end", this.t2); - - if( row != null && (row.rowIndex <= startRow.rowIndex || row.rowIndex >= endRow.rowIndex ) ) { - - this.t2.deleteRow(row.rowIndex); - this.visitorTimers[id] = null; - row = null; - } - - if (row == null) { - row = this.t2.insertRow(startRow.rowIndex+1); - row.id = "vis"+id; - this.visitorTimers[id] = new Array(time, modified, invitationtime); - CommonUtils.insertCell(row, "username", "visitor", null, null, HtmlGenerationUtils.viewVisOpenCell(username, this._options.inviteservl, id, this._options.trackedservl, operator==null)); - CommonUtils.insertCell(row, "addr", "visitor", "center", null, addr); - CommonUtils.insertCell(row, "time", "visitor", "center", null, this.getTimeSince(time) ); - CommonUtils.insertCell(row, "modified", "visitor", "center", null, this.getTimeSince(modified) ); - CommonUtils.insertCell(row, "operator", "visitor", "center", null, (operator != null) ? operator : '-'); - CommonUtils.insertCell(row, "invitationtime", "visitor", "center", null, (operator != null ? this.getTimeSince(invitationtime) : '-') ); - CommonUtils.insertCell(row, "invitations", "visitor", "center", null, invitations + ' / ' + chats); - CommonUtils.insertCell(row, "useragent", "visitor", "center", null, useragent); - } - else { - this.visitorTimers[id] = new Array(time, modified, invitationtime); - setcell(this.t2, row, "username",HtmlGenerationUtils.viewVisOpenCell(username, this._options.inviteservl, id, this._options.trackedservl, operator==null)); - setcell(this.t2, row, "addr", addr); - setcell(this.t2, row, "operator", (operator != null) ? operator : '-'); - setcell(this.t2, row, "time", this.getTimeSince(time) ); - setcell(this.t2, row, "modified", this.getTimeSince(modified) ); - setcell(this.t2, row, "invitationtime", (operator != null ? this.getTimeSince(invitationtime) : '-') ); - setcell(this.t2, row, "invitations", invitations + ' / ' + chats); - setcell(this.t2, row, "useragent", useragent); - } - - this.visitors[id] = 1; - - return false; - }, - - removeOldVisitors: function() { - for (id in this.old_visitors) { - if (this.visitors[id] === undefined) { - var row = CommonUtils.getRow("vis"+id, this.t2); - if( row ) { - this.t2.deleteRow(row.rowIndex); - } - this.visitorTimers[id] = null; - } - } - }, - - updateVisitorsList: function(visitors) { - var _status = $("visstatustd"); - if( _status) { - _status.innerHTML = (visitors > 0) ? "" : this._options.novisitors; - _status.height = (visitors > 0) ? 5 : 30; - } - }, - - updateVisitors: function(root) { - - this.old_visitors = this.visitors; - this.visitors = new Object(); - - var visitors_cnt = 0; - for( var i = 0; i < root.childNodes.length; i++ ) { - var node = root.childNodes[i]; - if( node.tagName == 'visitor' ) { - visitors_cnt++; - this.updateVisitor(node); - } - } - this.updateVisitorsTimers(); - this.removeOldVisitors(); - this.updateVisitorsList(visitors_cnt); - }, - - updateContent: function(root) { - if( root.tagName == 'update' ) { - for( var i = 0; i < root.childNodes.length; i++ ) { - var node = root.childNodes[i]; - - if (node.tagName == 'threads') { - this.updateThreads(node); - } else if (node.tagName == 'operators') { - this.updateOperators(node); - } else if (node.tagName == 'visitors') { - this.updateVisitors(node); - } - } - } else if( root.tagName == 'error' ) { - this.setStatus(NodeUtils.getNodeValue(root,"descr") ); - } else { - this.setStatus( "reconnecting" ); - } - } -}); - -function togglemenu() { -if($("sidebar") && $("wcontent") && $("togglemenu")) { - if($("wcontent").className == "contentnomenu") { - $("sidebar").style.display = "block"; - $("wcontent").className = "contentinner"; - $("togglemenu").innerHTML = localized[4]; - } else { - $("sidebar").style.display = "none"; - $("wcontent").className = "contentnomenu"; - $("togglemenu").innerHTML = localized[3]; - } -} -} - -var webimRoot = ""; - -Behaviour.register({ - '#togglemenu' : function(el) { - el.onclick = function() { - togglemenu(); - }; - } -}); - -EventHelper.register(window, 'onload', function(){ - webimRoot = updaterOptions.wroot; - new Ajax.ThreadListUpdater(({table:$("threadlist"),status:$("connstatus"),istatus:0,visitors_table:$("visitorslist")}).extend(updaterOptions || {})); - if(!updaterOptions.havemenu) { - togglemenu(); - } -}); diff --git a/src/messenger/webim/js/source/users/app.js b/src/messenger/webim/js/source/users/app.js new file mode 100644 index 00000000..4140d08d --- /dev/null +++ b/src/messenger/webim/js/source/users/app.js @@ -0,0 +1,104 @@ +/** + * @preserve This file is part of Mibew Messenger project. + * http://mibew.org + * + * Copyright (c) 2005-2011 Mibew Messenger Community + * License: http://mibew.org/license.php + */ + +(function(Mibew, Backbone, _){ + + // Create application instance + var App = new Backbone.Marionette.Application(); + + // Define regions + App.addRegions({ + agentsRegion: '#agents-region', + statusPanelRegion: '#status-panel-region', + threadsRegion: '#threads-region', + visitorsRegion: '#visitors-region', + soundRegion: '#sound-region' + }); + + // Initialize application + App.addInitializer(function(options){ + + // Create some shortcuts + var objs = Mibew.Objects; + var models = Mibew.Objects.Models; + var colls = Mibew.Objects.Collections; + + // Initialize Server, Thread and User + objs.server = new Mibew.Server(_.extend( + {'interactionType': MibewAPIUsersInteraction}, + options.server + )); + + // Initialize Page + models.page = new Mibew.Models.Page(options.page); + + // Initialize Agent + models.agent = new Mibew.Models.Agent(options.agent); + + // Initialize threads collection + colls.threads = new Mibew.Collections.Threads(); + App.threadsRegion.show(new Mibew.Views.ThreadsCollection({ + collection: colls.threads + })); + + // Initialize visitors collection + if (options.page.showOnlineOperators) { + colls.visitors = new Mibew.Collections.Visitors(); + App.visitorsRegion.show(new Mibew.Views.VisitorsCollection({ + collection: colls.visitors + })); + } + + // Initialize status panel + models.statusPanel = new Mibew.Models.StatusPanel(); + App.statusPanelRegion.show(new Mibew.Views.StatusPanel({ + model: models.statusPanel + })); + + // Initialize agents collection and show it + if (options.page.showOnlineOperators) { + colls.agents = new Mibew.Collections.Agents(); + App.agentsRegion.show(new Mibew.Views.AgentsCollection({ + collection: colls.agents + })); + } + + // Initialize sounds + models.sound = new Mibew.Models.Sound(); + App.soundRegion.show(new Mibew.Views.Sound({ + model: models.sound + })); + + // Periodically call update function at the server side + objs.server.callFunctionsPeriodically( + function() { + // Build functions list + return [ + { + "function": "update", + "arguments": { + "return": {}, + "references": {}, + "agentId": models.agent.id + } + } + ]; + }, + function(args) {} + ); + + }); + + App.on('start', function() { + // Run Server updater + Mibew.Objects.server.runUpdater(); + }); + + Mibew.Application = App; + +})(Mibew, Backbone, _); diff --git a/src/messenger/webim/js/source/users/collection_views/agents_collection.js b/src/messenger/webim/js/source/users/collection_views/agents_collection.js new file mode 100644 index 00000000..2e21e7a8 --- /dev/null +++ b/src/messenger/webim/js/source/users/collection_views/agents_collection.js @@ -0,0 +1,66 @@ +/** + * @preserve This file is part of Mibew Messenger project. + * http://mibew.org + * + * Copyright (c) 2005-2011 Mibew Messenger Community + * License: http://mibew.org/license.php + */ + +(function(Mibew) { + + /** + * @class Represents online agents bar + */ + Mibew.Views.AgentsCollection = Mibew.Views.CollectionBase.extend( + /** @lends Mibew.Views.AgentsCollection.prototype */ + { + /** + * Default item view constructor. + * @type Function + */ + itemView: Mibew.Views.Agent, + + /** + * Class name for view's DOM element + * @type String + */ + className: 'agents-collection', + + /** + * Map collection events to the view methods + * @type Object + */ + collectionEvents: { + 'sort add remove reset': 'render' + }, + + /** + * View initializer + */ + initialize: function() { + // Register events + this.on('itemview:before:render', this.updateIndexes, this); + }, + + /** + * Update 'isModelFirst' and 'isModelLast' child views fields on + * collection 'sort', 'add', 'remove' and 'reset' events.Indexies + */ + updateIndexes: function(childView) { + // Create some shortcuts + var collection = this.collection; + var model = childView.model; + + if (model) { + // Update isModelFirst and isModelLast properties + childView.isModelFirst = (collection.indexOf(model) == 0); + childView.isModelLast = ( + collection.indexOf(model) == (collection.length - 1) + ); + } + } + + } + ); + +})(Mibew); \ No newline at end of file diff --git a/src/messenger/webim/js/source/users/collection_views/threads_collection.js b/src/messenger/webim/js/source/users/collection_views/threads_collection.js new file mode 100644 index 00000000..60d1978f --- /dev/null +++ b/src/messenger/webim/js/source/users/collection_views/threads_collection.js @@ -0,0 +1,244 @@ +/** + * @preserve This file is part of Mibew Messenger project. + * http://mibew.org + * + * Copyright (c) 2005-2011 Mibew Messenger Community + * License: http://mibew.org/license.php + */ + +(function(Mibew, Backbone, Handlebars, _) { + + /** + * @class Represents threads list + */ + Mibew.Views.ThreadsCollection = Backbone.Marionette.CompositeView.extend( + /** @lends Mibew.Views.ThreadsCollection.prototype */ + { + template: Handlebars.templates.threads_collection, + + /** + * Default item view constructor. + * @type Function + */ + itemView: Mibew.Views.QueuedThread, + + /** + * DOM element for collection items + * @type String + */ + itemViewContainer: '#threads-container', + + /** + * Empty view constructor. + * @type Function + */ + emptyView: Mibew.Views.NoThreads, + + /** + * Class name for view's DOM element + * @type String + */ + className: 'threads-collection', + + /** + * Map collection events to the view methods + * @type Object + */ + collectionEvents: { + 'sort': 'renderCollection', + 'sort:field': 'createSortField', + 'add': 'threadAdded' + }, + + /** + * Pass some options to item view + * @returns {Object} Options object + */ + itemViewOptions: function(model) { + var page = Mibew.Objects.Models.page; + return { + tagName: page.get('threadTag'), + collection: model.get('controls') + } + }, + + /** + * View initializer. + * @todo Do something with timer. Do not render whole view! + */ + initialize: function() { + // Rerender view to keep timers in items views working + window.setInterval(_.bind(this.renderCollection, this), 2 * 1000); + // Register events + this.on('itemview:before:render', this.updateStyles, this); + }, + + /** + * Update thread DOM element classes depending on thread params. + * @param {Mibew.Views.QueuedThread} childView View instance for + * thread in the queue + */ + updateStyles: function(childView) { + // Create some shortcuts + var collection = this.collection; + var thread = childView.model; + var self = this; + + if (thread.id) { + var queueCode = this.getQueueCode(thread); + var isLast = false, isFirst = false; + + // Filter collection by queue type + var filteredThreads = collection.filter(function(model) { + return self.getQueueCode(model) == queueCode; + }); + + // Get isFirst and isLast flags + if (filteredThreads.length > 0) { + isFirst = (filteredThreads[0].id == thread.id); + isLast = ( + filteredThreads[filteredThreads.length-1].id == thread.id + ); + } + + // Remove all old styles + if (childView.lastStyles.length > 0) { + for(var i = 0, l = childView.lastStyles.length; i < l; i++) { + childView.$el.removeClass(childView.lastStyles[i]); + } + childView.lastStyles = []; + } + + // Create new style name + var style = ((queueCode != this.QUEUE_BAN)?'in':'') + + this.queueCodeToString(queueCode); + + // Store new styles + childView.lastStyles.push(style); + if (isFirst) { + childView.lastStyles.push(style + "-first"); + } + if (isLast) { + childView.lastStyles.push(style + "-last"); + } + + // Add styles names to DOM element + for(var i = 0, l = childView.lastStyles.length; i < l; i++) { + childView.$el.addClass(childView.lastStyles[i]); + } + } + }, + + /** + * This is the 'sort:field' event handler. + * Make threads sort by queue code and waiting time. + * @param {Mibew.Models.QueuedThread} thread Thread model + * @param {Object} sort Sorting object that contains property + * 'field' - a string by which threads will be sorted + */ + createSortField: function(thread, sort) { + var queueCode = this.getQueueCode(thread) || 'Z'; + sort.field = queueCode.toString() + + '_' + + thread.get('waitingTime').toString() + }, + + /** + * Play sound when new thread add to collection + */ + threadAdded: function() { + // Build sound path + var path = Mibew.Objects.Models.page.get('webimRoot'); + if (path) { + path += '/sounds/new_user.wav'; + // Play sound + Mibew.Objects.Models.sound.play(path); + } + }, + + /** + * Calculate queue code for thread + * @returns {Boolean|Number} Queue code or false if code is unknown + */ + getQueueCode: function(thread) { + var state = thread.get('state'); + if (thread.get('ban') != false + && state != thread.STATE_CHATTING) { + return this.QUEUE_BAN; + } + if (state == thread.STATE_QUEUE + || state == thread.STATE_LOADING) { + return this.QUEUE_WAITING; + } + if (state == thread.STATE_CLOSED + || state == thread.STATE_LEFT) { + return this.QUEUE_CLOSED; + } + if (state == thread.STATE_WAITING) { + return this.QUEUE_PRIO; + } + if (state == thread.STATE_CHATTING) { + return this.QUEUE_CHATTING; + } + + return false; + }, + + /** + * Convert numeric queue code to string one + * @returns {String} + */ + queueCodeToString: function(code) { + if (code == this.QUEUE_PRIO) { + return "prio"; + } + if (code == this.QUEUE_WAITING) { + return "wait"; + } + if (code == this.QUEUE_CHATTING) { + return "chat"; + } + if (code == this.QUEUE_BAN) { + return "ban"; + } + if (code == this.QUEUE_CLOSED) { + return "closed"; + } + return ""; + }, + + /** Queues codes */ + + /** + * Priority queue. Includes threads with STATE_WAITING state + */ + QUEUE_PRIO: 1, + + /** + * Waiting queue. Includes threads with STATE_LOADING and + * STATE_WAITING states. + */ + QUEUE_WAITING: 2, + + /** + * Chatting queue. Includes threads with STATE_CHATTING state + */ + QUEUE_CHATTING: 3, + + /** + * Ban queue. Includes all blocked threads. + */ + QUEUE_BAN: 4, + + /** + * Closed queue. Includes all threads with STATE_CLOSED and + * STATE_LEFT states + */ + QUEUE_CLOSED: 5 + + /** End of queues codes */ + + } + ); + +})(Mibew, Backbone, Handlebars, _); \ No newline at end of file diff --git a/src/messenger/webim/js/source/users/collection_views/visitors_collection.js b/src/messenger/webim/js/source/users/collection_views/visitors_collection.js new file mode 100644 index 00000000..2ecd4c74 --- /dev/null +++ b/src/messenger/webim/js/source/users/collection_views/visitors_collection.js @@ -0,0 +1,76 @@ +/** + * @preserve This file is part of Mibew Messenger project. + * http://mibew.org + * + * Copyright (c) 2005-2011 Mibew Messenger Community + * License: http://mibew.org/license.php + */ + +(function(Mibew, Backbone, Handlebars, _) { + + /** + * @class Represents visitors list + */ + Mibew.Views.VisitorsCollection = Backbone.Marionette.CompositeView.extend( + /** @lends Mibew.Views.VisitorsCollection.prototype */ + { + template: Handlebars.templates.visitors_collection, + + /** + * Default item view constructor. + * @type Function + */ + itemView: Mibew.Views.Visitor, + + /** + * DOM element for collection items + * @type String + */ + itemViewContainer: '#visitors-container', + + /** + * Empty view constructor. + * @type Function + */ + emptyView: Mibew.Views.NoVisitors, + + /** + * Class name for view's DOM element + * @type String + */ + className: 'visitors-collection', + + /** + * Map collection events to the view methods + * @type Object + */ + collectionEvents: { + 'sort': 'renderCollection' + }, + + /** + * Pass some options to item view + * @returns {Object} Options object + */ + itemViewOptions: function(model) { + var page = Mibew.Objects.Models.page; + return { + tagName: page.get('visitorTag'), + collection: model.get('controls') + } + }, + + /** + * View initializer. + * @todo Do something with timer. Do not render whole view! + */ + initialize: function() { + // Rerender view to keep timers in items views working + window.setInterval(_.bind(this.renderCollection, this), 2 * 1000); + // Register events + this.on('itemview:before:render', this.updateStyles, this); + } + } + ); + +})(Mibew, Backbone, Handlebars, _); \ No newline at end of file diff --git a/src/messenger/webim/js/source/users/collections/agents.js b/src/messenger/webim/js/source/users/collections/agents.js new file mode 100644 index 00000000..f6eb4fdf --- /dev/null +++ b/src/messenger/webim/js/source/users/collections/agents.js @@ -0,0 +1,69 @@ +/** + * @preserve This file is part of Mibew Messenger project. + * http://mibew.org + * + * Copyright (c) 2005-2011 Mibew Messenger Community + * License: http://mibew.org/license.php + */ + +(function(Mibew, Backbone, _){ + + /** + * @class Represents collection of agents + */ + Mibew.Collections.Agents = Backbone.Collection.extend( + /** @lends Mibew.Collections.Agents.prototype */ + { + /** + * Model type of the collection items + */ + model: Mibew.Models.Agent, + + /** + * Use for sort controls in collection + * @param {Backbone.Model} model Agent model + */ + comparator: function(model) { + return model.get('name'); + }, + + /** + * Collection initializer + */ + initialize: function() { + // Register some shortcuts + var agent = Mibew.Objects.Models.agent; + + // Call updateOperators periodically at the server + Mibew.Objects.server.callFunctionsPeriodically( + function(){ + return [ + { + 'function': 'updateOperators', + 'arguments': { + 'agentId': agent.id, + 'return': { + 'operators': 'operators' + }, + 'references': {} + } + } + ]; + }, + _.bind(this.updateOperators, this) + ); + }, + + /** + * Update available agents. + * @param {Object} args Arguments from the server + */ + updateOperators: function(args) { + this.update(args.operators); + } + } + ); + +})(Mibew, Backbone, _); + + diff --git a/src/messenger/webim/js/source/users/collections/threads.js b/src/messenger/webim/js/source/users/collections/threads.js new file mode 100644 index 00000000..a59e7025 --- /dev/null +++ b/src/messenger/webim/js/source/users/collections/threads.js @@ -0,0 +1,156 @@ +/** + * @preserve This file is part of Mibew Messenger project. + * http://mibew.org + * + * Copyright (c) 2005-2011 Mibew Messenger Community + * License: http://mibew.org/license.php + */ + +(function(Mibew, Backbone, _){ + + /** + * @class Represents threads collection + */ + Mibew.Collections.Threads = Backbone.Collection.extend( + /** @lends Mibew.Collections.Threads.prototype */ + { + /** + * Model type of the collection items + * @type Function + */ + model: Mibew.Models.QueuedThread, + + /** + * Collection initializer + */ + initialize: function() { + // Initialize fields and methods + + /** + * Last threads revision number. Prevent transfering not + * modified threads. + * @type Number + * @fieldOf Mibew.Collections.Threads + */ + this.revision = 0; + + // Register some shortcuts + var self = this; + var agent = Mibew.Objects.Models.agent; + + // Call updateThreads periodically at the server + Mibew.Objects.server.callFunctionsPeriodically( + function(){ + return [ + { + 'function': 'currentTime', + 'arguments': { + 'agentId': agent.id, + 'return': { + 'time': 'currentTime' + }, + 'references': {} + } + }, + { + 'function': 'updateThreads', + 'arguments': { + 'agentId': agent.id, + 'revision': self.revision, + 'return': { + 'threads': 'threads', + 'lastRevision': 'lastRevision' + }, + 'references': {} + } + } + ]; + }, + _.bind(this.updateThreads, this) + ); + }, + + /** + * Use for sort threads in collection. + * By default threads sort by state and waiting time. + * Triggers 'sort:field' event after sort field generated. + * @param {Mibew.Models.QueuedThread} thread Thread model + */ + comparator: function(thread) { + // Create default sort field + var sort = { + field: thread.get('waitingTime').toString() + } + + // Trigger event to provide an ability to change sorting order + this.trigger('sort:field', thread, sort); + + // Return sort field + return sort.field; + }, + + /** + * Update threads list. + * Trigger 'before:update:threads' event and pass array of raw + * threads data as argument to event handler. + * Also trigger 'after:update:threads' event. + * @param {Object} args Arguments returned from server + */ + updateThreads: function(args) { + if (args.errorCode == 0) { + + if (args.threads.length > 0) { + // Fix time difference between server and client + var delta; + if (args.currentTime) { + delta = Math.round((new Date()).getTime() / 1000) + - args.currentTime; + } else { + delta = 0; + } + for(var i = 0, l = args.threads.length; i < l; i++) { + args.threads[i].totalTime + = parseInt(args.threads[i].totalTime) + delta; + args.threads[i].waitingTime + = parseInt(args.threads[i].waitingTime) + delta; + } + + // Trigger event. Event handlers can change threads info + this.trigger('before:update:threads', args.threads); + + // Create shortcuts for thread states + var stateClosed = Mibew.Models.Thread.prototype.STATE_CLOSED; + var stateLeft = Mibew.Models.Thread.prototype.STATE_LEFT; + + // Define empty array for threads that should be remove + var remove = []; + + // Update threads list + this.update(args.threads, {remove: false, sort: false}); + + // Get closed and left thread. Collect them into + // remove array + remove = this.filter(function(thread) { + return (thread.get('state') == stateClosed + || thread.get('state') == stateLeft); + }); + // Remove closed and left threads + if (remove.length > 0) { + this.remove(remove); + } + + // Sort residual collection + this.sort(); + + // Trigger event + this.trigger('after:update:threads'); + } + this.revision = args.lastRevision; + } + } + } + ); + +})(Mibew, Backbone, _); + + diff --git a/src/messenger/webim/js/source/users/collections/visitors.js b/src/messenger/webim/js/source/users/collections/visitors.js new file mode 100644 index 00000000..c2113861 --- /dev/null +++ b/src/messenger/webim/js/source/users/collections/visitors.js @@ -0,0 +1,117 @@ +/** + * @preserve This file is part of Mibew Messenger project. + * http://mibew.org + * + * Copyright (c) 2005-2011 Mibew Messenger Community + * License: http://mibew.org/license.php + */ + +(function(Mibew, Backbone, _){ + + /** + * @class Represents visitors collection + */ + Mibew.Collections.Visitors = Backbone.Collection.extend( + /** @lends Mibew.Collections.Visitors.prototype */ + { + /** + * Model type of the collection items + * @type Function + */ + model: Mibew.Models.Visitor, + + /** + * Collection initializer + */ + initialize: function() { + // Register some shortcuts + var agent = Mibew.Objects.Models.agent; + + // Call updateThreads periodically at the server + Mibew.Objects.server.callFunctionsPeriodically( + function(){ + return [ + { + 'function': 'currentTime', + 'arguments': { + 'agentId': agent.id, + 'return': { + 'time': 'currentTime' + }, + 'references': {} + } + }, + { + 'function': 'updateVisitors', + 'arguments': { + 'agentId': agent.id, + 'return': { + 'visitors': 'visitors' + }, + 'references': {} + } + } + ]; + }, + _.bind(this.updateVisitors, this) + ); + }, + + /** + * Use for sort visitors in collection. + * By default visitors sort by firstTime field. + * Triggers 'sort:field' event after sort field generated. + * @param {Mibew.Models.Visitor} visitor Visitor model + */ + comparator: function(visitor) { + // Create default sort field + var sort = { + field: visitor.get('firstTime').toString() + } + + // Trigger event to provide an ability to change sorting order + this.trigger('sort:field', visitor, sort); + + // Return sort field + return sort.field; + }, + + /** + * Update visitors list. + * Trigger 'before:update:visitors' event and pass array of raw + * visitors data as argument to event handler. + * Also trigger 'after:update:visitors' event. + * @param {Object} args Arguments returned from server + */ + updateVisitors: function(args) { + if (args.errorCode == 0) { + // Fix time difference between server and client + var delta; + if (args.currentTime) { + delta = Math.round((new Date()).getTime() / 1000) + - args.currentTime; + } else { + delta = 0; + } + for(var i = 0, l = args.visitors.length; i < l; i++) { + args.visitors[i].lastTime = parseInt(args.visitors[i].lastTime) + delta; + args.visitors[i].firstTime = parseInt(args.visitors[i].firstTime) + delta; + } + + // Trigger event. Event handlers can change visitors info + this.trigger('before:update:visitors', args.visitors); + + // Update collection + this.update(args.visitors); + + // Trigger event + this.trigger('after:update:visitors'); + } + } + + } + ); + +})(Mibew, Backbone, _); + + diff --git a/src/messenger/webim/js/source/users/handlebars_helpers.js b/src/messenger/webim/js/source/users/handlebars_helpers.js new file mode 100644 index 00000000..a9572e6b --- /dev/null +++ b/src/messenger/webim/js/source/users/handlebars_helpers.js @@ -0,0 +1,33 @@ +/** + * @preserve This file is part of Mibew Messenger project. + * http://mibew.org + * + * Copyright (c) 2005-2011 Mibew Messenger Community + * License: http://mibew.org/license.php + */ + +(function(Handlebars){ + /** + * Register 'formatTimeToNow' Handlebars helper. + * + * This helper takes unix timestamp as argument and return difference + * between current timestamp and passed one in "HH:MM:SS" format. + */ + Handlebars.registerHelper('formatTimeSince', function(unixTimestamp){ + // Get time diff + var diff = Math.round((new Date()).getTime() / 1000) - unixTimestamp; + // Get time parts + var seconds = diff % 60; + var minutes = Math.floor(diff / 60) % 60; + var hours = Math.floor(diff / (60 * 60)); + // Get result parts + var result = []; + if (hours > 0) { + result.push(hours); + } + result.push(minutes < 10 ? '0' + minutes : minutes); + result.push(seconds < 10 ? '0' + seconds : seconds); + // Build result string + return result.join(':'); + }); +})(Handlebars); \ No newline at end of file diff --git a/src/messenger/webim/js/source/users/init.js b/src/messenger/webim/js/source/users/init.js new file mode 100644 index 00000000..78149e74 --- /dev/null +++ b/src/messenger/webim/js/source/users/init.js @@ -0,0 +1,33 @@ +/** + * @preserve This file is part of Mibew Messenger project. + * http://mibew.org + * + * Copyright (c) 2005-2011 Mibew Messenger Community + * License: http://mibew.org/license.php + */ + +(function(Mibew){ + + /** + * @namespace Holds application region constructors + */ + Mibew.Regions = {}; + + /** + * @namespace Holds popup windows control + */ + Mibew.Popup = {}; + + /** + * Open new window + * @param {String} link URL address of page to open + * @param {String} id Id of new window + * @param {String} params Window params passed to window.open method + */ + Mibew.Popup.open = function(link, id, params) { + var newWindow = window.open(link, id, params); + newWindow.focus(); + newWindow.opener = window; + } + +})(Mibew); \ No newline at end of file diff --git a/src/messenger/webim/js/source/users/mibewapi_users_interaction.js b/src/messenger/webim/js/source/users/mibewapi_users_interaction.js new file mode 100644 index 00000000..da17a789 --- /dev/null +++ b/src/messenger/webim/js/source/users/mibewapi_users_interaction.js @@ -0,0 +1,30 @@ +/** + * @preserve This file is part of Mibew Messenger project. + * http://mibew.org + * + * Copyright (c) 2005-2011 Mibew Messenger Community + * License: http://mibew.org/license.php + */ + +/** + * Represents User list Window to core interaction type + * + * @constructor + */ +MibewAPIUsersInteraction = function() { + this.obligatoryArguments = { + '*': { + 'agentId': null, + 'return': {}, + 'references': {} + }, + 'result': { + 'errorCode': 0 + } + }; + + this.reservedFunctionNames = [ + 'result' + ]; +} +MibewAPIUsersInteraction.prototype = new MibewAPIInteraction(); \ No newline at end of file diff --git a/src/messenger/webim/js/source/users/model_views/agent.js b/src/messenger/webim/js/source/users/model_views/agent.js new file mode 100644 index 00000000..3bbb253e --- /dev/null +++ b/src/messenger/webim/js/source/users/model_views/agent.js @@ -0,0 +1,81 @@ +/** + * @preserve This file is part of Mibew Messenger project. + * http://mibew.org + * + * Copyright (c) 2005-2011 Mibew Messenger Community + * License: http://mibew.org/license.php + */ + +(function(Mibew, Backbone, Handlebars) { + + /** + * @class Represents agent view. + */ + Mibew.Views.Agent = Backbone.Marionette.ItemView.extend( + /** @lends Mibew.Views.Agent.prototype */ + { + /** + * Template function + * @type Function + */ + template: Handlebars.templates.agent, + + /** + * Name of wrapper tag for an agent view + * @type String + */ + tagName: 'span', + + /** + * CSS class name for view's DOM element + * @type String + */ + className: 'agent', + + /** + * Map model events to the view methods + * @type Object + */ + modelEvents: { + 'change': 'render' + }, + + /** + * View initializer + */ + initialize: function() { + // Initialize fields and methods of the instance + + /** + * Indicates if model related to the view is first in collection + * @type Boolean + * @fieldOf Mibew.Views.Agent + */ + this.isModelFirst = false; + + /** + * Indicates if model related to the view is last in collection + * @type Boolean + * @fieldOf Mibew.Views.Agent + */ + this.isModelLast = false; + }, + + /** + * Override Backbone.Marionette.ItemView.serializeData to pass some + * extra fields to template. Add 'isFirst' and 'isLast' values. + * Following additional values available in template: + * - 'isFirst': indicates if model is first in collection + * - 'isLast': indicates if model is last in collection + * @returns {Object} Template data + */ + serializeData: function() { + var data = this.model.toJSON(); + data.isFirst = this.isModelFirst; + data.isLast = this.isModelLast; + return data; + } + } + ); + +})(Mibew, Backbone, Handlebars); \ No newline at end of file diff --git a/src/messenger/webim/js/source/users/model_views/no_threads.js b/src/messenger/webim/js/source/users/model_views/no_threads.js new file mode 100644 index 00000000..e40bb59e --- /dev/null +++ b/src/messenger/webim/js/source/users/model_views/no_threads.js @@ -0,0 +1,34 @@ +/** + * @preserve This file is part of Mibew Messenger project. + * http://mibew.org + * + * Copyright (c) 2005-2011 Mibew Messenger Community + * License: http://mibew.org/license.php + */ + +(function(Mibew, Backbone, Handlebars) { + + /** + * @class Represents empty thread view. + */ + Mibew.Views.NoThreads = Backbone.Marionette.ItemView.extend( + /** @lends Mibew.Views.NoThreads.prototype */ + { + /** + * Template function + * @type Function + */ + template: Handlebars.templates.no_threads, + + /** + * View initializer + * @param {Object} options Options object passed from + * {@link Mibew.Views.ThreadsCollection.prototype.itemViewOptions} + */ + initialize: function(options) { + this.tagName = options.tagName; + } + } + ); + +})(Mibew, Backbone, Handlebars); \ No newline at end of file diff --git a/src/messenger/webim/js/source/users/model_views/no_visitors.js b/src/messenger/webim/js/source/users/model_views/no_visitors.js new file mode 100644 index 00000000..68e20f9e --- /dev/null +++ b/src/messenger/webim/js/source/users/model_views/no_visitors.js @@ -0,0 +1,34 @@ +/** + * @preserve This file is part of Mibew Messenger project. + * http://mibew.org + * + * Copyright (c) 2005-2011 Mibew Messenger Community + * License: http://mibew.org/license.php + */ + +(function(Mibew, Backbone, Handlebars) { + + /** + * @class Represents empty visitor view. + */ + Mibew.Views.NoVisitors = Backbone.Marionette.ItemView.extend( + /** @lends Mibew.Views.NoVisitors.prototype */ + { + /** + * Template function + * @type Function + */ + template: Handlebars.templates.no_visitors, + + /** + * View initializer + * @param {Object} options Options object passed from + * {@link Mibew.Views.VisitorsCollection.prototype.itemViewOptions} + */ + initialize: function(options) { + this.tagName = options.tagName; + } + } + ); + +})(Mibew, Backbone, Handlebars); \ No newline at end of file diff --git a/src/messenger/webim/js/source/users/model_views/queued_thread.js b/src/messenger/webim/js/source/users/model_views/queued_thread.js new file mode 100644 index 00000000..b38e8257 --- /dev/null +++ b/src/messenger/webim/js/source/users/model_views/queued_thread.js @@ -0,0 +1,240 @@ +/** + * @preserve This file is part of Mibew Messenger project. + * http://mibew.org + * + * Copyright (c) 2005-2011 Mibew Messenger Community + * License: http://mibew.org/license.php + */ + +(function(Mibew, Handlebars) { + + /** + * @class Represents thread view. + */ + Mibew.Views.QueuedThread = Mibew.Views.CompositeBase.extend( + /** @lends Mibew.Views.QueuedThread.prototype */ + { + /** + * Template function + * @type Function + */ + template: Handlebars.templates.queued_thread, + + /** + * Default item view constructor. + * @type Function + */ + itemView: Mibew.Views.Control, + + /** + * DOM element for collection items + * @type String + */ + itemViewContainer: '.thread-controls', + + /** + * CSS class name for view's DOM element + * @type String + */ + className: 'thread', + + /** + * Map model events to the view methods + * @type Object + */ + modelEvents: { + 'change': 'render' + }, + + /** + * UI events hash. + * Map UI events on the view methods. + * @type Object + */ + events: { + 'click .open-dialog': 'openDialog', + 'click .view-control': 'viewDialog', + 'click .track-control': 'showTrack', + 'click .ban-control': 'showBan', + 'click .geo-link': 'showGeoInfo', + 'click .first-message a': 'showFirstMessage' + }, + + /** + * View initializer + */ + initialize: function() { + // Initialize fields and methods of the instance + + /** + * Contain list of last styles added to the thread DOM element. + * Used by {@link Mibew.Views.ThreadsCollection} view. + * @type Array + * @fieldOf Mibew.Views.Thread + */ + this.lastStyles = []; + }, + + /** + * Override Backbone.Marionette.ItemView.serializeData to pass some + * extra fields to template. + * Following additional values available in template: + * - 'stateDesc': thread state description + * - 'chatting': indicates if thread have STATE_CHATTING + * - 'tracked': indicates if tracked system is enabled + * - 'firstMessagePreview': first message limited by 30 characters + * @returns {Object} Template data + */ + serializeData: function() { + var thread = this.model + var page = Mibew.Objects.Models.page; + var data = thread.toJSON(); + data.stateDesc = this.stateToDesc(thread.get('state')); + data.chatting = (thread.get('state') == thread.STATE_CHATTING); + data.tracked = page.get('showVisitors'); + if (data.firstMessage) { + data.firstMessagePreview = data.firstMessage.length > 30 + ? data.firstMessage.substring(0,30) + '...' + : data.firstMessage + } + return data; + }, + + /** + * Convert numeric thread state code to string description of a + * state + * @param {Number} state Thread state code + * @returns {String} Description of the thread state + */ + stateToDesc: function(state) { + var l = Mibew.Localization; + if (state == this.model.STATE_QUEUE) { + return l.get('chat.thread.state_wait'); + } + if (state == this.model.STATE_WAITING) { + return l.get('chat.thread.state_wait_for_another_agent'); + } + if (state == this.model.STATE_CHATTING) { + return l.get('chat.thread.state_chatting_with_agent'); + } + if (state == this.model.STATE_CLOSED) { + return l.get('chat.thread.state_closed'); + } + if (state == this.model.STATE_LOADING) { + return l.get('chat.thread.state_loading'); + } + return ""; + }, + + /** + * Open window with geo information + */ + showGeoInfo: function() { + var ip = this.model.get('userIp'); + if (ip) { + var page = Mibew.Objects.Models.page; + var geoLink = page.get('geoLink') + .replace("{ip}", ip); + Mibew.Popup.open( + geoLink, + 'ip' + ip, + page.get('geoWindowParams') + ); + } + }, + + /** + * Open chat window in dialog mode + */ + openDialog: function() { + // Create some shortcuts + var thread = this.model; + var viewOnly = (thread.get('state') == thread.STATE_CHATTING) + && thread.get('canView'); + + // Show dialog window + this.showDialogWindow(viewOnly); + }, + + /** + * Open chat window in view mode + */ + viewDialog: function() { + this.showDialogWindow(true); + }, + + /** + * Open chat window + * @param {Boolean} viewOnly Indicates if chat window should be open + * in view mode + */ + showDialogWindow: function(viewOnly) { + // Create some shortcuts + var thread = this.model; + var threadId = thread.id; + var page = Mibew.Objects.Models.page; + + // Open chat window + Mibew.Popup.open( + page.get('agentLink') + + '?thread=' + + threadId + + (viewOnly ? '&viewonly=true': ''), + 'ImCenter' + threadId, + page.get('chatWindowParams') + ); + }, + + /** + * Open tracked window + */ + showTrack: function() { + // Create some shortcuts + var threadId = this.model.id; + var page = Mibew.Objects.Models.page; + + // Open tracked window + Mibew.Popup.open( + page.get('trackedLink') + + '?thread=' + + threadId, + 'ImTracked' + threadId, + page.get('trackedUserWindowParams') + ); + }, + + /** + * Open ban window + */ + showBan: function() { + // Create some shortcuts + var thread = this.model; + var ban = thread.get('ban'); + var page = Mibew.Objects.Models.page; + + // Open ban window + Mibew.Popup.open( + page.get('banLink') + + '?' + + (ban !== false + ? 'id='+ban.id + : 'thread='+ thread.id), + 'ImBan' + ban.id, + page.get('banWindowParams') + ); + }, + + /** + * Show first message from user to agent + */ + showFirstMessage: function() { + var message = this.model.get('firstMessage'); + if (message) { + alert(message); + } + } + + } + ); + +})(Mibew, Handlebars); \ No newline at end of file diff --git a/src/messenger/webim/js/source/users/model_views/status_panel.js b/src/messenger/webim/js/source/users/model_views/status_panel.js new file mode 100644 index 00000000..860a6756 --- /dev/null +++ b/src/messenger/webim/js/source/users/model_views/status_panel.js @@ -0,0 +1,74 @@ +/** + * @preserve This file is part of Mibew Messenger project. + * http://mibew.org + * + * Copyright (c) 2005-2011 Mibew Messenger Community + * License: http://mibew.org/license.php + */ + +(function(Mibew, Backbone, Handlebars) { + + /** + * @class Represents status panel view. + */ + Mibew.Views.StatusPanel = Backbone.Marionette.ItemView.extend( + /** @lends Mibew.Views.StatusPanel.prototype */ + { + /** + * Template function + * @type Function + */ + template: Handlebars.templates.status_panel, + + /** + * Map model events to the view methods + * @type Object + */ + modelEvents: { + 'change': 'render' + }, + + /** + * Shortcuts for ui elements + * @type Object + */ + ui: { + changeStatus: '#change-status' + }, + + /** + * Map ui events to view methods + * @type Object + */ + events: { + 'click #change-status': 'changeAgentStatus' + }, + + /** + * View initializer + */ + initialize: function() { + Mibew.Objects.Models.agent.on('change', this.render, this); + }, + + /** + * Changes users status + */ + changeAgentStatus: function() { + this.model.changeAgentStatus(); + }, + + /** + * Override Backbone.Marionette.ItemView.serializeData to pass some + * extra fields to template. + * @returns {Object} Template data + */ + serializeData: function() { + var data = this.model.toJSON(); + data.agent = Mibew.Objects.Models.agent.toJSON(); + return data; + } + } + ); + +})(Mibew, Backbone, Handlebars); \ No newline at end of file diff --git a/src/messenger/webim/js/source/users/model_views/visitor.js b/src/messenger/webim/js/source/users/model_views/visitor.js new file mode 100644 index 00000000..6f3bfb6f --- /dev/null +++ b/src/messenger/webim/js/source/users/model_views/visitor.js @@ -0,0 +1,117 @@ +/** + * @preserve This file is part of Mibew Messenger project. + * http://mibew.org + * + * Copyright (c) 2005-2011 Mibew Messenger Community + * License: http://mibew.org/license.php + */ + +(function(Mibew, Handlebars) { + + /** + * @class Represents visitor view. + */ + Mibew.Views.Visitor = Mibew.Views.CompositeBase.extend( + /** @lends Mibew.Views.Visitor.prototype */ + { + /** + * Template function + * @type Function + */ + template: Handlebars.templates.visitor, + + /** + * Default item view constructor. + * @type Function + */ + itemView: Mibew.Views.Control, + + /** + * DOM element for collection items + * @type String + */ + itemViewContainer: '.visitor-controls', + + /** + * CSS class name for view's DOM element + * @type String + */ + className: 'visitor', + + /** + * Map model events to the view methods + * @type Object + */ + modelEvents: { + 'change': 'render' + }, + + /** + * UI events hash. + * Map UI events on the view methods. + * @type Object + */ + events: { + 'click .invite-link': 'inviteUser', + 'click .geo-link': 'showGeoInfo', + 'click .track-control': 'showTrack' + }, + + /** + * Invite user to chat + */ + inviteUser: function() { + if (! this.model.get('invitationInfo')) { + // Create some shortcuts + var visitorId = this.model.id; + var page = Mibew.Objects.Models.page; + + // Open invite window + Mibew.Popup.open( + page.get('inviteLink') + + '?visitor=' + + visitorId, + 'ImCenter' + visitorId, + page.get('inviteWindowParams') + ); + } + }, + + /** + * Open tracked window + */ + showTrack: function() { + // Create some shortcuts + var visitorId = this.model.id; + var page = Mibew.Objects.Models.page; + + // Open tracked window + Mibew.Popup.open( + page.get('trackedLink') + + '?visitor=' + + visitorId, + 'ImTracked' + visitorId, + page.get('trackedVisitorWindowParams') + ); + }, + + /** + * Open window with geo information + */ + showGeoInfo: function() { + var ip = this.model.get('userIp'); + if (ip) { + var page = Mibew.Objects.Models.page; + var geoLink = page.get('geoLink') + .replace("{ip}", ip); + Mibew.Popup.open( + geoLink, + 'ip' + ip, + page.get('geoWindowParams') + ); + } + } + } + ); + +})(Mibew, Handlebars); \ No newline at end of file diff --git a/src/messenger/webim/js/source/users/models/agent.js b/src/messenger/webim/js/source/users/models/agent.js new file mode 100644 index 00000000..daa0e697 --- /dev/null +++ b/src/messenger/webim/js/source/users/models/agent.js @@ -0,0 +1,95 @@ +/** + * @preserve This file is part of Mibew Messenger project. + * http://mibew.org + * + * Copyright (c) 2005-2011 Mibew Messenger Community + * License: http://mibew.org/license.php + */ + +(function(Mibew, _){ + + /** + * @class Represents an agent + */ + Mibew.Models.Agent = Mibew.Models.User.extend( + /** @lends Mibew.Models.Agent.prototype */ + { + /** + * A list of default model values. + * Inherits values from Mibew.Models.User + * @type Object + */ + defaults: _.extend( + {}, + Mibew.Models.User.prototype.defaults, + { + /** + * Agent id on the server + * @type Number + */ + id: null, + + /** + * Indicates that user is agent. + * Left only for compatibility with Mibew.Models.User + * @type Boolean + */ + isAgent: true, + + /** + * Indicates if agent away or available at the moment + * @type Boolean + */ + away: false + } + ), + + /** + * Set user status to 'away' + * This is a shortcut for setAvailability method + */ + away: function() { + this.setAvailability(false); + }, + + /** + * Set user status to 'available' + * This is a shortcut for setAvailability method + */ + available: function() { + this.setAvailability(true); + }, + + /** + * Set agent status: 'away' or 'available' + * @param {Boolean} available true set agent's status to 'available' + * and false set agent's status to 'away' + */ + setAvailability: function(available) { + var funcName = available?'available':'away'; + var self = this; + Mibew.Objects.server.callFunctions( + [ + { + 'function': funcName, + 'arguments': { + 'agentId': this.id, + 'references': {}, + 'return': {} + } + } + ], + function(args){ + if (args.errorCode == 0) { + self.set({'away': !available}); + } + }, + true + ); + } + } + ); + +})(Mibew, _); + + diff --git a/src/messenger/webim/js/source/users/models/queued_thread.js b/src/messenger/webim/js/source/users/models/queued_thread.js new file mode 100644 index 00000000..15ce2323 --- /dev/null +++ b/src/messenger/webim/js/source/users/models/queued_thread.js @@ -0,0 +1,154 @@ +/** + * @preserve This file is part of Mibew Messenger project. + * http://mibew.org + * + * Copyright (c) 2005-2011 Mibew Messenger Community + * License: http://mibew.org/license.php + */ + +(function(Mibew, _){ + + /** + * Holds thread controls constructors + * @type Array + */ + var controlsConstructors = []; + + /** + * Prepresent thread in users queue + * @class + */ + var QueuedThread = Mibew.Models.QueuedThread = Mibew.Models.Thread.extend( + /** @lends Mibew.Models.QueuedThread.prototype */ + { + /** + * A list of default model values. + * Inherits values from Mibew.Models.Thread + * @type Object + */ + defaults: _.extend( + {}, + Mibew.Models.Thread.prototype.defaults, + { + /** + * Collection of thread controls + * @type Mibew.Collections.Controls + */ + controls: null, + + /** + * Name of the user + * @type String + */ + userName: '', + + /** + * Ip address of the user + * @type String + */ + userIp: '', + + /** + * Full remote address returned by web server. Generally + * equals to userIp. + * @type String + */ + remote: '', + + /** + * User agent + * @type String + */ + userAgent: '', + + /** + * Agent name + * @type String + */ + agentName: '', + + /** + * Indicates if agent can open thread + * @type Boolean + */ + canOpen: false, + + /** + * Indicates if agent can view thread + * @type Boolean + */ + canView: false, + + /** + * Indicates if agent can ban the user + * @type Boolean + */ + canBan: false, + + /** + * Contains ban info if user already blocked or boolean + * false otherwise. + * @type Boolean|Object + */ + ban: false, + + /** + * Unix timestamp when thread was started + * @type Number + */ + totalTime: 0, + + /** + * Unix timestamp when user begin wait for agent + * @type Number + */ + waitingTime: 0, + + /** + * First message from user to operator + * @type String + */ + firstMessage: null + } + ), + + /** + * Model initializer. + * Create controls collection and store it in the model field. + */ + initialize: function() { + var self = this; + var controls = []; + var constructors = QueuedThread.getControls(); + for (var i = 0, l = constructors.length; i < l; i++) { + controls.push(new constructors[i]({thread: self})); + } + this.set({ + controls: new Mibew.Collections.Controls(controls) + }); + } + }, + + + /** @lends Mibew.Models.QueuedThread */ + { + /** + * Add thread control constructor + * @static + * @param {Function} Mibew.Models.Control or inherited constructor + */ + addControl: function(control) { + controlsConstructors.push(control) + }, + + /** + * Returns list of thread controls constructors + * @static + * @returns {Array} List of controls constructors + */ + getControls: function() { + return controlsConstructors; + } + } + ); +})(Mibew, _); \ No newline at end of file diff --git a/src/messenger/webim/js/source/users/models/status_panel.js b/src/messenger/webim/js/source/users/models/status_panel.js new file mode 100644 index 00000000..ec9ba91e --- /dev/null +++ b/src/messenger/webim/js/source/users/models/status_panel.js @@ -0,0 +1,53 @@ +/** + * @preserve This file is part of Mibew Messenger project. + * http://mibew.org + * + * Copyright (c) 2005-2011 Mibew Messenger Community + * License: http://mibew.org/license.php + */ + +(function(Mibew){ + + /** + * @class Represents a status panel + */ + Mibew.Models.StatusPanel = Mibew.Models.Base.extend( + /** @lends Mibew.Models.StatusPanel.prototype */ + { + /** + * A list of default model values. + * @type Object + */ + defaults: { + /** + * Status message + * @type String + */ + message: '' + }, + + /** + * Set status message + * @param {String} message New status message + */ + setStatus: function(message) { + this.set({'message': message}); + }, + + /** + * Changes agent status + */ + changeAgentStatus: function() { + var agent = Mibew.Objects.Models.agent; + if (agent.get('away')) { + agent.available(); + } else { + agent.away(); + } + } + } + ); + +})(Mibew); + + diff --git a/src/messenger/webim/js/source/users/models/visitor.js b/src/messenger/webim/js/source/users/models/visitor.js new file mode 100644 index 00000000..9f226772 --- /dev/null +++ b/src/messenger/webim/js/source/users/models/visitor.js @@ -0,0 +1,141 @@ +/** + * @preserve This file is part of Mibew Messenger project. + * http://mibew.org + * + * Copyright (c) 2005-2011 Mibew Messenger Community + * License: http://mibew.org/license.php + */ + +(function(Mibew, _){ + + /** + * Holds visitor controls constructors + * @type Array + */ + var controlsConstructors = []; + + /** + * @class Represents a visitor. + */ + var Visitor = Mibew.Models.Visitor = Mibew.Models.User.extend( + /** @lends Mibew.Models.Visitor.prototype */ + { + /** + * A list of default model values. + * Inherits values from Mibew.Models.User + * @type Object + */ + defaults: _.extend( + {}, + Mibew.Models.User.prototype.defaults, + { + /** + * Collection of visitor controls + * @type Mibew.Collections.Controls + */ + controls: null, + + /** + * Name of the user + * @type String + */ + userName: '', + + /** + * Ip address of the user + * @type String + */ + userIp: '', + + /** + * Full remote address returned by web server. Generally + * equals to userIp. + * @type String + */ + remote: '', + + /** + * User agent + * @type String + */ + userAgent: '', + + /** + * Unix timestamp when visitor was first time observed + * on site + * @type Number + */ + firstTime: 0, + + /** + * Unix timestamp when visitor was first time observed + * on site + * @type Number + */ + lastTime: 0, + + /** + * Total invitations count + * @type Number + */ + invitations: 0, + + /** + * Total chats count with visitor + * @type Number + */ + chats: 0, + + /** + * Information about invitation or booean false if there is + * no invitation yet. + * + * Information object contains following keys: + * - 'agentName': name of the agent who invited the visitor + * - 'time': invitation time + * @type Object|Boolean + */ + invitationInfo: false + } + ), + + /** + * Model initializer. + * Create controls collection and store it in the model field. + */ + initialize: function() { + var self = this; + var controls = []; + var constructors = Visitor.getControls(); + for (var i = 0, l = constructors.length; i < l; i++) { + controls.push(new constructors[i]({visitor: self})); + } + this.set({ + controls: new Mibew.Collections.Controls(controls) + }); + } + }, + + /** @lends Mibew.Models.Visitor */ + { + /** + * Add visitor control constructor + * @static + * @param {Function} Mibew.Models.Control or inherited constructor + */ + addControl: function(control) { + controlsConstructors.push(control) + }, + + /** + * Returns list of visitor controls constructors + * @static + * @returns {Array} List of controls constructors + */ + getControls: function() { + return controlsConstructors; + } + } + ); + +})(Mibew, _); diff --git a/src/messenger/webim/js/templates/compiled/users_app.tpl.js b/src/messenger/webim/js/templates/compiled/users_app.tpl.js new file mode 100644 index 00000000..50b5e699 --- /dev/null +++ b/src/messenger/webim/js/templates/compiled/users_app.tpl.js @@ -0,0 +1 @@ +(function(){var a=Handlebars.template,b=Handlebars.templates=Handlebars.templates||{};b.agent=a(function(a,b,c,d,e){function m(a,b){return"away"}function n(a,b){return"online"}function o(a,b){var d,e;return e=c.L10n,d=e?e.call(a,"pending.status.away",{hash:{}}):i.call(a,"L10n","pending.status.away",{hash:{}}),j(d)}function p(a,b){var d,e;return e=c.L10n,d=e?e.call(a,"pending.status.online",{hash:{}}):i.call(a,"L10n","pending.status.online",{hash:{}}),j(d)}function q(a,b){return","}c=c||a.helpers;var f="",g,h,i=c.helperMissing,j=this.escapeExpression,k=this,l="function";f+='',h=c.name,h?g=h.call(b,{hash:{}}):(g=b.name,g=typeof g===l?g():g),f+=j(g),g=b.isLast,g=c.unless.call(b,g,{hash:{},inverse:k.noop,fn:k.program(9,q,e)});if(g||g===0)f+=g;return f}),b.threads_collection=a(function(a,b,c,d,e){c=c||a.helpers;var f="",g,h,i=c.helperMissing,j=this.escapeExpression;return f+='\n\n\n \n \n \n \n \n \n \n \n\n\n\n\n\n
',h=c.L10n,g=h?h.call(b,"pending.table.head.name",{hash:{}}):i.call(b,"L10n","pending.table.head.name",{hash:{}}),f+=j(g)+"",h=c.L10n,g=h?h.call(b,"pending.table.head.actions",{hash:{}}):i.call(b,"L10n","pending.table.head.actions",{hash:{}}),f+=j(g)+"",h=c.L10n,g=h?h.call(b,"pending.table.head.contactid",{hash:{}}):i.call(b,"L10n","pending.table.head.contactid",{hash:{}}),f+=j(g)+"",h=c.L10n,g=h?h.call(b,"pending.table.head.state",{hash:{}}):i.call(b,"L10n","pending.table.head.state",{hash:{}}),f+=j(g)+"",h=c.L10n,g=h?h.call(b,"pending.table.head.operator",{hash:{}}):i.call(b,"L10n","pending.table.head.operator",{hash:{}}),f+=j(g)+"",h=c.L10n,g=h?h.call(b,"pending.table.head.total",{hash:{}}):i.call(b,"L10n","pending.table.head.total",{hash:{}}),f+=j(g)+"",h=c.L10n,g=h?h.call(b,"pending.table.head.waittime",{hash:{}}):i.call(b,"L10n","pending.table.head.waittime",{hash:{}}),f+=j(g)+"",h=c.L10n,g=h?h.call(b,"pending.table.head.etc",{hash:{}}):i.call(b,"L10n","pending.table.head.etc",{hash:{}}),f+=j(g)+'
',f}),b.no_visitors=a(function(a,b,c,d,e){c=c||a.helpers;var f="",g,h,i=c.helperMissing,j=this.escapeExpression;return f+='',h=c.L10n,g=h?h.call(b,"visitors.no_visitors",{hash:{}}):i.call(b,"L10n","visitors.no_visitors",{hash:{}}),f+=j(g)+"",f}),b.visitor=a(function(a,b,c,d,e){function m(a,b){var d="",e,f;return d+='',f=c.userName,f?e=f.call(a,{hash:{}}):(e=a.userName,e=typeof e===k?e():e),d+=j(e)+"",d}function n(a,b){var d,e;return e=c.userName,e?d=e.call(a,{hash:{}}):(d=a.userName,d=typeof d===k?d():d),j(d)}function o(a,b){var d="",e,f;return d+='',f=c.remote,f?e=f.call(a,{hash:{}}):(e=a.remote,e=typeof e===k?e():e),d+=j(e)+"",d}function p(a,b){var d,e;return e=c.remote,e?d=e.call(a,{hash:{}}):(d=a.remote,d=typeof d===k?d():d),j(d)}function q(a,b){var c;return c=a.invitationInfo,c=c==null||c===!1?c:c.agentName,c=typeof c===k?c():c,j(c)}function r(a,b){return"-"}function s(a,b){var d,e;return d=a.invitationInfo,d=d==null||d===!1?d:d.time,e=c.formatTimeSince,d=e?e.call(a,d,{hash:{}}):i.call(a,"formatTimeSince",d,{hash:{}}),j(d)}function t(a,b){return"-"}c=c||a.helpers;var f="",g,h,i=c.helperMissing,j=this.escapeExpression,k="function",l=this;f+='\n ',g=b.invitationInfo,g=c.unless.call(b,g,{hash:{},inverse:l.program(3,n,e),fn:l.program(1,m,e)});if(g||g===0)f+=g;f+='\n\n\n
\n
\n
\n
\n\n',g=b.userIp,g=c["if"].call(b,g,{hash:{},inverse:l.program(7,p,e),fn:l.program(5,o,e)});if(g||g===0)f+=g;f+='\n',g=b.firstTime,h=c.formatTimeSince,g=h?h.call(b,g,{hash:{}}):i.call(b,"formatTimeSince",g,{hash:{}}),f+=j(g)+'\n',g=b.lastTime,h=c.formatTimeSince,g=h?h.call(b,g,{hash:{}}):i.call(b,"formatTimeSince",g,{hash:{}}),f+=j(g)+'\n',g=b.invitationInfo,g=c["if"].call(b,g,{hash:{},inverse:l.program(11,r,e),fn:l.program(9,q,e)});if(g||g===0)f+=g;f+='\n',g=b.invitationInfo,g=c["if"].call(b,g,{hash:{},inverse:l.program(15,t,e),fn:l.program(13,s,e)});if(g||g===0)f+=g;return f+='\n',h=c.invitations,h?g=h.call(b,{hash:{}}):(g=b.invitations,g=typeof g===k?g():g),f+=j(g)+" / ",h=c.chats,h?g=h.call(b,{hash:{}}):(g=b.chats,g=typeof g===k?g():g),f+=j(g)+'\n',h=c.userAgent,h?g=h.call(b,{hash:{}}):(g=b.userAgent,g=typeof g===k?g():g),f+=j(g)+"",f}),b.status_panel=a(function(a,b,c,d,e){function m(a,b){var d,e;return e=c.L10n,d=e?e.call(a,"pending.status.away",{hash:{}}):i.call(a,"L10n","pending.status.away",{hash:{}}),j(d)}function n(a,b){var d,e;return e=c.L10n,d=e?e.call(a,"pending.status.online",{hash:{}}):i.call(a,"L10n","pending.status.online",{hash:{}}),j(d)}function o(a,b){var d,e;return e=c.L10n,d=e?e.call(a,"pending.status.setonline",{hash:{}}):i.call(a,"L10n","pending.status.setonline",{hash:{}}),j(d)}function p(a,b){var d,e;return e=c.L10n,d=e?e.call(a,"pending.status.setaway",{hash:{}}):i.call(a,"L10n","pending.status.setaway",{hash:{}}),j(d)}c=c||a.helpers;var f="",g,h,i=c.helperMissing,j=this.escapeExpression,k="function",l=this;f+='
',h=c.message,h?g=h.call(b,{hash:{}}):(g=b.message,g=typeof g===k?g():g),f+=j(g),g=b.agent,g=g==null||g===!1?g:g.away,g=c["if"].call(b,g,{hash:{},inverse:l.program(3,n,e),fn:l.program(1,m,e)});if(g||g===0)f+=g;f+='
",f}),b.queued_thread=a(function(a,b,c,d,e){function m(a,b){var d,e;return e=c.L10n,d=e?e.call(a,"pending.table.speak",{hash:{}}):i.call(a,"L10n","pending.table.speak",{hash:{}}),j(d)}function n(a,b){var d,e;return e=c.L10n,d=e?e.call(a,"pending.table.view",{hash:{}}):i.call(a,"L10n","pending.table.view",{hash:{}}),j(d)}function o(a,b){var d="",e,f;return f=c.L10n,e=f?f.call(a,"chat.client.spam.prefix",{hash:{}}):i.call(a,"L10n","chat.client.spam.prefix",{hash:{}}),d+=j(e)+" ",d}function p(a,b){var d="",e,f;return d+='",d}function q(a,b){var d="",e,f;return d+='\n
\n ',d}function r(a,b){var d="",e,f;return d+='\n
\n ',d}function s(a,b){var d="",e,f;return d+='\n
\n ',d}function t(a,b){var d="",e,f;return d+='\n
\n ',d}function u(a,b){var d="",e,f;return d+='',f=c.remote,f?e=f.call(a,{hash:{}}):(e=a.remote,e=typeof e===k?e():e),d+=j(e)+"",d}function v(a,b){var d,e;return e=c.remote,e?d=e.call(a,{hash:{}}):(d=a.remote,d=typeof d===k?d():d),j(d)}function w(a,b){var d,e;return d=a.waitingTime,e=c.formatTimeSince,d=e?e.call(a,d,{hash:{}}):i.call(a,"formatTimeSince",d,{hash:{}}),j(d)}function x(a,b){return"-"}function y(a,b){var c;return c=a.ban,c=c==null||c===!1?c:c.reason,c=typeof c===k?c():c,j(c)}function z(a,b){var d,e;return e=c.userAgent,e?d=e.call(a,{hash:{}}):(d=a.userAgent,d=typeof d===k?d():d),j(d)}c=c||a.helpers;var f="",g,h,i=c.helperMissing,j=this.escapeExpression,k="function",l=this;f+='\n \n ",g=b.firstMessage,g=c["if"].call(b,g,{hash:{},inverse:l.noop,fn:l.program(7,p,e)});if(g||g===0)f+=g;f+='\n\n\n
\n ',g=b.canOpen,g=c["if"].call(b,g,{hash:{},inverse:l.noop,fn:l.program(9,q,e)});if(g||g===0)f+=g;f+="\n ",g=b.canView,g=c["if"].call(b,g,{hash:{},inverse:l.noop,fn:l.program(11,r,e)});if(g||g===0)f+=g;f+="\n ",g=b.tracked,g=c["if"].call(b,g,{hash:{},inverse:l.noop,fn:l.program(13,s,e)});if(g||g===0)f+=g;f+="\n ",g=b.canBan,g=c["if"].call(b,g,{hash:{},inverse:l.noop,fn:l.program(15,t,e)});if(g||g===0)f+=g;f+='\n
\n
\n\n',g=b.userIp,g=c["if"].call(b,g,{hash:{},inverse:l.program(19,v,e),fn:l.program(17,u,e)});if(g||g===0)f+=g;f+='\n',h=c.stateDesc,h?g=h.call(b,{hash:{}}):(g=b.stateDesc,g=typeof g===k?g():g),f+=j(g)+'\n',h=c.agentName,h?g=h.call(b,{hash:{}}):(g=b.agentName,g=typeof g===k?g():g),f+=j(g)+'\n',g=b.totalTime,h=c.formatTimeSince,g=h?h.call(b,g,{hash:{}}):i.call(b,"formatTimeSince",g,{hash:{}}),f+=j(g)+'\n',g=b.chatting,g=c.unless.call(b,g,{hash:{},inverse:l.program(23,x,e),fn:l.program(21,w,e)});if(g||g===0)f+=g;f+='\n',g=b.ban,g=c["if"].call(b,g,{hash:{},inverse:l.program(27,z,e),fn:l.program(25,y,e)});if(g||g===0)f+=g;return f+="",f}),b.no_threads=a(function(a,b,c,d,e){c=c||a.helpers;var f="",g,h,i=c.helperMissing,j=this.escapeExpression;return f+='',h=c.L10n,g=h?h.call(b,"clients.no_clients",{hash:{}}):i.call(b,"L10n","clients.no_clients",{hash:{}}),f+=j(g)+"",f}),b.visitors_collection=a(function(a,b,c,d,e){c=c||a.helpers;var f="",g,h,i=c.helperMissing,j=this.escapeExpression;return f+='\n\n\n \n \n \n \n \n \n \n \n \n\n\n\n\n
',h=c.L10n,g=h?h.call(b,"visitors.table.head.name",{hash:{}}):i.call(b,"L10n","visitors.table.head.name",{hash:{}}),f+=j(g)+"",h=c.L10n,g=h?h.call(b,"visitors.table.head.actions",{hash:{}}):i.call(b,"L10n","visitors.table.head.actions",{hash:{}}),f+=j(g)+"",h=c.L10n,g=h?h.call(b,"visitors.table.head.contactid",{hash:{}}):i.call(b,"L10n","visitors.table.head.contactid",{hash:{}}),f+=j(g)+"",h=c.L10n,g=h?h.call(b,"visitors.table.head.firsttimeonsite",{hash:{}}):i.call(b,"L10n","visitors.table.head.firsttimeonsite",{hash:{}}),f+=j(g)+"",h=c.L10n,g=h?h.call(b,"visitors.table.head.lasttimeonsite",{hash:{}}):i.call(b,"L10n","visitors.table.head.lasttimeonsite",{hash:{}}),f+=j(g)+"",h=c.L10n,g=h?h.call(b,"visitors.table.head.invited.by",{hash:{}}):i.call(b,"L10n","visitors.table.head.invited.by",{hash:{}}),f+=j(g)+"",h=c.L10n,g=h?h.call(b,"visitors.table.head.invitationtime",{hash:{}}):i.call(b,"L10n","visitors.table.head.invitationtime",{hash:{}}),f+=j(g)+"",h=c.L10n,g=h?h.call(b,"visitors.table.head.invitations",{hash:{}}):i.call(b,"L10n","visitors.table.head.invitations",{hash:{}}),f+=j(g)+"",h=c.L10n,g=h?h.call(b,"visitors.table.head.etc",{hash:{}}):i.call(b,"L10n","visitors.table.head.etc",{hash:{}}),f+=j(g)+'
',f})})() \ No newline at end of file diff --git a/src/messenger/webim/js/templates/source/users/agent.handlebars b/src/messenger/webim/js/templates/source/users/agent.handlebars new file mode 100644 index 00000000..a482a764 --- /dev/null +++ b/src/messenger/webim/js/templates/source/users/agent.handlebars @@ -0,0 +1 @@ +{{name}}{{#unless isLast}},{{/unless}} \ No newline at end of file diff --git a/src/messenger/webim/js/templates/source/users/no_threads.handlebars b/src/messenger/webim/js/templates/source/users/no_threads.handlebars new file mode 100644 index 00000000..7a629f6e --- /dev/null +++ b/src/messenger/webim/js/templates/source/users/no_threads.handlebars @@ -0,0 +1 @@ +{{L10n "clients.no_clients"}} \ No newline at end of file diff --git a/src/messenger/webim/js/templates/source/users/no_visitors.handlebars b/src/messenger/webim/js/templates/source/users/no_visitors.handlebars new file mode 100644 index 00000000..17e3f3da --- /dev/null +++ b/src/messenger/webim/js/templates/source/users/no_visitors.handlebars @@ -0,0 +1 @@ +{{L10n "visitors.no_visitors"}} \ No newline at end of file diff --git a/src/messenger/webim/js/templates/source/users/queued_thread.handlebars b/src/messenger/webim/js/templates/source/users/queued_thread.handlebars new file mode 100644 index 00000000..d3e82c1b --- /dev/null +++ b/src/messenger/webim/js/templates/source/users/queued_thread.handlebars @@ -0,0 +1,27 @@ + + + {{#if firstMessage}}{{/if}} + + +
+ {{#if canOpen}} +
+ {{/if}} + {{#if canView}} +
+ {{/if}} + {{#if tracked}} +
+ {{/if}} + {{#if canBan}} +
+ {{/if}} +
+
+ +{{#if userIp}}{{remote}}{{else}}{{remote}}{{/if}} +{{stateDesc}} +{{agentName}} +{{formatTimeSince totalTime}} +{{#unless chatting}}{{formatTimeSince waitingTime}}{{else}}-{{/unless}} +{{#if ban}}{{ban.reason}}{{else}}{{userAgent}}{{/if}} \ No newline at end of file diff --git a/src/messenger/webim/js/templates/source/users/status_panel.handlebars b/src/messenger/webim/js/templates/source/users/status_panel.handlebars new file mode 100644 index 00000000..4f8ec934 --- /dev/null +++ b/src/messenger/webim/js/templates/source/users/status_panel.handlebars @@ -0,0 +1 @@ +
{{message}}{{#if agent.away}}{{L10n "pending.status.away"}}{{else}}{{L10n "pending.status.online"}}{{/if}}
\ No newline at end of file diff --git a/src/messenger/webim/js/templates/source/users/threads_collection.handlebars b/src/messenger/webim/js/templates/source/users/threads_collection.handlebars new file mode 100644 index 00000000..ede4845f --- /dev/null +++ b/src/messenger/webim/js/templates/source/users/threads_collection.handlebars @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + +
{{L10n "pending.table.head.name"}}{{L10n "pending.table.head.actions"}}{{L10n "pending.table.head.contactid"}}{{L10n "pending.table.head.state"}}{{L10n "pending.table.head.operator"}}{{L10n "pending.table.head.total"}}{{L10n "pending.table.head.waittime"}}{{L10n "pending.table.head.etc"}}
\ No newline at end of file diff --git a/src/messenger/webim/js/templates/source/users/visitor.handlebars b/src/messenger/webim/js/templates/source/users/visitor.handlebars new file mode 100644 index 00000000..0f831313 --- /dev/null +++ b/src/messenger/webim/js/templates/source/users/visitor.handlebars @@ -0,0 +1,16 @@ + + {{#unless invitationInfo}}{{userName}}{{else}}{{userName}}{{/unless}} + + +
+
+
+
+ +{{#if userIp}}{{remote}}{{else}}{{remote}}{{/if}} +{{formatTimeSince firstTime}} +{{formatTimeSince lastTime}} +{{#if invitationInfo}}{{invitationInfo.agentName}}{{else}}-{{/if}} +{{#if invitationInfo}}{{formatTimeSince invitationInfo.time}}{{else}}-{{/if}} +{{invitations}} / {{chats}} +{{userAgent}} \ No newline at end of file diff --git a/src/messenger/webim/js/templates/source/users/visitors_collection.handlebars b/src/messenger/webim/js/templates/source/users/visitors_collection.handlebars new file mode 100644 index 00000000..cf263b83 --- /dev/null +++ b/src/messenger/webim/js/templates/source/users/visitors_collection.handlebars @@ -0,0 +1,17 @@ + + + + + + + + + + + + + + + + +
{{L10n "visitors.table.head.name"}}{{L10n "visitors.table.head.actions"}}{{L10n "visitors.table.head.contactid"}}{{L10n "visitors.table.head.firsttimeonsite"}}{{L10n "visitors.table.head.lasttimeonsite"}}{{L10n "visitors.table.head.invited.by"}}{{L10n "visitors.table.head.invitationtime"}}{{L10n "visitors.table.head.invitations"}}{{L10n "visitors.table.head.etc"}}
\ No newline at end of file diff --git a/src/messenger/webim/libs/classes/mibew_api_users_interaction.php b/src/messenger/webim/libs/classes/mibew_api_users_interaction.php new file mode 100644 index 00000000..663c3599 --- /dev/null +++ b/src/messenger/webim/libs/classes/mibew_api_users_interaction.php @@ -0,0 +1,51 @@ + array( + 'agentId' => null, + 'references' => array(), + 'return' => array() + ), + 'updateThreads' => array( + 'revision' => 0 + ), + 'result' => array( + 'errorCode' => 0 + ) + ); + + /** + * Reserved function's names + * @var array + * @see MibewAPIInteraction::$reservedFunctionNames + */ + public $reservedFunctionNames = array( + 'result' + ); +} + +?> \ No newline at end of file diff --git a/src/messenger/webim/libs/classes/users_processor.php b/src/messenger/webim/libs/classes/users_processor.php new file mode 100644 index 00000000..ffd80b47 --- /dev/null +++ b/src/messenger/webim/libs/classes/users_processor.php @@ -0,0 +1,578 @@ + '', + 'trusted_signatures' => array(''), + 'event_prefix' => 'users' + )); + } + + /** + * Creates and returns an instance of the MibewAPI class. + * + * @return MibewAPI + */ + protected function getMibewAPIInstance() { + return MibewAPI::getAPI('MibewAPIUsersInteraction'); + } + + /** + * Sends asynchronous request + * + * @param array $request The 'request' array. See Mibew API for details + * @return boolean true on success or false on failure + */ + protected function sendAsyncRequest($request) { + // Define empty agent id + $agent_id = null; + foreach ($request['functions'] as $function) { + // Save agent id from first function in package + if (is_null($agent_id)) { + $agent_id = $function['arguments']['agentId']; + continue; + } + // Check agent id for the remaining functions + if ($agent_id != $function['arguments']['agentId']) { + throw new UsersProcessorException( + 'Various agent ids in different functions in one package!', + UsersProcessorException::VARIOUS_AGENT_ID + ); + } + } + // Store request in buffer + $this->addRequestToBuffer('users_'.$agent_id, $request); + return true; + } + + /** + * Check operator id equals to $operatorId is current logged in operator + * + * @param int $operatorId Operator id to check + * @return array Operators info array + * + * @throws UsersProcessorException If operators not logged in or if + * $operatorId varies from current logged in operator. + */ + protected static function checkOperator($operatorId) { + $operator = get_logged_in(); + if (!$operator) { + throw new UsersProcessorException( + getstring("agent.not_logged_in"), + UsersProcessorException::ERROR_AGENT_NOT_LOGGED_IN + ); + } + if ($operatorId != $operator['operatorid']) { + throw new UsersProcessorException( + "Wrong agent id: '{$operatorId}' instead of {$operator['operatorid']}", + UsersProcessorException::ERROR_WRONG_AGENT_ID + ); + } + return $operator; + } + + /** + * Mark operator as away. API function + * + * @param array $args Associative array of arguments. It must contains + * following keys: + * - 'agentId': Id of the agent related to users window + */ + protected function apiAway($args) { + $operator = self::checkOperator($args['agentId']); + notify_operator_alive($operator['operatorid'], 1); + } + + /** + * Mark operator as available. API function + * + * @param array $args Associative array of arguments. It must contains + * following keys: + * - 'agentId': Id of the agent related to users window + */ + protected function apiAvailable($args) { + $operator = self::checkOperator($args['agentId']); + notify_operator_alive($operator['operatorid'], 0); + } + + /** + * Return updated threads list. API function + * + * @global string $mysqlprefix Database tables prefix + * @global int $can_viewthreads View threads permission code + * @global int $can_takeover Take threads over permission code + * @param array $args Associative array of arguments. It must contains + * following keys: + * - 'agentId': Id of the agent related to users window + * - 'revision': last revision number at client side + * @return array Array of results. It contains following keys: + * - 'threads': array of threads changes + */ + protected function apiUpdateThreads($args) { + global $mysqlprefix, $can_viewthreads, $can_takeover; + + $operator = self::checkOperator($args['agentId']); + + $since = $args['revision']; + // Get operator groups + if (!isset($_SESSION["${mysqlprefix}operatorgroups"])) { + $_SESSION["${mysqlprefix}operatorgroups"] + = get_operator_groupslist($operator['operatorid']); + } + $groupids = $_SESSION["${mysqlprefix}operatorgroups"]; + + $db = Database::getInstance(); + $query = "select t.*, " . + " g.vclocalname as group_localname, " . + " g.vccommonname as group_commonname " . + " from {chatthread} t left outer join {chatgroup} g on " . + " t.groupid = g.groupid " . + " where t.lrevision > :since " . + ($since == 0 + // Select only active threads at first time when lrevision = 0 + ? " AND t.istate <> " . Thread::STATE_CLOSED . + " AND t.istate <> " . Thread::STATE_LEFT + // Select all threads at when lrevision > 0. It provides the + // ability to update(and probably hide) closed threads at the + // clien side. + : "" + ) . + (Settings::get('enablegroups') == '1' + // If groups are enabled select only threads with empty groupid + // or groups related to current operator + ? " AND (g.groupid is NULL" . ($groupids + ? " OR g.groupid IN ($groupids) OR g.groupid IN " . + "(SELECT parent FROM {chatgroup} " . + "WHERE groupid IN ($groupids)) " + : "") . + ") " + : "" + ) . + " ORDER BY t.threadid"; + $rows = $db->query( + $query, + array(':since' => $since), + array('return_rows' => Database::RETURN_ALL_ROWS) + ); + + $revision = $since; + $threads = array(); + foreach($rows as $row) { + // Create thread instance + $thread = Thread::createFromDbInfo($row); + + // Calculate agent permissions + $can_open = !($thread->state == Thread::STATE_CHATTING + && $thread->agentId != $operator['operatorid'] + && !is_capable($can_takeover, $operator)); + + $can_view = ($thread->agentId != $operator['operatorid'] + && $thread->nextAgent != $operator['operatorid'] + && is_capable($can_viewthreads, $operator)); + + $can_ban = (Settings::get('enableban') == "1"); + + + // Get ban info + $ban_info = (Settings::get('enableban') == "1") + ? ban_for_addr($thread->remote) + : false; + if ($ban_info !== false) { + $ban = array( + 'id' => $ban_info['banid'], + 'reason' => $ban_info['comment'] + ); + } else { + $ban = false; + } + + // Get user name + $user_name = get_user_name( + $thread->userName, + $thread->remote, + $thread->userId + ); + + // Get user ip + if (preg_match("/(\\d+\\.\\d+\\.\\d+\\.\\d+)/", $thread->remote, $matches) != 0) { + $user_ip = $matches[1]; + } else { + $user_ip = false; + } + + // Get thread operartor name + $nextagent = $thread->nextAgent != 0 + ? operator_by_id($thread->nextAgent) + : false; + if ($nextagent) { + $agent_name = get_operator_name($nextagent); + } else { + if ($thread->agentName) { + $agent_name = $thread->agentName; + } else { + $group_name = get_group_name(array( + 'vccommonname' => $row['group_commonname'], + 'vclocalname' => $row['group_localname'] + )); + if($group_name) { + $agent_name = '-' . $group_name . '-'; + } else { + $agent_name = '-'; + } + } + } + + // Get first message + $first_message = null; + if ($thread->shownMessageId != 0) { + $line = $db->query( + "select tmessage from {chatmessage} " . + " where messageid = ? limit 1", + array($thread->shownMessageId), + array('return_rows' => Database::RETURN_ONE_ROW) + ); + if ($line) { + $first_message = preg_replace( + "/[\r\n\t]+/", + " ", + $line["tmessage"] + ); + } + } + + $threads[] = array( + 'id' => $thread->id, + 'token' => $thread->lastToken, + 'userName' => $user_name, + 'userIp' => $user_ip, + 'remote' => $thread->remote, + 'userAgent' => get_useragent_version($thread->userAgent), + 'agentName' => $agent_name, + 'canOpen' => $can_open, + 'canView' => $can_view, + 'canBan' => $can_ban, + 'ban' => $ban, + 'state' => $thread->state, + 'totalTime' => $thread->created, + 'waitingTime' => $thread->modified, + 'firstMessage' => $first_message + ); + + // Get max revision + if ($thread->lastRevision > $revision) { + $revision = $thread->lastRevision; + } + + // Clean up + unset($thread); + } + + // Send results back to the client + return array( + 'threads' => $threads, + 'lastRevision' => $revision + ); + } + + /** + * Return updated visitors list. API function + * + * @param array $args Associative array of arguments. It must contains + * following keys: + * - 'agentId': Id of the agent related to users window + * @return array Array of results. It contains following keys: + * - 'visitors': array of visitors on the site + */ + protected function apiUpdateVisitors($args) { + // Check access + self::checkOperator($args['agentId']); + + $db = Database::getInstance(); + + // Remove old visitors + $db->query( + "DELETE FROM {chatsitevisitor} " . + "WHERE (:now - lasttime) > :lifetime ". + "AND (threadid IS NULL OR " . + "(SELECT count(*) FROM {chatthread} " . + "WHERE threadid = {chatsitevisitor}.threadid " . + "AND istate <> " . Thread::STATE_CLOSED . " " . + "AND istate <> " . Thread::STATE_LEFT . ") = 0)", + array( + ':lifetime' => Settings::get('tracking_lifetime'), + ':now' => time() + ) + ); + + // Remove old invitations + $db->query( + "UPDATE {chatsitevisitor} SET invited = 0, " . + "invitationtime = NULL, invitedby = NULL". + " WHERE threadid IS NULL AND (:now - invitationtime) > :lifetime", + array( + ':lifetime' => Settings::get('invitation_lifetime'), + ':now' => time() + ) + ); + + // Remove associations of visitors with closed threads + $db->query( + "UPDATE {chatsitevisitor} SET threadid = NULL " . + "WHERE threadid IS NOT NULL AND " . + " (SELECT count(*) FROM {chatthread} " . + "WHERE threadid = {chatsitevisitor}.threadid" . + " AND istate <> " . Thread::STATE_CLOSED . " " . + " AND istate <> " . Thread::STATE_LEFT . ") = 0" + ); + + // Remove old visitors' tracks + $db->query( + "DELETE FROM {visitedpage} WHERE (:now - visittime) > :lifetime " . + " AND visitorid NOT IN (SELECT visitorid FROM {chatsitevisitor})", + array( + ':lifetime' => Settings::get('tracking_lifetime'), + ':now' => time() + ) + ); + + // Load visitors + $query = "SELECT visitorid, userid, username, firsttime, lasttime, " . + "entry, details, invited, invitationtime, invitedby, " . + "invitations, chats " . + "FROM {chatsitevisitor} " . + "WHERE threadid IS NULL " . + "ORDER BY invited, lasttime DESC, invitations"; + $query .= (Settings::get('visitors_limit') == '0') + ? "" + : " LIMIT " . Settings::get('visitors_limit'); + + $rows = $db->query( + $query, + NULL, + array('return_rows' => Database::RETURN_ALL_ROWS) + ); + + $visitors = array(); + foreach ($rows as $row) { + + // Get visitor details + $details = track_retrieve_details($row); + + // Get user agent + $user_agent = get_useragent_version($details['user_agent']); + + // Get user ip + if (preg_match("/(\\d+\\.\\d+\\.\\d+\\.\\d+)/", $details['remote_host'], $matches) != 0) { + $user_ip = $matches[1]; + } else { + $user_ip = false; + } + + // Get invitation info + if ($row['invited']) { + $agent_name = get_operator_name( + operator_by_id($row['invitedby']) + ); + $invitation_info = array( + 'time' => $row['invitationtime'], + 'agentName' => $agent_name + ); + } else { + $invitation_info = false; + } + + // Create resulting visitor structure + $visitors[] = array( + 'id' => (int)$row['visitorid'], + 'userName' => $row['username'], + 'userAgent' => $user_agent, + 'userIp' => $user_ip, + 'remote' => $details['remote_host'], + 'firstTime' => $row['firsttime'], + 'lastTime' => $row['lasttime'], + 'invitations' => (int)$row['invitations'], + 'chats' => (int)$row['chats'], + 'invitationInfo' => $invitation_info + ); + } + + return array( + 'visitors' => $visitors + ); + } + + /** + * Return updated operators list. API function + * + * @global string $webim_encoding Encoding for the current locale + * @param array $args Associative array of arguments. It must contains + * following keys: + * - 'agentId': Id of the agent related to users window + * @return array Array of results. It contains following keys: + * - 'operators': array of online operators + */ + protected function apiUpdateOperators($args) { + global $webim_encoding; + + // Check access and get operators info + $operator = self::checkOperator($args['agentId']); + + // Return empty array if show operators option disabled + if (Settings::get('showonlineoperators') != '1') { + return array( + 'operators' => array() + ); + } + + // Check if curent operator is in isolation + $list_options = in_isolation($operator) + ? array('isolated_operator_id' => $operator['operatorid']) + : array(); + + // Get operators list + $operators = get_operators_list($list_options); + + // Create resulting list of operators + $result_list = array(); + foreach ($operators as $item) { + if (!operator_is_online($item)) { + continue; + } + + $result_list[] = array( + 'id' => (int)$item['operatorid'], + // Convert name to UTF-8 + 'name' => myiconv( + $webim_encoding, + "utf-8", + htmlspecialchars($item['vclocalename']) + ), + 'away' => (bool)operator_is_away($item) + ); + } + + // Send operators list to the client side + return array( + 'operators' => $result_list + ); + } + + /** + * Update chat window state. API function + * + * Call periodically by chat window + * @param array $args Associative array of arguments. It must contains + * following keys: + * - 'agentId': Id of the agent related to users window + */ + protected function apiUpdate($args) { + // Check access and get operator array + $operator = self::checkOperator($args['agentId']); + + // Update operator status + notify_operator_alive($operator['operatorid'], $operator['istatus']); + + // Close old threads + Thread::closeOldThreads(); + + // Load stored requests + $stored_requests = $this->getRequestsFromBuffer('users_'.$args['agentId']); + if ($stored_requests !== false) { + $this->responses = array_merge($this->responses, $stored_requests); + } + } + + /** + * Returns current server time. API function + * + * @param array $args Associative array of arguments. It must contains + * following keys: + * - 'agentId': Id of the agent related to users window + * @return array Array of results. It contains following keys: + * - 'time': current server time + */ + protected function apiCurrentTime($args) { + // Check access + self::checkOperator($args['agentId']); + + // Return time + return array( + 'time' => time() + ); + } +} + +/** + * Class for users processor exceptions + */ +class UsersProcessorException extends RequestProcessorException { + /** + * Operator is not logged in + */ + const ERROR_AGENT_NOT_LOGGED_IN = 1; + /** + * Wrong agent id + */ + const ERROR_WRONG_AGENT_ID = 2; + /** + * Various agent ids in different functions in one package + */ + const VARIOUS_AGENT_ID = 3; +} + +?> \ No newline at end of file diff --git a/src/messenger/webim/libs/common/configurations.php b/src/messenger/webim/libs/common/configurations.php index c0d0a89e..edc5a60e 100644 --- a/src/messenger/webim/libs/common/configurations.php +++ b/src/messenger/webim/libs/common/configurations.php @@ -46,6 +46,20 @@ function get_core_style_config() { $config += array( 'history' => array( 'window_params' => '' + ), + 'users' => array( + 'thread_tag' => 'div', + 'visitor_tag' => 'div' + ), + 'tracked' => array( + 'user_window_params' => '', + 'visitor_window_params' => '' + ), + 'invitation' => array( + 'window_params' => '' + ), + 'ban' => array( + 'window_params' => '' ) ); diff --git a/src/messenger/webim/locales/en/properties b/src/messenger/webim/locales/en/properties index ba4aa9ff..771f905a 100644 --- a/src/messenger/webim/locales/en/properties +++ b/src/messenger/webim/locales/en/properties @@ -40,7 +40,7 @@ char.redirect.operator.online_suff=(online) chat.came.from=Vistor came from page {0} chat.client.changename=Change name chat.client.name=You are -chat.client.spam.prefix=[spam]  +chat.client.spam.prefix=[spam] chat.client.visited.page=Visitor navigated to {0} chat.close.confirmation=Are you sure want to leave chat? chat.default.username=Guest @@ -615,6 +615,7 @@ updates.title=Updates visitors.how_to=To invite the visitor to chat click on his/her name in the list. visitors.intro=The table below represents a list of visitors ready to chat on your site. visitors.no_visitors=There are no visitors ready to chat on your site at present time +visitors.table.head.actions=Actions visitors.table.head.contactid=Visitor's address visitors.table.head.etc=Misc visitors.table.head.firsttimeonsite=First seen diff --git a/src/messenger/webim/locales/ru/properties b/src/messenger/webim/locales/ru/properties index e0d884e5..10461abe 100644 --- a/src/messenger/webim/locales/ru/properties +++ b/src/messenger/webim/locales/ru/properties @@ -40,7 +40,7 @@ char.redirect.operator.online_suff=( chat.came.from=Посетитель пришел со страницы {0} chat.client.changename=Изменить имя chat.client.name=Вы -chat.client.spam.prefix=[спам]  +chat.client.spam.prefix=[спам] chat.client.visited.page=Посетитель перешел на {0} chat.close.confirmation=Вы действительно хотите покинуть диалог? chat.default.username=Посетитель @@ -617,6 +617,7 @@ updates.title= visitors.how_to=Для приглашения посетителя к диалогу кликните на его или её имя в списке. visitors.intro=В расположенной ниже таблице представлен список готовых к диалогу посетителей на Вашем сайте. visitors.no_visitors=В настоящее время на Вашем сайте нет готовых к диалогу посетителей +visitors.table.head.actions=Действия visitors.table.head.contactid=Адрес посетителя visitors.table.head.etc=Разное visitors.table.head.firsttimeonsite=Впервые замечен diff --git a/src/messenger/webim/operator/update.php b/src/messenger/webim/operator/update.php index 8ebbd3a9..b47a7a97 100644 --- a/src/messenger/webim/operator/update.php +++ b/src/messenger/webim/operator/update.php @@ -22,298 +22,14 @@ require_once('../libs/operator.php'); require_once('../libs/groups.php'); require_once('../libs/track.php'); require_once('../libs/classes/thread.php'); +require_once('../libs/classes/mibew_api.php'); +require_once('../libs/classes/mibew_api_interaction.php'); +require_once('../libs/classes/mibew_api_users_interaction.php'); +require_once('../libs/classes/mibew_api_execution_context.php'); +require_once('../libs/classes/client_side_processor.php'); +require_once('../libs/classes/users_processor.php'); -$operator = get_logged_in(); -if (!$operator) { - start_xml_output(); - echo "" . myiconv($webim_encoding, "utf-8", escape_with_cdata(getstring("agent.not_logged_in"))) . ""; - exit; -} - -$threadstate_to_string = array( - Thread::STATE_QUEUE => "wait", - Thread::STATE_WAITING => "prio", - Thread::STATE_CHATTING => "chat", - Thread::STATE_CLOSED => "closed", - Thread::STATE_LOADING => "wait", - Thread::STATE_LEFT => "closed" -); - -$threadstate_key = array( - Thread::STATE_QUEUE => "chat.thread.state_wait", - Thread::STATE_WAITING => "chat.thread.state_wait_for_another_agent", - Thread::STATE_CHATTING => "chat.thread.state_chatting_with_agent", - Thread::STATE_CLOSED => "chat.thread.state_closed", - Thread::STATE_LOADING => "chat.thread.state_loading" -); - -function thread_to_xml($thread_info) -{ - global $threadstate_to_string, $threadstate_key, - $webim_encoding, $operator, $can_viewthreads, $can_takeover; - - $thread = $thread_info['thread']; - - $state = $threadstate_to_string[$thread->state]; - $result = "id . "\" stateid=\"$state\""; - if ($state == "closed") - return $result . "/>"; - - $state = getstring($threadstate_key[$thread->state]); - $nextagent = $thread->nextAgent != 0 ? operator_by_id($thread->nextAgent) : null; - $threadoperator = $nextagent ? get_operator_name($nextagent) - : ($thread->agentName ? $thread->agentName : "-"); - - if ($threadoperator == "-" && ! empty($thread_info['groupname'])) { - $threadoperator = "- " . $thread_info['groupname'] . " -"; - } - - if (!($thread->state == Thread::STATE_CHATTING && $thread->agentId != $operator['operatorid'] && !is_capable($can_takeover, $operator))) { - $result .= " canopen=\"true\""; - } - if ($thread->agentId != $operator['operatorid'] && $thread->nextAgent != $operator['operatorid'] - && is_capable($can_viewthreads, $operator)) { - $result .= " canview=\"true\""; - } - if (Settings::get('enableban') == "1") { - $result .= " canban=\"true\""; - } - - $banForThread = Settings::get('enableban') == "1" ? ban_for_addr($thread->remote) : false; - if ($banForThread) { - $result .= " ban=\"blocked\" banid=\"" . $banForThread['banid'] . "\""; - } - - $result .= " state=\"$state\" typing=\"" . $thread->userTyping . "\">"; - $result .= ""; - if ($banForThread) { - $result .= htmlspecialchars(getstring('chat.client.spam.prefix')); - } - $result .= htmlspecialchars( - htmlspecialchars(get_user_name($thread->userName, $thread->remote, $thread->userId)) - ) . ""; - $result .= "" . htmlspecialchars(get_user_addr($thread->remote)) . ""; - $result .= "" . htmlspecialchars(htmlspecialchars($threadoperator)) . ""; - $result .= ""; - $result .= "" . $thread->modified . "000"; - - if ($banForThread) { - $result .= "" . $banForThread['comment'] . ""; - } - - $userAgent = get_useragent_version($thread->userAgent); - $result .= "" . $userAgent . ""; - if ($thread->shownMessageId != 0) { - $db = Database::getInstance(); - $line = $db->query( - "select tmessage from {chatmessage} where messageid = ?", - array($thread->shownMessageId), - array('return_rows' => Database::RETURN_ONE_ROW) - ); - if ($line) { - $message = preg_replace("/[\r\n\t]+/", " ", $line["tmessage"]); - $result .= "" . htmlspecialchars(htmlspecialchars($message)) . ""; - } - } - $result .= ""; - return $result; -} - -function print_pending_threads($groupids, $since) -{ - global $webim_encoding; - $db = Database::getInstance(); - - $revision = $since; - $query = "select {chatthread}.*, " . - "(select vclocalname from {chatgroup} where {chatgroup}.groupid = {chatthread}.groupid) as groupname " . - "from {chatthread} where lrevision > :since " . - ($since <= 0 - ? "AND istate <> " . Thread::STATE_CLOSED . " AND istate <> " . Thread::STATE_LEFT . " " - : "") . - (Settings::get('enablegroups') == '1' - ? "AND (groupid is NULL" . ($groupids - ? " OR groupid IN ($groupids) OR groupid IN (SELECT parent FROM {chatgroup} WHERE groupid IN ($groupids)) " - : "") . - ") " - : "") . - "ORDER BY threadid"; - $rows = $db->query( - $query, - array(':since' => $since), - array('return_rows' => Database::RETURN_ALL_ROWS) - ); - - $output = array(); - foreach ($rows as $row) { - $thread = Thread::createFromDbInfo($row); - $thread_info = array( - 'thread' => $thread, - 'groupname' => $row['groupname'] - ); - $thread_as_xml = thread_to_xml($thread_info); - $output[] = $thread_as_xml; - if ($thread->lastRevision > $revision) { - $revision = $thread->lastRevision; - } - } - - echo ""; - foreach ($output as $thr) { - print myiconv($webim_encoding, "utf-8", $thr); - } - echo ""; -} - -function print_operators($operator) -{ - global $webim_encoding; - echo ""; - - $list_options = in_isolation($operator)?array('isolated_operator_id' => $operator['operatorid']):array(); - $operators = get_operators_list($list_options); - - foreach ($operators as $operator) { - if (!operator_is_online($operator)) - continue; - - $name = myiconv($webim_encoding, "utf-8", htmlspecialchars(htmlspecialchars($operator['vclocalename']))); - $away = operator_is_away($operator) ? " away=\"1\"" : ""; - - echo ""; - } - echo ""; -} - -function visitor_to_xml($visitor) -{ - $result = ""; - -// $result .= "" . htmlspecialchars($visitor['userid']) . ""; - $result .= "" . htmlspecialchars($visitor['username']) . ""; - - $result .= ""; - $result .= "" . $visitor['lasttime'] . "000"; -// $result .= "" . htmlspecialchars($visitor['entry']) . ""; - -// $result .= ""; -// $path = track_retrieve_path($visitor); -// ksort($path); -// foreach ($path as $k => $v) { -// $result .= "" . htmlspecialchars($v) . ""; -// } -// $result .= ""; - - $details = track_retrieve_details($visitor); - $userAgent = get_useragent_version($details['user_agent']); - $result .= "" . $userAgent . ""; - $result .= "" . htmlspecialchars(get_user_addr($details['remote_host'])) . ""; - - $result .= "" . $visitor['invitations'] . ""; - $result .= "" . $visitor['chats'] . ""; - - $result .= ""; - if ($visitor['invited']) { - $result .= "" . $visitor['invitationtime'] . "000"; - $operator = get_operator_name(operator_by_id($visitor['invitedby'])); - $result .= "" . htmlspecialchars(htmlspecialchars($operator)) . ""; - } - $result .= ""; - - $result .= ""; - return $result; -} - -function print_visitors() -{ - global $webim_encoding; - - $db = Database::getInstance(); - -// Remove old visitors - $db->query( - "DELETE FROM {chatsitevisitor} " . - "WHERE (:now - lasttime) > :lifetime ". - "AND (threadid IS NULL OR " . - "(SELECT count(*) FROM {chatthread} WHERE threadid = {chatsitevisitor}.threadid " . - "AND istate <> " . Thread::STATE_CLOSED . " AND istate <> " . Thread::STATE_LEFT . ") = 0)", - array( - ':lifetime' => Settings::get('tracking_lifetime'), - ':now' => time() - ) - ); - -// Remove old invitations - $db->query( - "UPDATE {chatsitevisitor} SET invited = 0, invitationtime = NULL, invitedby = NULL". - " WHERE threadid IS NULL AND (:now - invitationtime) > :lifetime", - array( - ':lifetime' => Settings::get('invitation_lifetime'), - ':now' => time() - ) - ); - -// Remove associations of visitors with closed threads - $db->query( - "UPDATE {chatsitevisitor} SET threadid = NULL WHERE threadid IS NOT NULL AND" . - " (SELECT count(*) FROM {chatthread} WHERE threadid = {chatsitevisitor}.threadid" . - " AND istate <> " . Thread::STATE_CLOSED . " AND istate <> " . Thread::STATE_LEFT . ") = 0" - ); - -// Remove old visitors' tracks - $db->query( - "DELETE FROM {visitedpage} WHERE (:now - visittime) > :lifetime " . - " AND visitorid NOT IN (SELECT visitorid FROM {chatsitevisitor})", - array( - ':lifetime' => Settings::get('tracking_lifetime'), - ':now' => time() - ) - ); - - $query = "SELECT visitorid, userid, username, firsttime, lasttime, " . - "entry, details, invited, invitationtime, invitedby, invitations, chats " . - "FROM {chatsitevisitor} " . - "WHERE threadid IS NULL " . - "ORDER BY invited, lasttime DESC, invitations"; - $query .= (Settings::get('visitors_limit') == '0') ? "" : " LIMIT " . Settings::get('visitors_limit'); - - $rows = $db->query($query, NULL, array('return_rows' => Database::RETURN_ALL_ROWS)); - - $output = array(); - foreach ($rows as $row) { - $visitor = visitor_to_xml($row); - $output[] = $visitor; - } - - echo ""; - foreach ($output as $thr) { - print myiconv($webim_encoding, "utf-8", $thr); - } - echo ""; -} - -$since = verifyparam("since", "/^\d{1,9}$/", 0); -$status = verifyparam("status", "/^\d{1,2}$/", 0); -$showonline = verifyparam("showonline", "/^1$/", 0); -$showvisitors = verifyparam("showvisitors", "/^1$/", 0); - -if (!isset($_SESSION["${mysqlprefix}operatorgroups"])) { - $_SESSION["${mysqlprefix}operatorgroups"] = get_operator_groupslist($operator['operatorid']); -} -Thread::closeOldThreads(); -$groupids = $_SESSION["${mysqlprefix}operatorgroups"]; - -start_xml_output(); -echo ''; -if ($showonline) { - print_operators($operator); -} -print_pending_threads($groupids, $since); -if ($showvisitors) { - print_visitors(); -} -echo ''; -notify_operator_alive($operator['operatorid'], $status); -exit; +$processor = UsersProcessor::getInstance(); +$processor->receiveRequest($_POST['data']); ?> \ No newline at end of file diff --git a/src/messenger/webim/operator/users.php b/src/messenger/webim/operator/users.php index 00e96173..7ae813a2 100644 --- a/src/messenger/webim/operator/users.php +++ b/src/messenger/webim/operator/users.php @@ -35,6 +35,22 @@ $page['frequency'] = Settings::get('updatefrequency_operator'); $page['istatus'] = $status; $page['showonline'] = Settings::get('showonlineoperators') == '1' ? "1" : "0"; $page['showvisitors'] = Settings::get('enabletracking') == '1' ? "1" : "0"; +$page['agentId'] = $operator['operatorid']; +$page['geoLink'] = Settings::get('geolink'); +$page['geoWindowParams'] = Settings::get('geolinkparams'); + +// Load dialogs style options +$style_config = get_dialogs_style_config(getchatstyle()); +$page['chatStyles.chatWindowParams'] = $style_config['chat']['window_params']; + +// Load core style options +$style_config = get_core_style_config(); +$page['coreStyles.threadTag'] = $style_config['users']['thread_tag']; +$page['coreStyles.visitorTag'] = $style_config['users']['visitor_tag']; +$page['coreStyles.trackedUserWindowParams'] = $style_config['tracked']['user_window_params']; +$page['coreStyles.trackedVisitorWindowParams'] = $style_config['tracked']['visitor_window_params']; +$page['coreStyles.inviteWindowParams'] = $style_config['invitation']['window_params']; +$page['coreStyles.banWindowParams'] = $style_config['ban']['window_params']; prepare_menu($operator); start_html_output(); diff --git a/src/messenger/webim/view/config.ini b/src/messenger/webim/view/config.ini index 4e5e849c..606968be 100644 --- a/src/messenger/webim/view/config.ini +++ b/src/messenger/webim/view/config.ini @@ -4,3 +4,22 @@ [history] ; window_param use as param string in JavaScript window.open method window_params = "toolbar=0,scrollbars=0,location=0,status=1,menubar=0,width=720,height=560,resizable=1" + +[users] +; Use as wrap tag for the thread element +thread_tag = "tr" +; Use as wrap tag for the visitor element +visitor_tag = "tr" + +[tracked] +; window_param use as param string in JavaScript window.open method +user_window_params = "toolbar=0,scrollbars=0,location=0,status=1,menubar=0,width=640,height=480,resizable=1" +visitor_window_params = "toolbar=0,scrollbars=1,location=0,status=1,menubar=0,width=640,height=480,resizable=1" + +[invitation] +; window_param use as param string in JavaScript window.open method +window_params = "toolbar=0,scrollbars=0,location=0,status=1,menubar=0,width=640,height=480,resizable=1" + +[ban] +; window_param use as param string in JavaScript window.open method +window_params = "toolbar=0,scrollbars=0,location=0,status=1,menubar=0,width=720,height=480,resizable=1" diff --git a/src/messenger/webim/view/inc_main.php b/src/messenger/webim/view/inc_main.php index 0bb8c446..fc524514 100644 --- a/src/messenger/webim/view/inc_main.php +++ b/src/messenger/webim/view/inc_main.php @@ -30,6 +30,7 @@ $isrtl = getlocal("localedirection") == 'rtl'; - + style="min-width: 400px;"> diff --git a/src/messenger/webim/view/pending_users.php b/src/messenger/webim/view/pending_users.php index e844a4ae..39d759fb 100644 --- a/src/messenger/webim/view/pending_users.php +++ b/src/messenger/webim/view/pending_users.php @@ -21,29 +21,98 @@ $page['menuid'] = "users"; function tpl_header() { global $page, $webimroot; -?> - - + + + + + + + + + + + + - + + +
-
- -
+

- - - - - - - - - - - - - - - - - - - - - - - - - -
Loading....
+

- - - - - - - - - - - - - - - - - - - - - - -
Loading....
+

-
-
- - - - -
-
- - +
+
+