zentyal-core-2.3.21+quantal1/0000775000000000000000000000000012017102272012617 5ustar zentyal-core-2.3.21+quantal1/COPYING0000664000000000000000000004311012017102272013651 0ustar GNU GENERAL PUBLIC LICENSE Version 2, June 1991 Copyright (C) 1989, 1991 Free Software Foundation, Inc. 59 Temple Place, Suite 330, Boston, MA 02111-1307 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 Library 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. END OF TERMS AND CONDITIONS How to Apply These Terms to Your New Programs If you develop a new program, and you want it to be of the greatest possible use to the public, the best way to achieve this is to make it free software which everyone can redistribute and change under these terms. To do so, attach the following notices to the program. It is safest to attach them to the start of each source file to most effectively convey the exclusion of warranty; and each file should have at least the "copyright" line and a pointer to where the full notice is found. Copyright (C) This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. You should have received a copy of the GNU General Public License along with this program; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA Also add information on how to contact you by electronic and paper mail. If the program is interactive, make it output a short notice like this when it starts in an interactive mode: Gnomovision version 69, Copyright (C) year name of author Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'. This is free software, and you are welcome to redistribute it under certain conditions; type `show c' for details. The hypothetical commands `show w' and `show c' should show the appropriate parts of the General Public License. Of course, the commands you use may be called something other than `show w' and `show c'; they could even be mouse-clicks or menu items--whatever suits your program. You should also get your employer (if you work as a programmer) or your school, if any, to sign a "copyright disclaimer" for the program, if necessary. Here is a sample; alter the names: Yoyodyne, Inc., hereby disclaims all copyright interest in the program `Gnomovision' (which makes passes at compilers) written by James Hacker. , 1 April 1989 Ty Coon, President of Vice This General Public License does not permit incorporating your program into proprietary programs. If your program is a subroutine library, you may consider it more useful to permit linking proprietary applications with the library. If this is what you want to do, use the GNU Library General Public License instead of this License. zentyal-core-2.3.21+quantal1/debian/0000775000000000000000000000000012017102303014034 5ustar zentyal-core-2.3.21+quantal1/debian/zentyal-core.config0000664000000000000000000000235512017102272017651 0ustar #!/bin/bash set -e . /usr/share/debconf/confmodule # This will be replaced with debian/zentyal-core.scripts-common which includes # helper functions to set the password #SCRIPTSCOMMON# # ask zentyal port conf_port() { while true; do db_input high zentyal-core/port || true db_go || true db_get zentyal-core/port new_port="$RET" # check if the entry is valid port number nodigits="$(echo $new_port | sed 's/[[:digit:]]//g')" if [ -n "$nodigits" ]; then continue fi if [ $new_port -ge 65535 ] || [ $new_port -lt 1 ]; then continue; fi if ! check_port_available $new_port; then db_input high zentyal-core/port_used || true db_go || true db_get zentyal-core/port_used if [ "$RET" = "true" ]; then break; fi else break; fi done } # check non-existance of ebox user as a normal user EBOX_UID=`getent passwd ebox | cut -d: -f3` if [ -n "$EBOX_UID" ] && [ $EBOX_UID -ge 1000 ] then db_input high zentyal-core/user_exists || true db_go || true exit 1 fi if [ "$1" = reconfigure ] || [ -z "$2" ]; then conf_port fi exit 0 zentyal-core-2.3.21+quantal1/debian/zentyal-core.install0000664000000000000000000000010412017102272020040 0ustar debian/pam/zentyal /etc/pam.d debian/pam/zentyal.conf /etc/security zentyal-core-2.3.21+quantal1/debian/po/0000775000000000000000000000000012017102303014452 5ustar zentyal-core-2.3.21+quantal1/debian/po/templates.pot0000664000000000000000000000302312017102273017200 0ustar # SOME DESCRIPTIVE TITLE. # Copyright (C) YEAR THE PACKAGE'S COPYRIGHT HOLDER # This file is distributed under the same license as the PACKAGE package. # FIRST AUTHOR , YEAR. # #, fuzzy msgid "" msgstr "" "Project-Id-Version: zentyal-core\n" "Report-Msgid-Bugs-To: zentyal-core@packages.debian.org\n" "POT-Creation-Date: 2012-08-28 10:43+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" "Language: \n" "MIME-Version: 1.0\n" "Content-Type: text/plain; charset=CHARSET\n" "Content-Transfer-Encoding: 8bit\n" #. Type: string #. Description #: ../zentyal-core.templates:1001 msgid "Zentyal HTTPS port:" msgstr "" #. Type: string #. Description #: ../zentyal-core.templates:1001 msgid "" "Please enter the port which will be used by the Zentyal HTTPS server. Use an " "available port that is not being used by another service." msgstr "" #. Type: boolean #. Description #: ../zentyal-core.templates:2001 msgid "Do you want to continue?" msgstr "" #. Type: boolean #. Description #: ../zentyal-core.templates:2001 msgid "" "It seems that the port you have selected is already being used. You can " "continue anyway or enter a new port." msgstr "" #. Type: note #. Description #: ../zentyal-core.templates:3001 msgid "Installation failed" msgstr "" #. Type: note #. Description #: ../zentyal-core.templates:3001 msgid "" "There is already a user called 'ebox' in your machine. Please, remove it or " "choose a different name and try again." msgstr "" zentyal-core-2.3.21+quantal1/debian/po/POTFILES.in0000664000000000000000000000006112017102272016231 0ustar [type: gettext/rfc822deb] zentyal-core.templates zentyal-core-2.3.21+quantal1/debian/zentyal.80000664000000000000000000000224312017102272015621 0ustar .\" Hey, EMACS: -*- nroff -*- .TH EBOX 8 "Jun 13 2007" .\" Please adjust this date whenever revising the manpage. .\" .\" Some roff macros, for reference: .\" .nh disable hyphenation .\" .hy enable hyphenation .\" .ad l left justify .\" .ad b justify to both left and right margins .\" .nf disable filling .\" .fi enable filling .\" .br insert line break .\" .sp insert n+1 empty lines .\" for manpage-specific macros, see man(7) .SH NAME ebox \- start/stop ebox modules from command line .SH SYNOPSIS .B ebox .RI < start|stop|restart > .RI [ module ] " " < start|stop|restart > .SH DESCRIPTION This manual page documents briefly the .B ebox command. .PP .B ebox allows you to start/stop/restart ebox itself or one of its modules. .SH OPTIONS .TP .B start starts ebox, if it's not already running. .TP .B stop stops ebox, if it's running. .TP .B restart If ebox is running, restart it. If it's not running, start it. .TP If a .I module is given, the command only acts on a single module. .PP This manual page was written by Soren Hansen , for Ubuntu (but may be used by others). zentyal-core-2.3.21+quantal1/debian/ebox.loggerd.upstart0000664000000000000000000000005012017102272020037 0ustar exec /usr/share/zentyal/loggerd respawn zentyal-core-2.3.21+quantal1/debian/ebox.event-daemon.upstart0000664000000000000000000000014512017102272021003 0ustar exec /usr/bin/perl /usr/share/perl5/EBox/EventDaemon.pm >> /var/log/zentyal/events.err 2>&1 respawn zentyal-core-2.3.21+quantal1/debian/ebox.redis.upstart0000664000000000000000000000012112017102272017521 0ustar exec sudo -u ebox /usr/bin/redis-server /var/lib/zentyal/conf/redis.conf respawn zentyal-core-2.3.21+quantal1/debian/dirs0000664000000000000000000000014612017102272014726 0ustar var/log/zentyal var/lib/zentyal/conf/ssl-ca var/lib/zentyal/conf/backups var/lib/zentyal/purge-module zentyal-core-2.3.21+quantal1/debian/compat0000664000000000000000000000000212017102272015237 0ustar 5 zentyal-core-2.3.21+quantal1/debian/copyright0000664000000000000000000002032612017102272015777 0ustar This package was debianized by Zentyal Packaging Maintainers Fri, 20 Aug 2004 14:10:24 +0100. It was downloaded from http://www.zentyal.org/ Files: * Upstream Author: eBox Technologies S.L. Copyright (C) 2004-2012 eBox Technologies S.L. License: This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. On Debian systems, the complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL-2 file. Files: src/EBox/ThirdParty/* are derived from Apache::AuthCookie and its original authors are: Michael Schout Originally written by Eric Bartley versions 2.x were written by Ken Williams License: This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. On Debian systems, the complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL-2 file. Files: www/js/modalbox.js www/css/modalbox.css www/images/spinning.gif Copyright (C) 2006-2007 Andrey Okonetchnikov License: 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. Files: www/js/csshover.htc Copyright (C) 2004 Peter Nederlof License: CSSHover is free software, licensed under LGPL. Full license text can be found in /usr/share/common-licenses/LGPL. Files: www/js/carousel.js Copyright (C) 2009 Victor Stanciu License: 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. Files: www/js/excanvas.js Copyright (C) 2006 Google Inc. License: Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at http://www.apache.org/licenses/LICENSE-2.0 Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License. Files: www/js/table_orderer.js Copyright (c) 2007 Gregory SCHURGAST License: 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. Files: www/js/flotr.js Copyright (c) 2008 Bas Wenneker License: 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. Files: www/js/base64.js Copyright (C) 2002 The Caudium Group License: This program is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation; either version 2 of the License, or (at your option) any later version. This program 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 General Public License for more details. On Debian systems, the complete text of the GNU General Public License can be found in /usr/share/common-licenses/GPL-2 file. The Debian packaging is: (C) 2004-2011, Zentyal Packaging Maintainers and is licensed under the GPL, see `/usr/share/common-licenses/GPL-2'. zentyal-core-2.3.21+quantal1/debian/pam/0000775000000000000000000000000012017102272014616 5ustar zentyal-core-2.3.21+quantal1/debian/pam/zentyal0000664000000000000000000000041212017102272016224 0ustar # Zentyal authentication # Standard Un*x authentication. @include common-auth account required pam_access.so accessfile=/etc/security/zentyal.conf # Standard Un*x account and session @include common-account @include common-session @include common-password zentyal-core-2.3.21+quantal1/debian/pam/zentyal.conf0000664000000000000000000000017512017102272017156 0ustar # Zentyal configuration file for pam_access.so # only allow access to users in the sudo group + : (sudo) : ALL - : ALL : ALL zentyal-core-2.3.21+quantal1/debian/control0000664000000000000000000000315512017102272015450 0ustar Source: zentyal-core Section: web Priority: optional Maintainer: Zentyal Packaging Maintainers Uploaders: Jorge Salamero Sanz Build-Depends: zbuildtools, po-debconf Standards-Version: 3.9.2 Homepage: http://www.zentyal.org/ Vcs-Browser: http://git.zentyal.org/zentyal.git/tree/quantal:/main/core Vcs-Git: git://git.zentyal.org/zentyal.git Package: zentyal-core Architecture: all Replaces: ebox (<< 2.0.100) Breaks: ebox (<< 2.0.100) Depends: zentyal-common (>= 2.3), zentyal-common (<< 2.3.100), apache2-mpm-prefork | apache2-mpm-worker, openssl (>= 0.9.7e), libapache2-mod-perl2, libapache-singleton-perl, libsys-cpu-perl, libsys-cpuload-perl, libproc-processtable-perl, mysql-server-5.5, libdbd-mysql-perl, libclone-fast-perl, libnet-xmpp-perl, libfilesys-df-perl, libchart-perl, adduser, debconf, libfile-copy-recursive-perl, libfile-mmagic-perl, libxml-rss-perl, libtime-piece-perl, libjson-perl, libjson-xs-perl, libyaml-libyaml-perl, cron, anacron, libstring-shellquote-perl, liblinux-inotify2-perl, libredis-perl (>= 2:1.9260), redis-server (>= 2.2), libxml-simple-perl, libauthen-simple-pam-perl, libapt-pkg-perl, nmap, libnmap-parser-perl, libjson-rpc-perl, liblexical-persistence-perl, libterm-readline-gnu-perl, libtest-deep-perl, liburi-encode-perl, update-notifier-common, gpgv, ${misc:Depends} Description: Zentyal - Core Zentyal is a Linux small business server that can act as a Gateway, Unified Threat Manager, Office Server, Infrastructure Manager, Unified Communications Server or a combination of them. One single, easy-to-use platform to manage all your network services. zentyal-core-2.3.21+quantal1/debian/zentyal-core.manpages0000664000000000000000000000002112017102272020163 0ustar debian/zentyal.8 zentyal-core-2.3.21+quantal1/debian/dh_installscripts-common0000664000000000000000000000073012017102272021003 0ustar #!/usr/bin/perl -w # This code snippet has been taken from the openldap package use strict; use Debian::Debhelper::Dh_Lib; init(); foreach my $package (@{$dh{DOPACKAGES}}) { my $tmp=tmpdir($package); my $ext=pkgext($package); if (! -d "$tmp/DEBIAN") { next; } foreach my $file (qw{preinst postinst postrm config}) { my $f="$tmp/DEBIAN/$file"; if ($f) { complex_doit("perl -pe 's~#SCRIPTSCOMMON#~qx{cat debian/${ext}scripts-common}~eg' -i $f"); } } } zentyal-core-2.3.21+quantal1/debian/zentyal-core.lintian-overrides0000664000000000000000000000012212017102272022030 0ustar zentyal-core: init.d-script-uses-usr-interpreter etc/init.d/zentyal /usr/bin/perl zentyal-core-2.3.21+quantal1/debian/zentyal-core.scripts-common0000664000000000000000000000443012017102272021355 0ustar PORT=443 stop_ebox_processes() { stop ebox.redis 2>/dev/null || true OLD_APACHE_PID=`ps aux | grep 'apache2.* -f /var/lib/ebox' | grep -v grep | awk '/^root/{print $2;exit}'`; kill $OLD_APACHE_PID 2>/dev/null || true pkill -9 -u ebox || true } # This function uses the eBox API to fetch the configured apache port used # by eBox # # Usage: port=$(fetch_ebox_port) fetch_ebox_port() { set +e ret=$(perl -e ' use EBox; use EBox::Global; EBox::init(); my $apache = EBox::Global->modInstance('apache'); print $apache->port(); exit 0; ' 2> /dev/null ); if [ $? -eq 0 ]; then PORT=$ret; fi set -e } # This function is used to try guess if a given port is available. It tries # to connect to the port. Note that it does not distinguish if the port # is being already used by eBox. # # Usage: check_port_available port_number check_port_available() { check_port=$1 set +e perl -e ' use IO::Socket; my $port = $ARGV[0]; IO::Socket::INET->new( PeerAddr => "127.0.0.1", PeerPort => $port, Proto => "tcp", Timeout => 5) or exit 0; exit 1; ' $check_port 2> /dev/null; ret=$? set -e return $ret; } # This function uses the eBox API to set the apache port to be used by eBox. # # In case the current port and the new port are the same it returns without # modifying the current value. # # We have to do two things to set the port: # # Tell apache module its new port # Save changes in apache and services # # Usage: set_ebox_port new_port set_ebox_port() { db_get zentyal-core/port new_port=$RET fetch_ebox_port; if [ $new_port -eq $PORT ]; then return 0; fi set +e ret=$(perl -e ' use EBox; use EBox::Global; EBox::init(); my $port = $ARGV[0]; my $global = EBox::Global->getInstance(); my $apache = $global->modInstance('apache'); $apache->setPort($port); $apache->saveConfig(); if ($global->modExists('services')) { $global->modInstance('services')->saveConfig(); } ' $new_port); set -e } zentyal-core-2.3.21+quantal1/debian/zentyal-core.triggers0000664000000000000000000000010612017102272020222 0ustar interest zentyal-core interest /usr/lib/apache2 interest /usr/lib/ssl zentyal-core-2.3.21+quantal1/debian/source/0000775000000000000000000000000012017102272015341 5ustar zentyal-core-2.3.21+quantal1/debian/source/format0000664000000000000000000000001512017102272016550 0ustar 3.0 (native) zentyal-core-2.3.21+quantal1/debian/zentyal-core.logrotate0000664000000000000000000000111712017102272020377 0ustar /var/log/zentyal/error.log /var/log/zentyal/access.log /var/log/zentyal/zentyal.log /var/log/zentyal/redis-server.log /var/log/zentyal/events.log /var/log/zentyal/events.err { rotate 7 size 10M compress nocreate missingok delaycompress notifempty postrotate if [ -x /usr/sbin/invoke-rc.d ]; then \ invoke-rc.d zentyal apache restart > /dev/null; \ else \ /etc/init.d/zentyal apache restart > /dev/null; \ fi; \ endscript } zentyal-core-2.3.21+quantal1/debian/zentyal-core.postinst0000664000000000000000000000762412017102272020273 0ustar #!/bin/bash set -e . /usr/share/debconf/confmodule # This will be replaced with debian/zentyal-core.scripts-commmon which includes # helper functions to set the password #SCRIPTSCOMMON# #DEBHELPER# ZENTYAL_HOME=/var/lib/zentyal/ FIRST_FILE=$ZENTYAL_HOME/.first case "$1" in configure) ZENTYAL_LOGS_DIR=/var/log/zentyal ZENTYAL_BACKUPS_DIR=$ZENTYAL_HOME/conf/backups # create log directory test -d $ZENTYAL_LOGS_DIR || mkdir $ZENTYAL_LOGS_DIR chmod 750 $ZENTYAL_LOGS_DIR # create Zentyal apache certificates /usr/share/zentyal/create-certificate $ZENTYAL_HOME/conf/ssl > /dev/null 2>&1 # add the ebox group and user if ! getent group ebox > /dev/null 2>&1 then addgroup --system ebox # create the .first file only if we are on the first install touch $FIRST_FILE fi ENT_EBOX=`getent passwd ebox` || true if [ -z "$ENT_EBOX" ]; then adduser --system --home $ZENTYAL_HOME \ --disabled-password --ingroup ebox ebox adduser ebox adm elif [[ "$ENT_EBOX" =~ ":/var/lib/zentyal/:" ]] then true # user exists and has correct directory, do nothing else # override old Zentyal <= 2.0 home if the user ebox already exists chown ebox:ebox $ZENTYAL_HOME stop_ebox_processes usermod --home $ZENTYAL_HOME ebox > /dev/null 2>&1 fi # to allow PAM authentication adduser ebox shadow > /dev/null 2>&1 chown ebox:adm $ZENTYAL_LOGS_DIR chown -R ebox:adm $ZENTYAL_HOME/tmp chown -R ebox:adm $ZENTYAL_HOME/conf chown -R ebox:adm $ZENTYAL_HOME/log chown ebox:adm $ZENTYAL_BACKUPS_DIR chmod 700 $ZENTYAL_BACKUPS_DIR # create and set permissions for ebox.sid EBOX_SID="$ZENTYAL_HOME/conf/ebox.sid" if [ ! -e $EBOX_SID ]; then touch $EBOX_SID fi chown ebox:ebox $EBOX_SID chmod 0600 $EBOX_SID # add the stderr file needed by sudo STDERR_FILE=`perl -MEBox::Config -e'print EBox::Config::tmp() . 'stderr'; 1'`; touch ${STDERR_FILE} chmod 0600 ${STDERR_FILE} chown ebox:ebox ${STDERR_FILE} # add the dynamic-www- and downloads directories DYNAMIC_WWW_DIRS=$(perl -MEBox::Config -e'print EBox::Config::dynamicwww() ; print " " ; print join(" ", @{EBox::Config::dynamicwwwSubdirs()}); print " "; print EBox::Config::downloads; 1;'); for DIR in $DYNAMIC_WWW_DIRS; do mkdir -p $DIR chown -R ebox:ebox $DIR done # change owner of $ZENTYAL_HOME chown ebox:ebox $ZENTYAL_HOME # setup random redis password REDIS_PASS="$ZENTYAL_HOME/conf/redis.passwd" if [ ! -f $REDIS_PASS ]; then touch $REDIS_PASS chmod 0600 $REDIS_PASS chown ebox:ebox $REDIS_PASS tr -dc A-Za-z0-9 < /dev/urandom | head -c8 > $REDIS_PASS fi # sudo configuration /usr/share/zentyal/sudoers-friendly if [ -z "$2" ]; then # Set eBox port only if it's the first time we install set_ebox_port # Write redis configuration invoke-rc.d zentyal apache start || true fi # create logs & reports database /usr/share/zentyal/create-db # setup core modules /usr/share/zentyal/initial-setup sysinfo $2 /usr/share/zentyal/initial-setup --no-restart logs $2 /usr/share/zentyal/initial-setup --no-restart events $2 dpkg-trigger --no-await zentyal-core ;; triggered) # remove the menu cache rm -f $ZENTYAL_HOME/tmp/menucache # Call restart script (apache, logs, events) /usr/share/zentyal/restart-trigger rm -f $ZENTYAL_HOME/dpkg_running ;; esac db_stop exit 0 zentyal-core-2.3.21+quantal1/debian/zentyal-core.preinst0000664000000000000000000000041312017102272020061 0ustar #!/bin/bash set -e #DEBHELPER# SUDOERS=/etc/sudoers INCLUDE=" # sudoers.d included by Zentyal Server, read /etc/sudoers.d/README # #includedir /etc/sudoers.d" if ! grep -q "^#includedir /etc/sudoers.d" "$SUDOERS"; then echo "$INCLUDE" >> "$SUDOERS" fi exit 0 zentyal-core-2.3.21+quantal1/debian/rules0000775000000000000000000000074512017102272015127 0ustar #!/usr/bin/make -f include /usr/share/zbuildtools/1/rules/zentyal.mk DEB_DH_INSTALLINIT_ARGS = --no-start --init-script=zentyal install/zentyal-core:: test -e debian/zentyal-core.init || ln -s ../src/scripts/zentyal.init.d debian/zentyal-core.init rm -f $(CURDIR)/debian/ebox/var/lib/zentyal/conf/ebox.passwd binary-predeb/zentyal-core:: perl -w debian/dh_installscripts-common -p zentyal-core cleanbuilddir/zentyal-core:: rm -f debian/zentyal.init clean:: debconf-updatepo zentyal-core-2.3.21+quantal1/debian/zentyal-core.postrm0000664000000000000000000000135212017102272017724 0ustar #!/bin/bash set -e #SCRIPTSCOMMON# #DEBHELPER# case "$1" in purge) # logs database db_name="zentyal" db_user="zentyal" echo "DROP DATABASE $db_name; DROP USER '$db_user'@'localhost';" | mysql --defaults-file=/etc/mysql/debian.cnf rm -f /var/lib/zentyal/.db-created # delete apache certificates rm -f /var/lib/zentyal/conf/ssl.crt/ebox.cert /var/lib/zentyal/conf/ssl.key/ebox.key \ /var/lib/zentyal/conf/ssl.pem/ebox.pem # delete logs rm -rf /var/log/zentyal # delete ebox user stop_ebox_processes deluser --remove-home ebox # delete shared memory data rm -rf /run/shm/zentyal ;; esac exit 0 zentyal-core-2.3.21+quantal1/debian/links0000664000000000000000000000023512017102272015104 0ustar usr/share/javascript/prototype/prototype.js usr/share/zentyal/www/js/prototype.js usr/share/javascript/scriptaculous usr/share/zentyal/www/js/scriptaculous zentyal-core-2.3.21+quantal1/debian/zentyal-core.prerm0000664000000000000000000000024712017102272017527 0ustar #!/bin/bash set -e #DEBHELPER# # FIXME: check if this works properly if [ "$1" = remove ] || [ "$1" = purge ]; then invoke-rc.d zentyal stop || true fi exit 0 zentyal-core-2.3.21+quantal1/debian/zentyal-core.templates0000664000000000000000000000117312017102272020377 0ustar Template: zentyal-core/port Type: string Default: 443 _Description: Zentyal HTTPS port: Please enter the port which will be used by the Zentyal HTTPS server. Use an available port that is not being used by another service. Template: zentyal-core/port_used Type: boolean Default: true _Description: Do you want to continue? It seems that the port you have selected is already being used. You can continue anyway or enter a new port. Template: zentyal-core/user_exists Type: note _Description: Installation failed There is already a user called 'ebox' in your machine. Please, remove it or choose a different name and try again. zentyal-core-2.3.21+quantal1/debian/changelog0000664000000000000000000005527412017102272015730 0ustar zentyal-core (2.3.21+quantal1) quantal; urgency=low * New upstream release for Quantal -- Jorge Salamero Sanz Tue, 28 Aug 2012 10:03:33 +0200 zentyal-core (2.3.21) precise; urgency=low * New upstream release -- José A. Calvo Mon, 27 Aug 2012 13:24:19 +0200 zentyal-core (2.3.20) precise; urgency=low * New upstream release -- José A. Calvo Thu, 23 Aug 2012 19:44:14 +0200 zentyal-core (2.3.19) precise; urgency=low * New upstream release -- José A. Calvo Mon, 13 Aug 2012 11:40:17 +0200 zentyal-core (2.3.18) precise; urgency=low * New upstream release -- José A. Calvo Tue, 24 Jul 2012 01:47:21 +0200 zentyal-core (2.3.17) precise; urgency=low * New upstream release -- José A. Calvo Thu, 19 Jul 2012 06:48:55 +0200 zentyal-core (2.3.16) precise; urgency=low * New upstream release -- José A. Calvo Wed, 18 Jul 2012 21:28:34 +0200 zentyal-core (2.3.15) precise; urgency=low * New upstream release -- José A. Calvo Thu, 12 Jul 2012 14:35:00 +0200 zentyal-core (2.3.14) precise; urgency=low * New upstream release -- José A. Calvo Fri, 06 Jul 2012 11:06:07 +0200 zentyal-core (2.3.13) precise; urgency=low * New upstream release -- José A. Calvo Fri, 22 Jun 2012 03:27:54 +0200 zentyal-core (2.3.12) precise; urgency=low * New upstream release -- José A. Calvo Wed, 20 Jun 2012 11:18:21 +0200 zentyal-core (2.3.11) precise; urgency=low * New upstream release -- José A. Calvo Sun, 17 Jun 2012 13:53:22 +0200 zentyal-core (2.3.10) precise; urgency=low * New upstream release -- José A. Calvo Mon, 04 Jun 2012 11:12:03 +0200 zentyal-core (2.3.9) precise; urgency=low * New upstream release -- José A. Calvo Fri, 11 May 2012 09:32:07 -0700 zentyal-core (2.3.8) precise; urgency=low * New upstream release -- José A. Calvo Mon, 02 Apr 2012 17:51:50 +0200 zentyal-core (2.3.7) precise; urgency=low * New upstream release -- José A. Calvo Mon, 26 Mar 2012 20:03:00 +0200 zentyal-core (2.3.6) precise; urgency=low * New upstream release -- José A. Calvo Tue, 20 Mar 2012 02:56:39 +0100 zentyal-core (2.3.5) precise; urgency=low * New upstream release -- José A. Calvo Tue, 13 Mar 2012 11:56:35 +0100 zentyal-core (2.3.4) precise; urgency=low * New upstream release -- José A. Calvo Sat, 10 Mar 2012 04:22:35 +0100 zentyal-core (2.3.3) precise; urgency=low * New upstream release -- José A. Calvo Tue, 06 Mar 2012 11:57:19 +0100 zentyal-core (2.3.2) precise; urgency=low * New upstream release -- José A. Calvo Sun, 26 Feb 2012 19:40:10 +0100 zentyal-core (2.3.1) precise; urgency=low * New upstream release -- José A. Calvo Thu, 09 Feb 2012 18:08:49 +0100 zentyal-core (2.3) precise; urgency=low * New upstream release -- José A. Calvo Mon, 30 Jan 2012 01:42:31 +0100 zentyal-core (2.2.4) lucid; urgency=low * New upstream release -- José A. Calvo Fri, 25 Nov 2011 15:42:35 +0100 zentyal-core (2.2.3) lucid; urgency=low * New upstream release -- José A. Calvo Sat, 15 Oct 2011 03:33:02 +0200 zentyal-core (2.2.2) lucid; urgency=low * New upstream release -- José A. Calvo Sun, 02 Oct 2011 22:01:39 +0200 zentyal-core (2.2.1) lucid; urgency=low * New upstream release -- José A. Calvo Fri, 30 Sep 2011 13:43:48 +0200 zentyal-core (2.2) lucid; urgency=low * New upstream release -- José A. Calvo Tue, 13 Sep 2011 14:20:34 +0200 zentyal-core (2.1.34) lucid; urgency=low * New upstream release -- José A. Calvo Mon, 12 Sep 2011 20:33:26 +0200 zentyal-core (2.1.33) lucid; urgency=low * New upstream release -- José A. Calvo Sun, 11 Sep 2011 19:00:40 +0200 zentyal-core (2.1.32) lucid; urgency=low * New upstream release -- José A. Calvo Sat, 10 Sep 2011 19:12:48 +0200 zentyal-core (2.1.31) lucid; urgency=low * New upstream release -- José A. Calvo Fri, 09 Sep 2011 01:33:37 +0200 zentyal-core (2.1.30) lucid; urgency=low * New upstream release -- José A. Calvo Wed, 07 Sep 2011 17:41:20 +0200 zentyal-core (2.1.29) lucid; urgency=low * New upstream release -- José A. Calvo Thu, 01 Sep 2011 03:16:14 +0200 zentyal-core (2.1.28) lucid; urgency=low * New upstream release -- José A. Calvo Tue, 30 Aug 2011 17:25:16 +0200 zentyal-core (2.1.27) lucid; urgency=low * New upstream release -- José A. Calvo Thu, 25 Aug 2011 00:55:10 +0200 zentyal-core (2.1.26) lucid; urgency=low * New upstream release -- José A. Calvo Wed, 24 Aug 2011 03:18:36 +0200 zentyal-core (2.1.25) lucid; urgency=low * New upstream release -- José A. Calvo Mon, 15 Aug 2011 23:22:06 +0200 zentyal-core (2.1.24) lucid; urgency=low * New upstream release -- José A. Calvo Fri, 12 Aug 2011 16:17:54 +0200 zentyal-core (2.1.23) lucid; urgency=low * New upstream release -- José A. Calvo Thu, 28 Jul 2011 13:02:38 +0200 zentyal-core (2.1.22) lucid; urgency=low * New upstream release -- José A. Calvo Tue, 26 Jul 2011 22:23:32 +0200 zentyal-core (2.1.21) lucid; urgency=low * New upstream release -- José A. Calvo Mon, 25 Jul 2011 23:40:16 +0200 zentyal-core (2.1.20) lucid; urgency=low * New upstream release -- José A. Calvo Mon, 25 Jul 2011 11:54:42 +0200 zentyal-core (2.1.19) lucid; urgency=low * New upstream release -- José A. Calvo Fri, 22 Jul 2011 20:40:40 +0200 zentyal-core (2.1.18) lucid; urgency=low * New upstream release -- José A. Calvo Tue, 19 Jul 2011 14:00:29 +0200 zentyal-core (2.1.17) lucid; urgency=low * New upstream release -- José A. Calvo Wed, 06 Jul 2011 20:59:34 +0200 zentyal-core (2.1.16) lucid; urgency=low * New upstream release -- José A. Calvo Sat, 02 Jul 2011 14:14:22 +0200 zentyal-core (2.1.15) lucid; urgency=low * New upstream release -- José A. Calvo Thu, 30 Jun 2011 01:19:51 +0200 zentyal-core (2.1.14) lucid; urgency=low * New upstream release -- José A. Calvo Mon, 27 Jun 2011 21:52:24 +0200 zentyal-core (2.1.13) lucid; urgency=low * New upstream release -- José A. Calvo Mon, 27 Jun 2011 00:07:34 +0200 zentyal-core (2.1.12) lucid; urgency=low * New upstream release -- José A. Calvo Sat, 25 Jun 2011 00:35:35 +0200 zentyal-core (2.1.11) lucid; urgency=low * New upstream release -- José A. Calvo Thu, 23 Jun 2011 17:51:54 +0200 zentyal-core (2.1.10) lucid; urgency=low * New upstream release -- José A. Calvo Tue, 21 Jun 2011 18:34:34 +0200 zentyal-core (2.1.9) lucid; urgency=low * New upstream release -- José A. Calvo Mon, 20 Jun 2011 21:43:38 +0200 zentyal-core (2.1.8) lucid; urgency=low * New upstream release -- José A. Calvo Tue, 14 Jun 2011 14:22:22 +0200 zentyal-core (2.1.7) lucid; urgency=low * New upstream release -- José A. Calvo Fri, 10 Jun 2011 16:22:53 +0200 zentyal-core (2.1.6) lucid; urgency=low * New upstream release -- José A. Calvo Thu, 09 Jun 2011 10:36:08 +0200 zentyal-core (2.1.5) lucid; urgency=low * New upstream release -- José A. Calvo Mon, 06 Jun 2011 15:04:06 +0200 zentyal-core (2.1.4) lucid; urgency=low * New upstream release -- José A. Calvo Tue, 31 May 2011 16:49:10 +0200 zentyal-core (2.1.3) lucid; urgency=low * New upstream release -- José A. Calvo Tue, 10 May 2011 23:57:04 +0200 zentyal-core (2.1.2) lucid; urgency=low * New upstream release -- José A. Calvo Sat, 26 Feb 2011 23:16:54 +0100 zentyal-core (2.1.1) lucid; urgency=low * New upstream release -- José A. Calvo Thu, 24 Feb 2011 04:56:49 +0100 zentyal-core (2.1) lucid; urgency=low * New upstream release -- José A. Calvo Tue, 22 Feb 2011 03:11:33 +0100 ebox (2.0.14) lucid; urgency=low * New upstream release -- José A. Calvo Sun, 09 Jan 2011 19:26:42 +0100 ebox (2.0.13) lucid; urgency=low * New upstream release -- José A. Calvo Wed, 29 Dec 2010 03:01:16 +0100 ebox (2.0.12) lucid; urgency=low * New upstream release -- José A. Calvo Tue, 21 Dec 2010 22:25:17 +0100 ebox (2.0.11) lucid; urgency=low * New upstream release -- José A. Calvo Tue, 21 Dec 2010 00:43:24 +0100 ebox (2.0.10) lucid; urgency=low * New upstream release -- José A. Calvo Sun, 19 Dec 2010 16:03:26 +0100 ebox (2.0.9) lucid; urgency=low * New upstream release -- José A. Calvo Tue, 07 Dec 2010 15:11:19 +0100 ebox (2.0.8) lucid; urgency=low * New upstream release -- José A. Calvo Sun, 05 Dec 2010 13:30:35 +0100 ebox (2.0.7) lucid; urgency=low * New upstream release -- José A. Calvo Wed, 10 Nov 2010 11:19:06 +0100 ebox (2.0.6) lucid; urgency=low * New upstream release -- José A. Calvo Sun, 24 Oct 2010 22:14:00 +0200 ebox (2.0.5) lucid; urgency=low * New upstream release -- José A. Calvo Mon, 18 Oct 2010 17:41:43 +0200 ebox (2.0.4) lucid; urgency=low * New upstream release -- José A. Calvo Tue, 12 Oct 2010 18:52:31 +0200 ebox (2.0.3) lucid; urgency=low * New upstream release -- José A. Calvo Wed, 06 Oct 2010 15:24:49 +0200 ebox (2.0.2) lucid; urgency=low * New upstream release -- José A. Calvo Sat, 18 Sep 2010 15:18:38 +0200 ebox (2.0.1) lucid; urgency=low * New upstream release -- José A. Calvo Sun, 05 Sep 2010 13:28:45 +0200 ebox (2.0) lucid; urgency=low * New upstream release -- José A. Calvo Mon, 30 Aug 2010 21:51:57 +0200 ebox (1.5.14-0ubuntu1~ppa1~lucid1) lucid; urgency=low * New upstream release -- José A. Calvo Thu, 26 Aug 2010 16:02:15 +0200 ebox (1.5.13-0ubuntu1~ppa1~lucid1) lucid; urgency=low * New upstream release -- José A. Calvo Mon, 23 Aug 2010 12:15:35 +0200 ebox (1.5.12-0ubuntu1~ppa1~lucid1) lucid; urgency=low * New upstream release -- José A. Calvo Wed, 18 Aug 2010 17:24:56 +0200 ebox (1.5.11-0ubuntu1~ppa1~lucid1) lucid; urgency=low * New upstream release -- José A. Calvo Fri, 13 Aug 2010 11:37:14 +0200 ebox (1.5.10-0ubuntu1~ppa1~lucid1) lucid; urgency=low * New upstream release -- José A. Calvo Thu, 05 Aug 2010 12:16:01 +0200 ebox (1.5.9-0ubuntu1~ppa1~lucid1) lucid; urgency=low * New upstream release -- José A. Calvo Wed, 04 Aug 2010 15:48:15 +0200 ebox (1.5.8-0ubuntu1~ppa1~lucid1) lucid; urgency=low * New upstream release -- José A. Calvo Sun, 25 Jul 2010 20:37:21 +0200 ebox (1.5.7-0ubuntu1~ppa1~lucid1) lucid; urgency=low * New upstream release -- José A. Calvo Mon, 19 Jul 2010 12:13:05 +0200 ebox (1.5.6-0ubuntu1~ppa1~lucid1) lucid; urgency=low * New upstream release -- José A. Calvo Mon, 05 Jul 2010 16:42:59 +0200 ebox (1.5.5-0ubuntu1~ppa1~lucid1) lucid; urgency=low * New upstream release -- José A. Calvo Sat, 26 Jun 2010 21:46:46 +0200 ebox (1.5.4-0ubuntu1~ppa1~lucid1) lucid; urgency=low * New upstream release -- José A. Calvo Sun, 20 Jun 2010 20:37:30 +0200 ebox (1.5.3-0ubuntu1~ppa1~lucid1) lucid; urgency=low * New upstream release -- José A. Calvo Thu, 17 Jun 2010 00:54:15 +0200 ebox (1.5.2-0ubuntu1~ppa1~lucid1) lucid; urgency=low * New upstream release -- José A. Calvo Thu, 10 Jun 2010 16:31:34 +0200 ebox (1.5.1-0ubuntu1~ppa1~lucid1) lucid; urgency=low * New upstream release -- José A. Calvo Thu, 13 May 2010 01:34:16 +0200 ebox (1.5-0ubuntu1) lucid; urgency=low [Javier Uruen Val] * New usptream release (LP: #521799) * Drop debian/patches as upstream already ships them * Drop simple-patchsys * debian/control - Update description * Add debian/ebox.cron.daily -- Javier Uruen Val Sun, 07 Feb 2010 18:07:46 +0100 ebox (1.3.5-0ubuntu2) karmic; urgency=low [Javier Uruen Val] * debian/patches (LP: #451495) - Add 05_fix_lsb_default_values.patch - Add 06_fix_public_css_and_login_css_generation.patch -- Javier Uruen Val Tue, 13 Oct 2009 20:45:46 +0200 ebox (1.3.5-0ubuntu1) karmic; urgency=low [Javier Uruen Val] * New upstream release [LP: #411466] * cdbs/ebox.mk - GConf schemas are not used anymore - Remove SCHEMASPATH variable - Remove schemadir variable - Set execution permissions for /etc/ebox/hooks/* - Use new upstart directory and file naming convention * debian/control - Bump standards version - Bump libebox depenency - Add dependency on libtime-piece-perl, libjson-perl, libyaml-tiny-perl * debian/dirs - Fix var/log/ebox - Add var/lib/ebox/conf/ssl-ca * debian/ebox.cron.hourly - Send stderr and stdout to /dev/null * debian/ebox.postinst - Add set -e - Fix indentation - Do not pkill gconfd as it's not necessary anymore - Run ebox trigger - Create log consolidation tables - Migrate logs - Add missing db_stop * debian/ebox.prerm - Add set -e * debian/ebox.postrm - Add set -e * debian/patches - Drop dpatch + Drop 01_add_apache_authcookie (shipped by upstream) + Drop 02_dbus_gconf (shipped by upstream) + Drop 03_js_libraries (shipped by upstream) - Use simple-patchsys + Add 01_use_shipped_authcookie.patch + Add 02_upstart_fixes.patch + Add 03_json_api.patch + Add 04_remove_network_workaround.patch * debian/rules - Do not include debian/cdbs/gnome.mk * debian/watch - Change URL -- Javier Uruen Val Wed, 05 Aug 2009 12:29:43 +0200 ebox (0.12.4-0ubuntu1) jaunty; urgency=low * New upstream release. (LP: #318710, LP: #310045). * debian/control: - Bump standard version. - Depend on an apache2-mpm- rather then apache2 (LP: #225793). - Build depend on debconf-updatepo. * debian/rules: - Call debconf-updatepo from clean target. * debian/copyright: - Include section about apache-authcookie code. * debian/ebox.postrm: - Remove ebox log files (LP: #129738). * debian/ebox.prerm: - Stop ebox when the package is removed. (LP: #234912) * debian/watch: - add watch file. * debian/patches/01_add_apache_authcookie.dpatch - Patch to backport AuthCookie authentication for jaunty (LP: #255368). * debian/patches/02_dbus_gconf.dpatch - Patch to make gconf classes work with new gconf using dbus (LP: #314606). * debian/patches/03_js_libraries.dpatch - Patch to use javascript libraries that are already package for Jaunty -- Mathias Gug Mon, 26 Jan 2009 18:21:18 -0500 ebox (0.11.99-0ubuntu11) hardy; urgency=low * debian/patches/15_disable_bug_report.dpatch - Dont disclose bug information in bug reports. It might contain sensitive information. (LP: #219343) -- Chuck Short Mon, 21 Apr 2008 08:17:21 -0400 ebox (0.11.99-0ubuntu10) hardy; urgency=low * debian/patches/14_fix_backup_creation_mask.dpatch -- Chuck Short Thu, 10 Apr 2008 11:29:02 -0400 ebox (0.11.99-0ubuntu9) hardy; urgency=low * Fix permissions on /var/lib/zentyal/conf/ebox.sid. To prevent cookies from being read. -- Chuck Short Wed, 09 Apr 2008 10:51:36 -0400 ebox (0.11.99-0ubuntu8) hardy; urgency=low * debian/patches/11_progress_indicator_cleanup.dpatch - Clean up file descriptiors before running exec in progress indicator. * debian/patches/12_ebox_init.dpatch - use /lib/lsb/init-functions to log start/stop fucntions. * debian/patches/13_backup_configured_modules.dpatch - Backup only configured modules. * Add preinst script to fetch from eBox's apache port and set it in debconf. -- Chuck Short Mon, 07 Apr 2008 10:13:48 -0400 ebox (0.11.99-0ubuntu7) hardy; urgency=low * debian/patches/10_service_watcher.dpatch - Check to see if a service is up when it shouldn't be. * debian/ebox.postinst - Cosmetic changes to ebox.postinst - Wait for apache to stop completely. -- Chuck Short Mon, 31 Mar 2008 08:39:04 -0400 ebox (0.11.99-0ubuntu6) hardy; urgency=low * Add default value to ebox/port_used template. * Run db_input "|| true" to avoid issues with questions without default and noninteractive install. -- Chuck Short Thu, 27 Mar 2008 10:04:23 -0400 ebox (0.11.99-0ubuntu5) hardy; urgency=low * debian/patches/08_fix_apache_pid_file.dpatch - Store apache's pid file under eBox directory to not interfere with the main apache instance. * debian/patches/09_fix_apache_watcher.dpatch - Fix the state watcher by checking if apache port is accesible. * Ask user which port eBox apache must use in config using debconf. -- Chuck Short Wed, 26 Mar 2008 09:13:44 -0400 ebox (0.11.99-0ubuntu4) hardy; urgency=low * debian/patches/04_fix_disable_log_daemon.dpatch - Disable log daemon if requested by user * debian/patches/05_fix_event_link.dpatch - Fix symlink creation. * debian/patches/06_reduce_rss_items.dpatch - Reduce the number of RSS entries to 500. * debian/patches/07_intialise_log_statement.dpatch - Fix log queries. * debian/ebox.cron.hourly - Install in the right place. -- Chuck Short Mon, 24 Mar 2008 09:45:27 -0400 ebox (0.11.99-0ubuntu2) hardy; urgency=low * debian/control - Added dpatch. * debian/patches/01_fix_memory_leak_events.dpatch, debian/patches/02_fix_backup_parameter.dpatch, debian/patches/03_fix_apache_restart.dpatch: - Added patch from upstream to fix various issues. -- Chuck Short Tue, 11 Mar 2008 08:42:21 -0400 ebox (0.11.99-0ubuntu1) hardy; urgency=low * Added libfile-copy-recursive-perl to depends. -- Chuck Short Fri, 29 Feb 2008 09:40:52 -0500 ebox (0.11.99-0ubuntu1~ppa1) hardy; urgency=low * New upstream version. -- Chuck Short Wed, 27 Feb 2008 11:24:27 -0500 ebox (0.11.99-0ubuntu1~ppa2) hardy; urgency=low * Move progress.js from libebox to ebox -- Javier Uruen Val Tue, 26 Feb 2008 11:58:13 +0100 ebox (0.11.99-0ubuntu1~ppa1) hardy; urgency=low * New upstream release -- Javier Uruen Val Mon, 25 Feb 2008 12:47:13 +0100 ebox (0.11.99) unstable; urgency=low * New upstream release -- Enrique José Hernández Blasco Tue, 8 Jan 2008 16:14:38 +0100 ebox (0.11-0ubuntu1~ppa2) hardy; urgency=low * Use debconf to set admin password -- Javier Uruen Val Wed, 20 Feb 2008 18:57:29 +0100 ebox (0.11-0ubuntu1~ppa1) hardy; urgency=low * New upstream release -- Javier Uruen Val Wed, 28 Nov 2007 15:23:32 +0100 ebox (0.10.99) unstable; urgency=low * New upstream release -- Javier Uruen Val Thu, 01 Nov 2007 21:38:13 +0100 ebox (0.10) unstable; urgency=low * New upstream release -- Javier Uruen Val Wed, 10 Oct 2007 21:53:48 +0200 ebox (0.9.100) unstable; urgency=low * New upstream release -- Javier Uruen Val Mon, 03 Sep 2007 13:19:25 +0200 ebox (0.9.99) unstable; urgency=low * New upstream release -- Javier Amor Garcia Tue, 24 Jul 2007 12:26:07 +0200 ebox (0.9.3) unstable; urgency=low * New upstream release -- Javier Uruen Val Sun, 24 Jun 2007 16:38:47 +0200 ebox (0.9.2) unstable; urgency=low * New upstream release -- Javier Uruen Val Tue, 12 Jun 2007 18:59:26 +0200 ebox (0.9.1) unstable; urgency=low * New upstream release -- Enrique José Hernández Blasco Tue, 26 Jun 2007 17:22:39 +0200 ebox (0.9) unstable; urgency=low * New upstream release -- Javier Amor Garcia Wed, 21 Mar 2007 09:21:13 +0100 ebox (0.8.99) unstable; urgency=low * New upstream release -- Enrique Jos� Hern�ndez Blasco Fri, 16 Feb 2007 11:42:55 +0100 ebox (0.7.99) unstable; urgency=low * New upstream release -- Javier Amor Garcia Thu, 9 Nov 2006 10:56:04 +0100 ebox (0.7.1) unstable; urgency=low * New upstream release -- Daniel Baeyens Sicilia Wed, 22 Mar 2006 16:04:22 +0100 ebox (0.7.0.99-rc1+0.7.1-rc1) unstable; urgency=low * New upstream release -- Javier Uruen Val Tue, 17 Jan 2006 11:45:27 +0100 ebox (0.6-1) unstable; urgency=low * New upstream release -- Isaac Clerencia Thu, 27 Oct 2005 19:08:04 +0200 ebox (0.5.1) unstable; urgency=low * New upstream release -- Guillermo Ontañón Mon, 14 Mar 2005 14:32:15 +0100 ebox (0.5) unstable; urgency=low * New upstream release -- Isaac Clerencia Thu, 3 Mar 2005 19:55:13 +0100 ebox (0.4-3) unstable; urgency=low * SSL certs are now copied by Makefile * Added network detection capabilities -- Isaac Clerencia Mon, 17 Jan 2005 16:41:33 +0100 ebox (0.4-2) unstable; urgency=low * Added libapache-mod-ssl in Depends: -- Isaac Clerencia Sat, 4 Dec 2004 16:33:09 +0100 ebox (0.4-1) unstable; urgency=low * New upstream release -- Isaac Clerencia Fri, 3 Dec 2004 05:58:03 +0100 zentyal-core-2.3.21+quantal1/conf/0000775000000000000000000000000012017102272013544 5ustar zentyal-core-2.3.21+quantal1/conf/post-save/0000775000000000000000000000000012017102272015465 5ustar zentyal-core-2.3.21+quantal1/conf/post-save/README0000664000000000000000000000022312017102272016342 0ustar Any file added in this dir will be executed after Zentyal saves the changes. Remember to give execution permissions to your scripts with chmod +x zentyal-core-2.3.21+quantal1/conf/hooks/0000775000000000000000000000000012017102272014667 5ustar zentyal-core-2.3.21+quantal1/conf/hooks/template.postsetconf0000775000000000000000000000064512017102272021003 0ustar #!/bin/sh # This is a sample postsetconf script. # postsetconf scripts are run after the configuration for a given module is # written. The module will check if an executable file called # .presetconf exists in /etc/zentyal/hooks and will try to run it # Copy this file or create a script with the appropriate name if you want # to run some customization script after a module writes its configuration exit 0 zentyal-core-2.3.21+quantal1/conf/hooks/template.presetconf0000775000000000000000000000064512017102272020604 0ustar #!/bin/sh # This is a sample presetconf script. # presetconf scripts are run before the configuration for a given module is # written. The module will check if an executable file called # .presetconf exists in /etc/zentyal/hooks and will try to run it # Copy this file or create a script with the appropriate name if you want # to run some customization script before a module writes its configuration exit 0 zentyal-core-2.3.21+quantal1/conf/hooks/template.postservice0000775000000000000000000000103112017102272020770 0ustar #!/bin/sh # This is a sample postservice script. # postservice scripts are run after the services associated to a given service # module are run. The module will check if an executable file called # .postservice exists in /etc/zentyal/hooks and will try to run it. # The script will receive a command line argument indicating whether the # module is enabled (1) or not (0). # Copy this file or create a script with the appropriate name if you want # to run some customization script after a module runs its services. exit 0 zentyal-core-2.3.21+quantal1/conf/hooks/template.preservice0000775000000000000000000000103012017102272020570 0ustar #!/bin/sh # This is a sample preservice script. # preservice scripts are run before the services associated to a given service # module are run. The module will check if an executable file called # .preservice exists in /etc/zentyal/hooks and will try to run it. # The script will receive a command line argument indicating whether the # module is enabled (1) or not (0). # Copy this file or create a script with the appropriate name if you want # to run some customization script before a module runs its services. exit 0 zentyal-core-2.3.21+quantal1/conf/pre-save/0000775000000000000000000000000012017102272015266 5ustar zentyal-core-2.3.21+quantal1/conf/pre-save/README0000664000000000000000000000022412017102272016144 0ustar Any file added in this dir will be executed before Zentyal saves the changes. Remember to give execution permissions to your scripts with chmod +x zentyal-core-2.3.21+quantal1/conf/logs.conf0000664000000000000000000000304512017102272015361 0ustar # logs.conf - configuration file for Zentyal logs module. # # This file contains the most basic settings, most other stuff is configured # using the web interface. # # Everything after a '#' character is ignored # # All whitespace is ignored # # Config keys are set this way: # # key = value # # They may contain comments at the end: # # key = value # this is ignored eboxlogs_dbname = zentyal eboxlogs_dbuser = zentyal # Time to wait until the insertion of buffered log entries # Default: 10 (in seconds) multi_insert_interval = 10 # Disable consolidation for Cloud reports. Only useful # to increase performance in servers that are not going # to be subscribed to Zentyal Cloud or generate reports # Allowed values = [yes|no] disable_consolidation = yes # Wether to enable the sliced backup mode # Allowed values = [yes|no] eboxlogs_sliced_backup = no # Directory where slices are going to be stored # Recommended to set a remote directory # or the mount point of an external device # This directory needs to be manually created if not exits eboxlogs_sliced_backup_archive = /media/backup-slices # Duration of slices. Format: [number] days|weeks|months|years # Minimum value = 1 days eboxlogs_sliced_backup_period = 10 days # Number of slices archived in each archive operation eboxlogs_sliced_backup_archive_at_once = 3 # Wether to allow restore with unarchived slices (you will lose data) # Allowed values = [yes|no] eboxlogs_force_not_archived_restore = no # Wether to allow DB sliced restore without schema file eboxlogs_force_not_schema_sliced_restore = no zentyal-core-2.3.21+quantal1/conf/events.conf0000664000000000000000000000125512017102272015722 0ustar # events.conf - configuration file for Zentyal events module. # # This file contains the settings for the runit finisher which is intended # to be called after finishing every Zentyal managed service in order to check if the service # is restarted a lot sometimes within a fixed time. If it is so, the # Zentyal module which is charged of it will be stopped. # # Everything after a '#' character is ignored # # All whitespace is ignored # # Config keys are set this way: # # key = value # # They may contain comments at the end: # # key = value # this is ignored # Maximum restarting tries restart_max = 10 # Time interval when the tries will be done (in seconds) time_interval = 60 zentyal-core-2.3.21+quantal1/conf/core.conf0000664000000000000000000000173412017102272015350 0ustar # core.conf - configuration file for Zentyal core. # # This file contains the most basic settings, most other stuff is configured # using the web interface. # # Everything after a '#' character is ignored # # All whitespace is ignored # # Config keys are set this way: # # key = value # # They may contain comments at the end: # # key = value # this is ignored # Redis server port # If you change this value, you must manually restart the redis server # in two steps: # $ /etc/init.d/zentyal apache restart # write down the new configuration # $ restart ebox.redis # restart the daemon redis_port = 6380 # Ignore system updates in Dashboard widget #widget_ignore_updates = yes # Custom prefix for rebranding #custom_prefix = zentyal # Zentyal desktop services # For changes in this configuration to take effect you must run: # $ /etc/init.d/zentyal apache restart # write down the new configuration desktop_services_enabled = yes desktop_services_port = 6895 zentyal-core-2.3.21+quantal1/src/0000775000000000000000000000000012017102272013406 5ustar zentyal-core-2.3.21+quantal1/src/templates/0000775000000000000000000000000012017102272015404 5ustar zentyal-core-2.3.21+quantal1/src/templates/headerWithRefresh.mas0000664000000000000000000000146612017102272021520 0ustar <%args> $title $refreshTime => 60 $destination $favicon => '/favicon.ico' <% __('Zentyal') %> - <% $title %> zentyal-core-2.3.21+quantal1/src/templates/readOnlyForm.mas0000664000000000000000000000117012017102272020506 0ustar <%doc> This template is indicated to view the EBox::Model::DataForm::ReadOnly. It will show a form with the model description to be as edited all the time. The original overridden call at /ajax/modelViewer.mas <%flags> inherit => '/ajax/form.mas' <%args> $model $hasChanged $action => 'view' <%init> use EBox::Gettext; use EBox::Model::DataTable; <& PARENT:view, model => $model, hasChanged => $hasChanged, action => $action, &> <%doc> Method: buttons No buttons to show Overrides: /ajax/form.mas:buttons <%method buttons> zentyal-core-2.3.21+quantal1/src/templates/report/0000775000000000000000000000000012017102272016717 5ustar zentyal-core-2.3.21+quantal1/src/templates/report/raid.mas0000664000000000000000000001347412017102272020351 0ustar <%args> $array $raidInfo <%once> my %tabPrintableNames = ( unusedDevices => __('Unused devices'), ); <%init> use EBox::Gettext; my $unusedDevices; my @raidArrays; if (defined $raidInfo) { if ($array) { exists $raidInfo->{$array} or $m->abort( __x('Inexistent RAID array {a}', a => $array) ); } $unusedDevices = delete $raidInfo->{unusedDevices}; @raidArrays = sort keys %{ $raidInfo }; if (not $array) { $array = $raidArrays[0]; } if (@{ $unusedDevices } > 0) { push @raidArrays, 'unusedDevices'; # it must be the last } } % if (not defined $raidInfo) {

<% __('RAID is not enabled in this system.') %>

% return; % } <& .menuTabs, selected => $array, tabs => \@raidArrays &>
% if ($array ne 'unusedDevices') { <&.raid, dev => $array, properties => $raidInfo->{$array} &> % } else { <& .unusedDevices, devices => $unusedDevices &> % } <%def .menuTabs> <%args> $selected @tabs <%init>
% foreach my $tab (@tabs) { % my $printableTab = exists $tabPrintableNames{$tab} ? % $tabPrintableNames{$tab} : $tab; % if ($tab eq $selected) { <% $printableTab %> % } else { <% $printableTab %> % } % }
<%def .raid> <%args> $dev %properties <%init> my %raidTypePrintableValue = ( 'raid0' => 'RAID 0', 'raid1' => 'RAID 1', 'raid2' => 'RAID 2', 'raid3' => 'RAID 3', 'raid4' => 'RAID 4', 'raid4' => 'RAID 5', ); my %algorithmPrintableValue = ( 0 => __('left-asymmetric'), 1 => __('right-asymmetric'), 2 => __('left-symmetric'), 3 => __('right-symmetric'), ); # name, printableName, sub for message value my @raidProperties = ( [ 'state', __('Status'), sub { my ($compValue) = @_; my @values = split (', ', $compValue); my @printableValues = (); foreach my $value (@values) { if ( $value eq 'active' ) { push(@printableValues, __('active')); } elsif ( $value eq 'degraded' ) { push(@printableValues, __('degraded')); } elsif ( $value eq 'recovering' ) { push(@printableValues, __('recovering')); } elsif ( $value eq 'resyncing' ) { push(@printableValues, __('resyncing')); } elsif ( $value eq 'reshaping' ) { push(@printableValues, __('reshaping')); } elsif ( $value eq 'rebuilding' ) { push(@printableValues, __('rebuilding')); } elsif ( $value eq 'failed' ) { push(@printableValues, __('failed')); } } return join( ', ', @printableValues); } ], [ 'type', __('Type'), sub { my ($v) = @_; if (exists $raidTypePrintableValue{$v} ) { return $raidTypePrintableValue{$v} } else { return $v; } } ], ['activeDevices', __('Active RAID devices') ], ['activeDevicesNeeded', __('Active RAID devices needed') ], ['blocks', __('Size in blocks') ], ['chunkSize', __('Chunk size')], [ 'algorithm', __('Parity algorithm'), sub { my ($v) = @_; if (exists $algorithmPrintableValue{$v} ) { return $algorithmPrintableValue{$v} } else { return $v; } } ], ['bitmap', __('Bitmap')], ); my @rows; foreach (@raidProperties) { my ($prop, $printableName, $subValue_r) = @{ $_ }; exists $properties{$prop} or next; my $value = $properties{$prop}; if (defined $subValue_r) { $value = $subValue_r->($value); } push @rows, [$printableName => $value]; } my @titles = (__('Array properties'), ''); <& /presentationTable.mas, columnTitles => \@titles, rows => \@rows &> % if ($properties{operation} ne 'none') { <& .raidOperation, properties => \%properties &> % } <& .raidDevices, devices => $properties{raidDevices} &> <%def .raidOperation> <%args> %properties <%init> my %operationPrintableName = ( 'resync' => __('Resyncing array'), 'rebuild' => __('Rebuild array'), 'reshape' => __('Reshape array'), ); my $opPrintableName = exists $operationPrintableName{$properties{operation}} ? $operationPrintableName{$properties{operation}} : $properties{operation}; my @rows; push @rows, [ __('Operation type') => $opPrintableName ]; push @rows, [ __('Percentage completed') => $properties{operationPercentage} ]; push @rows, [ __('Estimated time left') => $properties{operationEstimatedTime} ]; push @rows, [ __('Operation speed') => $properties{operationSpeed} ]; my @titles = (__('Current operation'), ''); <& /presentationTable.mas, columnTitles => \@titles, rows => \@rows &> <%def .raidDevices> <%args> %devices <%init> my @titles = ( __('RAID device number'), __('Device'), __('State'), ); my %statePrintableValue = ( up => __('up'), failure => __('failure'), spare => __('spare'), failure_spare => __('Failure on spare'), ); my @rows; foreach my $number (sort keys %devices) { my @devRow; push @devRow, $number; push @devRow, $devices{$number}->{device}; my $state = $devices{$number}->{state}; push @devRow, $statePrintableValue{$state}; push @rows, \@devRow; } <& /presentationTable.mas, columnTitles => \@titles, rows => \@rows &> <%def .unusedDevices> <%args> @devices <%init> @devices or $m->abort(__('No unused RAID devices')); my @rows = map { [ $_ ] } @devices; my @titles = ( __('Unused RAID arrays')); <& /presentationTable.mas, columnTitles => \@titles, rows => \@rows &> zentyal-core-2.3.21+quantal1/src/templates/report/diskUsage.mas0000664000000000000000000000137212017102272021343 0ustar <%args> $partition $partitionAttr @partitions $chartUrl <%init> use EBox::Gettext;
% foreach (@partitions) { % if ($_ eq $partition) { <% $_ %> % } else { <% $_ %> % } % }

<& .partitionAttrs, $partitionAttr &> <%def .partitionAttrs> <%init> my ($attrs) = @_; my @rows; push @rows, [ __('Mounted on') => $attrs->{mountPoint} ]; push @rows, [ __('Type') => $attrs->{type} ]; push @rows, [__('Options') => $attrs->{options}]; my @titles = ( __('Partition Properties'), '' ); <& /presentationTable.mas, columnTitles => \@titles, rows => \@rows &> zentyal-core-2.3.21+quantal1/src/templates/title.mas0000664000000000000000000000175712017102272017241 0ustar <%args> $title => undef $crumbs => undef <%init> use EBox::Gettext; my $insideComposite = $m->notes('composite');
% if ( (not $insideComposite) and ($title or ($crumbs and @$crumbs))) { % if ($crumbs) { % for (my $i = 0; $i < scalar(@$crumbs); $i++) { % my $section = $crumbs->[$i]; % if ($i != scalar(@$crumbs) - 1) { <% $section->{title} %> % } else { <% $section->{title} %> % } % } % } elsif ($title) { <% $title %> % } <% __('show help')%> % }
zentyal-core-2.3.21+quantal1/src/templates/ajax/0000775000000000000000000000000012017102272016327 5ustar zentyal-core-2.3.21+quantal1/src/templates/ajax/selector.mas0000664000000000000000000000564612017102272020664 0ustar <%doc> This template creates a selector using a select entry which contains the selector entry to displayed the selected model. The displayed selector could be one of the following: if there are multiple instances of a model, selector is shown. Otherwise, the component name is shown. The load is done with JavaScript and AJAX requests. - composite - EBox::Model::Composite the dynamic composite - hasChanged - Boolean indicating whether the database has changed or not <%args> $composite $hasChanged => 0 <%init> use EBox::Gettext; my $components = $composite->components(); my @selectOptions; my $compName = $composite->name(); my $actionComposite = $composite->action('changeView'); % @selectOptions = sort { $a->{printableValue} cmp $b->{printableValue} } @selectOptions; % my $defaultComponent = (sort { $a->printableName() cmp $b->printableName() } @{$components})[0];
<% $composite->selectMessage() %> <& /input/select.mas, name => 'selection_' . $compName, options => \@selectOptions, id => 'selection_' . $compName, onchange => qq/selectComponentToHang( 'selectData_$compName', 'errorSelectData_$compName', 'selector_$compName', actions_$compName, 'loadingTable_$compName'); /, &>
<& $defaultComponent->Viewer(), model => $defaultComponent, hasChanged => $hasChanged, action => 'view' &>
zentyal-core-2.3.21+quantal1/src/templates/ajax/tablePager.mas0000664000000000000000000000702212017102272021100 0ustar <%args> $model $page $tpages <%init> use EBox::Gettext; my $tableName = $model->table()->{'tableName'}; my @sizeOptions; my @sizes = (10, 15, 20, 30, 50, 100, 200, 500); my $defaultSize = $model->defaultPageSize(); # if default size isn't in size options add it my $defaultSizeIncluded = grep { $_ == $defaultSize } @sizes; if ( not $defaultSizeIncluded) { push @sizes, $defaultSize; @sizes = sort { $a <=> $b }@sizes; } foreach my $size (@sizes) { push (@sizeOptions, { 'value' => $size, 'printableValue' => $size }); } <%perl> my $pageSizeId = $tableName . '_pageSizeId'; my $changeViewJS = $model->changeViewJS( changeType => "changeList", editId => "undefined", page => 0, isFilter => 1 ); <& /input/select.mas, 'name' => $tableName . '_pageSize', 'value' => $model->pageSize(), 'options' => \@sizeOptions, 'id' => $tableName . '_pageSize', 'onchange' => qq{setLoading('$pageSizeId'); $changeViewJS;return false} &> % my $pageStr = __('Page'); % my $ofStr = __('of'); % if ($tpages <= 0) { <% $pageStr %> <% $page + 1 %> % } else { <% $pageStr %> <% $page + 1 %> <% $ofStr %> <%$tpages + 1%> % } % my $pagerId = $tableName . '_pagerId'; % if ($page != 0) { % } else { % } % if ($page != $tpages) { % } else { % } zentyal-core-2.3.21+quantal1/src/templates/ajax/modelViewer.mas0000664000000000000000000002250512017102272021317 0ustar <%flags> inherit => undef <%doc> This template establishes the common things that every model viewer will have Parameters: model - the model to use its name hasChanged - Boolean indicating whether the model has changed or not action - String the action be performed. Only if action is equal to 'view' the whole model viewer will be loaded, any other action just the body will be updated. This behaviour could be enhanced just printing the updated version of needed. *(Optional)* Default value: 'view' <%args> $model $hasChanged $action => 'view' <%init> use EBox::Gettext; % if ( $action eq 'view' or $action eq 'presetUpdate') { <& SELF:view, model => $model, hasChanged => $hasChanged &> % } else { <& SELF:body, model => $model, onlyBody => 1, &> % } % # Anyway you should call the change menu sub-component to check if % # any change has been done <& SELF:changeMenu, hasChanged => $hasChanged &> <%doc> Method: view Prints the model viewer. This method must be called by every component which inherits from this one. The body is not displayed if the precondition is accomplished, if not a fail message is shown instead. Parameters: model - the model to view hasChanged - Boolean indicating whether the model has changed or not <%method view> <%args> $model $hasChanged <& SELF:header, model => $model &> % if ($model->precondition()) { <%perl> my $showBody = 1; my $noDataMsg; if (@{$model->ids()} == 0) { $noDataMsg = $model->noDataMsg(); my $actions = $model->table()->{'actions'}; $showBody = exists $actions->{add} ? $actions->{add} : 0; if ($showBody) { unless ($model->message) { $model->setMessage($noDataMsg); } } } % if ($showBody) {
<& SELF:body, model => $model, &>
% } elsif ($noDataMsg) {
<% $noDataMsg %>
% } % } else { % my $failMsg = $model->preconditionFailMsg(); % if ($failMsg) {
<% $failMsg %>
% } % } <%doc> Method: header Include everything that a model view should have Parameters: model - the model to view <%method header> <%args> $model <& SELF:headerScriptSection &>
<%method headerScriptSection> <%doc> Method: body Set the model viewer body. The message if any is also shown. Parameters: model - the model to view onlyBody - boolean indicating if just the body is updated *(Optional)* Default value: false <%method body> <%args> $model $onlyBody => 0 <%init> # Get the content from the first called template my $requestCaller = $onlyBody ? 1 : 2; my @childArgs = $m->caller_args($requestCaller); <%doc> % unless ( $model->pageTitle() ) { % my $viewCustomizer = $model->viewCustomizer(); % my $msg = $viewCustomizer ? $viewCustomizer->permanentMessage() : undef; % my $type = $viewCustomizer ? $viewCustomizer->permanentMessageType() : undef; % if ($msg) { <& /msg.mas, msg => $msg, class => $type, &> % } % if ( $model->message() ) { <& /msg.mas, msg => $model->popMessage(), class => $model->messageClass() &> % } % } <& SELF:_body, model => $model, @childArgs &> <%method messagesAndHelp> <%args> $model <%init> my $viewCustomizer = $model->viewCustomizer(); my $msg = $viewCustomizer ? $viewCustomizer->permanentMessage() : undef; my $type = $viewCustomizer ? $viewCustomizer->permanentMessageType() :undef; % if ($msg) { <& /msg.mas, msg => $msg, class => $type, &> % } % if ( $model->message() ) { <& /msg.mas, msg => $model->popMessage(), class => $model->messageClass() &> % } % my $help = $model->help(); % if ($help) {
<% $help %>
% } % my $disabledModuleWarning = $model->disabledModuleWarning(); % if ($disabledModuleWarning) {
<% $disabledModuleWarning %>
% } <%doc> Method: title Set the view title Parameters: title - String the title <%method title> <%args> $title

<% $title %>

<%doc> Method: editFormSection Show a section of an edit form Parameters: name - String with the name formData - array containing which form the table row <%method editFormSection> <%args> $name @formData $section => undef <%init> my @data; my $title; if ($section) { @data = grep { $_->section() and ($_->section() eq $section->{'name'}) } @formData; } else { @data = grep { not $_->section() } @formData; } % if ($section) { % my $sectionId = 'form_section_' . $section->{'name'};

<% $section->{'title'} %>

% } <& SELF:editRowFields, modelName => $name, formData => \@data &> % if ($section) { % } <%doc> Method: editForm Show the form to edit the fields from a row Parameters: modelName - String the model name formData - array containing which form the table row <%method editForm> <%args> $modelName @formData @sections => () % my $formAttrs = ''; % if (@formData and $formData[0]->model()->disableAutocomplete()) { % $formAttrs = 'autocomplete="off"'; % }
> <& SELF:editFormSection, name => $modelName, formData => \@formData, &> % foreach my $section (@sections) % { <& SELF:editFormSection, name => $modelName, formData => \@formData, section => $section &> % } <& SELF:buttons &>
% if (@formData) { % my $customizer = $formData[0]->model()->viewCustomizer(); % if ($customizer) { % } % } <%doc> Method: editRowFields Show the form to edit the fields from a row Parameters: modelName - String the model name formData - array containing which form the table row <%method editRowFields> <%args> $modelName @formData % foreach my $type (grep { defined($_->HTMLSetter())} @formData) % { <%perl> my $viewCustomizer = $type->model()->viewCustomizer(); my $rowInit; my $disableSetter = 0; my $displayRow; if ($viewCustomizer) { $rowInit = $viewCustomizer->initHTMLStateField($type->fieldName(), \@formData); if ($rowInit eq 'hide') { $displayRow = 'class="hidden"'; } elsif ($rowInit eq 'disable') { $disableSetter = 1; } } <& $type->typeRowLayout(), 'modelName' => $modelName, 'type' => $type, 'displayRow' => $displayRow, 'disableSetter' => $disableSetter, &> % } <%doc> Method: buttons Show the button to submit the form. It must be overriden. In order to obtain the main arguments used $m->request_args()->{argName}. <%method buttons> <%doc> Method: changeMenu Change the CSS class from the changes menu in order to advise the user some changes have been made <%method changeMenu> <%args> $hasChanged <%doc> Group: Protected methods <%doc> Method: _body Set the body for the viewer indeed. This method must be overridden by the subclasses in order to show the model content in some way. The original parameters from the subclass template must appear as well with their own default values. (Protected method) Parameters: model - the model to view - Additional parameters, see above. <%method _body> % # Default empty implementation % $m->call_next(); zentyal-core-2.3.21+quantal1/src/templates/ajax/tabMenu.mas0000664000000000000000000000563212017102272020432 0ustar <%doc> This template creates a tab selector which contains models which are displayed selecting the corresponding tab. The selection and load is done with JavaScript and AJAX requests. A parameter is required: - models - array ref with with the model instances - selectedTab - Integer the index for the selected tab. Default value: 0 - hasChanged - Boolean indicating whether the database has changed or not - tabName - String name used by tab in order not to have problems when more than one tab is used at the same page <%args> $tabName => '' $models => [] $selectedTab => 0 $hasChanged => 0 $directory => '' <%init> use EBox::Gettext; my $defaultModel = ${$models}[$selectedTab];
">
"> <& $defaultModel->Viewer(), model => $defaultModel, hasChanged => $hasChanged, action => 'view', &>
zentyal-core-2.3.21+quantal1/src/templates/ajax/passwordRowLayout.mas0000664000000000000000000000232712017102272022565 0ustar <%args> $modelName $type $displayRow $disableSetter > <% $type->printableName() %>: % if ($type->optionalLabel()) {
<% __('Optional') %>
% } <& $type->HTMLSetter(), 'data' => $type, 'tableName' => "$modelName", 'disabled' => $disableSetter &> % if ($type->{'confirm'} and $type->editable() and not $disableSetter) { > <% $type->confirmPrintableName() %>: % if ($type->optionalLabel()) {
<% __('Optional') %>
% } <%perl> my $id = $modelName . '_' . $type->fieldName() . '_confirm'; my $passValue = $type->printableValue(); $passValue = '' unless defined ( $passValue ); <& /input/password.mas, name => $id, value => $passValue, id => $id, disabled => 0 &> % }
<% $type->help() %>
zentyal-core-2.3.21+quantal1/src/templates/ajax/viewer/0000775000000000000000000000000012017102272017630 5ustar zentyal-core-2.3.21+quantal1/src/templates/ajax/viewer/linkRSS.mas0000664000000000000000000000052412017102272021660 0ustar <%doc> Template to show the link to the RSS putting the standard RSS feed icon near it. Parameters: data - the instanced type which contains the RSS type <%args> $data <%init> use EBox::Gettext; zentyal-core-2.3.21+quantal1/src/templates/ajax/viewer/passwordViewer.mas0000664000000000000000000000033212017102272023354 0ustar <%doc> The password type viewer <%args> $data % if ( defined ( $data->printableValue ) % and length ( $data->printableValue() ) > 0 ) % { **** % } % else % { -- % } zentyal-core-2.3.21+quantal1/src/templates/ajax/viewer/file.mas0000664000000000000000000000211012017102272021243 0ustar <%doc> This template show the file type on a HTML. The file path is shown. If the allowDownload property is set, a link to download the file is displayed and a user may perform the download action. <%args> $data <%init> my $showPath = defined ( $data->printableValue()); $showPath = $showPath && ($data->exist()); my $download = $showPath && $data->allowDownload(); my $id = $data->model()->tableName() . '_' . $data->fieldName(); my $idRemove = $id . '_remove'; my $idCurrent = $id . '_current'; % if ($data->model()->isa('EBox::Model::DataForm')) {
<% __('Current file') %>: <% $data->printableValue() %> % if ( $download ) { <% __('Download') %> % } else { % } <% __('Remove') %>
% } elsif ($data->model()->isa('EBox::Model::DataTable')) { <% $data->printableValue() %> % if ( $download ) { <% __('Download') %> % } else { % } % } zentyal-core-2.3.21+quantal1/src/templates/ajax/viewer/booleanViewer.mas0000664000000000000000000000035412017102272023135 0ustar <%args> $data % if ( (defined ( $data->printableValue())) and % ($data->printableValue() == 1)) { yes % } else { no % } zentyal-core-2.3.21+quantal1/src/templates/ajax/viewer/textViewer.mas0000664000000000000000000000025512017102272022502 0ustar <%args> $data % if ( defined ( $data->printableValue() )) { <% $data->printableValue() %> <%$data->trailingText() %> % } % else { -- % } zentyal-core-2.3.21+quantal1/src/templates/ajax/viewer/booleanInPlaceViewer.mas0000664000000000000000000000305312017102272024370 0ustar <%args> $data $checkAllId => undef; <%init> my $checked = ''; if (defined ( $data->printableValue()) and ($data->printableValue() == 1)) { $checked = 'checked'; } my $tableName = $data->model()->table()->{'tableName'}; my $id = $tableName . '_' . $data->fieldName() . '_' . $data->row()->id(); my $controller = $data->model()->table()->{'actions'}->{editField}, my $rowId = $data->row()->id(); my $dir = $data->model()->{confdir}; my $disabled = ''; if ($data->row()->readOnly() or (not $data->editable()) ) { $disabled = 'disabled'; } my $fieldName = $data->fieldName(); my $onChange = qq{sendInPlaceBooleanValue("$controller", "$tableName", "$rowId", "$dir", "$fieldName", this ); }; if ($checkAllId) { $onChange .= qq|if (this.checked) { checkAllControlValue("$controller", "$tableName", "$dir", "$checkAllId", "$fieldName"); } else { \$("$checkAllId").checked= false; } |; } id='<% $id %>' onChange='<% $onChange %>' <% $disabled %> />
<%doc> onChange='( "<% $controller %>", "<% $tableName %>", "<% $rowId %>", "<% $dir %>", "<% $data->fieldName() %>", this )' zentyal-core-2.3.21+quantal1/src/templates/ajax/viewer/composite.mas0000664000000000000000000000042012017102272022330 0ustar <%doc> This template is intended to show the type viewer within the composite type <%args> $data % foreach my $simpleType (@{$data->types()}) { <% $simpleType->printableName() %>: <& $simpleType->HTMLViewer(), data => $data &> % } zentyal-core-2.3.21+quantal1/src/templates/ajax/viewer/rawHTML.mas0000664000000000000000000000016112017102272021606 0ustar <%args> $tableName => '' $data $cssClass => '' $disabled => 0 <% $data->printableValue() | n %> zentyal-core-2.3.21+quantal1/src/templates/ajax/viewer/selectViewer.mas0000664000000000000000000000041612017102272022774 0ustar <%args> $data % my $printableValue = $data->printableValue(); % if ($data->{filter}) { % $printableValue = $data->filter(); % } % if (defined ($printableValue)) { <% $printableValue %> <%$data->trailingText() %> % } % else { -- % } zentyal-core-2.3.21+quantal1/src/templates/ajax/viewer/hasManyViewer.mas0000664000000000000000000000032412017102272023113 0ustar <%args> $data % my $link = $data->linkToView(); % if (defined $link) { % } else { -- % } zentyal-core-2.3.21+quantal1/src/templates/ajax/simpleModalDialog.mas0000664000000000000000000000041012017102272022412 0ustar <%args> $buttonText => undef <%init> use EBox::Gettext; if (not defined $buttonText) { $buttonText = __('OK'); }
onclick='Modalbox.hide(); return false' />
zentyal-core-2.3.21+quantal1/src/templates/ajax/tableSelector.mas0000664000000000000000000000322512017102272021623 0ustar <%doc> Table selector. It shows a selection to choose a table. None of the options is selected by default. When one is selected, the table associated to that option is shown. <%args> $multiTableModel_ref <%init> use EBox::Gettext; my $helpMessage = $multiTableModel_ref->helpMessage(); if ( $helpMessage eq '' ) { # Write a default message $helpMessage = __('Choose one of the options to show the associated table'); }
<% $helpMessage %>

<% $multiTableModel_ref->printableName() %>

<%perl> my @selectOptions; # Push the default one push ( @selectOptions, { value => '0', printableValue => __('Choose one...'), }); foreach my $option (@{$multiTableModel_ref->selectOptions()}) { push (@selectOptions, { value => $option->{id}, printableValue => $option->{printableId}, } ); }
<& /input/select.mas, name => 'tableSelection', value => '0', options => \@selectOptions, id => 'tableSelection', onchange => 'if ( ! selectDefault("tableSelection"))' . '{ hangTable("table", "errorMultiTable", ' . '"' . $multiTableModel_ref->action('select') . '"' . ', "selector"); }', &>
zentyal-core-2.3.21+quantal1/src/templates/ajax/setParent.mas0000664000000000000000000000033612017102272021000 0ustar <%args> $parentElementId $parentElementValue zentyal-core-2.3.21+quantal1/src/templates/ajax/tableModalView.mas0000664000000000000000000004432312017102272021736 0ustar <%flags> inherit => '/ajax/tableModal.mas' <%args> $model $action => 'view' $editid => '' $hasChanged => 1 $filter => '' $page => 0 $tpages => 0 $selectCallerId => undef %presetParams => () <%init> use EBox::Gettext; use EBox::Model::DataTable; use POSIX; use Perl6::Junction qw(any); <& SELF:view, model => $model, action => $action, &> <%doc> Method: _body Set the model viewer body. Check the overridden method very carefully. Overrides: /ajax/modelViewer.mas:_body <%method _body> <%args> $model $action => 'view' $editid => '' $hasChanged => 1 $filter => '' $page => 0 $tpages => 0 %presetParams => () <%init> use EBox::Gettext; # Fetch table head from model my @tableHead = @{$model->table()->{'tableDescription'}}; # Pointer to different values for convenience my $actions = $model->table()->{'actions'}; my $changeView = $model->action('changeView'); my $printableRowName = $model->table()->{'printableRowName'}; my $tableName = $model->table()->{'tableName'} . '_modal'; my $printableTableName = $model->table()->{'printableTableName'}; my @ids; if (not $model->customFilter()) { @ids = @{$model->ids()}; } else { @ids = @{$model->customFilterIds($filter)}; } unless ($page) { $page = 0; } # If the action is a preset update, choose for an edition or an # addition whether the editid is set or not if ( $action eq 'presetUpdate' ) { $action = 'changeAdd' if (not $editid); $action = 'changeEdit' if ( $editid ); } my $showEditForm = 0; my $changeAddAction = 0; my $viewAction = 0; if ($action eq 'view') { $viewAction = 1; } elsif ($action eq 'changeAdd') { $changeAddAction = 1 ; $showEditForm = 1; } elsif ($action eq 'viewAndAdd') { $viewAction = 1; $changeAddAction = 1 ; $showEditForm = 1; } elsif ($action eq 'clone') { $changeAddAction = 1; $showEditForm = 1; } elsif ($action eq 'changeEdit') { $showEditForm = 1; } my $pageSize = $model->pageSize(); $pageSize or $pageSize = 10; my $requestArgs = $m->request_args(); my $selectCallerId = $requestArgs->{selectCallerId};
<& .pageTitle &> % if ( $model->pageTitle() ) { % my $viewCustomizer = $model->viewCustomizer(); % my $msg = $viewCustomizer ? $viewCustomizer->permanentMessage() : undef; % my $type = $viewCustomizer ? $viewCustomizer->permanentMessageType() : undef; % if ($msg) { <& /msg.mas, msg => $msg, class => $type, &> % } % if ( $model->message() ) { <& /msg.mas, msg => $model->popMessage(), class => $model->messageClass() &> % } % } % if ($model->help()) {
<% $model->help() %>
% } % if (not $showEditForm) { % if ( $model->printableModelName() ) { <& PARENT:title, title => $model->headTitle() &> % } % if ($changeView and exists $actions->{'add'} and (not $changeAddAction)) { "undefined", page => $page, isFilter => 0, selectCallerId => $selectCallerId) %>;return false"><% __('Add new') %>

% } % } %# add/edit/clone form % if ( $changeAddAction or $showEditForm ) { <& SELF:editFormTitle, action => $action, printableRowName => $printableRowName, &> <& SELF:editForm, model => $model, action => $action, changeAddAction => $changeAddAction, editid => $editid, presetParams => \%presetParams, &> % } % if (@ids or length($filter) > 0) % { % if ( $action eq 'changeAdd' or $action eq 'changeEdit' ) % {

<% $printableTableName %>

% }
% my $filterId = $tableName . '_filterLoading'; 'undefined', page => 0, isFilter => 1) %>; return false" />
'> <& SELF:tableHead, model => $model, tableHead => \@tableHead, actions => 1, page => $page &> <%perl> my $displayRows = 0; my $matches = 0; for ( my $idx = 0; $idx < scalar(@ids); $idx++) { my $row; if (not $model->customFilter() and defined($filter) and (length($filter) > 0)) { $row = $model->row($ids[$idx]); if (not ($row->matchFilter($filter))) { next; } } else { $matches++; unless ($matches > ($pageSize * $page)) { next; } $displayRows++; if ($displayRows > $pageSize) { next; }; $row = $model->row($ids[$idx]); } % foreach my $td (@{$row->elements()}) % { % next unless ($td->HTMLViewer()); % } <& SELF:actionCell, model => $model, actions => $actions, row => $row, idx => $idx, ids => \@ids, changeView => $changeView, page => $page, selectCallerId => $selectCallerId, &> % if ($displayRows == $model->pageSize() and not (defined($filter) and length($filter) > 0) ) { % $matches = scalar(@ids); % last; % } % }
<& $td->HTMLViewer(), 'data' => $td &>
<& /ajax/tablePager.mas, model => $model, page => $page, tpages => POSIX::ceil($matches / $pageSize) - 1, pageSize => $pageSize &>
% } else { % } % if ( $action eq 'changeEdit' and $editid ) { % } <& .doneButton, model => $model, selectCallerId => $selectCallerId &>
% # End body method <%method editForm> <%args> $model $action $editid %presetParams $changeAddAction <%init> my $tableName = $model->table()->{'tableName'} . '_modal'; my @tableHead = @{$model->table()->{'tableDescription'}}; # Fetch the data to be displayed in setters, if we are editing a row # we should show the contents of the current fields. If we are # adding a new row, the fields are empty except for a preset values # adding where preset contents must be shown my @formData; if ($editid and ($editid ne 'undefined')) { my $rowEdit = $model->row($editid); @formData = @{$rowEdit->elements()}; if ($action eq 'clone') { # unique fields should not be cloned my @uniqFields = grep { $_->unique() } @tableHead; foreach my $uniqField (@uniqFields) { foreach my $clonedField (@formData) { if ($uniqField->fieldName() eq $clonedField->fieldName()) { $clonedField = $uniqField; last; } } } } } elsif ( ((keys %presetParams) > 0) and $action eq 'presetUpdate') { # The preset is not empty @formData = values(%presetParams); } else { @formData = @tableHead; } my $forceAction; if ($changeAddAction) { $forceAction = 'changeAdd'; } elsif ($action eq 'clone') { $forceAction = 'changeAdd'; } else { $forceAction = $action; }
<& PARENT:editRowFields, modelName => $tableName, formData => \@formData &> <& SELF:buttons, forceAction => $forceAction &>
% my $customizer = $formData[0]->model()->viewCustomizer(); % if ($customizer) { % } <%doc> Method: buttons Show the button to submit the form Overrides: /ajax/modelViewer.mas:buttons <%method buttons> <%args> $forceAction => undef <%init> # Getting the arguments from the first request my $requestArgs = $m->request_args(); my $model = $requestArgs->{model}; my $action = $requestArgs->{action}; my $editid = $requestArgs->{editid}; my $filter = $requestArgs->{filter}; my $page = $requestArgs->{page}; my $tpages = $requestArgs->{tpages}; my $changeView = $model->action('changeView'); my $selectCallerId = $requestArgs->{selectCallerId}; if ( $action eq 'presetUpdate' ) { $action = 'changeAdd' if ( not $editid ); $action = 'changeEdit' if ( $editid ); } if ($forceAction) { $action = $forceAction; } % if ( $action eq 'changeAdd' or $action eq 'viewAndAdd') { <& /input/submit.mas, class => 'inputButton', type => 'submit', name => 'add', value => __('Add'), title => 'Add', onclick => $model->modalAddNewRowJS($page, undef, selectCallerId => $selectCallerId) . '; return false' &> % } elsif ($action eq 'changeEdit') { <& /ajax/customActions.mas, model => $model, id => $editid &> <& /input/submit.mas, class => 'inputButton', type => 'submit', name => 'change', value => __('Change'), title => 'Change', onclick => $model->changeRowJS($editid, $page, 1, selectCallerId => $selectCallerId) . '; return false' &> % } % if ($changeView) { <& /input/submit.mas, class => 'inputButton', type => 'submit', name => 'cancel', value => __('Cancel'), title => 'Cancel', onclick => $model->modalChangeViewJS(changeType => 'changeList', editId => 'undefined', page => $page, isFilter => 0, selectCallerId => $selectCallerId) . '; return false' &> % } <%method actionCell> <%doc> Print the action cell for the row Parameters: $model - model of the table $idx $actions $row - the row printed $changeView $page - table's page Warning: this must be somewaht synchronized with SUPER method, we dont parametize more the SUPER method to avoid more slowness with this particular case <%args> $model $actions $idx @ids $row $changeView $page $selectCallerId <%perl> my $rowReadOnly = $row->readOnly() ; my $disabled = ''; my ($edit, $edit_msg); my ($delete, $delete_msg); my ($clone, $clone_msg); if (not $rowReadOnly) { $edit = 'edit'; $edit_msg = __('Edit'); $delete = 'delete'; $delete_msg = __('Delete'); $clone = 'clone'; $clone_msg = __('Clone'); } else { $disabled = 'disabled'; $edit = 'edit-inactive'; $delete = 'delete-inactive'; $edit_msg = __('Read-only row: edit disabled'); $delete_msg = __('Read-only row: delete disabled'); $clone = 'clone-inactive'; $clone_msg = __('Read-only row: clone disabled'); } <& /ajax/customActions.mas, model => $model, id => $row->{id}, type => 'image' &> % if ($actions->{'del'}) % { type='image' name='del' value="Del" title="<% $delete_msg %>" alt="Del" src='/data/images/<% $delete %>.gif' onClick="<% $model->actionClickedJS('del', $row->{id}, '' , $page, 1, selectCallerId => $selectCallerId ) %>" /> % } % if ($changeView) { type='image' name='edit' value="edit" title="<% $edit_msg %>" src='/data/images/<% $edit %>.gif' onClick="<% $model->modalChangeViewJS( changeType => 'changeEdit', editId => $row->{'id'}, page => $page, isFilter => 0, selectCallerId => $selectCallerId) %>" /> % } % if ($actions->{'clone'}) % { type='image' name='clone' value="Clone" title="<% $clone_msg %>" alt="Clone" src='/data/images/<% $clone %>.png' onClick="<% $model->actionClickedJS('clone', $row->{id}, '', $page, 1, selectCallerId => $selectCallerId) %>" /> % } % if ($model->table()->{'order'} == 1) % { <& SELF:.moveRowActions, model => $model, row => $row, idx => $idx, ids => \@ids, page => $page, selectCallerId => $selectCallerId &> % } <%method .moveRowActions> <%args> $model $row $idx @ids $page $selectCallerId <%perl> my $rowReadOnly = $row->readOnly(); my ($prevRowRO, $nextRowRO); my ($up_icon, $up_msg); my ($down_icon, $down_msg); my ($up_disabled, $down_disabled); my $firstRow = ($idx == 0); if (not $firstRow) { $prevRowRO = $model->isRowReadOnly($ids[$idx-1]); if ($rowReadOnly or $prevRowRO) { $up_disabled = 'disabled'; $up_icon = 'up-inactive.gif'; $up_msg = __('Read-only row: move up disabled'); } else { $up_disabled = ''; $up_icon = 'up.gif'; $up_msg = __('Move up'); } } my $lastRow = ($idx == $#ids); if (not $lastRow) { $nextRowRO = $model->isRowReadOnly($ids[$idx+1]); if ($rowReadOnly or $nextRowRO) { $down_disabled = 'disabled'; $down_icon = 'down-inactive.gif'; $down_msg = __('Read-only row: move down disabled'); } else { $down_disabled = ''; $down_icon = 'down.gif'; $down_msg = __('Move down'); } } % if (not $firstRow) { type='image' name='up' value="Up" title="<% $up_msg %>" alt="Up" src='/data/images/<% $up_icon %>' onClick="<% $model->actionClickedJS('move', $row->{id}, 'up', $page, 1, selectCallerId => $selectCallerId ) %>" /> % } % if (not $lastRow) { type='image' name='down' value="Down" title="<% $down_msg %>" alt="Down" src='/data/images/<% $down_icon %>' onClick="<% $model->actionClickedJS('move', $row->{id}, 'down', $page, 1, selectCallerId => $selectCallerId ) %>" /> % } <%doc> Method: pageTitle Set the page title Parameters: title - String the title <%def .pageTitle> <%args> $title => undef $crumbs => undef <& /title.mas, title => $title, crumbs => $crumbs &> <%def .doneButton> <%args> $model $selectCallerId <%init> my $empty = $model->size() == 0; if ($empty) { return; } my $cancelJs =$model->modalCancelAddJS(selectCallerId => $selectCallerId);

<& /input/submit.mas, class => 'inputButton', type => 'submit', name => 'done', value => __('Done'), title => 'Done', onclick => 'Modalbox.hide(); return false' &> <& /input/submit.mas, class => 'inputButton', type => 'submit', name => 'cancel', value => __('Cancel'), title => 'Cancel', onclick => $cancelJs . '; Modalbox.hide(); return false', &>
zentyal-core-2.3.21+quantal1/src/templates/ajax/customActions.mas0000664000000000000000000000066612017102272021674 0ustar <%args> $model $id $type => 'submit' % my $customActions = $model->customActions(undef, $id); % if ($customActions) % { % foreach my $customAction ( @{$customActions} ) % { <& /input/action.mas, action => $customAction, id => $id, type => $type &> % } % } zentyal-core-2.3.21+quantal1/src/templates/ajax/tableBody.mas0000664000000000000000000005705112017102272020746 0ustar <%flags> inherit => '/ajax/modelViewer.mas' <%args> $model $action => 'view' $editid => '' $hasChanged => 1 $filter => '' $page => 0 $tpages => 0 %presetParams => () <%init> use EBox::Gettext; use EBox::Model::DataTable; use POSIX; use Perl6::Junction qw(any); <& PARENT:view, model => $model, hasChanged => $hasChanged, action => $action, &> <%doc> Method: _body Set the model viewer body. Check the overridden method very carefully. Overrides: /ajax/modelViewer.mas:_body <%method _body> <%args> $model $action => 'view' $editid => '' $hasChanged => 1 $filter => '' $page => 0 $tpages => 0 %presetParams => () <%init> use EBox::Gettext; # Fetch table head from model my @tableHead = @{$model->table()->{'tableDescription'}}; # Pointer to different values for convenience my $actions = $model->table()->{'actions'}; my $changeView = $model->action('changeView'); my $printableRowName = $model->table()->{'printableRowName'}; my $tableName = $model->table()->{'tableName'}; my $printableTableName = $model->table()->{'printableTableName'}; my $onlyCustomActions = $model->table()->{'onlyCustomActions'}; my $withoutActions = $model->table()->{'withoutActions'}; my @ids; if (not $model->customFilter()) { @ids = @{$model->ids()}; } else { @ids = @{$model->customFilterIds($filter)}; } unless ($page) { $page = 0; } # If the action is a preset update, choose for an edition or an # addition whether the editid is set or not if ( $action eq 'presetUpdate' ) { $action = 'changeAdd' if (not $editid); $action = 'changeEdit' if ( $editid ); } my $pageSize = $model->pageSize(); $pageSize or $pageSize = 10; my %checkAllControls; my $checkAllProperty = $model->checkAllProperty(); if ($checkAllProperty) { %checkAllControls = map { my $field = $_; my $id = $tableName . '_'. $field . '_CheckAll'; ( $field => $id) } @{ $checkAllProperty } ; } <& .pageTitle, crumbs => $model->viewCustomizer()->HTMLTitle() &> % my $showEditForm = $action eq any('changeAdd', 'changeEdit', 'clone'); % if (not $showEditForm) { % if ( $model->printableModelName() ) { <& PARENT:title, title => $model->headTitle() &> <& SELF:messagesAndHelp, model => $model &> % } % unless ((defined $onlyCustomActions) and $onlyCustomActions) { % if ($changeView) { <& SELF:tableActionLinks, model => $model, addAction => (exists $actions->{'add'}), page => $page, &> % } % } % } % unless ((defined $onlyCustomActions) and $onlyCustomActions) { % if ($showEditForm) { <& SELF:editFormTitle, action => $action, printableRowName => $printableRowName, &> <& SELF:messagesAndHelp, model => $model &> <& SELF:editFormPopulated, action => $action, model => $model, editid => $editid, presetParams => \%presetParams &> % } % } % if (@ids or length($filter) > 0) { % % unless ((defined $onlyCustomActions) and $onlyCustomActions) { % if ( $action eq 'changeAdd' or $action eq 'changeEdit' ) {

<% $printableTableName %>

% } % } <& SELF:topToolbar, tableName => $tableName, model => $model, filter => $filter, &> '> <& SELF:tableHead, tableHead => \@tableHead, actions => ((defined $withoutActions) ? not $withoutActions: 1), model => $model, page => $page, checkAllControls => \%checkAllControls, &> <%perl> my $displayRows = 0; my $matches = 0; for ( my $idx = 0; $idx < scalar(@ids); $idx++) { my $row; if (not $model->customFilter() and defined($filter) and (length($filter) > 0)) { $row = $model->row($ids[$idx]); if (not ($row->matchFilter($filter))) { next; } } else { $matches++; unless ($matches > ($pageSize * $page)) { next;} $displayRows++; if ($displayRows > $pageSize) { next; }; $row = $model->row($ids[$idx]); } % foreach my $td (@{$row->elements()}) { <%perl> next unless ($td->HTMLViewer()); my @viewerParams = (data => $td); if ($td->isa('EBox::Types::Boolean')) { my $checkAllId = $checkAllControls{$td->fieldName()}; push @viewerParams, (checkAllId => $checkAllId); } % } % unless ((defined $withoutActions) and $withoutActions) { <& SELF:actionCell, model => $model, actions => $actions, row => $row, idx => $idx, ids => \@ids, changeView => $changeView, page => $page &> % } % if ($displayRows == $pageSize and not (defined($filter) and length($filter) > 0) ) { % $matches = scalar(@ids); % last; % } % }
<& $td->HTMLViewer(), @viewerParams &>
<& /ajax/tablePager.mas, model => $model, page => $page, tpages => POSIX::ceil($matches / $pageSize) - 1, pageSize => $pageSize &>
<& SELF:legend, model => $model, actions => $actions, ids => \@ids, changeView => $changeView, &> % } else { % } % unless ((defined $onlyCustomActions) and $onlyCustomActions) { % if ( $action eq 'changeEdit' and $editid ) { % } % } % # End body method <%method topToolbar> <%args> $tableName $model $filter => undef
<& SELF:filterForm, tableName => $tableName, model => $model, filter => $filter, &>
<%method filterForm> <%args> $tableName $model $filter <%init> my $filterId = $tableName . '_filterLoading';
'undefined', page => 0, isFilter => 1) %>; return false" />
<%method editFormTitle> <%args> $action $printableRowName % my $formTitle; % if ( $action eq 'changeAdd' ) { % $formTitle = __x('Adding a new {row}', row => $printableRowName); % } elsif ( $action eq 'changeEdit' ) {

% $formTitle = __x('Editing {row}', row => $printableRowName); % } elsif ($action eq 'clone') {

% $formTitle = __x('Cloning {row}', row => $printableRowName); % } <& PARENT:title, title => $formTitle &> <%method editFormPopulated> <%args> $action $model $editid %presetParams => () @customizerParams => () <%init> my $tableName = $model->table()->{'tableName'}; my @tableHead = @{$model->table()->{'tableDescription'}}; if ($action eq 'add') { foreach my $element (@tableHead) { $element->setValue($element->defaultValue()); } } # Fetch the data to be displayed in setters, if we are editing a row # we should show the contents of the current fields. If we are # adding a new row, the fields are empty except for a preset values # adding where preset contents must be shown my @formData; my @extraComponents; if ($editid and ($editid ne 'undefined')) { my $fetchRow = $model->row($editid); @formData = @{$fetchRow->elements()}; if ($action eq 'clone') { # unique fields should not be cloned my @uniqFields = grep { $_->unique() } @tableHead; foreach my $uniqField (@uniqFields) { foreach my $clonedField (@formData) { if ($uniqField->fieldName() eq $clonedField->fieldName()) { $clonedField = $uniqField; last; } } } } } elsif ( ((keys %presetParams) > 0) and $action eq 'presetUpdate') { # The preset is not empty @formData = values(%presetParams); } else { @formData = @tableHead; } <& PARENT:editForm, modelName => $tableName, formData => \@formData, sections => $model->sections(), &> % my $customizer = $formData[0]->model()->viewCustomizer(@customizerParams); % if ($customizer) { % } <%doc> Method: buttons Show the button to submit the form Overrides: /ajax/modelViewer.mas:buttons <%method buttons> <%init> # Getting the arguments from the first request my $requestArgs = $m->request_args(); my $model = $requestArgs->{model}; my $action = $requestArgs->{action}; my $editid = $requestArgs->{editid}; my $filter = $requestArgs->{filter}; my $page = $requestArgs->{page}; my $tpages = $requestArgs->{tpages}; my $modelName = $model->modelName(); my $changeView = $model->action('changeView'); my $onlyCustomActions = $model->table()->{'onlyCustomActions'}; my $withoutActions = $model->table()->{'withoutActions'}; my $cloneId; if ( $action eq 'presetUpdate' ) { $action = 'changeAdd' if ( not $editid ); $action = 'changeEdit' if ( $editid ); } elsif ($action eq 'clone') { $action = 'changeAdd'; $cloneId = $editid; } % unless ((defined $withoutActions) and $withoutActions) { % unless ((defined $onlyCustomActions) and $onlyCustomActions) % { % if ( $action eq 'changeAdd' ) % { % # extra input for cloneId % if ($cloneId) { <&/input/hidden.mas, name => $modelName . '_cloneId', id => $modelName . '_cloneId', value => $cloneId &> % } <& /input/submit.mas, class => 'inputButton', type => 'submit', name => 'add', value => __('Add'), title => 'Add', onclick => $model->addNewRowJS($page, cloneId => $cloneId) . '; return false' &> % } % elsif ($action eq 'changeEdit') % { <& /input/submit.mas, class => 'inputButton', type => 'submit', name => 'change', value => $model->printableActionName(), title => 'Change', onclick => $model->changeRowJS($editid, $page) . '; return false' &> % } % if ($changeView) % { <& /input/submit.mas, class => 'inputButton', type => 'submit', name => 'cancel', value => __('Cancel'), title => 'Cancel', onclick => $model->changeViewJS(changeType => 'changeList', editId => 'undefined', page => $page, isFilter => 0) . '; return false' &> % } % } % } <%method tableHead> <%doc> Method: tableHead Print the header of the table Parameteres: @tableHead - list with the types of the fields of the tale's rows $actions - whether to show Action row or not $model $page <%args> @tableHead $actions $model $page %checkAllControls => () % my $checkAll = %checkAllControls; % foreach my $th (@tableHead) { % next unless ($th->HTMLViewer()); <% $th->printableName() %> % if ($checkAll) { % my $spaceAdded = 0; % my $fieldName = $th->fieldName(); % if ($th->isa('EBox::Types::Boolean')) { % my $checkAllId = $checkAllControls{$fieldName}; % if ($checkAllId) { <& SELF:checkAllControl, id => $checkAllId, fieldName => $fieldName, model => $model, page => $page, &> % $spaceAdded = 1; % } % } % unless ($spaceAdded) {
 
% } % } % } % if ($actions) { <% __('Action') %> % if ($checkAll) {
 
% } % } <%method checkAllControl> <%args> $id $fieldName $model $page <%init> my $tableName = $model->name(); my $divId = $id; $divId =~ s/_CheckAll$/_div_CheckAll/; my $checkAllJS= $model->changeViewJS( changeType => "checkboxSetAll", editId => $fieldName, page => $page, isFilter => 0); my $uncheckAllJS= $model->changeViewJS( changeType => "checkboxUnsetAll", editId => $fieldName, page => $page, isFilter => 0); my $onChangeJS =qq|if (this.checked) {$checkAllJS } else {$uncheckAllJS;} |; my @htmlAttrs = (onchange => $onChangeJS); push @htmlAttrs, (id => $id); if ( $model->checkAllControlValue($fieldName)) { push @htmlAttrs, ('checked' => 'checked'); }
> />
<%method tableActionLinks> <%args> $model $addAction $page <%init> my $actionLinksAdded = 0; my $tableName = $model->name(); my $checkAll = $model->checkAllProperty(); % if ($addAction) { % $actionLinksAdded = 1; "undefined", page => $page, isFilter => 0) %>;return false"><% __('Add new') %> % } % if ($actionLinksAdded) {

% } <%method actionCell> <%doc> Print the action cell for the row Parameters: $model - model of the table $actions $idx @ids $row - the row printed $changeView $page - table's page <%args> $model $actions $idx @ids $row $changeView $page <%perl> my $rowReadOnly = $row->readOnly() ; my $disabled = ''; my ($edit, $edit_msg); my ($delete, $delete_msg); my ($clone, $clone_msg); if (not $rowReadOnly) { $edit = 'edit'; $edit_msg = __('Edit'); $delete = 'delete'; $delete_msg = __('Delete'); $clone = 'clone'; $clone_msg = __('Clone'); } else { $disabled = 'disabled'; $edit = 'edit-inactive'; $delete = 'delete-inactive'; $edit_msg = __('Read-only row: edit disabled'); $delete_msg = __('Read-only row: delete disabled'); $clone = 'clone-inactive'; $clone_msg = __('Read-only row: clone disabled'); } my $onlyCustomActions = $model->table()->{'onlyCustomActions'}; <& /ajax/customActions.mas, model => $model, id => $row->{id}, type => 'image' &> % unless ((defined $onlyCustomActions) and $onlyCustomActions) % { % if ($actions->{'del'}) % { type='image' name='del' value="Del" title="<% $delete_msg %>" alt="Del" src='/data/images/<% $delete %>.gif' onClick="<% $model->actionClickedJS('del', $row->{id}, '', $page) %>" /> % } % if ($changeView) { type='image' name='edit' value="edit" title="<% $edit_msg %>" src='/data/images/<% $edit %>.gif' onClick="<% $model->changeViewJS( changeType => 'changeEdit', editId => $row->{'id'}, page => $page, isFilter => 0) %>" /> % } % if ($actions->{'clone'}) % { type='image' name='clone' value="Clone" title="<% $clone_msg %>" alt="Clone" src='/data/images/<% $clone %>.png' onClick="<% $model->actionClickedJS('clone', $row->{id}, '', $page) %>" /> % } % if ($model->table()->{'order'} == 1) % { <& SELF:.moveRowActions, model => $model, row => $row, idx => $idx, ids => \@ids, page => $page, &> % } % } <%method .moveRowActions> <%args> $model $row $idx @ids $page <%perl> my $rowReadOnly = $row->readOnly(); my ($prevRowRO, $nextRowRO); my ($up_icon, $up_msg); my ($down_icon, $down_msg); my ($up_disabled, $down_disabled); my $firstRow = ($idx == 0); if (not $firstRow) { $prevRowRO = $model->isRowReadOnly($ids[$idx-1]); if ($rowReadOnly or $prevRowRO) { $up_disabled = 'disabled'; $up_icon = 'up-inactive.gif'; $up_msg = __('Read-only row: move up disabled'); } else { $up_disabled = ''; $up_icon = 'up.gif'; $up_msg = __('Move up'); } } my $lastRow = ($idx == $#ids); if (not $lastRow) { $nextRowRO = $model->isRowReadOnly($ids[$idx+1]); if ($rowReadOnly or $nextRowRO) { $down_disabled = 'disabled'; $down_icon = 'down-inactive.gif'; $down_msg = __('Read-only row: move down disabled'); } else { $down_disabled = ''; $down_icon = 'down.gif'; $down_msg = __('Move down'); } } % if (not $firstRow) { type='image' name='up' value="Up" title="<% $up_msg %>" alt="Up" src='/data/images/<% $up_icon %>' onClick="<% $model->actionClickedJS('move', $row->{id}, 'up', $page) %>" /> % } % if (not $lastRow) { type='image' name='down' value="Down" title="<% $down_msg %>" alt="Down" src='/data/images/<% $down_icon %>' onClick="<% $model->actionClickedJS('move', $row->{id}, 'down', $page) %>" /> % } <%doc> Method: pageTitle Set the page title Parameters: title - String the title <%def .pageTitle> <%args> $title => undef $crumbs => undef <& /title.mas, title => $title, crumbs => $crumbs &> <%method legend> <%doc> Print the legend of a table Parameters: $model - model of the table $actions @ids $changeView <%args> $model $actions @ids $changeView <%perl> my $customActions = $model->{'table'}->{'customActions'}; if ($customActions) {
<%perl> foreach my $customAction ( @{$customActions} ) { if (exists $customAction->{'states'}) { foreach my $keyname (keys %{$customAction->{'states'}} ) { my $state = $customAction->{'states'}->{$keyname}; <%perl> } } else { % } % } %# Not custom actions %# Currently they does not show, reenable them if you want they to appear in the %# legend. Also add the clone action if we want to show it <%perl> my $edit = 'edit'; my $edit_msg = __('Edit'); my $delete = 'delete'; my $delete_msg = __('Delete'); if ( 0 && $actions->{'del'}) { % } % if (0 && $changeView) { % } % if (0 && $model->table()->{'order'} == 1 && $#ids > 1 ) { % my $up_icon = 'up.gif'; % my $up_msg = __('Move up'); % my $down_icon = 'down.gif'; % my $down_msg = __('Move down');
<% $state->{'printableValue'} %> <% $state->{'printableValue'} %> <% $customAction->{'printableValue'} %> <% $customAction->{'printableValue'} %> Del <% $delete_msg %> edit <% $edit_msg %> Up <% $up_msg %> Down <% $down_msg %> % }
%} zentyal-core-2.3.21+quantal1/src/templates/ajax/setter/0000775000000000000000000000000012017102272017635 5ustar zentyal-core-2.3.21+quantal1/src/templates/ajax/setter/textSetter.mas0000664000000000000000000000153112017102272022512 0ustar <%args> $tableName $data $cssClass => '' $disabled % my $id = $tableName . '_' . $data->fieldName(); % my $disableAttr = $disabled ? 'disabled' : ''; % if ($data->editable()) { % if ( $data->size() < 40 ) { /> % } else { % my $colSize = 17; # FIXME % my $nRows = $data->size() / $colSize; % } <% $data->trailingText() %> % } else { <% $data->printableValue() %> <% $data->trailingText() %> %} zentyal-core-2.3.21+quantal1/src/templates/ajax/setter/inverseMatchUnionSetter.mas0000664000000000000000000000066012017102272025171 0ustar <%args> $tableName $data $cssClass => '' % my $id = $tableName . '_' . $data->fieldName() . '_inverseMatch'; <& '/ajax/setter/unionSetter.mas', 'data' => $data, 'tableName' => $tableName, 'cssClass' => $cssClass &> <% __('Inverse match') . ':' %> <& /input/checkbox.mas, 'name' => "$id", 'value' => $data->inverseMatch(), 'id' => "$id", 'class' => "$cssClass", &> zentyal-core-2.3.21+quantal1/src/templates/ajax/setter/booleanSetter.mas0000664000000000000000000000061412017102272023146 0ustar <%args> $tableName $data $cssClass => '' $disabled => undef <%perl> my $id = $tableName . '_' . $data->fieldName(); my %args = ('name' => "$id", 'value' => $data->printableValue(), 'id' => "$id", 'class' => "$cssClass"); if (not $data->editable() or $disabled) { $args{'disabled'} = 'disabled'; } <& /input/checkbox.mas, %args &> <% $data->trailingText() %> zentyal-core-2.3.21+quantal1/src/templates/ajax/setter/timeSetter.mas0000664000000000000000000000321212017102272022462 0ustar <%args> $tableName $data $cssClass => '' $disabled => undef <& SELF:setter, tableName => $tableName, data => $data, cssClass => $cssClass, disabled => $disabled &> <%method setter> <%args> $tableName $data $cssClass => '' $disabled <%init> my @hours; for (my $hour = 0; $hour < 24; $hour++) { my $strHour = sprintf("%02d", $hour); push (@hours, { 'value' => $strHour, 'printableValue' => $strHour, }); } my @mins_secs; for (my $ms = 0; $ms < 60; $ms++) { my $strMS = sprintf("%02d", $ms); push (@mins_secs, { 'value' => $strMS, 'printableValue' => $strMS, }); } % my $id = $tableName . '_' . $data->fieldName(); % if ( $data->editable() ) { <& /input/select.mas, 'name' => $id . '_hour' , 'options' => \@hours, 'id' => "$id" . '_hour', 'disabled' => $disabled, 'value' => $data->hour(), &> : <& /input/select.mas, 'name' => $id . '_min' , 'options' => \@mins_secs, 'id' => "$id" . '_min', 'disabled' => $disabled, 'value' => $data->minute(), &> : <& /input/select.mas, 'name' => $id . '_sec' , 'options' => \@mins_secs, 'id' => "$id" . '_sec', 'disabled' => $disabled, 'value' => $data->second(), &> % } % else % { <& $data->HTMLViewer(), data => $data &> % } zentyal-core-2.3.21+quantal1/src/templates/ajax/setter/file.mas0000664000000000000000000000216412017102272021261 0ustar <%args> $tableName $data $cssClass => '' <%init> my $id = $tableName . '_' . $data->fieldName(); my $loadingId = $id . '_loading'; my $formId = $id . '_form'; my $onChange = << "JS_END"; var fileUpload = new EBox.FileUpload( { formId : '$formId', onStart : function() { setLoading('$loadingId'); }, onComplete : function() { setDone('$loadingId') ; }, } ); return fileUpload.submit(); JS_END % if ( $data->editable() ) {
<& /input/file.mas, name => $id . '_path', printableName => $data->printableName(), id => $id . '_path', onchange => $onChange, &>
% if ( $data->showFileWhenEditing() and $data->exist()) { <& $data->HTMLViewer(), data => $data &> % } % } % else % { <& $data->HTMLViewer(), data => $data &> % } zentyal-core-2.3.21+quantal1/src/templates/ajax/setter/unionSetter.mas0000664000000000000000000000264312017102272022663 0ustar <%args> $tableName $data $cssClass => '' $disabled => undef <%init> my $id = $tableName . '_' . $data->fieldName() . '_selected'; my @options; foreach my $type (@{$data->subtypes()}) { push (@options, { 'value' => $type->fieldName(), 'printableValue' => $type->printableName(), 'disabled' => $type->disabled(), }); } <& /input/select.mas, 'name' => "$id" , 'value' => $data->selectedType(), 'options' => \@options, 'id' => "$id", 'onchange' => qq{showSelected(this);}, 'disabled' => $disabled &> <%perl> if ( not defined ( $data->selectedType() )) { # If there's no selected data, set one $data->setSelectedType($data->subtypes()->[0]->fieldName()); } foreach my $type (@{$data->{'subtypes'}}) { my $cssClass = ''; if ( $type->fieldName() ne $data->selectedType() ) { $cssClass = 'hidden'; } if ( defined ( $type->HTMLSetter() )) { ' class='<% $cssClass %>'> <& $type->HTMLSetter(), 'data' => $type, 'tableName' => $tableName, 'disabled' => $disabled, &> % } % } zentyal-core-2.3.21+quantal1/src/templates/ajax/setter/multiSelectSetter.mas0000664000000000000000000000066612017102272024030 0ustar <%args> $tableName $data $cssClass => '' $disabled => undef % my $id = $tableName . '_' . $data->fieldName(); % # my $onChange = "eval('OnChangeOn$id(this)')"; % my $onChange = "OnChangeOn$id(this)"; <& /input/multiSelect.mas, 'name' => "$id" , 'value' => $data->value(), 'options' => $data->options(), 'id' => "$id", 'class' => "$cssClass", 'disabled' => $disabled, &> <% $data->trailingText() %> zentyal-core-2.3.21+quantal1/src/templates/ajax/setter/composite.mas0000664000000000000000000000105512017102272022342 0ustar <%doc> This template is intended to show the type setters within the composite type <%args> $tableName $data $cssClass => '' $disabled => undef <%init> my $id = $tableName . '_' . $data->fieldName(); % foreach my $simpleType (@{$data->types()}) { % if ( $data->showTypeName() ) { <% $simpleType->printableName() %>: % } <& $simpleType->HTMLSetter(), data => $simpleType, tableName => $tableName, disabled => $disabled &> % } zentyal-core-2.3.21+quantal1/src/templates/ajax/setter/inverseMatchSelectSetter.mas0000664000000000000000000000115012017102272025313 0ustar <%init> my %args = @_; my $tableName = delete $args{tableName}; my $data = delete $args{data}; my $cssClass = delete $args{cssClass}; defined $cssClass or $cssClass = ''; % my $id = $tableName . '_' . $data->fieldName() . '_inverseMatch'; <& '/ajax/setter/selectSetter.mas', 'data' => $data, 'tableName' => $tableName, 'cssClass' => $cssClass, %args &> <% __('Inverse match') . ':' %> <& /input/checkbox.mas, 'name' => "$id", 'value' => $data->inverseMatch(), 'id' => "$id", 'class' => "$cssClass", %args &> zentyal-core-2.3.21+quantal1/src/templates/ajax/setter/portRangeSetter.mas0000664000000000000000000000265212017102272023474 0ustar <%args> $tableName $data $cssClass => '' $disabled => undef <%init> my $id = $tableName . '_' . $data->fieldName(); my $typeSelected = $data->rangeType(); <& /input/select.mas, 'name' => $id . '_range_type' , 'value' => $data->rangeType(), 'options' => $data->rangeTypes(), 'id' => "$id" . "_range_type", 'onchange' => qq{showPortRange('$id');}, 'disabled' => $disabled &> <%__('From')%> ', <% $disabled ? 'disabled' : '' %> /> <%__('To')%> ' <% $disabled ? 'disabled' : '' %> /> ' <% $disabled ? 'disabled' : '' %> /> zentyal-core-2.3.21+quantal1/src/templates/ajax/setter/serviceSetter.mas0000664000000000000000000000207312017102272023170 0ustar <%args> $tableName $data $cssClass => '' $disabled => undef <%init> % my $id = $tableName . '_' . $data->fieldName(); <& /input/select.mas, 'name' => $id . '_protocol' , 'value' => $data->protocol(), 'options' => $data->protocols(), 'id' => "$id" . "_protocol", 'onchange' => qq{showPort("$id} . qq{_protocol", "$id} . qq{_portText", } . $data->protocolsJS() . ")", 'disabled' => $disabled &> zentyal-core-2.3.21+quantal1/src/templates/ajax/setter/dateSetter.mas0000664000000000000000000000453612017102272022453 0ustar <%args> $tableName $data $cssClass => '' $disabled => undef <%init> use EBox::Gettext; <& SELF:setter, tableName => $tableName, data => $data, cssClass => $cssClass, disabled => $disabled &> <%method setter> <%args> $tableName $data $cssClass => '' $disabled <%init> my @days; for (my $day = 1; $day <= 31; $day++) { push (@days, { 'value' => $day, 'printableValue' => $day, } ); } my @months = ({'value' => 1, 'printableValue' => __('January'),}, {'value' => 2, 'printableValue' => __('February'),}, {'value' => 3, 'printableValue' => __('March'),}, {'value' => 4, 'printableValue' => __('April'),}, {'value' => 5, 'printableValue' => __('May'),}, {'value' => 6, 'printableValue' => __('June'),}, {'value' => 7, 'printableValue' => __('July'),}, {'value' => 8, 'printableValue' => __('August'),}, {'value' => 9, 'printableValue' => __('September'),}, {'value' => 10, 'printableValue' => __('October'),}, {'value' => 11, 'printableValue' => __('November'),}, {'value' => 12, 'printableValue' => __('December'),}, ); my @years; for (my $year=1990; $year<2030; $year++) { push (@years, { 'value' => $year, 'printableValue' => $year, } ); } % my $id = $tableName . '_' . $data->fieldName(); % if ( $data->editable() ) { <& /input/select.mas, 'name' => $id . '_day' , 'options' => \@days, 'id' => "$id" . '_day', 'disabled' => $disabled, 'value' => $data->day(), &> / <& /input/select.mas, 'name' => $id . '_month' , 'options' => \@months, 'id' => "$id" . '_month', 'disabled' => $disabled, 'value' => $data->month(), &> / <& /input/select.mas, 'name' => $id . '_year' , 'options' => \@years, 'id' => "$id" . '_year', 'disabled' => $disabled, 'value' => $data->year(), &> % } % else % { <& $data->HTMLViewer(), data => $data &> % } zentyal-core-2.3.21+quantal1/src/templates/ajax/setter/timezoneSetter.mas0000664000000000000000000000526012017102272023363 0ustar <%args> $tableName $data $cssClass => '' $disabled => undef <%init> use EBox::Gettext; <& SELF:setter, tableName => $tableName, data => $data, cssClass => $cssClass, disabled => $disabled &> <%method setter> <%args> $tableName $data $cssClass => '' $disabled <%init> my $zones = $data->zones(); my $id = $tableName . '_' . $data->fieldName(); / zentyal-core-2.3.21+quantal1/src/templates/ajax/setter/selectSetter.mas0000664000000000000000000001065212017102272023011 0ustar <%args> $tableName $data $cssClass => '' $disabled => undef <%init> use EBox::Gettext; use Perl6::Junction qw(any); use EBox::Types::Select; my $id = $tableName . '_' . $data->fieldName(); ######################### # EVIL HACK ######################### # This is done here, since it makes a deep recursion error on Select # type class at options method. This is due to rows implementation # which always ask for printableValues and it is not possible to ask # for value rows. This does not work when the edit/add will be # generated by JavaScript. # # We are very ashamed about this evil hack, we want to apologise for # that. Time constraints make us do this crap. Sorry. my $options = $data->options(); my $empty; my @filteredOptions; my @optionsAlreadyModel = (); my $value = $data->value(); if ($data->unique()) { my $valueFound = 0; # Filter the options in other just to show the ones allowed my $model = $data->model(); if ( defined ( $model )) { my $field = $data->fieldName(); foreach my $id (@{$model->ids()}) { # Check if the element exists to avoid nasty issues # with union types my $row = $model->row($id); next unless ($row->elementByName($field)); push( @optionsAlreadyModel, $row->valueByName($field) ); } # Difference among optionsAlreadyModel and options arrays @filteredOptions = grep { ! ($_->{value} eq any(@optionsAlreadyModel)) } @{$options}; # Add the current value if the action is an edition if ( $data->isValueSet() ) { $valueFound = 1; push ( @filteredOptions, { value => $data->value(), printableValue => $data->printableValue(), } ); @filteredOptions = sort { $a->{value} cmp $b->{value} } ( @filteredOptions ); } else { foreach my $opt (@filteredOptions) { if ($opt->{value} eq $value) { $valueFound = 1; last; } } } $options = \@filteredOptions; $empty = (@filteredOptions == 0); if (not $valueFound and not $empty) { $value = $options->[0]->{value}; } } } else { $empty = @{ $options } == 0; } my @extraParams; my $addNewJS; my $addNewModalValue = EBox::Types::Select::ADD_NEW_MODAL_VALUE; my $foreignModel = $data->foreignModel(); if ($foreignModel and $data->foreignNextPageField()) { my $alreadyAdded = grep {$_->{value} eq $addNewModalValue } @{ $options }; if (not $alreadyAdded) { unshift @{ $options }, ( { printableValue => __('Add new...'), value => $addNewModalValue, }, { printableValue => '
', disabled => 1, value => 'separator', }, ); } my $nextPageField = $data->foreignNextPageField(); my ($nextPageType) = grep { $_->fieldName() eq $nextPageField } @{ $foreignModel->table()->{tableDescription} }; my $modelManager = EBox::Model::Manager->instance(); my $nextPageModel = $modelManager->model($nextPageType->foreignModel()); my $nextPageModelContextName = $nextPageModel->contextName(); $addNewJS = $foreignModel->modalChangeViewJS( changeType => "changeAdd", editId => "undefined", selectCallerId => $id, selectForeignField => $data->foreignField(), nextPageContextName => $nextPageModelContextName, foreignNextPageField => $nextPageField, firstShow => 1, ); my $onChangeJS = << "ENDJS"; if (this.options[this.selectedIndex].value == '$addNewModalValue') { $addNewJS } ; return false; ENDJS push @extraParams, onChange => $onChangeJS; if ($empty) { push @extraParams, style => 'display:none'; } } unless ($data->editable()) { $disabled = 'disabled'; } <& /input/select.mas, 'name' => "$id" , 'value' => $value, 'options' => $options, 'id' => "$id", 'class' => "$cssClass", 'disabled' => $disabled, @extraParams &> % if ($addNewJS and $empty) {
<% __('No elements found. ') %> <% __('Add a new one') %>

% } <% $data->trailingText() %> zentyal-core-2.3.21+quantal1/src/templates/ajax/setter/ipRangeSetter.mas0000664000000000000000000000117712017102272023121 0ustar <%args> $tableName $data $cssClass => '' $disabled => undef % my $id = $tableName . '_' . $data->fieldName(); % if ( $data->editable() ) { ' name='<% $id . '_begin' %>' <% $disabled ? 'disabled' : '' %> /> - ' name='<% $id . '_end' %>' <% $disabled ? 'disabled' : '' %> /> % } % else % { <& $data->HTMLViewer(), data => $data &> % } zentyal-core-2.3.21+quantal1/src/templates/ajax/setter/ipnetworkSetter.mas0000664000000000000000000000100412017102272023543 0ustar <%flags> inherit => '/ajax/setter/ipaddrSetter.mas' <%args> $tableName $data $cssClass => '' $disabled => undef <%init> my @masks = (30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8); <& PARENT:setter, tableName => $tableName, data => $data, cssClass => $cssClass, masks => \@masks, defaultMask => 24, disabled => $disabled &> zentyal-core-2.3.21+quantal1/src/templates/ajax/setter/ipaddrSetter.mas0000664000000000000000000000246012017102272022773 0ustar <%args> $tableName $data $cssClass => '' $disabled => undef <%init> my @masks = (32, 30, 29, 28, 27, 26, 25, 24, 23, 22, 21, 20, 19, 18, 17, 16, 15, 14, 13, 12, 11, 10, 9, 8); <& SELF:setter, tableName => $tableName, data => $data, cssClass => $cssClass, masks => \@masks, disabled => $disabled &> <%method setter> <%args> $tableName $data $cssClass => '' $defaultMask => 32 @masks $disabled <%init> my @options; for my $mask (@masks) { push (@options, {'value' => $mask, 'printableValue' => $mask}); } my $defaultMaskValue = $data->mask(); unless ( $defaultMaskValue ) { $defaultMaskValue = $defaultMask; } % my $id = $tableName . '_' . $data->fieldName(); % if ( $data->editable() ) { ' name='<% $id . '_ip' %>' <% $disabled ? 'disabled' : '' %> /> / <& /input/select.mas, 'name' => $id . '_ip' , 'value' => $defaultMaskValue, options => \@options, 'id' => "$id" . "_mask", 'disabled' => $disabled &> % } % else % { <& $data->HTMLViewer(), data => $data &> % } zentyal-core-2.3.21+quantal1/src/templates/ajax/setter/hasManySetter.mas0000664000000000000000000000026012017102272023124 0ustar <%args> $tableName $data $cssClass => '' % my $id = $tableName . '_' . $data->fieldName(); -- zentyal-core-2.3.21+quantal1/src/templates/ajax/setter/passwordSetter.mas0000664000000000000000000000155612017102272023377 0ustar <%doc> Setter for password type <%args> $tableName $data $cssClass => '' $disabled => undef <%perl> my $id = $tableName . '_' . $data->fieldName(); if ($data->editable()) { my $passValue = $data->printableValue(); $passValue = '' unless defined ( $passValue ); <& /input/password.mas, name => $id, value => $passValue, id => $id, disabled => $disabled &> % # Add CSS class if any % if ( $cssClass ne '' ) % { % } % } else { % if ( $data->value() ) { <& /input/hidden.mas, name => $id, value => $data->value(), id => $id, disabled => $disabled &> % } else { <& /input/hidden.mas, name => $id, value => $id, disabled => $disabled &> % } <& /ajax/viewer/passwordViewer.mas, data => $data &> % } zentyal-core-2.3.21+quantal1/src/templates/ajax/graph.mas0000664000000000000000000001151712017102272020137 0ustar <%flags> inherit => '/ajax/tableBody.mas' <%args> $model $hasChanged $action => 'view' <%init> use EBox::Gettext; use EBox::Model::DataTable; sub timeTickFormatter { my ($timePeriod) = @_; if ($timePeriod eq 'monthly') { return < 1000) { date = new Date(ts*1000); } else { date = new Date(); } var dateParts = date.toDateString().split(' ', 4); var dateSt = dateParts[1] + ' ' + dateParts[3]; return dateSt; } END } elsif ($timePeriod eq 'weekly') { return < 1000) { date = new Date(ts*1000); } else { date = new Date(); } return date.toDateString(); } END } elsif ($timePeriod eq 'daily') { return < 1000) { date = new Date(ts*1000); } else { date = new Date(); } return date.toDateString(); } END } elsif ($timePeriod eq 'hourly') { return < 1000) { date = new Date(ts*1000); } else { date = new Date(); } var dateSt = date.toDateString(); dateSt += ' ' + date.getHours() + ':00'; return dateSt; } END } else { return < 1000) { date = new Date(ts*1000); } else { date = new Date(); } return date.toDateString(); } END } } <& SELF:view, model => $model, hasChanged => $hasChanged, action => $action, &> <%doc> % # Anyway you should call the change menu sub-component to check if % # any change has been done <& SELF:changeMenu, hasChanged => $hasChanged &> <%method _body> <%args> $model <%init> my $stackDeep = 2; my %childArgs = $m->caller_args($stackDeep); my $action = exists $childArgs{action} ? $childArgs{action} : 'view'; my $reloadAction = $action eq 'changeList'; % if ( $model->printableName() ) { <& PARENT:title, title => $model->printableName() &>
% } <& SELF:graph, model => $model &> <%method graph> <%args> $model <%init> my $container = $model->name() . 'GraphContainer'; my @dataSets = @{ $model->datasets }; my @labels = @{ $model->datasetsLabels }; # this supposes that all datsets have the same number of points my $noTicks = (scalar @{ $dataSets[0] }) - 1; ($noTicks < 1) and $noTicks = 1; my $timeTickFormatter = timeTickFormatter($model->timePeriod()); my $graphData = '[ '; foreach my $ds (@dataSets) { my $label = shift @labels; $graphData .= "{ \n"; $graphData .= "label: '$label',\n"; $graphData .= 'data: ['; foreach my $set ( @{ $ds } ) { my $elements = join ',', @{ $set }; $graphData .= "[ $elements ],"; } $graphData .= " ],\n"; if (@{ $ds } < 2) { $graphData .= 'points: {show: true, },'; } # $graphData .= 'mouse: { track: true, trackDecimals: 0, },'; $graphData .= " },\n"; } $graphData .= ' ]'; my $graphOptions = <<"END"; { xaxis: { autoscaleMargin: 0.001, tickDecimals: 0, tickFormatter: $timeTickFormatter, noTicks: $noTicks} , yaxis: { autoscaleMargin: 0.1, noTicks: $noTicks }, } END

<%method headerScriptSection> <& PARENT:headerScriptSection &> <%method editForm> $m->abort('images cannot be edited'); <%method editRowFields> $m->abort('images cannot be edited'); zentyal-core-2.3.21+quantal1/src/templates/ajax/composite.mas0000664000000000000000000000534512017102272021042 0ustar <%doc> This template will show the content from a model composite, that is the content from its components in an established layout. Parameters: model - the composite model to show hasChanged - Boolean indicating whether the composite model has changed or not <%args> $model $hasChanged <%init> use EBox::Gettext; use Data::Dumper; <& .pageTitle, crumbs => $model->HTMLTitle() &> <%perl> # Annotate we are in composite after printing own title $m->notes('composite', 1); <& .headTitle, title => $model->headTitle() &> % if ($model->precondition()) { % # Define the components when we are sure the precondition is % # matched % my $componentsRef = $model->components(); % # Print the help if any % my $help = $model->help(); % if ($help) {
<% $model->help() %>
% } % # Print the disabled module warning if needed % my $disabledModuleWarning = $model->disabledModuleWarning(); % if ($disabledModuleWarning) {
<% $disabledModuleWarning %>
% } % # Print the permanent message if any % my $permanentMessage = $model->permanentMessage(); % my $permanentMessageType = $model->permanentMessageType(); % if ($permanentMessage) {
<% $permanentMessage %>
% } % if ( $model->layout() eq 'tabbed' ) % { <& /ajax/tabMenu.mas, models => $componentsRef, selectedTab => 0, hasChanged => $hasChanged, tabName => $model->name(), directory => $model->directory(), &> % } % elsif ( $model->layout() eq 'top-bottom' ) % { % foreach my $component (@{$componentsRef}) { <& $component->Viewer(), model => $component, hasChanged => $hasChanged, action => 'view', &> % } % } % elsif ( $model->layout() eq 'select' ) % { <& /ajax/selector.mas, composite => $model, hasChanged => $hasChanged, &> % } % } % else % { % my $failMsg = $model->preconditionFailMsg(); % if ($failMsg) {
<% $failMsg %>
% } % } <%perl> # Annotate we are no longer in composite $m->notes('composite', 0); <%doc> Method: pageTitle Set the page title Parameters: title - String the title <%def .pageTitle> <%args> $crumbs => undef <& /title.mas, crumbs => $crumbs &> <%doc> Method: .headTitle Private component to print the title if any. It takes into account if the composite is the root one or not to determine which kind of title it is Parameters: title - String the title to print <%def .headTitle> <%args> $title => undef % if ($title) {

<% $title %>

% } zentyal-core-2.3.21+quantal1/src/templates/ajax/form.mas0000664000000000000000000000350612017102272020000 0ustar <%doc> This template is indicated to view the EBox::Model::DataForm. It will show a form with the model description to be edited all the time. The original overridden call at /ajax/modelViewer.mas <%flags> inherit => '/ajax/modelViewer.mas' <%args> $model $hasChanged $action => 'view' <%init> use EBox::Gettext; use EBox::Model::DataTable; <& PARENT:view, model => $model, hasChanged => $hasChanged, action => $action, &> <%doc> Method: _body Set the model viewer body. Overrides: /ajax/modelViewer.mas:_body <%method _body> <%args> $model <%init> my $formDataRef; if ( defined ( $model->row() )) { $formDataRef = $model->row()->{'values'}; } else { $formDataRef = $model->table()->{'tableDescription'}; } <& /title.mas, crumbs => $model->viewCustomizer()->HTMLTitle() &> <& PARENT:title, title => $model->headTitle() &> <& SELF:messagesAndHelp, model => $model &> <& SELF:editForm, modelName => $model->name(), formData => $formDataRef &> <%doc> Method: buttons Show the button to submit the form Overrides: /ajax/modelViewer.mas:buttons <%method buttons> <%init> # Getting the arguments from the first request my $model = $m->caller_args(2)->{model}; my $id = $model->row()->id(); my $actions = $model->table()->{actions}; my $onClick = $model->changeRowJS($id, 0) . '; return false'; $onClick = $model->confirmationJS('submit', $onClick); % if (defined $actions and (keys %{ $actions} > 0)) { <& /input/submit.mas, name => 'change', value => $model->printableActionName(), onclick => $onClick, &> % } <& /ajax/customActions.mas, model => $model, id => $id &> zentyal-core-2.3.21+quantal1/src/templates/ajax/tableHeader.mas0000664000000000000000000000141712017102272021234 0ustar <%args> @data $dataTable $hasChanged $tpages => 0 $model <%init> use EBox::Gettext; my @tableHead = @{$dataTable->{'tableDescription'}};
<% $dataTable->{'help'} %>
'>
<& /ajax/tableBody.mas, 'data' => \@data, 'dataTable' => $dataTable, 'model' => $model, 'hasChanged' => $hasChanged, 'tpages' => $tpages &>
zentyal-core-2.3.21+quantal1/src/templates/ajax/tableModal.mas0000664000000000000000000001720312017102272021100 0ustar <%flags> inherit => '/ajax/tableBody.mas' <%args> $model $action => 'view' $editid => '' $hasChanged => 1 $filter => '' $page => 0 $tpages => 0 %presetParams => () $hideTable => 1 $selectCallerId => undef $selectForeignField => undef $nextPageContextName => undef $foreignNextPageField => undef <%init> use EBox::Gettext; use EBox::Model::DataTable; use POSIX; <& SELF:view, model => $model, hasChanged => $hasChanged, action => $action, selectCallerId => $selectCallerId, selectForeignField => $selectForeignField, nextPageContextName => $nextPageContextName, foreignNextPageField => $foreignNextPageField, &> <%method view> <%args> $model <& SELF:header, model => $model &> % if ( $model->precondition() ) {
<& SELF:body, model => $model &>
% } % else % { % my $failMsg = $model->preconditionFailMsg(); % if ($failMsg) {
<% $failMsg %>
% } % } <%method header> <%args> $model <& SELF:headerScriptSection &>
<%doc> Method: _body Set the model viewer body. Check the overridden method very carefully. Overrides: /ajax/modelViewer.mas:_body <%method _body> <%args> $model $action => 'view' $editid => '' $hasChanged => 1 $filter => '' $page => 0 $tpages => 0 %presetParams => () <%init> use EBox::Gettext; # Fetch table head from model my @tableHead = @{$model->table()->{'tableDescription'}}; # Pointer to different values for convenience my $actions = $model->table()->{'actions'}; my $changeView = $model->action('changeView'); my $printableRowName = $model->table()->{'printableRowName'}; my $tableName = $model->table()->{'tableName'} . '_modal'; my $printableTableName = $model->table()->{'printableTableName'}; # Fetch the edited row if we are editing one my $rowEdit; if ($editid and ($editid ne 'undefined')) { $rowEdit = $model->row($editid); } # Fetch the data to be displayed in setters, if we are editing a row # we should show the contents of the current fields. If we are # adding a new row, the fields are empty except for a preset values # adding where preset contents must be shown my @formData = @tableHead; if ($rowEdit) { @formData = @{$rowEdit->elements()}; } elsif ( ((keys %presetParams) > 0) and $action eq 'presetUpdate') { # The preset is not empty @formData = values(%presetParams); } # If the action is a preset update, choose for an edition or an # addition whether the editid is set or not if ( $action eq 'presetUpdate' ) { $action = 'changeAdd' if (not $editid); $action = 'changeEdit' if ( $editid ); }
% if ( $model->pageTitle() ) { % my $viewCustomizer = $model->viewCustomizer(); % my $msg = $viewCustomizer ? $viewCustomizer->permanentMessage() : undef; % if ($msg) { <& /msg.mas, msg => $msg &> % } % if ( $model->message() ) { <& /msg.mas, msg => $model->popMessage(), class => $model->messageClass() &> % } % } % if ($model->help()) {
<% $model->help() %>
% } % if ( $action ne 'changeAdd' and $action ne 'changeEdit' ) % { % if ( $model->printableModelName() ) % { <& PARENT:title, title => $model->headTitle() &> % } % if ($changeView and exists $actions->{'add'}) % { "undefined", page => $page, isFilter => 0) %>;return false"><% __('Add new') %>

% } % } % if ( $action eq 'changeAdd' or $action eq 'changeEdit' ) % { % if ( $action eq 'changeAdd' ) % { % my $addingStr = __x('Adding a new {row}', row => $printableRowName); <& PARENT:title, title => $addingStr &> % } % elsif ( $action eq 'changeEdit' ) % {

% my $editingStr = __x('Editing {row}', row => $printableRowName); <& PARENT:title, title => $editingStr &> % } <& PARENT:editRowFields, modelName => $tableName, formData => \@formData &> <& SELF:buttons &>
% my $customizer = $formData[0]->model()->viewCustomizer(); % if ($customizer) { % } % }

% # End body method <%doc> Method: buttons Show the button to submit the form Overrides: /ajax/modelViewer.mas:buttons <%method buttons> <%init> # Getting the arguments from the first request my $requestArgs = $m->request_args(); my $model = $requestArgs->{model}; my $action = $requestArgs->{action}; my $editid = $requestArgs->{editid}; my $filter = $requestArgs->{filter}; my $page = $requestArgs->{page}; my $tpages = $requestArgs->{tpages}; my $selectCallerId = $requestArgs->{selectCallerId}; my $selectForeignField = $requestArgs->{selectForeignField}; my $nextPageContextName = $requestArgs->{nextPageContextName}; my $foreignNextPageField = $requestArgs->{foreignNextPageField}; my $changeView = $model->action('changeView'); if ( $action eq 'presetUpdate' ) { $action = 'changeAdd' if ( not $editid ); $action = 'changeEdit' if ( $editid ); } % if ( $action eq 'changeAdd' ) { <%perl> my $addOnClick; if ($foreignNextPageField) { $addOnClick = $model->modalAddNewRowJS($page, $foreignNextPageField, selectCallerId => $selectCallerId, selectForeignField => $selectForeignField, nextPageContextName => $nextPageContextName, ); $addOnClick .= ';'; } else { $addOnClick .= 'Modalbox.hide();' ; } $addOnClick .= 'return false'; <& /input/submit.mas, class => 'inputButton', type => 'submit', name => 'add', value => __('Add'), title => 'Add', onclick => $addOnClick, &> % } elsif ($action eq 'changeEdit') { <%perl> my $editOnClick = $model->changeRowJS($editid, $page) . ';' . 'Modalbox.hide();' . 'return false'; <& /ajax/customActions.mas, model => $model, id => $editid &> <& /input/submit.mas, class => 'inputButton', type => 'submit', name => 'change', value => __('Change'), title => 'Change', onclick => $editOnClick &> % } % if ($changeView) { <& /input/submit.mas, class => 'inputButton', type => 'submit', name => 'cancel', value => __('Cancel'), title => 'Cancel', onclick => 'Modalbox.hide(); return false' &> % } zentyal-core-2.3.21+quantal1/src/templates/ajax/typeRowLayout.mas0000664000000000000000000000113012017102272021673 0ustar <%args> $modelName $type $displayRow $disableSetter > % if (defined ($type->printableName())) { <% $type->printableName() %>: % } % if ($type->optionalLabel()) {
<% __('Optional') %>
% } <& $type->HTMLSetter(), 'data' => $type, 'tableName' => "$modelName", 'disabled' => $disableSetter &>
<% $type->help() %>
zentyal-core-2.3.21+quantal1/src/templates/wizard.mas0000664000000000000000000001331712017102272017413 0ustar <%args> @pages $first => 0 $image_title => '/data/images/title.png' <%init> use EBox::Gettext;
zentyal-core-2.3.21+quantal1/src/templates/table.mas0000664000000000000000000000545712017102272017210 0ustar <%doc> HTML table component Parameters: @columnTitles - The titles of the columns @rows - The rows from the table. Each row is a array reference which contains their elements @additionalComponents - Additional mason components to add to the table. Remember to wrap elements between and if you want the component inside of the table's rows <%args> @columnTitles => () @rows => () @additionalComponents => () <%attr> tableClass => undef theadClass => undef thClass => undef tbodyClass => undef trClass => undef tdClass => undef <%init> sub _class { my ($class) = @_; defined $class ? "class='$class'" : "" } <& SELF:table, columnTitles => \@columnTitles, rows => \@rows, additionalComponents => \@additionalComponents &> <%method table> <%args> @columnTitles => () @rows @additionalComponents => () <%init> my $tableClass = $m->base_comp->attr('tableClass'); > <& SELF:thead, columnTitles => \@columnTitles &> <& SELF:tbody, rows => \@rows, additionalComponents => \@additionalComponents &>
<%method thead> <%args> @columnTitles => () <%init> my $theadClass = $m->base_comp->attr('theadClass'); > <& SELF:theadContent, columnTitles => \@columnTitles &> <%method theadContent> <%args> @columnTitles => () <%init> my $thClass = $m->base_comp->attr('thClass'); % foreach my $title (@columnTitles) { <& SELF:th, text => $title, thClass => $thClass &> % } <%method th> <%args> $text $thClass => undef > <% $text %> <%method tbody> <%args> @rows @additionalComponents => () <%init> my $tbodyClass = $m->base_comp->attr('tbodyClass'); > <& SELF:rows, rows => \@rows &> <& SELF:additionalComponents, components => \@additionalComponents &> <%method rows> <%args> @rows % foreach my $row_r (@rows) { <& SELF:tr, row => $row_r &> % } <%method tr> <%args> @row <%init> my $trClass = $m->base_comp->attr('trClass'); > <& SELF:trContent, row => \@row &> <%method trContent> <%args> @row <%init> my $tdClass = $m->base_comp->attr('tdClass'); % foreach my $element ( @row ) { <& SELF:td, element => $element, tdClass => $tdClass &> % } <%method td> <%args> $element $tdClass => undef > <% $element %> <%method additionalComponents> <%args> @components % return if (@components == 0); <& /componentCall.mas, @components &> zentyal-core-2.3.21+quantal1/src/templates/pageNotFound.mas0000664000000000000000000000116412017102272020501 0ustar <%init> use EBox::Gettext; my $eboxHomepageName = __(q{Zentyal homepage}); my $eboxHomepageAHref = "$eboxHomepageName "; my @tipList = ( __(q{If you typed the URL manually, please check it}), __(q{Maybe a change in the Zentyal configuration or software made no longer available the requested page}), __x(q{If you need more help, you may want visit {homepage} for documentation and mailing lists}, homepage => $eboxHomepageAHref), );

<% __(q{we're sorry}) %>

<% __('The page cannot be found') %>
    % foreach my $tip (@tipList) {
  • <% $tip %>
  • % }
zentyal-core-2.3.21+quantal1/src/templates/common/0000775000000000000000000000000012017102272016674 5ustar zentyal-core-2.3.21+quantal1/src/templates/common/enable.mas0000664000000000000000000000244512017102272020631 0ustar <%doc> This template is indicated to view EBox::Common::Model::EnableForm. It will show a standard form with a the enable CSS class. <%flags> inherit => '/ajax/form.mas' <%args> $model $hasChanged <%init> use EBox::Gettext; <& PARENT:view, model => $model, hasChanged => $hasChanged &> <%doc> Method: editForm Show the form to submit if the service is enabled or not Overrides: /ajax/modelViewer.mas:editForm <%method editForm> <%args> $modelName @formData <%init> my $type = $formData[0];
<% $type->printableName() %> : <& $type->HTMLSetter(), 'data' => $type, 'tableName' => $modelName &> "> <& SELF:buttons &>
<%doc> Method: buttons Show the button to submit the form Overrides: /ajax/modelViewer.mas:buttons <%method buttons> <%init> # Getting the arguments from the first request my $model = $m->caller_args(2)->{model}; <& /input/submit.mas, name => 'change', class => 'inputButton', value => __('Change'), onclick => $model->changeRowJS(0, 0) . '; return false', &> zentyal-core-2.3.21+quantal1/src/templates/backup.mas0000664000000000000000000002617312017102272017364 0ustar <%args> @backups => () $modulesChanged => 1 $subscribed => undef <%init> use EBox::Gettext; use EBox::Config; % if ($subscribed) {
<% __sx('Thanks to your server subscription, the configuration backup is checked on daily basis and stored automatically on the Zentyal Cloud if any configuration changes are detected. You may check all your configuration backups {ohref}here{chref}.', ohref => '', chref => '') %>
% } else {
<% __sx('Get a FREE {ohref}Basic Server Subscription{chref}! It gives you a preview of Zentyal Cloud and allows access to some features, such as basic alerts, reports, monitoring options and jobs. In addition, it allows you to store one configuration backup remotely and create zentyal.me subdomain for your server.', ohref => '', chref => '') %>
% } <& .backupSection, modulesChanged => $modulesChanged &> <& .restoreSection &> <& .listSection, backups => \@backups &> <& .reportSection &> <%def .backupSection> <%args> $modulesChanged <%init> my $disabledAttr = $modulesChanged ? 'disabled' : '';
<% __x('Those modules which have not been enabled for first time in the {openref}Module Status{closeref} section will be ignored in both backup and restore operations.', openref => '', closeref => '') %>

<% __('Backup the Current State') %>

<% __("Backups will be stored in Zentyal and then you will be able to download them to your hard disk.") %>
<% __("If you have the file sharing module configured, all users having administration rights will be able to access the backup directory as a shared resource.") %>
% if ($modulesChanged) {
<% __(q{Backup is disabled because there are unsaved configuration changes. Please, save or discard them to be able to backup the configuration}) %>
% }

<% __('Description') %>:
<%perl> my $backupTitle = __('Backup'); my $backupOnClickJs =<< "ENDJS" ; var description= \$('backup_description').value; var title = '$backupTitle: ' + description; var url = '/SysInfo/Backup?'; url += '&description=' + escape(description); url += '&backup=1&popup=1' Modalbox.show(url, {title: title, transitions: false}); return false ENDJS name='backup' value="<% $backupTitle %>" onclick="<% $backupOnClickJs %>" />

<%def .restoreSection> <%init> my $id = 'backupfile'; my $loadingId = $id . '_loading'; my $formId = $id . '_form'; my $onChange = << "JS_END"; var fileUpload = new EBox.FileUpload( { formId : '$formId', onStart : function() { setLoading('$loadingId'); }, onComplete : function() { setDone('$loadingId') ; }, } ); var res = fileUpload.submit(); \$('restoreFromFile').disabled = false; return res; JS_END my $title = __('Restore'); my $uploadLocation = EBox::Config::tmp() . 'path'; my $onClickJs =<< "ENDJS" ; var backupfile = '$uploadLocation'; var url = '/SysInfo/ConfirmBackup?restoreFromFile=1'; url += '&backupfile=' + escape(backupfile); url += '&alreadyUploaded=1&popup=1'; Modalbox.show(url, {title: '$title', transitions: false}); return false; ENDJS

<% __('Restore Backup from File') %>

<% __('You can upload a backup file from your computer. When uploading files you should be warned that some web browsers cannot handle huge files (>= 1 Gb).') %>
<% __('Backup file') %>:
<& /input/file.mas, name => $id . '_path', id => $id . '_path', onchange => $onChange, &>
<%def .listSection> <%args> @backups <%init> return if not @backups; my $modalTitle = __('Restore backup'); my $restoreTitle = __('Restore configuration using this file'); my $restoreAlt = $modalTitle; my $downloadTitle = __('Download backup file'); my $deleteTitle = __('Delete backup'); my %printableTypeById = ( $EBox::Backup::FULL_BACKUP_ID => __('Full'), $EBox::Backup::CONFIGURATION_BACKUP_ID => __('Configuration only'), 'unknown' => __('Unknown type'), ); % if(@backups){

<% __("Backups' list") %>

% foreach my $backup (@backups) { % }
<%__('Description') %> <%__('Date')%> <% __('Archive size') %> <%__('Actions') %>
<% $backup->{'description'} %> <% $backup->{'date'} %> <% $backup->{'size'} %>
<%perl> my $restoreUrl = '/SysInfo/ConfirmBackup?'; $restoreUrl .= 'id=' . $backup->{'id'} . '&'; $restoreUrl .= 'restoreFromId=1&popup=1'; - - <%perl> my $deleteUrl = '/SysInfo/ConfirmBackup?'; $deleteUrl .= 'id=' . $backup->{'id'}; $deleteUrl .= '&delete=1&popup=1';

<%perl> my @legendElements = ( { imgFile => "/data/images/download.gif", imgName => __('download') }, { imgFile => "/data/images/restore.gif", imgName => __('restore') }, { imgFile => "/data/images/delete.gif", imgName => __('delete') } ); <& legendTable.mas, elements => \@legendElements, nColumns => 3 &> % } <%def .reportSection>

<% __('Configuration Report') %>

<% __('You can generate a file with information about the state of your system. This file can be useful if you want to report a problem or seek for support.')%>
zentyal-core-2.3.21+quantal1/src/templates/componentCall.mas0000664000000000000000000000147312017102272020711 0ustar <%doc> A element to call flexibly one or more components. If the argument list is a list of references to list it will treat each list as a component call. Otherwise, it will be handled as a single component call. Mixed list are interpreted as the type of the first element TODO: sanity check when the first element is ARRAYREF and the rest isn't TODO: optional argument to force the type of list? <%init> my @params = @_; return if @params == 0; my @calls; my $refType = ref $params[0]; if (not $refType) { # single component call @calls = (\@params); } elsif ($refType eq 'ARRAY') { @calls = @params; } else { $m->abort('You must supply either a flat parameter list or a list of references to parameter lists'); } foreach my $call_r (@calls) { $m->comp(@{ $call_r }); } return; zentyal-core-2.3.21+quantal1/src/templates/dataInUse.mas0000664000000000000000000000227212017102272017766 0ustar <%args> $warning $url $params <%init> use EBox::Gettext; use Data::Dumper; my $table = delete $params->{'tablename'}; my $action = delete $params->{'action'}; my $directory = delete $params->{'directory'}; my $rowId = delete $params->{'id'}; my $page = delete $params->{'page'}; $page = 0 unless ( $page );
<% $warning %>
% if ( $action eq 'edit' ) % { % my @fields = map { "'" . $_ . "'" } keys %{$params}; % } % else % { % } zentyal-core-2.3.21+quantal1/src/templates/link.mas0000664000000000000000000000061012017102272017040 0ustar <%doc> Component for HTML hyperlinks <%args> $text => '' $href $image => undef $onclick => undef <%init> if ($text eq '') { $text = $href; } % if (defined $image) { title="<% $text %>" alt="<% $text %>"/> % } % else { <% $text %> % } zentyal-core-2.3.21+quantal1/src/templates/progress.mas0000664000000000000000000001644412017102272017763 0ustar <%doc> This template is used to display the progress of the progress indicator. <%args> #$currentItemUrl $progressId $text => '' $currentItemCaption => undef $itemsLeftMessage => undef $endNote => undef $errorNote => undef $currentItemUrl => '/SysInfo/CurrentProgress' $reloadInterval => 2 $adsJson => '""' $inModalbox => undef $nextStepType => 'url', $nextStepUrl => '/Dashboard/Index' $nextStepUrlOnclick => undef $nextStepUrlFailureOnclick => undef $nextStepText => undef $nextStepTimeout => 0 <%init> use EBox::Gettext; use EBox::ProgressIndicator; defined $currentItemCaption or $currentItemCaption = __('Item'); defined $itemsLeftMessage or $itemsLeftMessage = __('items left'); defined $endNote or $endNote = __('Done'); defined $errorNote or $errorNote = __('Some error has ' . 'happened in Zentyal: ' ); my $progressIn = EBox::ProgressIndicator->retrieve($progressId); unless (defined ($nextStepText)) { $nextStepText = __('Click here to go to the Dashboard'); } my $barStyle =''; my $progresValueStyle =''; my $percentMargin; if ($inModalbox) { $barStyle = 'style="padding-left: 0pt; width: 450px; height: 30px; margin-left: 18px; margin-top: 20px;"'; $progresValueStyle = 'style="padding-left: 0pt; width: 0px; height: 30px; margin-left: 0px; margin-top: 20px;"'; $percentMargin = 245; } if ((defined $nextStepUrlOnclick) and (not defined $nextStepUrlFailureOnclick)){ $nextStepUrlFailureOnclick = $nextStepUrlOnclick; } % if ($inModalbox) { % } % unless ( $adsJson eq '""' ) {
% }

<% $text %>

<% $currentItemCaption %>: <% __("Starting...")%>
>
>

-<% __(" of ") %> -<% ' ' . $itemsLeftMessage %>

% } zentyal-core-2.3.21+quantal1/src/templates/enable.mas0000664000000000000000000000144612017102272017341 0ustar <%args> $active $title => undef $action => 'Enable' <%init> use EBox::Gettext; defined $title or $title = __("Service configuration"); if (($active ne 'yes') and ($active ne 'no')) { $active = $active ? 'yes' : 'no'; }
<% $title %>:
zentyal-core-2.3.21+quantal1/src/templates/moduleStatus.mas0000664000000000000000000000060312017102272020576 0ustar <%args> @modules <%init> use EBox::Gettext;
<& moduleStatusTable.mas, 'modules' => \@modules &>
zentyal-core-2.3.21+quantal1/src/templates/js/0000775000000000000000000000000012017102272016020 5ustar zentyal-core-2.3.21+quantal1/src/templates/js/onchange.mas0000664000000000000000000000040712017102272020305 0ustar <%args> $fieldName $JSONActions $tableName <%init> my $fullId = "${tableName}_${fieldName}"; Event.observe($('<% $fullId %>'), 'change', function (event) { onFieldChange(event, <% $JSONActions %>, '<% $tableName %>') } ); zentyal-core-2.3.21+quantal1/src/templates/confirm-backup.mas0000664000000000000000000000732412017102272021014 0ustar <%args> $backup $action $actiontext $popup => 0 <%init> use EBox::Gettext; use Perl6::Junction qw(any); use EBox::Backup; use URI::Escape; my $onclickSubmit = ''; my $onclickCancel = ''; if ($popup) { my $title; my $url = '/SysInfo/Backup?'; $url .= "$action=1"; if ($action eq any(qw(restoreFromId restoreFromFile))) { $title = __x('Restore backup: {desc}', desc => $backup->{description}); if ($action eq 'restoreFromId') { $url .= '&id=' . $backup->{id}; } elsif ($action eq 'restoreFromFile') { $url .= '&backupfile=' . uri_escape($backup->{file}); } $url .= "&popup=1"; $onclickSubmit = qq{onclick="Modalbox.show('$url', {title:'$title', transitions: false}); return false"}; } elsif ($action eq 'delete') { $url .= '&id=' . $backup->{id}; $onclickSubmit = qq(onclick="Modalbox.hide(); window.location='$url'; return false"); } $onclickCancel = qq{onclick="Modalbox.hide(); return false"}; }
% if (exists $backup->{'size'}) { % } <& /input/hidden.mas, name => 'popup', value => $popup &>
<% __('Date') %>: <% $backup->{'date'} %>
<% __('Description') %>: <% $backup->{'description'} %>
<% __('Archive size') %>: <% $backup->{'size'} %>
<& .backupIdentifier, backup => $backup &> /> />

<%def .backupIdentifier > <%args> $backup <%init> my $name; my $value; if (exists $backup->{id}) { $name = 'id'; $value = $backup->{id}; } elsif (exists $backup->{file}) { $name = 'backupfile'; $value = $backup->{file} } else { die "bad backup details data"; } <& /input/hidden.mas, name => $name, value => $value &> zentyal-core-2.3.21+quantal1/src/templates/notConfigured.mas0000664000000000000000000000052412017102272020715 0ustar <%args> $module <%init> use EBox::Gettext;
<%__x('You must enable the {module} module on the {openhref} Module Status section {closehref} in order to use it.', module => $module , openhref => '', closehref => '') %>

zentyal-core-2.3.21+quantal1/src/templates/input/0000775000000000000000000000000012017102272016543 5ustar zentyal-core-2.3.21+quantal1/src/templates/input/submit.mas0000664000000000000000000000024212017102272020546 0ustar <%init> if (! $ARGS{name}) { $m->abort('A name parameter is required'); } /> zentyal-core-2.3.21+quantal1/src/templates/input/select.mas0000664000000000000000000000305312017102272020525 0ustar <%doc> Component for HTML select input Parameters: name - name of the input value - value of the input options - this reference to a list contains all the available select's options. Each option is represented a hash reference with this keys: value - the value of the option printableValue - the string who represent the value in the GUI. Defaults to value disabled - boolean indicating if the option is enabled or not You can pass also other HTML attributes as parameters. <%init> my $name = delete $ARGS{name}; $name or die 'select control needs at least the name parameter'; my $value = exists $ARGS{value} ? delete $ARGS{value} : ''; my @options = exists $ARGS{options} ? @{ delete $ARGS{options} } : (); # disable the control if it isn't any option if (@options == 0) { $ARGS{disabled} = 'disabled'; } % return; <%def .option> <%args> $value $selected => 0 $printableValue => undef $disabled => 0 <%init> if (!defined $printableValue) { $printableValue = $value; } my $selectedAttr = $selected ? 'selected="selected"' : ''; my $disabledAttr = $disabled ? 'disabled="disabled"' : ''; zentyal-core-2.3.21+quantal1/src/templates/input/file.mas0000664000000000000000000000022212017102272020160 0ustar <%init> if (! $ARGS{name}) { $m->abort('A name parameter is required'); } />zentyal-core-2.3.21+quantal1/src/templates/input/multiSelect.mas0000664000000000000000000000127112017102272021540 0ustar <%init> use Perl6::Junction qw(any); my $name = delete $ARGS{name}; $name or die 'select control needs at least the name parameter'; my $value = exists $ARGS{value} ? delete $ARGS{value} : ''; my @options = exists $ARGS{options} ? @{ delete $ARGS{options} } : (); % if (@options == 0) { <% __("No options") %> % } else {
% foreach my $option (@options) {
% }
% } zentyal-core-2.3.21+quantal1/src/templates/input/action.mas0000664000000000000000000000102212017102272020515 0ustar <%args> $action $id => undef $type => 'submit' <%init> my %attributes = ( type => $type, name => $action->name($id), value => $action->printableValue($id), title => $action->printableValue($id), onclick => $action->onclick($id), ); if (not $action->enabled($id)) { $attributes{disabled} = 'disabled'; } if ($type eq 'image') { $attributes{src} = $action->image($id); $attributes{alt} = $action->printableValue($id); } /> zentyal-core-2.3.21+quantal1/src/templates/input/checkbox.mas0000664000000000000000000000054112017102272021033 0ustar <%init> my $name = delete $ARGS{name}; defined $name or $m->abort('The checkbox input needs a name parameter'); my $value = delete $ARGS{value}; my @checked = $value ? (checked => 'checked') : (); /> zentyal-core-2.3.21+quantal1/src/templates/input/password.mas0000664000000000000000000000036712017102272021115 0ustar <%init> if (! $ARGS{name}) { $m->abort('A name parameter is required'); } unless ( defined($ARGS{value}) and $ARGS{value} ne '' ) { delete $ARGS{value}; } /> zentyal-core-2.3.21+quantal1/src/templates/input/hidden.mas0000664000000000000000000000024312017102272020477 0ustar <%init> if (! $ARGS{name}) { $m->abort('A name parameter is required'); } /> zentyal-core-2.3.21+quantal1/src/templates/input/text.mas0000664000000000000000000000031312017102272020226 0ustar <%init> if (! $ARGS{name}) { $m->abort('A name parameter is required'); } elsif (!exists $ARGS{value}) { $ARGS{value} = ''; } />zentyal-core-2.3.21+quantal1/src/templates/headTitle.mas0000664000000000000000000000161312017102272020012 0ustar <%args> $save => '' $logout => '' $finishClass => '' $remoteServicesURL => '' $image_title <%init> use EBox::Gettext; my $mboxTitle = __('Save changes?');
% if ( $remoteServicesURL ne '' ) { <% __('Zentyal Cloud') %> % } % if ( $logout ne '' ) { <% $logout %> % } % if ( $save ne '' ) { <% $save %> % }
zentyal-core-2.3.21+quantal1/src/templates/cgiErrorNoReport.html0000664000000000000000000000135012017102272021536 0ustar
{{ title }}
{{ brokenPackages }}
{{ error }}
zentyal-core-2.3.21+quantal1/src/templates/formTable.mas0000664000000000000000000000364712017102272020033 0ustar <%flags> inherit => 'table.mas' <%args> @rows => (), @additionalComponents => () <%attr> tableClass => 'formTable' <%init> use EBox::Gettext; <& SELF:table, rows => \@rows, additionalComponents => \@additionalComponents &> <%method rows> <%args> @rows % foreach my $row_r (@rows) { <& SELF:tr, @{ $row_r } &> % } <%method tr> <%init> my $input = delete $ARGS{input}; defined $input or $input = 'text'; my $component = delete $ARGS{component}; my $warning = delete $ARGS{warning}; my $note = delete $ARGS{note}; my $help = delete $ARGS{help}; # adjust default printable name if needed my $printableName = delete $ARGS{printableName}; if (!defined $printableName ) { $printableName = exists $ARGS{name} ? $ARGS{name} : ''; } if ($input eq 'submit') { $printableName = ''; } # adjust input component my $inputComponent; if (defined $component) { $inputComponent = $component; } else { $inputComponent = "/input/$input.mas"; } % if (not $inputComponent =~ m/hidden\.mas$/ ) { <& .div, type => 'warning', content => $warning &> <& .div, type => 'note', content => $note &> <& .labelTd, printableName => $printableName, optional => $ARGS{optional} &> <& $inputComponent, %ARGS &> <& .div, type => 'field_help', content => $help &> % } % else { <& $inputComponent, %ARGS &> % } <%def .labelTd> <%args> $printableName $optional => 0 <%init> if ($printableName ne '') { $printableName .= ':'; } <% "$printableName" %> % if ( $optional ) { % my $optionalText = __('Optional');
<% $optionalText %>
% } <%def .div> <%args> $content $type % if (defined $content) {
<% $content %>
% } zentyal-core-2.3.21+quantal1/src/templates/configureView.mas0000664000000000000000000000311312017102272020720 0ustar <%args> $module @files @actions <%init> use EBox::Gettext; use Data::Dumper;
<% __('Enabling this module will cause Zentyal to perform the actions and file modifications listed below. You must explicitly accept these changes to enable the module.') %>
% if (@actions) {

<% __('Actions to perform by Zentyal') %>

% } % for my $action (@actions) {
<% __('Action') %>: <% $action->{'action'} %>
<% __('Reason') %>: <% $action->{'reason'} %>
% } % if (@files) {

<% __('Files to modify by Zentyal') %>

% } % for my $file (@files) {
<% __('File') %>: <% $file->{'file'} %>
<% __('Reason') %>: <% $file->{'reason'} %>
% }
zentyal-core-2.3.21+quantal1/src/templates/backupTabs.mas0000664000000000000000000000203412017102272020164 0ustar <%init> use EBox::Gettext; use EBox::Global; my %args = @_; my $selected = delete $args{selected}; $selected or $selected = 'local'; my %tabs = ( local => { component => 'backup.mas', title => __('Local'), url => '/SysInfo/Backup?selected=local', }, remote => { component => '/remoteservices/Backup/index.mas', title => __('Remote'), url => '/RemoteServices/Backup/Index?selected=remote', }, ); my @tabsOrder; if ( EBox::Global->modExists('remoteservices') ) { @tabsOrder = qw(local remote); } else { @tabsOrder = qw(local); } if (not exists $tabs{$selected}) { $m->abort("bad selected value $selected"); }
% foreach my $tab (@tabsOrder) { % if($selected eq $tab) { <% $tabs{$tab}->{title} %> % } else { <% $tabs{$tab}->{title} %> % } % }
<& $tabs{$selected}->{component}, %args &> zentyal-core-2.3.21+quantal1/src/templates/dataTable.mas0000664000000000000000000000275612017102272020001 0ustar <%doc> Create a table data which conforms with the following specifications: tabla data rows with simple contents and a cell with action icons. The actions cells must be hash references with the following fields url name icon Finally, some arbitary component can be added using the additionalComponents parameters <%flags> inherit => 'table.mas' <%args> @columnTitles => () @rows => () @additionalComponents => () <%attr> tableClass => 'dataTable' thClass => 'tleft' trClass => 'border' <%init> use EBox::Gettext; <& SELF:table, columnTitles => \@columnTitles, rows => \@rows, additionalComponents => \@additionalComponents &> <%method theadContent> <%args> @columnTitles => () <%init> my $actionColumnTitle = pop @columnTitles; <& PARENT:theadContent, columnTitles => \@columnTitles &> <% $actionColumnTitle %> <%method trContent> <%args> @row <%init> my $actionsCellContent = pop @row; <& PARENT:trContent, row => \@row &> <& SELF:tdAction, actions =>$actionsCellContent &> <%method tdAction> <%args> @actions % while ( my $action_r = shift @actions) { <& link.mas, href => $action_r->{url}, text => $action_r->{text}, image => $action_r->{icon}, onclick => $action_r->{onclick} &> % if (@actions) { - % } % } zentyal-core-2.3.21+quantal1/src/templates/legendTable.mas0000664000000000000000000000127412017102272020320 0ustar <%args> @elements => (), # An array of elements containing hashes where each element has "imgFile" and "imgName" properties for legend $nColumns => 2 # Number of columns in each row legend table <%init> use EBox::Gettext; % if ($#elements + 1 > 0) { % my $count = 0; % foreach my $element (@elements) { % if ( $count % $nColumns == 0 ) { % } % $count++; % if ( $count % $nColumns == 0 ) { % } % } % }
<% $element->{'imgName'} | h %> <% $element->{'imgName'} | h %>
zentyal-core-2.3.21+quantal1/src/templates/logs/0000775000000000000000000000000012017102272016350 5ustar zentyal-core-2.3.21+quantal1/src/templates/logs/dbtable.mas0000664000000000000000000000617212017102272020455 0ustar <%args> $params $filters $data $page $tpages $selected @fromdate @todate <%init> use EBox::Gettext;
% my %hash = %{$params->{'titles'}}; % my @khash = @{$params->{'order'}}; % my $takey = scalar @khash; % foreach my $key (@khash) { % } % foreach my $row (@{$data}) { % foreach my $it (@khash) { % if ($it eq 'event') { % } else { <%doc> FIXME: This is a quick solution to show URL's and tooltips. The nice thing would be implementing filters which can be set within the table definition % my $content = $row->{$it}; % $content =~ s/ +$//; % my $tooltip = $content; % if (length ($content) > 40 ) { % $content = substr ($content, 0, 40) . '...'; % } % if ($content =~ /^http:/) { % } else { % } % } % } % }
<% $hash{$key} %>
<% $params->{'events'}->{$row->{$it}} %><% $content | h %>
<% $content | h %>
% foreach my $filter (keys %{$filters}) { % }
% if ($page != 0) { % } % if ($tpages <= 0) { <% __('Page') %> <% $page+1 %> % } else { <% __('Page') %> <% $page+1 %> <% __('of') %> <%$tpages+1%> % } % if ($page != $tpages) { % }
zentyal-core-2.3.21+quantal1/src/templates/logs/index.mas0000664000000000000000000001577412017102272020177 0ustar <%args> %logdomains $tableinfo => undef $filters => undef $selected $page => undef $tpages => undef $data => undef @fromdate => () @todate => () $refresh => 0 <%init> use EBox::Gettext; use Data::Dumper;

<% __('Log Domain') %>

<% __('Last hour logs will be showed when selecting a component') %>
<% __('Select available full reports') %>:
% if ($selected ne 'none') { % my @time = localtime(time); % my $currentyear = $time[5]+1900;

<% __('Custom query') %>

% foreach my $filter (@{$tableinfo->{'filter'}}) { % if (exists $filters->{$filter}) { % } else { % } % }
<% __('From date:') %> / / - :
<% __('Refresh logs') %>: onclick="updateRefresh()" />
<% __('To date:') %> / / - :
<% $tableinfo->{'titles'}->{$filter} %>:
<% __('Event') %>:
% if (@{$data}) { <& logs/dbtable.mas , params => $tableinfo , data => $data , page => $page , selected => $selected, fromdate => \@fromdate, todate => \@todate, tpages => $tpages,filters => $filters &> <%perl> } else { my $logs = EBox::Global->modInstance('logs'); my $totalRec = $logs->totalRecords($tableinfo->{tablename}); if ( $totalRec > 0) {
<% __('No logs matching current criterion') %>
% } else {
<% __('There are no logs for this domain') %>
% } % } % } zentyal-core-2.3.21+quantal1/src/templates/presentationTable.mas0000664000000000000000000000063312017102272021573 0ustar <%doc> Create a table which is only used for presentation purposes <%flags> inherit => 'table.mas' <%args> @columnTitles => () @rows => () @additionalComponents => () <%attr> tableClass => 'dataTable' thClass => 'tleft' trClass => 'border' <& SELF:table, columnTitles => \@columnTitles, rows => \@rows, additionalComponents => \@additionalComponents &> zentyal-core-2.3.21+quantal1/src/templates/htmlAttributes.mas0000664000000000000000000000102112017102272021113 0ustar <%doc> Formats the named parameters list as HTML attributes. The quoting character used is ["] so don't use it in the value. <%init> my $attrString = ''; while (my ($key, $value) = each %ARGS) { if ($key eq 'disabled') { $attrString .= 'disabled ' if ($value); } else { defined $value or die "Attribute $key has not value.\n If $key is a boolean attribute with true value use the form '$key=$key'"; $attrString .= qq{$key="$value" }; } } $m->print($attrString); return zentyal-core-2.3.21+quantal1/src/templates/graph.mas0000664000000000000000000000654612017102272017222 0ustar <%doc> Plots the given @series in a graph within a
element which will have the given $id. The data should be in the following format: [ { 'data' => [ [x1,y1],[x2,y2],[x3,y3],[x4,y4],[x5,y5] ], 'label' => 'foo' }, { 'data => [ [x1,z1],[x2,z2],[x3,z3],[x4,z4],[x5,z5] ], 'label' => 'bar' } ] The template will generate a graph with one line for each different set of data. Repainting variable indicates the template to print the whole
elements and scripts or only update the graph if repainting is done <%args> $id $type $timetype => 'time' @series $repainting => 0 <%init> my $formatter; my $timeformatter; if($type eq 'int') { $formatter = undef; } elsif($type eq 'byte') { $formatter = 'getBytes'; } elsif($type eq 'degree') { $formatter = 'getDegrees'; } elsif($type eq 'millisecond' ) { $formatter = 'getTimeDiff'; } elsif($type eq 'bps' ) { $formatter = 'getBytesPerSec'; } if($timetype eq 'time') { $timeformatter = 'getTime'; } else { $timeformatter = 'getDate'; } my $tiny = 0.01; % unless ($repainting) {
% } zentyal-core-2.3.21+quantal1/src/templates/error.mas0000664000000000000000000000010012017102272017226 0ustar <%args> $error
<% $error %>
zentyal-core-2.3.21+quantal1/src/templates/finish.mas0000664000000000000000000001056112017102272017371 0ustar <%args> @actions => () $unsaved => 'no' @disabledModules => () $askPermission => undef <%init> use EBox::Gettext; % if ($askPermission) { % } % if($unsaved eq 'yes') {
% if (@disabledModules) { <% __x('The following modules have unsaved changes but are disabled. If you need to enable them go to {openref}Module Status{closeref}.', openref => '', closeref => '') %>
    % for my $module (@disabledModules) {
  • <% $module %>
  • % }
% } else { <% __('There are unsaved changes in one or more modules, you can save or discard those changes.')%>
% }
<%__('If you made changes to the network interfaces or the administration port, you may need to manually rewrite the url in order to access this administration interface again.') %>
% if (@actions) {
<% __('The following changes are pending. You can save or discard them.') %>
% my $oddRow = 1; % foreach my $action (@actions) { % my $class = $oddRow ? 'border odd' : 'border even'; % $oddRow = not $oddRow; % }
<% __('Date') %> <% __('Module') %> <% __('Section') %> <% __('Description') %>
<% $action->{'timestamp'} %> <% $action->{'modtitle'} %> <% $action->{'modeltitle'} %> <% $action->{'message'} %>
% } <%perl> my $mboxSaveTitle = __('Saving changes'); my $mboxDiscardTitle = __('Discarding changes');

% }else{
<% __('There are no unsaved changes.') %>
% } zentyal-core-2.3.21+quantal1/src/templates/moduleStatusTable.mas0000664000000000000000000000465012017102272021554 0ustar <%args> @modules $hasChanged => 'notset' <%init> use EBox::Gettext;
% for my $mod (@modules) { % my $name = $mod->{'name'}; % my $printableName = $mod->{'printableName'}; % my $depends = $mod->{'depends'}; % my $status = $mod->{'status'}; % my $configured = $mod->{'configured'}; % }
<% __('Module') %> <% __('Depends') %> <% __('Status') %>
<% $printableName %> <% join(', ', @{$mod->{'printableDepends'}}) %> % if (not $depends) { <& /input/checkbox.mas, 'name' => $name, 'id' => $name, 'value' => undef, 'disabled' => 1 &> % } elsif (not $configured) { % my $title = __x("Configure module: {module}", module => $name ); <& /input/checkbox.mas, 'name' => $name, 'id' => $name, 'value' => $status, 'onChange' => qq{configureModule('$name', '$title')} &> % } else { <& /input/checkbox.mas, 'name' => $name, 'id' => $name, 'value' => $status, 'onChange' => qq{sendStatus(); setLoading('${name}_status'); } &> % }
zentyal-core-2.3.21+quantal1/src/templates/configurationFiles.mas0000664000000000000000000000263712017102272021750 0ustar <%args> @files <%init> use EBox::Gettext;

<% __('Zentyal detected that some files which need to be overwritten have been modified by you.') %>

<% __('Do you wish to overwrite these files?') %>

% for my $file (@files) {
<% __('Module') %>: <% $file->{'module'} %>
<% __('File') %>: <% $file->{'file'} %>
<% __('Comment') %>: <% $file->{'reason'} %>
% } zentyal-core-2.3.21+quantal1/src/templates/header.mas0000664000000000000000000000163112017102272017337 0ustar <%args> $title $favicon => '/favicon.ico' <% $title %> zentyal-core-2.3.21+quantal1/src/templates/dashboard/0000775000000000000000000000000012017102272017333 5ustar zentyal-core-2.3.21+quantal1/src/templates/dashboard/section.mas0000664000000000000000000000046512017102272021506 0ustar <%args> $namespace $section %toggled => () <%init> my $sectname = ($namespace . '_' . $section->{name} . '_section');
<& /dashboard/sectioncontent.mas, namespace => $namespace, section => $section, toggled => \%toggled &>
zentyal-core-2.3.21+quantal1/src/templates/dashboard/status.mas0000664000000000000000000000321612017102272021362 0ustar <%args> $namespace $item $i <%init> use EBox::Gettext; my $status_str; my $status_tip_str; my $status_class; my $restart = __('Restart'); my $name = 'restart'; if ( $item->{statusStr} ) { $status_str = $item->{statusStr}; } else { if ($item->{enabled} and $item->{running}) { $status_str = __('Running'); $status_tip_str = __('The service is enabled and running'); $status_class = 'summaryRunning'; } elsif ($item->{enabled} and not $item->{running}) { $status_str = __('Stopped'); $status_tip_str = __('The service is enabled, but not running'); $status_class = 'summaryStopped'; $restart = __('Start'); $name = 'start'; } elsif ((not $item->{enabled}) and $item->{running}) { $status_str = __('Running unmanaged'); $status_tip_str = __('The service is running, but not enabled in Zentyal'); $status_class = 'summaryDisabled'; } else { $status_str = __('Disabled'); $status_tip_str = __('The service is not enabled in Zentyal'); $status_class = 'summaryDisabled'; } } <% $item->{printableName} %> ' class='summary_value'> <% $status_str %> % if ($item->{enabled} and not $item->{nobutton}) {
% } zentyal-core-2.3.21+quantal1/src/templates/dashboard/widget.mas0000664000000000000000000000037212017102272021322 0ustar <%args> $widget %toggled => () <%init> my $fqmn = $widget->{module} . ':' . $widget->{name};
<& /dashboard/widgetcontent.mas, widget => $widget, toggled => \%toggled &>
zentyal-core-2.3.21+quantal1/src/templates/dashboard/html.mas0000664000000000000000000000017412017102272021003 0ustar <%args> $namespace $item $i '> <% $item->{html} %> zentyal-core-2.3.21+quantal1/src/templates/dashboard/index.mas0000664000000000000000000003445212017102272021154 0ustar <%args> @dashboard1 @dashboard2 %toggled @brokenPackages $softwareInstalled => 0 <%init> use EBox::Gettext; <% __('Configure widgets') %> % if (@brokenPackages) {
<% __x('The following packages are not properly installed: {packages}.', packages => join (' ', @brokenPackages)) %> % if ($softwareInstalled) { <% __x('Go to {ohref}Software Management{chref} for more information.', ohref => '', chref => '') %> % } else { <% __x('To solve this situation, please try to execute the following command in the console: {cmd}', cmd => 'sudo dpkg --configure -a') %> % }
% }

% foreach my $widget (@dashboard1) { <& dashboard/widget.mas, widget => $widget, toggled => \%toggled &> % }

% foreach my $widget (@dashboard2) { <& dashboard/widget.mas, widget => $widget, toggled => \%toggled &> % }
zentyal-core-2.3.21+quantal1/src/templates/dashboard/graph.mas0000664000000000000000000000152412017102272021140 0ustar <%args> $namespace $item $i $parent % my $elem = ($namespace . '_' . $i); % if ($parent eq 'section') { <% $item->{title} %>
% } else { <% $item->{title} %>
% } zentyal-core-2.3.21+quantal1/src/templates/dashboard/value.mas0000664000000000000000000000051712017102272021154 0ustar <%args> $namespace $item $i <% $item->{key} %> ' % if ($item->{value_type} ne 'info') { class='summary_value summary_<% $item->{value_type} %>' % } else { class='summary_value' % } > <% $item->{value} %> zentyal-core-2.3.21+quantal1/src/templates/dashboard/list.mas0000664000000000000000000000165012017102272021012 0ustar <%args> $namespace $item $i <%init> use EBox::Gettext; % if(defined($item->{title})) {
<% $item->{title} %>
% } % my $list_display = 'block'; % my $none_display = 'block'; % if(@{$item->{ids}}) { % $none_display = 'none'; % } else { % $list_display = 'none'; % } ' style='display: <% $list_display %>;' class='dashboardTable'> % foreach my $colTitle (@{$item->{colTitles}}) { % } % foreach my $id (@{$item->{ids}}) { > % foreach my $col (@{$item->{rows}->{$id}}) { % } % }
<% $colTitle %>
<% $col %>
' style='display: <% $none_display %>;'> % if($item->{none_text}) { <% $item->{none_text} %> % } else { <% __('No entries in this list') %> % }
zentyal-core-2.3.21+quantal1/src/templates/dashboard/widgetcontent.mas0000664000000000000000000000175112017102272022717 0ustar <%args> $widget %toggled => () <%init> my $fqmn = $widget->{module} . ':' . $widget->{name}; my $content = ($fqmn . '_content');
% my $togglerClass = 'minBox'; % if (exists($toggled{$content}) && $toggled{$content}) { % $togglerClass = 'maxBox'; % }
<% $widget->{title} %>
zentyal-core-2.3.21+quantal1/src/templates/dashboard/configurewidgets.mas0000664000000000000000000001372712017102272023417 0ustar <%args> @modules <%init> use EBox::Gettext; use JSON;
% foreach my $module (@modules) { % my $modname = $module->{'name'}; % my $title = $module->{'title'}; % }
<% __('Browse the available widgets in the top menu and drag&drop them to the dashboard.') %>
zentyal-core-2.3.21+quantal1/src/templates/dashboard/graphrow.mas0000664000000000000000000000052312017102272021666 0ustar <%args> $namespace $item $i % my $elem = ($namespace . '_' . $i); % my $j = 0; % foreach my $graph (@{$item->graphs()}) { <& $graph->HTMLViewer(), namespace => $elem, item => $graph, i => $j, parent => 'graphrow' &> % $j++; % }
zentyal-core-2.3.21+quantal1/src/templates/dashboard/sectioncontent.mas0000664000000000000000000000161612017102272023100 0ustar <%args> $namespace $section %toggled => () <%init> my $ns = ($namespace . '_' . $section->{name}); my $sectname = ($ns . '_section'); % if ($section->{title}) {
% my $togglerClass = 'minBox'; % if (exists($toggled{$sectname}) && $toggled{$sectname}) { % $togglerClass = 'maxBox'; % } <% $section->{title} %>
% } zentyal-core-2.3.21+quantal1/src/templates/links-widget.mas0000664000000000000000000000553012017102272020512 0ustar <%init> use EBox::Gettext; my $lang = 'en'; if (substr ($ENV{LANG}, 0, 2) eq 'es') { $lang = 'es'; } my $BOOK_URL = "https://store.zentyal.com/book-admin-$lang.html?utm_source=zentyal&utm_medium=dashboard&utm_campaign=book_$lang"; zentyal-core-2.3.21+quantal1/src/templates/login/0000775000000000000000000000000012017102272016514 5ustar zentyal-core-2.3.21+quantal1/src/templates/login/index.mas0000664000000000000000000000671512017102272020336 0ustar <%args> $reason => '' $destination $image_login_logo $alt_logo $created_by_footer $title => '' $favicon => '/favicon.ico' <%init> use EBox::Gettext; <% __('Zentyal') %>
% if ($title) {

<% $title %>

% } % if (length($reason)) {
<% $reason %>
% }
<% __('Username') %>:
<% __('Password') %>:
zentyal-core-2.3.21+quantal1/src/templates/menu.mas0000664000000000000000000000511512017102272017054 0ustar <%args> @items $current => 'none' zentyal-core-2.3.21+quantal1/src/templates/logout/0000775000000000000000000000000012017102272016715 5ustar zentyal-core-2.3.21+quantal1/src/templates/logout/index.mas0000664000000000000000000000062712017102272020533 0ustar <%args> $unsaved => 'no' <%init> use EBox::Gettext; % if($unsaved eq 'yes'){
<% __('There are unsaved changes, if you log out now they will be lost.') %>
% }else{
<% __('There are no unsaved changes in this session.') %>
% } <% __('Exit') %> zentyal-core-2.3.21+quantal1/src/templates/msg.mas0000664000000000000000000000066712017102272016705 0ustar <%doc> This template is intended to show a simple message on HTML format as informative note or a warning Parameters: msg - String the message itself class - String the message kind (note, warning, ad, adwarning) are the possibilites. Optional. Default value: note <%args> $msg $class => '' <%init> unless ($class) { $class = 'note'; }
<% $msg %>
zentyal-core-2.3.21+quantal1/src/templates/footer.mas0000664000000000000000000000054112017102272017404 0ustar <%args> $copyright_footer zentyal-core-2.3.21+quantal1/src/templates/cgiError.html0000664000000000000000000000660012017102272020050 0ustar
{{ title }}
{{ error }}
zentyal-core-2.3.21+quantal1/src/scripts/0000775000000000000000000000000012017102272015075 5ustar zentyal-core-2.3.21+quantal1/src/scripts/keyring.gpg0000664000000000000000000000162212017102272017245 0ustar NJ\8_|]Ѫ N.)Ffv|T:)|UfX% `QW@dHAp:֢B_!ǩ{Tډ"ǂ1Y.7N/GZrۨҲUr 6TET MѡgO /~7  ,:~>Xý1e(Iƈ-l@>"m]O%ioCBF;Cjkr}φz[nU]dSǎ=s^ml 7B Ԡ9ĖiT最e9J Bkt3U^O)l]BkڲK^tl 6+IZentyal h(NJ 8d    ꗫsARO|+4Oe3?7$# IlA  NJ.'bGAV ӈ~rlFgGfn.4:*;"~Ilst-ƌmh*CS P{>?KGcDlX iO%+Hć Sokߊ[`wNKP_ymq}Kl|;& \SRB*vCEcZa".0nV*Y/8[ъb3ONJ  8d ꗫsA[Qh2. kvt0? ,a߰zentyal-core-2.3.21+quantal1/src/scripts/manage-logs0000775000000000000000000000175012017102272017220 0ustar #!/usr/bin/perl use strict; use warnings; use EBox; use EBox::Global; use EBox::Logs::Consolidate; use EBox::Util::Lock; use Error qw(:try); use constant LOCK_NAME => 'manageEBoxLogs'; EBox::init(); try { EBox::Util::Lock::lock(LOCK_NAME); } catch EBox::Exceptions::Lock with { print "Already a manageEBoxLogs process running. Exiting\n"; exit 0; }; try { my $logs = EBox::Global->modInstance('logs'); defined $logs or die 'Cannot instanstiate eBox logs module'; unless (EBox::Config::boolean('disable_consolidation')) { # we consolidate before the purge to not lose any data EBox::Logs::Consolidate->consolidate('all'); # do report consolidation my @mods = @{ EBox::Global->modInstancesOfType('EBox::LogObserver') }; for my $mod (@mods) { $mod->consolidateReportFromLogs(); } } $logs->archiveBackupSlices(); $logs->purge(); } finally { EBox::Util::Lock::unlock(LOCK_NAME); }; 1; zentyal-core-2.3.21+quantal1/src/scripts/zentyal.init.d0000775000000000000000000000312612017102272017677 0ustar #!/usr/bin/perl ### BEGIN INIT INFO # Provides: zentyal # Required-Start: $network # Required-Stop: $network # Default-Start: 2 3 4 5 # Default-Stop: 0 1 6 # Short-Description: Zentyal (Small Business Server) ### END INIT INFO use strict; use warnings; use EBox; use EBox::Util::Init; EBox::init(); $SIG{PIPE} = 'IGNORE'; sub usage { print "Usage: $0 start|stop|restart\n"; print " $0 start|stop|status|enabled|restart\n"; exit 1; } sub main { if (@ARGV == 1) { if ($ARGV[0] eq 'start') { EBox::Util::Init::cleanTmpOnBoot(); EBox::Util::Init::start(); } elsif ($ARGV[0] eq 'restart') { EBox::Util::Init::stop(); EBox::Util::Init::start(); } elsif ($ARGV[0] eq 'force-reload') { EBox::Util::Init::stop(); EBox::Util::Init::start(); } elsif ($ARGV[0] eq 'stop') { EBox::Util::Init::stop(); } else { usage(); } } elsif (@ARGV == 2) { # action upon one module mode my ($modName, $action) = @ARGV; if (($action eq 'restart') or ($action eq 'start')) { EBox::Util::Init::moduleRestart($modName); } elsif ($action eq 'stop') { EBox::Util::Init::moduleStop($modName); } elsif ($action eq 'status' or $action eq 'enabled') { # FIXME: Separate enabled and status actions EBox::Util::Init::status($modName); } else { usage(); } } else { usage(); } } main(); 1; zentyal-core-2.3.21+quantal1/src/scripts/set-locale0000775000000000000000000000051312017102272017052 0ustar #!/usr/bin/perl use strict; use warnings; use EBox; use EBox::Gettext; my ($locale) = @ARGV; $locale or die 'Not locale given in the command line'; EBox::init(); my $supportedLocales = langs(); if (not exists $supportedLocales->{$locale}) { die "Locale $locale unsupported by Zentyal"; } EBox::setLocale($locale); 1; zentyal-core-2.3.21+quantal1/src/scripts/shell0000775000000000000000000000626712017102272016145 0ustar #!/usr/bin/perl # Copyright (C) 2011-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Minimalistic perlconsole clone with Zentyal environment preloaded use warnings; use strict; use Term::ReadLine; use Lexical::Persistence; use Data::Dumper; use Error qw(:try); use EBox; use EBox::Global; use EBox::Model::Manager; EBox::init(); my $PROMPT = 'zentyal> '; my $term = new Term::ReadLine $PROMPT; my $lex = new Lexical::Persistence; my $globalInit = code_with_lex_environ('my $global = EBox::Global->getInstance()'); &$globalInit(); my $internal; my @commands = parse_commandline(); while (defined (my $line = get_command())) { last if ($line eq 'exit'); $internal = 1; $line = parse_internal_commands($line); next unless $line; my $code = code_with_lex_environ($line); next unless defined $code; eval_and_print($code); } sub parse_commandline { my @cmds = map { split (';', $_) } @ARGV; push (@cmds, 'exit') if @cmds; return @cmds; } sub get_command { if (@commands) { return shift (@commands); } else { return $term->readline($PROMPT); } } sub eval_and_print { my ($code) = @_; my $ret = eval { &$code(); }; print $@ if $@; if ((ref ($ret) eq 'ARRAY') or (ref ($ret) eq 'HASH')) { print Dumper($ret); } else { print "$ret\n" if ($ret and not $internal); } } sub parse_internal_commands { my ($cmd) = @_; my ($mod) = $cmd =~ /^instance (\w+);*/; if ($mod) { unless (EBox::Global->modExists($mod)) { print "Error: Module '$mod' not found\n"; return undef; } return "my \$$mod = \$global->modInstance('$mod'); print '\$$mod\n';"; } my ($model) = $cmd =~ /^model (\w+);*/; if ($model) { my $var = lc($model); return "my \$$var; try { \$$var = EBox::Model::Manager->instance()->model('$model'); print '\$$var\n'; } otherwise { print \"Error: Model '$model' not found\n\" };"; } my ($call) = $cmd =~ /^call (.+);*/; if ($call) { my @parts = split ('::', $call); if (@parts) { @parts = map { split ('->', $_) } @parts; pop (@parts); $call = 'use ' . join ('::', @parts) . "; $call;"; } return $call; } $internal = 0; return $cmd; } sub code_with_lex_environ { my ($code) = @_; my $vars = ''; foreach my $var (keys %{$lex->get_context('_')}) { $vars .= "my $var;"; } my $sub = "sub {$vars $code}"; my $ref = eval $sub; if ($@) { print $@; return undef; } return $lex->wrap($ref); } zentyal-core-2.3.21+quantal1/src/scripts/unconfigure-module0000775000000000000000000000072412017102272020635 0ustar #!/usr/bin/perl use EBox; use EBox::Global; EBox::init(); my $module = $ARGV[0]; unless (defined($module)) { print "Usage: $0 module\n"; exit 1; } my $global = EBox::Global->getInstance(); unless ($global->modExists($module)) { print "Module $module does not exist\n"; exit 1; } my $mod = $global->modInstance($module); unless ($mod->isa('EBox::Module::Service')) { print "Module $module is not a service\n"; exit 1; } $mod->setConfigured(undef); zentyal-core-2.3.21+quantal1/src/scripts/configuration-report0000775000000000000000000000045312017102272021205 0ustar #!/usr/bin/perl -w use strict; use warnings; use EBox; use EBox::Backup; EBox::init(); my $dst = '/tmp/zentyal-configuration-report.tar'; my $src = EBox::Backup->makeBugReport(); system "mv -f $src $dst"; if ($? != 0) { die "$!"; } print "Configuration report file generated: $dst\n"; 1; zentyal-core-2.3.21+quantal1/src/scripts/exec-mason-template0000775000000000000000000000347212017102272020701 0ustar #!/usr/bin/perl use strict; use warnings; use Getopt::Long; use EBox::Test::Mason; use File::Basename; use Cwd qw(abs_path); my $verbose = 0; my @comp_root = (); my $mason_cli_params = undef; my @options = ( 'help' => \&usage, 'verbose' => \$verbose, 'comp-root=s' => \@comp_root, 'params=s' => \$mason_cli_params, ); GetOptions(@options); my ($template) = @ARGV; defined $template or die "Not mason file provided"; _print_comp_root($template, \@comp_root) if $verbose; my $mason_params = _eval_params($mason_cli_params); my $output_r = EBox::Test::Mason::executeTemplate( template => $template, templateParams => $mason_params, compRoot => \@comp_root, ); print "\nComponent output:\n\n" if $verbose; print $$output_r; print "\n"; sub _eval_params { my ($mason_cli_params) = @_; if (! $mason_cli_params) { return []; } my @params; eval "\@params = ( $mason_cli_params ) "; if ($@) { die "Wrong template params. $@"; } return \@params; } sub _print_comp_root { my ($template, $root_paths_r) = @_; my @root_paths = @{ $root_paths_r }; my $main_root = abs_path ($template); $main_root = dirname $main_root; print "Component root: $main_root @root_paths\n" if $verbose } sub usage { print "usage: $0 [OPTION]... TEMPLATE_FILE\n"; print "\nExecute mason template\n\n"; print "\t--verbose verbose output. Prints the template output to STDOUT\n"; print "\t--comp-root component root. Set a component root for mason compiler. May be established multiple times\n"; print "\t--params mason parameters. Must be a string of perl code. The string will be evaluated to populate the parameters list\n"; print "\n\t--help show this usage display\n"; print "\n"; exit 0; } 1; zentyal-core-2.3.21+quantal1/src/scripts/sudoers-friendly0000775000000000000000000000377312017102272020333 0ustar #!/usr/bin/perl # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA use strict; use warnings; use EBox; use EBox::Global; use EBox::Config; use HTML::Mason::Interp; use constant SUDOERS_FILE => '/etc/sudoers.d/ebox'; # Write first sudoers file before EBox::init # to enable EBox::Sudo my $user = EBox::Config::user(); $user or die 'eBox user not set'; write_sudoers(( user => $user )); # Write full sudoers file: EBox::init(); my @extraUsers; my $remoteServices = EBox::Global->modInstance('remoteservices'); if (defined $remoteServices) { if ($remoteServices->can('extraSudoerUsers')) { push @extraUsers, $remoteServices->extraSudoerUsers(); } } EBox::Module::Base::writeConfFileNoCheck(SUDOERS_FILE . '.tmp', 'core/sudo.mas', [ user => $user, extraUsers => \@extraUsers ], { mode => '0440', uid => 0, gid => 0 }); EBox::Sudo::root('mv ' . SUDOERS_FILE . '.tmp ' . SUDOERS_FILE); sub write_sudoers { my (@params) = @_; my $output; my $interp = HTML::Mason::Interp->new(out_method => \$output); my $comp = $interp->make_component(comp_file => (EBox::Config::stubs . 'core/sudo.mas')); $interp->exec($comp, @params); my $tmpFile = SUDOERS_FILE . '.tmp'; open (my $fh, '>', $tmpFile) or die ($!); print $fh $output; close ($fh); rename $tmpFile, SUDOERS_FILE; chmod 0440, SUDOERS_FILE; } zentyal-core-2.3.21+quantal1/src/scripts/unblock-exec0000775000000000000000000000041712017102272017404 0ustar #!/usr/bin/perl use POSIX qw(:signal_h); use warnings; use strict; my $exec = shift @ARGV; die "Usage: executable" unless (defined($exec) and -e $exec); my $sigset = POSIX::SigSet->new(); $sigset->fillset(); sigprocmask(SIG_UNBLOCK, $sigset); exec ($exec, @ARGV); zentyal-core-2.3.21+quantal1/src/scripts/purge-logs0000775000000000000000000000110612017102272017105 0ustar #!/usr/bin/perl use strict; use warnings; use EBox; use EBox::Global; EBox::init(); my $logs = EBox::Global->modInstance('logs'); defined $logs or die 'Cannot instanstiate eBox logs module'; my $mode = $ARGV[0]; defined $mode or $mode =''; if ($mode eq '--force') { $logs->forcePurge(0.00001); } elsif ($mode eq '--scheduled') { $logs->purge(); } else { help(); } 1; sub help { print "$0 [--force|--scheduled]\n"; print "\t--scheduled\tProceed with the scheduled log purge\n"; print "\t--force\tForce the purge of all logs\n"; exit 0; } zentyal-core-2.3.21+quantal1/src/scripts/mysql0000664000000000000000000000023212017102272016162 0ustar #!/bin/bash db_user=zentyal db_pass=`cat /var/lib/zentyal/conf/zentyal-mysql.passwd` db_name=zentyal mysql --user=$db_user --password=$db_pass $db_name zentyal-core-2.3.21+quantal1/src/scripts/purge-module0000775000000000000000000000315712017102272017436 0ustar #!/usr/bin/perl # Copyright (C) 2011-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # This script removes the module configuration stored in our backend # (normal and state) and purge SQL log tables if exists use EBox; use EBox::Config; use EBox::GlobalImpl; use EBox::Sudo; use EBox::Util::SQL; use Error qw(:try); my ($modname) = @ARGV; EBox::init(); cleanModuleConf($modname); EBox::Util::SQL::dropModuleTables($modname); my $purgeModule = "/var/lib/zentyal/purge-module/$modname"; if (EBox::Sudo::fileTest('-x', $purgeModule)) { try { EBox::Sudo::root($purgeModule); EBox::Sudo::root("rm -f '$purgeModule'"); } otherwise { EBox::warn("Purge of module $modname failed"); }; } exit 0; sub cleanModuleConf { my ($module) = @_; my $global = EBox::GlobalImpl->instance(); $global->delete_dir("global/conf/modules/$module"); $global->{redis}->delete_dir("$module/conf"); $global->{redis}->delete_dir("$module/ro"); $global->{redis}->unset("$module/state"); } zentyal-core-2.3.21+quantal1/src/scripts/global-action0000775000000000000000000000240112017102272017533 0ustar #!/usr/bin/perl use strict; use warnings; use EBox; use EBox::Config; use EBox::Global; use EBox::ProgressIndicator; use EBox::Exceptions::Internal; use Error qw(:try); EBox::init(); my %params = @ARGV; my $action = $params{'--action'}; $action or throw EBox::Exceptions::Internal('No action provided'); if (($action ne 'saveAllModules') and ($action ne 'revokeAllModules')) { throw EBox::Exceptions::Internal("Bad action: $action"); } my @callParams; my $progress; my $progressId = delete $params{'--progress-id'}; if ($progressId) { $progress = EBox::ProgressIndicator->retrieve($progressId); @callParams = (progress => $progress); } try { my $global = EBox::Global->getInstance(); my $audit = $global->modInstance('audit'); if ($action eq 'saveAllModules') { $global->saveAllModules(@callParams); $audit->commit(); } elsif ($action eq 'revokeAllModules') { $global->revokeAllModules(@callParams); $audit->discard(); } } otherwise { my $ex = shift @_; if ($progress) { my $errorTxt = $ex->text(); if (EBox::Config::boolean('debug')) { $errorTxt .= "\n" . $ex->stacktrace(); } $progress->setAsFinished(1, $errorTxt); } $ex->throw(); }; 1; zentyal-core-2.3.21+quantal1/src/scripts/loggerd0000775000000000000000000000014712017102272016450 0ustar #!/usr/bin/perl use strict; use warnings; use EBox::Loggerd; my $l = new EBox::Loggerd(); $l->run; zentyal-core-2.3.21+quantal1/src/scripts/redis-cli0000775000000000000000000000021112017102272016670 0ustar #!/bin/bash PASSWD=`cat /var/lib/zentyal/conf/redis.passwd` SOCKET=/var/lib/zentyal/redis.ebox.sock redis-cli -s $SOCKET -a $PASSWD $@ zentyal-core-2.3.21+quantal1/src/scripts/gather-reportinfo0000775000000000000000000000127112017102272020463 0ustar #!/usr/bin/perl use strict; use warnings; use EBox; use EBox::Global; use EBox::Config; use EBox::DBEngineFactory; EBox::init(); my $db = EBox::DBEngineFactory::DBEngine(); my @mods = @{EBox::Global->modInstances()}; my @time = localtime(time); my $year = $time[5] + 1900; my $month = $time[4] + 1; my $day = $time[3]; my $hour = $time[2]; my $min = $time[1]; my $sec = $time[0]; my $date = "$year-$month-$day $hour:$min:$sec"; for my $mod (@mods) { my $data = $mod->logReportInfo(); for my $i (@{$data}) { $i->{'values'}->{'timestamp'} = $date; $db->insert($i->{'table'}, $i->{'values'}); } } # Perform the buffered inserts done above $db->multiInsert(); 1; zentyal-core-2.3.21+quantal1/src/scripts/consolidate-reportinfo0000775000000000000000000000032512017102272021514 0ustar #!/usr/bin/perl use strict; use warnings; use EBox; use EBox::Global; use EBox::Config; EBox::init(); my @mods = @{EBox::Global->modInstances()}; for my $mod (@mods) { $mod->consolidateReportInfo(); } 1; zentyal-core-2.3.21+quantal1/src/scripts/initial-setup0000775000000000000000000000410312017102272017610 0ustar #!/usr/bin/perl # Copyright (C) 2011-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA use strict; use warnings; use EBox; use EBox::Config; use EBox::Global; use EBox::Sudo; use EBox::Util::SQL; use EBox::Util::Debconf; use Error qw(:try); my $restart = 1; if ($ARGV[0] eq '--no-restart') { $restart = 0; shift (@ARGV); } my ($modname, $version) = @ARGV; # We need to get this as root to avoid warning on console my $drInstall = EBox::Util::Debconf::value('zentyal-core/dr_install'); EBox::init(); # Do not create tables if we are on the first stage of the DR installer # they will be created later before launching restore-tool unless (defined ($drInstall) and ($drInstall eq 'true')) { EBox::Util::SQL::createModuleTables($modname); } # Copy purge-module script if exists my $purgeSrc = "/usr/share/zentyal-$modname/purge-module"; if (EBox::Sudo::fileTest('-f', $purgeSrc) ) { my $purgeDst = "/var/lib/zentyal/purge-module/$modname"; EBox::Sudo::root("cp '$purgeSrc' '$purgeDst'"); EBox::Sudo::root("chmod 0750 '$purgeDst'"); } my $module = EBox::Global->modInstance($modname); $module->redis->begin(); $module->initialSetup($version); # Run migrations only if upgrading if ($version) { $module->migrate($version); } try { if ($restart) { $module->save(); } else { $module->saveConfig(); } $module->redis->commit(); } otherwise { $module->redis->rollback(); EBox::warn("Restart for $modname failed"); }; exit 0; zentyal-core-2.3.21+quantal1/src/scripts/grep-redis0000775000000000000000000000236312017102272017070 0ustar #!/usr/bin/perl # Copyright (C) 2011-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA use warnings; use strict; use EBox; use EBox::Config::Redis; use EBox::Sudo; use Error qw(:try); use JSON::XS; my ($pattern, $dir) = @ARGV; unless ($pattern) { print STDERR "Usage: $0 pattern [path]\n"; exit 1; } EBox::init(); my $redis = EBox::Config::Redis->instance(); my @keys = $redis->_keys($dir ? "$dir/*" : '*'); foreach my $key (@keys) { my $value = $redis->get($key); if (ref $value) { $value = encode_json($value); } if (($key =~ /$pattern/) or ($value =~ /$pattern/)) { print "$key: $value\n"; } } zentyal-core-2.3.21+quantal1/src/scripts/make-backup0000775000000000000000000000611012017102272017201 0ustar #!/usr/bin/perl -w use strict; use warnings; use EBox; use EBox::Backup; use EBox::Global; use EBox::ProgressIndicator; use Error qw(:try); use Getopt::Long; EBox::init(); # process cli my $progressId = undef; my $remoteBackupName = undef; my $configBackup = 0; my $bugReport = 0; my $fallbackToRO = 0; my $description = undef; my $destination = undef; my $help = 0;; my $optionsOk = GetOptions( 'progress-id:i' => \$progressId, 'config-backup' => \$configBackup, 'bug-report|configuration-report' => \$bugReport, 'description=s' => \$description, 'destination=s' => \$destination, 'remote-backup=s' => \$remoteBackupName, 'fallback-to-ro' => \$fallbackToRO, 'help' => \$help, ); if (not $optionsOk) { print "Incorrect options\n"; print "$0 --help for usage instructions\n"; exit 1; } if ($help) { usage(); exit 0; } # see if we have incompatible operation modes my $incompatibleModes = grep { $_ } ($configBackup, $bugReport); if ($incompatibleModes > 1) { die "You can only choose one backup mode from --config-backup, or --bug-report. (--config-restore is the default restore mode)"; } my %params; if ($bugReport) { my $incompatibleWithBug = grep { $_ } ($description, $progressId); if ($incompatibleWithBug) { die "You have specified options incompatibles with the bug report mode)"; } } if ($progressId) { my $progress = EBox::ProgressIndicator->retrieve($progressId); # put progress indicator param $params{progress} = $progress; } if (defined $description) { $params{description} = $description; } if (defined $destination) { $params{destination} = $destination; } if ($fallbackToRO) { $params{fallbackToRO} = 1; } try { if (not $bugReport) { my $file = EBox::Backup->makeBackup(%params); print "Backup stored into file $file\n"; if ($remoteBackupName and EBox::Global->modExists('remoteservices')) { eval 'use EBox::RemoteServices::Backup'; my $backupService = EBox::RemoteServices::Backup->new(); my $description = $params{description}; $backupService->sendRemoteBackup($file, $remoteBackupName, $description); } } else { # with bug report we ignore all other options my $reportFile = EBox::Backup->makeBugReport(); print "Bug report stored into file $reportFile\n"; } } otherwise { my $ex = shift @_; if (exists $params{progress}) { $params{progress}->setAsFinished(1, $ex->text); } $ex->throw(); }; sub usage { print < --fallback-to-ro --remote-backup --progress-id (only needed for web framework) END } 1; zentyal-core-2.3.21+quantal1/src/scripts/clean-conf0000775000000000000000000000076012017102272017033 0ustar #!/usr/bin/perl use strict; use warnings; use EBox; use EBox::Config; use EBox::GlobalImpl; my ($module) = @ARGV; $module or die "You must supply the name of a Zentyal module"; EBox::init(); my $global = EBox::GlobalImpl->instance(); $global->delete_dir("global/conf/modules/$module"); $global->{redis}->delete_dir("global/state/ServiceModule/$module"); $global->{redis}->delete_dir("$module/conf"); $global->{redis}->delete_dir("$module/ro"); $global->{redis}->unset("$module/state"); 1; zentyal-core-2.3.21+quantal1/src/scripts/restart-trigger0000775000000000000000000000211512017102272020147 0ustar #!/usr/bin/perl # Copyright (C) 2011-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # This scripts restarts apache, logs and events modules use warnings; use strict; use EBox; use EBox::Util::Init; use EBox::Global; use Error qw(:try); EBox::init(); my $apacheMod = EBox::Global->getInstance()->modInstance('apache'); unless ($apacheMod->restartOnTrigger()) { exit 0; } foreach my $modName (qw(apache logs events)) { EBox::Util::Init::moduleRestart($modName); } 1; zentyal-core-2.3.21+quantal1/src/scripts/change-hostname0000775000000000000000000000071012017102272020062 0ustar #!/bin/bash NEW=$1 DOMAIN=$2 if [ -z $NEW ] then echo "Usage: $0 [new_domain]" exit 1 fi CURRENT=`hostname` sed -i "s/$CURRENT/$NEW/" /etc/hostname sed -i "s/\s$CURRENT$/\t$NEW/" /etc/hosts sed -i "s/\s$CURRENT\s/\t$NEW/g" /etc/hosts hostname $NEW if [ -n "$DOMAIN" ] then sed -i "s/^127.0.0.1.*/127.0.0.1\tlocalhost.localdomain localhost/" /etc/hosts sed -i "s/^127.0.1.1.*/127.0.1.1\t$NEW.$DOMAIN $NEW/" /etc/hosts fi zentyal-core-2.3.21+quantal1/src/scripts/restore-backup0000775000000000000000000000727012017102272017757 0ustar #!/usr/bin/perl use strict; use warnings; use EBox; use EBox::Backup; use EBox::ProgressIndicator; use Getopt::Long; use English qw(-no-match-vars); use Error qw(:try); EBox::init(); # check user.. #if ($EUID == getpwnam('ebox')) { # # ok ... #} #elsif ($EUID == 0) { # EBox::init(); #} #else { # die 'This program can be only run by root or ebox user'; #} # process cli my $progressId = undef; my @modsToRestore = (); my @modsToExclude = (); my $configRestore = 0; my $fullRestore = 0; my $dataRestore = 0; my $deleteBackup = 0; my $forceDependencies = 0; my $forceZentyalVersion = 0; my $revokeAllOnModuleFail = 1; my $continueOnModuleFail = 0; my $help = 0;; my $info = 0; my $optionsOk = GetOptions( 'module:s' => \@modsToRestore, 'exclude:s' => \@modsToExclude, 'progress-id:i' => \$progressId, 'config-restore' => \$configRestore, 'full-restore' => \$fullRestore, 'data-restore' => \$dataRestore, 'force-dependencies' => \$forceDependencies, 'force-zentyal-version' => \$forceZentyalVersion, 'delete-backup' => \$deleteBackup, 'revoke-all-on-module-fail!' => \$revokeAllOnModuleFail, 'continue-on-module-fail!' => \$continueOnModuleFail, 'info' => \$info, 'help' => \$help, ); if (not $optionsOk) { usage(); exit 1; } elsif (@ARGV != 1) { usage(); exit 1; } my ($file) = @ARGV; # see operation mode if ($help) { usage(); exit 0; } elsif ($info) { info($file); exit 0; } # operation mode: restore backup # see if we have incompatible operation modes my $incompatibleModes = grep { $_ } ($configRestore, $dataRestore, $fullRestore, ); if ($incompatibleModes > 1) { die "You can only choose one restore mode from --config-restore, --data-restore or --full-restore. (--config-restore is the default restore mode)"; } # prepare restoreBackup execution my %restoreParams; if ($progressId) { my $progress = EBox::ProgressIndicator->retrieve($progressId); $restoreParams{progress} = $progress; } if (@modsToRestore) { $restoreParams{modsToRestore} = \@modsToRestore; } if (@modsToExclude) { $restoreParams{modsToExclude} = \@modsToExclude; } $restoreParams{fullRestore} = $fullRestore; $restoreParams{dataRestore} = $dataRestore; $restoreParams{forceDependencies} = $forceDependencies; $restoreParams{forceZentyalVersion} = $forceZentyalVersion, $restoreParams{deleteBackup} = $deleteBackup; $restoreParams{revokeAllOnModuleFail} = $revokeAllOnModuleFail; $restoreParams{continueOnModuleFail} = $continueOnModuleFail; try { EBox::Backup->restoreBackup($file, %restoreParams); } otherwise { my $ex = shift @_; if (exists $restoreParams{progress}) { my $progress = $restoreParams{progress}; $progress->setAsFinished(1, $ex->text); } $ex->throw(); }; sub info { my ($archive) = @_; my $details_r = EBox::Backup->backupDetailsFromArchive($archive); my $desc = $details_r->{description}; my $date = $details_r->{date}; my $type = $details_r->{type}; my @modulesInBackup = @{ EBox::Backup->_modulesInBackup($archive) }; print < '554056bd16e9f34db567dfd613b706bd', 'crt'=> 'c2b7b96aa141f8e0e415f6a977193433', 'pem' => '9b51c428fe86e6ac03fc1427c5bc0958', ); unless (defined($destDir)) { print "Usage:$0 prefix destDir\n"; exit 1; } _checkCredentials(); _checkPrograms(); my ($keyFile, $keyUpdated) = _generateRSAKey($destDir, $RSA_LENGTH); my $certFile = _generateCert($destDir, $keyFile, $keyUpdated); _generatePem($destDir, $certFile, $keyFile, $keyUpdated); print "All server's certificate files in place\n\n"; sub _checkCredentials { if ($EUID != 0) { die "This script can only be run by root"; } my ($gid) = split '\s', $EGID; if ($gid != 0) { die "To run this script your primary group must be set to root"; } } sub _checkPrograms { my @programs = qw(openssl md5sum); foreach (@programs) { system "which $_"; if ($? != 0) { die "$_ program not found in the path. Make sure it is installed"; } } } sub _generateRSAKey { my ($destDir, $length) = @_; my $type = 'key'; my ($keyFile, $alreadyExists) = _generateFileInfraestructure($type, $destDir); return ($keyFile, 0) if $alreadyExists; my @cmds = ( "openssl genrsa $RSA_LENGTH > $keyFile", "chmod 0400 $keyFile", ); foreach (@cmds) { system $_; if ($? != 0) { die "Generation of RSA key failed"; } } print "New key file generated\n"; return ($keyFile, 1); } sub _generateCert { my ($destDir, $keyFile, $keyUpdated) = @_; my $type = 'crt'; my ($certFile, $alreadyExists) = _generateFileInfraestructure($type, $destDir, $keyUpdated, 'cert'); return $certFile if $alreadyExists; my $subject = q{/CN=eBox\ Server/}; my @cmds = ( "openssl req -new -x509 -batch -subj $subject -sha1 -days 3650 -key $keyFile > $certFile", "chmod 0400 $certFile", ); foreach (@cmds) { system $_; if ($? != 0) { die "Generation of CERT file failed"; } } print "New certificate file generated\n"; return $certFile; } sub _generatePem { my ($destDir, $certFile, $keyFile, $keyUpdated) = @_; my $type = 'pem'; my ($pemFile, $alreadyExists) = _generateFileInfraestructure($type, $destDir, $keyUpdated); return $pemFile if $alreadyExists; my @cmds = ( "cat $certFile $keyFile > $pemFile", "chmod 0400 $pemFile", ); foreach (@cmds) { system $_; if ($? != 0) { die "Generation of PEM file failed"; } } print "New PEM file generated\n"; } sub _generateFileInfraestructure { my ($type, $destDir, $alwaysDelete, $extension) = @_; defined $alwaysDelete or $alwaysDelete = 0; $extension or $extension = $type; my $sslDir = _sslDir($destDir, $type); my $file = "$sslDir/ssl.$extension"; if (_correctFileExists($type, $file, $alwaysDelete)) { print "$file already exists. Skipping generation\n"; return ($file, 1); } my @cmds = ( "touch $file", "chmod 0600 $file", ); foreach (@cmds) { system $_; if ($? != 0) { die "Generation of $type file failed"; } } return ($file, 0); } sub _sslDir { my ($destDir, $postfix) = @_; my $sslDir = "$destDir"; if (not -d $sslDir) { print "Creating eBox's $destDir SSL directory\n"; mkdir $sslDir, 0700; } return $sslDir; } sub _correctFileExists { my ($type, $file, $alwaysDelete) = @_; if ( -e $file) { my $compromisedSum = $COMPROMISED_FILES_SUMS{$type}; if (not $compromisedSum) { warn "md5sum for type $type not found. For safety we assume it was compromised"; } else { my $md5sumOutput = `md5sum $file`; ($? == 0) or die "Error calculating md5 sum of file $file"; my ($fileSum) = split '\s', $md5sumOutput; # removing file name if ($fileSum eq $compromisedSum) { print "File $file was compromissed. Removing it\n"; } elsif ($alwaysDelete) { print "File $file is not updated. Removing it\n"; } else { # left file untouched... return 1; } } unlink $file or die "Unable to remove $file"; } return 0; } 1; zentyal-core-2.3.21+quantal1/src/scripts/create-db0000775000000000000000000000216412017102272016654 0ustar #!/bin/bash db_name="zentyal" db_user="zentyal" create_db() { PASSWD_FILE='/var/lib/zentyal/conf/zentyal-mysql.passwd' if [ -s $PASSWD_FILE ] then PASSWD=`cat $PASSWD_FILE` else PASSWD=`tr -dc A-Za-z0-9 < /dev/urandom | head -c8` echo -n $PASSWD > $PASSWD_FILE chmod 400 $PASSWD_FILE fi echo "Creating the $db_name database" echo "CREATE DATABASE $db_name; GRANT ALL ON $db_name.* TO '$db_user'@'localhost' IDENTIFIED BY \"$PASSWD\"; FLUSH PRIVILEGES;" | mysql --defaults-file=/etc/mysql/debian.cnf perl -MEBox -MEBox::Util::SQL -e'EBox::init(); EBox::Util::SQL::createCoreTables(); 1'; mysqladmin -u root password "$PASSWD" } # FIXME: Is all this needed with mysql? As it uses upstart probably it is always alive # or we even can manage it... # Check if we can connect to postgresql (with one retry after restart) #su postgres -c 'psql -Alt' >& /dev/null || /etc/init.d/postgresql-8.4 restart #su postgres -c 'psql -Alt' >& /dev/null || exit 1 if ! [ -d /var/lib/mysql/$db_name ]; then create_db touch /var/lib/zentyal/.db-created fi zentyal-core-2.3.21+quantal1/src/scripts/redisvi0000775000000000000000000000317212017102272016473 0ustar #!/usr/bin/perl # Copyright (C) 2010-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA use warnings; use strict; use EBox; use EBox::Config::Redis; use EBox::Sudo; use Error qw(:try); my $dir = ''; if ($ARGV[0]) { $dir = $ARGV[0]; } my $editor = 'vi'; if (defined $ENV{VISUAL}) { $editor = $ENV{VISUAL}; } elsif (defined $ENV{EDITOR}) { $editor = $ENV{EDITOR}; } EBox::init(); my $redis = EBox::Config::Redis->instance(); my $dumpfile = "/var/lib/zentyal/conf/redisvi-dump-$$"; $redis->export_dir_to_file($dir, $dumpfile, 1); system("cp $dumpfile $dumpfile.orig"); system("$editor $dumpfile"); system("diff $dumpfile.orig $dumpfile"); if ($? == 0) { print "Not modified.\n"; } else { $redis->delete_dir($dir); try { $redis->import_dir_from_file($dumpfile); } otherwise { print "Import of modifications failed, restoring the original...\n"; $redis->delete_dir($dir); $redis->import_dir_from_file("$dumpfile.orig"); }; } unlink $dumpfile; unlink "$dumpfile.orig"; zentyal-core-2.3.21+quantal1/src/EBox/0000775000000000000000000000000012017102272014243 5ustar zentyal-core-2.3.21+quantal1/src/EBox/TestStubs.pm0000664000000000000000000002306612017102272016550 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::TestStubs; use base 'Exporter'; # package: EBox::TestStubs # # this package is the global facade to all zentyal-base test stubs # # warning: # do NOT confuse with EBox::TestStub (this package is the teststub for the -package- EBox) # use strict; use warnings; use Test::MockObject::Extends; use EBox::Sudo::TestStub; use EBox::TestStub; use EBox::Config::TestStub; use EBox::Module::Config::TestStub; use EBox::Global::TestStub; use EBox::NetWrappers::TestStub; our @EXPORT_OK = qw(activateEBoxTestStubs fakeEBoxModule setConfig setConfigKeys); our %EXPORT_TAGS = ( all => \@EXPORT_OK ); # # Function: activateTestStubs # # Some of the parts of eBox need to be replaced with tests stubs for # an easy testing. This sub is intended for do this setup in only # one place. Please notice test classes created using # EBox::Test::Class automatically call this function # # Parameters: # There are optional parameters only intended for advanced # usage; each of the test stub may be controlled with two # parameters. # # "fake$componentName" - whether to activate the teststub for # this component or not (default: true) # "$componentName" - an array ref with extra parameters for the # component. (optional) # # See also: # # # sub activateTestStubs { my %params = @_; my @components = qw(EBox Sudo Config ModuleConfig Global NetWrappers); # they will be faked in this order # set default parameters foreach my $stub (@components) { my $fakeSwitch = "fake$stub"; $params{$fakeSwitch} = 1 if (!exists $params{$fakeSwitch}); $params{$stub} = [] if (!exists $params{$stub}); } my %fakeByComponent = ( 'EBox' => \&EBox::TestStub::fake, 'Sudo' => \&EBox::Sudo::TestStub::fake, 'Config' => \&EBox::Config::TestStub::fake, 'ModuleConfig' => \&EBox::Module::Config::TestStub::fake, 'Global' => \&EBox::Global::TestStub::fake, 'NetWrappers' => \&EBox::NetWrappers::TestStub::fake, ); foreach my $comp (@components) { my $fakeSub_r = $fakeByComponent{$comp}; defined $comp or throw EBox::Exceptions::Internal("No fake sub supplied for $comp"); my $fakeSwitch = "fake$comp"; if ($params{$fakeSwitch}) { my $fakeParams = $params{$comp}; $fakeSub_r->(@{ $fakeParams }); } } } # # Function: setConfig # # Set EBox config keys. (Currently stored in redis) # Please do NOT confuse this sub with setEBoxConfigKeys # # Parameters: # the keys and values to be established # # Prerequisites: # activateEBoxTestStubs must be called to be able to use this function # Usage examples: # setConfig(); # clear the current configuration # setConfig( # '/ebox/modules/openvpn/user' => $UID, # '/ebox/modules/openvpn/group' => $gids[0], # '/ebox/modules/openvpn/conf_dir' => $confDir, # '/ebox/modules/openvpn/dh' => "$confDir/dh1024.pem", # ); # set some keys # sub setConfig { return EBox::Module::Config::TestStub::setConfig(@_); } # # Function: setConfigKey # # set a EBox config key and his value. (Currently stored in redis) # Plese do not confuse this sub with setEBoxConfigKeys # # Parameters: # the key and value to be established # # Prerequisites: # activateEBoxTestStubs must be called to be able to use this function # Usage examples: # setConfigKey( '/ebox/modules/openvpn/user' => $UID) sub setConfigKey { return EBox::Module::Config::TestStub::setEntry(@_); } # # Function: setEBoxModule # # Register an eBox module in eBox configuration. This is not needed # for modules created with fakeEBoxModule # # Parameters: # $name - the module name # $package - the perl module package # $depends - a list reference with the module # dependencies (optional) # Prerequisites: # activateEBoxTestStubs must be called to be able to use this function # Usage examples: # setEBoxModule('openvpn' => 'EBox::OpenVPN'); sub setEBoxModule { return EBox::Global::TestStub::setEBoxModule(@_); } # # Function: setEBoxConfigKeys # # Set the keys and values of configuration values accessed via # EBox::Config. Don't confuse this configuration values with # ''normal'' eBox configuration that is retrevied using module # methods, for fake those last configuration you can use # EBox::Test::setConfig # If you try to establish an inexistent key, an error will be raised # # # Parameters: # the keys and values to be established, at least you must supply a pair # # Prerequisites: # activateEBoxTestStubs must be called to be able to use this function # Usage examples: # setEboxConfigKeys(locale => 'es', group => 'ebox', css => '/var/www/css', lang => 'cat') sub setEBoxConfigKeys { return EBox::Config::TestStub::setConfigKeys(@_); } # # Function: fakeEBoxModule # # Create on the fly fake eBox modules # # Parameters: # (named parameters) # name - the name of the ebox module (required) # package - the perl package of the ebox module (optional) # isa - the parents of the package in addtion of EBox::Module::Config. # May be a scalar (one addtional parent) or a reference to a # list of parents (optional) # subs - the subs to be installed in the package; in the form of # a reference to a list containing the names and sub references # of each sub. (optional) # initializer - a initializer sub for the module. The module # constructor will call this sub passing itself as first # parameter. (optional) # # # Prerequisites: # activateEBoxTestStubs must be called to be able to use this function # Usage examples: # fakeEBoxModule(name => 'idleModule'); # fakeEBoxModules( # name => 'macaco', package => 'EBox::Macaco', # subs => [ sayHello => sub { print 'hi' } ], # ); # sub fakeEBoxModule { my %params = @_; exists $params{name} or throw EBox::Exceptions::Internal('fakeEBoxModule: lacks name paramater'); exists $params{package} or $params{package} = 'EBox::' . ucfirst $params{name}; my @isa = ('EBox::Module::Config'); if (exists $params{isa} ) { my @extraIsa = ref $params{isa} ? @{ $params{isa} } : ($params{isa}); push @isa, @extraIsa; } my $createIsaCode = 'package ' . $params{package} . "; use base qw(@isa);"; eval $createIsaCode; die "When creating ISA array $@" if $@; my $initializerSub = exists $params{initializer} ? $params{initializer} : sub { my ($self) = @_; return $self}; Test::MockObject->fake_module($params{package}, _create => sub { my $self = EBox::Module::Config->_create(name => $params{name}); bless $self, $params{package}; $self = $initializerSub->($self); return $self; }, @{ $params{subs} } ); EBox::Global::TestStub::setEBoxModule($params{name} => $params{package}); } # # Function: setFakeIfaces # # Set fake computer network interfaces. This ifaces will used by # EBox::NetWrappers methods # # Parameters: # # a list with pairs of interfaces names and # attributes. The name is a string and the attributes is a hash # ref with the following elements: # # up - boolean value # address - hash reference to a hash with IP # addresses as keys and netmasks as values # mac_address - string with the mac address # # Prerequisites: # activateEBoxTestStubs must be called to be able to use this function # Usage examples: # my @fakeIfaces = ( # 'eth0' => { # up => 1, # address => { # '192.168.3.4' => '255.255.255.0', # }, # mac_address => '00:EE:11:CC:CE:8D', # # }, # 'eth1' => { # up => 1, # address => { # '192.168.45.4' => '255.255.255.0', # '10.0.0.7' => '255.0.0.0', # }, # mac_address => '00:11:11:CC:CE:8D', # # }, # 'eth2' => { # up => 0, # address => { # '142.120.45.4' => '255.255.255.0', # '44.0.0.7' => '255.0.0.0', # }, # mac_address => '00:11:11:CC:AA:8D', # }, # # ); # # EBox::TestStubs::setFakeIfaces(@fakeIfaces); sub setFakeIfaces { my $params_r = { @_ }; EBox::NetWrappers::TestStub::setFakeIfaces($params_r); } # # Function: setFakeRoutes # # Set fake computer network routes. This fake routes will used by # EBox::NetWrappers functions # # Parameters: # a list with pairs of network destination and gateways # # Prerequisites: # activateEBoxTestStubs must be called to be able to use this function # Usage example: # my @routes = ( # '192.168.45.0' => '0.0.0.0', # '0.0.0.0' => '10.0.1.100', # '10.0.0.0' => '192.168.45.123', # ); # # EBox::TestStubs::setFakeRoutes(@routes); sub setFakeRoutes { my $params_r = { @_ }; EBox::NetWrappers::TestStub::setFakeRoutes($params_r); } 1; zentyal-core-2.3.21+quantal1/src/EBox/EventDaemon.pm0000664000000000000000000003126712017102272017017 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::EventDaemon; # Class: EBox::EventDaemon # # This class is the daemon which is in charge of managing events at # eBox. It supports an Observer pattern to add dinamically # Event Watchers. They should inherit from in # order to have support for reporting events within eBox # framework. Every watcher must just watch one event. # # In order to dispatch an event, the same Observer pattern is # used. Dinamically, you can upload a dispatcher to send the event # wherever the event said. # # You may send events (Dumped object) to dispatch through a # named pipe which is pointed by the file name # "/var/lib/zentyal/tmp/events-fifo'. use strict; use warnings; ################### # Dependencies ################### use EBox::Config; use EBox::Global; use EBox::DBEngineFactory; # Core modules use JSON::XS; use File::Slurp; use IO::Handle; use IO::Select; use Error qw(:try); use POSIX; use Time::Local qw(timelocal); # Constants: # # WATCHERS_DIR - String directory where the Watchers lie # DISPATCHERS_DIR - String directory where the Dispatchers lie # EVENTS_FIFO - String the path to the named pipe to send events to # dispatch # SCANNING_INTERVAL - Integer interval between scannings # use constant LOG_TABLE => 'events'; use constant EVENTS_FIFO => EBox::Config::tmp() . 'events-fifo'; use constant SCANNING_INTERVAL => 60; use constant EVENT_FOLDING_INTERVAL => 30 * 60; # half hour use constant MAX_MSG_LENGTH => 256; # Group: Public methods # Constructor: new # # The constructor for the # # Parameters: # # granularity - Integer number of seconds when the process will # call the watchers. # # Returns: # # - the event daemon # sub new { my ($class, $granularity) = @_; my $self = { granularity => $granularity, # Registered events is a hash ref indexed by watcher # class name with two fields: deadOut (the number of # seconds till next run) and instance (with an # instance of the method). watchers => {}, dispatchers => {}, json => JSON::XS->new()->allow_blessed(1)->convert_blessed(1), }; bless ($self, $class); return $self; } # Method: run # # Run the event daemon. It never dies # sub run { my ($self) = @_; $self->_init(); my $eventPipe; my $pid = open($eventPipe, "|-"); $eventPipe->autoflush(1); unless ( defined ( $pid )) { die "$$: Cannot create a process error: $!"; } elsif ( $pid ) { # Parent code $self->_mainWatcherLoop($eventPipe); } else { # Child code # STDIN will have the read code $self->_mainDispatcherLoop(); exit; } } # Group: Private methods # Method: _init # # Initialises the process. Close the first 64 file descriptors # apart from standard input/output/error. Become an eBox user. # Catch the HUP and TERM signals. # sub _init { my ($self) = @_; EBox::init(); # Create the named pipe unless ( -p EVENTS_FIFO ) { unlink(EVENTS_FIFO); POSIX::mkfifo(EVENTS_FIFO, 0700) or die "Can't make a named pipe: $!"; } } # Method: _mainWatcherLoop # # Where the action is made. The algorithm can be discribed as # following: # # It never returns. # # Parameters: # # eventPipe - Filehandle to write the events # sub _mainWatcherLoop { my ($self, $eventPipe) = @_; # Load watchers classes $self->_loadModules('watcher'); while (1) { foreach my $registeredEvent (keys %{$self->{watchers}}) { my $queueElementRef = $self->{watchers}->{$registeredEvent}; $queueElementRef->{deadOut} -= $self->{granularity}; if ( $queueElementRef->{deadOut} <= 0 ) { my $eventsRef = undef; try { # Run the event $eventsRef = $queueElementRef->{instance}->run(); } otherwise { my $exception = shift; EBox::warn("Error executing run from $registeredEvent: $exception"); # Deleting from registered events delete ($self->{watchers}->{$registeredEvent}); }; # An event has happened if ( defined ( $eventsRef )) { foreach my $event (@{$eventsRef}) { # Send the events to the dispatcher $self->_addToDispatch($eventPipe, $event); } } $queueElementRef->{deadOut} = $queueElementRef->{instance}->period(); } } sleep ($self->{granularity}); } } # Method: _mainDispatcherLoop # # Process will be in charge of dispatching the event # wherever the event says so and the dispatcher is available # # sub _mainDispatcherLoop { my ($self) = @_; # load dbengine if neccessary if ($self->_logEnabled()) { $self->{dbengine} = EBox::DBEngineFactory::DBEngine(); } # Load dispatcher classes $self->_loadModules('dispatcher'); # Start main loop with a select open(my $fifo, '+<', EVENTS_FIFO); my $select = new IO::Select(); $select->add(\*STDIN); $select->add($fifo); while (1) { my @ready = $select->can_read(SCANNING_INTERVAL); foreach my $fh (@ready) { my $data; { local $/ = "\0"; $data = readline($fh); } my $event = $self->{json}->decode($data); bless ($event, 'EBox::Event'); # log the event if log is enabled if (exists $self->{dbengine}) { $self->_logEvent($event); } # dispatch event to its watchers # skip the given data if it is not a valid EBox::Event object if (defined($event) and $event->isa('EBox::Event')) { $self->_dispatchEventByDispatcher($event); } } } } # Group: Private helper functions # Method: _loadModules # # Load installed watchers or dispatchers. # # Parameters: # # type - can be 'watcher' or 'dispatcher' # sub _loadModules { my ($self, $type) = @_; my $events = EBox::Global->getInstance(1)->modInstance('events'); my $model = $type eq 'watcher' ? $events->model('ConfigureWatchers') : $events->model('ConfigureDispatchers'); foreach my $id (@{$model->enabledRows()}) { my $row = $model->row($id); my $className = $row->valueByName($type); eval "use $className"; if ($@) { EBox::error("Error loading $type class: $className $@"); next; } my $instance = $className->new(); if ($type eq 'watcher') { $self->{watchers}->{$className} = { instance => $instance, deadOut => 0 }; } else { $self->{dispatchers}->{$className} = $instance; } } } # Method: _dispatchEventByDispatcher # # Dispatch the event wherever the event wants that could be any # available # # Parameters: # # event - the event to dispatch # sub _dispatchEventByDispatcher { my ($self, $event) = @_; my @requestedDispatchers = (); my $reqByEventRef = $event->dispatchTo(); if ( grep { 'any' } @{$reqByEventRef} ) { @requestedDispatchers = values ( %{$self->{dispatchers}} ); } else { my @reqByEvent = map { "EBox::Dispatcher::$_" } @{$reqByEventRef}; foreach my $dispatcherName (keys (%{$self->{dispatchers}})) { if ( grep { $dispatcherName } @reqByEvent ) { push ( @requestedDispatchers, $self->{dispatchers}->{$dispatcherName}); } } } # Dispatch the event foreach my $dispatcher (@requestedDispatchers) { try { $dispatcher->enable(); EBox::info("Send event to $dispatcher"); $dispatcher->send($event); } catch EBox::Exceptions::External with { my ($exc) = @_; EBox::warn($dispatcher->name() . ' is not enabled to send messages'); EBox::error($exc->stringify()); }; } } sub _logEnabled { my ($self) = @_; my $logs = EBox::Global->modInstance('logs'); defined $logs or return undef; $logs->isEnabled() or return undef; # check if log for events module is enabled my $enabledLogs = $logs->_restoreEnabledLogsModules(); return $enabledLogs->{events}; } sub _foldingEventInLog { my ($self, $event) = @_; my $dbh = $self->{dbengine}->{dbh}; if (not exists $self->{selectStmt}) { my $selectStmt = $dbh->prepare( 'SELECT id, lastTimestamp FROM '. LOG_TABLE . ' ' . 'WHERE level = ? ' . 'AND source = ? ' . 'AND message = ? ' . 'ORDER BY lastTimestamp DESC ' . 'LIMIT 1' ); $self->{selectStmt} = $selectStmt; } $self->{selectStmt}->execute( $event->level(), $event->source(), $event->message() ); my $storedEvent = $self->{selectStmt}->fetchrow_hashref(); if (not defined $storedEvent) { # not matching event found ... return undef; } my ($year, $mon, $mday, $hour, $min, $sec) = split /[\s\-:]/, $storedEvent->{lastTimestamp}; $year -= 1900; $mon -= 1; my $storedTimestamp = timelocal($sec,$min,$hour,$mday,$mon,$year); if (($storedTimestamp + EVENT_FOLDING_INTERVAL) > $event->timestamp()) { # Last event of the same type happened before last # half an hour my $id = $storedEvent->{id}; return $id; } return undef; } sub _updateFoldingEventInLog { my ($self, $id, $event) = @_; my $ts = $event->timestamp(); my @tsParts = localtime($ts); $tsParts[5] += 1900; $tsParts[4] += 1; my $lastTimestamp = join('-', @tsParts[5,4,3]) . ' ' . join(':', @tsParts[2,1,0]); my $dbh = $self->{dbengine}->{dbh}; if (not exists $self->{updateStmt}) { my $updateStmt = $dbh->prepare( 'UPDATE ' . LOG_TABLE . ' ' . 'SET nRepeated = nRepeated + 1, lastTimestamp = ? ' . 'WHERE id = ?' ) or die $dbh->errstr;; $self->{updateStmt} = $updateStmt; } $self->{updateStmt}->execute($lastTimestamp, $id); } sub _insertEventInLog { my ($self, $event) = @_; # We don't use names on the date to avoid issues # with DB insertions and localization my $timeStmp = strftime("%F %H:%M:%S %z", localtime($event->timestamp())); # truncate message if needed my $message = $event->message(); if (length($message) > MAX_MSG_LENGTH) { $message = substr ($message, 0, MAX_MSG_LENGTH); } my $values = { timestamp => $timeStmp, lastTimestamp => $timeStmp, level => $event->level(), source => $event->source(), message => $message, }; $self->{dbengine}->unbufferedInsert(LOG_TABLE, $values); } # Method: _logEvent # # Add the event to the events log # # Parameters: # # event - the event to dispatch # sub _logEvent { my ($self, $event) = @_; my $foldingEventId = $self->_foldingEventInLog($event); if ($foldingEventId) { $self->_updateFoldingEventInLog($foldingEventId, $event); } else { $self->_insertEventInLog($event); } } # Method: _addToDispatch # # Send to the Dispatcher daemon the given event through the # given pipe # # Parameters: # # eventPipe - Filehandle pipe to send events from the watcher # daemon to the dispatcher daemon # event - the event to dispatch # sub _addToDispatch { my ($self, $eventPipe, $event) = @_; my $eventStr = $self->{json}->encode($event); # Sending the dumpered event with a null char print $eventPipe ( $eventStr . "\0" ); } ############### # Main program ############### # Granularity: 10 second my $eventd = new EBox::EventDaemon(10); $eventd->run(); zentyal-core-2.3.21+quantal1/src/EBox/Logs.pm0000664000000000000000000005717412017102272015523 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Logs; use strict; use warnings; use base qw(EBox::Module::Service EBox::Report::DiskUsageProvider); use EBox::Global; use EBox::Gettext; use EBox::Loggerd; use EBox::Config; use EBox::Sudo; use EBox::Exceptions::InvalidData; use EBox::Exceptions::External; use EBox::Exceptions::Internal; use EBox::DBEngineFactory; use EBox::Service; use EBox::Logs::SlicedBackup; use EBox::FileSystem; use EBox::Util::SQLTypes; use POSIX qw(ceil); use constant LOG_DAEMON => 'ebox.loggerd'; use constant IMAGEPATH => EBox::Config::tmp . '/varimages'; use constant PIDPATH => EBox::Config::tmp . '/pids/'; use constant ENABLED_LOG_CONF_DIR => EBox::Config::conf . '/logs';; use constant ENABLED_LOG_CONF_FILE => ENABLED_LOG_CONF_DIR . '/enabled.conf'; use constant PG_DATA_DIR => '/var/lib/postgres'; # EBox::Module::Service interface # sub _create { my $class = shift; my $self = $class->SUPER::_create(name => 'logs', printableName => __('Logs'), @_); bless($self, $class); return $self; } # Method: depends # # Override EBox::Module::Base::depends # sub depends { my $mods = EBox::Global->modInstancesOfType('EBox::LogObserver'); my @names = map ($_->{name}, @$mods); return \@names; } sub _daemons { return [ { 'name' => LOG_DAEMON, 'precondition' => \&_loggerdPrecondition, } ]; } # Method: _loggerdPrecondition # # loggerd daemon precondition, checks that a least one logger domain is # enabled sub _loggerdPrecondition { my ($self) = @_; my $enabled = $self->allEnabledLogHelpers(); return @{ $enabled } > 0; } # Method: enableService # # Used to enable a service. Overriddien to notify all LogObserver of the # changes. # # Parameters: # # boolean - true to enable, false to disable # # Overriddes: # sub enableService { my ($self, $status) = @_; defined $status or $status = 0; return unless ($self->isEnabled() xor $status); $self->SUPER::enableService($status); $self->_notifyLogEnable(); } sub _notifyLogEnable { my ($self, $status) = @_; my $modules = $self->getLogsModules(); foreach my $module (@{ $modules }) { $module->enableLog($status); } } # Method: isRunning # # Overrides: # # # sub isRunning { my ($self) = @_; return $self->isEnabled(); } # Method: _setConf # # Overrides: # # # sub _setConf { my ($self) = @_; $self->_saveEnabledLogsModules(); } sub cleanup { my ($self) = @_; $self->SUPER::revokeConfig(); } # Method: allEnabledLogHelpers # # This function fetchs all the classes implemeting the interface # which have been enabled for the user. # # If the user has not configured anything yet, all are enabled # by default. # # Returns: # # Array ref of objects # sub allEnabledLogHelpers { my ($self) = @_; my $global = EBox::Global->getInstance(); my $enabledLogs = $self->_restoreEnabledLogsModules(); # If there's no configuration stored it means the user # has not configured them yet. So by default, we enable all them unless (defined($enabledLogs)) { return $self->allLogHelpers(); } my @enabledObjects; my @mods = @{$global->modInstancesOfType('EBox::LogObserver')}; foreach my $mod (@mods) { if ($global->modEnabled($mod->name) and $enabledLogs->{$mod->name}) { my $logHelper = $mod->logHelper(); defined $logHelper or next; push (@enabledObjects, $logHelper); } } return \@enabledObjects; } # Method: allLogHelpers # # This function fetchs all the classes implemeting the interface # and its associated fifo. # # Returns: # # An array ref containing hash references holding: # # fifo - path to log file # classname - class name of the implementation # sub allLogHelpers { my ($self) = @_; my $global = EBox::Global->getInstance(); my @objects; my @mods = @{$global->modInstancesOfType('EBox::LogObserver')}; foreach my $mod (@mods) { my $obj = $mod->logHelper(); next unless defined($obj); push @objects, $obj; } return \@objects; } sub getLogsModules { my $global = EBox::Global->getInstance(); return [grep { $_->configured() } @{$global->modInstancesOfType('EBox::LogObserver')}]; } # Method: getAllTables # # Get the table information from all modules which implements # the log observer # # Return: # # hash ref - the table information indexed by table name (index) # as returned by + 'helper' # component which is the module that implements the LogObserver # interface # sub getAllTables { my ($self, $noCache) = @_; my $global = EBox::Global->getInstance(); my $tables; if (not $noCache and $self->{tables}) { return $self->{tables}; } foreach my $mod (@{getLogsModules()}) { my @tableInfos = @{ $self->getModTableInfos($mod) }; foreach my $comp (@tableInfos) { $comp->{'helper'} = $mod; next unless ($comp); $tables->{$comp->{'tablename'}} = $comp; } } $self->{tables} = $tables; return $tables; } # Method: getTableInfo # # Accessor to the table information from a log observer # # Returns: # # hash ref - the table information returned by # + 'helper' # component which is the module that implements the LogObserver # interface # sub getTableInfo { my ($self, $index) = @_; my $tables = $self->getAllTables(); unless (exists $tables->{$index}) { $self->{tables} = undef; # Delete cache $tables = $self->getAllTables(); # Ask again } return $tables->{$index}; } sub getModTableInfos { my ($self, $mod) = @_; my @tableInfos; my $ti = $mod->tableInfo(); if (ref $ti eq 'ARRAY') { @tableInfos = @{ $ti }; } elsif (ref $ti eq 'HASH') { EBox::warn('tableInfo() in ' . $mod->name . ' must return a reference to a list of hashes not the hash itself'); @tableInfos = ( $ti ); } else { throw EBox::Exceptions::Internal( $mod->name . "'s tableInfo returned invalid module" ); } return \@tableInfos; } sub getLogDomains { my ($self) = @_; my $tables = $self->getAllTables(); my %logdomains = map { $_ => $tables->{$_}->{'name'} } keys %{$tables}; return \%logdomains; } sub backupDomains { my $name = 'logs'; my %attrs = ( printableName => __('Logs'), description => __(q{Zentyal Server logs database}), extraDataDump => 1, ); return ($name, \%attrs); } sub dumpExtraBackupData { my ($self, $dir, %backupDomains) = @_; my @domainsDumped; if ($backupDomains{logs} ) { my $logsDir = $dir . '/logs'; if (not -d $logsDir) { mkdir $logsDir or throw EBox::Exceptions::Internal("Cannot create $logsDir: $!"); } my $dbengine = EBox::DBEngineFactory::DBEngine(); my $dumpFileBasename = "eboxlogs"; $dbengine->backupDB($logsDir, $dumpFileBasename); push @domainsDumped, 'logs'; } return \@domainsDumped; } sub dumpExtraBackupDataSize { my ($self, $dir, %backupDomains) = @_; my $size = 0; if ($backupDomains{logs} ) { $size += EBox::FileSystem::dirDiskUsage($dir); } return $size; } sub _checkValidDate # (date) { my ($datestr) = @_; my ($date, $time) = split (/ /, $datestr); my ($year, $month, $day) = split (/-/, $date); my ($hour, $min, $sec) = split (/:/, $time); unless (defined($year) and defined($month) and defined($day)) { return undef; } return undef unless ($year =~ /\d\d\d\d/ ); return undef unless ($month =~ /\d+/ and $month < 13 and $month > 0); return undef unless ($day =~ /\d+/ and $day < 32 and $day > 0); return undef unless ($hour =~ /\d+/ and $hour < 24 and $hour > -1); return undef unless ($min =~ /\d+/ and $min < 60 and $min > -1); return undef unless ($sec =~ /\d+/ and $sec < 60 and $sec > -1); return 1; } # Method: search # # Search for content in stored logs (in mysql database) # # Parameters: # # from - String which represents the "from" date in "year-month-day # hour:min:sec" format # # to - String which represents the "to" date in "year-month-day # hour:min:sec" format # # index - String the table's name # # pagesize - Int the page's size to return the result # # page - Int the page to search for results # # filters - hash ref a list of filters indexed by name which # contains the value of the given filter (normally a # string). Passing *undef* no filters are applied # # Returns: # # hash ref - containing the search result. The components are # the following: # # totalret - Int the number of results, it could be zero # arrayret - array ref containing the each returned row. Each # component is an hash ref whose description is determined by # the returned value of with # parameter . # sub search { my ($self, $from, $to, $index, $pagesize, $page, $timecol, $filters) = @_; my $dbengine = EBox::DBEngineFactory::DBEngine(); my $tables = $self->getAllTables(); my $tableinfo = $tables->{$index}; my $table = $tableinfo->{'tablename'}; unless (defined $tableinfo) { throw EBox::Exceptions::External( __x( 'Table {table} does not exist', 'table' => $table)); } $self->{'sqlselect'} = {}; $self->_addTableName($table); if (_checkValidDate($from)) { $self->_addDateFilter($timecol, $from, '>'); } if (_checkValidDate($to)) { $self->_addDateFilter($timecol, $to, '<'); } if ($filters and %{$filters}) { while (my ($field, $filterValue) = each %{$filters}) { $field or next; $filterValue or next; unless (exists $tableinfo->{'titles'}->{$field}) { throw EBox::Exceptions::Internal( "Field $field does not appear in tableinfo's titles field"); } if ($field eq 'event') { $self->{'sqlselect'}->{'filter'}->{$field} = $filterValue; } else { my $type = exists $tableinfo->{types}->{$field} ? $tableinfo->{types}->{$field} : undef; if ($type) { $field = EBox::Util::SQLTypes::stringifier($type, $field); } $self->{'sqlselect'}->{'regexp'}->{$field} = $filterValue; } } } $self->_addSelect('COUNT(*)'); my @count = @{$dbengine->query($self->_sqlStmnt())}; my $tcount = $count[0]{'COUNT(*)'}; # Do not go on if you don't have any result if ($tcount == 0) { return { 'totalret' => $tcount, 'arrayret' => [], }; } my $tpages = ceil($tcount / $pagesize) - 1; if ($page < 0) { $page = 0; } if ($page > $tpages) { $page = $tpages; } my $offset = $page * $pagesize; $self->_addPager($offset, $pagesize); $self->_addOrder("$timecol DESC"); if ($tableinfo->{types}) { my @keys; foreach my $key (keys %{$tableinfo->{titles}}) { my $type = $tableinfo->{types}->{$key}; if ($type) { $key = EBox::Util::SQLTypes::acquirer($type, $key); } push (@keys, $key); } $self->_addSelect(join (',', @keys)); } else { $self->_addSelect('*'); } my @ret = @{$dbengine->query($self->_sqlStmnt())}; my $hashret = { 'totalret' => $tcount, 'arrayret' => \@ret }; return $hashret; } # Method: totalRecords # # Get the total records stored in database for a given table # # Parameters: # # tableName - String the table name to check the number of # records # # Returns: # # Integer - the number of records for this table # sub totalRecords { my ($self, $table) = @_; my $dbengine = EBox::DBEngineFactory::DBEngine(); my $sql = "SELECT COUNT(*) FROM $table"; my @tarray = @{$dbengine->query($sql)}; my $tcount = $tarray[0]{'COUNT(*)'}; return $tcount; } # Method: consolidatedLogForDay # # Parameters: # table - consolidated table. The suffix '_daily' is added automaticallu # date - date in format yyyy-mm-dd # # Returns: # array reference. Each row will be a hash reference with # column/values as key/values. Remember that it will be always a 'date' # field. # If there is not data it will return a empty array sub consolidatedLogForDay { my ($self, $table, $date) = @_; $date or throw EBox::Exceptions::MissingArgument('date'); $table or throw EBox::Exceptions::MissingArgument('table'); # put the standard 00:00:00 hour ($date) = split '\s', $date; $date .= ' 00:00:00'; $table = $table . '_daily'; my $dbengine = EBox::DBEngineFactory::DBEngine(); # FIXME: what happens with acquirers here? my $sql = "SELECT * FROM $table WHERE date='$date'"; my @results = @{ $dbengine->query($sql) }; return \@results; } # Method: yesterdayDate # # Returns: # the yesterday date in string format so it can used in SQL queries and # i nthe consolidatedLogForDay method sub yesterdayDate { my ($self) = @_; my $yesterdayTs = time() - 86400; # 86400 seconds in a day my ($sec,$min,$hour,$mday,$mon,$year) = localtime($yesterdayTs); $year += 1900; $mon +=1; return "$year-$mon-$mday 00:00:00"; } sub _addFilter { my ($self, $field, $filter) = @_; return unless (defined($field) and defined($filter) and length($filter) > 0); $self->{'sqlselect'}->{'filter'}->{$field} = $filter; } sub _addDateFilter { my ($self, $field, $date, $operator) = @_; return unless (defined($date) and defined($operator)); $self->{'sqlselect'}->{'date'}->{$operator}->{'date'} = $date; $self->{'sqlselect'}->{'date'}->{$operator}->{'field'} = $field; } sub _addPager { my ($self, $offset, $limit) = @_; $self->{'sqlselect'}->{'offset'} = $offset; $self->{'sqlselect'}->{'limit'} = $limit; } sub _addOrder { my ($self, $order) = @_; $self->{sqlselect}->{order} = $order; } sub _addTableName { my ($self, $table) = @_; $self->{'sqlselect'}->{'table'} = $table; } sub _addSelect { my ($self, $select) = @_; $self->{'sqlselect'}->{'select'} = $select; } sub _sqlStmnt { my ($self) = @_; my @params; my $sql = $self->{'sqlselect'}; my $stmt = "SELECT $sql->{'select'} FROM $sql->{'table'} "; if ($sql->{'regexp'} or $sql->{'date'}) { $stmt .= 'WHERE '; } my $and = ''; if ($sql->{'date'}) { foreach my $op (keys %{$sql->{'date'}}) { $stmt .= "$and $sql->{'date'}->{$op}->{'field'} $op ? "; $and = 'AND'; push @params, $sql->{'date'}->{$op}->{'date'}; } } if ($sql->{'regexp'}) { foreach my $field (keys %{$sql->{'regexp'}}) { $stmt .= "$and CAST($field as CHAR CHARACTER SET utf8) REGEXP ? "; $and = 'AND'; push @params, $sql->{'regexp'}->{$field}; } } if ($sql->{'filter'}) { foreach my $field (keys %{$sql->{'filter'}}) { $stmt .= "$and $field = ? "; $and = 'AND'; push @params, $sql->{'filter'}->{$field}; } } if ($sql->{order}) { $stmt .= 'ORDER BY ' . $sql->{order} . ' '; } if (defined ($sql->{limit})) { $stmt .= 'LIMIT ?'; push (@params, $sql->{limit}); if (defined ($sql->{offset})) { $stmt .= ' OFFSET ?'; push (@params, $sql->{offset}); } } return $stmt, @params; } # Method: menu # # Overrides EBox::Module method. # # sub menu { my ($self, $root) = @_; my $folder = new EBox::Menu::Folder('name' => 'Maintenance', 'text' => __('Maintenance'), 'separator' => 'Core', 'order' => 70); $folder->add(new EBox::Menu::Item('url' => 'Maintenance/Logs', 'text' => $self->printableName(), 'order' => 20)); $root->add($folder); } # Helper functions # Method: _saveEnabledLogs # # (Private) # # This function saves the enabled logs in a file. # We have to do this because the logger daemon will request this # configuration as root user. # # Another approach could be creating a separated script to # query ebox conf. # sub _saveEnabledLogsModules { my ($self) = @_; my $enabledLogs = $self->model('ConfigureLogs')->enabledLogs(); unless (-d ENABLED_LOG_CONF_DIR) { mkdir (ENABLED_LOG_CONF_DIR); } # Create a string of domains separated by comas my $enabledLogsString = join (',', keys %{$enabledLogs}); my $file; unless (open($file, '>' . ENABLED_LOG_CONF_FILE)) { throw EBox::Exceptions::Internal( 'Cannot open ' . ENABLED_LOG_CONF_FILE); } print $file "$enabledLogsString"; close($file); } # Method: _restoreEnabledLogsModules # # (Private) # # This function restores the enabled logs saved in a file by # # We have to do this because the logger daemon will request this # configuration as root user. # # Anotther approach could be creating a separated script to # query ebox conf. # # Returns: # # undef - if there's no enabled logs stored yet # hash ref containing the enabled logs sub _restoreEnabledLogsModules { my ($self) = @_; my $file; unless (open($file, ENABLED_LOG_CONF_FILE)) { return undef; } my $string = <$file>; close($file); return undef unless (defined($string)); my %enabled; foreach my $domain (split(/,/, $string)) { $enabled{$domain} = 1; } return \%enabled; } # Overrides: # EBox::Report::DiskUsageProivider::_facilitiesForDiskUsage # # Warning: # this implies thhat all postgresql data are log, if someday other kind of # data is added to the database we will to change this (and maybe overriding # EBox::Report::DiskUsageProivider::diskUsage will be needed) sub _facilitiesForDiskUsage { my ($self) = @_; my $printableName = __('Log messages'); return { $printableName => [ PG_DATA_DIR ], }; } # Method: forcePurge # # Force to purge every table used to log data in eBox given a # timestamp # # Parameters: # # lifetime - Int the allowed data not to purge should be # timestamped before this limit which is set in hours # # Exceptions: # # - thrown if the lifetime is not a # positive number # sub forcePurge { my ($self, $lifetime) = @_; ($lifetime > 0) or throw EBox::Exceptions::External( __("Lifetime parameter must be a positive number of hours") ); my $now = time(); my $thresholdDate = $self->_thresholdDate($lifetime, $now); my @tables = map { @{ $self->getModTableInfos($_) } } @{ $self->getLogsModules }; foreach my $tableInfo ( @tables ) { my $table = $tableInfo->{tablename}; my $timeCol = 'timestamp'; $self->_purgeTable($table, $timeCol, $thresholdDate); } } # Method: purge # # Purge every table used to log data in eBox with the threshold # lifetime defined by 'lifetime' field in # model # # This method is called by a cron job. # # Exceptions: # # - thrown if the lifetime is not a # positive number # sub purge { my ($self) = @_; my $now = time(); my %thresholdByModule = (); # get the threshold date for each domain my $model = $self->model('ConfigureLogs'); foreach my $id (@{$model->ids()}) { my $row_r = $model->row($id); my $lifeTime = $row_r->valueByName('lifeTime'); # if lifeTime == 0, it should never expire $lifeTime or next; my $threshold = $self->_thresholdDate($lifeTime, $now); $thresholdByModule{$row_r->valueByName('domain')} = $threshold; } # purge each module while (my ($modName, $threshold) = each %thresholdByModule) { my $mod = EBox::Global->modInstance($modName); my @logTables = @{ $self->getModTableInfos($mod) }; foreach my $table (@logTables) { my $dbTable = $table->{tablename}; my $timeCol = 'timestamp'; $self->_purgeTable($dbTable, $timeCol, $threshold); } } } # Transform an hour into a localtime sub _thresholdDate { my ($self, $lifeTime, $now) = @_; # lifeTime must be in hours my $lifeTimeSeconds = $lifeTime * 3600; my $threshold = $now - $lifeTimeSeconds; return scalar localtime($threshold); } # Do perform the purge in a table sub _purgeTable { my ($self, $table, $timeCol, $thresholdDate) = @_; my $finalThreshold; my $dbengine = EBox::DBEngineFactory::DBEngine(); if (EBox::Logs::SlicedBackup::slicedMode()) { $finalThreshold = EBox::Logs::SlicedBackup::limitPurgeThreshold( $dbengine, $table, $thresholdDate, ); if (not defined $finalThreshold) { EBox::info("Cannot purge logs of table $table before $thresholdDate because they are unarchived backup slices"); return; } elsif ($thresholdDate ne $finalThreshold) { EBox::info("Log purge of table before $thresholdDate has be restricted to records before $finalThreshold because it were not archived backup slices"); } } else { $finalThreshold = $thresholdDate; } my $sqlStatement = "DELETE FROM $table WHERE $timeCol < '$finalThreshold'"; $dbengine->do($sqlStatement); } sub archiveBackupSlices { my ($self, $force) = @_; if (not $force) { my $enabled = EBox::Logs::SlicedBackup::slicedMode(); $enabled or return; } my $dbengine = EBox::DBEngineFactory::DBEngine(); EBox::Logs::SlicedBackup::archive($dbengine); } 1; zentyal-core-2.3.21+quantal1/src/EBox/ProgressIndicator/0000775000000000000000000000000012017102272017704 5ustar zentyal-core-2.3.21+quantal1/src/EBox/ProgressIndicator/Test.pm0000664000000000000000000000613712017102272021170 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::ProgressIndicator::Test; use strict; use warnings; use base 'EBox::Test::Class'; use Test::Exception; use Test::More; use Test::MockObject; use lib '../..'; use EBox::TestStubs; use EBox::ProgressIndicator; sub _fakeModules : Test(startup) { EBox::TestStubs::fakeEBoxModule( name => 'apache', class => 'EBox::Apache', ); Test::MockObject->fake_module( 'EBox::ProgressIndicator', _fork => sub { }, ); } sub creationAndRetrieveTest : Test(5) { my $progress; lives_ok { $progress = EBox::ProgressIndicator->create( totalTicks => 4, executable => '/bin/ls', ); } 'creating progress indicator'; my $id = $progress->id; my $progress2; lives_ok { $progress2 = EBox::ProgressIndicator->retrieve($id); } 'retrieve the same progress indicator'; my %progressAttrs; my %progress2Attrs; foreach (qw(id ticks totalTicks started _executable)) { $progressAttrs{$_} = $progress->$_; $progress2Attrs{$_} = $progress2->$_; } is_deeply \%progressAttrs, \%progress2Attrs, 'Checking that the two objects are equivalent'; lives_ok { $progress->destroy() } 'destroy progress indicator'; dies_ok { EBox::ProgressIndicator->retrieve($id); } 'checking we cannot retreive a destroyed progress indicator'; } sub basicUseCaseTest : Test(13) { my $totalTicks = 4; my $progress = EBox::ProgressIndicator->create( totalTicks => $totalTicks, executable => '/bin/ls', ); ok (not $progress->started), 'Checking started propierty after creation of the indicator'; ok (not $progress->finished), 'Checking finished propierty after creation of the indicator'; lives_ok { $progress->runExecutable(); } 'run executable'; ok $progress->started, 'Checking started propierty after runExecutable'; ok (not $progress->finished), 'Checking finished propierty after runExecutable'; my $i = 1; while ($i <= $totalTicks) { $progress->notifyTick(); is $progress->ticks(), $i, 'checking tick count'; $i++; } ok $progress->started, 'Checking started propierty after notify all the ticks'; ok (not $progress->finished), 'Checking finished propierty after notify all the ticks'; $progress->setAsFinished(); ok $progress->finished(), 'checking wether object is marked as finished after marked as finished'; ok $progress->started, 'Checking started propierty after object is marked as finished'; } 1; zentyal-core-2.3.21+quantal1/src/EBox/ThirdParty/0000775000000000000000000000000012017102272016335 5ustar zentyal-core-2.3.21+quantal1/src/EBox/ThirdParty/Apache2/0000775000000000000000000000000012017102272017600 5ustar zentyal-core-2.3.21+quantal1/src/EBox/ThirdParty/Apache2/AuthCookie/0000775000000000000000000000000012017102272021633 5ustar zentyal-core-2.3.21+quantal1/src/EBox/ThirdParty/Apache2/AuthCookie/Util.pm0000664000000000000000000000534012017102272023110 0ustar # This software is copyright (c) 2000 by Ken Williams. # # This is free software; you can redistribute it and/or modify it under # the same terms as the Perl 5 programming language system itself. # # Terms of the Perl programming language system itself # # a) the GNU General Public License as published by the Free # Software Foundation; either version 1, or (at your option) any # later version, or # b) the "Artistic License" package EBox::ThirdParty::Apache2::AuthCookie::Util; use strict; use vars '$VERSION'; $VERSION = do { my @r = (q$Revision: 222 $ =~ /\d+/g); sprintf "%d."."%02d" x $#r, @r }; # must be all one line, for MakeMaker # -- expires() shamelessly taken from CGI::Util sub expires { my($time,$format) = @_; $format ||= 'http'; my(@MON)=qw/Jan Feb Mar Apr May Jun Jul Aug Sep Oct Nov Dec/; my(@WDAY) = qw/Sun Mon Tue Wed Thu Fri Sat/; # pass through preformatted dates for the sake of expire_calc() $time = expire_calc($time); return $time unless $time =~ /^\d+$/; # make HTTP/cookie date string from GMT'ed time # (cookies use '-' as date separator, HTTP uses ' ') my($sc) = ' '; $sc = '-' if $format eq "cookie"; my($sec,$min,$hour,$mday,$mon,$year,$wday) = gmtime($time); $year += 1900; return sprintf("%s, %02d$sc%s$sc%04d %02d:%02d:%02d GMT", $WDAY[$wday],$mday,$MON[$mon],$year,$hour,$min,$sec); } # -- expire_calc() shamelessly taken from CGI::Util # This internal routine creates an expires time exactly some number of # hours from the current time. It incorporates modifications from # Mark Fisher. sub expire_calc { my($time) = @_; my(%mult) = ('s'=>1, 'm'=>60, 'h'=>60*60, 'd'=>60*60*24, 'M'=>60*60*24*30, 'y'=>60*60*24*365); # format for time can be in any of the forms... # "now" -- expire immediately # "+180s" -- in 180 seconds # "+2m" -- in 2 minutes # "+12h" -- in 12 hours # "+1d" -- in 1 day # "+3M" -- in 3 months # "+2y" -- in 2 years # "-3m" -- 3 minutes ago(!) # If you don't supply one of these forms, we assume you are # specifying the date yourself my($offset); if (!$time || (lc($time) eq 'now')) { $offset = 0; } elsif ($time=~/^\d+/) { return $time; } elsif ($time=~/^([+-]?(?:\d+|\d*\.\d*))([mhdMy]?)/) { $offset = ($mult{$2} || 1)*$1; } else { return $time; } return (time+$offset); } # escape embedded CR, LF, TAB's to prevent possible XSS attacks. # see http://www.securiteam.com/securityreviews/5WP0E2KFGK.html sub escape_destination { my $text = shift; $text =~ s/([\r\n\t])/sprintf("%%%02X", ord $1)/ge; return $text; } 1; zentyal-core-2.3.21+quantal1/src/EBox/ThirdParty/Apache2/AuthCookie.pm0000664000000000000000000003416412017102272022201 0ustar # This software is copyright (c) 2000 by Ken Williams. # # This is free software; you can redistribute it and/or modify it under # the same terms as the Perl 5 programming language system itself. # # Terms of the Perl programming language system itself # # a) the GNU General Public License as published by the Free # Software Foundation; either version 1, or (at your option) any # later version, or # b) the "Artistic License" package EBox::ThirdParty::Apache2::AuthCookie; use strict; use Carp; use CGI '3.12'; use mod_perl2 '1.9922'; use EBox::ThirdParty::Apache2::AuthCookie::Util; use Apache2::RequestRec; use Apache2::RequestUtil; use Apache2::Log; use Apache2::Access; use Apache2::Response; use Apache2::Util; use APR::Table; use Apache2::Const qw(:common M_GET HTTP_FORBIDDEN HTTP_MOVED_TEMPORARILY); use vars qw($VERSION); # $Id: AuthCookie.pm 227 2008-04-16 14:59:56Z mschout $ $VERSION = '3.12'; sub recognize_user { my ($self, $r) = @_; my $debug = $r->dir_config("AuthCookieDebug") || 0; my $auth_type = $r->auth_type; my $auth_name = $r->auth_name; return DECLINED unless $auth_type and $auth_name; return DECLINED unless $r->headers_in->get('Cookie'); my $cookie = $self->key($r); my $cookie_name = $self->cookie_name($r); $r->server->log_error("cookie $cookie_name is $cookie") if $debug >= 2; return DECLINED unless $cookie; my ($user,@args) = $auth_type->authen_ses_key($r, $cookie); if ($user and scalar @args == 0) { $r->server->log_error("user is $user") if $debug >= 2; # send cookie with update expires timestamp if session timeout is on if (my $expires = $r->dir_config("${auth_name}SessionTimeout")) { $self->send_cookie($r, $cookie, {expires => $expires}); } $r->user($user); } elsif (scalar @args > 0 and $auth_type->can('custom_errors')) { return $auth_type->custom_errors($r, $user, @args); } return OK; } sub cookie_name { my ($self, $r) = @_; my $auth_type = $r->auth_type; my $auth_name = $r->auth_name; my $cookie_name = $r->dir_config("${auth_name}CookieName") || "${auth_type}_${auth_name}"; return $cookie_name; } sub handle_cache { my ($self, $r) = @_; my $auth_name = $r->auth_name; return unless $auth_name; unless ($r->dir_config("${auth_name}Cache")) { $r->no_cache(1); $r->err_headers_out->set(Pragma => 'no-cache'); } } sub remove_cookie { my ($self, $r) = @_; my $cookie_name = $self->cookie_name($r); my $debug = $r->dir_config("AuthCookieDebug") || 0; my $str = $self->cookie_string( request => $r, key => $cookie_name, value => '', expires => 'Mon, 21-May-1971 00:00:00 GMT' ); $r->err_headers_out->add("Set-Cookie" => "$str"); $r->server->log_error("removed_cookie ". $r->err_headers_out->get("Set-Cookie")) if $debug >= 2; } # convert current request to GET sub _convert_to_get { my ($self, $r, $args) = @_; return unless $r->method eq 'POST'; my $debug = $r->dir_config("AuthCookieDebug") || 0; $r->server->log_error("Converting POST -> GET") if $debug >= 2; my @pairs =(); while (my ($name, $value) = each %$args) { # we dont want to copy login data, only extra data next if $name eq 'destination' or $name =~ /^credential_\d+$/; $value = '' unless defined $value; for my $v (split /\0/, $value) { push @pairs, escape_uri($r, $name) . '=' . escape_uri($r, $v); } } $r->args(join '&', @pairs) if scalar(@pairs) > 0; $r->method('GET'); $r->method_number(M_GET); $r->headers_in->unset('Content-Length'); } sub escape_uri { my ($r, $string) = @_; return EBox::ThirdParty::Apache2::AuthCookie::Util::escape_destination($string, $r->pool); } # get GET or POST data and return hash containing the data. sub _get_form_data { my ($self, $r) = @_; my $data = ''; my $cgi = CGI->new($r); return $cgi->Vars(); } sub login { my ($self, $r) = @_; my $debug = $r->dir_config("AuthCookieDebug") || 0; my $auth_type = $r->auth_type; my $auth_name = $r->auth_name; my %args = $self->_get_form_data($r); if ($r->method eq 'POST') { $self->_convert_to_get($r, \%args); } unless (exists $args{'destination'}) { $r->server->log_error("No key 'destination' found in form data"); $r->subprocess_env('AuthCookieReason', 'no_cookie'); return $auth_type->login_form($r); } # Get the credentials from the data posted by the client my @credentials; for (my $i = 0; exists $args{"credential_$i"}; $i++) { my $key = "credential_$i"; $r->server->log_error("$key $args{$key}") if $debug >= 2; push @credentials, $args{$key}; } # save creds in pnotes so login form script can use them if it wants to $r->pnotes("${auth_name}Creds", \@credentials); # Exchange the credentials for a session key. my $ses_key = $self->authen_cred($r, @credentials); unless ($ses_key) { $r->server->log_error("Bad credentials") if $debug >= 2; $r->subprocess_env('AuthCookieReason', 'bad_credentials'); $r->uri($args{'destination'}); return $auth_type->login_form($r); } if ($debug >= 2) { defined $ses_key ? $r->server->log_error("ses_key $ses_key") : $r->server->log_error("ses_key undefined"); } $self->send_cookie($r, $ses_key); $self->handle_cache($r); if ($debug >= 2) { $r->server->log_error("redirect to $args{destination}"); } $r->headers_out->set( "Location" => $self->untaint_destination($args{'destination'})); return HTTP_MOVED_TEMPORARILY; } sub untaint_destination { my ($self, $dest) = @_; return EBox::ThirdParty::Apache2::AuthCookie::Util::escape_destination($dest); } sub logout { my ($self,$r) = @_; my $debug = $r->dir_config("AuthCookieDebug") || 0; $self->remove_cookie($r); $self->handle_cache($r); } sub authenticate { my ($auth_type, $r) = @_; my $debug = $r->dir_config("AuthCookieDebug") || 0; $r->server->log_error("auth_type " . $auth_type) if ($debug >= 3); unless ($r->is_initial_req) { if (defined $r->prev) { # we are in a subrequest. Just copy user from previous request. $r->user( $r->prev->user ); } return OK; } if ($debug >= 3) { $r->server->log_error("r=$r authtype=". $r->auth_type); } if ($r->auth_type ne $auth_type) { # This location requires authentication because we are being called, # but we don't handle this AuthType. $r->server->log_error("AuthType mismatch: $auth_type =/= ".$r->auth_type) if $debug >= 3; return DECLINED; } # Ok, the AuthType is $auth_type which we handle, what's the authentication # realm's name? my $auth_name = $r->auth_name; $r->server->log_error("auth_name $auth_name") if $debug >= 2; unless ($auth_name) { $r->server->log_error("AuthName not set, AuthType=$auth_type", $r->uri); return SERVER_ERROR; } # Get the Cookie header. If there is a session key for this realm, strip # off everything but the value of the cookie. my $ses_key_cookie = $auth_type->key($r) || ''; $r->server->log_error("ses_key_cookie " . $ses_key_cookie) if $debug >= 1; $r->server->log_error("uri " . $r->uri) if $debug >= 2; if ($ses_key_cookie) { my ($auth_user, @args) = $auth_type->authen_ses_key($r, $ses_key_cookie); if ($auth_user and scalar @args == 0) { # We have a valid session key, so we return with an OK value. # Tell the rest of Apache what the authentication method and # user is. $r->ap_auth_type($auth_type); $r->user($auth_user); $r->server->log_error("user authenticated as $auth_user") if $debug >= 1; # send new cookie if SessionTimeout is on if (my $expires = $r->dir_config("${auth_name}SessionTimeout")) { $auth_type->send_cookie($r, $ses_key_cookie, {expires => $expires}); } return OK; } elsif (scalar @args > 0 and $auth_type->can('custom_errors')) { return $auth_type->custom_errors($r, $auth_user, @args); } else { # There was a session key set, but it's invalid for some reason. So, # remove it from the client now so when the credential data is posted # we act just like it's a new session starting. $auth_type->remove_cookie($r); $r->subprocess_env('AuthCookieReason', 'bad_cookie'); } } else { $r->subprocess_env('AuthCookieReason', 'no_cookie'); } # This request is not authenticated, but tried to get a protected # document. Send client the authen form. return $auth_type->login_form($r); } sub login_form { my ($self, $r) = @_; my $auth_name = $r->auth_name; my %args = $self->_get_form_data($r); if ($r->method eq 'POST') { $self->_convert_to_get($r, \%args); } # There should be a PerlSetVar directive that gives us the URI of # the script to execute for the login form. my $authen_script; unless ($authen_script = $r->dir_config($auth_name . "LoginScript")) { $r->server->log_error("PerlSetVar '${auth_name}LoginScript' not set", $r->uri); return SERVER_ERROR; } $r->custom_response(HTTP_FORBIDDEN, $authen_script); return HTTP_FORBIDDEN; } sub satisfy_is_valid { my ($auth_type, $r, $satisfy) = @_; $satisfy = lc $satisfy; if ($satisfy eq 'any' or $satisfy eq 'all') { return 1; } else { my $auth_name = $r->auth_name; $r->server->log_error("PerlSetVar ${auth_name}Satisfy $satisfy invalid",$r->uri); return 0; } } sub get_satisfy { my ($auth_type, $r) = @_; my $auth_name = $r->auth_name; return lc $r->dir_config("${auth_name}Satisfy") || 'all'; } sub authorize { my ($auth_type, $r) = @_; my $debug = $r->dir_config("AuthCookieDebug") || 0; $r->server->log_error('authorize() for '.$r->uri()) if $debug >= 3; return OK unless $r->is_initial_req; #only the first internal request if ($r->auth_type ne $auth_type) { $r->server->log_error("auth type mismatch $auth_type != ".$r->auth_type) if $debug >= 3; return DECLINED; } my $reqs_arr = $r->requires or return DECLINED; my $user = $r->user; $r->server->log_error("authorize user=$user type=$auth_type") if $debug >=3; unless ($user) { # user is either undef or =0 which means the authentication failed $r->server->log_error("No user authenticated", $r->uri); return HTTP_FORBIDDEN; } my $satisfy = $auth_type->get_satisfy($r); return SERVER_ERROR unless $auth_type->satisfy_is_valid($r,$satisfy); my $satisfy_all = $satisfy eq 'all'; my ($forbidden); foreach my $req (@$reqs_arr) { my ($requirement, $args) = split /\s+/, $req->{requirement}, 2; $args = '' unless defined $args; $r->server->log_error("requirement := $requirement, $args") if $debug >= 2; if (lc($requirement) eq 'valid-user') { if ($satisfy_all) { next; } else { return OK; } } if ($requirement eq 'user') { if ($args =~ m/\b$user\b/) { next if $satisfy_all; return OK; # satisfy any } $forbidden = 1; next; } # Call a custom method my $ret_val = $auth_type->$requirement($r, $args); $r->server->log_error("$auth_type->$requirement returned $ret_val") if $debug >= 3; if ($ret_val == OK) { next if $satisfy_all; return OK; # satisfy any } # Nothing succeeded, deny access to this user. $forbidden = 1; } return $forbidden ? HTTP_FORBIDDEN : OK; } sub send_cookie { my ($self, $r, $ses_key, $cookie_args) = @_; $cookie_args = {} unless defined $cookie_args; my $cookie_name = $self->cookie_name($r); my $cookie = $self->cookie_string( request => $r, key => $cookie_name, value => $ses_key, %$cookie_args ); my $auth_name = $r->auth_name; # add a P3P header if user has configured it. if (my $p3p = $r->dir_config("${auth_name}P3P")) { $r->err_headers_out->set(P3P => $p3p); } $r->err_headers_out->add("Set-Cookie" => $cookie); } # cookie_string takes named parameters: # request # key # value # expires # sub cookie_string { my $self = shift; my %p = @_; for (qw/request key/) { croak "missing required parameter $_" unless defined $p{$_}; } # its okay if value is undef here. my $r = $p{request}; $p{value} = '' unless defined $p{value}; my $string = sprintf '%s=%s', @p{'key','value'}; my $auth_name = $r->auth_name; if (my $expires = $p{expires} || $r->dir_config("${auth_name}Expires")) { $expires = EBox::ThirdParty::Apache2::AuthCookie::Util::expires($expires); $string .= "; expires=$expires"; } $string .= '; path=' . ( $self->get_cookie_path($r) || '/' ); if (my $domain = $r->dir_config("${auth_name}Domain")) { $string .= "; domain=$domain"; } if ($r->dir_config("${auth_name}Secure")) { $string .= '; secure'; } # HttpOnly is an MS extension. See # http://msdn.microsoft.com/workshop/author/dhtml/httponly_cookies.asp if ($r->dir_config("${auth_name}HttpOnly")) { $string .= '; HttpOnly'; } return $string; } sub key { my ($self, $r) = @_; my $cookie_name = $self->cookie_name($r); my $allcook = ($r->headers_in->get("Cookie") || ""); return ($allcook =~ /(?:^|\s)$cookie_name=([^;]*)/)[0]; } sub get_cookie_path { my ($self, $r) = @_; my $auth_name = $r->auth_name; return $r->dir_config("${auth_name}Path"); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Html.pm0000664000000000000000000001035512017102272015511 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Html; use strict; use warnings; use EBox::Global; use EBox::Config; use EBox::Gettext; use EBox::Menu::Root; use HTML::Mason; # # Method: title # # Returns the html code for the title # # Returns: # # string - containg the html code for the title # sub title { my $save = __('Save changes'); my $logout = __('Logout'); my $global = EBox::Global->getInstance(); my $finishClass; if ($global->unsaved()) { $finishClass = "changed"; } else { $finishClass = "notchanged"; } # Display control panel button only if the eBox is subscribed my $remoteServicesURL = ''; if ($global->modExists('remoteservices')) { my $remoteServicesMod = $global->modInstance('remoteservices'); if ($remoteServicesMod->eBoxSubscribed()) { unless (EBox::Config::configkey('hide_cloud_link')) { $remoteServicesURL = $remoteServicesMod->controlPanelURL(); } } } my $image_title = $global->theme()->{'image_title'}; my $html = makeHtml('headTitle.mas', save => $save, logout => $logout, finishClass => $finishClass, remoteServicesURL => $remoteServicesURL, image_title => $image_title, ); return $html; } # # Method: titleNoAction # # Returns the html code for the title without action buttons # # Returns: # # string - containg the html code for the title # sub titleNoAction { my $global = EBox::Global->getInstance(); my $image_title = $global->theme()->{'image_title'}; my $html = makeHtml('headTitle.mas', image_title => $image_title, ); return $html; } # # Method: menu # # Returns the html code for the menu # # Returns: # # string - containg the html code for the menu # sub menu { my $current = shift; my $global = EBox::Global->getInstance(); my $root = new EBox::Menu::Root('current' => $current); foreach (@{$global->modNames}) { my $mod = $global->modInstance($_); $mod->menu($root); } return $root->html; } # # Method: footer # # Returns the html code for the footer page # # Returns: # # string - containg the html code for the footer page # sub footer { my $global = EBox::Global->getInstance(); my $copyright = $global->theme()->{'copyright_footer'}; my $html = makeHtml('footer.mas', 'copyright_footer' => $copyright); return $html; } # # Method: header # # Returns the html code for the header page # # Returns: # # string - containg the html code for the header page # sub header # (title) { my ($title) = @_; my $serverName = __('Zentyal'); my $global = EBox::Global->getInstance(); if ( $global->modExists('remoteservices') ) { my $remoteServicesMod = $global->modInstance('remoteservices'); if ( $remoteServicesMod->eBoxSubscribed() ) { $serverName = $remoteServicesMod->eBoxCommonName(); } } $title = "$serverName - $title"; my $favicon = $global->theme()->{'favicon'}; my $html = makeHtml('header.mas', title => $title, favicon => $favicon ); return $html; } sub makeHtml { my ($filename, @params) = @_; my $filePath = EBox::Config::templates . "/$filename"; my $output; my $interp = HTML::Mason::Interp->new(comp_root => EBox::Config::templates, out_method => \$output,); my $comp = $interp->make_component(comp_file => $filePath); $interp->exec($comp, @params); return $output; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Dashboard/0000775000000000000000000000000012017102272016132 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Dashboard/Graph.pm0000664000000000000000000000252112017102272017531 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Dashboard::Graph; use strict; use warnings; use base 'EBox::Dashboard::Item'; use EBox::Gettext; use EBox; sub new { my $class = shift; my $self = $class->SUPER::new(); $self->{title} = shift; $self->{name} = shift; $self->{value} = shift; $self->{size} = shift; $self->{novalues} = 20; $self->{type} = 'graph'; if(defined($self->{size}) and ($self->{size} eq 'small')) { $self->{width} = 175; $self->{height} = 100; } else { $self->{width} = 350; $self->{height} = 200; } bless($self, $class); return $self; } sub HTMLViewer() { return '/dashboard/graph.mas'; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Dashboard/GraphRow.pm0000664000000000000000000000275012017102272020225 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Dashboard::GraphRow; use strict; use warnings; use base 'EBox::Dashboard::Item'; use EBox::Gettext; sub new # (name, value) { my $class = shift; my $self = $class->SUPER::new(); $self->{type} = 'graphrow'; bless($self, $class); return $self; } sub add # (graph) { my $self = shift; my $graph = shift; $graph->isa('EBox::Dashboard::Graph') or throw EBox::Exceptions::Internal( "Tried to add a non-graph to an EBox::Dashboard::GraphRow"); $graph->{width} = 175; $graph->{height} = 100; push(@{$self->graphs()}, $graph); } sub graphs # () { my ($self) = @_; unless (defined($self->{graphs})) { my @array = (); $self->{graphs} = \@array; } return $self->{graphs}; } sub HTMLViewer() { return '/dashboard/graphrow.mas'; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Dashboard/CounterGraph.pm0000664000000000000000000000433112017102272021072 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Dashboard::CounterGraph; use strict; use warnings; use base 'EBox::Dashboard::Graph'; use EBox::Gettext; use EBox; sub new { my $class = shift; my $self = $class->SUPER::new(); $self->{title} = shift; $self->{name} = shift; $self->{value} = shift; $self->{size} = shift; $self->{type} = 'graph'; if($self->{size} eq 'small') { $self->{width} = 175; $self->{height} = 100; } else { $self->{width} = 350; $self->{height} = 200; } bless($self, $class); $self->update(); return $self; } sub update { my ($self) = @_; my $counterdir = EBox::Config::tmp() . '/counter/'; if(! -d $counterdir) { mkdir($counterdir); } my $counterfile = $counterdir . '/' . $self->{name} . '.counter'; my $date = time(); my $startdate = $date - 120; my $secs; my $value = 0; if(-f $counterfile) { my $olddate = (stat $counterfile)[9]; if($olddate < $startdate) { unlink($counterfile); } else { $secs = $date - $olddate; open(COUNTERFILE, "< $counterfile"); my $oldcounter = ; close COUNTERFILE; chomp($oldcounter); if($secs != 0) { $value = ($self->{value}-$oldcounter)/$secs; } } } use EBox; open(COUNTERFILE, "> $counterfile"); print COUNTERFILE $self->{value}; close COUNTERFILE; $self->{value} = $value; } sub HTMLViewer() { return '/dashboard/graph.mas'; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Dashboard/Widget.pm0000664000000000000000000000262312017102272017716 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Dashboard::Widget; use strict; use warnings; use EBox::Gettext; sub new # (title?) { my ($class, $title, $module, $name) = @_; my $self = {}; $self->{title} = $title; $self->{module} = $module; $self->{name} = $name; $self->{size} = 1; bless($self, $class); return $self; } sub add # (section) { my ($self,$section) = @_; $section->isa('EBox::Dashboard::Section') or throw EBox::Exceptions::Internal( "Tried to add a non-item to an EBox::Dashboard::Section"); push(@{$self->sections()}, $section); } sub sections { my ($self) = @_; unless (defined($self->{sections})) { my @array = (); $self->{sections} = \@array; } return $self->{sections}; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Dashboard/Section.pm0000664000000000000000000000272012017102272020075 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Dashboard::Section; use strict; use warnings; use EBox::Gettext; use EBox::Exceptions::Internal; sub new # (title?) { my $class = shift; my $self = {}; $self->{name} = shift; $self->{title} = shift; if(not defined($self->{name})) { throw EBox::Exceptions::Internal('Section must have a name'); } bless($self, $class); return $self; } sub add # (item) { my $self = shift; my $item = shift; $item->isa('EBox::Dashboard::Item') or throw EBox::Exceptions::Internal( "Tried to add a non-item to an EBox::Dashboard::Section"); push(@{$self->items()}, $item); } sub items { my ($self) = @_; unless (defined($self->{items})) { my @array = (); $self->{items} = \@array; } return $self->{items}; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Dashboard/HTML.pm0000664000000000000000000000175112017102272017240 0ustar # Copyright (C) 2009-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Dashboard::HTML; use strict; use warnings; use base 'EBox::Dashboard::Item'; use EBox::Gettext; sub new # (html) { my $class = shift; my $self = $class->SUPER::new(); $self->{html} = shift; bless($self, $class); return $self; } sub HTMLViewer() { return '/dashboard/html.mas'; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Dashboard/ModuleStatus.pm0000664000000000000000000000316012017102272021121 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Dashboard::ModuleStatus; use strict; use warnings; use base 'EBox::Dashboard::Item'; use EBox::Gettext; # Constructor: new # # Create a Dashboard Module status item # # Named parameters: # # module - String the module to print # # printableName - String the module printable name # # enabled - Boolean indicating whether the module is enabled or # not # # running - Boolean indicating whether the service module is # running or not # # nobutton - Boolean indicating if the button is not necessary # # statusStr - String you may override the status displayed screen # sub new # (key, prettykey, value) { my ($class, %params) = @_; my $self = $class->SUPER::new(); while(my ($key, $value) = each(%params)) { $self->{$key} = $value; } $self->{'type'} = 'status'; bless($self, $class); return $self; } sub HTMLViewer() { return '/dashboard/status.mas'; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Dashboard/Item.pm0000664000000000000000000000165712017102272017377 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Dashboard::Item; use strict; use warnings; use EBox::Exceptions::Internal; use EBox::Gettext; sub new { my $class = shift; my $self = {}; bless($self, $class); return $self; } sub HTMLViewer() { #shouldn't ever be called } 1; zentyal-core-2.3.21+quantal1/src/EBox/Dashboard/List.pm0000664000000000000000000000217612017102272017411 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Dashboard::List; use strict; use warnings; use base 'EBox::Dashboard::Item'; use EBox::Gettext; sub new # (title, colTitles, rows) { my $class = shift; my $self = $class->SUPER::new(); $self->{title} = shift; $self->{colTitles} = shift; $self->{ids} = shift; $self->{rows} = shift; $self->{none_text} = shift; $self->{type} = 'list'; bless($self, $class); return $self; } sub HTMLViewer() { return '/dashboard/list.mas'; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Dashboard/Value.pm0000664000000000000000000000314312017102272017545 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Dashboard::Value; use strict; use warnings; use base 'EBox::Dashboard::Item'; use EBox::Gettext; # Constructor: new # # Create a dashboard key:value # # Parameters: # # name - String the key name # value - String the value related to that key # # value_type - String the value type to display in the UI accordingly, # it could one of the following values: good, info, warning, error # *(Optional)* Default value: info # # Returns: # # - the newly created object instance # sub new # (key, prettykey, value) { my $class = shift; my $self = $class->SUPER::new(); $self->{key} = shift; $self->{value} = shift; $self->{value_type} = shift; $self->{value_type} = 'info' unless defined($self->{value_type}); $self->{type} = 'value'; bless($self, $class); return $self; } sub HTMLViewer() { return '/dashboard/value.mas'; } 1; zentyal-core-2.3.21+quantal1/src/EBox/SOAPClient.pm0000664000000000000000000001505112017102272016504 0ustar # Copyright (C) 2009-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::SOAPClient; # Class: EBox::SOAPClient # # This package is used as a wrapper to package to # easily manage the SOAP connections using SSL # use warnings; use strict; use EBox; use EBox::Exceptions::Internal; use EBox::Exceptions::MissingArgument; use EBox::Exceptions::Protocol; use Error qw(:try); use SOAP::Lite; use IO::Socket::SSL; # Group: Public functions # Function: instance # # Wrapper constructor for # # Parameters: # # name - String the service to ask for # # proxy - String the URL to where ask for the name web service # # certs - hash ref containing the following file paths: # cert - String the certificate from the client # key - String the private key from the client # ca - String the Certification Authority (optional) # # *(Optional)* If not given, the connection is done without # credentials # # Example: # # new(name => 'urn:EBox/Services/CA', # proxy => 'https://ca.internal.ebox-services.com/soap', # certs => { cert => 'cert.pem', # private => 'privKey.pem', # ca => 'cacert.pem'}); # # Returns: # # - the instanced object which has established the # connection # # Exceptions: # # - thrown if any compulsory # argument is missing # sub instance { my ($class, %params) = @_; unless ( defined ( $params{name} )) { throw EBox::Exceptions::MissingArgument('name'); } unless ( defined ( $params{proxy} )) { throw EBox::Exceptions::MissingArgument('proxy'); } if ( $params{proxy} =~ m/^https/ and not defined( $params{certs} )) { EBox::warn('Doing connection to web service: ' . "$params{name} without credentials to $params{proxy}"); } # Force use of IO::Socket::SSL $Net::HTTPS::SSL_SOCKET_CLASS = "IO::Socket::SSL"; # Do not verify hostname $ENV{PERL_LWP_SSL_VERIFY_HOSTNAME} = 0; my $soapConn = new SOAP::Lite( uri => $params{name}, proxy => $params{proxy}, on_fault => sub { my ($soap, $res) = @_; if ( ref $res ) { # Get the exception type use Data::Dumper; # EBox::debug($soap->fault()); # EBox::debug($res); # my $excType = (keys %{$res->faultdetail()})[0]; # # Get the hash to bless # my $hash_ref = $res->faultdetail()->{$excType}; # # Substitution from __ to :: # $excType =~ s/__/::/g; # # Do the blessing to have the exception object # bless ( $hash_ref, $excType ); # throw $hash_ref; } else { throw EBox::Exceptions::Protocol($soap->transport()->status(), ''); }} ); if ( defined ( $params{certs} )) { $class->_setCerts($params{certs}); } my $self = { soapConn => $soapConn }; bless ( $self, $class ); return $self; } sub DESTROY { ; }; # Method: AUTOLOAD # # Launch every autoload method with the SOAP::Lite object to catch # the exceptions to launch them # # Returns: # # returnedValue - the returned value given by the external code # server # sub AUTOLOAD { my ($self, @params) = @_; my $methodName = our $AUTOLOAD; $methodName =~ s/.*:://; if ( $methodName =~ m/^_/ ) { throw EBox::Exceptions::Internal('Cannot call a private method'); } # Transform every given param into a SOAP::Data if it is not yet. # This assumes all parameters are named my @soapParams = (); my $even = @params % 2 == 0; for(my $i = 0; $i < @params; $i++) { my $param = $params[$i]; if (defined(ref($param)) and ref($param) eq 'SOAP::Data') { push(@soapParams, $param); } else { # This won't work with the special 2 parameters case if ( $even ) { push(@soapParams, SOAP::Data->name($param, $params[$i+1])); $i++; } else { push(@soapParams, SOAP::Data->value($param)); } } } my $response = $self->{soapConn}->call($methodName => @soapParams); if ( $response->fault() ) { if ( defined ( $response->faultdetail() )) { # Get the exception type with the hash ref from the first # element in hash (An element is expected) my ($excType, $excObj) = (each %{$response->faultdetail()}); # Subtitute from __ to :: $excType =~ s/__/::/g; # Do the blessing to throw the exception bless ( $excObj, $excType ); throw $excObj; } if ( $response->faultcode() eq 'soap:Server' ) { throw EBox::Exceptions::Internal('Server side: ' . $response->faultstring()); } elsif ( $response->faultcode() eq 'soap:Client' ) { throw EBox::Exceptions::Internal('Client side: ' . $response->faultstring()); } } return $response->paramsall(); } # Group: Private functions # Set the certificates enviroment variables to establish SSL # connection sub _setCerts { my ($class, $certs) = @_; $ENV{HTTPS_CERT_FILE} = $certs->{cert}; $ENV{HTTPS_KEY_FILE} = $certs->{private}; $IO::Socket::SSL::GLOBAL_CONTEXT_ARGS->{SSL_cert_file} = $ENV{HTTPS_CERT_FILE}; $IO::Socket::SSL::GLOBAL_CONTEXT_ARGS->{SSL_key_file} = $ENV{HTTPS_KEY_FILE}; $IO::Socket::SSL::GLOBAL_CONTEXT_ARGS->{SSL_use_cert} = 1; if (defined($certs->{ca})) { $ENV{HTTPS_CA_FILE} = $certs->{ca}; $IO::Socket::SSL::GLOBAL_CONTEXT_ARGS->{SSL_ca_file} = $certs->{ca}; $IO::Socket::SSL::GLOBAL_CONTEXT_ARGS->{SSL_verify_mode} = 0x01; # verify peer } $ENV{HTTPS_VERSION} = '3'; } 1; zentyal-core-2.3.21+quantal1/src/EBox/GlobalImpl.pm0000664000000000000000000007336612017102272016642 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::GlobalImpl; use strict; use warnings; use base qw(EBox::Module::Config Apache::Singleton::Process); use EBox; use EBox::Exceptions::Command; use EBox::Exceptions::InvalidData; use EBox::Exceptions::DataNotFound; use EBox::Exceptions::Internal; use EBox::Exceptions::MissingArgument; use EBox::Exceptions::DataExists; use Error qw(:try); use EBox::Config; use EBox::Gettext; use EBox::ProgressIndicator; use EBox::Sudo; use EBox::Validate qw( :all ); use File::Basename; use File::Glob; use YAML::XS; use Log::Log4perl; use POSIX qw(setuid setgid setlocale LC_ALL); use Perl6::Junction qw(any all); use EBox::Util::GPG; use Digest::MD5; use AptPkg::Cache; use File::stat; # Constants use constant { PRESAVE_SUBDIR => EBox::Config::etc() . 'pre-save', POSTSAVE_SUBDIR => EBox::Config::etc() . 'post-save', TIMESTAMP_KEY => 'saved_timestamp', FIRST_FILE => '/var/lib/zentyal/.first', DPKG_RUNNING_FILE => '/var/lib/zentyal/dpkg_running', }; use constant CORE_MODULES => qw(sysinfo apache events global logs audit); my $lastDpkgStatusMtime = undef; my $_cache = undef; my $_brokenPackages = {}; my $_installedPackages = {}; #redefine inherited method to create own constructor #for Singleton pattern sub _new_instance { my $class = shift; my $self = $class->SUPER::_create(name => 'global', printableName => 'global', @_); bless($self, $class); $self->{'mod_instances_rw'} = {}; $self->{'mod_instances_ro'} = {}; $self->{'mod_info'} = {}; # Messages produced during save changes process $self->{save_messages} = []; return $self; } #Method: readModInfo # # Static method which returns the information found in the module's yaml file # sub readModInfo # (module) { my ($self, $name) = @_; unless ($self->{mod_info}->{$name}) { my $yaml; try { ($yaml) = YAML::XS::LoadFile(EBox::Config::modules() . "$name.yaml"); } otherwise { $yaml = undef; }; $self->{mod_info}->{name} = $yaml; } return $self->{mod_info}->{name}; } #Method: theme # # Returns the information found in custom.theme if exists # exists or default.theme if not. # sub theme { my ($self) = @_; unless (defined $self->{theme}) { $self->{theme} = _readTheme(); } return $self->{theme}; } sub _readTheme { my $path = EBox::Config::share() . 'zentyal/www'; my $theme = "$path/default.theme"; my $custom = "$path/custom.theme"; if (-f $custom) { # Check theme's signature if (EBox::Util::GPG::checkSignature($custom)) { $theme = $custom; EBox::info('Using custom default.theme'); } else { EBox::warn('Invalid signature in custom.theme, fallbacking to default.theme'); } } my ($yaml) = YAML::XS::LoadFile($theme); return $yaml; } sub _className { my ($self, $name) = @_; my $info = $self->readModInfo($name); defined($info) or return undef; return $info->{'class'}; } # Method: modExists # # Check if a module exists # # Parameters: # # module - module's name to check # # Returns: # # boolean - True if the module exists, otherwise false # sub modExists { my ($self, $name) = @_; # is dpkg command running? my $DPKG_RUNNING = 0; if (-f DPKG_RUNNING_FILE) { $DPKG_RUNNING = 1 ; } unless ($DPKG_RUNNING) { if ($ENV{DPKG_RUNNING_VERSION}) { EBox::Sudo::command('touch ' . DPKG_RUNNING_FILE); $DPKG_RUNNING = 1; } } # Check if module package is properly installed # # No need to check core modules because if # zentyal-core package is not properly installed # nothing of this is going to work at all. # if ($name eq any((CORE_MODULES))) { return 1; } elsif ($DPKG_RUNNING) { return defined($self->_className($name)); } else { return _packageInstalled("zentyal-$name"); } } # Method: modEnabled # # Check if a module exists and it's enabled # # Parameters: # # module - module's name to check # # Returns: # # boolean - True if the module is enabled, otherwise false # sub modEnabled { my ($self, $ro, $name) = @_; unless ($self->modExists($name)) { return 0; } my $mod = $self->modInstance($ro, $name); return $mod->isEnabled(); } # Method: modIsChanged # # Check if the module config has changed # # GlobalImpl module is considered always unchanged # # Parameters: # # module - module's name to check # # Returns: # # boolean - True if the module config has changed , otherwise false # sub modIsChanged { my ($self, $name) = @_; defined($name) or return undef; ($name ne 'global') or return undef; $self->modExists($name) or return undef; my $info = $self->readModInfo($name); return $self->get_bool("modules/$name/changed"); } # Method: modChange # # Set a module as changed # # GlobalImpl cannot be marked as changed and such request will be ignored # # Parameters: # # module - module's name to set # sub modChange { my ($self, $ro, $name) = @_; defined($name) or return; ($name ne 'global') or return; return if $self->modIsChanged($name); if ($ro) { throw EBox::Exceptions::Internal("Cannot mark as changed a readonly instance of $name"); } my $mod = $self->modInstance($ro, $name); defined($mod) or throw EBox::Exceptions::Internal("Module $name does not exist"); # Set without mark as changed using _set() instead of set() $self->_set("modules/$name/changed", 1); } # Method: modRestarted # # Sets a module as restarted # # Parameters: # # module - module's name to set # sub modRestarted { my ($self, $name) = @_; defined($name) or return; ($name ne 'global') or return; $self->modExists($name) or return; # Set without mark as changed using _set() instead of set() $self->_set("modules/$name/changed", 0); } # Method: modNames # # Return an array containing all module names # # Returns: # # array ref - each element contains the module's name # sub modNames { my ($self) = @_; my $log = EBox::logger(); my @allmods = (); foreach (('sysinfo', 'network', 'firewall')) { if ($self->modExists($_)) { push(@allmods, $_); } } my @files = glob(EBox::Config::modules() . '*.yaml'); my @mods = map { basename($_) =~ m/(.*)\.yaml/ ; $1 } @files; foreach my $mod (@mods) { next unless ($self->modExists($mod)); next if (grep(/^$mod$/, @allmods)); my $class = $self->_className($mod); if(defined($class)) { push(@allmods, $mod); } } return \@allmods; } # Method: unsaved # # Tell you if there is at least one unsaved module # # Returns: # # boolean - indicating if at least a module has unsaved changes # sub unsaved { my $self = shift; my @names = @{$self->modNames()}; foreach (@names) { $self->modIsChanged($_) or next; return 1; } return undef; } sub prepareRevokeAllModules { my ($self) = @_; my $totalTicks = grep { $self->modIsChanged($_); } @{$self->modNames}; return $self->_prepareActionScript('revokeAllModules', $totalTicks); } # Method: revokeAllModules # # Revoke the changes made in the configuration for all the modules # sub revokeAllModules { my ($self, %options) = @_; my $ro = 0; my $progress = $options{progress}; my @names = @{$self->modNames}; my $failed = ""; foreach my $name (@names) { $self->modIsChanged($name) or next; if ($progress) { $progress->setMessage($name); $progress->notifyTick(); } my $mod = $self->modInstance($ro, $name); try { $mod->revokeConfig; } catch EBox::Exceptions::Internal with { $failed .= "$name "; }; } if (not $failed) { $progress->setAsFinished() if $progress; return; } my $errorText = "The following modules failed while ". "revoking their changes, their state is unknown: $failed"; $progress->setAsFinished(1, $errorText) if $progress; throw EBox::Exceptions::Internal($errorText); } # Method: modifiedModules # # Return the list of modified modules sorted by from parameter # # Parameters: # # from - String the result is sorted depending on this parameter: # 'enable' - the sort is done by enableDepends attribute # 'save' - the sort is done by depends attribute # # Returns: # # array ref - containing the list of modified module names # sub modifiedModules { my ($self, $from) = @_; defined($from) or throw EBox::Exceptions::MissingArgument('from'); my $ro = 0; my @names = @{$self->modNames}; my @mods; if ($self->modExists('firewall')) { push(@mods, 'firewall'); } foreach my $modname (@names) { $self->modIsChanged($modname) or next; unless (grep(/^$modname$/, @mods)) { push(@mods, $modname); } my @deps = @{$self->modRevDepends($ro, $modname)}; foreach my $aux (@deps) { unless (grep(/^$aux$/, @mods)) { push(@mods, $aux); } } } @mods = map { __PACKAGE__->modInstance($ro, $_) } @mods; my $sorted; if ( $from eq 'enable' ) { $sorted = sortModulesEnableModDepends(\@mods); } else { $sorted = __PACKAGE__->sortModulesByDependencies(\@mods, 'depends'); } my @sorted = map { $_->name() } @{$sorted}; return \@sorted; } sub sortModulesEnableModDepends { my ($mods) = @_; return __PACKAGE__->sortModulesByDependencies( $mods, 'enableModDepends' ); } sub prepareSaveAllModules { my ($self) = @_; my $totalTicks; if ($self->first()) { # enable + save modules $totalTicks = scalar @{$self->modNames} * 2; } else { # save changed modules $totalTicks = scalar @{$self->modifiedModules('save')}; } $totalTicks += $self->_nScripts(PRESAVE_SUBDIR, POSTSAVE_SUBDIR); return $self->_prepareActionScript('saveAllModules', $totalTicks); } sub packageCache { my $status = stat('/var/lib/dpkg/status'); my $currentMtime = $status->mtime(); if (defined ($lastDpkgStatusMtime)) { # Regenerate cache only if status file has changed if ($currentMtime != $lastDpkgStatusMtime) { $_cache = new AptPkg::Cache; $_brokenPackages = {}; } } else { $_cache = new AptPkg::Cache; } $lastDpkgStatusMtime = $currentMtime; unless (defined $_cache) { throw EBox::Exceptions::External( __("Cannot create software packages cache. Make sure that your sources and preferences files in /etc/apt are readable and retry") ); } return $_cache; } sub brokenPackages { my @names = keys %{$_brokenPackages}; return \@names; } sub _prepareActionScript { my ($self, $action, $totalTicks) = @_; my $script = EBox::Config::scripts() . 'global-action'; $script .= " --action $action"; my $progressIndicator = EBox::ProgressIndicator->create( executable => $script, totalTicks => $totalTicks, ); $progressIndicator->runExecutable(); return $progressIndicator; } # Method: saveAllModules # # Save changes in all modules # sub saveAllModules { my ($self, %options) = @_; my $ro = 0; my $log = EBox::logger(); my $failed = ''; # Reset save messages array $self->{save_messages} = []; my $progress = $options{progress}; my @mods = @{$self->modifiedModules('save')}; my $modNames = join (' ', @mods); # TODO: tell events module to stop its watchers $self->_runExecFromDir(PRESAVE_SUBDIR, $progress, $modNames); my $msg = "Saving config and restarting services: @mods"; $log->info($msg); # First installation modules enable if ($self->first()) { my $mgr = EBox::ServiceManager->new(); @mods = @{$mgr->_dependencyTree()}; $modNames = join(' ', @mods); foreach my $name (@mods) { if ($progress) { $progress->setMessage(__x("Enabling {modName} module", modName => $name)); $progress->notifyTick(); } next if ($name eq 'dhcp'); # Skip dhcp module my $module = EBox::GlobalImpl->modInstance($ro, $name); # Do not enable this module if dependencies were not enabled my $enable = 1; foreach my $dep (@{$module->enableModDepends()}) { unless (EBox::Global->modEnabled($dep)) { $enable = 0; } } next unless ($enable); $module->setInstalled(); try { $module->enableActions(); $module->setConfigured(1); $module->enableService(1); } otherwise { my ($ex) = @_; my $err = $ex->text(); $module->setConfigured(0); $module->enableService(0); EBox::debug("Failed to enable module $name: $err"); }; } } my $apache = 0; foreach my $name (@mods) { if ($name eq 'apache') { $apache = 1; next; } if ($progress) { $progress->setMessage(__x("Saving {modName} module", modName => $name)); $progress->notifyTick(); } my $mod = EBox::GlobalImpl->modInstance($ro, $name); my $class = 'EBox::Module::Service'; if ($mod->isa($class)) { $mod->setInstalled(); if (not $mod->configured()) { $mod->_saveConfig(); $self->modRestarted($name); next; } } try { $mod->save(); } catch EBox::Exceptions::External with { my $ex = shift; $ex->throw(); } otherwise { my $ex = shift; EBox::error("Failed to save changes in module $name: $ex"); $failed .= "$name "; }; } # Delete first time installation file (wizard) $self->deleteFirst(); # FIXME - tell the CGI to inform the user that apache is restarting if ($apache) { if ($progress) { $progress->setMessage(__x("Saving {modName} module", modName => 'apache')); $progress->notifyTick(); } my $mod = EBox::GlobalImpl->modInstance($ro, 'apache'); try { $mod->save(); } catch EBox::Exceptions::External with { my $ex = shift; $ex->throw(); } otherwise { my $ex = shift; EBox::error("Failed to save changes in module apache: $ex"); $failed .= "apache "; }; } # TODO: tell events module to resume its watchers if (not $failed) { $self->_runExecFromDir(POSTSAVE_SUBDIR, $progress, $modNames); # Store a timestamp with the time of the ending $self->st_set_int(TIMESTAMP_KEY, time()); my @messages = @{$self->saveMessages()}; my $message; if (@messages) { $message = '
  • ' . join("
  • ", @messages) . '
'; my $logWarning = "Changes saved with some warnings:\n\t"; $logWarning .= join ("\n\t", @messages); EBox::warn($logWarning); } else { EBox::info('Changes saved successfully'); } $progress->setAsFinished(0, $message) if $progress; return; } my $errorText = "The following modules failed while ". "saving their changes, their state is unknown: $failed"; $progress->setAsFinished(1, $errorText) if $progress; throw EBox::Exceptions::Internal($errorText); } # Method: restartAllModules # # Force a restart for all the modules # sub restartAllModules { my $self = shift; my $ro = 1; my @names = @{$self->modNames}; my $log = EBox::logger(); my $failed = ""; $log->info("Restarting all modules"); unless ($self->isReadOnly) { $self->{'mod_instances_rw'} = {}; } foreach my $name (@names) { my $mod = EBox::GlobalImpl->modInstance($ro, $name); try { $mod->restartService(); } catch EBox::Exceptions::Internal with { $failed .= "$name "; }; } if ($failed eq "") { return; } throw EBox::Exceptions::Internal("The following modules failed while ". "being restarted, their state is unknown: $failed"); } # Method: stopAllModules # # Stops all the modules # sub stopAllModules { my $self = shift; my @names = @{$self->modNames}; my $log = EBox::logger(); my $failed = ""; $log->info("Stopping all modules"); my $ro = 1; unless ($self->isReadOnly) { $self->{'mod_instances_rw'} = {}; } foreach my $name (@names) { my $mod = EBox::GlobalImpl->modInstance($ro, $name); try { $mod->stopService(); } catch EBox::Exceptions::Internal with { $failed .= "$name "; }; } if ($failed eq "") { return; } throw EBox::Exceptions::Internal("The following modules failed while ". "stopping, their state is unknown: $failed"); } # Method: modInstances # # Return an array ref with an instance of every module # # Returns: # # array ref - the elements contains the instance of modules # sub modInstances { my ($self, $ro) = @_; $self = EBox::GlobalImpl->instance(); my @names = @{$self->modNames}; my @array = (); foreach my $name (@names) { my $mod = $self->modInstance($ro, $name); push(@array, $mod); } return \@array; } # Method: modInstancesOfType # # Return an array ref with an instance of every module that extends # a given classname # # Parameters: # # classname - the class base you are interested in # # Returns: # # array ref - the elments contains the instance of the modules # extending the classname # sub modInstancesOfType { my ($self, $ro, $classname) = @_; $self = EBox::GlobalImpl->instance(); my @names = @{$self->modNames}; my @array = (); foreach my $name (@names) { my $mod = $self->modInstance($ro, $name); if ($mod->isa($classname)) { push(@array, $mod); } } return \@array; } # Method: modInstance # # Build an instance of a module. Can be called as a class method or as an # object method. # # Parameters: # # module - module name # # Returns: # # If everything goes ok: # # - An instance of the requested module # # Otherwise # # undef # sub modInstance { my ($self, $ro, $name) = @_; if (not $name) { throw EBox::Exceptions::MissingArgument(q{module's name}); } my $global = EBox::GlobalImpl->instance(); if ($name eq 'global') { return $global; } my $instances = $ro ? $global->{'mod_instances_ro'} : $global->{'mod_instances_rw'}; my $modInstance = $instances->{$name}; if (defined($modInstance)) { return $modInstance; } $global->modExists($name) or return undef; my $classname = $global->_className($name); unless ($classname) { throw EBox::Exceptions::Internal("Module '$name' ". "declared, but it has no classname."); } eval "use $classname"; if ($@) { throw EBox::Exceptions::Internal("Error loading ". "class: $classname error: $@"); } $instances->{$name} = $classname->_create(ro => $ro); return $instances->{$name}; } # Method: logger # # Initialise Log4perl if necessary, returns the logger for the i # caller package # # Parameters: # # caller - # # Returns: # # If everything goes ok: # # - A instance of the requested module # # Otherwise # # undef sub logger # (caller?) { shift; EBox::deprecated(); return EBox::logger(shift); } # Method: modDepends # # Return an array ref with the names of the modules that the requested # module depends on # # Parameters: # # module - requested module # # Returns: # # undef - if the module does not exist # array ref - holding the names of the modules that the requested module # sub modDepends { my ($self, $ro, $name) = @_; $self->modExists($name) or return undef; my $mod = $self->modInstance($ro, $name); return $mod->depends(); } # Method: modRevDepends # # Return an array ref with the names of the modules which depend on a given # module # # Parameters: # # module - requested module # # Returns: # # undef - if the module does not exist # array ref - holding the names of the modules which depend on the # requested module # sub modRevDepends { my ($self, $ro, $name) = @_; $self->modExists($name) or return undef; my @revdeps = (); my @mods = @{$self->modNames}; foreach my $mod (@mods) { my @deps = @{$self->modDepends($ro, $mod)}; foreach my $dep (@deps) { defined($dep) or next; if ($name eq $dep) { push(@revdeps, $mod); last; } } } return \@revdeps; } # Name: sortModulesByDependencies # # Sort a list of modules objects by its dependencies. The dependencies are get # using a method that returns the names of the dependencies of each module. # # Parameters: # modules_r - reference to list of modules # dependenciesMethod - name of the method called in each module # to get its dependencies sub sortModulesByDependencies { my ($package, $modules_r, $dependenciesMethod) = @_; my @modules = @{ $modules_r }; my %availableModulesAndDependencies = map { $_->name() => undef; } @modules; my $i =0; while ($i < @modules) { my $mod = $modules[$i]; my $modName = $mod->name(); my @depends = (); if (defined $availableModulesAndDependencies{$modName}) { @depends = @{ $availableModulesAndDependencies{$modName} } } elsif ($mod->can($dependenciesMethod)) { @depends = @{ $mod->$dependenciesMethod() }; @depends = grep { exists $availableModulesAndDependencies{$_} } @depends; $availableModulesAndDependencies{$modName} = \@depends; } my $depOk = 1; foreach my $dependency (@depends) { my $depFound = 0; foreach my $j (0 .. $i) { if ($i == $j) { # for $i ==0 case last; } elsif ($modules[$j]->name() eq $dependency) { $depFound = 1; last; } } if (not $depFound) { $depOk = 0; last; } } if ($depOk) { $i += 1; } else { my $unreadyMod = splice @modules, $i, 1; push @modules, $unreadyMod; } } return \@modules; } # Method: lastModificationTime # # Return the latest modification time, this is the latest of # these events: # # - After finishing saving changes using call # - After a modification in LDAP in users module is present and at # least configured # # Returns: # # Int - the lastModificationTime # sub lastModificationTime { my ($self) = @_; my $lastStamp = $self->st_get_int(TIMESTAMP_KEY); $lastStamp = 0 unless defined($lastStamp); if ( $self->modExists('users') ) { my $usersMod = $self->modInstance('ro', 'users'); if ( $usersMod->configured() ) { my ($sec, $min, $hour, $mday, $mon, $year) = localtime($lastStamp); my $lastStampStr = sprintf('%04d%02d%02d%02d%02d%02dZ', ($year + 1900, $mon + 1, $mday, $hour, $min, $sec)); my $ldapStamp = $usersMod->ldap()->lastModificationTime($lastStampStr); if ( $ldapStamp > $lastStamp ) { $lastStamp = $ldapStamp; } } } return $lastStamp; } # Method: first # # Check if the file created on the first installation exists # # Returns: # # boolean - True if the file exists, false if not # sub first { return (-f FIRST_FILE); } # Method: deleteFirst # # Delete the file created on first installation, if exists # sub deleteFirst { if (-f FIRST_FILE) { unlink (FIRST_FILE); } } # Method: saveMessages # # Returns: # # Array ref - messages produced by modules during saveAllModules process # sub saveMessages { my ($self) = @_; return $self->{save_messages}; } # Method: addSaveMessage # # Parameters: # # String - message to add to saveMessages list # sub addSaveMessage { my ($self, $message) = @_; my $messages = $self->{save_messages}; push (@{$messages}, $message); } # Method: edition # # Returns: # # Subscription level as string. Current possible values: # # 'community', 'basic', 'sb', 'professional', 'enterprise' # sub edition { my ($self, $ro) = @_; if ($self->modExists('remoteservices')) { my $rs = $self->modInstance($ro, 'remoteservices'); my $codename = $rs->subscriptionCodename(); return $codename if ($codename); } return 'community'; } # Method: _runExecFromDir # # Run executables files from a directory using # . The execution will be done in lexical # order # # Parameters: # # dir - String the directory to search for executables # # progress - to indicate the user how # the actions are being performed # # modNames - string with the names of modified modules # # Exceptions: # # The ones launched by # sub _runExecFromDir { my ($self, $dirPath, $progress, $modNames) = @_; unless ( -e $dirPath ) { throw EBox::Exceptions::DataNotFound(data => 'directory', value => $dirPath); } opendir(my $dh, $dirPath); my @execs = (); while( my $file = readdir($dh) ) { next unless ( -f "${dirPath}/$file" or -l "${dirPath}/$file"); next unless ( -x "${dirPath}/$file" ); push(@execs, "${dirPath}/$file"); } closedir($dh); # Sorting lexically the scripts to execute @execs = sort(@execs); if ( @execs > 0 ) { EBox::info("Running executable files from $dirPath"); foreach my $exec (@execs) { try { EBox::info("Running $exec"); # Progress indicator stuff if ($progress) { $progress->setMessage(__x('running {scriptName} script', scriptName => scalar(File::Basename::fileparse($exec)))); $progress->notifyTick(); } my $output = EBox::Sudo::command("$exec $modNames"); if ( @{$output} > 0) { EBox::info("Output from $exec: @{$output}"); } } catch EBox::Exceptions::Command with { my ($exc) = @_; my $msg = "Command $exec failed its execution\n" . 'Output: ' . @{$exc->output()} . "\n" . 'Error: ' . @{$exc->error()} . "\n" . 'Return value: ' . $exc->exitValue(); EBox::error($msg); } otherwise { my ($exc) = @_; EBox::error("Error executing $exec: $exc"); }; } } } # Method: _nScripts # # Parameters: # # array - the dir path to count executable files # # Returns: # # Integer - number of executable scripts in pre/post dirs # sub _nScripts { my ($self, @dirPaths) = @_; my $nScripts = 0; foreach my $dirPath (@dirPaths) { opendir(my $dh, $dirPath); while( my $file = readdir($dh) ) { next unless ( -f "${dirPath}/$file" or -l "${dirPath}/$file"); next unless ( -x "${dirPath}/$file" ); $nScripts++; } closedir($dh); } return $nScripts; } sub _packageInstalled { my ($name) = @_; if (exists $_installedPackages->{$name}) { return 1; } my $cache = packageCache(); my $installed = 0; if ($cache->exists($name)) { my $pkg = $cache->get($name); if ($pkg->{SelectedState} == AptPkg::State::Install) { $installed = ($pkg->{InstState} == AptPkg::State::Ok and $pkg->{CurrentState} == AptPkg::State::Installed); if ($installed) { $_installedPackages->{$name} = 1; } else { $_brokenPackages->{$name} = 1; } } } return $installed; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Backup/0000775000000000000000000000000012017102272015450 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Backup/Test.pm0000664000000000000000000005750612017102272016742 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Backup::Test; use strict; use warnings; use lib '../..'; use base 'EBox::Test::Class'; use EBox::Backup; use Test::MockObject; use Test::More; use Test::Exception; use Test::Differences; use Test::File; use EBox::Test qw(checkModuleInstantiation); use EBox::TestStubs qw(fakeEBoxModule); use EBox::Gettext; use File::Slurp qw(read_file write_file); use EBox::FileSystem qw(makePrivateDir); use Perl6::Junction qw(all); use Readonly; Readonly::Scalar my $GCONF_CANARY_KEY => '/ebox/modules/gConfCanary/canary'; Readonly::Scalar my $GCONF_EXTENDED_CANARY_KEY => '/ebox/modules/extendedCanary/key'; Readonly::Scalar my $GCONF_MIXEDCONF_CANARY_KEY => '/ebox/modules/mixedConfCanary/key'; use constant BEFORE_BACKUP_VALUE => 'beforeBackup'; use constant AFTER_BACKUP_VALUE => 'afterBackup'; use constant BUG_BACKUP_VALUE => 'bug'; sub testDir { return '/tmp/ebox.backup.test'; } sub notice : Test(startup) { diag 'This test use GConf and may left behind some test entries in the tree /ebox'; diag 'Remember you need the special GConf packages from eBox repository. Otherwise these tests will fail in awkward ways'; } # needed for progress indicator stuff sub setupProgressIndicatorHostModule : Test(setup) { fakeEBoxModule(name => 'apache', subs => [ _regenConfig => sub {}, ], ); } sub setupDirs : Test(setup) { my ($self) = @_; return if !exists $INC{'EBox/Backup.pm'}; EBox::TestStubs::setEBoxConfigKeys( conf => testDir(), tmp => $self->testDir(), group => 'ebox', ); my $testDir = $self->testDir(); system "rm -rf $testDir"; makePrivateDir($testDir); system "rm -rf /tmp/backup"; ($? == 0) or die $!; makePrivateDir('/tmp/backup'); } sub setUpCanaries : Test(setup) { my ($self) = @_; setupGConfCanary(); setupExtendedCanary(); setupMixedConfCanary(); } sub setupGConfCanary { fakeEBoxModule( name => 'gConfCanary', subs => [ revokeConfig => sub { _canaryRevokeGConf($GCONF_CANARY_KEY); } ] ); } sub setupExtendedCanary { fakeEBoxModule( name => 'extendedCanary', subs => [ setCanary => sub { my ($self, $canary) = @_; $self->{canary} = $canary }, canary => sub { my ($self) = @_; return $self->{canary} }, extendedBackup => sub { my ($self, %params) = @_; my $dir = $params{dir}; write_file ("$dir/canary", $self->{canary} ); }, extendedRestore => sub { my ($self, %params) = @_; my $dir = $params{dir}; my $backedUpData = read_file ("$dir/canary" ); $self->setCanary($backedUpData); }, revokeConfig => sub { _canaryRevokeGConf($GCONF_EXTENDED_CANARY_KEY); } ], ); } # this canary contains sensitive data so in debug sub setupMixedConfCanary { fakeEBoxModule( name => 'mixedConfCanary', subs => [ setCanary => sub { my ($self, $canary) = @_; $self->{canary} = $canary }, canary => sub { my ($self) = @_; return $self->{canary} }, dumpConfig => sub { my ($self, $dir, %options) = @_; EBox::Module::Config::_dump_to_file($self, $dir); if ($options{bug}) { write_file ("$dir/canary", BUG_BACKUP_VALUE ); } else { write_file ("$dir/canary", $self->{canary} ); } }, restoreConfig => sub { my ($self, $dir) = @_; EBox::Module::Config::_load_from_file($self, $dir); my $backedUpData = read_file ("$dir/canary" ); $self->setCanary($backedUpData); }, revokeConfig => sub { _canaryRevokeGConf($GCONF_MIXEDCONF_CANARY_KEY); } ], ); } sub setCanaries { my ($value) = @_; setGConfCanary($value); setExtendedCanary($value); setMixedConfCanary($value); } sub setGConfCanary { my ($value) = @_; _setGConfString($GCONF_CANARY_KEY, $value); my $canaryConf = EBox::Global->modInstance('gConfCanary'); $canaryConf->setAsChanged(); } sub setExtendedCanary { my ($value) = @_; _setGConfString($GCONF_EXTENDED_CANARY_KEY, $value); my $extendedCanary = EBox::Global->modInstance('extendedCanary'); $extendedCanary->setCanary($value); $extendedCanary->setAsChanged(); die 'canary not changed' if $extendedCanary->canary() ne $value; } sub setMixedConfCanary { my ($value) = @_; _setGConfString($GCONF_MIXEDCONF_CANARY_KEY, $value); my $mixedConfCanary = EBox::Global->modInstance('mixedConfCanary'); $mixedConfCanary->setCanary($value); $mixedConfCanary->setAsChanged(); die 'canary not changed' if $mixedConfCanary->canary() ne $value; } sub _canaryRevokeGConf { my ($key) = @_; _setGConfString($key, AFTER_BACKUP_VALUE); } sub _setGConfString { my ($key, $value) = @_; defined $key or die "Not key supplied"; defined $value or die "Not value supplied for key $key"; my $client = Gnome2::GConf::Client->get_default; defined $client or die "Can not retrieve GConf client"; $client->set_string($key, $value); die "gconf key $key not changed" if $client->get_string($key) ne $value; } sub checkCanaries { my ($expectedValue, $fullRestore) = @_; checkGConfCanary($expectedValue); checkExtendedCanary($expectedValue, $fullRestore); checkMixedConfCanary($expectedValue); } sub checkCanariesOnlyGConf { my ($expectedValue) = @_; checkGConfCanaryGconf($expectedValue); checkExtendedCanaryGConf($expectedValue); checkMixedConfCanaryGconf($expectedValue); } sub checkGConfCanary { my ($expectedValue) = @_; checkGConfCanaryGconf($expectedValue); } sub checkGConfCanaryGconf { my ($expectedValue) = @_; my $client = Gnome2::GConf::Client->get_default; my $value = $client->get_string($GCONF_CANARY_KEY); is $value, $expectedValue, 'Checking GConf data of simple module canary'; } sub checkExtendedCanaryGConf { my ($expectedValue) = @_; my $client = Gnome2::GConf::Client->get_default; my $value; $value = $client->get_string($GCONF_EXTENDED_CANARY_KEY); is $value, $expectedValue, 'Checking GConf data of canary module with extended backup and restore'; } sub checkExtendedCanary { my ($expectedValue, $fullRestore) = @_; checkExtendedCanaryGConf($expectedValue); checkExtendedCanaryData($expectedValue, $fullRestore); } sub checkExtendedCanaryData { my ($expectedValue, $dataRestored) = @_; my $value; my $extendedCanary = EBox::Global->modInstance('extendedCanary'); $value = $extendedCanary->canary(); if ($dataRestored ) { is $value, $expectedValue, 'Checking extra data of canary module with extended backup and restore'; } else { isnt $value, $expectedValue, 'Checking extra data of canary module was not restored with configuration restore'; } } sub checkMixedConfCanaryGconf { my ($expectedValue) = @_; my $client = Gnome2::GConf::Client->get_default; my $value; $value = $client->get_string($GCONF_MIXEDCONF_CANARY_KEY); is $value, $expectedValue, 'Checking GConf configuration data of canary module with mixed config'; } sub checkMixedConfCanaryOtherConf { my ($expectedValue) = @_; my $mixedConfCanary = EBox::Global->modInstance('mixedConfCanary'); my $value = $mixedConfCanary->canary(); is $value, $expectedValue, 'Checking no-GConf configuration data of canary module'; } sub checkMixedConfCanary { my ($expectedValue) = @_; checkMixedConfCanaryGconf($expectedValue); checkMixedConfCanaryOtherConf($expectedValue); } sub teardownGConfCanary : Test(teardown) { my $client = Gnome2::GConf::Client->get_default; $client->unset($GCONF_CANARY_KEY); $client->unset($GCONF_EXTENDED_CANARY_KEY); $client->unset($GCONF_MIXEDCONF_CANARY_KEY); } sub teardownCanaryModule : Test(teardown) { my ($self) = @_; EBox::TestStubs::setConfig(); } # this counts for 7 tests sub checkStraightRestore { my ($archiveFile, $options_r, $msg) = @_; my $backup = new EBox::Backup(); setCanaries(AFTER_BACKUP_VALUE); lives_ok { $backup->restoreBackup($archiveFile, @{ $options_r }) } $msg; my %options = @{ $options_r }; checkCanaries(BEFORE_BACKUP_VALUE, $options{fullRestore}); checkModulesChanged( name => 'Checking wether all restored modules have the changed state set' ); } sub checkModulesChanged { my %params = @_; my $name = $params{name}; my $global = EBox::Global->getInstance(); my @modules; if (exists $params{modules}) { @modules = @{ $params{modules} }; } else { @modules = @{ $global->modNames() }; } my @modulesChanged = grep { $global->modIsChanged($_) } @modules; diag "moduled changed @modulesChanged"; diag "modules @modules"; # is_deeply [sort @modulesChanged], [sort @modules], $name; use Test::Differences; eq_or_diff [sort @modulesChanged], [sort @modules], $name; } # this counts for 7 tests sub checkDeviantRestore { my ($archiveFile, $options_r, $msg) = @_; my $backup = new EBox::Backup(); setCanaries(AFTER_BACKUP_VALUE); dies_ok { $backup->restoreBackup($archiveFile, @{ $options_r }) } $msg; diag "Checking that failed restore has not changed the configuration"; checkCanaries(AFTER_BACKUP_VALUE, 1); } sub checkMakeBackup { my @backupParams = @_; my $global = EBox::Global->getInstance(); $global->saveAllModules(); my $backupArchive; my $b = new EBox::Backup; lives_ok { $backupArchive = $b->makeBackup(@backupParams) } 'Checking wether backup is correctly done'; return $backupArchive; } # this requires a correct testdata dir sub invalidArchiveTest : Test(30) { my ($self) = @_; my $incorrectFile = $self->testDir() . '/incorrect'; system "cp $0 $incorrectFile"; ($? == 0) or die "$!"; checkDeviantRestore($incorrectFile, [], 'restoreBackup() called with a incorrect file'); my @deviantFiles = ( ['badchecksum.tar', 'restoreBackup() called with a archive with fails checksum'], ['badsize.tar', 'restoreBackup() called with a archive with uncompressed size exceeds available storage'], ['missingtype.tar', 'restoreBackup() called with a archive missing type of backup information'], ['badtype.tar', 'restoreBackup() called with a archive wuth incorrect backup type information'], ); foreach my $case (@deviantFiles) { my ($file, $msg) = @{ $case }; $file = _testdataDir() . "/$file"; (-f $file) or die "Unavailble test data file $file"; checkDeviantRestore($file, [], $msg); } } sub _testdataDir { my $dir = __FILE__; $dir =~ s/Test\.pm/testdata/; return $dir; } sub restoreConfigurationBackupTest : Test(16) { my ($self) = @_; my $configurationBackup; setCanaries(BEFORE_BACKUP_VALUE); $configurationBackup = checkMakeBackup(description => 'test configuration backup'); checkStraightRestore($configurationBackup, [fullRestore => 0], 'configuration restore from a configuration backup'); my $fullBackup; setCanaries(BEFORE_BACKUP_VALUE); $fullBackup = checkMakeBackup(description => 'test full backup', fullBackup => 1); checkStraightRestore($fullBackup, [fullRestore => 0], 'configuration restore from a full backup'); } sub restoreBugreportTest : Test(13) { my ($self) = @_; my $backup = new EBox::Backup(); my $bugReportBackup; setCanaries(BEFORE_BACKUP_VALUE); lives_ok { $bugReportBackup = $backup->makeBugReport() } 'make a bug report'; setCanaries(AFTER_BACKUP_VALUE); lives_ok { $backup->restoreBackup($bugReportBackup) } 'Restoring bug report'; checkGConfCanary(BEFORE_BACKUP_VALUE); checkExtendedCanary(BEFORE_BACKUP_VALUE, 0 ); # mixedConfCanary contains sensitive data in his non-gconf configuration checkMixedConfCanaryGconf(BEFORE_BACKUP_VALUE); checkMixedConfCanaryOtherConf(BUG_BACKUP_VALUE); checkDeviantRestore($bugReportBackup, [fullRestore => 1], 'full restore not allowed from a bug report'); } sub restoreFullBackupTest : Test(15) { my ($self) = @_; my $configurationBackup; setCanaries(BEFORE_BACKUP_VALUE); $configurationBackup = checkMakeBackup(description => 'test configuration backup', fullBackup => 0); checkDeviantRestore($configurationBackup, [fullRestore => 1], 'checking that a full restore is forbidden from a configuration backup' ); my $fullBackup; setCanaries(BEFORE_BACKUP_VALUE); $fullBackup = checkMakeBackup(description => 'test full backup', fullBackup => 1); checkStraightRestore($fullBackup, [fullRestore => 1], 'full restore from a full backup'); } sub partialRestoreTest : Test(15) { my ($self) = @_; my $configurationBackup; setCanaries(BEFORE_BACKUP_VALUE); $configurationBackup = checkMakeBackup(description => 'test configuration backup', fullBackup => 0); setCanaries(AFTER_BACKUP_VALUE); # bad case: not modules to restore dies_ok { EBox::Backup->restoreBackup($configurationBackup, modsToRestore => []); } 'called restoreBackup with a empty list of modules to restore'; # bad case: inexistent module dies_ok { EBox::Backup->restoreBackup( $configurationBackup, modsToRestore => ['gConfCanary', 'inexistent'], ); } 'called restoreBackup with a list of modules t orestore which contains inexistent modules'; # good cases my @cases = ( [qw(gConfCanary)], [qw(gConfCanary extendedCanary)], [qw(gConfCanary extendedCanary mixedConfCanary)], ); foreach my $case (@cases) { my @modsToRestore = @{ $case }; lives_ok { EBox::Backup->restoreBackup( $configurationBackup, modsToRestore => \@modsToRestore, ) } "Partial restore with modules @modsToRestore"; my @checkSubs = map { 'check' . ucfirst $_ } @modsToRestore; foreach my $subName (@checkSubs) { my $sub = __PACKAGE__->can($subName); $sub->(BEFORE_BACKUP_VALUE, 0); } } } # XXX this must be remade taking in account that only modules both in the backup # and in the global module list will be restored # sub restoreWithModulesMissmatchTest : Test(46) { my ($self) = @_; # setCanaries(BEFORE_BACKUP_VALUE); # my $global = EBox::Global->getInstance(); # my @modsInBackup = @{ $global->modNames() }; # my $backupFile = checkMakeBackup( fullBackup => 0 ); # my @straightCases; # # one more module # push @straightCases, sub { # fakeEBoxModule( name => 'superfluousModule', ); # }; # # additional module with met dependencies # push @straightCases, sub { # fakeEBoxModule( name => 'superfluousModule', # subs => [ # restoreDependencies => sub { return ['gConfCanary'] }, # ], # ); # }; # # two additional modules with met dependencies between them # push @straightCases, sub { # fakeEBoxModule( name => 'superfluousModule1', ); # fakeEBoxModule( name => 'superfluousModule2', # subs => [ # restoreDependencies => sub { return ['superfluousModule1'] }, # ], # ); # }; # my @deviantCases; # # with a additional module with unmet dependencies # push @deviantCases, sub { # fakeEBoxModule( name => 'unmetDepModule', # subs => [ # restoreDependencies => sub { return ['inexistentModule'] }, # ], # ); # }; # # with a recursive dependency # push @deviantCases, sub { # fakeEBoxModule( name => 'recursiveDepModule1', # subs => [ # restoreDependencies => sub { return ['recursiveDepModule2'] }, # ], # ); # fakeEBoxModule( name => 'recursiveDepModule2', # subs => [ # restoreDependencies => sub { return ['recursiveDepModule1'] }, # ], # ); # }; # # with a module which depends on itself # push @deviantCases, sub { # fakeEBoxModule( name => 'depOnItselfModule', # subs => [ # restoreDependencies => sub { return ['depOnItselfModule'] }, # ], # ); # }; # my $backup = new EBox::Backup(); # foreach my $case (@straightCases) { # setUpCanaries(); # setGConfCanary(AFTER_BACKUP_VALUE); # setMixedConfCanary(AFTER_BACKUP_VALUE); # $case->(); # $self->_mangleModuleListInBackup($backupFile); # # restore backup # setCanaries(AFTER_BACKUP_VALUE); # lives_ok { # $backup->restoreBackup($backupFile, fullRestore => 0) # } 'checking restore without dependencies problems' ; # # check after backup state # checkCanaries(BEFORE_BACKUP_VALUE, 0); # checkModulesChanged( # name => 'Checking wether restored modules are marked as changed', # modules => \@modsInBackup, # ); # teardownCanaryModule(); # teardownGConfCanary(); # } # foreach my $case (@deviantCases) { # setUpCanaries(); # setGConfCanary(AFTER_BACKUP_VALUE); # setMixedConfCanary(AFTER_BACKUP_VALUE); # $case->(); # $self->_mangleModuleListInBackup($backupFile); # checkDeviantRestore($backupFile, [ fullRestore => 0], , 'checking wether restore with unmet dependencies raises error'); # teardownGConfCanary(); # } # } # this must be synchronized with EBox::Backup::_createM sub _mangleModuleListInBackup { my ($self, $archive) = @_; my $dir = $self->testDir(); my $backupDir = "$dir/eboxbackup"; mkdir $backupDir or die "cannot create $backupDir: $!"; EBox::Backup->_createModulesListFile($backupDir); my $modlistFile = "eboxbackup/modules"; my $replaceCmd = "tar -u -C $dir -f $archive $modlistFile"; system $replaceCmd; system "rm -rf $backupDir"; } sub listBackupsTest : Test(5) { my ($self) = @_; diag "The backup's details of id a are not tested for now. The date detail it is only tested as relative order"; my $backup = new EBox::Backup(); my @backupParams = ( [description => 'configuration backup', fullBackup => 0], [description => 'full backup', fullBackup => 1], ); setCanaries('indiferent configuration'); foreach (@backupParams) { my $global = EBox::Global->getInstance(); $global->saveAllModules(); $backup->makeBackup(@{ $_ }); sleep 1; } # add nobackup files in backup dir to test reliability my $backupsDir = $self->testDir() . '/backups'; system "touch $backupsDir/noBackup"; system "touch $backupsDir/noBackup.tar"; my @backups = @{$backup->listBackups()}; is @backups, @backupParams, 'Checking number of backups listed'; foreach my $backup (@backups) { my %backupParam = @{ pop @backupParams }; my $awaitedDescription = $backupParam{description}; my $awaitedType = $backupParam{fullBackup} ? 'full backup' : 'configuration backup'; is $backup->{description}, $awaitedDescription, 'Checking backup description'; is $backup->{type}, $awaitedType, 'Checking backup type'; } } sub backupDetailsFromArchiveTest : Test(9) { setCanaries(BEFORE_BACKUP_VALUE); my $global = EBox::Global->getInstance(); $global->saveAllModules(); my $configurationBackupDescription = 'test configuration backup for detail test'; my $configurationBackup = EBox::Backup->makeBackup(description => $configurationBackupDescription, fullBackup => 0) ; my $fullBackupDescription = 'test full backup for detail test'; my $fullBackup = EBox::Backup->makeBackup(description => $fullBackupDescription, fullBackup => 1); my $bugreportBackupDescription = 'Bug report'; # string foun in EBox::Backup::makeBugReport my $bugreportBackup = EBox::Backup->makeBugReport(); # XXX date detail IS NOT checked my %detailsExpectedByFile = ( $configurationBackup => { description => $configurationBackupDescription, type => $EBox::Backup::CONFIGURATION_BACKUP_ID, }, $fullBackup => { description => $fullBackupDescription, type => $EBox::Backup::FULL_BACKUP_ID, }, $bugreportBackup => { description => $bugreportBackupDescription, type => $EBox::Backup::BUGREPORT_BACKUP_ID, }, ); foreach my $file (keys %detailsExpectedByFile) { my $details_r; lives_ok { $details_r = EBox::Backup->backupDetailsFromArchive($file) } 'Getting details from file'; my $detailsExpected_r = $detailsExpectedByFile{$file}; while (my ($detail, $value) = each %{ $detailsExpected_r }) { is $details_r->{$detail}, $value, "Checking value of backup detail $detail"; } } } sub backupForbiddenWithChangesTest : Test(7) { my ($self) = @_; setCanaries(BEFORE_BACKUP_VALUE); setCanaries(AFTER_BACKUP_VALUE); my $global = EBox::Global->getInstance(); my @changedModules = grep { $global->modIsChanged($_); } @{ $global->modNames }; throws_ok { my $b = new EBox::Backup; $b->makeBackup(description => 'test'); } qr/not saved changes/, 'Checkign wether the backup is forbidden with changed modules'; checkCanaries(AFTER_BACKUP_VALUE, 1); checkModulesChanged( name => 'Check wether module changed state has not be changed', modules => \@changedModules, ); } sub restoreFailedTest #: Test(6) { my ($self) = @_; # we force failure in one of the modules my $forcedFailureMsg = 'forced failure '; fakeEBoxModule( name => 'unrestorableModule', subs => [ restoreConfig => sub { die $forcedFailureMsg; }, revokeConfig => sub { }, ], ); my $global = EBox::Global->getInstance(); setCanaries(BEFORE_BACKUP_VALUE); my $backupArchive = checkMakeBackup(); setCanaries(AFTER_BACKUP_VALUE); $global->saveAllModules(); foreach my $mod (@{ $global->modInstances }) { next if $mod->name eq 'apache'; # we dont use apache mmod $mod->setAsChanged(); # we mark modules as changed to be able to detect # revoked modules } throws_ok { my $b = new EBox::Backup; $b->restoreBackup($backupArchive); } qr /$forcedFailureMsg/, 'Checking wether restore failed as expected'; diag "Checking modules for revoked values. We check only GConf values because currently the revokation only takes care of them"; checkCanariesOnlyGConf(AFTER_BACKUP_VALUE); my @modules = @{ $global->modNames() }; # @modules = grep { $_ ne 'apache' } @modules; # ignore apache module my @modulesNotChanged = grep { (not $global->modIsChanged($_)) } @modules; ok scalar @modulesNotChanged > 0, 'Checking wether after the restore failure' . ' some modules not longer a changed state (this is a clue of revokation)' ; # setupMixedConfCanary(); } sub dataRestoreTest : Test(7) { my ($self) = @_; setCanaries(BEFORE_BACKUP_VALUE); my $fullBackup = checkMakeBackup(fullBackup => 1); setCanaries(AFTER_BACKUP_VALUE); lives_ok { EBox::Backup->restoreBackup($fullBackup, dataRestore => 1) } 'trying a data restore'; # gconf canary shouldn't be restored checkGConfCanary(AFTER_BACKUP_VALUE); # mixed conf canary shouldn't changed checkMixedConfCanary(AFTER_BACKUP_VALUE); # extended canary configuration should not be changed .. checkExtendedCanaryGConf(AFTER_BACKUP_VALUE); # .. but data must be restored checkExtendedCanaryData(BEFORE_BACKUP_VALUE, 1); } sub checkArchivePermissions : Test(3) { my ($self) = @_; setCanaries(BEFORE_BACKUP_VALUE); my $archive = checkMakeBackup(fullBackup => 0); Test::File::file_mode_is($archive, 0600, 'Checking wether the archive permission only allow reads by its owner'); my @op = `ls -l $archive`; diag "LS -l @op"; my $backupDir = EBox::Backup->backupDir(); Test::File::file_mode_is($backupDir, 0700, 'Checking wether the archives directory permission only allow reads by its owner'); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Backup/testdata/0000775000000000000000000000000012017102272017261 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Backup/testdata/missingtype.tar0000664000000000000000000002400012017102272022340 0ustar eboxbackup/0000755000175000017500000000000010524123344012521 5ustar javierjaviereboxbackup/md5sum0000644000175000017500000000004010524111750013646 0ustar javierjavierd2727261ea9afbd80503509dda05813deboxbackup/description0000644000175000017500000000003110524111750014757 0ustar javierjaviertest configuration backupeboxbackup/date0000644000175000017500000000002310524111750013352 0ustar javierjavier2006-11-07 15:10:48eboxbackup/modules0000644000175000017500000000005210524111750014107 0ustar javierjaviercanaryGConf canaryExtended canaryMixedConfeboxbackup/size0000644000175000017500000000000110524111750013403 0ustar javierjavier5eboxbackup/files.tgz0000644000175000017500000000062710524111750014354 0ustar javierjavierPEn0{S4>mŖeW{"0,1+j CQgv~T"hpf&Dd3AObT؜]^CX 5ʼC#bLdV j՟ w8ߊ{Q r~V-]ǡjws-JZD'{(2WOStC>ӰU<zYh*7#2.tY,@-L=>i֐"GڿauS$TWm(8uSrQ8{Q@Wm #pZjtS[?l}-±Ǡ| ?$ Z(zentyal-core-2.3.21+quantal1/src/EBox/Backup/testdata/badtype.tar0000664000000000000000000002400012017102272021415 0ustar eboxbackup/0000755000175000017500000000000010524124630012520 5ustar javierjaviereboxbackup/md5sum0000644000175000017500000000004010524111750013646 0ustar javierjavierd2727261ea9afbd80503509dda05813deboxbackup/description0000644000175000017500000000003110524111750014757 0ustar javierjaviertest configuration backupeboxbackup/date0000644000175000017500000000002310524111750013352 0ustar javierjavier2006-11-07 15:10:48eboxbackup/type0000644000175000017500000000002310524124630013417 0ustar javierjavierinvalid backup typeeboxbackup/modules0000644000175000017500000000005210524111750014107 0ustar javierjaviercanaryGConf canaryExtended canaryMixedConfeboxbackup/size0000644000175000017500000000000110524111750013403 0ustar javierjavier5eboxbackup/files.tgz0000644000175000017500000000062710524111750014354 0ustar javierjavierPEn0{S4>mŖeW{"0,1+j CQgv~T"hpf&Dd3AObT؜]^CX 5ʼC#bLdV j՟ w8ߊ{Q r~V-]ǡjws-JZD'{(2WOStC>ӰU<zYh*7#2.tY,@-L=>i֐"GڿauS$TWm(8uSrQ8{Q@Wm #pZjtS[?l}-±Ǡ| ?$ Z(eboxbackup/type~0000644000175000017500000000002410524111750013615 0ustar javierjavierconfiguration backupzentyal-core-2.3.21+quantal1/src/EBox/Backup/testdata/badchecksum.tar0000664000000000000000000002400012017102272022236 0ustar eboxbackup/0000755000175000017500000000000010524121772012524 5ustar javierjaviereboxbackup/md5sum0000644000175000017500000000004010524114341013645 0ustar javierjavierghd57261ea9afbd80503509dda05813deboxbackup/description0000644000175000017500000000003110524114317014761 0ustar javierjaviertest configuration backupeboxbackup/date0000644000175000017500000000002310524114317013354 0ustar javierjavier2006-11-07 15:10:48eboxbackup/type0000644000175000017500000000002410524114317013421 0ustar javierjavierconfiguration backupeboxbackup/modules0000644000175000017500000000005210524114317014111 0ustar javierjaviercanaryGConf canaryExtended canaryMixedConfeboxbackup/size0000644000175000017500000000000110524114317013405 0ustar javierjavier5eboxbackup/files.tgz0000644000175000017500000000062710524114317014356 0ustar javierjavierPEn0{S4>mŖeW{"0,1+j CQgv~T"hpf&Dd3AObT؜]^CX 5ʼC#bLdV j՟ w8ߊ{Q r~V-]ǡjws-JZD'{(2WOStC>ӰU<zYh*7#2.tY,@-L=>i֐"GڿauS$TWm(8uSrQ8{Q@Wm #pZjtS[?l}-±Ǡ| ?$ Z(zentyal-core-2.3.21+quantal1/src/EBox/Backup/testdata/badsize.tar0000664000000000000000000002400012017102272021406 0ustar eboxbackup/0000755000175000017500000000000010524117207012522 5ustar javierjaviereboxbackup/md5sum0000644000175000017500000000004010524117121013644 0ustar javierjavierd2727261ea9afbd80503509dda05813deboxbackup/description0000644000175000017500000000003110524117121014755 0ustar javierjaviertest configuration backupeboxbackup/date0000644000175000017500000000002310524117121013350 0ustar javierjavier2006-11-07 15:10:48eboxbackup/type0000644000175000017500000000002410524117121013415 0ustar javierjavierconfiguration backupeboxbackup/modules0000644000175000017500000000005210524117121014105 0ustar javierjaviercanaryGConf canaryExtended canaryMixedConfeboxbackup/size0000644000175000017500000000002510524117207013414 0ustar javierjavier900000000000000000000eboxbackup/files.tgz0000644000175000017500000000062710524117121014352 0ustar javierjavierPEn0{S4>mŖeW{"0,1+j CQgv~T"hpf&Dd3AObT؜]^CX 5ʼC#bLdV j՟ w8ߊ{Q r~V-]ǡjws-JZD'{(2WOStC>ӰU<zYh*7#2.tY,@-L=>i֐"GڿauS$TWm(8uSrQ8{Q@Wm #pZjtS[?l}-±Ǡ| ?$ Z(eboxbackup/size~0000644000175000017500000000000510524117153013610 0ustar javierjavier9e+17zentyal-core-2.3.21+quantal1/src/EBox/View/0000775000000000000000000000000012017102272015155 5ustar zentyal-core-2.3.21+quantal1/src/EBox/View/Customizer.pm0000664000000000000000000002546212017102272017670 0ustar # Copyright (C) 2009-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA # Class: EBox::View::Customizer # # This class is used to customize default views. It helps to change the # behaviour and layout of a view using Perl code. # package EBox::View::Customizer; use strict; use warnings; # Dependencies use EBox::Config; use EBox::Types::Boolean; # External dependencies use HTML::Mason::Interp; use JSON; # objToJson use List::Util; # first use Error qw(:try); # EBox exceptions use EBox::Exceptions::MissingArgument; use EBox::Exceptions::Internal; # Group: Public methods # Method: new # # Constructor for sub new { my $class = shift; my $self = {}; bless ($self, $class); return $self; } # Method: setModel # # Set the model this class is customizing # # Parameters: # (Positional) # # model - An instance of # sub setModel { my ($self, $model) = @_; unless (defined($model)) { throw EBox::Exceptions::MissingArgument('model'); } $self->{model} = $model; } # Method: model # # Return the model this class is customizing # # sub model { my ($self) = @_; return $self->{model}; } # Method: setPermanentMessage # # Set a message that will be always shown on top of the form as in opposed to # flash messages that are shown when an action takes place # # Parameters: # # string - string to show # type - (Optional) note, ad, warning # sub setPermanentMessage { my ($self, $msg, $type) = @_; defined($type) or $type = 'note'; $self->{permanentMessage} = $msg; $self->{permanentMessageType} = $type; } # Method: permanentMessage # # Return a message that will be always shown on top of the form as in opposed to # flash messages that are shown when an action takes place # # # Returns: # # string - string to show # sub permanentMessage { my ($self) = @_; return $self->{permanentMessage}; } # Method: permanentMessageType # # Return the type for the defined permanent message # # Returns: # # string - note, ad or warning # sub permanentMessageType { my ($self) = @_; return $self->{permanentMessageType}; } # Method: setOnChangeActions # # This method is used to set the actions -hide/show or enable/disable- that will take # place on the UI whenever there is a change on one field value. # # Parameters: # # A hash ref containing any number of: # # fieldName => # { # [ value1, value2 ] => { # disable => [ fieldName2, fieldName3 ], # enable => [ fieldName4, fieldName5] } # } # # Where # fieldName: is the name of the watched field # value1, value2: are the values of the watched field that trigger # hide/show actions # fieldName2, fieldName3: name of fields that need to be showed or hidden # # Example: # # Let's say we have two fields. One is called 'Protocol', and # it's a select that can take 'TCP','UDP', or 'GRE'. # The other field is called 'Port', and # it's only used if the protocol is either 'TCP' or 'UDP'. # # Protocol => # { # GRE => { disable => [ Port ] }, # TCP => { enable => [ Port ] }, # UDP => { enable => [ Port ] } # } # # # Note that you will have to use 'on' and 'off' for boolean values sub setOnChangeActions { my ($self, $onChangeActions) = @_; # TODO Make sanity checks $self->{onChangeActions} = $onChangeActions } # Method: onChangeActions # # Return the actions -hide or show- that will take place # on the UI whenever there is a change on one field value. # sub onChangeActions { my ($self) = @_; return $self->{onChangeActions}; } # Method: onChangeFields # # Return a hash name containing the field names that # trigger a show or hide action # sub onChangeFields { my ($self) = @_; my $actions = $self->{onChangeActions}; if ($actions) { return {map {$_ => undef} keys %$actions}; } else { return {}; } } # Method: skipField # # Parameters: # # (POSTIONAL) # # fieldName - string # onChangeValues - hash ref containing the field names # that trigger actions and their actual # values # Returns: # # boolean - true skip, otherwise false # sub skipField { my ($self, $field, $values) = @_; return 0 unless ($field); return 0 unless ($values); return 0 unless (%$values); my $actions = $self->{onChangeActions}; for my $triggerField (keys %$values) { my $actualValue = $values->{$triggerField}; my $actionTriggered = $actions->{$triggerField}->{$actualValue}; my $disable = $actionTriggered->{disable}; my $hide = $actionTriggered->{hide}; my @ignore; push (@ignore, @{$disable}) if ($disable); push (@ignore, @{$hide}) if ($hide); if (@ignore) { if (List::Util::first { $_ eq $field } @ignore) { return 1; } } } return 0; } # Method: onChangeActionOnFieldJS # # It returns the JS code to run when there is a change on a field # # Parameters: # (Positional) # # fieldName - field name # # Returns: # # A string containing js code or an empty string in case this field # doesn't need to trigger anything # sub onChangeActionOnFieldJS { my ($self, $tableName, $fieldName) = @_; unless (defined($fieldName)) { throw EBox::Exceptions::MissingArgument('fieldName'); } my $onChangeActions = $self->onChangeActions(); my $actions = $onChangeActions->{$fieldName}; return '' unless (defined($actions)); my $filename = EBox::Config::templates . '/js/onchange.mas'; my $output; my $interp = HTML::Mason::Interp->new(comp_root => EBox::Config::templates, out_method => \$output); my $comp = $interp->make_component(comp_file => $filename); my @params = (); push(@params, tableName => $tableName, JSONActions => objToJson($actions), fieldName => $fieldName); $interp->exec($comp, @params); return $output; } # Method: onChangeActionsJS # # It returns all the JS functions that are run when # there is a change on some fields # # Returns: # # A string containing js code or an empty string in case this field # doesn't need to trigger anything # sub onChangeActionsJS { my ($self, %params) = @_; my $modal = $params{modal}; my $tableName = $self->model()->table()->{'tableName'}; if ($modal) { $tableName .= '_modal'; } my $jsCode; for my $fieldName (@{$self->model()->fields()}) { $jsCode .= $self->onChangeActionOnFieldJS($tableName, $fieldName); } return $jsCode; } # Method: initHTMLStateField # # Given a field, it returns if the field has to be shown. hidden, or disabled # # Parameters: # # (Positional) # # fieldName - string containing the field name # fields - array ref of instancied types with their current values # # Returns: # # One of these strings: # # show # hide # disable # sub initHTMLStateField { my ($self, $fieldName, $fields) = @_; unless (defined($fieldName)) { throw EBox::Exceptions::MissingArgument('fieldName'); } unless (defined($fields)) { throw EBox::Exceptions::MissingArgument('fields'); } my $actions = $self->onChangeActions(); return 'show' unless (defined($actions)); my @triggers = @{ $self->initHTMLStateOrder() }; if (not @triggers) { @triggers = keys %{$actions}; } for my $trigger (@triggers) { next if ($trigger eq $fieldName); for my $value (keys %{$actions->{$trigger}}) { for my $action (keys %{$actions->{$trigger}->{$value}}) { for my $field (@{$actions->{$trigger}->{$value}->{$action}}) { if ($field eq $fieldName) { for my $f (@{$fields}) { if (($f->fieldName() eq $trigger) and $self->_hasTriggerValue($f, $value)) { return $action; } } } } } } } return 'show'; } sub initHTMLStateOrder { my ($self) = @_; my $order = $self->{initHTMLStateOrder}; return (defined ($order) ? $order : []); } sub setInitHTMLStateOrder { my ($self, $order) = @_; $self->{initHTMLStateOrder} = $order; } sub setHTMLTitle { my ($self, $title) = @_; $self->{htmlTitle} = $title; } # Method: HTMLTitle # # Return the data structure that is used to # create the page title. # # This structure is used to make up a breadcrumb header, if needed. # # Returns: # # Array ref of hash ref containing the page title. # Each hash represents a component of the title. For example: # # Services >> Pop 3 # # For every component you need its text and its link. So every hash # contains the following keys: # # title # link sub HTMLTitle { my ($self) = @_; if ($self->{htmlTitle}) { return $self->{htmlTitle}; } my @crumbs; my $model = $self->model(); while (1) { if ( $model->HTTPLink() or $model->pageTitle() ) { my $titleName = $model->printableName(); $titleName = $model->pageTitle() if ($model->pageTitle()); unshift (@crumbs, { title => $titleName, link => $model->HTTPLink() } ); } if ($model->parentRow()) { $model = $model->parentRow()->model() } else { last; } } return \@crumbs; } # Group: Private methods sub _hasTriggerValue { my ($self, $field, $value) = @_; if ($field->isa('EBox::Types::Boolean')) { my $bool = new EBox::Types::Boolean( fieldName => 'dummy', defaultValue => $value eq 'on'); return $field->isEqualTo($bool); } return ( $field->value() eq $value ); } sub _modelName { my ($self) = @_; my $model = $self->model(); unless ($model) { throw EBox::Exceptions::Internal('model is not set'); } return $model->tableName(); } 1; zentyal-core-2.3.21+quantal1/src/EBox/LogHelper.pm0000664000000000000000000000410312017102272016460 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::LogHelper # # This class exposes the interface to be implemented by those # modules willing to process logs generated by their daemon or service. # An instance of this class must be returned inheriting from # EBox::LogObserver and implementing the method logHelper() # package EBox::LogHelper; use strict; use warnings; use Time::Piece; sub new { my $class = shift; my $self = {}; bless($self, $class); return $self; } # Method: logFiles # # This function must return the file or files to be read from. # # Returns: # # array ref - containing the whole paths # sub logFiles { return []; } # Method: processLine # # This function will be run every time a new line is received in # the associated file. You must parse the line, and generate # the messages which will be logged to eBox through an object # implementing interface. # # Parameters: # # file - file name # line - string containing the log line # dbengine - An instance of class implemeting AbstractDBEngine interface # sub processLine # (file, line, dbengine) { return undef; } # Helper method to convert to the format accepted by the database sub _convertTimestamp { my ($self, $format, $timestamp) = @_; my $t = Time::Piece->strptime($timestamp, $format); return $t->strftime('%Y-%m-%d %H:%M:%S'); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Service.pm0000664000000000000000000000421012017102272016176 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Service; use strict; use warnings; use EBox::Sudo; # Function: manage # # Manage daemons # # Parameters: # # daemon - name of the daemon # action - [start|stop|restart] # # Exceptions: # # Internal - Bad argument # sub manage # (daemon,action) { my ($daemon, $action) = @_; (-f "/etc/init/$daemon.conf") or throw EBox::Exceptions::Internal("No such daemon: $daemon"); if ( $action eq 'start' ) { EBox::Sudo::root("start '$daemon'"); } elsif ( $action eq 'stop' ) { EBox::Sudo::root("stop '$daemon'") if (running($daemon)); } elsif ( $action eq 'restart') { EBox::Sudo::root("stop '$daemon'") if (running($daemon)); EBox::Sudo::root("start '$daemon'"); } else { throw EBox::Exceptions::Internal("Bad argument: $action"); } } # Function: running # # Check if a daemon is running # # Parameters: # # daemon - name of the daemon # # Exceptions: # # - Bad argument # sub running # (daemon) { my ($daemon) = @_; (-f "/etc/init/$daemon.conf") or throw EBox::Exceptions::Internal("No such daemon: $daemon"); my $status = `status '$daemon'`; # TODO: Parse different exit status: # Pre-start # Post-start # .... # Not it's running or stopped if ($status =~ m{^$daemon start/running.*}) { return 1; } else { return undef; } } 1; zentyal-core-2.3.21+quantal1/src/EBox/Menu.pm0000664000000000000000000000465412017102272015516 0ustar # Copyright (C) 2009-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Menu; use EBox; use EBox::Config; use EBox::Gettext; use EBox::Global; use EBox::Menu::Root; use EBox::CGI::Run; use Encode; use Error qw(:try); use Storable qw(store); sub _addWord { my ($keywords, $word, $id) = @_; if(not defined($keywords->{$word})) { $keywords->{$word} = []; } if(!grep(/^$id$/, @{$keywords->{$word}})) { push(@{$keywords->{$word}}, $id); } } sub getKeywords { my ($keywords, $item) = @_; if(defined($item->{'text'})) { my $text = $item->{'text'}; Encode::_utf8_on($text); $text = lc($text); my @words = split('\W+', $text); for my $word (@words) { _addWord($keywords, $word, $item->{id}); } } if(defined($item->{'url'})) { try { my $classname = EBox::CGI::Run::classFromUrl($item->{'url'}); my ($model, $action) = EBox::CGI::Run::lookupModel($classname); if($model) { my $words = $model->keywords(); for my $word (@{$words}) { _addWord($keywords, $word, $item->{id}); } } } otherwise { EBox::debug('No model found for ' . $item->{'url'} . "\n"); } } if($item->items()) { for my $i (@{$item->items()}) { getKeywords($keywords, $i); } } } sub cacheFile { return EBox::Config::tmp . 'menucache'; } sub regenCache { my $keywords = {}; my $root = new EBox::Menu::Root(); my $global = EBox::Global->getInstance(); foreach (@{$global->modNames}) { my $mod = $global->modInstance($_); $mod->menu($root); } getKeywords($keywords, $root); my $file = cacheFile(); store($keywords, $file); } 1; zentyal-core-2.3.21+quantal1/src/EBox/MyDBEngine.pm0000664000000000000000000004561212017102272016532 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::MyDBEngine; use strict; use warnings; use base qw(EBox::AbstractDBEngine); use DBI; use EBox::Gettext; use EBox::Validate; use EBox; use EBox::Global; use EBox::Config; use EBox::Sudo; use EBox::Exceptions::Internal; use EBox::Exceptions::InvalidData; use EBox::Exceptions::MissingArgument; use EBox::FileSystem; use File::Slurp; use File::Copy; use File::Basename; use EBox::Logs::SlicedBackup; use EBox::Util::SQLTypes; use Error qw(:try); use Data::Dumper; my $DB_PWD_FILE = '/var/lib/zentyal/conf/zentyal-mysql.passwd'; sub new { my $class = shift, my $self = {}; bless($self,$class); $self->_connect(); $self->{logs} = EBox::Global->getInstance(1)->modInstance('logs'); return $self; } # Method: _dbname # # This function returns the database name. # sub _dbname { my $root = EBox::Config::configkey('eboxlogs_dbname'); ($root) or throw EBox::Exceptions::External(__x('You must set the {variable} ' . 'variable in the ebox configuration file', variable => 'eboxlogs_dbname')); return $root; } # Method: _dbuser # # This function returns the database user. # sub _dbuser { my $root = EBox::Config::configkey('eboxlogs_dbuser'); ($root) or throw EBox::Exceptions::External(__x('You must set the {variable} ' . 'variable in the ebox configuration file', variable => 'eboxlogs_dbuser')); return $root; } # Method: _dbpass # # This function returns the database user password. # sub _dbpass { my ($pass) = @{EBox::Sudo::root("/bin/cat $DB_PWD_FILE")}; return $pass; } # Method: _dbsuperuser # # This function returns the database superuser's username # sub _dbsuperuser { return 'root'; } # Method: _connect # # This function do the necessary operations to establish a connection with the # database. # sub _connect { my ($self) = @_; return if ($self->{'dbh'}); my $dbh = DBI->connect('dbi:mysql:' . $self->_dbname(), $self->_dbuser(), $self->_dbpass(), { RaiseError => 1}); unless ($dbh) { throw EBox::Exceptions::Internal("Connection DB Error: $DBI::errstr\n"); } $self->{'dbh'} = $dbh; } # Method: _disconnect # # This function do the necessary operations to get disconnected from the # database. # sub _disconnect { my ($self) = @_; $self->{'sthinsert'}->finish() if ($self->{'sthinsert'}); if ($self->{'dbh'}) { $self->{'dbh'}->disconnect(); $self->{'dbh'} = undef; } else { throw EBox::Exceptions::Internal( 'There wasn\'t a database connection, check if database exists\n'); } } sub _prepare { my ($self, $sql) = @_; $self->{'sthinsert'} = $self->{'dbh'}->prepare($sql); unless ($self->{'sthinsert'}) { #throw exception EBox::debug("Error preparing sql: $sql\n"); throw EBox::Exceptions::Internal("Error preparing sql: $sql\n"); } } # Method: unbufferedInsert # # This function do the necessary operations to create and establish an insert # operation to a table form the database. This operation is executed # immediately as opposite to the insert method. # # Parameters: # $table: The table name to insert data. # $values: A hash ref with database fields name and values pairs that do you # want to insert to the table name passed as parameter too. # sub unbufferedInsert { my ($self, $table, $values) = @_; my $tableInfo = $self->{logs}->getTableInfo($table); my $sql = "INSERT INTO $table ( "; my @keys = (); my @vals = (); while (my ($key, $value) = each %$values) { if ($tableInfo and $tableInfo->{types}) { my $type = $tableInfo->{types}->{$key}; if ($type) { $value = EBox::Util::SQLTypes::storer($type, $value); } } push(@keys, $key); push(@vals, $value); } $sql .= join(", ", @keys); $sql .= ") VALUES ("; foreach (@vals) { $sql .= " ?,"; } $sql = (substr($sql, 0, -1)).')'; $self->_prepare($sql); my $err = $self->{'sthinsert'}->execute(@vals); if (!$err) { #throw exception EBox::debug ("Error inserting data: $sql\n" . $self->{dbh}->errstr . " \n"); EBox::debug ("Values: " . Dumper(\@vals) . "\n"); throw EBox::Exceptions::Internal("Error inserting data: $sql\n" . $self->{dbh}->errstr . " \n" . "Values: " . Dumper(\@vals) . "\n"); } } # Method: insert # # This function do the necessary operations to create and establish an insert # operation to a table form the database. This operation is buffered # and will be executed when calling the multiInsert method. # # Parameters: # $table: The table name to insert data. # $values: A hash ref with database fields name and values pairs that do you # want to insert to the table name passed as parameter too. # sub insert { my ($self, $table, $values) = @_; my $tableInfo = $self->{logs}->getTableInfo($table); if (not exists $self->{multiInsert}->{$table}) { $self->{multiInsert}->{$table} = []; } if ($tableInfo and $tableInfo->{types}) { foreach my $key (keys %{$values}) { my $type = $tableInfo->{types}->{$key}; if ($type) { my $value = $values->{$key}; $values->{$key} = EBox::Util::SQLTypes::storer($type, $value); } } } push (@{$self->{multiInsert}->{$table}}, $values); } # Method: multiInsert # # Commits the INSERT operation with all the buffered rows stored by # the insert function. This is called from EBox::Loggerd so in # general you don't have to care about it. # sub multiInsert { my ($self) = @_; for my $table (keys %{$self->{multiInsert}}) { my @values = @{$self->{multiInsert}->{$table}}; next unless (@values); my @keys = keys %{$values[0]}; my $sql = sprintf("INSERT INTO $table (%s) VALUES %s", join (',', @keys), join (',', map {'(' . join(',', map {'?'} @keys) . ')'} @values), ); $self->_prepare($sql); my @flat; for my $val (@values) { push (@flat, map {$val->{$_}} @keys); } my $err = $self->{'sthinsert'}->execute(@flat); $self->{multiInsert}->{$table} = []; if (!$err) { my $errStr = $self->{dbh}->errstr; if ($errStr =~ m/invalid byte sequence for encoding "UTF8"/) { EBox::warn("Encoding error found: $errStr . We will try to add each line individually"); $self->_multiInsertBadEncoding($table, \@values); } else { throw EBox::Exceptions::Internal( "Error inserting data: $sql\n" . $errStr . " \n" . "Values: " . Dumper(\@values) . "\n" ); } } } } sub _multiInsertBadEncoding { my ($self, $table, $values_r) = @_; foreach my $valuesToInsert (@{ $values_r }) { try { $self->unbufferedInsert($table, $valuesToInsert ); } otherwise { my $ex = shift; EBox::error("Error in unbuffered insert from multiInsert with encoding problems: $ex") }; } } # Method: update # # This function performs an update in the database. # # Parameters: # $table: The table name to insert data. # $values: A hash ref with database fields name and values pairs that do you # want to update # $where: An array ref with conditions for the where # sub update { my ($self, $table, $values, $where) = @_; my $sql = "UPDATE $table SET "; $sql .= join(", ", map { $_ . " = " . $values->{$_} } keys %$values); $sql .= ' WHERE ' . join(' AND ', @{$where}); $self->_prepare($sql); my $err = $self->{'sthinsert'}->execute(); if (!$err) { #throw exception EBox::debug ("Error updating data: $sql\n" . $self->{dbh}->errstr . " \n" ); throw EBox::Exceptions::Internal ("Error updating data: $sql\n" . $self->{dbh}->errstr . " \n" ); } } # Method: delete # # This function performs a delete in the database. # # Parameters: # $table: The table name to insert data. # $where: An array ref with conditions for the where # sub delete { my ($self, $table, $where) = @_; my $sql = "DELETE FROM $table "; $sql .= ' WHERE ' . join(' AND ', @{$where}); $self->_prepare($sql); my $err = $self->{'sthinsert'}->execute(); if (!$err) { #throw exception EBox::debug ("Error deleting data: $sql\n" . $self->{dbh}->errstr . " \n" ); throw EBox::Exceptions::Internal ("Error deleting data: $sql\n" . $self->{dbh}->errstr . " \n" ); } } # Method: query # # This function do the necessary operations to create and establish a query # operation to a table form the database. # # Parameters: # $sql: A string that contains the SQL query. # @values: An array with the values to substitute in the query. # # Returns: # (this is copied for the perldoc for DBI) # It returns a reference to an array that contains one hash reference per # row. If there are no rows to return, fetchall_arrayref returns a reference # to an empty array. If an error occurs, fetchall_arrayref returns the data # fetched thus far, which may be none. You should check $sth->err afterwards # (or use the RaiseError attribute) to discover if the data is complete or was # truncated due to an error. # # sub query { my ($self, $sql, @values) = @_; my $ret; my $err; $self->_prepare($sql); if (@values) { $err = $self->{'sthinsert'}->execute(@values); } else { $err = $self->{'sthinsert'}->execute(); } if (!$err) { my $errstr = $self->{'dbh'}->errstr(); EBox::debug ("Error querying data: $sql , $errstr\n"); # throw EBox::Exceptions::Internal ("Error querying data: $sql , $errstr"); } $ret = $self->{'sthinsert'}->fetchall_arrayref({}); $self->{'sthinsert'}->finish(); return $ret; } # Method: query_hash # # Run a custom SQL query and return the results # # Parameters: # # index - String the module name in lower case # query - Hash containing SQL strings with optional (except 'from') keys: # 'select', 'from', 'where', 'group', 'order', 'limit' # # Return: # array reference. Each row will be a hash reference with column/values # as key/values. sub query_hash { my ($self, $query) = @_; my $sql = $self->query_hash_to_sql($query); my @results = @{$self->query($sql)}; return \@results; } sub query_hash_to_sql { my ($self, $query, $semicolon) = @_; defined $semicolon or $semicolon = 1; my $sql = "SELECT "; if (defined($query->{'select'})) { $sql .= $query->{'select'}; } else { $sql .= '*'; } $sql .= " FROM " . $query->{'from'} . " "; if (defined($query->{'where'})) { $sql .= "WHERE " . $query->{'where'} . " "; } if (defined($query->{'group'})) { $sql .= "GROUP BY " . $query->{'group'} . " "; } if (defined($query->{'order'})) { $sql .= "ORDER BY " . $query->{'order'} . " "; } if (defined($query->{'limit'})) { $sql .= "LIMIT " . $query->{'limit'} . " "; } if ($semicolon) { $sql .= ';'; } return $sql; } # Method: do # # Prepare and execute a single statement. # # # Parameters: # $sql: A string that contains the SQL statement. # $attr: # @bind_values # # # Returns : the number of rows affected sub do { my ($self, $sql, $attr, @bindValues) = @_; my @optionalCallParams; if (defined $attr) { push @optionalCallParams, $attr; } if (@bindValues) { push @optionalCallParams, @bindValues; } my $res = $self->{dbh}->do($sql, @optionalCallParams); if (not defined $res) { my $errstr = $self->{'dbh'}->errstr(); throw EBox::Exceptions::Internal("Error doing statement: $sql , $errstr\n"); } return $res; } # Method: tables # # Returns: # reference to a list with all the public (regular) tables of the database sub tables { my ($self) = @_; my $dbname = $self->_dbname(); my $sql = 'show tables'; my @tables = map { $_->{"Tables_in_$dbname"} } @{$self->query($sql)}; return \@tables; } # Method: quote # # returns a quoted version of the string # # Warning: # it only can quote string values used in SQL statement, # it can not quote the SQL statement itself sub quote { my ($self, $string) = @_; return $self->{dbh}->quote($string); } sub backupDB { my ($self, $dir, $basename, %args) = @_; my $slicedMode; if (exists $args{slicedMode}) { $slicedMode = delete $args{slicedMode}; } else { $slicedMode = EBox::Logs::SlicedBackup::slicedMode(); } if ($slicedMode) { EBox::Logs::SlicedBackup::slicedBackup($self, $dir, %args); } else { my $file = "$dir/$basename.dump"; $self->dumpDB($file, 0); } } sub restoreDB { my ($self, $dir, $basename, %params) = @_; my $slicedMode; if (exists $params{slicedMode}) { $slicedMode = delete $params{slicedMode}; } else { $slicedMode = EBox::Logs::SlicedBackup::slicedMode(); } my $noSlicesDumpFile = "$dir/$basename.dump"; if ($slicedMode) { if (-e $noSlicesDumpFile) { throw EBox::Exceptions::External( __('You are using sliced backup mode and this backup was made in no-sliced mode') ); } EBox::Logs::SlicedBackup::slicedRestore($self, $dir, %params); } else { if (not -e $noSlicesDumpFile) { throw EBox::Exceptions::External( __('Database dump file not found. Maybe the backup you are trying to restore was made in sliced mode?') ); } $self->restoreDBDump($noSlicesDumpFile, 0); } } # Method: dumpDB # # Makes a dump of the database in the specified file # # Parameters: # $outputFile - output database dump file # sub dumpDB { my ($self, $outputFile, $onlySchema) = @_; defined $onlySchema or $onlySchema = 0; my $tmpFile = _superuserTmpFile(1); my $dbname = _dbname(); my $dbuser = _dbuser(); my $dbpass = _dbpass(); my $args = "-u$dbuser -p$dbpass"; if ($onlySchema) { $args .= ' --no-data'; } my $dumpCommand = "mysqldump $args $dbname > $tmpFile"; $self->commandAsSuperuser($dumpCommand); # give file to ebox and move to real desitnation EBox::Sudo::root("chown ebox.ebox $tmpFile"); File::Copy::move($tmpFile, $outputFile); $self->_mangleDumpFile($outputFile); } sub _mangleDumpFile { my ($self, $file) = @_; # TODO: check if this works with MySQL my @linesToComment = ( 'DROP TABLE public.' . EBox::Logs::SlicedBackup::backupSlicesDBTable(), ); foreach my $line (@linesToComment) { my $sed = qq{sed -i s/'$line'/'-- $line'/ $file}; EBox::Sudo::command($sed); } } # Method: restoreDB # # restore a database from a dump file. # WARNING: This erase all the DB current dara # # Parameters: # $file - database dump file # sub restoreDBDump { my ($self, $file, $onlySchema) = @_; defined $onlySchema or $onlySchema = 0; EBox::info('We wil try to restore the database. This will erase your current data' ); my $tmpFile = _superuserTmpFile(0); EBox::Sudo::root("mv $file $tmpFile"); try { my $superuser = _dbsuperuser(); EBox::Sudo::root("chown $superuser:$superuser $tmpFile"); } otherwise { # left file were it was before my $ex =shift; EBox::Sudo::root("mv $tmpFile $file"); $ex->throw(); }; try { $self->sqlAsSuperuser(file => $tmpFile); } finally { # undo ownership and file move EBox::Sudo::root("chown ebox:ebox $tmpFile"); EBox::Sudo::root("mv $tmpFile $file"); }; if ($onlySchema) { EBox::info('Database schema dump for ' . _dbname() . ' restored' ); } else { EBox::info('Database dump for ' . _dbname() . ' restored' ); } } # Method: sqlAsSuperuser # # Executes sql as the database's superuser # # Arguments (named): # sql - string with SQL code to execute # file - file which contents will be read as executed as SQL sub sqlAsSuperuser { my ($self, %args) = @_; my $file = $args{file}; my $sql = $args{sql}; if ($file and $sql) { throw EBox::Exceptions::Internal('Incompatible parameters: file and sql'); } elsif (not ($file or $sql)) { throw EBox::Exceptions::MissingArgument('file or sql'); } if ($sql) { $file = EBox::Config::tmp() . 'sqlSuper.cmd'; File::Slurp::write_file($file, $sql); } my $dbname = $self->_dbname(); $self->commandAsSuperuser("mysql --defaults-file=/etc/mysql/debian.cnf $dbname < $file"); } # Method: commandAsSuperuser # # Executes a shell command with the ID of the database superuser sub commandAsSuperuser { my ($self, $cmd) = @_; defined $cmd or throw EBox::Exceptions::MissingArgument('command'); EBox::Sudo::root($cmd); } sub _superuserTmpFile { my ($create) = @_; my $file = EBox::Config::tmp() . 'db_superuser.tmp'; if ($create) { my $superuser = _dbsuperuser(); if (not -e $file) { EBox::Sudo::command("touch $file"); } EBox::Sudo::root("chown $superuser:$superuser $file"); } return $file; } sub DESTROY { my ($self) = @_; $self->_disconnect() if (defined($self)); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Test/0000775000000000000000000000000012017102272015162 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Test/StaticForm.pm0000664000000000000000000001146612017102272017603 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Test::StaticForm # # This class is used as an example for EBox::Model::DataForm # # It subclasses # package EBox::Test::StaticForm; use base 'EBox::Model::DataForm::ReadOnly'; use strict; use warnings; use EBox::Gettext; use EBox::Types::Boolean; use EBox::Types::HasMany; use EBox::Types::Int; use EBox::Types::InverseMatchSelect; use EBox::Types::InverseMatchUnion; use EBox::Types::IPAddr; use EBox::Types::Link; use EBox::Types::MACAddr; use EBox::Types::Password; use EBox::Types::PortRange; use EBox::Types::Select; use EBox::Types::Service; use EBox::Types::Text; use EBox::Types::Union; use EBox::Types::Union::Text; sub new { my $class = shift; my %parms = @_; my $self = $class->SUPER::new(@_); bless($self, $class); return $self; } # Method: _table # # This method overrides to return # a table model description. # sub _table { my @tableHead = ( new EBox::Types::IPAddr( 'fieldName' => 'compulsory_addr', 'printableName' => 'Compulsory IP Address', 'defaultValue' => '192.168.45.1/32', ), new EBox::Types::Boolean( 'fieldName' => 'compulsory_boolean', 'printableName' => 'Compulsory Boolean', 'defaultValue' => 1, ), new EBox::Types::Int( 'fieldName' => 'compulsory_int', 'printableName' => 'Compulsory Integer', 'defaultValue' => 11, ), new EBox::Types::Text( 'fieldName' => 'compulsory_text', 'printableName' => 'Compulsory Text', 'defaultValue' => 'foo', ), new EBox::Types::MACAddr( 'fieldName' => 'compulsory_mac', 'printableName' => 'Compulsory MAC Address', 'defaultValue' => '00:0C:29:AD:B4:60', ), new EBox::Types::Password( 'fieldName' => 'compulsory_password', 'printableName' => 'Compulsory Password', 'minLength' => 5, 'maxLength' => 10, 'defaultValue' => 'foobar', ), new EBox::Types::PortRange( 'fieldName' => 'port_range', 'printableName' => 'Port range', 'defaultValue' => '2132', ), new EBox::Types::Service( 'fieldName' => 'compulsory_service', 'printableName' => 'Compulsory Service', 'defaultValue' => '1010/udp', ), ); my $dataTable = { 'tableName' => 'StaticTestForm', 'printableTableName' => 'Read only test form', 'defaultController' => '/Test/Controller/StaticTestForm', 'defaultActions' => [ 'changeView' ], 'tableDescription' => \@tableHead, 'modelDomain' => 'Logs', 'class' => 'dataForm', 'help' => 'Static test form', }; return $dataTable; } # Method: _content # # Overrides: # # # sub _content { return { compulsory_addr => '10.0.0.0/24', compulsory_boolean => 0, compulsory_int => 12, compulsory_text => 'bar', compulsory_service => 'icmp', compulsory_mac => '00:00:00:FA:BA:DA', compulsory_password => 'fabada', port_range => '20:2000', }; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Test/Mason.pm0000664000000000000000000000717112017102272016603 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Test::Mason; # package: EBox::Test::Mason # to ease the testing of mason components. This does NOT test all the content (use HTML::Maason::Test for this) but only checks if compiles. # You can revise the output files by eye after running the tests # # This currently depends from exec-mason-temaplate tool use strict; use warnings; use File::Slurp; use File::Basename; use HTML::Mason; use Cwd qw(abs_path); use Test::More; use Error qw(:try); sub checkTemplateExecution { my %args = @_; my $template = $args{template}; my $templateParams = exists $args{templateParams} ? $args{templateParams} : []; my $compRoot = exists $args{compRoot} ? $args{compRoot } : []; my $testName = exists $args{name} ? $args{name} : "Testing if execution of template $template with params @$templateParams was successful"; my $printOutput = $args{printOutput}; my $outputFile = exists $args{outputFile} ? $args{outputFile} : '/tmp/' . basename $template; my $templateOutput; my $templateError; my $templateExecutionOk = 0; try { $templateOutput = executeTemplate(template => $template, templateParams => $templateParams, compRoot => $compRoot, ); $templateExecutionOk = 1; } otherwise { my $ex = shift @_; $templateError = "$ex"; $templateOutput = \$templateError; # templateOutput must be a scalar ref to be in the same form that the return value of executeTemplate }; ok $templateExecutionOk, $testName; if ($printOutput || $templateError) { diag "Template $template with parameters @$templateParams output:\n$$templateOutput\n"; } if ($outputFile) { _printOutputFile($outputFile, $templateOutput); } return $templateExecutionOk; } sub executeTemplate { my %args = @_; my $template = $args{template}; my @params = exists $args{templateParams} ? @{ $args{templateParams} } : (); my $additionalRoots = exists $args{compRoot} ? $args{compRoot} : []; my $comp_root = _comp_root($template, $additionalRoots); my $templateOutput; my $interp = HTML::Mason::Interp->new(comp_root => $comp_root, out_method => \$templateOutput); my $comp = $interp->make_component(comp_file => $template); $interp->exec($comp, @params); return \$templateOutput; } sub _comp_root { my ($template, $root_paths_r) = @_; my @root_paths = @{ $root_paths_r } ; my $main_root = abs_path ($template); $main_root = dirname $main_root; my $i = 0; # counter to generate comp_root ids my @roots = map { $i++; [ "user-$i" => $_ ] } @root_paths; unshift @roots, [ MAIN => $main_root ]; return \@roots; } sub _printOutputFile { my ($outputFile, $data) = @_; my $separator; if ($outputFile =~ m/\.html?$/) { $separator = '
'; } else { $separator = "---------------\n"; } write_file($outputFile, { append => 1}, $separator ); write_file($outputFile, {append => 1 }, $data ); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Test/Class.pm0000664000000000000000000000206512017102272016570 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Test::Class; # class: EBox::Test::Class # # This class is intended to use as base, replacing Test:Class, to build eBox's test classes # use strict; use warnings; use base 'Test::Class'; use Test::More; use Test::Exception; use EBox::Test;; use EBox::TestStubs; sub _testStubsForFrameworkModules : Test(startup) { EBox::TestStubs::activateTestStubs(); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Test/CGI.pm0000664000000000000000000000516112017102272016125 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Test::CGI; # Description: # use strict; use warnings; use base 'Exporter'; our @EXPORT_OK = qw(runCgi setCgiParams cgiErrorOk cgiErrorNotOk checkCgiError checkMasonParameters muteHtmlOutput); our %EXPORT_TAGS = (all => \@EXPORT_OK ); use Test::Differences; use Test::Builder; my $Test = Test::Builder->new; sub runCgi { my ($cgi, @params) = @_; setCgiParams($cgi, @params); $cgi->run(); } sub setCgiParams { my ($cgi, %params) = @_; while (my ($paramName, $paramValue) = each %params) { my $query = $cgi->{cgi}; $query->param( $paramName => $paramValue); } } # there are 3 subs to check error because i am not sure what style/name is better sub cgiErrorOk { my ($cgi, $name) = @_; my $errorFound = _errorInCgi($cgi); $Test->ok($errorFound, $name); } sub cgiErrorNotOk { my ($cgi, $name) = @_; my $errorNotFound = not _errorInCgi($cgi); $Test->ok($errorNotFound, $name); } sub checkCgiError { my ($cgi, $wantError, $name) = @_; if ($wantError) { cgiErrorOk($cgi, $name); } else { cgiErrorNotOk($cgi, $name); } } sub _errorInCgi { my ($cgi) = @_; return defined ($cgi->{error}) or defined ($cgi->{olderror}); } sub muteHtmlOutput { my ($class) = @_; my $mutePrintHtmlCode = "no warnings; package $class; sub _print {}; "; eval $mutePrintHtmlCode; if ($@) { die "Error when overriding _printHtml with a muted version: $@"; } } sub checkMasonParameters { my ($cgi, %params) = @_; exists $params{wantedParameters} or die "wantedParameters argument not found"; my $wantedParameters = $params{wantedParameters}; my $testName = exists $params{testName} ? $params{testName} : 'Checking mason parameters'; # we convert to hash to eliminate order issues my $masonParameters = $cgi->{params}; my $params = defined $masonParameters ? { @{ $masonParameters } } : {}; eq_or_diff $params, $wantedParameters, $testName; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Test/Model.pm0000664000000000000000000004113112017102272016560 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Test::Model # # # This class is used as a model to refactoring as #690 # shows # # It subclasses # package EBox::Test::Model; use base 'EBox::Model::DataTable'; use strict; use warnings; use EBox::Gettext; use EBox::Types::Boolean; use EBox::Types::HasMany; use EBox::Types::Int; use EBox::Types::InverseMatchSelect; use EBox::Types::InverseMatchUnion; use EBox::Types::IPAddr; use EBox::Types::Link; use EBox::Types::MACAddr; use EBox::Types::Password; use EBox::Types::PortRange; use EBox::Types::Select; use EBox::Types::Service; use EBox::Types::Text; use EBox::Types::Union; use EBox::Types::Union::Text; sub new { my $class = shift; my %params = @_; my $self = $class->SUPER::new(@_); bless($self, $class); $self->{runtimeIndex} = $params{runtimeIndex}; return $self; } # Method: _table # # This method overrides to return # a table model description. # sub _table { my @tableHead = ( new EBox::Types::IPAddr( 'fieldName' => 'compulsory_addr', 'printableName' => 'Compulsory IP Address', 'class' => 'tcenter', 'size' => '12', 'editable' => 1, 'optional' => 0, ), new EBox::Types::IPAddr( 'fieldName' => 'optional_addr', 'printableName' => 'Optional IP Address', 'class' => 'tcenter', 'size' => '12', 'editable' => 1, 'optional' => 1, ), new EBox::Types::Boolean( 'fieldName' => 'compulsory_boolean', 'printableName' => 'Compulsory Boolean', 'class' => 'tcenter', 'size' => '1', 'editable' => 1, 'optional' => 0, ), new EBox::Types::Boolean( 'fieldName' => 'optional_boolean', 'printableName' => 'Optional Boolean', 'class' => 'tcenter', 'size' => '1', 'editable' => 1, 'optional' => 1, ), new EBox::Types::Int( 'fieldName' => 'compulsory_int', 'printableName' => 'Compulsory Integer', 'class' => 'tcenter', 'size' => '1', 'editable' => 1, 'optional' => 0, ), new EBox::Types::Int( 'fieldName' => 'optional_int', 'printableName' => 'Optional Integer', 'class' => 'tcenter', 'size' => '1', 'editable' => 1, 'optional' => 1, ), new EBox::Types::Select( 'fieldName' => 'compulsory_select', 'printableName' => 'Compulsory Select', 'class' => 'tcenter', 'size' => '1', 'editable' => 1, 'optional' => 0, 'populate' => \&compulsoryOptionsCallback, ), # new EBox::Types::Select( # 'fieldName' => 'unique_select', # 'printableName' => 'Unique Select', # 'class' => 'tcenter', # 'size' => '1', # 'editable' => 1, # 'optional' => 1, # 'populate' => \&optionalOptionsCallback, # 'unique' => 1, # ), # new EBox::Types::Select( # 'fieldName' => 'foreign_select', # 'printableName' => 'Foreign Select Object', # 'foreignModel' => \&objectModelCallback, # 'foreignField' => 'name', # 'class' => 'tcenter', # 'editable' => 1, # ), new EBox::Types::Text( 'fieldName' => 'compulsory_text', 'printableName' => 'Compulsory Text', 'class' => 'tcenter', 'size' => '10', 'editable' => 1, 'optional' => 0, ), new EBox::Types::Text( 'fieldName' => 'optional_text', 'printableName' => 'Optional Text', 'class' => 'tcenter', 'size' => '10', 'editable' => 1, 'optional' => 1, ), new EBox::Types::MACAddr( 'fieldName' => 'compulsory_mac', 'printableName' => 'Compulsory MAC Address', 'class' => 'tcenter', 'size' => '10', 'editable' => 1, 'optional' => 0, ), new EBox::Types::MACAddr( 'fieldName' => 'optional_mac', 'printableName' => 'Optional MAC address', 'class' => 'tcenter', 'size' => '10', 'editable' => 1, 'optional' => 1, ), new EBox::Types::Link( 'fieldName' => 'optional_link', 'printableName' => 'Optional Link', 'class' => 'tcenter', 'size' => '1', 'optional' => 1, 'volatile' => 1, 'acquirer' => sub { return '/Summary/Index' }, ), new EBox::Types::Password( 'fieldName' => 'compulsory_password', 'printableName' => 'Compulsory Password', 'class' => 'tcenter', 'size' => '10', 'editable' => 1, 'optional' => 0, 'minLength' => 5, 'maxLength' => 10, ), new EBox::Types::Password( 'fieldName' => 'optional_password', 'printableName' => 'Optional Password', 'class' => 'tcenter', 'size' => '10', 'editable' => 1, 'optional' => 1, 'maxLength' => 6, ), new EBox::Types::PortRange( 'fieldName' => 'port_range', 'printableName' => 'Port range', 'class' => 'tcenter', 'size' => '5', 'editable' => 1, 'optional' => 0, ), new EBox::Types::Union( 'fieldName' => 'union', 'printableName' => 'Union', 'class' => 'tcenter', 'size' => 10, 'editable' => 1, 'subtypes' => [ new EBox::Types::Text( 'fieldName' => 'foo', 'printableName' => 'Foo', 'editable' => 1, ), new EBox::Types::PortRange( 'fieldName' => 'bar', 'printableName' => 'Bar', 'editable' => 1, ), new EBox::Types::IPAddr( 'fieldName' => 'baz', 'printableName' => 'Baz', 'editable' => 1, ), new EBox::Types::Union::Text( 'fieldName' => 'others', 'printableName' => 'Other option', ), ] ), new EBox::Types::InverseMatchSelect( 'fieldName' => 'inverse_select', 'printableName' => 'Inverse Match Select', 'class' => 'tcenter', 'size' => '11', 'editable' => 1, 'populate' => \&compulsoryOptionsCallback, 'optional' => 0, ), new EBox::Types::InverseMatchUnion( 'fieldName' => 'inverse_union', 'printableName' => 'Inverse Match Union', 'class' => 'tcenter', 'size' => 10, 'editable' => 1, 'subtypes' => [ new EBox::Types::Text( 'fieldName' => 'inverse_foo', 'printableName' => 'Inverse Foo', 'editable' => 1, ), new EBox::Types::PortRange( 'fieldName' => 'inverse_bar', 'printableName' => 'Inverse Bar', 'editable' => 1, ), new EBox::Types::IPAddr( 'fieldName' => 'inverse_baz', 'printableName' => 'Inverse Baz', 'editable' => 1, ), new EBox::Types::Union::Text( 'fieldName' => 'inverse_others', 'printableName' => 'Inverse Other option', 'editable' => 1, ), ] ), new EBox::Types::HasMany( 'fieldName' => 'member', 'printableName' => 'Members', 'foreignModel' => 'MemberTable', 'view' => '/Objects/View/MemberTable', 'backView' => '/Test/View/TestTable', 'size' => 1, ), new EBox::Types::Service( 'fieldName' => 'compulsory_service', 'printableName' => 'Compulsory Service', 'class' => 'tcenter', 'editable' => 1, ), ); my $dataTable = { 'tableName' => 'TestTable', 'printableTableName' => 'Test model', 'defaultController' => '/Test/Controller/TestTable', 'defaultActions' => [ 'add', 'del', 'editField', 'changeView' ], 'tableDescription' => \@tableHead, 'modelDomain' => 'Logs', 'class' => 'dataTable', 'order' => 0, 'help' => 'Test model to test types', 'rowUnique' => 0, 'printableRowName' => 'row', }; return $dataTable; } # Method: index # # Overrides: # # # sub index { my ($self) = @_; return $self->{runtimeIndex} if ( defined ( $self->{runtimeIndex} )); return ''; } # Callback functions: # Function: compulsoryOptionsCallback # # Get the options for the compulsory_select field # # Returns: # # array ref - containing hash ref with the following elements: # - value # - printableValue # sub compulsoryOptionsCallback { return [ { value => 'a', printableValue => 'A' }, { value => 'b', printableValue => 'B' }, { value => 'c', printableValue => 'C' }, ]; } # Function: optionalOptionsCallback # # Get the options for the optional_select field # # Returns: # # array ref - containing hash ref with the following elements: # - value # - printableValue # sub optionalOptionsCallback { return [ { value => '1', printableValue => 1 }, { value => '2', printableValue => 2 }, { value => '5', printableValue => 5 }, ]; } # Function: objectModelCallback # # Get the object model to select one of the objects # # Returns: # # - the object model # sub objectModelCallback { if ( EBox::Global->modExists('objects') ) { return EBox::Global->modInstance('objects')->models()->[0]; } else { return undef; } } 1; zentyal-core-2.3.21+quantal1/src/EBox/Test/Form.pm0000664000000000000000000003073212017102272016430 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Test::Form # # This class is used as an example for EBox::Model::DataForm # # It subclasses # package EBox::Test::Form; use base 'EBox::Model::DataForm'; use strict; use warnings; use EBox::Gettext; use EBox::Types::Boolean; use EBox::Types::HasMany; use EBox::Types::Int; use EBox::Types::InverseMatchSelect; use EBox::Types::InverseMatchUnion; use EBox::Types::IPAddr; use EBox::Types::Link; use EBox::Types::MACAddr; use EBox::Types::Password; use EBox::Types::PortRange; use EBox::Types::Select; use EBox::Types::Service; use EBox::Types::Text; use EBox::Types::Union; use EBox::Types::Union::Text; sub new { my $class = shift; my %parms = @_; my $self = $class->SUPER::new(@_); bless($self, $class); return $self; } # Method: _table # # This method overrides to return # a table model description. # sub _table { my @tableHead = ( new EBox::Types::IPAddr( 'fieldName' => 'compulsory_addr', 'printableName' => 'Compulsory IP Address', 'class' => 'tcenter', 'size' => '12', 'editable' => 1, 'optional' => 0, 'defaultValue' => '192.168.45.1/32', ), new EBox::Types::Boolean( 'fieldName' => 'compulsory_boolean', 'printableName' => 'Compulsory Boolean', 'class' => 'tcenter', 'size' => '1', 'editable' => 1, 'optional' => 0, 'defaultValue' => 1, ), new EBox::Types::Int( 'fieldName' => 'compulsory_int', 'printableName' => 'Compulsory Integer', 'class' => 'tcenter', 'size' => '1', 'editable' => 1, 'optional' => 0, 'defaultValue' => 11, ), new EBox::Types::Select( 'fieldName' => 'compulsory_select', 'printableName' => 'Compulsory Select', 'class' => 'tcenter', 'size' => '1', 'editable' => 1, 'populate' => \&compulsoryOptionsCallback, 'defaultValue' => 'b', ), new EBox::Types::Text( 'fieldName' => 'compulsory_text', 'printableName' => 'Compulsory Text', 'class' => 'tcenter', 'size' => '10', 'editable' => 1, 'optional' => 0, 'defaultValue' => 'foo', ), new EBox::Types::MACAddr( 'fieldName' => 'compulsory_mac', 'printableName' => 'Compulsory MAC Address', 'class' => 'tcenter', 'size' => '10', 'editable' => 1, 'optional' => 0, 'defaultValue' => '00:0C:29:AD:B4:60', ), new EBox::Types::Password( 'fieldName' => 'compulsory_password', 'printableName' => 'Compulsory Password', 'class' => 'tcenter', 'size' => '10', 'editable' => 1, 'optional' => 0, 'minLength' => 5, 'maxLength' => 10, 'defaultValue' => 'foobar', ), new EBox::Types::PortRange( 'fieldName' => 'port_range', 'printableName' => 'Port range', 'class' => 'tcenter', 'size' => '5', 'editable' => 1, 'optional' => 0, 'defaultValue' => '2132', ), new EBox::Types::Union( 'fieldName' => 'union', 'printableName' => 'Union', 'class' => 'tcenter', 'size' => 10, 'editable' => 1, 'subtypes' => [ new EBox::Types::Text( 'fieldName' => 'foo', 'printableName' => 'Foo', 'editable' => 1, ), new EBox::Types::PortRange( 'fieldName' => 'bar', 'printableName' => 'Bar', 'editable' => 1, 'defaultValue' => '2000:2001', ), new EBox::Types::IPAddr( 'fieldName' => 'baz', 'printableName' => 'Baz', 'editable' => 1, ), new EBox::Types::Union::Text( 'fieldName' => 'others', 'printableName' => 'Other option', ), ] ), new EBox::Types::InverseMatchSelect( 'fieldName' => 'inverse_select', 'printableName' => 'Inverse Match Select', 'class' => 'tcenter', 'size' => '11', 'editable' => 1, 'populate' => \&compulsoryOptionsCallback, 'optional' => 0, 'defaultValue' => 'c', ), new EBox::Types::InverseMatchUnion( 'fieldName' => 'inverse_union', 'printableName' => 'Inverse Match Union', 'class' => 'tcenter', 'size' => 10, 'editable' => 1, 'subtypes' => [ new EBox::Types::Text( 'fieldName' => 'inverse_foo', 'printableName' => 'Inverse Foo', 'editable' => 1, ), new EBox::Types::PortRange( 'fieldName' => 'inverse_bar', 'printableName' => 'Inverse Bar', 'editable' => 1, 'defaultValue' => '19201', ), new EBox::Types::IPAddr( 'fieldName' => 'inverse_baz', 'printableName' => 'Inverse Baz', 'editable' => 1, ), new EBox::Types::Union::Text( 'fieldName' => 'inverse_others', 'printableName' => 'Inverse Other option', 'editable' => 1, ), ] ), new EBox::Types::Service( 'fieldName' => 'compulsory_service', 'printableName' => 'Compulsory Service', 'class' => 'tcenter', 'editable' => 1, 'defaultValue' => '1010/udp', ), ); my $dataTable = { 'tableName' => 'TestForm', 'printableTableName' => 'Test form', 'defaultController' => '/Test/Controller/TestForm', 'defaultActions' => [ 'editField', 'changeView' ], 'tableDescription' => \@tableHead, 'modelDomain' => 'Logs', 'class' => 'dataForm', 'help' => 'Test form to test types', }; return $dataTable; } # Callback functions: # Function: compulsoryOptionsCallback # # Get the options for the compulsory_select field # # Returns: # # array ref - containing hash ref with the following elements: # - value # - printableValue # sub compulsoryOptionsCallback { return [ { value => 'a', printableValue => 'A' }, { value => 'b', printableValue => 'B' }, { value => 'c', printableValue => 'C' }, ]; } # Function: optionalOptionsCallback # # Get the options for the optional_select field # # Returns: # # array ref - containing hash ref with the following elements: # - value # - printableValue # sub optionalOptionsCallback { return [ { value => '1', printableValue => 1 }, { value => '2', printableValue => 2 }, { value => '5', printableValue => 5 }, ]; } # Function: objectModelCallback # # Get the object model to select one of the objects # # Returns: # # - the object model # sub objectModelCallback { return EBox::Global->modInstance('objects')->models()->[0]; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/0000775000000000000000000000000012017102272015347 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Types/Port.pm0000664000000000000000000000317512017102272016637 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Types::Port; use base 'EBox::Types::Int'; # use strict; use warnings; use EBox::Validate; sub new { my $class = shift; my $self = $class->SUPER::new(@_); $self->{type} = 'port'; bless($self, $class); return $self; } # Method: size # # Overrides: # # # sub size { return 6; } # Method: _paramIsValid # # Check if the params has a correct port # # Overrides: # # # # Parameters: # # params - the HTTP parameters with contained the type # # Returns: # # true - if the parameter is a correct pot # # Exceptions: # # - throw if it's not a correct # port # sub _paramIsValid { my ($self, $params) = @_; my $value = $params->{$self->fieldName()}; if (defined ( $value )) { EBox::Validate::checkPort($value, $self->printableName()); } return 1; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/Int.pm0000664000000000000000000000710512017102272016442 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Types::Int; use strict; use warnings; use base 'EBox::Types::Basic'; use EBox::Exceptions::External; use EBox::Gettext; # Group: Public methods # Method: new # # Parameters: # (in addition of base classes parameters) # max - maximum integer value allowed # min - minimum integer value allowed (default: 0) sub new { my $class = shift; my %opts = @_; unless (exists $opts{'HTMLSetter'}) { $opts{'HTMLSetter'} ='/ajax/setter/textSetter.mas'; } unless (exists $opts{'HTMLViewer'}) { $opts{'HTMLViewer'} ='/ajax/viewer/textViewer.mas'; } # default min value is zero unless (exists $opts{'min'}) { $opts{'min'} = 0; } if (exists $opts{max}) { if (not ($opts{max} > $opts{min}) ) { throw EBox::Exceptions::Internal( 'Maximum value must be greater than minimum value' ); } } $opts{'type'} = 'int'; my $self = $class->SUPER::new(%opts); bless($self, $class); return $self; } sub size { my ($self) = @_; return $self->{'size'}; } # Method: cmp # # Overrides: # # # sub cmp { my ($self, $other) = @_; unless ( ref($self) eq ref($other) ) { return undef; } return $self->value() <=> $other->value(); } # Method: max # # Returns: # the maximum value allowed (undef means no maximum) # sub max { my ($self) = @_; return $self->{max}; } # Method: min # # Returns: # # the minimum value allowed (default: 0) # sub min { my ($self) = @_; return $self->{min}; } # Group: Protected methods # Method: _paramIsValid # # Overrides: # # # sub _paramIsValid { my ($self, $params) = @_; my $value = $params->{$self->fieldName()}; unless ($value =~ /^-?[0-9]+$/) { throw EBox::Exceptions::InvalidData( data => $self->printableName(), value => $value, advice => __('Enter an integer number')); } my $max = $self->max(); if (defined $max and ($value > $max)) { throw EBox::Exceptions::InvalidData( data => $self->printableName(), value => $value, advice => __x(q|The value shouldn't be greater than {m}|, m => $max) ); } my $min = $self->min(); if (defined $min and ($value < $min)) { throw EBox::Exceptions::InvalidData( data => $self->printableName(), value => $value, advice => __x(q|The value shouldn't be less than {m}|, m => $min) ); } return 1; } # Method: _paramIsSet # # Overrides: # # # sub _paramIsSet { my ($self, $params) = @_; # Check if the parameter exist my $param = $params->{$self->fieldName()}; return defined ($param) and ($param ne ''); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/DomainName.pm0000664000000000000000000000540612017102272017722 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Types::DomainName # # A specialised text type to represent an domain name # package EBox::Types::DomainName; use strict; use warnings; use base 'EBox::Types::Text'; use EBox::Validate; use EBox::Gettext; # Group: Public methods # Constructor: new # # The constructor for the # # Returns: # # the recently created object # sub new { my $class = shift; my $self = $class->SUPER::new(@_); $self->{'type'} = 'domainname'; bless($self, $class); return $self; } # Method: isEqualTo # # Overrides: # # # sub isEqualTo { my ($self, $compareType) = @_; return ($self->cmp($compareType) == 0); } # Method: cmp # # Overrides: # # # sub cmp { my ($self, $compareType) = @_; unless ( (ref $self) eq (ref $compareType) ) { return undef; } return uc($self->value()) cmp uc($compareType->value()); } # Group: Protected methods # Method: _paramIsValid # # Check if the params has a correct domain name # # Overrides: # # # # Parameters: # # params - the HTTP parameters with contained the type # # Returns: # # true - if the parameter is a correct domainName # # Exceptions: # # - throw if it's not a correct # host IP address # sub _paramIsValid { my ($self, $params) = @_; my $value = $params->{$self->fieldName()}; if (defined ( $value )) { EBox::Validate::checkDomainName($value, $self->printableName()); my $seemsIp = EBox::Validate::checkIP($value); if ($seemsIp) { throw EBox::Exceptions::InvalidData ('data' => $self->printableName(), 'value' => $value, 'advice' => __('IP addresses are not allowed'), ); } } return 1; } sub memValue { my ($self) = @_; my $value = $self->{'value'}; if ($value) { $value =~ s/\.$//; } return $value; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/Select.pm0000664000000000000000000002633112017102272017131 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Types::Select; use strict; use warnings; use base 'EBox::Types::Basic'; use EBox; use EBox::Gettext; use EBox::Exceptions::Internal; use EBox::Exceptions::MissingArgument; ################## # Dependencies: ################## use Perl6::Junction qw(any); use constant ADD_NEW_MODAL_VALUE => '_addNew'; # Group: Public methods sub new { my $class = shift; my %opts = @_; unless (exists $opts{'HTMLSetter'}) { $opts{'HTMLSetter'} ='/ajax/setter/selectSetter.mas'; } unless (exists $opts{'HTMLViewer'}) { $opts{'HTMLViewer'} ='/ajax/viewer/selectViewer.mas'; } unless (exists $opts{'disableCache'}) { $opts{'disableCache'} = 0; } unless (exists $opts{'compareContext'}) { $opts{'compareContext'} = 'default'; } $opts{'type'} = 'select'; my $self = $class->SUPER::new(%opts); # This doesn't check if the option method is implemented # unless ($self->{populate} or $self->{options} or $self->{foreignModel}) { # throw EBox::Exceptions::MissingArgument('populate or foreignModel'); #} if ($self->{foreignModel} and (not $self->{foreignField})) { throw EBox::Exceptions::MissingArgument('foreignField'); } if (scalar $self->{editable} and not $self->{editable}) { throw EBox::Exceptions::Internal( 'Select ' . $self->fieldName() . ' should be ' . 'editable. If you want a read only field, use ' . 'text type instead.' ); } if ($self->optional() and not $self->isa('EBox::Types::InverseMatchSelect') and not $self->isa('EBox::Types::MultiSelect')) { throw EBox::Exceptions::Internal('Select ' . $self->fieldName() . ' must be compulsory'); } bless($self, $class); return $self; } sub size { my ($self) = @_; return $self->{'size'}; } # Method: disableCache # # Return if we must disable the options cache. # For performance reasons, eBox caches the options given for a select type # # Returns: # # boolean - true means it mustn't cache, false it will cache # By default, it will return false # sub disableCache { my ($self) = @_; return $self->{disableCache}; } # Method: setDisableCache # # Set if we must disable the options cache. # For performance reasons, eBox caches the options given for a select type # By default, it will cache # # Parameters: # # boolean - true means it mustn't cache, false it will cache # sub setDisableCache { my ($self, $disable) = @_; $self->{disableCache} = $disable; } # Method: options # # Get the options from the select. It gets dynamically from a # foreign model if is defined # or from function defined in the # model template # # Returns: # # array ref - containing a hash ref with the following fields: # # - value - the id which identifies the option # - printableValue - the printable value for this option # sub options { my ($self) = @_; if ( exists $self->{'foreignModel'}) { $self->{'options'} = $self->_optionsFromForeignModel(); } else { if ((not exists $self->{'options'}) or $self->disableCache()) { my $populateFunc = $self->populate(); $self->{'options'} = &$populateFunc(); } } return $self->{'options'}; } # Method: printableValue # # Overrides: # # # sub printableValue { my ($self) = @_; # Cache the current options my $options = $self->options(); return '' unless (defined($options)); my $value = $self->value(); foreach my $option (@{$options}) { if ($option->{'value'} eq $value) { return $option->{'printableValue'}; } } } # Method: value # # Overrides: # # # sub value { my ($self) = @_; if (defined($self->{'value'})) { return $self->{'value'}; } else { my @options = @{$self->options()}; if (@options) { return $options[0]->{'value'}; } else { return undef; } } } # Method: foreignModel # # Return the foreignModel associated to the type # # Returns: # # object - an instance of class sub foreignModel { my ($self) = @_; my $foreignModel = $self->{'foreignModel'}; return undef unless (defined($foreignModel)); my $model = &$foreignModel($self); return $model; } sub foreignField { my ($self) = @_; if (not exists $self->{foreignField}) { return undef; } return $self->{foreignField}; } sub foreignNextPageField { my ($self) = @_; if (not exists $self->{foreignNextPageField}) { return undef; } return $self->{foreignNextPageField}; } # Method: populate # # Get the function pointer which populate options within the # select # # Return: # # function ref - the function which returns an array ref. This # array elements are the same that are returned by # . # sub populate { my ($self) = @_; unless ( defined ( $self->{'populate'} )) { throw EBox::Exceptions::Internal('No populate function has been ' . 'defined and it is required to fill ' . 'the select options'); } return $self->{'populate'}; } # Method: cmp # # # Warning: # We compare printableValues because it has more sense for the user # (especially when we have a foreignModel and the values are row Ids). # However there may be many cases when this would not be appropiate. # # Overrides: # sub cmp { my ($self, $other) = @_; if (ref($self) ne ref($other)) { return undef; } my $cmpContext = 0; if (defined $other->{cmpContext}) { $cmpContext = $self->{cmpContext} cmp $other->{cmpContext}; } if ($cmpContext == 0) { return ($self->printableValue() cmp $other->printableValue()); } else { return $cmpContext; } } # Method: isValueSet # # Check if the type has been set. You can't use value to do this because # it always defaults to the first value of options # # Return: # # boolean - true is set otherwise false # sub isValueSet { my ($self) = @_; return (defined($self->{'value'})); } # Group: Protected methods # Method: _paramIsValid # # Overrides: # # # sub _paramIsValid { my ($self, $params) = @_; my $value = $params->{$self->fieldName()}; # Check whether value is within the values returned by # populate callback function my @allowedValues = map { $_->{value} } @{$self->options()}; if (not @allowedValues) { if ($value eq ADD_NEW_MODAL_VALUE) { throw EBox::Exceptions::External( __x(q|{name} empty. You can add and select a new {name} with the 'add new' button|, name => $self->printableName(), ) ); } else { throw EBox::Exceptions::External( __x(q|{name} has not selectable values|, name => lcfirst $self->printableName(), ) ); } } # We're assuming the options value are always strings unless ( grep { $_ eq $value } @allowedValues ) { throw EBox::Exceptions::InvalidData( data => $self->printableName(), value => $value, advice => __x('Choose a value within the value set: {set}', set => join(', ', @allowedValues)) ); } return 1; } # Method: _paramIsSet # # Overrides: # # # sub _paramIsSet { my ($self, $params) = @_; # Check if the parameter exist my $param = $params->{$self->fieldName()}; return defined ( $params->{$self->fieldName()} ); } # Method: _setValue # # Set the value for the select. It allows to the select not only # use default options using populate function but also a field # from the foreign model indicating the printable value or the # row identifier # # Overrides: # # # sub _setValue { my ($self, $value) = @_; my $params; my $mappedValue = $value; if ( defined($self->foreignModel()) ) { # Map the given printable value to the real value to store in # GConf my $options = $self->_optionsFromForeignModel(); foreach my $option (@{$options}) { if ( $option->{printableValue} eq $value ) { $mappedValue = $option->{value}; last; } elsif ( $option->{value} eq $value ) { last; } } } $params = { $self->fieldName() => $mappedValue }; $self->setMemValue($params); } # Group: Private helper functions # Method: _optionsFromForeignModel # # (PRIVATE) # # This method is used to fetch options values from a foreign model # # sub _optionsFromForeignModel { my ($self) = @_; my $model = $self->foreignModel(); my $field = $self->{'foreignField'}; return unless (defined($model) and defined($field)); return $model->optionsFromForeignModel($field); } # Method: _filterOptions # # Given a set of available options, returns the ones which the user # may use. This method is done at selectSetter.mas due to deep # recursion using rows here. # sub _filterOptions { my ($self, $options) = @_; my $model = $self->model(); return $options unless defined ( $model ); my $field = $self->fieldName(); my @optionsAlreadyModel = (); foreach my $id (@{$model->ids()}) { my $row = $model->row($id); push( @optionsAlreadyModel, $row->valueByName($field)); } # Difference among optionsAlreadyModel and options arrays my @filteredOptions = grep { $_->{value} ne any(@optionsAlreadyModel) } @{$options}; # Add the current value if the action is an edition if ( $self->value() ) { push ( @filteredOptions, { value => $self->value(), printableValue => $self->printableValue(), } ); } return \@filteredOptions; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/Composite.pm0000664000000000000000000001601212017102272017647 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Types::Composite # # This type is a type composite which consists of other # types. This type is very useful as subtype in # . # # This type is useful in the following use case. You may require # to have 3 subtypes within a Union type and one of them is # complex, that is, it's comprised more than one type. For this # reason, Composite type is created. # package EBox::Types::Composite; use strict; use warnings; use base 'EBox::Types::Abstract'; use EBox::Exceptions::Internal; # Dependencies use Clone::Fast; use Perl6::Junction qw(none); # Group: Public methods # Constructor: new # # The constructor for the type # # Returns: # # - the union text recently created # object # sub new { my $class = shift; my %opts = @_; unless (exists $opts{'HTMLSetter'}) { $opts{'HTMLSetter'} ='/ajax/setter/composite.mas'; } unless (exists $opts{'HTMLViewer'}) { $opts{'HTMLViewer'} ='/ajax/viewer/composite.mas'; } my $self = $class->SUPER::new(%opts); $self->{'type'} = 'composite'; $self->{'types'} = $opts{'types'}; if (not defined ( $self->{'types'})) { throw EBox::Exceptions::Internal('Composite types must have "types" attribute'); } bless($self, $class); return $self; } # Method: clone # # Overrides: # # # sub clone { my ($self) = @_; my $clonedType = {}; bless($clonedType, ref($self)); my @suspectedAttrs = qw(model row types); foreach my $key (keys %{$self}) { if ( $key eq none(@suspectedAttrs) ) { $clonedType->{$key} = Clone::Fast::clone($self->{$key}); } } # Just copy the reference to the suspected attributes foreach my $suspectedAttr (@suspectedAttrs[0 .. 1]) { if ( exists $self->{$suspectedAttr} ) { $clonedType->{$suspectedAttr} = $self->{$suspectedAttr}; } } # Clone types by calling its method clone foreach my $subtype (@{$self->types()}) { push(@{$clonedType->{types}}, $subtype->clone()); } return $clonedType; } # Method: fields # # Overrides: # # # sub fields { my ($self) = @_; my @fields; foreach my $simpleType (@{$self->types()}) { push ( @fields, $simpleType->fields() ); } return @fields; } # Method: value # # Overrides: # # # # Returns: # # hash ref - containing each value for each simple type indexed # by the field name # sub value { my ($self) = @_; my %values; foreach my $simpleType (@{$self->types()}) { $values{$simpleType->fieldName()} = $simpleType->value(); } return \%values; } # Method: cmp # # Overrides: # # # # Returns: # # -1 - if all simpler types from self are lower than compareType # # 0 - if all simpler types from self are equal to compareType # # 1 - if all simpler types from self are higher than compareType # # undef - otherwise # sub cmp { my ($self, $compareType) = @_; return undef unless ( $self->type() eq $compareType->type() ); my @selfTypes = @{$self->types()}; my @comparedTypes = @{$compareType->types()}; return undef unless ( scalar(@selfTypes) == scalar(@comparedTypes) ); my $returnValue = undef; for( my $idx = 0; $idx < $#selfTypes; $idx++) { my $singleCmp = $selfTypes[$idx]->cmp($comparedTypes[$idx]); if ( not defined($returnValue) ) { $returnValue = $singleCmp; } else { return undef unless ($singleCmp == $returnValue); } } return $returnValue; } # Method: types # # Accessor to the simple types which consist of this composite # type # # Returns: # # array ref - containing instances of # class # sub types { my ($self) = @_; return $self->{types}; } # Method: showTypeName # # Accessor to the property which determines whether to show the # printable type name or not just helping the viewer/setter to # make beautiful viewer # # Returns: # # boolean - true if we want to show the type printable name, false # otherwise # sub showTypeName { my ($self) = @_; return $self->{showTypeName}; } # Protected Methods # Method: _setMemValue # # Overrides: # # # sub _setMemValue { my ($self, $params) = @_; $self->_callTypeMethod('_setMemValue', $params); } # Method: _restoreFromHash # # Overrides: # # # sub _restoreFromHash { my ($self, $hash) = @_; $self->_callTypeMethod('_restoreFromHash', $hash); } # Method: _storeInHash # # Overrides: # # # sub _storeInHash { my ($self, $hash) = @_; $self->_callTypeMethod('_storeInHash', $hash); } # Method: _paramIsValid # # Overrides: # # # sub _paramIsValid { my ($self, $params) = @_; $self->_callTypeMethod('_paramIsValid', $params); return 1; } # Method: _paramIsSet # # Overrides: # # # # Returns: # # true - if all simple types are set # # false - otherwise # sub _paramIsSet { my ($self, $params) = @_; foreach my $type ( @{$self->types()} ) { my $isSet = $type->_paramIsSet($params); unless ( $isSet ) { return 0; } } return 1; } # Method: _setValue # # Overrides: # # # # Parameters: # # value - array ref containing a value for each type to set its # value using its own _setValue method # sub _setValue { my ($self, $arrayValue) = @_; my @simpleTypes = @{$self->types()}; for (my $idx; $idx < scalar(@simpleTypes); $idx++) { my $simpleValue = $arrayValue->[$idx]; my $simpleType = $simpleTypes[$idx]; $simpleType->_setValue($simpleValue); } } # Group: Private methods # Call given method to every type which lives inside the Composite # type sub _callTypeMethod # (methodName, args) { my ($self, $methodName, @args) = @_; foreach my $simpleType ( @{$self->types()} ) { $simpleType->setRow($self->row()); $simpleType->$methodName(@args); } return; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/PortRange.pm0000664000000000000000000002501012017102272017604 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Types::PortRange # # Class to represent a type to store port ranges. # It has three possible values, the type of value stored is # stored in "range_type" and it can be: # # "any" - it means all ports # # "single_port" - just one port stored in single_port # # "port_range" - two ports to indicate a range "from_port" and "to_port" # # Use portRange() to check which type of port it has. # Use from(), to() to fetch port range. # Use single() to fetch single port. # Or you can use printableValue() to fecth an output which is nicely accepted # by iptables. package EBox::Types::PortRange; use strict; use warnings; use base 'EBox::Types::Abstract'; use EBox; use EBox::Validate qw(checkPort); use EBox::Exceptions::MissingArgument; use EBox::Exceptions::External; use EBox::Gettext; # Constructor: new # # Create a type which includes the protocol and the port from # Internet as a service # # Returns: # # a object # sub new { my $class = shift; my %opts = @_; unless (exists $opts{'HTMLSetter'}) { $opts{'HTMLSetter'} ='/ajax/setter/portRangeSetter.mas'; } unless (exists $opts{'HTMLViewer'}) { $opts{'HTMLViewer'} ='/ajax/viewer/textViewer.mas'; } if ( defined ( $opts{'optional'} ) and $opts{'optional'} ) { throw EBox::Exceptions::Internal( q{PortRange type cannot be optional. You should select 'any' value} ); } $opts{'type'} = 'portrange'; my $self = $class->SUPER::new(%opts); my $selectedDefault = $opts{'defaultSelectedType'}; $selectedDefault = 'any' unless defined ($selectedDefault); $self->{'range_type'} = $selectedDefault unless defined ($self->{'range_type'}); bless($self, $class); return $self; } # Method: paramExist # # Overrides method # sub paramExist { my ($self, $params) = @_; my $name = $self->fieldName(); my $type = $params->{$name . '_range_type'}; return undef unless($type); return 1 if ($type eq 'any'); if ($type eq 'range') { return undef unless(exists $params->{$name . '_from_port'} and exists $params->{$name . '_to_port'}); } else { return undef unless (exists $params->{$name . '_single_port'}); } } # Method: value # # Overrides method # sub value { my ($self) = @_; my $type = $self->rangeType(); if ($type eq 'range') { return $self->from() . ':' . $self->to() ; } elsif ($type eq 'single') { return $self->single(); } else { return 'any'; } } # Method: printableValue # # Overrides method # sub printableValue { my ($self) = @_; my $value = $self->value(); if ($value eq 'any') { return __('any'); } return $value; } # Method: compareToHash # # Overrides method # sub compareToHash { my ($self, $hash) = @_; my $name = $self->fieldName(); my $type = $hash->{$name . '_range_type'}; my $from = $hash->{$name . '_from_port'}; my $to = $hash->{$name . '_to_port'}; my $single = $hash->{$name . '_single_port'}; if ($self->rangeType() ne $type) { return 0; } if ($self->rangeType() eq 'single') { return 0 if ($single ne $self->single()); } if ($self->rangeType() eq 'range') { return 0 if (($from ne $self->from()) or ($to ne $self->to())); } return 1; } # Method: cmp # # Overrides method # sub cmp { my ($self, $other) = @_; if ((ref $self) ne (ref $other)) { return undef; } return ($self->printableValue() cmp $other->printableValue()); } # Method: fields # # Overrides method # sub fields { my ($self) = @_; my $name = $self->fieldName(); my $type = $name . '_range_type'; my $from = $name . '_from_port'; my $to = $name . '_to_port'; my $single = $name . '_single_port'; return ($type, $from, $to, $single); } ### # Own methods ### # Method: rangeTtype # # Return the port range type which can be: # # any # single # range # # Returns: # # string containing the type sub rangeType { my ($self) = @_; return $self->{'range_type'}; } # Method: from # # Return the "from" port # # Returns: # # string - containing the port sub from { my ($self) = @_; return $self->{'from'}; } # Method: to # # Return the "to" port # # Returns: # # string - containing the port sub to { my ($self) = @_; return $self->{'to'}; } # Method: single # # Return the single port # # Returns: # # string - containing the port sub single { my ($self) = @_; return $self->{'single'}; } # Method: rangeTypes # # Get the range available (Static method) # # Returns: # # array ref - the range types in a hash with the following elements # - value - the range name # - printableValue - the protocol printable name # sub rangeTypes { my ($self) = @_; my @rangeTypes = ( { value => 'any', printableValue => __('Any'), }, { value => 'single', printableValue => __('Single port'), }, { value => 'range', printableValue => __('Port range'), }, ); return \@rangeTypes; } # Group: Protected methods # Method: _setMemValue # # Overrides: # # # sub _setMemValue { my ($self, $params) = @_; my $name = $self->fieldName(); $self->{'range_type'} = $params->{$name . '_range_type'}; $self->{'from'} = $params->{$name . '_from_port'}; $self->{'to'} = $params->{$name . '_to_port'}; $self->{'single'} = $params->{$name . '_single_port'}; } # Method: _storeInHash # # Overrides: # # # sub _storeInHash { my ($self, $hash) = @_; my $type = $self->fieldName() . '_range_type'; my $from = $self->fieldName() . '_from_port'; my $to = $self->fieldName() . '_to_port'; my $single = $self->fieldName() . '_single_port'; map { delete $hash->{$_} } ($from, $to, $single); my $rangeType = $self->rangeType(); $hash->{$type} = $rangeType; if ($rangeType eq 'range') { $hash->{$from} = $self->from(); $hash->{$to} = $self->to(); } elsif ($rangeType eq 'single') { $hash->{$single} = $self->single(); } } # Method: _restoreFromHash # # Overrides: # # # sub _restoreFromHash { my ($self, $hash) = @_; return unless ($self->row()); my $range = $self->fieldName() . '_range_type'; my $from = $self->fieldName() . '_from_port'; my $to = $self->fieldName() . '_to_port'; my $single = $self->fieldName() . '_single_port'; $self->{'range_type'} = $hash->{$range}; $self->{'from'} = $hash->{$from}; $self->{'to'} = $hash->{$to}; $self->{'single'} = $hash->{$single}; } # Method: _paramIsValid # # Overrides: # # # sub _paramIsValid { my ($self, $params) = @_; my $name = $self->fieldName(); my $type = $params->{$name . '_range_type'}; return 1 if ($type eq 'any'); if ($type eq 'range') { my $from = $params->{$name . '_from_port'}; my $to = $params->{$name . '_to_port'}; checkPort($from, $self->printableName()); checkPort($to, $self->printableName()); if ($to < $from) { throw EBox::Exceptions::InvalidData( data => $self->printableName(), value => $from . ':' . $to, advice => __x('"From" {from} must be greater than "To" {to}', from => $from, to => $to)); } } else { checkPort($params->{$name . '_single_port'}, $self->printableName()); } return 1; } # Method: _paramIsSet # # Overrides: # # # sub _paramIsSet { my ($self, $params) = @_; my $name = $self->fieldName(); my $type = $params->{$name . '_range_type'}; return undef unless($type); return 1 if ($type eq 'any'); if ($type eq 'range') { return undef unless(exists $params->{$name . '_from_port'} and ($params->{$name . '_from_port'} ne '') and exists $params->{$name . '_to_port'} and ($params->{$name . '_to_port'}) ne ''); } else { return undef unless (exists $params->{$name . '_single_port'} and ($params->{$name . '_single_port'} ne '')); } return 1; } # Method: _setValue # # Set the value defined as a string in the # printableValue. That is, to define a port range, you can choose # one of following: # # any - any range # [0-9]+ - single port # [0-9]+:[8-9]+ - from:to port range # # Overrides: # # # # Parameters: # # value - String as defined above # sub _setValue # (defaultValue) { my ($self, $value) = @_; my $params = {}; if ( $value eq 'any' ) { $params->{$self->fieldName() . '_range_type'} = 'any'; } elsif ( $value =~ m/^[0-9]+$/g ) { $params->{$self->fieldName() . '_range_type'} = 'single'; $params->{$self->fieldName() . '_single_port'} = $value; } else { my ($from, $to) = split ( ':', $value); $params->{$self->fieldName() . '_range_type'} = 'range'; $params->{$self->fieldName() . '_from_port'} = $from; $params->{$self->fieldName() . '_to_port'} = $to; } $self->setMemValue($params); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/Union.pm0000664000000000000000000002676112017102272017011 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # TODO # * Optimize class. Use reference to fetch selected type # instead of transverse array. # # * Support automatic unique check # package EBox::Types::Union; use strict; use warnings; use base 'EBox::Types::Abstract'; use EBox; use EBox::Exceptions::Internal; # Dependencies use Clone::Fast; use Perl6::Junction qw(none); # It's a package global our $AUTOLOAD; # Group: Public methods sub new { my $class = shift; my %opts = @_; my $self = $class->SUPER::new(@_); $self->{'type'} = 'union'; # Union type cannot be optional if ( $self->{'optional'} ) { EBox::warn('Union type cannot be optional. To use the non-defined ' . 'value, choose EBox::Types::Union::Text'); } $self->{'optional'} = 0; # Union type must contain more than one subtype unless (@{$self->{'subtypes'}} > 1) { throw EBox::Exceptions::Internal("Union type: $self->{'fieldName'}" . " must contain more than one subtype"); } bless($self, $class); return $self; } # Method: clone # # Overrides: # # # sub clone { my ($self) = @_; my $clonedType = {}; bless($clonedType, ref($self)); my @suspectedAttrs = qw(model row subtypes); foreach my $key (keys %{$self}) { if ( $key eq none(@suspectedAttrs) ) { $clonedType->{$key} = Clone::Fast::clone($self->{$key}); } } # Just copy the reference foreach my $suspectedAttr (@suspectedAttrs[0 .. 1]) { if ( exists $self->{$suspectedAttr} ) { $clonedType->{$suspectedAttr} = $self->{$suspectedAttr}; } } # Clone subtypes by calling its method clone foreach my $subtype (@{$self->subtypes()}) { push(@{$clonedType->{'subtypes'}}, $subtype->clone()); } return $clonedType; } sub subtype { my ($self) = @_; my $selected = $self->selectedType(); foreach my $type (@{$self->{'subtypes'}}) { if ($type->fieldName() eq $selected) { return $type; } } return ""; } # Method: selectedType # # Get the selected type field name from the union. If the user # has not selected one explicitly, choose the first declared # # Returns: # # String - the selected type field name # sub selectedType { my ($self) = @_; if (not $self->{'selectedField'}) { my @subtypes = @{$self->{'subtypes'}}; if (@subtypes > 0) { return $subtypes[0]->fieldName(); } else { return undef; } } else { return $self->{'selectedField'}; } } sub setSelectedType { my ($self, $field) = @_; $self->{'selectedField'} = $field; } sub subtypes { my ($self) = @_; return $self->{'subtypes'}; } sub unique { my ($self) = @_; my @subtypes = @{$self->{'subtypes'}}; foreach my $subtype (@subtypes) { unless ( $subtype->unique() ) { return 0; } } return 1; } sub fields { my ($self) = @_; my @fields; foreach my $type (@{$self->{'subtypes'}}) { push (@fields, $type->fields()); } push (@fields, $self->fieldName() . '_selected'); return @fields; } sub setModel { my ($self, $model) = @_; $self->SUPER::setModel($model); $AUTOLOAD = 'setModel'; return $self->AUTOLOAD($model); } sub setRow { my ($self, $row) = @_; $self->SUPER::setRow($row); # Call AUTOLOAD method in order not to repeat code $AUTOLOAD = 'setRow'; return $self->AUTOLOAD($row); } sub paramExist { my ($self, $params) = @_; my $selPar = $self->fieldName() . '_selected'; my $selected = $params->{$selPar}; if ( (not defined ( $selected )) and $self->optional() ) { return 1; } return 0 unless (defined($selected)); foreach my $type (@{$self->{'subtypes'}}) { next unless ($type->fieldName() eq $selected); # If type has no setter, parameter is not required anyway return 1 unless( $type->HTMLSetter() ); return $type->paramExist($params); } return 0; } # Method: trailingText # # Overrides to show the trailing text from the selected subtype # # Overrides: # # # sub trailingText { my ($self) = @_; my $trailing = $self->SUPER::trailingText(); unless ( $trailing ) { $AUTOLOAD = 'trailingText'; $trailing = $self->AUTOLOAD(); } return $trailing; } sub printableValue { my ($self) = @_; # Call AUTOLOAD method in order not to repeat code $AUTOLOAD = 'printableValue'; return $self->AUTOLOAD(); } sub value { my ($self) = @_; # Call AUTOLOAD method in order not to repeat code $AUTOLOAD = 'value'; return $self->AUTOLOAD(); } sub memValue { } sub compareToHash { } # Method: isEqualTo # # Overrides: # # # sub isEqualTo { my ($self, $newObject) = @_; my $comparison = $self->cmp($newObject); if ( defined($comparison) ) { return $comparison == 0; } else { return 0; } } # Method: HTMLSetter # # Set the mason template to set the value for the type. # # It returns undef when all subtypes within has no setter. # # Overrides: # # # # Returns: # # String - the path to the mason template which contains the code # to set the value for this type # undef - if all subtypes have no setter # sub HTMLSetter { my ($self) = @_; return undef if $self->{'hiddenOnSetter'} or $self->{'hidden'}; my $definedSetter = 0; foreach my $type (@{$self->{'subtypes'}}) { next unless ( defined ( $type->HTMLSetter() )); $definedSetter = 1; last; } if ( $definedSetter ) { return '/ajax/setter/unionSetter.mas'; } else { return undef; } } sub HTMLViewer { my ($self) = @_; return undef if $self->{'hiddenOnViewer'} or $self->{'hidden'}; # Call AUTOLOAD method in order not to repeat code $AUTOLOAD = 'HTMLViewer'; return $self->AUTOLOAD(); } # Function: AUTOLOAD # # Special function called when an undefined method is called # within this package. The method name is stored at $AUTOLOAD # global variable. # # Known subclass methods: HTMLViewer, linkToView # # Parameters: # # self - the object # # parameters - Array containing the remainder parameters got from the # method call # sub AUTOLOAD { my ($self, @params) = @_; my $methodName = $AUTOLOAD; # Remove namespaces $methodName =~ s/.*:://; # Ignore DESTROY callings (the Perl destructor) if ( $methodName eq 'DESTROY' ) { return; } # setRow and setModel must be run for all subtypes if ($methodName eq 'setRow' or $methodName eq 'setModel') { foreach my $subtype (@{$self->{'subtypes'}}) { $subtype->$methodName(@params); } } # Call the method from the selected type my $selected = $self->selectedType(); unless ( defined ( $selected )) { throw EBox::Exceptions::Internal('There is no selected type ' . "to call its own method $methodName"); } foreach my $subtype (@{$self->{'subtypes'}}) { next unless ($subtype->fieldName() eq $selected); # Check if the method is defined if ( $subtype->can($methodName)) { return $subtype->$methodName(@params); } else { throw EBox::Exceptions::Internal("Method $methodName is not defined " . 'in type ' . $subtype->type()); } } } # Group: Protected methods # Method: _setMemValue # # Overrides: # # # sub _setMemValue { my ($self, $params) = @_; my $selPar = $self->fieldName() . '_selected'; my $selected = $params->{$selPar}; if (defined ($selected)) { foreach my $type (@{$self->{'subtypes'}}) { if ($type->fieldName() eq $selected) { $type->setMemValue($params); $self->setSelectedType($selected); } } } } # Method: _storeInHash # # Overrides: # # # sub _storeInHash { my ($self, $hash) = @_; my $selected = $self->selectedType(); foreach my $type (@{$self->{'subtypes'}}) { # Every union type should be stored in order to unset its # value if it has not got one $type->storeInHash($hash); if ($type->fieldName() eq $selected) { my $selected = $self->fieldName() . '_selected'; $hash->{$selected} = $self->selectedType(); } } } # Method: _restoreFromHash # # Overrides: # # # sub _restoreFromHash { my ($self, $hash) = @_; return unless ($self->row()); my $selPar = $self->fieldName() . '_selected'; my $selected = $hash->{$selPar}; return unless (defined($selected)); foreach my $type (@{$self->{'subtypes'}}) { next unless ($type->fieldName() eq $selected); $type->restoreFromHash($hash); $self->setSelectedType($selected); } } # Method: _paramIsValid # # Overrides: # # # sub _paramIsValid { return 1; } # Method: _paramIsSet # # Overrides: # # # sub _paramIsSet { my ($self, $params) = @_; my $selPar = $self->fieldName() . '_selected'; my $selected = $params->{$selPar}; unless ( defined ( $selected )) { return 0; } foreach my $type (@{$self->{'subtypes'}}) { next unless ($type->fieldName() eq $selected); # If type has no setter, parameter is not required anyway return 1 unless( $type->HTMLSetter() ); return $type->_paramIsSet($params); } return 0; } # Method: _setValue # # Overrides: # # # sub _setValue { my ($self, $value) = @_; my ($selectedField, $selectedValue) = each ( %{$value} ); $self->setSelectedType( $selectedField ); # Call AUTOLOAD method in order not to repeat code $AUTOLOAD = '_setValue'; return $self->AUTOLOAD($selectedValue); } # Method: cmp # # Overrides: # # # # Returns: # # -1 - if all simpler types from self are lower than compareType # # 0 - if all simpler types from self are equal to compareType # # 1 - if all simpler types from self are higher than compareType # # undef - otherwise # sub cmp { my ($self, $compareType) = @_; unless ( (ref $self) eq (ref $compareType) ) { return undef; } my $selfSelected = $self->subtype(); my $compareSelected = $compareType->subtype(); return $selfSelected->cmp($compareSelected); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/File.pm0000664000000000000000000003255612017102272016577 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Types::File # # This type is intended to support file uploading, downloading # from the web interface. Its value keeps the absolute path # where the file is stored within the eBox machine. # # If the type instance is editable, you can upload a file # although the file path has not changed. If non editable, the # viewer will only show the file absolute path. # # This type has an associated action which is the # *download*. This action can be actived setting allowDownload # property as true. This will show up the action download on any # data model. # package EBox::Types::File; use strict; use warnings; use base 'EBox::Types::Abstract'; use EBox; use EBox::Config; use EBox::Gettext; use EBox::Exceptions::Internal; use EBox::Sudo; # Core modules use File::Basename; use Error qw(:try); # Group: Public methods # Constructor: new # # Create a new instance # # Overrides: # # # # Parameters: # # allowDownload - Boolean indicating if the file uploaded can be # downloaded *(Optional)* Default value: false # filePath - path to the file location # dynamicPath - reference to a subroutine that returns the actual filePath # user - user which will own the file (default: ebox) # group - group which will be own the file, if it is not supplied # the group will be have the sanme name than the user # # Returns: # # - the file type instance # sub new { my $class = shift; my %opts = @_; unless (exists $opts{'HTMLSetter'}) { $opts{'HTMLSetter'} ='/ajax/setter/file.mas'; } unless (exists $opts{'HTMLViewer'}) { $opts{'HTMLViewer'} ='/ajax/viewer/file.mas'; } $opts{'type'} = 'file'; my $self = $class->SUPER::new(%opts); bless($self, $class); return $self; } # Method: printableValue # # Overrides: # # # sub printableValue { my ($self) = @_; my $path = $self->path(); if ( defined ($path) ) { return basename($path); } else { return ''; } } # Method: isEqualTo # # Overrides: # # # sub isEqualTo { my ($self, $new) = @_; my $fileExists = $self->exist(); my $uploadFile = (-f $new->tmpPath); my $removeFile = $new->toRemove; if ( $fileExists and $uploadFile) { # Check MD5 sum to check content uniqueness my $path = $self->path; my $tmpPath = $new->tmpPath; my $equal; try { EBox::Sudo::root("diff -q $path $tmpPath"); # diff return value 0; they are equal $equal = 1; } otherwise { # diff command failed, we assume that they ar different (cannot find # a reliable docuentation of diff's return values) $equal = 0; }; return $equal; } elsif ($uploadFile) { return 0 } elsif ($removeFile) { return 0; } return 1; } # Method: fields # # Overrides: # # # sub fields { my ($self) = @_; my $pathField = $self->fieldName() . '_path'; my $removeField = $self->fieldName() . '_remove'; return ( $pathField, $removeField ); } # Method: path # # Accessor to the path value stored # # Returns: # # String - the file path # sub path { my ($self) = @_; if (exists $self->{dynamicPath}) { my $dynamicPathFunc = $self->{dynamicPath}; return &$dynamicPathFunc($self); } return $self->{filePath}; } # Method: user # # Returns: # - the user which will own the file sub user { my ($self) = @_; if (not exists $self->{user}) { return 'ebox'; } return $self->{user}; } # Method: group # # Returns: # - the group which will own the file sub group { my ($self) = @_; if (not exists $self->{user}) { return $self->user(); } return $self->{user}; } # Method: exist # # Check if the file path marked exists # # Returns: # # boolean - if the file exists given a file path # undef - if the file path is not set # sub exist { my ($self, $path) = @_; defined $path or $path = $self->path(); $path or return undef; if ( $self->path() ) { return EBox::Sudo::fileTest('-f', $self->path); } else { return undef; } } # Method: toRemove # # Check if the file has been marked to be removed # # Returns: # # boolean - # sub toRemove { my ($self) = @_; return $self->{remove}; } sub filesPaths { my ($self) = @_; if ($self->exist()) { return [ $self->path() ]; } else { return [] } } # Method: allowDownload # # Check if it is possible to allow download or not from the viewer # point of view # # Returns: # # Boolean - true if it is allowed, false otherwise # sub allowDownload { my ($self) = @_; return $self->{allowDownload}; } # Method: showFileWhenEditing # # Show the file path name when edition is done # # Returns: # # Boolean - true if it is shown, false otherwise # sub showFileWhenEditing { my ($self) = @_; return $self->{showFileWhenEditing}; } # Method: linkToDownload # # Link to the CGI which downloads the file # sub linkToDownload { my ($self) = @_; my $contextName = $self->model()->contextName(); my $link = '/Controller/Downloader/FromModel?'; $link .= 'model=' . $contextName; $link .= '&dir=' . $self->model()->directory(); $link .= '&id=' . $self->row()->id(); $link .= '&field=' . $self->fieldName(); return $link; } # Method: tmpPath # # Get the tmp path when the file is not used by the file type by # it is already uploaded to the server # # Returns: # # String - the path within the tmp directory where the potential # file is stored # sub tmpPath { my ($self) = @_; return ( EBox::Config::tmp() . $self->fieldName() . '_path' ); } # Method: userPath # # Get the user given path where the file is stored to be # uploaded by the browser. This returned value is useful to # determine if any file has been uploaded or not. # # Returns: # # String - the user given path # sub userPath { my ($self) = @_; return $self->{userPath}; } # Method: backupPath # # return the path to the actual configuration backup # # Parameters: # path - path of the file which owns the backup, if it is not # provided the path() method will be used sub backupPath { my ($self, $path) = @_; defined $path or $path = $self->path(); $path or return undef; my $backupPath = $path . '.bak'; return $backupPath; } # Method: noPreviousFilePath # # return the path to the file to signals to the actual configuration backup # that there wasn't any file before # # Parameters: # path - path of the file which owns the backup, if it is not # provided the path() method will be used sub noPreviousFilePath { my ($self, $path) = @_; defined $path or $path = $self->path(); $path or return undef; my $backupPath = $path . '.noprevious.bak'; return $backupPath; } # Method: backupFiles # # Make an actual configuration backup of the file.. This backup will used to # discard changes when revoking the configuration # # Parameters: # path - path of the file which owns the backup, if it is not # provided the path() method will be used sub backupFiles { my ($self, $path) = @_; defined $path or $path = $self->path(); $path or return; my $backupPath = $self->backupPath($path); my $noPreviousFilePath = $self->noPreviousFilePath($path); if ($self->exist($path)) { $backupPath or return; EBox::Sudo::root("cp -p $path $backupPath"); EBox::Sudo::root("rm -f $noPreviousFilePath"); } else { EBox::Sudo::root("touch $noPreviousFilePath"); EBox::Sudo::root("rm -f $backupPath"); } } # Method: restoreFiles # # Restores the actual configuration backup of the file, thus discarding last # changes # # Parameters: # path - path of the file which owns the backup, if it is not # provided the path() method will be used sub restoreFiles { my ($self, $path) = @_; defined $path or $path = $self->path(); $path or return; my $backupPath = $self->backupPath($path); if ( EBox::Sudo::fileTest('-f', $backupPath) ) { EBox::Sudo::root("cp -p $backupPath $path"); return; } my $noPreviousFilePath = $self->noPreviousFilePath($path); if ( EBox::Sudo::fileTest('-f', $noPreviousFilePath) ) { EBox::Sudo::root("rm -f $path"); } } # Group: Protected methods # Method: _setMemValue # # Overrides: # # # # Exceptions: # # - thrown if the move cannot be # done # sub _setMemValue { my ($self, $params) = @_; my $homePathParam = $self->fieldName() . '_path'; my $removeParam = $self->fieldName() . '_remove'; $self->{userPath} = $params->{$homePathParam}; $self->{remove} = $params->{$removeParam}; } # Method: _storeInHash # # Overrides: # # # sub _storeInHash { my ($self, $hash) = @_; my $keyField = $self->fieldName() . '_path'; if ($self->path() and $self->userPath()) { # Do actually move $self->_moveToPath(); $hash->{$keyField} = $self->path(); } elsif ($self->{remove}) { delete $hash->{$keyField}; if (not $self->userPath()) { # Actually remove my $path = $self->path(); if (-f $path) { EBox::Sudo::root("rm $path"); } } } } # Method: _restoreFromHash # # Overrides: # # # sub _restoreFromHash { my ($self, $hash) = @_; my $pathField = $self->fieldName() . '_path'; $self->{filePath} = $hash->{$pathField}; } # Method: allowUnsafeChars # # Overrides: # # # sub allowUnsafeChars { return 1; } # Method: _paramIsValid # # Every file which exists (defined by # is valid. The subclasses may # override this method to check any content of the file. # # Overrides: # # # sub _paramIsValid { return 1; } # Method: _paramIsSet # # Overrides: # # # sub _paramIsSet { my ($self, $params) = @_; # Check if the parameter exist my $path = $self->fieldName() . '_path'; my $pathValue = $params->{$path}; my $remove = $self->fieldName() . '_remove'; my $removeValue = $params->{$remove}; return 1 if ($removeValue); return 1 if (defined ( $pathValue )); return (-f $self->tmpPath()); } # Method: _setValue # # Set the value for the file. # # Two choices are available: # # - give a path where the file will be stored (given a # non-empty string). The file must be set in # and the name must match "$fieldName" + '_path' # # - remove the current value passing (given an empty string or undef) # # Overrides: # # # # Parameters: # # value - String the path to store in file type or an empty string # to remove it # sub _setValue #(value) { my ($self, $value) = @_; my $params = {}; if ( $value ) { $params->{$self->fieldName() . '_path'} = $value; } else { $params->{$self->fieldName() . '_remove'} = $value; } $self->setMemValue($params); } # Group: Private methods sub _moveToPath { my ($self) = @_; my $path = $self->path(); my $tmpPath = $self->tmpPath(); if (not -f $tmpPath) { throw EBox::Exceptions::Internal("No file found at $tmpPath for moving to $path"); } my $user = $self->user(); my $group = $self->group(); if (($user eq 'root') or ($group eq 'root')) { EBox::Sudo::root("mv $tmpPath $path"); try { EBox::Sudo::root("chown $user.$group $path"); } otherwise { my $ex = shift; EBox::Sudo::root("rm -f $path"); $ex->throw(); }; } elsif (($user eq 'ebox') and ($group eq 'ebox')) { File::Copy::move($tmpPath, $self->path()) or throw EBox::Exceptions::Internal("Cannot move from $tmpPath " . ' to ' . $self->path()); } else { throw EBox::Exceptions::NotImplemented( "user and group combination not supported" ); } } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/Service.pm0000664000000000000000000001737012017102272017315 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Types::Service; use strict; use warnings; use EBox::Validate qw( checkPort checkProtocol); use EBox::Exceptions::MissingArgument; use EBox::Exceptions::External; use EBox::Gettext; use base 'EBox::Types::Abstract'; # Constructor: new # # Create a type which includes the protocol and the port from # Internet as a service # # Returns: # # a object # sub new { my $class = shift; my %opts = @_; unless (exists $opts{'HTMLSetter'}) { $opts{'HTMLSetter'} ='/ajax/setter/serviceSetter.mas'; } unless (exists $opts{'HTMLViewer'}) { $opts{'HTMLViewer'} ='/ajax/viewer/textViewer.mas'; } $opts{optional} = 0 unless defined ( $opts{optional} ); $opts{'type'} = 'service'; my $self = $class->SUPER::new(%opts); $self->{protocols} = $self->_protocolsHash(); bless($self, $class); return $self; } # Method: paramExist # # Overrides method # sub paramExist { my ($self, $params) = @_; my $proto = $self->fieldName() . '_protocol'; return (defined ($params->{$proto})); } # Method: printableValue # # Overrides method # sub printableValue { my ($self) = @_; if (defined($self->{protocol})) { if ( $self->_needPort($self->{protocol}) ) { return $self->{port} . '/' . $self->_printableValue($self->{protocol}); } else { return $self->_printableValue($self->{protocol}); } } else { return ''; } } # Method: compareToHash # # Overrides method # sub compareToHash { my ($self, $hash) = @_; my $oldProtocol = $self->protocol(); my $oldPort = $self->port(); my $newProtocol = $self->fieldName() . '_protocol'; my $newPort = $self->fieldName() . '_port'; if ( not defined ( $oldProtocol ) or not defined ( $hash->{$newProtocol} )) { return 0; } if ($oldProtocol ne $hash->{$newProtocol}) { return 0; } if ($oldPort ne $hash->{$newPort}) { return 0; } return 1; } # Method: cmp # # Overrides method # sub cmp { my ($self, $compared) = @_; $compared->isa(__PACKAGE__) or return undef; my $portA = $self->port(); my $portB = $compared->port(); my $res = $portA <=> $portB; if ( $res != 0 ) { return $res; } my $protoA = $self->protocol(); my $protoB = $compared->protocol(); if ($protoA gt $protoB) { return 1; } elsif ($protoA lt $protoB) { return -1; } else { return 0; } } sub _attrs { return [ 'protocol', 'port' ]; } ### # Own methods ### # Method: protocols # # Get the protocols available (Static method) # # Returns: # # array ref - the protocols in a hash with the following elements # - value - the protocol name # - printableValue - the protocol printable name # - needPort - set true if it needs a port # sub protocols { my ($self) = @_; my @protocols = ( { value => 'all', printableValue => __('Any'), needPort => 0, }, { value => 'tcp', printableValue => 'TCP', needPort => 1, }, { value => 'udp', printableValue => 'UDP', needPort => 1, }, { value => 'icmp', printableValue => 'ICMP', needPort => 0, }, { value => 'gre', printableValue => 'GRE', needPort => 0, }, ); return \@protocols; } # Method: protocolsJS # # Get the JavaScript definition of an array with # the protocols which need a port (Static method) # # Returns: # # String # sub protocolsJS { my ($self) = @_; my $str = "[ "; foreach my $proto ( @{$self->protocols()} ) { if ( $proto->{needPort} ) { $str .= q{"} . $proto->{value} . q{", }; } } # Deleting the trailing comma value from array variable $str =~ s/, $//; $str .= ']'; return $str; } # Method: protocol # # Get the protocol value # # Returns: # # String - the Internet protocol # sub protocol { my ($self) = @_; return $self->{protocol}; } # Method: port # # Get the port value # # Returns: # # Int - the port associated to that protocol # sub port { my ($self) = @_; return $self->{port}; } # Method: AnyProtocol # # Get the name of the protocol which represents all protocols. # (Class method) # # Returns: # # String - containing the desired name # sub AnyProtocol { my ($class) = @_; return 'all'; } # Group: Protected methods # Method: _paramIsValid # # Overrides: # # # sub _paramIsValid { my ($self, $params) = @_; my $proto = $self->fieldName() . '_protocol'; my $port = $self->fieldName() . '_port'; checkProtocol($params->{$proto}, $self->printableName()); if ($self->_needPort($params->{$proto})) { checkPort($params->{$port}, $self->printableName()); } return 1; } # Method: _paramIsSet # # Overrides: # # # sub _paramIsSet { my ($self, $params) = @_; my $proto = $self->fieldName() . '_protocol'; my $port = $self->fieldName() . '_port'; return undef unless ($params->{$proto}); if ($self->_needPort($params->{$proto})) { return undef unless ($params->{$port}); } return 1; } # Method: _setValue # # Set the value defined as a string in the # printableValue. That is, to define an service you must set # a valid port/protocol. # # Overrides: # # # # Parameters: # # value - String a valid port/protocol # sub _setValue # (value) { my ($self, $value) = @_; my ($port, $protocol) = split ('/', $value); unless ( defined ( $protocol )) { $protocol = $port; $port = undef; } my $params = { $self->fieldName() . '_port' => $port, $self->fieldName() . '_protocol' => $protocol, }; $self->setMemValue($params); } #### # Group: Private methods ### # Return if a protocol needs a port sub _needPort # (proto) { my ($self, $proto) = @_; return $self->{protocols}->{$proto}->{needPort}; } # Return the printable value from a protocol sub _printableValue # (proto) { my ($self, $proto) = @_; return $self->{protocols}->{$proto}->{printableValue}; } # Return a hash ref with the allowed protocols # indexed by protocol value sub _protocolsHash { my ($self) = @_; my %protocolsHash = map { $_->{value} => $_ } @{$self->protocols()}; return \%protocolsHash; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/IPRange.pm0000664000000000000000000001430312017102272017173 0ustar # Copyright (C) 2011-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Types::IPRange; use base 'EBox::Types::Abstract'; use strict; use warnings; use EBox::Validate qw(:all); use EBox::Gettext; use EBox::Exceptions::MissingArgument; use Net::IP; use Error qw(:try); use constant MAX_N_ADDRESS => 16777216; # we choose as max the number of # addresses for a net of class A sub new { my $class = shift; my %opts = @_; unless (exists $opts{'HTMLSetter'}) { $opts{'HTMLSetter'} ='/ajax/setter/ipRangeSetter.mas'; } unless (exists $opts{'HTMLViewer'}) { $opts{'HTMLViewer'} ='/ajax/viewer/textViewer.mas'; } $opts{'type'} = 'iprange'; my $self = $class->SUPER::new(%opts); bless($self, $class); return $self; } sub paramExist { my ($self, $params) = @_; my $begin = $self->fieldName() . '_begin'; my $end = $self->fieldName() . '_end'; return (defined($params->{$begin}) and defined($params->{$end})); } sub printableValue { my ($self) = @_; if (defined($self->{'begin'}) and defined($self->{'end'})) { return "$self->{'begin'} - $self->{'end'}"; } else { return ""; } } # Method: cmp # # Overrides: # # # sub cmp { my ($self, $compareType) = @_; unless ((ref $self) eq (ref $compareType)) { return undef; } my $rangeA = $self->_rangeObject(); my $rangeB = $compareType->_rangeObject();; if ($rangeA->bincomp('lt', $rangeB)) { return -1; } elsif ( $self->printableValue() eq $compareType->printableValue() ) { return 0; } else { return 1; } } sub size { my ($self) = @_; return $self->{'size'}; } sub compareToHash { my ($self, $hash) = @_; my $oldBegin = $self->begin(); my $oldEnd = $self->end(); my $begin = $self->fieldName() . '_begin'; my $end = $self->fieldName() . '_end'; if ($oldBegin ne $hash->{$begin}) { return 0; } if ($oldEnd ne $hash->{$end}) { return 0; } return 1; } sub _attrs { return [ 'begin', 'end' ]; } sub begin { my ($self) = @_; return $self->{'begin'}; } sub end { my ($self) = @_; return $self->{'end'}; } # Group: Protected methods # Method: _paramIsValid # # Overrides: # # # sub _paramIsValid { my ($self, $params) = @_; my $beginParam = $self->fieldName() . '_begin'; my $endParam = $self->fieldName() . '_end'; my $begin = $params->{$beginParam}; my $end = $params->{$endParam}; checkIP($begin, __('Begin of IP range')); checkIP($end, __('End of IP range')); my @beginParts = split '\.', $begin, 4; my @endParts = split '\.', $end, 4; foreach my $n (0 .. 3) { if ($beginParts[$n] < $endParts[$n]) { last; } elsif ($beginParts[$n] > $endParts[$n]) { throw EBox::Exceptions::InvalidData( data => $self->printableName(), value => $self->printableValue(), advice => __('End of range address should be smaller than start of range address') ) } } my $range; try { $range = Net::IP->new("$begin - $end"); } otherwise { my $ex = shift; throw EBox::Exceptions::InvalidData( data => $self->printableName(), value => $self->printableValue(), advice => "$ex", ); }; if ($range->size() > MAX_N_ADDRESS) { my $advice = __x( 'The IP range contained {size} addresses, the maximum is {max} addresses', size => $range->size(), max => MAX_N_ADDRESS, ); throw EBox::Exceptions::InvalidData( data => $self->printableName(), value => $self->printableValue(), advice => $advice, ); } return 1; } # Method: _paramIsSet # # Overrides: # # # sub _paramIsSet { my ($self, $params) = @_; # Check if the parameter exist my $begin = $self->fieldName() . '_begin'; my $end = $self->fieldName() . '_end'; unless ( defined($params->{$begin}) and defined($params->{$end})) { return 0; } # Check if has something, begin field is not empty return ( $params->{$begin} ne '' ); } # Method: _setValue # # Set the value defined as a string in the # printableValue. That is, to define an IP Address you must set # a valid CIDR IP Address. # # Overrides: # # # # Parameters: # # value - String an IP address with CIDR notation # sub _setValue # (value) { my ($self, $value) = @_; my ($begin, $end) = split ('\s*-\s*', $value); my $params = { $self->fieldName() . '_begin' => $begin, $self->fieldName() . '_end' => $end, }; $self->setMemValue($params); } sub addresses { my ($self) = @_; my $ipRange = $self->_rangeObject(); my @addresses; do { my $ip = $ipRange->ip(); unless ($ip =~ /\.0$/) { push (@addresses, $ip); } } while (++$ipRange); return \@addresses; } sub isIPInside { my ($self, $ip) = @_; my $ipB = Net::IP->new($ip); my $ipRange = $self->_rangeObject(); my $res = $ipRange->overlaps($ipB); if (defined $res) { return $res ne $Net::IP::IP_NO_OVERLAP ; } else { return undef; } } sub _rangeObject { my ($self) = @_; return new Net::IP ($self->printableValue()); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/Date.pm0000664000000000000000000001531612017102272016570 0ustar # Copyright (C) 2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Types::Date; use base 'EBox::Types::Abstract'; use EBox::Validate qw(:all); use EBox::Gettext; use EBox::Exceptions::MissingArgument; use strict; use warnings; # Group: Public methods sub new { my $class = shift; my %opts = @_; unless (exists $opts{'HTMLSetter'}) { $opts{'HTMLSetter'} ='/ajax/setter/dateSetter.mas'; } unless (exists $opts{'HTMLViewer'}) { $opts{'HTMLViewer'} ='/ajax/viewer/textViewer.mas'; } $opts{'type'} = 'date' unless defined ($opts{'type'}); my $self = $class->SUPER::new(%opts); bless($self, $class); return $self; } # Method: paramExist # # Overrides: # # # sub paramExist { my ($self, $params) = @_; my $day = $self->fieldName() . '_day'; my $month = $self->fieldName() . '_month'; my $year = $self->fieldName() . '_year'; return ( defined($params->{$day} ) and defined($params->{$month}) and defined($params->{$year} ) ); } # Method: printableValue # # Overrides: # # # sub printableValue { my ($self) = @_; if ( defined($self->{'day'} ) and defined($self->{'month'}) and defined($self->{'year'} ) ) { return "$self->{'day'}/$self->{'month'}/$self->{'year'}"; } else { return ""; } } # Method: cmp # # Overrides: # # # # Returns: # # -1 - if self is lower than compareType # # 0 - if both are equal # # 1 - if self is higher than compareType # # undef - otherwise (not equal types) # sub cmp { my ($self, $compareType) = @_; unless ( (ref $self) eq (ref $compareType) ) { return undef; } unless ( defined($self->{'day'} ) and defined($self->{'month'}) and defined($self->{'year'} ) ) { return undef; } # First check the year, if equal check the month and if equal check the day if ($self->{'year'} > $compareType->{'year'}) { return 1; } elsif ($self->{'year'} < $compareType->{'year'}) { return -1; } else { if ($self->{'month'} > $compareType->{'month'}) { return 1; } elsif ($self->{'month'} < $compareType->{'month'}) { return -1; } else { if ($self->{'day'} > $compareType->{'day'}) { return 1; } elsif ($self->{'day'} < $compareType->{'day'}) { return -1; } else { return 0; } } } } sub size { my ($self) = @_; return $self->{'size'}; } # Method: compareToHash # # Overrides: # # # # Returns: # # True (1) if equal, false (0) if not equal # sub compareToHash { my ($self, $hash) = @_; my $oldDay = $self->{'day'}; my $oldMonth = $self->{'month'}; my $oldYear = $self->{'year'}; my $day = $self->fieldName() . '_day'; my $month = $self->fieldName() . '_month'; my $year = $self->fieldName() . '_year'; if (($oldDay ne $hash->{$day} ) or ($oldMonth ne $hash->{$month}) or ($oldYear ne $hash->{$year} )) { return 0; } return 1; } # Method: value # # Overrides: # # # # Returns: # # Array containing the values (day, month, year) # sub value { my ($self) = @_; return ($self->{'day'}, $self->{'month'}, $self->{'year'}); } sub day { my ($self) = @_; return $self->{'day'}; } sub month { my ($self) = @_; return $self->{'month'}; } sub year { my ($self) = @_; return $self->{'year'}; } # Group: Protected methods # Method: _attrs # # Overrides: # # # sub _attrs { return [ 'day', 'month', 'year' ]; } # Method: _paramIsValid # # Checks that the date formed by the parameters ($day, $month, $year) # is correct. If not, throws an EBox::Exceptions::InvalidData exception. # sub _checkDate { my ($self, $day, $month, $year) = @_; if ( $month == 2 ) { if ( $day > 29 ) { return 0; } elsif ( $day == 29 ) { if ( ( ($year % 4) != 0) or ( (($year % 100) == 0) and (($year % 400) != 0) ) ) { throw EBox::Exceptions::InvalidData ('data' => $self->printableName(), 'value' => sprintf('%02d/%02d/%04d', $day, $month, $year), 'advice' => __('Not a leap year.'), ); } } } else { if ( $day > 30 ) { if ( ($month == 4) or ($month == 6) or ($month == 9) or ($month == 11) ) { throw EBox::Exceptions::InvalidData ('data' => $self->printableName(), 'value' => sprintf('%02d/%02d/%04d', $day, $month, $year), 'advice' => __('This month does not have 31 days.'), ); } } } } # Method: _paramIsValid # # Overrides: # # # sub _paramIsValid { my ($self, $params) = @_; my $day = $self->fieldName() . '_day'; my $month = $self->fieldName() . '_month'; my $year = $self->fieldName() . '_year'; $self->_checkDate($params->{$day}, $params->{$month}, $params->{$year}); return 1; } # Method: _paramIsSet # # Overrides: # # # sub _paramIsSet { return 1; } # Method: _setValue # # Set the value defined as a string: DD/MM/YYYY # # Overrides: # # # # Parameters: # # value - String DD/MM/YYYY # sub _setValue # (value) { my ($self, $value) = @_; my ($day, $month, $year) = split ('/', $value); $day =~ s/^0+//; $month =~ s/^0+//; $year =~ s/^0+//; my $params = { $self->fieldName() . '_day' => $day, $self->fieldName() . '_month' => $month, $self->fieldName() . '_year' => $year, }; $self->setMemValue($params); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/Text.pm0000664000000000000000000000450712017102272016637 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Types::Text; use strict; use warnings; use base 'EBox::Types::Basic'; use EBox; use EBox::Gettext; use EBox::Exceptions::InvalidData; # Group: Public methods sub new { my $class = shift; my %opts = @_; unless (exists $opts{'HTMLSetter'}) { $opts{'HTMLSetter'} ='/ajax/setter/textSetter.mas'; } unless (exists $opts{'HTMLViewer'}) { $opts{'HTMLViewer'} ='/ajax/viewer/textViewer.mas'; } $opts{'type'} = 'text' unless defined ($opts{'type'}); my $self = $class->SUPER::new(%opts); bless($self, $class); return $self; } sub size { my ($self) = @_; return $self->{'size'}; } # Method: printableValue # # This functions overrides # to i18nize the string in case the type is set as localizable # sub printableValue { my ($self) = @_; if ($self->{'localizable'}) { return __($self->{value}); } else { return $self->SUPER::printableValue(); } } # Method: cmp # # Overrides: # # # sub cmp { my ($self, $compareType) = @_; unless ( (ref $self) eq (ref $compareType) ) { return undef; } return $self->value() cmp $compareType->value(); } # Group: Protected methods # Method: _paramIsValid # # Overrides: # # # sub _paramIsValid { return 1; } # Method: _paramIsSet # # Overrides: # # # sub _paramIsSet { my ($self, $params) = @_; # Check if the parameter exist my $param = $params->{$self->fieldName()}; return defined ( $param ) and ($param ne ''); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/InverseMatchSelect.pm0000664000000000000000000001065312017102272021442 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Types::InverseMatchSelect # # This class inherits from to add # inverse match support # # FIXME: This package shouldn't exist as we should provide inverse match # feature form abstract types and provide a real OO approach, not this # ugly repetition of code in InverseMatch* types. # # We are repeating ourselves, this sucks so freaking much. # package EBox::Types::InverseMatchSelect; use strict; use warnings; use base 'EBox::Types::Select'; use EBox; use EBox::Gettext; # Group: Public methods sub new { my $class = shift; my %opts = @_; unless (exists $opts{'HTMLSetter'}) { $opts{'HTMLSetter'} ='/ajax/setter/inverseMatchSelectSetter.mas'; } unless (exists $opts{'inverseMatchPrintableString'}) { $opts{'inverseMatchPrintableString'} = __('Not'); } $opts{'type'} = 'select'; if ( defined ( $opts{'optional'} ) and (not $opts{'optional'} )) { EBox::warn('EBox::Types::InverseMatchSelect cannot be compulsory'); } $opts{'optional'} = undef; my $self = $class->SUPER::new(%opts); bless($self, $class); return $self; } sub inverseMatchField { my ($self) = @_; return $self->fieldName() . '_inverseMatch'; } sub compareToHash { my ($self, $hash) = @_; unless ($self->inverseMatch() eq $hash->{$self->inverseMatchField()}) { return undef; } return $self->SUPER::compareToHash($hash); } sub isEqualTo { my ($self, $newObject) = @_; unless ($self->SUPER::isEqualTo($newObject)) { return undef; } return ($self->inverseMatch() eq $newObject->inverseMatch()); } sub printableValue { my ($self) = @_; my $printValue = $self->SUPER::printableValue(); if ($self->inverseMatch()) { $printValue = $self->{inverseMatchPrintableString} . " $printValue"; } return $printValue; } sub fields { my ($self) = @_; return ($self->inverseMatchField(), $self->SUPER::fields()); } sub inverseMatch { my ($self) = @_; return 0 unless defined ($self->{'inverseMatch'}); return $self->{'inverseMatch'}; } # Group: Protected methods # Method: _setMemValue # # Overrides: # # # sub _setMemValue { my ($self, $params) = @_; $self->SUPER::_setMemValue($params); $self->{'inverseMatch'} = $params->{$self->inverseMatchField()}; } # Method: _storeInHash # # Overrides: # # # sub _storeInHash { my ($self, $hash) = @_; $self->SUPER::_storeInHash($hash); $hash->{$self->inverseMatchField()} = $self->inverseMatch(); } # Method: _restoreFromHash # # Overrides: # # # sub _restoreFromHash { my ($self, $hash) = @_; return unless ($self->row()); $self->SUPER::_restoreFromHash($hash); my $field = $self->fieldName() . '_inverseMatch'; $self->{'inverseMatch'} = $hash->{$field}; } # Method: _setValue # # Set the value if any. The value may follow this pattern: # # { inverse => [0|1], value => selectedValue } # # Or it can appear just the selected value, setting implicitily # the inverse value as false. # # Overrides: # # # # Parameters: # # value - hash ref or a basic value to pass # sub _setValue # (value) { my ($self, $value) = @_; my ($selectedValue, $invMatch); if ( ref ( $value ) eq 'HASH' ) { $selectedValue = $value->{'value'}; $invMatch = $value->{'inverse'}; } else { $selectedValue = $value; $invMatch = 0; } my $params = { $self->fieldName() => $selectedValue, $self->inverseMatchField => $invMatch, }; $self->setMemValue($params); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/Host.pm0000664000000000000000000000655512017102272016635 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Types::Host # # A specialised text type to represent either a host IP address or a host name. # package EBox::Types::Host; use strict; use warnings; use base 'EBox::Types::Text'; use EBox::Validate; # Dependencies use Net::IP; # Group: Public methods # Constructor: new # # The constructor for the # # Returns: # # the recently created object # sub new { my $class = shift; my $self = $class->SUPER::new( @_, ); $self->{'type'} = 'host'; bless($self, $class); return $self; } # Method: cmp # # Overrides: # # # sub cmp { my ($self, $compareType) = @_; unless ( (ref $self) eq (ref $compareType) ) { return undef; } my $aIsIp = $self->isIPAddress(); my $bIsIp = $compareType->isIPAddress();; if ($aIsIp and $bIsIp) { $self->_cmpIP($compareType); } elsif ((not $aIsIp) and (not $bIsIp)) { $self->_cmpHostname($compareType); } else { # we cannot compare host name with an a host address return undef; } } sub _cmpIP { my ($self, $compareType) = @_; my $ipA = new Net::IP($self->value()); my $ipB = new Net::IP($compareType->value()); if ( $ipA->bincomp('lt', $ipB) ) { return -1; } elsif ( $ipA->bincomp('gt', $ipB)) { return 1; } else { return 0; } } sub _cmpHostname { my ($self, $compareType) = @_; my $aValue = $self->value(); my $bValue = $compareType->value(); if ($aValue gt $bValue) { return 1; } elsif ($aValue lt $bValue) { return -1; } else { return 0; } } # Method: isIPAddress # # Returns: # true - if the value contained is an IP address sub isIPAddress { my ($self) = @_; my $value = $self->value(); return $value =~ m/^[\d.]+$/; } # Group: Protected methods # Method: _paramIsValid # # Check if the params has a correct host IP address # # Overrides: # # # # Parameters: # # params - the HTTP parameters with contained the type # # Returns: # # true - if the parameter is either a correct host IP address or name # # Exceptions: # # - throw if it's not a correct # host IP address or name # sub _paramIsValid { my ($self, $params) = @_; my $value = $params->{$self->fieldName()}; if (defined ( $value )) { EBox::Validate::checkHost($value, $self->printableName()); } return 1; } sub memValue { my ($self) = @_; my $value = $self->{'value'}; $value =~ s/\.$//; return $value; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/Union/0000775000000000000000000000000012017102272016437 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Types/Union/Text.pm0000664000000000000000000000636012017102272017726 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Types::Union::Text # # This type is a specialized version of text to be used at # type. Its meaning is the option which it # is left when the remainder options are not selected. # # For example, a type representing a source as an union of an # object or an IP address can be as well represented as any as a # value determining that you are selected the whole selection # space. # package EBox::Types::Union::Text; use strict; use warnings; use base 'EBox::Types::Text'; use EBox::Exceptions::NotImplemented; # Group: Public methods # Constructor: new # # The constructor for the type # # Returns: # # - the union text recently created # object # sub new { my $class = shift; my %opts = @_; my $self = $class->SUPER::new(@_); $self->{'type'} = 'union/text'; # If it is set to editable if ( $self->{editable} ) { EBox::warn('EBox::Types::Union::Text type cannot be editable ' . 'since it has no setter'); } $self->{editable} = 0; bless($self, $class); return $self; } # Method: HTMLSetter # # Overrides: # # # sub HTMLSetter { return undef; } # Method: value # # Overrides: # # # sub value { my ($self) = @_; return $self->{'value'} if defined($self->{'value'}); return $self->fieldName(); } # Method: printableValue # # Overrides: # # # sub printableValue { my ($self) = @_; return $self->printableName(); } # Protected Methods # Method: _setMemValue # # Overrides: # # # sub _setMemValue { my ($self, $params) = @_; $self->{'value'} = $self->fieldName(); } # Method: _restoreFromHash # # Overrides: # # # sub _restoreFromHash { my ($self, $hash) = @_; $self->{'value'} = $self->fieldName(); } # Method: _storeInHash # # Overrides: # # # sub _storeInHash { # Store nothing since it is already written as printableName } # Method: _paramIsValid # # Overrides: # # # sub _paramIsValid { return 1; } # Method: _paramIsSet # # Overrides: # # # sub _paramIsSet { return 1; } # Method: _setValue # # Overrides: # # # sub _setValue { my ($self) = @_; $self->_setMemValue(); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/Text/0000775000000000000000000000000012017102272016273 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Types/Text/WriteOnce.pm0000664000000000000000000000274012017102272020533 0ustar # Copyright (C) 2009-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Types::Text::WriteOnce # # Class that inherits from # # Text can only be written once. # # The need of this arose because the way that # the module manages the VPN files. To avoid nasty stuff, we don't allow to # change the name of the VPN. Afterwards the same problem appeared in another places # package EBox::Types::Text::WriteOnce; use strict; use warnings; use base 'EBox::Types::Text'; use EBox; # Group: Public methods sub new { my $class = shift; my %opts = @_; my $self = $class->SUPER::new(%opts); bless($self, $class); return $self; } # Method: editable # # To implement write-once feature # # Overrides: # # # sub editable { my ($self) = @_; return (not (defined($self->value()))); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/IPNetwork.pm0000664000000000000000000000637512017102272017602 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # package: EBox::Types::IPNetwork # # Type class intended to represent addresses of IP networks package EBox::Types::IPNetwork; use EBox::Validate qw(:all); use EBox::Gettext; use EBox::Exceptions::MissingArgument; use strict; use warnings; use base 'EBox::Types::IPAddr'; sub new { my $class = shift; my %opts = @_; unless (exists $opts{'HTMLSetter'}) { $opts{'HTMLSetter'} = '/ajax/setter/ipnetworkSetter.mas'; } my $self = $class->SUPER::new(%opts); $self->{'type'} = 'ipnetwork'; bless($self, $class); return $self; } # Method: _paramIsValid # # Overrides: # # # sub _paramIsValid { my ($self, $params) = @_; my $ipParam = $self->fieldName() . '_ip'; my $maskParam = $self->fieldName() . '_mask'; my $ip = $params->{$ipParam}; my $mask = $params->{$maskParam}; my $printableName = __($self->printableName()); checkCIDR($ip . "/$mask", $printableName); my ($unused, $expandedMask) = EBox::NetWrappers::to_network_without_mask("$ip/$mask"); checkIPIsNetwork($ip, $expandedMask, $printableName, $printableName); return 1; } # Function : checkIPIsNetwork # # Checks if the IP and the mask are valid and that the IP is a # network with the given mask. # # Note that both name_ip and name_mask should be set, or not set at all # # # Parameters: # # ip - IPv4 address # mask - network mask address # name_ip - Data's name to be used when throwing an Exception # name_mask - Data's name to be used when throwing an Exception # # Returns: # # boolean - True if it is a valid IPv4 address and network, false otherwise # # Exceptions: # # If name is passed an exception could be raised # # InvalidData - ip/mask is incorrect # check that a given IP and netmask correspond to a IP networ # # # Warning: # # derived from EBox::Validate::checkIPNetmask # XXX move to eBox::VAlidate if needed sub checkIPIsNetwork { my ($ip,$mask,$name_ip, $name_mask) = @_; checkIP($ip,$name_ip); checkNetmask($mask,$name_mask); my $ip_bpack = pack("CCCC", split(/\./, $ip)); my $mask_bpack = pack("CCCC", split(/\./, $mask)); my $net_bits = unpack("B*", $ip_bpack & (~$mask_bpack)); my $isNetwork = ($net_bits =~ /^0+$/); if (not $isNetwork) { if ($name_ip) { throw EBox::Exceptions::InvalidData ('data' => $name_ip . "/" . $name_mask, 'value' => $ip . "/" . $mask); } else { return undef; } } return 1; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/HTML.pm0000664000000000000000000000445312017102272016457 0ustar # Copyright (C) 2011-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Types::HTML # # This class represents a type which contains raw HTML # package EBox::Types::HTML; use strict; use warnings; use base 'EBox::Types::Text'; use EBox::Exceptions::InvalidData; use EBox::Gettext; # Group: Public methods # Constructor: new # # Create the type # # Returns: # # - the newly created type # sub new { my ($class, %opts) = @_; unless (exists $opts{'HTMLViewer'}) { $opts{'HTMLViewer'} ='/ajax/viewer/rawHTML.mas'; } $opts{'type'} = 'html'; $opts{'editable'} = 0; my $self = $class->SUPER::new(%opts); bless ( $self, $class ); unless (exists $opts{'HTMLSetter'}) { $self->{'HTMLSetter'} = '/ajax/viewer/rawHTML.mas'; } return $self; } # Group: Protected methods # Method: _paramIsValid # # Check if the params has a correct HTML string # # Overrides: # # # # Parameters: # # params - the HTTP parameters with contained the type # # Returns: # # true - if the parameter is a correct HTML string # # Exceptions: # # - throw if it's not a correct # HTML string # sub _paramIsValid { my ($self, $params) = @_; my $value = $params->{$self->fieldName()}; if ( defined ( $value )) { # The test is quite de risa unless ( $value =~ m:^<.*>$: ) { throw EBox::Exceptions::InvalidData( data => $self->fieldName(), value => $value, advice => __('It must be a valid HTML string') ); } } return 1; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/File/0000775000000000000000000000000012017102272016226 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Types/File/Test.pm0000664000000000000000000001505612017102272017512 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Types::File::Test; use base 'EBox::Test::Class'; # use strict; use warnings; use Test::More qw(no_plan); use Test::Exception; use Test::File; use File::Slurp qw(read_file write_file); use lib '../../..'; use EBox::TestStubs; use EBox::Types::Test; use EBox::Types::File; my $content = 'first'; my $secondContent = 'second'; my $path = '/tmp/ebox.type.file.test'; sub setEBoxTmp : Test(startup) { EBox::TestStubs::setEBoxConfigKeys(tmp => '/tmp/'); } sub clearFiles : Test(setup) { system "rm -f $path"; my $file = newFile(); my @toDelete = ($file->tmpPath, $file->path, $file->backupPath, $file->noPreviousFilePath); system (" rm -f @toDelete"); } sub newTest : Test(1) { EBox::Types::Test::createOk( 'EBox::Types::File', filePath => $path, fieldName => 'fileTest', ); } sub restoreWithoutBackup : Test(2) { my $file = newFile(); lives_ok { $file->restoreFiles() } ' restore without previous backup or file'; write_file($path, $content); file_exists_ok($path, 'Checking that restore without previous file deletes '); } sub restoreWithoutBackup: Test(4) { my $file = newFile(); write_file($path, $content); lives_ok { $file->restoreFiles() } ' restore with backup of no existent file'; my $actualContent = read_file($path); is $actualContent, $content, 'Checking that restore without backup does not alter the existent file'; unlink $path; lives_ok { $file->restoreFiles() } ' restore with backup of no existent file'; Test::File::file_not_exists_ok($path, "checking that restore without backup does not bring back deleted files"); } sub restoreWithoutPreviousFile : Test(3) { my $file = newFile(); # backup of a not existent file lives_ok { $file->backupFiles() } 'backup of a not existent file'; write_file($path, $content); lives_ok { $file->restoreFiles() } ' restore with backup of no existent file'; Test::File::file_not_exists_ok($path, "checking that restore bckup done without files erases the new file"); } sub restoreWithPreviousFile : Test(5) { my $file = newFile(); write_file($path, $content); lives_ok { $file->backupFiles() } 'backup with file'; unlink $path; lives_ok { $file->restoreFiles(); } 'restore after deleting file'; my $actualContent = read_file($path); is $actualContent, $content, 'Checking if the restored file after removal has the right content'; write_file($path, $secondContent); lives_ok { $file->restoreFiles(); } 'restore after replacing file with another'; is $actualContent, $content, 'Checking if the restored file after being replaced has the right content'; } sub isEqualToTest : Test(5) { my $file = newFile(); my $file2 = newFile(); clearFiles(); ok $file->isEqualTo($file2), 'Checking equalTo in identical files objects'; clearFiles(); write_file($path, $content); ok $file->isEqualTo($file2), 'Checking equalTo in identical files objects with file already in place'; clearFiles(); write_file($path, $content); write_file($file2->tmpPath(), $content); ok $file->isEqualTo($file2), 'Checking equalTo in identical files objects with the same file already in place and upload file'; my $notEqual; clearFiles(); write_file($path, $content); write_file($file2->tmpPath(), 'differentContent'); $notEqual = not $file->isEqualTo($file2); ok $notEqual, 'Checking equalTo in identical files objects with a file already in place and another upload file'; clearFiles(); write_file($file2->tmpPath(), $content); $notEqual = not $file->isEqualTo($file2); ok $notEqual, 'Checking equalTo in identical files objects without a file in place and upload file'; } sub existsTest : Test(2) { my $file = newFile(); ok (not $file->exist); write_file($path, $content); ok $file->exist; } sub printableValueTest : Test(2) { my $path = '/tmp/ea.jpg'; my $expectedPrintableValue = 'ea.jpg'; my $fileWithStaticPath = EBox::Types::File->new( filePath => $path, fieldName => 'fileTest', ); is $fileWithStaticPath->printableValue(), $expectedPrintableValue, 'checking printableValue in file with hardcoded path'; my $dynamicPathSub = sub { return $path }; my $fileWithDynamicPath = EBox::Types::File->new( dynamicPath => $dynamicPathSub, fieldName => 'fileTest', ); is $fileWithDynamicPath->printableValue(), $expectedPrintableValue, 'checking printableValue in file with dynamic path'; } sub filesPaths : Test(2) { my $file = EBox::Types::File->new( filePath => $path, fieldName => 'fileTest', ); is_deeply( $file->filesPaths(), [], 'Checking return value of filesPaths when no file is present ' ); write_file($path, $content); is_deeply ( $file->filesPaths(), [ $path ], 'Checking return value of filesPaths when file is present ' ); } sub newFile { my $file = EBox::Types::File->new( filePath => $path, fieldName => 'fileTest', ); # remove previous backup path my $backupPath = $file->backupPath(); system "rm -f $backupPath"; return $file; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/Boolean.pm0000664000000000000000000000551212017102272017267 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Types::Boolean; use strict; use warnings; use base 'EBox::Types::Basic'; # Group: Public methods sub new { my $class = shift; my %opts = @_; unless (exists $opts{'HTMLSetter'}) { $opts{'HTMLSetter'} ='/ajax/setter/booleanSetter.mas'; } unless (exists $opts{'HTMLViewer'}) { $opts{'HTMLViewer'} ='/ajax/viewer/booleanInPlaceViewer.mas'; } $opts{'type'} = 'boolean'; my $self = $class->SUPER::new(%opts); bless($self, $class); return $self; } # Method: isEqualTo # # Overrides: # # # sub isEqualTo { my ($self, $other) = @_; return $self->cmp($other) == 0; } # Method: cmp # # Overrides: # # # sub cmp { my ($self, $other) = @_; if ((ref $self) ne (ref $other)) { return undef; } my $ownValue = $self->value(); my $otherValue = $other->value(); if ($ownValue and (not $otherValue)) { return 1; } elsif ((not $ownValue) and $otherValue) { return -1; } else { # the two values are both true or false return 0; } } # Group: Protected methods # Method: _setMemValue # # Overrides: # # # sub _setMemValue { my ($self, $params) = @_; $self->{'value'} = $params->{$self->fieldName()} ? 1 : 0; } # Method: _paramIsValid # # Overrides: # # # sub _paramIsValid { my ($self, $params) = @_; return 1; } # Method: _paramIsSet # # Overrides: # # # sub _paramIsSet { my ($self, $params) = @_; # Check if the parameter exist my $param = $params->{$self->fieldName()}; if (defined ($param)) { return 1; } else { if ($self->optional()) { return 0; } else { # We assume when the parameter is compulsory and it is not # in params, that the type value is false. This is a side # effect from HTTP protocol which does not send a value when # a checkbox is not checked return 1; } } } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/IPAddr.pm0000664000000000000000000001137712017102272017021 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Types::IPAddr; use base 'EBox::Types::Abstract'; use EBox::Validate qw(:all); use EBox::Gettext; use EBox::Exceptions::MissingArgument; use Net::IP; use strict; use warnings; sub new { my $class = shift; my %opts = @_; unless (exists $opts{'HTMLSetter'}) { $opts{'HTMLSetter'} ='/ajax/setter/ipaddrSetter.mas'; } unless (exists $opts{'HTMLViewer'}) { $opts{'HTMLViewer'} ='/ajax/viewer/textViewer.mas'; } $opts{'type'} = 'ipaddr'; my $self = $class->SUPER::new(%opts); bless($self, $class); return $self; } sub paramExist { my ($self, $params) = @_; my $ip = $self->fieldName() . '_ip'; my $mask = $self->fieldName() . '_mask'; return (defined($params->{$ip}) and defined($params->{$mask})); } sub printableValue { my ($self) = @_; if (defined($self->{'ip'}) and defined($self->{'mask'})) { return "$self->{'ip'}/$self->{'mask'}"; } else { return ''; } } # Method: cmp # # Overrides: # # # sub cmp { my ($self, $compareType) = @_; unless ( (ref $self) eq (ref $compareType) ) { return undef; } my $ipA = new Net::IP($self->printableValue()); defined $ipA or return undef; my $ipB = new Net::IP($compareType->printableValue()); defined $ipB or return undef; if ( $ipA->bincomp('lt', $ipB) ) { return -1; } elsif ( $self->printableValue() eq $compareType->printableValue() ) { return 0; } else { return 1; } } sub size { my ($self) = @_; return $self->{'size'}; } sub compareToHash { my ($self, $hash) = @_; my ($oldIp, $oldMask) = $self->_ipNetmask(); my $ip = $self->fieldName() . '_ip'; my $mask = $self->fieldName() . '_mask'; if ($oldIp ne $hash->{$ip}) { return 0; } if ($oldMask ne $hash->{$mask}) { return 0; } return 1; } sub fields { my ($self) = @_; my $ip = $self->fieldName() . '_ip'; my $mask = $self->fieldName() . '_mask'; return ($ip, $mask); } sub value { my ($self) = @_; return $self->_ipNetmask(); } sub ip { my ($self) = @_; return $self->{'ip'}; } sub mask { my ($self) = @_; return $self->{'mask'}; } # Group: Protected methods sub _attrs { return [ 'ip', 'mask' ]; } # Method: _paramIsValid # # Overrides: # # # sub _paramIsValid { my ($self, $params) = @_; my $ip = $self->fieldName() . '_ip'; my $mask = $self->fieldName() . '_mask'; checkIP($params->{$ip}, __($self->printableName())); checkCIDR($params->{$ip} . "/$params->{$mask}", __($self->printableName())); return 1; } # Method: _paramIsSet # # Overrides: # # # sub _paramIsSet { my ($self, $params) = @_; # Check if the parameter exist my $ip = $self->fieldName() . '_ip'; my $mask = $self->fieldName() . '_mask'; unless ( defined($params->{$ip}) and defined($params->{$mask})) { return 0; } # Check if has something, ip field is not empty return ( $params->{$ip} ne '' ); } # Method: _setValue # # Set the value defined as a string in the # printableValue. That is, to define an IP Address you must set # a valid CIDR IP Address. # # Overrides: # # # # Parameters: # # value - String an IP address with CIDR notation # sub _setValue # (value) { my ($self, $value) = @_; my ($ip, $netmask) = split ('/', $value); my $params = { $self->fieldName() . '_ip' => $ip, $self->fieldName() . '_mask' => $netmask, }; $self->setMemValue($params); } # Group: Private methods # Helper funcionts sub _ipNetmask { my ($self) = @_; return ($self->{'ip'}, $self->{'mask'}); } sub isEqualTo { my ($self, $other) = @_; if (not $other->isa(__PACKAGE__)) { return undef; } if ($self->ip() ne $other->ip()) { return undef; } elsif ($self->mask() ne $other->mask()) { return undef; } return 1; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/MACAddr.pm0000664000000000000000000000354212017102272017104 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Types::MACAddr; use EBox::Validate qw(checkMAC); use EBox::Gettext; use strict; use warnings; use base 'EBox::Types::Text'; use EBox::Exceptions::InvalidData; use EBox::Gettext; # Group: Public methods # Constructor: new # # The constructor for the # # Returns: # # the recently created object # sub new { my $class = shift; my %opts = @_; my $self = $class->SUPER::new(@_); $self->{'type'} = 'macaddr'; bless($self, $class); return $self; } # Group: Protected methods # Method: _paramIsValid # # Check if the params has a correct MAC address # # Overrides: # # # # Parameters: # # params - the HTTP parameters with contained the type # # Returns: # # true - if the parameter is a correct MAC address # # Exceptions: # # - throw if it's not a correct # MAC address # sub _paramIsValid { my ($self, $params) = @_; my $value = $params->{$self->fieldName()}; if (defined ( $value )) { checkMAC($value, $self->printableName()); } return 1; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/Abstract.pm0000664000000000000000000004031412017102272017452 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # TODO # Document this class # Fix the method naming, some names such as # setMemValue and memValue are so broken!!! # # Class: EBox::Types::Abstract # # It is the parent type where the remainder types overrides from. # package EBox::Types::Abstract; use strict; use warnings; use EBox; use Clone::Fast; use Scalar::Util 'weaken'; use File::Basename; # Group: Public methods sub new { my $class = shift; my $self = {@_}; if (not $self->{fieldName}) { throw EBox::Exceptions::MissingArgument('fieldName'); } if (not $self->{printableName}) { $self->{printableName} = undef; } bless($self, $class); if ( defined ( $self->defaultValue() )) { if ( $self->optional() ) { throw EBox::Exceptions::Internal( 'Defined default value to an optional field ' . $self->fieldName() ); } $self->_setValue($self->defaultValue()); } return $self; } # Method: clone # # Clone the current type safely # # Returns: # # - the cloned object # sub clone { my ($self) = @_; my $clonedType = {}; bless($clonedType, ref($self)); my @suspectedAttrs = qw(model row); foreach my $key (keys %{$self}) { if ( $key ne 'model' and $key ne 'row') { $clonedType->{$key} = Clone::Fast::clone($self->{$key}); } } # Just copy the reference foreach my $suspectedAttr (@suspectedAttrs) { if ( exists $self->{$suspectedAttr} ) { $clonedType->{$suspectedAttr} = $self->{$suspectedAttr}; } } return $clonedType; } sub type { my ($self) = @_; return $self->{'type'}; } sub class { my ($self) = @_; return $self->{'class'}; } # Method: volatile # # A type is volatile when its value or printable value is # calculated in runtime. This value is obtained from the # method. Thus if this attribute # is set, the filter attribute should be defined, otherwise an # identity will be applied # # Returns: # # boolean - whether the type is volatile or not # sub volatile { my ($self) = @_; return $self->{volatile}; } sub unique { my ($self) = @_; return $self->{'unique'}; } # Method: editable # # A type is editable when it is possible to change its value by # the user. If the type is volatile, the type cannot be editable # unless a was defined. # # Returns: # # boolean - showing whether the type is editable or not # sub editable { my ($self) = @_; if ( $self->volatile() and not $self->storer()) { return 0; } elsif (ref $self->{'editable'}) { my $editableFunc = $self->{editable}; return &$editableFunc(); } else { return $self->{'editable'}; } } sub hidden { my ($self) = @_; if (defined $self->{'hidden'} and ref $self->{'hidden'}) { my $hiddenFunc = $self->{'hidden'}; return (&$hiddenFunc()); } elsif (defined($self->{'hidden'})) { return $self->{'hidden'}; } return 0; } sub fieldName { my ($self) = @_; return $self->{'fieldName'}; } sub fields { my ($self) = @_; my $field = $self->fieldName(); my @fields = @{$self->_attrs()}; unless (@fields) { return ($field); } @fields = map { $field . '_' . $_ } @fields; return @fields; } sub section { my ($self) = @_; return $self->{'section'}; } sub printableName { my ($self) = @_; return $self->{'printableName'}; } sub printableValue { my ($self) = @_; return $self->filter(); } # Method: filter # # This method is used to filter the output of printableValue # # Returns: # # Output filtered sub filter { my ($self) = @_; my $filterFunc = $self->{'filter'}; if ($filterFunc) { return (&$filterFunc($self)); } else { return $self->value(); } } sub value { my ($self) = @_; return $self->{'value'}; } # Method: defaultValue # # Accessor to the default value if any # # Returns: # # The default value # sub defaultValue { my ($self) = @_; my $value = $self->{defaultValue}; # Check if it is a reference. It can be a function that returns the value if (ref ($value)) { $value = &$value(); } return $value; } # Method: help # # Method to retrieve the associated help to this type # # Returns: # # string # sub help { my ($self) = @_; if (defined($self->{help})) { return $self->{help}; } else { return ''; } } sub trailingText { my ($self) = @_; return $self->{'trailingText'}; } sub leadingText { my ($self) = @_; return $self->{'leadingText'}; } sub setOptional # (optional) { my ($self, $optional) = @_; $self->{'optional'} = $optional; } sub optional { my ($self) = @_; return $self->{'optional'}; } sub optionalLabel { my ($self) = @_; return defined ($self->{'optionalLabel'}) ? $self->{'optionalLabel'} : $self->{'optional'}; } # Method: disabled # # An instanced type is disabled when it must appear to be # selectable on an union type but it is not selectable # # Returns: # # boolean - indicating if the instanced type is disabled or not # sub disabled { my ($self) = @_; return $self->{'disabled'}; } # Method: allowUnsafeChars # # Attribute to determine the value which contains this type # allows unsafe characters that may be used to inject malicious # code. This only makes sense on HTML-based values # # Returns: # # boolean - indicating if the instanced type may store unsafe # characters or not # sub allowUnsafeChars { my ($self) = @_; return $self->{'allowUnsafeChars'}; } # Method: paramExist # # *DEPRECATED* sub paramExist { } # Method: storeInHash # # Store the given type in a GConf directory from a # Module::Config. If the type is volatile, nothing will be done. # # Parameters: # # module - the module which is in charge # to store the type in GConf # # key - String of the key where the type will be stored # sub storeInHash { my ($self, $hash) = @_; if ($self->volatile()) { if ($self->storer()) { my $storerProc = $self->storer(); &$storerProc($self, $hash); } } else { $self->_storeInHash($hash); } } # Method: setValue # # Set the value for the type. Its behaviour is equal to # , but the argument is a # single value to set to the type instead of a series of CGI # parameters. # # The type is responsible to parse the value parameter to set # the type value appropiately. # # If the type is volatile, no # value is set unless function is # defined. # # Parameters: # # value - the value to set # sub setValue { my ($self, $value) = @_; $self->_setValue($value); } # Method: setMemValue # # Set the memory value for the type. If the type is volatile, no # value is set unless function is # defined # # Parameters: # # params - hash ref with the fields to fill the type with its # appropiate values # sub setMemValue { my ($self, $params) = @_; # Set the memory value only if persistent kind of type my $toSet = $self->volatile() ? 0 : 1; $toSet = ($toSet or ($self->volatile() and (ref($self->storer()) eq 'CODE'))); if ($toSet) { # Check if the parameters hasn't had an empty value if ($self->_paramIsSet($params)) { # Check if the parameter is valid $self->_paramIsValid($params); # Set finally the value $self->_setMemValue($params); } else { if ($self->optional()) { # set type to empty if ($self->memValue()) { $self->_setMemValue($params); } } else { my $defaultValue = $self->defaultValue(); if ($defaultValue) { $self->_setValue($defaultValue); } else { throw EBox::Exceptions::MissingArgument( $self->printableName() ); } } } } } sub memValue { } sub compareToHash { } # Method: cmp # # Comparison among two types of the same type. Some types will # override the function, some others not as # # # Its behaviour is equal to cmp function per string. The # comparison should be done only if the types are equal # # Parameters: # # compareType - the type to compare with # # Returns: # # -1 - if self is lower than compareType # # 0 - if both are equal # # 1 - if self is higher than compareType # # undef - otherwise (not equal types) # sub cmp { } # Method: restoreFromHash # # Restore the value from a hash. # # If the type is volatile, the memory value will be set from the # . If the function is empty, a # function which returns '' will be used. # # Parameters: # # hash - hash ref which contains the data to fill the type value # # sub restoreFromHash { my ($self, $hashRef) = @_; if ($self->volatile()) { my $volatileFunc = $self->{acquirer}; $volatileFunc = \&_identity unless defined ($volatileFunc); $self->{value} = &$volatileFunc($self); } else { $self->_restoreFromHash($hashRef); } } # Method: acquirer # # Get the function which obtains the value from somewhere instead # of GConf. This method is useful for volatile instances of # types. # # Parameters: # # hash - hash ref which contains the row from a data table. This # information should be sufficient to set the value for that # instance # # Returns: # # function - the pointer to that function # sub acquirer { my ($self) = @_; return $self->{acquirer}; } # Method: storer # # Get the procedure which stores the value to somewhere # instead of default backend. This method is useful for volatile instances # of types. # # The procedure has one argument: the type itself # # Returns: # # function - the pointer to that function # sub storer { my ($self) = @_; return $self->{storer}; } # Method: isEqualTo # # returns if a type object is equal to another # default implementation uses the cmp method # soon to be deprecated. sub isEqualTo { my ($self, $other) = @_; return $self->cmp($other) == 0; } # Method: row # # Return the row to which this data belongs # # Returns: # # row - hash ref containting a row sub row { my ($self) = @_; return $self->{'row'}; } # Method: setRow # # Set the row identifier to which this data belongs # # Parameters: # # (POSITIONAL) # # row - hash ref of a row sub setRow { my ($self, $row) = @_; $self->{'row'} = $row; weaken($self->{'row'}); } # Method: setModel # # Set the model to which this data belongs # # Parameters: # # (POSITIONAL) # # model - an object of type sub setModel { my ($self, $id) = @_; $self->{'model'} = $id; weaken($self->{'model'}); } # Method: model # # Return the model to which this data belongs # # Returns: # # model - an object of type sub model { my ($self) = @_; return $self->{'model'}; } sub HTMLSetter { my ($self) = @_; return undef if ($self->hidden()); if (defined($self->{'hiddenOnSetter'}) and $self->{'hiddenOnSetter'}) { return undef; } return undef unless (exists $self->{'HTMLSetter'}); return $self->{'HTMLSetter'}; } sub HTMLViewer { my ($self) = @_; return undef if ($self->hidden()); if (defined($self->{'hiddenOnViewer'}) and $self->{'hiddenOnViewer'}) { return undef; } return undef unless (exists $self->{'HTMLViewer'}); return $self->{'HTMLViewer'}; } sub typeRowLayout { my ($self) = @_; return '/ajax/typeRowLayout.mas' unless (exists $self->{'typeRowLayout'}); return $self->{'typeRowLayout'}; } # Group: Protected methods # Method: _setMemValue # # Set the memory value for the type. It is assured that this # method will be called if only there is something to fill the # type and its content is valid. This method should be # overridden from non volatile types. # # Parameters: # # params - hash ref with the fields to fill the type with its # appropiate values # sub _setMemValue { my ($self, $params) = @_; my @attrs = @{$self->_attrs()}; return unless @attrs; my $field = $self->fieldName(); foreach my $attr (@attrs) { $self->{$attr} = $params->{"${field}_$attr"}; } } # Method: _storeInHash # # Store this type in the given row hash # # Parameters: # # hash - reference to row hash # sub _storeInHash { my ($self, $hash) = @_; my @attrs = @{$self->_attrs()}; return unless @attrs; my $field = $self->fieldName(); foreach my $attr (@attrs) { my $full_attr = "${field}_$attr"; if ($self->{$attr}) { $hash->{$full_attr} = $self->{$attr}; } else { delete $hash->{$full_attr}; } } } # Method: _attrs # # This should be overriden by complex types which store more than # one value, so _restoreFromHash doesn't need to be implemented # sub _attrs { return []; } # Method: _restoreFromHash # # Restore the type value from a hash reference. # This method should be overridden from non volatile types. # # Parameters: # # hash - hash ref which has all the information required to set # the value from this type # sub _restoreFromHash { my ($self, $hash) = @_; my @attrs = @{$self->_attrs()}; return unless @attrs; my $row = $self->row(); return unless ($row); my $field = $self->fieldName(); for my $attr (@attrs) { $self->{$attr} = $hash->{"${field}_${attr}"}; } } # Method: _paramIsValid # # Check the correctness from the parameters passed. It assures # that it is something to be checked. # # It should launch an exception when the parameter is not valid, # It should be overridden by the subclasses. # # Parameters: # # params - hash ref which has all the information required to # check its correctness # # Exceptions: # # - thrown if the parameters passed # does not contain a valid data for this type # sub _paramIsValid { } # Method: _paramIsSet # # Check if the given parameters contain the data needed to fill # the type, i.e. it exists and it is not empty. It should be # overridden by the subclasses # # Parameters: # # params - hash ref which has all the information required to # check its emptyness # # Returns: # # boolean - indicating if the parameters does not contain enough # data to fill the type # sub _paramIsSet { return 0; } # Method: _setValue # # Set the value. To be overridden by subclasses which # allows values # # Parameters: # # values - the value to set # sub _setValue # (value) { return; } # Group: Private functions # Function: _identity # # Identity function in order to set the value from hash ref # # Parameters: # # instancedType - # sub _identity { return ''; } sub DESTROY { my ($self) = @_; $self->{model} = undef; $self->{row} = undef; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/TimeZone.pm0000664000000000000000000001431412017102272017442 0ustar # Copyright (C) 2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Types::TimeZone; use strict; use warnings; use base 'EBox::Types::Abstract'; use File::Slurp; use File::Basename; use EBox::Validate qw(:all); use EBox::Gettext; use EBox::Exceptions::MissingArgument; use constant ZONES_DIR => '/usr/share/zoneinfo'; use constant ZONES_FILE => ZONES_DIR . '/zone.tab'; my $zones = undef; # Group: Public methods sub new { my $class = shift; my %opts = @_; # Load and cache the zones unless (defined ($zones)) { $zones = _loadZones(); } unless (exists $opts{'HTMLSetter'}) { $opts{'HTMLSetter'} ='/ajax/setter/timezoneSetter.mas'; } unless (exists $opts{'HTMLViewer'}) { $opts{'HTMLViewer'} ='/ajax/viewer/textViewer.mas'; } unless (exists $opts{printableName}) { $opts{printableName} = __('Time zone'); } $opts{'type'} = 'timezone' unless defined ($opts{'type'}); my $self = $class->SUPER::new(%opts); bless ($self, $class); return $self; } # Method: printableValue # # Overrides: # # # sub printableValue { my ($self) = @_; my $ret = ""; if ( defined ($self->{'continent'}) and defined ($self->{'country'}) ) { $ret = "$self->{'continent'}/$self->{'country'}"; } return $ret; } # Method: cmp # # Overrides: # # # # Returns: # # -1 - if self is lower than compareType # # 0 - if both are equal # # 1 - if self is higher than compareType # # undef - otherwise (not equal types) # sub cmp { my ($self, $compareType) = @_; unless ( (ref $self) eq (ref $compareType) ) { return undef; } unless ( defined ($self->{'continent'} ) and defined ($self->{'country'}) ) { return undef; } if (($self->{'continent'} eq $compareType->{'continent'}) and ($self->{'country'} eq $compareType->{'country'})) { return 0; } else { return 1; } } # Method: compareToHash # # Overrides: # # # # Returns: # # True (1) if equal, false (0) if not equal # sub compareToHash { my ($self, $hash) = @_; my $oldContinent = $self->{'continent'}; my $oldCountry = $self->{'country'}; my $continent = $self->fieldName() . '_continent'; my $country = $self->fieldName() . '_country'; if (($oldContinent ne $hash->{$continent} ) or ($oldCountry ne $hash->{$country})) { return 0; } return 1; } sub _attrs { return [ 'continent', 'country' ]; } # Method: value # # Overrides: # # # # Returns: # # Hash ref containing the values (continent, country) # sub value { my ($self) = @_; my $value = {}; $value->{continent} = $self->{continent}; $value->{country} = $self->{country}; return $value; } sub continent { my ($self) = @_; return $self->{'continent'}; } sub country { my ($self) = @_; return $self->{'country'}; } sub zones { my ($self) = @_; unless (defined ($zones)) { $zones = _loadZones(); } return $zones; } # Group: Protected methods # Method: _paramIsValid # # Overrides: # # # sub _paramIsValid { my ($self, $params) = @_; my $continent = $self->fieldName() . '_continent'; my $country = $self->fieldName() . '_country'; my $continentValue = $params->{$continent}; my $countryValue = $params->{$country}; return 0 unless ($continentValue and $countryValue); unless (defined ($zones)) { $zones = $self->_loadZones(); } if (exists $zones->{$continentValue}) { foreach my $country (@{$zones->{$continentValue}}) { if ($country eq $countryValue) { return 1; } } throw EBox::Exceptions::InvalidData( 'data' => $self->printableName(), 'value' => $countryValue, 'advice' => __('This city does not exist.')); } throw EBox::Exceptions::InvalidData( 'data' => $self->printableName(), 'value' => $continentValue, 'advice' => __('This continent does not exist.')); return 1; } # Method: _paramIsSet # # Overrides: # # # sub _paramIsSet { my ($self, $params) = @_; return 1; } # Method: _setValue # # Set the value defined as a string: continent/country # # Overrides: # # # # Parameters: # # value - String continent/country # sub _setValue { my ($self, $value) = @_; # There are countries America/Indiana/Indianapolis my ($continent, @countryArray) = split(/\//, $value); my $country = join('/', @countryArray); my $params = { $self->fieldName() . '_continent' => $continent, $self->fieldName() . '_country' => $country, }; $self->setMemValue($params); } # private methods sub _loadZones { my $table = {}; my @lines = read_file(ZONES_FILE); foreach my $line (@lines) { chomp $line; if ($line =~ /^#/) { next; } my @fields = split(/^([^\s\#]+)\s([^\s]+)\s([^\s\/]+)(\/)([^\s]+)/, $line); my $continent = $fields[3]; my $country = $fields[5]; push (@{$table->{$continent}}, $country); } # Add US and Etc zones foreach my $dir ('US', 'Etc') { foreach my $file (glob (ZONES_DIR . "/$dir/*")) { push (@{$table->{$dir}}, basename($file)); } } return $table; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/Action.pm0000664000000000000000000000203412017102272017121 0ustar # Copyright (C) 2011-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Types::Action; use strict; use warnings; use base 'EBox::Types::MultiStateAction'; sub new { my $class = shift; my %opts = @_; my $self = {@_}; unless (defined $self->{enabled}) { $self->{enabled} = 1; } bless($self, $class); return $self; } sub action { my ($self, $id) = @_; return $self; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/Float.pm0000664000000000000000000000456012017102272016757 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Types::Float # # Describe a float number which is stored as a string in our # backend # package EBox::Types::Float; use strict; use warnings; use base 'EBox::Types::Int'; use EBox::Exceptions::External; use EBox::Gettext; # Core modules use Scalar::Util; # Group: Public methods # Constructor: new # # Parameters: # # (in addition of base classes parameters) # max - maximum integer value allowed # min - minimum integer value allowed (default: 0.0) # sub new { my ($class, %opts) = @_; my $self = $class->SUPER::new(%opts); bless($self, $class); $self->{'type'} = 'float'; return $self; } # Group: Protected methods # Method: _paramIsValid # # Overrides: # # # sub _paramIsValid { my ($self, $params) = @_; my $value = $params->{$self->fieldName()}; unless( Scalar::Util::looks_like_number($value) ) { throw EBox::Exceptions::InvalidData( data => $self->printableName(), value => $value, advice => __('Enter a float number')); } my $max = $self->max(); if (defined($max) and ($value > $max)) { throw EBox::Exceptions::InvalidData( data => $self->printableName(), value => $value, advice => __x(q|The value shouldn't be greater than {m}|, m => $max) ); } my $min = $self->min(); if (defined($min) and ($value < $min)) { throw EBox::Exceptions::InvalidData( data => $self->printableName(), value => $value, advice => __x(q|The value shouldn't be less than {m}|, m => $min) ); } return 1; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/Basic.pm0000664000000000000000000000561612017102272016736 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Types::Basic; use strict; use warnings; use base 'EBox::Types::Abstract'; use EBox::Exceptions::MissingArgument; # Group: Public methods sub new { my $class = shift; my %opts = @_; my $self = $class->SUPER::new(@_); bless($self, $class); return $self; } sub paramExist { my ($self, $params, $field) = @_; return (defined($params->{$self->fieldName()})); } sub memValue { my ($self) = @_; return $self->{'value'}; } sub compareToHash { my ($self, $hash) = @_; if ( defined ( $hash->{$self->fieldName()} ) and defined ( $self->memValue() )) { return ($self->memValue() eq $hash->{$self->fieldName()}); } else { return 0; } } sub isEqualTo { my ($self, $newObject) = @_; my $oldValue = $self->{'value'}; my $newValue = $newObject->memValue(); # A great dilemma if ( not defined ( $oldValue ) and not defined ( $newValue )) { return 1; } if ( not defined ( $oldValue ) or not defined ( $newValue )) { return 0; } return ($oldValue eq $newValue); } # Group: Protected methods # Method: _setMemValue # # Overrides: # # # sub _setMemValue { my ($self, $params) = @_; $self->{'value'} = $params->{$self->fieldName()}; } # Method: _restoreFromHash # # Overrides: # # # sub _restoreFromHash { my ($self, $hash) = @_; return unless ($self->row()); $self->{'value'} = $hash->{$self->fieldName()}; } # Method: _setValue # # Set the value if any # # Overrides: # # # # Parameters: # # value - the basic value to pass # sub _setValue # (value) { my ($self, $value) = @_; my $params = { $self->fieldName() => $value, }; $self->setMemValue($params); } # Method: _storeInHash # # Overrides: # # # sub _storeInHash { my ($self, $hash) = @_; my $field = $self->fieldName(); if (defined($self->memValue()) and $self->memValue() ne '') { $hash->{$field} = $self->memValue(); } else { delete $hash->{$field}; } } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/MailAddress.pm0000664000000000000000000000245612017102272020104 0ustar # Copyright (C) 2009-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Types::MailAddress; use strict; use warnings; use base 'EBox::Types::Text'; use EBox; use EBox::Gettext; use EBox::Exceptions::InvalidData; # Group: Public methods sub new { my $class = shift; my %opts = @_; my $self = $class->SUPER::new(%opts); $self->{localizable} = 0; bless($self, $class); return $self; } # Method: _paramIsValid # # Overrides: # # # sub _paramIsValid { my ($self, $params) = @_; my $value = $params->{$self->fieldName()}; EBox::Validate::checkEmailAddress($value, $self->printableName()); return 1; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/InverseMatchUnion.pm0000664000000000000000000001053412017102272021311 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Types::InverseMatchUnion # # This class inherits from to add # inverse match support # # FIXME: This package shouldn't exist as we should provide inverse match # feature form abstract types and provide a real OO approach, not this # ugly repetition of code in InverseMatch* types. # # We are repeating ourselves, this sucks so freaking much. # # use strict; use warnings; package EBox::Types::InverseMatchUnion; use base 'EBox::Types::Union'; use EBox::Gettext; # Group: Public methods sub new { my $class = shift; my %opts = @_; unless (exists $opts{'HTMLSetter'}) { $opts{'HTMLSetter'} ='/ajax/setter/inverseMatchUnionSetter.mas'; } unless (exists $opts{'inverseMatchPrintableString'}) { $opts{'inverseMatchPrintableString'} = __('Not'); } $opts{'type'} = 'union'; my $self = $class->SUPER::new(%opts); bless($self, $class); return $self; } sub inverseMatchField { my ($self) = @_; return $self->fieldName() . '_inverseMatch'; } sub compareToHash { my ($self, $hash) = @_; unless ($self->inverseMatch() eq $hash->{$self->inverseMatchField()}) { return undef; } return $self->SUPER::compareToHash($hash); } sub isEqualTo { my ($self, $newObject) = @_; unless ($self->SUPER::isEqualTo($newObject)) { return undef; } return ($self->inverseMatch() eq $newObject->inverseMatch()); } sub printableValue { my ($self) = @_; my $printValue = $self->SUPER::printableValue(); if ($self->inverseMatch()) { $printValue = $self->{inverseMatchPrintableString} . " $printValue"; } return $printValue; } sub fields { my ($self) = @_; return ($self->inverseMatchField(), $self->SUPER::fields()); } sub inverseMatch { my ($self) = @_; return 0 unless defined ($self->{'inverseMatch'}); return $self->{'inverseMatch'}; } # Group: Protected methods # Method: _setMemValue # # Overrides: # # # sub _setMemValue { my ($self, $params) = @_; $self->SUPER::_setMemValue($params); $self->{'inverseMatch'} = $params->{$self->inverseMatchField()}; } # Method: _storeInHash # # Overrides: # # # sub _storeInHash { my ($self, $hash) = @_; $self->SUPER::_storeInHash($hash); $hash->{$self->inverseMatchField()} = $self->inverseMatch(); } # Method: _restoreFromHash # # Overrides: # # # sub _restoreFromHash { my ($self, $hash) = @_; $self->SUPER::_restoreFromHash($hash); $self->{'inverseMatch'} = $hash->{$self->fieldName() . '_inverseMatch'}; } # Method: _setValue # # Set the value if any. The value may follow this pattern: # # { inverse => [0|1], selectedFieldName => selectedValue } # # Or it can appear just the selected field with its value, setting # implicitily the inverse value as false as follows: # # { selectedFieldName => selectedValue } # # Overrides: # # # # Parameters: # # value - hash ref or a basic value to pass # sub _setValue # (value) { my ($self, $value) = @_; my ($selectedField, $selectedValue, $invMatch); if ( exists ( $value->{'inverse'} )) { $invMatch = delete ( $value->{'inverse'} ); } else { $invMatch = 0; } # FIXME: It should be a method to do so $self->{'inverseMatch'} = $invMatch; $self->SUPER::_setValue( $value ); } sub HTMLSetter { my ($self) = @_; my $unionSetter = $self->SUPER::HTMLSetter(); if (not $unionSetter) { return undef; } return $self->{HTMLSetter}; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/HasMany.pm0000664000000000000000000002270212017102272017250 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Types::HasMany # # This class represents a pseudo-type to express relations amongst models. # When used in a model, it basically tells you that this field is # referencing another model which has more than one entry. # # For example, let's say we have a model which represents a table # of internet domains. Each domain is composed of several hosts. # The relation between the domain and the hosts can be expressed # by means of this type. # # # TODO # # - Review which methods are necessary and document them # - Implement backview correctly to allow the user to go back # when he is done with the spawned table # package EBox::Types::HasMany; use strict; use warnings; use base 'EBox::Types::Abstract'; use EBox::Model::Manager; use Error qw(:try); # Group: Public methods sub new { my $class = shift; my %opts = @_; unless (exists $opts{'HTMLSetter'}) { $opts{'HTMLSetter'} = undef; } unless (exists $opts{'HTMLViewer'}) { $opts{'HTMLViewer'} ='/ajax/viewer/hasManyViewer.mas'; } $opts{'type'} = 'hasMany'; $opts{'unique'} = undef; $opts{'editable'} = undef; $opts{'optional'} = 1; my $self = $class->SUPER::new(%opts); bless($self, $class); return $self; } sub printableValue { my ($self) = @_; return undef; } sub value { my ($self) = @_; return '' unless (exists $self->{'foreignModel'}); return { 'model' => $self->{'foreignModel'}, , 'directory' => $self->directory() }; } # Method: foreignModelIsComposite # # Returns: # # bool - wether the foreign model is compostie or not # sub foreignModelIsComposite { my ($self) = @_; return $self->{'foreignModelIsComposite'}; } # Method: foreignModel # # Get the foreign model which the hasMany type retrieves its # values # # Returns: # # String - the foreign model, empty if there is none # sub foreignModel { my ($self) = @_; return '' unless (exists $self->{'foreignModel'}); return $self->{'foreignModel'}; } # Method: foreignModelInstance # # Get the foreign model instance used in the HasMany instance # # Returns: # # - the foreign model instance or undef if thre is none # sub foreignModelInstance { my ($self) = @_; my $value = $self->value(); if (not $value) { return undef; } my $modelName = $value->{model}; my $directory = $value->{directory}; # directory maybe undef if the HasMany is not yet created $directory or return undef; my $model; my $manager = EBox::Model::Manager->instance(); my $ro = $self->row()->configModule->isReadOnly(); try { if ($self->foreignModelIsComposite()) { $model = $manager->composite($modelName, $ro); } else { $model = $manager->model($modelName, $ro); } } catch EBox::Exceptions::DataNotFound with { }; return undef unless (defined($model)); $model->setDirectory($directory); $model->{parent} = $self->model(); return $model; } # Method: setDirectory # # Set the directory for the foreign model # # Parameters: # # (POSITIONAL) # directory - string containing the directory sub setDirectory { my ($self, $directory) = @_; $self->{'directory'} = $directory; } # Method: directory # # Return the directory for the foreing model if any # # Returns: # # stirng - directory or undef if there isn't any sub directory { my ($self) = @_; my $row = $self->row(); if (not $row) { return undef; } my $directory = $row->dir() . '/' . $row->id(); $directory .= '/' . $self->fieldName(); return $directory; } # Method: view # # Return the view for the foreing model # # Returns: # # string - view's url sub view { my ($self) = @_; if (exists $self->{'view'}) { return $self->{'view'}; } else { return undef; } } # Method: backView # # Return the back view for the foreing model # # Returns: # # stirng - view's url sub backView { my ($self) = @_; if (exists $self->{'backView'}) { return $self->{'backView'}; } else { return undef; } } # Method: linkToView # # Return the link to the model's view # # Returns: # # string - containing the link # sub linkToView { my ($self) = @_; my $view = $self->view(); my $directory = $self->directory(); my $backview = $self->backView(); my $params="?directory=$directory" . "&backview=$backview"; my $url = $view . $params; return $url; } # Method: foreignModelAcquirer # # Get the function which has the possibility to get foreign model # which represents the class dynamically. It also fills the view # to show the model. # # Returns: # # function ref - the reference to the callback function # sub foreignModelAcquirer { my ($self) = @_; # This function is called at the _restoreFromHash return $self->{'foreignModelAcquirer'}; } sub paramExist { } sub setMemValue { } sub _memValue { } sub compareToHash { } sub isEqualTo { } sub modelView { my ($self) = @_; return $self->{'modelView'}; } # Group: Protected methods # Method: _storeInHash # # Overrides: # # # sub _storeInHash { } # Method: _restoreFromHash # # Overrides: # # # sub _restoreFromHash { my ($self, $hashRef) = @_; if (defined ($self->foreignModelAcquirer())) { my $acquirerFunc = $self->foreignModelAcquirer(); $self->{'foreignModel'} = &$acquirerFunc($self->row()); try { my $model = $self->foreignModelInstance(); if (not $model) { throw EBox::Exceptions::DataNotFound(); } $self->{'view'} = '/' . $model->menuNamespace(); $self->setDirectory($model->directory()); } catch EBox::Exceptions::DataNotFound with { $self->{'view'} = '/'; }; } } # Method: _paramIsValid # # Overrides: # # # sub _paramIsValid { return 1; } # Method: _paramIsSet # # Overrides: # # # sub _paramIsSet { return 1; } # Method: filesPaths # # Returns: # the paths of the files managed by the submodel and possible rows and sub-submodels sub filesPaths { my ($self) = @_; my $subModel = $self->foreignModelInstance(); if (not $subModel) { return []; } if ($subModel->can('filesPaths')) { return $subModel->filesPaths(); } else { return [] } } # Method: backupFiles # # Make an actual configuration backup of all the files contained in the # submodel and potentials rows and nested submodels. This backup will used to discard changes if needed sub backupFiles { my ($self) = @_; my $subModel = $self->foreignModelInstance(); if (not $subModel) { return; } if ($subModel->can('backupFiles')) { return $subModel->backupFiles(); } } # Method: restoreFiles # # Restores the actual configuration backup of files, thus discarding last # changes in files sub restoreFiles { my ($self) = @_; my $subModel = $self->foreignModelInstance(); if (not $subModel) { return; } if ($subModel->can('restoreFiles')) { return $subModel->restoreFiles(); } } sub setModel { my ($self, $model, @extraParams) = @_; $self->SUPER::setModel($model, @extraParams); if (defined $model) { $self->{modelName} = $model->name(); $self->{moduleName} =$model->{confmodule}->name(), } else { delete $self->{modelName} ; delete $self->{moduleName} ; } } sub model { my ($self) = @_; my $model = $self->SUPER::model(); if (defined $model) { return $model; } if ((not $self->{moduleName}) or (not $self->{modelName})) { return undef; } my $module = EBox::Global->modInstance($self->{moduleName}); if (not $module->can('model')) { EBox::warn('cannot recreate row for ' . $self->{fieldName} . ' because module has not model() method'); return undef; } $model = $module->model($self->{modelName}); defined $model or return undef; $self->setModel($model); return $model; } sub setRow { my ($self, $row, @extraParams) = @_; $self->SUPER::setRow($row, @extraParams); if (defined $row) { $self->{rowId} = $row->id(); } else { delete $self->{rowId}; } } sub row { my ($self) = @_; my $row = $self->SUPER::row(); if (defined $row) { return $row; } if (not ($self->{rowId})) { return undef; } my $model = $self->model(); $row = $model->row($self->{rowId}); defined $row or return undef; $self->setRow($row); return $row; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/Time.pm0000664000000000000000000001175312017102272016612 0ustar # Copyright (C) 2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Types::Time; use base 'EBox::Types::Abstract'; use EBox::Validate qw(:all); use EBox::Gettext; use EBox::Exceptions::MissingArgument; use strict; use warnings; # Group: Public methods sub new { my $class = shift; my %opts = @_; unless (exists $opts{'HTMLSetter'}) { $opts{'HTMLSetter'} ='/ajax/setter/timeSetter.mas'; } unless (exists $opts{'HTMLViewer'}) { $opts{'HTMLViewer'} ='/ajax/viewer/textViewer.mas'; } $opts{'type'} = 'time' unless defined ($opts{'type'}); my $self = $class->SUPER::new(%opts); bless($self, $class); return $self; } sub paramExist { my ($self, $params) = @_; my $hour = $self->fieldName() . '_hour'; my $min = $self->fieldName() . '_min'; my $sec = $self->fieldName() . '_sec'; return (defined($params->{$hour}) and defined($params->{$min}) and defined($params->{$sec})); } # Method: printableValue # # Overrides: # # # sub printableValue { my ($self) = @_; if (defined($self->{'hour'}) and defined($self->{'min'}) and defined($self->{'sec'})) { return "$self->{'hour'}:$self->{'min'}:$self->{'sec'}"; } else { return ''; } } # Method: cmp # # Overrides: # # # # Returns: # # -1 - if self is lower than compareType # # 0 - if both are equal # # 1 - if self is higher than compareType # # undef - otherwise (not equal types) # sub cmp { my ($self, $compareType) = @_; unless ( (ref $self) eq (ref $compareType) ) { return undef; } unless (defined($self->{'hour'}) and defined($self->{'min'}) and defined($self->{'sec'})) { return undef; } if ($self->{'hour'} > $compareType->{'hour'}) { return 1; } elsif ($self->{'hour'} < $compareType->{'hour'}) { return -1; } else { if ($self->{'min'} > $compareType->{'min'}) { return 1; } elsif ($self->{'min'} < $compareType->{'min'}) { return -1; } else { if ($self->{'sec'} > $compareType->{'sec'}) { return 1; } elsif ($self->{'sec'} < $compareType->{'sec'}) { return -1; } else { return 0; } } } } sub size { my ($self) = @_; return $self->{'size'}; } sub compareToHash { my ($self, $hash) = @_; my $oldHour = $self->{'hour'}; my $oldMin = $self->{'min'}; my $oldSec = $self->{'sec'}; my $hour = $self->fieldName() . '_hour'; my $min = $self->fieldName() . '_min'; my $sec = $self->fieldName() . '_sec'; if (($oldHour ne $hash->{$hour}) or ($oldMin ne $hash->{$min} ) or ($oldSec ne $hash->{$sec} )) { return 0; } return 1; } sub value { my ($self) = @_; return ($self->{'hour'}, $self->{'min'}, $self->{'sec'}); } sub hour { my ($self) = @_; return $self->{'hour'}; } sub minute { my ($self) = @_; return $self->{'min'}; } sub second { my ($self) = @_; return $self->{'sec'}; } # Group: Protected methods # Method: _attrs # # Overrides: # # # sub _attrs { return [ 'hour', 'min', 'sec' ]; } # Method: _paramIsValid # # Overrides: # # # sub _paramIsValid { return 1; } # Method: _paramIsSet # # Overrides: # # # sub _paramIsSet { return 1; } # Method: _setValue # # Set the value defined as a string: HH:MM:SS # # Overrides: # # # # Parameters: # # value - String HH:MM:SS # sub _setValue # (value) { my ($self, $value) = @_; my ($hour, $min, $sec) = split (':', $value); $hour =~ s/^0+//; $min =~ s/^0+//; $sec =~ s/^0+//; my $params = { $self->fieldName() . '_hour' => $hour, $self->fieldName() . '_min' => $min, $self->fieldName() . '_sec' => $sec, }; $self->setMemValue($params); } sub isEqualTo { my ($self, $other) = @_; if (not $other->isa(__PACKAGE__)) { return undef; } if (($self->hour() ne $other->hour()) or ($self->minute() ne $other->minute()) or ($self->second() ne $other->second())) { return undef; } return 1; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/Password.pm0000664000000000000000000001302412017102272017507 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Types::Password; # Class: EBox::Types::Password # # Define the password type. This typical text type will not show # its value and it may have a minimum and a maximum length use base 'EBox::Types::Text'; use strict; use warnings; use EBox::Exceptions::InvalidData; use EBox::Gettext; # Group: Public methods # Constructor: new # # The constructor for the # # Returns: # # the recently created object # sub new { my $class = shift; my %opts = @_; unless (exists $opts{'HTMLSetter'}) { $opts{'HTMLSetter'} = '/ajax/setter/passwordSetter.mas'; } unless (exists $opts{'HTMLViewer'}) { $opts{'HTMLViewer'} = '/ajax/viewer/passwordViewer.mas'; } unless (exists $opts{'typeRowLayout'}) { $opts{'typeRowLayout'} = '/ajax/passwordRowLayout.mas'; } $opts{'type'} = 'password'; my $self = $class->SUPER::new(%opts); $self->{'minLength'} = 0 unless defined ( $self->{'minLength'} ); $self->{'maxLength'} = 0 unless defined ( $self->{'maxLength'} ); $self->{'confirm'} = 0 unless defined ( $self->{'confirm'} ); $self->{'confirmPrintableName'} = '' unless defined ( $self->{'confirmPrintableName'} ); $self->{'allowUnsafeChars'} = 1; bless($self, $class); return $self; } # Method: minLength # # Get the minimum password length. # # Returns: # # Int - the minimum length. 0 if no minimum length is not set # sub minLength { my ($self) = @_; return $self->{minLength}; } # Method: maxLength # # Get the maximum password length. # # Returns: # # Int - the maximum length. 0 if no maximum length is not set # sub maxLength { my ($self) = @_; return $self->{maxLength}; } # Method: confirmPrintableName # # Get the printable name for the confirmation field. # # Returns: # # String - the printable name for the confirmation field # sub confirmPrintableName { my ($self) = @_; return $self->{confirmPrintableName}; } # Method: fields # # Get the list of fields of interest for the type # # Overrides: # # # sub fields { my ($self) = @_; my @fields = ($self->fieldName()); push @fields, $self->fieldName() . '_confirm' if $self->{'confirm'}; return @fields; } # Group: Protected methods # Method: _paramIsValid # # Check if the params has a correct password. Check its length to # be in the correct interval. # # Overrides: # # # # Parameters: # # params - the HTTP parameters with contained the type # # Returns: # # true - if the parameter is a password # # Exceptions: # # - throw if it's not a correct # password # sub _paramIsValid { my ($self, $params) = @_; my $value = $params->{$self->fieldName()}; if (defined ( $value )) { if ( $self->{'minLength'} != 0 ) { if ( length ( $value ) < $self->{'minLength'} ) { throw EBox::Exceptions::InvalidData( data => $self->printableName(), value => '****', advice => __x('The password should have at ' . 'least {minLength} characters', minLength => $self->{'minLength'}) ); } } if ( $self->{'maxLength'} != 0 ) { if ( length ( $value ) > $self->{'maxLength'} ) { throw EBox::Exceptions::InvalidData( data => $self->printableName(), value => '****', advice => __x('The password should have at ' . 'most {maxLength} characters', maxLength => $self->{'maxLength'}) ); } } } if ( $self->{'confirm'} ) { my $confirmValue = $params->{$self->fieldName() . '_confirm'}; if ( $confirmValue ne $value ) { throw EBox::Exceptions::InvalidData( data => $self->printableName(), value => '****', advice => __x('Password mismatch, make sure passwords are ' . 'identical') ); } } return 1; } # Method: cmp # # This method is overrien because we cannot sort the passwords (do so would be # given away clues about their value) but we need to know where they are equal # or we wil lhave trouble # # Overrides: # sub cmp { my ($self, $other) = @_; my $cmpRes = $self->SUPER::cmp($other); if (not defined $cmpRes ) { # no comparable case return undef; } elsif ($cmpRes == 0) { # equal case return 0; } # other cases we return 1 to have a non-content dependent order return 1; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/Link.pm0000664000000000000000000000542112017102272016604 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Types::Link # # This class represents a type which contains a hyperlink which # it will be used to set the content of a model field. # # This is very useful when is not # sufficient. For example, in order to configurate something # which requires the configuration of a whole eBox module, you # can advise the user setting this link so that he visits this. # package EBox::Types::Link; use strict; use warnings; use base 'EBox::Types::Text'; use EBox::Exceptions::InvalidData; use EBox::Exceptions::NotImplemented; use EBox::Gettext; use EBox::Validate; # Group: Public methods # Constructor: new # # Create the type # # Returns: # # - the newly created type # sub new { my $class = shift; my %opts = @_; unless (exists $opts{'HTMLViewer'}) { $opts{'HTMLViewer'} ='/ajax/viewer/hasManyViewer.mas'; } $opts{'type'} = 'link'; $opts{'editable'} = 0; $opts{'optional'} = 1; my $self = $class->SUPER::new(%opts); bless ( $self, $class ); unless (exists $opts{'HTMLSetter'}) { $self->{'HTMLSetter'} = undef; } return $self; } # Method: linkToView # # Alias to method to be used by the # hasManyViewer # # Returns: # # String - the relative path to the eBox template # sub linkToView { my ($self) = @_; return $self->value(); } # Group: Protected methods # Method: _paramIsValid # # Check if the params has a correct link # # Overrides: # # # # Parameters: # # params - the HTTP parameters with contained the type # # Returns: # # true - if the parameter is a correct link address # # Exceptions: # # - throw if it's not a correct # link # sub _paramIsValid { my ($self, $params) = @_; my $value = $params->{$self->fieldName()}; if ( defined ( $value )) { EBox::Validate::checkFilePath($value, $self->printableName()); } return 1; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/MultiStateAction.pm0000664000000000000000000000576112017102272021147 0ustar # Copyright (C) 2011-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Types::MultiStateAction; use strict; use warnings; sub new { my $class = shift; my %opts = @_; my $self = {@_}; unless ($self->{'acquirer'} or $self->{'defaultState'}) { throw EBox::Exceptions::MissingArgument('acquirer'); } unless (defined ($self->{enabled})) { $self->{enabled} = 1; } bless ($self, $class); return $self; } sub state { my ($self, $id) = @_; my $state; if ($self->{'acquirer'}) { $state = $self->{'acquirer'}->($self->{model}, $id); } elsif ($self->{'defaultState'}) { $state = $self->{'defaultState'}; } else { $state = (keys %{$self->{'states'}})[0]; } return $state; } sub action { my ($self, $id) = @_; my $stateName = $self->state($id); # XXX: Dependency cycle, Action inherits from MultiStateAction my $state = $self->{states}->{$stateName}; my $action = new EBox::Types::Action( name => $state->{name}, printableValue => $state->{printableValue}, handler => $state->{handler}, image => $state->{image}, message => $state->{message}, enabled => $state->{enabled}, model => $self->{model}, ); return $action; } sub name { my ($self, $id) = @_; return $self->action($id)->{name}; } sub printableValue { my ($self, $id) = @_; return $self->action($id)->{printableValue}; } sub message { my ($self, $id) = @_; return $self->action($id)->{message}; } sub handle { my ($self, $id, %params) = @_; $self->action($id)->{handler}->($self->{model}, $self, $id, %params); } sub image { my ($self, $id, %params) = @_; my $image = $self->action($id)->{image}; $image = '/data/images/run.gif' unless ($image); return $image; } sub enabled { my ($self, $id) = @_; my $action = $self->action($id); my $enabled = $action->{enabled}; if (ref $enabled) { $enabled = &$enabled; } return $enabled; } sub onclick { my ($self, $id) = @_; my $onclick; my $handler = $self->action($id)->{onclick}; if ($handler) { $onclick = $handler->($self->{model}, $id); } unless ($onclick) { $onclick = $self->{model}->customActionClickedJS($self->name($id), $id); $onclick .= '; return false'; } return $onclick; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/MultiSelect.pm0000664000000000000000000001126612017102272020145 0ustar # Copyright (C) 2011-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Types::MultiSelect; use strict; use warnings; use base 'EBox::Types::Select'; use EBox; use EBox::Gettext; use EBox::Exceptions::Internal; use EBox::Exceptions::MissingArgument; use EBox::Exceptions::NotImplemented; ################## # Dependencies: ################## use Perl6::Junction qw(any); # Group: Public methods sub new { my $class = shift; my %opts = @_; unless (exists $opts{'HTMLSetter'}) { $opts{'HTMLSetter'} ='/ajax/setter/multiSelectSetter.mas'; } my $self = $class->SUPER::new(%opts); bless($self, $class); return $self; } # Method: printableValue # # Overrides: # # # sub printableValue { my ($self) = @_; # Cache the current options my $options = $self->options(); return '' unless (defined($options)); my $value = $self->value(); my @printableValues; foreach my $option (@{$options}) { if ($option->{'value'} eq any(@{$value})) { push( @printableValues, $option->{'printableValue'} ); } } return join(', ', @printableValues); } # Method: fields # # Get the list of fields of interest for the type # # Overrides: # # # sub fields { my ($self) = @_; return map { $self->fieldName() . "_" . $_->{'value'} } @{ $self->options() }; } # Method: value # # Overrides: # # # sub value { my ($self) = @_; if (defined($self->{'value'})) { return $self->{'value'}; } else { return []; } } sub _setValue { my ($self, $values) = @_; my $params; my @mappedValues; my $options = $self->options(); foreach my $option ( @{$options} ) { if ( $option->{printableValue} eq any(@{$values}) or $option->{value} eq any(@{$values}) ) { push ( @mappedValues, $option->{value} ); } } $params = { $self->fieldName() => \@mappedValues }; $self->setMemValue($params); } # Method: _setMemValue # # Overrides: # # # sub _setMemValue { my ($self, $params) = @_; if ( $params->{ $self->fieldName() } ) { $self->{'value'} = $params->{$self->fieldName()}; } else { my @values; foreach my $fieldName ( $self->fields() ) { if ( $params->{$fieldName} ) { push ( @values, $params->{$fieldName} ); } } $self->{'value'} = \@values; } } # Method: cmp # # # Warning: # We compare printableValues because it has more sense for the user # (especially when we have a foreignModel and the values are row Ids). # However there may be many cases when this would not be appropiate. # # Overrides: # sub cmp { my ($self, $other) = @_; if (ref($self) ne ref($other)) { return undef; } my $cmpContext = 0; if (defined $other->{cmpContext}) { $cmpContext = $self->{cmpContext} cmp $other->{cmpContext}; } if ($cmpContext == 0) { # TODO: Compare sets of groups return ($self->printableValue() cmp $other->printableValue()); } else { return $cmpContext; } } # Group: Protected methods # Method: _paramIsSet # # Overrides: # # # sub _paramIsSet { my ($self, $params) = @_; return 1; } # Method: _paramIsValid # # Overrides: # # # sub _paramIsValid { my ($self, $params) = @_; return 1; } # Method: _storeInHash # # Overrides: # # # # TODO: It's currently used in LDAP-stored models, implement this if used in # redis-stored models. sub _storeInHash { throw EBox::Exceptions::NotImplemented(); } # Method: _restoreFromHash # # Overrides: # # # # TODO: It's currently used in LDAP-stored models, implement this if used in # redis-stored models. sub _restoreFromHash { throw EBox::Exceptions::NotImplemented(); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Types/HostIP.pm0000664000000000000000000000461312017102272017057 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Types::HostIP # # A specialised text type to represent an host IP address, that # is, those IP addresses whose netmask is equal to 32 # package EBox::Types::HostIP; use strict; use warnings; use base 'EBox::Types::Text'; use EBox::Validate; # Dependencies use Net::IP; # Group: Public methods # Constructor: new # # The constructor for the # # Returns: # # the recently created object # sub new { my $class = shift; my $self = $class->SUPER::new(@_); $self->{'type'} = 'hostip'; bless($self, $class); return $self; } # Method: cmp # # Overrides: # # # sub cmp { my ($self, $compareType) = @_; unless ( (ref $self) eq (ref $compareType) ) { return undef; } my $ipA = new Net::IP($self->value()); my $ipB = new Net::IP($compareType->value()); if ( $ipA->bincomp('lt', $ipB) ) { return -1; } elsif ( $ipA->bincomp('gt', $ipB)) { return 1; } else { return 0; } } # Group: Protected methods # Method: _paramIsValid # # Check if the params has a correct host IP address # # Overrides: # # # # Parameters: # # params - the HTTP parameters with contained the type # # Returns: # # true - if the parameter is a correct host IP address # # Exceptions: # # - throw if it's not a correct # host IP address # sub _paramIsValid { my ($self, $params) = @_; my $value = $params->{$self->fieldName()}; if (defined ($value)) { EBox::Validate::checkIP($value, $self->printableName()); } return 1; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Apache.pm0000664000000000000000000005425112017102272015771 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Apache; use strict; use warnings; use base qw(EBox::Module::Service); use EBox::Validate qw( :all ); use EBox::Sudo; use EBox::Global; use EBox::Service; use EBox::Menu; use HTML::Mason::Interp; use EBox::Exceptions::InvalidData; use EBox::Exceptions::InvalidType; use EBox::Exceptions::Internal; use EBox::Exceptions::DataExists; use EBox::Exceptions::DataMissing; use EBox::Exceptions::DataNotFound; use EBox::Exceptions::MissingArgument; use EBox::Gettext; use EBox::Config; use English qw(-no_match_vars); use File::Basename; use File::Slurp; use POSIX qw(setsid setlocale LC_ALL); use Error qw(:try); # Constants use constant INCLUDE_KEY => 'includes'; use constant CAS_KEY => 'cas'; use constant CA_CERT_PATH => EBox::Config::conf() . 'ssl-ca/'; use constant NO_RESTART_ON_TRIGGER => EBox::Config::tmp() . 'apache_no_restart_on_trigger'; sub _create { my $class = shift; my $self = $class->SUPER::_create(name => 'apache', printableName => __('Zentyal Webadmin'), @_); bless($self, $class); return $self; } sub serverroot { return '/var/lib/zentyal'; } # Method: cleanupForExec # # It does the job to prepare a forked apache process to do an exec. # We should use spawn_proc_prog() from mod_perl but we experience # some issues. # # sub cleanupForExec { POSIX::setsid(); opendir(my $dir, "/proc/$$/fd"); while (defined(my $fd = readdir($dir))) { next unless ($fd =~ /^\d+$/); eval('POSIX::close($fd)'); } open(STDOUT, '> /dev/null'); open(STDERR, '> /dev/null'); open(STDIN, '/dev/null'); } # restarting apache from inside apache could be problematic, so we fork() sub _daemon { my ($self, $action) = @_; my $conf = EBox::Config::conf(); my $ctl = "APACHE_CONFDIR=$conf apache2ctl"; # Sometimes apache is running but for some reason apache.pid does not # exist, with this workaround we always ensure a successful restart my $pidfile = EBox::Config::tmp() . 'apache.pid'; my $pid; unless (-f $pidfile) { $pid = `ps aux|grep 'apache2 -d $conf'|awk '/^root/{print \$2;exit}'`; write_file($pidfile, $pid) if $pid; } if ($action eq 'stop') { EBox::Sudo::root("$ctl stop"); } elsif ($action eq 'start') { EBox::Sudo::root("$ctl start"); } elsif ($action eq 'restart') { unless (defined($pid = fork())) { throw EBox::Exceptions::Internal("Cannot fork()."); } if ($pid) { return; # parent returns inmediately } else { EBox::Sudo::root("$ctl restart"); exit ($?); } } if ($action eq 'stop') { # Stop redis server $self->{redis}->stopRedis(); } } sub _stopService { my $self = shift; $self->_daemon('stop'); } sub _setConf { my ($self) = @_; $self->_setLanguage(); $self->_writeHttpdConfFile(); $self->_writeCSSFiles(); $self->_reportAdminPort(); $self->_setDesktopServicesPort(); $self->enableRestartOnTrigger(); } sub _enforceServiceState { my ($self) = @_; $self->_daemon('restart'); } sub _writeHttpdConfFile { my ($self) = @_; # Write CA links $self->_writeCAPath(); my $httpdconf = _httpdConfFile(); my $output; my $interp = HTML::Mason::Interp->new(out_method => \$output); my $comp = $interp->make_component( comp_file => (EBox::Config::stubs . 'core/apache.mas')); my @confFileParams = (); push @confFileParams, ( port => $self->port()); push @confFileParams, ( user => EBox::Config::user()); push @confFileParams, ( group => EBox::Config::group()); push @confFileParams, ( serverroot => $self->serverroot()); push @confFileParams, ( tmpdir => EBox::Config::tmp()); push @confFileParams, ( eboxconfdir => EBox::Config::conf()); push @confFileParams, ( restrictedResources => $self->get_list('restricted_resources') ); push @confFileParams, ( includes => $self->_includes(1) ); my $desktop_services_enabled = EBox::Config::configkey('desktop_services_enabled'); my $desktop_services_port = EBox::Config::configkey('desktop_services_port'); push @confFileParams, ( desktop_services_enabled => $desktop_services_enabled ); push @confFileParams, ( desktop_services_port => $desktop_services_port ); my $debugMode = EBox::Config::boolean('debug'); push @confFileParams, ( debug => $debugMode); $interp->exec($comp, @confFileParams); my $confile = EBox::Config::tmp . "httpd.conf"; unless (open(HTTPD, "> $confile")) { throw EBox::Exceptions::Internal("Could not write to $confile"); } print HTTPD $output; close(HTTPD); EBox::Sudo::root("/bin/mv $confile $httpdconf"); } sub _setLanguage { my ($self) = @_; my $languageModel = $self->model('Language'); # TODO: do this only if language has changed? my $lang = $languageModel->value('language'); EBox::setLocale($lang); POSIX::setlocale(LC_ALL, EBox::locale()); EBox::Menu::regenCache(); } sub _writeCSSFiles { my ($self) = @_; my $path = EBox::Config::dynamicwww() . '/css'; unless (-d $path) { mkdir $path; } my ($primaryGid) = split / /, $GID, 2; my $global = EBox::Global->getInstance(); my $theme = $global->theme(); my %params = %{ $theme }; foreach my $file ('public.css', 'login.css', 'tableorderer.css') { EBox::Module::Base::writeConfFileNoCheck("$path/$file", "css/$file.mas", [ %params ], { mode => '0644', uid => $UID, gid => $primaryGid}); } } # write CA Certificate Path with included CAs sub _writeCAPath { my ($self) = @_; system('rm -rf ' . CA_CERT_PATH); mkdir(CA_CERT_PATH); # Write links for each CA foreach my $ca (@{$self->_CAs(1)}) { my $link = $self->_caLinkPath($ca); unlink($link) if ( -l $link ); symlink($ca, $link); } } # Return the link name for the CA certificate in the given format # hashValue.0 - hash value is the output from openssl ciphering sub _caLinkPath { my ($self, $ca) = @_; my $hashRet = EBox::Sudo::command("openssl x509 -hash -noout -in $ca"); my $hashValue = $hashRet->[0]; chomp($hashValue); return CA_CERT_PATH . "${hashValue}.0"; } # Report the new TCP admin port to Zentyal Cloud sub _reportAdminPort { my ($self) = @_; my $global = EBox::Global->getInstance(1); if ($global->modExists('remoteservices')) { my $rs = $global->modInstance('remoteservices'); $rs->reportAdminPort($self->port()); } } sub _httpdConfFile { return '/var/lib/zentyal/conf/apache2.conf'; } sub _setDesktopServicesPort { my $desktop_services_port = (EBox::Config::configkey('desktop_services_port') or 6895); checkPort($desktop_services_port, __("Desktop services port")); my $fw = EBox::Global->modInstance('firewall'); my $services = EBox::Global->modInstance('services'); if (defined($fw)) { my $serviceName = 'desktop-services'; unless ( $services->serviceExists(name => $serviceName) ) { $fw->addInternalService( 'name' => $serviceName, 'printableName' => __('Desktop Services'), 'description' => __('Desktop Services (API for Zentyal Desktop)'), 'protocol' => 'tcp', 'sourcePort' => 'any', 'destinationPort' => $desktop_services_port, ); $fw->saveConfigRecursive(); } else { my $currentConf = $services->serviceConfiguration($services->serviceId($serviceName)); if ( $currentConf->[0]->{destination} ne $desktop_services_port ) { $services->setService(name => $serviceName, printableName => __('Desktop Services'), description => __('Desktop Services (API for Zentyal Desktop)'), protocol => 'tcp', sourcePort => 'any', destinationPort => $desktop_services_port, internal => 1, readOnly => 1); $services->saveConfigRecursive(); } } } } # Method: initialSetup # # Overrides: # EBox::Module::Base::initialSetup # sub initialSetup { my ($self, $version) = @_; # Create default rules and services # only if installing the first time unless ($version) { $self->_setDesktopServicesPort(); } # Execute initial-setup script $self->SUPER::initialSetup($version); } sub port { my ($self) = @_; return $self->model('AdminPort')->value('port'); } # Method: setPort # # Set the listening port for the apache perl # # Parameters: # # port - Int the new listening port # sub setPort # (port) { my ($self, $port) = @_; checkPort($port, __("port")); my $adminPortModel = $self->model('AdminPort'); my $oldPort = $adminPortModel->value('port'); return if ($oldPort == $port); $self->checkAdminPort($port); $adminPortModel->setValue('port', $port); $self->updateAdminPortService($port); } sub checkAdminPort { my ($self, $port) = @_; my $global = EBox::Global->getInstance(); my $fw = $global->modInstance('firewall'); if (defined($fw)) { unless ($fw->availablePort("tcp",$port)) { throw EBox::Exceptions::External(__x( 'Zentyal is already configured to use port {p} for another service. Choose another port or free it and retry.', p => $port )); } } my $netstatLines = EBox::Sudo::root('netstat -tlnp'); foreach my $line (@{ $netstatLines }) { my ($proto, $recvQ, $sendQ, $localAddr, $foreignAddr, $state, $PIDProgram) = split '\s+', $line, 7; if ($localAddr =~ m/:$port$/) { my ($pid, $program) = split '/', $PIDProgram; throw EBox::Exceptions::External(__x( q{Port {p} is already in use by program '{pr}'. Choose another port or free it and retry.}, p => $port, pr => $program, ) ); } } } sub updateAdminPortService { my ($self, $port) = @_; my $global = $self->global(); if ($global->modExists('services')) { my $services = $global->modInstance('services'); $services->setAdministrationPort($port); } } sub logs { my @logs = (); my $log; $log->{'module'} = 'apache'; $log->{'table'} = 'access'; $log->{'file'} = EBox::Config::log . "/access.log"; my @fields = qw{ host www_user date method url protocol code size referer ua }; $log->{'fields'} = \@fields; $log->{'regex'} = '(.*?) - (.*?) \[(.*)\] "(.*?) (.*?) (.*?)" (.*?) (.*?) "(.*?)" "(.*?)" "-"'; my @types = qw{ inet varchar timestamp varchar varchar varchar integer integer varchar varchar }; $log->{'types'} = \@types; push(@logs, $log); return \@logs; } # Method: setRestrictedResource # # Set a restricted resource to the Apache perl configuration # # Parameters: # # resourceName - String the resource name to restrict # # allowedIPs - Array ref the set of IPs which allow the # restricted resource to be accessed in CIDR format or magic word # 'all' or 'nobody'. The former all sources are allowed to see # that resourcename and the latter nobody is allowed to see this # resource. 'all' value has more priority than 'nobody' value. # # resourceType - String the resource type: It can be one of the # following: 'file', 'directory' and 'location'. # # Exceptions: # # - thrown if any compulsory # argument is missing # # - thrown if the resource type # is invalid # # - thrown if any of the allowed IP # addresses are not in CIDR format or no allowed IP is given # sub setRestrictedResource { my ($self, $resourceName, $allowedIPs, $resourceType) = @_; throw EBox::Exceptions::MissingArgument('resourceName') unless defined ( $resourceName ); throw EBox::Exceptions::MissingArgument('allowedIPs') unless defined ( $allowedIPs ); throw EBox::Exceptions::MissingArgument('resourceType') unless defined ( $resourceType ); unless ( $resourceType eq 'file' or $resourceType eq 'directory' or $resourceType eq 'location' ) { throw EBox::Exceptions::InvalidType('resourceType', 'file, directory or location'); } my $allFound = grep { $_ eq 'all' } @{$allowedIPs}; my $nobodyFound = grep { $_ eq 'nobody' } @{$allowedIPs}; if ( $allFound ) { $allowedIPs = ['all']; } elsif ( $nobodyFound ) { $allowedIPs = ['nobody']; } else { # Check the given list is a list of IPs my $notIPs = grep { ! checkCIDR($_) } @{$allowedIPs}; if ( $notIPs > 0 ) { throw EBox::Exceptions::Internal('Some of the given allowed IP' . 'addresses are not in CIDR format'); } if ( @{$allowedIPs} == 0 ) { throw EBox::Exceptions::Internal('Some allowed IP must be set'); } } my $resources = $self->get_list('restricted_resources'); push (@{$resources}, { name => $resourceName, allowedIPs => $allowedIPs, type => $resourceType}); $self->set('restricted_resources', $resources); } # Method: delRestrictedResource # # Remove a restricted resource from the list # # Parameters: # # resourcename - String the resource name which indexes which restricted # resource is requested to be deleted # # Exceptions: # # - thrown if any compulsory # argument is missing # # - thrown if the given resource name is # not in the list of restricted resources # sub delRestrictedResource { my ($self, $resourcename) = @_; throw EBox::Exceptions::MissingArgument('resourcename') unless defined ($resourcename); $resourcename =~ s:^/::; my $resources = $self->get_list('restricted_resources'); unless (exists $resources->{$resourcename}) { throw EBox::Exceptions::DataNotFound(data => 'resourcename', value => $resourcename); } my @deleted = grep { $_ ne $resourcename} @{$resources}; $self->set('restricted_resources', \@deleted); } # Method: isEnabled # # Overrides: # EBox::Module::Service::isEnabled sub isEnabled { # apache always has to be enabled return 1; } # Method: showModuleStatus # # Indicate to ServiceManager if the module must be shown in Module # status configuration. # # Overrides: # EBox::Module::Service::showModuleStatus # sub showModuleStatus { # we don't want it to appear in module status return undef; } # Method: addModuleStatus # # Do not show entry in the module status widget # # Overrides: # EBox::Module::Service::addModuleStatus # sub addModuleStatus { } # Method: addInclude # # Add an "include" directive to the apache configuration # # Added only in the main virtual host # # Parameters: # # includeFilePath - String the configuration file path to include # in apache configuration # # Exceptions: # # - thrown if any compulsory # argument is missing # # - thrown if the given file does # not exists # sub addInclude { my ($self, $includeFilePath) = @_; unless(defined($includeFilePath)) { throw EBox::Exceptions::MissingArgument('includeFilePath'); } unless(-f $includeFilePath and -r $includeFilePath) { throw EBox::Exceptions::Internal( "File $includeFilePath cannot be read or it is not a file" ); } my @includes = @{$self->_includes(0)}; unless ( grep { $_ eq $includeFilePath } @includes) { push(@includes, $includeFilePath); $self->set_list(INCLUDE_KEY, 'string', \@includes); } } # Method: removeInclude # # Remove an "include" directive to the apache configuration # # Parameters: # # includeFilePath - String the configuration file path to remove # from apache configuration # # Exceptions: # # - thrown if any compulsory # argument is missing # # - thrown if the given file has not # been included previously # sub removeInclude { my ($self, $includeFilePath) = @_; unless(defined($includeFilePath)) { throw EBox::Exceptions::MissingArgument('includeFilePath'); } my @includes = @{$self->_includes(0)}; my @newIncludes = grep { $_ ne $includeFilePath } @includes; if ( @newIncludes eq @includes ) { throw EBox::Exceptions::Internal("$includeFilePath has not been included previously"); } $self->set_list(INCLUDE_KEY, 'string', \@newIncludes); } # Return those include files that has been added sub _includes { my ($self, $check) = @_; my $includeList = $self->get_list(INCLUDE_KEY); if (not $check) { return $includeList } my @includes; foreach my $incPath (@{ $includeList }) { if ((-f $incPath) and (-r $incPath)) { push @includes, $incPath; } else { EBox::warn("Ignoring apache include $incPath: cannot read the file or not is a regular file"); } } return \@includes; } # Method: addCA # # Include the given CA in the SSLCACertificatePath # # Parameters: # # ca - CA Certificate # # Exceptions: # # - thrown if any compulsory # argument is missing # # - thrown if the given file does # not exists # sub addCA { my ($self, $ca) = @_; unless(defined($ca)) { throw EBox::Exceptions::MissingArgument('includeFilePath'); } unless(-f $ca and -r $ca) { throw EBox::Exceptions::Internal( "File $ca cannot be read or it is not a file" ); } my @cas = @{$self->_CAs(0)}; unless ( grep { $_ eq $ca } @cas) { push(@cas, $ca); $self->set_list(CAS_KEY, 'string', \@cas); } } # Method: removeCA # # Remove a previously added CA from the SSLCACertificatePath # # Parameters: # # ca - CA certificate # # Exceptions: # # - thrown if any compulsory # argument is missing # # - thrown if the given file has not # been included previously # sub removeCA { my ($self, $ca) = @_; unless(defined($ca)) { throw EBox::Exceptions::MissingArgument('includeFilePath'); } unless(-f $ca and -r $ca) { throw EBox::Exceptions::Internal( "File $ca cannot be read or it is not a file" ); } my @cas = @{$self->_CAs(0)}; my @newCAs = grep { $_ ne $ca } @cas; if ( @newCAs eq @cas ) { throw EBox::Exceptions::Internal("$ca has not been included previously"); } $self->set_list(CAS_KEY, 'string', \@newCAs); } # Return those include files that has been added sub _CAs { my ($self, $check) = @_; my $caList = $self->get_list(CAS_KEY); if (not $check) { return $caList; } my @cas; foreach my $ca (@{ $caList }) { if ((-f $ca) and (-r $ca)) { push @cas, $ca; } else { EBox::warn("Ignoring CA $ca: cannot read the file or not is a regular file"); } } return \@cas; } # Method: certificates # # This method is used to tell the CA module which certificates # and its properties we want to issue for this service module. # # Returns: # # An array ref of hashes containing the following: # # service - name of the service using the certificate # path - full path to store this certificate # user - user owner for this certificate file # group - group owner for this certificate file # mode - permission mode for this certificate file # sub certificates { my ($self) = @_; return [ { service => __('Zentyal Administration Web Server'), path => '/var/lib/zentyal/conf/ssl/ssl.pem', user => 'ebox', group => 'ebox', mode => '0600', }, ]; } # Method: disableRestartOnTrigger # # Makes apache and other modules listed in the restart-trigger script to # ignore it and do nothing sub disableRestartOnTrigger { system 'touch ' . NO_RESTART_ON_TRIGGER; if ($? != 0) { EBox::warn('Canot create apache no restart on trigger file'); } } # Method: enableRestartOnTrigger # # Makes apache and other modules listed in the restart-trigger script to # restart themselves when the script is executed (default behaviour) sub enableRestartOnTrigger { EBox::Sudo::root("rm -f " . NO_RESTART_ON_TRIGGER); } # Method: restartOnTrigger # # Whether apache and other modules listed in the restart-trigger script to # restart themselves when the script is executed sub restartOnTrigger { return not EBox::Sudo::fileTest('-e', NO_RESTART_ON_TRIGGER); } sub usesPort { my ($self, $proto, $port, $iface) = @_; if ($proto ne 'tcp') { return 0; } return $port == $self->port(); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Migration/0000775000000000000000000000000012017102272016174 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Migration/Base.pm0000664000000000000000000000415712017102272017413 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Migration::Base; use strict; use warnings; use EBox; sub new { my $class = shift; my %opts = @_; my $confmodule = delete $opts{'confmodule'}; my $version = delete $opts{'version'}; my $self = { 'confmodule' => $confmodule, 'version' => $version }; bless($self, $class); return $self; } sub _checkCurrentGConfVersion { my $self = shift; my $currentVer = $self->{'confmodule'}->get_int("data_version"); if (not defined($currentVer)) { $currentVer = 0; } $currentVer++; return ($currentVer eq $self->{'version'}); } sub _setCurrentGConfVersion { my $self = shift; $self->{'confmodule'}->set_int("data_version", $self->{'version'}); } sub _saveGConfChanges { my $self = shift; $self->{'confmodule'}->saveConfigRecursive(); } sub executeGConf { my $self = shift; my $name = $self->{'confmodule'}->name(); my $version = $self->{'version'}; if ($self->_checkCurrentGConfVersion()) { EBox::debug("Migrating $name to $version"); $self->runGConf(); $self->_setCurrentGConfVersion(); $self->_saveGConfChanges(); } else { EBox::debug("Skipping migration to $version in $name"); } } sub execute { my $self = shift; if (defined($self->{'confmodule'})) { $self->executeGConf(); } } # Method: runGConf # # This method must be overriden by each migration script to do # the neccessary changes to the data model stored in conf to migrate # between two consecutive versions sub runGConf { } 1; zentyal-core-2.3.21+quantal1/src/EBox/Migration/Helpers.pm0000664000000000000000000000536612017102272020146 0ustar # Copyright (C) 2009-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Migration::Helpers; use strict; use warnings; use EBox; use EBox::DBEngineFactory; use Error qw(:try); # SQL helpers sub runQuery { my ($query) = @_; my $error = 0; try { my $dbengine = EBox::DBEngineFactory::DBEngine(); $dbengine->do($query); } otherwise { $error = 1; }; return $error; } sub renameTable { my ($oldTable, $newTable) = @_; my $exists_query = "SELECT COUNT(*) FROM $oldTable"; my @queries = ( "ALTER TABLE $newTable RENAME TO $newTable" . "_new", "ALTER TABLE $oldTable RENAME TO $newTable", "INSERT INTO $newTable SELECT * FROM $newTable" . "_new", "DROP TABLE $newTable" . "_new" ); my $res = runQuery($exists_query); if ($res == 0) { for my $q (@queries) { runQuery($q); } } } sub renameConsolidationTable { my ($oldTable, $newTable) = @_; my @types = ('hourly', 'daily', 'weekly', 'monthly'); for my $t (@types) { renameTable($oldTable . "_$t", $newTable . "_$t"); } } sub renameField { my ($table, $oldField, $newField) = @_; my $query = "ALTER TABLE $table RENAME COLUMN $oldField TO $newField"; runQuery($query); } sub createIndex { my ($table, $field) = @_; my $query = "CREATE INDEX $table" . "_$field" . "_i ON $table($field)"; runQuery($query); } sub createTimestampIndex { my ($table) = @_; createIndex($table, 'timestamp'); } sub dropIndex { my ($index) = @_; my $query = "DROP INDEX $index"; runQuery($query); } sub addColumn { my ($table, $column, $columnData) = @_; my $exists_query = "SELECT COUNT(*) FROM $table"; my $res = runQuery($exists_query); if ($res == 0) { $exists_query = "SELECT $column FROM $table LIMIT 1"; my $exists = runQuery($exists_query) == 0; if ($exists) { return; } my $addColumnQuery = "ALTER TABLE $table " . "ADD COLUMN $column " . "$columnData"; runQuery($addColumnQuery); } } 1; zentyal-core-2.3.21+quantal1/src/EBox/Util/0000775000000000000000000000000012017102272015160 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Util/Version.pm0000664000000000000000000000274012017102272017146 0ustar # Copyright (C) 2010-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Util::Version; use strict; use warnings; # Function: compare # # Compare two versions given its strings # # Parameters: # # v1 - String first version # # v2 - String second version # # Returns: # # The same as built-in cmp does. # # Examples: # # compare('1.2', '1.2') => 0 # compare('1.3', '1.3.1') => -1 # compare('2.0', '1.9.9') => 1 # sub compare { my ($v1, $v2) = @_; my @v1 = split(/\./, $v1); my @v2 = split(/\./, $v2); my $min_len; if (scalar(@v1) < scalar(@v2)) { $min_len = scalar(@v1); } else { $min_len = scalar(@v2); } for (my $i = 0; $i < $min_len; $i++) { my $cmp = ($v1[$i] <=> $v2[$i]); $cmp and return $cmp; } return (scalar(@v1) <=> scalar(@v2)); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Util/Debconf.pm0000664000000000000000000000236212017102272017061 0ustar # Copyright (C) 2011-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # package EBox::Util::Debconf; use strict; use warnings; use Debconf::Db; use Debconf::Question; # Method: value # # Gets the value of the first debconf key that matches the given name # # Parameters: # # name - name of the key # # Returns # # string with the value of the key or undef if key not found # sub value { my ($name) = @_; Debconf::Db->load(readonly => 1); my $it = Debconf::Question->iterator(); while (my $key = $it->iterate()) { next unless ($key->name() eq $name); return $key->value(); } return undef; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Util/SystemKernel.pm0000664000000000000000000000220512017102272020142 0ustar # Copyright (C) 2010-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Util::SystemKernel; use EBox; use AptPkg::Cache; use strict; sub kernels { my $cache = AptPkg::Cache->new; my @flavours = ('server', 'virtual', 'ec2', '386', 'generic', 'generic-pae'); my @kernels = (); foreach my $flavour (@flavours) { if ($cache->{'linux-image-' . $flavour}) { if ( $cache->{'linux-image-' . $flavour}{CurrentState} eq 'Installed') { push (@kernels, 'linux-image-' . $flavour); } } } return \@kernels; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Util/Init.pm0000664000000000000000000001156012017102272016424 0ustar # Copyright (C) 2011-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Util::Init; use strict; use warnings; use EBox; use EBox::Global; use EBox::Config; use EBox::Sudo; use EBox::ServiceManager; use File::Slurp; use Error qw(:try); sub cleanTmpOnBoot { if (not exists $ENV{'USER'}) { my $tmpdir = EBox::Config::tmp(); EBox::Sudo::root("rm -rf $tmpdir/*"); } } sub moduleList { print "Module list: \n"; my $global = EBox::Global->getInstance(1); my @mods = @{$global->modInstancesOfType('EBox::Module::Service')}; my @names = map { $_->{name} } @mods; print join(' ', @names); print "\n"; } sub checkModule { my ($modname) = @_; my $global = EBox::Global->getInstance(1); my $mod = $global->modInstance($modname); if (!defined $mod) { print STDERR "$modname is not a valid module name\n"; moduleList(); exit 2; } if(!$mod->isa("EBox::Module::Service")) { print STDERR "$modname is a not manageable module name\n"; moduleList(); exit 2; } return $mod; } sub start { my $serviceManager = new EBox::ServiceManager; my @mods = @{$serviceManager->modulesInDependOrder()}; my @names = map { $_->{'name'} } @mods; @names = grep { $_ ne 'apache' } @names; push(@names, 'apache'); EBox::info("Modules to start: @names"); foreach my $modname (@names) { moduleAction($modname, 'restartService', 'start'); } EBox::info("Start modules finished"); } sub stop { my $serviceManager = new EBox::ServiceManager; my @mods = @{$serviceManager->modulesInDependOrder()}; my @names = map { $_->{'name'} } @mods; @names = grep { $_ ne 'apache' } @names; unshift(@names, 'apache'); EBox::info("Modules to stop: @names"); foreach my $modname (reverse @names) { moduleAction($modname, 'stopService', 'stop'); } EBox::info("Stop modules finished"); } sub moduleAction { my ($modname, $action, $actionName) = @_; my $mod = checkModule($modname); #exits if module is not manageable # Do not restart apache if we are run under zentyal-software if ($actionName eq 'restart' and $modname eq 'apache' ) { return if (exists $ENV{'EBOX_SOFTWARE'} and $ENV{'EBOX_SOFTWARE'} == 1 ); } my $redisTrans = $modname ne 'network'; my $success; my $errorMsg; my $redis = $mod->redis(); try { $redis->begin() if ($redisTrans); $mod->$action(); $redis->commit() if ($redisTrans); $success = 0; } catch EBox::Exceptions::Base with { my $ex = shift; $success = 1; $errorMsg = $ex->text(); $redis->rollback() if ($redisTrans); } otherwise { my ($ex) = @_; $success = 1; $errorMsg = "$ex"; $redis->rollback() if ($redisTrans); }; printModuleMessage($modname, $actionName, $success, $errorMsg); } sub status { my ($modname, $action, $actionName) = @_; my $mod = checkModule($modname); #exits if module is not manageable my $msg = "EBox: status module $modname:\t\t\t"; my $enabled = $mod->isEnabled(); my $running = $mod->isRunning(); if ($enabled and $running) { print STDOUT $msg . "[ RUNNING ]\n"; exit 0; } elsif ($enabled and not $running) { print STDOUT $msg . "[ STOPPED ]\n"; exit 0; } elsif ((not $enabled) and $running) { print STDOUT $msg . "[ RUNNING UNMANAGED ]\n"; exit 3; } else { print STDOUT $msg . "[ DISABLED ]\n"; exit 3; } } sub _logActionFunction { my ($action, $success) = @_; system(". /lib/lsb/init-functions; " . " log_begin_msg \"$action\"; log_end_msg $success"); } sub printModuleMessage { my ($modname, $action, $success, $errorMsg) = @_; my %actions = ( 'start' => 'Starting', 'stop' => 'Stopping', 'restart' => 'Restarting' ); my $msg = $actions{$action} . " Zentyal module: $modname"; _logActionFunction($msg, $success); if ($errorMsg) { print STDERR $errorMsg, "\n"; } } sub moduleRestart { my ($modname) = @_; moduleAction($modname, 'restartService', 'restart'); } sub moduleStop { my ($modname) = @_; moduleAction($modname, 'stopService', 'stop'); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Util/Software.pm0000664000000000000000000000603212017102272017311 0ustar # Copyright (C) 2011-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Util::Software; # Class: EBox::Util::Software # # Utility functions to query the APT cache database # use strict; use warnings; use AptPkg::Cache; use File::stat; use Readonly; Readonly::Scalar my $APT_CHECK => '/usr/lib/update-notifier/apt-check'; Readonly::Scalar my $PACKAGES_DB => '/var/cache/apt/pkgcache.bin'; my $_cache; # Function: latestUpdate # # Latest apt-get update which modifies the available packages # database # # Returns: # # Int - the latest update timestamp in seconds since epoch # sub latestUpdate { my $status = stat($PACKAGES_DB); return $status->mtime(); } # Function: upgradablePkgsNum # # Return the number of packages that are upgradable with the # current APT policy # # Returns: # # Array ref - containing two elements: # # Int - the number of total updates # Int - the number of security updates # sub upgradablePkgsNum { # As output is in stderr, cannot manage with EBox::Sudo my @output = `$APT_CHECK 2>&1`; my $line = $output[0]; chomp($line); my @result = split(/;/, $line); return \@result } # Function: upgradablePkgs # # Return an array of upgradable pkgs names # # Returns: # # Array ref - containing the names of the upgradable packages # sub upgradablePkgs { my @output = `$APT_CHECK -p 2>&1`; my @packages = map { chomp(); $_ } @output; return \@packages; } # Function: isSecUpdate # # Return if a package has a candidate version from a security # repository # # Parameters: # # pkg - String the package to check # # Returns: # # 1 - if the update is a security update # 0 - otherwise # # Exceptions: # # - thrown if the given package name # is not in the cache # sub isSecUpdate { my ($pkg) = @_; unless ( defined($_cache) ) { $_cache = new AptPkg::Cache(); } my $pkgObj = $_cache->{$pkg}; unless ( defined($pkgObj) ) { throw EBox::Exceptions::Internal("$pkg is not in APT cache") } my $verObj = $_cache->policy()->candidate($pkgObj); my $security = 0; foreach my $verFile (@{$verObj->FileList()}) { my $file = $verFile->File(); next unless defined($file->{Archive}); if ( $file->{Archive} =~ /security/ ) { $security = 1; } last if ($security); } return $security; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Util/Random.pm0000664000000000000000000000300112017102272016730 0ustar # Copyright (C) 2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Util::Random; use strict; use warnings; use EBox::Exceptions::Internal; # Function: generate # # Generate a random string with the given length # # Parameters: # # len - Desired pasword length # # Returns: # # String with a generated random password # sub generate { my ($len) = @_; my $char; my $data; my @chars; $len = int($len); if ($len <= 0) { throw EBox::Exceptions::Internal('Wrong length argument'); } @chars = split(//, "abcdefghijklmnopqrstuvwxyz" . "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789@/="); open(RD, " 0) { read(RD, $char, 1) == 1 or die "Failed to read random data"; $data .= $chars[ord($char) % @chars]; } close(RD); return $data; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Util/SHM.pm0000664000000000000000000000375112017102272016153 0ustar # Copyright (C) 2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Util::SHM; use warnings; use strict; use EBox::Config; use EBox::Exceptions::Internal; use File::Basename; use JSON::XS; my $SHM_PATH = EBox::Config::shm(); sub setValue { my ($dir, $name, $value) = @_; my $hash = hash($dir); $hash->{$name} = $value; setHash($dir, $hash); } sub setHash { my ($key, $hash) = @_; my $path = "$SHM_PATH/"; $path .= dirname($key); unless (-d $path) { system ("mkdir -p $path"); } $path = "$SHM_PATH/$key"; open (my $fh, '>', $path) or throw EBox::Exceptions::Internal("SHM: Can't write to $path: $!"); print $fh encode_json($hash); close ($fh); } sub value { my ($dir, $name) = @_; my $hash = hash($dir); return $hash->{$name}; } sub hash { my ($key) = @_; my $path = "$SHM_PATH/$key"; unless (-e $path) { return {}; } open (my $fh, '<', "$SHM_PATH/$key") or throw EBox::Exceptions::Internal("SHM: Can't read $path: $!"); my $value = <$fh>; close ($fh); unless ($value) { return {}; } return decode_json($value); } sub deletekey { my ($key) = @_; unlink ("$SHM_PATH/$key"); } sub subkeys { my ($dir) = @_; opendir (my $dh, "$SHM_PATH/$dir"); my @keys = readdir($dh); closedir ($dh); return @keys; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Util/BugReport.pm0000664000000000000000000001257712017102272017443 0ustar # Copyright (C) 2011-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Util::BugReport; use strict; use warnings; use EBox::Config; use JSON::RPC::Client; use MIME::Base64; use File::Slurp; use Error qw(:try); use constant RPC_URL => 'http://trac.zentyal.org/jsonrpc'; use constant MILESTONE => '3.0'; use constant SOFTWARE_LOG => EBox::Config::log() . 'software.log'; # Method: send # # Send a bug report to Zentyal trac. It will also attach # a generated log # # Params: # - author_email - Reporter's email # - description - Text describing what the user was doing # - software - Include also software.log # # Returns: # Assigned ticket number on trac # # Throws EBox::Exceptions::Internal if something goes wrong # sub send { my ($author_email, $description) = @_; my $client = new JSON::RPC::Client; my $title = 'Bug report from Zentyal Server'; my $callobj = { method => 'ticket.create', params => [ $title, # summary $description, # description { reporter => $author_email, # author milestone => MILESTONE, # milestone }, 'true', # notify ], }; my $res = $client->call(RPC_URL, $callobj); if ($res) { unless ($res->is_success) { throw EBox::Exceptions::Internal('Error creating a new ticket in trac: ' . $res->error_message->{message}); return; } # Get ticket number and upload log my $ticket = $res->result; EBox::info('Created trac ticket #' . $ticket); _attach($client, $ticket, 'zentyal.log', EBox::Util::BugReport::dumpLog()); if (-f SOFTWARE_LOG) { my @brokenPackages = brokenPackagesList(); if (@brokenPackages) { _attach($client, $ticket, 'software.log', EBox::Util::BugReport::dumpSoftwareLog(@brokenPackages)); } } return $ticket; } else { throw EBox::Exceptions::Internal("Couldn't add the ticket, probably this is a connectivity issue"); } } # Method: dumpLog # # Returns a summary log for the server. It contains installed # zentyal packages and last 1000 lines from zentyal.log # sub dumpLog { my @log = read_file(EBox::Config::logfile()) or throw EBox::Exceptions::Internal("Error opening zentyal.log: $!"); my $res; $res .= "Installed packages\n"; $res .= "------------------\n\n"; my $output = EBox::Sudo::root("dpkg -l | grep zentyal | awk '{ print " . '$1 " " $2 ": " $3 ' . "}'"); $res .= join('', @{ $output }); $res .= "\n\n"; $res .= "/var/log/zentyal/zentyal.log\n"; $res .= "----------------------------\n\n"; $res .= _joinLastLines(1000, @log); return $res; } sub brokenPackagesList { my $output = EBox::Sudo::root("dpkg -l | tail -n +6 | grep -v ^ii | grep -v ^rc | awk '{ print " . '$1 " " $2 ": " $3 ' . "}'"); return @{ $output }; } # Method: dumpSoftwareLog # # Returns a summary log for the installation. It contains some system # and broken packages info and last 5000 lines from software.log # sub dumpSoftwareLog { my (@brokenPackages) = @_; my @log = read_file(SOFTWARE_LOG) or throw EBox::Exceptions::Internal("Error opening software.log: $!"); my $res; $res .= "System info\n"; $res .= "-----------\n"; $res .= `cat /etc/lsb-release`; $res .= `uname -rsmv`; $res .= "\n\n"; if (@brokenPackages) { $res .= "Broken packages\n"; $res .= "---------------\n"; $res .= "@brokenPackages\n\n"; } $res .= "/var/log/zentyal/software.log\n"; $res .= "----------------------------\n\n"; $res .= _joinLastLines(5000, @log); return $res; } sub _joinLastLines { my ($num, @lines) = @_; if (scalar (@lines) <= $num) { return join('', @lines); } else { return join('', @lines[-$num..-1]); } } sub _attach { my ($client, $ticket, $filename, $content) = @_; my $log = encode_base64($content); my $callobj = { method => 'ticket.putAttachment', params => [ $ticket, # ticket $filename, # filename $filename, # description {__jsonclass__ => [ 'binary', $log ]}, # file (base64 format) 'true', # replace ], }; my $res = $client->call(RPC_URL, $callobj); if ($res) { unless ($res->is_success) { throw EBox::Exceptions::Internal("Error attaching log to #$ticket: " . $res->error_message->{message}); return; } EBox::info("Attached $filename to #$ticket"); } } 1; zentyal-core-2.3.21+quantal1/src/EBox/Util/SQLTypes.pm0000664000000000000000000000361712017102272017211 0ustar # Copyright (C) 2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Util::SQLTypes; use warnings; use strict; use Socket; sub storer { my ($type, $value) = @_; my $storer = "_${type}_storer"; my $ref = __PACKAGE__->can($storer); if ($ref) { return $ref->($value); } else { return $value; } } sub stringifier { my ($type, $field) = @_; my $stringifier = "_${type}_stringifier"; my $ref = __PACKAGE__->can($stringifier); if ($ref) { return $ref->($field); } else { return $field; } } sub acquirer { my ($type, $field) = @_; my $stringifier = stringifier($type, $field); if ($stringifier eq $field) { return $field; } else { return "$stringifier AS $field"; } } sub _IPAddr_storer { my ($value) = @_; return unpack ('N', inet_aton($value)); } sub _IPAddr_stringifier { my ($field) = @_; return "INET_NTOA($field)"; } sub _MACAddr_storer { my ($value) = @_; $value =~ s/://g; return pack ('H*', $value); } sub _MACAddr_stringifier { my ($field) = @_; my $hex = "HEX($field)"; my $concat = "CONCAT_WS(':'"; for my $i (0..5) { $concat .= ", MID($hex, " . ($i*2 + 1) . ', 2)'; } $concat .= ")"; return $concat; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Util/SHMLock.pm0000664000000000000000000000344112017102272016760 0ustar # Copyright (C) 2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Util::SHMLock; use strict; use warnings; use EBox; use EBox::Config; use EBox::Exceptions::Internal; use Fcntl qw(:flock); sub init { my ($class, $name, $path) = @_; $path = EBox::Config::shm() unless defined ($path); my $self = {}; bless $self, $class; $self->{name} = $name; my $file = "$path/$name.lock"; $self->{file} = $file; unless (-d $path) { system ("mkdir -p $path"); } unless (-f $file) { open(LOCKFILE, ">$file") or throw EBox::Exceptions::Internal("Cannot create lockfile: $file"); close(LOCKFILE); } return $self; } sub unlock { my ($self) = @_; my $file = $self->{file}; open(LOCKFILE, ">$file") or throw EBox::Exceptions::Internal("Cannot open lockfile to unlock: $file"); flock(LOCKFILE, LOCK_UN); close(LOCKFILE); } sub lock { my ($self) = @_; my $file = $self->{file}; open(LOCKFILE, ">$file") or throw EBox::Exceptions::Internal("Cannot open lockfile to lock: $file"); flock(LOCKFILE, LOCK_EX) or throw EBox::Exceptions::Lock($self->{name}); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Util/SQL.pm0000664000000000000000000000742112017102272016161 0ustar # Copyright (C) 2011-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # package EBox::Util::SQL; use strict; use warnings; use EBox::DBEngineFactory; use EBox::Logs::Consolidate; use Perl6::Junction qw(any); use File::Basename; use File::Slurp; use constant SQL_TABLES_DIR => '/var/lib/zentyal/sql-tables/'; # Method: createCoreTables # # This method creates the regular SQL log tables under # /usr/share/zentyal/sql/*.sql and the time-period # tables under /usr/share/zentyal/sql/period/*.sql # sub createCoreTables { _createTables(EBox::Config::share() . 'zentyal/sql'); } # Method: createModuleTables # # This method creates the regular SQL log tables under # /usr/share/zentyal-$module/sql/*.sql and the time-period # tables under /usr/share/zentyal-$module/sql/period/*.sql # sub createModuleTables { my ($module) = @_; _createTables(EBox::Config::share() . "zentyal-$module/sql", $module); } sub _createTables { my ($path, $modname) = @_; my @names; foreach my $sqlfile (glob ("$path/*.sql")) { push (@names, _addTable($sqlfile)); } my @timePeriods = @{ EBox::Logs::Consolidate->timePeriods() }; foreach my $sqlfile (glob ("$path/period/*.sql")) { push (@names, _addTable($sqlfile, @timePeriods)); } # Write table names file to drop them in purge-module if (defined $modname) { unless (-d SQL_TABLES_DIR) { mkdir (SQL_TABLES_DIR); } my $filename = SQL_TABLES_DIR . $modname; write_file($filename, join ("\n", @names)); } } # Method: dropModuleTables # # This method drops the SQL table names stored at # /var/lib/zentyal/sql-tables/$module # sub dropModuleTables { my ($module) = @_; my $dbName = EBox::Config::configkey('eboxlogs_dbname'); my $tablesFile = SQL_TABLES_DIR . $module; unless (-f $tablesFile) { return; } my $dbengine = EBox::DBEngineFactory::DBEngine(); my @tables = read_file($tablesFile); return unless @tables; chomp (@tables); foreach my $table (@tables) { $dbengine->do("DROP TABLE $table"); } unlink ($tablesFile); } sub _addTable { my ($file, @timePeriods) = @_; my $dbengine = EBox::DBEngineFactory::DBEngine(); my $dbName = EBox::Config::configkey('eboxlogs_dbname'); my $dbUser = EBox::Config::configkey('eboxlogs_dbuser'); my $dbPass = $dbengine->_dbpass(); my $table = basename($file); $table =~ s/\.sql$//; if (@timePeriods) { my @names; foreach my $timePeriod (@timePeriods) { my $fullName = $table . '_' . $timePeriod; my $fileCmds = read_file($file); my $sqlCmds = $fileCmds; $sqlCmds =~ s/$table/$fullName/g; $dbengine->sqlAsSuperuser(sql => $sqlCmds); $dbengine->sqlAsSuperuser(sql => "GRANT SELECT, INSERT, UPDATE, DELETE ON $fullName TO '$dbUser'\@'localhost'"); push (@names, $fullName); } return @names; } else { $dbengine->sqlAsSuperuser(file => $file); $dbengine->sqlAsSuperuser(sql => "GRANT SELECT, INSERT, UPDATE, DELETE ON $dbName.$table TO '$dbUser'\@'localhost'"); return $table; } } 1; zentyal-core-2.3.21+quantal1/src/EBox/Util/Nmap.pm0000664000000000000000000000757112017102272016423 0ustar # Copyright (C) 2010-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA use strict; use warnings; package EBox::Util::Nmap; use EBox::Sudo; use EBox::Gettext; use EBox::Exceptions::MissingArgument; use EBox::Exceptions::InvalidData; use Nmap::Parser; # Function: singlePortScan # # Check if the given host and port status # # Named parameters: # # host - String the hostname # proto - protocol # port - Int the port number # interface - interface to use (default value: auto) # priviliged - use priviliged mode (default value: no) # # Returns: # # String - the status of the port in the given host. Possible # values are the following: # # 'hostdown' - if the host is not reachable # 'open' - if the port is open in that host # 'open/filtered' - if the port is open or filtered in that host # Other values # sub singlePortScan { my %args = @_; my $host = $args{host}; defined $host or throw EBox::Exceptions::MissingArgument('host'); my $proto = lc $args{protocol}; if (($proto ne 'tcp') and ($proto ne 'udp')) { throw EBox::Exceptions::InvalidData( data => __('protocol'), value => $proto, advice => __(q{Only 'tcp' and 'udp' are supported})); } defined $proto or $proto = 'tcp'; my $port = $args{port}; defined $port or throw EBox::Exceptions::MissingArgument('port'); my $interface = $args{interface}; my $privileged = $args{privileged}; my @nmapArgs; if ($proto eq 'udp') { if (not $privileged) { throw EBox::Exceptions::Internal('UDP scan needs priviliged mode'); } push @nmapArgs, '-sU'; } else { push @nmapArgs, '-sT'; # connect scan } push @nmapArgs, "-p$port"; if ($interface) { push @nmapArgs, "-e$interface"; } if ($privileged) { push @nmapArgs, '--privileged'; } else { push @nmapArgs, '--unprivileged'; } push @nmapArgs, $host; my $np = _nmap(@nmapArgs); my @hosts = $np->all_hosts(); # using all_hosts instead of get_host # to allow use hostname as argument # instead of IP, however this only # works if we have one host if (not @hosts) { throw EBox::Exceptions::External( __('No hosts scanned, maybe you cannot resolve DNS names?') ); } if (@hosts > 1) { throw EBox::Exceptions::Internal('More than one host scanned'); } my $hostResult = shift @hosts; if ($hostResult->status() ne 'up') { return 'hostDown'; } if ($proto eq 'tcp') { return $hostResult->tcp_port_state($port); } elsif ($proto eq 'udp') { return $hostResult->udp_port_state($port); } } sub _nmap { my @nmapArgs = @_; my $cmd = qq{nmap -oX - @nmapArgs}; my $output; if ( grep { $_ eq '-sU' } @nmapArgs ) { $output = EBox::Sudo::root($cmd); } else { $output = EBox::Sudo::command($cmd); } my $nmapXml = join '', @{ $output }; my $np = new Nmap::Parser; $np->parse($nmapXml); return $np; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Util/GPG.pm0000664000000000000000000000250012017102272016130 0ustar # Copyright (C) 2011-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # package EBox::Util::GPG; use warnings; use EBox::Sudo; use Error qw(:try); use constant GPGV_PROGRAM => '/usr/bin/gpgv'; use constant KEYRING => '/usr/share/zentyal/keyring.gpg'; # Method: checkSignature # # Checks GPG signature for the given file. It uses # zentyal defined keyring containing Zentyal public keys # # Parameters: # file - path of the file to check # (method will search for a .sig file with the same name) sub checkSignature { my ($file) = @_; my $command = GPGV_PROGRAM . ' --homedir /dev/null --keyring ' . KEYRING . " $file.sig >/dev/null 2>&1"; return not system($command); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Util/Lock.pm0000664000000000000000000000253512017102272016413 0ustar # Copyright (C) 2009-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Util::Lock; use strict; use warnings; use EBox::Config; use Fcntl qw(:flock); sub lock { my ($modulename) = @_; my $file = EBox::Config::tmp . "/" . $modulename . ".lock"; open(LOCKFILE, ">$file") or throw EBox::Exceptions::Internal("Cannot open lockfile to lock: $file"); flock(LOCKFILE, LOCK_EX | LOCK_NB) or throw EBox::Exceptions::Lock($modulename); } sub unlock { my ($modulename) = @_; my $file = EBox::Config::tmp . "/" . $modulename . ".lock"; open(LOCKFILE, ">$file") or throw EBox::Exceptions::Internal("Cannot open lockfile to unlock: $file"); flock(LOCKFILE, LOCK_UN); close(LOCKFILE); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Apache/0000775000000000000000000000000012017102272015424 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Apache/Model/0000775000000000000000000000000012017102272016464 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Apache/Model/AdminPort.pm0000664000000000000000000000372012017102272020721 0ustar # Copyright (C) 2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Apache::Model::AdminPort # # This model is used to configure the interface port # use strict; use warnings; package EBox::Apache::Model::AdminPort; use base 'EBox::Model::DataForm'; use Error qw(:try); use EBox::Gettext; use EBox::Types::Port; use constant APACHE_PORT => 443; sub new { my $class = shift; my $self = $class->SUPER::new(@_); bless ($self, $class); return $self; } sub _table { my ($self) = @_; my @tableHead = (new EBox::Types::Port(fieldName => 'port', editable => 1, defaultValue => APACHE_PORT)); my $dataTable = { 'tableName' => 'AdminPort', 'printableTableName' => __('Administration interface TCP port'), 'modelDomain' => 'Apache', 'defaultActions' => [ 'editField' ], 'tableDescription' => \@tableHead, }; return $dataTable; } sub validateTypedRow { my ($self, $action, $changedValues, $allValues) = @_; my $port = $changedValues->{port}->value(); $self->parentModule()->checkAdminPort($port); } sub updatedRowNotify { my ($self, $row, $oldRow, $force) = @_; my $port = $row->valueByName('port'); $self->parentModule()->updateAdminPortService($port); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Apache/Model/Language.pm0000664000000000000000000000563312017102272020554 0ustar # Copyright (C) 2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Apache::Model::Language # # This model is used to configure the interface languaje # package EBox::Apache::Model::Language; use strict; use warnings; use Error qw(:try); use EBox::Gettext; use EBox::Types::Select; use base 'EBox::Model::DataForm'; sub new { my $class = shift; my $self = $class->SUPER::new(@_); bless ($self, $class); return $self; } # Method: validateTypedRow # # Override method # sub validateTypedRow { my ($self, $action, $oldParams, $newParams) = @_; my $langs = EBox::Gettext::langs(); my $lang = $newParams->{'language'}->value(); my $showPkgWarn = not EBox::Config::configkey('custom_prefix'); my $pkgInstalled = 1; my $package = ''; if ($showPkgWarn) { my ($pkglang) = split (/_/, $lang); if (($pkglang eq 'pt') or ($pkglang eq 'zh')) { ($pkglang) = split (/\./, $lang); $pkglang =~ tr/_/-/; $pkglang =~ tr/[A-Z]/[a-z]/; $pkglang = 'pt' if ($pkglang eq 'pt-pt'); } $package = "language-pack-zentyal-$pkglang"; $pkgInstalled = $lang eq 'C' ? 1 : EBox::GlobalImpl::_packageInstalled($package); } if ($showPkgWarn and not $pkgInstalled) { throw EBox::Exceptions::External( __x('The language pack for {l} is missing, you can install it by running the following command: {c}', l => $lang, c => "sudo apt-get install $package")); } } sub _table { my ($self) = @_; my @tableHead = (new EBox::Types::Select(fieldName => 'language', populate => \&_populateLanguages, editable => 1)); my $dataTable = { 'tableName' => 'Language', 'printableTableName' => __('Language selection'), 'modelDomain' => 'Apache', 'defaultActions' => [ 'editField' ], 'tableDescription' => \@tableHead, }; return $dataTable; } sub _populateLanguages { my $langs = EBox::Gettext::langs(); my $array = []; foreach my $l (sort keys %{$langs}) { push ($array, { value => $l, printableValue => $langs->{$l} }); } return $array; } 1; zentyal-core-2.3.21+quantal1/src/EBox/SysInfo/0000775000000000000000000000000012017102272015635 5ustar zentyal-core-2.3.21+quantal1/src/EBox/SysInfo/Composite/0000775000000000000000000000000012017102272017577 5ustar zentyal-core-2.3.21+quantal1/src/EBox/SysInfo/Composite/General.pm0000664000000000000000000000327112017102272021515 0ustar # Copyright (C) 2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::SysInfo::Composite::General; use base 'EBox::Model::Composite'; use strict; use warnings; use EBox::Gettext; # Group: Public methods # Constructor: new # # Constructor for the general events composite # # Returns: # # - a # general events composite # sub new { my ($class, @params) = @_; my $self = $class->SUPER::new(@params); return $self; } # Group: Protected methods # Method: _description # # Overrides: # # # sub _description { my $description = { layout => 'top-bottom', name => __PACKAGE__->nameFromClass, printableName => __('General configuration'), pageTitle => __('General configuration'), compositeDomain => 'SysInfo', help => __('On this page you can set different general system settings') }; return $description; } 1; zentyal-core-2.3.21+quantal1/src/EBox/SysInfo/Model/0000775000000000000000000000000012017102272016675 5ustar zentyal-core-2.3.21+quantal1/src/EBox/SysInfo/Model/DateTime.pm0000664000000000000000000001121612017102272020730 0ustar # Copyright (C) 2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::SysInfo::Model::DateTime # # This model is used to configure the system date time # package EBox::SysInfo::Model::DateTime; use strict; use warnings; use Error qw(:try); use EBox::Gettext; use EBox::Types::Date; use EBox::Types::Time; use EBox::Types::Action; use base 'EBox::Model::DataForm'; sub new { my $class = shift; my $self = $class->SUPER::new(@_); bless ($self, $class); return $self; } sub _table { my ($self) = @_; my @tableHead = (new EBox::Types::Date( fieldName => 'date', editable => $self->_enabledSub(), ), new EBox::Types::Time( fieldName => 'time', editable => $self->_enabledSub(), help => __('A change in the date or time will cause all Zentyal services to be restarted.'))); my $customActions = [ new EBox::Types::Action( name => 'changeDateTime', printableValue => __('Change'), model => $self, handler => \&_doChangeDateTime, enabled => $self->_enabledSub(), message => __('The date and time was changed successfully.'))]; my $dataTable = { 'tableName' => 'DateTime', 'printableTableName' => __('Date and time'), 'modelDomain' => 'SysInfo', 'defaultActions' => [], 'customActions' => $customActions, 'tableDescription' => \@tableHead, }; return $dataTable; } # Method: viewCustomizer # # Overrides to # show a message if changing the date and time is not allowed # sub viewCustomizer { my ($self) = @_; my $enabledSub = $self->_enabledSub(); unless ($enabledSub->()) { $self->setMessage(__('As the NTP synchronization with external servers is enabled, you cannot change the date or time.')); } my $custom = $self->SUPER::viewCustomizer(); return $custom; } # Method: row # # Override to build and return a # row dependening on the current date and time # sub row { my ($self) = @_; my $row = $self->_defaultRow(); my $date = `date '+%d/%m/%Y'`; my $time = `date '+%H:%M:%S'`; chomp $date; chomp $time; $row->elementByName('date')->setValue($date); $row->elementByName('time')->setValue($time); return $row; } # Method: _doChangeDateTime # # This is the custom action handler # sub _doChangeDateTime { my ($self, $action, $id, %params) = @_; my $day = $params{'date_day'}; my $month = $params{'date_month'}; my $year = $params{'date_year'}; my $hour = $params{'time_hour'}; my $minute = $params{'time_min'}; my $second = $params{'time_sec'}; # Date time $self->_setNewDate($day, $month, $year, $hour, $minute, $second); my $dateStr = "$year/$month/$day $hour:$minute:$second"; my $audit = EBox::Global->modInstance('audit'); $audit->logAction('System', 'General', 'changeDateTime', $dateStr); $self->setMessage($action->message(), 'note'); $self->{customActions} = {}; } # Method: _setNewDate # # Sets the system date and time # sub _setNewDate { my ($self, $day, $month, $year, $hour, $minute, $second) = @_; my $newdate = "$year-$month-$day $hour:$minute:$second"; my $command = "/bin/date --set \"$newdate\""; EBox::Sudo::root($command); $self->parentModule()->_restartAllServices(); } # Method: _enabled # # Returns 1 if changing the date and time is allowed, 0 otherwise # sub _enabledSub { my ($self) = @_; return sub { my $ntp = $self->global()->modInstance('ntp'); my $ntpsync = (defined ($ntp) and ($ntp->isEnabled()) and ($ntp->synchronized())); if ($ntpsync) { return 0; } else { return 1; } }; } 1; zentyal-core-2.3.21+quantal1/src/EBox/SysInfo/Model/HostName.pm0000664000000000000000000000764412017102272020764 0ustar # Copyright (C) 2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::SysInfo::Model::HostName # # This model is used to configure the host name and domain # package EBox::SysInfo::Model::HostName; use strict; use warnings; use Error qw(:try); use EBox::Gettext; use EBox::Types::DomainName; use EBox::Types::Host; use base 'EBox::Model::DataForm'; use constant RESOLV_FILE => '/etc/resolv.conf'; sub new { my $class = shift; my $self = $class->SUPER::new(@_); bless ($self, $class); return $self; } sub _table { my ($self) = @_; my @tableHead = (new EBox::Types::Host( fieldName => 'hostname', printableName => __('Hostname'), defaultValue => \&_getHostname, editable => 1), new EBox::Types::DomainName( fieldName => 'hostdomain', printableName => __('Domain'), defaultValue => \&_getHostdomain, editable => 1, help => __('You will need to restart all the services or reboot the system to apply the hostname change.'))); my $dataTable = { 'tableName' => 'HostName', 'printableTableName' => __('Hostname and Domain'), 'modelDomain' => 'SysInfo', 'defaultActions' => [ 'editField' ], 'tableDescription' => \@tableHead, 'confirmationDialog' => { submit => sub { my ($self, $params) = @_; my $new = $params->{hostname}; my $old = $self->value('hostname'); if ($new eq $old) { # only dialog if it is a hostname change return undef; } my $title = __('Change hostname'); my $msg = __x('Are you sure you want to change the hostname to {new}?. You may need to restart all the services or reboot the system to enforce the change', new => $new ); return { title => $title, message => $msg, } } } }; return $dataTable; } sub _getHostname { my $hostname = `hostname`; chomp ($hostname); return $hostname; } sub _getHostdomain { my $hostdomain = `hostname -d`; chomp ($hostdomain); unless ($hostdomain) { my ($searchDomain) = @{_readResolv()}; $hostdomain = defined ($searchDomain) ? $searchDomain : 'zentyal.lan'; } return $hostdomain; } sub _readResolv { my $resolvFH; unless (open ($resolvFH, RESOLV_FILE)) { EBox::warn ("Couldn't open " . RESOLV_FILE); return []; } my $searchdomain = undef; my @dns = (); for my $line (<$resolvFH>) { $line =~ s/^\s+//g; my @toks = split (/\s+/, $line); if ($toks[0] eq 'nameserver') { push (@dns, $toks[1]); } elsif ($toks[0] eq 'search') { $searchdomain = $toks[1]; } } close ($resolvFH); return [$searchdomain, @dns]; } 1; zentyal-core-2.3.21+quantal1/src/EBox/SysInfo/Model/Halt.pm0000664000000000000000000000501712017102272020126 0ustar # Copyright (C) 2011-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::SysInfo::Model::Halt; use strict; use warnings; use base 'EBox::Model::DataForm'; use EBox::Global; use EBox::Gettext; use EBox::Types::Action; sub new { my $class = shift; my $self = $class->SUPER::new(@_); bless ( $self, $class ); return $self; } sub _table { my ($self) = @_; my $customActions = [ new EBox::Types::Action( name => 'halt', printableValue => __('Halt'), model => $self, handler => \&_doHalt, message => __('Zentyal is going down for halt'), ), new EBox::Types::Action( name => 'reboot', printableValue => __('Reboot'), model => $self, handler => \&_doReboot, message => __("Zentyal is going down for reboot"), ), ]; my $form = { tableName => 'Halt', modelDomain => 'SysInfo', pageTitle => __('Halt or Reboot'), defaultActions => [], customActions => $customActions, tableDescription => [], message => __('You might lose your Internet connection if this machine is halted.'), messageClass => 'warning', }; return $form; } # Method: popMessage # # Get the message to show. Overrided to not delete the current message, # messages of this model are permanent (till reboot) by default. # # Overrides: # # EBox::SysInfo::Model::DataTable::popMessage sub popMessage { my ($self) = @_; return $self->message(); } sub _doHalt { my ($self, $action, %params) = @_; EBox::Sudo::root('/sbin/halt'); $self->setMessage($action->message(), 'note'); $self->{customActions} = {}; } sub _doReboot { my ($self, $action, %params) = @_; EBox::Sudo::root("/sbin/reboot"); $self->setMessage($action->message(), 'note'); $self->{customActions} = {}; } 1; zentyal-core-2.3.21+quantal1/src/EBox/SysInfo/Model/TimeZone.pm0000664000000000000000000000365412017102272020775 0ustar # Copyright (C) 2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::SysInfo::Model::TimeZone # # This model is used to configure the system time zone # package EBox::SysInfo::Model::TimeZone; use strict; use warnings; use Error qw(:try); use File::Slurp; use EBox::Gettext; use EBox::Types::TimeZone; use base 'EBox::Model::DataForm'; use constant TZ_FILE => '/etc/timezone'; sub new { my $class = shift; my $self = $class->SUPER::new(@_); bless ($self, $class); return $self; } sub _table { my ($self) = @_; my @tableHead = (new EBox::Types::TimeZone( fieldName => 'timezone', editable => 1, defaultValue => \&_getTimezone, help => __('You will probably have to restart some services after ' . 'changing the time zone.'))); my $dataTable = { 'tableName' => 'TimeZone', 'printableTableName' => __('Time zone'), 'modelDomain' => 'SysInfo', 'defaultActions' => [ 'editField' ], 'tableDescription' => \@tableHead, }; return $dataTable; } sub _getTimezone { my $tz = read_file(TZ_FILE); chomp $tz; return $tz; } 1; zentyal-core-2.3.21+quantal1/src/EBox/SysInfo/Model/Password.pm0000664000000000000000000000344012017102272021036 0ustar # Copyright (C) 2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::SysInfo::Model::Password # # This model is used to configure the administrator password # package EBox::SysInfo::Model::Password; use strict; use warnings; use Error qw(:try); use EBox::Gettext; use base 'EBox::Model::DataForm'; sub new { my $class = shift; my $self = $class->SUPER::new(@_); bless ($self, $class); return $self; } sub _table { my ($self) = @_; my @tableHead = (new EBox::Types::Text( fieldName => 'password', printableName => __('Administrator password'), editable => 1)); my $dataTable = { 'tableName' => 'Password', 'printableTableName' => __('Administrator password'), 'modelDomain' => 'SysInfo', 'defaultActions' => [ 'editField' ], 'tableDescription' => \@tableHead, 'help' => __('On this page you can set different general system settings'), }; return $dataTable; } # Method: formSubmitted # # Overrides: # # # sub formSubmitted { my ($self) = @_; } 1; zentyal-core-2.3.21+quantal1/src/EBox/SysInfo/Model/AdminUser.pm0000664000000000000000000001074712017102272021133 0ustar # Copyright (C) 2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::SysInfo::Model::AdminUser # # This model is used to configure the administrator user account # package EBox::SysInfo::Model::AdminUser; use strict; use warnings; use Error qw(:try); use EBox::Gettext; use EBox::Types::Password; use EBox::Types::Action; use base 'EBox::Model::DataForm'; sub new { my $class = shift; my $self = $class->SUPER::new(@_); bless ($self, $class); return $self; } sub _table { my ($self) = @_; my @tableHead = (new EBox::Types::Text( fieldName => 'username', printableName => __('User name'), editable => 1, size => 20, defaultValue => ''), new EBox::Types::Password( fieldName => 'password', printableName => __('Current password'), editable => 1, disableAutocomplete => 1, size => 16), new EBox::Types::Password( fieldName => 'newPassword', printableName => __('New password'), confirmPrintableName => __('Confirm Password'), editable => 1, disableAutocomplete => 1, confirm => 1, size => 16, minLength => 6, help => __('Your password must be at least 6 characters long.'))); my $customActions = [ new EBox::Types::Action( name => 'changePwd', printableValue => __('Change'), model => $self, handler => \&_doChangePassword, message => __('The password was changed successfully.'))]; my $dataTable = { 'tableName' => 'AdminUser', 'printableTableName' => __('Change administrator password'), 'modelDomain' => 'SysInfo', 'defaultActions' => [], 'customActions' => $customActions, 'tableDescription' => \@tableHead, 'disableAutocomplete' => 1, }; return $dataTable; } sub _doChangePassword { my ($self, $action, $id, %params) = @_; my $username = $params{'username'}; my $curpwd = $params{'password'}; my $newpwd1 = $params{'newPassword'}; my $newpwd2 = $params{'newPassword_confirm'}; unless (defined ($username)) { throw EBox::Exceptions::DataMissing(data => __('Username')); } unless (defined ($curpwd)) { throw EBox::Exceptions::DataMissing(data => __('Password')); } unless (defined ($newpwd1) and defined ($newpwd2)) { throw EBox::Exceptions::DataMissing(data => __('New password')); } unless ($newpwd1 eq $newpwd2) { throw EBox::Exceptions::External(__('New passwords do not match.')); } unless (length ($newpwd1) > 5) { throw EBox::Exceptions::External(__('The password must be at least 6 characters long')); } unless (EBox::Auth->checkValidUser($username, $curpwd)) { throw EBox::Exceptions::External(__('Incorrect current password.')); } EBox::Auth->setPassword($username, $newpwd1); my $audit = EBox::Global->modInstance('audit'); $audit->logAction('System', 'General', 'changePassword', $username); $self->setMessage($action->message(), 'note'); $self->{customActions} = {}; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Report/0000775000000000000000000000000012017102272015516 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Report/DiskUsageProvider.pm0000664000000000000000000000562512017102272021456 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Report::DiskUsageProvider; # class: EBox::Report::DiskUsageProvider # # Any module which wants add one or more section to the disk usage report must # subclass this module and override the _facilitiesForDiskUsage method. Some # modules with special needs may want to override the diskUsage method instead # of _facilitiesForDiskUsage use strict; use warnings; # Method: diskUsage # return the different facilities which takes up disk space and the amount used # in block size units # # Named parameters: # blockSize - size of the block units (mandatory) # fileSystem - if present, we will only scan the supplied filesystem # # Returns: # # A reference to a hash which ocntains the used filesystem as keys # and a hash with the disk usage by facility or pseudo-facilty. # The facilities are named with his printable name # # Bugs: # doesn't take account symbolic links # see _facilitiesForDiskUsage warnings sub diskUsage { my ($self, %params) = @_; my $blockSize = $params{blockSize}; defined $blockSize or throw EBox::Exceptions::MissingArgument('blockSize'); my ($fileSystemToScan) = $params{fileSystem}; my %facilities = %{ $self->_facilitiesForDiskUsage() }; my %moduleDiskUsage; while (my ($facility, $dirs_r) = each %facilities) { foreach my $dir (@{ $dirs_r }) { (-d $dir) or next; my $filesys = EBox::FileSystem::dirFileSystem($dir); if (defined $fileSystemToScan) { ($filesys eq $fileSystemToScan) or next; } $moduleDiskUsage{$filesys}->{$facility} += EBox::FileSystem::dirDiskUsage($dir, $blockSize); } } return \%moduleDiskUsage; } # Method: _facilitiesForDiskUsage # # This method will be overriden by almost subclasses to notify which # facilities and directories need to be included in the report # # Returns: # A hash reference with the printable name of the facilities as keys # and a reference to the list of directories included in each facility # # Warning: # if the directories overlap some files will be counted twice # any directory specified shouldn't have a directory in another filesystem or # we will have a bad filesystem usage count sub _facilitiesForDiskUsage { return {}; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Report/RAID.pm0000664000000000000000000003247312017102272016604 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Report::RAID; use strict; use warnings; use EBox; use EBox::Sudo; use File::Slurp qw(read_file); use Error qw(:try); use constant PROC_MDSTAT => '/proc/mdstat'; # see t/testdata fpr examples of mdstat files # Group: Public functions # Function: enabled # # Returns: # # whethet the RAID is enabled in the system # # Warning: # # it looks if /proc/mdstat exists # sub enabled { my ($self, $raidInfo) = @_; defined $raidInfo or $raidInfo = info(); my $nEntries = keys %{ $raidInfo }; if ($nEntries > 1) { return 1; } elsif ($nEntries == 1) { if (exists $raidInfo->{unusedDevices}) { return @{ $raidInfo->{unusedDevices} } > 0; } return 1; } else { return 0; } } # Function: info # # Retrieve the valuable info for RAID infrastructure if present. # # Unfortunately, the /sys/block/md?/md directory doesn't exist so we will parse # PROC_MDSTAT to get the require information # # Returns: # # hash reference with a key for each RAID array and a 'unusedDevices' entry # # - the unusedDevices contains a list with the unused RAID devices # - each RAID array entry contains a hash reference with the following fields: # # state - String the array state. Several values may appear # together separated by commas. # Possible values: 'active', 'degraded', 'recovering', # 'resyncing', 'failed'. # type - array type as found in mdstat (ej: raid1, raid2, ..) # activeDevicesNeeded - how many active RAID devices requires the array # to work properly # activeDevices - how many active RAID devices are now # blocks - size in blocks of the array # operation - whether the array is engaged in some important # management operation. Contains 'none' or # the name of the operation. # operationPercentage - percentage of the operation completed so far. # No present if there isn't any operation active # operationEstimatedTime - estimated time left for the operation's end. # No present if there isn't any operation active # operationSpeed - current operation speed, measured in data/time units. # No present if there isn't any operation active # raidDevices - reference to a hash with information of the devices # which comprise the array. # The RAID device numbers are used as keys and the # values are a reference to a hash which the following # fields: # device - device file of the RAID device # state - state of the device. # Values: 'up', 'failure', 'spare' # See also: # t/RAID.t to see some examples of the return value of this function # sub info { my @mdstat = @{ _mdstatContents() }; (@mdstat and (scalar @mdstat > 2)) or return undef; my %info; push @mdstat, 'endoffile:dummy'; # this dummy section is to force to process # the real last section my $currentSection; my @currentSectionData; foreach my $line (@mdstat) { chomp $line; $line =~ s/^\s*//; $line =~ s/\s*$//; next if $line =~ m/^\s*$/; my @parts = split '\s*:\s*', $line, 2; if (@parts == 2 and $parts[0] ne 'bitmap') { # begins a new section my @sectionInfo = @{ _processSection($currentSection, \@currentSectionData) }; while (@sectionInfo) { my ($key, $value) = splice @sectionInfo, 0, 2; $info{$key} = $value; } # reset section variables to new section values my ($sectionHeader, $sectionData) = @parts; $currentSection = $sectionHeader; @currentSectionData = ($sectionData); } else { push @currentSectionData, $line; } } foreach my $dev (keys %info) { if (not $dev =~ m{^/dev/}) { # not a device entry. Next next; } _calculateDevicesStatus($info{$dev}); _setArrayStatus($info{$dev}); } return \%info; } # Group: Private methods sub _mdstatContents { if (not -r PROC_MDSTAT) { return []; } my $contents_r = read_file(PROC_MDSTAT, array_ref => 1); return $contents_r; } # calculate devices status using other fields values sub _calculateDevicesStatus { my ($info_r) = @_; my @statusArray = exists $info_r->{statusArray} ? @{ delete $info_r->{statusArray} } : (); my $activeDevicesNeeded = $info_r->{activeDevicesNeeded}; my $raidDevices = $info_r->{raidDevices}; my $statusArrayPos = -1; # it has not to coincide with the nummbers bz it can # be holes! my @devNumbers = sort keys %{ $raidDevices }; foreach my $number (@devNumbers) { $statusArrayPos += 1; my $devAttrs = $raidDevices->{$number}; $devAttrs->{state} = '' unless (defined($devAttrs->{state})); if ($devAttrs->{state} eq 'failure') { if (not defined $statusArray[$statusArrayPos]) { $devAttrs = 'failure_spare'; } next; } elsif ($devAttrs->{state} eq 'spare') { next; } elsif (not defined $activeDevicesNeeded or (not @statusArray)) { $devAttrs->{state} = 'up'; # XXX need more test.. } else { my $status = $statusArray[$statusArrayPos]; if (not defined $status) { $devAttrs->{state} = 'spare'; } elsif ($status eq 'U') { $devAttrs->{state} = 'up'; } elsif ($status eq '_') { $devAttrs->{state} = 'failure'; } else { $devAttrs->{state} = 'spare'; } } } # if we lack activeDevices and activeDevicesNeeded calcualte form the number # of devices if (not (exists $info_r->{activeDevicesNeeded}) ) { $info_r->{activeDevicesNeeded} = @devNumbers; } if (not (exists $info_r->{activeDevices}) ) { $info_r->{activeDevices} = grep { $raidDevices->{$_}->{state} eq 'up' } @devNumbers; } } # set the array state from the remainder elements sub _setArrayStatus { my ($info_r) = @_; my $state = ''; if ( $info_r->{active} ) { $state .= 'active, '; if ( $info_r->{operation} ne 'none' ) { $state .= 'degraded, '; } if ( $info_r->{operation} eq 'recovery' ) { $state .= 'recovering'; } elsif ( $info_r->{operation} eq 'resync' ) { $state .= 'resyncing'; } elsif ( $info_r->{operation} eq 'reshape' ) { $state .= 'reshaping'; } elsif ( $info_r->{operation} eq 'rebuild' ) { $state .= 'rebuilding'; } if ( $info_r->{activeDevicesNeeded} > $info_r->{activeDevices} ) { unless ( $state =~ m/degraded/g ) { $state .= 'degraded'; } } } else { $state .= 'failed'; } $state =~ s/, $//g; $info_r->{state} = $state; } my %processBySection = ('Personalities' => \&_processPersonalitiesSection, 'unused devices' => \&_processUnusedDevicesSection, ); sub _processSection { my ($sectionName, $sectionLines_r) = @_; defined $sectionName or return []; my @sectionLines = @{ $sectionLines_r }; my @sectionInfo; # contains the pairs of keys and values which will be # returned my $sectionSub; $sectionSub = exists $processBySection{$sectionName} ? $processBySection{$sectionName} : \&_processDeviceSection; return $sectionSub->(@_); } sub _processPersonalitiesSection { my ($sectionName, $sectionLines_r) = @_; # for now we ignore this section return []; } sub _processUnusedDevicesSection { my ($sectionName, $sectionLines_r) = @_; my @sectionLines = @{ $sectionLines_r }; my $unusedDevicesLine = join ' ', @sectionLines; my @unusedDevices = split '\s', $unusedDevicesLine; if ($unusedDevices[0] eq '') { @unusedDevices = (); # none means none } return [ unusedDevices => \@unusedDevices ]; } sub _processDeviceSection { my ($device, $deviceLines_r) = @_; $device = '/dev/' . $device; my @lines = @{ $deviceLines_r }; my %deviceInfo; # hash with all the parsed information %deviceInfo = ( %deviceInfo, _processDeviceMainLine(shift @lines) ); %deviceInfo = (%deviceInfo, _processDeviceArrayLine(shift @lines)); foreach my $line (@lines) { my @newDeviceInfo; if ($line =~ m/^bitmap:/) { @newDeviceInfo = _processDeviceBitmapLine($line) } else { @newDeviceInfo = _processDeviceOperationLine($line); } %deviceInfo = ( %deviceInfo, @newDeviceInfo, ); } if (not $deviceInfo{operation}) { $deviceInfo{operation} = 'none'; } return [$device => \%deviceInfo]; } sub _processDeviceMainLine { my ($line) = @_; my %deviceInfo; my ($activeTag, $raidType, @raidDevicesTags) = split '\s', $line; $deviceInfo{active}= ($activeTag eq 'active') ? 1 : 0; $deviceInfo{type}= $raidType; my $raidDevices = _processRaidDevicesTags(@raidDevicesTags); $deviceInfo{raidDevices} = $raidDevices; return %deviceInfo; } sub _processDeviceArrayLine { my ($line) = @_; my %deviceInfo; if ($line =~ m/(\d+)\sblocks\s+/) { $deviceInfo{blocks}= $1; } if ($line =~ m/\s([^\s]+?)\s+chunk/) { $deviceInfo{chunkSize}= $1; } if ($line =~ m{\s\[(\d+)/(\d+)\]\s+\[(.*?)\]}) { # $4, $5 the number of active devices needed and the number of active $deviceInfo{activeDevicesNeeded}= $1; $deviceInfo{activeDevices}= $2; $deviceInfo{statusArray}= [ split //, $3 ]; } if ($line =~ m{\salgorithm\s+(.*?)\s}) { $deviceInfo{algorithm} = $1; } if (not keys %deviceInfo) { EBox::debug("not match for device array line regexes: $line"); } return %deviceInfo; } sub _processDeviceOperationLine { my ($line) = @_; $line = '' unless (defined($line)); my %deviceInfo; my $operationLineRe = qr{ (\w+)\s* # $1 operation name =\s*(\d+\.?\d*)%\s+ # $2 percentaje complete. may use one or two digits .*? # ignored blocks comleted field finish=(.*?)\s+ # $3 estimated relative finish time speed=(.*?) # $4 operation speed (\s|$) # end of parsing }x; my $operation = 'none'; if ($line =~ m/$operationLineRe/) { $operation = $1; $deviceInfo{'operationPercentage'}= $2; $deviceInfo{'operationEstimatedTime'}= $3; $deviceInfo{'operationSpeed'}= $4; } $deviceInfo{operation} = $operation; return %deviceInfo; } sub _processRaidDevicesTags { my (@tags) = @_; my %devices; my $devTagRe = qr{ ^(.*?) # $1 device filename \[(\d)\] # $2 device RAID number (\(\w\))? # $3 state mark (optional) }x; foreach my $tag (@tags) { my $raidDevice; my $device; my $stateMark; my $failure = 0; my $spare = 0; if ($tag =~ m/$devTagRe/) { $device = $1; $raidDevice =$2; $stateMark = $3; if (not $device =~ m{/}) { # if it is 'relative' device we may infer that it is in the /dev dir $device = '/dev/' . $device; } } else { EBox::error("Cannot extract device from tag $tag. Skipping tag"); next; } $devices{$raidDevice} = { device => $device, }; if ($stateMark) { if ($stateMark eq '(F)') { $devices{$raidDevice}->{state} = 'failure'; } elsif ($stateMark eq '(S)') { $devices{$raidDevice}->{state} = 'spare'; } } } return (raidDevices => \%devices); } sub _processDeviceBitmapLine { my ($line) = @_; my ($title, $data) = split '\s*:\s*', $line, 2; return (bitmap => $data); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Report/DiskUsage.pm0000664000000000000000000002346712017102272017747 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Report::DiskUsage; # use strict; use warnings; use EBox::Gettext; use EBox::CGI::Temp; use EBox::Backup; use EBox::FileSystem; use Chart::Pie; use GD; use Filesys::Df; use Perl6::Junction qw(all); use constant PERCENTAGE_TO_APPEAR => 0.1; # percentage of disk size must reach a # facilty to be worthwile of display # in the chart use constant PIE_RADIUS => 50; use constant MIN_GRAPH_HEIGHT => 400; use constant MIN_GRAPH_WIDTH => 650; # Function: charts # # make a disk usage chart formatted as a pie chart for each file system # # Returns: a hash ref with the file system as key and the url needed to embed # the chart in a web page as value sub charts { my %charts; my %usageData = %{ usage() }; while (my ($fsys, $usage) = each %usageData) { my $datasets = _chartDatasets($usage); $charts{$fsys} =_chart($datasets); } return \%charts; } # Function: chart # # make a disk usage chart formatted as a pie chart for the specified partition # # Parametes: # partition - path to the partition device file # # Returns: # the url needed to embed the chart in a web page as value sub chart { my ($partition) = @_; my $usage = usage( fileSystem => $partition); exists $usage->{$partition} or throw EBox::Exceptions::External( __x('No usage data for {d}. Are you sure is a valid disk?', d => $partition) ); my $datasets = _chartDatasets($usage->{$partition}); # this is to avoid errors with labels too large my $maxLabelSize = 30; my $labels = $datasets->[0]; foreach my $label (@{ $labels }) { if (length($label) > $maxLabelSize) { $label = substr($label, 0, ($maxLabelSize - 4)); $label .= ' ...'; } } return _chart($datasets); } sub _chart { my ($datasets) = @_; my $imageLocation = EBox::CGI::Temp::newImage(); my $labelFont = GD::Font->Large; my $legendFont = GD::Font->Small; my $textSpace = 2; my %colors = ( dataset0 => [18, 130, 76], ); my $chartParams = { transparent => 'true', grey_background => 'false', precision => 2, legend => 'bottom', colors => \%colors, label_font => $labelFont, legend_font => $legendFont, text_space => $textSpace, }; my $chart = new Chart::Pie( _calcGraphSize($datasets, $chartParams) ); $chart->set ( %{ $chartParams } ); foreach my $ds_r (@{ $datasets }) { $chart->add_dataset( @{ $ds_r }); } $chart->png($imageLocation->{file}); return $imageLocation->{url}; } # the calcualtion is derivated of the one found in Graph::Pie::_draw_data # this assumes that we use percents to show the values in the pie chart # and that the legend is at the bottom sub _calcGraphSize { my ($datasets, $params) = @_; my $graphWidth = 1; my $graphHeight = 1; $params->{'legend_space'} = 4; # extracted from Chart::Base::_init my $max_label_len = 1; my %labelsByUsage = @{ $datasets }; while (my($label, $value) = each %labelsByUsage) { my $text = sprintf("%s %4.2f%%", $label, $value ); my $length = length $text; $length += 6; # space took by percent if ($length > $max_label_len) { $max_label_len = $length; } } my $fWidth = $params->{label_font}->width; my $fHeight = $params->{label_font}->height; $max_label_len *= $fWidth; my $labeldistance = 2*($fWidth > $fHeight ? $fWidth : $fHeight); my $pieLabelsSize = 2*$max_label_len + $labeldistance; # graph width $graphWidth = $pieLabelsSize + PIE_RADIUS; $graphWidth += $params->{text_space} *2; if ($graphWidth < MIN_GRAPH_WIDTH) { $graphWidth = MIN_GRAPH_WIDTH; } # graph height $graphHeight= $pieLabelsSize + PIE_RADIUS; # calculate the height used by the legend and add it to the height # we take a row for datapoint to simplify to don't have to follow all the # calculations scattered in Chart code my $rows = values %labelsByUsage; my $legend_row_height = $params->{legend_font}->height + $params->{text_space}; $graphHeight += ($rows * $legend_row_height) + $params->{text_space} + (2 * $params->{'legend_space'}); if ($graphHeight < MIN_GRAPH_HEIGHT) { $graphHeight = MIN_GRAPH_HEIGHT; } return ($graphWidth, $graphHeight); } # Function: usage # # get a disk usage report by facility. The pseudo-facilities 'free' and # 'system' are also present, the first one to show the amount of free disk # space and the second one the amount of disk taken up to the files which # aren't included in any facility # # Parameters: # # fileSystem - if this parameter is supplied, it only scan the supplied # file system. Only disk and non-media filesystems are accepted. # # Returns: # reference to a hash with the filesystem as keys # and a hash with the disk usage in blocks by facility or pseudo-facility as # value. Block's size unit is 1MB # sub usage { my (%params) = @_; my $blockSize = 1048576; # 1 MB block size my $fileSystemToScan = $params{fileSystem}; my $fileSystems = EBox::FileSystem::partitionsFileSystems(); # check fileSystem argument if present if (defined $fileSystemToScan ) { if ($fileSystemToScan ne all keys %{ $fileSystems }) { throw EBox::Exceptions::External( __x('Invalid file system: {f}. Only regular and no removable media file systems are accepted', f => $fileSystemToScan ) ); } } # initialize partitions to zero usage my %usageByFilesys = map { $_ => { facilitiesUsage => 0, } } keys %{ $fileSystems }; # get usage infromation from modules my @modUsageParams = ( blockSize => $blockSize, ); if (defined $fileSystemToScan) { push @modUsageParams, ( fileSystems => $fileSystemToScan); } my $global = EBox::Global->getInstance(); foreach my $mod (@{ $global->modInstancesOfType('EBox::Report::DiskUsageProvider' )}) { my $modUsage = $mod->diskUsage( @modUsageParams ); while (my ($filesys, $usage) = each %{ $modUsage }) { while (my ($facility, $blocks) = each %{ $usage }) { $usageByFilesys{$filesys}->{facilitiesUsage} += $blocks; $usageByFilesys{$filesys}->{$facility} += $blocks; } } } # calculate system usage and free space for each file system foreach my $fileSys (keys %usageByFilesys) { exists $fileSystems->{$fileSys} or throw EBox::Exceptions::Internal("File system not found: $fileSys"); my $mountPoint = $fileSystems->{$fileSys}->{mountPoint}; my $df = df($mountPoint, $blockSize ); my $facilitiesUsage = delete $usageByFilesys{$fileSys}->{facilitiesUsage}; my $totalUsage = sprintf ("%.2f", $df->{used}); my $systemUsage = $totalUsage - $facilitiesUsage; if ($systemUsage < 0) { if ($systemUsage > -1000) { # small round error, approximate to zero $systemUsage = 0; } else { EBox::error( "Error calculating system usage. Result: $systemUsage. Set to zero for avoid error" ); $systemUsage = 0; } } my $freeSpace = sprintf ("%.2f", $df->{bfree}); $usageByFilesys{$fileSys}->{system} = $systemUsage; $usageByFilesys{$fileSys}->{free} = $freeSpace; } return \%usageByFilesys; } sub _chartDatasets { my ($usageByFacility_r) = @_; my %usageByFacility = %{ $usageByFacility_r }; my @labels; my @diskUsage; # we calculate the minimal size needed to appear in the chart my $totalSpace = 0; $totalSpace += $_ foreach values %usageByFacility; my $minSizeToAppear = ($totalSpace * PERCENTAGE_TO_APPEAR) / 100; my $freeSpace = delete $usageByFacility{free}; my $systemUsage = delete $usageByFacility{system}; # we put free space and system usage first bz we want they have always the # same colors # choose correct unit my $unit = 'MB'; if ($freeSpace > 1024 or $systemUsage > 1024) { $unit = 'GB'; } else { foreach my $size (values %usageByFacility) { if ($size > 1024) { $unit = 'GB'; last; } } } # we don't translate the strings: 'Free space' and 'System' to avoid # problems with special characters in some lenguages push @labels, 'Free space'; push @diskUsage, _sizeLabelWithUnit($freeSpace, $unit); push @labels, 'System'; push @diskUsage, _sizeLabelWithUnit($systemUsage, $unit); while (my ($facilityName, $facilityUsage) = each %usageByFacility ) { ($facilityUsage >= $minSizeToAppear) or next; push @labels, $facilityName; push @diskUsage, _sizeLabelWithUnit($facilityUsage, $unit); } return [ \@labels, \@diskUsage, ]; } sub _sizeLabelWithUnit { my ($size, $unit) = @_; if ($unit eq 'GB') { return sprintf ('%.2f GB', $size / 1024); } elsif ($unit eq 'MB') { return "$size MB"; } else { throw EBox::Exceptions::Internal("Unknown unit: $unit"); } } 1; zentyal-core-2.3.21+quantal1/src/EBox/Test.pm0000664000000000000000000000711012017102272015517 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Test; use base 'Exporter'; # # package: EBox::Test # # Contains specifically-ebox checks and helper. # # deprecated: # activateEBoxTestStubs fakeEBoxModule setConfig setConfigKeys # this was moved to EBox::TestStub use Test::More; use Test::Builder; use Error qw(:try); use Params::Validate; our @EXPORT_OK = ('checkModuleInstantiation', @deprecatedSubs); our %EXPORT_TAGS = ( all => \@EXPORT_OK ); my $Test = Test::Builder->new; # # Function: checkModuleInstantiation # # Checks we can use the module package correctly (like Test::More::use_ok) and that we can instantiate correctly using the methods from EBox::Global. # That counts as 1 test for the plan. # # # Parameters: # $moduleName - name of the module # $modulePackage - package of the module # # Usage example: # checkModuleInstantiation('dhcp', 'EBox::DHCP'); # sub checkModuleInstantiation { my ($moduleName, $modulePackage) = @_; validate_pos(@_, 1, 1); eval "use $modulePackage"; if ($@) { $Test->ok(0, "$modulePackage failed to load: $@"); return; } my $global = EBox::Global->getInstance(); defined $global or die "Cannot get a instance of the global module"; my $instance; my $modInstanceError = 0; try { $instance = $global->modInstance($moduleName); } otherwise { $modInstanceError = 1;; }; if ($modInstanceError or !defined $instance) { $Test->ok(0, "Cannot create an instance of the EBox's module $moduleName"); return; } my $refType = ref $instance; if ($refType eq $modulePackage) { $Test->ok(1, "$moduleName instantiated correctly"); } elsif (defined $refType) { $Test->ok(0, "The instance returned of $moduleName is not of type $modulePackage instead is a $refType"); } else { $Test->ok(0, "The instance returned of $moduleName is not a blessed reference"); } } sub checkModels { my ($mod, @modelsNames) = @_; my @failedModels; foreach my $name (@modelsNames) { try { $mod->model($name); } otherwise { push @failedModels, $name; }; } my $modName = $mod->name(); if (@failedModels) { $Test->ok(0, "Module $modName failed when loading the models: @failedModels"); } else { $Test->ok(1, "Module $modName loaded the models"); } } sub checkComposites { my ($mod, @compositesNames) = @_; my @failedComposites; foreach my $name (@compositesNames) { try { $mod->composite($name); } otherwise { push @failedComposites, $name; }; } my $modName = $mod->name(); if (@failedComposites) { $Test->ok(0, "Module $modName failed when loading the composites: @failedComposites"); } else { $Test->ok(1, "Module $modName loaded the composites"); } } 1; zentyal-core-2.3.21+quantal1/src/EBox/Auth.pm0000664000000000000000000002321412017102272015504 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Auth; use strict; use warnings; use base qw(EBox::ThirdParty::Apache2::AuthCookie); use EBox; use EBox::CGI::Run; use EBox::Config; use EBox::Gettext; use EBox::Global; use EBox::Exceptions::Internal; use EBox::Exceptions::Lock; use Apache2::Connection; use Apache2::Const qw(:common HTTP_FORBIDDEN HTTP_MOVED_TEMPORARILY); use Authen::Simple::PAM; use Digest::MD5; use Fcntl qw(:flock); # By now, the expiration time for session is hardcoded here use constant EXPIRE => 3600; #In seconds 1h # By now, the expiration time for a script session use constant MAX_SCRIPT_SESSION => 10; # In seconds # Remote access constants use constant CC_USER => '__remote_access__'; sub new { my $class = shift; my $self = {}; bless($self, $class); return $self; } # Parameters: # # - session id : if the id is undef, it truncates the session file # Exceptions: # - Internal # - When session file cannot be opened to write sub _savesession # (session_id) { my ($sid, $user) = @_; my $sessionPath = EBox::Config->sessionid(); my $sidFile; my $openMode = '>'; if ( -f $sessionPath ) { $openMode = '+<'; } unless ( open ( $sidFile, $openMode, $sessionPath )){ throw EBox::Exceptions::Internal( "Could not open to write ". EBox::Config->sessionid); } # Lock the file in exclusive mode flock($sidFile, LOCK_EX) or throw EBox::Exceptions::Lock('EBox::Auth'); # Truncate the file after locking truncate($sidFile, 0); my $time = time(); print $sidFile "$sid\t$time\t$user" if defined $sid; # Release the lock flock($sidFile, LOCK_UN); close($sidFile); } # Method: checkValidUser # # Check with PAM if the user/password provided is of a valid admin # # Parameters: # # username - string containing the user name # password - string containing the plain password # # Returns: # # boolean - true if it's correct, otherwise false # sub checkValidUser { my ($self, $username, $password) = @_; my $pam = new Authen::Simple::PAM(service => 'zentyal'); return $pam->authenticate($username, $password); } # Method: setPassword # # Changes the password of the given username # # Parameters: # # username - username to change the password # password - string containing the plain password # # Exceptions: # # - when password cannot be changed # - when password length is no # longer than 6 characters sub setPassword { my ($self, $username, $password) = @_; unless (length($password) > 5) { throw EBox::Exceptions::External('The password must be at least 6 characters long'); } open(my $pipe, "|/usr/bin/sudo /usr/sbin/chpasswd") or throw EBox::Exceptions::Internal("Could not change password: $!"); print $pipe "$username:$password\n"; close($pipe); } # Method: authen_cred # # Overriden method from . # sub authen_cred # (request, $user, password, fromCC) { my ($self, $r, $user, $passwd, $fromCC) = @_; # If there's a script session opened, give it priority to the # Web interface session if ( $self->_actionScriptSession() ){ EBox::warn('Failed login since a script session is opened'); $r->subprocess_env(LoginReason => 'Script active'); return; } my $ip = $r->connection->remote_ip(); my $audit = EBox::Global->modInstance('audit'); # Unless it is a CC session or password does if ( not (defined($fromCC) and $fromCC) ) { unless ($self->checkValidUser($user, $passwd)) { my $log = EBox->logger(); $log->warn("Failed login from: $ip"); $audit->logSessionEvent($user, $ip, 'fail'); return; } } $r->user($user); $audit->logSessionEvent($user, $ip, 'login'); my $rndStr; for my $i (1..64) { $rndStr .= rand (2**32); } my $md5 = Digest::MD5->new(); $md5->add($rndStr); my $sid = $md5->hexdigest(); _savesession($sid, $user); my $global = EBox::Global->getInstance(); $global->revokeAllModules(); return $sid; } # Method: authen_ses_key # # Overriden method from . # sub authen_ses_key # (request, session_key) { my ($self, $r, $session_key) = @_; my ($sid, $lastime, $user) = _currentSessionId(); my $expired = _timeExpired($lastime); if ( $self->_actionScriptSession() ) { $r->subprocess_env(LoginReason => 'Script active'); _savesession(undef); } elsif ( ($session_key eq $sid) and (!$expired) ) { my $audit = EBox::Global->modInstance('audit'); $audit->setUsername($user); _savesession($sid, $user); return $user; } elsif ($expired) { my $audit = EBox::Global->modInstance('audit'); my $ip = $r->connection->remote_ip(); $audit->logSessionEvent($user, $ip, 'expired'); $r->subprocess_env(LoginReason => "Expired"); _savesession(undef); } else { $r->subprocess_env(LoginReason => "Already"); } return; } # Method: loginCC # # Login from Control Center, which is different if the # passwordless option is activated # # Parameters: # # request - the HTTP request # # Return: # # the same response as gives back # sub loginCC { my ($self, $req) = @_; if ( $self->recognize_user($req) == OK ) { return $self->authenticate($req); } else { if ( EBox::Global->modExists('remoteservices') ) { my $remoteServMod = EBox::Global->modInstance('remoteservices'); if ( $remoteServMod->eBoxSubscribed() and $remoteServMod->model('AccessSettings')->passwordlessValue()) { # Do what login does my $sessionKey = $self->authen_cred($req, CC_USER, '', 1); $self->send_cookie($req, $sessionKey); $self->handle_cache($req); $req->headers_out()->set('Location' => '/'); return HTTP_MOVED_TEMPORARILY; } } return EBox::CGI::Run->run('/Login/Index', 'EBox'); } } # XXX not sure if this will be useful, if not remove sub alreadyLogged { my ($self) = @_; my ($sid, $lastime, $user) = _currentSessionId(); return 0 if !defined $sid; return 0 if _timeExpired($lastime); return 1; } # Method: logout # # Overriden method from . # sub logout { my ($self,$r) = @_; $self->SUPER::logout($r); my $audit = EBox::Global->modInstance('audit'); my $ip = $r->connection->remote_ip(); my $user = $r->user(); $audit->logSessionEvent($user, $ip, 'logout'); } # scalar mode: return the sessionid # list mode: return (sessionid, lastime) sub _currentSessionId { my $SID_F; # sid file handle my $sessionPath = EBox::Config->sessionid(); unless(-e $sessionPath) { unless (open ($SID_F, ">". $sessionPath)) { throw EBox::Exceptions::Internal("Could not create " . EBox::Config->sessionid); } close($SID_F); return; } unless (open ($SID_F, $sessionPath)) { throw EBox::Exceptions::Internal( "Could not open ". EBox::Config->sessionid); } # Lock in shared mode for reading flock($SID_F, LOCK_SH) or throw EBox::Exceptions::Lock('EBox::Auth'); $_ = <$SID_F>; my ($sid, $lastime, $user) = split /\t/ if defined $_; # Release the lock flock($SID_F, LOCK_UN); close($SID_F); if (wantarray()) { return ($sid, $lastime, $user); } else { return $sid; } } sub _timeExpired { my ($lastime) = @_; my $expires = $lastime + EXPIRE; my $expired = (time() > $expires); return $expired; } # Method: _actionScriptSession # # Check whether a script session is already opened or not # # Returns: # # Boolean - indicate if a script session is already opened # sub _actionScriptSession { my ($self) = @_; # The script session filehandle my $scriptSessionFile; unless ( -e EBox::Config->scriptSession() ){ return undef; } # Trying to open the script sid open( $scriptSessionFile, '<', EBox::Config->scriptSession() ) or throw EBox::Exceptions::Internal('Could not open ' . EBox::Config->scriptSession()); # Lock in shared mode flock($scriptSessionFile, LOCK_SH) or throw EBox::Exceptions::Lock($self); # The file structure is the following: # TIMESTAMP my ($timeStamp) = <$scriptSessionFile>; # Release the lock and close the file flock($scriptSessionFile, LOCK_UN); close($scriptSessionFile); # time() return the # of seconds since an epoch (1 Jan 1970 # typically) my $expireTime = $timeStamp + MAX_SCRIPT_SESSION; return ( $expireTime >= time() ); } 1; zentyal-core-2.3.21+quantal1/src/EBox/SysInfo.pm0000664000000000000000000003630612017102272016203 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::SysInfo; use strict; use warnings; use base qw(EBox::Module::Config EBox::Report::DiskUsageProvider); use HTML::Mason; use HTML::Entities; use Sys::Hostname; use Sys::CpuLoad; use Filesys::Df; use File::Slurp qw(read_file); use Filesys::Df; use List::Util qw(sum); use Error qw(:try); use EBox::Config; use EBox::Gettext; use EBox::Global; use EBox::Dashboard::Widget; use EBox::Dashboard::Section; use EBox::Dashboard::List; use EBox::Dashboard::Value; use EBox::Dashboard::HTML; use EBox::Menu::Item; use EBox::Menu::Folder; use EBox::Report::DiskUsage; use EBox::Report::RAID; use EBox::Util::Version; use EBox::Util::Software; use constant LATEST_VERSION => '/var/lib/zentyal/latestversion'; sub _create { my $class = shift; my $self = $class->SUPER::_create(name => 'sysinfo', printableName => __n('System Information'), @_); bless($self, $class); return $self; } # Method: menu # # Overriden method that returns the core menu entries: # # - Summary # - Save/Cancel # - Logout # - SysInfo/General # - SysInfo/Backup # - SysInfo/Halt # sub menu { my ($self, $root) = @_; $root->add(new EBox::Menu::Item('url' => 'Dashboard/Index', 'text' => __('Dashboard'), 'separator' => 'Core', 'order' => 10)); $root->add(new EBox::Menu::Item('url' => 'ServiceModule/StatusView', 'text' => __('Module Status'), 'separator' => 'Core', 'order' => 20)); my $system = new EBox::Menu::Folder('name' => 'SysInfo', 'text' => __('System'), 'order' => 30); $system->add(new EBox::Menu::Item('url' => 'SysInfo/Composite/General', 'text' => __('General'), 'order' => 10)); $system->add(new EBox::Menu::Item('url' => 'SysInfo/Backup', 'text' => __('Import/Export Configuration'), 'order' => 50)); $system->add(new EBox::Menu::Item('url' => 'SysInfo/View/Halt', 'text' => __('Halt/Reboot'), 'order' => 60)); $root->add($system); my $maint = new EBox::Menu::Folder('name' => 'Maintenance', 'text' => __('Maintenance'), 'separator' => 'Core', 'order' => 70); $maint->add(new EBox::Menu::Item('url' => 'Report/DiskUsage', 'order' => 40, 'text' => __('Disk Usage'))); $maint->add(new EBox::Menu::Item('url' => 'Report/RAID', 'order' => 50, 'text' => __('RAID'))); $root->add($maint); } # Method: _setConf # # Overrides: # # # sub _setConf { my ($self) = @_; # Time zone my $timezoneModel = $self->model('TimeZone'); my $tz = $timezoneModel->row()->elementByName('timezone'); my $tzStr = $tz->printableValue(); EBox::Sudo::root("echo $tzStr > /etc/timezone", "cp -f /usr/share/zoneinfo/$tzStr /etc/localtime"); # Host name my $hostNameModel = $self->model('HostName'); my $hostname = $hostNameModel->value('hostname'); if ($hostname) { my $cmd = EBox::Config::scripts() . "change-hostname $hostname"; my $domain = $hostNameModel->value('hostdomain'); if ($domain) { $cmd .= " $domain"; } EBox::Sudo::root($cmd); } } sub fqdn { my ($self) = @_; my $model = $self->model('HostName'); my $name = $model->hostnameValue(); my $domain = $model->hostdomainValue(); my $fqdn = $name . '.' . $domain; return $fqdn; } sub hostName { my ($self) = @_; my $model = $self->model('HostName'); my $name = $model->hostnameValue(); return $name; } sub hostDomain { my ($self) = @_; my $model = $self->model('HostName'); my $domain = $model->hostdomainValue(); return $domain; } # we override aroundRestoreconfig to restore also state data (for the widget) sub aroundRestoreConfig { my ($self, $dir, @extraOptions) = @_; $self->SUPER::aroundRestoreConfig($dir, @extraOptions); $self->_load_state_from_file($dir); } # # Method: widgets # # Overriden method that returns the widgets offered by this module # # Overrides: # # # sub widgets { my $widgets = { 'modules' => { 'title' => __("Module Status"), 'widget' => \&modulesWidget, 'order' => 6, 'default' => 1 }, 'general' => { 'title' => __("General Information"), 'widget' => \&generalWidget, 'order' => 1, 'default' => 1 }, 'processes' => { 'title' => __("Process List"), 'widget' => \&processesWidget }, }; unless (EBox::Config::boolean('disable_links_widget')) { $widgets->{'links'} = { 'title' => __('Resources'), 'widget' => \&linksWidget, 'order' => 2, 'default' => 1 }; } return $widgets; } sub modulesWidget { my ($self, $widget) = @_; my $section = new EBox::Dashboard::Section('status'); $widget->add($section); my $global = EBox::Global->getInstance(); my $typeClass = 'EBox::Module::Service'; my %moduleStatus; my $numModules = 0; for my $class (@{$global->modInstancesOfType($typeClass)}) { $class->addModuleStatus($section); $numModules++; } $widget->{size} = $numModules * 0.15; } sub generalWidget { my ($self, $widget) = @_; my $section = new EBox::Dashboard::Section('info'); $widget->add($section); my $time_command = "LC_TIME=" . EBox::locale() . " /bin/date"; my $time = `$time_command`; my $version = $self->version(); my $qaUpdates = 0; my $url = 'http://update.zentyal.org/updates'; if (EBox::Global->modExists('remoteservices')) { my $rs = EBox::Global->modInstance('remoteservices'); $qaUpdates = $rs->subscriptionLevel() > 0; } my $ignore = EBox::Config::boolean('widget_ignore_updates'); unless ($ignore) { my $lastVersion; open (my $fh, LATEST_VERSION); read ($fh, $lastVersion, 16); chomp($lastVersion); close ($fh); if (EBox::Util::Version::compare($lastVersion, $version) == 1) { unless ($qaUpdates) { my $available = __('available'); $version .= " ($lastVersion $available)"; } } } my $updatesStr = __('No updates'); my $updatesType = 'good'; if ($qaUpdates) { my $msg = $self->_secureMsg(); $updatesStr = qq{$updatesStr}; } else { my $onlyComp = 0; # [ updates, sec_updates] my $updates = EBox::Util::Software::upgradablePkgsNum(); if ( $updates->[1] > 0 ) { $updatesType = 'error'; $updatesStr = __x('{n} security updates', n => $updates->[1]); } elsif ( $updates->[0] > 0 ) { $updatesType = 'warning'; $updatesStr = __x('{n} system updates', n => $updates->[0]); my $pkgsToUpgrade = EBox::Util::Software::upgradablePkgs(); my $nonCompNum = grep { $_ !~ /^zentyal-/ } @{$pkgsToUpgrade}; if ( $nonCompNum == 0 ) { # Only components, then show components $updatesStr = __x('{n} component updates', n => $updates->[0]); $onlyComp = 1; } } my $href = $url; if (EBox::Global->modExists('software')) { if ( $onlyComp ) { $href = '/Software/EBox#update'; } else { $href = '/Software/Updates'; } } unless ($ignore) { my $msg = $self->_commercialMsg(); $updatesStr = qq{$updatesStr}; } } my $uptime_output=`uptime`; my ($uptime, $users, $la1, $la2, $la3) = $uptime_output =~ /.*up *(.*), (.*)users?, load average: (.*), (.*), (.*)/; $section->add(new EBox::Dashboard::Value(__('Time'), $time)); $section->add(new EBox::Dashboard::Value(__('Hostname'), hostname)); $section->add(new EBox::Dashboard::Value(__('Core version'), $version)); $section->add(new EBox::Dashboard::Value(__('Software'), $updatesStr, $updatesType)); $section->add(new EBox::Dashboard::Value( __("System load"), join(', ', Sys::CpuLoad::load))); $section->add(new EBox::Dashboard::Value(__("Uptime"), $uptime)); $section->add(new EBox::Dashboard::Value(__("Users"), $users)); } sub processesWidget { my ($self, $widget) = @_; my $section = new EBox::Dashboard::Section('foo'); $widget->add($section); my $titles = ['PID','Name']; my $ids = []; my @processes = `ps ax | grep -v PID| awk '{ print \$1, \$5 }'`; my $rows = {}; for my $p (@processes) { chomp($p); my ($pid, $name) = split(' ', $p); encode_entities($name); my $foopid = 'a' . $pid; push(@{$ids}, $foopid); $rows->{$foopid} = [$pid,$name]; } $section->add(new EBox::Dashboard::List(undef, $titles, $ids, $rows)); } sub linksWidget { my ($self, $widget) = @_; my $section = new EBox::Dashboard::Section('links'); $widget->add($section); # Write the links widget using mason my $html; my $interp = new HTML::Mason::Interp(comp_root => EBox::Config::templates(), out_method => sub { $html .= $_[0] }); my $component = $interp->make_component( comp_file => EBox::Config::templates() . 'links-widget.mas' ); $interp->exec($component, ()); $section->add(new EBox::Dashboard::HTML($html)); } sub addKnownWidget { my ($self, $wname) = @_; my $widgets = $self->st_get('known/widgets'); if (not $widgets) { $widgets = {}; } $widgets->{$wname} = 1; $self->st_set('known/widgets', $widgets); } sub isWidgetKnown { my ($self, $wname) = @_; my $hash = $self->st_get('known/widgets'); defined $hash or return 0; return exists $hash->{$wname}; } sub getDashboard { my ($self, $dashboard) = @_; return $self->st_get_list($dashboard); } sub setDashboard { my ($self, $dashboard, $widgets) = @_; $self->st_set($dashboard, $widgets); } sub toggleElement { my ($self, $element) = @_; my $hash = $self->st_get($element); $hash->{toggled} = not $hash->{toggled}; $self->st_set($element, $hash); } sub toggledElements { my ($self) = @_; my $toggled = $self->st_get('toggled'); if (not defined $toggled) { return [] } my @toggled = keys %{ $toggled }; return \@toggled; } sub _facilitiesForDiskUsage { my ($self, @params) = @_; return EBox::Backup->_facilitiesForDiskUsage(@params); } # TODO Check if the subs below are needed sub logReportInfo { my ($self) = @_; my @data; my $fileSysS = EBox::FileSystem::partitionsFileSystems(); foreach my $fileSys (keys %{$fileSysS}) { my $entry = {}; $entry->{'table'} = 'sysinfo_disk_usage'; $entry->{'values'} = {}; my $mount = $fileSysS->{$fileSys}->{mountPoint}; $entry->{'values'}->{'mountpoint'} = $mount; my $info = df($mount, 1); $entry->{'values'}->{'used'} = $info->{'used'}; $entry->{'values'}->{'free'} = $info->{'bavail'}; push(@data, $entry) } # Add the total disk usage column my $totalEntry = {}; $totalEntry->{'table'} = 'sysinfo_disk_usage'; $totalEntry->{'values'} = {}; $totalEntry->{'values'}->{'mountpoint'} = 'total'; $totalEntry->{'values'}->{'used'} = sum(map { $_->{'values'}->{'used'} ? $_->{'values'}->{'used'} : 0 } @data); $totalEntry->{'values'}->{'free'} = sum(map { $_->{'values'}->{'free'} ? $_->{'values'}->{'free'} : 0 } @data); unshift(@data, $totalEntry); return \@data; } sub consolidateReportInfoQueries { return [ { 'target_table' => 'sysinfo_disk_usage_report', 'query' => { 'select' => 'mountpoint, used, free', 'from' => 'sysinfo_disk_usage', 'key' => 'mountpoint' } } ]; } # Method: report # # Overrides: # sub report { my ($self, $beg, $end, $options) = @_; my $report = {}; $report->{'disk_usage'} = $self->runMonthlyQuery($beg, $end, { 'select' => 'mountpoint, used, free', 'from' => 'sysinfo_disk_usage_report', }, { 'key' => 'mountpoint' }); if ( keys %{$report->{'disk_usage'}} == 2 ) { # Only total + /, so we return only total delete($report->{'disk_usage'}->{'/'}); } return $report; } sub _restartAllServices { my ($self) = @_; my $global = EBox::Global->getInstance(); my $failed = ''; EBox::info('Restarting all modules'); foreach my $mod (@{$global->modInstancesOfType('EBox::Module::Service')}) { my $name = $mod->name(); next if ($name eq 'network') or ($name eq 'firewall'); try { $mod->restartService(); } catch EBox::Exceptions::Internal with { $failed .= "$name "; }; } if ($failed ne "") { throw EBox::Exceptions::Internal("The following modules " . "failed while being restarted, their state is " . "unknown: $failed"); } EBox::info('Restarting system logs'); try { EBox::Sudo::root('service rsyslog restart', 'service cron restart'); } catch EBox::Exceptions::Internal with { }; } # Return commercial message for QA updates sub _commercialMsg { return __s('Warning: The updates are community based and there is no guarantee that your server will work properly after applying them. In production environments you should use the Small Business or Enterprise Edition that include quality assured software updates.'); } sub _secureMsg { return __s('Your commercial server edition guarantees that these are quality assured software updates and will be automatically applied to your system.'); } 1; zentyal-core-2.3.21+quantal1/src/EBox/ProgressIndicator.pm0000664000000000000000000002352112017102272020245 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::ProgressIndicator; use strict; use warnings; use EBox::Gettext; use EBox::Apache; use EBox::Util::SHM; use EBox::Exceptions::InvalidData; use EBox::Exceptions::MissingArgument; use EBox::Exceptions::External; use POSIX ":sys_wait_h"; use Error qw(:try); my $KEY = 'progress_indicator'; sub create { my ($class, %params) = @_; # check parameter correctness.. exists $params{totalTicks} or throw EBox::Exceptions::MissingArgument('totalTicks named parameter not found'); ($params{totalTicks} > 0) or throw EBox::Exceptions::InvalidData( data => __('Total ticks'), value => $params{totalTicks}, advice => __('It must be a non-zero positive number'), ); exists $params{executable} or throw EBox::Exceptions::MissingArgument('executable named parameter not found'); my ($executableWoArguments) = split '\s', $params{executable}; (-x $executableWoArguments) or throw EBox::Exceptions::External(__x("Cannot execute {exe}", exe => $params{executable})); my $id = _unique_id(); $class->_cleanupFinished(); my $self = $class->retrieve($id); $self->_init($params{executable}, $params{totalTicks}); return $self; } sub retrieve { my ($class, $id) = @_; defined $id or throw EBox::Exceptions::MissingArgument('id'); my $self = { id => $id }; bless $self, $class; return $self; } sub notifyTick { my ($self, $nTicks) = @_; defined $nTicks or $nTicks = 1; if ($nTicks <= 0) { throw EBox::Exceptions::InvalidData( data => __('Number of ticks to notify'), value => $nTicks, advice => __('must be greater than zero'), ); } if (not $self->started()) { throw EBox::Exceptions::External( __('Executable has not been run') ); } my $ticks = $self->_get('ticks'); $ticks += $nTicks; $self->_set('ticks', $ticks); } sub ticks { my ($self) = @_; my $data = $self->_data(); my $ticks = $data->{ticks}; my $totalTicks = $data->{totalTicks}; if ($ticks >= $totalTicks) { # safeguard against bad counts and zombies process _collectChildrens(); return $totalTicks; } return $ticks; } sub setTotalTicks { my ($self, $nTTicks) = @_; $self->_set('totalTicks', $nTTicks); } sub totalTicks { my ($self) = @_; return $self->_get('totalTicks'); } # Method: percentage # # Return how many ticks have been performed of the total in # percentage means # # Returns: # # String - the percentage round to two decimals # sub percentage { my ($self) = @_; if ($self->finished()) { return 100; } my $totalTicks = $self->totalTicks(); # Workaround to avoid illegal division by zero if ($totalTicks == 0) { return 100; } my $per = $self->ticks() / $totalTicks; $per = sprintf("%.2f", $per); # round to two decimals $per *= 100; return $per; } sub setMessage { my ($self, $message) = @_; $self->_set('message', $message); } sub message { my ($self) = @_; return $self->_get('message'); } # Method: started # # Return whether the action to perform has started or not # # Returns: # # True - if it has started # False - otherwise # sub started { my ($self) = @_; return $self->_get('started'); } # Method: finished # # Return whether the action to perform has finished or not # # Returns: # # True - if it has finished # False - otherwise # sub finished { my ($self) = @_; my $finished = $self->_get('finished'); if ($finished) { _collectChildrens(); } return $finished; } # Method: setAsFinished # # Set the progress indicator as finished. # # Parameters: # # retValue - Int the returned value. Possible values # *(Optional)* Default value: 0 # # - 0 : mean the progress has finished correctly # - >0 : mean something wrong happened # - otherwise: undocumented # # errorMessage - String the error message # - if retValue == 0 this message is treated as a warning # - if retValue > 0 this message is treated as an error # *(Optional)* Default value: '' # sub setAsFinished { my ($self, $retValue, $errorMsg) = @_; defined $retValue or $retValue = 0; if (not $self->started()) { throw EBox::Exceptions::External(__('The executable has not run')); } $self->_set('finished', 1); $self->setRetValue($retValue); if (defined($errorMsg)) { $self->_set('errorMsg', $errorMsg); } } # Method: retValue # # Returned value if it makes sense and only if the state is # marked as finished # # Returns: # # Int - the returned value, if -1 is returned, the value must not # take into account # sub retValue { my ($self) = @_; return $self->_get('retValue'); } # Method: setRetValue # # Set returned value. # # Parameters: # # retValue - Int the returned value, if -1 is set, the value # must not take into account # # Exceptions: # # - thrown if the state is not # finished # sub setRetValue { my ($self, $retValue) = @_; unless ($self->finished()) { throw EBox::Exceptions::Internal('Cannot set a return value to ' . 'a not finished task'); } $self->_set('retValue', $retValue); } # Method: errorMsg # # Get the error message when the executable has finished. It only # makes sense when returns a # value greater than zero. # # Returns: # # String - the error message if any # sub errorMsg { my ($self) = @_; return $self->_get('errorMsg'); } sub stateAsHash { my ($self) = @_; my $data = $self->_data(); my $status; if (not $data->{started}) { $status = 'not running'; } elsif ($data->{finished}) { my $retValue = $data->{retValue}; if ($retValue == 0) { $status = 'done'; } elsif ($retValue != 0 ) { $status = 'error'; } } else { $status = 'running'; } $data->{state} = $status; return $data; } sub id { my ($self) = @_; return $self->{id}; } sub destroy { my ($self) = @_; if (exists $self->{childPid}) { for (0 .. 10) { my $kid = waitpid($self->{childPid}, WNOHANG); if ($kid != 0) { last; } sleep 1; } } $self->_delete(); $_[0] = undef; # to cancel the self value } sub runExecutable { my ($self) = @_; if ($self->started) { throw EBox::Exceptions::External( __('The executable has already been started') ); } elsif ($self->finished) { __('The executable has already finished'); } $self->_set('started', 1); $self->_fork(); } sub _fork { my ($self) = @_; my $pid = fork(); my $id = $self->{id}; unless (defined $pid) { throw EBox::Exceptions::Internal("Cannot fork()."); } if ($pid) { $self->{childPid} = $pid; return; # parent returns immediately } else { EBox::Apache::cleanupForExec(); my $executable = $self->_get('executable'); exec ("$executable --progress-id $id"); } } sub _set { my ($self, $key, $value) = @_; my $id = $self->{id}; EBox::Util::SHM::setValue("$KEY/$id", $key, $value); } sub _get { my ($self, $key) = @_; my $id = $self->{id}; return EBox::Util::SHM::value("$KEY/$id", $key); } sub _data { my ($self) = @_; my $id = $self->{id}; return EBox::Util::SHM::hash("$KEY/$id"); } sub _delete { my ($self, $key) = @_; my $id = $self->{id}; EBox::Util::SHM::deletekey("$KEY/$id"); } sub _init { my ($self, $executable, $totalTicks) = @_; my $id = $self->{id}; my $data = {}; $data->{executable} = $executable; $data->{totalTicks} = $totalTicks; $data->{ticks} = 0; $data->{message} = ''; $data->{started} = 0; $data->{finished} = 0; $data->{retValue} = -1; # retValue == -1, not finished EBox::Util::SHM::setHash("$KEY/$id", $data); } sub _currentIds { EBox::Util::SHM::subkeys($KEY); } # Method to clean up the rubbish regarding to the progress indicator # It must be called when a new progress indicator is created, because # a single ProgressIndicator should be alive on Apache sub _cleanupFinished { my ($class) = @_; foreach my $id (_currentIds()) { try { my $pI = $class->retrieve($id); if ($pI->finished()) { $pI->destroy(); } } catch EBox::Exceptions::Base with { # Ignore this strange case (Already cleaned up) ; }; } _collectChildrens(); } sub _collectChildrens { my $child; do { $child = waitpid(-1, WNOHANG); } while ($child > 0); } sub _unique_id { my %ids = map { $_ => 1 } _currentIds(); my $id; do { $id = int(rand(1000)); } while (exists $ids{$id}); return $id; } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/0000775000000000000000000000000012017102272014645 5ustar zentyal-core-2.3.21+quantal1/src/EBox/CGI/Run.pm0000775000000000000000000002141512017102272015755 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::Run; use strict; use warnings; use EBox; use EBox::Global; use EBox::Gettext; use EBox::CGI::Base; use EBox::Model::Manager; use EBox::CGI::Controller::Composite; use EBox::CGI::Controller::DataTable; use EBox::CGI::Controller::Modal; use EBox::CGI::View::DataTable; use EBox::CGI::View::Composite; use CGI; use File::Slurp; use Error qw(:try); use constant URL_ALIAS_FILTER => '/usr/share/zentyal/urls/*.urls'; my %urlAlias; # Method: classFromUrl # # Map from an URL to the name of the CGI class that needs to be run when # the URL is accessed # # It checks the *.urls files to check if the given URL is an alias # in order to get the real URL of the CGI # # Parameters: # # url - URL to map to a CGI classname # # Returns: # the name of the CGI class # sub classFromUrl { my ($url, $namespace) = @_; defined ($url) or exit; my $classname = ''; if ($namespace) { $classname = $namespace; } $classname .= '::CGI::'; $url = _urlAlias($url); $url =~ s/\?.*//g; $url =~ s/[\\"']//g; $url =~ s/\//::/g; $url =~ s/^:://; $classname .= $url; $classname =~ s/::::/::/g; $classname =~ s/::$//; if ($classname =~ /::CGI$/) { $classname .= '::Dashboard::Index'; } return $classname; } # Method: run # # Run the given URL and prints out the returned HTML. This is the eBox # Web UI core indeed. # # Parameters: # # url - String the URL to get the CGI from, it will transform # slashes to double colons # # namespace - String the namespace to prefix the CGI to extract the # class name, as explained in # sub run # (url, namespace) { my ($self, $url, $namespace) = @_; my $redis = EBox::Global->modInstance('global')->redis(); $redis->begin(); try { my $classname = classFromUrl($url, $namespace); my $cgi; eval "use $classname"; if ($@) { try{ $cgi = _lookupViewController($classname, $namespace); } catch EBox::Exceptions::DataNotFound with { # path not valid $cgi = undef; }; if (not $cgi) { my $log = EBox::logger; $log->error("Unable to import cgi: " . "$classname Eval error: $@"); my $error_cgi = 'EBox::CGI::SysInfo::PageNotFound'; eval "use $error_cgi"; $cgi = new $error_cgi('namespace' => $namespace); } } else { $cgi = new $classname(); } $cgi->run(); $redis->commit(); } finally { $redis->rollback(); }; } # Helper functions sub _urlAlias { my ($url) = @_; unless (keys %urlAlias) { _readUrlAliases(); } if (exists $urlAlias{$url}) { return $urlAlias{$url}; } else { return $url; } } sub _readUrlAliases { foreach my $file (glob (URL_ALIAS_FILTER)) { my @lines = read_file($file); foreach my $line (@lines) { my ($alias, $url) = split (/\s/, $line); $urlAlias{$alias} = $url; } } } sub _posAfterCGI { my ($namespaces) = @_; my $i = 0; for my $namespace (@{$namespaces}) { if ($namespace eq 'CGI') { last; } $i++; } return $i+1; } # Method: lookupModel # # Map from a CGI class name to the appropriate model # # Parameters: # # classname - CGI class name to map to a model and an action # # Returns: # the model and action appropriate for the classname # # Exceptions: # - thrown if the CGI doesn't use models # sub lookupModel { my ($classname) = @_; my @namespaces = split ('::', $classname); my $pos = _posAfterCGI(\@namespaces); my $manager = EBox::Model::Manager->instance(); my ($namespace, $modelName) = ($namespaces[$pos+1], $namespaces[$pos+2]); my ($model, $action) = (undef, undef); if ( ($namespace eq 'View') or ($namespace eq 'Controller') or ($namespace eq 'ModalController')) { if ( defined ( $namespaces[$pos+3] ) ) { # Set as model name, the context name $modelName = '/' . lc ( $namespaces[$pos] ) . '/' . $modelName . '/' . $namespaces[$pos+3]; } else { $modelName = '/' . lc ( $namespaces[$pos] ) . "/$modelName"; } try { $model = $manager->model($modelName); if ( @namespaces >= $pos+4 ) { $action = splice ( @namespaces, $pos+4, 1 ); } } catch EBox::Exceptions::DataNotFound with { $action = $namespaces[$pos+3]; # Remove the previous thought index $modelName =~ s:/.*?$::g; if (($modelName) ne '') { $model = $manager->model($modelName); } else { throw EBox::Exceptions::DataNotFound(q{model's name}); } }; } elsif ( $namespace eq 'Composite' ) { if ( defined ( $namespaces[$pos+3] )) { # It may be the index or the action # Compose the composite context name my $contextName = '/' . lc ( $namespaces[$pos] ) . '/' . $modelName . '/' . $namespaces[$pos+3]; try { $model = $manager->composite($contextName); $action = $namespaces[$pos+4]; } catch EBox::Exceptions::DataNotFound with { $action = $namespaces[$pos+3]; }; } unless ( defined ( $model)) { my $contextName = '/' . lc ( $namespaces[$pos] ) . "/$modelName"; $model = $manager->composite($contextName); } } return ($model, $action); } # Method:: _lookupViewController # # Check if a classname must be mapped to a View or Controller # cgi class from a model or a composite # sub _lookupViewController { my ($classname, $cginamespace) = @_; # URL to map: # url => 'EBox::CGI::::' menuNamespaceBranch # menuNamespaceBranch => 'View' model | 'Controller' model index | 'Composite' model index action # model => '::' # index => '::' | epsilon # action => '::' | epsilon my ($cgi, $menuNamespace) = (undef, undef); my ($model, $action) = lookupModel($classname); if($model) { my @namespaces = split ( '::', $classname); my $pos = _posAfterCGI(\@namespaces); my ($namespace, $modelName) = ($namespaces[$pos+1], $namespaces[$pos+2]); $menuNamespace = $model->menuNamespace(); if ( $namespace eq 'View' ) { $cgi = EBox::CGI::View::DataTable->new( 'tableModel' => $model, 'namespace' => $cginamespace); } elsif ( $namespace eq 'Controller' ) { $cgi = EBox::CGI::Controller::DataTable->new( 'tableModel' => $model, 'namespace' => $cginamespace); } elsif ( $namespace eq 'ModalController' ) { $cgi = EBox::CGI::Controller::Modal->new( 'tableModel' => $model, 'namespace' => $cginamespace); } elsif ( $namespace eq 'Composite' ) { # Check if the action is defined URL: Composite// if ( defined ( $action )) { $cgi = new EBox::CGI::Controller::Composite( composite => $model, action => $action, 'namespace' => $cginamespace ); } else { $cgi = new EBox::CGI::View::Composite( composite => $model, 'namespace' => $cginamespace ); } } if (defined($cgi) and defined($menuNamespace)) { $cgi->setMenuNamespace($menuNamespace); } } return $cgi; } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/Temp.pm0000664000000000000000000000400712017102272016111 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::Temp; # Package: EBox::CGI::Temp # # this packages contain methods to manage temporal www data, like generated images use strict; use warnings; use EBox::Config; use File::Temp; use File::Basename; sub urlImagesDir { return '/dynamic-data/images/'; } # Function: newImage # # create a empty temporal file in the images directory. The file is empty and # the user must overwrite it with a image file. # Before creating the files the # clean() function is called to cleanup old files # # Returns: # a hash which the following keys # file - fiel path to the temporal file # url - URL used to address the file from a web page # sub newImage { cleanImages(); my ($fh, $file) = File::Temp::tempfile(DIR => EBox::Config::dynamicimages()); close $fh; my $url = urlImagesDir() . basename $file; return { file => $file, url => $url, }; } # Function: cleanImages # # remove older than 300 second images files sub cleanImages { my $currentTime = time(); my $livingInterval = 300; my $dir = EBox::Config::dynamicimages(); my $DH; opendir $DH, $dir; while (my $f = readdir $DH) { my $path = "$dir/$f"; my @stat = stat $path; my $mtime =$stat[9]; if (($currentTime - $mtime) > $livingInterval) { unlink $path; } } closedir $DH; } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/WizardPage.pm0000664000000000000000000001074212017102272017244 0ustar # Copyright (C) 2010-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Wizard pages are used by modules to help user on initial configuration # If a module implements some wizard it will be shown by zentyal-software to # the user # # A wizard page CGI has 2 types of calls differentiated by HTTP request method: # # - GET - The page will show a form that the user must fill # - POST - That form will sent to this CGI for processing. # # If form processing fails POST request must response with an error code and # print an error messages that user will see # # If status is OK the wizard will step into next wizard page. # package EBox::CGI::WizardPage; use strict; use warnings; use base 'EBox::CGI::Base'; use EBox::Gettext; use EBox::Html; use HTML::Mason::Exceptions; use Apache2::RequestUtil; use Error qw(:try); use HTML::Mason::Exceptions; use EBox::Exceptions::DataInUse; use EBox::Exceptions::Base; use constant ERROR_STATUS => '500'; ## arguments ## title [optional] ## error [optional] ## msg [optional] ## cgi [optional] ## template [optional] sub new # (title=?, error=?, msg=?, cgi=?, template=?) { my $class = shift; my %opts = @_; my $self = $class->SUPER::new(@_); my $namespace = delete $opts{'namespace'}; my $tmp = $class; $tmp =~ s/^(.*?)::CGI::(.*?)(?:::)?(.*)//; if(not $namespace) { $namespace = $1; } $self->{namespace} = $namespace; $self->{module} = $2; $self->{cginame} = $3; if (defined($self->{cginame})) { $self->{url} = $self->{module} . "/" . $self->{cginame}; } else { $self->{url} = $self->{module} . "/Index"; } bless($self, $class); return $self; } # Method: _processWizard # # Processes form submission and configures module # sub _processWizard { # Override this to process wizard page } # Method: _masonParameters # # Configures parameteres for mason template # # Returns # array ref to mason parameters sub _masonParameters { # Override this to set mason template params } sub _print { my ($self) = @_; $self->_header(); if ( $self->{cgi}->request_method() eq 'GET' ) { $self->_body(); } } sub _process { my $self = shift; $self->{params} = $self->_masonParameters(); if ( $self->{cgi}->request_method() eq 'POST' ) { $self->_processWizard(); } } sub _print_error { my ($self, $text) = @_; $text or return; ($text ne "") or return; # We send a ERROR_STATUS code. This is necessary in order to trigger # onFailure functions on Ajax code my $r = Apache2::RequestUtil->request(); $r->status(ERROR_STATUS); $r->subprocess_env('suppress-error-charset' => 1) ; $r->custom_response(ERROR_STATUS, $text); } sub run { my $self = shift; if (not $self->_loggedIn) { $self->{redirect} = "/Login/Index"; } else { try { $self->_validateReferer(); $self->_process(); $self->_print; } otherwise { my $ex = shift; my $logger = EBox::logger; if (isa_mason_exception($ex)) { $logger->error($ex->as_text); my $error = __("An internal error related to ". "a template has occurred. This is ". "a bug, relevant information can ". "be found in the logs."); $self->_print_error($error); } else { if ($ex->can('text')) { $logger->error('Exception: ' . $ex->text()); $self->_print_error($ex->text()); } else { $logger->error('Unknown exception'); $self->_print_error('Unknown exception'); } } }; } } sub _title { } sub _header { my $self = shift; print($self->cgi()->header(-charset=>'utf-8')); } sub _footer { } sub _menu { } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/Dashboard/0000775000000000000000000000000012017102272016534 5ustar zentyal-core-2.3.21+quantal1/src/EBox/CGI/Dashboard/Update.pm0000664000000000000000000000274312017102272020322 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::Dashboard::Update; use strict; use warnings; use base 'EBox::CGI::ClientRawBase'; use EBox; use EBox::Gettext; use EBox::Global; sub new { my $class = shift; my $self = $class->SUPER::new(@_); bless($self, $class); return $self; } # Method: requiredParameters # # Overrides: # # # sub requiredParameters { return ['dashboard', 'widgets']; } # Method: actuate # # Overrides: # # # sub actuate { my ($self) = @_; my $global = EBox::Global->getInstance(); my $sysinfo = $global->modInstance('sysinfo'); $self->_requireParam("dashboard"); my $dashboard = $self->param("dashboard"); my @widgets = split(',', $self->unsafeParam("widgets")); $sysinfo->setDashboard($dashboard,\@widgets); } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/Dashboard/ConfigureWidgets.pm0000664000000000000000000000543312017102272022347 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::Dashboard::ConfigureWidgets; use strict; use warnings; use base 'EBox::CGI::ClientRawBase'; use EBox::Gettext; use EBox::Global; use Error qw(:try); sub new # (error=?, msg=?, cgi=?) { my $class = shift; my $self = $class->SUPER::new(@_, title => __('Configure Widgets'), 'template' => '/dashboard/configurewidgets.mas'); bless($self, $class); return $self; } my $widgetsToHide = undef; # Method: masonParameters # # Overrides: # # # sub masonParameters { my ($self) = @_; my $global = EBox::Global->getInstance(1); my @modNames = @{$global->modNames()}; my $modules = []; my $present_widgets = {}; my $sysinfo = $global->modInstance('sysinfo'); for my $wname (@{$sysinfo->getDashboard('dashboard1')}) { $present_widgets->{$wname} = 1; } for my $wname (@{$sysinfo->getDashboard('dashboard2')}) { $present_widgets->{$wname} = 1; } unless (defined $widgetsToHide) { $widgetsToHide = { map { $_ => 1 } split (/,/, EBox::Config::configkey('widgets_to_hide')) }; } foreach my $name (@modNames) { my $mod = $global->modInstance($name); my $widgets = $mod->widgets(); if (%{$widgets}) { my $modtitle = $mod->{'printableName'}; if (not defined($modtitle)) { $modtitle = $mod->{'title'}; } my $module = { 'title' => $modtitle, 'name' => $mod->{'name'}, 'widgets' => [] }; for my $k (sort keys %{$widgets}) { my $fullname = "$name:$k"; next if exists $widgetsToHide->{$fullname}; my $wid = {'name' => $k, 'title' => $widgets->{$k}->{'title'}}; $wid->{'present'} = $present_widgets->{$fullname}; push(@{$module->{'widgets'}}, $wid); } push(@{$modules}, $module) if (@{$module->{'widgets'}}); } } my @params = (); push(@params, 'modules' => $modules); return \@params; } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/Dashboard/Widget.pm0000664000000000000000000000327012017102272020317 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::Dashboard::Widget; use strict; use warnings; use base 'EBox::CGI::ClientRawBase'; use EBox::Gettext; use EBox::Global; use EBox::Dashboard::Widget; use EBox::Dashboard::Item; use Error qw(:try); sub new # (error=?, msg=?, cgi=?) { my $class = shift; my $self = $class->SUPER::new(@_, 'template' => '/dashboard/widgetcontent.mas'); bless($self, $class); return $self; } # Method: requiredParameters # # Overrides: # # # sub requiredParameters { return ['module', 'widget']; } # Method: masonParameters # # Overrides: # # # sub masonParameters { my ($self) = @_; my $global = EBox::Global->getInstance(1); my $modname = $self->param('module'); my $widgetname = $self->param('widget'); my $module = $global->modInstance($modname); my $widget = $module->widget($widgetname); my @params = (); push(@params, 'widget' => $widget); return \@params; } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/Dashboard/Section.pm0000664000000000000000000000376512017102272020511 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::Dashboard::Section; use strict; use warnings; use base 'EBox::CGI::ClientRawBase'; use EBox::Gettext; use EBox::Global; use EBox::Dashboard::Widget; use EBox::Dashboard::Item; use Error qw(:try); sub new # (error=?, msg=?, cgi=?) { my $class = shift; my $self = $class->SUPER::new(@_, 'template' => '/dashboard/sectioncontent.mas'); bless($self, $class); return $self; } # Method: requiredParameters # # Overrides: # # # sub requiredParameters { return ['module', 'widget', 'section']; } # Method: masonParameters # # Overrides: # # # sub masonParameters { my ($self) = @_; my $global = EBox::Global->getInstance(1); my $modname = $self->param('module'); my $widgetname = $self->param('widget'); my $sectionname = $self->unsafeParam('section'); my $module = $global->modInstance($modname); my $widget = $module->widget($widgetname); my $sect; foreach my $section (@{$widget->sections()}) { if ($section->{name} eq $sectionname) { $sect = $section; last; } } my @params = (); my $namespace = $modname . ":" . $widgetname; push(@params, 'section' => $sect); push(@params, 'namespace' => $namespace); return \@params; } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/Dashboard/Toggle.pm0000664000000000000000000000255312017102272020320 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::Dashboard::Toggle; use strict; use warnings; use base 'EBox::CGI::ClientRawBase'; use EBox; use EBox::Gettext; use EBox::Global; sub new { my $class = shift; my $self = $class->SUPER::new(@_); bless($self, $class); return $self; } # Method: requiredParameters # # Overrides: # # # sub requiredParameters { return [ 'element' ]; } # Method: actuate # # Overrides: # # # sub actuate { my ($self) = @_; my $global = EBox::Global->getInstance(); my $sysinfo = $global->modInstance('sysinfo'); my $element = $self->unsafeParam("element"); $sysinfo->toggleElement($element); } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/Dashboard/WidgetJSON.pm0000664000000000000000000000357312017102272021017 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::Dashboard::WidgetJSON; use strict; use warnings; use base 'EBox::CGI::ClientRawBase'; use EBox::Gettext; use EBox::Global; use EBox::Dashboard::Widget; use EBox::Dashboard::Item; use Error qw(:try); use JSON -convert_blessed_universally; sub new # (error=?, msg=?, cgi=?) { my $class = shift; my $self = $class->SUPER::new(@_); bless($self, $class); return $self; } # Method: requiredParameters # # Overrides: # # # sub requiredParameters { return ['module', 'widget']; } # Method: actuate # # Overrides: # # # sub actuate { my ($self) = @_; my $global = EBox::Global->getInstance(1); my $modname = $self->param('module'); my $widgetname = $self->param('widget'); my $module = $global->modInstance($modname); $self->{widget} = $module->widget($widgetname); } # Method: _print # # Overrides: # # # sub _print { my ($self) = @_; print($self->cgi()->header(-charset=>'utf-8',-type=>'application/json')); local $JSON::ConvBlessed = 1; my $json = new JSON; my $js = $json->allow_blessed->convert_blessed->encode( $self->{widget} ); print $js; } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/Dashboard/Index.pm0000664000000000000000000001067712017102272020154 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::Dashboard::Index; use strict; use warnings; use base 'EBox::CGI::ClientBase'; use EBox::Gettext; use EBox::Global; use EBox::Dashboard::Widget; use EBox::Dashboard::Item; use POSIX qw(INT_MAX); use List::Util qw(sum); use Error qw(:try); # TODO: Currently we can't have more than two dashboards because of # the design of the interface, but this could be incremented in the future my $NUM_DASHBOARDS = 2; sub new { my $class = shift; my $self = $class->SUPER::new(@_, title => __('Dashboard'), 'template' => '/dashboard/index.mas'); bless($self, $class); return $self; } my $widgetsToHide = undef; # Method: masonParameters # # Overrides: # # # sub masonParameters { my ($self) = @_; # Delete first install file if it exists EBox::Global->deleteFirst(); unless (defined $widgetsToHide) { $widgetsToHide = { map { $_ => 1 } split (/,/, EBox::Config::configkey('widgets_to_hide')) }; } my $global = EBox::Global->getInstance(1); my $sysinfo = $global->modInstance('sysinfo'); my @modNames = @{$global->modNames()}; my $widgets = {}; foreach my $name (@modNames) { my $mod = $global->modInstance($name); my $wnames = $mod->widgets(); for my $wname (keys (%{$wnames})) { my $fullname = "$name:$wname"; next if exists $widgetsToHide->{$fullname}; $widgets->{$fullname} = $wnames->{$wname}; } } # put the widgets in the dashboards according to the last configuration my @dashboards; for my $i (1 .. $NUM_DASHBOARDS) { my @dashboard; for my $wname (@{$sysinfo->getDashboard("dashboard$i")}) { if (delete $widgets->{$wname}) { my ($module, $name) = split (/:/, $wname); my $mod = $global->modInstance($module); next unless defined ($mod); my $widget = $mod->widget($name); next unless defined ($widget); push (@dashboard, $widget); } } $dashboards[$i - 1] = \@dashboard; } my @orderedWidgets = sort { $widgets->{$a}->{order} <=> $widgets->{$b}->{order} } keys %{$widgets}; # put the remaining widgets in the dashboards trying to balance them foreach my $wname (@orderedWidgets) { next if $sysinfo->isWidgetKnown($wname); $sysinfo->addKnownWidget($wname); my $winfo = delete $widgets->{$wname}; next unless (defined ($winfo) and $winfo->{default}); my ($module, $name) = split (/:/, $wname); my $mod = EBox::Global->modInstance($module); next unless defined ($mod); my $widget = $mod->widget($name); next unless defined ($widget); # Find the dashboard with less items and add the widget to it my $minValue = INT_MAX; my $minIndex = 0; for my $i (1 .. $NUM_DASHBOARDS) { my $size_i = sum(map { $_->{size} } @{$dashboards[$i - 1]}); if ($size_i < $minValue) { $minValue = $size_i; $minIndex = $i - 1; } } push (@{$dashboards[$minIndex]}, $widget); } my @params; for my $i (1 .. $NUM_DASHBOARDS) { #save the current state my @dash_widgets = map { $_->{'module'} . ":" . $_->{'name'} } @{$dashboards[$i-1]}; $sysinfo->setDashboard("dashboard$i", \@dash_widgets); push(@params, "dashboard$i" => \@{$dashboards[$i-1]}); } push(@params, 'toggled' => $sysinfo->toggledElements()); push(@params, 'brokenPackages' => $global->brokenPackages()); if (EBox::Global->modExists('software')) { push(@params, 'softwareInstalled' => 1); } return \@params; } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/DesktopServices/0000775000000000000000000000000012017102272017762 5ustar zentyal-core-2.3.21+quantal1/src/EBox/CGI/DesktopServices/Index.pm0000664000000000000000000000373512017102272021377 0ustar # Copyright (C) 2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::DesktopServices::Index; use strict; use warnings; use base 'EBox::CGI::ClientBase'; use Error qw(:try); use JSON::XS; use EBox::Global; sub new { my $class = shift; my $self = $class->SUPER::new(@_); bless($self, $class); return $self; } sub _validateReferer { return; } sub _validateParams { return; } # Method: actuate # # Overrides: # # # sub actuate { my ($self) = @_; # Parse the url my $url = $ENV{'script'}; $url =~ m:^([a-zA-Z]+)/([a-zA-Z]+)/$:; my $module_name = $1; my $action_name = $2; # List of all desktop service providers my $global = EBox::Global->getInstance(); my @modules = @{$global->modInstancesOfType('EBox::Desktop::ServiceProvider')}; $self->{json} = undef; foreach my $module ( @modules ) { # If the module is the one we are looking for if ($module->name() eq $module_name) { # All the exposed actions of the module my %actions = %{$module->desktopActions()}; foreach my $actname (keys %actions) { # If the action is the one we are looking for if ($actname eq $action_name) { $self->{json} = $actions{$actname}->(); } } } } } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/View/0000775000000000000000000000000012017102272015557 5ustar zentyal-core-2.3.21+quantal1/src/EBox/CGI/View/Composite.pm0000664000000000000000000000506512017102272020065 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::CGI::View::Composite # # This CGI is intended to show the composite model. The optional # parameters can follow this format: # # modelName - directory # # For example, ObjectTable=objectTable/keys/obje7908/members # package EBox::CGI::View::Composite; use strict; use warnings; use base 'EBox::CGI::ClientBase'; use EBox::Global; # Constructor: new # # Create the general Composite View CGI # # Parameters: # # the parent parameters # # compositeModel - the composite model # to show # # Returns: # # - the recently created CGI # sub new { my $class = shift; my %params = @_; my $composite = delete $params{composite}; my $self = $class->SUPER::new(template => $composite->Viewer(), @_); $self->{composite} = $composite; bless ($self, $class); return $self; } # Method: _header # # Overrides to print the page title in the HTML title if defined # # Overrides: # # # sub _header { my ($self) = @_; print $self->cgi()->header(-charset=>'utf-8'); print EBox::Html::header($self->{composite}->pageTitle()); } sub _process { my ($self) = @_; my $composite = $self->{'composite'}; my $directory = $self->param('directory'); $self->setMenuFolder($composite->menuFolder()); if (defined $directory) { $composite->setDirectory($directory); } else { $composite->setDirectory(''); } $self->{params} = $self->masonParameters(); } # Method: masonParameters # # Overrides # sub masonParameters { my ($self) = @_; my $global = EBox::Global->getInstance(); return [ model => $self->{composite}, hasChanged => $global->unsaved(), ]; } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/View/DataTable.pm0000664000000000000000000001044412017102272017741 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::View::DataTable; use strict; use warnings; use base 'EBox::CGI::ClientBase'; use EBox::Gettext; use EBox::Global; # Group: Public methods sub new # (cgi=?) { my $class = shift; my %params = @_; my $tableModel = delete $params{'tableModel'}; my $self = $class->SUPER::new('template' => $tableModel->Viewer(), @_); $self->{'tableModel'} = $tableModel; bless($self, $class); return $self; } # Group: Protected methods # Method: _header # # Overrides to print the page title in the HTML title if defined # # Overrides: # # # sub _header { my ($self) = @_; print $self->cgi()->header(-charset=>'utf-8'); print EBox::Html::header($self->{tableModel}->pageTitle()); } sub _process { my $self = shift; my $global = EBox::Global->getInstance(); my $model = $self->{'tableModel'}; $self->setMenuFolder($model->menuFolder()); my $directory = $self->param('directory'); if ($directory) { $model->setDirectory($directory); } if ( $self->param('action') eq 'presetUpdate' ) { $self->_presetUpdate(); } else { my @params; push(@params, 'data' => undef ); push(@params, 'dataTable' => $model->table()); push(@params, 'model' => $model); push(@params, 'hasChanged' => $global->unsaved()); push(@params, 'tpages' => 0); push(@params, 'page' => 0); $self->{'params'} = \@params; } } # Group: Private methods # Method to check if the given parameters are exactly in the model or # not and then add sub _presetUpdate { my ($self) = @_; my $model = $self->{'tableModel'}; my $presetParams = $self->_fillTypes(); my ($editid, $action) = ( '', $self->param('action')); if ( $model->rowUnique() ) { # Then, search for the element in order to edit instead of # adding a new one my $foundId = $self->_findEqualRow($model, $presetParams); if ( $foundId ) { $editid = $foundId; } } # Not unique or just adding a new unique row my $gl = EBox::Global->getInstance(); my @params; push(@params, 'model' => $model); push(@params, 'action' => $action); push(@params, 'hasChanged' => $gl->unsaved()); push(@params, 'presetParams' => $presetParams); push(@params, 'editid' => $editid); push(@params, 'page' => 0); $self->{'params'} = \@params; } # Method to fill a hash with instanced types from the array of CGI # params sub _fillTypes { my $self = shift; my $tableDesc = $self->{'tableModel'}->table()->{'tableDescription'}; my %params; my $cgiParams = $self->paramsAsHash(); foreach my $field (@{$tableDesc}) { my $instancedType = $field->clone(); $instancedType->setValue($cgiParams->{$instancedType->fieldName()}); $params{$field->fieldName()} = $instancedType; } return \%params; } # Method to search for the same row sub _findEqualRow { my ($self, $model, $presetParams) = @_; my $foundId = 0; foreach my $id (@{$model->ids()}) { my $row = $model->row($id); my $valueHash = $row->{'valueHash'}; my $nEqual = grep { $valueHash->{$_}->isEqualTo($presetParams->{$_}) } @{$model->fields()}; EBox::debug($nEqual); next unless ( $nEqual == scalar (@{$model->fields()})); $foundId = $row->{'id'}; last; } return $foundId; } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/View/DataMultiTable.pm0000664000000000000000000000261412017102272020754 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::View::DataMultiTable; use strict; use warnings; use base 'EBox::CGI::ClientBase'; use EBox::Gettext; use EBox::Global; # Constructor: new # # The constructor # # Parameters: # # multiTableModel - the multi table model # # sub new { my $class = shift; my %params = @_; my $self = $class->SUPER::new('template' => '/ajax/tableSelector.mas', @_); $self->{multiTableModel} = delete $params{multiTableModel}; bless($self, $class); return $self; } sub _process { my $self = shift; my @params; push ( @params, $self->{multiTableModel} ); $self->{'params'} = \@params; } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/Menu.pm0000664000000000000000000000472312017102272016115 0ustar # Copyright (C) 2009-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::Menu; use strict; use warnings; use base 'EBox::CGI::ClientRawBase'; use EBox::Config; use EBox::Menu; use Error qw(:try); use JSON; use Storable qw(retrieve); sub new # (error=?, msg=?, cgi=?) { my $class = shift; my $self = $class->SUPER::new(@_); bless($self, $class); return $self; } # Method: requiredParameters # # Overrides: # # # sub requiredParameters { return ['search']; } # Method: actuate # # Overrides: # # # sub actuate { my ($self) = @_; my $search = $self->param('search'); my $menuclass = $self->{namespace} . '::Menu'; eval "use $menuclass"; my $file = $menuclass->cacheFile(); unless (-f $file) { $menuclass->regenCache(); } my $keywords = retrieve($file); my @search_items = split(/\W+/, $search); my $sections = {}; my @words = keys(%{$keywords}); for my $it (@search_items) { my @fullwords = grep(/^$it/,@words); my $cur = {}; for my $word (@fullwords) { my $sects = $keywords->{$word}; for my $sect (@{$sects}) { if(not defined($cur->{$sect})) { $cur->{$sect} = 1; } } } for my $sect (keys %{$cur}) { if(not defined($sections->{$sect})) { $sections->{$sect} = 0; } $sections->{$sect}++; } } $self->{sections} = []; for my $sect (keys %{$sections}) { if($sections->{$sect} == @search_items) { push(@{$self->{sections}}, $sect); } } } sub _print { my ($self) = @_; print($self->cgi()->header(-charset=>'utf-8',-type=>'application/json')); my $js = objToJson($self->{sections}); print $js; } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/SaveChanges.pm0000664000000000000000000001050112017102272017367 0ustar # Copyright (C) 2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA use strict; use warnings; package EBox::CGI::SaveChanges; use base qw(EBox::CGI::ClientBase EBox::CGI::ProgressClient); use EBox::Config; use EBox::Global; use EBox::Gettext; use Error qw(:try); sub new # (error=?, msg=?, cgi=?) { my $class = shift; my $self = $class->SUPER::new('title' => __('Save configuration'), @_); bless($self, $class); return $self; } sub _process { my $self = shift; my $global = EBox::Global->getInstance(); if (not $global->unsaved) { # installer gives false positive there if (not $self->param('installer')) { throw EBox::Exceptions::External("No changes to be saved or revoked"); } } if (defined($self->param('save'))) { $self->saveAllModulesAction(); } elsif (defined($self->param('cancel'))) { $self->revokeAllModulesAction(); } else { throw EBox::Exceptions::External("No save or cancel parameter"); } } my @commonProgressParams = ( reloadInterval => 2, ); my @popupProgressParams = ( raw => 1, inModalbox => 1, nextStepType => 'submit', nextStepText => __('OK'), nextStepUrl => '#', nextStepUrlFailureOnclick => "Modalbox.hide(); window.location.reload(); return false", ); sub saveAllModulesAction { my ($self) = @_; my $global = EBox::Global->getInstance(); my $progressIndicator = $global->prepareSaveAllModules(); my @params = ( progressIndicator => $progressIndicator, text => __('Saving changes in modules'), currentItemCaption => __("Current operation"), itemsLeftMessage => __('operations performed'), endNote => __('Changes saved'), errorNote => __x('Some modules reported error when saving changes ' . '. More information on the logs in {dir}', dir => EBox::Config->log()), @commonProgressParams ); if ($self->param('noPopup')) { push @params, (title => __('Saving changes')); } else { push @params, @popupProgressParams; push @params, nextStepUrlOnclick => "Modalbox.hide(); \$('changes_menu').removeClassName('changed').addClassName('notchanged'); return false"; } $self->showProgress(@params); } sub revokeAllModulesAction { my ($self) = @_; my $global = EBox::Global->getInstance(); my $progressIndicator = $global->prepareRevokeAllModules(); my @params = ( progressIndicator => $progressIndicator, text => __('Revoking changes in modules'), currentItemCaption => __("Current module"), itemsLeftMessage => __('modules revoked'), endNote => __('Changes revoked'), errorNote => __x('Some modules reported error when discarding changes ' . '. More information on the logs in {dir}', dir => EBox::Config->log()), @commonProgressParams ); if ($self->param('noPopup')) { push @params, (title => __('Revoking changes')); } else { push @params, @popupProgressParams; push @params, nextStepUrlOnclick => "Modalbox.hide(); window.location.reload(); return false"; } $self->showProgress(@params); } # to avoid the
sub _print { my ($self) = @_; if ($self->param('noPopup')) { return $self->SUPER::_print(); } my $json = $self->{json}; if ($json) { $self->JSONReply($json); return; } $self->_header; print '
'; $self->_error; $self->_msg; $self->_body; print "
"; } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/ProgressClient.pm0000664000000000000000000000517212017102272020153 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::ProgressClient; # this class is to provide helper method for any CGI which must # use strict; use warnings; sub new { throw EBox::Exceptions::NotImplemented('This class must be inherited by other class which also inherits from EBox::CGI::Base'); } # Method: showProgress # # Redirect the browser to the progression screen CGI # # Parameters: # progressIndicator - a instance of EBox::ProgressIndicator needed # to drive the progress screen (mandatory) # # title - title of the page # currentItemCaption - caption before the actual item value # itemsLeftMessage - text after the 'x of y' # endNote - text of the note showed when the operation ends # # errorNote - String text showed when operation has not finished # correctly # # reloadInterval - reload interval in seconds (default 5) # # currentItemUrl - with this you can change the CGI used to fetch the current # item data. Probably you would NOT need it # # # url - URL to the progress' CGI. Most of the time you don't want to touch this # default: '/Progress' # # nextStepUrl - URL to redirect when job is done # # nextStepText - Text to show in link to redirect when job is done # # (other parameters) - will be passed to the CGI if they are defined sub showProgress { my ($self, %params) =@_; $params{progressIndicator} or throw EBox::Exceptions::MissingArgument('progressIndicator'); $params{url} or $params{url} = '/Progress'; my $progressIndicator = delete $params{progressIndicator}; my $url = delete $params{url}; $self->cgi()->delete(@{ $self->params() }); $self->cgi()->param('progress' => $progressIndicator->id()); $self->keepParam('progress'); # put the optional parameters in the CGI while (my ($param, $value) = each %params) { $self->cgi()->param($param, $value); $self->keepParam($param); } $self->setChain($url); } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/SysInfo/0000775000000000000000000000000012017102272016237 5ustar zentyal-core-2.3.21+quantal1/src/EBox/CGI/SysInfo/CurrentProgress.pm0000664000000000000000000000340612017102272021747 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::SysInfo::CurrentProgress; use strict; use warnings; use base 'EBox::CGI::ClientBase'; use EBox; use EBox::Global; use EBox::Gettext; use EBox::Config; use EBox::ProgressIndicator; use Error qw(:try); use JSON; ## arguments: ## title [required] sub new { my $class = shift; my $self = $class->SUPER::new('title' => __('Upgrading'), 'template' => 'none', @_); bless($self, $class); return $self; } sub _process { my ($self) = @_; $self->{params} = []; } sub _print { my ($self) = @_; my $progressId = $self->param('progress'); my $progress = EBox::ProgressIndicator->retrieve($progressId); my $response = $progress->stateAsHash(); $response->{changed} = $self->modulesChangedStateAsHash(); print($self->cgi()->header(-charset=>'utf-8')); print to_json($response); } sub modulesChangedStateAsHash { my ($self) = @_; my $global = EBox::Global->getInstance(); my $state = $global->unsaved() ? 'changed' : 'notChanged'; return $state; } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/SysInfo/Log.pm0000664000000000000000000000253712017102272017325 0ustar # Copyright (C) 2010-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::SysInfo::Log; use strict; use warnings; use EBox; use EBox::Config; use EBox::Gettext; use EBox::Util::BugReport; use base 'EBox::CGI::ClientBase'; sub new # (cgi=?) { my $class = shift; my $self = $class->SUPER::new(@_); bless($self, $class); return $self; } sub actuate { my ($self) = @_; $self->{downfilename} = 'zentyal.log'; } sub _print { my ($self) = @_; if ($self->{error}) { $self->SUPER::_print; return; } print ($self->cgi()->header(-type=>'application/octet-stream', -attachment=>$self->{downfilename})); print EBox::Util::BugReport::dumpLog(); } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/SysInfo/Backup.pm0000664000000000000000000002034712017102272020010 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA use strict; use warnings; package EBox::CGI::SysInfo::Backup; use base qw(EBox::CGI::ClientBase EBox::CGI::ProgressClient); use Error qw(:try); use EBox::Config; use EBox::Backup; use EBox::Gettext; use EBox::Exceptions::Internal; use EBox::Exceptions::External; use Sys::Hostname; sub new # (error=?, msg=?, cgi=?) { my $class = shift; my $self = $class->SUPER::new('title' => __('Import/Export Configuration'), 'template' => '/backupTabs.mas', @_); $self->{errorchain} = "SysInfo/Backup"; $self->{audit} = EBox::Global->modInstance('audit'); bless($self, $class); return $self; } sub _print { my ($self) = @_; if (defined($self->{downfile}) and (not defined $self->{error})) { # file download open(BACKUP,$self->{downfile}) or throw EBox::Exceptions::Internal('Could not open backup file.'); print($self->cgi()->header(-type=>'application/octet-stream', -attachment=>$self->{downfilename})); while () { print $_; } close BACKUP; return; } if (not $self->{popup}) { return $self->SUPER::_print(); } $self->_printPopup(); } sub requiredParameters { my ($self) = @_; if ($self->param('backup')) { return [qw(backup description)]; } elsif ($self->param('bugreport')) { return [qw(bugreport )]; } elsif ($self->param('restoreFromFile')) { return [qw(restoreFromFile backupfile)]; } elsif ($self->param('restoreFromId')) { return [qw(restoreFromId id)]; } elsif ($self->param('download.x')) { return [qw(id download.x download.y)]; } elsif ($self->param('delete')) { return [qw(delete id)]; } elsif ($self->param('bugReport')) { return [qw(bugReport)]; } else { return []; } } sub optionalParameters { my ($self) = @_; if ($self->param('cancel')) { return ['.*']; } return ['selected', 'download', 'popup']; } sub actuate { my ($self) = @_; $self->{popup} = $self->param('popup'); $self->param('cancel') and return; if ($self->param('backup')) { $self->_backupAction(); } elsif ($self->param('bugreport')) { $self->_bugreportAction(); } elsif ($self->param('delete')) { $self->_deleteAction(); } elsif ($self->param('download')) { $self->_downloadAction(); } elsif ($self->param('restoreFromId')) { $self->_restoreFromIdAction(); } elsif ($self->param('restoreFromFile')) { $self->_restoreFromFileAction(); } } sub masonParameters { my ($self) = @_; my @params = (); my $backup = EBox::Backup->new(); push @params, (backups => $backup->listBackups()); my $global = EBox::Global->getInstance(); my $modulesChanged = grep { $global->modIsChanged($_) } @{ $global->modNames() }; push @params, (modulesChanged => $modulesChanged); push @params, (selected => 'local'); my $subscribed = 0; if ($global->modExists('remoteservices')) { my $rs = $global->modInstance('remoteservices'); $subscribed = $rs->eBoxSubscribed(); } push @params, (subscribed => $subscribed); return \@params; } sub _backupAction { my ($self) = @_; my $description = $self->param('description'); my $progressIndicator; try { my $backup = EBox::Backup->new(); $progressIndicator= $backup->prepareMakeBackup(description => $description); } otherwise { my ($ex) = @_; $self->setErrorFromException($ex); }; if ($progressIndicator) { $self->_showBackupProgress($progressIndicator); $self->{audit}->logAction('System', 'Backup', 'exportConfiguration', $description); } elsif ($self->{popup}) { $self->{template} = '/ajax/simpleModalDialog.mas'; } } sub _restoreFromFileAction { my ($self) = @_; my $filename = $self->unsafeParam('backupfile'); # poor man decode html entity for '/' $filename =~ s{%2F}{/}g; $self->_restore($filename); $self->{audit}->logAction('System', 'Backup', 'importConfiguration', $filename); } sub _restoreFromIdAction { my ($self) = @_; my $id = $self->param('id'); if ($id =~ m{[./]}) { throw EBox::Exceptions::External( __("The input contains invalid characters")); } $self->_restore(EBox::Config::conf ."/backups/$id.tar"); $self->{audit}->logAction('System', 'Backup', 'importConfiguration', $id); } sub _restore { my ($self, $filename) = @_; my $backup = new EBox::Backup; my $progressIndicator; try { $progressIndicator = $backup->prepareRestoreBackup($filename); } otherwise { my ($ex) = @_; $self->setErrorFromException($ex); }; if ($progressIndicator) { $self->_showRestoreProgress($progressIndicator); } elsif ($self->{popup}) { $self->{template} = '/ajax/simpleModalDialog.mas'; } } my @popupProgressParams = ( raw => 1, inModalbox => 1, nextStepType => 'submit', nextStepText => __('OK'), nextStepUrl => '#', nextStepUrlOnclick => "Modalbox.hide(); window.location='/SysInfo/Backup?selected=local'; return false", ); sub _showBackupProgress { my ($self, $progressIndicator) = @_; my @params = ( progressIndicator => $progressIndicator, title => __('Backing up'), text => __('Backing up modules'), currentItemCaption => __('Operation') , itemsLeftMessage => __('operations left to finish backup'), endNote => __('Backup successful'), reloadInterval => 2, ); if ($self->param('popup')) { push @params, @popupProgressParams; } $self->showProgress(@params); } sub _showRestoreProgress { my ($self, $progressIndicator) = @_; my @params = ( progressIndicator => $progressIndicator, title => __('Restoring backup'), text => __('Restoring modules'), currentItemCaption => __('Module') , itemsLeftMessage => __('modules restored'), endNote => __('Restore successful'), reloadInterval => 4, ); if ($self->param('popup')) { push @params, @popupProgressParams; } $self->showProgress(@params); } sub _downloadAction { my ($self) = @_; my $id = $self->param('id'); if ($id =~ m{[./]}) { throw EBox::Exceptions::External( __("The input contains invalid characters")); } $self->{downfile} = EBox::Config::conf . "/backups/$id.tar"; $self->{downfilename} = hostname() . "_$id.tar"; $self->{audit}->logAction('System', 'Backup', 'downloadConfigurationBackup', $id); } sub _deleteAction { my ($self) = @_; my $id = $self->param('id'); if ($id =~ m{[./]}) { throw EBox::Exceptions::External( __("The input contains invalid characters")); } my $backup = EBox::Backup->new(); $backup->deleteBackup($id); $self->{audit}->logAction('System', 'Backup', 'deleteConfigurationBackup', $id); } sub _bugreportAction { my ($self) = @_; my $backup = EBox::Backup->new(); $self->{errorchain} = 'SysInfo/Bug'; $self->{downfile} = $backup->makeBugReport(); $self->{downfilename} = 'zentyal-configuration-report.tar'; $self->{audit}->logAction('System', 'Backup', 'downloadConfigurationReport'); } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/SysInfo/RestartService.pm0000664000000000000000000000451612017102272021550 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::SysInfo::RestartService; use strict; use warnings; use base 'EBox::CGI::ClientBase'; use EBox::Global; use EBox::Config; use EBox::Gettext; use EBox::Exceptions::Internal; use Error qw(:try); sub new # (cgi=?) { my $class = shift; my $self = $class->SUPER::new(@_); bless($self, $class); $self->{errorchain} = "/Dashboard/Index"; $self->{redirect} = "/Dashboard/Index"; return $self; } sub domain { return 'ebox'; } sub _process { my $self = shift; my $global = EBox::Global->getInstance(1); $self->_requireParam('module', __('module name')); my $mod = $global->modInstance($self->param('module')); my $name = $mod->printableName(); $self->{chain} = "/Dashboard/Index"; try { $mod->restartService(); $self->{msg} = __('The module was restarted correctly.'); my $audit = $global->modInstance('audit'); $audit->logAction('Dashboard', 'Module Status', 'restartService', $name); } catch EBox::Exceptions::Lock with { EBox::error("Restart of $name from dashboard failed because it was locked"); $self->{msg} = __x('Service {mod} is locked by another process. Please wait its end and then try again.', mod => $name, ); } catch EBox::Exceptions::Internal with { my ($ex) = @_; EBox::error("Restart of $name from dashboard failed: " . $ex->text); $self->{msg} = __x('Error restarting service {mod}. See {logs} for more information.', mod => $name, logs => EBox::Config::logfile()); }; $self->cgi()->delete_all(); } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/SysInfo/ConfirmBackup.pm0000664000000000000000000001133212017102272021320 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA use strict; use warnings; package EBox::CGI::SysInfo::ConfirmBackup; use base 'EBox::CGI::ClientBase'; use EBox::Config; use EBox::Backup; use EBox::Gettext; use EBox::Exceptions::Internal; use EBox::Exceptions::External; use Error qw(:try); sub new # (error=?, msg=?, cgi=?) { my $class = shift; my $self = $class->SUPER::new('title' => __('Configuration Backup'), 'template' => '/confirm-backup.mas', @_); $self->{errorchain} = "SysInfo/Backup"; bless($self, $class); return $self; } sub requiredParameters { my ($self) = @_; if ($self->param('download.x')) { return [qw(id download.x download.y)]; } elsif ($self->param('delete.x')) { return [qw(id delete.x delete.y)]; } elsif ($self->param('delete')) { return [qw(id delete)]; } elsif ($self->param('restoreFromId.x')) { return [qw(restoreFromId.x restoreFromId.y id)]; } elsif ($self->param('restoreFromId')) { return [qw(restoreFromId id)]; } elsif ($self->param('restoreFromFile')) { return [qw(restoreFromFile backupfile)]; } return []; } sub optionalParameters { return ['download', 'delete', 'popup', 'alreadyUploaded']; } sub actuate { my ($self) = @_; if (defined($self->param('download.x'))) { $self->{chain} = 'SysInfo/Backup'; return; } foreach my $actionParam (qw(delete restoreFromId restoreFromFile )) { if ($self->param($actionParam)) { my $actionSub = $self->can($actionParam . 'Action'); my ($backupAction, $backupActionText, $backupDetails) = $actionSub->($self); $self->{params} = [action => $backupAction, actiontext => $backupActionText, backup => $backupDetails]; if ($self->param('popup')) { push @{ $self->{params} }, (popup => 1); } return; } } # otherwise... $self->{redirect} = "SysInfo/Backup"; return; } sub masonParameters { my ($self) = @_; if (exists $self->{params}) { return $self->{params}; } return []; } sub deleteAction { my ($self) = @_; $self->{msg} = __('Please confirm that you want to delete the following backup file:'); return ('delete', __('Delete'), $self->backupDetailsFromId()); } sub restoreFromIdAction { my ($self) = @_; $self->{msg} = __('Please confirm that you want to restore using this backup file:'); return ('restoreFromId', __('Restore'), $self->backupDetailsFromId()); } sub restoreFromFileAction { my ($self) = @_; my $filename; if ($self->param('alreadyUploaded')) { $filename = $self->param('backupfile'); } else { $filename = $self->upload('backupfile'); } my $details = $self->backupDetailsFromFile($filename); $self->{msg} = __('Please confirm that you want to restore using this backup file:'); return ('restoreFromFile', __('Restore'), $details); } sub backupDetailsFromId { my ($self) = @_; my $backup = new EBox::Backup; my $id = $self->param('id'); if ($id =~ m{[./]}) { throw EBox::Exceptions::External( __("The input contains invalid characters")); } my $details = $backup->backupDetails($id); $self->setPrintabletype($details); return $details; } sub backupDetailsFromFile { my ($self, $filename) = @_; my $details = EBox::Backup->backupDetailsFromArchive($filename); $self->setPrintabletype($details); return $details; } sub setPrintabletype { my ($self, $details_r) = @_; my $type = $details_r->{type}; my $printableType; if ($type eq $EBox::Backup::CONFIGURATION_BACKUP_ID) { $printableType = __('Configuration backup'); } elsif ($type eq $EBox::Backup::FULL_BACKUP_ID) { $printableType = __('Full data and configuration backup'); } elsif ($type eq $EBox::Backup::BUGREPORT_BACKUP_ID) { $printableType = __('Bug-report configuration dump'); } $details_r->{printableType} = $printableType; return $details_r; } # to avoid the
sub _print { my ($self) = @_; if (not $self->param('popup')) { return $self->SUPER::_print(); } $self->_printPopup(); } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/SysInfo/PageNotFound.pm0000664000000000000000000000231612017102272021130 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::SysInfo::PageNotFound; use base 'EBox::CGI::ClientBase'; # Description: CGI for "page not found error" use strict; use warnings; use EBox::Gettext; sub new { my $class = shift; my $title = __("Page not found"); my $template = 'pageNotFound.mas'; my $self = $class->SUPER::new(title => $title, template => $template, @_); bless($self, $class); return $self; } # we do nothing, # we can not even valdiate params because this a page not found error (any parameter can be in) sub _process {} 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/SysInfo/CreateReport.pm0000664000000000000000000000326612017102272021203 0ustar # Copyright (C) 2011-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::SysInfo::CreateReport; use strict; use warnings; use base qw(EBox::CGI::ClientBase); use EBox::Util::BugReport; use Error qw(:try); sub new # (error=?, msg=?, cgi=?) { my $class = shift; my $self = $class->SUPER::new(@_); bless($self, $class); return $self; } sub _print { my ($self) = @_; my $description = $self->unsafeParam('description'); $description .= "\n\n'''Error'''\n\n"; $description .= "{{{\n"; $description .= $self->unsafeParam('error'); $description .= "\n}}}"; $description .= "\n\n'''Trace'''\n\n"; $description .= "{{{\n"; $description .= $self->unsafeParam('stacktrace'); $description .= "\n}}}"; my $ticket = EBox::Util::BugReport::send($self->unsafeParam('email'), $description); print($self->cgi()->header(-charset=>'utf-8')); print 'OK ' . $ticket; } sub requiredParameters { my ($self) = @_; return ['email', 'description', 'error', 'stacktrace']; } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/Report/0000775000000000000000000000000012017102272016120 5ustar zentyal-core-2.3.21+quantal1/src/EBox/CGI/Report/RAID.pm0000664000000000000000000000304612017102272017200 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::Report::RAID; use strict; use warnings; use base 'EBox::CGI::ClientBase'; use EBox; use EBox::Report::RAID; use EBox::Gettext; sub new # (error=?, msg=?, cgi=?) { my $class = shift; my $self = $class->SUPER::new('title' => 'RAID', 'template' => '/report/raid.mas', @_); bless($self, $class); return $self; } sub _process { my $self = shift; my $raidInfo = EBox::Report::RAID::info(); my $array = $self->param('array'); my @templateParams = ( array => $array, raidInfo => $raidInfo, ); $self->{params} = \@templateParams; } # Method: menuFolder # # Overrides # to set the menu folder sub menuFolder { return 'Maintenance'; } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/Report/DiskUsage.pm0000664000000000000000000000367712017102272020352 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::Report::DiskUsage; use strict; use warnings; use base 'EBox::CGI::ClientBase'; use EBox; use EBox::Report::DiskUsage; use EBox::FileSystem; use EBox::Gettext; sub new # (error=?, msg=?, cgi=?) { my $class = shift; my $self = $class->SUPER::new('title' => __('Disk Usage'), 'template' => '/report/diskUsage.mas', @_); bless($self, $class); return $self; } sub _process { my $self = shift; my $fileSystems = EBox::FileSystem::partitionsFileSystems(); my $partition = $self->param('partition'); my @partitions = sort keys %{ $fileSystems }; # if not partition supplied pick up the first in alphabetical order if (not $partition) { $partition = $partitions[0]; } my $chartUrl = EBox::Report::DiskUsage::chart($partition); my @templateParams = ( partition => $partition, partitionAttr => $fileSystems->{$partition}, chartUrl => $chartUrl, partitions => \@partitions, ); $self->{params} = \@templateParams; } # Method: menuFolder # # Overrides # to set the menu folder sub menuFolder { return 'Maintenance'; } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/Finish.pm0000664000000000000000000001242212017102272016424 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA use strict; use warnings; package EBox::CGI::Finish; use base qw(EBox::CGI::ClientBase EBox::CGI::ProgressClient); use EBox::Config; use EBox::Global; use EBox::Gettext; use EBox::ServiceManager; use Error qw(:try); sub new # (error=?, msg=?, cgi=?) { my $class = shift; my $self = $class->SUPER::new( #'title' => __('Save configuration'), 'template' => '/finish.mas', @_); bless($self, $class); return $self; } sub _process { my $self = shift; my @array = (); my $global = EBox::Global->getInstance(); if ($global->unsaved) { my $manager = new EBox::ServiceManager(); #my $askPermission = defined @{$manager->checkFiles()}; push(@array, 'unsaved' => 'yes'); push(@array, 'askPermission' => 0); push(@array, 'disabledModules' => _disabledModules()); push(@array, 'actions' => _pendingActions()); } else { push(@array, 'unsaved' => 'no'); } $self->{params} = \@array; } sub _pendingActions { my $global = EBox::Global->getInstance(1); my $audit = EBox::Global->modInstance('audit'); my $ret = $audit->queryPending(); my $actions = []; foreach my $action (@{$ret}) { my $modname = $action->{'module'}; my $model = $action->{'model'}; my $rowName; if($global->modExists($modname)) { my $mod = EBox::Global->modInstance($modname); $action->{'modtitle'} = $mod->title(); try { my $modelInstance = $mod->model($model); $action->{'modeltitle'} = $modelInstance->printableName(); $rowName = $modelInstance->printableRowName(); } otherwise { $action->{'modeltitle'} = $action->{'model'}; }; } else { $action->{'modtitle'} = $modname; } my $event = $action->{'event'}; my $id = $action->{'id'}; my $value = $action->{'value'}; my $oldvalue = $action->{'oldvalue'}; unless ($rowName) { $rowName = __('row'); } my $message; if ($event eq 'del') { $message = __x('The {rowName} "{r}" has been deleted', rowName => $rowName, r => $id); } elsif ($event eq 'move') { $message = __x('The {rowName} "{r}" has been moved from {x} to {y} position', rowName => $rowName, r => $id, x => $oldvalue, y => $value); } elsif ($event eq 'action') { my $action = $value ? "$id($value)" : $id; $message = __x('The action "{a}" has been executed', a => $action); } else { my ($parent, $row, $field) = split (/\//, $id); if (defined ($parent) and defined ($row)) { if (defined ($field)) { $row = "$parent/$row"; } else { ($row, $field) = ($parent, $row); } if ($event eq 'add') { $message = __x('A new {rowName} "{r}" has been added with "{f}" set to "{x}"', f => $field, rowName => $rowName, r => $row, x => $value); } elsif ($event eq 'set') { $message = __x('The field "{f}" in the {rowName} "{r}" has been changed from "{x}" to "{y}"', f => $field, rowName => $rowName, r => $row, x => $oldvalue, y => $value); } } else { if (($event eq 'set') and defined ($oldvalue)) { $message = __x('The value of "{id}" has been changed from "{x}" to "{y}"', id => $id, x => $oldvalue, y => $value); } else { $message = __x('The value of "{id}" has been set to "{x}"', id => $id, x => $oldvalue); } } } $action->{'message'} = $message; push(@{$actions}, $action); } return $actions; } # Method: _disabledModules # # Return those modules with unsaved changes that are disabled sub _disabledModules { my $global = EBox::Global->getInstance(); my @modules; for my $modName (@{$global->modifiedModules('save')}) { my $modInstance = $global->modInstance($modName); next unless ($modInstance->isa('EBox::Module::Service')); next if ($modInstance->isEnabled()); next unless ($modInstance->showModuleStatus()); push (@modules, $modInstance->printableName()); } return \@modules; } sub _print { my ($self) = @_; $self->_printPopup(); } sub _top {} sub _menu {} sub _footer {} 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/Base.pm0000664000000000000000000006327712017102272016074 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::Base; use strict; use warnings; use HTML::Mason; use HTML::Mason::Exceptions; use CGI; use EBox::Gettext; use EBox; use EBox::Global; use EBox::Exceptions::Base; use EBox::Exceptions::Internal; use EBox::Exceptions::External; use EBox::Exceptions::DataMissing; use EBox::Util::GPG; use POSIX qw(setlocale LC_ALL); use Error qw(:try); use Encode qw(:all); use Data::Dumper; use Perl6::Junction qw(all); use File::Temp qw(tempfile); use File::Basename; use Apache2::Connection; use Apache2::RequestUtil; use JSON::XS; ## arguments ## title [optional] ## error [optional] ## msg [optional] ## cgi [optional] ## template [optional] sub new # (title=?, error=?, msg=?, cgi=?, template=?) { my $class = shift; my %opts = @_; my $self = {}; $self->{title} = delete $opts{title}; $self->{crumbs} = delete $opts{crumbs}; $self->{olderror} = delete $opts{error}; $self->{msg} = delete $opts{msg}; $self->{cgi} = delete $opts{cgi}; $self->{template} = delete $opts{template}; unless (defined($self->{cgi})) { $self->{cgi} = new CGI; } $self->{paramsKept} = (); # XXX workaround for utf8 hell if (Encode::is_utf8($self->{title})) { Encode::_utf8_off($self->{title}); } bless($self, $class); return $self; } sub _header {} sub _top {} sub _menu {} sub _title { my $self = shift; my $title = $self->{title}; my $crumbs = $self->{crumbs}; my $filename = EBox::Config::templates . '/title.mas'; my $interp = $self->_masonInterp(); my $comp = $interp->make_component(comp_file => $filename); my @params = (title => $title, crumbs => $crumbs); $interp->exec($comp, @params); } sub _print_error # (text) { my ($self, $text) = @_; $text or return; ($text ne "") or return; my $filename = EBox::Config::templates . '/error.mas'; my $interp = $self->_masonInterp(); my $comp = $interp->make_component(comp_file => $filename); my @params = (); push(@params, 'error' => $text); $interp->exec($comp, @params); } sub _error # { my $self = shift; defined($self->{olderror}) and $self->_print_error($self->{olderror}); defined($self->{error}) and $self->_print_error($self->{error}); } sub _msg { my $self = shift; defined($self->{msg}) or return; my $filename = EBox::Config::templates . '/msg.mas'; my $interp = $self->_masonInterp(); my $comp = $interp->make_component(comp_file => $filename); my @params = (); push(@params, 'msg' => $self->{msg}); $interp->exec($comp, @params); } sub _body { my $self = shift; defined($self->{template}) or return; my $filename = EBox::Config::templates . $self->{template}; if (-f "$filename.custom") { # Check signature if (EBox::Util::GPG::checkSignature("$filename.custom")) { $filename = "$filename.custom"; EBox::info("Using custom $filename"); } else { EBox::warn("Invalid signature in $filename"); } } my $interp = $self->_masonInterp(); my $comp = $interp->make_component(comp_file => $filename); $interp->exec($comp, @{$self->{params}}); } MASON_INTERP: { my $masonInterp; sub _masonInterp { my ($self) = @_; return $masonInterp if defined $masonInterp; $masonInterp = HTML::Mason::Interp->new( comp_root => EBox::Config::templates, escape_flags => { h => \&HTML::Mason::Escapes::basic_html_escape, }, ); return $masonInterp; } }; sub _footer {} sub _print { my ($self) = @_; my $json = $self->{json}; if ($json) { $self->JSONReply($json); return; } $self->_header; $self->_top; $self->_menu; print '
'; $self->_title; $self->_error; $self->_msg; $self->_body; print "
"; $self->_footer; } # alternative print for CGI runs in popup # it hs been to explicitly called instead of # the regular print. For example, overlaoding print and calling this sub _printPopup { my ($self) = @_; my $json = $self->{json}; if ($json) { $self->JSONReply($json); return; } $self->_header; print '
'; $self->_error; $self->_msg; $self->_body; print "
"; } sub _checkForbiddenChars { my ($self, $value) = @_; POSIX::setlocale(LC_ALL, EBox::locale()); _utf8_on($value); unless ( $value =~ m{^[\w /.?&+:\-\@]*$} ) { my $logger = EBox::logger; $logger->info("Invalid characters in param value $value."); $self->{error} ='The input contains invalid characters'; throw EBox::Exceptions::External(__("The input contains invalid " . "characters. All alphanumeric characters, plus these non " . "alphanumeric chars: /.?&+:-\@ and spaces are allowed.")); if (defined($self->{redirect})) { $self->{chain} = $self->{redirect}; } return undef; } no locale; } sub _loggedIn { my $self = shift; # TODO return 1; } sub _urlToChain # (url) { my $str = shift; $str =~ s/\?.*//g; $str =~ s/\//::/g; $str =~ s/::$//g; $str =~ s/^:://g; return "EBox::CGI::" . $str; } # arguments # - name of the required parameter # - display name for the parameter (as seen by the user) sub _requireParam # (param, display) { my ($self, $param, $display) = @_; unless (defined($self->unsafeParam($param)) && $self->unsafeParam($param) ne "") { $display or $display = $param; throw EBox::Exceptions::DataMissing(data => $display); } } # arguments # - name of the required parameter # - display name for the parameter (as seen by the user) sub _requireParamAllowEmpty # (param, display) { my ($self, $param, $display) = @_; foreach my $cgiparam (@{$self->params}){ return if ($cgiparam =~ /^$param$/); } throw EBox::Exceptions::DataMissing(data => $display); } sub run { my $self = shift; if (not $self->_loggedIn) { $self->{redirect} = "/Login/Index"; } else { try { $self->_validateReferer(); $self->_process(); } catch EBox::Exceptions::Internal with { my $e = shift; throw $e; } catch EBox::Exceptions::Base with { my $e = shift; $self->setErrorFromException($e); if (defined($self->{redirect})) { $self->{chain} = $self->{redirect}; } } otherwise { my $e = shift; throw $e; }; } if (defined($self->{error})) { #only keep the parameters in paramsKept my $params = $self->params; foreach my $param (@{$params}) { unless (grep /^$param$/, @{$self->{paramsKept}}) { $self->{cgi}->delete($param); } } if (defined($self->{errorchain})) { if ($self->{errorchain} ne "") { $self->{chain} = $self->{errorchain}; } } } if (defined($self->{chain})) { my $classname = _urlToChain($self->{chain}); if (not $self->isa($classname)) { eval "use $classname"; if ($@) { throw EBox::Exceptions::Internal("Cannot load $classname. Error: $@"); } my $chain = $classname->new('error' => $self->{error}, 'msg' => $self->{msg}, 'cgi' => $self->{cgi}); $chain->run; return; } } if ((defined($self->{redirect})) && (!defined($self->{error}))) { my $request = Apache2::RequestUtil->request(); my $headers = $request->headers_in(); my $via = $headers->{'Via'}; my $host= $headers->{'Host'}; my $fwhost = $headers->{'X-Forwarded-Host'}; # If the connection comes from a Proxy, # redirects with the Proxy IP address if (defined($via) and defined($fwhost)) { $host = $fwhost; } my $protocol; if ($request->subprocess_env('https')) { $protocol = 'https'; } else { $protocol = 'http'; } my $url = "$protocol://${host}/" . $self->{redirect}; print($self->cgi()->redirect($url)); return; } try { $self->_print } catch EBox::Exceptions::Internal with { my $ex = shift; $self->setErrorFromException($ex); $self->_print_error($self->{error}); } otherwise { my $ex = shift; my $logger = EBox::logger; if (isa_mason_exception($ex)) { $logger->error($ex->as_text); my $error = __("An internal error related to ". "a template has occurred. This is ". "a bug, relevant information can ". "be found in the logs."); $self->_print_error($error); } else { if ($ex->can('text')) { $logger->error('Exception: ' . $ex->text()); } else { $logger->error("Unknown exception"); } throw $ex; } }; } # Method: unsafeParam # # Get the CGI parameter value in an unsafe way allowing all # character # # This is a security risk and it must be used with caution # # Parameters: # # param - String the parameter's name to get the value from # # Returns: # # string - the parameter's value without any security check if the # context is scalar # # array - containing the string values for the given parameter if # the context is an array # sub unsafeParam # (param) { my ($self, $param) = @_; my $cgi = $self->cgi; my @array; my $scalar; if (wantarray) { @array = $cgi->param($param); (@array) or return undef; my @ret = (); foreach my $v (@array) { _utf8_on($v); push(@ret, $v); } return @ret; } else { $scalar = $cgi->param($param); #check if $param.x exists for input type=image unless (defined($scalar)) { $scalar = $cgi->param($param . ".x"); } defined($scalar) or return undef; _utf8_on($scalar); return $scalar; } } sub param # (param) { my ($self, $param) = @_; my $cgi = $self->cgi; my @array; my $scalar; if (wantarray) { @array = $cgi->param($param); (@array) or return undef; my @ret = (); foreach my $v (@array) { $v =~ s/\t/ /g; $v =~ s/^ +//; $v =~ s/ +$//; $self->_checkForbiddenChars($v); _utf8_on($v); push(@ret, $v); } return @ret; } else { $scalar = $cgi->param($param); #check if $param.x exists for input type=image unless (defined($scalar)) { $scalar = $cgi->param($param . ".x"); } defined($scalar) or return undef; $scalar =~ s/\t/ /g; $scalar =~ s/^ +//; $scalar =~ s/ +$//; $self->_checkForbiddenChars($scalar); _utf8_on($scalar); return $scalar; } } # Method: params # # Get the CGI parameters # # Returns: # # array ref - containing the CGI parameters # sub params { my ($self) = @_; my $cgi = $self->cgi; my @names = $cgi->param; # Prototype adds a '_' empty param to Ajax POST requests when the agent is # webkit based @names = grep { !/^_$/ } @names; foreach (@names) { $self->_checkForbiddenChars($_); } return \@names; } sub keepParam # (param) { my ($self, $param) = @_; push(@{$self->{paramsKept}}, $param); } sub cgi { my $self = shift; return $self->{cgi}; } # Method: setTemplate # set the html template used by the CGI. The template can also be set in the constructor/ # # Parameters: # $template - the template path relative to the template root # # See also: # new sub setTemplate { my ($self, $template) = @_; $self->{template} = $template; } # Method: _process # process the CGI # # Default behaviour: # the default behaviour is intended to standarize and ease some common operations so do not override it except for backward compability or special reasons. # The default behaviour validate the peresence or absence or CGI parameters using requiredParameters and optionalParameters method, then it calls the method actuate, where the functionality of CGI resides, and finally uses masonParameters to get the parameters needed by the html template invocation. sub _process { my ($self) = @_; $self->_validateParams(); $self->actuate(); $self->{params} = $self->masonParameters(); } # Method: setMsg # sets the message attribute # # Parameters: # $msg - message to be setted sub setMsg { my ($self, $msg) = @_; $self->{msg} = $msg; } # Method: setError # set the error message # # Parameters: # $error - message to be setted sub setError { my ($self, $error) = @_; $self->{error} = $error; } # Method: setErrorFromException # set the error message eusing the description value found in a exception # # Parameters: # $ex - exception used to set the error attributer sub setErrorFromException { my ($self, $ex) = @_; my $dump = EBox::Config::configkey('dump_exceptions'); if (defined ($dump) and ($dump eq 'yes')) { $self->{error} = $ex->stringify() if $ex->can('stringify'); $self->{error} .= "
\n"; $self->{error} .= "
\n";
        $self->{error} .= Dumper($ex);
        $self->{error} .= "
\n"; $self->{error} .= "
\n"; return; } if ($ex->isa('EBox::Exceptions::External')) { $self->{error} = $ex->stringify(); return; } my $debug = EBox::Config::configkey('debug'); if ($debug eq 'yes') { my $log = ''; $log = $ex->stringify() if $ex->can('stringify'); $log .= Dumper($ex); EBox::debug($log); } if ($ex->isa('EBox::Exceptions::Internal')) { $self->{error} = __("An internal error has ". "occurred. This is most probably a ". "bug, relevant information can be ". "found in the logs."); } elsif ($ex->isa('EBox::Exceptions::Base')) { $self->{error} = __("An unexpected internal ". "error has occurred. This is a bug, ". "relevant information can be found ". "in the logs."); } else { $self->{error} = __('Sorry, you have just hit a bug in Zentyal.'); } my $reportHelp = __x('Please look for the details in the {f} file and take a minute to {oh}submit a bug report{ch} so we can fix the issue as soon as possible.', f => '/var/log/zentyal/zentyal.log', oh => '', ch => ''); $self->{error} .= " $reportHelp"; } # Method: setRedirect # sets the redirect attribute. If redirect is set to some value, the parent class will do an HTTP redirect after the _process method returns. # # An HTTP redirect makes the browser issue a new HTTP request, so all the status data in the old request gets lost, but there are cases when you want to keep that data for the new CGI. This could be done using the setChain method instead # # When an error happens you don't want redirects at all, as the error message would be lost. If an error happens and redirect has been set, then that value is used as if it was chain. # # Parameters: # $redirect - value for the redirect attribute # # See also: # setRedirect, setErrorchain sub setRedirect { my ($self, $redirect) = @_; $self->{redirect} = $redirect; } # Method: setChain # set the chain attribute. It works exactly the same way as redirect attribute but instead of sending an HTTP response to the browser, the parent class parses the url, instantiates the matching CGI, copies all data into it and runs it. Messages and errors are copied automatically, the parameters in the HTTP request are not, since an error caused by one of# them could propagate to the next CGI. # # If you need to keep HTTP parameters you can use the keepParam method in the parent class. It takes the name of the parameter as an argument and adds it to the list of parameters that will be copied to the new CGI if a "chain" is performed. # # # Parameters: # $chain - value for the chain attribute # # See also: # setRedirect, setErrorchain, keepParam sub setChain { my ($self, $chain) = @_; $self->{chain} = $chain; } # Method: setErrorchain # set the errorchain attribute. Sometimes you want to chain to a different CGI if there is an error, for example if the cause of the error is the absence of an input parameter necessary to show the page. If that's the case you can set the errorchain attribute, which will have a higher priority than chain and redirect if there's an error. # # Parameters: # $errorchain - value for the errorchain attribute # # See also: # setChain, setRedirect sub setErrorchain { my ($self, $errorchain) = @_; $self->{errorchain} = $errorchain; } # Method: paramsAsHash # # Returns: a reference to a hash which contains the CGI parameters and # its values as keys and values of the hash # # Possible implentation improvements: # maybe it will be good idea cache this in some field of the instance sub paramsAsHash { my ($self) = @_; my @names = @{ $self->params() }; my %params = map { my $value = $self->param($_) ; $_ => $value } @names; return \%params; } sub _validateParams { my ($self) = @_; my $params_r = $self->params(); $params_r = $self->_validateRequiredParams($params_r); $params_r = $self->_validateOptionalParams($params_r); my @paramsLeft = @{ $params_r }; if (@paramsLeft) { EBox::error("Unallowed parameters found in CGI request: @paramsLeft"); throw EBox::Exceptions::External ( __('Your request could not be processed because it had some incorrect parameters')); } return 1; } sub _validateReferer { my ($self) = @_; # Only check if the client sends params if ( ! @{$self->params()} ) { return; } my $referer = $ENV{HTTP_REFERER}; my $hostname = $ENV{HTTP_HOST}; my $rshostname = $ENV{HTTP_HOST}; # allow remoteservices proxy access # proxy is a valid subdomain of {domain} if ( EBox::Global->modExists('remoteservices') ) { my $rs = EBox::Global->modInstance('remoteservices'); if ( $rs->isConnected() ) { $rshostname = $rs->proxyDomain(); } } if ( $referer =~ m/^https:\/\/$hostname(:[0-9]*)?\// or $referer =~ m/^https:\/\/[^\/]*$rshostname(:[0-9]*)?\// ) { return; # everithing ok } throw EBox::Exceptions::External( __("Wrong HTTP referer detected, operation cancelled for security reasons")); } sub _validateRequiredParams { my ($self, $params_r) = @_; my $matchResult_r = _matchParams($self->requiredParameters(), $params_r); my @requiresWithoutMatch = @{ $matchResult_r->{targetsWithoutMatch} }; if (@requiresWithoutMatch) { EBox::error("Mandatory parameters not found in CGI request: @requiresWithoutMatch"); throw EBox::Exceptions::External ( __('Your request could not be processed because it lacked some required parameters')); } else { my $allMatches = all @{ $matchResult_r->{matches} }; my @newParams = grep { $_ ne $allMatches } @{ $params_r} ; return \@newParams; } } sub _validateOptionalParams { my ($self, $params_r) = @_; my $matchResult_r = _matchParams($self->optionalParameters(), $params_r); my $allMatches = all @{ $matchResult_r->{matches} }; my @newParams = grep { $_ ne $allMatches } @{ $params_r} ; return \@newParams; } sub _matchParams { my ($targetParams_r, $actualParams_r) = @_; my @targets = @{ $targetParams_r }; my @actualParams = @{ $actualParams_r}; my @targetsWithoutMatch; my @matchedParams; foreach my $targetParam ( @targets ) { my $targetRe = qr/^$targetParam$/; my @matched = grep { $_ =~ $targetRe } @actualParams; if (@matched) { push @matchedParams, @matched; } else { push @targetsWithoutMatch, $targetParam; } } return { matches => \@matchedParams, targetsWithoutMatch => \@targetsWithoutMatch }; } # Method: optionalParameters # # Get the optional CGI parameter list. Any # parameter that match with this list may be present or absent # in the CGI parameters. # # Returns: # # array ref - the list of matching parameters, it may be a names or # a regular expression, in the last case it cannot contain the # metacharacters ^ and $ # sub optionalParameters { return []; } # Method: requiredParameters # # Get the required CGI parameter list. Any # parameter that match with this list must be present in the CGI # parameters. # # Returns: # # array ref - the list of matching parameters, it may be a names # or a regular expression, in the last case it can not contain # the metacharacters ^ and $ # sub requiredParameters { return []; } # Method: actuate # # This method is the workhouse of the CGI it must be overriden by the different CGIs to achieve their objectives sub actuate {} # Method: masonParameters # # This method must be overriden by the different child to return # the adequate template parameter for its state. # # Returns: # a reference to a list which contains the names and values of the different mason parameters # sub masonParameters { my ($self) = @_; if (exists $self->{params}) { return $self->{params}; } return []; } # Method: upload # # Upload a file from the client computer. The file is place in # the tmp directory (/tmp) # # # Parameters: # # uploadParam - String CGI parameter name which contains the path to the # file which will be uploaded. It is usually obtained from a HTML # file input # # Returns: # # String - the path of the uploaded file # # Exceptions: # # - thrown if an error has happened # within the CGI or it is impossible to read the upload file or the # parameter does not pass on the request # # - thrown if there is no file to # upload or cannot create the temporally file # sub upload { my ($self, $uploadParam) = @_; defined $uploadParam or throw EBox::Exceptions::MissingArgument(); # upload parameter.. my $uploadParamValue = $self->cgi->param($uploadParam); if (not defined $uploadParamValue) { if ($self->cgi->cgi_error) { throw EBox::Exceptions::Internal('Upload error: ' . $self->cgi->cgi_error); } throw EBox::Exceptions::Internal("The upload parameter $uploadParam does not " . 'pass on HTTP request'); } # get upload contents file handle my $UPLOAD_FH = $self->cgi->upload($uploadParam); if (not $UPLOAD_FH) { throw EBox::Exceptions::External( __('Invalid uploaded file.')); } # destination file handle and path my ($FH, $filename) = tempfile("uploadXXXXX", DIR => EBox::Config::tmp()); if (not $FH) { throw EBox::Exceptions::External( __('Cannot create a temporally file for the upload')); } try { #copy the uploaded data to file.. my $readStatus; my $readData; my $readSize = 1024 * 8; # read in blocks of 8K while ($readStatus = read $UPLOAD_FH, $readData, $readSize) { print $FH $readData; } if (not defined $readStatus) { throw EBox::Exceptions::Internal("Error reading uploaded data: $!"); } } otherwise { my $ex = shift; unlink $filename; $ex->throw(); } finally { close $UPLOAD_FH; close $FH; }; # return the created file in tmp return $filename; } # Method: setMenuNamespace # # Set the menu namespace to help the menu code to find out # within which namespace this cgi is running. # # Note that, this is useful only if you are using base CGIs # in modules different to ebox base. If you do not use this, # the namespace used will be the one the base cgi belongs to. # # Parameters: # # (POSITIONAL) # namespace - string represeting the namespace in URL format. Example: # "EBox/Network" # sub setMenuNamespace { my ($self, $namespace) = @_; $self->{'menuNamespace'} = $namespace; } # Method: menuNamespace # # Return menu namespace to help the menu code to find out # within which namespace this cgi is running. # # Note that, this is useful only if you are using base CGIs # in modules different to ebox base. If you do not use this, # the namespace used will be the one the base cgi belongs to. # # Returns: # # namespace - string represeting the namespace in URL format. Example: # "EBox/Network" # sub menuNamespace { my ($self) = @_; if (exists $self->{'menuNamespace'}) { return $self->{'menuNamespace'}; } else { return $self->{'url'}; } } sub JSONReply { my ($self, $data_r) = @_; print$self->cgi()->header(-charset=>'utf-8', -type => 'application/JSON', ); my $error = $self->{error}; if ($error and not $data_r->{error}) { $data_r->{error} = $error; } print encode_json($data_r); } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/ClientBase.pm0000664000000000000000000000534212017102272017220 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::ClientBase; use strict; use warnings; use base 'EBox::CGI::Base'; use EBox::Gettext; use EBox::Html; ## arguments ## title [optional] ## error [optional] ## msg [optional] ## cgi [optional] ## template [optional] sub new # (title=?, error=?, msg=?, cgi=?, template=?) { my $class = shift; my %opts = @_; my $namespace = delete $opts{'namespace'}; my $htmlblocks = delete $opts{'htmlblocks'}; my $self = $class->SUPER::new(@_); my $tmp = $class; $tmp =~ s/^(.*?)::CGI::(.*?)(?:::)?(.*)//; if(not $namespace) { $namespace = $1; } if (not $htmlblocks) { $htmlblocks = $namespace . "::HtmlBlocks"; } eval "use $htmlblocks"; $self->{htmlblocks} = $htmlblocks; $self->{module} = $2; $self->{cginame} = $3; $self->{cginame} =~ s|::|/|g; if (defined($self->{cginame})) { $self->{url} = $self->{module} . "/" . $self->{cginame}; } else { $self->{url} = $self->{module} . "/Index"; } bless($self, $class); return $self; } # Method: setMenuFolder # # Set the name of the menu folder # # Parameters: # # folder - string (Positional) sub setMenuFolder { my ($self, $folder) = @_; $self->{menuFolder} = $folder; } # Method: menuFolder # # Fetch the menu folder. If it's not set it tries # to guess it from the URL # sub menuFolder { my ($self) = @_; unless ($self->{menuFolder}) { my @split = split ('/', $ENV{'script'}); if (@split) { return $split[0]; } else { return undef; } } return $self->{menuFolder}; } sub _header { my $self = shift; print($self->cgi()->header(-charset=>'utf-8')); print(EBox::Html::header($self->{title})); } sub _top { my $self = shift; print($self->{htmlblocks}->title()); } sub _topNoAction { my $self = shift; print($self->{htmlblocks}->titleNoAction()); } sub _menu { my $self = shift; print($self->{htmlblocks}->menu($self->menuFolder())); } sub _footer { my $self = shift; print($self->{htmlblocks}->footer()); } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/Logout/0000775000000000000000000000000012017102272016116 5ustar zentyal-core-2.3.21+quantal1/src/EBox/CGI/Logout/Logout.pm0000664000000000000000000000240212017102272017723 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::Logout::Logout; use strict; use warnings; use mod_perl; use base 'EBox::CGI::ClientBase'; use EBox::Gettext; use EBox::Global; use Apache2::RequestUtil; sub new { my $class = shift; my $self = $class->SUPER::new(@_); bless($self, $class); return $self; } sub _process { my ($self) = @_; my $r = Apache2::RequestUtil->request; my $auth_type = $r->auth_type; $self->{redirect} = "Login/Index"; $self->{errorchain} = "Logout/Index"; my $global = EBox::Global->getInstance(); $global->revokeAllModules; # Delete the cookie $auth_type->logout($r); } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/Logout/Index.pm0000664000000000000000000000235712017102272017532 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::Logout::Index; use strict; use warnings; use base 'EBox::CGI::ClientBase'; use EBox::Global; use EBox::Gettext; sub new # (error=?, msg=?, cgi=?) { my $class = shift; my $self = $class->SUPER::new('title' => __('Logout'), 'template' => '/logout/index.mas', @_); bless($self, $class); return $self; } sub _process { my $self = shift; my $global = EBox::Global->getInstance(); if ($global->unsaved) { my @array = (); push(@array, 'unsaved' => 'yes'); $self->{params} = \@array; } } sub _loggedIn { return 1; } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/Login/0000775000000000000000000000000012017102272015715 5ustar zentyal-core-2.3.21+quantal1/src/EBox/CGI/Login/Index.pm0000664000000000000000000000615612017102272017332 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::Login::Index; use strict; use warnings; use base 'EBox::CGI::ClientBase'; use EBox::Gettext; use Apache2::RequestUtil; use Readonly; Readonly::Scalar my $DEFAULT_DESTINATION => '/Dashboard/Index'; Readonly::Scalar my $FIRSTTIME_DESTINATION => '/Software/EBox'; sub new # (error=?, msg=?, cgi=?) { my $class = shift; my $self = $class->SUPER::new('title' => '', 'template' => '/login/index.mas', @_); bless($self, $class); return $self; } sub _print { my $self = shift; print($self->cgi()->header(-charset=>'utf-8')); $self->_body; } sub _process { my ($self) = @_; my $r = Apache2::RequestUtil->request; my $envre; my $authreason; if ($r->prev){ $envre = $r->prev->subprocess_env("LoginReason"); $authreason = $r->prev->subprocess_env('AuthCookieReason'); } my $destination = _requestDestination($r); my $reason; if ( (defined ($envre) ) and ($envre eq 'Script active') ) { $reason = __('There is a script which has asked to run in Zentyal exclusively. ' . 'Please, wait patiently until it is done'); } elsif ((defined $authreason) and ($authreason eq 'bad_credentials')){ $reason = __('Incorrect password'); } elsif ((defined $envre) and ($envre eq 'Expired')){ $reason = __('For security reasons your session ' . 'has expired due to inactivity'); }elsif ((defined $envre and $envre eq 'Already')){ $reason = __('You have been logged out because ' . 'a new session has been opened'); } my $global = EBox::Global->getInstance(); my @htmlParams = ( 'destination' => $destination, 'reason' => $reason, %{ $global->theme() } ); $self->{params} = \@htmlParams; } sub _requestDestination { my ($r) = @_; if ($r->prev) { return _requestDestination($r->prev); } my $request = $r->the_request; my $method = $r->method; my $protocol = $r->protocol; my ($destination) = ($request =~ m/$method\s*(.*?)\s*$protocol/ ); # redirect to config page on first install if (EBox::Global::first() and EBox::Global->modExists('software')) { return $FIRSTTIME_DESTINATION; } defined $destination or return $DEFAULT_DESTINATION; if ($destination =~ m{^/*Login/+Index$}) { # /Login/Index is the standard location from login, his destination must be the default destination return $DEFAULT_DESTINATION; } return $destination; } sub _top { } sub _loggedIn { return 1; } sub _menu { return; } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/ClientRawBase.pm0000664000000000000000000001333512017102272017673 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::ClientRawBase; use strict; use warnings; use base 'EBox::CGI::Base'; use EBox::Gettext; use EBox::Html; use HTML::Mason::Exceptions; use Apache2::RequestUtil; use Error qw(:try); use HTML::Mason::Exceptions; use EBox::Exceptions::DataInUse; use EBox::Exceptions::Base; use constant ERROR_STATUS => '500'; use constant DATA_IN_USE_STATUS => '501'; ## arguments ## title [optional] ## error [optional] ## msg [optional] ## cgi [optional] ## template [optional] sub new # (title=?, error=?, msg=?, cgi=?, template=?) { my $class = shift; my %opts = @_; my $self = $class->SUPER::new(@_); my $namespace = delete $opts{'namespace'}; my $tmp = $class; $tmp =~ s/^(.*?)::CGI::(.*?)(?:::)?(.*)//; if(not $namespace) { $namespace = $1; } $self->{namespace} = $namespace; $self->{module} = $2; $self->{cginame} = $3; if (defined($self->{cginame})) { $self->{url} = $self->{module} . "/" . $self->{cginame}; } else { $self->{url} = $self->{module} . "/Index"; } bless($self, $class); return $self; } sub _title { } sub _header { my $self = shift; print($self->cgi()->header(-charset=>'utf-8')); } sub _footer { } sub _menu { } sub _print { my $self = shift; my $json = $self->{json}; if ($json) { $self->JSONReply($json); return; } $self->_header(); $self->_body(); } sub _print_error { my ($self, $text) = @_; $text or return; ($text ne "") or return; # We send a ERROR_STATUS code. This is necessary in order to trigger # onFailure functions on Ajax code my $r = Apache2::RequestUtil->request(); my $filename = EBox::Config::templates . '/error.mas'; my $output; my $interp = HTML::Mason::Interp->new(comp_root => EBox::Config::templates, out_method => \$output); my $comp = $interp->make_component(comp_file => $filename); my @params = (); push(@params, 'error' => $text); $interp->exec($comp, @params); $r->status(ERROR_STATUS); $r->subprocess_env('suppress-error-charset' => 1) ; $r->custom_response(ERROR_STATUS, $output); } sub _print_warning { my ($self, $text) = @_; $text or return; ($text ne "") or return; # We send a WARNING_STATUS code. my $r = Apache2::RequestUtil->request(); $r->status(DATA_IN_USE_STATUS); $r->custom_response(DATA_IN_USE_STATUS, ""); my $filename = EBox::Config::templates . '/dataInUse.mas'; my $output; my $interp = HTML::Mason::Interp->new(comp_root => EBox::Config::templates, out_method => \$output); my $comp = $interp->make_component(comp_file => $filename); my @params = (); push(@params, 'warning' => $text); push(@params, 'url' => _requestURL()); push(@params, 'params' => $self->paramsAsHash()); $interp->exec($comp, @params); $r->status(DATA_IN_USE_STATUS); $r->subprocess_env('suppress-error-charset' => 1) ; $r->custom_response(DATA_IN_USE_STATUS, $output); } # TODO Refactor this stuff as it's used in the auth process too sub _requestURL { my $r = Apache2::RequestUtil->request(); return unless($r); my $request = $r->the_request(); my $method = $r->method(); my $protocol = $r->protocol(); my ($destination) = ($request =~ m/$method\s*(.*?)\s*$protocol/ ); return $destination; } sub run { my $self = shift; my $finish = 0; if (not $self->_loggedIn) { $self->{redirect} = "/Login/Index"; } else { try { $self->_validateReferer(); $self->_process(); } catch EBox::Exceptions::DataInUse with { my $e = shift; if ($self->{json}) { $self->setErrorFromException($e); } else { $self->_print_warning($e->text()); } $finish = 1; } otherwise { my $e = shift; $self->setErrorFromException($e); if (not $self->{json}) { $self->_error(); } $finish = 1; }; } if ($self->{json}) { $self->JSONReply($self->{json}); return; } return if ($finish == 1); try { $self->_print; } catch EBox::Exceptions::Internal with { my $ex = shift; $self->_print_error($ex->stacktrace()); } otherwise { my $ex = shift; my $logger = EBox::logger; if (isa_mason_exception($ex)) { $logger->error($ex->as_text); my $error = __("An internal error related to ". "a template has occurred. This is ". "a bug, relevant information can ". "be found in the logs."); $self->_print_error($error); } else { if ($ex->can('text')) { $logger->error('Exception: ' . $ex->text()); } else { $logger->error("Unknown exception"); } throw $ex; } }; } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/Controller/0000775000000000000000000000000012017102272016770 5ustar zentyal-core-2.3.21+quantal1/src/EBox/CGI/Controller/Composite.pm0000664000000000000000000000441012017102272021267 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::CGI::Controller::Composite # # This CGI is the composite controller. That is, it determines # what to do after a call to get an action performed. # # It inherits from , so the returning # HTML just includes what Viewer method returns # package EBox::CGI::Controller::Composite; use base 'EBox::CGI::ClientRawBase'; use strict; use warnings; use EBox::Gettext; # Constructor: new # # The CGI constructor. # # Parameters: # # the parent parameters # # composite - the composite model to # take the action and show it # # action - String the action to be performed # # - Named parameters # # Returns: # # - the recently created CGI # sub new { my ($class, %params) = @_; my $composite = delete $params{composite}; my $self = $class->SUPER::new('template' => $composite->Viewer(), @_); $self->{composite} = $composite; $self->{action} = delete $params{action}; bless($self, $class); return $self; } sub _process { my ($self) = @_; $self->{params} = $self->masonParameters(); } # Method: masonParameters # # Overrides # sub masonParameters { my ($self) = @_; if ( $self->{action} eq 'changeView' ) { my $global = EBox::Global->getInstance(); return [ model => $self->{composite}, hasChanged => $global->unsaved(), ]; } else { return []; } } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/Controller/Modal.pm0000664000000000000000000002053612017102272020370 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::Controller::Modal; use strict; use warnings; use base 'EBox::CGI::ClientRawBase'; use EBox::Gettext; use EBox::Global; use EBox::Exceptions::NotImplemented; use EBox::Exceptions::Internal; # Dependencies use Error qw(:try); sub new # (cgi=?) { my $class = shift; my %params = @_; my $tableModel = delete $params{'tableModel'}; my $self = $class->SUPER::new(@_); $self->{'tableModel'} = $tableModel; bless($self, $class); return $self; } sub getParams { my ($self) = @_; my $tableDesc = $self->{'tableModel'}->table()->{'tableDescription'}; my %params; foreach my $field (@{$tableDesc}) { foreach my $fieldName ($field->fields()) { my $value; if ( $field->allowUnsafeChars() ) { $value = $self->unsafeParam($fieldName); } else { $value = $self->param($fieldName); } # TODO Review code to see if we are actually checking # types which are not optional $params{$fieldName} = $value; } } $params{'id'} = $self->param('id'); $params{'filter'} = $self->param('filter'); return %params; } sub addRow { my $self = shift; my $model = $self->{'tableModel'}; $model->addRow($self->getParams()); } sub moveRow { my $self = shift; my $model = $self->{'tableModel'}; $self->_requireParam('id'); $self->_requireParam('dir'); my $id = $self->param('id'); my $dir = $self->param('dir'); if ($dir eq 'up') { $model->moveUp($id); } else { $model->moveDown($id); } } sub removeRow { my $self = shift; my $model = $self->{'tableModel'}; $self->_requireParam('id'); my $id = $self->param('id'); my $force = $self->param('force'); $model->removeRow($id, $force); } sub editField { my $self = shift; my $model = $self->{'tableModel'}; my %params = $self->getParams(); my $force = $self->param('force'); $model->setRow($force, %params); my $editField = $self->param('editfield'); if (not $editField) { return; } my $tableDesc = $self->{'tableModel'}->table()->{'tableDescription'}; foreach my $field (@{$tableDesc}) { my $fieldName = $field->{'fieldName'}; if ($editField ne $fieldName) { next; } my $fieldType = $field->{'type'}; if ($fieldType eq 'text' or $fieldType eq 'int') { $self->{'to_print'} = $params{$fieldName}; } } } sub editBoolean { my $self = shift; my $model = $self->{'tableModel'}; my $id = $self->param('id'); my $field = $self->param('field'); my $value = 0; if ($self->param('value')) { $value = 1; } my $currentRow = $model->row($id); my $element = $currentRow->elementByName($field); $element->setValue($value); $model->setTypedRow( $id, { $field => $element}, force => 1, readOnly => 0); $model->popMessage(); my $global = EBox::Global->getInstance(); # XXX Factor this class to be able to print 'application/json' # and 'text/html' headers. This way we could just return # a json object { changes_menu: true } and get it evaled # using prototype. That's the right way :) if ($global->unsaved()) { $self->_responseToEnableChangesMenuElement(); } } sub customAction { my ($self, $action) = @_; my $model = $self->{'tableModel'}; my %params = $self->getParams(); my $id = $params{id}; my $customAction = $model->customActions($action, $id); $customAction->handle($id, %params); } # Method to refresh the table by calling rows method sub refreshTable { my ($self, $showTable, $action, @extraParams) = @_; my $model = $self->{'tableModel'}; my $global = EBox::Global->getInstance(); my $rows = undef; my $editId; if ($action eq 'clone') { $editId = $self->param('id'); } else { $editId = $self->param('editid'); } my $page = $self->param('page'); my $pageSize = $self->param('pageSize'); if ( defined $pageSize) { $model->setPageSize($pageSize); } my $filter = $self->param('filter'); if (not defined $filter) { $filter = ''; } my $selectCallerId = $self->param('selectCallerId'); my $tpages = 1000; $self->{template} = $model->modalViewer($showTable); my @params = ( 'data' => $rows, 'dataTable' => $model->table(), 'model' => $model, 'action' => $action, 'editid' => $editId, 'hasChanged' => $global->unsaved(), 'filter' => $filter, 'page' => $page, 'tpages' => $tpages, ); if ($selectCallerId) { push @params, (selectCallerId => $selectCallerId); } push @params, @extraParams; $self->{'params'} = \@params; } sub cancelAdd { my ($self, $model) = @_; my %params = $self->getParams(); $self->{json} = { callParams => \%params, success => 0, }; my $parent = $model->parent(); my $id = $model->parentRow()->id(); $parent->removeRow($id); $self->{json}->{success} = 1; $self->{json}->{rowId} = $id; } # Group: Protected methods sub _process { my ($self) = @_; $self->_requireParam('action'); my $action = $self->param('action'); my $firstShow = $self->param('firstShow'); my $selectCallerId = $self->param('selectCallerId'); my $selectForeignField = $self->param('selectForeignField'); my $nextPageContextName = $self->param('nextPageContextName'); my $foreignNextPageField = $self->param('foreignNextPageField'); my $directory = $self->param('directory'); my $model = $self->{'tableModel'}; if ($directory) { $model->setDirectory($directory); } if ($action eq 'edit') { $self->editField(); $self->refreshTable(1, $action); } elsif ($action eq 'add') { $self->addRow(); $self->refreshTable(1, $action); } elsif ($action eq 'del') { $self->removeRow(); $self->refreshTable(1, $action); } elsif ($action eq 'move') { $self->moveRow(); $self->refreshTable(1, $action); } elsif ($action eq 'changeAdd') { my $showTable = not $firstShow; my @extraParams; if ($selectCallerId and $firstShow) { @extraParams = ( selectForeignField => $selectForeignField, foreignNextPageField => $foreignNextPageField, nextPageContextName => $nextPageContextName, ); } $self->setMsg(''); $self->refreshTable($showTable, $action, @extraParams); } elsif ($action eq 'changeList') { $self->refreshTable(1, $action); } elsif ($action eq 'changeEdit') { $self->refreshTable(1, $action); } elsif ($action eq 'clone') { $self->refreshTable(1, $action); } elsif ($action eq 'view') { # This action will show the whole table (including the # table header similarly View Base CGI but inheriting # from ClientRawBase instead of ClientBase $self->refreshTable(1, $action); } elsif ($action eq 'editBoolean') { delete $self->{template}; $self->editBoolean(); } elsif ($action eq 'viewAndAdd') { $self->refreshTable(1, $action); } elsif ($action eq 'cancelAdd') { $self->cancelAdd($model); # } elsif ($model->customActions($action, $self->param('id'))) { # $self->customAction($action); # $self->refreshTable(); } else { throw EBox::Exceptions::Internal("Action '$action' not supported"); } } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/Controller/DataTableAdd.pm0000664000000000000000000000217212017102272021562 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::Controller::DataTableAdd; use strict; use warnings; use base 'EBox::CGI::Controller::DataTable'; use EBox::Gettext; use EBox::Global; sub new # (cgi=?) { my $class = shift; my %params = @_; my $tableModel = $params{'tableModel'}; my $self = $class->SUPER::new(@_); $self->{'tableModel'} = $tableModel; bless($self, $class); return $self; } sub _process { my $self = shift; $self->addRow(); $self->refreshTable(); } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/Controller/DataTable.pm0000664000000000000000000003260012017102272021150 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::Controller::DataTable; use strict; use warnings; use base 'EBox::CGI::ClientRawBase'; use EBox::Gettext; use EBox::Global; use EBox::Exceptions::NotImplemented; use EBox::Exceptions::Internal; # Dependencies use Error qw(:try); sub new # (cgi=?) { my $class = shift; my %params = @_; my $tableModel = delete $params{'tableModel'}; my $template; if (defined($tableModel)) { $template = $tableModel->Viewer(); } my $self = $class->SUPER::new('template' => $template, @_); $self->{'tableModel'} = $tableModel; bless($self, $class); return $self; } sub getParams { my $self = shift; my $tableDesc = $self->{'tableModel'}->table()->{'tableDescription'}; my %params; foreach my $field (@{$tableDesc}) { foreach my $fieldName ($field->fields()) { my $value; if ( $field->allowUnsafeChars() ) { $value = $self->unsafeParam($fieldName); } else { $value = $self->param($fieldName); } # TODO Review code to see if we are actually checking # types which are not optional $params{$fieldName} = $value; } } $params{'id'} = $self->unsafeParam('id'); $params{'filter'} = $self->param('filter'); my $cloneId = $self->unsafeParam('cloneId'); if ($cloneId) { $params{cloneId} = $cloneId; } return %params; } sub _auditLog { my ($self, $event, $id, $value, $oldValue) = @_; unless (defined $self->{audit}) { $self->{audit} = EBox::Global->modInstance('audit'); } return unless $self->{audit}->isEnabled(); my $model = $self->{tableModel}; $value = '' unless defined $value; $oldValue = '' unless defined $oldValue; my ($rowId, $elementId) = split (/\//, $id); $elementId = $rowId unless defined ($elementId); my $row = $model->row($rowId); if (defined ($row)) { my $element = $row->hashElements()->{$elementId}; my $type; if (defined ($element)) { $type = $element->type(); } if ($type and ($type eq 'boolean')) { $value = $value ? 1 : 0; $oldValue = ($oldValue ? 1 : 0) if ($event eq 'set'); } elsif (($type and ($type eq 'password')) or ($elementId eq 'password')) { $value = '****' if $value; $oldValue = '****' if $oldValue; } } $self->{audit}->logModelAction($model, $event, $id, $value, $oldValue); } sub addRow { my ($self) = @_; my $model = $self->{'tableModel'}; my %params = $self->getParams(); if ($self->{json}) { $self->{json}->{callParams} = \%params; } my $id = $model->addRow(%params); my $cloneId =delete $params{cloneId}; if ($cloneId) { my $newRow = $model->row($id); my $clonedRow = $model->row($cloneId); $newRow->cloneSubModelsFrom($clonedRow); } my $auditId = $self->_getAuditId($id); # We don't want to include filter in the audit log # as it has no value (it's a function reference) my %fields = map { $_ => 1 } @{ $model->fields() }; delete $params{'filter'}; foreach my $fieldName (keys %params) { my $value = $params{$fieldName}; if ((not defined $value)) { # skip undef parameter which are not a field $fields{$fieldName} or next; # for boolean types undef means false my $instance = $model->fieldHeader($fieldName); $instance->isa('EBox::Types::Boolean') or next; } $self->_auditLog('add', "$auditId/$fieldName", $value); } return $id; } sub moveRow { my $self = shift; my $model = $self->{'tableModel'}; $self->_requireParam('id'); $self->_requireParam('dir'); my $id = $self->unsafeParam('id'); my $dir = $self->param('dir'); my $before = $model->_rowOrder($id); if ($dir eq 'up') { $model->moveUp($id); } else { $model->moveDown($id); } my $after = $model->_rowOrder($id); $self->_auditLog('move', $self->_getAuditId($id), $before, $after); } sub removeRow { my $self = shift; my $model = $self->{'tableModel'}; $self->_requireParam('id'); my $id = $self->unsafeParam('id'); my $force = $self->param('force'); $model->removeRow($id, $force); $self->_auditLog('del', $self->_getAuditId($id)); } sub editField { my $self = shift; my $model = $self->{'tableModel'}; my %params = $self->getParams(); my $force = $self->param('force'); my $tableDesc = $model->table()->{'tableDescription'}; my $id = $params{id}; my $row = $model->row($id); my $auditId = $self->_getAuditId($id); # Store old and new values before setting the row for audit log my %changedValues; for my $field (@{$tableDesc} ) { my $fieldName = $field->fieldName(); unless ($field->isa('EBox::Types::Boolean')) { next unless defined $params{$fieldName}; } my $newValue = $params{$fieldName}; my $oldValue = $row->valueByName($fieldName); next if ($newValue eq $oldValue); $changedValues{$fieldName} = { id => $id ? "$auditId/$fieldName" : $fieldName, new => $newValue, old => $oldValue, }; } $model->setRow($force, %params); for my $fieldName (keys %changedValues) { my $value = $changedValues{$fieldName}; $self->_auditLog('set', $value->{id}, $value->{new}, $value->{old}); } my $editField = $self->param('editfield'); if (not $editField) { return; } foreach my $field (@{$tableDesc}) { my $fieldName = $field->{'fieldName'}; if ($editField ne $fieldName) { next; } my $fieldType = $field->{'type'}; if ($fieldType eq 'text' or $fieldType eq 'int') { $self->{'to_print'} = $params{$fieldName}; } } } sub editBoolean { my ($self) = @_; my $model = $self->{'tableModel'}; my $id = $self->unsafeParam('id'); my $field = $self->param('field'); my $value = 0; if ($self->param('value')) { $value = 1; } my $currentRow = $model->row($id); my $oldValue = $currentRow->valueByName($field); my $element = $currentRow->elementByName($field); $element->setValue($value); $model->setTypedRow( $id, { $field => $element}, force => 1, readOnly => 0); $model->popMessage(); my $global = EBox::Global->getInstance(); # XXX Factor this class to be able to print 'application/json' # and 'text/html' headers. This way we could just return # a json object { changes_menu: true } and get it evaled # using prototype. That's the right way :) if ($global->unsaved()) { $self->_responseToEnableChangesMenuElement(); } my $auditId = $self->_getAuditId($id); $self->_auditLog('set', "$id/$field", $value, $oldValue); } sub setAllChecks { my ($self, $value) = @_; my $model = $self->{'tableModel'}; my $field = $self->param('editid'); $model->setAll($field, $value); } sub checkAllControlValueAction { my ($self) = @_; my $model = $self->{'tableModel'}; my $field = $self->param('field'); my $value = $model->checkAllControlValue($field) ? 1 : 0; $self->{json} = { success => $value }; } # prints a HTML response to enable the 'Save changes' web element # don't p[ritn any other HTML if you use this sub _responseToEnableChangesMenuElement { my ($self) = @_; $self->_header(); print '$("changes_menu").className = "changed"'; } sub customAction { my ($self, $action) = @_; my $model = $self->{'tableModel'}; my %params = $self->getParams(); my $id = $params{id}; my $customAction = $model->customActions($action, $id); $customAction->handle($id, %params); $self->_auditLog('action', $id, $action); } # Method to refresh the table by calling rows method sub refreshTable { my $self = shift; my $model = $self->{'tableModel'}; my $global = EBox::Global->getInstance(); my $action = $self->{'action'}; my $filter = $self->param('filter'); my $page = $self->param('page'); my $pageSize = $self->param('pageSize'); if ( defined ( $pageSize )) { $model->setPageSize($pageSize); } my $editId; if ($action eq 'clone') { $editId = $self->param('id'); } else { $editId = $self->param('editid'); } my $rows = undef; my $tpages = 1000; my @params; push(@params, 'data' => $rows); push(@params, 'dataTable' => $model->table()); push(@params, 'model' => $model); push(@params, 'action' => $action); push(@params, 'editid' => $editId); push(@params, 'hasChanged' => $global->unsaved()); push(@params, 'filter' => $filter); push(@params, 'page' => $page); push(@params, 'tpages' => $tpages); $self->{'params'} = \@params; } sub editAction { my ($self) = @_; $self->editField(); $self->refreshTable(); } sub addAction { my ($self, %params) = @_; my $rowId = $self->addRow(); if ($params{json}) { $self->{json}->{rowId} = $rowId; $self->{json}->{directory} = $params{directory}; $self->{json}->{success} = 1; } else { $self->refreshTable(); } } sub delAction { my ($self) = @_; $self->removeRow(); $self->refreshTable(); } sub moveAction { my ($self) = @_; $self->moveRow(); $self->refreshTable(); } sub changeAddAction { my ($self) = @_; $self->refreshTable(); } sub changeListAction { my ($self) = @_; $self->refreshTable(); } sub changeEditAction { my ($self) = @_; $self->refreshTable(); } # This action will show the whole table (including the # table header similarly View Base CGI but inheriting # from ClientRawBase instead of ClientBase sub viewAction { my ($self, %params) = @_; $self->{template} = $params{model}->Viewer(); $self->refreshTable(); } sub editBooleanAction { my ($self) = @_; delete $self->{template}; $self->editBoolean(); } sub cloneAction { my ($self) = @_; $self->refreshTable(); } sub checkboxSetAllAction { my ($self) = @_; $self->setAllChecks(1); $self->refreshTable(); } sub checkboxUnsetAllAction { my ($self) = @_; $self->setAllChecks(0); $self->refreshTable(); } sub confirmationDialogAction { my ($self, %params) = @_; my $actionToConfirm = $self->param('actionToConfirm'); my %confirmParams = $self->getParams(); my $res = $params{model}->_confirmationDialogForAction($actionToConfirm, \%confirmParams); my $msg; my $title = ''; if (ref $res) { $msg = $res->{message}; $title = $res->{title}; defined $title or $title = ''; } else { $msg = $res; } $self->{json} = { wantDialog => $msg ? 1 : 0, message => $msg, title => $title }; } # Group: Protected methods sub _process { my $self = shift; $self->_requireParam('action'); my $action = $self->param('action'); $self->{'action'} = $action; my $model = $self->{'tableModel'}; my $directory = $self->param('directory'); if ($directory) { $model->setDirectory($directory); } my $json = $self->param('json'); if ($json) { $self->{json} = { success => 0 }; } my $actionSub = $action . 'Action'; if ($self->can($actionSub)) { $self->$actionSub( model => $model, directory => $directory, json => $json, ); } elsif ($model->customActions($action, $self->unsafeParam('id'))) { $self->customAction($action); $self->refreshTable() } else { throw EBox::Exceptions::Internal("Action '$action' not supported"); } # json mode should not put messages in UI if ($self->{json}) { $model->setMessage(''); } } sub _redirect { my $self = shift; my $model = $self->{'tableModel'}; return unless (defined($model)); return $model->popRedirection(); } # TODO: Move this function to the proper place sub _printRedirect { my $self = shift; my $url = $self->_redirect(); return unless (defined($url)); print ""; } sub _print { my $self = shift; $self->SUPER::_print(); unless ($self->{json}) { $self->_printRedirect; } } sub _getAuditId { my ($self, $id) = @_; # Get parentRow id if any my $row = $self->{'tableModel'}->row($id); if (defined $row) { my $parentRow = $row->parentRow(); if ($parentRow) { return $parentRow->id() . "/$id"; } } return $id; } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/Controller/DataTableMove.pm0000664000000000000000000000217412017102272022002 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::Controller::DataTableMove; use strict; use warnings; use base 'EBox::CGI::Controller::DataTable'; use EBox::Gettext; use EBox::Global; sub new # (cgi=?) { my $class = shift; my %params = @_; my $tableModel = $params{'tableModel'}; my $self = $class->SUPER::new(@_); $self->{'tableModel'} = $tableModel; bless($self, $class); return $self; } sub _process { my $self = shift; $self->moveRow(); $self->refreshTable(); } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/Controller/DataMultiTable.pm0000664000000000000000000000200012017102272022152 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::Controller::DataMultiTable; use strict; use warnings; use base 'EBox::CGI::ClientRawBase'; use EBox::Gettext; use EBox::Global; sub new # (cgi=?) { my $class = shift; my $self = $class->SUPER::new('template' => '/ajax/tableBody.mas', @_); bless($self, $class); return $self; } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/Controller/Downloader/0000775000000000000000000000000012017102272021066 5ustar zentyal-core-2.3.21+quantal1/src/EBox/CGI/Controller/Downloader/FromTempDir.pm0000664000000000000000000000611412017102272023616 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::CGI::Controller::Downloader::FromTempDir # # This class is a subclass of # to download files from EBox::Config::tmp() # package EBox::CGI::Controller::Downloader::FromTempDir; use strict; use warnings; use base 'EBox::CGI::Controller::Downloader::Base'; use EBox::Gettext; use EBox::Global; use EBox::Config; use EBox::Exceptions::NotImplemented; use EBox::Exceptions::MissingArgument; use EBox::Exceptions::Internal; # Core modules use Error qw(:try); use Cwd 'abs_path'; # Group: Public methods # Constructor: new # # Create a # # Exceptions: # # - If filename is not passsed # - If file can't be read or is an invalid path # sub new # (cgi=?) { my ($class, %params) = @_; my $self = $class->SUPER::new(@_); bless($self, $class); return $self; } # Group: Protected methods # Method: _path # # This method must be overriden by subclasses to return the path # of the file to download # # Exceptions: # # - thrown if this method # is not implemented by the subclass sub _path { my ($self) = @_; return $self->{path}; } # Method: _process # # Overrides: # # # # Exceptions: # # - thrown if the field name is not # contained in the given model # sub _process { my ($self) = @_; $self->_requireParam('filename'); my $filename = $self->param('filename'); my $downloadDir = EBox::Config::downloads(); my $path = $downloadDir . $filename; my $normalized = abs_path($path); unless ($normalized) { throw EBox::Exceptions::Internal("Path $path cannot be normalized"); } unless ($normalized =~ /^$downloadDir/) { throw EBox::Exceptions::Internal("$normalized is not a valid path"); } unless (-r $normalized) { throw EBox::Exceptions::Internal("$normalized can't be read"); } $self->{path} = $normalized; $self->SUPER::_process(@_); } # Method: _print # # Overrides: # # # # To remove the file after it has been downloaded # sub _print { my ($self, @params) = @_; $self->SUPER::_print(@params); unlink ($self->{path}) if ( -e $self->{path} ); } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/Controller/Downloader/FromModel.pm0000664000000000000000000000602112017102272023307 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::CGI::Controller::Downloader::FromModel # # This class is a subclass of # to download files from a model. # # It will need the following parameters via CGI: # # model - model name # dir - directory to set the model to # id - row's id # field - the name of a type # package EBox::CGI::Controller::Downloader::FromModel; use strict; use warnings; use base 'EBox::CGI::Controller::Downloader::Base'; use EBox::Gettext; use EBox::Global; use EBox::Config; use EBox::Exceptions::Internal; use EBox::Model::Manager; # Core modules use Error qw(:try); # Group: Public methods # Constructor: new # # Create a # # Exceptions: # # sub new # (cgi=?) { my ($class, %params) = @_; my $self = $class->SUPER::new(@_); bless($self, $class); return $self; } # Group: Protected methods # Method: _path # # This method must be overriden by subclasses to return the path # of the file to download # # Exceptions: # # - thrown if this method # is not implemented by the subclass sub _path { my ($self) = @_; return $self->{path}; } # Method: _process # # Overrides: # # # # Exceptions: # - If file can't be read or is an invalid path # - thrown if the field name is not # contained in the given model # sub _process { my ($self) = @_; $self->_requireParam('model'); $self->_requireParam('dir'); $self->_requireParam('id'); $self->_requireParam('field'); my $modelName = $self->param('model'); my $dir = $self->param('dir'); my $id = $self->param('id'); my $field = $self->param('field'); my $model = EBox::Model::Manager->instance()->model($modelName); $model->setDirectory($dir); my $row = $model->row($id); my $type = $row->elementByName($field); unless (defined($type) and $type->isa('EBox::Types::File')) { throw EBox::Exceptions::Internal("field $field is not a File type"); } unless ($type->exist()) { throw EBox::Exceptions::Internal( "field $field does not contain a file"); } $self->{path} = $type->path(); $self->SUPER::_process(@_); } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/Controller/Downloader/Base.pm0000664000000000000000000000630712017102272022304 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::CGI::Controller::Downloader::Base # # This is the base cgi to implement controllers to download files # package EBox::CGI::Controller::Downloader::Base; use strict; use warnings; use base 'EBox::CGI::ClientRawBase'; use EBox::Gettext; use EBox::Global; use EBox::Exceptions::NotImplemented; # Core modules use File::Basename; use Error qw(:try); # Dependencies use File::MMagic; # Group: Public methods # Constructor: new # # Create a # # sub new # (cgi=?) { my ($class, %params) = @_; my $self = $class->SUPER::new(@_); bless($self, $class); return $self; } # Group: Protected methods # Method: _path # # This method must be overriden by subclasses to return the path # of the file to download # # Exceptions: # # - thrown if this method # is not implemented by the subclass sub _path { throw EBox::Exceptions::NotImplemented; } # Method: _process # # Overrides: # # # # Exceptions: # # - thrown if the field name is not # contained in the given model # sub _process { my ($self) = @_; my $path = $self->_path(); # Setting the file $self->{downfile} = $path; # Setting the file name $self->{downfilename} = fileparse($path); } # Method: _print # # Overrides: # # # sub _print { my ($self) = @_; if ( $self->{error} or not defined($self->{downfile})) { $self->SUPER::_print(); return; } my $file = $self->{downfile}; if (-r $file) { my $mm = new File::MMagic(); my $mimeType = $mm->checktype_filename($file); my $size = -s $file; $self->_printHeader($mimeType, $size); open( my $downFile, '<', $file) or throw EBox::Exceptions::Internal('Could open file ' . $self->{downfile} . " $!"); # Efficient way to print a whole file print do { local $/; <$downFile> }; close($downFile); } else { throw EBox::Exceptions::Internal('File does not exist or is of a special type: ' . $file); } } sub _printHeader { my ($self, $mimeType, $size) = @_; print($self->cgi()->header(-type => $mimeType, -attachment => $self->{downfilename}, -Content_length => (-s $self->{downfile})), ); } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/Controller/DataTableUpdate.pm0000664000000000000000000000237312017102272022317 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::Controller::DataTableUpdate; use strict; use warnings; use base 'EBox::CGI::Controller::DataTable'; use EBox::Gettext; use EBox::Global; sub new # (cgi=?) { my $class = shift; my %params = @_; my $tableModel = $params{'tableModel'}; my $self = $class->SUPER::new(@_); $self->{'tableModel'} = $tableModel; bless($self, $class); return $self; } sub _process { my $self = shift; $self->editAction(); } sub _print { my $self = shift; if ($self->{'to_print'}) { print($self->cgi()->header(-charset=>'utf-8')); print $self->{'to_print'}; } } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/Controller/Uploader.pm0000664000000000000000000000371412017102272021106 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::Controller::Uploader; use strict; use warnings; use base 'EBox::CGI::ClientRawBase'; use EBox::Gettext; use EBox::Global; use EBox::Exceptions::NotImplemented; # Core modules use Error qw(:try); use File::Basename; use File::Copy; # Group: Public methods sub new # (cgi=?) { my $class = shift; my %params = @_; my $self = $class->SUPER::new(@_); bless($self, $class); return $self; } # Group: Protected methods # Method: _process # # Upload a file which is defined by a single parameter. The file # is stored in directory with base name # equals to the base name from the user path. # # Overrides: # # # sub _process { my $self = shift; my $params = $self->params(); my $filePathParam = $params->[0]; my $uploadedFile = $self->upload($filePathParam); my ($baseTmp, $tmpDir) = fileparse($uploadedFile); # Remove the model name to get just the field name $filePathParam =~ s/^.*?_//g; # Rename to the user-defined file name move($uploadedFile, $tmpDir . $filePathParam) or throw EBox::Exceptions::Internal("Cannot move $uploadedFile to " . $tmpDir . $filePathParam . " $!"); } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/Controller/DataTableDel.pm0000664000000000000000000000217612017102272021602 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::Controller::DataTableDel; use strict; use warnings; use base 'EBox::CGI::Controller::DataTable'; use EBox::Gettext; use EBox::Global; sub new # (cgi=?) { my $class = shift; my %params = @_; my $tableModel = $params{'tableModel'}; my $self = $class->SUPER::new(@_); $self->{'tableModel'} = $tableModel; bless($self, $class); return $self; } sub _process { my $self = shift; $self->removeRow(); $self->refreshTable(); } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/Logs/0000775000000000000000000000000012017102272015551 5ustar zentyal-core-2.3.21+quantal1/src/EBox/CGI/Logs/Index.pm0000664000000000000000000002065512017102272017166 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::Logs::Index; use strict; use warnings; use base 'EBox::CGI::ClientBase'; use EBox; use EBox::Gettext; use EBox::Global; use EBox::Model::Manager; use EBox::Html; use POSIX qw(ceil); use constant PAGESIZE => 15; sub new # (error=?, msg=?, cgi=?) { my $class = shift; my $self = $class->SUPER::new('title' => __('Logs'), 'template' => '/logs/index.mas', @_); bless($self, $class); return $self; } sub _actualPage { my ($self, $tpages) = @_; my $page = $self->param('page'); unless (defined($self->param('page'))) { $page = 0; } if (defined($self->param('tofirst'))) { $page = 0; } if (defined($self->param('toprev'))) { if ($page > 0) { $page = $page -1; } } if (defined($self->param('tonext'))) { if ($page < $tpages) { $page = $page + 1; } } if (defined($self->param('tolast'))) { $page = $tpages; } return $page; } sub addToMasonParameters { my ($self, @masonParams) = @_; defined $self->{params} or $self->{params} = []; my $oldParams_r= $self->{params}; if (defined $oldParams_r) { push @masonParams, @{ $oldParams_r }; } $self->{params} = \@masonParams; } sub _fromDate { my ($self) = @_; my $defaultPeriod = -24*3600; # one day my $fromDate = $self->_getDateArray('from', $defaultPeriod); return $fromDate; } sub _toDate { my ($self) = @_; my $toDate; my $refresh = $self->refresh(); if ($refresh) { # 86400 second -> one day $toDate = $self->_getDateArray('to', 86400, 0); } else { $toDate = $self->_getDateArray('to'); } return $toDate; } sub _getDateArray { my ($self, $prefix, $defaultTimeAdjust, $useParamsValue) = @_; defined $defaultTimeAdjust or $defaultTimeAdjust = 0; defined $useParamsValue or $useParamsValue = 1; my %time; my @localtime = localtime((time() + $defaultTimeAdjust)); $time{$prefix . 'sec'} = $localtime[0]; $time{$prefix . 'min'} = $localtime[1]; $time{$prefix . 'hour'} = $localtime[2]; $time{$prefix . 'day'} = $localtime[3]; $time{$prefix . 'month'} = $localtime[4] + 1; $time{$prefix . 'year'} = $localtime[5] + 1900; if ($useParamsValue) { foreach my $key (keys %time) { my $paramValue = $self->param($key); if (defined $paramValue) { $time{$key} = $paramValue; } } } my @wantedOrder = map { $prefix . $_ } qw(day month year hour min sec) ; my @dateArray = map { $time{$_} } @wantedOrder; return \@dateArray; } sub _searchLogs { my ($self, $logs, $selected) = @_; my %hret; my $hfilters; my $tableinfo = $logs->getTableInfo($selected); my $table = $tableinfo->{'tablename'}; my $timecol = 'timestamp'; my $tpages = ceil($logs->totalRecords($table) / PAGESIZE) - 1; my $page = $self->_actualPage($tpages); my @fromdate = @{ $self->_fromDate() }; my @todate = @{ $self->_toDate() }; $hfilters = $self->_paramFilters(); %hret = %{$logs->search($fromdate[2].'-'.$fromdate[1].'-'.$fromdate[0].' '.$fromdate[3].':'.$fromdate[4].':0', $todate[2].'-'.$todate[1].'-'.$todate[0].' '.$todate[3].':'.$todate[4].':0', $selected, PAGESIZE, $page, $timecol, $hfilters)}; $tpages = ceil ($hret{'totalret'} / PAGESIZE) -1; $page = $self->_actualPage($tpages); my @masonParameters; push(@masonParameters, 'filters' => _encode_filters($hfilters)); push(@masonParameters, 'tableinfo' => $tableinfo); push(@masonParameters, 'page' => $page); push(@masonParameters, 'tpages' => $tpages); push(@masonParameters, 'data' => $hret{'arrayret'}); push(@masonParameters, 'fromdate' => \@fromdate); push(@masonParameters, 'todate' => \@todate); $self->addToMasonParameters(@masonParameters); } sub _encode_filters { my ($par) = @_; my %encoded = map { $par->{$_} =~ s/'/'/g; $_ => $par->{$_} } keys %{$par}; return \%encoded; } # Method called when the user may want to save the query as an event # to be notified sub _saveAsEvent { my ($self) = @_; # Get filters my $hfilters = $self->_paramFilters(); my $selected = $self->param('selected'); # TODO Use new row/ids API my $manager = EBox::Model::Manager->instance(); my $logConfModel = $manager->model('events/LogWatcherConfiguration'); my $loggerConfRow = $logConfModel->findValue(domain => $selected); my $logFilteringDirectory = $loggerConfRow->{filters}->{directory}; my $modelName = "LogWatcherFiltering_$selected"; my $url = "Events/View/$modelName"; my $params = "?action=presetUpdate&tablename=$modelName&directory=$logFilteringDirectory&page=0&filter=&pagesize=10"; while (my ($key, $value) = each %{$hfilters}) { if ( $key eq 'event' and not $value) { $value = 'any'; } # Do not pass the empty values '' next unless ( $value ); $params .= "&$key=$value"; } $self->setRedirect( $url . $params ); return; } # Function to get the filters from CGI parameters # Return an hash ref indexed by filter's name sub _paramFilters { my ($self) = @_; my $hfilters = {}; foreach my $filter (grep(s/^filter-//, @{$self->params()})) { $hfilters->{$filter} = $self->unsafeParam("filter-$filter"); } return $hfilters; } sub _header { my ($self) = @_; if (not $self->refresh()) { $self->SUPER::_header(); return; } my $destination = "/Logs/Index?"; my %params = %{ $self->paramsAsHash() }; $params{refresh} = 1; # to assure the refresh parameter is active while (my ($param, $value) = each %params) { if ($param eq 'View') { # View we want to only use it the first time to set default refresh # as 1 next; } $destination .= "$param=$value&"; } $destination =~ s/&$//; my $global = EBox::Global->getInstance(); my $favicon = $global->theme()->{'favicon'}; print($self->cgi()->header(-charset=>'utf-8')); my $html = EBox::Html::makeHtml( 'headerWithRefresh.mas', title => $self->{title}, destination => $destination, favicon => $favicon, ); print $html; } sub refresh { my ($self) = @_; return 1 if $self->param('refresh'); return 1 if $self->param('View'); return 0; } sub _process { my ($self) = @_; my $logs = EBox::Global->modInstance('logs'); # The user may click on saveAsEvent if ( $self->param('saveAsEvent') ) { $self->_saveAsEvent(); return; } my $selected = $self->param('selected'); if (defined($selected)) { $self->_searchLogs($logs, $selected); } else { $selected = 'none'; } $self->{crumbs} = [ { title => __('Query Logs'), link => '/Maintenance/Logs' }, { title => __('Full Reports'), link => "/Logs/Index?selected=$selected&refresh=1" }, ]; my @masonParameters; push(@masonParameters, 'logdomains' => $logs->getLogDomains()); push(@masonParameters, 'selected' => $selected); push(@masonParameters, refresh => $self->refresh); $self->addToMasonParameters(@masonParameters); } sub menuFolder { return 'Maintenance'; } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/Wizard.pm0000664000000000000000000000457412017102272016455 0ustar # Copyright (C) 2010-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::Wizard; use strict; use warnings; use base 'EBox::CGI::ClientBase'; use EBox::Global; use EBox::Gettext; sub new # (error=?, msg=?, cgi=?) { my $class = shift; my $self = $class->SUPER::new('title' => __('Initial configuration wizard'), 'template' => 'wizard.mas', @_); bless($self, $class); return $self; } sub _process { my $self = shift; my @array = (); my $global = EBox::Global->getInstance(); my $image = $global->theme()->{'image_title'}; push (@array, image_title => $image); if ($self->param('page')) { push(@array, 'pages' => [ $self->param('page') ]); } else { push(@array, 'pages' => $self->_modulesWizardPages); } push(@array, 'first' => EBox::Global->first()); $self->{params} = \@array; } # Method: _modulesWizardPages # # Returns an array ref with installed modules wizard pages sub _modulesWizardPages { my $global = EBox::Global->getInstance(); my @pages = (); my @modules = @{$global->modInstancesOfType('EBox::Module::Service')}; foreach my $module ( @modules ) { if ($module->firstInstall()) { push (@pages, @{$module->wizardPages()}); } } # Sort and get pages my @sortedPages = sort { $a->{order} <=> $b->{order} } @pages; @sortedPages = map { $_->{page} } @sortedPages; return \@sortedPages; } sub _menu { my ($self) = @_; if (EBox::Global->first() and EBox::Global->modExists('software')) { my $software = EBox::Global->modInstance('software'); $software->firstTimeMenu(3); } else { $self->SUPER::_menu(@_); } } sub _top { my ($self)= @_; $self->_topNoAction(); } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/ServiceModule/0000775000000000000000000000000012017102272017413 5ustar zentyal-core-2.3.21+quantal1/src/EBox/CGI/ServiceModule/Controller.pm0000664000000000000000000000301212017102272022070 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # package EBox::CGI::ServiceModule::Controller # # This class is to gather the files which have been accepted to modify # by the user # package EBox::CGI::ServiceModule::Controller; use strict; use warnings; use base 'EBox::CGI::ClientBase'; use EBox::ServiceManager; use EBox::Global; use EBox::Gettext; ## arguments: ## title [required] sub new { my $class = shift; my $self = $class->SUPER::new( @_); bless($self, $class); return $self; } sub _process { my ($self) = @_; my $manager = new EBox::ServiceManager(); my @accepted; my @rejected; if ($self->param('acceptedFiles')) { @accepted = $self->param('acceptedFiles'); } if ($self->param('rejectedFiles')) { @rejected = $self->param('rejectedFiles'); } $manager->setAcceptedFiles(\@accepted, \@rejected); } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/ServiceModule/StatusView.pm0000664000000000000000000000271612017102272022075 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # package EBox::CGI::ServiceModule::StatusView # # This class is used to list the status of the modules # package EBox::CGI::ServiceModule::StatusView; use strict; use warnings; use base 'EBox::CGI::ClientBase'; use EBox::ServiceManager; use EBox::Global; use EBox::Gettext; ## arguments: ## title [required] sub new { my $class = shift; my $self = $class->SUPER::new( 'title' => __('Module Status Configuration'), 'template' => '/moduleStatus.mas', @_); bless($self, $class); return $self; } sub _process { my ($self) = @_; my $manager = new EBox::ServiceManager(); my $modules = $manager->moduleStatus(); my @params; push @params, (modules => $modules); $self->{params} = \@params; } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/ServiceModule/ConfigureModuleController.pm0000664000000000000000000000406212017102272025106 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # package EBox::CGI::ServiceModule::ConfigureModuleController # # This class is used as a controller to receive the green light # from users to configure which is needed to enable a module # package EBox::CGI::ServiceModule::ConfigureModuleController; use strict; use warnings; use base 'EBox::CGI::ClientBase'; use EBox::ServiceManager; use EBox::Global; use EBox::Gettext; use Error qw(:try); use EBox::Exceptions::Base; ## arguments: ## title [required] sub new { my $class = shift; my $self = $class->SUPER::new( @_); bless($self, $class); return $self; } sub _process { my ($self) = @_; $self->_requireParam('module'); my $modName = $self->param('module'); my $manager = new EBox::ServiceManager(); my $module = EBox::Global->modInstance($modName); try { $module->enableActions(); $module->setConfigured(1); $module->enableService(1); } otherwise { my ($excep) = @_; $module->setConfigured(undef); $module->enableService(undef); if ($excep->isa("EBox::Exceptions::External")) { throw EBox::Exceptions::External("Failed to enable: " . $excep->stringify()); } else { throw EBox::Exceptions::Internal("Failed to enable: " . $excep->stringify()); } }; $self->{redirect} = "ServiceModule/StatusView"; } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/ServiceModule/StatusController.pm0000664000000000000000000000406412017102272023304 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # package EBox::CGI::ServiceModule::StatusController # # This class is to gather those modules which the user wants to enable. # It takes care of disabling/enabling dependencies # package EBox::CGI::ServiceModule::StatusController; use strict; use warnings; use base 'EBox::CGI::ClientRawBase'; use EBox::ServiceManager; use EBox::Global; use EBox::Gettext; ## arguments: ## title [required] sub new { my $class = shift; my $self = $class->SUPER::new('template' => '/moduleStatusTable.mas', @_); bless($self, $class); return $self; } sub _process { my ($self) = @_; my %modules; for my $mod (@{$self->params()}) { my @params = $self->param($mod); my $enabled = undef; if (@params > 1) { $enabled = 1; } $modules{$mod} = $enabled; } # use Data::Dumper; # EBox::debug(Dumper \%modules); my $manager = new EBox::ServiceManager(); $manager->enableServices(\%modules); my $modules = $manager->moduleStatus(); my @params; push @params, (modules => $modules, hasChanged => EBox::Global->getInstance()->unsaved()); $self->{params} = \@params; } sub _print { my $self = shift; if ($self->{'to_print'}) { print($self->cgi()->header(-charset=>'utf-8')); print $self->{'to_print'}; } else { $self->SUPER::_print(); } } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/ServiceModule/ConfigureView.pm0000664000000000000000000000340112017102272022523 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # package EBox::CGI::ServiceModule::ConfigureView # # This class is used to list the actions and file modifications # that eBox needs to do to enable the module # package EBox::CGI::ServiceModule::ConfigureView; use strict; use warnings; use base 'EBox::CGI::ClientRawBase'; use EBox::ServiceManager; use EBox::Global; use EBox::Gettext; ## arguments: ## title [required] sub new { my $class = shift; my $self = $class->SUPER::new( 'template' => '/configureView.mas', @_); bless($self, $class); return $self; } sub _process { my ($self) = @_; my $mod = $self->param('module'); my $modInstance = EBox::Global->modInstance($mod); my @params; push (@params, (files => $modInstance->usedFiles(), actions => $modInstance->actions(), module => $mod)); $self->{params} = \@params; } sub _print { my $self = shift; if ($self->{'to_print'}) { print($self->cgi()->header(-charset=>'utf-8')); print $self->{'to_print'}; } else { $self->SUPER::_print(); } } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/ServiceModule/View.pm0000664000000000000000000000320212017102272020660 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # package EBox::CGI::ServiceModule::View # # This class is to used to warn and prompt the user about the # configuration files which are going to be modified # package EBox::CGI::ServiceModule::View; use strict; use warnings; use base 'EBox::CGI::ClientRawBase'; use EBox::ServiceManager; use EBox::Global; use EBox::Gettext; ## arguments: ## title [required] sub new { my $class = shift; my $self = $class->SUPER::new( 'template' => '/configurationFiles.mas', @_); bless($self, $class); return $self; } sub _process { my ($self) = @_; my $manager = new EBox::ServiceManager(); #my $files = $manager->checkFiles(); my @params; push @params, (files => $files); $self->{params} = \@params; } sub _print { my $self = shift; if ($self->{'to_print'}) { print($self->cgi()->header(-charset=>'utf-8')); print $self->{'to_print'}; } else { $self->SUPER::_print(); } } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/MenuCSS.pm0000664000000000000000000000202012017102272016452 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::CGI::MenuCSS; use strict; use warnings; sub new { my $class = shift; my $self = {}; bless($self, $class); return $self; } sub domain { return "ebox"; } sub run { my $self = shift; my $cgi = new CGI; my $module = $cgi->param('section'); print $cgi->header('text/css'); print "#nav .menu$module { display: block;}\n"; } 1; zentyal-core-2.3.21+quantal1/src/EBox/CGI/Progress.pm0000664000000000000000000001077712017102272017023 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # package EBox::CGI::Progress # # This class is to used to show the progress of a long operation # # This CGI is not intended to be caled directly, any CGI whom wants to switch # to a progress view must inherit from ProgressClient and call to the method showProgress package EBox::CGI::Progress; use strict; use warnings; use base 'EBox::CGI::ClientBase'; use EBox::Global; use EBox::Config; use EBox::Gettext; use Encode; use File::Slurp; ## arguments: ## title [required] sub new { my $class = shift; my $self = $class->SUPER::new('template' => '/progress.mas', @_); bless($self, $class); return $self; } sub _process { my ($self) = @_; my @params = (); push @params, (progressId => $self->_progressId); my $title = ($self->param('title')); if ($title) { $self->{title} = encode (utf8 => $title); } my @paramsNames = qw( text currentItemCaption itemsLeftMessage endNote errorNote reloadInterval currentItemUrl inModalbox nextStepType nextStepUrl nextStepText nextStepTimeout nextStepUrlOnclick nextStepUrlFailureOnclick ); foreach my $name (@paramsNames) { # We use unsafeParam because these paramaters can be i18'ed. # Also, these parameters are only used to generate html, no command # or so is run. use Encode; my $value = encode (utf8 => $self->unsafeParam($name)); $value or next; push @params, ($name => $value); } if (EBox::Global->first()) { my $software = EBox::Global->modInstance('software'); # FIXME: workaround to show ads only during installation unless ( $self->{title} and encode(utf8 => __('Saving changes')) eq $self->{title} ) { push @params, ( adsJson => loadAds() ); } } $self->{params} = \@params; } sub _progressId { my ($self) = @_; my $pId = $self->param('progress'); $pId or throw EBox::Exceptions::Internal('No progress indicator id supplied'); return $pId; } # to avoid the
in raw mode sub _print { my ($self) = @_; if (not $self->param('raw')) { return $self->SUPER::_print(); } return $self->_printPopup(); } sub _menu { my ($self) = @_; if ($self->param('raw')) { return; } if (EBox::Global->first() and EBox::Global->modExists('software')) { my $software = EBox::Global->modInstance('software'); # FIXME: workaround to show distinct menu for saving changes and installation proccess if ( $self->{title} and encode(utf8 => __('Saving changes')) eq $self->{title} ) { $software->firstTimeMenu(4); } else { $software->firstTimeMenu(2); } } else { $self->SUPER::_menu(@_); } } sub _top { my ($self) = @_; if ($self->param('raw')) { return; } my $global = EBox::Global->getInstance(); my $img = $global->theme()->{'image_title'}; print "
"; return; } sub _footer { my ($self) = @_; if ($self->param('raw')) { return; } return $self->SUPER::_footer(); } sub loadAds { my $path = EBox::Config::share() . 'zentyal-software/ads'; my $file = "$path/ads_" + EBox::locale(); unless (-f $file) { $file = "$path/ads_" . substr (EBox::locale(), 0, 2); unless (-f $file) { $file = "$path/ads_en"; } } if (-f "$file.custom") { $file = "$file.custom"; } EBox::debug("Loading ads from: $file"); my @ads = read_file($file) or throw EBox::Exceptions::Internal("Error loading ads: $!"); my $text = ''; foreach my $line (@ads) { $text .= $line . "\n"; } return $text; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Backup.pm0000664000000000000000000013111112017102272016004 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Backup; use strict; use warnings; use EBox::Config; use EBox::Global; use EBox::Exceptions::Internal; use EBox::Exceptions::MissingArgument; use EBox::Gettext; use EBox::FileSystem; use EBox::ProgressIndicator; use File::Temp qw(tempdir); use File::Copy qw(copy move); use File::Slurp qw(read_file write_file); use File::Basename; use Error qw(:try); use Digest::MD5; use EBox::Sudo; use POSIX qw(strftime); use DirHandle; use Perl6::Junction qw(any all); use Filesys::Df; use Readonly; Readonly::Scalar our $FULL_BACKUP_ID => 'full backup'; Readonly::Scalar our $CONFIGURATION_BACKUP_ID =>'configuration backup'; Readonly::Scalar our $BUGREPORT_BACKUP_ID =>'bugreport configuration dump'; my $RECURSIVE_DEPENDENCY_THRESHOLD = 20; use constant REQUIRED_ZENTYAL_VERSION => '2.1'; sub new { my $class = shift; my $self = {}; bless($self, $class); return $self; } # returns: # string: path to the backup file. sub _makeBackup { my ($self, %options) = @_; my $description = delete $options{description}; my $time = delete $options{time}; my $bug = $options{bug}; my $progress = $options{progress}; my $changesSaved = $options{changesSaved}; my $date = strftime("%F %T", localtime($time)); my $confdir = EBox::Config::conf; my $tempdir = tempdir("$confdir/backup.XXXXXX") or throw EBox::Exceptions::Internal("Could not create tempdir."); EBox::Sudo::command("chmod 0700 $tempdir"); my $auxDir = "$tempdir/aux"; my $archiveContentsDirRelative = "eboxbackup"; my $archiveContentsDir = "$tempdir/$archiveContentsDirRelative"; my $backupArchive = "$confdir/eboxbackup.tar"; try { mkdir($auxDir, 0700) or throw EBox::Exceptions::Internal("Could not create auxiliar tempdir."); mkdir($archiveContentsDir, 0700) or throw EBox::Exceptions::Internal("Could not create archive tempdir."); $self->_dumpModulesBackupData($auxDir, %options); if ($bug) { $self->_bug($auxDir); } if ($progress) { $progress->setMessage(__('Creating backup archive')); $progress->notifyTick(); } my $filesArchive = "$archiveContentsDir/files.tgz"; $self->_createFilesArchive($auxDir, $filesArchive); $self->_createZentyalConfFilesArchive($archiveContentsDir); $self->_createMd5DigestForArchive($filesArchive, $archiveContentsDir); $self->_createDescriptionFile($archiveContentsDir, $description); $self->_createDateFile($archiveContentsDir, $date); $self->_createTypeFile($archiveContentsDir, $bug); $self->_createModulesListFile($archiveContentsDir); system "dpkg -l > $archiveContentsDir/debpackages"; $self->_createPartitionsFile($archiveContentsDir); copy ('/etc/fstab', "$archiveContentsDir/fstab"); $self->_createSizeFile($archiveContentsDir); $self->_createBackupArchive($backupArchive, $tempdir, $archiveContentsDirRelative); } finally { system "rm -rf '$tempdir'"; if ($? != 0) { EBox::error("$auxDir cannot be deleted: $!. Please do it manually"); } }; return $backupArchive; } sub _dumpModulesBackupData { my ($self, $auxDir, %options) = @_; my $progress = $options{progress}; my $changesSaved = $options{changesSaved}; my @modules = @{ $self->_modInstancesForBackup($changesSaved) }; foreach my $mod (@modules) { my $modName = $mod->name(); if ($progress) { # update progress object $progress->notifyTick(); $progress->setMessage(__x('Dumping configuration of module {m}', m => $modName)); } try { EBox::debug("Dumping $modName backup data"); $mod->makeBackup($auxDir, %options); } catch EBox::Exceptions::Base with { my $ex = shift; throw EBox::Exceptions::Internal($ex->text); }; } } sub _modInstancesForBackup { my ($self, $changesSaved) = @_; my $readOnly = $changesSaved ? 0 : 1; my @mods = @{ $self->_configuredModInstances($changesSaved) }; return \@mods; } sub _configuredModInstances { my ($self, $readOnly) = @_; defined $readOnly or $readOnly = 0; my $global = EBox::Global->getInstance($readOnly); my @modules = @{ $global->modInstances() }; my @configuredModules; foreach my $mod (@modules) { if ($mod->can('configured')) { if ($mod->configured()) { push @configuredModules, $mod; } } else { push @configuredModules, $mod; } } # leave aside not configured modules # @modules = grep { # # (not $_->isa('EBox::Module::Service') or # # ($_->configured())) # $_->configured() # } @modules; return \@configuredModules; } sub _createFilesArchive { my ($self, $auxDir, $filesArchive, $removeDir) = @_; defined $removeDir or $removeDir = 1; EBox::Sudo::root("tar czf '$filesArchive' --preserve-permissions -C '$auxDir' ."); EBox::Sudo::root("chmod 0660 '$filesArchive'"); EBox::Sudo::root("chown ebox.ebox '$filesArchive'"); if ($removeDir) { system "rm -rf '$auxDir'"; } } sub _createDateFile { my ($self, $archiveContentsDir, $time) = @_; my $DATE; unless (open($DATE, "> $archiveContentsDir/date")) { throw EBox::Exceptions::Internal ("Could not create date file."); } print $DATE $time ; close($DATE); } sub _createDescriptionFile { my ($self, $archiveContentsDir, $description) = @_; my $DESC; unless (open($DESC, "> $archiveContentsDir/description")) { throw EBox::Exceptions::Internal ("Could not create description file."); } print $DESC $description; close($DESC); } sub _createPartitionsFile { my ($self, $archiveContentsDir) = @_; my $path = "$archiveContentsDir/partitions"; my $PARTS; unless (open($PARTS, "> $path")) { throw EBox::Exceptions::Internal ("Could not create partitions info file."); } my $partitionsOutput = EBox::Sudo::root('fdisk -l'); foreach my $line (@{$partitionsOutput}) { print $PARTS $line; } close $PARTS or throw EBox::Exceptions::Internal ("Error writing partitions info file."); } sub _createTypeFile { my ($self, $archiveContentsDir, $bug) = @_; my $type = $bug ? $BUGREPORT_BACKUP_ID : $CONFIGURATION_BACKUP_ID; my $TYPE_F; unless (open($TYPE_F, "> $archiveContentsDir/type")) { throw EBox::Exceptions::Internal ("Could not create type file."); } print $TYPE_F $type; close($TYPE_F); } sub _createMd5DigestForArchive { my ($self, $filesArchive, $archiveContentsDir) = @_; my $ARCHIVE; unless (open($ARCHIVE, $filesArchive)) { throw EBox::Exceptions::Internal("Could not open files archive."); } my $md5 = Digest::MD5->new; $md5->addfile($ARCHIVE); my $digest = $md5->hexdigest; close($ARCHIVE); my $MD5; unless (open($MD5, "> $archiveContentsDir/md5sum")) { throw EBox::Exceptions::Internal("Could not open md5 file."); } print $MD5 $digest; close($MD5); } sub _createModulesListFile { my ($self, $archiveContentsDir) = @_; my @mods = @{ $self->_modInstancesForBackup() }; my @modNames = map { $_->name } @mods; my $file = "$archiveContentsDir/modules"; write_file($file, "@modNames"); } sub _createBackupArchive { my ($self, $backupArchive, $tempdir, $archiveContentsDirRelative) = @_; if ( -f $backupArchive) { if (`rm -f $backupArchive`) { throw EBox::Exceptions::Internal ("Could not delete old file."); } } my $filesArchive = "$archiveContentsDirRelative/files.tgz"; EBox::Sudo::root("tar cf $backupArchive -C $tempdir $archiveContentsDirRelative --preserve-permissions --exclude $filesArchive 2>&1"); # append filesArchive EBox::Sudo::root("tar --append -f '$backupArchive' -C '$tempdir' '$filesArchive' 2>&1"); # adjust permissions and ownership given to ebox EBox::Sudo::root("chmod 0660 '$backupArchive'"); EBox::Sudo::root("chown ebox.ebox '$backupArchive'"); } sub _createSizeFile { my ($self, $archiveContentsDir) = @_; my $size; my $duCommand = "du -b -s -c --block-size=1024 $archiveContentsDir"; my $output = EBox::Sudo::command($duCommand); my ($totalLine) = grep { m/total/ } @{ $output }; ($size) = split '\s', $totalLine; my $sizeFile = "$archiveContentsDir/size"; write_file($sizeFile, $size) } sub _createZentyalConfFilesArchive { my ($self, $backupDir) = @_; my $archive = "$backupDir/etcFiles.tgz"; my $etcDir = EBox::Config::etc(); $self->_createFilesArchive($etcDir, $archive, 0); } sub _bug { my ($self, $dir) = @_; system "/bin/ps aux > $dir/processes"; system "/bin/df -k > $dir/disks"; system "ip link show > $dir/links"; system "ip route list table all > $dir/routes"; my $sockets = EBox::Sudo::root("/bin/netstat -n -a --inet -p" ); File::Slurp::write_file("$dir/sockets", $sockets); system "/sbin/ifconfig -a > $dir/interfaces"; system "cp /etc/resolv.conf $dir/resolv.conf"; try { EBox::Sudo::root("/sbin/iptables -nvL > $dir/iptables-filter", "/sbin/iptables -t nat -nvL > $dir/iptables-nat"); } catch EBox::Exceptions::Base with {}; my $eboxLogDir = EBox::Config::log(); # copy files from ebox logs directories... my @dirs = `find $eboxLogDir -maxdepth 1 -type d`; foreach my $subdir (@dirs) { chomp $subdir; my $newSubDir = $dir .'/' . basename($subdir) . '.log.d'; (-d $newSubDir) or mkdir $newSubDir; system "cp -r $subdir/*.log $newSubDir"; } copy("/var/log/syslog", "$dir/syslog"); copy("/var/log/messages", "$dir/messages"); copy("/var/log/daemon.log", "$dir/daemon.log"); copy("/var/log/auth.log", "$dir/auth.log"); copy("/var/log/mail.log", "$dir/mail.log"); copy("/etc/apt/sources.list", "$dir/sources.list"); } # Method: backupDetails # # Gathers the information for a given backup # # Parameters: # # id - backup's identifier # # Returns: # # A hash reference with the details. This hash consists of: # # file - the filename of the archive # id - backup's identifier # date - when it was backed up # type - the type of backup # description - backup's description # sub backupDetails # (id) { my ($self, $id) = @_; defined $id or throw EBox::Exceptions::MissingArgument('id'); defined $self or throw EBox::Exceptions::MissingArgument('self'); $self->_checkId($id); my $file = $self->_backupFileById($id); my $details = $self->backupDetailsFromArchive($file); $details->{id} = $id; return $details; } # Method: backupDetailsFromArchive # # Gathers the details of the backup stored in a given file # # # Parameters: # archive - the path to the archive file # # # Returns: # # A hash reference with the details. This hash consists of: # # file - the filename of the archive # date - when it was backed up # description - backup's description # type - the type of backup contained by the archive sub backupDetailsFromArchive { my ($self, $archive) = @_; defined $archive or throw EBox::Exceptions::MissingArgument('archive'); defined $self or throw EBox::Exceptions::MissingArgument('self'); my $backupDetails = {}; my @details = qw(date description type); my $tempDir = $self->_unpackArchive($archive, @details); foreach my $detail (@details) { my $FH; unless (open($FH, "$tempDir/eboxbackup/$detail")) { $backupDetails->{$detail} = __('Unknown'); next; } my $value = <$FH>; $backupDetails->{$detail} = $value; close $FH; } $backupDetails->{file} = $archive; $backupDetails->{size} = $self->_printableSize($archive); system "rm -rf '$tempDir'"; return $backupDetails; } sub _printableSize { my ($self, $archive) = @_; my $size = (-s $archive); my @units = qw(KB MB GB); foreach my $unit (@units) { $size = sprintf ("%.2f", $size / 1024); if ($size < 1024) { return "$size $unit"; } } return $size . ' ' . (pop @units); } # if not specific files are specified all the fiels are extracted sub _unpackArchive { my ($self, $archive, @files) = @_; ($archive) or throw EBox::Exceptions::External('No backup archive provided.'); my $tempDir = tempdir(EBox::Config::tmp . "/backup.XXXXXX") or throw EBox::Exceptions::Internal("Could not create tempdir."); EBox::Sudo::command("chmod 0700 $tempDir"); my $filesWithPath = @files > 0 ? join ' ', map { q{'eboxbackup/} . $_ . q{'} } @files : ''; try { my $tarCommand = "/bin/tar xf '$archive' --same-owner --same-permissions -C '$tempDir' $filesWithPath"; EBox::Sudo::root($tarCommand); } otherwise { my $ex = shift; EBox::Sudo::silentRoot("rm -rf '$tempDir'"); if (@files > 0) { throw EBox::Exceptions::External( __x("Could not extract the requested backup files: {files}", files => "@files")); } else { throw EBox::Exceptions::External( __("Could not unpack the backup")); } }; return $tempDir; } # Method: deleteBackup # # Romoves a stored backup # # Parameters: # # id - backup's identifier # # Exceptions: # # External - If it can't be found or deleted. sub deleteBackup { my ($self, $id) = @_; defined $id or throw EBox::Exceptions::MissingArgument('id'); defined $self or throw EBox::Exceptions::MissingArgument('self'); $self->_checkId($id); my $file = $self->_backupFileById($id); unless (unlink($file)) { throw EBox::Exceptions::External("Could not delete the backup"); } } # Method: listBackups # # Returns a list with the availible backups stored in the system. # # Parameters: # # id - backup's identifier # # Returns: # # A a ref to an array of hashes. Each hash reference consists of: # # id - backup's identifier # date - when it was backed up # description - backup's description # type - type of backup (full or configuration only) # sub listBackups { my ($self) = @_; my $backupdir = backupDir(); my $bh = new DirHandle($backupdir); my @backups = (); my $backup; ($bh) or return \@backups; while (defined($backup = $bh->read)) { (-f "$backupdir/$backup") or next; my $isTar = $backup =~ s/\.tar$//; $isTar or next; my $entry = undef; try { $entry = $self->backupDetails($backup); } catch EBox::Exceptions::Base with {}; unless ($entry) { EBox::info("File $backupdir.$backup.tar is in backup directorty and is not a backup file"); next; } push(@backups, $entry); } undef $bh; my @ret = sort {$a->{date} lt $b->{date}} @backups; return \@ret; } # # Procedure: backupDir # # Returns: # the directory used by ebox to store the backup archives # # sub backupDir { my $backupdir = EBox::Config::conf . '/backups'; return $backupdir; } sub _ensureBackupdirExistence { my $backupdir = backupDir(); unless (-d $backupdir) { mkdir($backupdir, 0700) or throw EBox::Exceptions::Internal ("Could not create backupdir."); } } # Method: prepareMakeBackup # # Prepares a backup restauration # # Parameters: # # description - backup's description (default: 'Backup') # bug - whether this backup is intended for a bug report # (one consequence of this is that we must clear # private data) # # remoteBackup -- whether this a backup intended to be a remote backup # # Returns: # progress indicator object of the operation # # Exceptions: # # External - If it can't unpack de backup # sub prepareMakeBackup { my ($self, %options) = @_; my $scriptParams = ''; if ( $options{remoteBackup} ) { $scriptParams .= ' --remote-backup '; # Make sure remote backup name is scaped $scriptParams .= q{'} . $options{remoteBackup} . q{'}; } if (exists $options{description}) { $scriptParams .= ' --description '; # make sure description is scaped $scriptParams .= q{'} . $options{description} . q{'}; } if (exists $options{fallbackToRO} and $options{fallbackToRO}) { $scriptParams .= ' --fallback-to-ro'; } $scriptParams .= ' --config-backup'; if ($options{bug}) { $scriptParams .= ' --bug-report'; } my $makeBackupScript = EBox::Config::scripts() . 'make-backup'; $makeBackupScript .= $scriptParams; my $global = EBox::Global->getInstance(); # XXX: this could be wrong, we only do backup of the configured modules my $totalTicks = scalar @{ $global->modNames() } + 2; # there are one task for # each module plus two # tasks for writing the # archive file my @progressIndicatorParams = (executable => $makeBackupScript, totalTicks => $totalTicks); my $progressIndicator = EBox::ProgressIndicator->create( @progressIndicatorParams ); $progressIndicator->runExecutable(); return $progressIndicator; } # Method: makeBackup # # Backups the current configuration # # Parameters: # # progress - progress indicator # associated with this operation (optional) # description - backup's description (default: 'Backup') # bug - whether this backup is intended for a bug report # (one consequence of this is that we must clear # private data) # fallbackToRO - fallback to read-only configuration when # they are not saved changes # # Returns: # - path to the new backup archive # # Exceptions: # # Internal - If backup fails # External - If modules have unsaved changes sub makeBackup { my ($self, %options) = @_; exists $options{bug} or $options{bug} = 0; exists $options{fallbackToRO} or $options{fallbackToRO} = 0; $options{description} or $options{description} = __('Backup'); my $progress = $options{progress}; EBox::info('Backing up configuration'); if ($progress and not $progress->started()) { throw EBox::Exceptions::Internal("ProgressIndicator's executable has not been run"); } my $backupdir = backupDir(); my $time = time(); $options{time} = $time; my $filename; try { my $changesSaved = $self->_changesSaved($options{fallbackToRO}); _ensureBackupdirExistence(); $filename = $self->_makeBackup(%options, changesSaved => $changesSaved); } otherwise { my $ex = shift @_; $progress->setAsFinished(1, $ex->text) if $progress; $ex->throw(); }; my $backupFinalPath; try { if ($progress) { $progress->notifyTick(); $progress->setMessage(__('Writing backup file to hard disk')); } my $dest = $options{destination}; if (not defined $dest) { $dest = $self->_destinationFromTime($time); } $backupFinalPath = $self->_moveToArchives($filename, $backupdir, $dest); $progress->setAsFinished() if $progress; } otherwise { my $ex = shift @_; $progress->setAsFinished(1, $ex->text) if $progress; $ex->throw(); }; return $backupFinalPath; } sub _changesSaved { my ($self, $fallbackToRO) = @_; my @changedMods; my $global = EBox::Global->getInstance(); foreach my $modName (@{ $global->modNames() }) { if ($global->modIsChanged($modName)) { push @changedMods, $modName; } } if (@changedMods) { if ($fallbackToRO) { EBox::warn("The following modules have unsaved changes: @changedMods. A backup of the last saved configuration will be made instead"); return 0; } else { throw EBox::Exceptions::External( __x('The following modules have unsaved changes: {mods}. Before doing the backup you must save or discard them', mods => join (', ', @changedMods)) ); } } return 1; } sub _destinationFromTime { my ($self, $time) = @_; my $str = strftime("%Y-%m-%d-%H%M%S", localtime($time)); return $str . '.tar'; } sub _moveToArchives { my ($self, $filename, $backupdir, $dest) = @_; move($filename, "$backupdir/$dest") or throw EBox::Exceptions::Internal("Could not save the backup."); return "$backupdir/$dest"; } # Method: makeBugReport # # Makes a bug report # sub makeBugReport { my ($self) = @_; return $self->_makeBackup(description => 'Bug report', 'bug' => 1); } # unpacks a backup file into a temporary directory and verifies the md5sum # arguments: # string: backup file # returns: # string: path to the temporary directory sub _unpackAndVerify { my ($self, $archive, $fullRestore, %options) = @_; ($archive) or throw EBox::Exceptions::External('No backup file provided.'); my $tempdir; try { # unless (copy($file, "$tempdir/eboxbackup.tar")) { # throw EBox::Exceptions::Internal("Could not copy backup into ". # "the tempdir."); # } $tempdir = $self->_unpackArchive($archive); unless (-f "$tempdir/eboxbackup/files.tgz" && -f "$tempdir/eboxbackup/md5sum") { throw EBox::Exceptions::External( __('Incorrect or corrupt backup file')); } $self->_checkArchiveMd5Sum($tempdir); $self->_checkArchiveType($tempdir, $fullRestore); unless ($options{forceZentyalVersion}) { $self->_checkZentyalVersion($tempdir); } } otherwise { my $ex = shift; if (defined $tempdir) { EBox::Sudo::silentRoot("rm -rf '$tempdir'"); } $ex->throw(); }; return $tempdir; } sub _checkArchiveMd5Sum { my ($self, $tempdir) = @_; my $archiveFile = "$tempdir/eboxbackup/files.tgz"; my $ARCHIVE; unless (open($ARCHIVE, $archiveFile)) { EBox::error("Cannot open archive file $archiveFile"); throw EBox::Exceptions::External(__("The backup file is corrupt: could not open archive")); } my $md5 = Digest::MD5->new; $md5->addfile($ARCHIVE); my $digest = $md5->hexdigest; close($ARCHIVE); my $md5File = "$tempdir/eboxbackup/md5sum"; my $MD5; unless (open($MD5, $md5File)) { EBox::error("Could not open the md5sum $md5File"); throw EBox::Exceptions::External(__("The backup file is corrupt: could not open backup checksum")); } my $olddigest = <$MD5>; close($MD5); if ($digest ne $olddigest) { throw EBox::Exceptions::External( __('The backup file is corrupt.')); } } sub _checkArchiveType { my ($self, $tempdir, $fullRestore) = @_; my $typeFile = "$tempdir/eboxbackup/type"; my $TYPE_F; unless (open($TYPE_F, $typeFile )) { EBox::error("Cannot open type file: $typeFile"); throw EBox::Exceptions::External("The backup file is corrupt. Backup type information not found"); } my $type = <$TYPE_F>; close($TYPE_F); if ($type ne all($FULL_BACKUP_ID, $CONFIGURATION_BACKUP_ID, $BUGREPORT_BACKUP_ID)) { throw EBox::Exceptions::External(__("The backup archive has a invalid type. Maybe the file is corrupt or you are using a incompatible Zentyal version")); } if ($fullRestore) { if ($type ne $FULL_BACKUP_ID) { throw EBox::Exceptions::External(__('The archive does not contain a full backup, that made a full restore impossibe. A configuration recovery may be possible')); } } } sub _checkSize { my ($self, $archive) = @_; my $size; my $freeSpace; my $safetyFactor = 2; # to be sure we have space left we multiply the backup # size by this number. The value was guessed, so change # it if you have better judgment my $tempDir; try { $tempDir = $self->_unpackArchive($archive, 'size'); $size = read_file("$tempDir/eboxbackup/size"); # unit -> 1K } finally { if (defined $tempDir) { system("rm -rf '$tempDir'"); ($? == 0) or EBox::warn("Unable to remove $tempDir. Please do it manually"); } }; my $backupDir = $self->backupDir(); $freeSpace = df($backupDir, 1024)->{bfree}; if ($freeSpace < ($size*$safetyFactor)) { throw EBox::Exceptions::External(__x("There in not enough space left in the hard disk to complete the restore proccess. {size} Kb required. Free sufficient space and retry", size => $size)); } } sub _checkZentyalVersion { my ($self, $tempDir)= @_; my $file = "$tempDir/eboxbackup/debpackages" ; if (not -r $file) { throw EBox::Exceptions::External(__x( 'No debian packages list file found; probably the backup was done in a incompatible Zentyal version. Only backups done in Zentyal >= {v} can be restored', v => REQUIRED_ZENTYAL_VERSION ) ); } my $zentyalVersion; open my $FH, '<', $file or throw EBox::Exceptions::Internal("Opening $file: $!"); while (my $line = <$FH>) { if ($line =~ m/ii\s+zentyal-core\s+(.*?)\s/) { $zentyalVersion = $1; last; } } close $FH or throw EBox::Exceptions::Internal("Opening $file: $!"); if (not $zentyalVersion) { throw EBox::Exceptions::External(__x( 'No zentyal-core found in the debian packages list form the backup; probably the backup was done in a incompatible Zentyal version. Only backups done in Zentyal >= {v} can be restored', v => REQUIRED_ZENTYAL_VERSION ) ); } my ($major, $minor, $rest) = split '\.', $zentyalVersion; my ($wantedMajor, $wantedMinor, $wantedRest) = split '\.', REQUIRED_ZENTYAL_VERSION; my $versionOk = 0; if ($major > $wantedMajor ) { $versionOk = 1; } elsif (($major == $wantedMajor) and ($minor >= $wantedMinor)) { $versionOk = 1; } if (not $versionOk) { throw EBox::Exceptions::External(__x( 'Could not restore the backup because a missmatch between its Zentyal version and the current system version. Backup was done in Zentyal version {bv} and this system could only restore backups from Zentyal version {wv} or greater', bv => $zentyalVersion, wv => REQUIRED_ZENTYAL_VERSION) ); } } # Method: prepareRestoreBackup # # Prepares a backup restauration # # Parameters: # # file - backup's file (as positional parameter) # fullRestore - wether do a full restore or restore only configuration (default: false) # dataRestore - wether do a data-only restore # forceDependencies - wether ignore dependency errors between modules # deleteBackup - deletes the backup after resroting it or if the process is aborted # revokeAllOnModuleFail - whether to revoke all restored configuration # when a module restoration fails # continueOnModuleFail - wether continue when a module fails to restore # (default: false) # Returns: # the progress indicator object which represents the progress of the restauration # # Exceptions: # # External - If it can't unpack the backup archive # sub prepareRestoreBackup { my ($self, $file, %options) = @_; my $restoreBackupScript = EBox::Config::scripts() . 'restore-backup'; my $execOptions = ''; if (exists $options{fullRestore}) { if ($options{fullRestore}) { $execOptions .= '--full-restore '; } } if (exists $options{forceDependencies}) { if ($options{forceDependencies}) { $execOptions .= '--force-dependencies '; } } if (exists $options{deleteBackup}) { if ($options{deleteBackup}) { $execOptions .= '--delete-backup '; } } if (exists $options{modsToRestore}) { foreach my $m (@{ $options{modsToRestore} }) { $execOptions .= "--module $m "; } } if (exists $options{revokeAllOnModuleFail}) { if ($options{revokeAllOnModuleFail}) { $execOptions .= '--revoke-all-on-module-fail '; } else { $execOptions .= '--no-revoke-all-on-module-fail '; } } if (exists $options{continueOnModuleFail}) { if ($options{continueOnModuleFail}) { $execOptions .= '--continue-on-module-fail '; } else { $execOptions .= '--no-continue-on-module-fail '; } } $restoreBackupScript .= " $execOptions $file"; my $totalTicks = scalar @{ $self->_modInstancesForRestore($file) }; my $progressIndicator = EBox::ProgressIndicator->create( executable => $restoreBackupScript, totalTicks => $totalTicks, ); $progressIndicator->runExecutable(); return $progressIndicator; } # Method: restoreBackup # # Restores a backup from file # # Parameters: # # file - backup's file (as positional parameter) # progressIndicator - Progress indicator associated # with htis operation (optional ) # fullRestore - wether do a full restore or restore only configuration (default: false) # dataRestore - wether do a data-only restore # forceDependencies - wether ignore dependency errors between modules # forceZentyalVersion # deleteBackup - deletes the backup after resroting it or if the process is aborted # revokeAllOnModuleFail - whether to revoke all restored configuration # when a module restoration fail # continueOnModuleFail - wether continue when a module fails to restore # (default: false) # modsToRestore - names of modules to restore (default: all) # modsToExclude - name of modules to exclude fro the restore (default:none) # # Exceptions: # # External - If it can't unpack de backup # sub restoreBackup { my ($self, $file, %options) = @_; defined $file or throw EBox::Exceptions::MissingArgument('Backup file'); exists $options{revokeAllOnModuleFail} or $options{revokeAllOnModuleFail} = 1; exists $options{modsToExclude} or $options{modsToExclude} = []; my $progress = $options{progress}; # EBox::debug("restore backup id: " . $progress->id); if ($progress and not $progress->started()) { throw EBox::Exceptions::Internal("ProgressIndicator's executable has not been run"); } my $tempdir; try { _ensureBackupdirExistence(); $self->_checkSize($file); $tempdir = $self->_unpackAndVerify($file, $options{fullRestore}, %options); $self->_unpackModulesRestoreData($tempdir); $self->_restoreZentyalConfFiles($tempdir); # TODO: Make sure we don't open the file more than necessary $self->_preRestoreActions($file, %options); my @modules = @{ $self->_modInstancesForRestore($file, %options) }; my @restored = (); my @failed = (); # run pre-checks foreach my $mod (@modules) { $self->_restoreModulePreCheck($mod, $tempdir, \%options); } try { foreach my $mod (@modules) { my $restoreOk; try { $restoreOk = $self->_restoreModule($mod, $tempdir, \%options); } otherwise { my ($ex) = @_; if ($options{continueOnModuleFail}) { my $warn = 'Error when restoring ' . $mod->name() . ': ' . $ex->text() . '. The restore process will continue anyway'; EBox::error($warn); } else { $ex->throw(); } }; if ($restoreOk) { push @restored, $mod->name(); } else { push @failed, $mod->name(); } } } otherwise { my $ex = shift; my $errorMsg = 'Error while restoring: ' . $ex->text(); EBox::error($errorMsg); $progress->setAsFinished(1, $errorMsg) if $progress; if ($options{revokeAllOnModuleFail}) { $self->_revokeRestore(\@restored); } throw $ex; }; # We need to set them as changed to be sure that they are restarted # in the save all after restoring, if they have run any migration # during restore they have been set as restarted. # We only do this with correctly restored modules foreach my $modName (@restored) { my $mod = EBox::Global->modInstance($modName); $mod->setAsChanged(); } if (@restored == @modules) { EBox::info('Restore successful'); } else { @restored = sort @restored; @failed = sort @failed; EBox::info("Restore finished. The following modules have been successfuly restored: @restored. But the following ones have failed: @failed."); } $progress->setAsFinished() if $progress; } finally { if ($tempdir) { system "rm -rf '$tempdir'"; } if ($options{deleteBackup}) { unlink $file; } }; } sub _unpackModulesRestoreData { my ($self, $tempdir) = @_; my $unpackCmd = "tar xzf '$tempdir/eboxbackup/files.tgz' --same-owner --same-permissions -C '$tempdir/eboxbackup'"; try { EBox::Sudo::root($unpackCmd); } otherwise { EBox::Sudo::silentRoot("rm -rf '$tempdir'"); throw EBox::Exceptions::External( __('Could not unpack the backup') ); }; } sub _restoreZentyalConfFiles { my ($self, $tempdir) = @_; my $etc = EBox::Config::etc(); my $archive = "$tempdir/eboxbackup/etcFiles.tgz"; if (not -f $archive) { EBox::warn("$etc files archive not found; not restoring them" ); return; } my $tmpEtc = "$tempdir/etc"; mkdir $tmpEtc; my $unpackCmd = "tar xzf '$archive' -C '$tmpEtc'"; system $unpackCmd; if ($? != 0) { system "rm -rf '$tmpEtc'"; EBox::error("Could not unpack the Zentyal configuration files archive backup"); EBox::info("Zentyal configuration files in $etc are not restored, but the restore process will continue"); return; } # create backup directory for files/directories to be replaced my $dateSuffix = strftime("%Y-%m-%d-%H%M%S", localtime()); my $currentEtcBackupDirBase = "/var/backups/etc-zentyal-$dateSuffix"; my $currentEtcBackupDir = $currentEtcBackupDirBase; my $cnt = 0; while (EBox::Sudo::fileTest('-e', $currentEtcBackupDir)) { $cnt++; $currentEtcBackupDir = "$currentEtcBackupDirBase-$cnt"; } EBox::Sudo::root("cp -a $etc $currentEtcBackupDir"); try { # put restored directory in place EBox::Sudo::root("cp -af $tmpEtc/* $etc"); } catch EBox::Exceptions::Sudo::Command with { # continue with the restore anyway EBox::error("Cannot restore $etc files: $!."); EBox::info("We cannot restore Zentyal configuration files in $etc, but the restore process will continue."); } finally { EBox::Config::refreshConfFiles(); }; } sub _restoreModulePreCheck { my ($self, $mod, $tempdir, $options_r) = @_; $mod->callRestoreBackupPreCheck("$tempdir/eboxbackup", $options_r); } sub _restoreModule { my ($self, $mod, $tempdir, $options_r) = @_; my $modname = $mod->name(); # update progress indicator my $progress = $options_r->{progress}; if ($progress) { $progress->notifyTick(); $progress->setMessage($modname); } if (not -e "$tempdir/eboxbackup/$modname.bak") { EBox::error("Restore data not found for module $modname. Skipping $modname restore"); return 0; } EBox::debug("Restoring $modname from backup data"); $mod->setAsChanged(); # we set as changed first because it is not # guaranteed that a failed backup will not # change state $mod->restoreBackup("$tempdir/eboxbackup", %{ $options_r } ); $mod->migrate(); return 1; } sub _revokeRestore { my ($self, $restored_r) = @_; EBox::debug('revoking restore for all modules'); foreach my $restname (@{ $restored_r }) { my $restmod = EBox::Global->modInstance($restname); try { $restmod->revokeConfig(); # XXX remember non-redis changes are not revoked! EBox::debug("Revoked changes in $restname module"); } otherwise { EBox::debug("$restname has not changes to be revoked" ); }; } } sub _preRestoreActions { my ($self, $archive, %options) = @_; my $global = EBox::Global->getInstance(); my @inBackup = @{ $self->_modulesInBackup($archive) }; my @missing; foreach my $modName (@inBackup) { # Skip cloud-prof to check in restore since it is possible not # to be installed until the first restore process is done (DR) next if ($modName eq 'cloud-prof'); unless ($global->modExists($modName)) { push (@missing, $modName); } } if (@missing and not $options{forceDependencies}) { throw EBox::Exceptions::External( __x('The following modules present in the backup are not installed: {mods}. You need to install them before restoring.', 'mods' => join (' ', @missing)) ); } my $mgr = EBox::ServiceManager->new(); my @mods = @{$mgr->_dependencyTree()}; # TODO: Integrate with progressIndicator foreach my $name (@mods) { my $mod = $global->modInstance($name); if ($name eq any(@inBackup)) { next unless $mod->can('configured'); # The module is present in the backup but has # never been configured, so we must enable it unless ($mod->configured()) { $mod->setConfigured(1); $mod->enableService(1); try { EBox::info("Configuring previously unconfigured module $name present in the backup to restore"); $mod->enableActions(); } otherwise { my ($ex) = @_; my $err = $ex->text(); $mod->setConfigured(0); $mod->enableService(0); throw EBox::Exceptions::Internal( __x('Cannot restore backup, error enabling module {m}: {err}', 'm' => $name, 'err' => $err) ); }; } } else { next unless $mod->can('isEnabled'); # Module is enabled but not present in the backup # we are going to restore, so we must disable it if ($mod->isEnabled()) { EBox::info("Disabling module $name not present in the backup to restore"); $mod->enableService(0); } } } } sub _modInstancesForRestore { my ($self, $archive, %options) = @_; my $forceDependencies = $options{forceDependencies}; my $anyModuleInBackup = any( @{ $self->_modulesInBackup($archive) } ); my @modules = @{ $self->_configuredModInstances }; my $anyToExclude = any(@{ $options{modsToExclude}}); # if we have a module list we check it and only keep those modules if (exists $options{modsToRestore}) { my @modsToRestore = @{ $options{modsToRestore} }; foreach my $m (@modsToRestore) { if (not( $m eq $anyModuleInBackup)) { throw EBox::Exceptions::External( __x( 'No module {m} found in backup', 'm' => $m ) ); } if ($m eq $anyToExclude) { throw EBox::Exceptions::External( __x( 'Module {m} is in both exclude and include lists', 'm' => $m ) ); } } # we use the module list instead of the full list of backup's module $anyModuleInBackup = any @modsToRestore; } # we restore the intersection between the installed modules AND the modules in # the backup archive. We remove the excluded modules @modules = grep { my $name = $_->name(); ($name eq $anyModuleInBackup) and not ($name eq $anyToExclude) } @modules; # we remove global module because it will not be restored @modules = grep { $_->name ne 'global' } @modules; if (not @modules) { throw EBox::Exceptions::External( __('No modules to restore') ); } # check modules dependencies if (not $forceDependencies) { foreach my $mod (@modules) { $self->_checkModDeps($mod->name); } } my $sortedModules = EBox::Global->sortModulesByDependencies( \@modules, 'restoreDependencies', ); return $sortedModules; } sub _modulesInBackup { my ($self, $archive) = @_; my $tempDir = $self->_unpackArchive($archive, 'modules'); my $modulesString = read_file("$tempDir/eboxbackup/modules"); my @modules = split '\s', $modulesString; return \@modules; } sub _checkModDeps { my ($self, $modName, $level, $topModule) = @_; defined $level or $level = 0; if ($level == 0) { $topModule = $modName; } if ($level >= $RECURSIVE_DEPENDENCY_THRESHOLD) { throw EBox::Exceptions::Internal("Recursive restore dependency found in module $modName"); } my $global = EBox::Global->getInstance(); my $mod = $global->modInstance($modName); if (not defined $mod) { if ($level == 0) { throw EBox::Exceptions::Internal ("$topModule cannot be created again"); } else { throw EBox::Exceptions::External __x('Unresolved restore dependency for module {topModule}: {modName} is not installed', topModule => $topModule, modName => $modName ); } } my @dependencies = @{$mod->restoreDependencies}; foreach my $dep (@dependencies) { if ($dep eq $modName) { throw EBox::Exceptions::Internal ("$modName depends on it self. Maybe something is wrong in _modInstancesForRestore method?. $modName will not be restored"); } $self->_checkModDeps($dep, $level +1, $topModule); } } sub _checkId { my ($self, $id) = @_; if ($id =~ m{[./]}) { throw EBox::Exceptions::External( __("The input contains invalid characters")); } } sub _backupFileById { my ($self, $id) = @_; my $backupdir = EBox::Config::conf . '/backups'; my $file = "$backupdir/$id.tar"; unless (-f $file) { throw EBox::Exceptions::External("Could not find the backup."); } return $file; } # Function: _facilitiesForDiskUsage # Overrides: EBox::Report::DiskUsageProvider::_faciltiesForDiskUsage sub _facilitiesForDiskUsage { return { __(q{Backup archives}) => [ backupDir() ] } } 1; zentyal-core-2.3.21+quantal1/src/EBox/Events.pm0000664000000000000000000003716212017102272016056 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Events; # Class: EBox::Events # # Events module to manage the event architecture. You can # activate or deactivate event watchers and select which # dispatchers to select in order to send the event around. This # module is currently integrated within the eBox main package # since it may be considered as a base module as logs. It manages # the EventDaemon. use base qw(EBox::Module::Service EBox::LogObserver EBox::Events::WatcherProvider EBox::Events::DispatcherProvider); use strict; use warnings; use EBox::Config; use EBox::Event; use EBox::Events::Model::EventsDetails; use EBox::Events::Model::EventsGraph; use EBox::Events::Model::EventsReportOptions; use EBox::Events::Composite::EventsReport; use EBox::Exceptions::External; use EBox::Exceptions::Internal; use EBox::Exceptions::MissingArgument; use EBox::Gettext; use EBox::Global; use EBox::Menu::Folder; use EBox::Menu::Item; use EBox::Service; # Core modules use Data::Dumper; use Error qw(:try); # Constants: # # SERVICE - the service managed by this module # use constant SERVICE => 'ebox.event-daemon'; use constant CONF_DIR => EBox::Config::conf() . 'events/'; use constant ENABLED_DISPATCHERS_DIR => CONF_DIR . 'DispatcherEnabled/'; use constant ENABLED_WATCHERS_DIR => CONF_DIR . 'WatcherEnabled/'; use constant CONF_DISPATCHER_MODEL_PREFIX => 'EBox::Events::Model::Dispatcher::'; use constant CONF_WATCHER_MODEL_PREFIX => 'EBox::Events::Model::Watcher::'; use constant EVENTS_FIFO => EBox::Config::tmp() . 'events-fifo'; # Group: Protected methods # Constructor: _create # # Create an event module # # Overrides: # # # # Returns: # # - the recently created module # sub _create { my $class = shift; my $self = $class->SUPER::_create(name => 'events', printableName => __('Events'), @_); bless ($self, $class); return $self; } sub _daemons { return [ { 'name' => SERVICE } ]; } sub _enforceServiceState { my ($self) = @_; # Check for admin dumbness, it can throw an exception if ($self->_nothingEnabled()) { $self->_stopService(); return; } $self->SUPER::_enforceServiceState(); } # Group: Public methods # Method: menu # # Show the events menu entry # # Overrides: # # # sub menu { my ($self, $root) = @_; my $folder = new EBox::Menu::Folder('name' => 'Maintenance', 'text' => __('Maintenance'), 'separator' => 'Core', 'order' => 70); my $item = new EBox::Menu::Item( name => 'Events', text => $self->printableName(), url => 'Maintenance/Events', order => 30, ); $folder->add($item); $root->add($folder); } # Method: restoreDependencies # # Override EBox::Module::Base::restoreDependencies # sub restoreDependencies { my @depends = (); if (EBox::Global->modExists('mail')) { push(@depends, 'mail'); } return \@depends; } # Method: eventWatchers # # Overrides: # # # sub eventWatchers { return [ 'Log', 'DiskFreeSpace', 'RAID', 'Runit', 'Updates', 'State' ]; } # Method: eventDispatchers # # Overrides: # # # sub eventDispatchers { return [ 'Log', 'RSS', 'Jabber' ]; } sub reportDetailsModel { my ( $self ) = @_; # Check if it is already cached unless ( exists $self->{EventsDetailsModel} ) { $self->{EventsDetailsModel} = new EBox::Events::Model::EventsDetails( confmodule => $self, directory => 'EventsDetails' ); } return $self->{EventsDetailsModel}; } sub reportGraphModel { my ( $self ) = @_; # Check if it is already cached unless ( exists $self->{EventsGraphModel} ) { $self->{EventsGraphModel} = new EBox::Events::Model::EventsGraph( confmodule => $self, directory => 'EventsGraph' ); } return $self->{EventsGraphModel}; } sub reportOptionsModel { my ( $self ) = @_; # Check if it is already cached unless ( exists $self->{EventsOptionModel} ) { $self->{EventsOptionModel} = new EBox::Events::Model::EventsReportOptions( confmodule => $self, directory => 'EventsReportOptions' ); } return $self->{EventsOptionModel}; } # Method: models # # Override to load in manager the dynamic models for Log Watcher # Filtering # # Overrides: # # # sub models { my ($self) = @_; my $logWatcherConfModel = $self->model('LogWatcherConfiguration'); $logWatcherConfModel->setUpModels(); # This loads the log watcher models return $self->SUPER::models(); } # Method: isRunning # # Overrides: # # # sub isRunning { my ($self) = @_; return $self->isEnabled(); } sub enableDispatcher { my ($self, $dispatcher, $enabled) = @_; $self->model('ConfigureDispatchers')->enableDispatcher($dispatcher, $enabled); } sub isEnabledDispatcher { my ($self, $dispatcher) = @_; $self->model('ConfigureDispatchers')->enableDispatcher($dispatcher); } sub enableWatcher { my ($self, $watcher, $enabled) = @_; $self->model('ConfigureWatchers')->enableWatcher($watcher, $enabled); } sub isEnabledWatcher { my ($self, $watcher) = @_; $self->model('ConfigureWatchers')->isEnabledWatcher($watcher); } # Method: sendEvent # # Send an event to the event daemon to be dispatched to enabled # dispatchers. # # It could send to content of an event or object # itself to be sent. # # Parameters: # # message - String the i18ned message which will be dispatched # # source - String the event watcher/subwatcher name to # categorise afterwards the event depending on the # source # # level - Enumerate the level of the event *(Optional)* # Possible values: 'info', 'warn', 'error' or 'fatal'. # Default: 'info' # # timestamp - Int the number of seconds since the epoch (1 Jan 1970) # *(Optional)* Default value: now # # dispatchTo - array ref containing the relative name for the # dispatchers you want to dispatch this event *(Optional)* # Default value: *any*, which means any available dispatcher will # dispatch the event. Concrete example: ControlCenter # # - Named parameters # # event - the event to send. If this parameter is # set, then the previous parameters will be ignored. *(Optional)* # # Returns: # # Boolean - indicating if the sending was successful or not # # Exceptions: # # - thrown if there is no # message and source or no event. # # - thrown if we try to send events # giving the fact the events module is disabled # # - thrown if we cannot send the # event through the fifo # sub sendEvent { my ($self, %args) = @_; unless ( $self->isEnabled() ) { throw EBox::Exceptions::External( __('The events module is not enabled to send events') ); } my $event; if ( defined($args{event}) and $args{event}->isa('EBox::Event') ) { $event = $args{event}; } elsif ( defined($args{message}) and defined($args{source}) ) { $event = new EBox::Event(%args); } else { throw EBox::Exceptions::MissingArgument('message') if not defined($args{message}); throw EBox::Exceptions::MissingArgument('source') if not defined($args{source}); } # Remove NULL characters $event->{message} =~ tr/\0//d; my $dumper = new Data::Dumper([$event]); # Set no new lines to dump to communicate with FIFO, the end of # connection is done using newline character $dumper->Indent(0); # Send the dumpered event through the FIFO open(my $fifo, '+<', EVENTS_FIFO) or throw EBox::Exceptions::Internal('Could not open ' . EVENTS_FIFO . " for reading: $!"); print $fifo $dumper->Dump() . "\0"; close($fifo); return 1; } # Group: Private methods # Check either if at least one watcher and one dispatcher are enabled or the # logs are enabled sub _nothingEnabled { my ($self) = @_; if ($self->_logIsEnabled()) { return undef; } my $eventModel = $self->model('ConfigureWatchers'); my $dispatcherModel = $self->model('ConfigureDispatchers'); my $match = $eventModel->find(enabled => 1); unless (defined ($match)) { EBox::warn('No event watchers have been enabled'); return 1; } $match = $dispatcherModel->find(enabled => 1); unless (defined ($match)) { EBox::warn('No event dispatchers have been enabled'); return 1; } return undef; } # Method: _logIsEnabled # # check if log is enabled for the events module sub _logIsEnabled { my ($self) = @_; my $log = EBox::Global->modInstance('logs'); unless ($log->isEnabled()) { return undef; } my $configureLogTable = $log->model('ConfigureLogs'); my $enabledLogs = $configureLogTable->enabledLogs(); return $enabledLogs->{events}; } sub enableLog { my ($self, $status) = @_; $self->setAsChanged(); } sub tableInfo { my ($self) = @_; my $titles = { timestamp => __('Date of first event'), lastTimestamp => __('Date of last event'), nRepeated => __('Repetitions'), level => __('Level'), source => __('Source'), message => __('Message'), }; my @order =qw(timestamp lastTimestamp nRepeated level source message); my $levels = { info => __('Informative'), warn => __('Warning'), error => __('Error'), fatal => __('Fatal error'), }; return [ { 'name' => $self->printableName(), 'tablename' => 'events', 'titles' => $titles, 'order' => \@order, 'filter' => [ 'source', 'message'], 'events' => $levels, 'eventcol' => 'level', 'consolidate' => $self->_consolidateTable(), } ]; } sub _consolidateTable { my $table = 'events_accummulated'; my $spec= { consolidateColumns => { level => { accummulate => sub { # accummulate in correct # level column my ($type) = @_; return $type; }, conversor => sub { my ($v, $row) = @_; return $row->{nRepeated}; }, }, source => { destination => 'source' }, }, accummulateColumns => { info => 0, warn => 0, error => 0, fatal => 0, }, }; return { $table => $spec }; } sub consolidateReportQueries { return [ { 'target_table' => 'events_report', 'query' => { 'select' => 'source, level, sum(nRepeated) AS nEvents', 'from' => 'events', 'group' => 'source,level', }, 'quote' => { source => 1 }, }, ]; } # Method: report # # Overrides: # # # Returns: # # hash ref - the events report # sub report { my ($self, $beg, $end, $options) = @_; my $report = {}; my $allAlertsRaw = $self->runMonthlyQuery($beg, $end, { 'select' => 'level, SUM(nEvents)', 'from' => 'events_report', 'group' => 'level', }, { 'key' => 'level' } ); $report->{'all_alerts'} = {}; foreach my $key (%{ $allAlertsRaw }) { next if ( ($key eq 'debug') or ($key eq 'info')); my $sum = $allAlertsRaw->{$key}->{sum}; defined $sum or next; $report->{'all_alerts'}->{$key} = $sum; } my $alertsBySource = {}; foreach my $level (qw(warn error fatal)) { my $result = $self->runMonthlyQuery($beg, $end, { 'select' => 'source, sum(nEvents)', 'from' => 'events_report', 'group' => 'source,level', 'where' => qq{level='$level'} }, { 'key' => 'source' } ); foreach my $source (keys %{$result}) { if (not exists $alertsBySource->{$source}) { $alertsBySource->{$source} = {}; } my $sum = $result->{$source}->{sum}; defined($sum) or next; $alertsBySource->{$source}->{$level} = $sum; } } $report->{alerts_by_source} = $alertsBySource; return $report; } # Method: lastEventsReport # # Get the report of current month events report # # Returns: # # Hash ref - containing the following keys # # total - the sum of all alerts # info - the sum of info alerts # warn - the sum of warn alerts # error - the sum of error alerts # fatal - the sum of fatal alerts # sub lastEventsReport { my ($self) = @_; my @time = localtime(); my $beg = sprintf("%d-%d", $time[5]+1900, $time[4]+1); my $allAlerts = $self->runMonthlyQuery($beg, $beg, { 'select' => 'level, SUM(nEvents)', 'from' => 'events_report', 'group' => 'level', }, { 'key' => 'level' }); my %result = (info => 0, warn => 0, error => 0, fatal => 0); my $total = 0; foreach my $key (qw(info warn error fatal)) { if (exists $allAlerts->{$key} ) { $result{$key} = $allAlerts->{$key}->{sum}->[0]; $total += $result{$key}; } } $result{total} = $total; return \%result; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Module/0000775000000000000000000000000012017102272015470 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Module/Service.pm0000664000000000000000000005166412017102272017442 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Module::Service; use base qw(EBox::Module::Config); use EBox::Config; use EBox::Exceptions::Internal; use EBox::Global; use EBox::Dashboard::ModuleStatus; use EBox::Sudo; use EBox::AuditLogging; use Error qw(:try); use strict; use warnings; use constant INITDPATH => '/etc/init.d/'; # Method: usedFiles # # This method is mainly used to show information to the user # about the files which are going to be modified by the service # managed by Zentyal # # Returns: # # An array ref of hashes containing the following: # # file - file's path # reason - some info about why you need to modify the file # module - module's name # # Example: # # [ # { # 'file' => '/etc/samba/smb.conf', # 'reason' => __('The file sharing module needs to modify it' . # ' to configure samba properly') # 'module' => samba # } # ] # # sub usedFiles { return []; } # Method: actions # # This method is mainly used to show information to the user # about the actions that need to be carried out by the service # to configure the system # # Returns: # # An array ref of hashes containing the following: # # action - action to carry out # reason - some info about why you need to modify the file # module - module's name # # Example: # # [ # { # 'action' => 'remove samba init script" # 'reason' => # __('Zentyal will take care of start and stop the service') # 'module' => samba # } # ] # # sub actions { return []; } # Method: enableActions # # This method is run to carry out the actions that are needed to # enable a module. It usually executes those which are returned # by actions. # # For example: it could remove the samba init script # # The default implementation is to call the following # script if exists and has execution rights: # /usr/share/zentyal-$module/enable-module # # But this method can be overriden by any module. # sub enableActions { my ($self) = @_; my $path = EBox::Config::share(); my $modname = $self->{'name'}; my $command = "$path/zentyal-$modname/enable-module"; if (-x $command) { EBox::Sudo::root($command); } } # Method: disableActions # # This method is run to rollbackthe actions that have been carried out # to enable a module. # # For example: it could restore the samba init script # # sub disableActions { } # Method: enableModDepends # # This method is used to declare which modules need to be enabled # to use this module. # # It doesn't state a configuration dependency, it states a working # dependency. # # For example: the firewall module needs the network module. # # By default it returns the modules established in the enabledepends list # in the module YAML file. Override the method if you need something more # specific, e.g., having a dynamic list. # # Returns: # # array ref containing the dependencies. # # Example: # # [ 'firewall', 'users' ] # sub enableModDepends { my ($self) = @_; my $depends = $self->info()->{'enabledepends'}; if(not defined($depends)) { return []; } else { return $depends; } } # Method: bootDepends # # This method is used to declare which modules need to have its # daemons started before this module ones. # # It doesn't state a configuration dependency, it states a boot # dependency. # # For example: the samba module needs the printer daemon started before. # # By default it returns the modules established in the bootdepends list # in the module YAML file. Override the method if you need something more # specific, e.g., having a dynamic list. If nothing is specified in the # YAML file nor the method is overriden, the enabledepends value is # returned to provide compatibility with the previous behavior. # # Returns: # # array ref containing the dependencies. # # Example: # # [ 'firewall', 'users' ] # sub bootDepends { my ($self) = @_; my $depends = $self->info()->{'bootdepends'}; if(not defined($depends)) { return $self->enableModDepends(); } else { return $depends; } } # Method: configured # # This method is used to check if the module has been configured. # Configuration is done one time per service package version. # # If this method returns true it means that the user has accepted # to carry out the actions and file modifications that enabling a # service implies. # # If you must store this value in the status branch of conf in case # you decide to override it. # # Returns: # # boolean # sub configured { my ($self) = @_; if ((@{$self->usedFiles()} + @{$self->actions()}) == 0) { return 1; } if (-d EBox::Config::conf() . "configured/") { return -f (EBox::Config::conf() . "configured/" . $self->name()); } return $self->st_get_bool('_serviceConfigured'); } # Method: setConfigured # # This method is used to set if the module has been configured. # Configuration is done once per service package version. # # If it's set to true it means that the user has accepted # to carry out the actions and file modifications that enabling a # service implies. # # If you must store this value in the status branch of conf in case # you decide to override it. # # Parameters: # # boolean - true if configured, false otherwise # sub setConfigured { my ($self, $status) = @_; defined $status or $status = 0; return unless ($self->configured() xor $status); if (-d EBox::Config::conf() . "configured/") { if ($status) { EBox::Sudo::command('touch ' . EBox::Config::conf() . "configured/" . $self->name()); } else { EBox::Sudo::command('rm -f ' . EBox::Config::conf() . "configured/" . $self->name()); } } return $self->st_set_bool('_serviceConfigured', $status); } # Method: firstInstall # # This method is used to check if the module has been recently installed # and has not saved changes yet. This is useful to control which modules # have been configured through wizards. # # Returns: # # boolean # sub firstInstall { my ($self) = @_; if ($self->st_get_bool('_serviceInstalled')) { return 0; } return 1; } # Method: setInstalled # # This method is used to set if the module has been installed # # If it's set to true it means that the module has been installed # Call this method is only necessary on first install of the module. # sub setInstalled { my ($self) = @_; return $self->st_set_bool('_serviceInstalled', 1); } # Method: isEnabled # # Used to tell if a module is enabled or not # # Returns: # # boolean sub isEnabled { my ($self) = @_; my $enabled = $self->get_bool('_serviceModuleStatus'); if (not defined($enabled)) { return $self->defaultStatus(); } return $enabled; } # Method: _isDaemonRunning # # Used to tell if a daemon is running or not # # Returns: # # boolean - true if it's running otherwise false sub _isDaemonRunning { my ($self, $dname) = @_; my $daemons = $self->_daemons(); my $daemon; my @ds = grep { $_->{'name'} eq $dname } @{$daemons}; if(@ds) { $daemon = $ds[0]; } if(!defined($daemon)) { throw EBox::Exceptions::Internal( "no such daemon defined in this module: " . $dname); } if(defined($daemon->{'pidfiles'})) { foreach my $pidfile (@{$daemon->{'pidfiles'}}) { unless ($self->pidFileRunning($pidfile)) { return 0; } } return 1; } if(daemon_type($daemon) eq 'upstart') { my $running = 0; try { $running = EBox::Service::running($dname); } catch EBox::Exceptions::Internal with { # If the daemon does not exist, then return false ; }; return $running; } elsif(daemon_type($daemon) eq 'init.d') { my $output; my $notOk; try { $output = EBox::Sudo::root(INITDPATH . $dname . ' ' . 'status'); } catch EBox::Exceptions::Sudo::Command with { # Command returned != 0 $notOk = 1; }; if ($notOk) { return 0; } my $status = join ("\n", @{$output}); if ($status =~ m{$dname .* running}) { return 1; } elsif ($status =~ m{ is running}) { return 1; } elsif ($status =~ m{$dname .* [ OK ]}) { return 1; } elsif ($status =~ m{$dname .*done}s) { return 1; } else { return 0; } } else { throw EBox::Exceptions::Internal( "Service type must be either 'upstart' or 'init.d'"); } } # Method: isRunning # # Used to tell if a service is running or not. # # Modules with complex service management must # override this method to carry out their custom checks which can # involve checking an upstart script, an existing PID... # # By default it returns true if all the system services specified in # daemons are running # # Returns: # # boolean - true if it's running otherwise false sub isRunning { my ($self) = @_; my $daemons = $self->_daemons(); for my $daemon (@{$daemons}) { my $check = 1; my $pre = $daemon->{'precondition'}; if (defined ($pre)) { $check = $pre->($self); } # If precondition does not meet the daemon should not be running. $check or return 0; unless ($self->_isDaemonRunning($daemon->{'name'})) { return 0; } } return 1; } # Method: addModuleStatus # # Called by the sysinfo module status widget to give the desired information # about the current module status. This default implementation should be ok # for most modules but it can be overriden to provide a custom one (or none). # # Parameters: # # section - the section the information is added to # sub addModuleStatus { my ($self, $section) = @_; my $enabled = $self->isEnabled(); my $running = $self->isRunning(); my $name = $self->name(); my $modPrintName = ucfirst($self->printableName()); my $nobutton; if ($self->_supportActions()) { $nobutton = 0; } else { $nobutton = 1; } $section->add(new EBox::Dashboard::ModuleStatus( module => $name, printableName => $modPrintName, enabled => $enabled, running => $running, nobutton => $nobutton, )); } # Method: enableService # # Used to enable a service # # Parameters: # # boolean - true to enable, false to disable # sub enableService { my ($self, $status) = @_; defined $status or $status = 0; return unless ($self->isEnabled() xor $status); $self->set_bool('_serviceModuleStatus', $status); my $audit = EBox::Global->modInstance('audit'); if (defined ($audit)) { my $action = $status ? 'enableService' : 'disableService'; $audit->logAction('global', 'Module Status', $action, $self->{name}); } } # Method: defaultStatus # # This method must be overriden if you want to enable the service by default # # Returns: # # boolean sub defaultStatus { return 0; } sub daemon_type { my ($daemon) = @_; if($daemon->{'type'}) { return $daemon->{'type'}; } else { return 'upstart'; } } # Method: showModuleStatus # # Indicate to ServiceManager if the module must be shown in Module # status configuration. # # It must be overridden in rare cases # # Returns: # # true # sub showModuleStatus { return 1; } # Method: _daemons # # This method must be overriden to return the services required by this # module. # # Returns: # # An array of hashes containing keys 'name' and 'type', 'name' being the # name of the service and 'type' either 'upstart' or 'init.d', depending # on how the module should be managed. # # If the type is 'init.d' an extra 'pidfiles' key is needed with the paths # to the pid files the daemon uses. This will be used to check the status. # # It can optionally contain a key 'precondition', which should be a reference # to a class method which will be checked to determine if the given daemon # should be run (if it returns true) or not (otherwise). # # Example: # # sub externalConnection # { # my ($self) = @_; # return $self->isExternal; # } # # sub _daemons # { # return [ # { # 'name' => 'ebox.jabber.jabber-router', # 'type' => 'upstart' # }, # { # 'name' => 'ebox.jabber.jabber-resolver', # 'type' => 'upstart', # 'precondition' => \&externalConnection # } # ]; # } sub _daemons { return []; } sub _startDaemon { my($self, $daemon) = @_; my $isRunning = $self->_isDaemonRunning($daemon->{'name'}); if(daemon_type($daemon) eq 'upstart') { if($isRunning) { EBox::Service::manage($daemon->{'name'},'restart'); } else { EBox::Service::manage($daemon->{'name'},'start'); } } elsif(daemon_type($daemon) eq 'init.d') { my $script = INITDPATH . $daemon->{'name'}; if($isRunning) { $script = $script . ' ' . 'restart'; } else { $script = $script . ' ' . 'start'; } EBox::Sudo::root($script); } else { throw EBox::Exceptions::Internal( "Service type must be either 'upstart' or 'init.d'"); } } sub _stopDaemon { my($self, $daemon) = @_; if(daemon_type($daemon) eq 'upstart') { EBox::Service::manage($daemon->{'name'},'stop'); } elsif(daemon_type($daemon) eq 'init.d') { my $script = INITDPATH . $daemon->{'name'} . ' ' . 'stop'; EBox::Sudo::root($script); } else { throw EBox::Exceptions::Internal( "Service type must be either 'upstart' or 'init.d'"); } } # Method: _manageService # # This method will try to perform the action passed as first argument on # all the daemons return by the module's daemons method. # sub _manageService { my ($self,$action) = @_; my $daemons = $self->_daemons(); for my $daemon (@{$daemons}) { my $run = 1; my $pre = $daemon->{'precondition'}; if(defined($pre)) { $run = &$pre($self); } #even if parameter is 'start' we might have to stop some daemons #if they are no longer needed if(($action eq 'start') and $run) { $self->_startDaemon($daemon); } else { $self->_stopDaemon($daemon); } } } # Method: _startService # # This method will try to start or restart all the daemons associated to # the module # sub _startService { my ($self) = @_; $self->_manageService('start'); } # Method: stopService # # This is the external interface to call the implementation which lies in # _stopService in subclassess # # sub stopService { my $self = shift; $self->_lock(); try { $self->_stopService(); } finally { $self->_unlock(); }; } # Method: _stopService # # This method will try to stop all the daemons associated to the module # sub _stopService { my ($self) = @_; $self->_manageService('stop'); } sub _preServiceHook { my ($self, $enabled) = @_; $self->_hook('preservice', $enabled); } sub _postServiceHook { my ($self, $enabled) = @_; $self->_hook('postservice', $enabled); } # Method: _regenConfig # # Base method to regenerate configuration. It should NOT be overriden # sub _regenConfig { my $self = shift; return unless $self->configured(); $self->SUPER::_regenConfig(@_); my $enabled = ($self->isEnabled() or 0); $self->_preServiceHook($enabled); $self->_enforceServiceState(@_); $self->_postServiceHook($enabled); } # Method: restartService # # This method will try to restart the module's service by means of # calling _regenConfig. The method call has the named # parameter restart with true value # sub restartService { my ($self) = @_; $self->_lock(); my $global = EBox::Global->getInstance(); my $log = EBox::logger; $log->info("Restarting service for module: " . $self->name); try { $self->_regenConfig('restart' => 1); } otherwise { my ($ex) = @_; $log->error("Error restarting service: $ex"); throw $ex; } finally { $self->_unlock(); }; } # Method: _supportActions # # This method determines if the service will have a button to start/restart # it in the module status widget. By default services will have the button # unless this method is overriden to return undef sub _supportActions { return 1; } # Method: _enforceServiceState # # This method will start, restart or stop the associated daemons to # bring them to their desired state. If you need specific behaviour # override this method in your module. # sub _enforceServiceState { my ($self) = @_; if($self->isEnabled()) { $self->_startService(); } else { $self->_stopService(); } } # # Method: writeConfFile # # It executes a given mason component with the passed parameters over # a file. It becomes handy to set configuration files for services. # Also, its file permissions will be kept. # It can be called as class method. (XXX: this design or is an implementation accident?) # XXX : the correct behaviour will be to throw exceptions if file will not be stated and no defaults are provided. It will provide hardcored defaults instead because we need to be backwards-compatible # # # Parameters: # # file - file name which will be overwritten with the execution output # component - mason component # params - parameters for the mason component. Optional. Defaults to no parameters # defaults - a reference to hash with keys mode, uid and gid. Those values will be used when creating a new file. (If the file already exists the existent values of these parameters will be left untouched) # sub writeConfFile # (file, component, params, defaults) { my ($self, $file, $compname, $params, $defaults) = @_; # we will avoid check modification when the method is called as class method # this is awkward but is the fudge we had used to date for files created # by ebox which rhe user shoudn't give their permission to modify # maybe we need to add some parameter to better reflect this? my $manager; $manager = new EBox::ServiceManager(); # if ($manager->skipModification($self->{'name'}, $file)) { # EBox::info("Skipping modification of $file"); # return; # } EBox::Module::Base::writeConfFileNoCheck($file, $compname, $params, $defaults); # $manager->updateFileDigest($self->{'name'}, $file); } # Method: certificates # # This method is used to tell the CA module which certificates # and its properties we want to issuefor this service module. # # Returns: # # An array ref of hashes containing the following: # # service - name of the service using the certificate # path - full path to store this certificate # user - user owner for this certificate file # group - group owner for this certificate file # mode - permission mode for this certificate file # # Example: # # [ # { # 'service' => 'jabberd2', # 'path' => '/etc/jabberd2/ebox.pem', # 'user' => 'jabber', # 'group' => 'jabber', # 'mode' => '0640' # } # ] # sub certificates { return []; } # Method: disableApparmorProfile # # This method is used to disable a given # apparmor profile. # # It does nothing if apparmor or the profile # are not installed. # # Parameters: # # string - apparmor profile sub disableApparmorProfile { my ($self, $profile) = @_; if ( -f '/etc/init.d/apparmor' ) { my $profPath = "/etc/apparmor.d/$profile"; my $disPath = "/etc/apparmor.d/disable/$profile"; if (-f $profPath and not -f $disPath) { unless ( -d '/etc/apparmor.d/disable' ) { EBox::Sudo::root('mkdir /etc/apparmor.d/disable'); } my $cmd = "ln -s $profPath $disPath"; EBox::Sudo::root($cmd); EBox::Sudo::root('invoke-rc.d apparmor restart'); } } } 1; zentyal-core-2.3.21+quantal1/src/EBox/Module/Base.pm0000664000000000000000000013602612017102272016710 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Module::Base; use strict; use warnings; use File::Copy; use Proc::ProcessTable; use EBox; use EBox::Util::Lock; use EBox::Config; use EBox::Global; use EBox::Sudo; use EBox::Exceptions::Internal; use EBox::Exceptions::Lock; use EBox::Gettext; use EBox::FileSystem; use EBox::ServiceManager; use EBox::DBEngineFactory; use HTML::Mason; use File::Temp qw(tempfile); use Fcntl qw(:flock); use Error qw(:try); use Time::Local; use File::Slurp; use Perl6::Junction qw(any); use Scalar::Util; # Constants: use constant APPARMOR_PARSER => '/sbin/apparmor_parser'; use constant APPARMOR_D => '/etc/apparmor.d/'; # Method: _create # # Base constructor for a module # # Parameters: # # name - String module's name # printableName - String printable module's name # title - String the module's title # # Returns: # # EBox::Module instance # # Exceptions: # # Internal - If no name is provided sub _create # (name) { my $class = shift; my %opts = @_; my $self = {}; $self->{name} = delete $opts{name}; $self->{title} = delete $opts{title}; $self->{version} = undef; $self->{printableName} = __(delete $opts{printableName}); unless (defined($self->{name})) { use Devel::StackTrace; my $trace = Devel::StackTrace->new; print STDERR $trace->as_string; throw EBox::Exceptions::Internal( "No name provided on Module creation"); } bless($self, $class); return $self; } # Method: info # # Read module information from YAML file # sub info { my ($self) = @_; return EBox::Global->readModInfo($self->{name}); } # Method: depends # # Return an array ref with the names of the modules that this # module depends on # # Returns: # # array ref - holding the names of the modules that the requested module # sub depends { my ($self) = @_; my $info = $self->info(); my @list = map {s/^\s+//; $_} @{$info->{'depends'}}; if (@list) { return \@list; } else { return []; } } # Method: initialSetup # # This method is run to carry out the actions that are needed # when the module is installed or upgraded. # # The run of this method must be always idempotent. So # the actions will need to check if they are necessary or # not to avoid problems when executed on upgrades. # # The default implementation is to call the following # script if exists and has execution rights: # /usr/share/zentyal-$module/initial-setup # # But this method can be overriden by any module. # # When upgrading, the version of the previously installed # package is passed as argument. # # Parameters: # # version - version of the previous package or undef # if this is the first install # sub initialSetup { my ($self, $version) = @_; my $path = EBox::Config::share(); my $modname = $self->{'name'}; my $command = "$path/zentyal-$modname/initial-setup"; if (-x $command) { if (defined $version) { $command .= " $version"; } EBox::Sudo::root($command); } } # Method: migrate # # This method runs all the needed migrations on a # module upgrade. # # The migration scripts need to be located at: # /usr/share/zentyal-$module/migration/[00-99]*.pl # # Parameters: # # version - version of the previously installed package # sub migrate { my ($self, $version) = @_; my $path = EBox::Config::share(); my $modname = $self->{'name'}; my $package = $self->package(); my $dir = "$path/$package/migration"; my @scripts = glob ("$dir/[00-99]*.pl"); foreach my $script (@scripts) { my $file = read_file($script); { #silent warnings (redefined subs) local $SIG{__WARN__} = sub { # TODO: remove this after debugging period EBox::debug(@_); }; eval $file; }; } } # Method: revokeConfig # # Base method to revoke config. It just notifies that he module has been # restarted. # It should be overriden by subclasses as needed # sub revokeConfig { my $self = shift; my $global = EBox::Global->getInstance(); $global->modIsChanged($self->name) or return; $global->modRestarted($self->name); } # Method: _saveConfig # # Base method to save configuration. It should be overriden # by subclasses as needed # sub _saveConfig { # default empty implementation. It should be overriden by subclasses as # needed } # Method: save # # Sets a module as saved. This implies a call to _regenConfig and set # the module as saved and unlock it. # sub save { my $self = shift; $self->_lock(); my $global = EBox::Global->getInstance(); my $log = EBox::logger; $log->info("Restarting service for module: " . $self->name); $self->_saveConfig(); try { $self->_regenConfig(); } finally { $global->modRestarted($self->name); $self->_unlock(); }; } # Method: saveConfig # # Save module config, but do not call _regenConfig # sub saveConfig { my $self = shift; $self->_lock(); try { my $global = EBox::Global->getInstance(); my $log = EBox::logger; $log->info("Saving config for module: " . $self->name); $self->_saveConfig(); } finally { $self->_unlock(); }; } # Method: saveConfigRecursive # # Save module config and the modules which depends on recursively # sub saveConfigRecursive { my ($self) = @_; $self->_saveConfigRecursive($self->name); } # Method: _saveConfigRecursive # # Save module config and the modules which depends on recursively # sub _saveConfigRecursive { my ($self, $module) = @_; my $global = EBox::Global->getInstance(); for my $dependency (@{$global->modDepends($module)}) { $self->_saveConfigRecursive($dependency); } my $modInstance = EBox::Global->modInstance($module); $modInstance->saveConfig(); $global->modRestarted($module); } sub _unlock { my ($self) = @_; EBox::Util::Lock::unlock($self->name); } sub _lock { my ($self) = @_; EBox::Util::Lock::lock($self->name); } # Method: changed # # Returns whether the module has changes status or not sub changed { my ($self) = @_; my $name = $self->name; my $global = EBox::Global->getInstance(); return $global->modIsChanged($name); } # Method: setAsChanged # # Sets the module changed status # # Parameters: # newChangedStauts - optional, default to true (changed) # sub setAsChanged { my ($self, $newChangedStatus) = @_; defined $newChangedStatus or $newChangedStatus = 1; my $name = $self->name; my $global = EBox::Global->getInstance(); my $changedStatus = $global->modIsChanged($name); if ($newChangedStatus) { return if $changedStatus; $global->modChange($name); } else { return if not $changedStatus; $global->modRestarted($name); } } # Method: makeBackup # # restores the module state from a backup # # Parameters: # dir - directory used for the backup operation # (named parameters following) # bug - wether we are making a bug report instead of a normal backup sub makeBackup # (dir, %options) { my ($self, $dir, %options) = @_; defined $dir or throw EBox::Exceptions::InvalidArgument('directory'); my $backupDir = $self->_createBackupDir($dir); $self->aroundDumpConfig($backupDir, %options); } # Method: backupDir # # Parameters: # $dir - directory used for the restore/backup operation # # Returns: # the path to the directory used by the module to dump or restore his state # sub backupDir { my ($self, $dir) = @_; # avoid duplicate paths problem my $modulePathPart = '/' . $self->name() . '.bak'; ($dir) = split $modulePathPart, $dir; my $backupDir = $self->_bak_file_from_dir($dir); return $backupDir; } # Private method: _createBackupDir # creates a directory to dump or restore files containig the module state. # If there are already a apropiate directory, it simply returns the path of this directory # # Parameters: # $dir - directory used for the restore/backup operation # # Returns: # the path to the directory used by the module to dump or restore his state # sub _createBackupDir { my ($self, $dir) = @_; my $backupDir = $self->backupDir($dir); if (! -d $backupDir) { EBox::FileSystem::makePrivateDir($backupDir); } return $backupDir; } # Method: restoreBackup # # restores the module state from a backup # # Parameters: # dir - directory used for the restore operation # (named parameters following) # sub restoreBackup # (dir, %options) { my ($self, $dir, %options) = @_; defined $dir or throw EBox::Exceptions::InvalidArgument('directory'); my $backupDir = $self->backupDir($dir); (-d $backupDir) or throw EBox::Exceptions::Internal("$backupDir must be a directory"); if (not $options{dataRestore}) { $self->aroundRestoreConfig($backupDir, %options); } } sub callRestoreBackupPreCheck { my ($self, $dir, $options_r) = @_; my $backupDir = $self->backupDir($dir); (-d $backupDir) or throw EBox::Exceptions::Internal("$backupDir must be a directory"); $self->restoreBackupPreCheck($backupDir, %{ $options_r }); } sub restoreBackupPreCheck { my ($self, $dir, %options) = @_; # default: no check } sub _bak_file_from_dir { my ($self, $dir) = @_; $dir =~ s{/+$}{}; my $file = "$dir/" . $self->name . ".bak"; return $file; } # Method: restoreDependencies # # this method should be override by any module that depends on another module/s to be restored from a backup # # Returns: a reference to a list with the names of required eBox modules for a # sucesful restore. (default: module dependencies ) # # sub restoreDependencies { my ($self) = @_; my $global = EBox::Global->getInstance(); return $global->modDepends($self->name); } # Method: dumpConfig # # this must be override by individuals to restore to dump the # configuration properly # # Parameters: # dir - directory where the modules backup files are # dumped (without trailing slash) # sub dumpConfig { my ($self, $dir, %options) = @_; } # Method: aroundDumpConfig # # Wraps the dumpConfig call; the purpose of this sub is to allow # special types of modules like Module::Config to call another method # alongside with dumConfig transparently. # # Normally, ebox modules does not need to override this # # Parameters: # dir - Directoy where the module configuration is been dumped # sub aroundDumpConfig { my ($self, $dir, @options) = @_; $self->dumpConfig($dir, @options); } # # Method: restoreConfig # # This must be override by individuals to restore its configuration # from the backup file. Those files are the same were created with # dumpConfig # # Parameters: # dir - Directory where are located the backup files # (without the trailing slash) # sub restoreConfig { my ($self, $dir) = @_; } # Method: aroundRestoreConfig # # wraps the restoreConfig call; the purpose of this sub is to allow specila # types of modules like Module::Config to call another method alongside with # restoreConfig transparently # normally ebox modules does not need to override this # # Parameters: # dir - directory where are located the backup files # sub aroundRestoreConfig { my ($self, $dir, @extraOptions) = @_; $self->restoreConfig($dir, @extraOptions); } # Method: name # # Return the module name of the current module instance # # Returns: # # strings - name # sub name { my $self = shift; return $self->{name}; } # Method: setName # # Set the module name for the current module instance # # Parameters: # # name - module name # sub setName # (name) { my $self = shift; my $name = shift; $self->{name} = $name; } # Method: printableName # # Return the printable module name of the current module # instance # # Returns: # # String - the printable name # sub printableName { my ($self) = @_; if ( $self->{printableName} ) { return $self->{printableName}; } else { return $self->name(); } } # Method: setPrintableName # # Set the printable module name of the current module # instance # # Parameters: # # printableName - String the printable name # sub setPrintableName { my ($self, $printableName) = @_; $self->{printableName} = $printableName; } # Method: title # # Returns the module title of the current module instance # # Returns: # # string - title or name if title was not provided # sub title { my ($self) = @_; if(defined($self->{title})) { return $self->{title}; } elsif ( $self->{printableName} ) { return $self->{printableName}; } else { return $self->{name}; } } # Method: setTitle # # Sets the module title for the current module instance # # Parameters: # # title - module title # sub setTitle # (title) { my ($self,$title) = @_; $self->{title} = $title; } # Method: actionMessage # # Gets the action message for an action # # Parameters: # # action - action name sub actionMessage { my ($self,$action) = @_; if(defined($self->{'actions'})) { return $self->{'actions'}->{$action}; } else { return $action; } } # Method: menu # # This method returns the menu for the module. What it returns # it will be added up to the interface's menu. It should be # overriden by subclasses as needed sub menu { # default empty implementation return undef; } # Method: widgets # # Return the widget names for the module. It should be overriden by # subclasses as needed # # Returns: # # An array of hashes containing keys 'title' and 'widget', 'title' being the # title of the widget and 'widget' a function that can fill an # EBox::Dashboard::Widget that will be passed as a parameter # # It can optionally have the key 'default' set to 1 to have the widget # added by default to the dashboard the first time it's seen. # # It can also contain a 'parameter' key which will be passed as parameter to # the widget function. This is intended to allow the dynamic creation of # several widgets. # sub widgets { # default empty implementation return {}; } # Method: widget # # Return the appropriate widget if exists or undef otherwise # # Parameters: # name - the widget name # # Returns: # # with the appropriate widget # sub widget { my ($self, $name) = @_; my $widgets = $self->widgets(); my $winfo = $widgets->{$name}; if(defined($winfo)) { my $widget = new EBox::Dashboard::Widget($winfo->{'title'},$self->{'name'},$name); #fill the widget $widget->{'module'} = $self->{'name'}; $widget->{'default'} = $winfo->{'default'}; $widget->{'order'} = $winfo->{'order'}; my $wfunc = $winfo->{'widget'}; &$wfunc($self, $widget, $winfo->{'parameter'}); return $widget; } else { return undef; } } # Method: statusSummary # # Return the status summary for the module. What it returns will # be added up to the common status summary page. It should be overriden by # subclasses as needed. # # Returns: # # - the summary status for the module # sub statusSummary { # default empty implementation return undef; } # Method: package # # Returns the package name # # Returns: # # strings - package name # sub package { my ($self) = @_; my $name = $self->{name}; if ($name eq any((EBox::Global::CORE_MODULES()))) { return 'zentyal'; } else { return "zentyal-$name"; } } # Method: version # # Returns the package version # # Returns: # # strings - package version # sub version { my ($self) = @_; unless (defined ($self->{version})) { my $package = $self->package(); $package = 'zentyal-core' if ($package eq 'zentyal'); my @output = `dpkg-query -W $package`; foreach my $line (@output) { if ($line =~ m/^$package\s+([\d.]+)/) { $self->{version} = $1; last; } } } return $self->{version}; } # Method: wizardPages # # Return an array ref containin the wizard pages for the module. It should # be overriden by subclasses as needed # # Returns: # # An array ref of URL's of wizard pages for this module. This pages # must be implemented using WizardPage as base class. # # Example: # [ # { # page => '/Module/Wizard/Page' # order => 201 # }, # .... # ] # # sub wizardPages { # default implementation: no wizards return []; } # Method: appArmorProfiles # # Return the AppArmor profiles for this module # # There are two possible kinds of solutions: # # - Overwrite the distro AppArmor profile using a mason template # # - Use local/binary not to overwrite the distro AppArmor profile # but adding, normally, new directories to access/write/execute # # Returns: # # Array ref - containing hash ref as elements with the following # keys: # # binary - String the binary to set an AppArmor profile # # local - Boolean indicating if we use local implementation or # overwrite the distro one # # file - String the path for the new AppArmor profile. If it is a # template, it is relative to stubs path. If not, then it # is relative to schemas path # # params - Array ref the parameters if it is a mason template # # Default implementation is to have no profiles # sub appArmorProfiles { return []; } # Method: pidRunning # # Checks if a PID is running # # Parameters: # # pid - PID number # # Returns: # # boolean - True if it's running , otherise false sub pidRunning { my ($self, $pid) = @_; my $t = new Proc::ProcessTable; foreach my $proc (@{$t->table}) { ($pid eq $proc->pid) and return 1; } return undef; } # Method: pidFileRunning # # Given a file holding a PID, it gathers it and checks if it's running # # Parameters: # # file - file name # # Returns: # # boolean - True if it's running , otherise false # sub pidFileRunning { my ($self, $file) = @_; my $pid; try { my $output = EBox::Sudo::silentRoot("cat $file"); if (@{$output}) { ($pid) = @{$output}[0] =~ m/(\d+)/; } } otherwise { $pid = undef; }; unless ($pid) { return undef; } return $self->pidRunning($pid); } # Method: _preSetConf # # Base method which is called before _setConf. It should be overriden # by subclasses if you need something to be done before _setConf is run # sub _preSetConf { # default empty implementation. It should be overriden by subclasses as # needed } sub _hook { my ($self, $type, @params) = @_; my $hookfile = EBox::Config::etc() . "hooks/" . $self->{'name'} . "." . $type; if (-x "$hookfile") { my $log = EBox::logger; my $command = $hookfile . " " . join(" ", @params); $log->info("Running hook: " . $command); EBox::Sudo::root("$command"); } } sub _preSetConfHook { my ($self) = @_; $self->_hook('presetconf'); } sub _postSetConfHook { my ($self) = @_; $self->_hook('postsetconf'); } # Method: _setConf # # Base method to write the configuration. It should be overriden # by subclasses as needed # sub _setConf { # default empty implementation. It should be overriden by subclasses as # needed } # Method: _setAppArmorProfiles # # Set the apparmor profiles if AppArmor is installed and the module # has configured profiles overriding # sub _setAppArmorProfiles { my ($self) = @_; if ( -x APPARMOR_PARSER ) { foreach my $profile ( @{$self->appArmorProfiles()} ) { $profile->{params} = [] unless ($profile->{params}); my $targetProfile = APPARMOR_D . $profile->{binary}; if ( $profile->{local} ) { $targetProfile = APPARMOR_D . 'local/' . $profile->{binary}; } if ( $profile->{file} =~ /\.mas$/ ) { if ( $self->can('writeConfFile') ) { $self->writeConfFile($targetProfile, $profile->{file}, $profile->{params}); } else { writeConfFileNoCheck($targetProfile, $profile->{file}, $profile->{params}); } } else { my $baseDir = EBox::Config::scripts() . 'apparmor/'; EBox::Sudo::root("install -m 0644 $baseDir $targetProfile"); } # Reload the parser EBox::Sudo::root(APPARMOR_PARSER . ' --write-cache --replace ' . APPARMOR_D . $profile->{binary}); } } } # Method: _regenConfig # # Base method to regenerate configuration. It should be overriden # by subclasses as needed # sub _regenConfig { my ($self) = @_; my @params = (@_); shift(@params); $self->_preSetConf(@params); $self->_preSetConfHook(); $self->_setConf(@params); $self->_setAppArmorProfiles(); $self->_postSetConfHook(); } # Method: _writeFileCreateTmpFile # # Helper method that creates a temporary file for writeFile* methods. # sub _writeFileCreateTmpFile { my $oldUmask = umask 0007; my ($fh,$tmpfile); try { ($fh,$tmpfile) = tempfile(DIR => EBox::Config::tmp); unless($fh) { throw EBox::Exceptions::Internal( "Could not create temp file in " . EBox::Config::tmp); } } finally { umask $oldUmask; }; return ($fh, $tmpfile); } # Method: _writeFileSave # # Helper method that permanently saves the files created by writeFile # methods. # # # Parameters: # # tmpfile - file where changes are temporary stored # file - file where changes should be saved # defaults - mode, uid and gid for the final file (optional) # sub _writeFileSave # (tmpfile, file, defaults) { my ($tmpfile, $file, $defaults) = @_; my $mode; my $uid; my $gid; if ((not defined($defaults)) and (-e $file) and (my $st = EBox::Sudo::stat($file))) { $mode= sprintf("%04o", $st->mode & 07777); $uid = $st->uid; $gid = $st->gid; } else { defined $defaults or $defaults = {}; $mode = exists $defaults->{mode} ? $defaults->{mode} : '0644'; $uid = exists $defaults->{uid} ? $defaults->{uid} : 0; $gid = exists $defaults->{gid} ? $defaults->{gid} : 0; } my @commands; push (@commands, "/bin/mv $tmpfile '$file'"); push (@commands, "/bin/chmod $mode '$file'"); push (@commands, "/bin/chown $uid.$gid '$file'"); EBox::Sudo::root(@commands); } # Method: writeConfFileNoCheck # # It executes a given mason component with the passed parameters over # a file. It becomes handy to set configuration files for services. # Also, its file permissions will be kept. # It is called as class method. # XXX : the correct behaviour will be to throw exceptions if file will not be stated and no defaults are provided. It will provide hardcored defaults instead because we need to be backwards-compatible # # # Parameters: # # file - file name which will be overwritten with the execution output # component - mason component # params - parameters for the mason component. Optional. Defaults to no parameters # defaults - a reference to hash with keys mode, uid and gid. Those values will be used when creating a new file. (If the file already exists the existent values of these parameters will be left untouched) # sub writeConfFileNoCheck # (file, component, params, defaults) { my ($file, $compname, $params, $defaults) = @_; my ($fh, $tmpfile) = _writeFileCreateTmpFile(); my $interp = HTML::Mason::Interp->new( comp_root => EBox::Config::stubs, out_method => sub { $fh->print($_[0]) }); my $comp; try { my $stub = EBox::Config::stubs() . $compname; my $customStub = EBox::Config::etc() . "stubs/$compname"; if (-f $customStub) { try { EBox::info("Using custom template for $file: $customStub"); $comp = $interp->make_component(comp_file => $customStub); } otherwise { my $ex = shift; EBox::error("Falling back to default $stub due to exception " . "processing custom template $customStub: $ex"); $comp = $interp->make_component(comp_file => $stub); }; } else { $comp = $interp->make_component(comp_file => $stub); } } otherwise { my $ex = shift; throw EBox::Exceptions::Internal("Template $compname failed with $ex"); }; # Workaround bogus mason warnings, redirect stderr to /dev/null to not # scare users. New mason version fixes this issue my $old_stderr; my $tmpErr = EBox::Config::tmp() . 'mason.err'; open($old_stderr, ">&STDERR"); open(STDERR, ">$tmpErr"); $interp->exec($comp, @{$params}); $fh->close(); open(STDERR, ">&$old_stderr"); _writeFileSave($tmpfile, $file, $defaults); } # Method: writeFile # # Writes a file with the given data, owner and permissions. # # Parameters: # # file - file name which will be overwritten with the execution output # data - data to write in the file # defaults - a reference to hash with keys mode, uid and gid. Those values will be used when creating a new file. (If the file already exists the existent values of these parameters will be left untouched) # sub writeFile # (file, data, defaults) { my ($file, $data, $defaults) = @_; my ($fh, $tmpfile) = _writeFileCreateTmpFile(); $fh->print($data); $fh->close(); _writeFileSave($tmpfile, $file, $defaults); } # Method: report # # returns the reporting information provided by the module # # Returns: # hash reference with the report information # # If not overriden by the subclasses it will return undef sub report { my ($self) = @_; return undef; } # Method: runMonthlyQuery # # Runs a query in the database for all the months in t erange, # organizing the data for its use in the report # # Parameters: # # beg - initial year-month (i.e., '2009-10') # end - final year-month # query - SQL query without any dates # options - hash containing options for the processing of the results # key - if key is provided, multiple rows will be processed and # hashed by the content of the key field # # Returns: # hash reference with the report information # # If not overriden by the subclasses it will return undef sub runMonthlyQuery { my ($self, $beg, $end, $query, $options) = @_; defined($options) or $options = {}; my $key = $options->{'key'}; my $db = EBox::DBEngineFactory::DBEngine(); my @fields = @{ $self->_monthlyQueryDataFields($db, $query, $key) }; my $data = $self->_emptyMonthlyQueryData($db, $beg, $end, $query, $options, \@fields); # use Data::Dumper; # print "EMPTY DATA " . Dumper($data) . "\n"; # return $data; my ($begyear, $begmonth) = split('-', $beg); my ($endyear, $endmonth) = split('-', $end); my $year = $begyear; my $month = $begmonth; my $orig_where = $query->{'where'}; my $nMonth = 0; while ( ($year < $endyear) or (($year == $endyear) and ($month <= $endmonth)) ) { my $date_where = "date >= '$year-$month-01 00:00:00' AND " . "date < date '$year-$month-01 00:00:00' + interval '1 month'"; my $new_where; if (defined($orig_where)) { $new_where = "$orig_where AND $date_where"; } else { $new_where = "$date_where"; } $query->{'where'} = $new_where; my $results = $db->query_hash($query); if (@{$results}) { if (defined($key)) { for my $r (@{$results}) { my $keyname = $r->{$key}; for my $f (@fields) { my $val = $r->{$f}; if (defined $val) { if ( Scalar::Util::looks_like_number($val) ) { $val = $val + 0; } $data->{$keyname}->{$f}->[$nMonth] = $val; } } } } else { for my $r (@{$results}) { for my $f (@fields) { my $val = $r->{$f}; if (defined $val) { if ( Scalar::Util::looks_like_number($val) ) { $val = $val + 0; } $data->{$f}->[$nMonth] = $val; } } } } } $nMonth += 1; if($month == 12) { $month = 1; $year++; } else { $month++; } } return $data; } sub _monthlyQueryDataFields { my ($self, $db, $query, $key) = @_; my %fieldsQuery = %{ $query }; $fieldsQuery{limit} = 1; my @fields; my $resultFieldsQuery = $db->query_hash(\%fieldsQuery); if (defined $resultFieldsQuery and (exists $resultFieldsQuery->[0])) { @fields = (keys %{@{$resultFieldsQuery}[0]}); } if ($key) { @fields = grep { $_ ne $key} @fields; } return \@fields; } sub _emptyMonthlyQueryData { my ($self, $db, $beg, $end, $query, $options, $fields_r) = @_; my ($begyear, $begmonth) = split('-', $beg); my ($endyear, $endmonth) = split('-', $end); my $length = ($endyear - $begyear)*12 + ($endmonth - $begmonth); my $makeResults_r = sub { return [ map { 0 } (0 .. $length) ] }; my $key = $options->{'key'}; my @keys; if ($key) { my $select = "SELECT DISTINCT "; if ($options->{keyGenerator}) { $select .= $options->{keyGenerator}; } else { $select.= $key; } my $sql = "$select FROM " . $query->{from} . ";"; my $res = $db->query($sql); @keys = map { $_->{ $key } } @{ $res }; } my @fields = @{ $fields_r }; my $data = {}; if ($key) { foreach my $key (@keys) { $data->{$key} = {}; foreach my $field (@fields) { $data->{$key}->{$field} = $makeResults_r->(); } } } else { foreach my $field (@fields) { $data->{$field} = $makeResults_r->(); } } return $data; } sub runQuery { my ($self, $beg, $end, $query) = @_; my $data = {}; my $db = EBox::DBEngineFactory::DBEngine(); my ($begyear, $begmonth) = split('-', $beg); my ($endyear, $endmonth) = split('-', $end); my $orig_where = $query->{'where'}; my $date_where = "date >= '$begyear-$begmonth-01 00:00:00' AND " . "date < date '$endyear-$endmonth-01 00:00:00' + interval '1 month'"; my $new_where; unless (defined($query->{'options'}) and defined($query->{'options'}->{'no_date_in_where'}) and $query->{'options'}->{'no_date_in_where'}) { if (defined($orig_where)) { $new_where = "$orig_where AND $date_where"; } else { $new_where = "$date_where"; } $query->{'where'} = $new_where; } $query->{'from'} =~s/_date_/$date_where/g; my $results = $db->query_hash($query); if (@{$results}) { my @fields = keys(%{@{$results}[0]}); for my $f (@fields) { $data->{$f} = []; } for my $r (@{$results}) { for my $f (@fields) { my $val = $r->{$f}; if ( Scalar::Util::looks_like_number($val) ) { $val = $val + 0; } push(@{$data->{$f}}, $val); } } return $data; } return undef; } sub runCompositeQuery { my ($self, $beg, $end, $query, $key, $next_query) = @_; my $data = {}; my $db = EBox::DBEngineFactory::DBEngine(); my ($begyear, $begmonth) = split('-', $beg); my ($endyear, $endmonth) = split('-', $end); my $orig_where = $query->{'where'}; my $date_where = "date >= '$begyear-$begmonth-01 00:00:00' AND " . "date < date '$endyear-$endmonth-01 00:00:00' + interval '1 month'"; my $new_where; if (defined($orig_where)) { $new_where = "$orig_where AND $date_where"; } else { $new_where = "$date_where"; } $query->{'where'} = $new_where; my $results = $db->query_hash($query); my @keys = map { $_->{$key} } @{$results}; (@keys) or return undef; $orig_where = $next_query->{'where'}; for my $k (@keys) { $data->{$k} = {}; my $date_where = "date >= '$begyear-$begmonth-01 00:00:00' AND " . "date < date '$endyear-$endmonth-01 00:00:00' + interval '1 month'"; my $new_where; if (defined($orig_where)) { $new_where = "$orig_where AND $date_where"; } else { $new_where = "$date_where"; } my $regex = '_' . $key . '_'; $new_where =~ s/$regex/$k/; $next_query->{'where'} = $new_where; $results = $db->query_hash($next_query); if (@{$results}) { my @fields = keys(%{@{$results}[0]}); for my $f (@fields) { $data->{$k}->{$f} = []; } for my $r (@{$results}) { for my $f (@fields) { my $val = $r->{$f}; if ( Scalar::Util::looks_like_number($val) ) { $val = $val + 0; } push(@{$data->{$k}->{$f}}, $val); } } } } return $data; } # get the start date as timestamp for a new consolidation sub _consolidateReportStartDate { my ($self, $db, $target_table, $query) = @_; my $res = $db->query_hash({ 'select' => 'EXTRACT(EPOCH FROM last_date) AS date', 'from' => 'report_consolidation', 'where' => "report_table = '$target_table'" }); my $date; if(@{$res}) { my $row = shift(@{$res}); $date = $row->{'date'}; $date += 1; # we start consolidation in the next second } else { # get a reasonable first date from timestamp of source tables $res = $self->_unionQuery($db, { 'select' => 'EXTRACT(EPOCH FROM timestamp) AS date', 'from' => $query->{'from'}, 'order' => "timestamp", 'limit' => 1 }); my $row = shift(@{$res}); #if there is no rows in source tables for consolidation, return undef defined($row) or return undef; $date = $row->{date}; #later we call update so we need to have something inserted $db->unbufferedInsert('report_consolidation', { 'report_table' => $target_table, 'last_date' => 'epoch' }); } return $date; } sub consolidateReportFromLogs { my ($self) = @_; my $queries = $self->consolidateReportQueries(); return $self->_consolidateReportFromDB( $queries, \&_consolidationValuesForMonth ); } sub _consolidateReportFromDB { my ($self, $queries, $monthlyValuesMethod_r) = @_; my $db = EBox::DBEngineFactory::DBEngine(); my $consolidationStartTime = time(); my $gmConsolidationStartTime = gmtime($consolidationStartTime); for my $q (@{$queries}) { my $target_table = $q->{'target_table'}; my %quote = exists $q->{quote} ? %{ $q->{quote} } : (); my $query = $q->{'query'}; my $date = $self->_consolidateReportStartDate($db, $target_table, $query); $date or next; my @time = localtime($date); my $year = $time[5]+1900; my $month = $time[4]+1; my $day = $time[3]; my $hour = $time[2] . ':' . $time[1] . ':' . $time[0]; my $timeTs = $date; # no tz my $curTimeTs = $consolidationStartTime; # no tz my @curtime = localtime($curTimeTs); my $curyear = $curtime[5]+1900; my $curmonth = $curtime[4]+1; # precalculed query data if ( exists $query->{'where'} ) { $query->{orig_where} = $query->{'where'}; } $query->{from_tables} = [ split '\s*,\s*', $query->{from} ]; while ( $timeTs < $curTimeTs) { my $beginTime = "$year-$month-$day $hour"; my $beginMonth = "$year-$month-01 00:00:00"; my $results = $monthlyValuesMethod_r->($self, $db, $query, $beginTime, $beginMonth); if (@{$results}) { my $updateOverwrite = 0; if (exists $query->{updateMode} and ($query->{updateMode} eq 'overwrite')) { $updateOverwrite = 1; } # query to check if the record exists already my @fields = keys(%{@{$results}[0]}); # these are the fields which identify a line as not repeatable my @identityFields; if (exists $query->{group}) { my @groupFields = map { # to get column names when they are qualified with table my @parts = split '\.', $_; $parts[-1] } split(/ *, */,$query->{'group'}); push @identityFields, @groupFields; } if (exists $query->{key}) { push @identityFields, $query->{key}; } for my $r (@{$results}) { my @from = ($target_table); my @where; for my $f (@identityFields) { if (exists $r->{$f} and defined $r->{$f}) { my $value; if ($quote{$f}) { $value = $db->quote($r->{$f}); } else { $value = q{'} . $r->{$f} . q{'}; } push(@where, "$f=$value"); } # try to detect another required 'from' this will # fail if column name does nto specify table if # there is one of more dot in the nmae it will fail too my (@portions) = split '\.', $f; if (@portions == 2) { push @from, $portions[0]; } } push(@where, "date = '$beginMonth'"); my $res = $db->query_hash({ 'from' => join(',', @from), 'where' => join(' AND ', @where) }); if (@{$res}) { # record exists, we will update it my $row = shift(@{$res}); my $new_row = {}; for my $k (keys %$r) { if (!grep(/^$k$/, @identityFields)) { if ($updateOverwrite) { my $newValue = $r->{$k}; if ( $quote{$k} ) { $newValue = $db->quote($newValue); } $new_row->{$k} = $newValue; } else { # sum values avoiding undef warnings $new_row->{$k} = 0; $new_row->{$k} += $row->{$k} if defined $row->{$k}; $new_row->{$k} += $r->{$k} if defined $r->{$k}; } } } $db->update($target_table, $new_row, \@where); } else { # record does not exists, insert it $r->{'date'} = $beginMonth; $db->unbufferedInsert($target_table, $r); } } # update last consolidation time $db->update('report_consolidation', { 'last_date' => "'$gmConsolidationStartTime'" }, [ "report_table = '$target_table'" ], ); } # only the first loop could have a hour/day different than the 00:00:00/1 $hour = '00:00:00'; $day = 1; if($month == 12) { $month = 1; $year++; } else { $month++; } # update timeTs for the next month $timeTs = timelocal(0,0,0, 1,($month-1),($year-1900)); } } } sub _consolidationValuesForMonth { my ($self, $db, $query, $beginTime, $beginMonth) = @_; my @dateWherePortions; foreach my $table (@{ $query->{from_tables} }) { push @dateWherePortions, "($table.timestamp >= '$beginTime' AND " . "$table.timestamp < date '$beginMonth' + interval '1 month')"; } my $date_where = join ' AND ', @dateWherePortions; my $new_where; if (exists $query->{orig_where}) { $new_where = $query->{orig_where} . " AND $date_where"; } else { $new_where = "$date_where"; } $query->{'where'} = $new_where; my $results = $db->query_hash($query); return $results; } sub _lastConsolidationValuesForMonth { my ($self, $db, $origQuery, $beginTime, $beginMonth) = @_; my $query = { %{ $origQuery } }; my @dateWherePortions; foreach my $table (@{ $query->{from_tables} }) { push @dateWherePortions, "($table.timestamp >= '$beginTime' AND " . "$table.timestamp < date '$beginMonth' + interval '1 month')"; } my $date_where = join ' AND ', @dateWherePortions; if (exists $query->{where}) { $query->{where} = $query->{where} . " AND $date_where"; } else { $query->{where} = "$date_where"; } $query->{order} = 'timestamp DESC'; $query->{limit} = 1; my $key = $query->{key}; if (defined $key) { my $from = join ', ', @{ $query->{from_tables} }; my $sql = qq{SELECT DISTINCT $key FROM $from WHERE }. $query->{where}; my $keyResults = $db->query($sql); my @keyValues = map { $_->{$key} } @{ $keyResults }; my @results; my $origWhere = $query->{where}; foreach my $keyValue (@keyValues) { $query->{where} = $origWhere . " AND $key = '$keyValue'"; push @results, @{ $db->query_hash($query) }; } return \@results; } else { return $db->query_hash($query); } } # Method: consolidateReportQueries # # This method defines how to consolidate for the report the database logs of this # module, using an array including an entry for each table, such as: # # { # 'target_table' => 'samba_access_report', # 'query' => { # 'select' => 'username, COUNT(event) AS operations', # 'from' => 'samba_access', # 'group' => 'username' # }, # 'quote' => { username => 1}, # }, # # # # # 'target_table' defines the table where the consolidated data will be stored. # The data will considerate using the provided query. The format of the query i # the same of EBox::MyDBEngine::query_hash. But with the following caveats: # # # # - key : this signals a single field as part of the key fields of a row. The # other keyfields are the ones from a possible group clause. The query needs # either a group clause or a key option to be able to consolidate correctly. # # - updateMode : this signals what to do when you need to update a row. A row # will be updated instead of inserted when its date and key fields (group + key) # are identical. Available update modes: # # - sum: the non-key field are added tohether (default) # - overwrite: the non-key fields are overwritten with the last value # # 'quote' means which fields should be quoted to escape special characters # in strigns. No present fields default to false # # This data will be used to call consolidateReportFromLogs sub consolidateReportQueries { return []; } sub logReportInfo { return []; } # Method: consolidateReportInfoQueries # # This method is used to consolidate data from data tables which has been # populated by the logReportInfo method. It call consolidateReportInfoQueries for # that. # # The difference between consolidateReportFromLogs and # consolidateReportInfoQueries is that the last one only takes the latest value # or the latest value for each of the values of the 'key' field. # # Another difference is that the queries have default update mode the 'overwrite' # mode instead o 'add' sub consolidateReportInfoQueries { return []; } sub consolidateReportInfo { my ($self) = @_; my $queries = $self->consolidateReportInfoQueries(); # putting the default update mode foreach my $q (@{ $queries}) { if (not exists $q->{query}->{updateMode}) { $q->{query}->{updateMode} = 'overwrite'; } } return $self->_consolidateReportFromDB( $queries, \&_lastConsolidationValuesForMonth ); } # if this is neccesary in more places we will move it to MyDbEngine sub _unionQuery { my ($self, $dbengine, $orig_query) = @_; my %query = %{ $orig_query }; my @tables = split '\s*,\s*', $query{from}; my @tableQueries; foreach my $table (@tables) { $query{from} = $table; my $tableSql = '(' . $dbengine->query_hash_to_sql(\%query, 0) . ')'; push @tableQueries, $tableSql; } my $sql = join ' UNION ' , @tableQueries; $sql .= ';'; return $dbengine->query($sql); } # Method: global # # Gets an EBox::Global instance with the same read-only status as the module # # As EBox::Module::Base does not store config, this always returns a regular instance # sub global { my ($self) = @_; return EBox::Global->getInstance(); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Module/Config.pm0000664000000000000000000004172212017102272017241 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Module::Config; use strict; use warnings; use base 'EBox::Module::Base'; use EBox::Config; use EBox::Global; use EBox::Exceptions::Internal; use EBox::Gettext; use EBox::Types::File; use EBox::Config::Redis; use EBox::Model::Manager; use File::Basename; use Test::Deep qw(eq_deeply); sub _create { my $class = shift; my %opts = @_; my $ro = delete $opts{ro}; my $self = $class->SUPER::_create(@_); if (($self->name ne "global") && $ro) { $self->{ro} = 1; } bless($self, $class); $self->{redis} = EBox::Config::Redis->instance(); unless (defined($self->{redis})) { throw EBox::Exceptions::Internal("Error getting Redis client"); } return $self; } sub model { my ($self, $name) = @_; EBox::Model::Manager->instance()->_component('model', $self, $name); } sub composite { my ($self, $name) = @_; EBox::Model::Manager->instance()->_component('composite', $self, $name); } # Method: models # # Get the models from the model manager # # Returns: # # Array ref - containing the model instances for this module # sub models { my ($self) = @_; EBox::Model::Manager->instance()->models($self); } # Method: composites # # Get the composites from the model manager # # Returns: # # Array ref - containing the composite instances for this module # sub composites { my ($self) = @_; EBox::Model::Manager->instance()->composites($self); } # we override aroundRestoreconfig to restore conf data before restoring config sub aroundRestoreConfig { my ($self, $dir, @extraOptions) = @_; $self->_load_from_file($dir); $self->restoreFilesFromArchive($dir); $self->restoreConfig($dir, @extraOptions); } sub _state_bak_file_from_dir { my ($self, $dir) = @_; $dir =~ s{/+$}{}; my $file = "$dir/" . $self->name . ".state"; return $file; } # load config entries from a file sub _load_from_file { my ($self, $dir, $dst) = @_; ($dir) or $dir = EBox::Config::conf; my $file = $self->_bak_file_from_dir($dir); my $src = $self->name() . '/conf'; if (not $dst) { $dst = $self->_key(''); } $self->_load_redis_from_file($file, $src, $dst); } sub _load_state_from_file { my ($self, $dir, $dst) = @_; ($dir) or $dir = EBox::Config::conf; my $file = $self->_state_bak_file_from_dir($dir); my $src = $self->_st_key(''); if (not $dst) { $dst = $self->_st_key(''); } $self->_load_redis_from_file($file, $src, $dst); } sub _load_redis_from_file { my ($self, $file, $src, $dst) = @_; if (not -f $file) { EBox::error("Backup file '$file' missing for module " . $self->name); return; } elsif (not -r $file) { EBox::error("Cannot read backup file '$file' for module " . $self->name); } # Import to tmp dir and convert paths to $dst dest $self->{redis}->import_dir_from_file($file, 'tmp'); $self->{redis}->backup_dir('tmp/' . $src, $dst); $self->{redis}->delete_dir('tmp'); } sub aroundDumpConfig { my ($self, $dir, @options) = @_; $self->_dump_to_file($dir); # dump also state, it will not be restored as default $self->_dump_state_to_file($dir); $self->backupFilesInArchive($dir); $self->dumpConfig($dir, @options); } # dumps GConf entries to a file in the dir specified sub _dump_to_file { my ($self, $dir) = @_; ($dir) or $dir = EBox::Config::conf; my $key = $self->name() . '/conf'; my $file = $self->_bak_file_from_dir($dir); $self->{redis}->export_dir_to_file($key, $file); } sub _dump_state_to_file { my ($self, $dir) = @_; ($dir) or $dir = EBox::Config::conf; my $key = $self->_st_key(); my $file = $self->_state_bak_file_from_dir($dir); $self->{redis}->export_dir_to_file($key, $file); } sub isReadOnly { my $self = shift; return $self->{ro}; } # Method: revokeConfig # # Dismisses all changes done since the first write or delete operation. # sub revokeConfig { my ($self) = @_; # No sense to revoke config on a read-only instance return if ($self->{ro}); my $global = EBox::Global->getInstance(); $global->modIsChanged($self->name) or return; $self->modelsRevokeConfig(); # Disabled until method si reimplemented # $self->_revokeConfigFiles(); $global->modRestarted($self->name); $self->_copy_from_ro(); } sub _saveConfig { my $self = shift; if ($self->{ro}) { throw EBox::Exceptions::Internal("tried to save a read-only" . " module: " . $self->name() . "\n"); } $self->_dump_to_file(); $self->modelsSaveConfig(); $self->_copy_to_ro(); # Disabled until method si reimplemented # $self->_saveConfigFiles(); } sub _copy_to_ro { my ($self) = @_; $self->_copy('conf', 'ro'); } sub _copy_from_ro { my ($self) = @_; $self->_copy('ro', 'conf'); } sub _copy { my ($self, $src, $dst) = @_; my $name = $self->name(); $self->{redis}->backup_dir("$name/$src", "$name/$dst"); } # TODO: remove all the low-level _change calls here if at some point everything is modelized sub _change { my ($self) = @_; my $global = EBox::Global->getInstance($self->{ro}); $global->modChange($self->name); } sub _key { my ($self, $key) = @_; my $dir = $self->{ro} ? 'ro' : 'conf'; my $ret = $self->{name} . "/$dir"; if ($key) { $ret .= "/$key"; } return $ret; } sub _st_key { my ($self) = @_; return $self->{name} . '/state'; } sub _ro_key { my ($self, $key) = @_; return $self->{name} . "/ro/$key"; } ############# sub get_state { my ($self) = @_; $self->{redis}->get($self->_st_key(), {}); } sub set_state { my ($self, $hash) = @_; $self->{redis}->set($self->_st_key(), $hash); } sub st_entry_exists { my ($self, $key) = @_; my $state = $self->get_state(); return exists $state->{$key}; } ############# sub redis { my ($self) = @_; return $self->{redis}; } ############# # Method: get_bool # # Returns the value of a boolean key. # # Parameters: # # key - # # Returns: # # boolean - key's value# # sub get_bool { my ($self, $key) = @_; return $self->get($key, 0); } sub st_get_bool { my ($self, $key) = @_; return $self->st_get($key, 0); } ############# # Method: set_bool # # Sets a boolean key # # Parameters: # # key - key to set # value - value # sub set_bool { my ($self, $key, $val) = @_; $self->set($key, $val ? 1 : 0); } sub st_set_bool { my ($self, $key, $val) = @_; $self->st_set($key, $val); } ############# # Method: get_int # # Returns the value of an integer key. # # Parameters: # # key - # # Returns: # # integer - key's value # sub get_int { my ($self, $key) = @_; return $self->get($key); } sub st_get_int { my ($self, $key) = @_; return $self->st_get($key); } ############# # Method: set_int # # Sets an integer key # # Parameters: # # key - key to set # val - value # sub set_int { my ($self, $key, $val) = @_; $self->set($key, $val); } sub st_set_int { my ($self, $key, $val) = @_; $self->st_set($key, $val); } ############# # Method: get_string # # Returns the value of an string key. # # Parameters: # # key - key name # # Returns: # # string - key's value # sub get_string { my ($self, $key) = @_; return $self->get($key); } sub st_get_string { my ($self, $key) = @_; return $self->st_get($key); } ############# # Method: set_string # # Sets a string key # # Parameters: # # key - key to set # value - value # sub set_string { my ($self, $key, $val) = @_; $self->set($key, $val); } sub st_set_string { my ($self, $key, $val) = @_; $self->st_set($key, $val); } ############# # Method: get_list # # Returns the value of an string key. # # Parameters: # # key - # # Returns: # # It returns the list of values stored in the key . # # ref to an array - the list of values # sub get_list { my ($self, $key) = @_; return $self->get($key, []); } sub st_get_list { my ($self, $key) = @_; return $self->st_get($key, []); } ############# sub set_hash { my ($self, $key, $value) = @_; $key = $self->_key($key); $self->redis->set($key, $value); } sub get_hash { my ($self, $key) = @_; return $self->get($key, {}); } ############# # Method: get # # Returns the value of a key # # Parameters: # # key - # # Returns: # # Returns a # sub get { my ($self, $key, $defaultValue) = @_; $key = $self->_key($key); return $self->redis->get($key, $defaultValue); } sub st_get { my ($self, $key, $defaultValue) = @_; my $state = $self->get_state(); if (not exists $state->{$key}) { return $defaultValue; } return $state->{$key}; } # Method: set # # Set an arbitrary key and mark the module as changed if not readonly # # Parameters: # # key - string with the key # value - scalar or ref with the value # sub set { my ($self, $key, $value) = @_; $self->_set($key, $value); # Only mark as changed if stored value in ro is different unless ($self->{ro}) { my $oldvalue = $self->{redis}->get($self->_ro_key($key)); unless (eq_deeply($value, $oldvalue)) { $self->_change(); } } } # Method: _set # # Set an arbitrary key without marking the module as changed # # Parameters: # # key - string with the key # value - scalar or ref with the value # sub _set { my ($self, $key, $value) = @_; $self->{redis}->set($self->_key($key), $value); } sub st_set { my ($self, $key, $value) = @_; my $state = $self->get_state(); $state->{$key} = $value; $self->set_state($state); } ############# # Method: unset # # Unset a given key # # Parameters: # # key - # # sub unset { my ($self, $key) = @_; $key = $self->_key($key); $self->redis->unset($key); $self->_change() unless $self->{ro}; } sub st_unset { my ($self, $key) = @_; my $state = $self->get_state(); delete $state->{$key}; $self->set_state($state); } ############# # Method: set_list # # Sets a list of values. The type for the values is also specified # # Parameters: # # key - # type - type for each value # values - (ref to an array) proper list of values # sub set_list { my ($self, $key, $type, $val) = @_; $self->set($key, $val); } sub st_set_list { my ($self, $key, $type, $val) = @_; $self->st_set($key, $val); } ############# # Method: delete_dir # # Removes a whole directory # # Parameters: # # key - directory to be removed # sub delete_dir # (key) { my ($self, $dir) = @_; $dir = $self->_key($dir); $self->redis->delete_dir($dir); $self->_change() unless $self->{ro}; } ############# # files stuff we have to put this stuff in confmodule bz if we put into models # we lost data due to the parent/child relations sub _filesToRemoveIfCommittedDir { my ($self) = @_; return 'filesToRemoveIfComitted'; } sub _filesToRemoveIfRevokedDir { my ($self) = @_; return 'filesToRemoveIfRevoked'; } sub addFileToRemoveIfCommitted { my ($self, $file) = @_; my $dir = $self->_filesToRemoveIfCommittedDir(); $self->_addFileToList($file, $dir); } sub addFileToRemoveIfRevoked { my ($self, $file) = @_; my $dir = $self->_filesToRemoveIfRevokedDir(); $self->_addFileToList($file, $dir); } sub _fileListDirs { my ($self) = @_; my @dirs = ( $self->_filesToRemoveIfCommittedDir(), $self->_filesToRemoveIfRevokedDir(), ); return \@dirs; } sub _addFileToList { my ($self, $file, $dir) = @_; my $key = $file; $key =~ s{/}{N1N}g; #escape path, we do not intend that we can unescape them #but we need a predecible value to remove repeated keys # remove references to file in another lists my @dirs = @{ $self->_fileListDirs() }; foreach my $listDir (@dirs) { my $listKey = $listDir . '/' . $key; $self->unset($listKey); } my $fullKey = $dir . '/' . $key; $self->set_string($fullKey, $file); } # FIXME: reimplement this #sub _fileList #{ # my ($self, $dir) = @_; # # # if (not $self->dir_exists($dir)) { # return []; # } # # my @files = map { # $self->get_string($_); # } @{$self->all_entries($dir)}; # # return \@files; #} #sub _saveConfigFiles #{ # my ($self) = @_; # my $dir = $self->_filesToRemoveIfCommittedDir(); # $self->_removeFilesFromList($dir); #} # FIXME: reimplement this #sub _clearFilesToRemoveLists #{ # my ($self) = @_; # # return; # # my @dirs = @{ $self->_fileListDirs() }; # # foreach my $dir (@dirs) { # if ($self->dir_exists($dir)) { # $self->delete_dir($dir); # } # } #} #sub _revokeConfigFiles #{ # my ($self) = @_; # # my $dir = $self->_filesToRemoveIfRevokedDir(); # $self->_removeFilesFromList($dir); #} #sub _removeFilesFromList #{ # my ($self, $dir) = @_; # # my @files = @{ $self->_fileList($dir) }; # foreach my $file ( @files ) { # my $backupPath = EBox::Types::File->backupPath($file); # my $noPreviousFilePath = EBox::Types::File->noPreviousFilePath($file); # # EBox::Sudo::root("rm -rf '$file' '$backupPath' '$noPreviousFilePath'"); # } # # $self->_clearFilesToRemoveLists(); #} # Method: modelsSaveConfig # # Method called when the conifguraiton of a modules is saved sub modelsSaveConfig { my ($self) = @_; # $self->modelsBackupFiles(); } # Method: modelsRevokeConfig # # Method called when the conifguraiton of a modules is revoked sub modelsRevokeConfig { my ($self) = @_; # $self->modelsRestoreFiles(); } # Method: backupFiles # # Make an actual configuration backup of all the files contained in the # models sub modelsBackupFiles { my ($self) = @_; # foreach my $model ( @{ $self->models() } ) { # if ($model->can('backupFiles')) { # $model->backupFiles(); # } # } } # Method: restoreFiles # # Restores the actual configuration backup of files in the models , thus # discarding the lasts changes in files sub modelsRestoreFiles { my ($self) = @_; # foreach my $model ( @{ $self->models() } ) { # if ($model->can('restoreFiles')) { # $model->restoreFiles(); # } # } } sub _filesArchive { my ($self, $dir) = @_; return "$dir/modelsFiles.tar"; } # Method: backupFilesInArchive # # Backup all the modules' files in a compressed archive in the given dir # This is used to create backups # # Parameters: # dir - directory where the archive will be stored sub backupFilesInArchive { my ($self, $dir) = @_; my @filesToBackup; foreach my $model ( @{ $self->models() } ) { if ($model->can('filesPaths')) { push @filesToBackup, @{ $model->filesPaths() }; } } @filesToBackup or return; my $archive = $self->_filesArchive($dir); my $firstFile = shift @filesToBackup; my $archiveCmd = "tar -C / -cf $archive --atime-preserve --absolute-names --preserve --same-owner $firstFile"; EBox::Sudo::root($archiveCmd); # we append the files one per one bz we don't want to overflow the command # line limit. Another approach would be to use a file catalog however I think # that for only a few files (typical situation for now) the append method is better foreach my $file (@filesToBackup) { $archiveCmd = "tar -C / -rf $archive --atime-preserve --absolute-names --preserve --same-owner $file"; EBox::Sudo::root($archiveCmd); } } # Method: restoreFilesFromArchive # # Restore all the module's file from the compressed archive in the given dir # This is used to restore backups # # Parameters: # dir - directory where the archive is stored sub restoreFilesFromArchive { my ($self, $dir) = @_; my $archive = $self->_filesArchive($dir); (-f $archive) or return; my $restoreCmd = "tar -C / -xf $archive --atime-preserve --absolute-names --preserve --same-owner"; EBox::Sudo::root($restoreCmd); } # Method: global # # Gets an EBox::Global instance with the same read-only status as the module # sub global { my ($self) = @_; return EBox::Global->getInstance($self->{ro}); } 1; zentyal-core-2.3.21+quantal1/src/EBox/LogObserver.pm0000664000000000000000000001750412017102272017041 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # # Class: EBox::LogObserver # # Those modules FIXME to process logs generated by their # daemon or servide must inherit from this class and implement # the given interface # package EBox::LogObserver; use strict; use warnings; use Perl6::Junction qw(any); sub new { my $class = shift; my $self = {}; bless($self, $class); return $self; } # Method: logHelper # # Returns: # # An object implementing EBox::LogHelper sub logHelper { return undef; } # Method: enableLog # # This method must be overriden by the class implementing this interface, # if it needs to enable or disable logging facilities in the services its # managing. # # Parameters: # # enable - boolean, true if it's enabled, false it's disabled # sub enableLog { } # Method: tableInfo # # This function returns an array of hash ref or a single hash # ref with these fields: # # - name: the printable name of the table # - tablename: the name of the database table associated with the module. # - titles: A hash ref with the table fields and theirs user read # translation. # - order: An array with table fields ordered. # # - consolidate: instructions for consolidation of information in # periodic tables. The instruction is a hash ref (see below) # # # Consolidate hash specification: # # The hash has as keys the name of the destination consolidation tables (it # will be concataned to time periods prefix as daily, hourly). The values # must be another hash ref with the consolidation parameters. The hash ref # has those fields: # - consolidateColumns:hash ref which a key for each column that should # be treated the value is a hash ref with more options: # * conversor: reference to a sub to convert the column value. The # function will be called with row value as first argument and # the row as second # * accummulate: sginals that the vaule of the column will be # accummulated and in which column will be accummulated. # It may be a string to specif a column or a sub ref which # returns the name of the column to be accummulated. In the # last case it is called with the vaule of the column and # the row as arguments. # * destination: for not-accummulate column this will be the column # which wil be store the value. Defaults to a column whith # the same name # - accummulateColumns: hash ref to signal which data base columns # will be used to accummulate numeric data from other columns. # The keys should be the name of the clumn and the values # the number will be used to autoincremet the column in each row. # Use zero if you don't want autoincrement. # The drfault is a column called count which autoincrements one unit # each time # # - filter: refrence to a method used to filter out rows, the method will # be supplied to a reference to a hash with the row values and # if it returns false the row would be excluded # - quote: hash ref which signals which columns should be quoted # to protect special string characters. The columns should # contain strings. # Not present columns default to false. # # Warning: # -use lowercase in column names sub tableInfo { throw EBox::Exceptions::NotImplemented; } # Method: humanEventMessage # # Given a row with the table description given by # it will return a human readable # message to inform admin using events. # # To be overriden by subclasses. Default behaviour is showing # every field name following by a colon and the value. # # Parameters: # # row - hash ref the row returned by # # Returns: # # String - the i18ned human readable message to send in an event # sub humanEventMessage { my ($self, $row) = @_; my @tableInfos; my $tI = $self->tableInfo(); if ( ref($tI) eq 'HASH' ) { EBox::warn('tableInfo() in ' . $self->name() . ' must return a reference to a ' . 'list of hashes not the hash itself'); @tableInfos = ( $tI ); } else { @tableInfos = @{ $tI }; } my $message = q{}; foreach my $tableInfo (@tableInfos) { next unless (exists($tableInfo->{events}->{$row->{event}})); foreach my $field (@{$tableInfo->{order}}) { if ( $field eq $tableInfo->{eventcol} ) { $message .= $tableInfo->{titles}->{$tableInfo->{eventcol}} . ': ' . $tableInfo->{events}->{$row->{$field}} . ' '; } else { my $rowContent = $row->{$field}; # Delete trailing spaces $rowContent =~ s{ \s* \z}{}gxm; $message .= $tableInfo->{titles}->{$field} . ": $rowContent "; } } } return $message; } # Method: reportUrls # # this return the module's rows for the SelectLog table. sub reportUrls { my ($self) = @_; # my $domain = $self->name(); my @tableInfos; my $ti = $self->tableInfo(); if (ref $ti eq 'HASH') { EBox::warn('tableInfo() in ' . $self->name . ' must return a reference to a list of hashes not the hash itself'); @tableInfos = ( $ti ); } else { @tableInfos = @{ $ti }; } my @urls; foreach my $tableInfo (@tableInfos) { my $index = $tableInfo->{tablename}; my $rawUrl = "/Logs/Index?selected=$index&refresh=1"; if (not $tableInfo->{consolidate}) { push @urls, { domain => $tableInfo->{name}, raw => $rawUrl, }; next; } my @consolidateTables = keys %{ $tableInfo->{consolidate} }; my @reportComposites = grep { ((ref $_) =~ /Report$/) and ($self->_compositeUsesDbTable($_, \@consolidateTables) ) } @{ $self->composites }; (ref $self) =~ m/::(.*?)$/;; my $urlModName= $1; my $first = 1; foreach my $comp (@reportComposites) { my $compName = $comp->name(); my %compUrls =( domain => $tableInfo->{name}, summary => "/$urlModName/Composite/$compName", ); if ($first) { $compUrls{raw} = $rawUrl; $first =0; } else { $compUrls{raw} = undef; } push @urls, \%compUrls; } if (not @reportComposites) { push @urls, { domain => $tableInfo->{name}, raw => $rawUrl } ; } } return \@urls; } sub _compositeUsesDbTable { my ($self, $composite, $dbTables_r) = @_; my $usesDbTable = 0; foreach my $component (@{ $composite->components() } ) { if ($component->can('dbTableName')) { if ($component->dbTableName() eq any( @{ $dbTables_r } )) { $usesDbTable = 1; } last; } } return $usesDbTable; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Menu/0000775000000000000000000000000012017102272015147 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Menu/Node.pm0000664000000000000000000000427612017102272016403 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Menu::Node; use strict; use warnings; use EBox::Exceptions::Internal; use EBox::Gettext; use EBox::CGI::Run; use EBox::Menu::Separator; sub new { my $class = shift; my %opts = @_; my $self = {}; bless($self, $class); $self->{style} = delete $opts{style}; $self->{separator} = delete $opts{separator}; my $order = delete $opts{order}; if (defined($order)) { $self->{order} = $order; } else { $self->{order} = 999; } $self->{items} = []; return $self; } sub add # (item) { my ($self, $item) = @_; (defined($item)) or return; $item->isa('EBox::Menu::Node') or throw EBox::Exceptions::Internal( "Tried to add an unknown object to an EBox::Menu::Node composite"); foreach my $i (@{$self->{items}}) { if ($i->_compare($item)) { $i->_merge($item); return; } } if(defined($self->{id})) { $item->{id} = $self->{id} . '_' . scalar(@{$self->{items}}); my $i = 0; for my $it (@{$item->{items}}) { $it->{id} = $item->{id} . '_' . $i; $i++; } } push(@{$self->{items}}, $item); } sub items { my ($self) = @_; my @sorted = sort { $a->{order} <=> $b->{order} } @{$self->{items}}; return \@sorted; } sub _compare # (node) { return undef; } sub _merge # (node) { my ($self, $node) = @_; foreach my $item (@{$node->{items}}) { $self->add($item); } } sub html { # default empty implementation } 1; zentyal-core-2.3.21+quantal1/src/EBox/Menu/Root.pm0000664000000000000000000000354712017102272016441 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Menu::Root; use strict; use warnings; use base 'EBox::Menu::Node'; use EBox::Gettext; use HTML::Mason::Interp; sub new { my $class = shift; my %opts = @_; my $self = $class->SUPER::new(@_); $self->{'current'} = delete $opts{'current'}; $self->{'id'} = 'menu'; bless($self, $class); return $self; } sub html { my $self = shift; my $output; my $interp = HTML::Mason::Interp->new(out_method => \$output); my $comp = $interp->make_component( comp_file => (EBox::Config::templates . '/menu.mas')); # Add separators my @items; my $currentSeparator = ''; foreach my $item (@{$self->items}) { my $itemSeparator = $item->{separator}; if ($itemSeparator) { if ($itemSeparator ne $currentSeparator) { push (@items, new EBox::Menu::Separator('text' => $itemSeparator)); $currentSeparator = $itemSeparator; } } push (@items, $item); } my @params; push(@params, 'items' => \@items); push(@params, 'current' => $self->{'current'}); $interp->exec($comp, @params); return $output; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Menu/Separator.pm0000664000000000000000000000337312017102272017453 0ustar # Copyright (C) 2009-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Menu::Separator; use strict; use warnings; use base 'EBox::Menu::TextNode'; use EBox::Exceptions::Internal; use EBox::Gettext; sub new { my $class = shift; my %opts = @_; my $self = $class->SUPER::new(@_); bless($self, $class); return $self; } sub html { my ($self, $current) = @_; return '' if EBox::Config::configkey('hide_menu_separators'); my $text = $self->{text}; my $html = ''; my $show = 0; if (defined($self->{style})) { $html .= "
  • {style}\">\n"; } else { $html .= "
  • \n"; } $html .= "
    $text
    \n"; $html .= "
  • \n"; return $html; } sub _compare # (node) { my ($self, $node) = @_; defined($node) or return undef; $node->isa('EBox::Menu::Separator') or return undef; if ($node->{text} eq $self->{text}) { return 1; } return undef; } sub _merge # (node) { my ($self, $node) = @_; if (defined($self->{text}) and (length($self->{text}) != 0)) { $node->{text} = $self->{text}; } } 1; zentyal-core-2.3.21+quantal1/src/EBox/Menu/Item.pm0000664000000000000000000000431212017102272016403 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Menu::Item; use strict; use warnings; use base 'EBox::Menu::TextNode'; use EBox::Exceptions::Internal; use EBox::Exceptions::MissingArgument; use EBox::Gettext; sub new { my $class = shift; my %opts = @_; my $url = delete $opts{url}; unless (defined($url) and ($url ne '')) { throw EBox::Exceptions::MissingArgument('url'); } my $self = $class->SUPER::new(@_); bless($self, $class); $self->{url} = $url; return $self; } sub add # (item) { my $self = shift; throw EBox::Exceptions::Internal( "EBox::Menu::Item cannot have children"); } my $urlsToHide = undef; sub html { my ($self, $current) = @_; unless (defined $urlsToHide) { $urlsToHide = { map { $_ => 1 } split (/,/, EBox::Config::configkey('menu_urls_to_hide')) }; } my $text = $self->{text}; my $url = $self->{url}; my $html = ''; if ($urlsToHide->{$url} or (length($text) == 0)) { return $html; } my $class = ""; if (defined($current) and ($current eq $url)) { $class = "current "; } if (defined($self->{style})) { $class .= $self->{style}; } if($class) { $class = "class='$class'"; } my $style = ''; if ($current) { $style = qq/style='display:inline;'/; } $html .= "
  • \n"; $html .= qq{$text\n}; $html .= "
  • \n"; return $html; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Menu/TextNode.pm0000664000000000000000000000254012017102272017240 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Menu::TextNode; use strict; use warnings; use base 'EBox::Menu::Node'; use EBox::Exceptions::Internal; use EBox::Exceptions::MissingArgument; use EBox::Gettext; sub new { my $class = shift; my %opts = @_; my $text = delete $opts{text}; unless (defined($text)) { throw EBox::Exceptions::MissingArgument('text'); } my $self = $class->SUPER::new(@_); bless($self, $class); $self->{text} = $text; return $self; } sub _compare # (node) { my ($self, $node) = @_; defined($node) or return undef; $node->isa('EBox::Menu::TextNode') or return undef; if (($node->{text} eq $self->{text}) and ($node->{url} eq $self->{url})) { return 1; } return undef; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Menu/Folder.pm0000664000000000000000000000576212017102272016732 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Menu::Folder; use strict; use warnings; use base 'EBox::Menu::TextNode'; use EBox::Exceptions::Internal; use EBox::Gettext; sub new { my $class = shift; my %opts = @_; my $name = delete $opts{name}; my $url = delete $opts{url}; unless (defined($name) and ($name ne '')) { throw EBox::Exceptions::MissingArgument('name'); } my $self = $class->SUPER::new(@_); bless($self, $class); $self->{name} = $name; $self->{url} = $url; return $self; } my $foldersToHide = undef; sub html { my ($self, $current) = @_; unless (defined $foldersToHide) { $foldersToHide = { map { $_ => 1 } split (/,/, EBox::Config::configkey('menu_folders_to_hide')) }; } my $name = $self->{name}; my $text = $self->{text}; my $url = $self->{url}; my $html = ''; my $show = 0; if ($foldersToHide->{$name} or (scalar(@{$self->items()}) == 0)) { return $html; } if (defined($self->{style})) { $html .= "
  • \n"; } else { $html .= "
  • \n"; } if (defined($url)) { if ($url eq $current) { $show = 1; } $html .= "{order} <=> $b->{order} } @{$self->items()}; foreach my $item (@sorted) { $item->{style} = "menu$name"; my $display = ($name eq $current); $html .= $item->html($display); } $html .= "\n"; $html .= "
  • \n"; return $html; } sub _compare # (node) { my ($self, $node) = @_; defined($node) or return undef; $node->isa('EBox::Menu::Folder') or return undef; if ($node->{name} eq $self->{name}) { return 1; } return undef; } sub _merge # (node) { my ($self, $node) = @_; if ($self->{url}) { $node->{url} = $self->{url}; } if ($self->{text}) { $node->{text} = $self->{text}; } foreach my $item (@{$node->{items}}) { $self->add($item); } } 1; zentyal-core-2.3.21+quantal1/src/EBox/Common/0000775000000000000000000000000012017102272015473 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Common/Model/0000775000000000000000000000000012017102272016533 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Common/Model/EnableForm.pm0000664000000000000000000000676012017102272021114 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Common::EnableForm # # This class is a common model which stores a single boolean which # indicates if something is enabled or not. The data stored is just a # single boolean attribute # # - enabled # package EBox::Common::Model::EnableForm; use base 'EBox::Model::DataForm'; use strict; use warnings; use EBox::Exceptions::MissingArgument; use EBox::Gettext; use EBox::Types::Boolean; # Group: Public methods # Constructor: new # # Create an enabled form # # Overrides: # # # # Parameters: # # enableTitle - String the i18ned title for the printable name # for the enabled attribute # # modelDomain - String the model domain which this form belongs to # # - Named parameters # # Returns: # # - the recently created model # # Exceptions: # # - thrown if any compulsory # argument is missing # sub new { my ($class, %params) = @_; my $enableTitle = delete $params{enableTitle}; defined ( $enableTitle ) or throw EBox::Exceptions::MissingArgument('enableTitle'); my $modelDomain = delete $params{modelDomain}; defined ( $modelDomain ) or throw EBox::Exceptions::MissingArgument('modelDomain'); my $self = $class->SUPER::new(%params); bless( $self, $class ); $self->{enableTitle} = $enableTitle; $self->{modelDomain} = $modelDomain; return $self; } # Method: formSubmitted # # Overrides: # # # sub formSubmitted { my ($self, $oldRow) = @_; if ( $self->enabledValue() ) { $self->setMessage(__('Service enabled')); } else { $self->setMessage(__('Service disabled')); } } # Group: Class methods # Method: Viewer # # Overrides: # # # sub Viewer { return '/common/enable.mas'; } # Group: Protected methods # Method: _table # # Overrides: # # # sub _table { my ($self) = @_; my @tableDesc = ( new EBox::Types::Boolean( fieldName => 'enabled', printableName => $self->{enableTitle}, editable => 1, ), ); my $dataForm = { tableName => 'EnableForm', printableTableName => __('Enable service'), modelDomain => $self->{modelDomain}, defaultActions => [ 'editField', 'changeView' ], tableDescription => \@tableDesc, class => 'dataForm', }; return $dataForm; } 1; zentyal-core-2.3.21+quantal1/src/EBox/interactiveTest/0000775000000000000000000000000012017102272017420 5ustar zentyal-core-2.3.21+quantal1/src/EBox/interactiveTest/backupLogDB.t0000775000000000000000000000304112017102272021723 0ustar #!/usr/bin/perl # Copyright (C) 2010-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA use strict; use warnings; use EBox; use EBox::DBEngineFactory; my %args = map { $_ => 1 } @ARGV; my $backup; my $restore; if ($args{'-b'}) { $backup = 1; } elsif ($args {'-r'}) { $restore = 1; } else { die 'no operation mode'; } my $basic = $args{'--basic'}; my $cleanSlices = $args{'--clean-slices'}; my $slicedMode = $basic ? 0 : 1; EBox::init(); my $dir = '/tmp/testdb'; (-d $dir) or mkdir $dir; my $basename = 'test'; my $dbengine = EBox::DBEngineFactory::DBEngine(); if ($backup) { system "rm -rf $dir/*"; if ($cleanSlices) { $dbengine->do('TRUNCATE TABLE backup_slices'); print "Slice table truncated\n"; } $dbengine->backupDB($dir, $basename, slicedMode => $slicedMode); } elsif ($restore) { $dbengine->restoreDB($dir, $basename, slicedMode => $slicedMode); }else { die 'no reached'; } 1; zentyal-core-2.3.21+quantal1/src/EBox/interactiveTest/GConfModuleChanged.t0000664000000000000000000000462712017102272023232 0ustar package main; # use strict; use warnings; use Test::More tests => 8; use Test::Exception; use Error qw(:try); use EBox; use EBox::Global; use EBox::Config::TestStub; use constant { MODULE_NAME => 'apache', CONF_KEY => 'testConfKey', STATE_KEY => 'testStateKey', }; try { startup(); setAsChangedTest(); changeConfKeyTest(); changeStateKeyTest(); } finally { finalize(); }; sub warning { diag 'This test may modify your eBox configuration'; diag 'Do NOT use it in production environments'; } sub _fakeConfDir { return "/tmp/gconfchanged.test"; } sub startup { warning(); my $fakeConfDir = _fakeConfDir(); system "rm -rf $fakeConfDir"; EBox::init(); my $global = EBox::Global->getInstance(); if ($global->modIsChanged(MODULE_NAME)) { die 'This test needs that ' . MODULE_NAME . ' module is in a non-changed state'; } system "mkdir -p $fakeConfDir"; EBox::Config::TestStub::fake(conf => $fakeConfDir); } sub finalize { my $mod = EBox::Global->modInstance(MODULE_NAME); $mod->unset(CONF_KEY); $mod->st_unset(STATE_KEY); } sub setAsChangedTest # 3 { my $mod = EBox::Global->modInstance(MODULE_NAME); $mod->setAsChanged(); checkModuleChanged(1); lives_ok { $mod->revokeConfig(); } 'checking if revoke config do not fail'; } sub changeConfKeyTest # 3 { my $mod = EBox::Global->modInstance(MODULE_NAME); $mod->set_int(CONF_KEY, 4); checkModuleChanged(1); lives_ok { $mod->revokeConfig(); } 'checking if revoke config do not fail'; } sub changeStateKeyTest # 2 ch { my $mod = EBox::Global->modInstance(MODULE_NAME); $mod->st_set_int(STATE_KEY, 4); checkModuleChanged(0); lives_ok { $mod->revokeConfig(); } 'checking if revoke config do not fail'; } sub checkModuleChanged { my ($changeExpected) = @_; $changeExpected = $changeExpected ? 1 : 0; my $global = EBox::Global->getInstance(); my $changeState = $global->modIsChanged(MODULE_NAME) ? 1 : 0; is $changeState, $changeExpected, 'Testing module change state'; if ( $changeExpected) { checkBakFile(1, 'Checking bak file for expcted changed module'); } } sub checkBakFile { my ($bakFileExpected, $msg) = @_; my $mod = EBox::Global->modInstance(MODULE_NAME); my $bakFile = $mod->_bak_file_from_dir(EBox::Config::conf); my $bakFilePresent = ( -f $bakFile) ? 1 : 0; is $bakFilePresent, $bakFileExpected, $msg; } 1; zentyal-core-2.3.21+quantal1/src/EBox/interactiveTest/BackupSlices.t0000664000000000000000000003715312017102272022166 0ustar # Copyright (C) 2010-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA use strict; use warnings; use EBox; use EBox::Sudo; use EBox::DBEngineFactory; use EBox::Logs::SlicedBackup; use Error qw(:try); use Test::More qw(no_plan); use Test::Exception; use DateTime; use Time::Local; diag ("This test must be run in a complete Zentyal environment. DONT RUN IN A PRODUCTION ENVIRONMENT"); EBox::init(); my $dbengine = EBox::DBEngineFactory::DBEngine(); my $backupDir = "/tmp/backup-slices.d"; my $backupDirIn3Slice = "/tmp/in3slice-backup-slices.d"; my $archiveDir = "/tmp/backup-slices-archive.dd"; my $table = 'testslices'; _clearDir($backupDir); _clearDir($backupDirIn3Slice); _clearDir($archiveDir); EBox::Sudo::root("chmod a+rx $archiveDir"); _clearTables($dbengine, $table); my $period = '10 days'; my $periodInEpoch = EBox::Logs::SlicedBackup::_periodToEpoch($dbengine, $period); my $nowTs = _tsToEpoch('2010-01-01 04:45:42'); # no important at this point my $timeline = 1; #my $maxId = _maxArchiveIdInTimeline($dbengine, $timeline, $toDate); # check that no slices are made with a empty table my @actualSlice; @actualSlice = EBox::Logs::SlicedBackup::_actualTableSlice($dbengine, $table, $periodInEpoch, $nowTs); is @actualSlice, 0, 'Checkign that actualSlice returns empty'; my $sqlGetSlices = "select * from backup_slices where tablename = '$table'"; my $res = $dbengine->query($sqlGetSlices); is @{ $res}, 0, 'Check that no entries were made for a empty table'; # Rationale: slkice1 and slice 2 adjacents, slice 3 not-adjacent with 2 # a 4th empty partition my @slice1Timestamps = ( '2010-04-20 10:45:12', '2010-04-24 21:21:12', '2010-04-30 10:45:12', ); my @slice2Timestamps = ( '2010-04-30 10:45:13', '2010-05-03 21:21:12', '2010-05-08 04:45:41', ); my @slice3Timestamps = ( '2010-05-18 06:11:11', '2010-05-19 04:11:11', ); # isnert vlaue into test table _insertTsAndValue($dbengine, $table, \@slice1Timestamps, 1); _insertTsAndValue($dbengine, $table, \@slice2Timestamps, 2); _insertTsAndValue($dbengine, $table, \@slice3Timestamps, 3); # makgni a backup in the 3 slcie to be used in later test of restore toDate my $nowFor3SliceBackup = '2010-05-19 05:12:12'; my $nowTsFor3SliceBackup = _tsToEpoch($nowFor3SliceBackup); $dbengine->backupDB($backupDirIn3Slice, 'basaename', slicedMode => 1, nowTs => $nowTsFor3SliceBackup); # moving time to 4th slice $nowTs = _tsToEpoch('2010-05-30 04:45:42'); @actualSlice = EBox::Logs::SlicedBackup::_actualTableSlice($dbengine, $table, $periodInEpoch, $nowTs); is_deeply [@actualSlice], [4, '2010-05-20 10:45:15', '2010-05-30 10:45:15', 1], 'Checkign that actualSlice returns he valeus for the fourth active one'; $res = $dbengine->query($sqlGetSlices); is @{ $res}, 4, 'Check that sql rows count is ok'; @actualSlice = EBox::Logs::SlicedBackup::_actualTableSlice($dbengine, $table, $periodInEpoch, $nowTs); is_deeply [@actualSlice], [4, '2010-05-20 10:45:15', '2010-05-30 10:45:15', 1], 'Checkign that actualSlice is stable'; # moving nowTs to a new period to see if new slices are created $nowTs = _tsToEpoch('2010-06-4 04:45:42'); @actualSlice = EBox::Logs::SlicedBackup::_actualTableSlice($dbengine, $table, $periodInEpoch, $nowTs); is_deeply [@actualSlice], [5, '2010-05-30 10:45:16', '2010-06-09 10:45:16', 1], 'Checking that a new slice has been created'; # insert some data for this time slice my @slice5Timestamps = ( '2010-06-2 5:5:5' ); _insertTsAndValue($dbengine, $table, \@slice5Timestamps, 5); _checkMaxSlice($dbengine, $table, $timeline, 5); # do the actual backup $dbengine->backupDB($backupDir, 'basaename', slicedMode => 1, nowTs => $nowTs); _checkMaxSlice($dbengine, $table, $timeline, 5); _checkSlices({ mustExist => 1, compressed => 0 }, $backupDir, $table, $timeline, 5); _checkSlices(0, $backupDir, $table, $timeline, 1, 2, 3, 4); # is EBox::Logs::SlicedBackup::_noStoredSlices($dbengine, $timeline, 5), 4, 'see the backup has not archived itself any slice'; # check purge threshodl with no archives _checkLimitPurgeThreshold($dbengine, $table, '2010-05-04 21:21:12', 'Tue Apr 20 10:45:11 2010'); # in slice 2 _checkLimitPurgeThreshold($dbengine, $table, '2010-06-2 10:10:10', 'Tue Apr 20 10:45:11 2010' ); # in slice 5 # restore without archive _deleteTable($dbengine, $table); dies_ok { $dbengine->restoreDB($backupDir, 'basename', slicedMode => 1, archiveDir => $archiveDir); } 'by defualt restore with all archived tables is not allowed'; lives_ok { $dbengine->restoreDB($backupDir, 'basename', slicedMode => 1, archiveDir => $archiveDir, notArchivedForce => 1); } 'forcing restore even when are not archived tables'; # since we havent archive only the last slice (5) should be restored _checkDataFromSlices($dbengine, $table, { 5 => 1 }); _checkActualTimeline($dbengine, 1); # regenate DB for archive tests _regenerateAllDatabase($dbengine, $table, $timeline); diag 'archive'; EBox::Logs::SlicedBackup::archive($dbengine, archiveDir => $archiveDir, nowTs => $nowTs, limit => 10000, ); _checkSlices(0, $archiveDir, $table, $timeline, 5); _checkSlices(1, $archiveDir, $table, $timeline, 1, 2, 3, 4); _checkMarkAsArchived(0, $dbengine, $table, $timeline, 5); _checkMarkAsArchived(1, $dbengine, $table, $timeline, 1, 2, 3, 4); # slcie 2 is archived so not limitation in purge threshold _checkLimitPurgeThreshold($dbengine, $table, '2010-05-04 21:21:12','2010-05-04 21:21:12',); # slcie 5 not archived so limitation _checkLimitPurgeThreshold($dbengine, $table, '2010-06-2 10:10:10', 'Sun May 30 10:45:15 2010' ); diag 'retry archive to see that is stable'; EBox::Logs::SlicedBackup::archive($dbengine, archiveDir => $archiveDir, nowTs => $nowTs, limit => 10000, ); _checkSlices(0, $archiveDir, $table, $timeline, 5); _checkSlices(1, $archiveDir, $table, $timeline, 1, 2, 3, 4); _checkMarkAsArchived(0, $dbengine, $table, $timeline, 5); _checkMarkAsArchived(1, $dbengine, $table, $timeline, 1, 2, 3, 4); diag 'file retrieval from archive test' ; my @archiveFiles = sort @{ EBox::Logs::SlicedBackup::slicesFromArchive($dbengine, $archiveDir, $timeline) }; my @expectedFiles = sort map { EBox::Logs::SlicedBackup::_backupFileForTable($archiveDir, $table, $timeline, $_) . '.gz' } (1, 2, 3, 4); is_deeply \@archiveFiles, \@expectedFiles, "Checkign retireval files on the archive"; diag ' restore with archive'; my $dataFromAllSlices= { 1 => 3, 2 => 3, 3 => 2, 5 => 1, }; _deleteTable($dbengine, $table); $dbengine->restoreDB($backupDir, 'basename', slicedMode => 1, archiveDir => $archiveDir); # since we have restored with archive all data should be there (slcie 4 has not # dato so is not in the list) _checkDataFromSlices($dbengine, $table, $dataFromAllSlices); # we will restore and check again to assure restore is stable $dbengine->restoreDB($backupDir, 'basename', slicedMode => 1, archiveDir => $archiveDir); _checkDataFromSlices($dbengine, $table, $dataFromAllSlices); _checkActualTimeline($dbengine, 1); # now with up to date limit.. # slices up to time @archiveFiles = sort @{ EBox::Logs::SlicedBackup::slicesFromArchive($dbengine, $archiveDir, $timeline, $nowTsFor3SliceBackup ) }; @expectedFiles = sort map { EBox::Logs::SlicedBackup::_backupFileForTable($archiveDir, $table, $timeline, $_) . '.gz' } (1, 2); is_deeply \@archiveFiles, \@expectedFiles, "Checkign retrieval files on the archive up time"; # restore up to time $dbengine->restoreDB($backupDirIn3Slice, 'basename', slicedMode => 1, archiveDir => $archiveDir, toDate => $nowTsFor3SliceBackup ); my $dataForSlicesUpTo3 = { 1 => 3, 2 => 3, 3 => 2 }; _checkDataFromSlices($dbengine, $table, $dataForSlicesUpTo3); _checkDataNoLaterThan($dbengine, $table, $nowTsFor3SliceBackup); _checkActualTimeline($dbengine, 2); # restore again up to tiem to check stability $dbengine->restoreDB($backupDirIn3Slice, 'basename', slicedMode => 1, archiveDir => $archiveDir, toDate => $nowTsFor3SliceBackup ); my $dataForSlicesUpTo3 = { 1 => 3, 2 => 3, 3 => 2 }; _checkDataFromSlices($dbengine, $table, $dataForSlicesUpTo3); _checkDataNoLaterThan($dbengine, $table, $nowTsFor3SliceBackup); _checkActualTimeline($dbengine, 2); # restore again without date limit, checking that archvies for other timelines # are NOT restored $dbengine->restoreDB($backupDirIn3Slice, 'basename', slicedMode => 1, archiveDir => $archiveDir, toDate => $nowTsFor3SliceBackup ); _checkDataFromSlices($dbengine, $table, $dataForSlicesUpTo3); _checkDataNoLaterThan($dbengine, $table, $nowTsFor3SliceBackup); _checkActualTimeline($dbengine, 2); sub _regenerateAllDatabase { my ($dbengine, $table, $timeline) = @_; _insertTsAndValue($dbengine, $table, \@slice1Timestamps, 1); _insertTsAndValue($dbengine, $table, \@slice2Timestamps, 2); _insertTsAndValue($dbengine, $table, \@slice3Timestamps, 3); _insertTsAndValue($dbengine, $table, \@slice5Timestamps, 5); } sub _checkDataFromSlices { my ($dbengine, $table, $slices) = @_; my $sqlGetData = "select value, COUNT(value) AS count FROM $table GROUP BY VALUE"; my $res = $dbengine->query($sqlGetData); if (not defined $res) { die "Error in SQL $sqlGetData"; } my $allOk = 1; my %slicesDataInDB = map { $_->{value} => $_->{count} } @{ $res }; is_deeply \%slicesDataInDB, $slices, "Chekcing data stored for table $table"; } sub _checkMaxSlice { my ($dbengine, $table, $timeline, $max) = @_; my $sqlMaxSlices = "select MAX(id) AS maxid from backup_slices where tablename = '$table' AND timeline = $timeline"; my ($res) = @{ $dbengine->query($sqlMaxSlices) }; if (not defined $res) { die "Error in SQL $sqlMaxSlices"; } is $res->{maxid}, $max, "check that last slice number for table $table and timeline $timeline is $max"; } sub _checkSliceNotExistInDB { my ($dbengine, $table, $timeline, $n) = @_; my $sqlGetSlices = "select * from backup_slices where tablename = '$table' AND timeline = $timeline AND id = $n"; my $res = $dbengine->query($sqlGetSlices); is @{ $res}, 0, "check slice $n for table $table and timeline $timeline dows not exists"; } sub _checkMarkAsArchived { my ($archived, $dbengine, $table, $timeline, @slices) = @_; my $archivedSql = "SELECT id FROM backup_slices WHERE archived AND " . " tablename = '$table' AND timeline = $timeline"; my $res = $dbengine->query($archivedSql); my @archives = sort map { $_->{id} } @{ $res }; @slices = sort @slices; if ($archived) { is_deeply \@archives, \@slices, 'Check archived mark in slices'; } else { my $ok = 1; my %slicesInArchive = map { $_ => 1 } @archives; foreach my $slice (@slices) { if (exists $slicesInArchive{$slice}) { fail "$slice of $table was marked as archived"; $ok = 0; } } if ($ok) { pass "Slices @slices of table $table were not marked as archived as expected"; } } } sub _checkSlices { my ($options, $dir, $table, $timeline, @slices) = @_; my $mustExist; my $compressed = 1; if (ref $options) { $mustExist = $options->{mustExist}; $compressed = $options->{compressed}; } else { $mustExist = $options; } my @filesWanted = map { my $f = EBox::Logs::SlicedBackup::_backupFileForTable($dir, $table, $timeline, $_); if ($compressed) { $f .= '.gz'; } $f } @slices; foreach my $file (@filesWanted) { my $exists = (-e $file); if ($mustExist) { ok $exists, "Checking that slice file $file exists"; } else { is $exists, undef, "Checking that slice file $file NOT exists"; } } } sub _checkLimitPurgeThreshold { my ($dbengine, $table, $original, $expectedAfterLimit) = @_; my $final = EBox::Logs::SlicedBackup::limitPurgeThreshold( $dbengine, $table, $original, ); is $final, $expectedAfterLimit, "Checking limitPurgeThreshold for threshold $original"; } sub _checkDataNoLaterThan { my ($dbengine, $table, $date) = @_; my $query = "SELECT COUNT(*) AS count, to_timestamp($date) AS human FROM $table WHERE timestamp > to_timestamp($date)"; my ($res) = @{ $dbengine->query($query) }; my $human = $res->{human}; is $res->{count}, 0, "checking that no record later than $human for $table"; } sub _checkActualTimeline { my ($dbengine, $wanted) = @_; my $actual = EBox::Logs::SlicedBackup::_activeTimeline($dbengine); is $actual, $wanted, 'checking timeline'; } sub _deleteTable { my ($dbengine, $table) = @_; $dbengine->do("DELETE FROM $table"); } sub _clearTables { my ($dbengine, $table) = @_; # prepare table try { $dbengine->do("drop table $table"); } otherwise {}; $dbengine->do("CREATE TABLE IF NOT EXISTS $table (timestamp TIMESTAMP, value INT )"); $dbengine->do("DELETE FROM backup_slices WHERE tablename='$table'"); } sub _clearDir { my ($dir) = @_; system "rm -rf $dir"; system "mkdir -p $dir"; } sub _insertTsAndValue { my ($dbengine, $table, $tsList, $value) = @_; foreach my $ts (@{ $tsList}) { $dbengine->unbufferedInsert($table, { timestamp => "'$ts'", value => $value} ); } } sub _tsToEpoch { my ($ts) = @_; my ($year,$mon,$mday, $hour, $min, $sec) = $ts =~ m/(\d+)-(\d+)-(\d+)\s+(\d+):(\d+):(\d+)/; # perl lcoatiem adjsutments $mon -= 1; $year -= 1900; my $time = timelocal($sec,$min,$hour,$mday,$mon, $year); return $time; } sub _epochToTs { my ($epoch) = @_; my ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst) = localtime($epoch); $mon += 1; $year += 1900; return "$year-$mon-$mday $hour:$min:$sec"; } 1; zentyal-core-2.3.21+quantal1/src/EBox/AuditLogging.pm0000664000000000000000000001531312017102272017161 0ustar # Copyright (C) 2011-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::AuditLogging; use base qw(EBox::Module::Service EBox::LogObserver); use strict; use warnings; use POSIX qw(getlogin); use EBox::Global; use EBox::Gettext; use EBox::DBEngineFactory; use constant TABLE_ACTIONS => 'audit_actions'; use constant TABLE_SESSIONS => 'audit_sessions'; sub _create { my $class = shift; my $self = $class->SUPER::_create( name => 'audit', printableName => __('Audit Logging'), @_ ); bless ($self, $class); return $self; } sub _db { my ($self) = @_; unless ($self->{db}) { $self->{db} = EBox::DBEngineFactory::DBEngine(); } return $self->{db}; } # Method: showModuleStatus # # Indicate to ServiceManager if the module must be shown in Module # status configuration. # # Overrides: # EBox::Module::Service::showModuleStatus # sub showModuleStatus { # we don't want it to appear in module status return undef; } # Method: addModuleStatus # # Overrides: # EBox::Module::Service::addModuleStatus # sub addModuleStatus { # we don't want it to appear in the dashboard widget return undef; } # Method: enableLog # # Overrides # # sub enableLog { my ($self, $enable) = @_; $self->set_bool('logging', $enable); } sub isEnabled { my ($self) = @_; # Get readonly value to avoid disable of logging before saving changes my $globalRO = EBox::Global->getInstance(1); return $globalRO->modInstance('audit')->get_bool('logging') ? 1 : 0; } # Method: tableInfo # # Overrides: # # sub tableInfo { my $action_titles = { 'timestamp' => __('Date/Time'), 'username' => __('User'), 'module' => __('Module'), 'model' => __('Section'), 'event' => __('Event'), 'id' => __('Identifier'), 'value' => __('Value'), 'oldvalue' => __('Previous value'), }; my @action_order = qw{timestamp username module model event id value oldvalue}; my $action_events = { 'add' => __('Add'), 'set' => __('Set'), 'del' => __('Delete'), 'move' => __('Move'), 'action' => __('Action'), }; my $action_table = { 'name' => __('Configuration changes'), 'tablename' => TABLE_ACTIONS, 'titles' => $action_titles, 'order' => \@action_order, 'timecol' => 'timestamp', 'filter' => ['username', 'module', 'model'], 'events' => $action_events, 'eventcol' => 'event', }; my $session_titles = { 'timestamp' => __('Date/Time'), 'username' => __('User'), 'ip' => __('IP'), 'event' => __('Event'), }; my @session_order = qw{timestamp username ip event}; my $session_events = { 'login' => __('Login'), 'logout' => __('Logout'), 'fail' => __('Failed login'), 'expired' => __('Expired session'), }; my $session_table = { 'name' => __('Administrator sessions'), 'tablename' => TABLE_SESSIONS, 'titles' => $session_titles, 'order' => \@session_order, 'timecol' => 'timestamp', 'filter' => ['username', 'ip'], 'events' => $session_events, 'eventcol' => 'event', 'disabledByDefault' => 1, 'types' => { 'ip' => 'IPAddr' } }; return [ $action_table, $session_table ]; } sub queryPending { my ($self) = @_; return unless $self->isEnabled(); return $self->_db()->query('SELECT * FROM ' . TABLE_ACTIONS . ' WHERE temporal = TRUE '); } sub commit { my ($self) = @_; return unless $self->isEnabled(); $self->_db()->update(TABLE_ACTIONS, {'temporal' => 'FALSE'}, ['temporal = TRUE']); } sub discard { my ($self) = @_; return unless $self->isEnabled(); $self->_db()->{multiInsert}->{TABLE_ACTIONS} = []; $self->_db()->delete(TABLE_ACTIONS, ['temporal = TRUE']); } sub _timestamp { my ($self) = @_; my ($sec, $min, $hour, $day, $month, $year) = localtime(time()); $year += 1900; $month++; # TODO: Show this localized? Check the behavior in the rest of the logs return "$year-$month-$day $hour:$min:$sec"; } sub _username { my ($self) = @_; unless (defined $self->{username}) { # It is a script or cloud (or something unexpected is happening) $self->{username} = POSIX::getlogin(); } return $self->{username}; } sub setUsername { my ($self, $username) = @_; $self->{username} = $username; } sub logModelAction { my ($self, $model, $event, $id, $value, $oldvalue, $temporal) = @_; my $module = $model->parentModule()->name(); my $section = $model->name(); $self->_log($module, $section, $event, $id, $value, $oldvalue, $temporal); } sub logAction { my ($self, $module, $section, $action, $arg, $temporal) = @_; return unless $self->isEnabled(); $self->_log($module, $section, 'action', $action, $arg, undef, $temporal); } sub _log { my ($self, $module, $section, $event, $id, $value, $oldvalue, $temporal) = @_; $temporal = 1 unless defined $temporal; if ((defined $value) and (defined $oldvalue)) { if ($value eq $oldvalue) { # do not log changes to the same return; } } my %data = ( timestamp => $self->_timestamp(), username => $self->_username(), module => $module, event => $event, model => $section, id => $id, value => $value, oldvalue => $oldvalue, temporal => $temporal, ); $self->_db()->unbufferedInsert(TABLE_ACTIONS, \%data); } sub logSessionEvent { my ($self, $username, $ip, $event) = @_; return unless $self->isEnabled(); my %data = ( timestamp => $self->_timestamp(), username => $username, ip => $ip, event => $event, ); if ($event == 'login') { $self->{username} = $username; } if ($event == 'logout' or $event == 'expired') { delete $self->{username}; } $self->_db()->unbufferedInsert(TABLE_SESSIONS, \%data); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Events/0000775000000000000000000000000012017102272015507 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Events/Composite/0000775000000000000000000000000012017102272017451 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Events/Composite/EventsReport.pm0000664000000000000000000000343212017102272022451 0ustar # Copyright (C) 2009-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Events::Composite::EventsReport; use base 'EBox::Model::Composite'; use strict; use warnings; use EBox::Gettext; # Group: Public methods # Constructor: new # # Constructor for the general events composite # # Returns: # # - a # general events composite # sub new { my ($class, @params) = @_; my $self = $class->SUPER::new(@params); return $self; } # Group: Protected methods # Method: _description # # Overrides: # # # sub _description { my $description = { components => [ '/events/EventsReportOptions', 'EventsGraph', 'EventsDetails', ], layout => 'top-bottom', name => 'EventsReport', printableName => __('Events reports'), pageTitle => __('Events report'), compositeDomain => 'Events', # help => __(''), }; return $description; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Events/Composite/General.pm0000664000000000000000000000362712017102272021374 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Events::Model::EventsComposite # # This class is used to manage the events module within a single # element whose components # are: and # inside a top-bottom # layout. # package EBox::Events::Composite::General; use base 'EBox::Model::Composite'; use strict; use warnings; use EBox::Gettext; # Group: Public methods # Constructor: new # # Constructor for the general events composite # # Returns: # # - a general events composite # sub new { my $class = shift; my $self = $class->SUPER::new(@_); return $self; } # Group: Protected methods # Method: _description # # Overrides: # # # sub _description { my $description = { layout => 'tabbed', name => 'General', printableName => __('Events'), pageTitle => __('Events'), headTitle => undef, compositeDomain => 'Events', help => __('Events module configures Zentyal to help ' . 'informing you about events that happen'), }; return $description; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Events/WatcherProvider.pm0000664000000000000000000000231012017102272021151 0ustar # Copyright (C) 2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Events::WatcherProvider # # This interface needs to be implemented for those modules which add # custom watchers for the events module use strict; use warnings; package EBox::Events::WatcherProvider; use EBox::Exceptions::NotImplemented; # Method: eventWatchers # # This function must return the names of the watcher classes # without the "EBox::Event::Watcher" prefix. # # Returns: # # array ref - containing the watcher names # sub eventWatchers { throw EBox::Exceptions::NotImplemented(); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Events/Model/0000775000000000000000000000000012017102272016547 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Events/Model/EventsGraph.pm0000664000000000000000000000761412017102272021343 0ustar # Copyright (C) 2009-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Events::Model::EventsGraph; use base 'EBox::Logs::Model::Graph'; # use strict; use warnings; use EBox::Gettext; use Error qw(:try); sub new { my $class = shift; my $self = $class->SUPER::new(@_); bless $self, $class; return $self; } sub dbTableName { return 'events_accummulated'; } sub dbFields { my ($package) = @_; return { info => { printableName => __('Informative'), }, warn => { printableName => __('Warning'), }, error => { printableName => __('Error'), }, fatal => { printableName => __('Fatal error'), }, }; } sub altText { return __('Events summarized chart'); } # Method: _table # # The table description which consists of three fields: # # You can only edit enabled and configuration fields. The event # name and description are read-only fields. # sub _table { my $dataTable = { tableDescription => [], tableName => 'EventsGraph', printableTableName => __('Events summarized graph'), modelDomain => 'Events', # help => __(''), defaultActions => [ 'editField', 'changeView', ], messages => { 'add' => undef, 'del' => undef, 'update' => undef, 'moveUp' => undef, 'moveDown' => undef, } }; return $dataTable; } sub tableName { return 'EventsGraph'; } sub timePeriod { my ($self) = @_; my $model = $self->{confmodule}->reportOptionsModel(); my $row = $model->row(); return $row->valueByName('timePeriod'); } sub reportRows { my ($self) = @_; my %totalRowsByDate; my $rows = $self->SUPER::reportRows(); foreach my $row (@{ $rows } ) { my $date = $row->{date}; if (exists $totalRowsByDate{$date}) { $totalRowsByDate{$date}->{info} += $row->{info}; $totalRowsByDate{$date}->{warn} += $row->{warn}; $totalRowsByDate{$date}->{error} += $row->{error}; $totalRowsByDate{$date}->{fatal} += $row->{fatal}; } else { $totalRowsByDate{$date} = $row; } } my @totalRows; my @dates = sort keys %totalRowsByDate; foreach my $date (@dates) { push @totalRows, delete $totalRowsByDate{$date}; } return \@totalRows; } # Overriden bz in this case limit = normal limit * sources of events sub limitByTimePeriod { my ($self) = @_; my $unityLimit = $self->SUPER::limitByTimePeriod(); my $nSources; my $dbEngine = EBox::DBEngineFactory::DBEngine(); my $timePeriod = $self->timePeriod(); my $table = $self->dbTable($timePeriod) ; my $query = "SELECT COUNT(DISTINCT(source)) FROM $table"; my $dbRows = $dbEngine->do($query); if ($nSources == 0) { $nSources = 1; } return $unityLimit * $nSources; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Events/Model/LogFiltering.pm0000664000000000000000000001255112017102272021476 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Events::Model::LogFiltering # # This class is used to set those filters that you may want to be # informed. This model is used as template given a set of filters (all # String-based) and events (a selection) using tableInfo information # (Check for details) # # The model composition based on tableInfo information is the # following: # # - filter1..n - Text # - event - Selection between the given selections from tableInfo # package EBox::Events::Model::LogFiltering; use strict; use warnings; use base 'EBox::Model::DataTable'; use EBox::Exceptions::MissingArgument; use EBox::Gettext; use EBox::Types::Select; use EBox::Types::Text; # Group: Public methods # Constructor: new # # Constructor for # object instance # # Parameters: # # confmodule - # directory - # # tableInfo - hash ref the table info giving with the same # structure that it's described in # # # # Returns : # # A recently created # object # sub new { my ($class, %params) = @_; if (not defined ($params{tableInfo})) { throw EBox::Exceptions::MissingArgument('tableInfo', silent => 1); } my $self = $class->SUPER::new(%params); bless($self, $class); $self->{tableInfo} = $params{tableInfo}; return $self; } # Group: Protected methods # Method: _table # # Describe the traffic shaping table # # Returns: # # hash ref - table's description # sub _table { my ($self) = @_; my @tableDesc = (); # Every filter element from table info is a text-based type my @filters = @{$self->{tableInfo}->{filter}}; foreach my $filter (@filters) { push( @tableDesc, new EBox::Types::Text( fieldName => $filter, printableName => $self->{tableInfo}->{titles}->{$filter}, editable => 1, optional => 1, )); } # Every event is a selection filter, we always allow the 'any' # selection which matches with every event that logger logs # Create the options my %events = %{$self->{tableInfo}->{events}}; my @eventOptions = ({ value => 'any', printableValue => __('Any') }); my $defaultEventOptionValue = 'any'; foreach my $eventName (keys %events) { push ( @eventOptions, { value => $eventName, printableValue => $events{$eventName}, } ); } # If there are one only event, just show that event as selectable if ( @eventOptions == 2 ) { shift(@eventOptions); $defaultEventOptionValue = undef; } push ( @tableDesc, new EBox::Types::Select( fieldName => 'event', printableName => __('Event'), editable => 1, options => \@eventOptions, defaultValue => $defaultEventOptionValue, )); my $dataTable = { 'tableName' => 'LogWatcherFiltering_' . $self->{tableInfo}->{tablename}, 'printableTableName' => __x('Filters to apply to notify logs from ' . '{logDomain}', logDomain => $self->{tableInfo}->{name}), 'defaultActions' => [ 'add', 'del', 'editField', 'changeView' ], 'modelDomain' => 'Events', 'tableDescription' => \@tableDesc, 'class' => 'dataTable', 'help' => __('Every filter added is cumulative.' . 'Then every log line which matches ' . 'with any filter given will be notified'), 'rowUnique' => 1, # Set each row is unique 'printableRowName' => __('filter'), }; return $dataTable; } # Method: viewCustomizer # # Overrides to # provide a custom HTML title with breadcrumbs # sub viewCustomizer { my ($self) = @_; my $custom = $self->SUPER::viewCustomizer(); my $parentDir =$self->directory(); my @split = split('/', $parentDir); $parentDir = join('/', splice(@split, 0, 4)); $custom->setHTMLTitle([ { title => __('Events'), link => '/Events/Composite/General', }, { title => __('Log Observer Watcher'), link => "/Events/View/LogWatcherConfiguration?directory=$parentDir", }, { title => $self->{tableInfo}->{name}, link => '' }, ]); return $custom; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Events/Model/EventsReportOptions.pm0000664000000000000000000000211612017102272023121 0ustar # Copyright (C) 2009-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Events::Model::EventsReportOptions; use base 'EBox::Logs::Model::OptionsBase'; # use strict; use warnings; sub new { my $class = shift; my $self = $class->SUPER::new(@_); bless $self, $class; return $self; } sub tableName { return 'EventsReportOptions'; } sub modelDomain { return 'Events'; } sub reportUrl { return '/Events/Composite/EventsReport'; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Events/Model/ConfigureDispatchers.pm0000664000000000000000000002455312017102272023231 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Events::Model::ConfigureDispatchers; # Class: # # EBox::Events::Model::ConfigureDispatchers # # This class is used as a model to describe a table which will be # used to select the event dispatchers the user wants # to enable/disable and each dispatcher configuration. # # It subclasses # use base 'EBox::Model::DataTable'; use strict; use warnings; use EBox; use EBox::Config; use EBox::Exceptions::DataNotFound; use EBox::Gettext; use EBox::Types::Boolean; use EBox::Types::Link; use EBox::Types::HasMany; use EBox::Types::Text; use EBox::Types::Union; use EBox::Types::Union::Text; use Error qw(:try); # Group: Public methods # Method: headTitle # # Get the i18ned name of the header where the model is contained, if any # # Returns: # # string # sub headTitle { my ($self) = @_; return undef; } # Method: syncRows # # This method is overridden since the showed data is managed # differently. # # - The data is already available from the eBox installation # # - The adding/removal of event dispatchers is done dynamically # reading the directories where the event dispatcher are. The # adding/removal is done installing or deinstalling ebox modules # with dispatchers. # # # Overrides: # # # sub syncRows { my ($self, $currentIds) = @_; my %storedEventDispatchers; my %currentEventDispatchers; my $dispatchersRef = $self->_fetchDispatchers(); foreach my $dispatcherFetched (@{$dispatchersRef}) { $currentEventDispatchers{$dispatcherFetched} = 1; } my $modified = 0; # Removing old ones foreach my $id (@{$currentIds}) { my $row; my $remove = 0; try { $row = $self->row($id); my $stored = $row->valueByName('dispatcher'); $storedEventDispatchers{$stored} = 1; if (not exists $currentEventDispatchers{$stored}) { $remove = 1; } } otherwise { $remove = 1; }; if ($remove) { $self->removeRow($id); $modified = 1; } } # Adding new ones foreach my $dispatcher (keys (%currentEventDispatchers)) { next if (exists ($storedEventDispatchers{$dispatcher} )); # Create a new instance from this dispatcher eval "use $dispatcher"; my $enabled = not $dispatcher->DisabledByDefault(); my %params = ( # and the same with watchers 'dispatcher' => $dispatcher, # The value is obtained dynamically 'receiver' => '', # The dispatchers are disabled by default 'enabled' => $enabled, 'configuration_selected' => 'configuration_' . $dispatcher->ConfigurationMethod(), 'readOnly' => not $dispatcher->EditableByUser(), ); if ($dispatcher->ConfigurationMethod() eq 'none') { $params{configuration_none} = ''; } $self->addRow(%params); $modified = 1; } return $modified; } # Group: Protected methods # Method: _table # # The table description which consists of three fields: # # name - # description - # configuration - . It could have one of the following: # - model - # - link - # - none - # enabled - # # You can only edit enabled and configuration fields. The event # name and description are read-only fields. # sub _table { my @tableHeader = ( new EBox::Types::Boolean( fieldName => 'enabled', printableName => __('Enabled'), class => 'tcenter', type => 'boolean', size => 1, unique => 0, trailingText => '', editable => 1, # Set in order to store the type # metadata since sometimes the field # is editable and some not storeMetadata => 1, ), new EBox::Types::Text( fieldName => 'dispatcher', printableName => __('Name'), class => 'tleft', size => 12, unique => 1, editable => 0, optional => 0, filter => \&filterName, ), new EBox::Types::Text( fieldName => 'receiver', printableName => __('Receiver'), class => 'tcenter', size => 30, unique => 0, editable => 0, # The value is obtained dynamically volatile => 1, filter => \&filterReceiver, ), new EBox::Types::Union( fieldName => 'configuration', printableName => __('Configuration'), class => 'tcenter', editable => 0, subtypes => [ new EBox::Types::Link( fieldName => 'configuration_link', editable => 0, volatile => 1, acquirer => \&acquireURL, ), new EBox::Types::HasMany( fieldName => 'configuration_model', backView => '/Events/Composite/General', size => 1, trailingText => '', foreignModelAcquirer => \&acquireConfModel, ), new EBox::Types::Union::Text( fieldName => 'configuration_none', printableName => __('None'), ), ] ), ); my $dataTable = { tableName => 'ConfigureDispatchers', printableTableName => __('Configure Dispatchers'), actions => { editField => '/Events/Controller/ConfigureDispatchers', changeView => '/Events/Controller/ConfigureDispatchers', }, tableDescription => \@tableHeader, class => 'dataTable', order => 0, rowUnique => 1, printableRowName => __('dispatcher'), help => __('Enable/Disable each event dispatcher'), }; } # Group: Callback functions # Function: filterName # # Callback used to filter the output of the name field. It # localises the dispatcher name to the configured locale. # # Parameters: # # instancedType - the cell which will contain # the name # # Returns: # # String - localised the dispatcher name # sub filterName { my ($instancedType) = @_; my $className = $instancedType->value(); eval "use $className"; if ($@) { return undef; } my $dispatcher = $className->new(); return $dispatcher->name(); } # Function: filterReceiver # # Callback used to gather the value of the receiver field. It # localises the event receiver to the configured locale. # # Parameters: # # instancedType - the cell which will contain # the receiver # # Returns: # # String - localised the event receiver name # sub filterReceiver { my ($instancedType) = @_; my $className = $instancedType->row()->valueByName('dispatcher'); eval "use $className"; if ($@) { return undef; } my $dispatcher = $className->new(); return $dispatcher->receiver(); } # Function: acquireURL # # Callback function used to gather the URL that will fill the # value for the link # # Parameters: # # instancedType - the cell which will contain # the URL # # Returns: # # String - the URL # sub acquireURL { my ($instancedType) = @_; my $className = $instancedType->row()->valueByName('dispatcher'); eval "use $className"; if ($@) { return undef; } return $className->ConfigureURL(); } # Function: acquireConfModel # # Callback function used to gather the URL # in order to configure the event dispatcher # # Parameters: # # row - hash ref with the content what is stored in GConf # regarding to this row. # # Returns: # # String - the foreign model to configurate the dispatcher # sub acquireConfModel { my ($row) = @_; my $className = $row->valueByName('dispatcher'); eval "use $className"; if ($@) { return undef; } return $className->ConfigureModel(); } sub enableDispatcher { my ($self, $dispatcher, $enabled) = @_; my $row = $self->findRow(dispatcher => $dispatcher); if (not $row) { throw EBox::Exceptions::DataNotFound(data => 'dispatcher', value => $dispatcher); } $row->elementByName('enabled')->setValue($enabled); $row->store(); } sub isEnabledDispatcher { my ($self, $dispatcher) = @_; my $row = $self->findRow(dispatcher => $dispatcher); if (not $row) { throw EBox::Exceptions::DataNotFound(data => 'dispatcher', value => $dispatcher); } return $row->valueByName('enabled'); } # Group: Private methods # Fetch the current dispatchers on the system # Return an array ref with all the class names sub _fetchDispatchers { my ($self) = @_; my $mods = EBox::Global->modInstancesOfType('EBox::Events::DispatcherProvider'); my @dispatchers = map { "EBox::Event::Dispatcher::$_" } map { @{$_->eventDispatchers()} } @{$mods}; return \@dispatchers; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Events/Model/EventsDetails.pm0000664000000000000000000000665712017102272021675 0ustar # Copyright (C) 2009-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Events::Model::EventsDetails; use base 'EBox::Logs::Model::Details'; # use strict; use warnings; use EBox::Gettext; use EBox::Types::Text; use EBox::Types::Int; sub new { my $class = shift @_; my $self = $class->SUPER::new(@_); bless($self, $class); return $self; } sub dbTableName { return 'events_accummulated'; } sub dbFields { my ($package) = @_; return { source => { printableName => __('Source'), }, info => { printableName => __('Informative'), }, warn => { printableName => __('Warning'), }, error => { printableName => __('Error'), }, fatal => { printableName => __('Fatal error'), }, }; } sub _table { my $tableHead = [ new EBox::Types::Text( 'fieldName' => 'date', 'printableName' => __('Date'), 'size' => '12', editable => 0, ), new EBox::Types::Text( 'fieldName' => 'source', 'printableName' => __('Source'), 'size' => '12', editable => 0, ), new EBox::Types::Int( fieldName => 'info', printableName => __('Informative'), ), new EBox::Types::Int( fieldName => 'warn', printableName => __('Warning'), ), new EBox::Types::Int( fieldName => 'error', printableName => __('Error'), ), new EBox::Types::Int( fieldName => 'fatal', 'printableName' => __('Fatal error'), ), ]; my $dataTable = { 'tableName' =>__PACKAGE__->tableName(), 'printableTableName' => __('Events details'), 'defaultActions' => [ 'changeView', 'editField' ], 'defaultController' => '/Events/Controller/EventsReport', 'tableDescription' => $tableHead, 'class' => 'dataTable', 'order' => 0, 'rowUnique' => 0, 'printableRowName' => __('event'), 'sortedBy' => 'date', 'withoutActions' => 1, }; return $dataTable; } sub tableName { return 'EventsDetails'; } sub timePeriod { my ($self) = @_; my $model = $self->{confmodule}->reportOptionsModel(); my $row = $model->row(); return $row->valueByName('timePeriod'); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Events/Model/ConfigureWatchers.pm0000664000000000000000000003243512017102272022536 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: # # EBox::Events::Model::ConfigureWatchers # # This class is used as a model to describe a table which will be # used to select the event watchers the user wants to enable/disable # # It subclasses # use strict; use warnings; package EBox::Events::Model::ConfigureWatchers; use base 'EBox::Model::DataTable'; use EBox; use EBox::Config; use EBox::Exceptions::DataNotFound; use EBox::Gettext; use EBox::Model::Manager; use EBox::Types::HasMany; use EBox::Types::Link; use EBox::Types::Text; use EBox::Types::Union; use EBox::Types::Union::Text; use Error qw(:try); use constant SB_URL => 'https://store.zentyal.com/small-business-edition.html/?utm_source=zentyal&utm_medium=events&utm_campaign=smallbusiness_edition'; use constant ENT_URL => 'https://store.zentyal.com/enterprise-edition.html/?utm_source=zentyal&utm_medium=events&utm_campaign=enterprise_edition'; # Group: Public methods # Method: syncRows # # This method is overridden since the showed data is managed # differently. # # - The data is already available from the Zentyal installation # # - The adding/removal of event watchers is done dynamically # reading the directories where the event watchers are placed # # # Overrides: # # # sub syncRows { my ($self, $currentIds) = @_; my %storedEventWatchers; my %currentEventWatchers; my $watchersRef = $self->_fetchWatchers(); foreach my $watcherFetched (@{$watchersRef}) { $currentEventWatchers{$watcherFetched} = 'true'; } my $modified = 0; # Removing old ones foreach my $id (@{$currentIds}) { my $row; my $remove = 0; try { $row = $self->row($id); my $stored = $row->valueByName('watcher'); $storedEventWatchers{$stored} = 1; eval "use $stored"; if (exists $currentEventWatchers{$stored}) { # Check its ability my $able = $self->_checkWatcherAbility($stored); if (not $able and $self->_checkWatcherHidden($stored)) { $remove = 1; } else { $self->setTypedRow($id, undef, readOnly => not $able); } } else { $remove = 1; } } otherwise { $remove = 1; }; if ($remove) { $self->removeRow($id); $modified = 1; } } # Adding new ones foreach my $watcher (keys (%currentEventWatchers)) { next if (exists $storedEventWatchers{$watcher}); eval "use $watcher"; my $able = $self->_checkWatcherAbility($watcher); next if (not $able and $self->_checkWatcherHidden($watcher)); my $enabled = not $watcher->DisabledByDefault(); my %params = ('watcher' => $watcher, # The value is obtained dynamically 'description' => undef, 'enabled' => $enabled, 'configuration_selected' => 'configuration_' . $watcher->ConfigurationMethod(), 'readOnly' => not $able, ); if ( $watcher->ConfigurationMethod() eq 'none' ) { $params{configuration_none} = ''; } $self->addRow( %params ); $modified = 1; } return $modified; } # Method: updatedRowNotify # # Callback when the row has been updated. In this table model, # the main change is to switch the state from enabled to disabled # and viceversa. # # Overrides: # # # sub updatedRowNotify { my ($self, $row, $oldRow, $force) = @_; # Get whether the event watcher is enabled or not my $enabled = $row->valueByName('enabled'); my $className = $row->valueByName('watcher'); # if the class name is a the log one, check if any log observer is ready if ($className =~ m/::Log$/ and $enabled) { $self->_checkLogWatchers(); } } # Method: pageTitle # # Overrides: # # # # Returns: # # # undef sub pageTitle { return undef; } # Method: headTitle # # Overrides: # # # # Returns: # # # undef sub headTitle { return undef; } # Group: Protected methods # Method: _table # # The table description which consists of three fields: # # name - # description - # configuration - # # You can only edit enabled field to activate or deactivate the # event. The event name and description are read-only fields. # sub _table { my @tableHeader = ( new EBox::Types::Text( fieldName => 'watcher', printableName => __('Name'), class => 'tleft', size => 12, unique => 1, filter => \&filterName, ), new EBox::Types::Text( fieldName => 'description', printableName => __('Description'), size => 30, # The value is obtained dynamically volatile => 1, filter => \&filterDescription, ), new EBox::Types::Union( fieldName => 'configuration', printableName => __('Configuration'), editable => 0, subtypes => [ new EBox::Types::Link( fieldName => 'configuration_link', volatile => 1, acquirer => \&acquireURL, ), new EBox::Types::HasMany( fieldName => 'configuration_model', foreignModelAcquirer => \&acquireConfModel, backView => '/Events/Composite/General', ), new EBox::Types::Union::Text( fieldName => 'configuration_none', printableName => __('None'), ), ] ), ); my $dataTable = { tableName => 'ConfigureWatchers', printableTableName => __('Configure Events'), actions => { editField => '/Events/Controller/ConfigureWatchers', changeView => '/Events/Controller/ConfigureWatchers', }, tableDescription => \@tableHeader, class => 'dataTable', rowUnique => 1, printableRowName => __('event'), help => __('Enable/Disable each event watcher monitoring'), enableProperty => 1, defaultEnabledValue => 0, }; return $dataTable; } # Group: Callback functions # Function: filterName # # Callback used to filter the output of the name field. It # localises the event name to the configured locale. # # Parameters: # # instancedType - the cell containing the value # # Return: # # String - localised the event name # sub filterName { my ($instanceType) = @_; my $className = $instanceType->value(); eval "use $className"; if ($@) { return undef; } my $watcher = $className->new(); return $watcher->name(); } # Function: filterDescription # # Callback used to gather the value of the description field. It # localises the event description to the configured locale. # # Parameters: # # instancedType - the cell which will contain # the description # # Return: # # String - localised the description name # sub filterDescription { my ($instancedType) = @_; my $row = $instancedType->row(); if (not $row) { return undef; } my $className = $row->valueByName('watcher'); eval "use $className"; if ($@) { return undef; } my $watcher = $className->new(); return $watcher->description(); } # Function: acquireConfModel # # Callback function used to gather the foreignModel and its view # in order to configure the event watcher # # Parameters: # # row - hash ref with the content what is stored in GConf # regarding to this row. # # Returns: # # String - the foreign model to configurate the watcher # sub acquireConfModel { my ($row) = @_; my $className = $row->valueByName('watcher'); eval "use $className"; if ($@) { return undef; } return $className->ConfigureModel(); } # Function: acquireURL # # Callback function used to gather the URL that will fill the # value for the link # # Parameters: # # instancedType - the cell which will contain # the URL # # Returns: # # String - the URL # sub acquireURL { my ($instancedType) = @_; my $className = $instancedType->row()->valueByName('watcher'); eval "use $className"; if ($@) { return undef; } return $className->ConfigureURL(); } # Method: viewCustomizer # # Return a custom view customizer to set a permanent message # if needed # # Overrides: # # # sub viewCustomizer { my ($self) = @_; my $customizer = new EBox::View::Customizer(); $customizer->setModel($self); my $subscriptionLevel = -1; if (EBox::Global->modExists('remoteservices')) { my $rs = EBox::Global->modInstance('remoteservices'); $subscriptionLevel = $rs->subscriptionLevel(); } unless ($subscriptionLevel > 0) { $customizer->setPermanentMessage($self->_commercialMsg(), 'ad'); } return $customizer; } sub enableWatcher { my ($self, $watcher, $enabled) = @_; my $row = $self->findRow(watcher => $watcher); if (not $row) { throw EBox::Exceptions::DataNotFound(data => 'watcher', value => $watcher); } $row->elementByName('enabled')->setValue($enabled); $row->store(); } sub isEnabledWatcher { my ($self, $watcher) = @_; my $row = $self->findRow(watcher => $watcher); if (not $row) { throw EBox::Exceptions::DataNotFound(data => 'watcher', value => $watcher); } return $row->valueByName('enabled'); } # Group: Private methods # Fetch the current watchers on the system # Return an array ref with all the class names sub _fetchWatchers { my ($self) = @_; my $mods = EBox::Global->modInstancesOfType('EBox::Events::WatcherProvider'); my @watchers = map { "EBox::Event::Watcher::$_" } map { @{$_->eventWatchers()} } @{$mods}; return \@watchers; } # FIXME: check this # Method to check if there are any log watcher enabled sub _checkLogWatchers { my ($self) = @_; my $manager = EBox::Model::Manager->instance(); my $logWatcherConfModel = $manager->model('/' . $self->{confmodule}->name() . '/LogWatcherConfiguration'); # Find those log watchers that are enabled unless ( $logWatcherConfModel->find( enabled => 1 ) ) { $self->setMessage(__('Warning! There is no log domain watcher enabled. ' . q{Please, go to 'Configuration' to enable at least } . 'one to be notified when a log in this domain happens. ') . $self->message() ); } } # This method checks if the event watcher is able to monitor the # event. For example, a RAID watcher makes no sense if the disk # subsystem does not work with RAID sub _checkWatcherAbility { my ($self, $watcherClassName) = @_; return $watcherClassName->Able(); } # This method checks if the event watcher must be hidden if not able # to watch the events in order to not confuse the user sub _checkWatcherHidden { my ($self, $watcherClassName) = @_; return $watcherClassName->HiddenIfNotAble(); my $customizer = new EBox::View::Customizer(); $customizer->setModel($self); my $subscriptionLevel = -1; if (EBox::Global->modExists('remoteservices')) { my $rs = EBox::Global->modInstance('remoteservices'); $subscriptionLevel = $rs->subscriptionLevel(); } unless ($subscriptionLevel > 0) { $customizer->setPermanentMessage($self->_commercialMsg(), 'ad'); } return $customizer; } # Return the commercial message sub _commercialMsg { return __sx('Want to receive an alert when something has gone wrong in your system? Get the {ohs}Small Business{ch} or {ohe}Enterprise Edition{ch} to enable all automatic alerts.', ohs => '', ohe => '', ch => ''); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Events/Model/LogWatcherConfiguration.pm0000664000000000000000000002102212017102272023671 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Events::Model::LogWatcherConfiguration # # This class is the model to configurate Log watcher. It has as many # rows as logger exist in eBox # # The fields are the following: # # - name - the logger name (i18ned) # - filtering - model to configure as many filters as you may need # - enabled - enabled the event notification for that logger # package EBox::Events::Model::LogWatcherConfiguration; use strict; use warnings; use base 'EBox::Model::DataTable'; use EBox::Exceptions::DataNotFound; use EBox::Events::Model::LogFiltering; use EBox::Gettext; use EBox::Global; use EBox::Model::Manager; use EBox::Types::HasMany; use EBox::Types::Text; # Core modules use Error qw(:try); # Constants use constant FILTERING_MODEL_NAME => 'LogWatcherFiltering'; # Group: Public methods # Method: setUpModels # # Set in model manager the dynamic models for log watcher # configuration per domain # # The result of this method is to include in model manager the # dynamic models (one per log domain) # sub setUpModels { my ($self) = @_; my $manager = EBox::Model::Manager->instance(); my $readOnly = $self->parentModule()->isReadOnly(); my $logs = EBox::Global->getInstance($readOnly)->modInstance('logs'); my $logDomainTables = $logs->getAllTables(1); if (defined ( $logDomainTables)) { while (my ($domain, $tableInfo) = each %{$logDomainTables}) { next if ($domain eq 'events'); # avoid observe recursively itself! $manager->addModel($self->_createFilteringModel($domain, $tableInfo)); } } } # Method: syncRows # # Overrides: # # # # It is overriden because this table is kind of different in # comparation to the normal use of generic data tables. # # - The user does not add rows. When we detect the table is # empty we populate the table with the available log domains. # # - We check if we have to add/remove one the log domains. That happens # when a new module is installed or an existing one is removed. # sub syncRows { my ($self, $currentIds) = @_; my $anyChange = undef; my $logs = EBox::Global->modInstance('logs'); # Set up every dynamic model $self->setUpModels(); # Fetch the current log domains stored in conf my %storedLogDomains; foreach my $id (@{$currentIds}) { my $row = $self->row($id); $storedLogDomains{$row->valueByName('domain')} = 1; } # Fetch the current available log domains my %currentLogDomains; my $currentTables = $logs->getAllTables(1); foreach my $table (keys (%{$currentTables})) { next if ($table eq 'events'); # ignore events table $currentLogDomains{$table} = 1; } # Add new domains to conf foreach my $domain (keys %currentLogDomains) { next if (exists $storedLogDomains{$domain}); $self->addRow('domain' => $domain, 'enabled' => 0); $anyChange = 1; } # Remove non-existing domains from conf foreach my $id (@{$currentIds}) { my $row = $self->row($id); my $domain = $row->valueByName('domain'); next if (exists $currentLogDomains{$domain}); $self->removeRow($id); $self->_removeFilteringModel($domain); $anyChange = 1; } return $anyChange; } # Method: updatedRowNotify # # Overrides: # # # sub updatedRowNotify { my ($self, $row, $oldRow, $force) = @_; # Warn if the parent log observer is not enabled if ($row->valueByName('enabled')) { my $eventModel = EBox::Global->modInstance('events')->model('ConfigureWatchers'); my $logConfRow = $eventModel->findValue(eventWatcher => 'EBox::Event::Watcher::Log'); unless ($logConfRow->valueByName('enabled')) { $self->setMessage(__('Warning! The log watcher is not enabled. ' . 'Enable to be notified when logs happen. ' . $self->message())); } } } # Method: addedRowNotify # # Overrides: # # # sub addedRowNotify { my ($self, $row, $force) = @_; # Warn if the parent log observer is not enabled if ( $row->valueByName('enabled') ) { my $eventModel = EBox::Global->modInstance('events')->model('ConfigureWatchers'); my $logConfRow = $eventModel->findValue( eventWatcher => 'EBox::Event::Watcher::Log' ); unless ( $logConfRow->valueByName('enabled') ) { $self->setMessage(__('Warning! The log watcher is not enabled. ' . 'Enable to be notified when logs happen. ' . $self->message())); } } } # Group: Protected methods # Method: _table # # Overrides: # # # sub _table { my @tableDesc = ( new EBox::Types::Text( fieldName => 'domain', printableName => __('Domain'), editable => 0, ), new EBox::Types::HasMany( fieldName => 'filters', printableName => __('Filtering'), foreignModelAcquirer => \&acquireFilteringModel, backView => '/Events/View/LogWatcherConfiguration?directory=Log', ), ); my $dataForm = { tableName => 'LogWatcherConfiguration', printableTableName => __('Configure log watchers'), modelDomain => 'Events', printableRowName => __('Log watcher'), defaultActions => [ 'editField', 'changeView' ], tableDescription => \@tableDesc, class => 'dataTable', help => '', enableProperty => 1, defaultEnabledValue => 0, }; return $dataForm; } # Group: Callback functions # Function: acquireFilteringModel # # Callback function used to gather the foreignModel and its view # in order to configure the log event watcher filters # # Parameters: # # row - hash ref with the content what is stored in GConf # regarding to this row. # # Returns: # # String - the foreign model to configurate the filters # associated to the log event watcher # sub acquireFilteringModel { my ($row) = @_; my $logDomain = $row->valueByName('domain'); return 'events/' . FILTERING_MODEL_NAME . "_$logDomain"; } # Group: Private methods # Create a new filtering model given a # log domain and notify this new model to model manager sub _createFilteringModel { my ($self, $domain, $domainTableInfo) = @_; if (not defined $domainTableInfo) { my $logs = EBox::Global->modInstance('logs'); $domainTableInfo = $logs->getTableInfo($domain); } my $filteringModel = new EBox::Events::Model::LogFiltering(confmodule => $self->{confmodule}, directory => $self->{confdir}, tableInfo => $domainTableInfo); return $filteringModel; } # Remove an existing filtering model given a # log domain and notify this removal to model manager sub _removeFilteringModel { my ($self, $domain) = @_; my $modelManager = EBox::Model::Manager->instance(); $modelManager->removeModel('events/' . FILTERING_MODEL_NAME . "_$domain"); } # Method: viewCustomizer # # Overrides to # provide a custom HTML title with breadcrumbs # sub viewCustomizer { my ($self) = @_; my $custom = $self->SUPER::viewCustomizer(); $custom->setHTMLTitle([ { title => __('Events'), link => '/Events/Composite/General', }, { title => __('Log Observer Watcher'), link => '' } ]); return $custom; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Events/Model/JabberDispatcherConfiguration.pm0000664000000000000000000001030512017102272025030 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Events::Model::Dispatcher::Jabber # # package EBox::Events::Model::JabberDispatcherConfiguration; use base 'EBox::Model::DataForm'; use strict; use warnings; use EBox::Global; use EBox::Gettext; use EBox::Types::Port; use EBox::Types::Text; use EBox::Types::Host; use EBox::Types::Select; use EBox::Types::Password; use EBox::Types::Select; # Group: Public methods # Constructor: new # # Create the configure jabber dispatcher form # # Overrides: # # # # Returns: # # # sub new { my $class = shift; my $self = $class->SUPER::new(@_); bless ($self, $class); return $self; } # Group: Protected methods sub _populateSSL { my @opts = (); push (@opts, { value => 'none', printableValue => __('None') }); push (@opts, { value => 'ssl', printableValue => __('SSL') }); push (@opts, { value => 'tls', printableValue => __('TLS') }); return \@opts; } # Method: _table # # Overrides: # # # sub _table { my @tableDesc = ( new EBox::Types::Host( fieldName => 'server', printableName => __('Jabber Server'), size => 12, editable => 1, ), new EBox::Types::Port( fieldName => 'port', printableName => __('Port'), size => 4, editable => 1, defaultValue => 5222, ), new EBox::Types::Select( fieldName => 'ssl', printableName => __('SSL'), editable => 1, populate => \&_populateSSL, ), new EBox::Types::Text( fieldName => 'user', printableName => __('Username'), size => 12, editable => 1, ), new EBox::Types::Password( fieldName => 'password', printableName => __('Password'), size => 12, editable => 1, ), new EBox::Types::Text( fieldName => 'adminJID', printableName => __('Administrator Account'), size => 18, editable => 1, help => __('Destination Jabber account to send the messages to.'), ), ); my $dataForm = { tableName => 'JabberDispatcherConfiguration', printableTableName => __('Configure Jabber Dispatcher'), modelDomain => 'Events', defaultActions => [ 'editField' ], tableDescription => \@tableDesc, class => 'dataForm', help => __('This dispatcher will send ' . 'events to a Jabber account.'), messages => { update => __('Jabber dispatcher configuration updated.'), }, }; return $dataForm; } # Method: viewCustomizer # # Overrides to # provide a custom HTML title with breadcrumbs # sub viewCustomizer { my ($self) = @_; my $custom = $self->SUPER::viewCustomizer(); $custom->setHTMLTitle([ { title => __('Events'), link => '/Events/Composite/General#ConfigureDispatchers', }, { title => __('Jabber Dispatcher'), link => '' } ]); return $custom; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Events/Model/DiskFreeWatcherConfiguration.pm0000664000000000000000000000734412017102272024657 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Events::Model::DiskFreeWatcherConfiguration # # This class is the model to configurate DiskFreeSpace watcher. It has # a single field in a form: # # The field is the following: # # - spaceThreshold - Int the minimum disk free space before # notifying the user of lack of space in a disk # use strict; use warnings; package EBox::Events::Model::DiskFreeWatcherConfiguration; use base 'EBox::Model::DataForm'; use EBox::Exceptions::External; use EBox::Gettext; use EBox::Types::Int; # Core modules # Group: Public methods # Constructor: new # # Create the configure the log watchers # # Overrides: # # # # Returns: # # # sub new { my $class = shift; my $self = $class->SUPER::new(@_); bless ( $self, $class); return $self; } # Method: validateTypedRow # # Overrides: # # # sub validateTypedRow { my ($self, $action, $changedFields, $allFields) = @_; if ( exists ( $changedFields->{spaceThreshold} )) { my $spaceThreshold = $changedFields->{spaceThreshold}->value(); unless ( $spaceThreshold > 0 and $spaceThreshold < 100 ) { throw EBox::Exceptions::External('The allowed values for the ' . 'minimum free disk space must ' . 'be in the interval (1, 99)'); } } } # Group: Protected methods # Method: _table # # Overrides: # # # sub _table { my @tableDesc = ( new EBox::Types::Int( fieldName => 'spaceThreshold', printableName => __('Minimum free disk space per filesystem'), editable => 1, size => 4, trailingText => '%', defaultValue => 10, help => __('When the free space percentage of any ' . 'disk partition is under this value the event is triggered.') ), ); my $dataForm = { tableName => 'DiskFreeWatcherConfiguration', printableTableName => __('Configure disk free space watcher'), modelDomain => 'Events', defaultActions => [ 'editField', 'changeView' ], tableDescription => \@tableDesc, class => 'dataForm', help => '', }; return $dataForm; } # Method: viewCustomizer # # Overrides to # provide a custom HTML title with breadcrumbs # sub viewCustomizer { my ($self) = @_; my $custom = $self->SUPER::viewCustomizer(); $custom->setHTMLTitle([ { title => __('Events'), link => '/Events/Composite/General', }, { title => __('Free Disk Space Watcher'), link => '' } ]); return $custom; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Events/Model/RSSDispatcherConfiguration.pm0000664000000000000000000002065012017102272024316 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Events::Model::Dispatcher::RSS # # This class is the model to configurate RSS dispatcher. It # inherits from since it is not a table but a # simple form with two fields: # # - link # - allowed - the allowed readers # use strict; use warnings; package EBox::Events::Model::RSSDispatcherConfiguration; use base 'EBox::Model::DataForm'; use EBox::Config; use EBox::Event::Dispatcher::RSS; use EBox::Gettext; use EBox::Global; use EBox::Types::IPAddr; use EBox::Types::Link; use EBox::Types::Select; use EBox::Types::Union; use EBox::Types::Union::Text; ################ # Dependencies ################ use XML::RSS; # Constants # Group: Public methods # Constructor: new # # Create the configure RSS dispatcher form # # Overrides: # # # # Returns: # # # sub new { my $class = shift; my $self = $class->SUPER::new(@_); bless ($self, $class); return $self; } # Method: formSubmitted # # When the form is submitted, the model must set up the jabber # dispatcher client service and sets the output rule in the # firewall # # Overrides: # # # sub formSubmitted { my ($self, $oldRow) = @_; # Set the restricted file in Apache-perl configuration my $selectedAllowed = $self->allowedType()->selectedType(); my @ips = (); if ( $selectedAllowed eq 'allowedNobody' ) { push ( @ips, 'nobody'); } elsif ( $selectedAllowed eq 'allowedIP' ) { push ( @ips, $self->allowedPrintableValue()); } elsif ( $selectedAllowed eq 'allowedObject' ) { my $objMod = EBox::Global->modInstance('objects'); my $objIPs = $objMod->objectAddresses( $self->allowedValue() ); if ( @{$objIPs} > 0 ) { push ( @ips, @{$objIPs} ); } } elsif ( $selectedAllowed eq 'allowedAll' ) { push( @ips, 'all'); } my $apacheMod = EBox::Global->modInstance('apache'); my $rssFilePath = EBox::Event::Dispatcher::RSS::RSSFilePath(); my $dynamicWWWPath = EBox::Config::dynamicwww(); $rssFilePath =~ s:$dynamicWWWPath:/dynamic-data/:; if ( @ips > 0 ) { $apacheMod->setRestrictedResource($rssFilePath, \@ips, 'location'); } } # Group: Protected methods # Method: _table # # Overrides: # # # sub _table { my ($self) = @_; my $gl = EBox::Global->getInstance(); my @subtypesAllowed = ( new EBox::Types::Union::Text( fieldName => 'allowedNobody', printableName => __('Nobody'), ), new EBox::Types::IPAddr( fieldName => 'allowedIP', printableName => __('IP address'), editable => 1, )); if ( $gl->modExists('objects')) { push(@subtypesAllowed, new EBox::Types::Select( fieldName => 'allowedObject', printableName => __('Object'), editable => 1, foreignModel => $self->modelGetter('objects', 'ObjectTable'), foreignField => 'name', )); } push(@subtypesAllowed, new EBox::Types::Union::Text( fieldName => 'allowedAll', printableName => __('Public'), )); my @tableDesc = ( new EBox::Types::Text( fieldName => 'link', printableName => __('Channel link'), editable => 1, size => 30, defaultValue => $self->_defaultChannelLink(), ), new EBox::Types::Union( fieldName => 'allowed', printableName => __('Allowed readers'), editable => 1, subtypes => \@subtypesAllowed, help => __('Use this field to set the ' . 'access control for the channel') ), new EBox::Types::Link( fieldName => 'linkToRSS', printableName => __('Syndicate this RSS'), volatile => 1, acquirer => \&setLinkToRSS, HTMLViewer => '/ajax/viewer/linkRSS.mas', HTMLSetter => '/ajax/viewer/linkRSS.mas', ), ); my $dataForm = { tableName => 'RSSDispatcherConfiguration', printableTableName => __('Configure RSS dispatcher'), modelDomain => 'Events', defaultActions => [ 'editField' ], tableDescription => \@tableDesc, class => 'dataForm', help => __('The channel link is the link which is used to ' . 'syndicate the content to your favourite RSS. '), messages => { update => __('RSS dispatcher configuration updated'), }, }; return $dataForm; } # Group: Callback functions # Function: setLinkToRSS # # Acquirer for the link to RSS which is displayed on # configuration to syndicate the eBox alerts # # Returns: # # String - the url to the RSS to syndicate # sub setLinkToRSS { my ($row) = @_; my $rssPath = EBox::Event::Dispatcher::RSS::RSSFilePath(); my $dynamicWWWPath = EBox::Config::dynamicwww(); $rssPath =~ s:$dynamicWWWPath::; return '/dynamic-data/' . $rssPath; } # Group: Private methods # Update link shown at the RSS channel sub _updateLinkInRSS { my ($self, $ip) = @_; my $rss = new XML::RSS(version => '2.0'); # Locking exclusively EBox::Event::Dispatcher::RSS::LockRSSFile(1); $rss->parsefile(EBox::Event::Dispatcher::RSS::RSSFilePath()); # Update link my $channelLink = $rss->channel('link'); $channelLink =~ s{https://.*?/}{https://$ip/}g; $rss->channel(link => $channelLink); $rss->save(); EBox::Event::Dispatcher::RSS::UnlockRSSFile(); } # Set the default channel link sub _defaultChannelLink { my ($self) = @_; my $gl = EBox::Global->getInstance(); if ( $gl->modExists('network') ) { my $netMod = $gl->modInstance('network'); my @ifaces = @{$netMod->ifaces()}; my $idx = 0; my $ip = ''; do { $ip = $netMod->ifaceAddress($ifaces[$idx]); $idx++; } while ( not $ip and $idx < scalar(@ifaces)); if ( $ip ) { return "https://$ip/"; } } return 'http://zentyal.org'; } # Method: viewCustomizer # # Overrides to # provide a custom HTML title with breadcrumbs # sub viewCustomizer { my ($self) = @_; my $custom = $self->SUPER::viewCustomizer(); $custom->setHTMLTitle([ { title => __('Events'), link => '/Events/Composite/General#ConfigureDispatchers', }, { title => __('RSS Dispatcher'), link => '' } ]); return $custom; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Events/DispatcherProvider.pm0000664000000000000000000000232712017102272021652 0ustar # Copyright (C) 2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Events::DispatcherProvider # # This interface needs to be implemented for those modules which add # custom watchers for the events module use strict; use warnings; package EBox::Events::DispatcherProvider; use EBox::Exceptions::NotImplemented; # Method: eventDispatchers # # This function must return the names of the watcher classes # without the "EBox::Event::Dispatcher" prefix. # # Returns: # # array ref - containing the watcher names # sub eventDispatchers { throw EBox::Exceptions::NotImplemented(); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Loggerd.pm0000664000000000000000000001136712017102272016174 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Loggerd; use strict; use warnings; use EBox; use EBox::Global; use EBox::Config; use EBox::Gettext; use EBox::DBEngineFactory; use EBox::Exceptions::Internal; use Error qw(:try); use Fcntl; use Linux::Inotify2; use POSIX; use File::Basename; use constant BUFFER_SIZE => 65536; sub new { my $class = shift; my $self = {}; my %opts = @_; $self->{inotify} = undef; $self->{filehandlers} = {}; $self->{buffers} = {}; $self->{period} = EBox::Config::configkey('multi_insert_interval'); bless($self, $class); return $self; } sub run { my ($self) = @_; $self->initDaemon(); EBox::init(); my $global = EBox::Global->getInstance(); my $log = $global->modInstance('logs'); $self->{'loghelpers'} = $log->allEnabledLogHelpers(); $self->{'dbengine'} = EBox::DBEngineFactory::DBEngine(); $self->_prepare(); $self->_mainloop(); } sub initDaemon { my ($self) = @_; unless (POSIX::setsid) { EBox::error('Cannot start new session for ', $self->{'name'}); exit 1; } foreach my $fd (0 .. 64) { POSIX::close($fd); } my $tmp = EBox::Config::tmp(); open (STDIN, "+<$tmp/stdin"); if (EBox::Config::boolean('debug')) { open (STDOUT, "+>$tmp/stout"); open (STDERR, "+>$tmp/stderr"); } } # Method: _prepare # # Init the necessary stuff, such as open fifos, use required classes, etc. # sub _prepare # (fifo) { my ($self) = @_; $self->{inotify} = new Linux::Inotify2 or EBox::error("Unable to create inotify object: $!"); $self->{inotify}->blocking(0); my @loghelpers = @{$self->{'loghelpers'}}; for my $obj (@loghelpers) { for my $file (@{$obj->logFiles()}) { my $FH; unless (exists $self->{filehandlers}->{$file}) { my $skip = 0; try { my $dir = dirname($file); $self->{inotify}->watch($dir, IN_CREATE | IN_DELETE); $self->{inotify}->watch($file, IN_MODIFY | IN_DELETE_SELF | IN_MOVE_SELF); sysopen($FH, $file, O_RDONLY); sysseek($FH, 0, SEEK_END); } otherwise { EBox::warn("Error creating inotify watch on $file: $!"); $skip = 1; }; next if $skip; $self->{filehandlers}->{$file} = $FH; $self->{buffers}->{$file} = ''; } push @{$self->{'objects'}->{$file}}, $obj; } } } sub _parseLog { my ($self, $file) = @_; my $buffer; my $FH = $self->{filehandlers}->{$file}; return unless defined ($FH); my $bytes = sysread($FH, $buffer, BUFFER_SIZE); return unless ($bytes); my $line; # Append the rest of the previous non-complete line my $rest = $self->{buffers}->{$file} . $buffer; while (($line, $rest) = $rest =~ m/^([^\n]+\n)?(.+)?$/s) { last unless ($line); chomp ($line); for my $obj (@{$self->{'objects'}->{$file}}) { try { $obj->processLine($file, $line, $self->{'dbengine'}); } otherwise { EBox::warn("Error processing line $line of $file: $@"); }; } last unless ($rest); } $self->{buffers}->{$file} = ($rest or ''); } sub _mainloop { my ($self) = @_; while () { my @events = $self->{inotify}->read(); foreach my $event (@events) { my $file = $event->fullname(); if ($event->IN_MODIFY) { $self->_parseLog($file); } elsif ($event->IN_CREATE) { sysopen(my $FH, $file, O_RDONLY); $self->{filehandlers}->{$file} = $FH; } else { # IN_DELETE || IN_MOVE close($self->{filehandlers}->{$file}); $self->{filehandlers}->{$file} = undef; } } $self->{'dbengine'}->multiInsert(); sleep $self->{period}; } } 1; zentyal-core-2.3.21+quantal1/src/EBox/Event/0000775000000000000000000000000012017102272015324 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Event/Dispatcher/0000775000000000000000000000000012017102272017412 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Event/Dispatcher/RSS.pm0000664000000000000000000001752312017102272020427 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Event::Dispatcher::RSS; # Class: EBox::Dispatcher::RSS # # This class is a dispatcher which stores the Zentyal events in a single # file within the channel # use base 'EBox::Event::Dispatcher::Abstract'; use strict; use warnings; use EBox::Config; use EBox::Exceptions::MissingArgument; use EBox::Exceptions::External; use EBox::Exceptions::Internal; use EBox::Exceptions::Lock; use EBox::Gettext; use EBox::Global; use EBox::Model::Manager; ################ # Core modules ################ use Sys::Hostname; use Fcntl qw(:flock); use POSIX qw(strftime); ################ # Dependencies ################ use XML::RSS; # Constants use constant { RSS_FILE => EBox::Config::dynamicRSS() . 'alerts.rss', RSS_LOCK_FILE => EBox::Config::tmp() . 'alerts.rss.lock', CHANNEL_TTL => 5, MAX_RSS_ITEMS => 500 }; # Class data our $LockFH; # Group: Public methods # Constructor: new # # The constructor for # # # Returns: # # - the newly created object # sub new { my ($class) = @_; my $self = $class->SUPER::new('ebox'); bless( $self, $class ); return $self; } # Method: configured # # Overrides: # # # sub configured { my ($self) = @_; return 1; } # Method: send # # Send the event to the admin using Jabber protocol # # Overrides: # # # sub send { my ($self, $event) = @_; defined ( $event ) or throw EBox::Exceptions::MissingArgument('event'); $self->_addEventToRSS($event); return 1; } # Group: Static class methods # Method: RSSFilePath # # Get the RSS file path # # Returns: # # String - the RSS file path # sub RSSFilePath { return RSS_FILE; } # Method: LockRSSFile # # Lock the RSS file for working with it. This call is blocking # is said so until the other process releases the lock using # # # Parameters: # # exclusive - boolean indicating if the lock for the RSS file is # asked exclusively or not # sub LockRSSFile { my ($class, $exclusive) = @_; open($LockFH, '+>', $class->_RSSLockFilePath()) or throw EBox::Exceptions::Internal('Cannot open lock file ' . $class->_RSSLockFilePath() . ": $!"); my $flag = $exclusive ? LOCK_EX : LOCK_SH; flock($LockFH, $flag) or throw EBox::Exceptions::Lock($class); } # Method: UnlockRSSFile # # Release the lock for the RSS file after working with it. This # call must be done after locking the RSS file using # # sub UnlockRSSFile { my ($class) = @_; my $flag = LOCK_UN; flock($LockFH, $flag); close($LockFH); } # Method: ConfigurationMethod # # Overrides: # # # sub ConfigurationMethod { return 'model'; } # Method: ConfigureModel # # Overrides: # # # sub ConfigureModel { return 'RSSDispatcherConfiguration'; } # Group: Protected methods # Method: _receiver # # Overrides: # # # sub _receiver { return __x('RSS file: {path}', path => 'Alerts'); } # Method: _name # # Overrides: # # # sub _name { return __('RSS'); } # Method: _enable # # Overrides: # # # sub _enable { my ($self) = @_; # Check how the public access is done my $confModel = $self->configurationSubModel(__PACKAGE__); my $allowedType = $confModel->allowedType(); if ( $allowedType->selectedType() eq 'allowedNobody' ) { EBox::warn('There are no allowed readers for the RSS'); } return 1; } # Group: Private methods # Add the event to the RSS file, if it does exists, create a new one sub _addEventToRSS { my ($self, $event) = @_; my $confModel = $self->configurationSubModel(__PACKAGE__); my $rss = new XML::RSS(version => '2.0'); # Locking exclusively $self->LockRSSFile(1); my $rssComplaintDate = $self->_currentDate(); my $create = undef; if ( not -r RSS_FILE ) { $create = 1; } else { eval '$rss->parsefile(RSS_FILE)'; if ($@) { unlink(RSS_FILE); $create = 1; $rss = new XML::RSS(version => '2.0'); } } if ($create) { # Create the channel if it does not exist $rss->channel(title => __x('Zentyal alerts channel for {hostname}', hostname => hostname()), link => $confModel->linkValue(), description => __('This channel tracks what happens on ' . 'this Zentyal machine along the time'), language => $self->_currentLanguage(), # pubDate => $rssComplaintDate, lastBuildDate => $rssComplaintDate, ttl => CHANNEL_TTL, ); unless (EBox::Config::configkey('custom_prefix')) { $rss->image(title => 'Zentyal', url => 'http://static.zentyal.org/img/zentyal.png', link => 'http://zentyal.org', description => 'Zentyal', alt => 'Zentyal', ); } } # Update the lastBuildDate and pubDate $rss->channel(pubDate => $rssComplaintDate, lastBuildDate => $rssComplaintDate ); my $descriptionStr = __x('The event has happened in Zentyal {hostname} ' . 'from {source}', hostname => hostname(), source => $event->source()); my $url = '' . __('Zentyal') . ''; $descriptionStr .= '

    ' . __x('Go to your {url} to check its status.', url => $url); $rss->add_item( description => $descriptionStr, title => ($event->level() . ' : ' . $event->message()), pubDate => $event->strTimestamp(), category => $event->source(), guid => $event->source() . '-' . $event->timestamp(), ); # Remove entries to MAX_RSS_ITEMS my $length = $rss->{'num_items'}; if ($length > MAX_RSS_ITEMS) { splice(@{$rss->{'items'}}, - MAX_RSS_ITEMS, ($length - MAX_RSS_ITEMS)); } $rss->save(RSS_FILE); $self->UnlockRSSFile(); } sub _RSSLockFilePath { return RSS_LOCK_FILE; } # Get the current language sub _currentLanguage { my $lang = $ENV{LANG}; $lang =~ s:\..*$::g; $lang =~ s:_:-:g; return $lang; } # Get the current date in RSS 2.0 complaint way sub _currentDate { return strftime("%a, %d %b %Y %T %z", localtime(time())); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Event/Dispatcher/Log.pm0000664000000000000000000000674412017102272020504 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Event::Dispatcher::Log; # Class: EBox::Dispatcher::Log # # This class is a dispatcher which sends the event to the eBox log. # use base 'EBox::Event::Dispatcher::Abstract'; ################ # Dependencies ################ use Data::Dumper; use EBox; use EBox::Gettext; use EBox::Exceptions::MissingArgument; use constant LOG_FILE => EBox::Config::log() . 'events.log'; # Group: Public methods # Constructor: new # # The constructor for # # # Returns: # # - the newly created object # sub new { my ($class) = @_; my $self = $class->SUPER::new('ebox'); bless ($self, $class); return $self; } # Method: DisabledByDefault # # Overrides # to enable it by default # sub DisabledByDefault { return 0; } # Method: EditableByUser # # Overrides # to not allow the user to disable it sub EditableByUser { return 0; } # Method: ConfigurationMethod # # Overrides: # # # sub ConfigurationMethod { return 'none'; } # Method: configured # # Overrides: # # # sub configured { return 'true'; } # Method: send # # Send the event to the eBox log system # # Overrides: # # # sub send { my ($self, $event) = @_; defined ($event) or throw EBox::Exceptions::MissingArgument('event'); open (my $logfile, '>>', LOG_FILE); my $timestamp = POSIX::strftime("%d/%m/%Y %H:%M:%S", localtime(EBox::Event::timestamp($event))); my $debug = EBox::Config::boolean('debug'); #Print the event into the log file print $logfile "$timestamp "; if (defined $event->{duration}) { print $logfile '(' . $event->{duration} . ' s) '; } print $logfile uc($event->{level}) . '> ' . $event->{source}; if ($debug && defined $event->{dispatchers}) { print $logfile '->['; my $count = 0; for my $disp (@{$event->{dispatchers}}) { $count++; print $logfile ($count>1 ? ',' : '' ) . "'$disp'"; } print $logfile ']'; } print $logfile ': ' . $event->{message}; if ($debug && defined $event->{compMessage}) { print $logfile ' (compMessage: ' . $event->{compMessage} . ')'; } print $logfile "\n"; close ($logfile); return 1; } # Group: Protected methods # Method: _receiver # # Overrides: # # # sub _name { return __('Log'); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Event/Dispatcher/Abstract.pm0000664000000000000000000001237312017102272021521 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Event::Dispatcher::Abstract; # Class: EBox::Event::Dispatcher::Abstract # # This class is the base for developing all event dispatchers by any # module. It should inherit in order to have support for dispatching # events within eBox framework. Every subclass should just dispatch an # event from fixed transport way # use strict; use warnings; use base 'EBox::Event::Component'; use EBox::Exceptions::NotImplemented; use EBox::Exceptions::MissingArgument; use EBox::Event; use EBox::Gettext; use EBox::Model::Manager; # Constructor: new # # The constructor for the object # # Returns: # # - the newly created object # # Exceptions: # # - thrown if any argument # is missing # sub new { my ($class) = @_; my $self = $class->SUPER::new(); bless ( $self, $class); return $self; } # Method: receiver # # Accessor to the receiver of what this event dispatcher # does. If is not # overridden, an empty string is returned. # # Returns: # # String - the detailed description # sub receiver { my ($self) = @_; my $receiver = $self->_receiver(); return $receiver; } # Method: configured # # Indicate if the dispatcher transport layer is already # configured or not to send the events *(Abstract)* # # Returns: # # boolean - whether the dispatcher is already configured or # not # sub configured { throw EBox::Exceptions::NotImplemented(); } # Method: enable # # Set the dispatcher to work through a given configuration. This # method makes sure that is already configured and it will # test that it is enable to send the information to the # receiver. # # An example could be the control center dispatcher that it will # be test its connectivity to the listening server. # # Returns: # # true - indicating the dispatcher is enabled to send # events # # Exceptions: # # - thrown if the dispatcher is not # able to send events # sub enable { my ($self) = @_; if ($self->configured()) { $self->_enable(); } else { throw EBox::Exceptions::External(__x('Dispatcher {name} is not ' . 'configured to be enabled', name => $self->name())); } } # Method: send # # Send an event through its own transport layer. It must be # overriden. *(Abstract)* # # Parameters: # # event - the event to dispatch # # Returns: # # boolean - whether the event has been sent or not # # Exceptions: # # - thrown if any compulsory # argument is missing # sub send { throw EBox::Exceptions::NotImplemented(); } # Method: configurationSubModel # # Fetch the configuration submodel for a given # event dispacher. # # Given a class name it will look up the row # of the model which contains this class, and # it will return its configuration model # # Parameters: # # package - String containing a class to look up # # Returns: # # An instance of # # sub configurationSubModel { my ($self, $package) = @_; defined ( $package ) or throw EBox::Exceptions::MissingArgument('package'); my $manager = EBox::Model::Manager->instance(); my $watchers = $manager->model('events/ConfigureDispatcherDataTable'); for my $id (@{$watchers->ids()}) { my $row = $watchers->row($id); next unless ($row->valueByName('eventDispatcher') eq $package); return $row->subModel('configuration_model'); } } # Group: Protected methods # Method: _description # # The i18ned method to describe the event receiver. To be # overridden by subclasses. # # Returns: # # String - the receiver description. Default value: an empty # string. # sub _receiver { # Default empty implementation return ''; } # Method: _enable # # It will test that it is enable to send the information to the # receiver. It assumes that some configuration is already # given. *(Abstract)* # # An example could be the control center dispatcher that it will # be test its connectivity to the listening server. # # Returns: # # true - if the dispatcher is enabled to send events # # Exceptions: # # - thrown if the dispatcher is not # able to send events # sub _enable { return 1; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Event/Dispatcher/Jabber.pm0000664000000000000000000001477212017102272021150 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Dispatcher::Jabber # # This class is a dispatcher which sends events to a Jabber account # # TODO: Disconnect seamlessly from the Jabber server # TODO: Send presence from time to time package EBox::Event::Dispatcher::Jabber; use base 'EBox::Event::Dispatcher::Abstract'; use strict; use warnings; use EBox::Gettext; use EBox::Model::Manager; use EBox::Exceptions::MissingArgument; use EBox::Exceptions::External; use Net::XMPP; use Sys::Hostname; # Group: Public methods # Constructor: new # # The constructor for # # Returns: # # - the newly created object # sub new { my ($class) = @_; my $self = $class->SUPER::new('ebox'); bless ($self, $class); $self->{resource} = 'Zentyal'; $self->{ready} = 0; return $self; } # Method: configured # # Overrides: # # # sub configured { my ($self) = @_; # get parameters from the model $self->_jabberDispatcherParams(); # Jabber dispatcher is configured only if the values from the # configuration model are set return ($self->{server} and $self->{port} and $self->{user} and $self->{password} and $self->{adminJID}); } # Method: ConfigurationMethod # # Overrides: # # # sub ConfigurationMethod { return 'model'; } # Method: ConfigureModel # # Overrides: # # # sub ConfigureModel { return 'JabberDispatcherConfiguration'; } # Method: send # # Send the event to the admin using Jabber protocol # # Overrides: # # # sub send { my ($self, $event) = @_; defined ($event) or throw EBox::Exceptions::MissingArgument('event'); unless ( $self->{ready} ) { $self->enable(); } # send to the jabber my $msg = $self->_createEventMessage($event); $self->{connection}->Send($msg); return 1; } # Group: Protected methods # Method: _receiver # # Overrides: # # # sub _receiver { return __('Jabber Account'); } # Method: _name # # Overrides: # # # sub _name { return __('Jabber'); } # Method: _enable # # Overrides: # # # sub _enable { my ($self) = @_; $self->_jabberDispatcherParams(); # don't reenable a connection when it's already connected if ( defined ($self->{connection}) ) { if ( $self->{connection}->Connected() ) { # just send a presence send and return $self->{connection}->PresenceSend(); } else { # destroy previous connection and reconnect $self->{connection}->Disconnect(); $self->_confClient(); } } else { $self->_confClient(); } } # Group: Private methods # configure the Jabber connection sub _confClient { my ($self) = @_; $self->{connection} = new Net::XMPP::Client(); # gtalk needs this to work my $comp = undef; if ($self->{server} eq 'talk.google.com') { $comp = 'gmail.com'; } my $status = $self->{connection}->Connect( hostname => $self->{server}, port => $self->{port}, tls => $self->{tls}, ssl => $self->{ssl}, connectiontype => 'tcpip', componentname => $comp, ); unless ( defined ($status) ) { throw EBox::Exceptions::External(__x('Jabber server {serverName}' . ' is down or connection is not allowed.', serverName => $self->{server}) ); } if ($comp) { my $sid = $self->{connection}->{SESSION}->{id}; $self->{connection}->{STREAM}->{SIDS}->{$sid}->{hostname} = $comp; } my @authResult = $self->{connection}->AuthSend( username => $self->{user}, password => $self->{password}, resource => $self->{resource}, ); unless ( defined ($authResult[0]) ) { $self->_problems('AuthSend'); } unless ( $authResult[0] eq 'ok' ) { throw EBox::Exceptions::External(__x('Authorization failed: ' . '{result} - {message}', result => $authResult[0], message => $authResult[1]) ); } $self->{connection}->PresenceSend(); $self->{ready} = 1; } # populate the message with the event sub _createEventMessage # (event) { my ($self, $event) = @_; my $msg = new Net::XMPP::Message(); my $hostname = Sys::Hostname::hostname(); $msg->SetMessage( to => $self->{adminJID}, type => 'chat', subject => 'Zentyal event on' . $hostname, body => $hostname .' ['. $event->level() .']: '. $event->message(), ); return $msg; } sub _emptyCallback { return; } # get configuration sub _jabberDispatcherParams { my ($self) = @_; my $model = $self->configurationSubModel(__PACKAGE__); my $row = $model->row(); return unless defined ( $row ); $self->{server} = $row->valueByName('server'); $self->{port} = $row->valueByName('port'); $self->{user} = $row->valueByName('user'); $self->{password} = $row->valueByName('password'); $self->{adminJID} = $row->valueByName('adminJID'); $self->{ssl} = $row->valueByName('ssl') eq 'ssl' ? 1 : 0; $self->{tls} = $row->valueByName('ssl') eq 'tls' ? 1 : 0; } # method to get the error code sub _problems { my ($self, $methodName) = @_; EBox::error("Error processing $methodName." . $self->{connection}->GetErrorCode()); throw EBox::Exceptions::External('Error when communicating to the Jabber server.'); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Event/Watcher/0000775000000000000000000000000012017102272016721 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Event/Watcher/Runit.pm0000664000000000000000000001252512017102272020365 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Event::Watcher::Runit; # Class: EBox::Event::Watcher::Runit # # This class is a watcher which checks if a service has been down # for MAX_DOWN_PERIODS in PERIODS and send an event notifying it # use base 'EBox::Event::Watcher::Base'; use constant PERIOD => 60; use constant MAX_DOWN_PERIODS => 5; use constant DOMAIN => 'ebox'; use EBox::Event; use EBox::Event::Watcher::Base; use EBox::Exceptions::Internal; use EBox::Gettext; use EBox::Service; use EBox::Global; # Core modules use Error qw(:try); use Fcntl qw(:flock); # Import LOCK * constants # Group: Public methods # Constructor: new # # The constructor for # # Overrides: # # # # Parameters: # # - non parameters # # Returns: # # - the newly created object # sub new { my ($class) = @_; my $self = $class->SUPER::new( period => PERIOD, domain => DOMAIN, ); bless( $self, $class); $self->{downPeriods} = {}; return $self; } # Method: ConfigurationMethod # # Overrides: # # # sub ConfigurationMethod { return 'none'; } # Method: run # # Check if any service is being restarted many times within a # time interval. # # Overrides: # # # # Returns: # # undef - if no services are out of control (Chemical # Brothers!) # # array ref - an event is sent when some service # is out of control or come back to order # sub run { my ($self) = @_; my $modules = $self->_runningAlertServices(); my @events = (); if (@{$modules->{notRunning}->{names}}) { $msg = __x("The following modules are not running but they are enabled: {modules}\n", modules => join(', ', @{$modules->{notRunning}->{printableNames}}) ); my %modules = map { $_ => 1 } @{$modules->{notRunning}->{names}}; push(@events, new EBox::Event( message => $msg, level => 'error', source => 'networkservice', additional => \%modules, )); } if (@{$modules->{runningAgain}->{names}}) { $msg = __x("The following modules are running again: {modules}\n", modules => join(', ', @{$modules->{runningAgain}->{printableNames}}) ); my %modules = map { $_ => 1 } @{$modules->{runningAgain}->{names}}; push(@events, new EBox::Event( message => $msg, level => 'info', source => 'networkservice', additional => \%modules)); } return \@events; } # Group: Protected methods # Method: _name # # Overrides: # # # # Returns: # # String - the event watcher name # sub _name { return __('Service'); } # Method: _description # # Overrides: # # # # Returns: # # String - the event watcher detailed description # sub _description { return __('Check if any Zentyal service is not running when it is enabled'); } # Method: _runningAlertServices # # Generate events for services which are running or not when they should sub _runningAlertServices { my ($self) = @_; my $gl = EBox::Global->getInstance(1); my $class = 'EBox::Module::Service'; my %ret = ( notRunning => { printableNames => [], names => [] }, runningAgain => { printableNames => [], names => [] } ); for my $mod (@{$gl->modInstancesOfType($class)}) { next unless ($mod->can('isRunning')); my $enabled = $mod->isEnabled(); my $running = $mod->isRunning(); my $name = $mod->name(); if (not $running and $enabled) { unless (exists $self->{downPeriods}->{$name}) { $self->{downPeriods}->{$name} = 0; } if ($self->{downPeriods}->{$name}++ >= MAX_DOWN_PERIODS) { push (@{$ret{notRunning}->{printableNames}}, $mod->printableName()); push (@{$ret{notRunning}->{names}}, $name); } EBox::debug("Module $name is not running (" . $self->{downPeriods}->{$name} . ')'); } elsif (exists $self->{downPeriods}->{$name}) { EBox::debug("Module $name is running again"); delete $self->{downPeriods}->{$name}; push (@{$ret{runningAgain}->{printableNames}}, $mod->printableName()); push (@{$ret{runningAgain}->{names}}, $name); } } return \%ret; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Event/Watcher/Log.pm0000664000000000000000000002165512017102272020011 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Event::Watcher::Log; # Class: EBox::Event::Watcher::Log # # This class is a watcher which search for new logs in Logs module # within an interval. # use base 'EBox::Event::Watcher::Base'; use strict; use warnings; use EBox::Event; use EBox::Event::Watcher::Base; use EBox::Exceptions::Internal; use EBox::Gettext; use EBox::Global; use EBox::Model::Manager; # Dependencies use POSIX; # Core modules use Error qw(:try); use Time::Local; # Constants use constant LAST_QUERIED_KEY => 'LogWatcher/LastQueriedTime'; use constant PAGESIZE => 100; # Group: Public methods # Constructor: new # # The constructor for # # Overrides: # # # # Parameters: # # - non parameters # # Returns: # # - the newly created object # sub new { my ($class) = @_; my $self = $class->SUPER::new(period => 10); bless( $self, $class); # Get the last interval queried from Events namespace $self->{lastQueried} = 0; $self->{logs} = EBox::Global->modInstance('logs'); return $self; } # Method: run # # Check if any logger has logged anything to create events # # Overrides: # # # # Returns: # # undef - if no new event has been created # # array ref - the events, one created per new log # line on every logger # sub run { my ($self) = @_; my $logs = $self->{logs}; my @loggers = keys %{$logs->getAllTables()}; my $events = []; my $lastQueried = $self->_lastQueriedTime(); my $now = time(); my $from = $self->_toYMDHMS($lastQueried); my $to = $self->_toYMDHMS($now); foreach my $logger (@loggers) { next unless $self->_isLoggerEnabled($logger); my $pagesize = PAGESIZE; my $timeCol = 'timestamp'; foreach my $filter (@{$self->_filters($logger)}) { # Copy filter in filterCpy to workaround nasty # issues with the garbage collector. my $filterCpy; if (%{$filter}) { $filterCpy = $filter; } else { $filterCpy = undef; } my $finished = 0; my $page = 0; do { my $result = $logs->search($from, $to, $logger, $pagesize, $page, $timeCol, $filterCpy); my $nPages = POSIX::ceil ( $result->{totalret} / $pagesize ); $finished = ($page + 1) >= $nPages; $page++; my $newEvents = $self->_createEvents($logger, $result->{arrayret}); push (@{$events}, @{$newEvents}); } while (not $finished); } } $self->_setLastQueriedTime($now); if ( @{$events} > 0 ) { return $events; } else { return; } } # Group: Static class methods # Method: ConfigurationMethod # # Overrides: # # # sub ConfigurationMethod { return 'model'; } # Method: ConfigureModel # # Overrides: # # # sub ConfigureModel { return 'LogWatcherConfiguration'; } # Method: Able # # Overrides: # # # sub Able { my $logs = EBox::Global->modInstance('logs'); return defined($logs->getAllTables()); } # Group: Protected methods # Method: _name # # Overrides: # # # # Returns: # # String - the event watcher name # sub _name { return __('Log observer'); } # Method: _description # # Overrides: # # # # Returns: # # String - the event watcher detailed description # sub _description { my ($self) = @_; my $logs = $self->{logs}; my $loggerTables = $logs->getAllTables(); my $loggersMsg = ''; if ( defined ( $loggerTables ) ) { my @names; while (my ($domain, $tableInfo) = each %{ $loggerTables }) { if ($domain eq 'events') { next; } push @names, $tableInfo->{name}; } $loggersMsg = join(', ', @names); # XX Delete # my @loggers = keys %{$loggerTables}; # $loggersMsg = join( ', ', # map { $logs->getTableInfo($_)->{name} } @loggers); } return __x('Notify when a logger ({loggers}) has logged something', loggers => $loggersMsg); } # Group: Private methods # Create the objects to send to the dispatcher sub _createEvents { my ($self, $loggerName, $rows) = @_; my $helperMod = $self->{logs}->getTableInfo($loggerName)->{helper}; my @retEvents = (); foreach my $row (@{$rows}) { # Remove timestamp field from row to add the source category my $msg = $helperMod->humanEventMessage($row); delete($row->{timestamp}); push(@retEvents, new EBox::Event( message => $msg, level => 'info', source => "log-observer-$loggerName", additional => $row, ) ); } return \@retEvents; } # Get the last queried time sub _lastQueriedTime { my ($self) = @_; if ( $self->{lastQueried} == 0 ) { # Get the last queried from State my $eventsMod = EBox::Global->modInstance('events'); my $lastQueried = $eventsMod->st_get_int(LAST_QUERIED_KEY); if ( not defined($lastQueried) or $lastQueried == 0) { # First query time in ages (1 January 2000) $self->{lastQueried} = timelocal(0, 0, 0, 1, 1, 2000); } else { $self->{lastQueried} = $lastQueried; } } return $self->{lastQueried}; } # Get the last queried time sub _setLastQueriedTime { my ($self, $lastQueried) = @_; $self->{lastQueried} = $lastQueried; my $eventsMod = EBox::Global->modInstance('events'); $eventsMod->st_set_int(LAST_QUERIED_KEY, $lastQueried); } # Transform from Epoch seconds to YMDHMS date string sub _toYMDHMS { my ($self, $epochSecs) = @_; my ($secs, $mins, $hours, $days, $month, $year) = localtime($epochSecs); return sprintf( "%04d-%02d-%02d %02d:%02d:%02d", $year+1900, $month+1, $days, $hours, $mins, $secs); } # Get from configuration model if an event notification from a logger # is enabled or not sub _isLoggerEnabled { my ($self, $logger) = @_; unless (exists $self->{logger}->{$logger}) { my $confModel = $self->_logSubModel(); my $row = $confModel->find(domain => $logger); if ($row) { $self->{logger}->{$logger} = $row->valueByName('enabled'); } else { $self->{logger}->{$logger} = 0; } } return $self->{logger}->{$logger}; } # Returns the filters used to do the search in and-ed mode sub _filters { my ($self, $logger) = @_; unless ($self->{filters}->{$logger}) { my $logConfModel = $self->_logSubModel(); my $loggerConfRow = $logConfModel->findValue(domain => $logger); my $filterModel = $loggerConfRow->subModel('filters'); my @filterSearchs = (); foreach my $id (@{$filterModel->ids()}) { my $filterRow = $filterModel->row($id); my $filterSearch = {}; foreach my $filterField (@{$filterRow->elements()}) { if ( $filterField->value() ) { # Do not store a thing if the field is the event with # 'any' value to work with API unless ( $filterField->fieldName() eq 'event' and $filterField->value() eq 'any' ) { $filterSearch->{$filterField->fieldName()} = $filterField->value(); } } } push ( @filterSearchs, $filterSearch ); } $self->{filters}->{$logger} = \@filterSearchs; } return $self->{filters}->{$logger}; } sub _logSubModel { my ($self) = @_; return $self->configurationSubModel(__PACKAGE__); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Event/Watcher/Base.pm0000664000000000000000000001131412017102272020131 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Event::Watcher::Base; # Class: EBox::Event::Watcher::Base # # This class is the base for developing all event watchers by any # module. It should inherit in order to have support for reporting # events within eBox framework. Every subclass should just watch one # event. # use strict; use warnings; use base 'EBox::Event::Component'; use EBox::Exceptions::NotImplemented; use EBox::Exceptions::MissingArgument; use EBox::Event; use EBox::Gettext; use EBox::Model::Manager; # Constructor: new # # The constructor for the object # # Parameters: # # period - Integer the period in number of calls between # calls # # - Named parameters # # Returns: # # - the newly created object # # Exceptions: # # - thrown if any argument # is missing # sub new { my ($class, %args) = @_; defined ( $args{period} ) or throw EBox::Exceptions::MissingArgument('period'); my $self = $class->SUPER::new(%args); bless ($self, $class); $self->{period} = $args{period}; return $self; } # Method: period # # Accessor to the period among calls # # Returns: # # Integer - the number of minutes between calls # sub period { my ($self) = @_; return $self->{period}; } # Method: description # # Accessor to the description of what this event watcher # does. If is not # overridden, an empty string is returned. # # Returns: # # String - the detailed description # sub description { my ($self) = @_; my $description = $self->_description(); return $description; } # Method: run # # Check an event to report it. This method will take into # account anything in the system to get known if the watched # event has happened. It must be overriden. *(Abstract)* # # Returns: # # undef - if no new event has been reported # # array ref - containing which as many events as they have # happened event has happened # sub run { throw EBox::Exceptions::NotImplemented(); } # Group: Class static methods # Method: Able # # Check if the event watcher is able to watch and monitor an # event. If it is unable, then the event will never happen # anyway. # # Example: # # Monitoring RAID events in a flat disk subsystem has no sense # # Returns: # # boolean - indicating if the event watcher is able to monitor a # set of events # # Default value: # # true # sub Able { return 1; } # Method: HiddenIfNotAble # # Check if the event watcher must be hidden if the watcher is not # able to watch and monitor an event. # # Example: # # Hidden events only available in a level of subscription # # Returns: # # boolean - indicating if the event watcher must be hidden or not # when it is unable # # Default value: # # false # sub HiddenIfNotAble { return 0; } # Method: configurationSubModel # # Fetch the configuration submodel for a given # event watcher. # # Given a class name it will look up the row # of the model which contains this class, and # it will return its configuration model # # Parameters: # # package - String containing a class to look up # # Returns: # # An instance of # # sub configurationSubModel { my ($self, $package) = @_; defined ( $package ) or throw EBox::Exceptions::MissingArgument('package'); my $events = EBox::Global->modInstance('events'); my $watchers = $events->model('ConfigureWatchers'); my $row = $watchers->findValue('watcher' => $package); return $row->subModel('configuration_model'); } # Group: Protected method # Method: _description # # The i18ned method to describe the event watcher. To be # overridden by subclasses. # # Returns: # # String - the description. Default value: an empty string. # sub _description { # Default empty implementation return ''; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Event/Watcher/RAID.pm0000664000000000000000000004310212017102272017776 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Event::Watcher::RAID; # Class: EBox::Event::Watcher::RAID # # This class is a watcher which intended to notify about events that # may happen at RAID installation. # # The events are the following ones: # # Partition events: # # - Changing its state: active (sync, resync), faulty, spare, removed # # - Hot additions and removals # # RAID array events: # # - Changing its state: active, degraded, recovering, clean, failed, resync # # - Number of devices # # - Operation: active with percentage and estimated time to # finish. Finish and starting ones. # # - Management: addition, removal, failure # # At first time, the RAID event watcher will supply all its initial # information as new one. use base 'EBox::Event::Watcher::Base'; use EBox::Event; use EBox::Gettext; use EBox::Global; use EBox::Report::RAID; # Core modules # Group: Public methods # Constructor: new # # The constructor for # # Overrides: # # # # Parameters: # # - non parameters # # Returns: # # - the newly created object # sub new { my ($class) = @_; my $self = $class->SUPER::new(period => 50); bless( $self, $class); $self->{events} = EBox::Global->modInstance('events'); return $self; } # Method: run # # Check if any event has happened to the RAID installation # # Overrides: # # # # Returns: # # undef - if no services are out of control (Chemical # Brothers!) # # array ref - an event is sent when some service # is out of control # sub run { my ($self) = @_; my @events; my $raidInfo = EBox::Report::RAID->info(); foreach my $raidArray (keys %{$raidInfo}) { # Skip unused devices next if ( $raidArray eq 'unusedDevices' ); my $eventsRaidArray = $self->_checkRaidArray($raidArray, $raidInfo->{$raidArray}); if ( defined ($eventsRaidArray) ) { push (@events, @{$eventsRaidArray} ); } } # Check removed ones my $removedArrayEvents = $self->_checkRemoveArray($raidInfo); if ( @{$removedArrayEvents} > 0 ) { push (@events, @{$removedArrayEvents}); } # Store last info in GConf state if changed if ( @events > 0 ) { $self->_storeNewRAIDState($raidInfo); } if ( @events > 0 ) { return \@events; } else { return undef; } } # Group: Class static methods # Method: ConfigurationMethod # # Overrides: # # # sub ConfigurationMethod { return 'none'; } # Method: Able # # Overrides: # # # sub Able { return EBox::Report::RAID->enabled(); } # Group: Protected methods # Method: _name # # Overrides: # # # # Returns: # # String - the event watcher name # sub _name { return 'RAID'; } # Method: _description # # Overrides: # # # # Returns: # # String - the event watcher detailed description # sub _description { return __('Check if any event has happened in RAID subsystem'); } # Group: Private methods # Check any event that may occur in the RAID array sub _checkRaidArray # (arrayRaidName, raidInfo) { my ($self, $arrayRaidName, $raidArrayInfo) = @_; my $storedInfo = $self->_storedArrayRaidInfo($arrayRaidName); unless ( defined ($storedInfo) ) { return $self->_createEventArrayRaid($arrayRaidName, $raidArrayInfo); } my @updatedEvents = (); # Check the state # Check the devices' number # Check the operations # Check each RAID device my @checkSubs = ('_checkArrayStatus', '_checkArrayCompNum', '_checkArrayOp', '_checkComponents'); foreach my $checkSub (@checkSubs) { my $newEvents = $self->$checkSub($arrayRaidName, $raidArrayInfo, $storedInfo); if ( defined($newEvents) ) { push(@updatedEvents, @{$newEvents}); } } return \@updatedEvents; } # Get stored info from the raid array sub _storedArrayRaidInfo { my ($self, $arrayRaidName) = @_; my $state = $self->{events}->get_state(); return $state->{raid_arrays}->{$arrayRaidName}; } # Create the event from the raid info sub _createEventArrayRaid # (arrayName, raidInfo) { my ($self, $arrayName, $raidArrayInfo) = @_; my $msg = __x('New array RAID device {devName} information:', devName => $arrayName) . ' '; $msg .= __x('State: {state}', state => $raidArrayInfo->{state}) . ' '; $msg .= __x('Type: {type}', type => $raidArrayInfo->{type}) . ' '; $msg .= __x('Active devices needed: {nb}', nb => $raidArrayInfo->{activeDevicesNeeded}) . ' '; $msg .= __x('Active devices: {nb}', nb => $raidArrayInfo->{activeDevices}) . ' '; unless ( $raidArrayInfo eq 'none' ) { $msg .= __x('Operation in progress: {operation}', operation => $raidArrayInfo->{operation}) . ' '; $msg .= __x('Completed operation percentage: {per}', per => $raidArrayInfo->{operationPercentage}) . ' '; $msg .= __x('Operation estimated finish time: {time}', time => $raidArrayInfo->{operationEstimatedTime}) . ' '; } while (my ($raidCompNum, $raidCompInfo) = each %{$raidArrayInfo->{raidDevices}}) { $msg .= __x('Raid component {nb}: device {device} state {state}', nb => $raidCompNum, device => $raidCompInfo->{device}, state => $raidCompInfo->{state}) . ' '; } my $arrayRaidEvent = new EBox::Event( level => 'info', source => $self->name(), message => $msg ); return [ $arrayRaidEvent ]; } # Store last RAID info in GConf state sub _storeNewRAIDState { my ($self, $raidInfo) = @_; my $state = $self->{events}->get_state(); $state->{raid_arrays} = $raidInfo; $self->{events}->set_state($state); } # Check if any of the stored RAID array has dissappeared sub _checkRemoveArray { my ($self, $raidInfo) = @_; my $evMod = $self->{events}; my @removeEvents = (); my @currentArrays = grep { $_ ne 'unusedDevices' } keys %{$raidInfo}; my %currentArrays = map { $_ => 1 } @currentArrays; my $state = $self->{events}->get_state(); foreach my $devName (keys %{$state->{raid_array}}) { next if (exists ($currentArrays{$devName})); my $evtMsg = __x('RAID device {name} has dissappeared: A RAID array ' . 'which previously was configured appears to no ' . 'longer be configured', name => $devName); push( @removeEvents, new EBox::Event(level => 'info', source => $self->name(), message => $evtMsg)); } return \@removeEvents; } # Group: Checkers update in RAID subsystem # Check if the RAID device status has changed sub _checkArrayStatus # (arrayName, arrayInfo, storedInfo) { my ($self, $arrayName, $arrayInfo, $storedInfo) = @_; if ($arrayInfo->{operation} eq 'check') { # ignore changes dues to check operations return undef; } if ( $arrayInfo->{state} ne $storedInfo->{state} ) { my $evtMsg = __x('RAID device {name} has changed its state ' . 'from {oldState} to {newState}', name => $arrayName, oldState => $self->_i18nState($storedInfo->{state}), newState => $self->_i18nState($arrayInfo->{state})); return [ new EBox::Event(level => 'info', source => $self->name(), message => $evtMsg) ]; } return undef; } # Check the array component number in the RAID array device sub _checkArrayCompNum # (arrayName, arrayInfo, storedInfo) { my ($self, $arrayName, $arrayInfo, $storedInfo) = @_; if ( $storedInfo->{deviceNumber} != $arrayInfo->{activeDevices} ) { my $evtMsg = __x('RAID device {name} has changed its number ' . 'of active components from {oldNum} to {newNum}', name => $arrayName, oldNum => $storedInfo->{deviceNumber}, newNum => $arrayInfo->{activeDevices}); return [ new EBox::Event(level => 'info', source => $self->name(), message => $evtMsg) ]; } return undef; } # Check the current operation in the RAID array device sub _checkArrayOp # (arrayName, arrayInfo, storedInfo) { my ($self, $arrayName, $arrayInfo, $storedInfo) = @_; my ($evtMsg, $showPer) = ('', 0); if (($arrayInfo->{operation} eq 'check') or ($storedInfo->{operation} eq 'check')) { # ignore check operations return undef; } if ( $storedInfo->{operation} ne $arrayInfo->{operation} ) { if ( $storedInfo->{operation} eq 'none' ) { $evtMsg = __x('RAID device {name} has started operation {opName}.', name => $arrayName, opName => $self->_i18nOp($arrayInfo->{operation}), ); $showPer = 1; } elsif ( $arrayInfo->{operation} eq 'none' ) { $evtMsg = __x('RAID device {name} has finished operation {opName} ' . 'or it was aborted.', name => $arrayName, opName => $self->_i18nOp($storedInfo->{operation})); } else { # None is 'none' operation $evtMsg = __x('RAID device {name} has finished operation {oldOpName} ' . 'and started {newOpName}.', name => $arrayName, oldOpName => $self->_i18nOp($storedInfo->{operation}), newOpName => $self->_i18nOp($arrayInfo->{operation}) ); $showPer = 1; } } elsif ( $arrayInfo->{operation} ne 'none' ) { # An operation in RAID array is being performed, show we # ignroe this because is very berbose to show various messages for a # verbose operation # $evtMsg = __x('RAID device {name} is performing operation {opName}', # name => $arrayName, # opName => $self->_i18nOp($arrayInfo->{operation}) # ) ; # $showPer = 1; return undef; } if ( $evtMsg ) { if ( $showPer ) { $evtMsg .= ' '; my $percentage = $arrayInfo->{operationPercentage} . '%'; $evtMsg .= __x('Status: {percentage} completed.', percentage => $percentage); $evtMsg .= ' '; $evtMsg .= __x('Estimated finish time: {time}.', time => $arrayInfo->{operationEstimatedTime}); } return [ new EBox::Event(level => 'info', source => $self->name(), message => $evtMsg) ]; } else { return undef; } } # Check each array component in the RAID array device sub _checkComponents # (arrayName, arrayInfo, storedInfo) { my ($self, $arrayName, $arrayInfo, $storedInfo) = @_; my %currentComps = map { $_->{device} => $_->{state} } values %{$arrayInfo->{raidDevices}}; my %storedComps = map { $_->{device} => $_->{state} } @{$storedInfo->{components}}; my @compEvents = (); my $evtMsg; foreach my $currentComp (keys %currentComps) { if ( exists $storedComps{$currentComp} ) { # Check updates my $oldStatus = $storedComps{$currentComp}; my $newStatus = $currentComps{$currentComp}; if ( $newStatus ne $oldStatus ) { if ( $newStatus eq 'failure' and $oldStatus eq 'up') { $evtMsg = __x('Active component {compName} from RAID array {arrayName} ' . 'has been marked as faulty', compName => $currentComp, arrayName => $arrayName); push ( @compEvents, new EBox::Event(level => 'error', source => $self->name(), message => $evtMsg)); } elsif ( $newStatus eq 'failure' and $oldStatus eq 'spare' ) { $evtMsg = __x('Spare component {compName} from RAID array {arrayName} ' . 'which was being rebuilt to replace a faulty device ' . 'has failed', compName => $currentComp, arrayName => $arrayName); push ( @compEvents, new EBox::Event(level => 'error', source => $self->name(), message => $evtMsg)); } elsif ( $newStatus eq 'up' and $oldStatus eq 'spare' ) { $evtMsg = __x('Spare component {compName} from RAID array {arrayName} ' . 'which was being rebuilt to replace a faulty device ' . 'has been successfully rebuilt and has been made ' . 'active', compName => $currentComp, arrayName => $arrayName); push ( @compEvents, new EBox::Event(level => 'info', source => $self->name(), message => $evtMsg)); } } } else { # An addition $evtMsg = __x('A new component {compName} has been hot added ' . 'to RAID device {arrayName} with status {status}', compName => $currentComp, arrayName => $arrayName, status => $self->_i18nCompStatus($currentComps{$currentComp})); push ( @compEvents, new EBox::Event(level => 'info', source => $self->name(), message => $evtMsg)); } } # Check removals foreach my $storedComp (keys %storedComps) { next if (exists $currentComps{$storedComp}); $evtMsg = __x('A component {compName} has been hot removed from ' . 'RAID array {arrayName} when its status was {status}', compName => $storedComp, arrayName => $arrayName, status => $storedComps{$storedComp}); push ( @compEvents, new EBox::Event(level => 'warn', source => $self->name(), message => $evtMsg)); } return \@compEvents; } # Group: Helper methods # Get the array i18ned state message sub _i18nState { my ($self, $state) = @_; my @singleStates = split( ', ', $state); my @i18nedStates = (); foreach my $singleState (@singleStates) { if ( $singleState eq 'active' ) { push(@i18nedStates, __('active')); } elsif ( $singleState eq 'degraded' ) { push(@i18nedStates, __('degraded')); } elsif ( $singleState eq 'recovering' ) { push(@i18nedStates, __('recovering')); } elsif ( $singleState eq 'resyncing' ) { push(@i18nedStates, __('resyncing')); } elsif ( $singleState eq 'rebuilding' ) { push(@i18nedStates, __('rebuilding')); } elsif ( $singleState eq 'reshaping' ) { push(@i18nedStates, __('reshaping')); } elsif ( $singleState eq 'failed' ) { push(@i18nedStates, __('failed')); } } return join( ', ', @i18nedStates); } sub _i18nOp { my ($self, $op) = @_; if ( $op eq 'resync' ) { return __('resync'); } elsif ( $op eq 'rebuild' ) { return __('rebuild'); } elsif ( $op eq 'reshape' ) { return __('reshape'); } elsif ( $op eq 'recovery' ) { return __('recovery'); } elsif ($op eq 'check') { return __('check'); } return $op; } # Get the component i18ned status message sub _i18nCompStatus { my ($self, $status) = @_; if ( $status eq 'up' ) { return __('active'); } elsif ( $status eq 'failure' ) { return __('faulty'); } elsif ( $status eq 'spare' ) { return __('spare'); } } 1; zentyal-core-2.3.21+quantal1/src/EBox/Event/Watcher/State.pm0000664000000000000000000000644012017102272020343 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Event::Watcher::State; # Class: EBox::Watcher::State; # # This class is a watcher which checks current state from eBox # use base 'EBox::Event::Watcher::Base'; # Constants: # use IO::Socket; use EBox::Event; use EBox::Service; use EBox::Global; use EBox::Gettext; # Group: Public methods # Constructor: new # # The constructor for # # Overrides: # # # # Parameters: # # - non parameters # # Returns: # # - the newly created object # sub new { my ($class) = @_; my $self = $class->SUPER::new(period => 10 * 60); bless( $self, $class); return $self; } # Method: ConfigurationMethod # # Overrides: # # # sub ConfigurationMethod { return 'none'; } # Method: run # # Check that Apache-perl from Web UI is up and running. # Optionally, if the soap module is already installed, check if # the corresponding Apache-soap is up and running. # # Overrides: # # # # Returns: # # array ref - an info event is sent if eBox is up and # running and a fatal event if eBox is down # sub run { my ($self) = @_; # Check if apache is up and running my $up = undef; my $gl = EBox::Global->getInstance(1); my $sock = IO::Socket::INET->new( PeerAddr => "127.0.0.1", PeerPort => $gl->modInstance('apache')->port(), Proto => "tcp", Timeout => 5); if ($sock) { close($sock); $up = 1; } my $event; if ( $up ) { $event = new EBox::Event( message => __('Zentyal UI is up and running'), level => 'info', source => 'state', additional => { 'up' => 1 }, ); } else { $event = new EBox::Event( message => __('Zentyal UI is critically down'), level => 'fatal', source => 'state', additional => { 'down' => 1 }, ); } return [ $event ]; } # Group: Protected methods # Method: _name # # Overrides: # # # # Returns: # # String - the event watcher name # sub _name { return __('State'); } # Method: _description # # Overrides: # # # # Returns: # # String - the event watcher detailed description # sub _description { return __('Check if Zentyal is currently up or down'); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Event/Watcher/DiskFreeSpace.pm0000664000000000000000000001617712017102272021743 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Event::Watcher::DiskFreeSpace; # Class: EBox::Event::Watcher::DiskFreeSpace # # This class is a watcher which checks if a partition has no free # space left. The measure is done in percentages which is # configurable by the user. # use base 'EBox::Event::Watcher::Base'; use EBox::Event; use EBox::Event::Watcher::Base; use EBox::Exceptions::Internal; use EBox::Gettext; use EBox::Global; use EBox::FileSystem; use Filesys::Df; use Error qw(:try); use Perl6::Junction qw(any); use constant SPACE_THRESHOLD => 1024; # a file system is considered full with it has # less than this space (in 1K blocks) free # Group: Public methods # Constructor: new # # The constructor for # # Overrides: # # # # Parameters: # # - non parameters # # Returns: # # - the newly created object # sub new { my ($class) = @_; my $self = $class->SUPER::new(period => 120); bless( $self, $class); return $self; } # Method: ConfigurationMethod # # Overrides: # # # sub ConfigurationMethod { return 'model'; } # Method: ConfigureModel # # Overrides: # # # sub ConfigureModel { return 'DiskFreeWatcherConfiguration'; } # Method: run # # Check if any partition is full # # Overrides: # # # # Returns: # # undef - if all partitions have sufficent space left # # array ref - an event is sent when some # partitions does not have space left # sub run { my ($self) = @_; my @events; my $eventMod = EBox::Global->modInstance('events'); my %fileSys = %{ $self->_filesysToMonitor() }; while (my ($fs, $properties) = each %fileSys) { my $key = _eventKey($fs); my $eventHappened = $eventMod->st_get_bool($key); my $threshold = $self->_spaceThreshold(); my $df = df($properties->{mountPoint}); if ($self->_isFSFull($df) and not $eventHappened) { $eventMod->st_set_bool($key, 1); my $msg = __x('The partition {mp} has low free space available.', mp => $properties->{mountPoint}) . "\n\n" . __x('The file system {fs}, mounted on {mp},' . ' has {left} space left which is below ' . 'than the configured threshold {thres}.' , fs => $fs, mp => $properties->{mountPoint}, left => (100 - $df->{per}) . '%', thres => $threshold . '%', ); push(@events, new EBox::Event( message => $msg, level => 'error', source => 'diskusage', additional => { 'file_system' => $fs, 'mount_point' => $properties->{mountPoint}, 'user_used_percentage' => $df->{per}, 'threshold' => $threshold })); } elsif (not $self->_isFSFull($df) and $eventHappened) { # disable key bz the problem has solved $eventMod->st_set_bool($key, 0); my $msg = __x('The partition {mp} does have enough space available.', mp => $properties->{mountPoint}) . "\n\n" . __x('The file system {fs}, mounted on {mp}, has again ' . 'enough space available {left} which is greater ' . 'than the configured threshold {thres}.', fs => $fs, mp => $properties->{mountPoint}, left => (100 - $df->{per}) . '%', thres => $self->_spaceThreshold() . '%' ); push(@events, new EBox::Event(message => $msg, level => 'info', source => 'diskusage', additional => { 'file_system' => $fs, 'mount_point' => $properties->{mountPoint}, 'user_used_percentage' => $df->{per}, 'threshold' => $threshold }, )); } } return \@events if @events; return undef; } # Group: Protected methods # Method: _name # # Overrides: # # # # Returns: # # String - the event watcher name # sub _name { return __('Free storage space'); } # Method: _description # # Overrides: # # # # Returns: # # String - the event watcher detailed description # sub _description { return __('Check if any disk partition has no storage space left'); } # Group: Private methods sub _eventKey { my ($fs) = @_; # Substitute slashes because of GConf key structure $fs =~ s{/}{S}g; return "event_fired/partition_full/$fs"; } sub _filesysToMonitor { my %fileSys = %{ EBox::FileSystem::fileSystems() }; foreach my $fs (keys %fileSys) { # remove not-device filesystems if (not $fs =~ m{^/dev/}) { delete $fileSys{$fs}; next; } # remove removable media filesystems my $mpoint = $fileSys{$fs}->{mountPoint}; if ($mpoint =~ m{^/media/}) { delete $fileSys{$fs}; next; } # we don't care about space shortage in read only file systems my @options = split ',', $fileSys{$fs}->{options}; if ('ro' eq any @options) { delete $fileSys{$fs}; next; } } return \%fileSys; } # Method to get if a filesystem is full of space or not sub _isFSFull { my ($self, $df) = @_; return ( 100 - $df->{per} < $self->_spaceThreshold() ); } # Method to get the configurable minimum percentage before the user is # notified because of the free space lack sub _spaceThreshold { my ($self) = @_; return $self->configurationSubModel(__PACKAGE__)->spaceThreshold(); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Event/Watcher/Updates.pm0000664000000000000000000002043712017102272020672 0ustar # Copyright (C) 2011-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Event::Watcher::Updates; # Class: EBox::Event::Watcher::Updates # # This class is a watcher which checks if there is any software # updates available in our system and show a message informing how # this could break the system. # # This check is done weekly. # use strict; use warnings; use 5.010; use base 'EBox::Event::Watcher::Base'; use EBox::Event; use EBox::Gettext; use EBox::Global; use EBox::Util::Software; use constant SB_URL => 'https://store.zentyal.com/small-business-edition.html/?utm_source=zentyal&utm_medium=updates&utm_campaign=smallbusiness_edition'; use constant ENT_URL => 'https://store.zentyal.com/enterprise-edition.html/?utm_source=zentyal&utm_medium=updates&utm_campaign=enterprise_edition'; # Group: Public methods # Constructor: new # # The constructor for # # Overrides: # # # # Parameters: # # - non parameters # # Returns: # # - the newly created object # sub new { my ($class) = @_; my $self = $class->SUPER::new(period => 50 * 60); bless( $self, $class); return $self; } # Method: ConfigurationMethod # # Overrides: # # # sub ConfigurationMethod { return 'none'; } # Method: Able # # Overrides to perform the check only for basic subscriptions # # Overrides: # # # sub Able { my $gl = EBox::Global->getInstance(1); my $retVal = 0; if ( $gl->modExists('remoteservices') ) { my $rs = $gl->modInstance('remoteservices'); my $subsLevel = $rs->subscriptionLevel(); $retVal = ($subsLevel == 0); # Only for basic if ( $rs->eBoxSubscribed() and ($subsLevel == -1) ) { # We don't know yet the subscription level if ( $gl->modExists('software') ) { my $software = $gl->modInstance('software'); $retVal = (not $software->QAUpdates()); } } } return $retVal; } # Method: HiddenIfNotAble # # Overrides to hide the event when it is not able to watch the # event # # Overrides: # # # sub HiddenIfNotAble { return 1; } # Method: DisabledByDefault # # Overrides to enable the event by default # # Overrides: # # # sub DisabledByDefault { return 0; } # Method: run # # Check if there is any updates # # Overrides: # # # # Returns: # # undef - if all partitions have sufficent space left # # array ref - an event is sent when some # partitions does not have space left # sub run { my ($self) = @_; my $latestUpdate = EBox::Util::Software::latestUpdate(); return undef unless ( $latestUpdate > $self->_latestStatus() ); $self->_setStatus($latestUpdate); # [updates, sec_updates] my ($nUpdates, $nSecUpdates) = @{EBox::Util::Software::upgradablePkgsNum()}; my @packages = (); my @secUpdates = (); if ( $nUpdates > 0 or $nSecUpdates > 0) { # Get the package names @packages = @{EBox::Util::Software::upgradablePkgs()}; # Check for a security update or not @secUpdates = grep { EBox::Util::Software::isSecUpdate($_) } @packages; if ( not @secUpdates ) { # No new sec updates, update the list $self->_setStoredSecurityUpdates([]); return undef; } my $currentStored = $self->_storedSecurityUpdates(); # Return if they are the same ones. return undef if ( @secUpdates ~~ @{$currentStored} ); } if ( @secUpdates > 0 ) { my $msg = __x('There are {nSecUpdates} security updates. The affected packages are: {packages}.', nSecUpdates => $nSecUpdates, packages => join(', ', @secUpdates)); $msg .= "\n\n"; my $nSysUpdates = $nUpdates - $nSecUpdates; if ($nSysUpdates > 0 ) { # Perform the diff among packages and secUpdates my %count; my @diff; foreach my $pkg (@packages, @secUpdates) { $count{$pkg}++; }; foreach my $pkg (keys %count) { push(@diff, $pkg) if ( $count{$pkg} == 1 ); } $msg .= __x('Additionally, there are {nUpdates} more. The upgradable packages are: {packages}.', nUpdates => $nSysUpdates, packages => join(', ', @diff)); } # Commercial msg $msg .= "\n\n"; $msg .= __sx('Warning: The updates are community based and there is no guarantee that your server will work properly after applying them. In production environments you should use the {ohs}Small Business{ch} or {ohe}Enterprise Edition{ch} that include quality assured software updates.', ohs => '', ohe => '', ch => ''); return [ new EBox::Event(message => $msg, source => 'security-software-update', level => 'warn', additional => { 'n_updates' => $nUpdates, 'n_sec_updates' => $nSecUpdates, 'updates' => \@packages, 'sec_updates' => \@secUpdates, }), ]; } return [ new EBox::Event(message => 'Up-to-date', source => 'security-software-update', level => 'info', dispatchTo => [ 'ControlCenter' ], additional => { 'n_updates' => 0 }), ]; } # Group: Protected methods # Method: _name # # Overrides: # # # # Returns: # # String - the event watcher name # sub _name { return __('Security software updates'); } # Method: _description # # Overrides: # # # # Returns: # # String - the event watcher detailed description # sub _description { return __('Check if there is any security software update.'); } # Group: Private methods sub _list_key { return "security_updates/list"; } # Return the stored security updates sub _storedSecurityUpdates { my ($self) = @_; my $eventsMod = EBox::Global->modInstance('events'); my $key = $self->_list_key(); my $list = []; if ( $eventsMod->st_entry_exists($key) ) { $list = $eventsMod->st_get_list($key); } return $list; } # Set the alerted security updates sub _setStoredSecurityUpdates { my ($self, $secUpdates) = @_; my $eventsMod = EBox::Global->modInstance('events'); my $key = $self->_list_key(); $eventsMod->st_set_list($key, $secUpdates); } sub _latest_status_key { return 'security_updates/latest_status'; } # Return the latest apt-get update timestamp with new packages sub _latestStatus { my ($self) = @_; my $eventsMod = EBox::Global->modInstance('events'); my $key = $self->_latest_status_key(); my $timestamp = 0; if ( $eventsMod->st_entry_exists($key) ) { $timestamp = $eventsMod->st_get_int($key); } return $timestamp; } # Set the latest timestamp apt-get update sub _setStatus { my ($self, $timestamp) = @_; my $eventsMod = EBox::Global->modInstance('events'); my $key = $self->_latest_status_key(); $eventsMod->st_set_int($key, $timestamp); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Event/Component.pm0000664000000000000000000000654012017102272017631 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Event::Component # # This class incorporates those methods which are common for event # architecture components: watchers and dispatchers # package EBox::Event::Component; use strict; use warnings; use EBox::Exceptions::MissingArgument; # Group: Public methods # Constructor: new # # Create a object instance # sub new { my ($class, %args) = @_; my $self = {}; bless($self, $class); return $self; } # Method: name # # Accessor to the event component identifier. If # is not overridden, the # class name is returned. # # Returns: # # String - the unique name # sub name { my ($self) = @_; my $componentEventName = $self->_name(); return $componentEventName; } # Method: ConfigurationMethod # # Class method which determines which kind of method is used in # order to select which kind of configuration will be used. This # method should be overridden. *(Abstract)* # # Returns: # # String - one of the following: # - link - if the configuration is done via URL # - model - if the configuration is done via Model # - none - if no configuration is required # sub ConfigurationMethod { throw EBox::Exceptions::NotImplemented(); } # Method: ConfigureURL # # Get the configuration URL to set the configuration. Static # method. # # Returns: # # String - the URL where to set the configuration # sub ConfigureURL { throw EBox::Exceptions::NotImplemented(); } # Method: ConfigureModel # # Get the configuration model to set the dispatcher # configuration. Static method. # # Returns: # # String - the model which describe the configuration # sub ConfigureModel { throw EBox::Exceptions::NotImplemented(); } # Method: DisabledByDefault # # This method is used to enable or disable the component # when is added to a table for first time. # # Returns true by default. That means the component will be disabled # # Returns: # # Boolean - indicating if it's disabled by default or not # sub DisabledByDefault { return 1; } # Method: EditableByUser # # Check if the given event component is editable # (enable/disable) by user or only by eBox code # # Returns: # # Boolean - indicating if editable by user or not # sub EditableByUser { return 1; } # Group: Protected methods # Method: _name # # The i18ned method to name the event watcher. To be # overridden by subclasses. # # Returns: # # String - the name. Default value: the class name # sub _name { my ($self) = @_; # Default, return the class name return ref ( $self ); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Global.pm0000664000000000000000000000503712017102272016006 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Global; use strict; use warnings; use EBox; use EBox::GlobalImpl; use EBox::Exceptions::Internal; use Devel::StackTrace; # Methods of GlobalImpl that need to know if the instance is read-only or not my %ro_methods = map { $_ => 1 } qw(modEnabled modChange modInstances modInstancesOfType modInstance modDepends modRevDepends edition); # Methods of GlobalImpl that cannot be called on read-only instances my %rw_only_methods = map { $_ => 1 } qw(modIsChanged modChange modRestarted saveAllModules revokeAllModules modifiedModules); sub new { my ($class, $ro) = @_; my $self = {}; $self->{'ro'} = $ro; $self->{'global'} = EBox::GlobalImpl->instance(); bless($self, $class); return $self; } sub isReadOnly { my ($self) = @_; return $self->{ro}; } sub getInstance { my ($self, $ro) = @_; return EBox::Global->new($ro); } # TODO: This method should never be called directly, but # as there are lots of call along the code, we need to wrap it sub instance { return EBox::Global->new(); } sub AUTOLOAD { my ($self, @params) = @_; my $methodName = our $AUTOLOAD; $methodName =~ s/.*:://; return if ($methodName eq 'DESTROY'); unless (ref $self) { $self = EBox::Global->new(); } my $global = $self->{'global'}; unless ($global->can($methodName)) { EBox::debug((new Devel::StackTrace)->as_string()); throw EBox::Exceptions::Internal("Undefined method EBox::GlobalImpl::$methodName"); } my $ro = $self->{'ro'}; if ($ro and $rw_only_methods{$methodName}) { EBox::debug((new Devel::StackTrace)->as_string()); throw EBox::Exceptions::Internal("Cannot call $methodName method on a read-only instance"); } if ($ro_methods{$methodName}) { $global->$methodName($ro, @params); } else { $global->$methodName(@params); } } 1; zentyal-core-2.3.21+quantal1/src/EBox/Desktop/0000775000000000000000000000000012017102272015654 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Desktop/ServiceProvider.pm0000664000000000000000000000232312017102272021325 0ustar # Copyright (C) 2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Desktop::ServiceProvider # # This is an abstract class for desktop services providers # # 'desktopActions' method should return an array reference containing, for # each one of the exposed actions, a name and a reference to this action: # 'action_name' => \&action # package EBox::Desktop::ServiceProvider; use strict; use warnings; # Method: actions # # Return an array ref with the exposed methods # # Returns: # # array ref - Containing pairs: action_name => action_ref # sub desktopActions { return {}; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Model/0000775000000000000000000000000012017102272015303 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Model/Row.pm0000664000000000000000000003331512017102272016415 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Model::Row # # This class represents a row. # # TODO: # Add more convenient methods # # The preferred way to use this class is using its methods. Although for # backwards compatibility here is the internal representation of the row: # # Hash reference containing: # # - 'id' => row id # - 'order' => row order # - 'readOnly' => Boolean indicating if the row is readOnly or not # - 'values' => array ref containing objects # implementing interface # - 'valueHash' => hash ref containing the same objects as # 'values' but indexed by 'fieldName' # # - 'plainValueHash' => hash ref containing the fields and their # value # # - 'printableValueHash' => hash ref containing the fields and # their printable value package EBox::Model::Row; use strict; use warnings; use EBox::Model::Manager; use EBox::Exceptions::Internal; use EBox::Exceptions::MissingArgument; use EBox::Exceptions::InvalidType; use Error qw(:try); # Dependencies # Group: Public methods # Constructor: new # # Create a row # # Parameters: # # (NAMED) # # dir - row's directory # confmodule - confmodule # # # Returns: # # - the newly created object # instance # sub new { my ($class, %opts) = @_; my $self; unless (exists $opts{dir}) { throw EBox::Exceptions::MissingArgument('dir'); } $self->{'dir'} = $opts{dir}; unless (exists $opts{confmodule}) { throw EBox::Exceptions::MissingArgument('confmodule'); } $self->{'confmodule'} = $opts{confmodule}; $self->{'values'} = []; bless ( $self, $class); return $self; } # Group: Public methods # Method: model # # model which this row belongs to # # Returns: # # An instance of a class implementing # sub model { my ($self) = @_; return $self->{'model'}; } # Method: setModel # # set the model # # Parameters: # # (Positional) # # model - An instance of a class implementing # # Returns: # # An instance of a class implementing # sub setModel { my ($self, $model) = @_; unless (defined($model)) { throw EBox::Exceptions::MissingArgument('model'); } unless ($model->isa('EBox::Model::DataTable')) { throw EBox::Exceptions::InvalidType(arg => 'model', type => 'EBox::Model::DataTable' ); } $self->{'model'} = $model; } # Method: id # # row's id # # Returns: # # string - containing id # # sub id { my ($self) = @_; return $self->{id}; } # Method: setId # # Set row id # # Returns: # # string - containing id # # sub setId { my ($self, $id) = @_; unless (defined($id)) { throw EBox::Exceptions::MissingArgument('id'); } $self->{id} = $id; } # Method: readOnly # # row's readOnly # # Returns: # # string - containing readOnly # # sub readOnly { my ($self) = @_; return $self->{readOnly}; } # Method: setReadOnly # # Set row readOnly # # Returns: # # string - containing readOnly # # sub setReadOnly { my ($self, $readOnly) = @_; $self->{readOnly} = $readOnly; } # Method: dir # # configuration directory # # Returns: # # string - containing dir # # sub dir { my ($self) = @_; return $self->{dir}; } # Method: parentRow # # Return parent row if any. This methods makes sense when is used # by submodels # # Returns: # # An instance of class implmenenting # or undef if it has no parent # sub parentRow { my ($self) = @_; my $model = $self->model(); unless ($model) { return undef; } return $model->parentRow(); } # Method: order # # row's order # # Returns: # # string - containing order # # sub order { my ($self) = @_; return $self->{order}; } # Method: setOrder # # Set row order # # Returns: # # string - containing order # # sub setOrder { my ($self, $order) = @_; unless (defined($order)) { return; } $self->{order} = $order; } # Method: configModule # # Return the GConf module this row is stored in # # Returns: # # A class implementing # # sub configModule { my ($self) = @_; return $self->{confmodule}; } # Method: addElement # # Add an element to the row # # Parameters: # # element - A class implementing interface # # sub addElement { my ($self, $element) = @_; unless (defined($element) and $element->isa('EBox::Types::Abstract')) { throw EBox::Exceptions::Internal('element is not a valid type'); } my $fieldName = $element->fieldName(); if (not $fieldName) { throw EBox::Exceptions::Internal('element has not field name or has a empty one'); } if ($self->elementExists($fieldName)) { throw EBox::Exceptions::Internal( "Element $fieldName already is in the row" ); } $element->setRow($self); $element->setModel($self->model()); push (@{$self->{'values'}}, $element); $self->{'valueHash'}->{$fieldName} = $element; } # Method: elementExists # # Check if a given element exists # # Parameters: # # element - element's name # # Exceptions: # # boolean - 1 or undef # sub elementExists { my ($self, $element) = @_; unless ($element) { throw EBox::Exceptions::MissingArgument('element'); } return 1 if (exists $self->{valueHash}->{$element}); # this is only for EBox::Types::Union selected subtype for my $value (@{$self->{values}}) { next unless ($value->isa('EBox::Types::Union')); return 1 if ($value->selectedType() eq $element); } return undef; } # Method: elementByName # # Retreive an element from a row # # Parameters: # # element - element's name # # Exceptions: # # undef - if the element exists under a union type but it's not # the selected one # if the element does not exist # # sub elementByName { my ($self, $element) = @_; unless ($element) { throw EBox::Exceptions::MissingArgument('element'); } unless (exists $self->{valueHash}->{$element}) { for my $value (@{$self->{values}}) { next unless ($value->isa('EBox::Types::Union')); if ($value->selectedType() eq $element) { return $value->subtype(); } for my $type (@{$value->subtypes}) { return undef if ($type->fieldName() eq $element); } } throw EBox::Exceptions::DataNotFound( data => 'element', value => $element); } return $self->{valueHash}->{$element}; } # Method: elementByIndex # # Retreive an element from a row by index # # Parameters: # # index - integer # # Exceptions: # # if the element does not exist # sub elementByIndex { my ($self, $index) = @_; unless (defined $index) { throw EBox::Exceptions::MissingArgument('index'); } unless ($index < $self->size()) { throw EBox::Exceptions::DataNotFound(data => 'index', value => $index); } return @{$self->{'values'}}[$index]; } # Method: elements # # Return the elements contained in the row # # Returns: # # array ref of # sub elements { my ($self) = @_; return $self->{'values'}; } # Method: hashElements # # Return the elements contained in the row # in a hash ref # # Returns: # # hash ref of # sub hashElements { my ($self) = @_; return $self->{'valueHash'}; } # Method: valueByName # # Return the value of a given element. # This method will fecth the element and will # return element->value(). # # Element is a subclass of # # Returns: # # Whatever the given type returns # sub valueByName { my ($self,$name) = @_; unless ($name) { throw EBox::Exceptions::MissingArgument('name'); } return $self->elementByName($name)->value(); } # Method: printableValueByName # # Return the printableValue of a given element. # This method will fecth the element and will # return element->printableBalue(). # # Element is a subclass of # # # Returns: # # Whatever the given type returns # sub printableValueByName { my ($self,$name) = @_; unless ($name) { throw EBox::Exceptions::MissingArgument('name'); } return $self->elementByName($name)->printableValue(); } # Method: size # # Return the numbe of elements of this row # # Return: # # size - integer # sub size { my ($self) = @_; unless (exists $self->{'values'}) { return 0; } return scalar(@{$self->{'values'}}); } # Method: store # # This method is used to synchronize the elements of a row with usually disk. # # Exceptions: # # # sub store { my ($self) = @_; my $model = $self->model(); unless (defined($model)) { throw EBox::Exceptions::Internal('Cannot store a row without a model'); } $model->setTypedRow($self->id(), $self->{'valueHash'}, readOnly => $self->readOnly(), force => 1); } # Method: storeElementByName # # This method is used to synchronize a given element of a row with # usually disk. # # Use this method if you just want to store one element # # Parameters: # # element - element's name # # Exceptions: # # # sub storeElementByName { my ($self, $element) = @_; unless ($element) { throw EBox::Exceptions::MissingArgument('element'); } my $model = $self->model(); $model->setTypedRow($self->id(), {$element => $self->elementByName($element)}, readOnly => $self->readOnly(), force => 1); } # Method: subModel # # Return a submodel contained in hasMany type # # Parameters: # # fieldName - string identifying a hasMany type # # Returns: # # An instance of a class implementing # # Exceptions: # # if the element does not exist # # # sub subModel { my ($self, $fieldName) = @_; unless ($fieldName) { throw EBox::Exceptions::MissingArgument('fieldName'); } my $element = $self->elementByName($fieldName); unless ($element->isa('EBox::Types::HasMany')) { throw EBox::Exceptions::Internal("$fieldName is not a HasMany type"); } my $model; try { $model = $element->foreignModelInstance(); } catch EBox::Exceptions::DataNotFound with { EBox::warn("Couldn't fetch foreign model: " . $element->foreignModel()); }; return $model; } # Method: matchFilter # # Check if any of the printable values of the row matches a given filter # # Parameters: # # filter - string containing the filter # # Returns: # # boolean - true if it matches, false otherwise # sub matchFilter { my ($self, $filter) = @_; unless ($filter) { throw EBox::Exceptions::MissingArgument('filter'); } for my $value (@{$self->{values}}) { return 1 if ($value->printableValue() =~ /$filter/i); } return undef; } # Method: filesPaths # # Returns: # the paths of the files managed by the row and its submodels sub filesPaths { my ($self) = @_; my @files; foreach my $element ( @{ $self->elements() } ) { if ($element->can('filesPaths')) { push @files, @{ $element->filesPaths() }; } } return \@files; } # Method: backupFiles # # Make an actual configuration backup of all the files contained in the # row and its submodels. This backup will used to discard cahnges if # needed sub backupFiles { my ($self) = @_; foreach my $element ( @{ $self->elements() } ) { if ($element->can('backupFiles')) { $element->backupFiles(); } } } # Method: restoreFiles # # Restores the actual configuration backup of files, thus discarding last # changes in files sub restoreFiles { my ($self) = @_; foreach my $element (@{$self->elements()}) { if ($element->can('restoreFiles')) { $element->restoreFiles(); } } } # Method: cloneSubModelsFrom # # Auxiliary method for EBox::Model::DataTable to help it to clone the submodels sub cloneSubModelsFrom { my ($self, $otherRow) = @_; my @hasMany = grep { $_->isa('EBox::Types::HasMany') } @{$self->elements()}; @hasMany or return; my $id = $self->id(); my $otherId0 = $otherRow->id(); foreach my $hasMany (@hasMany) { my $fieldName = $hasMany->fieldName(); my $subModel = $self->subModel($fieldName); my $subDir = $subModel->directory(); my $otherSubModel = $otherRow->subModel($fieldName); my $otherSubDir = $otherSubModel->directory(); $subModel->clone($otherSubDir, $subDir); } } 1; zentyal-core-2.3.21+quantal1/src/EBox/Model/DataForm.pm0000664000000000000000000003771312017102272017351 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Model::DataForm # # An specialized model from which # stores just one row. In fact, the viewer and setter is # different. use strict; use warnings; package EBox::Model::DataForm; use base 'EBox::Model::DataTable'; use EBox::Model::Row; use EBox::Exceptions::Internal; use EBox::Gettext; ################### # Dependencies ################### use Perl6::Junction qw(any); use NEXT; # Core modules use Error qw(:try); my $ROW_ID = 'form'; # Group: Public methods # Constructor: new # # Create the model instance # # Parameters: # # confmodule - the GConf eBox module which # gives the environment where to store data # # directory - String the subdirectory within the environment # where the data will be stored # sub new { my $class = shift; my $self = $class->SUPER::new(@_); bless ($self, $class); return $self; } # Method: ids # # Overrides to return only the single row in the form # sub ids { return [ $ROW_ID ]; } sub _ids { return [ $ROW_ID ]; } # Method: setValue # # Set the value of a element and store the row # sub setValue { my ($self, $element, $value) = @_; my $row = $self->row(); $row->elementByName($element)->setValue($value); $row->store(); } # Method: value # # Get the value of a element of the row # sub value { my ($self, $element) = @_; return $self->row()->valueByName($element); } # Method: _checkTable # # Method overriden to add some checks # # Overrides: # EBox::Model::DataTable::_checkTable sub _checkTable { my ($self, $table) = @_; $self->SUPER::_checkTable($table); my @unallowedSuperParams = qw(sortedBy order); foreach my $param (@unallowedSuperParams) { if (exists $table->{$param}) { # FIXME: WTF is this? throw EBox::Exceptions::Internal( ); } } } # Method: addRow # # This method has no sense since it has just one row. To fill # the model instance it should be used # . # # Overrides: # # # # Exceptions: # # - throw since it is not possible # to add rows to an one-rowed table # sub addRow { throw EBox::Exceptions::Internal('It is not possible to add a row to ' . 'an one-rowed table'); } # Method: addTypedRow # # This method has no sense since it has just one row. To fill # the model instance it should be used # . # # Overrides: # # # # Exceptions: # # - throw since it is not possible # to add rows to an one-rowed table # sub addTypedRow { throw EBox::Exceptions::Internal('It is not possible to add a row to ' . 'an one-rowed table'); } # Method: row # # Return the row. It ignores any additional parameter # # Overrides: # # # sub row { my ($self, $id) = @_; if ($self->_rowStored()) { return $self->SUPER::row($ROW_ID); } else { $self->_defaultRow(); } } sub _rowStored { my ($self) = @_; my $rowDir = $self->{directory} . "/$ROW_ID"; return defined $self->{'confmodule'}->get($rowDir); } # Method: moveUp # # Move a row up. It makes no sense in an one-rowed table. # # Overrides: # # # # Exceptions: # # - thrown since it has no sense in # an one-rowed table # sub moveUp { throw EBox::Exceptions::Internal('Cannot move up a row in an form'); } # Method: moveDown # # Move a row down. It makes no sense in an one-rowed table. # # Overrides: # # # # Exceptions: # # - thrown since it has no sense in # an one-rowed table # sub moveDown { throw EBox::Exceptions::Internal('Cannot move down a row in a form'); } # Method: removeRow # # Remove a row. It makes no sense in an one-rowed table. # # When the remove is forced, # is called. # # Overrides: # # # # Exceptions: # # - thrown since it has no sense in # an one-rowed table except when forcing. # sub removeRow { my ($self, $id, $force) = @_; if ($force) { $self->removeAll($force); } else { throw EBox::Exceptions::Internal('Cannot remove a row in a form. Use removeAll() instead.'); } } # Method: removeAll # # Remove all data from the form # # Overrides: # # # # Exceptions: # # - thrown if the remove is not # forced # sub removeAll { my ($self, $force) = @_; if ($force) { # Remove the data $self->{confmodule}->unset("$self->{directory}/form"); } else { throw EBox::Exceptions::Internal('Cannot remove data unless force specified'); } } # Method: warnIfIdUsed # # Warn if the data is already in use. The overridden method must # ignore any additional parameter. # # Overrides: # # # sub warnIfIdUsed { } # Method: setRow # # Set an existing row. The unique row is set here. If there was # no row. It will be created. # # # Overrides: # # # # Parameters: # # force - boolean indicating whether the set is forced or not # params - hash named parameters containing the expected fields # for each row # - Positional parameters # sub setRow { my ($self, $force, %params) = @_; $self->validateRow('update', \%params); # We can only set those types which have setters my @newValues = @{$self->setterTypes()}; # Fetch field trigger names my $viewCustom = $self->viewCustomizer(); my %triggerFields = %{$self->viewCustomizer()->onChangeFields()}; # Fetch trigger values for my $name (keys %triggerFields) { $triggerFields{$name} = $params{$name}; } my $changedData; for (my $i = 0; $i < @newValues ; $i++) { my $newData = $newValues[$i]->clone(); my $fieldName = $newData->fieldName(); # Skip fields that are hidden or disabled by the view customizer unless ($viewCustom->skipField($fieldName, \%triggerFields)) { $newData->setMemValue(\%params); } $changedData->{$fieldName} = $newData; } $self->setTypedRow($ROW_ID, $changedData, force => $force, readOnly => $params{'readOnly'}); } # Method: setTypedRow # # Set an existing row using types to fill the fields. The unique # row is set here. If there was no row, it is created. # # Overrides: # # # sub setTypedRow { my ($self, $id, $paramsRef, %optParams) = @_; if (not $self->_rowStored()) { # first set the default row to be sure we have all the defaults my $row = $self->_defaultRow(); $self->SUPER::addTypedRow( $row->{'valueHash'}, id => $ROW_ID, noOrder => 1, noValidateRow => 1, ); } $self->SUPER::setTypedRow($ROW_ID, $paramsRef, %optParams); } # Method: set # # Set a value from the form # # Parameters: # # There is a variable number of parameters following this # structure: fieldName => fieldValue. Check # for every type to know which # fieldValue is required to be passed # # force - Boolean indicating if the update is forced or not # *(Optional)* Default value: false # # readOnly - Boolean indicating if the row becomes a read only # kind one *(Optional)* Default value: false # # Exceptions: # # - thrown if no params are # passed to set a value sub set { my ($self, %params) = @_; my $force = delete $params{force}; $force = 0 unless defined($force); my $readOnly = delete $params{readOnly}; $readOnly = 0 unless defined($readOnly); unless ( keys %params > 0 ) { throw EBox::Exceptions::MissingArgument('Missing parameters to set their value'); } my $typedParams = $self->_fillTypes(\%params, 1); $self->setTypedRow($ROW_ID, $typedParams, force => $force, readOnly => $readOnly); } # Method: order # # Get the keys order in an array ref. It makes no sense in an one-rowed table. # # Overrides: # # # # Exceptions: # # - thrown since it has no sense in # an one-rowed table # sub order { throw EBox::Exceptions::Internal('It has no sense order in an one-rowed table'); } # Method: sortedBy # # Return the field name which is used by model to sort rows when # the model is not ordered. It makes no sense in an one-rowed table. # # Returns: # # String - field name used to sort the rows # sub sortedBy { throw EBox::Exceptions::Internal('It has no sense sortedBy in an one-rowed table'); } # Method: rowUnique # # Since we have only one row we return false to disable # row-uniqueness tests # # Overrides: # # # sub rowUnique { return 0; } # Method: setFilter # # Set the string used to filter the return of rows. It makes no # sense in an one-rowed table. # # Overrides: # # # # Exceptions: # # - thrown since it has no sense in # an one-rowed table # sub setFilter { throw EBox::Exceptions::Internal('No filter is needed in an one-rowed table'); } # Method: filter # # Get the string used to filter the return of rows. It makes no # sense in an one-rowed table. # # Overrides: # # # # Exceptions: # # - thrown since it has no sense in # an one-rowed table # sub filter { throw EBox::Exceptions::Internal('No filter is needed in an one-rowed table'); } # Method: pages # # Return the number of pages. # # Overrides: # # # sub pages { return 1; } # Method: automaticRemoveMsg # # Get the i18ned string to show when an automatic remove is done # in a model # # Overrides: # # # # Parameters: # # nDeletedRows - Int the deleted row number # sub automaticRemoveMsg { my ($self, $nDeletedRows) = @_; return __x('Remove data from {model}{br}', model => $self->printableName(), br => '
    '); } # Method: updatedRowNotify # # Overrides: # # # sub updatedRowNotify { my ($self, @params) = @_; $self->formSubmitted(@params); } # Method: formSubmitted # # Override this method to be notified whenever a form # is submitted # # Parameters: # # oldRow - containing the old row # sub formSubmitted { } # Method: AUTOLOAD # # This method will intercept any call done to undefined # methods. It is used to return the value, printableValue or Type # from an attribute which belongs to the form. # # So, a form which contains a boolean attribute called enabled # may have this four methods: # # - enabledValue() - return the value from the attribute # enabled # # - enabledPrintableValue() - return the printable value # from the attribute enabled # # - enabledType() - return the type from the attribute # enabled, that is, a instance of class. # # - enabled() - the same as enabledValue() # # Returns: # # - the value if it ends with Value() or it is just the attribute name # - String - the printable value if it ends with PrintableValue() # - - the type if it ends with Type() # # Exceptions: # # - thrown if no attribute exists or # it is not finished correctly # sub AUTOLOAD { my ($self, @params) = @_; my $methodName = our $AUTOLOAD; $methodName =~ s/.*:://; # Ignore DESTROY callings (the Perl destructor) if ( $methodName eq 'DESTROY' ) { return; } unless ( UNIVERSAL::can($self, 'row') ) { use Devel::StackTrace; my $trace = new Devel::StackTrace(); EBox::debug($trace->as_string()); throw EBox::Exceptions::Internal("Not valid autoload method $methodName since " . "$self is not a EBox::Model::DataForm"); } my $row = $self->row(); # Get the attribute and its suffix if any (Value|PrintableValue|Type|) my ($attr, $suffix) = $methodName =~ m/^(.+?)(Value|PrintableValue|Type|)$/; unless ( any( keys ( %{$row->hashElements()} ) ) eq $attr ) { # Try with the parent autoload return $self->NEXT::ACTUAL::AUTOLOAD(@params); } # If no suffix is given used unless ( $suffix ) { # Use the default value $suffix = 'Value'; } if ( $suffix eq 'Value' ) { return $row->valueByName($attr); } elsif ( $suffix eq 'PrintableValue' ) { return $row->printableValueByName($attr); } elsif ( $suffix eq 'Type' ) { return $row->elementByName($attr); } } # Group: Protected methods # Method: _setDefaultMessages # # Overrides: # # # sub _setDefaultMessages { my ($self) = @_; unless (exists $self->table()->{'messages'}->{'update'}) { $self->table()->{'messages'}->{'update'} = __('Done'); } } # Group: Class methods # Method: Viewer # # Overrides: # # # sub Viewer { return '/ajax/form.mas'; } # Group: Private methods # Return a row with only default values sub _defaultRow { my ($self) = @_; my $dir = $self->{'directory'}; my $confmod = $self->{'confmodule'}; my $row = EBox::Model::Row->new(dir => $dir, confmodule => $confmod); $row->setModel($self); $row->setId($ROW_ID); foreach my $type (@{$self->table()->{'tableDescription'}}) { my $element = $type->clone(); $row->addElement($element); } return $row; } # Method: clone # # Overrides: EBox::Model::DataTable::clone # # Adaptation of the overriden method to DataForm class sub clone { my ($self, $srcDir, $dstDir) = @_; my $origDir = $self->directory(); try { $self->setDirectory($srcDir); my $srcRow = $self->row(); my $srcHashElements = $srcRow->hashElements(); $self->setDirectory($dstDir); my $dstRow = $self->row(); while (my ($name, $srcElement) = each %{ $srcHashElements }) { $dstRow->elementByName($name)->setValue($srcElement->value()); } $dstRow->store(); $dstRow->cloneSubModelsFrom($srcRow) } finally { $self->setDirectory($origDir); }; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Model/Composite.pm0000664000000000000000000004650012017102272017610 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Model::Composite # # This class is intended to hold a number of models inside. This # composite class will have a defined layout. This layout will be # used to establish the output on the view. # # The possible components should be subclasses of: # # - # - # # The possible layout that it will implemented are the following: # # - top-bottom - the components will be shown from top to the # bottom in the given order # - tabbed - the components will be shown in a tab way # package EBox::Model::Composite; use base 'EBox::Model::Component'; use strict; use warnings; use EBox::Exceptions::Internal; use EBox::Exceptions::DataNotFound; use EBox::Exceptions::InvalidType; use EBox::Exceptions::InvalidData; use EBox::Exceptions::MissingArgument; use EBox::Gettext; use EBox::Model::Manager; # Other modules uses use Error qw(:try); ################# # Dependencies ################# use Perl6::Junction qw(any); # Constants use constant LAYOUTS => qw(top-bottom tabbed); # Group: Public methods # Constructor: new # # Constructor for the EBox::Model::Composite. In order to set # the attributes for the composite object, it is required to # override the method # # Exceptions: # # - thrown if layout parameter # has not any of the allowed values # - thrown if any parameter has # not the correct type # sub new { my ($class, %params) = @_; my $self = { %params }; bless ($self, $class); my $description = $self->_description(); $self->_setDescription($description); return $self; } # Method: components # # Get the components which this composite consists of # # Returns: # # array ref - containing the components which this composite # comprises. The elements are or # . # sub components { my ($self) = @_; return $self->{components}; } # Method: componentByName # # get a specific component from the composite # # Parameters: # name - the name of the component to fetch # recursive - searchs inside the components (default: false) # # Returns: # the component or undef if there is not any component with the given name sub componentByName { my ($self, $name, $recursive) = @_; $name or throw EBox::Exceptions::MissingArgument('name'); my $components = $self->components(); foreach my $comp (@{ $components }) { if ($name eq $comp->name()) { return $comp; } if ($recursive) { if ($comp->can('componentByName')) { my $compFromChild; $compFromChild = $comp->componentByName($name, 1); if (defined $compFromChild) { return $compFromChild; } } } } return undef; } # Method: componentNames # # Override this to dynamically calculate which components should # be included in the composite # # Returns: # # array ref - containing the names of the components # sub componentNames { my ($self) = @_; return []; } # Method: setLayout # # Set the layout where the elements will be displayed. # # Parameters: # # layout - String Set the element layout. The possible values # are: # # - top-bottom - the elements will be shown sequentially # # - tabbed - every element will be shown in a tab # # Exceptions: # # - # thrown if any mandatory parameter is missing # - thrown if layout parameter # has not any of the allowed values # sub setLayout { my ($self, $layout) = @_; defined ($layout) or throw EBox::Exceptions::MissingArgument('layout'); unless ($layout eq any(LAYOUTS)) { throw EBox::Exceptions::InvalidData( data => 'layout', value => $layout, advice => __x('It should be one of following values: {values}', values => join(', ', LAYOUTS)) ); } $self->{layout} = $layout; } # Method: layout # # Get the components layout from this composite # # Returns: # # String - with the current used layout # sub layout { my ($self) = @_; return $self->{layout}; } # Method: name # # Get the composite's name # # Returns: # # String - the composite's name sub name { my ($self) = @_; return $self->{name}; } # Method: contextName # # Get the composite's context name, that is, the composite's name # plus its index if any separated by a slash. If the composite # has no index, the return value is equal to the # return value. # # Returns: # # String - the composite's context name # sub contextName { my ($self) = @_; if ($self->index()) { return '/' . $self->{name} . '/' . $self->index(); } else { return $self->{name}; } } # XXX transitional method, this will be the future name() method sub nameFromClass { my ($self) = @_; my $class; if (ref $self) { $class = ref $self; } else { $class = $self; } my @parts = split '::', $class; my $name = pop @parts; return $name; } # Method: printableName # # Get the composite's printable name # # Returns: # # String - the composite's printable name # sub printableName { my ($self) = @_; return $self->{printableName}; } # Method: precondition # # Check if the composite has enough data to be manipulated, that # is, this precondition constraint is accomplished. # # This method must be override by those composites which requires # any precondition to work correctly. Associated to the # precondition there is a fail message which displays what it is # required to make composite work using method # # # Returns: # # Boolean - true if the precondition is accomplished, false # otherwise # Default value: true sub precondition { return 1; } # Method: preconditionFailMsg # # Return the fail message to inform why the precondition to # manage this composite is not accomplished. This method is related # to . # # Returns: # # String - the i18ned message to inform user why this model # cannot be handled # # Default value: empty string # sub preconditionFailMsg { return ''; } # Method: index # # Get the composite's index if any to distinguish same composite # class with different instances. To be overridden by # subclasses. Default value: empty string # # Returns: # # String - the index to distinguish instances if any # sub index { return ''; } # Method: printableIndex # # Get the composite's printable index. Explanation about index # value on method # header. To be overridden by children classes which are # parameterised composites. # # Returns: # # String - the i18ned index value # sub printableIndex { return ''; } # Method: help # # Get the help string which may indicate the user how to use the # composite content # # Returns: # # String - the i18ned string which contents the help if any # sub help { my ($self) = @_; return $self->{help}; } # Method: permanentMessage # # Get the permanent message to be shown as a note within the # composite # # Returns: # # String - the i18ned string which contents the permanent message # sub permanentMessage { my ($self) = @_; return $self->{permanentMessage}; } # Method: permanentMessageType # # Return the type for the defined permanent message # # Returns: # # string - note, ad or warning # sub permanentMessageType { my ($self) = @_; return $self->{permanentMessageType}; } # Method: compositeDomain # # Get the domain where the model is handled. That is, the eBox # module which the composite belongs to # # Returns: # # String - the composite domain, the first letter is upper-case # sub compositeDomain { my ($self) = @_; return $self->{compositeDomain}; } # Method: menuNamespace # # Get the composite's menu namespace # # Returns: # # String - the composite's menu namespace # sub menuNamespace { my ($self) = @_; if ($self->{menuNamespace}) { return $self->{menuNamespace}; } elsif ( defined ( $self->compositeDomain() )) { # This is autogenerated menuNamespace got from the composite # domain and its name return $self->compositeDomain() . '/Composite/' . $self->name(); } else { return undef; } } # Method: action # # Accessor to the URLs where the actions are published to # run. In a composite type, two actions are possible: # - view - show the composite type within the whole eBox menu # - changeView - show the composite type isolated. I.e. the HTML # dumped from the composite Viewer # # Parameters: # # actionName - String the action name # # Returns: # # String - URL where the action will be called # # Exceptions: # # - thrown if the action name # is not one of the allowed ones # # - thrown if the action name # has not defined action # sub action { my ($self, $actionName) = @_; unless ($actionName eq any('view', 'changeView')) { throw EBox::Exceptions::InvalidData(data => __('Action'), value => $actionName, advice => __x('Actions to be taken ' . 'allowed are: {view} and ' . '{cView}', view => 'view', cView => 'changeView', )); } my $actionsRef = $self->{actions}; if (exists ($actionsRef->{$actionName})) { return $actionsRef->{$actionName}; } else { throw EBox::Exceptions::DataNotFound(data => __('Action'), value => $actionName); } } # Group: Class methods # Method: Viewer # # Class method to return the viewer from this composite. # # Returns: # # String - the path to the Mason template which acts as the # viewer from composite # sub Viewer { return '/ajax/composite.mas'; } # Group: Protected methods # Method: _description # # Describe the Composite content, that is, its components, # layout, and much more. # # # Returns: # # hash ref - the composite description containing the following # elements: # # components - array ref containing the components that will # contain the composite. It can be a String which can refer # to a/some or # . *(Optional)* Default value: empty array # # layout - String define the layout of the corresponding views # of the models. It can be one of the following: 'top-bottom' or # 'tabbed' *(Optional)* Default value: 'top-bottom' # # name - String the composite's name *(Optional)* Default value: # class name # # printableName - String the composite's localisated name # *(Optional)* Default value: empty string # # help - String the localisated help which may indicate the user # how to use the composite content. *(Optional)* Default value: # empty string # # actions - array ref containing hash ref whose elements has a # String as a key which is the action name and the value is # another String which represents the URL which takes the # action. This is useful to know what to call when an action # should be taken. *(Optional)* Default values: default actions # are general done by generic CGIs if compositeDomain attribute # is set. # # compositeDomain - String the composite's domain, that is, the # eBox module which composite belongs to. First letter must be # upper-case *(Optional)* # # menuNamespace - String the menu namespace, this is used in # order to show the context within the eBox menu *(Optional)* # # permanentMessage - String the permanent message to be shown # always as a side note in the composite *(Optional)* # # permanentMessage - Type of permanent message: note (default), # ad, warning *(Optional)* # # sub _description { } # Group: Private methods # Method: _setDescription # # Check the composite description and stores the attributes in # composite instance # # Parameters: # # description - hash ref the description to check and set # # Exceptions: # # - thrown if any attribute has # not correct type # # - thrown if any attribute has # not correct data # sub _setDescription { my ($self, $description) = @_; $self->{layout} = 'top-bottom'; $self->{name} = ref( $self ); $self->{printableName} = ''; $self->{help} = ''; $self->{permanentMessage} = ''; $self->{permanentMessageType} = 'note'; $self->{compositeDomain} = delete ( $description->{compositeDomain} ); $self->{menuNamespace} = delete ($description->{menuNamespace}); if (exists ($description->{layout})) { $self->setLayout( delete ( $description->{layout} )); } if (exists ($description->{name})) { $description->{name} or throw EBox::Exceptions::Internal('name for composite cannot be empty'); $self->{name} = delete ( $description->{name} ); } # String properties foreach my $property (qw(printableName help permanentMessage permanentMessageType)) { if (exists ($description->{$property})) { $self->{$property} = delete ( $description->{$property} ); } } $self->{actions} = $description->{actions}; # Set the Composite actions, do not ovewrite the user-defined actions $self->_setDefaultActions(); } # Method: _setDefaultActions # # Set the default actions if no user defined previously # sub _setDefaultActions { my ($self) = @_; my $actionsRef = $self->{actions}; $actionsRef = {} unless defined ($actionsRef); if (defined ($self->compositeDomain())) { unless (exists $actionsRef->{view}) { $actionsRef->{view} = '/' . $self->compositeDomain() . '/Composite/' . $self->name(); if ( $self->index() ) { # Append the index $actionsRef->{view} .= '/' . $self->index(); } } unless (exists $actionsRef->{changeView}) { $actionsRef->{changeView} = '/' . $self->compositeDomain() . '/Composite/' . $self->name(); if ( $self->index() ) { $actionsRef->{changeView} .= '/' . $self->index(); } $actionsRef->{changeView} .= '/changeView'; } } $self->{actions} = $actionsRef; } sub keywords { my ($self) = @_; #get the keywords from every component plus own ones and flatten them into #an array return [@{$self->SUPER::keywords()}, map { @{$_->keywords()} } @{$self->components()}]; } # Method: setDirectory # # Sets directory on its child components # # Parameters: # # directory - string containing the directory key # sub setDirectory { my ($self, $dir, $force) = @_; unless (defined $dir) { throw EBox::Exceptions::MissingArgument('dir'); } $self->{directory} = $dir; foreach my $component (@{$self->components()}) { $self->_setComponentDirectory($component, $dir); } } # Method: directory # # Get the current directory. # # Returns: # # String - Containing the directory # sub directory { my ($self) = @_; return $self->{directory}; } sub _setComponentDirectory { my ($self, $comp, $dir) = @_; $comp->{parent} = $self->{parent}; if ($comp->isa('EBox::Model::DataTable')) { $dir .= '/' if $dir; $dir .= $comp->name(); } $comp->setDirectory($dir); } # Method: filesPaths # # Returns: # the paths of the files managed by the composite and all of its nested components sub filesPaths { my ($self) = @_; my @paths; foreach my $comp (@{ $self->components() }) { if ($comp->can('filesPaths')) { push @paths, @{ $comp->filesPaths() }; } } return \@paths; } # Method: backupFiles # # Make an actual configuration backup of all the files contained in the # components. This backup will used to discard cahnges if needed sub backupFiles { my ($self) = @_; foreach my $comp (@{ $self->components() }) { if ($comp->can('backupFiles')) { $comp->backupFiles(); } } } # Method: restoreFiles # # Restores the actual configuration backup of files, thus discarding last # changes in files sub restoreFiles { my ($self) = @_; foreach my $comp (@{ $self->components() }) { if ($comp->can('restoreFiles')) { $comp->restoreFiles(); } } } sub pageTitle { my ($self) = @_; my $desc = $self->_description(); if (exists $desc->{pageTitle}) { return $desc->{pageTitle}; } else { return undef; } } sub headTitle { my ($self) = @_; my $desc = $self->_description(); if (exists $desc->{headTitle}) { return $desc->{headTitle}; } else { return undef; } } sub HTMLTitle { my ($self) = @_; my $pageTitle = $self->pageTitle(); return undef unless ($pageTitle); return [ { title => $pageTitle, link => undef } ]; } # Method: clone # # This works as the EBox::DataTable::clone method it does the operation in all # composite's component sub clone { my ($self, $srcDir, $dstDir) = @_; my $origDir = $self->directory(); try { # we need to do this operation in each component with the correct # directories for src and dst component my @components = @{ $self->components() }; foreach my $comp (@components) { $self->setDirectory($srcDir, 1); my $compSrcDir = $comp->directory(); $self->setDirectory($dstDir, 1); my $compDstDir = $comp->directory(); $comp->clone($compSrcDir, $compDstDir); } } finally { $self->setDirectory($origDir, 1); }; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Model/DataForm/0000775000000000000000000000000012017102272017000 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Model/DataForm/ReadOnly.pm0000664000000000000000000000731112017102272021055 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Model::DataForm::ReadOnly # # An specialized model from which # only shows information in a form schema # package EBox::Model::DataForm::ReadOnly; use base 'EBox::Model::DataForm'; use strict; use warnings; use EBox::Exceptions::Internal; use EBox::Gettext; # Group: Public methods # Method: setRow # # It has non sense in a read only form # # Overrides: # # # # Exceptions: # # - thrown since it is not possible # to set rows to a read only row # sub setRow { throw EBox::Exceptions::Internal('It is not possible to set a row to ' . 'an read only form'); } # Method: setTypedRow # # It has non sense in a read only form # # Overrides: # # # # Exceptions: # # - thrown since it is not possible # to set rows to a read only row # sub setTypedRow { throw EBox::Exceptions::Internal('It is not possible to set a row to ' . 'an read only form'); } # Method: row # # Return the row. # # Overrides: # # # sub row { my ($self) = @_; unless ( $self->{readOnlyed} ) { # Set every field as non-editable foreach my $fieldName (@{$self->fields()}) { my $field = $self->fieldHeader($fieldName); $field->{editable} = 0; } $self->{readOnlyed} = 1; } $self->{content} = $self->_content(); if (defined ($self->{content})) { my $types = $self->_fillTypes($self->{content}); my %printableValueHash = map { $_->fieldName() => $_->printableValue() } values (%{$types}); my @values = map { $types->{$_} } @{$self->fields()}; @values = grep { defined ( $_ ) } @values; # Remove not defined fields return { values => \@values , valueHash => $types, plainValueHash => $self->{content}, printableValueHash => \%printableValueHash, }; } else { return $self->SUPER::row(); } } # Group: Class methods # Method: Viewer # # Overrides: # # # sub Viewer { return '/readOnlyForm.mas'; } # Group: Protected methods # Method: _content # # This method is intended to be overridden by subclasses to return # the data acquired using the programming logic from wherever it # is needed. # # It is predominant against default values from the model # description. # # Default return value: undef # # Returns: # # hash ref - the pair (key, value) where the key is the field name # from the model description using # and the value is the same used # when creating a type with a default value or setting or updating # using autoload methods. Check _setValue method from every # class for more information # sub _content { return undef; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Model/DataForm/Test.pm0000664000000000000000000002435212017102272020263 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Model::DataForm::Test; use lib '../../..'; use base 'EBox::Test::Class'; use strict; use warnings; use Test::More;; use Test::Exception; use Test::MockObject; use Test::MockObject::Extends; use Perl6::Junction qw(any); use EBox::Types::Abstract; use EBox::Model::Row; use EBox::Model::DataForm; use EBox::Model::Manager; use EBox::Types::Abstract; use EBox::Types::HasMany; use EBox::Types::Text; { my $rowIdUsed; no warnings 'redefine'; sub EBox::Model::Manager::warnIfIdIsUsed { my ($self, $context, $id) = @_; if (not defined $rowIdUsed) { return; } elsif ($rowIdUsed eq $id) { throw EBox::Exceptions::DataInUse('fake warnIfIdIsUsed: row in use'); } } sub EBox::Model::Manager::warnOnChangeOnId { my ($self, $tableName, $id) = @_; if (not defined $rowIdUsed) { return; } elsif ($rowIdUsed eq $id) { throw EBox::Exceptions::DataInUse('fake warnIfIdIsUsed: row in use'); } } sub EBox::Model::Manager::removeRowsUsingId { # do nothing } sub EBox::Model::Manager::modelActionTaken { # do nothing } sub setRowIdInUse { my ($rowId) = @_; $rowIdUsed = $rowId; } } sub setEBoxModules : Test(setup) { EBox::TestStubs::fakeEBoxModule(name => 'fakeModule'); } sub clearGConf : Test(teardown) { EBox::TestStubs::setConfig(); } sub deviantFormTest : Test(6) { my ($self) = @_; my @cases; push @cases, [ 'empty table description' => { tableName => 'test', } ]; push @cases, [ 'empty tableDescription' => { tableDescription => [], tableName => 'test', } ]; push @cases, [ 'repeated field name' => { tableDescription => [ new EBox::Types::Abstract( fieldName => 'repeated', ), new EBox::Types::Abstract( fieldName => 'repeated', ), ], tableName => 'test', } ]; push @cases, [ 'no table name' => { tableDescription => [ new EBox::Types::Abstract( fieldName => 'field1', ), ], } ]; push @cases, [ 'form with order' => { tableDescription => [ new EBox::Types::Abstract( fieldName => 'field1', ), ], order => 1, } ]; push @cases, [ 'form with sortedBy' => { tableDescription => [ new EBox::Types::Abstract( fieldName => 'field1', ), ], sortedBy => 'field1', } ]; foreach my $case_r (@cases) { my ($caseName, $table) = @{ $case_r }; my $dataForm = $self->_newDataForm($table); dies_ok { $dataForm->table(); } "expecting error with deviant form case: $caseName"; } } sub formTest : Test(2) { my ($self) = @_; my @cases; push @cases, [ 'simple form' => { tableDescription => [ new EBox::Types::Abstract( fieldName => 'field1', ), ], tableName => 'test', } ]; foreach my $case_r (@cases) { my ($caseName, $table) = @{ $case_r }; my $dataForm = $self->_newDataForm($table); my $tableFromForm; lives_ok { $tableFromForm = $dataForm->table(); } "checking first call to form method with: $caseName"; ok exists $tableFromForm->{tableDescriptionByName}, 'checking that some fileds were inserted by first time setup'; } } sub deviantSetTest : Test(2) { my ($self) = @_; my $dataForm = $self->_newDataForm(); $dataForm->set_true('addedRowNotify', 'updatedRowNotify'); my @cases = ( { secondField => 'aaa', }, ); foreach my $case (@cases) { my %params = %{ $case }; dies_ok { $dataForm->set(%params); } 'expecting error with incorrect srt operation'; ok( (not $dataForm->called('updatedRowNotify')), 'Checing that noitfication method was nto called' ); } } sub setTest : Test(11) { my ($self) = @_; my $dataForm = $self->_newDataForm(); $dataForm->set_true('addedRowNotify', 'updatedRowNotify'); my @cases = ( { firstField => 'aaa', secondField => 'bbb', }, { firstField => 'aaz', secondField => 'bbc', defaultField => 'adad', optionalField => 'dadaa', }, ); my $firstTime = 1; foreach my $case_r (@cases) { my %params = %{ $case_r }; lives_ok { $dataForm->set(%params) } 'setting data form values'; if (not exists $params{defaultField}) { $params{defaultField} = 'defaultText'; } while (my ($field, $expectedValue) = each %params) { my $getter = $field . 'Value'; is $dataForm->$getter, $expectedValue, "Checking value of field $field"; } $dataForm->called_ok('updatedRowNotify'); $dataForm->clear(); } } sub _newDataForm { my ($self, $table) = @_; if (not defined $table) { $table = $self->_tableDescription4fields(); } my $confmodule = EBox::Global->modInstance('fakeModule'); my $dataFormDir = '/ebox/modules/fakeModule/DataForm'; # remove old data from previous modules $confmodule->delete_dir($dataFormDir); my $dataFormBase = EBox::Model::DataForm->new( confmodule => $confmodule, directory => $dataFormDir, domain => 'domain', ); my $dataForm = Test::MockObject::Extends->new($dataFormBase); $dataForm->set_always('_table' => $table); return $dataForm; } sub _tableDescription4fields { my $tableDescription = { tableDescription => [ new EBox::Types::Text( fieldName => 'firstField', printableName => 'firstField', ), new EBox::Types::Text( fieldName => 'secondField', printableName => 'secondField', ), new EBox::Types::Text( fieldName => 'defaultField', printableName => 'defaultField', defaultValue => 'defaultText', ), new EBox::Types::Text( fieldName => 'optionalField', printableName => 'optionalField', optional => 1, ), ], tableName => 'test', }; return $tableDescription; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Model/DataForm/Download.pm0000664000000000000000000000431212017102272021105 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Model::DataFormDownload # # An specialized model from # which is used to download a file from an action form. # # It redirects the response to # to download a file from EBox::Config::tmp() # # How to use it? # # Extends this model with your custom model. # # Implement formSubmitted(). Do your stuff and create a file in # EBox::Config::tmp() . '/downloads/' . your_file_name # # At the end of this method call pushFileToDownload(your_file_name); # # package EBox::Model::DataForm::Download; use base 'EBox::Model::DataForm::Action'; use strict; use warnings; # eBox Exceptions use EBox::Exceptions::MissingArgument; # Core modules use Error qw(:try); use constant URL_REDIRECT => '/Controller/Downloader/FromTempDir?filename='; # Group: Public methods # Constructor: new # # Create the model instance # sub new { my $class = shift; my $self = $class->SUPER::new(@_); bless ( $self, $class ); return $self; } # Method: pushFileToDownload # # Push a file to be downloaded # # Parameters: # # file - file name. This file must live under EBox::Config::tmp() . # '/downloads', typically /var/lib/zentyal/tmp/downloads/ # # You do not have to use the whole path, only the file name sub pushFileToDownload { my ($self, $file) = @_; unless (defined($file)) { throw EBox::Exceptions::MissingArgument('file'); } $self->pushRedirection(URL_REDIRECT . $file); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Model/DataForm/Action.pm0000664000000000000000000000613012017102272020553 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Model::DataForm::Action # # An specialized model from which # performs an action and nothing is stored in a persistent # state. The action may have parameters, however these ones are # not stored for future actions # package EBox::Model::DataForm::Action; use base 'EBox::Model::DataForm'; use strict; use warnings; use EBox::Exceptions::Internal; use EBox::Gettext; # Group: Public methods # Constructor: new # # Create a new model instance # # Parameters: # # confmodule - the GConf eBox module which # gives the environment where to store data # # directory - String the subdirectory within the environment # where the data will be stored # # domain - String the Gettext domain # sub new { my ($class, @params) = @_; my $self = $class->SUPER::new(@params); bless ($self, $class); return $self; } # Method: setTypedRow # # Here it is where to perform the action. This method MUST NOT be # overridden to perform the action. You must better subclass # to perform the action # # Overrides: # # # sub setTypedRow { my ($self, $id, $paramsRef, %optParams) = @_; my $force = delete $optParams{force}; # This method just perform those actions $self->validateTypedRow('update', $paramsRef, $paramsRef, $force); # Notify remainder elements if ((not $force) and $self->table()->{automaticRemove}) { my $manager = EBox::Model::Manager->instance(); $manager->warnOnChangeOnId($self->tableName(), 0, $paramsRef, undef); } # Create the row my @values = values(%{$paramsRef}); my $dir = $self->{'directory'}; my $confmod = $self->{'confmodule'}; my $row = EBox::Model::Row->new(dir => $dir, confmodule => $confmod); $row->setModel($self); $row->setId('dummy'); for my $value (@values) { $row->addElement($value); } $self->setMessage($self->message('update')); my $depModelMsg = $self->_notifyManager('update', $row); if (defined ($depModelMsg) and ($depModelMsg ne '' and $depModelMsg ne '

    ' )) { $self->setMessage($self->message('update') . '

    ' . $depModelMsg); } $self->_notifyManager('update', $row); $self->updatedRowNotify($row, undef, $force); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Model/Image.pm0000664000000000000000000000671112017102272016670 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Model::Image # # An specialized model from which # includes a image or a graphic. package EBox::Model::Image; use base 'EBox::Model::DataForm'; use strict; use warnings; use EBox::CGI::Temp; sub new { my ($class, @params) = @_; my $self = $class->SUPER::new(@params); bless ( $self, $class ); return $self; } sub Viewer { return '/ajax/image.mas'; } # returns a hash with the following: # uri - image uri # alt - alt text for image # - or - # error - error message to print instead of image sub image { my ($self) = @_; my $imageFile_r = EBox::CGI::Temp::newImage(); my $generatedImage_r = $self->_generateImage($imageFile_r->{file}); # add default alt if needed exists $generatedImage_r->{alt} or $generatedImage_r->{alt} = ''; exists $generatedImage_r->{error} or $generatedImage_r->{error} = 'No image available'; if ($generatedImage_r->{image}) { return { url => $imageFile_r->{url}, alt => $generatedImage_r->{alt}, } } else { return { error => $generatedImage_r->{error}, } } } # to override by subclass # must return a hash with the following: # image - wether the image was created or not # alt - alt text for image (optional) # error - error message to print instead of image (optional) # sub _generateImage { throw EBox::Exceptions::NotImplemented(); } # must return the ImageControl subclass associated with the image sub _controlModel { throw EBox::Exceptions::NotImplemented; } sub _controlModelField { my ($self, $field) = @_; my $control = $self->_controlModel; my $getter = $field . 'Value'; return $control->$getter; } # Method: checkTable # # This method does some fast and general checks in the table specification # We override it bz for Images is acceptable to not have elements in the tableDescription # # Override: sub checkTable { my ($self, $table) = @_; if (not exists $table->{tableDescription}) { throw EBox::Exceptions::Internal('Missing tableDescription in table definition'); } if (not $table->{tableName}) { throw EBox::Exceptions::Internal( 'table description has not tableName field or has a empty one' ); } if ((exists $table->{sortedBy}) and (exists $table->{order})) { if ($table->{sortedBy}and $table->{order}) { throw EBox::Exceptions::Internal( 'sortedBy and order are incompatible options' ); } } } # Method: refreshImage # # signal wether the image msut be periodically refreshed or not # # Defaults: true sub refreshImage { my ($self) = @_; return 1; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Model/Composite/0000775000000000000000000000000012017102272017245 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Model/Composite/Test.pm0000664000000000000000000003471412017102272020533 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Model::Composite::Test; use lib '../../..'; use base 'EBox::Test::Class'; use strict; use warnings; use Test::More;; use Test::Exception; use Test::MockObject; use Test::MockObject::Extends; use Perl6::Junction qw(any); use EBox::Types::Abstract; use EBox::Model::Row; use EBox::Model::DataTable; use EBox::Model::Composite; use EBox::Model::Manager; use EBox::Types::Abstract; use EBox::Types::HasMany; use EBox::Types::Text; { my $rowIdUsed; no warnings 'redefine'; sub EBox::Model::Manager::warnIfIdIsUsed { my ($self, $context, $id) = @_; if (not defined $rowIdUsed) { return; } elsif ($rowIdUsed eq $id) { throw EBox::Exceptions::DataInUse('fake warnIfIdIsUsed: row in use'); } } sub EBox::Model::Manager::warnOnChangeOnId { my ($self, $tableName, $id) = @_; if (not defined $rowIdUsed) { return; } elsif ($rowIdUsed eq $id) { throw EBox::Exceptions::DataInUse('fake warnIfIdIsUsed: row in use'); } } sub EBox::Model::Manager::removeRowsUsingId { # do nothing } sub EBox::Model::Manager::modelActionTaken { # do nothing } my %models; sub EBox::Model::Manager::model { my ($self, $path) = @_; unless ( $path ) { throw EBox::Exceptions::MissingArgument('path'); } my $model = $models{$path}; if (not defined $model) { throw EBox::Exceptions::DataNotFound( data => 'model', value => $path); } return $model; } sub setModelForPath { my ($path, $model) = @_; $models{$path} = $model; } sub clearModelsForPath { %models = (); } sub setRowIdInUse { my ($rowId) = @_; $rowIdUsed = $rowId; } my %composites; sub EBox::Model::Manager::composite { my ($self, $path) = @_; unless ( $path ) { throw EBox::Exceptions::MissingArgument('path'); } my $composite = $composites{$path}; if (not defined $composite) { throw EBox::Exceptions::DataNotFound( data => 'composite', value => $path); } return $composite; } sub setCompositeForPath { my ($path, $composite) = @_; $composites{$path} = $composite; } sub clearCompositesForPath { %composites = (); } } sub setEBoxModules : Test(setup) { EBox::TestStubs::fakeEBoxModule(name => 'fakeModule'); } sub clearGConf : Test(teardown) { EBox::TestStubs::setConfig(); } sub clearModelsAndComposites : Test(teardown) { clearModelsForPath(); clearCompositesForPath(); } sub standardSetupForModelsAndComposites { my ($self) = @_; for my $id (1 ..2) { my $name = 'model' . $id; $self->_setMockModel($name); } for my $id (1 ..2) { # XXX is very broken to mock the same class that we are testing! refactor # this my $name = 'composite' . $id; my $composite = Test::MockObject->new(); $composite->{components} = []; $composite->set_always('name' => $name); $composite->set_isa('EBox::Model::Composite', 'EBox::Model::Component'); $composite->mock('setDirectory' => sub { my ($self, $dir) = @_; defined $dir or die 'no dir'; $self->{confdir} = $dir; } ); $composite->mock('directory' => sub { my ($self) = @_; return $self->{confdir}; } ); $composite->mock('addComponent' => sub { my ($self, $comp) = @_; push @{ $self->{components} }, $comp, } ); $composite->mock('components' => sub { my ($self) = @_; return $self->{components} ; } ), $composite->mock('componentByName' => sub { my ($self, $name, $recursive) = @_; # XXX recursive otpion not supproted my @comps = @{ $self->components() }; my ($comp) = grep { $name eq $_->name() } @comps; return $comp; }, ); $composite->mock('setParent' => \&EBox::Model::Component::setParent); $composite->mock('parent' => \&EBox::Model::Component::parent); $composite->mock('parentRow' => \&EBox::Model::Composite::parentRow); $composite->mock('setParentComposite' => \&EBox::Model::Component::setParentComposite); $composite->mock('parentComposite' => \&EBox::Model::Component::parentComposite); # path is the same than name for now setCompositeForPath($name, $composite); } } sub _setMockModel { my ($self, $name) = @_; my $model = Test::MockObject->new(); $model->set_always('name' => $name); $model->set_isa('EBox::Model::DataTable', 'EBox::Model::Component'); $model->mock('setDirectory' => sub { my ($self, $dir) = @_; $dir or die 'no dir'; $self->{confdir} = $dir; } ); $model->mock('directory' => sub { my ($self) = @_; return $self->{confdir}; } ); $model->mock('setParent' => \&EBox::Model::Component::setParent); $model->mock('parent' => \&EBox::Model::Component::parent); $model->mock('parentRow' => \&EBox::Model::DataTable::parentRow); $model->mock('setParentComposite' => \&EBox::Model::Component::setParentComposite); $model->mock('parentComposite' => \&EBox::Model::Component::parentComposite); # path is the same than name for now setModelForPath($name, $model); } sub deviantDescriptionTest : Test(2) { my ($self) = @_; my %cases = ( 'select layout text but select layout was not setted' => { name => 'ctest', printableName => 'ctest', layout => 'tabbed', selectMessage => 'select', }, 'empty name' => { name => '', printableName => 'ctest', }, ); while (my ($testName, $description) = each %cases) { CompositeSubclass->setNextDescription($description); my $composite; dies_ok { $composite = new CompositeSubclass(); } $testName } } sub descriptionTest : Test(2) { my ($self) = @_; my %cases = ( 'empty descrition' => {}, 'description' => { name => 'ctest', printableName => 'ctest', } ); while (my ($testName, $description) = each %cases) { CompositeSubclass->setNextDescription($description); my $composite; lives_ok { $composite = new CompositeSubclass(); } $testName; } } sub componentsTest : Test(17) { my ($self) = @_; CompositeSubclass->setStandardDescriptionWithComponents(); my $composite = new CompositeSubclass(); my @components; lives_ok { @components = @{ $composite->components() }; } 'Checking components method call'; is @components, 4, 'checking number of components'; foreach my $comp (@components) { isa_ok $comp, 'EBox::Model::Component', 'Checking class of value form the list returned in components()'; } # check componentByName # @componentByName from setStandardDescriptionWithComponents my @componentNames = qw(model1 model2 composite1 composite2); foreach my $name (@componentNames) { my $component = $composite->componentByName($name); is $component->name(), $name, 'checking component fetched with componentByName'; } foreach my $name (@componentNames) { my $component = $composite->componentByName($name, 1); is $component->name(), $name, 'checking component fetched with componentByName with recursive option'; } is $composite->componentByName('sdfd'), undef, 'checking that componentByName for inexistent component returns undef'; my $composite1 = $composite->componentByName('composite1'); my $nestedComponentName = 'nested1'; $self->_setMockModel($nestedComponentName); my $modelManager = EBox::Model::Manager->instance(); my $nestedModel = $modelManager->model($nestedComponentName); $composite1->addComponent($nestedModel); defined($composite1->componentByName($nestedComponentName)) or die 'addComponent error'; is $composite->componentByName($nestedComponentName), undef, 'checking that componentByName without recursive cannot get a nested component'; my $fetchedNestedModel = $composite->componentByName($nestedComponentName, 1); is $fetchedNestedModel->name(), $nestedComponentName, 'checking name of nested model fetched with componentByName with resursive option'; } sub setDirectoryTest : Test(10) { my ($self) = @_; CompositeSubclass->setStandardDescription(); my $composite = new CompositeSubclass(); is '', $composite->directory(), 'checking that default directory is root (empty string))'; my $directory = '/ea/oe'; lives_ok { $composite->setDirectory($directory); } 'setDirectory in a composite with No components'; is $composite->directory(), $directory, 'checking directory'; dies_ok { $composite->setDirectory(undef); } 'setDirectory to undef fails'; CompositeSubclass->setStandardDescriptionWithComponents(); $composite = new CompositeSubclass(); lives_ok { $composite->setDirectory($directory); } 'setDirectory for a composite with components'; is $composite->directory(), $directory, 'checking directory change'; foreach my $component (@{ $composite->components() }) { my $compDir = $directory; if (not $component->isa('EBox::Model::Composite')) { $compDir .= '/' . $component->name(); } is $component->directory(), $compDir, 'checking that directory was changed in subcompoents too'; } } sub parentTest : Test(13) { my ($self) = @_; my $compDirectory = 'GlobalGroupPolicy/keys/glob9253/filterPolicy'; my $parentRowId = 'glob9253'; CompositeSubclass->setStandardDescriptionWithComponents(); my $parent = Test::MockObject->new(); $parent->set_isa('EBox::Model::DataTable'); $parent->mock( 'row' => sub { my ($self, $id) = @_; if ($id eq $parentRowId) { my $row = Test::MockObject->new(); $row->set_isa('EBox::Model::Row'); $row->set_always(id => $parentRowId); return $row; } else { return undef; } } ); my $composite = new CompositeSubclass(); $composite->setDirectory($compDirectory); is $composite->parent(), undef, 'checking that default parent is undef'; is $composite->parentRow(), undef, 'checkign that parentRow without parent returns undef'; lives_ok { $composite->setParent($parent); } 'Setting parent for composite'; is $composite->parent(), $parent, 'checking that parent was correctly setted'; my $parentRow = $composite->parentRow(); is $parentRow->id(), $parentRowId, 'checking that parentRow upon composite returns the correct row'; my @components = @{ $composite->components() }; while (@components) { my $component = shift @components; if ($component->isa('EBox::Model::Composite')) { push @components, @{ $component->components() }; } is $component->parent(), $parent, 'checking that parent was correctly setted in component'; $parentRow = $component->parentRow(); is $parentRow->id(), $parentRowId, 'checking that parentRow upon component returns the correct row'; } } package CompositeSubclass; use base 'EBox::Model::Composite'; my $nextDescription; sub _description { return $nextDescription; } sub setNextDescription { my ($class, $desc) = @_; $nextDescription = $desc; } sub setStandardDescription { my ($class) = @_; my $desc = { name => 'ctest', printableName => 'ctest', components => [], }; $class->setNextDescription($desc); } sub setStandardDescriptionWithComponents { my ($class) = @_; EBox::Model::Composite::Test->standardSetupForModelsAndComposites(); my $desc = { name => 'ctest', printableName => 'ctest', components => [qw(model1 model2 composite1 composite2)], }; $class->setNextDescription($desc); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Model/DataTable.pm0000664000000000000000000034052112017102272017467 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Model::DataTable; use base 'EBox::Model::Component'; use strict; use warnings; use EBox; use EBox::Global; use EBox::Model::Manager; use EBox::Model::Row; use EBox::View::Customizer; use EBox::Gettext; use EBox::Exceptions::Internal; use EBox::Exceptions::MissingArgument; use EBox::Exceptions::DataExists; use EBox::Exceptions::DataMissing; use EBox::Exceptions::DataNotFound; use EBox::Exceptions::DataInUse; use EBox::Exceptions::DeprecatedMethod; use EBox::Exceptions::NotImplemented; use EBox::Sudo; use EBox::Types::Boolean; use Clone::Fast; use Encode; use Error qw(:try); use POSIX qw(ceil); use Perl6::Junction qw(all any); use List::Util; sub new { my $class = shift; my %opts = @_; my $confmodule = delete $opts{'confmodule'}; $confmodule or throw EBox::Exceptions::MissingArgument('confmodule'); my $directory = delete $opts{'directory'}; $directory or throw EBox::Exceptions::MissingArgument('directory'); my $self = { 'confmodule' => $confmodule, 'confdir' => $directory, 'parent' => $opts{'parent'}, 'directory' => "$directory/keys", 'order' => "$directory/order", 'table' => undef, }; bless($self, $class); return $self; } # Method: table # # Get the table description. It must NOT be overrided. # # Returns: # # hash ref with the table description # sub table { my ($self) = @_; # It's a singleton method unless( defined( $self->{'table'} ) and defined( $self->{'table'}->{'tableDescription'})) { $self->_setupTable(); } return $self->{'table'}; } sub _setupTable { my ($self) = @_; my $table = $self->_table(); $self->checkTable($table); $self->{'table'} = $table; # Set the needed controller and undef setters $self->_setControllers(); # This is useful for submodels $self->{'table'}->{'confdir'} = $self->{'confdir'}; # Add enabled field if desired if ( $self->isEnablePropertySet() ) { $self->_setEnabledAsFieldInTable(); } # Make fields accessible by their names for my $field (@{$self->{'table'}->{'tableDescription'}}) { my $name = $field->fieldName(); $name or throw EBox::Exceptions::Internal('empty field name in type object in tableDescription'); if (exists $self->{'table'}->{'tableDescriptionByName'}->{$name}) { throw EBox::Exceptions::Internal( "Repeated field name in tableDescription: $name" ); } $self->{'table'}->{'tableDescriptionByName'}->{$name} = $field; # Set the model here to allow types have the model from the # addition as well $field->setModel($self); } # If all fields are volatile, then the model is volatile $self->_setIfVolatile(); # Some default values unless (defined($self->{'table'}->{'class'})) { $self->{'table'}->{'class'} = 'dataTable'; } $self->_setDefaultMessages(); } # Method: checkTable # # This method does some fast and general checks in the table specification # sub checkTable { my ($self, $table) = @_; if (not exists $table->{tableDescription} and not exists $table->{customActions}) { throw EBox::Exceptions::Internal('Missing tableDescription in table definition'); } elsif (@{ $table->{tableDescription} } == 0 and @{ $table->{customActions} } == 0) { throw EBox::Exceptions::Internal('tableDescription has not any field or custom action'); } if (not $table->{tableName}) { throw EBox::Exceptions::Internal( 'table description has not tableName field or has a empty one' ); } if ((exists $table->{sortedBy}) and (exists $table->{order})) { if ($table->{sortedBy}and $table->{order}) { throw EBox::Exceptions::Internal( 'sortedBy and order are incompatible options' ); } } } # Method: _table # # Override this method to describe your table. # This method is (PROTECTED) # # Returns: # # table description. See example on . # sub _table { throw EBox::Exceptions::NotImplemented('_table'); } # Method # # Override this method to define sections for thsi model # XXX: define sections format # # Returns: # # Sections description # sub sections { return []; } # Method: modelName # # Return the model name which is set by the key 'tableName' when # a model table is described # # Returns: # # string containing the model name # sub modelName { my ($self) = @_; return $self->table()->{'tableName'}; } # Method: name # # Return the same that # sub name { my ($self) = @_; return $self->modelName(); } # XXX transitional method, this will be the future name() method sub nameFromClass { my ($self) = @_; my $class; if (ref $self) { $class = ref $self; } else { $class = $self; } my @parts = split '::', $class; my $name = pop @parts; return $name; } # Method: contextName # # The context name which is used as a way to know exactly which # module this model belongs to # # Returns: # # String - following this pattern: # '/moduleName/modelName # sub contextName { my ($self) = @_; my $path = '/' . $self->{'confmodule'}->name() . '/' . $self->name() . '/'; return $path; } # Method: printableContextName # # Localisated version of # method to be shown on the user # # Returns: # # String - the localisated version of context name # sub printableContextName { my ($self) = @_; my $printableContextName = __x( '{model} in {module} module', model => $self->printableName(), module => $self->{'confmodule'}->printableName()); return $printableContextName; } # DEPRECATED sub index { #EBox::trace(); #throw EBox::Exceptions::MethodDeprecated(); return ''; } # DEPRECATED sub printableIndex { #EBox::trace(); #throw EBox::Exceptions::MethodDeprecated(); return ''; } # Method: precondition # # Check if the model has enough data to be manipulated, that # is, this precondition constraint is accomplished. # # This method must be override by those models which requires # any precondition to work correctly. Associated to the # precondition there is a fail message which displays what it is # required to make model work using method # # # Returns: # # Boolean - true if the precondition is accomplished, false # otherwise # Default value: true sub precondition { return 1; } # Method: preconditionFailMsg # # Return the fail message to inform why the precondition to # manage this model is not accomplished. This method is related # to . # # Returns: # # String - the i18ned message to inform user why this model # cannot be handled # # Default value: empty string # sub preconditionFailMsg { return ''; } # Method: noDataMsg # # Return the fail message to inform that there are no rows # in the table # # Returns: # # String - the i18ned message to inform user that the table # is empty # # Default value: empty string # sub noDataMsg { my ($self) = @_; my $table = $self->{table}; if ((exists $table->{noDataMsg}) and (defined $table->{noDataMsg})) { return $table->{noDataMsg}; } my $rowName = $self->printableRowName(); if (not $rowName) { $rowName = __('element'); } return __x('There is not any {element}', element => $rowName, ); } # Method: customFilter # # Return whether a custom filter should be used or not. # # If customFilter is enabled, filter searches on the rows have to # be carried out by the method customFilterIds. # # When should I use this? # # Use this option when you have model where you override ids() and row(), # and the amount of rows you can potentially have is big > ~ 4000 entries. # # This is useful to speed up filter searches. If you don't this, the # automatic filter mechanism of models will be to slow. # # This is also useful when you want to take advantage of the search system # of your backend data. For example, if you are mapping data from an LDAP, # you can use this feature to carry out searches using the LDAP protocol. # # Returns: # # Boolean - true if a row may be enabled or not in the model # sub customFilter { my ($self) = @_; return $self->{'table'}->{'customFilter'}; } # Method: isEnablePropertySet # # Return whether the row enabled is set or not # # Returns: # # Boolean - true if a row may be enabled or not in the model # sub isEnablePropertySet { my ($self) = @_; return $self->{'table'}->{'enableProperty'}; } # Method: defaultEnabledValue # # Get the default value for enabled field in a row if the Enable # property is set, check # . If it is not set # the undef value is returned. # # Default value is false. # # Returns: # # boolean - true or false depending on the user defined option on # *defaultEnabledValue* # # undef - if # returns false # sub defaultEnabledValue { my ($self) = @_; unless ( $self->isEnablePropertySet() ) { return undef; } if ( not defined ( $self->{'table'}->{'defaultEnabledValue'} )) { $self->{'table'}->{'defaultEnabledValue'} = 0; } return $self->{'table'}->{'defaultEnabledValue'}; } # Method: sortedBy # # Return the field name which is used by model to sort rows when # the model is not ordered # # Returns: # # String - field name used to sort the rows # sub sortedBy { my ($self) = @_; my $sortedBy = $self->table()->{'sortedBy'}; return '' unless ( defined $sortedBy ); return $sortedBy; } # Method: fieldHeader # # Return the instanced type of a given header field # # Arguments: # # fieldName - field's name # # Returns: # # instance of a type derivated of # # Exceptions: # # - thrown if any compulsory # argument is missing # # - thrown if the given field # name does not exist in the table description # sub fieldHeader { my ($self, $name) = @_; unless (defined($name)) { throw EBox::Exceptions::MissingArgument('field name') } unless (exists ($self->table()->{'tableDescriptionByName'}->{$name})) { throw EBox::Exceptions::DataNotFound( data => __('field'), value => $name); } return $self->table()->{'tableDescriptionByName'}->{$name}; } # Method: optionsFromForeignModel # # This method is used to fetch an array of hashes containing # pairs of value and printableValue. # # It's a convenience method to be used by types # when using foreing modules. # # It's implemented here, because it has to do some caching # due to performance reasons. # # Arguments: # # field - field's name # # Returns: # # Array ref of hashes containing: # # value - row's id # printableValue - field's printableValue # # Example: # [{ 'value' => 'obj001', 'printableValue' => 'administration'}] sub optionsFromForeignModel { my ($self, $field) = @_; unless (defined($field)) { throw EBox::Exceptions::MissingArgument("field's name") } my @options; for my $id (@{$self->ids()}) { my $row = $self->row($id); push (@options, { 'value' => $id, 'printableValue' => $row->printableValueByName($field) }); } return \@options; } # Method: selectOptions # # Override this method to return your select options # for the given select. # # This method is *deprecated*. Use # callback to fill the select options. # # Arguments: # # select - select's name # # Returns: # # Array ref containing hash ref with value, printable # value and selected status # # example: # # [ # { value => '1', printableValue => '1'}, # { value => '2', printableValue => '2'} # ] # sub selectOptions { throw EBox::Exceptions::DeprecatedMethod(); } # Method: validateRow # # Override this method to add your custom checks for # the table fields. The parameters are passed just like they are # received from the CGI. If you want to check on typed data use # instead. # # It will be called whenever a row is added/updated. # # Arguments: # # action - String containing the action to be performed # after validating this row. # Current options: 'add', 'update' # params - hash ref containing fields names and their values # # Returns: # # Nothing # # Exceptions: # # You must throw an exception whenever a field value does not # fulfill your requirements # sub validateRow { } # Method: validateTypedRow # # Override this method to add your custom checks for # the table fields. The parameters are passed like data types. # # It will be called whenever a row is added/updated. # # # Arguments: # # action - String containing the action to be performed # after validating this row. # Current options: 'add', 'update' # # changedFields - hash ref containing the typed parameters # subclassing from # that has changed, the key will be the field's name # Also a key 'id' with the id of the row # # allFields - hash ref containing the typed parameters # subclassing from including changed, # the key is the field's name # Also a key 'id' with the id of the row # # Returns: # # Nothing # # Exceptions: # # You must throw an exception whenever a field value does not # fulfill your requirements # sub validateTypedRow { } # Method: validateRowRemoval # # Override this method to add your custom checks when # a row is to be removed # # It will be called just before the a row is removed # # # Arguments: # # row - Row to be removed # force - whether the removal is force # # Returns: # # Nothing # # Exceptions: # # You must throw an exception whenever you think the removal # is not valid; this will abort it sub validateRowRemoval { } # Method: addedRowNotify # # Override this method to be notified whenever # a new row is added # # Arguments: # # row - the new row to add # sub addedRowNotify { } # Method: deletedRowNotify # # Override this method to be notified whenever # a new row is deleted # # Arguments: # # row - hash ref containing fields and values of the deleted # row. The same structure as # return value # # force - boolean indicating whether the delete is forced or not # # sub deletedRowNotify { } # Method: movedUpRowNotify # # Override this method to be notified whenever # a row is moved up # # Arguments: # # row - containing fields and values of the moved row # sub movedUpRowNotify { } # Method: movedDownRowNotify # # Override this method to be notified whenever # a row is moved down # # Arguments: # # row - containing fields and values of the moved row # sub movedDownRowNotify { } # Method: updatedRowNotify # # Override this method to be notified whenever # a row is updated # # Arguments: # # row - row containing fields and values of the # updated row # # oldRow - row containing fields and values of the # updated row before modification # # force - boolean indicating whether the delete is forced or not # sub updatedRowNotify { } # Method: notifyForeignModelAction # # This method is used to let models know when other model has # taken an action. # # To be notified your table description must contain: # an entry 'notifyAction' => [ ModelName1, ModelName2] # where ModelName is the model you are interested of receiving # notifications. # # If you are interested on some action on a module you should # override this method to take the actions you need on response to # the foreign module action # # Parameters: # # (POSITIONAL) # # model - model name where the action took place # action - string represting the action: # [ add, del, edit, moveUp, moveDown ] # # row - row modified # # Returns: # # String - any i18ned String to inform the user about something that # has happened when the foreign model action was done in the current # model # sub notifyForeignModelAction { return ''; } # Method: addRow # # Add a new row. This method should be used only by CGIs # # Parameters: # # named parameters containing the expected fields for each row # # Returns: # # just added row's id # sub addRow { my ($self, %params) = @_; $self->validateRow('add', %params); my $userData = {}; foreach my $type (@{$self->table()->{'tableDescription'}}) { my $data = $type->clone(); $data->setMemValue(\%params); $userData->{$data->fieldName()} = $data; } return $self->addTypedRow($userData, readOnly => $params{'readOnly'}, id => $params{'id'}); } # Method: addTypedRow # # Add a row with the instanced types as parameter # # Parameters: # # params - hash ref containing subclasses from # with content indexed by field name # # readOnly - boolean indicating if the new row is read only or not # *(Optional)* Default value: false # # id - String the possible identifier to set *(Optional)* Default # value: undef # # Returns: # # String - the identifier for the given row # sub addTypedRow { my ($self, $paramsRef, %optParams) = @_; my $dir = $self->{'directory'}; my $confmod = $self->{'confmodule'}; my $readOnly = delete $optParams{'readOnly'}; my $id = delete $optParams{'id'}; unless (defined ($id) and length ($id) > 0) { $id = $self->_newId(); } my $row = EBox::Model::Row->new(dir => $dir, confmodule => $confmod); $row->setReadOnly($readOnly); $row->setModel($self); $row->setId($id); # Check compulsory fields $self->_checkCompulsoryFields($paramsRef); try { $self->_beginTransaction(); my $checkRowUnique = $self->rowUnique(); # Check field uniqueness if any my @userData = (); my $userData = {}; while ( my ($paramName, $param) = each (%{$paramsRef})) { # Check uniqueness if ($param->unique()) { # No need to check if the entire row is unique if # any of the fields are already checked $checkRowUnique = 0; $self->_checkFieldIsUnique($param); } push(@userData, $param); $row->addElement($param); } unless ($optParams{noValidateRow}) { $self->validateTypedRow('add', $paramsRef, $paramsRef); } # Check if the new row is unique, only if needed if ($checkRowUnique) { $self->_checkRowIsUnique(undef, $paramsRef); } my $hash = {}; foreach my $data (@userData) { $data->storeInHash($hash); $data = undef; } unless ($optParams{noOrder}) { # Insert the element in order if ($self->table()->{'order'}) { my $pos = 0; my $insertPos = $self->insertPosition(); if (defined($insertPos)) { if ( $insertPos eq 'front' ) { $pos = 0; } elsif ( $insertPos eq 'back' ) { $pos = $#{$self->order()} + 1; } } $self->_insertPos($id, $pos); } else { my $order = $confmod->get_list($self->{'order'}); push (@{$order}, $id); $confmod->set($self->{'order'}, $order); } } if ($readOnly) { $hash->{readOnly} = 1; } $confmod->set("$dir/$id", $hash); my $newRow = $self->row($id); $self->setMessage($self->message('add')); $self->addedRowNotify($newRow); $self->_notifyManager('add', $newRow); # check if there are files to delete if revoked my $filesToRemove = $self->filesPathsForRow($newRow); foreach my $file (@{ $filesToRemove }) { $self->{confmodule}->addFileToRemoveIfRevoked($file); } $self->_commitTransaction(); } otherwise { my $ex = shift; $self->_rollbackTransaction(); throw $ex; }; return $id; } # Method: row # # Return a given row # # Parameters: # # id - row id # # Returns: # # An object of # sub row { my ($self, $id) = @_; my $dir = $self->{'directory'}; my $confmod = $self->{'confmodule'}; my $row = EBox::Model::Row->new(dir => $dir, confmodule => $confmod); unless (defined($id) and $self->_rowExists($id)) { return undef; } $self->{'cacheOptions'} = {}; $row->setId($id); my $hash = $confmod->get_hash("$dir/$id"); $row->setReadOnly($hash->{'readOnly'}); $row->setModel($self); # If element is volatile we set its value after the rest # of the table elements are set, as it's typical to have # volatile values calculated from other values of the row my @volatileElements; foreach my $type (@{$self->table()->{'tableDescription'}}) { my $element = $type->clone(); if ($element->volatile()) { push (@volatileElements, $element); } else { _setRowElement($element, $row, $hash); } $row->addElement($element); } foreach my $element (@volatileElements) { _setRowElement($element, $row, $hash); } return $row; } sub _setRowElement { my ($element, $row, $hash) = @_; $element->setRow($row); $element->restoreFromHash($hash); if ((not defined($element->value())) and $element->defaultValue()) { $element->setValue($element->defaultValue()); } } # Method: isRowReadOnly # # Given a row it returns if it is read-only or not # # Parameters: # (POSITIONAL) # # id - row's id # # Returns: # # boolean - true if it is read-only, otherwise false # sub isRowReadOnly { my ($self, $id) = @_; my $row = $self->row($id); return undef unless ($row); return $row->{'readOnly'}; } sub _selectOptions { my ($self, $field) = @_; my $cached = $self->{'cacheOptions'}->{$field}; $self->{'cacheOptions'}->{$field} = $self->selectOptions($field); return $self->{'cacheOptions'}->{$field}; } sub moveUp { my ($self, $id) = @_; my %order = $self->_orderHash(); my $pos = $order{$id}; if ($order{$id} == 0) { return; } $self->_swapPos($pos, $pos - 1); $self->setMessage($self->message('moveUp')); $self->movedUpRowNotify($self->row($id)); $self->_notifyManager('moveUp', $self->row($id)); } sub moveDown { my ($self, $id) = @_; my %order = $self->_orderHash(); my $numOrder = keys %order; my $pos = $order{$id}; if ($order{$id} == $numOrder -1) { return; } $self->_swapPos($pos, $pos + 1); $self->setMessage($self->message('moveDown')); $self->movedDownRowNotify($self->row($id)); $self->_notifyManager('moveDown', $self->row($id)); } # Method: _removeRow # # Removes a row in the configuration backend, override it when removing # a row stored in other places # # Parameters: # # 'id' - row id # sub _removeRow { my ($self, $id) = @_; my $confmod = $self->{'confmodule'}; $confmod->unset("$self->{'directory'}/$id"); my @order = @{$confmod->get_list($self->{'order'})}; @order = grep ($_ ne $id, @order); $confmod->set_list($self->{'order'}, 'string', \@order); } # TODO Split into removeRow and removeRowForce # # Method: removeRow # # Remove a row # # Parameters: # # (POSITIONAL) # # 'id' - row id # 'force' - boolean to skip integrations checks of the row to remove # # Exceptions: # # - throw if any mandatory # argument is missing # sub removeRow { my ($self, $id, $force) = @_; unless (defined($id)) { throw EBox::Exceptions::MissingArgument( "Missing row identifier to remove") } try { $self->_beginTransaction(); # If force != true and automaticRemove is enabled it means # the model has to automatically check if the row which is # about to removed is referenced elsewhere. In that # case throw a DataInUse exceptions to iform the user about # the effects its actions will have. if ((not $force) and $self->table()->{'automaticRemove'}) { my $manager = EBox::Model::Manager->instance(); $manager->warnIfIdIsUsed($self->contextName(), $id); } $self->_checkRowExist($id, ''); my $row = $self->row($id); $self->validateRowRemoval($row, $force); # check if there are files to delete my $filesToRemove = $self->filesPathsForRow($row); foreach my $file (@{ $filesToRemove }) { $self->{confmodule}->addFileToRemoveIfCommitted($file); } $self->_removeRow($id); my $userMsg = $self->message('del'); # Dependant models may return some message to inform the user my $depModelMsg = $self->_notifyManager('del', $row); $self->_notifyManager('del', $row); if ( defined( $depModelMsg ) and $depModelMsg ne '' and $depModelMsg ne '

    ') { $userMsg .= "

    $depModelMsg"; } # If automaticRemove is enabled then remove all rows using referencing # this row in other models if ($self->table()->{'automaticRemove'}) { my $manager = EBox::Model::Manager->instance(); $depModelMsg = $manager->removeRowsUsingId($self->contextName(), $id); if ( defined( $depModelMsg ) and $depModelMsg ne '' and $depModelMsg ne '

    ') { $userMsg .= "

    $depModelMsg"; } } $self->setMessage($userMsg); $self->deletedRowNotify($row, $force); $self->_commitTransaction(); } otherwise { my $ex = shift; $self->_rollbackTransaction(); throw $ex; }; } # Method: removeAll # # Remove every data inside a model # # Parameters: # # force - boolean force the operation *(Optional)* Default value: false # sub removeAll { my ($self, $force) = @_; $force = 0 unless defined ($force); foreach my $id (@{$self->_ids(1)}) { $self->removeRow($id, $force); } } # Method: warnIfIdUsed # # This method must be overriden in case you want to warn the user # when a row is going to be deleted. Note that models manage this # situation automatically, this method is intended for situations # where the use of the model is done in a non-standard way. # # Override this method and raise a # excpetions to warn the user # # Parameters: # # (POSITIONAL) # # 'modelName' - String the observable model's name # 'id' - String row id # sub warnIfIdUsed { } # Method: warnOnChangeOnId # # This method must be overriden in case you want to advise the # eBox user about the change on a observable model. Note that # models manage this situation automatically if you are using # or types. This # method is intended to be used by models which use # 'notifyActions' attribute to be warned on other model's # change. # # Parameters: # # (NAMED) # # 'modelName' - String the observable model's name # # 'id' - String row id # # 'changeData' - hash ref of data types which are going to be # changed # # 'oldRow' - hash ref the same content as # using old row content # # Returns: # # A i18ned string explaining what happens if the requested action # takes place sub warnOnChangeOnId { } # Method: isIdUsed # # TODO # # (POSITIONAL) # # 'modelName' - model's name # 'id' - row id sub isIdUsed { } # Method: setRow # # Set an existing row. It should be used only by CGIs # # Parameters: # # named parameters containing the expected fields for each row sub setRow { my ($self, $force, %params) = @_; my $id = delete $params{'id'}; $self->_checkRowExist($id, ''); $self->validateRow('update', @_); # We can only set those types which have setters my @newValues = @{$self->setterTypes()}; my $changedData; for (my $i = 0; $i < @newValues ; $i++) { my $newData = $newValues[$i]->clone(); $newData->setMemValue(\%params); $changedData->{$newData->fieldName()} = $newData; } $self->setTypedRow( $id, $changedData, force => $force, readOnly => $params{'readOnly'}); } # Method: setTypedRow # # Set the values for a single existing row using typed parameters # # Parameters: # # id - String the row identifier # # paramsRef - hash ref Containing the parameter to set. You can # update your selected values. Indexed by field name. # # force - Boolean indicating if the update is forced or not # *(Optional)* Default value: false # # readOnly - Boolean indicating if the row becomes a read only # kind one *(Optional)* Default value: false # # - Optional parameters are NAMED # # Exceptions: # # - thrown if the update cannot be done # sub setTypedRow { my ($self, $id, $paramsRef, %optParams) = @_; my $force = delete $optParams{'force'}; my $readOnly = delete $optParams{'readOnly'}; $self->_checkRowExist($id, ''); my $dir = $self->{'directory'}; my $confmod = $self->{'confmodule'}; my @setterTypes = @{$self->setterTypes()}; try { $self->_beginTransaction(); my $checkRowUnique = $self->rowUnique(); my $row = $self->row($id); my $oldRow = $self->_cloneRow($row); my $allHashElements = $row->hashElements(); my $changedElements = {}; my @changedElements = (); foreach my $paramName (keys %{$paramsRef}) { unless ($paramName ne any(@setterTypes)) { throw EBox::Exceptions::Internal('Trying to update a non setter type'); } my $paramData = $paramsRef->{$paramName}; if ($row->elementByName($paramName)->isEqualTo($paramsRef->{$paramName})) { next; } if ($paramData->unique()) { # No need to check if the entire row is unique if # any of the fields are already checked $checkRowUnique = 0; $self->_checkFieldIsUnique($paramData); } $paramData->setRow($row); $changedElements->{$paramName} = $paramData; push (@changedElements, $paramData); $allHashElements->{$paramName} = $paramData; } # Check if the new row is unique if needed if ($checkRowUnique and (keys %{$paramsRef} > 0)) { $self->_checkRowIsUnique($id, $allHashElements); } # add ids parameters for call to validateTypedRow $changedElements->{id} = $id; $allHashElements->{id} = $id; $self->validateTypedRow('update', $changedElements, $allHashElements, $force); # remove ids after call to validateTypedRow delete $changedElements->{id}; delete $allHashElements->{id}; # If force != true automaticRemove is enabled it means # the model has to automatically check if the row which is # about to be changed is referenced elsewhere and this change # produces an inconsistent state if ((not $force) and $self->table()->{'automaticRemove'}) { my $manager = EBox::Model::Manager->instance(); $manager->warnOnChangeOnId($self->contextName(), $id, $changedElements, $oldRow); } my $key = "$dir/$id"; my $hash = $confmod->get_hash($key); my $modified = @changedElements; for my $data (@changedElements) { $data->storeInHash($hash); } # update readonly if change my $oldRO = $hash->{readOnly}; if (defined ($readOnly) and $readOnly) { $hash->{readOnly} = 1; } else { delete $hash->{readOnly}; } # Update row hash if needed if ($modified or ($hash->{readOnly} xor $oldRO)) { $confmod->set($key, $hash); } if ($modified) { $self->setMessage($self->message('update')); # Dependant models may return some message to inform the user my $depModelMsg = $self->_notifyManager('update', $row); if (defined ($depModelMsg) and ($depModelMsg ne '' and $depModelMsg ne '

    ')) { $self->setMessage($self->message('update') . '

    ' . $depModelMsg); } $self->_notifyManager('update', $row); $self->updatedRowNotify($row, $oldRow, $force); } $self->_commitTransaction(); } otherwise { my $ex = shift; $self->_rollbackTransaction(); throw $ex; }; } # Method: enabledRows # # Returns those row ids that are enabled, that is, those whose # field 'enabled' is set to true. If there is no enabled field, # all rows are returned. # # Returns: # # Array ref containing the row ids # sub enabledRows { my ($self) = @_; my $fields = $self->fields(); unless (grep { $_ eq 'enabled' } @{$fields}) { return $self->ids(); } my @rows = @{$self->ids()}; @rows = grep { $self->row($_)->valueByName('enabled') } @rows; return \@rows; } # Method: size # # Determine the size (in number of rows) from a model # # Returns: # # Int - the number of rows which the model contains # sub size { my ($self) = @_; return scalar(@{$self->ids()}); } # Method: syncRows # # This method might be useful to add or remove rows before they # are presented. In that case you must override this method. # # Warning: You should never call # within this function or you will enter into a deep recursion # # Parameters: # # (POSITIONAL) # # currentIds - array ref containing the current row indentifiers # # Returns: # # boolean - true if the current rows have been modified, i.e: there's # been a row addition or row removal # sub syncRows { my ($self, $currentIds) = @_; return 0; } # Method: ids # # # Return an array containing the identifiers of each table row. # The ids are ordered by the field specified by the model. # # This method will call # # Returns: # # array ref - containing the ids # sub ids { my ($self) = @_; my $currentIds = $self->_ids(); my $changed = 0; unless ($self->{'confmodule'}->isReadOnly()) { my $modAlreadyChanged = $self->{'confmodule'}->changed(); try { $self->_beginTransaction(); my $msgBeforeSyncRows = $self->message(); $changed = $self->syncRows($currentIds); if ($changed) { # restore any previous message, hiding any message caused by # sync Rows $self->setMessage($msgBeforeSyncRows); if (not $modAlreadyChanged) { # save changes but don't mark it as changed $self->{confmodule}->_saveConfig(); $self->{confmodule}->setAsChanged(0); } } $self->_commitTransaction(); } otherwise { my $ex = shift; $self->_rollbackTransaction(); throw $ex; }; } if ($changed) { return $self->_ids(); } else { return $currentIds; } } # Method: customFilterIds # # Return Ids filtered by the string that is passed # # You must enable the 'customFilter' property in your table description. # # When should I use this? # # Use this option when you have model where you override ids() and row(), # and the amount of rows you can potentially have is big > ~ 4000 entries. # # This is useful to speed up filter searches. If you don't this, the # automatic filter mechanism of models will be to slow. # # This is also useful when you want to take advantage of the search system # of your backend data. For example, if you are mapping data from an LDAP, # you can use this feature to carry out searches using the LDAP protocol. # # Parameters: # # filter string # # Returns: # # Array ref of ids sub customFilterIds { throw EBox::Exceptions::NotImplemented('customFilterIds'); } # Method: _ids # # (PROTECTED) # # Return an array containing the identifiers of each table row. # The ids are ordered by the field specified by the model # # Returns: # # array ref - containing the ids # sub _ids { my ($self, $notOrder) = @_; my $confmod = $self->{'confmodule'}; my $ids = $confmod->get_list($self->{'order'}); unless ($notOrder) { my $sortedBy = $self->sortedBy(); if (@{$ids} and $sortedBy) { my %idsToOrder; for my $id (@{$ids}) { $idsToOrder{$id} = $self->row($id)->printableValueByName($sortedBy); } $ids = [ sort {$idsToOrder{$a} cmp $idsToOrder{$b}} keys %idsToOrder]; my $global = EBox::Global->getInstance(); my $modChanged = $global->modIsChanged($confmod->name()); if (not $confmod->isReadOnly() and (@{$ids} and $modChanged)) { $confmod->set_list($self->{'order'}, 'string', $ids); } } } return $ids; } sub _rows { my ($self) = @_; my @rows = map { $self->row($_) } @{$self->_ids()}; return \@rows; } # Method: _tailoredOrder # # Function to be overriden by the subclasses in order to do # ordering in a different way as normal order is done. It's # functional if only if is set # to 0. # # Parameters: # # rows - an array ref with the hashes with the rows to order # # Returns: # # an array ref with the order from the current model with a # hash ref of every row # sub _tailoredOrder # (rows) { my ($self, $rows) = @_; # Sorted by sortedBy field element if it's given my $fieldName = $self->sortedBy(); if ( $fieldName ) { if ( $self->fieldHeader($fieldName) ) { my @sortedRows = sort { $a->elementByName($fieldName)->cmp($b->elementByName($fieldName)) } @{$rows}; return \@sortedRows; } } return $_[1]; } # Method: setTableName # # Use this method to set the current table name. This method # comes in handy to manage several tables with same model # # Parameters: # # tablename - string containing the name # sub setTableName { my ($self, $name) = @_; unless ($name) { throw Exceptions::MissingArgument('name'); } $self->{'tablename'} = $name; } # Method: setDirectory # # Use this method to set the current directory. This method # comes in handy to manage several tables with same model # # Parameters: # # directory - string containing the name # sub setDirectory { my ($self, $dir) = @_; unless ($dir) { throw EBox::Exceptions::MissingArgument('dir'); } my $olddir = $self->{'confdir'}; return if ($dir eq $olddir); $self->{'confdir'} = $dir; $self->{'directory'} = "$dir/keys"; $self->{'order'} = "$dir/order"; $self->{'table'}->{'confdir'} = $dir; } # Method: tableName # # Get the table name associated to this model # # Returns: # # String - containing the table name # sub tableName { my ($self) = @_; return $self->table()->{'tableName'}; } # Method: printableModelName # # Get the i18ned model name # # Returns: # # String - the localisated model name # sub printableModelName { my ($self) = @_; return $self->table()->{'printableTableName'}; } # Method: printableName # # Get the i18ned name # # Returns: # # What returns # sub printableName { my ($self) = @_; return $self->printableModelName(); } # Method: pageTitle # # Get the i18ned name of the page where the model is contained, if any # # Returns: # # string # sub pageTitle { my ($self) = @_; return $self->table()->{'pageTitle'}; } # Method: headTitle # # Get the i18ned name of the page where the model is contained, if any # # Returns: # # string # sub headTitle { my ($self) = @_; return $self->printableModelName(); } # Method: directory # # Get the current directory. This method is handy to manage # several tables with the same model # # Returns: # # String - Containing the directory # sub directory { my ($self) = @_; return $self->{'confdir'}; } # Method: menuNamespace # # Fetch the menu namespace which this model belongs to # # Returns: # # String - Containing namespace # sub menuNamespace { my ($self) = @_; if (exists $self->table()->{'menuNamespace'}) { return $self->table()->{'menuNamespace'}; } elsif ( defined ( $self->modelDomain() )) { # This is autogenerated menuNamespace got from the model # domain and the table name my $menuNamespace = $self->modelDomain() . '/View/' . $self->tableName(); return $menuNamespace; } else { return undef; } } # Method: order # # Get the keys order in an array ref # # Returns: # # array ref - the key order where each element is the key # identifier # sub order { my ($self) = @_; return $self->{'confmodule'}->get_list( $self->{'order'} ); } # Method: insertPosition # # Get the insert order position. It makes sense only if the table # is ordered, that is, the order field is set. # # Default value: front # # Returns: # # 'back' - if the element is inserted at the end of the model # # 'front' - so the element is inserted at the beginning of the model # sub insertPosition { my ($self) = @_; return $self->table()->{'insertPosition'}; } # Method: rowUnique # # Get if the model must have each row different # # Returns: # # true - if each row is unique # false - otherwise # sub rowUnique { my ($self) = @_; return $self->table()->{'rowUnique'}; } # Method: action # # Accessor to the URLs where the actions are published to be # run. # # Parameters: # # actionName - String the action name # # Returns: # # String - URL where the action will be called # # Exceptions: # # - thrown if the action name # has not defined action # sub action { my ($self, $actionName) = @_; my $actionsRef = $self->table()->{actions}; if ( exists ($actionsRef->{$actionName}) ){ return $actionsRef->{$actionName}; } else { throw EBox::Exceptions::DataNotFound( data => __('Action'), value => $actionName); } } sub _setupCustomActions { my ($self, $id) = @_; my $customActions = $self->{'table'}->{'customActions'}; if ($customActions) { # Store the custom actions in a list to access in order my @customActionsList = map { $_->action($id) } @{ $customActions }; # Store the custom actions in a hash to access by name my %customActionsHash = map { $_->name() => $_ } @customActionsList; $self->{'customActionsList'} = \@customActionsList; $self->{'customActionsHash'} = \%customActionsHash; $self->_setCustomMessages(\@customActionsList, $id); } } # Method: customActions # # Obtains the definition of the custom actions # # Returns: # # Array ref - List of # sub customActions { my ($self, $action, $id) = @_; $self->_setupCustomActions($id); if ($action) { return undef unless $self->{table}->{customActions}; return $self->{customActionsHash}->{$action}; } else { return [] unless $self->{table}->{customActions}; return $self->{customActionsList}; } } # Method: printableRowName # # Get the printable row name # # Returns: # # String - containing the i18n name for the row # sub printableRowName { my ($self) = @_; return $self->table()->{'printableRowName'}; } # Method: help # # Get the help message from the model # # Returns: # # String - containing the i18n help message # sub help { my ($self) = @_; return $self->table()->{'help'}; } # Method: message # # Get a message depending on the action parameter # # Current actions are: # # add - when a row is added # del - when a row is deleted # update - when a row is updated # moveUp - when a row is moved up # moveDown - when a row is moved down # # Parameters: # # action - String the action from where to get the message. There # are one default message per action. If the action is undef # returns the current message to show. *(Optional)* Default value: # undef # # Returns: # # String - the message to show # sub message { my ($self, $action) = @_; if ( defined ( $action ) ) { return $self->table()->{'messages'}->{$action}; } else { return $self->table()->{'message'}; } } sub messageClass { my ($self, $action) = @_; return $self->table()->{'messageClass'} or 'note'; } # Method: popMessage # # Get the message to show and *delete* it afterwards. # # Returns: # # String - the message to show # sub popMessage { my ($self) = @_; my $msg = $self->message(); $self->setMessage(''); return $msg; } # Method: setMessage # # Set the message to show the user # # Parameters: # # newMessage - String the new message to show # sub setMessage { my ($self, $newMessage, $messageClass) = @_; $self->table()->{'message'} = $newMessage; $self->table()->{'messageClass'} = $messageClass if ($messageClass); } # Method: modelDomain # # Get the domain where the model is handled. That is, the eBox # module which the model belongs to # # Returns: # # String - the model domain, the first letter is upper-case # sub modelDomain { my ($self) = @_; return $self->{'table'}->{'modelDomain'}; } # Method: fields # # Return a list containing the fields which compose each row # # Returns: # # Array ref containing the fields sub fields { my ($self) = @_; if ($self->{'fields'}) { return $self->{'fields'}; } unless (defined($self->table()->{'tableDescription'})) { throw EBox::Exceptions::Internal('Table description not defined'); } my @tableHead = @{$self->table()->{'tableDescription'}}; my @tableFields = map { $_->{'fieldName'} } @tableHead; $self->{'fields'} = \@tableFields; return \@tableFields; } # Method: fieldsWithUndefSetter # # Return a hash containing the fields which compose each row # and dont have a defined Setter # # Returns: # # Hash ref containing the field names as keys # sub fieldsWithUndefSetter { my ($self) = @_; unless (defined($self->table()->{'tableDescription'})) { throw Excepetions::Internal('table description not defined'); } my @tableHead = @{$self->table()->{'tableDescription'}}; my %tableFields; for my $type (@tableHead) { $tableFields{$type->fieldName()} = 1 unless $type->HTMLSetter(); } return \%tableFields; } # Method: setterTypes # # Return a list containing those fields which have defined setters # # Returns: # # Array ref containing the fields sub setterTypes { my ($self) = @_ ; unless (defined($self->table()->{'tableDescription'})) { throw Exceptions::Internal('table description not defined'); } my @tableHead = @{$self->table()->{'tableDescription'}}; my @types = grep { defined($_->HTMLSetter) } @tableHead; return \@types; } # Method: setFilter # # Set the the string used to filter the return of rows # # Parameters: # (POSITIONAL) # filter - string containing the filter # sub setFilter { my ($self, $filter) = @_; $self->{'filter'} = $filter; } # Method: filter # # Return the string used to filter the return of rows # # Returns: # # string - containing the value sub filter { my ($self) = @_; return $self->{'filter'}; } # Method: find # # Return the first row which matches the value of the given # field against the data returned by the method printableValue() # # If you want to match against value use # # # Parameters: # # fieldName => value # # Example: # # find('default' => 1); # # Returns: # # - The matched row # # undef - if there was not any match # # Exceptions: # # # sub find { my ($self, $fieldName, $value) = @_; unless (defined ($fieldName)) { throw EBox::Exceptions::MissingArgument("Missing field name"); } my @matched = @{$self->_find($fieldName, $value, undef, 'printableValue')}; if (@matched) { return $self->row($matched[0]); } else { return undef; } } # Method: findAll # # Return all the id rows that match the value of the given # field against the data returned by the method printableValue() # # If you want to match against value use # # # Parameters: # # fieldName => value # # Example: # # find('default' => 1); # # Returns: # # Array ref of ids which reference to the matched # rows () # # Exceptions: # # sub findAll { my ($self, $fieldName, $value) = @_; unless (defined ($fieldName)) { throw EBox::Exceptions::MissingArgument("Missing field name"); } my @matched = @{$self->_find($fieldName, $value, 1, 'printableValue')}; return \@matched; } # Method: findValue # # Return the first row that matches the value of the given # field against the data returned by the method value() # # If you want to match against printable value use # # Parameters: # # fieldName => value # # Example: # # find('default' => 1); # # Returns: # # - the matched row # # undef if there was not any match # # Exceptions: # # # sub findValue { my ($self, $fieldName, $value) = @_; unless (defined ($fieldName)) { throw EBox::Exceptions::MissingArgument("Missing field name"); } my @matched = @{$self->_find($fieldName, $value, undef, 'value')}; if (@matched) { return $self->row($matched[0]); } else { return undef; } } # Method: findAllValue # # Return all the rows that match the value of the given # field against the data returned by the method value() # # If you want to match against value use # # # # Parameters: # # fieldName => value # # Example: # # find('default' => 1); # # Returns: # # An array ref of ids that reference matched rows # () # # # Exceptions: # # # sub findAllValue { my ($self, $fieldName, $value) = @_; unless (defined ($fieldName)) { throw EBox::Exceptions::MissingArgument("Missing field name"); } my @matched = @{$self->_find($fieldName, $value, 1, 'value')}; return \@matched; } # Method: findId # # Return the first row identifier which matches the value of the # given field against the data returned by the method value() or # the method printableValue() # # Parameters: # # fieldName => value # # Example: # # findId('default' => 1); # # Returns: # # String - the row identifier from the first matched rule # # undef - if there was not any match # # Exceptions: # # # sub findId { my ($self, $fieldName, $value) = @_; unless (defined ($fieldName)) { throw EBox::Exceptions::MissingArgument("Missing field name"); } my @matched = @{$self->_find($fieldName, $value, undef, 'value')}; if (@matched) { return $matched[0]; } else { @matched = @{$self->_find($fieldName, $value, undef, 'printableValue')}; return @matched ? $matched[0] : undef; } } # Method: findRow # # Return the first row that matches the value of the given field # against the data returned by the method printableValue() or # method value() # # Parameters: # # fieldName => value # # Example: # # findRow('default' => 1); # # Returns: # # - the row from the first matched rule # # undef - if there was not any match # # Exceptions: # # # sub findRow { my ($self, $fieldName, $value) = @_; unless (defined($fieldName)) { throw EBox::Exceptions::MissingArgument("Missing field name"); } my $id = $self->findId($fieldName, $value); if ( defined($id) ) { return $self->row($id); } else { return undef; } } # Method: _HTTPUrlView # # Returns the HTTP URL base used to get the view for this model # sub _HTTPUrlView { my ($self) = @_; return $self->table()->{'HTTPUrlView'}; } # Method: HTTPLink # # The HTTP URL base + directory parameter to get the view for this # model # # Returns: # # String - the URL to link # # '' - if the _HTTPUrlView is not defined to a non-zero string # sub HTTPLink { my ($self) = @_; if ( $self->_HTTPUrlView() ) { my $link = '/' . $self->_HTTPUrlView(); my $parentRow = $self->parentRow(); if ($parentRow) { $link .= '?directory=' . $self->directory(); } return $link; } else { return ""; } } sub DESTROY { ; } # Method: AUTOLOAD # # Autoload function called whenever a method is undefined for # this class. # # We use it to generate an automatic add/del/set/get methods to # data models. The methods will follow these patterns: # # - Addition # # - add[]( property1 => value1, # property2 => value2,.. ) # # - addTo( indexValue, property1 => value1, property2 => # value2,.. ) # # - addToTo ( # indexValue, indexSubModel1, property1 => value1, property2 => # value2,.. ) # # - Removal # # - del[]( indexValue ) # # - delTo( indexValue, # indexSubModel1 ); # # - delToTo( indexValue, # indexSubModel1, indexSubModel2 ); # # - Access # # - get[]( indexValue[, [ field1, field2, ... ]]); # # - getTo( indexValue, # indexSubModel1[, [ field1, field2, ... ]]); # # - getToTo( indexValue, # indexSubModel1, indexSubModel2[, [ field1, field2, ... ]]); # # All methods return the same data as # method does except if one # field is requested when just one type is returned. In order # to make queries about multiple rows, use # , # methods or similars. # # - Update # # - set[] ( indexValue, property1 => value1, # property2 => value2, ... ); # # - setTo( indexValue, # indexSubModel1, property1 => value1, property2 => value2, # ...); # # - setToTo( # indexValue, indexSubModel1, indexSubModel2, property1 => # value1, property2 => value2, ...); # # The indexes are unique fields from the data models. If there is # none, the identifier may be used. The values can be multiple # using array references. # # Returns: # # String - the newly added row identifier if the AUTOLOAD method is # an addition # # - if the AUTOLOAD method is a getter and it # returns a single row # # - if the AUTOLOAD method is a getter and # it returns a single field from a row # # Array ref - if the AUTOLOAD method is a getter and it returns # more than one row. Each component is a . # # Exceptions: # # - thrown if no valid pattern was # used # # - thrown if any compulsory # argument is missing # sub AUTOLOAD { my ($self, @params) = @_; my $methodName = our $AUTOLOAD; $methodName =~ s/.*:://; unless ( UNIVERSAL::can($self, '_autoloadAdd') ) { use Devel::StackTrace; my $trace = new Devel::StackTrace(); EBox::debug($trace->as_string()); throw EBox::Exceptions::Internal("Not valid autoload method $methodName since " . "$self is not a EBox::Model::DataTable"); } # Depending on the method name beginning, the action to be # performed is selected if ( $methodName =~ m/^add/ ) { return $self->_autoloadAdd($methodName, \@params); } elsif ( $methodName =~ m/^del/ ) { return $self->_autoloadDel($methodName, \@params); } elsif ( $methodName =~ m/^get/ ) { return $self->_autoloadGet($methodName, \@params); } elsif ( $methodName =~ m/^set/ ) { return $self->_autoloadSet($methodName, \@params); } else { use Devel::StackTrace; my $trace = new Devel::StackTrace(); EBox::debug($trace->as_string()); throw EBox::Exceptions::Internal("Not valid autoload method $methodName for " . ref($self) . ' class'); } } # Method: Viewer # # Method to return the viewer from this model. This method # can be overriden # # Returns: # # String - the path to the Mason template which acts as the # viewer from this kind of model. # sub Viewer { return '/ajax/tableBody.mas'; } sub modalViewer { my ($self, $showTable) = @_; if ($showTable) { return '/ajax/tableModalView.mas'; } else { return '/ajax/tableModal.mas'; } } # Method: automaticRemoveMsg # # Get the i18ned string to show when an automatic remove is done # in a model # # Parameters: # # nDeletedRows - Int the deleted row number # sub automaticRemoveMsg { my ($self, $nDeletedRows) = @_; return __x('Remove {num} rows of {rowName} from {model}{br}', num => $nDeletedRows, rowName => $self->printableRowName(), model => $self->printableContextName(), br => '
    '); } # Method: pageSize # # Return the number of rows per page # # Returns: # # int - page size sub pageSize { my ($self) = @_; # if the user has selected a page size return it if (exists $self->{'pageSize'} ) { return $self->{'pageSize'}; } return $self->defaultPageSize(); } # Method: defaultPageSize # # Return the default number of rows per page. This value must be defined in # the table description. If it is not defined it defaults to 10 # # Returns: # # int - default page size sub defaultPageSize { my ($self) = @_; my $table = $self->table(); if (exists $table->{'pageSize'} ) { return $table->{'pageSize'}; } # fallback to defautl value of 10 return 10; } # Method: setPageSize # # set the number of rows per page # # Parameters: # # rows - number of rows per page # # Returns: # # int - page size sub setPageSize { my ($self, $rows) = @_; unless (defined ($rows)) { throw EBox::Exceptions::MissingArgument("Missing field rows"); } if ($rows < 0) { throw EBox::Exceptions::InvalidData( data => __('Page size'), value => $rows, advice => __('Must be either a positive number or zero') ) } $self->{'pageSize'} = $rows; } # Method: changeViewJS # # Return the javascript function to change view to # add a row # # Parameters: # # (NAMED) # changeType - changeAdd or changeList # editId - edit id # page - page number # isFilter - boolean indicating if comes from filtering # # # Returns: # # string - holding a javascript funcion sub changeViewJS { my ($self, %args) = @_; my ($type, $editId, $page, $isFilter) = ($args{changeType}, $args{editId}, $args{page}, $args{isFilter}, ); my $function = "changeView('%s','%s','%s','%s','%s', %s, %s)"; my $table = $self->table(); return sprintf ($function, $table->{'actions'}->{'changeView'}, $table->{'tableName'}, $table->{'confdir'}, $type, $editId, $page, $isFilter); } # Method: modalChangeViewJS # # Return the javascript function to change view # # Parameters: # # (NAMED) # changeType - changeAdd or changeList # editId - edit id # page - page number # isFilter - boolean indicating if comes from filtering # # # Returns: # # string - holding a javascript funcion sub modalChangeViewJS { my ($self, %args) = @_; my $actionType = delete $args{changeType}; my $editId = delete $args{editId}; if (not $args{title}) { $args{title} = __x('New {name}', name => $self->printableRowName() ); } my $extraParamsJS = _paramsToJSON(%args); my $function = "modalChangeView('%s','%s','%s','%s','%s', %s)"; my $table = $self->table(); my $url = $table->{'actions'}->{'changeView'}; # url $url =~ s/Controller/ModalController/; my $tableId = $table->{'tableName'} . '_modal'; my $js = sprintf ($function, $url, $tableId, $table->{'confdir'}, $actionType, $editId, $extraParamsJS, ); return $js; } sub modalCancelAddJS { my ($self, %params) = @_; my $table = $self->table(); my $url = $table->{'actions'}->{'changeView'}; # url $url =~ s/Controller/ModalController/; my $directory = $self->directory(); my $params = "action=cancelAdd&directory=$directory"; my $selectCallerId = $params{selectCallerId}; my $onSuccess=''; if ($selectCallerId) { $onSuccess = "function(t) { var json = t.responseText.evalJSON(true); if (json.success) { removeSelectChoice('$selectCallerId', json.rowId, 2) } }"; } my $js = "new Ajax.Request('$url', { method: 'post', parameters: '$params'"; if ($onSuccess) { $js .= ", onSuccess: $onSuccess"; } $js.= '});'; return $js; } # Method: addNewRowJS # # Return the javascript function for addNewRow # # Parameters: # # (POSITIONAL) # page - page number # # Returns: # # string - holding a javascript funcion sub addNewRowJS { my ($self, $page, %params) = @_; my $cloneId = $params{cloneId}; my $function = "addNewRow('%s','%s',%s,'%s',%s)"; my $table = $self->table(); my @extraFields; push @extraFields, 'cloneId' if $cloneId; my $fields = $self->_paramsWithSetterJS(@extraFields); return sprintf ($function, $table->{'actions'}->{'add'}, $table->{'tableName'}, $fields, $table->{'confdir'}, $page); } sub modalAddNewRowJS { my ($self, $page, $nextPage, @extraParams) = @_; $nextPage or $nextPage = ''; my $function = "modalAddNewRow('%s','%s',%s,'%s', '%s', %s)"; my $table = $self->table(); my $url = $table->{'actions'}->{'add'}; if (not $nextPage) { $url =~ s/Controller/ModalController/; } my $extraParamsJS = _paramsToJSON(@extraParams); my $tableId = $table->{'tableName'} . '_modal'; my $fields = $self->_paramsWithSetterJS(); return sprintf ($function, $url, $tableId, $fields, $table->{'confdir'}, $nextPage, $extraParamsJS); } # Method: changeRowJS # # Return the javascript function for changeRow # # Parameters: # # (POSITIONAL) # editId - row id to edit # page - page number # # Returns: # # string - holding a javascript funcion sub changeRowJS { my ($self, $editId, $page, $modal, @extraParams) = @_; my $function = "changeRow('%s','%s',%s,'%s','%s',%s, %s, %s, %s)"; my $table = $self->table(); my $tablename = $table->{'tableName'}; my $actionUrl = $table->{'actions'}->{'editField'}; my $modalResize = 0; if ($modal) { $tablename .= '_modal'; $actionUrl =~ s/Controller/ModalController/; $modalResize = 1; } my $force =0; my $extraParamsJS = _paramsToJSON(@extraParams); my $fields = $self->_paramsWithSetterJS(); return sprintf ($function, $actionUrl, $tablename, $fields, $table->{'confdir'}, $editId, $page, $force, $modalResize, $extraParamsJS); } sub _paramsToJSON { my (%params) = @_; my $paramString = '{'; while (my ($name, $value) = each %params) { $paramString .= "'$name'" . ': ' . "'$value'" . ', '; } $paramString .= '}'; return $paramString; } # Method: actionClicked # # Return the javascript function for actionClicked # # Parameters: # # (POSITIONAL) # action - move or del # editId - row id to edit # direction - up or down # page - page number # # Returns: # # string - holding a javascript funcion sub actionClickedJS { my ($self, $action, $editId, $direction, $page, $modal, @extraParams) = @_; unless (($action eq 'move') or ($action eq 'del') or ($action eq 'clone')) { throw EBox::Exceptions::External("Wrong action $action"); } if ($action eq 'move' and not ($direction eq 'up' or $direction eq 'down')) { throw EBox::Exceptions::External("Wrong action $direction"); } my $function = "actionClicked('%s','%s','%s','%s','%s','%s',%s, %s)"; if ($direction) { $direction = "dir=$direction"; } else { $direction = ""; } my $table = $self->table(); my $actionUrl = $table->{'actions'}->{$action}; my $tablename = $table->{'tableName'}; if ($modal) { $actionUrl =~ s/Controller/ModalController/; $tablename .= '_modal'; } my $extraParamsJS = _paramsToJSON(@extraParams); my $fields = $self->_paramsWithSetterJS(); return sprintf ($function, $actionUrl, $tablename, $action, $editId, $direction, $table->{'confdir'}, $page, $extraParamsJS); } sub actionHandlerUrl { my ($self) = @_; return $self->_mainController(); } # Method: customActionClickedJS # # Return the javascript function for customActionClicked # # Parameters: # # TODO # # Returns: # # string - holding a javascript funcion sub customActionClickedJS { my ($self, $action, $id, $page) = @_; unless ( $self->customActions($action, $id) ) { throw EBox::Exceptions::Internal("Wrong custom action $action"); } my $function = "customActionClicked('%s','%s','%s',%s,'%s','%s',%s)"; my $table = $self->table(); my $fields = $self->_paramsWithSetterJS(); $page = 0 unless $page; return sprintf ($function, $action, $self->actionHandlerUrl(), $table->{'tableName'}, $fields, $table->{'confdir'}, $id, $page); } # Method: backupFiles # # Make an actual configuration backup of all the files contained in the # datatable and its submodels. This backup will used to discard changes if # needed sub backupFiles { my ($self) = @_; # XXX Disable backupFiles as this is messing with the directories # and making eBox fail return; $self->_hasFileFields() or return; foreach my $id (@{ $self->ids() }) { $self->row($id)->backupFiles(); } } # Method: restoreFiles # # Restores the actual configuration backup of files, thus discarding last # changes in files sub restoreFiles { my ($self) = @_; # FIXME: Is this no longer needed? # XXX Disable restoreFiles as this is messing with the directories # and making eBox fail return; $self->_hasFileFields() or return; foreach my $row (@{ $self->ids() } ) { $self->row($row)->restoreFiles(); } } # Method: _hasFileFields # # Returns: # wether the types in the tableDescription could manage any file sub _hasFileFields { my ($self) = @_; my $tableDesc = $self->table()->{tableDescription}; foreach my $header ( @{ $tableDesc } ) { if ($header->can('filesPaths')) { return 1; } } return 0; } # Method: reloadTable # # This method is intended to reload the information from the table # description. It is useful when the table description may change # on the fly due to some state # # Returns: # # - the same info that # returned value # sub reloadTable { my ($self) = @_; undef $self->{'table'}; return $self->table(); } # Group: Protected methods # Method: _prepareRow # # Returns a new row instance with all its elements cloned # and ready to be set # sub _prepareRow { my ($self) = @_; my $row = EBox::Model::Row->new(dir => $self->directory(), confmodule => $self->{confmodule}); $row->setModel($self); foreach my $type (@{$self->table()->{'tableDescription'}}) { my $data = $type->clone(); $row->addElement($data); } return $row; } # Method: _cloneRow # # Returns a new row instance with all its elements cloned # from the given row # sub _cloneRow { my ($self, $other) = @_; my $row = EBox::Model::Row->new(dir => $self->directory(), confmodule => $self->{confmodule}); $row->setModel($self); foreach my $type (@{$self->table()->{'tableDescription'}}) { my $element = $other->elementByName($type->{fieldName}); my $newElement = $element->clone(); $row->addElement($newElement); } return $row; } # Method: _setValueRow # # Returns a new row instance with all its elements cloned # and set to the passed value. # # Parameters: # # (NAMED) # # Hash containing field names as keys, and values that will # be passed to setValue for every element. # sub _setValueRow { my ($self, %values) = @_; my $row = $self->_prepareRow(); while (my ($key, $value) = each %values) { $row->elementByName($key)->setValue($value); } return $row; } # Method: _setDefaultMessages # # Set the default messages done by possible actions # sub _setDefaultMessages { my ($self) = @_; # Table is already defined my $table = $self->{'table'}; $table->{'messages'} = {} unless ( $table->{'messages'} ); my $rowName = $self->printableRowName(); my %defaultMessages = ( 'add' => __x('{row} added', row => $rowName), 'del' => __x('{row} deleted', row => $rowName), 'update' => __x('{row} updated', row => $rowName), 'moveUp' => __x('{row} moved up', row => $rowName), 'moveDown' => __x('{row} moved down', row => $rowName), ); foreach my $action (keys (%defaultMessages)) { unless ( exists $table->{'messages'}->{$action} ) { $table->{'messages'}->{$action} = $defaultMessages{$action}; } } } # Method: _setCustomMessages # # Set the custom messages based on possibel custom actions # sub _setCustomMessages { my ($self, $actions, $id) = @_; my $table = $self->{'table'}; $table->{'messages'} = {} unless ( $table->{'messages'} ); for my $customAction ( @{$actions} ) { my $action = $customAction->name($id); my $message = $customAction->message($id); $table->{messages}->{$action} = $message; } } # Method: _volatile # # Check if this model is volatile. That is, the data is not # stored in disk but it is done by the storer and restored by # the acquirer. Every type must be volatile in order to have a # model as volatile # # Returns: # # Boolean - indicating if the table is volatile or not # sub _volatile { my ($self) = @_; return $self->{'volatile'}; } # Group: Private helper functions # Method: _find # # (PRIVATE) # # Used by find and findAll to find rows in a table # # Parameters: # # (POSITIONAL) # # fieldName - the name of the field to match # value - value we want to match # allMatches - 1 or undef to tell the method to return just the # first match or all of them # # kind - *(Optional)* String if 'printableValue' match against # printableValue, if 'value' against value # Default value: 'value' # # nosync - *(Optional)* don't call to syncRows to avoid recursion # # Example: # # _find('default', 1, undef, 'printableValue'); # # Returns: # # An array of ids which references those rows that match with the # given filter # sub _find { my ($self, $fieldName, $value, $allMatches, $kind, $nosync) = @_; unless (defined ($fieldName)) { throw EBox::Exceptions::MissingArgument("Missing field name"); } my $conf = $self->{confmodule}; $kind = 'value' unless defined ($kind); my @rows = @{$nosync ? $self->_ids(1) : $self->ids()}; my @matched; foreach my $id (@rows) { my $row = $self->row($id); my $element = $row->elementByName($fieldName); if (defined ($element)) { my $eValue; if ($kind eq 'printableValue') { $eValue = $element->printableValue(); } else { $eValue = $element->value(); } if ((defined $eValue) and ($eValue eq $value)) { if ($allMatches) { push (@matched, $id); } else { return [ $id ]; } } } } return \@matched; } sub _checkFieldIsUnique { my ($self, $newData) = @_; if ($newData->optional() and not defined($newData->value())) { return 0; } my $printableValue = $newData->printableValue(); my @matched = @{$self->_find($newData->fieldName(), $printableValue, undef, 'printableValue', 1)}; if (@matched) { throw EBox::Exceptions::DataExists( 'data' => $newData->printableName(), 'value' => $printableValue, ); } return 0; } # Check the new row to add/set is unique, it ignores enabled parameter # is any # rowId can be undef if the call comes from an addition # A hash ref of types is passing in # throw if not unique sub _checkRowIsUnique # (rowId, row_ref) { my ($self, $rowId, $row_ref) = @_; # Call _rows instead of rows because of deep recursion my $rows = $self->_rows(); my $fields = $self->fields(); # Exclude 'enabled' field if isEnablePropertySet if ( $self->isEnablePropertySet() ) { my @fieldsWithoutEnabled = grep { $_ ne 'enabled' } @{$fields}; $fields = \@fieldsWithoutEnabled; } foreach my $id (@{$self->_ids()}) { my $row = $self->row($id); next unless ( defined($row) ); # Compare if the row identifier is different next if ( defined($rowId) and $row->{'id'} eq $rowId); my $nEqual = 0; foreach my $fieldName (@{$fields}) { if ( defined($row_ref->{$fieldName}) ) { if ( $row_ref->{$fieldName}->isEqualTo($row->elementByName($fieldName)) ) { $nEqual++; } } # If not defined, then the field is optional and the comparation here is useless else { $nEqual++; } } next unless ( $nEqual == scalar(@{$fields}) ); throw EBox::Exceptions::DataExists( 'data' => $self->printableRowName(), 'value' => '' ); } } # FIXME: Deprecated? sub _checkAllFieldsExist { my ($self, $params) = @_; my $types = $self->table()->{'tableDescription'}; foreach my $field (@{$types}) { unless ($field->paramExist($params)) { throw Exceptions::MissingArgument($field->printableName()); } } } # Method to check if compulsory are given when adding sub _checkCompulsoryFields { my ($self, $paramsRef) = @_; my @compulsoryFields = @{$self->_compulsoryFields()}; foreach my $compulsoryField (@compulsoryFields) { my $found = 0; foreach my $userField (keys %{$paramsRef}) { $found = $userField eq $compulsoryField; last if ( $found ); } unless ( $found ) { my $missingField = $self->fieldHeader($compulsoryField); throw EBox::Exceptions::DataMissing(data => $missingField->printableName()); } } } # Gives back the compulsory field names sub _compulsoryFields { my ($self) = @_; my @compulsory = (); foreach my $fieldName (@{$self->fields()}) { my $field = $self->fieldHeader($fieldName); unless ($field->optional() or $field->hidden()) { push (@compulsory, $fieldName); } } return \@compulsory; } sub _checkRowExist { my ($self, $id, $text) = @_; unless ($self->_rowExists($id)) { throw EBox::Exceptions::DataNotFound( data => $text, value => $id); } } sub _rowExists { my ($self, $id) = @_; # TODO: is it worth implementing this with redis ZSET to get O(1) # and easy ordering ? foreach my $row (@{$self->_ids(1)}) { if ($row eq $id) { return 1; } } return 0; } sub _newId { my ($self) = @_; my $model = $self->modelName(); my $leadingText = lc ($model); my $firstLetter = substr ($leadingText, 0, 1); my $rest = substr ($leadingText, 1, length ($leadingText) - 1); $rest =~ tr/aeiou//d; $leadingText = $firstLetter . $rest; $leadingText = substr($leadingText, 0, length ($leadingText) / 2); my $id = 1; my $maxId = $self->{confmodule}->get("$model/max_id"); if ($maxId) { $id = $maxId + 1; } $self->{confmodule}->set("$model/max_id", $id); return $leadingText . $id; } # Insert the id element in selected position, if the position is the # last + 1 is inserted after the last one sub _insertPos #(id, position) { my ($self, $id, $pos) = @_; my $confmod = $self->{'confmodule'}; my @order = @{$confmod->get_list($self->{'order'})}; if (@order == 0) { push (@order, $id); } elsif ($pos == 0) { @order = ($id, @order); } elsif ($pos == @order) { push (@order, $id); } else { splice (@order, $pos, 1, ($id, $order[$pos])); } $confmod->set_list($self->{'order'}, 'string', \@order); } sub _swapPos { my ($self, $posA, $posB ) = @_; my $confmod = $self->{'confmodule'}; my @order = @{$confmod->get_list($self->{'order'})}; my $temp = $order[$posA]; $order[$posA] = $order[$posB]; $order[$posB] = $temp; $confmod->set_list($self->{'order'}, 'string', \@order); } sub _orderHash { my $self = shift; my $confmod = $self->{'confmodule'}; my %order; if ($self->table()->{'order'}) { my @order = @{$confmod->get_list($self->{'order'})}; my $i = 0; foreach my $id (@order) { $order{$id} = $i; $i++; } } return %order; } sub _rowOrder { my ($self, $id) = @_; unless (defined($id)) { return; } my %order = $self->_orderHash(); return $order{$id}; } # Method: _notifyManager # # Notify to the model manager that an action has been performed on # this model # sub _notifyManager { my ($self, $action, $row) = @_; my $manager = EBox::Model::Manager->instance(); my $contextName = $self->contextName(); # remove begining and trailing '/' for context name $contextName =~ s{^/}{}; $contextName =~ s{/$}{}; return $manager->modelActionTaken($contextName, $action, $row); } sub _filterRows { my ($self, $rows, $filter, $page) = @_; # Filter using regExp my @newRows; if (defined($filter) and length($filter) > 0) { my @words = split (/\s+/, $filter); my $totalWords = scalar(@words); for my $row (@{$rows}) { my $nwords = $totalWords; my %wordFound; for my $element (@{$row->elements()}) { my $printableVal = $element->printableValue(); next unless defined($printableVal); my $rowFound; for my $regExp (@words) { if (not exists $wordFound{$regExp} and $printableVal =~ /$regExp/) { $nwords--; $wordFound{$regExp} = 1; unless ($nwords) { push(@newRows, $row); $rowFound = 1; last; } } } last if $rowFound; } } } else { @newRows = @{$rows}; } # Paging unless (defined($page) and $self->pageSize()) { return \@newRows; } my $pageSize = $self->pageSize(); my $tpages; if (@newRows == 0) { $tpages = 0; } else { $tpages = ceil(@newRows / $pageSize) - 1; } if ($page < 0) { $page = 0; } if ($page > $tpages) { $page = $tpages; } my $index; if ($tpages > 0 and defined($pageSize) and $pageSize > 0) { $index = $page * $pageSize; } else { $index = 0; $pageSize = @{$rows} - 1; } my $offset = $index + $pageSize; if ($page == $tpages) { $offset = @newRows; } if ($tpages > 0) { return [@newRows[$index .. ($offset - 1)]]; } else { return \@newRows; } } sub _mainController { my ($self) = @_; my $table = $self->{'table'}; my $defAction = $table->{'defaultController'}; if ( (not defined ( $defAction )) and defined ( $self->modelDomain() )) { # If it is not a defaultController, we try to guess it from # the model domain and its name $defAction = '/' . $self->modelDomain() . '/Controller/' . $self->{'table'}->{'tableName'}; } return $defAction; } # Set the default controller to that actions which do not have a # custom controller sub _setControllers { my ($self) = @_; # Table is already defined my $table = $self->{'table'}; my $defAction = $self->_mainController(); if ($defAction) { foreach my $action (@{$table->{'defaultActions'}}) { # Do not overwrite existing actions unless ( exists ( $table->{'actions'}->{$action} )) { $table->{'actions'}->{$action} = $defAction; } } } } # Method: _paramsWithSetterJS # # Return the string which defines an array with that parameters # which have a setter defined # # Returns: # # String - the string ready to print on a JavaScript file # sub _paramsWithSetterJS { my ($self, @additionalParams) = @_; my $table = $self->table(); my @parameters; foreach my $type ( @{$table->{'tableDescription'}}) { push ( @parameters, $type->fields()); } my $fieldsWithOutSetter = $self->fieldsWithUndefSetter(); my @paramsWithSetter = grep {!$fieldsWithOutSetter->{$_}} @parameters; push (@paramsWithSetter, 'filter', 'page'); push @paramsWithSetter, @additionalParams; my $paramsArray = '[' . "'" . pop(@paramsWithSetter) . "'"; foreach my $param (@paramsWithSetter) { $paramsArray .= ', ' . "'" . $param . "'"; } $paramsArray .= ']'; return $paramsArray; } ###################################### # AUTOLOAD helper private functions ###################################### # Method: _autoloadAdd # # This method implements the addition autoload. This method parses # the method name, # # Parameters: # # methodName - String the method name begins with 'add' # paramsRef - array ref the undefined method parameters # # Returns: # # String - the newly created row identifier (it does not matter if # the addition is done in a model or any submodel) # sub _autoloadAdd { my ($self, $methodName, $paramsRef) = @_; # It will possibly launch an internal exception $self->_checkMethodSignature( 'add', $methodName, $paramsRef); if ( $self->_actionAppliedToModel( 'add', $methodName) ) { # Convert array ref to hash ref my %params = @{$paramsRef}; $paramsRef = \%params; # Simple add (add a new row to a model including submodels...) my $instancedTypes = $self->_fillTypes($paramsRef, 1); my $addedId = $self->addTypedRow($instancedTypes); my $subModels = $self->_subModelFields(); foreach my $subModel (@{$subModels}) { if ( exists $paramsRef->{$subModel} ) { $self->_autoloadAddSubModel($subModel, $paramsRef->{$subModel}, $addedId); } } return $addedId; } else { # An addition to one of the submodels return $self->_autoloadActionSubModel('add', $methodName, $paramsRef); } } # Method: _autoloadDel # # This method implements the remove autoload. This method parses # the method name, # # Parameters: # # methodName - String the method name begins with 'del' # paramsRef - array ref the undefined method parameters # # Returns: # # true - if the removal was successful # # Exceptions: # # - thrown if the removal cannot be done # sub _autoloadDel { my ($self, $methodName, $paramsRef) = @_; # It will possibly launch an internal exception $self->_checkMethodSignature( 'del', $methodName, $paramsRef); if ( $self->_actionAppliedToModel( 'del', $methodName) ) { # Get the identifier my $removeId = $self->_autoloadGetId($self, $paramsRef); # Simple del (del a row to a model) $self->removeRow($removeId, 1); return 1; } else { # A removal to one of the submodels return $self->_autoloadActionSubModel('del', $methodName, $paramsRef); } } # Method: _autoloadGet # # This method implements the accessor methods # # Parameters: # # methodName - String the method name begins with 'get' # paramsRef - array ref the undefined method parameters # # Returns: # # hash ref - the same as return # value if the answer has more that one field # - if the answer just return a single # field # # Exceptions: # # - thrown if the access cannot be done # sub _autoloadGet { my ($self, $methodName, $paramsRef) = @_; # It will possibly launch an internal exception $self->_checkMethodSignature( 'get', $methodName, $paramsRef); if ($self->_actionAppliedToModel( 'get', $methodName)) { # Get the identifier my $getId = $self->_autoloadGetId($self, $paramsRef); # Simple del (del a row to a model) my $row = $self->row($getId); my $fieldNames = undef; # Get the field names if any $fieldNames = $paramsRef->[$#$paramsRef] if ( scalar(@{$paramsRef}) % 2 == 0 ); return $self->_filterFields($row, $fieldNames); } else { # A removal to one of the submodels return $self->_autoloadActionSubModel('get', $methodName, $paramsRef); } } # Method: _autoloadSet # # This method implements the update autoload # # Parameters: # # methodName - String the method name begins with 'set' # paramsRef - array ref the undefined method parameters # # Exceptions: # # - thrown if the update cannot be done # sub _autoloadSet { my ($self, $methodName, $paramsRef) = @_; # It will possibly launch an internal exception $self->_checkMethodSignature('set', $methodName, $paramsRef); if ( $self->_actionAppliedToModel('set', $methodName) ) { my $updateId = $self->_autoloadGetId($self, $paramsRef); # Remove the id from the params shift ( @{$paramsRef} ); # Convert array ref to hash ref my %params = @{$paramsRef}; my $force = delete $params{force}; defined $force or $force = 0; $paramsRef = \%params; # Simple add (add a new row to a model including submodels...) my $instancedTypes = $self->_fillTypes($paramsRef); $self->setTypedRow($updateId, $instancedTypes, force => $force); my $subModels = $self->_subModelFields(); foreach my $subModel (@{$subModels}) { if ( exists $paramsRef->{$subModel} ) { $self->_autoloadSetSubModel($subModel, $paramsRef->{$subModel}, $updateId); } } } else { # An update to one of the submodels $self->_autoloadActionSubModel('set', $methodName, $paramsRef); } } ############################################################# # Protected helper methods to help autoload helper functions ############################################################# # Method: _checkMethodSignature # # Check the method name and parameters from the autoloads # # Parameters: # # action - String the action to run (add, del, set or get) # # methodName - String the method name to check # # paramsRef - array ref the parameters to check all parameters # are set correctly # sub _checkMethodSignature # (action, methodName, paramsRef) { my ($self, $action, $methodName, $oldParamsRef) = @_; my $paramsRef = Clone::Fast::clone($oldParamsRef); # Delete the action from the name my $first = ( $methodName =~ s/^$action// ); my @modelNames = split ( 'To', $methodName); my $tableNameInModel = $modelNames[$#modelNames]; my $subModelInMethod = $modelNames[$#modelNames - 1] unless ( $#modelNames == 0 ); my $submodels = $self->_subModelFields(); if ( defined ( $subModelInMethod ) and defined ( $submodels )) { # Turn into lower case the first letter $subModelInMethod = lcfirst($subModelInMethod); if ( $subModelInMethod eq any(@{$submodels}) ) { # Remove one parameter, since the index is used shift ( @{$paramsRef} ); # newMethodName goes to the recursion my $newMethodName = $methodName; $newMethodName =~ s/To$tableNameInModel$//; my $foreignModelName = $self->fieldHeader($subModelInMethod)->foreignModel(); my $manager = EBox::Model::Manager->instance(); my $foreignModel = $manager->model($foreignModelName); # In order to decrease the number of calls if ( scalar ( @modelNames ) > 2 ) { # Call recursively to the submodel $foreignModel->_checkMethodSignature($action, $newMethodName, $paramsRef); } } else { throw EBox::Exceptions::Internal('Illegal sub model field name. It ' . 'should be one of the following: ' . join(' ', @{$submodels}) ); } } else { # The final recursion is reached # If the action is an addition, there is no identifier my $nParams = scalar( @{$paramsRef} ); unless ( $action eq 'add') { $nParams--; } if ( $action eq 'get' ) { if ( $nParams > 0 ) { # Check the final get parameter is an array ref if any unless ( ref ( $paramsRef->[$#$paramsRef] ) eq 'ARRAY' ) { throw EBox::Exceptions::Internal('If you use a field selector, it must be ' . 'an array reference'); } } } else { # Check the number of parameters are even unless ( $nParams % 2 == 0 ) { throw EBox::Exceptions::Internal('The number of parameters is odd. Some ' . 'index argument is missing. Remember the ' . 'indexes are positional and the model arguments ' . 'are named'); } } } # If the iteration is the first one, check the table name or nothing if ( $first ) { # Check only simple cases (add[]) if ( $methodName and not defined ( $subModelInMethod )) { unless ( $methodName eq $self->tableName() ) { throw EBox::Exceptions::Internal( "Method $_[2] does not exist, May you have mispelled it?"); } } } } # Function: _actionAppliedToModel # # Determine whether the action is only applied to a single row on # a model or refers to a submodel. No matter how deep the # submodel to apply the action is placed # # Parameters: # # action - String the action name # methodName - String the method name which describes the action # # Returns: # # boolean - true if the action is applied to the model itself, # false if the action is applied only to one of the submodels # sub _actionAppliedToModel { my ($self, $action, $methodName) = @_; $methodName =~ s/^$action//; my $tableName = $self->tableName(); if ( $methodName =~ m/.+To$tableName/ ) { return 0; } else { return 1; } } # Get the fields which contains a HasMany type sub _subModelFields { my ($self) = @_; my @subModelFields = (); foreach my $fieldName (@{$self->fields()}) { my $type = $self->fieldHeader($fieldName); if ( $type->isa('EBox::Types::HasMany') ) { push ( @subModelFields, $fieldName ); } } return \@subModelFields; } # Method: _fillTypes # # Fill the types with the given parameters, it returns a list # containing the types with the defining types. # # Parameters: # # params - hash ref containing the name and the values for each # type to fill # # fillDefault - boolean indicating if there are any field which is # not provided in params parameter, it will feed with its default # value if any *(Optional)* Default value: false # # Returns: # # hash ref - the types instanced with a value set indexed by field # name # # Exceptions: # # - thrown if any error setting the # types is done # sub _fillTypes { my ($self, $params, $fillDefault) = @_; $fillDefault = '' unless defined($fillDefault); # Check all given fields to fill are in the table description foreach my $paramName (keys %{$params}) { unless ( $paramName eq any(@{$self->fields()}) ) { throw EBox::Exceptions::Internal("$paramName does not exist in the " . 'model ' . $self->name() . ' description'); } } my $filledTypes = {}; foreach my $fieldName (@{$self->fields()}) { my $field = $self->fieldHeader($fieldName); if ( exists $params->{$fieldName} ) { my $paramValue = $params->{$fieldName}; my $newType = $field->clone(); $newType->setValue($paramValue); $filledTypes->{$fieldName} = $newType; } elsif ( $fillDefault and defined($field->defaultValue()) and (not $field->optional())) { # New should set default value my $newType = $field->clone(); $filledTypes->{$fieldName} = $newType; } } return $filledTypes; } # Method: _autoloadAddSubModel # # Add every row to a submodel in the bulk addition # # Parameters: # # subModelFieldName - String the submodel (HasMany) field name # # subModelRows - array ref the submodel rows to add having the scheme as # addition has # # id - String the identifier which determines where to # store the data within this submodel # sub _autoloadAddSubModel # (subModelFieldName, rows, id) { my ($self, $subModelFieldName, $subModelRows, $id) = @_; my $hasManyField = $self->fieldHeader($subModelFieldName); my $userField = $hasManyField->clone(); my $directory = $self->directory() . "/keys/$id/$subModelFieldName"; my $foreignModelName = $userField->foreignModel(); my $submodel = EBox::Model::Manager->instance()->model( $foreignModelName ); $submodel->setDirectory($directory); # Addition to a submodel foreach my $subModelRow (@{$subModelRows}) { my $instancedTypes = $submodel->_fillTypes($subModelRow, 1); my $addedId = $submodel->addTypedRow($instancedTypes); my $subSubModels = $submodel->_subModelFields(); foreach my $subSubModel (@{$subSubModels}) { if ( exists $subModelRow->{$subSubModel} ) { $submodel->_autoloadAddSubModel($subSubModel, $subModelRow->{$subSubModel}, $addedId); } } } } # Method: _autoloadSetSubModel # # Update every row to a submodel in the bulk update # # Parameters: # # subModelFieldName - String the submodel (HasMany) field name # # subModelRows - array ref the submodel rows to set having the scheme as # addition has # # id - String the identifier which determines where to # store the data within this submodel # sub _autoloadSetSubModel # (subModelFieldName, rows, id) { my ($self, $subModelFieldName, $subModelRows, $id) = @_; my $hasManyField = $self->fieldHeader($subModelFieldName); my $userField = $hasManyField->clone(); my $directory = $self->directory() . "/keys/$id/$subModelFieldName"; my $foreignModelName = $userField->foreignModel(); my $submodel = EBox::Model::Manager->instance()->model( $foreignModelName ); $submodel->setDirectory($directory); # Addition to a submodel foreach my $subModelRowKey (keys %{$subModelRows}) { my $updateId = $self->_autoloadGetId($submodel, [ $subModelRowKey ] ); unless ( defined ( $updateId )) { throw EBox::Exceptions::DataNotFound( data => 'submodel row identifier', value => $subModelRowKey); } my $instancedTypes = $submodel->_fillTypes($subModelRows->{$subModelRowKey}); $submodel->setTypedRow($updateId, $instancedTypes, force => 1); } } # Method: _autoloadActionSubModel # # Action performed to a single row from a submodel in a model # # Parameters: # # action - String the action name (add, del, get or set) are # possible # # methodName - String the method name # # paramsRef - array ref the undefined method parameters # sub _autoloadActionSubModel # (action, methodName, paramsRef) { my ($self, $action, $methodName, $origParamsRef) = @_; my $paramsRef = Clone::Fast::clone($origParamsRef); $methodName =~ s/^$action//; my @modelNames = split ( 'To', $methodName); @modelNames = reverse ( @modelNames ); # Let's go along the method name delTableToTableToTable my $model = $self; foreach my $subModelField (@modelNames[1 .. @modelNames - 1]) { # Turn to lower case the first letter $subModelField = lcfirst($subModelField); # Get the has many field my $hasManyField = $model->fieldHeader($subModelField); my $userField = $hasManyField->clone(); # Get the identifier to set the directory my $id = $self->_autoloadGetId($model, $paramsRef); # Remove an index to get the model shift ( @{$paramsRef} ); my $directory = $model->directory() . "/keys/$id/$subModelField"; my $foreignModelName = $userField->foreignModel(); $model = EBox::Model::Manager->instance()->model( $foreignModelName, ); $model->setDirectory($directory); } # Change from lower case to upper case the first letter my $UCAction = ucfirst ( $action ); my $methodAutoload = "_autoload$UCAction"; # Action performed in a row in a submodel $model->$methodAutoload( $action . $model->tableName(), $paramsRef, ); } # Method: pushRedirection # # Push a redirection to be used by the controller # # Parameters: # # redirect - URL containing the redirect, should be something like: # /zentyal/Controller/Foo sub pushRedirection { my ($self, $redirect) = @_; $self->{redirection} = $redirect; } # Method: popRedirection # # Pop a redirection to be used by the controller # # Returns: # # redirect - URL containing the redirect, should be something like: # /zentyal/Controller/Foo sub popRedirection { my ($self) = @_; my $redirection = $self->{redirection}; $self->{redirection} = undef; return $redirection; } # Method: printableActionName # # Get the i18ned action name for the form. # # Returns: # # String - the i18ned action name. Default value: 'Change' # sub printableActionName { my ($self) = @_; unless (defined ( $self->table()->{'printableActionName'})) { $self->table()->{'printableActionName'} = __('Change'); } return $self->table()->{'printableActionName'}; } # Method: disableAutocomplete # # Return if the autocompletion in the add/edit forms # must be disabled # # Returns: # # boolean - true if autocompletion is disabled, false otherwise # sub disableAutocomplete { my ($self) = @_; return $self->{table}->{'disableAutocomplete'}; } # Method: viewCustomizer # # Returns EBox::View::Customizer for this model. # By default it creates an empty object. # # Returns: # # An instance of # sub viewCustomizer { my ($self) = @_; unless ($self->{viewCustomizer}) { my $viewCustom = new EBox::View::Customizer(); $viewCustom->setModel($self); $self->{viewCustomizer} = $viewCustom; } return $self->{viewCustomizer}; } # Method: _autoloadGetId # # Get the identifier which will be used to set the directory to # that model # # Parameters: # # model - the model to get the row # identifier # # paramsRef - array ref the method parameters # # Returns: # # String - the identifier if found # # Exceptions: # # - thrown if the identifier is # not found # sub _autoloadGetId { my ($self, $model, $paramsRef) = @_; # Get the first element to get the identifier from my $id; # index field is the field to be used to sort by. It MUST be unique if ( defined ( $model->indexField() )) { $id = $model->findId( $model->indexField() => $paramsRef->[0] ); unless ( defined ( $id )) { unless ( defined ( $model->row($paramsRef->[0] ))) { throw EBox::Exceptions::DataNotFound( data => 'identifier', value => $paramsRef->[0]); } $id = $paramsRef->[0]; } } else { # Check if it a valid identifier $id = $paramsRef->[0]; unless ( defined ( $model->row($paramsRef->[0]) )) { # the given id is a number (position) if ( $paramsRef->[0] =~ m/^\d+$/ ) { my @ids = @{$model->ids()}; if ( exists ( $ids[$paramsRef->[0]] )) { $id = $ids[$paramsRef->[0]]; } } } } return $id; } # Method: _filterFields # # Giving a result in a row structure, it will only return the # field names given in the array ref. If any of the given fields # does not exist in the model, it will rise an exception. # # Parameters: # # row - hash ref the row to filter the fields to return # # fieldNames - array ref containing the requested fields to return # # Returns: # # hash ref - the filtered row comprising the fields requested at # fieldNames array # # - if the fieldNames array consist only # of one element # # Exceptions: # # - thrown if any of the fields does # not correspond from any of the model fields # sub _filterFields { my ($self, $row, $fieldNames) = @_; unless ( defined ( $fieldNames ) ){ return $row; } my $newRow = EBox::Model::Row->new(dir => $row->dir(), confmodule => $row->configModule()); $newRow->setId($row->id()); $newRow->setOrder($row->order()); my @modelFields = @{$self->fields()}; foreach my $fieldName ( @{$fieldNames} ) { unless ( $fieldName eq any(@modelFields) ) { throw EBox::Exceptions::Internal( 'Trying to get a field which does exist in this model. These fields ' . 'are available: ' . join ( ', ', @modelFields)); } # Put it the new one $newRow->addElement($row->elementByName($fieldName)); } if ($newRow->size() == 1) { return $newRow->elementByIndex(0); } return $newRow; } # Method: _setEnabledAsFieldInTable # # Set the enabled field (a boolean type) in the current model # with name 'Enabled' # sub _setEnabledAsFieldInTable { my ($self) = @_; # Check if enabled field already exists if ( exists $self->{'table'}->{'tableDescriptionByName'}->{'enabled'} ) { return; } my $tableDesc = $self->{'table'}->{'tableDescription'}; my $enabledType = new EBox::Types::Boolean(fieldName => 'enabled', printableName => __('Enabled'), editable => 1, defaultValue => $self->defaultEnabledValue()); unshift (@{$tableDesc}, $enabledType); } # Set the table as volatile if all its fields are so sub _setIfVolatile { my ($self) = @_; my $desc = $self->{table}->{tableDescription}; foreach my $field (@{$desc}) { return if ( not $field->volatile()); } $self->{volatile} = 1; } sub _parse_words { my ($str) = @_; my @w = (); if(defined($str)) { Encode::_utf8_on($str); @w = split('\W+', lc($str)); } return @w; } # Method: keywords # # Overrides: # # # sub keywords { my ($self) = @_; my @words = (); push(@words, _parse_words($self->pageTitle())); push(@words, _parse_words($self->headTitle())); push(@words, _parse_words($self->printableName())); push(@words, _parse_words($self->printableModelName())); push(@words, _parse_words($self->printableRowName())); push(@words, _parse_words($self->help())); for my $fieldName (@{$self->fields()}) { my $field = $self->fieldHeader($fieldName); push(@words, _parse_words($field->printableName())); push(@words, _parse_words($field->help())); } return \@words; } # Method: filesPaths # # Returns: # the paths of the files managed by the datatable and its submodels sub filesPaths { my ($self) = @_; $self->_hasFileFields() or return []; my @files = map { @{ $self->row($_)->filesPaths() } } @{ $self->ids() }; return \@files; } # Method: filesPathsForRow # # returns the file paths for a given row. # # Warnings: # we need to do this bz we cannot override row's methods for specific models! sub filesPathsForRow { my ($self, $row) = @_; return $row->filesPaths(); } sub _beginTransaction { my ($self) = @_; $self->parentModule()->{redis}->begin(); } sub _commitTransaction { my ($self) = @_; $self->parentModule()->{redis}->commit(); } sub _rollbackTransaction { my ($self) = @_; $self->parentModule()->{redis}->rollback(); } # Method: clone # # clone the contents on one DataTable into another. Due to # the impossibilit of having two instances with different directories # the databases are reffered as directories. This must called on a DataTable # instance of the same class as the source and the destination. # # # Parameters: # # srcDir - conf directory of the datatable to be clone. # dstDir - conf directory of the datatable to receive the clone # # Returns: # nothing sub clone { my ($self, $srcDir, $dstDir) = @_; my $selfDir = $self->directory(); try { $self->setDirectory($srcDir); my @srcRows = map { $self->row($_) } @{$self->ids()}; $self->setDirectory($dstDir); $self->removeAll(1); foreach my $srcRow (@srcRows) { my $newId = $self->addTypedRow($srcRow->hashElements()); my $newRow = $self->row($newId); $newRow->cloneSubModelsFrom($srcRow) } } finally { $self->setDirectory($selfDir); }; } # Method: setAll # # set the specified field in all rows to the fiven value # # Parameters: # fieldName - field to set # value - value to set sub setAll { my ($self, $fieldName, $value) = @_; foreach my $id (@{ $self->ids() }) { my $row = $self->row($id); my $field = $row->elementByName($fieldName); if (not $field) { throw EBox::Exceptions::Internal( "Field $field is not present in table " . $self->name(), ); } $field->setValue($value); $row->store(); } } # Method: checkAllProperty # # return the value of the 'checkAll' property # # This property should be set to the name of a checkbox field to enable it # or to undef to not use the check all option (default: disabled) sub checkAllProperty { my ($self) = @_; return $self->{'table'}->{'checkAll'}; } sub checkAllControlValue { my ($self, $fieldName) = @_; foreach my $id (@{ $self->ids() }) { my $row = $self->row($id); if (not $row->valueByName($fieldName)) { return 0; } } return 1; } sub _confirmationDialogForAction { my ($self, $action, $params_r) = @_; exists $self->{'table'}->{'confirmationDialog'} or return 1; exists $self->{'table'}->{'confirmationDialog'}->{$action} or return 1; return $self->{'table'}->{'confirmationDialog'}->{$action}->($self, $params_r); } sub confirmationJS { my ($self, $action, $goAheadJS) = @_; my $table = $self->table(); exists $table->{'confirmationDialog'} or return $goAheadJS; exists $table->{'confirmationDialog'}->{$action} or return $goAheadJS; my $actionUrl = $table->{'actions'}->{'editField'}; my @elements = map { my $element = $_; my @fields = map { qq{'$_'} } $element->fields(); @fields; } @ { $table->{tableDescription} }; my $elementsArrayJS = '['. join(',', @elements) . ']' ; my $function = "confirmationDialog('%s', '%s','%s', '%s', %s)"; my $call = sprintf ($function, $self->_mainController(), $table->{'tableName'}, $table->{'confdir'}, $action, $elementsArrayJS ); my $goAheadJSEscaped = $goAheadJS; $goAheadJSEscaped =~ s{'}{\\'}g; my $js =<< "ENDJS"; this.disable = true; var specs = $call; this.disable = false; if (specs.wantDialog) { showConfirmationDialog(specs, '$goAheadJSEscaped'); } else { $goAheadJS ; } return false; ENDJS return $js; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Model/DataMultiTable.pm0000664000000000000000000001020312017102272020471 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Model::DataMultiTable; use strict; use warnings; use EBox::Exceptions::InvalidType; # Constructor: new # # Constructor for # # Parameters: # # # Exceptions: sub new { my $class = shift; my %params = @_; my $self = {}; bless( $self, $class ); return $self; } # Method: tableModel # # Get a table model given its identifier. # It should be implemented # # Parameters: # # id - the table's identifier # # Returns: # # - the required table model # # Exceptions: # # - - throw if the rule model # does NOT exist # sub tableModel # (id) { throw EBox::Exceptions::NotImplemented(); } # Method: selectOptions # # Get the selectable options to choose a table to handle with. # This method should be override # # Returns: # # array ref - a list with hash ref with the following elements: # - id - the table identifier # - printableId - the table's printable identifier # sub selectOptions { throw EBox::Exceptions::NotImplemented(); } # Method: multiTable # # Get the multi table description. It must NOT be overrided. # # Returns: # # hash ref with the table description # sub multiTable { my ($self) = @_; # It's a singleton method unless (defined ($self->{multiTable})) { $self->{multiTable} = $self->_multiTable(); } return $self->{multiTable}; } # Method: _multiTable # # Override this method to describe your multi table. # This method is (PROTECTED) # # Returns: # # table description. See example on # . # sub _multiTable { throw EBox::Exceptions::NotImplemented(); } # Method: selectedTableNotify # # Override this method to be notified whenever a table is # selected # # Arguments: # # table - # sub selectedTableNotify { } # Method: action # # Return the CGI Controller which performs this action # # Parameters: # # action - the action's name # # Return: # # String - path to the selected action # # Exceptions: # # - throw if there is no action # sub action # (action) { my ($self, $action) = @_; my $actions_ref = $self->multiTable()->{actions}; if (exists ($actions_ref->{$action})){ return $actions_ref->{$action}; } else { throw EBox::Exceptions::DataNotFound( data => __('Action'), value => $action); } } # Method: printableName # # Return the multitable printable name # # Returns: # # String - the printable name # sub printableName { my ($self) = @_; return $self->multiTable()->{printableName}; } # Method: helpMessage # # Return the help message # # Returns: # # String - the help message # sub helpMessage { my ($self) = @_; return $self->multiTable()->{help}; } # Method: optionMessage # # Return the option message to show close to the selector # # Returns: # # String - the option message # sub optionMessage { my ($self) = @_; return $self->multiTable()->{optionMessage}; } ### # Private helper methods ### # Check all models sub _checkModels { my ($self) = @_; foreach my $model (@{$self->{tables}}) { if (not $model->isa('EBox::Model::DataTable')) { throw EBox::Exception::InvalidType( 'model', 'EBox::Model::DataTable' ); } } } 1; zentyal-core-2.3.21+quantal1/src/EBox/Model/Manager.pm0000664000000000000000000005104412017102272017217 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: Manager # # This class is used to coordinate all the available models and composites # along Zentyal. It allows us to do things like specifiying relations # amongst different models. # # # package EBox::Model::Manager; use strict; use warnings; use EBox; use EBox::Gettext; use EBox::Global; use EBox::Exceptions::Internal; use EBox::Exceptions::DataNotFound; use EBox::Exceptions::DataInUse; use Error qw(:try); # Constant use constant MAX_INT => 32767; # Singleton variable my $_instance = undef; sub _new { my $class = shift; my $self = {}; $self->{models} = {}; $self->{composites} = {}; $self->{foreign} = {}; $self->{modByModel} = {}; $self->{modByComposite} = {}; $self->{parentByComponent} = {}; $self->{notifyActions} = {}; $self->{revModelDeps} = {}; bless($self, $class); $self->_setupInfo(); return $self; } # Method: instance # # Return a singleton instance of class # # # Returns: # # object of class # sub instance { unless(defined($_instance)) { $_instance = EBox::Model::Manager->_new(); } return $_instance; } # Method: model # # Return model instance # # Parameters: # # (POSITIONAL) # # path - String determines the model's name following this pattern: # # 'modelName' - used only if the modelName is unique within # eBox framework and no execution parameters are required to # its creation # # '/moduleName/modelName[/index1/index2]' - used in # new calls and common models which requires a name space and # parameters not set on compilation time # # Returns: # # An object of type - if just one model # instance is alive # # array ref - containing instances if more # than model corresponds to the given path # # Exceptions: # # - thrown if the given path does # not correspond with any stored model instance # # - thrown if any compulsory # argument is missing # # - thrown if the path argument is # bad-formed # sub model { my ($self, $path, $readonly) = @_; return $self->_componentByPath('model', $path, $readonly); } # Method: composite # # Given a composite name it returns an instance of this composite # # Parameters: # # composite - String the composite model's name, it can follow one # of these patterns: # # 'compositeName' - used only if the compositeName is unique # within eBox framework and no execution parameters are # required to its creation # # '/moduleName/compositeName[/index1] - used when a name space # is required or parameters are set on runtime. # # Returns: # # - the composite object if just one # composite instance is required # # array ref - containing instances if # more than one composite corresponds to the given composite name. # # Exceptions: # # - thrown if the composite does # not exist given the composite parameter # # - thrown if any compulsory # argument is missing # # - thrown if the composite parameter # does not follow the given patterns # sub composite { my ($self, $path, $readonly) = @_; return $self->_componentByPath('composite', $path, $readonly); } # Method: component # # Given a component name it returns an instance of this component # No need to specify if it's a model or a composite # sub component { my ($self, $path, $readonly) = @_; if ($self->_modelExists($path)) { return $self->model($path, $readonly); } elsif ($self->_compositeExists($path)) { return $self->composite($path, $readonly); } else { throw EBox::Exceptions::Internal("Component $path does not exist"); } } sub models { my ($self, $module) = @_; return $self->_components('model', $module); } sub composites { my ($self, $module) = @_; return $self->_components('composite', $module); } sub _components { my ($self, $kind, $module) = @_; my $name = $module->{name}; return [ map { $self->_component($kind, $module, $_) } keys %{$self->{"${kind}s"}->{$name}} ]; } sub _componentByPath { my ($self, $kind, $path, $readonly) = @_; # Check arguments unless (defined ($path)) { throw EBox::Exceptions::MissingArgument('composite'); } my ($moduleName, $compName, @other) = grep { $_ ne '' } split ( '/', $path); if (@other) { throw EBox::Exceptions::DataNotFound(data => $kind, value => $path, silent => 1); } if (not defined ($compName) and $path =~ m:/:) { throw EBox::Exceptions::Internal("Component name can't contain slashes, valid formats are: 'component' or 'module/component'"); } unless (defined ($compName)) { $compName = $moduleName; # Try to infer the module name from the compName my $key = 'modBy' . ucfirst($kind); unless (defined ($self->{$key}->{$compName})) { throw EBox::Exceptions::DataNotFound(data => $kind, value => $compName, silent => 1); } my @modules = keys %{$self->{$key}->{$compName}}; if (@modules == 1) { $moduleName = $modules[0]; } else { throw EBox::Exceptions::Internal("Can't guess module because $compName belongs to more than one module (@modules)"); } } my $module = EBox::Global->getInstance($readonly)->modInstance($moduleName); return $self->_component($kind, $module, $compName); } sub _component { my ($self, $kind, $module, $name) = @_; my $key = "${kind}s"; my $moduleName = $module->{name}; my $access = $module->{ro} ? 'ro' : 'rw'; unless (exists $self->{$key}->{$moduleName}->{$name}) { throw EBox::Exceptions::DataNotFound(data => $kind, value => $name, silent => 1); } unless (defined $self->{$key}->{$moduleName}->{$name}->{instance}->{$access}) { my $global = EBox::Global->getInstance(); my $class = $global->_className($moduleName) . '::' . ucfirst($kind) . "::$name"; eval "use $class"; if ($@) { throw EBox::Exceptions::Internal("Error loading $class: $@"); } my $parent = undef; my $parentName = $self->{parentByComponent}->{$moduleName}->{$name}; if ($parentName) { $parent = $self->component("$moduleName/$parentName"); } my %params = (confmodule => $module, parent => $parent, directory => $name); my $instance = $class->new(%params); if ($kind eq 'composite') { my $components = $self->{composites}->{$moduleName}->{$name}->{components}; unless (@{$components}) { $components = $instance->componentNames(); } my @instances; foreach my $cname (@{$components}) { unless ($cname =~ m{/}) { $cname = "$moduleName/$cname"; } my $component = $self->component($cname, $module->{ro}); push (@instances, $component); } $instance->{components} = \@instances; } $self->{$key}->{$moduleName}->{$name}->{instance}->{$access} = $instance; } return $self->{$key}->{$moduleName}->{$name}->{instance}->{$access}; } # Method: modelsUsingId # # Given a row id of a model, it returns the models which # are currently referencing it # # Parameters: # # (POSITIONAL) # # model - model string # rowId - string containing the row's id # # Returns: # # # # Exceptions: # # if the model does not exist sub modelsUsingId { my ($self, $modelName, $rowId) = @_; my $model = $self->model($modelName); unless (defined($model)) { throw EBox::Exceptions::DataNotFound( 'data' => 'model name', 'value' => $modelName); } # Fetch dependencies based on types my %models; my $modelDepHash = $self->_oneToOneDependencies($modelName); foreach my $modelDepName (keys %{$modelDepHash}) { my $modelDep = $self->model($modelDepName); next unless(defined($modelDep)); for my $fieldName (@{$modelDepHash->{$modelDepName}}) { if (defined($modelDep->findValue($fieldName => $rowId))) { $models{$modelDepName} = $modelDep->table()->{'printableTableName'}; } } } # Fetch dependencies from models which are not declaring dependencies # in types and instead they are using notifyActions if (exists $self->{'notifyActions'}->{$modelName}) { foreach my $observer (@{$self->{'notifyActions'}->{$modelName}}) { my $observerModel = $self->model($observer); if ($observerModel->isUsingId($modelName, $rowId)) { $models{$observer} = $observerModel->printableContextName(); } } } return \%models; } # Method: modelActionTaken # # This method is used to let models know when other model has # taken an action. # # It will automatically call the model in which descrption they # request to be warned about the current action and model. # # # Parameters: # # (POSITIONAL) # # model - model name, with module path, where the action took place # action - string represting the action: # [ add, del, edit, moveUp, moveDown ] # # row - row modified # # Returns: # # String - any i18ned string given by other modules when a change is done # # Exceptions: # # if the model does not exist # if argument is missing # sub modelActionTaken { my ($self, $model, $action, $row) = @_; throw EBox::Exceptions::MissingArgument('model') unless (defined($model)); throw EBox::Exceptions::MissingArgument('action') unless (defined($action)); throw EBox::Exceptions::MissingArgument('row') unless (defined($row)); my $strToRet = ''; for my $observerName (@{$self->{'notifyActions'}->{$model}}) { my $observerModel = $self->model($observerName); $strToRet .= $observerModel->notifyForeignModelAction($model, $action, $row) . '
    '; } return $strToRet; } # Method: removeRowsUsingId # # Given a row id of a model, remove rows from models referencing it # # Parameters: # # (POSITIONAL) # # model - model object # rowId - string containing the row's id # # Returns: # # String - the i18ned string informing about the changes done in # other models # # Exceptions: # # if the model does not exist sub removeRowsUsingId { my ($self, $modelName, $rowId) = @_; my $strToShow = ''; my $model = $self->model($modelName); unless (defined($model)) { throw EBox::Exceptions::DataNotFound( 'data' => 'model name', 'value' => $modelName); } my $modelDepHash = $self->_oneToOneDependencies($modelName); foreach my $modelDepName (keys %{$modelDepHash}) { my $modelDep = $self->model($modelDepName); next unless(defined($modelDep)); my $deletedNum = 0; for my $fieldName (@{$modelDepHash->{$modelDepName}}) { my %rowsDeleted; for my $id (@{$modelDep->findAllValue($fieldName => $rowId)}) { next if (exists $rowsDeleted{$id}); $modelDep->removeRow($id, 1); $deletedNum++; $rowsDeleted{$id} = 1; } } if ($deletedNum > 0) { $strToShow .= $modelDep->automaticRemoveMsg($deletedNum); } } while (my ($modelDepName, $fieldName) = each %{$modelDepHash}) { my $modelDep = $self->model($modelDepName); next unless(defined($modelDep)); } return $strToShow; } # Method: warnIfIdIsUsed # # Check from a model if any model is using this row # # Parameters: # # modelName - String the model which the action is going to be # performed # # id - String the row identifier # # Exceptions: # # - thrown if the id is used by # any other model # sub warnIfIdIsUsed { my ($self, $modelName, $id) = @_; my $tablesUsing; for my $name (values %{$self->modelsUsingId($modelName, $id)}) { $tablesUsing .= '
    - ' . $name ; } if ($tablesUsing) { throw EBox::Exceptions::DataInUse( __('The data you are removing is being used by the following sections:') . '
    ' . $tablesUsing); } } # Method: warnOnChangeOnId # # Check from a model if any model is using a row that is # changing # # Parameters: # # modelName - String the model which the action is going to be # performed # # id - String the row identifier # # changedData - hash ref the types that has been changed # # oldRow - hash ref the old row with the content as # return value # # Exceptions: # # - thrown if the id is used by # any other model # sub warnOnChangeOnId { my ($self, $modelName, $id, $changeData, $oldRow) = @_; my $tablesUsing; for my $name (keys %{$self->modelsUsingId($modelName, $id)}) { my $model = $self->model($name); my $issue = $model->warnOnChangeOnId(modelName => $modelName, id => $id, changedData => $changeData, oldRow => $oldRow); if ($issue) { $tablesUsing .= '
    - ' . $issue ; } } if ($tablesUsing) { throw EBox::Exceptions::DataInUse( __('The data you are modifying is being used by the following sections:') . '
    ' . $tablesUsing); } } # Method: addModel # # Adds an already instanced model to the manager. # # Parameters: # # model - instance of the model # sub addModel { my ($self, $model) = @_; my $module = $model->parentModule(); my $moduleName = $module->name(); my $modelName = $model->modelName(); unless (exists $self->{models}->{$moduleName}->{$modelName}) { $self->{models}->{$moduleName}->{$modelName} = { instance => { rw => undef, ro => undef }, parent => undef }; } if ($module->isReadOnly()) { $self->{models}->{$moduleName}->{$modelName}->{instance}->{ro} = $model; } else { $self->{models}->{$moduleName}->{$modelName}->{instance}->{rw} = $model; } $self->{modByModel}->{$modelName}->{$moduleName} = 1; } # Method: removeModel # # Remove a model from the manager # # Parameters: # # module - name of the module # model - name of the model # sub removeModel { my ($self, $module, $model) = @_; delete $self->{models}->{$module}->{$model}; delete $self->{modByModel}->{$model}->{$module}; } # Group: Private methods sub _setupInfo { my ($self) = @_; my $global = EBox::Global->getInstance(); foreach my $moduleName (@{$global->modNames()}) { my $info = $global->readModInfo($moduleName); $self->_setupModelInfo($moduleName, $info); $self->_setupCompositeInfo($moduleName, $info); $self->_setupForeignInfo($moduleName, $info); $self->_setupModelDepends($moduleName, $info); $self->_setupNotifyActions($moduleName, $info); } } sub _setupModelInfo { my ($self, $moduleName, $info) = @_; return unless exists $info->{models}; $self->{models}->{$moduleName} = {}; foreach my $model (@{$info->{models}}) { $self->{models}->{$moduleName}->{$model} = { instance => { rw => undef, ro => undef }, parent => undef }; unless (exists $self->{modByModel}->{$model}) { $self->{modByModel}->{$model} = {}; } $self->{modByModel}->{$model}->{$moduleName} = 1; } } sub _setupCompositeInfo { my ($self, $moduleName, $info) = @_; return unless exists $info->{composites}; $self->{composites}->{$moduleName} = {}; foreach my $composite (keys %{$info->{composites}}) { my $components = $info->{composites}->{$composite}; $self->{composites}->{$moduleName}->{$composite} = { instance => undef, components => $components}; foreach my $component (@{$components}) { if (exists $self->{models}->{$moduleName}->{$component}) { $self->{models}->{$moduleName}->{$component}->{parent} = $composite; } elsif (exists $self->{composites}->{$moduleName}->{$component}) { $self->{composites}->{$moduleName}->{$component}->{parent} = $composite; } } unless (exists $self->{modByComposite}->{$composite}) { $self->{modByComposite}->{$composite} = {}; } $self->{modByComposite}->{$composite}->{$moduleName} = 1; } } sub _setupForeignInfo { my ($self, $moduleName, $info) = @_; return unless exists $info->{foreign}; $self->{foreign}->{$moduleName} = {}; foreach my $component (keys %{$info->{foreign}}) { my $foreings = $info->{foreign}->{$component}; $self->{foreign}->{$moduleName}->{$component} = $foreings; foreach my $foreign (@{$foreings}) { $self->{parentByComponent}->{$moduleName}->{$foreign} = $component; } } } sub _setupModelDepends { my ($self, $moduleName, $info) = @_; my $depends = $info->{modeldepends}; foreach my $model (keys %{$depends}) { my $fullPath = $moduleName . '/' . $model; my $modelDeps = $depends->{$model}; foreach my $modelDep (keys %{$modelDeps}) { my $deps = $modelDeps->{$modelDep}; unless (exists $self->{revModelDeps}->{$modelDep}) { $self->{revModelDeps}->{$modelDep} = {}; } $self->{revModelDeps}->{$modelDep}->{$fullPath} = $deps; } } } sub _setupNotifyActions { my ($self, $moduleName, $info) = @_; my $notify = $info->{notifyactions}; foreach my $model (keys %{$notify}) { my $observerPath = '/' . $moduleName . '/' . $model . '/'; foreach my $notifier (@{ $notify->{$model} }) { # XXX change when we change the yaml to the more intuitive notifier # - >wathcer format if (not exists $self->{notifyActions}->{$notifier}) { $self->{notifyActions}->{$notifier} = []; } push @{ $self->{notifyActions}->{$notifier} }, $observerPath; } # $self->{notifyActions}->{$contextName} = $notify->{$model}; } } sub _modelExists { my ($self, $model) = @_; my ($mod, $comp) = split ('/', $model); if ($comp and $self->{modByModel}->{$comp}->{$mod}) { return 1; } else { $comp = $mod; foreach my $module (keys %{$self->{modByModel}}) { if ($self->{modByModel}->{$comp}->{$module}) { return 1; } } } return 0; } sub _compositeExists { my ($self, $composite) = @_; my ($mod, $comp) = split ('/', $composite); if ($comp and $self->{modByComposite}->{$comp}->{$mod}) { return 1; } else { $comp = $mod; foreach my $module (keys %{$self->{modByComposite}}) { if ($self->{modByComposite}->{$comp}->{$module}) { return 1; } } } return 0; } # Method: _oneToOneDependencies # # (PRIVATE) # # Given a model, it returns which models depends on it. # # Parameters: # # (POSITIONAL) # # model - model's name # # Return: # # hash refs containing pairs of: # # model name => field name which references # sub _oneToOneDependencies { my ($self, $model) = @_; $model =~ s{^/}{}; $model =~ s{/$}{}; unless (exists $self->{revModelDeps}->{$model}) { return {}; } return $self->{revModelDeps}->{$model}; } # FIXME: Is this needed? sub markAsChanged { } 1; zentyal-core-2.3.21+quantal1/src/EBox/Model/Row/0000775000000000000000000000000012017102272016052 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Model/Row/Test.pm0000664000000000000000000003476212017102272017343 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Model::Row::Test; use lib '../../..'; use base 'EBox::Test::Class'; use strict; use warnings; use Test::More;; use Test::Exception; use Test::MockObject; use Test::MockObject::Extends; use Perl6::Junction qw(any); use EBox::Types::Abstract; use lib '../../..'; use EBox::Model::Row; use EBox::Model::DataTable; use EBox::Types::Abstract; use EBox::Types::HasMany; sub setEBoxModules : Test(setup) { EBox::TestStubs::fakeEBoxModule(name => 'fakeModule'); } sub clearGConf : Test(teardown) { EBox::TestStubs::setConfig(); } sub deviantElementsTest : Test(8) { my ($self) = @_; my $row= $self->_newRow(); dies_ok { $row->addElement(undef); } 'Expecting fail when trying to add a undefined element'; dies_ok { my $badElement = new Test::MockObject(); $row->addElement($badElement); } 'Expecting fail when trying to add a non zentyal-type element'; dies_ok { my $badElement = new EBox::Types::Abstract(); $row->addElement($badElement); } 'Expecting fail when trying to add a zentyal-type element without fieldName'; $self->_populateRow($row); dies_ok { my $repeatedElement = $row->elementByIndex(1); $row->addElement($repeatedElement); } 'Expecting fail when adding a repeated element'; my $inexistentIndex = $row->size() + 2; dies_ok { $row->elementByIndex($inexistentIndex); } 'Expecting error when calling elementByIndex with a inexistent index'; my $inexistentElement = 'inexistent'; foreach my $accesor (qw(elementByName valueByName printableValueByName)) { dies_ok { $row->$accesor($inexistentElement); } "Expecting error when calling $accesor with inexistent name"; } } sub elementsTest : Test(35) { my ($self) = @_; my $row= $self->_newRow(); my @elementsToAdd; foreach my $i(0 .. 5) { my $el = new EBox::Types::Abstract( fieldName => "fieldName$i", printableName => "printableName$i", ); $el->setValue($i); push @elementsToAdd, $el; } lives_ok { foreach my $element (@elementsToAdd) { $row->addElement($element); } } 'Adding elements to the row'; is scalar @elementsToAdd, $row->size(), 'checking size of row after addition of elements'; is_deeply $row->elements(), \@elementsToAdd, 'checkign contents of the wor using elements() method'; my %expectedHashElements = map { ( $_->fieldName => $_) } @elementsToAdd; is_deeply $row->hashElements, \%expectedHashElements, 'checkign contents of the wor using hashElements() method'; ok (not $row->elementExists('inexistent')), 'checking elementExists on inexistent element'; foreach my $index (0 .. $#elementsToAdd) { my $el = $elementsToAdd[$index]; my $name = $el->fieldName(); my $value = $el->value(); my $printableValue = $el->printableValue(); ok $row->elementExists($name), "checking elementExists on existent element $name"; is_deeply $row->elementByName($name), $el, "checking elementByName in a existent element $name"; is_deeply $row->elementByIndex($index), $el, "checking elementByIndex in a existent element $name"; is $row->valueByName($name), $value, "checking valueByName in a existent element $name"; is $row->printableValueByName($name), $printableValue, "checking printableValueByName in a existent element $name"; } } sub parentRowTest : Test(3) { my ($self) = @_; my $row= $self->_newRow(); $self->_populateRow($row); is $row->parentRow(), undef, 'checking that calling parentRow when the model has not parent returns undef'; my $confmodule = EBox::Global->modInstance('fakeModule'); my $parentDirectory = '/ebox/modules/fakeModule/Parent'; my $rowWithChildId = 'ParentRow'; my $childDirectory = "$parentDirectory/$rowWithChildId/Child"; my $rowDirectory = "$childDirectory/Row"; my $parentModel = Test::MockObject::Extends->new( EBox::Model::DataTable->new( confmodule => $confmodule, directory => $parentDirectory, domain => 'domain', ) ); $parentModel->mock('row', sub { my ($self, $id) = @_; if ($id eq $rowWithChildId) { my $fakeRow = Test::MockObject->new(); $fakeRow->set_always('id', $rowWithChildId); } else { die "BAD ID $id"; } } ); my $childModel = EBox::Model::DataTable->new( confmodule => $confmodule, directory => $parentDirectory, domain => 'domain', ); $row = EBox::Model::Row->new( confmodule => $confmodule, dir => $rowDirectory ); $row->setId('FAKE_ID'); $row->setModel($childModel); $childModel->setParent($parentModel); my $parentRow; lives_ok { $parentRow = $row->parentRow() } 'getting parent row'; is $parentRow->id(), $rowWithChildId, 'checking ID of parent row'; } sub subModelTest : Test(3) { my ($self) = @_; my $row= $self->_newRow(); $self->_populateRow($row); my $confmodule = EBox::Global->modInstance('fakeModule'); my $subModelObject = EBox::Model::DataTable->new( confmodule => $confmodule, directory => 'Submodel', domain => 'domain', ); my $hasManyName = 'mockHasMany'; my $hasManyObject = Test::MockObject::Extends->new( EBox::Types::HasMany->new( fieldName => $hasManyName, printableName => $hasManyName, ) ); $hasManyObject->set_isa('EBox::Types::Abstract', 'EBox::Types::HasMany'); $hasManyObject->set_always(foreignModelInstance => $subModelObject); $row->addElement($hasManyObject); dies_ok { $row->subModel('inexistent'); } 'expecting error when calling subModel with a inexistent element'; dies_ok { my $name = $row->elementByIndex(0)->fieldName(); $row->subModel($name); } 'expecting error when calling subModel with a element that is not a HasMany'; is_deeply( $row->subModel($hasManyName), $subModelObject, 'checking that subModel returns the correct hasMany submodel' ); } sub unionTest : Test(6) { my ($self) = @_; my $row= $self->_newRow(); $self->_populateRow($row); my $unionName = 'fakeUnion'; my $selectedUnionSubtype = 'selected'; my $selectedUnionSubtypeObject = EBox::Types::Abstract->new( fieldName => $selectedUnionSubtype, printableName => $selectedUnionSubtype, ); my $unselectedUnionSubtype = 'unselected'; my $unselectedUnionSubtypeObject = EBox::Types::Abstract->new( fieldName => $unselectedUnionSubtype, printableName => $unselectedUnionSubtype, ); my $unionObject = new Test::MockObject(); $unionObject->set_isa('EBox::Types::Union', 'EBox::Types::Abstract'); $unionObject->set_always('fieldName', $unionName); $unionObject->set_always('selectedType', $selectedUnionSubtype); $unionObject->set_always('subtypes', [ $selectedUnionSubtypeObject, $unselectedUnionSubtypeObject, ]); $unionObject->set_always('subtype', $selectedUnionSubtypeObject); $row->addElement($unionObject); ok $row->elementExists($unionName), 'checking that union object exists using elementExists'; ok $row->elementExists($selectedUnionSubtype), 'checking that selected union-subtype object exists using elementExists'; ok ( not $row->elementExists($unselectedUnionSubtype) ), 'checking that unselected union-subtype object does not exist for elementExists'; is_deeply $row->elementByName($unionName), $unionObject, 'checking that elementByName can return the union object itself if requested'; is_deeply( $row->elementByName($selectedUnionSubtype), $selectedUnionSubtypeObject, 'checking that elementByName returns the selected union-subtype object if requested' ); is $row->elementByName($unselectedUnionSubtype), undef, 'checking that elementByName return undef when requested a unselected union subtype'; } sub filesToRemoveTest : Test(3) { my ($self) = @_; my $row = $self->_newRow(); my $element1 = _filesPaths('element1'); my $element2 = _filesPaths('element2'); $row->addElement( new EBox::Types::Abstract( fieldName => "1", printableName => "1", ) ); $row->addElement( new EBox::Types::Abstract( fieldName => "2", printableName => "2", ) ); $row->addElement($element1); $row->addElement( new EBox::Types::Abstract( fieldName => "5", printableName => "5", ) ); $row->addElement($element2); is_deeply( $row->filesPaths(), [], 'Checking filesPaths when there are not files to remove' ); my @element1Files = qw(aFile); $row->elementByName('element1')->_setFilesToRemoveIfDeleted(\@element1Files); is_deeply( $row->filesPaths(), [@element1Files], 'Checking filesPaths when there is a element with files to remove' ); my @element2Files = qw(bFile cFile); $row->elementByName('element2')->_setFilesToRemoveIfDeleted(\@element2Files); is_deeply( $row->filesPaths(), [@element1Files, @element2Files], 'Checking filesPaths when there are toe element with files to remove' ); } sub _filesPaths { my ($fieldName) = @_; my $object = new EBox::Types::Abstract( fieldName => $fieldName, printableName => $fieldName, ); $object = Test::MockObject::Extends->new( $object ); $object->mock('_setFilesToRemoveIfDeleted', sub { my ($self, $f) = @_; $self->{_filesToRemove} = $f; } ); $object->mock('filesPaths', sub { my ($self) = @_; return exists $self->{_filesToRemove} ? $self->{_filesToRemove} : []; } ); $object->can('filesPaths') or die 'AAAAAAAa'; return $object; } sub _populateRow { my ($self, $row) = @_; my @elementsToAdd; foreach my $i(0 .. 5) { my $el = new EBox::Types::Abstract( fieldName => "fieldName$i", printableName => "printableName$i", ); $el->setValue($i); push @elementsToAdd, $el; } foreach my $element (@elementsToAdd) { $row->addElement($element); } } sub _newRow { my $confmodule = EBox::Global->modInstance('fakeModule'); my $dataTableDir = 'DataTable'; my $rowDir = "$dataTableDir/Row"; my $row = EBox::Model::Row->new( confmodule => $confmodule, dir => $rowDir, ); $row->setId('FAKE_ID'); my $dataTable = EBox::Model::DataTable->new( confmodule => $confmodule, directory => $dataTableDir, domain => 'domain', ); my $mockDataTable = Test::MockObject::Extends->new($dataTable); $row->setModel( $mockDataTable ); return $row; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Model/DataTable/0000775000000000000000000000000012017102272017124 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Model/DataTable/Test.pm0000664000000000000000000011044112017102272020402 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Model::DataTable::Test; use lib '../../..'; use base 'EBox::Test::Class'; use strict; use warnings; use Test::More;; use Test::Exception; use Test::MockObject; use Test::MockObject::Extends; use Test::File; use Perl6::Junction qw(any); use POSIX; use EBox::Types::Abstract; use EBox::Model::Row; use EBox::Model::DataTable; use EBox::Model::Manager; use EBox::Types::Abstract; use EBox::Types::HasMany; use EBox::Types::Text; { my $rowIdUsed; no warnings 'redefine'; sub EBox::Model::Manager::warnIfIdIsUsed { my ($self, $context, $id) = @_; if (not defined $rowIdUsed) { return; } elsif ($rowIdUsed eq $id) { throw EBox::Exceptions::DataInUse('fake warnIfIdIsUsed: row in use'); } } sub EBox::Model::Manager::warnOnChangeOnId { my ($self, $tableName, $id) = @_; if (not defined $rowIdUsed) { return; } elsif ($rowIdUsed eq $id) { throw EBox::Exceptions::DataInUse('fake warnIfIdIsUsed: row in use'); } } sub EBox::Model::Manager::removeRowsUsingId { # do nothing } sub EBox::Model::Manager::modelActionTaken { # do nothing } sub setRowIdInUse { my ($rowId) = @_; $rowIdUsed = $rowId; } } sub setEBoxModules : Test(setup) { EBox::TestStubs::fakeEBoxModule(name => 'fakeModule'); } sub clearGConf : Test(teardown) { EBox::TestStubs::setConfig(); } sub deviantTableTest : Test(6) { my ($self) = @_; my @cases; push @cases, [ 'empty table description' => { tableName => 'test', } ]; push @cases, [ 'empty tableDescription' => { tableDescription => [], tableName => 'test', } ]; push @cases, [ 'repeated field name' => { tableDescription => [ new EBox::Types::Abstract( fieldName => 'repeated', ), new EBox::Types::Abstract( fieldName => 'repeated', ), ], tableName => 'test', } ]; push @cases, [ 'no table name' => { tableDescription => [ new EBox::Types::Abstract( fieldName => 'field1', ), ], } ]; # XXX this feature was temporally removed form DataTable # push @cases, [ # 'sortedBy uses unexistent field' => { # tableDescription => [ # new EBox::Types::Abstract( # fieldName => 'field1', # ), # ], # tableName => 'test', # sortedBy => 'unexistentField', # } # ]; push @cases, [ 'sortedBy and order are both set' => { tableDescription => [ new EBox::Types::Abstract( fieldName => 'field1', ), ], tableName => 'test', sortedBy => 'field1', order => 1, } ]; foreach my $case_r (@cases) { my ($caseName, $table) = @{ $case_r }; my $dataTable = $self->_newDataTable($table); dies_ok { $dataTable->table(); } "expecting error with deviant table case: $caseName"; } } sub tableTest : Test(6) { my ($self) = @_; my @cases; push @cases, [ 'simple table' => { tableDescription => [ new EBox::Types::Abstract( fieldName => 'field1', ), ], tableName => 'test', } ]; push @cases, [ 'sorted table' => { tableDescription => [ new EBox::Types::Abstract( fieldName => 'field1', ), new EBox::Types::Abstract( fieldName => 'field2', ), ], tableName => 'test', sortedBy => 'field1', } ]; push @cases, [ 'ordered by user table' => { tableDescription => [ new EBox::Types::Abstract( fieldName => 'field1', ), new EBox::Types::Abstract( fieldName => 'field2', ), ], tableName => 'test', order => 1, } ]; foreach my $case_r (@cases) { my ($caseName, $table) = @{ $case_r }; my $dataTable = $self->_newDataTable($table); my $tableFromModel; lives_ok { $tableFromModel = $dataTable->table(); } "checking first call to table method with: $caseName"; ok exists $tableFromModel->{tableDescriptionByName}, 'checking that some fileds were inserted by first time setup'; } } sub contextNameTest : Test(2) { my ($self) = @_; my $dataTable = $self->_newDataTable(); my $expectedContenxtName = '/fakeModule/test/'; is $dataTable->contextName, $expectedContenxtName, 'checking contextName'; # text with index $dataTable->set_always('index' => 'indexed'); $expectedContenxtName = '/fakeModule/test/indexed'; is $dataTable->contextName, $expectedContenxtName, 'checking contextName for model with index'; } sub deviantAddTest : Test(4) { my ($self) = @_; my $tableDescription = _tableDescription4fields(); my $dataTable = $self->_newDataTable($tableDescription); # add one row $dataTable->add(uniqueField => 'a', regularField => 'regular'); my %invalidAdds = ( 'unique field repeated' => [ uniqueField => 'a', regularField =>'adaads', ], 'missing required field' => [ uniqueField => 'c', ], ); my $dataTableSize = $dataTable->size(); while (my ($testName, $addParams_r) = each %invalidAdds) { dies_ok { $dataTable->add( @{ $addParams_r }); } "expecting error with incorrect row addition: $testName"; is $dataTable->size(), $dataTableSize, 'checking wether no new rows were added using size method'; } } sub addTest : Test(25) { my ($self) = @_; my $tableDescription = _tableDescription4fields(); my $dataTable = $self->_newDataTable($tableDescription); my @correctAdds = ( # only mandatory [ uniqueField => 'a', regularField => 'regular' ], # value for default field [ uniqueField => 'b', regularField => 'regular', defaultField => 'noDefaultText' ], # value for optional field [ uniqueField => 'c', regularField => 'regular', optionalField => 'noDefaultText' ], ); my @addParams; my %expectedAddedRowFields; $dataTable->mock(validateTypedRow => sub { my @callParams = @_; my %expectedChanged = @addParams; if (not exists $expectedChanged{defaultField}) { # defualt field always is added with its default value $expectedChanged{defaultField} = 'defaultText'; } # for and add call changed and all fields are the same my %expectedAll = %expectedChanged; _checkValidateTypedRowCall( callParams => \@callParams, expectedAction => 'add', expectedChanged => \%expectedChanged, expectedAll => \%expectedAll, ); } ); $dataTable->mock(addedRowNotify => sub { my @callParams = @_; _checkAddeRowNotifyCall( callParams => \@callParams, expectedFields => \%expectedAddedRowFields ) } ); foreach my $addCase (@correctAdds) { @addParams = @{ $addCase }; %expectedAddedRowFields = @addParams; if (not exists $expectedAddedRowFields{defaultField}) { # default field always is added with its default value $expectedAddedRowFields{defaultField} = 'defaultText'; } if (not exists $expectedAddedRowFields{optionalField}) { # optional field exists with undef value $expectedAddedRowFields{optionalField} = undef; } my $rowId; lives_ok { $rowId = $dataTable->add( @addParams ); } "adding correct rows => @addParams"; $dataTable->called_ok('validateTypedRow'); $dataTable->called_ok('addedRowNotify'); $dataTable->clear(); # clear mock object call registed my $row = $dataTable->row($rowId); _checkRow( row => $row, expectedFields => \%expectedAddedRowFields, testName => 'checking new added row after retrieval' ); } is $dataTable->size(), scalar @correctAdds, 'Checking data table size after the additions'; } # XXX TODO: # deviant test up and down in no-prderer table # straight test of moving up and down sub moveRowsTest : Test(8) { my ($self) = @_; my $tableDescription = _tableDescription4fields(); $tableDescription->{order} = 1; my $dataTable = $self->_newDataTable($tableDescription); $dataTable->set_true('movedUpRowNotify', 'movedDownRowNotify'); my @tableRows = ( [ uniqueField => 'a', regularField => 'regular' ], [ uniqueField => 'b', regularField => 'regular', ], ); foreach (@tableRows) { $dataTable->add( @{ $_ } ); } my @order = @{ $dataTable->order() }; my $upperRow = $order[0]; my $lowerRow = $order[1]; $dataTable->moveUp($upperRow); is_deeply $dataTable->order, \@order, 'checking that moving up the upper row has not changed the order'; ok ((not $dataTable->called('movedUpRowNotify')), 'Checking that movedUpRowNotify has not been triggered'); $dataTable->clear(); $dataTable->moveDown($lowerRow); is_deeply $dataTable->order, \@order, 'checking that moving down the lower row has not changed the order'; ok ((not $dataTable->called('movedDownRowNotify')), 'Checking that movedDownRowNotify has not been triggered'); $dataTable->clear(); my @reverseOrder = reverse @order; $dataTable->moveUp($lowerRow); is_deeply $dataTable->order, \@reverseOrder, 'checking that lower row was moved up'; ok ( $dataTable->called('movedUpRowNotify'), 'Checking that movedUpRowNotify has been triggered'); $dataTable->clear(); $dataTable->moveDown($lowerRow); is_deeply $dataTable->order, \@order, 'checking that upper row was moved down'; ok ( $dataTable->called('movedDownRowNotify'), 'Checking that movedDownRowNotify has been triggered'); $dataTable->clear(); } sub removeAllTest : Test(8) { my ($self) = @_; my $dataTable = $self->_newPopulatedDataTable(); lives_ok { $dataTable->removeAll(0); } 'removeAll without force in a table without autoremove'; is $dataTable->size, 0, 'checking that after removing all rows the table is empty'; lives_ok { $dataTable->removeAll(); } 'call removeAll in a empty table'; $dataTable = $self->_newPopulatedDataTableWithAutomaticRemove(); my $rowId = $dataTable->rows()->[0]->id(); setRowIdInUse($rowId); throws_ok { $dataTable->removeAll(0) } 'EBox::Exceptions::DataInUse', 'Checking removeAll without force with autoremove and used files'; lives_ok { $dataTable->removeAll(1) } 'Checking removeAll with force with autoremove and used files'; is $dataTable->size, 0, 'checking that after removing all rowswith force=1 the table is empty'; # automatic remve with no row used case setRowIdInUse(undef); $dataTable = $self->_newPopulatedDataTableWithAutomaticRemove(); lives_ok { $dataTable->removeAll(0) } 'Checking removeAll withoy force with autoremove option but no used rows'; is $dataTable->size, 0, 'checking that after removing all rows with force-0 but not used rows the table is empty'; } sub removeRowTest : Test(13) { my ($self) = @_; my $dataTable; my $id; my $notifyMethodName = 'deletedRowNotify'; $dataTable = $self->_newPopulatedDataTable(); $dataTable->can($notifyMethodName) or die "bad notify method name $notifyMethodName"; $dataTable->set_true($notifyMethodName); my @ids = map { $_->id() } @{ $dataTable->rows() }; dies_ok { $dataTable->removeRow('inexistent'); } 'expecting error when trying to remove a inexistent row'; ok ( (not $dataTable->called($notifyMethodName)), 'checking that on error notify method was not called', ); $id = shift @ids; lives_ok { $dataTable->removeRow($id); } 'removing row'; is $dataTable->row($id), undef, 'checking that row is not longer in the table'; $dataTable->called_ok($notifyMethodName); $dataTable->clear(); # tests with automatic remove $dataTable = $self->_newPopulatedDataTableWithAutomaticRemove(); $dataTable->set_true($notifyMethodName); @ids = map { $_->id() } @{ $dataTable->rows() }; $id = shift @ids; setRowIdInUse($id); throws_ok { $dataTable->removeRow($id, 0) } 'EBox::Exceptions::DataInUse', 'removeRow in a row reported as usedin a automaticRemove table raises DataInUse execption'; ok ( (not $dataTable->called($notifyMethodName)), 'checking that on DataInUse excpeion notify method was not called', ); lives_ok { $dataTable->removeRow($id, 1) } 'removeRow with force in a used row within a automaticRemove table works'; is $dataTable->row($id), undef, 'checking that row is not longer in the table'; $dataTable->called_ok($notifyMethodName); $dataTable->clear(); $id = shift @ids; lives_ok { $dataTable->removeRow($id, 0) } 'removeRow with force in a unused row within a automaticRemove table works'; is $dataTable->row($id), undef, 'checking that row is not longer in the table'; $dataTable->called_ok($notifyMethodName); $dataTable->clear(); } sub deviantSetTest : Test(12) { my ($self) = @_; my $dataTable = $self->_newPopulatedDataTable(); my @ids = map { $_->id() } @{ $dataTable->rows() }; my $id = shift @ids; my $notifyMethodName = 'updatedRowNotify'; my $repeatedUnique = $dataTable->row($ids[0])->valueByName('uniqueField'); $self->_checkDeviantSet( $dataTable, $id, { uniqueField => $repeatedUnique, regularField => 'distinctData', defaultField => 'aa', }, 'Checking that setting repeated unique field raises error' ); $self->_checkDeviantSet( $dataTable, $id, { inexistentField => 'inexistentData', uniqueField => 'zaszxza', regularField => 'distinctData', defaultField => 'aa', }, 'Checking that setting a inexistent field raises error' ); $dataTable->mock('validateTypedRow' => sub { die 'always fail' }); $self->_checkDeviantSet( $dataTable, $id, { uniqueField => 'zaszxza', regularField => 'distinctData', defaultField => 'aa', }, 'Checking error when validateTypedRow fails' ); } sub _checkDeviantSet # counts as 4 tests { my ($self, $dataTable, $id, $params_r, $testName) = @_; my $notifyMethodName = 'updatedRowNotify'; my $version = $dataTable->_storedVersion(); my $oldValues = $dataTable->row($id)->hashElements(); dies_ok { $dataTable->set( $id, %{ $params_r } ); } $testName; is_deeply $dataTable->row($id)->hashElements, $oldValues, 'checking that erroneous operation has not changed the row values'; is $version, $dataTable->_storedVersion(), 'checking that stored table version has not changed after incorrect set operation'; ok ( (not $dataTable->called($notifyMethodName)), 'checking that on error notify method was not called', ); } sub _checkSet { my ($self, $dataTable, $id, $changeParams_r, $testName) = @_; my $notifyMethodName = 'updatedRowNotify'; my %changeParams = %{ $changeParams_r }; my $oldSize = $dataTable->size(); my $version = $dataTable->_storedVersion(); lives_ok { $dataTable->set ( $id, %changeParams, ); } $testName; my $row = $dataTable->row($id); while (my ($field, $value) = each %changeParams) { ($field eq 'force') and next; is $row->valueByName($field), $value, "testing if $field has the updated value"; } is $dataTable->_storedVersion, ($version + 1), 'checking that stored version has been incremented'; is $dataTable->size(), $oldSize, 'checking that table size has not changed after the setRow'; $dataTable->called_ok($notifyMethodName); $dataTable->clear(); } # XXX tODO add notification method parameters test sub setTest : Test(10) { my ($self) = @_; my $dataTable = $self->_newPopulatedDataTable(); my @ids = map { $_->id() } @{ $dataTable->rows() }; my $id = shift @ids; my $notifyMethodName = 'updatedRowNotify'; $dataTable->set_true($notifyMethodName); my %changeParams = ( regularField => 'distinctData', uniqueField => 'newUniqueValue', defaultField => 'aaa', ); $self->_checkSet( $dataTable, $id, \%changeParams, 'Setting row', ); my $version = $dataTable->_storedVersion(); lives_ok { $dataTable->set ( $id, %changeParams, ); } 'Setting row with the same values'; is $version, $dataTable->_storedVersion(), 'checking that stored table version has not changed'; ok ( (not $dataTable->called($notifyMethodName)), 'checking that on setting row with no changes notify method was not called', ); } sub setWithDataInUseTest : Test(18) { my ($self) = @_; my $dataTable = $self->_newPopulatedDataTableWithAutomaticRemove(); my @ids = map { $_->id() } @{ $dataTable->rows() }; my $id = shift @ids; my $notifyMethodName = 'updatedRowNotify'; $dataTable->set_true($notifyMethodName); setRowIdInUse($id); my %changeParams = ( regularField => 'distinctData', uniqueField => 'newUniqueValue', defaultField => 'aaa', ); $self->_checkDeviantSet ( $dataTable, $id, \%changeParams, 'Checking that setting a row with data on use raises error' ); $changeParams{force} = 1; $self->_checkSet ( $dataTable, $id, \%changeParams, 'Checking that setting a row with data on use and force =1 works' ); delete $changeParams{force}; setRowIdInUse(undef); $changeParams{defaultField} = 'anotherValue'; $self->_checkSet ( $dataTable, $id, \%changeParams, 'Checking that setting a row with no data on use and force =0 works in a automaticRemoveTable' ); } sub _checkValidateTypedRowCall { my %params = @_; my $expectedAction = $params{expectedAction}; my %expectedChangedFields = %{ $params{expectedChanged} } ; my %expectedAllFields = %{ $params{expectedAll} } ; my ($dataTable, $action, $changedFields_r, $allFields_r) = @{ $params{callParams} }; my %changedFields = %{ $changedFields_r }; my %allFields = %{ $allFields_r }; foreach (values %changedFields) { $_ = $_->value(); } foreach (values %allFields) { $_ = $_->value(); } is $action, $expectedAction, "checking action parameter in validateTypedRow"; is_deeply \%changedFields, \%expectedChangedFields, 'checking changedFields names in validateTypeRow'; is_deeply \%allFields , \%expectedAllFields, 'checkinf allFields names in validateTypeRow'; } sub _checkAddeRowNotifyCall { my %params = @_; my ($dataTable, $row) = @{ $params{callParams} }; _checkRow( row => $row, expectedFields => $params{expectedFields}, testName => 'checking row contents in addedRowNotify', ); } sub _checkRow { my %params = @_; my $row = $params{row}; my %expectedFields = %{ $params{expectedFields} }; my $testName = $params{testName}; $testName or $testName = 'checking row'; my %valueHash = %{ $row->hashElements }; foreach (values %valueHash) { $_ = $_->value(); } is_deeply \%valueHash, \%expectedFields, $testName ; } sub optionsFromForeignModelTest : Test(2) { my ($self) = @_; my $tableDescription = { tableDescription => [ new EBox::Types::Text( fieldName => 'field1', printableName => 'field1', unique => 1, ), new EBox::Types::Text( fieldName => 'field2', printableName => 'field2', ), ], tableName => 'test', }; my $dataTable = $self->_newDataTable($tableDescription); my @field1Values= qw(a b c); foreach my $value (@field1Values) { $dataTable->add(field1 => $value, field2 => 'irrelevant'); } dies_ok { $dataTable->optionsFromForeignModel('inexistentField'); }'expecting error when using a inexistent field for optionsFromForeignModel'; my $field = 'field1'; my @expectedOptions = map { { value => $_->id(), printableValue => $_->printableValueByName($field), } } @{ $dataTable->rows() }; my $options= $dataTable->optionsFromForeignModel($field); is_deeply $options, \@expectedOptions, 'checking optionsFromForeignModel for a existent field'; } sub findTest : Test(6) { my ($self) = @_; my $dataTable = $self->_newPopulatedDataTable(); my $fieldName = 'uniqueField'; my $fieldValue = 'b'; my $row; dies_ok { $dataTable->find('inexistentField' => 'b'); } 'checking that find() with a inexistent field fails' ; $row = $dataTable->find($fieldName => 'inexistent'); ok ((not defined $row), 'checking that find() with a inexistent value returns undef' ); $row = $dataTable->find($fieldName => $fieldValue); isa_ok ($row, 'EBox::Model::Row', 'checking that find with row name and value returns a row' ); my $rowfound = $dataTable->findRow($fieldName => $fieldValue); is $row->id(), $rowfound->id(), 'checking return value of findRow method'; my $idfound = $dataTable->findId($fieldName => $fieldValue); is $idfound, $row->id(), 'checking return value of findId metthod'; my $valueFound = $dataTable->findValue($fieldName => $fieldValue); is $valueFound->id(), $row->id(), 'checking return value of findValue method'; } sub filterTest : Test(5) { my ($self) = @_; my $dataTable = $self->_newPopulatedDataTable(); $dataTable->add( 'uniqueField' => 'x', 'regularField' => 'onceRepeated twiceRepeated', ); $dataTable->add( 'uniqueField' => 'z', 'regularField' => 'twiceRepeated', ); my %filterAndRowsExpected = ( 'twiceRepeated' => 2, 'onceRepeated' => 1, 'twiceRepeated onceRepeated' => 1, 'onceRepeated zeroRepeated' => 0, 'zeroRepeated' => 0, ); while (my ($filter, $rowsExpected) = each %filterAndRowsExpected) { $dataTable->setFilter($filter); my $nRows = scalar @{ $dataTable->rows($filter) }; is $nRows, $rowsExpected, "Checking number of rows returned with filter: $filter"; } } sub pageTest : Test(38) { my ($self) = @_; my $rows = 20; my $dataTable = $self->_newDataTable($self->_tableDescription4fields()); foreach (1 .. $rows) { $dataTable->add( uniqueField => $_, regularField => "regular for $_", ); } my @pagesSizes = (1, 5, 7, 11); foreach my $size (@pagesSizes) { my %rowsSeen = (); lives_ok { $dataTable->setPageSize($size) } "Setting page size to $size"; my $pageCount = POSIX::ceil($rows / $size); my $lastPage = $pageCount - 1; my $lastPageRows = $rows - ( ($pageCount -1) * $size ); foreach my $page (0 .. $lastPage) { my $expectedRows = ($page != $lastPage) ? $size : $lastPageRows; my @rows = @{ $dataTable->rows(undef, $page) }; foreach my $row (@rows) { my $id = $row->id(); if (exists $rowsSeen{$id}) { fail "Row with id $id was previously returned i nanother page"; next; } $rowsSeen{$id} = 1; } is scalar @rows, $expectedRows, "Checking expected number of rows ($expectedRows) for page $page with size $size"; } } dies_ok { $dataTable->rows(undef, -1); } 'Check that rows() raises error when requested a negative page'; $dataTable->setPageSize($rows); is_deeply $dataTable->rows(undef, 0), $dataTable->rows(undef, 1), 'Checking that a number greater than available page means last page'; dies_ok { $dataTable->setPageSize(-1); } 'Setting page size to a negative number must raise error'; $dataTable->setPageSize($rows + 1); is scalar @{ $dataTable->rows(undef, 1) }, $rows, 'Checking that a apge size greater than the number of rows returns all the rows'; $dataTable->setPageSize(0); is scalar @{ $dataTable->rows(undef, 1) }, $rows, 'Checking that pageZise ==0 means unlimited page size'; } sub _newDataTable { my ($self, $table) = @_; if (not defined $table) { $table = { tableDescription => [ new EBox::Types::Abstract( fieldName => 'field1', printableName => 'field1', ), ], tableName => 'test', }; } my $confmodule = EBox::Global->modInstance('fakeModule'); my $dataTableDir = '/ebox/modules/fakeModule/DataTable'; # remove old data from prvious modules $confmodule->delete_dir($dataTableDir); my $dataTableBase = EBox::Model::DataTable->new( confmodule => $confmodule, directory => $dataTableDir, domain => 'domain', ); my $dataTable = Test::MockObject::Extends->new($dataTableBase); $dataTable->set_always('_table' => $table); return $dataTable; } sub _newPopulatedDataTable { my ($self) = @_; my $tableDescription = _tableDescription4fields(); my $dataTable = $self->_newDataTable($tableDescription); my @values = ( [ uniqueField => 'a', regularField => 'regular' ], [ uniqueField => 'b', regularField => 'regular', defaultField => 'noDefaultText' ], [ uniqueField => 'c', regularField => 'regular', optionalField => 'noDefaultText' ], ); foreach (@values) { $dataTable->add( @{ $_ } ); } return $dataTable; } sub _newPopulatedDataTableWithAutomaticRemove { my ($self) = @_; my $tableDescription = _tableDescription4fields(); $tableDescription->{automaticRemove} = 1; my $dataTable = $self->_newDataTable($tableDescription); my @values = ( [ uniqueField => 'a', regularField => 'regular' ], [ uniqueField => 'b', regularField => 'regular', defaultField => 'noDefaultText' ], [ uniqueField => 'c', regularField => 'regular', optionalField => 'noDefaultText' ], ); foreach (@values) { $dataTable->add( @{ $_ } ); } return $dataTable; } sub _tableDescription4fields { my $tableDescription = { tableDescription => [ new EBox::Types::Text( fieldName => 'uniqueField', printableName => 'uniqueField', unique => 1, ), new EBox::Types::Text( fieldName => 'regularField', printableName => 'regularField', ), new EBox::Types::Text( fieldName => 'defaultField', printableName => 'defaultField', defaultValue => 'defaultText', ), new EBox::Types::Text( fieldName => 'optionalField', printableName => 'optionalField', optional => 1, ), ], tableName => 'test', }; return $tableDescription; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Model/ImageControl.pm0000664000000000000000000000606712017102272020235 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Model::ImageControl; # use strict; use warnings; use base 'EBox::Model::DataForm'; use EBox::Gettext; sub new { my ($class, @params) = @_; my $self = $class->SUPER::new(@params); bless( $self, $class ); return $self; } # Method: _table # # Overrides: # # # sub _table { my ($self) = @_; my $tableDesc = $self->_tableDesc(); my $modelDomain = $self->_modelDomain(); my $dataForm = { tableName => $self->nameFromClass, printableTableName => $self->printableTableName, modelDomain => $modelDomain, defaultActions => [ 'editField', 'changeView' ], tableDescription => $tableDesc, messages => $self->_messages(), # class => 'dataForm', }; return $dataForm; } sub _messages { my ($self) = @_; return { 'add' => undef, 'del' => undef, 'update' => undef, 'moveUp' => undef, 'moveDown' => undef, }; } sub _tableDesc { throw EBox::Exceptions::NotImplemented; } sub _modelDomain { my ($self) = @_; my $imageModel = $self->_imageModel(); my $imageTable = $imageModel->_table(); return $imageTable->{modelDomain}; } sub Viewer { return '/ajax/imageControl.mas'; } # custom changeRowJS to update the list sub changeRowJS { my ($self, $editId, $page) = @_; my $functionName = $self->name . 'Update'; my $superJS = $self->SUPER::changeRowJS($editId, $page); my $function = 'applyChangeToImage("%s", "%s", %s, "%s")'; my $table = $self->_imageModel->table(); my $fields = $self->_paramsWithSetterJS(); $fields =~ s/'/"/g; my $ownJS = sprintf ($function, $table->{'actions'}->{'editField'}, $table->{'tableName'}, $fields, $table->{'confdir'}, 0, # force ); my $JS = "var $functionName = function() { $superJS; $ownJS; return false }; $functionName()"; return $JS; } sub printableTableName { return ''; } sub _imageModel { throw EBox::Exceptions::NotImplemented; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Model/Component.pm0000664000000000000000000001265212017102272017611 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Model::Component # # This class is intended to provide common methods which are used # by and . # package EBox::Model::Component; use strict; use warnings; use EBox::Global; use EBox::Gettext; use EBox::Exceptions::InvalidType; use EBox::Exceptions::MissingArgument; use Encode; use Error qw(:try); use POSIX qw(getuid); # Method: parentModule # # Get the parent confmodule for the model # # Returns: # # - the module # sub parentModule { my ($self) = @_; return $self->{'confmodule'}; } # Method: global # # returns a EBox::Global instance with the correct read-only status # sub global { my ($self) = @_; return $self->{'confmodule'}->global(); } # Method: modelGetter # # return a sub which is a getter of the specified model from the specified # module. Useful for foreignModel attribute # # Parameters: # module # model sub modelGetter { my ($self, $module, $model) = @_; my $global = $self->global(); my $modelInstance = $global->modInstance($module)->model($model); return sub{ return $modelInstance; }; } # Method: pageTitle # # This method must be overriden by the component to show a page title # # Return: # # string or undef # sub pageTitle { my ($self) = @_; return undef; } # Method: headTitle # # This method must be overriden by the component to show a headTitle # # Return: # # string or undef # sub headTitle { my ($self) = @_; return undef; } # Method: help # # Get the help message from the model # # Returns: # # string - containing the i18n help message # sub help { return ''; } # Method: keywords # # Returns words related to the model, extracted from different sources such # as row names, help, ..., that can be used to make lookups from words to # models, menus, ... # # Return: # # string array - the keywords # sub keywords { my ($self) = @_; my $help = $self->help(); Encode::_utf8_on($help); return [split('\W+', lc($help))]; } # Method: parent # # Return component's parent. # If the component is child of a composite the parent is the top's composite parent # # Returns: # # An instance of a class implementing or # sub parent { my ($self) = @_; return $self->{'parent'}; } # Method: parentRow # # Is the component is a submodel of a DataTable return the row where the # parent model resides # # Returns: # # row object or undef if there is not # sub parentRow { my ($self) = @_; unless ($self->{parent}) { return undef; } my $dir = $self->directory(); my @parts = split ('/', $dir); my $rowId = undef; for (my $i = scalar (@parts) - 1; $i > 0; $i--) { if (($parts[$i] eq 'form') or ($parts[$i - 1] eq 'keys')) { $rowId = $parts[$i]; last; } } if (not defined $rowId) { return undef; } my $row = $self->{parent}->row($rowId); unless ($row) { throw EBox::Exceptions::Internal("Cannot find row with rowId $rowId. Component directory: $dir."); } return $row; } # Method: menuFolder # # Override this function if you model is placed within a folder # from other module # sub menuFolder { return undef; } # Method: disabledModuleWarning # # Return the warn message to inform if the parent module is disabled # # Returns: # # String - The warn message if the module is disabled # # Empty string - if module is enabled # sub disabledModuleWarning { my ($self) = @_; # Avoid to show warning if running in usercorner's apache return '' unless (getuid() == getpwnam(EBox::Config::user())); my $pageTitle = $self->pageTitle(); if ($self->isa('EBox::Model::DataTable')) { my $htmlTitle = @{$self->viewCustomizer()->HTMLTitle()}; # Do not show warning on nested components return '' unless ($pageTitle or $htmlTitle); } elsif ($self->isa('EBox::Model::Composite')) { return '' unless ($pageTitle); } else { return ''; } my $module = $self->parentModule();; unless (defined ($module) and $module->isa('EBox::Module::Service')) { return ''; } if ($module->isEnabled()) { return ''; } else { # TODO: If someday we implement the auto-enable for dependencies with one single click # we could replace the Module Status link with a "Click here to enable it" one return __x("{mod} module is disabled. Don't forget to enable it on the {oh}Module Status{ch} section, otherwise your changes won't have any effect.", mod => $module->printableName(), oh => '', ch => ''); } } 1; zentyal-core-2.3.21+quantal1/src/EBox/Logs/0000775000000000000000000000000012017102272015147 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Logs/Composite/0000775000000000000000000000000012017102272017111 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Logs/Composite/ConfigureLog.pm0000664000000000000000000000231212017102272022030 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Logs::Composite::ConfigureLog package EBox::Logs::Composite::ConfigureLog; use base 'EBox::Model::Composite'; use strict; use warnings; use EBox::Gettext; # Group: Protected methods # Method: _description # # Overrides: # # # sub _description { my $description = { layout => 'top-bottom', name => 'ConfigureLog', compositeDomain => 'Logs', printableName => __('Configure Logs'), }; return $description; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Logs/Composite/General.pm0000664000000000000000000000361612017102272021032 0ustar # Copyright (C) 2011-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Logs::Composite::General # # This class is used to manage the logs module within a single # element whose components are: # and # inside a tabbed layout. package EBox::Logs::Composite::General; use base 'EBox::Model::Composite'; use strict; use warnings; use EBox::Gettext; # Group: Public methods # Constructor: new # # Constructor for the general events composite # # Returns: # # - a # general events composite # sub new { my $class = shift; my $self = $class->SUPER::new(@_); return $self; } # Group: Protected methods # Method: _description # # Overrides: # # # sub _description { my $description = { layout => 'tabbed', name => 'General', printableName => __('Logs'), pageTitle => __('Logs'), headTitle => undef, compositeDomain => 'Logs', help => __('Logs module allows you to register and query ' . 'information about the Zentyal services'), }; return $description; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Logs/Consolidate/0000775000000000000000000000000012017102272017413 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Logs/Consolidate/Test.pm0000664000000000000000000015350012017102272020674 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Logs::Consolidate::Test; use strict; use warnings; use base 'EBox::Test::Class'; use EBox::Test; use Perl6::Junction qw(any all); use Test::Exception; use Test::More; use Test::MockObject; use Data::Dumper; use lib '../../..'; use EBox::Logs::Consolidate; use EBox::TestStubs; sub weeklyDateTest #: Test(6) { my %cases = ( "2008-01-02 13:42:12" => "2007-12-31 00:00:00", # year leap "2008-03-01 12:12:00" => "2008-2-25 00:00:00", # month leap "2008-02-17 12:12:00" => "2008-2-11 00:00:00", # sunday "2008-02-18 12:12:00" => "2008-2-18 00:00:00", # monday "2008-02-27 12:12:00" => "2008-2-25 00:00:00", # thursday "2008-02-29 12:12:00" => "2008-2-25 00:00:00", # friday ); while (my ($timeStamp, $expectedDate) = each %cases) { my $date = EBox::Logs::Consolidate->_weeklyDate($timeStamp); is $date, $expectedDate, "checking weekly date for time stam $timeStamp"; } } # XXX DELETE operation is not tested my %tableInfoByMod; sub fakeTableInfoFromMod { my ($self, $modName) = @_; return $tableInfoByMod{$modName}; } sub setFakeTableInfoForMod { my ($modName, $tableInfo) = @_; $tableInfoByMod{$modName} = $tableInfo; } sub fakeConsolidate : Test(startup) { Test::MockObject->fake_module( 'EBox::Logs::Consolidate', _tableInfoFromMod => \&fakeTableInfoFromMod, _sourceRows => \&fakeSourceRows, _cleanRows => sub {}, # we donot test last consolidation dates for # now _lastConsolidationDate => sub { return undef }, _updateLastConsolidationDate => sub { }, ); } sub fakeDBEngineFactory : Test(startup) { Test::MockObject->fake_module( 'EBox::DBEngineFactory', DBEngine => \&fakeDBEngine, ); } my %fakeDB = (); my $dbengine; sub fakeDBEngine { if ($dbengine) { return $dbengine; } $dbengine = Test::MockObject->new(); $dbengine->mock('insert' => sub { my ($self, $table, $row) = @_; if (not exists $fakeDB{$table}) { $fakeDB{$table} = []; } push @{ $fakeDB{$table} }, $row; } ); $dbengine->mock('do' => sub { my ($self, $query) = @_; if (not $query =~ /^UPDATE/) { if ($query =~ /^DELETE/) { return; } die "Not mocked query $query"; } $query =~ m/UPDATE\s+(.*?)\s+SET\s+(.*)\s+WHERE/; my $table = $1; my $setPortion = $2; if (not exists $fakeDB{$table}) { # table is empty, not update return 0; } my %accummulator; foreach my $part (split ',', $setPortion) { $part =~ m/= (.*) \+ (.*)/; my ($col, $inc) = ($1, $2); $accummulator{$col} = $inc; } $query =~ s/^UPDATE.*WHERE.*\(//; $query =~ s/\).*$//; my %row; foreach my $pair (split ' AND ', $query) { my ($name, $v) = split ' = ', $pair; $v =~ s/^\'//g; $v =~ s/'$//g; $row{$name} = $v; } my $updated = 0; foreach my $row_r (@{ $fakeDB{$table} }) { my %dbRow = %{ $row_r }; my $notSame = 0; while (my ($key, $value) = each %row) { my $v = delete $dbRow{$key}; if ((not defined $v) or ($v ne $value)) { $notSame = 1; keys %row; # to reset internal counter last; } } if ($notSame) { next; } while (my ($col, $inc) = each %accummulator) { $row_r->{$col} += $inc; } $updated += 1; } return $updated; } ); return $dbengine; } sub fakeSourceRows { my ($self, $dbengine, $table, $dateCol) = @_; return $fakeDB{$table}; } sub setFakeDB { my ($table, $columns_r,) = @_; %fakeDB = ( $table => $columns_r ); } # Method : modNameAndClass # # must be overriden return the modName and class of the module tested. # default: returns undef, that means to use default values for a general test sub modNameAndClass { return (undef, undef); } sub _setupDB { my ($self, $dbRows_r, $consolidate) = @_; my ($modName, $modClass) = $self->modNameAndClass(); if (not defined $modName) { $modName = 'testMod'; EBox::TestStubs::fakeEBoxModule( name => $modName, subs => [ isEnabled => sub { return 1 }, tableInfo => sub { my ($mock) = @_; return $self->fakeTableInfoFromMod($mock->name); } # name => sub { return $modName }, ], isa => ['EBox::LogObserver'], ); } my $tableInfo; if (defined $modClass) { $tableInfo = $modClass->tableInfo(); } else { # my %titles = map { $_ => $_ } @{ $dbColumns_r }; my %titles = (); $tableInfo = { name => $modName, index => $modName, titles => \%titles, order => [ keys %titles ], tablename => 'testTable', timecol => 'date', filter => [], events => { eventOne => 'eventOne', eventTwo => 'eventTwo'}, eventcol => 'event', consolidate => $consolidate, }; } my @dbRows = @{ $dbRows_r }; setFakeDB($tableInfo->{tablename}, \@dbRows); setFakeTableInfoForMod($modName => $tableInfo); } sub _standardDbContent { return [ { date => '2008-08-24 13:12:36', event => 'eventOne', sender => 'bee@insects.com', recipient => 'macaco@monos.org', size => 321, }, { date => '2008-08-24 13:21:36', event => 'eventTwo', sender => 'bee@insects.com', recipient => 'macaco@monos.org', size => 341, }, { date => '2008-08-24 13:21:36', event => 'eventTwo', sender => 'snake@reptiles.net', recipient => 'macaco@monos.org', size => 21, }, { date => '2008-08-24 14:21:12', event => 'eventOne', sender => 'wasp@insects.com', recipient => 'macaco@monos.org', size => 521, }, { date => '2008-08-24 19:12:36', event => 'eventOne', sender => 'bee@insects.com', recipient => 'macaco@monos.org', size => 821, }, { date => '2008-08-25 13:12:36', event => 'eventOne', sender => 'bee@insects.com', recipient => 'macaco@monos.org', size => 121, }, { date => '2008-08-26 19:12:36', event => 'eventOne', sender => 'bee@insects.com', recipient => 'macaco@monos.org', size => 321, }, { date => '2008-08-26 20:12:36', event => 'eventOne', sender => 'bee@insects.com', recipient => 'mandrill@monos.org', size => 721, }, { date => '2008-08-26 20:12:36', event => 'eventOne', sender => 'bee@insects.com', recipient => 'macaco@monos.org', size => 121, }, ]; } # Method: consolidateTest # # override in client cases to provide a different test count! # it needs only to call runCases to run the tests sub consolidateTest : Test(32) { my ($self) = @_; $self->runCases(); } sub runCases { my ($self) = @_; my $cases = $self->cases(); foreach my $case_r (@{ $cases }) { $self->_checkConsolidate($case_r); } } sub _checkConsolidate { my ($self, $case) = @_; diag $case->{name}; $self->_setupDB( $case->{dbRows}, $case->{consolidate} ); my ($modName) = $self->modNameAndClass(); defined $modName or $modName = 'testMod'; lives_ok { EBox::Logs::Consolidate->consolidate($modName); } 'consolidate method does not raise any error'; my $dbEngine = fakeDBEngine(); $dbEngine->called_ok('insert'); $dbEngine->called_ok('do'); my @expectedRows = @{ $case->{expectedConsolidatedRows} }; my %expectedTables; foreach ( @expectedRows ) { my $table = $_->{table}; $expectedTables{$table} = 1; } my @dbRows; foreach my $table (keys %expectedTables) { if (not exists $fakeDB{$table}) { die "Expected table was not existent: $table"; } foreach my $row (@{ $fakeDB{$table} } ) { push @dbRows, { table => $table, value => $row, }; } } use Data::Dumper; # diag Dumper \@dbRows; is_deeply( _rowsToCompare(\@dbRows), _rowsToCompare(\@expectedRows), 'Checking database rows' ); $dbEngine->clear(); $dbEngine->{rows} = []; } sub _rowsToCompare { my ($rows_r) = @_; my %rtc = map { my @val = ($_->{table}, sort values %{ $_->{value} } ); my $valStr = join '-', @val; ($valStr => $_) } @{ $rows_r }; return \%rtc; } # Method: cases # # Provides runCases with cases to run # override in client classes to provide other cases sub cases { my @cases = ( { name => 'simple case', dbColumns => [qw(date event sender recipient comments)], dbRows => [ { date => '2008-08-24 13:12:36', event => 'eventOne', sender => 'bee@insects.com', recipient => 'macaco@monos.org', comments => '11faas sfa aa4 ge eqf efw4 w', }, { date => '2008-08-24 13:21:36', event => 'eventTwo', sender => 'bee@insects.com', recipient => 'macaco@monos.org', comments => '222faas sfa aa4 ge eqf efw4 w', }, { date => '2008-08-24 14:21:12', event => 'eventOne', sender => 'wasp@insects.com', recipient => 'macaco@monos.org', comments => '33faas sfa aa4 ge eqf efw4 w', }, { date => '2008-08-24 19:12:36', event => 'eventOne', sender => 'bee@insects.com', recipient => 'macaco@monos.org', comments => '444faas sfa aa4 ge eqf efw4 w', }, { date => '2008-08-25 13:12:36', event => 'eventOne', sender => 'bee@insects.com', recipient => 'macaco@monos.org', comments => '55faas sfa aa4 ge eqf efw4 w', }, { date => '2008-08-26 19:12:36', event => 'eventOne', sender => 'bee@insects.com', recipient => 'macaco@monos.org', comments => '666faas sfa aa4 ge eqf efw4 w', }, { date => '2008-08-26 20:12:36', event => 'eventOne', sender => 'bee@insects.com', recipient => 'madrill@monos.org', comments => '777faas sfa aa4 ge eqf efw4 w', }, { date => '2008-08-26 20:12:36', event => 'eventOne', sender => 'bee@insects.com', recipient => 'macaco@monos.org', comments => '888faas sfa aa4 ge eqf efw4 w', }, ], consolidate => { testTable => { consolidateColumns => { event => 1, sender => 1, }, }, }, expectedConsolidatedRows => [ { table => 'testTable_daily', value => { date => '2008-08-24 00:00:00', event => 'eventOne', sender => 'bee@insects.com', count => 2, }, }, { table => 'testTable_daily', value => { date => '2008-08-24 00:00:00', event => 'eventTwo', sender => 'bee@insects.com', count => 1, }, }, { table => 'testTable_daily', value => { date => '2008-08-24 00:00:00', event => 'eventOne', sender => 'wasp@insects.com', count => 1, }, }, { table => 'testTable_daily', value => { date => '2008-08-25 00:00:00', event => 'eventOne', sender => 'bee@insects.com', count => 1, }, }, { table => 'testTable_daily', value => { date => '2008-08-26 00:00:00', event => 'eventOne', sender => 'bee@insects.com', count => 3, }, }, ] }, # end case { name => 'case with a method to consolidate sender', dbColumns => [qw(date event sender recipient comments)], dbRows => [ { date => '2008-08-24 13:12:36', event => 'eventOne', sender => 'bee@insects.com', recipient => 'macaco@monos.org', comments => '11faas sfa aa4 ge eqf efw4 w' }, { date => '2008-08-24 13:21:36', event => 'eventTwo', sender => 'bee@insects.com', recipient => 'macaco@monos.org', comments => '222faas sfa aa4 ge eqf efw4 w' }, { date => '2008-08-24 13:21:36', event => 'eventTwo', sender => 'snake@reptiles.net', recipient => 'macaco@monos.org', comments => '222faas sfa aa4 ge eqf efw4 w' }, { date => '2008-08-24 14:21:12', event => 'eventOne', sender => 'wasp@insects.com', recipient => 'macaco@monos.org', comments => '33faas sfa aa4 ge eqf efw4 w' }, { date => '2008-08-24 19:12:36', event => 'eventOne', sender => 'bee@insects.com', recipient => 'macaco@monos.org', comments => '444faas sfa aa4 ge eqf efw4 w' }, { date => '2008-08-25 13:12:36', event => 'eventOne', sender => 'bee@insects.com', recipient => 'macaco@monos.org', comments => '55faas sfa aa4 ge eqf efw4 w' }, { date => '2008-08-26 19:12:36', event => 'eventOne', sender => 'bee@insects.com', recipient => 'macaco@monos.org', comments => '666faas sfa aa4 ge eqf efw4 w' }, { date => '2008-08-26 20:12:36', event => 'eventOne', sender => 'bee@insects.com', recipient => 'madrill@monos.org', comments => '777faas sfa aa4 ge eqf efw4 w' }, { date => '2008-08-26 20:12:36', event => 'eventOne', sender => 'bee@insects.com', recipient => 'macaco@monos.org', comments => '888faas sfa aa4 ge eqf efw4 w' }, ], consolidate => { testTable => { consolidateColumns => { event => 1, sender => sub { my ($value) = @_; my ($addr, $domain) = split '@', $value; return $domain; }, }, }, }, expectedConsolidatedRows => [ { table => 'testTable_daily', value => { date => '2008-08-24 00:00:00', event => 'eventOne', sender => 'insects.com', count => 3, }, }, { table => 'testTable_daily', value => { date => '2008-08-24 00:00:00', event => 'eventTwo', sender => 'insects.com', count => 1, }, }, { table => 'testTable_daily', value => { date => '2008-08-24 00:00:00', event => 'eventTwo', sender => 'reptiles.net', count => 1, }, }, { table => 'testTable_daily', value => { date => '2008-08-25 00:00:00', event => 'eventOne', sender => 'insects.com', count => 1, }, }, { table => 'testTable_daily', value => { date => '2008-08-26 00:00:00', event => 'eventOne', sender => 'insects.com', count => 3, }, }, ] }, # end case { name => 'case with accumulation (size by sender)', dbColumns => [qw(date event sender recipient size)], dbRows => _standardDbContent(), consolidate => { testTable => { accummulateColumns => {'size' => 0 }, consolidateColumns => { sender => 1, size => { accummulate => 'size', } }, }, }, expectedConsolidatedRows => [ { table => 'testTable_daily', value => { date => '2008-08-24 00:00:00', sender => 'bee@insects.com', size => 1483, }, }, { table => 'testTable_daily', value => { date => '2008-08-24 00:00:00', sender => 'wasp@insects.com', size => 521, }, }, { table => 'testTable_daily', value => { date => '2008-08-24 00:00:00', sender => 'snake@reptiles.net', size => 21, }, }, { table => 'testTable_daily', value => { date => '2008-08-25 00:00:00', sender => 'bee@insects.com', size => 121, }, }, { table => 'testTable_daily', value => { date => '2008-08-26 00:00:00', sender => 'bee@insects.com', size => 1163, }, }, ] }, # end case { name => 'case with two accumulation (n messages and size by sender)', dbColumns => [qw(date event sender recipient size)], dbRows => _standardDbContent(), consolidate => { testTable => { accummulateColumns => { 'size' => 0, 'messages' => 1, }, consolidateColumns => { sender => 1, size => { accummulate => 'size', } }, }, }, expectedConsolidatedRows => [ { table => 'testTable_daily', value => { date => '2008-08-24 00:00:00', sender => 'bee@insects.com', size => 1483, messages => 3, }, }, { table => 'testTable_daily', value => { date => '2008-08-24 00:00:00', sender => 'wasp@insects.com', size => 521, messages => 1, }, }, { table => 'testTable_daily', value => { date => '2008-08-24 00:00:00', sender => 'snake@reptiles.net', size => 21, messages => 1, }, }, { table => 'testTable_daily', value => { date => '2008-08-25 00:00:00', sender => 'bee@insects.com', size => 121, messages => 1, }, }, { table => 'testTable_daily', value => { date => '2008-08-26 00:00:00', sender => 'bee@insects.com', size => 1163, messages => 3, }, }, ] }, # end case { name => 'case with filtered out rows', dbColumns => [qw(date event sender recipient size)], dbRows => _standardDbContent(), consolidate => { testTable => { filter => sub { my ($row_r) = @_; return $row_r->{event} eq 'eventOne', }, consolidateColumns => { sender => 1, }, } }, expectedConsolidatedRows => [ { table => 'testTable_daily', value => { date => '2008-08-24 00:00:00', sender => 'bee@insects.com', count => 2, }, }, { table => 'testTable_daily', value => { date => '2008-08-24 00:00:00', sender => 'wasp@insects.com', count => 1, }, }, { table => 'testTable_daily', value => { date => '2008-08-25 00:00:00', sender => 'bee@insects.com', count => 1, }, }, { table => 'testTable_daily', value => { date => '2008-08-26 00:00:00', sender => 'bee@insects.com', count => 3, }, }, ] }, # end case { name => 'case with two tables', dbColumns => [qw(date event sender recipient size)], dbRows => _standardDbContent(), consolidate => { senderTable => { consolidateColumns => { sender => 1, }, }, recipientTable => { consolidateColumns => { recipient => 1, }, }, }, expectedConsolidatedRows => [ { table => 'senderTable_daily', value => { date => '2008-08-24 00:00:00', sender => 'bee@insects.com', count => 3, }, }, { table => 'senderTable_daily', value => { date => '2008-08-24 00:00:00', sender => 'wasp@insects.com', count => 1, }, }, { table => 'senderTable_daily', value => { date => '2008-08-24 00:00:00', sender => 'snake@reptiles.net', count => 1, }, }, { table => 'recipientTable_daily', value => { date => '2008-08-24 00:00:00', recipient => 'macaco@monos.org', count => 5, }, }, { table => 'senderTable_daily', value => { date => '2008-08-25 00:00:00', sender => 'bee@insects.com', count => 1, }, }, { table => 'recipientTable_daily', value => { date => '2008-08-25 00:00:00', recipient => 'macaco@monos.org', count => 1, }, }, { table => 'senderTable_daily', value => { date => '2008-08-26 00:00:00', sender => 'bee@insects.com', count => 3, }, }, { table => 'recipientTable_daily', value => { date => '2008-08-26 00:00:00', recipient => 'macaco@monos.org', count => 2, }, }, { table => 'recipientTable_daily', value => { date => '2008-08-26 00:00:00', recipient => 'mandrill@monos.org', count => 1, }, }, ] }, # end case { name => 'case with two accumulation from the same columnn', dbColumns => [qw(date event sender recipient size)], dbRows => _standardDbContent(), consolidate => { testTable => { accummulateColumns => { 'eventOne' => 0, 'eventTwo' => 0, }, consolidateColumns => { event => { conversor => sub { return 1; }, accummulate => sub { my ($v) = @_; return $v; }, } }, }, }, expectedConsolidatedRows => [ { table => 'testTable_daily', value => { date => '2008-08-24 00:00:00', eventOne => 3, eventTwo => 2, }, }, { table => 'testTable_daily', value => { date => '2008-08-25 00:00:00', eventOne => 1, }, }, { table => 'testTable_daily', value => { date => '2008-08-26 00:00:00', eventOne => 3, }, }, ], }, # end case { name => 'case with hourly consolodation and daily reconsolidation', dbColumns => [qw(date event sender recipient size)], dbRows => _standardDbContent(), consolidate => { testTable => { timePeriods => ['hourly', 'daily'], accummulateColumns => {'size' => 0 }, consolidateColumns => { sender => 1, size => { accummulate => 'size', } }, }, }, expectedConsolidatedRows => [ # hourly table { table => 'testTable_hourly', value => { date => '2008-08-24 13:00:00', sender => 'bee@insects.com', size => 662, }, }, { table => 'testTable_hourly', value => { date => '2008-08-24 19:00:00', sender => 'bee@insects.com', size => 821, }, }, { table => 'testTable_hourly', value => { date => '2008-08-24 13:00:00', sender => 'snake@reptiles.net', size => 21, }, }, { table => 'testTable_hourly', value => { date => '2008-08-24 14:00:00', sender => 'wasp@insects.com', size => 521, }, }, { table => 'testTable_hourly', value => { date => '2008-08-25 13:00:00', sender => 'bee@insects.com', size => 121, }, }, { table => 'testTable_hourly', value => { date => '2008-08-26 19:00:00', sender => 'bee@insects.com', size => 321, }, }, { table => 'testTable_hourly', value => { date => '2008-08-26 20:00:00', sender => 'bee@insects.com', size => 842, }, }, # daily table { table => 'testTable_daily', value => { date => '2008-08-24 00:00:00', sender => 'bee@insects.com', size => 1483, }, }, { table => 'testTable_daily', value => { date => '2008-08-24 00:00:00', sender => 'wasp@insects.com', size => 521, }, }, { table => 'testTable_daily', value => { date => '2008-08-24 00:00:00', sender => 'snake@reptiles.net', size => 21, }, }, { table => 'testTable_daily', value => { date => '2008-08-25 00:00:00', sender => 'bee@insects.com', size => 121, }, }, { table => 'testTable_daily', value => { date => '2008-08-26 00:00:00', sender => 'bee@insects.com', size => 1163, }, }, ] }, # end case ); return \@cases; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Logs/Consolidate.pm0000664000000000000000000003566012017102272017763 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Logs::Consolidate; use strict; use warnings; use EBox::Global; use EBox::DBEngineFactory; use Time::Piece; use Time::Seconds; use constant TIME_PERIODS => qw(hourly daily weekly monthly); # Method: consolidate # # consolidate data tables # # Parameters: # modName - name of the module to consolidate or 'all' to consolidate # all modules sub consolidate { my ($self, $modName) = @_; my @modNames; if ($modName eq 'all') { @modNames = @{ $self->_allModulesWithConsolidation() }; } else { if (not EBox::Global->modInstance($modName)->configured()) { return; } @modNames = ( $modName ); } foreach my $name (@modNames) { my @tableInfos = @{ $self->_tableInfosFromMod($name) }; foreach my $tableInfo (@tableInfos) { while (my ($destTable, $configuration) = each %{ $tableInfo->{consolidate} }) { my @timePeriods = TIME_PERIODS; my $firstTimePeriod = shift @timePeriods; $self->_consolidateTable( destinationTable => $destTable, configuration => $configuration, tableInfo => $tableInfo, timePeriod => $firstTimePeriod, ); my $prevTimePeriod = $firstTimePeriod; foreach my $timePeriod (@timePeriods) { $self->_reconsolidateTable( destinationTable => $destTable, configuration => $configuration, timePeriod => $timePeriod, sourceTimePeriod => $prevTimePeriod, ); $prevTimePeriod = $timePeriod; } } } } } # Method: timePeriods # # Returns: # reference to a list of time periods used for consolidation sub timePeriods { return [ TIME_PERIODS ]; } sub checkTimePeriod { my ($self, $timePeriod) = @_; if (grep { $_ eq $timePeriod } TIME_PERIODS) { throw EBox::Exceptions::Internal( "inexistent time period: $_" ); } } sub _allModulesWithConsolidation { my ($self) = @_; my $logs = EBox::Global->modInstance('logs'); my @modNames; my @mods = @{ $logs->getLogsModules() }; foreach my $mod (@mods) { my $name = $mod->name(); my $consolidate = @{ $self->_tableInfosFromMod($name, 1) }; if (not $consolidate) { next; } push @modNames, $name; } return \@modNames; } sub _consolidateTable { my ($self, %args) = @_; my $destinationTable = $args{destinationTable}; my $tableInfo = $args{tableInfo}; my $timePeriod = $args{timePeriod}; my $conf = $args{configuration}; my $table = $destinationTable . '_' . $timePeriod; my $sourceTable = $tableInfo->{tablename};; my $dateCol = 'timestamp'; my $consDateSub = "_$timePeriod" . 'Date'; my %consColumns = %{ $self->_columnsSpec($conf->{consolidateColumns}) }; my %accummulateColumns; if (exists $conf->{accummulateColumns} ) { %accummulateColumns = %{ $conf->{accummulateColumns} }; } else { %accummulateColumns = (count => 1); } my $filterSub = $conf->{filter}; my $dbengine = EBox::DBEngineFactory::DBEngine(); my $tsGetRows = time(); my $rows_r = $self->_sourceRows($dbengine, $sourceTable, $dateCol); foreach my $row (@{ $rows_r }) { # filter out bad rows if ($filterSub) { $filterSub->($row) or next; } my %consRow; my %accummulator = %accummulateColumns; while (my ($column, $value) = each %{ $row}) { if ($column eq $dateCol) { $consRow{date} = $self->$consDateSub($value); next; } exists $consColumns{$column} or next; my $dest = $consColumns{$column}->{destination}; my $accummulateColumn = undef; if ( $consColumns{$column}->{accummulate}) { $accummulateColumn = $consColumns{$column}->{accummulate}->($value, $row); } my $conversor = $consColumns{$column}->{conversor}; my $consValue = $conversor->($value, $row); if (not defined $accummulateColumn) { $consRow{$dest} = $consValue; } else { # XXX TODO replace the warning with a die when we will be able to # do a rollback exists $accummulator{$accummulateColumn} or EBox::warn("Accummulatin in $accummulateColumn which was not defined as accummulate column"); $accummulator{$accummulateColumn} += $consValue; } } $self->_addConsolidatedRow($dbengine, $table, \%consRow, \%accummulator, $conf->{quote}); } $self->_clearRows( dbengine => $dbengine, table => $sourceTable, dateCol => $dateCol, time => $tsGetRows, timePeriod => $timePeriod, ); } sub _reconsolidateTable { my ($self, %args) = @_; my $destinationTable = $args{destinationTable}; my $timePeriod = $args{timePeriod}; my $sourceTimePeriod = $args{sourceTimePeriod}; my $conf = $args{configuration}; my $table = $destinationTable . '_' . $timePeriod; my $sourceTable = $destinationTable . '_' . $sourceTimePeriod; my $dateCol = 'date'; my $consDateSub = "_$timePeriod" . 'Date'; my %accummulateColumns; if (exists $conf->{accummulateColumns} ) { %accummulateColumns = map { ($_ => 0) } keys %{ $conf->{accummulateColumns} }; } else { %accummulateColumns = (count => 0); } my $dbengine = EBox::DBEngineFactory::DBEngine(); my $tsGetRows = time(); my $rows_r = $self->_sourceRows($dbengine, $sourceTable, $dateCol); foreach my $row (@{ $rows_r }) { my %consRow; my %accummulator = %accummulateColumns; while (my ($column, $value) = each %{ $row}) { if ($column eq $dateCol) { $consRow{date} = $self->$consDateSub($value); next; } if (exists $accummulator{$column}) { $accummulator{$column} = $value; } else { $consRow{$column} = $value; } } $self->_addConsolidatedRow($dbengine, $table, \%consRow, \%accummulator, $conf->{quote}); } $self->_clearRows( dbengine => $dbengine, table => $sourceTable, dateCol => $dateCol, time => $tsGetRows, timePeriod => $timePeriod, ); } my $identitySub_r = sub { return $_[0] }; sub _columnsSpec { my ($self, $consolidateColumns) = @_; my %spec = (); while (my ($column, $oldSpec) = each %{ $consolidateColumns }) { my $newSpec = {}; if (ref $oldSpec eq 'CODE') { $newSpec->{conversor} = $oldSpec; } elsif (ref $oldSpec eq 'HASH') { $newSpec = $oldSpec; } elsif (not ref $oldSpec) { if ($oldSpec != 1) { $newSpec->{dest} = $oldSpec; } } exists $newSpec->{destination} or $newSpec->{destination} = $column; exists $newSpec->{conversor} or $newSpec->{conversor} = $identitySub_r; if (exists $newSpec->{accummulate}) { my $accummulate= $newSpec->{accummulate}; my $refType = ref $accummulate; if (not $refType) { $newSpec->{accummulate} = sub { return $accummulate }; } elsif (not $refType eq 'CODE') { throw EBox::Exceptions::Internal( "Bad reference type for accummulate field: $refType" ); } } else { $newSpec->{accummulate} = undef; } $spec{$column} = $newSpec; } return \%spec; } my %ttlByTimePeriod = ( monthly => 0, weekly => 0, daily => 0, # XXX DEBUG! hourly => 0, # hourly => 3600*48, ); sub _clearRows { my ($self, %params) = @_; my $timePeriod = $params{timePeriod}; my $ttl = $ttlByTimePeriod{$timePeriod}; if ($ttl == 0) { return; } my $dbengine = $params{dbengine}; my $table = $params{table}; my $dateCol = $params{dateCol}; my $time = $params{time}; my $deadline = $time - $ttl; my ($sec,$min,$hour,$mday,$mon,$year) = localtime($deadline); $year += 1900; $mon +=1; my $deadlineDate = "$year-$mon-$mday $hour:$min:$sec"; my $deleteStatement = "DELETE FROM $table WHERE $dateCol < '$deadlineDate'"; $dbengine->do($deleteStatement); } sub _tableInfosFromMod { my ($self, $modName, $noThrowsException) = @_; defined $noThrowsException or $noThrowsException = 0; my $mod = EBox::Global->modInstance($modName); if (not $mod->isa('EBox::LogObserver')) { throw EBox::Exceptions::Internal("Module $modName has not log capabilities"); } my @tableInfos; my $ti = $mod->tableInfo(); if (ref $ti eq 'HASH') { EBox::warn('tableInfo() in ' . $mod->name . 'must return a reference to a list of hashes not the hash itself'); @tableInfos = ( $ti ); } else { @tableInfos = @{ $ti }; } @tableInfos = grep { exists $_->{consolidate} } @tableInfos; if (not @tableInfos and (not $noThrowsException)) { throw EBox::Exceptions::Internal("Module $modName has not any table with consolidate configuration"); } return \@tableInfos; } sub _monthlyDate { my ($self, $timeStamp) = @_; $timeStamp =~ s/\-\d\d?\s\d\d?:\d\d?:\d\d?$/-01 00:00:00/; return $timeStamp; } sub _weeklyDate { my ($self, $timeStamp) = @_; my ($datePart) = split '\s', $timeStamp; my $t = Time::Piece->strptime($datePart, "%Y-%m-%d"); my $dweek = $t->day_of_week; my $monday; my $daysToMonday; if ($dweek == 0) { # 0 == sunday $daysToMonday = 6; } else { $daysToMonday = $dweek - 1; # monday is day nubmer one; } $t -= $daysToMonday * ONE_DAY; return $t->year() .'-'. $t->mon() . '-' . $t->mday() . ' 00:00:00'; } sub _dailyDate { my ($self, $timeStamp) = @_; $timeStamp =~ s/\d\d?:\d\d?:\d\d?$/00:00:00/; return $timeStamp; } sub _hourlyDate { my ($self, $timeStamp) = @_; $timeStamp =~ s/\:\d\d?:\d\d?$/:00:00/; return $timeStamp; } sub _addConsolidatedRow { my ($self, $dbengine, $table, $row, $accummulator_r, $quote) = @_; defined $quote or $quote = {}; my $setPortion = ''; while (my ($column, $amount) = each %{ $accummulator_r }) { if ($amount == 0) { next; } $setPortion .= "$column = $column + $amount,"; } $setPortion =~ s/,$//; # remove last comma if (not $setPortion) { # add a idle update just to avoid failure and get the rows count my ($field) = keys %{ $row }; $setPortion = " $field = $field "; } my $wherePortion = '('; while (my ($col, $value) = each %{ $row }) { if ($quote->{$col}) { $value = $dbengine->quote($value); } else { $value = "'$value'"; } $wherePortion .= "$col = $value AND "; } $wherePortion =~ s/ AND $//; # remove last AND $wherePortion .= ')'; my $updateStatement = "UPDATE $table SET $setPortion WHERE $wherePortion"; my $res = $dbengine->do($updateStatement); # if there is not a line for the consolidate values the update statement will # return 0 and we must do the insert if ($res == 0) { while (my ($column, $amount) = each %{ $accummulator_r }) { if ($amount == 0) { next; } $row->{$column} = $amount; } $dbengine->unbufferedInsert($table, $row); } } sub _sourceRows { my ($self, $dbengine, $table, $dateCol) = @_; defined $dateCol or $dateCol = 'date'; my $select = "SELECT * FROM $table"; my $lastConsolidationDate = $self->_lastConsolidationDate($dbengine, $table); if (defined $lastConsolidationDate) { $select .= " WHERE $dateCol > '$lastConsolidationDate'"; } $select .= " ORDER BY $dateCol "; my $res = $dbengine->query($select); $self->_updateLastConsolidationDate($dbengine, $table, $res, $dateCol); return $res; } sub _lastConsolidationDate { my ($self, $dbengine, $table) = @_; my $select = "SELECT * FROM consolidation WHERE consolidatedTable = '$table'"; my $res = $dbengine->query($select); my @rows = @{ $res }; if (@rows == 0) { return undef; } elsif (@rows > 1) { throw EBox::Exceptions::Internal( "More than one result for lastConsolidationDate for $table" ); } return $rows[0]->{lastdate}; } sub _updateLastConsolidationDate { my ($self, $dbengine, $table, $res, $dateCol) = @_; if ( @{ $res } == 0) { return; } my $lastRow = $res->[-1]; my $lastDate = $lastRow->{$dateCol}; my $updateSt = "UPDATE consolidation SET lastdate ='$lastDate' " . "WHERE consolidatedTable = '$table'"; my $updateRes = $dbengine->do($updateSt); if ($updateRes == 0) { my $row = { lastDate => $lastDate, consolidatedTable => $table }; $dbengine->unbufferedInsert('consolidation', $row); } } 1; zentyal-core-2.3.21+quantal1/src/EBox/Logs/Model/0000775000000000000000000000000012017102272016207 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Logs/Model/Graph.pm0000664000000000000000000000746112017102272017616 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Logs::Model::Graph; use base ( 'EBox::Model::DataTable', 'EBox::Logs::Model::Base',); # use strict; use warnings; use EBox::Gettext; use EBox::Exceptions::DataNotFound; use Time::Local; use Error qw(:try); sub datasets { my ($self) = @_; my @fields; my %dataPoints; my %conversors; my $dbFields = $self->dbFields(); while (my ($field, $attr) = each %{ $dbFields }) { push @fields, $field; $dataPoints{$field} = []; if (exists $attr->{conversor}) { $conversors{$field} = $attr->{conversor}; } } my $dbRows = $self->reportRows(); foreach my $row (@{ $dbRows }) { # transform time in timestamp $row->{date} =~ m/^(\d+)\-(\d+)\-(\d+)\s(\d+):/; my $month = $2 - 1; # months go throught 0-11 my $timestamp = timelocal( 0, 0, $4, $3, $month, $1); foreach my $field (@fields) { my $value = $row->{$field}; if (exists $conversors{$field}) { $value = $conversors{$field}->($value); } push @{ $dataPoints{$field} }, [$timestamp, $value]; } } my @dataSet; foreach my $field (@fields) { push @dataSet, $dataPoints{$field}; } return \@dataSet; } # Overrides base method to limit the number of rows according timeperiod my %nRowsByTimePeriod = ( hourly => 30, daily => 10, weekly => 10, monthly => 24, ); sub reportRows { my ($self) = @_; my $limit = $self->limitByTimePeriod(); return $self->SUPER::reportRows($limit); } sub limitByTimePeriod { my ($self) = @_; my $limit; my $timePeriod = $self->timePeriod(); if (exists $nRowsByTimePeriod{$timePeriod}) { $limit = $nRowsByTimePeriod{$timePeriod}; } else { $limit = 30; } return $limit; } sub datasetsLabels { my ($self) = @_; my $dbFields = $self->dbFields(); my @labels = map { $_->{printableName} } values %{ $dbFields }; return \@labels; } # Method: checkTable # # This method does some fast and general checks in the table specification # We override it bz for Images is acceptable to not have elements in the tableDescription # # Override: sub checkTable { my ($self, $table) = @_; if (not exists $table->{tableDescription}) { throw EBox::Exceptions::Internal('Missing tableDescription in table definition'); } if (not $table->{tableName}) { throw EBox::Exceptions::Internal( 'table description has not tableName field or has a empty one' ); } if ((exists $table->{sortedBy}) and (exists $table->{order})) { if ($table->{sortedBy}and $table->{order}) { throw EBox::Exceptions::Internal( 'sortedBy and order are incompatible options' ); } } } # Method altText # # Returns: # the alternative text for the graphic. Default to a empty string sub altText { return ''; } sub Viewer { return '/ajax/graph.mas'; } # sub modelDomain # { # return 'ebox'; # } 1; zentyal-core-2.3.21+quantal1/src/EBox/Logs/Model/ForcePurge.pm0000664000000000000000000000767512017102272020625 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::Logs::Model::ForcePurge # # Form model to purge a selected kind of log from a fixed # date. This model inherits from # since no data is required to be stored. # package EBox::Logs::Model::ForcePurge; use base 'EBox::Model::DataForm::Action'; use strict; use warnings; use EBox::Exceptions::MissingArgument; use EBox::Gettext; use EBox::Types::Boolean; use EBox::Types::Select; use EBox::Logs::Consolidate; # Core modules use Error qw(:try); # Group: Public methods # Constructor: new # # Create a force purge form # # Overrides: # # # # Returns: # # - the recently created model # sub new { my ($class, @params) = @_; my $self = $class->SUPER::new(@params); bless( $self, $class ); return $self; } # Method: formSubmitted # # Overrides: # # # sub formSubmitted { my ($self, $row, $force) = @_; my $lifeTime = $row->valueByName('lifeTime'); # we consolidate before to avoid any data loss # FIXME consolidation disabled until we reworked # EBox::Logs::Consolidate->consolidate('all'); my $logs = EBox::Global->modInstance('logs'); $logs->forcePurge($lifeTime); } # Group: Protected methods # Method: _table # # Overrides: # # # sub _table { my ($self) = @_; my @tableDesc = ( new EBox::Types::Select( 'fieldName' => 'lifeTime', 'printableName' => __('Purge logs older than'), populate => \&_populateSelectLifeTime, editable => 1, defaultValue => 1, ), ); my $dataForm = { tableName => 'ForcePurge', printableTableName => __('Force log purge'), modelDomain => 'Logs', defaultActions => [ 'editField', 'changeView' ], printableActionName => __('Purge'), tableDescription => \@tableDesc, class => 'dataForm', }; return $dataForm; } sub _populateSelectLifeTime { # life time values must be in hours return [ { printableValue => __('one hour'), value => 1, }, { printableValue => __('twelve hours'), value => 12, }, { printableValue => __('one day'), value => 24, }, { printableValue => __('three days'), value => 72, }, { printableValue => __('one week'), value => 168, }, { printableValue => __('fifteeen days'), value => 360, }, { printableValue => __('thirty days'), value => 720, }, { printableValue => __('ninety days'), value => 2160, }, ]; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Logs/Model/SelectLog.pm0000664000000000000000000001404712017102272020434 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Logs::Model::SelectLog; use base 'EBox::Model::DataTable'; # use strict; use warnings; use Error qw(:try); use EBox::Global; use EBox::Gettext; use EBox::Logs::Consolidate; use EBox::Types::Link; use constant SB_URL => 'https://store.zentyal.com/small-business-edition.html/?utm_source=zentyal&utm_medium=logs&utm_campaign=smallbusiness_edition'; use constant ENT_URL => 'https://store.zentyal.com/enterprise-edition.html/?utm_source=zentyal&utm_medium=logs&utm_campaign=enterprise_edition'; sub new { my $class = shift; my %parms = @_; my $self = $class->SUPER::new(@_); bless($self, $class); return $self; } # Method: ids # # Override as we don't need to # store these rows. Actually, the rows returned by this model # are built in runtime. All their elements are read only so # there is no need to store anything. # # We simply create an id array using an integer for every # row. # # This id will be used to build the row in runtime in # modInstance('logs'); my @mods = @{ $logs->getLogsModules() }; my $idx = 0; foreach my $mod (@mods) { foreach my $urlGroup (@{ $mod->reportUrls }) { push (@ids, $idx); $idx++; } } return \@ids; } # Method: row # # Override as we don't need to # store these rows. Actually, the rows returned by this model # are built in runtime. All their elements are read only so # there is no need to store anything. # # Use one of the id returned by # to build and return the requested row sub row { my ($self, $id) = @_; my $logs = EBox::Global->modInstance('logs'); my @mods = @{ $logs->getLogsModules() }; my $idx = 0; my $rowValues; foreach my $mod (@mods) { foreach my $urlGroup (@{ $mod->reportUrls }) { if ($idx == $id) { my $row = $self->_setValueRow(%{$urlGroup}); $row->setId($id); $row->setReadOnly(1); return $row; } $idx++; } } return undef; } sub logRows { my ($self) = @_; } # Function: filterDomain # # This is a callback used to filter the output of the field domain. # It basically translates the log domain # # Parameters: # # instancedType- an object derivated of # # Return: # # string - translation sub filterDomain { my ($instancedType) = @_; my $logs = EBox::Global->modInstance('logs'); my $table = $logs->getTableInfo($instancedType->value()); my $translation = $table->{'name'}; if ($translation) { return $translation; } else { return $instancedType->value(); } } sub _table { my @tableHead = ( new EBox::Types::Text( 'fieldName' => 'domain', 'printableName' => __('Domain'), 'size' => '12', 'unique' => 0, 'editable' => 0, 'filter' => \&filterDomain ), new EBox::Types::Link( fieldName => 'raw', printableName => __('Full report'), editable => 0, optional => 1, ), new EBox::Types::Link( fieldName => 'summary', printableName => __('Summarized report'), editable => 0, optional => 1, ), ); my $dataTable = { 'tableName' => 'SelectLog', 'printableTableName' => __('Query Logs'), 'defaultController' => '/Logs/Controller/SelectLog', 'defaultActions' => [ 'editField', 'changeView' ], 'tableDescription' => \@tableHead, 'class' => 'dataTable', 'order' => 0, 'rowUnique' => 0, 'printableRowName' => __('logs'), 'messages' => { add => undef, }, }; return $dataTable; } # Method: viewCustomizer # # Return a custom view customizer to set a permanent message # if needed # # Overrides: # # # sub viewCustomizer { my ($self) = @_; my $customizer = new EBox::View::Customizer(); $customizer->setModel($self); my $subscriptionLevel = -1; if (EBox::Global->modExists('remoteservices')) { my $rs = EBox::Global->modInstance('remoteservices'); $subscriptionLevel = $rs->subscriptionLevel(); } unless ($subscriptionLevel > 0) { $customizer->setPermanentMessage($self->_commercialMsg(), 'ad'); } return $customizer; } # Return the commercial message sub _commercialMsg { return __sx('Want to know what is your system status and usage? Get the {ohs}Small Business{ch} or {ohe}Enterprise Edition{ch} to receive regular system reports.', ohs => '', ohe => '', ch => ''); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Logs/Model/Base.pm0000664000000000000000000001146412017102272017425 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA use strict; use warnings; package EBox::Logs::Model::Base; use EBox::DBEngineFactory; use EBox::Gettext; use Perl6::Junction qw(all); use Error qw(:try); sub _printableDate { my ($self, $date, $timePeriod) = @_; defined $timePeriod or $timePeriod = $self->timePeriod(); my ($daysPortion, $hoursPortion) = split '\s', $date; my ($year, $month, $day) = split '-', $daysPortion; my ($hour) = split ':', $hoursPortion; if ($timePeriod eq 'hourly') { return "$day-$month-$year $hour:00"; } elsif ($timePeriod eq 'daily') { return "$day-$month-$year"; } elsif ($timePeriod eq 'weekly') { return "$day-$month-$year"; } elsif ($timePeriod eq 'monthly') { return "$month-$year"; } else { throw EBox::Exceptions::Internal("Bad time period: $timePeriod"); } } my $allAllowedTimePeriods = all(qw(hourly daily weekly monthly)); sub _checkTimePeriod { my ($self, $period) = @_; if ($period ne $allAllowedTimePeriods) { throw EBox::Exceptions::Internal("No time period $period allowed in the report"); } } # Method: dbTableName # # Abstract method to ve overriden. # # Returns: # the name of the table used to generate the report minus the time # period suffix sub dbTableName { throw EBox::Exceptions::NotImplemented('dbTableName'); } # Method: dbTable # # Returns: # the specific dbTable that will be used for the time period specified # # Parameters: # $timePeriod - the time period sub dbTable { my ($self, $timePeriod) = @_; defined $timePeriod or $timePeriod = $self->timePeriod(); $self->_checkTimePeriod($timePeriod); return $self->dbTableName() . '_' . $timePeriod; } # Method: dbFields # # this must be return information about the data base field used for the # report. The information must be returning as a hash ref with the name of # the column as key and the following configuration hash ref with this # format: # - printableName # - totalSub: for the detail table this sub reference will be used # to get the total for the field instead of adding # the values of all rows. The function will be called # passing a reference to all rows. # sub dbFields { throw EBox::Exceptions::NotImplemented('dbFields'); } # Method: reportRows # # Parameters: # limit - max number of report rows returned # # Returns: reference to a list with the rows of the table appropiate to the # selected time period sub reportRows { my ($self, $limit) = @_; my $timePeriod = $self->timePeriod(); my $table = $self->dbTable($timePeriod) ; my @fields = map { q{`} . $_ . q{`} } keys %{ $self->dbFields() }; push @fields, q{`date`}; my $dbEngine = EBox::DBEngineFactory::DBEngine(); my $orderMode = $limit ? 'DESC' : 'ASC'; my $columns = join ',', @fields; my $query = "SELECT $columns FROM $table ORDER BY date $orderMode"; if ($limit) { $query .= " LIMIT $limit"; } my $dbRows = $dbEngine->query($query); if ($limit) { # with limit we have used desc order and we must retuen in asc order $dbRows = [ reverse @{ $dbRows } ]; } return $dbRows; } # Method: timePeriod # # get the time period active in the report. This is by default done getting # the model specied in timePeriodModelPath and its value for the field # 'timePeriod'. This method should be overriden if the child needs to use a # different mechanism to get the time period sub timePeriod { my ($self) = @_; my $modelPath = $self->timePeriodModelPath; my $model = $self->{confmodule}->model($modelPath); my $row = $model->row(); return $row->valueByName('timePeriod'); } # Method: timePeriodModelPath # # Abstact method. Used in the default implementation of timePeriod Must # return the path of a component with a 'timePeriod' field which reflect the # currently selected time period sub timePeriodModelPath { throw EBox::Exceptions::NotImplemented('timePeriodModelPath'); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Logs/Model/ConfigureLogs.pm0000664000000000000000000002573312017102272021325 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: # # EBox::Logs::Model::ConfigureLogs # # This class is used as a model to describe a table which will be # used to select the logs domains the user wants to enable/disable. # # It subclasses # # # package EBox::Logs::Model::ConfigureLogs; use strict; use warnings; use EBox::Global; use EBox::Gettext; use EBox::Model::Manager; use EBox::Validate qw(:all); use EBox::Types::Boolean; use EBox::Types::Int; use EBox::Types::IPAddr; use EBox::Types::Link; use EBox::Types::Select; use EBox::Types::Text; use EBox::Types::Union; use EBox::Sudo; use EBox::Exceptions::External; # Core modules use Error qw(:try); use List::Util; use base 'EBox::Model::DataTable'; # Group: Public methods sub new { my $class = shift; my %parms = @_; my $self = $class->SUPER::new(@_); bless($self, $class); return $self; } # Method: enabledLogs # # Return those log domains which must be logged. # # Returns: # # Hashref containing the enabled logs. # # Example: # # { 'squid' => 1, 'dhcp' => 1 } # # sub enabledLogs { my ($self) = @_; my %enabledLogs; for my $id (@{$self->ids()}) { my $row = $self->row($id); next unless ($row->valueByName('enabled')); $enabledLogs{$row->valueByName('domain')} = 1; } return \%enabledLogs; } # Method: syncRows # # Override # # It is overriden because this table is kind of different in # comparation to the normal use of generic data tables. # # - The user does not add rows. When we detect the table is # empty we populate the table with the available log domains. # # - We check if we have to add/remove one the log domains. That happens # when a new module is installed or an existing one is removed. # # sub syncRows { my ($self, $currentRows) = @_; my $changed = undef; # Fetch the current log domains stored in conf my %storedLogDomains; foreach my $id (@{$currentRows}) { my $row = $self->row($id); $storedLogDomains{$row->valueByName('domain')} = 1; } # Fetch the current available log domains my %currentLogDomains; my $logs = EBox::Global->modInstance('logs'); foreach my $mod (@{ $logs->getLogsModules()} ) { $currentLogDomains{$mod->name} = $mod; } # Add new domains to conf foreach my $domain (keys %currentLogDomains) { next if (exists $storedLogDomains{$domain}); my @tableInfos; my $mod = $currentLogDomains{$domain}; my $ti = $mod->tableInfo(); if (ref $ti eq 'HASH') { EBox::warn('tableInfo() in ' . $mod->name . 'must return a reference to a list of hashes not the hash itself'); @tableInfos = ( $ti ); } else { @tableInfos = @{ $ti }; } my $enabled = not grep { $_->{'disabledByDefault'} } @tableInfos; $self->addRow(domain => $domain, enabled => $enabled, lifeTime => 168); $changed = 1; } # Remove non-existing domains from conf foreach my $id (@{$currentRows}) { my $row = $self->row($id); my $domain = $row->valueByName('domain'); next if (exists $currentLogDomains{$domain}); $self->removeRow($row->id()); $changed = 1; } return $changed; } # Method: validateTypedRow # # Override # sub validateTypedRow { my ($self, $action, $params_r, $actual_r) = @_; if (exists $params_r->{enabled} or exists $params_r->{enabled}) { my $enabled = exists $params_r->{enabled} ? $params_r->{enabled}->value() : $actual_r->{enabled}->value(); my $domain = exists $params_r->{domain} ? $params_r->{domain}->value() : $actual_r->{domain}->value(); if (not $enabled) { my $logs = EBox::Global->modInstance('logs'); foreach my $mod (@{$logs->getLogsModules()}) { if ($mod->name eq $domain) { my @tableInfos = @{ $mod->tableInfo() }; my $force = grep { $_->{'forceEnabled'} } @tableInfos; throw EBox::Exceptions::External( __x('This log is forced by its module. You can only disable it by disabling {module} module', module => $mod->printableName) ) if ($force); return; } } } } } sub updatedRowNotify { my ($self, $row, $oldRow, $force) = @_; my $domain = $row->valueByName('domain'); my $enabled = $row->valueByName('enabled'); my $logs = EBox::Global->modInstance('logs'); my $tables = $logs->getAllTables(); my $index = List::Util::first { $tables->{$_}->{helper}->name() eq $domain } keys %{ $tables }; if ($index) { $tables->{$index}->{helper}->enableLog($enabled); } else { EBox::warn("Domain: $domain does not exist in logs"); } } # Group: Callback functions # Function: filterDomain # # This is a callback used to filter the output of the field domain. # It basically translates the log domain # # Parameters: # # instancedType- an object derivated of # # Return: # # string - translation sub filterDomain { my ($instancedType) = @_; my $logs = EBox::Global->modInstance('logs'); my $table = $logs->getTableInfo($instancedType->value()); my $translation = $table->{'name'}; if ($translation) { return $translation; } else { return $instancedType->value(); } } sub _populateSelectLifeTime { # life time values must be in hours return [ { printableValue => __('never purge'), value => 0, }, { printableValue => __('one hour'), value => 1, }, { printableValue => __('twelve hours'), value => 12, }, { printableValue => __('one day'), value => 24, }, { printableValue => __('three days'), value => 72, }, { printableValue => __('one week'), value => 168, }, { printableValue => __('fifteeen days'), value => 360, }, { printableValue => __('thirty days'), value => 720, }, { printableValue => __('ninety days'), value => 2160, }, { printableValue => __('one year'), value => 8760, }, { printableValue => __('two years'), value => 17520, }, ]; } # Function: acquireEventConfURL # # Callback function used to gather the foreign model view URL to # configure the event watcher configuration for this log domain # # Parameters: # # instancedType - the cell from which the # URL will be obtained # # Returns: # # String - the desired URL # sub acquireEventConfURL { my ($instancedType) = @_; my $logDomain = $instancedType->row()->valueByName('domain'); my $modelManager = EBox::Model::Manager->instance(); my $logConfModel = $modelManager->model('/events/LogWatcherConfiguration'); my $id = $logConfModel->findValue(domain => $logDomain); my $loggerConfRow = $logConfModel->row($id); my $filterDirectory = $loggerConfRow->{filters}->{directory}; my $logFilteringWatcher; try { $logFilteringWatcher = $modelManager->model("/events/LogWatcherFiltering/$logDomain"); } catch EBox::Exceptions::DataNotFound with { $logFilteringWatcher = undef; }; if ( $logFilteringWatcher ) { return '/' . $logFilteringWatcher->menuNamespace() . "?directory=$filterDirectory"; } else { return '/'; } } # Group: Protected methods # Method: _table # # This method overrides to return # a table model description. # # This table is composed of four fields: # # domain () # enabled () # lifeTime () # eventConf () # # The only avaiable action is edit and only makes sense for 'enabled' # and lifeTime. # sub _table { my @tableHead = ( new EBox::Types::Text( 'fieldName' => 'domain', 'printableName' => __('Domain'), 'size' => '12', 'unique' => 1, 'editable' => 0, 'filter' => \&filterDomain ), new EBox::Types::Boolean( 'fieldName' => 'enabled', 'printableName' => __('Enabled'), 'unique' => 0, 'trailingText' => '', 'editable' => 1, ), new EBox::Types::Select( 'fieldName' => 'lifeTime', 'printableName' => __('Purge logs older than'), 'populate' => \&_populateSelectLifeTime, 'editable' => 1, 'defaultValue' => 168, # one week ), ); my $dataTable = { 'tableName' => 'ConfigureLogs', 'printableTableName' => __('Current configuration'), 'defaultController' => '/Logs/Controller/ConfigureLogs', 'defaultActions' => [ 'editField', 'changeView' ], 'tableDescription' => \@tableHead, 'class' => 'dataTable', 'order' => 0, 'help' => __x('Enable/disable logging per-module basis'), 'rowUnique' => 0, 'printableRowName' => __('logs'), }; return $dataTable; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Logs/Model/OptionsBase.pm0000664000000000000000000001040012017102272020766 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Logs::Model::OptionsBase; use base qw(EBox::Model::DataForm); use strict; use warnings; use EBox::Gettext; use EBox::Types::Select; use Error qw(:try); sub periods { my ($package) = @_; return [qw(hourly daily weekly monthly)]; } sub defaultPeriod { my ($package) = @_; return 'daily'; } my %printableValues = ( hourly => __('hour'), daily => __('day'), weekly => __('week'), monthly => __('month'), ); sub populateSelect { my ($package) = @_; my @options; my @periods = @{ $package->periods() }; foreach my $period (@periods) { push @options, { value => $period, printableValue => $printableValues{$period}, } } return \@options; } # Method: _standardTablehead # # this return a standard table head. Appropiate for models with only the time # period option sub _standardTablehead { my ($package) = @_; my $populateSelect = sub { return $package->populateSelect() }; my $tableHead = [ new EBox::Types::Select( fieldName => 'timePeriod', printableName => __('Report time period'), editable => 1, populate => $populateSelect, defaultValue => $package->defaultPeriod(), ) ]; } # Method: _table # # This implementation of the _table method would be suffice for most cases. # It depends in the calues supplied by the methods modelDomain, tableName and _standardTablehead sub _table { my ($package) = @_; my $tableHead = $package->_standardTablehead(); my $dataTable = { 'tableName' => $package->tableName(), 'printableTableName' => __('Report options'), 'defaultActions' => [ 'changeView', 'editField' ], 'tableDescription' => $tableHead, # 'class' => 'dataTable', 'order' => 0, # 'help' => __x('Enable/disable logging per-module basis'), 'rowUnique' => 0, 'modelDomain' => $package->modelDomain(), 'messages' => $package->_messages(), }; return $dataTable; } # Method: modelDomain # # Abstract method # Must return the model domain sub modelDomain { throw EBox::Exceptions::NotImplemented('modelDomain'); } sub setTypedRow { my ($self, @params) = @_; my $global = EBox::Global->getInstance(); my $modName = $self->{confmodule}->name(); my $alreadyChanged = $global->modIsChanged($modName); try { $self->SUPER::setTypedRow(@params); } finally { if (not $alreadyChanged) { # unmark module as changed $global->modRestarted($modName); } }; } sub _messages { my ($package) = @_; return { 'add' => undef, 'del' => undef, 'update' => undef, 'moveUp' => undef, 'moveDown' => undef, }; } # Method: reportUrl # # Abstract method. # This must be the URL of the report page. The user will be redirected there # when the options will be setted sub reportUrl { throw EBox::Exceptions::NotImplemented('reportUIrl'); } sub formSubmitted { my ($self) = @_; $self->pushRedirection($self->reportUrl); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Logs/Model/Details.pm0000664000000000000000000001613312017102272020136 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA use strict; use warnings; package EBox::Logs::Model::Details; use base qw(EBox::Model::DataTable EBox::Logs::Model::Base); use EBox::Gettext; use Perl6::Junction qw(all); use Error qw(:try); # Method: ids # # Override as we don't need to # store these rows. Actually, the rows returned by this model # are built in runtime. All their elements are read only so # there is no need to store anything. # # The data is actually fectched from the a relational DB, as # that's where the logs are stored. # # We simply create an id array using an integer for every # row. # # The last row uses 'total' as its identifier. This is # a special case to compute the agreggation of the other # rows. # # Note that we add it at the end of the returned array. # # This id will be used to build the row in runtime in # reportRows($self->timePeriod()); return [ 0 .. scalar(@{$dbRows}) - 1, 'total']; } # Method: row # # Override as we don't need to # store these rows. Actually, the rows returned by this model # are built in runtime. All their elements are read only so # there is no need to store anything. # # The data is actually fectched from the a relational DB, as # that's where the logs are stored. # # We just return a row containing the fields stored on the DB. # # The last row uses 'total' as its identifier. This is # a special case to compute the agreggation of the other # rows. # sub row { my ($self, $id) = @_; my $dbRows = $self->reportRows($self->timePeriod()); my $row; if ($id ne 'total') { my $rowData = $dbRows->[$id]; defined $rowData or return undef; $row = $self->_setValueRow(%{$rowData}); } else { $row = $self->_totalRow($dbRows); } $row->setId($id); $row->setReadOnly(1); return $row; } # Method: message # # overriden method to ignore add messages, bz we re always adding rows when # refreshing # # Overriden: # sub message { my ($self, $action) = @_; if ($action eq 'add') { return undef; } return $self->SUPER::message($action); } sub reportRows { my ($self) = @_; my $timePeriod = $self->timePeriod(); my $dbRows = $self->SUPER::reportRows(); foreach my $row (@{ $dbRows }) { $row->{date} = $self->_printableDate($row->{date}, $timePeriod); } return $dbRows; } my %secondsByTimePeriod = ( daily => 24*60*60, ); sub _needUpdate { my ($self, $timePeriod) = @_; my $last = exists $self->{lastUpdate} ? $self->{lastUpdate} : 0; my $now = time(); if ($now < ($last + $secondsByTimePeriod{$timePeriod} )) { return 0; } $self->{lastUpdate} = $now; return 1; } # Method: sortedBy # # we override this so by default is sortedBy the field 'date'. It can be changed # to other field or to '' in the table definition sub sortedBy { my ($self) = @_; my $sortedBy = $self->table()->{'sortedBy'}; return 'date' unless ( defined $sortedBy ); return $sortedBy; } sub _tableHead { my ($self, $timePeriod) = @_; my @tableHead = ( new EBox::Types::Text( 'fieldName' => 'date', 'printableName' => $self->printableTimePeriod($timePeriod), 'size' => '12', ), ); while (my ($name, $spec) = each %{ $self->dbFields }) { my $type = exists $spec->{type} ? $spec->{type} : 'EBox::Types::Text'; my $printableName = exists $spec->{printableName} ? $spec->{printableName} : $name; push @tableHead, $type->new( fieldName => $name, printableName => $printableName, editable => 0, ); } return \@tableHead; } sub _tableName { throw EBox::Exceptions::NotImplemented('_tableName'); } # lists the fields which should not be aggregated in the 'total' row sub _noAggregateFields { return [] ; } sub _totalRow { my ($self, $dbRows) = @_; my $row = {}; $row->{date} = __('All'); my %dbFields = %{ $self->dbFields() }; # remove non-aggregate fields my @noAggregateFields = @{ $self->_noAggregateFields() }; foreach my $field (@noAggregateFields) { delete $dbFields{$field}; $row->{$field} = __('All'); } while (my ($name, $attr) = each %dbFields) { my $total; if (exists $attr->{totalSub}) { $total = $attr->{totalSub}->($dbRows); } else { $total = 0; foreach my $r (@{ $dbRows }) { $total += $r->{$name}; } } $row->{$name} = $total; } return $self->_setValueRow( %{ $row } ); } sub _tailoredOrder # (rows) { my ($self, $rows) = @_; # Sorted by sortedBy field element if it's given my $fieldName = $self->sortedBy(); $fieldName or $fieldName = 'date'; if (not $self->fieldHeader($fieldName) ) { throw EBox::Exceptions::Internal("orderBy field $fieldName does not exist"); } my $allString = __('All'); my @sortedRows = sort { _compareDates($a, $b, $allString); } @{$rows}; return \@sortedRows; } sub _compareDates { my ($a, $b, $allString) = @_; my $aDate = $a->valueByName('date'); my $bDate = $b->valueByName('date'); if ($aDate eq $allString) { return -1; } elsif ($bDate eq $allString) { return 1; } my ($aDatePortion, $aTimePortion) = split '\s', $aDate; my @aDateParts = split '-', $aDatePortion; my ($bDatePortion, $bTimePortion) = split '\s', $bDate; my @bDateParts = split '-', $bDatePortion; while (@aDateParts) { my $aP = pop @aDateParts; my $bP = pop @bDateParts; my $res = $bP <=> $aP; if ($res != 0) { return $res; } } if (not defined $aTimePortion) { return 0; } return $bTimePortion cmp $aTimePortion; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Logs/SlicedBackup.pm0000664000000000000000000005733512017102272020053 0ustar # Copyright (C) 2010-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA use strict; use warnings; package EBox::Logs::SlicedBackup; use EBox; use EBox::Config; use EBox::Gettext; use EBox::Exceptions::InvalidData; use File::Basename; use Error qw(:try); use constant CONF_FILE => EBox::Config::etc() . 'logs.conf'; use constant SLICES_TABLE => 'backup_slices'; use constant MIN_SLICE_INTERVAL => 86400; # 1 day (in seconds) sub backupSlicesDBTable { return SLICES_TABLE; } # Method: slicedBackup # # Backs up the logs database in sliced mode # # Parameters: # # dbengine - database engine # dir - directory when the backup files will be left # period (named) - duration of the slices (default: from configuration file) # nowTs (named) - actual timestamp in epoch seconds. Useful for testing (default: current time) sub slicedBackup { my ($dbengine, $dir, %params) = @_; my $period; if (exists $params{period}) { $period = delete $params{period}; } else { $period = EBox::Config::configkeyFromFile('eboxlogs_sliced_backup_period', CONF_FILE); } my $epochPeriod = _periodToEpoch($dbengine, $period); my $schemaDumpFile = _schemaFile($dir); $dbengine->dumpDB($schemaDumpFile, 1); _backupTableSlices($dbengine, $dir, $epochPeriod, %params); } # Method: slicedRestore # # Restore the logs database in sliced mode # # Parameters: # # dbengine - database engine # dir - directory when the backup files had to be found # notArchivedForce(named) - force to restore even when they are not archived slices (default: from configuration file) # toDate (named) - restore data until this date. Date must be a epoch timestamp. (default: rstore all the available data in the backup) # archiveDir (named) - directory where we could found the archived backup slices # (default: from confioguration file) # nowTs (named) - actual timestamp in epoch seconds. Useful for testing (default: current time) sub slicedRestore { my ($dbengine, $dir, %params) = @_; my $toDate = $params{toDate}; my $forceNoSchema = EBox::Config::configkeyFromFile( 'eboxlogs_force_not_schema_sliced_restore', CONF_FILE) eq 'yes'; my $timeline = _activeTimeline($dbengine); my $notStored = _noStoredSlices($dbengine, $timeline); if ($notStored) { my $force; if (exists $params{notArchivedForce}) { $force = $params{notArchivedForce} ; } else { my $value = EBox::Config::configkeyFromFile('eboxlogs_force_not_archived_restore', CONF_FILE); $force = 'yes' eq (lc $value); } unless ($force) { throw EBox::Exceptions::External( __x('There are {n} log slices not stored; wait or force its storage and retry', n => $notStored ) ); } } my $schemaDumpFile = _schemaFile($dir); if ($schemaDumpFile and (-e $schemaDumpFile)) { $dbengine->restoreDBDump($schemaDumpFile, 1); } else { if ($forceNoSchema) { EBox::error('No schema file found in this backup. Forcing restore without schema restore'); } else { throw EBox::Exceptions::External( __x('No schema file found in this backup. Try another date or force restore without schema changing the relevant key in {cf}. Schemas change unfrequently so it could be safe to use a older one', cf => CONF_FILE) ); } } _restoreTables($dbengine, $dir, %params); } sub _schemaFile { my ($dir) = @_; return "$dir/db.schema-dump"; } sub _periodToEpoch { my ($dbengine, $period) = @_; $period =~ s/^\s+//; $period =~ s/\s+$//; my $sql = "select extract (epoch FROM interval '$period') AS time"; my ($res) = @{ $dbengine->query($sql) }; if (not defined $res) { throw EBox::Exceptions::InvalidData( data => __('Backup slice time interval'), value => $period, ); } my $time = $res->{time}; if ($time < MIN_SLICE_INTERVAL) { throw EBox::Exceptions::InvalidData( data => __('Backup slice time interval'), value => $period, advices => "Time interval too small"); } return $res->{time}; } sub _backupTableSlices { my ($dbengine, $dir, $period, %params) = @_; my $nowTs = exists $params{nowTs} ? $params{nowTs} : time(); my $superuserTmpDir = _superuserTmpDir(); my @tables = @{ $dbengine->tables() }; my $copySqlCmd = ''; foreach my $table (@tables) { my ($n, $startTs, $endTs, $timeline) = _actualTableSlice($dbengine, $table, $period, $nowTs); $copySqlCmd .= _backupTableSliceCmd($superuserTmpDir, $table, $timeline, $n, $startTs, $endTs); } _copySqlAsSuperuser($dbengine, $copySqlCmd, $superuserTmpDir, $dir); return 1; } sub _restoreTables { my ($dbengine, $dir, %params) = @_; my $toDate = $params{toDate}; my $archiveDir = $params{archiveDir}; # to avoid glob // problems $dir =~ s{/+$}{}; my $tmpDir = EBox::Config::tmp(); my $actualTimeline = _activeTimeline($dbengine); my @tableFiles = glob("$dir/*.table-slice"); push @tableFiles, @{ EBox::Logs::SlicedBackup::slicesFromArchive($dbengine, $archiveDir, $actualTimeline, $toDate) }; push @tableFiles, glob("$dir/*.table-dump"); my $copySqlCmd; foreach my $file (@tableFiles) { EBox::info("Next DB table file: $file"); my ($basename, $dirname, $extension) = fileparse($file, qr/\.[^.]*/); # in slices case basenane is table-number my ($table, $timeline, $n) = split '-', $basename, 3; if ($table eq SLICES_TABLE) { # this table is exempted from backup next; } my $uncompresedFile; my $toDelete = undef; if ($extension =~ m/\.gz$/) { $uncompresedFile = $tmpDir . 'table-tmp'; my $zcat = "zcat $file > $uncompresedFile"; system $zcat; if ($? != 0) { EBox::error( "Unable to decompress file with command $zcat\n". "Skipping slice" ); next; } $toDelete = $uncompresedFile; } else { $uncompresedFile = $file; } my $copySqlCmd = "COPY $table FROM '$uncompresedFile';\n"; # to copy from a file must be db superuser $dbengine->sqlAsSuperuser(sql => $copySqlCmd); EBox::info("$file restored to the DB\n"); if ($toDelete) { unlink $toDelete; } } if ($toDate) { _updateTimeline($dbengine, $actualTimeline, $toDate); } } sub _updateTimeline { my ($dbengine, $actual, $date) = @_; my $neededSql = 'SELECT id FROM ' . SLICES_TABLE . ' ' . "WHERE timeline = $actual AND " . "beginTs > to_timestamp($date) LIMIT 1"; my $res = $dbengine->query($neededSql); if (not @{ $res} ) { # no new timeline needed.. return; } my $new = $actual + 1; my $updateTimeline = 'UPDATE ' . SLICES_TABLE . ' ' . "SET TIMELINE=$new " . "WHERE timeline = $actual AND " . "beginTs <= to_timestamp($date)" ; $dbengine->do($updateTimeline); } # wether a database table should be backed/restored in sliced mode. # currently are exmpted all the report/consolidation tables, 'admin' and 'backup_slices' sub _noSliceableTable { my ($table) = @_; my $suffix = (split '_', $table)[-1]; if ($suffix eq 'report') { # the report tables are dumped fully return 1; } elsif (($suffix eq 'hourly') or ($suffix eq 'daily') or ($suffix eq 'weekly') or ($suffix eq 'monthly') ) { # accumulated tables are not sliceable return 1; } elsif ($table eq 'consolidation' or ($table eq 'report_consolidation')) { return 1; } elsif ($table eq 'admin' or ($table eq 'backup_slices')) { return 1; } return 0; } sub _actualTableSlice { my ($dbengine, $table, $period, $nowTs) = @_; if (_noSliceableTable($table)) { return (); } my $sqlActiveSlice = "SELECT id, beginTs, endTs, timeline FROM backup_slices WHERE tablename = '$table' AND endTs >= to_timestamp($nowTs) ORDER BY id DESC LIMIT 1"; my ($res) = @{ $dbengine->query($sqlActiveSlice) }; if (not defined $res) { # table is empty or last slice is not longer active, create slices _updateSliceMap($dbengine, table => $table, period => $period, nowTs => $nowTs); # try again wtih the new tables (we use the try again isntead of making # return with the new values from _updateSliceMap to avoid to add data # conversion code ($res) = @{ $dbengine->query($sqlActiveSlice) }; if (not defined $res) { # no slices return (); } } # data from active slice return ($res->{id}, $res->{begints}, $res->{endts}, $res->{timeline}); } sub _activeTimeline { my ($dbengine) = @_; my $sql = 'SELECT MAX(timeline) AS timeline FROM backup_slices'; my ($res) = @{ $dbengine->query($sql) }; my $timeline = $res->{timeline}; if ($timeline) { return $timeline; } return 1; # valeu for first timeline } sub _updateSliceMap { my ($dbengine, %params) = @_; my $table = $params{table}; defined $table or throw EBox::Exceptions::MissingArgument('table'); my $period = $params{period}; defined $period or throw EBox::Exceptions::MissingArgument('period'); # is assummed that nowTs >= all timestamp records which it should be true # when time is properly set my $nowTs = $params{nowTs}; defined $nowTs or throw EBox::Exceptions::MissingArgument('nowTs'); my ($beginTs, $slice, $timeline); # first we will try to get them from last active slice my $sqlLastSlice = "SELECT id AS slice, EXTRACT (EPOCH FROM endTs) AS end, timeline FROM backup_slices WHERE tablename = '$table' ORDER BY id DESC LIMIT 1"; my ($res) = @{ $dbengine->query($sqlLastSlice) }; if (defined $res) { $beginTs = $res->{end} + 1; $slice = $res->{slice} + 1; $timeline = $res->{timeline}; } else { # no slices create first slice my $sqlFirstTs = "SELECT EXTRACT (EPOCH FROM timestamp) AS first FROM $table ORDER BY timestamp ASC LIMIT 1"; my ($res) = @{ $dbengine->query($sqlFirstTs) }; if (not defined $res) { # table empty, no creating slices return; } $beginTs = $res->{first}; $slice = 1; $timeline = _activeTimeline($dbengine); } my $endTs = $beginTs + $period; while ($beginTs <= $nowTs) { my $sql = qq{INSERT INTO backup_slices ( tablename, id, beginTs, endTs, timeline, archived) VALUES ('$table', $slice, to_timestamp($beginTs), to_timestamp($endTs),$timeline, FALSE)}; $dbengine->do($sql); # check that next slice has not it begins in the future.. my $nextSliceBeginTs = $endTs + 1; if ( $nextSliceBeginTs <= $nowTs) { # update values for next slice $slice += 1; $beginTs = $nextSliceBeginTs; $endTs = $nextSliceBeginTs + $period; } else { # break.. last; } } } # return the uncompressed name (without .gz) sub _backupTableSliceCmd { my ($dir, $table, $timeline, $n, $beginTs, $endTs) = @_; my $outputFile = _backupFileForTable($dir, $table, $timeline, $n); my $sqlCommand; if (not defined $n) { $sqlCommand = qq{COPY $table TO '$outputFile';\n}; } else { my $select = "SELECT * FROM $table WHERE " . qq{timestamp >= '$beginTs' AND timestamp <= '$endTs'}; $sqlCommand = qq{COPY ($select) TO '$outputFile';\n}; } return $sqlCommand; } sub _backupFileForTable { my ($dir, $table, $timeline, $n) = @_; if (defined $n) { return lc "$dir/$table-$timeline-$n.table-slice"; } else { return lc "$dir/$table.table-dump"; } } # Method: archive # # Move the data from past slices to the archive. It only archives it,does not purge it # # Parameters: # dbengine - # archiveDir (named) - directory where we could found the archived backup slices # (default: from configuration file) # limit (named) - how many past slices we should move to the archive, this parameter exists for load purposes because this method is executed periodically by cron. (default: from configuration file) # nowTs (named) - actual timestamp in epoch seconds. Useful for testing (default: current time) sub archive { my ($dbengine, %params) = @_; # unroll parameters and check them my $limit = exists $params{limit} ? $params{limit} : EBox::Config::configkeyFromFile( 'eboxlogs_sliced_backup_archive_at_once', CONF_FILE ); ($limit > 0) or throw EBox::Exceptions::InvalidData( data => __('Slices to be archived at once'), value => $limit, ); my $timeline = exists $params{timeline} ? $params{timeline} : 1; ($timeline > 0) or throw EBox::Exceptions::InvalidData( data => __('Slices timeline'), value => $timeline, ); my $archiveDir = exists $params{archiveDir} ? $params{archiveDir} : archiveDir(); my $nowTs = exists $params{nowTs} ? $params{nowTs} : time(); try { $dbengine->commandAsSuperuser("test -d $archiveDir"); } otherwise { throw EBox::Exceptions::External( qq{Directory $archiveDir must be readable by DB's superuser} ); }; # assure we that we haves a updated slicemaps my $period = EBox::Config::configkeyFromFile('eboxlogs_sliced_backup_period', CONF_FILE); my $epochPeriod = _periodToEpoch($dbengine, $period); foreach my $table (@{ archivableTables($dbengine) }) { _updateSliceMap($dbengine, table => $table, period => $epochPeriod, nowTs => $nowTs); } # get slices to backup my $sql = 'SELECT id, tablename, beginTs, endTs FROM ' . SLICES_TABLE . ' '. " WHERE archived = FALSE AND ". " endTs < to_timestamp($nowTs) AND " . "TIMELINE = $timeline " . "LIMIT $limit"; my $copyCmds; my @toArchive; my @outputFiles; my $superuserTmpDir = _superuserTmpDir(); my $res = $dbengine->query($sql); foreach my $slice (@{ $res }) { my $table = $slice->{tablename}; my $id = $slice->{id}; my $beginTs = $slice->{begints}; my $endTs = $slice->{endts}; push @toArchive, [$table, $id]; my $outputFile = _backupFileForTable($archiveDir, $table, $timeline, $id); push @outputFiles, $outputFile; $copyCmds .= _backupTableSliceCmd( $superuserTmpDir, $table, $timeline, $id, $beginTs, $endTs ); } if (not @toArchive) { return; } _copySqlAsSuperuser($dbengine, $copyCmds, $superuserTmpDir, $archiveDir); # mark as archived foreach my $arch (@toArchive) { my ($table, $id) = @{ $arch }; my $updateSql = "UPDATE " . SLICES_TABLE . ' ' . "SET archived = TRUE " . "WHERE tablename = '$table' AND " . "id = $id AND " . "timeline = $timeline"; $dbengine->do($updateSql); } # compress files foreach my $file (@outputFiles) { try { EBox::Sudo::root("gzip $file"); } catch EBox::Exceptions::Command with { EBox::error("Cannot compress file $file. Try to do it manully. Skipping to next file.") }; } } # Method: archiveDir # # Returns: # the archive directory found in the config file sub archiveDir { return EBox::Config::configkeyFromFile('eboxlogs_sliced_backup_archive', CONF_FILE); } # Method: archivableTables # # Returns: # list of tables that must be archived sub archivableTables { my ($dbengine) = @_; my @tables = grep { not _noSliceableTable($_) } @{ $dbengine->tables() }; return \@tables; } # Method: slicedMode # # Returns: # whether sliced mode is enabled sub slicedMode { my $value = lc EBox::Config::configkeyFromFile('eboxlogs_sliced_backup', CONF_FILE); return $value eq 'yes'; } # Method: limitPurgeThreshold # # Push back if needed the purge threshold so data form no-archived slices isnt purged # # Parameters: # dbengine - # table - table to be purged # threshold - purge's threshold, this a date in string format # # Returns: # the same threshold if not change is needed, a new one if needed sub limitPurgeThreshold { my ($dbengine, $table, $threshold) = @_; my $query = "SELECT EXTRACT(EPOCH FROM beginTs) AS ts " . "FROM " . SLICES_TABLE . ' ' . "WHERE archived = FALSE AND " . "tablename = '$table' AND" . "((endTs <= '$threshold') OR (beginTs <= '$threshold') ) " . "ORDER BY beginTs ASC LIMIT 1"; my ($res) = @{ $dbengine->query($query) }; if (defined $res) { my $newTh = $res->{ts} -1; return scalar localtime($newTh); } # no neccessary to limit.. return $threshold; } # executes a COPY SQL command as db's superuser. The COPY command needs superuser permissions. sub _copySqlAsSuperuser { my ($dbengine, $copySql, $superuserDir, $dstDir) = @_; my $superUser = $dbengine->_dbsuperuser(); # we need a place where superusers user is able to write EBox::Sudo::root("rm -rf $superuserDir"); mkdir $superuserDir or throw EBox::Exceptions::Internal("Cannot mkdir $superuserDir"); EBox::Sudo::root("chown -R $superUser.$superUser $superuserDir"); # to copy to a file must be db superuser $dbengine->sqlAsSuperuser(sql => $copySql); # move files to their final destination EBox::Sudo::root("chown -R ebox.ebox $superuserDir"); EBox::Sudo::root("mv $superuserDir/* $dstDir") } sub _superuserTmpDir { return EBox::Config::tmp() . 'postgres-tmp'; } # Method: slicesFromArchive # # retrieve the slices from archive directory ehich meets the criteria # # Parameters: # dbengine - # archiveDir - directory where we could found the archived backup slices # actualTimeline - current timeline # toDate - restore data until this date. Date must be a epoch timestamp. (default: all archives) # # Returns: # list of archive files pargs sub slicesFromArchive { my ($dbengine, $archiveDir, $actualTimeline, $toDate) = @_; defined $archiveDir or $archiveDir = archiveDir(); $archiveDir =~ s{/+$}{/}; my @allTableFiles = glob("$archiveDir/*.table-slice.gz"); my %maxIds = %{ _maxIdsInTimeline($dbengine, $actualTimeline, $toDate) }; # this is to discard restores from either inadecuate dates or timelines my %selected; foreach my $file (@allTableFiles) { my ($basename, $dirname, $extension) = fileparse($file, qr/\..*/); my ($table, $timeline, $n) = split '-', $basename, 3; if ($timeline > $actualTimeline) { # we should never restoredfrom a timeline in the future next; } else { # check when id is in this timeline my $max= $maxIds{$table}; if (not defined $max) { EBox::warn("Unknow table has backup slices : $table"); next; } if ($n > $max) { # not applyable to this timeline next; } } if (not exists $selected{$table}) { # create hash for table $selected{$table} = {}; } if ($timeline == $actualTimeline) { # actual timeline always have priority even when it is not in slcies # data table $selected{$table}->{$n} = { timeline => $timeline, file => $file, }; next; } # this is to give priority to more recent timelines.. if (not exists $selected{$table}->{$n}) { # create new entry $selected{$table}->{$n} = { timeline => $timeline, file => $file, }; next; } elsif ($selected{$table}->{$n}->{timeline} < $timeline) { # update entry $selected{$table}->{$n} = { timeline => $timeline, file => $file, }; next; } # end choose files loop } my @files = map { my $tableValues = $_; map { $_->{file} } values %{ $tableValues }; } values %selected; return \@files; } sub _maxIdsInTimeline { my ($dbengine, $timeline, $toDate) = @_; my $query = 'SELECT tablename, MAX(id) AS maxid FROM ' . SLICES_TABLE . ' ' . " WHERE timeline = $timeline"; if ($toDate) { # restrict by date $query .= " AND (endTs <= to_timestamp($toDate))"; } $query .= ' GROUP BY tablename'; my ($res) = $dbengine->query($query) ; if (not @{ $res }) { return undef; } my %maxIds = map { $_->{tablename} => $_->{maxid} } @{ $res }; return \%maxIds; } # Method: _noStoredSlices # # Parameters: # dbengine # timeline - current timeline # # Returns: # the number of past slices which aren't stored sub _noStoredSlices { my ($dbengine, $timeline) = @_; my $query = 'SELECT SUM(noArchived.amount) As count FROM ' . '(SELECT (COUNT(*) -1) AS amount FROM ' . SLICES_TABLE . ' ' . " WHERE archived =FALSE " . " AND timeline <= $timeline " . ' GROUP BY tablename ' . ' ) AS noArchived'; my ($res) = @{ $dbengine->query($query) }; if (not $res) { return 0; } return $res->{count}; } 1; zentyal-core-2.3.21+quantal1/src/EBox/DBEngineFactory.pm0000664000000000000000000000162312017102272017546 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::DBEngineFactory; use strict; use warnings; use EBox; use EBox::MyDBEngine; # # Function: DBEngine # # Returns: # a instance of the DBEngine to be used # # sub DBEngine { return new EBox::MyDBEngine; } 1; zentyal-core-2.3.21+quantal1/src/EBox/Config/0000775000000000000000000000000012017102272015450 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Config/Redis.pm0000664000000000000000000003146512017102272017065 0ustar # Copyright (C) 2010-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Config::Redis; use 5.010001; use strict; use warnings; use Redis; use EBox::Config; use EBox::Service; use EBox::Module::Base; use EBox::Util::SHMLock; use File::Slurp; use File::Basename; use Perl6::Junction qw(any); use JSON::XS; use Error qw/:try/; # Constants use constant REDIS_CONF => 'conf/redis.conf'; use constant REDIS_PASS => 'conf/redis.passwd'; use constant CLIENT_CONF => EBox::Config::etc() . 'core.conf'; my %cache; my %modified; my %deleted; my $cacheVersion = 0; my $trans = 0; my $lock = undef; # Singleton variable my $_instance = undef; sub _new { my ($class, %args) = @_; my $self = {}; bless($self, $class); $self->_initRedis; $self->_respawn; $self->{pid} = $$; $self->{json_pretty} = JSON::XS->new->pretty; unless ($lock) { my $path = undef; if ($self->_user() eq 'ebox-usercorner') { $path = '/run/shm/zentyal-usercorner'; } $lock = EBox::Util::SHMLock->init('redis', $path); } return $self; } # Method: instance # # Return a singleton instance of class # # # Returns: # # object of class # sub instance { unless (defined ($_instance)) { $_instance = EBox::Config::Redis->_new(); } return $_instance; } # Method: set # # Set a key with a scalar value or a reference # sub set { my ($self, $key, $value) = @_; $self->begin(); $cache{$key} = $value; $modified{$key} = 1; delete $deleted{$key}; $self->commit(); } # Method: get # # Set the value of a key, or the given defaultValue if not exists # sub get { my ($self, $key, $defaultValue) = @_; # Get from redis if not in cache unless (exists $cache{$key}) { my $value = $self->_redis_call('get', $key); if (defined ($value)) { # XXX: this can be problematic if we store a string # starting with '[' or '{', but decode_json fails to decode # regular strings some times, even with allow_nonref # An alternative could be to try always the decode # ignoring the exception my $firstChar = substr ($value, 0, 1); if (($firstChar eq '[') or ($firstChar eq '{')) { $value = decode_json($value); } } else { $value = $defaultValue; } $cache{$key} = $value; } return $cache{$key}; } # Method: delete_dir # # Delete a directory recursively # sub delete_dir { my ($self, $dir) = @_; $self->begin(); my @keys = $self->_keys("$dir/*"); $self->unset(@keys); $self->commit(); } # Method: unset # # Unset a key # sub unset { my ($self, @keys) = @_; $self->begin(); foreach my $key (@keys) { delete $cache{$key}; $deleted{$key} = 1; delete $modified{$key}; } $self->commit(); } # Method: backup_dir # # Back up a given dir $key in $dest # sub backup_dir { my ($self, $key, $dest) = @_; $self->begin(); $self->delete_dir($dest); $self->_backup_dir( key => $key, destination_type => 'redis', destination => $dest ); $self->commit(); } # Method: export_dir_to_file # # Back up a given dir in "key: value" format # # Parameters: # # key - key for the directory # file - file to write # sub export_dir_to_file { my ($self, $key, $file) = @_; my @keys; $self->_backup_dir( key => $key, destination_type => 'file', destination => \@keys ); my @lines = sort (map { "$_->{key}: $_->{value}\n" } @keys); try { write_file($file, @lines); } otherwise { throw EBox::Exceptions::External("Error dumping $key to $file"); }; } sub _keys { my ($self, $pattern) = @_; my @keys = $self->_redis_call('keys', $pattern); foreach my $name (keys %cache) { if ($name =~ /^$pattern/) { push (@keys, $name); } } return @keys; } # Method: import_dir_from_file # # Given a "key: value" file, restore them under destination folder # # Parameters: # # filename - filename with the dump # dest - destination folder key # sub import_dir_from_file { my ($self, $filename, $dest) = @_; my @lines; try { @lines = split ("\n\n", read_file($filename)); } otherwise { throw EBox::Exceptions::External("Error parsing YAML:$filename"); }; $self->begin(); foreach my $line (@lines) { my ($key, $value) = $line =~ /^(.+?): (.*)$/s; if ($dest) { $key = $dest . '/' . $key; } # XXX: this can be problematic if we store a string # starting with '[' or '{', but decode_json fails to decode # regular strings some times, even with allow_nonref # An alternative could be to try always the decode # ignoring the exception my $firstChar = substr ($value, 0, 1); if (($firstChar eq '[') or ($firstChar eq '{')) { $value = $self->{json_pretty}->decode($value); } $self->set($key, $value); } $self->commit(); } sub _backup_dir { my ($self, %args) = @_; $self->begin(); my $key = $args{key}; my $destinationType = $args{destination_type}; my $dest = $args{destination}; my @keys = $self->_keys($key ? "$key/*" : '*'); push @keys, $self->_keys($key); # add own key itself for my $entry (@keys) { my $destKey = $entry; my $value = $self->get($entry); next unless defined ($value); if ($destinationType eq 'redis') { $destKey =~ s/^$key/$dest/; $self->set($destKey, $value); } else { if (ref $value) { $value = $self->{json_pretty}->encode($value); } else { $value .= "\n"; } push (@{$args{destination}}, { key => $destKey, value => $value } ); } } $self->commit(); } sub begin { my ($self) = @_; # Do not allow nested transactions return if ($trans++); $lock->lock(); my $version = $self->_redis_call('get', 'version'); defined ($version) or $version = 0; if ($version > $cacheVersion) { %cache = (); $cacheVersion = $version; } return 1; } sub commit { my ($self) = @_; $trans--; if ($trans == 0) { $self->_sync(); $lock->unlock(); } } sub rollback { my ($self) = @_; if ($self->{multi}) { $self->_redis_call('discard'); } $trans = 0; $lock->unlock(); } sub _sync { my ($self) = @_; return unless (%modified or %deleted); $self->_redis_call('multi'); foreach my $key (keys %modified) { my $value = $cache{$key}; if (ref $value) { $value = encode_json($value); } $self->_redis_call('set', $key, $value); } %modified = (); if (%deleted) { $self->_redis_call('del', keys %deleted); %deleted = (); } $self->_redis_call('incr', 'version'); my @result = $self->_redis_call('exec'); $cacheVersion = pop @result; } # Wrapper to reconnect to redis in case of detecting a failure when # issuing a command. # sub _redis_call { my ($self, $command, @args) = @_; # Check process id and respawn redis if has changed (fork) if ($self->{pid} ne $$) { $self->_respawn(); } my $response; my @response; my $wantarray = wantarray; my $tries = 5; for my $i (1 .. $tries) { our $failure = 1; our $ret; { local $SIG{PIPE}; $SIG{PIPE} = sub { # EBox::warn("$$ Reconnecting to redis server after SIGPIPE"); $failure = 1; }; eval { if ($wantarray) { @response = $self->{redis}->$command(@args); map { utf8::encode($_) if defined ($_) } @response; } else { $response = $self->{redis}->$command(@args); utf8::encode($response) if defined ($response); } $failure = 0; }; $ret = $@; if ($ret or $failure) { # EBox::warn("$$ - $ret"); sleep(1); # Disconnected, try to reconnect eval { $self->_initRedis(); $self->_respawn(); $failure = 1; }; if ($@) { # EBox::warn("$$ -- $@"); sleep(1); $failure = 1; } } } if ($failure) { if ( $i < $tries) { warn "Reconnecting to redis server ($i try)..."; } else { my $conProblem = 1; if ($ret) { $conProblem = $ret =~ m/closed connection/; } if ($conProblem) { throw EBox::Exceptions::Internal('Cannot connect to redis server'); } else { my $error = "Redis command '$command @args' failed: $ret"; throw EBox::Exceptions::Internal($error); } } } else { last; } } if ($wantarray) { return @response; } else { return $response; } } # Reconnect to redis server sub _respawn { my ($self) = @_; my $user = $self->_user(); my $home = $self->_home(); my $filepasswd = $self->_passwd(); my $redis = Redis->new(sock => "$home/redis.$user.sock"); $redis->auth($filepasswd); $self->{redis} = $redis; $self->{pid} = $$; # EBox::info("$$ Respawning the redis connection"); } # Initialize redis daemon if it's not running sub _initRedis { my ($self) = @_; # User corner redis server is managed by service return if ( $self->_user eq 'ebox-usercorner' ); unless (EBox::Service::running('ebox.redis')) { EBox::info('Starting redis server'); # Write redis daemon conf file $self->writeConfigFile(); # Launch daemon, added sleep to avoid first connection problems EBox::Sudo::silentRoot('start ebox.redis && sleep 1'); } } # Method: writeConfigFile # # Write redis daemon config file # sub writeConfigFile { my ($self, $user) = @_; defined($user) or $user = EBox::Config::user(); my $home = $self->_home($user); my $confFile = $home . REDIS_CONF; my $pass = $self->_passwd($home); my $uid = getpwnam($user); my $dir = $user; $dir =~ s/ebox/zentyal/; my $port = $self->_port($user); my @params = (); push (@params, user => $user); push (@params, home => $home); push (@params, dir => $dir); push (@params, port => $port); push (@params, passwd => $pass); EBox::Module::Base::writeConfFileNoCheck($confFile, 'core/redis.conf.mas', \@params, {mode => '0600', uid => $uid}); } # Stop redis server, sync changes to disk before sub stopRedis { my ($self) = @_; # User corner redis server is managed by service return if ($self->_user eq 'ebox-usercorner'); $self->_redis_call('save'); EBox::Service::manage('ebox.redis', 'stop'); } # Returns redis server password sub _passwd { my ($self, $home) = @_; defined($home) or $home = $self->_home(); return read_file($home . REDIS_PASS) or throw EBox::Exceptions::External('Could not open passwd file'); } # Returns redis server port sub _port { my ($self, $user) = @_; defined($user) or $user = $self->_user(); if ($user eq 'ebox-usercorner') { return EBox::Config::configkey('redis_port_usercorner'); } else { return EBox::Config::configkeyFromFile('redis_port', CLIENT_CONF); } # Unknown user return undef; } sub _home { my ($self, $user) = @_; defined($user) or $user = $self->_user(); my ($name,$passwd,$uid,$gid,$quota,$comment,$gcos,$dir,$shell,$expire) = getpwnam($user); return $dir; } # Returns current user name sub _user { my @userdata = getpwuid(POSIX::getuid()); return $userdata[0]; } 1; zentyal-core-2.3.21+quantal1/src/EBox/AbstractDBEngine.pm0000664000000000000000000000645612017102272017713 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::AbstractEngine # # This class exposes the interface to be implemented by a new database backend # module. # package EBox::AbstractDBEngine; use strict; use warnings; use DBI; use EBox::Exceptions::NotImplemented; sub new { my $class = shift; my $self = {}; bless($self, $class); return $self; } # Method: _dbname # # This function returns the database name. # sub _dbname { throw EBox::Exceptions::NotImplemented(); } # Method: _dbuser # # This function returns the database user. # sub _dbuser { throw EBox::Exceptions::NotImplemented(); } # Method: _dbpass # # This function returns the database user password. # sub _dbpass { throw EBox::Exceptions::NotImplemented(); } # Method: _connect # # This function do the necessary operations to establish a connection with the # database. # sub _connect { throw EBox::Exceptions::NotImplemented(); } # Method: _disconnect # # This function do the necessary operations to get disconnected from the # database. # sub _disconnect { throw EBox::Exceptions::NotImplemented(); } # Method: update # # This function performs an update in the database. # # Parameters: # $table: The table name to insert data. # $values: A hash ref with database fields name and values pairs that do you # want to update # $where: An array ref with conditions for the where # sub update { throw EBox::Exceptions::NotImplemented(); } # Method: insert # # This function do the necessary operations to create and establish an insert # operation to a table form the database. # # Parameters: # $table: The table name to insert data. # $values: A hash ref with database fields name and values pairs that do you # want to insert to.the table name passed as parameter too. # sub insert { throw EBox::Exceptions::NotImplemented(); } # Method: delete # # This function performs a delete in the database. # # Parameters: # $table: The table name to insert data. # $where: An array ref with conditions for the where # sub delete { throw EBox::Exceptions::NotImplemented(); } # Method: query # # This function do the necessary operations to create and establish a query # operation to a table form the database. # sub query { throw EBox::Exceptions::NotImplemented(); } # Method: dumpDB # # Makes a dump of the database in the specified file # # Parameters: # $outputFike : the output database dump file sub dumpDB { throw EBox::Exceptions::NotImplemented(); } # Method: restoreDB # # restore a database from a dump file. # # Parameters: # $file - the dump file sub restoreDB { throw EBox::Exceptions::NotImplemented(); } 1; zentyal-core-2.3.21+quantal1/src/EBox/Global/0000775000000000000000000000000012017102272015443 5ustar zentyal-core-2.3.21+quantal1/src/EBox/Global/TestStub.pm0000664000000000000000000000402612017102272017560 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA package EBox::Global::TestStub; # Description: # use strict; use warnings; use Test::MockObject; use Params::Validate; use EBox::Global; use EBox::Module::Config::TestStub; my %modulesInfo; sub setAllEBoxModules { my (%modulesByName) = @_; while (my ($name, $module) = each %modulesByName) { setEBoxModule($name, $module); } } sub setEBoxModule { my ($name, $class, $depends) = @_; validate_pos(@_ ,1, 1, 0); defined $depends or $depends = []; $modulesInfo{$name} = { class => $class, depends => $depends, changed => 0, }; } sub clear { %modulesInfo = (); } sub _fakedReadModInfo { my ($name) = @_; if (exists $modulesInfo{$name}) { return $modulesInfo{$name}; } return undef; } sub _fakedWriteModInfo { my ($self, $name, $info) = @_; $modulesInfo{$name} = $info; } sub _fakedModNames { return [keys %modulesInfo]; } sub fake { EBox::Module::Config::TestStub::fake(); # needed by some method, like changed # state of modules Test::MockObject->fake_module('EBox::Global', readModInfo => \&_fakedReadModInfo, modNames => \&_fakedModNames, ); } # only for interface completion sub unfake { } 1; zentyal-core-2.3.21+quantal1/src/EBox/HtmlBlocks.pm0000664000000000000000000000343112017102272016644 0ustar # Copyright (C) 2009-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Interface class EBox::HtmlBlocks, it should be implemented by CGI namespaces # # This file defines the interface and gives the implementation for the EBox # namespace package EBox::HtmlBlocks; use strict; use warnings; use EBox::Html; sub new { my $class = shift; my $self = {}; bless($self, $class); return $self; } # Method: title # # Returns the html code for the title # # Returns: # # string - containg the html code for the title # sub title { return EBox::Html::title(); } # Method: titleNoAction # # Returns the html code for the title without actions # # Returns: # # string - containg the html code for the title # sub titleNoAction { return EBox::Html::titleNoAction(); } # Method: menu # # Returns the html code for the menu # # Returns: # # string - containg the html code for the menu # sub menu { my ($self, $current) = @_; return EBox::Html::menu($current); } # Method: footer # # Returns the html code for the footer page # # Returns: # # string - containg the html code for the footer page # sub footer { return EBox::Html::footer(); } 1; zentyal-core-2.3.21+quantal1/src/EBox/ServiceManager.pm0000664000000000000000000003735712017102272017513 0ustar # Copyright (C) 2008-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA # Class: EBox::ServiceManager # # This class is responsible to check if a given module is going to # modify a file which has previously been modifyed by a user. # # It uses MD5 digests to track the changes # # package EBox::ServiceManager; use strict; use warnings; use EBox::Config; use EBox::Exceptions::MissingArgument; use EBox::Exceptions::Internal; use EBox::Global; use EBox::Sudo; use Error qw(:try); use File::Basename; use constant CONF_DIR => 'ServiceModule/'; use constant CLASS => 'EBox::Module::Service'; # Group: Public methods sub new { my $class = shift; my $ebox = EBox::Global->getInstance(); my $self = { 'confmodule' => $ebox, }; bless($self, $class); return $self; } # Method: moduleStatus # # It returns the status for all modules which implement the interface CLASS # # The status is a hash ref containing: # # configured - boolean to store if the user has accepted to carry out # the operation that involve enabling the module # depends - boolean to store if the module's dependencies are met # and therefore it can be enabled or not # status - boolean to store if the user wants the service enabled # # Returns: # # Array ref of hashes containing the module's name and its status # sub moduleStatus { my ($self) = @_; my $global = $self->{'confmodule'}; my @mods; my $change = undef; #for my $mod (@{$global->modInstancesOfType(CLASS)}) { for my $modName (@{$self->_dependencyTree()}) { my $mod = $global->modInstance($modName); my $status = {}; $status->{'configured'} = $mod->configured(); $status->{'depends'} = $self->dependencyEnabled($mod->name()); $status->{'status'} = $mod->isEnabled(); $status->{'name'} = $mod->name(); $status->{'printableName'} = $mod->printableName(); $status->{'printableDepends'} = $self->printableDepends($mod->name()); unless ($status->{'configured'} and $status->{'depends'}) { $status->{'status'} = undef; $mod->enableService(undef); } if ( $mod->showModuleStatus() ) { push (@mods, $status); } } return \@mods; } # Method: printableDepends # # Return the printable dependencies for a given module, that is: i18ized # # Parameters: # # module - module's name # # Returns: # # Array ref sub printableDepends { my ($self, $module) = @_; my $global = $self->{'confmodule'}; my @depends; for my $mod (@{$global->modInstance($module)->enableModDepends()}) { push (@depends, $global->modInstance($mod)->printableName()); } return \@depends; } # Method: dependencyEnabled # # Check if every module's dependency is enabled # # Returns: # # boolean - true if all dependencies are enabled sub dependencyEnabled { my ($self, $module) = @_; my $global = $self->{'confmodule'}; for my $mod (@{$global->modInstance($module)->enableModDepends()}) { my $instance = $global->modInstance($mod); unless (defined($instance)) { EBox::debug("$mod can't be instanced"); next; } next unless ($instance->isa(CLASS)); return undef unless ($instance->isEnabled()); } return 1; } # Method: enableServices # # Enable/disable module services # # Parameters: # # Hash containing the services and its required status # # Example: # # { 'network' => 1, 'samba' => undef } sub enableServices { my ($self, $services) = @_; my $global = $self->{'confmodule'}; for my $mod (keys %{$services}) { my $instance = $global->modInstance($mod); unless (defined($instance)) { EBox::debug("$mod can't be instanced"); next; } next unless ($instance->isa(CLASS)); $instance->enableService($services->{$mod}); } } # Method: checkFiles # # This method must be called to get all those files which are going # to be modified by eBox and have been modified by the user. # # If returns false, then this method will # return an empty array reference. # # Returns: # # An array ref of hashes containing the following: # # key - file's path # value - some info about why you need to modify the file # # Only those files which have been modified by the user are returned #sub checkFiles #{ # my ($self) = @_; # # unless ($self->checkUserModifications()) { # return []; # } # # my $global = EBox::Global->getInstance(); # my @mods; # for my $modName (@{$global->modifiedModules('enable')}) { # my $modIns = $global->modInstance($modName); # push (@mods, $modIns) if ($modIns->isa(CLASS)); # } # # my @modified; # my %files; # for my $mod (@mods) { # next unless ($mod->configured()); # for my $file (@{$mod->usedFiles()}) { # $file->{'id'} = $self->_fileId($file); # $file->{'globalId'} = $file->{'module'} . '_' . $file->{'id'}; # my $mod = $file->{'module'}; # my $path = $file->{'file'}; # next if (exists $files{$path}); # if ($self->_fileModified($file) # or (not $self->modificationAllowed($mod, $path))) { # push (@modified, $file); # $files{$path} = 1; # } # } # } # # return \@modified; #} # Method: setAcceptedFiles # # This method must be called to set those files which are allowed to # be modified. # # # Params: # # array ref - containing the global identifiers of the files # #sub setAcceptedFiles #{ # my ($self, $accept, $reject) = @_; # # my $gconf = $self->{'gconfmodule'}; # # my $regexp = '(.*)_([^_]+)$'; # for my $global (@{$accept}) { # my ($module, $file) = $global =~ m/$regexp/; # $gconf->st_set_bool(GCONF_DIR . "$module/$file/accepted", 1); # $self->updateFileDigest($module, $self->_idToPath($module, $file)); # } # # for my $global (@{$reject}) { # my ($module, $file) = $global =~ m/$regexp/; # $gconf->st_set_bool(GCONF_DIR . "$module/$file/accepted", undef); # $gconf->st_set_string(GCONF_DIR . "$module/$file/digest", ""); # } # #} # Method: modificationAllowed # # Given a file it returns the user policy for that file # # Params: # # module - module's name # file - file's path # # Return: # # boolean - true if the user allows the modification, false otherwise. # False indicates that either the user has rejected the modification # or has not established any policy yet # #sub modificationAllowed #{ # my ($self, $module, $file) = @_; # # my $gconf = $self->{'gconfmodule'}; # # my $fileEntry = {'module' => $module, 'file' => $file}; # my $fileId = $self->_fileId($fileEntry); # # return $gconf->st_get_bool(GCONF_DIR . "$module/$fileId/accepted"); #} # Method: skipModification # # This method is used to check if a file can be modified or not. # It won't allow the modification if: # # - The user has rejected to modify the file # - The user has accepted by there's been a manual change in the # file since the last time eBox modified it # # Parameters: # # module - module's name # file - file's path # # Return: # # boolean - true if we must skip the modification, false otherwise # #sub skipModification #{ # my ($self, $module, $file) = @_; # # return 0 unless ($self->checkUserModifications()); # # my $gconf = $self->{'gconfmodule'}; # # return 1 unless ($self->modificationAllowed($module, $file)); # # my $fileEntry = {'module' => $module, 'file' => $file}; # my $fileId = $self->_fileId($fileEntry); # $fileEntry->{'id'} = $fileId; # # return $self->_fileModified($fileEntry); #} # Method: updateFileDigest # # This method is used to update a file digests. It should be used as soon # as eBox modifies a file. # # Parameters: # # module - module's name # file - file's path # #sub updateFileDigest #{ # my ($self, $module, $file) = @_; # # return unless ($self->checkUserModifications()); # # my $gconf = $self->{'gconfmodule'}; # # my $fileEntry = {'module' => $module, 'file' => $file}; # $self->_updateMD5($fileEntry); #} # Method: updateDigests # # This method must be called once changes have been saved to # update the digests. # #sub updateDigests #{ # my ($self) = @_; # # my $global = EBox::Global->getInstance(); # my $class = 'EBox::Module::Service'; # # for my $mod (@{$global->modInstancesOfType($class)}) { # for my $file (@{$mod->usedFiles()}) { # next unless ($self->modificationAllowed($file->{'module'}, # $file->{'file'})); # $self->_updateMD5($file); # } # } #} # Method: updateModuleDigests # # This method must be called when the user configures a module # for first time. Note this function updates digest for a # given module while updateDigests does the same for all modules. # # This function set the packages as accepted # # Parameters: # # module - module name # #sub updateModuleDigests #{ # my ($self, $modName) = @_; # # return unless $self->checkUserModifications(); # # my $global = EBox::Global->getInstance(); # my $gconf = $self->{'gconfmodule'}; # # my $mod = $global->modInstance($modName); # unless (defined($mod)) { # throw EBox::Exceptions::Internal("Can't instance $modName"); # } # my @files; # for my $file (@{$mod->usedFiles()}) { # $self->_updateMD5($file); # my $module = $file->{'module'}; # push (@files, "${module}_" . $self->_fileId($file)); # # } # # $self->setAcceptedFiles(\@files, []); #} # Method: enableAllModules # # This method enables all modules implementing # # sub enableAllModules { my ($self) = @_; my $global = EBox::Global->getInstance(); for my $modName (@{$self->_dependencyTree()}) { my $module = $global->modInstance($modName); $module->setConfigured(1); $module->enableService(1); #$self->updateModuleDigests($modName); try { $module->enableActions(); } otherwise { my ($ex) = @_; $module->setConfigured(0); $module->enableService(0); EBox::warn("Falied to enable module $modName: " . $ex->text()); }; #$self->updateModuleDigests($modName); } } # Method: checkUserModifications # # Indicate if eBox must check user modifications from # configuration files or not. It is defined in main Zentyal # configuration file at "/etc/zentyal" by # "override_user_modification" value # # Returns: # # true - if it must check user modifications # # false - otherwise # #sub checkUserModifications #{ # return not EBox::Config::boolean('override_user_modification'); #} # Method: modulesInDependOrder # # Return a module list ordered by the boot dependencies # sub modulesInDependOrder { my ($self) = @_; my @modules = map { EBox::Global->modInstance($_) } (@{$self->_dependencyBootTree()}); return \@modules; } # Group: Private methods sub _dependencyTree { my ($self, $tree, $hash) = @_; return $self->_genericDependencyTree($tree, $hash, 'enableModDepends'); } sub _dependencyBootTree { my ($self, $tree, $hash) = @_; return $self->_genericDependencyTree($tree, $hash, 'bootDepends'); } sub _genericDependencyTree { my ($self, $tree, $hash, $func) = @_; $tree = [] unless (defined($tree)); $hash = {} unless (defined($hash)); my $global = $self->{'confmodule'}; my $numMods = @{$tree}; for my $mod (@{$global->modInstancesOfType(CLASS)}) { next if (exists $hash->{$mod->{'name'}}); my $depOk = 1; for my $modDep (@{$mod->$func()}) { unless (exists $hash->{$modDep}) { $depOk = undef; last; } } if ($depOk) { push (@{$tree}, $mod->{'name'}); $hash->{$mod->{'name'}} = 1; } } if ($numMods == @{$tree}) { return $tree; } else { return $self->_genericDependencyTree($tree, $hash, $func); } } #sub _fileId #{ # my ($self, $file) = @_; # # unless (defined($file)) { # throw EBox::Exceptions::MissingArgument($file); # } # # my $gconf = $self->{'gconfmodule'}; # # my $modPath = GCONF_DIR. $file->{'module'}; # for my $dir ($gconf->st_all_dirs($modPath)) { # my $hashEntry = $gconf->st_hash_from_dir("$dir"); # next unless (exists $hashEntry->{'file'}); # return basename($dir) if ($hashEntry->{'file'} eq $file->{'file'}); # } # # # File does not exist within our database. Add new entry # my $id = $gconf->get_unique_id('file', "$modPath"); # $gconf->st_set_string("$modPath/$id/file", $file->{'file'}); # $gconf->st_set_string("$modPath/$id/digest", ""); # $gconf->st_set_bool("$modPath/$id/accepted", undef); # # return $id; #} #sub _idToPath #{ # my ($self, $module, $id) = @_; # # my $gconf = $self->{'gconfmodule'}; # # $gconf->st_get_string(GCONF_DIR . "$module/$id/file"); #} #sub _fileModified #{ # my ($self, $file) = @_; # # unless (defined($file)) { # throw EBox::Exceptions::MissingArgument($file); # } # # unless (defined($file->{'id'})) { # throw EBox::Exception::Internal( # 'file must contain a valid directory id'); # } # # my $gconf = $self->{'gconfmodule'}; # # my $dir = GCONF_DIR . $file->{'module'} . '/' . $file->{'id'}; # # unless ($gconf->st_dir_exists($dir)) { # throw EBox::Exceptions::Internal("$dir does not exist"); # } # # my $hashEntry = $gconf->st_hash_from_dir($dir); # unless ($hashEntry->{'file'} eq $file->{'file'}) { # throw EBox::Exceptions::Internal("file does not match"); # } # # my $stDigest = $hashEntry->{'digest'}; # my $currDigest = $self->_getMD5($file->{'file'}); # return ($stDigest ne $currDigest) #} #sub _updateMD5 #{ # my ($self, $file) = @_; # # unless (defined($file)) { # throw EBox::Exceptions::MissingArgument($file); # } # # my $gconf = $self->{'gconfmodule'}; # my $currDigest = $self->_getMD5($file->{'file'}); # # my $modPath = GCONF_DIR. $file->{'module'}; # for my $dir ($gconf->st_all_dirs($modPath)) { # my $hashEntry = $gconf->st_hash_from_dir($dir); # next unless (exists $hashEntry->{'file'} # and $hashEntry->{'file'} eq $file->{'file'}); # my $stDigest = $hashEntry->{'digest'}; # # return if ($stDigest eq $currDigest); # $gconf->st_set_string("$dir/digest", $currDigest); # return; # } # # my $id = $gconf->get_unique_id('file', "$modPath"); # $gconf->st_set_string("$modPath/$id/file", $file->{'file'}); # $gconf->st_set_string("$modPath/$id/digest", $currDigest); #} #sub _getMD5 #{ # my ($self, $path) = @_; # # unless (EBox::Sudo::fileTest('-e', $path)) { # EBox::debug("File $path does not exist. So we won't compute its digest"); # return 'notexists'; # } # # my $md5 = pop(@{EBox::Sudo::root("md5sum $path | cut -d' ' -f1")}); # chomp $md5; # # return $md5; #} 1; zentyal-core-2.3.21+quantal1/src/cgi/0000775000000000000000000000000012017102272014150 5ustar zentyal-core-2.3.21+quantal1/src/cgi/ebox.cgi0000775000000000000000000001327512017102272015604 0ustar #!/usr/bin/perl # Copyright (C) 2010-2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA use strict; use warnings; use EBox::Gettext; use Error qw(:try); use POSIX qw(:signal_h); try { use EBox::CGI::Run; use EBox; # Workaround to clear Apache2's process mask my $sigset = POSIX::SigSet->new(); $sigset->fillset(); sigprocmask(SIG_UNBLOCK, $sigset); EBox::init(); EBox::CGI::Run->run($ENV{'script'}, 'EBox'); } otherwise { my $ex = shift; use Devel::StackTrace; use CGI qw/:standard/; use Data::Dumper; use File::Slurp qw(read_file); my $trace = Devel::StackTrace->new; print STDERR $trace->as_string; print STDERR Dumper($ex); print header; print start_html(-title => __('Zentyal'), -script => [ {-type => 'text/javascript', -src => '/data/js/common.js'}, {-type => 'text/javascript', -src => '/data/js/prototype.js'}, {-type => 'text/javascript', -src => '/data/js/scriptaculous/scriptaculous.js'} ], -head => Link({-rel=>'stylesheet', -href => '/dynamic-data/css/public.css', -type => 'text/css' }), ); # template params my $theme = EBox::Global::_readTheme(); my $templateFile; my $params = {}; $params->{image_title} = $theme->{image_title}; $params->{actions} = __('Actions'); $params->{go_back} = __('Go back'); $params->{title} = __('Sorry, an unexpected error has occurred'); my @brokenPackages = @{ _brokenPackages() }; if ($theme->{hide_bug_report}) { $params->{title} .= '. ' . __('Please contact support.'); $params->{brokenPackages} = ''; if (@brokenPackages) { $params->{brokenPackages} = __x('The following software packages are not correctly installed: {pack}', pack => join ', ', @brokenPackages); } $templateFile = 'cgiErrorNoReport.html'; } elsif (@brokenPackages) { $params->{show_details} = __('Show technical details'); $params->{main_text} = __x('There are some software packages which are not correctly installed: {pack}.

    You should reinstall them and retry your operation.

    ', pack => join ', ', @brokenPackages ); $templateFile = 'cgiErrorBrokenPackages.html'; } else { $params->{show_details} = __('Show technical details'); $params->{report} = __('Report the problem'); $params->{cancel} = __('Cancel'); $params->{email} = __('Email (you will receive updates on the report)'); $params->{description} = __('Describe in English what you where doing'); $params->{newticket_url} = 'http://trac.zentyal.org/newticket'; $params->{report_error} = __("Couldn't send the report"); $params->{report_sent} = __('The report has been successfully sent, you can keep track of it in the following ticket:'); my $instructions = '' . __('To do a manual report, please follow these instructions:') . ''; $instructions .= '
  • ' . __('Create a new ticket in the Zentyal trac by clicking ') . '' . __('here') . ".
  • "; $instructions .= '
  • ' . __('Write a short description of the problem in the summary field') . '.
  • '; $instructions .= '
  • ' . __('Write a detailed report of what you were doing before this problem ocurred in the description field') . '.
  • '; $instructions .= '
  • ' . __('Download the log file with additional information by clicking') . ' ' . __('here') . '.
  • '; $instructions .= '
  • ' . __('Attach the downloaded file in the ticket') . '.
  • '; $instructions .= '
    '; $params->{report_instructions} = $instructions; $templateFile = 'cgiError.html'; } my $error; if ( $ex->can('text') ) { $error = $ex->text(); } elsif ( $ex->can('as_text') ) { $error = $ex->as_text(); } $error =~ s/"/'/g; $params->{error} = $error; my $stacktrace = $ex->stacktrace(); $params->{stacktrace_html} = '
      '; for my $line (split (/\n/, $stacktrace)) { $params->{stacktrace_html} .= "
    • $line
    • \n"; } $params->{stacktrace_html} .= '
    '; $stacktrace =~ s/"/'/g; $params->{stacktrace} = $stacktrace; # Fill HTML template values my $html = read_file(EBox::Config::templates . $templateFile); foreach my $key (%{$params}) { my $value = $params->{$key}; $html =~ s/\Q{{ $key }}\E/$value/g; } utf8::decode($html); print $html; }; sub _brokenPackages { my @pkgs; my @output = `dpkg -l | grep -i ^i[fFHh]`; foreach my $line (@output) { my ($status, $name, $other) = split '\s+', $line, 3; push @pkgs, $name; } return \@pkgs; } 1; zentyal-core-2.3.21+quantal1/src/cgi/desktop-services.cgi0000775000000000000000000000414312017102272020133 0ustar #!/usr/bin/perl # Copyright (C) 2012 eBox Technologies S.L. # # This program is free software; you can redistribute it and/or modify # it under the terms of the GNU General Public License, version 2, as # published by the Free Software Foundation. # # This program 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 General Public License for more details. # # You should have received a copy of the GNU General Public License # along with this program; if not, write to the Free Software # Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA use strict; use warnings; use EBox::Gettext; use Error qw(:try); use POSIX qw(:signal_h); use Devel::StackTrace; use Data::Dumper; use HTTP::Response; try { use EBox::CGI::Run; use EBox; # Workaround to clear Apache2's process mask my $sigset = POSIX::SigSet->new(); $sigset->fillset(); sigprocmask(SIG_UNBLOCK, $sigset); EBox::init(); EBox::CGI::Run->run('DesktopServices::Index', 'EBox'); } catch EBox::Exceptions::External with { my $ex = shift; my $trace = Devel::StackTrace->new; print STDERR $trace->as_string; print STDERR Dumper($ex); my $error; if ( $ex->can('text') ) { $error = $ex->text(); } elsif ( $ex->can('as_text') ) { $error = $ex->as_text(); } $error =~ s/"/'/g; print "Status: 400 Bad Request\n"; print "Content-type: text/html\n\n"; print ''; print '400 Bad Request'; print ''; print '

    Error

    '; print "

    $error

    "; print ''; } otherwise { my $ex = shift; my $trace = Devel::StackTrace->new; print STDERR $trace->as_string; print STDERR Dumper($ex); print "Status: 500 Internal Server Error\n"; print "Content-type: text/html\n\n"; print ''; print '500 Internal Server Error'; print ''; print '

    Internal Server Error

    '; print ''; }; zentyal-core-2.3.21+quantal1/stubs/0000775000000000000000000000000012017102272013757 5ustar zentyal-core-2.3.21+quantal1/stubs/apache.mas0000664000000000000000000002164212017102272015707 0ustar <%doc> Template to set the apache configuration file for the Web administration Parameters: port - Int the listening port group - String the group that must live apache-perl process user - String the user that the apache-perl process must be serverroot - String the path to the Apache server root tmpdir - String the path to the Apache temporary directory debug - String to enable Apache::Reload or not *(Optional)* Default value: 'no' restrictedResources - Array containing a hash ref structure which contains the following elements: - allowedIPs - array ref containing the allowed IPs for accessing this resource - name - String the resource name - type - String the resource type. Options: file, directory or location. includes - Array containing those file includes we want to add this Apache configuration desktop_services_enabled - String to enable the Zentyal desktop services CGI Default value: 'no' desktop_services_port - Port for the Zentyal desktop services CGI virtual host Default value: 6895 # FIXME: unhardcode paths passing them as args... <%args> $port $group $user $serverroot $tmpdir $eboxconfdir $debug => 'no' @restrictedResources => () @includes => () $desktop_services_enabled => 'no' $desktop_services_port => 6895 Timeout 300 KeepAlive Off MaxKeepAliveRequests 500 KeepAliveTimeout 15 AddDefaultCharset utf-8 PidFile <% $tmpdir %>/apache.pid StartServers 1 MinSpareServers 1 MaxSpareServers 1 MaxClients 1 MaxRequestsPerChild 10000 # worker MPM # StartServers: initial number of server processes to start # MaxClients: maximum number of simultaneous client connections # MinSpareThreads: minimum number of worker threads which are kept spare # MaxSpareThreads: maximum number of worker threads which are kept spare # ThreadsPerChild: constant number of worker threads in each server process # MaxRequestsPerChild: maximum number of requests a server process serves StartServers 1 MinSpareThreads 1 MaxSpareThreads 1 ThreadsPerChild 1 MaxClients 1 MaxRequestsPerChild 10000 PerlInterpMaxRequests 10000 Include /etc/apache2/mods-available/auth_basic.load Include /etc/apache2/mods-available/authn_file.load Include /etc/apache2/mods-available/authz_default.load Include /etc/apache2/mods-available/authz_groupfile.load Include /etc/apache2/mods-available/authz_host.load Include /etc/apache2/mods-available/authz_user.load Include /etc/apache2/mods-available/autoindex.load Include /etc/apache2/mods-available/cgi.load Include /etc/apache2/mods-available/deflate.conf Include /etc/apache2/mods-available/deflate.load Include /etc/apache2/mods-available/dir.conf Include /etc/apache2/mods-available/dir.load Include /etc/apache2/mods-available/env.load Include /etc/apache2/mods-available/mime.load Include /etc/apache2/mods-available/negotiation.load Include /etc/apache2/mods-available/setenvif.load Include /etc/apache2/mods-available/rewrite.load Include /etc/apache2/mods-available/ssl.conf Include /etc/apache2/mods-available/ssl.load Include /etc/apache2/mods-available/status.load Include /etc/apache2/mods-available/perl.load Listen <% $port %> User <% $user %> Group <% $group %> ServerAdmin webmaster@localhost ServerName localhost ServerSignature Off ServerTokens Min <%perl> if ( @restrictedResources > 0 ) { foreach my $restrictedResource (@restrictedResources) { my @allowIPs = @{$restrictedResource->{allowedIPs}}; my $restrictedResourceName = $restrictedResource->{name}; my $foundNobody = grep { $_ eq 'nobody' } @allowIPs; my $foundAll = grep { $_ eq 'all' } @allowIPs; my $type = $restrictedResource->{type}; if ( $type eq 'file' ) { $type = 'Files'; } elsif ( $type eq 'dir' ) { $type = 'Directory'; } elsif ( $type eq 'location' ) { $type = 'Location'; } <<% $type %> <% $restrictedResourceName %>> Order Allow,Deny % unless ( $foundNobody ) { % foreach my $ip (@allowIPs) { Allow from <% $ip %> % } % } > % } % } UseCanonicalName Off TypesConfig /etc/mime.types DefaultType text/plain MIMEMagicFile /usr/share/misc/file/magic.mime HostnameLookups Off ErrorLog /var/log/zentyal/error.log LogLevel warn LogFormat "%h %l %u %t \"%r\" %>s %b \"%{Referer}i\" \"%{User-Agent}i\" \"%{forensic-id}n\"" combined EnableExceptionHook On EnableExceptionHook On ServerSignature Off ServerTokens Min SSLEngine on SSLProtocol all SSLCipherSuite HIGH:MEDIUM SSLInsecureRenegotiation on SSLCertificateFile /var/lib/zentyal/conf/ssl/ssl.pem SSLCACertificatePath <% $eboxconfdir %>ssl-ca/ BrowserMatch "Mozilla/2" nokeepalive BrowserMatch "MSIE 4\.0b2;" nokeepalive downgrade-1.0 force-response-1.0 BrowserMatch "RealPlayer 4\.0" force-response-1.0 BrowserMatch "Java/1\.0" force-response-1.0 BrowserMatch "JDK/1\.0" force-response-1.0 > DocumentRoot /usr/share/zentyal/www/ % foreach my $includeFile (@includes) { Include <% $includeFile %> % } SSLEngine on SSLProtocol all SSLCipherSuite HIGH:MEDIUM SSLCertificateFile /var/lib/zentyal/conf/ssl/ssl.pem SSLCACertificatePath <% $eboxconfdir %>ssl-ca/ Options SymLinksIfOwnerMatch AllowOverride None Options Indexes MultiViews FollowSymLinks AllowOverride None Order allow,deny Allow from all Options Indexes MultiViews AllowOverride None Order allow,deny Allow from all CustomLog /var/log/zentyal/access.log combined % if ($debug eq 'yes') { # Yes, this is useless right now. #PerlInitHandler Apache2::Reload % } PerlWarn On PerlModule EBox::Auth PerlSetVar EBoxPath / PerlSetVar EBoxLoginScript /Login/Index PerlSetVar EBoxSatisfy Any PerlSetVar EBoxCookieName Zentyal PerlSetVar AuthCookieDebug 0 AuthType EBox::Auth AuthName EBox SetHandler perl-script PerlHandler EBox::Auth->login SSLOptions +StdEnvVars AuthType EBox::Auth AuthName EBox PerlAuthenHandler EBox::Auth->authenticate PerlAuthzHandler EBox::Auth->authorize require valid-user SetHandler perl-script PerlHandler ModPerl::Registry PerlSendHeader On AllowOverride None Options +ExecCGI Order allow,deny Allow from all RewriteEngine On # Compatibility with old URLs (permanent redirects) RewriteRule ^/ebox(.*) /$1 RewriteRule ^/zentyal(.*) /$1 # skip rewrites for favicon and login RewriteCond %{REQUEST_FILENAME} ^/favicon.ico$ [OR] RewriteCond %{REQUEST_FILENAME} ^/LOGIN$ RewriteRule .? - [S=100] # Map /ebox.cgi to the right Perl CGI and redirect RewriteRule ^/ebox.cgi$ / # From /data/ to / and finish RewriteRule ^/data(.*) $1 [L] # From /dynamic-data/ to the right directory in FS and finish RewriteRule ^/dynamic-data(.*) /var/lib/zentyal/dynamicwww$1 [L] RewriteRule ^/(.*) /usr/share/zentyal/cgi/ebox.cgi [E=script:$1,L] % if ($desktop_services_enabled eq 'yes') { Listen <% $desktop_services_port %> > DocumentRoot /usr/share/zentyal/www/ SSLEngine on SSLProtocol all SSLCipherSuite HIGH:MEDIUM SSLCertificateFile /var/lib/zentyal/conf/ssl/ssl.pem SSLCACertificatePath <% $eboxconfdir %>ssl-ca/ SSLOptions +StdEnvVars SetHandler perl-script PerlHandler ModPerl::Registry PerlSendHeader On AllowOverride None Options +ExecCGI Order allow,deny Allow from all CustomLog /var/log/zentyal/access-desktop-services.log combined RewriteEngine On RewriteRule ^/(.*) /usr/share/zentyal/cgi/desktop-services.cgi [E=script:$1,L] % } zentyal-core-2.3.21+quantal1/stubs/redis.conf.mas0000664000000000000000000004272712017102272016527 0ustar <%args> $user $home $dir $port $passwd # Redis configuration file example # Note on units: when memory size is needed, it is possible to specifiy # it in the usual form of 1k 5GB 4M and so forth: # # 1k => 1000 bytes # 1kb => 1024 bytes # 1m => 1000000 bytes # 1mb => 1024*1024 bytes # 1g => 1000000000 bytes # 1gb => 1024*1024*1024 bytes # # units are case insensitive so 1GB 1Gb 1gB are all the same. # By default Redis does not run as a daemon. Use 'yes' if you need it. # Note that Redis will write a pid file in /var/run/redis.pid when daemonized. daemonize no # When run as a daemon, Redis write a pid file in /var/run/redis.pid by default. # You can specify a custom pid file location here. pidfile /var/lib/zentyal/tmp/redis.<% $user %>.pid # Accept connections on the specified port, default is 6379 port <% $port %> # If you want you can bind a single interface, if the bind option is not # specified all the interfaces will listen for connections. # bind 127.0.0.1 # Specify the path for the unix socket that will be used to listen for # incoming connections. There is no default, so Redis will not listen # on a unix socket when not specified. # unixsocket <% $home %>/redis.<% $user %>.sock # Close the connection after a client is idle for N seconds (0 to disable) timeout 300 # Set server verbosity to 'debug' # it can be one of: # debug (a lot of information, useful for development/testing) # verbose (many rarely useful info, but not a mess like the debug level) # notice (moderately verbose, what you want in production probably) # warning (only very important / critical messages are logged) loglevel notice # Specify the log file name. Also 'stdout' can be used to force # Redis to log on the standard output. Note that if you use standard # output for logging but daemonize, logs will be sent to /dev/null logfile /var/log/<% $dir %>/redis-server.log # To enable logging to the system logger, just set 'syslog-enabled' to yes, # and optionally update the other syslog parameters to suit your needs. # syslog-enabled no # Specify the syslog identity. # syslog-ident redis # Specify the syslog facility. Must be USER or between LOCAL0-LOCAL7. # syslog-facility local0 # Set the number of databases. The default database is DB 0, you can select # a different one on a per-connection basis using SELECT where # dbid is a number between 0 and 'databases'-1 databases 1 ################################ SNAPSHOTTING ################################# # # Save the DB on disk: # # save # # Will save the DB if both the given number of seconds and the given # number of write operations against the DB occurred. # # In the example below the behaviour will be to save: # after 900 sec (15 min) if at least 1 key changed # after 300 sec (5 min) if at least 10 keys changed # after 60 sec if at least 10000 keys changed # # Note: you can disable saving at all commenting all the "save" lines. save 900 1 save 300 10 save 60 10000 # Compress string objects using LZF when dump .rdb databases? # For default that's set to 'yes' as it's almost always a win. # If you want to save some CPU in the saving child set it to 'no' but # the dataset will likely be bigger if you have compressible values or keys. rdbcompression yes # The filename where to dump the DB dbfilename dump.rdb # The working directory. # # The DB will be written inside this directory, with the filename specified # above using the 'dbfilename' configuration directive. # # Also the Append Only File will be created inside this directory. # # Note that you must specify a directory here, not a file name. dir /var/lib/<% $dir %>/ ################################# REPLICATION ################################# # Master-Slave replication. Use slaveof to make a Redis instance a copy of # another Redis server. Note that the configuration is local to the slave # so for example it is possible to configure the slave to save the DB with a # different interval, or to listen to another port, and so on. # # slaveof # If the master is password protected (using the "requirepass" configuration # directive below) it is possible to tell the slave to authenticate before # starting the replication synchronization process, otherwise the master will # refuse the slave request. # # masterauth # When a slave lost the connection with the master, or when the replication # is still in progress, the slave can act in two different ways: # # 1) if slave-serve-stale-data is set to 'yes' (the default) the slave will # still reply to client requests, possibly with out of data data, or the # data set may just be empty if this is the first synchronization. # # 2) if slave-serve-stale data is set to 'no' the slave will reply with # an error "SYNC with master in progress" to all the kind of commands # but to INFO and SLAVEOF. # slave-serve-stale-data yes ################################## SECURITY ################################### # Require clients to issue AUTH before processing any other # commands. This might be useful in environments in which you do not trust # others with access to the host running redis-server. # # This should stay commented out for backward compatibility and because most # people do not need auth (e.g. they run their own servers). # # Warning: since Redis is pretty fast an outside user can try up to # 150k passwords per second against a good box. This means that you should # use a very strong password otherwise it will be very easy to break. # requirepass <% $passwd %> # Command renaming. # # It is possilbe to change the name of dangerous commands in a shared # environment. For instance the CONFIG command may be renamed into something # of hard to guess so that it will be still available for internal-use # tools but not available for general clients. # # Example: # # rename-command CONFIG b840fc02d524045429941cc15f59e41cb7be6c52 # # It is also possilbe to completely kill a command renaming it into # an empty string: # # rename-command CONFIG "" ################################### LIMITS #################################### # Set the max number of connected clients at the same time. By default there # is no limit, and it's up to the number of file descriptors the Redis process # is able to open. The special value '0' means no limits. # Once the limit is reached Redis will close all the new connections sending # an error 'max number of clients reached'. # #maxclients 10 # Don't use more memory than the specified amount of bytes. # When the memory limit is reached Redis will try to remove keys with an # EXPIRE set. It will try to start freeing keys that are going to expire # in little time and preserve keys with a longer time to live. # Redis will also try to remove objects from free lists if possible. # # If all this fails, Redis will start to reply with errors to commands # that will use more memory, like SET, LPUSH, and so on, and will continue # to reply to most read-only commands like GET. # # WARNING: maxmemory can be a good idea mainly if you want to use Redis as a # 'state' server or cache, not as a real DB. When Redis is used as a real # database the memory usage will grow over the weeks, it will be obvious if # it is going to use too much memory in the long run, and you'll have the time # to upgrade. With maxmemory after the limit is reached you'll start to get # errors for write operations, and this may even lead to DB inconsistency. # # maxmemory # MAXMEMORY POLICY: how Redis will select what to remove when maxmemory # is reached? You can select among five behavior: # # volatile-lru -> remove the key with an expire set using an LRU algorithm # allkeys-lru -> remove any key accordingly to the LRU algorithm # volatile-random -> remove a random key with an expire set # allkeys->random -> remove a random key, any key # volatile-ttl -> remove the key with the nearest expire time (minor TTL) # noeviction -> don't expire at all, just return an error on write operations # # Note: with all the kind of policies, Redis will return an error on write # operations, when there are not suitable keys for eviction. # # At the date of writing this commands are: set setnx setex append # incr decr rpush lpush rpushx lpushx linsert lset rpoplpush sadd # sinter sinterstore sunion sunionstore sdiff sdiffstore zadd zincrby # zunionstore zinterstore hset hsetnx hmset hincrby incrby decrby # getset mset msetnx exec sort # # The default is: # # maxmemory-policy volatile-lru # LRU and minimal TTL algorithms are not precise algorithms but approximated # algorithms (in order to save memory), so you can select as well the sample # size to check. For instance for default Redis will check three keys and # pick the one that was used less recently, you can change the sample size # using the following configuration directive. # # maxmemory-samples 3 ############################## APPEND ONLY MODE ############################### # By default Redis asynchronously dumps the dataset on disk. If you can live # with the idea that the latest records will be lost if something like a crash # happens this is the preferred way to run Redis. If instead you care a lot # about your data and don't want to that a single record can get lost you should # enable the append only mode: when this mode is enabled Redis will append # every write operation received in the file appendonly.aof. This file will # be read on startup in order to rebuild the full dataset in memory. # # Note that you can have both the async dumps and the append only file if you # like (you have to comment the "save" statements above to disable the dumps). # Still if append only mode is enabled Redis will load the data from the # log file at startup ignoring the dump.rdb file. # # The name of the append only file is "appendonly.log" # # IMPORTANT: Check the BGREWRITEAOF to check how to rewrite the append # log file in background when it gets too big. appendonly no # The name of the append only file (default: "appendonly.aof") # appendfilename appendonly.aof # The fsync() call tells the Operating System to actually write data on disk # instead to wait for more data in the output buffer. Some OS will really flush # data on disk, some other OS will just try to do it ASAP. # # Redis supports three different modes: # # no: don't fsync, just let the OS flush the data when it wants. Faster. # always: fsync after every write to the append only log . Slow, Safest. # everysec: fsync only if one second passed since the last fsync. Compromise. # # The default is "everysec" that's usually the right compromise between # speed and data safety. It's up to you to understand if you can relax this to # "no" that will will let the operating system flush the output buffer when # it wants, for better performances (but if you can live with the idea of # some data loss consider the default persistence mode that's snapshotting), # or on the contrary, use "always" that's very slow but a bit safer than # everysec. # # If unsure, use "everysec". appendfsync always # appendfsync everysec # appendfsync no # When the AOF fsync policy is set to always or everysec, and a background # saving process (a background save or AOF log background rewriting) is # performing a lot of I/O against the disk, in some Linux configurations # Redis may block too long on the fsync() call. Note that there is no fix for # this currently, as even performing fsync in a different thread will block # our synchronous write(2) call. # # In order to mitigate this problem it's possible to use the following option # that will prevent fsync() from being called in the main process while a # BGSAVE or BGREWRITEAOF is in progress. # # This means that while another child is saving the durability of Redis is # the same as "appendfsync none", that in pratical terms means that it is # possible to lost up to 30 seconds of log in the worst scenario (with the # default Linux settings). # # If you have latency problems turn this to "yes". Otherwise leave it as # "no" that is the safest pick from the point of view of durability. no-appendfsync-on-rewrite no ################################ VIRTUAL MEMORY ############################### # Virtual Memory allows Redis to work with datasets bigger than the actual # amount of RAM needed to hold the whole dataset in memory. # In order to do so very used keys are taken in memory while the other keys # are swapped into a swap file, similarly to what operating systems do # with memory pages. # # To enable VM just set 'vm-enabled' to yes, and set the following three # VM parameters accordingly to your needs. vm-enabled no # vm-enabled yes # This is the path of the Redis swap file. As you can guess, swap files # can't be shared by different Redis instances, so make sure to use a swap # file for every redis process you are running. Redis will complain if the # swap file is already in use. # # The best kind of storage for the Redis swap file (that's accessed at random) # is a Solid State Disk (SSD). # # *** WARNING *** if you are using a shared hosting the default of putting # the swap file under /tmp is not secure. Create a dir with access granted # only to Redis user and configure Redis to create the swap file there. vm-swap-file /var/lib/redis/redis.swap # vm-max-memory configures the VM to use at max the specified amount of # RAM. Everything that deos not fit will be swapped on disk *if* possible, that # is, if there is still enough contiguous space in the swap file. # # With vm-max-memory 0 the system will swap everything it can. Not a good # default, just specify the max amount of RAM you can in bytes, but it's # better to leave some margin. For instance specify an amount of RAM # that's more or less between 60 and 80% of your free RAM. vm-max-memory 0 # Redis swap files is split into pages. An object can be saved using multiple # contiguous pages, but pages can't be shared between different objects. # So if your page is too big, small objects swapped out on disk will waste # a lot of space. If you page is too small, there is less space in the swap # file (assuming you configured the same number of total swap file pages). # # If you use a lot of small objects, use a page size of 64 or 32 bytes. # If you use a lot of big objects, use a bigger page size. # If unsure, use the default :) vm-page-size 32 # Number of total memory pages in the swap file. # Given that the page table (a bitmap of free/used pages) is taken in memory, # every 8 pages on disk will consume 1 byte of RAM. # # The total swap size is vm-page-size * vm-pages # # With the default of 32-bytes memory pages and 134217728 pages Redis will # use a 4 GB swap file, that will use 16 MB of RAM for the page table. # # It's better to use the smallest acceptable value for your application, # but the default is large in order to work in most conditions. vm-pages 134217728 # Max number of VM I/O threads running at the same time. # This threads are used to read/write data from/to swap file, since they # also encode and decode objects from disk to memory or the reverse, a bigger # number of threads can help with big objects even if they can't help with # I/O itself as the physical device may not be able to couple with many # reads/writes operations at the same time. # # The special value of 0 turn off threaded I/O and enables the blocking # Virtual Memory implementation. vm-max-threads 4 ############################### ADVANCED CONFIG ############################### # Hashes are encoded in a special way (much more memory efficient) when they # have at max a given numer of elements, and the biggest element does not # exceed a given threshold. You can configure this limits with the following # configuration directives. hash-max-zipmap-entries 512 hash-max-zipmap-value 64 # Similarly to hashes, small lists are also encoded in a special way in order # to save a lot of space. The special representation is only used when # you are under the following limits: list-max-ziplist-entries 512 list-max-ziplist-value 64 # Sets have a special encoding in just one case: when a set is composed # of just strings that happens to be integers in radix 10 in the range # of 64 bit signed integers. # The following configuration setting sets the limit in the size of the # set in order to use this special memory saving encoding. set-max-intset-entries 512 # Active rehashing uses 1 millisecond every 100 milliseconds of CPU time in # order to help rehashing the main Redis hash table (the one mapping top-level # keys to values). The hash table implementation redis uses (see dict.c) # performs a lazy rehashing: the more operation you run into an hash table # that is rhashing, the more rehashing "steps" are performed, so if the # server is idle the rehashing is never complete and some more memory is used # by the hash table. # # The default is to use this millisecond 10 times every second in order to # active rehashing the main dictionaries, freeing memory when possible. # # If unsure: # use "activerehashing no" if you have hard latency requirements and it is # not a good thing in your environment that Redis can reply form time to time # to queries with 2 milliseconds delay. # # use "activerehashing yes" if you don't have such hard requirements but # want to free memory asap when possible. #activerehashing yes ################################## INCLUDES ################################### # Include one or more other config files here. This is useful if you # have a standard template that goes to all redis server but also need # to customize a few per-server settings. Include files can include # other files, so use this wisely. # # include /path/to/local.conf # include /path/to/other.conf zentyal-core-2.3.21+quantal1/stubs/sudo.mas0000664000000000000000000000044012017102272015431 0ustar <%args> $user => 'ebox' @extraUsers => () # Zentyal sudo configuration # allow there users to run all commands without passwd <% $user %> ALL = NOPASSWD:ALL % if (@extraUsers) { % foreach my $extraUser (@extraUsers) { <% $extraUser %> ALL = NOPASSWD:ALL % } % } zentyal-core-2.3.21+quantal1/extra/0000775000000000000000000000000012017102272013742 5ustar zentyal-core-2.3.21+quantal1/extra/css/0000775000000000000000000000000012017102272014532 5ustar zentyal-core-2.3.21+quantal1/extra/css/login.css.mas0000664000000000000000000000600012017102272017127 0ustar <%args> $logo_style $body_background_color $main_color $loginin_padding_style $error_color $error_background $error_border $warn_color $warn_background $warn_border #login { margin: 0px auto; min-width: 500px; border: 2px solid <% $main_color %>; padding: 40px 50px; background: #fff; text-align: left; border-radius: 10px; -moz-border-radius: 10px; box-shadow: 0 0 20px <% $main_color %>; } fieldset#loginfieldset { border: none; padding-left: 15%; } fieldset#loginfieldset label { float: left; width: 88px; font-size: 14px; line-height: 24px; } #reason { text-align: left; font-weight: normal; border-bottom: 1px solid <% $error_border %>; border-top: 1px solid <% $error_border %>; padding: 5px; font-size: 11px; margin: 0 0 4px 0px; background: <% $error_background %>; color: <% $error_color %>; max-width: 250; } #capsWarning, #ieWarning, #ffWarning { text-align: left; font-weight: normal; border-bottom: 1px solid <% $warn_border %>; border-top: 1px solid <% $warn_border %>; padding: 5px; font-size: 11px; margin: 0 0 4px 0px; background: <% $warn_background %>; color: <% $warn_color %>; max-width: 250; } body { background-color: <% $body_background_color %>; color: #000000; font-family: Verdana, sans-serif; behavior: url(/data/js/csshover.htc); margin-top: 90px; } td#logotd { padding-right: 25px; } td.labeltd { min-width: 88px; font-size: 14px; line-height: 24px; color:#666; } #footer { text-align: center; font-size: 11px; color: #888; } #footer a { text-decoration: underline; color: #888; } #footer a:active { text-decoration: underline; color: #888; } #footer a:hover { text-decoration: none; background: #888; color: #fff; } input[type='password'], input[type='text'] { border:1px solid #A5A5A5; border-radius: 3px; background-image: -ms-linear-gradient(top, #f5f5f5, #fff); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#fff)); background-image: -webkit-linear-gradient(top, #f5f5f5, #fff); background-image: -o-linear-gradient(top, #f5f5f5, #fff); background-image: linear-gradient(top, #f5f5f5, #fff); background-image: -moz-linear-gradient(top, #f5f5f5, #fff); filter: progid:dximagetransform.microsoft.gradient(startColorstr='#f5f5f5', endColorstr='#fff', GradientType=0); filter: progid:dximagetransform.microsoft.gradient(enabled=false); padding:10px; width:150px; } input[type='submit'] { color:#666; border-radius: 3px; background-color: #E9E9E9; border-top: 1px solid #A5A5A5; border-left: 1px solid #A5A5A5; border-right: 2px solid #A5A5A5; border-bottom: 2px solid #A5A5A5; padding:5px 10px; cursor:pointer; text-transform: uppercase; letter-spacing:1px; font-weight:bold; } input[type='submit']:hover {background:#DFDFDF} zentyal-core-2.3.21+quantal1/extra/css/public.css.mas0000664000000000000000000010717312017102272017312 0ustar /*<%args> $main_color $secondary_color $third_color $header_img_padding $hmenu_bg_color $hmenu_hover_bg_color $remote_svcs_bg_color $remote_svcs_hover_bg_color $footer_custom_style $data_table_border_color $data_table_inner_border_color $data_table_color $msg_bg_color $helpbutton_color $nav_li_color $a_notchanged_hover_color $data_table_bg_color $additional_footer_style $progress_bar_bg_color $progress_bar_fg_color $help_color $help_background $help_border $note_color $note_background $note_border $error_color $error_background $error_border $warn_color $warn_background $warn_border $ad_color $ad_background $ad_border $ad_extra_style $data_table_bg_even $data_table_bg_highlight $data_table_bg_highlight_hover $dashboard_thead_bg_color $image_note */ #header { height: 70px; padding:0; margin: 0; color: transparent; border-bottom: 3px solid #EEE; } #header a:hover { background: transparent; color: black; } #header img { <% $header_img_padding %> padding-bottom: 0; margin: 5px 0px 0px -5px; } #hmenu { position: relative; display: block; top: -60px; float:right; } #hmenu a { float:left; font-size: 11px; color: #666; line-height: 16px; padding: 25px 15px 5px 15px; text-decoration: none; border-top: 1px solid #A5A5A5; border-left: 1px solid #A5A5A5; border-right: 2px solid #A5A5A5; border-bottom: 2px solid #A5A5A5; border-radius:3px; -webkit-border-radius: 3px; -moz-border-radius: 3px; margin-left:5px; background-color: #E9E9E9; } #hmenu a:hover { background-color: #DFDFDF; } #hmenu #remote_services_link{ background:url("/data/images/remote.png") center 2px no-repeat ; background-color: <% $remote_svcs_hover_bg_color %>; display:block; border-top: 1px solid #A7BE56; border-left: 1px solid #A7BE56; border-right: 2px solid #A7BE56; border-bottom: 2px solid #A7BE56; } #hmenu #remote_services_link:hover { background-color: <% $remote_svcs_bg_color %>; } #hmenu #m { background:url("/data/images/logout.gif") center 5px no-repeat ; } #hmenu #m:hover { background-color: #DFDFDF; } #hmenu #changes_menu { background: #FFD600 url("/data/images/save.gif") center 5px no-repeat; display:none; border-top: 1px solid #E6A603; border-left: 1px solid #E6A603; border-right: 2px solid #E6A603; border-bottom: 2px solid #E6A603; } #hmenu #changes_menu:hover { background-color: #FFC200; } #top { padding:0; font-size: 0; margin:0; } #content { z-index: 9; margin-top: 10px; margin-left: 180px; position: relative; } #menu { clear: left; float: left; padding: 10px 0 0 0; margin-bottom: 50px; } #footer { color: #888; clear: both; font-size: 11px; <% $footer_custom_style %> width: 100%; margin-top: 20px; margin-left: 0px; padding: 3px; floar:left; border-top:3px solid #eee; } #footer a { text-decoration: underline; color: #888; } #footer a:active { text-decoration: underline; color: #888; } #footer a:hover { text-decoration: none; background: #888; color: #fff; } <% $additional_footer_style %> #menusearch { background: #fff url("/data/images/magnifying-glass.png") right center no-repeat; border: 1px solid #DDD; padding: 3px; width: 150px; height:20px; margin: 11px 0px; } #menusearch:hover, #menusearch:focus { border: 1px solid <% $main_color %>; } #module_list { width: 100%; float: left; } #module_list > div {padding: 5px 10px; float:left; border-right:1px solid #ccc;} #module_list > div > a {text-decoration:none;} #module_list > div:hover {background-color:#eee;} #widget_list { margin-top: 10px; float: left; width:100%; } #widget_list > .note{margin:0px;} .help { display: none; border-bottom: 1px solid <% $help_border %>; padding: 9pt 9pt 9pt 33pt; color: <% $help_color %>; font-size: 8pt; background: <% $help_background %> url("/data/images/help.png") 6pt 6pt no-repeat; margin: 1em 0px 1em 0px; } .note, .ad { border-bottom: 1px solid <% $note_border %>; padding: 9pt 9pt 9pt 33pt; margin-bottom: 10px; color: <% $note_color %>; font-size: 8pt; background: <% $note_background %> url("<% $image_note %>") 6pt 6pt no-repeat; margin: 1em 0px 0em 0px; margin-bottom: 5px; } .ad { <% $ad_extra_style %> } .error { border-bottom: 1px solid <% $error_border %>; padding: 9pt 9pt 9pt 33pt; color: <% $error_color %>; font-size: 8pt; background: <% $error_background %> url("/data/images/error.png") 6pt 6pt no-repeat; margin: 1em 0px 0em 0px; } .warning, .adwarning { border-bottom: 1px solid <% $warn_border %>; padding: 9pt 9pt 9pt 33pt; color: <% $warn_color %>; font-size: 8pt; background: <% $warn_background %> url("/data/images/warning.png") 6pt 6pt no-repeat; margin: 1em 0px 0em 0px; margin-bottom: 10px; } .adwarning { <% $ad_extra_style %> } .helptd { width: 100%; color: #AAAAAA; text-align: justify; margin-left: 30px; padding-left: 25px; background: url("../images/help.png") no-repeat center left; } .ifname { color: #000000; font-weight: bold; border-bottom: 7px solid #FFFFFF; } .tleft { text-align: left; } .objectAddr { font-size: 11px; margin-left: 30px; } .test { border-bottom: 0px; } .ok { color: #70FC00; } .nok { color: #BC0606; } .tcenter { text-align: center; } .thOptions { padding:0; margin:0; width: 20%; } .thOptionsFwd { padding:0; margin:0; background: #787878; } .summarySection { width: 100%; margin-bottom: 15px; margin-left:0px; } .summarySection tr { vertical-align: center; } .summary_value { padding: 5px 10px 5px 15px; font-family: Verdana, sans-serif; background-color: #F4F4F4; line-height:25px; } .summary_good, .summary_good a { color: #339933; } .summary_warning, .summary_warning a { color: #FF8C00; font-weight: bold; } .summary_error, .summary_error a { color: #DD0000; font-weight: bold; } .summary_error a, .summary_warning a, .summary_good a { text-decoration: none; } .summaryKey { width: 35%; color: #4c4c4c; background-color: #ffffff; text-align: right; font-weight: bold; padding: 3px 4px 3px 4px; } .legend { margin-bottom: 15px; } .dataTable { width: 100%; /*float: left;*/ color:#000000; font-size: 11px; border: 1px solid <% $data_table_border_color %>; border-collapse: collapse; border-bottom:0; /*margin-bottom: 20px; */ } .dataTable thead { background: <% $secondary_color %>; color: white; } .dataTable thead > tr > th { font-size: 11px; border-right: #fff 1px solid; } .dataTable th { font-family: Verdana, 'Bitstream Vera Sans', sans-serif; padding: 8px 6px 8px 6px; font-weight: bold; vertical-align: middle; /*border: 1px solid <% $data_table_inner_border_color %>;*/ } .dataTable td { padding: 3px; /*border-bottom: 1px solid <% $data_table_inner_border_color %>; border-left: 1px solid <% $data_table_color %>;*/ border-right: 1px solid <% $data_table_color %>; } .dataTable td a { color: #000000; background-color: none; text-decoration: none; } .dataTable td a:hover { background: none; text-decoration: underline; color: <% $secondary_color %>; } .dataTable tr.even { background: <% $data_table_bg_even %>; } .dataTable tr.odd { background: #FFF; } .dataTable tr.highlight { background: <% $data_table_bg_highlight %>; border-color: white; } .pagination_n{margin: 0px 10px;} .iptable { font-size: 11px; margin-left: 25px; background-color: #EEEEEE; } iptable tr { background-color: #EEEEEE; } .iptableaux { width: 100%; margin-left: 0px; margin-top: 10px; } .iptableaux th { color: #4f4f4f; background-color: #e4e8f1; } .tright { text-align: right; vertical-align: top; white-space: nowrap; } .border { border-bottom: 1px solid #CCCCCC; } .noborder { border: 0; } .endform { padding-top: 8px; border-top: 1px solid #CCCCCC; } select { font-size: 11px; font-family: Verdana, sans-serif; border:1px solid #A5A5A5; border-radius: 3px; -webkit-border-radius: 3px; -moz-border-radius: 3px; background-image: -ms-linear-gradient(top, #f5f5f5, #fff); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#fff)); background-image: -webkit-linear-gradient(top, #f5f5f5, #fff); background-image: -o-linear-gradient(top, #f5f5f5, #fff); background-image: linear-gradient(top, #f5f5f5, #fff); background-image: -moz-linear-gradient(top, #f5f5f5, #fff); filter: progid:dximagetransform.microsoft.gradient(startColorstr='#f5f5f5', endColorstr='#fff', GradientType=0); filter: progid:dximagetransform.microsoft.gradient(enabled=false); padding:5px; } .multiselect { margin: 2px; height: 10em; padding: 2px; overflow: auto; border: 2px inset; } input[type='password'], input[type='text'] { border:1px solid #A5A5A5; border-radius: 3px; -webkit-border-radius: 3px; -moz-border-radius: 3px; background-image: -ms-linear-gradient(top, #f5f5f5, #fff); background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f5f5f5), to(#fff)); background-image: -webkit-linear-gradient(top, #f5f5f5, #fff); background-image: -o-linear-gradient(top, #f5f5f5, #fff); background-image: linear-gradient(top, #f5f5f5, #fff); background-image: -moz-linear-gradient(top, #f5f5f5, #fff); filter: progid:dximagetransform.microsoft.gradient(startColorstr='#f5f5f5', endColorstr='#fff', GradientType=0); filter: progid:dximagetransform.microsoft.gradient(enabled=false); padding:5px; } input[type='password'][disabled], input[type='text'][disabled] { border-color:#eee; background-color:#f5f5f5; } input[type='submit'], button, input[type='button'] { color:#666; border-radius: 3px; -webkit-border-radius: 3px; -moz-border-radius: 3px; background-color: #E9E9E9; border-top: 1px solid #A5A5A5; border-left: 1px solid #A5A5A5; border-right: 2px solid #A5A5A5; border-bottom: 2px solid #A5A5A5; padding:5px 10px ; cursor:pointer; text-transform: uppercase; letter-spacing:1px; font-weight:bold; font-size:11px; } button > img {margin-bottom:-3px;} .big { color:#666; border-radius: 3px; -webkit-border-radius: 3px; -moz-border-radius: 3px; background-color: #E9E9E9; border-top: 1px solid #A5A5A5; border-left: 1px solid #A5A5A5; border-right: 2px solid #A5A5A5; border-bottom: 2px solid #A5A5A5; padding:5px 10px; cursor:pointer; text-transform: uppercase; letter-spacing:1px; font-weight:bold; } #installButtons > button > img { margin-bottom:-3px;} input[type='submit']:hover, input[type='button']:hover {background:#DFDFDF} button:hover {background:#DFDFDF} input[type='submit'][disabled], input[type='button'][disabled]{ cursor:default; border-color:#dbdbdb; background-color:#eee; color:#BBB; } input[type='submit'][disabled]:hover {background-color:#eee;} .inputText, .inputTextError { color: #000000; font-size: 11px; font-family: Verdana, sans-serif; } .inputTextError { border: 3px solid red; } .inputText:focus { background-color: #ffffff; } .inputTextLogin { color: #000000; font-size: 11px; font-family: Verdana, sans-serif; } .inputButtonRestart { font-size: 10px; font-family: Verdana, sans-serif; margin: 0px; padding: 4px 8px 4px 20px !important; text-transform: none!important; font-weight: normal !important; } .inputButtonRestart[name='restart']{ background:#BDEDF7 url("/data/images/restart.gif") 5px center no-repeat; border-color:#8CBBC5; } .inputButtonRestart[name='restart']:hover{ background:#D4EFF5 url("/data/images/restart.gif") 5px center no-repeat;} .inputButtonRestart[name='start'], .inputButtonRestart[value='start']{ background:#BFE784 url("/data/images/start.gif") 5px center no-repeat; border-color:#829E59} .inputButtonRestart[name='start']:hover{background:#D8F1B3 url("/data/images/start.gif") 5px center no-repeat;} .inputButtonRestart[name='stop']{ background:#FAA68B url("/data/images/stop_p.gif") 5px center no-repeat; border-color:#DB866B} .inputButtonRestart[name='stop']:hover{background:#F5B099 url("/data/images/stop_p.gif") 5px center no-repeat;} .inputButton, input[type='image'], .edit_table_B, #installButtons > button { font-size: 11px; font-family: Verdana, sans-serif; background: whiteSmoke; border-top: 1px solid #A5A5A5; border-left: 1px solid #A5A5A5; border-right: 2px solid #A5A5A5; border-bottom: 2px solid #A5A5A5; border-radius: 3px; -webkit-border-radius: 3px; -moz-border-radius: 3px; padding: 3px; } .inputButton:hover, input[type='image']:hover, .edit_table_B:hover, #installButtons > button:hover {background: #E7E7E7;} #installButtons > button { padding: 5px 10px !important; } .edit_table_B { display: inline-block;} .edit_table_B:hover { display: inline-block; background: #E7E7E7 !important;} .inputButton[disabled], input[type='image'][disabled]{cursor:default;} .inputButton[disabled]:hover, input[type='image'][disabled]:hover{background-color:whitesmoke;} .msg { color: #FFFFFF; padding: 10px; background-color: <% $msg_bg_color %>; } .logout { font-size: 11px; font-family: Verdana, sans-serif; border-top: 1px solid #ccc; border-left: 1px solid #ccc; border-right: 1px solid #4c4c4c; border-bottom: 1px solid #4c4c4c; background-color: #eee; color: #000; padding: 2px 5px 2px 5px; } body { width: 960px; margin: 0px auto; padding:0; color: #000000; font-size: 11px; font-family: Verdana, sans-serif; background-color: #f5f5f5; behavior: url(/data/js/csshover.htc); } .title { display: inline; color: #666; font-weight: bold; font-size: 25px; font-family: Trebuchet MS, Verdana, sans-serif; float: none; } .title_link_sep { display: inline; color: #999; font-weight: bold; font-size: 25px; font-family: Trebuchet MS, Verdana, sans-serif; float: none; } .title_link { display: inline; color: #999; font-weight: bold; font-size: 25px; font-family: Trebuchet MS, Verdana, sans-serif; float: none; } h3 { font-size: 20px; font-family: Trebuchet MS, Verdana, sans-serif; font-weight: bold; color: <% $secondary_color %>; border-bottom:2px solid; padding:3px 0px; margin:20px 0px 10px 0px; } h4 { font-size: 12px; color: #666464; margin-top: 10px; margin-bottom: 7px; padding-left: 2px; letter-spacing: .2ex; } a { color: #888; text-decoration: underline; } a:active { color: #888; text-decoration: underline; } a:hover { text-decoration: none; /*color: #ffffff;*/ } a img { border: 0px; } input { font-size: 11px; font-family: Verdana, sans-serif; } .dataTableMini caption, .ftitle { text-align: left; border: 0px; padding: 2px; } .dataTable caption { font-weight: bold; background-color: #ffffff; text-align: left; color: #4c4c4c; border: 0px; padding: 2px; } .formTable { border: 0px; border-collapse: collapse; border-spacing: 0px; width: auto; } .formTable td { padding-top: 4px; padding-bottom: 4px; } .endproxyform { margin-left: 167px; padding-top: 7px; } .ifaces { border-bottom: 1px solid #787878; padding-bottom: 5px; padding-left: 15px; padding-top: 13px; } .tabs { border-bottom: 1px solid #989898; margin: 10px 0px 0 0; padding: 5px 10px 5px 10px; } .tabs li { list-style: none; display : inline; margin-right: 5px; } .tabs li a { padding: 5px 10px 5px 10px; border-top: 1px solid #dddddd; border-left: 1px solid #dddddd; border-right: 1px solid #dddddd; background: #efefef; color: #999; -moz-border-radius: 5px 5px 0px 0px; -webkit-border-radius: 5px 5px 0px 0px; border-radius: 5px 5px 0px 0px; } .tabs li a:hover{ } .tabs li a.current{ background: #fff; font-weight: bold; border: 1px solid #989898; border-bottom: 1px solid white; -moz-border-radius: 5px 5px 0px 0px; -webkit-border-radius: 5px 5px 0px 0px; border-radius: 5px 5px 0px 0px; } .selected { border-top: 1px solid #787878; border-left: 1px solid #787878; border-right: 1px solid #787878; border-bottom: 1px solid #fff; padding: 5px 10px 5px 10px; background: #fff; color: #787878; font-weight: bold; -moz-border-radius: 5px 5px 0px 0px; -webkit-border-radius: 5px 5px 0px 0px; border-radius: 5px 5px 0px 0px; } .ifaces a { border-top: 1px solid #dddddd; border-left: 1px solid #dddddd; border-right: 1px solid #dddddd; background: #eee; color: #999; padding: 5px 10px 5px 10px; -moz-border-radius: 5px 5px 0px 0px; -webkit-border-radius: 5px 5px 0px 0px; border-radius: 5px 5px 0px 0px; text-decoration: none; } .ifaces a:hover { color: #4c4c4c; background-color: #eeeeee; border-top: 1px solid #aaa; border-left: 1px solid #aaa; border-right: 1px solid #aaa; } #helpbutton { font-size: 11px; font-family: Trebuchet MS, sans-serif; text-align: center; position: absolute; right: 0px; top: 10px; } #helpbutton a { color: #999; padding: 15px 3px 3px 3px; border-radius: 3px; text-decoration: none; background:url("/data/images/hidehelp.png") top center no-repeat; } #helpbutton a:hover { color: #428592; background:url("/data/images/showhelp.png") top center no-repeat; } #helpbutton > #hidehelp { background:url("/data/images/showhelp.png") top center no-repeat; color: #428592; } #helpbutton > #hidehelp:hover {color: #999; background:url("/data/images/hidehelp.png") top center no-repeat;} #titlehelp { width: auto; height: 25px; } #confbutton { font-size: 11px; font-family: Trebuchet MS, sans-serif; text-align: center; position: absolute; right: 0px; top: 10px; } #confbutton a { color: #666; padding: 15px 3px 3px 3px; border-radius: 3px; text-decoration: none; background:url("/data/images/edit-table.gif") top center no-repeat; filter: alpha(opacity=70); -moz-opacity: 0.7; KhtmlOpacity: .7; opacity: .7; } #confbutton a:hover { filter: alpha(opacity=100); -moz-opacity: 1; KhtmlOpacity: 1; opacity: 1; } #nav, #nav ul, #nav li ul { margin: 0; padding: 0; list-style: none; } #nav { margin-bottom: 0em; } #nav li, #nav li li { display: inline; padding: 0; margin: 0; } #nav li li { display: none; } #nav .navarrow{ background-image: url('/data/images/desc.gif'); background-position: center right; background-repeat:no-repeat; } #nav .despleg{ background-image: url('/data/images/asc.gif') !important; background-position: center right; background-repeat:no-repeat; } #nav li a { display: block; padding: 10px 20px 10px 4px; text-decoration: none; margin: 0px; line-height: 120%; color: <% $nav_li_color %>; font-weight: bold; /*background-color: #ffffff; border-bottom: 1px solid #e9e9e9;*/ border-top: 1px solid #ccc; } #nav li a:hover { background-color: rgba(220, 220, 220, 1); color: #000; } #nav li li a { display: block; padding: 6px 6px 6px 4px; margin:0; /*background: rgb(247,247,247);*/ border-top:1px dashed #ccc; /*border-top:0;*/ font-weight: normal; } #nav li li a:hover { background-color: rgba(230, 230, 230, 1); color: #000; } #nav { line-height: 1em; width: 160px; } #nav .submenu { margin-left:20px; } .sleft { float: left; } .sright { float: right; } .stitle { color: #4c4c4c; margin: 3px; font-weight: bold; padding: 1px 1px 1px 3px; line-height: 14px; } .trimp { border-bottom: 1px solid #eeeeee; background-color: #ffe9e8; } pre { background-color: #4B4B4B; color: #EAEAEA; border: 1px solid #BABABA; padding: 10px; overflow: auto; } .notchanged { background-color: <% $third_color %>; display:none; } a.notchanged:hover { background-color: <% $a_notchanged_hover_color %>; } .changed { display: block !important; } .comment { font-style: italic; color: #4a4a4a; font-size: 9px; } #enable { background-color: #FFFFCC; padding: 10px; border: 1px solid #FFCC99; margin: 5px 0 5px 0; } .enable { background-color: #FFFFCC; padding: 10px; border: 1px solid #FFCC99; margin: 5px 0 5px 0; } .bold { font-weight: bold; } .legendName { padding-right: 10px; } .hidden { display: none; } .eDataTable, .bDataTable { width: 100%; font-size: 11px; /*background: <% $data_table_bg_color %>; border: 1px solid <% $data_table_color %>;*/ border-collapse: collapse; } .eDataTable td, .bDataTable td { padding: 6px; } .eDataTable input, .eDataTable select, .bDataTable input, .bDataTable select { } .bDataTable form , .eDataTable form { padding:0; margin:0; } .bDataTable { /*border-bottom: 1px solid <% $data_table_border_color %>; border-top: 1px solid <% $data_table_border_color %>;*/ } .button { cursor: pointer; } .button_link { text-decoration: none; border: 1px solid <% $data_table_border_color %>; padding: 5px 10px; margin-right: 5px; -moz-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; } .insideTab { border: 1px solid #989898; border-top: none; margin: 0px; padding: 10px; background: white; } .addNew { background-color: #E9E9E9; background-image: url('/data/images/add.gif'); background-position: 6px; background-repeat: no-repeat; border-top: 1px solid #A5A5A5; border-left: 1px solid #A5A5A5; border-right: 2px solid #A5A5A5; border-bottom: 2px solid #A5A5A5; border-radius: 3px; -webkit-border-radius: 3px; -moz-border-radius: 3px; padding: 0px 10px 0px 25px; height: 30px; display: inline-block; line-height: 30px; text-decoration: none; font-size: 12px; color: #333; } .addNew:hover { background-color: #dfdfdf } #field_help { color: #888; clear: both; font-size: 11px; } #file_comment { width: 70%; border:3px solid #E5503B; color: #404040; font-weight: bold; padding-left: 5px; background: #FDDDD9; } .conf_file_entry { border-top: 1px solid #ccc; width: 70%; margin-top: 10px; padding-top: 6px; margin-left: 5px; } .conf_file_entry_reason { color: #aaa; margin-bottom: 3px; } .conf_file_entry_reason span { color: black; } .conf_file_entry input { float: right; margin-top: 6px; } .conf_file_entry .image { margin-left: 5px; padding-left: 5px; border-left: 1px solid #767676; } .graph { width: 350px; height: 200px; } #finishForm { border-top: 1px solid #ccc; width: 95%; margin-left:auto; margin-right:auto; margin-top: 10px; } #file_comment { width: 95%; margin-left:auto; margin-right:auto; border:3px solid #E5503B; color: #404040; font-weight: bold; padding-left: 5px; background: #FDDDD9; } .conf_file_entry { border-top: 1px solid #ccc; width: 95%; margin-left:auto; margin-right:auto; margin-top: 10px; padding-top: 6px; margin-left: 5px; } .conf_file_entry_reason { color: #aaa; margin-bottom: 3px; } .conf_file_entry_reason span { color: black; } .conf_file_entry input { float: right; margin-top: 6px; } .conf_file_entry .image { margin-left: 5px; padding-left: 5px; border-left: 1px solid #767676; } #finishForm, .endForm { border-top: 1px solid #ccc; width: 95%; margin-left: 5px; margin-right:auto; margin-top: 10px; } .endForm { padding-top: 5px; } .dashboard { display: block; float: left; width: 385px; } #dashboard1 { padding-right: 5px; } #dashboard2 { padding-left: 5px; } .widgetHandle { height: 100%; width: 100%; position: absolute; cursor: move; left: 0pt; top: 0pt; z-index: 4; } .widgetTopBar { position: relative; z-index: 1; } .widgetTopBackground { background: <% $secondary_color %>; height: 100%; width: 100%; position: absolute; left: 0pt; top: 0pt; z-index: 1; border-radius: 5px 5px 0 0; -webkit-border-radius: 5px 5px 0 0; -moz-border-radius: 5px 5px 0 0; } .widgetName { position: relative; float: left; z-index: 3; font-size: 12px; font-family: Verdana, sans-serif; font-weight: bold; color: white; padding: 3px 6px; } a.closeBox { position: relative; float: right; margin-right:5px; margin-top: 5px; overflow:hidden; z-index: 5; background-image:url('/data/images/widget_controls.png'); height:12px; width:12px; } a.closeBox { background-position: -24px 0px; } a.closeBox:hover { background-position: -24px -12px; background-color: transparent; } a.minBox, a.maxBox { position: relative; float: left; margin-right: 3px; overflow:hidden; z-index: 5; } .widgetBarBox { display: block; min-width: 100px; float: left; margin-left: 5px; margin-right: 5px; } .widgetBarBox > .widgetBox{margin-bottom:0px;} .widgetBox { margin-bottom: 15px; background-color:white; border-radius: 5px ; -webkit-border-radius: 5px; -moz-border-radius: 5px; box-shadow: 0px 0px 10px rgba(200,200,200,1) } .widgetBoxIn { /*border:1px solid <% $third_color %>;*/ padding:10px 0px; } .widgetBoxIn > .summarySection {margin-bottom:0px;} .sectionContent { width: 98%; } a.maxBox { background-position: -15px 0px; } a.maxBox:hover { background-position: 0px 0px; } a.minBox { background-position: -15px -15px; } a.minBox:hover { background-position: 0px -15px; } a.minBox, a.minBox:hover, a.maxBox, a.maxBox:hover { background-image:url('/data/images/triangles.png'); height:15px; width:15px; } .widArrow { float: left; margin-left: 10px; margin-right: 10px; margin-top: 2px; } .widArrow a:hover { background: transparent; } .field_help { color: #888; clear: both; font-size: 11px; } .optional_field { color: #005AA3; clear: both; font-size: 10px; font-style: italic; padding-right: 3px; } .dashboardTable { border-collapse: collapse; border: 0; font-size: 11px; width: 100%; } .dashboardTable thead { color: white; background-color: <% $dashboard_thead_bg_color %>; } .dashboardTable th { font-family: Verdana, 'Bitstream Vera Sans', sans-serif; padding: 3px; font-weight: bold; vertical-align: middle; } .dashboardTable td { padding: 3px; } .dashboardTable td a { color: #000000; background-color: none; text-decoration: none; } .dashboardTable td a:hover { background: none; text-decoration: underline; color: <% $secondary_color %>; } .dashboardTable tr.highlight { background: #E58A22; } .separator { color: white; font-weight: bold; font-size: 12px; /*background: #F3A61D;*/ background: #bbb; padding: 10px 18px 10px 10px; margin-top:10px; /*border-radius: 0 5px 5px 0; -moz-border-radius: 0 5px 5px 0;*/ } .linksBlock { display: block; float: left; width: 170px; margin-right: 12px; margin-bottom: -12px; } .linksTitle { width: 100%; color: #4c4c4c; background-color: #ffffff; text-align: left; font-weight: bold; padding: 3px 4px 3px 4px; } .linksBlock a { color: #03A9F0; } .linksBlock li { list-style: none; margin-bottom: 14px; margin-left: -18px; margin-top: -8px; } /* packet Filter*/ .PackFilter > div { background-color: white; margin:5px 0px; float:left; border-radius:5px; -webkit-border-radius: 5px; -moz-border-radius: 5px; width:100%; padding:10px; border: 1px solid #ccc; } .PackFilter > div > a{ float:left; } .PackFilter > div > div { float:left; width:65%; margin-left:20px; } .PackFilter > div > div > h3 { margin-top:5px; } .PackFilter > div > div .conf_rul { float:left; margin-top:5px; text-decoration:none; color:#666; border-radius: 3px; -webkit-border-radius: 3px; -moz-border-radius: 3px; background: #E9E9E9 url(/data/images/conf_rul.gif) 5px center no-repeat; border-top: 1px solid #A5A5A5; border-left: 1px solid #A5A5A5; border-right: 2px solid #A5A5A5; border-bottom: 2px solid #A5A5A5; padding:5px 10px 5px 30px; cursor:pointer; } .big { text-decoration:none; background-color:#FFD600; border-color:#E6A603; padding:15px 20px; float:left; } .big:hover {background-color:#FFC200;} /* Progress bar */ #progress_bar { width:100%; margin-top:10px; } #progress_bar .bar { height: 30px; margin: 5px 0; background-color: <% $progress_bar_bg_color %>; -moz-border-radius: 5px; border-radius: 5px } .percent{ width: 100%; text-align:center; line-height:30px; height:30px } #progressValue { height: 30px; width: 0px; background-color: <% $progress_bar_fg_color %>; -moz-border-radius: 5px; border-radius: 5px; } /* Software styles */ #software .feature { display:inline; float:left; padding-left:10px; width: 355px; height: 250px; } #software .image:hover { cursor: pointer; } #software .info { background-color:#ecf5da; padding: 15px; -moz-border-radius: 10px; border-radius: 10px; margin-top: 20px; } #software .close_button { float: right; font-size: 1.4em; text-decoration: none; color: #A6C733; font-weight: bold; } #software #packages { margin: 20px 0 10px 0; border: 1px solid #CCC; -moz-border-radius: 15px; -webkit-border-radius: 15px; border-radius: 15px; padding: 10px 0; } #software .package { text-align: center; margin: 8px; display: inline-block; width: 80px; height: 70px; overflow: hidden; padding: 9px 5px; -moz-border-radius: 5px; -webkit-border-radius: 5px; border-radius: 5px; vertical-align: middle; } #software .package:hover { background: #ECF5DA; cursor: pointer; } #software .package img { height: 32px; width: 32px; margin-top: 5px; } #software .package p { margin: 0; font-weight: bold; color: #333; } #software .package_selected, #software .package_selected:hover { background: <% $progress_bar_bg_color %>; } #software .selected { border-style:solid; border-width:1px; color:#000000; padding:5px 10px; font-weight:normal; } .package_installed { background: url(/data/images/apply.gif) 5px 5px no-repeat #ccc !important;} .package_installed:hover { background: url(/data/images/apply.gif) 5px 5px no-repeat #ccc !important;} /*input[name="change"]{ height: 30px; display: block; width: 60px; border: 1px solid #FF7000; background: background-image: linear-gradient(bottom, #FFA200 18%, #FFD500 82%); background-image: -o-linear-gradient(bottom, #FFA200 18%, #FFD500 82%); background-image: -moz-linear-gradient(bottom, #FFA200 18%, #FFD500 82%); background-image: -webkit-linear-gradient(bottom, #FFA200 18%, #FFD500 82%); background-image: -ms-linear-gradient(bottom, #FFA200 18%, #FFD500 82%); background-image: -webkit-gradient( linear, left bottom, left top, color-stop(0.18, #FF8F00), color-stop(0.82, #FFAD00)); color: white; font-weight: bold; border-radius: 3px; -webkit-border-radius: 3px; -moz-border-radius: 3px; } input[name="change"]:hover { border: 1px solid #FF7000; background: background-image: linear-gradient(bottom, #FFD500 18% , #FFA200 82%); background-image: -o-linear-gradient(bottom, #FFD500 18%, #FFA200 82%); background-image: -moz-linear-gradient(bottom, #FFD500 18%, #FFA200 82%); background-image: -webkit-linear-gradient(bottom, #FFD500 18%, #FFA200 82%); background-image: -ms-linear-gradient(bottom, #FFD500 18%, #FFA200 82%); background-image: -webkit-gradient( linear, left bottom, left top, color-stop(0.18, #FFAD00), color-stop(0.82, #FF8F00)); color: white; font-weight: bold; border-radius: 3px; -webkit-border-radius: 3px; -moz-border-radius: 3px; }*/ /* modalbox.js stuff */ #MB_overlay { position: absolute; margin: auto; top: 0; left: 0; width: 100%; height: 100%; z-index: 9999; background-color: #000!important; } #MB_overlay[id] { position: fixed; } #MB_window { position: absolute; top: 0; border: 0 solid; text-align: left; z-index: 10000; } #MB_window[id] { position: fixed!important; } #MB_frame { position: relative; background-color: #fff; height: 100%; } #MB_header { margin: 0; padding: 0; } #MB_content { padding: 6px .75em; overflow: auto; } #MB_caption { font: bold 100% "Lucida Grande", Arial, sans-serif; padding: .5em 2em .5em .75em; margin: 0; text-align: left; font-size: 15px; } #MB_close { display: block; position: absolute; right: 5px; top: 4px; padding: 2px 3px; font-weight: bold; text-decoration: none; font-size: 13px; } #MB_close:hover { background: transparent; } #MB_loading { padding: 1.5em; text-indent: -10000px; background: transparent url(/data/images/spinner.gif) 50% 0 no-repeat; } /* Color scheme */ #MB_frame { padding-bottom:10px; background-color: #FFF; color: #000; -webkit-box-shadow: 0 8px 64px #000; -moz-box-shadow: 0 0 64px #000; box-shadow: 0 0 64px #000; -webkit-border-radius: 7px; -moz-border-radius: 7px; border-radius: 7px; /*width:500px;*/ margin: 0px auto; } #MB_header { background-color: <% $main_color %>; color: #fff; } #MB_caption { color: #fff } #MB_close { color: #777 } #MB_close:hover { color: #000 } /* Alert message */ .MB_alert { margin: 10px 0; text-align: center; } .MB_dialog { top: 100px !important; } .MB_dialog #MB_header { border-radius: 7px 7px 0px 0px; -moz-border-radius: 7px 7px 0px 0px; } .MB_dialog #MB_frame { padding-bottom:10px; background-color: #FFF; color: #000; -webkit-box-shadow: 0 8px 64px #000; -moz-box-shadow: 0 0 64px #000; box-shadow: 0 0 64px #000; -webkit-border-radius: 7px; -moz-border-radius: 7px; border-radius: 7px; width:500px; margin: 0px auto; } #MB_window { width:960px !important; margin:0px auto !important; } /* end modalbox.js stuff */ .headcontainer{ border-radius: 15px ; -moz-border-radius: 15px; border: 1px solid #ccc; padding: 10px 0px; width:100%; margin-top:15px; } .headcontainer > h3, #packages > h3 { font-size: 16px; color: #666; border-bottom: none; padding: 3px 0px; margin: 0px 0px 0px 10px; } .head { margin: 0 30px; padding: 5px; display: inline-block; text-align: center; width:115px; } .head:hover{background-color: #ECF5DA;} .head h3{ font-size:14px; border-bottom:none; color:black; text-align: center; margin-bottom: 2px; margin-top: 0; } zentyal-core-2.3.21+quantal1/extra/css/tableorderer.css.mas0000664000000000000000000000263012017102272020476 0ustar <%args> $secondary_color $data_table_inner_border_color $data_table_bg_color /*TAB SORTER */ .prototools-table{width:100%;background-color:#E1E1E1;text-align:left;font-size:11px;} .prototools-table th{ background-color: <% $secondary_color %>; border:1px solid <% $data_table_inner_border_color %>; cursor:pointer; background-position:right center; background-repeat:no-repeat; background-image:url('/data/images/bg.gif'); color:#FFF; padding: 8px 6px 8px 6px; } .prototools-table th.desc{background-image:url('/data/images/desc.gif');} .prototools-table th.asc{background-image:url('/data/images/asc.gif');} .prototools-table td,.prototools-table th{ padding : 6px} .prototools-table tr.line0{ background-color:#FFF; } .prototools-table tr.line1{ background-color: <% $data_table_bg_color %>; } div.prototools-options {border : 1px solid #E1E1E1; font-size:11px; padding :5px 7px;} div.prototools-pager {border : 1px solid #E1E1E1; font-size:11px; padding :5px 7px; text-align:right;} div.prototools-pager .currentpage {padding-left:10px; padding-right:10px;} div.prototools-pager .totalpages {padding:0;} div.prototools-pager input {border : 1px solid #E1E1E1; font-size:11px; margin :0 3px;} div.prototools-search {border : 1px solid #E1E1E1; font-size:11px; padding :5px 7px;} div.prototools-search input {border : 1px solid #E1E1E1; font-size:11px; margin :0 3px;} th {font-weight:bold;} zentyal-core-2.3.21+quantal1/extra/mysql/0000775000000000000000000000000012017102272015107 5ustar zentyal-core-2.3.21+quantal1/extra/mysql/zentyal.cnf0000664000000000000000000000006612017102272017267 0ustar [mysqld] innodb = off default-storage-engine = MyISAM zentyal-core-2.3.21+quantal1/extra/cron/0000775000000000000000000000000012017102272014703 5ustar zentyal-core-2.3.21+quantal1/extra/cron/zentyal0000775000000000000000000000050312017102272016315 0ustar #!/bin/sh # Check latest version URL="http://update.zentyal.org/last-2.3.txt" TIMEOUT="300" DEST="/var/lib/zentyal/latestversion" wget --timeout $TIMEOUT $URL -O $DEST >/dev/null 2>&1 # Gather reporting info /usr/share/zentyal/gather-reportinfo # Consolidate this reporting info /usr/share/zentyal/consolidate-reportinfo zentyal-core-2.3.21+quantal1/extra/cron/90zentyal-manage-logs0000775000000000000000000000005412017102272020657 0ustar #!/bin/bash /usr/share/zentyal/manage-logs zentyal-core-2.3.21+quantal1/extra/ebox.passwd0000664000000000000000000000004112017102272016115 0ustar abe273236738fa261e7df1613cf68f78 zentyal-core-2.3.21+quantal1/extra/files.list0000664000000000000000000000031312017102272015736 0ustar keyring.gpg /usr/share/zentyal ebox.passwd /var/lib/zentyal/conf cron/zentyal /etc/cron.daily cron/90zentyal-manage-logs /etc/cron.hourly css /usr/share/zentyal/stubs mysql/zentyal.cnf /etc/mysql/conf.d zentyal-core-2.3.21+quantal1/TESTING0000664000000000000000000000142312017102272013657 0ustar TEST DEPENDENCIES ------------ + Debian packages: # apt-get install + libtest-class-perl + modules from cpan + Test::MockTime + Test::Deep + eBox components: libebox TEST DIRECTORIES ----------- 1.- 't' The tests in this directory will been executed by 'make check'. Of course, you may execute them manually using the Perl interpreter or the prove tool 2.- 'rootTests' The tests in this directory requires root permissions. So they won't be executed automatically with 'make check'; you must gain root status and execute them manually 3.- 'interactiveTests' The tests in this directory needs user interaction and some of them root permissions. They won't be executed by 'make check'; you must execute the manually and, in some cases, with root status zentyal-core-2.3.21+quantal1/ChangeLog0000664000000000000000000020316212017102272014375 0ustar HEAD + Modify low-level redis calls to be compatible with the new libredis-perl version in quantal 2.3.21 + Fixes on notifyActions + Check for isDaemonRunning now compatible with asterisk status + Fixed warning call in EBox::Types::HasMany 2.3.20 + New look & feel for the web interface + Adjust slides transition timeout during installation + Audit changes table in save changes popup has scroll and better style + Model messages are printed below model title + noDataMsg now allows to add elements if it makes sense + Fixed ajax/form.mas to avoid phantom change button + EBox::Model::Manager::_setupModelDepends uses full paths so the dependecies can discriminate between models with the same name + Default row addition in DataForm does not fires validateTypedRow + Code typo fix in change administration port model + Set only Remote as option to export/import configuration to a remote site + Return undef in HasMany type when a model is not longer available due to being uninstalled + Added onclick atribute to the link.mas template + Fix exception raising when no event component is found + table_ordered.js : more robust trClick event method + Changed dashboard JS which sometimes halted widget updates + Added popup dialogs for import/export configuration + Changes in styles and sizes of the save/revoke dialog + Removed redudant code in ConfigureWatchers::syncRows which made module to have an incorrect modified state + Dont show in bug report removed packages with configuration held as broken packages + DataTable::size() now calls to syncRows() + EBox::Module::Config::set_list quivalent now has the same behaviour than EBox::Module::Config::set 2.3.19 + Manually set up models for events to take into account the dynamic models from the log watcher filtering models + Fixed warnings when deleting a row which is referenced in other model + Disable HTML form autocompletion in admin password change model + Fixed incorrect non-editable warnings in change date and time model + Fixed parsing value bug in EBox::Types::Date and EBox::Types::Time + Reworked mdstat parsing, added failure_spare status + Configuration backup implicitly preserves ownership of files + Changes in styles and sizes of the save/revoke dialog + New data form row is copied from default row, avoiding letting hidden fields without its default value and causing missing fields errors + Always fill abstract type with its default value, this avoids errors with hidden fields with default value + Different page to show errors when there are broken software packages + InverseMatchSelect and InverseMatchUnion use 'not' instead of '!' to denote inverse match. This string is configurable with a type argument + Fixed types EBox::Type::InverseMatchSelect and InverseMatchUnion + Fixed bug in DataTable::setTypedRow() which produced an incorrect 'id' row element in DataTable::updateRowNotify() + In tableBody.mas template: decomposed table topToolbar section in methods + Fixed bug in discard changes dialog + Confirmation dialogs now use styled modalboxes + Do not reload page after save changes dialog if operation is successful + Maintenance menu is now kept open when visiting the logs index page 2.3.18 + Manual clone of row in DataTable::setTypedRow to avoid segfault + Avoid undef warnings in EBox::Model::DataTable::_find when the element value is undef + Fixed kill of ebox processes during postrm + Set MySQL root password in create-db script and added mysql script to /usr/share/zentyal for easy access to the zentyal database + Increased timeout redirecting to wizards on installation to 5 seconds to avoid problems on some slow or loaded machines + Save changes dialog do not appear if there are no changes + Delete no longer needed duplicated code + Do not go to save changes after a regular package installation they are saved only in the first install + Progress bar in installation refactored 2.3.17 + Do not use modal box for save changes during installation + Hidden fields in DataTables are no longer considered compulsory + Select type has now its own viewer that allows use of filter function + User is now enabled together with the rest of modules on first install 2.3.16 + Fix 'oldRow' parameter in UpdatedRowNotify + Use Clone::Fast instead of Clone + Modal dialog for the save and discard changes operations + Use a different lock file for the usercorner redis + Improved look of tables when checkAll controls are present + Better icons for clone action + Added confirmation dialog feature to models; added confirmation dialog to change hostname model + Dynamic default values are now properly updated when adding a row + Kill processes owned by the ebox user before trying to delete it + Do not use sudo to call status command at EBox::Service::running + Fixed regression setting default CSS class in notes 2.3.15 + Added missing call to updateRowNotify in DataForms + Fixed silent error in EBox::Types::File templates for non-readable by ebox files + Use pkill instead of killall in postinst + Use unset instead of delete_dir when removing rows + Do not set order list for DataForms + Only try to clean tmp dir on global system start 2.3.14 + Error message for failure in package cache creation + Fixed regression when showing a data table in a modal view + Do not do a redis transaction for network module init actions + Fixed EBox::Module::Config::st_unset() + Allowed error class in msg template 2.3.13 + Fixed problems in EventDaemon with JSON and blessed references + More crashes avoided when watchers or dispatchers doesn't exist + Proper RAID watcher reimplementation using the new state API + EBox::Config::Redis singleton has now a instance() method instead of new() + Deleted wrong use in ForcePurge model 2.3.12 + Fixed problem with watchers and dispatchers after a module deletion + Fixed EBox::Model::DataTable::_checkFieldIsUnique, it failed when the printableValue of the element was different to its value + Fixed separation between Add table link and table body + Adaptation of EventDaemon to model and field changes + Disabled logs consolidation on purge until it is reworked, fixed missing use in purge logs model + Fixed Componet::parentRow, it not longer tries to get a row with undefined id + Fix typo in ConfigureLogs model + Mark files for removing before deleting the row from backend in removeRow + The Includes directives are set just for the main virtual host + Fixed EventDaemon crash 2.3.11 + Mark files for removing before deleting the row from backend in removeRow + Dashboard widgets now always read the information from RO + Enable actions are now executed before enableService() + Fixed regression which prevented update of the administration service port when it was changed in the interface + New EBox::Model::Composite::componentNames() for dynamic composites + Remove _exposedMethods() feature to reduce use of AUTOLOAD + Removed any message set in the model in syncRows method + Added global() method to modules and components to get a coherent read-write or read-only instance depending on the context + Removed Model::Report and Composite::Report namespaces to simplify model management and specification + New redis key naming, with $mod/conf/*, $mod/state and $mod/ro/* replacing /ebox/modules/$mod/*, /ebox/state/$mod/* and /ebox-ro/modules/$mod/* + Removed unnecessary parentComposite methods in EBox::Model::Component + Only mark modules as changed when data has really changed + EBox::Global::modChange() throws exception if instance is readonly + New get_state() and set_state() methods, st_* methods are kept for backwards compatibility, but they are deprecated + Simplified events module internals with Watcher and Dispatcher providers + Model Manager is now able to properly manage read-only instances + Composites can now use parentModule() like Models + Renamed old EBox::GConfModule to EBox::Module::Config + Unified model and composite management in the new EBox::Model::Manager + Model and composites are loaded on demand to reduce memory consumption + Model and composite information is now stored in .yaml schemas + ModelProvider and CompositeProvider are no longer necessary + Simplified DataForm using more code from DataTable + Adapted RAID and restrictedResources() to the new JSON objects in redis + Remove unused override modifications code + Added /usr/share/zentyal/redis-cli wrapper for low-level debugging + Use simpler "key: value" format for dumps instead of YAML + Row id prefixes are now better chosen to avoid confusion + Use JSON instead of list and hash redis types (some operations, specially on lists, are up to 50% faster and caching is much simpler) + Store rows as hashes instead of separated keys + Remove deprecated all_dirs and all_entries methods + Remove obsolete EBox::Order package + Remove no longer needed redis directory tree sets + Fixed isEqualTo() method on EBox::Types::Time + EBox::Types::Abstract now provides default implementations of fields(), _storeInGConf() and _restoreFromHash() using the new _attrs() method + Remove indexes on DataTables to reduce complexity, no longer needed + Simplified ProgressIndicator implementation using shared memory + New EBox::Util::SHMLock package + Implemented transactions for redis operations + Replace old MVC cache system with a new low-level redis one + Delete no longer necessary regen-redis-db tool + Added new checkAll property to DataTable description to allow multiple check/uncheck of boolean columns 2.3.10 + Added Desktop::ServiceProvider to allow modules to implement requests from Zentyal desktop + Added VirtualHost to manage desktop requests to Zentyal server + Fix EventDaemon in the transition to MySQL + Send EventDaemon errors to new rotated log file /var/log/zentyal/events.err + Send an event to Zentyal Cloud when the updates are up-to-date + Send an info event when modules come back to running + Include additional info for current event watchers + Fixed RAID report for some cases of spare devices and bitmaps + Fixed log purge, SQL call must be a statement not a query + Fixed regex syntax in user log queries + Added missing "use Filesys::Df" to SysInfo + Disabled consolidation by default until is fixed or reimplemented + Fixed regresion in full log page for events + Added clone action to data tables + Fixed regression in modal popup when showing element table + Added new type EBox::Types::KrbRealm + Fix broken packages when dist-upgrading from old versions: stop ebox owned processes before changing home directory + Log the start and finish of start/stop modules actions + Added usesPort() method to apache module 2.3.9 + Enable SSLInsecureRenegotiation to avoid master -> slave SOAP handsake problems + Added validateRowRemoval method to EBox::Model::DataTable + Use rm -rf instead of remove_tree to avoid chdir permission problems + Avoid problems restarting apache when .pid file does not exist + Do not use graceful on apache to allow proper change of listen port + Simplified apache restart mechanism and avoid some problems 2.3.8 + Create tables using MyISAM engine by default + Delete obsolete 'admin' table 2.3.7 + Fixed printableName for apache module and remove entry in status widget + Merged tableBodyWithoutActions.mas into tableBody.mas + Removed tableBodyWithoutEdit.mas because it is no longer used + Better form validation message when there are no ids for foreign rows in select control with add new popup + Fixed branding of RSS channel items + Fixed destination path when copying zentyal.cnf to /etc/mysql/conf.d + Packaging fixes for precise 2.3.6 + Switch from CGIs to models in System -> General + New value() and setValue() methods in DataForm::setValue() for cleaner code avoiding use of AUTOLOAD + Added new EBox::Types::Time, EBox::Types::Date and EBox::Types::TimeZone + Added new attribute 'enabled' to the Action and MultiStateAction types to allow disabling an action. Accepts a scalar or a CODE ref + The 'defaultValue' parameter of the types now accept a CODE ref that returns the default value. 2.3.5 + Added force parameter in validateTypedRow + Fixed 'hidden' on types when using method references + Removed some console problematic characters from Util::Random::generate + Added methods to manage apache CA certificates + Use IO::Socket::SSL for SOAPClient connections + Removed apache rewrite from old slaves implementation + Do not show RSS image if custom_prefix defined 2.3.4 + Avoid 'negative radius' error in DiskUsage chart + Fixed call to partitionFileSystems in EBox::SysInfo::logReportInfo + Log audit does not ignore fields which their values could be interpreted as boolean false + Avoid ebox.cgi failure when showing certain strings in the error template + Do not calculate md5 digests if override_user_modification is enabled + Clean /var/lib/zentyal/tmp on boot + Stop apache gracefully and delete unused code in Apache.pm + Cache contents of module.yaml files in Global 2.3.3 + The editable attribute of the types now accept a reference to a function to dinamically enable or disable the field. + In progress bar CGIs AJAX call checks the availability of the next page before loading it + Replaced community logo + Adapted messages in the UI for new editions + Changed cookie name to remove forbidden characters to avoid incompatibilities with some applications + Added methods to enable/disable restart triggers 2.3.2 + Fixed redis unix socket permissions problem with usercorner + Get row ids without safe characters checking + Added EBox::Util::Random as random string generator + Set log level to debug when cannot compute md5 for a nonexistent file + Filtering in tables is now case insensitive + ProgressIndicator no longer leaves zombie processes in the system + Implemented mysqldump for logs database + Remove zentyal-events cron script which should not be longer necessary + Bugfix: set executable permissions to cron scripts and example hooks + Added a global method to retrieve installed server edition + Log also duration and compMessage to events.log 2.3.1 + Updated Standards-Version to 3.9.2 + Fixed JS client side table sorting issue due to Prototype library upgrade + Disable InnoDB by default to reduce memory consumption of MySQL + Now events are logged in a new file (events.log) in a more human-readable format + Added legend to DataTables with custom actions + Changed JS to allow the restore of the action cell when a delete action fails + Set milestone to 3.0 when creating bug reports in the trac + Avoid temporal modelInstance errors when adding or removing modules with LogWatchers or LogDispatcher + Unallow administration port change when the port is in use 2.3 + Do not launch a passwordless redis instance during first install + New 'types' field in LogObserver and storers/acquirers to store special types like IPs or MACs in an space-efficient way + Use MySQL for the logs database instead of PostgreSQL + Bugfix: logs database is now properly recreated after purge & install + Avoid use of AUTOLOAD to execute redis commands, improves performance + Use UNIX socket to connect to redis for better performance and update default redis 2.2 settings + Use "sudo" group instead of "admin" one for the UI access control + Added EBox::Module::Base::version() to get package version + Fixed problem in consalidation report when accumulating results from queries having a "group by table.field" + Added missing US and Etc zones in timezone selector + Replaced autotools with zbuildtools + Refuse to restore configuration backup from version lesser than 2.1 unless forced + Do not retrieve format.js in every graph to improve performance + The purge-module scripts are always managed as root user + New grep-redis tool to search for patterns in redis keys or values + Use partitionFileSystems method from EBox::FileSystem 2.2.4 + New internal 'call' command in Zentyal shell to 'auto-use' the module + Zentyal shell now can execute commandline arguments + Bugfix: EBox::Types::IPAddr::isEqualTo allows to change netmask now + Removed some undefined concatenation and compare warnings in error.log + Ignore check operation in RAID event watcher + Skip IP addresses ending in .0 in EBox::Types::IPRange::addresses() + Do not store in redis trailing dots in Host and DomainName types + Added internal command to instance models and other improvements in shell + Now the whole /etc/zentyal directory is backed up and a copy of the previous contents is stored at /var/backups before restoring + Removing a module with a LogWatcher no longer breaks the LogWatcher Configuration page anymore + Fixed error in change-hostname script it does not longer match substrings + Bugfix: Show breadcrumbs even from models which live in a composite + HTTPLink now returns empty string if no HTTPUrlView is defined in DataTable class + Added mising use sentence in EBox::Event::Watcher::Base 2.2.3 + Bugfix: Avoid url rewrite to ebox.cgi when requesting to /slave + Fixed logrotate configuration + More resilient way to handle with missing indexes in _find + Added more informative text when mispelling methods whose prefix is an AUTOLOAD action + A more resilient solution to load events components in EventDaemon + Added one and two years to the purge logs periods + Fixed downloads from EBox::Type::File 2.2.2 + Revert cookie name change to avoid session loss in upgrades + Do not try to change owner before user ebox is created 2.2.1 + Removed obsolete references to /zentyal URL + Create configuration backup directories on install to avoid warnings accessing the samba share when there are no backups + Log result of save changes, either successful or with warnings + Changed cookie name to remove forbidden characters to avoid incompatibilities with some applications + Removed duplicated and incorrect auding logging for password change + Fixed some non-translatable strings + Create automatic bug reports under 2.2.X milestone instead of 2.2 + Fixed bug changing background color on selected software packages 2.1.34 + Volatile types called password are now also masked in audit log + Adjust padding for module descriptions in basic software view + Removed beta icon 2.1.33 + Fixed modal add problems when using unique option on the type + Fixed error management in the first screen of modal add + Unify software selection and progress colors in CSS + Set proper message type in Configure Events model + Fixed error checking permanentMessage types in templates/msg.mas 2.1.32 + Added progress bar colors to theme definition + Remove no longer correct UTF8 decode in ProgressIndicator + Fixed UTF8 double-encoding on unexpected error CGI + Reviewed some subscription strings + Always fork before apache restart to avoid port change problems + Stop modules in the correct order (inverse dependencies order) + Better logging of failed modules on restore 2.1.31 + Do not start managed daemons on boot if the module is disabled + Better message on redis error + Watch for dependencies before automatic enable of modules on first install 2.1.30 + Removed obsolete /ebox URL from RSS link + Changed methods related with extra backup data in modules logs to play along with changes in ebackup module + Set a user for remote access for audit reasons + Detect session loss on AJAX requests 2.1.29 + Startup does not fail if SIGPIPE received 2.1.28 + Added code to mitigate false positives on module existence + Avoid error in logs full summary due to incorrect syntax in template + Allow unsafe chars in EBox::Types::File to avoid problems in some browsers + Reviewed some subscription strings + Warning about language-packs installed works again after Global changes + Show n components update when only zentyal packages are left to upgrade in the system widget + Do not show debconf warning when installing packages + EBox::Types::IPAddr (and IPNetwork) now works with defaultValue + Allow to hide menu items, separators and dashboard widgets via conf keys 2.1.27 + Do not create tables during Disaster Recovery installation + Added new EBox::Util::Debconf::value to get debconf values + DataTable controller does no longer try to get a deleted row for gather elements values for audit log + Check if Updates watcher can be enabled if the subscription level is yet unknown 2.1.26 + Detection of broken packages works again after proper deletion of dpkg_running file + Keep first install redis server running until trigger + Unified module restart for package trigger and init.d + Use restart-trigger script in postinst for faster daemons restarting + System -> Halt/Reboot works again after regression in 2.1.25 + Added framework to show warning messages after save changes + Change caption of remote services link to Zentyal Cloud + Do not show Cloud link if hide_cloud_link config key is defined + Added widget_ignore_updates key to hide updates in the dashboard + Differentiate ads from notes + Allow custom message type on permanentMessage + Only allow custom themes signed by Zentyal + Removed /zentyal prefix from URLs + Caps lock detection on login page now works again + Added HiddenIfNotAble property to event watchers to be hidden if it is unabled to monitor the event + Dashboard values can be now error and good as well + Include a new software updates widget + Include a new alert for basic subscriptions informing about software updates + Add update-notifier-common to dependencies + EBox::DataTable::enabledRows returns rows in proper order + Use custom ads when available + Disable bug report when hide_bug_report defined on theme 2.1.25 + Do not show disabled module warnings in usercorner + Mask passwords and unify boolean values in audit log + Do not override type attribute for EBox::Types::Text subtypes + Corrected installation finished message after first install + Added new disableAutocomplete attribute on DataTables + Optional values can be unset + Minor improvements on nmap scan 2.1.24 + Do not try to generate config for unconfigured services + Remove unnecessary redis call getting _serviceConfigured value + Safer sizes for audit log fields + Fix non-translatable "show help" string + Allow links to first install wizard showing a desired page + Fixed bug in disk usage when we have both values greater and lower than 1024 MB + Always return a number in EBox::AuditLogging::isEnabled to avoid issues when returning the module status + Added noDataMsg attribute on DataTable to show a message when there are no rows 2.1.23 + Removed some warnings during consolidation process + Depend on libterm-readline-gnu-perl for history support in shells + Fixed error trying to change the admin port with NTP enabled + Fixed breadcrumb destination for full log query page + Use printableActionName in DataTable setter 2.1.22 + Fixed parentRow method in EBox::Types::Row + Added new optionalLabel flag to EBox::Types::Abstract to avoid show the label on non-optional values that need to be set as optional when using show/hide viewCustomizers + Added initHTMLStateOrder to View::Customizer to avoid incorrect initial states + Improved exceptions info in CGIs to help bug reporting + Do not show customActions when editing row on DataTables 2.1.21 + Fixed bug printing traces at Global.pm + Check new dump_exceptions confkey instead of the debug one in CGIs + Explicit conversion to int those values stored in our database for correct dumping in reporting + Quote values in update overwrite while consolidating for reporting 2.1.20 + Fixed regression in edition in place of booleans + Better default balance of the dashboard based on the size of the widgets + Added defaultSelectedType argument to PortRange 2.1.19 + Disable KeepAlive as it seems to give performance problems with Firefox and set MaxClients value back to 1 in apache.conf + Throw exceptions when calling methods not aplicable to RO instances + Fixed problems when mixing read/write and read-only instances + Date/Time and Timezone moved from NTP to core under System -> General + Do not instance hidden widgets to improve dashboard performance + New command shell with Zentyal environment at /usr/share/zentyal/shell + Show warning when a language-pack is not installed + Removed unnecessary dump/load operations to .bak yaml files + AuditLogging and Logs constructor now receive the 'ro' parameter + Do not show Audit Logging in Module Status widget 2.1.18 + New unificated zentyal-core.logrotate for all the internal logs + Added forceEnabled option for logHelpers + Moved carousel.js to wizard template + Add ordering option to wizard pages + Fixed cmp and isEqualTo methods for EBox::Types::IPAddr + Fixed wrong Mb unit labels in Disk Usage and use GB when > 1024 MB + Now global-action script can be called without progress indicator + Fixed EBox::Types::File JavaScript setter code + Added support for "Add new..." modal boxes in foreign selectors + Each module can have now its customized purge-module script that will be executed after the package is removed + Added Administration Audit Logging to log sessions, configuration changes, and show pending actions in save changes confirmation + User name is stored in session + Remove deprecated extendedRestore from the old Full Backup 2.1.17 + Fixed RAID event crash + Added warning on models and composites when the module is disabled + Fixed login page style with some languages + Login page template can now be reused accepting title as parameter + EBox::Types::File does not write on redis when it fails to move the fail to its final destination + Added quote column option for periodic log consolidation and report consolidation + Added exclude module option to backup restore 2.1.16 + Do not show incompatible navigator warning on Google Chrome + Fixed syncRows override detection on DataTable find + clean-conf script now deletes also state data + Avoid 'undefined' message in selectors 2.1.15 + Move Disk Usage and RAID to the new Maintenance menu + Always call syncRows on find (avoid data inconsistencies) + Filename when downloading a conf backup now contains hostname + Fixed bug in RAID template + Set proper menu order in System menu (fixes NTP position) + Fixed regresion in page size selector on DataTables + Fixed legend style in Import/Export Configuration 2.1.14 + Fixed regresion with double quotes in HTML templates + Fixed problems with libredis-perl version dependency + Adding new apparmor profile management 2.1.13 + Better control of errors when saving changes + Elements of Union type can be hidden + Model elements can be hidden only in the viewer or the setter + HTML attributtes are double-quoted + Models can have sections of items + Password view modified to show the confirmation field + New multiselect type + Redis backend now throws different kind of exceptions 2.1.12 + Revert no longer necessary parents workaround + Hide action on viewCustomizer works now on DataTables 2.1.11 + Fixed bug which setted bad directory to models in tab view + Union type: Use selected subtype on trailingText property if the major type does not have the property + Raise MaxClients to 2 to prevent apache slowness 2.1.10 + Security [ZSN-2-1]: Avoid XSS in process list widget 2.1.9 + Do not try to initialize redis client before EBox::init() + Safer way to delete rows, deleting its id reference first + Delete no longer needed workaround for gconf with "removed" attribute + Fixed regression in port range setter 2.1.8 + Fixed regression in menu search + Fixed missing messages of multi state actions + Help toggler is shown if needed when dynamic content is received + Fixed issue when disabling several actions at once in a data table view + All the custom actions are disabled when one is clicked + Submit wizard pages asynchronously and show loading indicator + Added carousel.js for slide effects 2.1.7 + Fixed issues with wrong html attributes quotation + Bugfix: volatile types can now calculate their value using other the value from other elements in the row no matter their position 2.1.6 + Attach software.log to bug report if there are broken packages + Added keyGenerator option to report queries + Tuned apache conf to provide a better user experience + Actions click handlers can contain custom javascript + Restore configuration with force dependencies option continues when modules referenced in the backup are not present + Added new MultiStateAction type 2.1.5 + Avoid problems getting parent if the manager is uninitialized + Rename some icon files with wrong extension + Remove wrong optional attribute for read-only fields in Events + Renamed all /EBox/ CGI URLs to /SysInfo/ for menu folder coherency + Added support for custom actions in DataTables + Replaced Halt/Reboot CGI with a model + Message classes can be set from models + Fixed error in Jabber dispatcher + Show module name properly in log when restart from the dashboard fails + Avoid warning when looking for inexistent PID in pidFileRunning 2.1.4 + Changed Component's parent/child relationships implementation + Fixed WikiFormat on automatic bug report tickets + Do not show available community version in Dashboard with QA updates 2.1.3 + Fall back to readonly data in config backup if there are unsaved changes + Allow to automatically send a report in the unexpected error page + Logs and Events are now submenus of the new Maintenance menu + Configuration Report option is now present on the Import/Export section + Require save changes operation after changing the language + Added support for URL aliases via schemas/urls/*.urls files + Allow to sort submenu items via 'order' attribute + Automatically save changes after syncRows is called and mark the module mark the module as unchanged unless it was previously changed + Removed unnecessary ConfigureEvents composite + Removed unnecessary code from syncRows in logs and events + Restore configuration is safer when restoring /etc/zentyal files + Fixed unescaped characters when showing an exception + Fixed nested error page on AJAX requests + Adapted dumpBackupExtraData to new expected return value + Report remoteservices, when required, a change in administration port + Added continueOnModuleFail mode to configuration restore + Fixed Firefox 4 issue when downloading backups + Show scroll when needed in stacktraces (error page) + More informative error messages when trying to restart locked modules from the dashboard + Creation of plpgsql language moved from EBox::Logs::initialSetup to create-db script + Redis backend now throws different kind of exceptions + Avoid unnecesary warnings about PIDs + Update Jabber dispatcher to use Net::XMPP with some refactoring + Save changes messages are correctly shown with international charsets + Support for bitmap option in RAID report + Retry multiInsert line by line if there are encoding errors + Adapted to new location of partitionsFileSystems in EBox::FileSystem + Event messages are cleaned of null characters and truncated before inserting in the database when is necessary + Improve message for "Free storage space" event and send an info message when a given partition is not full anymore + Event messages now can contain newline characters + Objects of select type are compared also by context + Remove cache from optionsFromForeignModel since it produces problems and it is useless + Set title with server name if the server is subscribed + Fix title HTML tag in views for Models and Composites + Added lastEventsReport to be queried by remoteservices module + Added EBox::Types::HTML type + Added missing manage-logs script to the package + Fixed problems with show/hide help switch and dynamic content + Menus with subitems are now kept unfolded until a section on a different menu is accessed + Sliced restore mode fails correctly when schema file is missing, added option to force restore without schema file + Purge conf now purges the state keys as well + Added EBox::Types::IPRange 2.1.2 + Now a menu folder can be closed clicking on it while is open + Bugfix: cron scripts are renamed and no longer ignored by run-parts + Added new EBox::Util::Nmap class implementing a nmap wrapper 2.1.1 + Fixed incoherency problems with 'on' and '1' in boolean indexes + Move cron scripts from debian packaging to src/scripts/cron + Trigger restart of logs and events when upgrading zentyal-core without any other modules + Don't restart apache twice when upgrading together with more modules + Fixed params validation issues in addRow 2.1 + Replace YAML::Tiny with libyaml written in C through YAML::XS wrapper + Minor bugfix: filter invalid '_' param added by Webkit-based browser on EBox::CGI::Base::params() instead of _validateParams(), avoids warning in zentyal.log when enabling modules + All CGI urls renamed from /ebox to /zentyal + New first() and deleteFirst() methods in EBox::Global to check existence and delete the /var/lib/zentyal/.first file + PO files are now included in the language-pack-zentyal-* packages + Migrations are now always located under /usr/share/$package/migration this change only affects to the events and logs migrations + Delete no longer used domain and translationDomain methods/attributes + Unified src/libexec and tools in the new src/scripts directory + Remove the ebox- prefix on all the names of the /usr/share scripts + New EBox::Util::SQL package with helpers to create and drop tables from initial-setup and purge-module for each module + Always drop tables when purging a package + Delete 'ebox' user when purging zentyal-core + Moved all SQL schemas from tools/sqllogs to schemas/sql + SQL time-period tables are now located under schemas/sql/period + Old ebox-clean-gconf renamed to /usr/share/zentyal/clean-conf and ebox-unconfigure-module is now /usr/share/zentyal/unconfigure-module + Added default implementation for enableActions, executing /usr/share/zentyal-$modulename/enable-module if exists + Optimization: Do not check if a row is unique if any field is unique + Never call syncRows on read-only instances + Big performance improvements using hashes and sets in redis database to avoid calls to the keys command + Delete useless calls to exists in EBox::Config::Redis + New regen-redis-db tool to recreate the directory structure + Renamed /etc/cron.hourly/90manageEBoxLogs to 90zentyal-manage-logs and moved the actual code to /usr/share/zentyal/manage-logs + Move /usr/share/ebox/zentyal-redisvi to /usr/share/zentyal/redisvi + New /usr/share/zentyal/initial-setup script for modules postinst + New /usr/share/zentyal/purge-module script for modules postrm + Removed obsolete logs and events migrations + Create plpgsql is now done on EBox::Logs::initialSetup + Replace old ebox-migrate script with EBox::Module::Base::migrate + Rotate duplicity-debug.log log if exists + Bug fix: Port selected during installation is correctly saved + Zentyal web UI is restarted if their dependencies are upgraded + Bug fix: Logs don't include unrelated information now + Add total in disk_usage report + Bugfix: Events report by source now works again + Do not include info messages in the events report + Services event is triggered only after five failed checkings + Do not add redundant includedir lines to /etc/sudoers + Fixed encoding for strings read from redis server + Support for redis-server 2.0 configuration + Move core templates to /usr/share/zentyal/stubs/core + Old /etc/ebox directory replaced with the new /etc/zentyal with renamed core.conf, logs.conf and events.conf files + Fixed broken link to alerts list 2.0.15 + Do not check the existence of cloud-prof package during the restore since it is possible not to be installed while disaster recovery process is done + Renamed /etc/init.d/ebox to /etc/init.d/zentyal + Use new zentyal-* package names + Don't check .yaml existence for core modules 2.0.14 + Added compMessage in some events to distinguish among events if required + Make source in events non i18n + After restore, set all the restored modules as changed + Added module pre-checks for configuration backup 2.0.13 + Fixed dashboard graphs refresh + Fixed module existence check when dpkg is running + Fix typo in sudoers creation to make remote support work again 2.0.12 + Include status of packages in the downloadable bug report + Bugfix: Avoid possible problems deleting redis.first file if not exist 2.0.11 + New methods entry_exists and st_entry_exists in config backend 2.0.10 + Now redis backend returns undef on get for undefined values + Allow custom mason templates under /etc/ebox/stubs + Better checks before restoring a configuration backup with a set of modules different than the installed one + Wait for 10 seconds to the child process when destroying the progress indicator to avoid zombie processes + Caught SIGPIPE when trying to contact Redis server and the socket was already closed + Do not stop redis server when restarting apache but only when the service is asked to stop + Improvements in import/export configuration (know before as configuration backup) + Improvements in ProgressIndicator + Better behaviour of read-only rows with up/down arrows + Added support for printableActionName in DataTable's + Added information about automatic configuration backup + Removed warning on non existent file digest + Safer way to check if core modules exist during installation 2.0.9 + Treat wrong installed packages as not-existent modules + Added a warning in dashboard informing about broken packages + File sharing and mailfilter log event watchers works again since it is managed several log tables per module 2.0.8 + Replaced zentyal-conf script with the more powerful zentyal-redisvi + Set always the same default order for dashboard widgets + Added help message to the configure widgets dialog + Check for undefined values in logs consolidation + Now dashboard notifies fails when restarting a service + Fixed bug with some special characters in dashboard + Fixed bug with some special characters in disk usage graph 2.0.7 + Pre-installation includes sudoers.d into sudoers file if it's not yet installed + Install apache-prefork instead of worker by default + Rename service certificate to Zentyal Administration Web Server 2.0.6 + Use mod dependencies as default restore dependencies + Fixed dependencies in events module + Increased recursive dependency threshold to avoid backup restoration problems 2.0.5 + Removed deprecated "Full backup" option from configuration backup + Bugfix: SCP method works again after addition of SlicedBackup + Added option in 90eboxpglogger.conf to disable logs consolidation 2.0.4 + Removed useless gconf backup during upgrade + Fixed postinstall script problems during upgrade 2.0.3 + Added support for the sliced backup of the DB + Hostname change is now visible in the form before saving changes + Fixed config backend problems with _fileList call + Added new bootDepends method to customize daemons boot order + Added permanent message property to Composite + Bugfix: Minor aesthetic fix in horizontal menu + Bugfix: Disk usage is now reported in expected bytes + Bugfix: Event dispatcher is not disabled when it is impossible for it to dispatch the message 2.0.2 + Better message for the service status event + Fixed modules configuration purge script + Block enable module button after first click + Avoid division by zero in progress indicator when total ticks is zero + Removed warning during postinst + Added new subscription messages in logs, events and backup 2.0.1 + Bugfix: Login from Zentyal Cloud is passwordless again + Some defensive code for the synchronization in Events models + Bugfix: add EBox::Config::Redis::get to fetch scalar or list values. Make GConfModule use it to avoid issues with directories that have both sort of values. 1.5.14 + Fixed redis bug with dir keys prefix + Improved login page style + New login method using PAM instead of password file + Allow to change admin passwords under System->General + Avoid auto submit wizard forms + Wizard skip buttons always available + Rebranded post-installation questions + Added zentyal-conf script to get/set redis config keys 1.5.13 + Added transition effect on first install slides + Zentyal rebrand + Added web page favicon + Fixed already seen wizards apparition + Fixed ro module creation with redis backend + Use mason for links widgets + Use new domain to official strings for subscriptions 1.5.12 + Added option to change hostname under System->General + Show option "return to dashboard" when save changes fails. 1.5.11 + Added more tries on redis reconnection + Fixed user corner access problems with redis server + writeFile* methods reorganized + Added cron as dependency as cron.hourly was never executed with anacron + Improvements in consolidation of data for reports 1.5.10 + Fixed gconf to redis conversion for boolean values 1.5.9 + Improved migrations speed using the same perl interpreter + Redis as configuration backend (instead of gconf) + Improved error messages in ebox-software + Set event source to 256 chars in database to adjust longer event sources + Progress bar AJAX updates are sent using JSON + Fixed progress bar width problems + Fixed top menu on wizards + Improved error message when disconnecting a not connected database + Abort installation if 'ebox' user already exists + Bugfix: IP address is now properly registered if login fails 1.5.8 + Added template tableorderer.css.mas + Added buttonless top menu option + Bugfix: Save all modules on first installation + Bugfix: General ebox database is now created if needed when re/starting services + Bugfix: Data to report are now uniform in number of elements per value. This prevents errors when a value is present in a month and not in another + Bugfix: Don't show already visited wizard pages again 1.5.7 + Bugfix: Avoid error when RAID is not present + Bugfix: Add ebox-consolidate-reportinfo call in daily cron script + Bugfix: Called multiInsert and unbufferedInsert when necessary after the loggerd reimplementation + Bugfix: EBox::ThirdParty::Apache2::AuthCookie and EBox::ThirdParty::Apache2::AuthCookie::Util package defined just once + Added util SystemKernel + Improved progress indicator + Changes in sudo generation to allow sudo for remote support user + Initial setup wizards support 1.5.6 + Reimplementation of loggerd using inotify instead of File::Tail 1.5.5 + Asynchronous load of dashboard widgets for a smoother interface 1.5.4 + Changed dbus-check script to accept config file as a parameter 1.5.3 + Function _isDaemonRunning works now with snort in lucid + Javascript refreshing instead of meta tag in log pages + Updated links in dashboard widget + Add package versions to downloadable ebox.log + Fixed postgresql data dir path for disk usage with pg 8.4 + GUI improvements in search box 1.5.2 + Security [ESN-1-1]: Validate referer to avoid CSRF attacks + Added reporting structure to events module + Added new CGI to download the last lines of ebox.log 1.5.1 + Bugfix: Catch exception when upstart daemon does not exist and return a stopped status + Added method in logs module to dump database in behalf of ebackup module + Bugfix: Do not check in row uniqueness for optional fields that are not passed as parameters + Improve the output of ebox module status, to be consistent with the one shown in the interface + Add options to the report generation to allow queries to be more flexible + Events: Add possibility to enable watchers by default + Bugfix: Adding a new field to a model now uses default value instead of an empty value + Added script and web interface for configuration report, added more log files to the configuration report 1.5 + Use built-in authentication + Use new upstart directory "init" instead of "event.d" + Use new libjson-perl API + Increase PerlInterpMaxRequests to 200 + Increase MaxRequestsPerChild (mpm-worker) to 200 + Fix issue with enconding in Ajax error responses + Loggerd: if we don't have any file to watch we just sleep otherwise the process will finish and upstart will try to start it over again and again. + Make /etc/init.d/ebox depend on $network virtual facility + Show uptime and users on General Information widget. 1.4.2 + Start services in the appropriate order (by dependencies) to fix a problem when running /etc/init.d/ebox start in slaves (mail and other modules were started before usersandgroups and thus failed) 1.4.1 + Remove network workarounds from /etc/init.d/ebox as we don't bring interfaces down anymore 1.4 + Bug fix: i18n. setDomain in composites and models. 1.3.19 + Make the module dashboard widget update as the rest of the widgets + Fix problem regarding translation of module names: fixes untranslated module names in the dashboard, module status and everywhere else where a module name is written 1.3.18 + Add version comparing function and use it instead of 'gt' in the general widget 1.3.17 + Minor bug fix: check if value is defined in EBox::Type::Union 1.3.16 + Move enable field to first row in ConfigureDispatcherDataTable + Add a warning to let users know that a module with unsaved changes is disabled + Remove events migration directory: - 0001_add_conf_configureeventtable.pl - 0002_add_conf_diskfree_watcher.pl + Bug fix: We don't use names to stringify date to avoid issues with DB insertions and localisation in event logging + Bug fix: do not warn about disabled services which return false from showModuleStatus() + Add blank line under "Module Status" + Installed and latest available versions of the core are now displayed in the General Information widget 1.3.15 + Bug fix: Call EBox::Global::sortModulesByDependencies when saving all modules and remove infinite loop in that method. EBox::Global::modifiedModules now requires an argument to sort its result dependending on enableDepends or depends attribute. + Bug fix: keep menu folders open during page reloads + Bug fix: enable the log events dispatcher by default now works + Bug fix: fixed _lock function in EBox::Module::Base + Bug fix: composites honor menuFolder() + Add support for in-place edition for boolean types. (Closes #1664) + Add method to add new database table columnts to EBox::Migration::Helpers + Bug fix: enable "Save Changes" button after an in-place edition 1.3.14 + Bug fix: fix critical bug in migration helper that caused some log log tables to disappear + Create events table + Bug fix: log watcher works again + Bug fix: delete cache if log index is not found as it could be disabled 1.3.13 + Bug fix: critical error in EventDaemon that prevented properly start + Cron script for manage logs does not run if another is already running, hope that this will avoid problems with large logs + Increased maximum size of message field in events + Added script to purge logs + Bug fix: multi-domain logs can be enabled again 1.3.12 + Added type for EBox::Dashboard::Value to stand out warning messages in dashboard + Added EBox::MigrationHelpers to include migration helpers, for now, include a db table renaming one + Bug fix: Fix mismatch in event table field names + Bug fix: Add migration to create language plpgsql in database + Bug fix: Add missing script for report log consolidation + Bug fix: Don't show modules in logs if they are not configured. This prevents some crashes when modules need information only available when configured, such as mail which holds the vdomains in LDAP + Added method EBox::Global::lastModificationTime to know when eBox configuration was modified for last time + Add support for breadcrumbs on the UI + Bug fix: in Loggerd files are only parsed one time regardless of how many LogHelper reference them + Added precondition for Loggerd: it does not run if there isnt anything to watch 1.3.11 + Support customFilter in models for big tables + Added EBox::Events::sendEvent method to send events using Perl code (used by ebackup module) + Bug fix: EBox::Type::Service::cmp now works when only the protocols are different + Check $self is defined in PgDBEngine::DESTROY + Do not watch files in ebox-loggerd related to disabled modules and other improvements in the daemon + Silent some exceptions that are used for flow control + Improve the message from Service Event Watcher 1.3.10 + Show warning when accesing the UI with unsupported browsers + Add disableApparmorProfile to EBox::Module::Service + Bug fix: add missing use + Bug fix: Make EventDaemon more robust against malformed sent events by only accepting EBox::Event objects 1.3.8 + Bug fix: fixed order in EBox::Global::modified modules. Now Global and Backup use the same method to order the module list by dependencies 1.3.7 + Bug fix: generate public.css and login.css in dynamic-www directory which is /var/lib/zentyal/dynamicwww/css/ and not in /usr/share/ebox/www/css as these files are generate every time eBox's apache is restarted + Bug fix: modules are restored now in the correct dependency order + ebox-make-backup accepts --destinaton flag to set backup's file name + Add support for permanent messages to EBox::View::Customizer 1.3.6 + Bug fix: override _ids in EBox::Events::Watcher::Log to not return ids which do not exist + Bug fix: fixed InverseMatchSelect type which is used by Firewall module + New widget for the dashboard showing useful support information + Bugfix: wrong permissions on CSS files caused problem with usercorner + CSS are now templates for easier rebranding + Added default.theme with eBox colors 1.3.5 + Bugfix: Allow unsafe characters in password type + Add FollowSymLinks in eBox apache configuration. This is useful if we use js libraries provided by packages 1.3.4 + Updated company name in the footer + Bugfix: humanEventMessage works with multiple tableInfos now + Add ebox-dbus-check to test if we can actually connect to dbus 1.3.4 + bugfix: empty cache before calling updatedRowNotify + enable Log dispatcher by default and not allow users to disable it + consolidation process continues in disabled but configured modules + bugfix: Save Changes button doesn't turn red when accessing events for first time 1.3.2 + bugfix: workaround issue with dhcp configured interfaces at boot time 1.3.1 + bugfix: wrong regex in service status check 1.3.0 + bugfix: make full backup work again 1.1.30 + Change footer to new company holder + RAID does not generate 'change in completion events, some text problems fixed with RAID events + Report graphics had a datapoints limit dependent on the active time unit + Apache certificate can be replaced by CA module + Fixed regression in detailed report: total row now aggregates properly + More characters allowed when changing password from web GUI + Fixed regression with already used values in select types + Do not a button to restart eBox's apache + Fixed auth problem when dumping and restoring postgre database 1.1.20 + Added custom view support + Bugfix: report models now can use the limit parameter in reportRows() method + use a regexp to fetch the PID in a pidfile, some files such as postfix's add tabs and spaces before the actual number + Changed "pidfile" to "pidfiles" in _daemons() to allow checking more than one (now it is a array ref instead of scalar) + Modified Service.pm to support another output format for /etc/init.d daemon status that returns [OK] instead of "running". + unuformized case in menu entries and some more visual fixes 1.1.10 + Fix issue when there's a file managed by one module that has been modified when saving changes + Bugfix: events models are working again even if an event aware module is uninstalled and it is in a backup to restore + Select.pm returns first value in options as default + Added 'parentModule' to model class to avoid recursive problems + Added Float type + Apache module allows to add configuration includes from other modules + Display remote services button if subscribed + Event daemon may received events through a named pipe + Bugfix. SysInfo revokes its config correctly + Added storer property to types in order to store the data in somewhere different from GConf + Added protected property 'volatile' to the models to indicate that they store nothing in GConf but in somewhere different + System Menu item element 'RAID' is always visible even when RAID is not installed + Files in deleted rows are deleted when the changes are saved + Fixed some bug whens backing and restore files + Components can be subModels of the HasMany type + Added EBox::Types::Text::WriteOnce type + Do not use rows(), use row to force iteration over the rows and increase performance and reduce memory use. + Do not suggest_sync after read operations in gconf + Increase MaxRequestsPerChild to 200 in eBox's apache + Make apache spawn only one child process + Log module is backed up and restored normally because the old problem is not longer here + Backup is more gentle with no backup files in backup directory, now it does not delete them + HasMany can retrieve again the model and row after the weak refence is garbage-collected. (Added to solve a bug in the doenload bundle dialog) + EBox::Types::DomainName no longer accepts IP addresses as domain names + Bugfix: modules that fail at configuration stage no longer appear as enabled + Add parameter to EBox::Types::Select to disable options cache 0.12.103 + Bugfix: fix SQL statement to fetch last rows to consolidate 0.12.102 + Bugfix: consolidate logs using the last date and not starting from scratch 0.12.101 + Bugfix: DomainName type make comparisons case insensitive according to RFC 1035 0.12.100 + Bugfix: Never skip user's modifications if it set to true override user's changes + EBox::Module::writeConfFile and EBox::Service scape file's path + Bugfix. Configure logrotate to actually rotate ebox logs + Fixed bug in ForcePurge logs model + Fixed bug in DataTable: ModelManaged was called with tableName instead of context Name + Fixing an `img` tag closed now properly and adding alternative text to match W3C validation in head title + Backup pages now includes the size of the archive + Fixed bug in ForcePurge logs model + Now the modules can have more than one tableInfo for logging information + Improve model debugging + Improve restart debugging + Backups and bug reports can be made from the command line + Bugfix: `isEqualTo` is working now for `Boolean` types + Bugfix: check if we must disable file modification checks in Manager::skipModification 0.12.99 + Add support for reporting + Refresh logs automatically + Reverse log order + Remove temp file after it is downloaded with FromTempDir controller 0.12.3 + Bug fix: use the new API in purge method. Now purging logs is working again. 0.12.2 + Increase random string length used to generate the cookie to 2048 bits + Logs are show in inverse chronological order 0.12.1 + Bug fix: use unsafeParam for progress indicator or some i18 strings will fail when saving changes 0.12 + Bugfix: Don't assume timecol is 'timestamp' but defined by module developer. This allows to purge some logs tables again + Add page titles to models + Set default values when not given in `add` method in models + Add method to manage page size in model + Add hidden field to help with Ajax request and automated testing with ANSTE + Bugfix: cast sql types to filter fields in logs + Bugfix: Restricted resources are back again to make RSS access policy work again + Workaround bogus mason warnings + Make postinst script less verbose + Disable keepalive in eBox apache + Do not run a startup script in eBox apache + Set default purge time for logs stored in eBox db to 1 week + Disable LogAdmin actions in `ebox-global-action` until LogAdmin feature is completely done 0.11.103 + Modify EBox::Types::HasMany to create directory based on its row + Add _setRelationship method to set up relationships between models and submodels + Use the new EBox::Model::Row api + Add help method to EBox::Types::Abstract + Decrease size for percentage value in disk free watcher + Increase channel link field size in RSS dispatcher 0.11.102 + Bugfix: cmp in EBox::Types::HostIP now sorts correctly + updatedRowNotify in EBox::Model::DataTable receives old row as well as the recently updated row + Added `override_user_modification` configuration parameter to avoid user modification checkings and override them without asking + Added EBox::Model::Row to ease the management of data returned by models + Added support to pre-save and post-save executable files. They must be placed at /etc/ebox/pre-save or /etc/ebox/post-save + Added `findRow` method to ease find and set 0.11.101 + Bugfix: Fix memory leak in models while cloning types. Now cloning is controlled by clone method in types + Bugfix: Union type now checks for its uniqueness + DESTROY is not an autoloaded method anymore + HasOne fields now may set printable value from the foreign field to set its value + findId now searches as well using printableValue + Bugfix. Minor bug found when key is an IP address in autoloaded methods + Ordered tables may insert values at the beginning or the end of the table by "insertPosition" attribute + Change notConfigured template to fix English and add link to the module status section + Add loading gif to module status actions + Remove debug from ServiceInterface.pm + Add support for custom separators to be used as index separators on exposedMethods + Bugfix. Stop eBox correctly when it's removed + Improve apache-restart to make it more reliable. 0.11.100 + Bugfix. Fix issue with event filters and empty hashes + Bugfix. Cache stuff in log and soap watcher to avoid memory leaks + Bugfix. Fix bug that prevented the user from being warned when a row to be deleted is being used by other model + Bugfix. Add missing use of EBox::Global in State event watcher + Added progress screen, now pogress screen keeps track of the changed state of the modules and change the top page element properly + Do not exec() to restart apache outside mod_perl + Improve apache restart script + Improve progress screen 0.11.99 + DataTable contains the property 'enableProperty' to set a column called 'enabled' to enable/disable rows from the user point of view. The 'enabled' column is put the first + Added state to the RAID report instead of simpler active boolean + Fix bug when installing new event components and event GConf subtree has not changed + Add RSS dispatcher to show eBox events under a RSS feed + Rotate log files when they reach 10MB for 7 rotations + Configurable minimum free space left for being notified by means of percentage + Add File type including uploading and downloading + Event daemon now checks if it is possible to send an event before actually sending it + Added Action forms to perform an action without modifying persistent data + Log queries are faster if there is no results + Show no data stored when there are no logs for a domain + Log watcher is added in order to notify when an event has happened. You can configure which log watcher you may enable and what you want to be notify by a determined filter and/or event. + RAID watcher is added to check the RAID events that may happen when the RAID subsystem is configured in the eBox machine + Change colour dataset in pie chart used for disk usage reporting + Progress indicator now contains a returned value and error message as well + Lock session file for HTTP session to avoid bugs related to multiple requests (AJAX) in a short time + Upgrade runit dependency until 1.8.0 to avoid runit related issues 0.11 + Use apache2 + Add ebox-unblock-exec to unset signal mask before running a executable + Fix issue with multiple models and models with params. This triggered a bug in DHCP when there was just one static interface + Fix _checkRowIsUnique and _checkFieldIsUnique + Fix paging + Trim long strings in log table, show tooltip with the whole string and show links for URLs starting with "http://" 0.10.99 + Add disk usage information + Show progress in backup process + Add option to purge logs + Create a link from /var/lib/zentyal/log to /var/log/ebox + Fix bug with backup descriptions containing spaces + Add removeAll method on data models + Add HostIP, DomainName and Port types + Add readonly forms to display static information + Add Danish translation thanks to Allan Jacobsen 0.10 + New release 0.9.100 + Add checking for SOAP session opened + Add EventDaemon + Add Watcher and Dispatch framework to support an event architecture on eBox + Add volatile EBox::Types in order not to store their values on GConf + Add generic form + Improvements on generic table + Added Swedish translation 0.9.99 + Added Portuguese from Portugal translation + Added Russian translation + Bugfix: bad changed state in modules after restore 0.9.3 + New release 0.9.2 + Add browser warning when uploading files + Enable/disable logging modules 0.9.1 + Fix backup issue with changed state + Generic table supports custom ordering 0.9 + Added Polish translation + Bug in recognition of old CD-R writting devices fixed + Added Aragonese translation + Added Dutch translation + Added German translation + Added Portuguese translation 0.8.99 + Add data table model for generic Ajax tables + Add types to be used by models + Add MigrationBase and ebox-migrate to upgrade data models + Some English fixes 0.8.1 + New release 0.8 + Fix backup issue related to bug reports + Improved backup GUI 0.7.99 + changed sudo stub to be more permissive + added startup file to apache web server + enhanced backup module + added basic CD/DVD support to backup module + added test stubs to simplify testing + added test class in the spirit of Test::Class + Html.pm now uses mason templates 0.7.1 + use Apache::Reload to reload modules when changed + GUI consistency (#12) + Fixed a bug for passwords longer than 16 chars + ebox-sudoers-friendly added to not overwrite /etc/sudoers each time 0.7 + First public release 0.6 + Move to client + Remove obsolete TODO list + Remove firewall module from base system + Remove objects module from base system + Remove network module from base system + Add modInstances and modInstancesOfType + Raname Base to ClientBase + Remove calls to deprecated methods + API documented using naturaldocs + Update INSTALL + Use a new method to get configkeys, now configkey reads every [0.9 + Added Polish translation][0-9]+.conf file from the EBox::Config::etc() dir and tries to get the value from the files in order. + Display date in the correct languae in Summary + Update debian scripts + Several bugfixes 0.5.2 + Fix some packaging issues 0.5.1 + New menu system + New firewall filtering rules + 802.1q support 0.5 + New bug-free menus (actually Internet Explorer is the buggy piece of... software that caused the reimplementation) + Lots of small bugfixes + Firewall: apply rules with no destination address to packets routed through external interfaces only + New debianize script + Firewall: do not require port and protocol parameters as they are now optional. + Include SSL stuff in the dist tarball + Let modules block changes in the network interfaces configuration if they have references to the network config in their config. + Debian network configuration import script + Fix the init.d script: it catches exceptions thrown by modules so that it can try to start/stop all of them if an exception is thrown. + Firewall: fix default policy bug in INPUT chains. + Restore textdomain in exceptions + New services section in the summary + Added Error item to Summary. Catch exceptions from modules in summary and generate error item + Fix several errors with redirections and error handling in CGIs + Several data validation functions were fixed, and a few others added + Prevent the global module from keeping a reference to itself. And make the read-only/read-write behavior of the factory consistent. + Stop using ifconfig-wrapper and implement our own NetWrapper module with wrappers for ifconfig and ip. + Start/stop apache, network and firewall modules in first place. + Ignore some network interface names such as irda, sit0, etc. + The summary page uses read-only module instances. + New DataInUse exception, old one renamed to DataExists. + Network: do not overwrite resolv.conf if there are nameservers given via dhcp. + Do not set a default global policy for the ssh service. + Check for forbiden characters when the parameter value is requested by the CGI, this allows CGI's to handle the error, and make some decissions before it happens. + Create an "edit object" template and remove the object edition stuff from the main objects page. + Fix the apache restarting code. + Network: Remove the route reordering feature, the kernel handles that automatically. + Fix tons of bugs in the network restarting code. + Network: removed the 3rd nameserver configuration. + Network: Get gateway info in the dhcp hook. + Network: Removed default configuration from the gconf schema. + New function for config-file generation + New functions for pid file handling 0.4 + debian package + added module to export/import configuration + changes in firewall's API + Added content filter based on dansguardian + Added French translation + Added Catalan translation + Sudoers file is generated automatically based on module's needs + Apache config file is generated by ebox now + Use SSL + Added ebox.conf file + Added module template generator 0.3 + Supports i18n + API name consistency + Use Mason for templates + added tips to GUI + added dhcp hooks + administration port configuration + Fixed bugs to IE compliant + Revoke changes after logout + Several bugfixes 0.2 + All modules are now based on gconf. + Removed dependencies on xml-simple, xerces and xpath + New MAC address field in Object members. + Several bugfixes. 0.1 + Initial release zentyal-core-2.3.21+quantal1/AUTHORS0000664000000000000000000000024012017102272013663 0ustar Copyright (C) 2004-2012 eBox Technologies S.L. For an updated list of the current and past developers please visit: http://trac.zentyal.org/wiki/Contributors zentyal-core-2.3.21+quantal1/schemas/0000775000000000000000000000000012017102272014242 5ustar zentyal-core-2.3.21+quantal1/schemas/logs.yaml0000664000000000000000000000026412017102272016074 0ustar class: 'EBox::Logs' models: - ConfigureLogs - ForcePurge - SelectLog composites: General: [SelectLog, ConfigureLog] ConfigureLog: [ConfigureLogs, ForcePurge] zentyal-core-2.3.21+quantal1/schemas/apache.yaml0000664000000000000000000000007512017102272016351 0ustar class: 'EBox::Apache' models: - Language - AdminPort zentyal-core-2.3.21+quantal1/schemas/urls/0000775000000000000000000000000012017102272015227 5ustar zentyal-core-2.3.21+quantal1/schemas/urls/core.urls0000664000000000000000000000012412017102272017063 0ustar Maintenance/Logs Logs/Composite/General Maintenance/Events Events/Composite/General zentyal-core-2.3.21+quantal1/schemas/sysinfo.yaml0000664000000000000000000000036012017102272016617 0ustar class: 'EBox::SysInfo' models: - Halt - AdminUser - TimeZone - DateTime - HostName composites: General: [ sysinfo/AdminUser, apache/Language, sysinfo/TimeZone, sysinfo/DateTime, apache/AdminPort, sysinfo/HostName ] zentyal-core-2.3.21+quantal1/schemas/events.yaml0000664000000000000000000000057012017102272016434 0ustar class: 'EBox::Events' models: - ConfigureWatchers - ConfigureDispatchers - LogWatcherConfiguration - DiskFreeWatcherConfiguration - JabberDispatcherConfiguration - RSSDispatcherConfiguration composites: General: [ConfigureWatchers, ConfigureDispatchers] modeldepends: RSSDispatcherConfiguration: objects/ObjectTable: [allowedObject] zentyal-core-2.3.21+quantal1/schemas/audit.yaml0000664000000000000000000000003412017102272016231 0ustar class: 'EBox::AuditLogging' zentyal-core-2.3.21+quantal1/schemas/sql/0000775000000000000000000000000012017102272015041 5ustar zentyal-core-2.3.21+quantal1/schemas/sql/audit_sessions.sql0000664000000000000000000000031712017102272020617 0ustar CREATE TABLE IF NOT EXISTS audit_sessions( timestamp TIMESTAMP, username VARCHAR(40), ip INT UNSIGNED, event ENUM('login', 'logout', 'fail', 'expired') ) ENGINE = MyISAM; zentyal-core-2.3.21+quantal1/schemas/sql/events.sql0000664000000000000000000000055312017102272017071 0ustar CREATE TABLE IF NOT EXISTS events( id SERIAL, timestamp TIMESTAMP, lastTimestamp TIMESTAMP, nRepeated INTEGER DEFAULT 1, level VARCHAR(6), source VARCHAR(256), message VARCHAR(256) ) ENGINE = MyISAM; zentyal-core-2.3.21+quantal1/schemas/sql/report_consolidation.sql0000664000000000000000000000022512017102272022021 0ustar CREATE TABLE IF NOT EXISTS report_consolidation( report_table VARCHAR(40) NOT NULL, last_date TIMESTAMP NOT NULL ) ENGINE = MyISAM; zentyal-core-2.3.21+quantal1/schemas/sql/period/0000775000000000000000000000000012017102272016323 5ustar zentyal-core-2.3.21+quantal1/schemas/sql/period/events_accummulated.sql0000664000000000000000000000053012017102272023072 0ustar CREATE TABLE IF NOT EXISTS events_accummulated( date TIMESTAMP NOT NULL, source VARCHAR(256), info INTEGER DEFAULT 0, warn INTEGER DEFAULT 0, error INTEGER DEFAULT 0, fatal INTEGER DEFAULT 0 ) ENGINE = MyISAM; zentyal-core-2.3.21+quantal1/schemas/sql/sysinfo_disk_usage_report.sql0000664000000000000000000000022612017102272023045 0ustar CREATE TABLE IF NOT EXISTS sysinfo_disk_usage_report ( DATE date, mountpoint VARCHAR(80), used BIGINT, free BIGINT ) ENGINE = MyISAM; zentyal-core-2.3.21+quantal1/schemas/sql/events_report.sql0000664000000000000000000000024712017102272020464 0ustar CREATE TABLE IF NOT EXISTS events_report( date DATE, level VARCHAR(6), source VARCHAR(256), nEvents INT DEFAULT 0 ) ENGINE = MyISAM; zentyal-core-2.3.21+quantal1/schemas/sql/sysinfo_disk_usage.sql0000664000000000000000000000026112017102272021451 0ustar CREATE TABLE IF NOT EXISTS sysinfo_disk_usage ( `timestamp` TIMESTAMP, mountpoint VARCHAR(80), used BIGINT, free BIGINT, INDEX(timestamp) ) ENGINE = MyISAM; zentyal-core-2.3.21+quantal1/schemas/sql/consolidation.sql0000664000000000000000000000022112017102272020422 0ustar CREATE TABLE IF NOT EXISTS consolidation( consolidatedTable VARCHAR(40) NOT NULL, lastDate TIMESTAMP NOT NULL ) ENGINE = MyISAM; zentyal-core-2.3.21+quantal1/schemas/sql/backup_slices.sql0000664000000000000000000000053612017102272020375 0ustar CREATE TABLE IF NOT EXISTS backup_slices( tablename VARCHAR(40) NOT NULL, id BIGINT NOT NULL, beginTs TIMESTAMP NOT NULL, endTs TIMESTAMP NOT NULL, archived BOOLEAN DEFAULT FALSE, timeline INT NOT NULL ) ENGINE = MyISAM; CREATE UNIQUE INDEX backup_slices_i ON backup_slices (id, tablename, timeline); zentyal-core-2.3.21+quantal1/schemas/sql/audit_actions.sql0000664000000000000000000000053312017102272020411 0ustar CREATE TABLE IF NOT EXISTS audit_actions( timestamp TIMESTAMP, username VARCHAR(40), module VARCHAR(40), event ENUM('add', 'set', 'del', 'move', 'action'), model VARCHAR(60), id VARCHAR(120), value TEXT, oldvalue TEXT, temporal BOOLEAN DEFAULT TRUE ) ENGINE = MyISAM; zentyal-core-2.3.21+quantal1/schemas/global.yaml0000664000000000000000000000002612017102272016364 0ustar class: 'EBox::Global' zentyal-core-2.3.21+quantal1/doc/0000775000000000000000000000000012017102272013364 5ustar zentyal-core-2.3.21+quantal1/doc/CodingStyle0000664000000000000000000000554712017102272015546 0ustar A few coding style rules. Some of them make sense, some are just there to provide consistency across everybody's code. - KISS + code should be kept simple and readable. This should be a _major_ priority. Some examples of this: less readable: if (!condition) { more readable: unless (condition) { less readable: $foo->doSomeStuff(someArg, anotherArga) if (condition) more readable: if (condition) { $foo->doSomeStuff(...); } the shorter versions of if constructs are good when the code is small: checkData($arg) or die "wrong data"; # this is perfectly readable but, the ones that put the condition before are more readable: die "Wrong data" unless checkData($arg); # this is less readable - Format + Lines should be 80 characters wide unless there is a _very_ good reason for doing so. + Tabs are tabs, spaces may only be used for alignment purposes, when the space to fill is smaller than a tab. Tabs are 8 spaces wide. - Braces + Opening brace starts at the end of the line: + Ending brace goes on a line by itself. Aligned with the opening line. if () { do stuff } + else and elsif clauses go on the same line as the closing brace of the if: if () { do stuff } else { do other stuff } - Spacing + put spaces before and after parethesis, and braces they make everything more readable: wrong: if(someCondition){ do stuff }else{ do other stuff } right: if (someCondition) { do stuff } else { do other stuff } + put spaces around operators: wrong: my $foo=$bar+$foobar; right: my $foo = $bar + $foobar; - Functions + Make argument fixed argument lists whenever possible: wrong: sub foo {} right: sub foo ($$$) {} + Document all of these things as precisely as possible above the function + all the arguments + what the function returns, in all possible cases + the cases in which the function dies + what exceptions may be thrown and why. + private function names should start with "_": wrong: sub privateFunctionDoNotUse ($$) {} right: sub _privateFunction ($$) {} - Names + Function names should describe what the function does, but they should not be verbose beyond usefulness. + Global variable names should... not exist (beyond the scope of a module). + Local variable names should be short, while understandable: wrong: my $m = $self->moduleName(); wrong: my $myModuleName = self->moduleName(); right my $module = self->moduleName(); right my $mod = self->moduleName(); + Classnames start with capital letters + Function names do not start with capital letters + The whole path to a class must be used to reference it, so it does not make sense to put redundant information in the classname: wrong: EBox::Exception::FooException right: EBox::Exception::Foo right: EBox::FooException zentyal-core-2.3.21+quantal1/doc/eventDoc/0000775000000000000000000000000012017102272015133 5ustar zentyal-core-2.3.21+quantal1/doc/eventDoc/eventFlowDiagram.dia0000664000000000000000000000405712017102272021056 0ustar [o6SWnRt, h֎,L^KJ$%DZL%;K3G?IF&/,##.(NWW9QVތi]en:},I| 3C'Di.27zc ʳ4שvY[\>.i##yo,r?wg Gsޚ|?6+b$$.Ua?#.=4 6j]v@gXgwN|&܁*͗Ro tDl.֤\MqF6tArzSC!Aܰf33#pg{eGoN mЃ{Ś~񛎓A  C9<灇]b4Fs\|t1 =H_z ЁףBV"Crn?o8@n\ob85-ܠG jiO8?b<T g +Jf2FԵ3esFCgR =9+cr@O6:WH?4 1bHP1>q^:/}I7*>V#g8Z̐őm'N0ܴWq7:k \p*KKc\`C[z^+Jș[ 8P^ӯӸ$\KK\,sGKJS VcR;* T)T!t*~ы0zo "8(`^':vxI5D|f09 #KH~ޗ ^ew?wO .^wzentyal-core-2.3.21+quantal1/doc/eventDoc/eventFlowDiagram.png0000664000000000000000000003337112017102272021106 0ustar PNG  IHDRPn3gsBITO IDATx}\ea?]rs1@bb^oҀT :ӊd,bJ+!4I:v"XP#Eg섩 :7Gl" !$XY7{ݻ/=9?ws>s<9[srYkTh(˝Ds!Yjx / C (T辑#iԠ=07@פi@\ DFl*HUmf#@ 7@Jkn H\SBwIL2@7@R vFn~i Y`6*k:; G꫋=xQXSdpĠɦȯɰЩ QA7@G l*hdS%@v7@$: dpC|Y -lo5͸.hd$H%ٴDnnH6SV ДJu*%$l:'@j`2R&Mm inF HHpɑ8 J&m% nJHpaR m3!i &i:o Q 8nt輁7@PtۤO N!"An3&>ӊ! pJƤN"AncR`d "tX n7@P ( Yj7#pE"An7@P ( pEB @x(4?iG"An7@P ( pE"AnC/pE"An̄'pS fC\ ( (Lu+@ ( =#S3&IY'Et(r\Ǡס3l,N @t@҄TbHpEInHp}$HF& YVRtǨY蜞`t:oSPdnVl'$Cort@r؉54 =b @Hdn7@P ( p{~k (㚫8㌭[Vn=ztnݺtfVMb+MZ;߿c=VwͫVJb@7@RSN9r(ZxO<144O~O>VZuСmݶp޳>'(R ZrwRg?+JwV\s˗OzJ{O=Ծe˖Yf߾}{[޲vڑ+)Jׯ߶m[;v`JR޺uk%lY4d[1jqƕ+WVpezQm۶^5kVu /RCzzzk7t뭷^veQ͙3g}}}QXbӦMutС3gym߾}Ku={-2bQ Pe(gܶ\=ٿEvE)sΩSN81z-m k[^Zx\uU_''7wό3~_{Vꫯ 'Mpe°iӦݼy͛/袩SJٳgڵkppphhZZ3w~?sKҹ瞻nݺs9gƌRW_s cٳgر-v@ʄ YW׿o|cՕ%W]uՕW^O>4u۷.Yjя|#\ss=w5k4ށVw貔iy?… K6l7qij:;אַn7pI'T{_00Pw֭K.wmT'Ν;wŊ/jjiU vro]r=[\uP/dpC8(;s? WZuwTn߿yUVȑ#?9sy=-G$o{M7S/dW1joooKW򕾾'o}kPٹsY(?{7GQ400p'ڵkǎ\rI't_p{(=v3fL: _B='>1sI&\w]7|'LKhƍ\rI]w}駟>qľ~unݺuܹ KҺuʿovWW;jyF~" 2L yo馳:~?=p˯ڻc .^/J>իJ{SO[lٚ5k۷o߾-k׮R~m۶رcݍdÆ -?~'?ykw{vmTAoR>g֬YSkI](hKow9sv5y饗*:3O[/(̙{(VXiӦ8t̙3G]ɼyo>rGݓm۶Un fͪ>~Ϟ=F`[ng{M74wG}PĻu]wu_(Q˳zCM*kP:<e6pqEw7z>{)tMQ1neMozӒ%KjFMgV d Z`g{W~^uUͯaԩ۷o]jժx~wT:s 4iҮ]>я|#\ss=w ꪫʧ~zpp''?ӟwʕկ=:<<\{μy?>BUr}_u*Q 1{===\7pI'T?x`Eo~󛯿ʿ -±sOz!ѱ31!k*FmUrF~Af("0,E"An7@P ( pE"An7@P (=hJ\.p 5YA)GQ}|1P)K%@G2]H,y$@7U;)7H.oɆnC! d|Cו wɆ\n +H !n CB!0 d|C$#@7F!H d|C $B%@F7$J!` d|CB$&@U8"Ɇ 7 p> B|oCܨVI6pyb1ml(rF% $@74I%qI6py%ЀdC 7c l(8MAI7T谁 B @poLZ C)&7h$I7!|S E07al%e`\ L hpo!@|'@!7&@K( &$hp"dmnXl= N(MI66 J2N7P\M^y%@U󍈓  )!@\@>b$|U K~O b' ߤL$71H6'ߤ@7(^O$7$谁7xI6hDdnq7l M 0> L"ߴG 7@VI6 y tpFAw 7@t@ 7@;QI6 &d!o$|Sl cS7 dpĠzsd$)Zl  N7.Y&1P#8_S%>HDF\n47Z5xz,YjԕH6 $o$HVrfDW+@.7@Fo2b c3} ojזȗdy$)7ߌ6:L!KLp6u-c6@.A.t ŸtKOw(ڔ+P H@SM9tm3* &䂞 %#BSh ]Sn:1nR)'(C Ŝt!L$A pE"An7@P ( p0?"-(Gnhĉs]b/~v7@kc%#GrHeΝ;/_>y׽u^x}FufΜ9mڴ/~=ϥRk[,X[AMnySL\6mҥK7l#ݻ㏿Oя~ӟ˗/+Ju4 >-?~'?yᇫ<;w|pٲek֬ٷo߾}򖷬]v_~۶m;vؽ{w}.J ^֭[[/Qc@MK3kC6nܸr.[o5m۶U fͪᥗ^>tPOOOuy݆j r-U ^={Fnw$0$^A(Gi4߿hѢ;vDQt)ܹsԩ'N^ryxxx?3cI~~=ФI/-_r JIJk2x4uIc[qy":5mڴ7o޼yE]4uR4{]v  Ul⮻:so߲eˋ/կ~oowYcǎ7{nxx_+O̞={ǎMUp`_׿o^䪫+~'|rժU0u۷-~/_ӟtݽ+Wկ~uᡡ̭Yf͇>ÇN4i׮]GG>k;p5ksHI`@V5 Ն ͟?…%6l8qYguwVPʍnᤓN*;w„ gϾKOַui,Y{7V`>\Ye˖ŋ,ZhӦMcmѣk֬>}ink<|0$snR)wɂ`2Űp An7@P ( pE"An7@Pz@n@A)] ^VrVTXjg;y.2=7@ru<ʃ Y+2K о#}KImܴΟ[:jRSխt@œ #f\DKn X#cdXmHssm œPݑ2yTRk-2%@PR5l6RnC2,Hgvs %Jy, 쭩b&3FnɹT1RcE5]=7LTd-Bf{G\ WjɦUQe<48W3,if)e8d=lCrHVWL}2{Enl鍔l 1,Ir)؜M2JE蹁58GZڇ9[ Tw U˱- y)Šޚ\$}!0鹁Ne"iy3ڗޚ &rM4&T9(BF(֔lrzW!Z &_VNMEwd1Z5l+oiRk 4%v9.&eɠ{  0}$rtL4&̹1:֔rGy!<(2R0<BU8eG]8%W4& K1r[S!dDSUFR{5 M`^NEo3@ǚR@2>/ϰF3A42JEPLAwp&js@`\,sC[S!E~SpȬ?o0bM)d1WWһ7Xq>e97c%{n$;52$*N}hL"@uMj0oPMl*JKF*gKPcM)dS@A95s"R"A*& >TtE!@{k*O6EZ_[a4 7X$ل(:mȯ4"r$RkӹC 9SޚRbM{n* +R(=7IzkJEJ6Dc|(B%|IpC+T)9+O K]SҾq+Eꭩl)" W4&FȖƚRQXSO12,EVmJ;N.*TtH Wޚ"'Nhȍ0n*r`F-oFU[,5ubBw9֔$6h-nHF1$܌{1CL(&=2\KLhL̹! zk*$d~X\fw3$*ǤƼU+ !)bM-+̩f¡=7O#q(jRjuL(&Nu ;kfNisC<֌JIDULġBSbͨĚ8`DqR XPKKRl)ڡɆ9s=Υ*8RFiLia=ʭmfa)Z`1ɆDiCmKe8g%ِoE )0,8t6CiN(i BaLZ&I6MrlwZ\4n3,( B5xť$RI ybMB1H*׹|["]岯1p9Px ִLi#Gf4n M{9&Nv?M]a* KA$80w޺]a* KޚH6TʌRe@Ld7 $׬(nQݥ:&|H VbHwֻL(.v(& ~|'kjCD.28I6)КwWE뚟h-^znEoM $46LgDOwwg B &^c Mj ? jD%tẌ́dEʼ+{u2nIIdjiIufQ*5#'BMNdQfŞvW6ϦFuHM"L2[>Ew)n6 FR8ECZ0ǂ 9ʅgMA^g\zj´)HaF=fnvKLL~JYHZq;$ԄiSh3,`Q3,#;md`ѨPmB+ 07eBQO qh 1Jc 3`]M|GITS;Xp5͆Ȅ(XHVGO]Tq; GHZ =7>Aw M05Na1&.oK9ԌU!RHt[.o%sϸEr\.O8qܹ+V/~[*?8@¨8&uglݺrѣӧOu֥KQmb?¥;lWw>44Tn)㾣(:r?|9wy>hO̯Q?kznFjLBAZUVq*n޼yժUmSX4j3gN4iʕJ77juy7nx%uw~'NWx;vKN:? .ػwotG]vƌSNP]W򕾾'o}+oy&Lh[zJKkn;jE lMOH(J;v83>7~<+/]CPYu7Y={f̘Qw׌3W_}g+^tE/޽{/kݨ?f͚cg=tg>󙷿_{g}.򫯾+W͔^Kǩ{9묳z{{7˿˸6حn(o/}iڴiw&wb޼y۷o3/R=۶m[ /0k֬3rU񄛇zh{#G~/MDɬ|+bʔ)SL+>\Y+|>f͚Uu vz뭗]vYEsٽ{w___E+VشiS԰ &ƫpSƪQ7YJj< -m{^tèzw]'xberYudElݺuٲe3f̘?꽃۵oqwb„ ;Sg„ &L8I턛o~u[ס1k׮袋 /_źu*׭[lٲ}ݻ}{X1_:_|)Smo{/)ST^Kc&L6pQF]d)$Qozӛ,YR0y}޿/ӕO=Xr_j/;_nEUj|ڄic^iPbcU٨˛,%5~ԅox;vXbEKfhhhϞ=7n|_.' fcU٨˛,%5V78lٲe===-ڴiST*M0aٗ^ziuַN;%Kw}{oH_^0np 'tRߣGYfӦM0r mذ2BrYgy ^Qc턛 oxþ}_^;a3gΨqΜ9 ؞6y׿'O6h>Fm´qii͇>+ƪQ7YJjW]vٲe/}-[VWv(-cN9#nL5ޭM$*{޸ 7>ٳᄎZ^ȡ1>tس7C_IN>}s'N]o˘}N44??~衇ؓnm"QyvM3=Sj{kZ^-ƸEu~ 73yMkΎ<@&<x9irqUėnEkNhϓ^>=z+oKy(@r?7zSLJhà!;Fꜗ鯿t V٤&<x= 1.1 == TODO: Ilustrate this changes with some examples Some changes have been introduced since 1.1 to EBox::Model::DataTable. Prior to 1.1, to iterate over each row we had to call EBox::Model::DataTable::rows() which returned an array reference containing all the rows in a model. That was a pretty naive implementation that didn't work ok when dealing with thousands of entries for obvious reasons. Now you will need to call EBox::Model::DataTable::ids() to retrieve all the row identifiers that make up a table. Once you have the id array, you just need to iterate over it and fetch every row with EBox::Model::DataTable::row($id). This approach let us manage more entries using less memory, although you will have an extra line code to retrieve the ids. Deprecated methods ================== EBox::Model::DataTable::rows() New methods =========== EBox::Model::DataTable::syncRows This method might be useful to add or remove rows before they are presented. In that case you must override this method. Warning: You should never call within this function or you will enter into a deep recursion Parameters: (POSITIONAL) currentIds - array ref containing the current row indentifiers Returns: boolean - true if the current rows have been modified, i.e: there's been a row addition or row removal EBox::Model::DataTable::ids Return an array containing the identifiers of each table row. The ids are ordered by the field specified by the model. This method will call Returns: array ref - containing the row identifiers Changed methods =============== All find* method that returned more than one row have been changed. Now they return an array of id rows. == End of Change == zentyal-core-2.3.21+quantal1/www/0000775000000000000000000000000012017102272013443 5ustar zentyal-core-2.3.21+quantal1/www/js/0000775000000000000000000000000012017102272014057 5ustar zentyal-core-2.3.21+quantal1/www/js/common.js0000664000000000000000000000624112017102272015710 0ustar // Copyright (C) 2004-2012 eBox Technologies S.L. licensed under the GPLv2 function getElementByClass(classname) { ccollect=new Array() var inc=0; var alltags=document.getElementsByTagName("*"); for (i=0; i= 65 && which <= 90) && !shift_status) || ((which >= 97 && which <= 122) && shift_status)) { // uppercase, no shift key capslock.show_warning(targ); } else { capslock.hide_warning(targ); } }, show_warning: function(targ) { var warning = document.getElementById('capsWarning'); if (warning) { warning.style.display = "block"; } }, hide_warning: function(targ) { var warning = document.getElementById('capsWarning'); if (warning) { warning.style.display = "none"; } } }; function toggleWarning() { var warning = document.getElementById('capsWarning'); if (warning) { if (warning.style.display == "none") { warning.style.display = "block"; } else { warning.style.display = "none"; } } return false; } (function(i) { var u =navigator.userAgent; var e=/*@cc_on!@*/false; var st = setTimeout; if(/webkit/i.test(u)) { st( function() { var dr=document.readyState; if (dr == "loaded" || dr == "complete" ) { i() } else { st(arguments.callee,10); } }, 10); } else if ( (/mozilla/i.test(u) && !/(compati)/.test(u)) || (/opera/i.test(u) )) { document.addEventListener("DOMContentLoaded",i,false); } else if (e) { ( function() { var t = document.createElement('doc:rdy'); try{ t.doScroll('left'); i(); t=null; } catch (e) { st(arguments.callee,0); } } ) (); } else { window.onload=i; } } )(capslock.init); zentyal-core-2.3.21+quantal1/www/js/fileUpload.js0000664000000000000000000000721512017102272016506 0ustar /* * Copyright (C) 2008-2012 eBox Technologies S.L. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, as * published by the Free Software Foundation. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Class: EBox.FileUpload * * This object manages a file upload using only JS and Asynchronous * requests. It is based on Ajax Iframe method from * www.webtoolkit.info. The method is based on storing the server * response on a Iframe element while the file is sent using a form * submitted when a change on the file input is done, i.e. when "browse" * action is finished. * * Adapted to be use with PrototypeJS library * * */ if ( typeof(EBox) == 'undefined' ) { var EBox = {}; } EBox.FileUpload = Class.create(); EBox.FileUpload.prototype = { /* Group: Public methods */ /* Constructor: initialize Create a EBox.FileUpload JS class to manage file upload Parameters: formId - String the form identifier which stores the input file onStart - Function to be performed before submit the file onComplete - Function to be performed after completing the file submitting - Named parameters Returns: - the recently created object */ initialize : function(params) { this.onStart = params.onStart; this.onComplete = params.onComplete; this.form = $(params.formId); this.iframe = this._createIframe(); this.form.setAttribute('target', this.iframe.id); }, /* Method: submit Submit the file */ submit : function() { if ( this.onStart ) { this.onStart(); } this.form.submit(); return false; }, /* Group: Private methods */ // Method to create the iframe to store the server response // Returns the iframe created and already stored in the document _createIframe : function() { this.div = document.createElement('DIV'); var iframe = document.createElement('IFRAME'); var iframeId = 'iframe_' + Math.floor(Math.random() * 99999); iframe.setAttribute('id', iframeId); iframe.setAttribute('name', iframeId); iframe.setAttribute('src', 'about:blank'); Element.extend(iframe); iframe.hide(); // To "cache" the bound functions so that observing is finished this.handler = EBox.FileUpload.prototype._onIframeLoad.bindAsEventListener(this); Event.observe(iframe, 'load', this.handler); //
    ...
    this.div.appendChild(iframe); this.form.parentNode.appendChild(this.div); return iframe; }, // Handler to manage when the iframe is loaded _onIframeLoad : function ( event ) { var doc; if ( this.iframe.contentDocument ) { doc = this.iframe.contentDocument; } else if ( this.iframe.contentWindow ) { doc = this.iframe.contentWindow.document; } else { doc = window.frames[this.iframe.id].document; } if ( doc.location.href == "about:blank" ) { return; } if ( typeof(this.onComplete) == "function" ) { this.onComplete(doc.body.innerHTML); // Remove everything created before // $(this.div).remove(); Event.stopObserving(this.iframe, 'load', this.handler); } } } zentyal-core-2.3.21+quantal1/www/js/format.js0000664000000000000000000000552112017102272015710 0ustar // Copyright (C) 2004-2012 eBox Technologies S.L. licensed under the GPLv2 /* * * * Get the human-readable size for an amount of bytes * * @param int size : the number of bytes to be converted * * @param int precision : number of decimal places to round to; * * optional - defaults to 2 * * @param bool long_name : whether or not the returned size tag should * * be unabbreviated (ie "Gigabytes" or "GB"); * * optional - defaults to true * * @param bool real_size : whether or not to use the real (base 1024) * * or commercial (base 1000) size; * * optional - defaults to true * * @return string : the converted size * */ function getBytes(size,precision,longName,realSize) { if (typeof precision=="undefined") { precision=2; } if (typeof longName=="undefined") { longName=false; } if (typeof realSize=="undefined") { realSize=true; } var base=realSize?1024:1000; var pos=0; while (size>base) { size/=base; pos++; } var prefix=getSizePrefix(pos); var sizeName=longName?prefix+"bytes":prefix.charAt(0)+'B'; sizeName=sizeName.charAt(0).toUpperCase()+sizeName.substring(1); var num=Math.pow(10,precision); return (Math.round(size*num)/num)+' '+sizeName; } /* * * * @param int pos : the distence along the metric scale relitive to 0 * * @return string : the prefix * */ function getSizePrefix(pos) { switch (pos) { case 0: return ""; case 1: return "kilo"; case 2: return "mega"; case 3: return "giga"; case 4: return "tera"; case 5: return "peta"; case 6: return "exa"; case 7: return "zetta"; case 8: return "yotta"; case 9: return "xenna"; default: return "?-"; } } function getDegrees(degrees) { return degrees + "°"; } function getTimeDiff(milliseconds) { var pos = 0; var base = 1000; var timeDiff = milliseconds; while ( timeDiff > base ) { timeDiff /= base; pos++; if ( pos >= 1 ) { base = 60; } if ( pos > 2 ) { break; } } var num = Math.pow(10, 2); return ( Math.round( timeDiff * num) / num) + ' ' + getTimeDiffSuffix(pos); } function getTimeDiffSuffix(pos) { switch (pos) { case 0 : return "ms"; case 1 : return "s"; case 2 : return "min"; default: return "h"; } } function getBytesPerSec(bps) { return getBytes(bps) + '/s'; } function getTime(seconds) { var d = new Date(seconds * 1000); return d.toLocaleTimeString(); } function getDate(seconds) { var d = new Date(seconds * 1000); return d.toLocaleDateString(); } function getFullDate(seconds) { var d = new Date(seconds * 1000); return d.toLocaleString(); } zentyal-core-2.3.21+quantal1/www/js/csshover.htc0000664000000000000000000000536312017102272016422 0ustar zentyal-core-2.3.21+quantal1/www/js/excanvas.js0000664000000000000000000002527012017102272016233 0ustar // Copyright 2006 Google Inc. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. if(!window.CanvasRenderingContext2D){(function(){var N=Math;var O=N.round;var L=N.sin;var U=N.cos;var A=10;var I=A/2;var G={init:function(W){var X=W||document;if(/MSIE/.test(navigator.userAgent)&&!window.opera){var V=this;X.attachEvent("onreadystatechange",function(){V.init_(X)})}},init_:function(Y){if(Y.readyState=="complete"){if(!Y.namespaces.g_vml_){Y.namespaces.add("g_vml_","urn:schemas-microsoft-com:vml")}var X=Y.createStyleSheet();X.cssText="canvas{display:inline-block;overflow:hidden;text-align:left;width:300px;height:150px}g_vml_\\:*{behavior:url(#default#VML)}";var W=Y.getElementsByTagName("canvas");for(var V=0;V"){var V="/"+X.tagName;var W;while((W=X.nextSibling)&&W.tagName!=V){W.removeNode()}if(W){W.removeNode()}}X.parentNode.replaceChild(Y,X);return Y},initElement:function(W){W=this.fixElement_(W);W.getContext=function(){if(this.context_){return this.context_}return this.context_=new J(this)};W.attachEvent("onpropertychange",T);W.attachEvent("onresize",B);var V=W.attributes;if(V.width&&V.width.specified){W.style.width=V.width.nodeValue+"px"}else{W.width=W.clientWidth}if(V.height&&V.height.specified){W.style.height=V.height.nodeValue+"px"}else{W.height=W.clientHeight}return W}};function T(W){var V=W.srcElement;switch(W.propertyName){case"width":V.style.width=V.attributes.width.nodeValue+"px";V.getContext().clearRect();break;case"height":V.style.height=V.attributes.height.nodeValue+"px";V.getContext().clearRect();break}}function B(W){var V=W.srcElement;if(V.firstChild){V.firstChild.style.width=V.clientWidth+"px";V.firstChild.style.height=V.clientHeight+"px"}}G.init();var D=[];for(var R=0;R<16;R++){for(var Q=0;Q<16;Q++){D[R*16+Q]=R.toString(16)+Q.toString(16)}}function K(){return[[1,0,0],[0,1,0],[0,0,1]]}function E(Y,X){var W=K();for(var V=0;V<3;V++){for(var b=0;b<3;b++){var Z=0;for(var a=0;a<3;a++){Z+=Y[V][a]*X[a][b]}W[V][b]=Z}}return W}function P(W,V){V.fillStyle=W.fillStyle;V.lineCap=W.lineCap;V.lineJoin=W.lineJoin;V.lineWidth=W.lineWidth;V.miterLimit=W.miterLimit;V.shadowBlur=W.shadowBlur;V.shadowColor=W.shadowColor;V.shadowOffsetX=W.shadowOffsetX;V.shadowOffsetY=W.shadowOffsetY;V.strokeStyle=W.strokeStyle;V.arcScaleX_=W.arcScaleX_;V.arcScaleY_=W.arcScaleY_}function C(W){var Z,Y=1;W=String(W);if(W.substring(0,3)=="rgb"){var b=W.indexOf("(",3);var V=W.indexOf(")",b+1);var a=W.substring(b+1,V).split(",");Z="#";for(var X=0;X<3;X++){Z+=D[Number(a[X])]}if((a.length==4)&&(W.substr(3,1)=="a")){Y=a[3]}}else{Z=W}return[Z,Y]}function M(V){switch(V){case"butt":return"flat";case"round":return"round";case"square":default:return"square"}}function J(W){this.m_=K();this.mStack_=[];this.aStack_=[];this.currentPath_=[];this.strokeStyle="#000";this.fillStyle="#000";this.lineWidth=1;this.lineJoin="miter";this.lineCap="butt";this.miterLimit=A*1;this.globalAlpha=1;this.canvas=W;var V=W.ownerDocument.createElement("div");V.style.width=W.clientWidth+"px";V.style.height=W.clientHeight+"px";V.style.overflow="hidden";V.style.position="absolute";W.appendChild(V);this.element_=V;this.arcScaleX_=1;this.arcScaleY_=1}var H=J.prototype;H.clearRect=function(){this.element_.innerHTML="";this.currentPath_=[]};H.beginPath=function(){this.currentPath_=[]};H.moveTo=function(W,V){this.currentPath_.push({type:"moveTo",x:W,y:V});this.currentX_=W;this.currentY_=V};H.lineTo=function(W,V){this.currentPath_.push({type:"lineTo",x:W,y:V});this.currentX_=W;this.currentY_=V};H.bezierCurveTo=function(X,V,a,Z,Y,W){this.currentPath_.push({type:"bezierCurveTo",cp1x:X,cp1y:V,cp2x:a,cp2y:Z,x:Y,y:W});this.currentX_=Y;this.currentY_=W};H.quadraticCurveTo=function(c,b,a,Z){var W=this.currentX_+2/3*(c-this.currentX_);var V=this.currentY_+2/3*(b-this.currentY_);var Y=W+(a-this.currentX_)/3;var X=V+(Z-this.currentY_)/3;this.bezierCurveTo(W,V,Y,X,a,Z)};H.arc=function(b,Z,a,Y,W,X){a*=A;var f=X?"at":"wa";var c=b+(U(Y)*a)-I;var e=Z+(L(Y)*a)-I;var V=b+(U(W)*a)-I;var d=Z+(L(W)*a)-I;if(c==V&&!X){c+=0.125}this.currentPath_.push({type:f,x:b,y:Z,radius:a,xStart:c,yStart:e,xEnd:V,yEnd:d})};H.rect=function(X,W,V,Y){this.moveTo(X,W);this.lineTo(X+V,W);this.lineTo(X+V,W+Y);this.lineTo(X,W+Y);this.closePath()};H.strokeRect=function(X,W,V,Y){this.beginPath();this.moveTo(X,W);this.lineTo(X+V,W);this.lineTo(X+V,W+Y);this.lineTo(X,W+Y);this.closePath();this.stroke()};H.fillRect=function(X,W,V,Y){this.beginPath();this.moveTo(X,W);this.lineTo(X+V,W);this.lineTo(X+V,W+Y);this.lineTo(X,W+Y);this.closePath();this.fill()};H.createLinearGradient=function(W,Y,V,X){var Z=new S("gradient");return Z};H.createRadialGradient=function(Y,a,X,W,Z,V){var b=new S("gradientradial");b.radius1_=X;b.radius2_=V;b.focus_.x=Y;b.focus_.y=a;return b};H.drawImage=function(n,Y){var f,c,i,u,l,j,p,x;var g=n.runtimeStyle.width;var m=n.runtimeStyle.height;n.runtimeStyle.width="auto";n.runtimeStyle.height="auto";var e=n.width;var s=n.height;n.runtimeStyle.width=g;n.runtimeStyle.height=m;if(arguments.length==3){f=arguments[1];c=arguments[2];l=j=0;p=i=e;x=u=s}else{if(arguments.length==5){f=arguments[1];c=arguments[2];i=arguments[3];u=arguments[4];l=j=0;p=e;x=s}else{if(arguments.length==9){l=arguments[1];j=arguments[2];p=arguments[3];x=arguments[4];f=arguments[5];c=arguments[6];i=arguments[7];u=arguments[8]}else{throw"Invalid number of arguments"}}}var v=this.getCoords_(f,c);var Z=p/2;var X=x/2;var t=[];var V=10;var b=10;t.push(" ','","");this.element_.insertAdjacentHTML("BeforeEnd",t.join(""))};H.stroke=function(y){var e=[];var d=false;var AB=C(y?this.fillStyle:this.strokeStyle);var u=AB[0];var Y=AB[1]*this.globalAlpha;var X=10;var j=10;e.push("v.x){v.x=AA.x}if(t.y==null||AA.yv.y){v.y=AA.y}}}e.push(' ">');if(typeof this.fillStyle=="object"){var n={x:"50%",y:"50%"};var r=(v.x-t.x);var l=(v.y-t.y);var z=(r>l)?r:l;n.x=O((this.fillStyle.focus_.x/r)*100+50)+"%";n.y=O((this.fillStyle.focus_.y/l)*100+50)+"%";var g=[];if(this.fillStyle.type_=="gradientradial"){var x=(this.fillStyle.radius1_/z*100);var m=(this.fillStyle.radius2_/z*100)-x}else{var x=0;var m=100}var V={offset:null,color:null};var Z={offset:null,color:null};this.fillStyle.colors_.sort(function(a,W){return a.offset-W.offset});for(var w=0;wV.offset||V.offset==null){V.offset=f.offset;V.color=f.color}if(f.offset')}else{if(y){e.push('')}else{e.push("')}}e.push("");this.element_.insertAdjacentHTML("beforeEnd",e.join(""))};H.fill=function(){this.stroke(true)};H.closePath=function(){this.currentPath_.push({type:"close"})};H.getCoords_=function(W,V){return{x:A*(W*this.m_[0][0]+V*this.m_[1][0]+this.m_[2][0])-I,y:A*(W*this.m_[0][1]+V*this.m_[1][1]+this.m_[2][1])-I}};H.save=function(){var V={};P(this,V);this.aStack_.push(V);this.mStack_.push(this.m_);this.m_=E(K(),this.m_)};H.restore=function(){P(this.aStack_.pop(),this);this.m_=this.mStack_.pop()};H.translate=function(X,W){var V=[[1,0,0],[0,1,0],[X,W,1]];this.m_=E(V,this.m_)};H.rotate=function(W){var Y=U(W);var X=L(W);var V=[[Y,X,0],[-X,Y,0],[0,0,1]];this.m_=E(V,this.m_)};H.scale=function(X,W){this.arcScaleX_*=X;this.arcScaleY_*=W;var V=[[X,0,0],[0,W,0],[0,0,1]];this.m_=E(V,this.m_)};H.clip=function(){};H.arcTo=function(){};H.createPattern=function(){return new F};function S(V){this.type_=V;this.radius1_=0;this.radius2_=0;this.colors_=[];this.focus_={x:0,y:0}}S.prototype.addColorStop=function(W,V){V=C(V);this.colors_.push({offset:1-W,color:V})};function F(){}G_vmlCanvasManager=G;CanvasRenderingContext2D=J;CanvasGradient=S;CanvasPattern=F})()};zentyal-core-2.3.21+quantal1/www/js/tabMenu.js0000664000000000000000000002246012017102272016014 0ustar /* * Copyright (C) 2008-2012 eBox Technologies S.L. * * This program is free software; you can redistribute it and/or modify * it under the terms of the GNU General Public License, version 2, as * published by the Free Software Foundation. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA */ /* * Class: EBox.Tabs * * This object creates a tab group to manage using CSS and JavaScript. * * */ if ( typeof(EBox) == 'undefined' ) { var EBox = {}; } EBox.Tabs = Class.create(); EBox.Tabs.prototype = { /* Constructor: initialize Create a EBox.Tabs JS class to manage a tab group Parameters: tabContainer - String the tab container identifier from where the tabs hang modelAttrs - Associative array indexing by element identifier containing the following properties: action - URL for the actions to perform by an AJAX request additionalParams - array containing associative arrays with the following elements: name - String the param's name value - String the param's value directory - a parameter to send specific for the tab model options - Associate array which can contain the following optional parameters: - activeClassName : String the CSS class name to the active tab - defaultTab : String the name of the default tab or first or last Returns: - the recently created object */ initialize : function(tabContainer, modelAttrs, options) { // The div where the tabs are this.tabContainer = $(tabContainer); // Set the tabMenu name var nameParts = tabContainer.split('_'); this.tabName = nameParts[1]; // Set the active tab this.activeTab = false; this.activeTabIdx = -1; // The tabs this.tabs = []; // The object where the action URLs are stored, indexed by model name this.modelAttrs = modelAttrs; // Create a form to send the parameters this._createForm(); if ( options && options.activeClassName ) { this.activeClassName = options.activeClassName; } else { // Default CSS active class name this.activeClassName = 'current'; } if ( options && options.defaultTab ) { this.defaultTab = options.defaultTab; } else { // Default tab to show this.defaultTab = 'first'; } // Menu stores all the A hrefs children from the div tab given var tabs = this.tabContainer.getElementsBySelector('a'); // Add the onclick function tabs.each( function(linkElement) { this._addTab(linkElement); }.bind(this)); if ( this.defaultTab == 'first' ) { this.activeTab = this.tabs.first(); this.activeTabIdx = 0; } else if ( this.defaultTab == 'last' ) { this.activeTab = this.tabs.last(); this.activeTabIdx = this.tabs.length; } else { for( var idx = 0, len = this.tabs.length; idx < len; idx++ ) { if ( this.tabs[idx].id == options.defaultTab ) { this.activeTab = this.tabs[idx]; this.activeTabIdx = idx; } } } // Show default tab (Done at the template) // if ( this.defaultTab == 'first' ) { // this.showActiveTab( this.tabs.first() ); // } else if ( this.defaultTab == 'last' ) { // this.showActiveTab( this.tabs.last() ); // } else { // this.showActiveTab( this.defaultTab ); // } }, /* Method: showActiveTab Show the current active tab Parameters: linkElement - Extended element which contains the link to load the tab container *or* tabName - String the link name whose element contains the link to load the tab */ showActiveTab : function(tab) { if ( ! tab ) { // If no tab is passed, then return silently return; } if ( typeof( tab ) == 'string' ) { // Search for the element whose id is call tab for ( var idx = 0, len = this.tabs.length; idx < len; idx++) { if ( this.tabs[idx].id == tab ) { this.showActiveTab(this.tabs[idx]); return; } } } else { // Hide the remainder tabs this.tabs.without(tab).each( function(linkElement) { linkElement.removeClassName( this.activeClassName ); }.bind(this)); // Set the current active tab this.activeTab = tab; // Show the tab tab.addClassName(this.activeClassName); // Set the correct form values this._setDirInput(); // Set additional parameters this._setAdditionalParams(); // Load the content from table-helper hangTable( 'tabData_' + this.tabName , 'errorTabData_' + this.tabName, this.modelAttrs[ tab.id ].action, 'tableForm', 'tabData_' + this.tabName ); } }, /* Method: next Show the next tab. If the last one, it does nothing. */ next : function () { if ( this.activeTabIdx == this.tabs.length ) { return; } this.activeTabIdx++; this.activeTab = this.tabs[this.activeTabIdx]; this.showActiveTab( this.activeTab ); }, /* Method: previous Show the previous tab. If the first one, it does nothing. */ previous : function () { if ( this.activeTabIdx == 0 ) { return; } this.activeTabIdx--; this.activeTab = this.tabs[this.activeTabIdx]; this.showActiveTab( this.activeTab ); }, /* Method: first Show the first tab. */ first : function () { this.activeTabIdx = 0; this.activeTab = this.tabs[this.activeTabIdx]; this.showActiveTab( this.activeTab ); }, /* Method: last Show the last tab. */ last : function () { this.activeTabIdx = this.tabs.length - 1; this.activeTab = this.tabs[this.activeTabIdx]; this.showActiveTab( this.activeTab ); }, /* Group: Private method */ /* Method: _addTab Add the the link element to the EBox.Tabs object Parameters: linkElement - Element the extended element which it is the element */ _addTab : function(linkElement) { // Add the tab to the this.tabs array in order to manage hrefs this.tabs.push(linkElement); // Create the property key to call by user url# linkElement.key = linkElement.hash.substring(1); var clickHandler = function(linkElement) { if ( window.event ) { Event.stop( window.event ); } this.showActiveTab(linkElement); return false; }.bind(this, linkElement); Event.observe( linkElement, 'click', clickHandler); }, /* Method: _createForm Create the form to send parameters */ _createForm : function () { // Create the form inputs var actionInput = document.createElement('INPUT'); actionInput.setAttribute('name', 'action'); actionInput.setAttribute('type', 'hidden'); actionInput.setAttribute('value', 'view'); // Create Form element var form = document.createElement('FORM'); form.setAttribute( 'id', 'tableForm'); form.appendChild( actionInput ); // Append the form to the body this.tabContainer.parentNode.appendChild(form); }, /* Method: _setDirInput Set the directory input value from the selected tab in order to make the POST request dynamically. It will replace any previous directory input value if any. */ _setDirInput : function() { var input = $('tableForm')['directory']; if ( input ) { // Input is defined input.setAttribute('value', this.modelAttrs[this.activeTab.id].directory); } else { // Create the input var dirInput = document.createElement('input'); dirInput.setAttribute('name', 'directory'); dirInput.setAttribute('type', 'hidden'); dirInput.setAttribute('value', this.modelAttrs[this.activeTab.id].directory); $('tableForm').appendChild(dirInput); } }, /* Method: _setAdditionalParams Set the additional parameters to be sent in POST request. */ _setAdditionalParams : function() { // Check if additionalParams is defined if ( this.modelAttrs[this.activeTab.id].additionalParams ) { for(var idx=0; idx < this.modelAttrs[this.activeTab.id].additionalParams.length; idx++) { var param = this.modelAttrs[this.activeTab.id].additionalParams[idx]; var input = $('tableForm')[param.name]; if ( input ) { // Input is defined input.setAttribute('value', param.value); } else { // Create the input var dirInput = document.createElement('input'); dirInput.setAttribute('name', param.name); dirInput.setAttribute('type', 'hidden'); dirInput.setAttribute('value', param.value); $('tableForm').appendChild(dirInput); } } } } } zentyal-core-2.3.21+quantal1/www/js/modalbox.js0000664000000000000000000005413512017102272016232 0ustar /* ModalBox - The pop-up window thingie with AJAX, based on prototype and script.aculo.us. Copyright Andrey Okonetchnikov (andrej.okonetschnikow@gmail.com), 2006-2007 All rights reserved. VERSION 1.6.0 Last Modified: 12/13/2007 */ if (!window.Modalbox) var Modalbox = new Object(); Modalbox.Methods = { overrideAlert: false, // Override standard browser alert message with ModalBox focusableElements: new Array, currFocused: 0, initialized: false, active: true, options: { title: "ModalBox Window", // Title of the ModalBox window overlayClose: true, // Close modal box by clicking on overlay width: 500, // Default width in px height: 90, // Default height in px overlayOpacity: .65, // Default overlay opacity overlayDuration: .25, // Default overlay fade in/out duration in seconds slideDownDuration: .5, // Default Modalbox appear slide down effect in seconds slideUpDuration: .5, // Default Modalbox hiding slide up effect in seconds resizeDuration: .25, // Default resize duration seconds inactiveFade: true, // Fades MB window on inactive state transitions: true, // Toggles transition effects. Transitions are enabled by default loadingString: "Please wait. Loading...", // Default loading string message closeString: "Close window", // Default title attribute for close window link closeValue: "×", // Default string for close link in the header params: {}, method: 'get', // Default Ajax request method autoFocusing: true, // Toggles auto-focusing for form elements. Disable for long text pages. aspnet: false // Should be use then using with ASP.NET costrols. Then true Modalbox window will be injected into the first form element. }, _options: new Object, setOptions: function(options) { Object.extend(this.options, options || {}); }, _init: function(options) { // Setting up original options with default options Object.extend(this._options, this.options); this.setOptions(options); //Create the overlay this.MBoverlay = new Element("div", { id: "MB_overlay", opacity: "0" }); //Create DOm for the window this.MBwindow = new Element("div", {id: "MB_window", style: "display: none"}).update( this.MBframe = new Element("div", {id: "MB_frame"}).update( this.MBheader = new Element("div", {id: "MB_header"}).update( this.MBcaption = new Element("div", {id: "MB_caption"}) ) ) ); this.MBclose = new Element("a", {id: "MB_close", title: this.options.closeString, href: "#"}).update("" + this.options.closeValue + ""); this.MBheader.insert({'bottom':this.MBclose}); this.MBcontent = new Element("div", {id: "MB_content"}).update( this.MBloading = new Element("div", {id: "MB_loading"}).update(this.options.loadingString) ); this.MBframe.insert({'bottom':this.MBcontent}); // Inserting into DOM. If parameter set and form element have been found will inject into it. Otherwise will inject into body as topmost element. // Be sure to set padding and marging to null via CSS for both body and (in case of asp.net) form elements. var injectToEl = this.options.aspnet ? $(document.body).down('form') : $(document.body); injectToEl.insert({'top':this.MBwindow}); injectToEl.insert({'top':this.MBoverlay}); // Initial scrolling position of the window. To be used for remove scrolling effect during ModalBox appearing this.initScrollX = window.pageXOffset || document.body.scrollLeft || document.documentElement.scrollLeft; this.initScrollY = window.pageYOffset || document.body.scrollTop || document.documentElement.scrollTop; //Adding event observers this.hideObserver = this._hide.bindAsEventListener(this); this.kbdObserver = this._kbdHandler.bindAsEventListener(this); this._initObservers(); this.initialized = true; // Mark as initialized }, show: function(content, options) { if(!this.initialized) this._init(options); // Check for is already initialized this.content = content; this.setOptions(options); if(this.options.title) // Updating title of the MB $(this.MBcaption).update(this.options.title); else { // If title isn't given, the header will not displayed $(this.MBheader).hide(); $(this.MBcaption).hide(); } if(this.MBwindow.style.display == "none") { // First modal box appearing this._appear(); this.event("onShow"); // Passing onShow callback } else { // If MB already on the screen, update it this._update(); this.event("onUpdate"); // Passing onUpdate callback } }, hide: function(options) { // External hide method to use from external HTML and JS if(this.initialized) { // Reading for options/callbacks except if event given as a pararmeter if(options && typeof options.element != 'function') Object.extend(this.options, options); // Passing beforeHide callback this.event("beforeHide"); if(this.options.transitions) Effect.SlideUp(this.MBwindow, { duration: this.options.slideUpDuration, transition: Effect.Transitions.sinoidal, afterFinish: this._deinit.bind(this) } ); else { $(this.MBwindow).hide(); this._deinit(); } } else throw("Modalbox is not initialized."); }, _hide: function(event) { // Internal hide method to use with overlay and close link event.stop(); // Stop event propaganation for link elements /* Then clicked on overlay we'll check the option and in case of overlayClose == false we'll break hiding execution [Fix for #139] */ if(event.element().id == 'MB_overlay' && !this.options.overlayClose) return false; this.hide(); }, alert: function(message){ var html = '

    ' + message + '

    '; Modalbox.show(html, {title: 'Alert: ' + document.title, width: 300}); }, _appear: function() { // First appearing of MB if(Prototype.Browser.IE && !navigator.appVersion.match(/\b7.0\b/)) { // Preparing IE 6 for showing modalbox window.scrollTo(0,0); this._prepareIE("100%", "hidden"); } this._setWidth(); this._setPosition(); if(this.options.transitions) { $(this.MBoverlay).setStyle({opacity: 0}); new Effect.Fade(this.MBoverlay, { from: 0, to: this.options.overlayOpacity, duration: this.options.overlayDuration, afterFinish: function() { new Effect.SlideDown(this.MBwindow, { duration: this.options.slideDownDuration, transition: Effect.Transitions.sinoidal, afterFinish: function(){ this._setPosition(); this.loadContent(); }.bind(this) }); }.bind(this) }); } else { $(this.MBoverlay).setStyle({opacity: this.options.overlayOpacity}); $(this.MBwindow).show(); this._setPosition(); this.loadContent(); } this._setWidthAndPosition = this._setWidthAndPosition.bindAsEventListener(this); Event.observe(window, "resize", this._setWidthAndPosition); }, resize: function(byWidth, byHeight, options) { // Change size of MB without loading content var wHeight = $(this.MBwindow).getHeight(); var wWidth = $(this.MBwindow).getWidth(); var hHeight = $(this.MBheader).getHeight(); var cHeight = $(this.MBcontent).getHeight(); var newHeight = ((wHeight - hHeight + byHeight) < cHeight) ? (cHeight + hHeight - wHeight) : byHeight; if(options) this.setOptions(options); // Passing callbacks if(this.options.transitions) { new Effect.ScaleBy(this.MBwindow, byWidth, newHeight, { duration: this.options.resizeDuration, afterFinish: function() { this.event("_afterResize"); // Passing internal callback this.event("afterResize"); // Passing callback }.bind(this) }); } else { this.MBwindow.setStyle({width: wWidth + byWidth + "px", height: wHeight + newHeight + "px"}); setTimeout(function() { this.event("_afterResize"); // Passing internal callback this.event("afterResize"); // Passing callback }.bind(this), 1); } }, resizeToContent: function(options){ // Resizes the modalbox window to the actual content height. // This might be useful to resize modalbox after some content modifications which were changed ccontent height. var byHeight = this.options.height - this.MBwindow.offsetHeight; if(byHeight != 0) { if(options) this.setOptions(options); // Passing callbacks Modalbox.resize(0, byHeight); } }, resizeToInclude: function(element, options){ // Resizes the modalbox window to the camulative height of element. Calculations are using CSS properties for margins and border. // This method might be useful to resize modalbox before including or updating content. var el = $(element); var elHeight = el.getHeight() + parseInt(el.getStyle('margin-top')) + parseInt(el.getStyle('margin-bottom')) + parseInt(el.getStyle('border-top-width')) + parseInt(el.getStyle('border-bottom-width')); if(elHeight > 0) { if(options) this.setOptions(options); // Passing callbacks Modalbox.resize(0, elHeight); } }, _update: function() { // Updating MB in case of wizards $(this.MBcontent).update(""); this.MBcontent.appendChild(this.MBloading); $(this.MBloading).update(this.options.loadingString); this.currentDims = [this.MBwindow.offsetWidth, this.MBwindow.offsetHeight]; Modalbox.resize((this.options.width - this.currentDims[0]), (this.options.height - this.currentDims[1]), {_afterResize: this._loadAfterResize.bind(this) }); }, loadContent: function () { if(this.event("beforeLoad") != false) { // If callback passed false, skip loading of the content if(typeof this.content == 'string') { var htmlRegExp = new RegExp(/<\/?[^>]+>/gi); if(htmlRegExp.test(this.content)) { // Plain HTML given as a parameter this._insertContent(this.content.stripScripts()); this._putContent(function(){ this.content.extractScripts().map(function(script) { return eval(script.replace("", "")); }.bind(window)); }.bind(this)); } else // URL given as a parameter. We'll request it via Ajax new Ajax.Request( this.content, { method: this.options.method.toLowerCase(), parameters: this.options.params, onSuccess: function(transport) { var response = new String(transport.responseText); this._insertContent(transport.responseText.stripScripts()); this._putContent(function(){ response.extractScripts().map(function(script) { return eval(script.replace("", "")); }.bind(window)); }); }.bind(this), onException: function(instance, exception){ Modalbox.hide(); throw('Modalbox Loading Error: ' + exception); } }); } else if (typeof this.content == 'object') {// HTML Object is given this._insertContent(this.content); this._putContent(); } else { Modalbox.hide(); throw('Modalbox Parameters Error: Please specify correct URL or HTML element (plain HTML or object)'); } } }, _insertContent: function(content){ $(this.MBcontent).hide().update(""); if(typeof content == 'string') { setTimeout(function() { // Hack to disable content flickering in Firefox this.MBcontent.update(content); }.bind(this), 1); } else if (typeof content == 'object') { // HTML Object is given var _htmlObj = content.cloneNode(true); // If node already a part of DOM we'll clone it // If clonable element has ID attribute defined, modifying it to prevent duplicates if(content.id) content.id = "MB_" + content.id; /* Add prefix for IDs on all elements inside the DOM node */ $(content).select('*[id]').each(function(el){ el.id = "MB_" + el.id; }); this.MBcontent.appendChild(_htmlObj); this.MBcontent.down().show(); // Toggle visibility for hidden nodes if(Prototype.Browser.IE) // Toggling back visibility for hidden selects in IE $$("#MB_content select").invoke('setStyle', {'visibility': ''}); } }, _putContent: function(callback){ // Prepare and resize modal box for content if(this.options.height == this._options.height) { setTimeout(function() { // MSIE sometimes doesn't display content correctly Modalbox.resize(0, $(this.MBcontent).getHeight() - $(this.MBwindow).getHeight() + $(this.MBheader).getHeight(), { afterResize: function(){ this.MBcontent.show().makePositioned(); this.focusableElements = this._findFocusableElements(); this._setFocus(); // Setting focus on first 'focusable' element in content (input, select, textarea, link or button) setTimeout(function(){ // MSIE fix if(callback != undefined) callback(); // Executing internal JS from loaded content this.event("afterLoad"); // Passing callback }.bind(this),1); }.bind(this) }); }.bind(this), 1); } else { // Height is defined. Creating a scrollable window this._setWidth(); this.MBcontent.setStyle({overflow: 'auto', height: $(this.MBwindow).getHeight() - $(this.MBheader).getHeight() - 13 + 'px'}); this.MBcontent.show(); this.focusableElements = this._findFocusableElements(); this._setFocus(); // Setting focus on first 'focusable' element in content (input, select, textarea, link or button) setTimeout(function(){ // MSIE fix if(callback != undefined) callback(); // Executing internal JS from loaded content this.event("afterLoad"); // Passing callback }.bind(this),1); } }, activate: function(options){ this.setOptions(options); this.active = true; $(this.MBclose).observe("click", this.hideObserver); if(this.options.overlayClose) $(this.MBoverlay).observe("click", this.hideObserver); $(this.MBclose).show(); if(this.options.transitions && this.options.inactiveFade) new Effect.Appear(this.MBwindow, {duration: this.options.slideUpDuration}); }, deactivate: function(options) { this.setOptions(options); this.active = false; $(this.MBclose).stopObserving("click", this.hideObserver); if(this.options.overlayClose) $(this.MBoverlay).stopObserving("click", this.hideObserver); $(this.MBclose).hide(); if(this.options.transitions && this.options.inactiveFade) new Effect.Fade(this.MBwindow, {duration: this.options.slideUpDuration, to: .75}); }, _initObservers: function(){ $(this.MBclose).observe("click", this.hideObserver); if(this.options.overlayClose) $(this.MBoverlay).observe("click", this.hideObserver); if(Prototype.Browser.IE) Event.observe(document, "keydown", this.kbdObserver); else Event.observe(document, "keypress", this.kbdObserver); }, _removeObservers: function(){ $(this.MBclose).stopObserving("click", this.hideObserver); if(this.options.overlayClose) $(this.MBoverlay).stopObserving("click", this.hideObserver); if(Prototype.Browser.IE) Event.stopObserving(document, "keydown", this.kbdObserver); else Event.stopObserving(document, "keypress", this.kbdObserver); }, _loadAfterResize: function() { this._setWidth(); this._setPosition(); this.loadContent(); }, _setFocus: function() { /* Setting focus to the first 'focusable' element which is one with tabindex = 1 or the first in the form loaded. */ if(this.focusableElements.length > 0 && this.options.autoFocusing == true) { var firstEl = this.focusableElements.find(function (el){ return el.tabIndex == 1; }) || this.focusableElements.first(); this.currFocused = this.focusableElements.toArray().indexOf(firstEl); firstEl.focus(); // Focus on first focusable element except close button } else if($(this.MBclose).visible()) $(this.MBclose).focus(); // If no focusable elements exist focus on close button }, _findFocusableElements: function(){ // Collect form elements or links from MB content this.MBcontent.select('input:not([type~=hidden]), select, textarea, button, a[href]').invoke('addClassName', 'MB_focusable'); return this.MBcontent.select('.MB_focusable'); }, _kbdHandler: function(event) { var node = event.element(); switch(event.keyCode) { case Event.KEY_TAB: event.stop(); /* Switching currFocused to the element which was focused by mouse instead of TAB-key. Fix for #134 */ if(node != this.focusableElements[this.currFocused]) this.currFocused = this.focusableElements.toArray().indexOf(node); if(!event.shiftKey) { //Focusing in direct order if(this.currFocused == this.focusableElements.length - 1) { this.focusableElements.first().focus(); this.currFocused = 0; } else { this.currFocused++; this.focusableElements[this.currFocused].focus(); } } else { // Shift key is pressed. Focusing in reverse order if(this.currFocused == 0) { this.focusableElements.last().focus(); this.currFocused = this.focusableElements.length - 1; } else { this.currFocused--; this.focusableElements[this.currFocused].focus(); } } break; case Event.KEY_ESC: if(this.active) this._hide(event); break; case 32: this._preventScroll(event); break; case 0: // For Gecko browsers compatibility if(event.which == 32) this._preventScroll(event); break; case Event.KEY_UP: case Event.KEY_DOWN: case Event.KEY_PAGEDOWN: case Event.KEY_PAGEUP: case Event.KEY_HOME: case Event.KEY_END: // Safari operates in slightly different way. This realization is still buggy in Safari. if(Prototype.Browser.WebKit && !["textarea", "select"].include(node.tagName.toLowerCase())) event.stop(); else if( (node.tagName.toLowerCase() == "input" && ["submit", "button"].include(node.type)) || (node.tagName.toLowerCase() == "a") ) event.stop(); break; } }, _preventScroll: function(event) { // Disabling scrolling by "space" key if(!["input", "textarea", "select", "button"].include(event.element().tagName.toLowerCase())) event.stop(); }, _deinit: function() { this._removeObservers(); Event.stopObserving(window, "resize", this._setWidthAndPosition ); if(this.options.transitions) { Effect.toggle(this.MBoverlay, 'appear', {duration: this.options.overlayDuration, afterFinish: this._removeElements.bind(this) }); } else { this.MBoverlay.hide(); this._removeElements(); } $(this.MBcontent).setStyle({overflow: '', height: ''}); }, _removeElements: function () { $(this.MBoverlay).remove(); $(this.MBwindow).remove(); if(Prototype.Browser.IE && !navigator.appVersion.match(/\b7.0\b/)) { this._prepareIE("", ""); // If set to auto MSIE will show horizontal scrolling window.scrollTo(this.initScrollX, this.initScrollY); } /* Replacing prefixes 'MB_' in IDs for the original content */ if(typeof this.content == 'object') { if(this.content.id && this.content.id.match(/MB_/)) { this.content.id = this.content.id.replace(/MB_/, ""); } this.content.select('*[id]').each(function(el){ el.id = el.id.replace(/MB_/, ""); }); } /* Initialized will be set to false */ this.initialized = false; this.event("afterHide"); // Passing afterHide callback this.setOptions(this._options); //Settings options object into intial state }, _setWidth: function () { //Set size $(this.MBwindow).setStyle({width: this.options.width + "px", height: this.options.height + "px"}); }, _setPosition: function () { $(this.MBwindow).setStyle({left: Math.round((Element.getWidth(document.body) - Element.getWidth(this.MBwindow)) / 2 ) + "px"}); }, _setWidthAndPosition: function () { $(this.MBwindow).setStyle({width: this.options.width + "px"}); this._setPosition(); }, _getScrollTop: function () { //From: http://www.quirksmode.org/js/doctypes.html var theTop; if (document.documentElement && document.documentElement.scrollTop) theTop = document.documentElement.scrollTop; else if (document.body) theTop = document.body.scrollTop; return theTop; }, _prepareIE: function(height, overflow){ $$('html, body').invoke('setStyle', {width: height, height: height, overflow: overflow}); // IE requires width and height set to 100% and overflow hidden $$("select").invoke('setStyle', {'visibility': overflow}); // Toggle visibility for all selects in the common document }, event: function(eventName) { if(this.options[eventName]) { var returnValue = this.options[eventName](); // Executing callback this.options[eventName] = null; // Removing callback after execution if(returnValue != undefined) return returnValue; else return true; } return true; } }; Object.extend(Modalbox, Modalbox.Methods); if(Modalbox.overrideAlert) window.alert = Modalbox.alert; Effect.ScaleBy = Class.create(); Object.extend(Object.extend(Effect.ScaleBy.prototype, Effect.Base.prototype), { initialize: function(element, byWidth, byHeight, options) { this.element = $(element) var options = Object.extend({ scaleFromTop: true, scaleMode: 'box', // 'box' or 'contents' or {} with provided values scaleByWidth: byWidth, scaleByHeight: byHeight }, arguments[3] || {}); this.start(options); }, setup: function() { this.elementPositioning = this.element.getStyle('position'); this.originalTop = this.element.offsetTop; this.originalLeft = this.element.offsetLeft; this.dims = null; if(this.options.scaleMode=='box') this.dims = [this.element.offsetHeight, this.element.offsetWidth]; if(/^content/.test(this.options.scaleMode)) this.dims = [this.element.scrollHeight, this.element.scrollWidth]; if(!this.dims) this.dims = [this.options.scaleMode.originalHeight, this.options.scaleMode.originalWidth]; this.deltaY = this.options.scaleByHeight; this.deltaX = this.options.scaleByWidth; }, update: function(position) { var currentHeight = this.dims[0] + (this.deltaY * position); var currentWidth = this.dims[1] + (this.deltaX * position); currentHeight = (currentHeight > 0) ? currentHeight : 0; currentWidth = (currentWidth > 0) ? currentWidth : 0; this.setDimensions(currentHeight, currentWidth); }, setDimensions: function(height, width) { var d = {}; d.width = width + 'px'; d.height = height + 'px'; var topd = Math.round((height - this.dims[0])/2); var leftd = Math.round((width - this.dims[1])/2); if(this.elementPositioning == 'absolute' || this.elementPositioning == 'fixed') { if(!this.options.scaleFromTop) d.top = this.originalTop-topd + 'px'; d.left = this.originalLeft-leftd + 'px'; } else { if(!this.options.scaleFromTop) d.top = -topd + 'px'; d.left = -leftd + 'px'; } this.element.setStyle(d); } });zentyal-core-2.3.21+quantal1/www/js/progress.js0000664000000000000000000001062612017102272016266 0ustar // Copyright (C) 2004-2012 eBox Technologies S.L. licensed under the GPLv2 // code used by progress.mas var percent = 0; var time = 0; var ticks = 0; var totalTicks = 0; function porcentH(i){ this.value = 0; this.setValue = function(v){ if(v > 100) v = 100; if(v < 0) v = 0; this.value = v; $('progressValue').morph('width: ' + v + '%', { duration: 0.5 }); document.getElementById('percentValue').innerHTML= v+"%"; }; this.upValue = function(v){ v += this.value; this.setValue(v); }; this.downValue = function(v){ v = this.value - v; this.setValue(v); }; }; var ph = new porcentH('progress'); // Update the page function updatePage (xmlHttp, nextStepTimeout, nextStepUrl) { var rawResponse = xmlHttp.responseText.replace(/\n/g, "
    "); var response = eval("(" + rawResponse + ")"); if (xmlHttp.readyState == 4) { if (response.state == 'running') { // current item if (('message' in response) && response.message.length > 0 ) { $('currentItem').innerHTML = response.message; } if ( ('ticks' in response) && (response.ticks >= 0)) { $('ticks').innerHTML = response.ticks; ticks = response.ticks; } if ( ('totalTicks' in response) && (response.totalTicks > 0)) { $('totalTicks').innerHTML = response.totalTicks; totalTicks = response.totalTicks; } if ( totalTicks > 0 ) { percent = Math.ceil((ticks/totalTicks)*100); ph.setValue(percent); } } else if (response.state == 'done') { pe.stop(); if ( nextStepTimeout > 0 ) { // setTimeout ( "location.href='" + nextStepUrl + "';", nextStepTimeout*1000 ); loadWhenAvailable(nextStepUrl, nextStepTimeout); } if (('errorMsg' in response) && (response.errorMsg)) { $('warning-progress-messages').update( response.errorMsg); $('done_note').removeClassName('note'); $('done_note').addClassName('warning'); $('warning-progress').show(); $('warning-progress-messages').show(); } Element.hide('progressing'); $('done').show(); // Used to tell selenium we are done // with saving changes $('ajax_request_cookie').value = 1337; } else if (response.state == 'error') { pe.stop(); Element.hide('progressing'); $('error-progress').show(); if ('errorMsg' in response) { $('error-progress-message').update( response.errorMsg); } } } } // Generate an Ajax request to fetch the current package function callServer(progressId, url, nextStepTimeout, nextStepUrl) { // Build the URL to connect to var par = "progress=" + progressId ; new Ajax.Request(url, { method: 'post', parameters: par, asynchronous: true, onSuccess: function (t) { updatePage(t, nextStepTimeout, nextStepUrl); } } ); time++; if (time >= 10) { time = 0; showAds(); } } var pe; function createPeriodicalExecuter(progressId, currentItemUrl, reloadInterval, nextStepTimeout, nextStepUrl) { var callServerCurriedBody = "callServer(" + progressId + ", '" + currentItemUrl + "', " + nextStepTimeout + ", '" + nextStepUrl + "')"; callServerCurried = new Function(callServerCurriedBody ); pe = new PeriodicalExecuter(callServerCurried, reloadInterval); } var progress_pl; // use progress_pe if it works function loadWhenAvailable(url, secondsTimeout) { var loadMethod = function() { new Ajax.Request(url, { onSuccess: function(transport) { if (transport.responseText) { progress_pl.stop(); window.location.replace(url); } } } ); }; progress_pl = new PeriodicalExecuter(loadMethod, secondsTimeout); } zentyal-core-2.3.21+quantal1/www/js/table-helper.js0000664000000000000000000007734312017102272016777 0ustar // Copyright (C) 2004-2012 eBox Technologies S.L. licensed under the GPLv2 // TODO // - Use Form.serialize stuff to get params // - Refactor addNewRow and actionClicked, they do almost the same // - Implement a generic function for the onComplete stage function cleanError(table) { var error = $('error_' + table); if (error) { error.innerHTML = ""; } } function setError(table, html) { var error = $('error_' + table); error.className = 'error'; if (error) { error.innerHTML = html; } } // Function: setEnableRecursively // // Disable or enable recursively all child elements of a given elment // // Parameters: // // element - Parent HTMLElement object // state - boolean, true to enable, false to disable // function setEnableRecursively(element, state) { element.childElements().each( function (child) { //XXX Should we check child is a From or // prototype takes care of it? if (state) { Form.Element.enable(child); } else { Form.Element.disable(child); } setEnableRecursively(child, state); } ); } // Function: onFieldChange // // Function called from onChange events on form and table fields. // // Parameters: // // Event - Event prototype // JSONActions - JSON Object containing the actions to take // function onFieldChange(event, JSONActions, table) { var actions = new Hash(JSONActions); var selectedValue = $F(Event.element(event)); if (selectedValue == undefined) { selectedValue = 'off'; } if (! actions.get(selectedValue)) { return; } var onValue = new Hash(actions.get(selectedValue)); var supportedActions = new Array('show', 'hide', 'enable', 'disable'); supportedActions.each ( function (action) { if (onValue.get(action) == undefined) { return; } var fields = onValue.get(action); for (var i = 0; i < fields.length; i++) { var fullId = table + '_' + fields[i] + '_row'; switch (action) { case 'show': show(fullId); break; case 'hide': hide(fullId); break; case 'enable': setEnableRecursively($(fullId), true); break; case 'disable': setEnableRecursively($(fullId), false); break; } } } ); } function encodeFields(table, fields) { var pars = []; for (i in fields) { var field = fields[i]; var value = inputValue(table + '_' + field); if (value) { pars.push(field + '=' + encodeURIComponent(value)); } } return pars.join('&'); } function modalAddNewRow(url, table, fields, directory, nextPage, extraParams) { var title = ''; var selectForeignField; var selectCallerId; var nextPageContextName; var MyAjax; var AjaxParams; var pars = 'action=add&tablename=' + table + '&directory=' + directory ; var wantJSON = 0; if (nextPage){ wantJSON = 1; pars += '&json=1'; } else { pars += '&page=0'; pars += '&filter=' + inputValue(table + '_filter'); pars += '&pageSize=' + inputValue(table + '_pageSize'); } if (extraParams) { selectCallerId = extraParams['selectCallerId']; selectForeignField = extraParams['selectForeignField']; nextPageContextName = extraParams['nextPageContextName']; } cleanError(table); if (fields) { pars += '&' + encodeFields(table, fields); } if (selectCallerId) { pars += '&selectCallerId=' + selectCallerId; } AjaxParams = { method: 'post', parameters: pars, evalScripts: true, onComplete: function(t) { stripe('dataTable', 'even', 'odd'); completedAjaxRequest(); if (!wantJSON) { Modalbox.resizeToContent(); return; } var json = t.responseText.evalJSON(true); if (!json.success) { var error = json.error; if (!error) { error = 'Unknown error'; } setError(table, error); restoreHidden('buttons_' + table, table); Modalbox.resizeToContent(); return; } if (nextPage && nextPageContextName) { var nextDirectory = json.directory; var rowId = json.rowId; if (selectCallerId && selectForeignField){ var printableValue = json.callParams[selectForeignField]; addSelectChoice(selectCallerId, rowId, printableValue, true); // hide 'Add a new one' element var newLink = document.getElementById(selectCallerId + '_empty'); if (newLink) { newLink.style.display = 'none'; document.getElementById(selectCallerId).style.display ='inline'; } } if (rowId && directory) { var nameParts = nextPageContextName.split('/'); var baseUrl = '/zentyal/' + nameParts[1] + '/'; baseUrl += 'ModalController/' + nameParts[2]; var newDirectory = nextDirectory + '/keys/' + rowId + '/' + nextPage; var nextPageUrl = baseUrl; nextPageUrl += '?directory=' + newDirectory; nextPageUrl += '&firstShow=0'; nextPageUrl += '&action=viewAndAdd'; nextPageUrl += "&selectCallerId=" + selectCallerId; Modalbox.show(nextPageUrl, { transitions: false, overlayClose : false } ); } else { setError(table, 'Cannot get next page URL'); restoreHidden('buttons_' + table, table); Modalbox.resizeToContent(); } return; } //sucesss and not next page restoreHidden('buttons_' + table, table); Modalbox.resizeToContent(); }, onFailure: function(t) { restoreHidden('buttons_' + table, table); Modalbox.resizeToContent(); } }; if (nextPage) { MyAjax = new Ajax.Request( url, AjaxParams ); } else { MyAjax = new Ajax.Updater( { success: table, failure: 'error_' + table }, url, AjaxParams ); } setLoading('buttons_' + table, table, true); } function addNewRow(url, table, fields, directory) { var pars = 'action=add&tablename=' + table + '&directory=' + directory + '&'; pars += '&page=0'; pars += '&filter=' + inputValue(table + '_filter'); pars += '&pageSize=' + inputValue(table + '_pageSize'); cleanError(table); if (fields) pars += '&' + encodeFields(table, fields); var MyAjax = new Ajax.Updater( { success: table, failure: 'error_' + table }, url, { method: 'post', parameters: pars, evalScripts: true, onComplete: function(t) { stripe('dataTable', 'even', 'odd'); completedAjaxRequest(); }, onFailure: function(t) { restoreHidden('buttons_' + table, table); } } ); setLoading('buttons_' + table, table, true); } function changeRow(url, table, fields, directory, id, page, force, resizeModalbox, extraParams) { var pars = '&action=edit&tablename=' + table + '&directory=' + directory + '&id=' + id + '&'; if ( page != undefined ) pars += '&page=' + page; pars += '&filter=' + inputValue(table + '_filter'); pars += '&pageSize=' + inputValue(table + '_pageSize'); // If force parameter is ready, show it if ( force ) pars += '&force=1'; cleanError(table); if (fields) { pars += '&' + encodeFields(table, fields); } for (name in extraParams) { pars += '&' + name + '=' + extraParams[name]; } var MyAjax = new Ajax.Updater( { success: table, failure: 'error_' + table }, url, { method: 'post', parameters: pars, evalScripts: true, onComplete: function(t) { highlightRow( id, false); stripe('dataTable', 'even', 'odd'); if (resizeModalbox) { Modalbox.resizeToContent(); } }, onFailure: function(t) { restoreHidden('buttons_' + table, table ); if (resizeModalbox) { Modalbox.resizeToContent(); } } }); setLoading('buttons_' + table, table, true); } /* Function: actionClicked Callback function when an action on the table is clicked Parameters: url - the CGI URL to call to do the action table - the table's name action - the action to do (move, del) rowId - the affected row identifier paramsAction - an string with the parameters related to the action E.g.: param1=value1¶m2=value2 *(Optional)* directory - the GConf directory where table is stored */ function actionClicked(url, table, action, rowId, paramsAction, directory, page, extraParams) { var pars = '&action=' + action + '&id=' + rowId; if ( paramsAction != '' ) { pars += '&' + paramsAction; } if ( page != undefined ) { pars += '&page=' + page; } pars += '&filter=' + inputValue(table + '_filter'); pars += '&pageSize=' + inputValue(table + '_pageSize'); pars += '&directory=' + directory + '&tablename=' + table; for (name in extraParams) { pars += '&' + name + '=' + extraParams[name]; } cleanError(table); var MyAjax = new Ajax.Updater( { success: table, failure: 'error_' + table }, url, { method: 'post', parameters: pars, evalScripts: true, onComplete: function(t) { stripe('dataTable', 'even', 'odd'); if ( action == 'del' ) { delete savedElements['actionsCell_' + rowId]; } }, onFailure: function(t) { restoreHidden('actionsCell_' + rowId, table); } }); if ( action == 'del' ) { setLoading('actionsCell_' + rowId, table, true); } else if ( action == 'move' ) { setLoading('actionsCell_' + rowId, table); } } function customActionClicked(action, url, table, fields, directory, id, page) { var pars = '&action=' + action; pars += '&tablename=' + table; pars += '&directory=' + directory; pars += '&id=' + id; if (page) pars += '&page=' + page; pars += '&filter=' + inputValue(table + '_filter'); pars += '&pageSize=' + inputValue(table + '_pageSize'); cleanError(table); if (fields) pars += '&' + encodeFields(table, fields); var MyAjax = new Ajax.Updater( { success: table, failure: 'error_' + table }, url, { method: 'post', parameters: pars, evalScripts: true, onComplete: function(t) { highlightRow(id, false); stripe('dataTable', 'even', 'odd'); $$('.customActions').each(function(e) { restoreHidden(e.identify(), table); }); }, onFailure: function(t) { $$('.customActions').each(function(e) { restoreHidden(e.identify(), table); }); } } ); $$('.customActions').each(function(e) { setLoading(e.identify(), table, true); }); } function changeView(url, table, directory, action, id, page, isFilter) { var pars = 'action=' + action + '&tablename=' + table + '&directory=' + directory + '&editid=' + id; pars += '&filter=' + inputValue(table + '_filter'); pars += '&pageSize=' + inputValue(table + '_pageSize'); pars += '&page=' + page; cleanError(table); var MyAjax = new Ajax.Updater( { success: table, failure: 'error_' + table }, url, { method: 'post', parameters: pars, evalScripts: true, onComplete: function(t) { // Highlight the element if (id != undefined) { highlightRow(id, true); } // Stripe again the table stripe('dataTable', 'even', 'odd'); if ( action == 'changeEdit' ) { restoreHidden('actionsCell_' + id, table); } completedAjaxRequest(); }, onFailure: function(t) { if ( action == 'changeAdd' ) { restoreHidden('creatingForm_' + table, table); } else if ( action == 'changeList' ) { if (! isFilter ) { restoreHidden('buttons_' + table, table); } } else if ( action == 'changeEdit' ) { restoreHidden('actionsCell_' + id, table); } else if ( (action == 'checkboxSetAll') || (action == 'checkboxUnsetAll') ) { var selector = 'input[id^="' + table + '_' + id + '_"]'; var checkboxes = $$(selector); checkboxes.each(function(e) { restoreHidden(e.parentNode.identify(), table); }); restoreHidden(table + '_' + id + '_div_CheckAll', table); } } }); if ( action == 'changeAdd' ) { setLoading('creatingForm_' + table, table, true); } else if ( action == 'changeList' ) { if ( ! isFilter ) { setLoading('buttons_' + table, table, true); } } else if ( action == 'changeEdit' ) { setLoading('actionsCell_' + id, table, true); } else if ( (action == 'checkboxSetAll') || (action == 'checkboxUnsetAll') ) { var selector = 'input[id^="' + table + '_' + id + '_"]'; var checkboxes = $$(selector); checkboxes.each(function(e) { setLoading(e.parentNode.identify(), table, true); }); setLoading(table + '_' + id + '_div_CheckAll', table, true); } } function modalChangeView(url, table, directory, action, id, extraParams) { var title = ''; var page = 1; var firstShow = false; var isFilter= false; var pars = 'action=' + action + '&tablename=' + table + '&directory=' + directory + '&editid=' + id; for (name in extraParams) { if (name == 'title') { title = extraParams['title']; } else if (name == 'page') { page = extraParams['page']; } else if (name == 'firstShow') { firstShow = extraParams['firstShow']; pars += '&firstShow=' + extraParams['firstShow']; } else { pars += '&' + name + '=' + extraParams[name]; } } if (! firstShow ) { pars += '&firstShow=0'; } pars += '&filter=' + inputValue(table + '_filter'); pars += '&pageSize=' + inputValue(table + '_pageSize'); pars += '&page=' + page; if (firstShow) { Modalbox.show(url, {title: title, params: pars, transitions: false, overlayClose: false, afterLoad: function() { // fudge for pootle bug var badText = document.getElementById('ServiceTable_modal_name'); if (badText){ badText.value = ''; } } } ); } else { cleanError(table); var MyAjax = new Ajax.Updater( { success: table, failure: 'error_' + table }, url, { method: 'post', parameters: pars, evalScripts: true, onComplete: function(t) { // Highlight the element if (id != undefined) { highlightRow(id, true); } // Stripe again the table stripe('dataTable', 'even', 'odd'); if ( action == 'changeEdit' ) { restoreHidden('actionsCell_' + id, table); } completedAjaxRequest(); Modalbox.resizeToContent(); }, onFailure: function(t) { if ( action == 'changeAdd' ) { restoreHidden('creatingForm_' + table, table); } else if ( action == 'changeList' ) { if (! isFilter ) { restoreHidden('buttons_' + table, table); } } else if ( action == 'changeEdit' ) { restoreHidden('actionsCell_' + id, table); } Modalbox.resizeToContent(); } }); if ( action == 'changeAdd' ) { setLoading('creatingForm_' + table, table, true); } else if ( action == 'changeList' ) { if ( ! isFilter ) { setLoading('buttons_' + table, table, true); } } else if ( action == 'changeEdit' ) { setLoading('actionsCell_' + id, table, true); } } } /* Function: hangTable Hang a table under the given identifier via AJAX request replacing all HTML content. The parameters to the HTTP request are passed by an HTML form. Parameters: successId - div identifier where the new table will be on on success errorId - div identifier url - the URL where the CGI which generates the HTML is placed formId - form identifier which has the parameters to pass to the CGI loadingId - String element identifier that it will substitute by the loading image *(Optional)* Default: 'loadingTable' */ function hangTable(successId, errorId, url, formId, loadingId) { // Cleaning manually $(errorId).innerHTML = ""; if ( ! loadingId ) { loadingId = 'loadingTable'; } var ajaxUpdate = new Ajax.Updater( { success: successId, failure: errorId }, url, { method: 'post', parameters: Form.serialize(formId, true), // The parameters are taken from the form asynchronous: true, evalScripts: true, onComplete: function(t) { stripe('dataTable', 'even', 'odd'); completedAjaxRequest(); }, onFailure: function(t) { restoreHidden(loadingId); } } ); setLoading(loadingId); } /* Function: selectComponentToHang Call to a component to be hang in a select entry Parameters: successId - div identifier where the new table will be on on success errorId - div identifier formId - form identifier which has the parameters to pass to the CGI urls - associative array which contains tthe URL where the CGI which generates the HTML is placed loadingId - String element identifier that it will substitute by the loading image *(Optional)* Default: 'loadingTable' */ function selectComponentToHang(successId, errorId, formId, urls, loadingId) { // Cleaning manually $(errorId).innerHTML = ""; if ( ! loadingId ) { loadingId = 'loadingTable'; } // Currently buggy, since select elements are not inputs // var selects = $(formId).getInputs('select'); var children = $(formId).immediateDescendants(); var select; for ( var i = 0; i < children.length; i++) { if ( children[i].tagName == 'SELECT' ) { select = children[i]; } } var url = urls[ $F(select.id) ]; var pars = "action=view"; // FIXME: maybe the directory could be sent var ajaxUpdate = new Ajax.Updater( { success: successId, failure: errorId }, url, { method: 'post', parameters: pars, asynchronous: true, evalScripts: true, onSuccess: function(t) { restoreHidden(loadingId); }, onFailure: function(t) { restoreHidden(loadingId); } } ); setLoading(loadingId); } /* Function: showSelected Show the HTML setter selected in select Parameters: selectElement - HTMLSelectElement */ function showSelected (selectElement) { var selectedValue = $F(selectElement); var options = selectElement.options; for (var i = 0; i < options.length; i++) { var option = options[i].value; var childId = selectElement.id + "_" + option + "_container"; if (selectedValue == option) { show(childId); } else { hide(childId); } } } /* Function: showPort Show port if it's necessary given a protocol Parameters: protocolSelectId - the select identifier which the protocol is chosen portId - the identifier where port is going to be set protocols - the list of protocols which need a port to be set */ function showPort(protocolSelectId, portId, protocols) { var selectedIdx = $(protocolSelectId).selectedIndex; var selectedValue = $(protocolSelectId).options[selectedIdx].value; var found = false; // Search the selected value into the array to know if it needs a port or not for ( var idx = 0; idx < protocols.length && ! found; idx++) { if ( selectedValue == protocols[idx] ) { found = true; show(portId); } } if (! found) { hide(portId); } } /* TODO: showPortRange and showPort do things in common like showing/hiding elments depending on which value is selected elsewhere. We should refactor this and provide a generic function to do that. Logic should come from model and translated in javascript. */ /* Function: showPortRange Show/Hide elements in PortRange view Parameters: id - the select identifier which the protocol is chosen */ function showPortRange(id) { var selectId = id + "_range_type"; var selectedIdx = $(selectId).selectedIndex; var selectedValue = $(selectId).options[selectedIdx].value; if ( selectedValue == "range") { show(id + "_range"); hide(id + "_single"); $(id + "_single_port").value = ""; } else if (selectedValue == "single") { hide(id + "_range"); show(id + "_single"); $(id + "_to_port").value = ""; $(id + "_from_port").value = ""; } else { hide(id + "_range"); hide(id + "_single"); $(id + "_to_port").value = ""; $(id + "_from_port").value = ""; $(id + "_single_port").value = ""; } } /* Function: setLoading Set the loading icon on the given HTML element erasing everything which were there. If modelName is set, isSaved parameter can be used Parameters: elementId - the element identifier modelName - the model name to distinguish among hiddenDiv tags *(Optional)* isSaved - boolean to indicate if the inner HTML should be saved at *hiddenDiv_* in order to be rescued afterwards *(Optional)* */ var savedElements = {}; function setLoading (elementId, modelName, isSaved) { var element = $(elementId); if (isSaved) { savedElements[elementId] = element.innerHTML; } element.innerHTML = 'loading...'; } /* Function: setDone Set the done icon (a tick) on the given HTML element erasing everything which were there. Parameters: elementId - String the element identifier */ function setDone (elementId) { $(elementId).innerHTML = ""; } /* Function: restoreHidden Restore HTML stored in *hiddenDiv* Parameters: elementId - the element identifier where to restore the HTML hidden modelName - the model name to distinguish among hiddenDiv tags */ function restoreHidden (elementId, modelName) { if (savedElements[elementId] != null) { $(elementId).innerHTML = savedElements[elementId]; } else { $(elementId).innerHTML = ''; } } function restoreHiddenElement (element) { var elementId = element.id; if (savedElements[elementId] != null) { element.innerHTML = savedElements[elementId]; } else { element.innerHTML = ''; } } /* Function: disableInput Disable all inputs attached as children to the given element Parameters: elementId - the element identifier where all input elements hang */ function disableInput(elementId) { var children = $(elementId).childNodes; for (var idx = 0; idx < children.length; idx++) { // I'd like to use constant but in IE 6 simply they don't exist node = children[idx]; if ( node.nodeType == 1 /* Node.ELEMENT_NODE */ ) { // if ( typeof node == "HTMLInputElement" ) { node.disable = true; //} } } } /* Function: highlightRow Enable/Disable a hightlight over an element on the table Parameters: elementId - the row identifier to highlight enable - if enables/disables the highlight *(Optional)* Default value: true */ function highlightRow(elementId, enable) { // If enable has value null or undefined if ( enable == null) { enable = true; } if (enable) { // Highlight the element putting the CSS class which does so Element.addClassName(elementId, "highlight"); } else { Element.removeClassName(elementId, "highlight"); } } /* Function: inputValue Return an input value. It firstly checks using $() if the id exits Parameters: elementId - the input element to fetch the value from Returns: input value if it exits, otherwise empty string */ function inputValue(elementId) { var $element = $(elementId); if ($element) { return $element.getValue(); } else { return ''; } } /* Function: markFileToRemove This function is used along with the File view and setter to mark a file to be removed Parameters: elementId - a EBox::Types::File id */ function markFileToRemove(id) { $(id + '_remove').value = "1"; hide(id + '_current'); } /* Function: sendInPlaceBooleanValue This function is used to send the value change of a boolean type with in-place edtion Parameters: controller - url model - model id - row id dir - conf dir field - field name element - HTML element */ function sendInPlaceBooleanValue(controller, model, id, dir, field, element) { startAjaxRequest(); cleanError(model); var parameters = new Hash(); parameters.set('action', 'editBoolean'); parameters.set('model', model); parameters.set('dir', dir); parameters.set('field', field); if ($F(element) == 'on') { parameters.set('value', 1); } parameters.set('id', id); hide(element.id); setLoading(element.id + '_loading'); var MyAjax = new Ajax.Updater( { failure: 'error_' + model }, controller, { method: 'post', parameters: parameters, onFailure: function(t) { completedAjaxRequest(); show(element.id); $(element.id + '_loading').innerHTML = ''; element.checked = ! element.checked; }, onSuccess: function(t) { eval(t.responseText); completedAjaxRequest(); show(element.id); $(element.id + '_loading').innerHTML = ''; } }); } /* Function: startAjaxRequest This function is used to mark we start an ajax request. This is used to help test using selenium, it modifies a dom element -request_cookie- to be able to know when an ajax request starts and stops. */ function startAjaxRequest() { $('ajax_request_cookie').value = 1; } /* Function: completedAjaxRequest This function is used to mark we finished an ajax request. This is used to help test using selenium, it modifies a dom element -request_cookie- to be able to know when an ajax request starts and stops. */ function completedAjaxRequest() { $('ajax_request_cookie').value = 0; } function addSelectChoice(id, value, printableValue, selected) { var selectControl = document.getElementById(id); if (!selectControl) { return; } var newChoice = new Option(printableValue, value); selectControl.options.add(newChoice); if (selected) { selectControl.options.selectedIndex = selectControl.options.length -1; } } function removeSelectChoice(id, value, selectedIndex) { var selectControl = document.getElementById(id); if (!selectControl) { return; } var options = selectControl.options; for(var i=0;i< options.length;i++){ if(options[i].value==value){ options[i] = null; break; } } if (selectedIndex) { options.selectedIndex = selectedIndex; } } function checkAllControlValue(url, table, directory, controlId, field) { var pars = 'action=checkAllControlValue&tablename=' + table + '&directory=' + directory; pars += '&controlId=' + controlId + '&field=' + field; pars += '&json=1'; AjaxParams = { method: 'post', parameters: pars, evalScripts: true, onComplete: function(t) { completedAjaxRequest(); var json = t.responseText.evalJSON(true); $(controlId).checked = json.success; } }; MyAjax = new Ajax.Request( url, AjaxParams ); } function confirmationDialog(url, table, directory, actionToConfirm, elements) { var wantDialog = true; var dialogTitle = null; var dialogMsg = null; var pars = 'action=confirmationDialog' + '&tablename=' + table + '&directory=' + directory; pars +='&actionToConfirm=' + actionToConfirm; for (var i=0; i < elements.length; i++) { var name = elements[i]; var id = table + '_' + name; var el = $(id); pars +='&'+ name + '='; pars +=el.value; } var request = new Ajax.Request(url, { method: 'post', parameters: pars, asynchronous: false, onSuccess: function (t) { var json = t.responseText.evalJSON(true); if (json.wantDialog) { dialogTitle = json.title; dialogMsg = json.message; } else { wantDialog = false; } }, onFailure: function(t) { dialogTitle = ''; dialogMsg = 'Are you sure?'; } } ); return { 'wantDialog' : wantDialog, 'title': dialogTitle, 'message': dialogMsg }; } function showConfirmationDialog(params, acceptJS) { var modalboxHtml = "

    " + params.message + '

    '; modalboxHtml += "

    "; Modalbox.show(modalboxHtml, {'title' : params.title }); } // Detect session loss on ajax request: Ajax.Responders.register({ onComplete: function(x,response) { if (response.status == 403) { location.reload(true); } } }); zentyal-core-2.3.21+quantal1/www/js/image-helper.js0000664000000000000000000000470012017102272016755 0ustar // Copyright (C) 2004-2012 eBox Technologies S.L. licensed under the GPLv2 /* This bunch of function are JS function to improve the development of /ajax/image.mas template. */ function reloadGraph(url, target, tableName, directory, action) { var pars = 'action=' + action + '&tablename=' + target + '&directory=' + directory; //+ '&editid=' + id; cleanError(tableName); var MyAjax = new Ajax.Updater( { success: target, failure: target, }, url, { method: 'post', parameters: pars, asyncrhonous: false, evalScripts: true, onComplete: function(t) { stripe('dataTable', 'even', 'odd'); }, }); } function switchImg(hiddenPrefix, activePrefix) { var hiddenImgId = hiddenPrefix + 'Img'; var hiddenDivId = hiddenPrefix + 'Div'; var activeImgId = activePrefix + 'Img'; var activeDivId = activePrefix + 'Div'; var oldHiddenImg = $(hiddenImgId); var oldHiddenDiv = $(hiddenDivId); var oldActiveImg = $(activeImgId); var oldActiveDiv = $(activeDivId); oldHiddenImg.id = activeImgId; oldHiddenDiv.id = activeDivId; oldActiveImg.id = hiddenImgId; oldActiveDiv.id = hiddenDivId; Element.setStyle( oldActiveDiv, { position: 'absolute', display: 'none'}); Element.setStyle( oldHiddenDiv, { position: 'absolute', display : 'block' }); Element.setStyle( oldActiveImg, { position: 'absolute', display: 'none'}); Element.setStyle( oldHiddenImg, { position: 'absolute', display: 'block' }); var hiddenImgOnload = oldHiddenDiv.onload; oldHiddenDiv.onload = ''; // oldActiveDiv = hiddenImgOnload; oldActiveDiv.onload = hiddenImgOnload; // Set the correct heigth to the white background image var imgHeight = oldHiddenImg.getHeight(); var backgroundImg = document.createElement('IMG'); backgroundImg.setAttribute( 'height', imgHeight); backgroundImg.setAttribute( 'src' , '/data/images/bkgwhite.png'); backgroundImg.setAttribute( 'id' , 'whiteBkgImg'); var imgElements = oldHiddenDiv.parentNode.getElementsByTagName('img'); // If only contains the hidden and active images appended if ( imgElements.length < 3 ) { oldHiddenDiv.parentNode.appendChild(backgroundImg); } else { oldHiddenDiv.parentNode.replaceChild(backgroundImg, imgElements[imgElements.length - 1]); } } zentyal-core-2.3.21+quantal1/www/js/base64.js0000664000000000000000000000770612017102272015513 0ustar /* * Caudium - An extensible World Wide Web server * Copyright C 2002 The Caudium Group * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of the * License, or (at your option) any later version. * * This program 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 * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. * */ /* * base64.js - a JavaScript implementation of the base64 algorithm, * (mostly) as defined in RFC 2045. * * This is a direct JavaScript reimplementation of the original C code * as found in the Exim mail transport agent, by Philip Hazel. * * $Id: base64.js,v 1.7 2002/07/16 17:21:23 kazmer Exp $ * */ function encode_base64( what ) { var base64_encodetable = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"; var result = ""; var len = what.length; var x, y; var ptr = 0; while( len-- > 0 ) { x = what.charCodeAt( ptr++ ); result += base64_encodetable.charAt( ( x >> 2 ) & 63 ); if( len-- <= 0 ) { result += base64_encodetable.charAt( ( x << 4 ) & 63 ); result += "=="; break; } y = what.charCodeAt( ptr++ ); result += base64_encodetable.charAt( ( ( x << 4 ) | ( ( y >> 4 ) & 15 ) ) & 63 ); if ( len-- <= 0 ) { result += base64_encodetable.charAt( ( y << 2 ) & 63 ); result += "="; break; } x = what.charCodeAt( ptr++ ); result += base64_encodetable.charAt( ( ( y << 2 ) | ( ( x >> 6 ) & 3 ) ) & 63 ); result += base64_encodetable.charAt( x & 63 ); } return result; } function decode_base64( what ) { var base64_decodetable = new Array ( 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 255, 62, 255, 255, 255, 63, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 255, 255, 255, 255, 255, 255, 255, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 255, 255, 255, 255, 255, 255, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 255, 255, 255, 255, 255 ); var result = ""; var len = what.length; var x, y; var ptr = 0; while( !isNaN( x = what.charCodeAt( ptr++ ) ) ) { if( x == 13 || x == 10 ) continue; if( ( x > 127 ) || (( x = base64_decodetable[x] ) == 255) ) return false; if( ( isNaN( y = what.charCodeAt( ptr++ ) ) ) || (( y = base64_decodetable[y] ) == 255) ) return false; result += String.fromCharCode( (x << 2) | (y >> 4) ); if( (x = what.charCodeAt( ptr++ )) == 61 ) { if( (what.charCodeAt( ptr++ ) != 61) || (!isNaN(what.charCodeAt( ptr ) ) ) ) return false; } else { if( ( x > 127 ) || (( x = base64_decodetable[x] ) == 255) ) return false; result += String.fromCharCode( (y << 4) | (x >> 2) ); if( (y = what.charCodeAt( ptr++ )) == 61 ) { if( !isNaN(what.charCodeAt( ptr ) ) ) return false; } else { if( (y > 127) || ((y = base64_decodetable[y]) == 255) ) return false; result += String.fromCharCode( (x << 6) | y ); } } } return result; } function wrap76( what ) { var result = ""; var i; for(i=0; i < what.length; i+=76) { result += what.substring(i, i+76) + String.fromCharCode(13) + String.fromCharCode(10); } return result; } zentyal-core-2.3.21+quantal1/www/js/modalbox-zentyal.js0000664000000000000000000000105012017102272017702 0ustar // This files modifies Modalbox to force zentyal styles Modalbox.show = Modalbox.show.wrap(function (origFunc) { var args = Array.prototype.slice.call(arguments, 1); origFunc.apply(Modalbox, args); Modalbox.MBwindow.addClassName('MB_dialog'); }); Modalbox._setPosition = function() { Modalbox.MBwindow.setStyle({'left' : 'auto'}); }; zentyal-core-2.3.21+quantal1/www/js/flotr.js0000664000000000000000000005266712017102272015563 0ustar //Flotr 0.1.0alpha Copyright (c) 2008 Bas Wenneker, , MIT License. var Flotr=(function(){var C=0;function L(M){return M.collect(function(N){return(N.data)?Object.clone(N):{data:N}})}function G(P,N){var M=N||{};for(var O in P){M[O]=(typeof (P[O])=="object"&&!(P[O].constructor==Array||P[O].constructor==RegExp))?G(P[O],N[O]):M[O]=P[O]}return M}function I(Q,P,M,N){var T=(M-P)/Q;var S=H(T);var O=T/S;var R=10;if(O<1.5){R=1}else{if(O<2.25){R=2}else{if(O<3){R=2.5}else{if(O<7.5){R=5}}}}if(R==2.5&&N==0){R=2}R*=S;return R}function E(M){return M.toString()}function F(M){return"("+M.x+", "+M.y+")"}function H(M){return Math.pow(10,Math.floor(Math.log(M)/Math.LN10))}function K(O){var M;if((M=/rgb\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*\)/.exec(O))){return new B(parseInt(M[1]),parseInt(M[2]),parseInt(M[3]))}if((M=/rgba\(\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]{1,3})\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(O))){return new B(parseInt(M[1]),parseInt(M[2]),parseInt(M[3]),parseFloat(M[4]))}if((M=/rgb\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*\)/.exec(O))){return new B(parseFloat(M[1])*2.55,parseFloat(M[2])*2.55,parseFloat(M[3])*2.55)}if((M=/rgba\(\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\%\s*,\s*([0-9]+(?:\.[0-9]+)?)\s*\)/.exec(O))){return new B(parseFloat(M[1])*2.55,parseFloat(M[2])*2.55,parseFloat(M[3])*2.55,parseFloat(M[4]))}if((M=/#([a-fA-F0-9]{2})([a-fA-F0-9]{2})([a-fA-F0-9]{2})/.exec(O))){return new B(parseInt(M[1],16),parseInt(M[2],16),parseInt(M[3],16))}if((M=/#([a-fA-F0-9])([a-fA-F0-9])([a-fA-F0-9])/.exec(O))){return new B(parseInt(M[1]+M[1],16),parseInt(M[2]+M[2],16),parseInt(M[3]+M[3],16))}var N=O.strip().toLowerCase();if(N=="transparent"){return new B(255,255,255,0)}M=D[N];return new B(M[0],M[1],M[2])}function A(N){var M;do{M=N.getStyle("background-color").toLowerCase();if(M!=""&&M!="transparent"){break}N=N.up(0)}while(N.nodeName.toLowerCase()!="body");if(M=="rgba(0, 0, 0, 0)"){return"transparent"}return M}function B(S,R,N,P){var Q=["r","g","b","a"];var M=4;while(-1<--M){this[Q[M]]=arguments[M]||((M==3)?1:0)}this.toString=function(){return(this.a>=1)?"rgb("+[this.r,this.g,this.b].join(",")+")":"rgba("+[this.r,this.g,this.b,this.a].join(",")+")"};this.scale=function(V,U,W,T){M=4;while(-1<--M){if(arguments[M]!=null){this[Q[M]]*=arguments[M]}}return this.normalize()};this.adjust=function(V,U,W,T){M=4;while(-1<--M){if(arguments[M]!=null){this[Q[M]]+=arguments[M]}}return this.normalize()};this.clone=function(){return new B(this.r,this.b,this.g,this.a)};var O=function(U,T,V){return Math.max(Math.min(U,V),T)};this.normalize=function(){this.r=O(parseInt(this.r),0,255);this.g=O(parseInt(this.g),0,255);this.b=O(parseInt(this.b),0,255);this.a=O(this.a,0,1);return this};this.normalize()}var D={aqua:[0,255,255],azure:[240,255,255],beige:[245,245,220],black:[0,0,0],blue:[0,0,255],brown:[165,42,42],cyan:[0,255,255],darkblue:[0,0,139],darkcyan:[0,139,139],darkgrey:[169,169,169],darkgreen:[0,100,0],darkkhaki:[189,183,107],darkmagenta:[139,0,139],darkolivegreen:[85,107,47],darkorange:[255,140,0],darkorchid:[153,50,204],darkred:[139,0,0],darksalmon:[233,150,122],darkviolet:[148,0,211],fuchsia:[255,0,255],gold:[255,215,0],green:[0,128,0],indigo:[75,0,130],khaki:[240,230,140],lightblue:[173,216,230],lightcyan:[224,255,255],lightgreen:[144,238,144],lightgrey:[211,211,211],lightpink:[255,182,193],lightyellow:[255,255,224],lime:[0,255,0],magenta:[255,0,255],maroon:[128,0,0],navy:[0,0,128],olive:[128,128,0],orange:[255,165,0],pink:[255,192,203],purple:[128,0,128],violet:[128,0,128],red:[255,0,0],silver:[192,192,192],white:[255,255,255],yellow:[255,255,0]};function J(y,AO,p){var o,S,AL,h,AS;var z="flotr-"+C++;var R=L(AO);var N=y;var u={},c={};var l={left:0,right:0,top:0,bottom:0};var AA=0;var d=0;var AH=0;var U=0;var P=0;var AD=0;var AC=0;var AB=0;g(p);k();AN();q();AK(u,o.xaxis);Z();AK(c,o.yaxis);m(u,o.xaxis);m(c,o.yaxis);Y();AP();AQ();this.getCanvas=function(){return S};this.getPlotOffset=function(){return l};this.clearSelection=M;this.setSelection=AF;function g(AV){o=G(AV,{colors:["#00A8F0","#C0D800","#cb4b4b","#4da74d","#9440ed"],legend:{show:true,noColumns:1,labelFormatter:null,labelBoxBorderColor:"#ccc",container:null,position:"ne",margin:5,backgroundColor:null,backgroundOpacity:0.85},xaxis:{ticks:null,noTicks:5,tickFormatter:E,tickDecimals:null,min:null,max:null,autoscaleMargin:0},yaxis:{ticks:null,noTicks:5,tickFormatter:E,tickDecimals:null,min:null,max:null,autoscaleMargin:0},points:{show:false,radius:3,lineWidth:2,fill:true,fillColor:"#ffffff"},lines:{show:false,lineWidth:2,fill:false,fillColor:null},bars:{show:false,lineWidth:2,barWidth:1,fill:true,fillColor:null},grid:{color:"#545454",backgroundColor:null,tickColor:"#dddddd",labelMargin:3},selection:{mode:null,color:"#B6D9FF",fps:10},mouse:{track:null,position:"se",trackFormatter:F,margin:3,color:"#ff3f19",trackDecimals:1,sensibility:2,radius:3},shadowSize:4});var Ah=R.length;var AT=[];var AZ=[];for(var Ac=0;Ac=o.colors.length){Aa=0;++AY}}var Ad=0;for(var AX=0,Ai;AX0){u.datamin=u.datamax=R[AV].data[0][0];c.datamin=c.datamax=R[AV].data[0][1];AY=true;break}}if(!AY){return }for(var AU=0;AUu.datamax){u.datamax=AT}}if(AZc.datamax){c.datamax=AZ}}}}}function AK(AW,AY){var AV=AY.min!=null?AY.min:AW.datamin;var AT=AY.max!=null?AY.max:AW.datamax;if(AT-AV==0){var AU=(AT==0)?1:0.01;AV-=AU;AT+=AU}AW.tickSize=I(AY.noTicks,AV,AT,AY.tickDecimals);var AX;if(AY.min==null){AX=AY.autoscaleMargin;if(AX!=0){AV-=AW.tickSize*AX;if(AV<0&&AW.datamin>=0){AV=0}AV=AW.tickSize*Math.floor(AV/AW.tickSize)}}if(AY.max==null){AX=AY.autoscaleMargin;if(AX!=0){AT+=AW.tickSize*AX;if(AT>0&&AW.datamax<=0){AT=0}AT=AW.tickSize*Math.ceil(AT/AW.tickSize)}}AW.min=AV;AW.max=AT}function Z(){if(o.xaxis.max==null){var AU=u.max;for(var AT=0;ATAU){AU=u.max+R[AT].bars.barWidth}}u.max=AU}}function m(AV,AW){AV.ticks=[];if(AW.ticks){var AZ=AW.ticks;if(Object.isFunction(AZ)){AZ=AZ({min:AV.min,max:AV.max})}for(var AX=0,Aa,AY;AX1)?Ab[1]:AW.tickFormatter(Aa)}else{Aa=Ab;AY=AW.tickFormatter(Aa)}AV.ticks[AX]={v:Aa,label:AY}}}else{var AT=AV.tickSize*Math.ceil(AV.min/AV.tickSize);for(AX=0;AT+AX*AV.tickSize<=AV.max;++AX){Aa=AT+AX*AV.tickSize;var AU=AW.tickDecimals;if(AU==null){AU=1-Math.floor(Math.log(AV.tickSize)/Math.LN10)}if(AU<0){AU=0}Aa=Aa.toFixed(AU);AV.ticks.push({v:Aa,label:AW.tickFormatter(Aa)})}}}function Y(){var AX="";for(var AW=0;AWAX.length){AX=c.ticks[AW].label}}var AT=N.insert('
    '+AX+"
    ").down(0).next(1);AA=AT.getWidth();d=AT.getHeight();AT.remove();var AY=2;if(o.points.show){AY=Math.max(AY,o.points.radius+o.points.lineWidth/2)}for(var AV=0;AV';for(var AV=0,AW=null;AV'+AW.label+"
    "}for(var AT=0,AW=null;AT'+AW.label+"
    "}AX+="";N.insert(AX)}function AI(AT){if(AT.lines.show||(!AT.bars.show&&!AT.points.show)){i(AT)}if(AT.bars.show){v(AT)}if(AT.points.show){w(AT)}}function i(AV){function AU(Ad,Ac){if(Ad.length<2){return }var Ab=AE(Ad[0][0]),Aa=j(Ad[0][1])+Ac;h.beginPath();h.moveTo(Ab,Aa);for(var Ae=0;Ae=Af&&Ag>c.max){if(Af>c.max){continue}AZ=(c.max-Ag)/(Af-Ag)*(AY-AZ)+AZ;Ag=c.max}else{if(Af>=Ag&&Af>c.max){if(Ag>c.max){continue}AY=(c.max-Ag)/(Af-Ag)*(AY-AZ)+AZ;Af=c.max}}if(AZ<=AY&&AZ=AY&&AZ>u.max){if(AY>u.max){continue}Ag=(u.max-AZ)/(AY-AZ)*(Af-Ag)+Ag;AZ=u.max}else{if(AY>=AZ&&AY>u.max){if(AZ>u.max){continue}Af=(u.max-AZ)/(AY-AZ)*(Af-Ag)+Ag;AY=u.max}}if(Ab!=AE(AZ)||Aa!=j(Ag)+Ac){h.moveTo(AE(AZ),j(Ag)+Ac)}Ab=AE(AY);Aa=j(Af)+Ac;h.lineTo(Ab,Aa)}h.stroke()}function AW(Ac){if(Ac.length<2){return }var AY=Math.min(Math.max(0,c.min),c.max);var Ah,Aa=0;var Ae=true;h.beginPath();for(var Ad=0;Ad=AZ&&Ab>u.max){if(AZ>u.max){continue}Ai=(u.max-Ab)/(AZ-Ab)*(Ag-Ai)+Ai;Ab=u.max}else{if(AZ>=Ab&&AZ>u.max){if(Ab>u.max){continue}Ag=(u.max-Ab)/(AZ-Ab)*(Ag-Ai)+Ai;AZ=u.max}}if(Ae){h.moveTo(AE(Ab),j(AY));Ae=false}if(Ai>=c.max&&Ag>=c.max){h.lineTo(AE(Ab),j(c.max));h.lineTo(AE(AZ),j(c.max));continue}else{if(Ai<=c.min&&Ag<=c.min){h.lineTo(AE(Ab),j(c.min));h.lineTo(AE(AZ),j(c.min));continue}}var Aj=Ab,Af=AZ;if(Ai<=Ag&&Ai=c.min){Ab=(c.min-Ai)/(Ag-Ai)*(AZ-Ab)+Ab;Ai=c.min}else{if(Ag<=Ai&&Ag=c.min){AZ=(c.min-Ai)/(Ag-Ai)*(AZ-Ab)+Ab;Ag=c.min}}if(Ai>=Ag&&Ai>c.max&&Ag<=c.max){Ab=(c.max-Ai)/(Ag-Ai)*(AZ-Ab)+Ab;Ai=c.max}else{if(Ag>=Ai&&Ag>c.max&&Ai<=c.max){AZ=(c.max-Ai)/(Ag-Ai)*(AZ-Ab)+Ab;Ag=c.max}}if(Ab!=Aj){Ah=(Ai<=c.min)?Ah=c.min:c.max;h.lineTo(AE(Aj),j(Ah));h.lineTo(AE(Ab),j(Ah))}h.lineTo(AE(Ab),j(Ai));h.lineTo(AE(AZ),j(Ag));if(AZ!=Af){Ah=(Ag<=c.min)?c.min:c.max;h.lineTo(AE(Af),j(Ah));h.lineTo(AE(AZ),j(Ah))}Aa=Math.max(AZ,Af)}h.lineTo(AE(Aa),j(AY));h.closePath();h.fill()}h.save();h.translate(l.left,l.top);h.lineJoin="round";var AX=AV.lines.lineWidth;var AT=AV.shadowSize;if(AT>0){h.lineWidth=AT/2;h.strokeStyle="rgba(0,0,0,0.1)";AU(AV.data,AX/2+AT/2+h.lineWidth/2);h.lineWidth=AT/2;h.strokeStyle="rgba(0,0,0,0.2)";AU(AV.data,AX/2+h.lineWidth/2)}h.lineWidth=AX;h.strokeStyle=AV.color;if(AV.lines.fill){h.fillStyle=AV.lines.fillColor!=null?AV.lines.fillColor:K(AV.color).scale(null,null,null,0.4).toString();AW(AV.data,0)}AU(AV.data,0);h.restore()}function w(AU){function AX(Ab,AZ,Ac){for(var Aa=0;Aau.max||Adc.max){continue}h.beginPath();h.arc(AE(AY),j(Ad),AZ,0,2*Math.PI,true);if(Ac){h.fill()}h.stroke()}}function AW(Ab,Ac,AZ){for(var Aa=0;Aau.max||Adc.max){continue}h.beginPath();h.arc(AE(AY),j(Ad)+Ac,AZ,0,Math.PI,false);h.stroke()}}h.save();h.translate(l.left,l.top);var AV=AU.lines.lineWidth;var AT=AU.shadowSize;if(AT>0){h.lineWidth=AT/2;h.strokeStyle="rgba(0,0,0,0.1)";AW(AU.data,AT/2+h.lineWidth/2,AU.points.radius);h.lineWidth=AT/2;h.strokeStyle="rgba(0,0,0,0.2)";AW(AU.data,h.lineWidth/2,AU.points.radius)}h.lineWidth=AU.points.lineWidth;h.strokeStyle=AU.color;h.fillStyle=AU.points.fillColor!=null?AU.points.fillColor:AU.color;AX(AU.data,AU.points.radius,AU.points.fill);h.restore()}function v(AU){function AT(Ab,Ak,AZ,Aj){if(Ab.length<2){return }for(var Ac=0;Acu.max||Aec.max){continue}if(AYu.max){Ah=u.max;Ad=false}if(AXc.max){Ae=c.max;Aa=false}if(Aj){h.beginPath();h.moveTo(AE(AY),j(AX)+AZ);h.lineTo(AE(AY),j(Ae)+AZ);h.lineTo(AE(Ah),j(Ae)+AZ);h.lineTo(AE(Ah),j(AX)+AZ);h.fill()}if(Ai||Ad||Aa){h.beginPath();h.moveTo(AE(AY),j(AX)+AZ);if(Ai){h.lineTo(AE(AY),j(Ae)+AZ)}else{h.moveTo(AE(AY),j(Ae)+AZ)}if(Aa){h.lineTo(AE(Ah),j(Ae)+AZ)}else{h.moveTo(AE(Ah),j(Ae)+AZ)}if(Ad){h.lineTo(AE(Ah),j(AX)+AZ)}else{h.moveTo(AE(Ah),j(AX)+AZ)}h.stroke()}}}h.save();h.translate(l.left,l.top);h.lineJoin="round";var AW=AU.bars.barWidth;var AV=Math.min(AU.bars.lineWidth,AW);h.lineWidth=AV;h.strokeStyle=AU.color;if(AU.bars.fill){h.fillStyle=AU.bars.fillColor!=null?AU.bars.fillColor:K(AU.color).scale(null,null,null,0.4).toString()}AT(AU.data,AW,0,AU.bars.fill);h.restore()}function AQ(){if(!o.legend.show){return }var Aa=[];var AY=false;for(var AX=0;AX":"");AY=true}var Ac=R[AX].label;if(o.legend.labelFormatter!=null){Ac=o.legend.labelFormatter(Ac)}Aa.push('
    '+Ac+"")}if(AY){Aa.push("")}if(Aa.length>0){var Ad=''+Aa.join("")+"
    ";if(o.legend.container!=null){$(o.legend.container).update(Ad)}else{var Ab="";var AU=o.legend.position,AV=o.legend.margin;if(AU.charAt(0)=="n"){Ab+="top:"+(AV+l.top)+"px;"}else{if(AU.charAt(0)=="s"){Ab+="bottom:"+(AV+l.bottom)+"px;"}}if(AU.charAt(1)=="e"){Ab+="right:"+(AV+l.right)+"px;"}else{if(AU.charAt(1)=="w"){Ab+="left:"+(AV+l.bottom)+"px;"}}var AT=N.insert('
    '+Ad+"
    ").getElementsBySelector("div.flotr-legend").first();if(o.legend.backgroundOpacity!=0){var AZ=o.legend.backgroundColor;if(AZ==null){var AW=(o.grid.backgroundColor!=null)?o.grid.backgroundColor:A(AT);AZ=K(AW).adjust(null,null,null,1).toString()}N.insert('
    ').select("div.flotr-legend-bg").first().setStyle({opacity:o.legend.backgroundOpacity})}}}}var AG={pageX:null,pageY:null};var b={first:{x:-1,y:-1},second:{x:-1,y:-1}};var T=null;var x=null;var t=false;var Q=null;function f(AT){if(t){t=false;return }var AU=AL.cumulativeOffset();N.fire("flotr:click",[{x:u.min+(AT.pageX-AU.left-l.left)/AC,y:c.max-(AT.pageY-AU.top-l.top)/AB}])}function e(AU){if(AU.pageX==null&&AU.clientX!=null){var AX=document.documentElement,AT=document.body;AG.pageX=AU.clientX+(AX&&AX.scrollLeft||AT.scrollLeft||0);AG.pageY=AU.clientY+(AX&&AX.scrollTop||AT.scrollTop||0)}else{AG.pageX=AU.pageX;AG.pageY=AU.pageY}var AV=AL.cumulativeOffset();var AW={x:u.min+(AU.pageX-AV.left-l.left)/AC,y:c.max-(AU.pageY-AV.top-l.top)/AB};if(o.mouse.track&&x==null){n(AW)}N.fire("flotr:mousemove",[AU,AW])}function r(AT){if(!AT.isLeftClick()){return }AM(b.first,AT);if(x!=null){clearInterval(x)}AG.pageX=null;x=setInterval(AJ,1000/o.selection.fps);$(document).observe("mouseup",O)}function a(){var AU=(b.first.x<=b.second.x)?b.first.x:b.second.x;var AT=(b.first.x<=b.second.x)?b.second.x:b.first.x;var AW=(b.first.y>=b.second.y)?b.first.y:b.second.y;var AV=(b.first.y>=b.second.y)?b.second.y:b.first.y;AU=u.min+AU/AC;AT=u.min+AT/AC;AW=c.max-AW/AB;AV=c.max-AV/AB;N.fire("flotr:select",[{x1:AU,y1:AW,x2:AT,y2:AV}])}function O(AT){$(document).stopObserving("mouseup",O);if(x!=null){clearInterval(x);x=null}AM(b.second,AT);M();if(W()||AT.isLeftClick()){X();a();t=true}Event.stop(AT)}function AM(AV,AT){var AU=$(AL).cumulativeOffset();if(o.selection.mode=="y"){AV.x=(AV==b.first)?0:P}else{AV.x=AT.pageX-AU.left-l.left;AV.x=Math.min(Math.max(0,AV.x),P)}if(o.selection.mode=="x"){AV.y=(AV==b.first)?0:AD}else{AV.y=AT.pageY-AU.top-l.top;AV.y=Math.min(Math.max(0,AV.y),AD)}}function AJ(){if(AG.pageX==null){return }AM(b.second,AG);M();if(W()){X()}}function M(){if(T==null){return }var AT=Math.min(T.first.x,T.second.x),AW=Math.min(T.first.y,T.second.y),AU=Math.abs(T.second.x-T.first.x),AV=Math.abs(T.second.y-T.first.y);AS.clearRect(AT+l.left-AS.lineWidth,AW+l.top-AS.lineWidth,AU+AS.lineWidth*2,AV+AS.lineWidth*2);T=null}function AF(AT){M();b.first.y=(o.selection.mode=="x")?0:(c.max-AT.y1)*AB;b.second.y=(o.selection.mode=="x")?AD:(c.max-AT.y2)*AB;b.first.x=(o.selection.mode=="y")?0:(AT.x1-u.min)*AC;b.second.x=(o.selection.mode=="y")?P:(AT.x2-u.min)*AC;X();a()}function X(){if(T!=null&&b.first.x==T.first.x&&b.first.y==T.first.y&&b.second.x==T.second.x&&b.second.y==T.second.y){return }AS.strokeStyle=K(o.selection.color).scale(null,null,null,0.8).toString();AS.lineWidth=1;h.lineJoin="round";AS.fillStyle=K(o.selection.color).scale(null,null,null,0.4).toString();T={first:{x:b.first.x,y:b.first.y},second:{x:b.second.x,y:b.second.y}};var AT=Math.min(b.first.x,b.second.x),AW=Math.min(b.first.y,b.second.y),AU=Math.abs(b.second.x-b.first.x),AV=Math.abs(b.second.y-b.first.y);AS.fillRect(AT+l.left,AW+l.top,AU,AV);AS.strokeRect(AT+l.left,AW+l.top,AU,AV)}function W(){var AT=5;return Math.abs(b.second.x-b.first.x)>=AT&&Math.abs(b.second.y-b.first.y)>=AT}function s(){if(Q){AS.clearRect(AE(Q.x)+l.left-o.points.radius*2,j(Q.y)+l.top-o.points.radius*2,o.points.radius*3+o.points.lineWidth*3,o.points.radius*3+o.points.lineWidth*3);Q=null}}function n(Ae){var AX={dist:Number.MAX_VALUE,x:null,y:null,mouse:null};for(var Ad=0,Ac,AZ,AV;Ad
    tݖ\"z FBSF!bg/Al|) M{iQM399AQSYÈG[Gl+֐0#& 5?f{LX!"\}aD .8#RB2&%sb̬|̟)>NZdZI u 0%UYU,tT&%U:TT2,"gWķoDk"+ i0KǛ4_*-(AhwfSd ̄e*Z/ ;귲2]l5z.s0Lٜ@a27SKf6%&RgAsL ڂJ)wAI#L0uƊ!D~g4Hh?(bi4%kuh7pk7vgyЅJie[LtΗFϵIjm$Z./_%]f,q{LFyLff ް/c'afǓc(#Iq-s];9%kl lɼzۯ(xsL :I' ' ؍c}B;Nq찖tX;mRNMzl.+N:q׎U/FNR|qrUY؉jEr=O22<>Ö ;|\Eg5b߃;]|K=bE.-ş>J^" !G\t-ZA;Aըr3J|%ķ"me:Gv.>(n-'.˫UB\kS==H{D~LtJt7Phfěn?N^Nh#&Q%WGvd|qƾrDG4RJ$P{sƣXe$~_}& WښK[Kq=z<edo)K^=fv^֓{tϵNg5Ub-h* LUQ7-Mڈ4~0Zr<}YJnpRyĥDV}0ǖ ͷ]gE`Pjkb 8k蠫73F umFMrG5FF=Zp6u481fG)~XoՆ؎FR80Z|Tɥys=M 1>awH^ʘ N֮#ܟ5RonfK\gqNz݉M=JƱ <(Z*Wv׹[!{cRv51~(q=eOis2QR4 =؂pې^gö:QS#W=+HumF|5qz7Jk8I09u& WIT() NO襉IgEyFLK;Lbq)M%$ YQ cBנ6͑ؑ5z߷2&k"7.(z9?FhԮ8yW<\9ˏHX̵L_G@W0"oKq ˞ctzl31ȖHl@;S| *[:"D8zs/;W+u6+bt芣okGW]qtn^83{^O6O[OW<]tav!KOW<}<-2nOW9zZ+U^G1GbU`*Gbꊩ[G^ѫU^G/GU*G銧W*tWtyѫ{5ѫbW*tWt"Oa?TYE޿.ZC13 l5b뉳aǏeOœr-0BXḠge 8O/cOM=.qs YX~Կ:iDĹ-qSԨtS<=vܵYWm{>3 Fܤ_uƘ.ڴ*銧+.Ӛз+.ݨc O?MHrޓsИxޞϽT _BE̅|Nx }`!,ύ7T͑p7;q?CKp$ ~_Eq}Q7oWI{HU{Yzl y[y>~>8cfm@5&;'D_U'Y*٫}:i =4 ,Aj-3q V^&md\.2~i<| }ggjf )*%!L A~,&v:vsCbc)DN1`g6{$T@u LhJ$ߩ$^1o)uIG_1r `!;{e5%Y' YiH5y'e #.x}-gG^rVgo}Z}'[,iQ♲mHPwǧR$Qt'㴆d{"A#nR,i=xm%XK &滢i#Rdm2܏Ȫ䏪|1Fc29YmWf[9>1ʃu36ýMY$/kLf_&yk]# =/=mxGE`$4PKyJ:~n%nq1we>屨 O(uSb*%؃/yB3%.Йdܑ q ܛ<W3S%䡓$tY.^~beVT"1IYowt ?DL*k f&l-Т9iD(sF ?MGKP>w1 qHX2d++OB_ W `u/HP.#˭za xO6yO8PO.g8Oo?vo֛~D !k_2¶ACP>Ә<*yFl{ zq"GruM;܎>-,?ogx>"ɲhM hE99_K=Ivۗ("5.5|mݞm$n7qJ6Y^^P 'J~D諄N>oY6uH߄-j4".gNl2$Q7a=άƨ+u/.Yd 'e퓿30#:#KvLʟs{0i9d{ l n'x5 ?Af7҂51yGCg  `{NsE7ưhA}4AspL3#;h=h@nt 3hPRܧWT;ק:MVh!tǓ4MgW;&zX+5hi iTt4q"L1{Hs{&fvauVv:vuzm:cvYqE?i^O؍jql~K vyw~keZk'u '_$>^O:}zk*݇_"Εo'TšH1j "&pI?DTG(­>+hkET8pƺ`]ݿ'5,]<_<ƅr4I]!vg;XYZroЯ>nqpxy7*)<8б'T~.gbq& S@ P@Aj CGer4^4>lq-,N!Zj1&ɽܢ->wD}Y G]Ƿ4uBP6]3^,b%0к{ P݂ȩwmM]nY-؂߶H j3ioF}{w;hp~j1>Auб-T{Lmx xm {Yd'NCKYAp 1@` Vqjcih vz {-?&~2QU3;m0ZvT+0v_m#t;[P h{[C1T$ekV]޶Oˆmh  pXȭ}#w.e-Ȩ#ٹh=䳰16 lT)6FfzaLq<6f؁ _8]<ħq0„^B́iYh .VB˒@tl89Mb*{(4Tz<'j8ϤXjXF3i=o: Oo pZlf?5:+9Og4熽kc'6!ZdL Ye TcB="&Mb(K פs{Xi xfIR4*FeШ AHժpz*iJ_#JYHvyWi4jIw`1\`J:+9L[|B24hO1|,DdD] ~!3L2ÇbMx| rW`C C𝥟㗍?f58 y,>Sos٢i@0E*{4yg 0CMcAVHY^<(ŨIS yvSf65,SMe0i"P~V Roc 6,]65Zr y}+ojT[56t+ #/P- 1,nRPYQ s qJ`u>Ȋ7 ;'0Q9C"z\;N(M.X54pȬ'2<iq3'b=1}4/Y,V4m Q$Z&V&/\όh$FSɮ>qloI}\ǁ$;n8"|Nh!1S.m\W2i$fO/y\άZDݗ:-AE|ۯs 6CТa3ME&ÊAmL> sY,)Y1&e 3J8vH?PbݯEyRX<Ox؄4b vk~468N(Yƣ2o>-;5< p wf@-ь[in3h<"r0c(p lFѳ8e295|_ǩFJ|)<@aϨML> brven%y| 9- >E '06sC$Ŧ%Ζ1_0 ٠R 0y(4; gNsr/5n/$#VϕB7g)\V!OaFLjj~%y9?B,D/ˆ\pF)KeLJB/ĘYg0 m30-\6&XV3VB jIUV, A {~rՁλ> 3ș-5Ǜ$Q暮ʳFk@F( &͗J ''~7Ɲ(G:|l.o`&,PъxQ(ϐg˭w_ mpC˃B܈D%;812`Kv=F#@w8c_9Lf%dp؄)?817\M$[BS˥GM}l/'Z %sӋZy2~LhJh7Z"tqrBJh% mgD_p1Ɏ8{D96HHKi&g/?ZsAqk9qyL\^%ZB9G??#=D7dVFaG6bBUBޝ{ENzdGg+'@ OTqM#e@XǑ/WIGlk1`;m=H2L;5i{x+?N3llFlKݞn=׬M,\+zƩX#Q@__X/bz^tSVFݴ47j#jɝvfI3o8+ѻKJ&)&1Mv3 )."0wխ:ׅ؃h`\M,Ќm,oYxua؍Sa,fM|C-k!bF&=A ,cƓuۺ\9nJrZЇɝ L|;F<# ~jslή|[>uViVQ-jx3cmDڠQiL^-wZ^XkktXꯈ=:{^ gh[iIsP- Nkv9Gv^mXꯈlQ+ӫG\:wBvg}DeۺɊlJ9y?_#ivtuW'W.?!oG8vG/F2 W*:?Aї)i/{)&$_]=zF/5:5؃]b/+_0;|uqؑoh ʕ!TGΩ+5E8 ZJ/Y)NO襉IgEyFLK;Lbq)M%$ YQ cBנ6͑ؑ5z߷2&k"҈C%s[~֧Q֧xk{k۳=lo|JfI;<1]iU"FOW<]5oW<]?@Q?~&'49$1틟?3={A:"5܋ޙ 6h~vC8>XqoZ#nw6>ݟ;Ij?ܣoU)y?@˓'pf:8j}}q,DŽC2ۀtk1MvO:|s3&aOTWe7Χ -Wt,4zi2XycY[f6M~;#]d#i!Ӳy,ԾRL3UJCЙjQd!YMuRLJ1"u;cb!dme)IA$4@єHBSIcRȓ ʿc/1Cv; kJ{gɳ~O,ҐjvNObG\>_Z֏RO;>.bOYJ+;2|43eې0OHۣ={9"N^;i DF6X̿z Jse#!;LwE#Yi_.F55*dUUcne0sڮ$lr&x]ʒE?*bg"s8c#Sy殴o-n)ֶGBmu^`-::X=` 150c嬁i{]);3"y2;do}ˊ`MV\k>^Q ]izЛ\N~(flwC^bq34HYug|}b{flv#^{H'ns_)_"֜^;5ͧNTK߭_f/aK\83ɮ#<:F7ydy gJC'IԳ\&{ >Dc &>f.A.CT60-͐5MZE=s.&!ӈQB20 -p4Yiá |8dc r>#]eVxNwF\&$Y Sznzv8H1iu{z&ZohZ#AIKMģRt):rcYZcIG^H=* ;hcn yŠl(\6G[u7A,Mlp޷W?] p~5߬7*“Cֈdm2.ܿ}1yTv ?,nEp2[w\͹EQ}B[X~Φ|DelAlrr 7,{r9ѓ/#zQDVkd{]0kۖ=UIR9ofܕlK;ڽ AOJ,W )=f}޲l :5) Zd hD\,eHa?o{&?SYQW_F3]SA?0O87" 'g`FtF(e:7?9BVO`r!bQ tדfk<AI~fIь?`F}ta?8_lQDc [Y/vi.aG4T81f{o<уT Mgr:.}|EE_`C{}:dE6Nw^O:}zk*݇_"Εo'TšH1jE0Kp!?BnuY F[[-զ6emz22N  ^0 +y!g6ya -mJobD2HiE~##y=eXL]Nȅ4ek4Z߭=+G U!PqQlP:p_;?E9Qn_ }ymkBT8x횉m0]HI!)$FR?6c>>~sm+vuՑνYu8uN?WP>1JsWiV_uKEϸ/rˆ_gKW]ױEYcl,[TYHT}xL#}A GV7^}>iҞ-i;}LJX&TP3T#ߨgJl e'=?͘ona|7>?ǐU%;/mN/IfQփz{G}?v✽3X~j{zTAO^ʰ>?sy|G)P׻gI@XjjgiЃ`0 `0 ?ϟ|:seQ3|ӧO|:2|.};7eGFO6_Qv]T]^ˮg{>pjzkuo{yye?{-x/ D:3D&򈼹e^Hyi#/OGzϪ߯_~ :sMe#M3Y#=2 QЙ[\s=E8}E>GȩT ڲTg-}VfoSVwzV}./>~!?U1<#}=F[ ~QڋBN..+푹^edLo+[\-k dW(}6q$#?z6Bөi?L7!3O_Q}Пuo[=tkȋM!'}/Ƈdr2_Cﲨ: `0 :8o=+8-4}۞cĥXdq{bUq©ήm!ƶg*ΪU\z[GA=^+ru{LV U?)V>ғ)x|Yҁgi\yi^cUo*= !TY?rfgWsʽVn*VX#=Fϫ+[F~yH\L~[O҇h5ݵTow|Sfӟ+);F;:x )/OS yUo2e)Ve3'wgGg=J^`0  ľu kU,Ksؑ5nY,bXw{ w&3QהNQev ]ƷgcH˞i{A3I8hwduwUIWq8I>+@pQşGcZ\ƪUߝ]/:3d;ɫ:gB9R|GW~w2;fzt|+i5nΟgZY|<1NyŬ|E7k?z/k><=Α}N΅>uWydʬdz `0 *\?W8GY:Dgcg< 2+'W6qn؟{ru"wU쏘~c#T?+y{Q,,^qF/Xv8.֩g3}ȸOP ~n%hUG4(_sn|W}Tg&x^c,Fѭ+ <#+}/Uw8BRh_|33!mr\7U9m({ѝpvew[xG]߱?g;,nҽow8]וb?OV=Z_#ve?vN_WrYLo;1g9pV^G~>[_vNOS3 `0Q[ veO\k^8֔v<Zbz\Opbn$~}oz3ј mK vU]^iNWA#x딫jt q :E= z%օq)CcYEqyRG-+u (K\hP'*^ء^q=m=y|Kvūe\rȊ4={W1;=ݷxp;o@>ȘT\Ԏ+C=*ɫ|GJOCW]x1.ﵠ9_Eб Vq)v(ʑ}[GwǺ{-oSdו_˞׃2;iT&w*w:g׭SOsj%Z[~_˯d֮+w]7 `0]kIu+eL]ւoA^;=GR?v쯱;<y o$N1紈=:ߥPVu< <&3KyC/4r)i=*/|Ύ^]QNН1qGw>ù{ ?Kv:A}E:_n+{u=rq͓̳]>>d}+|L01`0 leg:׺񶊝`W,3O?]\9P~[kOWiGc~)-<w.3q}'vuw$Vnv(r52S;Wk_Kϔ8B/hEՠ'9w?K;x:x<|@cϽVyc@ۖSw8Bq]=2lBe6V}eR( VeZT4ade2ޒ+nYBTqSߔ<[&=f[|szP)G}{Zׅ3n7jpWwftEw[ǽ;`l? `0 `0 `{~i`oLy>uoi\qK|}7Svu9G쯿c¾#>,jow{ՆݲL=mW2u_8دjo?kD߱mw>#}E:OۡO;y`$j?tUKmkBTrxٱkQlѡT\TE 64!-MS@\Ȑѡ\B@BpA]I(IZH=>Çx}ywGf$8comm%rhmmr3z~ o]/J`0z<(Ux6~?w| 'gڴ%/n ;/zls#{o^(F{i|-t:}{qN\ItBF$ӹ'˜尔htZ, s=54~Ο?Wȁ4]M~?>Ys+mfW*_7>jyAv6J8rmkBTx\~6$"H$ɛpnm_fNt:`黮[FA 9yҭumA('|w 0 }9b[^u@^@\bǝ*"kT&xTx6',drGK< 8zL!srPum~Ow\gݍ/=^\#.jHEgT?,-Q'} 6 4XE;us ٮ>L&6'tl~3~Ffv^|I>5.z {/_ۿpg4$@ 4F6 }Y>ΰ=Nb`'n ϳ%S }:eR18k KamB 7IE_Qѳ; rdL'm~OP! vP0AA6m(Oj:լm~OM '|,I:kP3 x[3Is 3.6fC1˽:GfF b_TxӐ zlM5wiK4 xl}6X&HdY|"}֩`+&OdOɪQqm~OV+rs[Ӟ*vrх؟~ +.fVrmPXK`H`)Evw/o{^s{}+ ?u1t#,s#8Ey}y hKKp/Zb|ų*]殻fV`p}i'*[43ׁm훑<1)F $+ ~}[ۀv찭-0GTN3Jo9Kt$}:ms_vpGWmt~Vu?Xnt_RV8eg?4ctO~=>9X]>6_ xH 9bo9,oWs>|2%|jԩ?T>]Ze8@ @>@81Hn=;vjQ﷿wEgF_^^m~O8Y󙦚X!]Ǫ S}tѪ$ӱ-E LXR'I_A`LAR!ٻo{EsNuL|SIቃ=9ioX1tڭ-3) B=Wp4 @61 \yG=?UA{ p#c9q 9 _b-2Pc@ۻy+03**s"*9H-Xtg|%?}*_X=>+ a6 E ;cFȚB2E"8?}K>,rv/nPa)ekFCI뜹sN@L K6' ąޚ]K+VMg 8{TDo{Wn> rf7$R\g-.TkEQs~o:ɗ +1ցְ`$F́\@yP=swv9y`g0k[jD=e>4VHSv ypЊ۵9=wu?rB8 & o{r6H-Kh߳[kP`9qG4XǟߣuKzЎH#9,q6WĻk1"Jo{vNƦm%$")̫WT[F'ql66PDK:}@Ep sm~OgO={Ua[@WwR3|bC)(6'(D\^rCW?e>$r-rPӞ6rs[f A>.l$\pL@66L뿖79_mp5JdIeрqxBXF5w# ,6'`8o`֮&7:CJd9[b6'Q6]͍GMsC|!lSj~rVɾR[;xŹ]ڀOkʐ`Tz 4#xw~tW[=CMFCz~*pWw+7PM`s2k'޲V"kȵsNR0\Yo;Zm+y9Hx0p Gm5/pb^ر,ZkR?eӶpk3E'c_ZH;w\F0<ȁ7S~]w$_C! 6'#ĿQ \c1'X zŵ_61@(0*WB`!6%aL> QN?KOJSp Nñ&Od<bZ cO Xsp1fhn]Qj%ڳk(9!ح-z&}z~f&Ģ~K*5ʄCDIQB~?{vmP^f5 =r0tI@Q10 u"i u汅vD'a`Mn1,7N6 H5 xg6ag'dk92y' QZs>:q8֩',6Xv`9 "RijBv-h+h<$>`"6ξhLf30o%2\H鿾>r 8oⰧ1<L-eOSI_U; __#0<-~?3EQƝ+rᖜΡ<r,phQz_\h<࠯FD_jw]^̓O~ ? e8$R(--<1q!y2oʁ֢ߪ:s|qg6(u<0c:':o` p=abwhıϺT,ifVzejwSU 4M,o{Bx|w$pIukZ6 w2rK 7D/_)MCXʡnS]Sv0_ |}GqX0ح &G=k%У!Q׿ڳ66d^B&h'3Q:_oo  h|vkyg&rٱz]7B{inH ;,~gݡwM_r3!Mxw3b\/:ZB+@qW8;j?\NrsO &t <|}9愺~ Կ= q<pMmuɺ}EGWvU@J]'ƟsR&SJ!gz.+"%pB H'ϑd6'씂X ~E//=xS<#___GZa<;= H\azQìеPg8vV< AV ۄZ8ZO~!s~$lR?m3ҏ;,52՝^_z ;jg? =8sI0 @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @44Բ- mkBT~x흍8 FSHI!)$FRHnw HYx3ꇤsaaaaxIǏ'U{o_ھgW9 o'GW {>~Jlo߾)*/N\ϱov[iZ_ձaJΝ/:6O- 92b?Tlk%?_21B sY5>:>c=1Ow y^- ڶ,XzusM#גU]>H_yYv!ۉ_mi Rus]Xm_g)YY)m]y,m z1aaaxEߓGקo/Y\k6xjgH|yu.\aæM&wk#ϐ$?]Mo\Ⱦ,/ڥQ@~6s?)}, l gX #vQg Bٙ^uのuhm?}{].~}v_J;xogJY]޳@.)oqC?}>@Xߘ'-(W? źvƔOʙRv[K?[A}?-wmՑ}g\=c}M ggg DŽ-B^k_g?F? v0||؎=ǧHPgs/hؑI t~{n^}ZyD5XWvO)"c0vY Z|~_%/,p\ɹyΰZ/;/xs_9?Pܯ5ݻ\[y|č8gʱL{? 0 0 _k3>z_\S |<)b|7aaaxn.ta?l^Cvkؽ#~e)3<3^kdlc&jK+o"e<.ʞ`^(3zu l+6v<ï k7]/lc[`On}򚄫 G뎱zt^v2)?;Wmr5ocIz?Ozx{&!ez."ѯ 1Gg{+ҏlw<=}GݽFƨ^)zIpG K֜{{e G12ۭqiumf>.}~a? 0 0 [u+7Svq֭y΅ ?ނ}XwŶv?ߩDZۓ-q/?߳=<~#>Fk"qzrQo 9r,nY[;o:)@-`ק-7({߯S@µK9֠ɸ>:n3 _[_*mtcmC>qSL=<6;ǫsaaa{xˌ\ފpx?0׋#5zяc]x^l򼠕(f:~٣^lin59W~\;?vn6erUbS~v^U O7O(|;+SG4|?f*?rW~2oNٟS9~daևmH6mX[J~s.ym4ٶO|Bd/b5ɿyU? 0 0 0 0 0 0 0.P~*1@G\⟿KrKXs2(ߥ纎J8'>X@▼QQbqwx b)_K|v 1M6kee-2Ǜ59?K^E~9ϱQﱮYF8N?~;:=J<-tĒyNAgC \NXKs)'^Kg\~2}6}Գ)n]Or^j~"{p29w6/.z-v:+M{WJYZ굢`% Ҥl9ힶկ#OUz+U?;sd~vND7*.Y+v:ye;8}~|+ÑޅN9}{Bƞ#txխsXɿkSV/uJ=o G<ջL'L:D]6jfgLz/+ؽ[{rCMYq~[{yy czA;w9zszWHVax3 ף2!iTXtXML:com.adobe.xmp Adobe Fireworks CS5 11.0.1.7 Macintosh 2012-08-01T15:09:29Z 2012-08-01T15:10:21Z image/png >KIDAT8}1Q7,X7 ,:Dm/BG D-hlytF„] FM̮l$a,);SbDl  ӥ0 hd2l-&VDvO  dYj^sx ly O q)|''@*<ބ+z=4emy H)pH$r`ZHxim:E˲m{Gk}0.?QJňFKtץR;ֺS.۶M? 0MJX)cN}nkd2n2|bǶ`yR3V ue'&8 vE$  fy5/3~;u@DB0[Wwy  &sΪCxQBa'>g\ g`fQ,6Z;"1hIENDB`zentyal-core-2.3.21+quantal1/www/images/left.gif0000664000000000000000000000011112017102272016322 0ustar GIF89aVVV!, QTq|&jFi ;zentyal-core-2.3.21+quantal1/www/images/reload.png0000664000000000000000000000057712017102272016675 0ustar GIF89asssoookkkgggccc___ZZZVVVRRRNNNJJJ!$,@pH, 90ђ$0Res(h٬,a2&<E8؈ݐw $&M" Q ۨ(ӢAqJ@:δ5INKigZ k+ YAlF6GH-K.-\S+ҜF!AF}B| $ħB[QKc$bȼ7µe7E,_ [~Q5uCP/ yQ w| zV 懮UpT+ܻQ ?gV|q(L* Jf$h1&u~M(_ 5@j=i,)]>ZK ##3ҡ\tʕ5[&5e C>"?ۆr5Vn} f^ƶ#Oi+ϰoE,33 ܹp8ԣxN{Xھ/dspBaCۉ9]N O8X*l `F}öjؾW$l{g,ɐx6%hlЇ8CYɠJKK5UЉS/6vJD)yNz vBi]8Aۥ4C8V)%#Bu>eK[_옝W_k(⹩mȄٰa/ea^0c62%C@FdBUzPj?yFPjdCd _&:DS+ĔX%i9Z17L,xK%"J3҄ fDPSFNŌvmDBI+d{AN,+X\kYҀfkcP&ӵ-`D&'4aêqALUe`.2Rbef% ֦13s&˄a11fdcHjjj8ـ LЋ'j^AgŸd/gMQj QY0Y )=掎kjj uMeee8'ԉƓXv"e69OqKG-z!җ:fVx0I ijad1gORg \NG`qr }9yK5O°Nk;/nonF0Ew/\m0ڂ?ՕEV@u0buBfZ`<㴒~DTܹvO~2 L{9MX?POĢ' cK#/SC;ǜcxKkB`"bNhZ6>P_0eoBRȴDwrOW 5 ZUð} @5@&$K2aCaJNFUe8LOm?B%ո"K:gqlx"wc@OC#c>۲b B֞ŒPϬ@&lޥ4!x ͝I;$//ı /$'?f:/ xAi{/gnnnb5> Ak Q10\ʡ1Ș$ݺx3k4Bk> *Ο?= GnD-7ryydIeaZ}¬apCv,6Vu޷3.íV+1| Ik&+rE:,1Px^oLeZ.Z`qPNhg%Mb:e16^>X,q2 EN2b,V]v Tx梢OdD (ˌ!3!􅤃e64n0F7ʽ (Ҕ%"+v䢗9Y2Ijj̜9;-R4ZX9ǘ }ŅW\.`~BD2򊯉 EiNI̷7oQywK 㪊`sG* }%|Y̖7I OyaDLZҝ[SWmzD hbpQ0pD^Ny5:|!}r<΀o VIќbKD#cPnbz)EK(d)"XB{ g:P$ 0P(kLb jHTM?5(@Ƌ2;ؕ@]/+@H])F RNNUZ?p@NmD@4IɹTU eOo&+V:}\%-/mω****_uR)\<IENDB`zentyal-core-2.3.21+quantal1/www/images/warning.png0000664000000000000000000014131012017102272017063 0ustar PNG  IHDRw=sBIT|d pHYs  ~tEXtSoftwareAdobe Fireworks CS5q6prVWxw|fq9N,ľ8sq\==2.,BF(eҌQڲRViye@IF t:;Ȓ-ے-߹/^<َm}{dmIіɞdRKKɥ$n;=KIIMKKKK=K<ͭ[.m7ŝQFl5oFG{zLҬ&Iķtq~<~1/p)~~qE_"U9W\r7F܄s6܎;r_*pyߋp?y|x Ƣ\*|ոϯ=݈J`̤u8+8$?kFf0z@q%\kadUpw| 0F|巰ׅfjӺ%5 T1Rticdce.\W!hR͝( _SdNce97 Oś`kAϑ9,oY|j{]nw, _&sX+_U-WUoPhzG۰4Mjjyp̤X\]լZzO7+`퀛3s  @;bbiAMMoyAuEه~ gi5f-=֍n!h?kКjMe iEV;.?Fd#1.Ξu-`tb8dQ` of4JQ#݆kOyB~~IHi)Sb6ckZV]U8 ;!M.t46 9So"w/aʅK_KˌV@ocy/­KOo?`m瑎y&DEyxk `K/+=|>[:yȝx9 W"nwuXe?kuV hXJwaE(m1#lOs9A.C1yه~ ״PS\$s)~{utpW?]dZo|C/?yASdNcEߵ~ssdc*CH}Ϟs~e,Bu[2+XŚG*ٶKK5md&En3o1YpgAC?%so#3vUivDv=ˮ߽{C?\ele HߤBngda#aOؽ,7v" ncdcP8Q_v0XAo̐=O[Qϣ~ Ȝ=ETd@OjO8c?^} hD$ot< c쉽7@qa8GHKR Ghcq^Wx^x^7xǛD o 0wxFq2ލSp*ރ}4 >*\ q>{Ⱥ~<6?r0nsyU?ZJF*F+\9Vpgo?6?SFʆҁqk]&c77w̰ mCA=-oc6}wXզ>]ۮӷv=vµAXﳓ}V{gebVas_4uoPц::+ ,ʩ2Q.61K]m9<ќ 2h}y2?DH֢"ݲ>'8>}@Wј4 d9%5IJjJ&Ci2`( !Jg@i@&CJwTX"VҠニg^ч.E(av KE@s:(kC{1Uӹ.Jl.ژae6G{9v5 C΍ؘ?zؽ0s`4`ń[=6١%ܔM$0!IA%!}#u }fnx,YBEO^d@sAs t QfܼmNgeh\o9Ћ>>#8$H^Qǚ~pu>| }`C]s1cXcJksx8G:q9"S<"5%o ǹ8N8G4!^֠䨟N|7&$6b aa<EMh_F(h0j87ir gʥy.دqB +P*~9LWINM92tX~0{=|Obc\5<~'pIOgs#sףMC!]s.$k)ÒZd8 pgEtuH5-:40Y"0s0p'w1#0gZB)St.)9M5's!UVu[ KyɃ|vF :7`ȍo(, k%|Ur} ,F7XF8F.Rq$ӓ>bx~:c.^"+p1^%`fZ 3m(ƽP ?RbA6#Z p2_A;4c>,A&R|?O9RN"|'(n&wjX|f>GK>I!$'fsOzLT1pv8F2 EGnIC0!D^ |Թ0ciFBSb94`2rtaM%V<) CgGĩ [cȺGDвZ6C˖72e o1_0FR\@ӑ(N$%1oF~C,ؖ$%z]4@bbpvq&cQV&fzQ &;?qnl`U ̒.7ReH Hx՜<XJGg4&.Uq*N<8F†>c&Y|_EjGIib0 LS^N7>*|Q"߿MJ#%V 'Y匈7t˞YG5`Eס(;:KL> Ĕ,4KD^җC"<5w\$#hfxE&;A`UpGZd3v\ObC8&U?^}xpYK i{g<ʯR9|BpX¥'ƅuf1[OlȤ:`QLTֈ!xzi4#Bs Sr;BdF%,5|6hӔY y~JUV $.ąDб29L6?f)s98+0K)v#aM%S ˵UĆyq#SM8+3+(."n?e-q% )d&G[$ |.v4HlEXAIiw/r#o{[ݳx8ܦ\ңRW=Ils"r<*_|DtW8E/(6hMנ؛%wQB>QoE nj~+Hy"͔ S g38?)՞Y^^HŐJC e6Bï 4@I ώHw;> ͭ5c@@'TWJJ٠m0R0OKpc`II5>DXw2S!9+-LTk0I_8Q3g0J1_ Zy+ q,kr\SN&A%]ЙINv&Ńsh2aL4=8`,3\r5l Nݯe#LS'|y*:~q,&55%w3H՟Jt x99LhN%kMbX"Ld/'4 ͭVBhZ|gG")튃WDU"-!\uJ8m'V0qJk-yy>?=#gʉnD7Dwmόx m̄6vzw' e*{<5#s(j43;XqO䳍KrYeG/PȫH3F=yrw[qd3\r3}ʭvN,I#v(Buc҄2נ7oV׿j6Z b2});0We̚pp.tGi{CR:k+<OxlDopgTJ ~ MϨ•+_v +lpQN8%ӂZKJ1h#zW[+~@w5:ϙOШk@*N iXM[ސz9xF b$ܥ cDwi]lǖSwW p{ }kp36k$P 'ܛOknNvPiw&Z.aZi0l.*cVZ1F.~|ҼL|mWej|Z|lH o4:؇gw%<%?cIf;^Vcz}fS/u |]O/eƭx銧y>6۳XqtKh1*8lv֗ښ_1wfnb+.vT+^6OOW<]t(HjV+^>G[GW]qtn^Wf2?`>Z1ufjbꊩ+:E9zZwO<=V~U<}xѫx<ѫ8.ptWqt9ѫn0wW1wţ OW9zOW<]< ѫ8.ptWqt9W1]`*GbꊩenyT%o G (/9y-{\Bצ#;``͌#jE'=J\)kpqyk9{?Μ~RV?E}~O3zV~;7B@3}SPX'Hy3tD u8N[(ޗ־G)hT,?i]@B~Qk%8.23u{]bl㊟+~ 2VE\vLxLi:Js1gb#oq󴿃Tv^gE؟Yy@˫NnY(~g_:N-F}z[ڽ I'+HMe[z'E^š/Pfx%ǹγZ7syqqHk1kήM%_pHB:WxTYAL1iwH_ОE`…(%=ꋌ L BS)Ny"\63YLm!qBㆤ'ҾA2ސq+h_ QFG8XT'v0]Qqݨ#Z?t'jnIOqנ揸SB:IB'cMZIEmebI*lmL0Kwrc"ˤ2^487Dk!V^ջٌwiCq5k5L$K1! !.xVNi%Hvj!lQ 5ۭ/ՄoX U7y@k԰ݯcjS_|^.عmVbW!)5h˫W]8o6/kK;Nʙ #aU{MotzXt;ԃnax +w@@ k+|l֠o&>T3+xV?uz|ea鳯[,~- Nm<h ok5|ޑѷ.Vg$$"AEJJx>ۄZ{ʏbMmsb)j|J:OO#l YsxZtK2r@>lHCb Я7tpF3] } Bc*Pz0;+tyWVJ:/ *[{ _PS8DEFiZm[sOM+wه?R!hkRawk%?b vvI;VNCKX$ 4\[.V Dm5h::09kԎ_l၃m=Ǐ,aT>]۠;>~kt>[Xmouyar]3T3n2zǎi/+hjNHh2J#"!w3k؋aaט5I[7NBpϭSoٵk4712qye 4Z{]}luo.:qNPA 퐝+K}`z7[4nIN=SOh@hQ&OGl< d:te MSqGo*JEՄsziRsI;tQׯ87Ӣ\M55a=!GDƚm|;si6NRWUKtQ͑[w#s,1n[lJ 0YMfi8Ufkz-žٷ6Vgv6Pz .~({``GhK8 W%B 4|*k<j &^{M*=}dDzgRG}V#'j硥x<"C"#Y×-ꦦ[a4~*=%yjiZMq,Y\)֢fkosMWbg9fTۺ-+q\uXZ:d X+qJquPuo +񒕘ktT3b%z*o +&+;p$tna%AXGIQ-c#֋#AgV2+z$hJ&Jr$X҂Zd-1.|bxekAsS,bJxZQ4zjDdaOë @(^aqZͶT TkUPR-g[2u̘l bY]GR-gWtKx [x(A/0kiԌظspC6&S. ]9ejQu5{ӱm;MshJ!iԚi@9W$L#},2Ƃ+F8Q1~X{.;dפe澆iXMsߠaYa`&څq l4!ˢ]m9<ќkן'3sMd-*-k=>p|`V֯ {4U,6&@@IMCd(D( !Jg@i@&C  KRXTT]\Jҽ]o(av KE@s:(kC{1Uӹ.Jl.ژae6G{9v5\04>9J~(b8ρnW|dGlrS6 $$ -ҏ>y=vg =Ѝ@sAs t QfܼmNgeh\o9Ћ>>#8$H^Qǚ6~M\ 9T ÒCUB^|yB\߱4#)ʍLxɏY9}`asL0Uc؊zp\+YQ3#efԌʭQ1d# MJreih -eqx2ƷK/#)^. HFk ['E@Ƿ #z? fxlcpS^RuV11x88`ucjsPm3K=(Wd f8h7 +\h^`t9*CRe@CvI M|&7XzVr=:(5i~p8NSq 1u4|Z/~W?8J_NLi`02v)WomzE p(X8*gD\M])<{]M=ZLϮy+[7 '#qAxERәfKRxH d|0 $Ss' NHlnWe&NrUbE?u$\vV@>Ƃ^<=3 #ԅU5O&z☇.=7.f?#6zfD&+,d5<4ye鑉e9@P{!2LC6Qq+^w,.g@ ),/e5q_vI!EIJ|f2N`/~e{ @b;RY&8Մ;!F83N]6|Ѣ~~6)d&G[$ |.v4HlEXAIiw!Έɷ?AwSvzlm֧8c\R1_fXTEi֬,^('~ɰewiq&a_Nۑ۷=-Y- CIEGDG{U]dNrlн 0{3¾Nߐ>Cȧ4꾣} gqE nj~+Hy"_EheT_7OiJgRc1PBͲk8 PGó#厢ς3xs+m2% R6hLaA1?("pR{(jM]>. THJ;Ūm&S( 7qNRmRUni3%9`4ݾɧJ]l:C#raӼsWo'vAJ€'Q 3[/x|>o({ &Ni)?=#gʉnD7Dw~U2ژ m\ s4O c?2y`ιr0N=i /hgMzk|`rx˦ԅo((^ik.m-ӳN#ԘX/#i[>I+hœ| 2tˈ?刷(V(9dR]vZu YՆ ųf66'M8kG'8P5ceW-k- <T̴FyikII Gn g@kvjƓ0JvpRYRI-RmYj@6Rot&Yj18\Vz8aC z(T([AQ|9[4cԓ'g|iM6c%ǚL<ǬXo0j7/Zr4b1*T/:8^> []h?/Zʄp?…cQϢ+W3-6W>' W'|E[.wگEՏWGTgl(tO j-іs() M=%oi~wawI\KY]4ӉҤn-ALﺯ?#1m;h~5g( m0I5<N'ҝ>Q/L\®Diarg5']$1TƬ53P']y,L˯<¢銧2V<]tӋmZᾨ8sGW]qt6G;K)SMaU1wfnb+.vT+^6OOW<])*]U{8W]*G銧1GbU^S?9zOr*xxtWq]*G芣stW1`*GbGA~?9zOr*xZ&G+HH5~'9{]ЌEykٻ16gJ6K-eC[3FP{##|Kh {45.e0J-ױ\ǒ#~Ɩ_Kv}v}O{V}'fs ?EK_3*x;``͌#jE'=J\)kpqyk9{?Μ~RV?E}~O3zV~;7B@3}SPX'Hy3tD u8N[(ޗ־G)hT,?i]@B~QkO=c ܯW~GxXxH^F UAqա?hW (MZ_HE+=2_卉?97zL0CVN=e+CQMTȄpКt N%Y|vGAqNRҝdk+Y&2kpnB|dƫ*P;\~Lhq=' /1;{qn9%2yʇNxZFـgGoQmI,x@;cؙ8,3o1]*|3X7~G 8ĝ{ nCW[?y{H[57gJkߘ3q (g y^g{(p4Ә* TC}{Ja6|2.O~WzOi?A 23yc^"_e "{|~y툞7+{3xÐ9sׄ^>" 3D'y^Iݦ'H}HswO:B׮f_.|Ky_ٞ}-#}i8Wc%]ތ™R'۴cPDeMҧUƼ~h<3ykѱdSߎmZh𢡄w*}>Hs AZLM,Pҡ&kXiwUHa?Ellu&PYO[qKsv~X2={g")yL201;")ȧuTۧs؃3\E9D-#yHAbV3񿯰{RmΧ_T7U{oTѤ;QcE/ GUTaY_;Uw_{,fc7aѓ0Z}b_'9y+Wr .vk˫zs|5m[7oh].qn)oP^a_?Ƣզ=[J ۽&]s۬8"/C@)pqy m5e uaZi_9pD3aU{MotzXt;ԃn ./{.J}Z ;[5[IՌ&ްOݿ_YX>+xV{|fe}cu?u%$q[~u,|Z ;wsmlI/TRyx&\*l7x4܆~M='7(:='p Q̿XL Y_9k-:%{9X6k$!#gpNG gă{R1a U*l`vVHB /u^Tx]֡v_aq]Ө65:ڬѷUݟ\V~?CRpwפ:9K=hjٝ]RNxÐoUi`W>>@w4Zтsfҩm| 2;eYn掏"lt+[XUouݭ˫;̮6qfng LK:%Wd`V~ ; ua r;; >OEMޯaĆe 4d{]&}luo.:qNPA 퐱u+K}[7[4xIN=S )+7(#d!+ph:2&8#}EozǢj¹M=4?FYU q7Ӣ\M55a=r#k"cͶR3!mb[颚#H6G&UYb#O]ٔyA`ǘ4-MpF2d[[}kom+ɝ:m\PvI 6-]ЖMqTKh '8hU֦y&WMATz ?DJ}3BХ9\GxOCKxLEEF2<(g! /[(MMiTB{TKMl嵨X2SEW X-ަ皶-+1Mr̨Uu[\-V⸖p75ձu8: YgS7 ñW⊕8%BV%+17u0=+fIJV2LVwHJd%Ecj[P(%(.FЊ_--1 =]=r)G #{ 5͟kwuwq,<pN?sR?աmz'iIObɨߡL-3íyK-W Z14c|iҤ#<ӰrcS4vߧxڒz22N  ^0 +y!g6ya -mJobD2HiE~##y=eXL]Nȅ4ek4Z߭=+G U!PqQlP:p_;?E9Qn_ }ymkBT8x횉m0]HI!)$FR?6c>>~sm+vuՑνYu8uN?WP>1JsWiV_uKEϸ/rˆ_gKW]ױEYcl,[TYHT}xL#}A GV7^}>iҞ-i;}LJX&TP3T#ߨgJl e'=?͘ona|7>?ǐU%;/mN/IfQփz{G}?v✽3X~j{zTAO^ʰ>?sy|G)PoO,F;gVmkBTxx흍) q ĉ8D^>׻gI@XjjgiЃ`0 `0 ?ϟ|:seQ3|ӧO|:2|.};7eGFO6_Qv]T]^ˮg{>pjzkuo{yye?{-x/ D:3D&򈼹e^Hyi#/OGzϪ߯_~ :sMe#M3Y#=2 QЙ[\s=E8}E>GȩT ڲTg-}VfoSVwzV}./>~!?U1<#}=F[ ~QڋBN..+푹^edLo+[\-k dW(}6q$#?z6Bөi?L7!3O_Q}Пuo[=tkȋM!'}/Ƈdr2_Cﲨ: `0 :8o=+8-4}۞cĥXdq{bUq©ήm!ƶg*ΪU\z[GA=^+ru{LV U?)V>ғ)x|Yҁgi\yi^cUo*= !TY?rfgWsʽVn*VX#=Fϫ+[F~yH\L~[O҇h5ݵTow|Sfӟ+);F;:x )/OS yUo2e)Ve3'wgGg=J^`0  ľu kU,Ksؑ5nY,bXw{ w&3QהNQev ]ƷgcH˞i{A3I8hwduwUIWq8I>+@pQşGcZ\ƪUߝ]/:3d;ɫ:gB9R|GW~w2;fzt|+i5nΟgZY|<1NyŬ|E7k?z/k><=Α}N΅>uWydʬdz `0 *\?W8GY:Dgcg< 2+'W6qn؟{ru"wU쏘~c#T?+y{Q,,^qF/Xv8.֩g3}ȸOP ~n%hUG4(_sn|W}Tg&x^c,Fѭ+ <#+}/Uw8BRh_|33!mr\7U9m({ѝpvew[xG]߱?g;,nҽow8]וb?OV=Z_#ve?vN_WrYLo;1g9pV^G~>[_vNOS3 `0Q[ veO\k^8֔v<Zbz\Opbn$~}oz3ј mK vU]^iNWA#x딫jt q :E= z%օq)CcYEqyRG-+u (K\hP'*^ء^q=m=y|Kvūe\rȊ4={W1;=ݷxp;o@>ȘT\Ԏ+C=*ɫ|GJOCW]x1.ﵠ9_Eб Vq)v(ʑ}[GwǺ{-oSdו_˞׃2;iT&w*w:g׭SOsj%Z[~_˯d֮+w]7 `0]kIu+eL]ւoA^;=GR?v쯱;<y o$N1紈=:ߥPVu< <&3KyC/4r)i=*/|Ύ^]QNН1qGw>ù{ ?Kv:A}E:_n+{u=rq͓̳]>>d}+|L01`0 leg:׺񶊝`W,3O?]\9P~[kOWiGc~)-<w.3q}'vuw$Vnv(r52S;Wk_Kϔ8B/hEՠ'9w?K;x:x<|@cϽVyc@ۖSw8Bq]=2lBe6V}eR( VeZT4ade2ޒ+nYBTqSߔ<[&=f[|szP)G}{Zׅ3n7jpWwftEw[ǽ;`l? `0 `0 `{~i`oLy>uoi\qK|}7Svu9G쯿c¾#>,jow{ՆݲL=mW2u_8دjo?kD߱mw>#}E:OۡO;y`$j?tUmkBTxm@@QGJ@).((((8qudJC̮h-㽪l[+7M0a m/(=WCB'jJG;_DN~'A@`=ct;G?݃ctmD`<~>Xہ1~Dx=,=O??/'^=Xz>o26Cw{|Del;:Ӧ5+;0a߿ߜתUg ih9rmkBTx\~6$"H$ɛpnm_fNt:`黮[FA 9yҭumA('|w 0 }9b[^u@^@\bǝ*"kT&xTx6',drGK< 8zL!srPum~Ow\gݍ/=^\#.jHEgT?,-Q'} 6 4XE;us ٮ>L&6'tl~3~Ffv^|I>5.z {/_ۿpg4$@ 4F6 }Y>ΰ=Nb`'n ϳ%S }:eR18k KamB 7IE_Qѳ; rdL'm~OP! vP0AA6m(Oj:լm~OM '|,I:kP3 x[3Is 3.6fC1˽:GfF b_TxӐ zlM5wiK4 xl}6X&HdY|"}֩`+&OdOɪQqm~OV+rs[Ӟ*vrх؟~ +.fVrmPXK`H`)Evw/o{^s{}+ ?u1t#,s#8Ey}y hKKp/Zb|ų*]殻fV`p}i'*[43ׁm훑<1)F $+ ~}[ۀv찭-0GTN3Jo9Kt$}:ms_vpGWmt~Vu?Xnt_RV8eg?4ctO~=>9X]>6_ xH 9bo9,oWs>|2%|jԩ?T>]Ze8@ @>@81Hn=;vjQ﷿wEgF_^^m~O8Y󙦚X!]Ǫ S}tѪ$ӱ-E LXR'I_A`LAR!ٻo{EsNuL|SIቃ=9ioX1tڭ-3) B=Wp4 @61 \yG=?UA{ p#c9q 9 _b-2Pc@ۻy+03**s"*9H-Xtg|%?}*_X=>+ a6 E ;cFȚB2E"8?}K>,rv/nPa)ekFCI뜹sN@L K6' ąޚ]K+VMg 8{TDo{Wn> rf7$R\g-.TkEQs~o:ɗ +1ցְ`$F́\@yP=swv9y`g0k[jD=e>4VHSv ypЊ۵9=wu?rB8 & o{r6H-Kh߳[kP`9qG4XǟߣuKzЎH#9,q6WĻk1"Jo{vNƦm%$")̫WT[F'ql66PDK:}@Ep sm~OgO={Ua[@WwR3|bC)(6'(D\^rCW?e>$r-rPӞ6rs[f A>.l$\pL@66L뿖79_mp5JdIeрqxBXF5w# ,6'`8o`֮&7:CJd9[b6'Q6]͍GMsC|!lSj~rVɾR[;xŹ]ڀOkʐ`Tz 4#xw~tW[=CMFCz~*pWw+7PM`s2k'޲V"kȵsNR0\Yo;Zm+y9Hx0p Gm5/pb^ر,ZkR?eӶpk3E'c_ZH;w\F0<ȁ7S~]w$_C! 6'#ĿQ \c1'X zŵ_61@(0*WB`!6%aL> QN?KOJSp Nñ&Od<bZ cO Xsp1fhn]Qj%ڳk(9!ح-z&}z~f&Ģ~K*5ʄCDIQB~?{vmP^f5 =r0tI@Q10 u"i u汅vD'a`Mn1,7N6 H5 xg6ag'dk92y' QZs>:q8֩',6Xv`9 "RijBv-h+h<$>`"6ξhLf30o%2\H鿾>r 8oⰧ1<L-eOSI_U; __#0<-~?3EQƝ+rᖜΡ<r,phQz_\h<࠯FD_jw]^̓O~ ? e8$R(--<1q!y2oʁ֢ߪ:s|qg6(u<0c:':o` p=abwhıϺT,ifVzejwSU 4M,o{Bx|w$pIukZ6 w2rK 7D/_)MCXʡnS]Sv0_ |}GqX0ح &G=k%У!Q׿ڳ66d^B&h'3Q:_oo  h|vkyg&rٱz]7B{inH ;,~gݡwM_r3!Mxw3b\/:ZB+@qW8;j?\NrsO &t <|}9愺~ Կ= q<pMmuɺ}EGWvU@J]'ƟsR&SJ!gz.+"%pB H'ϑd6'씂X ~E//=xS<#___GZa<;= H\azQìеPg8vV< AV ۄZ8ZO~!s~$lR?m3ҏ;,52՝^_z ;jg? =8sI0 @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @ @44Բ-~mkBTxx;j@@Q J>^ }Ed0s…51ɀq_7MS[)”!]OV5!5wǼcVA=Z?꘏][unKYF[^Ҭ5P].?_!˗f W#=_ ONW#=Ϗ}^~Jlo߾)*/N\ϱov[iZ_ձaJΝ/:6O- 92b?Tlk%?_21B sY5>:>c=1Ow y^- ڶ,XzusM#גU]>H_yYv!ۉ_mi Rus]Xm_g)YY)m]y,m z1aaaxEߓGקo/Y\k6xjgH|yu.\aæM&wk#ϐ$?]Mo\Ⱦ,/ڥQ@~6s?)}, l gX #vQg Bٙ^uのuhm?}{].~}v_J;xogJY]޳@.)oqC?}>@Xߘ'-(W? źvƔOʙRv[K?[A}?-wmՑ}g\=c}M ggg DŽ-B^k_g?F? v0||؎=ǧHPgs/hؑI t~{n^}ZyD5XWvO)"c0vY Z|~_%/,p\ɹyΰZ/;/xs_9?Pܯ5ݻ\[y|č8gʱL{? 0 0 _k3>z_\S |<)b|7aaaxn.ta?l^Cvkؽ#~e)3<3^kdlc&jK+o"e<.ʞ`^(3zu l+6v<ï k7]/lc[`On}򚄫 G뎱zt^v2)?;Wmr5ocIz?Ozx{&!ez."ѯ 1Gg{+ҏlw<=}GݽFƨ^)zIpG K֜{{e G12ۭqiumf>.}~a? 0 0 [u+7Svq֭y΅ ?ނ}XwŶv?ߩDZۓ-q/?߳=<~#>Fk"qzrQo 9r,nY[;o:)@-`ק-7({߯S@µK9֠ɸ>:n3 _[_*mtcmC>qSL=<6;ǫsaaa{xˌ\ފpx?0׋#5zяc]x^l򼠕(f:~٣^lin59W~\;?vn6erUbS~v^U O7O(|;+SG4|?f*?rW~2oNٟS9~daևmH6mX[J~s.ym4ٶO|Bd/b5ɿyU? 0 0 0 0 0 0 0.P~*1@G\⟿KrKXs2(ߥ纎J8'>X@▼QQbqwx b)_K|v 1M6kee-2Ǜ59?K^E~9ϱQﱮYF8N?~;:=J<-tĒyNAgC \NXKs)'^Kg\~2}6}Գ)n]Or^j~"{p29w6/.z-v:+M{WJYZ굢`% Ҥl9ힶկ#OUz+U?;sd~vND7*.Y+v:ye;8}~|+ÑޅN9}{Bƞ#txխsXɿkSV/uJ=o G<ջL'L:D]6jfgLz/+ؽ[{rCMYq~[{yy czA;w9zszWHVax3 ףmkBT xα 0p=ɮxevjvR+/OmkBT Rx 0QGpGpFqF`GaFpjCӠ Mpذ6ۦomsyYY[Z`Fc[quǶe;>+Ożצ|]X_Kֿ?b}|ƹm^{i?߆lǴlп [V_)n2!iTXtXML:com.adobe.xmp Adobe Fireworks CS5 11.0.1.7 Macintosh 2012-08-02T09:45:51Z 2012-08-02T10:04:53Z image/png 8>JIDATHKQ?[ڴtLעS̥V.mmZAYF""H"ԹǍVQ"zJ's2a7d i{~ha ?؞?wMRiiϱ-c[ LHuOkn3T_*RQQ%=v+Op nTNm],/vx٫&㟫3r<x`oZentyal created by eBox Technologies S.L." # footer.mas copyright_footer: "Zentyal created by eBox Technologies S.L."