From: Ben Klang Date: Sun, 10 Jan 2010 03:53:40 +0000 (-0500) Subject: Operator: Move files into proper application directory X-Git-Url: https://git.internetallee.de/?a=commitdiff_plain;h=6142bf4e7fa3b467203d90739bc08af9a68118bc;p=horde.git Operator: Move files into proper application directory --- diff --git a/COPYING b/COPYING deleted file mode 100644 index a6b67561a..000000000 --- a/COPYING +++ /dev/null @@ -1,280 +0,0 @@ - GNU GENERAL PUBLIC LICENSE - Version 2, June 1991 - - Copyright 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 diff --git a/README b/README deleted file mode 100644 index 6fa2b3e66..000000000 --- a/README +++ /dev/null @@ -1,95 +0,0 @@ -What is Operator? -================= - -:Last update: $Date: 2008/04/19 01:26:06 $ -:Revision: $Revision: 1.1 $ - -.. contents:: Contents -.. section-numbering:: - -Operator is an application to read call detail records out of an SQL database -and allow the user to view those records. - -Today it is a read-only user interface with very limited search capabilities. -In the future the goal is to allow for sorting by arbitrary fields, more -advanced search criteria, exporting of search results, per-account-code -security (to allow for CDR self-service), rule-based call rating and more. - -This software is OSI Certified Open Source Software. OSI Certified is a -certification mark of the `Open Source Initiative`_. - -.. _`Open Source Initiative`: http://www.opensource.org/ - - -Obtaining Operator ------------------- - -Further information on Operator and the latest version can be obtained at - - http://www.horde.org/operator/ - -But not until it leaves the incubator... - - -Documentation -------------- - -The following documentation is available in the Operator distribution: - -:README_: This file -:COPYING_: Copyright and license information -:LICENSE_: Copyright and license information -:`docs/CHANGES`_: Changes by release -:`docs/CREDITS`_: Project developers -:`docs/INSTALL`_: Installation instructions and notes -:`docs/TODO`_: Development TODO list -:`docs/UPGRADING`_: Pointers on upgrading from previous Operator versions - - -Installation ------------- - -Instructions for installing Operator can be found in the file INSTALL_ in the -``docs/`` directory of the Operator distribution. - - -Assistance ----------- - -If you encounter problems with Operator, help is available! - -The Horde Frequently Asked Questions List (FAQ), available on the Web at - - http://www.horde.org/faq/ - -The Horde Project runs a number of mailing lists, for individual applications -and for issues relating to the project as a whole. Information, archives, and -subscription information can be found at - - http://www.horde.org/mail/ - -Lastly, Horde developers, contributors and users also make occasional -appearances on IRC, on the channel #horde on the freenode Network -(irc.freenode.net). - - -Licensing ---------- - -For licensing and copyright information, please see the file COPYING_/LICENSE_ -in the Operator distribution. - -Thanks, - -The Operator team - - -.. _README: ?f=README.html -.. _COPYING: http://www.horde.org/licenses/gpl.php -.. _LICENSE: http://www.horde.org/licenses/asl.php -.. _docs/CHANGES: ?f=CHANGES.html -.. _docs/CREDITS: ?f=CREDITS.html -.. _INSTALL: -.. _docs/INSTALL: ?f=INSTALL.html -.. _docs/TODO: ?f=TODO.html -.. _docs/UPGRADING: ?f=UPGRADING.html diff --git a/config/.cvsignore b/config/.cvsignore deleted file mode 100644 index 51adefac7..000000000 --- a/config/.cvsignore +++ /dev/null @@ -1,3 +0,0 @@ -conf.php -conf.bak.php -prefs.php diff --git a/config/conf.xml b/config/conf.xml deleted file mode 100644 index 3d96910c2..000000000 --- a/config/conf.xml +++ /dev/null @@ -1,28 +0,0 @@ - - - - - Storage System Settings - asterisksql - - - - cdr - - - - - - - - - - Menu Settings - - - - - - - diff --git a/config/prefs.php.dist b/config/prefs.php.dist deleted file mode 100644 index 2c5160e94..000000000 --- a/config/prefs.php.dist +++ /dev/null @@ -1,57 +0,0 @@ - _("Options"), - 'label' => _("Display Preferences"), - 'desc' => _("Set default display parameters."), - 'members' => array('rowsperpage', 'resultlimit', 'columns') -); - -$_prefs['rowsperpage'] = array( - 'value' => 100, - 'locked' => false, - 'shared' => false, - 'type' => 'number', - 'desc' => _("The columns to be displayed on the Call Detail Review screen") -); - -$_prefs['resultlimit'] = array( - 'value' => 100, - 'locked' => false, - 'shared' => false, - 'type' => 'number', - 'desc' => _("Limit the number of CDR results when searching to this number") -); - -$_prefs['columns'] = array( - 'value' => 'a:18:{i:0;s:11:"accountcode";i:1;s:3:"src";i:2;s:3:"dst";i:3;s:8:"dcontext";i:4;s:4:"clid";i:5;s:7:"channel";i:6;s:10:"dstchannel";i:7;s:7:"lastapp";i:8;s:8:"lastdata";i:9;s:5:"start";i:10;s:6:"answer";i:11;s:3:"end";i:12;s:8:"duration";i:13;s:7:"billsec";i:14;s:11:"disposition";i:15;s:8:"amaflags";i:16;s:9:"userfield";i:17;s:8:"uniqueid";}', - 'locked' => false, - 'shared' => false, - 'type' => 'multienum', - 'enum' => array( - 'accountcode' => _("Account Code"), - 'src' => _("Source"), - 'dst' => _("Destination"), - 'dcontext' => _("Destination Context"), - 'clid' => _("Caller ID"), - 'channel' => _("Channel"), - 'dstchannel' => _("Destination Channel"), - 'lastapp' => _("Last Application"), - 'lastdata' => _("Last Application Data"), - 'start' => _("Call Start Time"), - 'answer' => _("Call Answer Time"), - 'end' => _("Call End Time"), - 'duration' => _("Call Duration"), - 'billsec' => _("Billing Time (seconds)"), - 'disposition' => _("Call Disposition"), - 'amaflags' => _("AMA Flags"), - 'userfield' => _("User Defined Field"), - 'uniqueid' => _("Call Unique ID")), - 'desc' => _("The columns to be displayed on the Call Detail Review screen") -); - diff --git a/docs/CHANGES b/docs/CHANGES deleted file mode 100644 index 141c0cc98..000000000 --- a/docs/CHANGES +++ /dev/null @@ -1,5 +0,0 @@ ---- -0.1 ---- - -[xyz] Initial Release diff --git a/docs/CREDITS b/docs/CREDITS deleted file mode 100644 index 77f1093ee..000000000 --- a/docs/CREDITS +++ /dev/null @@ -1,26 +0,0 @@ -=========================== - Operator Development Team -=========================== - - -Core Developers -=============== - -Ben Klang - - - -Drivers -======= - - - -Localization -============ - -===================== ====================================================== -===================== ====================================================== - - -Contributions -============= diff --git a/docs/INSTALL b/docs/INSTALL deleted file mode 100644 index 898af4e68..000000000 --- a/docs/INSTALL +++ /dev/null @@ -1,243 +0,0 @@ -========================= - Installing Skeleton 1.0 -========================= - -:Last update: $Date: 2008/04/19 01:26:06 $ -:Revision: $Revision: 1.1 $ - -.. contents:: Contents -.. section-numbering:: - -This document contains instructions for installing the Skeleton ... - -For information on the capabilities and features of Skeleton, see the file -README_ in the top-level directory of the Skeleton distribution. - - -Obtaining Skeleton -================== - -Skeleton can be obtained from the Horde website and FTP server, at - - http://www.horde.org/skeleton/ - - ftp://ftp.horde.org/pub/skeleton/ - -Or use the mirror closest to you: - - http://www.horde.org/mirrors.php - -Bleeding-edge development versions of Skeleton are available via CVS; see the -file `horde/docs/HACKING`_ in the Horde distribution, or the website -http://www.horde.org/source/, for information on accessing the Horde CVS -repository. - - -Prerequisites -============= - -To function properly, Skeleton **requires** the following: - -1. A working Horde installation. - - Skeleton runs within the `Horde Application Framework`_, a set of common - tools for Web applications written in PHP. You must install Horde before - installing Skeleton. - - .. Important:: Skeleton 1.0 requires version 3.0+ of the Horde Framework - - earlier versions of Horde will **not** work. - - .. _`Horde Application Framework`: http://www.horde.org/horde/ - - The Horde Framework can be obtained from the Horde website and FTP server, - at - - http://www.horde.org/horde/ - - ftp://ftp.horde.org/pub/horde/ - - Many of Skeleton's prerequisites are also Horde prerequisites. - - .. Important:: Be sure to have completed all of the steps in the - `horde/docs/INSTALL`_ file for the Horde Framework before - installing Skeleton. - -2. The following PHP capabilities: - - a. FOO support ``--with-foo`` [OPTIONAL] - - Description of Foo and what it is used for. - -3. The following PEAR packages: - (See `horde/docs/INSTALL`_ for instructions on installing PEAR packages) - - a. PEAR_Package x.x.x [OPTIONAL] - - Skeleton uses the Foo_Bar class for... - -4. The following PECL modules: - (See `horde/docs/INSTALL`_ for instructions on installing PECL modules) - - a. pecl_package x.x.x [OPTIONAL] - - pecl_package is required to... - -5. Something else. - -The following items are not required, but are strongly **recommended**: - -1. Yet something else. - - -Installing Skeleton -=================== - -Skeleton is written in PHP, and must be installed in a web-accessible -directory. The precise location of this directory will differ from system to -system. Conventionally, Skeleton is installed directly underneath Horde in the -web server's document tree. - -Since Skeleton is written in PHP, there is no compilation necessary; simply -expand the distribution where you want it to reside and rename the root -directory of the distribution to whatever you wish to appear in the URL. For -example, with the Apache web server's default document root of -``/usr/local/apache/htdocs``, you would type:: - - cd /usr/local/apache/htdocs/horde - tar zxvf /path/to/skeleton-h3-x.y.z.tar.gz - mv skeleton-h3-x.y.z skeleton - -and would then find Skeleton at the URL:: - - http://your-server/horde/skeleton/ - - -Configuring Skeleton -==================== - -1. Configuring Horde for Skeleton - - a. Register the application - - In ``horde/config/registry.php``, find the ``applications['skeleton']`` - stanza. The default settings here should be okay, but you can change - them if desired. If you have changed the location of Skeleton relative - to Horde, either in the URL, in the filesystem or both, you must update - the ``fileroot`` and ``webroot`` settings to their correct values. - -2. Creating the database tables - - The specific steps to create Skeleton's database tables depend on which - database you've chosen to use. - - First, look in ``scripts/sql/`` to see if a script already exists for your - database type. If so, you should be able to simply execute that script as - superuser in your database. (Note that executing the script as the "horde" - user will probably fail when granting privileges.) - - If such a script does not exist, you'll need to build your own, using the - file ``skeleton.sql`` as a starting point. If you need assistance in - creating database tables, you may wish to let us know on the Skeleton - mailing list. - - You will also need to make sure that the "horde" user in your database has - table-creation privileges, so that the tables that `PEAR DB`_ uses to - provide portable sequences can be created. - - .. _`PEAR DB`: http://pear.php.net/DB - -3. Configuring Skeleton - - To configure Skeleton, change to the ``config/`` directory of the installed - distribution, and make copies of all of the configuration ``dist`` files - without the ``dist`` suffix:: - - cd config/ - for foo in *.dist; do cp $foo `basename $foo .dist`; done - - Or on Windows:: - - copy *.dist *. - - Documentation on the format and purpose of those files can be found in each - file. You may edit these files if you wish to customize Skeleton's - appearance and behavior. With one exception (``foo.php``) the defaults will - be correct for most sites. - - You must login to Horde as a Horde Administrator to finish the - configuration of Skeleton. Use the Horde ``Administration`` menu item to - get to the administration page, and then click on the ``Configuration`` - icon to get the configuration page. Select ``Skeleton Name`` from the - selection list of applications. Fill in or change any configuration values - as needed. When done click on ``Generate Skeleton Name Configuration`` to - generate the ``conf.php`` file. If your web server doesn't have write - permissions to the Skeleton configuration directory or file, it will not be - able to write the file. In this case, go back to ``Configuration`` and - choose one of the other methods to create the configuration file - ``skeleton/config/conf.php``. - - Note for international users: Skeleton uses GNU gettext to provide local - translations of text displayed by applications; the translations are found - in the ``po/`` directory. If a translation is not yet available for your - locale (and you wish to create one), see the ``horde/po/README`` file, or - if you're having trouble using a provided translation, please see the - `horde/docs/TRANSLATIONS`_ file for instructions. - -4. More instructions, upgrading, securing, etc. - -5. Testing Skeleton - - Once you have configured Skeleton, bring up the included test page in your - Web browser to ensure that all necessary prerequisites have been met. See - the `horde/docs/INSTALL`_ document for further details on Horde test - scripts. If you installed Skeleton as described above, the URL to the test - page would be:: - - http://your-server/horde/skeleton/test.php - - The test script will also allow you to test... - - Next, use Skeleton to.... Test at least the following: - - - Foo - - Bar - - -Known Problems -============== - -... - - -Obtaining Support -================= - -If you encounter problems with Skeleton, help is available! - -The Horde Frequently Asked Questions List (FAQ), available on the Web at - - http://www.horde.org/faq/ - -The Horde Project runs a number of mailing lists, for individual applications -and for issues relating to the project as a whole. Information, archives, and -subscription information can be found at - - http://www.horde.org/mail/ - -Lastly, Horde developers, contributors and users may also be found on IRC, -on the channel #horde on the Freenode Network (irc.freenode.net). - -Please keep in mind that Skeleton is free software written by volunteers. -For information on reasonable support expectations, please read - - http://www.horde.org/support.php - -Thanks for using Skeleton! - -The Skeleton team - - -.. _README: ?f=README.html -.. _`horde/docs/HACKING`: ../../horde/docs/?f=HACKING.html -.. _`horde/docs/INSTALL`: ../../horde/docs/?f=INSTALL.html -.. _`horde/docs/TRANSLATIONS`: ../../horde/docs/?f=TRANSLATIONS.html diff --git a/docs/RELEASE_NOTES b/docs/RELEASE_NOTES deleted file mode 100644 index d327d5bd3..000000000 --- a/docs/RELEASE_NOTES +++ /dev/null @@ -1,49 +0,0 @@ -notes['fm']['focus'] = 4; - -/* Mailing list release notes. */ -$this->notes['ml']['changes'] = <<notes['fm']['changes'] = <<notes['name'] = 'Skeleton'; -$this->notes['fm']['project'] = 'skeleton'; -$this->notes['fm']['branch'] = 'Default'; diff --git a/docs/TODO b/docs/TODO deleted file mode 100644 index fffa4329a..000000000 --- a/docs/TODO +++ /dev/null @@ -1,8 +0,0 @@ -================================ - Skeleton Development TODO List -================================ - -:Last update: $Date: 2008/04/19 01:26:06 $ -:Revision: $Revision: 1.1 $ - -- Example todo diff --git a/graphgen.php b/graphgen.php deleted file mode 100644 index 06e186df8..000000000 --- a/graphgen.php +++ /dev/null @@ -1,210 +0,0 @@ - - * - * See the enclosed file COPYING for license information (GPL). If you - * did not receive this file, see http://www.fsf.org/copyleft/gpl.html. - * - * @author Ben Klang - */ - -@define('OPERATOR_BASE', dirname(__FILE__)); -require_once OPERATOR_BASE . '/lib/base.php'; - -// Load PEAR's Image_Graph library -require_once 'Image/Graph.php'; - -#setlocale(LC_ALL, Horde_Nls::select()); -setlocale(LC_ALL, 'en_US'); - -$graphtype = Horde_Util::getFormData('graph'); -$graphinfo = Operator::getGraphInfo($graphtype); -$cachekey = Horde_Util::getFormData('key'); - -$stats = unserialize($cache->get($cachekey, 0)); - -// Create the graph image base. -if (empty($graphinfo['imageX'])) { - $graphinfo['imageX'] = 700; -} -if (empty($graphinfo['imageY'])) { - $graphinfo['imageY'] = 600; -} -if (!isset($graphinfo['charttype'])) { - $graphinfo['charttype'] = 'bar'; -} -if (!isset($graphinfo['markers'])) { - $graphinfo['markers'] = true; -} -if (!isset($graphinfo['legendsplit'])) { - $graphinfo['legendsplit'] = 90; -} - -$canvas =& Image_Canvas::factory('png', array('width' => $graphinfo['imageX'], 'height' => $graphinfo['imageY'], 'antialias' => true)); -$graph =& Image_Graph::factory('graph', $canvas); - -if (isset($graphinfo['orientation']) && - $graphinfo['orientation'] == 'horizontal') { - $graph->horizontal = true; -} else { - $graph->horizontal = false; -} - -if (!empty($conf['ttf_font'])) { - // add a TrueType font - $Font =& $graph->addNew('ttf_font', $conf['ttf_font']); - // set the font size to 11 pixels - $Font->setSize(8); - $graph->setFont($Font); -} - - -// create the plotarea layout -if ($graph->horizontal) { - $plotarea =& Image_Graph::factory('plotarea', array('Image_Graph_Axis_Category', 'Image_Graph_Axis', 'horizontal')); -} else { - $plotarea =& Image_Graph::factory('plotarea', array('Image_Graph_Axis_Category', 'Image_Graph_Axis', 'vertical')); -} - -$graph->add( - Image_Graph::vertical( - Image_Graph::factory('title', array($graphinfo['title'], 11)), - Image_Graph::vertical( - $plotarea, - $legend = Image_Graph::factory('legend'), - $graphinfo['legendsplit'] - ), - 5 - ) -); - -$plotarea->setAxisPadding(array('top' => 20)); - -// make the legend use the plotarea (or implicitly its plots) -$legend->setPlotarea($plotarea); - -// create a grid and assign it to the secondary Y axis -$gridY2 =& $plotarea->addNew('line_grid', IMAGE_GRAPH_AXIS_Y_SECONDARY); -#$gridY2->setLineColor('black'); -#$gridY2->setFillStyle( -# Image_Graph::factory( -# 'gradient', -# array(IMAGE_GRAPH_GRAD_HORIZONTAL, 'white', 'lightgrey') -# ) -#); - -$linecolor = 0x000042; -$increment = 0x173147; -foreach ($stats[$graphtype] as $title => $data) { - $lcstring = sprintf('#%06x', $linecolor); - $linecolor += $increment; - if ($linecolor >= 0x5555555) { - $linecolor = $linecolor & 0xFFFFFF; - $linecolor += $increment; - } - - if ($graph->horizontal) { - // Horizontal graphs reverse the data points - $data = array_reverse($data, true); - } - - $dataset = Image_Graph::factory('dataset'); - foreach ($data as $expr => $value) { - $dataset->addPoint($expr, $value); - } - $plot =& $plotarea->addNew($graphinfo['charttype'], $dataset); - $plot->setLineColor($lcstring); - $plot->setFillColor($lcstring . '@0.5'); - //$plot->setFillColor('blue@0.2'); - $plot->setTitle($title); - - if ($graphinfo['markers']) { - $marker =& $plot->addNew('Image_Graph_Marker_Value', IMAGE_GRAPH_VALUE_Y); - // create a pin-point marker type - if ($graph->horizontal) { - $PointingMarker =& $plot->addNew('Image_Graph_Marker_Pointing', array(-37, 0, $marker)); - } else { - $PointingMarker =& $plot->addNew('Image_Graph_Marker_Pointing', array(0, -7, $marker)); - } - $PointingMarker->setLineColor(false); - $marker->setBorderColor(false); - $marker->setFillColor(false); - // and use the marker on the 1st plot - $plot->setMarker($PointingMarker); - - #if (!empty($graphinfo['numberformat'])) { - # $marker->setDataPreprocessor(Image_Graph::factory('Image_Graph_DataPreprocessor_Formatted', $graphinfo['numberformat'])); - #} - $marker->setDataPreprocessor(Image_Graph::factory('Image_Graph_DataPreprocessor_Function', '_format')); - $marker->setFontSize(7.5); - } -} - -// create an area plot using a random dataset -#$dataset2 =& Image_Graph::factory('random', array(8, 1, 10, true)); -#$plot2 =& $plotarea->addNew( -# 'Image_Graph_Plot_Area', -# $dataset2, -# IMAGE_GRAPH_AXIS_Y_SECONDARY -#); - -#$plot2->setLineColor('gray'); -#$plot2->setFillColor('blue@0.2'); -#$plot2->setTitle('Secondary Axis'); - -$axisX =& $plotarea->getAxis(IMAGE_GRAPH_AXIS_X); -$axisY =& $plotarea->getAxis(IMAGE_GRAPH_AXIS_Y); -if ($graph->horizontal) { - $axisX->setTitle($graphinfo['axisX'], 'vertical'); - $axisY->setTitle($graphinfo['axisY'], 'horizontal'); -} else { - $axisX->setTitle($graphinfo['axisX'], 'horizontal'); - $axisY->setTitle($graphinfo['axisY'], 'vertical'); -} -$axisY->setDataPreprocessor(Image_Graph::factory('Image_Graph_DataPreprocessor_Function', 'axis2human')); -#$axisYsecondary =& $plotarea->getAxis(IMAGE_GRAPH_AXIS_Y_SECONDARY); -#$axisYsecondary->setTitle('Pears', 'vertical2'); - -// output the Graph -$graph->done(); -exit; - -function _format($number) -{ - if (($number - (int)$number) == 0) { - return money_format('%!.0n', $number); - } else { - return money_format('%!.2n', $number); - } -} - -function number2human($number, $showCurrency = true) -{ - $currency = ''; - $suffix = ''; - if ($showCurrency) { - //$currency = 'ISK '; - $currency = ''; - } - - if (abs($number) >= 500000000) { - $number = $number / 1000000000; - $suffix = 'T'; - } - if (abs($number) >= 500000) { - $number = $number / 1000000; - $suffix = 'M'; - } - if (abs($number) >= 500) { - $number = $number / 1000; - $suffix = 'K'; - } - return $currency . _format($number) . $suffix; -} - -function axis2human($number) -{ - return number2human($number, false); -} diff --git a/index.php b/index.php deleted file mode 100644 index bb539f4c6..000000000 --- a/index.php +++ /dev/null @@ -1,22 +0,0 @@ - - */ - -@define('OPERATOR_BASE', dirname(__FILE__)); -$operator_configured = (is_readable(OPERATOR_BASE . '/config/conf.php')); - -if (!$operator_configured) { - require OPERATOR_BASE . '/../lib/Test.php'; - Horde_Test::configFilesMissing('Operator', OPERATOR_BASE, - array('conf.php')); -} - -require OPERATOR_BASE . '/viewgraph.php'; diff --git a/lib/Driver.php b/lib/Driver.php deleted file mode 100644 index 59b6d522e..000000000 --- a/lib/Driver.php +++ /dev/null @@ -1,125 +0,0 @@ - - * @package Operator - */ -class Operator_Driver { - - /** - * Search the database for call detail records, taking permissions into - * consideration. - * - * @return boolean|PEAR_Error True on success, PEAR_Error on failure. - */ - function getRecords($start, $end, $accountcode = null, $dcontext = null, - $rowstart = 0, $rowlimit = 100) - { - if (empty($accountcode) || $accountcode == '%') { - $permentry = 'operator:accountcodes'; - } else { - $permentry = 'operator:accountcodes:' . $accountcode; - } - if (Horde_Auth::isAdmin() || - $GLOBALS['perms']->hasPermission('operator:accountcodes', - Horde_Auth::getAuth(), - Horde_Perms::READ) || - $GLOBALS['perms']->hasPermission($permentry, Horde_Auth::getAuth(), - Horde_Perms::READ)) { - return $this->_getRecords($start, $end, $accountcode, $dcontext, - $rowstart, $rowlimit); - } - return PEAR::raiseError(_("You do not have permission to view call detail records for that account code.")); - } - - /** - * Get summary call statistics per-month for a given time range, account and - * destination. - * - * @param Horde_Date startdate Start of the statistics window - * @param Horde_Date enddate End of the statistics window - * @param string accountcode Name of the accont for statistics. Defaults - * to null meaning all accounts. - * @param string dcontext Destination of calls. Defaults to null. - * - * - * @return array|PEAR_Error Array of call statistics. The key of each - * element is the month name in date('Y-m') - * format and the value being an array of - * statistics for calls placed that month. This - * method will additionall return PEAR_Error - * on failure. - */ - function getMonthlyCallStats($start, $end, $accountcode = null, - $dcontext = null){ - if (empty($accountcode) || $accountcode == '%') { - $permentry = 'operator:accountcodes'; - } else { - $permentry = 'operator:accountcodes:' . $accountcode; - } - if (Horde_Auth::isAdmin() || - $GLOBALS['perms']->hasPermission('operator:accountcodes', - Horde_Auth::getAuth(), - Horde_Perms::READ) || - $GLOBALS['perms']->hasPermission($permentry, Horde_Auth::getAuth(), - Horde_Perms::READ)) { - return $this->_getMonthlyCallStats($start, $end, $accountcode, - $dcontext); - } - - return PEAR::raiseError(_("You do not have permission to view call detail records for that account code.")); - } - - /** - * Attempts to return a concrete Operator_Driver instance based on $driver. - * - * @param string $driver The type of the concrete Operator_Driver subclass - * to return. The class name is based on the - * storage driver ($driver). The code is - * dynamically included. - * - * @param array $params A hash containing any additional configuration - * or connection parameters a subclass might need. - * - * @return Operator_Driver The newly created concrete Operator_Driver - * instance, or false on an error. - */ - function factory($driver = null, $params = null) - { - if ($driver === null) { - $driver = $GLOBALS['conf']['storage']['driver']; - } - $driver = basename($driver); - - if (is_null($params)) { - // Since we have more than one backend that uses SQL make sure - // all of them have a chance to inherit the site-wide config. - $sqldrivers = array('sql', 'asterisksql'); - if (in_array($driver, $sqldrivers)) { - $params = Horde::getDriverConfig('storage', 'sql'); - } else { - $params = Horde::getDriverConfig('storage', $driver); - } - } - - $class = 'Operator_Driver_' . $driver; - if (!class_exists($class)) { - include dirname(__FILE__) . '/Driver/' . $driver . '.php'; - } - if (class_exists($class)) { - return new $class($params); - } else { - return false; - } - } - -} diff --git a/lib/Driver/asterisksql.php b/lib/Driver/asterisksql.php deleted file mode 100644 index d7c9fa49e..000000000 --- a/lib/Driver/asterisksql.php +++ /dev/null @@ -1,444 +0,0 @@ - - * 'phptype' The database type (e.g. 'pgsql', 'mysql', etc.). - * 'table' The name of the foo table in 'database'. - * 'charset' The database's internal charset. - * - * Required by some database implementations:
- *      'database'      The name of the database.
- *      'hostspec'      The hostname of the database server.
- *      'protocol'      The communication protocol ('tcp', 'unix', etc.).
- *      'username'      The username with which to connect to the database.
- *      'password'      The password associated with 'username'.
- *      'options'       Additional options to pass to the database.
- *      'tty'           The TTY on which to connect to the database.
- *      'port'          The port on which to connect to the database.
- * - * The table structure can be created by the scripts/sql/operator_foo.sql - * script. - * - * $Horde: incubator/operator/lib/Driver/asterisksql.php,v 1.12 2009/05/31 17:14:08 bklang Exp $ - * - * Copyright 2008-2009 The Horde Project (http://www.horde.org/) - * - * See the enclosed file COPYING for license information (GPL). If you - * did not receive this file, see http://www.fsf.org/copyleft/gpl.html. - * - * @author Your Name - * @package Operator - */ -class Operator_Driver_asterisksql extends Operator_Driver { - - /** - * Hash containing connection parameters. - * - * @var array - */ - var $_params = array(); - - /** - * Handle for the current database connection. - * - * @var DB - */ - var $_db; - - /** - * Handle for the current database connection, used for writing. Defaults - * to the same handle as $_db if a separate write database is not required. - * - * @var DB - */ - var $_write_db; - - /** - * Boolean indicating whether or not we're connected to the SQL server. - * - * @var boolean - */ - var $_connected = false; - - /** - * Constructs a new SQL storage object. - * - * @param array $params A hash containing connection parameters. - */ - function Operator_Driver_asterisksql($params = array()) - { - $this->_params = $params; - } - - /** - * Get call detail records from the database - * - * @return boolean|PEAR_Error True on success, PEAR_Error on failure. - */ - function _getRecords($start, $end, $accountcode = null, $dcontext = null, - $rowstart = 0, $rowlimit = 100) - { - - // Use the query to make the MySQL driver look like the CDR-CSV driver - $sql = 'SELECT accountcode, src, dst, dcontext, clid, channel, ' . - 'dstchannel, lastapp, lastdata, calldate AS start, ' . - 'calldate AS answer, calldate AS end, duration, ' . - 'billsec, disposition, amaflags, userfield, uniqueid ' . - ' FROM ' . $this->_params['table'] . ' WHERE %s'; - $filter = array(); - $values = array(); - - if (!is_numeric($rowstart)) { - Horde::logMessage('Invalid start row requested.', __FILE__, __LINE__, PEAR_LOG_ERR); - return PEAR::raiseError(_("Internal error. Details have been logged for the administrator.")); - } - if (!is_numeric($rowlimit)) { - Horde::logMessage('Invalid row limit requested.', __FILE__, __LINE__, PEAR_LOG_ERR); - return PEAR::raiseError(_("Internal error. Details have been logged for the administrator.")); - } - - - // Start Date - if (!is_a($start, 'Horde_Date')) { - $start = new Horde_Date($start); - if (is_a($start, 'PEAR_Error')) { - return $start; - } - } - $filter[] = 'calldate >= ?'; - $values[] = $start->strftime('%Y-%m-%d %T'); - - // End Date - if (!is_a($end, 'Horde_Date')) { - $end = new Horde_Date($end); - if (is_a($end, 'PEAR_Error')) { - return $end; - } - } - $filter[] = 'calldate < ?'; - $values[] = $end->strftime('%Y-%m-%d %T'); - - // Filter by account code - if ($accountcode !== null) { - $filter[] = 'accountcode LIKE ?'; - $values[] = $accountcode; - } else { - $filter[] = 'accountcode = ""'; - } - - // Filter by destination context - if ($dcontext !== null) { - $filter[] = 'dcontext LIKE ?'; - $values[] = $dcontext; - } else { - $filter[] = 'dcontext = ""'; - } - - /* Make sure we have a valid database connection. */ - $this->_connect(); - - $filterstring = implode(' AND ', $filter); - $sql = sprintf($sql, $filterstring); - /* Log the query at a DEBUG log level. */ - Horde::logMessage(sprintf('Operator_Driver_asterisksql::getData(): %s', $sql), __FILE__, __LINE__, PEAR_LOG_DEBUG); - - /* Execute the query. */ - $res = $this->_db->limitQuery($sql, $rowstart, $rowlimit, $values); - if (is_a($res, 'PEAR_Error')) { - Horde::logMessage($res, __FILE__, __LINE__, PEAR_LOG_ERR); - return PEAR::raiseError(_("Internal error. Details have been logged for the administrator.")); - } - - $data = array(); - while ($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) { - $data[] = $row; - } - - // Get summary statistics on the requested criteria - $sql = 'SELECT COUNT(*) AS numcalls, SUM(duration)/60 AS minutes, ' . - 'SUM(CASE disposition WHEN "FAILED" THEN 1 ELSE 0 END) AS ' . - 'failed FROM ' . $this->_params['table'] . ' WHERE %s'; - $sql = sprintf($sql, $filterstring); - Horde::logMessage(sprintf('Operator_Driver_asterisksql::getData(): %s', $sql), __FILE__, __LINE__, PEAR_LOG_DEBUG); - - /* Execute the query. */ - $res = $this->_db->getRow($sql, $values, DB_FETCHMODE_ASSOC); - if (is_a($res, 'PEAR_Error')) { - Horde::logMessage($res, __FILE__, __LINE__, PEAR_LOG_ERR); - return PEAR::raiseError(_("Internal error. Details have been logged for the administrator.")); - } - - return array_merge($data, $res); - } - - /** - * Get summary call statistics per-month for a given time range, account and - * destination. - * - * @param Horde_Date startdate Start of the statistics window - * @param Horde_Date enddate End of the statistics window - * @param string accountcode Name of the accont for statistics. Defaults - * to null meaning all accounts. - * @param string dcontext Destination of calls. Defaults to null. - * - * - * @return array|PEAR_Error Array of call statistics. The key of each - * element is the month name in date('Y-m') - * format and the value being an array of - * statistics for calls placed that month. This - * method will additionall return PEAR_Error - * on failure. - */ - function _getMonthlyCallStats($start, $end, $accountcode = null, - $dcontext = null) - { - if (!is_a($start, 'Horde_Date') || !is_a($end, 'Horde_Date')) { - Horde::logMessage('Start ane end date must be Horde_Date objects.', __FILE__, __LINE__, PEAR_LOG_ERR); - return PEAR::raiseError(_("Internal error. Details have been logged for the administrator.")); - } - - /* Make sure we have a valid database connection. */ - $this->_connect(); - - // Construct the queries we will be running below - // Use 1=1 to make constructing the filter string easier - $numcalls_query = 'SELECT MONTH(calldate) AS month, ' . - 'YEAR(calldate) AS year, ' . - 'COUNT(*) AS numcalls FROM ' . - $this->_params['table'] . ' WHERE %s ' . - 'GROUP BY year, month'; - - $minutes_query = 'SELECT MONTH(calldate) AS month, ' . - 'YEAR(calldate) AS year, ' . - 'SUM(duration)/60 AS minutes FROM ' . - $this->_params['table'] . ' WHERE %s ' . - 'GROUP BY year, month'; - - $failed_query = 'SELECT MONTH(calldate) AS month, ' . - 'YEAR(calldate) AS year, ' . - 'COUNT(disposition) AS failed FROM ' . - $this->_params['table'] . ' ' . - 'WHERE disposition="failed" AND %s ' . - 'GROUP BY year, month'; - - // Shared SQL filter - $filter = array(); - $values = array(); - - // Filter by account code - if ($accountcode !== null) { - $filter[] = 'accountcode LIKE ?'; - $values[] = $accountcode; - } else { - $filter[] = 'accountcode = ""'; - } - - // Filter by destination context - if ($dcontext !== null) { - $filter[] = 'dcontext LIKE ?'; - $values[] = $dcontext; - } else { - $filter[] = 'dcontext = ""'; - } - - // Filter by the date range (filled in below) - $filter[] = 'calldate >= ?'; - $values[] = $start->strftime('%Y-%m-%d %T'); - $filter[] = 'calldate < ?'; - $values[] = $end->strftime('%Y-%m-%d %T'); - - $filterstring = implode(' AND ', $filter); - - $stats = array(); - - /* Log the query at a DEBUG log level. */ - $sql = sprintf($numcalls_query, $filterstring); - Horde::logMessage(sprintf('Operator_Driver_asterisksql::getCallStats(): Values: %s', print_r($values, true)), __FILE__, __LINE__, PEAR_LOG_DEBUG); - Horde::logMessage(sprintf('Operator_Driver_asterisksql::getCallStats(): %s', $sql), __FILE__, __LINE__, PEAR_LOG_DEBUG); - $numcalls_res = $this->_db->getAll($sql, $values, DB_FETCHMODE_ASSOC); - if (is_a($numcalls_res, 'PEAR_Error')) { - Horde::logMessage($numcalls_res, __FILE__, __LINE__, PEAR_LOG_ERR); - return PEAR::raiseError(_("Internal error. Details have been logged for the administrator.")); - } - - $sql = sprintf($minutes_query, $filterstring); - Horde::logMessage(sprintf('Operator_Driver_asterisksql::getCallStats(): %s', $sql), __FILE__, __LINE__, PEAR_LOG_DEBUG); - $minutes_res = $this->_db->getAll($sql, $values, DB_FETCHMODE_ASSOC); - if (is_a($minutes_res, 'PEAR_Error')) { - Horde::logMessage($minutes_res, __FILE__, __LINE__, PEAR_LOG_ERR); - return PEAR::raiseError(_("Internal error. Details have been logged for the administrator.")); - } - - $sql = sprintf($failed_query, $filterstring); - Horde::logMessage(sprintf('Operator_Driver_asterisksql::getCallStats(): %s', $sql), __FILE__, __LINE__, PEAR_LOG_DEBUG); - $failed_res = $this->_db->getAll($sql, $values, DB_FETCHMODE_ASSOC); - if (is_a($failed_res, 'PEAR_Error')) { - Horde::logMessage($failed_res, __FILE__, __LINE__, PEAR_LOG_ERR); - return PEAR::raiseError(_("Internal error. Details have been logged for the administrator.")); - } - - // Normalize the results from the database. This is done because - // the database will not return values if there are no data that match - // the query. For example if there were no calls in the month of June - // the results will not have any rows with data for June. Instead of - // searching through the results for each month we stuff the values we - // have into a temporary array and then create the return value below - // using 0 values where necessary. - $numcalls = array(); - foreach ($numcalls_res as $row) { - $numcalls[$row['year']][$row['month']] = $row['numcalls']; - } - $minutes = array(); - foreach ($minutes_res as $row) { - $minutes[$row['year']][$row['month']] = $row['minutes']; - } - $failed = array(); - foreach ($failed_res as $row) { - $failed[$row['year']][$row['month']] = $row['failed']; - } - - $s_numcalls = array(); - $s_minutes = array(); - $s_failed = array(); - while($start->compareDate($end) <= 0) { - $index = $start->strftime('%Y-%m'); - $year = $start->year; - $month = $start->month; - - if (empty($numcalls[$year]) || empty($numcalls[$year][$month])) { - $s_numcalls[$index] = 0; - } else { - $s_numcalls[$index] = $numcalls[$year][$month]; - } - - if (empty($minutes[$year]) || empty($minutes[$year][$month])) { - $s_minutes[$index] = 0; - } else { - $s_minutes[$index] = $minutes[$year][$month]; - } - - if (empty($failed[$year]) || empty($failed[$year][$month])) { - $s_failed[$index] = 0; - } else { - $s_failed[$index] = $failed[$year][$month]; - } - - // Find the first day of the next month - $start->month++; - $start->correct(); - } - - $info = Operator::getGraphInfo('numcalls'); - $stats['numcalls'] = array($info['title'] => $s_numcalls); - $info = Operator::getGraphInfo('minutes'); - $stats['minutes'] = array($info['title'] => $s_minutes); - $info = Operator::getGraphInfo('failed'); - $stats['failed'] = array($info['title'] => $s_failed); - - return $stats; - } - - function getAccountCodes() - { - /* Make sure we have a valid database connection. */ - $this->_connect(); - - $sql = 'SELECT DISTINCT(accountcode) FROM ' . $this->_params['table'] . - ' ORDER BY accountcode'; - Horde::logMessage(sprintf('Operator_Driver_asterisksql::getAccountCodes(): %s', $sql), __FILE__, __LINE__, PEAR_LOG_DEBUG); - $res = $this->_db->getCol($sql, 'accountcode'); - if (is_a($res, 'PEAR_Error')) { - Horde::logMessage($res, __FILE__, __LINE__, PEAR_LOG_ERR); - return PEAR::raiseError(_("Internal error. Details have been logged for the administrator.")); - } - - return $res; - } - - /** - * Attempts to open a connection to the SQL server. - * - * @return boolean True on success; exits (Horde::fatal()) on error. - */ - function _connect() - { - if ($this->_connected) { - return true; - } - - Horde::assertDriverConfig($this->_params, 'storage', - array('phptype', 'charset', 'table')); - - if (!isset($this->_params['database'])) { - $this->_params['database'] = ''; - } - if (!isset($this->_params['username'])) { - $this->_params['username'] = ''; - } - if (!isset($this->_params['hostspec'])) { - $this->_params['hostspec'] = ''; - } - - /* Connect to the SQL server using the supplied parameters. */ - require_once 'DB.php'; - $this->_write_db = &DB::connect($this->_params, - array('persistent' => !empty($this->_params['persistent']))); - if (is_a($this->_write_db, 'PEAR_Error')) { - Horde::fatal($this->_write_db, __FILE__, __LINE__); - } - - // Set DB portability options. - switch ($this->_write_db->phptype) { - case 'mssql': - $this->_write_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS | DB_PORTABILITY_RTRIM); - break; - default: - $this->_write_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS); - } - - /* Check if we need to set up the read DB connection seperately. */ - if (!empty($this->_params['splitread'])) { - $params = array_merge($this->_params, $this->_params['read']); - $this->_db = &DB::connect($params, - array('persistent' => !empty($params['persistent']))); - if (is_a($this->_db, 'PEAR_Error')) { - Horde::fatal($this->_db, __FILE__, __LINE__); - } - - // Set DB portability options. - switch ($this->_db->phptype) { - case 'mssql': - $this->_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS | DB_PORTABILITY_RTRIM); - break; - default: - $this->_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS); - } - - } else { - /* Default to the same DB handle for the writer too. */ - $this->_db =& $this->_write_db; - } - - $this->_connected = true; - - return true; - } - - /** - * Disconnects from the SQL server and cleans up the connection. - * - * @return boolean True on success, false on failure. - */ - function _disconnect() - { - if ($this->_connected) { - $this->_connected = false; - $this->_db->disconnect(); - $this->_write_db->disconnect(); - } - - return true; - } - -} diff --git a/lib/Form/SearchCDR.php b/lib/Form/SearchCDR.php deleted file mode 100644 index 73421943b..000000000 --- a/lib/Form/SearchCDR.php +++ /dev/null @@ -1,69 +0,0 @@ - - * - * See the enclosed file LICENSE for license information (BSD). If you - * did not receive this file, see http://www.horde.org/licenses/bsdl.php. - * - * @author Ben Klang - * @package Operator - */ - -class SearchCDRForm extends Horde_Form { - - function SearchCDRForm($title, &$vars) - { - global $operator_driver; - - parent::Horde_Form($vars, $title); - - // FIXME: Generate a list of clients from Turba? - //$clients = - - $now = time(); - if (!$vars->exists('startdate')) { - // Default to the beginning of the previous calendar month - $startdate = array('day' => 1, - 'month' => date('n', $now) - 1, - 'year' => date('Y', $now), - 'hour' => 0, - 'minute' => 0, - 'second' => 0); - $vars->set('startdate', $startdate); - } - - if (!$vars->exists('enddate')) { - // Default to the end of the previous calendar month - $month = date('n', $now) - 1; - $year = date('Y', $now); - $lastday = Horde_Date_Utils::daysInMonth($month, $year); - $enddate = array('day' => $lastday, - 'month' => $month, - 'year' => $year, - 'hour' => 23, - 'minute' => 59, - 'second' => 59); - $vars->set('enddate', $enddate); - } - - - // Parameters for Horde_Form_datetime - $start_year = date('Y', $now) - 3; - $end_year = ''; - $picker = true; - $format_in = null; - $format_out = '%x'; - $show_seconds = true; - $params = array($start_year, $end_year, $picker, $format_in, - $format_out, $show_seconds); - - $this->addVariable(_("Account Code"), 'accountcode', 'enum', false, false, null, array(Operator::getAccountCodes(true))); - $this->addVariable(_("Destination Context"), 'dcontext', 'text', false, false, _("An empty destination context will match all destination contexts.")); - $this->addVariable(_("Start Date/Time"), 'startdate', 'datetime', true, false, null, $params); - $this->addVariable(_("End Date/Time"), 'enddate', 'datetime', true, false, null, $params); - } -} diff --git a/lib/Operator.php b/lib/Operator.php deleted file mode 100644 index a882ce9e6..000000000 --- a/lib/Operator.php +++ /dev/null @@ -1,171 +0,0 @@ - - * @package Operator - */ -class Operator { - - /** - * Build Operator's list of menu items. - */ - function getMenu($returnType = 'object') - { - global $conf, $registry, $browser, $print_link; - - $menu = new Horde_Menu(Horde_Menu::MASK_ALL); - $menu->add(Horde::applicationUrl('viewgraph.php'), _("View Graphs"), 'graphs.png', null, null, null, basename($_SERVER['PHP_SELF']) == 'index.php' ? 'current' : null); - $menu->add(Horde::applicationUrl('search.php'), _("Search"), 'search.png', $registry->getImageDir('horde')); - - if ($returnType == 'object') { - return $menu; - } else { - return $menu->render(); - } - } - - function getColumns() - { - #static $columns = array( - $columns = array( - 'accountcode' => _("Account Code"), - 'src' => _("Source"), - 'dst' => _("Destination"), - 'dcontext' => _("Destination Context"), - 'clid' => _("Caller ID"), - 'channel' => _("Channel"), - 'dstchannel' => _("Destination Channel"), - 'lastapp' => _("Last Application"), - 'lastdata' => _("Last Application Data"), - 'start' => _("Call Start Time"), - 'answer' => _("Call Answer Time"), - 'end' => _("Call End Time"), - 'duration' => _("Call Duration (seconds)"), - 'billsec' => _("Billable Call Duration (seconds)"), - 'disposition' => _("Call Disposition"), - 'amaflags' => _("AMA Flag"), - 'userfield' => _("User Defined Field"), - 'uniqueid' => _("Call Unique ID")); - - return $columns; - } - - - function getColumnName($column) - { - $columns = Operator::getColumns(); - return $columns[$column]; - } - - function getAMAFlagName($flagid) - { - // See for definitions - switch($flagid) { - case 1: - return _("OMIT"); - break; - case 2: - return _("BILLING"); - break; - case 3: - return _("DOCUMENTATION"); - break; - } - } - - /** - * Get a list of valid account codes from the database - * - * @return array List of valid account codes. - */ - function getAccountCodes($permfilter = false) - { - global $operator_driver; - - $accountcodes = $operator_driver->getAccountCodes(); - - if (Horde_Auth::isAdmin() || - $GLOBALS['perms']->hasPermission('operator:accountcodes', - Horde_Auth::getAuth(), - Horde_Perms::READ)) { - $permfilter = false; - } - - if (!$permfilter || - $GLOBALS['perms']->hasPermission('operator:accountcodes:%', - Horde_Auth::geAuth(), - Horde_Perms::READ)) { - - // Add an option to select all accounts - $keys = $accountcodes; - array_unshift($keys, '%'); - $values = $accountcodes; - array_unshift($values, _("-- All Accounts Combined --")); - } - - // Only add the Empty value if it is exists in the backend - if ($index = array_search('', $values)) { - $values[$index] = _("-- Empty Accountcode --"); - } - - // Filter the returned list of account codes through Permissions - // if requested. - $accountcodes = array(); - foreach ($keys as $index => $accountcode) { - if ($permfilter) { - if (empty($accountcode)) { - $permitem = 'operator:accountcodes'; - } else { - $permitem = 'operator:accountcodes:' . $accountcode; - } - - if (Horde_Auth::isAdmin() || - $GLOBALS['perms']->hasPermission($permitem, - Horde_Auth::getAuth(), - Horde_Perms::SHOW)) { - $accountcodes[$accountcode] = $values[$index]; - } - } else { - $accountcodes[$accountcode] = $values[$index]; - } - } - return $accountcodes; - } - - function getGraphInfo($graphid) - { - switch($graphid) { - case 'numcalls': - return array( - 'title' => _("Number of Calls by Month"), - 'axisX' => _("Month"), - 'axisY' => _("Number of Calls"), - ); - break; - case 'minutes': - return array( - 'title' => _("Total Minutes Used by Month"), - 'axisX' => _("Month"), - 'axisY' => _("Minute"), - 'numberformat' => '%0.1f', - ); - break; - case 'failed': - return array( - 'title' => _("Number of Failed Calls by Month"), - 'axisX' => _("Month"), - 'axisY' => _("Failed Calls"), - ); - break; - } - } - -} diff --git a/lib/api.php b/lib/api.php deleted file mode 100644 index 116358928..000000000 --- a/lib/api.php +++ /dev/null @@ -1,44 +0,0 @@ - - * @package Operator - */ - -$_services['perms'] = array( - 'args' => array(), - 'type' => '{urn:horde}stringArray'); - -function _operator_perms() -{ - static $perms = array(); - - if (!empty($perms)) { - return $perms; - } - - @define('OPERATOR_BASE', dirname(__FILE__) . '/..'); - require_once OPERATOR_BASE . '/lib/base.php'; - - $perms['tree']['operator']['accountcodes'] = false; - $perms['title']['operator:accountcodes'] = _("Account Codes"); - - $accountcodes = Operator::getAccountCodes(); - foreach ($accountcodes as $accountcode) { - $perms['tree']['operator']['accountcodes'][$accountcode] = false; - $perms['title']['operator:accountcodes:' . $accountcode] = $accountcode; - } - - return $perms; -} diff --git a/lib/base.php b/lib/base.php deleted file mode 100644 index 17ab6b543..000000000 --- a/lib/base.php +++ /dev/null @@ -1,56 +0,0 @@ - - */ - -// Check for a prior definition of HORDE_BASE (perhaps by an auto_prepend_file -// definition for site customization). -if (!defined('HORDE_BASE')) { - @define('HORDE_BASE', dirname(__FILE__) . '/../../..'); -} - -// Load the Horde Framework core, and set up inclusion paths. -require_once HORDE_BASE . '/lib/core.php'; - -// Registry. -$registry = Horde_Registry::singleton(); -try { - $registry->pushApp('operator', !defined('AUTH_HANDLER')); -} catch (Horde_Exception $e) { - if ($e->getCode() == 'permission_denied') { - Horde::authenticationFailureRedirect(); - } - Horde::fatal($e, __FILE__, __LINE__, false); -} -$conf = &$GLOBALS['conf']; -@define('OPERATOR_TEMPLATES', $registry->get('templates')); - -// Notification system. -$notification = &Horde_Notification::singleton(); -$notification->attach('status'); - -// Define the base file path of Operator. -@define('OPERATOR_BASE', dirname(__FILE__) . '/..'); - -// Operator base library -require_once OPERATOR_BASE . '/lib/Operator.php'; - -// Operator backend. -require_once OPERATOR_BASE . '/lib/Driver.php'; -$GLOBALS['operator_driver'] = Operator_Driver::factory(); - -// Caching system for storing DB results -$cache = &Horde_Cache::singleton($GLOBALS['conf']['cache']['driver'], - Horde::getDriverConfig('cache', $GLOBALS['conf']['cache']['driver'])); - -// Start output compression. -Horde::compressOutput(); diff --git a/lib/version.php b/lib/version.php deleted file mode 100644 index 59bf5b296..000000000 --- a/lib/version.php +++ /dev/null @@ -1 +0,0 @@ - diff --git a/locale/en_US/help.xml b/locale/en_US/help.xml deleted file mode 100644 index f5afb83a4..000000000 --- a/locale/en_US/help.xml +++ /dev/null @@ -1,14 +0,0 @@ - - - - - - Skeleton Overview - - What is Skeleton? - Use this module as a blank base for Horde appliations. All of the - files in this module are to be used as examples. Please customize them - for your own application's requirements. - - - diff --git a/operator/COPYING b/operator/COPYING new file mode 100644 index 000000000..a6b67561a --- /dev/null +++ b/operator/COPYING @@ -0,0 +1,280 @@ + GNU GENERAL PUBLIC LICENSE + Version 2, June 1991 + + Copyright 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 diff --git a/operator/README b/operator/README new file mode 100644 index 000000000..6fa2b3e66 --- /dev/null +++ b/operator/README @@ -0,0 +1,95 @@ +What is Operator? +================= + +:Last update: $Date: 2008/04/19 01:26:06 $ +:Revision: $Revision: 1.1 $ + +.. contents:: Contents +.. section-numbering:: + +Operator is an application to read call detail records out of an SQL database +and allow the user to view those records. + +Today it is a read-only user interface with very limited search capabilities. +In the future the goal is to allow for sorting by arbitrary fields, more +advanced search criteria, exporting of search results, per-account-code +security (to allow for CDR self-service), rule-based call rating and more. + +This software is OSI Certified Open Source Software. OSI Certified is a +certification mark of the `Open Source Initiative`_. + +.. _`Open Source Initiative`: http://www.opensource.org/ + + +Obtaining Operator +------------------ + +Further information on Operator and the latest version can be obtained at + + http://www.horde.org/operator/ + +But not until it leaves the incubator... + + +Documentation +------------- + +The following documentation is available in the Operator distribution: + +:README_: This file +:COPYING_: Copyright and license information +:LICENSE_: Copyright and license information +:`docs/CHANGES`_: Changes by release +:`docs/CREDITS`_: Project developers +:`docs/INSTALL`_: Installation instructions and notes +:`docs/TODO`_: Development TODO list +:`docs/UPGRADING`_: Pointers on upgrading from previous Operator versions + + +Installation +------------ + +Instructions for installing Operator can be found in the file INSTALL_ in the +``docs/`` directory of the Operator distribution. + + +Assistance +---------- + +If you encounter problems with Operator, help is available! + +The Horde Frequently Asked Questions List (FAQ), available on the Web at + + http://www.horde.org/faq/ + +The Horde Project runs a number of mailing lists, for individual applications +and for issues relating to the project as a whole. Information, archives, and +subscription information can be found at + + http://www.horde.org/mail/ + +Lastly, Horde developers, contributors and users also make occasional +appearances on IRC, on the channel #horde on the freenode Network +(irc.freenode.net). + + +Licensing +--------- + +For licensing and copyright information, please see the file COPYING_/LICENSE_ +in the Operator distribution. + +Thanks, + +The Operator team + + +.. _README: ?f=README.html +.. _COPYING: http://www.horde.org/licenses/gpl.php +.. _LICENSE: http://www.horde.org/licenses/asl.php +.. _docs/CHANGES: ?f=CHANGES.html +.. _docs/CREDITS: ?f=CREDITS.html +.. _INSTALL: +.. _docs/INSTALL: ?f=INSTALL.html +.. _docs/TODO: ?f=TODO.html +.. _docs/UPGRADING: ?f=UPGRADING.html diff --git a/operator/config/.cvsignore b/operator/config/.cvsignore new file mode 100644 index 000000000..51adefac7 --- /dev/null +++ b/operator/config/.cvsignore @@ -0,0 +1,3 @@ +conf.php +conf.bak.php +prefs.php diff --git a/operator/config/conf.xml b/operator/config/conf.xml new file mode 100644 index 000000000..3d96910c2 --- /dev/null +++ b/operator/config/conf.xml @@ -0,0 +1,28 @@ + + + + + Storage System Settings + asterisksql + + + + cdr + + + + + + + + + + Menu Settings + + + + + + + diff --git a/operator/config/prefs.php.dist b/operator/config/prefs.php.dist new file mode 100644 index 000000000..2c5160e94 --- /dev/null +++ b/operator/config/prefs.php.dist @@ -0,0 +1,57 @@ + _("Options"), + 'label' => _("Display Preferences"), + 'desc' => _("Set default display parameters."), + 'members' => array('rowsperpage', 'resultlimit', 'columns') +); + +$_prefs['rowsperpage'] = array( + 'value' => 100, + 'locked' => false, + 'shared' => false, + 'type' => 'number', + 'desc' => _("The columns to be displayed on the Call Detail Review screen") +); + +$_prefs['resultlimit'] = array( + 'value' => 100, + 'locked' => false, + 'shared' => false, + 'type' => 'number', + 'desc' => _("Limit the number of CDR results when searching to this number") +); + +$_prefs['columns'] = array( + 'value' => 'a:18:{i:0;s:11:"accountcode";i:1;s:3:"src";i:2;s:3:"dst";i:3;s:8:"dcontext";i:4;s:4:"clid";i:5;s:7:"channel";i:6;s:10:"dstchannel";i:7;s:7:"lastapp";i:8;s:8:"lastdata";i:9;s:5:"start";i:10;s:6:"answer";i:11;s:3:"end";i:12;s:8:"duration";i:13;s:7:"billsec";i:14;s:11:"disposition";i:15;s:8:"amaflags";i:16;s:9:"userfield";i:17;s:8:"uniqueid";}', + 'locked' => false, + 'shared' => false, + 'type' => 'multienum', + 'enum' => array( + 'accountcode' => _("Account Code"), + 'src' => _("Source"), + 'dst' => _("Destination"), + 'dcontext' => _("Destination Context"), + 'clid' => _("Caller ID"), + 'channel' => _("Channel"), + 'dstchannel' => _("Destination Channel"), + 'lastapp' => _("Last Application"), + 'lastdata' => _("Last Application Data"), + 'start' => _("Call Start Time"), + 'answer' => _("Call Answer Time"), + 'end' => _("Call End Time"), + 'duration' => _("Call Duration"), + 'billsec' => _("Billing Time (seconds)"), + 'disposition' => _("Call Disposition"), + 'amaflags' => _("AMA Flags"), + 'userfield' => _("User Defined Field"), + 'uniqueid' => _("Call Unique ID")), + 'desc' => _("The columns to be displayed on the Call Detail Review screen") +); + diff --git a/operator/docs/CHANGES b/operator/docs/CHANGES new file mode 100644 index 000000000..141c0cc98 --- /dev/null +++ b/operator/docs/CHANGES @@ -0,0 +1,5 @@ +--- +0.1 +--- + +[xyz] Initial Release diff --git a/operator/docs/CREDITS b/operator/docs/CREDITS new file mode 100644 index 000000000..77f1093ee --- /dev/null +++ b/operator/docs/CREDITS @@ -0,0 +1,26 @@ +=========================== + Operator Development Team +=========================== + + +Core Developers +=============== + +Ben Klang + + + +Drivers +======= + + + +Localization +============ + +===================== ====================================================== +===================== ====================================================== + + +Contributions +============= diff --git a/operator/docs/INSTALL b/operator/docs/INSTALL new file mode 100644 index 000000000..898af4e68 --- /dev/null +++ b/operator/docs/INSTALL @@ -0,0 +1,243 @@ +========================= + Installing Skeleton 1.0 +========================= + +:Last update: $Date: 2008/04/19 01:26:06 $ +:Revision: $Revision: 1.1 $ + +.. contents:: Contents +.. section-numbering:: + +This document contains instructions for installing the Skeleton ... + +For information on the capabilities and features of Skeleton, see the file +README_ in the top-level directory of the Skeleton distribution. + + +Obtaining Skeleton +================== + +Skeleton can be obtained from the Horde website and FTP server, at + + http://www.horde.org/skeleton/ + + ftp://ftp.horde.org/pub/skeleton/ + +Or use the mirror closest to you: + + http://www.horde.org/mirrors.php + +Bleeding-edge development versions of Skeleton are available via CVS; see the +file `horde/docs/HACKING`_ in the Horde distribution, or the website +http://www.horde.org/source/, for information on accessing the Horde CVS +repository. + + +Prerequisites +============= + +To function properly, Skeleton **requires** the following: + +1. A working Horde installation. + + Skeleton runs within the `Horde Application Framework`_, a set of common + tools for Web applications written in PHP. You must install Horde before + installing Skeleton. + + .. Important:: Skeleton 1.0 requires version 3.0+ of the Horde Framework - + earlier versions of Horde will **not** work. + + .. _`Horde Application Framework`: http://www.horde.org/horde/ + + The Horde Framework can be obtained from the Horde website and FTP server, + at + + http://www.horde.org/horde/ + + ftp://ftp.horde.org/pub/horde/ + + Many of Skeleton's prerequisites are also Horde prerequisites. + + .. Important:: Be sure to have completed all of the steps in the + `horde/docs/INSTALL`_ file for the Horde Framework before + installing Skeleton. + +2. The following PHP capabilities: + + a. FOO support ``--with-foo`` [OPTIONAL] + + Description of Foo and what it is used for. + +3. The following PEAR packages: + (See `horde/docs/INSTALL`_ for instructions on installing PEAR packages) + + a. PEAR_Package x.x.x [OPTIONAL] + + Skeleton uses the Foo_Bar class for... + +4. The following PECL modules: + (See `horde/docs/INSTALL`_ for instructions on installing PECL modules) + + a. pecl_package x.x.x [OPTIONAL] + + pecl_package is required to... + +5. Something else. + +The following items are not required, but are strongly **recommended**: + +1. Yet something else. + + +Installing Skeleton +=================== + +Skeleton is written in PHP, and must be installed in a web-accessible +directory. The precise location of this directory will differ from system to +system. Conventionally, Skeleton is installed directly underneath Horde in the +web server's document tree. + +Since Skeleton is written in PHP, there is no compilation necessary; simply +expand the distribution where you want it to reside and rename the root +directory of the distribution to whatever you wish to appear in the URL. For +example, with the Apache web server's default document root of +``/usr/local/apache/htdocs``, you would type:: + + cd /usr/local/apache/htdocs/horde + tar zxvf /path/to/skeleton-h3-x.y.z.tar.gz + mv skeleton-h3-x.y.z skeleton + +and would then find Skeleton at the URL:: + + http://your-server/horde/skeleton/ + + +Configuring Skeleton +==================== + +1. Configuring Horde for Skeleton + + a. Register the application + + In ``horde/config/registry.php``, find the ``applications['skeleton']`` + stanza. The default settings here should be okay, but you can change + them if desired. If you have changed the location of Skeleton relative + to Horde, either in the URL, in the filesystem or both, you must update + the ``fileroot`` and ``webroot`` settings to their correct values. + +2. Creating the database tables + + The specific steps to create Skeleton's database tables depend on which + database you've chosen to use. + + First, look in ``scripts/sql/`` to see if a script already exists for your + database type. If so, you should be able to simply execute that script as + superuser in your database. (Note that executing the script as the "horde" + user will probably fail when granting privileges.) + + If such a script does not exist, you'll need to build your own, using the + file ``skeleton.sql`` as a starting point. If you need assistance in + creating database tables, you may wish to let us know on the Skeleton + mailing list. + + You will also need to make sure that the "horde" user in your database has + table-creation privileges, so that the tables that `PEAR DB`_ uses to + provide portable sequences can be created. + + .. _`PEAR DB`: http://pear.php.net/DB + +3. Configuring Skeleton + + To configure Skeleton, change to the ``config/`` directory of the installed + distribution, and make copies of all of the configuration ``dist`` files + without the ``dist`` suffix:: + + cd config/ + for foo in *.dist; do cp $foo `basename $foo .dist`; done + + Or on Windows:: + + copy *.dist *. + + Documentation on the format and purpose of those files can be found in each + file. You may edit these files if you wish to customize Skeleton's + appearance and behavior. With one exception (``foo.php``) the defaults will + be correct for most sites. + + You must login to Horde as a Horde Administrator to finish the + configuration of Skeleton. Use the Horde ``Administration`` menu item to + get to the administration page, and then click on the ``Configuration`` + icon to get the configuration page. Select ``Skeleton Name`` from the + selection list of applications. Fill in or change any configuration values + as needed. When done click on ``Generate Skeleton Name Configuration`` to + generate the ``conf.php`` file. If your web server doesn't have write + permissions to the Skeleton configuration directory or file, it will not be + able to write the file. In this case, go back to ``Configuration`` and + choose one of the other methods to create the configuration file + ``skeleton/config/conf.php``. + + Note for international users: Skeleton uses GNU gettext to provide local + translations of text displayed by applications; the translations are found + in the ``po/`` directory. If a translation is not yet available for your + locale (and you wish to create one), see the ``horde/po/README`` file, or + if you're having trouble using a provided translation, please see the + `horde/docs/TRANSLATIONS`_ file for instructions. + +4. More instructions, upgrading, securing, etc. + +5. Testing Skeleton + + Once you have configured Skeleton, bring up the included test page in your + Web browser to ensure that all necessary prerequisites have been met. See + the `horde/docs/INSTALL`_ document for further details on Horde test + scripts. If you installed Skeleton as described above, the URL to the test + page would be:: + + http://your-server/horde/skeleton/test.php + + The test script will also allow you to test... + + Next, use Skeleton to.... Test at least the following: + + - Foo + - Bar + + +Known Problems +============== + +... + + +Obtaining Support +================= + +If you encounter problems with Skeleton, help is available! + +The Horde Frequently Asked Questions List (FAQ), available on the Web at + + http://www.horde.org/faq/ + +The Horde Project runs a number of mailing lists, for individual applications +and for issues relating to the project as a whole. Information, archives, and +subscription information can be found at + + http://www.horde.org/mail/ + +Lastly, Horde developers, contributors and users may also be found on IRC, +on the channel #horde on the Freenode Network (irc.freenode.net). + +Please keep in mind that Skeleton is free software written by volunteers. +For information on reasonable support expectations, please read + + http://www.horde.org/support.php + +Thanks for using Skeleton! + +The Skeleton team + + +.. _README: ?f=README.html +.. _`horde/docs/HACKING`: ../../horde/docs/?f=HACKING.html +.. _`horde/docs/INSTALL`: ../../horde/docs/?f=INSTALL.html +.. _`horde/docs/TRANSLATIONS`: ../../horde/docs/?f=TRANSLATIONS.html diff --git a/operator/docs/RELEASE_NOTES b/operator/docs/RELEASE_NOTES new file mode 100644 index 000000000..d327d5bd3 --- /dev/null +++ b/operator/docs/RELEASE_NOTES @@ -0,0 +1,49 @@ +notes['fm']['focus'] = 4; + +/* Mailing list release notes. */ +$this->notes['ml']['changes'] = <<notes['fm']['changes'] = <<notes['name'] = 'Skeleton'; +$this->notes['fm']['project'] = 'skeleton'; +$this->notes['fm']['branch'] = 'Default'; diff --git a/operator/docs/TODO b/operator/docs/TODO new file mode 100644 index 000000000..fffa4329a --- /dev/null +++ b/operator/docs/TODO @@ -0,0 +1,8 @@ +================================ + Skeleton Development TODO List +================================ + +:Last update: $Date: 2008/04/19 01:26:06 $ +:Revision: $Revision: 1.1 $ + +- Example todo diff --git a/operator/graphgen.php b/operator/graphgen.php new file mode 100644 index 000000000..06e186df8 --- /dev/null +++ b/operator/graphgen.php @@ -0,0 +1,210 @@ + + * + * See the enclosed file COPYING for license information (GPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/gpl.html. + * + * @author Ben Klang + */ + +@define('OPERATOR_BASE', dirname(__FILE__)); +require_once OPERATOR_BASE . '/lib/base.php'; + +// Load PEAR's Image_Graph library +require_once 'Image/Graph.php'; + +#setlocale(LC_ALL, Horde_Nls::select()); +setlocale(LC_ALL, 'en_US'); + +$graphtype = Horde_Util::getFormData('graph'); +$graphinfo = Operator::getGraphInfo($graphtype); +$cachekey = Horde_Util::getFormData('key'); + +$stats = unserialize($cache->get($cachekey, 0)); + +// Create the graph image base. +if (empty($graphinfo['imageX'])) { + $graphinfo['imageX'] = 700; +} +if (empty($graphinfo['imageY'])) { + $graphinfo['imageY'] = 600; +} +if (!isset($graphinfo['charttype'])) { + $graphinfo['charttype'] = 'bar'; +} +if (!isset($graphinfo['markers'])) { + $graphinfo['markers'] = true; +} +if (!isset($graphinfo['legendsplit'])) { + $graphinfo['legendsplit'] = 90; +} + +$canvas =& Image_Canvas::factory('png', array('width' => $graphinfo['imageX'], 'height' => $graphinfo['imageY'], 'antialias' => true)); +$graph =& Image_Graph::factory('graph', $canvas); + +if (isset($graphinfo['orientation']) && + $graphinfo['orientation'] == 'horizontal') { + $graph->horizontal = true; +} else { + $graph->horizontal = false; +} + +if (!empty($conf['ttf_font'])) { + // add a TrueType font + $Font =& $graph->addNew('ttf_font', $conf['ttf_font']); + // set the font size to 11 pixels + $Font->setSize(8); + $graph->setFont($Font); +} + + +// create the plotarea layout +if ($graph->horizontal) { + $plotarea =& Image_Graph::factory('plotarea', array('Image_Graph_Axis_Category', 'Image_Graph_Axis', 'horizontal')); +} else { + $plotarea =& Image_Graph::factory('plotarea', array('Image_Graph_Axis_Category', 'Image_Graph_Axis', 'vertical')); +} + +$graph->add( + Image_Graph::vertical( + Image_Graph::factory('title', array($graphinfo['title'], 11)), + Image_Graph::vertical( + $plotarea, + $legend = Image_Graph::factory('legend'), + $graphinfo['legendsplit'] + ), + 5 + ) +); + +$plotarea->setAxisPadding(array('top' => 20)); + +// make the legend use the plotarea (or implicitly its plots) +$legend->setPlotarea($plotarea); + +// create a grid and assign it to the secondary Y axis +$gridY2 =& $plotarea->addNew('line_grid', IMAGE_GRAPH_AXIS_Y_SECONDARY); +#$gridY2->setLineColor('black'); +#$gridY2->setFillStyle( +# Image_Graph::factory( +# 'gradient', +# array(IMAGE_GRAPH_GRAD_HORIZONTAL, 'white', 'lightgrey') +# ) +#); + +$linecolor = 0x000042; +$increment = 0x173147; +foreach ($stats[$graphtype] as $title => $data) { + $lcstring = sprintf('#%06x', $linecolor); + $linecolor += $increment; + if ($linecolor >= 0x5555555) { + $linecolor = $linecolor & 0xFFFFFF; + $linecolor += $increment; + } + + if ($graph->horizontal) { + // Horizontal graphs reverse the data points + $data = array_reverse($data, true); + } + + $dataset = Image_Graph::factory('dataset'); + foreach ($data as $expr => $value) { + $dataset->addPoint($expr, $value); + } + $plot =& $plotarea->addNew($graphinfo['charttype'], $dataset); + $plot->setLineColor($lcstring); + $plot->setFillColor($lcstring . '@0.5'); + //$plot->setFillColor('blue@0.2'); + $plot->setTitle($title); + + if ($graphinfo['markers']) { + $marker =& $plot->addNew('Image_Graph_Marker_Value', IMAGE_GRAPH_VALUE_Y); + // create a pin-point marker type + if ($graph->horizontal) { + $PointingMarker =& $plot->addNew('Image_Graph_Marker_Pointing', array(-37, 0, $marker)); + } else { + $PointingMarker =& $plot->addNew('Image_Graph_Marker_Pointing', array(0, -7, $marker)); + } + $PointingMarker->setLineColor(false); + $marker->setBorderColor(false); + $marker->setFillColor(false); + // and use the marker on the 1st plot + $plot->setMarker($PointingMarker); + + #if (!empty($graphinfo['numberformat'])) { + # $marker->setDataPreprocessor(Image_Graph::factory('Image_Graph_DataPreprocessor_Formatted', $graphinfo['numberformat'])); + #} + $marker->setDataPreprocessor(Image_Graph::factory('Image_Graph_DataPreprocessor_Function', '_format')); + $marker->setFontSize(7.5); + } +} + +// create an area plot using a random dataset +#$dataset2 =& Image_Graph::factory('random', array(8, 1, 10, true)); +#$plot2 =& $plotarea->addNew( +# 'Image_Graph_Plot_Area', +# $dataset2, +# IMAGE_GRAPH_AXIS_Y_SECONDARY +#); + +#$plot2->setLineColor('gray'); +#$plot2->setFillColor('blue@0.2'); +#$plot2->setTitle('Secondary Axis'); + +$axisX =& $plotarea->getAxis(IMAGE_GRAPH_AXIS_X); +$axisY =& $plotarea->getAxis(IMAGE_GRAPH_AXIS_Y); +if ($graph->horizontal) { + $axisX->setTitle($graphinfo['axisX'], 'vertical'); + $axisY->setTitle($graphinfo['axisY'], 'horizontal'); +} else { + $axisX->setTitle($graphinfo['axisX'], 'horizontal'); + $axisY->setTitle($graphinfo['axisY'], 'vertical'); +} +$axisY->setDataPreprocessor(Image_Graph::factory('Image_Graph_DataPreprocessor_Function', 'axis2human')); +#$axisYsecondary =& $plotarea->getAxis(IMAGE_GRAPH_AXIS_Y_SECONDARY); +#$axisYsecondary->setTitle('Pears', 'vertical2'); + +// output the Graph +$graph->done(); +exit; + +function _format($number) +{ + if (($number - (int)$number) == 0) { + return money_format('%!.0n', $number); + } else { + return money_format('%!.2n', $number); + } +} + +function number2human($number, $showCurrency = true) +{ + $currency = ''; + $suffix = ''; + if ($showCurrency) { + //$currency = 'ISK '; + $currency = ''; + } + + if (abs($number) >= 500000000) { + $number = $number / 1000000000; + $suffix = 'T'; + } + if (abs($number) >= 500000) { + $number = $number / 1000000; + $suffix = 'M'; + } + if (abs($number) >= 500) { + $number = $number / 1000; + $suffix = 'K'; + } + return $currency . _format($number) . $suffix; +} + +function axis2human($number) +{ + return number2human($number, false); +} diff --git a/operator/index.php b/operator/index.php new file mode 100644 index 000000000..bb539f4c6 --- /dev/null +++ b/operator/index.php @@ -0,0 +1,22 @@ + + */ + +@define('OPERATOR_BASE', dirname(__FILE__)); +$operator_configured = (is_readable(OPERATOR_BASE . '/config/conf.php')); + +if (!$operator_configured) { + require OPERATOR_BASE . '/../lib/Test.php'; + Horde_Test::configFilesMissing('Operator', OPERATOR_BASE, + array('conf.php')); +} + +require OPERATOR_BASE . '/viewgraph.php'; diff --git a/operator/lib/Driver.php b/operator/lib/Driver.php new file mode 100644 index 000000000..59b6d522e --- /dev/null +++ b/operator/lib/Driver.php @@ -0,0 +1,125 @@ + + * @package Operator + */ +class Operator_Driver { + + /** + * Search the database for call detail records, taking permissions into + * consideration. + * + * @return boolean|PEAR_Error True on success, PEAR_Error on failure. + */ + function getRecords($start, $end, $accountcode = null, $dcontext = null, + $rowstart = 0, $rowlimit = 100) + { + if (empty($accountcode) || $accountcode == '%') { + $permentry = 'operator:accountcodes'; + } else { + $permentry = 'operator:accountcodes:' . $accountcode; + } + if (Horde_Auth::isAdmin() || + $GLOBALS['perms']->hasPermission('operator:accountcodes', + Horde_Auth::getAuth(), + Horde_Perms::READ) || + $GLOBALS['perms']->hasPermission($permentry, Horde_Auth::getAuth(), + Horde_Perms::READ)) { + return $this->_getRecords($start, $end, $accountcode, $dcontext, + $rowstart, $rowlimit); + } + return PEAR::raiseError(_("You do not have permission to view call detail records for that account code.")); + } + + /** + * Get summary call statistics per-month for a given time range, account and + * destination. + * + * @param Horde_Date startdate Start of the statistics window + * @param Horde_Date enddate End of the statistics window + * @param string accountcode Name of the accont for statistics. Defaults + * to null meaning all accounts. + * @param string dcontext Destination of calls. Defaults to null. + * + * + * @return array|PEAR_Error Array of call statistics. The key of each + * element is the month name in date('Y-m') + * format and the value being an array of + * statistics for calls placed that month. This + * method will additionall return PEAR_Error + * on failure. + */ + function getMonthlyCallStats($start, $end, $accountcode = null, + $dcontext = null){ + if (empty($accountcode) || $accountcode == '%') { + $permentry = 'operator:accountcodes'; + } else { + $permentry = 'operator:accountcodes:' . $accountcode; + } + if (Horde_Auth::isAdmin() || + $GLOBALS['perms']->hasPermission('operator:accountcodes', + Horde_Auth::getAuth(), + Horde_Perms::READ) || + $GLOBALS['perms']->hasPermission($permentry, Horde_Auth::getAuth(), + Horde_Perms::READ)) { + return $this->_getMonthlyCallStats($start, $end, $accountcode, + $dcontext); + } + + return PEAR::raiseError(_("You do not have permission to view call detail records for that account code.")); + } + + /** + * Attempts to return a concrete Operator_Driver instance based on $driver. + * + * @param string $driver The type of the concrete Operator_Driver subclass + * to return. The class name is based on the + * storage driver ($driver). The code is + * dynamically included. + * + * @param array $params A hash containing any additional configuration + * or connection parameters a subclass might need. + * + * @return Operator_Driver The newly created concrete Operator_Driver + * instance, or false on an error. + */ + function factory($driver = null, $params = null) + { + if ($driver === null) { + $driver = $GLOBALS['conf']['storage']['driver']; + } + $driver = basename($driver); + + if (is_null($params)) { + // Since we have more than one backend that uses SQL make sure + // all of them have a chance to inherit the site-wide config. + $sqldrivers = array('sql', 'asterisksql'); + if (in_array($driver, $sqldrivers)) { + $params = Horde::getDriverConfig('storage', 'sql'); + } else { + $params = Horde::getDriverConfig('storage', $driver); + } + } + + $class = 'Operator_Driver_' . $driver; + if (!class_exists($class)) { + include dirname(__FILE__) . '/Driver/' . $driver . '.php'; + } + if (class_exists($class)) { + return new $class($params); + } else { + return false; + } + } + +} diff --git a/operator/lib/Driver/asterisksql.php b/operator/lib/Driver/asterisksql.php new file mode 100644 index 000000000..d7c9fa49e --- /dev/null +++ b/operator/lib/Driver/asterisksql.php @@ -0,0 +1,444 @@ + + * 'phptype' The database type (e.g. 'pgsql', 'mysql', etc.). + * 'table' The name of the foo table in 'database'. + * 'charset' The database's internal charset. + * + * Required by some database implementations:
+ *      'database'      The name of the database.
+ *      'hostspec'      The hostname of the database server.
+ *      'protocol'      The communication protocol ('tcp', 'unix', etc.).
+ *      'username'      The username with which to connect to the database.
+ *      'password'      The password associated with 'username'.
+ *      'options'       Additional options to pass to the database.
+ *      'tty'           The TTY on which to connect to the database.
+ *      'port'          The port on which to connect to the database.
+ * + * The table structure can be created by the scripts/sql/operator_foo.sql + * script. + * + * $Horde: incubator/operator/lib/Driver/asterisksql.php,v 1.12 2009/05/31 17:14:08 bklang Exp $ + * + * Copyright 2008-2009 The Horde Project (http://www.horde.org/) + * + * See the enclosed file COPYING for license information (GPL). If you + * did not receive this file, see http://www.fsf.org/copyleft/gpl.html. + * + * @author Your Name + * @package Operator + */ +class Operator_Driver_asterisksql extends Operator_Driver { + + /** + * Hash containing connection parameters. + * + * @var array + */ + var $_params = array(); + + /** + * Handle for the current database connection. + * + * @var DB + */ + var $_db; + + /** + * Handle for the current database connection, used for writing. Defaults + * to the same handle as $_db if a separate write database is not required. + * + * @var DB + */ + var $_write_db; + + /** + * Boolean indicating whether or not we're connected to the SQL server. + * + * @var boolean + */ + var $_connected = false; + + /** + * Constructs a new SQL storage object. + * + * @param array $params A hash containing connection parameters. + */ + function Operator_Driver_asterisksql($params = array()) + { + $this->_params = $params; + } + + /** + * Get call detail records from the database + * + * @return boolean|PEAR_Error True on success, PEAR_Error on failure. + */ + function _getRecords($start, $end, $accountcode = null, $dcontext = null, + $rowstart = 0, $rowlimit = 100) + { + + // Use the query to make the MySQL driver look like the CDR-CSV driver + $sql = 'SELECT accountcode, src, dst, dcontext, clid, channel, ' . + 'dstchannel, lastapp, lastdata, calldate AS start, ' . + 'calldate AS answer, calldate AS end, duration, ' . + 'billsec, disposition, amaflags, userfield, uniqueid ' . + ' FROM ' . $this->_params['table'] . ' WHERE %s'; + $filter = array(); + $values = array(); + + if (!is_numeric($rowstart)) { + Horde::logMessage('Invalid start row requested.', __FILE__, __LINE__, PEAR_LOG_ERR); + return PEAR::raiseError(_("Internal error. Details have been logged for the administrator.")); + } + if (!is_numeric($rowlimit)) { + Horde::logMessage('Invalid row limit requested.', __FILE__, __LINE__, PEAR_LOG_ERR); + return PEAR::raiseError(_("Internal error. Details have been logged for the administrator.")); + } + + + // Start Date + if (!is_a($start, 'Horde_Date')) { + $start = new Horde_Date($start); + if (is_a($start, 'PEAR_Error')) { + return $start; + } + } + $filter[] = 'calldate >= ?'; + $values[] = $start->strftime('%Y-%m-%d %T'); + + // End Date + if (!is_a($end, 'Horde_Date')) { + $end = new Horde_Date($end); + if (is_a($end, 'PEAR_Error')) { + return $end; + } + } + $filter[] = 'calldate < ?'; + $values[] = $end->strftime('%Y-%m-%d %T'); + + // Filter by account code + if ($accountcode !== null) { + $filter[] = 'accountcode LIKE ?'; + $values[] = $accountcode; + } else { + $filter[] = 'accountcode = ""'; + } + + // Filter by destination context + if ($dcontext !== null) { + $filter[] = 'dcontext LIKE ?'; + $values[] = $dcontext; + } else { + $filter[] = 'dcontext = ""'; + } + + /* Make sure we have a valid database connection. */ + $this->_connect(); + + $filterstring = implode(' AND ', $filter); + $sql = sprintf($sql, $filterstring); + /* Log the query at a DEBUG log level. */ + Horde::logMessage(sprintf('Operator_Driver_asterisksql::getData(): %s', $sql), __FILE__, __LINE__, PEAR_LOG_DEBUG); + + /* Execute the query. */ + $res = $this->_db->limitQuery($sql, $rowstart, $rowlimit, $values); + if (is_a($res, 'PEAR_Error')) { + Horde::logMessage($res, __FILE__, __LINE__, PEAR_LOG_ERR); + return PEAR::raiseError(_("Internal error. Details have been logged for the administrator.")); + } + + $data = array(); + while ($row = $res->fetchRow(DB_FETCHMODE_ASSOC)) { + $data[] = $row; + } + + // Get summary statistics on the requested criteria + $sql = 'SELECT COUNT(*) AS numcalls, SUM(duration)/60 AS minutes, ' . + 'SUM(CASE disposition WHEN "FAILED" THEN 1 ELSE 0 END) AS ' . + 'failed FROM ' . $this->_params['table'] . ' WHERE %s'; + $sql = sprintf($sql, $filterstring); + Horde::logMessage(sprintf('Operator_Driver_asterisksql::getData(): %s', $sql), __FILE__, __LINE__, PEAR_LOG_DEBUG); + + /* Execute the query. */ + $res = $this->_db->getRow($sql, $values, DB_FETCHMODE_ASSOC); + if (is_a($res, 'PEAR_Error')) { + Horde::logMessage($res, __FILE__, __LINE__, PEAR_LOG_ERR); + return PEAR::raiseError(_("Internal error. Details have been logged for the administrator.")); + } + + return array_merge($data, $res); + } + + /** + * Get summary call statistics per-month for a given time range, account and + * destination. + * + * @param Horde_Date startdate Start of the statistics window + * @param Horde_Date enddate End of the statistics window + * @param string accountcode Name of the accont for statistics. Defaults + * to null meaning all accounts. + * @param string dcontext Destination of calls. Defaults to null. + * + * + * @return array|PEAR_Error Array of call statistics. The key of each + * element is the month name in date('Y-m') + * format and the value being an array of + * statistics for calls placed that month. This + * method will additionall return PEAR_Error + * on failure. + */ + function _getMonthlyCallStats($start, $end, $accountcode = null, + $dcontext = null) + { + if (!is_a($start, 'Horde_Date') || !is_a($end, 'Horde_Date')) { + Horde::logMessage('Start ane end date must be Horde_Date objects.', __FILE__, __LINE__, PEAR_LOG_ERR); + return PEAR::raiseError(_("Internal error. Details have been logged for the administrator.")); + } + + /* Make sure we have a valid database connection. */ + $this->_connect(); + + // Construct the queries we will be running below + // Use 1=1 to make constructing the filter string easier + $numcalls_query = 'SELECT MONTH(calldate) AS month, ' . + 'YEAR(calldate) AS year, ' . + 'COUNT(*) AS numcalls FROM ' . + $this->_params['table'] . ' WHERE %s ' . + 'GROUP BY year, month'; + + $minutes_query = 'SELECT MONTH(calldate) AS month, ' . + 'YEAR(calldate) AS year, ' . + 'SUM(duration)/60 AS minutes FROM ' . + $this->_params['table'] . ' WHERE %s ' . + 'GROUP BY year, month'; + + $failed_query = 'SELECT MONTH(calldate) AS month, ' . + 'YEAR(calldate) AS year, ' . + 'COUNT(disposition) AS failed FROM ' . + $this->_params['table'] . ' ' . + 'WHERE disposition="failed" AND %s ' . + 'GROUP BY year, month'; + + // Shared SQL filter + $filter = array(); + $values = array(); + + // Filter by account code + if ($accountcode !== null) { + $filter[] = 'accountcode LIKE ?'; + $values[] = $accountcode; + } else { + $filter[] = 'accountcode = ""'; + } + + // Filter by destination context + if ($dcontext !== null) { + $filter[] = 'dcontext LIKE ?'; + $values[] = $dcontext; + } else { + $filter[] = 'dcontext = ""'; + } + + // Filter by the date range (filled in below) + $filter[] = 'calldate >= ?'; + $values[] = $start->strftime('%Y-%m-%d %T'); + $filter[] = 'calldate < ?'; + $values[] = $end->strftime('%Y-%m-%d %T'); + + $filterstring = implode(' AND ', $filter); + + $stats = array(); + + /* Log the query at a DEBUG log level. */ + $sql = sprintf($numcalls_query, $filterstring); + Horde::logMessage(sprintf('Operator_Driver_asterisksql::getCallStats(): Values: %s', print_r($values, true)), __FILE__, __LINE__, PEAR_LOG_DEBUG); + Horde::logMessage(sprintf('Operator_Driver_asterisksql::getCallStats(): %s', $sql), __FILE__, __LINE__, PEAR_LOG_DEBUG); + $numcalls_res = $this->_db->getAll($sql, $values, DB_FETCHMODE_ASSOC); + if (is_a($numcalls_res, 'PEAR_Error')) { + Horde::logMessage($numcalls_res, __FILE__, __LINE__, PEAR_LOG_ERR); + return PEAR::raiseError(_("Internal error. Details have been logged for the administrator.")); + } + + $sql = sprintf($minutes_query, $filterstring); + Horde::logMessage(sprintf('Operator_Driver_asterisksql::getCallStats(): %s', $sql), __FILE__, __LINE__, PEAR_LOG_DEBUG); + $minutes_res = $this->_db->getAll($sql, $values, DB_FETCHMODE_ASSOC); + if (is_a($minutes_res, 'PEAR_Error')) { + Horde::logMessage($minutes_res, __FILE__, __LINE__, PEAR_LOG_ERR); + return PEAR::raiseError(_("Internal error. Details have been logged for the administrator.")); + } + + $sql = sprintf($failed_query, $filterstring); + Horde::logMessage(sprintf('Operator_Driver_asterisksql::getCallStats(): %s', $sql), __FILE__, __LINE__, PEAR_LOG_DEBUG); + $failed_res = $this->_db->getAll($sql, $values, DB_FETCHMODE_ASSOC); + if (is_a($failed_res, 'PEAR_Error')) { + Horde::logMessage($failed_res, __FILE__, __LINE__, PEAR_LOG_ERR); + return PEAR::raiseError(_("Internal error. Details have been logged for the administrator.")); + } + + // Normalize the results from the database. This is done because + // the database will not return values if there are no data that match + // the query. For example if there were no calls in the month of June + // the results will not have any rows with data for June. Instead of + // searching through the results for each month we stuff the values we + // have into a temporary array and then create the return value below + // using 0 values where necessary. + $numcalls = array(); + foreach ($numcalls_res as $row) { + $numcalls[$row['year']][$row['month']] = $row['numcalls']; + } + $minutes = array(); + foreach ($minutes_res as $row) { + $minutes[$row['year']][$row['month']] = $row['minutes']; + } + $failed = array(); + foreach ($failed_res as $row) { + $failed[$row['year']][$row['month']] = $row['failed']; + } + + $s_numcalls = array(); + $s_minutes = array(); + $s_failed = array(); + while($start->compareDate($end) <= 0) { + $index = $start->strftime('%Y-%m'); + $year = $start->year; + $month = $start->month; + + if (empty($numcalls[$year]) || empty($numcalls[$year][$month])) { + $s_numcalls[$index] = 0; + } else { + $s_numcalls[$index] = $numcalls[$year][$month]; + } + + if (empty($minutes[$year]) || empty($minutes[$year][$month])) { + $s_minutes[$index] = 0; + } else { + $s_minutes[$index] = $minutes[$year][$month]; + } + + if (empty($failed[$year]) || empty($failed[$year][$month])) { + $s_failed[$index] = 0; + } else { + $s_failed[$index] = $failed[$year][$month]; + } + + // Find the first day of the next month + $start->month++; + $start->correct(); + } + + $info = Operator::getGraphInfo('numcalls'); + $stats['numcalls'] = array($info['title'] => $s_numcalls); + $info = Operator::getGraphInfo('minutes'); + $stats['minutes'] = array($info['title'] => $s_minutes); + $info = Operator::getGraphInfo('failed'); + $stats['failed'] = array($info['title'] => $s_failed); + + return $stats; + } + + function getAccountCodes() + { + /* Make sure we have a valid database connection. */ + $this->_connect(); + + $sql = 'SELECT DISTINCT(accountcode) FROM ' . $this->_params['table'] . + ' ORDER BY accountcode'; + Horde::logMessage(sprintf('Operator_Driver_asterisksql::getAccountCodes(): %s', $sql), __FILE__, __LINE__, PEAR_LOG_DEBUG); + $res = $this->_db->getCol($sql, 'accountcode'); + if (is_a($res, 'PEAR_Error')) { + Horde::logMessage($res, __FILE__, __LINE__, PEAR_LOG_ERR); + return PEAR::raiseError(_("Internal error. Details have been logged for the administrator.")); + } + + return $res; + } + + /** + * Attempts to open a connection to the SQL server. + * + * @return boolean True on success; exits (Horde::fatal()) on error. + */ + function _connect() + { + if ($this->_connected) { + return true; + } + + Horde::assertDriverConfig($this->_params, 'storage', + array('phptype', 'charset', 'table')); + + if (!isset($this->_params['database'])) { + $this->_params['database'] = ''; + } + if (!isset($this->_params['username'])) { + $this->_params['username'] = ''; + } + if (!isset($this->_params['hostspec'])) { + $this->_params['hostspec'] = ''; + } + + /* Connect to the SQL server using the supplied parameters. */ + require_once 'DB.php'; + $this->_write_db = &DB::connect($this->_params, + array('persistent' => !empty($this->_params['persistent']))); + if (is_a($this->_write_db, 'PEAR_Error')) { + Horde::fatal($this->_write_db, __FILE__, __LINE__); + } + + // Set DB portability options. + switch ($this->_write_db->phptype) { + case 'mssql': + $this->_write_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS | DB_PORTABILITY_RTRIM); + break; + default: + $this->_write_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS); + } + + /* Check if we need to set up the read DB connection seperately. */ + if (!empty($this->_params['splitread'])) { + $params = array_merge($this->_params, $this->_params['read']); + $this->_db = &DB::connect($params, + array('persistent' => !empty($params['persistent']))); + if (is_a($this->_db, 'PEAR_Error')) { + Horde::fatal($this->_db, __FILE__, __LINE__); + } + + // Set DB portability options. + switch ($this->_db->phptype) { + case 'mssql': + $this->_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS | DB_PORTABILITY_RTRIM); + break; + default: + $this->_db->setOption('portability', DB_PORTABILITY_LOWERCASE | DB_PORTABILITY_ERRORS); + } + + } else { + /* Default to the same DB handle for the writer too. */ + $this->_db =& $this->_write_db; + } + + $this->_connected = true; + + return true; + } + + /** + * Disconnects from the SQL server and cleans up the connection. + * + * @return boolean True on success, false on failure. + */ + function _disconnect() + { + if ($this->_connected) { + $this->_connected = false; + $this->_db->disconnect(); + $this->_write_db->disconnect(); + } + + return true; + } + +} diff --git a/operator/lib/Form/SearchCDR.php b/operator/lib/Form/SearchCDR.php new file mode 100644 index 000000000..73421943b --- /dev/null +++ b/operator/lib/Form/SearchCDR.php @@ -0,0 +1,69 @@ + + * + * See the enclosed file LICENSE for license information (BSD). If you + * did not receive this file, see http://www.horde.org/licenses/bsdl.php. + * + * @author Ben Klang + * @package Operator + */ + +class SearchCDRForm extends Horde_Form { + + function SearchCDRForm($title, &$vars) + { + global $operator_driver; + + parent::Horde_Form($vars, $title); + + // FIXME: Generate a list of clients from Turba? + //$clients = + + $now = time(); + if (!$vars->exists('startdate')) { + // Default to the beginning of the previous calendar month + $startdate = array('day' => 1, + 'month' => date('n', $now) - 1, + 'year' => date('Y', $now), + 'hour' => 0, + 'minute' => 0, + 'second' => 0); + $vars->set('startdate', $startdate); + } + + if (!$vars->exists('enddate')) { + // Default to the end of the previous calendar month + $month = date('n', $now) - 1; + $year = date('Y', $now); + $lastday = Horde_Date_Utils::daysInMonth($month, $year); + $enddate = array('day' => $lastday, + 'month' => $month, + 'year' => $year, + 'hour' => 23, + 'minute' => 59, + 'second' => 59); + $vars->set('enddate', $enddate); + } + + + // Parameters for Horde_Form_datetime + $start_year = date('Y', $now) - 3; + $end_year = ''; + $picker = true; + $format_in = null; + $format_out = '%x'; + $show_seconds = true; + $params = array($start_year, $end_year, $picker, $format_in, + $format_out, $show_seconds); + + $this->addVariable(_("Account Code"), 'accountcode', 'enum', false, false, null, array(Operator::getAccountCodes(true))); + $this->addVariable(_("Destination Context"), 'dcontext', 'text', false, false, _("An empty destination context will match all destination contexts.")); + $this->addVariable(_("Start Date/Time"), 'startdate', 'datetime', true, false, null, $params); + $this->addVariable(_("End Date/Time"), 'enddate', 'datetime', true, false, null, $params); + } +} diff --git a/operator/lib/Operator.php b/operator/lib/Operator.php new file mode 100644 index 000000000..a882ce9e6 --- /dev/null +++ b/operator/lib/Operator.php @@ -0,0 +1,171 @@ + + * @package Operator + */ +class Operator { + + /** + * Build Operator's list of menu items. + */ + function getMenu($returnType = 'object') + { + global $conf, $registry, $browser, $print_link; + + $menu = new Horde_Menu(Horde_Menu::MASK_ALL); + $menu->add(Horde::applicationUrl('viewgraph.php'), _("View Graphs"), 'graphs.png', null, null, null, basename($_SERVER['PHP_SELF']) == 'index.php' ? 'current' : null); + $menu->add(Horde::applicationUrl('search.php'), _("Search"), 'search.png', $registry->getImageDir('horde')); + + if ($returnType == 'object') { + return $menu; + } else { + return $menu->render(); + } + } + + function getColumns() + { + #static $columns = array( + $columns = array( + 'accountcode' => _("Account Code"), + 'src' => _("Source"), + 'dst' => _("Destination"), + 'dcontext' => _("Destination Context"), + 'clid' => _("Caller ID"), + 'channel' => _("Channel"), + 'dstchannel' => _("Destination Channel"), + 'lastapp' => _("Last Application"), + 'lastdata' => _("Last Application Data"), + 'start' => _("Call Start Time"), + 'answer' => _("Call Answer Time"), + 'end' => _("Call End Time"), + 'duration' => _("Call Duration (seconds)"), + 'billsec' => _("Billable Call Duration (seconds)"), + 'disposition' => _("Call Disposition"), + 'amaflags' => _("AMA Flag"), + 'userfield' => _("User Defined Field"), + 'uniqueid' => _("Call Unique ID")); + + return $columns; + } + + + function getColumnName($column) + { + $columns = Operator::getColumns(); + return $columns[$column]; + } + + function getAMAFlagName($flagid) + { + // See for definitions + switch($flagid) { + case 1: + return _("OMIT"); + break; + case 2: + return _("BILLING"); + break; + case 3: + return _("DOCUMENTATION"); + break; + } + } + + /** + * Get a list of valid account codes from the database + * + * @return array List of valid account codes. + */ + function getAccountCodes($permfilter = false) + { + global $operator_driver; + + $accountcodes = $operator_driver->getAccountCodes(); + + if (Horde_Auth::isAdmin() || + $GLOBALS['perms']->hasPermission('operator:accountcodes', + Horde_Auth::getAuth(), + Horde_Perms::READ)) { + $permfilter = false; + } + + if (!$permfilter || + $GLOBALS['perms']->hasPermission('operator:accountcodes:%', + Horde_Auth::geAuth(), + Horde_Perms::READ)) { + + // Add an option to select all accounts + $keys = $accountcodes; + array_unshift($keys, '%'); + $values = $accountcodes; + array_unshift($values, _("-- All Accounts Combined --")); + } + + // Only add the Empty value if it is exists in the backend + if ($index = array_search('', $values)) { + $values[$index] = _("-- Empty Accountcode --"); + } + + // Filter the returned list of account codes through Permissions + // if requested. + $accountcodes = array(); + foreach ($keys as $index => $accountcode) { + if ($permfilter) { + if (empty($accountcode)) { + $permitem = 'operator:accountcodes'; + } else { + $permitem = 'operator:accountcodes:' . $accountcode; + } + + if (Horde_Auth::isAdmin() || + $GLOBALS['perms']->hasPermission($permitem, + Horde_Auth::getAuth(), + Horde_Perms::SHOW)) { + $accountcodes[$accountcode] = $values[$index]; + } + } else { + $accountcodes[$accountcode] = $values[$index]; + } + } + return $accountcodes; + } + + function getGraphInfo($graphid) + { + switch($graphid) { + case 'numcalls': + return array( + 'title' => _("Number of Calls by Month"), + 'axisX' => _("Month"), + 'axisY' => _("Number of Calls"), + ); + break; + case 'minutes': + return array( + 'title' => _("Total Minutes Used by Month"), + 'axisX' => _("Month"), + 'axisY' => _("Minute"), + 'numberformat' => '%0.1f', + ); + break; + case 'failed': + return array( + 'title' => _("Number of Failed Calls by Month"), + 'axisX' => _("Month"), + 'axisY' => _("Failed Calls"), + ); + break; + } + } + +} diff --git a/operator/lib/api.php b/operator/lib/api.php new file mode 100644 index 000000000..116358928 --- /dev/null +++ b/operator/lib/api.php @@ -0,0 +1,44 @@ + + * @package Operator + */ + +$_services['perms'] = array( + 'args' => array(), + 'type' => '{urn:horde}stringArray'); + +function _operator_perms() +{ + static $perms = array(); + + if (!empty($perms)) { + return $perms; + } + + @define('OPERATOR_BASE', dirname(__FILE__) . '/..'); + require_once OPERATOR_BASE . '/lib/base.php'; + + $perms['tree']['operator']['accountcodes'] = false; + $perms['title']['operator:accountcodes'] = _("Account Codes"); + + $accountcodes = Operator::getAccountCodes(); + foreach ($accountcodes as $accountcode) { + $perms['tree']['operator']['accountcodes'][$accountcode] = false; + $perms['title']['operator:accountcodes:' . $accountcode] = $accountcode; + } + + return $perms; +} diff --git a/operator/lib/base.php b/operator/lib/base.php new file mode 100644 index 000000000..17ab6b543 --- /dev/null +++ b/operator/lib/base.php @@ -0,0 +1,56 @@ + + */ + +// Check for a prior definition of HORDE_BASE (perhaps by an auto_prepend_file +// definition for site customization). +if (!defined('HORDE_BASE')) { + @define('HORDE_BASE', dirname(__FILE__) . '/../../..'); +} + +// Load the Horde Framework core, and set up inclusion paths. +require_once HORDE_BASE . '/lib/core.php'; + +// Registry. +$registry = Horde_Registry::singleton(); +try { + $registry->pushApp('operator', !defined('AUTH_HANDLER')); +} catch (Horde_Exception $e) { + if ($e->getCode() == 'permission_denied') { + Horde::authenticationFailureRedirect(); + } + Horde::fatal($e, __FILE__, __LINE__, false); +} +$conf = &$GLOBALS['conf']; +@define('OPERATOR_TEMPLATES', $registry->get('templates')); + +// Notification system. +$notification = &Horde_Notification::singleton(); +$notification->attach('status'); + +// Define the base file path of Operator. +@define('OPERATOR_BASE', dirname(__FILE__) . '/..'); + +// Operator base library +require_once OPERATOR_BASE . '/lib/Operator.php'; + +// Operator backend. +require_once OPERATOR_BASE . '/lib/Driver.php'; +$GLOBALS['operator_driver'] = Operator_Driver::factory(); + +// Caching system for storing DB results +$cache = &Horde_Cache::singleton($GLOBALS['conf']['cache']['driver'], + Horde::getDriverConfig('cache', $GLOBALS['conf']['cache']['driver'])); + +// Start output compression. +Horde::compressOutput(); diff --git a/operator/lib/version.php b/operator/lib/version.php new file mode 100644 index 000000000..59bf5b296 --- /dev/null +++ b/operator/lib/version.php @@ -0,0 +1 @@ + diff --git a/operator/locale/en_US/help.xml b/operator/locale/en_US/help.xml new file mode 100644 index 000000000..f5afb83a4 --- /dev/null +++ b/operator/locale/en_US/help.xml @@ -0,0 +1,14 @@ + + + + + + Skeleton Overview + + What is Skeleton? + Use this module as a blank base for Horde appliations. All of the + files in this module are to be used as examples. Please customize them + for your own application's requirements. + + + diff --git a/operator/po/.cvsignore b/operator/po/.cvsignore new file mode 100644 index 000000000..fd8854c89 --- /dev/null +++ b/operator/po/.cvsignore @@ -0,0 +1 @@ +messages.po diff --git a/operator/po/README b/operator/po/README new file mode 100644 index 000000000..a985e94aa --- /dev/null +++ b/operator/po/README @@ -0,0 +1 @@ +see horde/po/README diff --git a/operator/search.php b/operator/search.php new file mode 100644 index 000000000..3eae3e5db --- /dev/null +++ b/operator/search.php @@ -0,0 +1,88 @@ + + */ + +@define('OPERATOR_BASE', dirname(__FILE__)); +require_once OPERATOR_BASE . '/lib/base.php'; + +// Form libraries. +require_once 'Horde/Form.php'; +require_once 'Horde/Form/Renderer.php'; +require_once OPERATOR_BASE . '/lib/Form/SearchCDR.php'; + +$renderer = new Horde_Form_Renderer(); +$vars = Horde_Variables::getDefaultVariables(); + +if (!$vars->exists('rowstart')) { + $rowstart = 0; +} elseif (!is_numeric($rowstart = $vars->get('rowstart'))) { + $notification->push(_("Invalid number for row start. Using 0.")); + $rowstart = 0; +} + +$numrows = $prefs->getValue('resultlimit'); +if (!is_numeric($numrows)) { + $notification->push(_("Invalid number for rows for search limit. Using 100.")); + $numrows = 100; +} + +$form = new SearchCDRForm(_("Search Call Detail Records"), $vars); +if ($form->isSubmitted() && $form->validate($vars, true)) { + $accountcode = $vars->get('accountcode'); + $dcontext = $vars->get('dcontext'); + if (empty($dcontext)) { + $dcontext = '%'; + } + $start = new Horde_Date($vars->get('startdate')); + + $end = new Horde_Date($vars->get('enddate')); + if (is_a($start, 'PEAR_Error') || is_a($end, 'PEAR_Error')) { + $notification->push(_("Invalid date requested.")); + } else { + $data = $operator_driver->getRecords($start, $end, $accountcode, + $dcontext, $rowstart, $numrows); + if (is_a($data, 'PEAR_Error')) { + $notification->push($data); + $data = array(); + } + $_SESSION['operator']['lastsearch']['params'] = array( + 'accountcode' => $vars->get('accountcode'), + 'dcontext' => $vars->get('dcontext'), + 'startdate' => $vars->get('startdate'), + 'enddate' => $vars->get('enddate')); + } +} else { + if (isset($_SESSION['operator']['lastsearch']['params'])) { + foreach($_SESSION['operator']['lastsearch']['params'] as $var => $val) { + $vars->set($var, $val); + } + } +} + +$title = _("Search Call Detail Records"); +Horde::addScriptFile('stripe.js', 'horde', true); + +require OPERATOR_TEMPLATES . '/common-header.inc'; +require OPERATOR_TEMPLATES . '/menu.inc'; + +$form->renderActive($renderer, $vars); + +$columns = unserialize($prefs->getValue('columns')); +if (!empty($data)) { + require OPERATOR_TEMPLATES . '/search/header.inc'; + unset($data['count'], $data['minutes'], $data['failed']); + foreach ($data as $record) { + require OPERATOR_TEMPLATES . '/search/row.inc'; + } + require OPERATOR_TEMPLATES . '/search/footer.inc'; +} + +require $registry->get('templates', 'horde') . '/common-footer.inc'; diff --git a/operator/templates/common-header.inc b/operator/templates/common-header.inc new file mode 100644 index 000000000..366aa52f4 --- /dev/null +++ b/operator/templates/common-header.inc @@ -0,0 +1,29 @@ + + + + + +' : '' ?> + +get('name'); +if (!empty($title)) $page_title .= ' :: ' . $title; +if (!empty($refresh_time) && ($refresh_time > 0) && !empty($refresh_url)) { + echo "\n"; +} + +Horde::includeScriptFiles(); + +?> +<?php echo htmlspecialchars($page_title) ?> + + + + +> diff --git a/operator/templates/menu.inc b/operator/templates/menu.inc new file mode 100644 index 000000000..c7b0898bc --- /dev/null +++ b/operator/templates/menu.inc @@ -0,0 +1,4 @@ + +notify(array('listeners' => 'status')) ?> diff --git a/operator/templates/search/footer.inc b/operator/templates/search/footer.inc new file mode 100644 index 000000000..000ca4b01 --- /dev/null +++ b/operator/templates/search/footer.inc @@ -0,0 +1 @@ + diff --git a/operator/templates/search/header.inc b/operator/templates/search/header.inc new file mode 100644 index 000000000..36127b06d --- /dev/null +++ b/operator/templates/search/header.inc @@ -0,0 +1,15 @@ +Call Statistics Summary:
+
    +
  • Total Calls:
  • +
  • Total Minutes:
  • +
  • Failed Calls:
  • +
+
+ + +' . Operator::getColumnName($column) . ''; +} +?> + diff --git a/operator/templates/search/row.inc b/operator/templates/search/row.inc new file mode 100644 index 000000000..c1359d062 --- /dev/null +++ b/operator/templates/search/row.inc @@ -0,0 +1,12 @@ + +"; + if ($column == 'amaflags') { + $record[$column] = Operator::getAMAFlagName($record[$column]); + } + echo $record[$column]; + echo ""; +} +?> + diff --git a/operator/themes/graphics/graphs.png b/operator/themes/graphics/graphs.png new file mode 100644 index 000000000..a5e467d0e Binary files /dev/null and b/operator/themes/graphics/graphs.png differ diff --git a/operator/themes/graphics/operator.png b/operator/themes/graphics/operator.png new file mode 100644 index 000000000..e819577a0 Binary files /dev/null and b/operator/themes/graphics/operator.png differ diff --git a/operator/themes/screen.css b/operator/themes/screen.css new file mode 100644 index 000000000..958d88a04 --- /dev/null +++ b/operator/themes/screen.css @@ -0,0 +1,3 @@ +/** + * $Horde: incubator/operator/themes/screen.css,v 1.1 2008/04/19 01:26:06 bklang Exp $ + */ diff --git a/operator/themes/silver/graphics/graphs.png b/operator/themes/silver/graphics/graphs.png new file mode 100644 index 000000000..01e933a61 Binary files /dev/null and b/operator/themes/silver/graphics/graphs.png differ diff --git a/operator/themes/silver/graphics/operator.png b/operator/themes/silver/graphics/operator.png new file mode 100644 index 000000000..cecc436fb Binary files /dev/null and b/operator/themes/silver/graphics/operator.png differ diff --git a/operator/themes/silver/themed_graphics b/operator/themes/silver/themed_graphics new file mode 100644 index 000000000..e69de29bb diff --git a/operator/viewgraph.php b/operator/viewgraph.php new file mode 100644 index 000000000..58dd9a02c --- /dev/null +++ b/operator/viewgraph.php @@ -0,0 +1,112 @@ + + */ + +@define('OPERATOR_BASE', dirname(__FILE__)); +require_once OPERATOR_BASE . '/lib/base.php'; + +// Form libraries. +require_once 'Horde/Form.php'; +require_once 'Horde/Form/Renderer.php'; +require_once OPERATOR_BASE . '/lib/Form/SearchCDR.php'; + +$renderer = new Horde_Form_Renderer(); +$vars = Horde_Variables::getDefaultVariables(); + +$form = new SearchCDRForm(_("Graph CDR Data"), $vars); +if ($form->isSubmitted() && $form->validate($vars, true)) { + $accountcode = $vars->get('accountcode'); + $dcontext = $vars->get('dcontext'); + if (empty($dcontext)) { + $dcontext = '%'; + } + $start = new Horde_Date($vars->get('startdate')); + $end = new Horde_Date($vars->get('enddate')); + + if (is_a($start, 'PEAR_Error') || is_a($end, 'PEAR_Error')) { + $notification->push(_("Invalid date requested.")); + } elseif (($end->month - $start->month) == 0 && + ($end->year - $start->year) == 0) { + $notification->push(_("You must select a range that includes more than one month to view these graphs.")); + } else { + // See if we have cached data + $cachekey = md5(serialize(array('getMonthlyCallStats', $start, $end, + $accountcode, $dcontext))); + // Use 0 lifetime to allow cache lifetime to be set when storing + // the object. + $stats = $cache->get($cachekey, 0); + if ($stats === false) { + $stats = $operator_driver->getMonthlyCallStats($start, + $end, + $accountcode, + $dcontext); + if (is_a($stats, 'PEAR_Error')) { + $notification->push($stats); + $stats = array(); + } else { + $res = $cache->set($cachekey, serialize($stats), 600); + if ($res === false) { + Horde::logMessage('The cache system has experienced an error. Unable to continue.', __FILE__, __LINE__, PEAR_LOG_ERR); + $notification->push(_("Internal error. Details have been logged for the administrator.")); + unset($stats); + } + } + } else { + // Cached data is stored serialized + $stats = unserialize($stats); + } + $_SESSION['operator']['lastsearch']['params'] = array( + 'accountcode' => $vars->get('accountcode'), + 'dcontext' => $vars->get('dcontext'), + 'startdate' => $vars->get('startdate'), + 'enddate' => $vars->get('enddate')); + } +} else { + if (isset($_SESSION['operator']['lastsearch']['params'])) { + foreach($_SESSION['operator']['lastsearch']['params'] as $var => $val) { + $vars->set($var, $val); + } + } + if (isset($_SESSION['operator']['lastsearch']['data'])) { + $data = $_SESSION['operator']['lastsearch']['data']; + } +} + +if (!empty($stats)) { + $numcalls_graph = $minutes_graph = $failed_graph = + Horde::applicationUrl('graphgen.php'); + + $numcalls_graph = Horde_Util::addParameter($numcalls_graph, array( + 'graph' => 'numcalls', 'key' => $cachekey)); + $minutes_graph = Horde_Util::addParameter($minutes_graph, array( + 'graph' => 'minutes', 'key' => $cachekey)); + $failed_graph = Horde_Util::addParameter($failed_graph, array( + 'graph' => 'failed', 'key' => $cachekey)); +} + +$title = _("Call Detail Records Graph"); + +require OPERATOR_TEMPLATES . '/common-header.inc'; +require OPERATOR_TEMPLATES . '/menu.inc'; + +$form->renderActive($renderer, $vars); + +if (!empty($stats)) { + echo '
'; + echo '
'; + echo '
'; + echo '
'; +} + +require $registry->get('templates', 'horde') . '/common-footer.inc'; + +// Don't leave stale stats lying about +unset($_SESSION['operator']['stats']); diff --git a/po/.cvsignore b/po/.cvsignore deleted file mode 100644 index fd8854c89..000000000 --- a/po/.cvsignore +++ /dev/null @@ -1 +0,0 @@ -messages.po diff --git a/po/README b/po/README deleted file mode 100644 index a985e94aa..000000000 --- a/po/README +++ /dev/null @@ -1 +0,0 @@ -see horde/po/README diff --git a/search.php b/search.php deleted file mode 100644 index 3eae3e5db..000000000 --- a/search.php +++ /dev/null @@ -1,88 +0,0 @@ - - */ - -@define('OPERATOR_BASE', dirname(__FILE__)); -require_once OPERATOR_BASE . '/lib/base.php'; - -// Form libraries. -require_once 'Horde/Form.php'; -require_once 'Horde/Form/Renderer.php'; -require_once OPERATOR_BASE . '/lib/Form/SearchCDR.php'; - -$renderer = new Horde_Form_Renderer(); -$vars = Horde_Variables::getDefaultVariables(); - -if (!$vars->exists('rowstart')) { - $rowstart = 0; -} elseif (!is_numeric($rowstart = $vars->get('rowstart'))) { - $notification->push(_("Invalid number for row start. Using 0.")); - $rowstart = 0; -} - -$numrows = $prefs->getValue('resultlimit'); -if (!is_numeric($numrows)) { - $notification->push(_("Invalid number for rows for search limit. Using 100.")); - $numrows = 100; -} - -$form = new SearchCDRForm(_("Search Call Detail Records"), $vars); -if ($form->isSubmitted() && $form->validate($vars, true)) { - $accountcode = $vars->get('accountcode'); - $dcontext = $vars->get('dcontext'); - if (empty($dcontext)) { - $dcontext = '%'; - } - $start = new Horde_Date($vars->get('startdate')); - - $end = new Horde_Date($vars->get('enddate')); - if (is_a($start, 'PEAR_Error') || is_a($end, 'PEAR_Error')) { - $notification->push(_("Invalid date requested.")); - } else { - $data = $operator_driver->getRecords($start, $end, $accountcode, - $dcontext, $rowstart, $numrows); - if (is_a($data, 'PEAR_Error')) { - $notification->push($data); - $data = array(); - } - $_SESSION['operator']['lastsearch']['params'] = array( - 'accountcode' => $vars->get('accountcode'), - 'dcontext' => $vars->get('dcontext'), - 'startdate' => $vars->get('startdate'), - 'enddate' => $vars->get('enddate')); - } -} else { - if (isset($_SESSION['operator']['lastsearch']['params'])) { - foreach($_SESSION['operator']['lastsearch']['params'] as $var => $val) { - $vars->set($var, $val); - } - } -} - -$title = _("Search Call Detail Records"); -Horde::addScriptFile('stripe.js', 'horde', true); - -require OPERATOR_TEMPLATES . '/common-header.inc'; -require OPERATOR_TEMPLATES . '/menu.inc'; - -$form->renderActive($renderer, $vars); - -$columns = unserialize($prefs->getValue('columns')); -if (!empty($data)) { - require OPERATOR_TEMPLATES . '/search/header.inc'; - unset($data['count'], $data['minutes'], $data['failed']); - foreach ($data as $record) { - require OPERATOR_TEMPLATES . '/search/row.inc'; - } - require OPERATOR_TEMPLATES . '/search/footer.inc'; -} - -require $registry->get('templates', 'horde') . '/common-footer.inc'; diff --git a/templates/common-header.inc b/templates/common-header.inc deleted file mode 100644 index 366aa52f4..000000000 --- a/templates/common-header.inc +++ /dev/null @@ -1,29 +0,0 @@ - - - - - -' : '' ?> - -get('name'); -if (!empty($title)) $page_title .= ' :: ' . $title; -if (!empty($refresh_time) && ($refresh_time > 0) && !empty($refresh_url)) { - echo "\n"; -} - -Horde::includeScriptFiles(); - -?> -<?php echo htmlspecialchars($page_title) ?> - - - - -> diff --git a/templates/menu.inc b/templates/menu.inc deleted file mode 100644 index c7b0898bc..000000000 --- a/templates/menu.inc +++ /dev/null @@ -1,4 +0,0 @@ - -notify(array('listeners' => 'status')) ?> diff --git a/templates/search/footer.inc b/templates/search/footer.inc deleted file mode 100644 index 000ca4b01..000000000 --- a/templates/search/footer.inc +++ /dev/null @@ -1 +0,0 @@ -
diff --git a/templates/search/header.inc b/templates/search/header.inc deleted file mode 100644 index 36127b06d..000000000 --- a/templates/search/header.inc +++ /dev/null @@ -1,15 +0,0 @@ -Call Statistics Summary:
-
    -
  • Total Calls:
  • -
  • Total Minutes:
  • -
  • Failed Calls:
  • -
-
- - -' . Operator::getColumnName($column) . ''; -} -?> - diff --git a/templates/search/row.inc b/templates/search/row.inc deleted file mode 100644 index c1359d062..000000000 --- a/templates/search/row.inc +++ /dev/null @@ -1,12 +0,0 @@ - -"; - if ($column == 'amaflags') { - $record[$column] = Operator::getAMAFlagName($record[$column]); - } - echo $record[$column]; - echo ""; -} -?> - diff --git a/themes/graphics/graphs.png b/themes/graphics/graphs.png deleted file mode 100644 index a5e467d0e..000000000 Binary files a/themes/graphics/graphs.png and /dev/null differ diff --git a/themes/graphics/operator.png b/themes/graphics/operator.png deleted file mode 100644 index e819577a0..000000000 Binary files a/themes/graphics/operator.png and /dev/null differ diff --git a/themes/screen.css b/themes/screen.css deleted file mode 100644 index 958d88a04..000000000 --- a/themes/screen.css +++ /dev/null @@ -1,3 +0,0 @@ -/** - * $Horde: incubator/operator/themes/screen.css,v 1.1 2008/04/19 01:26:06 bklang Exp $ - */ diff --git a/themes/silver/graphics/graphs.png b/themes/silver/graphics/graphs.png deleted file mode 100644 index 01e933a61..000000000 Binary files a/themes/silver/graphics/graphs.png and /dev/null differ diff --git a/themes/silver/graphics/operator.png b/themes/silver/graphics/operator.png deleted file mode 100644 index cecc436fb..000000000 Binary files a/themes/silver/graphics/operator.png and /dev/null differ diff --git a/themes/silver/themed_graphics b/themes/silver/themed_graphics deleted file mode 100644 index e69de29bb..000000000 diff --git a/viewgraph.php b/viewgraph.php deleted file mode 100644 index 58dd9a02c..000000000 --- a/viewgraph.php +++ /dev/null @@ -1,112 +0,0 @@ - - */ - -@define('OPERATOR_BASE', dirname(__FILE__)); -require_once OPERATOR_BASE . '/lib/base.php'; - -// Form libraries. -require_once 'Horde/Form.php'; -require_once 'Horde/Form/Renderer.php'; -require_once OPERATOR_BASE . '/lib/Form/SearchCDR.php'; - -$renderer = new Horde_Form_Renderer(); -$vars = Horde_Variables::getDefaultVariables(); - -$form = new SearchCDRForm(_("Graph CDR Data"), $vars); -if ($form->isSubmitted() && $form->validate($vars, true)) { - $accountcode = $vars->get('accountcode'); - $dcontext = $vars->get('dcontext'); - if (empty($dcontext)) { - $dcontext = '%'; - } - $start = new Horde_Date($vars->get('startdate')); - $end = new Horde_Date($vars->get('enddate')); - - if (is_a($start, 'PEAR_Error') || is_a($end, 'PEAR_Error')) { - $notification->push(_("Invalid date requested.")); - } elseif (($end->month - $start->month) == 0 && - ($end->year - $start->year) == 0) { - $notification->push(_("You must select a range that includes more than one month to view these graphs.")); - } else { - // See if we have cached data - $cachekey = md5(serialize(array('getMonthlyCallStats', $start, $end, - $accountcode, $dcontext))); - // Use 0 lifetime to allow cache lifetime to be set when storing - // the object. - $stats = $cache->get($cachekey, 0); - if ($stats === false) { - $stats = $operator_driver->getMonthlyCallStats($start, - $end, - $accountcode, - $dcontext); - if (is_a($stats, 'PEAR_Error')) { - $notification->push($stats); - $stats = array(); - } else { - $res = $cache->set($cachekey, serialize($stats), 600); - if ($res === false) { - Horde::logMessage('The cache system has experienced an error. Unable to continue.', __FILE__, __LINE__, PEAR_LOG_ERR); - $notification->push(_("Internal error. Details have been logged for the administrator.")); - unset($stats); - } - } - } else { - // Cached data is stored serialized - $stats = unserialize($stats); - } - $_SESSION['operator']['lastsearch']['params'] = array( - 'accountcode' => $vars->get('accountcode'), - 'dcontext' => $vars->get('dcontext'), - 'startdate' => $vars->get('startdate'), - 'enddate' => $vars->get('enddate')); - } -} else { - if (isset($_SESSION['operator']['lastsearch']['params'])) { - foreach($_SESSION['operator']['lastsearch']['params'] as $var => $val) { - $vars->set($var, $val); - } - } - if (isset($_SESSION['operator']['lastsearch']['data'])) { - $data = $_SESSION['operator']['lastsearch']['data']; - } -} - -if (!empty($stats)) { - $numcalls_graph = $minutes_graph = $failed_graph = - Horde::applicationUrl('graphgen.php'); - - $numcalls_graph = Horde_Util::addParameter($numcalls_graph, array( - 'graph' => 'numcalls', 'key' => $cachekey)); - $minutes_graph = Horde_Util::addParameter($minutes_graph, array( - 'graph' => 'minutes', 'key' => $cachekey)); - $failed_graph = Horde_Util::addParameter($failed_graph, array( - 'graph' => 'failed', 'key' => $cachekey)); -} - -$title = _("Call Detail Records Graph"); - -require OPERATOR_TEMPLATES . '/common-header.inc'; -require OPERATOR_TEMPLATES . '/menu.inc'; - -$form->renderActive($renderer, $vars); - -if (!empty($stats)) { - echo '
'; - echo '
'; - echo '
'; - echo '
'; -} - -require $registry->get('templates', 'horde') . '/common-footer.inc'; - -// Don't leave stale stats lying about -unset($_SESSION['operator']['stats']);