From ba19d9dab94858c68ef753f0148a1c3ec42e61c1 Mon Sep 17 00:00:00 2001 From: Hadi Nategh Date: Wed, 1 Apr 2015 16:10:43 +0000 Subject: [PATCH 01/30] Fix addressing wrong href in previous commit #52368 --- phpgwapi/js/jquery/blueimp/js/blueimp-gallery.js | 2 +- phpgwapi/js/jquery/blueimp/js/blueimp-gallery.min.js | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/phpgwapi/js/jquery/blueimp/js/blueimp-gallery.js b/phpgwapi/js/jquery/blueimp/js/blueimp-gallery.js index 9f3b7b1bc7..85d1a2582f 100644 --- a/phpgwapi/js/jquery/blueimp/js/blueimp-gallery.js +++ b/phpgwapi/js/jquery/blueimp/js/blueimp-gallery.js @@ -881,7 +881,7 @@ } else if (isTarget(options.downloadClass)) { // Click on "download" control - if (typeof event.target.download_href != 'undefined') + if (this.list[this.getIndex()] && typeof this.list[this.getIndex()].download_href != 'undefined') { event.target.href = this.list[this.getIndex()].download_href; } diff --git a/phpgwapi/js/jquery/blueimp/js/blueimp-gallery.min.js b/phpgwapi/js/jquery/blueimp/js/blueimp-gallery.min.js index 5affe66b6a..c867f8dd9a 100644 --- a/phpgwapi/js/jquery/blueimp/js/blueimp-gallery.min.js +++ b/phpgwapi/js/jquery/blueimp/js/blueimp-gallery.min.js @@ -1,2 +1,2 @@ -(function(factory){"use strict";if(typeof define==="function"&&define.amd){define(["./blueimp-helper"],factory)}else{window.blueimp=window.blueimp||{};window.blueimp.Gallery=factory(window.blueimp.helper||window.jQuery)}})(function($){"use strict";function Gallery(list,options){if(document.body.style.maxHeight===undefined){return null}if(!this||this.options!==Gallery.prototype.options){return new Gallery(list,options)}if(!list||!list.length){this.console.log("blueimp Gallery: No or empty list provided as first argument.",list);return}this.list=list;this.num=list.length;this.initOptions(options);this.initialize()}$.extend(Gallery.prototype,{options:{container:"#blueimp-gallery",slidesContainer:"div",titleElement:"h3",displayClass:"blueimp-gallery-display",controlsClass:"blueimp-gallery-controls",singleClass:"blueimp-gallery-single",leftEdgeClass:"blueimp-gallery-left",rightEdgeClass:"blueimp-gallery-right",playingClass:"blueimp-gallery-playing",slideClass:"slide",slideLoadingClass:"slide-loading",slideErrorClass:"slide-error",slideContentClass:"slide-content",toggleClass:"toggle",prevClass:"prev",nextClass:"next",closeClass:"close",playPauseClass:"play-pause",fullscreenClass:"fullscreen",downloadClass:"download",typeProperty:"type",titleProperty:"title",urlProperty:"href",displayTransition:true,clearSlides:true,stretchImages:false,toggleControlsOnReturn:true,toggleSlideshowOnSpace:true,toggleFullscreenOnSlideShow:false,enableKeyboardNavigation:true,closeOnEscape:true,hideControlsOnSlideshow:false,closeOnSlideClick:true,closeOnSwipeUpOrDown:true,emulateTouchEvents:true,stopTouchEventsPropagation:false,hidePageScrollbars:true,disableScroll:true,carousel:false,continuous:true,unloadElements:true,startSlideshow:false,slideshowInterval:5e3,index:0,preloadRange:2,transitionSpeed:400,slideshowTransitionSpeed:undefined,event:undefined,onopen:undefined,onopened:undefined,onslide:undefined,onslideend:undefined,onslidecomplete:undefined,onclose:undefined,onclosed:undefined},carouselOptions:{hidePageScrollbars:false,toggleControlsOnReturn:false,toggleSlideshowOnSpace:false,enableKeyboardNavigation:false,closeOnEscape:false,closeOnSlideClick:false,closeOnSwipeUpOrDown:false,disableScroll:false,startSlideshow:true},console:window.console&&typeof window.console.log==="function"?window.console:{log:function(){}},support:function(element){var support={touch:window.ontouchstart!==undefined||window.DocumentTouch&&document instanceof DocumentTouch},transitions={webkitTransition:{end:"webkitTransitionEnd",prefix:"-webkit-"},MozTransition:{end:"transitionend",prefix:"-moz-"},OTransition:{end:"otransitionend",prefix:"-o-"},transition:{end:"transitionend",prefix:""}},elementTests=function(){var transition=support.transition,prop,translateZ;document.body.appendChild(element);if(transition){prop=transition.name.slice(0,-9)+"ransform";if(element.style[prop]!==undefined){element.style[prop]="translateZ(0)";translateZ=window.getComputedStyle(element).getPropertyValue(transition.prefix+"transform");support.transform={prefix:transition.prefix,name:prop,translate:true,translateZ:!!translateZ&&translateZ!=="none"}}}if(element.style.backgroundSize!==undefined){support.backgroundSize={};element.style.backgroundSize="contain";support.backgroundSize.contain=window.getComputedStyle(element).getPropertyValue("background-size")==="contain";element.style.backgroundSize="cover";support.backgroundSize.cover=window.getComputedStyle(element).getPropertyValue("background-size")==="cover"}document.body.removeChild(element)};(function(support,transitions){var prop;for(prop in transitions){if(transitions.hasOwnProperty(prop)&&element.style[prop]!==undefined){support.transition=transitions[prop];support.transition.name=prop;break}}})(support,transitions);if(document.body){elementTests()}else{$(document).on("DOMContentLoaded",elementTests)}return support}(document.createElement("div")),requestAnimationFrame:window.requestAnimationFrame||window.webkitRequestAnimationFrame||window.mozRequestAnimationFrame,initialize:function(){this.initStartIndex();if(this.initWidget()===false){return false}this.initEventListeners();this.onslide(this.index);this.ontransitionend();if(this.options.startSlideshow){this.play()}},slide:function(to,speed){window.clearTimeout(this.timeout);var index=this.index,direction,naturalDirection,diff;if(index===to||this.num===1){return}if(!speed){speed=this.options.transitionSpeed}if(this.support.transform){if(!this.options.continuous){to=this.circle(to)}direction=Math.abs(index-to)/(index-to);if(this.options.continuous){naturalDirection=direction;direction=-this.positions[this.circle(to)]/this.slideWidth;if(direction!==naturalDirection){to=-direction*this.num+to}}diff=Math.abs(index-to)-1;while(diff){diff-=1;this.move(this.circle((to>index?to:index)-diff-1),this.slideWidth*direction,0)}to=this.circle(to);this.move(index,this.slideWidth*direction,speed);this.move(to,0,speed);if(this.options.continuous){this.move(this.circle(to-direction),-(this.slideWidth*direction),0)}}else{to=this.circle(to);this.animate(index*-this.slideWidth,to*-this.slideWidth,speed)}this.onslide(to)},getIndex:function(){return this.index},getNumber:function(){return this.num},prev:function(){if(this.options.continuous||this.index){this.slide(this.index-1)}},next:function(){if(this.options.continuous||this.index1){this.timeout=this.setTimeout(!this.requestAnimationFrame&&this.slide||function(to,speed){that.animationFrameId=that.requestAnimationFrame.call(window,function(){that.slide(to,speed)})},[this.index+1,this.options.slideshowTransitionSpeed],this.interval)}this.container.addClass(this.options.playingClass)},pause:function(){window.clearTimeout(this.timeout);this.interval=null;this.container.removeClass(this.options.playingClass)},add:function(list){var i;if(!list.concat){list=Array.prototype.slice.call(list)}if(!this.list.concat){this.list=Array.prototype.slice.call(this.list)}this.list=this.list.concat(list);this.num=this.list.length;if(this.num>2&&this.options.continuous===null){this.options.continuous=true;this.container.removeClass(this.options.leftEdgeClass)}this.container.removeClass(this.options.rightEdgeClass).removeClass(this.options.singleClass);for(i=this.num-list.length;ispeed){that.slidesContainer[0].style.left=to+"px";that.ontransitionend();window.clearInterval(timer);return}that.slidesContainer[0].style.left=(to-from)*(Math.floor(timeElap/speed*100)/100)+from+"px"},4)},preventDefault:function(event){if(event.preventDefault){event.preventDefault()}else{event.returnValue=false}},stopPropagation:function(event){if(event.stopPropagation){event.stopPropagation()}else{event.cancelBubble=true}},onresize:function(){this.initSlides(true)},onmousedown:function(event){if(event.which&&event.which===1&&event.target.nodeName!=="VIDEO"){event.preventDefault();(event.originalEvent||event).touches=[{pageX:event.pageX,pageY:event.pageY}];this.ontouchstart(event)}},onmousemove:function(event){if(this.touchStart){(event.originalEvent||event).touches=[{pageX:event.pageX,pageY:event.pageY}];this.ontouchmove(event)}},onmouseup:function(event){if(this.touchStart){this.ontouchend(event);delete this.touchStart}},onmouseout:function(event){if(this.touchStart){var target=event.target,related=event.relatedTarget;if(!related||related!==target&&!$.contains(target,related)){this.onmouseup(event)}}},ontouchstart:function(event){if(this.options.stopTouchEventsPropagation){this.stopPropagation(event)}var touches=(event.originalEvent||event).touches[0];this.touchStart={x:touches.pageX,y:touches.pageY,time:Date.now()};this.isScrolling=undefined;this.touchDelta={}},ontouchmove:function(event){if(this.options.stopTouchEventsPropagation){this.stopPropagation(event)}var touches=(event.originalEvent||event).touches[0],scale=(event.originalEvent||event).scale,index=this.index,touchDeltaX,indices;if(touches.length>1||scale&&scale!==1){return}if(this.options.disableScroll){event.preventDefault()}this.touchDelta={x:touches.pageX-this.touchStart.x,y:touches.pageY-this.touchStart.y};touchDeltaX=this.touchDelta.x;if(this.isScrolling===undefined){this.isScrolling=this.isScrolling||Math.abs(touchDeltaX)0||index===this.num-1&&touchDeltaX<0?Math.abs(touchDeltaX)/this.slideWidth+1:1);indices=[index];if(index){indices.push(index-1)}if(index20||Math.abs(this.touchDelta.x)>slideWidth/2,isPastBounds=!index&&this.touchDelta.x>0||index===this.num-1&&this.touchDelta.x<0,isValidClose=!isValidSlide&&this.options.closeOnSwipeUpOrDown&&(isShortDuration&&Math.abs(this.touchDelta.y)>20||Math.abs(this.touchDelta.y)>this.slideHeight/2),direction,indexForward,indexBackward,distanceForward,distanceBackward;if(this.options.continuous){isPastBounds=false}direction=this.touchDelta.x<0?-1:1;if(!this.isScrolling){if(isValidSlide&&!isPastBounds){indexForward=index+direction;indexBackward=index-direction;distanceForward=slideWidth*direction;distanceBackward=-slideWidth*direction;if(this.options.continuous){this.move(this.circle(indexForward),distanceForward,0);this.move(this.circle(index-2*direction),distanceBackward,0)}else if(indexForward>=0&&indexForwardthis.container[0].clientHeight){target.style.maxHeight=this.container[0].clientHeight}if(this.interval&&this.slides[this.index]===parent){this.play()}this.setTimeout(this.options.onslidecomplete,[index,parent])},onload:function(event){this.oncomplete(event)},onerror:function(event){this.oncomplete(event)},onkeydown:function(event){switch(event.which||event.keyCode){case 13:if(this.options.toggleControlsOnReturn){this.preventDefault(event);this.toggleControls()}break;case 27:if(this.options.closeOnEscape){this.close()}break;case 32:if(this.options.toggleSlideshowOnSpace){this.preventDefault(event);this.toggleSlideshow()}break;case 37:if(this.options.enableKeyboardNavigation){this.preventDefault(event);this.prev()}break;case 39:if(this.options.enableKeyboardNavigation){this.preventDefault(event);this.next()}break}},handleClick:function(event){var options=this.options,target=event.target||event.srcElement,parent=target.parentNode,isTarget=function(className){return $(target).hasClass(className)||$(parent).hasClass(className)};if(isTarget(options.toggleClass)){this.preventDefault(event);this.toggleControls()}else if(isTarget(options.prevClass)){this.preventDefault(event);this.prev()}else if(isTarget(options.nextClass)){this.preventDefault(event);this.next()}else if(isTarget(options.closeClass)){this.preventDefault(event);this.close()}else if(isTarget(options.playPauseClass)){this.preventDefault(event);this.toggleSlideshow()}else if(isTarget(options.fullscreenClass)){this.preventDefault(event);this.toggleFullscreen()}else if(isTarget(options.downloadClass)){if(typeof event.target.download_href!="undefined"){event.target.href=this.list[this.getIndex()].download_href}else{event.target.href=this.list[this.getIndex()].href}if(typeof event.target.download!="undefined"){event.target.download=this.list[this.getIndex()].title};}else if(parent===this.slidesContainer[0]){this.preventDefault(event);if(options.closeOnSlideClick){this.close()}else{this.toggleControls()}}else if(parent.parentNode&&parent.parentNode===this.slidesContainer[0]){this.preventDefault(event);this.toggleControls()}},onclick:function(event){if(this.options.emulateTouchEvents&&this.touchDelta&&(Math.abs(this.touchDelta.x)>20||Math.abs(this.touchDelta.y)>20)){delete this.touchDelta;return}return this.handleClick(event)},updateEdgeClasses:function(index){if(!index){this.container.addClass(this.options.leftEdgeClass)}else{this.container.removeClass(this.options.leftEdgeClass)}if(index===this.num-1){this.container.addClass(this.options.rightEdgeClass)}else{this.container.removeClass(this.options.rightEdgeClass)}},handleSlide:function(index){if(!this.options.continuous){this.updateEdgeClasses(index)}this.loadElements(index);if(this.options.unloadElements){this.unloadElements(index)}this.setTitle(index)},onslide:function(index){this.index=index;this.handleSlide(index);this.setTimeout(this.options.onslide,[index,this.slides[index]])},setTitle:function(index){var text=this.slides[index].firstChild.title,titleElement=this.titleElement;if(titleElement.length){this.titleElement.empty();if(text){titleElement[0].appendChild(document.createTextNode(text))}}},setTimeout:function(func,args,wait){var that=this;return func&&window.setTimeout(function(){func.apply(that,args||[])},wait||0)},imageFactory:function(obj,callback){var that=this,img=this.imagePrototype.cloneNode(false),url=obj,backgroundSize=this.options.stretchImages,called,element,callbackWrapper=function(event){if(!called){event={type:event.type,target:element};if(!element.parentNode){return that.setTimeout(callbackWrapper,[event])}called=true;$(img).off("load error",callbackWrapper);if(backgroundSize){if(event.type==="load"){element.style.background='url("'+url+'") center no-repeat';element.style.backgroundSize=backgroundSize}}callback(event)}},title;if(typeof url!=="string"){url=this.getItemProperty(obj,this.options.urlProperty);title=this.getItemProperty(obj,this.options.titleProperty)}if(backgroundSize===true){backgroundSize="contain"}backgroundSize=this.support.backgroundSize&&this.support.backgroundSize[backgroundSize]&&backgroundSize;if(backgroundSize){element=this.elementPrototype.cloneNode(false)}else{element=img;img.draggable=false}if(title){element.title=title}$(img).on("load error",callbackWrapper);img.src=url;return element},createElement:function(obj,callback){var type=obj&&this.getItemProperty(obj,this.options.typeProperty),factory=type&&this[type.split("/")[0]+"Factory"]||this.imageFactory,element=obj&&factory.call(this,obj,callback);if(!element){element=this.elementPrototype.cloneNode(false);this.setTimeout(callback,[{type:"error",target:element}])}$(element).addClass(this.options.slideContentClass);return element},loadElement:function(index){if(!this.elements[index]){if(this.slides[index].firstChild){this.elements[index]=$(this.slides[index]).hasClass(this.options.slideErrorClass)?3:2}else{this.elements[index]=1;$(this.slides[index]).addClass(this.options.slideLoadingClass);this.slides[index].appendChild(this.createElement(this.list[index],this.proxyListener))}}},loadElements:function(index){var limit=Math.min(this.num,this.options.preloadRange*2+1),j=index,i;for(i=0;ithis.options.preloadRange&&diff+this.options.preloadRangeindex?-this.slideWidth:this.indexindex?to:index)-diff-1),this.slideWidth*direction,0)}to=this.circle(to);this.move(index,this.slideWidth*direction,speed);this.move(to,0,speed);if(this.options.continuous){this.move(this.circle(to-direction),-(this.slideWidth*direction),0)}}else{to=this.circle(to);this.animate(index*-this.slideWidth,to*-this.slideWidth,speed)}this.onslide(to)},getIndex:function(){return this.index},getNumber:function(){return this.num},prev:function(){if(this.options.continuous||this.index){this.slide(this.index-1)}},next:function(){if(this.options.continuous||this.index1){this.timeout=this.setTimeout(!this.requestAnimationFrame&&this.slide||function(to,speed){that.animationFrameId=that.requestAnimationFrame.call(window,function(){that.slide(to,speed)})},[this.index+1,this.options.slideshowTransitionSpeed],this.interval)}this.container.addClass(this.options.playingClass)},pause:function(){window.clearTimeout(this.timeout);this.interval=null;this.container.removeClass(this.options.playingClass)},add:function(list){var i;if(!list.concat){list=Array.prototype.slice.call(list)}if(!this.list.concat){this.list=Array.prototype.slice.call(this.list)}this.list=this.list.concat(list);this.num=this.list.length;if(this.num>2&&this.options.continuous===null){this.options.continuous=true;this.container.removeClass(this.options.leftEdgeClass)}this.container.removeClass(this.options.rightEdgeClass).removeClass(this.options.singleClass);for(i=this.num-list.length;ispeed){that.slidesContainer[0].style.left=to+"px";that.ontransitionend();window.clearInterval(timer);return}that.slidesContainer[0].style.left=(to-from)*(Math.floor(timeElap/speed*100)/100)+from+"px"},4)},preventDefault:function(event){if(event.preventDefault){event.preventDefault()}else{event.returnValue=false}},stopPropagation:function(event){if(event.stopPropagation){event.stopPropagation()}else{event.cancelBubble=true}},onresize:function(){this.initSlides(true)},onmousedown:function(event){if(event.which&&event.which===1&&event.target.nodeName!=="VIDEO"){event.preventDefault();(event.originalEvent||event).touches=[{pageX:event.pageX,pageY:event.pageY}];this.ontouchstart(event)}},onmousemove:function(event){if(this.touchStart){(event.originalEvent||event).touches=[{pageX:event.pageX,pageY:event.pageY}];this.ontouchmove(event)}},onmouseup:function(event){if(this.touchStart){this.ontouchend(event);delete this.touchStart}},onmouseout:function(event){if(this.touchStart){var target=event.target,related=event.relatedTarget;if(!related||related!==target&&!$.contains(target,related)){this.onmouseup(event)}}},ontouchstart:function(event){if(this.options.stopTouchEventsPropagation){this.stopPropagation(event)}var touches=(event.originalEvent||event).touches[0];this.touchStart={x:touches.pageX,y:touches.pageY,time:Date.now()};this.isScrolling=undefined;this.touchDelta={}},ontouchmove:function(event){if(this.options.stopTouchEventsPropagation){this.stopPropagation(event)}var touches=(event.originalEvent||event).touches[0],scale=(event.originalEvent||event).scale,index=this.index,touchDeltaX,indices;if(touches.length>1||scale&&scale!==1){return}if(this.options.disableScroll){event.preventDefault()}this.touchDelta={x:touches.pageX-this.touchStart.x,y:touches.pageY-this.touchStart.y};touchDeltaX=this.touchDelta.x;if(this.isScrolling===undefined){this.isScrolling=this.isScrolling||Math.abs(touchDeltaX)0||index===this.num-1&&touchDeltaX<0?Math.abs(touchDeltaX)/this.slideWidth+1:1);indices=[index];if(index){indices.push(index-1)}if(index20||Math.abs(this.touchDelta.x)>slideWidth/2,isPastBounds=!index&&this.touchDelta.x>0||index===this.num-1&&this.touchDelta.x<0,isValidClose=!isValidSlide&&this.options.closeOnSwipeUpOrDown&&(isShortDuration&&Math.abs(this.touchDelta.y)>20||Math.abs(this.touchDelta.y)>this.slideHeight/2),direction,indexForward,indexBackward,distanceForward,distanceBackward;if(this.options.continuous){isPastBounds=false}direction=this.touchDelta.x<0?-1:1;if(!this.isScrolling){if(isValidSlide&&!isPastBounds){indexForward=index+direction;indexBackward=index-direction;distanceForward=slideWidth*direction;distanceBackward=-slideWidth*direction;if(this.options.continuous){this.move(this.circle(indexForward),distanceForward,0);this.move(this.circle(index-2*direction),distanceBackward,0)}else if(indexForward>=0&&indexForwardthis.container[0].clientHeight){target.style.maxHeight=this.container[0].clientHeight}if(this.interval&&this.slides[this.index]===parent){this.play()}this.setTimeout(this.options.onslidecomplete,[index,parent])},onload:function(event){this.oncomplete(event)},onerror:function(event){this.oncomplete(event)},onkeydown:function(event){switch(event.which||event.keyCode){case 13:if(this.options.toggleControlsOnReturn){this.preventDefault(event);this.toggleControls()}break;case 27:if(this.options.closeOnEscape){this.close()}break;case 32:if(this.options.toggleSlideshowOnSpace){this.preventDefault(event);this.toggleSlideshow()}break;case 37:if(this.options.enableKeyboardNavigation){this.preventDefault(event);this.prev()}break;case 39:if(this.options.enableKeyboardNavigation){this.preventDefault(event);this.next()}break}},handleClick:function(event){var options=this.options,target=event.target||event.srcElement,parent=target.parentNode,isTarget=function(className){return $(target).hasClass(className)||$(parent).hasClass(className)};if(isTarget(options.toggleClass)){this.preventDefault(event);this.toggleControls()}else if(isTarget(options.prevClass)){this.preventDefault(event);this.prev()}else if(isTarget(options.nextClass)){this.preventDefault(event);this.next()}else if(isTarget(options.closeClass)){this.preventDefault(event);this.close()}else if(isTarget(options.playPauseClass)){this.preventDefault(event);this.toggleSlideshow()}else if(isTarget(options.fullscreenClass)){this.preventDefault(event);this.toggleFullscreen()}else if(isTarget(options.downloadClass)){if(this.list[this.getIndex()]&&typeof this.list[this.getIndex()].download_href!="undefined"){event.target.href=this.list[this.getIndex()].download_href}else{event.target.href=this.list[this.getIndex()].href}if(typeof event.target.download!="undefined"){event.target.download=this.list[this.getIndex()].title};}else if(parent===this.slidesContainer[0]){this.preventDefault(event);if(options.closeOnSlideClick){this.close()}else{this.toggleControls()}}else if(parent.parentNode&&parent.parentNode===this.slidesContainer[0]){this.preventDefault(event);this.toggleControls()}},onclick:function(event){if(this.options.emulateTouchEvents&&this.touchDelta&&(Math.abs(this.touchDelta.x)>20||Math.abs(this.touchDelta.y)>20)){delete this.touchDelta;return}return this.handleClick(event)},updateEdgeClasses:function(index){if(!index){this.container.addClass(this.options.leftEdgeClass)}else{this.container.removeClass(this.options.leftEdgeClass)}if(index===this.num-1){this.container.addClass(this.options.rightEdgeClass)}else{this.container.removeClass(this.options.rightEdgeClass)}},handleSlide:function(index){if(!this.options.continuous){this.updateEdgeClasses(index)}this.loadElements(index);if(this.options.unloadElements){this.unloadElements(index)}this.setTitle(index)},onslide:function(index){this.index=index;this.handleSlide(index);this.setTimeout(this.options.onslide,[index,this.slides[index]])},setTitle:function(index){var text=this.slides[index].firstChild.title,titleElement=this.titleElement;if(titleElement.length){this.titleElement.empty();if(text){titleElement[0].appendChild(document.createTextNode(text))}}},setTimeout:function(func,args,wait){var that=this;return func&&window.setTimeout(function(){func.apply(that,args||[])},wait||0)},imageFactory:function(obj,callback){var that=this,img=this.imagePrototype.cloneNode(false),url=obj,backgroundSize=this.options.stretchImages,called,element,callbackWrapper=function(event){if(!called){event={type:event.type,target:element};if(!element.parentNode){return that.setTimeout(callbackWrapper,[event])}called=true;$(img).off("load error",callbackWrapper);if(backgroundSize){if(event.type==="load"){element.style.background='url("'+url+'") center no-repeat';element.style.backgroundSize=backgroundSize}}callback(event)}},title;if(typeof url!=="string"){url=this.getItemProperty(obj,this.options.urlProperty);title=this.getItemProperty(obj,this.options.titleProperty)}if(backgroundSize===true){backgroundSize="contain"}backgroundSize=this.support.backgroundSize&&this.support.backgroundSize[backgroundSize]&&backgroundSize;if(backgroundSize){element=this.elementPrototype.cloneNode(false)}else{element=img;img.draggable=false}if(title){element.title=title}$(img).on("load error",callbackWrapper);img.src=url;return element},createElement:function(obj,callback){var type=obj&&this.getItemProperty(obj,this.options.typeProperty),factory=type&&this[type.split("/")[0]+"Factory"]||this.imageFactory,element=obj&&factory.call(this,obj,callback);if(!element){element=this.elementPrototype.cloneNode(false);this.setTimeout(callback,[{type:"error",target:element}])}$(element).addClass(this.options.slideContentClass);return element},loadElement:function(index){if(!this.elements[index]){if(this.slides[index].firstChild){this.elements[index]=$(this.slides[index]).hasClass(this.options.slideErrorClass)?3:2}else{this.elements[index]=1;$(this.slides[index]).addClass(this.options.slideLoadingClass);this.slides[index].appendChild(this.createElement(this.list[index],this.proxyListener))}}},loadElements:function(index){var limit=Math.min(this.num,this.options.preloadRange*2+1),j=index,i;for(i=0;ithis.options.preloadRange&&diff+this.options.preloadRangeindex?-this.slideWidth:this.index Date: Wed, 1 Apr 2015 16:24:43 +0000 Subject: [PATCH 02/30] Better error message for invalid sprintf --- phpgwapi/js/egw_action/egw_action_common.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/phpgwapi/js/egw_action/egw_action_common.js b/phpgwapi/js/egw_action/egw_action_common.js index 8c4b645bf5..26f706e895 100644 --- a/phpgwapi/js/egw_action/egw_action_common.js +++ b/phpgwapi/js/egw_action/egw_action_common.js @@ -512,7 +512,7 @@ function sprintf() { case 'd': a = parseInt(a); break; case 'e': a = m[6] ? a.toExponential(m[6]) : a.toExponential(); break; case 'f': a = m[6] ? parseFloat(a).toFixed(m[6]) : parseFloat(a); break; - case 'o': a = a.toString(8); break; + case 'o': a = typeof(a) == 'number' ? a.toString(8):JSON.stringify(a); break; case 's': a = ((a = String(a)) && m[6] ? a.substring(0, m[6]) : a); break; case 'u': a = Math.abs(a); break; case 'x': a = a.toString(16); break; @@ -525,7 +525,7 @@ function sprintf() { o.push(s + (m[4] ? a + p : p + a)); } else { - throw('Huh ?!'); + throw('Invalid sprintf format "' + arguments[0]+'"'); } f = f.substring(m[0].length); } From bfad87395272600ea03e1cef2cbcfc56306f7d75 Mon Sep 17 00:00:00 2001 From: Hadi Nategh Date: Wed, 1 Apr 2015 16:46:01 +0000 Subject: [PATCH 03/30] Fix broken blueimp-gallery plugin minified css file --- phpgwapi/js/jquery/blueimp/css/blueimp-gallery.min.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/phpgwapi/js/jquery/blueimp/css/blueimp-gallery.min.css b/phpgwapi/js/jquery/blueimp/css/blueimp-gallery.min.css index b7bc2746b2..0e1351ea03 100644 --- a/phpgwapi/js/jquery/blueimp/css/blueimp-gallery.min.css +++ b/phpgwapi/js/jquery/blueimp/css/blueimp-gallery.min.css @@ -1 +1 @@ -@charset "UTF-8";.blueimp-gallery,.blueimp-gallery>.slides>.slide>.slide-content{position:absolute;top:0;right:0;bottom:0;left:0;-moz-backface-visibility:hidden}.blueimp-gallery>.slides>.slide>.slide-content{margin:auto;width:auto;height:auto;max-width:100%;max-height:100%;opacity:1}.blueimp-gallery{position:fixed;z-index:999999;overflow:hidden;background:#000;background:rgba(0,0,0,0.9);opacity:0;display:none;direction:ltr;-ms-touch-action:none;touch-action:none}.blueimp-gallery-carousel{position:relative;z-index:auto;margin:1em auto;padding-bottom:56.25%;box-shadow:0 0 10px #000;-ms-touch-action:pan-y;touch-action:pan-y}.blueimp-gallery-display{display:block;opacity:1}.blueimp-gallery>.slides{position:relative;height:100%;overflow:hidden}.blueimp-gallery-carousel>.slides{position:absolute}.blueimp-gallery>.slides>.slide{position:relative;float:left;height:100%;text-align:center;-webkit-transition-timing-function:cubic-bezier(0.645,0.045,0.355,1.000);-moz-transition-timing-function:cubic-bezier(0.645,0.045,0.355,1.000);-ms-transition-timing-function:cubic-bezier(0.645,0.045,0.355,1.000);-o-transition-timing-function:cubic-bezier(0.645,0.045,0.355,1.000);transition-timing-function:cubic-bezier(0.645,0.045,0.355,1.000)}.blueimp-gallery,.blueimp-gallery>.slides>.slide>.slide-content{-webkit-transition:opacity .5s linear;-moz-transition:opacity .5s linear;-ms-transition:opacity .5s linear;-o-transition:opacity .5s linear;transition:opacity .5s linear}.blueimp-gallery>.slides>.slide-loading{background:url(../img/loading.gif) center no-repeat;background-size:64px 64px}.blueimp-gallery>.slides>.slide-loading>.slide-content{opacity:0}.blueimp-gallery>.slides>.slide-error{background:url(../img/error.png) center no-repeat}.blueimp-gallery>.slides>.slide-error>.slide-content{display:none}.blueimp-gallery>.prev,.blueimp-gallery>.next{position:absolute;top:50%;left:15px;width:40px;height:40px;margin-top:-23px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:60px;font-weight:100;line-height:30px;color:#fff;text-decoration:none;text-shadow:0 0 2px #000;text-align:center;background:#222;background:rgba(0,0,0,0.5);-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;border:3px solid #fff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:.5;cursor:pointer;display:none}.blueimp-gallery>.next{left:auto;right:15px}.blueimp-gallery>.close,.blueimp-gallery>.title{position:absolute;top:15px;left:15px;margin:0 40px 0 0;font-size:20px;line-height:30px;color:#fff;text-shadow:0 0 2px #000;opacity:.8;display:none}.blueimp-gallery>.close{padding:15px;right:15px;left:auto;margin:-15px;font-size:30px;text-decoration:none;cursor:pointer}.blueimp-gallery>.play-pause{position:absolute;right:15px;bottom:15px;width:15px;height:15px;background:url(../img/play-pause.png) 0 0 no-repeat;cursor:pointer;opacity:.5;display:none}.blueimp-gallery-playing>.play-pause{background-position:-15px 0}.blueimp-gallery>.fullscreen{position:absolute;right:15px;bottom:15px;width:15px;height:15px;background:url(../img/fullscreen.png) 0 0 no-repeat;cursor:pointer;opacity:.5;display:none}.blueimp-gallery>.download{position:absolute;right:45px;bottom:15px;width:15px;height:15px;background:url(../img/download.png) 0 0 no-repeat;cursor:pointer;opacity:.5;display:none}.blueimp-gallery>.prev:hover,.blueimp-gallery>.next:hover,.blueimp-gallery>.close:hover,.blueimp-gallery>.title:hover,.blueimp-gallery>.play-pause:hover,.blueimp-gallery>.fullscreen:hover,.blueimp-gallery>.download:hover{color:#fff;opacity:1}.blueimp-gallery-controls>.prev,.blueimp-gallery-controls>.next,.blueimp-gallery-controls>.close,.blueimp-gallery-controls>.title,.blueimp-gallery-controls>.play-pause,.blueimp-gallery-controls>.fullscreen,.blueimp-gallery-controls>.download{display:block;-webkit-transform:translateZ(0);-moz-transform:translateZ(0);-ms-transform:translateZ(0);-o-transform:translateZ(0);transform:translateZ(0)}.blueimp-gallery-single>.prev,.blueimp-gallery-left>.prev,.blueimp-gallery-single>.next,.blueimp-gallery-right>.next,.blueimp-gallery-single>.play-pause{display:none}.blueimp-gallery>.slides>.slide>.slide-content,.blueimp-gallery>.prev,.blueimp-gallery>.next,.blueimp-gallery>.close,.blueimp-gallery>.play-pause,.blueimp-gallery>.fullscreen,.blueimp-gallery>.download{-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}body:last-child .blueimp-gallery>.slides>.slide-error{background-image:url(../img/error.svg)}body:last-child .blueimp-gallery>.play-pause{width:20px;height:20px;background-size:40px 20px;background-image:url(../img/play-pause.svg)}body:last-child .blueimp-gallery-playing>.play-pause{background-position:-20px 0}*+html .blueimp-gallery>.slides>.slide{min-height:300px}*+html .blueimp-gallery>.slides>.slide>.slide-content{position:relative} \ No newline at end of file +@charset "UTF-8";.blueimp-gallery,.blueimp-gallery>.slides>.slide>.slide-content{position:absolute;top:0;right:0;bottom:0;left:0;-moz-backface-visibility:hidden}.blueimp-gallery>.slides>.slide>.slide-content{margin:auto;width:auto;height:auto;max-width:100%;max-height:100%;opacity:1}.blueimp-gallery{position:fixed;z-index:999999;overflow:hidden;background:#000;background:rgba(0,0,0,0.9);opacity:0;display:none;direction:ltr;-ms-touch-action:none;touch-action:none}.blueimp-gallery-carousel{position:relative;z-index:auto;margin:1em auto;padding-bottom:56.25%;box-shadow:0 0 10px #000;-ms-touch-action:pan-y;touch-action:pan-y}.blueimp-gallery-display{display:block;opacity:1}.blueimp-gallery>.slides{position:relative;height:100%;overflow:hidden}.blueimp-gallery-carousel>.slides{position:absolute}.blueimp-gallery>.slides>.slide{position:relative;float:left;height:100%;text-align:center;-webkit-transition-timing-function:cubic-bezier(0.645,0.045,0.355,1.000);-moz-transition-timing-function:cubic-bezier(0.645,0.045,0.355,1.000);-ms-transition-timing-function:cubic-bezier(0.645,0.045,0.355,1.000);-o-transition-timing-function:cubic-bezier(0.645,0.045,0.355,1.000);transition-timing-function:cubic-bezier(0.645,0.045,0.355,1.000)}.blueimp-gallery,.blueimp-gallery>.slides>.slide>.slide-content{-webkit-transition:opacity .5s linear;-moz-transition:opacity .5s linear;-ms-transition:opacity .5s linear;-o-transition:opacity .5s linear;transition:opacity .5s linear}.blueimp-gallery>.slides>.slide-loading{background:url(../img/loading.gif) center no-repeat;background-size:64px 64px}.blueimp-gallery>.slides>.slide-loading>.slide-content{opacity:0}.blueimp-gallery>.slides>.slide-error{background:url(../img/error.png) center no-repeat}.blueimp-gallery>.slides>.slide-error>.slide-content{display:none}.blueimp-gallery>.prev,.blueimp-gallery>.next{position:absolute;top:50%;left:15px;width:40px;height:40px;margin-top:-23px;font-family:"Helvetica Neue",Helvetica,Arial,sans-serif;font-size:60px;font-weight:100;line-height:30px;color:#fff;text-decoration:none;text-shadow:0 0 2px #000;text-align:center;background:#222;background:rgba(0,0,0,0.5);-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;border:3px solid #fff;-webkit-border-radius:23px;-moz-border-radius:23px;border-radius:23px;opacity:.5;cursor:pointer;display:none}.blueimp-gallery>.next{left:auto;right:15px}.blueimp-gallery>.close,.blueimp-gallery>.title{position:absolute;top:15px;left:15px;margin:0 40px 0 0;font-size:20px;line-height:30px;color:#fff;text-shadow:0 0 2px #000;opacity:.8;display:none}.blueimp-gallery>.close{padding:15px;right:15px;left:auto;margin:-15px;font-size:30px;text-decoration:none;cursor:pointer}.blueimp-gallery>.play-pause{position:absolute;right:15px;bottom:15px;width:15px;height:15px;background:url(../img/play-pause.png) 0 0 no-repeat;cursor:pointer;opacity:.5;display:none}.blueimp-gallery-playing>.play-pause{background-position:-15px 0}.blueimp-gallery>.fullscreen{position:absolute;right:15px;bottom:15px;width:15px;height:15px;background:url(../img/fullscreen.png) 0 0 no-repeat;cursor:pointer;opacity:.5;display:none}.blueimp-gallery>.download{position:absolute;right:45px;bottom:15px;width:15px;height:15px;background:url(../img/download.png) 0 0 no-repeat;cursor:pointer;opacity:.5;display:none}.blueimp-gallery>.prev:hover,.blueimp-gallery>.next:hover,.blueimp-gallery>.close:hover,.blueimp-gallery>.title:hover,.blueimp-gallery>.play-pause:hover,.blueimp-gallery>.fullscreen:hover,.blueimp-gallery>.download:hover{color:#fff;opacity:1}.blueimp-gallery-controls>.prev,.blueimp-gallery-controls>.next,.blueimp-gallery-controls>.close,.blueimp-gallery-controls>.title,.blueimp-gallery-controls>.play-pause,.blueimp-gallery-controls>.fullscreen,.blueimp-gallery-controls>.download{display:block;-webkit-transform:translateZ(0);-moz-transform:translateZ(0);-ms-transform:translateZ(0);-o-transform:translateZ(0);transform:translateZ(0)}.blueimp-gallery-single>.prev,.blueimp-gallery-left>.prev,.blueimp-gallery-single>.next,.blueimp-gallery-right>.next,.blueimp-gallery-single>.play-pause{display:none}.blueimp-gallery>.slides>.slide>.slide-content,.blueimp-gallery>.prev,.blueimp-gallery>.next,.blueimp-gallery>.close,.blueimp-gallery>.play-pause,.blueimp-gallery>.fullscreen,.blueimp-gallery>.download{-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}body:last-child .blueimp-gallery>.slides>.slide-error{background-image:url(../img/error.svg)}body:last-child .blueimp-gallery>.play-pause{width:20px;height:20px;background-size:40px 20px;background-image:url(../img/play-pause.svg)}body:last-child .blueimp-gallery-playing>.play-pause{background-position:-20px 0}*+html .blueimp-gallery>.slides>.slide{min-height:300px}*+html .blueimp-gallery>.slides>.slide>.slide-content{position:relative}@charset "UTF-8";.blueimp-gallery>.indicator{position:absolute;top:auto;right:15px;bottom:15px;left:15px;margin:0 40px;padding:0;list-style:none;text-align:center;line-height:10px;display:none}.blueimp-gallery>.indicator>li{display:inline-block;width:9px;height:9px;margin:6px 3px 0 3px;-webkit-box-sizing:content-box;-moz-box-sizing:content-box;box-sizing:content-box;border:1px solid transparent;background:#ccc;background:rgba(255,255,255,0.25) center no-repeat;border-radius:5px;box-shadow:0 0 2px #000;opacity:.5;cursor:pointer}.blueimp-gallery>.indicator>li:hover,.blueimp-gallery>.indicator>.active{background-color:#fff;border-color:#fff;opacity:1}.blueimp-gallery-controls>.indicator{display:block;-webkit-transform:translateZ(0);-moz-transform:translateZ(0);-ms-transform:translateZ(0);-o-transform:translateZ(0);transform:translateZ(0)}.blueimp-gallery-single>.indicator{display:none}.blueimp-gallery>.indicator{-webkit-user-select:none;-khtml-user-select:none;-moz-user-select:none;-ms-user-select:none;user-select:none}*+html .blueimp-gallery>.indicator>li{display:inline}.blueimp-gallery>.slides>.slide>.video-content>img{position:absolute;top:0;right:0;bottom:0;left:0;margin:auto;width:auto;height:auto;max-width:100%;max-height:100%;-moz-backface-visibility:hidden}.blueimp-gallery>.slides>.slide>.video-content>video{position:absolute;top:0;left:0;width:100%;height:100%}.blueimp-gallery>.slides>.slide>.video-content>iframe{position:absolute;top:100%;left:0;width:100%;height:100%;border:0}.blueimp-gallery>.slides>.slide>.video-playing>iframe{top:0}.blueimp-gallery>.slides>.slide>.video-content>a{position:absolute;top:50%;right:0;left:0;margin:-64px auto 0;width:128px;height:128px;background:url(../img/video-play.png) center no-repeat;opacity:.8;cursor:pointer}.blueimp-gallery>.slides>.slide>.video-content>a:hover{opacity:1}.blueimp-gallery>.slides>.slide>.video-playing>a,.blueimp-gallery>.slides>.slide>.video-playing>img{display:none}.blueimp-gallery>.slides>.slide>.video-content>video{display:none}.blueimp-gallery>.slides>.slide>.video-playing>video{display:block}.blueimp-gallery>.slides>.slide>.video-loading>a{background:url(../img/loading.gif) center no-repeat;background-size:64px 64px}body:last-child .blueimp-gallery>.slides>.slide>.video-content:not(.video-loading)>a{background-image:url(../img/video-play.svg)}*+html .blueimp-gallery>.slides>.slide>.video-content{height:100%}*+html .blueimp-gallery>.slides>.slide>.video-content>a{left:50%;margin-left:-64px} \ No newline at end of file From 5c944330c5153b5a86b8686f085ffa6033800f9a Mon Sep 17 00:00:00 2001 From: Hadi Nategh Date: Wed, 1 Apr 2015 16:51:18 +0000 Subject: [PATCH 04/30] Expose link widget WIP: - Implement expose view for link-string widget - Adapt link-string widget, and other widgets which are using expose to download_href link --- etemplate/js/et2_widget_description.js | 1 + etemplate/js/et2_widget_link.js | 54 +++++++++++++++++++++++--- etemplate/js/et2_widget_vfs.js | 1 + phpgwapi/inc/class.egw_link.inc.php | 4 ++ 4 files changed, 55 insertions(+), 5 deletions(-) diff --git a/etemplate/js/et2_widget_description.js b/etemplate/js/et2_widget_description.js index ba832a5164..8909e28aba 100644 --- a/etemplate/js/et2_widget_description.js +++ b/etemplate/js/et2_widget_description.js @@ -218,6 +218,7 @@ var et2_description = expose(et2_baseWidget.extend([et2_IDetachedDOM], type: this.options.type + "/*", thumbnail: base_url + _value }]; + if (_value.match(/\/webdav.php/,'ig')) mediaContent[0]["download_href"] = base_url + _value + '?download'; } return mediaContent; }, diff --git a/etemplate/js/et2_widget_link.js b/etemplate/js/et2_widget_link.js index 9ac47fff91..16425f2991 100644 --- a/etemplate/js/et2_widget_link.js +++ b/etemplate/js/et2_widget_link.js @@ -1237,7 +1237,7 @@ et2_register_widget(et2_link, ["link", "link-entry_ro"]); * * @augments et2_valueWidget */ -var et2_link_string = et2_valueWidget.extend([et2_IDetachedDOM], +var et2_link_string = expose(et2_valueWidget.extend([et2_IDetachedDOM], { attributes: { "application": { @@ -1261,7 +1261,13 @@ var et2_link_string = et2_valueWidget.extend([et2_IDetachedDOM], "type": "string", "default":"", "description": "Sub-type key to list only entries of that type" - } + }, + "expose_view":{ + name: "Expose view", + type: "boolean", + default: true, + description: "Clicking on description with href value would popup an expose view, and will show content referenced by href." + }, }, /** @@ -1339,14 +1345,52 @@ var et2_link_string = et2_valueWidget.extend([et2_IDetachedDOM], this.egw().jsonq(this.egw().getAppName()+'.etemplate_widget_link.ajax_link_list', [_value], this.set_value, this); return; }, - + /** + * Function to get media content to feed the expose + * @param {type} _value + * @returns {Array|Array.getMedia.mediaContent} + */ + getMedia: function (_value) + { + var base_url = egw.webserverUrl.match(/^\//,'ig')?egw(window).window.location.origin + egw.webserverUrl : egw.webserverUrl; + var mediaContent = []; + if (_value && typeof _value.type !='undefined' && _value.type.match(/video\/|audio\//,'ig')) + { + mediaContent = [{ + title: _value.id, + type: _value.type, + poster:'', // TODO: Should be changed by correct video thumbnail later + href: base_url + egw().mime_open(_value), + download_href: base_url + egw().mime_open(_value) + '?download', + }]; + } + else if(_value) + { + mediaContent = [{ + title: _value.id, + href: base_url + egw().mime_open(_value).url, + download_href: base_url + egw().mime_open(_value).url + '?download', + type: _value.type, + }]; + } + if (mediaContent[0].href && mediaContent[0].href.match(/\/webdav.php/,'ig')) mediaContent[0]["download_href"] = mediaContent[0].href + '?download'; + return mediaContent; + }, _add_link: function(_link_data) { var self = this; var link = $j(document.createElement("li")) .appendTo(this.list) .addClass("et2_link loading") .click( function(e){ - self.egw().open(_link_data, "", "view",null,_link_data.app,_link_data.app); + if (self.options.expose_view && typeof _link_data.type !='undefined' + && _link_data.type.match(self.mime_regexp,'ig')) + { + self._init_blueimp_gallery(e, _link_data); + } + else + { + self.egw().open(_link_data, "", "view",null,_link_data.app,_link_data.app); + } e.stopImmediatePropagation(); }); @@ -1423,7 +1467,7 @@ var et2_link_string = et2_valueWidget.extend([et2_IDetachedDOM], this._labelContainer.contents().not(this.list).remove(); } } -}); +})); et2_register_widget(et2_link_string, ["link-string"]); /** diff --git a/etemplate/js/et2_widget_vfs.js b/etemplate/js/et2_widget_vfs.js index 8b44006088..50083e5431 100644 --- a/etemplate/js/et2_widget_vfs.js +++ b/etemplate/js/et2_widget_vfs.js @@ -346,6 +346,7 @@ var et2_vfsMime = expose(et2_valueWidget.extend([et2_IDetachedDOM], thumbnail: _value.path && _value.mime ? this.egw().mime_icon(_value.mime, _value.path, undefined, _value.mtime) : this.image.attr('src')+ '&thheight=128' }]; } + if (mediaContent[0].href && mediaContent[0].href.match(/\/webdav.php/,'ig')) mediaContent[0]["download_href"] = mediaContent[0].href + '?download'; return mediaContent; }, diff --git a/phpgwapi/inc/class.egw_link.inc.php b/phpgwapi/inc/class.egw_link.inc.php index 0a6beb631a..e84f8c0302 100644 --- a/phpgwapi/inc/class.egw_link.inc.php +++ b/phpgwapi/inc/class.egw_link.inc.php @@ -152,6 +152,10 @@ class egw_link extends solink 'mime_popup' => '640x480', 'mime_target' => '_blank', ), + '/^image\\//' => array( // image + 'mime_popup' => '640x480', + 'mime_target' => '_blank', + ), ), ), ); From 1deb3d4f34508b18b6ea822e23c7bfd2ec9181c5 Mon Sep 17 00:00:00 2001 From: Nathan Gray Date: Wed, 1 Apr 2015 17:11:21 +0000 Subject: [PATCH 05/30] Selectbox options kept on the client side Static options copied to the client side in the JS code where possible, and requested from the server once via AJAX when needed. --- .../class.etemplate_widget_menupopup.inc.php | 122 +++++++--- .../class.etemplate_widget_nextmatch.inc.php | 8 + etemplate/js/et2_extension_nextmatch.js | 26 ++- etemplate/js/et2_widget_selectbox.js | 218 +++++++++++++++++- 4 files changed, 328 insertions(+), 46 deletions(-) diff --git a/etemplate/inc/class.etemplate_widget_menupopup.inc.php b/etemplate/inc/class.etemplate_widget_menupopup.inc.php index f49d56d2de..868706cab5 100644 --- a/etemplate/inc/class.etemplate_widget_menupopup.inc.php +++ b/etemplate/inc/class.etemplate_widget_menupopup.inc.php @@ -23,6 +23,27 @@ class etemplate_widget_menupopup extends etemplate_widget */ const SEARCH_ROW_LIMIT = PHP_INT_MAX; // Automatic disabled, only explicit + /** + * These types are either set or cached on the client side, so we don't send + * their options unless asked via AJAX + */ + public static $cached_types = array( + 'select-account', + 'select-app', + 'select-bool', + 'select-country', + 'select-dow', + 'select-number', + 'select-priority', + 'select-percent', + 'select-year', + 'select-month', + 'select-day', + 'select-hour', + 'select-lang', + 'select-timezone' + ); + /** * @var array */ @@ -42,6 +63,19 @@ class etemplate_widget_menupopup extends etemplate_widget 12 => 'December' ); + /** + * Constructor + * + * @param string|XMLReader $xml string with xml or XMLReader positioned on the element to construct + * @throws egw_exception_wrong_parameter + */ + public function __construct($xml = '') + { + if($xml) { + parent::__construct($xml); + } + } + /** * Parse and set extra attributes from xml in template object * @@ -92,6 +126,11 @@ class etemplate_widget_menupopup extends etemplate_widget $value = $value_in = self::get_array($content, $form_name); $allowed = self::selOptions($form_name, true); // true = return array of option-values + $type_options = self::typeOptions($this, + // typeOptions thinks # of rows is the first thing in options + ($this->attrs['rows'] && strpos($this->attrs['options'], $this->attrs['rows']) !== 0 ? $this->attrs['rows'].','.$this->attrs['options'] : $this->attrs['options'])); + $allowed = array_merge($allowed,array_keys($type_options)); + if (!$this->attrs['multiple'] || !($this->attrs['options'] > 1)) $allowed[] = ''; foreach((array) $value as $val) @@ -232,30 +271,32 @@ class etemplate_widget_menupopup extends etemplate_widget unset(self::$request->content[$this->id]); $this->attrs['readonly'] = true; } - - // adding type specific options here, while keep further options set by app code - // we need to make sure to run only once for auto-repeated rows, because - // array_merge used to keep options from app would otherwise add - // type-specific ones multiple time (and of cause better performance) - $no_lang = null; - static $form_names_done = array(); - if (!isset($form_names_done[$form_name]) && - ($type_options = self::typeOptions($this, - // typeOptions thinks # of rows is the first thing in options - ($this->attrs['rows'] && strpos($this->attrs['options'], $this->attrs['rows']) !== 0 ? $this->attrs['rows'].','.$this->attrs['options'] : $this->attrs['options']), - $no_lang, $this->attrs['readonly'], self::get_array(self::$request->content, $form_name), $form_name))) + if(!in_array($this->attrs['type'], self::$cached_types)) { - self::fix_encoded_options($type_options); - - self::$request->sel_options[$form_name] = array_merge(self::$request->sel_options[$form_name], $type_options); - - // if no_lang was modified, forward modification to the client - if ($no_lang != $this->attr['no_lang']) + // adding type specific options here, while keep further options set by app code + // we need to make sure to run only once for auto-repeated rows, because + // array_merge used to keep options from app would otherwise add + // type-specific ones multiple time (and of cause better performance) + $no_lang = null; + static $form_names_done = array(); + if (!isset($form_names_done[$form_name]) && + ($type_options = self::typeOptions($this, + // typeOptions thinks # of rows is the first thing in options + ($this->attrs['rows'] && strpos($this->attrs['options'], $this->attrs['rows']) !== 0 ? $this->attrs['rows'].','.$this->attrs['options'] : $this->attrs['options']), + $no_lang, $this->attrs['readonly'], self::get_array(self::$request->content, $form_name), $form_name))) { - self::setElementAttribute($form_name, 'no_lang', $no_lang); + self::fix_encoded_options($type_options); + + self::$request->sel_options[$form_name] = array_merge(self::$request->sel_options[$form_name], $type_options); + + // if no_lang was modified, forward modification to the client + if ($no_lang != $this->attr['no_lang']) + { + self::setElementAttribute($form_name, 'no_lang', $no_lang); + } } + $form_names_done[$form_name] = true; } - $form_names_done[$form_name] = true; } // Make sure  s, etc. are properly encoded when sent, and not double-encoded @@ -431,15 +472,16 @@ class etemplate_widget_menupopup extends etemplate_widget { $widget = $widget_type; $widget_type = $widget->attrs['type'] ? $widget->attrs['type'] : $widget->type; - // Legacy / static support - // Have to do this explicitly, since legacy options is not defined on class level - $legacy_options = explode(',',$_legacy_options); - foreach($legacy_options as &$field) - { - $field = self::expand_name($field, 0, 0,'','',self::$cont); - } - list($rows,$type,$type2,$type3,$type4,$type5) = $legacy_options; } + // Legacy / static support + // Have to do this explicitly, since legacy options is not defined on class level + $legacy_options = explode(',',$_legacy_options); + foreach($legacy_options as &$field) + { + $field = self::expand_name($field, 0, 0,'','',self::$cont); + } + + list($rows,$type,$type2,$type3,$type4,$type5) = $legacy_options; $no_lang = false; $options = array(); switch ($widget_type) @@ -466,14 +508,6 @@ class etemplate_widget_menupopup extends etemplate_widget $options = array(0 => 'no',1 => 'yes'); break; - case 'select-access': - $options = array( - 'private' => 'Private', - 'public' => 'Global public', - 'group' => 'Group public' - ); - break; - case 'select-country': // #Row|Extralabel,1=use country name, 0=use 2 letter-code,custom country field name if($type == 0 && $type2) { @@ -790,6 +824,22 @@ class etemplate_widget_menupopup extends etemplate_widget } return $info; } + + /** + * Some select options are fairly static, but can only be generated on the server + * so we generate them here, then cache them client-side + * + * @param string $type + * @param Array|String $attributes + * + */ + public static function ajax_get_options($type, $attributes) + { + $options = self::typeOptions($type, $attributes); + self::fix_encoded_options($options,true); + $response = egw_json_response::get(); + $response->data($options); + } } etemplate_widget::registerWidget('etemplate_widget_menupopup', array('selectbox','listbox','select','menupopup')); diff --git a/etemplate/inc/class.etemplate_widget_nextmatch.inc.php b/etemplate/inc/class.etemplate_widget_nextmatch.inc.php index 5dde80c4bd..9729eb53d3 100644 --- a/etemplate/inc/class.etemplate_widget_nextmatch.inc.php +++ b/etemplate/inc/class.etemplate_widget_nextmatch.inc.php @@ -1206,6 +1206,10 @@ class etemplate_widget_nextmatch_customfilter extends etemplate_widget_transform list($type) = explode('-',$this->attrs['type']); if($type == 'select') { + if(in_array($this->attrs['type'], etemplate_widget_menupopup::$cached_types)) + { + $widget_type = $this->attrs['type']; + } $this->attrs['type'] = 'nextmatch-filterheader'; } self::$transformation['type'] = $this->attrs['type']; @@ -1215,6 +1219,10 @@ class etemplate_widget_nextmatch_customfilter extends etemplate_widget_transform $this->setElementAttribute($form_name, 'options', trim($this->attrs['widget_options']) != '' ? $this->attrs['widget_options'] : ''); $this->setElementAttribute($form_name, 'type', $this->attrs['type']); + if($widget_type) + { + $this->setElementAttribute($form_name, 'widget_type', $widget_type); + } parent::beforeSendToClient($cname, $expand); } } diff --git a/etemplate/js/et2_extension_nextmatch.js b/etemplate/js/et2_extension_nextmatch.js index 3968513c94..66b3d2db8e 100644 --- a/etemplate/js/et2_extension_nextmatch.js +++ b/etemplate/js/et2_extension_nextmatch.js @@ -3226,12 +3226,14 @@ var et2_nextmatch_customfilter = et2_nextmatch_filterheader.extend( "widget_type": { "name": "Actual type", "type": "string", - "description": "The actual type of widget you should use" + "description": "The actual type of widget you should use", + "no_lang": 1 }, "widget_options": { "name": "Actual options", "type": "any", "description": "The options for the actual widget", + "no_lang": 1, "default": {} } }, @@ -3247,7 +3249,6 @@ var et2_nextmatch_customfilter = et2_nextmatch_filterheader.extend( * @memberOf et2_nextmatch_customfilter */ init: function(_parent, _attrs) { - this._super.apply(this, arguments); switch(_attrs.widget_type) { @@ -3255,11 +3256,26 @@ var et2_nextmatch_customfilter = et2_nextmatch_filterheader.extend( _attrs.type = 'nextmatch-entryheader'; break; default: - _attrs.type = _attrs.widget_type; + if(_attrs.widget_type.indexOf('select') === 0) + { + _attrs.type = 'nextmatch-filterheader'; + } + else + { + _attrs.type = _attrs.widget_type; + } } - // Avoid warning about non-existant attribute - delete(_attrs.widget_type); + jQuery.extend(_attrs.widget_options,{id: this.id}); + + _attrs.id = ''; + this._super.apply(this, arguments); this.real_node = et2_createWidget(_attrs.type, _attrs.widget_options, this._parent); + var select_options = []; + var correct_type = _attrs.type; + this.real_node._type = _attrs.widget_type; + et2_selectbox.find_select_options(this.real_node, select_options, _attrs); + this.real_node._type = correct_type; + this.real_node.set_select_options(select_options); }, // Just pass the real DOM node through, in case anybody asks diff --git a/etemplate/js/et2_widget_selectbox.js b/etemplate/js/et2_widget_selectbox.js index 2c17c14a64..5fe57e0614 100644 --- a/etemplate/js/et2_widget_selectbox.js +++ b/etemplate/js/et2_widget_selectbox.js @@ -170,7 +170,7 @@ var et2_selectbox = et2_inputWidget.extend( return; } - var sel_options = et2_selectbox.find_select_options(this, _attrs['select_options']); + var sel_options = et2_selectbox.find_select_options(this, _attrs['select_options'], _attrs); if(!jQuery.isEmptyObject(sel_options)) { _attrs['select_options'] = sel_options; @@ -748,7 +748,7 @@ var et2_selectbox = et2_inputWidget.extend( } }); et2_register_widget(et2_selectbox, ["menupopup", "listbox", "select", "select-cat", - "select-percent", 'select-priority', 'select-access', + "select-percent", 'select-priority', 'select-country', 'select-state', 'select-year', 'select-month', 'select-day', 'select-dow', 'select-hour', 'select-number', 'select-app', 'select-lang', 'select-bool', 'select-timezone' ]); @@ -756,20 +756,35 @@ et2_register_widget(et2_selectbox, ["menupopup", "listbox", "select", "select-ca // Static class stuff jQuery.extend(et2_selectbox, { + type_cache: {}, + /** * Find the select options for a widget, out of the many places they could be. * @param {et2_widget} widget to check for. Should be some sort of select widget. * @param {object} attr_options Select options in attributes array + * @param {object} attrs Widget attributes * @return {object} Select options, or empty object */ - find_select_options: function(widget, attr_options) + find_select_options: function(widget, attr_options, attrs) { var name_parts = widget.id.replace(/[/g,'[').replace(/]|]/g,'').split('['); var content_options = {}; - // Try to find the options inside the "sel-options" array - if(widget.getArrayMgr("sel_options")) + // First check type, there may be static options. There's some special handling + // for filterheaders, which have the wrong type. + var type = widget.instanceOf(et2_nextmatch_filterheader) ? attrs.widget_type || '' : widget._type; + var type_function = type.replace('select-','').replace('_ro','')+'_options'; + if(typeof this[type_function] == 'function') + { + var old_type = widget._type; + widget._type = type; + content_options = this[type_function].call(this, widget, attrs); + widget._type = old_type; + } + + // Try to find the options inside the "sel-options" + if(jQuery.isEmptyObject(content_options) && widget.getArrayMgr("sel_options")) { // Try first according to ID content_options = widget.getArrayMgr("sel_options").getEntry(widget.id); @@ -860,6 +875,199 @@ jQuery.extend(et2_selectbox, content_options = {}; } return content_options; + }, + + /** + * Some static options, no need to transfer them over and over. + * We still need the same thing on the server side to validate, so they + * have to match. See etemplate_widget_menupopup::typeOptions() + * The type specific legacy options wind up in attrs.other. + */ + priority_options: function(widget) { + return [ + {value: 1, label: 'low'}, + {value: 2, label: 'normal'}, + {value: 3, label: 'high'} + ]; + }, + bool_options: function(widget) { + return [ + {value: 0, label: 'no'}, + {value: 1, label: 'yes'} + ]; + }, + month_options: function(widget) { + return [ + {value: 1, label:'January'}, + {value: 2, label:'February'}, + {value: 3, label:'March'}, + {value: 4, label:'April'}, + {value: 5, label:'May'}, + {value: 6, label:'June'}, + {value: 7, label:'July'}, + {value: 8, label:'August'}, + {value: 9, label:'September'}, + {value: 10, label:'October'}, + {value: 11, label:'November'}, + {value: 12, label:'December'} + ]; + }, + number_options: function(widget, attrs) { + if(typeof attrs.other != 'object') + { + attrs.other = []; + } + var options = []; + var min = typeof(attrs.other[0]) == 'undefined' ? 1 : parseInt(attrs.other[0]); + var max = typeof(attrs.other[1]) == 'undefined' ? 10: parseInt(attrs.other[1]); + var interval = typeof(attrs.other[2]) == 'undefined' ? 1: parseInt(attrs.other[2]); + var format = '%d'; + + // leading zero specified in interval + if (attrs.other[2] && attrs.other[2][0] == '0') + { + format = '%0'+(''+interval).length+'d'; + } + // Suffix + if(attrs.other[3]) + { + format += widget.egw().lang(attrs.other[3]); + } + + // Avoid infinite loop if interval is the wrong direction + if ((min <= max) != (interval > 0)) + { + interval = -interval; + } + + for (var i=0, n=min; n <= max && i <= 100; n += interval,++i) + { + options.push({value: n, label: sprintf(format,n)}); + } + return options; + }, + percent_options: function(widget, attrs) + { + if(typeof attrs.other != 'object') + { + attrs.other = []; + } + attrs.other[0] = 0; + attrs.other[1] = 100; + attrs.other[2] = typeof(attrs.other[2]) == 'undefined' ? 10 : parseInt(attrs.other[2]); + attrs.other[3] = '%%'; + return this.number_options(widget,attrs); + }, + year_options: function(widget, attrs) + { + if(typeof attrs.other != 'object') + { + attrs.other = []; + } + var t = new Date(); + attrs.other[0] = t.getFullYear() - (typeof(attrs.other[0]) == 'undefined' ? 3 : parseInt(attrs.other[0])); + attrs.other[1] = t.getFullYear() + (typeof(attrs.other[1]) == 'undefined' ? 2 : parseInt(attrs.other[1])); + attrs.other[2] = typeof(attrs.other[2]) == 'undefined' ? 1 : parseInt(attrs.other[2]); + return this.number_options(widget,attrs); + }, + day_options: function(widget, attrs) + { + attrs.other = [1,31,1]; + return this.number_options(widget,attrs); + }, + hour_options: function(widget, attrs) + { + var options = []; + var timeformat = egw.preference('common','timeformat'); + for (var h = 0; h <= 23; ++h) + { + options.push({ + value: h, + label: timeformat == 12 ? + (( 12 ? h % 12 : 12)+' '+(h < 12 ? egw.lang('am') : egw.lang('pm'))) : + sprintf('%02d',h) + }); + } + return options; + }, + app_options: function(widget,attrs) { + var options = ','+(attrs.other||[]).join(','); + return this.cached_server_side_options(widget, options, attrs); + }, + cat_options: function(widget, attrs) { + // Add in application, if not there + if(typeof attrs.other == 'undefined') + { + attrs.other = new Array(4); + } + if(typeof attrs.other[3] == 'undefined') + { + attrs.other[3] = attrs.application || widget.getInstanceManager().app; + } + var options =(attrs.other||[]).join(','); + return this.cached_server_side_options(widget, options, attrs); + }, + country_options: function(widget, attrs) { + var options = ','+(attrs.other||[]).join(','); + return this.cached_server_side_options(widget, options, attrs); + }, + dow_options: function(widget,attrs) { + var options = ','+(attrs.other||[]).join(','); + return this.cached_server_side_options(widget, options, attrs); + }, + lang_options: function(widget,attrs) { + var options = ','+(attrs.other||[]).join(','); + return this.cached_server_side_options(widget, options, attrs); + }, + timezone_options: function(widget,attrs) { + var options = ','+(attrs.other||[]).join(','); + return this.cached_server_side_options(widget, options, attrs); + }, + /** + * Some options change, or are too complicated to have twice, so we get the + * options from the server once, then keep them to use if they're needed again. + * We use the options string to keep the different possibilites (eg. categories + * for different apps) seperate. + * + * @param {et2_selectbox} widget Selectbox we're looking at + * @param {string} options_string + * @param {Object} attrs Widget attributes (not yet fully set) + * @returns {Object} Array of options, or empty and they'll get filled in later + */ + cached_server_side_options: function(widget, options_string, attrs) + { + var cache_id = widget._type+'_'+options_string; + var cache = egw.window.et2_selectbox.type_cache[cache_id]; + if (typeof cache == 'undefined') + { + // Fetch with json instead of jsonq because there may be more than + // one widget listening for the response by the time it gets back, + // and we can't do that when it's queued. + egw.window.et2_selectbox.type_cache[cache_id] = egw.json( + widget.getInstanceManager().app+'.etemplate_widget_menupopup.ajax_get_options.etemplate', + [widget._type,options_string] + ).sendRequest(); + } + cache = egw.window.et2_selectbox.type_cache[cache_id]; + if(typeof cache.done == 'function') + { + // pending, wait for it + cache.done(jQuery.proxy(function(response) { + egw.window.et2_selectbox.type_cache[this.cache_id] = response.response[0].data||undefined; + // Set select_options in attributes in case we get a resonse before + // the widget is finished loading (otherwise it will re-set to {}) + attrs.select_options = egw.window.et2_selectbox.type_cache[this.cache_id]; + + egw.window.setTimeout(jQuery.proxy(function() { + this.widget.set_select_options(egw.window.et2_selectbox.type_cache[this.cache_id]||{}); + },this),1); + },{widget:widget,cache_id:cache_id})); + return {}; + } + else + { + return cache; + } } }); From 3ceda8324d7762b373337edfd07144508a54e43d Mon Sep 17 00:00:00 2001 From: Nathan Gray Date: Wed, 1 Apr 2015 17:26:04 +0000 Subject: [PATCH 06/30] Avoid validation error in alarm options --- calendar/templates/default/edit.xet | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/calendar/templates/default/edit.xet b/calendar/templates/default/edit.xet index b55f8784b4..da3eed7638 100644 --- a/calendar/templates/default/edit.xet +++ b/calendar/templates/default/edit.xet @@ -40,7 +40,7 @@ - + From bd51cb4a087b2c0e8ae862170ecf68314698a02c Mon Sep 17 00:00:00 2001 From: Nathan Gray Date: Wed, 1 Apr 2015 19:35:35 +0000 Subject: [PATCH 07/30] Add support for multi-part history values into historylog --- etemplate/js/et2_widget_historylog.js | 95 ++++++++++++++++++++++----- phpgwapi/inc/class.historylog.inc.php | 8 +++ 2 files changed, 87 insertions(+), 16 deletions(-) diff --git a/etemplate/js/et2_widget_historylog.js b/etemplate/js/et2_widget_historylog.js index 518a1f1854..ac8a66c72b 100644 --- a/etemplate/js/et2_widget_historylog.js +++ b/etemplate/js/et2_widget_historylog.js @@ -285,9 +285,40 @@ var et2_historylog = et2_valueWidget.extend([et2_IDataProvider,et2_IResizeable], var field = this.options.value['status-widgets'][key]; var attrs = {'readonly': true, 'id': key}; var options = null; + var widget = null; if(typeof field == 'object') { - attrs['select_options'] = field; + var need_box = false; + for(var j in field) + { + if(et2_registry[field[j]]) + { + need_box = true; + break; + } + } + if(need_box) + { + // Multi-part value needs multiple widgets + widget = et2_createWidget('vbox', attrs, this); + for(var i in field) + { + if(typeof field[i] == 'object') + { + attrs['select_options'] = field[i]; + } + else + { + delete attrs['select_options']; + } + var child = et2_createWidget(typeof field[i] == 'string' ? field[i] : 'select', attrs, widget); + child.transformAttributes(attrs); + } + } + else + { + attrs['select_options'] = field; + } } // Check for options after the type, ex: link-entry:infolog else if (field.indexOf(':') > 0) @@ -296,7 +327,10 @@ var et2_historylog = et2_valueWidget.extend([et2_IDataProvider,et2_IResizeable], field = options.shift(); } - var widget = et2_createWidget(typeof field == 'string' ? field : 'select', attrs, this); + if(widget == null) + { + widget = et2_createWidget(typeof field == 'string' ? field : 'select', attrs, this); + } // Parse / set legacy options if(options) @@ -336,10 +370,15 @@ var et2_historylog = et2_valueWidget.extend([et2_IDataProvider,et2_IResizeable], widget.transformAttributes(attrs); // Save to use for each row + var nodes = widget._children.length ? [] : jQuery(widget.getDetachedNodes()); + for(var i = 0; i < widget._children.length; i++) + { + nodes.push(jQuery(widget._children[i].getDetachedNodes())); + } this.fields[key] = { attrs: attrs, widget: widget, - nodes: jQuery(widget.getDetachedNodes()) + nodes: nodes }; } // Widget for text diffs @@ -351,18 +390,18 @@ var et2_historylog = et2_valueWidget.extend([et2_IDataProvider,et2_IResizeable], }, getDOMNode: function(_sender) { - if (_sender == this) - { - return this.div[0]; - } + if (_sender == this) + { + return this.div[0]; + } - for (var i = 0; i < this.columns.length; i++) - { - if (_sender == this.columns[i].widget) - { - return this.dataview.getHeaderContainerNode(i); - } - } + for (var i = 0; i < this.columns.length; i++) + { + if (_sender == this.columns[i].widget) + { + return this.dataview.getHeaderContainerNode(i); + } + } return null; }, @@ -410,8 +449,15 @@ var et2_historylog = et2_valueWidget.extend([et2_IDataProvider,et2_IResizeable], var widget = self.columns[i].widget; if(typeof widget == 'undefined' && typeof self.fields[_data.status] != 'undefined') { - nodes = self.fields[_data.status].nodes.clone(); widget = self.fields[_data.status].widget; + if(!widget._children.length) + { + nodes = self.fields[_data.status].nodes.clone(); + } + for(var j = 0; j < widget._children.length; j++) + { + nodes.push(self.fields[_data.status].nodes[j].clone()); + } } else if (widget) { @@ -461,7 +507,24 @@ var et2_historylog = et2_valueWidget.extend([et2_IDataProvider,et2_IResizeable], // No widget fallback - display actual value nodes = ''+_data[self.columns[i].id] + ''; } - if(widget) widget.setDetachedAttributes(nodes, {value:_data[self.columns[i].id]}); + if(widget) + { + if(widget._children.length) + { + // Multi-part values + var box = $j(widget.getDOMNode()).clone(); + for(var j = 0; j < widget._children.length; j++) + { + widget._children[j].setDetachedAttributes(nodes[j], {value:_data[self.columns[i].id][j]}); + box.append(nodes[j]); + } + nodes = box; + } + else + { + widget.setDetachedAttributes(nodes, {value:_data[self.columns[i].id]}); + } + } $j(this).append(nodes); }); $j(tr).append(row.children()); diff --git a/phpgwapi/inc/class.historylog.inc.php b/phpgwapi/inc/class.historylog.inc.php index a98bcfb6c7..7db61086e0 100644 --- a/phpgwapi/inc/class.historylog.inc.php +++ b/phpgwapi/inc/class.historylog.inc.php @@ -249,6 +249,14 @@ class historylog ) as $row) { $row['user_ts'] = $GLOBALS['egw']->db->from_timestamp($row['history_timestamp']) + 3600 * $GLOBALS['egw_info']['user']['preferences']['common']['tz_offset']; + // Explode multi-part values + foreach(array('history_new_value','history_old_value') as $field) + { + if(strpos($row[$field],bo_tracking::ONE2N_SEPERATOR) !== false) + { + $row[$field] = explode(bo_tracking::ONE2N_SEPERATOR,$row[$field]); + } + } // Get information needed for proper display if($row['history_appname'] == 'filemanager') { From df515ab7e8ab09207bfcf59d0f3156719b354c11 Mon Sep 17 00:00:00 2001 From: Nathan Gray Date: Wed, 1 Apr 2015 20:03:04 +0000 Subject: [PATCH 08/30] Match title input max length to database max length --- timesheet/templates/default/edit.xet | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/timesheet/templates/default/edit.xet b/timesheet/templates/default/edit.xet index 7c96ec41d0..ede4deb40b 100644 --- a/timesheet/templates/default/edit.xet +++ b/timesheet/templates/default/edit.xet @@ -100,7 +100,7 @@ - + From db74398622664065f51aee9101ce7d96a142229e Mon Sep 17 00:00:00 2001 From: Nathan Gray Date: Wed, 1 Apr 2015 20:17:53 +0000 Subject: [PATCH 09/30] Adapt detection for missing 'All' category option to array of options style --- etemplate/js/et2_extension_nextmatch.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/etemplate/js/et2_extension_nextmatch.js b/etemplate/js/et2_extension_nextmatch.js index 66b3d2db8e..f30eed6461 100644 --- a/etemplate/js/et2_extension_nextmatch.js +++ b/etemplate/js/et2_extension_nextmatch.js @@ -2394,10 +2394,9 @@ var et2_nextmatch_header_bar = et2_DOMWidget.extend(et2_INextmatchHeader, } // Legacy: Add in 'All' option for cat_id, if not provided. - if(name == 'cat_id' && options != null && typeof options[''] == 'undefined' && typeof options[0] == 'undefined') + if(name == 'cat_id' && options != null && (typeof options[''] == 'undefined' || options[0].value != '')) { widget_options.empty_label = this.egw().lang('All'); - this.egw().debug('warn', 'Nextmatch category filter had no "All" option. Added, but you should fix that.'); } // Create widget From 5edd9b902a4355e2194d701cc71789bf44717847 Mon Sep 17 00:00:00 2001 From: Nathan Gray Date: Wed, 1 Apr 2015 23:04:45 +0000 Subject: [PATCH 10/30] Add button type to et2_dropdown_buttons to stop default browser behaviour. Fixes filters get cleared when pressing enter in entryfilter --- etemplate/js/et2_widget_dropdown_button.js | 2 ++ 1 file changed, 2 insertions(+) diff --git a/etemplate/js/et2_widget_dropdown_button.js b/etemplate/js/et2_widget_dropdown_button.js index 9dfd32b029..0972d2d07e 100644 --- a/etemplate/js/et2_widget_dropdown_button.js +++ b/etemplate/js/et2_widget_dropdown_button.js @@ -146,12 +146,14 @@ var et2_dropdown_button = et2_inputWidget.extend( // Left side - activates click action this.button = $j(document.createElement("button")) .attr("id", this.internal_ids.button) + .attr("type", "button") .addClass("ui-widget ui-corner-left").removeClass("ui-corner-all") .appendTo(this.buttons); // Right side - shows dropdown this.arrow = $j(document.createElement("button")) .addClass("ui-widget ui-corner-right").removeClass("ui-corner-all") + .attr("type", "button") .click(function() { // Clicking it again hides menu if(self.menu.is(":visible")) From 0142bcb50c9623983f4aeb2e5d2972026579c3a0 Mon Sep 17 00:00:00 2001 From: Nathan Gray Date: Wed, 1 Apr 2015 23:14:21 +0000 Subject: [PATCH 11/30] When creating an infolog from other entries, only set primary contact if there's only one entry --- infolog/inc/class.infolog_ui.inc.php | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/infolog/inc/class.infolog_ui.inc.php b/infolog/inc/class.infolog_ui.inc.php index 42bd6e4afa..e583c14147 100644 --- a/infolog/inc/class.infolog_ui.inc.php +++ b/infolog/inc/class.infolog_ui.inc.php @@ -1968,8 +1968,12 @@ class infolog_ui $pm_links = array($action_id); default: // to allow other apps to participate $content['info_subject'] = egw_link::title($action, $id); - $content['info_contact'] = $action.':'.$action_id; - foreach (explode(',', $action_id) as $n => $id) + $action_ids = explode(',',$action_id); + if(count($action_ids) == 1) + { + $content['info_contact'] = $action.':'.$action_id; + } + foreach ($action_ids as $n => $id) { egw_link::link('infolog', $content['link_to']['to_id'], $action, $id); From cca60fa93f1979e052229391ee9f7085e076585a Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Thu, 2 Apr 2015 07:19:37 +0000 Subject: [PATCH 12/30] ?download in WebDAV url did not trigger content-dispostion attachment header --- phpgwapi/inc/class.html.inc.php | 2 +- phpgwapi/inc/class.vfs_webdav_server.inc.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/phpgwapi/inc/class.html.inc.php b/phpgwapi/inc/class.html.inc.php index be1ad71cd5..c3e822c84e 100644 --- a/phpgwapi/inc/class.html.inc.php +++ b/phpgwapi/inc/class.html.inc.php @@ -1630,7 +1630,7 @@ egw_LAB.wait(function() { */ public static function content_disposition_header($fn,$forceDownload=true) { - if ($forceDownload===true) + if ($forceDownload) { $attachment = ' attachment;'; } diff --git a/phpgwapi/inc/class.vfs_webdav_server.inc.php b/phpgwapi/inc/class.vfs_webdav_server.inc.php index 48762610a9..3348b74a29 100644 --- a/phpgwapi/inc/class.vfs_webdav_server.inc.php +++ b/phpgwapi/inc/class.vfs_webdav_server.inc.php @@ -627,7 +627,7 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem */ function __construct() { - if ($_SERVER['REQUEST_METHOD'] == 'GET' && ($this->force_download = strpos($_SERVER['REQUEST_URI'],'?download'))) + if ($_SERVER['REQUEST_METHOD'] == 'GET' && ($this->force_download = strpos($_SERVER['REQUEST_URI'],'?download') !== false)) { $_SERVER['REQUEST_URI'] = substr($_SERVER['REQUEST_URI'],0,$this->force_download); } From ed092a58877bd60c70797f0619a979bd7b780679 Mon Sep 17 00:00:00 2001 From: Hadi Nategh Date: Thu, 2 Apr 2015 08:37:13 +0000 Subject: [PATCH 13/30] Trigger expose view by clicking on link-list's links --- etemplate/js/et2_widget_link.js | 13 ++++++++++++- 1 file changed, 12 insertions(+), 1 deletion(-) diff --git a/etemplate/js/et2_widget_link.js b/etemplate/js/et2_widget_link.js index 16425f2991..30bef8c843 100644 --- a/etemplate/js/et2_widget_link.js +++ b/etemplate/js/et2_widget_link.js @@ -1748,7 +1748,18 @@ var et2_link_list = et2_link_string.extend( $j(document.createElement("td")) .appendTo(row) .addClass(columns[i]) - .click( function(){self.egw().open(_link_data, "", "view",null,_link_data.target ? _link_data.target : _link_data.app,_link_data.app);}) + .click( function(){ + // Check if the link entry is mime with media type, in order to open it in expose view + if (typeof _link_data.type != 'undefined' && _link_data.type.match(self.mime_regexp,'ig')) + { + var $vfs_img_node = jQuery(this).parent().find('.vfsMimeIcon'); + if ($vfs_img_node.length > 0) $vfs_img_node.click(); + } + else + { + self.egw().open(_link_data, "", "view",null,_link_data.target ? _link_data.target : _link_data.app,_link_data.app); + } + }) .text(_link_data[columns[i]] ? _link_data[columns[i]]+"" : ""); } From e3d4a685af012a839d840ca03482270dfbe5640b Mon Sep 17 00:00:00 2001 From: Klaus Leithoff Date: Thu, 2 Apr 2015 08:48:34 +0000 Subject: [PATCH 14/30] use ENT_SUBSTITUTE on htmlspecialchars to harden display of message as source --- mail/inc/class.mail_ui.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/mail/inc/class.mail_ui.inc.php b/mail/inc/class.mail_ui.inc.php index 4e72b41d83..2531225496 100644 --- a/mail/inc/class.mail_ui.inc.php +++ b/mail/inc/class.mail_ui.inc.php @@ -2611,7 +2611,7 @@ class mail_ui else { html::safe_content_header($message, $subject.".eml", $mime='text/html', $size=0, true, false); - print '
'. htmlspecialchars($message, ENT_NOQUOTES, 'utf-8') .'
'; + print '
'. htmlspecialchars($message, ENT_NOQUOTES|ENT_SUBSTITUTE, 'utf-8') .'
'; } } From a14c8801024dde6d045faaad9edc277bd4642a84 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Thu, 2 Apr 2015 08:49:49 +0000 Subject: [PATCH 15/30] * LDAP/Admin: empty groups showed all users, if selected in accounts-list --- phpgwapi/inc/class.accounts_ldap.inc.php | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/phpgwapi/inc/class.accounts_ldap.inc.php b/phpgwapi/inc/class.accounts_ldap.inc.php index 0bb7cf8f69..67872af356 100644 --- a/phpgwapi/inc/class.accounts_ldap.inc.php +++ b/phpgwapi/inc/class.accounts_ldap.inc.php @@ -751,11 +751,7 @@ class accounts_ldap $relevantAccounts = array(); $sri = ldap_search($this->ds,$this->group_context,"(&(objectClass=posixGroup)(gidnumber=" . abs($param['type']) . "))",array('memberuid')); $group = ldap_get_entries($this->ds, $sri); - - if (isset($group[0]['memberuid'])) - { - $fullSet = array_intersect_key($fullSet, array_flip($group[0]['memberuid'])); - } + $fullSet = $group[0]['memberuid'] ? array_intersect_key($fullSet, array_flip($group[0]['memberuid'])) : array(); } $totalcount = count($fullSet); From 21a48297bb20843bbad8b1d972c422ed39ca0770 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Thu, 2 Apr 2015 08:51:36 +0000 Subject: [PATCH 16/30] * Admin/ActiveDirectory: filtering user-list by groups was not implemented --- phpgwapi/inc/class.accounts_ads.inc.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/phpgwapi/inc/class.accounts_ads.inc.php b/phpgwapi/inc/class.accounts_ads.inc.php index d789cda319..211bc27897 100644 --- a/phpgwapi/inc/class.accounts_ads.inc.php +++ b/phpgwapi/inc/class.accounts_ads.inc.php @@ -860,7 +860,7 @@ class accounts_ads $query = ldap::quote(strtolower($param['query'])); $accounts = array(); - if($param['type'] != 'groups') + if($param['type'] !== 'groups') { if (!empty($query) && $query != '*') { @@ -890,6 +890,11 @@ class accounts_ads break; } } + if (is_numeric($param['type'])) + { + $membership_filter = '(memberOf='.$this->id2name((int)$param['type'], 'account_dn').')'; + $filter = $filter ? "(&$membership_filter$filter)" : $membership_filter; + } foreach($this->filter($filter, 'u', self::$user_attributes) as $account_id => $data) { $account = $this->_ldap2user($data); @@ -901,7 +906,7 @@ class accounts_ads $accounts[$account_id] = $account; } } - if ($param['type'] == 'groups' || $param['type'] == 'both') + if ($param['type'] === 'groups' || $param['type'] === 'both') { $query = ldap::quote(strtolower($param['query'])); From ee4997632a482dc504f9c04f114c43b72fdc250f Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Thu, 2 Apr 2015 10:16:22 +0000 Subject: [PATCH 17/30] fixed not shown [Mount /etemplates] button in trunk --- filemanager/inc/class.filemanager_admin.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/filemanager/inc/class.filemanager_admin.inc.php b/filemanager/inc/class.filemanager_admin.inc.php index 41c27f8e6a..4e96bfcae6 100644 --- a/filemanager/inc/class.filemanager_admin.inc.php +++ b/filemanager/inc/class.filemanager_admin.inc.php @@ -200,7 +200,7 @@ class filemanager_admin extends filemanager_ui 'everyone' => lang('Everyone'), ); // show [Mount /etemplates] button for admin, if not already mounted and available - $readonlys['etemplates'] = !class_exists('stylite_merge_stream_wrapper') || egw_vfs::file_exists('/etemplates') || + $readonlys['etemplates'] = !class_exists('\EGroupware\Stylite\Vfs\Merge\StreamWrapper') || egw_vfs::file_exists('/etemplates') || !isset($GLOBALS['egw_info']['user']['apps']['admin']); //_debug_array($content); From 0127e6d07cb47618a8de8a13d6ed0144ee3453ba Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Thu, 2 Apr 2015 10:46:53 +0000 Subject: [PATCH 18/30] fix not working customized etemplates after fix with content-disposition attachment, we now only set it for urls ending with "?download", not for "?download=something" --- phpgwapi/inc/class.vfs_webdav_server.inc.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/phpgwapi/inc/class.vfs_webdav_server.inc.php b/phpgwapi/inc/class.vfs_webdav_server.inc.php index 3348b74a29..a3740ae64d 100644 --- a/phpgwapi/inc/class.vfs_webdav_server.inc.php +++ b/phpgwapi/inc/class.vfs_webdav_server.inc.php @@ -627,7 +627,9 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem */ function __construct() { - if ($_SERVER['REQUEST_METHOD'] == 'GET' && ($this->force_download = strpos($_SERVER['REQUEST_URI'],'?download') !== false)) + if ($_SERVER['REQUEST_METHOD'] == 'GET' && ($this->force_download = strpos($_SERVER['REQUEST_URI'],'?download') !== false && + // do NOT send content-disposition attachment for etemplates containing ?download=$timestamp + strpos($_SERVER['REQUEST_URI'],'?download=') === false)) { $_SERVER['REQUEST_URI'] = substr($_SERVER['REQUEST_URI'],0,$this->force_download); } From 788cd3e078208bfd28488564303da597924e462a Mon Sep 17 00:00:00 2001 From: Hadi Nategh Date: Thu, 2 Apr 2015 11:26:35 +0000 Subject: [PATCH 19/30] Fix REQUEST_URI if there is ?download in url --- phpgwapi/inc/class.vfs_webdav_server.inc.php | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/phpgwapi/inc/class.vfs_webdav_server.inc.php b/phpgwapi/inc/class.vfs_webdav_server.inc.php index a3740ae64d..4e1cb38512 100644 --- a/phpgwapi/inc/class.vfs_webdav_server.inc.php +++ b/phpgwapi/inc/class.vfs_webdav_server.inc.php @@ -627,9 +627,7 @@ class vfs_webdav_server extends HTTP_WebDAV_Server_Filesystem */ function __construct() { - if ($_SERVER['REQUEST_METHOD'] == 'GET' && ($this->force_download = strpos($_SERVER['REQUEST_URI'],'?download') !== false && - // do NOT send content-disposition attachment for etemplates containing ?download=$timestamp - strpos($_SERVER['REQUEST_URI'],'?download=') === false)) + if ($_SERVER['REQUEST_METHOD'] == 'GET' && (($this->force_download = strpos($_SERVER['REQUEST_URI'],'?download')) !== false)) { $_SERVER['REQUEST_URI'] = substr($_SERVER['REQUEST_URI'],0,$this->force_download); } From 17885d27afc800c300aa6e81df76326623060e18 Mon Sep 17 00:00:00 2001 From: Nathan Gray Date: Thu, 2 Apr 2015 14:57:16 +0000 Subject: [PATCH 20/30] Handle bad rounding / floating point math fun --- etemplate/js/et2_widget_date.js | 3 +++ 1 file changed, 3 insertions(+) diff --git a/etemplate/js/et2_widget_date.js b/etemplate/js/et2_widget_date.js index 1d82175c1f..7d8a6684e8 100644 --- a/etemplate/js/et2_widget_date.js +++ b/etemplate/js/et2_widget_date.js @@ -683,6 +683,9 @@ var et2_date_duration = et2_date.extend( value *= 60; break; } + // Minutes should be an integer. Floating point math. + value = Math.round(value); + switch(this.options.data_format) { case 'd': From 6ddf16941f673bf2e1bae1ce9bd0f166ec6e25c5 Mon Sep 17 00:00:00 2001 From: Ralf Becker Date: Thu, 2 Apr 2015 15:19:08 +0000 Subject: [PATCH 21/30] German holidays until 2020 --- calendar/egroupware.org/holidays.DE.csv | 72 +++++++++++++++++++++++++ 1 file changed, 72 insertions(+) diff --git a/calendar/egroupware.org/holidays.DE.csv b/calendar/egroupware.org/holidays.DE.csv index b2932c109b..4c3c16b158 100644 --- a/calendar/egroupware.org/holidays.DE.csv +++ b/calendar/egroupware.org/holidays.DE.csv @@ -279,3 +279,75 @@ DE 1. Advent 27 11 2016 0 0 DE 2. Advent 4 12 2016 0 0 DE 3. Advent 11 12 2016 0 0 DE 4. Advent 18 12 2016 0 0 + +2017: +DE Rosenmontag 11 2 2017 0 0 +DE Fastnacht 12 2 2017 0 0 +DE Aschermittwoch 13 2 2017 0 0 +DE Gründonnerstag 13 4 2017 0 0 +DE Karfreitag 14 4 2017 0 0 +DE Ostersonntag 16 4 2017 0 0 +DE Ostermontag 17 4 2017 0 0 +DE Christi Himmelfahrt 25 5 2017 0 0 +DE Pfingstsonntag 4 5 2017 0 0 +DE Pfingstmontag 5 5 2017 0 0 +DE Fronleichnam 15 6 2017 0 0 +DE Buß- und Bettag 22 11 2017 0 0 +DE 1. Advent 3 12 2017 0 0 +DE 2. Advent 10 12 2017 0 0 +DE 3. Advent 17 12 2017 0 0 +DE 4. Advent 24 12 2017 0 0 + +2018: +DE Rosenmontag 12 2 2018 0 0 +DE Fastnacht 13 2 2018 0 0 +DE Aschermittwoch 14 2 2018 0 0 +DE Gründonnerstag 29 3 2018 0 0 +DE Karfreitag 30 3 2018 0 0 +DE Ostersonntag 1 4 2018 0 0 +DE Ostermontag 2 4 2018 0 0 +DE Christi Himmelfahrt 10 5 2018 0 0 +DE Pfingstsonntag 20 5 2018 0 0 +DE Pfingstmontag 21 5 2018 0 0 +DE Fronleichnam 31 5 2018 0 0 +DE Buß- und Bettag 21 11 2018 0 0 +DE 1. Advent 2 12 2018 0 0 +DE 2. Advent 9 12 2018 0 0 +DE 3. Advent 16 12 2018 0 0 +DE 4. Advent 23 12 2018 0 0 + +2019: +DE Rosenmontag 4 3 2019 0 0 +DE Fastnacht 5 3 2019 0 0 +DE Aschermittwoch 6 3 2019 0 0 +DE Gründonnerstag 18 4 2019 0 0 +DE Karfreitag 19 4 2019 0 0 +DE Ostersonntag 21 4 2019 0 0 +DE Ostermontag 22 4 2019 0 0 +DE Christi Himmelfahrt 30 5 2019 0 0 +DE Pfingstsonntag 9 6 2019 0 0 +DE Pfingstmontag 10 6 2019 0 0 +DE Fronleichnam 20 6 2019 0 0 +DE Buß- und Bettag 20 11 2019 0 0 +DE 1. Advent 1 12 2019 0 0 +DE 2. Advent 8 12 2019 0 0 +DE 3. Advent 15 12 2019 0 0 +DE 4. Advent 22 12 2019 0 0 + +2020: +DE Rosenmontag 24 2 2020 0 0 +DE Fastnacht 25 2 2020 0 0 +DE Aschermittwoch 26 2 2020 0 0 +DE Gründonnerstag 9 4 2020 0 0 +DE Karfreitag 10 3 2020 0 0 +DE Ostersonntag 12 3 2020 0 0 +DE Ostermontag 13 3 2020 0 0 +DE Christi Himmelfahrt 21 5 2020 0 0 +DE Pfingstsonntag 31 5 2020 0 0 +DE Pfingstmontag 1 6 2020 0 0 +DE Fronleichnam 11 6 2020 0 0 +DE Buß- und Bettag 18 11 2020 0 0 +DE 1. Advent 29 11 2020 0 0 +DE 2. Advent 6 12 2020 0 0 +DE 3. Advent 13 12 2020 0 0 +DE 4. Advent 20 12 2020 0 0 From 9cebe6a8fb62ae33f2f5a6bf601f310f2d96cf52 Mon Sep 17 00:00:00 2001 From: Nathan Gray Date: Thu, 2 Apr 2015 15:41:05 +0000 Subject: [PATCH 22/30] Fix unable to remove last value from multiselect --- etemplate/inc/class.etemplate_widget_menupopup.inc.php | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/etemplate/inc/class.etemplate_widget_menupopup.inc.php b/etemplate/inc/class.etemplate_widget_menupopup.inc.php index 868706cab5..5584eb309b 100644 --- a/etemplate/inc/class.etemplate_widget_menupopup.inc.php +++ b/etemplate/inc/class.etemplate_widget_menupopup.inc.php @@ -213,11 +213,9 @@ class etemplate_widget_menupopup extends etemplate_widget } } } - if (isset($value)) - { - self::set_array($validated, $form_name, $value); + + self::set_array($validated, $form_name, $value); //error_log(__METHOD__."() $form_name: ".array2string($value_in).' --> '.array2string($value).', allowed='.array2string($allowed)); - } } else { From b6a4afa80c1322aeb145e7818badf9d3541520f8 Mon Sep 17 00:00:00 2001 From: Hadi Nategh Date: Thu, 2 Apr 2015 15:43:15 +0000 Subject: [PATCH 23/30] Fix style of details tab in edit dialog --- calendar/templates/default/edit.xet | 8 ++++---- calendar/templates/pixelegg/app.css | 1 + calendar/templates/pixelegg/app.less | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/calendar/templates/default/edit.xet b/calendar/templates/default/edit.xet index da3eed7638..891f98d99c 100644 --- a/calendar/templates/default/edit.xet +++ b/calendar/templates/default/edit.xet @@ -4,7 +4,7 @@