diff options
author | Bart Visscher <bartv@thisnet.nl> | 2012-07-24 18:35:09 +0200 |
---|---|---|
committer | Bart Visscher <bartv@thisnet.nl> | 2012-07-24 18:35:09 +0200 |
commit | 0040b7109f1201805d9e4ac7e586b44261c335a4 (patch) | |
tree | 852fdb4394e8ab3bfb2bdb4300e542e8abbb7d80 | |
parent | 768b44b9b685a07af6030e484ab6322ba44b5b7e (diff) | |
parent | 856d9c0b54cc971940df4ed64429d994faf770f9 (diff) | |
download | nextcloud-server-0040b7109f1201805d9e4ac7e586b44261c335a4.tar.gz nextcloud-server-0040b7109f1201805d9e4ac7e586b44261c335a4.zip |
Merge branch 'master' into routing
Conflicts:
lib/ocs.php
65 files changed, 3455 insertions, 1341 deletions
diff --git a/3rdparty/miniColors/GPL-LICENSE.txt b/3rdparty/miniColors/GPL-LICENSE.txt new file mode 100644 index 00000000000..11dddd00ef0 --- /dev/null +++ b/3rdparty/miniColors/GPL-LICENSE.txt @@ -0,0 +1,278 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright (C) 1989, 1991 Free Software Foundation, Inc. + 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + Everyone is permitted to copy and distribute verbatim copies + of this license document, but changing it is not allowed. + + Preamble + + The licenses for most software are designed to take away your +freedom to share and change it. By contrast, the GNU General Public +License is intended to guarantee your freedom to share and change free +software--to make sure the software is free for all its users. This +General Public License applies to most of the Free Software +Foundation's software and to any other program whose authors commit to +using it. (Some other Free Software Foundation software is covered by +the GNU Lesser General Public License instead.) You can apply it to +your programs, too. + + When we speak of free software, we are referring to freedom, not +price. Our General Public Licenses are designed to make sure that you +have the freedom to distribute copies of free software (and charge for +this service if you wish), that you receive source code or can get it +if you want it, that you can change the software or use pieces of it +in new free programs; and that you know you can do these things. + + To protect your rights, we need to make restrictions that forbid +anyone to deny you these rights or to ask you to surrender the rights. +These restrictions translate to certain responsibilities for you if you +distribute copies of the software, or if you modify it. + + For example, if you distribute copies of such a program, whether +gratis or for a fee, you must give the recipients all the rights that +you have. You must make sure that they, too, receive or can get the +source code. And you must show them these terms so they know their +rights. + + We protect your rights with two steps: (1) copyright the software, and +(2) offer you this license which gives you legal permission to copy, +distribute and/or modify the software. + + Also, for each author's protection and ours, we want to make certain +that everyone understands that there is no warranty for this free +software. If the software is modified by someone else and passed on, we +want its recipients to know that what they have is not the original, so +that any problems introduced by others will not reflect on the original +authors' reputations. + + Finally, any free program is threatened constantly by software +patents. We wish to avoid the danger that redistributors of a free +program will individually obtain patent licenses, in effect making the +program proprietary. To prevent this, we have made it clear that any +patent must be licensed for everyone's free use or not licensed at all. + + The precise terms and conditions for copying, distribution and +modification follow. + + GNU GENERAL PUBLIC LICENSE + TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION + + 0. This License applies to any program or other work which contains +a notice placed by the copyright holder saying it may be distributed +under the terms of this General Public License. The "Program", below, +refers to any such program or work, and a "work based on the Program" +means either the Program or any derivative work under copyright law: +that is to say, a work containing the Program or a portion of it, +either verbatim or with modifications and/or translated into another +language. (Hereinafter, translation is included without limitation in +the term "modification".) Each licensee is addressed as "you". + +Activities other than copying, distribution and modification are not +covered by this License; they are outside its scope. The act of +running the Program is not restricted, and the output from the Program +is covered only if its contents constitute a work based on the +Program (independent of having been made by running the Program). +Whether that is true depends on what the Program does. + + 1. You may copy and distribute verbatim copies of the Program's +source code as you receive it, in any medium, provided that you +conspicuously and appropriately publish on each copy an appropriate +copyright notice and disclaimer of warranty; keep intact all the +notices that refer to this License and to the absence of any warranty; +and give any other recipients of the Program a copy of this License +along with the Program. + +You may charge a fee for the physical act of transferring a copy, and +you may at your option offer warranty protection in exchange for a fee. + + 2. You may modify your copy or copies of the Program or any portion +of it, thus forming a work based on the Program, and copy and +distribute such modifications or work under the terms of Section 1 +above, provided that you also meet all of these conditions: + + a) You must cause the modified files to carry prominent notices + stating that you changed the files and the date of any change. + + b) You must cause any work that you distribute or publish, that in + whole or in part contains or is derived from the Program or any + part thereof, to be licensed as a whole at no charge to all third + parties under the terms of this License. + + c) If the modified program normally reads commands interactively + when run, you must cause it, when started running for such + interactive use in the most ordinary way, to print or display an + announcement including an appropriate copyright notice and a + notice that there is no warranty (or else, saying that you provide + a warranty) and that users may redistribute the program under + these conditions, and telling the user how to view a copy of this + License. (Exception: if the Program itself is interactive but + does not normally print such an announcement, your work based on + the Program is not required to print an announcement.) + +These requirements apply to the modified work as a whole. If +identifiable sections of that work are not derived from the Program, +and can be reasonably considered independent and separate works in +themselves, then this License, and its terms, do not apply to those +sections when you distribute them as separate works. But when you +distribute the same sections as part of a whole which is a work based +on the Program, the distribution of the whole must be on the terms of +this License, whose permissions for other licensees extend to the +entire whole, and thus to each and every part regardless of who wrote it. + +Thus, it is not the intent of this section to claim rights or contest +your rights to work written entirely by you; rather, the intent is to +exercise the right to control the distribution of derivative or +collective works based on the Program. + +In addition, mere aggregation of another work not based on the Program +with the Program (or with a work based on the Program) on a volume of +a storage or distribution medium does not bring the other work under +the scope of this License. + + 3. You may copy and distribute the Program (or a work based on it, +under Section 2) in object code or executable form under the terms of +Sections 1 and 2 above provided that you also do one of the following: + + a) Accompany it with the complete corresponding machine-readable + source code, which must be distributed under the terms of Sections + 1 and 2 above on a medium customarily used for software interchange; or, + + b) Accompany it with a written offer, valid for at least three + years, to give any third party, for a charge no more than your + cost of physically performing source distribution, a complete + machine-readable copy of the corresponding source code, to be + distributed under the terms of Sections 1 and 2 above on a medium + customarily used for software interchange; or, + + c) Accompany it with the information you received as to the offer + to distribute corresponding source code. (This alternative is + allowed only for noncommercial distribution and only if you + received the program in object code or executable form with such + an offer, in accord with Subsection b above.) + +The source code for a work means the preferred form of the work for +making modifications to it. For an executable work, complete source +code means all the source code for all modules it contains, plus any +associated interface definition files, plus the scripts used to +control compilation and installation of the executable. However, as a +special exception, the source code distributed need not include +anything that is normally distributed (in either source or binary +form) with the major components (compiler, kernel, and so on) of the +operating system on which the executable runs, unless that component +itself accompanies the executable. + +If distribution of executable or object code is made by offering +access to copy from a designated place, then offering equivalent +access to copy the source code from the same place counts as +distribution of the source code, even though third parties are not +compelled to copy the source along with the object code. + + 4. You may not copy, modify, sublicense, or distribute the Program +except as expressly provided under this License. Any attempt +otherwise to copy, modify, sublicense or distribute the Program is +void, and will automatically terminate your rights under this License. +However, parties who have received copies, or rights, from you under +this License will not have their licenses terminated so long as such +parties remain in full compliance. + + 5. You are not required to accept this License, since you have not +signed it. However, nothing else grants you permission to modify or +distribute the Program or its derivative works. These actions are +prohibited by law if you do not accept this License. Therefore, by +modifying or distributing the Program (or any work based on the +Program), you indicate your acceptance of this License to do so, and +all its terms and conditions for copying, distributing or modifying +the Program or works based on it. + + 6. Each time you redistribute the Program (or any work based on the +Program), the recipient automatically receives a license from the +original licensor to copy, distribute or modify the Program subject to +these terms and conditions. You may not impose any further +restrictions on the recipients' exercise of the rights granted herein. +You are not responsible for enforcing compliance by third parties to +this License. + + 7. If, as a consequence of a court judgment or allegation of patent +infringement or for any other reason (not limited to patent issues), +conditions are imposed on you (whether by court order, agreement or +otherwise) that contradict the conditions of this License, they do not +excuse you from the conditions of this License. If you cannot +distribute so as to satisfy simultaneously your obligations under this +License and any other pertinent obligations, then as a consequence you +may not distribute the Program at all. For example, if a patent +license would not permit royalty-free redistribution of the Program by +all those who receive copies directly or indirectly through you, then +the only way you could satisfy both it and this License would be to +refrain entirely from distribution of the Program. + +If any portion of this section is held invalid or unenforceable under +any particular circumstance, the balance of the section is intended to +apply and the section as a whole is intended to apply in other +circumstances. + +It is not the purpose of this section to induce you to infringe any +patents or other property right claims or to contest validity of any +such claims; this section has the sole purpose of protecting the +integrity of the free software distribution system, which is +implemented by public license practices. Many people have made +generous contributions to the wide range of software distributed +through that system in reliance on consistent application of that +system; it is up to the author/donor to decide if he or she is willing +to distribute software through any other system and a licensee cannot +impose that choice. + +This section is intended to make thoroughly clear what is believed to +be a consequence of the rest of this License. + + 8. If the distribution and/or use of the Program is restricted in +certain countries either by patents or by copyrighted interfaces, the +original copyright holder who places the Program under this License +may add an explicit geographical distribution limitation excluding +those countries, so that distribution is permitted only in or among +countries not thus excluded. In such case, this License incorporates +the limitation as if written in the body of this License. + + 9. The Free Software Foundation may publish revised and/or new versions +of the General Public License from time to time. Such new versions will +be similar in spirit to the present version, but may differ in detail to +address new problems or concerns. + +Each version is given a distinguishing version number. If the Program +specifies a version number of this License which applies to it and "any +later version", you have the option of following the terms and conditions +either of that version or of any later version published by the Free +Software Foundation. If the Program does not specify a version number of +this License, you may choose any version ever published by the Free Software +Foundation. + + 10. If you wish to incorporate parts of the Program into other free +programs whose distribution conditions are different, write to the author +to ask for permission. For software which is copyrighted by the Free +Software Foundation, write to the Free Software Foundation; we sometimes +make exceptions for this. Our decision will be guided by the two goals +of preserving the free status of all derivatives of our free software and +of promoting the sharing and reuse of software generally. + + NO WARRANTY + + 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY +FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN +OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES +PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED +OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF +MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS +TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE +PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING, +REPAIR OR CORRECTION. + + 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING +WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR +REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES, +INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING +OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED +TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY +YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER +PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE +POSSIBILITY OF SUCH DAMAGES. diff --git a/3rdparty/miniColors/MIT-LICENSE.txt b/3rdparty/miniColors/MIT-LICENSE.txt new file mode 100644 index 00000000000..ec6f7581576 --- /dev/null +++ b/3rdparty/miniColors/MIT-LICENSE.txt @@ -0,0 +1,20 @@ +Copyright (c) Cory LaViska + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/3rdparty/miniColors/css/images/colors.png b/3rdparty/miniColors/css/images/colors.png Binary files differnew file mode 100755 index 00000000000..1b4f819d8d9 --- /dev/null +++ b/3rdparty/miniColors/css/images/colors.png diff --git a/3rdparty/miniColors/css/images/trigger.png b/3rdparty/miniColors/css/images/trigger.png Binary files differnew file mode 100755 index 00000000000..8c169fd6053 --- /dev/null +++ b/3rdparty/miniColors/css/images/trigger.png diff --git a/3rdparty/miniColors/css/jquery.miniColors.css b/3rdparty/miniColors/css/jquery.miniColors.css new file mode 100755 index 00000000000..381bc1dc065 --- /dev/null +++ b/3rdparty/miniColors/css/jquery.miniColors.css @@ -0,0 +1,81 @@ +.miniColors-trigger { + height: 22px; + width: 22px; + background: url(images/trigger.png) center no-repeat; + vertical-align: middle; + margin: 0 .25em; + display: inline-block; + outline: none; +} + +.miniColors-selector { + position: absolute; + width: 175px; + height: 150px; + background: #FFF; + border: solid 1px #BBB; + -moz-box-shadow: 0 0 6px rgba(0, 0, 0, .25); + -webkit-box-shadow: 0 0 6px rgba(0, 0, 0, .25); + box-shadow: 0 0 6px rgba(0, 0, 0, .25); + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; + padding: 5px; + z-index: 999999; +} + +.miniColors-selector.black { + background: #000; + border-color: #000; +} + +.miniColors-colors { + position: absolute; + top: 5px; + left: 5px; + width: 150px; + height: 150px; + background: url(images/colors.png) right no-repeat; + cursor: crosshair; +} + +.miniColors-hues { + position: absolute; + top: 5px; + left: 160px; + width: 20px; + height: 150px; + background: url(images/colors.png) left no-repeat; + cursor: crosshair; +} + +.miniColors-colorPicker { + position: absolute; + width: 9px; + height: 9px; + border: 1px solid #fff; + -moz-border-radius: 11px; + -webkit-border-radius: 11px; + border-radius: 11px; +} +.miniColors-colorPicker-inner { + position: absolute; + top: 0; + left: 0; + width: 7px; + height: 7px; + border: 1px solid #000; + -moz-border-radius: 9px; + -webkit-border-radius: 9px; + border-radius: 9px; +} + +.miniColors-huePicker { + position: absolute; + left: -3px; + width: 24px; + height: 1px; + border: 1px solid #fff; + border-radius: 2px; + background: #000; +}
\ No newline at end of file diff --git a/3rdparty/miniColors/js/jquery.miniColors.js b/3rdparty/miniColors/js/jquery.miniColors.js new file mode 100755 index 00000000000..187db3fa84e --- /dev/null +++ b/3rdparty/miniColors/js/jquery.miniColors.js @@ -0,0 +1,580 @@ +/* + * jQuery miniColors: A small color selector + * + * Copyright 2011 Cory LaViska for A Beautiful Site, LLC. (http://abeautifulsite.net/) + * + * Dual licensed under the MIT or GPL Version 2 licenses + * +*/ +if(jQuery) (function($) { + + $.extend($.fn, { + + miniColors: function(o, data) { + + var create = function(input, o, data) { + // + // Creates a new instance of the miniColors selector + // + + // Determine initial color (defaults to white) + var color = expandHex(input.val()); + if( !color ) color = 'ffffff'; + var hsb = hex2hsb(color); + + // Create trigger + var trigger = $('<a class="miniColors-trigger" style="background-color: #' + color + '" href="#"></a>'); + trigger.insertAfter(input); + + // Set input data and update attributes + input + .addClass('miniColors') + .data('original-maxlength', input.attr('maxlength') || null) + .data('original-autocomplete', input.attr('autocomplete') || null) + .data('letterCase', 'uppercase') + .data('trigger', trigger) + .data('hsb', hsb) + .data('change', o.change ? o.change : null) + .data('close', o.close ? o.close : null) + .data('open', o.open ? o.open : null) + .attr('maxlength', 7) + .attr('autocomplete', 'off') + .val('#' + convertCase(color, o.letterCase)); + + // Handle options + if( o.readonly ) input.prop('readonly', true); + if( o.disabled ) disable(input); + + // Show selector when trigger is clicked + trigger.bind('click.miniColors', function(event) { + event.preventDefault(); + if( input.val() === '' ) input.val('#'); + show(input); + + }); + + // Show selector when input receives focus + input.bind('focus.miniColors', function(event) { + if( input.val() === '' ) input.val('#'); + show(input); + }); + + // Hide on blur + input.bind('blur.miniColors', function(event) { + var hex = expandHex( hsb2hex(input.data('hsb')) ); + input.val( hex ? '#' + convertCase(hex, input.data('letterCase')) : '' ); + }); + + // Hide when tabbing out of the input + input.bind('keydown.miniColors', function(event) { + if( event.keyCode === 9 ) hide(input); + }); + + // Update when color is typed in + input.bind('keyup.miniColors', function(event) { + setColorFromInput(input); + }); + + // Handle pasting + input.bind('paste.miniColors', function(event) { + // Short pause to wait for paste to complete + setTimeout( function() { + setColorFromInput(input); + }, 5); + }); + + }; + + var destroy = function(input) { + // + // Destroys an active instance of the miniColors selector + // + + hide(); + input = $(input); + + // Restore to original state + input.data('trigger').remove(); + input + .attr('autocomplete', input.data('original-autocomplete')) + .attr('maxlength', input.data('original-maxlength')) + .removeData() + .removeClass('miniColors') + .unbind('.miniColors'); + $(document).unbind('.miniColors'); + }; + + var enable = function(input) { + // + // Enables the input control and the selector + // + input + .prop('disabled', false) + .data('trigger') + .css('opacity', 1); + }; + + var disable = function(input) { + // + // Disables the input control and the selector + // + hide(input); + input + .prop('disabled', true) + .data('trigger') + .css('opacity', 0.5); + }; + + var show = function(input) { + // + // Shows the miniColors selector + // + if( input.prop('disabled') ) return false; + + // Hide all other instances + hide(); + + // Generate the selector + var selector = $('<div class="miniColors-selector"></div>'); + selector + .append('<div class="miniColors-colors" style="background-color: #FFF;"><div class="miniColors-colorPicker"><div class="miniColors-colorPicker-inner"></div></div>') + .append('<div class="miniColors-hues"><div class="miniColors-huePicker"></div></div>') + .css({ + top: input.is(':visible') ? input.offset().top + input.outerHeight() : input.data('trigger').offset().top + input.data('trigger').outerHeight(), + left: input.is(':visible') ? input.offset().left : input.data('trigger').offset().left, + display: 'none' + }) + .addClass( input.attr('class') ); + + // Set background for colors + var hsb = input.data('hsb'); + selector + .find('.miniColors-colors') + .css('backgroundColor', '#' + hsb2hex({ h: hsb.h, s: 100, b: 100 })); + + // Set colorPicker position + var colorPosition = input.data('colorPosition'); + if( !colorPosition ) colorPosition = getColorPositionFromHSB(hsb); + selector.find('.miniColors-colorPicker') + .css('top', colorPosition.y + 'px') + .css('left', colorPosition.x + 'px'); + + // Set huePicker position + var huePosition = input.data('huePosition'); + if( !huePosition ) huePosition = getHuePositionFromHSB(hsb); + selector.find('.miniColors-huePicker').css('top', huePosition.y + 'px'); + + // Set input data + input + .data('selector', selector) + .data('huePicker', selector.find('.miniColors-huePicker')) + .data('colorPicker', selector.find('.miniColors-colorPicker')) + .data('mousebutton', 0); + + $('BODY').append(selector); + selector.fadeIn(100); + + // Prevent text selection in IE + selector.bind('selectstart', function() { return false; }); + + $(document).bind('mousedown.miniColors touchstart.miniColors', function(event) { + + input.data('mousebutton', 1); + var testSubject = $(event.target).parents().andSelf(); + + if( testSubject.hasClass('miniColors-colors') ) { + event.preventDefault(); + input.data('moving', 'colors'); + moveColor(input, event); + } + + if( testSubject.hasClass('miniColors-hues') ) { + event.preventDefault(); + input.data('moving', 'hues'); + moveHue(input, event); + } + + if( testSubject.hasClass('miniColors-selector') ) { + event.preventDefault(); + return; + } + + if( testSubject.hasClass('miniColors') ) return; + + hide(input); + }); + + $(document) + .bind('mouseup.miniColors touchend.miniColors', function(event) { + event.preventDefault(); + input.data('mousebutton', 0).removeData('moving'); + }) + .bind('mousemove.miniColors touchmove.miniColors', function(event) { + event.preventDefault(); + if( input.data('mousebutton') === 1 ) { + if( input.data('moving') === 'colors' ) moveColor(input, event); + if( input.data('moving') === 'hues' ) moveHue(input, event); + } + }); + + // Fire open callback + if( input.data('open') ) { + input.data('open').call(input.get(0), '#' + hsb2hex(hsb), hsb2rgb(hsb)); + } + + }; + + var hide = function(input) { + + // + // Hides one or more miniColors selectors + // + + // Hide all other instances if input isn't specified + if( !input ) input = '.miniColors'; + + $(input).each( function() { + var selector = $(this).data('selector'); + $(this).removeData('selector'); + $(selector).fadeOut(100, function() { + // Fire close callback + if( input.data('close') ) { + var hsb = input.data('hsb'), hex = hsb2hex(hsb); + input.data('close').call(input.get(0), '#' + hex, hsb2rgb(hsb)); + } + $(this).remove(); + }); + }); + + $(document).unbind('.miniColors'); + + }; + + var moveColor = function(input, event) { + + var colorPicker = input.data('colorPicker'); + + colorPicker.hide(); + + var position = { + x: event.pageX, + y: event.pageY + }; + + // Touch support + if( event.originalEvent.changedTouches ) { + position.x = event.originalEvent.changedTouches[0].pageX; + position.y = event.originalEvent.changedTouches[0].pageY; + } + position.x = position.x - input.data('selector').find('.miniColors-colors').offset().left - 5; + position.y = position.y - input.data('selector').find('.miniColors-colors').offset().top - 5; + if( position.x <= -5 ) position.x = -5; + if( position.x >= 144 ) position.x = 144; + if( position.y <= -5 ) position.y = -5; + if( position.y >= 144 ) position.y = 144; + + input.data('colorPosition', position); + colorPicker.css('left', position.x).css('top', position.y).show(); + + // Calculate saturation + var s = Math.round((position.x + 5) * 0.67); + if( s < 0 ) s = 0; + if( s > 100 ) s = 100; + + // Calculate brightness + var b = 100 - Math.round((position.y + 5) * 0.67); + if( b < 0 ) b = 0; + if( b > 100 ) b = 100; + + // Update HSB values + var hsb = input.data('hsb'); + hsb.s = s; + hsb.b = b; + + // Set color + setColor(input, hsb, true); + }; + + var moveHue = function(input, event) { + + var huePicker = input.data('huePicker'); + + huePicker.hide(); + + var position = { + y: event.pageY + }; + + // Touch support + if( event.originalEvent.changedTouches ) { + position.y = event.originalEvent.changedTouches[0].pageY; + } + + position.y = position.y - input.data('selector').find('.miniColors-colors').offset().top - 1; + if( position.y <= -1 ) position.y = -1; + if( position.y >= 149 ) position.y = 149; + input.data('huePosition', position); + huePicker.css('top', position.y).show(); + + // Calculate hue + var h = Math.round((150 - position.y - 1) * 2.4); + if( h < 0 ) h = 0; + if( h > 360 ) h = 360; + + // Update HSB values + var hsb = input.data('hsb'); + hsb.h = h; + + // Set color + setColor(input, hsb, true); + + }; + + var setColor = function(input, hsb, updateInput) { + input.data('hsb', hsb); + var hex = hsb2hex(hsb); + if( updateInput ) input.val( '#' + convertCase(hex, input.data('letterCase')) ); + input.data('trigger').css('backgroundColor', '#' + hex); + if( input.data('selector') ) input.data('selector').find('.miniColors-colors').css('backgroundColor', '#' + hsb2hex({ h: hsb.h, s: 100, b: 100 })); + + // Fire change callback + if( input.data('change') ) { + if( hex === input.data('lastChange') ) return; + input.data('change').call(input.get(0), '#' + hex, hsb2rgb(hsb)); + input.data('lastChange', hex); + } + + }; + + var setColorFromInput = function(input) { + + input.val('#' + cleanHex(input.val())); + var hex = expandHex(input.val()); + if( !hex ) return false; + + // Get HSB equivalent + var hsb = hex2hsb(hex); + + // If color is the same, no change required + var currentHSB = input.data('hsb'); + if( hsb.h === currentHSB.h && hsb.s === currentHSB.s && hsb.b === currentHSB.b ) return true; + + // Set colorPicker position + var colorPosition = getColorPositionFromHSB(hsb); + var colorPicker = $(input.data('colorPicker')); + colorPicker.css('top', colorPosition.y + 'px').css('left', colorPosition.x + 'px'); + input.data('colorPosition', colorPosition); + + // Set huePosition position + var huePosition = getHuePositionFromHSB(hsb); + var huePicker = $(input.data('huePicker')); + huePicker.css('top', huePosition.y + 'px'); + input.data('huePosition', huePosition); + + setColor(input, hsb); + + return true; + + }; + + var convertCase = function(string, letterCase) { + if( letterCase === 'lowercase' ) return string.toLowerCase(); + if( letterCase === 'uppercase' ) return string.toUpperCase(); + return string; + }; + + var getColorPositionFromHSB = function(hsb) { + var x = Math.ceil(hsb.s / 0.67); + if( x < 0 ) x = 0; + if( x > 150 ) x = 150; + var y = 150 - Math.ceil(hsb.b / 0.67); + if( y < 0 ) y = 0; + if( y > 150 ) y = 150; + return { x: x - 5, y: y - 5 }; + }; + + var getHuePositionFromHSB = function(hsb) { + var y = 150 - (hsb.h / 2.4); + if( y < 0 ) h = 0; + if( y > 150 ) h = 150; + return { y: y - 1 }; + }; + + var cleanHex = function(hex) { + return hex.replace(/[^A-F0-9]/ig, ''); + }; + + var expandHex = function(hex) { + hex = cleanHex(hex); + if( !hex ) return null; + if( hex.length === 3 ) hex = hex[0] + hex[0] + hex[1] + hex[1] + hex[2] + hex[2]; + return hex.length === 6 ? hex : null; + }; + + var hsb2rgb = function(hsb) { + var rgb = {}; + var h = Math.round(hsb.h); + var s = Math.round(hsb.s*255/100); + var v = Math.round(hsb.b*255/100); + if(s === 0) { + rgb.r = rgb.g = rgb.b = v; + } else { + var t1 = v; + var t2 = (255 - s) * v / 255; + var t3 = (t1 - t2) * (h % 60) / 60; + if( h === 360 ) h = 0; + if( h < 60 ) { rgb.r = t1; rgb.b = t2; rgb.g = t2 + t3; } + else if( h < 120 ) {rgb.g = t1; rgb.b = t2; rgb.r = t1 - t3; } + else if( h < 180 ) {rgb.g = t1; rgb.r = t2; rgb.b = t2 + t3; } + else if( h < 240 ) {rgb.b = t1; rgb.r = t2; rgb.g = t1 - t3; } + else if( h < 300 ) {rgb.b = t1; rgb.g = t2; rgb.r = t2 + t3; } + else if( h < 360 ) {rgb.r = t1; rgb.g = t2; rgb.b = t1 - t3; } + else { rgb.r = 0; rgb.g = 0; rgb.b = 0; } + } + return { + r: Math.round(rgb.r), + g: Math.round(rgb.g), + b: Math.round(rgb.b) + }; + }; + + var rgb2hex = function(rgb) { + var hex = [ + rgb.r.toString(16), + rgb.g.toString(16), + rgb.b.toString(16) + ]; + $.each(hex, function(nr, val) { + if (val.length === 1) hex[nr] = '0' + val; + }); + return hex.join(''); + }; + + var hex2rgb = function(hex) { + hex = parseInt(((hex.indexOf('#') > -1) ? hex.substring(1) : hex), 16); + + return { + r: hex >> 16, + g: (hex & 0x00FF00) >> 8, + b: (hex & 0x0000FF) + }; + }; + + var rgb2hsb = function(rgb) { + var hsb = { h: 0, s: 0, b: 0 }; + var min = Math.min(rgb.r, rgb.g, rgb.b); + var max = Math.max(rgb.r, rgb.g, rgb.b); + var delta = max - min; + hsb.b = max; + hsb.s = max !== 0 ? 255 * delta / max : 0; + if( hsb.s !== 0 ) { + if( rgb.r === max ) { + hsb.h = (rgb.g - rgb.b) / delta; + } else if( rgb.g === max ) { + hsb.h = 2 + (rgb.b - rgb.r) / delta; + } else { + hsb.h = 4 + (rgb.r - rgb.g) / delta; + } + } else { + hsb.h = -1; + } + hsb.h *= 60; + if( hsb.h < 0 ) { + hsb.h += 360; + } + hsb.s *= 100/255; + hsb.b *= 100/255; + return hsb; + }; + + var hex2hsb = function(hex) { + var hsb = rgb2hsb(hex2rgb(hex)); + // Zero out hue marker for black, white, and grays (saturation === 0) + if( hsb.s === 0 ) hsb.h = 360; + return hsb; + }; + + var hsb2hex = function(hsb) { + return rgb2hex(hsb2rgb(hsb)); + }; + + + // Handle calls to $([selector]).miniColors() + switch(o) { + + case 'readonly': + + $(this).each( function() { + if( !$(this).hasClass('miniColors') ) return; + $(this).prop('readonly', data); + }); + + return $(this); + + case 'disabled': + + $(this).each( function() { + if( !$(this).hasClass('miniColors') ) return; + if( data ) { + disable($(this)); + } else { + enable($(this)); + } + }); + + return $(this); + + case 'value': + + // Getter + if( data === undefined ) { + if( !$(this).hasClass('miniColors') ) return; + var input = $(this), + hex = expandHex(input.val()); + return hex ? '#' + convertCase(hex, input.data('letterCase')) : null; + } + + // Setter + $(this).each( function() { + if( !$(this).hasClass('miniColors') ) return; + $(this).val(data); + setColorFromInput($(this)); + }); + + return $(this); + + case 'destroy': + + $(this).each( function() { + if( !$(this).hasClass('miniColors') ) return; + destroy($(this)); + }); + + return $(this); + + default: + + if( !o ) o = {}; + + $(this).each( function() { + + // Must be called on an input element + if( $(this)[0].tagName.toLowerCase() !== 'input' ) return; + + // If a trigger is present, the control was already created + if( $(this).data('trigger') ) return; + + // Create the control + create($(this), o, data); + + }); + + return $(this); + + } + + } + + }); + +})(jQuery);
\ No newline at end of file diff --git a/3rdparty/miniColors/js/jquery.miniColors.min.js b/3rdparty/miniColors/js/jquery.miniColors.min.js new file mode 100755 index 00000000000..c00e0ace6b5 --- /dev/null +++ b/3rdparty/miniColors/js/jquery.miniColors.min.js @@ -0,0 +1,9 @@ +/* + * jQuery miniColors: A small color selector + * + * Copyright 2011 Cory LaViska for A Beautiful Site, LLC. (http://abeautifulsite.net/) + * + * Dual licensed under the MIT or GPL Version 2 licenses + * +*/ +if(jQuery)(function($){$.extend($.fn,{miniColors:function(o,data){var create=function(input,o,data){var color=expandHex(input.val());if(!color)color='ffffff';var hsb=hex2hsb(color);var trigger=$('<a class="miniColors-trigger" style="background-color: #'+color+'" href="#"></a>');trigger.insertAfter(input);input.addClass('miniColors').data('original-maxlength',input.attr('maxlength')||null).data('original-autocomplete',input.attr('autocomplete')||null).data('letterCase','uppercase').data('trigger',trigger).data('hsb',hsb).data('change',o.change?o.change:null).data('close',o.close?o.close:null).data('open',o.open?o.open:null).attr('maxlength',7).attr('autocomplete','off').val('#'+convertCase(color,o.letterCase));if(o.readonly)input.prop('readonly',true);if(o.disabled)disable(input);trigger.bind('click.miniColors',function(event){event.preventDefault();if(input.val()==='')input.val('#');show(input)});input.bind('focus.miniColors',function(event){if(input.val()==='')input.val('#');show(input)});input.bind('blur.miniColors',function(event){var hex=expandHex(hsb2hex(input.data('hsb')));input.val(hex?'#'+convertCase(hex,input.data('letterCase')):'')});input.bind('keydown.miniColors',function(event){if(event.keyCode===9)hide(input)});input.bind('keyup.miniColors',function(event){setColorFromInput(input)});input.bind('paste.miniColors',function(event){setTimeout(function(){setColorFromInput(input)},5)})};var destroy=function(input){hide();input=$(input);input.data('trigger').remove();input.attr('autocomplete',input.data('original-autocomplete')).attr('maxlength',input.data('original-maxlength')).removeData().removeClass('miniColors').unbind('.miniColors');$(document).unbind('.miniColors')};var enable=function(input){input.prop('disabled',false).data('trigger').css('opacity',1)};var disable=function(input){hide(input);input.prop('disabled',true).data('trigger').css('opacity',0.5)};var show=function(input){if(input.prop('disabled'))return false;hide();var selector=$('<div class="miniColors-selector"></div>');selector.append('<div class="miniColors-colors" style="background-color: #FFF;"><div class="miniColors-colorPicker"><div class="miniColors-colorPicker-inner"></div></div>').append('<div class="miniColors-hues"><div class="miniColors-huePicker"></div></div>').css({top:input.is(':visible')?input.offset().top+input.outerHeight():input.data('trigger').offset().top+input.data('trigger').outerHeight(),left:input.is(':visible')?input.offset().left:input.data('trigger').offset().left,display:'none'}).addClass(input.attr('class'));var hsb=input.data('hsb');selector.find('.miniColors-colors').css('backgroundColor','#'+hsb2hex({h:hsb.h,s:100,b:100}));var colorPosition=input.data('colorPosition');if(!colorPosition)colorPosition=getColorPositionFromHSB(hsb);selector.find('.miniColors-colorPicker').css('top',colorPosition.y+'px').css('left',colorPosition.x+'px');var huePosition=input.data('huePosition');if(!huePosition)huePosition=getHuePositionFromHSB(hsb);selector.find('.miniColors-huePicker').css('top',huePosition.y+'px');input.data('selector',selector).data('huePicker',selector.find('.miniColors-huePicker')).data('colorPicker',selector.find('.miniColors-colorPicker')).data('mousebutton',0);$('BODY').append(selector);selector.fadeIn(100);selector.bind('selectstart',function(){return false});$(document).bind('mousedown.miniColors touchstart.miniColors',function(event){input.data('mousebutton',1);var testSubject=$(event.target).parents().andSelf();if(testSubject.hasClass('miniColors-colors')){event.preventDefault();input.data('moving','colors');moveColor(input,event)}if(testSubject.hasClass('miniColors-hues')){event.preventDefault();input.data('moving','hues');moveHue(input,event)}if(testSubject.hasClass('miniColors-selector')){event.preventDefault();return}if(testSubject.hasClass('miniColors'))return;hide(input)});$(document).bind('mouseup.miniColors touchend.miniColors',function(event){event.preventDefault();input.data('mousebutton',0).removeData('moving')}).bind('mousemove.miniColors touchmove.miniColors',function(event){event.preventDefault();if(input.data('mousebutton')===1){if(input.data('moving')==='colors')moveColor(input,event);if(input.data('moving')==='hues')moveHue(input,event)}});if(input.data('open')){input.data('open').call(input.get(0),'#'+hsb2hex(hsb),hsb2rgb(hsb))}};var hide=function(input){if(!input)input='.miniColors';$(input).each(function(){var selector=$(this).data('selector');$(this).removeData('selector');$(selector).fadeOut(100,function(){if(input.data('close')){var hsb=input.data('hsb'),hex=hsb2hex(hsb);input.data('close').call(input.get(0),'#'+hex,hsb2rgb(hsb))}$(this).remove()})});$(document).unbind('.miniColors')};var moveColor=function(input,event){var colorPicker=input.data('colorPicker');colorPicker.hide();var position={x:event.pageX,y:event.pageY};if(event.originalEvent.changedTouches){position.x=event.originalEvent.changedTouches[0].pageX;position.y=event.originalEvent.changedTouches[0].pageY}position.x=position.x-input.data('selector').find('.miniColors-colors').offset().left-5;position.y=position.y-input.data('selector').find('.miniColors-colors').offset().top-5;if(position.x<=-5)position.x=-5;if(position.x>=144)position.x=144;if(position.y<=-5)position.y=-5;if(position.y>=144)position.y=144;input.data('colorPosition',position);colorPicker.css('left',position.x).css('top',position.y).show();var s=Math.round((position.x+5)*0.67);if(s<0)s=0;if(s>100)s=100;var b=100-Math.round((position.y+5)*0.67);if(b<0)b=0;if(b>100)b=100;var hsb=input.data('hsb');hsb.s=s;hsb.b=b;setColor(input,hsb,true)};var moveHue=function(input,event){var huePicker=input.data('huePicker');huePicker.hide();var position={y:event.pageY};if(event.originalEvent.changedTouches){position.y=event.originalEvent.changedTouches[0].pageY}position.y=position.y-input.data('selector').find('.miniColors-colors').offset().top-1;if(position.y<=-1)position.y=-1;if(position.y>=149)position.y=149;input.data('huePosition',position);huePicker.css('top',position.y).show();var h=Math.round((150-position.y-1)*2.4);if(h<0)h=0;if(h>360)h=360;var hsb=input.data('hsb');hsb.h=h;setColor(input,hsb,true)};var setColor=function(input,hsb,updateInput){input.data('hsb',hsb);var hex=hsb2hex(hsb);if(updateInput)input.val('#'+convertCase(hex,input.data('letterCase')));input.data('trigger').css('backgroundColor','#'+hex);if(input.data('selector'))input.data('selector').find('.miniColors-colors').css('backgroundColor','#'+hsb2hex({h:hsb.h,s:100,b:100}));if(input.data('change')){if(hex===input.data('lastChange'))return;input.data('change').call(input.get(0),'#'+hex,hsb2rgb(hsb));input.data('lastChange',hex)}};var setColorFromInput=function(input){input.val('#'+cleanHex(input.val()));var hex=expandHex(input.val());if(!hex)return false;var hsb=hex2hsb(hex);var currentHSB=input.data('hsb');if(hsb.h===currentHSB.h&&hsb.s===currentHSB.s&&hsb.b===currentHSB.b)return true;var colorPosition=getColorPositionFromHSB(hsb);var colorPicker=$(input.data('colorPicker'));colorPicker.css('top',colorPosition.y+'px').css('left',colorPosition.x+'px');input.data('colorPosition',colorPosition);var huePosition=getHuePositionFromHSB(hsb);var huePicker=$(input.data('huePicker'));huePicker.css('top',huePosition.y+'px');input.data('huePosition',huePosition);setColor(input,hsb);return true};var convertCase=function(string,letterCase){if(letterCase==='lowercase')return string.toLowerCase();if(letterCase==='uppercase')return string.toUpperCase();return string};var getColorPositionFromHSB=function(hsb){var x=Math.ceil(hsb.s/0.67);if(x<0)x=0;if(x>150)x=150;var y=150-Math.ceil(hsb.b/0.67);if(y<0)y=0;if(y>150)y=150;return{x:x-5,y:y-5}};var getHuePositionFromHSB=function(hsb){var y=150-(hsb.h/2.4);if(y<0)h=0;if(y>150)h=150;return{y:y-1}};var cleanHex=function(hex){return hex.replace(/[^A-F0-9]/ig,'')};var expandHex=function(hex){hex=cleanHex(hex);if(!hex)return null;if(hex.length===3)hex=hex[0]+hex[0]+hex[1]+hex[1]+hex[2]+hex[2];return hex.length===6?hex:null};var hsb2rgb=function(hsb){var rgb={};var h=Math.round(hsb.h);var s=Math.round(hsb.s*255/100);var v=Math.round(hsb.b*255/100);if(s===0){rgb.r=rgb.g=rgb.b=v}else{var t1=v;var t2=(255-s)*v/255;var t3=(t1-t2)*(h%60)/60;if(h===360)h=0;if(h<60){rgb.r=t1;rgb.b=t2;rgb.g=t2+t3}else if(h<120){rgb.g=t1;rgb.b=t2;rgb.r=t1-t3}else if(h<180){rgb.g=t1;rgb.r=t2;rgb.b=t2+t3}else if(h<240){rgb.b=t1;rgb.r=t2;rgb.g=t1-t3}else if(h<300){rgb.b=t1;rgb.g=t2;rgb.r=t2+t3}else if(h<360){rgb.r=t1;rgb.g=t2;rgb.b=t1-t3}else{rgb.r=0;rgb.g=0;rgb.b=0}}return{r:Math.round(rgb.r),g:Math.round(rgb.g),b:Math.round(rgb.b)}};var rgb2hex=function(rgb){var hex=[rgb.r.toString(16),rgb.g.toString(16),rgb.b.toString(16)];$.each(hex,function(nr,val){if(val.length===1)hex[nr]='0'+val});return hex.join('')};var hex2rgb=function(hex){hex=parseInt(((hex.indexOf('#')>-1)?hex.substring(1):hex),16);return{r:hex>>16,g:(hex&0x00FF00)>>8,b:(hex&0x0000FF)}};var rgb2hsb=function(rgb){var hsb={h:0,s:0,b:0};var min=Math.min(rgb.r,rgb.g,rgb.b);var max=Math.max(rgb.r,rgb.g,rgb.b);var delta=max-min;hsb.b=max;hsb.s=max!==0?255*delta/max:0;if(hsb.s!==0){if(rgb.r===max){hsb.h=(rgb.g-rgb.b)/delta}else if(rgb.g===max){hsb.h=2+(rgb.b-rgb.r)/delta}else{hsb.h=4+(rgb.r-rgb.g)/delta}}else{hsb.h=-1}hsb.h*=60;if(hsb.h<0){hsb.h+=360}hsb.s*=100/255;hsb.b*=100/255;return hsb};var hex2hsb=function(hex){var hsb=rgb2hsb(hex2rgb(hex));if(hsb.s===0)hsb.h=360;return hsb};var hsb2hex=function(hsb){return rgb2hex(hsb2rgb(hsb))};switch(o){case'readonly':$(this).each(function(){if(!$(this).hasClass('miniColors'))return;$(this).prop('readonly',data)});return $(this);case'disabled':$(this).each(function(){if(!$(this).hasClass('miniColors'))return;if(data){disable($(this))}else{enable($(this))}});return $(this);case'value':if(data===undefined){if(!$(this).hasClass('miniColors'))return;var input=$(this),hex=expandHex(input.val());return hex?'#'+convertCase(hex,input.data('letterCase')):null}$(this).each(function(){if(!$(this).hasClass('miniColors'))return;$(this).val(data);setColorFromInput($(this))});return $(this);case'destroy':$(this).each(function(){if(!$(this).hasClass('miniColors'))return;destroy($(this))});return $(this);default:if(!o)o={};$(this).each(function(){if($(this)[0].tagName.toLowerCase()!=='input')return;if($(this).data('trigger'))return;create($(this),o,data)});return $(this)}}})})(jQuery);
\ No newline at end of file diff --git a/apps/user_openid/class.openid.v3.php b/3rdparty/openid/class.openid.v3.php index eeb31986659..eeb31986659 100644 --- a/apps/user_openid/class.openid.v3.php +++ b/3rdparty/openid/class.openid.v3.php diff --git a/apps/user_openid/phpmyid.php b/3rdparty/openid/phpmyid.php index 13fd31c47ca..13fd31c47ca 100644 --- a/apps/user_openid/phpmyid.php +++ b/3rdparty/openid/phpmyid.php diff --git a/apps/calendar/ajax/import/calendarcheck.php b/apps/calendar/ajax/import/calendarcheck.php new file mode 100644 index 00000000000..a91bab70573 --- /dev/null +++ b/apps/calendar/ajax/import/calendarcheck.php @@ -0,0 +1,18 @@ +<?php +/** + * Copyright (c) 2012 Georg Ehrke <ownclouddev at georgswebsite dot de> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +OCP\JSON::checkLoggedIn(); +OCP\App::checkAppEnabled('calendar'); +$calname = strip_tags($_POST['calname']); +$calendars = OC_Calendar_Calendar::allCalendars(OCP\User::getUser()); +foreach($calendars as $calendar){ + if($calendar['displayname'] == $calname){ + OCP\JSON::success(array('message'=>'exists')); + exit; + } +} +OCP\JSON::error();
\ No newline at end of file diff --git a/apps/calendar/ajax/import/dialog.php b/apps/calendar/ajax/import/dialog.php index b99c32278c4..18fe226172c 100644 --- a/apps/calendar/ajax/import/dialog.php +++ b/apps/calendar/ajax/import/dialog.php @@ -5,8 +5,6 @@ * later. * See the COPYING-README file. */ - - OCP\JSON::checkLoggedIn(); OCP\App::checkAppEnabled('calendar'); $tmpl = new OCP\Template('calendar', 'part.import'); diff --git a/apps/calendar/ajax/import/dropimport.php b/apps/calendar/ajax/import/dropimport.php index 87667d4de68..f46e7314098 100644 --- a/apps/calendar/ajax/import/dropimport.php +++ b/apps/calendar/ajax/import/dropimport.php @@ -1,73 +1,32 @@ <?php +/** + * Copyright (c) 2012 Georg Ehrke <ownclouddev@georgswebsite.de> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ $data = $_POST['data']; $data = explode(',', $data); $data = end($data); $data = base64_decode($data); OCP\JSON::checkLoggedIn(); OCP\App::checkAppEnabled('calendar'); -$nl="\r\n"; -$comps = array('VEVENT'=>true, 'VTODO'=>true, 'VJOURNAL'=>true); -$data = str_replace(array("\r","\n\n"), array("\n","\n"), $data); -$lines = explode("\n", $data); -unset($data); -$comp=$uid=$cal=false; -$cals=$uids=array(); -$i = 0; -foreach($lines as $line) { - if(strpos($line, ':')!==false) { - list($attr, $val) = explode(':', strtoupper($line)); - if ($attr == 'BEGIN' && $val == 'VCALENDAR') { - $cal = $i; - $cals[$cal] = array('first'=>$i,'last'=>$i,'end'=>$i); - } elseif ($attr =='BEGIN' && $cal!==false && isset($comps[$val])) { - $comp = $val; - $beginNo = $i; - } elseif ($attr == 'END' && $cal!==false && $val == 'VCALENDAR') { - if($comp!==false) { - unset($cals[$cal]); // corrupt calendar, unset it - } else { - $cals[$cal]['end'] = $i; - } - $comp=$uid=$cal=false; // reset calendar - } elseif ($attr == 'END' && $comp!==false && $val == $comp) { - if(! $uid) { - $uid = OC_Calendar_Object::createUID(); - } - $uids[$uid][$beginNo] = array('end'=>$i, 'cal'=>$cal); - if ($cals[$cal]['first'] == $cal) { - $cals[$cal]['first'] = $beginNo; - } - $cals[$cal]['last'] = $i; - $comp=$uid=false; // reset component - } elseif ($attr =="UID" && $comp!==false) { - list($attr, $uid) = explode(':', $line); - } - } - $i++; +$import = new OC_Calendar_Import($data); +$import->setUserID(OCP\User::getUser()); +$import->setTimeZone(OC_Calendar_App::$tz); +$import->disableProgressCache(); +if(!$import->isValid()){ + OCP\JSON::error(); + exit; } -$calendars = OC_Calendar_Calendar::allCalendars(OCP\USER::getUser(), true); -$id = $calendars[0]['id']; -foreach($uids as $uid) { - $prefix=$suffix=$content=array(); - foreach($uid as $begin=>$details) { - $cal = $details['cal']; - if(!isset($cals[$cal])) { - continue; // from corrupt/incomplete calendar - } - $cdata = $cals[$cal]; - // if we have multiple components from different calendar objects, - // we should really merge their elements (enhancement?) -- 1st one wins for now. - if(! count($prefix)) { - $prefix = array_slice($lines, $cal, $cdata['first'] - $cal); - } - if(! count($suffix)) { - $suffix = array_slice($lines, $cdata['last']+1, $cdata['end'] - $cdata['last']); - } - $content = array_merge($content, array_slice($lines, $begin, $details['end'] - $begin + 1)); - } - if(count($content)) { - $import = join($nl, array_merge($prefix, $content, $suffix)) . $nl; - OC_Calendar_Object::add($id, $import); - } -} -OCP\JSON::success();
\ No newline at end of file +$newcalendarname = strip_tags($import->createCalendarName()); +$newid = OC_Calendar_Calendar::addCalendar(OCP\User::getUser(),$newcalendarname,'VEVENT,VTODO,VJOURNAL',null,0,$import->createCalendarColor()); +$import->setCalendarID($newid); +$import->import(); +$count = $import->getCount(); +if($count == 0){ + OC_Calendar_Calendar::deleteCalendar($newid); + OCP\JSON::error(array('message'=>OC_Calendar_App::$l10n->t('The file contained either no events or all events are already saved in your calendar.'))); +}else{ + OCP\JSON::success(array('message'=>$count . ' ' . OC_Calendar_App::$l10n->t('events has been saved in the new calendar') . ' ' . $newcalendarname, 'eventSource'=>OC_Calendar_Calendar::getEventSourceInfo(OC_Calendar_Calendar::find($newid)))); +}
\ No newline at end of file diff --git a/apps/calendar/ajax/import/import.php b/apps/calendar/ajax/import/import.php index 38008af4a9d..b1dfc464d00 100644 --- a/apps/calendar/ajax/import/import.php +++ b/apps/calendar/ajax/import/import.php @@ -5,42 +5,71 @@ * later. * See the COPYING-README file. */ -//check for calendar rights or create new one -ob_start(); - OCP\JSON::checkLoggedIn(); OCP\App::checkAppEnabled('calendar'); OCP\JSON::callCheck(); session_write_close(); - -$nl="\r\n"; -$comps = array('VEVENT'=>true, 'VTODO'=>true, 'VJOURNAL'=>true); - -global $progresskey; -$progresskey = 'calendar.import-' . $_POST['progresskey']; - -if (isset($_POST['progress']) && $_POST['progress']) { - echo OC_Cache::get($progresskey); - die; +if (isset($_POST['progresskey']) && isset($_POST['getprogress'])) { + echo OCP\JSON::success(array('percent'=>OC_Cache::get($_POST['progresskey']))); + exit; } - -function writeProgress($pct) { - global $progresskey; - OC_Cache::set($progresskey, $pct, 300); -} -writeProgress('10'); $file = OC_Filesystem::file_get_contents($_POST['path'] . '/' . $_POST['file']); +if(!$file){ + OCP\JSON::error(array('error'=>'404')); +} +$import = new OC_Calendar_Import($file); +$import->setUserID(OCP\User::getUser()); +$import->setTimeZone(OC_Calendar_App::$tz); +$import->enableProgressCache(); +$import->setProgresskey($_POST['progresskey']); +if(!$import->isValid()){ + OCP\JSON::error(array('error'=>'notvalid')); + exit; +} +$newcal = false; if($_POST['method'] == 'new'){ - $id = OC_Calendar_Calendar::addCalendar(OCP\USER::getUser(), $_POST['calname']); - OC_Calendar_Calendar::setCalendarActive($id, 1); + $calendars = OC_Calendar_Calendar::allCalendars(OCP\User::getUser()); + foreach($calendars as $calendar){ + if($calendar['displayname'] == $_POST['calname']){ + $id = $calendar['id']; + $newcal = false; + break; + } + $newcal = true; + } + if($newcal){ + $id = OC_Calendar_Calendar::addCalendar(OCP\USER::getUser(), strip_tags($_POST['calname']),'VEVENT,VTODO,VJOURNAL',null,0,strip_tags($_POST['calcolor'])); + OC_Calendar_Calendar::setCalendarActive($id, 1); + } }else{ $calendar = OC_Calendar_App::getCalendar($_POST['id']); if($calendar['userid'] != OCP\USER::getUser()){ - OCP\JSON::error(); + OCP\JSON::error(array('error'=>'missingcalendarrights')); exit(); } $id = $_POST['id']; } +$import->setCalendarID($id); +try{ + $import->import(); +}catch (Exception $e) { + OCP\JSON::error(array('message'=>OC_Calendar_App::$l10n->t('Import failed'), 'debug'=>$e->getMessage())); + //write some log +} +$count = $import->getCount(); +if($count == 0){ + if($newcal){ + OC_Calendar_Calendar::deleteCalendar($id); + } + OCP\JSON::error(array('message'=>OC_Calendar_App::$l10n->t('The file contained either no events or all events are already saved in your calendar.'))); +}else{ + if($newcal){ + OCP\JSON::success(array('message'=>$count . ' ' . OC_Calendar_App::$l10n->t('events has been saved in the new calendar') . ' ' . strip_tags($_POST['calname']))); + }else{ + OCP\JSON::success(array('message'=>$count . ' ' . OC_Calendar_App::$l10n->t('events has been saved in your calendar'))); + } +} +/* //////////////////////////// Attention: following code is quite painfull !!! /////////////////////// writeProgress('20'); // normalize the newlines $file = str_replace(array("\r","\n\n"), array("\n","\n"), $file); @@ -92,7 +121,6 @@ foreach($lines as $line) { // import the calendar writeProgress('60'); foreach($uids as $uid) { - $prefix=$suffix=$content=array(); foreach($uid as $begin=>$details) { @@ -120,4 +148,4 @@ foreach($uids as $uid) { writeProgress('100'); sleep(3); OC_Cache::remove($progresskey); -OCP\JSON::success();
\ No newline at end of file +OCP\JSON::success();*/ diff --git a/apps/calendar/appinfo/app.php b/apps/calendar/appinfo/app.php index c8fccc326c3..4fdba291262 100644 --- a/apps/calendar/appinfo/app.php +++ b/apps/calendar/appinfo/app.php @@ -9,6 +9,7 @@ OC::$CLASSPATH['OC_Calendar_Repeat'] = 'apps/calendar/lib/repeat.php'; OC::$CLASSPATH['OC_Calendar_Share'] = 'apps/calendar/lib/share.php'; OC::$CLASSPATH['OC_Search_Provider_Calendar'] = 'apps/calendar/lib/search.php'; OC::$CLASSPATH['OC_Calendar_Export'] = 'apps/calendar/lib/export.php'; +OC::$CLASSPATH['OC_Calendar_Import'] = 'apps/calendar/lib/import.php'; //General Hooks OCP\Util::connectHook('OC_User', 'post_createUser', 'OC_Calendar_Hooks', 'createUser'); OCP\Util::connectHook('OC_User', 'post_deleteUser', 'OC_Calendar_Hooks', 'deleteUser'); @@ -24,6 +25,8 @@ OCP\Util::connectHook('OC_Calendar', 'deleteCalendar', 'OC_Calendar_Share', 'pos OCP\Util::addscript('calendar','loader'); OCP\Util::addscript("3rdparty", "chosen/chosen.jquery.min"); OCP\Util::addStyle("3rdparty", "chosen/chosen"); +OCP\Util::addStyle('3rdparty/miniColors', 'jquery.miniColors'); +OCP\Util::addscript('3rdparty/miniColors', 'jquery.miniColors.min'); OCP\App::addNavigationEntry( array( 'id' => 'calendar_index', 'order' => 10, diff --git a/apps/calendar/css/import.css b/apps/calendar/css/import.css new file mode 100644 index 00000000000..8abdc3aecd1 --- /dev/null +++ b/apps/calendar/css/import.css @@ -0,0 +1,14 @@ +/** + * Copyright (c) 2012 Georg Ehrke <ownclouddev at georgswebsite dot de> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +#calendar_import_newcalform, #calendar_import_mergewarning, #calendar_import_process, #calendar_import_done{display:none;} +#calendar_import_process_message, #calendar_import_status, #calendar_import_form_message, #calendar_import_mergewarning{text-align:center;} +#calendar_import_form_message{font-weight: bold;} +#calendar_import_newcalendar{width:415px;float:right;} +#calendar_import_mergewarning{clear: both;} +#calendar_import_defaultcolors{clear:both;margin: 0 auto;text-align: center;} +.calendar_import_warning{border-color: #fc3333;} +.calendar-colorpicker-color{display:inline-block;width:20px;height:5px;margin: 0 auto;cursor:pointer;border:2px solid transparent;}
\ No newline at end of file diff --git a/apps/calendar/js/calendar.js b/apps/calendar/js/calendar.js index 004b2386fb1..25311fa0df4 100644 --- a/apps/calendar/js/calendar.js +++ b/apps/calendar/js/calendar.js @@ -622,18 +622,11 @@ Calendar={ drop:function(e){ var files = e.dataTransfer.files; for(var i = 0;i < files.length;i++){ - var file = files[i] + var file = files[i]; reader = new FileReader(); reader.onload = function(event){ - if(file.type != 'text/calendar'){ - $('#notification').html('At least one file don\'t seems to be a calendar file. File skipped.'); - $('#notification').slideDown(); - window.setTimeout(function(){$('#notification').slideUp();}, 5000); - return false; - }else{ - Calendar.UI.Drop.import(event.target.result); - $('#calendar_holder').fullCalendar('refetchEvents'); - } + Calendar.UI.Drop.import(event.target.result); + $('#calendar_holder').fullCalendar('refetchEvents'); } reader.readAsDataURL(file); } @@ -641,9 +634,13 @@ Calendar={ import:function(data){ $.post(OC.filePath('calendar', 'ajax/import', 'dropimport.php'), {'data':data},function(result) { if(result.status == 'success'){ + $('#calendar_holder').fullCalendar('addEventSource', result.eventSource); + $('#notification').html(result.message); + $('#notification').slideDown(); + window.setTimeout(function(){$('#notification').slideUp();}, 5000); return true; }else{ - $('#notification').html('ownCloud wasn\'t able to import at least one file. File skipped.'); + $('#notification').html(result.message); $('#notification').slideDown(); window.setTimeout(function(){$('#notification').slideUp();}, 5000); } diff --git a/apps/calendar/js/loader.js b/apps/calendar/js/loader.js index cef95afc3aa..b28d19ab00e 100644 --- a/apps/calendar/js/loader.js +++ b/apps/calendar/js/loader.js @@ -5,77 +5,175 @@ * See the COPYING-README file. */ Calendar_Import={ - importdialog: function(filename){ - var path = $('#dir').val(); - $('body').append('<div id="calendar_import"></div>'); - $('#calendar_import').load(OC.filePath('calendar', 'ajax/import', 'dialog.php'), {filename:filename, path:path}, function(){Calendar_Import.initdialog(filename);}); + Store:{ + file: '', + path: '', + id: 0, + method: '', + calname: '', + calcolor: '', + progresskey: '', + percentage: 0 }, - initdialog: function(filename){ - $('#calendar_import_dialog').dialog({ - width : 500, - close : function() { - $(this).dialog('destroy').remove(); - $('#calendar_import').remove(); + Dialog:{ + open: function(filename){ + OC.addStyle('calendar', 'import'); + Calendar_Import.Store.file = filename; + Calendar_Import.Store.path = $('#dir').val(); + $('body').append('<div id="calendar_import"></div>'); + $('#calendar_import').load(OC.filePath('calendar', 'ajax/import', 'dialog.php'), {filename:Calendar_Import.Store.file, path:Calendar_Import.Store.path},function(){ + Calendar_Import.Dialog.init(); + }); + }, + close: function(){ + Calendar_Import.reset(); + $(this).dialog('destroy').remove(); + $('#calendar_import_dialog').remove(); + }, + init: function(){ + //init dialog + $('#calendar_import_dialog').dialog({ + width : 500, + resizable: false, + close : function() { + Calendar_Import.Dialog.close(); + } + }); + //init buttons + $('#calendar_import_done').click(function(){ + Calendar_Import.Dialog.close(); + }); + $('#calendar_import_submit').click(function(){ + Calendar_Import.Core.process(); + }); + $('#calendar_import_mergewarning').click(function(){ + $('#calendar_import_newcalendar').attr('value', $('#calendar_import_availablename').val()); + Calendar_Import.Dialog.mergewarning($('#calendar_import_newcalendar').val()); + }); + $('#calendar_import_calendar').change(function(){ + if($('#calendar_import_calendar option:selected').val() == 'newcal'){ + $('#calendar_import_newcalform').slideDown('slow'); + Calendar_Import.Dialog.mergewarning($('#calendar_import_newcalendar').val()); + }else{ + $('#calendar_import_newcalform').slideUp('slow'); + $('#calendar_import_mergewarning').slideUp('slow'); + } + }); + $('#calendar_import_newcalendar').keyup(function(){ + Calendar_Import.Dialog.mergewarning($.trim($('#calendar_import_newcalendar').val())); + }); + $('#calendar_import_newcalendar_color').miniColors({ + letterCase: 'uppercase' + }); + $('.calendar-colorpicker-color').click(function(){ + var str = $(this).attr('rel'); + str = str.substr(1); + $('#calendar_import_newcalendar_color').attr('value', str); + $(".color-picker").miniColors('value', '#' + str); + }); + //init progressbar + $('#calendar_import_progressbar').progressbar({value: Calendar_Import.Store.percentage}); + Calendar_Import.Store.progresskey = $('#calendar_import_progresskey').val(); + }, + mergewarning: function(newcalname){ + $.post(OC.filePath('calendar', 'ajax/import', 'calendarcheck.php'), {calname: newcalname}, function(data){ + if(data.message == 'exists'){ + $('#calendar_import_mergewarning').slideDown('slow'); + }else{ + $('#calendar_import_mergewarning').slideUp('slow'); + } + }); + }, + update: function(){ + if(Calendar_Import.Store.percentage == 100){ + return false; } - }); - $('#import_done_button').click(function(){ - $('#calendar_import_dialog').dialog('destroy').remove(); - $('#calendar_import').remove(); - }); - $('#progressbar').progressbar({value: 0}); - $('#startimport').click(function(){ - var filename = $('#filename').val(); - var path = $('#path').val(); - var calid = $('#calendar option:selected').val(); - if($('#calendar option:selected').val() == 'newcal'){ - var method = 'new'; - var calname = $('#newcalendar').val(); - var calname = $.trim(calname); - if(calname == ''){ - $('#newcalendar').css('background-color', '#FF2626'); - $('#newcalendar').focus(function(){ - $('#newcalendar').css('background-color', '#F8F8F8'); - }); - return false; + $.post(OC.filePath('calendar', 'ajax/import', 'import.php'), {progresskey: Calendar_Import.Store.progresskey, getprogress: true}, function(data){ + if(data.status == 'success'){ + if(data.percent == null){ + return false; + } + Calendar_Import.Store.percentage = parseInt(data.percent); + $('#calendar_import_progressbar').progressbar('option', 'value', parseInt(data.percent)); + if(data.percent < 100 ){ + window.setTimeout('Calendar_Import.Dialog.update()', 250); + }else{ + $('#calendar_import_done').css('display', 'block'); + } + }else{ + $('#calendar_import_progressbar').progressbar('option', 'value', 100); + $('#calendar_import_progressbar > div').css('background-color', '#FF2626'); + $('#calendar_import_status').html(data.message); } - }else{ - var method = 'old'; + }); + return 0; + }, + warning: function(selector){ + $(selector).addClass('calendar_import_warning'); + $(selector).focus(function(){ + $(selector).removeClass('calendar_import_warning'); + }); + } + }, + Core:{ + process: function(){ + var validation = Calendar_Import.Core.prepare(); + if(validation){ + $('#calendar_import_form').css('display', 'none'); + $('#calendar_import_process').css('display', 'block'); + $('#calendar_import_newcalendar').attr('readonly', 'readonly'); + $('#calendar_import_calendar').attr('disabled', 'disabled'); + Calendar_Import.Core.send(); + window.setTimeout('Calendar_Import.Dialog.update()', 250); } - $('#newcalendar').attr('readonly', 'readonly'); - $('#calendar').attr('disabled', 'disabled'); - var progresskey = $('#progresskey').val(); - $.post(OC.filePath('calendar', 'ajax/import', 'import.php'), {progresskey: progresskey, method: String (method), calname: String (calname), path: String (path), file: String (filename), id: String (calid)}, function(data){ + }, + send: function(){ + $.post(OC.filePath('calendar', 'ajax/import', 'import.php'), + {progresskey: Calendar_Import.Store.progresskey, method: String (Calendar_Import.Store.method), calname: String (Calendar_Import.Store.calname), path: String (Calendar_Import.Store.path), file: String (Calendar_Import.Store.file), id: String (Calendar_Import.Store.id), calcolor: String (Calendar_Import.Store.calcolor)}, function(data){ if(data.status == 'success'){ - $('#progressbar').progressbar('option', 'value', 100); - $('#import_done').css('display', 'block'); + $('#calendar_import_progressbar').progressbar('option', 'value', 100); + Calendar_Import.Store.percentage = 100; + $('#calendar_import_done').css('display', 'block'); + $('#calendar_import_status').html(data.message); + }else{ + $('#calendar_import_progressbar').progressbar('option', 'value', 100); + $('#calendar_import_progressbar > div').css('background-color', '#FF2626'); + $('#calendar_import_status').html(data.message); } }); - $('#form_container').css('display', 'none'); - $('#progressbar_container').css('display', 'block'); - window.setTimeout('Calendar_Import.getimportstatus(\'' + progresskey + '\')', 500); - }); - $('#calendar').change(function(){ - if($('#calendar option:selected').val() == 'newcal'){ - $('#newcalform').slideDown('slow'); + }, + prepare: function(){ + Calendar_Import.Store.id = $('#calendar_import_calendar option:selected').val(); + if($('#calendar_import_calendar option:selected').val() == 'newcal'){ + Calendar_Import.Store.method = 'new'; + Calendar_Import.Store.calname = $.trim($('#calendar_import_newcalendar').val()); + if(Calendar_Import.Store.calname == ''){ + Calendar_Import.Dialog.warning('#calendar_import_newcalendar'); + return false; + } + Calendar_Import.Store.calcolor = $.trim($('#calendar_import_newcalendar_color').val()); + if(Calendar_Import.Store.calcolor == ''){ + Calendar_Import.Store.calcolor = $('.calendar-colorpicker-color:first').attr('rel'); + } }else{ - $('#newcalform').slideUp('slow'); + Calendar_Import.Store.method = 'old'; } - }); + return true; + } }, - getimportstatus: function(progresskey){ - $.post(OC.filePath('calendar', 'ajax/import', 'import.php'), {progress:1,progresskey: progresskey}, function(percent){ - $('#progressbar').progressbar('option', 'value', parseInt(percent)); - if(percent < 100){ - window.setTimeout('Calendar_Import.getimportstatus(\'' + progresskey + '\')', 500); - }else{ - $('#import_done').css('display', 'block'); - } - }); + reset: function(){ + Calendar_Import.Store.file = ''; + Calendar_Import.Store.path = ''; + Calendar_Import.Store.id = 0; + Calendar_Import.Store.method = ''; + Calendar_Import.Store.calname = ''; + Calendar_Import.Store.progresskey = ''; + Calendar_Import.Store.percentage = 0; } } $(document).ready(function(){ if(typeof FileActions !== 'undefined'){ - FileActions.register('text/calendar','importcal', '', Calendar_Import.importdialog); - FileActions.setDefault('text/calendar','importcal'); + FileActions.register('text/calendar','importCalendar', '', Calendar_Import.Dialog.open); + FileActions.setDefault('text/calendar','importCalendar'); }; }); diff --git a/apps/calendar/js/settings.js b/apps/calendar/js/settings.js index 03e4217573d..60741f2b6fc 100644 --- a/apps/calendar/js/settings.js +++ b/apps/calendar/js/settings.js @@ -34,6 +34,7 @@ $(document).ready(function(){ $.getJSON(OC.filePath('calendar', 'ajax/settings', 'timeformat.php'), function(jsondata, status) { $('#' + jsondata.timeformat).attr('selected',true); $('#timeformat').chosen(); + $('#timeformat_chzn').css('width', '100px'); }); $.getJSON(OC.filePath('calendar', 'ajax/settings', 'gettimezonedetection.php'), function(jsondata, status){ if(jsondata.detection == 'true'){ @@ -43,6 +44,7 @@ $(document).ready(function(){ $.getJSON(OC.filePath('calendar', 'ajax/settings', 'getfirstday.php'), function(jsondata, status) { $('#' + jsondata.firstday).attr('selected',true); $('#firstday').chosen(); + $('#firstday_chzn').css('width', '100px'); }); $('#cleancalendarcache').click(function(){ $.getJSON(OC.filePath('calendar', 'ajax/cache', 'rescan.php'), function(){ @@ -55,7 +57,7 @@ function calendarcachecheck(){ $.getJSON(OC.filePath('calendar', 'ajax/cache', 'status.php'), function(jsondata, status) { $('#cleancalendarcache').attr('title', jsondata.l10n.text); if(jsondata.status == 'success'){ - $('#cleancalendarcache').css('background', '#90EE90'); + $('#cleancalendarcache').css('background', '#F8F8F8'); $('#cleancalendarcache').css('color', '#333'); $('#cleancalendarcache').css('text-shadow', '#fff 0 1px 0'); }else{ diff --git a/apps/calendar/lib/calendar.php b/apps/calendar/lib/calendar.php index 128b55c48e9..7778242464c 100644 --- a/apps/calendar/lib/calendar.php +++ b/apps/calendar/lib/calendar.php @@ -267,8 +267,42 @@ class OC_Calendar_Calendar{ 'url' => OCP\Util::linkTo('calendar', 'ajax/events.php').'?calendar_id='.$calendar['id'], 'backgroundColor' => $calendar['calendarcolor'], 'borderColor' => '#888', - 'textColor' => 'black', + 'textColor' => self::generateTextColor($calendar['calendarcolor']), 'cache' => true, ); } + + /* + * @brief checks if a calendar name is available for a user + * @param string $calendarname + * @param string $userid + * @return boolean + */ + public static function isCalendarNameavailable($calendarname, $userid){ + $calendars = self::allCalendars($userid); + foreach($calendars as $calendar){ + if($calendar['displayname'] == $calendarname){ + return false; + } + } + return true; + } + + /* + * @brief generates the text color for the calendar + * @param string $calendarcolor rgb calendar color code in hex format (with or without the leading #) + * (this function doesn't pay attention on the alpha value of rgba color codes) + * @return boolean + */ + public static function generateTextColor($calendarcolor){ + if(substr_count($calendarcolor, '#') == 1){ + $calendarcolor = substr($calendarcolor,1); + } + $red = hexdec(substr($calendarcolor,0,2)); + $green = hexdec(substr($calendarcolor,2,2)); + $blue = hexdec(substr($calendarcolor,2,2)); + //recommendation by W3C + $computation = ((($red * 299) + ($green * 587) + ($blue * 114)) / 1000); + return ($computation > 130)?'#000000':'#FAFAFA'; + } } diff --git a/apps/calendar/lib/import.php b/apps/calendar/lib/import.php new file mode 100644 index 00000000000..d36891cb2b9 --- /dev/null +++ b/apps/calendar/lib/import.php @@ -0,0 +1,334 @@ +<?php +/** + * Copyright (c) 2012 Georg Ehrke <ownclouddev@georgswebsite.de> + * This file is licensed under the Affero General Public License version 3 or + * later. + * See the COPYING-README file. + */ +/* + * This class does import and converts all times to the users current timezone + */ +class OC_Calendar_Import{ + /* + * @brief counts the absolute number of parsed elements + */ + private $abscount; + + /* + * @brief var saves if the percentage should be saved with OC_Cache + */ + private $cacheprogress; + + /* + * @brief Sabre_VObject_Component_VCalendar object - for documentation see http://code.google.com/p/sabredav/wiki/Sabre_VObject_Component_VCalendar + */ + private $calobject; + + /* + * @brief var counts the number of imported elements + */ + private $count; + + /* + * @brief var to check if errors happend while initialization + */ + private $error; + + /* + * @brief var saves the ical string that was submitted with the __construct function + */ + private $ical; + + /* + * @brief calendar id for import + */ + private $id; + + /* + * @brief var saves the percentage of the import's progress + */ + private $progress; + + /* + * @brief var saves the key for the percentage of the import's progress + */ + private $progresskey; + + /* + * @brief var saves the timezone the events shell converted to + */ + private $tz; + + /* + * @brief var saves the userid + */ + private $userid; + + /* + * public methods + */ + + /* + * @brief does general initialization for import object + * @param string $calendar content of ical file + * @param string $tz timezone of the user + * @return boolean + */ + public function __construct($ical){ + $this->error = null; + $this->ical = $ical; + $this->abscount = 0; + $this->count = 0; + try{ + $this->calobject = OC_VObject::parse($this->ical); + }catch(Exception $e){ + //MISSING: write some log + $this->error = true; + return false; + } + return true; + } + + /* + * @brief imports a calendar + * @return boolean + */ + public function import(){ + if(!$this->isValid()){ + return false; + } + $numofcomponents = count($this->calobject->getComponents()); + foreach($this->calobject->getComponents() as $object){ + if(!($object instanceof Sabre_VObject_Component_VEvent) && !($object instanceof Sabre_VObject_Component_VJournal) && !($object instanceof Sabre_VObject_Component_VTodo)){ + continue; + } + $dtend = OC_Calendar_Object::getDTEndFromVEvent($object); + $object->DTSTART->getDateTime()->setTimezone(new DateTimeZone($this->tz)); + $object->DTEND->setDateTime($dtend->getDateTime(), $object->DTSTART->getDateType()); + $object->DTEND->getDateTime()->setTimezone(new DateTimeZone($this->tz)); + $vcalendar = $this->createVCalendar($object->serialize()); + $insertid = OC_Calendar_Object::add($this->id, $vcalendar); + $this->abscount++; + if($this->isDuplicate($insertid)){ + OC_Calendar_Object::delete($insertid); + }else{ + $this->count++; + } + $this->updateProgress(intval(($this->abscount / $numofcomponents)*100)); + } + OC_Cache::remove($this->progresskey); + return true; + } + + /* + * @brief sets the timezone + * @return boolean + */ + public function setTimeZone($tz){ + $this->tz = $tz; + return true; + } + + /* + * @brief sets the progresskey + * @return boolean + */ + public function setProgresskey($progresskey){ + $this->progresskey = $progresskey; + return true; + } + + /* + * @brief checks if something went wrong while initialization + * @return boolean + */ + public function isValid(){ + if(is_null($this->error)){ + return true; + } + return false; + } + + /* + * @brief returns the percentage of progress + * @return integer + */ + public function getProgress(){ + return $this->progress; + } + + /* + * @brief enables the cache for the percentage of progress + * @return boolean + */ + public function enableProgressCache(){ + $this->cacheprogress = true; + return true; + } + + /* + * @brief disables the cache for the percentage of progress + * @return boolean + */ + public function disableProgressCache(){ + $this->cacheprogress = false; + return false; + } + + /* + * @brief generates a new calendar name + * @return string + */ + public function createCalendarName(){ + $calendars = OC_Calendar_Calendar::allCalendars($this->userid); + $calendarname = $guessedcalendarname = !is_null($this->guessCalendarName())?($this->guessCalendarName()):(OC_Calendar_App::$l10n->t('New Calendar')); + $i = 1; + while(!OC_Calendar_Calendar::isCalendarNameavailable($calendarname, $this->userid)){ + $calendarname = $guessedcalendarname . ' (' . $i . ')'; + $i++; + } + return $calendarname; + } + + /* + * @brief generates a new calendar color + * @return string + */ + public function createCalendarColor(){ + if(is_null($this->guessCalendarColor())){ + return '#9fc6e7'; + } + return $this->guessCalendarColor(); + } + + /* + * @brief sets the id for the calendar + * @param integer $id of the calendar + * @return boolean + */ + public function setCalendarID($id){ + $this->id = $id; + return true; + } + + /* + * @brief sets the userid to import the calendar + * @param string $id of the user + * @return boolean + */ + public function setUserID($userid){ + $this->userid = $userid; + return true; + } + + /* + * @brief returns the private + * @param string $id of the user + * @return boolean + */ + public function getCount(){ + return $this->count; + } + + /* + * private methods + */ + + /* + * @brief generates an unique ID + * @return string + */ + //private function createUID(){ + // return substr(md5(rand().time()),0,10); + //} + + /* + * @brief checks is the UID is already in use for another event + * @param string $uid uid to check + * @return boolean + */ + //private function isUIDAvailable($uid){ + // + //} + + /* + * @brief generates a proper VCalendar string + * @param string $vobject + * @return string + */ + private function createVCalendar($vobject){ + if(is_object($vobject)){ + $vobject = @$vobject->serialize(); + } + $vcalendar = "BEGIN:VCALENDAR\nVERSION:2.0\nPRODID:ownCloud Calendar " . OCP\App::getAppVersion('calendar') . "\n"; + $vcalendar .= $vobject; + $vcalendar .= "END:VCALENDAR"; + return $vcalendar; + } + + /* + * @brief checks if an event already exists in the user's calendars + * @param integer $insertid id of the new object + * @return boolean + */ + private function isDuplicate($insertid){ + $newobject = OC_Calendar_Object::find($insertid); + $stmt = OCP\DB::prepare('SELECT COUNT(*) as count FROM *PREFIX*calendar_objects WHERE objecttype=? AND startdate=? AND enddate=? AND repeating=? AND summary=? AND calendardata=?'); + $result = $stmt->execute(array($newobject['objecttype'],$newobject['startdate'],$newobject['enddate'],$newobject['repeating'],$newobject['summary'],$newobject['calendardata'])); + $result = $result->fetchRow(); + if($result['count'] >= 2){ + return true; + } + return false; + } + + /* + * @brief updates the progress var + * @param integer $percentage + * @return boolean + */ + private function updateProgress($percentage){ + $this->progress = $percentage; + if($this->cacheprogress){ + OC_Cache::set($this->progresskey, $this->progress, 300); + } + return true; + } + + /* + * public methods for (pre)rendering of X-... Attributes + */ + + /* + * @brief guesses the calendar color + * @return mixed - string or boolean + */ + public function guessCalendarColor(){ + if(!is_null($this->calobject->__get('X-APPLE-CALENDAR-COLOR'))){ + return $this->calobject->__get('X-APPLE-CALENDAR-COLOR'); + } + return null; + } + + /* + * @brief guesses the calendar description + * @return mixed - string or boolean + */ + public function guessCalendarDescription(){ + if(!is_null($this->calobject->__get('X-WR-CALDESC'))){ + return $this->calobject->__get('X-WR-CALDESC'); + } + return null; + } + + /* + * @brief guesses the calendar name + * @return mixed - string or boolean + */ + public function guessCalendarName(){ + if(!is_null($this->calobject->__get('X-WR-CALNAME'))){ + return $this->calobject->__get('X-WR-CALNAME'); + } + return null; + } +} diff --git a/apps/calendar/templates/part.import.php b/apps/calendar/templates/part.import.php index 70ff9612157..2ce3cc34239 100644 --- a/apps/calendar/templates/part.import.php +++ b/apps/calendar/templates/part.import.php @@ -1,30 +1,58 @@ -<div id="calendar_import_dialog" title="<?php echo $l->t("Import a calendar file"); ?>"> -<div id="form_container"> -<input type="hidden" id="filename" value="<?php echo $_['filename'];?>"> -<input type="hidden" id="path" value="<?php echo $_['path'];?>"> -<input type="hidden" id="progresskey" value="<?php echo rand() ?>"> -<p style="text-align:center;"><b><?php echo $l->t('Please choose the calendar'); ?></b></p> -<select style="width:100%;" id="calendar" name="calendar"> <?php +//Prerendering for iCalendar file +$file = OC_Filesystem::file_get_contents($_['path'] . '/' . $_['filename']); +if(!$file){ + OCP\JSON::error(array('error'=>'404')); +} +$import = new OC_Calendar_Import($file); +$import->setUserID(OCP\User::getUser()); +$newcalendarname = strip_tags($import->createCalendarName()); +$guessedcalendarname = strip_tags($import->guessCalendarName()); +$calendarcolor = strip_tags($import->createCalendarColor()); +//loading calendars for select box $calendar_options = OC_Calendar_Calendar::allCalendars(OCP\USER::getUser()); $calendar_options[] = array('id'=>'newcal', 'displayname'=>$l->t('create a new calendar')); -for($i = 0;$i<count($calendar_options);$i++){ - $calendar_options[$i]['displayname'] = $calendar_options[$i]['displayname']; -} -echo OCP\html_select_options($calendar_options, $calendar_options[0]['id'], array('value'=>'id', 'label'=>'displayname')); +$defaultcolors = OC_Calendar_Calendar::getCalendarColorOptions(); ?> -</select> -<div id="newcalform" style="display: none;"> - <input type="text" style="width: 97%;" placeholder="<?php echo $l->t('Name of new calendar'); ?>" id="newcalendar" name="newcalendar"> -</div> -<input type="button" value="<?php echo $l->t("Import");?>!" id="startimport"> -</div> -<div id="progressbar_container" style="display: none"> -<p style="text-align:center;"><b><?php echo $l->t('Importing calendar'); ?></b></p> -<div id="progressbar"></div> -<div id="import_done" style="display: none;"> -<p style="text-align:center;"><b><?php echo $l->t('Calendar imported successfully'); ?></b></p> -<input type="button" value="<?php echo $l->t('Close Dialog'); ?>" id="import_done_button"> +<div id="calendar_import_dialog" title="<?php echo $l->t("Import a calendar file");?>"> +<div id="calendar_import_form"> + <form> + <input type="hidden" id="calendar_import_filename" value="<?php echo $_['filename'];?>"> + <input type="hidden" id="calendar_import_path" value="<?php echo $_['path'];?>"> + <input type="hidden" id="calendar_import_progresskey" value="<?php echo rand() ?>"> + <input type="hidden" id="calendar_import_availablename" value="<?php echo $newcalendarname ?>"> + <div id="calendar_import_form_message"><?php echo $l->t('Please choose a calendar'); ?></div> + <select style="width:100%;" id="calendar_import_calendar" name="calendar_import_calendar"> + <?php + for($i = 0;$i<count($calendar_options);$i++){ + $calendar_options[$i]['displayname'] = $calendar_options[$i]['displayname']; + } + echo OCP\html_select_options($calendar_options, $calendar_options[0]['id'], array('value'=>'id', 'label'=>'displayname')); + ?> + </select> + <br><br> + <div id="calendar_import_newcalform"> + <input id="calendar_import_newcalendar_color" class="color-picker" type="hidden" size="6" value="<?php echo substr($calendarcolor,1); ?>"> + <input id="calendar_import_newcalendar" class="" type="text" placeholder="<?php echo $l->t('Name of new calendar'); ?>" value="<?php echo $guessedcalendarname ?>"><br> + <div id="calendar_import_defaultcolors"> + <?php + foreach($defaultcolors as $color){ + echo '<span class="calendar-colorpicker-color" rel="' . $color . '" style="background-color: ' . $color . ';"></span>'; + } + ?> + </div> + <!--<input id="calendar_import_generatename" type="button" class="button" value="<?php echo $l->t('Take an available name!'); ?>"><br>--> + <div id="calendar_import_mergewarning" class="hint"><?php echo $l->t('A Calendar with this name already exists. If you continue anyhow, these calendars will be merged.'); ?></div> + </div> + <input id="calendar_import_submit" type="button" class="button" value="» <?php echo $l->t('Import'); ?> »" id="startimport"> + <form> </div> +<div id="calendar_import_process"> + <div id="calendar_import_process_message"></div> + <div id="calendar_import_progressbar"></div> + <br> + <div id="calendar_import_status" class="hint"></div> + <br> + <input id="calendar_import_done" type="button" value="<?php echo $l->t('Close Dialog'); ?>"> </div> </div>
\ No newline at end of file diff --git a/apps/contacts/ajax/loadphoto.php b/apps/contacts/ajax/loadphoto.php deleted file mode 100644 index be924b5db4d..00000000000 --- a/apps/contacts/ajax/loadphoto.php +++ /dev/null @@ -1,46 +0,0 @@ -<?php -/** - * ownCloud - Addressbook - * - * @author Thomas Tanghus - * @copyright 2012 Thomas Tanghus <thomas@tanghus.net> - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE - * License as published by the Free Software Foundation; either - * version 3 of the License, or any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - * GNU AFFERO GENERAL PUBLIC LICENSE for more details. - * - * You should have received a copy of the GNU Affero General Public - * License along with this library. If not, see <http://www.gnu.org/licenses/>. - * - */ - -// Check if we are a user -OCP\JSON::checkLoggedIn(); -OCP\JSON::checkAppEnabled('contacts'); - -require_once 'loghandler.php'; - -$id = isset($_GET['id']) ? $_GET['id'] : ''; -$refresh = isset($_GET['refresh']) ? true : false; - -if($id == '') { - bailOut(OC_Contacts_App::$l10n->t('Missing contact id.')); -} - -$checksum = ''; -$vcard = OC_Contacts_App::getContactVCard( $id ); -foreach($vcard->children as $property){ - if($property->name == 'PHOTO') { - $checksum = md5($property->serialize()); - break; - } -} - -OCP\JSON::success(array('data' => array('checksum'=>$checksum))); - diff --git a/apps/contacts/css/contacts.css b/apps/contacts/css/contacts.css index 927e7309807..ddae27da211 100644 --- a/apps/contacts/css/contacts.css +++ b/apps/contacts/css/contacts.css @@ -125,3 +125,12 @@ input[type="checkbox"] { width: 20px; height: 20px; vertical-align: bottom; } .typelist[type="button"] { float: left; max-width: 10em; border: 0; background-color: #fff; color: #bbb} /* for multiselect */ .typelist[type="button"]:hover { color: #777; } /* for multiselect */ .addresslist { clear: both; font-weight: bold; } +#ninjahelp { position: absolute; bottom: 0; left: 0; right: 0; padding: 1em; margin: 1em; border: thin solid #eee; border-radius: 5px; background-color: #DBDBDB; opacity: 0.9; } +#ninjahelp .close { position: absolute; top: 5px; right: 5px; height: 20px; width: 20px; } +#ninjahelp h2, .help-section h3 { width: 100%; font-weight: bold; text-align: center; } +#ninjahelp h2 { font-size: 1.4em; } +.help-section { width: 45%; min-width: 35em; float: left; } +.help-section h3 { font-size: 1.2em; } +.help-section dl { width: 100%; float: left; clear: right; margin: 0; padding: 0; cursor: normal; } +.help-section dt { display: table-cell; clear: left; float: left; width: 35%; margin: 0; padding: 0.2em; text-align: right; text-overflow: ellipsis; vertical-align: text-bottom; font-weight: bold: } +.help-section dd { display: table-cell; clear: right; float: left; margin: 0; padding: 0.2em; white-space: nowrap; vertical-align: text-bottom; } diff --git a/apps/contacts/js/contacts.js b/apps/contacts/js/contacts.js index 8d881498768..4c6c8bf3d93 100644 --- a/apps/contacts/js/contacts.js +++ b/apps/contacts/js/contacts.js @@ -12,10 +12,42 @@ String.prototype.strip_tags = function(){ Contacts={ UI:{ + /** + * Arguments: + * message: The text message to show. The only mandatory parameter. + * timeout: The timeout in seconds before the notification disappears. Default 10. + * timeouthandler: A function to run on timeout. + * clickhandler: A function to run on click. If a timeouthandler is given it will be cancelled. + * data: An object that will be passed as argument to the timeouthandler and clickhandler functions. + */ notify:function(params) { - $('#notification').text(params.message); - $('#notification').fadeIn(); - setTimeout(function() {$('#notification').fadeOut();}, 10000); + self = this; + if(!self.notifier) { + self.notifier = $('#notification'); + } + self.notifier.text(params.message); + self.notifier.fadeIn(); + self.notifier.on('click', function() { $(this).fadeOut();}); + var timer = setTimeout(function() { + self.notifier.fadeOut(); + if(params.timeouthandler && $.isFunction(params.timeouthandler)) { + params.timeouthandler(self.notifier.data(dataid)); + self.notifier.off('click'); + self.notifier.removeData(dataid); + } + }, params.timeout && $.isNumeric(params.timeout) ? parseInt(params.timeout)*1000 : 10000); + var dataid = timer.toString(); + if(params.data) { + self.notifier.data(dataid, params.data); + } + if(params.clickhandler && $.isFunction(params.clickhandler)) { + self.notifier.on('click', function() { + clearTimeout(timer); + self.notifier.off('click'); + params.clickhandler(self.notifier.data(dataid)); + self.notifier.removeData(dataid); + }); + } }, notImplemented:function() { OC.dialogs.alert(t('contacts', 'Sorry, this functionality has not been implemented yet'), t('contacts', 'Not implemented')); @@ -96,7 +128,7 @@ Contacts={ $('.addresscard,.propertylist li,.propertycontainer').hover( function () { $(this).find('.globe,.mail,.delete,.edit').animate({ opacity: 1.0 }, 200, function() {}); - }, + }, function () { $(this).find('.globe,.mail,.delete,.edit').animate({ opacity: 0.1 }, 200, function() {}); } @@ -107,7 +139,7 @@ Contacts={ obj.tipsy('hide'); Contacts.UI.Card.deleteProperty(obj, 'single'); } - + var goToUrl = function(obj) { var url = Contacts.UI.propertyContainerFor(obj).find('#url').val(); if(url != '') { @@ -115,7 +147,7 @@ Contacts={ newWindow.focus(); } } - + $('#identityprops a.delete').click( function() { deleteItem($(this)) }); $('#identityprops a.delete').keydown( function() { deleteItem($(this)) }); $('#categories_value a.edit').click( function() { $(this).tipsy('hide');OCCategories.edit(); } ); @@ -140,7 +172,7 @@ Contacts={ }); $('#edit_name').click(function(){Contacts.UI.Card.editName()}); $('#edit_name').keydown(function(){Contacts.UI.Card.editName()}); - + $('#phototools li a').click(function() { $(this).tipsy('hide'); }); @@ -167,13 +199,13 @@ Contacts={ OC.dialogs.filepicker(t('contacts', 'Select photo'), Contacts.UI.Card.cloudPhotoSelected, false, 'image', true); }); /* Initialize the photo edit dialog */ - $('#edit_photo_dialog').dialog({ + $('#edit_photo_dialog').dialog({ autoOpen: false, modal: true, height: 'auto', width: 'auto' }); $('#edit_photo_dialog' ).dialog( 'option', 'buttons', [ { text: "Ok", - click: function() { + click: function() { Contacts.UI.Card.savePhoto(this); $(this).dialog('close'); } @@ -183,31 +215,27 @@ Contacts={ click: function() { $(this).dialog('close'); } } ] ); - + // Name has changed. Update it and reorder. $('#fn').change(function(){ var name = $('#fn').val().strip_tags(); var item = $('.contacts li[data-id="'+Contacts.UI.Card.id+'"]').detach(); $(item).find('a').html(name); Contacts.UI.Card.fn = name; - Contacts.UI.Contacts.insertContact({ - contactlist:$('#contacts ul[data-id="'+Contacts.UI.Card.bookid+'"]'), - contacts:$('#contacts ul[data-id="'+Contacts.UI.Card.bookid+'"] li'), - contact:item, - }); + Contacts.UI.Contacts.insertContact({contact:item}); Contacts.UI.Contacts.scrollTo(Contacts.UI.Card.id); }); - $('#contacts_deletecard').click( function() { Contacts.UI.Card.doDelete();return false;} ); - $('#contacts_deletecard').keydown( function(event) { + $('#contacts_deletecard').click( function() { Contacts.UI.Card.delayedDelete();return false;} ); + $('#contacts_deletecard').keydown( function(event) { if(event.which == 13 || event.which == 32) { - Contacts.UI.Card.doDelete(); + Contacts.UI.Card.delayedDelete(); } return false; }); $('#contacts_downloadcard').click( function() { Contacts.UI.Card.doExport();return false;} ); - $('#contacts_downloadcard').keydown( function(event) { + $('#contacts_downloadcard').keydown( function(event) { if(event.which == 13 || event.which == 32) { Contacts.UI.Card.doExport(); } @@ -222,12 +250,12 @@ Contacts={ $('#contacts_details_photo_wrapper').bind('dragover',function(event){ $(event.target).addClass('droppable'); event.stopPropagation(); - event.preventDefault(); + event.preventDefault(); }); $('#contacts_details_photo_wrapper').bind('dragleave',function(event){ $(event.target).removeClass('droppable'); //event.stopPropagation(); - //event.preventDefault(); + //event.preventDefault(); }); $('#contacts_details_photo_wrapper').bind('drop',function(event){ event.stopPropagation(); @@ -267,16 +295,6 @@ Contacts={ $('#contacts_propertymenu_dropdown a').keydown(propertyMenuItem); }, Card:{ - id:'', - fn:'', - fullname:'', - shortname:'', - famname:'', - givname:'', - addname:'', - honpre:'', - honsuf:'', - data:undefined, update:function(params) { // params {cid:int, aid:int} if(!params) { params = {}; } $('#contacts li,#contacts h3').removeClass('active'); @@ -293,10 +311,11 @@ Contacts={ newid = parseInt($('#contacts').find('li[data-bookid="'+bookid+'"]').first().data('id')); } else if(parseInt(params.cid) && !parseInt(params.aid)) { newid = parseInt(params.cid); - var listitem = $('#contacts li[data-id="'+newid+'"]'); + var listitem = Contacts.UI.Contacts.getContact(newid); //$('#contacts li[data-id="'+newid+'"]'); console.log('Is contact in list? ' + listitem.length); if(listitem.length) { - bookid = parseInt($('#contacts li[data-id="'+newid+'"]').data('bookid')); + //bookid = parseInt($('#contacts li[data-id="'+newid+'"]').data('bookid')); + bookid = parseInt(Contacts.UI.Contacts.getContact(newid).data('bookid')); } else { // contact isn't in list yet. bookid = 'unknown'; } @@ -313,10 +332,10 @@ Contacts={ if($('.contacts li').length > 0) { $.getJSON(OC.filePath('contacts', 'ajax', 'contactdetails.php'),{'id':newid},function(jsondata){ if(jsondata.status == 'success'){ - if(bookid == 'unknown') { - bookid = jsondata.data.addressbookid; + if(bookid == 'unknown') { + bookid = jsondata.data.addressbookid; var contact = Contacts.UI.Contacts.insertContact({ - contactlist:$('#contacts ul[data-id="'+bookid+'"]'), + contactlist:$('#contacts ul[data-id="'+bookid+'"]'), data:jsondata.data }); } @@ -330,7 +349,7 @@ Contacts={ }); } } - + // Make sure proper DOM is loaded. if(!$('#card').length && newid) { console.log('Loading card DOM'); @@ -384,19 +403,7 @@ Contacts={ $.getJSON(OC.filePath('contacts', 'ajax', 'contactdetails.php'),{'id':id},function(jsondata){ if(jsondata.status == 'success'){ Contacts.UI.Card.loadContact(jsondata.data, aid); - $('#contacts .active').removeClass('active'); - var item = $('<li data-id="'+jsondata.data.id+'" class="active"><a href="index.php?id='+jsondata.data.id+'" style="background: url('+OC.filePath('contacts', '', 'thumbnail.php')+'?id='+jsondata.data.id+') no-repeat scroll 0% 0% transparent;">'+Contacts.UI.Card.fn+'</a></li>'); - var added = false; - $('#contacts ul[data-id="'+aid+'"] li').each(function(){ - if ($(this).text().toLowerCase() > Contacts.UI.Card.fn.toLowerCase()) { - $(this).before(item).fadeIn('fast'); - added = true; - return false; - } - }); - if(!added) { - $('#contacts ul[data-id="'+aid+'"]').append(item); - } + var item = Contacts.UI.Contacts.insertContact({data:jsondata.data}); if(isnew) { // add some default properties Contacts.UI.Card.addProperty('EMAIL'); Contacts.UI.Card.addProperty('TEL'); @@ -415,7 +422,7 @@ Contacts={ } }); } - + if(!$('#card').length) { console.log('Loading card DOM'); $.getJSON(OC.filePath('contacts', 'ajax', 'loadcard.php'),{'requesttoken': requesttoken},function(jsondata){ @@ -432,47 +439,63 @@ Contacts={ localAddcontact(n, fn, aid, isnew); } }, - doDelete:function() { + delayedDelete:function() { + /* TODO: + $(window).unload(function() { + deleteFilesInQueue(); + }); + */ $('#contacts_deletecard').tipsy('hide'); - OC.dialogs.confirm(t('contacts', 'Are you sure you want to delete this contact?'), t('contacts', 'Warning'), function(answer) { - if(answer == true) { - $.post(OC.filePath('contacts', 'ajax', 'deletecard.php'),{'id':Contacts.UI.Card.id},function(jsondata){ - if(jsondata.status == 'success'){ - var newid = '', bookid; - var curlistitem = $('#contacts li[data-id="'+jsondata.data.id+'"]'); - var newlistitem = curlistitem.prev('li'); - if(!newlistitem) { - newlistitem = curlistitem.next('li'); - } - curlistitem.remove(); - if($(newlistitem).is('li')) { - newid = newlistitem.data('id'); - bookid = newlistitem.data('bookid'); - } - $('#rightcontent').data('id',newid); - this.id = this.fn = this.fullname = this.shortname = this.famname = this.givname = this.addname = this.honpre = this.honsuf = ''; - this.data = undefined; - - if($('.contacts li').length > 0) { // Load first in list. - Contacts.UI.Card.update({cid:newid, aid:bookid}); - } else { - // load intro page - $.getJSON(OC.filePath('contacts', 'ajax', 'loadintro.php'),{},function(jsondata){ - if(jsondata.status == 'success'){ - id = ''; - $('#rightcontent').data('id',''); - $('#rightcontent').html(jsondata.data.page); - } - else{ - OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); - } - }); - } - } - else{ - OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); - } - }); + var newid = '', bookid; + var curlistitem = Contacts.UI.Contacts.getContact(this.id); + curlistitem.removeClass('active'); + var newlistitem = curlistitem.prev('li'); + if(!newlistitem) { + newlistitem = curlistitem.next('li'); + } + curlistitem.detach(); + if($(newlistitem).is('li')) { + newid = newlistitem.data('id'); + bookid = newlistitem.data('bookid'); + } + $('#rightcontent').data('id', newid); + + with(this) { + delete id; delete fn; delete fullname; delete shortname; delete famname; + delete givname; delete addname; delete honpre; delete honsuf; delete data; + } + + if($('.contacts li').length > 0) { + Contacts.UI.Card.update({cid:newid, aid:bookid}); + } else { + // load intro page + $.getJSON(OC.filePath('contacts', 'ajax', 'loadintro.php'),{},function(jsondata){ + if(jsondata.status == 'success'){ + id = ''; + $('#rightcontent').html(jsondata.data.page).removeData('id'); + } + else{ + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); + } + }); + } + Contacts.UI.notify({ + data:curlistitem, + message:t('contacts','Click to undo deletion of "') + curlistitem.find('a').text() + '"', + timeouthandler:function(contact) { + Contacts.UI.Card.doDelete(contact.data('id')); + delete contact; + }, + clickhandler:function(contact) { + Contacts.UI.Contacts.insertContact({contact:contact}); + Contacts.UI.notify({message:t('contacts', 'Cancelled deletion of: "') + curlistitem.find('a').text() + '"'}); + } + }); + }, + doDelete:function(id) { + $.post(OC.filePath('contacts', 'ajax', 'deletecard.php'),{'id':id},function(jsondata) { + if(jsondata.status == 'error'){ + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); } }); return false; @@ -489,21 +512,22 @@ Contacts={ this.loadAddresses(); this.loadSingleProperties(); Contacts.UI.loadListHandlers(); + var note = $('#note'); if(this.data.NOTE) { - $('#note').data('checksum', this.data.NOTE[0]['checksum']); - var note = $('#note').find('textarea'); + note.data('checksum', this.data.NOTE[0]['checksum']); + var textarea = note.find('textarea'); var txt = this.data.NOTE[0]['value']; var nheight = txt.split('\n').length > 4 ? txt.split('\n').length+2 : 5; - note.css('min-height', nheight+'em'); - note.attr('rows', nheight); - note.val(txt); - $('#note').show(); - note.expandingTextarea(); + textarea.css('min-height', nheight+'em'); + textarea.attr('rows', nheight); + textarea.val(txt); + note.show(); + textarea.expandingTextarea(); $('#contacts_propertymenu_dropdown a[data-type="NOTE"]').parent().hide(); } else { - $('#note').data('checksum', ''); - $('#note').find('textarea').val(''); - $('#note').hide(); + note.removeData('checksum'); + note.find('textarea').val(''); + note.hide(); $('#contacts_propertymenu_dropdown a[data-type="NOTE"]').parent().show(); } }, @@ -524,15 +548,16 @@ Contacts={ $('#contacts_propertymenu_dropdown a[data-type="'+propname+'"]').parent().hide(); var property = this.data[propname][0]; var value = property['value'], checksum = property['checksum']; - + if(propname == 'BDAY') { var val = $.datepicker.parseDate('yy-mm-dd', value.substring(0, 10)); value = $.datepicker.formatDate('dd-mm-yy', val); } - $('#contact_identity').find('#'+propname.toLowerCase()).val(value); - $('#contact_identity').find('#'+propname.toLowerCase()+'_value').data('checksum', checksum); - $('#contact_identity').find('#'+propname.toLowerCase()+'_label').show(); - $('#contact_identity').find('#'+propname.toLowerCase()+'_value').show(); + var identcontainer = $('#contact_identity'); + identcontainer.find('#'+propname.toLowerCase()).val(value); + identcontainer.find('#'+propname.toLowerCase()+'_value').data('checksum', checksum); + identcontainer.find('#'+propname.toLowerCase()+'_label').show(); + identcontainer.find('#'+propname.toLowerCase()+'_value').show(); } else { $('#contacts_propertymenu_dropdown a[data-type="'+propname+'"]').parent().show(); } @@ -547,8 +572,12 @@ Contacts={ $(this).find('input').val(''); } }); - this.fn = ''; this.fullname = ''; this.givname = ''; this.famname = ''; this.addname = ''; this.honpre = ''; this.honsuf = ''; - var narray = undefined; + + with(this) { + delete fn; delete fullname; delete givname; delete famname; + delete addname; delete honpre; delete honsuf; + } + if(this.data.FN) { this.fn = this.data.FN[0]['value']; } @@ -589,7 +618,7 @@ Contacts={ $.each(names, function(key, value) { $('#fn_select') .append($('<option></option>') - .text(value)); + .text(value)); }); $('#fn_select').combobox('value', this.fn); $('#contact_identity').find('*[data-element="N"]').data('checksum', this.data.N[0]['checksum']); @@ -732,17 +761,9 @@ Contacts={ }, addProperty:function(type){ switch (type) { - case 'PHOTO': - this.loadPhoto(true); - $('#file_upload_form').show(); - $('#contacts_propertymenu_dropdown a[data-type="'+type+'"]').parent().hide(); - $('#file_upload_start').trigger('click'); - break; case 'NOTE': - $('#note').show(); $('#contacts_propertymenu_dropdown a[data-type="'+type+'"]').parent().hide(); - $('#note').find('textarea').expandingTextarea(); - $('#note').find('textarea').focus(); + $('#note').find('textarea').expandingTextarea().show().focus(); break; case 'EMAIL': if($('#emaillist>li').length == 1) { @@ -797,8 +818,7 @@ Contacts={ } } else { $('dl dt[data-element="'+proptype+'"],dd[data-element="'+proptype+'"]').hide(); - $('dl dd[data-element="'+proptype+'"]').data('checksum', ''); - $('dl dd[data-element="'+proptype+'"]').find('input').val(''); + $('dl dd[data-element="'+proptype+'"]').data('checksum', '').find('input').val(''); } $('#contacts_propertymenu_dropdown a[data-type="'+proptype+'"]').parent().show(); Contacts.UI.loading(obj, false); @@ -825,14 +845,14 @@ Contacts={ } } }, - editName:function(){ + editName:function() { var params = {id: this.id}; /* Initialize the name edit dialog */ - if($('#edit_name_dialog').dialog('isOpen') == true){ + if($('#edit_name_dialog').dialog('isOpen') == true) { $('#edit_name_dialog').dialog('moveToTop'); - }else{ - $.getJSON(OC.filePath('contacts', 'ajax', 'editname.php'),{id: this.id},function(jsondata){ - if(jsondata.status == 'success'){ + } else { + $.getJSON(OC.filePath('contacts', 'ajax', 'editname.php'),{id: this.id},function(jsondata) { + if(jsondata.status == 'success') { $('body').append('<div id="name_dialog"></div>'); $('#name_dialog').html(jsondata.data.page).find('#edit_name_dialog' ).dialog({ modal: true, @@ -840,7 +860,7 @@ Contacts={ title: t('contacts', 'Edit name'), height: 'auto', width: 'auto', buttons: { - 'Ok':function() { + 'Ok':function() { Contacts.UI.Card.saveName(this); $(this).dialog('close'); }, @@ -891,9 +911,9 @@ Contacts={ $.each(names, function(key, value) { $('#fn_select') .append($('<option></option>') - .text(value)); + .text(value)); }); - + if(this.id == '') { var aid = $(dlg).find('#aid').val(); Contacts.UI.Card.add(n.join(';'), $('#short').text(), aid); @@ -904,10 +924,11 @@ Contacts={ loadAddresses:function(){ $('#addresses').hide(); $('#addressdisplay dl.propertycontainer').remove(); + var addresscontainer = $('#addressdisplay'); for(var adr in this.data.ADR) { - $('#addressdisplay dl').first().clone().insertAfter($('#addressdisplay dl').last()).show(); - $('#addressdisplay dl').last().removeClass('template').addClass('propertycontainer'); - $('#addressdisplay dl').last().data('checksum', this.data.ADR[adr]['checksum']); + addresscontainer.find('dl').first().clone().insertAfter($('#addressdisplay dl').last()).show(); + addresscontainer.find('dl').last().removeClass('template').addClass('propertycontainer'); + addresscontainer.find('dl').last().data('checksum', this.data.ADR[adr]['checksum']); var adrarray = this.data.ADR[adr]['value']; var adrtxt = ''; if(adrarray[0] && adrarray[0].length > 0) { @@ -919,7 +940,7 @@ Contacts={ if(adrarray[2] && adrarray[2].length > 0) { adrtxt = adrtxt + '<li>' + adrarray[2].strip_tags() + '</li>'; } - if((adrarray[3] && adrarray[5]) && adrarray[3].length > 0 || adrarray[5].length > 0) { + if((3 in adrarray && 5 in adrarray) && adrarray[3].length > 0 || adrarray[5].length > 0) { adrtxt = adrtxt + '<li>' + adrarray[5].strip_tags() + ' ' + adrarray[3].strip_tags() + '</li>'; } if(adrarray[4] && adrarray[4].length > 0) { @@ -928,7 +949,7 @@ Contacts={ if(adrarray[6] && adrarray[6].length > 0) { adrtxt = adrtxt + '<li>' + adrarray[6].strip_tags() + '</li>'; } - $('#addressdisplay dl').last().find('.addresslist').html(adrtxt); + addresscontainer.find('dl').last().find('.addresslist').html(adrtxt); var types = new Array(); var ttypes = new Array(); for(var param in this.data.ADR[adr]['parameters']) { @@ -937,12 +958,12 @@ Contacts={ ttypes.push(this.data.ADR[adr]['parameters'][param]); } } - $('#addressdisplay dl').last().find('.adr_type_label').text(types.join('/')); - $('#addressdisplay dl').last().find('.adr_type').val(ttypes.join(',')); - $('#addressdisplay dl').last().find('.adr').val(adrarray.join(';')); - $('#addressdisplay dl').last().data('checksum', this.data.ADR[adr]['checksum']); + addresscontainer.find('dl').last().find('.adr_type_label').text(types.join('/')); + addresscontainer.find('dl').last().find('.adr_type').val(ttypes.join(',')); + addresscontainer.find('dl').last().find('.adr').val(adrarray.join(';')); + addresscontainer.find('dl').last().data('checksum', this.data.ADR[adr]['checksum']); } - if($('#addressdisplay dl').length > 1) { + if(addresscontainer.find('dl').length > 1) { $('#addresses').show(); $('#contact_communication').show(); } @@ -957,7 +978,7 @@ Contacts={ container = $('#addressdisplay dl').last(); container.removeClass('template').addClass('propertycontainer'); } else { - params['checksum'] = Contacts.UI.checksumFor(obj); + params['checksum'] = Contacts.UI.checksumFor(obj); } /* Initialize the address edit dialog */ if($('#edit_address_dialog').dialog('isOpen') == true){ @@ -987,9 +1008,6 @@ Contacts={ close : function(event, ui) { $(this).dialog('destroy').remove(); $('#address_dialog').remove(); - if(isnew) { - container.remove(); - } }, open : function(event, ui) { $( "#adr_city" ).autocomplete({ @@ -1028,7 +1046,7 @@ Contacts={ $( this ).removeClass( "ui-corner-top" ).addClass( "ui-corner-all" ); } }); - $( "#adr_country" ).autocomplete({ + $('#adr_country').autocomplete({ source: function( request, response ) { $.ajax({ url: "http://ws.geonames.org/searchJSON", @@ -1079,15 +1097,23 @@ Contacts={ saveAddress:function(dlg, obj, isnew){ if(isnew) { container = $('#addressdisplay dl').last(); - obj = $('#addressdisplay dl:last-child').find('input').first(); + obj = container.find('input').first(); } else { checksum = Contacts.UI.checksumFor(obj); container = Contacts.UI.propertyContainerFor(obj); } - var adr = new Array($(dlg).find('#adr_pobox').val().strip_tags(),$(dlg).find('#adr_extended').val().strip_tags(),$(dlg).find('#adr_street').val().strip_tags(),$(dlg).find('#adr_city').val().strip_tags(),$(dlg).find('#adr_region').val().strip_tags(),$(dlg).find('#adr_zipcode').val().strip_tags(),$(dlg).find('#adr_country').val().strip_tags()); - $(container).find('.adr').val(adr.join(';')); - $(container).find('.adr_type').val($(dlg).find('#adr_type').val()); - $(container).find('.adr_type_label').html(t('contacts',ucwords($(dlg).find('#adr_type').val().toLowerCase()))); + var adr = new Array( + $(dlg).find('#adr_pobox').val().strip_tags(), + $(dlg).find('#adr_extended').val().strip_tags(), + $(dlg).find('#adr_street').val().strip_tags(), + $(dlg).find('#adr_city').val().strip_tags(), + $(dlg).find('#adr_region').val().strip_tags(), + $(dlg).find('#adr_zipcode').val().strip_tags(), + $(dlg).find('#adr_country').val().strip_tags() + ); + container.find('.adr').val(adr.join(';')); + container.find('.adr_type').val($(dlg).find('#adr_type').val()); + container.find('.adr_type_label').html(t('contacts',ucwords($(dlg).find('#adr_type').val().toLowerCase()))); Contacts.UI.Card.saveProperty($(container).find('input').first()); var adrtxt = ''; if(adr[0].length > 0) { @@ -1108,7 +1134,7 @@ Contacts={ if(adr[6].length > 0) { adrtxt = adrtxt + '<li>' + adr[6] + '</li>'; } - $(container).find('.addresslist').html(adrtxt); + container.find('.addresslist').html(adrtxt); }, uploadPhoto:function(filelist) { if(!filelist) { @@ -1135,24 +1161,25 @@ Contacts={ form.submit(); } }, - loadPhotoHandlers:function(){ - $('#phototools li a').tipsy('hide'); - $('#phototools li a').tipsy(); + loadPhotoHandlers:function() { + var phototools = $('#phototools'); + phototools.find('li a').tipsy('hide'); + phototools.find('li a').tipsy(); if(this.data.PHOTO) { - $('#phototools .delete').click(function() { + phototools.find('.delete').click(function() { $(this).tipsy('hide'); Contacts.UI.Card.deleteProperty($('#contacts_details_photo'), 'single'); $(this).hide(); }); - $('#phototools .edit').click(function() { + phototools.find('.edit').click(function() { $(this).tipsy('hide'); Contacts.UI.Card.editCurrentPhoto(); }); - $('#phototools .delete').show(); - $('#phototools .edit').show(); + phototools.find('.delete').show(); + phototools.find('.edit').show(); } else { - $('#phototools .delete').hide(); - $('#phototools .edit').hide(); + phototools.find('.delete').hide(); + phototools.find('.edit').hide(); } }, cloudPhotoSelected:function(path){ @@ -1173,28 +1200,18 @@ Contacts={ $('#phototools li a').tipsy('hide'); var wrapper = $('#contacts_details_photo_wrapper'); wrapper.addClass('loading').addClass('wait'); - - var img = new Image(); - $(img).load(function () { + delete this.photo; + this.photo = new Image(); + $(this.photo).load(function () { $('img.contacts_details_photo').remove() - $(this).addClass('contacts_details_photo').hide(); + $(this).addClass('contacts_details_photo'); wrapper.removeClass('loading').removeClass('wait'); $(this).insertAfter($('#phototools')).fadeIn(); }).error(function () { // notify the user that the image could not be loaded - $(t('contacts','something went wrong.')).insertAfter($('#phototools')); + Contacts.UI.notify({message:t('contacts','Error loading profile picture.')}); }).attr('src', OC.linkTo('contacts', 'photo.php')+'?id='+self.id+refreshstr); - - $.getJSON(OC.filePath('contacts', 'ajax', 'loadphoto.php'),{'id':this.id, 'refresh': refresh},function(jsondata){ - if(jsondata.status == 'success'){ - $('#contacts_details_photo_wrapper').data('checksum', jsondata.data.checksum); - Contacts.UI.Card.loadPhotoHandlers(); - } - else{ - OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); - } - }); - $('#file_upload_form').show(); + this.loadPhotoHandlers() }, editCurrentPhoto:function(){ $.getJSON(OC.filePath('contacts', 'ajax', 'currentphoto.php'),{'id':this.id},function(jsondata){ @@ -1248,10 +1265,11 @@ Contacts={ }, addMail:function() { //alert('addMail'); - $('#emaillist li.template:first-child').clone(true).appendTo($('#emaillist')).show().find('a .tip').tipsy(); - $('#emaillist li.template:last-child').find('select').addClass('contacts_property'); - $('#emaillist li.template:last-child').removeClass('template').addClass('propertycontainer'); - $('#emaillist li:last-child').find('input[type="email"]').focus(); + var emaillist = $('#emaillist'); + emaillist.find('li.template:first-child').clone(true).appendTo(emaillist).show().find('a .tip').tipsy(); + emaillist.find('li.template:last-child').find('select').addClass('contacts_property'); + emaillist.find('li.template:last-child').removeClass('template').addClass('propertycontainer'); + emaillist.find('li:last-child').find('input[type="email"]').focus(); return false; }, loadMails:function() { @@ -1287,35 +1305,37 @@ Contacts={ return false; }, addPhone:function() { - $('#phonelist li.template:first-child').clone(true).appendTo($('#phonelist')); //.show(); - $('#phonelist li.template:last-child').find('select').addClass('contacts_property'); - $('#phonelist li.template:last-child').removeClass('template').addClass('propertycontainer'); - $('#phonelist li:last-child').find('input[type="text"]').focus(); - $('#phonelist li:last-child').find('select').multiselect({ + var phonelist = $('#phonelist'); + phonelist.find('li.template:first-child').clone(true).appendTo(phonelist); //.show(); + phonelist.find('li.template:last-child').find('select').addClass('contacts_property'); + phonelist.find('li.template:last-child').removeClass('template').addClass('propertycontainer'); + phonelist.find('li:last-child').find('input[type="text"]').focus(); + phonelist.find('li:last-child').find('select').multiselect({ noneSelectedText: t('contacts', 'Select type'), header: false, selectedList: 4, classes: 'typelist' }); - $('#phonelist li:last-child').show(); + phonelist.find('li:last-child').show(); return false; }, loadPhones:function() { $('#phones').hide(); $('#phonelist li.propertycontainer').remove(); + var phonelist = $('#phonelist'); for(var phone in this.data.TEL) { this.addPhone(); - $('#phonelist li:last-child').find('select').multiselect('destroy'); - $('#phonelist li:last-child').data('checksum', this.data.TEL[phone]['checksum']) - $('#phonelist li:last-child').find('input[type="text"]').val(this.data.TEL[phone]['value']); + phonelist.find('li:last-child').find('select').multiselect('destroy'); + phonelist.find('li:last-child').data('checksum', this.data.TEL[phone]['checksum']) + phonelist.find('li:last-child').find('input[type="text"]').val(this.data.TEL[phone]['value']); for(var param in this.data.TEL[phone]['parameters']) { if(param.toUpperCase() == 'PREF') { - $('#phonelist li:last-child').find('input[type="checkbox"]').attr('checked', 'checked'); + phonelist.find('li:last-child').find('input[type="checkbox"]').attr('checked', 'checked'); } else if(param.toUpperCase() == 'TYPE') { for(ptype in this.data.TEL[phone]['parameters'][param]) { var pt = this.data.TEL[phone]['parameters'][param][ptype]; - $('#phonelist li:last-child').find('select option').each(function(){ + phonelist.find('li:last-child').find('select option').each(function(){ //if ($(this).val().toUpperCase() == pt.toUpperCase()) { if ($.inArray($(this).val().toUpperCase(), pt.toUpperCase().split(',')) > -1) { $(this).attr('selected', 'selected'); @@ -1324,14 +1344,14 @@ Contacts={ } } } - $('#phonelist li:last-child').find('select').multiselect({ - noneSelectedText: t('contacts', 'Select type'), - header: false, - selectedList: 4, - classes: 'typelist' - }); + phonelist.find('li:last-child').find('select').multiselect({ + noneSelectedText: t('contacts', 'Select type'), + header: false, + selectedList: 4, + classes: 'typelist' + }); } - if($('#phonelist li').length > 1) { + if(phonelist.find('li').length > 1) { $('#phones').show(); $('#contact_communication').show(); } @@ -1430,7 +1450,7 @@ Contacts={ var displayname = $("#displayname_"+bookid).val().trim(); var active = $("#edit_active_"+bookid+":checked").length; var description = $("#description_"+bookid).val(); - + if(displayname.length == 0) { OC.dialogs.alert(t('contacts', 'Displayname cannot be empty.'), t('contacts', 'Error')); return false; @@ -1456,7 +1476,25 @@ Contacts={ } }, Contacts:{ + contacts:{}, batchnum:50, + getContact:function(id) { + if(!this.contacts[id]) { + this.contacts[id] = $('#contacts li[data-id="'+id+'"]'); + if(!this.contacts[id]) { + self = this; + $.getJSON(OC.filePath('contacts', 'ajax', 'contactdetails.php'),{'id':id},function(jsondata){ + if(jsondata.status == 'success'){ + self.contacts[id] = self.insertContact({data:jsondata.data}); + } + else{ + OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); + } + }); + } + } + return this.contacts[id]; + }, drop:function(event, ui) { var dragitem = ui.draggable, droptarget = $(this); if(dragitem.is('li')) { @@ -1473,21 +1511,12 @@ Contacts={ $.post(OC.filePath('contacts', 'ajax', 'movetoaddressbook.php'), { ids: dragitem.data('id'), aid: droptarget.data('id') }, function(jsondata){ if(jsondata.status == 'success'){ - // Do some inserting/removing/sorting magic - var name = $(dragitem).find('a').html(); - var added = false; - $(droplist).children().each(function(){ - if ($(this).text().toLowerCase() > name.toLowerCase()) { - $(this).before(dragitem.detach()); //.fadeIn('slow'); - added = true; - return false; - } - }); - if(!added) { - $(droplist).append(dragitem.detach()); - } dragitem.attr('data-bookid', droptarget.data('id')) dragitem.data('bookid', droptarget.data('id')); + Contacts.UI.Contacts.insertContact({ + contactlist:droplist, + contact:dragitem.detach() + }); Contacts.UI.Contacts.scrollTo(dragitem.data('id')); } else { OC.dialogs.alert(jsondata.data.message, t('contacts', 'Error')); @@ -1498,13 +1527,24 @@ Contacts={ alert('Dropping address books not implemented yet'); }, /** - * @params params An object with the propeties 'contactlist':a jquery object of the ul to insert into, - * 'contacts':a jquery object of all items in the list and either 'data': an object with the properties + * @params params An object with the properties 'contactlist':a jquery object of the ul to insert into, + * 'contacts':a jquery object of all items in the list and either 'data': an object with the properties * id, addressbookid and displayname or 'contact': a listitem to be inserted directly. - * If 'contacts' is defined the new contact will be inserted alphabetically into the list, otherwise - * it will be appended. + * If 'contactlist' or 'contacts' aren't defined they will be search for based in the properties in 'data'. */ insertContact:function(params) { + var id, bookid; + if(!params.contactlist) { + // FIXME: Check if contact really exists. + bookid = params.data ? params.data.addressbookid : params.contact.data('bookid'); + id = params.data ? params.data.id : params.contact.data('id'); + params.contactlist = $('#contacts ul[data-id="'+bookid+'"]'); + } + if(!params.contacts) { + bookid = params.data ? params.data.addressbookid : params.contact.data('bookid'); + id = params.data ? params.data.id : params.contact.data('id'); + params.contacts = $('#contacts ul[data-id="'+bookid+'"] li'); + } var contact = params.data ? $('<li data-id="'+params.data.id+'" data-bookid="'+params.data.addressbookid+'" role="button"><a href="'+OC.linkTo('contacts', 'index.php')+'&id='+params.data.id+'" style="background: url('+OC.filePath('contacts', '', 'thumbnail.php')+'?id='+params.data.id+') no-repeat scroll 0% 0% transparent;">'+params.data.displayname+'</a></li>') : params.contact; @@ -1522,8 +1562,24 @@ Contacts={ if(!added || !params.contacts) { params.contactlist.append(contact); } + //this.contacts[id] = contact; return contact; }, + next:function(reverse) { + // TODO: Check if we're last-child/first-child and jump to next/prev address book. + var curlistitem = $('#contacts li[data-id="'+Contacts.UI.Card.id+'"]'); + var newlistitem = reverse ? curlistitem.prev('li') : curlistitem.next('li'); + if(newlistitem) { + curlistitem.removeClass('active'); + Contacts.UI.Card.update({ + cid:newlistitem.data('id'), + aid:newlistitem.data('bookid') + }); + } + }, + previous:function() { + this.next(true); + }, // Reload the contacts list. update:function(params){ if(!params) { params = {}; } @@ -1545,7 +1601,7 @@ Contacts={ $.getJSON(OC.filePath('contacts', 'ajax', 'contacts.php'),opts,function(jsondata){ if(jsondata.status == 'success'){ var books = jsondata.data.entries; - $.each(books, function(b, book) { + $.each(books, function(b, book) { if($('#contacts h3[data-id="'+b+'"]').length == 0) { firstrun = true; if($('#contacts h3').length == 0) { @@ -1564,7 +1620,7 @@ Contacts={ if(!added) { $('#contacts').append(item); } - + } } $('#contacts h3[data-id="'+b+'"]').on('click', function(event) { @@ -1627,10 +1683,10 @@ Contacts={ }, scrollTo:function(id){ var item = $('#contacts li[data-id="'+id+'"]'); - console.log('scrollTo, found item '+id+'? ' + item.length); - if(item) { + if(item && $.isNumeric(item.offset().top)) { + console.log('scrollTo ' + parseInt(item.offset().top)); $('#contacts').animate({ - scrollTop: item.offset().top-40}, 'slow','swing'); + scrollTop: parseInt(item.offset()).top-40}, 'slow','swing'); } } } @@ -1641,23 +1697,90 @@ $(document).ready(function(){ OCCategories.changed = Contacts.UI.Card.categoriesChanged; OCCategories.app = 'contacts'; - $('#notification').click(function(){ - $('#notification').fadeOut(); + $('#chooseaddressbook').on('click keydown', Contacts.UI.Addressbooks.overview); + $('#contacts_newcontact').on('click keydown', Contacts.UI.Card.editNew); + + var ninjahelp = $('#ninjahelp'); + + ninjahelp.find('.close').on('click keydown',function() { + ninjahelp.hide(); }); - - $('#chooseaddressbook').click(Contacts.UI.Addressbooks.overview); - $('#chooseaddressbook').keydown(Contacts.UI.Addressbooks.overview); - $('#contacts_newcontact').click(Contacts.UI.Card.editNew); - $('#contacts_newcontact').keydown(Contacts.UI.Card.editNew); - - // Load a contact. + $(document).on('keyup', function(event) { + console.log(event.which + ' ' + event.target.nodeName); + if(event.target.nodeName.toUpperCase() != 'BODY' + || $('#contacts li').length == 0 + || !Contacts.UI.Card.id) { + return; + } + /** + * To add: + * (Shift)n/p: next/prev addressbook + * u (85): hide/show leftcontent + * f (70): add field + */ + switch(event.which) { + case 27: // Esc + ninjahelp.hide(); + break; + case 46: + if(event.shiftKey) { + Contacts.UI.Card.delayedDelete(); + } + break; + case 32: // space + if(event.shiftKey) { + Contacts.UI.Contacts.previous(); + break; + } + case 40: // down + case 75: // k + Contacts.UI.Contacts.next(); + break; + case 65: // a + if(event.shiftKey) { + // add addressbook + Contacts.UI.notImplemented(); + break; + } + Contacts.UI.Card.editNew(); + break; + case 38: // up + case 74: // j + Contacts.UI.Contacts.previous(); + break; + case 78: // n + // next addressbook + Contacts.UI.notImplemented(); + break; + case 13: // Enter + case 79: // o + var aid = $('#contacts h3.active').first().data('id'); + if(aid) { + $('#contacts ul[data-id="'+aid+'"]').slideToggle(300); + } + break; + case 80: // p + // prev addressbook + Contacts.UI.notImplemented(); + break; + case 82: // r + Contacts.UI.Contacts.update({cid:Contacts.UI.Card.id}); + break; + case 191: // ? + ninjahelp.toggle('fast'); + break; + } + + }); + + // Load a contact. $('.contacts').keydown(function(event) { if(event.which == 13 || event.which == 32) { $('.contacts').click(); } }); - $(document).on('click', '.contacts', function(event){ + $(document).on('click', '#contacts', function(event){ var $tgt = $(event.target); if ($tgt.is('li') || $tgt.is('a')) { var item = $tgt.is('li')?$($tgt):($tgt).parent(); @@ -1722,7 +1845,7 @@ $(document).ready(function(){ } } }; - + fileUpload.onprogress = function(e){ if (e.lengthComputable){ var _progress = Math.round((e.loaded * 100) / e.total); @@ -1752,7 +1875,7 @@ $(document).ready(function(){ $(function() { var uploadingFiles = {}, numfiles = 0, uploadedfiles = 0, retries = 0; var aid; - + $('#import_upload_start').fileupload({ dropZone: $('#contacts'), // restrict dropZone to contacts list. acceptFileTypes: /^text\/(directory|vcard|x-vcard)$/i, @@ -1886,14 +2009,15 @@ $(document).ready(function(){ $('#selectaddressbook_dialog').dialog('moveToTop'); } else { $('#dialog_holder').html(jsondata.data.page).ready(function($) { - $('#selectaddressbook_dialog').dialog({ + var select_dlg = $('#selectaddressbook_dialog'); + select_dlg.dialog({ modal: true, height: 'auto', width: 'auto', buttons: { 'Ok':function() { - aid = $('#selectaddressbook_dialog').find('input:checked').val(); + aid = select_dlg.find('input:checked').val(); if(aid == 'new') { - var displayname = $('#selectaddressbook_dialog').find('input.name').val(); - var description = $('#selectaddressbook_dialog').find('input.desc').val(); + var displayname = select_dlg.find('input.name').val(); + var description = select_dlg.find('input.desc').val(); if(!displayname.trim()) { OC.dialogs.alert(t('contacts', 'The address book name cannot be empty.'), t('contacts', 'Error')); return false; @@ -1914,7 +2038,7 @@ $(document).ready(function(){ $(this).dialog('close'); } }, - 'Cancel':function() { + 'Cancel':function() { $(this).dialog('close'); numfiles = uploadedfiles = retries = aid = 0; uploadingFiles = {}; @@ -1941,11 +2065,11 @@ $(document).ready(function(){ } if(data.dataType != 'iframe ') { $('#upload input.stop').hide(); - } + } } }) }); - + Contacts.UI.loadHandlers(); Contacts.UI.Contacts.update({cid:id}); }); diff --git a/apps/contacts/js/loader.js b/apps/contacts/js/loader.js index 577ad103064..5bca0ab7237 100644 --- a/apps/contacts/js/loader.js +++ b/apps/contacts/js/loader.js @@ -78,9 +78,9 @@ Contacts_Import={ }
$(document).ready(function(){
if(typeof FileActions !== 'undefined'){
- FileActions.register('text/vcard','importaddressbook', '', Contacts_Import.importdialog);
+ FileActions.register('text/vcard','importaddressbook', '', Contacts_Import.importdialog);
FileActions.setDefault('text/vcard','importaddressbook');
- FileActions.register('text/x-vcard','importaddressbook', '', Contacts_Import.importdialog);
+ FileActions.register('text/x-vcard','importaddressbook', '', Contacts_Import.importdialog);
FileActions.setDefault('text/x-vcard','importaddressbook');
};
});
\ No newline at end of file diff --git a/apps/contacts/templates/index.php b/apps/contacts/templates/index.php index 1bc4a195534..b2dde12684c 100644 --- a/apps/contacts/templates/index.php +++ b/apps/contacts/templates/index.php @@ -32,6 +32,38 @@ echo $this->inc('part.no_contacts'); } ?> + <div class="hidden" id="ninjahelp"> + <a class="close" tabindex="0" role="button"> + <img class="svg" src="core/img/actions/delete.svg" alt="<?php echo $l->t('Close'); ?>" /> + </a> + <h2><?php echo $l->t('Keyboard shortcuts'); ?></h2> + <div class="help-section"> + <h3><?php echo $l->t('Navigation'); ?></h3> + <dl> + <dt>j/Down/Space</dt> + <dd><?php echo $l->t('Next contact in list'); ?></dd> + <dt>k/Up/Shift-Space</dt> + <dd><?php echo $l->t('Previous contact in list'); ?></dd> + <dt>o/Enter</dt> + <dd><?php echo $l->t('Expand/collapse current addressbook'); ?></dd> + <dt>n/p</dt> + <dd><?php echo $l->t('Next/previous addressbook'); ?></dd> + </dl> + </div> + <div class="help-section"> + <h3><?php echo $l->t('Actions'); ?></h3> + <dl> + <dt>r</dt> + <dd><?php echo $l->t('Refresh contacts list'); ?></dd> + <dt>a</dt> + <dd><?php echo $l->t('Add new contact'); ?></dd> + <dt>Shift-a</dt> + <dd><?php echo $l->t('Add new addressbook'); ?></dd> + <dt>Shift-Delete</dt> + <dd><?php echo $l->t('Delete current contact'); ?></dd> + </dl> + </div> + </div> </div> <!-- Dialogs --> <div id="dialog_holder"></div> diff --git a/apps/files/ajax/newfile.php b/apps/files/ajax/newfile.php index 7236deb65c9..cc9208ad08f 100644 --- a/apps/files/ajax/newfile.php +++ b/apps/files/ajax/newfile.php @@ -1,16 +1,25 @@ <?php // Init owncloud +global $eventSource; +if(!OC_User::isLoggedIn()){ + exit; +} -OCP\JSON::checkLoggedIn(); -OCP\JSON::callCheck(); +session_write_close(); // Get the params -$dir = isset( $_POST['dir'] ) ? stripslashes($_POST['dir']) : ''; -$filename = isset( $_POST['filename'] ) ? stripslashes($_POST['filename']) : ''; -$content = isset( $_POST['content'] ) ? $_POST['content'] : ''; -$source = isset( $_POST['source'] ) ? stripslashes($_POST['source']) : ''; +$dir = isset( $_REQUEST['dir'] ) ? stripslashes($_REQUEST['dir']) : ''; +$filename = isset( $_REQUEST['filename'] ) ? stripslashes($_REQUEST['filename']) : ''; +$content = isset( $_REQUEST['content'] ) ? $_REQUEST['content'] : ''; +$source = isset( $_REQUEST['source'] ) ? stripslashes($_REQUEST['source']) : ''; + +if($source){ + $eventSource=new OC_EventSource(); +}else{ + OC_JSON::callCheck(); +} if($filename == '') { OCP\JSON::error(array("data" => array( "message" => "Empty Filename" ))); @@ -21,22 +30,49 @@ if(strpos($filename,'/')!==false){ exit(); } +function progress($notification_code, $severity, $message, $message_code, $bytes_transferred, $bytes_max){ + static $filesize = 0; + static $lastsize = 0; + global $eventSource; + + switch($notification_code) { + case STREAM_NOTIFY_FILE_SIZE_IS: + $filesize = $bytes_max; + break; + + case STREAM_NOTIFY_PROGRESS: + if ($bytes_transferred > 0) { + if (!isset($filesize)) { + } else { + $progress = (int)(($bytes_transferred/$filesize)*100); + if($progress>$lastsize){//limit the number or messages send + $eventSource->send('progress',$progress); + } + $lastsize=$progress; + } + } + break; + } +} + if($source){ if(substr($source,0,8)!='https://' and substr($source,0,7)!='http://'){ OCP\JSON::error(array("data" => array( "message" => "Not a valid source" ))); exit(); } - $sourceStream=fopen($source,'rb'); + + $ctx = stream_context_create(null, array('notification' =>'progress')); + $sourceStream=fopen($source,'rb', false, $ctx); $target=$dir.'/'.$filename; $result=OC_Filesystem::file_put_contents($target,$sourceStream); if($result){ $mime=OC_Filesystem::getMimetype($target); - OCP\JSON::success(array("data" => array('mime'=>$mime))); - exit(); + $eventSource->send('success',$mime); }else{ - OCP\JSON::error(array("data" => array( "message" => "Error while downloading ".$source. ' to '.$target ))); - exit(); + $eventSource->send('error',"Error while downloading ".$source. ' to '.$target); } + $eventSource->close(); + exit(); }else{ if($content){ if(OC_Filesystem::file_put_contents($dir.'/'.$filename,$content)){ diff --git a/apps/files/ajax/scan.php b/apps/files/ajax/scan.php index 6fcf97688c2..eef38858516 100644 --- a/apps/files/ajax/scan.php +++ b/apps/files/ajax/scan.php @@ -16,6 +16,11 @@ session_write_close(); if($force or !OC_FileCache::inCache('')){ if(!$checkOnly){ OCP\DB::beginTransaction(); + + if(OC_Cache::isFast()){ + OC_Cache::clear('fileid/'); //make sure the old fileid's don't mess things up + } + OC_FileCache::scan($dir,$eventSource); OC_FileCache::clean(); OCP\DB::commit(); diff --git a/apps/files/appinfo/update.php b/apps/files/appinfo/update.php index f9953ba4de5..5514aed197f 100644 --- a/apps/files/appinfo/update.php +++ b/apps/files/appinfo/update.php @@ -1,5 +1,16 @@ <?php +// fix webdav properties, remove namespace information between curly bracket (update from OC4 to OC5) +$installedVersion=OCP\Config::getAppValue('files', 'installed_version');
+if (version_compare($installedVersion, '1.1.4', '<')) { + $query = OC_DB::prepare( "SELECT propertyname, propertypath, userid FROM `*PREFIX*properties`" );
+ $result = $query->execute();
+ while( $row = $result->fetchRow()){
+ $query = OC_DB::prepare( 'UPDATE *PREFIX*properties SET propertyname = ? WHERE userid = ? AND propertypath = ?' );
+ $query->execute( array( preg_replace("/^{.*}/", "", $row["propertyname"]),$row["userid"], $row["propertypath"] ));
+ } +} + //update from OC 3 //try to remove remaining files. diff --git a/apps/files/appinfo/version b/apps/files/appinfo/version index 9c1218c201f..1b87bcd0b09 100644 --- a/apps/files/appinfo/version +++ b/apps/files/appinfo/version @@ -1 +1 @@ -1.1.3
\ No newline at end of file +1.1.4
\ No newline at end of file diff --git a/apps/files/js/files.js b/apps/files/js/files.js index 86c5185bf72..a4e2361feeb 100644 --- a/apps/files/js/files.js +++ b/apps/files/js/files.js @@ -497,23 +497,27 @@ $(document).ready(function() { localName=(localName.match(/:\/\/(.[^/]+)/)[1]).replace('www.',''); } localName = getUniqueName(localName); - $.post( - OC.filePath('files','ajax','newfile.php'), - {dir:$('#dir').val(),source:name,filename:localName}, - function(result){ - if(result.status == 'success'){ - var date=new Date(); - FileList.addFile(localName,0,date); - var tr=$('tr').filterAttr('data-file',localName); - tr.data('mime',result.data.mime); - getMimeIcon(result.data.mime,function(path){ - tr.find('td.filename').attr('style','background-image:url('+path+')'); - }); - }else{ + $('#uploadprogressbar').progressbar({value:0}); + $('#uploadprogressbar').fadeIn(); - } - } - ); + var eventSource=new OC.EventSource(OC.filePath('files','ajax','newfile.php'),{dir:$('#dir').val(),source:name,filename:localName}); + eventSource.listen('progress',function(progress){ + $('#uploadprogressbar').progressbar('value',progress); + }); + eventSource.listen('success',function(mime){ + $('#uploadprogressbar').fadeOut(); + var date=new Date(); + FileList.addFile(localName,0,date); + var tr=$('tr').filterAttr('data-file',localName); + tr.data('mime',mime); + getMimeIcon(mime,function(path){ + tr.find('td.filename').attr('style','background-image:url('+path+')'); + }); + }); + eventSource.listen('error',function(error){ + $('#uploadprogressbar').fadeOut(); + alert(error); + }); break; } var li=$(this).parent(); diff --git a/apps/user_migrate/templates/settings.php b/apps/user_migrate/templates/settings.php index 1718abe9e0f..bce5fb2d7ca 100644 --- a/apps/user_migrate/templates/settings.php +++ b/apps/user_migrate/templates/settings.php @@ -12,7 +12,7 @@ <?php } ?> <legend><strong><?php echo $l->t('Import user account');?></strong></legend> </p> - <p><input type="file" id="owncloud_import" name="owncloud_import" style="width:180px;"><label for="owncloud_import"> <?php echo $l->t('ownCloud User Zip');?></label> + <p><input type="file" id="owncloud_import" name="owncloud_import" style="width:280px;"><label for="owncloud_import"> <?php echo $l->t('ownCloud User Zip');?></label> </p> <input type="submit" name="user_import" value="<?php echo $l->t('Import'); ?>" /> </fieldset> diff --git a/apps/user_openid/appinfo/app.php b/apps/user_openid/appinfo/app.php index fe57b189fac..a5fb6a0f45e 100644 --- a/apps/user_openid/appinfo/app.php +++ b/apps/user_openid/appinfo/app.php @@ -19,7 +19,7 @@ OCP\Util::addHeader('link',array('rel'=>'openid.delegate', 'href'=>OCP\Util::lin OCP\App::registerPersonal('user_openid','settings'); -require_once 'apps/user_openid/user_openid.php'; +require_once 'openid/user_openid.php'; //active the openid backend OC_User::useBackend('openid'); diff --git a/apps/user_openid/user.php b/apps/user_openid/user.php index d25b95259e0..88571ba618e 100644 --- a/apps/user_openid/user.php +++ b/apps/user_openid/user.php @@ -44,4 +44,4 @@ if(!OCP\User::userExists($USERNAME)){ } $IDENTITY=OCP\Util::linkToAbsolute( "user_openid", "user.php" ).'/'.$USERNAME; -require_once 'phpmyid.php'; +require_once 'openid/phpmyid.php'; diff --git a/apps/user_openid/user_openid.php b/apps/user_openid/user_openid.php index 70b193a30b1..19f2f719b06 100644 --- a/apps/user_openid/user_openid.php +++ b/apps/user_openid/user_openid.php @@ -21,7 +21,7 @@ * */ -require_once('class.openid.v3.php'); +require_once('openid/class.openid.v3.php'); /** * Class for user OpenId backend diff --git a/core/css/styles.css b/core/css/styles.css index 338518e5b2f..b818caa279f 100644 --- a/core/css/styles.css +++ b/core/css/styles.css @@ -139,6 +139,8 @@ li.error { width:640px; margin:4em auto; padding:1em 1em 1em 4em; background:#ff a.bookmarklet { background-color: #ddd; border:1px solid #ccc; padding: 5px;padding-top: 0px;padding-bottom: 2px; text-decoration: none; margin-top: 5px } +.exception{color: #000000;} +.exception textarea{width:95%;height: 200px;background:#ffe;border:0;} /* ---- DIALOGS ---- */ #dirtree {width: 100%;} diff --git a/core/js/eventsource.js b/core/js/eventsource.js index 08259e02cae..e3ad7e3a671 100644 --- a/core/js/eventsource.js +++ b/core/js/eventsource.js @@ -40,6 +40,7 @@ OC.EventSource=function(src,data){ dataStr+=name+'='+encodeURIComponent(data[name])+'&'; } } + dataStr+='requesttoken='+OC.EventSource.requesttoken; if(!this.useFallBack && typeof EventSource !='undefined'){ this.source=new EventSource(src+'?'+dataStr); this.source.onmessage=function(e){ diff --git a/core/templates/exception.php b/core/templates/exception.php new file mode 100644 index 00000000000..7f58ce252cf --- /dev/null +++ b/core/templates/exception.php @@ -0,0 +1,30 @@ +<ul> + <li class='error'> + <details> + <summary class="error">We're sorry, but something went terribly wrong.<br></summary> + <p class="exception"> + <?php + if($_['showsysinfo'] == true){ + echo 'If you would like to support ownCloud\'s developers and report this error in our <a href="http://bugs.owncloud.org">Bugtracker</a>, please copy the following informations into the description. <br><br><textarea readonly>'; + echo 'Message: ' . $_['message'] . "\n"; + echo 'Error Code: ' . $_['code'] . "\n"; + echo 'File: ' . $_['file'] . "\n"; + echo 'Line: ' . $_['line'] . "\n\n"; + echo 'PHP: ' . $_['sysinfo']['phpversion'] . "\n"; + echo 'OS: ' . $_['sysinfo']['os'] . "\n"; + echo 'OS Release: ' . $_['sysinfo']['osrelease'] . "\n"; + echo 'OS Arch.: ' . $_['sysinfo']['osarchitecture'] . "\n"; + echo 'PHP-Server-Interface: ' . $_['sysinfo']['phpserverinterface'] . "\n"; + echo 'Protocol: ' . $_['sysinfo']['serverprotocol'] . "\n"; + echo 'HTTPS: ' . $_['sysinfo']['https'] . "\n"; + echo 'Request Method: ' . $_['sysinfo']['requestmethod'] . "\n"; + echo 'Database: ' . $_['sysinfo']['database'] . "\n"; + echo '</textarea>'; + }else{ + echo 'Your administrator has disabled systeminformations.'; + } + ?> + </p> + </details> + </li> +</ul>
\ No newline at end of file diff --git a/core/templates/layout.user.php b/core/templates/layout.user.php index 7e98fdedc2d..dc303ffc1a7 100644 --- a/core/templates/layout.user.php +++ b/core/templates/layout.user.php @@ -33,6 +33,7 @@ <script type="text/javascript"> $(function() { requesttoken = '<?php echo $_['requesttoken']; ?>'; + OC.EventSource.requesttoken=requesttoken; $(document).bind('ajaxSend', function(elm, xhr, s){ if(requesttoken) { xhr.setRequestHeader('requesttoken', requesttoken); diff --git a/lib/app.php b/lib/app.php index 2b975de9ef0..56132c08671 100755 --- a/lib/app.php +++ b/lib/app.php @@ -195,7 +195,7 @@ class OC_App{ // check if the app is compatible with this version of ownCloud $info=OC_App::getAppInfo($app); $version=OC_Util::getVersion(); - if(!isset($info['require']) or ($version[0]>$info['require'])){ + if(!isset($info['require']) or ($version[0]>$info['require'])){ OC_Log::write('core','App "'.$info['name'].'" can\'t be installed because it is not compatible with this version of ownCloud',OC_Log::ERROR); return false; }else{ @@ -331,8 +331,8 @@ class OC_App{ } /** - * Get the path where to install apps - */ + * Get the path where to install apps + */ public static function getInstallPath() { if(OC_Config::getValue('appstoreenabled', true)==false) { return false; diff --git a/lib/cache.php b/lib/cache.php index 1f269174fad..55f189a5da8 100644 --- a/lib/cache.php +++ b/lib/cache.php @@ -7,48 +7,96 @@ */ class OC_Cache { + /** + * @var OC_Cache $user_cache + */ static protected $user_cache; + /** + * @var OC_Cache $global_cache + */ static protected $global_cache; + /** + * @var OC_Cache $global_cache_fast + */ + static protected $global_cache_fast; + /** + * @var OC_Cache $user_cache_fast + */ + static protected $user_cache_fast; + static protected $isFast=null; - static public function getGlobalCache() { + /** + * get the global cache + * @return OC_Cache + */ + static public function getGlobalCache($fast=false) { if (!self::$global_cache) { - $fast_cache = null; - if (!$fast_cache && function_exists('xcache_set')) { - $fast_cache = new OC_Cache_XCache(true); + self::$global_cache_fast = null; + if (!self::$global_cache_fast && function_exists('xcache_set')) { + self::$global_cache_fast = new OC_Cache_XCache(true); } - if (!$fast_cache && function_exists('apc_store')) { - $fast_cache = new OC_Cache_APC(true); + if (!self::$global_cache_fast && function_exists('apc_store')) { + self::$global_cache_fast = new OC_Cache_APC(true); } + self::$global_cache = new OC_Cache_FileGlobal(); - if ($fast_cache) { - self::$global_cache = new OC_Cache_Broker($fast_cache, self::$global_cache); + if (self::$global_cache_fast) { + self::$global_cache = new OC_Cache_Broker(self::$global_cache_fast, self::$global_cache); + } + } + if($fast){ + if(self::$global_cache_fast){ + return self::$global_cache_fast; + }else{ + return false; } } return self::$global_cache; } - static public function getUserCache() { + /** + * get the user cache + * @return OC_Cache + */ + static public function getUserCache($fast=false) { if (!self::$user_cache) { - $fast_cache = null; - if (!$fast_cache && function_exists('xcache_set')) { - $fast_cache = new OC_Cache_XCache(); + self::$user_cache_fast = null; + if (!self::$user_cache_fast && function_exists('xcache_set')) { + self::$user_cache_fast = new OC_Cache_XCache(); } - if (!$fast_cache && function_exists('apc_store')) { - $fast_cache = new OC_Cache_APC(); + if (!self::$user_cache_fast && function_exists('apc_store')) { + self::$user_cache_fast = new OC_Cache_APC(); } + self::$user_cache = new OC_Cache_File(); - if ($fast_cache) { - self::$user_cache = new OC_Cache_Broker($fast_cache, self::$user_cache); + if (self::$user_cache_fast) { + self::$user_cache = new OC_Cache_Broker(self::$user_cache_fast, self::$user_cache); + } + } + + if($fast){ + if(self::$user_cache_fast){ + return self::$user_cache_fast; + }else{ + return false; } } return self::$user_cache; } + /** + * get a value from the user cache + * @return mixed + */ static public function get($key) { $user_cache = self::getUserCache(); return $user_cache->get($key); } + /** + * set a value in the user cache + * @return bool + */ static public function set($key, $value, $ttl=0) { if (empty($key)) { return false; @@ -57,19 +105,43 @@ class OC_Cache { return $user_cache->set($key, $value, $ttl); } + /** + * check if a value is set in the user cache + * @return bool + */ static public function hasKey($key) { $user_cache = self::getUserCache(); return $user_cache->hasKey($key); } + /** + * remove an item from the user cache + * @return bool + */ static public function remove($key) { $user_cache = self::getUserCache(); return $user_cache->remove($key); } - static public function clear() { + /** + * clear the user cache of all entries starting with a prefix + * @param string prefix (optional) + * @return bool + */ + static public function clear($prefix='') { $user_cache = self::getUserCache(); - return $user_cache->clear(); + return $user_cache->clear($prefix); + } + + /** + * check if a fast memory based cache is available + * @return true + */ + static public function isFast() { + if(is_null(self::$isFast)){ + self::$isFast=function_exists('xcache_set') || function_exists('apc_store'); + } + return self::$isFast; } } diff --git a/lib/cache/apc.php b/lib/cache/apc.php index 6cf47d0c158..c192fe2f196 100644 --- a/lib/cache/apc.php +++ b/lib/cache/apc.php @@ -43,14 +43,15 @@ class OC_Cache_APC { return apc_delete($this->getNamespace().$key); } - public function clear(){ - $ns = $this->getNamespace(); + public function clear($prefix=''){ + $ns = $this->getNamespace().$prefix; $cache = apc_cache_info('user'); foreach($cache['cache_list'] as $entry) { if (strpos($entry['info'], $ns) === 0) { apc_delete($entry['info']); } } + return true; } } if(!function_exists('apc_exists')) { diff --git a/lib/cache/broker.php b/lib/cache/broker.php index 931d0dd407e..c2aceabaf53 100644 --- a/lib/cache/broker.php +++ b/lib/cache/broker.php @@ -46,8 +46,8 @@ class OC_Cache_Broker { return $this->slow_cache->remove($key); } - public function clear(){ - $this->fast_cache->clear(); - $this->slow_cache->clear(); + public function clear($prefix=''){ + $this->fast_cache->clear($prefix); + $this->slow_cache->clear($prefix); } } diff --git a/lib/cache/file.php b/lib/cache/file.php index 0b7d3e30508..562c3d17167 100644 --- a/lib/cache/file.php +++ b/lib/cache/file.php @@ -62,15 +62,16 @@ class OC_Cache_File{ return $storage->unlink($key); } - public function clear(){ + public function clear($prefix=''){ $storage = $this->getStorage(); if($storage and $storage->is_dir('/')){ $dh=$storage->opendir('/'); while($file=readdir($dh)){ - if($file!='.' and $file!='..'){ + if($file!='.' and $file!='..' and ($prefix==='' || strpos($file, $prefix) === 0)){ $storage->unlink('/'.$file); } } } + return true; } } diff --git a/lib/cache/xcache.php b/lib/cache/xcache.php index bd55cee8f6b..951f9b47545 100644 --- a/lib/cache/xcache.php +++ b/lib/cache/xcache.php @@ -43,7 +43,8 @@ class OC_Cache_XCache { return xcache_unset($this->getNamespace().$key); } - public function clear(){ - return xcache_unset_by_prefix($this->getNamespace()); + public function clear($prefix=''){ + xcache_unset_by_prefix($this->getNamespace().$prefix); + return true; } } diff --git a/lib/connector/sabre/node.php b/lib/connector/sabre/node.php index 3cb5412f09f..663970487fb 100644 --- a/lib/connector/sabre/node.php +++ b/lib/connector/sabre/node.php @@ -142,6 +142,7 @@ abstract class OC_Connector_Sabre_Node implements Sabre_DAV_INode, Sabre_DAV_IPr public function updateProperties($properties) { $existing = $this->getProperties(array()); foreach($properties as $propertyName => $propertyValue) { + $propertyName = preg_replace("/^{.*}/", "", $propertyName); // remove leading namespace from property name // If it was null, we need to delete the property if (is_null($propertyValue)) { if(array_key_exists( $propertyName, $existing )){ diff --git a/lib/db.php b/lib/db.php index 6f083d17cfb..6971fe4a583 100644 --- a/lib/db.php +++ b/lib/db.php @@ -368,9 +368,6 @@ class OC_DB { if( $definition instanceof MDB2_Schema_Error ){ die( $definition->getMessage().': '.$definition->getUserInfo()); } -// if(OC_Config::getValue('dbtype','sqlite')=='sqlite'){ -// $definition['overwrite']=true;//always overwrite for sqlite -// } $ret=self::$schema->createDatabase( $definition ); // Die in case something went wrong @@ -527,8 +524,7 @@ class OC_DB { * @brief replaces the owncloud tables with a new set * @param $file string path to the MDB2 xml db export file */ - public static function replaceDB( $file ){ - + public static function replaceDB( $file ){ $apps = OC_App::getAllApps(); self::beginTransaction(); // Delete the old tables diff --git a/lib/eventsource.php b/lib/eventsource.php index 2a8c6b92902..95af2e471bc 100644 --- a/lib/eventsource.php +++ b/lib/eventsource.php @@ -42,6 +42,9 @@ class OC_EventSource{ }else{ header("Content-Type: text/event-stream"); } + if( !OC_Util::isCallRegistered()){ + exit(); + } flush(); } diff --git a/lib/exception.php b/lib/exception.php new file mode 100644 index 00000000000..db516fc12d2 --- /dev/null +++ b/lib/exception.php @@ -0,0 +1,93 @@ +<?php +/** + * ownCloud + * + * @author Georg Ehrke + * @copyright 2012 georg@owncloud.com + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE + * License as published by the Free Software Foundation; either + * version 3 of the License, or any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU AFFERO GENERAL PUBLIC LICENSE for more details. + * + * You should have received a copy of the GNU Affero General Public + * License along with this library. If not, see <http://www.gnu.org/licenses/>. + * + */ +class OC_Exception extends Exception{ + + function __construct($message = null, $code = 0, $file = null, $line = null){ + parent::__construct($message, $code); + if(!is_null($file)){ + $this->file = $file; + } + if(!is_null($line)){ + $this->line = $line; + } + $this->writelog(); + } + + private function writelog(){ + @OC_Log::write(OC_App::getCurrentApp(), $this->getMessage() . '-' . $this->getFile() . '-' . $this->getLine(), OC_Log::FATAL); + } + + private function generatesysinfo(){ + return array('phpversion' => PHP_VERSION, + 'os' => php_uname('s'), + 'osrelease' => php_uname('r'), + 'osarchitecture' => php_uname('m'), + 'phpserverinterface' => php_sapi_name(), + 'serverprotocol' => $_SERVER['SERVER_PROTOCOL'], + 'requestmethod' => $_SERVER['REQUEST_METHOD'], + 'https' => ($_SERVER['HTTPS']==''?'false':'true'), + 'database'=>(@OC_Config::getValue('dbtype')!=''?@OC_Config::getValue('dbtype'):'') + ); + } + + function __toString(){ + $tmpl = new OC_Template('core', 'exception', 'guest'); + $tmpl->assign('showsysinfo', true); + $tmpl->assign('message', $this->getMessage()); + $tmpl->assign('code', $this->getCode()); + $tmpl->assign('file', $this->getFile()); + $tmpl->assign('line', $this->getLine()); + $tmpl->assign('sysinfo', $this->generatesysinfo()); + $tmpl->printPage(); + } +} + +function oc_exceptionhandler($exception){ + switch($exception->getCode()){ + case E_NOTICE: + case E_DEPRECATED: + case E_USER_NOTICE: + case E_USER_DEPRECATED: + break; + default: + throw new OC_Exception($exception->getMessage(), $exception->getCode(), $exception->getFile(), $exception->getLine()); + break; + } + return true; +} + +function oc_errorhandler($errno , $errstr , $errfile , $errline){ + switch($errno){ + case E_NOTICE: + case E_DEPRECATED: + case E_USER_NOTICE: + case E_USER_DEPRECATED: + break; + default: + throw new OC_Exception($errstr, $errno, $errfile, $errline); + break; + } + return true; +} +set_exception_handler('oc_exceptionhandler'); +set_error_handler('oc_errorhandler'); +error_reporting(E_ERROR | E_WARNING | E_PARSE);
\ No newline at end of file diff --git a/lib/filecache.php b/lib/filecache.php index 4b1774925c3..22f7427ae42 100644 --- a/lib/filecache.php +++ b/lib/filecache.php @@ -98,6 +98,10 @@ class OC_FileCache{ if(OC_DB::isError($result)){ OC_Log::write('files','error while writing file('.$path.') to cache',OC_Log::ERROR); } + + if($cache=OC_Cache::getUserCache(true)){ + $cache->remove('fileid/'.$path);//ensure we don't have -1 cached + } } /** @@ -146,6 +150,11 @@ class OC_FileCache{ $query=OC_DB::prepare('UPDATE *PREFIX*fscache SET parent=? ,name=?, path=?, path_hash=? WHERE path_hash=?'); $query->execute(array($newParent,basename($newPath),$newPath,md5($newPath),md5($oldPath))); + if(($cache=OC_Cache::getUserCache(true)) && $cache->hasKey('fileid/'.$oldPath)){ + $cache->set('fileid/'.$newPath,$cache->get('fileid/'.$oldPath)); + $cache->remove('fileid/'.$oldPath); + } + $query=OC_DB::prepare('SELECT path FROM *PREFIX*fscache WHERE path LIKE ?'); $oldLength=strlen($oldPath); $updateQuery=OC_DB::prepare('UPDATE *PREFIX*fscache SET path=?, path_hash=? WHERE path_hash=?'); @@ -153,6 +162,11 @@ class OC_FileCache{ $old=$row['path']; $new=$newPath.substr($old,$oldLength); $updateQuery->execute(array($new,md5($new),md5($old))); + + if(($cache=OC_Cache::getUserCache(true)) && $cache->hasKey('fileid/'.$old)){ + $cache->set('fileid/'.$new,$cache->get('fileid/'.$old)); + $cache->remove('fileid/'.$old); + } } } @@ -171,6 +185,8 @@ class OC_FileCache{ //delete everything inside the folder $query=OC_DB::prepare('DELETE FROM *PREFIX*fscache WHERE path LIKE ?'); $query->execute(array($root.$path.'/%')); + + OC_Cache::remove('fileid/'.$root.$path); } /** @@ -245,9 +261,14 @@ class OC_FileCache{ if($root===false){ $root=OC_Filesystem::getRoot(); } + + $fullPath=$root.$path; + if(($cache=OC_Cache::getUserCache(true)) && $cache->hasKey('fileid/'.$fullPath)){ + return $cache->get('fileid/'.$fullPath); + } $query=OC_DB::prepare('SELECT id FROM *PREFIX*fscache WHERE path_hash=?'); - $result=$query->execute(array(md5($root.$path))); + $result=$query->execute(array(md5($fullPath))); if(OC_DB::isError($result)){ OC_Log::write('files','error while getting file id of '.$path,OC_Log::ERROR); return -1; @@ -255,10 +276,15 @@ class OC_FileCache{ $result=$result->fetchRow(); if(is_array($result)){ - return $result['id']; + $id=$result['id']; }else{ - return -1; + $id=-1; + } + if($cache=OC_Cache::getUserCache(true)){ + $cache->set('fileid/'.$fullPath,$id); } + + return $id; } /** diff --git a/lib/filesystem.php b/lib/filesystem.php index ec30ffb8f4c..a5edcf5bab3 100644 --- a/lib/filesystem.php +++ b/lib/filesystem.php @@ -53,99 +53,99 @@ class OC_Filesystem{ static private $defaultInstance; - /** - * classname which used for hooks handling - * used as signalclass in OC_Hooks::emit() - */ - const CLASSNAME = 'OC_Filesystem'; - - /** - * signalname emited before file renaming - * @param oldpath - * @param newpath - */ - const signal_rename = 'rename'; - - /** - * signal emited after file renaming - * @param oldpath - * @param newpath - */ - const signal_post_rename = 'post_rename'; - - /** - * signal emited before file/dir creation - * @param path - * @param run changing this flag to false in hook handler will cancel event - */ - const signal_create = 'create'; - - /** - * signal emited after file/dir creation - * @param path - * @param run changing this flag to false in hook handler will cancel event - */ - const signal_post_create = 'post_create'; - - /** - * signal emits before file/dir copy - * @param oldpath - * @param newpath - * @param run changing this flag to false in hook handler will cancel event - */ - const signal_copy = 'copy'; - - /** - * signal emits after file/dir copy - * @param oldpath - * @param newpath - */ - const signal_post_copy = 'post_copy'; - - /** - * signal emits before file/dir save - * @param path - * @param run changing this flag to false in hook handler will cancel event - */ - const signal_write = 'write'; - - /** - * signal emits after file/dir save - * @param path - */ - const signal_post_write = 'post_write'; - - /** - * signal emits when reading file/dir - * @param path - */ - const signal_read = 'read'; - - /** - * signal emits when removing file/dir - * @param path - */ - const signal_delete = 'delete'; - - /** - * parameters definitions for signals - */ - const signal_param_path = 'path'; - const signal_param_oldpath = 'oldpath'; - const signal_param_newpath = 'newpath'; - - /** - * run - changing this flag to false in hook handler will cancel event - */ - const signal_param_run = 'run'; - - /** - * get the mountpoint of the storage object for a path - ( note: because a storage is not always mounted inside the fakeroot, the returned mountpoint is relative to the absolute root of the filesystem and doesn't take the chroot into account - * - * @param string path - * @return string - */ + /** + * classname which used for hooks handling + * used as signalclass in OC_Hooks::emit() + */ + const CLASSNAME = 'OC_Filesystem'; + + /** + * signalname emited before file renaming + * @param oldpath + * @param newpath + */ + const signal_rename = 'rename'; + + /** + * signal emited after file renaming + * @param oldpath + * @param newpath + */ + const signal_post_rename = 'post_rename'; + + /** + * signal emited before file/dir creation + * @param path + * @param run changing this flag to false in hook handler will cancel event + */ + const signal_create = 'create'; + + /** + * signal emited after file/dir creation + * @param path + * @param run changing this flag to false in hook handler will cancel event + */ + const signal_post_create = 'post_create'; + + /** + * signal emits before file/dir copy + * @param oldpath + * @param newpath + * @param run changing this flag to false in hook handler will cancel event + */ + const signal_copy = 'copy'; + + /** + * signal emits after file/dir copy + * @param oldpath + * @param newpath + */ + const signal_post_copy = 'post_copy'; + + /** + * signal emits before file/dir save + * @param path + * @param run changing this flag to false in hook handler will cancel event + */ + const signal_write = 'write'; + + /** + * signal emits after file/dir save + * @param path + */ + const signal_post_write = 'post_write'; + + /** + * signal emits when reading file/dir + * @param path + */ + const signal_read = 'read'; + + /** + * signal emits when removing file/dir + * @param path + */ + const signal_delete = 'delete'; + + /** + * parameters definitions for signals + */ + const signal_param_path = 'path'; + const signal_param_oldpath = 'oldpath'; + const signal_param_newpath = 'newpath'; + + /** + * run - changing this flag to false in hook handler will cancel event + */ + const signal_param_run = 'run'; + + /** + * get the mountpoint of the storage object for a path + ( note: because a storage is not always mounted inside the fakeroot, the returned mountpoint is relative to the absolute root of the filesystem and doesn't take the chroot into account + * + * @param string path + * @return string + */ static public function getMountPoint($path){ OC_Hook::emit(self::CLASSNAME,'get_mountpoint',array('path'=>$path)); if(!$path){ diff --git a/lib/filesystemview.php b/lib/filesystemview.php index a23d7bbe7fd..9beda01e5a1 100644 --- a/lib/filesystemview.php +++ b/lib/filesystemview.php @@ -22,19 +22,19 @@ /** - * Class to provide access to ownCloud filesystem via a "view", and methods for - * working with files within that view (e.g. read, write, delete, etc.). Each - * view is restricted to a set of directories via a virtual root. The default view - * uses the currently logged in user's data directory as root (parts of + * Class to provide access to ownCloud filesystem via a "view", and methods for + * working with files within that view (e.g. read, write, delete, etc.). Each + * view is restricted to a set of directories via a virtual root. The default view + * uses the currently logged in user's data directory as root (parts of * OC_Filesystem are merely a wrapper for OC_FilesystemView). - * + * * Apps that need to access files outside of the user data folders (to modify files * belonging to a user other than the one currently logged in, for example) should * use this class directly rather than using OC_Filesystem, or making use of PHP's - * built-in file manipulation functions. This will ensure all hooks and proxies + * built-in file manipulation functions. This will ensure all hooks and proxies * are triggered correctly. * - * Filesystem functions are not called directly; they are passed to the correct + * Filesystem functions are not called directly; they are passed to the correct * OC_Filestorage object */ @@ -43,11 +43,11 @@ class OC_FilesystemView { private $internal_path_cache=array(); private $storage_cache=array(); - public function __construct($root){ + public function __construct($root) { $this->fakeRoot=$root; } - public function getAbsolutePath($path){ + public function getAbsolutePath($path) { if(!$path){ $path='/'; } @@ -63,9 +63,9 @@ class OC_FilesystemView { * @param string fakeRoot * @return bool */ - public function chroot($fakeRoot){ + public function chroot($fakeRoot) { if(!$fakeRoot==''){ - if($fakeRoot[0]!=='/'){ + if($fakeRoot[0]!=='/') { $fakeRoot='/'.$fakeRoot; } } @@ -76,7 +76,7 @@ class OC_FilesystemView { * get the fake root * @return string */ - public function getRoot(){ + public function getRoot() { return $this->fakeRoot; } @@ -85,7 +85,7 @@ class OC_FilesystemView { * @param string path * @return bool */ - public function getInternalPath($path){ + public function getInternalPath($path) { if (!isset($this->internal_path_cache[$path])) { $this->internal_path_cache[$path] = OC_Filesystem::getInternalPath($this->getAbsolutePath($path)); } @@ -97,23 +97,23 @@ class OC_FilesystemView { * @param string path * @return string */ - public function getRelativePath($path){ - if($this->fakeRoot==''){ + public function getRelativePath($path) { + if($this->fakeRoot=='') { return $path; } - if(strpos($path,$this->fakeRoot)!==0){ + if(strpos($path, $this->fakeRoot)!==0) { return null; }else{ - return substr($path,strlen($this->fakeRoot)); + return substr($path, strlen($this->fakeRoot)); } } - + /** * get the storage object for a path * @param string path * @return OC_Filestorage */ - public function getStorage($path){ + public function getStorage($path) { if (!isset($this->storage_cache[$path])) { $this->storage_cache[$path] = OC_Filesystem::getStorage($this->getAbsolutePath($path)); } @@ -127,7 +127,7 @@ class OC_FilesystemView { * @param string path * @return string */ - public function getMountPoint($path){ + public function getMountPoint($path) { return OC_Filesystem::getMountPoint($this->getAbsolutePath($path)); } @@ -137,55 +137,55 @@ class OC_FilesystemView { * @param string path * @return string */ - public function getLocalFile($path){ - $parent=substr($path,0,strrpos($path,'/')); - if(OC_Filesystem::isValidPath($parent) and $storage=$this->getStorage($path)){ + public function getLocalFile($path) { + $parent=substr($path, 0, strrpos($path,'/')); + if(OC_Filesystem::isValidPath($parent) and $storage=$this->getStorage($path)) { return $storage->getLocalFile($this->getInternalPath($path)); } } /** - * the following functions operate with arguments and return values identical - * to those of their PHP built-in equivalents. Mostly they are merely wrappers + * the following functions operate with arguments and return values identical + * to those of their PHP built-in equivalents. Mostly they are merely wrappers * for OC_Filestorage via basicOperation(). */ - public function mkdir($path){ - return $this->basicOperation('mkdir',$path,array('create','write')); + public function mkdir($path) { + return $this->basicOperation('mkdir', $path, array('create', 'write')); } - public function rmdir($path){ - return $this->basicOperation('rmdir',$path,array('delete')); + public function rmdir($path) { + return $this->basicOperation('rmdir', $path, array('delete')); } - public function opendir($path){ - return $this->basicOperation('opendir',$path,array('read')); + public function opendir($path) { + return $this->basicOperation('opendir', $path, array('read')); } - public function readdir($handle){ + public function readdir($handle) { $fsLocal= new OC_Filestorage_Local( array( 'datadir' => '/' ) ); return $fsLocal->readdir( $handle ); } - public function is_dir($path){ + public function is_dir($path) { if($path=='/'){ return true; } - return $this->basicOperation('is_dir',$path); + return $this->basicOperation('is_dir', $path); } - public function is_file($path){ + public function is_file($path) { if($path=='/'){ return false; } - return $this->basicOperation('is_file',$path); + return $this->basicOperation('is_file', $path); } - public function stat($path){ - return $this->basicOperation('stat',$path); + public function stat($path) { + return $this->basicOperation('stat', $path); } - public function filetype($path){ - return $this->basicOperation('filetype',$path); + public function filetype($path) { + return $this->basicOperation('filetype', $path); } - public function filesize($path){ - return $this->basicOperation('filesize',$path); + public function filesize($path) { + return $this->basicOperation('filesize', $path); } - public function readfile($path){ + public function readfile($path) { @ob_end_clean(); - $handle=$this->fopen($path,'rb'); + $handle=$this->fopen($path, 'rb'); if ($handle) { $chunkSize = 8192;// 8 MB chunks while (!feof($handle)) { @@ -197,137 +197,210 @@ class OC_FilesystemView { } return false; } - public function is_readable($path){ - return $this->basicOperation('is_readable',$path); + public function is_readable($path) { + return $this->basicOperation('is_readable', $path); } - public function is_writable($path){ - return $this->basicOperation('is_writable',$path); + public function is_writable($path) { + return $this->basicOperation('is_writable', $path); } - public function file_exists($path){ + public function file_exists($path) { if($path=='/'){ return true; } - return $this->basicOperation('file_exists',$path); + return $this->basicOperation('file_exists', $path); } - public function filectime($path){ - return $this->basicOperation('filectime',$path); + public function filectime($path) { + return $this->basicOperation('filectime', $path); } - public function filemtime($path){ - return $this->basicOperation('filemtime',$path); + public function filemtime($path) { + return $this->basicOperation('filemtime', $path); } - public function touch($path, $mtime=null){ + public function touch($path, $mtime=null) { return $this->basicOperation('touch', $path, array('write'), $mtime); } - public function file_get_contents($path){ - return $this->basicOperation('file_get_contents',$path,array('read')); - } - public function file_put_contents($path,$data){ - if(is_resource($data)){//not having to deal with streams in file_put_contents makes life easier - $exists=$this->file_exists($path); - $run=true; - if(!$exists){ - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_create, array( OC_Filesystem::signal_param_path => $path, OC_Filesystem::signal_param_run => &$run)); + public function file_get_contents($path) { + return $this->basicOperation('file_get_contents', $path, array('read')); + } + public function file_put_contents($path, $data) { + if(is_resource($data)) {//not having to deal with streams in file_put_contents makes life easier + $exists = $this->file_exists($path); + $run = true; + if(!$exists) { + OC_Hook::emit( + OC_Filesystem::CLASSNAME, + OC_Filesystem::signal_create, + array( + OC_Filesystem::signal_param_path => $path, + OC_Filesystem::signal_param_run => &$run + ) + ); } - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_write, array( OC_Filesystem::signal_param_path => $path, OC_Filesystem::signal_param_run => &$run)); - if(!$run){ + OC_Hook::emit( + OC_Filesystem::CLASSNAME, + OC_Filesystem::signal_write, + array( + OC_Filesystem::signal_param_path => $path, + OC_Filesystem::signal_param_run => &$run + ) + ); + if(!$run) { return false; } - $target=$this->fopen($path,'w'); - if($target){ - $count=OC_Helper::streamCopy($data,$target); + $target=$this->fopen($path, 'w'); + if($target) { + $count=OC_Helper::streamCopy($data, $target); fclose($target); fclose($data); - if(!$exists){ - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_create, array( OC_Filesystem::signal_param_path => $path)); + if(!$exists) { + OC_Hook::emit( + OC_Filesystem::CLASSNAME, + OC_Filesystem::signal_post_create, + array( OC_Filesystem::signal_param_path => $path) + ); } - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_write, array( OC_Filesystem::signal_param_path => $path)); - return $count>0; + OC_Hook::emit( + OC_Filesystem::CLASSNAME, + OC_Filesystem::signal_post_write, + array( OC_Filesystem::signal_param_path => $path) + ); + return $count > 0; }else{ return false; } }else{ - return $this->basicOperation('file_put_contents',$path,array('create','write'),$data); + return $this->basicOperation('file_put_contents', $path, array('create', 'write'), $data); } } - public function unlink($path){ - return $this->basicOperation('unlink',$path,array('delete')); + public function unlink($path) { + return $this->basicOperation('unlink', $path, array('delete')); } public function deleteAll( $directory, $empty = false ) { return $this->basicOperation( 'deleteAll', $directory, array('delete'), $empty ); } - public function rename($path1,$path2){ - $absolutePath1=$this->getAbsolutePath($path1); - $absolutePath2=$this->getAbsolutePath($path2); - if(OC_FileProxy::runPreProxies('rename',$absolutePath1,$absolutePath2) and OC_Filesystem::isValidPath($path2)){ - $path1=$this->getRelativePath($absolutePath1); - $path2=$this->getRelativePath($absolutePath2); - if($path1==null or $path2==null){ + public function rename($path1, $path2) { + $absolutePath1 = $this->getAbsolutePath($path1); + $absolutePath2 = $this->getAbsolutePath($path2); + if(OC_FileProxy::runPreProxies('rename', $absolutePath1, $absolutePath2) and OC_Filesystem::isValidPath($path2)) { + $path1 = $this->getRelativePath($absolutePath1); + $path2 = $this->getRelativePath($absolutePath2); + if($path1 == null or $path2 == null) { return false; } $run=true; - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_rename, array( OC_Filesystem::signal_param_oldpath => $path1 , OC_Filesystem::signal_param_newpath=>$path2, OC_Filesystem::signal_param_run => &$run)); - if($run){ - $mp1=$this->getMountPoint($path1); - $mp2=$this->getMountPoint($path2); - if($mp1==$mp2){ - if($storage=$this->getStorage($path1)){ - $result=$storage->rename($this->getInternalPath($path1),$this->getInternalPath($path2)); + OC_Hook::emit( + OC_Filesystem::CLASSNAME, OC_Filesystem::signal_rename, + array( + OC_Filesystem::signal_param_oldpath => $path1, + OC_Filesystem::signal_param_newpath => $path2, + OC_Filesystem::signal_param_run => &$run + ) + ); + if($run) { + $mp1 = $this->getMountPoint($path1); + $mp2 = $this->getMountPoint($path2); + if($mp1 == $mp2) { + if($storage = $this->getStorage($path1)) { + $result = $storage->rename($this->getInternalPath($path1), $this->getInternalPath($path2)); } - }else{ - $source=$this->fopen($path1,'r'); - $target=$this->fopen($path2,'w'); - $count=OC_Helper::streamCopy($source,$target); - $storage1=$this->getStorage($path1); + } else { + $source = $this->fopen($path1, 'r'); + $target = $this->fopen($path2, 'w'); + $count = OC_Helper::streamCopy($source, $target); + $storage1 = $this->getStorage($path1); $storage1->unlink($this->getInternalPath($path1)); - $result=$count>0; + $result = $count>0; } - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_rename, array( OC_Filesystem::signal_param_oldpath => $path1, OC_Filesystem::signal_param_newpath=>$path2)); + OC_Hook::emit( + OC_Filesystem::CLASSNAME, + OC_Filesystem::signal_post_rename, + array( + OC_Filesystem::signal_param_oldpath => $path1, + OC_Filesystem::signal_param_newpath => $path2 + ) + ); return $result; } } } - public function copy($path1,$path2){ - $absolutePath1=$this->getAbsolutePath($path1); - $absolutePath2=$this->getAbsolutePath($path2); - if(OC_FileProxy::runPreProxies('copy',$absolutePath1,$absolutePath2) and OC_Filesystem::isValidPath($path2)){ - $path1=$this->getRelativePath($absolutePath1); - $path2=$this->getRelativePath($absolutePath2); - if($path1==null or $path2==null){ + public function copy($path1, $path2) { + $absolutePath1 = $this->getAbsolutePath($path1); + $absolutePath2 = $this->getAbsolutePath($path2); + if(OC_FileProxy::runPreProxies('copy', $absolutePath1, $absolutePath2) and OC_Filesystem::isValidPath($path2)) { + $path1 = $this->getRelativePath($absolutePath1); + $path2 = $this->getRelativePath($absolutePath2); + if($path1 == null or $path2 == null) { return false; } $run=true; - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_copy, array( OC_Filesystem::signal_param_oldpath => $path1 , OC_Filesystem::signal_param_newpath=>$path2, OC_Filesystem::signal_param_run => &$run)); + OC_Hook::emit( + OC_Filesystem::CLASSNAME, + OC_Filesystem::signal_copy, + array( + OC_Filesystem::signal_param_oldpath => $path1, + OC_Filesystem::signal_param_newpath=>$path2, + OC_Filesystem::signal_param_run => &$run + ) + ); $exists=$this->file_exists($path2); - if($run and !$exists){ - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_create, array( OC_Filesystem::signal_param_path => $path2, OC_Filesystem::signal_param_run => &$run)); + if($run and !$exists) { + OC_Hook::emit( + OC_Filesystem::CLASSNAME, + OC_Filesystem::signal_create, + array( + OC_Filesystem::signal_param_path => $path2, + OC_Filesystem::signal_param_run => &$run + ) + ); } - if($run){ - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_write, array( OC_Filesystem::signal_param_path => $path2, OC_Filesystem::signal_param_run => &$run)); + if($run) { + OC_Hook::emit( + OC_Filesystem::CLASSNAME, + OC_Filesystem::signal_write, + array( + OC_Filesystem::signal_param_path => $path2, + OC_Filesystem::signal_param_run => &$run + ) + ); } - if($run){ + if($run) { $mp1=$this->getMountPoint($path1); $mp2=$this->getMountPoint($path2); - if($mp1==$mp2){ - if($storage=$this->getStorage($path1)){ - $result=$storage->copy($this->getInternalPath($path1),$this->getInternalPath($path2)); + if($mp1 == $mp2){ + if($storage = $this->getStorage($path1)) { + $result=$storage->copy($this->getInternalPath($path1), $this->getInternalPath($path2)); } - }else{ - $source=$this->fopen($path1,'r'); - $target=$this->fopen($path2,'w'); - $result=OC_Helper::streamCopy($source,$target); + } else { + $source = $this->fopen($path1, 'r'); + $target = $this->fopen($path2, 'w'); + $result = OC_Helper::streamCopy($source, $target); } - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_copy, array( OC_Filesystem::signal_param_oldpath => $path1 , OC_Filesystem::signal_param_newpath=>$path2)); - if(!$exists){ - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_create, array( OC_Filesystem::signal_param_path => $path2)); + OC_Hook::emit( + OC_Filesystem::CLASSNAME, + OC_Filesystem::signal_post_copy, + array( + OC_Filesystem::signal_param_oldpath => $path1, + OC_Filesystem::signal_param_newpath=>$path2 + ) + ); + if(!$exists) { + OC_Hook::emit( + OC_Filesystem::CLASSNAME, + OC_Filesystem::signal_post_create, + array(OC_Filesystem::signal_param_path => $path2) + ); } - OC_Hook::emit( OC_Filesystem::CLASSNAME, OC_Filesystem::signal_post_write, array( OC_Filesystem::signal_param_path => $path2)); + OC_Hook::emit( + OC_Filesystem::CLASSNAME, + OC_Filesystem::signal_post_write, + array( OC_Filesystem::signal_param_path => $path2) + ); return $result; } } } - public function fopen($path,$mode){ + public function fopen($path, $mode) { $hooks=array(); - switch($mode){ + switch($mode) { case 'r': case 'rb': $hooks[]='read'; @@ -355,49 +428,49 @@ class OC_FilesystemView { OC_Log::write('core','invalid mode ('.$mode.') for '.$path,OC_Log::ERROR); } - return $this->basicOperation('fopen',$path,$hooks,$mode); + return $this->basicOperation('fopen', $path, $hooks, $mode); } - public function toTmpFile($path){ - if(OC_Filesystem::isValidPath($path)){ - $source=$this->fopen($path,'r'); - if($source){ + public function toTmpFile($path) { + if(OC_Filesystem::isValidPath($path)) { + $source = $this->fopen($path, 'r'); + if($source) { $extension=''; - $extOffset=strpos($path,'.'); + $extOffset=strpos($path, '.'); if($extOffset !== false) { - $extension=substr($path,strrpos($path,'.')); + $extension=substr($path, strrpos($path,'.')); } - $tmpFile=OC_Helper::tmpFile($extension); - file_put_contents($tmpFile,$source); + $tmpFile = OC_Helper::tmpFile($extension); + file_put_contents($tmpFile, $source); return $tmpFile; } } } - public function fromTmpFile($tmpFile,$path){ - if(OC_Filesystem::isValidPath($path)){ - if(!$tmpFile){ + public function fromTmpFile($tmpFile, $path) { + if(OC_Filesystem::isValidPath($path)) { + if(!$tmpFile) { debug_print_backtrace(); } - $source=fopen($tmpFile,'r'); - if($source){ - $this->file_put_contents($path,$source); + $source=fopen($tmpFile, 'r'); + if($source) { + $this->file_put_contents($path, $source); unlink($tmpFile); return true; - }else{ + } else { } - }else{ + } else { return false; } } - public function getMimeType($path){ - return $this->basicOperation('getMimeType',$path); + public function getMimeType($path) { + return $this->basicOperation('getMimeType', $path); } - public function hash($type,$path){ - return $this->basicOperation('hash',$path,array('read'),$type); + public function hash($type, $path) { + return $this->basicOperation('hash', $path, array('read'), $type); } - public function free_space($path='/'){ - return $this->basicOperation('free_space',$path); + public function free_space($path='/') { + return $this->basicOperation('free_space', $path); } /** @@ -407,41 +480,56 @@ class OC_FilesystemView { * @param array (optional) hooks * @param mixed (optional) $extraParam * @return mixed - * - * This method takes requests for basic filesystem functions (e.g. reading & writing - * files), processes hooks and proxies, sanitises paths, and finally passes them on to + * + * This method takes requests for basic filesystem functions (e.g. reading & writing + * files), processes hooks and proxies, sanitises paths, and finally passes them on to * OC_Filestorage for delegation to a storage backend for execution */ - private function basicOperation($operation,$path,$hooks=array(),$extraParam=null){ - $absolutePath=$this->getAbsolutePath($path); - if(OC_FileProxy::runPreProxies($operation,$absolutePath, $extraParam) and OC_Filesystem::isValidPath($path)){ - $path=$this->getRelativePath($absolutePath); - if($path==null){ + private function basicOperation($operation, $path, $hooks=array(), $extraParam=null) { + $absolutePath = $this->getAbsolutePath($path); + if(OC_FileProxy::runPreProxies($operation, $absolutePath, $extraParam) and OC_Filesystem::isValidPath($path)) { + $path = $this->getRelativePath($absolutePath); + if($path == null) { return false; } - $internalPath=$this->getInternalPath($path); - $run=true; - if(OC_Filesystem::$loaded and $this->fakeRoot==OC_Filesystem::getRoot()){ - foreach($hooks as $hook){ - if($hook!='read'){ - OC_Hook::emit( OC_Filesystem::CLASSNAME, $hook, array( OC_Filesystem::signal_param_path => $path, OC_Filesystem::signal_param_run => &$run)); - }else{ - OC_Hook::emit( OC_Filesystem::CLASSNAME, $hook, array( OC_Filesystem::signal_param_path => $path)); + $internalPath = $this->getInternalPath($path); + $run = true; + if(OC_Filesystem::$loaded and $this->fakeRoot==OC_Filesystem::getRoot()) { + foreach($hooks as $hook) { + if($hook!='read') { + OC_Hook::emit( + OC_Filesystem::CLASSNAME, + $hook, + array( + OC_Filesystem::signal_param_path => $path, + OC_Filesystem::signal_param_run => &$run + ) + ); + } else { + OC_Hook::emit( + OC_Filesystem::CLASSNAME, + $hook, + array( OC_Filesystem::signal_param_path => $path) + ); } } } - if($run and $storage=$this->getStorage($path)){ - if(!is_null($extraParam)){ - $result=$storage->$operation($internalPath,$extraParam); - }else{ - $result=$storage->$operation($internalPath); + if($run and $storage = $this->getStorage($path)) { + if(!is_null($extraParam)) { + $result = $storage->$operation($internalPath, $extraParam); + } else { + $result = $storage->$operation($internalPath); } - $result=OC_FileProxy::runPostProxies($operation,$this->getAbsolutePath($path),$result); - if(OC_Filesystem::$loaded and $this->fakeRoot==OC_Filesystem::getRoot()){ - if($operation!='fopen'){//no post hooks for fopen, the file stream is still open - foreach($hooks as $hook){ + $result = OC_FileProxy::runPostProxies($operation, $this->getAbsolutePath($path), $result); + if(OC_Filesystem::$loaded and $this->fakeRoot==OC_Filesystem::getRoot()) { + if($operation!='fopen') {//no post hooks for fopen, the file stream is still open + foreach($hooks as $hook) { if($hook!='read'){ - OC_Hook::emit( OC_Filesystem::CLASSNAME, 'post_'.$hook, array( OC_Filesystem::signal_param_path => $path)); + OC_Hook::emit( + OC_Filesystem::CLASSNAME, + 'post_'.$hook, + array( OC_Filesystem::signal_param_path => $path) + ); } } } @@ -457,7 +545,7 @@ class OC_FilesystemView { * @param int $time * @return bool */ - public function hasUpdated($path,$time){ - return $this->basicOperation('hasUpdated',$path,array(),$time); + public function hasUpdated($path, $time) { + return $this->basicOperation('hasUpdated', $path, array(), $time); } } diff --git a/lib/helper.php b/lib/helper.php index c4f7e8b2e19..f328c14ac77 100644 --- a/lib/helper.php +++ b/lib/helper.php @@ -175,10 +175,8 @@ class OC_Helper { */ public static function mimetypeIcon( $mimetype ){ $alias=array('application/xml'=>'code/xml'); -// echo $mimetype; if(isset($alias[$mimetype])){ $mimetype=$alias[$mimetype]; -// echo $mimetype; } // Replace slash with a minus $mimetype = str_replace( "/", "-", $mimetype ); @@ -345,18 +343,24 @@ class OC_Helper { */ static function getMimeType($path){ $isWrapped=(strpos($path,'://')!==false) and (substr($path,0,7)=='file://'); - $mimeType='application/octet-stream'; - if ($mimeType=='application/octet-stream') { - self::$mimetypes = include('mimetypes.fixlist.php'); - $extension=strtolower(strrchr(basename($path), ".")); - $extension=substr($extension,1);//remove leading . - $mimeType=(isset(self::$mimetypes[$extension]))?self::$mimetypes[$extension]:'application/octet-stream'; - } if (@is_dir($path)) { // directories are easy return "httpd/unix-directory"; } + + if(strpos($path,'.')){ + //try to guess the type by the file extension + if(!self::$mimetypes || self::$mimetypes != include('mimetypes.list.php')){ + self::$mimetypes=include('mimetypes.list.php'); + } + $extension=strtolower(strrchr(basename($path), ".")); + $extension=substr($extension,1);//remove leading . + $mimeType=(isset(self::$mimetypes[$extension]))?self::$mimetypes[$extension]:'application/octet-stream'; + }else{ + $mimeType='application/octet-stream'; + } + if($mimeType=='application/octet-stream' and function_exists('finfo_open') and function_exists('finfo_file') and $finfo=finfo_open(FILEINFO_MIME)){ $info = @strtolower(finfo_file($finfo,$path)); if($info){ @@ -385,15 +389,6 @@ class OC_Helper { } } - if ($mimeType=='application/octet-stream') { - // Fallback solution: (try to guess the type by the file extension - if(!self::$mimetypes || self::$mimetypes != include('mimetypes.list.php')){ - self::$mimetypes=include('mimetypes.list.php'); - } - $extension=strtolower(strrchr(basename($path), ".")); - $extension=substr($extension,1);//remove leading . - $mimeType=(isset(self::$mimetypes[$extension]))?self::$mimetypes[$extension]:'application/octet-stream'; - } return $mimeType; } diff --git a/lib/image.php b/lib/image.php index c438b3d67f6..90c64320a7c 100644 --- a/lib/image.php +++ b/lib/image.php @@ -23,12 +23,12 @@ //From user comments at http://dk2.php.net/manual/en/function.exif-imagetype.php if ( ! function_exists( 'exif_imagetype' ) ) { - function exif_imagetype ( $filename ) { - if ( ( $info = getimagesize( $filename ) ) !== false ) { - return $info[2]; - } - return false; - } + function exif_imagetype ( $filename ) { + if ( ( $info = getimagesize( $filename ) ) !== false ) { + return $info[2]; + } + return false; + } } function ellipsis($str, $maxlen) { @@ -66,7 +66,6 @@ class OC_Image { public function __construct($imageref = null) { //OC_Log::write('core',__METHOD__.'(): start', OC_Log::DEBUG); if(!extension_loaded('gd') || !function_exists('gd_info')) { - //if(!function_exists('imagecreatefromjpeg')) { OC_Log::write('core',__METHOD__.'(): GD module not installed', OC_Log::ERROR); return false; } diff --git a/lib/installer.php b/lib/installer.php index 00feb6d4709..a8b56cb34f2 100644 --- a/lib/installer.php +++ b/lib/installer.php @@ -126,19 +126,19 @@ class OC_Installer{ return false; } $info=OC_App::getAppInfo($extractDir.'/appinfo/info.xml',true); - // check the code for not allowed calls - if(!OC_Installer::checkCode($info['id'],$extractDir)){ + // check the code for not allowed calls + if(!OC_Installer::checkCode($info['id'],$extractDir)){ OC_Log::write('core','App can\'t be installed because of not allowed code in the App',OC_Log::ERROR); OC_Helper::rmdirr($extractDir); - return false; + return false; } - // check if the app is compatible with this version of ownCloud + // check if the app is compatible with this version of ownCloud $version=OC_Util::getVersion(); - if(!isset($info['require']) or ($version[0]>$info['require'])){ + if(!isset($info['require']) or ($version[0]>$info['require'])){ OC_Log::write('core','App can\'t be installed because it is not compatible with this version of ownCloud',OC_Log::ERROR); OC_Helper::rmdirr($extractDir); - return false; + return false; } //check if an app with the same id is already installed @@ -339,12 +339,12 @@ class OC_Installer{ } - /** - * check the code of an app with some static code checks - * @param string $folder the folder of the app to check - * @returns true for app is o.k. and false for app is not o.k. - */ - public static function checkCode($appname,$folder){ + /** + * check the code of an app with some static code checks + * @param string $folder the folder of the app to check + * @returns true for app is o.k. and false for app is not o.k. + */ + public static function checkCode($appname,$folder){ $blacklist=array( 'exec(', @@ -377,9 +377,7 @@ class OC_Installer{ return true; }else{ - return true; + return true; } - } - - + } } diff --git a/lib/json.php b/lib/json.php index c49b831c12b..b46878375d5 100644 --- a/lib/json.php +++ b/lib/json.php @@ -94,12 +94,12 @@ class OC_JSON{ * Encode and print $data in json format */ public static function encodedPrint($data,$setContentType=true){ - // Disable mimesniffing, don't move this to setContentTypeHeader! - header( 'X-Content-Type-Options: nosniff' ); - if($setContentType){ - self::setContentTypeHeader(); - } - array_walk_recursive($data, array('OC_JSON', 'to_string')); - echo json_encode($data); + // Disable mimesniffing, don't move this to setContentTypeHeader! + header( 'X-Content-Type-Options: nosniff' ); + if($setContentType){ + self::setContentTypeHeader(); + } + array_walk_recursive($data, array('OC_JSON', 'to_string')); + echo json_encode($data); } } diff --git a/lib/mimetypes.fixlist.php b/lib/mimetypes.fixlist.php deleted file mode 100644 index 13e3f16b369..00000000000 --- a/lib/mimetypes.fixlist.php +++ /dev/null @@ -1,22 +0,0 @@ -<?php -return array( - 'ics'=>'text/calendar', - 'ical'=>'text/calendar', - 'js'=>'application/javascript', - 'odt'=>'application/vnd.oasis.opendocument.text', - 'ods'=>'application/vnd.oasis.opendocument.spreadsheet', - 'odg'=>'application/vnd.oasis.opendocument.graphics', - 'odp'=>'application/vnd.oasis.opendocument.presentation', - 'pl'=>'text/x-script.perl', - 'py'=>'text/x-script.phyton', - 'vcf' => 'text/vcard', - 'vcard' => 'text/vcard', - 'doc'=>'application/msword', - 'docx'=>'application/msword', - 'xls'=>'application/msexcel', - 'xlsx'=>'application/msexcel', - 'ppt'=>'application/mspowerpoint', - 'pptx'=>'application/mspowerpoint', - 'sgf' => 'application/sgf', - 'cdr' => 'application/coreldraw' -); diff --git a/lib/mimetypes.list.php b/lib/mimetypes.list.php index ccf47999b1c..f7207493f7f 100644 --- a/lib/mimetypes.list.php +++ b/lib/mimetypes.list.php @@ -78,5 +78,16 @@ return array( 'mpeg'=>'video/mpeg', 'mov'=>'video/quicktime', 'webm'=>'video/webm', - 'wmv'=>'video/x-ms-asf' + 'wmv'=>'video/x-ms-asf', + 'py'=>'text/x-script.phyton', + 'vcf' => 'text/vcard', + 'vcard' => 'text/vcard', + 'doc'=>'application/msword', + 'docx'=>'application/msword', + 'xls'=>'application/msexcel', + 'xlsx'=>'application/msexcel', + 'ppt'=>'application/mspowerpoint', + 'pptx'=>'application/mspowerpoint', + 'sgf' => 'application/sgf', + 'cdr' => 'application/coreldraw', ); diff --git a/lib/minimizer.php b/lib/minimizer.php index 3bf5ff9980b..3dc89e331a6 100644 --- a/lib/minimizer.php +++ b/lib/minimizer.php @@ -46,3 +46,13 @@ abstract class OC_Minimizer { echo $out; } } + +if (!function_exists('gzdecode')) { + function gzdecode($data,$maxlength=null,&$filename='',&$error='') + { + if (strcmp(substr($data,0,9),"\x1f\x8b\x8\0\0\0\0\0\0")) { + return null; // Not the GZIP format we expect (See RFC 1952) + } + return gzinflate(substr($data,10,-8)); + } +} diff --git a/lib/ocs.php b/lib/ocs.php index 27007076661..18f01518bdb 100644 --- a/lib/ocs.php +++ b/lib/ocs.php @@ -4,7 +4,9 @@ * ownCloud * * @author Frank Karlitschek +* @author Michael Gapczynski * @copyright 2012 Frank Karlitschek frank@owncloud.org +* @copyright 2012 Michael Gapczynski mtgap@owncloud.com * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU AFFERO GENERAL PUBLIC LICENSE @@ -27,431 +29,476 @@ */ class OC_OCS { - /** - * reads input date from get/post/cookies and converts the date to a special data-type - * - * @param variable $key - * @param variable-type $type - * @param priority $getpriority - * @param default $default - * @return data - */ - public static function readData($key,$type='raw',$getpriority=false,$default='') { - if($getpriority) { - if(isset($_GET[$key])) { - $data=$_GET[$key]; - } elseif(isset($_POST[$key])) { - $data=$_POST[$key]; - } else { - if($default=='') { - if(($type=='int') or ($type=='float')) $data=0; else $data=''; - } else { - $data=$default; - } - } - } else { - if(isset($_POST[$key])) { - $data=$_POST[$key]; - } elseif(isset($_GET[$key])) { - $data=$_GET[$key]; - } elseif(isset($_COOKIE[$key])) { - $data=$_COOKIE[$key]; - } else { - if($default=='') { - if(($type=='int') or ($type=='float')) $data=0; else $data=''; - } else { - $data=$default; - } - } - } - - if($type=='raw') return($data); - elseif($type=='text') return(addslashes(strip_tags($data))); - elseif($type=='int') { $data = (int) $data; return($data); } - elseif($type=='float') { $data = (float) $data; return($data); } - elseif($type=='array') { $data = $data; return($data); } - } - - - /** - main function to handle the REST request - **/ - public static function handle() { - - // overwrite the 404 error page returncode - header("HTTP/1.0 200 OK"); - - $router = new OC_Router(); - $router->useCollection('ocs'); - // CONFIG - $router->create('config', '/config.{format}') - ->defaults(array('format'=>'')) - ->action('OC_OCS', 'apiConfig') - ->requirements(array('format'=>'xml|json')); - - // PERSON - $router->create('person_check', '/person/check.{format}') - ->post() - ->defaults(array('format'=>'')) - ->action(function ($parameters) { - $format = $parameters['format'] ? $parameters['format'] : OC_OCS::readdata('format','text'); - $login=OC_OCS::readdata('login','text'); - $passwd=OC_OCS::readdata('password','text'); - OC_OCS::personcheck($format,$login,$passwd); - }) - ->requirements(array('format'=>'xml|json')); - - // ACTIVITY - // activityget - GET ACTIVITY page,pagesize als urlparameter - $router->create('activity_get', '/activity.{format}') - ->defaults(array('format'=>'')) - ->action(function ($parameters) { - $format = $parameters['format'] ? $parameters['format'] : OC_OCS::readdata('format','text'); - $page=OC_OCS::readdata('page','int'); - $pagesize=OC_OCS::readdata('pagesize','int'); - if($pagesize<1 or $pagesize>100) $pagesize=10; - OC_OCS::activityget($format,$page,$pagesize); - }) - ->requirements(array('format'=>'xml|json')); - // activityput - POST ACTIVITY - $router->create('activity_put', '/activity.{format}') - ->post() - ->defaults(array('format'=>'')) - ->action(function ($parameters) { - $format = $parameters['format'] ? $parameters['format'] : OC_OCS::readdata('format','text'); - $message=OC_OCS::readdata('message','text'); - OC_OCS::activityput($format,$message); - }) - ->requirements(array('format'=>'xml|json')); - - // PRIVATEDATA - // get - GET DATA - $router->create('privatedata_get', - '/privatedata/getattribute/{app}/{key}.{format}') - ->defaults(array('app' => '', 'key' => '', 'format' => '')) - ->action(function ($parameters) { - $format = $parameters['format'] ? $parameters['format'] : OC_OCS::readdata('format','text'); - $app = addslashes(strip_tags($parameters['app'])); - $key = addslashes(strip_tags($parameters['key'])); - OC_OCS::privateDataGet($format, $app, $key); - }) - ->requirements(array('format'=>'xml|json')); - // set - POST DATA - $router->create('privatedata_set', - '/privatedata/setattribute/{app}/{key}.{format}') - ->post() - ->defaults(array('format'=>'')) - ->action(function ($parameters) { - $format = $parameters['format'] ? $parameters['format'] : OC_OCS::readdata('format','text'); - $app = addslashes(strip_tags($parameters['app'])); - $key = addslashes(strip_tags($parameters['key'])); - $value=OC_OCS::readdata('value','text'); - OC_OCS::privateDataSet($format, $app, $key, $value); - }) - ->requirements(array('format'=>'xml|json')); - // delete - POST DATA - $router->create('privatedata_delete', - '/privatedata/deleteattribute/{app}/{key}.{format}') - ->post() - ->defaults(array('format'=>'')) - ->action(function ($parameters) { - $format = $parameters['format'] ? $parameters['format'] : OC_OCS::readdata('format','text'); - $app = addslashes(strip_tags($parameters['app'])); - $key = addslashes(strip_tags($parameters['key'])); - OC_OCS::privateDataDelete($format, $app, $key); - }) - ->requirements(array('format'=>'xml|json')); - - try { - $router->match($_SERVER['PATH_INFO']); - } catch (ResourceNotFoundException $e) { - $format=OC_OCS::readdata('format','text'); - $txt='Invalid query, please check the syntax. API specifications are here: http://www.freedesktop.org/wiki/Specifications/open-collaboration-services. DEBUG OUTPUT:'."\n"; - $txt.=OC_OCS::getdebugoutput(); - echo(OC_OCS::generatexml($format,'failed',999,$txt)); - } - exit(); - } - - /** - * generated some debug information to make it easier to find faild API calls - * @return debug data string - */ - private static function getDebugOutput() { - $txt=''; - $txt.="debug output:\n"; - if(isset($_SERVER['REQUEST_METHOD'])) $txt.='http request method: '.$_SERVER['REQUEST_METHOD']."\n"; - if(isset($_SERVER['REQUEST_URI'])) $txt.='http request uri: '.$_SERVER['REQUEST_URI']."\n"; - if(isset($_GET)) foreach($_GET as $key=>$value) $txt.='get parameter: '.$key.'->'.$value."\n"; - if(isset($_POST)) foreach($_POST as $key=>$value) $txt.='post parameter: '.$key.'->'.$value."\n"; - return($txt); - } - - /** - * checks if the user is authenticated - * checks the IP whitlist, apikeys and login/password combination - * if $forceuser is true and the authentication failed it returns an 401 http response. - * if $forceuser is false and authentification fails it returns an empty username string - * @param bool $forceuser - * @return username string - */ - private static function checkPassword($forceuser=true) { - //valid user account ? - if(isset($_SERVER['PHP_AUTH_USER'])) $authuser=$_SERVER['PHP_AUTH_USER']; else $authuser=''; - if(isset($_SERVER['PHP_AUTH_PW'])) $authpw=$_SERVER['PHP_AUTH_PW']; else $authpw=''; - - if(empty($authuser)) { - if($forceuser){ - header('WWW-Authenticate: Basic realm="your valid user account or api key"'); - header('HTTP/1.0 401 Unauthorized'); - exit; - }else{ - $identifieduser=''; - } - }else{ - if(!OC_User::login($authuser,$authpw)){ - if($forceuser){ - header('WWW-Authenticate: Basic realm="your valid user account or api key"'); - header('HTTP/1.0 401 Unauthorized'); - exit; - }else{ - $identifieduser=''; - } - }else{ - $identifieduser=$authuser; - } - } - - return($identifieduser); - } - - - /** - * generates the xml or json response for the API call from an multidimenional data array. - * @param string $format - * @param string $status - * @param string $statuscode - * @param string $message - * @param array $data - * @param string $tag - * @param string $tagattribute - * @param int $dimension - * @param int $itemscount - * @param int $itemsperpage - * @return string xml/json - */ - private static function generateXml($format,$status,$statuscode,$message,$data=array(),$tag='',$tagattribute='',$dimension=-1,$itemscount='',$itemsperpage='') { - if($format=='json') { - - $json=array(); - $json['status']=$status; - $json['statuscode']=$statuscode; - $json['message']=$message; - $json['totalitems']=$itemscount; - $json['itemsperpage']=$itemsperpage; - $json['data']=$data; - return(json_encode($json)); - - - }else{ - $txt=''; - $writer = xmlwriter_open_memory(); - xmlwriter_set_indent( $writer, 2 ); - xmlwriter_start_document($writer ); - xmlwriter_start_element($writer,'ocs'); - xmlwriter_start_element($writer,'meta'); - xmlwriter_write_element($writer,'status',$status); - xmlwriter_write_element($writer,'statuscode',$statuscode); - xmlwriter_write_element($writer,'message',$message); - if($itemscount<>'') xmlwriter_write_element($writer,'totalitems',$itemscount); - if(!empty($itemsperpage)) xmlwriter_write_element($writer,'itemsperpage',$itemsperpage); - xmlwriter_end_element($writer); - if($dimension=='0') { - // 0 dimensions - xmlwriter_write_element($writer,'data',$data); - - }elseif($dimension=='1') { - xmlwriter_start_element($writer,'data'); - foreach($data as $key=>$entry) { - xmlwriter_write_element($writer,$key,$entry); - } - xmlwriter_end_element($writer); - - }elseif($dimension=='2') { - xmlwriter_start_element($writer,'data'); - foreach($data as $entry) { - xmlwriter_start_element($writer,$tag); - if(!empty($tagattribute)) { - xmlwriter_write_attribute($writer,'details',$tagattribute); - } - foreach($entry as $key=>$value) { - if(is_array($value)){ - foreach($value as $k=>$v) { - xmlwriter_write_element($writer,$k,$v); - } - } else { - xmlwriter_write_element($writer,$key,$value); - } - } - xmlwriter_end_element($writer); - } - xmlwriter_end_element($writer); - - }elseif($dimension=='3') { - xmlwriter_start_element($writer,'data'); - foreach($data as $entrykey=>$entry) { - xmlwriter_start_element($writer,$tag); - if(!empty($tagattribute)) { - xmlwriter_write_attribute($writer,'details',$tagattribute); - } - foreach($entry as $key=>$value) { - if(is_array($value)){ - xmlwriter_start_element($writer,$entrykey); - foreach($value as $k=>$v) { - xmlwriter_write_element($writer,$k,$v); - } - xmlwriter_end_element($writer); - } else { - xmlwriter_write_element($writer,$key,$value); - } - } - xmlwriter_end_element($writer); - } - xmlwriter_end_element($writer); - }elseif($dimension=='dynamic') { - xmlwriter_start_element($writer,'data'); - OC_OCS::toxml($writer,$data,'comment'); - xmlwriter_end_element($writer); - } - - xmlwriter_end_element($writer); - - xmlwriter_end_document( $writer ); - $txt.=xmlwriter_output_memory( $writer ); - unset($writer); - return($txt); - } - } - - public static function toXml($writer,$data,$node) { - foreach($data as $key => $value) { - if (is_numeric($key)) { - $key = $node; - } - if (is_array($value)){ - xmlwriter_start_element($writer,$key); - OC_OCS::toxml($writer,$value,$node); - xmlwriter_end_element($writer); - }else{ - xmlwriter_write_element($writer,$key,$value); - } - - } - } - - - - - /** - * return the config data of this server - * @param string $format - * @return string xml/json - */ - public static function apiConfig($parameters) { - $format = $parameters['format'] ? $parameters['format'] : OC_OCS::readdata('format','text'); - $xml['version']='1.5'; - $xml['website']='ownCloud'; - $xml['host']=OCP\Util::getServerHost(); - $xml['contact']=''; - $xml['ssl']='false'; - echo(OC_OCS::generatexml($format,'ok',100,'',$xml,'config','',1)); - } - - - /** - * check if the provided login/apikey/password is valid - * @param string $format - * @param string $login - * @param string $passwd - * @return string xml/json - */ - private static function personCheck($format,$login,$passwd) { - if($login<>''){ - if(OC_User::login($login,$passwd)){ - $xml['person']['personid']=$login; - echo(OC_OCS::generatexml($format,'ok',100,'',$xml,'person','check',2)); - }else{ - echo(OC_OCS::generatexml($format,'failed',102,'login not valid')); - } - }else{ - echo(OC_OCS::generatexml($format,'failed',101,'please specify all mandatory fields')); - } - } - - - - // ACTIVITY API ############################################# - - /** - * get my activities - * @param string $format - * @param string $page - * @param string $pagesize - * @return string xml/json - */ - private static function activityGet($format,$page,$pagesize) { - $user=OC_OCS::checkpassword(); - - //TODO - - $txt=OC_OCS::generatexml($format,'ok',100,'',$xml,'activity','full',2,$totalcount,$pagesize); - echo($txt); - } - - /** - * submit a activity - * @param string $format - * @param string $message - * @return string xml/json - */ - private static function activityPut($format,$message) { - // not implemented in ownCloud - OC_OCS::checkpassword(); - echo(OC_OCS::generatexml($format,'ok',100,'')); - } - - // PRIVATEDATA API ############################################# - - /** - * get private data and create the xml for ocs - * @param string $format - * @param string $app - * @param string $key - * @return string xml/json - */ - private static function privateDataGet($format,$app="",$key="") { - $user=OC_OCS::checkpassword(); - $result=OC_OCS::getData($user,$app,$key); - $xml=array(); - foreach($result as $i=>$log) { - $xml[$i]['key']=$log['key']; - $xml[$i]['app']=$log['app']; - $xml[$i]['value']=$log['value']; - } - - - $txt=OC_OCS::generatexml($format, 'ok', 100, '', $xml, 'privatedata', 'full', 2, count($xml), 0);//TODO: replace 'privatedata' with 'attribute' once a new libattice has been released that works with it - echo($txt); - } - - /** - * set private data referenced by $key to $value and generate the xml for ocs - * @param string $format - * @param string $app - * @param string $key - * @param string $value - * @return string xml/json - */ + /** + * reads input date from get/post/cookies and converts the date to a special data-type + * + * @param string HTTP method to read the key from + * @param string Parameter to read + * @param string Variable type to format data + * @param mixed Default value to return if the key is not found + * @return mixed Data or if the key is not found and no default is set it will exit with a 400 Bad request + */ + public static function readData($method, $key, $type = 'raw', $default = null) { + if ($method == 'get') { + if (isset($_GET[$key])) { + $data = $_GET[$key]; + } else if (isset($default)) { + return $default; + } else { + $data = false; + } + } else if ($method == 'post') { + if (isset($_POST[$key])) { + $data = $_POST[$key]; + } else if (isset($default)) { + return $default; + } else { + $data = false; + } + } + if ($data === false) { + echo self::generateXml('', 'fail', 400, 'Bad request. Please provide a valid '.$key); + exit(); + } else { + // NOTE: Is the raw type necessary? It might be a little risky without sanitization + if ($type == 'raw') return $data; + elseif ($type == 'text') return OC_Util::sanitizeHTML($data); + elseif ($type == 'int') return (int) $data; + elseif ($type == 'float') return (float) $data; + elseif ($type == 'array') return OC_Util::sanitizeHTML($data); + else return OC_Util::sanitizeHTML($data); + } + } + + /** + main function to handle the REST request + **/ + public static function handle() { + // overwrite the 404 error page returncode + header("HTTP/1.0 200 OK"); + + + if($_SERVER['REQUEST_METHOD'] == 'GET') { + $method='get'; + }elseif($_SERVER['REQUEST_METHOD'] == 'PUT') { + $method='put'; + parse_str(file_get_contents("php://input"),$put_vars); + }elseif($_SERVER['REQUEST_METHOD'] == 'POST') { + $method='post'; + }else{ + echo('internal server error: method not supported'); + exit(); + } + + // preprocess url + $url = strtolower($_SERVER['REQUEST_URI']); + $format = self::readData($method, 'format', 'text', ''); + + $router = new OC_Router(); + $router->useCollection('ocs'); + // CONFIG + $router->create('config', '/config.{format}') + ->defaults(array('format' => $format)) + ->action('OC_OCS', 'apiConfig') + ->requirements(array('format'=>'xml|json')); + + // PERSON + $router->create('person_check', '/person/check.{format}') + ->post() + ->defaults(array('format' => $format)) + ->action(function ($parameters) { + $format = $parameters['format']; + $login = OC_OCS::readData('post', 'login', 'text'); + $passwd = OC_OCS::readData('post', 'password', 'text'); + OC_OCS::personCheck($format,$login,$passwd); + }) + ->requirements(array('format'=>'xml|json')); + + // ACTIVITY + // activityget - GET ACTIVITY page,pagesize als urlparameter + $router->create('activity_get', '/activity.{format}') + ->defaults(array('format' => $format)) + ->action(function ($parameters) { + $format = $parameters['format']; + $page = OC_OCS::readData('get', 'page', 'int', 0); + $pagesize = OC_OCS::readData('get', 'pagesize', 'int', 10); + if($pagesize<1 or $pagesize>100) $pagesize=10; + OC_OCS::activityGet($format, $page, $pagesize); + }) + ->requirements(array('format'=>'xml|json')); + // activityput - POST ACTIVITY + $router->create('activity_put', '/activity.{format}') + ->post() + ->defaults(array('format' => $format)) + ->action(function ($parameters) { + $format = $parameters['format']; + $message = OC_OCS::readData('post', 'message', 'text'); + OC_OCS::activityPut($format,$message); + }) + ->requirements(array('format'=>'xml|json')); + + // PRIVATEDATA + // get - GET DATA + $router->create('privatedata_get', + '/privatedata/getattribute/{app}/{key}.{format}') + ->defaults(array('app' => '', 'key' => '', 'format' => $format)) + ->action(function ($parameters) { + $format = $parameters['format']; + $app = addslashes(strip_tags($parameters['app'])); + $key = addslashes(strip_tags($parameters['key'])); + OC_OCS::privateDataGet($format, $app, $key); + }) + ->requirements(array('format'=>'xml|json')); + // set - POST DATA + $router->create('privatedata_set', + '/privatedata/setattribute/{app}/{key}.{format}') + ->post() + ->defaults(array('format' => $format)) + ->action(function ($parameters) { + $format = $parameters['format']; + $app = addslashes(strip_tags($parameters['app'])); + $key = addslashes(strip_tags($parameters['key'])); + $value=OC_OCS::readData('post', 'value', 'text'); + OC_OCS::privateDataSet($format, $app, $key, $value); + }) + ->requirements(array('format'=>'xml|json')); + // delete - POST DATA + $router->create('privatedata_delete', + '/privatedata/deleteattribute/{app}/{key}.{format}') + ->post() + ->defaults(array('format' => $format)) + ->action(function ($parameters) { + $format = $parameters['format']; + $app = addslashes(strip_tags($parameters['app'])); + $key = addslashes(strip_tags($parameters['key'])); + OC_OCS::privateDataDelete($format, $app, $key); + }) + ->requirements(array('format'=>'xml|json')); + + // CLOUD + // quotaget + $router->create('quota_get', + '/cloud/user/{user}.{format}') + ->defaults(array('format' => $format)) + ->action(function ($parameters) { + $format = $parameters['format']; + $user = $parameters['user']; + OC_OCS::quotaGet($format, $user); + }) + ->requirements(array('format'=>'xml|json')); + // quotaset + $router->create('quota_set', + '/cloud/user/{user}.{format}') + ->post() + ->defaults(array('format' => $format)) + ->action(function ($parameters) { + $format = $parameters['format']; + $user = $parameters['user']; + $quota = self::readData('post', 'quota', 'int'); + OC_OCS::quotaSet($format, $user, $quota); + }) + ->requirements(array('format'=>'xml|json')); + + +// add more calls here +// please document all the call in the draft spec +// http://www.freedesktop.org/wiki/Specifications/open-collaboration-services-1.7#CLOUD + +// TODO: +// users +// groups +// bookmarks +// sharing +// versioning +// news (rss) + try { + $router->match($_SERVER['PATH_INFO']); + } catch (ResourceNotFoundException $e) { + $txt='Invalid query, please check the syntax. API specifications are here: http://www.freedesktop.org/wiki/Specifications/open-collaboration-services. DEBUG OUTPUT:'."\n"; + $txt.=OC_OCS::getdebugoutput(); + echo(OC_OCS::generatexml($format,'failed',999,$txt)); + } + exit(); + } + + /** + * generated some debug information to make it easier to find faild API calls + * @return debug data string + */ + private static function getDebugOutput() { + $txt=''; + $txt.="debug output:\n"; + if(isset($_SERVER['REQUEST_METHOD'])) $txt.='http request method: '.$_SERVER['REQUEST_METHOD']."\n"; + if(isset($_SERVER['REQUEST_URI'])) $txt.='http request uri: '.$_SERVER['REQUEST_URI']."\n"; + if(isset($_GET)) foreach($_GET as $key=>$value) $txt.='get parameter: '.$key.'->'.$value."\n"; + if(isset($_POST)) foreach($_POST as $key=>$value) $txt.='post parameter: '.$key.'->'.$value."\n"; + return($txt); + } + + /** + * checks if the user is authenticated + * checks the IP whitlist, apikeys and login/password combination + * if $forceuser is true and the authentication failed it returns an 401 http response. + * if $forceuser is false and authentification fails it returns an empty username string + * @param bool $forceuser + * @return username string + */ + private static function checkPassword($forceuser=true) { + //valid user account ? + if(isset($_SERVER['PHP_AUTH_USER'])) $authuser=$_SERVER['PHP_AUTH_USER']; else $authuser=''; + if(isset($_SERVER['PHP_AUTH_PW'])) $authpw=$_SERVER['PHP_AUTH_PW']; else $authpw=''; + + if(empty($authuser)) { + if($forceuser){ + header('WWW-Authenticate: Basic realm="your valid user account or api key"'); + header('HTTP/1.0 401 Unauthorized'); + exit; + }else{ + $identifieduser=''; + } + }else{ + if(!OC_User::login($authuser,$authpw)){ + if($forceuser){ + header('WWW-Authenticate: Basic realm="your valid user account or api key"'); + header('HTTP/1.0 401 Unauthorized'); + exit; + }else{ + $identifieduser=''; + } + }else{ + $identifieduser=$authuser; + } + } + + return($identifieduser); + } + + + /** + * generates the xml or json response for the API call from an multidimenional data array. + * @param string $format + * @param string $status + * @param string $statuscode + * @param string $message + * @param array $data + * @param string $tag + * @param string $tagattribute + * @param int $dimension + * @param int $itemscount + * @param int $itemsperpage + * @return string xml/json + */ + private static function generateXml($format,$status,$statuscode,$message,$data=array(),$tag='',$tagattribute='',$dimension=-1,$itemscount='',$itemsperpage='') { + if($format=='json') { + $json=array(); + $json['status']=$status; + $json['statuscode']=$statuscode; + $json['message']=$message; + $json['totalitems']=$itemscount; + $json['itemsperpage']=$itemsperpage; + $json['data']=$data; + return(json_encode($json)); + }else{ + $txt=''; + $writer = xmlwriter_open_memory(); + xmlwriter_set_indent( $writer, 2 ); + xmlwriter_start_document($writer ); + xmlwriter_start_element($writer,'ocs'); + xmlwriter_start_element($writer,'meta'); + xmlwriter_write_element($writer,'status',$status); + xmlwriter_write_element($writer,'statuscode',$statuscode); + xmlwriter_write_element($writer,'message',$message); + if($itemscount<>'') xmlwriter_write_element($writer,'totalitems',$itemscount); + if(!empty($itemsperpage)) xmlwriter_write_element($writer,'itemsperpage',$itemsperpage); + xmlwriter_end_element($writer); + if($dimension=='0') { + // 0 dimensions + xmlwriter_write_element($writer,'data',$data); + + }elseif($dimension=='1') { + xmlwriter_start_element($writer,'data'); + foreach($data as $key=>$entry) { + xmlwriter_write_element($writer,$key,$entry); + } + xmlwriter_end_element($writer); + + }elseif($dimension=='2') { + xmlwriter_start_element($writer,'data'); + foreach($data as $entry) { + xmlwriter_start_element($writer,$tag); + if(!empty($tagattribute)) { + xmlwriter_write_attribute($writer,'details',$tagattribute); + } + foreach($entry as $key=>$value) { + if(is_array($value)){ + foreach($value as $k=>$v) { + xmlwriter_write_element($writer,$k,$v); + } + } else { + xmlwriter_write_element($writer,$key,$value); + } + } + xmlwriter_end_element($writer); + } + xmlwriter_end_element($writer); + + }elseif($dimension=='3') { + xmlwriter_start_element($writer,'data'); + foreach($data as $entrykey=>$entry) { + xmlwriter_start_element($writer,$tag); + if(!empty($tagattribute)) { + xmlwriter_write_attribute($writer,'details',$tagattribute); + } + foreach($entry as $key=>$value) { + if(is_array($value)){ + xmlwriter_start_element($writer,$entrykey); + foreach($value as $k=>$v) { + xmlwriter_write_element($writer,$k,$v); + } + xmlwriter_end_element($writer); + } else { + xmlwriter_write_element($writer,$key,$value); + } + } + xmlwriter_end_element($writer); + } + xmlwriter_end_element($writer); + }elseif($dimension=='dynamic') { + xmlwriter_start_element($writer,'data'); + OC_OCS::toxml($writer,$data,'comment'); + xmlwriter_end_element($writer); + } + + xmlwriter_end_element($writer); + + xmlwriter_end_document( $writer ); + $txt.=xmlwriter_output_memory( $writer ); + unset($writer); + return($txt); + } + } + + public static function toXml($writer,$data,$node) { + foreach($data as $key => $value) { + if (is_numeric($key)) { + $key = $node; + } + if (is_array($value)){ + xmlwriter_start_element($writer,$key); + OC_OCS::toxml($writer,$value,$node); + xmlwriter_end_element($writer); + }else{ + xmlwriter_write_element($writer,$key,$value); + } + } + } + + + + + /** + * return the config data of this server + * @param string $format + * @return string xml/json + */ + public static function apiConfig($parameters) { + $format = $parameters['format']; + $user=OC_OCS::checkpassword(false); + $url=substr(OCP\Util::getServerHost().$_SERVER['SCRIPT_NAME'],0,-11).''; + + $xml['version']='1.7'; + $xml['website']='ownCloud'; + $xml['host']=OCP\Util::getServerHost(); + $xml['contact']=''; + $xml['ssl']='false'; + echo(OC_OCS::generatexml($format,'ok',100,'',$xml,'config','',1)); + } + + + /** + * check if the provided login/apikey/password is valid + * @param string $format + * @param string $login + * @param string $passwd + * @return string xml/json + */ + private static function personCheck($format,$login,$passwd) { + if($login<>''){ + if(OC_User::login($login,$passwd)){ + $xml['person']['personid']=$login; + echo(OC_OCS::generatexml($format,'ok',100,'',$xml,'person','check',2)); + }else{ + echo(OC_OCS::generatexml($format,'failed',102,'login not valid')); + } + }else{ + echo(OC_OCS::generatexml($format,'failed',101,'please specify all mandatory fields')); + } + } + + + + // ACTIVITY API ############################################# + + /** + * get my activities + * @param string $format + * @param string $page + * @param string $pagesize + * @return string xml/json + */ + private static function activityGet($format,$page,$pagesize) { + $user=OC_OCS::checkpassword(); + + //TODO + + $txt=OC_OCS::generatexml($format,'ok',100,'',$xml,'activity','full',2,$totalcount,$pagesize); + echo($txt); + } + + /** + * submit a activity + * @param string $format + * @param string $message + * @return string xml/json + */ + private static function activityPut($format,$message) { + // not implemented in ownCloud + $user=OC_OCS::checkpassword(); + echo(OC_OCS::generatexml($format,'ok',100,'')); + } + + // PRIVATEDATA API ############################################# + + /** + * get private data and create the xml for ocs + * @param string $format + * @param string $app + * @param string $key + * @return string xml/json + */ + private static function privateDataGet($format,$app="",$key="") { + $user=OC_OCS::checkpassword(); + $result=OC_OCS::getData($user,$app,$key); + $xml=array(); + foreach($result as $i=>$log) { + $xml[$i]['key']=$log['key']; + $xml[$i]['app']=$log['app']; + $xml[$i]['value']=$log['value']; + } + + + $txt=OC_OCS::generatexml($format, 'ok', 100, '', $xml, 'privatedata', 'full', 2, count($xml), 0);//TODO: replace 'privatedata' with 'attribute' once a new libattice has been released that works with it + echo($txt); + } + + /** + * set private data referenced by $key to $value and generate the xml for ocs + * @param string $format + * @param string $app + * @param string $key + * @param string $value + * @return string xml/json + */ private static function privateDataSet($format, $app, $key, $value) { $user=OC_OCS::checkpassword(); if(OC_OCS::setData($user,$app,$key,$value)){ @@ -529,4 +576,71 @@ class OC_OCS { public static function deleteData($user, $app, $key) { return OC_Preferences::deleteKey($user,$app,$key); } + + + // CLOUD API ############################################# + + /** + * get the quota of a user + * @param string $format + * @param string $user + * @return string xml/json + */ + private static function quotaGet($format,$user) { + $login=OC_OCS::checkpassword(); + if(OC_Group::inGroup($login, 'admin') or ($login==$user)) { + + if(OC_User::userExists($user)){ + // calculate the disc space + $user_dir = '/'.$user.'/files'; + OC_Filesystem::init($user_dir); + $rootInfo=OC_FileCache::get(''); + $sharedInfo=OC_FileCache::get('/Shared'); + $used=$rootInfo['size']-$sharedInfo['size']; + $free=OC_Filesystem::free_space(); + $total=$free+$used; + if($total==0) $total=1; // prevent division by zero + $relative=round(($used/$total)*10000)/100; + + $xml=array(); + $xml['quota']=$total; + $xml['free']=$free; + $xml['used']=$used; + $xml['relative']=$relative; + + $txt=OC_OCS::generatexml($format, 'ok', 100, '', $xml, 'cloud', 'full', 1, count($xml), 0); + echo($txt); + }else{ + echo self::generateXml('', 'fail', 300, 'User does not exist'); + } + }else{ + echo self::generateXml('', 'fail', 300, 'You don´t have permission to access this ressource.'); + } + } + + /** + * set the quota of a user + * @param string $format + * @param string $user + * @param string $quota + * @return string xml/json + */ + private static function quotaSet($format,$user,$quota) { + $login=OC_OCS::checkpassword(); + if(OC_Group::inGroup($login, 'admin')) { + + // todo + // not yet implemented + // edit logic here + error_log('OCS call: user:'.$user.' quota:'.$quota); + + $xml=array(); + $txt=OC_OCS::generatexml($format, 'ok', 100, '', $xml, 'cloud', 'full', 1, count($xml), 0); + echo($txt); + }else{ + echo self::generateXml('', 'fail', 300, 'You don´t have permission to access this ressource.'); + } + } + + } diff --git a/lib/user.php b/lib/user.php index 356be4decf9..549319b3a77 100644 --- a/lib/user.php +++ b/lib/user.php @@ -104,11 +104,17 @@ class OC_User { break; } } - true; } /** + * remove all used backends + */ + public static function clearBackends(){ + self::$_usedBackends=array(); + } + + /** * @brief Create a new user * @param $uid The username of the user to create * @param $password The password of the new user diff --git a/lib/util.php b/lib/util.php index f35e5a18e42..0c563278cc5 100755 --- a/lib/util.php +++ b/lib/util.php @@ -77,13 +77,13 @@ class OC_Util { return '5 pre alpha'; } - /** - * get the current installed edition of ownCloud. There is the community edition that just returns an empty string and the enterprise edition that returns "Enterprise". - * @return string - */ - public static function getEditionString(){ - return ''; - } + /** + * get the current installed edition of ownCloud. There is the community edition that just returns an empty string and the enterprise edition that returns "Enterprise". + * @return string + */ + public static function getEditionString(){ + return ''; + } /** * add a javascript file @@ -131,12 +131,12 @@ class OC_Util { self::$headers[]=array('tag'=>$tag,'attributes'=>$attributes,'text'=>$text); } - /** - * formats a timestamp in the "right" way - * - * @param int timestamp $timestamp - * @param bool dateOnly option to ommit time from the result - */ + /** + * formats a timestamp in the "right" way + * + * @param int timestamp $timestamp + * @param bool dateOnly option to ommit time from the result + */ public static function formatDate( $timestamp,$dateOnly=false){ if(isset($_SESSION['timezone'])){//adjust to clients timezone if we know it $systemTimeZone = intval(date('O')); @@ -438,26 +438,25 @@ class OC_Util { } - /** - * Check if the htaccess file is working by creating a test file in the data directory and trying to access via http - */ - public static function ishtaccessworking() { - + /** + * Check if the htaccess file is working by creating a test file in the data directory and trying to access via http + */ + public static function ishtaccessworking() { // testdata $filename='/htaccesstest.txt'; $testcontent='testcontent'; // creating a test file - $testfile = OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ).'/'.$filename; - $fp = @fopen($testfile, 'w'); - @fwrite($fp, $testcontent); - @fclose($fp); + $testfile = OC_Config::getValue( "datadirectory", OC::$SERVERROOT."/data" ).'/'.$filename; + $fp = @fopen($testfile, 'w'); + @fwrite($fp, $testcontent); + @fclose($fp); // accessing the file via http - $url = OC_Helper::serverProtocol(). '://' . OC_Helper::serverHost() . OC::$WEBROOT.'/data'.$filename; - $fp = @fopen($url, 'r'); - $content=@fread($fp, 2048); - @fclose($fp); + $url = OC_Helper::serverProtocol(). '://' . OC_Helper::serverHost() . OC::$WEBROOT.'/data'.$filename; + $fp = @fopen($url, 'r'); + $content=@fread($fp, 2048); + @fclose($fp); // cleanup @unlink($testfile); @@ -467,13 +466,7 @@ class OC_Util { return(false); }else{ return(true); - } - - } - - - - + } } diff --git a/ocs/providers.php b/ocs/providers.php index f9cafd20b01..cc6de32266b 100644 --- a/ocs/providers.php +++ b/ocs/providers.php @@ -35,7 +35,9 @@ echo(' <termsofuse></termsofuse> <register></register> <services> - <activity ocsversion="1.5" /> + <config ocsversion="1.7" /> + <activity ocsversion="1.7" /> + <cloud ocsversion="1.7" /> </services> </provider> </providers> diff --git a/tests/lib/cache.php b/tests/lib/cache.php index bb5cfc6ee19..511999be956 100644 --- a/tests/lib/cache.php +++ b/tests/lib/cache.php @@ -42,6 +42,27 @@ abstract class Test_Cache extends UnitTestCase { $this->assertNull($this->instance->get('not_set'),'Unset value not equal to null'); $this->assertTrue($this->instance->remove('value1')); + $this->assertFalse($this->instance->hasKey('value1')); + } + + function testClear(){ + $value='ipsum lorum'; + $this->instance->set('1_value1',$value); + $this->instance->set('1_value2',$value); + $this->instance->set('2_value1',$value); + $this->instance->set('3_value1',$value); + + $this->assertTrue($this->instance->clear('1_')); + $this->assertFalse($this->instance->hasKey('1_value1')); + $this->assertFalse($this->instance->hasKey('1_value2')); + $this->assertTrue($this->instance->hasKey('2_value1')); + $this->assertTrue($this->instance->hasKey('3_value1')); + + $this->assertTrue($this->instance->clear()); + $this->assertFalse($this->instance->hasKey('1_value1')); + $this->assertFalse($this->instance->hasKey('1_value2')); + $this->assertFalse($this->instance->hasKey('2_value1')); + $this->assertFalse($this->instance->hasKey('3_value1')); } function testTTL(){ diff --git a/tests/lib/cache/file.php b/tests/lib/cache/file.php index c33c513fcff..28778efb68c 100644 --- a/tests/lib/cache/file.php +++ b/tests/lib/cache/file.php @@ -21,8 +21,10 @@ */ class Test_Cache_File extends Test_Cache { + private $user; + function skip() { - $this->skipUnless(OC_User::isLoggedIn()); + //$this->skipUnless(OC_User::isLoggedIn()); } public function setUp(){ @@ -39,10 +41,23 @@ class Test_Cache_File extends Test_Cache { OC_Filesystem::clearMounts(); OC_Filesystem::mount('OC_Filestorage_Temporary',array(),'/'); + OC_User::clearBackends(); + OC_User::useBackend(new OC_User_Dummy()); + + //login + OC_User::createUser('test', 'test'); + + $this->user=OC_User::getUser(); + OC_User::setUserId('test'); + //set up the users dir $rootView=new OC_FilesystemView(''); - $rootView->mkdir('/'.OC_User::getUser()); + $rootView->mkdir('/test'); $this->instance=new OC_Cache_File(); } + + public function tearDown(){ + OC_User::setUserId($this->user); + } } |