From 3c438e20eddf5e6fd992a9eacb95678dff7e5a3d Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Tue, 28 Apr 2009 08:52:21 +0000 Subject: [PATCH 001/227] Main trunk (i.e. latest development version) of itop SVN:trunk[53] From b756db09925f2d10b790a9da4b67a5d6bdac2e3e Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Tue, 28 Apr 2009 09:03:12 +0000 Subject: [PATCH 002/227] Moved under "trunk" to be able to track releases under "tags" SVN:trunk[55] --- .../userrights/userrightsmatrix.class.inc.php | 385 ++ .../userrights/userrightsnull.class.inc.php | 64 + application/ajaxwebpage.class.inc.php | 145 + application/application.inc.php | 12 + application/applicationcontext.class.inc.php | 81 + application/audit.category.class.inc.php | 45 + application/audit.rule.class.inc.php | 52 + application/cmdbabstract.class.inc.php | 647 +++ application/csvpage.class.inc.php | 35 + application/dialogstack.class.inc.php | 252 + application/displayblock.class.inc.php | 686 +++ application/iotask.class.inc.php | 54 + application/itopwebpage.class.inc.php | 414 ++ application/itopwizardwebpage.class.inc.php | 32 + application/loginwebpage.class.inc.php | 117 + application/menunode.class.inc.php | 219 + application/nicewebpage.class.inc.php | 76 + application/startup.inc.php | 7 + application/template.class.inc.php | 224 + application/templates/audit_category.html | 12 + application/ui.linkswidget.class.inc.php | 301 + application/uiwizard.class.inc.php | 257 + application/usercontext.class.inc.php | 106 + application/utils.inc.php | 80 + application/webpage.class.inc.php | 289 + application/wizardhelper.class.inc.php | 204 + application/xmlpage.class.inc.php | 36 + business/ChangeMgmt.php | 284 + business/Changes-04-Sep-2007.php | 32 + business/KEDB.php | 165 + business/ServiceMgmt.business.php | 261 + business/business_itopbegins.class.inc.php | 261 + business/business_test.class.inc.php | 366 ++ business/data.samples.inc.php | 4845 +++++++++++++++++ business/incident.business.php | 367 ++ business/itop.business.class.inc.php | 1629 ++++++ business/templates/Circuits.html | 12 + business/templates/application.html | 19 + business/templates/change.html | 17 + business/templates/contract.html | 18 + business/templates/default.html | 8 + business/templates/document.html | 13 + business/templates/group.html | 16 + business/templates/interface.html | 14 + business/templates/knownError.html | 11 + business/templates/location.html | 25 + business/templates/network.device.html | 25 + business/templates/pc.html | 27 + business/templates/person.html | 18 + business/templates/server.html | 30 + business/templates/service.html | 13 + business/templates/software.html | 13 + business/templates/team.html | 21 + business/templates/ticket.html | 21 + business/test_farm.class.inc.php | 272 + config-dist.php | 38 + config-test-farm.php | 35 + config-test-mymodel.php | 36 + core/MyHelpers.class.inc.php | 491 ++ core/archive.class.inc.php | 323 ++ core/attributedef.class.inc.php | 989 ++++ core/bulkchange.class.inc.php | 439 ++ core/cmdbchange.class.inc.php | 44 + core/cmdbchangeop.class.inc.php | 167 + core/cmdbobject.class.inc.php | 432 ++ core/cmdbsource.class.inc.php | 420 ++ core/config.class.inc.php | 272 + core/coreexception.class.inc.php | 63 + core/csvparser.class.inc.php | 192 + core/data.generator.class.inc.php | 362 ++ core/dbobject.class.php | 831 +++ core/dbobjectsearch.class.php | 973 ++++ core/dbobjectset.class.php | 250 + core/expression.class.inc.php | 485 ++ core/filterdef.class.inc.php | 296 + core/metamodel.class.php | 2643 +++++++++ core/oql/build.cmd | 3 + core/oql/oql-lexer.php | 522 ++ core/oql/oql-lexer.plex | 305 ++ core/oql/oql-parser.php | 1669 ++++++ core/oql/oql-parser.y | 250 + core/oql/oqlexception.class.inc.php | 78 + core/oql/oqlinterpreter.class.inc.php | 59 + core/oql/oqlquery.class.inc.php | 168 + core/sqlquery.class.inc.php | 413 ++ core/stimulus.class.inc.php | 59 + core/test.class.inc.php | 504 ++ core/userrights.class.inc.php | 211 + core/valuesetdef.class.inc.php | 238 + css/blue_green.css | 222 + css/date.picker.css | 117 + css/default.css | 165 + css/jqModal.css | 40 + css/jquery.autocomplete.css | 46 + css/jquery.tabs-ie.css | 15 + css/jquery.tabs.css | 76 + css/jquery.treeview.css | 47 + css/light-grey.css | 633 +++ images/Resize of Resize of erwanIncidents.jpg | Bin 0 -> 559 bytes images/Resize of Resize of erwanNetwork.jpg | Bin 0 -> 764 bytes images/Resize of Resize of erwanPC.jpg | Bin 0 -> 866 bytes images/Resize of erwanBuidling.jpg | Bin 0 -> 976 bytes images/Resize of erwanBuilding.jpg | Bin 0 -> 853 bytes images/Resize of erwanDocument.jpg | Bin 0 -> 883 bytes images/Resize of erwanIncidents.jpg | Bin 0 -> 859 bytes images/Resize of erwanNetwork.jpg | Bin 0 -> 1317 bytes images/Resize of erwanPC.jpg | Bin 0 -> 823 bytes images/Thumbs.db | Bin 0 -> 187392 bytes images/WanLinks.jpg | Bin 0 -> 3908 bytes images/actions_left.png | Bin 0 -> 205 bytes images/actions_right.png | Bin 0 -> 283 bytes images/bandeau2.gif | Bin 0 -> 1222 bytes images/bandeau3.gif | Bin 0 -> 132 bytes images/blue-corner.gif | Bin 0 -> 309 bytes images/bomb.jpg | Bin 0 -> 4145 bytes images/bomb.png | Bin 0 -> 8543 bytes images/calendar.png | Bin 0 -> 1091 bytes images/charts.swf | Bin 0 -> 29627 bytes images/charts_library/arno.swf | Bin 0 -> 6541 bytes images/charts_library/arst.swf | Bin 0 -> 6799 bytes images/charts_library/brfl.swf | Bin 0 -> 6848 bytes images/charts_library/brno.swf | Bin 0 -> 7004 bytes images/charts_library/brst.swf | Bin 0 -> 6854 bytes images/charts_library/cl3d.swf | Bin 0 -> 10125 bytes images/charts_library/clfl.swf | Bin 0 -> 6613 bytes images/charts_library/clno.swf | Bin 0 -> 6632 bytes images/charts_library/clp3.swf | Bin 0 -> 10106 bytes images/charts_library/cls3.swf | Bin 0 -> 10165 bytes images/charts_library/clst.swf | Bin 0 -> 6655 bytes images/charts_library/cnno.swf | Bin 0 -> 6801 bytes images/charts_library/lnno.swf | Bin 0 -> 7239 bytes images/charts_library/mxno.swf | Bin 0 -> 8736 bytes images/charts_library/pi3d.swf | Bin 0 -> 8592 bytes images/charts_library/pino.swf | Bin 0 -> 6152 bytes images/charts_library/pono.swf | Bin 0 -> 8178 bytes images/charts_library/scno.swf | Bin 0 -> 7209 bytes images/clean-mid.png | Bin 0 -> 2406 bytes images/clean.png | Bin 0 -> 8714 bytes images/connect_to_network.png | Bin 0 -> 15254 bytes images/contacts.gif | Bin 0 -> 1057 bytes images/contacts_big.gif | Bin 0 -> 4586 bytes images/database exi.png | Bin 0 -> 14728 bytes images/database.png | Bin 0 -> 13514 bytes images/device.gif | Bin 0 -> 1070 bytes images/devices_big.gif | Bin 0 -> 4142 bytes images/documents.jpg | Bin 0 -> 7871 bytes images/drawer-handle.gif | Bin 0 -> 318 bytes images/erwanBuilding.jpg | Bin 0 -> 848 bytes images/erwanContracts.jpg | Bin 0 -> 1830 bytes images/erwanContracts2.jpg | Bin 0 -> 3298 bytes images/erwanContracts3.jpg | Bin 0 -> 2609 bytes images/erwanDocument.jpg | Bin 0 -> 887 bytes images/erwanIncidents.jpg | Bin 0 -> 861 bytes images/erwanMobilePhones.jpg | Bin 0 -> 2569 bytes images/erwanNetwork.jpg | Bin 0 -> 766 bytes images/erwanPC.jpg | Bin 0 -> 828 bytes images/erwanServices.jpg | Bin 0 -> 2839 bytes images/erwanTask.jpg | Bin 0 -> 2914 bytes images/erwanTools.jpg | Bin 0 -> 2247 bytes images/folder_documents.png | Bin 0 -> 11783 bytes images/green-corner.png | Bin 0 -> 287 bytes images/green-header.gif | Bin 0 -> 120 bytes images/green-square.gif | Bin 0 -> 56 bytes images/grey-header.gif | Bin 0 -> 122 bytes images/hgrabber2.gif | Bin 0 -> 217 bytes images/hgrabber2_active.gif | Bin 0 -> 220 bytes images/home.gif | Bin 0 -> 988 bytes images/iTop-icon.ico | Bin 0 -> 1150 bytes images/iTop-icon.png | Bin 0 -> 608 bytes images/iTop.gif | Bin 0 -> 3681 bytes images/iTop2.gif | Bin 0 -> 3765 bytes images/imageChange.gif | Bin 0 -> 4227 bytes images/imageChange.png | Bin 0 -> 2107 bytes images/indicator.gif | Bin 0 -> 1553 bytes images/indicator_arrows.gif | Bin 0 -> 729 bytes images/info-mid.png | Bin 0 -> 3165 bytes images/info.png | Bin 0 -> 13918 bytes images/kservices-big.png | Bin 0 -> 19890 bytes images/laptop_pcmcia.png | Bin 0 -> 15250 bytes images/left-border.gif | Bin 0 -> 83 bytes images/lifecycle/bizChangeTicket.png | Bin 0 -> 39688 bytes images/lifecycle/bizContract.png | Bin 0 -> 36691 bytes images/lifecycle/bizIncidentTicket.png | Bin 0 -> 36813 bytes images/lifecycle/bizServer.png | Bin 0 -> 78782 bytes images/location.gif | Bin 0 -> 998 bytes images/magnifier.gif | Bin 0 -> 534 bytes images/messagebox_warning-mid.png | Bin 0 -> 3168 bytes images/messagebox_warning.png | Bin 0 -> 7104 bytes images/mini-arrow-green-open.gif | Bin 0 -> 53 bytes images/mini-arrow-green.gif | Bin 0 -> 54 bytes images/minus.gif | Bin 0 -> 139 bytes images/network-server.png | Bin 0 -> 9234 bytes images/open-flash-chart.swf | Bin 0 -> 263109 bytes images/orange-header.gif | Bin 0 -> 122 bytes images/plus.gif | Bin 0 -> 142 bytes images/red-arrow.gif | Bin 0 -> 56 bytes images/red-header.gif | Bin 0 -> 122 bytes images/settings.gif | Bin 0 -> 256 bytes images/software.jpg | Bin 0 -> 2186 bytes images/starthere.png | Bin 0 -> 14272 bytes images/std_view.gif | Bin 0 -> 571 bytes images/stop-mid.png | Bin 0 -> 2616 bytes images/stop.png | Bin 0 -> 9678 bytes images/tab-topleft.gif | Bin 0 -> 46 bytes images/tab.png | Bin 0 -> 593 bytes images/tar.png | Bin 0 -> 20427 bytes images/tv-collapsable-last.gif | Bin 0 -> 85 bytes images/tv-collapsable.gif | Bin 0 -> 781 bytes images/tv-expandable-last.gif | Bin 0 -> 89 bytes images/tv-expandable.gif | Bin 0 -> 787 bytes images/tv-item-last.gif | Bin 0 -> 65 bytes images/tv-item.gif | Bin 0 -> 750 bytes images/users2-big.png | Bin 0 -> 11703 bytes images/vgrabber2.gif | Bin 0 -> 381 bytes images/vgrabber2_active.gif | Bin 0 -> 258 bytes images/vsplitter-grey.gif | Bin 0 -> 59 bytes images/wan-mid.gif | Bin 0 -> 547 bytes images/wan.gif | Bin 0 -> 262 bytes images/wizActiveStepLeft.gif | Bin 0 -> 362 bytes images/wizActiveStepRight.gif | Bin 0 -> 501 bytes images/wizArrow.gif | Bin 0 -> 155 bytes images/wizStepLeft.gif | Bin 0 -> 217 bytes images/wizStepRight.gif | Bin 0 -> 425 bytes images/zoom.gif | Bin 0 -> 275 bytes index.php | 32 + js/LinksWidget.js | 112 + js/date.js | 467 ++ js/dimensions.js | 317 ++ js/forms-json-utils.js | 106 + js/hovertip.js | 458 ++ js/jqModal.js | 67 + js/jquery.autocomplete.js | 513 ++ js/jquery.bgiframe.js | 49 + js/jquery.blockUI.js | 312 ++ js/jquery.date.picker.js | 1056 ++++ js/jquery.dimensions.js | 308 ++ js/jquery.history_remote.pack.js | 1 + js/jquery.jdMenu.js | 435 ++ js/jquery.latest.js | 3549 ++++++++++++ js/jquery.pack.js | 1 + js/jquery.tablehover.js | 426 ++ js/jquery.tablesorter.min.js | 2 + js/jquery.tabs-ie.css | 15 + js/jquery.tabs.pack.js | 1 + js/jquery.treeview.js | 223 + js/json.js | 473 ++ js/json/json2.js | 461 ++ js/splitter.js | 180 + js/swfobject.js | 5 + js/themes/dark/dark.css | 11 + js/themes/dark/dark.form.css | 192 + js/themes/dark/dark.form.png | Bin 0 -> 12308 bytes js/themes/dark/dark.menu.css | 9 + js/themes/dark/dark.modal.css | 93 + js/themes/dark/dark.tabs.css | 73 + js/themes/dark/dark.tree.css | 61 + js/themes/flora/flora.accordion.css | 12 + js/themes/flora/flora.all.css | 9 + js/themes/flora/flora.calendar.css | 167 + js/themes/flora/flora.css | 2 + js/themes/flora/flora.dialog.css | 86 + js/themes/flora/flora.menu.css | 8 + js/themes/flora/flora.resizable.css | 20 + js/themes/flora/flora.shadow.css | 33 + js/themes/flora/flora.slider.css | 8 + js/themes/flora/flora.tablesorter.css | 40 + js/themes/flora/flora.tabs.css | 80 + js/themes/flora/i/Thumbs.db | Bin 0 -> 39424 bytes js/themes/flora/i/accordion-left-act.png | Bin 0 -> 249 bytes js/themes/flora/i/accordion-left-over.png | Bin 0 -> 174 bytes js/themes/flora/i/accordion-left.png | Bin 0 -> 174 bytes js/themes/flora/i/accordion-middle-act.png | Bin 0 -> 148 bytes js/themes/flora/i/accordion-middle-over.png | Bin 0 -> 122 bytes js/themes/flora/i/accordion-middle.png | Bin 0 -> 122 bytes js/themes/flora/i/accordion-right-act.png | Bin 0 -> 245 bytes js/themes/flora/i/accordion-right-over.png | Bin 0 -> 177 bytes js/themes/flora/i/accordion-right.png | Bin 0 -> 177 bytes js/themes/flora/i/asc.gif | Bin 0 -> 54 bytes js/themes/flora/i/bg.gif | Bin 0 -> 64 bytes js/themes/flora/i/desc.gif | Bin 0 -> 54 bytes js/themes/flora/i/dialog-e.gif | Bin 0 -> 440 bytes js/themes/flora/i/dialog-n.gif | Bin 0 -> 569 bytes js/themes/flora/i/dialog-ne.gif | Bin 0 -> 353 bytes js/themes/flora/i/dialog-nw.gif | Bin 0 -> 353 bytes js/themes/flora/i/dialog-s.gif | Bin 0 -> 434 bytes js/themes/flora/i/dialog-se.gif | Bin 0 -> 175 bytes js/themes/flora/i/dialog-sw.gif | Bin 0 -> 175 bytes js/themes/flora/i/dialog-title.gif | Bin 0 -> 238 bytes .../flora/i/dialog-titlebar-close-hover.png | Bin 0 -> 2927 bytes js/themes/flora/i/dialog-titlebar-close.png | Bin 0 -> 2880 bytes js/themes/flora/i/dialog-w.gif | Bin 0 -> 437 bytes js/themes/flora/i/menu-submenu.gif | Bin 0 -> 93 bytes js/themes/flora/i/resizable-e.gif | Bin 0 -> 338 bytes js/themes/flora/i/resizable-n.gif | Bin 0 -> 341 bytes js/themes/flora/i/resizable-ne.gif | Bin 0 -> 124 bytes js/themes/flora/i/resizable-nw.gif | Bin 0 -> 91 bytes js/themes/flora/i/resizable-s.gif | Bin 0 -> 341 bytes js/themes/flora/i/resizable-se.gif | Bin 0 -> 120 bytes js/themes/flora/i/resizable-sw.gif | Bin 0 -> 175 bytes js/themes/flora/i/resizable-w.gif | Bin 0 -> 339 bytes js/themes/flora/i/shadow.png | Bin 0 -> 3977 bytes js/themes/flora/i/slider-bg-1.png | Bin 0 -> 204 bytes js/themes/flora/i/slider-bg-2.png | Bin 0 -> 326 bytes js/themes/flora/i/slider-handle.gif | Bin 0 -> 176 bytes js/themes/flora/i/tabs.gif | Bin 0 -> 377 bytes js/themes/light/light.css | 11 + js/themes/light/light.form.css | 45 + js/themes/light/light.menu.css | 8 + js/themes/light/light.modal.css | 91 + js/themes/light/light.tabs.css | 71 + js/themes/light/light.tree.css | 61 + js/ui.accordion.js | 246 + js/ui.calendar.js | 871 +++ js/ui.dialog.js | 133 + js/ui.draggable.ext.js | 259 + js/ui.draggable.js | 182 + js/ui.droppable.ext.js | 25 + js/ui.droppable.js | 201 + js/ui.magnifier.js | 191 + js/ui.mouse.js | 253 + js/ui.resizable.js | 304 ++ js/ui.selectable.js | 116 + js/ui.shadow.js | 87 + js/ui.slider.js | 294 + js/ui.sortable.js | 262 + js/ui.tablesorter.js | 808 +++ js/ui.tabs.js | 445 ++ js/wizard.utils.js | 163 + js/wizardhelper.js | 108 + pages/ITopConsultant.php | 467 ++ pages/UI.php | 660 +++ pages/UniversalSearch.php | 142 + pages/advanced_search.php | 310 ++ pages/ajax.php | 40 + pages/ajax.render.php | 218 + pages/audit.php | 149 + pages/csvimport.php | 527 ++ pages/data_generator.php | 321 ++ pages/db_importer.php | 171 + pages/graphviz.php | 82 + pages/incident.php | 757 +++ pages/index.php | 704 +++ pages/opensearch.xml | 8 + pages/opensearch.xml.php | 16 + pages/php-ofc-library/JSON.php | 806 +++ pages/php-ofc-library/README.txt | 16 + pages/php-ofc-library/json_format.php | 86 + pages/php-ofc-library/ofc_area_base.php | 66 + pages/php-ofc-library/ofc_area_hollow.php | 10 + pages/php-ofc-library/ofc_area_line.php | 10 + pages/php-ofc-library/ofc_bar.php | 34 + pages/php-ofc-library/ofc_bar_3d.php | 31 + pages/php-ofc-library/ofc_bar_base.php | 41 + pages/php-ofc-library/ofc_bar_filled.php | 39 + pages/php-ofc-library/ofc_bar_glass.php | 33 + pages/php-ofc-library/ofc_bar_sketch.php | 23 + pages/php-ofc-library/ofc_bar_stack.php | 50 + pages/php-ofc-library/ofc_hbar.php | 64 + pages/php-ofc-library/ofc_line.php | 9 + pages/php-ofc-library/ofc_line_base.php | 70 + pages/php-ofc-library/ofc_line_dot.php | 33 + pages/php-ofc-library/ofc_line_hollow.php | 9 + pages/php-ofc-library/ofc_line_style.php | 11 + pages/php-ofc-library/ofc_pie.php | 109 + pages/php-ofc-library/ofc_radar_axis.php | 47 + .../php-ofc-library/ofc_radar_axis_labels.php | 15 + .../ofc_radar_spoke_labels.php | 15 + pages/php-ofc-library/ofc_scatter.php | 42 + pages/php-ofc-library/ofc_scatter_line.php | 27 + pages/php-ofc-library/ofc_shape.php | 25 + pages/php-ofc-library/ofc_title.php | 15 + pages/php-ofc-library/ofc_tooltip.php | 51 + pages/php-ofc-library/ofc_upload_image.php | 61 + pages/php-ofc-library/ofc_x_axis.php | 77 + pages/php-ofc-library/ofc_x_axis_label.php | 42 + pages/php-ofc-library/ofc_x_axis_labels.php | 34 + pages/php-ofc-library/ofc_x_legend.php | 15 + pages/php-ofc-library/ofc_y_axis.php | 17 + pages/php-ofc-library/ofc_y_axis_base.php | 56 + pages/php-ofc-library/ofc_y_axis_right.php | 6 + pages/php-ofc-library/ofc_y_legend.php | 15 + .../open-flash-chart-object.php | 109 + pages/php-ofc-library/open-flash-chart.php | 138 + pages/schema.php | 475 ++ pages/sibusql.php | 57 + pages/test.php | 126 + pages/testlist.inc.php | 1132 ++++ setup/ajax.dataloader.php | 64 + setup/data/01.organizations.xml | 15 + setup/data/02.locations.xml | 22 + setup/data/03.persons.xml | 33 + setup/data/04.teams.xml | 19 + setup/data/05.pcs.xml | 1865 +++++++ setup/data/06.servers.xml | 4063 ++++++++++++++ setup/data/07.applications.xml | 33 + setup/data/08.nw-devices.xml | 71 + setup/data/09.links_contacts.xml | 23 + setup/data/10.workgroups.xml | 17 + setup/data/11.incidents.xml | 132 + setup/data/12.relatedtickets.xml | 8 + setup/data/13.infratickets.xml | 33 + setup/data/14.contacttickets.xml | 13 + setup/data/15.changetickets.xml | 85 + setup/data/16.infrachangetickets.xml | 23 + setup/data/17.contactchangetickets.xml | 23 + setup/data/18.contracts.xml | 23 + setup/data/19.infracontracts.xml | 9 + setup/data/20.contactcontracts.xml | 13 + setup/data/21.auditcategories.xml | 8 + setup/data/22.auditrules.xml | 17 + setup/data/export.cmd | 27 + setup/export_menus.cmd | 6 + setup/index.php | 536 ++ setup/jquery.progression.js | 193 + setup/menus.xml | 610 +++ setup/orange-progress.gif | Bin 0 -> 40302 bytes setup/setup.js | 138 + setup/setuppage.class.inc.php | 176 + setup/xmldataloader.class.inc.php | 282 + toolkit.php | 53 + webservices/export.php | 73 + webservices/import.php | 168 + 422 files changed, 62557 insertions(+) create mode 100644 addons/userrights/userrightsmatrix.class.inc.php create mode 100644 addons/userrights/userrightsnull.class.inc.php create mode 100644 application/ajaxwebpage.class.inc.php create mode 100644 application/application.inc.php create mode 100644 application/applicationcontext.class.inc.php create mode 100644 application/audit.category.class.inc.php create mode 100644 application/audit.rule.class.inc.php create mode 100644 application/cmdbabstract.class.inc.php create mode 100644 application/csvpage.class.inc.php create mode 100644 application/dialogstack.class.inc.php create mode 100644 application/displayblock.class.inc.php create mode 100644 application/iotask.class.inc.php create mode 100644 application/itopwebpage.class.inc.php create mode 100644 application/itopwizardwebpage.class.inc.php create mode 100644 application/loginwebpage.class.inc.php create mode 100644 application/menunode.class.inc.php create mode 100644 application/nicewebpage.class.inc.php create mode 100644 application/startup.inc.php create mode 100644 application/template.class.inc.php create mode 100644 application/templates/audit_category.html create mode 100644 application/ui.linkswidget.class.inc.php create mode 100644 application/uiwizard.class.inc.php create mode 100644 application/usercontext.class.inc.php create mode 100644 application/utils.inc.php create mode 100644 application/webpage.class.inc.php create mode 100644 application/wizardhelper.class.inc.php create mode 100644 application/xmlpage.class.inc.php create mode 100644 business/ChangeMgmt.php create mode 100644 business/Changes-04-Sep-2007.php create mode 100644 business/KEDB.php create mode 100644 business/ServiceMgmt.business.php create mode 100644 business/business_itopbegins.class.inc.php create mode 100644 business/business_test.class.inc.php create mode 100644 business/data.samples.inc.php create mode 100644 business/incident.business.php create mode 100644 business/itop.business.class.inc.php create mode 100644 business/templates/Circuits.html create mode 100644 business/templates/application.html create mode 100644 business/templates/change.html create mode 100644 business/templates/contract.html create mode 100644 business/templates/default.html create mode 100644 business/templates/document.html create mode 100644 business/templates/group.html create mode 100644 business/templates/interface.html create mode 100644 business/templates/knownError.html create mode 100644 business/templates/location.html create mode 100644 business/templates/network.device.html create mode 100644 business/templates/pc.html create mode 100644 business/templates/person.html create mode 100644 business/templates/server.html create mode 100644 business/templates/service.html create mode 100644 business/templates/software.html create mode 100644 business/templates/team.html create mode 100644 business/templates/ticket.html create mode 100644 business/test_farm.class.inc.php create mode 100644 config-dist.php create mode 100644 config-test-farm.php create mode 100644 config-test-mymodel.php create mode 100644 core/MyHelpers.class.inc.php create mode 100644 core/archive.class.inc.php create mode 100644 core/attributedef.class.inc.php create mode 100644 core/bulkchange.class.inc.php create mode 100644 core/cmdbchange.class.inc.php create mode 100644 core/cmdbchangeop.class.inc.php create mode 100644 core/cmdbobject.class.inc.php create mode 100644 core/cmdbsource.class.inc.php create mode 100644 core/config.class.inc.php create mode 100644 core/coreexception.class.inc.php create mode 100644 core/csvparser.class.inc.php create mode 100644 core/data.generator.class.inc.php create mode 100644 core/dbobject.class.php create mode 100644 core/dbobjectsearch.class.php create mode 100644 core/dbobjectset.class.php create mode 100644 core/expression.class.inc.php create mode 100644 core/filterdef.class.inc.php create mode 100644 core/metamodel.class.php create mode 100644 core/oql/build.cmd create mode 100644 core/oql/oql-lexer.php create mode 100644 core/oql/oql-lexer.plex create mode 100644 core/oql/oql-parser.php create mode 100644 core/oql/oql-parser.y create mode 100644 core/oql/oqlexception.class.inc.php create mode 100644 core/oql/oqlinterpreter.class.inc.php create mode 100644 core/oql/oqlquery.class.inc.php create mode 100644 core/sqlquery.class.inc.php create mode 100644 core/stimulus.class.inc.php create mode 100644 core/test.class.inc.php create mode 100644 core/userrights.class.inc.php create mode 100644 core/valuesetdef.class.inc.php create mode 100644 css/blue_green.css create mode 100644 css/date.picker.css create mode 100644 css/default.css create mode 100644 css/jqModal.css create mode 100644 css/jquery.autocomplete.css create mode 100644 css/jquery.tabs-ie.css create mode 100644 css/jquery.tabs.css create mode 100644 css/jquery.treeview.css create mode 100644 css/light-grey.css create mode 100644 images/Resize of Resize of erwanIncidents.jpg create mode 100644 images/Resize of Resize of erwanNetwork.jpg create mode 100644 images/Resize of Resize of erwanPC.jpg create mode 100644 images/Resize of erwanBuidling.jpg create mode 100644 images/Resize of erwanBuilding.jpg create mode 100644 images/Resize of erwanDocument.jpg create mode 100644 images/Resize of erwanIncidents.jpg create mode 100644 images/Resize of erwanNetwork.jpg create mode 100644 images/Resize of erwanPC.jpg create mode 100644 images/Thumbs.db create mode 100644 images/WanLinks.jpg create mode 100644 images/actions_left.png create mode 100644 images/actions_right.png create mode 100644 images/bandeau2.gif create mode 100644 images/bandeau3.gif create mode 100644 images/blue-corner.gif create mode 100644 images/bomb.jpg create mode 100644 images/bomb.png create mode 100644 images/calendar.png create mode 100644 images/charts.swf create mode 100644 images/charts_library/arno.swf create mode 100644 images/charts_library/arst.swf create mode 100644 images/charts_library/brfl.swf create mode 100644 images/charts_library/brno.swf create mode 100644 images/charts_library/brst.swf create mode 100644 images/charts_library/cl3d.swf create mode 100644 images/charts_library/clfl.swf create mode 100644 images/charts_library/clno.swf create mode 100644 images/charts_library/clp3.swf create mode 100644 images/charts_library/cls3.swf create mode 100644 images/charts_library/clst.swf create mode 100644 images/charts_library/cnno.swf create mode 100644 images/charts_library/lnno.swf create mode 100644 images/charts_library/mxno.swf create mode 100644 images/charts_library/pi3d.swf create mode 100644 images/charts_library/pino.swf create mode 100644 images/charts_library/pono.swf create mode 100644 images/charts_library/scno.swf create mode 100644 images/clean-mid.png create mode 100644 images/clean.png create mode 100644 images/connect_to_network.png create mode 100644 images/contacts.gif create mode 100644 images/contacts_big.gif create mode 100644 images/database exi.png create mode 100644 images/database.png create mode 100644 images/device.gif create mode 100644 images/devices_big.gif create mode 100644 images/documents.jpg create mode 100644 images/drawer-handle.gif create mode 100644 images/erwanBuilding.jpg create mode 100644 images/erwanContracts.jpg create mode 100644 images/erwanContracts2.jpg create mode 100644 images/erwanContracts3.jpg create mode 100644 images/erwanDocument.jpg create mode 100644 images/erwanIncidents.jpg create mode 100644 images/erwanMobilePhones.jpg create mode 100644 images/erwanNetwork.jpg create mode 100644 images/erwanPC.jpg create mode 100644 images/erwanServices.jpg create mode 100644 images/erwanTask.jpg create mode 100644 images/erwanTools.jpg create mode 100644 images/folder_documents.png create mode 100644 images/green-corner.png create mode 100644 images/green-header.gif create mode 100644 images/green-square.gif create mode 100644 images/grey-header.gif create mode 100644 images/hgrabber2.gif create mode 100644 images/hgrabber2_active.gif create mode 100644 images/home.gif create mode 100644 images/iTop-icon.ico create mode 100644 images/iTop-icon.png create mode 100644 images/iTop.gif create mode 100644 images/iTop2.gif create mode 100644 images/imageChange.gif create mode 100644 images/imageChange.png create mode 100644 images/indicator.gif create mode 100644 images/indicator_arrows.gif create mode 100644 images/info-mid.png create mode 100644 images/info.png create mode 100644 images/kservices-big.png create mode 100644 images/laptop_pcmcia.png create mode 100644 images/left-border.gif create mode 100644 images/lifecycle/bizChangeTicket.png create mode 100644 images/lifecycle/bizContract.png create mode 100644 images/lifecycle/bizIncidentTicket.png create mode 100644 images/lifecycle/bizServer.png create mode 100644 images/location.gif create mode 100644 images/magnifier.gif create mode 100644 images/messagebox_warning-mid.png create mode 100644 images/messagebox_warning.png create mode 100644 images/mini-arrow-green-open.gif create mode 100644 images/mini-arrow-green.gif create mode 100644 images/minus.gif create mode 100644 images/network-server.png create mode 100644 images/open-flash-chart.swf create mode 100644 images/orange-header.gif create mode 100644 images/plus.gif create mode 100644 images/red-arrow.gif create mode 100644 images/red-header.gif create mode 100644 images/settings.gif create mode 100644 images/software.jpg create mode 100644 images/starthere.png create mode 100644 images/std_view.gif create mode 100644 images/stop-mid.png create mode 100644 images/stop.png create mode 100644 images/tab-topleft.gif create mode 100644 images/tab.png create mode 100644 images/tar.png create mode 100644 images/tv-collapsable-last.gif create mode 100644 images/tv-collapsable.gif create mode 100644 images/tv-expandable-last.gif create mode 100644 images/tv-expandable.gif create mode 100644 images/tv-item-last.gif create mode 100644 images/tv-item.gif create mode 100644 images/users2-big.png create mode 100644 images/vgrabber2.gif create mode 100644 images/vgrabber2_active.gif create mode 100644 images/vsplitter-grey.gif create mode 100644 images/wan-mid.gif create mode 100644 images/wan.gif create mode 100644 images/wizActiveStepLeft.gif create mode 100644 images/wizActiveStepRight.gif create mode 100644 images/wizArrow.gif create mode 100644 images/wizStepLeft.gif create mode 100644 images/wizStepRight.gif create mode 100644 images/zoom.gif create mode 100644 index.php create mode 100644 js/LinksWidget.js create mode 100644 js/date.js create mode 100644 js/dimensions.js create mode 100644 js/forms-json-utils.js create mode 100644 js/hovertip.js create mode 100644 js/jqModal.js create mode 100644 js/jquery.autocomplete.js create mode 100644 js/jquery.bgiframe.js create mode 100644 js/jquery.blockUI.js create mode 100644 js/jquery.date.picker.js create mode 100644 js/jquery.dimensions.js create mode 100644 js/jquery.history_remote.pack.js create mode 100644 js/jquery.jdMenu.js create mode 100644 js/jquery.latest.js create mode 100644 js/jquery.pack.js create mode 100644 js/jquery.tablehover.js create mode 100644 js/jquery.tablesorter.min.js create mode 100644 js/jquery.tabs-ie.css create mode 100644 js/jquery.tabs.pack.js create mode 100644 js/jquery.treeview.js create mode 100644 js/json.js create mode 100644 js/json/json2.js create mode 100644 js/splitter.js create mode 100644 js/swfobject.js create mode 100644 js/themes/dark/dark.css create mode 100644 js/themes/dark/dark.form.css create mode 100644 js/themes/dark/dark.form.png create mode 100644 js/themes/dark/dark.menu.css create mode 100644 js/themes/dark/dark.modal.css create mode 100644 js/themes/dark/dark.tabs.css create mode 100644 js/themes/dark/dark.tree.css create mode 100644 js/themes/flora/flora.accordion.css create mode 100644 js/themes/flora/flora.all.css create mode 100644 js/themes/flora/flora.calendar.css create mode 100644 js/themes/flora/flora.css create mode 100644 js/themes/flora/flora.dialog.css create mode 100644 js/themes/flora/flora.menu.css create mode 100644 js/themes/flora/flora.resizable.css create mode 100644 js/themes/flora/flora.shadow.css create mode 100644 js/themes/flora/flora.slider.css create mode 100644 js/themes/flora/flora.tablesorter.css create mode 100644 js/themes/flora/flora.tabs.css create mode 100644 js/themes/flora/i/Thumbs.db create mode 100644 js/themes/flora/i/accordion-left-act.png create mode 100644 js/themes/flora/i/accordion-left-over.png create mode 100644 js/themes/flora/i/accordion-left.png create mode 100644 js/themes/flora/i/accordion-middle-act.png create mode 100644 js/themes/flora/i/accordion-middle-over.png create mode 100644 js/themes/flora/i/accordion-middle.png create mode 100644 js/themes/flora/i/accordion-right-act.png create mode 100644 js/themes/flora/i/accordion-right-over.png create mode 100644 js/themes/flora/i/accordion-right.png create mode 100644 js/themes/flora/i/asc.gif create mode 100644 js/themes/flora/i/bg.gif create mode 100644 js/themes/flora/i/desc.gif create mode 100644 js/themes/flora/i/dialog-e.gif create mode 100644 js/themes/flora/i/dialog-n.gif create mode 100644 js/themes/flora/i/dialog-ne.gif create mode 100644 js/themes/flora/i/dialog-nw.gif create mode 100644 js/themes/flora/i/dialog-s.gif create mode 100644 js/themes/flora/i/dialog-se.gif create mode 100644 js/themes/flora/i/dialog-sw.gif create mode 100644 js/themes/flora/i/dialog-title.gif create mode 100644 js/themes/flora/i/dialog-titlebar-close-hover.png create mode 100644 js/themes/flora/i/dialog-titlebar-close.png create mode 100644 js/themes/flora/i/dialog-w.gif create mode 100644 js/themes/flora/i/menu-submenu.gif create mode 100644 js/themes/flora/i/resizable-e.gif create mode 100644 js/themes/flora/i/resizable-n.gif create mode 100644 js/themes/flora/i/resizable-ne.gif create mode 100644 js/themes/flora/i/resizable-nw.gif create mode 100644 js/themes/flora/i/resizable-s.gif create mode 100644 js/themes/flora/i/resizable-se.gif create mode 100644 js/themes/flora/i/resizable-sw.gif create mode 100644 js/themes/flora/i/resizable-w.gif create mode 100644 js/themes/flora/i/shadow.png create mode 100644 js/themes/flora/i/slider-bg-1.png create mode 100644 js/themes/flora/i/slider-bg-2.png create mode 100644 js/themes/flora/i/slider-handle.gif create mode 100644 js/themes/flora/i/tabs.gif create mode 100644 js/themes/light/light.css create mode 100644 js/themes/light/light.form.css create mode 100644 js/themes/light/light.menu.css create mode 100644 js/themes/light/light.modal.css create mode 100644 js/themes/light/light.tabs.css create mode 100644 js/themes/light/light.tree.css create mode 100644 js/ui.accordion.js create mode 100644 js/ui.calendar.js create mode 100644 js/ui.dialog.js create mode 100644 js/ui.draggable.ext.js create mode 100644 js/ui.draggable.js create mode 100644 js/ui.droppable.ext.js create mode 100644 js/ui.droppable.js create mode 100644 js/ui.magnifier.js create mode 100644 js/ui.mouse.js create mode 100644 js/ui.resizable.js create mode 100644 js/ui.selectable.js create mode 100644 js/ui.shadow.js create mode 100644 js/ui.slider.js create mode 100644 js/ui.sortable.js create mode 100644 js/ui.tablesorter.js create mode 100644 js/ui.tabs.js create mode 100644 js/wizard.utils.js create mode 100644 js/wizardhelper.js create mode 100644 pages/ITopConsultant.php create mode 100644 pages/UI.php create mode 100644 pages/UniversalSearch.php create mode 100644 pages/advanced_search.php create mode 100644 pages/ajax.php create mode 100644 pages/ajax.render.php create mode 100644 pages/audit.php create mode 100644 pages/csvimport.php create mode 100644 pages/data_generator.php create mode 100644 pages/db_importer.php create mode 100644 pages/graphviz.php create mode 100644 pages/incident.php create mode 100644 pages/index.php create mode 100644 pages/opensearch.xml create mode 100644 pages/opensearch.xml.php create mode 100644 pages/php-ofc-library/JSON.php create mode 100644 pages/php-ofc-library/README.txt create mode 100644 pages/php-ofc-library/json_format.php create mode 100644 pages/php-ofc-library/ofc_area_base.php create mode 100644 pages/php-ofc-library/ofc_area_hollow.php create mode 100644 pages/php-ofc-library/ofc_area_line.php create mode 100644 pages/php-ofc-library/ofc_bar.php create mode 100644 pages/php-ofc-library/ofc_bar_3d.php create mode 100644 pages/php-ofc-library/ofc_bar_base.php create mode 100644 pages/php-ofc-library/ofc_bar_filled.php create mode 100644 pages/php-ofc-library/ofc_bar_glass.php create mode 100644 pages/php-ofc-library/ofc_bar_sketch.php create mode 100644 pages/php-ofc-library/ofc_bar_stack.php create mode 100644 pages/php-ofc-library/ofc_hbar.php create mode 100644 pages/php-ofc-library/ofc_line.php create mode 100644 pages/php-ofc-library/ofc_line_base.php create mode 100644 pages/php-ofc-library/ofc_line_dot.php create mode 100644 pages/php-ofc-library/ofc_line_hollow.php create mode 100644 pages/php-ofc-library/ofc_line_style.php create mode 100644 pages/php-ofc-library/ofc_pie.php create mode 100644 pages/php-ofc-library/ofc_radar_axis.php create mode 100644 pages/php-ofc-library/ofc_radar_axis_labels.php create mode 100644 pages/php-ofc-library/ofc_radar_spoke_labels.php create mode 100644 pages/php-ofc-library/ofc_scatter.php create mode 100644 pages/php-ofc-library/ofc_scatter_line.php create mode 100644 pages/php-ofc-library/ofc_shape.php create mode 100644 pages/php-ofc-library/ofc_title.php create mode 100644 pages/php-ofc-library/ofc_tooltip.php create mode 100644 pages/php-ofc-library/ofc_upload_image.php create mode 100644 pages/php-ofc-library/ofc_x_axis.php create mode 100644 pages/php-ofc-library/ofc_x_axis_label.php create mode 100644 pages/php-ofc-library/ofc_x_axis_labels.php create mode 100644 pages/php-ofc-library/ofc_x_legend.php create mode 100644 pages/php-ofc-library/ofc_y_axis.php create mode 100644 pages/php-ofc-library/ofc_y_axis_base.php create mode 100644 pages/php-ofc-library/ofc_y_axis_right.php create mode 100644 pages/php-ofc-library/ofc_y_legend.php create mode 100644 pages/php-ofc-library/open-flash-chart-object.php create mode 100644 pages/php-ofc-library/open-flash-chart.php create mode 100644 pages/schema.php create mode 100644 pages/sibusql.php create mode 100644 pages/test.php create mode 100644 pages/testlist.inc.php create mode 100644 setup/ajax.dataloader.php create mode 100644 setup/data/01.organizations.xml create mode 100644 setup/data/02.locations.xml create mode 100644 setup/data/03.persons.xml create mode 100644 setup/data/04.teams.xml create mode 100644 setup/data/05.pcs.xml create mode 100644 setup/data/06.servers.xml create mode 100644 setup/data/07.applications.xml create mode 100644 setup/data/08.nw-devices.xml create mode 100644 setup/data/09.links_contacts.xml create mode 100644 setup/data/10.workgroups.xml create mode 100644 setup/data/11.incidents.xml create mode 100644 setup/data/12.relatedtickets.xml create mode 100644 setup/data/13.infratickets.xml create mode 100644 setup/data/14.contacttickets.xml create mode 100644 setup/data/15.changetickets.xml create mode 100644 setup/data/16.infrachangetickets.xml create mode 100644 setup/data/17.contactchangetickets.xml create mode 100644 setup/data/18.contracts.xml create mode 100644 setup/data/19.infracontracts.xml create mode 100644 setup/data/20.contactcontracts.xml create mode 100644 setup/data/21.auditcategories.xml create mode 100644 setup/data/22.auditrules.xml create mode 100644 setup/data/export.cmd create mode 100644 setup/export_menus.cmd create mode 100644 setup/index.php create mode 100644 setup/jquery.progression.js create mode 100644 setup/menus.xml create mode 100644 setup/orange-progress.gif create mode 100644 setup/setup.js create mode 100644 setup/setuppage.class.inc.php create mode 100644 setup/xmldataloader.class.inc.php create mode 100644 toolkit.php create mode 100644 webservices/export.php create mode 100644 webservices/import.php diff --git a/addons/userrights/userrightsmatrix.class.inc.php b/addons/userrights/userrightsmatrix.class.inc.php new file mode 100644 index 000000000..de90baa47 --- /dev/null +++ b/addons/userrights/userrightsmatrix.class.inc.php @@ -0,0 +1,385 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ + + +class UserRightsMatrixUsers extends DBObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "addon/userrights", + "name" => "user", + "description" => "users and credentials", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "login", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_ur_matrixusers", + "db_key_field" => "id", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + //MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeInteger("userid", array("label"=>"User id", "description"=>"User identifier (depends on the business model)", "allowed_values"=>null, "sql"=>"userid", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("login", array("label"=>"login", "description"=>"user identification string", "allowed_values"=>null, "sql"=>"login", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("password", array("label"=>"password", "description"=>"user authentication string", "allowed_values"=>null, "sql"=>"pwd", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + + //MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("userid"); + MetaModel::Init_AddFilterFromAttribute("login"); + } +} + +class UserRightsMatrixClassGrant extends DBObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "addon/userrights", + "name" => "class_permission", + "description" => "permissions on classes", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_ur_matrixclasses", + "db_key_field" => "id", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + //MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", array("targetclass"=>"UserRightsMatrixUsers", "jointype"=> "", "label"=>"user", "description"=>"user account", "allowed_values"=>null, "sql"=>"userid", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("login", array("label"=>"Login", "description"=>"Login", "allowed_values"=>null, "extkey_attcode"=> 'userid', "target_attcode"=>"login"))); + MetaModel::Init_AddAttribute(new AttributeString("class", array("label"=>"class", "description"=>"class name", "allowed_values"=>null, "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_AddAttribute(new AttributeString("action", array("label"=>"action", "description"=>"operations to perform on the given class", "allowed_values"=>null, "sql"=>"action", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("label"=>"permission", "description"=>"allowed or not allowed?", "allowed_values"=>new ValueSetEnum('yes,no'), "sql"=>"permission", "default_value"=>"yes", "is_null_allowed"=>false, "depends_on"=>array()))); + + //MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("userid"); + MetaModel::Init_AddFilterFromAttribute("login"); + MetaModel::Init_AddFilterFromAttribute("class"); + MetaModel::Init_AddFilterFromAttribute("action"); + } +} + +class UserRightsMatrixClassStimulusGrant extends DBObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "addon/userrights", + "name" => "stimulus_permission", + "description" => "permissions on stimilus in the life cycle of the object", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_ur_matrixclassesstimulus", + "db_key_field" => "id", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + //MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", array("targetclass"=>"UserRightsMatrixUsers", "jointype"=> "", "label"=>"user", "description"=>"user account", "allowed_values"=>null, "sql"=>"userid", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("login", array("label"=>"Login", "description"=>"Login", "allowed_values"=>null, "extkey_attcode"=> 'userid', "target_attcode"=>"login"))); + MetaModel::Init_AddAttribute(new AttributeString("class", array("label"=>"class", "description"=>"class name", "allowed_values"=>null, "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_AddAttribute(new AttributeString("stimulus", array("label"=>"action", "description"=>"operations to perform on the given class", "allowed_values"=>null, "sql"=>"action", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("label"=>"permission", "description"=>"allowed or not allowed?", "allowed_values"=>new ValueSetEnum('yes,no'), "sql"=>"permission", "default_value"=>"yes", "is_null_allowed"=>false, "depends_on"=>array()))); + + //MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("userid"); + MetaModel::Init_AddFilterFromAttribute("login"); + MetaModel::Init_AddFilterFromAttribute("class"); + MetaModel::Init_AddFilterFromAttribute("stimulus"); + } +} + +class UserRightsMatrixAttributeGrant extends DBObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "addon/userrights", + "name" => "attribute_permission", + "description" => "permissions at the attributes level", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_ur_matrixattributes", + "db_key_field" => "id", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + //MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", array("targetclass"=>"UserRightsMatrixUsers", "jointype"=> "", "label"=>"user", "description"=>"user account", "allowed_values"=>null, "sql"=>"userid", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("login", array("label"=>"Login", "description"=>"Login", "allowed_values"=>null, "extkey_attcode"=> 'userid', "target_attcode"=>"login"))); + MetaModel::Init_AddAttribute(new AttributeString("class", array("label"=>"class", "description"=>"class name", "allowed_values"=>null, "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("attcode", array("label"=>"attribute", "description"=>"attribute code", "allowed_values"=>null, "sql"=>"attcode", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_AddAttribute(new AttributeString("action", array("label"=>"action", "description"=>"operations to perform on the given class", "allowed_values"=>null, "sql"=>"action", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("label"=>"permission", "description"=>"allowed or not allowed?", "allowed_values"=>new ValueSetEnum('yes,no'), "sql"=>"permission", "default_value"=>"yes", "is_null_allowed"=>false, "depends_on"=>array()))); + + //MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("userid"); + MetaModel::Init_AddFilterFromAttribute("login"); + MetaModel::Init_AddFilterFromAttribute("class"); + MetaModel::Init_AddFilterFromAttribute("attcode"); + MetaModel::Init_AddFilterFromAttribute("action"); + } +} + + + + +class UserRightsMatrix extends UserRightsAddOnAPI +{ + static public $m_aActionCodes = array( + UR_ACTION_READ => 'read', + UR_ACTION_MODIFY => 'modify', + UR_ACTION_DELETE => 'delete', + UR_ACTION_BULK_READ => 'bulk read', + UR_ACTION_BULK_MODIFY => 'bulk modify', + UR_ACTION_BULK_DELETE => 'bulk delete', + ); + + // Installation: create the very first user + public function CreateAdministrator($sAdminUser, $sAdminPwd) + { + // Maybe we should check that no other user with userid == 0 exists + $oUser = new UserRightsMatrixUsers(); + $oUser->Set('login', $sAdminUser); + $oUser->Set('password', $sAdminPwd); + $oUser->Set('userid', 1); // one is for root ! + $iUserId = $oUser->DBInsertNoReload(); + $this->SetupUser($iUserId, true); + return true; + } + + public function Setup() + { + // Users must be added manually + // This procedure will then update the matrix when a new user is found or a new class/attribute appears + $oUserSet = new DBObjectSet(DBObjectSearch::FromSibuSQL("UserRightsMatrixUsers")); + while ($oUser = $oUserSet->Fetch()) + { + $this->SetupUser($oUser->GetKey()); + } + return true; + } + + protected function SetupUser($iUserId, $bNewUser = false) + { + foreach(array('bizmodel', 'application', 'gui', 'core/cmdb') as $sCategory) + { + foreach (MetaModel::GetClasses($sCategory) as $sClass) + { + foreach (self::$m_aActionCodes as $iActionCode => $sAction) + { + if ($bNewUser) + { + $bAddCell = true; + } + else + { + $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixClassGrant WHERE class = '$sClass' AND action = '$sAction' AND userid = $iUserId")); + $bAddCell = ($oSet->Count() < 1); + } + if ($bAddCell) + { + // Create a new entry + $oMyClassGrant = MetaModel::NewObject("UserRightsMatrixClassGrant"); + $oMyClassGrant->Set("userid", $iUserId); + $oMyClassGrant->Set("class", $sClass); + $oMyClassGrant->Set("action", $sAction); + $oMyClassGrant->Set("permission", "yes"); + $iId = $oMyClassGrant->DBInsertNoReload(); + } + } + foreach (MetaModel::EnumStimuli($sClass) as $sStimulusCode => $oStimulus) + { + if ($bNewUser) + { + $bAddCell = true; + } + else + { + $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixClassStimulusGrant WHERE class = '$sClass' AND stimulus = '$sStimulusCode' AND userid = $iUserId")); + $bAddCell = ($oSet->Count() < 1); + } + if ($bAddCell) + { + // Create a new entry + $oMyClassGrant = MetaModel::NewObject("UserRightsMatrixClassStimulusGrant"); + $oMyClassGrant->Set("userid", $iUserId); + $oMyClassGrant->Set("class", $sClass); + $oMyClassGrant->Set("stimulus", $sStimulusCode); + $oMyClassGrant->Set("permission", "yes"); + $iId = $oMyClassGrant->DBInsertNoReload(); + } + } + foreach (MetaModel::GetAttributesList($sClass) as $sAttCode) + { + if ($bNewUser) + { + $bAddCell = true; + } + else + { + $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixAttributeGrant WHERE class = '$sClass' AND attcode = '$sAttCode' AND userid = $iUserId")); + $bAddCell = ($oSet->Count() < 1); + } + if ($bAddCell) + { + foreach (array('read', 'modify') as $sAction) + { + // Create a new entry + $oMyAttGrant = MetaModel::NewObject("UserRightsMatrixAttributeGrant"); + $oMyAttGrant->Set("userid", $iUserId); + $oMyAttGrant->Set("class", $sClass); + $oMyAttGrant->Set("attcode", $sAttCode); + $oMyAttGrant->Set("action", $sAction); + $oMyAttGrant->Set("permission", "yes"); + $iId = $oMyAttGrant->DBInsertNoReload(); + } + } + } + } + } + } + + + public function Init() + { + // Could be loaded in a shared memory (?) + return true; + } + + public function CheckCredentials($sUserName, $sPassword) + { + $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixUsers WHERE login = '$sUserName'")); + if ($oSet->Count() < 1) + { + // todo: throw an exception? + return false; + } + + $oLogin = $oSet->Fetch(); + if ($oLogin->Get('password') == $sPassword) + { + return true; + } + // todo: throw an exception? + return false; + } + + public function GetFilter($sUserName, $sClass) + { + $oNullFilter = new DBObjectSearch($sClass); + return $oNullFilter; + } + + public function IsActionAllowed($sUserName, $sClass, $iActionCode, dbObjectSet $aInstances) + { + if (!array_key_exists($iActionCode, self::$m_aActionCodes)) + { + return UR_ALLOWED_NO; + } + $sAction = self::$m_aActionCodes[$iActionCode]; + + $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixClassGrant WHERE class = '$sClass' AND action = '$sAction' AND login = '$sUserName'")); + if ($oSet->Count() < 1) + { + return UR_ALLOWED_NO; + } + + $oGrantRecord = $oSet->Fetch(); + switch ($oGrantRecord->Get('permission')) + { + case 'yes': + $iRetCode = UR_ALLOWED_YES; + break; + case 'no': + default: + $iRetCode = UR_ALLOWED_NO; + break; + } + return $iRetCode; + } + + public function IsActionAllowedOnAttribute($sUserName, $sClass, $sAttCode, $iActionCode, dbObjectSet $aInstances) + { + if (!array_key_exists($iActionCode, self::$m_aActionCodes)) + { + return UR_ALLOWED_NO; + } + $sAction = self::$m_aActionCodes[$iActionCode]; + + $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixAttributeGrant WHERE UserRightsMatrixAttributeGrant.class = '$sClass' AND UserRightsMatrixAttributeGrant.attcode = '$sAttCode' AND UserRightsMatrixAttributeGrant.action = '$sAction' AND UserRightsMatrixAttributeGrant.login = '$sUserName'")); + if ($oSet->Count() < 1) + { + return UR_ALLOWED_NO; + } + + $oGrantRecord = $oSet->Fetch(); + switch ($oGrantRecord->Get('permission')) + { + case 'yes': + $iRetCode = UR_ALLOWED_YES; + break; + case 'no': + default: + $iRetCode = UR_ALLOWED_NO; + break; + } + return $iRetCode; + } + + public function IsStimulusAllowed($sUserName, $sClass, $sStimulusCode, dbObjectSet $aInstances) + { + $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixClassStimulusGrant WHERE class = '$sClass' AND stimulus = '$sStimulusCode' AND login = '$sUserName'")); + if ($oSet->Count() < 1) + { + return UR_ALLOWED_NO; + } + + $oGrantRecord = $oSet->Fetch(); + switch ($oGrantRecord->Get('permission')) + { + case 'yes': + $iRetCode = UR_ALLOWED_YES; + break; + case 'no': + default: + $iRetCode = UR_ALLOWED_NO; + break; + } + return $iRetCode; + } +} + +UserRights::SelectModule('UserRightsMatrix'); + +?> diff --git a/addons/userrights/userrightsnull.class.inc.php b/addons/userrights/userrightsnull.class.inc.php new file mode 100644 index 000000000..c5480f7ea --- /dev/null +++ b/addons/userrights/userrightsnull.class.inc.php @@ -0,0 +1,64 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ + + +class UserRightsNull extends UserRightsAddOnAPI +{ + // Installation: create the very first user + public function CreateAdministrator($sAdminUser, $sAdminPwd) + { + return true; + } + + public function Setup() + { + return true; + } + + public function Init() + { + return true; + } + + public function CheckCredentials($sUserName, $sPassword) + { + return true; + } + + public function GetFilter($sUserName, $sClass) + { + $oNullFilter = new DBObjectSearch($sClass); + return $oNullFilter; + } + + public function IsActionAllowed($sUserName, $sClass, $iActionCode, dbObjectSet $aInstances) + { + return UR_ALLOWED_YES; + } + + public function IsStimulusAllowed($sUserName, $sClass, $sStimulusCode, dbObjectSet $aInstances) + { + return UR_ALLOWED_YES; + } + + public function IsActionAllowedOnAttribute($sUserName, $sClass, $sAttCode, $iActionCode, dbObjectSet $aInstances) + { + return UR_ALLOWED_YES; + } +} + +UserRights::SelectModule('UserRightsNull'); + +?> diff --git a/application/ajaxwebpage.class.inc.php b/application/ajaxwebpage.class.inc.php new file mode 100644 index 000000000..3d061f69a --- /dev/null +++ b/application/ajaxwebpage.class.inc.php @@ -0,0 +1,145 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + */ + +class ajax_page extends web_page +{ + /** + * Jquery style ready script + * @var Hash + */ + protected $m_sReadyScript; + + /** + * constructor for the web page + * @param string $s_title Not used + */ + function __construct($s_title) + { + parent::__construct($s_title); + $this->m_sReadyScript = ""; + } + + + /** + * Echoes the content of the whole page + * @return void + */ + public function output() + { + foreach($this->a_headers as $s_header) + { + header($s_header); + } + $s_captured_output = ob_get_contents(); + ob_end_clean(); + echo trim($this->s_content); + if (!empty($this->m_sReadyScript)) + { + echo "\n"; + } + if (trim($s_captured_output) != "") + { + echo $s_captured_output; + } + } + + /** + * Adds a paragraph with a smaller font into the page + * NOT implemented (i.e does nothing) + * @param string $sText Content of the (small) paragraph + * @return void + */ + public function small_p($sText) + { + } + + /** + * Adds a tabular content to the web page + * @param Hash $aConfig Configuration of the table: hash array of 'column_id' => 'Column Label' + * @param Hash $aData Hash array. Data to display in the table: each row is made of 'column_id' => Data. A column 'pkey' is expected for each row + * @param Hash $aParams Hash array. Extra parameters for the table. Entry 'class' holds the class of the objects listed in the table + * @return void + */ + public function table($aConfig, $aData, $aParams = array()) + { + // WARNING WARNING WARNING + // This whole function is actually a copy paste from iTopWebPage::table + $oAppContext = new ApplicationContext(); + + static $iNbTables = 0; + $iNbTables++; + $sHtml = ""; + $sHtml .= "\n"; + $sHtml .= "\n"; + $sHtml .= "\n"; + foreach($aConfig as $sName=>$aDef) + { + $sHtml .= "\n"; + } + $sHtml .= "\n"; + $sHtml .= "\n"; + $sHtml .= "\n"; + foreach($aData as $aRow) + { + if (false) //(isset($aParams['preview']) && $aParams['preview']) + { + $sHtml .= "\n"; + } + else if (isset($aRow['key'])) + { + $sHtml .= "\n"; + } + else + { + $sHtml .= "\n"; + } + foreach($aConfig as $sName=>$aVoid) + { + if ($sName != 'key') + { + $sValue = empty($aRow[$sName]) ? ' ' : $aRow[$sName]; + $sHtml .= "\n"; + } + else + { + $sUIPage = cmdbAbstractObject::ComputeUIPage($aParams['class']); + $sHtml .= "\n"; + } + } + $sHtml .= "\n"; + } + $sHtml .= "\n"; + $sHtml .= "
".$aDef['label']."
$sValueGetForLink()."\">
\n"; + if (isset($aParams['preview']) && $aParams['preview']) + { + $sHtml .= "
Preview Pane
"; + } + $this->add($sHtml); + } + /** + * Adds a script to be executed when the DOM is ready (typical JQuery use) + * NOT implemented in this version of the class. + * @return void + */ + public function add_ready_script($sScript) + { + // Does nothing in ajax rendered content.. for now... + // Maybe we should add this as a simple '); // TO DO: add support for $aExtraParams in asynchronous/Ajax mode + } + } + + public function GetDisplay(web_page $oPage, $sId, $aExtraParams = array()) + { + $sHtml = ''; + $aExtraParams = array_merge($aExtraParams, $this->m_aParams); + if (!$this->m_bAsynchronous) + { + // render now + $sHtml .= "
\n"; + $sHtml .= $this->GetRenderContent($oPage, $aExtraParams); + $sHtml .= "
\n"; + } + else + { + // render it as an Ajax (asynchronous) call + $sFilter = $this->m_oFilter->serialize(); + $sHtml .= "
\n"; + $sHtml .= $oPage->GetP(" Loading..."); + $sHtml .= "
\n"; + $sHtml .= ' + '; // TO DO: add support for $aExtraParams in asynchronous/Ajax mode + } + return $sHtml; + } + + public function RenderContent(web_page $oPage, $aExtraParams = array()) + { + $oPage->add($this->GetRenderContent($oPage, $aExtraParams)); + } + + public function GetRenderContent(web_page $oPage, $aExtraParams = array()) + { + $sHtml = ''; + // Add the extra params into the filter if they make sense for such a filter + $bDoSearch = utils::ReadParam('dosearch', false); + if ($this->m_oSet == null) + { + $aFilterCodes = array_keys(MetaModel::GetClassFilterDefs($this->m_oFilter->GetClass())); + foreach($aFilterCodes as $sFilterCode) + { + $sExternalFilterValue = utils::ReadParam($sFilterCode, ''); + if (isset($aExtraParams[$sFilterCode])) + { + $this->m_oFilter->AddCondition($sFilterCode, $aExtraParams[$sFilterCode]); // Use the default 'loose' operator + } + else if ($bDoSearch && $sExternalFilterValue != "") + { + $this->m_oFilter->AddCondition($sFilterCode, $sExternalFilterValue); // Use the default 'loose' operator + } + } + $this->m_oSet = new CMDBObjectSet($this->m_oFilter); + } + switch($this->m_sStyle) + { + case 'count': + if (isset($aExtraParams['group_by'])) + { + $sGroupByField = $aExtraParams['group_by']; + $aGroupBy = array(); + while($oObj = $this->m_oSet->Fetch()) + { + $sValue = $oObj->Get($sGroupByField); + $aGroupBy[$sValue] = isset($aGroupBy[$sValue]) ? $aGroupBy[$sValue]+1 : 1; + } + $sFilter = urlencode($this->m_oFilter->serialize()); + $aData = array(); + foreach($aGroupBy as $sValue => $iCount) + { + $aData[] = array ( 'group' => $sValue, + 'value' => "$iCount"); // TO DO: add the context information + } + $sHtml .= $oPage->GetTable(array('group' => array('label' => MetaModel::GetLabel($this->m_oFilter->GetClass(), $sGroupByField), 'description' => ''), 'value' => array('label'=>'Count', 'description' => 'Number of elements')), $aData); + } + else + { + // Simply count the number of elements in the set + $iCount = $oSet->Count(); + $sHtml .= $oPage->GetP("$iCount objects matching the criteria."); + } + + break; + + case 'list': + $bDashboardMode = isset($aExtraParams['dashboard']) ? ($aExtraParams['dashboard'] == 'true') : false; + if ( ($this->m_oSet->Count()> 0) && (UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_READ, $this->m_oSet) == UR_ALLOWED_YES) ) + { + if (!$bDashboardMode) + { + $sHtml .= $oPage->GetP($this->m_oSet->Count()." object(s)."); + } + $sLinkage = isset($aExtraParams['linkage']) ? $aExtraParams['linkage'] : ''; + $sHtml .= cmdbAbstractObject::GetDisplaySet($oPage, $this->m_oSet, $sLinkage, !$bDashboardMode /* bDisplayMenu */); + } + else + { + $sHtml .= $oPage->GetP("No object to display."); + $sClass = $this->m_oFilter->GetClass(); + if (!$bDashboardMode) + { + if (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY, $this->m_oSet) == UR_ALLOWED_YES) + { + $sHtml .= $oPage->GetP("Click here to create a new ".Metamodel::GetName($sClass)."\n"); + } + } + } + break; + + case 'details': + if (UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_READ, $this->m_oSet) == UR_ALLOWED_YES) + { + while($oObj = $this->m_oSet->Fetch()) + { + $sHtml .= $oObj->GetDetails($oPage); + } + } + break; + + case 'bare_details': + if (UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_READ, $this->m_oSet) == UR_ALLOWED_YES) + { + while($oObj = $this->m_oSet->Fetch()) + { + $sHtml .= $oObj->GetBareDetails($oPage); + } + } + break; + + case 'csv': + if (UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_READ, $this->m_oSet) == UR_ALLOWED_YES) + { + $sHtml .= "\n"; + } + break; + + case 'modify': + if (UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_MODIFY, $this->m_oSet) == UR_ALLOWED_YES) + { + while($oObj = $this->m_oSet->Fetch()) + { + $sHtml .= $oObj->GetModifyForm($oPage); + } + } + break; + + case 'search': + $iSearchSectionId = 1; + $sStyle = (isset($aExtraParams['open']) && ($aExtraParams['open'] == 'true')) ? 'SearchDrawer' : 'SearchDrawer DrawerClosed'; + $sHtml .= "
\n"; + $sHtml .= "

Search form for ".Metamodel::GetName($this->m_oSet->GetClass())."

\n"; + $oPage->add_ready_script("\$(\"#LnkSearch_$iSearchSectionId\").click(function() {\$(\"#Search_$iSearchSectionId\").slideToggle('normal'); $(\"#LnkSearch_$iSearchSectionId\").toggleClass('open');});"); + $sHtml .= cmdbAbstractObject::GetSearchForm($oPage, $this->m_oSet, $aExtraParams); + $sHtml .= "
\n"; + $sHtml .= "
\n"; + $sHtml .= "
Search
\n"; + break; + + case 'pie_chart': + $sGroupBy = isset($aExtraParams['group_by']) ? $aExtraParams['group_by'] : ''; + $sFilter = $this->m_oFilter->ToOQL(); + $sHtml .= " + + + + + + + + + "; + break; + + case 'pie_chart_ajax': + if (isset($aExtraParams['group_by'])) + { + $sGroupByField = $aExtraParams['group_by']; + $aGroupBy = array(); + while($oObj = $this->m_oSet->Fetch()) + { + $sValue = $oObj->Get($sGroupByField); + $aGroupBy[$sValue] = isset($aGroupBy[$sValue]) ? $aGroupBy[$sValue]+1 : 1; + } + $sFilter = urlencode($this->m_oFilter->serialize()); + $aData = array(); + $sHtml .= "\n"; + $sHtml .= "3d pie\n"; + $sHtml .= "\n"; + $sHtml .= "\n"; + $sHtml .= "\n"; + foreach($aGroupBy as $sValue => $void) + { + $sHtml .= "$sValue\n"; + } + $sHtml .= "\n"; + $sHtml .= "\n"; + $sHtml .= "\n"; + foreach($aGroupBy as $void => $iCount) + { + $sHtml .= "$iCount\n"; + } + $sHtml .= "\n"; + $sHtml .= "\n"; + $sHtml .= " + + + + ||||||||||||||||||||||||||||||||||||||||||||||| + + + + + + ddaa41 + 88dd11 + 4e62dd + ff8811 + 4d4d4d + 5a4b6e + 1188ff + + "; + $sHtml .= "\n"; + } + else + { + // Simply count the number of elements in the set + $iCount = $oSet->Count(); + $sHtml .= "\n\n"; + } + break; + + case 'open_flash_chart': + static $iChartCounter = 0; + $sChartType = isset($aExtraParams['chart_type']) ? $aExtraParams['chart_type'] : 'pie'; + $sTitle = isset($aExtraParams['chart_title']) ? $aExtraParams['chart_title'] : ''; + $sGroupBy = isset($aExtraParams['group_by']) ? $aExtraParams['group_by'] : ''; + $sFilter = $this->m_oFilter->ToOQL(); + $sHtml .= "\n"; + $sHtml .= "
Here goes the chart
\n"; + $iChartCounter++; + break; + + case 'open_flash_chart_ajax': + include './php-ofc-library/open-flash-chart.php'; + $sChartType = isset($aExtraParams['chart_type']) ? $aExtraParams['chart_type'] : 'pie'; + + $oChart = new open_flash_chart(); + switch($sChartType) + { + case 'bars': + $oChartElement = new bar_glass(); + + if (isset($aExtraParams['group_by'])) + { + $sGroupByField = $aExtraParams['group_by']; + $aGroupBy = array(); + while($oObj = $this->m_oSet->Fetch()) + { + $sValue = $oObj->Get($sGroupByField); + $aGroupBy[$sValue] = isset($aGroupBy[$sValue]) ? $aGroupBy[$sValue]+1 : 1; + } + $sFilter = urlencode($this->m_oFilter->serialize()); + $aData = array(); + $aLabels = array(); + foreach($aGroupBy as $sValue => $iValue) + { + $aData[] = $iValue; + $aLabels[] = $sValue; + } + $maxValue = max($aData); + $oYAxis = new y_axis(); + $aMagicValues = array(1,2,5,10); + $iMultiplier = 1; + $index = 0; + $iTop = $aMagicValues[$index % count($aMagicValues)]*$iMultiplier; + while($maxValue > $iTop) + { + $index++; + $iTop = $aMagicValues[$index % count($aMagicValues)]*$iMultiplier; + if (($index % count($aMagicValues)) == 0) + { + $iMultiplier = $iMultiplier * 10; + } + } + //echo "oYAxis->set_range(0, $iTop, $iMultiplier);\n"; + $oYAxis->set_range(0, $iTop, $iMultiplier); + $oChart->set_y_axis( $oYAxis ); + + $oChartElement->set_values( $aData ); + $oXAxis = new x_axis(); + $oXLabels = new x_axis_labels(); + // set them vertical + $oXLabels->set_vertical(); + // set the label text + $oXLabels->set_labels($aLabels); + // Add the X Axis Labels to the X Axis + $oXAxis->set_labels( $oXLabels ); + $oChart->set_x_axis( $oXAxis ); + } + break; + + case 'pie': + default: + $oChartElement = new pie(); + $oChartElement->set_start_angle( 35 ); + $oChartElement->set_animate( true ); + $oChartElement->set_tooltip( '#label# - #val# (#percent#)' ); + if (isset($aExtraParams['group_by'])) + { + $sGroupByField = $aExtraParams['group_by']; + $aGroupBy = array(); + while($oObj = $this->m_oSet->Fetch()) + { + $sValue = $oObj->Get($sGroupByField); + $aGroupBy[$sValue] = isset($aGroupBy[$sValue]) ? $aGroupBy[$sValue]+1 : 1; + } + $sFilter = urlencode($this->m_oFilter->serialize()); + $aData = array(); + foreach($aGroupBy as $sValue => $iValue) + { + $aData[] = new pie_value($iValue, $sValue); + } + + + $oChartElement->set_values( $aData ); + $oChart->x_axis = null; + } + } + if (isset($aExtraParams['chart_title'])) //@@ BUG: not passed via ajax !!! + { + $oTitle = new title( $aExtraParams['chart_title'] ); + $oChart->set_title( $oTitle ); + } + $oChart->set_bg_colour('#FFFFFF'); + $oChart->add_element( $oChartElement ); + + $sHtml = $oChart->toPrettyString(); + break; + + default: + // Unsupported style, do nothing. + $sHtml .= "Error: unsupported style of block: ".$this->m_sStyle; + } + return $sHtml; + } +} + +/** + * Helper class to manage 'blocks' of HTML pieces that are parts of a page and contain some list of cmdb objects + * + * Each block is actually rendered as a
tag that can be rendered synchronously + * or as a piece of Javascript/JQuery/Ajax that will get its content from another page (ajax.render.php). + * The list of cmdbObjects to be displayed into the block is defined by a filter + * Right now the type of display is either: list, count or details + * - list produces a table listing the objects + * - count produces a paragraphs with a sentence saying 'cont' objects found + * - details display (as table) the details of each object found (best if only one) + */ +class HistoryBlock extends DisplayBlock +{ + public function GetRenderContent(web_page $oPage, $aExtraParams = array()) + { + $sHtml = ''; + // Add the extra params into the filter if they make sense for such a filter + $aFilterCodes = array_keys(MetaModel::GetClassFilterDefs($this->m_oFilter->GetClass())); + foreach($aFilterCodes as $sFilterCode) + { + if (isset($aExtraParams[$sFilterCode])) + { + $this->m_oFilter->AddCondition($sFilterCode, $aExtraParams[$sFilterCode]); // Use the default 'loose' operator + } + } + $oSet = new CMDBObjectSet($this->m_oFilter, array('date'=>false)); + $sHtml .= "\n"; + switch($this->m_sStyle) + { + case 'toggle': + $oLatestChangeOp = $oSet->Fetch(); + if (is_object($oLatestChangeOp)) + { + global $oContext; // User Context.. should be statis instead of global... + // There is one change in the list... only when the object has been created ! + $sDate = $oLatestChangeOp->GetAsHTML('date'); + $oChange = $oContext->GetObject('CMDBChange', $oLatestChangeOp->Get('change')); + $sUserInfo = $oChange->GetAsHTML('userinfo'); + $oSet->Load(); // Reset the pointer to the beginning of the set: there should be a better way to do this... + $sHtml .= $oPage->GetStartCollapsibleSection("Last modified on $sDate by $sUserInfo."); + $sHtml .= cmdbAbstractObject::GetDisplaySet($oPage, $oSet); + $sHtml .= $oPage->GetEndCollapsibleSection(); + } + break; + + default: + $sHtml .= parent::GetRenderContent($oPage, $aExtraParams); + } + return $sHtml; + } +} + +class MenuBlock extends DisplayBlock +{ + public function GetRenderContent(web_page $oPage, $aExtraParams = array()) + { + $sHtml = ''; + $oAppContext = new ApplicationContext(); + $sContext = $oAppContext->GetForLink(); + $sClass = $this->m_oFilter->GetClass(); + $oSet = new CMDBObjectSet($this->m_oFilter); + $sFilter = $this->m_oFilter->serialize(); + $aActions = array(); + $sUIPage = cmdbAbstractObject::ComputeUIPage($sClass); + switch($oSet->Count()) + { + case 0: + // No object in the set, the only possible action is "new" + $bIsModifyAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY, $oSet); + if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'New', 'url' => "../page/$sUIPage?operation=new&class=$sClass&$sContext"); } + break; + + case 1: + $oObj = $oSet->Fetch(); + $id = $oObj->GetKey(); + $bIsModifyAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY, $oSet); + $bIsDeleteAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_DELETE, $oSet); + $bIsBulkModifyAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_MODIFY, $oSet); + $bIsBulkDeleteAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_DELETE, $oSet); + // Just one object in the set, possible actions are "new / clone / modify and delete" + if (isset($aExtraParams['linkage'])) + { + if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'New...', 'url' => "#"); } + if ($bIsBulkModifyAllowed) { $aActions[] = array ('label' => 'Modify All...', 'url' => "#"); } + if ($bIsBulkDeleteAllowed) { $aActions[] = array ('label' => 'Remove All', 'url' => "#"); } + if ($bIsModifyAllowed | $bIsDeleteAllowed) { $aActions[] = array ('label' => 'Manage Links...', 'url' => "#"); } + } + else + { + $aActions[] = array ('label' => 'eMail', 'url' => "mailto:?subject=".$oSet->GetFilter()->__DescribeHTML()."&body=".urlencode("http://localhost:81/pages/UI.php?operation=search&filter=$sFilter&$sContext")); + $aActions[] = array ('label' => 'CSV Export', 'url' => "../pages/$sUIPage?operation=search&filter=$sFilter&format=csv&$sContext"); + $aActions[] = array ('label' => 'Bookmark...', 'url' => "../pages/ajax.render.php?operation=create&class=$sClass&filter=$sFilter", 'class' => 'jqmTrigger'); + if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'New...', 'url' => "../pages/$sUIPage?operation=new&class=$sClass&$sContext"); } + if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'Clone...', 'url' => "../pages/$sUIPage?operation=clone&class=$sClass&id=$id&$sContext"); } + if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'Modify...', 'url' => "../pages/$sUIPage?operation=modify&class=$sClass&id=$id&$sContext"); } + if ($bIsDeleteAllowed) { $aActions[] = array ('label' => 'Delete', 'url' => "../pages/$sUIPage?operation=delete&class=$sClass&id=$id&$sContext"); } + } + $aTransitions = $oObj->EnumTransitions(); + $aStimuli = Metamodel::EnumStimuli($sClass); + foreach($aTransitions as $sStimulusCode => $aTransitionDef) + { + $iActionAllowed = UserRights::IsStimulusAllowed($sClass, $sStimulusCode, $oSet); + switch($iActionAllowed) + { + case UR_ALLOWED_YES: + $aActions[] = array('label' => $aStimuli[$sStimulusCode]->Get('label'), 'url' => "../pages/UI.php?operation=stimulus&stimulus=$sStimulusCode&class=$sClass&id=$id&$sContext"); + break; + + case UR_ALLOWED_DEPENDS: + $aActions[] = array('label' => $aStimuli[$sStimulusCode]->Get('label').' (*)', 'url' => "../pages/UI.php?operation=stimulus&stimulus=$sStimulusCode&class=$sClass&id=$id&$sContext"); + break; + + default: + // Do nothing + } + } + //print_r($aTransitions); + break; + + default: + // Check rights + // New / Modify + $bIsModifyAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY, $oSet); + $bIsBulkModifyAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_MODIFY, $oSet); + $bIsBulkDeleteAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_DELETE, $oSet); + if (isset($aExtraParams['linkage'])) + { + $bIsDeleteAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_DELETE, $oSet); + if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'New...', 'url' => "#"); } + if ($bIsBulkModifyAllowed) { $aActions[] = array ('label' => 'Modify All...', 'url' => "#"); } + if ($bIsBulkDeleteAllowed) { $aActions[] = array ('label' => 'Remove All', 'url' => "#"); } + if ($bIsModifyAllowed | $bIsDeleteAllowed) { $aActions[] = array ('label' => 'Manage Links...', 'url' => "#"); } + } + else + { + // many objects in the set, possible actions are: new / modify all / delete all + $aActions[] = array ('label' => 'eMail', 'url' => "mailto:?subject=".$oSet->GetFilter()->__DescribeHTML()."&body=".urlencode("http://localhost:81/pages/UI.php?operation=search&filter=$sFilter&$sContext")); + $aActions[] = array ('label' => 'CSV Export', 'url' => "../pages/$sUIPage?operation=search&filter=$sFilter&format=csv&$sContext"); + $aActions[] = array ('label' => 'Bookmark...', 'url' => "../pages/ajax.render.php?operation=create&class=$sClass&filter=$sFilter", 'class' => 'jqmTrigger'); + if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'New...', 'url' => "../pages/$sUIPage?operation=new&class=$sClass&$sContext"); } + if ($bIsBulkModifyAllowed) { $aActions[] = array ('label' => 'Modify All...', 'url' => "../pages/$sUIPage?operation=modify_all&filter=$sFilter&$sContext"); } + if ($bIsBulkDeleteAllowed) { $aActions[] = array ('label' => 'Delete All', 'url' => "../pages/$sUIPage?operation=delete_all&filter=$sFilter&$sContext"); } + } + } + $sHtml .= "
    \n
  • Actions\n
      \n"; + foreach ($aActions as $aAction) + { + $sClass = isset($aAction['class']) ? " class=\"{$aAction['class']}\"" : ""; + $sHtml .= "
    • {$aAction['label']}
    • \n
    • \n"; + } + $sHtml .= "
    \n
  • \n
\n"; + $oPage->add_ready_script("$(\"ul.jd_menu\").jdMenu();\n"); + return $sHtml; + } + +} +?> diff --git a/application/iotask.class.inc.php b/application/iotask.class.inc.php new file mode 100644 index 000000000..32eb33400 --- /dev/null +++ b/application/iotask.class.inc.php @@ -0,0 +1,54 @@ + "application", + "name" => "IOTask", + "description" => "Input / Output Task for synchronizing information with external data sources", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "name", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_iotask", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/default.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_AddAttribute(new AttributeString("name", array("label"=>"Task Name", "description"=>"Short name for this task", "allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("description", array("label"=>"Task Description", "description"=>"Long description for this task", "allowed_values"=>null, "sql"=>"description", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("category", array("label"=>"Category", "description"=>"Type of task", "allowed_values"=>new ValueSetEnum('Input, Ouput'), "sql"=>"category", "default_value"=>"Input", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("source_type", array("label"=>"Source Type", "description"=>"Type of data source", "allowed_values"=>new ValueSetEnum('File, Database, Web Service'), "sql"=>"source_type", "default_value"=>"File", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("source_subtype", array("label"=>"Source Subtype", "description"=>"Subtype of Data Source", "allowed_values"=>new ValueSetEnum('Oracle, MySQL, Postgress, MSSQL, SOAP, HTTP-Get, HTTP-Post, XML/RPC, CSV, XML, Excel'), "sql"=>"source_subtype", "default_value"=>"CSV", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("source_path", array("label"=>"Source Path", "description"=>"Path to the icon o the menu", "allowed_values"=>null, "sql"=>"source_path", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("objects_class", array("label"=>"Objects Class", "description"=>"Class of the objects processed by this task", "allowed_values"=>new ValueSetEnum('bizOrganization, bizContact, bizTeam, bizPerson, bizLocation, bizServer, bizPC, bizNetworkDevice, bizInterface, bizService, bizContract, bizInfraGroup, bizIncidentTicket, bizSoftware, bizApplication, bizPatch, bizWorkgroup, lnkContactRealObject, lnkInterfaces, bizInfraGrouping' ), "sql"=>"objects_class", "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("test_mode", array("label"=>"Test Mode", "description"=>"If set to 'Yes' the modifications are not applied", "allowed_values"=>new ValueSetEnum('Yes,No'), "sql"=>"test_mode", "default_value"=>'No', "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("verbose_mode", array("label"=>"Verbose Mode", "description"=>"If set to 'Yes' extra debug information is added to the log", "allowed_values"=>new ValueSetEnum('Yes,No'), "sql"=>"verbose_mode", "default_value" => 'No', "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("options", array("label"=>"Options", "description"=>"Reconciliation options", "allowed_values"=>new ValueSetEnum('Full, Update Only, Creation Only'), "sql"=>"options", "default_value"=> 'Full', "is_null_allowed"=>true, "depends_on"=>array()))); + + MetaModel::Init_AddFilterFromAttribute("name"); + MetaModel::Init_AddFilterFromAttribute("description"); + MetaModel::Init_AddFilterFromAttribute("category"); + MetaModel::Init_AddFilterFromAttribute("source_type"); + MetaModel::Init_AddFilterFromAttribute("source_subtype"); + MetaModel::Init_AddFilterFromAttribute("objects_class"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('name', 'description', 'category', 'objects_class', 'source_type', 'source_subtype', 'source_path' , 'options', 'test_mode', 'verbose_mode')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('name', 'description', 'category', 'objects_class', 'source_type', 'source_subtype', 'options')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('name', 'category', 'objects_class', 'source_type', 'source_subtype')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('name', 'description', 'category', 'objects_class', 'source_type', 'source_subtype')); // Criteria of the advanced search form + } +} +?> diff --git a/application/itopwebpage.class.inc.php b/application/itopwebpage.class.inc.php new file mode 100644 index 000000000..de1cd14ca --- /dev/null +++ b/application/itopwebpage.class.inc.php @@ -0,0 +1,414 @@ +m_sCurrentTabContainer = ''; + $this->m_sCurrentTab = ''; + $this->m_aTabs = array(); + $this->m_sMenu = ""; + $oAppContext = new ApplicationContext(); + $sExtraParams = $oAppContext->GetForLink(); + $this->add_header("Content-type: text/html; charset=utf-8"); + $this->add_header("Cache-control: no-cache"); + $this->m_currentOrganization = $currentOrganization; + $this->add_linked_script("../js/jquery.dimensions.js"); + $this->add_linked_script("../js/splitter.js"); + $this->add_linked_script("../js/jquery.tablehover.js"); + $this->add_linked_script("../js/jquery.treeview.js"); + $this->add_linked_script("../js/jquery.autocomplete.js"); + $this->add_linked_script("../js/jquery.bgiframe.js"); + $this->add_linked_script("../js/jquery.jdMenu.js"); + $this->add_linked_script("../js/date.js"); + $this->add_linked_script("../js/jquery.date.picker.js"); + $this->add_linked_script("../js/jquery.tablesorter.min.js"); + //$this->add_linked_script("../js/jquery-ui-personalized-1.5.3.js"); + $this->add_linked_script("../js/swfobject.js"); + $this->add_linked_stylesheet("../css/jquery.treeview.css"); + $this->add_linked_stylesheet("../css/jquery.autocomplete.css"); + $this->add_linked_stylesheet("../css/date.picker.css"); + $this->add_ready_script( +<< 0) + { + $("#RightPane").splitter({ + type: "h" //, + //minA: 100, initA: 150, maxA: 500, + //accessKey: "_" + }); + } + + // Manually set the outer splitter's height to fill the browser window. + // This must be re-done any time the browser window is resized. + $(window).bind("resize", function(){ + var ms = $("#MySplitter"); + var top = ms.offset().top; // from dimensions.js + var wh = $(window).height(); + // Account for margin or border on the splitter container + var mrg = parseInt(ms.css("marginBottom")) || 0; + var brd = parseInt(ms.css("borderBottomWidth")) || 0; + ms.css("height", (wh-top-mrg-brd)+"px"); + + // IE fires resize for splitter; others don't so do it here + if ( !jQuery.browser.msie ) + ms.trigger("resize"); + + + }).trigger("resize"); + + var ms = $("#MySplitter"); + ms.trigger("resize"); + + if ( $("#TopPane").length > 0) + { + $("#RightPane").trigger("resize"); + } + + $("#tabbedContent > ul").tabs( 1, { fxFade: true, fxSpeed: 'fast' } ); // tabs + $("table.listResults").tableHover(); // hover tables + $(".listResults").tablesorter( { headers: { 0:{sorter: false }}, widgets: ['zebra']} ); // sortable and zebra tables + $(".date-pick").datePicker( {clickInput: false, createButton: true, startDate: '2000-01-01'} ); // Date picker + $('#ModalDlg').jqm({ajax: '@href', trigger: 'a.jqmTrigger', overlay:70, modal:true, toTop:true}); // jqModal Window + //$('.display_block').draggable(); // make the blocks draggable +EOF +); + $this->add_script(" + // For automplete + function findValue(li) { + if( li == null ) return alert(\"No match!\"); + + // if coming from an AJAX call, let's use the CityId as the value + if( !!li.extra ) var sValue = li.extra[0]; + + // otherwise, let's just display the value in the text box + else var sValue = li.selectValue; + + //alert(\"The value you selected was: \" + sValue); + } + + function selectItem(li) { + findValue(li); + } + + function formatItem(row) { + return row[0]; + } + + function goBack() + { + window.history.back(); + } + "); + $this->DisplayMenu(); + } + + public function AddToMenu($sHtml) + { + $this->m_sMenu .= $sHtml; + } + + public function DisplayMenu() + { + // Combo box to select the organization + $this->AddToMenu("
+
\n"); + $this->AddToMenu("
\n"); + $this->AddToMenu("
    \n"); + $oAppContext = new ApplicationContext(); + // Display the menu + // 1) Application defined menus + $oSearchFilter = $oContext->NewFilter("menuNode"); + $oSearchFilter->AddCondition('parent_id', 0, '='); + $oSearchFilter->AddCondition('type', 'application', '='); + // There may be more criteria added later to have a specific menu based on the user's profile + $oSet = new CMDBObjectSet($oSearchFilter, array('rank' => true)); + while ($oRootMenuNode = $oSet->Fetch()) + { + $oRootMenuNode->DisplayMenu($this, 'application', $oAppContext->GetAsHash()); + } + // 2) User defined menus (Bookmarks) + $oSearchFilter = $oContext->NewFilter("menuNode"); + $oSearchFilter->AddCondition('parent_id', 0, '='); + $oSearchFilter->AddCondition('type', 'user', '='); + $oSearchFilter->AddCondition('user_id', UserRights::GetUserId(), '='); + // There may be more criteria added later to have a specific menu based on the user's profile + $oSet = new CMDBObjectSet($oSearchFilter, array('rank' => true)); + while ($oRootMenuNode = $oSet->Fetch()) + { + $oRootMenuNode->DisplayMenu($this, 'user', $oAppContext->GetAsHash()); + } + $this->AddToMenu("
\n"); + } + + /** + * Outputs (via some echo) the complete HTML page by assembling all its elements + */ + public function output() + { + foreach($this->a_headers as $s_header) + { + header($s_header); + } + $s_captured_output = ob_get_contents(); + ob_end_clean(); + echo "\n"; + echo "\n"; + echo "\n"; + echo "{$this->s_title}\n"; + foreach($this->a_linked_scripts as $s_script) + { + echo "\n"; + } + if (count($this->m_aReadyScripts)>0) + { + $this->add_script("\$(document).ready(function() {\n".implode("\n", $this->m_aReadyScripts)."\n});"); + } + if (count($this->a_scripts)>0) + { + echo "\n"; + } + foreach($this->a_linked_stylesheets as $a_stylesheet) + { + if ($a_stylesheet['condition'] != "") + { + echo "\n"; + } + } + + if (count($this->a_styles)>0) + { + echo "\n"; + } + echo "\n"; + echo "\n"; + echo "\n"; + + // Display the header + echo "
\n"; + echo "
iTop
\n"; + //echo "
\n"; + $sText = Utils::ReadParam('text', ''); + $sOnClick = ""; + if (empty($sText)) + { + // if no search text is supplied then + // 1) the search text is filled with "your search" + // 2) clicking on it will erase it + $sText = "Your search"; + $sOnClick = " onclick=\"this.value='';this.onclick=null;\""; + } + echo "
"; + echo "
+
\n"; + echo "
\n"; + + echo "
\n"; + + // Display the menu + echo "
\n"; + echo "
\n"; + echo $this->m_sMenu; + echo "
\n"; + + echo "
\n"; + + // Render the tabs in the page (if any) + foreach($this->m_aTabs as $sTabContainerName => $m_aTabs) + { + $sTabs = ''; + if (count($m_aTabs) > 0) + { + $sTabs = "\n
\n"; + $sTabs .= "
    \n"; + // Display the unordered list that will be rendered as the tabs + $i = 0; + foreach($m_aTabs as $sTabName => $sTabContent) + { + $sTabs .= "
  • ".htmlentities($sTabName)."
  • \n"; + $i++; + } + $sTabs .= "
\n"; + // Now add the content of the tabs themselves + $i = 0; + foreach($m_aTabs as $sTabName => $sTabContent) + { + $sTabs .= "
".$sTabContent."
\n"; + $i++; + } + $sTabs .= "
\n\n"; + } + $this->s_content = str_replace("\$Tabs:$sTabContainerName\$", $sTabs, $this->s_content); + } + + // Display the page's content + echo $this->s_content; + + // Add the captured output + if (trim($s_captured_output) != "") + { + echo "
$s_captured_output
\n"; + } + echo "
Please wait...
\n"; // jqModal Window + echo "
\n"; + echo "
\n"; + echo "
"; + echo "\n"; + echo "\n"; + } + + public function AddTabContainer($sTabContainer) + { + $this->m_aTabs[$sTabContainer] = array(); + $this->add("\$Tabs:$sTabContainer\$"); + } + + public function AddToTab($sTabContainer, $sTabLabel, $sHtml) + { + if (!isset($this->m_aTabs[$sTabContainer][$sTabLabel])) + { + // Set the content of the tab + $this->m_aTabs[$sTabContainer][$sTabLabel] = $sHtml; + } + else + { + // Append to the content of the tab + $this->m_aTabs[$sTabContainer][$sTabLabel] .= $sHtml; + } + } + + public function SetCurrentTabContainer($sTabContainer = '') + { + $sPreviousTabContainer = $this->m_sCurrentTabContainer; + $this->m_sCurrentTabContainer = $sTabContainer; + return $sPreviousTabContainer; + } + + public function SetCurrentTab($sTabLabel = '') + { + $sPreviousTab = $this->m_sCurrentTab; + $this->m_sCurrentTab = $sTabLabel; + return $sPreviousTab; + } + + public function StartCollapsibleSection($sSectionLabel, $bOpen = false) + { + $this->add($this->GetStartCollapsibleSection($sSectionLabel, $bOpen)); + } + + public function GetStartCollapsibleSection($sSectionLabel, $bOpen = false) + { + $sHtml = ''; + static $iSectionId = 0; + $sHtml .= "$sSectionLabel
\n"; + $sStyle = $bOpen ? '' : 'style="display:none" '; + $sHtml .= "
"; + $this->add_ready_script("\$(\"#LnkCollapse_$iSectionId\").click(function() {\$(\"#Collapse_$iSectionId\").slideToggle('normal'); $(\"#LnkCollapse_$iSectionId\").toggleClass('open');});"); + //$this->add_ready_script("$('#LnkCollapse_$iSectionId').hide();"); + $iSectionId++; + return $sHtml; + } + + public function EndCollapsibleSection() + { + $this->add($this->GetEndCollapsibleSection()); + } + + public function GetEndCollapsibleSection() + { + return "
"; + } + + public function add($sHtml) + { + if (!empty($this->m_sCurrentTabContainer) && !empty($this->m_sCurrentTab)) + { + $this->AddToTab($this->m_sCurrentTabContainer, $this->m_sCurrentTab, $sHtml); + } + else + { + parent::add($sHtml); + } + } + + /* + public function AddSearchForm($sClassName, $bOpen = false) + { + $iSearchSectionId = 0; + + $sStyle = $bOpen ? 'SearchDrawer' : 'SearchDrawer DrawerClosed'; + $this->add("
\n"); + $this->add("

Search form for ".Metamodel::GetName($sClassName)."

\n"); + $this->add_ready_script("\$(\"#LnkSearch_$iSearchSectionId\").click(function() {\$(\"#Search_$iSearchSectionId\").slideToggle('normal'); $(\"#LnkSearch_$iSearchSectionId\").toggleClass('open');});"); + $oFilter = new DBObjectSearch($sClassName); + $sFilter = $oFilter->serialize(); + $oSet = new CMDBObjectSet($oFilter); + cmdbAbstractObject::DisplaySearchForm($this, $oSet, array('operation' => 'search', 'filter' => $sFilter, 'search_form' => true)); + $this->add("
\n"); + $this->add("
\n"); + $this->add("
Search
\n"); + + + $iSearchSectionId++; + } + */ +} + +?> diff --git a/application/itopwizardwebpage.class.inc.php b/application/itopwizardwebpage.class.inc.php new file mode 100644 index 000000000..aadaf30c7 --- /dev/null +++ b/application/itopwizardwebpage.class.inc.php @@ -0,0 +1,32 @@ +m_iCurrentStep = $iCurrentStep; + $this->m_aSteps = $aSteps; + } + + public function output() + { + $aSteps = array(); + $iIndex = 0; + foreach($this->m_aSteps as $sStepTitle) + { + $iIndex++; + $sStyle = ($iIndex == $this->m_iCurrentStep) ? 'wizActiveStep' : 'wizStep'; + $aSteps[] = "
$sStepTitle
"; + } + $sWizardHeader = "

{$this->s_title}

\n".implode("
", $aSteps)."
\n"; + $this->s_content = "$sWizardHeader
".$this->s_content."
"; + parent::output(); + } +} +?> diff --git a/application/loginwebpage.class.inc.php b/application/loginwebpage.class.inc.php new file mode 100644 index 000000000..32e7ad3fb --- /dev/null +++ b/application/loginwebpage.class.inc.php @@ -0,0 +1,117 @@ +add_style(" +body { + background-color: #eee; + margin: 0; + padding: 0; +} +#login { + width: 230px; + margin-left: auto; + margin-right: auto; + margin-top: 150px; + padding: 20px; + background-color: #fff; + border: 1px solid #000; +} +.center { + text-align: center; +} + +h1 { + color: #83b217; + font-size: 16pt; +} +.v-spacer { + padding-top: 1em; +} + "); + } + + public function DisplayLoginForm($bFailedLogin = false) + { + $sAuthUser = utils::ReadParam('auth_user', ''); + $sAuthPwd = utils::ReadParam('suggest_pwd', ''); + + $this->add("
\n"); + $this->add("

Welcome to iTop!

\n"); + if ($bFailedLogin) + { + $this->add("

Incorrect login/password, please try again.

\n"); + } + else + { + $this->add("

Please identify yourself before continuing.

\n"); + } + $this->add("
\n"); + $this->add("\n"); + $this->add("\n"); + $this->add("\n"); + $this->add("\n"); + $this->add("
\n"); + $this->add("\n"); + $this->add("
\n"); + $this->add("
\n"); + } + + static function DoLogin() + { + $operation = utils::ReadParam('operation', ''); + session_start(); + + if (!session_is_registered('auth_user') || !session_is_registered('auth_pwd')) + { + if ($operation == 'login') + { + $sAuthUser = utils::ReadParam('auth_user', '', 'post'); + $sAuthPwd = utils::ReadParam('auth_pwd', '', 'post'); + } + else + { + $oPage = new login_web_page(); + $oPage->DisplayLoginForm(); + $oPage->output(); + exit; + } + } + else + { + $sAuthUser = $_SESSION['auth_user']; + $sAuthPwd = $_SESSION['auth_pwd']; + } + if (!UserRights::Login($sAuthUser, $sAuthPwd)) + { + // Unset all of the session variables. + $_SESSION = array(); + // If it's desired to kill the session, also delete the session cookie. + // Note: This will destroy the session, and not just the session data! + if (isset($_COOKIE[session_name()])) + { + setcookie(session_name(), '', time()-3600, '/'); + } + // Finally, destroy the session. + session_destroy(); + + $oPage = new login_web_page(); + $oPage->DisplayLoginForm( true /* failed attempt */); + $oPage->output(); + exit; + } + else + { + $_SESSION['auth_user'] = $sAuthUser ; + $_SESSION['auth_pwd'] = $sAuthPwd; + + } + } +} // End of class +?> diff --git a/application/menunode.class.inc.php b/application/menunode.class.inc.php new file mode 100644 index 000000000..f571f40f3 --- /dev/null +++ b/application/menunode.class.inc.php @@ -0,0 +1,219 @@ + "gui", + "name" => "menuNode", + "description" => "Main menu configuration elements", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "name", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_menunode", + "db_key_field" => "id", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); +// MetaModel::Init_AddAttribute(new AttributeExternalKey("change", array("label"=>"change", "description"=>"change", "allowed_values"=>null, "sql"=>"changeid", "targetclass"=>"CMDBChange", "jointype"=>"closed"))); +// MetaModel::Init_AddAttribute(new AttributeExternalField("date", array("label"=>"date", "description"=>"date and time of the change", "allowed_values"=>null, "extkey_attcode"=>"change", "target_attcode"=>"date"))); + MetaModel::Init_AddAttribute(new AttributeString("name", array("label"=>"Menu Name", "description"=>"Short name for this menu", "allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("label", array("label"=>"Menu Description", "description"=>"Long description for this menu", "allowed_values"=>null, "sql"=>"label", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("hyperlink", array("label"=>"Hyperlink", "description"=>"Hyperlink to the page", "allowed_values"=>null, "sql"=>"hyperlink", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("icon_path", array("label"=>"Menu Icon", "description"=>"Path to the icon o the menu", "allowed_values"=>null, "sql"=>"icon_path", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeText("template", array("label"=>"Template", "description"=>"HTML template for the view", "allowed_values"=>null, "sql"=>"template", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("type", array("label"=>"Type", "description"=>"Type of menu", "allowed_values"=>new ValueSetEnum('application,user'), "sql"=>"type", "default_value"=>"application", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeInteger("rank", array("label"=>"Display rank", "description"=>"Sort order for displaying the menu", "allowed_values"=>null, "sql"=>"rank", "default_value" => 999, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("parent_id", array("label"=>"Parent Menu Item", "description"=>"Parent Menu Item", "allowed_values"=>null, "sql"=>"parent_id", "targetclass"=>"menuNode", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("parent_name", array("label"=>"Parent Menu Item", "description"=>"Parent Menu Item", "allowed_values"=>null, "extkey_attcode"=>"parent_id", "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("label"=>"Owner of the menu", "description"=>"User who owns this menu (for user defined menus)", "allowed_values"=>null, "sql"=>"user_id", "targetclass"=>"UserRightsMatrixUsers", "is_null_allowed"=>true, "depends_on"=>array('type')))); + + MetaModel::Init_AddFilterFromAttribute("label"); + MetaModel::Init_AddFilterFromAttribute("parent_id"); + MetaModel::Init_AddFilterFromAttribute("rank"); + MetaModel::Init_AddFilterFromAttribute("type"); + MetaModel::Init_AddFilterFromAttribute("user_id"); + + MetaModel::Init_SetZListItems('details', array('parent_id', 'name', 'label', 'hyperlink', 'template', 'rank', 'type')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('parent_id', 'name', 'label', 'rank', 'type')); // Attributes to be displayed for a list + } + + public function IsVisible() + { + return true; + } + + public function GetMenuName() + { + return $this->Get('name'); + } + + public function GetMenuIcon() + { + return $this->Get('icon_path'); + } + + public function GetMenuLabel() + { + return $this->Get('label'); + } + + public function GetMenuLink($aExtraParams) + { + $aExtraParams['menu'] = $this->GetKey(); // Make sure we overwrite the current menu id (if any) + $aParams = array(); + foreach($aExtraParams as $sName => $sValue) + { + $aParams[] = urlencode($sName)."=".urlencode($sValue); + } + return $this->Get('hyperlink')."?".implode("&", $aParams); + } + + public function GetChildNodesSet($sType) + { + $oSearchFilter = new DBObjectSearch("menuNode"); + $oSearchFilter->AddCondition('parent_id', $this->GetKey(), '='); + $oSearchFilter->AddCondition('type', $sType, '='); + if ($sType == 'user') + { + $oSearchFilter->AddCondition('user_id', UserRights::GetUserId(), '='); + } + $oSet = new CMDBObjectSet($oSearchFilter, array('rank' => true)); + return $oSet; + } + + public function RenderContent(web_page $oPage, $aExtraParams = array()) + { + $sTemplate = $this->Get('template'); + $this->ProcessTemplate($sTemplate, $oPage, $aExtraParams); + } + + protected function ProcessTemplate($sTemplate, web_page $oPage, $aExtraParams = array()) + { + $iStartPos = stripos($sTemplate, '<'.DisplayBlock::TAG_BLOCK.' ',0); + $index = 0; + while(($iStartPos = stripos($sTemplate, '<'.DisplayBlock::TAG_BLOCK.' ',0)) !== false) + { + $iEndPos = stripos($sTemplate, '', $iStartPos); + + $sBlockDefinition = substr($sTemplate, $iStartPos, $iEndPos - $iStartPos + strlen('')); + $oBlock = DisplayBlock::FromTemplate($sBlockDefinition); + + $oPage->add(substr($sTemplate, 0, $iStartPos)); + if ($oBlock) // Protects agains invalid XML templates + { + $oBlock->Display($oPage, "block{$index}", $aExtraParams); // Values from $aExtraParams have precedence over $aParams + } + $index++; + $sTemplate = substr($sTemplate, $iEndPos + strlen('')); + } + // What remains is purely static (without any block inside), just output as it is + $oPage->add($sTemplate); + } + + public function DisplayMenu(iTopWebPage $oP, $sType, $aExtraParams) + { + $oP->AddToMenu("
  • GetMenuLink($aExtraParams)."\" title=\"".$this->GetMenuLabel()."\">".$this->GetMenuName().""); + $oSet = $this->GetChildNodesSet($sType); + if ($oSet->Count() > 0) + { + $oP->AddToMenu("\n
      \n"); + while($oChildNode = $oSet->Fetch()) + { + $oChildNode->DisplayMenu($oP, $sType, $aExtraParams); + } + $oP->AddToMenu("
    \n"); + } + $oP->AddToMenu("
  • \n"); + } + static public function DisplayCreationForm(web_page $oP, $sClass, $sFilter, $aExtraParams = array()) + { + $oFilter = DBObjectSearch::unserialize($sFilter); + $oP->p('Create a new menu item for: '.$oFilter->__DescribeHTML()); + $oP->add('
    '); + $oP->add(''); + $oP->add(''); + $oP->add(''); + $oP->p('Menu Label: '); + $oP->p('Description: '); + $oP->add('

    Insert after:

    '); + $oP->p(' Create as a child menu item'); + $oP->p('      '); + $oP->add('
    '); + } + + static public function GetMenuAsArray($oRootNode = null, $sType = 'application', $iDepth = 0) + { + $aNodes = array(); + if (is_object($oRootNode)) + { + $oChildSet = $oRootNode->GetChildNodesSet($sType); + while($oNode = $oChildSet->Fetch()) + { + $aNodes[] = array('depth' => $iDepth, 'id' => $oNode->GetKey(), 'label' => $oNode->GetName()); + $aNodes = array_merge($aNodes, self::GetMenuAsArray($oNode, $sType, $iDepth+1)); + } + } + else + { + $oSearchFilter = new DbObjectSearch("menuNode"); + $oSearchFilter->AddCondition('parent_id', 0, '='); + $oSearchFilter->AddCondition('type', $sType, '='); + if ($sType == 'user') + { + $oSearchFilter->AddCondition('user_id', UserRights::GetUserId(), '='); + } + $oRootSet = new CMDBObjectSet($oSearchFilter, array('rank' => true)); + while($oNode = $oRootSet->Fetch()) + { + $aNodes[] = array('depth' => $iDepth, 'id' => $oNode->GetKey(), 'label' => $oNode->GetName()); + $aNodes = array_merge($aNodes, self::GetMenuAsArray($oNode, $sType, $iDepth+1)); + } + } + return $aNodes; + } + /** + * Returns a set of all the nodes following the current node in the tree + * (i.e. nodes with the same parent but with a greater rank) + */ + public function GetNextNodesSet($sType = 'application') + { + $oSearchFilter = new DBObjectSearch("menuNode"); + $oSearchFilter->AddCondition('parent_id', $this->Get('parent_id')); + $oSearchFilter->AddCondition('rank', $this->Get('rank'), '>'); + $oSearchFilter->AddCondition('type', $sType, '='); + if ($sType == 'user') + { + $oSearchFilter->AddCondition('user_id', UserRights::GetUserId(), '='); + } + $oSet = new DBObjectSet($oSearchFilter, array('rank'=> true)); // Order by rank (true means ascending) + return $oSet; + } +} +?> diff --git a/application/nicewebpage.class.inc.php b/application/nicewebpage.class.inc.php new file mode 100644 index 000000000..bd29966ed --- /dev/null +++ b/application/nicewebpage.class.inc.php @@ -0,0 +1,76 @@ +m_aReadyScripts = array(); + $this->add_linked_script("../js/jquery.latest.js"); + $this->add_linked_script("../js/jquery.history_remote.pack.js"); + //$this->add_linked_script("../js/ui.resizable.js"); + $this->add_linked_script("../js/ui.tabs.js"); + $this->add_linked_script("../js/hovertip.js"); + $this->add_linked_script("../js/jqModal.js"); + $this->add_linked_stylesheet("../css/light-grey.css"); + $this->add_linked_stylesheet("../js/themes/light/light.tabs.css"); + //$this->add_linked_stylesheet("../css/jquery.tabs-ie.css", "lte IE 7"); + $this->add_linked_stylesheet("../css/jqModal.css"); + $this->add_ready_script(' window.setTimeout(hovertipInit, 1);'); + } + + public function small_p($sText) + { + $this->add("

    $sText

    \n"); + } + + // By Rom, used by CSVImport and Advanced search + public function MakeClassesSelect($sName, $sDefaultValue, $iWidthPx) + { + // $aTopLevelClasses = array('bizService', 'bizContact', 'logInfra', 'bizDocument'); + // These are classes wich root class is cmdbAbstractObject ! + $this->add(""); + } + + // By Rom, used by Advanced search + public function add_select($aChoices, $sName, $sDefaultValue, $iWidthPx) + { + $this->add(""); + } + + public function add_ready_script($sScript) + { + $this->m_aReadyScripts[] = $sScript; + } + + /** + * Outputs (via some echo) the complete HTML page by assembling all its elements + */ + public function output() + { + if (count($this->m_aReadyScripts)>0) + { + $this->add_script("\$(document).ready(function() {\n".implode("\n", $this->m_aReadyScripts)."\n});"); + } + parent::output(); + } +} + +?> diff --git a/application/startup.inc.php b/application/startup.inc.php new file mode 100644 index 000000000..83ea90c2a --- /dev/null +++ b/application/startup.inc.php @@ -0,0 +1,7 @@ + diff --git a/application/template.class.inc.php b/application/template.class.inc.php new file mode 100644 index 000000000..b6bdd8a61 --- /dev/null +++ b/application/template.class.inc.php @@ -0,0 +1,224 @@ +m_aTags = array('itopblock', 'itoptabs', 'itoptab', 'itoptoggle'); + $this->m_sTemplate = $sTemplate; + } + + public function Render(web_page $oPage, $aParams = array()) + { + $this->ApplyParams($aParams); + $iStart = 0; + $iEnd = strlen($this->m_sTemplate); + $iCount = 0; + $iBeforeTagPos = $iStart; + $iAfterTagPos = $iStart; + while($sTag = $this->GetNextTag($iStart, $iEnd)) + { + $sContent = $this->GetTagContent($sTag, $iStart, $iEnd); + $aAttributes = $this->GetTagAttributes($sTag, $iStart, $iEnd); + //$oPage->p("Tag: $sTag - ($iStart, $iEnd)"); + $oPage->add(substr($this->m_sTemplate, $iBeforeTagPos, $iStart - $iBeforeTagPos)); + $this->RenderTag($oPage, $sTag, $aAttributes, $sContent); + + $iAfterTagPos = $iEnd + strlen(''); + $iBeforeTagPos = $iAfterTagPos; + $iStart = $iEnd; + $iEnd = strlen($this->m_sTemplate); + $iCount++; + if ($iCount > 10) break; + } + $oPage->add(substr($this->m_sTemplate, $iAfterTagPos)); + } + + /** + * Replaces all the parameters by the values passed in the hash array + */ + public function ApplyParams($aParams) + { + $aSearches = array(); + $aReplacements = array(); + foreach($aParams as $sSearch => $sReplace) + { + $aSearches[] = '$'.$sSearch.'$'; + $aReplacements[] = $sReplace; + } + $this->m_sTemplate = str_replace($aSearches, $aReplacements, $this->m_sTemplate); + } + + public function GetNextTag(&$iStartPos, &$iEndPos) + { + $iChunkStartPos = $iStartPos; + $sNextTag = null; + $iStartPos = $iEndPos; + foreach($this->m_aTags as $sTag) + { + // Search for the opening tag + $iOpeningPos = stripos($this->m_sTemplate, '<'.$sTag.' ', $iChunkStartPos); + if ($iOpeningPos === false) + { + $iOpeningPos = stripos($this->m_sTemplate, '<'.$sTag.'>', $iChunkStartPos); + } + if ($iOpeningPos !== false) + { + $iClosingPos = stripos($this->m_sTemplate, '', $iOpeningPos); + } + if ( ($iOpeningPos !== false) && ($iClosingPos !== false)) + { + if ($iOpeningPos < $iStartPos) + { + // This is the next tag + $iStartPos = $iOpeningPos; + $iEndPos = $iClosingPos; + $sNextTag = $sTag; + } + } + } + return $sNextTag; + } + + public function GetTagContent($sTag, $iStartPos, $iEndPos) + { + $sContent = ""; + $iContentStart = strpos($this->m_sTemplate, '>', $iStartPos); // Content of tag start immediatly after the first closing bracket + if ($iContentStart !== false) + { + $sContent = substr($this->m_sTemplate, 1+$iContentStart, $iEndPos - $iContentStart - 1); + } + return $sContent; + } + + public function GetTagAttributes($sTag, $iStartPos, $iEndPos) + { + $aAttr = array(); + $iAttrStart = strpos($this->m_sTemplate, ' ', $iStartPos); // Attributes start just after the first space + $iAttrEnd = strpos($this->m_sTemplate, '>', $iStartPos); // Attributes end just before the first closing bracket + if ( ($iAttrStart !== false) && ($iAttrEnd !== false) && ($iAttrEnd > $iAttrStart)) + { + $sAttributes = substr($this->m_sTemplate, 1+$iAttrStart, $iAttrEnd - $iAttrStart - 1); + $aAttributes = explode(' ', $sAttributes); + foreach($aAttributes as $sAttr) + { + if ( preg_match('/(.+) *= *"(.+)"$/', $sAttr, $aMatches) ) + { + $aAttr[strtolower($aMatches[1])] = $aMatches[2]; + } + } + } + return $aAttr; + } + + protected function RenderTag($oPage, $sTag, $aAttributes, $sContent) + { + static $iTabContainerCount = 0; + static $iBlockCount = 0; + switch($sTag) + { + case 'itoptabs': + $oPage->AddTabContainer('Tabs_'.$iTabContainerCount); + $oPage->SetCurrentTabContainer('Tabs_'.$iTabContainerCount); + $iTabContainerCount++; + //$oPage->p('Content:
    '.htmlentities($sContent).'
    '); + $oTemplate = new DisplayTemplate($sContent); + $oTemplate->Render($oPage, array()); // no params to apply, they have already been applied + $oPage->SetCurrentTabContainer(''); + break; + + case 'itoptab': + $oPage->SetCurrentTab($aAttributes['name']); + $oTemplate = new DisplayTemplate($sContent); + $oTemplate->Render($oPage, array()); // no params to apply, they have already been applied + //$oPage->p('iTop Tab Content:
    '.htmlentities($sContent).'
    '); + $oPage->SetCurrentTab(''); + break; + + case 'itoptoggle': + $oPage->StartCollapsibleSection($aAttributes['name']); + $oTemplate = new DisplayTemplate($sContent); + $oTemplate->Render($oPage, array()); // no params to apply, they have already been applied + //$oPage->p('iTop Tab Content:
    '.htmlentities($sContent).'
    '); + $oPage->EndCollapsibleSection(); + break; + + case 'itopblock': // TO DO: Use DisplayBlock::FromTemplate here + $sBlockClass = $aAttributes['blockclass']; + $sBlockType = $aAttributes['type']; + $aExtraParams = array(); + if (isset($aAttributes['linkage'])) + { + $aExtraParams['linkage'] = $aAttributes['linkage']; + } + + switch($aAttributes['encoding']) + { + case 'text/sibusql': + $oFilter = CMDBSearchFilter::FromSibusQL($sContent); + break; + + case 'text/oql': + $oFilter = CMDBSearchFilter::FromOQL($sContent); + break; + + case 'text/serialize': + default: + $oFilter = CMDBSearchFilter::unserialize($sContent); + break; + } + $oBlock = new $sBlockClass($oFilter, $sBlockType, false, $aExtraParams); + $oBlock->Display($oPage, 'block_'.$iBlockCount); + $iBlockCount++; + break; + + default: + // Unknown tag, just ignore it or now -- output an HTML comment + $oPage->add(""); + } + } + + /** + * Unit test + */ + static public function UnitTest() + { + require_once('../application/startup.inc.php'); + require_once("../application/itopwebpage.class.inc.php"); + + $sTemplate = ' + + bizNetworkDevice: pkey = $pkey$ + + + bizInterface: device_id = $pkey$ + + + bizContact: PKEY IS contact_id IN (ContactsLinks: object_id = $pkey$) + + + bizDocument: PKEY IS doc_id IN (lnkDocumentRealObject: object_id = $pkey$) + + '; + + $oPage = new iTopWebPage('Unit Test', 3); + //$oPage->add("Template content:
    ".htmlentities($sTemplate)."
    \n"); + $oTemplate = new DisplayTemplate($sTemplate); + $oTemplate->Render($oPage, array('class'=>'Network device','pkey'=> 271, 'name' => 'deliversw01.mecanorama.fr', 'org_id' => 3)); + $oPage->output(); + } +} + +//DisplayTemplate::UnitTest(); + +?> diff --git a/application/templates/audit_category.html b/application/templates/audit_category.html new file mode 100644 index 000000000..7a25e8791 --- /dev/null +++ b/application/templates/audit_category.html @@ -0,0 +1,12 @@ + + +$class$: pkey = $pkey$ + + + AuditRule: category_id = $pkey$ + + diff --git a/application/ui.linkswidget.class.inc.php b/application/ui.linkswidget.class.inc.php new file mode 100644 index 000000000..f980adec1 --- /dev/null +++ b/application/ui.linkswidget.class.inc.php @@ -0,0 +1,301 @@ +m_sClass = $sClass; + $this->m_sAttCode = $sAttCode; + $this->m_iInputId = $iInputId; + } + + public function Display(web_page $oPage, $oCurrentValuesSet = null) + { + $sHTMLValue = ''; + $sTargetClass = self::GetTargetClass($this->m_sClass, $this->m_sAttCode); + $aAllowedValues = MetaModel::GetAllowedValues_att($this->m_sClass, $this->m_sAttCode, array(), ''); + $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sAttCode); + $sExtKeyToRemote = $oAttDef->GetExtKeyToRemote(); + $sExtKeyToMe = $oAttDef->GetExtKeyToMe(); + $sStateAttCode = MetaModel::GetStateAttributeCode($this->m_sClass); + $sDefaultState = MetaModel::GetDefaultState($this->m_sClass); + + $sLinkedClass = $oAttDef->GetLinkedClass(); + foreach(MetaModel::ListAttributeDefs($sLinkedClass) as $sAttCode=>$oAttDef) + { + if ($sStateAttCode == $sAttCode) + { + // State attribute is always hidden from the UI + } + else if (!$oAttDef->IsExternalField() && ($sAttCode != $sExtKeyToMe) && ($sAttCode != $sExtKeyToRemote)) + { + $iFlags = MetaModel::GetAttributeFlags($this->m_sClass, $sDefaultState, $sAttCode); + if ( !($iFlags & OPT_ATT_HIDDEN) && !($iFlags & OPT_ATT_READONLY) ) + { + $aAttributes[] = $sAttCode; + } + } + } + $sAttributes = "['".implode("','", $aAttributes)."']"; + if ($oCurrentValuesSet != null) + { + // Serialize the link set into a JSon object + $aCurrentValues = array(); + $sRow = '{'; + while($oLinkObj = $oCurrentValuesSet->Fetch()) + { + foreach($aAttributes as $sLinkAttCode) + { + $sRow.= "\"$sLinkAttCode\": \"".addslashes($oLinkObj->Get($sLinkAttCode))."\", "; + } + $sRow .= "\"$sExtKeyToRemote\": ".$oLinkObj->Get($sExtKeyToRemote).'}'; + $aCurrentValues[] = $sRow; + } + $sJSON = '['.implode(',', $aCurrentValues).']'; + } + + // Many values (or even a unknown list) display an autocomplete + if ( (count($aAllowedValues) == 0) || (count($aAllowedValues) > 20) ) + { + // too many choices, use an autocomplete + // The input for the auto complete + $sTitle = $oAttDef->GetDescription(); + $sHTMLValue .= "\n"; + $sHTMLValue .= $this->GetObjectPickerDialog($oPage, $sTargetClass, 'oLinkWidget'.$this->m_iInputId.'.OnOk'); + $sHTMLValue .= $this->GetLinkObjectDialog($oPage, $this->m_iInputId); + $sHTMLValue .= "m_iInputId}\" size=\"35\" name=\"\" value=\"\" style=\"border: 1px solid red;\" />"; + $sHTMLValue .= "m_iInputId}.AddObject();\"/>"; + $sHTMLValue .= " m_iInputId', '$sExtKeyToRemote');\"/>"; + // another hidden input to store & pass the object's Id + $sHTMLValue .= "m_iInputId}\"/>\n"; + $sHTMLValue .= "m_iInputId}\" name=\"attr_{$this->m_sAttCode}\" value=\"\"/>\n"; + $oPage->add_ready_script("\$('#{$this->m_iInputId}').val('$sJSON');\n\$('#ac_{$this->m_iInputId}').autocomplete('./ajax.render.php', { minChars:3, onItemSelect:selectItem, onFindValue:findValue, formatItem:formatItem, autoFill:true, keyHolder:'#id_ac_{$this->m_iInputId}', extraParams:{operation:'ui.linkswidget', sclass:'{$this->m_sClass}', attCode:'{$this->m_sAttCode}', max:30}});"); + } + else + { + // Few choices, use a normal 'select' + $sHTMLValue = "\n"; + } + $sHTMLValue .= "
    m_iInputId}_values\">\n"; + if ($oCurrentValuesSet != null) + { + // transform the DBObjectSet into a CMDBObjectSet !!! + $aLinkedObjects = $oCurrentValuesSet->ToArray(false); + if (count($aLinkedObjects) > 0) + { + $oSet = CMDBObjectSet::FromArray($sLinkedClass, $aLinkedObjects); + $oDisplayBlock = DisplayBlock::FromObjectSet($oSet, 'list'); + $sHTMLValue .= $oDisplayBlock->GetDisplay($oPage, $this->m_iInputId.'_current', array('linkage' => $sExtKeyToMe)); + } + } + $sHTMLValue .= "
    \n"; + return $sHTMLValue; + } + /** + * This static function is called by the Ajax Page when there is a need to fill an autocomplete combo + * @param $oPage web_page The ajax page used for the put^put (sent back to the browser + * @param $oContext UserContext The context of the user (for limiting the search) + * @param $sClass string The name of the class of the current object being edited + * @param $sAttCode string The name of the attribute being edited + * @param $sName string The partial name that was typed by the user + * @param $iMaxCount integer The maximum number of items to return + * @return void + */ + static public function Autocomplete(web_page $oPage, UserContext $oContext, $sClass, $sAttCode, $sName, $iMaxCount) + { + $aAllowedValues = MetaModel::GetAllowedValues_att($sClass, $sAttCode, array() /* $aArgs */, $sName); + if ($aAllowedValues != null) + { + $iCount = $iMaxCount; + foreach($aAllowedValues as $key => $value) + { + $oPage->add($value."|".$key."\n"); + $iCount--; + if ($iCount == 0) break; + } + } + else // No limitation to the allowed values + { + // Search for all the object of the linked class + $oAttDef = $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode); + $sLinkedClass = $oAttDef->GetLinkedClass(); + $sSearchClass = self::GetTargetClass($sClass, $sAttCode); + $oFilter = $oContext->NewFilter($sSearchClass); + $sSearchAttCode = MetaModel::GetNameAttributeCode($sSearchClass); + $oFilter->AddCondition($sSearchAttCode, $sName, 'Begins with'); + $oSet = new CMDBObjectSet($oFilter, array($sSearchAttCode => true)); + $iCount = 0; + while( ($iCount < $iMaxCount) && ($oObj = $oSet->fetch()) ) + { + $oPage->add($oObj->GetName()."|".$oObj->GetKey()."\n"); + $iCount++; + } + } + } + + /** + * This static function is called by the Ajax Page display a set of objects being linked + * to the object being created + * @param $oPage web_page The ajax page used for the put^put (sent back to the browser + * @param $sClass string The name of the class 'linking class' which is the class of the objects to display + * @param $sAttCode string The name of the attribute is the main object being created + * @param $sSet JSON serialized set of objects + * @return void + */ + static public function RenderSet($oPage, $sClass, $sJSONSet, $sExtKeyToMe) + { + $aSet = json_decode($sJSONSet, true); // true means hash array instead of object + $oSet = CMDBObjectSet::FromScratch($sClass); + foreach($aSet as $aObject) + { + $oObj = MetaModel::NewObject($sClass); + foreach($aObject as $sAttCode => $value) + { + $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode); + if ($oAttDef->IsExternalKey()) + { + $oTargetObj = MetaModel::GetObject($oAttDef->GetTargetClass(), $value); // @@ optimization, don't do & query per object in the set ! + $oObj->Set($sAttCode, $oTargetObj); + } + else + { + $oObj->Set($sAttCode, $value); + } + + } + $oSet->AddObject($oObj); + } + cmdbAbstractObject::DisplaySet($oPage, $oSet, $sExtKeyToMe); + } + + + protected static function GetTargetClass($sClass, $sAttCode) + { + $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode); + $sLinkedClass = $oAttDef->GetLinkedClass(); + switch(get_class($oAttDef)) + { + case 'AttributeLinkedSetIndirect': + $oLinkingAttDef = MetaModel::GetAttributeDef($sLinkedClass, $oAttDef->GetExtKeyToRemote()); + $sTargetClass = $oLinkingAttDef->GetTargetClass(); + break; + + case 'AttributeLinkedSet': + $sTargetClass = $sLinkedClass; + break; + } + + return $sTargetClass; + } + + protected function GetObjectPickerDialog($oPage, $sTargetClass, $sOkFunction) + { + $sHTML = <<< EOF +
    + + + + + + + + + + +
    +

    Selected objects:

    + +

    +
    +

    +

    +
    +

    Available objects:

    + +

    +
    +       +
    +
    +EOF; + $oPage->add_ready_script("$('#ManageObjectsDlg_$this->m_iInputId').jqm({overlay:70, modal:true, toTop:true});"); // jqModal Window + //$oPage->add_ready_script("UpdateObjectList('$sClass');"); + return $sHTML; + } + + protected function GetLinkObjectDialog($oPage, $sId) + { + $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sAttCode); + $sLinkedClass = $oAttDef->GetLinkedClass(); + $sStateAttCode = MetaModel::GetStateAttributeCode($sLinkedClass); + $sDefaultState = MetaModel::GetDefaultState($sLinkedClass); + $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sAttCode); + $sExtKeyToMe = $oAttDef->GetExtKeyToMe(); + $sExtKeyToRemote = $oAttDef->GetExtKeyToRemote(); + + $sHTML = "
    \n"; + $sHTML .= "

    $sLinkedClass attributes

    \n"; + $sHTML .= "
    \n"; + $index = 0; + $aAttrsMap = array(); + foreach(MetaModel::ListAttributeDefs($sLinkedClass) as $sAttCode=>$oAttDef) + { + if ($sStateAttCode == $sAttCode) + { + // State attribute is always hidden from the UI + //$sHTMLValue = $this->GetState(); + //$aDetails[] = array('label' => $oAttDef->GetLabel(), 'value' => $sHTMLValue); + } + else if (!$oAttDef->IsExternalField() && ($sAttCode != $sExtKeyToMe) && ($sAttCode != $sExtKeyToRemote)) + { + $iFlags = MetaModel::GetAttributeFlags($sLinkedClass, $sDefaultState, $sAttCode); + if ($iFlags & OPT_ATT_HIDDEN) + { + // Attribute is hidden, do nothing + } + else + { + if ($iFlags & OPT_ATT_READONLY) + { + // Attribute is read-only + $sHTMLValue = $this->GetAsHTML($sAttCode); + } + else + { + $sValue = ""; //$this->Get($sAttCode); + $sDisplayValue = ""; //$this->GetDisplayValue($sAttCode); + $sSubId = $sId.'_'.$index; + $aAttrsMap[$sAttCode] = $sSubId; + $index++; + $sHTMLValue = cmdbAbstractObject::GetFormElementForField($oPage, $sLinkedClass, $sAttCode, $oAttDef, $sValue, $sDisplayValue, $sSubId); + } + $aDetails[] = array('label' => $oAttDef->GetLabel(), 'value' => $sHTMLValue); + } + } + } + $sHTML .= $oPage->GetDetails($aDetails); + $sHTML .= "     \n"; + $sHTML .= "
    \n"; + $sHTML .= "
    \n"; + $oPage->add_ready_script("$('#LinkDlg_$sId').jqm({overlay:70, modal:true, toTop:true});"); // jqModal Window + //$oPage->add_ready_script("UpdateObjectList('$sClass');"); + return $sHTML; + } +} +?> diff --git a/application/uiwizard.class.inc.php b/application/uiwizard.class.inc.php new file mode 100644 index 000000000..3c8a61192 --- /dev/null +++ b/application/uiwizard.class.inc.php @@ -0,0 +1,257 @@ +m_oPage = $oPage; + $this->m_sClass = $sClass; + if (empty($sTargetState)) + { + $sTargetState = MetaModel::GetDefaultState($sClass); + } + $this->m_sTargetState = $sTargetState; + $this->m_aWizardSteps = $this->ComputeWizardStructure(); + } + + public function GetObjectClass() { return $this->m_sClass; } + public function GetTargetState() { return $this->m_sTargetState; } + public function GetWizardStructure() { return $this->m_aWizardSteps; } + + /** + * Displays one step of the wizard + */ + public function DisplayWizardStep($aStep, $iStepIndex, &$iMaxInputId, &$aFieldsMap, $bFinishEnabled = false) + { + $this->m_oPage->add("
    \n"); + $this->m_oPage->add("\n"); + $aStates = MetaModel::EnumStates($this->m_sClass); + $aDetails = array(); + $sJSHandlerCode = ''; // Javascript code to be executed each time this step of the wizard is entered + foreach($aStep as $sAttCode) + { + $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode); + $sAttLabel = $oAttDef->GetLabel(); + $iOptions = isset($aStates[$this->m_sTargetState]['attribute_list'][$sAttCode]) ? $aStates[$this->m_sTargetState]['attribute_list'][$sAttCode] : 0; + + $aPrerequisites = $oAttDef->GetPrerequisiteAttributes(); + if ($iOptions & (OPT_ATT_MANDATORY | OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT)) + { + $aFields[$sAttCode] = array(); + foreach($aPrerequisites as $sCode) + { + $aFields[$sAttCode][$sCode] = ''; + } + } + if (count($aPrerequisites) > 0) + { + $aOptions[] = 'Prerequisites: '.implode(', ', $aPrerequisites); + } + + $sFieldFlag = ($iOptions & (OPT_ATT_MANDATORY | OPT_ATT_MUSTCHANGE)) ? ' *' : ''; + $oDefaultValuesSet = $oAttDef->GetDefaultValue(); // @@@ TO DO: get the object's current value if the object exists + $sHTMLValue = cmdbAbstractObject::GetFormElementForField($this->m_oPage, $this->m_sClass, $sAttCode, $oAttDef, $oDefaultValuesSet, '', "att_$iMaxInputId"); + $aFieldsMap[$iMaxInputId] = $sAttCode; + $aDetails[] = array('label' => $oAttDef->GetLabel().$sFieldFlag, 'value' => "
    $sHTMLValue
    "); + if ($oAttDef->GetValuesDef() != null) + { + $sJSHandlerCode .= "\toWizardHelper.RequestAllowedValues('$sAttCode');\n"; + } + if ($oAttDef->GetDefaultValue() != null) + { + $sJSHandlerCode .= "\toWizardHelper.RequestDefaultValue('$sAttCode');\n"; + } + if ($oAttDef->IsLinkSet()) + { + $sJSHandlerCode .= "\toLinkWidgetatt_$iMaxInputId.Init();"; + } + $iMaxInputId++; + } + //$aDetails[] = array('label' => '', 'value' => ''); + $this->m_oPage->details($aDetails); + $sBackButtonDisabled = ($iStepIndex <= 1) ? 'disabled' : ''; + $sDisabled = $bFinishEnabled ? '' : 'disabled'; + $nbSteps = count($this->m_aWizardSteps['mandatory']) + count($this->m_aWizardSteps['optional']); + $this->m_oPage->add("
    + + + +
    \n"); + $this->m_oPage->add(" +\n"); + $this->m_oPage->add("
    \n\n"); + } + + /** + * Display the final step of the wizard: a confirmation screen + */ + public function DisplayFinalStep($iStepIndex, $aFieldsMap) + { + $this->m_oPage->add("\n"); + } + /** + * Compute the order of the fields & pages in the wizard + * @param $oPage iTopWebPage The current page (used to display error messages) + * @param $sClass string Name of the class + * @param $sStateCode string Code of the target state of the object + * @return hash Two dimensional array: each element represents the list of fields for a given page + */ + protected function ComputeWizardStructure() + { + $aWizardSteps = array( 'mandatory' => array(), 'optional' => array()); + $aFieldsDone = array(); // Store all the fields that are already covered by a previous step of the wizard + + $aStates = MetaModel::EnumStates($this->m_sClass); + + if ( (!empty($this->m_sTargetState)) && (count($aStates[$this->m_sTargetState]['attribute_list']) > 0) ) + { + // Check all the fields that *must* be included in the wizard for this + // particular target state + $aFields = array(); + foreach($aStates[$this->m_sTargetState]['attribute_list'] as $sAttCode => $iOptions) + { + $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode); + $sAttLabel = $oAttDef->GetLabel(); + + if ($iOptions & (OPT_ATT_MANDATORY | OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT)) + { + $aPrerequisites = $oAttDef->GetPrerequisiteAttributes(); + $aFields[$sAttCode] = array(); + foreach($aPrerequisites as $sCode) + { + $aFields[$sAttCode][$sCode] = ''; + } + } + } + + // Now use the dependencies between the fields to order them + while(count($aFields) > 0) + { + $aCurrentStep = array(); + foreach($aFields as $sAttCode => $aDependencies) + { + // All fields with no remaining dependencies can be entered at this + // step of the wizard + if (count($aDependencies) == 0) + { + $aCurrentStep[] = $sAttCode; + $aFieldsDone[$sAttCode] = ''; + unset($aFields[$sAttCode]); + // Remove this field from the dependencies of the other fields + foreach($aFields as $sUpdatedCode => $aDummy) + { + // remove the dependency + unset($aFields[$sUpdatedCode][$sAttCode]); + } + } + } + if (count($aCurrentStep) == 0) + { + // This step of the wizard would contain NO field ! + echo "Error: Circular reference in the dependencies between the fields."; + print_r($aFields); + break; + } + $aWizardSteps['mandatory'][] = $aCurrentStep; + } + } + + // Now computes the steps to fill the optional fields + $sStateAttCode = MetaModel::GetStateAttributeCode($this->m_sClass); + $aFields = array(); // reset + foreach(MetaModel::ListAttributeDefs($this->m_sClass) as $sAttCode=>$oAttDef) + { + $iOptions = (isset($aStates[$this->m_sTargetState]['attribute_list'][$sAttCode])) ? $aStates[$this->m_sTargetState]['attribute_list'][$sAttCode] : 0; + if ( ($sStateAttCode != $sAttCode) && + (!$oAttDef->IsExternalField()) && + (($iOptions & (OPT_ATT_HIDDEN | OPT_ATT_READONLY)) == 0) && + (!isset($aFieldsDone[$sAttCode])) ) + + { + // 'State', external fields, read-only and hidden fields + // and fields that are already listed in the wizard + // are removed from the 'optional' part of the wizard + $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode); + $aPrerequisites = $oAttDef->GetPrerequisiteAttributes(); + $aFields[$sAttCode] = array(); + foreach($aPrerequisites as $sCode) + { + if (!isset($aFieldsDone[$sCode])) + { + // retain only the dependencies that were not covered + // in the 'mandatory' part of the wizard + $aFields[$sAttCode][$sCode] = ''; + } + } + } + } + // Now use the dependencies between the fields to order them + while(count($aFields) > 0) + { + $aCurrentStep = array(); + foreach($aFields as $sAttCode => $aDependencies) + { + // All fields with no remaining dependencies can be entered at this + // step of the wizard + if (count($aDependencies) == 0) + { + $aCurrentStep[] = $sAttCode; + $aFieldsDone[$sAttCode] = ''; + unset($aFields[$sAttCode]); + // Remove this field from the dependencies of the other fields + foreach($aFields as $sUpdatedCode => $aDummy) + { + // remove the dependency + unset($aFields[$sUpdatedCode][$sAttCode]); + } + } + } + if (count($aCurrentStep) == 0) + { + // This step of the wizard would contain NO field ! + $oPage->add("Error: Circular reference in the dependencies between the fields."); + print_r($aFields); + break; + } + $aWizardSteps['optional'][] = $aCurrentStep; + } + + return $aWizardSteps; + + } +} +?> diff --git a/application/usercontext.class.inc.php b/application/usercontext.class.inc.php new file mode 100644 index 000000000..21451feee --- /dev/null +++ b/application/usercontext.class.inc.php @@ -0,0 +1,106 @@ +AddCondition('SomeClass', 'someFilter', 'SomeValue', '='); + * ... + * + * 2) Use the restrictions contained in the context when retrieving objects either when: + * getting directly an instance of an object + * $oObj = $oContext->GetObject('myClass', 'someKey'); // Instead of $oObj = MetaModel::GetObject('Klass', 'someKey'); + * or when building a new search filter + * $oFilter = $oContext->NewFilter('myClass'); // Instead of $oFilter = new CMDBSearchFilter('Klass'); + */ +class UserContext +{ + /** + * Hash array to store the restricting conditions by myClass + */ + protected $m_aConditions; + + /** + * Constructor + */ + public function __construct() + { + $this->m_aConditions = array(); + } + /** + * Create a new search filter for the given class of objects that already contains the context's restrictions + */ + public function NewFilter($sClass) + { + return UserRights::GetFilter($sClass); + /* + $oFilter = new CMDBSearchFilter($sClass); + foreach($this->m_aConditions as $sConditionClass => $aConditionList) + { + // Add to the filter all the conditions of the parent classes of this class + if ($this->IsSubclass($sConditionClass,$sClass)) + { + foreach($aConditionList as $sFilterCode => $aCondition) + { + $oFilter->AddCondition($sFilterCode, $aCondition['value'], $aCondition['operator']); + } + } + } + return $oFilter; + */ + } + /** + * Retrieve an instance of an object (if allowed by the context) + */ + public function GetObject($sClass, $sKey) + { + $oObject = null; + $oFilter = $this->NewFilter($sClass); + $oFilter->AddCondition('pkey', $sKey, '='); + $oSet = new CMDBObjectSet($oFilter); + if ($oSet->Count() > 0) + { + $oObject = $oSet->Fetch(); + } + return $oObject; + } + + /** + * Add a restriction to the context for a given class of objects (and all its persistent subclasses) + */ + public function AddCondition($sClass, $sFilterCode, $value, $sOperator) + { + if(!isset($this->m_aConditions[$sClass])) + { + $this->m_aConditions[$sClass] = array(); + } + $this->m_aConditions[$sClass][$sFilterCode] = array('value'=>$value, 'operator'=>$sOperator); + } + + /** + * Check if a given class is a subclass of (or same as) another one + */ + protected function IsSubclass($sParentClass, $sSubclass) + { + $bResult = false; + if ($sParentClass == $sSubclass) + { + $bResult = true; + } + else + { + $aParentList = MetaModel::EnumParentClasses($sSubclass); + $bResult = in_array($sParentClass, $aParentList); + } + return $bResult; + } +} +?> diff --git a/application/utils.inc.php b/application/utils.inc.php new file mode 100644 index 000000000..00aa4c8d3 --- /dev/null +++ b/application/utils.inc.php @@ -0,0 +1,80 @@ + diff --git a/application/webpage.class.inc.php b/application/webpage.class.inc.php new file mode 100644 index 000000000..077586983 --- /dev/null +++ b/application/webpage.class.inc.php @@ -0,0 +1,289 @@ +p("Hello World !"); + * $oPage->output(); + */ +class web_page +{ + protected $s_title; + protected $s_content; + protected $a_scripts; + protected $a_styles; + protected $a_include_scripts; + protected $a_include_stylesheets; + protected $a_headers; + + public function __construct($s_title) + { + $this->s_title = $s_title; + $this->s_content = ""; + $this->a_scripts = array(); + $this->a_styles = array(); + $this->a_linked_scripts = array(); + $this->a_linked_stylesheets = array(); + $this->a_headers = array(); + ob_start(); // Start capturing the output + } + + /** + * Change the title of the page after its creation + */ + public function set_title($s_title) + { + $this->s_title = $s_title; + } + + /** + * Add any text or HTML fragment to the body of the page + */ + public function add($s_html) + { + $this->s_content .= $s_html; + } + + /** + * Add a paragraph to the body of the page + */ + public function p($s_html) + { + $this->add($this->GetP($s_html)); + } + + /** + * Add a paragraph to the body of the page + */ + public function GetP($s_html) + { + return "

    $s_html

    \n"; + } + + public function table($aConfig, $aData, $aParams = array()) + { + $this->add($this->GetTable($aConfig, $aData, $aParams)); + } + + public function GetTable($aConfig, $aData, $aParams = array()) + { + $oAppContext = new ApplicationContext(); + + static $iNbTables = 0; + $iNbTables++; + $sHtml = ""; + $sHtml .= "\n"; + $sHtml .= "\n"; + $sHtml .= "\n"; + foreach($aConfig as $sName=>$aDef) + { + $sHtml .= "\n"; + } + $sHtml .= "\n"; + $sHtml .= "\n"; + $sHtml .= "\n"; + foreach($aData as $aRow) + { + if (false) //(isset($aParams['preview']) && $aParams['preview']) + { + $sHtml .= "\n"; + } + else if (isset($aRow['key'])) + { + $sHtml .= "\n"; + } + else + { + $sHtml .= "\n"; + } + foreach($aConfig as $sName=>$aAttribs) + { + $sClass = isset($aAttribs['class']) ? 'class="'.$aAttribs['class'].'"' : ''; + if ($sName != 'key') + { + $sValue = ($aRow[$sName] === '') ? ' ' : $aRow[$sName]; + $sHtml .= "\n"; + } + else + { + $sUIPage = cmdbAbstractObject::ComputeUIPage($aParams['class']); + $sHtml .= "\n"; + } + } + $sHtml .= "\n"; + } + $sHtml .= "\n"; + $sHtml .= "
    ".$aDef['label']."
    $sValueGetForLink()."\">
    \n"; + return $sHtml; + } + + /** + * Add some Javascript to the header of the page + */ + public function add_script($s_script) + { + $this->a_scripts[] = $s_script; + } + + /** + * Add some Javascript to the header of the page + */ + public function add_ready_script($s_script) + { + // Do nothing silently... this is not supported by this type of page... + } + /** + * Add some CSS definitions to the header of the page + */ + public function add_style($s_style) + { + $this->a_styles[] = $s_style; + } + + /** + * Add a script (as an include, i.e. link) to the header of the page + */ + public function add_linked_script($s_linked_script) + { + $this->a_linked_scripts[] = $s_linked_script; + } + + /** + * Add a CSS stylesheet (as an include, i.e. link) to the header of the page + */ + public function add_linked_stylesheet($s_linked_stylesheet, $s_condition = "") + { + $this->a_linked_stylesheets[] = array( 'link' => $s_linked_stylesheet, 'condition' => $s_condition); + } + + /** + * Add some custom header to the page + */ + public function add_header($s_header) + { + $this->a_headers[] = $s_header; + } + + /** + * Add needed headers to the page so that it will no be cached + */ + public function no_cache() + { + $this->add_header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1 + $this->add_header("Expires: Fri, 17 Jul 1970 05:00:00 GMT"); // Date in the past + } + + /** + * Build a special kind of TABLE useful for displaying the details of an object from a hash array of data + */ + public function details($aFields) + { + + $this->add($this->GetDetails($aFields)); + } + + /** + * Build a special kind of TABLE useful for displaying the details of an object from a hash array of data + */ + public function GetDetails($aFields) + { + $sHtml = "\n"; + $sHtml .= "\n"; + foreach($aFields as $aAttrib) + { + $sHtml .= "\n"; + // By Rom, for csv import, proposed to show several values for column selection + if (is_array($aAttrib['value'])) + { + $sHtml .= "\n"; + } + else + { + $sHtml .= "\n"; + } + $sHtml .= "\n"; + } + $sHtml .= "\n"; + $sHtml .= "
    ".$aAttrib['label']."".implode("", $aAttrib['value'])."".$aAttrib['label']."".$aAttrib['value']."
    \n"; + return $sHtml; + } + + /** + * Outputs (via some echo) the complete HTML page by assembling all its elements + */ + public function output() + { + foreach($this->a_headers as $s_header) + { + header($s_header); + } + $s_captured_output = ob_get_contents(); + ob_end_clean(); + echo "\n"; + echo "\n"; + echo "\n"; + echo "{$this->s_title}\n"; + foreach($this->a_linked_scripts as $s_script) + { + echo "\n"; + } + if (count($this->a_scripts)>0) + { + echo "\n"; + } + foreach($this->a_linked_stylesheets as $a_stylesheet) + { + if ($a_stylesheet['condition'] != "") + { + echo "\n"; + } + } + + if (count($this->a_styles)>0) + { + echo "\n"; + } + echo "\n"; + echo "\n"; + echo $this->s_content; + if (trim($s_captured_output) != "") + { + echo "
    $s_captured_output
    \n"; + } + echo "\n"; + echo "\n"; + } + + /** + * Build a series of hidden field[s] from an array + */ + // By Rom - je verrais bien une serie d'outils pour gerer des parametres que l'on retransmet entre pages d'un wizard... + // ptet deriver webpage en webwizard + public function add_input_hidden($sLabel, $aData) + { + foreach($aData as $sKey=>$sValue) + { + $this->add(""); + } + } +} +?> diff --git a/application/wizardhelper.class.inc.php b/application/wizardhelper.class.inc.php new file mode 100644 index 000000000..3ab7b5c1c --- /dev/null +++ b/application/wizardhelper.class.inc.php @@ -0,0 +1,204 @@ +m_aData['m_sClass']); + foreach($this->m_aData['m_aCurrentValues'] as $iIndex => $value) + { + $sAttCode = array_search($iIndex, $this->m_aData['m_oFieldsMap']); + // Because this is stored in a Javascript array, unused indexes + // are filled with null values + if ( ($sAttCode !== false) && ($value !== null)) + { + $oAttDef = MetaModel::GetAttributeDef($this->m_aData['m_sClass'], $sAttCode); + if (($oAttDef->IsLinkSet()) && ($value != '') ) + { + // special handling for lists + // assumes this is handled as an array of objects + // thus encoded in json like: [ { name:'link1', 'id': 123}, { name:'link2', 'id': 124}...] + $aData = json_decode($value, true); // true means decode as a hash array (not an object) + // Check what are the meaningful attributes + $aFields = $this->GetLinkedWizardStructure($oAttDef); + $sLinkedClass = $oAttDef->GetLinkedClass(); + $aLinkedObjectsArray = array(); + if (!is_array($aData)) + { + echo ("aData: '$aData' (value: '$value')\n"); + } + foreach($aData as $aLinkedObject) + { + $oLinkedObj = MetaModel::NewObject($sLinkedClass); + foreach($aFields as $sLinkedAttCode) + { + if ( isset($aLinkedObject[$sLinkedAttCode]) && ($aLinkedObject[$sLinkedAttCode] !== null) ) + { + $sLinkedAttDef = MetaModel::GetAttributeDef($sLinkedClass, $sLinkedAttCode); + if (($sLinkedAttDef->IsExternalKey()) && ($aLinkedObject[$sLinkedAttCode] != '') ) + { + // For external keys: load the target object so that external fields + // get filled too + $oTargetObj = MetaModel::GetObject($sLinkedAttDef->GetTargetClass(), $aLinkedObject[$sLinkedAttCode]); + $oLinkedObj->Set($sLinkedAttCode, $oTargetObj); + } + else + { + $oLinkedObj->Set($sLinkedAttCode, $aLinkedObject[$sLinkedAttCode]); + } + } + } + $aLinkedObjectsArray[] = $oLinkedObj; + } + $oSet = DBObjectSet::FromArray($sLinkedClass, $aLinkedObjectsArray); + $oObj->Set($sAttCode, $oSet); + } + else if (($oAttDef->IsExternalKey()) && ($value != '') ) + { + // For external keys: load the target object so that external fields + // get filled too + $oTargetObj = MetaModel::GetObject($oAttDef->GetTargetClass(), $value); + $oObj->Set($sAttCode, $oTargetObj); + } + else + { + $oObj->Set($sAttCode, $value); + } + } + } + return $oObj; + } + + public function GetFieldsForDefaultValue() + { + return $this->m_aData['m_aDefaultValueRequested']; + } + + public function SetDefaultValue($sAttCode, $value) + { + // Protect against a request for a non existing field + if (isset($this->m_aData['m_oFieldsMap'][$sAttCode])) + { + $iIndex = $this->m_aData['m_oFieldsMap'][$sAttCode]; + $oAttDef = MetaModel::GetAttributeDef($this->m_aData['m_sClass'], $sAttCode); + if ($oAttDef->GetEditClass() == 'List') + { + // special handling for lists + // this as to be handled as an array of objects + // thus encoded in json like: [ { name:'link1', 'id': 123}, { name:'link2', 'id': 124}...] + // NOT YET IMPLEMENTED !! + $sLinkedClass = $oAttDef->GetLinkedClass(); + $oSet = $value; + $aData = array(); + $aFields = $this->GetLinkedWizardStructure($oAttDef); + while($oSet->fetch()) + { + foreach($aFields as $sLinkedAttCode) + { + $aRow[$sAttCode] = $oLinkedObj->Get($sLinkedAttCode); + } + $aData[] = $aRow; + } + $this->m_aData['m_aDefaultValue'][$iIndex] = json_encode($aData); + + } + else + { + // Normal handling for all other scalar attributes + $this->m_aData['m_aDefaultValue'][$iIndex] = $value; + } + } + } + + public function GetFieldsForAllowedValues() + { + return $this->m_aData['m_aAllowedValuesRequested']; + } + + public function SetAllowedValuesHtml($sAttCode, $sHtml) + { + // Protect against a request for a non existing field + if (isset($this->m_aData['m_oFieldsMap'][$sAttCode])) + { + $iIndex = $this->m_aData['m_oFieldsMap'][$sAttCode]; + $this->m_aData['m_aAllowedValues'][$iIndex] = $sHtml; + } + } + + public function ToJSON() + { + return json_encode($this->m_aData); + } + + static public function FromJSON($sJSON) + { + $oWizHelper = new WizardHelper(); + if (get_magic_quotes_gpc()) + { + $sJSON = stripslashes($sJSON); + } + $aData = json_decode($sJSON, true); // true means hash array instead of object + $oWizHelper->m_aData = $aData; + return $oWizHelper; + } + + protected function GetLinkedWizardStructure($oAttDef) + { + $oWizard = new UIWizard(null, $oAttDef->GetLinkedClass()); + $aWizardSteps = $oWizard->GetWizardStructure(); + $aFields = array(); + $sExtKeyToMeCode = $oAttDef->GetExtKeyToMe(); + // Retrieve as a flat list, all the attributes that are needed to create + // an object of the linked class and put them into a flat array, except + // the attribute 'ext_key_to_me' which is a constant in our case + foreach($aWizardSteps as $sDummy => $aMainSteps) + { + // 2 entries: 'mandatory' and 'optional' + foreach($aMainSteps as $aSteps) + { + // One entry for each step of the wizard + foreach($aSteps as $sAttCode) + { + if ($sAttCode != $sExtKeyToMeCode) + { + $aFields[] = $sAttCode; + } + } + } + } + return $aFields; + } + + static function ParseJsonSet($oMe, $sLinkClass, $sExtKeyToMe, $sJsonSet) + { + $aSet = json_decode($sJsonSet, true); // true means hash array instead of object + $oSet = CMDBObjectSet::FromScratch($sLinkClass); + foreach($aSet as $aLinkObj) + { + $oLink = MetaModel::NewObject($sLinkClass); + foreach($aLinkObj as $sAttCode => $value) + { + $oAttDef = MetaModel::GetAttributeDef($sLinkClass, $sAttCode); + if (($oAttDef->IsExternalKey()) && ($value != '') ) + { + // For external keys: load the target object so that external fields + // get filled too + $oTargetObj = MetaModel::GetObject($oAttDef->GetTargetClass(), $value); + $oLink->Set($sAttCode, $oTargetObj); + } + $oLink->Set($sAttCode, $value); + } + $oLink->Set($sExtKeyToMe, $oMe->GetKey()); + $oSet->AddObject($oLink); + } + return $oSet; + } +} +?> diff --git a/application/xmlpage.class.inc.php b/application/xmlpage.class.inc.php new file mode 100644 index 000000000..d3a62adfe --- /dev/null +++ b/application/xmlpage.class.inc.php @@ -0,0 +1,36 @@ +add_header("Content-type: text/xml; charset=utf-8"); + $this->add_header("Cache-control: no-cache"); + $this->add_header("Content-location: export.xml"); + $this->add("\n"); + } + + public function output() + { + $this->add_header("Content-Length: ".strlen(trim($this->s_content))); + foreach($this->a_headers as $s_header) + { + header($s_header); + } + echo trim($this->s_content); + } + + public function small_p($sText) + { + } + + public function table($aConfig, $aData, $aParams = array()) + { + } +} +?> diff --git a/business/ChangeMgmt.php b/business/ChangeMgmt.php new file mode 100644 index 000000000..891f42e28 --- /dev/null +++ b/business/ChangeMgmt.php @@ -0,0 +1,284 @@ + "bizmodel,searchable", + "name" => "Change", + "description" => "Change ticket", + "key_type" => "autoincrement", + "key_label" => "id", + "name_attcode" => "name", + "state_attcode" => "ticket_status", + "reconc_keys" => array("title"), + "db_table" => "change_ticket", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/change.html", + ); + MetaModel::Init_Params($aParams); + //MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeString("name", array("label"=>"TicketID", "description"=>"Refence number ofr this change", "allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("title", array("label"=>"Title", "description"=>"Overview of the Change", "allowed_values"=>null, "sql"=>"title", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("type", array("label"=>"Change Type", "description"=>"Type of the Change", "allowed_values"=>new ValueSetEnum("Routine, Normal, Emergency"), "sql"=>"type", "default_value"=>"Routine", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("domain", array("label"=>"Domain", "description"=>"Domain for the Change", "allowed_values"=>new ValueSetEnum("Network,Server,Desktop,Application"), "sql"=>"domain", "default_value"=>"Desktop", "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_AddAttribute(new AttributeString("reason", array("label"=>"Reason for change", "description"=>"Reason for the Change", "allowed_values"=>null, "sql"=>"reason", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_AddAttribute(new AttributeExternalKey("requestor_id", array("targetclass"=>"bizPerson", "jointype"=> "", "label"=>"Requestor", "description"=>"who is requesting this change", "allowed_values"=>null, "sql"=>"requestor_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("requestor_mail", array("label"=>"Requested by", "description"=>"mail of user requesting this change", "allowed_values"=>null, "extkey_attcode"=> 'requestor_id', "target_attcode"=>"email"))); + + + MetaModel::Init_AddAttribute(new AttributeExternalKey("customer_id", array("targetclass"=>"bizOrganization", "label"=>"Customer", "description"=>"who is impacted by the ticket", "allowed_values"=>null, "sql"=>"customer", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("customer_name", array("label"=>"Customer", "description"=>"Name of the customer impacted by this ticket", "allowed_values"=>null, "extkey_attcode"=> 'customer_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeEnum("ticket_status", array("label"=>"Status", "description"=>"Status of the ticket", "allowed_values"=>new ValueSetEnum("New, Validated,Rejected,PlannedScheduled,Approved,NotApproved,Implemented,Monitored, Closed"), "sql"=>"change_status", "default_value"=>"New", "is_null_allowed"=>false, "depends_on"=>array()))); + // SetPossibleValues("status",array("Open","Monitored","Closed")); + + MetaModel::Init_AddAttribute(new AttributeDate("creation_date", array("label"=>"Creation date", "description"=>"Change creation date", "allowed_values"=>null, "sql"=>"creation_date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + // définir une date de défaut à maintenant, alias creation ou modification du ticket + MetaModel::Init_AddAttribute(new AttributeDate("last_update", array("label"=>"Last update", "description"=>"last time the Ticket was modified", "allowed_values"=>null, "sql"=>"last_update", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeDate("start_date", array("label"=>"Start date", "description"=>"Time the change is expected to start", "allowed_values"=>null, "sql"=>"start_date", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeDate("end_date", array("label"=>"End Date", "description"=>"Date when the change is supposed to end", "allowed_values"=>null, "sql"=>"end_date", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeDate("close_date", array("label"=>"Closed Date", "description"=>"Date when the Ticket was closed", "allowed_values"=>null, "sql"=>"closed_date", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("impact", array("label"=>"Risk Assessment", "description"=>"Impact of the change", "allowed_values"=>null, "sql"=>"impact", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("workgroup_id", array("targetclass"=>"bizWorkgroup", "jointype"=> "", "label"=>"Workgroup", "description"=>"which workgroup is owning ticket", "allowed_values"=>null, "sql"=>"workgroup_id", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("workgroup_name", array("label"=>"Managed by Workgroup", "description"=>"name of workgroup managing the Ticket", "allowed_values"=>null, "extkey_attcode"=> 'workgroup_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("agent_id", array("targetclass"=>"bizPerson", "jointype"=> "", "label"=>"Agent", "description"=>"who is managing the ticket", "allowed_values"=>null, "sql"=>"agent_id", "is_null_allowed"=>true, "depends_on"=>array('workgroup_id')))); + MetaModel::Init_AddAttribute(new AttributeExternalField("agent_name", array("label"=>"Managed by Agent", "description"=>"name of agent managing the Ticket", "allowed_values"=>null, "extkey_attcode"=> 'agent_id', "target_attcode"=>"name"))); + + MetaModel::Init_AddAttribute(new AttributeExternalKey("supervisorgroup_id", array("targetclass"=>"bizWorkgroup", "jointype"=> "", "label"=>"Supervisor group", "description"=>"which workgroup is supervising ticket", "allowed_values"=>null, "sql"=>"supervisorgroup_id", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("supervisorgroup_name", array("label"=>"Supervise by Workgroup", "description"=>"name of the group supervising the Ticket", "allowed_values"=>null, "extkey_attcode"=> 'supervisorgroup_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("supervisor_id", array("targetclass"=>"bizPerson", "jointype"=> "", "label"=>"Supervisor", "description"=>"who is managing the ticket", "allowed_values"=>null, "sql"=>"supervisor_id", "is_null_allowed"=>true, "depends_on"=>array('supervisorgroup_id')))); + MetaModel::Init_AddAttribute(new AttributeExternalField("supervisor_name", array("label"=>"Managed by Supervisor", "description"=>"name of agent supervising the Ticket", "allowed_values"=>null, "extkey_attcode"=> 'supervisor_id', "target_attcode"=>"name"))); + + MetaModel::Init_AddAttribute(new AttributeExternalKey("managergroup_id", array("targetclass"=>"bizWorkgroup", "jointype"=> "", "label"=>"Manager group", "description"=>"which workgroup is approving ticket", "allowed_values"=>null, "sql"=>"managergroup_id", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("managergroup_name", array("label"=>"Approved by group", "description"=>"name of workgroup approving the Ticket", "allowed_values"=>null, "extkey_attcode"=> 'managergroup_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("manager_id", array("targetclass"=>"bizPerson", "jointype"=> "", "label"=>"Manager", "description"=>"who is approving the ticket", "allowed_values"=>null, "sql"=>"manager_id", "is_null_allowed"=>true, "depends_on"=>array('managergroup_id')))); + MetaModel::Init_AddAttribute(new AttributeExternalField("manager_name", array("label"=>"Approved by Agent", "description"=>"name of agent approving the Ticket", "allowed_values"=>null, "extkey_attcode"=> 'manager_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeEnum("outage", array("label"=>"Planned Outage", "description"=>"Flag to define if there is a planned outage", "allowed_values"=>new ValueSetEnum("Yes,No"), "sql"=>"outage", "default_value"=>"No", "is_null_allowed"=>false, "depends_on"=>array()))); + + + MetaModel::Init_AddAttribute(new AttributeText("change_request", array("label"=>"Change Request", "description"=>"Description of Change required", "allowed_values"=>null, "sql"=>"change_req", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeText("change_log", array("label"=>"Implementation log", "description"=>"List all action performed during the change", "allowed_values"=>null, "sql"=>"change_log", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeText("fallback", array("label"=>"Fallback plan", "description"=>"Instruction to come back to former situation", "allowed_values"=>null, "sql"=>"fallback", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeInteger("assignment_count", array("label"=>"Assignment Count", "description"=>"Number of times this ticket was assigned or reassigned", "allowed_values"=>null, "sql"=>"assignment_count", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("impacted_infra_manual", array("label"=>"Impacted Infrastructure", "description"=>"CIs that are impacted by this change", "linked_class"=>"lnkInfraChangeTicket", "ext_key_to_me"=>"ticket_id", "ext_key_to_remote"=>"infra_id", "allowed_values"=>null, "count_min"=>1, "count_max"=>0, "depends_on"=>array()))); + + //MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("name"); + MetaModel::Init_AddFilterFromAttribute("title"); + MetaModel::Init_AddFilterFromAttribute("type"); + MetaModel::Init_AddFilterFromAttribute("domain"); + MetaModel::Init_AddFilterFromAttribute("customer_id"); + MetaModel::Init_AddFilterFromAttribute("requestor_id"); + MetaModel::Init_AddFilterFromAttribute("ticket_status"); + MetaModel::Init_AddFilterFromAttribute("creation_date"); + MetaModel::Init_AddFilterFromAttribute("start_date"); + MetaModel::Init_AddFilterFromAttribute("last_update"); + MetaModel::Init_AddFilterFromAttribute("end_date"); + MetaModel::Init_AddFilterFromAttribute("close_date"); + MetaModel::Init_AddFilterFromAttribute("workgroup_id"); + MetaModel::Init_AddFilterFromAttribute("workgroup_name"); + MetaModel::Init_AddFilterFromAttribute("supervisorgroup_id"); + MetaModel::Init_AddFilterFromAttribute("managergroup_id"); + MetaModel::Init_AddFilterFromAttribute("supervisor_id"); + MetaModel::Init_AddFilterFromAttribute("manager_id"); + MetaModel::Init_AddFilterFromAttribute("agent_id"); + MetaModel::Init_AddFilterFromAttribute("impact"); + MetaModel::Init_AddFilterFromAttribute("assignment_count"); + MetaModel::Init_AddFilterFromAttribute("outage"); + + // doit-on aussi ajouter un filtre sur les extfields lié à une extkey ? ici le name de l'agent? + + // Display lists + MetaModel::Init_SetZListItems('details', array('name','title', 'customer_id','type','domain','requestor_id','change_request','ticket_status', 'outage','impact', 'last_update', 'start_date','end_date', 'assignment_count', 'workgroup_id','agent_id','supervisorgroup_id','supervisor_id','managergroup_id','manager_id','change_log','fallback')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('list', array('name', 'title', 'customer_id', 'ticket_status','outage','start_date','type')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('name', 'title', 'customer_id', 'ticket_status','type', 'outage','requestor_id','workgroup_id','agent_id')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('name', 'title', 'customer_id', 'ticket_status','type', 'outage','workgroup_id','agent_id')); // Criteria of the advanced search form + + // State machine + MetaModel::Init_DefineState("New", array("label"=>"New (Unassigned)", "description"=>"Newly created ticket", "attribute_inherit"=>null, + "attribute_list"=>array('name' => OPT_ATT_READONLY,'customer_id' => OPT_ATT_MANDATORY, 'title' => OPT_ATT_MANDATORY, 'reason' => OPT_ATT_MANDATORY, 'impacted_infra_manual' => OPT_ATT_MANDATORY, + 'assignment_count' => OPT_ATT_HIDDEN, 'end_date' => OPT_ATT_HIDDEN))); + MetaModel::Init_DefineState("Validated", array("label"=>"Validated", "description"=>"Ticket is approved", "attribute_inherit"=>null, + "attribute_list"=>array('name' => OPT_ATT_READONLY, 'customer_id' => OPT_ATT_READONLY,'assignment_count' => OPT_ATT_READONLY,'managergroup_id' => OPT_ATT_MANDATORY, 'supervisorgroup_id' => OPT_ATT_MANDATORY))); + MetaModel::Init_DefineState("Rejected", array("label"=>"Rejected", "description"=>"This ticket is not approved", "attribute_inherit"=>null, + "attribute_list"=>array('name' => OPT_ATT_READONLY, 'customer_id' => OPT_ATT_READONLY,'assignment_count' => OPT_ATT_HIDDEN, 'end_date' => OPT_ATT_HIDDEN))); + MetaModel::Init_DefineState("PlannedScheduled", array("label"=>"Planned&Scheduled", "description"=>"Evaluation is done for this change", "attribute_inherit"=>null, + "attribute_list"=>array('name' => OPT_ATT_READONLY, 'customer_id' => OPT_ATT_READONLY,'assignment_count' => OPT_ATT_HIDDEN, 'end_date' => OPT_ATT_MANDATORY, 'impact' => OPT_ATT_MANDATORY, 'workgroup_id' => OPT_ATT_MANDATORY, 'change_log' => OPT_ATT_MUSTCHANGE,'fallback' => OPT_ATT_MANDATORY))); + MetaModel::Init_DefineState("Approved", array("label"=>"Approved", "description"=>"Ticket is approved by CAB", "attribute_inherit"=>null, + "attribute_list"=>array('name' => OPT_ATT_READONLY,'customer_id' => OPT_ATT_READONLY, 'assignment_count' => OPT_ATT_HIDDEN, 'end_date' => OPT_ATT_HIDDEN))); + MetaModel::Init_DefineState("NotApproved", array("label"=>"Not Approved", "description"=>"Ticket has not been approved by CAB", "attribute_inherit"=>null, + "attribute_list"=>array('name' => OPT_ATT_READONLY,'customer_id' => OPT_ATT_READONLY, 'assignment_count' => OPT_ATT_HIDDEN, 'end_date' => OPT_ATT_HIDDEN))); + MetaModel::Init_DefineState("Implemented", array("label"=>"Implementation", "description"=>"Work is in progress for this ticket", "attribute_inherit"=>null, + "attribute_list"=>array('name' => OPT_ATT_READONLY,'customer_id' => OPT_ATT_READONLY, 'assignment_count' => OPT_ATT_HIDDEN, 'end_date' => OPT_ATT_HIDDEN))); + MetaModel::Init_DefineState("Monitored", array("label"=>"Monitored", "description"=>"Change performed is now monitored", "attribute_inherit"=>null, + "attribute_list"=>array('name' => OPT_ATT_READONLY,'customer_id' => OPT_ATT_READONLY,'assignment_count' => OPT_ATT_HIDDEN, 'end_date' => OPT_ATT_HIDDEN))); + MetaModel::Init_DefineState("Closed", array("label"=>"Closed", "description"=>"Ticket is closed", "attribute_inherit"=>null, "attribute_list"=>array('customer_id' => OPT_ATT_READONLY,"workgroup_id"=>OPT_ATT_MANDATORY, "agent_id"=>OPT_ATT_MANDATORY))); + + MetaModel::Init_DefineStimulus("ev_validate", new StimulusUserAction(array("label"=>"Validate this change", "description"=>"Make sure it is a valid change request"))); + MetaModel::Init_DefineStimulus("ev_reject", new StimulusUserAction(array("label"=>"Reject this change", "description"=>"This change request is rejected because it is a non valid one"))); + MetaModel::Init_DefineStimulus("ev_reopen", new StimulusUserAction(array("label"=>"Modify this change", "description"=>"Update change request to make it valid"))); + MetaModel::Init_DefineStimulus("ev_plan", new StimulusUserAction(array("label"=>"Plan this change", "description"=>"Plan and Schedule this change for validation"))); + MetaModel::Init_DefineStimulus("ev_approve", new StimulusUserAction(array("label"=>"Approve this change", "description"=>"This change is approved by CAB"))); + MetaModel::Init_DefineStimulus("ev_replan", new StimulusUserAction(array("label"=>"Update planning and schedule", "description"=>"Modify Plan and Schedule in order to have this change re-validated"))); + MetaModel::Init_DefineStimulus("ev_notapprove", new StimulusUserAction(array("label"=>"Not approve this change", "description"=>"This change is not approved by CAB"))); + MetaModel::Init_DefineStimulus("ev_implement", new StimulusUserAction(array("label"=>"Implement this change", "description"=>"Implementation pahse for current change"))); + MetaModel::Init_DefineStimulus("ev_monitor", new StimulusUserAction(array("label"=>"Monitor this change", "description"=>"Starting monitoring period for this change"))); + MetaModel::Init_DefineStimulus("ev_finish", new StimulusUserAction(array("label"=>"Close change", "description"=>"Change is done, and can be closed"))); + + MetaModel::Init_DefineTransition("New", "ev_validate", array("target_state"=>"Validated", "actions"=>array('SetLastUpDate'), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("New", "ev_reject", array("target_state"=>"Rejected", "actions"=>array('SetLastUpDate'), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Rejected", "ev_reopen", array("target_state"=>"New", "actions"=>array('SetLastUpDate'), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Validated", "ev_plan", array("target_state"=>"PlannedScheduled", "actions"=>array('SetLastUpDate'), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("PlannedScheduled", "ev_approve", array("target_state"=>"Approved", "actions"=>array('SetLastUpDate'), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("PlannedScheduled", "ev_notapprove", array("target_state"=>"NotApproved", "actions"=>array('SetLastUpDate'), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("NotApproved", "ev_replan", array("target_state"=>"PlannedScheduled", "actions"=>array('SetLastUpDate'), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Approved", "ev_implement", array("target_state"=>"Implemented", "actions"=>array('SetLastUpDate'), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Implemented", "ev_monitor", array("target_state"=>"Monitored", "actions"=>array('SetLastUpDate'), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Implemented", "ev_finish", array("target_state"=>"Closed", "actions"=>array('SetLastUpDate'), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Monitored", "ev_finish", array("target_state"=>"Closed", "actions"=>array(), "user_restriction"=>null)); + } + + public function Generate(cmdbDataGenerator $oGenerator) + { + $this->Set('customer_id', $oGenerator->GetOrganizationId()); + $this->Set('title', $oGenerator->GenerateString("enum(Site,Server,Line)| |enum(is down,is flip-flopping,is not responding)")); + $this->Set('agent_id', $oGenerator->GenerateKey("bizPerson", array('org_id' =>$oGenerator->GetOrganizationId() ))); + $this->Set('ticket_status', $oGenerator->GenerateString("enum(Open,Closed,Closed,Monitored)")); + $this->Set('start_date', $oGenerator->GenerateString("2007-|number(07-12)|-|number(01-30)| |number(07-12)|:|number(00-59)|:|number(00-59)")); + $this->Set('last_update', $oGenerator->GenerateString("2007-|number(07-12)|-|number(01-30)| |number(07-12)|:|number(00-59)|:|number(00-59)")); + $this->Set('end_date', $oGenerator->GenerateString("2007-|number(07-12)|-|number(01-30)| |number(07-12)|:|number(00-59)|:|number(00-59)")); + } + + + + // State machine actions + public function IncrementAssignmentCount($sStimulusCode) + { + $this->Set('assignment_count', $this->Get('assignment_count') + 1); + return true; + } + + public function SetClosureDate($sStimulusCode) + { + $this->Set('end_date', time()); + return true; + } + public function SetLastUpDate($sStimulusCode) + { + $this->Set('last_update', time()); + return true; + } +} + +//////////////////////////////////////////////////////////////////////////////////// +/** +* n-n link between any Infra and a Change Ticket +*/ +//////////////////////////////////////////////////////////////////////////////////// +class lnkInfraChangeTicket extends cmdbAbstractObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "Infra Change Ticket", + "description" => "Infra impacted by a Change ticket", + "key_type" => "autoincrement", + "key_label" => "link_id", + "name_attcode" => "impact", // ???? + "state_attcode" => "", + "reconc_keys" => array("impact"), // ???? + "db_table" => "infra_changeticket", + "db_key_field" => "link_id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/default.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_AddAttribute(new AttributeExternalKey("infra_id", array("targetclass"=>"logInfra", "jointype"=> '', "label"=>"Infrastructure", "description"=>"The infrastructure impacted", "allowed_values"=>null, "sql"=>"infra_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("infra_name", array("label"=>"Infrastructure name", "description"=>"Name of the impacted infrastructure", "allowed_values"=>null, "extkey_attcode"=> 'infra_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("ticket_id", array("targetclass"=>"bizChangeTicket", "jointype"=> '', "label"=>"Ticket #", "description"=>"Ticket number", "allowed_values"=>null, "sql"=>"ticket_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("ticket_name", array("label"=>"Ticket name", "description"=>"Name of the ticket", "allowed_values"=>null, "extkey_attcode"=> 'ticket_id', "target_attcode"=>"title"))); + MetaModel::Init_AddAttribute(new AttributeString("impact", array("label"=>"Impact", "description"=>"Level of impact of the infra by the related ticket", "allowed_values"=>null, "sql"=>"impact", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + + MetaModel::Init_AddFilterFromAttribute("infra_id"); + MetaModel::Init_AddFilterFromAttribute("ticket_id"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('infra_id', 'ticket_id', 'impact')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('list', array('infra_id', 'ticket_id', 'impact')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('infra_id', 'ticket_id')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('infra_id', 'ticket_id')); // Criteria of the advanced search form + } + + public function Generate(cmdbDataGenerator $oGenerator) + { + $this->Set('infra_id', $oGenerator->GenerateKey("logInfra", array('org_id' =>$oGenerator->GetOrganizationId() ))); + $this->Set('ticket_id', $oGenerator->GenerateKey("bizIncidentTicket", array('org_id' =>$oGenerator->GetOrganizationId() ))); + $this->Set('impact', $oGenerator->GenerateString("enum(none,mandatory,partial)")); + } +} +//////////////////////////////////////////////////////////////////////////////////// +/** +* n-n link between any contact and a Contract +*/ +//////////////////////////////////////////////////////////////////////////////////// +class lnkContactChange extends cmdbAbstractObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "ContactChangeLink", + "description" => "Contact associated to a change", + "key_type" => "autoincrement", + "key_label" => "link_id", + "name_attcode" => "role", // ???? + "state_attcode" => "", + "reconc_keys" => array("role"), // ???? + "db_table" => "contact_change", + "db_key_field" => "link_id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/default.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_AddAttribute(new AttributeExternalKey("contact_id", array("targetclass"=>"bizContact", "jointype"=> '', "label"=>"Contact", "description"=>"The contact linked to contract", "allowed_values"=>null, "sql"=>"contact_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("contact_mail", array("label"=>"Contact E-mail", "description"=>"Mail for the contact", "allowed_values"=>null, "extkey_attcode"=> 'contact_id', "target_attcode"=>"email"))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("change_id", array("targetclass"=>"bizChangeTicket", "jointype"=> '', "label"=>"Change Ticket", "description"=>"Change ticket ID", "allowed_values"=>null, "sql"=>"change_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("change_number", array("label"=>"change number", "description"=>"Ticket number for this change", "allowed_values"=>null, "extkey_attcode"=> 'change_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeString("role", array("label"=>"Role", "description"=>"Role of this contact for this change", "allowed_values"=>null, "sql"=>"role", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + + MetaModel::Init_AddFilterFromAttribute("change_id"); + MetaModel::Init_AddFilterFromAttribute("contact_id"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('change_id', 'contact_id', 'role')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('list', array('change_id', 'contact_id', 'role')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('change_id', 'contact_id')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('change_id', 'contact_id')); // Criteria of the advanced search form + } + + public function Generate(cmdbDataGenerator $oGenerator) + { + $this->Set('contract_id', $oGenerator->GenerateKey("logInfra", array('org_id' =>$oGenerator->GetOrganizationId() ))); + $this->Set('contact_id', $oGenerator->GenerateKey("bizIncidentTicket", array('org_id' =>$oGenerator->GetOrganizationId() ))); + $this->Set('role', $oGenerator->GenerateString("enum(none,mandatory,partial)")); + } + +} + +?> diff --git a/business/Changes-04-Sep-2007.php b/business/Changes-04-Sep-2007.php new file mode 100644 index 000000000..e8097d75e --- /dev/null +++ b/business/Changes-04-Sep-2007.php @@ -0,0 +1,32 @@ +Changements principaux: +- la classe AbstractObject est sortie du biz model +- join_type remplacé par is_null_allowed (placé à la fin pour être + facile à retrouver) +- j'ai enlevé toute la classe logLocatedObject qui était en commentaire +- Enlevé 'address' de l'advanced search sur une location car ce n'est plus un critère de recherche possible (remplacé par country) +- Ajouté des critères de recherche sur bizCircuit +- Ajouté les ZList sur bizCircuit +- Ajouté les Zlist pour bizInterface +- Ajouté les Zlist pour lnkInfraInfra +- Ajouté les Zlist pour lnkInfraTicket + +Dans AbstractObject: désactivé l'affichage des contacts liés qui ne marche pas pour les tickets. + +Bug fix ? +- J'ai rajouté un blindage if (is_object($proposedValue) &&... dans AttributeDate::MakeRealValue mais je ne comprends pas d'où sort la classe DateTime... et pourtant il y en a... + +Améliorations: +- Ajouter une vérification des ZList (les attributs/critèresde recherche déclarés dans la liste existent-ils pour cet objet) + +Ne marche pas: +- Objets avec des clefs externes vides +- Enums !!!! + +Data Generator: +Organization '1' updated. +5 Location objects created. +19 PC objects created. +19 Network Device objects created. +42 Person objects created. +6 Incident objects created. +17 Infra Group objects created. +34 Infra Infra objects created. diff --git a/business/KEDB.php b/business/KEDB.php new file mode 100644 index 000000000..ec8057f32 --- /dev/null +++ b/business/KEDB.php @@ -0,0 +1,165 @@ + "bizmodel,searchable", + "name" => "Known Error", + "description" => "Error documented for a known issue", + "key_type" => "autoincrement", + "key_label" => "id", + "name_attcode" => "name", + "state_attcode" => "", + "reconc_keys" => array("cust_id", "name"), // inherited attributes + "db_table" => "known_error", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/knownError.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeString("name", array("label"=>"Name", "description"=>"Name to identify this error", "allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_AddAttribute(new AttributeExternalKey("cust_id", array("targetclass"=>"bizOrganization", "label"=>"Organization", "description"=>"Organization for this known error", "allowed_values"=>null, "sql"=>"cust_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("cust_name", array("label"=>"Organization", "description"=>"Company / Department owning this object", "allowed_values"=>null, "extkey_attcode"=> 'cust_id', "target_attcode"=>"name"))); + + MetaModel::Init_AddAttribute(new AttributeText("symptom", array("label"=>"Symptom", "description"=>"Description of this error", "allowed_values"=>null, "sql"=>"symptom", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeText("root_cause", array("label"=>"Root cause", "description"=>"Original cause for this known error", "allowed_values"=>null, "sql"=>"rootcause", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeText("workaround", array("label"=>"Work around", "description"=>"Work around to fix this error", "allowed_values"=>null, "sql"=>"workaround", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeText("solution", array("label"=>"Solution", "description"=>"Description of this contract", "allowed_values"=>null, "sql"=>"solution", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + + MetaModel::Init_AddAttribute(new AttributeString("error_code", array("label"=>"Error Code", "description"=>"Key word to identify error", "allowed_values"=>null, "sql"=>"error_code", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("domain", array("label"=>"Domain", "description"=>"Domain for this known error, network, desktop, ...", "allowed_values"=>new ValueSetEnum("Network, Server, Application, Desktop"), "sql"=>"domain", "default_value"=>"Application", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("vendor", array("label"=>"Vendor", "description"=>"Vendor concerned by this known error", "allowed_values"=>null, "sql"=>"vendor", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("model", array("label"=>"Model", "description"=>"Model concerned by this known error, it may be an application, a device ...", "allowed_values"=>null, "sql"=>"model", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("version", array("label"=>"Version", "description"=>"Version related to model impacted by known error", "allowed_values"=>null, "sql"=>"version", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + + + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("name"); + MetaModel::Init_AddFilterFromAttribute("cust_id"); + MetaModel::Init_AddFilterFromAttribute("cust_name"); + MetaModel::Init_AddFilterFromAttribute("error_code"); + MetaModel::Init_AddFilterFromAttribute("domain"); + + + + + + MetaModel::Init_SetZListItems('details', array('name', 'cust_id','error_code','domain','vendor','model','version', 'symptom','root_cause','workaround','solution')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('name', 'cust_id','error_code', 'symptom')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('name', 'error_code','domain')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('name', 'cust_id','error_code', 'error_code','symptom')); // Criteria of the advanced search form + + } + + // State machine actions + public function IncrementVersion($sStimulusCode) + { + $this->Set('version_number', $this->Get('version_number') + 1); + return true; + } +} + +//////////////////////////////////////////////////////////////////////////////////// +/** +* n-n link between any Infra and a Known Error +*/ +//////////////////////////////////////////////////////////////////////////////////// +class lnkInfraError extends cmdbAbstractObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "InfraErrorLinks", + "description" => "Infra related to a known error", + "key_type" => "autoincrement", + "key_label" => "link_id", + "name_attcode" => "", // ???? + "state_attcode" => "", + "reconc_keys" => array("infra_id","error_id"), // ???? + "db_table" => "infra_error_links", + "db_key_field" => "link_id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/default.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_AddAttribute(new AttributeExternalKey("infra_id", array("targetclass"=>"logInfra", "jointype"=> '', "label"=>"Infrastructure", "description"=>"The infrastructure impacted", "allowed_values"=>null, "sql"=>"infra_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("infra_name", array("label"=>"Infrastructure name", "description"=>"Name of the impacted infrastructure", "allowed_values"=>null, "extkey_attcode"=> 'infra_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeExternalField("infra_status", array("label"=>"Status", "description"=>"Status of the impacted infrastructure", "allowed_values"=>null, "extkey_attcode"=> 'infra_id', "target_attcode"=>"status"))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("error_id", array("targetclass"=>"bizKnownError", "jointype"=> '', "label"=>"Error name", "description"=>"Error id", "allowed_values"=>null, "sql"=>"error_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("error_name", array("label"=>"Error name", "description"=>"Name of the error", "allowed_values"=>null, "extkey_attcode"=> 'error_id', "target_attcode"=>"name"))); + + MetaModel::Init_AddFilterFromAttribute("infra_id"); + MetaModel::Init_AddFilterFromAttribute("error_id"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('infra_id', 'error_id')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('list', array('infra_id', 'infra_status','error_id')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('infra_id', 'error_id')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('infra_id', 'error_id')); // Criteria of the advanced search form + } + + +} + +//////////////////////////////////////////////////////////////////////////////////// +/** +* n-n link between any Contract and a Document +*/ +//////////////////////////////////////////////////////////////////////////////////// +class lnkDocumentError extends cmdbAbstractObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "DocumentsErrorLinks", + "description" => "A link between a document and a known error", + "key_type" => "autoincrement", + "key_label" => "link_id", + "name_attcode" => "link_type", + "state_attcode" => "", + "reconc_keys" => array("doc_name", "error_name"), + "db_table" => "documents_error_link", + "db_key_field" => "link_id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/default.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_AddAttribute(new AttributeExternalKey("doc_id", array("targetclass"=>"bizDocument", "label"=>"Document Name", "description"=>"id of the Document", "allowed_values"=>null, "sql"=>"doc_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("doc_name", array("label"=>"Document", "description"=>"name of the document", "allowed_values"=>null, "extkey_attcode"=> 'doc_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("error_id", array("targetclass"=>"bizKnownError", "label"=>"Error", "description"=>"Error linked to this document", "allowed_values"=>null, "sql"=>"error_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("error_name", array("label"=>"Error name", "description"=>"name of the linked error", "allowed_values"=>null, "extkey_attcode"=> 'error_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeString("link_type", array("label"=>"link_type", "description"=>"Type of the link", "allowed_values"=>null, "sql"=>"link_type", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + + MetaModel::Init_AddFilterFromAttribute("doc_id"); + MetaModel::Init_AddFilterFromAttribute("doc_name"); + MetaModel::Init_AddFilterFromAttribute("error_id"); + MetaModel::Init_AddFilterFromAttribute("error_name"); + MetaModel::Init_AddFilterFromAttribute("link_type"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('doc_id', 'error_name', 'link_type')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('doc_id', 'error_name', 'link_type')); // Attributes to be displayed for a list + } +} + + +?> diff --git a/business/ServiceMgmt.business.php b/business/ServiceMgmt.business.php new file mode 100644 index 000000000..cbb67ca5f --- /dev/null +++ b/business/ServiceMgmt.business.php @@ -0,0 +1,261 @@ + "bizmodel,searchable", + "name" => "Contract", + "description" => "Contract signed by an organization", + "key_type" => "autoincrement", + "key_label" => "id", + "name_attcode" => "name", + "state_attcode" => "status", + "reconc_keys" => array("customer_id", "name"), // inherited attributes + "db_table" => "contracts", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/contract.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeString("name", array("label"=>"Name", "description"=>"Name of the contract", "allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_AddAttribute(new AttributeExternalKey("customer_id", array("targetclass"=>"bizOrganization", "label"=>"Customer", "description"=>"Customer for this contract", "allowed_values"=>null, "sql"=>"customer_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("customer_name", array("label"=>"Customer", "description"=>"name of the Customer", "allowed_values"=>null, "extkey_attcode"=> 'customer_id', "target_attcode"=>"name"))); + + MetaModel::Init_AddAttribute(new AttributeExternalKey("provider_id", array("targetclass"=>"bizOrganization", "label"=>"Provider", "description"=>"Provider for this contract", "allowed_values"=>null, "sql"=>"provider_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("provider_name", array("label"=>"Provider", "description"=>"name of the service provider", "allowed_values"=>null, "extkey_attcode"=> 'provider_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeString("service_name", array("label"=>"Service Name", "description"=>"Name of service for this contract", "allowed_values"=>null, "sql"=>"service_name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("team_id", array("targetclass"=>"bizTeam", "label"=>"Team", "description"=>"Team managing this contract", "allowed_values"=>null, "sql"=>"team_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("team_name", array("label"=>"Team", "description"=>"name of the team managing this contract", "allowed_values"=>null, "extkey_attcode"=> 'team_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeEnum("service_level", array("label"=>"Service Level", "description"=>"Level of service for this contract", "allowed_values"=>new ValueSetEnum("Gold,Silver,Bronze"), "sql"=>"service_level", "default_value"=>"Bronze", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("cost_unit", array("label"=>"Cost unit", "description"=>"Cost unit to compute global cost for this contract", "allowed_values"=>new ValueSetEnum("Devices,Persons,Applications,Global"), "sql"=>"cost_unit", "default_value"=>"Global", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("cost_freq", array("label"=>"Cost frequency", "description"=>"Frequency of cost for this contract", "allowed_values"=>new ValueSetEnum("Monthly,Yearly,Once"), "sql"=>"cost_freq", "default_value"=>"Once", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("cost", array("label"=>"Cost", "description"=>"Cost of this contract", "allowed_values"=>null, "sql"=>"cost", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("currency", array("label"=>"Currency", "description"=>"Currency of cost for this contract", "allowed_values"=>new ValueSetEnum("Euros,Dollars"), "sql"=>"currency", "default_value"=>"Euros", "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_AddAttribute(new AttributeText("description", array("label"=>"Description", "description"=>"Description of this contract", "allowed_values"=>null, "sql"=>"description", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeDate("move2prod_date", array("label"=>"Date of move to production", "description"=>"Date when the contract is on production", "allowed_values"=>null, "sql"=>"move2prod_date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeDate("end_prod", array("label"=>"Date of end of production", "description"=>"Date when the contract is stopped", "allowed_values"=>null, "sql"=>"end_date", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("status", array("label"=>"Status", "description"=>"Status of the contract", "allowed_values"=>new ValueSetEnum("New, Negotiating, Signed, Production, Notice, Finished"), "sql"=>"status", "default_value"=>"New", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("type", array("label"=>"Type", "description"=>"Type of the contract", "allowed_values"=>new ValueSetEnum("Hardware,Software,Support,Licence"), "sql"=>"type", "default_value"=>"Support", "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_AddAttribute(new AttributeInteger("version_number", array("label"=>"Version number", "description"=>"Revision number for this contract", "allowed_values"=>null, "sql"=>"version_number", "default_value"=>1, "is_null_allowed"=>false, "depends_on"=>array()))); + + + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("service_name"); + MetaModel::Init_AddFilterFromAttribute("provider_id"); + MetaModel::Init_AddFilterFromAttribute("customer_id"); + MetaModel::Init_AddFilterFromAttribute("team_id"); + MetaModel::Init_AddFilterFromAttribute("team_name"); + MetaModel::Init_AddFilterFromAttribute("service_level"); + MetaModel::Init_AddFilterFromAttribute("end_prod"); + MetaModel::Init_AddFilterFromAttribute("status"); + MetaModel::Init_AddFilterFromAttribute("version_number"); + MetaModel::Init_AddFilterFromAttribute("name"); + MetaModel::Init_AddFilterFromAttribute("type"); + + + // Life cycle + MetaModel::Init_DefineState("New", array("label"=>"New", "description"=>"Newly created contract", "attribute_inherit"=>null, + "attribute_list"=>array())); + MetaModel::Init_DefineState("Negotiating", array("label"=>"Negotiating", "description"=>"The contract is being worked on", "attribute_inherit"=>null, + "attribute_list"=>array())); + MetaModel::Init_DefineState("Signed", array("label"=>"Signed", "description"=>"The contract has been signed", "attribute_inherit"=>null, + "attribute_list"=>array())); + MetaModel::Init_DefineState("Production", array("label"=>"Production", "description"=>"The contract is effective in production", "attribute_inherit"=>null, + "attribute_list"=>array())); + MetaModel::Init_DefineState("Notice", array("label"=>"Notice", "description"=>"The contract is about to be terminated", "attribute_inherit"=>null, + "attribute_list"=>array())); + MetaModel::Init_DefineState("Finished", array("label"=>"Finished", "description"=>"The contract is terminated", "attribute_inherit"=>null, + "attribute_list"=>array())); + + MetaModel::Init_DefineStimulus("ev_freeze_version", new StimulusUserAction(array("label"=>"Freeze this version", "description"=>"This version of the contract is published"))); + MetaModel::Init_DefineStimulus("ev_sign", new StimulusUserAction(array("label"=>"Sign this contract", "description"=>"This contract is being signed"))); + MetaModel::Init_DefineStimulus("ev_begin", new StimulusUserAction(array("label"=>"Move to production", "description"=>"The contract becomes applicable in production"))); + MetaModel::Init_DefineStimulus("ev_notice", new StimulusUserAction(array("label"=>"Start notice period", "description"=>"The end date of the contract is approaching"))); + MetaModel::Init_DefineStimulus("ev_terminate", new StimulusUserAction(array("label"=>"Ends this contract", "description"=>"The contract is ending"))); + MetaModel::Init_DefineStimulus("ev_elapsed", new StimulusUserAction(array("label"=>"Times up [Do not click!]", "description"=>"The contract over"))); + + MetaModel::Init_DefineTransition("New", "ev_freeze_version", array("target_state"=>"Negotiating", "actions"=>array('IncrementVersion'), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Negotiating", "ev_freeze_version", array("target_state"=>"Negotiating", "actions"=>array('IncrementVersion'), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Negotiating", "ev_sign", array("target_state"=>"Signed", "actions"=>array(), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Negotiating", "ev_terminate", array("target_state"=>"Finished", "actions"=>array(), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Signed", "ev_freeze_version", array("target_state"=>"Signed", "actions"=>array('IncrementVersion'), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Signed", "ev_begin", array("target_state"=>"Production", "actions"=>array(), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Signed", "ev_terminate", array("target_state"=>"Finished", "actions"=>array(), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Production", "ev_freeze_version", array("target_state"=>"Production", "actions"=>array('IncrementVersion'), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Production", "ev_elapsed", array("target_state"=>"Notice", "actions"=>array(), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Production", "ev_terminate", array("target_state"=>"Finished", "actions"=>array(), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Notice", "ev_elapsed", array("target_state"=>"Finished", "actions"=>array(), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Notice", "ev_terminate", array("target_state"=>"Finished", "actions"=>array(), "user_restriction"=>null)); + + + MetaModel::Init_SetZListItems('details', array('name', 'status', 'customer_id', 'service_name','provider_id','type','description','team_id','service_level','cost','currency','cost_unit','cost_freq','move2prod_date','end_prod', 'version_number')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('name', 'status', 'customer_id', 'provider_id','service_name','service_level','type')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('name', 'status','service_name','provider_id','team_name','service_level','type')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('name', 'status', 'service_name','provider_id','team_name', 'service_level', 'customer_id')); // Criteria of the advanced search form + + } + + // State machine actions + public function IncrementVersion($sStimulusCode) + { + $this->Set('version_number', $this->Get('version_number') + 1); + return true; + } +} + +//////////////////////////////////////////////////////////////////////////////////// +/** +* n-n link between any Infra and a Contract +*/ +//////////////////////////////////////////////////////////////////////////////////// +class lnkInfraContract extends cmdbAbstractObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "InfraContractLinks", + "description" => "Infra covered by a contract", + "key_type" => "autoincrement", + "key_label" => "link_id", + "name_attcode" => "coverage", // ???? + "state_attcode" => "", + "reconc_keys" => array("infra_id","contract_id"), // ???? + "db_table" => "infra_contract_links", + "db_key_field" => "link_id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/default.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_AddAttribute(new AttributeExternalKey("infra_id", array("targetclass"=>"logInfra", "jointype"=> '', "label"=>"Infrastructure", "description"=>"The infrastructure impacted", "allowed_values"=>null, "sql"=>"infra_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("infra_name", array("label"=>"Infrastructure name", "description"=>"Name of the impacted infrastructure", "allowed_values"=>null, "extkey_attcode"=> 'infra_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeExternalField("infra_status", array("label"=>"Status", "description"=>"Status of the impacted infrastructure", "allowed_values"=>null, "extkey_attcode"=> 'infra_id', "target_attcode"=>"status"))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("contract_id", array("targetclass"=>"bizContract", "jointype"=> '', "label"=>"Contract name", "description"=>"Contract id", "allowed_values"=>null, "sql"=>"contract_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("contract_name", array("label"=>"Contract name", "description"=>"Name of the contract", "allowed_values"=>null, "extkey_attcode"=> 'contract_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeString("coverage", array("label"=>"coverage", "description"=>"coverage for the given infra", "allowed_values"=>null, "sql"=>"coverage", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("service_level", array("label"=>"service level", "description"=>"service level for the given infra", "allowed_values"=>null, "sql"=>"sla", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + + + MetaModel::Init_AddFilterFromAttribute("infra_id"); + MetaModel::Init_AddFilterFromAttribute("contract_id"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('infra_id', 'contract_id', 'coverage','service_level')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('list', array('infra_id', 'infra_status','contract_id' , 'coverage','service_level')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('infra_id', 'contract_id')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('infra_id', 'contract_id')); // Criteria of the advanced search form + } + + +} +//////////////////////////////////////////////////////////////////////////////////// +/** +* n-n link between any contact and a Contract +*/ +//////////////////////////////////////////////////////////////////////////////////// +class lnkContactContract extends cmdbAbstractObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "ContactContractLink", + "description" => "Contact associated to a contract", + "key_type" => "autoincrement", + "key_label" => "link_id", + "name_attcode" => "role", // ???? + "state_attcode" => "", + "reconc_keys" => array("role"), // ???? + "db_table" => "contact_Contract", + "db_key_field" => "link_id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/default.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_AddAttribute(new AttributeExternalKey("contact_id", array("targetclass"=>"bizContact", "jointype"=> '', "label"=>"Contact", "description"=>"The contact linked to contract", "allowed_values"=>null, "sql"=>"contact_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("contact_mail", array("label"=>"Contact E-mail", "description"=>"Mail for the contact", "allowed_values"=>null, "extkey_attcode"=> 'contact_id', "target_attcode"=>"email"))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("contract_id", array("targetclass"=>"bizContract", "jointype"=> '', "label"=>"Contract", "description"=>"Contract ID", "allowed_values"=>null, "sql"=>"contract_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("contract_name", array("label"=>"Contract name", "description"=>"Name of the contract", "allowed_values"=>null, "extkey_attcode"=> 'contract_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeString("role", array("label"=>"Role", "description"=>"Role of this contact for this contract", "allowed_values"=>null, "sql"=>"role", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + + MetaModel::Init_AddFilterFromAttribute("contract_id"); + MetaModel::Init_AddFilterFromAttribute("contact_id"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('contract_id', 'contact_id', 'role')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('list', array('contract_id', 'contact_id', 'role')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('contract_id', 'contact_id')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('contract_id', 'contact_id')); // Criteria of the advanced search form + } + + public function Generate(cmdbDataGenerator $oGenerator) + { + $this->Set('contract_id', $oGenerator->GenerateKey("logInfra", array('org_id' =>$oGenerator->GetOrganizationId() ))); + $this->Set('contact_id', $oGenerator->GenerateKey("bizIncidentTicket", array('org_id' =>$oGenerator->GetOrganizationId() ))); + $this->Set('role', $oGenerator->GenerateString("enum(none,mandatory,partial)")); + } + +} +//////////////////////////////////////////////////////////////////////////////////// +/** +* n-n link between any Contract and a Document +*/ +//////////////////////////////////////////////////////////////////////////////////// +class lnkDocumentContract extends cmdbAbstractObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "DocumentsContractLinks", + "description" => "A link between a document and another contract", + "key_type" => "autoincrement", + "key_label" => "link_id", + "name_attcode" => "link_type", + "state_attcode" => "", + "reconc_keys" => array("doc_name", "contract_name"), + "db_table" => "documents_contracts", + "db_key_field" => "link_id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/default.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_AddAttribute(new AttributeExternalKey("doc_id", array("targetclass"=>"bizDocument", "label"=>"Document Name", "description"=>"id of the Document", "allowed_values"=>null, "sql"=>"doc_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("doc_name", array("label"=>"Document", "description"=>"name of the document", "allowed_values"=>null, "extkey_attcode"=> 'doc_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("contract_id", array("targetclass"=>"bizContract", "label"=>"Contract", "description"=>"Contract linked to this document", "allowed_values"=>null, "sql"=>"contract_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("contract_name", array("label"=>"contract name", "description"=>"name of the linked contract", "allowed_values"=>null, "extkey_attcode"=> 'contract_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeString("link_type", array("label"=>"link_type", "description"=>"Type of the link", "allowed_values"=>null, "sql"=>"link_type", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + + MetaModel::Init_AddFilterFromAttribute("doc_id"); + MetaModel::Init_AddFilterFromAttribute("doc_name"); + MetaModel::Init_AddFilterFromAttribute("contract_id"); + MetaModel::Init_AddFilterFromAttribute("contract_name"); + MetaModel::Init_AddFilterFromAttribute("link_type"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('doc_id', 'contract_name', 'link_type')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('doc_id', 'contract_name', 'link_type')); // Attributes to be displayed for a list + } +} + + +?> diff --git a/business/business_itopbegins.class.inc.php b/business/business_itopbegins.class.inc.php new file mode 100644 index 000000000..5ac001c57 --- /dev/null +++ b/business/business_itopbegins.class.inc.php @@ -0,0 +1,261 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ + + +/////////////////////////////////////////////////////////////////////////////// +// Business implementation demo +/////////////////////////////////////////////////////////////////////////////// + + +/** + * blah blah + * + * @package iTopUnitTests + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ +class cmdbContact extends CMDBObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "blah", + "name" => "klassContact", + "description" => "klass contact description", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "att_contact_name", + "state_attcode" => "", + "reconc_keys" => array("att_contact_name"), + "db_table" => "contact", + "db_key_field" => "contactid", + "db_finalclass_field" => "actualclass", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeString("att_contact_name", array("label"=>"name of the contact", "description"=>"blah", "allowed_values"=>null, "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array(), "sql"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeInteger("att_contact_availability", array("label"=>"degree of availability in percent", "description"=>"blah", "allowed_values"=>null, "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array(), "sql"=>"availability"))); + MetaModel::Init_AddAttribute(new AttributeDate("start_date", array("label"=>"Starting date", "description"=>"Incident starting date", "allowed_values"=>null, "sql"=>"start_date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("att_contact_name"); + MetaModel::Init_AddFilterFromAttribute("att_contact_availability"); + } +} + +/** + * blah blah + * + * @package iTopUnitTests + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ +class cmdbPerson extends cmdbContact +{ + public static function Init() + { + $oValsDunsNumber = new ValueSetObjects("cmdbCompany: att_company_dunsnumber Begins with '$[duns_prm::]'", "att_company_dunsnumber", array("att_company_dunsnumber"=>true)); + + $aParams = array + ( + "category" => "blah", + "name" => "klassPerson", + "description" => "klass person description", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "att_contact_name", + "state_attcode" => "", + "reconc_keys" => array("att_contact_name"), + "db_table" => "person", + "db_key_field" => "personid", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeString("att_person_email", array("label"=>"iMaile", "description"=>"imelle", "allowed_values"=>$oValsDunsNumber, "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array(), "sql"=>"email"))); + MetaModel::Init_AddAttribute(new AttributeString("att_person_name", array("label"=>"secName", "description"=>"secondary name", "allowed_values"=>new ValueSetEnum(array("nom1", "nom2", "nom10", "no", "noms", "")), "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array(), "sql"=>"name"))); + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("att_person_email"); + } +} + +/** + * blah blah + * + * @package iTopUnitTests + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ +class cmdbSubcontractor extends cmdbPerson +{ + public static function Init() + { + $aParams = array + ( + "category" => "blah", + "name" => "klassSubcontractor", + "description" => "klass subcontractor description", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "att_contact_name", + "state_attcode" => "", + "reconc_keys" => array("att_contact_name"), + "db_table" => "subcontractor", + "db_key_field" => "subcontractorid", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeString("att_contractinfo", array("label"=>"contract info", "description"=>"blah", "allowed_values"=>null, "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array(), "sql"=>"contractinfo"))); + + MetaModel::Init_AddAttribute(new AttributeExternalKey("ext_subcontractor_provider", array("label"=>"ssii", "description"=>"blah", "allowed_values"=>null, "sql"=>"provider", "targetclass"=>"cmdbProvider", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("extatt_subcontractor_provider_ref", array("label"=>"ref", "description"=>"blah", "allowed_values"=>null, "extkey_attcode"=>"ext_subcontractor_provider", "target_attcode"=>"att_provider_ref"))); + + MetaModel::Init_AddAttribute(new AttributeExternalKey("ext_subcontractor_tutor", array("label"=>"tutor", "description"=>"blah", "allowed_values"=>null, "sql"=>"tutor", "targetclass"=>"cmdbPerson", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("extatt_subcontractor_tutor_email", array("label"=>"tutor email", "description"=>"blah", "allowed_values"=>null, "extkey_attcode"=>"ext_subcontractor_tutor", "target_attcode"=>"att_person_email"))); + MetaModel::Init_AddAttribute(new AttributeExternalField("extatt_subcontractor_tutor_secondname", array("label"=>"2ndname (ext field)", "description"=>"blah", "allowed_values"=>null, "extkey_attcode"=>"ext_subcontractor_tutor", "target_attcode"=>"att_person_name"))); + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("extatt_subcontractor_tutor_secondname"); + } +} + +/** + * blah blah + * + * @package iTopUnitTests + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ +class cmdbCrowd extends cmdbObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "blah", + "name" => "klassCrowd", + "description" => "klass crowd description", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "att_crowd_peoplecount", + "state_attcode" => "", + "reconc_keys" => array("att_crowd_peoplecount"), + "db_table" => "crowd", + "db_key_field" => "crowdid", + "db_finalclass_field" => "crowdclass", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeInteger("att_crowd_peoplecount", array("label"=>"people count", "description"=>"blah", "allowed_values"=>null, "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array(), "sql"=>"peoplecount"))); + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("att_crowd_peoplecount"); + } +} + +/** + * blah blah + * + * @package iTopUnitTests + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ +class cmdbCompany extends cmdbCrowd +{ + public static function Init() + { + $aParams = array + ( + "category" => "blah", + "name" => "klassCompany", + "description" => "klass company description", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "att_company_dunsnumber", + "state_attcode" => "", + "reconc_keys" => array("att_company_dunsnumber"), + "db_table" => "company", + "db_key_field" => "companyid", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeString("att_company_dunsnumber", array("label"=>"duns number", "description"=>"blah", "allowed_values"=>null, "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array(), "sql"=>"dunsnumber"))); + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("att_company_dunsnumber"); + } +} + +/** + * blah blah + * + * @package iTopUnitTests + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ +class cmdbProvider extends cmdbCompany +{ + public static function Init() + { + $aParams = array + ( + "category" => "blah", + "name" => "klassProvider", + "description" => "klass provider description", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "att_provider_ref", + "state_attcode" => "", + "reconc_keys" => array("att_provider_ref"), + "db_table" => "provider", + "db_key_field" => "providerid", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeInteger("att_provider_ref", array("label"=>"provider ref", "description"=>"blah", "allowed_values"=>null, "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array(), "sql"=>"providerref"))); + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("att_provider_ref"); + } +} + + +?> diff --git a/business/business_test.class.inc.php b/business/business_test.class.inc.php new file mode 100644 index 000000000..3a51b8f93 --- /dev/null +++ b/business/business_test.class.inc.php @@ -0,0 +1,366 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ + +/////////////////////////////////////////////////////////////////////////////// +// Business implementation demo +/////////////////////////////////////////////////////////////////////////////// + +MetaModel::RegisterRelation("Potes", array("description"=>"ceux dont l'email ressemble au mien", "verb_down"=>"est pote de", "verb_up"=>"est pote de")); + + +MetaModel::RegisterZList("list1", array("description"=>"une premiere list, just for fun", "type"=>"attributes")); +MetaModel::RegisterZList("list2", array("description"=>"la secunda e meliora", "type"=>"attributes")); +MetaModel::RegisterZList("list3", array("description"=>"la variante qui tue", "type"=>"filters")); + + +/** + * blah blah + * + * @package iTopUnitTests + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ +class cmdbObjectHomeMade extends cmdbObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "blah", + "name" => "anyObject", + "description" => "std object", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "", + "state_attcode" => "", + "reconc_keys" => array(""), + "db_table" => "", + "db_key_field" => "", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + } + + public static function GetRelationQueries($sRelCode) + { + //trigger_error("GetRelationQueries: cmdbObjectHomeMade"); + switch ($sRelCode) + { + case "Potes": + $aRels = array("xxxx" => array("sQuery"=>"cmdbContact: pkey = 40", "bPropagate"=>true, "iDistance"=>3)); + return $aRels; + } + } +} + + +/** + * blah blah + * + * @package iTopUnitTests + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ +class cmdbContact extends cmdbObjectHomeMade +{ + public static function Init() + { + $aParams = array + ( + "category" => "blah", + "name" => "Contact", + "description" => "Un object que l'on peut communiquer avec", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "name", + "state_attcode" => "etat", + "reconc_keys" => array("name"), + "db_table" => "contact", + "db_key_field" => "contactid", + "db_finalclass_field" => "actualclass", + ); + MetaModel::Init_Params($aParams); + //MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeString("etat", array("label"=>"l'etat", "description"=>"les etats d'ame d'eric", "allowed_values"=>new ValueSetEnum('justborn, 15, 21'), "sql"=>"etat", "default_value"=>"justborn", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("name", array("label"=>"nom", "description"=>"ze equipe", "allowed_values"=>null, "sql"=>"name", "default_value"=>"XXXX", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("email", array("label"=>"iMaile", "description"=>"imelle", "allowed_values"=>null, "sql"=>"email", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("owner", array("label"=>"owned by", "description"=>"organization owning the team", "allowed_values"=>null, "sql"=>"ownerorg", "targetclass"=>"cmdbOrga", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("ownername", array("label"=>"owned by", "description"=>"name of organization owning the team", "allowed_values"=>null, "extkey_attcode"=>"owner", "target_attcode"=>"_name_"))); + MetaModel::Init_AddAttribute(new AttributeExternalField("ownertnut", array("label"=>"owntnut", "description"=>"blah tnut blah", "allowed_values"=>null, "extkey_attcode"=>"owner", "target_attcode"=>"_dunsnumber_"))); + + MetaModel::Init_AddAttribute(new AttributeLinkedSet("myworkshops", array("label"=>"held workshops", "description"=>"blah tnut blah", "depends_on"=>array(), "linked_class"=>"cmdbLiens", "ext_key_to_me"=>"tocontact", "count_min"=>1, "count_max"=>10, "allowed_values"=>null))); + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("owner"); + MetaModel::Init_AddFilterFromAttribute("name"); + MetaModel::Init_AddFilterFromAttribute("ownername"); + + MetaModel::Init_SetZListItems("list1", array("name", "email")); + MetaModel::Init_SetZListItems("list2", array()); + MetaModel::Init_SetZListItems("list3", array("ownername")); + + MetaModel::Init_DefineState("justborn", array("label"=>"just born", "description"=>"too young to die", "attribute_inherit"=>null, "attribute_list"=>array("owner"=>OPT_ATT_MANDATORY))); + MetaModel::Init_DefineState("15", array("label"=>"student", "description"=>"stupid age", "attribute_inherit"=>"justborn", "attribute_list"=>array("owner"=>OPT_ATT_MUSTPROMPT, "email"=>OPT_ATT_MUSTPROMPT))); + MetaModel::Init_DefineState("21", array("label"=>"old", "description"=>"one foot in the grave", "attribute_inherit"=>"15", "attribute_list"=>array("email"=>OPT_ATT_READONLY|OPT_ATT_MUSTCHANGE))); + + MetaModel::Init_DefineStimulus("toschool", new StimulusUserAction(array("label"=>"go to school", "description"=>"start learning stupid things"))); + MetaModel::Init_DefineStimulus("raise", new StimulusUserAction(array("label"=>"grow!", "description"=>"eat tons of BigMACs"))); + + MetaModel::Init_DefineTransition("justborn", "toschool", array("target_state"=>"15", "actions"=>array('MyLifecycleHandler', 'MyLifecycleHandler2'), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("15", "raise", array("target_state"=>"21", "actions"=>null, "user_restriction"=>null)); + } + + public static function GetRelationQueries($sRelCode) + { + //trigger_error("GetRelationQueries: cmdbContact"); + switch ($sRelCode) + { + case "Potes": + $aRels = array( + "zz1" => array("sQuery"=>"cmdbContact: name Begins with '\$[this.name::]' AND pkey != \$[this.pkey::]", "bPropagate"=>false, "iDistance"=>3), + "zz2" => array("sQuery"=>"cmdbContact: owner = \$[this.owner::] AND owner != 2", "bPropagate"=>false, "iDistance"=>3), + ); + return array_merge($aRels, parent::GetRelationQueries($sRelCode)); + } + } + + public function MyLifecycleHandler($sStimulusCode) + { + echo "

    youhou!

    "; + return true; + } + public function MyLifecycleHandler2($sStimulusCode) + { + echo "

    ... les papous...

    "; + return true; + } +} + +/** + * blah blah + * + * @package iTopUnitTests + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ +class cmdbTeam extends cmdbContact +{ + public static function Init() + { + $aParams = array + ( + "category" => "blah", + "name" => "Equipado", + "description" => "Un regroupement de gens", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "email", + "state_attcode" => "", + "reconc_keys" => array("email"), + "db_table" => "team", + "db_key_field" => "teamid", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_OverloadAttributeParams("email", array("label"=>"email2", "description"=>"emailleu22")); + MetaModel::Init_AddAttribute(new AttributeInteger("headcount", array("label"=>"nombre", "description"=>"combien ils sont", "allowed_values"=>null, "sql"=>"headcount", "default_value"=>654321, "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("headcount"); + + MetaModel::Init_SetZListItems("noneditable", array("name")); + } + + public function ComputeValues() + { + //echo "Set(), function ComputeValues has been found for ".get_class($this)."
    \n"; + $this->Set("name", $this->Get("email")." and ".$this->Get("headcount")); + } + + public static function GetRelationQueries($sRelCode) + { + //trigger_error("GetRelationQueries: cmdbTeam"); + switch ($sRelCode) + { + case "Potes": + //$aRels = array("Relies on" => array("sQuery"=>"cmdbContact: name Begins with 'Louis'", "bPropagate"=>false, "iDistance"=>3)); + return array_merge(array(), parent::GetRelationQueries($sRelCode)); + } + } +} + + +/** + * blah blah + * + * @package iTopUnitTests + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ +class cmdbOrga extends cmdbObjectHomeMade +{ + public static function Init() + { + $aParams = array + ( + "category" => "blah", + "name" => "Organization", + "description" => "Une entite qui possede des choses", + "key_type" => "", + "key_label" => "", + "name_attcode" => "_name_", + "state_attcode" => "", + "reconc_keys" => array("_name_"), + "db_table" => "organization", + "db_key_field" => "orgid", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + //MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeString("_name_", array("label"=>"namo", "description"=>"official company name", "allowed_values"=>null, "sql"=>"name", "default_value"=>"XXXX", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("_status_", array("label"=>"step", "description"=>"step or status, etc.", "allowed_values"=>null, "sql"=>"status", "default_value"=>"XXXX", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeInteger("_dunsnumber_", array("label"=>"duns debile number", "description"=>"une bonne idee a OVSD", "allowed_values"=>null, "sql"=>"dunsnumber", "default_value"=>99007, "is_null_allowed"=>false, "depends_on"=>array()))); +// not yet allowed MetaModel::Init_AddAttribute(new AttributeInteger("_dunsnumberBY2_", array("label"=>"dummy duns", "description"=>"deux fois plus debile", "allowed_values"=>null, "sql"=>"dunsnumber * 3.141592654"))); + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("_name_"); + + MetaModel::Init_SetZListItems("list1", array("_status_")); + MetaModel::Init_SetZListItems("list2", array()); + MetaModel::Init_SetZListItems("list3", array("_name_")); + } + +} + +/** + * blah blah + * + * @package iTopUnitTests + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ +class cmdbLiens extends cmdbObjectHomeMade +{ + public static function Init() + { + $aParams = array + ( + "category" => "blah", + "name" => "Liens_entre_contacts_et_workshop", + "description" => "Une entite qui lie des contacts et workshops", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "function", + "state_attcode" => "", + "reconc_keys" => array("function"), + "db_table" => "role_ws", + "db_key_field" => "linkid", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + //MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeString("function", array("label"=>"fonction", "description"=>"la fonction...", "allowed_values"=>null, "sql"=>"function", "default_value"=>"XXXX", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("a1", array("label"=>"a1", "description"=>"a1", "allowed_values"=>null, "sql"=>"a1", "default_value"=>"XXXX", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("a2", array("label"=>"a1", "description"=>"a2", "allowed_values"=>null, "sql"=>"a2", "default_value"=>"XXXX", "is_null_allowed"=>true, "depends_on"=>array()))); + + // What makes it being a link... + MetaModel::Init_AddAttribute(new AttributeExternalKey("toworkshop", array("label"=>"participates in", "description"=>"workshop in wich the person is participating", "allowed_values"=>null, "sql"=>"ws_id", "targetclass"=>"cmdbWorkshop", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("ws_info", array("label"=>"name", "description"=>"namedescription", "allowed_values"=>null, "extkey_attcode"=>"toworkshop", "target_attcode"=>"namitus"))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("tocontact", array("label"=>"is held by", "description"=>"people involved in that mess", "allowed_values"=>null, "sql"=>"contactid", "targetclass"=>"cmdbContact", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("contact_info", array("label"=>"name", "description"=>"namedescription", "allowed_values"=>null, "extkey_attcode"=>"tocontact", "target_attcode"=>"name"))); + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("function"); + + MetaModel::Init_SetZListItems("list1", array("toworkshop", "contact_info")); + MetaModel::Init_SetZListItems("list2", array("function")); + MetaModel::Init_SetZListItems("list3", array("function")); + } + + public static function GetRelationQueries($sRelCode) + { + throw new CoreException("GetRelationQueries: cmdbLiens"); + return array("Relies on" => array("sQuery"=>"", "bPropagate"=>true, "iDistance"=>3)); + } +} + +/** + * blah blah + * + * @package iTopUnitTests + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ +class cmdbWorkshop extends cmdbObjectHomeMade +{ + public static function Init() + { + $aParams = array + ( + "category" => "blah", + "name" => "Workshop", + "description" => "Une entite qui pond des theories insensees", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "namitus", + "state_attcode" => "", + "reconc_keys" => array("namitus"), + "db_table" => "workshop", + "db_key_field" => "ws_id", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + //MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeString("namitus", array("label"=>"namo", "description"=>"nom imbitique pour pondeurs de debilites", "allowed_values"=>null, "sql"=>"name", "default_value"=>"XXXX", "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("namitus"); + + MetaModel::Init_SetZListItems("list1", array("namitus")); + MetaModel::Init_SetZListItems("list2", array()); + MetaModel::Init_SetZListItems("list3", array("namitus")); + } + + public static function GetRelationQueries($sRelCode) + { + throw new CoreException("GetRelationQueries: cmdbWorkshop"); + return array("Relies on" => array("sQuery"=>"", "bPropagate"=>true, "iDistance"=>3)); + } +} + + +?> diff --git a/business/data.samples.inc.php b/business/data.samples.inc.php new file mode 100644 index 000000000..8f76a3bd1 --- /dev/null +++ b/business/data.samples.inc.php @@ -0,0 +1,4845 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ + +$aCompanies = array(); +$aCompanies[] = array('name' => "iTop", 'domain' => "itop.com", 'code' => "ITOP"); +$aCompanies[] = array('name' => "R-Skin Design", 'domain' => "rskindesign.com", 'code' => "RSKIN"); +$aCompanies[] = array('name' => "Pen Shop", 'domain' => "penshop.com", 'code' => "PENSHOP"); +$aCompanies[] = array('name' => "Joyant", 'domain' => "joyan.com", 'code' => "JOYAN"); +$aCompanies[] = array('name' => "Inhabitants", 'domain' => "inhabitants.org", 'code' => "INH"); +$aCompanies[] = array('name' => "Electric Poulp", 'domain' => "epoulp.com", 'code' => "EPOULP"); +$aCompanies[] = array('name' => "Well Steam", 'domain' => "wellsteam.com", 'code' => "WEELSTM"); +$aCompanies[] = array('name' => "Express Engine", 'domain' => "expressengine.com", 'code' => "XPRESS"); +$aCompanies[] = array('name' => "Electric Bytes", 'domain' => "ebytes.com", 'code' => "EBYTES"); +$aCompanies[] = array('name' => "Iceberg", 'domain' => "iceberg.com", 'code' => "ICEBERG"); +$aCompanies[] = array('name' => "VThird", 'domain' => "vthird.com", 'code' => "V3RD"); +$aCompanies[] = array('name' => "AAssurance", 'domain' => "aasurance.com", 'code' => "AASSUR"); +$aCompanies[] = array('name' => "Webgets", 'domain' => "webgets.com", 'code' => "WEBGETS"); +$aCompanies[] = array('name' => "Brianair", 'domain' => array("brianair.co.uk", "brianair.com", "britanair.com"), 'code' => "BRAIR"); +$aCompanies[] = array('name' => "NetCMDB", 'domain' => array("netcmdb.com", "netcmdb.net", "octopus.com"), 'code' => "NECMDB"); +$aCompanies[] = array('name' => "Zen Garden", 'domain' => "zengarden.net", 'code' => "ZEN"); +$aCompanies[] = array('name' => "T-Supply", 'domain' => "tsupply.de", 'code' => "TSUPPLY"); +$aCompanies[] = array('name' => "Berliner Druckmaschinen", 'domain' => "bdm.de", 'code' => "BDM"); +$aCompanies[] = array('name' => "ZKS", 'domain' => "zks.com", 'code' => "ZKS"); +$aCompanies[] = array('name' => "Air Amérique", 'domain' => "airamerique.fr", 'code' => "AIRAM"); +$aCompanies[] = array('name' => "Radio Mongo", 'domain' => "radio-mongo.fr", 'code' => "MONGO"); +$aCompanies[] = array('name' => "Radio Andorre", 'domain' => "radio.ad", 'code' => "RADAND"); +$aCompanies[] = array('name' => "Idahos Mutual Life Insurance", 'domain' => "idahosmutual.com", 'code' => "IMLI"); +$aCompanies[] = array('name' => "Amott Laboratories", 'domain' => "amott.com", 'code' => "AMOTT"); +$aCompanies[] = array('name' => "Hereford Financial Services Group", 'domain' => "hfsg.com", 'code' => "HFSG"); +$aCompanies[] = array('name' => "Techni Data Corp", 'domain' => array("technidata.com", "techniservices.com"), 'code' => "TECHNI"); +$aCompanies[] = array('name' => "Southwestern Mutual Life Insurance", 'domain' => array("swmutual.com", "swmutual-loans.com", "insurance.swmutual.com"), 'code' => "SWMUTUAL"); +$aCompanies[] = array('name' => "American Solar Power", 'domain' => "solarpower.com", 'code' => "ASP"); +$aCompanies[] = array('name' => "Altena", 'domain' => "altena.fr", 'code' => "ALTENA"); +$aCompanies[] = array('name' => "Groupe Commeco", 'domain' => "commeco.fr", 'code' => "COMMECO"); +$aCompanies[] = array('name' => "Virages", 'domain' => array("virages.fr", "virages.corp.net"), 'code' => "VIRAGES"); +$aCompanies[] = array('name' => "Ciments Lorrains", 'domain' => "cimlor.fr", 'code' => "CIMLOR"); +$aCompanies[] = array('name' => "Durand SA", 'domain' => "durand.fr", 'code' => "DURAND"); +$aCompanies[] = array('name' => "Transports Jeanton", 'domain' => "jeanton.fr", 'code' => "JEANTON"); +$aCompanies[] = array('name' => "Companie Générale du Bois", 'domain' => "cgb.fr", 'code' => "CGB"); +$aCompanies[] = array('name' => "Village Group", 'domain' => "village.com", 'code' => "VGP"); + +$aCompaniesCode = array(); +foreach($aCompanies as $aCompany) +{ + $aCompaniesCode[$aCompany['code']] = array('name' => $aCompany['name'], 'domain' => $aCompany['domain']); +} + +$aFirstNames = array(); +$aFirstNames[] = 'Enzo'; +$aFirstNames[] = 'Quentin'; +$aFirstNames[] = 'Mohamed'; +$aFirstNames[] = 'Lucas'; +$aFirstNames[] = 'Julien'; +$aFirstNames[] = 'Ethan'; +$aFirstNames[] = 'Nathan'; +$aFirstNames[] = 'Thomas'; +$aFirstNames[] = 'Nicolas'; +$aFirstNames[] = 'Alexandre'; +$aFirstNames[] = 'Theo'; +$aFirstNames[] = 'Yanis'; +$aFirstNames[] = 'Hugo'; +$aFirstNames[] = 'Rayan'; +$aFirstNames[] = 'Tom'; +$aFirstNames[] = 'Romain'; +$aFirstNames[] = 'Sacha'; +$aFirstNames[] = 'Xavier'; +$aFirstNames[] = 'Olivier'; +$aFirstNames[] = 'Antoine'; +$aFirstNames[] = 'Raphael'; +$aFirstNames[] = 'Louis'; +$aFirstNames[] = 'Clement'; +$aFirstNames[] = 'Maxime'; +$aFirstNames[] = 'Pierre'; +$aFirstNames[] = 'Jules'; +$aFirstNames[] = 'Mael'; +$aFirstNames[] = 'Axel'; +$aFirstNames[] = 'Guillaume'; +$aFirstNames[] = 'Benjamin'; +$aFirstNames[] = 'Gabriel'; +$aFirstNames[] = 'Maxence'; +$aFirstNames[] = 'Evan'; +$aFirstNames[] = 'Sebastien'; +$aFirstNames[] = 'Arthur'; +$aFirstNames[] = 'Noah'; +$aFirstNames[] = 'Kevin'; +$aFirstNames[] = 'Mathieu'; +$aFirstNames[] = 'Leo'; +$aFirstNames[] = 'Mathis'; +$aFirstNames[] = 'David'; +$aFirstNames[] = 'Florian'; +$aFirstNames[] = 'Paul'; +$aFirstNames[] = 'Tristan'; +$aFirstNames[] = 'Timeo'; +$aFirstNames[] = 'Simon'; +$aFirstNames[] = 'Matthieu'; +$aFirstNames[] = 'Cedric'; +$aFirstNames[] = 'Corentin'; +$aFirstNames[] = 'Vincent'; +$aFirstNames[] = 'Audrey'; +$aFirstNames[] = 'Aurelie'; +$aFirstNames[] = 'Manon'; +$aFirstNames[] = 'Jade'; +$aFirstNames[] = 'Lea'; +$aFirstNames[] = 'Chloe'; +$aFirstNames[] = 'Emma'; +$aFirstNames[] = 'Camille'; +$aFirstNames[] = 'Caroline'; +$aFirstNames[] = 'Marie'; +$aFirstNames[] = 'Sarah'; +$aFirstNames[] = 'Pauline'; +$aFirstNames[] = 'Lilou'; +$aFirstNames[] = 'Julie'; +$aFirstNames[] = 'Ines'; +$aFirstNames[] = 'Fanny'; +$aFirstNames[] = 'Melissa'; +$aFirstNames[] = 'Lola'; +$aFirstNames[] = 'Clara'; +$aFirstNames[] = 'Oceane'; +$aFirstNames[] = 'Sabrina'; +$aFirstNames[] = 'Mathilde'; +$aFirstNames[] = 'Laura'; +$aFirstNames[] = 'Ambre'; +$aFirstNames[] = 'Alexia'; +$aFirstNames[] = 'Elodie'; +$aFirstNames[] = 'Anais'; +$aFirstNames[] = 'Amandine'; +$aFirstNames[] = 'Sophie'; +$aFirstNames[] = 'Emilie'; +$aFirstNames[] = 'Celia'; +$aFirstNames[] = 'Malika'; +$aFirstNames[] = 'Margaux'; +$aFirstNames[] = 'Ursule'; +$aFirstNames[] = 'Louise'; +$aFirstNames[] = 'Ophelie'; +$aFirstNames[] = 'Charlotte'; +$aFirstNames[] = 'Gaetane'; +$aFirstNames[] = 'Abigail'; +$aFirstNames[] = 'Noemie'; +$aFirstNames[] = 'Coralie'; +$aFirstNames[] = 'Celine'; +$aFirstNames[] = 'Louna'; +$aFirstNames[] = 'Emeline'; +$aFirstNames[] = 'Lena'; +$aFirstNames[] = 'Marion'; +$aFirstNames[] = 'Solene'; +$aFirstNames[] = 'Alice'; +$aFirstNames[] = 'Lucie'; +$aFirstNames[] = 'Lisa'; +$aFirstNames[] = 'Jean'; +$aFirstNames[] = 'Philippe'; +$aFirstNames[] = 'Michel'; +$aFirstNames[] = 'Alain'; +$aFirstNames[] = 'Patrick'; +$aFirstNames[] = 'Pascal'; +$aFirstNames[] = 'Daniel'; +$aFirstNames[] = 'Christophe'; +$aFirstNames[] = 'Eric'; +$aFirstNames[] = 'Nicolas'; +$aFirstNames[] = 'Pierre'; +$aFirstNames[] = 'Christian'; +$aFirstNames[] = 'Laurent'; +$aFirstNames[] = 'Stephane'; +$aFirstNames[] = 'David'; +$aFirstNames[] = 'Olivier'; +$aFirstNames[] = 'Sebastien'; +$aFirstNames[] = 'Gerard'; +$aFirstNames[] = 'Frederic'; +$aFirstNames[] = 'Bernard'; +$aFirstNames[] = 'Julien'; +$aFirstNames[] = 'Dominique'; +$aFirstNames[] = 'Thierry'; +$aFirstNames[] = 'Claude'; +$aFirstNames[] = 'Francis'; +$aFirstNames[] = 'Alexandre'; +$aFirstNames[] = 'Denis'; +$aFirstNames[] = 'Francois'; +$aFirstNames[] = 'Didier'; +$aFirstNames[] = 'Vincent'; +$aFirstNames[] = 'Bruno'; +$aFirstNames[] = 'Thomas'; +$aFirstNames[] = 'Gilles'; +$aFirstNames[] = 'Jacques'; +$aFirstNames[] = 'Jerome'; +$aFirstNames[] = 'Herve'; +$aFirstNames[] = 'Marc'; +$aFirstNames[] = 'Fabrice'; +$aFirstNames[] = 'Patrice'; +$aFirstNames[] = 'Maxime'; +$aFirstNames[] = 'Anthony'; +$aFirstNames[] = 'Serge'; +$aFirstNames[] = 'Andre'; +$aFirstNames[] = 'Cedric'; +$aFirstNames[] = 'Romain'; +$aFirstNames[] = 'Guillaume'; +$aFirstNames[] = 'Arnaud'; +$aFirstNames[] = 'Franck'; +$aFirstNames[] = 'Kevin'; +$aFirstNames[] = 'Sylvain'; +$aFirstNames[] = 'Lea'; +$aFirstNames[] = 'Manon'; +$aFirstNames[] = 'Camille'; +$aFirstNames[] = 'Emma'; +$aFirstNames[] = 'Clara'; +$aFirstNames[] = 'Chloe'; +$aFirstNames[] = 'Oceane'; +$aFirstNames[] = 'Lisa'; +$aFirstNames[] = 'Laura'; +$aFirstNames[] = 'Lucie'; +$aFirstNames[] = 'Mathilde'; +$aFirstNames[] = 'Lola'; +$aFirstNames[] = 'Marie'; +$aFirstNames[] = 'Sarah'; +$aFirstNames[] = 'Celia'; +$aFirstNames[] = 'Eva'; +$aFirstNames[] = 'Julie'; +$aFirstNames[] = 'Justine'; +$aFirstNames[] = 'Emilie'; +$aFirstNames[] = 'Ines'; +$aFirstNames[] = 'Pauline'; +$aFirstNames[] = 'Anais'; +$aFirstNames[] = 'Maeva'; +$aFirstNames[] = 'Zoe'; +$aFirstNames[] = 'Juliette'; +$aFirstNames[] = 'Alice'; +$aFirstNames[] = 'Margaux'; +$aFirstNames[] = 'Louise'; +$aFirstNames[] = 'Morgane'; +$aFirstNames[] = 'Clemence'; +$aFirstNames[] = 'Lena'; +$aFirstNames[] = 'Jeanne'; +$aFirstNames[] = 'Melissa'; +$aFirstNames[] = 'Noemie'; +$aFirstNames[] = 'Charlotte'; +$aFirstNames[] = 'Elise'; +$aFirstNames[] = 'Elisa'; +$aFirstNames[] = 'Jade'; +$aFirstNames[] = 'Margot'; +$aFirstNames[] = 'Romane'; +$aFirstNames[] = 'Alexia'; +$aFirstNames[] = 'Carla'; +$aFirstNames[] = 'Alicia'; +$aFirstNames[] = 'Flavie'; +$aFirstNames[] = 'Marine'; +$aFirstNames[] = 'Lou'; +$aFirstNames[] = 'Marion'; +$aFirstNames[] = 'Claire'; +$aFirstNames[] = 'Solene'; +$aFirstNames[] = 'Amelie'; +$aFirstNames[] = 'Mary'; +$aFirstNames[] = 'Patricia'; +$aFirstNames[] = 'Linda'; +$aFirstNames[] = 'Barbara'; +$aFirstNames[] = 'Elizabeth'; +$aFirstNames[] = 'Jennifer'; +$aFirstNames[] = 'Maria'; +$aFirstNames[] = 'Susan'; +$aFirstNames[] = 'Margaret'; +$aFirstNames[] = 'Dorothy'; +$aFirstNames[] = 'Lisa'; +$aFirstNames[] = 'Nancy'; +$aFirstNames[] = 'Karen'; +$aFirstNames[] = 'Betty'; +$aFirstNames[] = 'Helen'; +$aFirstNames[] = 'Sandra'; +$aFirstNames[] = 'Donna'; +$aFirstNames[] = 'Carol'; +$aFirstNames[] = 'Ruth'; +$aFirstNames[] = 'Sharon'; +$aFirstNames[] = 'Michelle'; +$aFirstNames[] = 'Laura'; +$aFirstNames[] = 'Sarah'; +$aFirstNames[] = 'Kimberly'; +$aFirstNames[] = 'Deborah'; +$aFirstNames[] = 'Jessica'; +$aFirstNames[] = 'Shirley'; +$aFirstNames[] = 'Cynthia'; +$aFirstNames[] = 'Angela'; +$aFirstNames[] = 'Melissa'; +$aFirstNames[] = 'Brenda'; +$aFirstNames[] = 'Amy'; +$aFirstNames[] = 'Anna'; +$aFirstNames[] = 'Rebecca'; +$aFirstNames[] = 'Virginia'; +$aFirstNames[] = 'Kathleen'; +$aFirstNames[] = 'Pamela'; +$aFirstNames[] = 'Martha'; +$aFirstNames[] = 'Debra'; +$aFirstNames[] = 'Amanda'; +$aFirstNames[] = 'Stephanie'; +$aFirstNames[] = 'Carolyn'; +$aFirstNames[] = 'Christine'; +$aFirstNames[] = 'Marie'; +$aFirstNames[] = 'Janet'; +$aFirstNames[] = 'Catherine'; +$aFirstNames[] = 'Frances'; +$aFirstNames[] = 'Ann'; +$aFirstNames[] = 'Joyce'; +$aFirstNames[] = 'Diane'; +$aFirstNames[] = 'Alice'; +$aFirstNames[] = 'Julie'; +$aFirstNames[] = 'Heather'; +$aFirstNames[] = 'Teresa'; +$aFirstNames[] = 'Doris'; +$aFirstNames[] = 'Gloria'; +$aFirstNames[] = 'Evelyn'; +$aFirstNames[] = 'Jean'; +$aFirstNames[] = 'Cheryl'; +$aFirstNames[] = 'Mildred'; +$aFirstNames[] = 'Katherine'; +$aFirstNames[] = 'Joan'; +$aFirstNames[] = 'Ashley'; +$aFirstNames[] = 'Judith'; +$aFirstNames[] = 'Rose'; +$aFirstNames[] = 'Janice'; +$aFirstNames[] = 'Kelly'; +$aFirstNames[] = 'Nicole'; +$aFirstNames[] = 'Judy'; +$aFirstNames[] = 'Christina'; +$aFirstNames[] = 'Kathy'; +$aFirstNames[] = 'Theresa'; +$aFirstNames[] = 'Beverly'; +$aFirstNames[] = 'Denise'; +$aFirstNames[] = 'Tammy'; +$aFirstNames[] = 'Irene'; +$aFirstNames[] = 'Jane'; +$aFirstNames[] = 'Lori'; +$aFirstNames[] = 'Rachel'; +$aFirstNames[] = 'Marilyn'; +$aFirstNames[] = 'Andrea'; +$aFirstNames[] = 'Kathryn'; +$aFirstNames[] = 'Louise'; +$aFirstNames[] = 'Sara'; +$aFirstNames[] = 'Anne'; +$aFirstNames[] = 'Jacqueline'; +$aFirstNames[] = 'Wanda'; +$aFirstNames[] = 'Bonnie'; +$aFirstNames[] = 'Julia'; +$aFirstNames[] = 'Ruby'; +$aFirstNames[] = 'Lois'; +$aFirstNames[] = 'Tina'; +$aFirstNames[] = 'Phyllis'; +$aFirstNames[] = 'Norma'; +$aFirstNames[] = 'Paula'; +$aFirstNames[] = 'Diana'; +$aFirstNames[] = 'Annie'; +$aFirstNames[] = 'Lillian'; +$aFirstNames[] = 'Emily'; +$aFirstNames[] = 'Robin'; +$aFirstNames[] = 'Peggy'; +$aFirstNames[] = 'Crystal'; +$aFirstNames[] = 'Gladys'; +$aFirstNames[] = 'Rita'; +$aFirstNames[] = 'Dawn'; +$aFirstNames[] = 'Connie'; +$aFirstNames[] = 'Florence'; +$aFirstNames[] = 'Tracy'; +$aFirstNames[] = 'Edna'; +$aFirstNames[] = 'Tiffany'; +$aFirstNames[] = 'Carmen'; +$aFirstNames[] = 'Rosa'; +$aFirstNames[] = 'Cindy'; +$aFirstNames[] = 'Grace'; +$aFirstNames[] = 'Wendy'; +$aFirstNames[] = 'Victoria'; +$aFirstNames[] = 'Edith'; +$aFirstNames[] = 'Kim'; +$aFirstNames[] = 'Sherry'; +$aFirstNames[] = 'Sylvia'; +$aFirstNames[] = 'Josephine'; +$aFirstNames[] = 'Thelma'; +$aFirstNames[] = 'Shannon'; +$aFirstNames[] = 'Sheila'; +$aFirstNames[] = 'Ethel'; +$aFirstNames[] = 'Ellen'; +$aFirstNames[] = 'Elaine'; +$aFirstNames[] = 'Marjorie'; +$aFirstNames[] = 'Carrie'; +$aFirstNames[] = 'Charlotte'; +$aFirstNames[] = 'Monica'; +$aFirstNames[] = 'Esther'; +$aFirstNames[] = 'Pauline'; +$aFirstNames[] = 'Emma'; +$aFirstNames[] = 'Juanita'; +$aFirstNames[] = 'Anita'; +$aFirstNames[] = 'Rhonda'; +$aFirstNames[] = 'Hazel'; +$aFirstNames[] = 'Amber'; +$aFirstNames[] = 'Eva'; +$aFirstNames[] = 'Debbie'; +$aFirstNames[] = 'April'; +$aFirstNames[] = 'Leslie'; +$aFirstNames[] = 'Clara'; +$aFirstNames[] = 'Lucille'; +$aFirstNames[] = 'Jamie'; +$aFirstNames[] = 'Joanne'; +$aFirstNames[] = 'Eleanor'; +$aFirstNames[] = 'Valerie'; +$aFirstNames[] = 'Danielle'; +$aFirstNames[] = 'Megan'; +$aFirstNames[] = 'Alicia'; +$aFirstNames[] = 'Suzanne'; +$aFirstNames[] = 'Michele'; +$aFirstNames[] = 'Gail'; +$aFirstNames[] = 'Bertha'; +$aFirstNames[] = 'Darlene'; +$aFirstNames[] = 'Veronica'; +$aFirstNames[] = 'Jill'; +$aFirstNames[] = 'Erin'; +$aFirstNames[] = 'Geraldine'; +$aFirstNames[] = 'Lauren'; +$aFirstNames[] = 'Cathy'; +$aFirstNames[] = 'Joann'; +$aFirstNames[] = 'Lorraine'; +$aFirstNames[] = 'Lynn'; +$aFirstNames[] = 'Sally'; +$aFirstNames[] = 'Regina'; +$aFirstNames[] = 'Erica'; +$aFirstNames[] = 'Beatrice'; +$aFirstNames[] = 'Dolores'; +$aFirstNames[] = 'Bernice'; +$aFirstNames[] = 'Audrey'; +$aFirstNames[] = 'Yvonne'; +$aFirstNames[] = 'Annette'; +$aFirstNames[] = 'June'; +$aFirstNames[] = 'Samantha'; +$aFirstNames[] = 'Marion'; +$aFirstNames[] = 'Dana'; +$aFirstNames[] = 'Stacy'; +$aFirstNames[] = 'Ana'; +$aFirstNames[] = 'Renee'; +$aFirstNames[] = 'Ida'; +$aFirstNames[] = 'Vivian'; +$aFirstNames[] = 'Roberta'; +$aFirstNames[] = 'Holly'; +$aFirstNames[] = 'Brittany'; +$aFirstNames[] = 'Melanie'; +$aFirstNames[] = 'Loretta'; +$aFirstNames[] = 'Yolanda'; +$aFirstNames[] = 'Jeanette'; +$aFirstNames[] = 'Laurie'; +$aFirstNames[] = 'Katie'; +$aFirstNames[] = 'Kristen'; +$aFirstNames[] = 'Vanessa'; +$aFirstNames[] = 'Alma'; +$aFirstNames[] = 'Sue'; +$aFirstNames[] = 'Elsie'; +$aFirstNames[] = 'Beth'; +$aFirstNames[] = 'Jeanne'; +$aFirstNames[] = 'Vicki'; +$aFirstNames[] = 'Carla'; +$aFirstNames[] = 'Tara'; +$aFirstNames[] = 'Rosemary'; +$aFirstNames[] = 'Eileen'; +$aFirstNames[] = 'Terri'; +$aFirstNames[] = 'Gertrude'; +$aFirstNames[] = 'Lucy'; +$aFirstNames[] = 'Tonya'; +$aFirstNames[] = 'Ella'; +$aFirstNames[] = 'Stacey'; +$aFirstNames[] = 'Wilma'; +$aFirstNames[] = 'Gina'; +$aFirstNames[] = 'Kristin'; +$aFirstNames[] = 'Jessie'; +$aFirstNames[] = 'Natalie'; +$aFirstNames[] = 'Agnes'; +$aFirstNames[] = 'Vera'; +$aFirstNames[] = 'Willie'; +$aFirstNames[] = 'Charlene'; +$aFirstNames[] = 'Bessie'; +$aFirstNames[] = 'Delores'; +$aFirstNames[] = 'Melinda'; +$aFirstNames[] = 'Pearl'; +$aFirstNames[] = 'Arlene'; +$aFirstNames[] = 'Maureen'; +$aFirstNames[] = 'Colleen'; +$aFirstNames[] = 'Allison'; +$aFirstNames[] = 'Tamara'; +$aFirstNames[] = 'Joy'; +$aFirstNames[] = 'Georgia'; +$aFirstNames[] = 'Constance'; +$aFirstNames[] = 'Lillie'; +$aFirstNames[] = 'Claudia'; +$aFirstNames[] = 'Jackie'; +$aFirstNames[] = 'Marcia'; +$aFirstNames[] = 'Tanya'; +$aFirstNames[] = 'Nellie'; +$aFirstNames[] = 'Minnie'; +$aFirstNames[] = 'Marlene'; +$aFirstNames[] = 'Heidi'; +$aFirstNames[] = 'Glenda'; +$aFirstNames[] = 'Lydia'; +$aFirstNames[] = 'Viola'; +$aFirstNames[] = 'Courtney'; +$aFirstNames[] = 'Marian'; +$aFirstNames[] = 'Stella'; +$aFirstNames[] = 'Caroline'; +$aFirstNames[] = 'Dora'; +$aFirstNames[] = 'Jo'; +$aFirstNames[] = 'Vickie'; +$aFirstNames[] = 'Mattie'; +$aFirstNames[] = 'Terry'; +$aFirstNames[] = 'Maxine'; +$aFirstNames[] = 'Irma'; +$aFirstNames[] = 'Mabel'; +$aFirstNames[] = 'Marsha'; +$aFirstNames[] = 'Myrtle'; +$aFirstNames[] = 'Lena'; +$aFirstNames[] = 'Christy'; +$aFirstNames[] = 'Deanna'; +$aFirstNames[] = 'Patsy'; +$aFirstNames[] = 'Hilda'; +$aFirstNames[] = 'Gwendolyn'; +$aFirstNames[] = 'Jennie'; +$aFirstNames[] = 'Nora'; +$aFirstNames[] = 'Margie'; +$aFirstNames[] = 'Nina'; +$aFirstNames[] = 'Cassandra'; +$aFirstNames[] = 'Leah'; +$aFirstNames[] = 'Penny'; +$aFirstNames[] = 'Kay'; +$aFirstNames[] = 'Priscilla'; +$aFirstNames[] = 'Naomi'; +$aFirstNames[] = 'Carole'; +$aFirstNames[] = 'Brandy'; +$aFirstNames[] = 'Olga'; +$aFirstNames[] = 'Billie'; +$aFirstNames[] = 'Dianne'; +$aFirstNames[] = 'Tracey'; +$aFirstNames[] = 'Leona'; +$aFirstNames[] = 'Jenny'; +$aFirstNames[] = 'Felicia'; +$aFirstNames[] = 'Sonia'; +$aFirstNames[] = 'Miriam'; +$aFirstNames[] = 'Velma'; +$aFirstNames[] = 'Becky'; +$aFirstNames[] = 'Bobbie'; +$aFirstNames[] = 'Violet'; +$aFirstNames[] = 'Kristina'; +$aFirstNames[] = 'Toni'; +$aFirstNames[] = 'Misty'; +$aFirstNames[] = 'Mae'; +$aFirstNames[] = 'Shelly'; +$aFirstNames[] = 'Daisy'; +$aFirstNames[] = 'Ramona'; +$aFirstNames[] = 'Sherri'; +$aFirstNames[] = 'Erika'; +$aFirstNames[] = 'Katrina'; +$aFirstNames[] = 'Claire'; +$aFirstNames[] = 'Lindsey'; +$aFirstNames[] = 'Lindsay'; +$aFirstNames[] = 'Geneva'; +$aFirstNames[] = 'Guadalupe'; +$aFirstNames[] = 'Belinda'; +$aFirstNames[] = 'Margarita'; +$aFirstNames[] = 'Sheryl'; +$aFirstNames[] = 'Cora'; +$aFirstNames[] = 'Faye'; +$aFirstNames[] = 'Ada'; +$aFirstNames[] = 'Natasha'; +$aFirstNames[] = 'Sabrina'; +$aFirstNames[] = 'Isabel'; +$aFirstNames[] = 'Marguerite'; +$aFirstNames[] = 'Hattie'; +$aFirstNames[] = 'Harriet'; +$aFirstNames[] = 'Molly'; +$aFirstNames[] = 'Cecilia'; +$aFirstNames[] = 'Kristi'; +$aFirstNames[] = 'Brandi'; +$aFirstNames[] = 'Blanche'; +$aFirstNames[] = 'Sandy'; +$aFirstNames[] = 'Rosie'; +$aFirstNames[] = 'Joanna'; +$aFirstNames[] = 'Iris'; +$aFirstNames[] = 'Eunice'; +$aFirstNames[] = 'Angie'; +$aFirstNames[] = 'Inez'; +$aFirstNames[] = 'Lynda'; +$aFirstNames[] = 'Madeline'; +$aFirstNames[] = 'Amelia'; +$aFirstNames[] = 'Alberta'; +$aFirstNames[] = 'Genevieve'; +$aFirstNames[] = 'Monique'; +$aFirstNames[] = 'Jodi'; +$aFirstNames[] = 'Janie'; +$aFirstNames[] = 'Maggie'; +$aFirstNames[] = 'Kayla'; +$aFirstNames[] = 'Sonya'; +$aFirstNames[] = 'Jan'; +$aFirstNames[] = 'Lee'; +$aFirstNames[] = 'Kristine'; +$aFirstNames[] = 'Candace'; +$aFirstNames[] = 'Fannie'; +$aFirstNames[] = 'Maryann'; +$aFirstNames[] = 'Opal'; +$aFirstNames[] = 'Alison'; +$aFirstNames[] = 'Yvette'; +$aFirstNames[] = 'Melody'; +$aFirstNames[] = 'Luz'; +$aFirstNames[] = 'Susie'; +$aFirstNames[] = 'Olivia'; +$aFirstNames[] = 'Flora'; +$aFirstNames[] = 'Shelley'; +$aFirstNames[] = 'Kristy'; +$aFirstNames[] = 'Mamie'; +$aFirstNames[] = 'Lula'; +$aFirstNames[] = 'Lola'; +$aFirstNames[] = 'Verna'; +$aFirstNames[] = 'Beulah'; +$aFirstNames[] = 'Antoinette'; +$aFirstNames[] = 'Candice'; +$aFirstNames[] = 'Juana'; +$aFirstNames[] = 'Jeannette'; +$aFirstNames[] = 'Pam'; +$aFirstNames[] = 'Kelli'; +$aFirstNames[] = 'Hannah'; +$aFirstNames[] = 'Whitney'; +$aFirstNames[] = 'Bridget'; +$aFirstNames[] = 'Karla'; +$aFirstNames[] = 'Celia'; +$aFirstNames[] = 'Latoya'; +$aFirstNames[] = 'Patty'; +$aFirstNames[] = 'Shelia'; +$aFirstNames[] = 'Gayle'; +$aFirstNames[] = 'Della'; +$aFirstNames[] = 'Vicky'; +$aFirstNames[] = 'Lynne'; +$aFirstNames[] = 'Sheri'; +$aFirstNames[] = 'Marianne'; +$aFirstNames[] = 'Kara'; +$aFirstNames[] = 'Jacquelyn'; +$aFirstNames[] = 'Erma'; +$aFirstNames[] = 'Blanca'; +$aFirstNames[] = 'Myra'; +$aFirstNames[] = 'Leticia'; +$aFirstNames[] = 'Pat'; +$aFirstNames[] = 'Krista'; +$aFirstNames[] = 'Roxanne'; +$aFirstNames[] = 'Angelica'; +$aFirstNames[] = 'Johnnie'; +$aFirstNames[] = 'Robyn'; +$aFirstNames[] = 'Francis'; +$aFirstNames[] = 'Adrienne'; +$aFirstNames[] = 'Rosalie'; +$aFirstNames[] = 'Alexandra'; +$aFirstNames[] = 'Brooke'; +$aFirstNames[] = 'Bethany'; +$aFirstNames[] = 'Sadie'; +$aFirstNames[] = 'Bernadette'; +$aFirstNames[] = 'Traci'; +$aFirstNames[] = 'Jody'; +$aFirstNames[] = 'Kendra'; +$aFirstNames[] = 'Jasmine'; +$aFirstNames[] = 'Nichole'; +$aFirstNames[] = 'Rachael'; +$aFirstNames[] = 'Chelsea'; +$aFirstNames[] = 'Mable'; +$aFirstNames[] = 'Ernestine'; +$aFirstNames[] = 'Muriel'; +$aFirstNames[] = 'Marcella'; +$aFirstNames[] = 'Elena'; +$aFirstNames[] = 'Krystal'; +$aFirstNames[] = 'Angelina'; +$aFirstNames[] = 'Nadine'; +$aFirstNames[] = 'Kari'; +$aFirstNames[] = 'Estelle'; +$aFirstNames[] = 'Dianna'; +$aFirstNames[] = 'Paulette'; +$aFirstNames[] = 'Lora'; +$aFirstNames[] = 'Mona'; +$aFirstNames[] = 'Doreen'; +$aFirstNames[] = 'Rosemarie'; +$aFirstNames[] = 'Angel'; +$aFirstNames[] = 'Desiree'; +$aFirstNames[] = 'Antonia'; +$aFirstNames[] = 'Hope'; +$aFirstNames[] = 'Ginger'; +$aFirstNames[] = 'Janis'; +$aFirstNames[] = 'Betsy'; +$aFirstNames[] = 'Christie'; +$aFirstNames[] = 'Freda'; +$aFirstNames[] = 'Mercedes'; +$aFirstNames[] = 'Meredith'; +$aFirstNames[] = 'Lynette'; +$aFirstNames[] = 'Teri'; +$aFirstNames[] = 'Cristina'; +$aFirstNames[] = 'Eula'; +$aFirstNames[] = 'Leigh'; +$aFirstNames[] = 'Meghan'; +$aFirstNames[] = 'Sophia'; +$aFirstNames[] = 'Eloise'; +$aFirstNames[] = 'Rochelle'; +$aFirstNames[] = 'Gretchen'; +$aFirstNames[] = 'Cecelia'; +$aFirstNames[] = 'Raquel'; +$aFirstNames[] = 'Henrietta'; +$aFirstNames[] = 'Alyssa'; +$aFirstNames[] = 'Jana'; +$aFirstNames[] = 'Kelley'; +$aFirstNames[] = 'Gwen'; +$aFirstNames[] = 'Kerry'; +$aFirstNames[] = 'Jenna'; +$aFirstNames[] = 'Tricia'; +$aFirstNames[] = 'Laverne'; +$aFirstNames[] = 'Olive'; +$aFirstNames[] = 'Alexis'; +$aFirstNames[] = 'Tasha'; +$aFirstNames[] = 'Silvia'; +$aFirstNames[] = 'Elvira'; +$aFirstNames[] = 'Casey'; +$aFirstNames[] = 'Delia'; +$aFirstNames[] = 'Sophie'; +$aFirstNames[] = 'Kate'; +$aFirstNames[] = 'Patti'; +$aFirstNames[] = 'Lorena'; +$aFirstNames[] = 'Kellie'; +$aFirstNames[] = 'Sonja'; +$aFirstNames[] = 'Lila'; +$aFirstNames[] = 'Lana'; +$aFirstNames[] = 'Darla'; +$aFirstNames[] = 'May'; +$aFirstNames[] = 'Mindy'; +$aFirstNames[] = 'Essie'; +$aFirstNames[] = 'Mandy'; +$aFirstNames[] = 'Lorene'; +$aFirstNames[] = 'Elsa'; +$aFirstNames[] = 'Josefina'; +$aFirstNames[] = 'Jeannie'; +$aFirstNames[] = 'Miranda'; +$aFirstNames[] = 'Dixie'; +$aFirstNames[] = 'Lucia'; +$aFirstNames[] = 'Marta'; +$aFirstNames[] = 'Faith'; +$aFirstNames[] = 'Lela'; +$aFirstNames[] = 'Johanna'; +$aFirstNames[] = 'Shari'; +$aFirstNames[] = 'Camille'; +$aFirstNames[] = 'Tami'; +$aFirstNames[] = 'Shawna'; +$aFirstNames[] = 'Elisa'; +$aFirstNames[] = 'Ebony'; +$aFirstNames[] = 'Melba'; +$aFirstNames[] = 'Ora'; +$aFirstNames[] = 'Nettie'; +$aFirstNames[] = 'Tabitha'; +$aFirstNames[] = 'Ollie'; +$aFirstNames[] = 'Jaime'; +$aFirstNames[] = 'Winifred'; +$aFirstNames[] = 'Kristie'; +$aFirstNames[] = 'Marina'; +$aFirstNames[] = 'Alisha'; +$aFirstNames[] = 'Aimee'; +$aFirstNames[] = 'Rena'; +$aFirstNames[] = 'Myrna'; +$aFirstNames[] = 'Marla'; +$aFirstNames[] = 'Tammie'; +$aFirstNames[] = 'Latasha'; +$aFirstNames[] = 'Bonita'; +$aFirstNames[] = 'Patrice'; +$aFirstNames[] = 'Ronda'; +$aFirstNames[] = 'Sherrie'; +$aFirstNames[] = 'Addie'; +$aFirstNames[] = 'Francine'; +$aFirstNames[] = 'Deloris'; +$aFirstNames[] = 'Stacie'; +$aFirstNames[] = 'Adriana'; +$aFirstNames[] = 'Cheri'; +$aFirstNames[] = 'Shelby'; +$aFirstNames[] = 'Abigail'; +$aFirstNames[] = 'Celeste'; +$aFirstNames[] = 'Jewel'; +$aFirstNames[] = 'Cara'; +$aFirstNames[] = 'Adele'; +$aFirstNames[] = 'Rebekah'; +$aFirstNames[] = 'Lucinda'; +$aFirstNames[] = 'Dorthy'; +$aFirstNames[] = 'Chris'; +$aFirstNames[] = 'Effie'; +$aFirstNames[] = 'Trina'; +$aFirstNames[] = 'Reba'; +$aFirstNames[] = 'Shawn'; +$aFirstNames[] = 'Sallie'; +$aFirstNames[] = 'Aurora'; +$aFirstNames[] = 'Lenora'; +$aFirstNames[] = 'Etta'; +$aFirstNames[] = 'Lottie'; +$aFirstNames[] = 'Kerri'; +$aFirstNames[] = 'Trisha'; +$aFirstNames[] = 'Nikki'; +$aFirstNames[] = 'Estella'; +$aFirstNames[] = 'Francisca'; +$aFirstNames[] = 'Josie'; +$aFirstNames[] = 'Tracie'; +$aFirstNames[] = 'Marissa'; +$aFirstNames[] = 'Karin'; +$aFirstNames[] = 'Brittney'; +$aFirstNames[] = 'Janelle'; + +$aFirstNames[] = 'Lourdes'; +$aFirstNames[] = 'Laurel'; +$aFirstNames[] = 'Helene'; +$aFirstNames[] = 'Fern'; +$aFirstNames[] = 'Elva'; +$aFirstNames[] = 'Corinne'; +$aFirstNames[] = 'Kelsey'; +$aFirstNames[] = 'Ina'; +$aFirstNames[] = 'Bettie'; +$aFirstNames[] = 'Elisabeth'; +$aFirstNames[] = 'Aida'; +$aFirstNames[] = 'Caitlin'; +$aFirstNames[] = 'Ingrid'; +$aFirstNames[] = 'Iva'; +$aFirstNames[] = 'Eugenia'; +$aFirstNames[] = 'Christa'; +$aFirstNames[] = 'Goldie'; +$aFirstNames[] = 'Cassie'; +$aFirstNames[] = 'Maude'; +$aFirstNames[] = 'Jenifer'; +$aFirstNames[] = 'Therese'; +$aFirstNames[] = 'Frankie'; +$aFirstNames[] = 'Dena'; +$aFirstNames[] = 'Lorna'; +$aFirstNames[] = 'Janette'; +$aFirstNames[] = 'Latonya'; +$aFirstNames[] = 'Candy'; +$aFirstNames[] = 'Morgan'; +$aFirstNames[] = 'Consuelo'; +$aFirstNames[] = 'Tamika'; +$aFirstNames[] = 'Rosetta'; +$aFirstNames[] = 'Debora'; +$aFirstNames[] = 'Cherie'; +$aFirstNames[] = 'Polly'; +$aFirstNames[] = 'Dina'; +$aFirstNames[] = 'Jewell'; +$aFirstNames[] = 'Fay'; +$aFirstNames[] = 'Jillian'; +$aFirstNames[] = 'Dorothea'; +$aFirstNames[] = 'Nell'; +$aFirstNames[] = 'Trudy'; +$aFirstNames[] = 'Esperanza'; +$aFirstNames[] = 'Patrica'; +$aFirstNames[] = 'Kimberley'; +$aFirstNames[] = 'Shanna'; +$aFirstNames[] = 'Helena'; +$aFirstNames[] = 'Carolina'; +$aFirstNames[] = 'Cleo'; +$aFirstNames[] = 'Stefanie'; +$aFirstNames[] = 'Rosario'; +$aFirstNames[] = 'Ola'; +$aFirstNames[] = 'Janine'; +$aFirstNames[] = 'Mollie'; +$aFirstNames[] = 'Lupe'; +$aFirstNames[] = 'Alisa'; +$aFirstNames[] = 'Lou'; +$aFirstNames[] = 'Maribel'; +$aFirstNames[] = 'Susanne'; +$aFirstNames[] = 'Bette'; +$aFirstNames[] = 'Susana'; +$aFirstNames[] = 'Elise'; +$aFirstNames[] = 'Cecile'; +$aFirstNames[] = 'Isabelle'; +$aFirstNames[] = 'Lesley'; +$aFirstNames[] = 'Jocelyn'; +$aFirstNames[] = 'Paige'; +$aFirstNames[] = 'Joni'; +$aFirstNames[] = 'Rachelle'; +$aFirstNames[] = 'Leola'; +$aFirstNames[] = 'Daphne'; +$aFirstNames[] = 'Alta'; +$aFirstNames[] = 'Ester'; +$aFirstNames[] = 'Petra'; +$aFirstNames[] = 'Graciela'; +$aFirstNames[] = 'Imogene'; +$aFirstNames[] = 'Jolene'; +$aFirstNames[] = 'Keisha'; +$aFirstNames[] = 'Lacey'; +$aFirstNames[] = 'Glenna'; +$aFirstNames[] = 'Gabriela'; +$aFirstNames[] = 'Keri'; +$aFirstNames[] = 'Ursula'; +$aFirstNames[] = 'Lizzie'; +$aFirstNames[] = 'Kirsten'; +$aFirstNames[] = 'Shana'; +$aFirstNames[] = 'Adeline'; +$aFirstNames[] = 'Mayra'; +$aFirstNames[] = 'Jayne'; +$aFirstNames[] = 'Jaclyn'; +$aFirstNames[] = 'Gracie'; +$aFirstNames[] = 'Sondra'; +$aFirstNames[] = 'Carmela'; +$aFirstNames[] = 'Marisa'; +$aFirstNames[] = 'Rosalind'; +$aFirstNames[] = 'Charity'; +$aFirstNames[] = 'Tonia'; +$aFirstNames[] = 'Beatriz'; +$aFirstNames[] = 'Marisol'; +$aFirstNames[] = 'Clarice'; +$aFirstNames[] = 'Jeanine'; +$aFirstNames[] = 'Sheena'; +$aFirstNames[] = 'Angeline'; +$aFirstNames[] = 'Frieda'; +$aFirstNames[] = 'Lily'; +$aFirstNames[] = 'Robbie'; +$aFirstNames[] = 'Shauna'; +$aFirstNames[] = 'Millie'; +$aFirstNames[] = 'Claudette'; +$aFirstNames[] = 'Cathleen'; +$aFirstNames[] = 'Angelia'; +$aFirstNames[] = 'Gabrielle'; +$aFirstNames[] = 'Autumn'; +$aFirstNames[] = 'Katharine'; +$aFirstNames[] = 'Summer'; +$aFirstNames[] = 'Jodie'; +$aFirstNames[] = 'Staci'; +$aFirstNames[] = 'Lea'; +$aFirstNames[] = 'Christi'; +$aFirstNames[] = 'Jimmie'; +$aFirstNames[] = 'Justine'; +$aFirstNames[] = 'Elma'; +$aFirstNames[] = 'Luella'; +$aFirstNames[] = 'Margret'; +$aFirstNames[] = 'Dominique'; +$aFirstNames[] = 'Socorro'; +$aFirstNames[] = 'Rene'; +$aFirstNames[] = 'Martina'; +$aFirstNames[] = 'Margo'; +$aFirstNames[] = 'Mavis'; +$aFirstNames[] = 'Callie'; +$aFirstNames[] = 'Bobbi'; +$aFirstNames[] = 'Maritza'; +$aFirstNames[] = 'Lucile'; +$aFirstNames[] = 'Leanne'; +$aFirstNames[] = 'Jeannine'; +$aFirstNames[] = 'Deana'; +$aFirstNames[] = 'Aileen'; +$aFirstNames[] = 'Lorie'; +$aFirstNames[] = 'Ladonna'; +$aFirstNames[] = 'Willa'; +$aFirstNames[] = 'Manuela'; +$aFirstNames[] = 'Gale'; +$aFirstNames[] = 'Selma'; +$aFirstNames[] = 'Dolly'; +$aFirstNames[] = 'Sybil'; +$aFirstNames[] = 'Abby'; +$aFirstNames[] = 'Lara'; +$aFirstNames[] = 'Dale'; +$aFirstNames[] = 'Ivy'; +$aFirstNames[] = 'Dee'; +$aFirstNames[] = 'Winnie'; +$aFirstNames[] = 'Marcy'; +$aFirstNames[] = 'Luisa'; +$aFirstNames[] = 'Jeri'; +$aFirstNames[] = 'Magdalena'; +$aFirstNames[] = 'Ofelia'; +$aFirstNames[] = 'Meagan'; +$aFirstNames[] = 'Audra'; +$aFirstNames[] = 'Matilda'; +$aFirstNames[] = 'Leila'; +$aFirstNames[] = 'Cornelia'; +$aFirstNames[] = 'Bianca'; +$aFirstNames[] = 'Simone'; +$aFirstNames[] = 'Bettye'; +$aFirstNames[] = 'Randi'; +$aFirstNames[] = 'Virgie'; +$aFirstNames[] = 'Latisha'; +$aFirstNames[] = 'Barbra'; +$aFirstNames[] = 'Georgina'; +$aFirstNames[] = 'Eliza'; +$aFirstNames[] = 'Leann'; +$aFirstNames[] = 'Bridgette'; +$aFirstNames[] = 'Rhoda'; +$aFirstNames[] = 'Haley'; +$aFirstNames[] = 'Adela'; +$aFirstNames[] = 'Nola'; +$aFirstNames[] = 'Bernadine'; +$aFirstNames[] = 'Flossie'; +$aFirstNames[] = 'Ila'; +$aFirstNames[] = 'Greta'; +$aFirstNames[] = 'Ruthie'; +$aFirstNames[] = 'Nelda'; +$aFirstNames[] = 'Minerva'; +$aFirstNames[] = 'Lilly'; +$aFirstNames[] = 'Terrie'; +$aFirstNames[] = 'Letha'; +$aFirstNames[] = 'Hilary'; +$aFirstNames[] = 'Estela'; +$aFirstNames[] = 'Valarie'; +$aFirstNames[] = 'Brianna'; +$aFirstNames[] = 'Rosalyn'; +$aFirstNames[] = 'Earline'; +$aFirstNames[] = 'Catalina'; +$aFirstNames[] = 'Ava'; +$aFirstNames[] = 'Mia'; +$aFirstNames[] = 'Clarissa'; +$aFirstNames[] = 'Lidia'; +$aFirstNames[] = 'Corrine'; +$aFirstNames[] = 'Alexandria'; +$aFirstNames[] = 'Concepcion'; +$aFirstNames[] = 'Tia'; +$aFirstNames[] = 'Sharron'; +$aFirstNames[] = 'Rae'; +$aFirstNames[] = 'Dona'; +$aFirstNames[] = 'Ericka'; +$aFirstNames[] = 'Jami'; +$aFirstNames[] = 'Elnora'; +$aFirstNames[] = 'Chandra'; +$aFirstNames[] = 'Lenore'; +$aFirstNames[] = 'Neva'; +$aFirstNames[] = 'Marylou'; +$aFirstNames[] = 'Melisa'; +$aFirstNames[] = 'Tabatha'; +$aFirstNames[] = 'Serena'; +$aFirstNames[] = 'Avis'; +$aFirstNames[] = 'Allie'; +$aFirstNames[] = 'Sofia'; +$aFirstNames[] = 'Jeanie'; +$aFirstNames[] = 'Odessa'; +$aFirstNames[] = 'Nannie'; +$aFirstNames[] = 'Harriett'; +$aFirstNames[] = 'James'; +$aFirstNames[] = 'John'; +$aFirstNames[] = 'Robert'; +$aFirstNames[] = 'Michael'; +$aFirstNames[] = 'William'; +$aFirstNames[] = 'David'; +$aFirstNames[] = 'Richard'; +$aFirstNames[] = 'Charles'; +$aFirstNames[] = 'Joseph'; +$aFirstNames[] = 'Thomas'; +$aFirstNames[] = 'Christopher'; +$aFirstNames[] = 'Daniel'; +$aFirstNames[] = 'Paul'; +$aFirstNames[] = 'Mark'; +$aFirstNames[] = 'Donald'; +$aFirstNames[] = 'George'; +$aFirstNames[] = 'Kenneth'; +$aFirstNames[] = 'Steven'; +$aFirstNames[] = 'Edward'; +$aFirstNames[] = 'Brian'; +$aFirstNames[] = 'Ronald'; +$aFirstNames[] = 'Anthony'; +$aFirstNames[] = 'Kevin'; +$aFirstNames[] = 'Jason'; +$aFirstNames[] = 'Matthew'; +$aFirstNames[] = 'Gary'; +$aFirstNames[] = 'Timothy'; +$aFirstNames[] = 'Jose'; +$aFirstNames[] = 'Larry'; +$aFirstNames[] = 'Jeffrey'; +$aFirstNames[] = 'Frank'; +$aFirstNames[] = 'Scott'; +$aFirstNames[] = 'Eric'; +$aFirstNames[] = 'Stephen'; +$aFirstNames[] = 'Andrew'; +$aFirstNames[] = 'Raymond'; +$aFirstNames[] = 'Gregory'; +$aFirstNames[] = 'Joshua'; +$aFirstNames[] = 'Jerry'; +$aFirstNames[] = 'Dennis'; +$aFirstNames[] = 'Walter'; +$aFirstNames[] = 'Patrick'; +$aFirstNames[] = 'Peter'; +$aFirstNames[] = 'Harold'; +$aFirstNames[] = 'Douglas'; +$aFirstNames[] = 'Henry'; +$aFirstNames[] = 'Carl'; +$aFirstNames[] = 'Arthur'; +$aFirstNames[] = 'Ryan'; +$aFirstNames[] = 'Roger'; +$aFirstNames[] = 'Joe'; +$aFirstNames[] = 'Juan'; +$aFirstNames[] = 'Jack'; +$aFirstNames[] = 'Albert'; +$aFirstNames[] = 'Jonathan'; +$aFirstNames[] = 'Justin'; +$aFirstNames[] = 'Terry'; +$aFirstNames[] = 'Gerald'; +$aFirstNames[] = 'Keith'; +$aFirstNames[] = 'Samuel'; +$aFirstNames[] = 'Willie'; +$aFirstNames[] = 'Ralph'; +$aFirstNames[] = 'Lawrence'; +$aFirstNames[] = 'Nicholas'; +$aFirstNames[] = 'Roy'; +$aFirstNames[] = 'Benjamin'; +$aFirstNames[] = 'Bruce'; +$aFirstNames[] = 'Brandon'; +$aFirstNames[] = 'Adam'; +$aFirstNames[] = 'Harry'; +$aFirstNames[] = 'Fred'; +$aFirstNames[] = 'Wayne'; +$aFirstNames[] = 'Billy'; +$aFirstNames[] = 'Steve'; +$aFirstNames[] = 'Louis'; +$aFirstNames[] = 'Jeremy'; +$aFirstNames[] = 'Aaron'; +$aFirstNames[] = 'Randy'; +$aFirstNames[] = 'Howard'; +$aFirstNames[] = 'Eugene'; +$aFirstNames[] = 'Carlos'; +$aFirstNames[] = 'Russell'; +$aFirstNames[] = 'Bobby'; +$aFirstNames[] = 'Victor'; +$aFirstNames[] = 'Martin'; +$aFirstNames[] = 'Ernest'; +$aFirstNames[] = 'Phillip'; +$aFirstNames[] = 'Todd'; +$aFirstNames[] = 'Jesse'; +$aFirstNames[] = 'Craig'; +$aFirstNames[] = 'Alan'; +$aFirstNames[] = 'Shawn'; +$aFirstNames[] = 'Clarence'; +$aFirstNames[] = 'Sean'; +$aFirstNames[] = 'Philip'; +$aFirstNames[] = 'Chris'; +$aFirstNames[] = 'Johnny'; +$aFirstNames[] = 'Earl'; +$aFirstNames[] = 'Jimmy'; +$aFirstNames[] = 'Antonio'; +$aFirstNames[] = 'Danny'; +$aFirstNames[] = 'Bryan'; +$aFirstNames[] = 'Tony'; +$aFirstNames[] = 'Luis'; +$aFirstNames[] = 'Mike'; +$aFirstNames[] = 'Stanley'; +$aFirstNames[] = 'Leonard'; +$aFirstNames[] = 'Nathan'; +$aFirstNames[] = 'Dale'; +$aFirstNames[] = 'Manuel'; +$aFirstNames[] = 'Rodney'; +$aFirstNames[] = 'Curtis'; +$aFirstNames[] = 'Norman'; +$aFirstNames[] = 'Allen'; +$aFirstNames[] = 'Marvin'; +$aFirstNames[] = 'Vincent'; +$aFirstNames[] = 'Glenn'; +$aFirstNames[] = 'Jeffery'; +$aFirstNames[] = 'Travis'; +$aFirstNames[] = 'Jeff'; +$aFirstNames[] = 'Chad'; +$aFirstNames[] = 'Jacob'; +$aFirstNames[] = 'Lee'; +$aFirstNames[] = 'Melvin'; +$aFirstNames[] = 'Alfred'; +$aFirstNames[] = 'Kyle'; +$aFirstNames[] = 'Francis'; +$aFirstNames[] = 'Bradley'; +$aFirstNames[] = 'Jesus'; +$aFirstNames[] = 'Herbert'; +$aFirstNames[] = 'Frederick'; +$aFirstNames[] = 'Ray'; +$aFirstNames[] = 'Joel'; +$aFirstNames[] = 'Edwin'; +$aFirstNames[] = 'Don'; +$aFirstNames[] = 'Eddie'; +$aFirstNames[] = 'Ricky'; +$aFirstNames[] = 'Troy'; +$aFirstNames[] = 'Randall'; +$aFirstNames[] = 'Barry'; +$aFirstNames[] = 'Alexander'; +$aFirstNames[] = 'Bernard'; +$aFirstNames[] = 'Mario'; +$aFirstNames[] = 'Leroy'; +$aFirstNames[] = 'Francisco'; +$aFirstNames[] = 'Marcus'; +$aFirstNames[] = 'Micheal'; +$aFirstNames[] = 'Theodore'; +$aFirstNames[] = 'Clifford'; +$aFirstNames[] = 'Miguel'; +$aFirstNames[] = 'Oscar'; +$aFirstNames[] = 'Jay'; +$aFirstNames[] = 'Jim'; +$aFirstNames[] = 'Tom'; +$aFirstNames[] = 'Calvin'; +$aFirstNames[] = 'Alex'; +$aFirstNames[] = 'Jon'; +$aFirstNames[] = 'Ronnie'; +$aFirstNames[] = 'Bill'; +$aFirstNames[] = 'Lloyd'; +$aFirstNames[] = 'Tommy'; +$aFirstNames[] = 'Leon'; +$aFirstNames[] = 'Derek'; +$aFirstNames[] = 'Warren'; +$aFirstNames[] = 'Darrell'; +$aFirstNames[] = 'Jerome'; +$aFirstNames[] = 'Floyd'; +$aFirstNames[] = 'Leo'; +$aFirstNames[] = 'Alvin'; +$aFirstNames[] = 'Tim'; +$aFirstNames[] = 'Wesley'; +$aFirstNames[] = 'Gordon'; +$aFirstNames[] = 'Dean'; +$aFirstNames[] = 'Greg'; +$aFirstNames[] = 'Jorge'; +$aFirstNames[] = 'Dustin'; +$aFirstNames[] = 'Pedro'; +$aFirstNames[] = 'Derrick'; +$aFirstNames[] = 'Dan'; +$aFirstNames[] = 'Lewis'; +$aFirstNames[] = 'Zachary'; +$aFirstNames[] = 'Corey'; +$aFirstNames[] = 'Herman'; +$aFirstNames[] = 'Maurice'; +$aFirstNames[] = 'Vernon'; +$aFirstNames[] = 'Roberto'; +$aFirstNames[] = 'Clyde'; +$aFirstNames[] = 'Glen'; +$aFirstNames[] = 'Hector'; +$aFirstNames[] = 'Shane'; +$aFirstNames[] = 'Ricardo'; +$aFirstNames[] = 'Sam'; +$aFirstNames[] = 'Rick'; +$aFirstNames[] = 'Lester'; +$aFirstNames[] = 'Brent'; +$aFirstNames[] = 'Ramon'; +$aFirstNames[] = 'Charlie'; +$aFirstNames[] = 'Tyler'; +$aFirstNames[] = 'Gilbert'; +$aFirstNames[] = 'Gene'; +$aFirstNames[] = 'Marc'; +$aFirstNames[] = 'Reginald'; +$aFirstNames[] = 'Ruben'; +$aFirstNames[] = 'Brett'; +$aFirstNames[] = 'Angel'; +$aFirstNames[] = 'Nathaniel'; +$aFirstNames[] = 'Rafael'; +$aFirstNames[] = 'Leslie'; +$aFirstNames[] = 'Edgar'; +$aFirstNames[] = 'Milton'; +$aFirstNames[] = 'Raul'; +$aFirstNames[] = 'Ben'; +$aFirstNames[] = 'Chester'; +$aFirstNames[] = 'Cecil'; +$aFirstNames[] = 'Duane'; +$aFirstNames[] = 'Franklin'; +$aFirstNames[] = 'Andre'; +$aFirstNames[] = 'Elmer'; +$aFirstNames[] = 'Brad'; +$aFirstNames[] = 'Gabriel'; +$aFirstNames[] = 'Ron'; +$aFirstNames[] = 'Mitchell'; +$aFirstNames[] = 'Roland'; +$aFirstNames[] = 'Arnold'; +$aFirstNames[] = 'Harvey'; +$aFirstNames[] = 'Jared'; +$aFirstNames[] = 'Adrian'; +$aFirstNames[] = 'Karl'; +$aFirstNames[] = 'Cory'; +$aFirstNames[] = 'Claude'; +$aFirstNames[] = 'Erik'; +$aFirstNames[] = 'Darryl'; +$aFirstNames[] = 'Jamie'; +$aFirstNames[] = 'Neil'; +$aFirstNames[] = 'Jessie'; +$aFirstNames[] = 'Christian'; +$aFirstNames[] = 'Javier'; +$aFirstNames[] = 'Fernando'; +$aFirstNames[] = 'Clinton'; +$aFirstNames[] = 'Ted'; +$aFirstNames[] = 'Mathew'; +$aFirstNames[] = 'Tyrone'; +$aFirstNames[] = 'Darren'; +$aFirstNames[] = 'Lonnie'; +$aFirstNames[] = 'Lance'; +$aFirstNames[] = 'Cody'; +$aFirstNames[] = 'Julio'; +$aFirstNames[] = 'Kelly'; +$aFirstNames[] = 'Kurt'; +$aFirstNames[] = 'Allan'; +$aFirstNames[] = 'Nelson'; +$aFirstNames[] = 'Guy'; +$aFirstNames[] = 'Clayton'; +$aFirstNames[] = 'Hugh'; +$aFirstNames[] = 'Max'; +$aFirstNames[] = 'Dwayne'; +$aFirstNames[] = 'Dwight'; +$aFirstNames[] = 'Armando'; +$aFirstNames[] = 'Felix'; +$aFirstNames[] = 'Jimmie'; +$aFirstNames[] = 'Everett'; +$aFirstNames[] = 'Jordan'; +$aFirstNames[] = 'Ian'; +$aFirstNames[] = 'Wallace'; +$aFirstNames[] = 'Ken'; +$aFirstNames[] = 'Bob'; +$aFirstNames[] = 'Jaime'; +$aFirstNames[] = 'Casey'; +$aFirstNames[] = 'Alfredo'; +$aFirstNames[] = 'Alberto'; +$aFirstNames[] = 'Dave'; +$aFirstNames[] = 'Ivan'; +$aFirstNames[] = 'Johnnie'; +$aFirstNames[] = 'Sidney'; +$aFirstNames[] = 'Byron'; +$aFirstNames[] = 'Julian'; +$aFirstNames[] = 'Isaac'; +$aFirstNames[] = 'Morris'; +$aFirstNames[] = 'Clifton'; +$aFirstNames[] = 'Willard'; +$aFirstNames[] = 'Daryl'; +$aFirstNames[] = 'Ross'; +$aFirstNames[] = 'Virgil'; +$aFirstNames[] = 'Andy'; +$aFirstNames[] = 'Marshall'; +$aFirstNames[] = 'Salvador'; +$aFirstNames[] = 'Perry'; +$aFirstNames[] = 'Kirk'; +$aFirstNames[] = 'Sergio'; +$aFirstNames[] = 'Marion'; +$aFirstNames[] = 'Tracy'; +$aFirstNames[] = 'Seth'; +$aFirstNames[] = 'Kent'; +$aFirstNames[] = 'Terrance'; +$aFirstNames[] = 'Rene'; +$aFirstNames[] = 'Eduardo'; +$aFirstNames[] = 'Terrence'; +$aFirstNames[] = 'Enrique'; +$aFirstNames[] = 'Freddie'; +$aFirstNames[] = 'Wade'; +$aFirstNames[] = 'Austin'; +$aFirstNames[] = 'Stuart'; +$aFirstNames[] = 'Fredrick'; +$aFirstNames[] = 'Arturo'; +$aFirstNames[] = 'Alejandro'; +$aFirstNames[] = 'Jackie'; +$aFirstNames[] = 'Joey'; +$aFirstNames[] = 'Nick'; +$aFirstNames[] = 'Luther'; +$aFirstNames[] = 'Wendell'; +$aFirstNames[] = 'Jeremiah'; +$aFirstNames[] = 'Evan'; +$aFirstNames[] = 'Julius'; +$aFirstNames[] = 'Dana'; +$aFirstNames[] = 'Donnie'; +$aFirstNames[] = 'Otis'; +$aFirstNames[] = 'Shannon'; +$aFirstNames[] = 'Trevor'; +$aFirstNames[] = 'Oliver'; +$aFirstNames[] = 'Luke'; +$aFirstNames[] = 'Homer'; +$aFirstNames[] = 'Gerard'; +$aFirstNames[] = 'Doug'; +$aFirstNames[] = 'Kenny'; +$aFirstNames[] = 'Hubert'; +$aFirstNames[] = 'Angelo'; +$aFirstNames[] = 'Shaun'; +$aFirstNames[] = 'Lyle'; +$aFirstNames[] = 'Matt'; +$aFirstNames[] = 'Lynn'; +$aFirstNames[] = 'Alfonso'; +$aFirstNames[] = 'Orlando'; +$aFirstNames[] = 'Rex'; +$aFirstNames[] = 'Carlton'; +$aFirstNames[] = 'Ernesto'; +$aFirstNames[] = 'Cameron'; +$aFirstNames[] = 'Neal'; +$aFirstNames[] = 'Pablo'; +$aFirstNames[] = 'Lorenzo'; +$aFirstNames[] = 'Omar'; +$aFirstNames[] = 'Wilbur'; +$aFirstNames[] = 'Blake'; +$aFirstNames[] = 'Grant'; +$aFirstNames[] = 'Horace'; +$aFirstNames[] = 'Roderick'; +$aFirstNames[] = 'Kerry'; +$aFirstNames[] = 'Abraham'; +$aFirstNames[] = 'Willis'; +$aFirstNames[] = 'Rickey'; +$aFirstNames[] = 'Jean'; +$aFirstNames[] = 'Ira'; +$aFirstNames[] = 'Andres'; +$aFirstNames[] = 'Cesar'; +$aFirstNames[] = 'Johnathan'; +$aFirstNames[] = 'Malcolm'; +$aFirstNames[] = 'Rudolph'; +$aFirstNames[] = 'Damon'; +$aFirstNames[] = 'Kelvin'; +$aFirstNames[] = 'Rudy'; +$aFirstNames[] = 'Preston'; +$aFirstNames[] = 'Alton'; +$aFirstNames[] = 'Archie'; +$aFirstNames[] = 'Marco'; +$aFirstNames[] = 'Wm'; +$aFirstNames[] = 'Pete'; +$aFirstNames[] = 'Randolph'; +$aFirstNames[] = 'Garry'; +$aFirstNames[] = 'Geoffrey'; +$aFirstNames[] = 'Jonathon'; +$aFirstNames[] = 'Felipe'; +$aFirstNames[] = 'Bennie'; +$aFirstNames[] = 'Gerardo'; +$aFirstNames[] = 'Ed'; +$aFirstNames[] = 'Dominic'; +$aFirstNames[] = 'Robin'; +$aFirstNames[] = 'Loren'; +$aFirstNames[] = 'Delbert'; +$aFirstNames[] = 'Colin'; +$aFirstNames[] = 'Guillermo'; +$aFirstNames[] = 'Earnest'; +$aFirstNames[] = 'Lucas'; +$aFirstNames[] = 'Benny'; +$aFirstNames[] = 'Noel'; +$aFirstNames[] = 'Spencer'; +$aFirstNames[] = 'Rodolfo'; +$aFirstNames[] = 'Myron'; +$aFirstNames[] = 'Edmund'; +$aFirstNames[] = 'Garrett'; +$aFirstNames[] = 'Salvatore'; +$aFirstNames[] = 'Cedric'; +$aFirstNames[] = 'Lowell'; +$aFirstNames[] = 'Gregg'; +$aFirstNames[] = 'Sherman'; +$aFirstNames[] = 'Wilson'; +$aFirstNames[] = 'Devin'; +$aFirstNames[] = 'Sylvester'; +$aFirstNames[] = 'Kim'; +$aFirstNames[] = 'Roosevelt'; +$aFirstNames[] = 'Israel'; +$aFirstNames[] = 'Jermaine'; +$aFirstNames[] = 'Forrest'; +$aFirstNames[] = 'Wilbert'; +$aFirstNames[] = 'Leland'; +$aFirstNames[] = 'Simon'; +$aFirstNames[] = 'Guadalupe'; +$aFirstNames[] = 'Clark'; +$aFirstNames[] = 'Irving'; +$aFirstNames[] = 'Carroll'; +$aFirstNames[] = 'Bryant'; +$aFirstNames[] = 'Owen'; +$aFirstNames[] = 'Rufus'; +$aFirstNames[] = 'Woodrow'; +$aFirstNames[] = 'Sammy'; +$aFirstNames[] = 'Kristopher'; +$aFirstNames[] = 'Mack'; +$aFirstNames[] = 'Levi'; +$aFirstNames[] = 'Marcos'; +$aFirstNames[] = 'Gustavo'; +$aFirstNames[] = 'Jake'; +$aFirstNames[] = 'Lionel'; +$aFirstNames[] = 'Marty'; +$aFirstNames[] = 'Taylor'; +$aFirstNames[] = 'Ellis'; +$aFirstNames[] = 'Dallas'; +$aFirstNames[] = 'Gilberto'; +$aFirstNames[] = 'Clint'; +$aFirstNames[] = 'Nicolas'; +$aFirstNames[] = 'Laurence'; +$aFirstNames[] = 'Ismael'; +$aFirstNames[] = 'Orville'; +$aFirstNames[] = 'Drew'; +$aFirstNames[] = 'Jody'; +$aFirstNames[] = 'Ervin'; +$aFirstNames[] = 'Dewey'; +$aFirstNames[] = 'Al'; +$aFirstNames[] = 'Wilfred'; +$aFirstNames[] = 'Josh'; +$aFirstNames[] = 'Hugo'; +$aFirstNames[] = 'Ignacio'; +$aFirstNames[] = 'Caleb'; +$aFirstNames[] = 'Tomas'; +$aFirstNames[] = 'Sheldon'; +$aFirstNames[] = 'Erick'; +$aFirstNames[] = 'Frankie'; +$aFirstNames[] = 'Stewart'; +$aFirstNames[] = 'Doyle'; +$aFirstNames[] = 'Darrel'; +$aFirstNames[] = 'Rogelio'; +$aFirstNames[] = 'Terence'; +$aFirstNames[] = 'Santiago'; +$aFirstNames[] = 'Alonzo'; +$aFirstNames[] = 'Elias'; +$aFirstNames[] = 'Bert'; +$aFirstNames[] = 'Elbert'; +$aFirstNames[] = 'Ramiro'; +$aFirstNames[] = 'Conrad'; +$aFirstNames[] = 'Pat'; +$aFirstNames[] = 'Noah'; +$aFirstNames[] = 'Grady'; +$aFirstNames[] = 'Phil'; +$aFirstNames[] = 'Cornelius'; +$aFirstNames[] = 'Lamar'; +$aFirstNames[] = 'Rolando'; +$aFirstNames[] = 'Clay'; +$aFirstNames[] = 'Percy'; +$aFirstNames[] = 'Dexter'; +$aFirstNames[] = 'Bradford'; +$aFirstNames[] = 'Merle'; +$aFirstNames[] = 'Darin'; +$aFirstNames[] = 'Amos'; +$aFirstNames[] = 'Terrell'; +$aFirstNames[] = 'Moses'; +$aFirstNames[] = 'Irvin'; +$aFirstNames[] = 'Saul'; +$aFirstNames[] = 'Roman'; +$aFirstNames[] = 'Darnell'; +$aFirstNames[] = 'Randal'; +$aFirstNames[] = 'Tommie'; +$aFirstNames[] = 'Timmy'; +$aFirstNames[] = 'Darrin'; +$aFirstNames[] = 'Winston'; +$aFirstNames[] = 'Brendan'; +$aFirstNames[] = 'Toby'; +$aFirstNames[] = 'Van'; +$aFirstNames[] = 'Abel'; +$aFirstNames[] = 'Dominick'; +$aFirstNames[] = 'Boyd'; +$aFirstNames[] = 'Courtney'; +$aFirstNames[] = 'Jan'; +$aFirstNames[] = 'Emilio'; +$aFirstNames[] = 'Elijah'; +$aFirstNames[] = 'Cary'; +$aFirstNames[] = 'Domingo'; +$aFirstNames[] = 'Santos'; +$aFirstNames[] = 'Aubrey'; +$aFirstNames[] = 'Emmett'; +$aFirstNames[] = 'Marlon'; +$aFirstNames[] = 'Emanuel'; +$aFirstNames[] = 'Jerald'; +$aFirstNames[] = 'Edmond'; +$aFirstNames[] = 'Jan Jozef '; +$aFirstNames[] = 'Konrad'; +$aFirstNames[] = 'Lidia'; +$aFirstNames[] = 'Lorenz'; +$aFirstNames[] = 'Otto'; +$aFirstNames[] = 'Piotr'; +$aFirstNames[] = 'Wladyslaw'; + +$aNames = array(); +$aNames[] = 'Smith'; +$aNames[] = 'Johnson'; +$aNames[] = 'Williams'; +$aNames[] = 'Jones'; +$aNames[] = 'Brown'; +$aNames[] = 'Davis'; +$aNames[] = 'Miller'; +$aNames[] = 'Wilson'; +$aNames[] = 'Moore'; +$aNames[] = 'Taylor'; +$aNames[] = 'Anderson'; +$aNames[] = 'Thomas'; +$aNames[] = 'Jackson'; +$aNames[] = 'White'; +$aNames[] = 'Harris'; +$aNames[] = 'Martin'; +$aNames[] = 'Thompson'; +$aNames[] = 'Garcia'; +$aNames[] = 'Martinez'; +$aNames[] = 'Robinson'; +$aNames[] = 'Clark'; +$aNames[] = 'Rodriguez'; +$aNames[] = 'Lewis'; +$aNames[] = 'Lee'; +$aNames[] = 'Walker'; +$aNames[] = 'Hall'; +$aNames[] = 'Allen'; +$aNames[] = 'Young'; +$aNames[] = 'Hernandez'; +$aNames[] = 'King'; +$aNames[] = 'Wright'; +$aNames[] = 'Lopez'; +$aNames[] = 'Hill'; +$aNames[] = 'Scott'; +$aNames[] = 'Green'; +$aNames[] = 'Adams'; +$aNames[] = 'Baker'; +$aNames[] = 'Gonzalez'; +$aNames[] = 'Nelson'; +$aNames[] = 'Carter'; +$aNames[] = 'Mitchell'; +$aNames[] = 'Perez'; +$aNames[] = 'Roberts'; +$aNames[] = 'Turner'; +$aNames[] = 'Phillips'; +$aNames[] = 'Campbell'; +$aNames[] = 'Parker'; +$aNames[] = 'Evans'; +$aNames[] = 'Edwards'; +$aNames[] = 'Collins'; +$aNames[] = 'Stewart'; +$aNames[] = 'Sanchez'; +$aNames[] = 'Morris'; +$aNames[] = 'Rogers'; +$aNames[] = 'Reed'; +$aNames[] = 'Cook'; +$aNames[] = 'Morgan'; +$aNames[] = 'Bell'; +$aNames[] = 'Murphy'; +$aNames[] = 'Bailey'; +$aNames[] = 'Rivera'; +$aNames[] = 'Cooper'; +$aNames[] = 'Richardson'; +$aNames[] = 'Cox'; +$aNames[] = 'Howard'; +$aNames[] = 'Ward'; +$aNames[] = 'Torres'; +$aNames[] = 'Peterson'; +$aNames[] = 'Gray'; +$aNames[] = 'Ramirez'; +$aNames[] = 'James'; +$aNames[] = 'Watson'; +$aNames[] = 'Brooks'; +$aNames[] = 'Kelly'; +$aNames[] = 'Sanders'; +$aNames[] = 'Price'; +$aNames[] = 'Bennett'; +$aNames[] = 'Wood'; +$aNames[] = 'Barnes'; +$aNames[] = 'Ross'; +$aNames[] = 'Henderson'; +$aNames[] = 'Coleman'; +$aNames[] = 'Jenkins'; +$aNames[] = 'Perry'; +$aNames[] = 'Powell'; +$aNames[] = 'Long'; +$aNames[] = 'Patterson'; +$aNames[] = 'Hughes'; +$aNames[] = 'Flores'; +$aNames[] = 'Washington'; +$aNames[] = 'Butler'; +$aNames[] = 'Simmons'; +$aNames[] = 'Foster'; +$aNames[] = 'Gonzales'; +$aNames[] = 'Bryant'; +$aNames[] = 'Alexander'; +$aNames[] = 'Russell'; +$aNames[] = 'Griffin'; +$aNames[] = 'Diaz'; +$aNames[] = 'Hayes'; +$aNames[] = 'Myers'; +$aNames[] = 'Ford'; +$aNames[] = 'Hamilton'; +$aNames[] = 'Graham'; +$aNames[] = 'Sullivan'; +$aNames[] = 'Wallace'; +$aNames[] = 'Woods'; +$aNames[] = 'Cole'; +$aNames[] = 'West'; +$aNames[] = 'Jordan'; +$aNames[] = 'Owens'; +$aNames[] = 'Reynolds'; +$aNames[] = 'Fisher'; +$aNames[] = 'Ellis'; +$aNames[] = 'Harrison'; +$aNames[] = 'Gibson'; +$aNames[] = 'Mcdonald'; +$aNames[] = 'Cruz'; +$aNames[] = 'Marshall'; +$aNames[] = 'Ortiz'; +$aNames[] = 'Gomez'; +$aNames[] = 'Murray'; +$aNames[] = 'Freeman'; +$aNames[] = 'Wells'; +$aNames[] = 'Webb'; +$aNames[] = 'Simpson'; +$aNames[] = 'Stevens'; +$aNames[] = 'Tucker'; +$aNames[] = 'Porter'; +$aNames[] = 'Hunter'; +$aNames[] = 'Hicks'; +$aNames[] = 'Crawford'; +$aNames[] = 'Henry'; +$aNames[] = 'Boyd'; +$aNames[] = 'Mason'; +$aNames[] = 'Morales'; +$aNames[] = 'Kennedy'; +$aNames[] = 'Warren'; +$aNames[] = 'Dixon'; +$aNames[] = 'Ramos'; +$aNames[] = 'Reyes'; +$aNames[] = 'Burns'; +$aNames[] = 'Gordon'; +$aNames[] = 'Shaw'; +$aNames[] = 'Holmes'; +$aNames[] = 'Rice'; +$aNames[] = 'Robertson'; +$aNames[] = 'Hunt'; +$aNames[] = 'Black'; +$aNames[] = 'Daniels'; +$aNames[] = 'Palmer'; +$aNames[] = 'Mills'; +$aNames[] = 'Nichols'; +$aNames[] = 'Grant'; +$aNames[] = 'Knight'; +$aNames[] = 'Ferguson'; +$aNames[] = 'Rose'; +$aNames[] = 'Stone'; +$aNames[] = 'Hawkins'; +$aNames[] = 'Dunn'; +$aNames[] = 'Perkins'; +$aNames[] = 'Hudson'; +$aNames[] = 'Spencer'; +$aNames[] = 'Gardner'; +$aNames[] = 'Stephens'; +$aNames[] = 'Payne'; +$aNames[] = 'Pierce'; +$aNames[] = 'Berry'; +$aNames[] = 'Matthews'; +$aNames[] = 'Arnold'; +$aNames[] = 'Wagner'; +$aNames[] = 'Willis'; +$aNames[] = 'Ray'; +$aNames[] = 'Watkins'; +$aNames[] = 'Olson'; +$aNames[] = 'Carroll'; +$aNames[] = 'Duncan'; +$aNames[] = 'Snyder'; +$aNames[] = 'Hart'; +$aNames[] = 'Cunningham'; +$aNames[] = 'Bradley'; +$aNames[] = 'Lane'; +$aNames[] = 'Andrews'; +$aNames[] = 'Ruiz'; +$aNames[] = 'Harper'; +$aNames[] = 'Fox'; +$aNames[] = 'Riley'; +$aNames[] = 'Armstrong'; +$aNames[] = 'Carpenter'; +$aNames[] = 'Weaver'; +$aNames[] = 'Greene'; +$aNames[] = 'Lawrence'; +$aNames[] = 'Elliott'; +$aNames[] = 'Chavez'; +$aNames[] = 'Sims'; +$aNames[] = 'Austin'; +$aNames[] = 'Peters'; +$aNames[] = 'Kelley'; +$aNames[] = 'Franklin'; +$aNames[] = 'Lawson'; +$aNames[] = 'Fields'; +$aNames[] = 'Gutierrez'; +$aNames[] = 'Ryan'; +$aNames[] = 'Schmidt'; +$aNames[] = 'Carr'; +$aNames[] = 'Vasquez'; +$aNames[] = 'Castillo'; +$aNames[] = 'Wheeler'; +$aNames[] = 'Chapman'; +$aNames[] = 'Oliver'; +$aNames[] = 'Montgomery'; +$aNames[] = 'Richards'; +$aNames[] = 'Williamson'; +$aNames[] = 'Johnston'; +$aNames[] = 'Banks'; +$aNames[] = 'Meyer'; +$aNames[] = 'Bishop'; +$aNames[] = 'Mccoy'; +$aNames[] = 'Howell'; +$aNames[] = 'Alvarez'; +$aNames[] = 'Morrison'; +$aNames[] = 'Hansen'; +$aNames[] = 'Fernandez'; +$aNames[] = 'Garza'; +$aNames[] = 'Harvey'; +$aNames[] = 'Little'; +$aNames[] = 'Burton'; +$aNames[] = 'Stanley'; +$aNames[] = 'Nguyen'; +$aNames[] = 'George'; +$aNames[] = 'Jacobs'; +$aNames[] = 'Reid'; +$aNames[] = 'Kim'; +$aNames[] = 'Fuller'; +$aNames[] = 'Lynch'; +$aNames[] = 'Dean'; +$aNames[] = 'Gilbert'; +$aNames[] = 'Garrett'; +$aNames[] = 'Romero'; +$aNames[] = 'Welch'; +$aNames[] = 'Larson'; +$aNames[] = 'Frazier'; +$aNames[] = 'Burke'; +$aNames[] = 'Hanson'; +$aNames[] = 'Day'; +$aNames[] = 'Mendoza'; +$aNames[] = 'Moreno'; +$aNames[] = 'Bowman'; +$aNames[] = 'Medina'; +$aNames[] = 'Fowler'; +$aNames[] = 'Brewer'; +$aNames[] = 'Hoffman'; +$aNames[] = 'Carlson'; +$aNames[] = 'Silva'; +$aNames[] = 'Pearson'; +$aNames[] = 'Holland'; +$aNames[] = 'Douglas'; +$aNames[] = 'Fleming'; +$aNames[] = 'Jensen'; +$aNames[] = 'Vargas'; +$aNames[] = 'Byrd'; +$aNames[] = 'Davidson'; +$aNames[] = 'Hopkins'; +$aNames[] = 'May'; +$aNames[] = 'Terry'; +$aNames[] = 'Herrera'; +$aNames[] = 'Wade'; +$aNames[] = 'Soto'; +$aNames[] = 'Walters'; +$aNames[] = 'Curtis'; +$aNames[] = 'Neal'; +$aNames[] = 'Caldwell'; +$aNames[] = 'Lowe'; +$aNames[] = 'Jennings'; +$aNames[] = 'Barnett'; +$aNames[] = 'Graves'; +$aNames[] = 'Jimenez'; +$aNames[] = 'Horton'; +$aNames[] = 'Shelton'; +$aNames[] = 'Barrett'; +$aNames[] = 'Obrien'; +$aNames[] = 'Castro'; +$aNames[] = 'Sutton'; +$aNames[] = 'Gregory'; +$aNames[] = 'Mckinney'; +$aNames[] = 'Lucas'; +$aNames[] = 'Miles'; +$aNames[] = 'Craig'; +$aNames[] = 'Rodriquez'; +$aNames[] = 'Chambers'; +$aNames[] = 'Holt'; +$aNames[] = 'Lambert'; +$aNames[] = 'Fletcher'; +$aNames[] = 'Watts'; +$aNames[] = 'Bates'; +$aNames[] = 'Hale'; +$aNames[] = 'Rhodes'; +$aNames[] = 'Pena'; +$aNames[] = 'Beck'; +$aNames[] = 'Newman'; +$aNames[] = 'Haynes'; +$aNames[] = 'Mcdaniel'; +$aNames[] = 'Mendez'; +$aNames[] = 'Bush'; +$aNames[] = 'Vaughn'; +$aNames[] = 'Parks'; +$aNames[] = 'Dawson'; +$aNames[] = 'Santiago'; +$aNames[] = 'Norris'; +$aNames[] = 'Hardy'; +$aNames[] = 'Love'; +$aNames[] = 'Steele'; +$aNames[] = 'Curry'; +$aNames[] = 'Powers'; +$aNames[] = 'Schultz'; +$aNames[] = 'Barker'; +$aNames[] = 'Guzman'; +$aNames[] = 'Page'; +$aNames[] = 'Munoz'; +$aNames[] = 'Ball'; +$aNames[] = 'Keller'; +$aNames[] = 'Chandler'; +$aNames[] = 'Weber'; +$aNames[] = 'Leonard'; +$aNames[] = 'Walsh'; +$aNames[] = 'Lyons'; +$aNames[] = 'Ramsey'; +$aNames[] = 'Wolfe'; +$aNames[] = 'Schneider'; +$aNames[] = 'Mullins'; +$aNames[] = 'Benson'; +$aNames[] = 'Sharp'; +$aNames[] = 'Bowen'; +$aNames[] = 'Daniel'; +$aNames[] = 'Barber'; +$aNames[] = 'Cummings'; +$aNames[] = 'Hines'; +$aNames[] = 'Baldwin'; +$aNames[] = 'Griffith'; +$aNames[] = 'Valdez'; +$aNames[] = 'Hubbard'; +$aNames[] = 'Salazar'; +$aNames[] = 'Reeves'; +$aNames[] = 'Warner'; +$aNames[] = 'Stevenson'; +$aNames[] = 'Burgess'; +$aNames[] = 'Santos'; +$aNames[] = 'Tate'; +$aNames[] = 'Cross'; +$aNames[] = 'Garner'; +$aNames[] = 'Mann'; +$aNames[] = 'Mack'; +$aNames[] = 'Moss'; +$aNames[] = 'Thornton'; +$aNames[] = 'Dennis'; +$aNames[] = 'Mcgee'; +$aNames[] = 'Farmer'; +$aNames[] = 'Delgado'; +$aNames[] = 'Aguilar'; +$aNames[] = 'Vega'; +$aNames[] = 'Glover'; +$aNames[] = 'Manning'; +$aNames[] = 'Cohen'; +$aNames[] = 'Harmon'; +$aNames[] = 'Rodgers'; +$aNames[] = 'Robbins'; +$aNames[] = 'Newton'; +$aNames[] = 'Todd'; +$aNames[] = 'Blair'; +$aNames[] = 'Higgins'; +$aNames[] = 'Ingram'; +$aNames[] = 'Reese'; +$aNames[] = 'Cannon'; +$aNames[] = 'Strickland'; +$aNames[] = 'Townsend'; +$aNames[] = 'Potter'; +$aNames[] = 'Goodwin'; +$aNames[] = 'Walton'; +$aNames[] = 'Rowe'; +$aNames[] = 'Hampton'; +$aNames[] = 'Ortega'; +$aNames[] = 'Patton'; +$aNames[] = 'Swanson'; +$aNames[] = 'Joseph'; +$aNames[] = 'Francis'; +$aNames[] = 'Goodman'; +$aNames[] = 'Maldonado'; +$aNames[] = 'Yates'; +$aNames[] = 'Becker'; +$aNames[] = 'Erickson'; +$aNames[] = 'Hodges'; +$aNames[] = 'Rios'; +$aNames[] = 'Conner'; +$aNames[] = 'Adkins'; +$aNames[] = 'Webster'; +$aNames[] = 'Norman'; +$aNames[] = 'Malone'; +$aNames[] = 'Hammond'; +$aNames[] = 'Flowers'; +$aNames[] = 'Cobb'; +$aNames[] = 'Moody'; +$aNames[] = 'Quinn'; +$aNames[] = 'Blake'; +$aNames[] = 'Maxwell'; +$aNames[] = 'Pope'; +$aNames[] = 'Floyd'; +$aNames[] = 'Osborne'; +$aNames[] = 'Paul'; +$aNames[] = 'Mccarthy'; +$aNames[] = 'Guerrero'; +$aNames[] = 'Lindsey'; +$aNames[] = 'Estrada'; +$aNames[] = 'Sandoval'; +$aNames[] = 'Gibbs'; +$aNames[] = 'Tyler'; +$aNames[] = 'Gross'; +$aNames[] = 'Fitzgerald'; +$aNames[] = 'Stokes'; +$aNames[] = 'Doyle'; +$aNames[] = 'Sherman'; +$aNames[] = 'Saunders'; +$aNames[] = 'Wise'; +$aNames[] = 'Colon'; +$aNames[] = 'Gill'; +$aNames[] = 'Alvarado'; +$aNames[] = 'Greer'; +$aNames[] = 'Padilla'; +$aNames[] = 'Simon'; +$aNames[] = 'Waters'; +$aNames[] = 'Nunez'; +$aNames[] = 'Ballard'; +$aNames[] = 'Schwartz'; +$aNames[] = 'Mcbride'; +$aNames[] = 'Houston'; +$aNames[] = 'Christensen'; +$aNames[] = 'Klein'; +$aNames[] = 'Pratt'; +$aNames[] = 'Briggs'; +$aNames[] = 'Parsons'; +$aNames[] = 'Mclaughlin'; +$aNames[] = 'Zimmerman'; +$aNames[] = 'French'; +$aNames[] = 'Buchanan'; +$aNames[] = 'Moran'; +$aNames[] = 'Copeland'; +$aNames[] = 'Roy'; +$aNames[] = 'Pittman'; +$aNames[] = 'Brady'; +$aNames[] = 'Mccormick'; +$aNames[] = 'Holloway'; +$aNames[] = 'Brock'; +$aNames[] = 'Poole'; +$aNames[] = 'Frank'; +$aNames[] = 'Logan'; +$aNames[] = 'Owen'; +$aNames[] = 'Bass'; +$aNames[] = 'Marsh'; +$aNames[] = 'Drake'; +$aNames[] = 'Wong'; +$aNames[] = 'Jefferson'; +$aNames[] = 'Park'; +$aNames[] = 'Morton'; +$aNames[] = 'Abbott'; +$aNames[] = 'Sparks'; +$aNames[] = 'Patrick'; +$aNames[] = 'Norton'; +$aNames[] = 'Huff'; +$aNames[] = 'Clayton'; +$aNames[] = 'Massey'; +$aNames[] = 'Lloyd'; +$aNames[] = 'Figueroa'; +$aNames[] = 'Carson'; +$aNames[] = 'Bowers'; +$aNames[] = 'Roberson'; +$aNames[] = 'Barton'; +$aNames[] = 'Tran'; +$aNames[] = 'Lamb'; +$aNames[] = 'Harrington'; +$aNames[] = 'Casey'; +$aNames[] = 'Boone'; +$aNames[] = 'Cortez'; +$aNames[] = 'Clarke'; +$aNames[] = 'Mathis'; +$aNames[] = 'Singleton'; +$aNames[] = 'Wilkins'; +$aNames[] = 'Cain'; +$aNames[] = 'Bryan'; +$aNames[] = 'Underwood'; +$aNames[] = 'Hogan'; +$aNames[] = 'Mckenzie'; +$aNames[] = 'Collier'; +$aNames[] = 'Luna'; +$aNames[] = 'Phelps'; +$aNames[] = 'Mcguire'; +$aNames[] = 'Allison'; +$aNames[] = 'Bridges'; +$aNames[] = 'Wilkerson'; +$aNames[] = 'Nash'; +$aNames[] = 'Summers'; +$aNames[] = 'Atkins'; +$aNames[] = 'Wilcox'; +$aNames[] = 'Pitts'; +$aNames[] = 'Conley'; +$aNames[] = 'Marquez'; +$aNames[] = 'Burnett'; +$aNames[] = 'Richard'; +$aNames[] = 'Cochran'; +$aNames[] = 'Chase'; +$aNames[] = 'Davenport'; +$aNames[] = 'Hood'; +$aNames[] = 'Gates'; +$aNames[] = 'Clay'; +$aNames[] = 'Ayala'; +$aNames[] = 'Sawyer'; +$aNames[] = 'Roman'; +$aNames[] = 'Vazquez'; +$aNames[] = 'Dickerson'; +$aNames[] = 'Hodge'; +$aNames[] = 'Acosta'; +$aNames[] = 'Flynn'; +$aNames[] = 'Espinoza'; +$aNames[] = 'Nicholson'; +$aNames[] = 'Monroe'; +$aNames[] = 'Wolf'; +$aNames[] = 'Morrow'; +$aNames[] = 'Kirk'; +$aNames[] = 'Randall'; +$aNames[] = 'Anthony'; +$aNames[] = 'Whitaker'; +$aNames[] = 'Oconnor'; +$aNames[] = 'Skinner'; +$aNames[] = 'Ware'; +$aNames[] = 'Molina'; +$aNames[] = 'Kirby'; +$aNames[] = 'Huffman'; +$aNames[] = 'Bradford'; +$aNames[] = 'Charles'; +$aNames[] = 'Gilmore'; +$aNames[] = 'Dominguez'; +$aNames[] = 'Oneal'; +$aNames[] = 'Bruce'; +$aNames[] = 'Lang'; +$aNames[] = 'Combs'; +$aNames[] = 'Kramer'; +$aNames[] = 'Heath'; +$aNames[] = 'Hancock'; +$aNames[] = 'Gallagher'; +$aNames[] = 'Gaines'; +$aNames[] = 'Shaffer'; +$aNames[] = 'Short'; +$aNames[] = 'Wiggins'; +$aNames[] = 'Mathews'; +$aNames[] = 'Mcclain'; +$aNames[] = 'Fischer'; +$aNames[] = 'Wall'; +$aNames[] = 'Small'; +$aNames[] = 'Melton'; +$aNames[] = 'Hensley'; +$aNames[] = 'Bond'; +$aNames[] = 'Dyer'; +$aNames[] = 'Cameron'; +$aNames[] = 'Grimes'; +$aNames[] = 'Contreras'; +$aNames[] = 'Christian'; +$aNames[] = 'Wyatt'; +$aNames[] = 'Baxter'; +$aNames[] = 'Snow'; +$aNames[] = 'Mosley'; +$aNames[] = 'Shepherd'; +$aNames[] = 'Larsen'; +$aNames[] = 'Hoover'; +$aNames[] = 'Beasley'; +$aNames[] = 'Glenn'; +$aNames[] = 'Petersen'; +$aNames[] = 'Whitehead'; +$aNames[] = 'Meyers'; +$aNames[] = 'Keith'; +$aNames[] = 'Garrison'; +$aNames[] = 'Vincent'; +$aNames[] = 'Shields'; +$aNames[] = 'Horn'; +$aNames[] = 'Savage'; +$aNames[] = 'Olsen'; +$aNames[] = 'Schroeder'; +$aNames[] = 'Hartman'; +$aNames[] = 'Woodard'; +$aNames[] = 'Mueller'; +$aNames[] = 'Kemp'; +$aNames[] = 'Deleon'; +$aNames[] = 'Booth'; +$aNames[] = 'Patel'; +$aNames[] = 'Calhoun'; +$aNames[] = 'Wiley'; +$aNames[] = 'Eaton'; +$aNames[] = 'Cline'; +$aNames[] = 'Navarro'; +$aNames[] = 'Harrell'; +$aNames[] = 'Lester'; +$aNames[] = 'Humphrey'; +$aNames[] = 'Parrish'; +$aNames[] = 'Duran'; +$aNames[] = 'Hutchinson'; +$aNames[] = 'Hess'; +$aNames[] = 'Dorsey'; +$aNames[] = 'Bullock'; +$aNames[] = 'Robles'; +$aNames[] = 'Beard'; +$aNames[] = 'Dalton'; +$aNames[] = 'Avila'; +$aNames[] = 'Vance'; +$aNames[] = 'Rich'; +$aNames[] = 'Blackwell'; +$aNames[] = 'York'; +$aNames[] = 'Johns'; +$aNames[] = 'Blankenship'; +$aNames[] = 'Trevino'; +$aNames[] = 'Salinas'; +$aNames[] = 'Campos'; +$aNames[] = 'Pruitt'; +$aNames[] = 'Moses'; +$aNames[] = 'Callahan'; +$aNames[] = 'Golden'; +$aNames[] = 'Montoya'; +$aNames[] = 'Hardin'; +$aNames[] = 'Guerra'; +$aNames[] = 'Mcdowell'; +$aNames[] = 'Carey'; +$aNames[] = 'Stafford'; +$aNames[] = 'Gallegos'; +$aNames[] = 'Henson'; +$aNames[] = 'Wilkinson'; +$aNames[] = 'Booker'; +$aNames[] = 'Merritt'; +$aNames[] = 'Miranda'; +$aNames[] = 'Atkinson'; +$aNames[] = 'Orr'; +$aNames[] = 'Decker'; +$aNames[] = 'Hobbs'; +$aNames[] = 'Preston'; +$aNames[] = 'Tanner'; +$aNames[] = 'Knox'; +$aNames[] = 'Pacheco'; +$aNames[] = 'Stephenson'; +$aNames[] = 'Glass'; +$aNames[] = 'Rojas'; +$aNames[] = 'Serrano'; +$aNames[] = 'Marks'; +$aNames[] = 'Hickman'; +$aNames[] = 'English'; +$aNames[] = 'Sweeney'; +$aNames[] = 'Strong'; +$aNames[] = 'Prince'; +$aNames[] = 'Mcclure'; +$aNames[] = 'Conway'; +$aNames[] = 'Walter'; +$aNames[] = 'Roth'; +$aNames[] = 'Maynard'; +$aNames[] = 'Farrell'; +$aNames[] = 'Lowery'; +$aNames[] = 'Hurst'; +$aNames[] = 'Nixon'; +$aNames[] = 'Weiss'; +$aNames[] = 'Trujillo'; +$aNames[] = 'Ellison'; +$aNames[] = 'Sloan'; +$aNames[] = 'Juarez'; +$aNames[] = 'Winters'; +$aNames[] = 'Mclean'; +$aNames[] = 'Randolph'; +$aNames[] = 'Leon'; +$aNames[] = 'Boyer'; +$aNames[] = 'Villarreal'; +$aNames[] = 'Mccall'; +$aNames[] = 'Gentry'; +$aNames[] = 'Carrillo'; +$aNames[] = 'Kent'; +$aNames[] = 'Ayers'; +$aNames[] = 'Lara'; +$aNames[] = 'Shannon'; +$aNames[] = 'Sexton'; +$aNames[] = 'Pace'; +$aNames[] = 'Hull'; +$aNames[] = 'Leblanc'; +$aNames[] = 'Browning'; +$aNames[] = 'Velasquez'; +$aNames[] = 'Leach'; +$aNames[] = 'Chang'; +$aNames[] = 'House'; +$aNames[] = 'Sellers'; +$aNames[] = 'Herring'; +$aNames[] = 'Noble'; +$aNames[] = 'Foley'; +$aNames[] = 'Bartlett'; +$aNames[] = 'Mercado'; +$aNames[] = 'Landry'; +$aNames[] = 'Durham'; +$aNames[] = 'Walls'; +$aNames[] = 'Barr'; +$aNames[] = 'Mckee'; +$aNames[] = 'Bauer'; +$aNames[] = 'Rivers'; +$aNames[] = 'Everett'; +$aNames[] = 'Bradshaw'; +$aNames[] = 'Pugh'; +$aNames[] = 'Velez'; +$aNames[] = 'Rush'; +$aNames[] = 'Estes'; +$aNames[] = 'Dodson'; +$aNames[] = 'Morse'; +$aNames[] = 'Sheppard'; +$aNames[] = 'Weeks'; +$aNames[] = 'Camacho'; +$aNames[] = 'Bean'; +$aNames[] = 'Barron'; +$aNames[] = 'Livingston'; +$aNames[] = 'Middleton'; +$aNames[] = 'Spears'; +$aNames[] = 'Branch'; +$aNames[] = 'Blevins'; +$aNames[] = 'Chen'; +$aNames[] = 'Kerr'; +$aNames[] = 'Mcconnell'; +$aNames[] = 'Hatfield'; +$aNames[] = 'Harding'; +$aNames[] = 'Ashley'; +$aNames[] = 'Solis'; +$aNames[] = 'Herman'; +$aNames[] = 'Frost'; +$aNames[] = 'Giles'; +$aNames[] = 'Blackburn'; +$aNames[] = 'William'; +$aNames[] = 'Pennington'; +$aNames[] = 'Woodward'; +$aNames[] = 'Finley'; +$aNames[] = 'Mcintosh'; +$aNames[] = 'Koch'; +$aNames[] = 'Best'; +$aNames[] = 'Solomon'; +$aNames[] = 'Mccullough'; +$aNames[] = 'Dudley'; +$aNames[] = 'Nolan'; +$aNames[] = 'Blanchard'; +$aNames[] = 'Rivas'; +$aNames[] = 'Brennan'; +$aNames[] = 'Mejia'; +$aNames[] = 'Kane'; +$aNames[] = 'Benton'; +$aNames[] = 'Joyce'; +$aNames[] = 'Buckley'; +$aNames[] = 'Haley'; +$aNames[] = 'Valentine'; +$aNames[] = 'Maddox'; +$aNames[] = 'Russo'; +$aNames[] = 'Mcknight'; +$aNames[] = 'Buck'; +$aNames[] = 'Moon'; +$aNames[] = 'Mcmillan'; +$aNames[] = 'Crosby'; +$aNames[] = 'Berg'; +$aNames[] = 'Dotson'; +$aNames[] = 'Mays'; +$aNames[] = 'Roach'; +$aNames[] = 'Church'; +$aNames[] = 'Chan'; +$aNames[] = 'Richmond'; +$aNames[] = 'Meadows'; +$aNames[] = 'Faulkner'; +$aNames[] = 'Oneill'; +$aNames[] = 'Knapp'; +$aNames[] = 'Kline'; +$aNames[] = 'Barry'; +$aNames[] = 'Ochoa'; +$aNames[] = 'Jacobson'; +$aNames[] = 'Gay'; +$aNames[] = 'Avery'; +$aNames[] = 'Hendricks'; +$aNames[] = 'Horne'; +$aNames[] = 'Shepard'; +$aNames[] = 'Hebert'; +$aNames[] = 'Cherry'; +$aNames[] = 'Cardenas'; +$aNames[] = 'Mcintyre'; +$aNames[] = 'Whitney'; +$aNames[] = 'Waller'; +$aNames[] = 'Holman'; +$aNames[] = 'Donaldson'; +$aNames[] = 'Cantu'; +$aNames[] = 'Terrell'; +$aNames[] = 'Morin'; +$aNames[] = 'Gillespie'; +$aNames[] = 'Fuentes'; +$aNames[] = 'Tillman'; +$aNames[] = 'Sanford'; +$aNames[] = 'Bentley'; +$aNames[] = 'Peck'; +$aNames[] = 'Key'; +$aNames[] = 'Salas'; +$aNames[] = 'Rollins'; +$aNames[] = 'Gamble'; +$aNames[] = 'Dickson'; +$aNames[] = 'Battle'; +$aNames[] = 'Santana'; +$aNames[] = 'Cabrera'; +$aNames[] = 'Cervantes'; +$aNames[] = 'Howe'; +$aNames[] = 'Hinton'; +$aNames[] = 'Hurley'; +$aNames[] = 'Spence'; +$aNames[] = 'Zamora'; +$aNames[] = 'Yang'; +$aNames[] = 'Mcneil'; +$aNames[] = 'Suarez'; +$aNames[] = 'Case'; +$aNames[] = 'Petty'; +$aNames[] = 'Gould'; +$aNames[] = 'Mcfarland'; +$aNames[] = 'Sampson'; +$aNames[] = 'Carver'; +$aNames[] = 'Bray'; +$aNames[] = 'Rosario'; +$aNames[] = 'Macdonald'; +$aNames[] = 'Stout'; +$aNames[] = 'Hester'; +$aNames[] = 'Melendez'; +$aNames[] = 'Dillon'; +$aNames[] = 'Farley'; +$aNames[] = 'Hopper'; +$aNames[] = 'Galloway'; +$aNames[] = 'Potts'; +$aNames[] = 'Bernard'; +$aNames[] = 'Joyner'; +$aNames[] = 'Stein'; +$aNames[] = 'Aguirre'; +$aNames[] = 'Osborn'; +$aNames[] = 'Mercer'; +$aNames[] = 'Bender'; +$aNames[] = 'Franco'; +$aNames[] = 'Rowland'; +$aNames[] = 'Sykes'; +$aNames[] = 'Benjamin'; +$aNames[] = 'Travis'; +$aNames[] = 'Pickett'; +$aNames[] = 'Crane'; +$aNames[] = 'Sears'; +$aNames[] = 'Mayo'; +$aNames[] = 'Dunlap'; +$aNames[] = 'Hayden'; +$aNames[] = 'Wilder'; +$aNames[] = 'Mckay'; +$aNames[] = 'Coffey'; +$aNames[] = 'Mccarty'; +$aNames[] = 'Ewing'; +$aNames[] = 'Cooley'; +$aNames[] = 'Vaughan'; +$aNames[] = 'Bonner'; +$aNames[] = 'Cotton'; +$aNames[] = 'Holder'; +$aNames[] = 'Stark'; +$aNames[] = 'Ferrell'; +$aNames[] = 'Cantrell'; +$aNames[] = 'Fulton'; +$aNames[] = 'Lynn'; +$aNames[] = 'Lott'; +$aNames[] = 'Calderon'; +$aNames[] = 'Rosa'; +$aNames[] = 'Pollard'; +$aNames[] = 'Hooper'; +$aNames[] = 'Burch'; +$aNames[] = 'Mullen'; +$aNames[] = 'Fry'; +$aNames[] = 'Riddle'; +$aNames[] = 'Levy'; +$aNames[] = 'David'; +$aNames[] = 'Duke'; +$aNames[] = 'Odonnell'; +$aNames[] = 'Guy'; +$aNames[] = 'Michael'; +$aNames[] = 'Britt'; +$aNames[] = 'Frederick'; +$aNames[] = 'Daugherty'; +$aNames[] = 'Berger'; +$aNames[] = 'Dillard'; +$aNames[] = 'Alston'; +$aNames[] = 'Jarvis'; +$aNames[] = 'Frye'; +$aNames[] = 'Riggs'; +$aNames[] = 'Chaney'; +$aNames[] = 'Odom'; +$aNames[] = 'Duffy'; +$aNames[] = 'Fitzpatrick'; +$aNames[] = 'Valenzuela'; +$aNames[] = 'Merrill'; +$aNames[] = 'Mayer'; +$aNames[] = 'Alford'; +$aNames[] = 'Mcpherson'; +$aNames[] = 'Acevedo'; +$aNames[] = 'Donovan'; +$aNames[] = 'Barrera'; +$aNames[] = 'Albert'; +$aNames[] = 'Cote'; +$aNames[] = 'Reilly'; +$aNames[] = 'Compton'; +$aNames[] = 'Raymond'; +$aNames[] = 'Mooney'; +$aNames[] = 'Mcgowan'; +$aNames[] = 'Craft'; +$aNames[] = 'Cleveland'; +$aNames[] = 'Clemons'; +$aNames[] = 'Wynn'; +$aNames[] = 'Nielsen'; +$aNames[] = 'Baird'; +$aNames[] = 'Stanton'; +$aNames[] = 'Snider'; +$aNames[] = 'Rosales'; +$aNames[] = 'Bright'; +$aNames[] = 'Witt'; +$aNames[] = 'Stuart'; +$aNames[] = 'Hays'; +$aNames[] = 'Holden'; +$aNames[] = 'Rutledge'; +$aNames[] = 'Kinney'; +$aNames[] = 'Clements'; +$aNames[] = 'Castaneda'; +$aNames[] = 'Slater'; +$aNames[] = 'Hahn'; +$aNames[] = 'Emerson'; +$aNames[] = 'Conrad'; +$aNames[] = 'Burks'; +$aNames[] = 'Delaney'; +$aNames[] = 'Pate'; +$aNames[] = 'Lancaster'; +$aNames[] = 'Sweet'; +$aNames[] = 'Justice'; +$aNames[] = 'Tyson'; +$aNames[] = 'Sharpe'; +$aNames[] = 'Whitfield'; +$aNames[] = 'Talley'; +$aNames[] = 'Macias'; +$aNames[] = 'Irwin'; +$aNames[] = 'Burris'; +$aNames[] = 'Ratliff'; +$aNames[] = 'Mccray'; +$aNames[] = 'Madden'; +$aNames[] = 'Kaufman'; +$aNames[] = 'Beach'; +$aNames[] = 'Goff'; +$aNames[] = 'Cash'; +$aNames[] = 'Bolton'; +$aNames[] = 'Mcfadden'; +$aNames[] = 'Levine'; +$aNames[] = 'Good'; +$aNames[] = 'Byers'; +$aNames[] = 'Kirkland'; +$aNames[] = 'Kidd'; +$aNames[] = 'Workman'; +$aNames[] = 'Carney'; +$aNames[] = 'Dale'; +$aNames[] = 'Mcleod'; +$aNames[] = 'Holcomb'; +$aNames[] = 'England'; +$aNames[] = 'Finch'; +$aNames[] = 'Head'; +$aNames[] = 'Burt'; +$aNames[] = 'Hendrix'; +$aNames[] = 'Sosa'; +$aNames[] = 'Haney'; +$aNames[] = 'Franks'; +$aNames[] = 'Sargent'; +$aNames[] = 'Nieves'; +$aNames[] = 'Downs'; +$aNames[] = 'Rasmussen'; +$aNames[] = 'Bird'; +$aNames[] = 'Hewitt'; +$aNames[] = 'Lindsay'; +$aNames[] = 'Le'; +$aNames[] = 'Foreman'; +$aNames[] = 'Valencia'; +$aNames[] = 'Oneil'; +$aNames[] = 'Delacruz'; +$aNames[] = 'Vinson'; +$aNames[] = 'Dejesus'; +$aNames[] = 'Hyde'; +$aNames[] = 'Forbes'; +$aNames[] = 'Gilliam'; +$aNames[] = 'Guthrie'; +$aNames[] = 'Wooten'; +$aNames[] = 'Huber'; +$aNames[] = 'Barlow'; +$aNames[] = 'Boyle'; +$aNames[] = 'Mcmahon'; +$aNames[] = 'Buckner'; +$aNames[] = 'Rocha'; +$aNames[] = 'Puckett'; +$aNames[] = 'Langley'; +$aNames[] = 'Knowles'; +$aNames[] = 'Cooke'; +$aNames[] = 'Velazquez'; +$aNames[] = 'Whitley'; +$aNames[] = 'Noel'; +$aNames[] = 'Vang'; +$aNames[] = 'Shea'; +$aNames[] = 'Rouse'; +$aNames[] = 'Hartley'; +$aNames[] = 'Mayfield'; +$aNames[] = 'Elder'; +$aNames[] = 'Rankin'; +$aNames[] = 'Hanna'; +$aNames[] = 'Cowan'; +$aNames[] = 'Lucero'; +$aNames[] = 'Arroyo'; +$aNames[] = 'Slaughter'; +$aNames[] = 'Haas'; +$aNames[] = 'Oconnell'; +$aNames[] = 'Minor'; +$aNames[] = 'Kendrick'; +$aNames[] = 'Shirley'; +$aNames[] = 'Kendall'; +$aNames[] = 'Boucher'; +$aNames[] = 'Archer'; +$aNames[] = 'Boggs'; +$aNames[] = 'Odell'; +$aNames[] = 'Dougherty'; +$aNames[] = 'Andersen'; +$aNames[] = 'Newell'; +$aNames[] = 'Crowe'; +$aNames[] = 'Wang'; +$aNames[] = 'Friedman'; +$aNames[] = 'Bland'; +$aNames[] = 'Swain'; +$aNames[] = 'Holley'; +$aNames[] = 'Adam'; +$aNames[] = 'Adami'; +$aNames[] = 'Adriaen'; +$aNames[] = 'Adriaensen'; +$aNames[] = 'Adriaenssen'; +$aNames[] = 'Adriaenssens'; +$aNames[] = 'Adriencense'; +$aNames[] = 'Adriensence'; +$aNames[] = 'Adrienssens'; +$aNames[] = 'Aelter'; +$aNames[] = 'Aelterman'; +$aNames[] = 'Aelters'; +$aNames[] = 'Aerens'; +$aNames[] = 'Aerts'; +$aNames[] = 'Aertsens'; +$aNames[] = 'Albumazard'; +$aNames[] = 'Alloo'; +$aNames[] = 'Alsteen'; +$aNames[] = 'Andersson'; +$aNames[] = 'André'; +$aNames[] = 'Andries'; +$aNames[] = 'Andriessen'; +$aNames[] = 'Anthon'; +$aNames[] = 'Antoine'; +$aNames[] = 'Appelbaum'; +$aNames[] = 'Applaer'; +$aNames[] = 'Arimont'; +$aNames[] = 'Arquin'; +$aNames[] = 'Arteman'; +$aNames[] = 'Baert'; +$aNames[] = 'Bartholomeeus'; +$aNames[] = 'Bastien'; +$aNames[] = 'Bastin'; +$aNames[] = 'Baugnet'; +$aNames[] = 'Baugniet'; +$aNames[] = 'Baugniez'; +$aNames[] = 'Bauwens'; +$aNames[] = 'Beauve'; +$aNames[] = 'Beck'; +$aNames[] = 'Beckers'; +$aNames[] = 'Bernard'; +$aNames[] = 'Bertrand'; +$aNames[] = 'Bietmé'; +$aNames[] = 'Blaas'; +$aNames[] = 'Blankaert'; +$aNames[] = 'Blanquaert'; +$aNames[] = 'Blondeel'; +$aNames[] = 'Blondeeuw'; +$aNames[] = 'Blondoo'; +$aNames[] = 'Bodart'; +$aNames[] = 'Bodson'; +$aNames[] = 'Boeck'; +$aNames[] = 'Boesmans'; +$aNames[] = 'Bogaert'; +$aNames[] = 'Bogaerts'; +$aNames[] = 'Bogemans'; +$aNames[] = 'Booghmans'; +$aNames[] = 'Borremans'; +$aNames[] = 'Borsu'; +$aNames[] = 'Borsus'; +$aNames[] = 'Borsut'; +$aNames[] = 'Bosmans'; +$aNames[] = 'Bouch'; +$aNames[] = 'Bouchhout'; +$aNames[] = 'Bouillère'; +$aNames[] = 'Bouillet'; +$aNames[] = 'Boulanger'; +$aNames[] = 'Bourton'; +$aNames[] = 'Bouxin'; +$aNames[] = 'Brasseur'; +$aNames[] = 'Brouck'; +$aNames[] = 'Broucke'; +$aNames[] = 'Broucq'; +$aNames[] = 'Broucque'; +$aNames[] = 'Brouhier'; +$aNames[] = 'Brug'; +$aNames[] = 'Bruggesman'; +$aNames[] = 'Bruynseel'; +$aNames[] = 'Bruynseels'; +$aNames[] = 'Burger'; +$aNames[] = 'Burghgraeve'; +$aNames[] = 'Burgmeester'; +$aNames[] = 'Burton'; +$aNames[] = 'Burtont'; +$aNames[] = 'Buyle'; +$aNames[] = 'Calbert'; +$aNames[] = 'Callebaut'; +$aNames[] = 'Callebert'; +$aNames[] = 'Callebout'; +$aNames[] = 'Camby'; +$aNames[] = 'Cappelaere'; +$aNames[] = 'Cappelaire'; +$aNames[] = 'Cappelier'; +$aNames[] = 'Cappeliez'; +$aNames[] = 'Cappellier'; +$aNames[] = 'Carbonez'; +$aNames[] = 'Carbonnez'; +$aNames[] = 'Carlier'; +$aNames[] = 'Casteau'; +$aNames[] = 'Castel'; +$aNames[] = 'Castiaux'; +$aNames[] = 'Cauderlier'; +$aNames[] = 'Caudron'; +$aNames[] = 'Cauvel'; +$aNames[] = 'Cauvet'; +$aNames[] = 'Cauvin'; +$aNames[] = 'Cavard'; +$aNames[] = 'Ceulemans'; +$aNames[] = 'Chantry'; +$aNames[] = 'Charlier'; +$aNames[] = 'Chêneboit'; +$aNames[] = 'Chestay'; +$aNames[] = 'Chestia'; +$aNames[] = 'Chrispeels'; +$aNames[] = 'Christiaens'; +$aNames[] = 'Christoffel'; +$aNames[] = 'Claes'; +$aNames[] = 'Claessens'; +$aNames[] = 'Claeys'; +$aNames[] = 'Claus'; +$aNames[] = 'Cléban'; +$aNames[] = 'Clébant'; +$aNames[] = 'Clerx'; +$aNames[] = 'Colinus'; +$aNames[] = 'Collard'; +$aNames[] = 'Colleye'; +$aNames[] = 'Collignon'; +$aNames[] = 'Collin'; +$aNames[] = 'Colson'; +$aNames[] = 'Cool'; +$aNames[] = 'Cools'; +$aNames[] = 'Coppens'; +$aNames[] = 'Corain'; +$aNames[] = 'Corijn'; +$aNames[] = 'Corin'; +$aNames[] = 'Cornelis'; +$aNames[] = 'Cornet'; +$aNames[] = 'Corrin'; +$aNames[] = 'Corring'; +$aNames[] = 'Corringer'; +$aNames[] = 'Coryn'; +$aNames[] = 'Coudyser'; +$aNames[] = 'Couhysder'; +$aNames[] = 'Coutijser'; +$aNames[] = 'Coutiser'; +$aNames[] = 'Crab'; +$aNames[] = 'Crabbe'; +$aNames[] = 'Crama'; +$aNames[] = 'Crépez'; +$aNames[] = 'Crespel'; +$aNames[] = 'Crevisse'; +$aNames[] = 'Crevits'; +$aNames[] = 'Crispeel'; +$aNames[] = 'Crispeels'; +$aNames[] = 'Crispel'; +$aNames[] = 'Crispiels'; +$aNames[] = 'Cuvelier'; +$aNames[] = 'Cuypers'; +$aNames[] = 'Daan'; +$aNames[] = 'Daels'; +$aNames[] = 'Daems'; +$aNames[] = 'Dalmans'; +$aNames[] = 'Damard'; +$aNames[] = 'Damart'; +$aNames[] = 'Danis'; +$aNames[] = 'Dany'; +$aNames[] = 'Danys'; +$aNames[] = 'Dapvril'; +$aNames[] = 'Daufresne'; +$aNames[] = 'Dawance'; +$aNames[] = 'Debacker'; +$aNames[] = 'Debaere'; +$aNames[] = 'Debakker'; +$aNames[] = 'Debaut'; +$aNames[] = 'Debecker'; +$aNames[] = 'Debekker'; +$aNames[] = 'Debled'; +$aNames[] = 'Deboschere'; +$aNames[] = 'Deboscker'; +$aNames[] = 'Deboskre'; +$aNames[] = 'Debosscher'; +$aNames[] = 'Debosschere'; +$aNames[] = 'Debusschere'; +$aNames[] = 'Debuyst'; +$aNames[] = 'Declerck'; +$aNames[] = 'Declercq'; +$aNames[] = 'Decock'; +$aNames[] = 'Decocq'; +$aNames[] = 'Decrucq'; +$aNames[] = 'Decruyenaere'; +$aNames[] = 'Defaux'; +$aNames[] = 'Defawe'; +$aNames[] = 'Degroote'; +$aNames[] = 'Dehoorne'; +$aNames[] = 'Dehorne'; +$aNames[] = 'Dehornes'; +$aNames[] = 'Deilgat'; +$aNames[] = 'Dejong'; +$aNames[] = 'Dejonghe'; +$aNames[] = 'Dekale'; +$aNames[] = 'Dekimpe'; +$aNames[] = 'Dekoch'; +$aNames[] = 'Dekuiper'; +$aNames[] = 'Dekyndt'; +$aNames[] = 'Delacuvellerie'; +$aNames[] = 'Delafosse'; +$aNames[] = 'Delahaye'; +$aNames[] = 'Delahayes'; +$aNames[] = 'Delbouille'; +$aNames[] = 'Delboulle'; +$aNames[] = 'Delcorps'; +$aNames[] = 'Delflache'; +$aNames[] = 'Delfosse'; +$aNames[] = 'Delgat'; +$aNames[] = 'Delhaye'; +$aNames[] = 'Delhoste'; +$aNames[] = 'Delhotte'; +$aNames[] = 'Delmare'; +$aNames[] = 'Delmer'; +$aNames[] = 'Delobbe'; +$aNames[] = 'Delobe'; +$aNames[] = 'Delobes'; +$aNames[] = 'Delplace'; +$aNames[] = 'Delvaux'; +$aNames[] = 'Demain'; +$aNames[] = 'Demeiere'; +$aNames[] = 'Demeyer'; +$aNames[] = 'Demoor'; +$aNames[] = 'Demoore'; +$aNames[] = 'Demunck'; +$aNames[] = 'Demuynck'; +$aNames[] = 'Den'; +$aNames[] = 'Denaeyer'; +$aNames[] = 'Denayer'; +$aNames[] = 'Deneyer'; +$aNames[] = 'Denis'; +$aNames[] = 'Denoor'; +$aNames[] = 'Depannemaecker'; +$aNames[] = 'Depelsemacker'; +$aNames[] = 'Depelsemaeker'; +$aNames[] = 'Depelsenaire'; +$aNames[] = 'Depelseneer'; +$aNames[] = 'Depercenaire'; +$aNames[] = 'Depester'; +$aNames[] = 'Depiéreux'; +$aNames[] = 'Depierreux'; +$aNames[] = 'Depireux'; +$aNames[] = 'Depoorter'; +$aNames[] = 'Depoortere'; +$aNames[] = 'Depooter'; +$aNames[] = 'Depootere'; +$aNames[] = 'Deporter'; +$aNames[] = 'Deportere'; +$aNames[] = 'Depoterre'; +$aNames[] = 'Deprez'; +$aNames[] = 'Deramaix'; +$aNames[] = 'Deroosse'; +$aNames[] = 'Desandrouins'; +$aNames[] = 'Descamps'; +$aNames[] = 'Deschepper'; +$aNames[] = 'Desmedt'; +$aNames[] = 'Desmet'; +$aNames[] = 'Desmets'; +$aNames[] = 'Desmeytere'; +$aNames[] = 'Desmidt'; +$aNames[] = 'Desmidts'; +$aNames[] = 'Desmit'; +$aNames[] = 'Desmyter'; +$aNames[] = 'Desmytter'; +$aNames[] = 'Desmyttere'; +$aNames[] = 'Després'; +$aNames[] = 'Despret'; +$aNames[] = 'Desprets'; +$aNames[] = 'Despretz'; +$aNames[] = 'Desprey'; +$aNames[] = 'Desprez'; +$aNames[] = 'Destoute'; +$aNames[] = 'Deswart'; +$aNames[] = 'Deswarte'; +$aNames[] = 'Dethier'; +$aNames[] = 'Deur'; +$aNames[] = 'Deurwaerder'; +$aNames[] = 'Devis'; +$aNames[] = 'Devloo'; +$aNames[] = 'Devos'; +$aNames[] = 'Devriend'; +$aNames[] = 'Dewever'; +$aNames[] = 'Dewit'; +$aNames[] = 'Dewitte'; +$aNames[] = 'Dewyse'; +$aNames[] = 'Dhaeyer'; +$aNames[] = "D'Haeyer"; +$aNames[] = 'Dhoeraen'; +$aNames[] = "D'Hoeraen"; +$aNames[] = "D'Hoolaege"; +$aNames[] = 'Dierckx'; +$aNames[] = 'Dierik'; +$aNames[] = 'Doeraene'; +$aNames[] = 'Dolhaeghe'; +$aNames[] = 'Domiens'; +$aNames[] = 'Dominicus'; +$aNames[] = 'Dondaine'; +$aNames[] = 'Dondeine'; +$aNames[] = 'Dondenne'; +$aNames[] = 'Dondeyne'; +$aNames[] = 'Doolaeghe'; +$aNames[] = 'Doolaegue'; +$aNames[] = 'Doolage'; +$aNames[] = 'Doorn'; +$aNames[] = 'Doorne'; +$aNames[] = 'Doorneman'; +$aNames[] = 'Draier'; +$aNames[] = 'Dresselaers'; +$aNames[] = 'Dubled'; +$aNames[] = 'Dubois'; +$aNames[] = 'Dumont'; +$aNames[] = 'Dupont'; +$aNames[] = 'Duquesnay'; +$aNames[] = 'Duquesne'; +$aNames[] = 'Duquesnoy'; +$aNames[] = 'Ebrard'; +$aNames[] = 'Eeckeman'; +$aNames[] = 'Eerkens'; +$aNames[] = 'Erckens'; +$aNames[] = 'Erk'; +$aNames[] = 'Erken'; +$aNames[] = 'Erkens'; +$aNames[] = 'Etienne'; +$aNames[] = 'Euvrard'; +$aNames[] = 'Evert'; +$aNames[] = 'Evrard'; +$aNames[] = 'Evras'; +$aNames[] = 'Evrat'; +$aNames[] = 'Eyck'; +$aNames[] = 'Eysermans'; +$aNames[] = 'Fawat'; +$aNames[] = 'Faweux'; +$aNames[] = 'Fee'; +$aNames[] = 'Felix'; +$aNames[] = 'Flamenck'; +$aNames[] = 'Floche'; +$aNames[] = 'Floquet'; +$aNames[] = 'Fontaine'; +$aNames[] = 'Fonteyne'; +$aNames[] = 'Fraigany'; +$aNames[] = 'Fraigneux'; +$aNames[] = 'Francoeur'; +$aNames[] = 'François'; +$aNames[] = 'Francon'; +$aNames[] = 'Frankel'; +$aNames[] = 'Franken'; +$aNames[] = 'Frankeur'; +$aNames[] = 'Frans'; +$aNames[] = 'Fransman'; +$aNames[] = 'Fransolet'; +$aNames[] = 'Franzman'; +$aNames[] = 'Frijer'; +$aNames[] = 'Gabriels'; +$aNames[] = 'Gadisseur'; +$aNames[] = 'Gadisseux'; +$aNames[] = 'Gasthuys'; +$aNames[] = 'Gaudisseu'; +$aNames[] = 'Geeregat'; +$aNames[] = 'Geerts'; +$aNames[] = 'Geerts'; +$aNames[] = 'Geets'; +$aNames[] = 'Gehucht'; +$aNames[] = 'Geiregat'; +$aNames[] = 'Gendebien'; +$aNames[] = 'Genot'; +$aNames[] = 'Georges'; +$aNames[] = 'Gérard'; +$aNames[] = 'Gerlache'; +$aNames[] = 'Gerlaxhe'; +$aNames[] = 'Germay'; +$aNames[] = 'Germéa'; +$aNames[] = 'Germeau'; +$aNames[] = 'Ghiste'; +$aNames[] = 'Gidts'; +$aNames[] = 'Giets'; +$aNames[] = 'Gilles'; +$aNames[] = 'Gillet'; +$aNames[] = 'Gilson'; +$aNames[] = 'Gits'; +$aNames[] = 'Glaze'; +$aNames[] = 'Glazeman'; +$aNames[] = 'Goethals'; +$aNames[] = 'Goffin'; +$aNames[] = 'Gomaert'; +$aNames[] = 'Gomardt'; +$aNames[] = 'Goor'; +$aNames[] = 'Goossens'; +$aNames[] = 'Goud'; +$aNames[] = 'Goudman'; +$aNames[] = 'Goudsmith'; +$aNames[] = 'Gourdet'; +$aNames[] = 'Gousson'; +$aNames[] = 'Graas'; +$aNames[] = 'Greggs'; +$aNames[] = 'Gregh'; +$aNames[] = 'Grégoire'; +$aNames[] = 'Gregoor'; +$aNames[] = 'Grewis'; +$aNames[] = 'Groot'; +$aNames[] = 'Groote'; +$aNames[] = 'Grotaers'; +$aNames[] = 'Guillaume'; +$aNames[] = 'Guyaux'; +$aNames[] = 'Haesen'; +$aNames[] = 'Haesevoets'; +$aNames[] = 'Halasi'; +$aNames[] = 'Halazy'; +$aNames[] = 'Hamers'; +$aNames[] = 'Hanssens'; +$aNames[] = 'Hardas'; +$aNames[] = 'Hardat'; +$aNames[] = 'Hardy'; +$aNames[] = 'Heerbrant'; +$aNames[] = 'Hendrick'; +$aNames[] = 'Hendrickx'; +$aNames[] = 'Hendriks'; +$aNames[] = 'Henry'; +$aNames[] = 'Herbrand'; +$aNames[] = 'Herbrandt'; +$aNames[] = 'Herbrant'; +$aNames[] = 'Herman'; +$aNames[] = 'Hermann'; +$aNames[] = 'Hermans'; +$aNames[] = 'Herten'; +$aNames[] = 'Hertogs'; +$aNames[] = 'Hertogue'; +$aNames[] = 'Heylen'; +$aNames[] = 'Heymans'; +$aNames[] = 'Heynemans'; +$aNames[] = 'Heyrman'; +$aNames[] = 'Hinck'; +$aNames[] = 'Hinckel'; +$aNames[] = 'Hincker'; +$aNames[] = 'Hinkel'; +$aNames[] = 'Hinkels'; +$aNames[] = 'Hinkens'; +$aNames[] = 'Hinker'; +$aNames[] = 'Hinkle'; +$aNames[] = 'Hoefnagel'; +$aNames[] = 'Hoefnagels'; +$aNames[] = 'Holemans'; +$aNames[] = 'Honnay'; +$aNames[] = 'Horlin'; +$aNames[] = 'Houvenaghel'; +$aNames[] = 'Hoyois'; +$aNames[] = 'Hubert'; +$aNames[] = 'Huig'; +$aNames[] = 'Ickx'; +$aNames[] = 'Istace'; +$aNames[] = 'Istasse'; +$aNames[] = 'Jaak'; +$aNames[] = 'Jaap'; +$aNames[] = 'Jacob'; +$aNames[] = 'Jacobs'; +$aNames[] = 'Jacques'; +$aNames[] = 'Jacquet'; +$aNames[] = 'Jan'; +$aNames[] = 'Janhes'; +$aNames[] = 'Jansen'; +$aNames[] = 'Janssen'; +$aNames[] = 'Janssens'; +$aNames[] = 'Jef'; +$aNames[] = 'Jenot'; +$aNames[] = 'Jeuniaux'; +$aNames[] = 'Joire'; +$aNames[] = 'Jone'; +$aNames[] = 'Joneau'; +$aNames[] = 'Jonet'; +$aNames[] = 'Jonet'; +$aNames[] = 'Jongers'; +$aNames[] = 'Jonné'; +$aNames[] = 'Jonnet'; +$aNames[] = 'Jordaens'; +$aNames[] = 'Jorez'; +$aNames[] = 'Joris'; +$aNames[] = 'Jorissen'; +$aNames[] = 'Jozef'; +$aNames[] = 'Julianus'; +$aNames[] = 'Julius'; +$aNames[] = 'Jurgen'; +$aNames[] = 'Kaalman'; +$aNames[] = 'Kaisin'; +$aNames[] = 'Keetels'; +$aNames[] = 'Kenens'; +$aNames[] = 'Kenes'; +$aNames[] = 'Kenis'; +$aNames[] = 'Kennens'; +$aNames[] = 'Kennes'; +$aNames[] = 'Kennis'; +$aNames[] = 'Kesteloot'; +$aNames[] = 'Ketel'; +$aNames[] = 'Ketelsmit'; +$aNames[] = 'Kiecken'; +$aNames[] = 'Kimpe'; +$aNames[] = 'Kinnen'; +$aNames[] = 'Klein'; +$aNames[] = 'Kleineman'; +$aNames[] = 'Kleiner'; +$aNames[] = 'Kleinerman'; +$aNames[] = 'Kleinman'; +$aNames[] = 'Klerk'; +$aNames[] = 'Kleynen'; +$aNames[] = 'Klingeleers'; +$aNames[] = 'Kobus'; +$aNames[] = 'Koeck'; +$aNames[] = 'Konninckx'; +$aNames[] = 'Koolman'; +$aNames[] = 'Korring'; +$aNames[] = 'Kramers'; +$aNames[] = 'Kreemers'; +$aNames[] = 'Kuipers'; +$aNames[] = 'Labbez'; +$aNames[] = 'Lacroix'; +$aNames[] = 'Laenen'; +$aNames[] = 'Laenens'; +$aNames[] = 'Lafontaine'; +$aNames[] = 'Lambert'; +$aNames[] = 'Lambrechts'; +$aNames[] = 'Lanen'; +$aNames[] = 'Lanens'; +$aNames[] = 'Langlez'; +$aNames[] = 'Lapayre'; +$aNames[] = 'Laseur'; +$aNames[] = 'Laseure'; +$aNames[] = 'Lauffer'; +$aNames[] = 'Laurent'; +$aNames[] = 'Lauwers'; +$aNames[] = 'Le Demunck'; +$aNames[] = 'Leboutte'; +$aNames[] = 'Lebrun'; +$aNames[] = 'Leclerc'; +$aNames[] = 'Leclercq'; +$aNames[] = 'Lecocq'; +$aNames[] = 'Lecomte'; +$aNames[] = 'Ledecq'; +$aNames[] = 'Leenhard'; +$aNames[] = 'Leenhart'; +$aNames[] = 'Lefebvre'; +$aNames[] = 'Lefèvre'; +$aNames[] = 'Legrand'; +$aNames[] = 'Lejeune'; +$aNames[] = 'Lemaire'; +$aNames[] = 'Lemmens'; +$aNames[] = 'Lemonnier'; +$aNames[] = 'Lemounie'; +$aNames[] = 'Lenaerts'; +$aNames[] = 'Lénel'; +$aNames[] = 'Lénelle'; +$aNames[] = 'Lennel'; +$aNames[] = 'Léonard'; +$aNames[] = 'Lepoutre'; +$aNames[] = 'Leprette'; +$aNames[] = 'Lepropre'; +$aNames[] = 'Leroy'; +$aNames[] = 'Lescohy'; +$aNames[] = 'Lesoil'; +$aNames[] = 'Lesoile'; +$aNames[] = 'Lesoille'; +$aNames[] = 'Levecq'; +$aNames[] = 'Lewek'; +$aNames[] = 'Libert'; +$aNames[] = 'Liens'; +$aNames[] = 'Liephoudt'; +$aNames[] = 'Liepot'; +$aNames[] = 'Liepout'; +$aNames[] = 'Lieseborghs'; +$aNames[] = 'Liesenborghs'; +$aNames[] = 'Lietaer'; +$aNames[] = 'Lietaert'; +$aNames[] = 'Lietar'; +$aNames[] = 'Liétar'; +$aNames[] = 'Liétard'; +$aNames[] = 'Liétart'; +$aNames[] = 'Lievens'; +$aNames[] = 'Lievesoons'; +$aNames[] = 'Lievevrouw'; +$aNames[] = 'Lievrouw'; +$aNames[] = 'Liévrouw'; +$aNames[] = 'Lievrow'; +$aNames[] = 'Linglay'; +$aNames[] = 'Linglet'; +$aNames[] = 'Liphout'; +$aNames[] = 'Lisenborgh'; +$aNames[] = 'Lisenborgs'; +$aNames[] = 'Locreille'; +$aNames[] = 'Locrel'; +$aNames[] = 'Locrelle'; +$aNames[] = 'Lode'; +$aNames[] = 'Loo'; +$aNames[] = 'Lorfèvre'; +$aNames[] = 'Lorphêvre'; +$aNames[] = 'Losseau'; +$aNames[] = 'Losset'; +$aNames[] = 'Louis'; +$aNames[] = 'Louzeau'; +$aNames[] = 'Lowie'; +$aNames[] = 'Ludovicus'; +$aNames[] = 'Lugen'; +$aNames[] = 'Lugens'; +$aNames[] = 'Lust'; +$aNames[] = 'Lustig'; +$aNames[] = 'Luyer'; +$aNames[] = 'Luyrik'; +$aNames[] = 'Luyten'; +$aNames[] = 'Lyphoudt'; +$aNames[] = 'Lyphout'; +$aNames[] = 'Maca'; +$aNames[] = 'Maertens'; +$aNames[] = 'Maes'; +$aNames[] = 'Maessen'; +$aNames[] = 'Mahieu'; +$aNames[] = 'Maka'; +$aNames[] = 'Malchamp'; +$aNames[] = 'Malchamps'; +$aNames[] = 'Malmedier'; +$aNames[] = 'Malmedy'; +$aNames[] = 'Malmendier'; +$aNames[] = 'Mangon'; +$aNames[] = 'Maqua'; +$aNames[] = 'Marchal'; +$aNames[] = 'Marckx'; +$aNames[] = 'Marcus'; +$aNames[] = 'Mardaga'; +$aNames[] = 'Maréchal'; +$aNames[] = 'Maria'; +$aNames[] = 'Mark'; +$aNames[] = 'Markgraff'; +$aNames[] = 'Martens'; +$aNames[] = 'Martin'; +$aNames[] = 'Martins'; +$aNames[] = 'Massart'; +$aNames[] = 'Masson'; +$aNames[] = 'Mathieu'; +$aNames[] = 'Mathissen'; +$aNames[] = 'Mathy'; +$aNames[] = 'Matthys'; +$aNames[] = 'Mauchamp'; +$aNames[] = 'Mauchamps'; +$aNames[] = 'Maurichon'; +$aNames[] = 'Maurissen'; +$aNames[] = 'Maurits'; +$aNames[] = 'Mayeur'; +$aNames[] = 'Mayeux'; +$aNames[] = 'Mechelaere'; +$aNames[] = 'Meert'; +$aNames[] = 'Meertens'; +$aNames[] = 'Meester'; +$aNames[] = 'Meeus'; +$aNames[] = 'Melaerts'; +$aNames[] = 'Mellaerts'; +$aNames[] = 'Merchié'; +$aNames[] = 'Merchier'; +$aNames[] = 'Mergeai'; +$aNames[] = 'Mergeay'; +$aNames[] = 'Merjai'; +$aNames[] = 'Merjay'; +$aNames[] = 'Mertens'; +$aNames[] = 'Mertes'; +$aNames[] = 'Merts'; +$aNames[] = 'Mertz'; +$aNames[] = 'Meulemans'; +$aNames[] = 'Meulemeesters'; +$aNames[] = 'Meunier'; +$aNames[] = 'Meurice'; +$aNames[] = 'Mewis'; +$aNames[] = 'Mewissen'; +$aNames[] = 'Michaël'; +$aNames[] = 'Michaux'; +$aNames[] = 'Michel'; +$aNames[] = 'Michiels'; +$aNames[] = 'Mixhel'; +$aNames[] = 'Mochamps'; +$aNames[] = 'Moens'; +$aNames[] = 'Moeyaert'; +$aNames[] = 'Moiling'; +$aNames[] = 'Moinil'; +$aNames[] = 'Molemans'; +$aNames[] = 'Molenaers'; +$aNames[] = 'Monceau'; +$aNames[] = 'Moncia'; +$aNames[] = 'Monciaux'; +$aNames[] = 'Monsay'; +$aNames[] = 'Monteyne'; +$aNames[] = 'Moreau'; +$aNames[] = 'Mouyart'; +$aNames[] = 'Moyaert'; +$aNames[] = 'Mullenders'; +$aNames[] = 'Munck'; +$aNames[] = 'Muynck'; +$aNames[] = 'Nachtegael'; +$aNames[] = 'Nagelmaekers'; +$aNames[] = 'Nagels'; +$aNames[] = 'Natus'; +$aNames[] = 'Neel'; +$aNames[] = 'Neels'; +$aNames[] = 'Neuray'; +$aNames[] = 'Neureau'; +$aNames[] = 'Neuret'; +$aNames[] = 'Neurot'; +$aNames[] = 'Neuts'; +$aNames[] = 'Neuven'; +$aNames[] = 'Neven'; +$aNames[] = 'Nguyen'; +$aNames[] = 'Nicolas'; +$aNames[] = 'Nicolaus'; +$aNames[] = 'Nicolus'; +$aNames[] = 'Nijs'; +$aNames[] = 'Niklaas'; +$aNames[] = 'Noël'; +$aNames[] = 'Nuts'; +$aNames[] = 'Nuttin'; +$aNames[] = 'Ochin'; +$aNames[] = 'Olivier'; +$aNames[] = 'Olyff'; +$aNames[] = 'Paindavaine'; +$aNames[] = 'Pannaye'; +$aNames[] = 'Parmentier'; +$aNames[] = 'Pas'; +$aNames[] = 'Pauss'; +$aNames[] = 'Pauwels'; +$aNames[] = 'Peeters'; +$aNames[] = 'Pelser'; +$aNames[] = 'Pelsmaeker'; +$aNames[] = 'Peschon'; +$aNames[] = 'Peschoniez'; +$aNames[] = 'Pester'; +$aNames[] = 'Petersen'; +$aNames[] = 'Petit'; +$aNames[] = 'Pierre'; +$aNames[] = 'Piet'; +$aNames[] = 'Pieters'; +$aNames[] = 'Pietersen'; +$aNames[] = 'Piette'; +$aNames[] = 'Pirard'; +$aNames[] = 'Piron'; +$aNames[] = 'Pirotte'; +$aNames[] = 'Plaats'; +$aNames[] = 'Poels'; +$aNames[] = 'Poelsmans'; +$aNames[] = 'Poncelet'; +$aNames[] = 'Pools'; +$aNames[] = 'Posson'; +$aNames[] = 'Potstainer'; +$aNames[] = 'Potter'; +$aNames[] = 'Pottiaux'; +$aNames[] = 'Pottié'; +$aNames[] = 'Potty'; +$aNames[] = 'Poyon'; +$aNames[] = 'Praat'; +$aNames[] = 'Premereur'; +$aNames[] = 'Premmereur'; +$aNames[] = 'Prevostel'; +$aNames[] = 'Priesse'; +$aNames[] = 'Prisse'; +$aNames[] = 'Proost'; +$aNames[] = 'Prost'; +$aNames[] = 'Proust'; +$aNames[] = 'Putman'; +$aNames[] = 'Putmans'; +$aNames[] = 'Putmans'; +$aNames[] = 'Puttemans'; +$aNames[] = 'Puttemans'; +$aNames[] = 'Quaisin'; +$aNames[] = 'Quesnay'; +$aNames[] = 'Quesne'; +$aNames[] = 'Quesneau'; +$aNames[] = 'Quesnel'; +$aNames[] = 'Quesney'; +$aNames[] = 'Quesnoy'; +$aNames[] = 'Queval'; +$aNames[] = 'Raes'; +$aNames[] = 'Ramael'; +$aNames[] = 'Raucent'; +$aNames[] = 'Rauscent'; +$aNames[] = 'Rausin'; +$aNames[] = 'Raussain'; +$aNames[] = 'Raussent'; +$aNames[] = 'Raussin'; +$aNames[] = 'Raveydts'; +$aNames[] = 'Ravignat'; +$aNames[] = 'Remy'; +$aNames[] = 'Renard'; +$aNames[] = 'Retelet'; +$aNames[] = 'Ricaart'; +$aNames[] = 'Ricaert'; +$aNames[] = 'Ricard'; +$aNames[] = 'Robaert'; +$aNames[] = 'Robbert'; +$aNames[] = 'Robert'; +$aNames[] = 'Roels'; +$aNames[] = 'Roland'; +$aNames[] = 'Rooseels'; +$aNames[] = 'Roosengardt'; +$aNames[] = 'Rosseel'; +$aNames[] = 'Rousseau'; +$aNames[] = 'Saintmaux'; +$aNames[] = 'Saint-Maux'; +$aNames[] = 'Sanctorum'; +$aNames[] = 'Santilman'; +$aNames[] = 'Schmitz'; +$aNames[] = 'Schnock'; +$aNames[] = 'Schoenmakers'; +$aNames[] = 'Schoenman'; +$aNames[] = 'Schoone'; +$aNames[] = 'Scorier'; +$aNames[] = 'Scuvie'; +$aNames[] = 'Scuvie'; +$aNames[] = 'Segers'; +$aNames[] = 'Seghers'; +$aNames[] = 'Seppen'; +$aNames[] = 'Servais'; +$aNames[] = 'Shoen'; +$aNames[] = 'Sijmen'; +$aNames[] = 'Simoens'; +$aNames[] = 'Simon'; +$aNames[] = 'Simons'; +$aNames[] = 'Sinnesaël'; +$aNames[] = 'Sinnesal'; +$aNames[] = 'Slagmolder'; +$aNames[] = 'Slagmulder'; +$aNames[] = 'Slamulder'; +$aNames[] = 'Smal'; +$aNames[] = 'Smeets'; +$aNames[] = 'Smet'; +$aNames[] = 'Smets'; +$aNames[] = 'Smit'; +$aNames[] = 'Smolders'; +$aNames[] = 'Smulders'; +$aNames[] = 'Somers'; +$aNames[] = 'Sottiaux'; +$aNames[] = 'Spinette'; +$aNames[] = 'Sprecher'; +$aNames[] = 'Stas'; +$aNames[] = 'Stass'; +$aNames[] = 'Stassaert'; +$aNames[] = 'Stassar'; +$aNames[] = 'Stassard'; +$aNames[] = 'Stassart'; +$aNames[] = 'Stasse'; +$aNames[] = 'Stassiaux'; +$aNames[] = 'Stassin'; +$aNames[] = 'Stassinet'; +$aNames[] = 'Statius'; +$aNames[] = 'Steculorum'; +$aNames[] = 'Stefaans'; +$aNames[] = 'Stercken'; +$aNames[] = 'Sterckmans'; +$aNames[] = 'Sterckx'; +$aNames[] = 'Stevens'; +$aNames[] = 'Stier'; +$aNames[] = 'Stiers'; +$aNames[] = 'Stievens'; +$aNames[] = 'Stine'; +$aNames[] = 'Stoffel'; +$aNames[] = 'Stordair'; +$aNames[] = 'Stordeur'; +$aNames[] = 'Stoutmans'; +$aNames[] = 'Swart'; +$aNames[] = 'Swarte'; +$aNames[] = 'Tack'; +$aNames[] = 'Taverner'; +$aNames[] = 'Teissant'; +$aNames[] = 'Terreur'; +$aNames[] = 'Thijs'; +$aNames[] = 'Thiry'; +$aNames[] = 'Thissen'; +$aNames[] = 'Thomas'; +$aNames[] = 'Thonnisen'; +$aNames[] = 'Thuiliau'; +$aNames[] = 'Thuiliaux'; +$aNames[] = 'Thuiliet'; +$aNames[] = 'Thys'; +$aNames[] = 'Tibaut'; +$aNames[] = 'Timmerman'; +$aNames[] = 'Timmermans'; +$aNames[] = 'Tjampens'; +$aNames[] = "T'Jampens"; +$aNames[] = 'Toussaint'; +$aNames[] = 'Trausch'; +$aNames[] = 'Tuiliau'; +$aNames[] = 'Tuiliaux'; +$aNames[] = 'Tuilliet'; +$aNames[] = 'Tuin'; +$aNames[] = 'Tumson'; +$aNames[] = 'Tweelinckx'; +$aNames[] = 'Urbain'; +$aNames[] = 'Urting'; +$aNames[] = 'Vanbattel'; +$aNames[] = 'Vanbergh'; +$aNames[] = 'Vandamme'; +$aNames[] = 'Vandenberghe'; +$aNames[] = 'Vandenbossche'; +$aNames[] = 'Vandenbussche'; +$aNames[] = 'Vandendorpe'; +$aNames[] = 'Vandeputte'; +$aNames[] = 'Vanderhorst'; +$aNames[] = 'Vanderlinden'; +$aNames[] = 'Vanderplaetsen'; +$aNames[] = 'Vandevelde'; +$aNames[] = 'Vandoolaeghe'; +$aNames[] = 'Vandorpe'; +$aNames[] = 'Vanlierde'; +$aNames[] = 'Vanpé'; +$aNames[] = 'Vanpede'; +$aNames[] = 'Vanpée'; +$aNames[] = 'Vansteertegem'; +$aNames[] = 'Vecq'; +$aNames[] = 'Veld'; +$aNames[] = 'Veldmann'; +$aNames[] = 'Vellemans'; +$aNames[] = 'Veraghe'; +$aNames[] = 'Veraghen'; +$aNames[] = 'Verbeeck'; +$aNames[] = 'Verbeke'; +$aNames[] = 'Verbruggen'; +$aNames[] = 'Vercammen'; +$aNames[] = 'Vercheval'; +$aNames[] = 'Verdoolaeg(H)E'; +$aNames[] = 'Verhaege'; +$aNames[] = 'Verhaegen'; +$aNames[] = 'Verhaeghe'; +$aNames[] = 'Verhaeghen'; +$aNames[] = 'Verhaegue'; +$aNames[] = 'Verhage'; +$aNames[] = 'Verhagen'; +$aNames[] = 'Verhaghe'; +$aNames[] = 'Verhelst'; +$aNames[] = 'Verheyen'; +$aNames[] = 'Verhoeven'; +$aNames[] = 'Verlinden'; +$aNames[] = 'Vermeer'; +$aNames[] = 'Vermeersch'; +$aNames[] = 'Vermeiren'; +$aNames[] = 'Vermeren'; +$aNames[] = 'Vermeulen'; +$aNames[] = 'Vermotte'; +$aNames[] = 'Verplaetse'; +$aNames[] = 'Verplancke'; +$aNames[] = 'Verplancken'; +$aNames[] = 'Verschueren'; +$aNames[] = 'Verslijke'; +$aNames[] = 'Verslycke'; +$aNames[] = 'Verstraete'; +$aNames[] = 'Verstraeten'; +$aNames[] = 'Vervoort'; +$aNames[] = 'Vet'; +$aNames[] = 'Vette'; +$aNames[] = 'Viatour'; +$aNames[] = 'Vieutems'; +$aNames[] = 'Vieuxtemps'; +$aNames[] = 'Vilain'; +$aNames[] = 'Vincent'; +$aNames[] = 'Vinchent'; +$aNames[] = 'Visje'; +$aNames[] = 'Vlaamsche'; +$aNames[] = 'Vlaeminck'; +$aNames[] = 'Vlaemynck'; +$aNames[] = 'Vlaminck'; +$aNames[] = 'Vlamynck'; +$aNames[] = 'Vlemincks'; +$aNames[] = 'Vleminckx'; +$aNames[] = 'Vleminx'; +$aNames[] = 'Vlemynckx'; +$aNames[] = 'Vogels'; +$aNames[] = 'Volckaert'; +$aNames[] = 'Volkaert'; +$aNames[] = 'Volkaerts'; +$aNames[] = 'Volkart'; +$aNames[] = 'Volkert'; +$aNames[] = 'Voller'; +$aNames[] = 'Vos'; +$aNames[] = 'Vossen'; +$aNames[] = 'Vrank'; +$aNames[] = 'Vrindt'; +$aNames[] = 'Vrolijt'; +$aNames[] = 'Vrolyck'; +$aNames[] = 'Vullers'; +$aNames[] = 'Wagemans'; +$aNames[] = 'Wagenmann'; +$aNames[] = 'Waghon'; +$aNames[] = 'Wagon'; +$aNames[] = 'Walle'; +$aNames[] = 'Wastiaux'; +$aNames[] = 'Watrigant'; +$aNames[] = 'Watriquant'; +$aNames[] = 'Watteau'; +$aNames[] = 'Watteau'; +$aNames[] = 'Watteaux'; +$aNames[] = 'Watteaux'; +$aNames[] = 'Wattecamp'; +$aNames[] = 'Wattecamps'; +$aNames[] = 'Wattecant'; +$aNames[] = 'Watteel'; +$aNames[] = 'Wattel'; +$aNames[] = 'Wattelle'; +$aNames[] = 'Wattiau'; +$aNames[] = 'Wattiaux'; +$aNames[] = 'Wattieaux'; +$aNames[] = 'Wauters'; +$aNames[] = 'Weers'; +$aNames[] = 'Weerts'; +$aNames[] = 'Wek'; +$aNames[] = 'Wevers'; +$aNames[] = 'Weynen'; +$aNames[] = 'Wilbaert'; +$aNames[] = 'Wilfart'; +$aNames[] = 'Willems'; +$aNames[] = 'Willock'; +$aNames[] = 'Willocq'; +$aNames[] = 'Wilock'; +$aNames[] = 'Wintgens'; +$aNames[] = 'Wouter'; +$aNames[] = 'Wouters'; +$aNames[] = 'Wuyts'; +$aNames[] = 'Wylock'; +$aNames[] = 'Wylocke'; +$aNames[] = 'Yildirim'; +$aNames[] = 'Yilmaz'; +$aNames[] = 'Zadelaar'; +$aNames[] = 'Zegers'; +$aNames[] = 'Zeggers'; +$aNames[] = 'Zègres'; +$aNames[] = 'Bernard'; +$aNames[] = 'Bertrand'; +$aNames[] = 'Blanc'; +$aNames[] = 'Bonnet'; +$aNames[] = 'David'; +$aNames[] = 'Dubois'; +$aNames[] = 'Durand'; +$aNames[] = 'Fournier'; +$aNames[] = 'Garcia'; +$aNames[] = 'Girard'; +$aNames[] = 'Lambert'; +$aNames[] = 'Laurent'; +$aNames[] = 'Lefebvre'; +$aNames[] = 'Leroy'; +$aNames[] = 'Martin'; +$aNames[] = 'Michel'; +$aNames[] = 'Moreau'; +$aNames[] = 'Morel'; +$aNames[] = 'Petit'; +$aNames[] = 'Rang'; +$aNames[] = 'Richard'; +$aNames[] = 'Robert'; +$aNames[] = 'Rousseau'; +$aNames[] = 'Roux'; +$aNames[] = 'Simon'; +$aNames[] = 'Thomas'; +$aNames[] = 'André'; +$aNames[] = 'Chevalier'; +$aNames[] = 'Clément'; +$aNames[] = 'Dupont'; +$aNames[] = 'Faure'; +$aNames[] = 'François'; +$aNames[] = 'Garnier'; +$aNames[] = 'Gauthier'; +$aNames[] = 'Gautier'; +$aNames[] = 'Guerin'; +$aNames[] = 'Henry'; +$aNames[] = 'Lefèvre'; +$aNames[] = 'Legrand'; +$aNames[] = 'Lopez'; +$aNames[] = 'Martinez'; +$aNames[] = 'Masson'; +$aNames[] = 'Mathieu'; +$aNames[] = 'Mercier'; +$aNames[] = 'Morin'; +$aNames[] = 'Muller'; +$aNames[] = 'Nicolas'; +$aNames[] = 'Perrin'; +$aNames[] = 'Rang'; +$aNames[] = 'Robin'; +$aNames[] = 'Roussel'; +$aNames[] = 'Vincent'; +$aNames[] = 'Arnaud'; +$aNames[] = 'Blanchand'; +$aNames[] = 'Boyer'; +$aNames[] = 'Brun'; +$aNames[] = 'Brunet'; +$aNames[] = 'Denis'; +$aNames[] = 'Dufour'; +$aNames[] = 'Dumont'; +$aNames[] = 'Duval'; +$aNames[] = 'Fabre'; +$aNames[] = 'Fontaine'; +$aNames[] = 'Gaillard'; +$aNames[] = 'Giraud'; +$aNames[] = 'Joly'; +$aNames[] = 'Lemaire'; +$aNames[] = 'Lucas'; +$aNames[] = 'Marchand'; +$aNames[] = 'Meunier'; +$aNames[] = 'Meyer'; +$aNames[] = 'Noël'; +$aNames[] = 'Perez'; +$aNames[] = 'Rang'; +$aNames[] = 'Roche'; +$aNames[] = 'Roy'; +$aNames[] = 'Sanchez'; +$aNames[] = 'Vidal'; +$aNames[] = 'Adamcki'; +$aNames[] = 'Adamczak'; +$aNames[] = 'Adamczewski'; +$aNames[] = 'Adamczyk'; +$aNames[] = 'Adamiak'; +$aNames[] = 'Adamowicz'; +$aNames[] = 'Adamowitz'; +$aNames[] = 'Adamski'; +$aNames[] = 'Adamsky'; +$aNames[] = 'Adamus'; +$aNames[] = 'Ambroziak'; +$aNames[] = 'Ambroziewicz'; +$aNames[] = 'Ambrozy'; +$aNames[] = 'Andrzeg'; +$aNames[] = 'Andrzejak'; +$aNames[] = 'Antczak'; +$aNames[] = 'Antkowiak'; +$aNames[] = 'Augustyniak'; +$aNames[] = 'Balcerzak'; +$aNames[] = 'Balcrowiak'; +$aNames[] = 'Balczarek'; +$aNames[] = 'Banaszak'; +$aNames[] = 'Baranowski'; +$aNames[] = 'Bejm'; +$aNames[] = 'Biczysko'; +$aNames[] = 'Biernaciak'; +$aNames[] = 'Biernacki'; +$aNames[] = 'Biernaczyk'; +$aNames[] = 'Biernat'; +$aNames[] = 'Blaszak'; +$aNames[] = 'Blaszczak'; +$aNames[] = 'Blaszczyk'; +$aNames[] = 'Bloch'; +$aNames[] = 'Bloszak'; +$aNames[] = 'Boguslawiak'; +$aNames[] = 'Bor'; +$aNames[] = 'Brama'; +$aNames[] = 'Bressler'; +$aNames[] = 'Brezisky'; +$aNames[] = 'Brzezinski'; +$aNames[] = 'Buczak'; +$aNames[] = 'Buczek'; +$aNames[] = 'Buczko'; +$aNames[] = 'Buczkowski'; +$aNames[] = 'Buczynski'; +$aNames[] = 'Bugajny'; +$aNames[] = 'Bukowski'; +$aNames[] = 'Bulinski'; +$aNames[] = 'Bulinsky'; +$aNames[] = 'Cepak'; +$aNames[] = 'Cepek'; +$aNames[] = 'Ceremuga'; +$aNames[] = 'Cérémuga'; +$aNames[] = 'Cernota'; +$aNames[] = 'Charbul'; +$aNames[] = 'Chlebowski'; +$aNames[] = 'Chmiel'; +$aNames[] = 'Chodkowski'; +$aNames[] = 'Ciemior'; +$aNames[] = 'Ciepluch'; +$aNames[] = 'Cieplucha'; +$aNames[] = 'Ciesielski'; +$aNames[] = 'Cieslak'; +$aNames[] = 'Cieslik'; +$aNames[] = 'Cyganek'; +$aNames[] = 'Cyganik'; +$aNames[] = 'Cygankiewicz'; +$aNames[] = 'Czajka'; +$aNames[] = 'Czajkowski'; +$aNames[] = 'Czapka'; +$aNames[] = 'Czapski'; +$aNames[] = 'Czeremcha'; +$aNames[] = 'Czeremuga'; +$aNames[] = 'Dabbor'; +$aNames[] = 'Dabik'; +$aNames[] = 'Dabrowski'; +$aNames[] = 'Dambek'; +$aNames[] = 'Dambik'; +$aNames[] = 'Damian'; +$aNames[] = 'Danielak'; +$aNames[] = 'Danielczak'; +$aNames[] = 'Danilevitch'; +$aNames[] = 'Danilewicz'; +$aNames[] = 'Dawidowicz'; +$aNames[] = 'Deka'; +$aNames[] = 'Dembski'; +$aNames[] = 'Dembsky'; +$aNames[] = 'Dobosz'; +$aNames[] = 'Dobosz'; +$aNames[] = 'Dobrowolski'; +$aNames[] = 'Domin'; +$aNames[] = 'Dominiak'; +$aNames[] = 'Dominiczak'; +$aNames[] = 'Dominik'; +$aNames[] = 'Dominikowski'; +$aNames[] = 'Doroszewski'; +$aNames[] = 'Dowbor'; +$aNames[] = 'Dudek'; +$aNames[] = 'Erazmus'; +$aNames[] = 'Feliks'; +$aNames[] = 'Fialek'; +$aNames[] = 'Fijal'; +$aNames[] = 'Fijalek'; +$aNames[] = 'Fijalkowski'; +$aNames[] = 'Filipczak'; +$aNames[] = 'Filipczuk'; +$aNames[] = 'Filipek'; +$aNames[] = 'Filipiak'; +$aNames[] = 'Filipiuk'; +$aNames[] = 'Filipkowski'; +$aNames[] = 'Filipowicz'; +$aNames[] = 'Filipowski'; +$aNames[] = 'Filipski'; +$aNames[] = 'Firlej'; +$aNames[] = 'Florack'; +$aNames[] = 'Florczak'; +$aNames[] = 'Fracczak'; +$aNames[] = 'Frackowiak'; +$aNames[] = 'Franciscek'; +$aNames[] = 'Fratczak'; +$aNames[] = 'Frydryszak'; +$aNames[] = 'Gabryelczyk'; +$aNames[] = 'Gabrysiak'; +$aNames[] = 'Gaik'; +$aNames[] = 'Galinski'; +$aNames[] = 'Galka'; +$aNames[] = 'Gasztowtt'; +$aNames[] = 'Glebocki'; +$aNames[] = 'Glinczanki'; +$aNames[] = 'Glowacki'; +$aNames[] = 'Gniewek'; +$aNames[] = 'Godes'; +$aNames[] = 'Godès'; +$aNames[] = 'Godesh'; +$aNames[] = 'Gomolka'; +$aNames[] = 'Gongal'; +$aNames[] = 'Goszczynski'; +$aNames[] = 'Grabara'; +$aNames[] = 'Grabarczyk'; +$aNames[] = 'Grabarz'; +$aNames[] = 'Grabowski'; +$aNames[] = 'Grajoszek'; +$aNames[] = 'Gregorz'; +$aNames[] = 'Gryczkowiak'; +$aNames[] = 'Grzegorczyk'; +$aNames[] = 'Grzeskowiak'; +$aNames[] = 'Grzybek'; +$aNames[] = 'Grzybowski'; +$aNames[] = 'Habiera'; +$aNames[] = 'Halborn'; +$aNames[] = 'Harbul'; +$aNames[] = 'Heilpern'; +$aNames[] = 'Heilporn'; +$aNames[] = 'Heleniak'; +$aNames[] = 'Heltowni'; +$aNames[] = 'Hercberg'; +$aNames[] = 'Ickowicz'; +$aNames[] = 'Idaszek'; +$aNames[] = 'Idkowiak'; +$aNames[] = 'Iglicki'; +$aNames[] = 'Ignasiak'; +$aNames[] = 'Ignatczak'; +$aNames[] = 'Iszezuk'; +$aNames[] = 'Izydorczyk'; +$aNames[] = 'Jablonowski'; +$aNames[] = 'Jachowicz'; +$aNames[] = 'Jackowiak'; +$aNames[] = 'Jackowska'; +$aNames[] = 'Jackowski'; +$aNames[] = 'Jan'; +$aNames[] = 'Janas'; +$aNames[] = 'Janiak'; +$aNames[] = 'Janicki'; +$aNames[] = 'Janik'; +$aNames[] = 'Jankowiak'; +$aNames[] = 'Jankowski'; +$aNames[] = 'Janow'; +$aNames[] = 'Janowczyk'; +$aNames[] = 'Janowiak'; +$aNames[] = 'Janowicz'; +$aNames[] = 'Janowiec'; +$aNames[] = 'Janowski'; +$aNames[] = 'Janusz'; +$aNames[] = 'Jarosz'; +$aNames[] = 'Jaroszek'; +$aNames[] = 'Jaroszewicz'; +$aNames[] = 'Jaroszewski'; +$aNames[] = 'Jaroszuk'; +$aNames[] = 'Jaroszynski'; +$aNames[] = 'Jasicki'; +$aNames[] = 'Jasinski'; +$aNames[] = 'Jaskowiak'; +$aNames[] = 'Jedlenska'; +$aNames[] = 'Jedruch'; +$aNames[] = 'Jedrych'; +$aNames[] = 'Jedrzej'; +$aNames[] = 'Jedrzejak'; +$aNames[] = 'Jedrzejczak'; +$aNames[] = 'Jedrzejczy'; +$aNames[] = 'Jedrzejczyk'; +$aNames[] = 'Jedrzejewski'; +$aNames[] = 'Jelenski'; +$aNames[] = 'Jerzmanowski'; +$aNames[] = 'Jezierski'; +$aNames[] = 'Jodiowski'; +$aNames[] = 'Joskowiak'; +$aNames[] = 'Jozefiak'; +$aNames[] = 'Jozwiak'; +$aNames[] = 'Julia'; +$aNames[] = 'Juliusz'; +$aNames[] = 'Jura'; +$aNames[] = 'Jurak'; +$aNames[] = 'Juras'; +$aNames[] = 'Jurasz'; +$aNames[] = 'Juraszek'; +$aNames[] = 'Jurczak'; +$aNames[] = 'Jurczuk'; +$aNames[] = 'Jurczyk'; +$aNames[] = 'Jurczynski'; +$aNames[] = 'Jurek'; +$aNames[] = 'Martin'; +$aNames[] = 'Durand'; +$aNames[] = 'Robert'; +$aNames[] = 'Dupont'; +$aNames[] = 'Bernard'; +$aNames[] = 'Dubois'; +$aNames[] = 'Petit'; +$aNames[] = 'Cohen'; +$aNames[] = 'Richard'; +$aNames[] = 'Moreau'; +$aNames[] = 'Thomas'; +$aNames[] = 'Nguyen'; +$aNames[] = 'Simon'; +$aNames[] = 'Laurent'; +$aNames[] = 'Michel'; +$aNames[] = 'Levy'; +$aNames[] = 'Vincent'; +$aNames[] = 'David'; +$aNames[] = 'Bertrand'; +$aNames[] = 'Rousseau'; +$aNames[] = 'Leroy'; +$aNames[] = 'Girard'; +$aNames[] = 'Fournier'; +$aNames[] = 'Roux'; +$aNames[] = 'Andre'; +$aNames[] = 'Garnier'; +$aNames[] = 'Mercier'; +$aNames[] = 'Morin'; +$aNames[] = 'Jean'; +$aNames[] = 'Denis'; +$aNames[] = 'Henry'; +$aNames[] = 'Duval'; +$aNames[] = 'Morel'; +$aNames[] = 'Nicolas'; +$aNames[] = 'Francois'; +$aNames[] = 'Blanc'; +$aNames[] = 'Faure'; +$aNames[] = 'Pierre'; +$aNames[] = 'Garcia'; +$aNames[] = 'Lambert'; +$aNames[] = 'Gautier'; +$aNames[] = 'Lefebvre'; +$aNames[] = 'Meunier'; +$aNames[] = 'Lefevre'; +$aNames[] = 'Perrin'; +$aNames[] = 'Legrand'; +$aNames[] = 'Clement'; +$aNames[] = 'Mathieu'; +$aNames[] = 'Bonnet'; +$aNames[] = 'Chevalier'; +$aNames[] = 'Muller'; +$aNames[] = 'Fontaine'; +$aNames[] = 'Robin'; +$aNames[] = 'Blanchard'; +$aNames[] = 'Perez'; +$aNames[] = 'Guerin'; +$aNames[] = 'Masson'; +$aNames[] = 'Roger'; +$aNames[] = 'Gauthier'; +$aNames[] = 'Dupuis'; +$aNames[] = 'Martinez'; +$aNames[] = 'Leroux'; +$aNames[] = 'Bourgeois'; +$aNames[] = 'Dumont'; +$aNames[] = 'Dupond'; +$aNames[] = 'Paul'; +$aNames[] = 'Louis'; +$aNames[] = 'Dufour'; +$aNames[] = 'Meyer'; +$aNames[] = 'Lacroix'; +$aNames[] = 'Noel'; +$aNames[] = 'Fabre'; +$aNames[] = 'Lopez'; +$aNames[] = 'Gerard'; +$aNames[] = 'Lemaire'; +$aNames[] = 'Roussel'; +$aNames[] = 'Berger'; +$aNames[] = 'Barbier'; +$aNames[] = 'Boyer'; +$aNames[] = 'Brun'; +$aNames[] = 'Vidal'; +$aNames[] = 'Giraud'; +$aNames[] = 'Fernandez'; +$aNames[] = 'Marie'; +$aNames[] = 'Roche'; +$aNames[] = 'Colin'; +$aNames[] = 'Tran'; +$aNames[] = 'Marchand'; +$aNames[] = 'Lemoine'; +$aNames[] = 'Charpentier'; + +$aCountries = array(); +$aCountries[] = 'France'; +$aCountries[] = 'United Kingdom'; +$aCountries[] = 'Germany'; +$aCountries[] = 'United States of America'; +$aCountries[] = 'Canada'; +$aCountries[] = 'Switzerland'; +$aCountries[] = 'Belgium'; +$aCountries[] = 'Poland'; +$aCountries[] = 'Slovakia'; +$aCountries[] = 'Italy'; +$aCountries[] = 'Spain'; +$aCountries[] = 'Bulgaria'; + +$aCities = array(); +$aCities[]="Orvault"; +$aCities[]="Oullins"; +$aCities[]="Oyonnax"; +$aCities[]="Ozoir-la-Ferrière"; +$aCities[]="Palaiseau"; +$aCities[]="Pantin"; +$aCities[]="Paris"; +$aCities[]="Pau"; +$aCities[]="Périgueux"; +$aCities[]="Perpignan"; +$aCities[]="Le Perreux-sur-Marne"; +$aCities[]="Pessac"; +$aCities[]="Le Petit-Quevilly"; +$aCities[]="Pierrefitte-sur-Seine"; +$aCities[]="Plaisir"; +$aCities[]="Le Plessis-Robinson"; +$aCities[]="Poissy"; +$aCities[]="Poitiers"; +$aCities[]="Pontault-Combault"; +$aCities[]="Pontoise"; +$aCities[]="Le Port"; +$aCities[]="La Possession"; +$aCities[]="Puteaux"; +$aCities[]="Le Puy-en-Velay"; +$aCities[]="Quimper"; +$aCities[]="Rambouillet"; +$aCities[]="Reims"; +$aCities[]="Rennes"; +$aCities[]="Rezé"; +$aCities[]="Rillieux-la-Pape"; +$aCities[]="Ris-Orangis"; +$aCities[]="Roanne"; +$aCities[]="Rochefort"; +$aCities[]="La Rochelle"; +$aCities[]="La Roche-sur-Yon"; +$aCities[]="Rodez"; +$aCities[]="Romainville"; +$aCities[]="Romans-sur-Isère"; +$aCities[]="Rosny-sous-Bois"; +$aCities[]="Roubaix"; +$aCities[]="Rouen"; +$aCities[]="Rueil-Malmaison"; +$aCities[]="Saint-André"; +$aCities[]="Saint-Benoît"; +$aCities[]="Saint-Brieuc"; +$aCities[]="Saint-Chamond"; +$aCities[]="Saint-Cloud"; +$aCities[]="Saint-Denis"; +$aCities[]="Saint-Denis"; +$aCities[]="Saint-Dié-des-Vosges"; +$aCities[]="Saint-Dizier"; +$aCities[]="Sainte-Foy-lès-Lyon"; +$aCities[]="Sainte-Geneviève-des-Bois"; +$aCities[]="Saintes"; +$aCities[]="Saint-Étienne"; +$aCities[]="Saint-Étienne-du-Rouvray"; +$aCities[]="Saint-Germain-en-Laye"; +$aCities[]="Saint-Herblain"; +$aCities[]="Saint-Joseph"; +$aCities[]="Saint-Laurent-du-Var"; +$aCities[]="Saint-Leu"; +$aCities[]="Saint-Lô"; +$aCities[]="Saint-Louis"; +$aCities[]="Saint-Louis"; +$aCities[]="Saint-Malo"; +$aCities[]="Saint-Martin-d'Hères"; +$aCities[]="Saint-Maur-des-Fossés"; +$aCities[]="Saint-Médard-en-Jalles"; +$aCities[]="Saint-Michel-sur-Orge"; +$aCities[]="Saint-Nazaire"; +$aCities[]="Saint-Ouen"; +$aCities[]="Saint-Paul"; +$aCities[]="Saint-Pierre"; +$aCities[]="Saint-Pol-sur-Mer"; +$aCities[]="Saint-Priest"; +$aCities[]="Saint-Quentin"; +$aCities[]="Saint-Raphaël"; +$aCities[]="Saint-Sébastien-sur-Loire"; +$aCities[]="Salon-de-Provence"; +$aCities[]="Sannois"; +$aCities[]="Sarcelles"; +$aCities[]="Sarreguemines"; +$aCities[]="Sartrouville"; +$aCities[]="Saumur"; +$aCities[]="Savigny-le-Temple"; +$aCities[]="Savigny-sur-Orge"; +$aCities[]="Schiltigheim"; +$aCities[]="Sedan"; +$aCities[]="Sens"; +$aCities[]="Sète"; +$aCities[]="Sevran"; +$aCities[]="Sèvres"; +$aCities[]="La Seyne-sur-Mer"; +$aCities[]="Six-Fours-les-Plages"; +$aCities[]="Soissons"; +$aCities[]="Sotteville-lès-Rouen"; +$aCities[]="Stains"; +$aCities[]="Strasbourg"; +$aCities[]="Sucy-en-Brie"; +$aCities[]="Suresnes"; +$aCities[]="Talence"; +$aCities[]="Le Tampon"; +$aCities[]="Tarbes"; +$aCities[]="Taverny"; +$aCities[]="La Teste-de-Buch"; +$aCities[]="Thiais"; +$aCities[]="Thionville"; +$aCities[]="Thonon-les-Bains"; +$aCities[]="Torcy"; +$aCities[]="Toulon"; +$aCities[]="Toulouse"; +$aCities[]="Tourcoing"; +$aCities[]="Tournefeuille"; +$aCities[]="Tours"; +$aCities[]="Trappes"; +$aCities[]="Tremblay-en-France"; +$aCities[]="Troyes"; +$aCities[]="Les Ulis"; +$aCities[]="Valence"; +$aCities[]="Valenciennes"; +$aCities[]="La Valette-du-Var"; +$aCities[]="Vallauris"; +$aCities[]="Vandœuvre-lès-Nancy"; +$aCities[]="Vannes"; +$aCities[]="Vanves"; +$aCities[]="Vaulx-en-Velin"; +$aCities[]="Vélizy-Villacoublay"; +$aCities[]="Vénissieux"; +$aCities[]="Verdun"; +$aCities[]="Vernon"; +$aCities[]="Versailles"; +$aCities[]="Vertou"; +$aCities[]="Vichy"; +$aCities[]="Vienne"; +$aCities[]="Vierzon"; +$aCities[]="Vigneux-sur-Seine"; +$aCities[]="Villefranche-sur-Saône"; +$aCities[]="Villejuif"; +$aCities[]="Villemomble"; +$aCities[]="Villenave-d'Ornon"; +$aCities[]="Villeneuve-d'Ascq"; +$aCities[]="Villeneuve-la-Garenne"; +$aCities[]="Villeneuve-Saint-Georges"; +$aCities[]="Villeneuve-sur-Lot"; +$aCities[]="Villeparisis"; +$aCities[]="Villepinte"; +$aCities[]="Villeurbanne"; +$aCities[]="Villiers-le-Bel"; +$aCities[]="Villiers-sur-Marne"; +$aCities[]="Vincennes"; +$aCities[]="Viry-Châtillon"; +$aCities[]="Vitrolles"; +$aCities[]="Vitry-sur-Seine"; +$aCities[]="Voiron"; +$aCities[]="Wattrelos"; +$aCities[]="Yerres"; +$aCities[]="Madrid"; +$aCities[]="Mataró"; +$aCities[]="Málaga"; +$aCities[]="Manzanares"; +$aCities[]="Marbella"; +$aCities[]="Marines"; +$aCities[]="Melilla"; +$aCities[]="Mijas"; +$aCities[]="Móstoles"; +$aCities[]="Murcie"; +$aCities[]="Sabadell"; +$aCities[]="Sagonte"; +$aCities[]="Salamanque"; +$aCities[]="Saint-Sébastien"; +$aCities[]="San Antonio de Benagéber"; +$aCities[]="Sant Boi de Llobregat"; +$aCities[]="Santa Cruz de Tenerife"; +$aCities[]="Santander"; +$aCities[]="Saragosse"; +$aCities[]="Ségovia"; +$aCities[]="Serra"; +$aCities[]="Séville"; +$aCities[]="Sitges"; +$aCities[]="Sóller"; +$aCities[]="Soria"; +$aCities[]="Aberdeen"; +$aCities[]="Aberystwyth"; +$aCities[]="Ashford"; +$aCities[]="Armagh"; +$aCities[]="Amersham"; +$aCities[]="Andover"; +$aCities[]="Arundel"; +$aCities[]="Abingdon"; +$aCities[]="Aylesbury"; +$aCities[]="Ayr"; +$aCities[]="Barrow-in-Furness"; +$aCities[]="Bath"; +$aCities[]="Bangor"; +$aCities[]="Bangor"; +$aCities[]="Belfast"; +$aCities[]="Birmingham"; +$aCities[]="Bournemouth"; +$aCities[]="Bradford"; +$aCities[]="Brighton"; +$aCities[]="Bristol"; +$aCities[]="Barrow-in-Furness"; +$aCities[]="Bath"; +$aCities[]="Bangor"; +$aCities[]="Bangor"; +$aCities[]="Belfast"; +$aCities[]="Birmingham"; +$aCities[]="Bournemouth"; +$aCities[]="Bradford"; +$aCities[]="Brighton"; +$aCities[]="Bristol"; +$aCities[]="Eastbourne"; +$aCities[]="Édimbourg"; +$aCities[]="Ely"; +$aCities[]="Epsom"; +$aCities[]="Eton"; +$aCities[]="Evanton"; +$aCities[]="Exeter"; +$aCities[]="Lancaster"; +$aCities[]="Larne"; +$aCities[]="Leeds"; +$aCities[]="Leicester"; +$aCities[]="Lichfield"; +$aCities[]="Lincoln"; +$aCities[]="Lisburn"; +$aCities[]="Liverpool"; +$aCities[]="Londres"; +$aCities[]="Londonderry"; +$aCities[]="Newcastle-upon-Tyne"; +$aCities[]="Newport"; +$aCities[]="Newry"; +$aCities[]="Norwich"; +$aCities[]="Nottingham"; +$aCities[]="Reading"; +$aCities[]="Ripon"; +$aCities[]="Richmond"; +$aCities[]="Ripley"; +$aCities[]="Rugby"; +$aCities[]="Wakefield"; +$aCities[]="Wells"; +$aCities[]="Westminster"; +$aCities[]="Weymouth"; +$aCities[]="Wick"; +$aCities[]="Wigan"; +$aCities[]="Winchester"; +$aCities[]="Windsor"; +$aCities[]="Wolverhampton"; +$aCities[]="Worcester"; +$aCities[]="Wye"; +$aCities[]="Abbeyville"; +$aCities[]="Abbeyville"; +$aCities[]="Aberdeen"; +$aCities[]="Abilene"; +$aCities[]="Abilene"; +$aCities[]="Acron"; +$aCities[]="Ada"; +$aCities[]="Adelanto"; +$aCities[]="Affton"; +$aCities[]="Agoura Hills"; +$aCities[]="Aitkin"; +$aCities[]="Akron"; +$aCities[]="Alabaster"; +$aCities[]="Alameda"; +$aCities[]="Alamogordo"; +$aCities[]="Albany"; +$aCities[]="Albany"; +$aCities[]="Albany"; +$aCities[]="Albuquerque"; +$aCities[]="Alexandria"; +$aCities[]="Alhambra"; +$aCities[]="Alhambra"; +$aCities[]="Aliso Viejo"; +$aCities[]="Allentown"; +$aCities[]="Alton"; +$aCities[]="Alturas"; +$aCities[]="Amador City"; +$aCities[]="Amarillo"; +$aCities[]="American Canyon"; +$aCities[]="Ames"; +$aCities[]="Amesbury"; +$aCities[]="Amherst"; +$aCities[]="Amherst"; +$aCities[]="Anaheim"; +$aCities[]="Anchorage"; +$aCities[]="Anderson"; +$aCities[]="Anderson"; +$aCities[]="Andover"; +$aCities[]="Angels Camp"; +$aCities[]="Ann Arbor"; +$aCities[]="Annapolis"; +$aCities[]="Anniston"; +$aCities[]="Antioch"; +$aCities[]="Apex"; +$aCities[]="Apple Valley"; +$aCities[]="Appleton"; +$aCities[]="Appleton"; +$aCities[]="Appleton"; +$aCities[]="Appomattox"; +$aCities[]="Arcadia"; +$aCities[]="Arcata"; +$aCities[]="Arlington"; +$aCities[]="Arlington"; +$aCities[]="Arlington"; +$aCities[]="Armonk"; +$aCities[]="Arnaudville"; +$aCities[]="Arnold"; +$aCities[]="Arroyo Grande"; +$aCities[]="Artesia"; +$aCities[]="Arvin"; +$aCities[]="Asbury Park"; +$aCities[]="Asheville"; +$aCities[]="Ashland"; +$aCities[]="Aspen"; +$aCities[]="Assonet"; +$aCities[]="Atascadero"; +$aCities[]="Athens"; +$aCities[]="Athens"; +$aCities[]="Atherton"; +$aCities[]="Atlanta"; +$aCities[]="Atlantic City"; +$aCities[]="Attica"; +$aCities[]="Atwater"; +$aCities[]="Auburn"; +$aCities[]="Auburn"; +$aCities[]="Augusta"; +$aCities[]="Augusta"; +$aCities[]="Aurora"; +$aCities[]="Austin"; +$aCities[]="Autaugaville"; +$aCities[]="Ava"; +$aCities[]="Avalon"; +$aCities[]="Ave Maria"; +$aCities[]="Avenal"; +$aCities[]="Avon"; +$aCities[]="Avondale"; +$aCities[]="Azusa"; +$aCities[]="Bakersfield"; +$aCities[]="Baltimore"; +$aCities[]="Baton Rouge"; +$aCities[]="Bay Minette"; +$aCities[]="Berkeley"; +$aCities[]="Bessemer"; +$aCities[]="Billings"; +$aCities[]="Billingsley"; +$aCities[]="Biloxi"; +$aCities[]="Birmingham"; +$aCities[]="Bismarck"; +$aCities[]="Boise"; +$aCities[]="Boston"; +$aCities[]="Branson"; +$aCities[]="Brownsville"; +$aCities[]="Buffalo"; +$aCities[]="Burlington"; +$aCities[]="Cambridge"; +$aCities[]="Canton"; +$aCities[]="Carmel"; +$aCities[]="Carson City"; +$aCities[]="Casper"; +$aCities[]="Cedar Rapids"; +$aCities[]="Champaign"; +$aCities[]="Charleston"; +$aCities[]="Charleston"; +$aCities[]="Charlotte"; +$aCities[]="Charlottesville"; +$aCities[]="Chattanooga"; +$aCities[]="Cheyenne"; +$aCities[]="Chicago"; +$aCities[]="Cicero"; +$aCities[]="Cincinnati"; +$aCities[]="Cleveland"; +$aCities[]="Colby"; +$aCities[]="Colorado Springs"; +$aCities[]="Columbia"; +$aCities[]="Columbus"; +$aCities[]="Concord"; +$aCities[]="Corpus Christi"; +$aCities[]="Cullman"; +$aCities[]="Dallas"; +$aCities[]="Daphne"; +$aCities[]="Davenport"; +$aCities[]="Dayton"; +$aCities[]="Daytona Beach"; +$aCities[]="Denver"; +$aCities[]="Des Moines"; +$aCities[]="Detroit"; +$aCities[]="Dodge City"; +$aCities[]="Dothan"; +$aCities[]="Dover"; +$aCities[]="Duluth"; +$aCities[]="El Paso"; +$aCities[]="Enterprise"; +$aCities[]="Érié"; +$aCities[]="Eugene"; +$aCities[]="Evansville"; +$aCities[]="Fairfield"; +$aCities[]="Fairbanks"; +$aCities[]="Fargo"; +$aCities[]="Fayetteville"; +$aCities[]="Fayetteville"; +$aCities[]="Flagstaff"; +$aCities[]="Flint"; +$aCities[]="Fort Collins"; +$aCities[]="Fort Lauderdale"; +$aCities[]="Fort Worth"; +$aCities[]="Frankfort"; +$aCities[]="Fresno"; +$aCities[]="Grand Rapids"; +$aCities[]="Great Falls"; +$aCities[]="Green Bay"; +$aCities[]="Greensboro"; +$aCities[]="Greenville"; +$aCities[]="Harrisburg"; +$aCities[]="Hartford"; +$aCities[]="Hays"; +$aCities[]="Helena"; +$aCities[]="Honolulu"; +$aCities[]="Hoover"; +$aCities[]="Hot Springs"; +$aCities[]="Houston"; +$aCities[]="Huntington"; +$aCities[]="Huntsville"; +$aCities[]="Jackson"; +$aCities[]="Jacksonville"; +$aCities[]="Jefferson City"; +$aCities[]="Joplin"; +$aCities[]="Juneau"; +$aCities[]="Idaho Falls"; +$aCities[]="Indianapolis"; +$aCities[]="Lafayette"; +$aCities[]="Lancaster"; +$aCities[]="Lansing"; +$aCities[]="Laredo"; +$aCities[]="Las Cruces"; +$aCities[]="Las Vegas"; +$aCities[]="Leavenworth"; +$aCities[]="Lexington"; +$aCities[]="Lincoln"; +$aCities[]="Little Rock"; +$aCities[]="Littleton"; +$aCities[]="Los Alamos"; +$aCities[]="Los Angeles"; +$aCities[]="Louisville"; +$aCities[]="Lubbock"; +$aCities[]="Madison"; +$aCities[]="Manchester"; +$aCities[]="McAllen"; +$aCities[]="Melbourne"; +$aCities[]="Memphis"; +$aCities[]="Mesa"; +$aCities[]="Miami"; +$aCities[]="Milbrook"; +$aCities[]="Milwaukee"; +$aCities[]="Minneapolis"; +$aCities[]="Missoula"; +$aCities[]="Mobile"; +$aCities[]="Modesto"; +$aCities[]="Monterey"; +$aCities[]="Montgomery"; +$aCities[]="Montpelier"; +$aCities[]="Mountain View"; +$aCities[]="Muncie"; +$aCities[]="Nashville"; +$aCities[]="New Bedford"; +$aCities[]="New Haven"; +$aCities[]="Newport"; +$aCities[]="New York"; +$aCities[]="Niagara Falls"; +$aCities[]="Norfolk"; +$aCities[]="La Nouvelle-Orléans"; +$aCities[]="Newark"; +$aCities[]="Oakland"; +$aCities[]="Oklahoma City"; +$aCities[]="Olympia"; +$aCities[]="Omaha"; +$aCities[]="Orem"; +$aCities[]="Orlando"; +$aCities[]="Palm Springs"; +$aCities[]="Palo Alto"; +$aCities[]="Pearl Harbor"; +$aCities[]="Pensacola"; +$aCities[]="Peoria"; +$aCities[]="Philadelphie"; +$aCities[]="Phoenix"; +$aCities[]="Pierre"; +$aCities[]="Pittsburgh"; +$aCities[]="Point du lac"; +$aCities[]="Portland"; +$aCities[]="Portland"; +$aCities[]="Prattville"; +$aCities[]="Providence"; +$aCities[]="Racine"; +$aCities[]="Raleigh"; +$aCities[]="Rapid City"; +$aCities[]="Redding"; +$aCities[]="Redmond"; +$aCities[]="Redwood City"; +$aCities[]="Reno"; +$aCities[]="Richmond"; +$aCities[]="Roanoke"; +$aCities[]="Rochester"; +$aCities[]="Rockford"; +$aCities[]="Sacramento"; +$aCities[]="Saint Louis"; +$aCities[]="Saint Paul"; +$aCities[]="Salem"; +$aCities[]="Salina"; +$aCities[]="Salt Lake City"; +$aCities[]="San Antonio"; +$aCities[]="San Bernardino"; +$aCities[]="San Diego"; +$aCities[]="San Francisco"; +$aCities[]="San José"; +$aCities[]="San Luis Obispo"; +$aCities[]="San Mateo"; +$aCities[]="Santa Barbara"; +$aCities[]="Santa Cruz"; +$aCities[]="Santa Fe"; +$aCities[]="Sarasota"; +$aCities[]="Saratoga"; +$aCities[]="Savannah"; +$aCities[]="Scranton"; +$aCities[]="Seattle"; +$aCities[]="Shreveport"; +$aCities[]="Sioux Falls"; +$aCities[]="South Bend"; +$aCities[]="Spokane"; +$aCities[]="Springfield"; +$aCities[]="Springfield"; +$aCities[]="Springfield"; +$aCities[]="Stamford"; +$aCities[]="Stockton"; +$aCities[]="Syracuse"; +$aCities[]="Tacoma"; +$aCities[]="Tallahassee"; +$aCities[]="Tampa"; +$aCities[]="Taunton"; +$aCities[]="Toledo"; +$aCities[]="Topeka"; +$aCities[]="Trenton"; +$aCities[]="Tucson"; +$aCities[]="Tulsa"; +$aCities[]="Tuscaloosa"; +$aCities[]="Tuskegee"; +$aCities[]="Waco"; +$aCities[]="Washington, DC"; +$aCities[]="Waterbury"; +$aCities[]="Wichita"; +$aCities[]="Williamsburg"; +$aCities[]="Wilmington"; +$aCities[]="Wilmington"; +$aCities[]="Worcester"; +$aCities[]="Babenhausen"; +$aCities[]="Bacharach"; +$aCities[]="Backnang"; +$aCities[]="Bad Aibling"; +$aCities[]="Bad Arolsen"; +$aCities[]="Bad Bentheim"; +$aCities[]="Bad Bergzabern"; +$aCities[]="Bad Berka"; +$aCities[]="Bad Berleburg"; +$aCities[]="Bad Berneck im Fichtelgebirge"; +$aCities[]="Bad Bevensen"; +$aCities[]="Bad Bibra"; +$aCities[]="Bad Blankenburg"; +$aCities[]="Bad Bramstedt"; +$aCities[]="Bad Breisig"; +$aCities[]="Bad Brückenau"; +$aCities[]="Bad Buchau"; +$aCities[]="Bad Camberg"; +$aCities[]="Bad Colberg-Heldburg"; +$aCities[]="Bad Doberan"; +$aCities[]="Bad Driburg"; +$aCities[]="Bad Düben"; +$aCities[]="Bad Dürkheim"; +$aCities[]="Bad Dürrenberg"; +$aCities[]="Bad Dürrheim"; +$aCities[]="Bad Elster"; +$aCities[]="Bad Ems"; +$aCities[]="Baden-Baden"; +$aCities[]="Bad Fallingbostel"; +$aCities[]="Bad Frankenhausen"; +$aCities[]="Bad Freienwalde"; +$aCities[]="Bad Friedrichshall"; +$aCities[]="Bad Gandersheim"; +$aCities[]="Bad Gottleuba-Berggießhübel"; +$aCities[]="Bad Griesbach im Rottal"; +$aCities[]="Bad Grund"; +$aCities[]="Bad Harzburg"; +$aCities[]="Bad Herrenalb"; +$aCities[]="Bad Hersfeld"; +$aCities[]="Bad Homburg vor der Höhe"; +$aCities[]="Bad Honnef"; +$aCities[]="Bad Hönningen"; +$aCities[]="Calau"; +$aCities[]="Calbe"; +$aCities[]="Calw"; +$aCities[]="Camburg"; +$aCities[]="Castrop-Rauxel"; +$aCities[]="Celle"; +$aCities[]="Cham"; +$aCities[]="Chemnitz"; +$aCities[]="Clausthal-Zellerfeld"; +$aCities[]="Cloppenburg"; +$aCities[]="Coburg"; +$aCities[]="Cochem"; +$aCities[]="Coesfeld"; +$aCities[]="Colditz"; +$aCities[]="Coswig"; +$aCities[]="Coswig"; +$aCities[]="Crailsheim"; +$aCities[]="Creglingen"; +$aCities[]="Creussen"; +$aCities[]="Creuzburg"; +$aCities[]="Crimmitschau"; +$aCities[]="Crivitz"; +$aCities[]="Cuxhaven"; +$aCities[]="Bad Iburg"; +$aCities[]="Bad Karlshafen"; +$aCities[]="Bad Kissingen"; +$aCities[]="Bad König"; +$aCities[]="Bad Königshofen im Grabfeld"; +$aCities[]="Bad Kösen"; +$aCities[]="Bad Köstritz"; +$aCities[]="Bad Kötzting"; +$aCities[]="Bad Kreuznach"; +$aCities[]="Bad Krozingen"; +$aCities[]="Bad Laasphe"; +$aCities[]="Bad Langensalza"; +$aCities[]="Bad Lauchstädt"; +$aCities[]="Bad Lausick"; +$aCities[]="Bad Lauterberg im Harz"; +$aCities[]="Bad Liebenstein"; +$aCities[]="Bad Liebenwerda"; +$aCities[]="Bad Liebenzell"; +$aCities[]="Bad Lippspringe"; +$aCities[]="Bad Lobenstein"; +$aCities[]="Bad Marienberg"; +$aCities[]="Bad Mergentheim"; +$aCities[]="Bad Münder am Deister"; +$aCities[]="Bad Münster am Stein-Ebernburg"; +$aCities[]="Bad Münstereifel"; +$aCities[]="Bad Muskau"; +$aCities[]="Bad Nauheim"; +$aCities[]="Bad Nenndorf"; +$aCities[]="Bad Neuenahr-Ahrweiler"; +$aCities[]="Bad Neustadt an der Saale"; +$aCities[]="Bad Oeynhausen"; +$aCities[]="Bad Oldesloe"; +$aCities[]="Bad Orb"; +$aCities[]="Bad Pyrmont"; +$aCities[]="Bad Rappenau"; +$aCities[]="Bad Reichenhall"; +$aCities[]="Bad Rodach"; +$aCities[]="Bad Sachsa"; +$aCities[]="Bad Säckingen"; +$aCities[]="Bad Salzdetfurth"; +$aCities[]="Bad Salzuflen"; +$aCities[]="Bad Salzungen"; +$aCities[]="Bad Saulgau"; +$aCities[]="Bad Schandau"; +$aCities[]="Bad Schmiedeberg"; +$aCities[]="Bad Schussenried"; +$aCities[]="Bad Schwalbach"; +$aCities[]="Bad Schwartau"; +$aCities[]="Bad Segeberg"; +$aCities[]="Bad Soden am Taunus"; +$aCities[]="Bad Soden-Salmünster"; +$aCities[]="Bad Sooden-Allendorf"; +$aCities[]="Bad Staffelstein"; +$aCities[]="Bad Sulza"; +$aCities[]="Bad Sülze"; +$aCities[]="Bad Teinach-Zavelstein"; +$aCities[]="Bad Tennstedt"; +$aCities[]="Bad Tölz"; +$aCities[]="Bad Urach"; +$aCities[]="Bad Vilbel"; +$aCities[]="Bad Waldsee"; +$aCities[]="Bad Wildbad"; +$aCities[]="Bad Wildungen"; +$aCities[]="Bad Wilsnack"; +$aCities[]="Bad Wimpfen"; +$aCities[]="Bad Windsheim"; +$aCities[]="Bad Wörishofen"; +$aCities[]="Bad Wünnenberg"; +$aCities[]="Bad Wurzach"; +$aCities[]="Baesweiler"; +$aCities[]="Baiersdorf"; +$aCities[]="Balingen"; +$aCities[]="Ballenstedt"; +$aCities[]="Balve"; +$aCities[]="Bamberg"; +$aCities[]="Barby"; +$aCities[]="Bargteheide"; +$aCities[]="Barmstedt"; +$aCities[]="Bärnau"; +$aCities[]="Barntrup"; +$aCities[]="Barsinghausen"; +$aCities[]="Barth"; +$aCities[]="Baruth/Mark"; +$aCities[]="Bassum"; +$aCities[]="Battenberg"; +$aCities[]="Baumholder"; +$aCities[]="Baunach"; +$aCities[]="Baunatal"; +$aCities[]="Bautzen"; +$aCities[]="Bayreuth"; +$aCities[]="Bebra"; +$aCities[]="Beckum"; +$aCities[]="Bedburg"; +$aCities[]="Beelitz"; +$aCities[]="Beerfelden"; +$aCities[]="Beeskow"; +$aCities[]="Beilngries"; +$aCities[]="Beilstein"; +$aCities[]="Belgern"; +$aCities[]="Belzig"; +$aCities[]="Bendorf"; +$aCities[]="Benneckenstein"; +$aCities[]="Bensheim"; +$aCities[]="Berching"; +$aCities[]="Berga/Elster"; +$aCities[]="Bergen"; +$aCities[]="Bergen auf Rügen"; +$aCities[]="Bergheim"; +$aCities[]="Bergisch Gladbach"; +$aCities[]="Bergkamen"; +$aCities[]="Bergneustadt"; +$aCities[]="Berka/Werra"; +$aCities[]="Berlin"; +$aCities[]="Bernau bei Berlin"; +$aCities[]="Bernburg"; +$aCities[]="Bernkastel-Kues"; +$aCities[]="Bernsdorf"; +$aCities[]="Bernstadt a. d. Eigen"; +$aCities[]="Bersenbrück"; +$aCities[]="Besigheim"; +$aCities[]="Betzdorf"; +$aCities[]="Betzenstein"; +$aCities[]="Beverungen"; +$aCities[]="Bexbach"; +$aCities[]="Biberach an der Riß"; +$aCities[]="Biedenkopf"; +$aCities[]="Bielefeld"; +$aCities[]="Biesenthal"; +$aCities[]="Bietigheim-Bissingen"; +$aCities[]="Billerbeck"; +$aCities[]="Bingen am Rhein"; +$aCities[]="Birkenfeld"; +$aCities[]="Bischofsheim an der Rhön"; +$aCities[]="Bischofswerda"; +$aCities[]="Bismark"; +$aCities[]="Bitburg"; +$aCities[]="Bitterfeld"; +$aCities[]="Blankenburg"; +$aCities[]="Blankenhain"; +$aCities[]="Bleckede"; +$aCities[]="Bleicherode"; +$aCities[]="Blieskastel"; +$aCities[]="Blomberg"; +$aCities[]="Blumberg"; +$aCities[]="Bobingen"; +$aCities[]="Böblingen"; +$aCities[]="Bocholt"; +$aCities[]="Bochum"; +$aCities[]="Bockenem"; +$aCities[]="Bodenwerder"; +$aCities[]="Bogen"; +$aCities[]="Böhlen"; +$aCities[]="Boizenburg/Elbe"; +$aCities[]="Bonn"; +$aCities[]="Bonndorf im Schwarzwald"; +$aCities[]="Bönnigheim"; +$aCities[]="Bopfingen"; +$aCities[]="Boppard"; +$aCities[]="Borgentreich"; +$aCities[]="Borgholzhausen"; +$aCities[]="Borken"; +$aCities[]="Borken"; +$aCities[]="Borkum"; +$aCities[]="Borna"; +$aCities[]="Bornheim"; +$aCities[]="Bottrop"; +$aCities[]="Boxberg"; +$aCities[]="Brackenheim"; +$aCities[]="Brake"; +$aCities[]="Brakel"; +$aCities[]="Bramsche"; +$aCities[]="Brandenburg an der Havel"; +$aCities[]="Brand-Erbisdorf"; +$aCities[]="Brandis"; +$aCities[]="Braubach"; +$aCities[]="Braunfels"; +$aCities[]="Braunlage"; +$aCities[]="Bräunlingen"; +$aCities[]="Braunsbedra"; +$aCities[]="Braunschweig"; +$aCities[]="Breckerfeld"; +$aCities[]="Bredstedt"; +$aCities[]="Brehna"; +$aCities[]="Breisach am Rhein"; +$aCities[]="Bremen"; +$aCities[]="Bremerhaven"; +$aCities[]="Bremervörde"; +$aCities[]="Bretten"; +$aCities[]="Breuberg"; +$aCities[]="Brilon"; +$aCities[]="Brotterode"; +$aCities[]="Bruchköbel"; +$aCities[]="Bruchsal"; +$aCities[]="Brück"; +$aCities[]="Brüel"; +$aCities[]="Brühl"; +$aCities[]="Brunsbüttel"; +$aCities[]="Brüssow"; +$aCities[]="Buchen"; +$aCities[]="Buchholz in der Nordheide"; +$aCities[]="Buchloe"; +$aCities[]="Bückeburg"; +$aCities[]="Buckow"; +$aCities[]="Büdelsdorf"; +$aCities[]="Büdingen"; +$aCities[]="Bühl"; +$aCities[]="Bünde"; +$aCities[]="Büren"; +$aCities[]="Burg"; +$aCities[]="Burgau"; +$aCities[]="Burgbernheim"; +$aCities[]="Burgdorf"; +$aCities[]="Bürgel"; +$aCities[]="Burghausen"; +$aCities[]="Burgkunstadt"; +$aCities[]="Burglengenfeld"; +$aCities[]="Burgstädt"; +$aCities[]="Burg Stargard"; +$aCities[]="Burgwedel"; +$aCities[]="Burladingen"; +$aCities[]="Burscheid"; +$aCities[]="Bürstadt"; +$aCities[]="Buttelstedt"; +$aCities[]="Buttstädt"; +$aCities[]="Butzbach"; +$aCities[]="Bützow"; +$aCities[]="Buxtehude"; + +?> diff --git a/business/incident.business.php b/business/incident.business.php new file mode 100644 index 000000000..c41d7dd6b --- /dev/null +++ b/business/incident.business.php @@ -0,0 +1,367 @@ + + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ + +//////////////////////////////////////////////////////////////////////////////////// +/** +* An Incident Ticket +*/ +//////////////////////////////////////////////////////////////////////////////////// +class bizIncidentTicket extends cmdbAbstractObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "Incident", + "description" => "Incident ticket", + "key_type" => "autoincrement", + "key_label" => "id", + "name_attcode" => "name", + "state_attcode" => "ticket_status", + "reconc_keys" => array("title"), + "db_table" => "incident", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/ticket.html", + ); + MetaModel::Init_Params($aParams); + //MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeString("name", array("label"=>"TicketID", "description"=>"Refence number ofr this incident", "allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("title", array("label"=>"Title", "description"=>"Overview of the Incident", "allowed_values"=>null, "sql"=>"title", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_AddAttribute(new AttributeEnum("type", array("label"=>"Type", "description"=>"Type of the Incident", "allowed_values"=>new ValueSetEnum("Network,Server,Desktop,Application"), "sql"=>"type", "default_value"=>"Server", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("customer_id", array("targetclass"=>"bizOrganization", "label"=>"Customer", "description"=>"who is impacted by the ticket", "allowed_values"=>null, "sql"=>"customer", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("customer_name", array("label"=>"Customer", "description"=>"Name of the customer impacted by this ticket", "allowed_values"=>null, "extkey_attcode"=> 'customer_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeEnum("ticket_status", array("label"=>"Status", "description"=>"Status of the ticket", "allowed_values"=>new ValueSetEnum("New, Assigned, WorkInProgress, Closed"), "sql"=>"ticket_status", "default_value"=>"New", "is_null_allowed"=>false, "depends_on"=>array()))); + // SetPossibleValues("status",array("Open","Monitored","Closed")); + MetaModel::Init_AddAttribute(new AttributeText("initial_situation", array("label"=>"Initial Situation", "description"=>"Initial situation of the Incident", "allowed_values"=>null, "sql"=>"initial_situation", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeText("current_situation", array("label"=>"Current Situation", "description"=>"Current situation of the Incident", "allowed_values"=>null, "sql"=>"current_situation", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeDate("start_date", array("label"=>"Starting date", "description"=>"Incident starting date", "allowed_values"=>null, "sql"=>"start_date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + // définir une date de défaut à maintenant, alias creation ou modification du ticket + MetaModel::Init_AddAttribute(new AttributeDate("last_update", array("label"=>"Last update", "description"=>"last time the Ticket was modified", "allowed_values"=>null, "sql"=>"last_update", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeDate("next_update", array("label"=>"Next update", "description"=>"next time the Ticket is expected to be modified", "allowed_values"=>null, "sql"=>"next_update", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + + MetaModel::Init_AddAttribute(new AttributeDate("end_date", array("label"=>"Closed Date", "description"=>"Date when the Ticket was closed", "allowed_values"=>null, "sql"=>"closed_date", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("caller_id", array("targetclass"=>"bizPerson", "jointype"=> "", "label"=>"Caller", "description"=>"person that trigger incident", "allowed_values"=>null, "sql"=>"caller_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("caller_mail", array("label"=>"Caller", "description"=>"Person that trigger this incident", "allowed_values"=>null, "extkey_attcode"=> 'caller_id', "target_attcode"=>"email"))); + + MetaModel::Init_AddAttribute(new AttributeString("impact", array("label"=>"Impact", "description"=>"Impact of the Incident", "allowed_values"=>null, "sql"=>"impact", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("workgroup_id", array("targetclass"=>"bizWorkgroup", "jointype"=> "", "label"=>"Workgroup", "description"=>"which workgroup is owning ticket", "allowed_values"=>null, "sql"=>"workgroup_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("workgroup_name", array("label"=>"Managed by Workgroup", "description"=>"name of workgroup managing the Ticket", "allowed_values"=>null, "extkey_attcode"=> 'workgroup_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("agent_id", array("targetclass"=>"bizPerson", "jointype"=> "", "label"=>"Agent", "description"=>"who is managing the ticket", "allowed_values"=>null, "sql"=>"agent_id", "is_null_allowed"=>true, "depends_on"=>array("workgroup_id")))); + MetaModel::Init_AddAttribute(new AttributeExternalField("agent_mail", array("label"=>"Managed by Agent", "description"=>"mail of agent managing the Ticket", "allowed_values"=>null, "extkey_attcode"=> 'agent_id', "target_attcode"=>"email"))); + // Comment afficher le first + last name de l'agent ? Est-ce utile d'ajouter ce champ? + MetaModel::Init_AddAttribute(new AttributeText("action_log", array("label"=>"Action Logs", "description"=>"List all action performed during the incident", "allowed_values"=>null, "sql"=>"action_log", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("severity", array("label"=>"Severity", "description"=>"Field defining the criticity if the incident", "allowed_values"=>new ValueSetEnum("critical,medium,low"), "sql"=>"criticity", "default_value"=>"low", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeInteger("assignment_count", array("label"=>"Assignment Count", "description"=>"Number of times this ticket was assigned or reassigned", "allowed_values"=>null, "sql"=>"assignment_count", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeText("resolution", array("label"=>"Resolution", "description"=>"Description of the resolution", "allowed_values"=>null, "sql"=>"resolution", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + + MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("impacted_infra_manual", array("label"=>"Impacted Infrastructure", "description"=>"CIs that are not meeting the SLA", "linked_class"=>"lnkInfraTicket", "ext_key_to_me"=>"ticket_id", "ext_key_to_remote"=>"infra_id", "allowed_values"=>null, "count_min"=>1, "count_max"=>0, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("related_tickets", array("label"=>"Related Tickets", "description"=>"Other incident tickets related to this one", "linked_class"=>"lnkRelatedTicket", "ext_key_to_me"=>"ticket_id", "ext_key_to_remote"=>"rel_ticket_id", "allowed_values"=>null, "count_min"=>0, "count_max"=>0, "depends_on"=>array(/*'impacted_infra_computed',*/ 'impacted_infra_manual')))); + + + //MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("name"); + MetaModel::Init_AddFilterFromAttribute("title"); + MetaModel::Init_AddFilterFromAttribute("type"); + MetaModel::Init_AddFilterFromAttribute("customer_id"); + MetaModel::Init_AddFilterFromAttribute("caller_id"); + MetaModel::Init_AddFilterFromAttribute("ticket_status"); + MetaModel::Init_AddFilterFromAttribute("start_date"); + MetaModel::Init_AddFilterFromAttribute("last_update"); + MetaModel::Init_AddFilterFromAttribute("end_date"); + MetaModel::Init_AddFilterFromAttribute("workgroup_id"); + MetaModel::Init_AddFilterFromAttribute("agent_id"); + MetaModel::Init_AddFilterFromAttribute("severity"); + MetaModel::Init_AddFilterFromAttribute("assignment_count"); + + // doit-on aussi ajouter un filtre sur les extfields lié à une extkey ? ici le name de l'agent? + + // Display lists + MetaModel::Init_SetZListItems('details', array('name','title', 'customer_id', 'type','ticket_status', 'severity','start_date', 'initial_situation', 'current_situation','caller_id', 'impact', 'last_update', 'next_update','end_date', 'assignment_count', 'workgroup_id','agent_id','action_log','resolution')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('list', array('name', 'title', 'customer_id', 'type','ticket_status','severity','start_date', 'initial_situation')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('name', 'title', 'customer_id', 'caller_id','type', 'ticket_status', 'severity','start_date', 'last_update','end_date','agent_id')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('name', 'title', 'customer_id','caller_id','type','ticket_status', 'severity','start_date', 'last_update', 'end_date','agent_id')); // Criteria of the advanced search form + + // State machine + MetaModel::Init_DefineState("New", array("label"=>"New (Unassigned)", "description"=>"Newly created ticket", "attribute_inherit"=>null, + "attribute_list"=>array('name' => OPT_ATT_READONLY, 'assignment_count' => OPT_ATT_HIDDEN, 'end_date' => OPT_ATT_HIDDEN, 'next_update' => OPT_ATT_HIDDEN, 'last_update' => OPT_ATT_HIDDEN, + "title"=>OPT_ATT_MANDATORY, "customer_id"=>OPT_ATT_MANDATORY, "caller_id"=>OPT_ATT_MANDATORY, "initial_situation"=>OPT_ATT_MANDATORY, "start_date"=>OPT_ATT_MANDATORY, "workgroup_id"=>OPT_ATT_MANDATORY, + "severity"=>OPT_ATT_MANDATORY, "agent_id"=>OPT_ATT_HIDDEN,"impacted_infra_manual"=>OPT_ATT_MANDATORY, "related_tickets"=>OPT_ATT_MUSTPROMPT))); + MetaModel::Init_DefineState("Assigned", array("label"=>"Assigned", "description"=>"Ticket is assigned to somebody", "attribute_inherit"=>null, + "attribute_list"=>array('name' => OPT_ATT_READONLY, "title"=>OPT_ATT_READONLY, "customer_id"=>OPT_ATT_READONLY, "caller_id"=>OPT_ATT_READONLY, "initial_situation"=>OPT_ATT_READONLY, "start_date"=>OPT_ATT_READONLY,'assignment_count' => OPT_ATT_READONLY,'end_date' => OPT_ATT_HIDDEN, "workgroup_id"=>OPT_ATT_MUSTCHANGE, "agent_id"=>OPT_ATT_MUSTCHANGE))); + MetaModel::Init_DefineState("WorkInProgress", array("label"=>"Work In Progress", "description"=>"Work is in progress", "attribute_inherit"=>null, "attribute_list"=>array("title"=>OPT_ATT_READONLY, "customer_id"=>OPT_ATT_READONLY, "caller_id"=>OPT_ATT_READONLY, "initial_situation"=>OPT_ATT_READONLY,'end_date' => OPT_ATT_HIDDEN, "start_date"=>OPT_ATT_READONLY,"workgroup_id"=>OPT_ATT_MANDATORY, "agent_id"=>OPT_ATT_MANDATORY))); + MetaModel::Init_DefineState("Closed", array("label"=>"Closed", "description"=>"Ticket is closed", "attribute_inherit"=>null, "attribute_list"=>array("workgroup_id"=>OPT_ATT_MANDATORY, "agent_id"=>OPT_ATT_MANDATORY, "resolution"=>OPT_ATT_MANDATORY, "end_date"=>OPT_ATT_MANDATORY))); + + MetaModel::Init_DefineStimulus("ev_assign", new StimulusUserAction(array("label"=>"Assign this ticket", "description"=>"Assign this ticket to a group and an agent"))); + MetaModel::Init_DefineStimulus("ev_reassign", new StimulusUserAction(array("label"=>"Reassign this ticket", "description"=>"Reassign this ticket to a different group and agent"))); + MetaModel::Init_DefineStimulus("ev_start_working", new StimulusUserAction(array("label"=>"Work on this ticket", "description"=>"Start working on this ticket"))); + MetaModel::Init_DefineStimulus("ev_close", new StimulusUserAction(array("label"=>"Close this ticket", "description"=>"Close/resolve this ticket"))); + + MetaModel::Init_DefineTransition("New", "ev_assign", array("target_state"=>"Assigned", "actions"=>array('IncrementAssignmentCount'), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Assigned", "ev_reassign", array("target_state"=>"Assigned", "actions"=>array('IncrementAssignmentCount'), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Assigned", "ev_start_working", array("target_state"=>"WorkInProgress", "actions"=>array(), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("WorkInProgress", "ev_reassign", array("target_state"=>"Assigned", "actions"=>array('IncrementAssignmentCount'), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("WorkInProgress", "ev_close", array("target_state"=>"Closed", "actions"=>array('SetClosureDate'), "user_restriction"=>null)); + + } + + public function Generate(cmdbDataGenerator $oGenerator) + { + $this->Set('customer_id', $oGenerator->GetOrganizationId()); + $this->Set('title', $oGenerator->GenerateString("enum(Site,Server,Line)| |enum(is down,is flip-flopping,is not responding)")); + $this->Set('agent_id', $oGenerator->GenerateKey("bizPerson", array('org_id' =>$oGenerator->GetOrganizationId() ))); + $this->Set('ticket_status', $oGenerator->GenerateString("enum(Open,Closed,Closed,Monitored)")); + $this->Set('start_date', $oGenerator->GenerateString("2007-|number(07-12)|-|number(01-30)| |number(07-12)|:|number(00-59)|:|number(00-59)")); + $this->Set('last_update', $oGenerator->GenerateString("2007-|number(07-12)|-|number(01-30)| |number(07-12)|:|number(00-59)|:|number(00-59)")); + $this->Set('end_date', $oGenerator->GenerateString("2007-|number(07-12)|-|number(01-30)| |number(07-12)|:|number(00-59)|:|number(00-59)")); + } + + public static function GetUIPage() + { + return './UI.php'; + } + + // State machine actions + public function IncrementAssignmentCount($sStimulusCode) + { + $this->Set('assignment_count', $this->Get('assignment_count') + 1); + return true; + } + + public function SetClosureDate($sStimulusCode) + { + $this->Set('end_date', time()); + return true; + } + + public function ComputeFields() + { + if ($this->GetKey() > 0) + { + $sName = sprintf('I-%06d', $this->GetKey()); + } + else + { + $sName = "Id not set"; + } + $this->Set('name', $sName); + } +} + + +//////////////////////////////////////////////////////////////////////////////////// +/** +* n-n link between any Infra and a Incident +*/ +//////////////////////////////////////////////////////////////////////////////////// +class lnkRelatedTicket extends cmdbAbstractObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "Related Ticket", + "description" => "Ticket related to a ticket", + "key_type" => "autoincrement", + "key_label" => "link_id", + "name_attcode" => "impact", // ???? + "state_attcode" => "", + "reconc_keys" => array("impact"), // ???? + "db_table" => "related_ticket", + "db_key_field" => "link_id", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_AddAttribute(new AttributeExternalKey("rel_ticket_id", array("targetclass"=>"bizIncidentTicket", "jointype"=> '', "label"=>"Related Ticket id", "description"=>"The related ticket", "allowed_values"=>null, "sql"=>"rel_ticket_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("rel_ticket_name", array("label"=>"Related ticket", "description"=>"Name of the related ticket", "allowed_values"=>null, "extkey_attcode"=> 'rel_ticket_id', "target_attcode"=>"title"))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("ticket_id", array("targetclass"=>"bizIncidentTicket", "jointype"=> '', "label"=>"Ticket #", "description"=>"Ticket number", "allowed_values"=>null, "sql"=>"ticket_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("ticket_name", array("label"=>"Ticket name", "description"=>"Name of the ticket", "allowed_values"=>null, "extkey_attcode"=> 'ticket_id', "target_attcode"=>"title"))); + MetaModel::Init_AddAttribute(new AttributeString("impact", array("label"=>"Impact", "description"=>"Impact on the related ticket", "allowed_values"=>null, "sql"=>"impact", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + + MetaModel::Init_AddFilterFromAttribute("rel_ticket_id"); + MetaModel::Init_AddFilterFromAttribute("ticket_id"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('rel_ticket_id', 'ticket_id', 'impact')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('list', array('rel_ticket_id', 'ticket_id', 'impact')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('rel_ticket_id', 'ticket_id')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('rel_ticket_id', 'ticket_id')); // Criteria of the advanced search form + } + + public function Generate(cmdbDataGenerator $oGenerator) + { + $this->Set('infra_id', $oGenerator->GenerateKey("logInfra", array('org_id' =>$oGenerator->GetOrganizationId() ))); + $this->Set('ticket_id', $oGenerator->GenerateKey("bizIncidentTicket", array('org_id' =>$oGenerator->GetOrganizationId() ))); + $this->Set('impact', $oGenerator->GenerateString("enum(none,mandatory,partial)")); + } + +} + +//////////////////////////////////////////////////////////////////////////////////// +/** +* n-n link between any Infra and a Incident +*/ +//////////////////////////////////////////////////////////////////////////////////// +class lnkInfraTicket extends cmdbAbstractObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "Infra Ticket", + "description" => "Infra impacted by a ticket", + "key_type" => "autoincrement", + "key_label" => "link_id", + "name_attcode" => "impact", // ???? + "state_attcode" => "", + "reconc_keys" => array("impact"), // ???? + "db_table" => "infra_ticket", + "db_key_field" => "link_id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/default.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_AddAttribute(new AttributeExternalKey("infra_id", array("targetclass"=>"logInfra", "jointype"=> '', "label"=>"Infrastructure", "description"=>"The infrastructure impacted", "allowed_values"=>null, "sql"=>"infra_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("infra_name", array("label"=>"Infrastructure name", "description"=>"Name of the impacted infrastructure", "allowed_values"=>null, "extkey_attcode"=> 'infra_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("ticket_id", array("targetclass"=>"bizIncidentTicket", "jointype"=> '', "label"=>"Ticket #", "description"=>"Ticket number", "allowed_values"=>null, "sql"=>"ticket_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("ticket_name", array("label"=>"Ticket name", "description"=>"Name of the ticket", "allowed_values"=>null, "extkey_attcode"=> 'ticket_id', "target_attcode"=>"title"))); + MetaModel::Init_AddAttribute(new AttributeString("impact", array("label"=>"Impact", "description"=>"Level of impact of the infra by the related ticket", "allowed_values"=>null, "sql"=>"impact", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + + MetaModel::Init_AddFilterFromAttribute("infra_id"); + MetaModel::Init_AddFilterFromAttribute("ticket_id"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('infra_id', 'ticket_id', 'impact')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('list', array('infra_id', 'ticket_id', 'impact')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('infra_id', 'ticket_id')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('infra_id', 'ticket_id')); // Criteria of the advanced search form + } + + public function Generate(cmdbDataGenerator $oGenerator) + { + $this->Set('infra_id', $oGenerator->GenerateKey("logInfra", array('org_id' =>$oGenerator->GetOrganizationId() ))); + $this->Set('ticket_id', $oGenerator->GenerateKey("bizIncidentTicket", array('org_id' =>$oGenerator->GetOrganizationId() ))); + $this->Set('impact', $oGenerator->GenerateString("enum(none,mandatory,partial)")); + } + +} + +//////////////////////////////////////////////////////////////////////////////////// +/** +* n-n link between any Contqct and a Incident +*/ +//////////////////////////////////////////////////////////////////////////////////// +class lnkContactTicket extends cmdbAbstractObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "Contact Ticket", + "description" => "Contacts to be notify for a ticket", + "key_type" => "autoincrement", + "key_label" => "link_id", + "name_attcode" => "role", // ???? + "state_attcode" => "", + "reconc_keys" => array("role"), // ???? + "db_table" => "contact_ticket", + "db_key_field" => "link_id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/default.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_AddAttribute(new AttributeExternalKey("contact_id", array("targetclass"=>"bizContact", "jointype"=> '', "label"=>"Contact", "description"=>"Contact to Notify", "allowed_values"=>null, "sql"=>"contact_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("contact_email", array("label"=>"Contact email", "description"=>"Mail for the contact", "allowed_values"=>null, "extkey_attcode"=> 'contact_id', "target_attcode"=>"email"))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("ticket_id", array("targetclass"=>"bizIncidentTicket", "jointype"=> '', "label"=>"Ticket #", "description"=>"Ticket number", "allowed_values"=>null, "sql"=>"ticket_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("ticket_name", array("label"=>"Ticket name", "description"=>"Name of the ticket", "allowed_values"=>null, "extkey_attcode"=> 'ticket_id', "target_attcode"=>"title"))); + MetaModel::Init_AddAttribute(new AttributeString("role", array("label"=>"Role", "description"=>"Role of the contact", "allowed_values"=>null, "sql"=>"role", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_AddFilterFromAttribute("contact_id"); + MetaModel::Init_AddFilterFromAttribute("ticket_id"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('contact_id', 'ticket_id', 'role')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('list', array('contact_id', 'ticket_id', 'role')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('contact_id', 'ticket_id')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('contact_id', 'ticket_id')); // Criteria of the advanced search form + } + + public function Generate(cmdbDataGenerator $oGenerator) + { + $this->Set('infra_id', $oGenerator->GenerateKey("logInfra", array('org_id' =>$oGenerator->GetOrganizationId() ))); + $this->Set('ticket_id', $oGenerator->GenerateKey("bizIncidentTicket", array('org_id' =>$oGenerator->GetOrganizationId() ))); + $this->Set('impact', $oGenerator->GenerateString("enum(none,mandatory,partial)")); + } + +} +//////////////////////////////////////////////////////////////////////////////////// +//** +//* A workgroup is a queue in a given call tracking system +//* It belongs to a team and a given organization +//////////////////////////////////////////////////////////////////////////////////// +class bizWorkgroup extends logRealObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "Workgroup", + "description" => "Call tracking workgroup", + "key_type" => "", + "key_label" => "id", + "name_attcode" => "name", + "state_attcode" => "", + "reconc_keys" => array("org_name", "name"), // inherited attributes + "db_table" => "workgroups", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/default.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeExternalField("org_name", array("label"=>"Organization", "description"=>"Company / Department owning this object", "allowed_values"=>null, "extkey_attcode"=> 'org_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("team_id", array("targetclass"=>"bizTeam", "label"=>"Team", "description"=>"Team owning the workgroup", "allowed_values"=>null, "sql"=>"team_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("team_name", array("label"=>"Team name", "description"=>"name of the team", "allowed_values"=>null, "extkey_attcode"=> 'team_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeString("role", array("label"=>"Role", "description"=>"Role of this work group", "allowed_values"=>new ValueSetEnum("1st level support,2nd level support,3rd level support"), "sql"=>"role", "default_value"=>"1st level support", "is_null_allowed"=>true, "depends_on"=>array()))); + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("org_name"); + MetaModel::Init_AddFilterFromAttribute("team_id"); + MetaModel::Init_AddFilterFromAttribute("role"); + + + // Display lists + MetaModel::Init_SetZListItems('details', array('name', 'status', 'org_id', 'team_id', 'role')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('name', 'team_id','role')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('name', 'status', 'team_id')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('name', 'status', 'team_id','role')); // Criteria of the advanced search form + + } +} + + + +?> diff --git a/business/itop.business.class.inc.php b/business/itop.business.class.inc.php new file mode 100644 index 000000000..31e561e92 --- /dev/null +++ b/business/itop.business.class.inc.php @@ -0,0 +1,1629 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ + +/** + * Possible values for the statuses of objects + */ +$oAllowedStatuses = new ValueSetEnum('production,implementation,obsolete'); + +/** + * Relation graphs + */ +MetaModel::RegisterRelation("impacts", array("description"=>"objects being functionaly impacted", "verb_down"=>"impacts", "verb_up"=>"is impacted by")); + +//////////////////////////////////////////////////////////////////////////////////// +/** +* An organization that owns some objects +* +* An organization "owns" some persons (its employees) but also some other objects +* (its assets) like buildings, computers, furniture... +* the services that they provides, the contracts/OLA they have signed as customer +* +* Organization ownership might be used to manage the R/W access to the object +*/ +//////////////////////////////////////////////////////////////////////////////////// +/** + * itop.business.class.inc.php + * User defined objects, implements the business need + * + * @package iTopBizModelSamples + * @author Erwan Taloc + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ +class bizOrganization extends cmdbAbstractObject +{ + public static function Init() + { + global $oAllowedStatuses; + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "Organization", + "description" => "Organizational structure: can be Company and/or Department", + "key_type" => "autoincrement", + "key_label" => "id", + "name_attcode" => "name", + "state_attcode" => "", + "reconc_keys" => array("name"), + "db_table" => "organizations", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/default.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_AddAttribute(new AttributeString("name", array("label"=>"Name", "description"=>"Common name", "allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array() ))); + MetaModel::Init_AddAttribute(new AttributeString("code", array("label"=>"Code", "description"=>"Organization code (Siret, DUNS,...)", "allowed_values"=>null, "sql"=>"code", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array() ))); + MetaModel::Init_AddAttribute(new AttributeEnum("status", array("label"=>"Status", "description"=>"Lifecycle status", "allowed_values"=>$oAllowedStatuses, "sql"=>"status", "default_value"=>"implementation", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("parent_id", array("targetclass"=>"bizOrganization", "label"=>"Parent Id", "description"=>"Parent organization", "allowed_values"=>null, "sql"=>"parent_id", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("parent_name", array("label"=>"Parent Name", "description"=>"Name of the parent organization", "allowed_values"=>null, "extkey_attcode"=> 'parent_id', "target_attcode"=>"name"))); + + MetaModel::Init_AddFilterFromAttribute("name"); + MetaModel::Init_AddFilterFromAttribute("code"); + MetaModel::Init_AddFilterFromAttribute("status"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('name', 'code', 'status', 'parent_id')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('name', 'status', 'parent_id')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('name', 'code', 'status')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('name', 'code', 'status')); // Criteria of the advanced search form + } + + public function Generate(cmdbDataGenerator $oGenerator) + { + //$this->SetKey($oGenerator->GetOrganizationCode()); + $this->Set('name', $oGenerator->GetOrganizationName()); + $this->Set('code', $oGenerator->GetOrganizationCode()); + $this->Set('status', 'implementation'); + $this->Set('parent_id', 1); + + } +} + +//////////////////////////////////////////////////////////////////////////////////// +/** +* Class of objects owned by some organization +* +* This is the root class of all the objects that can be "owned" by an organization +* +* A Real Object +* can be supported by Contacts, having a specific role (same contact with multiple roles?) +* can be documented by Documents +*/ +//////////////////////////////////////////////////////////////////////////////////// +/** + * itop.business.class.inc.php + * User defined objects, implements the business need + * + * @package iTopBizModelSamples + * @author Erwan Taloc + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ +class logRealObject extends cmdbAbstractObject +{ + public static function Init() + { + global $oAllowedStatuses; + + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "Object", + "description" => "Any CMDB object", + "key_type" => "autoincrement", + "key_label" => "id", + "name_attcode" => "name", + "state_attcode" => "", + "reconc_keys" => array("name"), + "db_table" => "objects", + "db_key_field" => "id", + "db_finalclass_field" => "obj_class", + "display_template" => "../business/templates/default.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_AddAttribute(new AttributeString("name", array("label"=>"Name", "description"=>"Common name", "allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("status", array("label"=>"Status", "description"=>"Lifecycle status", "allowed_values"=>$oAllowedStatuses, "sql"=>"status", "default_value"=>"implementation", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("org_id", array("targetclass"=>"bizOrganization", "label"=>"Organization Id", "description"=>"ID of the object owner organization", "allowed_values"=>new ValueSetObjects("bizOrganization: status Contains 'implementation'", 'name', array('name'=>true)), "sql"=>"org_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("org_name", array("label"=>"Organization", "description"=>"Company / Department owning this object", "allowed_values"=>null, "extkey_attcode"=> 'org_id', "target_attcode"=>"name"))); + + MetaModel::Init_AddFilterFromAttribute("name"); + MetaModel::Init_AddFilterFromAttribute("status"); + MetaModel::Init_AddFilterFromAttribute("org_id"); + MetaModel::Init_AddFilterFromAttribute("org_name"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('name', 'status', 'org_id')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('finalclass', 'name', 'status', 'org_id')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('name', 'status')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('name', 'status', 'org_id')); // Criteria of the advanced search form + } + + public function Generate(cmdbDataGenerator $oGenerator) + { + $this->Set('org_id', $oGenerator->GetOrganizationId()); + $this->Set('name', ""); + $this->Set('status', $oGenerator->GenerateString("enum(implementation,production)")); + } +} + +//////////////////////////////////////////////////////////////////////////////////// +/** +* Any kind of thing that can be contacted (person, team, hotline...) +* A contact can: +* be linked to any Real Object with a role +* be part of a GroupContact +*/ +//////////////////////////////////////////////////////////////////////////////////// +class bizContact extends logRealObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "Contact", + "description" => "Contact", + "key_type" => "", + "key_label" => "id", + "name_attcode" => "name", + "state_attcode" => "", + "reconc_keys" => array("org_name", "name"), // inherited attributes + "db_table" => "contacts", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/default.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeExternalField("org_name", array("label"=>"Organization", "description"=>"Company / Department of the contact", "allowed_values"=>null, "extkey_attcode"=> 'org_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeString("email", array("label"=>"eMail", "description"=>"Email address", "allowed_values"=>null, "sql"=>"email", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("phone", array("label"=>"Phone", "description"=>"Telephone", "allowed_values"=>null, "sql"=>"telephone", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("location_id", array("targetclass"=>"bizLocation", "label"=>"Location Id", "description"=>"Id of the location where the contact is located", "allowed_values"=>null, "sql"=>"location_id", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("location_name", array("label"=>"Location Name", "description"=>"Name of the location where the contact is located", "allowed_values"=>null, "extkey_attcode"=> 'location_id', "target_attcode"=>"name"))); + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("org_name"); + MetaModel::Init_AddFilterFromAttribute("email"); + MetaModel::Init_AddFilterFromAttribute("phone"); + MetaModel::Init_AddFilterFromAttribute("location_id"); + MetaModel::Init_AddFilterFromAttribute("location_name"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('name', 'status', 'org_id', 'email', 'location_id', 'phone')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('finalclass', 'name', 'status', 'org_id', 'email', 'location_id', 'phone')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('name', 'status', 'email', 'location_id', 'phone')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('name', 'status', 'org_id')); // Criteria of the advanced search form + } + + public function Generate(cmdbDataGenerator $oGenerator) + { + $this->Set('org_id', $oGenerator->GetOrganizationId()); + $this->Set('name', ""); + $this->Set('email', ""); + $this->Set('phone', $oGenerator->GenerateString("enum(+1,+33,+44,+49,+421)| |number(100-999)| |number(000-999)")); + $this->Set('location_id', $oGenerator->GenerateKey("bizLocation", array('org_id' =>$oGenerator->GetOrganizationId() ))); + } +} + +//////////////////////////////////////////////////////////////////////////////////// +/** +* Physical person only +*/ +//////////////////////////////////////////////////////////////////////////////////// +class bizPerson extends bizContact +{ + public static function Init() + { + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "Person", + "description" => "Person", + "key_type" => "", + "key_label" => "id", + "name_attcode" => "name", + "state_attcode" => "", + "reconc_keys" => array("org_name", "first_name", "name"), // comment en définir plusieurs + // "reconc_keys" => array("org_name", "employe_number"), + "db_table" => "persons", // Can it use the same physical DB table as any contact ? + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/person.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeString("first_name", array("label"=>"first Name", "description"=>"First name", "allowed_values"=>null, "sql"=>"first_name", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("employe_number", array("label"=>"Employe Number", "description"=>"employe number", "allowed_values"=>null, "sql"=>"employe_number", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("first_name"); + MetaModel::Init_AddFilterFromAttribute("employe_number"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('first_name', 'name', 'status', 'org_id', 'email', 'location_id', 'phone', 'employe_number')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('first_name', 'name', 'status', 'org_id', 'email', 'location_id', 'phone')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('first_name', 'name', 'status', 'email', 'location_id', 'phone', 'employe_number')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('first_name', 'name', 'status', 'email', 'location_id', 'phone', 'employe_number')); // Criteria of the advanced search form + } + + public function Generate(cmdbDataGenerator $oGenerator) + { + parent::Generate($oGenerator); + $this->Set('name', $oGenerator->GenerateLastName()); + $this->Set('first_name', $oGenerator->GenerateFirstName()); + $this->Set('email', $oGenerator->GenerateEmail($this->Get('first_name'), $this->Get('name'))); + $this->Set('phone', $oGenerator->GenerateString("enum(+1,+33,+44,+49,+421)| |number(100-999)| |number(000-999)")); + } +} + +//////////////////////////////////////////////////////////////////////////////////// +/** +* A team is basically a contact which is also a group of contacts +* (and thus a team can contain other teams) +*/ +//////////////////////////////////////////////////////////////////////////////////// +class bizTeam extends bizContact +{ + public static function Init() + { + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "Team", + "description" => "A group of contacts", + "key_type" => "", + "key_label" => "id", + "name_attcode" => "name", + "state_attcode" => "", + "reconc_keys" => array("org_name", "name"), // inherited attributes + "db_table" => "teams", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/team.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + + MetaModel::Init_InheritFilters(); + + // Display lists + MetaModel::Init_SetZListItems('details', array('name', 'status', 'org_id', 'email', 'location_id', 'phone')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('name', 'status', 'org_id', 'email', 'location_id', 'phone')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('name', 'status', 'email', 'location_id', 'phone')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('name', 'status', 'org_id')); // Criteria of the advanced search form + } +} + + +//////////////////////////////////////////////////////////////////////////////////// +/** +* An electronic document, with version tracking +*/ +//////////////////////////////////////////////////////////////////////////////////// +class bizDocument extends logRealObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "Document", + "description" => "Document", + "key_type" => "", + "key_label" => "id", + "name_attcode" => "name", + "state_attcode" => "", + "reconc_keys" => array("org_name", "name"), // inherited attributes + "db_table" => "documents", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/document.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeExternalField("org_name", array("label"=>"Organization", "description"=>"Company / Department owning the document", "allowed_values"=>null, "extkey_attcode"=> 'org_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeEnum("scope", array("label"=>"scope", "description"=>"Scope of this document", "allowed_values"=>new ValueSetEnum("organization,hardware support"), "sql"=>"scope", "default_value"=>"organization", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("description", array("label"=>"Description", "description"=>"Service Description", "allowed_values"=>null, "sql"=>"description", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("scope"); + MetaModel::Init_AddFilterFromAttribute("description"); + + MetaModel::Init_SetZListItems('details', array('name', 'status', 'org_id', 'scope','description')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('name', 'status', 'org_id', 'scope')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('name', 'status', 'scope')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('name', 'status', 'scope')); // Criteria of the advanced search form + + } + +} + +//////////////////////////////////////////////////////////////////////////////////// +/** +* A version of an electronic document +*/ +//////////////////////////////////////////////////////////////////////////////////// +class bizDocVersion extends cmdbAbstractObject +{ + public static function Init() + { + global $oAllowedStatuses; + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "DocumentVersion", + "description" => "A version of a document", + "key_type" => "autoincrement", + "key_label" => "id", + "name_attcode" => "version_number", + "state_attcode" => "", + "reconc_keys" => array("docname", "version_number"), + "db_table" => "document_versions", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/document.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_AddAttribute(new AttributeExternalKey("document", array("targetclass"=>"bizDocument", "label"=>"document", "description"=>"The main document", "allowed_values"=>null, "sql"=>"document_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("docname", array("label"=>"document name", "description"=>"name of the document", "allowed_values"=>null, "extkey_attcode"=> 'document', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeString("version_number", array("label"=>"version number", "description"=>"Service name", "allowed_values"=>null, "sql"=>"version_number", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("status", array("label"=>"status", "description"=>"Status", "allowed_values"=>$oAllowedStatuses, "sql"=>"status", "default_value"=>"implementation", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("type", array("label"=>"type", "description"=>"Type", "allowed_values"=>new ValueSetEnum("local,draft"), "sql"=>"type", "default_value"=>"local", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeURL("url", array("label"=>"URL", "description"=>"Hyperlink to the version", "allowed_values"=>null, "target"=>"_blank", "sql"=>"url", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("description", array("label"=>"Description", "description"=>"Service Description", "allowed_values"=>null, "sql"=>"description", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_AddFilterFromAttribute("document"); + MetaModel::Init_AddFilterFromAttribute("docname"); + MetaModel::Init_AddFilterFromAttribute("version_number"); + MetaModel::Init_AddFilterFromAttribute("status"); + MetaModel::Init_AddFilterFromAttribute("type"); + MetaModel::Init_AddFilterFromAttribute("description"); + + MetaModel::Init_SetZListItems('details', array('docname', 'status', 'version_number', 'type','url','description')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('version_number', 'status', 'type', 'url')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('docname', 'type')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('docname', 'type')); // Criteria o + } +} + +//////////////////////////////////////////////////////////////////////////////////// +/** +* n-n link between any Object and a Document +*/ +//////////////////////////////////////////////////////////////////////////////////// +class lnkDocumentRealObject extends cmdbAbstractObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "DocumentsLinks", + "description" => "A link between a document and another object", + "key_type" => "autoincrement", + "key_label" => "link_id", + "name_attcode" => "link_type", + "state_attcode" => "", + "reconc_keys" => array("doc_name", "object_name"), + "db_table" => "documents_links", + "db_key_field" => "link_id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/default.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_AddAttribute(new AttributeExternalKey("doc_id", array("targetclass"=>"bizDocument", "label"=>"Document Name", "description"=>"id of the Document", "allowed_values"=>null, "sql"=>"doc_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("doc_name", array("label"=>"Document", "description"=>"name of the document", "allowed_values"=>null, "extkey_attcode"=> 'doc_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("object_id", array("targetclass"=>"logRealObject", "label"=>"object", "description"=>"Object linked", "allowed_values"=>null, "sql"=>"object_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("object_name", array("label"=>"object name", "description"=>"name of the linked object", "allowed_values"=>null, "extkey_attcode"=> 'object_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeString("link_type", array("label"=>"link_type", "description"=>"Type of the link", "allowed_values"=>null, "sql"=>"link_type", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + + MetaModel::Init_AddFilterFromAttribute("doc_id"); + MetaModel::Init_AddFilterFromAttribute("doc_name"); + MetaModel::Init_AddFilterFromAttribute("object_id"); + MetaModel::Init_AddFilterFromAttribute("object_name"); + MetaModel::Init_AddFilterFromAttribute("link_type"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('doc_id', 'object_name', 'link_type')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('doc_id', 'object_name', 'link_type')); // Attributes to be displayed for a list + } +} + + + + +//////////////////////////////////////////////////////////////////////////////////// +/** +* n-n link between any Object and a contact +*/ +//////////////////////////////////////////////////////////////////////////////////// +class lnkContactRealObject extends cmdbAbstractObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "ContactsLinks", + "description" => "A link between a contact and another object", + "key_type" => "autoincrement", + "key_label" => "link_id", + "name_attcode" => "role", + "state_attcode" => "", + "reconc_keys" => array("contact_name", "object_name"), + "db_table" => "contacts_links", + "db_key_field" => "link_id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/default.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_AddAttribute(new AttributeExternalKey("contact_id", array("targetclass"=>"bizContact", "label"=>"Contact", "description"=>"The contact", "allowed_values"=>null, "sql"=>"contact_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("contact_name", array("label"=>"Contact name", "description"=>"name of the contact", "allowed_values"=>null, "extkey_attcode"=> 'contact_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeExternalField("contact_phone", array("label"=>"Phone", "description"=>"Phone number of the contact", "allowed_values"=>null, "extkey_attcode"=> 'contact_id', "target_attcode"=>"phone"))); + MetaModel::Init_AddAttribute(new AttributeExternalField("contact_email", array("label"=>"eMail", "description"=>"eMail address of the contact", "allowed_values"=>null, "extkey_attcode"=> 'contact_id', "target_attcode"=>"email"))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("object_id", array("targetclass"=>"logRealObject", "label"=>"object", "description"=>"Object linked", "allowed_values"=>null, "sql"=>"object_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("object_name", array("label"=>"Object name", "description"=>"name of the linked object", "allowed_values"=>null, "extkey_attcode"=> 'object_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeString("role", array("label"=>"Role", "description"=>"Role of the contact", "allowed_values"=>null, "sql"=>"role", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_AddFilterFromAttribute("contact_id"); + MetaModel::Init_AddFilterFromAttribute("contact_name"); + MetaModel::Init_AddFilterFromAttribute("object_id"); + MetaModel::Init_AddFilterFromAttribute("object_name"); + MetaModel::Init_AddFilterFromAttribute("role"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('contact_id', 'contact_phone', 'contact_email', 'object_id', 'role')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('contact_id', 'contact_phone', 'contact_email', 'object_id', 'role')); // Attributes to be displayed for a list + } +} + +//////////////////////////////////////////////////////////////////////////////////// +/** +* Any Infrastructure object (bizLocation, bizDevice, bizApplication, bizCircuit, bizInterface) +* An infrastructure object: +* can be covered by an OLA +* can support the delivery of a Service +* can be part of an GroupInfra +*/ +//////////////////////////////////////////////////////////////////////////////////// +class logInfra extends logRealObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "Infra", + "description" => "Infrastructure real object", + "key_type" => "", + "key_label" => "id", + "name_attcode" => "name", + "state_attcode" => "", + "reconc_keys" => array("org_name", "name"), // inherited attributes + "db_table" => "infra", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/default.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeEnum("severity", array("label"=>"Severity", "description"=>"Severity for this infrastructure", "allowed_values"=>new ValueSetEnum("high,medium,low"), "sql"=>"severity", "default_value"=>"low", "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("severity"); + } +} + +//////////////////////////////////////////////////////////////////////////////////// +/** +* bizLocation (Region, Country, City, Site, Building, Floor, Room, Rack,...) +* pourrait être mis en plusieurs sous objects, puisqu'une adresse sur region n'a pas trop de sens +* +*/ +//////////////////////////////////////////////////////////////////////////////////// +class bizLocation extends logInfra +{ + public static function Init() + { + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "Location", + "description" => "Any type of location: Region, Country, City, Site, Building, Floor, Room, Rack,...", + "key_type" => "", + "key_label" => "id", + "name_attcode" => "name", + "state_attcode" => "", + "reconc_keys" => array("org_name", "name"), // inherited attributes + "db_table" => "location", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/location.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeText("address", array("label"=>"Address", "description"=>"The postal address of the location", "allowed_values"=>null, "sql"=>"address", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("country", array("label"=>"Country", "description"=>"Country of the location", "allowed_values"=>null, "sql"=>"country", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("parent_location_id", array("targetclass"=>"bizLocation", "label"=>"Parent Location", "description"=>"where is the real object physically located", "allowed_values"=>null, "sql"=>"parent_location_id", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("parent_location_name", array("label"=>"Parent location (Name)", "description"=>"name of the parent location", "allowed_values"=>null, "extkey_attcode"=> 'parent_location_id', "target_attcode"=>"name"))); + + // on veut pouvoir rechercher une location qui soit un descendant (pas obligatoirement direct) d'une Location, on fait comment ? + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("country"); + MetaModel::Init_AddFilterFromAttribute("address"); + MetaModel::Init_AddFilterFromAttribute("parent_location_id"); + MetaModel::Init_AddFilterFromAttribute("parent_location_name"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('name', 'status', 'org_id', 'address', 'country', 'parent_location_id')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('name', 'status', 'org_id', 'country')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('name', 'status', 'country', 'parent_location_name')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('name', 'status', 'address', 'country', 'parent_location_id', 'org_id')); // Criteria of the advanced search form + } + + public function ComputeValues() + { + /* + $this->Set("location_id", $this->GetKey()); + // Houston, I've got an issue, as this field is calculated, I should reload the object... ? + $this->Set("location_name", "abc (to be finalized)"); + */ + } + + function DisplayDetails(web_page $oPage) + { + parent::DisplayDetails($oPage); +/* + parent::DisplayDetails($oPage); + + + + $oSearchFilter = new CMDBSearchFilter('bizServer'); + $oSearchFilter->AddCondition('location_id', $this->GetKey(), '='); + $oSet = new CMDBObjectSet($oSearchFilter); + $count = $oSet->Count(); + if ($count > 0) + { + $oPage->SetCurrentTab("Servers"); + $oPage->p("$count server(s) at this location:"); + $this->DisplaySet($oPage, $oSet); + } + $oSearchFilter = new CMDBSearchFilter('bizNetworkDevice'); + $oSearchFilter->AddCondition('location_id', $this->GetKey(), '='); + $oSet = new CMDBObjectSet($oSearchFilter); + $count = $oSet->Count(); + if ($count > 0) + { + $oPage->SetCurrentTab("Network Devices"); + $oPage->p("$count Network Device(s) at this location:"); + $this->DisplaySet($oPage, $oSet); + } + $oSearchFilter = new CMDBSearchFilter('bizPC'); + $oSearchFilter->AddCondition('location_id', $this->GetKey(), '='); + $oSet = new CMDBObjectSet($oSearchFilter); + $count = $oSet->Count(); + if ($count > 0) + { + $oPage->SetCurrentTab("PCs"); + $oPage->p("$count PC(s) at this location:"); + $this->DisplaySet($oPage, $oSet); + } + $oSearchFilter = new CMDBSearchFilter('bizPerson'); + $oSearchFilter->AddCondition('location_id', $this->GetKey(), '='); + $oSet = new CMDBObjectSet($oSearchFilter); + $count = $oSet->Count(); + if ($count > 0) + { + $oPage->SetCurrentTab("Contacts"); + $oPage->p("$count person(s) located to this location:"); + $this->DisplaySet($oPage, $oSet); + } + + $oSearchFilter = new CMDBSearchFilter('lnkDocumentRealObject'); + $oSearchFilter->AddCondition('object_id', $this->GetKey(), '='); + $oSet = new CMDBObjectSet($oSearchFilter); + $count = $oSet->Count(); + if ($count > 0) + { + $oPage->SetCurrentTab("Details"); + $oPage->p("$count Document(s) linked to this location:"); + $this->DisplaySet($oPage, $oSet); + } +*/ + + } + + + public function Generate(cmdbDataGenerator $oGenerator) + { + parent::Generate($oGenerator); + $sLastName = $oGenerator->GenerateLastName(); + $sCityName = $oGenerator->GenerateCityName(); + $this->Set('name', $sCityName); + $this->Set('country', $oGenerator->GenerateCountryName()); + $this->Set('address', $oGenerator->GenerateString("number(1-999)| |enum(rue,rue,rue,place,avenue,av.,route de)| |$sLastName| |number(0000-9999)|0 |$sCityName")); + $this->Set('parent_location_id', 1); + } +} + +//////////////////////////////////////////////////////////////////////////////////// +/** +* Circuit (one end only) +*/ +//////////////////////////////////////////////////////////////////////////////////// +class bizCircuit extends logInfra +{ + public static function Init() + { + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "Circuit", + "description" => "Any type of circuit", + "key_type" => "", + "key_label" => "id", + "name_attcode" => "name", + "state_attcode" => "", + "reconc_keys" => array("org_name", "carrier_name", "carrier_ref", "name"), // inherited attributes + "db_table" => "circuits", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/Circuits.html", + ); + + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeString("speed", array("label"=>"speed", "description"=>"speed of the circuit", "allowed_values"=>null, "sql"=>"speed", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("location_id", array("targetclass"=>"bizLocation", "label"=>"Location ID", "description"=>"Id of the location", "allowed_values"=>null, "sql"=>"location_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("location_name", array("label"=>"Location", "description"=>"Name of the location", "allowed_values"=>null, "extkey_attcode"=> 'location_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("interface_id", array("targetclass"=>"bizInterface", "label"=>"Interface Id", "description"=>"id of the interface", "allowed_values"=>null, "sql"=>"interface_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("interface_name", array("label"=>"Interface", "description"=>"Name of the interface", "allowed_values"=>null, "extkey_attcode"=> 'interface_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("provider_id", array("targetclass"=>"bizOrganization", "label"=>"Carrier ID", "description"=>"Organization ID of the provider of the Circuit", "allowed_values"=>null, "sql"=>"provider_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("carrier_name", array("label"=>"Carrier", "description"=>"Name of the carrier", "allowed_values"=>null, "extkey_attcode"=> 'provider_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeString("carrier_ref", array("label"=>"Carrier reference", "description"=>"reference of the circuit used by the carrier", "allowed_values"=>null, "sql"=>"carrier_ref", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("speed"); + MetaModel::Init_AddFilterFromAttribute("location_id"); + MetaModel::Init_AddFilterFromAttribute("location_name"); + MetaModel::Init_AddFilterFromAttribute("interface_id"); + MetaModel::Init_AddFilterFromAttribute("provider_id"); + MetaModel::Init_AddFilterFromAttribute("carrier_ref"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('name', 'status', 'org_id', 'speed', 'provider_id', 'carrier_ref', 'location_id')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('name', 'status', 'org_id', 'provider_id', 'carrier_ref', 'speed')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('name', 'status', 'carrier_ref', 'speed', 'provider_id')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('name', 'status', 'carrier_ref', 'speed', 'provider_id')); // Criteria of the advanced search form + } + + public function ComputeValues() + { +/* + $oLocatedObject = MetaModel::GetObject("Located Object", $this->Get("located_object_id")); + + $this->Set("location_id", $oLocatedObject->Get("location_id")); + // Houston, I've got an issue, as this field is calculated, I should reload the object... + $this->Set("location_name", "abc (to be finalized)"); + + $this->Set("device_id", $oLocatedObject->Get("device_id")); + // Houston, I've got an issue, as this field is calculated, I should reload the object... + $this->Set("device_name", "abc (to be finalized)"); + + $this->Set("interface_id", $oLocatedObject->Get("interface_id")); + // Houston, I've got an issue, as this field is calculated, I should reload the object... + $this->Set("interface_name", "abc (to be finalized)"); +*/ + } +} + +//////////////////////////////////////////////////////////////////////////////////// +/** +* Any Device Network Interface +*/ +//////////////////////////////////////////////////////////////////////////////////// +class bizInterface extends logInfra +{ + public static function Init() + { + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "Interface", + "description" => "Interface", + "key_type" => "", + "key_label" => "id", + "name_attcode" => "name", + "state_attcode" => "", + "reconc_keys" => array("org_name", "device_name", "name"), + "db_table" => "interfaces", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/interface.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeExternalKey("device_id", array("targetclass"=>"bizDevice", "label"=>"Device", "description"=>"Device on which the interface is physically located", "allowed_values"=>null, "sql"=>"device_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("device_name", array("label"=>"Device", "description"=>"name of the device on which the interface is located", "allowed_values"=>null, "extkey_attcode"=> 'device_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeExternalField("device_location_id", array("label"=>"Device location", "description"=>"location of the device on which the interface is located", "allowed_values"=>null, "extkey_attcode"=> 'device_id', "target_attcode"=>"location_id"))); + MetaModel::Init_AddAttribute(new AttributeExternalField("device_location_name", array("label"=>"Device location", "description"=>"name of the location of the device on which the interface is located", "allowed_values"=>null, "extkey_attcode"=> 'device_id', "target_attcode"=>"location_name"))); + + MetaModel::Init_AddAttribute(new AttributeEnum("logical_type", array("label"=>"Logical type", "description"=>"Logical type of interface", "allowed_values"=>new ValueSetEnum("primary,secondary,backup,port,logical"), "sql"=>"logical_type", "default_value"=>"port", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("physical_type", array("label"=>"Physical type", "description"=>"Physical type of interface", "allowed_values"=>new ValueSetEnum("ethernet,framerelay,atm,vlan"), "sql"=>"physical_type", "default_value"=>"ethernet", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("ip_address", array("label"=>"IP address", "description"=>"address IP for this interface", "allowed_values"=>null, "sql"=>"ip_address", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("mask", array("label"=>"Subnet Mask", "description"=>"Subnet mask for this interface", "allowed_values"=>null, "sql"=>"mask", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("mac", array("label"=>"MAC address", "description"=>"MAC address for this interface", "allowed_values"=>null, "sql"=>"mac", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("speed", array("label"=>"Speed (Kb/s)", "description"=>"speed of this interface", "allowed_values"=>null, "sql"=>"speed", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("duplex", array("label"=>"Duplex", "description"=>"Duplex configured for this interface", "allowed_values"=>new ValueSetEnum("half,full,unknown"), "sql"=>"duplex", "default_value"=>"unknown", "is_null_allowed"=>true, "depends_on"=>array()))); + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("device_id"); + MetaModel::Init_AddFilterFromAttribute("device_name"); + MetaModel::Init_AddFilterFromAttribute("device_location_id"); + MetaModel::Init_AddFilterFromAttribute("logical_type"); + MetaModel::Init_AddFilterFromAttribute("physical_type"); + MetaModel::Init_AddFilterFromAttribute("ip_address"); + MetaModel::Init_AddFilterFromAttribute("mac"); + // Display lists + MetaModel::Init_SetZListItems('details', array('name', 'status', 'org_id', 'device_id', 'device_location_id','severity','logical_type','physical_type','ip_address','mask','mac','speed','duplex')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('name', 'status', 'org_id', 'device_id','severity')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('name', 'status', 'ip_address','mac','device_id')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('name', 'status', 'device_id', 'org_id')); // Criteria of the advanced search form + } + + function DisplayDetails(web_page $oPage) + { + parent::DisplayDetails($oPage); + /* + $oSearchFilter = new CMDBSearchFilter('lnkInterfaces'); + $oSearchFilter->AddCondition('interface1_id', $this->GetKey(), '='); + $oSet = new CMDBObjectSet($oSearchFilter); + $count = $oSet->Count(); + if ($count > 0) + { + $oPage->SetCurrentTab("Connected interfaces"); + $oPage->p("$count interface(s) connected to this device:"); + $this->DisplaySet($oPage, $oSet); + } + */ + } + + public function ComputeValues() + { + /* + // my location is the location of my device + $oDevice = MetaModel::GetObject("bizDevice", $this->Get("device_id")); + $this->Set("location_id", $oDevice->Get("location_id")); + // Houston, I've got an issue, as this field is calculated, I should reload the object... + $this->Set("location_name", "abc (to be finalized)"); + + // my device is given by my Creator + + // my interface is myself + $this->Set("interface_id", $this->GetKey()); + // Houston, I've got an issue, as this field is calculated, I should reload the object... + $this->Set("interface_name", "abc (to be finalized)"); + */ + } +} + +//////////////////////////////////////////////////////////////////////////////////// +/** +* n-n link between any Interfaces +*/ +//////////////////////////////////////////////////////////////////////////////////// +class lnkInterfaces extends cmdbAbstractObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "InterfacesLinks", + "description" => "A link between two interfaces", + "key_type" => "autoincrement", + "key_label" => "link_id", + "name_attcode" => "link_type", + "state_attcode" => "", + "reconc_keys" => array("interface1_id", "interface2_id"), + "db_table" => "interfaces_links", + "db_key_field" => "link_id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/default.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_AddAttribute(new AttributeExternalKey("interface1_id", array("targetclass"=>"bizInterface", "label"=>"Interface1", "description"=>"The interface1", "sql"=>"interface1_id", "allowed_values"=> null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("interface1_name", array("label"=>"Interface1 name", "description"=>"name of the interface1", "extkey_attcode"=> 'interface1_id', "allowed_values"=> null, "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("interface2_id", array("targetclass"=>"bizInterface", "label"=>"Interface2", "description"=>"The interface2", "sql"=>"interface2_id", "allowed_values"=> null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("interface2_name", array("label"=>"Interface2 name", "description"=>"name of the interface2", "extkey_attcode"=> 'interface2_id', "allowed_values"=> null, "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeExternalField("interface1_device_id", array("label"=>"Device1", "description"=>"device", "extkey_attcode"=> 'interface1_id', "allowed_values"=> null, "target_attcode"=>"device_id"))); + MetaModel::Init_AddAttribute(new AttributeExternalField("interface1_device_name", array("label"=>"Device name", "description"=>"name of the device", "extkey_attcode"=> 'interface1_id', "allowed_values"=> null, "target_attcode"=>"device_name"))); + MetaModel::Init_AddAttribute(new AttributeExternalField("interface2_device_id", array("label"=>"Device2", "description"=>"device", "extkey_attcode"=> 'interface2_id', "allowed_values"=> null, "target_attcode"=>"device_id"))); + MetaModel::Init_AddAttribute(new AttributeExternalField("interface2_device_name", array("label"=>"Device name", "description"=>"name of the device", "extkey_attcode"=> 'interface2_id', "allowed_values"=> null, "target_attcode"=>"device_name"))); + MetaModel::Init_AddAttribute(new AttributeString("link_type", array("label"=>"link type", "description"=>" Definition of the link", "sql"=>"link_type", "default_value"=>"", "allowed_values"=> null, "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_AddFilterFromAttribute("interface1_id"); + MetaModel::Init_AddFilterFromAttribute("interface1_name"); + MetaModel::Init_AddFilterFromAttribute("interface2_id"); + MetaModel::Init_AddFilterFromAttribute("interface2_name"); + MetaModel::Init_AddFilterFromAttribute("interface2_device_name"); + MetaModel::Init_AddFilterFromAttribute("link_type"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('interface1_id', 'interface2_id', 'link_type')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('interface1_id', 'interface1_device_id', 'interface2_id', 'interface2_device_id', 'link_type')); // Attributes to be displayed for the complete details + } +} + +//////////////////////////////////////////////////////////////////////////////////// +/** +* Any electronic device +*/ +//////////////////////////////////////////////////////////////////////////////////// +class bizDevice extends logInfra +{ + public static function Init() + { + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "Device", + "description" => "Electronic devices", + "key_type" => "", + "key_label" => "id", + "name_attcode" => "name", + "state_attcode" => "", + "reconc_keys" => array("org_name", "name"), // inherited attributes + "db_table" => "devices", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/default.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeExternalKey("location_id", array("targetclass"=>"bizLocation", "label"=>"Location", "description"=>"where is the located object physically located", "allowed_values"=>null, "sql"=>"location_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("location_name", array("label"=>"Location name", "description"=>"name of the location", "allowed_values"=>null, "extkey_attcode"=> 'location_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeExternalField("country", array("label"=>"Country", "description"=>"country where the device is located", "allowed_values"=>null, "extkey_attcode"=> 'location_id', "target_attcode"=>"country"))); + MetaModel::Init_AddAttribute(new AttributeString("brand", array("label"=>"Brand", "description"=>"The manufacturer of the device", "allowed_values"=>null, "sql"=>"brand", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("model", array("label"=>"Model", "description"=>"The model number of the device", "allowed_values"=>null, "sql"=>"model", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("serial_number", array("label"=>"Serial Number", "description"=>"The serial number of the device", "allowed_values"=>null, "sql"=>"serial_number", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("location_id"); + MetaModel::Init_AddFilterFromAttribute("country"); + MetaModel::Init_AddFilterFromAttribute("brand"); + MetaModel::Init_AddFilterFromAttribute("model"); + MetaModel::Init_AddFilterFromAttribute("serial_number"); + } + + public static function GetRelationQueries($sRelCode) + { + switch ($sRelCode) + { + case "impacts": + $aRels = array( + "connected device" => array("sQuery"=>"bizDevice: PKEY IS device_id IN (bizInterface: PKEY IS interface2_id IN (lnkInterfaces: interface1_id IN (bizInterface: device_id = \$[this.pkey::])))", "bPropagate"=>true, "iDistance"=>3), + "hosted app" => array("sQuery"=>"bizApplication: infra_id = \$[this.pkey::]", "bPropagate"=>true, "iDistance"=>3), + ); + return array_merge($aRels, parent::GetRelationQueries($sRelCode)); + } + } + + public function ComputeValues() + { + /* + // my location is the location of my device (external field) + $this->Set("location_id", $this->Get("device_location_id")); + // Houston, I've got an issue, as this field is calculated, I should reload the object... + $this->Set("location_name", "abc (to be finalized)"); + + // my device is myself + $this->Set("device_id", $this->GetKey()); + + // my interface is "nothing" + $this->Set("interface_id", null); + */ + } +} + +//////////////////////////////////////////////////////////////////////////////////// +/** +* A personal computer +*/ +//////////////////////////////////////////////////////////////////////////////////// +class bizPC extends bizDevice +{ + public static function Init() + { + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "PC", + "description" => "Personal Computers", + "key_type" => "", + "key_label" => "id", + "name_attcode" => "name", + "state_attcode" => "", + "reconc_keys" => array("org_name", "name"), // inherited attributes + "db_table" => "pcs", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/pc.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeEnum("type", array("label"=>"Type", "description"=>"Type of computer", "allowed_values"=>new ValueSetEnum("desktop PC,laptop"), "sql"=>"type", "default_value"=>"desktop PC", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("memory_size", array("label"=>"Memory Size", "description"=>"Size of the memory", "allowed_values"=>null, "sql"=>"memory_size", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("cpu", array("label"=>"CPU", "description"=>"CPU type", "allowed_values"=>null, "sql"=>"cpu_type", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("hdd_size", array("label"=>"HDD Size", "description"=>"Size of the hard drive", "allowed_values"=>null, "sql"=>"hdd_size", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("os_family", array("label"=>"OS Family", "description"=>"Type of operating system", "allowed_values"=>null, "sql"=>"os_family", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("os_version", array("label"=>"OS Version", "description"=>"Detailed version number of the operating system", "allowed_values"=>null, "sql"=>"os_version", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("shipment_number", array("label"=>"Shipment number", "description"=>"Number for tracking shipment", "allowed_values"=>null, "sql"=>"shipment_number", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("mgmt_ip", array("label"=>"Mgmt IP", "description"=>"Management IP", "allowed_values"=>null, "sql"=>"mgmt_ip", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("default_gateway", array("label"=>"Default Gateway", "description"=>"Default Gateway for this device", "allowed_values"=>null, "sql"=>"default_gateway", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("type"); + MetaModel::Init_AddFilterFromAttribute("memory_size"); + MetaModel::Init_AddFilterFromAttribute("cpu"); + MetaModel::Init_AddFilterFromAttribute("hdd_size"); + MetaModel::Init_AddFilterFromAttribute("os_family"); + MetaModel::Init_AddFilterFromAttribute("os_version"); + MetaModel::Init_AddFilterFromAttribute("mgmt_ip"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('name', 'status','severity', 'org_id', 'location_id', 'brand', 'model','os_family','os_version','mgmt_ip','default_gateway','shipment_number','serial_number', 'type', 'cpu', 'memory_size', 'hdd_size')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('name', 'status', 'severity', 'org_id', 'location_id', 'brand', 'model', 'type')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('name', 'status', 'severity','type', 'brand', 'model','os_family','mgmt_ip')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('name', 'status', 'type', 'brand', 'model', 'cpu', 'memory_size', 'hdd_size')); // Criteria of the advanced search form + } + + function DisplayDetails(web_page $oPage) + { + parent::DisplayDetails($oPage); + /* + parent::DisplayDetails($oPage); + $oSearchFilter = new CMDBSearchFilter('lnkContactRealObject'); + $oSearchFilter->AddCondition('object_id', $this->GetKey(), '='); + $oSet = new CMDBObjectSet($oSearchFilter); + $count = $oSet->Count(); + if ($count > 0) + { + $oPage->SetCurrentTab("Contacts"); + $oPage->p("$count contact(s) linked to this PC:"); + $this->DisplaySet($oPage, $oSet); + } + $oSearchFilter = new CMDBSearchFilter('bizInterface'); + $oSearchFilter->AddCondition('device_id', $this->GetKey(), '='); + $oSet = new CMDBObjectSet($oSearchFilter); + $count = $oSet->Count(); + if ($count > 0) + { + $oPage->SetCurrentTab("Interfaces"); + $oPage->p("$count interface(s) for this device:"); + $this->DisplaySet($oPage, $oSet); + } + */ + } + + + public function Generate(cmdbDataGenerator $oGenerator) + { + $this->Set('org_id', $oGenerator->GetOrganizationId()); + $this->Set('location_id', $oGenerator->GenerateKey("bizLocation", array('org_id' =>$oGenerator->GetOrganizationId() ))); + $this->Set('name', $oGenerator->GenerateString("enum(pc,pc,pc,pc,pc,win,redhat,linux,srv,workstation)|number(000-999)|.|domain()")); + $this->Set('brand', $oGenerator->GenerateString("enum(Hewlett-Packard,Dell,Compaq,Siemens,Packard Bell,IBM,Gateway,Medion,Sony)")); + $this->Set('model', $oGenerator->GenerateString("enum(Vectra,Deskpro,Dimension,Optiplex,Latitude,Precision,Vaio)")); + $this->Set('serial_number', $oGenerator->GenerateString("enum(FR,US,TW,CH)|number(000000-999999)")); + $this->Set('memory_size', $oGenerator->GenerateString("enum(128,256,384,512,768,1024,1536,2048)")); + $this->Set('cpu', $oGenerator->GenerateString("enum(Pentium III,Pentium 4, Pentium M,Core Duo,Core 2 Duo,Celeron,Opteron,Thurion,Athlon)")); + $this->Set('hdd_size', $oGenerator->GenerateString("enum(40,60,80,120,200,300)")); + } +} + +//////////////////////////////////////////////////////////////////////////////////// +/** +* A server +*/ +//////////////////////////////////////////////////////////////////////////////////// +class bizServer extends bizDevice +{ + public static function Init() + { + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "Server", + "description" => "Computer Servers", + "key_type" => "", + "key_label" => "id", + "name_attcode" => "name", + "state_attcode" => "status", + "reconc_keys" => array("org_name", "name"), // inherited attributes + "db_table" => "servers", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/server.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeEnum("status", array("label"=>"Status", "description"=>"Status of the server", "allowed_values"=>new ValueSetEnum("In Store,Shipped,Plugged,Production Candidate,In Production,In Change,Being Deconfigured,Obsolete"), "sql"=>"status", "default_value"=>"In Store", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("memory_size", array("label"=>"Memory Size", "description"=>"Size of the memory", "allowed_values"=>null, "sql"=>"memory_size", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("cpu", array("label"=>"Model", "description"=>"CPU type", "allowed_values"=>null, "sql"=>"cpu_type", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("number_of_cpus", array("label"=>"Number of CPUs", "description"=>"Number of CPUs", "allowed_values"=>null, "sql"=>"number_of_cpus", "default_value"=>"1", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("hdd_size", array("label"=>"HDD Size", "description"=>"Size of the hard drive", "allowed_values"=>null, "sql"=>"hdd_size", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("hdd_free_size", array("label"=>"Free HDD Size", "description"=>"Size of the free space on the hard drive(s)", "allowed_values"=>null, "sql"=>"hdd_free_size", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("os_family", array("label"=>"OS Family", "description"=>"Type of operating system", "allowed_values"=>null, "sql"=>"os_family", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("os_version", array("label"=>"OS Version", "description"=>"Detailed version number of the operating system", "allowed_values"=>null, "sql"=>"os_version", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("shipment_number", array("label"=>"Shipment number", "description"=>"Number for tracking shipment", "allowed_values"=>null, "sql"=>"shipment_number", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("mgmt_ip", array("label"=>"Mgmt IP", "description"=>"Management IP", "allowed_values"=>null, "sql"=>"mgmt_ip", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("default_gateway", array("label"=>"Default Gateway", "description"=>"Default Gateway for this device", "allowed_values"=>null, "sql"=>"default_gateway", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("memory_size"); + MetaModel::Init_AddFilterFromAttribute("cpu"); + MetaModel::Init_AddFilterFromAttribute("number_of_cpus"); + MetaModel::Init_AddFilterFromAttribute("hdd_size"); + MetaModel::Init_AddFilterFromAttribute("hdd_free_size"); + MetaModel::Init_AddFilterFromAttribute("os_family"); + MetaModel::Init_AddFilterFromAttribute("os_version"); + + + // Life cycle + MetaModel::Init_DefineState("In Store", array("label"=>"InStore", "description"=>"Device in store", "attribute_inherit"=>null, + "attribute_list"=>array())); + MetaModel::Init_DefineState("Shipped", array("label"=>"Shipped", "description"=>"The device had been shipped to future location", "attribute_inherit"=>null, + "attribute_list"=>array("location_id"=>OPT_ATT_MANDATORY,"serial_number"=>OPT_ATT_MANDATORY,"shipment_number"=>OPT_ATT_MANDATORY))); + MetaModel::Init_DefineState("Plugged", array("label"=>"Plugged", "description"=>"The device is connected to the network", "attribute_inherit"=>null, + "attribute_list"=>array("location_id"=>OPT_ATT_MANDATORY,"mgmt_ip"=>OPT_ATT_MANDATORY,"name"=>OPT_ATT_MANDATORY))); + MetaModel::Init_DefineState("Production Candidate", array("label"=>"Pre-Production", "description"=>"The device is ready to be move to production", "attribute_inherit"=>null, + "attribute_list"=>array())); + MetaModel::Init_DefineState("In Production", array("label"=>"Production", "description"=>"The device is on production", "attribute_inherit"=>null, + "attribute_list"=>array())); + MetaModel::Init_DefineState("In Change", array("label"=>"InChange", "description"=>"A change is being performed on the device", "attribute_inherit"=>null, + "attribute_list"=>array())); + MetaModel::Init_DefineState("Being Deconfigured", array("label"=>"BeingDeconfigured", "description"=>"The device is about to be removed from is current location", "attribute_inherit"=>null, + "attribute_list"=>array())); + MetaModel::Init_DefineState("Obsolete", array("label"=>"Obsolete", "description"=>"The device is no more used", "attribute_inherit"=>null, + "attribute_list"=>array())); + + + MetaModel::Init_DefineStimulus("ev_store", new StimulusUserAction(array("label"=>"Store this server", "description"=>"This server is move to storage"))); + MetaModel::Init_DefineStimulus("ev_ship", new StimulusUserAction(array("label"=>"Ship this server", "description"=>"This server is shipped to futur location"))); + MetaModel::Init_DefineStimulus("ev_plug", new StimulusUserAction(array("label"=>"Plug this server", "description"=>"The server is pluuged on the network"))); + MetaModel::Init_DefineStimulus("ev_configuration_finished", new StimulusUserAction(array("label"=>"Configuration finished", "description"=>"The device is ready to move to production evaluation"))); + MetaModel::Init_DefineStimulus("ev_val_failed", new StimulusUserAction(array("label"=>"Review configuration", "description"=>"The configuration for this server is not completed"))); + MetaModel::Init_DefineStimulus("ev_mtp", new StimulusUserAction(array("label"=>"Move to Production", "description"=>"The server is moved to production"))); + MetaModel::Init_DefineStimulus("ev_start_change", new StimulusUserAction(array("label"=>"Change Start [No Click]", "description"=>"A change starts for this server"))); + MetaModel::Init_DefineStimulus("ev_end_change", new StimulusUserAction(array("label"=>"End Change [No Click]", "description"=>"No more change running for this server"))); + MetaModel::Init_DefineStimulus("ev_decommission", new StimulusUserAction(array("label"=>"Decommission", "description"=>"The server is being decommissioned"))); + MetaModel::Init_DefineStimulus("ev_obsolete", new StimulusUserAction(array("label"=>"Obsolete", "description"=>"The server is no more used"))); + MetaModel::Init_DefineStimulus("ev_recycle", new StimulusUserAction(array("label"=>"Recycle this server", "description"=>"The server is move back to deconfiguration"))); + + MetaModel::Init_DefineTransition("In Store", "ev_ship", array("target_state"=>"Shipped", "actions"=>array(), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("In Store", "ev_plug", array("target_state"=>"Plugged", "actions"=>array(), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Shipped", "ev_store", array("target_state"=>"In Store", "actions"=>array(), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Shipped", "ev_plug", array("target_state"=>"Plugged", "actions"=>array(), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Plugged", "ev_ship", array("target_state"=>"Shipped", "actions"=>array(), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Plugged", "ev_store", array("target_state"=>"In Store", "actions"=>array(), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Plugged", "ev_configuration_finished", array("target_state"=>"Production Candidate", "actions"=>array(), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Production Candidate", "ev_val_failed", array("target_state"=>"Plugged", "actions"=>array(), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Production Candidate", "ev_mtp", array("target_state"=>"In Production", "actions"=>array(), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("In Production", "ev_start_change", array("target_state"=>"In Change", "actions"=>array(), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("In Production", "ev_obsolete", array("target_state"=>"Obsolete", "actions"=>array(), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("In Production", "ev_decommission", array("target_state"=>"Being Deconfigured", "actions"=>array(), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("In Change", "ev_end_change", array("target_state"=>"In Production", "actions"=>array(), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Being Deconfigured", "ev_ship", array("target_state"=>"Shipped", "actions"=>array(), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Being Deconfigured", "ev_plug", array("target_state"=>"Plugged", "actions"=>array(), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Being Deconfigured", "ev_store", array("target_state"=>"In Store", "actions"=>array(), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Being Deconfigured", "ev_obsolete", array("target_state"=>"Obsolete", "actions"=>array(), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Obsolete", "ev_recycle", array("target_state"=>"Being Deconfigured", "actions"=>array(), "user_restriction"=>null)); + + + + + + // Display lists + + MetaModel::Init_SetZListItems('details', array('name', 'mgmt_ip','default_gateway','status', 'severity','org_id', 'location_id', 'brand', 'model', 'os_family', 'os_version','serial_number','shipment_number', 'cpu', 'number_of_cpus', 'memory_size', 'hdd_size', 'hdd_free_size')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('name', 'status','severity', 'org_id', 'location_id', 'brand', 'model', 'os_family', 'os_version')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('name', 'status','severity', 'brand', 'model', 'os_family', 'location_id')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('name', 'status','brand', 'model', 'os_family', 'os_version', 'location_id', 'cpu', 'number_of_cpus', 'memory_size', 'hdd_size', 'hdd_free_size')); // Criteria of the advanced search form + } + + function DisplayDetails(web_page $oPage) + { + parent::DisplayDetails($oPage); + /* + parent::DisplayDetails($oPage); + $oSearchFilter = new CMDBSearchFilter('lnkContactRealObject'); + $oSearchFilter->AddCondition('object_id', $this->GetKey(), '='); + $oSet = new CMDBObjectSet($oSearchFilter); + $count = $oSet->Count(); + if ($count > 0) + { + $oPage->SetCurrentTab("Contacts"); + $oPage->p("$count contact(s) for this server:"); + $this->DisplaySet($oPage, $oSet); + } + $oSearchFilter = new CMDBSearchFilter('bizInterface'); + $oSearchFilter->AddCondition('device_id', $this->GetKey(), '='); + $oSet = new CMDBObjectSet($oSearchFilter); + $count = $oSet->Count(); + if ($count > 0) + { + $oPage->SetCurrentTab("Interfaces"); + $oPage->p("$count interface(s) for this server:"); + $this->DisplaySet($oPage, $oSet); + } + $oSearchFilter = new CMDBSearchFilter('Application'); + $oSearchFilter->AddCondition('infra_id', $this->GetKey(), '='); + $oSet = new CMDBObjectSet($oSearchFilter); + $count = $oSet->Count(); + if ($count > 0) + { + $oPage->SetCurrentTab("Installed applications"); + $oPage->p("$count application(s) installed on this server:"); + $this->DisplaySet($oPage, $oSet); + } + $oSearchFilter = new CMDBSearchFilter('bizPatch'); + $oSearchFilter->AddCondition('infra_id', $this->GetKey(), '='); + $oSet = new CMDBObjectSet($oSearchFilter); + $count = $oSet->Count(); + if ($count > 0) + { + $oPage->SetCurrentTab("Installed patches"); + $oPage->p("$count patch(s) installed on this server:"); + $this->DisplaySet($oPage, $oSet); + } + */ + + + } + + + + public function Generate(cmdbDataGenerator $oGenerator) + { + $this->Set('org_id', $oGenerator->GetOrganizationId()); + $this->Set('location_id', $oGenerator->GenerateKey("bizLocation", array('org_id' =>$oGenerator->GetOrganizationId() ))); + $this->Set('name', $oGenerator->GenerateString("enum(pc,pc,pc,pc,pc,win,redhat,linux,srv,workstation)|number(000-999)|.|domain()")); + $this->Set('brand', $oGenerator->GenerateString("enum(Hewlett-Packard,Dell,Compaq,Siemens,Packard Bell,IBM,Gateway,Medion,Sony)")); + $this->Set('model', $oGenerator->GenerateString("enum(Vectra,Deskpro,Dimension,Optiplex,Latitude,Precision,Vaio)")); + $this->Set('serial_number', $oGenerator->GenerateString("enum(FR,US,TW,CH)|number(000000-999999)")); + $this->Set('memory_size', $oGenerator->GenerateString("enum(512,1024,2048,4096,2048,4096,8192,8192,8192,16384,32768)")); + $this->Set('cpu', $oGenerator->GenerateString("enum(Pentium III,Pentium 4,Pentium M,Core Duo,Core 2 Duo,Celeron,Opteron,Thurion,Athlon)")); + $this->Set('number_of_cpu', $oGenerator->GenerateString("enum(1,1,2,2,2,2,2,4,4,8)")); + $this->Set('hdd_size', $oGenerator->GenerateString("enum(500,1024,500,1024,500,1024,2048)")); + $this->Set('hdd_free_size', $this->Get('hdd_size')*$oGenerator->GenerateString("number(20-80)")); + $this->Set('os_family', $oGenerator->GenerateString("enum(Windows,Windows,Windows,Linux,Windows,Linux,Windows,Linux,Linux,HP-UX,Solaris,AIX)")); + $this->Set('os_version', $oGenerator->GenerateString("enum(XP,XP,XP,RH EL 4,RH EL 5,SuSE 10.3,SuSE 10.4,11.11,11.11i)")); + } +} + +//////////////////////////////////////////////////////////////////////////////////// +/** +* A network device +*/ +//////////////////////////////////////////////////////////////////////////////////// +class bizNetworkDevice extends bizDevice +{ + public static function Init() + { + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "Network Device", + "description" => "A network device", + "key_type" => "", + "key_label" => "id", + "name_attcode" => "name", + "state_attcode" => "", + "reconc_keys" => array("org_name", "name"), // inherited attributes + "db_table" => "network_devices", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/network.device.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeEnum("type", array("label"=>"Type", "description"=>"Type of device", "allowed_values"=>new ValueSetEnum("switch,router,firewall,load balancer,hub,WAN accelerator"), "sql"=>"type", "default_value"=>"switch", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("ip_address", array("label"=>"Mgmt IP", "description"=>"Management IP address", "allowed_values"=>null, "sql"=>"ip_address", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("default_gateway", array("label"=>"Default Gateway", "description"=>"Default Gateway for this device", "allowed_values"=>null, "sql"=>"default_gateway", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("ios_version", array("label"=>"IOS version", "description"=>"IOS (software) version", "allowed_values"=>null, "sql"=>"ios_version", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("memory", array("label"=>"Memory", "description"=>"Memory description", "allowed_values"=>null, "sql"=>"memory", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + + MetaModel::Init_AddAttribute(new AttributeString("snmp_read", array("label"=>"SNMP Community (Read)", "description"=>"SNMP Read Community String", "allowed_values"=>null, "sql"=>"snmp_read", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("snmp_write", array("label"=>"SNMP Community (Write)", "description"=>"SNMP Write Community String", "allowed_values"=>null, "sql"=>"snmp_write", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("type"); + MetaModel::Init_AddFilterFromAttribute("ip_address"); + MetaModel::Init_AddFilterFromAttribute("ios_version"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('name', 'status','severity','org_id', 'location_id', 'brand','model','type','ip_address','default_gateway','serial_number','ios_version','memory','snmp_read','snmp_write')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('name', 'status','brand','model','type','ip_address')); // Attributes to be displayed for a list + + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('name', 'status', 'location_id', 'brand','model','type','ip_address')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('name', 'status', 'org_id', 'location_id', 'brand','model','type','ip_address','serial_number','ios_version','snmp_read','snmp_write')); // Criteria of the advanced search form + + + } + + public function Generate(cmdbDataGenerator $oGenerator) + { + $this->Set('org_id', $oGenerator->GetOrganizationId()); + $this->Set('location_id', $oGenerator->GenerateKey("bizLocation", array('org_id' =>$oGenerator->GetOrganizationId() ))); + $this->Set('name', $oGenerator->GenerateString("enum(sw,swi,switch,rout,rtr,gw)|number(000-999)|.|domain()")); + $this->Set('brand', $oGenerator->GenerateString("enum(Hewlett-Packard,Cisco,3Com,Avaya,Alcatel,Cabletron,Extrem Networks,Juniper,Netgear,Synopitcs,Xylan)")); + $this->Set('model', $oGenerator->GenerateString("enum(Procurve ,Catalyst ,Multiswitch ,C)|enum(25,26,36,40,65)|enum(00,09,10,50)")); + $this->Set('serial_number', $oGenerator->GenerateString("enum(FAA,AGA,PAD,COB,DFE)|number(0000-9999)|enum(M,X,L)")); + $this->Set('ip_address', $oGenerator->GenerateString("number(10-248)|.|number(1-254)|.|number(1-254)|.|number(1-254)")); + $this->Set('ios_version', $oGenerator->GenerateString("enum(9,10,12)|.|enum(0,1,2)|enum(,,,,XP,.5.1)")); + $this->Set('snmp_read', $oGenerator->GenerateString("enum(Ew,+0,**,Ps)|number(00-99)|enum(+,=,],;, )|enum(Aze,Vbn,Bbn,+9+,-9-,#)")); + $this->Set('snmp_write', $oGenerator->GenerateString("enum(M3,l3,$,*,Zz,Ks,jh)|number(00-99)|enum(A*e,V%n,Bbn,+,-,#)|number(0-9)")); + } +} + +//////////////////////////////////////////////////////////////////////////////////// +/** +* A "Solution" +*/ +//////////////////////////////////////////////////////////////////////////////////// +class bizInfraGroup extends logInfra +{ + public static function Init() + { + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "Infra Group", + "description" => "A group of infrastructure elements", + "key_type" => "", + "key_label" => "id", + "name_attcode" => "name", + "state_attcode" => "", + "reconc_keys" => array("org_name", "name"), // inherited attributes + "db_table" => "infra_group", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/group.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeEnum("type", array("label"=>"Type", "description"=>"Type of groupe", "allowed_values"=>new ValueSetEnum("Monitoring,Reporting,list"), "sql"=>"type", "default_value"=>"list", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("description", array("label"=>"Description", "description"=>"usage of the Group", "allowed_values"=>null, "sql"=>"description", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("parent_group_id", array("targetclass"=>"bizInfraGroup", "label"=>"Parent Group", "description"=>"including group", "allowed_values"=>null, "sql"=>"parent_group_id", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("parent_group_name", array("label"=>"Parent Group (Name)", "description"=>"name of the parent group", "allowed_values"=>null, "extkey_attcode"=> 'parent_group_id', "target_attcode"=>"name"))); + + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("type"); + MetaModel::Init_AddFilterFromAttribute("parent_group_id"); + MetaModel::Init_AddFilterFromAttribute("parent_group_name"); + + + // Display lists + MetaModel::Init_SetZListItems('details', array('name', 'status', 'org_id', 'type', 'description','parent_group_id')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('list', array('name', 'status', 'org_id', 'type', 'description')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('name', 'status', 'type')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('name', 'status', 'type', 'description', 'org_id')); // Criteria of the advanced search form + } + + function DisplayDetails(web_page $oPage) + { + parent::DisplayDetails($oPage); + /* + $oSearchFilter = new CMDBSearchFilter('lnkInfraGrouping'); + $oSearchFilter->AddCondition('infra_group_id', $this->GetKey(), '='); + $oSet = new CMDBObjectSet($oSearchFilter); + $count = $oSet->Count(); + if ($count > 0) + { + $oPage->SetCurrentTab("RelatedInfrastructure"); + $oPage->p("Infrastructure Link to this group:"); + $this->DisplaySet($oPage, $oSet); + } + $oSearchFilter = new CMDBSearchFilter('lnkContactRealObject'); + $oSearchFilter->AddCondition('object_id', $this->GetKey(), '='); + $oSet = new CMDBObjectSet($oSearchFilter); + $count = $oSet->Count(); + if ($count > 0) + { + $oPage->SetCurrentTab("TeamLinks"); + $oPage->p("People concerned by this group:"); + $this->DisplaySet($oPage, $oSet); + } +*/ + } + + + + + public function Generate(cmdbDataGenerator $oGenerator) + { + $this->Set('org_id', $oGenerator->GetOrganizationId()); + $this->Set('name', $oGenerator->GenerateString("enum(ov_nnm_,ovpi_,vitalnet_,datacenter_,web_farm_)|number(000-999)")); + $this->Set('type', $oGenerator->GenerateString("enum(Application,Infrastructure)")); + } +} +//////////////////////////////////////////////////////////////////////////////////// +//** +//* An application is an instance of a software install on a PC or Server +//* +//////////////////////////////////////////////////////////////////////////////////// +class bizApplication extends logInfra +{ + public static function Init() + { + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "Application", + "description" => "General application", + "key_type" => "", + "key_label" => "id", + "name_attcode" => "name", + "state_attcode" => "", + "reconc_keys" => array("device_name", "name"), // inherited attributes + "db_table" => "applications", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/application.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeExternalKey("device_id", array("targetclass"=>"bizDevice", "jointype"=> '', "label"=>"Hosting device", "description"=>"The device where application is installed", "allowed_values"=>null, "sql"=>"device_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("device_name", array("label"=>"Hosting device", "description"=>"Name of the device where application is installed", "allowed_values"=>null, "extkey_attcode"=> 'device_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeDate("install_date", array("label"=>"Installed date", "description"=>"Date when application was installed", "allowed_values"=>null, "sql"=>"install_date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_AddAttribute(new AttributeString("version", array("label"=>"Version", "description"=>"Application version", "allowed_values"=>null, "sql"=>"version", "default_value"=>"undefined", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("function", array("label"=>"Function", "description"=>"Function provided by this application", "allowed_values"=>null, "sql"=>"function", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("function"); + MetaModel::Init_AddFilterFromAttribute("version"); + MetaModel::Init_AddFilterFromAttribute("device_id"); + + + + + // Display lists + MetaModel::Init_SetZListItems('details', array('name','device_id','org_id','status','install_date', 'version','function')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('name','device_id', 'version', 'function')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('name', 'device_id','version','function')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('name', 'device_id','version','function')); // Criteria of the advanced search form + + } + + public static function GetRelationQueries($sRelCode) + { + switch ($sRelCode) + { + case "impacts": + $aRels = array( + "client app" => array("sQuery"=>"bizApplication: PKEY IS client_id IN (lnkClientServer: server_id = \$[this.pkey::])", "bPropagate"=>true, "iDistance"=>3), + ); + return array_merge($aRels, parent::GetRelationQueries($sRelCode)); + } + } + + function DisplayDetails(web_page $oPage) + { + parent::DisplayDetails($oPage); + /* + $oSearchFilter = new CMDBSearchFilter('lnkClientServer'); + $oSearchFilter->AddCondition('server_id', $this->GetKey(), '='); + $oSet = new CMDBObjectSet($oSearchFilter); + $count = $oSet->Count(); + if ($count > 0) + { + $oPage->SetCurrentTab("Connected clients"); + $oPage->p("Client applications impacted when down:"); + $this->DisplaySet($oPage, $oSet); + } +*/ + } + +} + +//////////////////////////////////////////////////////////////////////////////////// +/** +* n-n link between any Infra and a Group +*/ +//////////////////////////////////////////////////////////////////////////////////// +class lnkInfraGrouping extends cmdbAbstractObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "Infra Grouping", + "description" => "Infra part of an Infra Group", + "key_type" => "autoincrement", + "key_label" => "link_id", + "name_attcode" => "impact", + "state_attcode" => "", + "reconc_keys" => array(""), + "db_table" => "infra_grouping", + "db_key_field" => "link_id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/default.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_AddAttribute(new AttributeExternalKey("infra_id", array("targetclass"=>"logInfra", "jointype"=> '', "label"=>"Infrastructure", "description"=>"Infrastructure part of the group", "allowed_values"=>null, "sql"=>"infra_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("infra_name", array("label"=>"Infrastructure name", "description"=>"Name of the impacted infrastructure", "allowed_values"=>null, "extkey_attcode"=> 'infra_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeExternalField("infra_status", array("label"=>"Status", "description"=>"Status of the impacted infrastructure", "allowed_values"=>null, "extkey_attcode"=> 'infra_id', "target_attcode"=>"status"))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("infra_group_id", array("targetclass"=>"bizInfraGroup", "jointype"=> '', "label"=>"Group Name", "description"=>"Name of the group", "allowed_values"=>null, "sql"=>"infra_group_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("group_name", array("label"=>"Group name", "description"=>"Name of the group containing infrastructure", "allowed_values"=>null, "extkey_attcode"=> 'infra_group_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeString("impact", array("label"=>"Relation", "description"=>"Relation between this group and infra", "allowed_values"=>null, "sql"=>"impact", "default_value"=>"none", "is_null_allowed"=>true, "depends_on"=>array()))); + // impact should modelized: enum (eg: if the group si dead when infra is dead) + + MetaModel::Init_AddFilterFromAttribute("infra_id"); + MetaModel::Init_AddFilterFromAttribute("infra_group_id"); + MetaModel::Init_AddFilterFromAttribute("impact"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('infra_id','infra_status', 'impact', 'infra_group_id')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('list', array('infra_id','infra_status', 'impact', 'infra_group_id')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('infra_id', 'infra_group_id', 'impact')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('infra_id', 'infra_group_id', 'impact')); // Criteria of the advanced search form + } + + public function Generate(cmdbDataGenerator $oGenerator) + { + $this->Set('infra_id', $oGenerator->GenerateKey("logInfra", array('org_id' =>$oGenerator->GetOrganizationId() ))); + $this->Set('infra_group_id', $oGenerator->GenerateKey("bizInfraGroup", array('org_id' =>$oGenerator->GetOrganizationId() ))); + $this->Set('impact', $oGenerator->GenerateString("enum(none,mandatory,partial)")); + } + +} + + + + + + +//////////////////////////////////////////////////////////////////////////////////// +/** +* n-n link between two applications, one is the server side and the scond one the client*/ +//////////////////////////////////////////////////////////////////////////////////// +class lnkClientServer extends logRealObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "ClientServerLinks", + "description" => "Link between client server application", + "key_type" => "autoincrement", + "key_label" => "link_id", + "name_attcode" => "relation", // ???? + "state_attcode" => "", + "reconc_keys" => array("relation"), // ???? + "db_table" => "clientserver_links", + "db_key_field" => "link_id", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_AddAttribute(new AttributeExternalKey("client_id", array("targetclass"=>"bizApplication", "jointype"=> '', "label"=>"Client", "description"=>"The client part of the link", "allowed_values"=>null, "sql"=>"client_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("client_name", array("label"=>"Client", "description"=>"Name of the client", "allowed_values"=>null, "extkey_attcode"=> 'client_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("server_id", array("targetclass"=>"bizApplication", "jointype"=> '', "label"=>"Server", "description"=>"the server part of the link", "allowed_values"=>null, "sql"=>"server_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("server_name", array("label"=>"Server", "description"=>"Name of the server", "allowed_values"=>null, "extkey_attcode"=> 'server_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeString("relation", array("label"=>"Relation", "description"=>"Type of relation between both application", "allowed_values"=>null, "sql"=>"relation", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + + MetaModel::Init_AddFilterFromAttribute("client_id"); + MetaModel::Init_AddFilterFromAttribute("server_id"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('client_id', 'server_id', 'relation')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('list', array('client_id', 'server_id', 'relation')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('client_id', 'server_id')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('client_id', 'server_id')); // Criteria of the advanced search form + } + + +} + +//////////////////////////////////////////////////////////////////////////////////// +//** +//* A patch is an application or OS fixe for an infrastructure +//* +//////////////////////////////////////////////////////////////////////////////////// +class bizPatch extends logRealObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "bizmodel,searchable", + "name" => "Patch", + "description" => "Patch installed on infrastucture", + "key_type" => "", + "key_label" => "id", + "name_attcode" => "name", + "state_attcode" => "", + "reconc_keys" => array("device_name", "name"), // inherited attributes + "db_table" => "patches", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "../business/templates/default.html", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeExternalKey("device_id", array("targetclass"=>"bizDevice", "jointype"=> '', "label"=>"Device", "description"=>"The Device where patch is installed", "allowed_values"=>null, "sql"=>"device_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("device_name", array("label"=>"Device name", "description"=>"Name of the impacted device", "allowed_values"=>null, "extkey_attcode"=> 'device_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeDate("install_date", array("label"=>"Installed date", "description"=>"Date when application was installed", "allowed_values"=>null, "sql"=>"install_date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_AddAttribute(new AttributeString("description", array("label"=>"Description", "description"=>"description du patch", "allowed_values"=>null, "sql"=>"description", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("patch_type", array("label"=>"Type", "description"=>"type de patch", "allowed_values"=>new ValueSetEnum("OS,Application"), "sql"=>"patch_type", "default_value"=>"OS", "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_InheritFilters(); + + MetaModel::Init_AddFilterFromAttribute("patch_type"); + MetaModel::Init_AddFilterFromAttribute("device_id"); + + + + + // Display lists + MetaModel::Init_SetZListItems('details', array('name','device_id', 'install_date', 'patch_type','description')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('name','device_id', 'patch_type','install_date')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('name', 'device_id','patch_type')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('name', 'device_id','patch_type')); // Criteria of the advanced search form + + } +} + +/*** Insert here all modules requires for ITOP application ***/ + +require_once('incident.business.php'); +require_once('ServiceMgmt.business.php'); +require_once('ChangeMgmt.php'); +require_once('KEDB.php') +?> diff --git a/business/templates/Circuits.html b/business/templates/Circuits.html new file mode 100644 index 000000000..c3de83c89 --- /dev/null +++ b/business/templates/Circuits.html @@ -0,0 +1,12 @@ + + +$class$: pkey = $pkey$ + + + bizInterface: PKEY IS interface_id IN (bizCircuit: pkey = $pkey$) + + diff --git a/business/templates/application.html b/business/templates/application.html new file mode 100644 index 000000000..4e3c7575e --- /dev/null +++ b/business/templates/application.html @@ -0,0 +1,19 @@ + + +bizApplication: pkey = $pkey$ + + + lnkClientServer: server_id = $pkey$ + + + lnkClientServer: client_id = $pkey$ + + + lnkInfraContract: infra_id = $pkey$ + + + diff --git a/business/templates/change.html b/business/templates/change.html new file mode 100644 index 000000000..694b26fd6 --- /dev/null +++ b/business/templates/change.html @@ -0,0 +1,17 @@ + + +$class$: pkey = $pkey$ + + + lnkInfraChangeTicket: ticket_id = $pkey$ + + + lnkContactChange: change_id = $pkey$ + + + +
    diff --git a/business/templates/contract.html b/business/templates/contract.html new file mode 100644 index 000000000..574c6e37e --- /dev/null +++ b/business/templates/contract.html @@ -0,0 +1,18 @@ + + +bizContract: pkey = $pkey$ + + + lnkInfraContract: contract_id = $pkey$ + + + lnkContactContract: contract_id = $pkey$ + + + lnkDocumentContract: contract_id = $pkey$ + + diff --git a/business/templates/default.html b/business/templates/default.html new file mode 100644 index 000000000..238a714d8 --- /dev/null +++ b/business/templates/default.html @@ -0,0 +1,8 @@ + + +$class$: pkey = $pkey$ + diff --git a/business/templates/document.html b/business/templates/document.html new file mode 100644 index 000000000..470559e8f --- /dev/null +++ b/business/templates/document.html @@ -0,0 +1,13 @@ + + +$class$: pkey = $pkey$ + + + bizDocVersion: document = $pkey$ + + + diff --git a/business/templates/group.html b/business/templates/group.html new file mode 100644 index 000000000..11bc83845 --- /dev/null +++ b/business/templates/group.html @@ -0,0 +1,16 @@ + + +bizInfraGroup: pkey = $pkey$ + + + lnkInfraGrouping: infra_group_id = $pkey$ + + + lnkContactRealObject: object_id = $pkey$ + + + diff --git a/business/templates/interface.html b/business/templates/interface.html new file mode 100644 index 000000000..3f1fe7f95 --- /dev/null +++ b/business/templates/interface.html @@ -0,0 +1,14 @@ + + +bizInterface: pkey = $pkey$ + + + lnkInterfaces: interface1_id = $pkey$ + lnkInterfaces: interface2_id = $pkey$ + + + diff --git a/business/templates/knownError.html b/business/templates/knownError.html new file mode 100644 index 000000000..3c321384d --- /dev/null +++ b/business/templates/knownError.html @@ -0,0 +1,11 @@ + +bizKnownError: pkey = $pkey$ + + + lnkInfraError: error_id = $pkey$ + + diff --git a/business/templates/location.html b/business/templates/location.html new file mode 100644 index 000000000..af2df49e0 --- /dev/null +++ b/business/templates/location.html @@ -0,0 +1,25 @@ + + +bizLocation: pkey = $pkey$ + + + bizContact: location_id = $pkey$ + + + bizServer: location_id = $pkey$ + + + bizPC: location_id = $pkey$ + + + bizNetworkDevice: location_id = $pkey$ + + + + lnkDocumentRealObject: object_id = $pkey$ + + diff --git a/business/templates/network.device.html b/business/templates/network.device.html new file mode 100644 index 000000000..cb3a32385 --- /dev/null +++ b/business/templates/network.device.html @@ -0,0 +1,25 @@ + + +bizNetworkDevice: pkey = $pkey$ + + + bizInterface: device_id = $pkey$ + + + lnkContactRealObject: object_id = $pkey$ + + + bizIncidentTicket: PKEY IS ticket_id IN (lnkInfraTicket: infra_id = $pkey$) + + + bizChangeTicket: PKEY IS ticket_id IN (lnkInfraChangeTicket: infra_id = $pkey$) + + + lnkDocumentRealObject: object_id = $pkey$ + + + diff --git a/business/templates/pc.html b/business/templates/pc.html new file mode 100644 index 000000000..55597a4ac --- /dev/null +++ b/business/templates/pc.html @@ -0,0 +1,27 @@ + + +bizPC: pkey = $pkey$ + + + bizApplication: device_id = $pkey$ + + + bizPatch: device_id = $pkey$ + + + lnkContactRealObject: object_id = $pkey$ + + + bizInterface: device_id = $pkey$ + + + bizIncidentTicket: PKEY IS ticket_id IN (lnkInfraTicket: infra_id = $pkey$) + + + bizDocument: PKEY IS doc_id IN (lnkDocumentRealObject: object_id = $pkey$) + + diff --git a/business/templates/person.html b/business/templates/person.html new file mode 100644 index 000000000..9bc09ad33 --- /dev/null +++ b/business/templates/person.html @@ -0,0 +1,18 @@ + + +bizPerson: pkey = $pkey$ + + + lnkContactRealObject: object_id = $pkey$ + + + lnkContactRealObject: contact_id = $pkey$ + + + lnkDocumentRealObject: object_id = $pkey$ + + diff --git a/business/templates/server.html b/business/templates/server.html new file mode 100644 index 000000000..f6f5793ed --- /dev/null +++ b/business/templates/server.html @@ -0,0 +1,30 @@ + + +bizServer: pkey = $pkey$ + + + bizApplication: device_id = $pkey$ + + + bizPatch: device_id = $pkey$ + + + bizInterface: device_id = $pkey$ + + + lnkContactRealObject: object_id = $pkey$ + + + bizIncidentTicket: PKEY IS ticket_id IN (lnkInfraTicket: infra_id = $pkey$) + + + bizChangeTicket: PKEY IS ticket_id IN (lnkInfraChangeTicket: infra_id = $pkey$) + + + lnkDocumentRealObject: object_id = $pkey$ + + diff --git a/business/templates/service.html b/business/templates/service.html new file mode 100644 index 000000000..747ab187a --- /dev/null +++ b/business/templates/service.html @@ -0,0 +1,13 @@ + + +$class$: pkey = $pkey$ + + + bizContract: service_id = $pkey$ + + + diff --git a/business/templates/software.html b/business/templates/software.html new file mode 100644 index 000000000..ee51b9651 --- /dev/null +++ b/business/templates/software.html @@ -0,0 +1,13 @@ + + +bizSoftware: pkey = $pkey$ + + + bizApplication: soft_id = $pkey$ + + + diff --git a/business/templates/team.html b/business/templates/team.html new file mode 100644 index 000000000..05e0ec2d2 --- /dev/null +++ b/business/templates/team.html @@ -0,0 +1,21 @@ + + +$class$: pkey = $pkey$ + + + bizContact: PKEY IS object_id IN (lnkContactRealObject: contact_id = $pkey$) + + + bizTeam: PKEY IS object_id IN (lnkContactRealObject: contact_id = $pkey$) + + + logInfra: PKEY IS object_id IN (lnkContactRealObject: contact_id = $pkey$) + + + bizDocument: PKEY IS doc_id IN (lnkDocumentRealObject: object_id = $pkey$) + + diff --git a/business/templates/ticket.html b/business/templates/ticket.html new file mode 100644 index 000000000..1c034cc7b --- /dev/null +++ b/business/templates/ticket.html @@ -0,0 +1,21 @@ + + +$class$: pkey = $pkey$ + + + lnkInfraTicket: ticket_id = $pkey$ + + + lnkRelatedTicket: ticket_id = $pkey$ + + + lnkContactTicket: ticket_id = $pkey$ + + + + +
    diff --git a/business/test_farm.class.inc.php b/business/test_farm.class.inc.php new file mode 100644 index 000000000..5f7b4ecc3 --- /dev/null +++ b/business/test_farm.class.inc.php @@ -0,0 +1,272 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ + +/////////////////////////////////////////////////////////////////////////////// +// Business implementation demo +/////////////////////////////////////////////////////////////////////////////// + +//todo MetaModel::RegisterRelation("Potes", array("description"=>"ceux dont l'email ressemble au mien", "verb_down"=>"est pote de", "verb_up"=>"est pote de")); + + +//todo MetaModel::RegisterZList("list1", array("description"=>"une premiere list, just for fun", "type"=>"attributes")); +//todo MetaModel::RegisterZList("list2", array("description"=>"la secunda e meliora", "type"=>"attributes")); +//todo MetaModel::RegisterZList("list3", array("description"=>"la variante qui tue", "type"=>"filters")); + + +class Animal extends cmdbObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "blah", + "name" => "Animal", + "description" => "An animal", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "", + "state_attcode" => "", + "reconc_keys" => array(""), + "db_table" => "animals", + "db_key_field" => "animalid", + "db_finalclass_field" => "actualclass", + ); + MetaModel::Init_Params($aParams); + + MetaModel::Init_AddAttribute(new AttributeEnum("sex", array("label"=>"sex", "description"=>"sex", "allowed_values"=>new ValueSetEnum('male, female'), "sql"=>"sex", "default_value"=>"male", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("species", array("label"=>"species", "description"=>"species", "allowed_values"=>null, "sql"=>"species", "default_value"=>"xxx", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeInteger("speed", array("label"=>"walk speed", "description"=>"maximum possible speed m.s-1", "allowed_values"=>null, "sql"=>"speed", "default_value"=>4, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("mother", array("label"=>"mother", "description"=>"mother", "allowed_values"=>null, "sql"=>"mother", "targetclass"=>"Animal", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("father", array("label"=>"father", "description"=>"father", "allowed_values"=>null, "sql"=>"father", "targetclass"=>"Animal", "is_null_allowed"=>true, "depends_on"=>array()))); + + //MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("sex"); + MetaModel::Init_AddFilterFromAttribute("species"); + MetaModel::Init_AddFilterFromAttribute("speed"); + MetaModel::Init_AddFilterFromAttribute("mother"); + MetaModel::Init_AddFilterFromAttribute("father"); + } +} + + +class Mammal extends Animal +{ + public static function Init() + { + $aParams = array + ( + "category" => "blah", + "name" => "Mammal", + "description" => "An animal with some characteristics", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "name", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "mammals", + "db_key_field" => "mammalid", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeString("name", array("label"=>"name", "description"=>"name", "allowed_values"=>null, "sql"=>"name", "default_value"=>"xxx", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeInteger("height", array("label"=>"height", "description"=>"size in centimeters", "allowed_values"=>null, "sql"=>"height", "default_value"=>1, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeDate("birth", array("label"=>"birth date", "description"=>"birth date", "allowed_values"=>null, "sql"=>"birth", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("member", array("label"=>"member", "description"=>"leader", "allowed_values"=>null, "sql"=>"member", "targetclass"=>"Group", "is_null_allowed"=>true, "depends_on"=>array()))); + +// ? MetaModel::Init_AddAttribute(new AttributeLinkedSet("a2a", array("label"=>"animal to animal", "description"=>"interanimal relations", "depends_on"=>array(), "linked_class"=>"Animal2animal", "ext_key_to_me"=>"animal1", "count_min"=>0, "count_max"=>10, "allowed_values"=>null))); + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("name"); + MetaModel::Init_AddFilterFromAttribute("height"); + MetaModel::Init_AddFilterFromAttribute("birth"); + MetaModel::Init_AddFilterFromAttribute("member"); + } +} + +class Bird extends Animal +{ + public static function Init() + { + $aParams = array + ( + "category" => "blah", + "name" => "Bird", + "description" => "Un regroupement de gens", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "birds", + "db_key_field" => "birdid", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_OverloadAttributeParams("species", array("allowed_values"=>array('geese', 'rooster', 'chicken', 'turckey', 'pie', 'corbeau'))); + + MetaModel::Init_InheritFilters(); + } +} + +class WalkingBird extends Bird +{ + public static function Init() + { + $aParams = array + ( + "category" => "blah", + "name" => "WalkingBird", + "description" => "A bird which nevers flies", + "key_type" => "", + "key_label" => "", + "name_attcode" => "", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "walkingbirds", + "db_key_field" => "walkingbirdid", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_OverloadAttributeParams("species", array("allowed_values"=>array('geese', 'rooster', 'chicken', 'turckey'))); + MetaModel::Init_InheritFilters(); + } +} + +class FlyingBird extends Bird +{ + public static function Init() + { + $aParams = array + ( + "category" => "blah", + "name" => "FlyingBird", + "description" => "A bird which nevers flies", + "key_type" => "", + "key_label" => "", + "name_attcode" => "", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "flyingbirds", + "db_key_field" => "flyingbirdid", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_OverloadAttributeParams("species", array("allowed_values"=>array('pie', 'corbeau'))); + MetaModel::Init_AddAttribute(new AttributeInteger("flyingspeed", array("label"=>"flying speed", "description"=>"flying at ms.s-1", "allowed_values"=>null, "sql"=>"headcount", "default_value"=>10, "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("flyingspeed"); + } +} + +class AnimalRelation extends cmdbObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "blah", + "name" => "AnimalRelation", + "description" => "Link between two animals", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "a2a", + "db_key_field" => "linkid", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + //MetaModel::Init_InheritAttributes(); + + // What makes it being a link... + MetaModel::Init_AddAttribute(new AttributeExternalKey("animal1", array("label"=>"source", "description"=>"the animal which does ...", "allowed_values"=>null, "sql"=>"a1", "targetclass"=>"Animal", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("animal2", array("label"=>"target", "description"=>"the animal to which something is done...", "allowed_values"=>null, "sql"=>"a2", "targetclass"=>"Animal", "is_null_allowed"=>false, "depends_on"=>array()))); + + //MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("animal1"); + MetaModel::Init_AddFilterFromAttribute("animal2"); + } +} + + +class EaterToEaten extends AnimalRelation +{ + public static function Init() + { + $aParams = array + ( + "category" => "blah", + "name" => "EaterToEaten", + "description" => "Animal 1 eats animal 2", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "eatertoeaten", + "db_key_field" => "eatertoeatonid", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeEnum("DeadOrAlive", array("label"=>"DeadOrAlive", "description"=>"State in which it is ok for the eater to proceed", "allowed_values"=>new ValueSetEnum('dead, fresh, cooked'), "sql"=>"deadoralive", "default_value"=>"fresh", "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("DeadOrAlive"); + } +} + +class Group extends cmdbObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "blah", + "name" => "Group", + "description" => "Group of animals", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "name", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "groups", + "db_key_field" => "groupid", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeString("name", array("label"=>"name", "description"=>"name", "allowed_values"=>null, "sql"=>"name", "default_value"=>"xxx", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("leader", array("label"=>"leader", "description"=>"leader", "allowed_values"=>null, "sql"=>"leader", "targetclass"=>"Mammal", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("leader_name", array("label"=>"Leader Name", "description"=>"Name of the leader (defined on Mammal)", "allowed_values"=>null, "extkey_attcode"=> 'leader', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeExternalField("leader_speed", array("label"=>"Leader Name", "description"=>"Speed of the leader (defined on Animal)", "allowed_values"=>null, "extkey_attcode"=> 'leader', "target_attcode"=>"speed"))); + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("name"); + MetaModel::Init_AddFilterFromAttribute("leader"); + MetaModel::Init_AddFilterFromAttribute("leader_name"); + MetaModel::Init_AddFilterFromAttribute("leader_speed"); + } +} + + +?> diff --git a/config-dist.php b/config-dist.php new file mode 100644 index 000000000..db5babff2 --- /dev/null +++ b/config-dist.php @@ -0,0 +1,38 @@ + 'localhost', + 'db_user' => 'itop', + 'db_pwd' => '1T0p', + 'db_name' => 'itopv06', + 'db_subname' => '', // use it to differentiate two applications instances running on the same DB +); + +// Modules: file names should be specified as a absolute paths + +$MyModules = array( + 'application' => array ( + '../application/menunode.class.inc.php', + '../application/audit.rule.class.inc.php', + // to be continued... + ), + 'business' => array ( + '../business/itop.business.class.inc.php' + // to be continued... + ), + 'addons' => array ( + 'user rights' => '../addons/userrights/userrightsmatrix.class.inc.php', + // other modules to come later + ) +); + + +?> diff --git a/config-test-farm.php b/config-test-farm.php new file mode 100644 index 000000000..3d1ab9381 --- /dev/null +++ b/config-test-farm.php @@ -0,0 +1,35 @@ + 'localhost', + 'db_user' => 'RomainDBLogin', + 'db_pwd' => '', + 'db_name' => 'TestFarm', + 'db_subname' => '', // use it to differentiate two applications instances running on the same DB +); + +// Modules: file names should be specified as a absolute paths + +$MyModules = array( + 'application' => array ( + // to be continued... + ), + 'business' => array ( + '../business/test_farm.class.inc.php', + // to be continued... + ), + 'addons' => array ( + //'user rights' => '/addons/userrights/userrightsnull.class.inc.php', // or userrightsmatrix.class.inc.php + // other modules to come later + ) +); + +?> diff --git a/config-test-mymodel.php b/config-test-mymodel.php new file mode 100644 index 000000000..e3176169d --- /dev/null +++ b/config-test-mymodel.php @@ -0,0 +1,36 @@ + 'localhost', + 'db_user' => 'itop', + 'db_pwd' => '1T0p', + 'db_name' => 'TestBizModelGenericItop', + 'db_subname' => 'tribute2itop', // use it to differentiate two applications instances running on the same DB +); + +// Modules: file names should be specified as a absolute paths + +$MyModules = array( + 'application' => array ( + // to be continued... + ), + 'business' => array ( + '../business/business_test.class.inc.php' + // to be continued... + ), + 'addons' => array ( + // other modules to come later + ) +); + + +?> diff --git a/core/MyHelpers.class.inc.php b/core/MyHelpers.class.inc.php new file mode 100644 index 000000000..8ee89a5ff --- /dev/null +++ b/core/MyHelpers.class.inc.php @@ -0,0 +1,491 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ + +class MyHelpers +{ + public static function CheckValueInArray($sDescription, $value, $aData) + { + if (!in_array($value, $aData)) + { + self::HandleWrongValue($sDescription, $value, $aData); + } + } + + public static function CheckKeyInArray($sDescription, $key, $aData) + { + if (!array_key_exists($key, $aData)) + { + self::HandleWrongValue($sDescription, $key, array_keys($aData)); + } + } + + public static function HandleWrongValue($sDescription, $value, $aData) + { + if (count($aData) == 0) + { + $sArrayDesc = "{}"; + } + else + { + $sArrayDesc = "{".implode(", ", $aData)."}"; + } + // exit! + throw new CoreException("Wrong value for $sDescription, found '$value' while expecting a value in $sArrayDesc"); + } + + // getmicrotime() + // format sss.mmmuuupppnnn + public static function getmicrotime() + { + list($usec, $sec) = explode(" ",microtime()); + return ((float)$usec + (float)$sec); + } + + /* + * MakeSQLComment + * converts hash into text comment which we can use in a (mySQL) query + */ + public static function MakeSQLComment ($aHash) + { + if (empty($aHash)) return ""; + $sComment = ""; + { + foreach($aHash as $sKey=>$sValue) + { + $sComment .= "\n-- ". $sKey ."=>" . $sValue; + } + } + return $sComment; + } + + public static function var_dump_html($aWords, $bFullDisplay = false) + { + echo "
    \n";
    +		if ($bFullDisplay)
    +		{
    +			print_r($aWords); // full dump!
    +		}
    +		else
    +		{
    +			var_dump($aWords); // truncate things when they are too big
    +		}
    +		echo "\n
    \n"; + } + + public static function arg_dump_html() + { + echo "
    \n";
    +		echo "GET:\n";
    +		var_dump($_GET);
    +		echo "POST:\n";
    +		var_dump($_POST);
    +		echo "\n
    \n"; + } + + public static function var_dump_string($var) + { + ob_start(); + print_r($var); + $sRet = ob_get_clean(); + return $sRet; + } + + protected static function first_diff_line($s1, $s2) + { + $aLines1 = explode("\n", $s1); + $aLines2 = explode("\n", $s2); + for ($i = 0 ; $i < min(count($aLines1), count($aLines2)) ; $i++) + { + if ($aLines1[$i] != $aLines2[$i]) return $i; + } + return false; + } + + protected static function highlight_line($sMultiline, $iLine, $sHighlightStart = '', $sHightlightEnd = '') + { + $aLines = explode("\n", $sMultiline); + $aLines[$iLine] = $sHighlightStart.$aLines[$iLine].$sHightlightEnd; + return implode("\n", $aLines); + } + + protected static function first_diff($s1, $s2) + { + // do not work fine with multiline strings + $iLen1 = strlen($s1); + $iLen2 = strlen($s2); + for ($i = 0 ; $i < min($iLen1, $iLen2) ; $i++) + { + if ($s1[$i] !== $s2[$i]) return $i; + } + return false; + } + + protected static function last_diff($s1, $s2) + { + // do not work fine with multiline strings + $iLen1 = strlen($s1); + $iLen2 = strlen($s2); + for ($i = 0 ; $i < min(strlen($s1), strlen($s2)) ; $i++) + { + if ($s1[$iLen1 - $i - 1] !== $s2[$iLen2 - $i - 1]) return array($iLen1 - $i, $iLen2 - $i); + } + return false; + } + + protected static function text_cmp_html($sText1, $sText2, $sHighlight) + { + $iDiffPos = self::first_diff_line($sText1, $sText2); + $sDisp1 = self::highlight_line($sText1, $iDiffPos, '
    ', '
    '); + $sDisp2 = self::highlight_line($sText2, $iDiffPos, '
    ', '
    '); + echo "\n"; + echo "\n"; + echo "\n"; + echo "\n"; + echo "\n"; + echo "
    $sDisp1
    $sDisp2
    \n"; + } + + protected static function string_cmp_html($s1, $s2, $sHighlight) + { + $iDiffPos = self::first_diff($s1, $s2); + if ($iDiffPos === false) + { + echo "strings are identical"; + return; + } + $sStart = substr($s1, 0, $iDiffPos); + + $aLastDiff = self::last_diff($s1, $s2); + $sEnd = substr($s1, $aLastDiff[0]); + + $sMiddle1 = substr($s1, $iDiffPos, $aLastDiff[0] - $iDiffPos); + $sMiddle2 = substr($s2, $iDiffPos, $aLastDiff[1] - $iDiffPos); + + echo "

    $sStart$sMiddle1$sEnd

    \n"; + echo "

    $sStart$sMiddle2$sEnd

    \n"; + } + + protected static function object_cmp_html($oObj1, $oObj2, $sHighlight) + { + $sObj1 = self::var_dump_string($oObj1); + $sObj2 = self::var_dump_string($oObj2); + return self::text_cmp_html($sObj1, $sObj2, $sHighlight); + } + + public static function var_cmp_html($var1, $var2, $sHighlight = 'color:red; font-weight:bold;') + { + if (is_object($var1)) + { + return self::object_cmp_html($var1, $var2, $sHighlight); + } + else if (count(explode("\n", $var1)) > 1) + { + // multiline string + return self::text_cmp_html($var1, $var2, $sHighlight); + } + else + { + return self::string_cmp_html($var1, $var2, $sHighlight); + } + } + + public static function get_callstack_html($iLevelsToIgnore = 0, $aCallStack = null) + { + if ($aCallStack == null) $aCallStack = debug_backtrace(); + + $aCallStack = array_slice($aCallStack, $iLevelsToIgnore); + + $aDigestCallStack = array(); + $bFirstLine = true; + foreach ($aCallStack as $aCallInfo) + { + $sLine = empty($aCallInfo['line']) ? "" : $aCallInfo['line']; + $sFile = empty($aCallInfo['file']) ? "" : $aCallInfo['file']; + $sClass = empty($aCallInfo['class']) ? "" : $aCallInfo['class']; + $sType = empty($aCallInfo['type']) ? "" : $aCallInfo['type']; + $sFunction = empty($aCallInfo['function']) ? "" : $aCallInfo['function']; + + if ($bFirstLine) + { + $bFirstLine = false; + // For this line do not display the "function name" because + // that will be the name of our error handler for sure ! + $sFunctionInfo = "N/A"; + } + else + { + $args = ''; + if (empty($aCallInfo['args'])) $aCallInfo['args'] = array(); + foreach ($aCallInfo['args'] as $a) + { + if (!empty($args)) + { + $args .= ', '; + } + switch (gettype($a)) + { + case 'integer': + case 'double': + $args .= $a; + break; + case 'string': + $a = Str::pure2html(self::beautifulstr($a, 1024, true, true)); + $args .= "\"$a\""; + break; + case 'array': + $args .= 'Array('.count($a).')'; + break; + case 'object': + $args .= 'Object('.get_class($a).')'; + break; + case 'resource': + $args .= 'Resource('.strstr($a, '#').')'; + break; + case 'boolean': + $args .= $a ? 'True' : 'False'; + break; + case 'NULL': + $args .= 'Null'; + break; + default: + $args .= 'Unknown'; + } + } + $sFunctionInfo = "$sClass $sType $sFunction($args)"; + } + $aDigestCallStack[] = array('File'=>$sFile, 'Line'=>$sLine, 'Function'=>$sFunctionInfo); + } + return self::make_table_from_assoc_array($aDigestCallStack); + } + + public static function dump_callstack($iLevelsToIgnore = 0, $aCallStack = null) + { + return self::get_callstack_html($iLevelsToIgnore, $aCallStack); + } + + /////////////////////////////////////////////////////////////////////////////// + // Source: New + // Last modif: 2004/12/20 RQU + /////////////////////////////////////////////////////////////////////////////// + public static function make_table_from_assoc_array(&$aData) + { + if (!is_array($aData)) throw new CoreException("make_table_from_assoc_array: Error - the passed argument is not an array"); + $aFirstRow = reset($aData); + if (count($aData) == 0) return ''; + if (!is_array($aFirstRow)) throw new CoreException("make_table_from_assoc_array: Error - the passed argument is not a bi-dimensional array"); + $sOutput = ""; + $sOutput .= "\n"; + + // Table header + // + $sOutput .= " \n"; + foreach ($aFirstRow as $fieldname=>$trash) { + $sOutput .= " \n"; + } + $sOutput .= " \n"; + + // Table contents + // + $iCount = 0; + foreach ($aData as $aRow) { + $sStyle = ($iCount++ % 2 ? "STYLE=\"background-color : #eeeeee\"" : ""); + $sOutput .= " \n"; + foreach ($aRow as $data) { + if (strlen($data) == 0) { + $data = " "; + } + $sOutput .= " \n"; + } + $sOutput .= " \n"; + } + + $sOutput .= "
    ".$fieldname."
    ".$data."
    \n"; + return $sOutput; + } + + public static function debug_breakpoint($arg) + { + echo "

    Debug breakpoint

    \n"; + MyHelpers::var_dump_html($arg); + MyHelpers::dump_callstack(); + exit; + } + public static function debug_breakpoint_notempty($arg) + { + if (empty($arg)) return; + echo "

    Debug breakpoint (triggered on non-empty value)

    \n"; + MyHelpers::var_dump_html($arg); + MyHelpers::dump_callstack(); + exit; + } + + /** + * utf8... converts non ASCII chars into '?' + * Decided after some complex investigations, to have the tools work fine (Oracle+Perl vs mySQL+PHP...) + */ + public static function utf8($strText) + { + return iconv("WINDOWS-1252", "ASCII//TRANSLIT", $strText); + } + + /** + * xmlentities() + * ... same as htmlentities, but designed for xml ! + */ + public static function xmlentities($string) + { + return str_replace( array( '&', '"', "'", '<', '>' ), array ( '&' , '"', ''' , '<' , '>' ), $string ); + } + + /** + * xmlencode() + * Encodes a string so that for sure it can be output as an xml data string + */ + public static function xmlencode($string) + { + return xmlentities(iconv("UTF-8", "UTF-8//IGNORE",$string)); + } + + /////////////////////////////////////////////////////////////////////////////// + // Source: New - format strings for output + // Last modif: 2005/01/18 RQU + /////////////////////////////////////////////////////////////////////////////// + public static function beautifulstr($sLongString, $iMaxLen, $bShowLen=false, $bShowTooltip=true) + { + if (!is_string($sLongString)) throw new CoreException("beautifulstr: expect a string as 1st argument"); + + // Nothing to do if the string is short + if (strlen($sLongString) <= $iMaxLen) return $sLongString; + + // Truncate the string + $sSuffix = "..."; + if ($bShowLen) { + $sSuffix .= "(".strlen($sLongString)." chars)..."; + } + $sOutput = substr($sLongString, 0, $iMaxLen - strlen($sSuffix)).$sSuffix; + $sOutput = htmlspecialchars($sOutput); + + // Add tooltip if required + //if ($bShowTooltip) { + // $oTooltip = new gui_tooltip($sLongString); + // $sOutput = "get_mouseOver_code().">".$sOutput.""; + //} + return $sOutput; + } +} + +/** +Utility class: static methods for cleaning & escaping untrusted (i.e. +user-supplied) strings. +Any string can (usually) be thought of as being in one of these 'modes': +pure = what the user actually typed / what you want to see on the page / + what is actually stored in the DB +gpc = incoming GET, POST or COOKIE data +sql = escaped for passing safely to RDBMS via SQL (also, data from DB + queries and file reads if you have magic_quotes_runtime on--which + is rare) +html = safe for html display (htmlentities applied) +Always knowing what mode your string is in--using these methods to +convert between modes--will prevent SQL injection and cross-site scripting. +This class refers to its own namespace (so it can work in PHP 4--there is no +self keyword until PHP 5). Do not change the name of the class w/o changing +all the internal references. +Example usage: a POST value that you want to query with: +$username = Str::gpc2sql($_POST['username']); +*/ +//This sets SQL escaping to use slashes; for Sybase(/MSSQL)-style escaping +// ( ' --> '' ), set to true. +define('STR_SYBASE', false); +class Str +{ + public static function gpc2sql($gpc, $maxLength = false) + { + return self::pure2sql(self::gpc2pure($gpc), $maxLength); + } + public static function gpc2html($gpc, $maxLength = false) + { + return self::pure2html(self::gpc2pure($gpc), $maxLength); + } + public static function gpc2pure($gpc) + { + if (ini_get('magic_quotes_sybase')) $pure = str_replace("''", "'", $gpc); + else $pure = get_magic_quotes_gpc() ? stripslashes($gpc) : $gpc; + return $pure; + } + public static function html2pure($html) + { + return html_entity_decode($html); + } + public static function html2sql($html, $maxLength = false) + { + return self::pure2sql(self::html2pure($html), $maxLength); + } + public static function pure2html($pure, $maxLength = false) + { + return $maxLength + ? htmlentities(substr($pure, 0, $maxLength)) + : htmlentities($pure); + } + public static function pure2sql($pure, $maxLength = false) + { + if ($maxLength) $pure = substr($pure, 0, $maxLength); + return (STR_SYBASE) + ? str_replace("'", "''", $pure) + : addslashes($pure); + } + public static function sql2html($sql, $maxLength = false) + { + $pure = self::sql2pure($sql); + if ($maxLength) $pure = substr($pure, 0, $maxLength); + return self::pure2html($pure); + } + public static function sql2pure($sql) + { + return (STR_SYBASE) + ? str_replace("''", "'", $sql) + : stripslashes($sql); + } + + public static function xml2pure($xml) + { + // #@# - not implemented + return $xml; + } + public static function pure2xml($pure) + { + return self::xmlencode($pure); + } + + protected static function xmlentities($string) + { + return str_replace( array( '&', '"', "'", '<', '>' ), array ( '&' , '"', ''' , '<' , '>' ), $string ); + } + + /** + * xmlencode() + * Encodes a string so that for sure it can be output as an xml data string + */ + protected static function xmlencode($string) + { + return self::xmlentities(iconv("ISO-8859-1", "UTF-8//IGNORE",$string)); + } + + public static function islowcase($sString) + { + return (strtolower($sString) == $sString); + } +} + +?> diff --git a/core/archive.class.inc.php b/core/archive.class.inc.php new file mode 100644 index 000000000..6403fffca --- /dev/null +++ b/core/archive.class.inc.php @@ -0,0 +1,323 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ + +/** + * iTopArchive a class to manipulate (read/write) iTop archives with their catalog + * Each iTop archive is a zip file that contains (at the root of the archive) + * a file called catalog.xml holding the description of the archive + */ +class iTopArchive +{ + const read = 0; + const create = ZipArchive::CREATE; + + protected $m_sZipPath; + protected $m_oZip; + protected $m_sVersion; + protected $m_sTitle; + protected $m_sDescription; + protected $m_aPackages; + protected $m_aErrorMessages; + + /** + * Construct an iTopArchive object + * @param $sArchivePath string The full path the archive file + * @param $iMode integrer Either iTopArchive::read for reading an existing archive or iTopArchive::create for creating a new one. Updating is not supported (yet) + */ + public function __construct($sArchivePath, $iMode = iTopArchive::read) + { + $this->m_sZipPath = $sArchivePath; + $this->m_oZip = new ZipArchive(); + $this->m_oZip->open($this->m_sZipPath, $iMode); + $this->m_aErrorMessages = array(); + $this->m_sVersion = '1.0'; + $this->m_sTitle = ''; + $this->m_sDescription = ''; + $this->m_aPackages = array(); + } + + public function SetTitle($sTitle) + { + $this->m_sTitle = $sTitle; + } + + public function SetDescription($sDescription) + { + $this->m_sDescription = $sDescription; + } + + public function GetTitle() + { + return $this->m_sTitle; + } + + public function GetDescription() + { + return $this->m_sDescription; + } + + public function GetPackages() + { + return $this->m_aPackages; + } + + public function __destruct() + { + $this->m_oZip->close(); + } + + /** + * Get the error message explaining the latest error encountered + * @return array All the error messages encountered during the validation + */ + public function GetErrors() + { + return $this->m_aErrorMessages; + } + + /** + * Read the catalog from the archive (zip) file + * @param sPath string Path the the zip file + * @return boolean True in case of success, false otherwise + */ + public function ReadCatalog() + { + if ($this->IsValid()) + { + $sXmlCatalog = $this->m_oZip->getFromName('catalog.xml'); + $oParser = xml_parser_create(); + xml_parse_into_struct($oParser, $sXmlCatalog, $aValues, $aIndexes); + xml_parser_free($oParser); + + $iIndex = $aIndexes['ARCHIVE'][0]; + $this->m_sVersion = $aValues[$iIndex]['attributes']['VERSION']; + $iIndex = $aIndexes['TITLE'][0]; + $this->m_sTitle = $aValues[$iIndex]['value']; + $iIndex = $aIndexes['DESCRIPTION'][0]; + if (array_key_exists('value', $aValues[$iIndex])) + { + // #@# implement a get_array_value(array, key, default) ? + $this->m_sDescription = $aValues[$iIndex]['value']; + } + + foreach($aIndexes['PACKAGE'] as $iIndex) + { + $this->m_aPackages[$aValues[$iIndex]['attributes']['HREF']] = array( 'type' => $aValues[$iIndex]['attributes']['TYPE'], 'title'=> $aValues[$iIndex]['attributes']['TITLE'], 'description' => $aValues[$iIndex]['value']); + } + + //echo "Archive path: {$this->m_sZipPath}
    \n"; + //echo "Archive format version: {$this->m_sVersion}
    \n"; + //echo "Title: {$this->m_sTitle}
    \n"; + //echo "Description: {$this->m_sDescription}
    \n"; + //foreach($this->m_aPackages as $aFile) + //{ + // echo "{$aFile['title']} ({$aFile['type']}): {$aFile['description']}
    \n"; + //} + } + return true; + } + + public function WriteCatalog() + { + $sXml = "\n"; // split the XML closing tag that disturbs PSPad's syntax coloring + $sXml .= "\n"; + $sXml .= "{$this->m_sTitle}\n"; + $sXml .= "{$this->m_sDescription}\n"; + foreach($this->m_aPackages as $sFileName => $aFile) + { + $sXml .= "{$aFile['description']}\n"; + } + $sXml .= ""; + $this->m_oZip->addFromString('catalog.xml', $sXml); + } + + /** + * Add a package to the archive + * @param string $sExternalFilePath The path to the file to be added to the archive as a package (directories are not yet implemented) + * @param string $sFilePath The name of the file inside the archive + * @param string $sTitle A short title for this package + * @param string $sType Type of the package. SQL scripts must be of type 'text/sql' + * @param string $sDescription A longer description of the purpose of this package + * @return none + */ + public function AddPackage($sExternalFilePath, $sFilePath, $sTitle, $sType, $sDescription) + { + $this->m_aPackages[$sFilePath] = array('title' => $sTitle, 'type' => $sType, 'description' => $sDescription); + $this->m_oZip->addFile($sExternalFilePath, $sFilePath); + } + + /** + * Reads the contents of the given file from the archive + * @param string $sFileName The path to the file inside the archive + * @return string The content of the file read from the archive + */ + public function GetFileContents($sFileName) + { + return $this->m_oZip->getFromName($sFileName); + } + + /** + * Extracts the contents of the given file from the archive + * @param string $sFileName The path to the file inside the archive + * @param string $sDestinationFileName The path of the file to write + * @return none + */ + public function ExtractToFile($sFileName, $sDestinationFileName) + { + $iBufferSize = 64 * 1024; // Read 64K at a time + $oZipStream = $this->m_oZip->getStream($sFileName); + $oDestinationStream = fopen($sDestinationFileName, 'wb'); + while (!feof($oZipStream)) { + $sContents = fread($oZipStream, $iBufferSize); + fwrite($oDestinationStream, $sContents); + } + fclose($oZipStream); + fclose($oDestinationStream); + } + + /** + * Apply a SQL script taken from the archive. The package must be listed in the catalog and of type text/sql + * @param string $sFileName The path to the SQL package inside the archive + * @return boolean false in case of error, true otherwise + */ + public function ImportSql($sFileName, $sDatabase = 'itop') + { + if ( ($this->m_oZip->locateName($sFileName) == false) || (!isset($this->m_aPackages[$sFileName])) || ($this->m_aPackages[$sFileName]['type'] != 'text/sql')) + { + // invalid type or not listed in the catalog + return false; + } + $sTempName = tempnam("../tmp/", "sql"); + //echo "Extracting to: '$sTempName'
    \n"; + $this->ExtractToFile($sFileName, $sTempName); + // Note: the command line below works on Windows with the right path to mysql !!! + $sCommandLine = 'type "'.$sTempName.'" | "/iTop/MySQL Server 5.0/bin/mysql.exe" -u root '.$sDatabase; + //echo "Executing: '$sCommandLine'
    \n"; + exec($sCommandLine, $aOutput, $iRet); + //echo "Return code: $iRet
    \n"; + //echo "Output:
    \n";
    +		//print_r($aOutput);
    +		//echo "

    \n"; + unlink($sTempName); + return ($iRet == 0); + } + + /** + * Dumps some part of the specified MySQL database into the archive as a text/sql package + * @param $sTitle string A short title for this SQL script + * @param $sDescription string A longer description of the purpose of this SQL script + * @param $sFileName string The name of the package inside the archive + * @param $sDatabase string name of the database + * @param $aTables array array or table names. If empty, all tables are dumped + * @param $bStructureOnly boolean Whether or not to dump the data or just the schema + * @return boolean False in case of error, true otherwise + */ + public function AddDatabaseDump($sTitle, $sDescription, $sFileName, $sDatabase = 'itop', $aTables = array(), $bStructureOnly = true) + { + $sTempName = tempnam("../tmp/", "sql"); + $sNoData = $bStructureOnly ? "--no-data" : ""; + $sCommandLine = "\"/iTop/MySQL Server 5.0/bin/mysqldump.exe\" --user=root --opt $sNoData --result-file=$sTempName $sDatabase ".implode(" ", $aTables); + //echo "Executing command: '$sCommandLine'
    \n"; + exec($sCommandLine, $aOutput, $iRet); + //echo "Return code: $iRet
    \n"; + //echo "Output:
    \n";
    +		//print_r($aOutput);
    +		//echo "

    \n"; + if ($iRet == 0) + { + $this->AddPackage($sTempName, $sFileName, $sTitle, 'text/sql', $sDescription); + } + //unlink($sTempName); + return ($iRet == 0); + } + + /** + * Check the consistency of the archive + * @return boolean True if the archive file is consistent + */ + public function IsValid() + { + // TO DO: use a DTD to validate the XML instead of this hand-made validation + $bResult = true; + $aMandatoryTags = array('ARCHIVE' => array('VERSION'), + 'TITLE' => array(), + 'DESCRIPTION' => array(), + 'PACKAGE' => array('TYPE', 'HREF', 'TITLE')); + + $sXmlCatalog = $this->m_oZip->getFromName('catalog.xml'); + $oParser = xml_parser_create(); + xml_parse_into_struct($oParser, $sXmlCatalog, $aValues, $aIndexes); + xml_parser_free($oParser); + + foreach($aMandatoryTags as $sTag => $aAttributes) + { + // Check that all the required tags are present + if (!isset($aIndexes[$sTag])) + { + $this->m_aErrorMessages[] = "The XML catalog does not contain the mandatory tag $sTag."; + $bResult = false; + } + else + { + foreach($aIndexes[$sTag] as $iIndex) + { + switch($aValues[$iIndex]['type']) + { + case 'complete': + case 'open': + // Check that all the required attributes are present + foreach($aAttributes as $sAttribute) + { + if (!isset($aValues[$iIndex]['attributes'][$sAttribute])) + { + $this->m_aErrorMessages[] = "The tag $sTag ($iIndex) does not contain the required attribute $sAttribute."; + } + } + break; + + default: + // ignore other type of tags: close or cdata + } + } + } + } + return $bResult; + } +} +/* +// Unit test - reading an archive +$sArchivePath = '../tmp/archive.zip'; +$oArchive = new iTopArchive($sArchivePath, iTopArchive::read); +$oArchive->ReadCatalog(); +$oArchive->ImportSql('full_backup.sql'); + +// Writing an archive -- + +$sArchivePath = '../tmp/archive2.zip'; +$oArchive = new iTopArchive($sArchivePath, iTopArchive::create); +$oArchive->SetTitle('First Archive !'); +$oArchive->SetDescription('This is just a test. Does not contain a lot of useful data.'); +$oArchive->AddPackage('../tmp/schema.sql', 'test.sql', 'this is just a test', 'text/sql', 'My first attempt at creating an archive from PHP...'); +$oArchive->WriteCatalog(); + + +$sArchivePath = '../tmp/archive2.zip'; +$oArchive = new iTopArchive($sArchivePath, iTopArchive::create); +$oArchive->SetTitle('First Archive !'); +$oArchive->SetDescription('This is just a test. Does not contain a lot of useful data.'); +$oArchive->AddDatabaseDump('Test', 'This is my first automatic dump', 'schema.sql', 'itop', array('objects')); +$oArchive->WriteCatalog(); +*/ +?> diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php new file mode 100644 index 000000000..d1af378a4 --- /dev/null +++ b/core/attributedef.class.inc.php @@ -0,0 +1,989 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ +abstract class AttributeDefinition +{ + abstract public function GetType(); + abstract public function GetTypeDesc(); + abstract public function GetEditClass(); + abstract public function GetDBFieldType(); + + protected $m_sCode; + private $m_aParams = array(); + private $m_sHostClass = array(); + protected function Get($sParamName) {return $this->m_aParams[$sParamName];} + + public function __construct($sCode, $aParams) + { + $this->m_sCode = $sCode; + $this->m_aParams = $aParams; + $this->ConsistencyCheck(); + } + public function OverloadParams($aParams) + { + foreach ($aParams as $sParam => $value) + { + if (!array_key_exists($sParam, $this->m_aParams)) + { + throw new CoreException("Unknown attribute definition parameter '$sParam', please select a value in {".implode(", ", $this->m_aParams)."}"); + } + else + { + $this->m_aParams[$sParam] = $value; + } + } + } + public function SetHostClass($sHostClass) + { + $this->m_sHostClass = $sHostClass; + } + public function GetHostClass() + { + return $this->m_sHostClass; + } + + // Note: I could factorize this code with the parameter management made for the AttributeDef class + // to be overloaded + static protected function ListExpectedParams() + { + return array("label", "description", "allowed_values"); + } + + private function ConsistencyCheck() + { + + // Check that any mandatory param has been specified + // + $aExpectedParams = $this->ListExpectedParams(); + foreach($aExpectedParams as $sParamName) + { + if (!array_key_exists($sParamName, $this->m_aParams)) + { + $aBacktrace = debug_backtrace(); + $sTargetClass = $aBacktrace[2]["class"]; + $sCodeInfo = $aBacktrace[1]["file"]." - ".$aBacktrace[1]["line"]; + throw new Exception("ERROR missing parameter '$sParamName' in ".get_class($this)." declaration for class $sTargetClass ($sCodeInfo)"); + } + } + } + + // table, key field, name field + public function ListDBJoins() + { + return ""; + // e.g: return array("Site", "infrid", "name"); + } + public function IsDirectField() {return false;} + public function IsScalar() {return false;} + public function IsLinkSet() {return false;} + public function IsExternalKey($iType = EXTKEY_RELATIVE) {return false;} + public function IsExternalField() {return false;} + public function IsWritable() {return false;} + public function GetCode() {return $this->m_sCode;} + public function GetLabel() {return $this->Get("label");} + public function GetDescription() {return $this->Get("description");} + public function GetValuesDef() {return $this->Get("allowed_values");} + public function GetPrerequisiteAttributes() {return $this->Get("depends_on");} + //public function IsSearchableStd() {return $this->Get("search_std");} + //public function IsSearchableGlobal() {return $this->Get("search_global");} + //public function IsMandatory() {return $this->Get("is_mandatory");} + //public function GetMinVal() {return $this->Get("min");} + //public function GetMaxVal() {return $this->Get("max");} + //public function GetSize() {return $this->Get("size");} + //public function GetCheckRegExp() {return $this->Get("regexp");} + //public function GetCheckFunc() {return $this->Get("checkfunc");} + + // Definition: real value is what will be stored in memory and maintained by MetaModel + // DBObject::Set() relies on MakeRealValue() + // MetaModel::MakeQuery() relies on RealValueToSQLValue() + // DBObject::FromRow() relies on SQLToRealValue() + public function MakeRealValue($proposedValue) {return $proposedValue;} // force an allowed value (type conversion and possibly forces a value as mySQL would do upon writing!) + public function RealValueToSQLValue($value) {return $value;} // format value as a valuable SQL literal (quoted outside) + public function SQLValueToRealValue($value) {return $value;} // take the result of a fetch... and make it a PHP variable + + public function GetJSCheckFunc() + { + $sRegExp = $this->Get("regexp"); + if (empty($sRegExp)) return 'return true;'; + + return "return regexp('$sRegExp', myvalue);"; + } + public function CheckValue($value) + { + $sRegExp = $this->Get("regexp"); + if (empty($sRegExp)) return true; + + return preg_match(preg_escape($this->Get("regexp")), $value); + } + + public function MakeValue() + { + $sComputeFunc = $this->Get("compute_func"); + if (empty($sComputeFunc)) return null; + + return call_user_func($sComputeFunc); + } + + abstract public function DBGetUsedFields(); + abstract public function GetDefaultValue(); + + // + // To be overloaded in subclasses + // + + abstract public function GetBasicFilterOperators(); // returns an array of "opCode"=>"description" + abstract public function GetBasicFilterLooseOperator(); // returns an "opCode" + //abstract protected GetBasicFilterHTMLInput(); + abstract public function GetBasicFilterSQLExpr($sOpCode, $value); + + public function GetAsHTML($sValue) + { + return Str::pure2html($sValue); + } + + public function GetAsXML($sValue) + { + return Str::pure2xml($sValue); + } + + public function GetAsCSV($sValue, $sSeparator = ';', $sSepEscape = ',') + { + return str_replace($sSeparator, $sSepEscape, $sValue); + } +} + +/** + * Set of objects directly linked to an object, and being part of its definition + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class AttributeLinkedSet extends AttributeDefinition +{ + static protected function ListExpectedParams() + { + return array_merge(parent::ListExpectedParams(), array("depends_on", "linked_class", "ext_key_to_me", "count_min", "count_max")); + } + + public function GetType() {return "Array of objects";} + public function GetTypeDesc() {return "Any kind of objects [subclass] of the same class";} + public function GetEditClass() {return "List";} + public function GetDBFieldType() {return "N/A";} // should be moved out of the AttributeDef root class + + public function IsWritable() {return true;} + public function IsLinkSet() {return true;} + + public function GetDefaultValue() {return DBObjectSet::FromScratch($this->Get('linked_class'));} + + public function GetLinkedClass() {return $this->Get('linked_class');} + public function GetExtKeyToMe() {return $this->Get('ext_key_to_me');} + + public function DBGetUsedFields() {return array();} + public function GetBasicFilterOperators() {return array();} + public function GetBasicFilterLooseOperator() {return '';} + public function GetBasicFilterSQLExpr($sOpCode, $value) {return '';} + + public function GetAsHTML($sValue) + { + return "ERROR: LIST OF OBJECTS"; + } + + public function GetAsXML($sValue) + { + return "ERROR: LIST OF OBJECTS"; + } + + public function GetAsCSV($sValue, $sSeparator = ';', $sSepEscape = ',') + { + return "ERROR: LIST OF OBJECTS"; + } +} + +/** + * Set of objects linked to an object (n-n), and being part of its definition + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class AttributeLinkedSetIndirect extends AttributeLinkedSet +{ + static protected function ListExpectedParams() + { + return array_merge(parent::ListExpectedParams(), array("ext_key_to_remote")); + } + public function GetExtKeyToRemote() { return $this->Get('ext_key_to_remote'); } +} + +/** + * Abstract class implementing default filters for a DB column + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class AttributeDBFieldVoid extends AttributeDefinition +{ + static protected function ListExpectedParams() + { + return array_merge(parent::ListExpectedParams(), array("depends_on", "sql")); + } + + public function GetType() {return "Void";} + public function GetTypeDesc() {return "Any kind of value, from the DB";} + public function GetEditClass() {return "String";} + public function GetDBFieldType() {return "VARCHAR(255)";} + + public function IsDirectField() {return true;} + public function IsScalar() {return true;} + public function IsWritable() {return true;} + public function GetSQLExpr() {return $this->Get("sql");} + public function GetDefaultValue() {return "";} + public function IsNullAllowed() {return false;} + public function DBGetUsedFields() + { + // #@# bugge a mort... a suivre... + return array($this->Get("sql")); + } + + public function GetBasicFilterOperators() + { + return array("="=>"equals", "!="=>"differs from"); + } + public function GetBasicFilterLooseOperator() + { + return "="; + } + + public function GetBasicFilterSQLExpr($sOpCode, $value) + { + $sQValue = CMDBSource::Quote($value); + switch ($sOpCode) + { + case '!=': + return $this->GetSQLExpr()." != $sQValue"; + break; + case '=': + default: + return $this->GetSQLExpr()." = $sQValue"; + } + } +} + +/** + * Base class for all kind of DB attributes, with the exception of external keys + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class AttributeDBField extends AttributeDBFieldVoid +{ + static protected function ListExpectedParams() + { + return array_merge(parent::ListExpectedParams(), array("default_value", "is_null_allowed")); + } + public function GetDefaultValue() {return $this->Get("default_value");} + public function IsNullAllowed() {return strtolower($this->Get("is_null_allowed"));} +} + +/** + * Map an integer column to an attribute + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class AttributeInteger extends AttributeDBField +{ + static protected function ListExpectedParams() + { + return parent::ListExpectedParams(); + //return array_merge(parent::ListExpectedParams(), array()); + } + + public function GetType() {return "Integer";} + public function GetTypeDesc() {return "Numeric value (could be negative)";} + public function GetEditClass() {return "String";} + public function GetDBFieldType() {return "INT";} + + public function GetBasicFilterOperators() + { + return array( + "!="=>"differs from", + "="=>"equals", + ">"=>"greater (strict) than", + ">="=>"greater than", + "<"=>"less (strict) than", + "<="=>"less than", + "in"=>"in" + ); + } + public function GetBasicFilterLooseOperator() + { + // Unless we implement an "equals approximately..." or "same order of magnitude" + return "="; + } + + public function GetBasicFilterSQLExpr($sOpCode, $value) + { + $sQValue = CMDBSource::Quote($value); + switch ($sOpCode) + { + case '!=': + return $this->GetSQLExpr()." != $sQValue"; + break; + case '>': + return $this->GetSQLExpr()." > $sQValue"; + break; + case '>=': + return $this->GetSQLExpr()." >= $sQValue"; + break; + case '<': + return $this->GetSQLExpr()." < $sQValue"; + break; + case '<=': + return $this->GetSQLExpr()." <= $sQValue"; + break; + case 'in': + if (!is_array($value)) throw new CoreException("Expected an array for argument value (sOpCode='$sOpCode')"); + return $this->GetSQLExpr()." IN ('".implode("', '", $value)."')"; + break; + + case '=': + default: + return $this->GetSQLExpr()." = \"$value\""; + } + } + + public function MakeRealValue($proposedValue) + { + //return intval($proposedValue); could work as well + return (int)$proposedValue; + } + public function RealValueToSQLValue($value) + { + assert(is_numeric($value)); + return $value; // supposed to be an int + } + public function SQLValueToRealValue($value) + { + // Use cast (int) or intval() ? + return (int)$value; + + } +} + +/** + * Map a varchar column (size < ?) to an attribute + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class AttributeString extends AttributeDBField +{ + static protected function ListExpectedParams() + { + return parent::ListExpectedParams(); + //return array_merge(parent::ListExpectedParams(), array()); + } + + public function GetType() {return "String";} + public function GetTypeDesc() {return "Alphanumeric string";} + public function GetEditClass() {return "String";} + public function GetDBFieldType() {return "VARCHAR(255)";} + + public function GetBasicFilterOperators() + { + return array( + "="=>"equals", + "!="=>"differs from", + "Like"=>"equals (no case)", + "NotLike"=>"differs from (no case)", + "Contains"=>"contains", + "Begins with"=>"begins with", + "Finishes with"=>"finishes with" + ); + } + public function GetBasicFilterLooseOperator() + { + return "Contains"; + } + + public function GetBasicFilterSQLExpr($sOpCode, $value) + { + $sQValue = CMDBSource::Quote($value); + switch ($sOpCode) + { + case '=': + case '!=': + return $this->GetSQLExpr()." $sOpCode $sQValue"; + case 'Begins with': + return $this->GetSQLExpr()." LIKE ".CMDBSource::Quote("$value%"); + case 'Finishes with': + return $this->GetSQLExpr()." LIKE ".CMDBSource::Quote("%$value"); + case 'Contains': + return $this->GetSQLExpr()." LIKE ".CMDBSource::Quote("%$value%"); + case 'NotLike': + return $this->GetSQLExpr()." NOT LIKE $sQValue"; + case 'Like': + default: + return $this->GetSQLExpr()." LIKE $sQValue"; + } + } + + public function MakeRealValue($proposedValue) + { + return (string)$proposedValue; + // if (!settype($proposedValue, "string")) + // { + // throw new CoreException("Failed to change the type of '$proposedValue' to a string"); + // } + } + public function RealValueToSQLValue($value) + { + assert(is_string($value)); + return $value; + } + public function SQLValueToRealValue($value) + { + return $value; + } +} + +/** + * Map a text column (size > ?) to an attribute + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class AttributeText extends AttributeString +{ + public function GetType() {return "Text";} + public function GetTypeDesc() {return "Multiline character string";} + public function GetEditClass() {return "Text";} + public function GetDBFieldType() {return "TEXT";} + + public function GetAsHTML($sValue) + { + return str_replace("\n", "
    \n", parent::GetAsHTML($sValue)); + } + + public function GetAsXML($value) + { + return Str::pure2xml($value); + } + + public function GetAsCSV($value, $sSeparator = ';', $sSepEscape = ',') + { + return str_replace("\n", "[newline]", parent::GetAsCSV($sValue, $sSeparator, $sSepEscape)); + } +} + +/** + * Map a enum column to an attribute + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class AttributeEnum extends AttributeString +{ + static protected function ListExpectedParams() + { + return parent::ListExpectedParams(); + //return array_merge(parent::ListExpectedParams(), array()); + } + + public function GetType() {return "Enum";} + public function GetTypeDesc() {return "List of predefined alphanumeric strings";} + public function GetEditClass() {return "String";} + public function GetDBFieldType() + { + $oValDef = $this->GetValuesDef(); + if ($oValDef) + { + $aValues = CMDBSource::Quote($oValDef->GetValues(array(), ""), true); + } + else + { + $aValues = array(); + } + if (count($aValues) > 0) + { + return "ENUM(".implode(", ", $aValues).")"; + } + else + { + return "VARCHAR(255)"; // ENUM() is not an allowed syntax! + } + } + + public function GetBasicFilterOperators() + { + return parent::GetBasicFilterOperators(); + } + public function GetBasicFilterLooseOperator() + { + return parent::GetBasicFilterLooseOperator(); + } + + public function GetBasicFilterSQLExpr($sOpCode, $value) + { + return parent::GetBasicFilterSQLExpr($sOpCode, $value); + } +} + +/** + * Map a date+time column to an attribute + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class AttributeDate extends AttributeDBField +{ + const MYDATEFORMAT = "Y-m-d H:i:s"; + //const MYDATETIMEZONE = "UTC"; + const MYDATETIMEZONE = "Europe/Paris"; + static protected $const_TIMEZONE = null; // set once for all upon object construct + + static public function InitStatics() + { + // Init static constant once for all (remove when PHP allows real static const) + self::$const_TIMEZONE = new DateTimeZone(self::MYDATETIMEZONE); + + // #@# Init default timezone -> do not get a notice... to be improved !!! + date_default_timezone_set(self::MYDATETIMEZONE); + } + + static protected function ListExpectedParams() + { + return parent::ListExpectedParams(); + //return array_merge(parent::ListExpectedParams(), array()); + } + + public function GetType() {return "Date";} + public function GetTypeDesc() {return "Date and time";} + public function GetEditClass() {return "Date";} + public function GetDBFieldType() {return "TIMESTAMP";} + + public function GetBasicFilterOperators() + { + return array( + "="=>"equals", + "!="=>"differs from", + "<"=>"before", + "<="=>"before", + ">"=>"after (strictly)", + ">="=>"after", + "SameDay"=>"same day (strip time)", + "SameMonth"=>"same year/month", + "SameYear"=>"same year", + "Today"=>"today", + ">|"=>"after today + N days", + "<|"=>"before today + N days", + "=|"=>"equals today + N days", + ); + } + public function GetBasicFilterLooseOperator() + { + // Unless we implement a "same xxx, depending on given precision" ! + return "="; + } + + public function GetBasicFilterSQLExpr($sOpCode, $value) + { + $sQValue = CMDBSource::Quote($value); + + switch ($sOpCode) + { + case '=': + case '!=': + case '<': + case '<=': + case '>': + case '>=': + return $this->GetSQLExpr()." $sOpCode $sQValue"; + case 'SameDay': + return "DATE(".$this->GetSQLExpr().") = DATE($sQValue)"; + case 'SameMonth': + return "DATE_FORMAT(".$this->GetSQLExpr().", '%Y-%m') = DATE_FORMAT($sQValue, '%Y-%m')"; + case 'SameYear': + return "MONTH(".$this->GetSQLExpr().") = MONTH($sQValue)"; + case 'Today': + return "DATE(".$this->GetSQLExpr().") = CURRENT_DATE()"; + case '>|': + return "DATE(".$this->GetSQLExpr().") > DATE_ADD(CURRENT_DATE(), INTERVAL $sQValue DAY)"; + case '<|': + return "DATE(".$this->GetSQLExpr().") < DATE_ADD(CURRENT_DATE(), INTERVAL $sQValue DAY)"; + case '=|': + return "DATE(".$this->GetSQLExpr().") = DATE_ADD(CURRENT_DATE(), INTERVAL $sQValue DAY)"; + default: + return $this->GetSQLExpr()." = $sQValue"; + } + } + + public function MakeRealValue($proposedValue) + { + if (!is_numeric($proposedValue)) + { + return $proposedValue; + } + else + { + return date("Y-m-d H:i:s", $proposedValue); + } + throw new CoreException("Invalid type for a date (found ".gettype($proposedValue)." and accepting string/int/DateTime)"); + return null; + } + public function RealValueToSQLValue($value) + { + if (empty($value)) + { + // Make a valid date for MySQL. TO DO: support NULL as a literal value for fields that can be null. + return '0000-00-00 00:00:00'; + } + return $value; + } + public function SQLValueToRealValue($value) + { + return $value; + } + + public function GetAsHTML($value) + { + return Str::pure2html($value); + } + + public function GetAsXML($value) + { + return Str::pure2xml($value); + } + + public function GetAsCSV($value, $sSeparator = ';', $sSepEscape = ',') + { + return str_replace($sSeparator, $sSepEscape, $value); + } +} + +// Init static constant once for all (remove when PHP allows real static const) +AttributeDate::InitStatics(); + + +/** + * Map a foreign key to an attribute + * AttributeExternalKey and AttributeExternalField may be an external key + * the difference is that AttributeExternalKey corresponds to a column into the defined table + * where an AttributeExternalField corresponds to a column into another table (class) + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class AttributeExternalKey extends AttributeDBFieldVoid +{ + static protected function ListExpectedParams() + { + return array_merge(parent::ListExpectedParams(), array("targetclass", "is_null_allowed")); + } + + public function GetType() {return "Extkey";} + public function GetTypeDesc() {return "Link to another object";} + public function GetEditClass() {return "ExtKey";} + public function GetDBFieldType() {return "INT";} + + public function IsExternalKey($iType = EXTKEY_RELATIVE) {return true;} + public function GetTargetClass($iType = EXTKEY_RELATIVE) {return $this->Get("targetclass");} + public function GetKeyAttDef($iType = EXTKEY_RELATIVE){return $this;} + public function GetKeyAttCode() {return $this->GetCode();} + + + public function GetDefaultValue() {return 0;} + public function IsNullAllowed() {return $this->Get("is_null_allowed");} + + public function GetBasicFilterOperators() + { + return parent::GetBasicFilterOperators(); + } + public function GetBasicFilterLooseOperator() + { + return parent::GetBasicFilterLooseOperator(); + } + + public function GetBasicFilterSQLExpr($sOpCode, $value) + { + return parent::GetBasicFilterSQLExpr($sOpCode, $value); + } + + // overloaded here so that an ext key always have the answer to + // "what are you possible values?" + public function GetValuesDef() + { + $oValSetDef = $this->Get("allowed_values"); + if (!$oValSetDef) + { + // Let's propose every existing value + $oValSetDef = new ValueSetObjects($this->GetTargetClass()); + } + return $oValSetDef; + } +} + +/** + * An attribute which corresponds to an external key (direct or indirect) + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class AttributeExternalField extends AttributeDefinition +{ + + static protected function ListExpectedParams() + { + return array_merge(parent::ListExpectedParams(), array("extkey_attcode", "target_attcode")); + } + + public function GetType() {return "ExtkeyField";} + public function GetTypeDesc() {return "Field of an object pointed to by the current object";} + public function GetEditClass() {return "ExtField";} + public function GetDBFieldType() + { + // throw new CoreException("external attribute: does it make any sense to request its type ?"); + $oExtAttDef = $this->GetExtAttDef(); + return $oExtAttDef->GetDBFieldType(); + } + + public function IsExternalKey($iType = EXTKEY_RELATIVE) + { + switch($iType) + { + case EXTKEY_ABSOLUTE: + // see further + $oRemoteAtt = $this->GetExtAttDef(); + return $oRemoteAtt->IsExternalKey($iType); + + case EXTKEY_RELATIVE: + return false; + + default: + throw new CoreException("Unexpected value for argument iType: '$iType'"); + } + } + + public function GetTargetClass($iType = EXTKEY_RELATIVE) + { + return $this->GetKeyAttDef($iType)->GetTargetClass(); + } + + public function IsExternalField() {return true;} + public function GetKeyAttCode() {return $this->Get("extkey_attcode");} + public function GetExtAttCode() {return $this->Get("target_attcode");} + + public function GetKeyAttDef($iType = EXTKEY_RELATIVE) + { + switch($iType) + { + case EXTKEY_ABSOLUTE: + // see further + $oRemoteAtt = $this->GetExtAttDef(); + if ($oRemoteAtt->IsExternalField()) + { + return $oRemoteAtt->GetKeyAttDef(EXTKEY_ABSOLUTE); + } + else if ($oRemoteAtt->IsExternalKey()) + { + return $oRemoteAtt; + } + return $this->GetKeyAttDef(EXTKEY_RELATIVE); // which corresponds to the code hereafter ! + + case EXTKEY_RELATIVE: + return MetaModel::GetAttributeDef($this->GetHostClass(), $this->Get("extkey_attcode")); + + default: + throw new CoreException("Unexpected value for argument iType: '$iType'"); + } + } + + public function GetExtAttDef() + { + $oKeyAttDef = $this->GetKeyAttDef(); + $oExtAttDef = MetaModel::GetAttributeDef($oKeyAttDef->Get("targetclass"), $this->Get("target_attcode")); + return $oExtAttDef; + } + + public function GetSQLExpr() + { + $oExtAttDef = $this->GetExtAttDef(); + return $oExtAttDef->GetSQLExpr(); + } + public function DBGetUsedFields() + { + // No field is used but the one defined in the field of the external class + // #@# so what ? + return array(); + } + + public function GetDefaultValue() + { + $oExtAttDef = $this->GetExtAttDef(); + return $oExtAttDef->GetDefaultValue(); + } + public function IsNullAllowed() + { + $oExtAttDef = $this->GetExtAttDef(); + return $oExtAttDef->IsNullAllowed(); + } + + public function GetBasicFilterOperators() + { + $oExtAttDef = $this->GetExtAttDef(); + return $oExtAttDef->GetBasicFilterOperators(); + } + public function GetBasicFilterLooseOperator() + { + $oExtAttDef = $this->GetExtAttDef(); + return $oExtAttDef->GetBasicFilterLooseOperator(); + } + + public function GetBasicFilterSQLExpr($sOpCode, $value) + { + $oExtAttDef = $this->GetExtAttDef(); + return $oExtAttDef->GetBasicFilterSQLExpr($sOpCode, $value); + } + + public function MakeRealValue($proposedValue) + { + $oExtAttDef = $this->GetExtAttDef(); + return $oExtAttDef->MakeRealValue($proposedValue); + } + public function RealValueToSQLValue($value) + { + // This one could be used in case of filtering only + $oExtAttDef = $this->GetExtAttDef(); + return $oExtAttDef->RealValueToSQLValue($value); + } + public function SQLValueToRealValue($value) + { + $oExtAttDef = $this->GetExtAttDef(); + return $oExtAttDef->SQLValueToRealValue($value); + } + public function GetAsHTML($value) + { + $oExtAttDef = $this->GetExtAttDef(); + return $oExtAttDef->GetAsHTML($value); + } + public function GetAsXML($value) + { + $oExtAttDef = $this->GetExtAttDef(); + return $oExtAttDef->GetAsXML($value); + } + public function GetAsCSV($value, $sSeparator = ';', $sSepEscape = ',') + { + $oExtAttDef = $this->GetExtAttDef(); + return $oExtAttDef->GetAsCSV($value); + } +} + +/** + * Map a varchar column to an URL (formats the ouput in HMTL) + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class AttributeURL extends AttributeString +{ + static protected function ListExpectedParams() + { + //return parent::ListExpectedParams(); + return array_merge(parent::ListExpectedParams(), array("target", "label")); + } + + public function GetType() {return "Url";} + public function GetTypeDesc() {return "Absolute or relative URL as a text string";} + public function GetEditClass() {return "String";} + + public function GetAsHTML($sValue) + { + $sTarget = $this->Get("target"); + if (empty($sTarget)) $sTarget = "_blank"; + $sLabel = Str::pure2html($sValue); + if (strlen($sLabel) > 40) + { + // Truncate the length to about 40 characters, by removing the middle + $sLabel = substr($sLabel, 0, 25).'...'.substr($sLabel, -15); + } + return "
    $sLabel"; + } +} + +?> diff --git a/core/bulkchange.class.inc.php b/core/bulkchange.class.inc.php new file mode 100644 index 000000000..491852a22 --- /dev/null +++ b/core/bulkchange.class.inc.php @@ -0,0 +1,439 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ + +class BulkChangeException extends CoreException +{ +} + +/** + * CellChangeSpec + * A series of classes, keeping the information about a given cell: could it be changed or not (and why)? + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +abstract class CellChangeSpec +{ + protected $m_proposedValue; + + public function __construct($proposedValue) + { + $this->m_proposedValue = $proposedValue; + } + + public function GetValue() + { + return $this->m_proposedValue; + } + + abstract public function GetDescription(); +} + + +class CellChangeSpec_Void extends CellChangeSpec +{ + public function GetDescription() + { + return $this->GetValue(); + } +} + +class CellChangeSpec_Unchanged extends CellChangeSpec +{ + public function GetDescription() + { + return $this->GetValue()." (unchanged)"; + } +} + +class CellChangeSpec_Init extends CellChangeSpec +{ + public function GetDescription() + { + return $this->GetValue(); + } +} + +class CellChangeSpec_Modify extends CellChangeSpec +{ + protected $m_previousValue; + + public function __construct($proposedValue, $previousValue) + { + $this->m_previousValue = $previousValue; + parent::__construct($proposedValue); + } + + public function GetDescription() + { + return $this->GetValue()." (previous: ".$this->m_previousValue.")"; + } +} + +class CellChangeSpec_Issue extends CellChangeSpec_Modify +{ + protected $m_sReason; + + public function __construct($proposedValue, $previousValue, $sReason) + { + $this->m_sReason = $sReason; + parent::__construct($proposedValue, $previousValue); + } + + public function GetDescription() + { + if (is_null($this->m_proposedValue)) + { + return 'Could not be changed - reason: '.$this->m_sReason; + } + return 'Could not be changed to "'.$this->GetValue().'" - reason: '.$this->m_sReason.' (previous: '.$this->m_previousValue.')'; + } +} + + +/** + * RowStatus + * A series of classes, keeping the information about a given row: could it be changed or not (and why)? + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +abstract class RowStatus +{ + public function __construct() + { + } + + abstract public function GetDescription(); +} + +class RowStatus_NoChange extends RowStatus +{ + public function GetDescription() + { + return "unchanged"; + } +} + +class RowStatus_NewObj extends RowStatus +{ + protected $m_iObjKey; + + public function __construct($iObjKey = null) + { + $this->m_iObjKey = $iObjKey; + } + + public function GetDescription() + { + if (is_null($this->m_iObjKey)) + { + return "Create"; + } + else + { + return 'Created ('.$this->m_iObjKey.')'; + } + } +} + +class RowStatus_Modify extends RowStatus +{ + protected $m_iChanged; + + public function __construct($iChanged) + { + $this->m_iChanged = $iChanged; + } + + public function GetDescription() + { + return "update ".$this->m_iChanged." cols"; + } +} + +class RowStatus_Issue extends RowStatus +{ + protected $m_sReason; + + public function __construct($sReason) + { + $this->m_sReason = $sReason; + } + + public function GetDescription() + { + return 'Skipped - reason:'.$this->m_sReason; + } +} + + +/** + * BulkChange + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class BulkChange +{ + protected $m_sClass; + protected $m_aData; + // Note: hereafter, iCol maybe actually be any acceptable key (string) + // #@# todo: rename the variables to sColIndex + protected $m_aAttList; // attcode => iCol + protected $m_aReconcilKeys;// iCol => attcode + protected $m_aExtKeys; // aExtKeys[sExtKeyAttCode][sExtReconcKeyAttCode] = iCol; + + public function __construct($sClass, $aData, $aAttList, $aReconcilKeys, $aExtKeys) + { + $this->m_sClass = $sClass; + $this->m_aData = $aData; + $this->m_aAttList = $aAttList; + $this->m_aReconcilKeys = $aReconcilKeys; + $this->m_aExtKeys = $aExtKeys; + } + + protected function PrepareObject(&$oTargetObj, $aRowData, &$aErrors) + { + $aResults = array(); + $aErrors = array(); + + // External keys reconciliation + // + foreach($this->m_aExtKeys as $sAttCode => $aKeyConfig) + { + $oExtKey = MetaModel::GetAttributeDef(get_class($oTargetObj), $sAttCode); + $oReconFilter = new CMDBSearchFilter($oExtKey->GetTargetClass()); + foreach ($aKeyConfig as $sForeignAttCode => $iCol) + { + // The foreign attribute is one of our reconciliation key + $sFieldId = MakeExtFieldSelectValue($sAttCode, $sForeignAttCode); + $oReconFilter->AddCondition($sForeignAttCode, $aRowData[$iCol], '='); + $aResults["col$iCol"] = new CellChangeSpec_Void($aRowData[$iCol]); + } + $oExtObjects = new CMDBObjectSet($oReconFilter); + switch($oExtObjects->Count()) + { + case 0: + $aErrors[$sAttCode] = "Object not found"; + $aResults[$sAttCode]= new CellChangeSpec_Issue(null, $oTargetObj->Get($sAttCode), 'Object not found'); + break; + case 1: + // Do change the external key attribute + $oForeignObj = $oExtObjects->Fetch(); + $oTargetObj->Set($sAttCode, $oForeignObj->GetKey()); + + // Report it + if (array_key_exists($sAttCode, $oTargetObj->ListChanges())) + { + if ($oTargetObj->IsNew()) + { + $aResults[$sAttCode]= new CellChangeSpec_Init($oForeignObj->GetKey(), $oTargetObj->Get($sAttCode), $oTargetObj->GetOriginal($sAttCode)); + } + else + { + $aResults[$sAttCode]= new CellChangeSpec_Modify($oForeignObj->GetKey(), $oTargetObj->Get($sAttCode), $oTargetObj->GetOriginal($sAttCode)); + } + } + else + { + $aResults[$sAttCode]= new CellChangeSpec_Unchanged($oTargetObj->Get($sAttCode)); + } + break; + default: + $aErrors[$sAttCode] = "Found ".$oExtObjects->Count()." matches"; + $aResults[$sAttCode]= new CellChangeSpec_Issue(null, $oTargetObj->Get($sAttCode), "Found ".$oExtObjects->Count()." matches"); + } + } + + // Set the object attributes + // + foreach ($this->m_aAttList as $sAttCode => $iCol) + { + if (!$oTargetObj->CheckValue($sAttCode, $aRowData[$iCol])) + { + $aErrors[$sAttCode] = "Unexpected value"; + } + else + { + $oTargetObj->Set($sAttCode, $aRowData[$iCol]); + } + } + + // Reporting on fields + // + $aChangedFields = $oTargetObj->ListChanges(); + foreach ($this->m_aAttList as $sAttCode => $iCol) + { + if (isset($aErrors[$sAttCode])) + { + $aResults["col$iCol"]= new CellChangeSpec_Issue($aRowData[$iCol], $oTargetObj->Get($sAttCode), $aErrors[$sAttCode]); + } + elseif (array_key_exists($sAttCode, $aChangedFields)) + { + $originalValue = $oTargetObj->GetOriginal($sAttCode); + if ($oTargetObj->IsNew()) + { + $aResults["col$iCol"]= new CellChangeSpec_Init($aRowData[$iCol], $oTargetObj->Get($sAttCode), $originalValue); + } + else + { + $aResults["col$iCol"]= new CellChangeSpec_Modify($aRowData[$iCol], $oTargetObj->Get($sAttCode), $originalValue); + } + } + else + { + // By default... nothing happens + $aResults["col$iCol"]= new CellChangeSpec_Void($aRowData[$iCol]); + } + } + + // Checks + // + if (!$oTargetObj->CheckConsistency()) + { + $aErrors["GLOBAL"] = "Attributes not consistent with each others"; + } + return $aResults; + } + + + protected function CreateObject(&$aResult, $iRow, $aRowData, CMDBChange $oChange = null) + { + $oTargetObj = MetaModel::NewObject($this->m_sClass); + $aResult[$iRow] = $this->PrepareObject($oTargetObj, $aRowData, $aErrors); + + if (count($aErrors) > 0) + { + $sErrors = implode(', ', $aErrors); + $aResult[$iRow]["__STATUS__"] = new RowStatus_Issue("Unexpected attribute value(s)"); + return; + } + + // Check that any external key will have a value proposed + // Could be said once for all rows !!! + foreach(MetaModel::ListAttributeDefs($this->m_sClass) as $sAttCode=>$oAtt) + { + if (!$oAtt->IsExternalKey()) continue; + } + + // Optionaly record the results + // + if ($oChange) + { + $newID = $oTargetObj->DBInsertTrackedNoReload($oChange); + $aResult[$iRow]["__STATUS__"] = new RowStatus_NewObj($newID); + } + else + { + $aResult[$iRow]["__STATUS__"] = new RowStatus_NewObj(); + } + + } + + protected function UpdateObject(&$aResult, $iRow, $oTargetObj, $aRowData, CMDBChange $oChange = null) + { + $aResult[$iRow] = $this->PrepareObject($oTargetObj, $aRowData, $aErrors); + + // Reporting + // + if (count($aErrors) > 0) + { + $sErrors = implode(', ', $aErrors); + $aResult[$iRow]["__STATUS__"] = new RowStatus_Issue("Unexpected attribute value(s)"); + return; + } + + $aChangedFields = $oTargetObj->ListChanges(); + if (count($aChangedFields) > 0) + { + $aResult[$iRow]["__STATUS__"] = new RowStatus_Modify(count($aChangedFields)); + + // Optionaly record the results + // + if ($oChange) + { + $oTargetObj->DBUpdateTracked($oChange); + } + } + else + { + $aResult[$iRow]["__STATUS__"] = new RowStatus_NoChange(); + } + } + + public function Process(CMDBChange $oChange = null) + { + // Note: $oChange can be null, in which case the aim is to check what would be done + + // Compute the results + // + $aResult = array(); + foreach($this->m_aData as $iRow => $aRowData) + { + $oReconciliationFilter = new CMDBSearchFilter($this->m_sClass); + foreach($this->m_aReconcilKeys as $sAttCode) + { + $iCol = $this->m_aAttList[$sAttCode]; + $oReconciliationFilter->AddCondition($sAttCode, $aRowData[$iCol], '='); + } + $oReconciliationSet = new CMDBObjectSet($oReconciliationFilter); + switch($oReconciliationSet->Count()) + { + case 0: + $this->CreateObject($aResult, $iRow, $aRowData, $oChange); + // $aResult[$iRow]["__STATUS__"]=> set in CreateObject + $aResult[$iRow]["__RECONCILIATION__"] = "Object not found"; + break; + case 1: + $oTargetObj = $oReconciliationSet->Fetch(); + $this->UpdateObject($aResult, $iRow, $oTargetObj, $aRowData, $oChange); + $aResult[$iRow]["__RECONCILIATION__"] = "Found a match ".$oTargetObj->GetKey(); + // $aResult[$iRow]["__STATUS__"]=> set in UpdateObject + break; + default: + foreach ($this->m_aAttList as $sAttCode => $iCol) + { + $aResult[$iRow]["col$iCol"]= $aRowData[$iCol]; + } + $aResult[$iRow]["__RECONCILIATION__"] = "Found ".$oReconciliationSet->Count()." matches"; + $aResult[$iRow]["__STATUS__"]= new RowStatus_Issue("ambiguous reconciliation"); + } + + // Whatever happened, do report the reconciliation values + foreach($this->m_aReconcilKeys as $sAttCode) + { + $iCol = $this->m_aAttList[$sAttCode]; + $aResult[$iRow]["col$iCol"] = new CellChangeSpec_Void($aRowData[$iCol]); + } + } + return $aResult; + } +} + + +?> diff --git a/core/cmdbchange.class.inc.php b/core/cmdbchange.class.inc.php new file mode 100644 index 000000000..02d011a04 --- /dev/null +++ b/core/cmdbchange.class.inc.php @@ -0,0 +1,44 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ +class CMDBChange extends DBObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "core/cmdb", + "name" => "change", + "description" => "Changes tracking", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "date", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_change", + "db_key_field" => "id", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + //MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeDate("date", array("label"=>"date", "description"=>"date and time at which the changes have been recorded", "allowed_values"=>null, "sql"=>"date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("userinfo", array("label"=>"misc. info", "description"=>"caller's defined information", "allowed_values"=>null, "sql"=>"userinfo", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); + + //MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("date"); + MetaModel::Init_AddFilterFromAttribute("userinfo"); + } + +} + +?> diff --git a/core/cmdbchangeop.class.inc.php b/core/cmdbchangeop.class.inc.php new file mode 100644 index 000000000..0ed8776de --- /dev/null +++ b/core/cmdbchangeop.class.inc.php @@ -0,0 +1,167 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ + +class CMDBChangeOp extends DBObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "core/cmdb", + "name" => "change operation", + "description" => "Change operations tracking", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "change", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_changeop", + "db_key_field" => "id", + "db_finalclass_field" => "optype", + ); + MetaModel::Init_Params($aParams); + //MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeExternalKey("change", array("label"=>"change", "description"=>"change", "allowed_values"=>null, "sql"=>"changeid", "targetclass"=>"CMDBChange", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("date", array("label"=>"date", "description"=>"date and time of the change", "allowed_values"=>null, "extkey_attcode"=>"change", "target_attcode"=>"date"))); + MetaModel::Init_AddAttribute(new AttributeExternalField("userinfo", array("label"=>"user", "description"=>"who made this change", "allowed_values"=>null, "extkey_attcode"=>"change", "target_attcode"=>"userinfo"))); + MetaModel::Init_AddAttribute(new AttributeString("objclass", array("label"=>"object class", "description"=>"object class", "allowed_values"=>null, "sql"=>"objclass", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("objkey", array("label"=>"object id", "description"=>"object id", "allowed_values"=>null, "sql"=>"objkey", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_AddFilterFromAttribute("objclass"); + MetaModel::Init_AddFilterFromAttribute("objkey"); + MetaModel::Init_AddFilterFromAttribute("date"); + MetaModel::Init_AddFilterFromAttribute("userinfo"); + } +} + + + +/** + * Record the creation of an object + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class CMDBChangeOpCreate extends CMDBChangeOp +{ + public static function Init() + { + $aParams = array + ( + "category" => "core/cmdb", + "name" => "object creation", + "description" => "Object creation tracking", + "key_type" => "", + "key_label" => "", + "name_attcode" => "change", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_changeop_create", + "db_key_field" => "id", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + + MetaModel::Init_InheritFilters(); + } +} + + +/** + * Record the deletion of an object + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class CMDBChangeOpDelete extends CMDBChangeOp +{ + public static function Init() + { + $aParams = array + ( + "category" => "core/cmdb", + "name" => "object deletion", + "description" => "Object deletion tracking", + "key_type" => "", + "key_label" => "", + "name_attcode" => "change", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_changeop_delete", + "db_key_field" => "id", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + + MetaModel::Init_InheritFilters(); + } +} + + +/** + * Record the modification of an attribute + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class CMDBChangeOpSetAttribute extends CMDBChangeOp +{ + public static function Init() + { + $aParams = array + ( + "category" => "core/cmdb", + "name" => "object change", + "description" => "Object properties change tracking", + "key_type" => "", + "key_label" => "", + "name_attcode" => "change", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_changeop_setatt", + "db_key_field" => "id", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeString("attcode", array("label"=>"Attribute", "description"=>"code of the modified property", "allowed_values"=>null, "sql"=>"attcode", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("oldvalue", array("label"=>"Previous value", "description"=>"previous value of the attribute", "allowed_values"=>null, "sql"=>"oldvalue", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("newvalue", array("label"=>"New value", "description"=>"new value of the attribute", "allowed_values"=>null, "sql"=>"newvalue", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("attcode"); + MetaModel::Init_AddFilterFromAttribute("oldvalue"); + MetaModel::Init_AddFilterFromAttribute("newvalue"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'attcode', 'oldvalue', 'newvalue')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'attcode', 'oldvalue', 'newvalue')); // Attributes to be displayed for a list + } +} + +?> diff --git a/core/cmdbobject.class.inc.php b/core/cmdbobject.class.inc.php new file mode 100644 index 000000000..d49f7280b --- /dev/null +++ b/core/cmdbobject.class.inc.php @@ -0,0 +1,432 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ + +require_once('coreexception.class.inc.php'); + +require_once('config.class.inc.php'); + +require_once('attributedef.class.inc.php'); +require_once('filterdef.class.inc.php'); +require_once('stimulus.class.inc.php'); +require_once('valuesetdef.class.inc.php'); +require_once('MyHelpers.class.inc.php'); + +require_once('expression.class.inc.php'); + +require_once('cmdbsource.class.inc.php'); +require_once('sqlquery.class.inc.php'); +require_once('oql/oqlquery.class.inc.php'); +require_once('oql/oqlexception.class.inc.php'); +require_once('oql/oql-parser.php'); +require_once('oql/oql-lexer.php'); +require_once('oql/oqlinterpreter.class.inc.php'); + +require_once('dbobject.class.php'); +require_once('dbobjectsearch.class.php'); +require_once('dbobjectset.class.php'); + +require_once('cmdbchange.class.inc.php'); +require_once('cmdbchangeop.class.inc.php'); + +require_once('csvparser.class.inc.php'); +require_once('bulkchange.class.inc.php'); + +require_once('userrights.class.inc.php'); + +// +// Error handling +// To be finalized... or removed ? +// +function cmdbErrorHandler($errno, $errstr, $errfile, $errline) +{ +// font-family: Courier-New, Courier, Arial, Helevtica; + $sErrorStyle = " + background-color: #ffaaaa; + color: #000000; + border: 1px dashed #000000; + padding: 0.25em; + margin-top: 1em; + "; + $sCallStackStyle = " + font-size: smaller; + background-color: #ffcccc; + color: #000000; + border: 1px dashed #000000; + padding: 0.25em; + margin-top: 1em; + "; + + switch ($errno) + { + case E_USER_ERROR: + case E_ERROR: + echo "
    \n"; + echo "Error [$errno] $errstr
    \n"; + echo "
    \n"; + MyHelpers::dump_callstack(1); + echo "
    \n"; + echo "Hereafter the biz model internals:
    \n"; + echo "
    \n";
    +		MetaModel::static_var_dump();
    +		echo "
    \n"; + echo "Aborting...
    \n"; + echo "
    \n"; + exit(1); + break; + case E_USER_WARNING: + case E_WARNING: + echo "
    \n"; + echo "Warning [$errno] $errstr
    \n"; + echo "
    \n"; + MyHelpers::dump_callstack(1); + echo "
    \n"; + echo "
    \n"; + break; + case E_USER_NOTICE: + case E_NOTICE: + echo "
    \n"; + echo "Notice [$errno] $errstr
    \n"; + echo "
    \n"; + MyHelpers::dump_callstack(1); + echo "
    \n"; + echo "
    \n"; + break; + default: + echo "Unknown error type: [$errno] $errstr
    \n"; + MyHelpers::dump_callstack(1); + break; + } +} + +error_reporting(E_ALL | E_STRICT); +//set_error_handler("cmdbErrorHandler"); + + + +// +// +// + + +/** + * A persistent object, which changes are accurately recorded + * + * @package iTopORM + * @author Romain Quetiez + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ +abstract class CMDBObject extends DBObject +{ + protected $m_datCreated; + protected $m_datUpdated; + protected static $m_oCurrChange = null; + + + private function RecordObjCreation(CMDBChange $oChange) + { + $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpCreate"); + $oMyChangeOp->Set("change", $oChange->GetKey()); + $oMyChangeOp->Set("objclass", get_class($this)); + $oMyChangeOp->Set("objkey", $this->GetKey()); + $iId = $oMyChangeOp->DBInsertNoReload(); + } + private function RecordObjDeletion(CMDBChange $oChange, $objkey) + { + $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpDelete"); + $oMyChangeOp->Set("change", $oChange->GetKey()); + $oMyChangeOp->Set("objclass", get_class($this)); + $oMyChangeOp->Set("objkey", $objkey); + $iId = $oMyChangeOp->DBInsertNoReload(); + } + private function RecordAttChanges(CMDBChange $oChange, array $aValues) + { + // $aValues is an array of $sAttCode => $value + // + foreach ($aValues as $sAttCode=> $value) + { + $oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode); + if ($oAttDef->IsLinkSet()) continue; // #@# temporary + $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttribute"); + $oMyChangeOp->Set("change", $oChange->GetKey()); + $oMyChangeOp->Set("objclass", get_class($this)); + $oMyChangeOp->Set("objkey", $this->GetKey()); + $oMyChangeOp->Set("attcode", $sAttCode); + $oMyChangeOp->Set("oldvalue", $this->GetOriginal($sAttCode)); + $oMyChangeOp->Set("newvalue", $value); + $iId = $oMyChangeOp->DBInsertNoReload(); + } + } + + public function DBInsert() + { + if(!is_object(self::$m_oCurrChange)) + { + throw new CoreException("DBInsert() could not be used here, please use DBInsertTracked() instead"); + } + return $this->DBInsertTracked_Internal(); + } + + public function DBInsertTracked(CMDBChange $oChange) + { + self::$m_oCurrChange = $oChange; + $ret = $this->DBInsertTracked_Internal(); + self::$m_oCurrChange = null; + return $ret; + } + + public function DBInsertTrackedNoReload(CMDBChange $oChange) + { + self::$m_oCurrChange = $oChange; + $ret = $this->DBInsertTracked_Internal(true); + self::$m_oCurrChange = null; + return $ret; + } + + protected function DBInsertTracked_Internal($bDoNotReload = false) + { + if ($bDoNotReload) + { + $ret = parent::DBInsertNoReload(); + } + else + { + $ret = parent::DBInsert(); + } + $this->RecordObjCreation(self::$m_oCurrChange); + return $ret; + } + + public function DBClone($newKey = null) + { + if(!self::$m_oCurrChange) + { + throw new CoreException("DBClone() could not be used here, please use DBCloneTracked() instead"); + } + return $this->DBCloneTracked_Internal(); + } + + public function DBCloneTracked(CMDBChange $oChange, $newKey = null) + { + self::$m_oCurrChange = $oChange; + $this->DBCloneTracked_Internal($newKey); + self::$m_oCurrChange = null; + } + + protected function DBCloneTracked_Internal($newKey = null) + { + $newKey = parent::DBClone($newKey); + $oClone = MetaModel::GetObject(get_class($this), $newKey); + + $oClone->RecordObjCreation(self::$m_oCurrChange); + return $newKey; + } + + public function DBUpdate() + { + if(!self::$m_oCurrChange) + { + throw new CoreException("DBUpdate() could not be used here, please use DBUpdateTracked() instead"); + } + return $this->DBUpdateTracked_internal(); + } + + public function DBUpdateTracked(CMDBChange $oChange) + { + self::$m_oCurrChange = $oChange; + $this->DBUpdateTracked_Internal(); + self::$m_oCurrChange = null; + } + + protected function DBUpdateTracked_Internal() + { + // Copy the changes list before the update (the list should be reset afterwards) + $aChanges = $this->ListChanges(); + if (count($aChanges) == 0) + { + throw new CoreWarning("Attempting to update an unchanged object"); + return; + } + + $ret = parent::DBUpdate(); + $this->RecordAttChanges(self::$m_oCurrChange, $aChanges); + return $ret; + } + + public function DBDelete() + { + if(!self::$m_oCurrChange) + { + throw new CoreException("DBDelete() could not be used here, please use DBDeleteTracked() instead"); + } + return $this->DBDeleteTracked_Internal(); + } + + public function DBDeleteTracked(CMDBChange $oChange) + { + self::$m_oCurrChange = $oChange; + $this->DBDeleteTracked_Internal(); + self::$m_oCurrChange = null; + } + + protected function DBDeleteTracked_Internal() + { + $prevkey = $this->GetKey(); + $ret = parent::DBDelete(); + $this->RecordObjDeletion(self::$m_oCurrChange, $prevkey); + return $ret; + } + + public static function BulkDelete(DBObjectSearch $oFilter) + { + if(!self::$m_oCurrChange) + { + throw new CoreException("BulkDelete() could not be used here, please use BulkDeleteTracked() instead"); + } + return $this->BulkDeleteTracked_Internal($oFilter); + } + + public static function BulkDeleteTracked(CMDBChange $oChange, DBObjectSearch $oFilter) + { + self::$m_oCurrChange = $oChange; + $this->BulkDeleteTracked_Internal($oFilter); + self::$m_oCurrChange = null; + } + + protected static function BulkDeleteTracked_Internal(DBObjectSearch $oFilter) + { + throw new CoreWarning("Change tracking not tested for bulk operations"); + + // Get the list of objects to delete (and record data before deleting the DB records) + $oObjSet = new CMDBObjectSet($oFilter); + $aObjAndKeys = array(); // array of pkey=>object + while ($oItem = $oObjSet->Fetch()) + { + $aObjAndKeys[$oItem->GetKey()] = $oItem; + } + $oObjSet->FreeResult(); + + // Delete in one single efficient query + $ret = parent::BulkDelete($oFilter); + // Record... in many queries !!! + foreach($aObjAndKeys as $prevkey=>$oItem) + { + $oItem->RecordObjDeletion(self::$m_oCurrChange, $prevkey); + } + return $ret; + } + + public static function BulkUpdate(DBObjectSearch $oFilter, array $aValues) + { + if(!self::$m_oCurrChange) + { + throw new CoreException("BulkUpdate() could not be used here, please use BulkUpdateTracked() instead"); + } + return $this->BulkUpdateTracked_Internal($oFilter, $aValues); + } + + public static function BulkUpdateTracked(CMDBChange $oChange, DBObjectSearch $oFilter, array $aValues) + { + self::$m_oCurrChange = $oChange; + $this->BulkUpdateTracked_Internal($oFilter, $aValues); + self::$m_oCurrChange = null; + } + + protected static function BulkUpdateTracked_Internal(DBObjectSearch $oFilter, array $aValues) + { + // $aValues is an array of $sAttCode => $value + + // Get the list of objects to update (and load it before doing the change) + $oObjSet = new CMDBObjectSet($oFilter); + $oObjSet->Load(); + + // Update in one single efficient query + $ret = parent::BulkUpdate($oFilter, $aValues); + + // Record... in many queries !!! + while ($oItem = $oObjSet->Fetch()) + { + $aChangedValues = $oItem->ListChangedValues($aValues); + $oItem->RecordAttChanges(self::$m_oCurrChange, $aChangedValues); + } + return $ret; + } +} + + + +/** + * TODO: investigate how to get rid of this class that was made to workaround some language limitation... or a poor design! + * + * @package iTopORM + * @author Romain Quetiez + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ +class CMDBObjectSet extends DBObjectSet +{ + // this is the public interface (?) + + // I have to define those constructors here... :-( + // just to get the right object class in return. + // I have to think again to those things: maybe it will work fine if a have a constructor define here (?) + + static public function FromScratch($sClass) + { + $oFilter = new CMDBSearchFilter($sClass); + $oRetSet = new CMDBObjectSet($oFilter); // THE ONLY DIFF IS HERE + // NOTE: THIS DOES NOT WORK IF m_bLoaded is private... + // BUT IT THAT CASE YOU DO NOT GET ANY ERROR !!!!! + $oRetSet->m_bLoaded = true; // no DB load + return $oRetSet; + } + + static public function FromArray($sClass, $aObjects) + { + $oFilter = new CMDBSearchFilter($sClass); + $oRetSet = new CMDBObjectSet($oFilter); // THE ONLY DIFF IS HERE + // NOTE: THIS DOES NOT WORK IF m_bLoaded is private... + // BUT IT THAT CASE YOU DO NOT GET ANY ERROR !!!!! + $oRetSet->m_bLoaded = true; // no DB load + $oRetSet->AddObjectArray($aObjects); + return $oRetSet; + } + +} + +/** + * TODO: investigate how to get rid of this class that was made to workaround some language limitation... or a poor design! + * + * @package iTopORM + * @author Romain Quetiez + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ +class CMDBSearchFilter extends DBObjectSearch +{ + // this is the public interface (?) +} + + +?> diff --git a/core/cmdbsource.class.inc.php b/core/cmdbsource.class.inc.php new file mode 100644 index 000000000..2f76df24f --- /dev/null +++ b/core/cmdbsource.class.inc.php @@ -0,0 +1,420 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ + +require_once('MyHelpers.class.inc.php'); + +class MySQLException extends CoreException +{ + public function __construct($sIssue, $aContext) + { + $aContext['mysql_error'] = mysql_error(); + parent::__construct($sIssue, $aContext); + } +} + + +class CMDBSource +{ + protected static $m_sDBHost; + protected static $m_sDBUser; + protected static $m_sDBPwd; + protected static $m_sDBName; + protected static $m_resDBLink; + + public static function Init($sServer, $sUser, $sPwd, $sSource = '') + { + self::$m_sDBHost = $sServer; + self::$m_sDBUser = $sUser; + self::$m_sDBPwd = $sPwd; + self::$m_sDBName = $sSource; + if (!self::$m_resDBLink = @mysql_pconnect($sServer, $sUser, $sPwd)) + { + throw new MySQLException('Could not connect to the DB server', array('host'=>$sServer, 'user'=>$sUser)); + } + if (!empty($sSource)) + { + if (!mysql_select_db($sSource, self::$m_resDBLink)) + { + throw new MySQLException('Could not select DB', array('db_name'=>$sSource)); + } + } + } + + public static function ListDB() + { + $aDBs = self::QueryToCol('SHOW DATABASES', 'Database'); + // Show Database does return the DB names in lower case + return $aDBs; + } + + public static function IsDB($sSource) + { + try + { + $aDBs = self::ListDB(); + foreach($aDBs as $sDBName) + { + // perform a case insensitive test because on Windows the table names become lowercase :-( + if (strtolower($sDBName) == strtolower($sSource)) return true; + } + return false; + } + catch(Exception $e) + { + // In case we don't have rights to enumerate the databases + // Let's try to connect directly + return @mysql_select_db($sSource, self::$m_resDBLink); + } + + } + + public static function GetDBVersion() + { + $aVersions = self::QueryToCol('SELECT Version() as version', 'version'); + return $aVersions[0]; + } + + public static function SelectDB($sSource) + { + if (!mysql_select_db($sSource, self::$m_resDBLink)) + { + throw new MySQLException('Could not select DB', array('db_name'=>$sSource)); + } + self::$m_sDBName = $sSource; + } + + public static function CreateDB($sSource) + { + self::Query("CREATE DATABASE `$sSource`"); + self::SelectDB($sSource); + } + + public static function DropDB($sDBToDrop = '') + { + if (empty($sDBToDrop)) + { + $sDBToDrop = self::$m_sDBName; + } + self::Query("DROP DATABASE `$sDBToDrop`"); + if ($sDBToDrop == self::$m_sDBName) + { + self::$m_sDBName = ''; + } + } + + public static function CreateTable($sQuery) + { + $res = self::Query($sQuery); + self::_TablesInfoCacheReset(); // reset the table info cache! + return $res; + } + + public static function DropTable($sTable) + { + $res = self::Query("DROP TABLE `$sTable`"); + self::_TablesInfoCacheReset(true); // reset the table info cache! + return $res; + } + + public static function DBHost() {return self::$m_sDBHost;} + public static function DBUser() {return self::$m_sDBUser;} + public static function DBPwd() {return self::$m_sDBPwd;} + public static function DBName() {return self::$m_sDBName;} + + public static function Quote($value, $bAlways = false, $cQuoteStyle = "'") + { + // Quote variable and protect against SQL injection attacks + // Code found in the PHP documentation: quote_smart($value) + + // bAlways should be set to true when the purpose is to create a IN clause, + // otherwise and if there is a mix of strings and numbers, the clause + // would always be false + + if (is_array($value)) + { + $aRes = array(); + foreach ($value as $key => $itemvalue) + { + $aRes[$key] = self::Quote($itemvalue, $bAlways, $cQuoteStyle); + } + return $aRes; + } + + // Stripslashes + if (get_magic_quotes_gpc()) + { + $value = stripslashes($value); + } + // Quote if not a number or a numeric string + if ($bAlways || !is_numeric($value)) + { + $value = $cQuoteStyle . mysql_real_escape_string($value, self::$m_resDBLink) . $cQuoteStyle; + } + return $value; + } + + public static function Query($sSQLQuery) + { + // Add info into the query as a comment, for easier error tracking + // + //if ($user_contact) $aTraceInf['userID'] = $user_contact->get_key(); + //$aTraceInf['file'] = __FILE__; + if ($_SERVER['REQUEST_URI']) $aTraceInf['requestURI'] = $_SERVER['REQUEST_URI']; + $i = 0; + foreach(debug_backtrace() as $aCallData) + { + $sClass = key_exists("class", $aCallData) ? $aCallData["class"]."::" : ""; + //if ($aCallData['function'] !== 'mysql_simple_query' AND $sClass !== 'r2_set::') + //{ + if ($i == 3) break; + $aTraceInf['function'.$i] = $sClass.$aCallData["function"]." on line ".$aCallData['line']; + $i++; + //} + } + // disabled until we need it really! + // $sSQLQuery = $sSQLQuery.MyHelpers::MakeSQLComment($aTraceInf); + + $mu_t1 = MyHelpers::getmicrotime(); + $result = mysql_query($sSQLQuery, self::$m_resDBLink); + if (!$result) + { + throw new MySQLException('Failed to issue SQL query', array('query' => $sSQLQuery)); + } + $mu_t2 = MyHelpers::getmicrotime(); + // #@# todo - query_trace($sSQLQuery, $mu_t2 - $mu_t1); + + return $result; + } + + public static function GetInsertId() + { + return mysql_insert_id(self::$m_resDBLink); + } + public static function InsertInto($sSQLQuery) + { + if (self::Query($sSQLQuery)) + { + return self::GetInsertId(); + } + return false; + } + + public static function QueryToArray($sSql) + { + $aData = array(); + $result = mysql_query($sSql, self::$m_resDBLink); + if (!$result) + { + throw new MySQLException('Failed to issue SQL query', array('query' => $sSql)); + } + while ($aRow = mysql_fetch_array($result, MYSQL_BOTH)) + { + $aData[] = $aRow; + } + mysql_free_result($result); + return $aData; + } + + public static function QueryToCol($sSql, $col) + { + $aColumn = array(); + $aData = self::QueryToArray($sSql); + foreach($aData as $aRow) + { + @$aColumn[] = $aRow[$col]; + } + return $aColumn; + } + + public static function ExplainQuery($sSql) + { + $aData = array(); + $result = mysql_query("EXPLAIN $sSql", self::$m_resDBLink); + if (!$result) + { + throw new MySQLException('Failed to issue SQL query', array('query' => $sSql)); + } + + $aNames = array(); + for ($i = 0; $i < mysql_num_fields($result) ; $i++) + { + $meta = mysql_fetch_field($result, $i); + if (!$meta) + { + throw new MySQLException('mysql_fetch_field: No information available', array('query'=>$sSql, 'i'=>$i)); + } + else + { + $aNames[] = $meta->name; + } + } + + $aData[] = $aNames; + while ($aRow = mysql_fetch_array($result, MYSQL_ASSOC)) + { + $aData[] = $aRow; + } + mysql_free_result($result); + return $aData; + } + + public static function TestQuery($sSql) + { + $result = mysql_query("EXPLAIN $sSql", self::$m_resDBLink); + if (!$result) + { + return mysql_error(); + } + + mysql_free_result($result); + return ''; + } + + public static function NbRows($result) + { + return mysql_num_rows($result); + } + + public static function FetchArray($result) + { + return mysql_fetch_array($result, MYSQL_ASSOC); + } + + public static function Seek($result, $iRow) + { + return mysql_data_seek($result, $iRow); + } + + public static function FreeResult($result) + { + return mysql_free_result($result); + } + + public static function IsTable($sTable) + { + $aTableInfo = self::GetTableInfo($sTable); + return (!empty($aTableInfo)); + } + + public static function IsKey($sTable, $iKey) + { + $aTableInfo = self::GetTableInfo($sTable); + if (empty($aTableInfo)) return false; + if (!array_key_exists($iKey, $aTableInfo["Fields"])) return false; + $aFieldData = $aTableInfo["Fields"][$iKey]; + if (!array_key_exists("Key", $aFieldData)) return false; + return ($aFieldData["Key"] == "PRI"); + } + + public static function IsAutoIncrement($sTable, $sField) + { + $aTableInfo = self::GetTableInfo($sTable); + if (empty($aTableInfo)) return false; + if (!array_key_exists($sField, $aTableInfo["Fields"])) return false; + $aFieldData = $aTableInfo["Fields"][$sField]; + if (!array_key_exists("Extra", $aFieldData)) return false; + //MyHelpers::debug_breakpoint($aFieldData); + return (strstr($aFieldData["Extra"], "auto_increment")); + } + + public static function IsField($sTable, $sField) + { + $aTableInfo = self::GetTableInfo($sTable); + if (empty($aTableInfo)) return false; + if (!array_key_exists($sField, $aTableInfo["Fields"])) return false; + return true; + } + + public static function IsNullAllowed($sTable, $sField) + { + $aTableInfo = self::GetTableInfo($sTable); + if (empty($aTableInfo)) return false; + if (!array_key_exists($sField, $aTableInfo["Fields"])) return false; + $aFieldData = $aTableInfo["Fields"][$sField]; + return (strtolower($aFieldData["Null"]) == "yes"); + } + + // Returns an array of (fieldname => array of field info) + public static function GetTableFieldsList($sTable) + { + assert(!empty($sTable)); + + $aTableInfo = self::GetTableInfo($sTable); + if (empty($aTableInfo)) return array(); // #@# or an error ? + + return array_keys($aTableInfo["Fields"]); + } + + // Cache the information about existing tables, and their fields + private static $m_aTablesInfo = array(); + private static function _TablesInfoCacheReset() + { + self::$m_aTablesInfo = array(); + } + private static function _TableInfoCacheInit($sTableName) + { + if (isset(self::$m_aTablesInfo[strtolower($sTableName)]) + && (self::$m_aTablesInfo[strtolower($sTableName)] != null)) return; + + try + { + // Check if the table exists + $aFields = self::QueryToArray("SHOW COLUMNS FROM `$sTableName`"); + // Note: without backticks, you get an error with some table names (e.g. "group") + foreach ($aFields as $aFieldData) + { + $sFieldName = $aFieldData["Field"]; + self::$m_aTablesInfo[strtolower($sTableName)]["Fields"][$sFieldName] = + array + ( + "Name"=>$aFieldData["Field"], + "Type"=>$aFieldData["Type"], + "Null"=>$aFieldData["Null"], + "Key"=>$aFieldData["Key"], + "Default"=>$aFieldData["Default"], + "Extra"=>$aFieldData["Extra"] + ); + } + } + catch(MySQLException $e) + { + // Table does not exist + self::$m_aTablesInfo[strtolower($sTableName)] = null; + } + } + //public static function EnumTables() + //{ + // self::_TablesInfoCacheInit(); + // return array_keys(self::$m_aTablesInfo); + //} + public static function GetTableInfo($sTable) + { + self::_TableInfoCacheInit($sTable); + + // perform a case insensitive match because on Windows the table names become lowercase :-( + //foreach(self::$m_aTablesInfo as $sTableName => $aInfo) + //{ + // if (strtolower($sTableName) == strtolower($sTable)) + // { + // return $aInfo; + // } + //} + return self::$m_aTablesInfo[strtolower($sTable)]; + //return null; + } +} + + +?> diff --git a/core/config.class.inc.php b/core/config.class.inc.php new file mode 100644 index 000000000..94bfe8717 --- /dev/null +++ b/core/config.class.inc.php @@ -0,0 +1,272 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ +class ConfigException extends CoreException +{ +} + +class Config +{ + //protected $m_bIsLoaded = false; + protected $m_sFile = ''; + + protected $m_aAppModules; + protected $m_aDataModels; + protected $m_aAddons; + + protected $m_sDBHost; + protected $m_sDBUser; + protected $m_sDBPwd; + protected $m_sDBName; + protected $m_sDBSubname; + + public function __construct($sConfigFile, $bLoadConfig = true) + { + $this->m_sFile = $sConfigFile; + $this->m_aAppModules = array(); + $this->m_aDataModels = array(); + $this->m_aAddons = array(); + + $this->m_sDBHost = ''; + $this->m_sDBUser = ''; + $this->m_sDBPwd = ''; + $this->m_sDBName = ''; + $this->m_sDBSubname = ''; + if ($bLoadConfig) + { + $this->Load($sConfigFile); + $this->Verify(); + } + } + + protected function CheckFile($sPurpose, $sFileName) + { + if (!file_exists($sFileName)) + { + throw new ConfigException("Could not find $sPurpose file", array('file' => $sFileName)); + } + } + + protected function Load($sConfigFile) + { + $this->CheckFile('configuration', $sConfigFile); + + $sConfigCode = trim(file_get_contents($sConfigFile)); + + // This does not work on several lines + // preg_match('/^<\\?php(.*)\\?'.'>$/', $sConfigCode, $aMatches)... + // So, I've implemented a solution suggested in the PHP doc (search for phpWrapper) + try + { + ob_start(); + $sCode = str_replace('<'.'?php','<'.'?', $sConfigCode); + eval('?'.'>'.trim($sCode).'<'.'?'); + $sNoise = trim(ob_get_contents()); + ob_end_clean(); + } + catch (Exception $e) + { + // well, never reach in case of parsing error :-( + // will be improved in PHP 6 ? + throw new ConfigException('Error in configuration file', array('file' => $sConfigFile, 'error' => $e->getMessage())); + } + if (strlen($sNoise) > 0) + { + // Note: sNoise is an html output, but so far it was ok for me (e.g. showing the entire call stack) + throw new ConfigException('Syntax error in configuration file', array('file' => $sConfigFile, 'error' => $sNoise)); + } + + if (!isset($MySettings) || !is_array($MySettings)) + { + throw new ConfigException('Missing array in configuration file', array('file' => $sConfigFile, 'expected' => '$MySettings')); + } + if (!isset($MyModules) || !is_array($MyModules)) + { + throw new ConfigException('Missing item in configuration file', array('file' => $sConfigFile, 'expected' => '$MyModules')); + } + if (!array_key_exists('application', $MyModules)) + { + throw new ConfigException('Missing item in configuration file', array('file' => $sConfigFile, 'expected' => '$MyModules[\'application\']')); + } + if (!array_key_exists('business', $MyModules)) + { + throw new ConfigException('Missing item in configuration file', array('file' => $sConfigFile, 'expected' => '$MyModules[\'business\']')); + } + if (!array_key_exists('addons', $MyModules)) + { + throw new ConfigException('Missing item in configuration file', array('file' => $sConfigFile, 'expected' => '$MyModules[\'addons\']')); + } + if (!array_key_exists('user rights', $MyModules['addons'])) + { + $MyModules['addons']['user rights'] = '../addons/userrights/userrightsnull.class.inc.php'; + } + $this->m_aAppModules = $MyModules['application']; + $this->m_aDataModels = $MyModules['business']; + $this->m_aAddons = $MyModules['addons']; + + $this->m_sDBHost = trim($MySettings['db_host']); + $this->m_sDBUser = trim($MySettings['db_user']); + $this->m_sDBPwd = trim($MySettings['db_pwd']); + $this->m_sDBName = trim($MySettings['db_name']); + $this->m_sDBSubname = trim($MySettings['db_subname']); + } + + protected function Verify() + { + foreach ($this->m_aAppModules as $sModule => $sToInclude) + { + $this->CheckFile('application module', $sToInclude); + } + foreach ($this->m_aDataModels as $sModule => $sToInclude) + { + $this->CheckFile('business model', $sToInclude); + } + foreach ($this->m_aAddons as $sModule => $sToInclude) + { + $this->CheckFile('addon module', $sToInclude); + } + } + + public function GetAppModules() + { + return $this->m_aAppModules; + } + + public function GetDataModels() + { + return $this->m_aDataModels; + } + + public function GetAddons() + { + return $this->m_aAddons; + } + + public function GetDBHost() + { + return $this->m_sDBHost; + } + + public function GetDBName() + { + return $this->m_sDBName; + } + + public function GetDBSubname() + { + return $this->m_sDBSubname; + } + + public function GetDBUser() + { + return $this->m_sDBUser; + } + + public function GetDBPwd() + { + return $this->m_sDBPwd; + } + + public function SetDBHost($sDBHost) + { + $this->m_sDBHost = $sDBHost; + } + + public function SetDBName($sDBName) + { + $this->m_sDBName = $sDBName; + } + + public function SetDBSubname($sDBSubName) + { + $this->m_sDBSubname = $sDBSubName; + } + + public function SetDBUser($sUser) + { + $this->m_sDBUser = $sUser; + } + + public function SetDBPwd($sPwd) + { + $this->m_sDBPwd = $sPwd; + } + public function FileIsWritable() + { + return is_writable($this->m_sFile); + } + + /** + * Write the configuration to a file (php format) that can be reloaded later + * By default write to the same file that was specified when constructing the object + * @param $sFileName string Name of the file to write to (emtpy to write to the same file) + * @return boolean True otherwise throws an Exception + */ + public function WriteToFile($sFileName = '') + { + if (empty($sFileName)) + { + $sFileName = $this->m_sFile; + } + $hFile = @fopen($sFileName, 'w'); + if ($hFile !== false) + { + fwrite($hFile, " '{$this->m_sDBHost}',\n"); + fwrite($hFile, "\t'db_user' => '{$this->m_sDBUser}',\n"); + fwrite($hFile, "\t'db_pwd' => '{$this->m_sDBPwd}',\n"); + fwrite($hFile, "\t'db_name' => '{$this->m_sDBName}',\n"); + fwrite($hFile, "\t'db_subname' => '{$this->m_sDBSubname}',\n"); + fwrite($hFile, ");\n"); + + fwrite($hFile, "\n/**\n"); + fwrite($hFile, " *\n"); + fwrite($hFile, " * Data model modules to be loaded. Names should be specified as absolute paths\n"); + fwrite($hFile, " *\n"); + fwrite($hFile, " */\n"); + fwrite($hFile, "\$MyModules = array(\n"); + fwrite($hFile, "\t'application' => array (\n"); + fwrite($hFile, "\t\t'../application/menunode.class.inc.php',\n"); + fwrite($hFile, "\t\t'../application/audit.rule.class.inc.php',\n"); + fwrite($hFile, "\t\t// to be continued...\n"); + fwrite($hFile, "\t),\n"); + fwrite($hFile, "\t'business' => array (\n"); + fwrite($hFile, "\t\t'../business/itop.business.class.inc.php'\n"); + fwrite($hFile, "\t\t// to be continued...\n"); + fwrite($hFile, "\t),\n"); + fwrite($hFile, "\t'addons' => array (\n"); + fwrite($hFile, "\t\t'user rights' => '../addons/userrights/userrightsmatrix.class.inc.php',\n"); + fwrite($hFile, "\t\t// other modules to come later\n"); + fwrite($hFile, "\t)\n"); + fwrite($hFile, ");\n"); + fwrite($hFile, '?'.'>'); // Avoid perturbing the syntax highlighting ! + return fclose($hFile); + } + else + { + throw new ConfigException("Could not write to configuration file", array('file' => $sFileName)); + } + } +} +?> diff --git a/core/coreexception.class.inc.php b/core/coreexception.class.inc.php new file mode 100644 index 000000000..eb5ae8b9d --- /dev/null +++ b/core/coreexception.class.inc.php @@ -0,0 +1,63 @@ +m_sIssue = $sIssue; + $this->m_sImpact = $sImpact; + $this->m_aContextData = $aContextData ? $aContextData : array(); + + $sMessage = $sIssue; + if (!empty($sImpact)) $sMessage .= "($sImpact)"; + if (count($this->m_aContextData) > 0) + { + $sMessage .= ": "; + $aContextItems = array(); + foreach($this->m_aContextData as $sKey => $value) + { + if (is_array($value)) + { + $aPairs = array(); + foreach($value as $key => $val) + { + if (is_array($val)) + { + $aPairs[$key] = '('.implode(', ', $val).')'; + } + else + { + $aPairs[$key] = $val; + } + } + $sValue = '{'.implode('; ', $aPairs).'}'; + } + else + { + $sValue = $value; + } + $aContextItems[] = "$sKey = $sValue"; + } + $sMessage .= implode(', ', $aContextItems); + } + parent::__construct($sMessage, 0); + } + + public function getHtmlDesc($sHighlightHtmlBegin = '', $sHighlightHtmlEnd = '') + { + return $this->getMessage(); + } + + public function getTraceAsHtml() + { + $aBackTrace = $this->getTrace(); + return MyHelpers::get_callstack_html(0, $this->getTrace()); + // return "
    \n".$this->getTraceAsString()."
    \n"; + } +} + +class CoreWarning extends CoreException +{ +} + +?> diff --git a/core/csvparser.class.inc.php b/core/csvparser.class.inc.php new file mode 100644 index 000000000..c1e49f10f --- /dev/null +++ b/core/csvparser.class.inc.php @@ -0,0 +1,192 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ + +class CSVParserException extends CoreException +{ +} + + + + +/** + * CSVParser + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class CSVParser +{ + private $m_sCSVData; + private $m_sSep; + private $m_iSkip; + + public function __construct($sTxt) + { + $this->m_sCSVData = $sTxt; + } + + public function SetSeparator($sSep) + { + $this->m_sSep = $sSep; + } + public function GetSeparator() + { + return $this->m_sSep; + } + + public function SetSkipLines($iSkip) + { + $this->m_iSkip = $iSkip; + } + public function GetSkipLines() + { + return $this->m_iSkip; + } + + public function GuessSeparator() + { + // Note: skip the first line anyway + + $aKnownSeps = array(';', ',', "\t"); // Use double quote for special chars!!! + $aStatsBySeparator = array(); + foreach ($aKnownSeps as $sSep) + { + $aStatsBySeparator[$sSep] = array(); + } + + foreach(split("\n", $this->m_sCSVData) as $sLine) + { + $sLine = trim($sLine); + if (substr($sLine, 0, 1) == '#') continue; + if (empty($sLine)) continue; + + $aLineCharsCount = count_chars($sLine, 0); + foreach ($aKnownSeps as $sSep) + { + $aStatsBySeparator[$sSep][] = $aLineCharsCount[ord($sSep)]; + } + } + + // Default to ',' + $this->SetSeparator(","); + + foreach ($aKnownSeps as $sSep) + { + // Note: this function is NOT available :-( + // stats_variance($aStatsBySeparator[$sSep]); + $iMin = min($aStatsBySeparator[$sSep]); + $iMax = max($aStatsBySeparator[$sSep]); + if (($iMin == $iMax) && ($iMax > 0)) + { + $this->SetSeparator($sSep); + break; + } + } + return $this->GetSeparator(); + } + + public function GuessSkipLines() + { + // Take the FIRST -valuable- LINE ONLY + // If there is a number, then for sure this is not a header line + // Otherwise, we may consider that there is one line to skip + foreach(split("\n", $this->m_sCSVData) as $sLine) + { + $sLine = trim($sLine); + if (substr($sLine, 0, 1) == '#') continue; + if (empty($sLine)) continue; + + foreach (split($this->m_sSep, $sLine) as $value) + { + if (is_numeric($value)) + { + $this->SetSkipLines(0); + return 0; + } + } + $this->SetSkipLines(1); + return 1; + } + } + + function ToArray($aFieldMap = null, $iMax = 0) + { + // $aFieldMap is an array of col_index=>col_name + // $iMax is to limit the count of rows computed + $aRes = array(); + + $iCount = 0; + $iSkipped = 0; + foreach(split("\n", $this->m_sCSVData) as $sLine) + { + $sLine = trim($sLine); + if (substr($sLine, 0, 1) == '#') continue; + if (empty($sLine)) continue; + + if ($iSkipped < $this->m_iSkip) + { + $iSkipped++; + continue; + } + + foreach (split($this->m_sSep, $sLine) as $iCol=>$sValue) + { + if (is_array($aFieldMap)) $sColRef = $aFieldMap[$iCol]; + else $sColRef = $iCol; + $aRes[$iCount][$sColRef] = $sValue; + } + + $iCount++; + if (($iMax > 0) && ($iCount >= $iMax)) break; + } + return $aRes; + } + + public function ListFields() + { + // Take the first valuable line + foreach(explode("\n", $this->m_sCSVData) as $sLine) + { + $sLine = trim($sLine); + if (substr($sLine, 0, 1) == '#') continue; + if (empty($sLine)) continue; + // We've got the first valuable line, that's it! + break; + } + + $aRet = array(); + foreach (explode($this->m_sSep, $sLine) as $iCol=>$value) + { + if ($this->m_iSkip == 0) + { + // No header to help us + $sLabel = "field $iCol"; + } + else + { + $sLabel = "$value"; + } + $aRet[] = $sLabel; + } + return $aRet; + } +} + + +?> diff --git a/core/data.generator.class.inc.php b/core/data.generator.class.inc.php new file mode 100644 index 000000000..f9d284e3e --- /dev/null +++ b/core/data.generator.class.inc.php @@ -0,0 +1,362 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ + +/** + * Data Generator helper class + * + * This class is useful to generate a lot of sample data that look consistent + * for a given organization in order to simulate a real CMDB + */ +class cmdbDataGenerator +{ + protected $m_sOrganizationKey; + protected $m_sOrganizationCode; + protected $m_sOrganizationName; + protected $m_OrganizationDomains; + + /** + * Constructor + */ + public function __construct($sOrganizationId = "") + { + global $aCompanies, $aCompaniesCode; + if ($sOrganizationId == '') + { + // No organization provided, pick a random and unused one from our predefined list + $retries = 5*count($aCompanies); + while ( ($retries > 0) && !isset($this->m_sOrganizationCode)) // Stupid algorithm, but I'm too lazy to do something bulletproof tonight + { + $index = rand(0, count($aCompanies) - 1); + if (!$this->OrganizationExists($aCompanies[$index]['code'])) + { + $this->m_sOrganizationCode = $aCompanies[$index]['code']; + $this->m_sOrganizationName = $aCompanies[$index]['name']; + $this->m_OrganizationDomains = $aCompanies[$index]['domain']; + } + $retries--; + } + } + else + { + // A code has been provided, let's take the information we need from the organization itself + $this->m_sOrganizationId = $sOrganizationId; + $oOrg = $this->GetOrganization($sOrganizationId); + if ($oOrg == null) + { + echo "Unable to find the organization '$sOrganisationCode' in the database... can not add objects into this organization.
    \n"; + exit(); + } + $this->m_sOrganizationCode = $oOrg->Get('code'); + $this->m_sOrganizationName = $oOrg->Get('name'); + if (!isset($aCompaniesCode[$this->m_sOrganizationCode]['domain'])) + { + // Generate some probable domain names for this organization + $this->m_OrganizationDomains = array(strtolower($this->m_sOrganizationCode).".com", strtolower($this->m_sOrganizationCode).".org", strtolower($this->m_sOrganizationCode)."corp.net",); + } + else + { + // Pick the domain names for this organization from the predefined list + $this->m_OrganizationDomains = $aCompaniesCode[$this->m_sOrganizationCode]['domain']; + } + } + + if (!isset($this->m_sOrganizationCode)) + { + echo "Unable to find an organization code which is not already used... can not create a new organization. Enhance the list of fake organizations (\$aCompanies in data_sample.inc.php).
    \n"; + exit(); + } + } + + /** + * Get the current organization id used by the generator + * + * @return string The organization id + */ + public function GetOrganizationId() + { + return $this->m_sOrganizationId; + } + + /** + * Get the current organization id used by the generator + * + * @param string The organization id + * @return none + */ + public function SetOrganizationId($sId) + { + $this->m_sOrganizationId = $sId; + } + + /** + * Get the current organization code used by the generator + * + * @return string The organization code + */ + public function GetOrganizationCode() + { + return $this->m_sOrganizationCode; + } + + /** + * Get the current organization name used by the generator + * + * @return string The organization name + */ + function GetOrganizationName() + { + return $this->m_sOrganizationName; + } + + /** + * Get a pseudo random first name taken from a (big) prefedined list + * + * @return string A random first name + */ + function GenerateFirstName() + { + global $aFirstNames; + return $aFirstNames[rand(0, count($aFirstNames) - 1)]; + } + + /** + * Get a pseudo random last name taken from a (big) prefedined list + * + * @return string A random last name + */ + function GenerateLastName() + { + global $aNames; + return $aNames[rand(0, count($aNames) - 1)]; + } + + /** + * Get a pseudo random country name taken from a prefedined list + * + * @return string A random city name + */ + function GenerateCountryName() + { + global $aCountries; + return $aCountries[rand(0, count($aCountries) - 1)]; + } + + /** + * Get a pseudo random city name taken from a (big) prefedined list + * + * @return string A random city name + */ + function GenerateCityName() + { + global $aCities; + return $aCities[rand(0, count($aCities) - 1)]; + } + + /** + * Get a pseudo random email address made of the first name, last name and organization's domain + * + * @return string A random email address + */ + function GenerateEmail($sFirstName, $sLastName) + { + if (rand(1, 20) > 18) + { + // some people (let's say 5~10%) have an irregular email address + $sEmail = strtolower($this->CleanForEmail($sLastName))."@".strtolower($this->GenerateDomain()); + } + else + { + $sEmail = strtolower($this->CleanForEmail($sFirstName)).".".strtolower($this->CleanForEmail($sLastName))."@".strtolower($this->GenerateDomain()); + } + return $sEmail; + } + + /** + * Generate (pseudo) random strings that follow a given pattern + * + * The template is made of any number of 'parts' separated by pipes '|' + * Each part is either: + * - domain() => returns a domain name for the current organization + * - enum(aaa,bb,c,dddd) => returns randomly one of aaa,bb,c or dddd with the same + * probability of occurence. If you want to change the probability you can repeat some values + * i.e enum(most probable,most probable,most probable,most probable,most probable,rare) + * - number(xxx-yyy) => a random number between xxx and yyy (bounds included) + * note that if the first number (xxx) begins with a zero, then the result will zero padded + * to the same number of digits as xxx. + * All other 'part' that does not follow one of the above mentioned pattern is returned as is + * + * Example: GenerateString("enum(sw,rtr,gw)|number(00-99)|.|domain()") + * will produce strings like "sw01.netcmdb.com" or "rtr45.itop.org" + * + * @param string $sTemplate The template used for generating the string + * @return string The generated pseudo random the string + */ + function GenerateString($sTemplate) + { + $sResult = ""; + $aParts = split("\|", $sTemplate); + foreach($aParts as $sPart) + { + if (preg_match("/domain\(\)/", $sPart, $aMatches)) + { + $sResult .= strtolower($this->GenerateDomain()); + } + elseif (preg_match("/enum\((.+)\)/", $sPart, $aMatches)) + { + $sEnumValues = $aMatches[1]; + $aEnumValues = split(",", $sEnumValues); + $sResult .= $aEnumValues[rand(0, count($aEnumValues) - 1)]; + } + elseif (preg_match("/number\((\d+)-(\d+)\)/", $sPart, $aMatches)) + { + $sStartNumber = $aMatches[1]; + if ($sStartNumber[0] == '0') + { + // number must be zero padded + $sFormat = "%0".strlen($sStartNumber)."d"; + } + else + { + $sFormat = "%d"; + } + $sEndNumber = $aMatches[2]; + $sResult .= sprintf($sFormat, rand($sStartNumber, $sEndNumber)); + } + else + { + $sResult .= $sPart; + } + } + return $sResult; + } + + /** + * Generate a foreign key by picking a random element of the given class in a set limited by the given search criteria + * + * Example: GenerateKey("bizLocation", array('org_id', $oGenerator->GetOrganizationId()); + * will produce the foreign key of a Location object picked at random in the same organization + * + * @param string $sClass The name of the class to search for + * @param string $aFilterCriteria A hash array of filterCOde => FilterValue (the strict operator '=' is used ) + * @return mixed The key to an object of the given class, or null if none are found + */ + function GenerateKey($sClass, $aFilterCriteria) + { + $retKey = null; + $oFilter = new CMDBSearchFilter($sClass); + foreach($aFilterCriteria as $sFilterCode => $filterValue) + { + $oFilter->AddCondition($sFilterCode, $filterValue, '='); + } + $oSet = new CMDBObjectSet($oFilter); + if ($oSet->Count() > 0) + { + $max_count = $index = rand(1, $oSet->Count()); + do + { + $oObj = $oSet->Fetch(); + $index--; + } + while($index > 0); + + if (!is_object($oObj)) + { + echo "
    ";
    +				echo "ERROR: non empty set, but invalid object picked! class='$sClass'\n";
    +				echo "Index chosen: $max_count\n";
    +				echo "The set is supposed to contain ".$oSet->Count()." object(s)\n";
    +				echo "Filter criteria:\n";
    +				print_r($aFilterCriteria);
    +				echo "
    "; + } + else + { + $retKey = $oObj->GetKey(); + } + } + return $retKey; + } + /////////////////////////////////////////////////////////////////////////////// + // + // Protected methods + // + /////////////////////////////////////////////////////////////////////////////// + + /** + * Generate a (random) domain name consistent with the organization name & code + * + * The values are pulled from a (limited) predefined list. Note that a given + * organization may have several domain names, so the result may be random + * + * @return string A domain name (like netcnmdb.com) + */ + protected function GenerateDomain() + { + if (is_array($this->m_OrganizationDomains)) + { + $sDomain = $this->m_OrganizationDomains[rand(0, count($this->m_OrganizationDomains)-1)]; + } + else + { + $sDomain = $this->m_OrganizationDomains; + } + return $sDomain; + } + + /** + * Strips accented characters from a string in order to produce a suitable email address + * + * @param string The text string to clean + * @return string The cleanified text string + */ + protected function CleanForEmail($sText) + { + return str_replace(array("'", "é", "è", "ê", "ç", "à", "â", "ñ", "ö", "ä"), array("", "e", "e", "e", "c", "a", "a", "n", "oe", "ae"), $sText); + } + + /** + * Check if an organization with the given code already exists in the database + * + * @param string $sCode The code to look for + * @return boolean true if the given organization exists, false otherwise + */ + protected function OrganizationExists($sCode) + { + $oFilter = new CMDBSearchFilter('bizOrganization'); + $oFilter->AddCondition('code', $sCode, '='); + $oSet = new CMDBObjectSet($oFilter); + return ($oSet->Count() > 0); + } + + /** + * Search for an organization with the given code in the database + * + * @param string $Id The organization Id to look for + * @return cmdbOrganization the organization if it exists, null otherwise + */ + protected function GetOrganization($sId) + { + $oOrg = null; + $oFilter = new CMDBSearchFilter('bizOrganization'); + $oFilter->AddCondition('pkey', $sId, '='); + $oSet = new CMDBObjectSet($oFilter); + if ($oSet->Count() > 0) + { + $oOrg = $oSet->Fetch(); // Let's take the first one found + } + return $oOrg; + } +} +?> diff --git a/core/dbobject.class.php b/core/dbobject.class.php new file mode 100644 index 000000000..c9d69a0b9 --- /dev/null +++ b/core/dbobject.class.php @@ -0,0 +1,831 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ + +require_once('metamodel.class.php'); + + +/** + * A persistent object, as defined by the metamodel + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +abstract class DBObject +{ + private static $m_aMemoryObjectsByClass = array(); + + private $m_bIsInDB = false; // true IIF the object is mapped to a DB record + private $m_iKey = null; + private $m_aCurrValues = array(); + private $m_aOrigValues = array(); + + private $m_bFullyLoaded = false; // Compound objects can be partially loaded + private $m_aLoadedAtt = array(); // Compound objects can be partially loaded, array of sAttCode + + // Use the MetaModel::NewObject to build an object (do we have to force it?) + public function __construct($aRow = null) + { + if (!empty($aRow)) + { + $this->FromRow($aRow); + $this->m_bFullyLoaded = $this->IsFullyLoaded(); + return; + } + // Creation of brand new object + // + + $this->m_iKey = self::GetNextTempId(get_class($this)); + + // set default values + foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef) + { + $this->m_aCurrValues[$sAttCode] = $oAttDef->GetDefaultValue(); + $this->m_aOrigValues[$sAttCode] = null; + if ($oAttDef->IsExternalField()) + { + // This field has to be read from the DB + $this->m_aLoadedAtt[$sAttCode] = false; + } + else + { + // No need to trigger a reload for that attribute + // Let's consider it as being already fully loaded + $this->m_aLoadedAtt[$sAttCode] = true; + } + } + } + + public function IsNew() + { + return (!$this->m_bIsInDB); + } + + // Returns an Id for memory objects + static protected function GetNextTempId($sClass) + { + if (!array_key_exists($sClass, self::$m_aMemoryObjectsByClass)) + { + self::$m_aMemoryObjectsByClass[$sClass] = 0; + } + self::$m_aMemoryObjectsByClass[$sClass]++; + return (- self::$m_aMemoryObjectsByClass[$sClass]); + } + + public function __toString() + { + $sRet = ''; + $sClass = get_class($this); + $sRootClass = MetaModel::GetRootClass($sClass); + $iPKey = $this->GetKey(); + $sRet .= "$sClass::$iPKey
    \n"; + $sRet .= "
      \n"; + foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef) + { + $sRet .= "
    • ".$oAttDef->GetLabel()." = ".$this->GetAsHtml($sAttCode)."
    • \n"; + } + $sRet .= "
    "; + return $sRet; + } + + // Restore initial values... mmmm, to be discussed + public function DBRevert() + { + $this->Reload(); + } + + protected function IsFullyLoaded() + { + foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef) + { + @$bIsLoaded = $this->m_aLoadedAtt[$sAttCode]; + if ($bIsLoaded !== true) + { + return false; + } + } + return true; + } + + protected function Reload() + { + assert($this->m_bIsInDB); + $aRow = MetaModel::MakeSingleRow(get_class($this), $this->m_iKey); + if (empty($aRow)) + { + throw new CoreException("Failed to reload object of class '".get_class($this)."', id = ".$this->m_iKey); + } + $this->FromRow($aRow); + + // Process linked set attributes + // + foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef) + { + if (!$oAttDef->IsLinkSet()) continue; + + // Load the link information + $sLinkClass = $oAttDef->GetLinkedClass(); + $sExtKeyToMe = $oAttDef->GetExtKeyToMe(); + + // The class to target is not the current class, because if this is a derived class, + // it may differ from the target class, then things start to become confusing + $oRemoteExtKeyAtt = MetaModel::GetAttributeDef($sLinkClass, $sExtKeyToMe); + $sMyClass = $oRemoteExtKeyAtt->GetTargetClass(); + + $oMyselfSearch = new DBObjectSearch($sMyClass); + $oMyselfSearch->AddCondition('id', $this->m_iKey, '='); + + $oLinkSearch = new DBObjectSearch($sLinkClass); + $oLinkSearch->AddCondition_PointingTo($oMyselfSearch, $sExtKeyToMe); + $oLinks = new DBObjectSet($oLinkSearch); + + $this->m_aCurrValues[$sAttCode] = $oLinks; + $this->m_aOrigValues[$sAttCode] = clone $this->m_aCurrValues[$sAttCode]; + $this->m_aLoadedAtt[$sAttCode] = true; + } + + $this->m_bFullyLoaded = true; + } + + protected function FromRow($aRow) + { + $this->m_iKey = null; + $this->m_bIsInDB = true; + $this->m_aCurrValues = array(); + $this->m_aOrigValues = array(); + $this->m_aLoadedAtt = array(); + + // Get the key + // + $sKeyField = "id"; + if (!array_key_exists($sKeyField, $aRow)) + { + // #@# Bug ? + throw new CoreException("Missing key for class '".get_class($this)."'"); + } + else + { + $iPKey = $aRow[$sKeyField]; + if (!self::IsValidPKey($iPKey)) + { + throw new CoreWarning("An object id must be an integer value ($iPKey)"); + } + $this->m_iKey = $iPKey; + } + + // Build the object from an array of "attCode"=>"value") + // + $bFullyLoaded = true; // ... set to false if any attribute is not found + foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef) + { + // Say something, whatever the type of attribute + $this->m_aLoadedAtt[$sAttCode] = false; + + // Skip links (could not be loaded by the mean of this query) + if ($oAttDef->IsLinkSet()) continue; + + if (array_key_exists($sAttCode, $aRow)) + { + $sValue = $oAttDef->SQLValueToRealValue($aRow[$sAttCode]); + $this->m_aCurrValues[$sAttCode] = $sValue; + $this->m_aOrigValues[$sAttCode] = $sValue; + $this->m_aLoadedAtt[$sAttCode] = true; + } + else + { + // This attribute was expected and not found in the query columns + $bFullyLoaded = false; + } + } + return $bFullyLoaded; + } + + public function Set($sAttCode, $value) + { + if (!array_key_exists($sAttCode, MetaModel::ListAttributeDefs(get_class($this)))) + { + throw new CoreException("Unknown attribute code '$sAttCode' for the class ".get_class($this)); + } + $oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode); + if ($this->m_bIsInDB && !$this->m_bFullyLoaded) + { + // First time Set is called... ensure that the object gets fully loaded + // Otherwise we would lose the values on a further Reload + // + consistency does not make sense ! + $this->Reload(); + } + if($oAttDef->IsScalar() && !$oAttDef->IsNullAllowed() && is_null($value)) + { + throw new CoreWarning("null not allowed for attribute '$sAttCode', setting default value"); + $this->m_aCurrValues[$sAttCode] = $oAttDef->GetDefaultValue(); + return; + } + if ($oAttDef->IsExternalKey() && is_object($value)) + { + // Setting an external key with a whole object (instead of just an ID) + // let's initialize also the external fields that depend on it + // (useful when building objects in memory and not from a query) + if ( (get_class($value) != $oAttDef->GetTargetClass()) && (!is_subclass_of($value, $oAttDef->GetTargetClass()))) + { + throw new CoreWarning("Trying to set the value of '$sAttCode', to an object of class '".get_class($value)."', whereas it's an ExtKey to '".$oAttDef->GetTargetClass()."'. Ignored"); + $this->m_aCurrValues[$sAttCode] = $oAttDef->GetDefaultValue(); + } + else + { + $this->m_aCurrValues[$sAttCode] = $value->GetKey(); + foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sCode => $oDef) + { + if ($oDef->IsExternalField() && ($oDef->GetKeyAttCode() == $sAttCode)) + { + $this->m_aCurrValues[$sCode] = $value->Get($oDef->GetExtAttCode()); + } + } + } + return; + } + if(!$oAttDef->IsScalar() && !is_object($value)) + { + throw new CoreWarning("scalar not allowed for attribute '$sAttCode', setting default value (empty list)"); + $this->m_aCurrValues[$sAttCode] = $oAttDef->GetDefaultValue(); + return; + } + if($oAttDef->IsLinkSet()) + { + if((get_class($value) != 'DBObjectSet') && !is_subclass_of($value, 'DBObjectSet')) + { + throw new CoreWarning("expecting a set of persistent objects (found a '".get_class($value)."'), setting default value (empty list)"); + $this->m_aCurrValues[$sAttCode] = $oAttDef->GetDefaultValue(); + return; + } + + $oObjectSet = $value; + $sSetClass = $oObjectSet->GetClass(); + $sLinkClass = $oAttDef->GetLinkedClass(); + // not working fine :-( if (!is_subclass_of($sSetClass, $sLinkClass)) + if ($sSetClass != $sLinkClass) + { + throw new CoreWarning("expecting a set of '$sLinkClass' objects (found a set of '$sSetClass'), setting default value (empty list)"); + $this->m_aCurrValues[$sAttCode] = $oAttDef->GetDefaultValue(); + return; + } + } + $this->m_aCurrValues[$sAttCode] = $oAttDef->MakeRealValue($value); + } + + public function Get($sAttCode) + { + if (!array_key_exists($sAttCode, MetaModel::ListAttributeDefs(get_class($this)))) + { + throw new CoreException("Unknown attribute code '$sAttCode' for the class ".get_class($this)); + } + if ($this->m_bIsInDB && !$this->m_aLoadedAtt[$sAttCode]) + { + // #@# non-scalar attributes.... handle that differentely + $this->Reload(); + } + $this->ComputeFields(); + return $this->m_aCurrValues[$sAttCode]; + } + + public function GetOriginal($sAttCode) + { + if (!array_key_exists($sAttCode, MetaModel::ListAttributeDefs(get_class($this)))) + { + throw new CoreException("Unknown attribute code '$sAttCode' for the class ".get_class($this)); + } + return $this->m_aOrigValues[$sAttCode]; + } + + public function ComputeFields() + { + if (is_callable(array($this, 'ComputeValues'))) + { + // First check that we are not currently computing the fields + // (yes, we need to do some things like Set/Get to compute the fields which will in turn trigger the update...) + foreach (debug_backtrace() as $aCallInfo) + { + if (!array_key_exists("class", $aCallInfo)) continue; + if ($aCallInfo["class"] != get_class($this)) continue; + if ($aCallInfo["function"] != "ComputeValues") continue; + return; //skip! + } + + $this->ComputeValues(); + } + } + + public function GetAsHTML($sAttCode) + { + $sClass = get_class($this); + $oAtt = MetaModel::GetAttributeDef($sClass, $sAttCode); + + $aExtKeyFriends = MetaModel::GetExtKeyFriends($sClass, $sAttCode); + if (count($aExtKeyFriends) > 0) + { + // This attribute is an ext key (in this class or in another class) + // The corresponding value is an id of the remote object + // Let's try to use the corresponding external fields for a sexy display + + $aAvailableFields = array(); + foreach ($aExtKeyFriends as $sDispAttCode => $oExtField) + { + $aAvailableFields[$oExtField->GetExtAttCode()] = $oExtField->GetAsHTML($this->Get($oExtField->GetCode())); + } + + $sTargetClass = $oAtt->GetTargetClass(EXTKEY_ABSOLUTE); + $aMakeHLink = array(get_class($this), 'MakeHyperLink'); + if (is_callable($aMakeHLink)) + { + return call_user_func($aMakeHLink, $sTargetClass, $this->Get($sAttCode), $aAvailableFields); + } + else + { + return $this->Get($sAttCode); + } + } + + // That's a standard attribute (might be an ext field or a direct field, etc.) + return $oAtt->GetAsHTML($this->Get($sAttCode)); + } + + public function GetAsXML($sAttCode) + { + $oAtt = MetaModel::GetAttributeDef(get_class($this), $sAttCode); + return $oAtt->GetAsXML($this->Get($sAttCode)); + } + + public function GetAsCSV($sAttCode, $sSeparator = ';', $sSepEscape = ',') + { + $oAtt = MetaModel::GetAttributeDef(get_class($this), $sAttCode); + return $oAtt->GetAsCSV($this->Get($sAttCode), $sSeparator, $sSepEscape); + } + + // could be in the metamodel ? + public static function IsValidPKey($value) + { + return ((string)$value === (string)(int)$value); + } + + public function GetKey() + { + return $this->m_iKey; + } + public function SetKey($iNewKey) + { + if (!self::IsValidPKey($iNewKey)) + { + throw new CoreException("An object id must be an integer value ($iNewKey)"); + } + + if ($this->m_bIsInDB && !empty($this->m_iKey) && ($this->m_iKey != $iNewKey)) + { + throw new CoreException("Changing the key ({$this->m_iKey} to $iNewKey) on an object (class {".get_class($this).") wich already exists in the Database"); + } + $this->m_iKey = $iNewKey; + } + + public function GetName() + { + $sNameAttCode = MetaModel::GetNameAttributeCode(get_class($this)); + if (empty($sNameAttCode)) + { + return $this->m_iKey; + } + else + { + return $this->Get($sNameAttCode); + } + } + + public function GetState() + { + $sStateAttCode = MetaModel::GetStateAttributeCode(get_class($this)); + if (empty($sStateAttCode)) + { + return ''; + } + else + { + $aStates = MetaModel::EnumStates(get_class($this)); + return $aStates[$this->Get($sStateAttCode)]['label']; + } + } + + /** + * Returns the set of flags (OPT_ATT_HIDDEN, OPT_ATT_READONLY, OPT_ATT_MANDATORY...) + * for the given attribute in the current state of the object + * @param string $sAttCode The code of the attribute + * @return integer Flags: the binary combination of the flags applicable to this attribute + */ + public function GetAttributeFlags($sAttCode) + { + $iFlags = 0; // By default (if no life cycle) no flag at all + $sStateAttCode = MetaModel::GetStateAttributeCode(get_class($this)); + if (!empty($sStateAttCode)) + { + $iFlags = MetaModel::GetAttributeFlags(get_class($this), $this->Get($sStateAttCode), $sAttCode); + } + return $iFlags; + } + + // check if the given (or current) value is suitable for the attribute + public function CheckValue($sAttCode, $value = null) + { + if (!is_null($value)) + { + $toCheck = $value; + } + else + { + $toCheck = $this->Get($sAttCode); + } + + $oAtt = MetaModel::GetAttributeDef(get_class($this), $sAttCode); + if ($oAtt->IsExternalKey()) + { + if (!$oAtt->IsNullAllowed() || ($toCheck != 0) ) + { + try + { + $oTargetObj = MetaModel::GetObject($oAtt->GetTargetClass(), $toCheck); + return true; + } + catch (CoreException $e) + { + return false; + } + } + } + return true; + } + + // check attributes together + public function CheckConsistency() + { + return true; + } + + // check if it is allowed to record the new object into the database + // a displayable error is returned + // Note: checks the values and consistency + public function CheckToInsert() + { + foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef) + { + if (!$this->CheckValue($sAttCode)) return false; + } + if (!$this->CheckConsistency()) return false; + return true; + } + + // check if it is allowed to update the existing object into the database + // a displayable error is returned + // Note: checks the values and consistency + public function CheckToUpdate() + { + foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef) + { + if (!$this->CheckValue($sAttCode)) return false; + } + if (!$this->CheckConsistency()) return false; + return true; + } + + // check if it is allowed to delete the existing object from the database + // a displayable error is returned + public function CheckToDelete() + { + return true; + } + + protected function ListChangedValues(array $aProposal) + { + $aDelta = array(); + foreach ($aProposal as $sAtt => $proposedValue) + { + if (!array_key_exists($sAtt, $this->m_aOrigValues) || ($this->m_aOrigValues[$sAtt] != $proposedValue)) + { + $aDelta[$sAtt] = $proposedValue; + } + } + return $aDelta; + } + + // List the attributes that have been changed + // Returns an array of attname => currentvalue + public function ListChanges() + { + return $this->ListChangedValues($this->m_aCurrValues); + } + + // used both by insert/update + private function DBWriteLinks() + { + foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef) + { + if (!$oAttDef->IsLinkSet()) continue; + + $oLinks = $this->Get($sAttCode); + $oLinks->Rewind(); + while ($oLinkedObject = $oLinks->Fetch()) + { + $oLinkedObject->Set($oAttDef->GetExtKeyToMe(), $this->m_iKey); + $oLinkedObject->DBWrite(); + } + + // Delete the objects that were initialy present and disappeared from the list + // (if any) + $oOriginalSet = $this->m_aOrigValues[$sAttCode]; + if ($oOriginalSet != null) + { + $aOriginalList = $oOriginalSet->ToArray(); + $aNewSet = $oLinks->ToArray(); + $aToDelete = array_diff($aOriginalList, $aNewSet); + foreach ($aToDelete as $iKey => $oObject) + { + $oObject->DBDelete(); + } + } + } + } + + private function DBInsertSingleTable($sTableClass) + { + $sClass = get_class($this); + + // fields in first array, values in the second + $aFieldsToWrite = array(); + $aValuesToWrite = array(); + + if (!empty($this->m_iKey) && ($this->m_iKey >= 0)) + { + // Add it to the list of fields to write + $aFieldsToWrite[] = MetaModel::DBGetKey($sTableClass); + $aValuesToWrite[] = CMDBSource::Quote($this->m_iKey); + } + + foreach(MetaModel::ListAttributeDefs($sTableClass) as $sAttCode=>$oAttDef) + { + // Skip this attribute if not defined in this table + if (!MetaModel::IsAttributeOrigin($sTableClass, $sAttCode)) continue; + if ($oAttDef->IsDirectField()) + { + $aFieldsToWrite[] = $oAttDef->GetSQLExpr(); + $aValuesToWrite[] = CMDBSource::Quote($oAttDef->RealValueToSQLValue($this->m_aCurrValues[$sAttCode])); + } + } + + if (count($aValuesToWrite) == 0) return false; + + $sTable = MetaModel::DBGetTable($sTableClass); + $sInsertSQL = "INSERT INTO $sTable (".join(",", $aFieldsToWrite).") VALUES (".join(", ", $aValuesToWrite).")"; + + $iNewKey = CMDBSource::InsertInto($sInsertSQL); + // Note that it is possible to have a key defined here, and the autoincrement expected, this is acceptable in a non root class + if (empty($this->m_iKey)) + { + // Take the autonumber + $this->m_iKey = $iNewKey; + } + return $this->m_iKey; + } + + // Insert of record for the new object into the database + // Returns the key of the newly created object + public function DBInsertNoReload() + { + if ($this->m_bIsInDB) + { + throw new CoreException("The object already exists into the Database, you may want to use the clone function"); + } + + $sClass = get_class($this); + $sRootClass = MetaModel::GetRootClass($sClass); + + // Ensure the update of the values (we are accessing the data directly) + $this->ComputeFields(); + + if ($this->m_iKey < 0) + { + // This was a temporary "memory" key: discard it so that DBInsertSingleTable will not try to use it! + $this->m_iKey = null; + } + + // If not automatically computed, then check that the key is given by the caller + if (!MetaModel::IsAutoIncrementKey($sRootClass)) + { + if (empty($this->m_iKey)) + { + throw new CoreWarning("Missing key for the object to write - This class is supposed to have a user defined key, not an autonumber"); + } + } + + // First query built upon on the root class, because the ID must be created first + $this->m_iKey = $this->DBInsertSingleTable($sRootClass); + + // Then do the leaf class, if different from the root class + if ($sClass != $sRootClass) + { + $this->DBInsertSingleTable($sClass); + } + + // Then do the other classes + foreach(MetaModel::EnumParentClasses($sClass) as $sParentClass) + { + if ($sParentClass == $sRootClass) continue; + if (MetaModel::DBGetTable($sParentClass) == "") continue; + $this->DBInsertSingleTable($sParentClass); + } + + $this->DBWriteLinks(); + + // Reload to update the external attributes + $this->m_bIsInDB = true; + return $this->m_iKey; + } + + public function DBInsert() + { + $this->DBInsertNoReload(); + $this->Reload(); + return $this->m_iKey; + } + + // Creates a copy of the current object into the database + // Returns the id of the newly created object + public function DBClone($iNewKey = null) + { + $this->m_bIsInDB = false; + $this->m_iKey = $iNewKey; + return $this->DBInsert(); + } + + // Update a record + public function DBUpdate() + { + if (!$this->m_bIsInDB) + { + throw new CoreException("DBUpdate: could not update a newly created object, please call DBInsert instead"); + } + $aChanges = $this->ListChanges(); + if (count($aChanges) == 0) + { + throw new CoreWarning("Attempting to update an unchanged object"); + return; + } + $bHasANewExternalKeyValue = false; + foreach($aChanges as $sAttCode => $valuecurr) + { + $oAttDef = MetaModel::GetAttributeDef(get_class($this), $sAttCode); + if ($oAttDef->IsExternalKey()) $bHasANewExternalKeyValue = true; + if (!$oAttDef->IsDirectField()) unset($aChanges[$sAttCode]); + } + + // Update scalar attributes + if (count($aChanges) != 0) + { + $oFilter = new DBObjectSearch(get_class($this)); + $oFilter->AddCondition('id', $this->m_iKey, '='); + + $sSQL = MetaModel::MakeUpdateQuery($oFilter, $aChanges); + CMDBSource::Query($sSQL); + } + + $this->DBWriteLinks(); + + // Reload to get the external attributes + if ($bHasANewExternalKeyValue) $this->Reload(); + + return $this->m_iKey; + } + + // Make the current changes persistent - clever wrapper for Insert or Update + public function DBWrite() + { + if ($this->m_bIsInDB) + { + return $this->DBUpdate(); + } + else + { + return $this->DBInsert(); + } + } + + // Delete a record + public function DBDelete() + { + $oFilter = new DBObjectSearch(get_class($this)); + $oFilter->AddCondition('id', $this->m_iKey, '='); + + $sSQL = MetaModel::MakeDeleteQuery($oFilter); + CMDBSource::Query($sSQL); + + $this->m_bIsInDB = false; + $this->m_iKey = null; + } + + public function EnumTransitions() + { + $sStateAttCode = MetaModel::GetStateAttributeCode(get_class($this)); + if (empty($sStateAttCode)) return array(); + + $sState = $this->Get(MetaModel::GetStateAttributeCode(get_class($this))); + return MetaModel::EnumTransitions(get_class($this), $sState); + } + + public function ApplyStimulus($sStimulusCode) + { + $sStateAttCode = MetaModel::GetStateAttributeCode(get_class($this)); + if (empty($sStateAttCode)) return false; + + MyHelpers::CheckKeyInArray('object lifecycle stimulus', $sStimulusCode, MetaModel::EnumStimuli(get_class($this))); + + $aStateTransitions = $this->EnumTransitions(); + $aTransitionDef = $aStateTransitions[$sStimulusCode]; + + // Change the state before proceeding to the actions, this is necessary because an action might + // trigger another stimuli (alternative: push the stimuli into a queue) + $this->Set($sStateAttCode, $aTransitionDef['target_state']); + + // $aTransitionDef is an + // array('target_state'=>..., 'actions'=>array of handlers procs, 'user_restriction'=>TBD + + $bSuccess = true; + foreach ($aTransitionDef['actions'] as $sActionHandler) + { + // std PHP spec + $aActionCallSpec = array($this, $sActionHandler); + + if (!is_callable($aActionCallSpec)) + { + throw new CoreException("Unable to call action: ".get_class($this)."::$sActionHandler"); + return; + } + $bRet = call_user_func($aActionCallSpec, $sStimulusCode); + // if one call fails, the whole is considered as failed + if (!$bRet) $bSuccess = false; + } + + return $bSuccess; + } + + // Return an empty set for the parent of all + public static function GetRelationQueries($sRelCode) + { + return array(); + } + + public function GetRelatedObjects($sRelCode, $iMaxDepth = 99, &$aResults = array()) + { + foreach (MetaModel::EnumRelationQueries(get_class($this), $sRelCode) as $sDummy => $aQueryInfo) + { + MetaModel::DbgTrace("object=".$this->GetKey().", depth=$iMaxDepth, rel=".$aQueryInfo["sQuery"]); + $sQuery = $aQueryInfo["sQuery"]; + $bPropagate = $aQueryInfo["bPropagate"]; + $iDistance = $aQueryInfo["iDistance"]; + + $iDepth = $bPropagate ? $iMaxDepth - 1 : 0; + + $oFlt = DBObjectSearch::FromSibusQL($sQuery, array(), $this); + $oObjSet = new DBObjectSet($oFlt); + while ($oObj = $oObjSet->Fetch()) + { + $sRootClass = MetaModel::GetRootClass(get_class($oObj)); + $sObjKey = $oObj->GetKey(); + if (array_key_exists($sRootClass, $aResults)) + { + if (array_key_exists($sObjKey, $aResults[$sRootClass])) + { + continue; // already visited, skip + } + } + + $aResults[$sRootClass][$sObjKey] = $oObj; + if ($iDepth > 0) + { + $oObj->GetRelatedObjects($sRelCode, $iDepth, $aResults); + } + } + } + return $aResults; + } +} + + +?> diff --git a/core/dbobjectsearch.class.php b/core/dbobjectsearch.class.php new file mode 100644 index 000000000..86d6e057a --- /dev/null +++ b/core/dbobjectsearch.class.php @@ -0,0 +1,973 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ + + +/** + * Sibusql - value set start + * @package iTopORM + * @info zis is private + */ +define('VS_START', '{'); +/** + * Sibusql - value set end + * @package iTopORM + */ +define('VS_END', '}'); + + +define('SIBUSQLPARAMREGEXP', "/\\$\\[(.*)\\:(.*)\\:(.*)\\]/U"); +define('SIBUSQLTHISREGEXP', "/this\\.(.*)/U"); + + +/** + * Define filters for a given class of objects (formerly named "filter") + * + * @package iTopORM + * @author Romain Quetiez + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @mytagrom youpi + * @since 1.0 + * @version 1.1.1.1 $ + */ +class DBObjectSearch +{ + private $m_sClass; + private $m_sClassAlias; + private $m_aClasses; // queried classes (alias => class name) + private $m_oSearchCondition; + private $m_aFullText; + private $m_aPointingTo; + private $m_aReferencedBy; + private $m_aRelatedTo; + + public function __construct($sClass, $sClassAlias = '') + { + if (empty($sClassAlias)) $sClassAlias = $sClass; + assert('is_string($sClass)'); + assert('MetaModel::IsValidClass($sClass)'); // #@# could do better than an assert, or at least give the caller's reference + // => idee d'un assert avec call stack (autre utilisation = echec sur query SQL) + if (empty($sClassAlias)) $sClassAlias = $sClass; + $this->m_sClass = $sClass; + $this->m_sClassAlias = $sClassAlias; + $this->m_aClasses = array($sClassAlias => $sClass); + $this->m_oSearchCondition = new TrueExpression; + $this->m_aFullText = array(); + $this->m_aPointingTo = array(); + $this->m_aReferencedBy = array(); + $this->m_aRelatedTo = array(); + } + + public function IsAny() + { + // #@# todo - if (!$this->m_oSearchCondition->IsTrue()) return false; + if (count($this->m_aFullText) > 0) return false; + if (count($this->m_aPointingTo) > 0) return false; + if (count($this->m_aReferencedBy) > 0) return false; + if (count($this->m_aRelatedTo) > 0) return false; + return true; + } + + public function Describe() + { + // To replace __Describe + } + + public function DescribeConditionPointTo($sExtKeyAttCode) + { + if (!isset($this->m_aPointingTo[$sExtKeyAttCode])) return ""; + $oFilter = $this->m_aPointingTo[$sExtKeyAttCode]; + if ($oFilter->IsAny()) return ""; + $oAtt = MetaModel::GetAttributeDef($this->GetClass(), $sExtKeyAttCode); + return $oAtt->GetLabel()." having ({$oFilter->DescribeConditions()})"; + } + + public function DescribeConditionRefBy($sForeignClass, $sForeignExtKeyAttCode) + { + if (!isset($this->m_aReferencedBy[$sForeignClass][$sForeignExtKeyAttCode])) return ""; + $oFilter = $this->m_aReferencedBy[$sForeignClass][$sForeignExtKeyAttCode]; + if ($oFilter->IsAny()) return ""; + $oAtt = MetaModel::GetAttributeDef($sForeignClass, $sForeignExtKeyAttCode); + return "being ".$oAtt->GetLabel()." for ".$sForeignClass."s in ({$oFilter->DescribeConditions()})"; + } + + public function DescribeConditionRelTo($aRelInfo) + { + $oFilter = $aRelInfo['flt']; + $sRelCode = $aRelInfo['relcode']; + $iMaxDepth = $aRelInfo['maxdepth']; + return "related ($sRelCode... peut mieux faire !, $iMaxDepth dig depth) to a {$oFilter->GetClass()} ({$oFilter->DescribeConditions()})"; + } + + public function DescribeConditions() + { + $aConditions = array(); + + $aCondFT = array(); + foreach($this->m_aFullText as $sFullText) + { + $aCondFT[] = " contain word(s) '$sFullText'"; + } + if (count($aCondFT) > 0) + { + $aConditions[] = "which ".implode(" and ", $aCondFT); + } + + // #@# todo - review textual description of the JOIN and search condition (is that still feasible?) + $aConditions[] = $this->RenderCondition(); + + $aCondPoint = array(); + foreach($this->m_aPointingTo as $sExtKeyAttCode=>$oFilter) + { + if ($oFilter->IsAny()) continue; + $aCondPoint[] = $this->DescribeConditionPointTo($sExtKeyAttCode); + } + if (count($aCondPoint) > 0) + { + $aConditions[] = implode(" and ", $aCondPoint); + } + + $aCondReferred= array(); + foreach($this->m_aReferencedBy as $sForeignClass=>$aReferences) + { + foreach($aReferences as $sForeignExtKeyAttCode=>$oForeignFilter) + { + if ($oForeignFilter->IsAny()) continue; + $aCondReferred[] = $this->DescribeConditionRefBy($sForeignClass, $sForeignExtKeyAttCode); + } + } + foreach ($this->m_aRelatedTo as $aRelInfo) + { + $aCondReferred[] = $this->DescribeConditionRelTo($aRelInfo); + } + if (count($aCondReferred) > 0) + { + $aConditions[] = implode(" and ", $aCondReferred); + } + + return implode(" and ", $aConditions); + } + + public function __DescribeHTML() + { + $sConditionDesc = $this->DescribeConditions(); + if (!empty($sConditionDesc)) + { + return "Objects of class '$this->m_sClass', $sConditionDesc"; + } + return "Any object of class '$this->m_sClass'"; + } + + protected function TransferConditionExpression($oFilter, $aTranslation) + { + $oTranslated = $oFilter->GetCriteria()->Translate($aTranslation, false); + $this->AddConditionExpression($oTranslated); + } + + public function ResetCondition() + { + $this->m_oSearchCondition = new TrueExpression(); + // ? is that usefull/enough, do I need to rebuild the list after the subqueries ? + // $this->m_aClasses = array($this->m_sClassAlias => $this->m_sClass); + } + + public function AddConditionExpression($oExpression) + { + $this->m_oSearchCondition = $this->m_oSearchCondition->LogAnd($oExpression); + } + + public function AddCondition($sFilterCode, $value, $sOpCode = null) + { + // #@# backward compatibility for pkey/id + if (strtolower(trim($sFilterCode)) == 'pkey') $sFilterCode = 'id'; +// #@# todo - obsolete smoothly, first send exceptions +// throw new CoreException('SibusQL has been obsoleted, please update your queries', array('sibusql'=>$sQuery, 'oql'=>$oFilter->ToOQL())); + + MyHelpers::CheckKeyInArray('filter code', $sFilterCode, MetaModel::GetClassFilterDefs($this->m_sClass)); + $oFilterDef = MetaModel::GetClassFilterDef($this->m_sClass, $sFilterCode); + + if (empty($sOpCode)) + { + $sOpCode = $oFilterDef->GetLooseOperator(); + } + MyHelpers::CheckKeyInArray('operator', $sOpCode, $oFilterDef->GetOperators()); + + // Preserve backward compatibility - quick n'dirty way to change that API semantic + // + $oField = new FieldExpression($sFilterCode, $this->m_sClassAlias); + switch($sOpCode) + { + case 'SameDay': + case 'SameMonth': + case 'SameYear': + case 'Today': + case '>|': + case '<|': + case '=|': + throw new CoreException('Deprecated operator, please consider using OQL (SQL) expressions like "(TO_DAYS(NOW()) - TO_DAYS(x)) AS AgeDays"', array('operator' => $sOpCode)); + break; + + case "IN": + if (!is_array($value)) $value = array($value); + $sListExpr = '('.implode(', ', CMDBSource::Quote($value)).')'; + $sOQLCondition = $oField->Render()." IN $sListExpr"; + break; + + case "NOTIN": + if (!is_array($value)) $value = array($value); + $sListExpr = '('.implode(', ', CMDBSource::Quote($value)).')'; + $sOQLCondition = $oField->Render()." NOT IN $sListExpr"; + break; + + case 'Contains': + $oRightExpr = new ScalarExpression("%$value%"); + $sOperator = 'LIKE'; + break; + + case 'Begins with': + $oRightExpr = new ScalarExpression("$value%"); + $sOperator = 'LIKE'; + break; + + case 'Finishes with': + $oRightExpr = new ScalarExpression("%$value"); + $sOperator = 'LIKE'; + break; + + default: + $oRightExpr = new ScalarExpression($value); + $sOperator = $sOpCode; + } + + switch($sOpCode) + { + case "IN": + case "NOTIN": + $oNewCondition = Expression::FromOQL($sOQLCondition); + break; + + case 'Contains': + case 'Begins with': + case 'Finishes with': + default: + $oNewCondition = new BinaryExpression($oField, $sOperator, $oRightExpr); + } + + $this->AddConditionExpression($oNewCondition); + } + + public function AddCondition_FullText($sFullText) + { + $this->m_aFullText[] = $sFullText; + } + + protected function AddToNameSpace(&$aClassAliases, &$aAliasTranslation) + { + $sOrigAlias = $this->m_sClassAlias; + if (array_key_exists($sOrigAlias, $aClassAliases)) + { + $this->m_sClassAlias = MetaModel::GenerateUniqueAlias($aClassAliases, $sOrigAlias, $this->m_sClass); + // Translate the condition expression with the new alias + $aAliasTranslation[$sOrigAlias]['*'] = $this->m_sClassAlias; + } + + foreach($this->m_aPointingTo as $sExtKeyAttCode=>$oFilter) + { + $oFilter->AddToNameSpace($aClassAliases, $aAliasTranslation); + } + + foreach($this->m_aReferencedBy as $sForeignClass=>$aReferences) + { + foreach($aReferences as $sForeignExtKeyAttCode=>$oForeignFilter) + { + $oForeignFilter->AddToNameSpace($aClassAliases, $aAliasTranslation); + } + } + } + + public function AddCondition_PointingTo(DBObjectSearch $oFilter, $sExtKeyAttCode) + { + $aAliasTranslation = array(); + $res = $this->AddCondition_PointingTo_InNameSpace($oFilter, $sExtKeyAttCode, $this->m_aClasses, $aAliasTranslation); + $this->TransferConditionExpression($oFilter, $aAliasTranslation); + return $res; + } + + protected function AddCondition_PointingTo_InNameSpace(DBObjectSearch $oFilter, $sExtKeyAttCode, &$aClassAliases, &$aAliasTranslation) + { + if (!MetaModel::IsValidKeyAttCode($this->GetClass(), $sExtKeyAttCode)) + { + throw new CoreWarning("The attribute code '$sExtKeyAttCode' is not an external key of the class '{$this->GetClass()}' - the condition will be ignored"); + } + $oAttExtKey = MetaModel::GetAttributeDef($this->GetClass(), $sExtKeyAttCode); + if(!MetaModel::IsSameFamilyBranch($oFilter->GetClass(), $oAttExtKey->GetTargetClass())) + { + throw new CoreException("The specified filter (pointing to {$oFilter->GetClass()}) is not compatible with the key '{$this->GetClass()}::$sExtKeyAttCode', which is pointing to {$oAttExtKey->GetTargetClass()}"); + } + + if (array_key_exists($sExtKeyAttCode, $this->m_aPointingTo)) + { + $this->m_aPointingTo[$sExtKeyAttCode]->MergeWith_InNamespace($oFilter, $aClassAliases, $aAliasTranslation); + } + else + { + $oFilter->AddToNamespace($aClassAliases, $aAliasTranslation); + + // #@# The condition expression found in that filter should not be used - could be another kind of structure like a join spec tree !!!! + // $oNewFilter = clone $oFilter; + // $oNewFilter->ResetCondition(); + + $this->m_aPointingTo[$sExtKeyAttCode] = $oFilter; + } + } + + public function AddCondition_ReferencedBy(DBObjectSearch $oFilter, $sForeignExtKeyAttCode) + { + $aAliasTranslation = array(); + $res = $this->AddCondition_ReferencedBy_InNameSpace($oFilter, $sForeignExtKeyAttCode, $this->m_aClasses, $aAliasTranslation); + $this->TransferConditionExpression($oFilter, $aAliasTranslation); + return $res; + } + + protected function AddCondition_ReferencedBy_InNameSpace(DBObjectSearch $oFilter, $sForeignExtKeyAttCode, &$aClassAliases, &$aAliasTranslation) + { + $sForeignClass = $oFilter->GetClass(); + $sForeignClassAlias = $oFilter->GetClassAlias(); + if (!MetaModel::IsValidKeyAttCode($sForeignClass, $sForeignExtKeyAttCode)) + { + throw new CoreException("The attribute code '$sForeignExtKeyAttCode' is not an external key of the class '{$sForeignClass}' - the condition will be ignored"); + } + $oAttExtKey = MetaModel::GetAttributeDef($sForeignClass, $sForeignExtKeyAttCode); + if(!MetaModel::IsSameFamilyBranch($this->GetClass(), $oAttExtKey->GetTargetClass())) + { + throw new CoreException("The specified filter (objects referencing an object of class {$this->GetClass()}) is not compatible with the key '{$sForeignClass}::$sForeignExtKeyAttCode', which is pointing to {$oAttExtKey->GetTargetClass()}"); + } + if (array_key_exists($sForeignClass, $this->m_aReferencedBy) && array_key_exists($sForeignExtKeyAttCode, $this->m_aReferencedBy[$sForeignClass])) + { + $this->m_aReferencedBy[$sForeignClass][$sForeignExtKeyAttCode]->MergeWith_InNamespace($oFilter, $aClassAliases, $aAliasTranslation); + } + else + { + $oFilter->AddToNamespace($aClassAliases, $aAliasTranslation); + + // #@# The condition expression found in that filter should not be used - could be another kind of structure like a join spec tree !!!! + //$oNewFilter = clone $oFilter; + //$oNewFilter->ResetCondition(); + + $this->m_aReferencedBy[$sForeignClass][$sForeignExtKeyAttCode]= $oFilter; + } + } + + public function AddCondition_LinkedTo(DBObjectSearch $oLinkFilter, $sExtKeyAttCodeToMe, $sExtKeyAttCodeTarget, DBObjectSearch $oFilterTarget) + { + $oLinkFilterFinal = clone $oLinkFilter; + $oLinkFilterFinal->AddCondition_PointingTo($sExtKeyAttCodeToMe); + + $this->AddCondition_ReferencedBy($oLinkFilterFinal, $sExtKeyAttCodeToMe); + } + + public function AddCondition_RelatedTo(DBObjectSearch $oFilter, $sRelCode, $iMaxDepth) + { + MyHelpers::CheckValueInArray('relation code', $sRelCode, MetaModel::EnumRelations()); + $this->m_aRelatedTo[] = array('flt'=>$oFilter, 'relcode'=>$sRelCode, 'maxdepth'=>$iMaxDepth); + } + + public function MergeWith($oFilter) + { + $aAliasTranslation = array(); + $res = $this->MergeWith_InNamespace($oFilter, $this->m_aClasses, $aAliasTranslation); + $this->TransferConditionExpression($oFilter, $aAliasTranslation); + return $res; + } + + protected function MergeWith_InNamespace($oFilter, &$aClassAliases, &$aAliasTranslation) + { + if ($this->GetClass() != $oFilter->GetClass()) + { + throw new CoreException("Attempting to merge a filter of class '{$this->GetClass()}' with a filter of class '{$oFilter->GetClass()}'"); + } + + // Translate search condition into our aliasing scheme + $aAliasTranslation[$oFilter->GetClassAlias()]['*'] = $this->GetClassAlias(); + + $this->m_aFullText = array_merge($this->m_aFullText, $oFilter->m_aFullText); + $this->m_aRelatedTo = array_merge($this->m_aRelatedTo, $oFilter->m_aRelatedTo); + + foreach($oFilter->m_aPointingTo as $sExtKeyAttCode=>$oExtFilter) + { + $this->AddCondition_PointingTo_InNamespace($oExtFilter, $sExtKeyAttCode, $aClassAliases, $aAliasTranslation); + } + foreach($oFilter->m_aReferencedBy as $sForeignClass => $aReferences) + { + foreach($aReferences as $sForeignExtKeyAttCode => $oForeignFilter) + { + $this->AddCondition_ReferencedBy_InNamespace($oForeignFilter, $sForeignExtKeyAttCode, $aClassAliases, $aAliasTranslation); + } + } + } + + + public function GetClassName($sAlias) {return $this->m_aClasses[$sAlias];} + public function GetClasses() {return $this->m_aClasses;} + + public function GetClass() {return $this->m_sClass;} + public function GetClassAlias() {return $this->m_sClassAlias;} + public function GetCriteria() {return $this->m_oSearchCondition;} + public function GetCriteria_FullText() {return $this->m_aFullText;} + public function GetCriteria_PointingTo($sKeyAttCode = "") + { + if (empty($sKeyAttCode)) + { + return $this->m_aPointingTo; + } + if (!array_key_exists($sKeyAttCode, $this->m_aPointingTo)) return null; + return $this->m_aPointingTo[$sKeyAttCode]; + } + public function GetCriteria_ReferencedBy($sRemoteClass = "", $sForeignExtKeyAttCode = "") + { + if (empty($sRemoteClass)) + { + return $this->m_aReferencedBy; + } + if (!array_key_exists($sRemoteClass, $this->m_aReferencedBy)) return null; + if (empty($sForeignExtKeyAttCode)) + { + return $this->m_aReferencedBy[$sRemoteClass]; + } + if (!array_key_exists($sForeignExtKeyAttCode, $this->m_aReferencedBy[$sRemoteClass])) return null; + return $this->m_aReferencedBy[$sRemoteClass][$sForeignExtKeyAttCode]; + } + public function GetCriteria_RelatedTo() + { + return $this->m_aRelatedTo; + } + + public function RenderCondition() + { + return $this->m_oSearchCondition->Render(); + } + + public function serialize() + { + // Efficient but resulting in long strings: + // -> return (base64_encode(serialize($this))); + + $sValue = $this->GetClass()."\n"; + $sValue .= $this->GetClassAlias()."\n"; + + foreach($this->m_aClasses as $sClassAlias => $sClass) + { + // A stands for "Aliases" + $sValue .= "A:$sClassAlias:$sClass\n"; + } + foreach($this->m_aFullText as $sFullText) + { + // F stands for "Full text" + $sValue .= "F:".$sFullText."\n"; + } + $sValue .= "C:".$this->m_oSearchCondition->serialize()."\n"; + + foreach($this->m_aPointingTo as $sExtKey=>$oFilter) + { + // P stands for "Pointing to" + $sValue .= "P:".$sExtKey.":".$oFilter->serialize()."\n"; + } + foreach($this->m_aReferencedBy as $sForeignClass=>$aReferences) + { + foreach($aReferences as $sForeignExtKeyAttCode=>$oForeignFilter) + { + // R stands for "Referenced by" + $sValue .= "R:".$sForeignExtKeyAttCode.":".$oForeignFilter->serialize()."\n"; + } + } + foreach($this->m_aRelatedTo as $aRelatedTo) + { + $oFilter = $aRelatedTo['flt']; + $sRelCode = $aRelatedTo['relcode']; + $iMaxDepth = $aRelatedTo['maxdepth']; + + $sValue .= "T:".$oFilter->serialize().":$sRelCode:$iMaxDepth"; + } + return base64_encode($sValue); + } + + static public function unserialize($sValue) + { + // See comment above... + // -> return (unserialize(base64_decode($sValue))); + + $sClearText = base64_decode($sValue); + $aValues = split("\n", $sClearText); + $i = 0; + $sClass = $aValues[$i++]; + $sClassAlias = $aValues[$i++]; + $oFilter = new DBObjectSearch($sClass, $sClassAlias); + while($i < count($aValues) && !empty($aValues[$i])) + { + $aCondition = split(":", $aValues[$i++]); + switch ($aCondition[0]) + { + case "A": + $oFilter->m_aClasses[$aCondition[1]] = $aCondition[2]; + break; + case "F": + $oFilter->AddCondition_FullText($aCondition[1]); + break; + case "C": + $oFilter->m_oSearchCondition = Expression::unserialize($aCondition[1]); + break; + case "P": + //$oAtt = DBObject::GetAttributeDef($sClass, $aCondition[1]); + //$sRemoteClass = $oAtt->GetTargetClass(); + $oSubFilter = self::unserialize($aCondition[2]); + $sExtKeyAttCode = $aCondition[1]; + $oFilter->AddCondition_PointingTo($oSubFilter, $sExtKeyAttCode); + break; + case "R": + $oRemoteFilter = self::unserialize($aCondition[2]); + $sExtKeyAttCodeToMe = $aCondition[1]; + $oFilter->AddCondition_ReferencedBy($oRemoteFilter, $sExtKeyAttCodeToMe); + break; + case "T": + $oSubFilter = self::unserialize($aCondition[1]); + $sRelCode = $aCondition[2]; + $iMaxDepth = $aCondition[3]; + $oFilter->AddCondition_RelatedTo($oSubFilter, $sRelCode, $iMaxDepth); + default: + throw new CoreException("invalid filter definition (cannot unserialize the data, clear text = '$sClearText')"); + } + } + return $oFilter; + } + + // SImple BUt Structured Query Languag - SubuSQL + // + static private function Value2Expression($value) + { + $sRet = $value; + if (is_array($value)) + { + $sRet = VS_START.implode(', ', $value).VS_END; + } + else if (!is_numeric($value)) + { + $sRet = "'".addslashes($value)."'"; + } + return $sRet; + } + static private function Expression2Value($sExpr) + { + $retValue = $sExpr; + if ((substr($sExpr, 0, 1) == "'") && (substr($sExpr, -1, 1) == "'")) + { + $sNoQuotes = substr($sExpr, 1, -1); + return stripslashes($sNoQuotes); + } + if ((substr($sExpr, 0, 1) == VS_START) && (substr($sExpr, -1, 1) == VS_END)) + { + $sNoBracket = substr($sExpr, 1, -1); + $aRetValue = array(); + foreach (explode(",", $sNoBracket) as $sItem) + { + $aRetValue[] = self::Expression2Value(trim($sItem)); + } + return $aRetValue; + } + return $retValue; + } + + public function ToOQL() + { + $sRes = "SELECT ".$this->GetClass().' AS '.$this->GetClassAlias(); + $sRes .= $this->ToOQL_Joins(); + $sRes .= " WHERE ".$this->m_oSearchCondition->Render(); + return $sRes; + } + + protected function ToOQL_Joins() + { + $sRes = ''; + foreach($this->m_aPointingTo as $sExtKey=>$oFilter) + { + $sRes .= ' JOIN '.$oFilter->GetClass().' AS '.$oFilter->GetClassAlias().' ON '.$this->GetClassAlias().'.'.$sExtKey.' = '.$oFilter->GetClassAlias().'.id'; + $sRes .= $oFilter->ToOQL_Joins(); + } + foreach($this->m_aReferencedBy as $sForeignClass=>$aReferences) + { + foreach($aReferences as $sForeignExtKeyAttCode=>$oForeignFilter) + { + $sRes .= ' JOIN '.$oForeignFilter->GetClass().' AS '.$oForeignFilter->GetClassAlias().' ON '.$oForeignFilter->GetClassAlias().'.'.$sForeignExtKeyAttCode.' = '.$this->GetClassAlias().'.id'; + $sRes .= $oForeignFilter->ToOQL_Joins(); + } + } + return $sRes; + } + + public function ToSibusQL() + { + return "NONONO"; + } + + static private function privProcessParams($sQuery, array $aParams, $oDbObject) + { + $iPlaceHoldersCount = preg_match_all(SIBUSQLPARAMREGEXP, $sQuery, $aMatches, PREG_SET_ORDER); + if ($iPlaceHoldersCount > 0) + { + foreach($aMatches as $aMatch) + { + $sStringToSearch = $aMatch[0]; + $sParameterName = $aMatch[1]; + $sDefaultValue = $aMatch[2]; + $sDescription = $aMatch[3]; + + $sValue = $sDefaultValue; + if (array_key_exists($sParameterName, $aParams)) + { + $sValue = $aParams[$sParameterName]; + unset($aParams[$sParameterName]); + } + else if (is_object($oDbObject)) + { + if (strpos($sParameterName, "this.") === 0) + { + $sAttCode = substr($sParameterName, strlen("this.")); + if ($sAttCode == 'id') + { + $sValue = $oDbObject->GetKey(); + } + else if ($sAttCode == 'class') + { + $sValue = get_class($oDbObject); + } + else if (MetaModel::IsValidAttCode(get_class($oDbObject), $sAttCode)) + { + $sValue = $oDbObject->Get($sAttCode); + } + } + } + $sQuery = str_replace($sStringToSearch, $sValue, $sQuery); + } + } + if (count($aParams) > 0) + { + throw new CoreException("Unused parameter(s) for this SibusQL expression: (".implode(', ', array_keys($aParams)).")"); + } + return $sQuery; + } + + static public function ListSibusQLParams($sQuery) + { + $aRet = array(); + $iPlaceHoldersCount = preg_match_all(SIBUSQLPARAMREGEXP, $sQuery, $aMatches, PREG_SET_ORDER); + if ($iPlaceHoldersCount > 0) + { + foreach($aMatches as $aMatch) + { + $sStringToSearch = $aMatch[0]; + $sParameterName = $aMatch[1]; + $sDefaultValue = $aMatch[2]; + $sDescription = $aMatch[3]; + $aRet[$sParameterName]["description"] = $sDescription; + $aRet[$sParameterName]["default"] = $sDefaultValue; + } + } + return $aRet; + } + + protected function OQLExpressionToCondition($sQuery, $oExpression, $aClassAliases) + { + if ($oExpression instanceof BinaryOqlExpression) + { + $sOperator = $oExpression->GetOperator(); + $oLeft = $this->OQLExpressionToCondition($sQuery, $oExpression->GetLeftExpr(), $aClassAliases); + $oRight = $this->OQLExpressionToCondition($sQuery, $oExpression->GetRightExpr(), $aClassAliases); + return new BinaryExpression($oLeft, $sOperator, $oRight); + } + elseif ($oExpression instanceof FieldOqlExpression) + { + $sClassAlias = $oExpression->GetParent(); + $sFltCode = $oExpression->GetName(); + if (empty($sClassAlias)) + { + // Try to find an alias + // Build an array of field => array of aliases + $aFieldClasses = array(); + foreach($aClassAliases as $sAlias => $sReal) + { + foreach(MetaModel::GetFiltersList($sReal) as $sAnFltCode) + { + $aFieldClasses[$sAnFltCode][] = $sAlias; + } + } + if (!array_key_exists($sFltCode, $aFieldClasses)) + { + throw new OqlNormalizeException('Unknown filter code', $sQuery, $oExpression->GetNameDetails(), array_keys($aFieldClasses)); + } + if (count($aFieldClasses[$sFltCode]) > 1) + { + throw new OqlNormalizeException('Ambiguous filter code', $sQuery, $oExpression->GetNameDetails()); + } + $sClassAlias = $aFieldClasses[$sFltCode][0]; + } + else + { + if (!array_key_exists($sClassAlias, $aClassAliases)) + { + throw new OqlNormalizeException('Unknown class [alias]', $sQuery, $oExpression->GetParentDetails(), array_keys($aClassAliases)); + } + $sClass = $aClassAliases[$sClassAlias]; + if (!MetaModel::IsValidFilterCode($sClass, $sFltCode)) + { + throw new OqlNormalizeException('Unknown filter code', $sQuery, $oExpression->GetNameDetails(), MetaModel::GetFiltersList($sClass)); + } + } + + return new FieldExpression($sFltCode, $sClassAlias); + } + elseif ($oExpression instanceof TrueOqlExpression) + { + return new TrueExpression; + } + elseif ($oExpression instanceof ScalarOqlExpression) + { + return new ScalarExpression($oExpression->GetValue()); + } + elseif ($oExpression instanceof ListOqlExpression) + { + return new ListExpression($oExpression->GetItems()); + } + elseif ($oExpression instanceof FunctionOqlExpression) + { + return new FunctionExpression($oExpression->GetVerb(), $oExpression->GetArgs()); + } + else + { + throw new CoreException('Unknown expression type', array('class'=>get_class($oExpression), 'query'=>$sQuery)); + } + } + + static public function FromOQL($sQuery, array $aParams = array(), $oObject = null) + { + if (empty($sQuery)) return null; + + $oOql = new OqlInterpreter($sQuery); + $oOqlQuery = $oOql->ParseQuery(); + + $sClass = $oOqlQuery->GetClass(); + $sClassAlias = $oOqlQuery->GetClassAlias(); + + if (!MetaModel::IsValidClass($sClass)) + { + throw new OqlNormalizeException('Unknown class', $sQuery, $oOqlQuery->GetClassDetails(), MetaModel::GetClasses()); + } + + $oResultFilter = new DBObjectSearch($sClass, $sClassAlias); + $aAliases = array($sClassAlias => $sClass); + + // Maintain an array of filters, because the flat list is in fact referring to a tree + // And this will be an easy way to dispatch the conditions + // $oResultFilter will be referenced by the other filters, or the other way around... + $aJoinItems = array($sClassAlias => $oResultFilter); + + $aJoinSpecs = $oOqlQuery->GetJoins(); + if (is_array($aJoinSpecs)) + { + foreach ($aJoinSpecs as $oJoinSpec) + { + $sJoinClass = $oJoinSpec->GetClass(); + $sJoinClassAlias = $oJoinSpec->GetClassAlias(); + if (!MetaModel::IsValidClass($sJoinClass)) + { + throw new OqlNormalizeException('Unknown class', $sQuery, $oJoinSpec->GetClassDetails(), MetaModel::GetClasses()); + } + if (array_key_exists($sJoinClassAlias, $aAliases)) + { + if ($sJoinClassAlias != $sJoinClass) + { + throw new OqlNormalizeException('Duplicate class alias', $sQuery, $oJoinSpec->GetClassAliasDetails()); + } + else + { + throw new OqlNormalizeException('Duplicate class name', $sQuery, $oJoinSpec->GetClassDetails()); + } + } + + // Assumption: ext key on the left only !!! + // normalization should take care of this + $oLeftField = $oJoinSpec->GetLeftField(); + $sFromClass = $oLeftField->GetParent(); + $sExtKeyAttCode = $oLeftField->GetName(); + + $oRightField = $oJoinSpec->GetRightField(); + $sToClass = $oRightField->GetParent(); + $sPKeyDescriptor = $oRightField->GetName(); + if ($sPKeyDescriptor != 'id') + { + throw new OqlNormalizeException('Wrong format for Join clause (right hand), expecting an id', $sQuery, $oRightField->GetNameDetails(), array('id')); + } + + $aAliases[$sJoinClassAlias] = $sJoinClass; + $aJoinItems[$sJoinClassAlias] = new DBObjectSearch($sJoinClass, $sJoinClassAlias); + + if (!array_key_exists($sFromClass, $aJoinItems)) + { + throw new OqlNormalizeException('Unknown class in join condition (left expression)', $sQuery, $oLeftField->GetParentDetails(), array_keys($aJoinItems)); + } + if (!array_key_exists($sToClass, $aJoinItems)) + { + throw new OqlNormalizeException('Unknown class in join condition (right expression)', $sQuery, $oRightField->GetParentDetails(), array_keys($aJoinItems)); + } + $aExtKeys = array_keys(MetaModel::GetExternalKeys($aAliases[$sFromClass])); + if (!in_array($sExtKeyAttCode, $aExtKeys)) + { + throw new OqlNormalizeException('Unknown external key in join condition (left expression)', $sQuery, $oLeftField->GetNameDetails(), $aExtKeys); + } + + if ($sFromClass == $sJoinClassAlias) + { + $aJoinItems[$sToClass]->AddCondition_ReferencedBy($aJoinItems[$sFromClass], $sExtKeyAttCode); + } + else + { + $aJoinItems[$sFromClass]->AddCondition_PointingTo($aJoinItems[$sToClass], $sExtKeyAttCode); + } + } + } + + $oConditionTree = $oOqlQuery->GetCondition(); + if ($oConditionTree instanceof Expression) + { + $oResultFilter->m_oSearchCondition = $oResultFilter->OQLExpressionToCondition($sQuery, $oConditionTree, $aAliases); + } + + return $oResultFilter; + } + + static public function FromSibusQL($sQuery, array $aParams = array(), $oObject = null) + { + if (empty($sQuery)) return null; + $sQuery = self::privProcessParams($sQuery, $aParams, $oObject); + + $iSepPos = strpos($sQuery, ":"); + if ($iSepPos === false) + { + if (preg_match('@^\\s*SELECT@', $sQuery)) + { + return self::FromOQL($sQuery, $aParams, $oObject); + } + // Only the class was specified -> all rows are required + $sClass = trim($sQuery); + $oFilter = new DBObjectSearch($sClass); + } + else + { + $sClass = trim(substr($sQuery, 0, $iSepPos)); + $sConds = trim(substr($sQuery, $iSepPos + 1)); + $aValues = split(" AND ", $sConds); + + $oFilter = new DBObjectSearch($sClass); + + foreach ($aValues as $sCond) + { + $sCond = trim($sCond); + + if (strpos($sCond, "* HAS ") === 0) + { + $sValue = self::Expression2Value(substr($sCond, strlen("* HAS "))); + $oFilter->AddCondition_FullText($sValue); + } + else if (preg_match("@^(\S+) IN \\((.+)\\)$@", $sCond, $aMatches)) + { + $sExtKeyAttCode = $aMatches[1]; + $sFilterExp = $aMatches[2]; + + $oSubFilter = self::FromSibuSQL($sFilterExp); + $oFilter->AddCondition_PointingTo($oSubFilter, $sExtKeyAttCode); + } + else if (strpos($sCond, "PKEY IS ") === 0) + { + if (preg_match("@^PKEY IS (\S+) IN \\((.+)\\)$@", $sCond, $aMatches)) + { + $sExtKeyAttCodeToMe = $aMatches[1]; + $sFilterExp = $aMatches[2]; + $oRemoteFilter = self::FromSibuSQL($sFilterExp); + $oFilter->AddCondition_ReferencedBy($oRemoteFilter, $sExtKeyAttCodeToMe); + } + } + else if (strpos($sCond, "RELATED") === 0) + { + if (preg_match("@^RELATED\s*\\((.+)\\)\s*TO\s*\\((.+)\\)@", trim($sCond), $aMatches)) + { + $aRelation = explode(',', trim($aMatches[1])); + $sRelCode = trim($aRelation[0]); + $iMaxDepth = intval(trim($aRelation[1])); + $sFilterExp = trim($aMatches[2]); + + $oSubFilter = self::FromSibuSQL($sFilterExp); + $oFilter->AddCondition_RelatedTo($oSubFilter, $sRelCode, $iMaxDepth); + } + } + else + { + $sOperandExpr = "'.*'|\d+|-\d+|".VS_START.".+".VS_END; + if (preg_match("@^(\S+)\s+(.*)\s+($sOperandExpr)$@", $sCond, $aMatches)) + { + $sFltCode = trim($aMatches[1]); + $sOpCode = trim($aMatches[2]); + $value = self::Expression2Value($aMatches[3]); + $oFilter->AddCondition($sFltCode, $value, $sOpCode); + } + else + { + throw new CoreException("Wrong format for filter definition: '$sQuery'"); + } + } + } + } + +// #@# todo - obsolete smoothly, first give the OQL version ! +// throw new CoreException('SibusQL has been obsoleted, please update your queries', array('sibusql'=>$sQuery, 'oql'=>$oFilter->ToOQL())); + + return $oFilter; + } + + // Sexy display of a SibuSQL expression + static public function SibuSQLAsHtml($sQuery) + { + $sQuery = htmlentities($sQuery); + $aParams = self::ListSibusQLParams($sQuery); + $aParamValues = array(); + foreach ($aParams as $sParamName => $aParamInfo) + { + $sDescription = $aParamInfo["description"]; + $sDefaultValue = $aParamInfo["default"]; + $aParamValues[$sParamName] = "$sParamName"; + } + $sQuery = self::privProcessParams($sQuery, $aParamValues, null); + return $sQuery; + } + + public function toxpath() + { + // #@# a voir... + } + static public function fromxpath() + { + // #@# a voir... + } +} + + +?> diff --git a/core/dbobjectset.class.php b/core/dbobjectset.class.php new file mode 100644 index 000000000..d2719c5cd --- /dev/null +++ b/core/dbobjectset.class.php @@ -0,0 +1,250 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ + +class DBObjectSet +{ + private $m_oFilter; + private $m_aOrderBy; + public $m_bLoaded; + private $m_aData; + private $m_aId2Row; + private $m_iCurrRow; + + public function __construct($oFilter, $aOrderBy = array()) + { + $this->m_oFilter = $oFilter; + $this->m_aOrderBy = $aOrderBy; + + $this->m_bLoaded = false; + $this->m_aData = array(); + $this->m_aId2Row = array(); + $this->m_iCurrRow = 0; + } + + public function __destruct() + { + } + + public function __toString() + { + $sRet = ''; + $this->Rewind(); + $sRet .= "Set (".$this->m_oFilter->ToSibuSQL().")
    \n"; + $sRet .= "Query:
    ".MetaModel::MakeSelectQuery($this->m_oFilter, array()).")
    \n"; + + $sRet .= $this->Count()." records
    \n"; + if ($this->Count() > 0) + { + $sRet .= "
      \n"; + while ($oObj = $this->Fetch()) + { + $sRet .= "
    • ".$oObj->__toString()."
    • \n"; + } + $sRet .= "
    \n"; + } + return $sRet; + } + + static public function FromScratch($sClass) + { + $oFilter = new CMDBSearchFilter($sClass); + $oRetSet = new self($oFilter); + $oRetSet->m_bLoaded = true; // no DB load + return $oRetSet; + } + + static public function FromArray($sClass, $aObjects) + { + $oFilter = new CMDBSearchFilter($sClass); + $oRetSet = new self($oFilter); + $oRetSet->m_bLoaded = true; // no DB load + $oRetSet->AddObjectArray($aObjects); + return $oRetSet; + } + + public function ToArray($bWithId = true) + { + $aRet = array(); + $this->Rewind(); + while ($oObject = $this->Fetch()) + { + if ($bWithId) + { + $aRet[$oObject->GetKey()] = $oObject; + } + else + { + $aRet[] = $oObject; + } + } + return $aRet; + } + + public function GetFilter() + { + return $this->m_oFilter; + } + + public function GetClass() + { + return $this->m_oFilter->GetClass(); + } + + public function GetRootClass() + { + return MetaModel::GetRootClass($this->GetClass()); + } + + public function Load() + { + if ($this->m_bLoaded) return; +// #@# debug - echo "Loading (".$this->m_oFilter->ToSibuSQL().")....
    \n"; + $sSQL = MetaModel::MakeSelectQuery($this->m_oFilter, $this->m_aOrderBy); + $resQuery = CMDBSource::Query($sSQL); + if (!$resQuery) return; + + while ($aRow = CMDBSource::FetchArray($resQuery)) + { + $sClass = $this->m_oFilter->GetClass(); + $oObject = MetaModel::GetObjectByRow($sClass, $aRow); + $this->AddObject($oObject); + } + CMDBSource::FreeResult($resQuery); + + $this->m_bLoaded = true; + } + + public function Count() + { + if (!$this->m_bLoaded) $this->Load(); + return count($this->m_aData); + } + + public function Fetch() + { + if (!$this->m_bLoaded) $this->Load(); + + if ($this->m_iCurrRow >= count($this->m_aData)) + { + return null; + } + $oRetObj = $this->m_aData[$this->m_iCurrRow]; + $this->m_iCurrRow++; + return $oRetObj; + } + + public function Rewind() + { + $this->Seek(0); + } + + public function Seek($iRow) + { + if (!$this->m_bLoaded) $this->Load(); + + $this->m_iCurrRow = min($iRow, count($this->m_aData)); + return $this->m_iCurrRow; + } + + public function AddObject($oObject) + { + // ?usefull? if ($oObject->GetClass() != $this->GetClass()) return; + + // it is mandatory to avoid duplicates + if (array_key_exists($oObject->GetKey(), $this->m_aId2Row)) return; + + // Do not load here, because the load uses that method too + $iNextPos = count($this->m_aData); + $this->m_aData[$iNextPos] = $oObject; + $this->m_aId2Row[$oObject->GetKey()] = $iNextPos; + } + + public function AddObjectArray($aObjects) + { + foreach ($aObjects as $oObj) + { + $this->AddObject($oObj); + } + } + + public function Merge($oObjectSet) + { + if ($this->GetRootClass() != $oObjectSet->GetRootClass()) + { + throw new CoreException("Could not merge two objects sets if they don't have the same root class"); + } + if (!$this->m_bLoaded) $this->Load(); + + $oObjectSet->Seek(0); + while ($oObject = $oObjectSet->Fetch()) + { + $this->AddObject($oObject); + } + } + + public function CreateIntersect($oObjectSet) + { + if ($this->GetRootClass() != $oObjectSet->GetRootClass()) + { + throw new CoreException("Could not 'intersect' two objects sets if they don't have the same root class"); + } + if (!$this->m_bLoaded) $this->Load(); + + $oNewSet = DBObjectSet::FromScratch($this->GetClass()); + + $oObjectSet->Seek(0); + while ($oObject = $oObjectSet->Fetch()) + { + if (array_key_exists($oObject->GetKey(), $this->m_aId2Row)) + { + $oNewSet->AddObject($oObject); + } + } + return $oNewSet; + } + + public function CreateDelta($oObjectSet) + { + if ($this->GetRootClass() != $oObjectSet->GetRootClass()) + { + throw new CoreException("Could not 'delta' two objects sets if they don't have the same root class"); + } + if (!$this->m_bLoaded) $this->Load(); + + $oNewSet = DBObjectSet::FromScratch($this->GetClass()); + + $oObjectSet->Seek(0); + while ($oObject = $oObjectSet->Fetch()) + { + if (!array_key_exists($oObject->GetKey(), $this->m_aId2Row)) + { + $oNewSet->AddObject($oObject); + } + } + return $oNewSet; + } + + public function GetRelatedObjects($sRelCode, $iMaxDepth = 99) + { + $aVisited = array(); // optimization for consecutive calls of MetaModel::GetRelatedObjects + $this->Seek(0); + while ($oObject = $this->Fetch()) + { + $aRelatedObjs = $oObject->GetRelatedObjects($sRelCode, $iMaxDepth, $aVisited); + } + return $aRelatedObjs; + } +} + +?> diff --git a/core/expression.class.inc.php b/core/expression.class.inc.php new file mode 100644 index 000000000..76409cdc1 --- /dev/null +++ b/core/expression.class.inc.php @@ -0,0 +1,485 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ +abstract class Expression +{ + // recursive translation of identifiers + abstract public function Translate($aTranslationData, $bMatchAll = true); + + // recursive rendering + abstract public function Render(); + + // recursively builds an array of class => fieldname + abstract public function ListRequiredFields(); + + abstract public function IsTrue(); + + public function RequiresField($sClass, $sFieldName) + { + // #@# todo - optimize : this is called quite often when building a single query ! + $aRequired = $this->ListRequiredFields(); + if (!in_array($sClass.'.'.$sFieldName, $aRequired)) return false; + return true; + } + + public function serialize() + { + return base64_encode($this->Render()); + } + + static public function unserialize($sValue) + { + return self::FromOQL(base64_decode($sValue)); + } + + static public function FromOQL($sConditionExpr) + { + $oOql = new OqlInterpreter($sConditionExpr); + $oExpression = $oOql->ParseExpression(); + + return $oExpression; + } + + public function LogAnd($oExpr) + { + if ($this->IsTrue()) return clone $oExpr; + if ($oExpr->IsTrue()) return clone $this; + return new BinaryExpression($this, 'AND', $oExpr); + } + + public function LogOr($oExpr) + { + return new BinaryExpression($this, 'OR', $oExpr); + } +} + + +class BinaryExpression extends Expression +{ + protected $m_oLeftExpr; // filter code or an SQL expression (later?) + protected $m_oRightExpr; + protected $m_sOperator; + + public function __construct($oLeftExpr, $sOperator, $oRightExpr) + { + if (!is_object($oLeftExpr)) + { + throw new CoreException('Expecting an Expression object on the left hand', array('found_type' => gettype($oLeftExpr))); + } + if (!is_object($oRightExpr)) + { + throw new CoreException('Expecting an Expression object on the right hand', array('found_type' => gettype($oRightExpr))); + } + if (!$oLeftExpr instanceof Expression) + { + throw new CoreException('Expecting an Expression object on the left hand', array('found_class' => get_class($oLeftExpr))); + } + if (!$oRightExpr instanceof Expression) + { + throw new CoreException('Expecting an Expression object on the right hand', array('found_class' => get_class($oRightExpr))); + } + $this->m_oLeftExpr = $oLeftExpr; + $this->m_oRightExpr = $oRightExpr; + $this->m_sOperator = $sOperator; + } + + public function IsTrue() + { + // return true if we are certain that it will be true + if ($this->m_sOperator == 'AND') + { + if ($this->m_oLeftExpr->IsTrue() && $this->m_oLeftExpr->IsTrue()) return true; + } + return false; + } + + public function GetLeftExpr() + { + return $this->m_oLeftExpr; + } + + public function GetRightExpr() + { + return $this->m_oRightExpr; + } + + public function GetOperator() + { + return $this->m_sOperator; + } + + // recursive rendering + public function Render() + { + $sOperator = $this->GetOperator(); + $sLeft = $this->GetLeftExpr()->Render(); + $sRight = $this->GetRightExpr()->Render(); + return "($sLeft $sOperator $sRight)"; + } + + public function Translate($aTranslationData, $bMatchAll = true) + { + $oLeft = $this->GetLeftExpr()->Translate($aTranslationData, $bMatchAll); + $oRight = $this->GetRightExpr()->Translate($aTranslationData, $bMatchAll); + return new BinaryExpression($oLeft, $this->GetOperator(), $oRight); + } + + public function ListRequiredFields() + { + $aLeft = $this->GetLeftExpr()->ListRequiredFields(); + $aRight = $this->GetRightExpr()->ListRequiredFields(); + return array_merge($aLeft, $aRight); + } +} + + +class UnaryExpression extends Expression +{ + protected $m_value; + + public function __construct($value) + { + $this->m_value = $value; + } + + public function IsTrue() + { + // return true if we are certain that it will be true + return ($this->m_value == 1); + } + + public function GetValue() + { + return $this->m_value; + } + + // recursive rendering + public function Render() + { + return CMDBSource::Quote($this->m_value); + } + + public function Translate($aTranslationData, $bMatchAll = true) + { + return clone $this; + } + + public function ListRequiredFields() + { + return array(); + } +} + +class ScalarExpression extends UnaryExpression +{ + public function __construct($value) + { + if (!is_scalar($value)) + { + throw new CoreException('Attempt to create a scalar expression from a non scalar', array('var_type'=>gettype($value))); + } + parent::__construct($value); + } +} + +class TrueExpression extends ScalarExpression +{ + public function __construct() + { + parent::__construct(1); + } + + public function IsTrue() + { + return true; + } +} + +class FieldExpression extends UnaryExpression +{ + protected $m_sParent; + protected $m_sName; + + public function __construct($sName, $sParent = '') + { + parent::__construct("$sParent.$sName"); + + $this->m_sParent = $sParent; + $this->m_sName = $sName; + } + + public function IsTrue() + { + // return true if we are certain that it will be true + return false; + } + + public function GetParent() {return $this->m_sParent;} + public function GetName() {return $this->m_sName;} + + // recursive rendering + public function Render() + { + if (empty($this->m_sParent)) + { + return "`{$this->m_sName}`"; + } + return "`{$this->m_sParent}`.`{$this->m_sName}`"; + } + + public function Translate($aTranslationData, $bMatchAll = true) + { + if (!array_key_exists($this->m_sParent, $aTranslationData)) + { + if ($bMatchAll) throw new CoreException('Unknown parent id in translation table', array('parent_id' => $this->m_sParent, 'translation_table' => array_keys($aTranslationData))); + return clone $this; + } + if (!array_key_exists($this->m_sName, $aTranslationData[$this->m_sParent])) + { + if (!array_key_exists('*', $aTranslationData[$this->m_sParent])) + { + // #@# debug - if ($bMatchAll) MyHelpers::var_dump_html($aTranslationData, true); + if ($bMatchAll) throw new CoreException('Unknown name in translation table', array('name' => $this->m_sName, 'parent_id' => $this->m_sParent, 'translation_table' => array_keys($aTranslationData[$this->m_sParent]))); + return clone $this; + } + $sNewParent = $aTranslationData[$this->m_sParent]['*']; + $sNewName = $this->m_sName; + } + else + { + $sNewParent = $aTranslationData[$this->m_sParent][$this->m_sName][0]; + $sNewName = $aTranslationData[$this->m_sParent][$this->m_sName][1]; + } + return new FieldExpression($sNewName, $sNewParent); + } + + public function ListRequiredFields() + { + return array($this->m_sParent.'.'.$this->m_sName); + } +} + + +// Temporary, until we implement functions and expression casting! +// ... or until we implement a real full text search based in the MATCH() expression +class ListExpression extends Expression +{ + protected $m_aExpressions; + + public function __construct($aExpressions) + { + $this->m_aExpressions = $aExpressions; + } + + public function IsTrue() + { + // return true if we are certain that it will be true + return false; + } + + public function GetItems() + { + return $this->m_aExpressions; + } + + // recursive rendering + public function Render() + { + $aRes = array(); + foreach ($this->m_aExpressions as $oExpr) + { + $aRes[] = $oExpr->Render(); + } + return '('.implode(', ', $aRes).')'; + } + + public function Translate($aTranslationData, $bMatchAll = true) + { + $aRes = array(); + foreach ($this->m_aExpressions as $oExpr) + { + $aRes[] = $oExpr->Translate($aTranslationData, $bMatchAll); + } + return new ListExpression($aRes); + } + + public function ListRequiredFields() + { + $aRes = array(); + foreach ($this->m_aExpressions as $oExpr) + { + $aRes = array_merge($aRes, $oExpr->ListRequiredFields()); + } + return $aRes; + } +} + + +class FunctionExpression extends Expression +{ + protected $m_sVerb; + protected $m_aArgs; // array of expressions + + public function __construct($sVerb, $aArgExpressions) + { + $this->m_sVerb = $sVerb; + $this->m_aArgs = $aArgExpressions; + } + + public function IsTrue() + { + // return true if we are certain that it will be true + return false; + } + + public function GetVerb() + { + return $this->m_sVerb; + } + + public function GetArgs() + { + return $this->m_aArgs; + } + + // recursive rendering + public function Render() + { + $aRes = array(); + foreach ($this->m_aArgs as $oExpr) + { + $aRes[] = $oExpr->Render(); + } + return $this->m_sVerb.'('.implode(', ', $aRes).')'; + } + + public function Translate($aTranslationData, $bMatchAll = true) + { + $aRes = array(); + foreach ($this->m_aArgs as $oExpr) + { + $aRes[] = $oExpr->Translate($aTranslationData, $bMatchAll); + } + return new FunctionExpression($this->m_sVerb, $aRes); + } + + public function ListRequiredFields() + { + $aRes = array(); + foreach ($this->m_aArgs as $oExpr) + { + $aRes = array_merge($aRes, $oExpr->ListRequiredFields()); + } + return $aRes; + } +} + +class IntervalExpression extends Expression +{ + protected $m_oValue; // expression + protected $m_sUnit; + + public function __construct($oValue, $sUnit) + { + $this->m_oValue = $oValue; + $this->m_sUnit = $sUnit; + } + + public function IsTrue() + { + // return true if we are certain that it will be true + return false; + } + + public function GetValue() + { + return $this->m_oValue; + } + + public function GetUnit() + { + return $this->m_sUnit; + } + + // recursive rendering + public function Render() + { + return 'INTERVAL '.$this->m_oValue->Render().' '.$this->m_sUnit; + } + + public function Translate($aTranslationData, $bMatchAll = true) + { + return new IntervalExpression($this->m_oValue->Translate($aTranslationData, $bMatchAll), $this->m_sUnit); + } + + public function ListRequiredFields() + { + return array(); + } +} + +class CharConcatExpression extends Expression +{ + protected $m_aExpressions; + + public function __construct($aExpressions) + { + $this->m_aExpressions = $aExpressions; + } + + public function IsTrue() + { + // return true if we are certain that it will be true + return false; + } + + public function GetItems() + { + return $this->m_aExpressions; + } + + // recursive rendering + public function Render() + { + $aRes = array(); + foreach ($this->m_aExpressions as $oExpr) + { + $sCol = $oExpr->Render(); + // Concat will be globally NULL if one single argument is null ! + $aRes[] = "COALESCE($sCol, '')"; + } + return "CAST(CONCAT(".implode(', ', $aRes).") AS CHAR)"; + } + + public function Translate($aTranslationData, $bMatchAll = true) + { + $aRes = array(); + foreach ($this->m_aExpressions as $oExpr) + { + $aRes[] = $oExpr->Translate($aTranslationData, $bMatchAll); + } + return new CharConcatExpression($aRes); + } + + public function ListRequiredFields() + { + $aRes = array(); + foreach ($this->m_aExpressions as $oExpr) + { + $aRes = array_merge($aRes, $oExpr->ListRequiredFields()); + } + return $aRes; + } +} + +?> diff --git a/core/filterdef.class.inc.php b/core/filterdef.class.inc.php new file mode 100644 index 000000000..371eadbd5 --- /dev/null +++ b/core/filterdef.class.inc.php @@ -0,0 +1,296 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ +abstract class FilterDefinition +{ + abstract public function GetType(); + abstract public function GetTypeDesc(); + + protected $m_sCode; + private $m_aParams = array(); + protected function Get($sParamName) {return $this->m_aParams[$sParamName];} + + public function __construct($sCode, $aParams = array()) + { + $this->m_sCode = $sCode; + $this->m_aParams = $aParams; + $this->ConsistencyCheck(); + } + + public function OverloadParams($aParams) + { + foreach ($aParams as $sParam => $value) + { + if (!array_key_exists($sParam, $this->m_aParams)) + { + throw new CoreException("Unknown attribute definition parameter '$sParam', please select a value in {".implode(", ", $this->m_aParams)."}"); + } + else + { + $this->m_aParams[$sParam] = $value; + } + } + } + + // to be overloaded + static protected function ListExpectedParams() + { + return array(); + } + + private function ConsistencyCheck() + { + // Check that any mandatory param has been specified + // + $aExpectedParams = $this->ListExpectedParams(); + foreach($aExpectedParams as $sParamName) + { + if (!array_key_exists($sParamName, $this->m_aParams)) + { + $aBacktrace = debug_backtrace(); + $sTargetClass = $aBacktrace[2]["class"]; + $sCodeInfo = $aBacktrace[1]["file"]." - ".$aBacktrace[1]["line"]; + throw new CoreException("ERROR missing parameter '$sParamName' in ".get_class($this)." declaration for class $sTargetClass ($sCodeInfo)"); + } + } + } + + public function GetCode() {return $this->m_sCode;} + abstract public function GetLabel(); + abstract public function GetValuesDef(); + + // returns an array of opcode=>oplabel (e.g. "differs from") + abstract public function GetOperators(); + // returns an opcode + abstract public function GetLooseOperator(); + abstract public function GetFilterSQLExpr($sOpCode, $value); + abstract public function TemporaryGetSQLCol(); + + // Wrapper - no need for overloading this one + public function GetOpDescription($sOpCode) + { + $aOperators = $this->GetOperators(); + if (!array_key_exists($sOpCode, $aOperators)) + { + throw new CoreException("Unknown operator '$sOpCode'"); + } + + return $aOperators[$sOpCode]; + } +} + +/** + * Match against the object unique identifier + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class FilterPrivateKey extends FilterDefinition +{ + static protected function ListExpectedParams() + { + return array_merge(parent::ListExpectedParams(), array("id_field")); + } + + public function GetType() {return "PrivateKey";} + public function GetTypeDesc() {return "Match against object identifier";} + + public function GetLabel() + { + return "Object Private Key"; + } + + public function GetValuesDef() + { + return null; + } + + public function GetOperators() + { + return array( + "="=>"equals", + "!="=>"differs from", + "IN"=>"in", + "NOTIN"=>"not in" + ); + } + public function GetLooseOperator() + { + return "IN"; + } + + public function GetFilterSQLExpr($sOpCode, $value) + { + $sFieldName = $this->Get("id_field"); + // #@# not obliged to quote... these are numbers !!! + $sQValue = CMDBSource::Quote($value); + switch($sOpCode) + { + case "IN": + if (!is_array($sQValue)) throw new CoreException("Expected an array for argument value (sOpCode='$sOpCode')"); + return "$sFieldName IN (".implode(", ", $sQValue).")"; + + case "NOTIN": + if (!is_array($sQValue)) throw new CoreException("Expected an array for argument value (sOpCode='$sOpCode')"); + return "$sFieldName NOT IN (".implode(", ", $sQValue).")"; + + case "!=": + return $sFieldName." != ".$sQValue; + + case "=": + default: + return $sFieldName." = ".$sQValue; + } + } + public function TemporaryGetSQLCol() + { + return $this->Get("id_field"); + } +} + +/** + * Match against an existing attribute (the attribute type will determine the available operators) + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class FilterFromAttribute extends FilterDefinition +{ + static protected function ListExpectedParams() + { + return array_merge(parent::ListExpectedParams(), array("refattribute")); + } + + public function __construct($oRefAttribute, $aParam = array()) + { + // In this very specific case, the code is the one of the attribute + // (this to get a very very simple syntax upon declaration) + $aParam["refattribute"] = $oRefAttribute; + parent::__construct($oRefAttribute->GetCode(), $aParam); + } + + public function GetType() {return "Basic";} + public function GetTypeDesc() {return "Match against field contents";} + + public function __GetRefAttribute() // for checking purposes only !!! + { + return $oAttDef = $this->Get("refattribute"); + } + + public function GetLabel() + { + $oAttDef = $this->Get("refattribute"); + return $oAttDef->GetLabel(); + } + + public function GetValuesDef() + { + $oAttDef = $this->Get("refattribute"); + return $oAttDef->GetValuesDef(); + } + + public function GetOperators() + { + $oAttDef = $this->Get("refattribute"); + return $oAttDef->GetBasicFilterOperators(); + } + public function GetLooseOperator() + { + $oAttDef = $this->Get("refattribute"); + return $oAttDef->GetBasicFilterLooseOperator(); + } + + public function GetFilterSQLExpr($sOpCode, $value) + { + $oAttDef = $this->Get("refattribute"); + return $oAttDef->GetBasicFilterSQLExpr($sOpCode, $value); + } + + public function TemporaryGetSQLCol() + { + $oAttDef = $this->Get("refattribute"); + return $oAttDef->GetSQLExpr(); + } +} + +/** + * Match against a given column (experimental -to be cleaned up later) + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class FilterDBValues extends FilterDefinition +{ + static protected function ListExpectedParams() + { + return array_merge(parent::ListExpectedParams(), array("dbfield")); + } + + public function GetType() {return "Values from DB";} + public function GetTypeDesc() {return "Match against the existing values in a field";} + + public function GetLabel() + { + return "enum de valeurs DB"; + } + + public function GetValuesDef() + { + return null; + } + + public function GetOperators() + { + return array( + "IN"=>"in", + ); + } + public function GetLooseOperator() + { + return "IN"; + } + + public function GetFilterSQLExpr($sOpCode, $value) + { + $sFieldName = $this->Get("dbfield"); + if (is_array($value) && !empty($value)) + { + $sValueList = "'".implode("', '", $value)."'"; + return "$sFieldName IN ($sValueList)"; + } + return "1=1"; + } + + public function TemporaryGetSQLCol() + { + return $this->Get("dbfield"); + } +} + +?> diff --git a/core/metamodel.class.php b/core/metamodel.class.php new file mode 100644 index 000000000..275ad454a --- /dev/null +++ b/core/metamodel.class.php @@ -0,0 +1,2643 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ +abstract class MetaModel +{ + /////////////////////////////////////////////////////////////////////////// + // + // STATIC Members + // + /////////////////////////////////////////////////////////////////////////// + + // Purpose: workaround the following limitation = PHP5 does not allow to know the class (derived from the current one) + // from which a static function is called (__CLASS__ and self are interpreted during parsing) + private static function GetCallersPHPClass($sExpectedFunctionName = null) + { + //var_dump(debug_backtrace()); + $aBacktrace = debug_backtrace(); + // $aBacktrace[0] is where we are + // $aBacktrace[1] is the caller of GetCallersPHPClass + // $aBacktrace[1] is the info we want + if (!empty($sExpectedFunctionName)) + { + assert('$aBacktrace[2]["function"] == $sExpectedFunctionName'); + } + return $aBacktrace[2]["class"]; + } + + // Static init -why and how it works + // + // We found the following limitations: + //- it is not possible to define non scalar constants + //- it is not possible to declare a static variable as '= new myclass()' + // Then we had do propose this model, in which a derived (non abstract) + // class should implement Init(), to call InheritAttributes or AddAttribute. + + private static function _check_subclass($sClass) + { + // See also IsValidClass()... ???? #@# + // class is mandatory + // (it is not possible to guess it when called as myderived::...) + if (!array_key_exists($sClass, self::$m_aClassParams)) + { + throw new CoreException("Unknown class '$sClass', expected a value in {".implode(', ', array_keys(self::$m_aClassParams))."}"); + } + } + + public static function static_var_dump() + { + var_dump(get_class_vars(__CLASS__)); + } + + private static $m_bDebugQuery = false; + private static $m_iStackDepthRef = 0; + + public static function StartDebugQuery() + { + $aBacktrace = debug_backtrace(); + self::$m_iStackDepthRef = count($aBacktrace); + self::$m_bDebugQuery = true; + } + public static function StopDebugQuery() + { + self::$m_bDebugQuery = false; + } + public static function DbgTrace($value) + { + if (!self::$m_bDebugQuery) return; + $aBacktrace = debug_backtrace(); + $iCallStackPos = count($aBacktrace) - self::$m_bDebugQuery; + $sIndent = ""; + for ($i = 0 ; $i < $iCallStackPos ; $i++) + { + $sIndent .= " .-=^=-. "; + } + $aCallers = array(); + foreach($aBacktrace as $aStackInfo) + { + $aCallers[] = $aStackInfo["function"]; + } + $sCallers = "Callstack: ".implode(', ', $aCallers); + $sFunction = "".$aBacktrace[1]["function"].""; + + if (is_string($value)) + { + echo "$sIndent$sFunction: $value
    \n"; + } + else if (is_object($value)) + { + echo "$sIndent$sFunction:\n
    \n";
    +			print_r($value);
    +			echo "
    \n"; + } + else + { + echo "$sIndent$sFunction: $value
    \n"; + } + } + + private static $m_sDBName = ""; + private static $m_sTablePrefix = ""; // table prefix for the current application instance (allow several applications on the same DB) + private static $m_Category2Class = array(); + private static $m_aRootClasses = array(); // array of "classname" => "rootclass" + private static $m_aParentClasses = array(); // array of ("classname" => array of "parentclass") + private static $m_aChildClasses = array(); // array of ("classname" => array of "childclass") + + private static $m_aClassParams = array(); // array of ("classname" => array of class information) + + static public function GetParentPersistentClass($sRefClass) + { + $sClass = get_parent_class($sRefClass); + if (!$sClass) return ''; + + if ($sClass == 'DBObject') return ''; // Warning: __CLASS__ is lower case in my version of PHP + + // Note: the UI/business model may implement pure PHP classes (intermediate layers) + if (array_key_exists($sClass, self::$m_aClassParams)) + { + return $sClass; + } + return self::GetParentPersistentClass($sClass); + } + + final static public function GetName($sClass) + { + self::_check_subclass($sClass); + return self::$m_aClassParams[$sClass]["name"]; + } + final static public function GetCategory($sClass) + { + self::_check_subclass($sClass); + return self::$m_aClassParams[$sClass]["category"]; + } + final static public function HasCategory($sClass, $sCategory) + { + self::_check_subclass($sClass); + return (strpos(self::$m_aClassParams[$sClass]["category"], $sCategory) !== false); + } + final static public function GetClassDescription($sClass) + { + self::_check_subclass($sClass); + return self::$m_aClassParams[$sClass]["description"]; + } + final static public function IsAutoIncrementKey($sClass) + { + self::_check_subclass($sClass); + return (self::$m_aClassParams[$sClass]["key_type"] == "autoincrement"); + } + final static public function GetKeyLabel($sClass) + { + self::_check_subclass($sClass); + return self::$m_aClassParams[$sClass]["key_label"]; + } + final static public function GetNameAttributeCode($sClass) + { + self::_check_subclass($sClass); + return self::$m_aClassParams[$sClass]["name_attcode"]; + } + final static public function GetStateAttributeCode($sClass) + { + self::_check_subclass($sClass); + return self::$m_aClassParams[$sClass]["state_attcode"]; + } + final static public function GetDefaultState($sClass) + { + $sDefaultState = ''; + $sStateAttrCode = self::GetStateAttributeCode($sClass); + if (!empty($sStateAttrCode)) + { + $oStateAttrDef = self::GetAttributeDef($sClass, $sStateAttrCode); + $sDefaultState = $oStateAttrDef->GetDefaultValue(); + } + return $sDefaultState; + } + final static public function GetReconcKeys($sClass) + { + self::_check_subclass($sClass); + return self::$m_aClassParams[$sClass]["reconc_keys"]; + } + final static public function GetDisplayTemplate($sClass) + { + self::_check_subclass($sClass); + return self::$m_aClassParams[$sClass]["display_template"]; + } + final static public function GetAttributeOrigin($sClass, $sAttCode) + { + self::_check_subclass($sClass); + return self::$m_aAttribOrigins[$sClass][$sAttCode]; + } + final static public function GetPrequisiteAttributes($sClass, $sAttCode) + { + self::_check_subclass($sClass); + $oAtt = self::GetAttributeDef($sClass, $sAttCode); + // Temporary implementation: later, we might be able to compute + // the dependencies, based on the attributes definition + // (allowed values and default values) + if ($oAtt->IsWritable()) + { + return $oAtt->GetPrerequisiteAttributes(); + } + else + { + return array(); + } + } + // #@# restore to private ? + final static public function DBGetTable($sClass, $sAttCode = null) + { + self::_check_subclass($sClass); + if (empty($sAttCode) || ($sAttCode == "id")) + { + $sTableRaw = self::$m_aClassParams[$sClass]["db_table"]; + if (empty($sTableRaw)) + { + // return an empty string whenever the table is undefined, meaning that there is no table associated to this 'abstract' class + return ''; + } + else + { + return self::$m_sTablePrefix.$sTableRaw; + } + } + // This attribute has been inherited (compound objects) + return self::DBGetTable(self::$m_aAttribOrigins[$sClass][$sAttCode]); + } + final static public function DBGetKey($sClass) + { + self::_check_subclass($sClass); + return self::$m_aClassParams[$sClass]["db_key_field"]; + } + final static public function DBGetClassField($sClass) + { + self::_check_subclass($sClass); + return self::$m_aClassParams[$sClass]["db_finalclass_field"]; + } + final static public function HasFinalClassField($sClass) + { + self::_check_subclass($sClass); + if (!array_key_exists("db_finalclass_field", self::$m_aClassParams[$sClass])) return false; + return (self::$m_aClassParams[$sClass]["db_finalclass_field"]); + } + final static public function IsStandaloneClass($sClass) + { + self::_check_subclass($sClass); + + $sRootClass = self::GetRootClass($sClass); + return (!self::HasFinalClassField($sRootClass)); + } + final static public function IsSameFamilyBranch($sClassA, $sClassB) + { + self::_check_subclass($sClassA); + self::_check_subclass($sClassB); + if (in_array($sClassA, self::$m_aParentClasses[$sClassB])) return true; + if (in_array($sClassB, self::$m_aParentClasses[$sClassA])) return true; + if ($sClassA == $sClassB) return true; + return false; + } + final static public function IsSameFamily($sClassA, $sClassB) + { + self::_check_subclass($sClassA); + self::_check_subclass($sClassB); + return (self::GetRootClass($sClassA) == self::GetRootClass($sClassB)); + } + + // Attributes of a given class may contain attributes defined in a parent class + // - Some attributes are a copy of the definition + // - Some attributes correspond to the upper class table definition (compound objects) + // (see also filters definition) + private static $m_aAttribDefs = array(); // array of ("classname" => array of attributes) + private static $m_aAttribOrigins = array(); // array of ("classname" => array of ("attcode"=>"sourceclass")) + private static $m_aExtKeyFriends = array(); // array of ("classname" => array of ("indirect ext key attcode"=> array of ("relative ext field"))) + final static public function ListAttributeDefs($sClass) + { + self::_check_subclass($sClass); + return self::$m_aAttribDefs[$sClass]; + } + + final public static function GetAttributesList($sClass) + { + self::_check_subclass($sClass); + return array_keys(self::$m_aAttribDefs[$sClass]); + } + + final public static function GetFiltersList($sClass) + { + self::_check_subclass($sClass); + return array_keys(self::$m_aFilterDefs[$sClass]); + } + + final public static function GetKeysList($sClass) + { + self::_check_subclass($sClass); + $aExtKeys = array(); + foreach(self::$m_aAttribDefs[$sClass] as $sAttCode => $oAttDef) + { + if ($oAttDef->IsExternalKey()) + { + $aExtKeys[] = $sAttCode; + } + } + return $aExtKeys; + } + + final static public function IsValidKeyAttCode($sClass, $sAttCode) + { + if (!array_key_exists($sClass, self::$m_aAttribDefs)) return false; + if (!array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass])) return false; + return (self::$m_aAttribDefs[$sClass][$sAttCode]->IsExternalKey()); + } + final static public function IsValidAttCode($sClass, $sAttCode) + { + if (!array_key_exists($sClass, self::$m_aAttribDefs)) return false; + return (array_key_exists($sAttCode, self::$m_aAttribDefs[$sClass])); + } + final static public function IsAttributeOrigin($sClass, $sAttCode) + { + return (self::$m_aAttribOrigins[$sClass][$sAttCode] == $sClass); + } + + final static public function IsValidFilterCode($sClass, $sFilterCode) + { + if (!array_key_exists($sClass, self::$m_aFilterDefs)) return false; + return (array_key_exists($sFilterCode, self::$m_aFilterDefs[$sClass])); + } + public static function IsValidClass($sClass) + { + return (array_key_exists($sClass, self::$m_aAttribDefs)); + } + + public static function IsReconcKey($sClass, $sAttCode) + { + return (in_array($sAttCode, self::GetReconcKeys($sClass))); + } + + final static public function GetAttributeDef($sClass, $sAttCode) + { + self::_check_subclass($sClass); + return self::$m_aAttribDefs[$sClass][$sAttCode]; + } + + final static public function GetExternalKeys($sClass) + { + $aExtKeys = array(); + foreach (self::ListAttributeDefs($sClass) as $sAttCode => $oAtt) + { + if ($oAtt->IsExternalKey()) + { + $aExtKeys[$sAttCode] = $oAtt; + } + } + return $aExtKeys; + } + + final static public function GetExternalFields($sClass, $sKeyAttCode) + { + $aExtFields = array(); + foreach (self::ListAttributeDefs($sClass) as $sAttCode => $oAtt) + { + if ($oAtt->IsExternalField() && ($oAtt->GetKeyAttCode() == $sKeyAttCode)) + { + $aExtFields[] = $oAtt; + } + } + return $aExtFields; + } + + final static public function GetExtKeyFriends($sClass, $sExtKeyAttCode) + { + if (array_key_exists($sExtKeyAttCode, self::$m_aExtKeyFriends[$sClass])) + { + return self::$m_aExtKeyFriends[$sClass][$sExtKeyAttCode]; + } + else + { + return array(); + } + } + + public static function GetLabel($sClass, $sAttCode) + { + $oAttDef = self::GetAttributeDef($sClass, $sAttCode); + if ($oAttDef) return $oAttDef->GetLabel(); + return ""; + } + + public static function GetDescription($sClass, $sAttCode) + { + $oAttDef = self::GetAttributeDef($sClass, $sAttCode); + if ($oAttDef) return $oAttDef->GetDescription(); + return ""; + } + + // Filters of a given class may contain filters defined in a parent class + // - Some filters are a copy of the definition + // - Some filters correspond to the upper class table definition (compound objects) + // (see also attributes definition) + private static $m_aFilterDefs = array(); // array of ("classname" => array filterdef) + private static $m_aFilterOrigins = array(); // array of ("classname" => array of ("attcode"=>"sourceclass")) + + public static function GetClassFilterDefs($sClass) + { + self::_check_subclass($sClass); + return self::$m_aFilterDefs[$sClass]; + } + + final static public function GetClassFilterDef($sClass, $sFilterCode) + { + self::_check_subclass($sClass); + return self::$m_aFilterDefs[$sClass][$sFilterCode]; + } + + public static function GetFilterLabel($sClass, $sFilterCode) + { + $oFilter = self::GetClassFilterDef($sClass, $sFilterCode); + if ($oFilter) return $oFilter->GetLabel(); + return ""; + } + + public static function GetFilterDescription($sClass, $sFilterCode) + { + $oFilter = self::GetClassFilterDef($sClass, $sFilterCode); + if ($oFilter) return $oFilter->GetDescription(); + return ""; + } + + // returns an array of opcode=>oplabel (e.g. "differs from") + public static function GetFilterOperators($sClass, $sFilterCode) + { + $oFilter = self::GetClassFilterDef($sClass, $sFilterCode); + if ($oFilter) return $oFilter->GetOperators(); + return array(); + } + + // returns an opcode + public static function GetFilterLooseOperator($sClass, $sFilterCode) + { + $oFilter = self::GetClassFilterDef($sClass, $sFilterCode); + if ($oFilter) return $oFilter->GetLooseOperator(); + return array(); + } + + public static function GetFilterOpDescription($sClass, $sFilterCode, $sOpCode) + { + $oFilter = self::GetClassFilterDef($sClass, $sFilterCode); + if ($oFilter) return $oFilter->GetOpDescription($sOpCode); + return ""; + } + + public static function GetFilterHTMLInput($sFilterCode) + { + return ""; + } + + // Lists of attributes/search filters + // + private static $m_aListInfos = array(); // array of ("listcode" => various info on the list, common to every classes) + private static $m_aListData = array(); // array of ("classname" => array of "listcode" => list) + // list may be an array of attcode / fltcode + // list may be an array of "groupname" => (array of attcode / fltcode) + + public static function EnumZLists() + { + return array_keys(self::$m_aListInfos); + } + + final static public function GetZListInfo($sListCode) + { + return self::$m_aListInfos[$sListCode]; + } + + public static function GetZListItems($sClass, $sListCode) + { + if (array_key_exists($sClass, self::$m_aListData)) + { + if (array_key_exists($sListCode, self::$m_aListData[$sClass])) + { + return self::$m_aListData[$sClass][$sListCode]; + } + } + $sParentClass = self::GetParentPersistentClass($sClass); + if (empty($sParentClass)) return array(); // nothing for the mother of all classes + // Dig recursively + return self::GetZListItems($sParentClass, $sListCode); + } + + public static function IsAttributeInZList($sClass, $sListCode, $sAttCodeOrFltCode, $sGroup = null) + { + $aZList = self::GetZListItems($sClass, $sListCode); + if (!$sGroup) + { + return (in_array($sAttCodeOrFltCode, $aZList)); + } + return (in_array($sAttCodeOrFltCode, $aZList[$sGroup])); + } + + // + // Relations + // + private static $m_aRelationInfos = array(); // array of ("relcode" => various info on the list, common to every classes) + + public static function EnumRelations() + { + return array_keys(self::$m_aRelationInfos); + } + + public static function EnumRelationProperties($sRelCode) + { + MyHelpers::CheckKeyInArray('relation code', $sRelCode, self::$m_aRelationInfos); + return self::$m_aRelationInfos[$sRelCode]; + } + + final static public function GetRelationProperty($sRelCode, $sProperty) + { + MyHelpers::CheckKeyInArray('relation code', $sRelCode, self::$m_aRelationInfos); + MyHelpers::CheckKeyInArray('relation property', $sProperty, self::$m_aRelationInfos[$sRelCode]); + + return self::$m_aRelationInfos[$sRelCode][$sProperty]; + } + + public static function EnumRelationQueries($sClass, $sRelCode) + { + MyHelpers::CheckKeyInArray('relation code', $sRelCode, self::$m_aRelationInfos); + return call_user_func_array(array($sClass, 'GetRelationQueries'), array($sRelCode)); + } + + // + // Object lifecycle model + // + private static $m_aStates = array(); // array of ("classname" => array of "statecode"=>array('label'=>..., 'description'=>..., attribute_inherit=> attribute_list=>...)) + private static $m_aStimuli = array(); // array of ("classname" => array of ("stimuluscode"=>array('label'=>..., 'description'=>...))) + private static $m_aTransitions = array(); // array of ("classname" => array of ("statcode_from"=>array of ("stimuluscode" => array('target_state'=>..., 'actions'=>array of handlers procs, 'user_restriction'=>TBD))) + + public static function EnumStates($sClass) + { + if (array_key_exists($sClass, self::$m_aStates)) + { + return self::$m_aStates[$sClass]; + } + else + { + return array(); + } + } + + public static function EnumStimuli($sClass) + { + if (array_key_exists($sClass, self::$m_aStimuli)) + { + return self::$m_aStimuli[$sClass]; + } + else + { + return array(); + } + } + + public static function EnumTransitions($sClass, $sStateCode) + { + if (array_key_exists($sClass, self::$m_aTransitions)) + { + if (array_key_exists($sStateCode, self::$m_aTransitions[$sClass])) + { + return self::$m_aTransitions[$sClass][$sStateCode]; + } + } + return array(); + } + public static function GetAttributeFlags($sClass, $sState, $sAttCode) + { + $iFlags = 0; // By default (if no life cycle) no flag at all + $sStateAttCode = self::GetStateAttributeCode($sClass); + if (!empty($sStateAttCode)) + { + $aStates = MetaModel::EnumStates($sClass); + $aCurrentState = $aStates[$sState]; + if ( (array_key_exists('attribute_list', $aCurrentState)) && (array_key_exists($sAttCode, $aCurrentState['attribute_list'])) ) + { + $iFlags = $aCurrentState['attribute_list'][$sAttCode]; + } + } + return $iFlags; + } + + // + // Allowed values + // + + public static function GetAllowedValues_att($sClass, $sAttCode, $aArgs = array(), $sBeginsWith = '') + { + $oAttDef = self::GetAttributeDef($sClass, $sAttCode); + if (!$oAttDef) return null; + $oValSetDef = $oAttDef->GetValuesDef(); + if (!$oValSetDef) return null; + return $oValSetDef->GetValues($aArgs, $sBeginsWith); + } + + public static function GetAllowedValues_flt($sClass, $sFltCode, $aArgs = array(), $sBeginsWith = '') + { + $oFltDef = self::GetClassFilterDef($sClass, $sFltCode); + if (!$oFltDef) return null; + $oValSetDef = $oFltDef->GetValuesDef(); + if (!$oValSetDef) return null; + return $oValSetDef->GetValues($aArgs, $sBeginsWith); + } + + // + // Businezz model declaration verbs (should be static) + // + + public static function RegisterZList($sListCode, $aListInfo) + { + // Check mandatory params + $aMandatParams = array( + "description" => "detailed (though one line) description of the list", + "type" => "attributes | filters", + ); + foreach($aMandatParams as $sParamName=>$sParamDesc) + { + if (!array_key_exists($sParamName, $aListInfo)) + { + throw new CoreException("Declaration of list $sListCode - missing parameter $sParamName"); + } + } + + self::$m_aListInfos[$sListCode] = $aListInfo; + } + + public static function RegisterRelation($sRelCode, $aRelationInfo) + { + // Check mandatory params + $aMandatParams = array( + "description" => "detailed (though one line) description of the list", + "verb_down" => "e.g.: 'impacts'", + "verb_up" => "e.g.: 'is impacted by'", + ); + foreach($aMandatParams as $sParamName=>$sParamDesc) + { + if (!array_key_exists($sParamName, $aRelationInfo)) + { + throw new CoreException("Declaration of relation $sRelCode - missing parameter $sParamName"); + } + } + + self::$m_aRelationInfos[$sRelCode] = $aRelationInfo; + } + + // Must be called once and only once... + public static function InitClasses($sTablePrefix) + { + if (count(self::GetClasses()) > 0) + { + throw new CoreException("InitClasses should not be called more than once -skipped"); + return; + } + + self::$m_sTablePrefix = $sTablePrefix; + + foreach(get_declared_classes() as $sPHPClass) { + if (is_subclass_of($sPHPClass, 'DBObject')) + { + if (method_exists($sPHPClass, 'Init')) + { + call_user_func(array($sPHPClass, 'Init')); + } + } + } + foreach (self::GetClasses() as $sClass) + { + // Compute the fields that will be used to display a pointer to another object + // + self::$m_aExtKeyFriends[$sClass] = array(); + foreach (self::$m_aAttribDefs[$sClass] as $sAttCode => $oAttDef) + { + if ($oAttDef->IsExternalKey(EXTKEY_ABSOLUTE)) + { + // oAttDef is either + // - an external KEY / FIELD (direct), + // - an external field pointing to an external KEY / FIELD + // - an external field pointing to an external field pointing to.... + + // Get the real external key attribute + // It will be our reference to determine the other ext fields related to the same ext key + $oFinalKeyAttDef = $oAttDef->GetKeyAttDef(EXTKEY_ABSOLUTE); + + self::$m_aExtKeyFriends[$sClass][$sAttCode] = array(); + foreach (self::GetExternalFields($sClass, $oAttDef->GetKeyAttCode($sAttCode)) as $oExtField) + { + // skip : those extfields will be processed as external keys + if ($oExtField->IsExternalKey(EXTKEY_ABSOLUTE)) continue; + + // Note: I could not compare the objects by the mean of '===' + // because they are copied for the inheritance, and the internal references are NOT updated + if ($oExtField->GetKeyAttDef(EXTKEY_ABSOLUTE) == $oFinalKeyAttDef) + { + self::$m_aExtKeyFriends[$sClass][$sAttCode][$oExtField->GetCode()] = $oExtField; + } + } + } + } + + // Add a 'id' filter + // + if (array_key_exists('id', self::$m_aAttribDefs[$sClass])) + { + throw new CoreException("Class $sClass, 'id' is a reserved keyword, it cannot be used as an attribute code"); + } + if (array_key_exists('id', self::$m_aFilterDefs[$sClass])) + { + throw new CoreException("Class $sClass, 'id' is a reserved keyword, it cannot be used as a filter code"); + } + $oFilter = new FilterPrivateKey('id', array('id_field' => self::DBGetKey($sClass))); + self::$m_aFilterDefs[$sClass]['id'] = $oFilter; + self::$m_aFilterOrigins[$sClass]['id'] = $sClass; + + // Add a 'class' attribute/filter to the root classes and their children + // + if (!self::IsStandaloneClass($sClass)) + { + if (array_key_exists('finalclass', self::$m_aAttribDefs[$sClass])) + { + throw new CoreException("Class $sClass, 'finalclass' is a reserved keyword, it cannot be used as an attribute code"); + } + if (array_key_exists('finalclass', self::$m_aFilterDefs[$sClass])) + { + throw new CoreException("Class $sClass, 'finalclass' is a reserved keyword, it cannot be used as a filter code"); + } + $sClassAttCode = 'finalclass'; + $sRootClass = self::GetRootClass($sClass); + $sDbFinalClassField = self::DBGetClassField($sRootClass); + $oClassAtt = new AttributeString($sClassAttCode, array( + "label"=>"Class", + "description"=>"Real (final) object class", + "allowed_values"=>null, + "sql"=>$sDbFinalClassField, + "default_value"=>$sClass, + "is_null_allowed"=>false, + "depends_on"=>array() + )); + self::$m_aAttribDefs[$sClass][$sClassAttCode] = $oClassAtt; + self::$m_aAttribOrigins[$sClass][$sClassAttCode] = $sRootClass; + + $oClassFlt = new FilterFromAttribute($oClassAtt); + self::$m_aFilterDefs[$sClass][$sClassAttCode] = $oClassFlt; + self::$m_aFilterOrigins[$sClass][$sClassAttCode] = self::GetRootClass($sClass); + } + } + + } + + // To be overriden, must be called for any object class (optimization) + public static function Init() + { + // In fact it is an ABSTRACT function, but this is not compatible with the fact that it is STATIC (error in E_STRICT interpretation) + } + // To be overloaded by biz model declarations + public static function GetRelationQueries($sRelCode) + { + // In fact it is an ABSTRACT function, but this is not compatible with the fact that it is STATIC (error in E_STRICT interpretation) + return array(); + } + + public static function Init_Params($aParams) + { + // Check mandatory params + $aMandatParams = array( + "category" => "group classes by modules defining their visibility in the UI", + "name" => "internal class name, may be different than the PHP class name", + "description" => "detailed (though one line) description of the class", + "key_type" => "autoincrement | string", + "key_label" => "if set, then display the key as an attribute", + "name_attcode" => "define wich attribute is the class name, may be an inherited attribute", + "state_attcode" => "define wich attribute is representing the state (object lifecycle)", + "reconc_keys" => "define the attributes that will 'almost uniquely' identify an object in batch processes", + "db_table" => "database table", + "db_key_field" => "database field which is the key", + "db_finalclass_field" => "database field wich is the reference to the actual class of the object, considering that this will be a compound class", + ); + + $sClass = self::GetCallersPHPClass("Init"); + if (!array_key_exists("name", $aParams)) + { + throw new CoreException("Declaration of class $sClass: missing name ({$aMandatParams["name"]})"); + } + + foreach($aMandatParams as $sParamName=>$sParamDesc) + { + if (!array_key_exists($sParamName, $aParams)) + { + throw new CoreException("Declaration of class $sClass - missing parameter $sParamName"); + } + } + + $aCategories = explode(',', $aParams['category']); + foreach ($aCategories as $sCategory) + { + self::$m_Category2Class[$sCategory][] = $sClass; + } + self::$m_Category2Class[''][] = $sClass; // all categories, include this one + + + self::$m_aRootClasses[$sClass] = $sClass; // first, let consider that I am the root... updated on inheritance + self::$m_aParentClasses[$sClass] = array(); + self::$m_aChildClasses[$sClass] = array(); + + self::$m_aClassParams[$sClass]= $aParams; + + self::$m_aAttribDefs[$sClass] = array(); + self::$m_aAttribOrigins[$sClass] = array(); + self::$m_aExtKeyFriends[$sClass] = array(); + self::$m_aFilterDefs[$sClass] = array(); + self::$m_aFilterOrigins[$sClass] = array(); + } + + protected static function object_array_mergeclone($aSource1, $aSource2) + { + $aRes = array(); + foreach ($aSource1 as $key=>$object) + { + $aRes[$key] = clone $object; + } + foreach ($aSource2 as $key=>$object) + { + $aRes[$key] = clone $object; + } + return $aRes; + } + + public static function Init_InheritAttributes($sSourceClass = null) + { + $sTargetClass = self::GetCallersPHPClass("Init"); + if (empty($sSourceClass)) + { + // Default: inherit from parent class + $sSourceClass = self::GetParentPersistentClass($sTargetClass); + if (empty($sSourceClass)) return; // no attributes for the mother of all classes + } + if (isset(self::$m_aAttribDefs[$sSourceClass])) + { + if (!isset(self::$m_aAttribDefs[$sTargetClass])) + { + self::$m_aAttribDefs[$sTargetClass] = array(); + self::$m_aAttribOrigins[$sTargetClass] = array(); + } + self::$m_aAttribDefs[$sTargetClass] = self::object_array_mergeclone(self::$m_aAttribDefs[$sTargetClass], self::$m_aAttribDefs[$sSourceClass]); + self::$m_aAttribOrigins[$sTargetClass] = array_merge(self::$m_aAttribOrigins[$sTargetClass], self::$m_aAttribOrigins[$sSourceClass]); + } + // later on, we might consider inheritance in different ways !!! + //if (strlen(self::DBGetTable($sSourceClass)) != 0) + if (self::HasFinalClassField(self::$m_aRootClasses[$sSourceClass])) + { + // Inherit the root class + self::$m_aRootClasses[$sTargetClass] = self::$m_aRootClasses[$sSourceClass]; + } + else + { + // I am a root class, standalone as well ! + // ???? + //self::$m_aRootClasses[$sTargetClass] = $sTargetClass; + } + self::$m_aParentClasses[$sTargetClass] += self::$m_aParentClasses[$sSourceClass]; + self::$m_aParentClasses[$sTargetClass][] = $sSourceClass; + // I am the child of each and every parent... + foreach(self::$m_aParentClasses[$sTargetClass] as $sAncestorClass) + { + self::$m_aChildClasses[$sAncestorClass][] = $sTargetClass; + } + } + public static function Init_OverloadAttributeParams($sAttCode, $aParams) + { + $sTargetClass = self::GetCallersPHPClass("Init"); + + if (!self::IsValidAttCode($sTargetClass, $sAttCode)) + { + throw new CoreException("Could not overload '$sAttCode', expecting a code from {".implode(", ", self::GetAttributesList($sTargetClass))."}"); + } + self::$m_aAttribDefs[$sTargetClass][$sAttCode]->OverloadParams($aParams); + } + public static function Init_AddAttribute(AttributeDefinition $oAtt) + { + $sTargetClass = self::GetCallersPHPClass("Init"); + self::$m_aAttribDefs[$sTargetClass][$oAtt->GetCode()] = $oAtt; + self::$m_aAttribOrigins[$sTargetClass][$oAtt->GetCode()] = $sTargetClass; + // Note: it looks redundant to put targetclass there, but a mix occurs when inheritance is used + + // Specific case of external fields: + // I wanted to simplify the syntax of the declaration of objects in the biz model + // Therefore, the reference to the host class is set there + $oAtt->SetHostClass($sTargetClass); + } + + public static function Init_InheritFilters($sSourceClass = null) + { + $sTargetClass = self::GetCallersPHPClass("Init"); + if (empty($sSourceClass)) + { + // Default: inherit from parent class + $sSourceClass = self::GetParentPersistentClass($sTargetClass); + if (empty($sSourceClass)) return; // no filters for the mother of all classes + } + if (isset(self::$m_aFilterDefs[$sSourceClass])) + { + if (!isset(self::$m_aFilterDefs[$sTargetClass])) + { + self::$m_aFilterDefs[$sTargetClass] = array(); + self::$m_aFilterOrigins[$sTargetClass] = array(); + } + + foreach (self::$m_aFilterDefs[$sSourceClass] as $sFltCode=>$oFilter) + { + if ($oFilter instanceof FilterFromAttribute) + { + // In that case, cloning is not enough: + // we must ensure that we will point to the correct + // attribute definition (in case some properties are overloaded) + $oAttDef1 = $oFilter->__GetRefAttribute(); + $oAttDef2 = self::GetAttributeDef($sTargetClass, $oAttDef1->GetCode()); + $oNewFilter = new FilterFromAttribute($oAttDef2); + } + else + { + $oNewFilter = clone $oFilter; + } + self::$m_aFilterDefs[$sTargetClass][$sFltCode] = $oNewFilter; + } + + self::$m_aFilterOrigins[$sTargetClass] = array_merge(self::$m_aFilterOrigins[$sTargetClass], self::$m_aFilterOrigins[$sSourceClass]); + } + } + + public static function Init_OverloadFilterParams($sFltCode, $aParams) + { + $sTargetClass = self::GetCallersPHPClass("Init"); + + if (!self::IsValidFilterCode($sTargetClass, $sFltCode)) + { + throw new CoreException("Could not overload '$sFltCode', expecting a code from {".implode(", ", self::GetFiltersList($sTargetClass))."}"); + } + self::$m_aFilterDefs[$sTargetClass][$sFltCode]->OverloadParams($aParams); + } + + public static function Init_AddFilter(FilterDefinition $oFilter) + { + $sTargetClass = self::GetCallersPHPClass("Init"); + self::$m_aFilterDefs[$sTargetClass][$oFilter->GetCode()] = $oFilter; + self::$m_aFilterOrigins[$sTargetClass][$oFilter->GetCode()] = $sTargetClass; + // Note: it looks redundant to put targetclass there, but a mix occurs when inheritance is used + } + public static function Init_AddFilterFromAttribute($sAttCode) + { + $sTargetClass = self::GetCallersPHPClass("Init"); + + $oAttDef = self::GetAttributeDef($sTargetClass, $sAttCode); + + $sFilterCode = $sAttCode; + $oNewFilter = new FilterFromAttribute($oAttDef); + self::$m_aFilterDefs[$sTargetClass][$sFilterCode] = $oNewFilter; + + if ($oAttDef->IsExternalField()) + { + $sKeyAttCode = $oAttDef->GetKeyAttCode(); + $oKeyDef = self::GetAttributeDef($sTargetClass, $sKeyAttCode); + self::$m_aFilterOrigins[$sTargetClass][$sFilterCode] = $oKeyDef->GetTargetClass(); + } + else + { + self::$m_aFilterOrigins[$sTargetClass][$sFilterCode] = $sTargetClass; + } + // Note: it looks redundant to put targetclass there, but a mix occurs when inheritance is used + } + + public static function Init_SetZListItems($sListCode, $aItems) + { + MyHelpers::CheckKeyInArray('list code', $sListCode, self::$m_aListInfos); + + $sTargetClass = self::GetCallersPHPClass("Init"); + self::$m_aListData[$sTargetClass][$sListCode] = $aItems; + } + + public static function Init_DefineState($sStateCode, $aStateDef) + { + $sTargetClass = self::GetCallersPHPClass("Init"); + if (is_null($aStateDef['attribute_list'])) $aStateDef['attribute_list'] = array(); + + $sParentState = $aStateDef['attribute_inherit']; + if (!empty($sParentState)) + { + // Inherit from the given state (must be defined !) + $aToInherit = self::$m_aStates[$sTargetClass][$sParentState]; + // The inherited configuration could be overriden + $aStateDef['attribute_list'] = array_merge($aToInherit, $aStateDef['attribute_list']); + } + self::$m_aStates[$sTargetClass][$sStateCode] = $aStateDef; + + // by default, create an empty set of transitions associated to that state + self::$m_aTransitions[$sTargetClass][$sStateCode] = array(); + } + + public static function Init_DefineStimulus($sStimulusCode, $oStimulus) + { + $sTargetClass = self::GetCallersPHPClass("Init"); + self::$m_aStimuli[$sTargetClass][$sStimulusCode] = $oStimulus; + } + + public static function Init_DefineTransition($sStateCode, $sStimulusCode, $aTransitionDef) + { + $sTargetClass = self::GetCallersPHPClass("Init"); + if (is_null($aTransitionDef['actions'])) $aTransitionDef['actions'] = array(); + self::$m_aTransitions[$sTargetClass][$sStateCode][$sStimulusCode] = $aTransitionDef; + } + + public static function Init_InheritLifecycle($sSourceClass = '') + { + $sTargetClass = self::GetCallersPHPClass("Init"); + if (empty($sSourceClass)) + { + // Default: inherit from parent class + $sSourceClass = self::GetParentPersistentClass($sTargetClass); + if (empty($sSourceClass)) return; // no attributes for the mother of all classes + } + + self::$m_aClassParams[$sTargetClass]["state_attcode"] = self::$m_aClassParams[$sSourceClass]["state_attcode"]; + self::$m_aStates[$sTargetClass] = clone self::$m_aStates[$sSourceClass]; + self::$m_aStimuli[$sTargetClass] = clone self::$m_aStimuli[$sSourceClass]; + self::$m_aTransitions[$sTargetClass] = clone self::$m_aTransitions[$sSourceClass]; + } + + // + // Static API + // + + public static function GetRootClass($sClass = null) + { + self::_check_subclass($sClass); + return self::$m_aRootClasses[$sClass]; + } + public static function IsRootClass($sClass) + { + self::_check_subclass($sClass); + return (self::GetRootClass($sClass) == $sClass); + } + public static function EnumParentClasses($sClass) + { + self::_check_subclass($sClass); + return self::$m_aParentClasses[$sClass]; + } + public static function EnumChildClasses($sClass, $iOption = ENUM_CHILD_CLASSES_EXCLUDETOP) + { + self::_check_subclass($sClass); + + $aRes = self::$m_aChildClasses[$sClass]; + if ($iOption != ENUM_CHILD_CLASSES_EXCLUDETOP) + { + // Add it to the list + $aRes[] = $sClass; + } + return $aRes; + } + + public static function EnumCategories() + { + return array_keys(self::$m_Category2Class); + } + + // Note: use EnumChildClasses to take the compound objects into account + public static function GetSubclasses($sClass) + { + self::_check_subclass($sClass); + $aSubClasses = array(); + foreach(get_declared_classes() as $sSubClass) { + if (is_subclass_of($sSubClass, $sClass)) + { + $aSubClasses[] = $sSubClass; + } + } + return $aSubClasses; + } + public static function GetClasses($sCategory = '') + { + if (array_key_exists($sCategory, self::$m_Category2Class)) + { + return self::$m_Category2Class[$sCategory]; + } + + if (count(self::$m_Category2Class) > 0) + { + throw new CoreException("unkown class category '$sCategory', expecting a value in {".implode(', ', array_keys(self::$m_Category2Class))."}"); + } + return array(); + } + + public static function IsAbstract($sClass) + { + if (strlen(self::DBGetTable($sClass)) == 0) return true; + return false; + } + + public static function MakeSelectQuery(DBObjectSearch $oFilter, $aOrderBy = array()) + { + $aTranslation = array(); + $aClassAliases = array(); + $aTableAliases = array(); + $oConditionTree = $oFilter->GetCriteria(); + $oSelect = self::MakeQuery($oFilter->GetClassAlias(), $oConditionTree, $aClassAliases, $aTableAliases, $aTranslation, $oFilter); + + // Check the order by specification + foreach ($aOrderBy as $sFieldAlias => $bAscending) + { + MyHelpers::CheckValueInArray('field name in ORDER BY spec', $sFieldAlias, self::GetAttributesList($oFilter->GetClass())); + if (!is_bool($bAscending)) + { + throw new CoreException("Wrong direction in ORDER BY spec, found '$bAscending' and expecting a boolean value"); + } + } + if (empty($aOrderBy)) + { + $sNameAttCode = self::GetNameAttributeCode($oFilter->GetClass()); + if (!empty($sNameAttCode)) + { + // By default, simply order on the "name" attribute, ascending + $aOrderBy = array($sNameAttCode => true); + } + } + + //MyHelpers::var_dump_html($oSelect->RenderSelect($aOrderBy)); + return $oSelect->RenderSelect($aOrderBy); + } + + public static function MakeDeleteQuery(DBObjectSearch $oFilter) + { + $aTranslation = array(); + $aClassAliases = array(); + $aTableAliases = array(); + $oConditionTree = $oFilter->GetCriteria(); + $oSelect = self::MakeQuery($oFilter->GetClassAlias(), $oConditionTree, $aClassAliases, $aTableAliases, $aTranslation, $oFilter); + return $oSelect->RenderDelete(); + } + + public static function MakeUpdateQuery(DBObjectSearch $oFilter, $aValues) + { + // $aValues is an array of $sAttCode => $value + $aTranslation = array(); + $aClassAliases = array(); + $aTableAliases = array(); + $oConditionTree = $oFilter->GetCriteria(); + $oSelect = self::MakeQuery($oFilter->GetClassAlias(), $oConditionTree, $aClassAliases, $aTableAliases, $aTranslation, $oFilter, array(), $aValues); + return $oSelect->RenderUpdate(); + } + + private static function MakeQuery($sGlobalTargetAlias, &$oConditionTree, &$aClassAliases, &$aTableAliases, &$aTranslation, DBObjectSearch $oFilter, $aExpectedAtts = array(), $aValues = array()) + { + // Note: query class might be different than the class of the filter + // -> this occurs when we are linking our class to an external class (referenced by, or pointing to) + // $aExpectedAtts is an array of sAttCode=>sAlias + $sClass = $oFilter->GetClass(); + $sClassAlias = $oFilter->GetClassAlias(); + + $bIsOnQueriedClass = ($sClassAlias == $sGlobalTargetAlias); + if ($bIsOnQueriedClass) + { + $aClassAliases = array_merge($aClassAliases, $oFilter->GetClasses()); + } + + self::DbgTrace("Entering: ".$oFilter->ToSibuSQL().", ".($bIsOnQueriedClass ? "MAIN" : "SECONDARY").", expectedatts=".count($aExpectedAtts).": ".implode(",", $aExpectedAtts)); + + $sRootClass = self::GetRootClass($sClass); + $sKeyField = self::DBGetKey($sClass); + + if (empty($aExpectedAtts) && $bIsOnQueriedClass) + { + // default to the whole list of attributes + the very std id/finalclass + $aExpectedAtts['id'] = 'id'; + foreach (self::GetAttributesList($sClass) as $sAttCode) + { + $aExpectedAtts[$sAttCode] = $sAttCode; // alias == attcode + } + } + + // Compute a clear view of external keys, and external attributes + // Build the list of external keys: + // -> ext keys required by a closed join ??? + // -> ext keys mentionned in a 'pointing to' condition + // -> ext keys required for an external field + // + $aExtKeys = array(); // array of sTableClass => array of (sAttCode (keys) => array of (sAttCode (fields)=> oAttDef)) + // + // Optimization: could be computed once for all (cached) + // Could be done in MakeQuerySingleTable ??? + // + + if ($bIsOnQueriedClass) + { + // Get all Ext keys for the queried class (??) + foreach(self::GetKeysList($sClass) as $sKeyAttCode) + { + $sKeyTableClass = self::$m_aAttribOrigins[$sClass][$sKeyAttCode]; + $aExtKeys[$sKeyTableClass][$sKeyAttCode] = array(); + } + } + // Get all Ext keys used by the filter + foreach ($oFilter->GetCriteria_PointingTo() as $sKeyAttCode => $trash) + { + $sKeyTableClass = self::$m_aAttribOrigins[$sClass][$sKeyAttCode]; + $aExtKeys[$sKeyTableClass][$sKeyAttCode] = array(); + } + // Add the ext fields used in the select (eventually adds an external key) + foreach(self::ListAttributeDefs($sClass) as $sAttCode=>$oAttDef) + { + if ($oAttDef->IsExternalField()) + { + $sKeyAttCode = $oAttDef->GetKeyAttCode(); + if (array_key_exists($sAttCode, $aExpectedAtts) || $oConditionTree->RequiresField($sClassAlias, $sAttCode)) + { + // Add the external attribute + $sKeyTableClass = self::$m_aAttribOrigins[$sClass][$sKeyAttCode]; + $aExtKeys[$sKeyTableClass][$sKeyAttCode][$sAttCode] = $oAttDef; + } + } + } + + // First query built upon on the leaf (ie current) class + // + self::DbgTrace("Main (=leaf) class, call MakeQuerySingleTable()"); + $oSelectBase = self::MakeQuerySingleTable($sGlobalTargetAlias, $oConditionTree, $aClassAliases, $aTableAliases, $aTranslation, $oFilter, $sClass, $aExpectedAtts, $aExtKeys, $aValues); + + // Then we join the queries of the eventual parent classes (compound model) + foreach(self::EnumParentClasses($sClass) as $sParentClass) + { + if (self::DBGetTable($sParentClass) == "") continue; + self::DbgTrace("Parent class: $sParentClass... let's call MakeQuerySingleTable()"); + $oSelectParentTable = self::MakeQuerySingleTable($sGlobalTargetAlias, $oConditionTree, $aClassAliases, $aTableAliases, $aTranslation, $oFilter, $sParentClass, $aExpectedAtts, $aExtKeys, $aValues); + $oSelectBase->AddInnerJoin($oSelectParentTable, $sKeyField, self::DBGetKey($sParentClass)); + } + + // Filter on objects referencing me + foreach ($oFilter->GetCriteria_ReferencedBy() as $sForeignClass => $aKeysAndFilters) + { + foreach ($aKeysAndFilters as $sForeignKeyAttCode => $oForeignFilter) + { + $oForeignKeyAttDef = self::GetAttributeDef($sForeignClass, $sForeignKeyAttCode); + + // We don't want any attribute from the foreign class, just filter on an inner join + $aExpAtts = array(); + + self::DbgTrace("Referenced by foreign key: $sForeignKeyAttCode... let's call MakeQuery()"); + //self::DbgTrace($oForeignFilter); + //self::DbgTrace($oForeignFilter->ToSibuSQL()); + //self::DbgTrace($oSelectForeign); + //self::DbgTrace($oSelectForeign->RenderSelect(array())); + $oSelectForeign = self::MakeQuery($sGlobalTargetAlias, $oConditionTree, $aClassAliases, $aTableAliases, $aTranslation, $oForeignFilter, $aExpAtts); + + $sForeignClassAlias = $oForeignFilter->GetClassAlias(); + $sForeignKeyTable = $aTranslation[$sForeignClassAlias][$sForeignKeyAttCode][0]; + $sForeignKeyColumn = $aTranslation[$sForeignClassAlias][$sForeignKeyAttCode][1]; + $oSelectBase->AddInnerJoin($oSelectForeign, $sKeyField, $sForeignKeyColumn, $sForeignKeyTable); + } + } + + // Filter on related objects + // + foreach ($oFilter->GetCriteria_RelatedTo() as $aCritInfo) + { + $oSubFilter = $aCritInfo['flt']; + $sRelCode = $aCritInfo['relcode']; + $iMaxDepth = $aCritInfo['maxdepth']; + + // Get the starting point objects + $oStartSet = new CMDBObjectSet($oSubFilter); + + // Get the objects related to those objects... recursively... + $aRelatedObjs = $oStartSet->GetRelatedObjects($sRelCode, $iMaxDepth); + $aRestriction = array_key_exists($sRootClass, $aRelatedObjs) ? $aRelatedObjs[$sRootClass] : array(); + + // #@# todo - related objects and expressions... + // Create condition + if (count($aRestriction) > 0) + { + $oSelectBase->AddCondition($sKeyField.' IN ('.implode(', ', CMDBSource::Quote(array_keys($aRestriction), true)).')'); + } + else + { + // Quick N'dirty -> generate an empty set + $oSelectBase->AddCondition('false'); + } + } + + // Translate the conditions... and go + // + if ($bIsOnQueriedClass) + { + $oConditionTranslated = $oConditionTree->Translate($aTranslation); + $oSelectBase->SetCondition($oConditionTranslated); + } + + // That's all... cross fingers and we'll get some working query + + //MyHelpers::var_dump_html($oSelectBase, true); + //MyHelpers::var_dump_html($oSelectBase->RenderSelect(), true); + if (self::$m_bDebugQuery) $oSelectBase->DisplayHtml(); + return $oSelectBase; + } + + protected static function MakeQuerySingleTable($sGlobalTargetAlias, &$oConditionTree, &$aClassAliases, &$aTableAliases, &$aTranslation, $oFilter, $sTableClass, $aExpectedAtts, $aExtKeys, $aValues) + { + // $aExpectedAtts is an array of sAttCode=>sAlias + // $aExtKeys is an array of sTableClass => array of (sAttCode (keys) => array of sAttCode (fields)) + + // Prepare the query for a single table (compound objects) + // Ignores the items (attributes/filters) that are not on the target table + // Perform an (inner or left) join for every external key (and specify the expected fields) + // + // Returns an SQLQuery + // + $sTargetClass = $oFilter->GetClass(); + $sTargetAlias = $oFilter->GetClassAlias(); + $sTable = self::DBGetTable($sTableClass); + $sTableAlias = self::GenerateUniqueAlias($aTableAliases, $sTargetAlias.'_'.$sTable, $sTable); + + $bIsOnQueriedClass = ($sTargetAlias == $sGlobalTargetAlias); + + self::DbgTrace("Entering: tableclass=$sTableClass, filter=".$oFilter->ToSibuSQL().", ".($bIsOnQueriedClass ? "MAIN" : "SECONDARY").", expectedatts=".count($aExpectedAtts).": ".implode(",", $aExpectedAtts)); + + // 1 - SELECT and UPDATE + // + // Note: no need for any values nor fields for foreign Classes (ie not the queried Class) + // + $aSelect = array(); + $aUpdateValues = array(); + + // 1/a - Get the key + // + if ($bIsOnQueriedClass) + { + $aSelect[$aExpectedAtts['id']] = new FieldExpression(self::DBGetKey($sTableClass), $sTableAlias); + } + // We need one pkey to be the key, let's take the one corresponding to the leaf + if ($sTableClass == $sTargetClass) + { + $aTranslation[$sTargetAlias]['id'] = array($sTableAlias, self::DBGetKey($sTableClass)); + } + + // 1/b - Get the other attributes + // + foreach(self::ListAttributeDefs($sTableClass) as $sAttCode=>$oAttDef) + { + // Skip this attribute if not defined in this table + if (self::$m_aAttribOrigins[$sTargetClass][$sAttCode] != $sTableClass) continue; + + // Skip this attribute if not writable (means that it does not correspond + if (count($oAttDef->DBGetUsedFields()) == 0) continue; + + // Update... + // + if ($bIsOnQueriedClass && array_key_exists($sAttCode, $aValues)) + { + assert ($oAttDef->IsDirectField()); + // Later, we'll have to use $oAttDef->GetDBField(); + $aUpdateValues[$oAttDef->GetSQLExpr()] = $oAttDef->RealValueToSQLValue($aValues[$sAttCode]); + } + + // Select... + // + // Skip, if a list of fields has been specified and it is not there + if (!array_key_exists($sAttCode, $aExpectedAtts)) continue; + $sAttAlias = $aExpectedAtts[$sAttCode]; + + if ($oAttDef->IsExternalField()) + { + // skip, this will be handled in the joined tables + } + else + { + // standard field, or external key + // add it to the output + $aSelect[$sAttAlias] = new FieldExpression($oAttDef->GetSQLExpr(), $sTableAlias); + } + } + + // 2 - WHERE + // + foreach(self::$m_aFilterDefs[$sTargetClass] as $sFltCode => $oFltAtt) + { + // Skip this filter if not defined in this table + if (self::$m_aFilterOrigins[$sTargetClass][$sFltCode] != $sTableClass) continue; + + // #@# todo - aller plus loin... a savoir que la table de translation doit contenir une "Expression" + // non-sens: $aTranslation[$sTargetAlias][$sFltCode] = array($sTableAlias, $oFltAtt->GetFilterSQLExpr(opcode, operand)); + $aTranslation[$sTargetAlias][$sFltCode] = array($sTableAlias, $oFltAtt->TemporaryGetSQLCol()); + } + + // #@# todo - See what a full text search condition should be + // 2' - WHERE / Full text search condition + // + if ($bIsOnQueriedClass) + { + $aFullText = $oFilter->GetCriteria_FullText(); + } + else + { + // Pourquoi ??? + $aFullText = array(); + } + + // 3 - The whole stuff, for this table only + // + $oSelectBase = new SQLQuery($sTable, $sTableAlias, $aSelect, null, $aFullText, $bIsOnQueriedClass, $aUpdateValues); + + // 4 - The external keys -> joins... + // + if (array_key_exists($sTableClass, $aExtKeys)) + { + foreach ($aExtKeys[$sTableClass] as $sKeyAttCode => $aExtFields) + { + $oKeyAttDef = self::GetAttributeDef($sTargetClass, $sKeyAttCode); + + $oExtFilter = $oFilter->GetCriteria_PointingTo($sKeyAttCode); + + // In case the join was not explicitely defined in the filter, + // we need to do it now + if (empty($oExtFilter)) + { + $sKeyClass = $oKeyAttDef->GetTargetClass(); + $sKeyClassAlias = self::GenerateUniqueAlias($aClassAliases, $sKeyClass.'_'.$sKeyAttCode, $sKeyClass); + $oExtFilter = new DBObjectSearch($sKeyClass, $sKeyClassAlias); + } + else + { + // The aliases should not conflict because normalization occured while building the filter + $sKeyClass = $oExtFilter->GetClass(); + $sKeyClassAlias = $oExtFilter->GetClassAlias(); + + // Note: there is no search condition in $oExtFilter, because normalization did merge the condition onto the top of the filter tree + } + + // Specify expected attributes for the target class query + // ... and use the current alias ! + $aExpAtts = array(); + $aIntermediateTranslation = array(); + foreach($aExtFields as $sAttCode => $oAtt) + { + + $sExtAttCode = $oAtt->GetExtAttCode(); + if (array_key_exists($sAttCode, $aExpectedAtts)) + { + // Request this attribute... transmit the alias ! + $aExpAtts[$sExtAttCode] = $aExpectedAtts[$sAttCode]; + } + // Translate mainclass.extfield => remoteclassalias.remotefieldcode + $oRemoteAttDef = self::GetAttributeDef($sKeyClass, $sExtAttCode); + $sRemoteAttExpr = $oRemoteAttDef->GetSQLExpr(); + $aIntermediateTranslation[$sTargetAlias][$sAttCode] = array($sKeyClassAlias, $sRemoteAttExpr); + //#@# debug - echo "

    $sTargetAlias.$sAttCode to $sKeyClassAlias.$sRemoteAttExpr (class: $sKeyClass)

    \n"; + } + $oConditionTree = $oConditionTree->Translate($aIntermediateTranslation, false); + + self::DbgTrace("External key $sKeyAttCode (class: $sKeyClass), call MakeQuery()"); + $oSelectExtKey = self::MakeQuery($sGlobalTargetAlias, $oConditionTree, $aClassAliases, $aTableAliases, $aTranslation, $oExtFilter, $aExpAtts); + + $sLocalKeyField = $oKeyAttDef->GetSQLExpr(); + $sExternalKeyField = self::DBGetKey($sKeyClass); + self::DbgTrace("External key $sKeyAttCode, Join on $sLocalKeyField = $sExternalKeyField"); + if ($oKeyAttDef->IsNullAllowed()) + { + $oSelectBase->AddLeftJoin($oSelectExtKey, $sLocalKeyField, $sExternalKeyField); + } + else + { + $oSelectBase->AddInnerJoin($oSelectExtKey, $sLocalKeyField, $sExternalKeyField); + } + } + } + + //MyHelpers::var_dump_html($oSelectBase->RenderSelect()); + return $oSelectBase; + } + + public static function GenerateUniqueAlias(&$aAliases, $sNewName, $sRealName) + { + if (!array_key_exists($sNewName, $aAliases)) + { + $aAliases[$sNewName] = $sRealName; + return $sNewName; + } + + for ($i = 1 ; $i < 100 ; $i++) + { + $sAnAlias = $sNewName.$i; + if (!array_key_exists($sAnAlias, $aAliases)) + { + // Create that new alias + $aAliases[$sAnAlias] = $sRealName; + return $sAnAlias; + } + } + throw new CoreException('Failed to create an alias', array('aliases' => $aAliases, 'new'=>$sNewName)); + } + + public static function CheckDefinitions() + { + if (count(self::GetClasses()) == 0) + { + throw new CoreException("MetaModel::InitClasses() has not been called, or no class has been declared ?!?!"); + } + + $aErrors = array(); + $aSugFix = array(); + foreach (self::GetClasses() as $sClass) + { + if (self::IsAbstract($sClass)) continue; + + $sNameAttCode = self::GetNameAttributeCode($sClass); + if (empty($sNameAttCode)) + { + // let's try this !!! + // $aErrors[$sClass][] = "Missing value for name definition: the framework will (should...) replace it by the id"; + // $aSugFix[$sClass][] = "Expecting a value in ".implode(", ", self::GetAttributesList($sClass)); + } + else if(!self::IsValidAttCode($sClass, $sNameAttCode)) + { + $aErrors[$sClass][] = "Unkown attribute code '".$sNameAttCode."' for the name definition"; + $aSugFix[$sClass][] = "Expecting a value in ".implode(", ", self::GetAttributesList($sClass)); + } + + foreach(self::GetReconcKeys($sClass) as $sReconcKeyAttCode) + if (!empty($sReconcKeyAttCode) && !self::IsValidAttCode($sClass, $sReconcKeyAttCode)) + { + $aErrors[$sClass][] = "Unkown attribute code '".$sReconcKeyAttCode."' in the list of reconciliation keys"; + $aSugFix[$sClass][] = "Expecting a value in ".implode(", ", self::GetAttributesList($sClass)); + } + + foreach(self::ListAttributeDefs($sClass) as $sAttCode=>$oAttDef) + { + // It makes no sense to check the attributes again and again in the subclasses + if (self::$m_aAttribOrigins[$sClass][$sAttCode] != $sClass) continue; + + if ($oAttDef->IsExternalKey()) + { + if (!self::IsValidClass($oAttDef->GetTargetClass())) + { + $aErrors[$sClass][] = "Unkown class '".$oAttDef->GetTargetClass()."' for the external key '$sAttCode'"; + $aSugFix[$sClass][] = "Expecting a value in {".implode(", ", self::GetClasses())."}"; + } + } + elseif ($oAttDef->IsExternalField()) + { + $sKeyAttCode = $oAttDef->GetKeyAttCode(); + if (!self::IsValidAttCode($sClass, $sKeyAttCode) || !self::IsValidKeyAttCode($sClass, $sKeyAttCode)) + { + $aErrors[$sClass][] = "Unkown key attribute code '".$sKeyAttCode."' for the external field $sAttCode"; + $aSugFix[$sClass][] = "Expecting a value in {".implode(", ", self::GetKeysList($sClass))."}"; + } + else + { + $oKeyAttDef = self::GetAttributeDef($sClass, $sKeyAttCode); + $sTargetClass = $oKeyAttDef->GetTargetClass(); + $sExtAttCode = $oAttDef->GetExtAttCode(); + if (!self::IsValidAttCode($sTargetClass, $sExtAttCode)) + { + $aErrors[$sClass][] = "Unkown key attribute code '".$sExtAttCode."' for the external field $sAttCode"; + $aSugFix[$sClass][] = "Expecting a value in {".implode(", ", self::GetKeysList($sTargetClass))."}"; + } + } + } + else // standard attributes + { + // Check that the default values definition is a valid object! + $oValSetDef = $oAttDef->GetValuesDef(); + if (!is_null($oValSetDef) && !$oValSetDef instanceof ValueSetDefinition) + { + $aErrors[$sClass][] = "Allowed values for attribute $sAttCode is not of the relevant type"; + $aSugFix[$sClass][] = "Please set it as an instance of a ValueSetDefinition object."; + } + else + { + // Default value must be listed in the allowed values (if defined) + $aAllowedValues = self::GetAllowedValues_att($sClass, $sAttCode); + if (!is_null($aAllowedValues)) + { + $sDefaultValue = $oAttDef->GetDefaultValue(); + if (!array_key_exists($sDefaultValue, $aAllowedValues)) + { + $aErrors[$sClass][] = "Default value '".$sDefaultValue."' for attribute $sAttCode is not an allowed value"; + $aSugFix[$sClass][] = "Please pickup the default value out of {'".implode(", ", array_keys($aAllowedValues))."'}"; + } + } + } + } + // Check dependencies + if ($oAttDef->IsWritable()) + { + foreach ($oAttDef->GetPrerequisiteAttributes() as $sDependOnAttCode) + { + if (!self::IsValidAttCode($sClass, $sDependOnAttCode)) + { + $aErrors[$sClass][] = "Unkown attribute code '".$sDependOnAttCode."' in the list of prerequisite attributes"; + $aSugFix[$sClass][] = "Expecting a value in ".implode(", ", self::GetAttributesList($sClass)); + } + } + } + } + foreach(self::GetClassFilterDefs($sClass) as $sFltCode=>$oFilterDef) + { + if (method_exists($oFilterDef, '__GetRefAttribute')) + { + $oAttDef = $oFilterDef->__GetRefAttribute(); + if (!self::IsValidAttCode($sClass, $oAttDef->GetCode())) + { + $aErrors[$sClass][] = "Wrong attribute code '".$oAttDef->GetCode()."' (wrong class) for the \"basic\" filter $sFltCode"; + $aSugFix[$sClass][] = "Expecting a value in {".implode(", ", self::GetAttributesList($sClass))."}"; + } + } + } + + // Lifecycle + // + $sStateAttCode = self::GetStateAttributeCode($sClass); + if (strlen($sStateAttCode) > 0) + { + // Lifecycle - check that the state attribute does exist as an attribute + if (!self::IsValidAttCode($sClass, $sStateAttCode)) + { + $aErrors[$sClass][] = "Unkown attribute code '".$sStateAttCode."' for the state definition"; + $aSugFix[$sClass][] = "Expecting a value in {".implode(", ", self::GetAttributesList($sClass))."}"; + } + else + { + // Lifecycle - check that there is a value set constraint on the state attribute + $aAllowedValuesRaw = self::GetAllowedValues_att($sClass, $sStateAttCode); + $aStates = array_keys(self::EnumStates($sClass)); + if (is_null($aAllowedValuesRaw)) + { + $aErrors[$sClass][] = "Attribute '".$sStateAttCode."' will reflect the state of the object. It must be restricted to a set of values"; + $aSugFix[$sClass][] = "Please define its allowed_values property as [new ValueSetEnum('".implode(", ", $aStates)."')]"; + } + else + { + $aAllowedValues = array_keys($aAllowedValuesRaw); + + // Lifecycle - check the the state attribute allowed values are defined states + foreach($aAllowedValues as $sValue) + { + if (!in_array($sValue, $aStates)) + { + $aErrors[$sClass][] = "Attribute '".$sStateAttCode."' (object state) has an allowed value ($sValue) which is not a known state"; + $aSugFix[$sClass][] = "You may define its allowed_values property as [new ValueSetEnum('".implode(", ", $aStates)."')], or reconsider the list of states"; + } + } + + // Lifecycle - check that defined states are allowed values + foreach($aStates as $sStateValue) + { + if (!in_array($sStateValue, $aAllowedValues)) + { + $aErrors[$sClass][] = "Attribute '".$sStateAttCode."' (object state) has a state ($sStateValue) which is not an allowed value"; + $aSugFix[$sClass][] = "You may define its allowed_values property as [new ValueSetEnum('".implode(", ", $aStates)."')], or reconsider the list of states"; + } + } + } + + // Lifcycle - check that the action handlers are defined + foreach (self::EnumStates($sClass) as $sStateCode => $aStateDef) + { + foreach(self::EnumTransitions($sClass, $sStateCode) as $sStimulusCode => $aTransitionDef) + { + foreach ($aTransitionDef['actions'] as $sActionHandler) + { + if (!method_exists($sClass, $sActionHandler)) + { + $aErrors[$sClass][] = "Unknown function '$sActionHandler' in transition [$sStateCode/$sStimulusCode] for state attribute '$sStateAttCode'"; + $aSugFix[$sClass][] = "Specify a function which prototype is in the form [public function $sActionHandler(\$sStimulusCode){return true;}]"; + } + } + } + } + } + } + + // ZList + // + foreach(self::EnumZLists() as $sListCode) + { + foreach (self::GetZListItems($sClass, $sListCode) as $sMyAttCode) + { + if (!self::IsValidAttCode($sClass, $sMyAttCode)) + { + $aErrors[$sClass][] = "Unkown attribute code '".$sMyAttCode."' from ZList '$sListCode'"; + $aSugFix[$sClass][] = "Expecting a value in {".implode(", ", self::GetAttributesList($sClass))."}"; + } + } + } + } + if (count($aErrors) > 0) + { + echo "
    "; + echo "

    Business model inconsistencies have been found

    \n"; + // #@# later -> this is the responsibility of the caller to format the output + foreach ($aErrors as $sClass => $aMessages) + { + echo "

    Wrong declaration for class $sClass

    \n"; + echo "
      \n"; + $i = 0; + foreach ($aMessages as $sMsg) + { + echo "
    • $sMsg ({$aSugFix[$sClass][$i]})
    • \n"; + $i++; + } + echo "
    \n"; + } + echo "

    Aborting...

    \n"; + echo "
    \n"; + exit; + } + } + + public static function DBShowApplyForm($sRepairUrl, $sSQLStatementArgName, $aSQLFixes) + { + if (empty($sRepairUrl)) return; + if (count($aSQLFixes) == 0) return; + + echo "
    \n"; + echo " \n"; + echo " \n"; + echo "
    \n"; + } + + public static function DBExists() + { + // returns true if at least one table exists (taking into account the DB sharing) + // then some tables might be missing, but that is made in DBCheckFormat + // + if (empty(self::$m_sTablePrefix)) + { + return CMDBSource::IsDB(self::$m_sDBName); + } + + // DB sharing + // Check if there is at least one table with the prefix + // + if (!CMDBSource::IsDB(self::$m_sDBName)) + { + return false; + } + CMDBSource::SelectDB(self::$m_sDBName); + + // If a table matches the prefix, then consider that the database already exists + $sSQL = "SHOW TABLES LIKE '".strtolower(self::$m_sTablePrefix)."%' "; + $result = CMDBSource::Query($sSQL); + return (CMDBSource::NbRows($result) > 0); + } + + public static function DBDrop() + { + $bDropEntireDB = true; + + if (!empty(self::$m_sTablePrefix)) + { + // Do drop only tables corresponding to the sub-database (table prefix) + // then possibly drop the DB itself (if no table remain) + foreach (CMDBSource::EnumTables() as $sTable) + { + // perform a case insensitive test because on Windows the table names become lowercase :-( + if (strtolower(substr($sTable, 0, strlen(self::$m_sTablePrefix))) == strtolower(self::$m_sTablePrefix)) + { + CMDBSource::DropTable($sTable); + } + else + { + // There is at least one table which is out of the scope of the current application + $bDropEntireDB = false; + } + } + } + + if ($bDropEntireDB) + { + CMDBSource::DropDB(self::$m_sDBName); + } + } + + + public static function DBCreate() + { + // Note: we have to check if the DB does exist, because we may share the DB + // with other applications (in which case the DB does exist, not the tables with the given prefix) + if (!CMDBSource::IsDB(self::$m_sDBName)) + { + CMDBSource::CreateDB(self::$m_sDBName); + } + self::DBCreateTables(); + } + + protected static function DBCreateTables() + { + list($aErrors, $aSugFix) = self::DBCheckFormat(); + + $aSQL = array(); + foreach ($aSugFix as $sClass => $aQueries) + { + foreach ($aQueries as $sQuery) + { + //$aSQL[] = $sQuery; + // forces a refresh of cached information + CMDBSource::CreateTable($sQuery); + } + } + // does not work -how to have multiple statements in a single query? + // $sDoCreateAll = implode(" ; ", $aSQL); + } + + public static function DBCheckFormat() + { + $aErrors = array(); + $aSugFix = array(); + foreach (self::GetClasses() as $sClass) + { + if (self::IsAbstract($sClass)) continue; + + // Check that the table exists + // + $sTable = self::DBGetTable($sClass); + $sKeyField = self::DBGetKey($sClass); + $sAutoIncrement = (self::IsAutoIncrementKey($sClass) ? "AUTO_INCREMENT" : ""); + if (!CMDBSource::IsTable($sTable)) + { + $aErrors[$sClass][] = "table '$sTable' could not be found into the DB"; + $aSugFix[$sClass][] = "CREATE TABLE `$sTable` (`$sKeyField` INT(11) NOT NULL $sAutoIncrement PRIMARY KEY) ENGINE = innodb"; + } + // Check that the key field exists + // + elseif (!CMDBSource::IsField($sTable, $sKeyField)) + { + $aErrors[$sClass][] = "key '$sKeyField' (table $sTable) could not be found"; + $aSugFix[$sClass][] = "ALTER TABLE `$sTable` ADD `$sKeyField` INT(11) NOT NULL $sAutoIncrement PRIMARY KEY"; + } + else + { + // Check the key field properties + // + if (!CMDBSource::IsKey($sTable, $sKeyField)) + { + $aErrors[$sClass][] = "key '$sKeyField' is not a key for table '$sTable'"; + $aSugFix[$sClass][] = "ALTER TABLE `$sTable`, DROP PRIMARY KEY, ADD PRIMARY key(`$sKeyField`)"; + } + if (self::IsAutoIncrementKey($sClass) && !CMDBSource::IsAutoIncrement($sTable, $sKeyField)) + { + $aErrors[$sClass][] = "key '$sKeyField' (table $sTable) is not automatically incremented"; + $aSugFix[$sClass][] = "ALTER TABLE `$sTable` CHANGE `$sKeyField` `$sKeyField` INT(11) NOT NULL AUTO_INCREMENT"; + } + } + + // Check that any defined field exists + // + $aTableInfo = CMDBSource::GetTableInfo($sTable); + + foreach(self::ListAttributeDefs($sClass) as $sAttCode=>$oAttDef) + { + // Skip this attribute if not originaly defined in this class + if (self::$m_aAttribOrigins[$sClass][$sAttCode] != $sClass) continue; + + foreach($oAttDef->DBGetUsedFields() as $sField) + { + $sDBFieldType = $oAttDef->GetDBFieldType(); + $sFieldSpecs = $oAttDef->IsNullAllowed() ? "$sDBFieldType NULL" : "$sDBFieldType NOT NULL"; + if (!CMDBSource::IsField($sTable, $sField)) + { + $aErrors[$sClass][] = "field '$sField' could not be found in table '$sTable'"; + $aSugFix[$sClass][] = "ALTER TABLE `$sTable` ADD `$sField` $sFieldSpecs"; + } + elseif ($oAttDef->IsNullAllowed() != CMDBSource::IsNullAllowed($sTable, $sField)) + { + if ($oAttDef->IsNullAllowed()) + { + $aErrors[$sClass][] = "field '$sField' in table '$sTable' could be NULL"; + $aSugFix[$sClass][] = "ALTER TABLE `$sTable` CHANGE `$sField` `$sField` $sFieldSpecs"; + } + else + { + $aErrors[$sClass][] = "field '$sField' in table '$sTable' could NOT be NULL"; + $aSugFix[$sClass][] = "ALTER TABLE `$sTable` CHANGE `$sField` `$sField` $sFieldSpecs"; + } + } + } + } + } + return array($aErrors, $aSugFix); + } + + + private static function DBCheckIntegrity_Check2Delete($sSelWrongRecs, $sErrorDesc, $sClass, &$aErrorsAndFixes, &$iNewDelCount, &$aPlannedDel, $bProcessingFriends = false) + { + $sRootClass = self::GetRootClass($sClass); + $sTable = self::DBGetTable($sClass); + $sKeyField = self::DBGetKey($sClass); + + if (array_key_exists($sTable, $aPlannedDel) && count($aPlannedDel[$sTable]) > 0) + { + $sSelWrongRecs .= " AND maintable.`$sKeyField` NOT IN ('".implode("', '", $aPlannedDel[$sTable])."')"; + } + $aWrongRecords = CMDBSource::QueryToCol($sSelWrongRecs, "id"); + if (count($aWrongRecords) == 0) return; + + if (!array_key_exists($sRootClass, $aErrorsAndFixes)) $aErrorsAndFixes[$sRootClass] = array(); + if (!array_key_exists($sTable, $aErrorsAndFixes[$sRootClass])) $aErrorsAndFixes[$sRootClass][$sTable] = array(); + + foreach ($aWrongRecords as $iRecordId) + { + if (array_key_exists($iRecordId, $aErrorsAndFixes[$sRootClass][$sTable])) + { + switch ($aErrorsAndFixes[$sRootClass][$sTable][$iRecordId]['Action']) + { + case 'Delete': + // Already planned for a deletion + // Let's concatenate the errors description together + $aErrorsAndFixes[$sRootClass][$sTable][$iRecordId]['Reason'] .= ', '.$sErrorDesc; + break; + + case 'Update': + // Let's plan a deletion + break; + } + } + else + { + $aErrorsAndFixes[$sRootClass][$sTable][$iRecordId]['Reason'] = $sErrorDesc; + } + + if (!$bProcessingFriends) + { + if (!array_key_exists($sTable, $aPlannedDel) || !in_array($iRecordId, $aPlannedDel[$sTable])) + { + // Something new to be deleted... + $iNewDelCount++; + } + } + + $aErrorsAndFixes[$sRootClass][$sTable][$iRecordId]['Action'] = 'Delete'; + $aErrorsAndFixes[$sRootClass][$sTable][$iRecordId]['Action_Details'] = array(); + $aErrorsAndFixes[$sRootClass][$sTable][$iRecordId]['Pass'] = 123; + $aPlannedDel[$sTable][] = $iRecordId; + } + + // Now make sure that we would delete the records of the other tables for that class + // + if (!$bProcessingFriends) + { + $sDeleteKeys = "'".implode("', '", $aWrongRecords)."'"; + foreach (self::EnumChildClasses($sRootClass, ENUM_CHILD_CLASSES_ALL) as $sFriendClass) + { + $sFriendTable = self::DBGetTable($sFriendClass); + $sFriendKey = self::DBGetKey($sFriendClass); + + // skip the current table + if ($sFriendTable == $sTable) continue; + + $sFindRelatedRec = "SELECT DISTINCT maintable.`$sFriendKey` AS id FROM `$sFriendTable` AS maintable WHERE maintable.`$sFriendKey` IN ($sDeleteKeys)"; + self::DBCheckIntegrity_Check2Delete($sFindRelatedRec, "Cascading deletion of record in friend table `$sTable`", $sFriendClass, $aErrorsAndFixes, $iNewDelCount, $aPlannedDel, true); + } + } + } + + private static function DBCheckIntegrity_Check2Update($sSelWrongRecs, $sErrorDesc, $sColumn, $sNewValue, $sClass, &$aErrorsAndFixes, &$iNewDelCount, &$aPlannedDel) + { + $sRootClass = self::GetRootClass($sClass); + $sTable = self::DBGetTable($sClass); + $sKeyField = self::DBGetKey($sClass); + + if (array_key_exists($sTable, $aPlannedDel) && count($aPlannedDel[$sTable]) > 0) + { + $sSelWrongRecs .= " AND maintable.`$sKeyField` NOT IN ('".implode("', '", $aPlannedDel[$sTable])."')"; + } + $aWrongRecords = CMDBSource::QueryToCol($sSelWrongRecs, "id"); + if (count($aWrongRecords) == 0) return; + + if (!array_key_exists($sRootClass, $aErrorsAndFixes)) $aErrorsAndFixes[$sRootClass] = array(); + if (!array_key_exists($sTable, $aErrorsAndFixes[$sRootClass])) $aErrorsAndFixes[$sRootClass][$sTable] = array(); + + foreach ($aWrongRecords as $iRecordId) + { + if (array_key_exists($iRecordId, $aErrorsAndFixes[$sRootClass][$sTable])) + { + switch ($aErrorsAndFixes[$sRootClass][$sTable][$iRecordId]['Action']) + { + case 'Delete': + // No need to update, the record will be deleted! + break; + + case 'Update': + // Already planned for an update + // Add this new update spec to the list + $bFoundSameSpec = false; + foreach ($aErrorsAndFixes[$sRootClass][$sTable][$iRecordId]['Action_Details'] as $aUpdateSpec) + { + if (($sColumn == $aUpdateSpec['column']) && ($sNewValue == $aUpdateSpec['newvalue'])) + { + $bFoundSameSpec = true; + } + } + if (!$bFoundSameSpec) + { + $aErrorsAndFixes[$sRootClass][$sTable][$iRecordId]['Action_Details'][] = (array('column' => $sColumn, 'newvalue'=>$sNewValue)); + $aErrorsAndFixes[$sRootClass][$sTable][$iRecordId]['Reason'] .= ', '.$sErrorDesc; + } + break; + } + } + else + { + $aErrorsAndFixes[$sRootClass][$sTable][$iRecordId]['Reason'] = $sErrorDesc; + $aErrorsAndFixes[$sRootClass][$sTable][$iRecordId]['Action'] = 'Update'; + $aErrorsAndFixes[$sRootClass][$sTable][$iRecordId]['Action_Details'] = array(array('column' => $sColumn, 'newvalue'=>$sNewValue)); + $aErrorsAndFixes[$sRootClass][$sTable][$iRecordId]['Pass'] = 123; + } + + } + } + + // returns the count of records found for deletion + public static function DBCheckIntegrity_SinglePass(&$aErrorsAndFixes, &$iNewDelCount, &$aPlannedDel) + { + foreach (self::GetClasses() as $sClass) + { + if (self::IsAbstract($sClass)) continue; + $sRootClass = self::GetRootClass($sClass); + $sTable = self::DBGetTable($sClass); + $sKeyField = self::DBGetKey($sClass); + + // Check that the final class field contains the name of a class which inherited from the current class + // + if (self::HasFinalClassField($sClass)) + { + $sFinalClassField = self::DBGetClassField($sClass); + + $aAllowedValues = self::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL); + $sAllowedValues = implode(",", CMDBSource::Quote($aAllowedValues, true)); + + $sSelWrongRecs = "SELECT DISTINCT maintable.`$sKeyField` AS id FROM `$sTable` AS maintable WHERE `$sFinalClassField` NOT IN ($sAllowedValues)"; + self::DBCheckIntegrity_Check2Delete($sSelWrongRecs, "final class (field `$sFinalClassField`) is wrong (expected a value in {".$sAllowedValues."})", $sClass, $aErrorsAndFixes, $iNewDelCount, $aPlannedDel); + } + + // Compound objects model - node/leaf classes (not the root itself) + // + if (!self::IsStandaloneClass($sClass) && !self::HasFinalClassField($sClass)) + { + $sRootTable = self::DBGetTable($sRootClass); + $sRootKey = self::DBGetKey($sRootClass); + $sFinalClassField = self::DBGetClassField($sRootClass); + + $aExpectedClasses = self::EnumChildClasses($sClass, ENUM_CHILD_CLASSES_ALL); + $sExpectedClasses = implode(",", CMDBSource::Quote($aExpectedClasses, true)); + + // Check that any record found here has its counterpart in the root table + // and which refers to a child class + // + $sSelWrongRecs = "SELECT DISTINCT maintable.`$sKeyField` AS id FROM `$sTable` as maintable LEFT JOIN `$sRootTable` ON maintable.`$sKeyField` = `$sRootTable`.`$sRootKey` AND `$sRootTable`.`$sFinalClassField` IN ($sExpectedClasses) WHERE `$sRootTable`.`$sRootKey` IS NULL"; + self::DBCheckIntegrity_Check2Delete($sSelWrongRecs, "Found a record in `$sTable`, but no counterpart in root table `$sRootTable` (inc. records pointing to a class in {".$sExpectedClasses."})", $sClass, $aErrorsAndFixes, $iNewDelCount, $aPlannedDel); + + // Check that any record found in the root table and referring to a child class + // has its counterpart here (detect orphan nodes -root or in the middle of the hierarchy) + // + $sSelWrongRecs = "SELECT DISTINCT maintable.`$sRootKey` AS id FROM `$sRootTable` AS maintable LEFT JOIN `$sTable` ON maintable.`$sRootKey` = `$sTable`.`$sKeyField` WHERE `$sTable`.`$sKeyField` IS NULL AND maintable.`$sFinalClassField` IN ($sExpectedClasses)"; + self::DBCheckIntegrity_Check2Delete($sSelWrongRecs, "Found a record in root table `$sRootTable`, but no counterpart in table `$sTable` (root records pointing to a class in {".$sExpectedClasses."})", $sRootClass, $aErrorsAndFixes, $iNewDelCount, $aPlannedDel); + } + + foreach(self::ListAttributeDefs($sClass) as $sAttCode=>$oAttDef) + { + // Skip this attribute if not defined in this table + if (self::$m_aAttribOrigins[$sClass][$sAttCode] != $sClass) continue; + + if ($oAttDef->IsExternalKey()) + { + // Check that any external field is pointing to an existing object + // + $sRemoteClass = $oAttDef->GetTargetClass(); + $sRemoteTable = self::DBGetTable($sRemoteClass); + $sRemoteKey = self::DBGetKey($sRemoteClass); + + $sExtKeyField = $oAttDef->GetSQLExpr(); + + // Note: a class/table may have an external key on itself + $sSelBase = "SELECT DISTINCT maintable.`$sKeyField` AS id, maintable.`$sExtKeyField` AS extkey FROM `$sTable` AS maintable LEFT JOIN `$sRemoteTable` ON maintable.`$sExtKeyField` = `$sRemoteTable`.`$sRemoteKey`"; + + $sSelWrongRecs = $sSelBase." WHERE `$sRemoteTable`.`$sRemoteKey` IS NULL"; + if ($oAttDef->IsNullAllowed()) + { + // Exclude the records pointing to 0/null from the errors + $sSelWrongRecs .= " AND maintable.`$sExtKeyField` IS NOT NULL"; + $sSelWrongRecs .= " AND maintable.`$sExtKeyField` != 0"; + self::DBCheckIntegrity_Check2Update($sSelWrongRecs, "Record pointing to (external key '$sAttCode') non existing objects", $sExtKeyField, 'null', $sClass, $aErrorsAndFixes, $iNewDelCount, $aPlannedDel); + } + else + { + self::DBCheckIntegrity_Check2Delete($sSelWrongRecs, "Record pointing to (external key '$sAttCode') non existing objects", $sClass, $aErrorsAndFixes, $iNewDelCount, $aPlannedDel); + } + + // Do almost the same, taking into account the records planned for deletion + if (array_key_exists($sRemoteTable, $aPlannedDel) && count($aPlannedDel[$sRemoteTable]) > 0) + { + // This could be done by the mean of a 'OR ... IN (aIgnoreRecords) + // but in that case you won't be able to track the root cause (cascading) + $sSelWrongRecs = $sSelBase." WHERE maintable.`$sExtKeyField` IN ('".implode("', '", $aPlannedDel[$sRemoteTable])."')"; + if ($oAttDef->IsNullAllowed()) + { + // Exclude the records pointing to 0/null from the errors + $sSelWrongRecs .= " AND maintable.`$sExtKeyField` IS NOT NULL"; + $sSelWrongRecs .= " AND maintable.`$sExtKeyField` != 0"; + self::DBCheckIntegrity_Check2Update($sSelWrongRecs, "Record pointing to (external key '$sAttCode') a record planned for deletion", $sExtKeyField, 'null', $sClass, $aErrorsAndFixes, $iNewDelCount, $aPlannedDel); + } + else + { + self::DBCheckIntegrity_Check2Delete($sSelWrongRecs, "Record pointing to (external key '$sAttCode') a record planned for deletion", $sClass, $aErrorsAndFixes, $iNewDelCount, $aPlannedDel); + } + } + } + else if ($oAttDef->IsDirectField()) + { + // Check that the values fit the allowed values + // + $aAllowedValues = self::GetAllowedValues_att($sClass, $sAttCode); + if (!is_null($aAllowedValues) && count($aAllowedValues) > 0) + { + $sExpectedValues = implode(",", CMDBSource::Quote(array_keys($aAllowedValues), true)); + + $sMyAttributeField = $oAttDef->GetSQLExpr(); + $sDefaultValue = $oAttDef->GetDefaultValue(); + $sSelWrongRecs = "SELECT DISTINCT maintable.`$sKeyField` AS id FROM `$sTable` AS maintable WHERE maintable.`$sMyAttributeField` NOT IN ($sExpectedValues)"; + self::DBCheckIntegrity_Check2Update($sSelWrongRecs, "Record having a column ('$sAttCode') with an unexpected value", $sMyAttributeField, CMDBSource::Quote($sDefaultValue), $sClass, $aErrorsAndFixes, $iNewDelCount, $aPlannedDel); + } + } + } + } + } + + public static function DBCheckIntegrity($sRepairUrl = "", $sSQLStatementArgName = "") + { + // Records in error, and action to be taken: delete or update + // by RootClass/Table/Record + $aErrorsAndFixes = array(); + + // Records to be ignored in the current/next pass + // by Table = array of RecordId + $aPlannedDel = array(); + + // Count of errors in the next pass: no error means that we can leave... + $iErrorCount = 0; + // Limit in case of a bug in the algorythm + $iLoopCount = 0; + + $iNewDelCount = 1; // startup... + while ($iNewDelCount > 0) + { + $iNewDelCount = 0; + self::DBCheckIntegrity_SinglePass($aErrorsAndFixes, $iNewDelCount, $aPlannedDel); + $iErrorCount += $iNewDelCount; + + // Safety net #1 - limit the planned deletions + // + $iMaxDel = 1000; + $iPlannedDel = 0; + foreach ($aPlannedDel as $sTable => $aPlannedDelOnTable) + { + $iPlannedDel += count($aPlannedDelOnTable); + } + if ($iPlannedDel > $iMaxDel) + { + throw new CoreWarning("DB Integrity Check safety net - Exceeding the limit of $iMaxDel planned record deletion"); + break; + } + // Safety net #2 - limit the iterations + // + $iLoopCount++; + $iMaxLoops = 10; + if ($iLoopCount > $iMaxLoops) + { + throw new CoreWarning("DB Integrity Check safety net - Reached the limit of $iMaxLoops loops"); + break; + } + } + + // Display the results + // + $iIssueCount = 0; + $aFixesDelete = array(); + $aFixesUpdate = array(); + + foreach ($aErrorsAndFixes as $sRootClass => $aTables) + { + foreach ($aTables as $sTable => $aRecords) + { + foreach ($aRecords as $iRecord => $aError) + { + $sAction = $aError['Action']; + $sReason = $aError['Reason']; + $iPass = $aError['Pass']; + + switch ($sAction) + { + case 'Delete': + $sActionDetails = ""; + $aFixesDelete[$sTable][] = $iRecord; + break; + + case 'Update': + $aUpdateDesc = array(); + foreach($aError['Action_Details'] as $aUpdateSpec) + { + $aUpdateDesc[] = $aUpdateSpec['column']." -> ".$aUpdateSpec['newvalue']; + $aFixesUpdate[$sTable][$aUpdateSpec['column']][$aUpdateSpec['newvalue']][] = $iRecord; + } + $sActionDetails = "Set ".implode(", ", $aUpdateDesc); + + break; + + default: + $sActionDetails = "bug: unknown action '$sAction'"; + } + $aIssues[] = "$sRootClass / $sTable / $iRecord / $sReason / $sAction / $sActionDetails"; + $iIssueCount++; + } + } + } + + if ($iIssueCount > 0) + { + // Build the queries to fix in the database + // + // First step, be able to get class data out of the table name + // Could be optimized, because we've made the job earlier... but few benefits, so... + $aTable2ClassProp = array(); + foreach (self::GetClasses() as $sClass) + { + if (self::IsAbstract($sClass)) continue; + + $sRootClass = self::GetRootClass($sClass); + $sTable = self::DBGetTable($sClass); + $sKeyField = self::DBGetKey($sClass); + + $aErrorsAndFixes[$sRootClass][$sTable] = array(); + $aTable2ClassProp[$sTable] = array('rootclass'=>$sRootClass, 'class'=>$sClass, 'keyfield'=>$sKeyField); + } + // Second step, build a flat list of SQL queries + $aSQLFixes = array(); + $iPlannedUpdate = 0; + foreach ($aFixesUpdate as $sTable => $aColumns) + { + foreach ($aColumns as $sColumn => $aNewValues) + { + foreach ($aNewValues as $sNewValue => $aRecords) + { + $iPlannedUpdate += count($aRecords); + $sWrongRecords = "'".implode("', '", $aRecords)."'"; + $sKeyField = $aTable2ClassProp[$sTable]['keyfield']; + + $aSQLFixes[] = "UPDATE `$sTable` SET `$sColumn` = $sNewValue WHERE `$sKeyField` IN ($sWrongRecords)"; + } + } + } + $iPlannedDel = 0; + foreach ($aFixesDelete as $sTable => $aRecords) + { + $iPlannedDel += count($aRecords); + $sWrongRecords = "'".implode("', '", $aRecords)."'"; + $sKeyField = $aTable2ClassProp[$sTable]['keyfield']; + + $aSQLFixes[] = "DELETE FROM `$sTable` WHERE `$sKeyField` IN ($sWrongRecords)"; + } + + // Report the results + // + echo "
    "; + echo "

    Database corruption error(s): $iErrorCount issues have been encountered. $iPlannedDel records will be deleted, $iPlannedUpdate records will be updated:

    \n"; + // #@# later -> this is the responsibility of the caller to format the output + echo "
      \n"; + foreach ($aIssues as $sIssueDesc) + { + echo "
    • $sIssueDesc
    • \n"; + } + echo "
    \n"; + self::DBShowApplyForm($sRepairUrl, $sSQLStatementArgName, $aSQLFixes); + echo "

    Aborting...

    \n"; + echo "
    \n"; + exit; + } + } + + public static function Startup($sConfigFile, $bAllowMissingDB = false) + { + self::LoadConfig($sConfigFile); + if (self::DBExists()) + { + CMDBSource::SelectDB(self::$m_sDBName); + } + else + { + if (!$bAllowMissingDB) + { + throw new CoreException('Database not found, check your configuration file', array('config_file'=>$sConfigFile, 'db_name'=>self::$m_sDBName)); + } + } + } + + public static function LoadConfig($sConfigFile) + { + $oConfig = new Config($sConfigFile); + + foreach ($oConfig->GetAppModules() as $sModule => $sToInclude) + { + self::Plugin($sConfigFile, 'application', $sToInclude); + } + foreach ($oConfig->GetDataModels() as $sModule => $sToInclude) + { + self::Plugin($sConfigFile, 'business', $sToInclude); + } + foreach ($oConfig->GetAddons() as $sModule => $sToInclude) + { + self::Plugin($sConfigFile, 'addons', $sToInclude); + } + + $sServer = $oConfig->GetDBHost(); + $sUser = $oConfig->GetDBUser(); + $sPwd = $oConfig->GetDBPwd(); + $sSource = $oConfig->GetDBName(); + $sTablePrefix = $oConfig->GetDBSubname(); + + // The include have been included, let's browse the existing classes and + // develop some data based on the proposed model + self::InitClasses($sTablePrefix); + + self::$m_sDBName = $sSource; + self::$m_sTablePrefix = $sTablePrefix; + + CMDBSource::Init($sServer, $sUser, $sPwd); // do not select the DB (could not exist) + } + + protected static function Plugin($sConfigFile, $sModuleType, $sToInclude) + { + if (!file_exists($sToInclude)) + { + throw new CoreException('Wrong filename in configuration file', array('file' => $sConfigFile, 'module' => $sModuleType, 'filename' => $sToInclude)); + } + require_once($sToInclude); + } + + // Building an object + // + // + private static $aQueryCacheGetObject = array(); + private static $aQueryCacheGetObjectHits = array(); + public static function GetQueryCacheStatus() + { + $aRes = array(); + $iTotalHits = 0; + foreach(self::$aQueryCacheGetObjectHits as $sClass => $iHits) + { + $aRes[] = "$sClass: $iHits"; + $iTotalHits += $iHits; + } + return $iTotalHits.' ('.implode(', ', $aRes).')'; + } + + public static function MakeSingleRow($sClass, $iKey) + { + if (!array_key_exists($sClass, self::$aQueryCacheGetObject)) + { + // NOTE: Quick and VERY dirty caching mechanism which relies on + // the fact that the string '987654321' will never appear in the + // standard query + // This will be replaced for sure with a prepared statement + // or a view... next optimization to come! + $oFilter = new DBObjectSearch($sClass); + $oFilter->AddCondition('id', 987654321, '='); + + $sSQL = self::MakeSelectQuery($oFilter); + self::$aQueryCacheGetObject[$sClass] = $sSQL; + self::$aQueryCacheGetObjectHits[$sClass] = 0; + } + else + { + $sSQL = self::$aQueryCacheGetObject[$sClass]; + self::$aQueryCacheGetObjectHits[$sClass] += 1; +// echo " -load $sClass/$iKey- ".self::$aQueryCacheGetObjectHits[$sClass]."
    \n"; + } + $sSQL = str_replace('987654321', CMDBSource::Quote($iKey), $sSQL); + $res = CMDBSource::Query($sSQL); + + $aRow = CMDBSource::FetchArray($res); + CMDBSource::FreeResult($res); + if (empty($aRow)) + { + throw new CoreException("No result for the single row query: '$sSQL'"); + } + return $aRow; + } + + public static function GetObjectByRow($sClass, $aRow) + { + self::_check_subclass($sClass); + + // Compound objects: if available, get the final object class + // + if (!array_key_exists("finalclass", $aRow)) + { + // Either this is a bug (forgot to specify a root class with a finalclass field + // Or this is the expected behavior, because the object is not made of several tables + } + elseif (empty($aRow["finalclass"])) + { + // The data is missing in the DB + // @#@ possible improvement: check that the class is valid ! + $sRootClass = self::GetRootClass($sClass); + $sFinalClassField = self::DBGetClassField($sRootClass); + throw new CoreException("Empty class name for object $sClass::{$aRow["id"]} (root class '$sRootClass', field '{$sFinalClassField}' is empty)"); + } + else + { + // do the job for the real target class + $sClass = $aRow["finalclass"]; + } + return new $sClass($aRow); + } + + public static function GetObject($sClass, $iKey) + { + self::_check_subclass($sClass); + $aRow = self::MakeSingleRow($sClass, $iKey); + if (empty($aRow)) + { + return null; + } + return self::GetObjectByRow($sClass, $aRow); + } + + public static function NewObject($sClass) + { + self::_check_subclass($sClass); + return new $sClass(); + } + + public static function BulkDelete(DBObjectSearch $oFilter) + { + $sSQL = self::MakeDeleteQuery($oFilter); + CMDBSource::Query($sSQL); + } + + public static function BulkUpdate(DBObjectSearch $oFilter, array $aValues) + { + // $aValues is an array of $sAttCode => $value + $sSQL = self::MakeUpdateQuery($oFilter, $aValues); + CMDBSource::Query($sSQL); + } + + // Links + // + // + public static function EnumReferencedClasses($sClass) + { + self::_check_subclass($sClass); + + // 1-N links (referenced by my class), returns an array of sAttCode=>sClass + $aResult = array(); + foreach(self::$m_aAttribDefs[$sClass] as $sAttCode=>$oAttDef) + { + if ($oAttDef->IsExternalKey()) + { + $aResult[$sAttCode] = $oAttDef->GetTargetClass(); + } + } + return $aResult; + } + public static function EnumReferencingClasses($sClass, $bSkipLinkingClasses = false) + { + self::_check_subclass($sClass); + + $aLinksClasses = self::EnumLinksClasses(); + + // 1-N links (referencing my class), array of sClass => array of sAttcode + $aResult = array(); + foreach (self::$m_aAttribDefs as $sSomeClass=>$aClassAttributes) + { + if ($bSkipLinkingClasses && in_array($sSomeClass, $aLinksClasses)) continue; + + $aExtKeys = array(); + foreach ($aClassAttributes as $sAttCode=>$oAttDef) + { + if (self::$m_aAttribOrigins[$sSomeClass][$sAttCode] != $sSomeClass) continue; + if ($oAttDef->IsExternalKey() && ($oAttDef->GetTargetClass() == $sClass)) + { + $aExtKeys[] = $sAttCode; + } + } + if (count($aExtKeys) != 0) + { + $aResult[$sSomeClass] = $aExtKeys; + } + } + return $aResult; + } + public static function EnumLinksClasses() + { + // Returns a flat array of classes having at least two external keys + $aResult = array(); + foreach (self::$m_aAttribDefs as $sSomeClass=>$aClassAttributes) + { + $iExtKeyCount = 0; + foreach ($aClassAttributes as $sAttCode=>$oAttDef) + { + if (self::$m_aAttribOrigins[$sSomeClass][$sAttCode] != $sSomeClass) continue; + if ($oAttDef->IsExternalKey()) + { + $iExtKeyCount++; + } + } + if ($iExtKeyCount >= 2) + { + $aResult[] = $sSomeClass; + } + } + return $aResult; + } + public static function EnumLinkingClasses($sClass = "") + { + // N-N links, array of sLinkClass => (array of sAttCode=>sClass) + $aResult = array(); + foreach (self::EnumLinksClasses() as $sSomeClass) + { + $aTargets = array(); + $bFoundClass = false; + foreach (self::ListAttributeDefs($sSomeClass) as $sAttCode=>$oAttDef) + { + if (self::$m_aAttribOrigins[$sSomeClass][$sAttCode] != $sSomeClass) continue; + if ($oAttDef->IsExternalKey()) + { + $sRemoteClass = $oAttDef->GetTargetClass(); + if (empty($sClass)) + { + $aTargets[$sAttCode] = $sRemoteClass; + } + elseif ($sClass == $sRemoteClass) + { + $bFoundClass = true; + } + else + { + $aTargets[$sAttCode] = $sRemoteClass; + } + } + } + if (empty($sClass) || $bFoundClass) + { + $aResult[$sSomeClass] = $aTargets; + } + } + return $aResult; + } + + public static function GetLinkLabel($sLinkClass, $sAttCode) + { + self::_check_subclass($sLinkClass); + + // e.g. "supported by" (later: $this->GetLinkLabel(), computed on link data!) + return self::GetLabel($sLinkClass, $sAttCode); + } + +} // class MetaModel + + +// Standard attribute lists +MetaModel::RegisterZList("noneditable", array("description"=>"non editable fields", "type"=>"attributes")); + +MetaModel::RegisterZList("details", array("description"=>"All attributes to be displayed for the 'details' of an object", "type"=>"attributes")); +MetaModel::RegisterZList("list", array("description"=>"All attributes to be displayed for a list of objects", "type"=>"attributes")); +MetaModel::RegisterZList("preview", array("description"=>"All attributes visible in preview mode", "type"=>"attributes")); + +MetaModel::RegisterZList("standard_search", array("description"=>"List of criteria for the standard search", "type"=>"filters")); +MetaModel::RegisterZList("advanced_search", array("description"=>"List of criteria for the advanced search", "type"=>"filters")); + + +?> diff --git a/core/oql/build.cmd b/core/oql/build.cmd new file mode 100644 index 000000000..2187cec66 --- /dev/null +++ b/core/oql/build.cmd @@ -0,0 +1,3 @@ +c:\itop\php-5.2.3\php.exe -q "C:\itop\PHP-5.2.3\PEAR\PHP\LexerGenerator\cli.php" oql-lexer.plex +c:\itop\php-5.2.3\php.exe -q "C:\itop\PHP-5.2.3\PEAR\PHP\ParserGenerator\cli.php" oql-parser.y +pause \ No newline at end of file diff --git a/core/oql/oql-lexer.php b/core/oql/oql-lexer.php new file mode 100644 index 000000000..dede6ca1f --- /dev/null +++ b/core/oql/oql-lexer.php @@ -0,0 +1,522 @@ +data = $data; + $this->count = 0; + $this->line = 1; + } + + + private $_yy_state = 1; + private $_yy_stack = array(); + + function yylex() + { + return $this->{'yylex' . $this->_yy_state}(); + } + + function yypushstate($state) + { + array_push($this->_yy_stack, $this->_yy_state); + $this->_yy_state = $state; + } + + function yypopstate() + { + $this->_yy_state = array_pop($this->_yy_stack); + } + + function yybegin($state) + { + $this->_yy_state = $state; + } + + + + + function yylex1() + { + if ($this->count >= strlen($this->data)) { + return false; // end of input + } + do { + $rules = array( + '/^[ \t\n]+/', + '/^SELECT/', + '/^AS/', + '/^WHERE/', + '/^JOIN/', + '/^ON/', + '/^\//', + '/^\\*/', + '/^\\+/', + '/^-/', + '/^AND/', + '/^OR/', + '/^,/', + '/^\\(/', + '/^\\)/', + '/^=/', + '/^!=/', + '/^>/', + '/^=/', + '/^<=/', + '/^LIKE/', + '/^NOT LIKE/', + '/^IN/', + '/^NOT IN/', + '/^INTERVAL/', + '/^IF/', + '/^ELT/', + '/^COALESCE/', + '/^CONCAT/', + '/^SUBSTR/', + '/^TRIM/', + '/^DATE/', + '/^DATE_FORMAT/', + '/^CURRENT_DATE/', + '/^NOW/', + '/^TIME/', + '/^TO_DAYS/', + '/^FROM_DAYS/', + '/^YEAR/', + '/^MONTH/', + '/^DAY/', + '/^DATE_ADD/', + '/^DATE_SUB/', + '/^ROUND/', + '/^FLOOR/', + '/^[0-9]+|0x[0-9a-fA-F]+/', + '/^\"([^\\\\\"]|\\\\\"|\\\\\\\\)*\"|'.chr(94).chr(39).'([^\\\\'.chr(39).']|\\\\'.chr(39).'|\\\\\\\\)*'.chr(39).'/', + '/^([_a-zA-Z][_a-zA-Z0-9]*|`[^`]+`)/', + '/^\\./', + ); + $match = false; + foreach ($rules as $index => $rule) { + if (preg_match($rule, substr($this->data, $this->count), $yymatches)) { + if ($match) { + if (strlen($yymatches[0]) > strlen($match[0][0])) { + $match = array($yymatches, $index); // matches, token + } + } else { + $match = array($yymatches, $index); + } + } + } + if (!$match) { + throw new Exception('Unexpected input at line' . $this->line . + ': ' . $this->data[$this->count]); + } + $this->token = $match[1]; + $this->value = $match[0][0]; + $yysubmatches = $match[0]; + array_shift($yysubmatches); + if (!$yysubmatches) { + $yysubmatches = array(); + } + $r = $this->{'yy_r1_' . $this->token}($yysubmatches); + if ($r === null) { + $this->count += strlen($this->value); + $this->line += substr_count($this->value, "\n"); + // accept this token + return true; + } elseif ($r === true) { + // we have changed state + // process this token in the new state + return $this->yylex(); + } elseif ($r === false) { + $this->count += strlen($this->value); + $this->line += substr_count($this->value, "\n"); + if ($this->count >= strlen($this->data)) { + return false; // end of input + } + // skip this token + continue; + } else { + $yy_yymore_patterns = array_slice($rules, $this->token, true); + // yymore is needed + do { + if (!isset($yy_yymore_patterns[$this->token])) { + throw new Exception('cannot do yymore for the last token'); + } + $match = false; + foreach ($yy_yymore_patterns[$this->token] as $index => $rule) { + if (preg_match('/' . $rule . '/', + substr($this->data, $this->count), $yymatches)) { + $yymatches = array_filter($yymatches, 'strlen'); // remove empty sub-patterns + if ($match) { + if (strlen($yymatches[0]) > strlen($match[0][0])) { + $match = array($yymatches, $index); // matches, token + } + } else { + $match = array($yymatches, $index); + } + } + } + if (!$match) { + throw new Exception('Unexpected input at line' . $this->line . + ': ' . $this->data[$this->count]); + } + $this->token = $match[1]; + $this->value = $match[0][0]; + $yysubmatches = $match[0]; + array_shift($yysubmatches); + if (!$yysubmatches) { + $yysubmatches = array(); + } + $this->line = substr_count($this->value, "\n"); + $r = $this->{'yy_r1_' . $this->token}(); + } while ($r !== null || !$r); + if ($r === true) { + // we have changed state + // process this token in the new state + return $this->yylex(); + } else { + // accept + $this->count += strlen($this->value); + $this->line += substr_count($this->value, "\n"); + return true; + } + } + } while (true); + + } // end function + + function yy_r1_0($yy_subpatterns) + { + + return false; + } + function yy_r1_1($yy_subpatterns) + { + + $this->token = OQLParser::SELECT; + } + function yy_r1_2($yy_subpatterns) + { + + $this->token = OQLParser::AS_ALIAS; + } + function yy_r1_3($yy_subpatterns) + { + + $this->token = OQLParser::WHERE; + } + function yy_r1_4($yy_subpatterns) + { + + $this->token = OQLParser::JOIN; + } + function yy_r1_5($yy_subpatterns) + { + + $this->token = OQLParser::ON; + } + function yy_r1_6($yy_subpatterns) + { + + $this->token = OQLParser::MATH_DIV; + } + function yy_r1_7($yy_subpatterns) + { + + $this->token = OQLParser::MATH_MULT; + } + function yy_r1_8($yy_subpatterns) + { + + $this->token = OQLParser::MATH_PLUS; + } + function yy_r1_9($yy_subpatterns) + { + + $this->token = OQLParser::MATH_MINUS; + } + function yy_r1_10($yy_subpatterns) + { + + $this->token = OQLParser::LOG_AND; + } + function yy_r1_11($yy_subpatterns) + { + + $this->token = OQLParser::LOG_OR; + } + function yy_r1_12($yy_subpatterns) + { + + $this->token = OQLParser::COMA; + } + function yy_r1_13($yy_subpatterns) + { + + $this->token = OQLParser::PAR_OPEN; + } + function yy_r1_14($yy_subpatterns) + { + + $this->token = OQLParser::PAR_CLOSE; + } + function yy_r1_15($yy_subpatterns) + { + + $this->token = OQLParser::EQ; + } + function yy_r1_16($yy_subpatterns) + { + + $this->token = OQLParser::NOT_EQ; + } + function yy_r1_17($yy_subpatterns) + { + + $this->token = OQLParser::GT; + } + function yy_r1_18($yy_subpatterns) + { + + $this->token = OQLParser::LT; + } + function yy_r1_19($yy_subpatterns) + { + + $this->token = OQLParser::GE; + } + function yy_r1_20($yy_subpatterns) + { + + $this->token = OQLParser::LE; + } + function yy_r1_21($yy_subpatterns) + { + + $this->token = OQLParser::LIKE; + } + function yy_r1_22($yy_subpatterns) + { + + $this->token = OQLParser::NOT_LIKE; + } + function yy_r1_23($yy_subpatterns) + { + + $this->token = OQLParser::IN; + } + function yy_r1_24($yy_subpatterns) + { + + $this->token = OQLParser::NOT_IN; + } + function yy_r1_25($yy_subpatterns) + { + + $this->token = OQLParser::INTERVAL; + } + function yy_r1_26($yy_subpatterns) + { + + $this->token = OQLParser::F_IF; + } + function yy_r1_27($yy_subpatterns) + { + + $this->token = OQLParser::F_ELT; + } + function yy_r1_28($yy_subpatterns) + { + + $this->token = OQLParser::F_COALESCE; + } + function yy_r1_29($yy_subpatterns) + { + + $this->token = OQLParser::F_CONCAT; + } + function yy_r1_30($yy_subpatterns) + { + + $this->token = OQLParser::F_SUBSTR; + } + function yy_r1_31($yy_subpatterns) + { + + $this->token = OQLParser::F_TRIM; + } + function yy_r1_32($yy_subpatterns) + { + + $this->token = OQLParser::F_DATE; + } + function yy_r1_33($yy_subpatterns) + { + + $this->token = OQLParser::F_DATE_FORMAT; + } + function yy_r1_34($yy_subpatterns) + { + + $this->token = OQLParser::F_CURRENT_DATE; + } + function yy_r1_35($yy_subpatterns) + { + + $this->token = OQLParser::F_NOW; + } + function yy_r1_36($yy_subpatterns) + { + + $this->token = OQLParser::F_TIME; + } + function yy_r1_37($yy_subpatterns) + { + + $this->token = OQLParser::F_TO_DAYS; + } + function yy_r1_38($yy_subpatterns) + { + + $this->token = OQLParser::F_FROM_DAYS; + } + function yy_r1_39($yy_subpatterns) + { + + $this->token = OQLParser::F_YEAR; + } + function yy_r1_40($yy_subpatterns) + { + + $this->token = OQLParser::F_MONTH; + } + function yy_r1_41($yy_subpatterns) + { + + $this->token = OQLParser::F_DAY; + } + function yy_r1_42($yy_subpatterns) + { + + $this->token = OQLParser::F_DATE_ADD; + } + function yy_r1_43($yy_subpatterns) + { + + $this->token = OQLParser::F_DATE_SUB; + } + function yy_r1_44($yy_subpatterns) + { + + $this->token = OQLParser::F_ROUND; + } + function yy_r1_45($yy_subpatterns) + { + + $this->token = OQLParser::F_FLOOR; + } + function yy_r1_46($yy_subpatterns) + { + + $this->token = OQLParser::NUMVAL; + } + function yy_r1_47($yy_subpatterns) + { + + $this->token = OQLParser::STRVAL; + } + function yy_r1_48($yy_subpatterns) + { + + $this->token = OQLParser::NAME; + } + function yy_r1_49($yy_subpatterns) + { + + $this->token = OQLParser::DOT; + } + + +} + +define('UNEXPECTED_INPUT_AT_LINE', 'Unexpected input at line'); + +class OQLLexerException extends OQLException +{ + public function __construct($sInput, $iLine, $iCol, $sUnexpected) + { + parent::__construct("Syntax error", $sInput, $iLine, $iCol, $sUnexpected); + } +} + +class OQLLexer extends OQLLexerRaw +{ + public function getTokenPos() + { + return max(0, $this->count - strlen($this->value)); + } + + function yylex() + { + try + { + return parent::yylex(); + } + catch (Exception $e) + { + $sMessage = $e->getMessage(); + if (substr($sMessage, 0, strlen(UNEXPECTED_INPUT_AT_LINE)) == UNEXPECTED_INPUT_AT_LINE) + { + $sLineAndChar = substr($sMessage, strlen(UNEXPECTED_INPUT_AT_LINE)); + if (preg_match('#^([0-9]+): (.+)$#', $sLineAndChar, $aMatches)) + { + $iLine = $aMatches[1]; + $sUnexpected = $aMatches[2]; + throw new OQLLexerException($this->data, $iLine, $this->count, $sUnexpected); + } + } + // Default: forward the exception + throw $e; + } + } +} +?> diff --git a/core/oql/oql-lexer.plex b/core/oql/oql-lexer.plex new file mode 100644 index 000000000..8196f540a --- /dev/null +++ b/core/oql/oql-lexer.plex @@ -0,0 +1,305 @@ +data = $data; + $this->count = 0; + $this->line = 1; + } + +/*!lex2php +%input $this->data +%counter $this->count +%token $this->token +%value $this->value +%line $this->line +%matchlongest 1 +whitespace = /[ \t\n]+/ +select = "SELECT" +as_alias = "AS" +where = "WHERE" +join = "JOIN" +on = "ON" +coma = "," +par_open = "(" +par_close = ")" +math_div = "/" +math_mult = "*" +math_plus = "+" +math_minus = "-" +log_and = "AND" +log_or = "OR" +eq = "=" +not_eq = "!=" +gt = ">" +lt = "<" +ge = ">=" +le = "<=" +like = "LIKE" +not_like = "NOT LIKE" +in = "IN" +not_in = "NOT IN" +interval = "INTERVAL" +f_if = "IF" +f_elt = "ELT" +f_coalesce = "COALESCE" +f_concat = "CONCAT" +f_substr = "SUBSTR" +f_trim = "TRIM" +f_date = "DATE" +f_date_format = "DATE_FORMAT" +f_current_date = "CURRENT_DATE" +f_now = "NOW" +f_time = "TIME" +f_to_days = "TO_DAYS" +f_from_days = "FROM_DAYS" +f_year = "YEAR" +f_month = "MONTH" +f_day = "DAY" +f_date_add = "DATE_ADD" +f_date_sub = "DATE_SUB" +f_round = "ROUND" +f_floor = "FLOOR" +numval = /[0-9]+|0x[0-9a-fA-F]+/ +strval = /"([^\\"]|\\"|\\\\)*"|'.chr(94).chr(39).'([^\\'.chr(39).']|\\'.chr(39).'|\\\\)*'.chr(39).'/ +name = /([_a-zA-Z][_a-zA-Z0-9]*|`[^`]+`)/ +dot = "." +*/ + +/*!lex2php +whitespace { + return false; +} +select { + $this->token = OQLParser::SELECT; +} +as_alias { + $this->token = OQLParser::AS_ALIAS; +} +where { + $this->token = OQLParser::WHERE; +} +join { + $this->token = OQLParser::JOIN; +} +on { + $this->token = OQLParser::ON; +} +math_div { + $this->token = OQLParser::MATH_DIV; +} +math_mult { + $this->token = OQLParser::MATH_MULT; +} +math_plus { + $this->token = OQLParser::MATH_PLUS; +} +math_minus { + $this->token = OQLParser::MATH_MINUS; +} +log_and { + $this->token = OQLParser::LOG_AND; +} +log_or { + $this->token = OQLParser::LOG_OR; +} +coma { + $this->token = OQLParser::COMA; +} +par_open { + $this->token = OQLParser::PAR_OPEN; +} +par_close { + $this->token = OQLParser::PAR_CLOSE; +} +eq { + $this->token = OQLParser::EQ; +} +not_eq { + $this->token = OQLParser::NOT_EQ; +} +gt { + $this->token = OQLParser::GT; +} +lt { + $this->token = OQLParser::LT; +} +ge { + $this->token = OQLParser::GE; +} +le { + $this->token = OQLParser::LE; +} +like { + $this->token = OQLParser::LIKE; +} +not_like { + $this->token = OQLParser::NOT_LIKE; +} +in { + $this->token = OQLParser::IN; +} +not_in { + $this->token = OQLParser::NOT_IN; +} +interval { + $this->token = OQLParser::INTERVAL; +} +f_if { + $this->token = OQLParser::F_IF; +} +f_elt { + $this->token = OQLParser::F_ELT; +} +f_coalesce { + $this->token = OQLParser::F_COALESCE; +} +f_concat { + $this->token = OQLParser::F_CONCAT; +} +f_substr { + $this->token = OQLParser::F_SUBSTR; +} +f_trim { + $this->token = OQLParser::F_TRIM; +} +f_date { + $this->token = OQLParser::F_DATE; +} +f_date_format { + $this->token = OQLParser::F_DATE_FORMAT; +} +f_current_date { + $this->token = OQLParser::F_CURRENT_DATE; +} +f_now { + $this->token = OQLParser::F_NOW; +} +f_time { + $this->token = OQLParser::F_TIME; +} +f_to_days { + $this->token = OQLParser::F_TO_DAYS; +} +f_from_days { + $this->token = OQLParser::F_FROM_DAYS; +} +f_year { + $this->token = OQLParser::F_YEAR; +} +f_month { + $this->token = OQLParser::F_MONTH; +} +f_day { + $this->token = OQLParser::F_DAY; +} +f_date_add { + $this->token = OQLParser::F_DATE_ADD; +} +f_date_sub { + $this->token = OQLParser::F_DATE_SUB; +} +f_round { + $this->token = OQLParser::F_ROUND; +} +f_floor { + $this->token = OQLParser::F_FLOOR; +} +numval { + $this->token = OQLParser::NUMVAL; +} +strval { + $this->token = OQLParser::STRVAL; +} +name { + $this->token = OQLParser::NAME; +} +dot { + $this->token = OQLParser::DOT; +} +*/ + +} + +define('UNEXPECTED_INPUT_AT_LINE', 'Unexpected input at line'); + +class OQLLexerException extends OQLException +{ + public function __construct($sInput, $iLine, $iCol, $sUnexpected) + { + parent::__construct("Syntax error", $sInput, $iLine, $iCol, $sUnexpected); + } +} + +class OQLLexer extends OQLLexerRaw +{ + public function getTokenPos() + { + return max(0, $this->count - strlen($this->value)); + } + + function yylex() + { + try + { + return parent::yylex(); + } + catch (Exception $e) + { + $sMessage = $e->getMessage(); + if (substr($sMessage, 0, strlen(UNEXPECTED_INPUT_AT_LINE)) == UNEXPECTED_INPUT_AT_LINE) + { + $sLineAndChar = substr($sMessage, strlen(UNEXPECTED_INPUT_AT_LINE)); + if (preg_match('#^([0-9]+): (.+)$#', $sLineAndChar, $aMatches)) + { + $iLine = $aMatches[1]; + $sUnexpected = $aMatches[2]; + throw new OQLLexerException($this->data, $iLine, $this->count, $sUnexpected); + } + } + // Default: forward the exception + throw $e; + } + } +} +?> diff --git a/core/oql/oql-parser.php b/core/oql/oql-parser.php new file mode 100644 index 000000000..c3995650c --- /dev/null +++ b/core/oql/oql-parser.php @@ -0,0 +1,1669 @@ +string = $s->string; + $this->metadata = $s->metadata; + } else { + $this->string = (string) $s; + if ($m instanceof OQLParser_yyToken) { + $this->metadata = $m->metadata; + } elseif (is_array($m)) { + $this->metadata = $m; + } + } + } + + function __toString() + { + return $this->_string; + } + + function offsetExists($offset) + { + return isset($this->metadata[$offset]); + } + + function offsetGet($offset) + { + return $this->metadata[$offset]; + } + + function offsetSet($offset, $value) + { + if ($offset === null) { + if (isset($value[0])) { + $x = ($value instanceof OQLParser_yyToken) ? + $value->metadata : $value; + $this->metadata = array_merge($this->metadata, $x); + return; + } + $offset = count($this->metadata); + } + if ($value === null) { + return; + } + if ($value instanceof OQLParser_yyToken) { + if ($value->metadata) { + $this->metadata[$offset] = $value->metadata; + } + } elseif ($value) { + $this->metadata[$offset] = $value; + } + } + + function offsetUnset($offset) + { + unset($this->metadata[$offset]); + } +} + +/** The following structure represents a single element of the + * parser's stack. Information stored includes: + * + * + The state number for the parser at this level of the stack. + * + * + The value of the token stored at this level of the stack. + * (In other words, the "major" token.) + * + * + The semantic value stored at this level of the stack. This is + * the information used by the action routines in the grammar. + * It is sometimes called the "minor" token. + */ +class OQLParser_yyStackEntry +{ + public $stateno; /* The state-number */ + public $major; /* The major token value. This is the code + ** number for the token at this stack level */ + public $minor; /* The user-supplied minor token value. This + ** is the value of the token */ +}; + +// code external to the class is included here + +// declare_class is output here +#line 24 "oql-parser.y" +class OQLParserRaw#line 102 "oql-parser.php" +{ +/* First off, code is included which follows the "include_class" declaration +** in the input file. */ + +/* Next is all token values, as class constants +*/ +/* +** These constants (all generated automatically by the parser generator) +** specify the various kinds of tokens (terminals) that the parser +** understands. +** +** Each symbol here is a terminal symbol in the grammar. +*/ + const SELECT = 1; + const AS_ALIAS = 2; + const WHERE = 3; + const JOIN = 4; + const ON = 5; + const EQ = 6; + const PAR_OPEN = 7; + const PAR_CLOSE = 8; + const COMA = 9; + const INTERVAL = 10; + const F_DAY = 11; + const F_MONTH = 12; + const F_YEAR = 13; + const DOT = 14; + const NAME = 15; + const NUMVAL = 16; + const STRVAL = 17; + const NOT_EQ = 18; + const LOG_AND = 19; + const LOG_OR = 20; + const MATH_DIV = 21; + const MATH_MULT = 22; + const MATH_PLUS = 23; + const MATH_MINUS = 24; + const GT = 25; + const LT = 26; + const GE = 27; + const LE = 28; + const LIKE = 29; + const NOT_LIKE = 30; + const IN = 31; + const NOT_IN = 32; + const F_IF = 33; + const F_ELT = 34; + const F_COALESCE = 35; + const F_CONCAT = 36; + const F_SUBSTR = 37; + const F_TRIM = 38; + const F_DATE = 39; + const F_DATE_FORMAT = 40; + const F_CURRENT_DATE = 41; + const F_NOW = 42; + const F_TIME = 43; + const F_TO_DAYS = 44; + const F_FROM_DAYS = 45; + const F_DATE_ADD = 46; + const F_DATE_SUB = 47; + const F_ROUND = 48; + const F_FLOOR = 49; + const YY_NO_ACTION = 205; + const YY_ACCEPT_ACTION = 204; + const YY_ERROR_ACTION = 203; + +/* Next are that tables used to determine what action to take based on the +** current state and lookahead token. These tables are used to implement +** functions that take a state number and lookahead value and return an +** action integer. +** +** Suppose the action integer is N. Then the action is determined as +** follows +** +** 0 <= N < self::YYNSTATE Shift N. That is, +** push the lookahead +** token onto the stack +** and goto state N. +** +** self::YYNSTATE <= N < self::YYNSTATE+self::YYNRULE Reduce by rule N-YYNSTATE. +** +** N == self::YYNSTATE+self::YYNRULE A syntax error has occurred. +** +** N == self::YYNSTATE+self::YYNRULE+1 The parser accepts its +** input. (and concludes parsing) +** +** N == self::YYNSTATE+self::YYNRULE+2 No such action. Denotes unused +** slots in the yy_action[] table. +** +** The action table is constructed as a single large static array $yy_action. +** Given state S and lookahead X, the action is computed as +** +** self::$yy_action[self::$yy_shift_ofst[S] + X ] +** +** If the index value self::$yy_shift_ofst[S]+X is out of range or if the value +** self::$yy_lookahead[self::$yy_shift_ofst[S]+X] is not equal to X or if +** self::$yy_shift_ofst[S] is equal to self::YY_SHIFT_USE_DFLT, it means that +** the action is not in the table and that self::$yy_default[S] should be used instead. +** +** The formula above is for computing the action when the lookahead is +** a terminal symbol. If the lookahead is a non-terminal (as occurs after +** a reduce action) then the static $yy_reduce_ofst array is used in place of +** the static $yy_shift_ofst array and self::YY_REDUCE_USE_DFLT is used in place of +** self::YY_SHIFT_USE_DFLT. +** +** The following are the tables generated in this section: +** +** self::$yy_action A single table containing all actions. +** self::$yy_lookahead A table containing the lookahead for each entry in +** yy_action. Used to detect hash collisions. +** self::$yy_shift_ofst For each state, the offset into self::$yy_action for +** shifting terminals. +** self::$yy_reduce_ofst For each state, the offset into self::$yy_action for +** shifting non-terminals after a reduce. +** self::$yy_default Default action for each state. +*/ + const YY_SZ_ACTTAB = 419; +static public $yy_action = array( + /* 0 */ 5, 57, 8, 4, 95, 96, 97, 6, 93, 76, + /* 10 */ 77, 89, 2, 53, 86, 50, 54, 25, 52, 55, + /* 20 */ 51, 46, 47, 49, 56, 70, 94, 110, 109, 108, + /* 30 */ 107, 111, 112, 115, 114, 113, 106, 71, 98, 99, + /* 40 */ 100, 104, 103, 26, 66, 38, 42, 9, 81, 5, + /* 50 */ 62, 44, 82, 95, 96, 97, 3, 93, 76, 77, + /* 60 */ 39, 102, 92, 75, 74, 73, 72, 75, 74, 73, + /* 70 */ 72, 10, 66, 41, 91, 94, 110, 109, 108, 107, + /* 80 */ 111, 112, 115, 114, 113, 106, 71, 98, 99, 100, + /* 90 */ 104, 103, 5, 63, 90, 22, 95, 96, 97, 61, + /* 100 */ 93, 76, 77, 65, 64, 60, 83, 11, 80, 79, + /* 110 */ 91, 33, 91, 22, 21, 18, 16, 12, 94, 110, + /* 120 */ 109, 108, 107, 111, 112, 115, 114, 113, 106, 71, + /* 130 */ 98, 99, 100, 104, 103, 204, 105, 87, 42, 23, + /* 140 */ 43, 24, 66, 88, 30, 28, 84, 45, 36, 6, + /* 150 */ 22, 20, 58, 15, 32, 37, 1, 76, 77, 101, + /* 160 */ 116, 75, 74, 73, 72, 41, 13, 66, 7, 160, + /* 170 */ 67, 93, 24, 42, 35, 78, 173, 173, 88, 34, + /* 180 */ 28, 84, 45, 40, 42, 173, 20, 173, 15, 69, + /* 190 */ 37, 173, 173, 173, 59, 173, 75, 74, 73, 72, + /* 200 */ 41, 42, 173, 173, 173, 173, 88, 34, 28, 84, + /* 210 */ 45, 41, 173, 173, 20, 173, 15, 173, 37, 173, + /* 220 */ 173, 173, 68, 173, 75, 74, 73, 72, 41, 173, + /* 230 */ 173, 173, 85, 42, 173, 173, 173, 173, 88, 30, + /* 240 */ 28, 84, 45, 173, 173, 173, 20, 173, 15, 173, + /* 250 */ 37, 173, 173, 173, 173, 173, 75, 74, 73, 72, + /* 260 */ 41, 42, 173, 173, 173, 173, 88, 17, 28, 84, + /* 270 */ 45, 173, 173, 173, 20, 173, 15, 42, 37, 173, + /* 280 */ 173, 48, 44, 173, 75, 74, 73, 72, 41, 173, + /* 290 */ 173, 173, 173, 42, 173, 173, 173, 173, 88, 27, + /* 300 */ 28, 84, 45, 173, 41, 173, 20, 173, 15, 173, + /* 310 */ 37, 173, 173, 173, 173, 173, 75, 74, 73, 72, + /* 320 */ 41, 42, 173, 173, 173, 173, 88, 173, 28, 84, + /* 330 */ 45, 173, 173, 173, 20, 173, 15, 173, 31, 173, + /* 340 */ 173, 173, 173, 173, 75, 74, 73, 72, 41, 173, + /* 350 */ 173, 173, 173, 42, 173, 173, 173, 173, 88, 173, + /* 360 */ 28, 84, 45, 173, 173, 173, 20, 173, 14, 173, + /* 370 */ 173, 173, 173, 173, 173, 173, 75, 74, 73, 72, + /* 380 */ 41, 42, 173, 173, 173, 173, 88, 173, 28, 84, + /* 390 */ 45, 42, 173, 173, 19, 173, 88, 173, 29, 84, + /* 400 */ 45, 173, 173, 173, 75, 74, 73, 72, 41, 173, + /* 410 */ 173, 173, 173, 173, 75, 74, 73, 72, 41, + ); + static public $yy_lookahead = array( + /* 0 */ 7, 6, 70, 10, 11, 12, 13, 73, 15, 16, + /* 10 */ 17, 8, 9, 18, 56, 83, 84, 54, 23, 24, + /* 20 */ 25, 26, 27, 28, 29, 30, 33, 34, 35, 36, + /* 30 */ 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + /* 40 */ 47, 48, 49, 1, 81, 54, 54, 68, 62, 7, + /* 50 */ 58, 59, 62, 11, 12, 13, 3, 15, 16, 17, + /* 60 */ 74, 82, 8, 77, 78, 79, 80, 77, 78, 79, + /* 70 */ 80, 7, 81, 81, 20, 33, 34, 35, 36, 37, + /* 80 */ 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, + /* 90 */ 48, 49, 7, 55, 66, 57, 11, 12, 13, 56, + /* 100 */ 15, 16, 17, 11, 12, 13, 8, 9, 31, 32, + /* 110 */ 20, 55, 20, 57, 2, 54, 6, 5, 33, 34, + /* 120 */ 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, + /* 130 */ 45, 46, 47, 48, 49, 51, 52, 53, 54, 2, + /* 140 */ 54, 4, 81, 59, 60, 61, 62, 63, 55, 73, + /* 150 */ 57, 67, 76, 69, 14, 71, 7, 16, 17, 21, + /* 160 */ 22, 77, 78, 79, 80, 81, 5, 81, 72, 14, + /* 170 */ 19, 15, 4, 54, 65, 81, 85, 85, 59, 60, + /* 180 */ 61, 62, 63, 64, 54, 85, 67, 85, 69, 59, + /* 190 */ 71, 85, 85, 85, 75, 85, 77, 78, 79, 80, + /* 200 */ 81, 54, 85, 85, 85, 85, 59, 60, 61, 62, + /* 210 */ 63, 81, 85, 85, 67, 85, 69, 85, 71, 85, + /* 220 */ 85, 85, 75, 85, 77, 78, 79, 80, 81, 85, + /* 230 */ 85, 85, 53, 54, 85, 85, 85, 85, 59, 60, + /* 240 */ 61, 62, 63, 85, 85, 85, 67, 85, 69, 85, + /* 250 */ 71, 85, 85, 85, 85, 85, 77, 78, 79, 80, + /* 260 */ 81, 54, 85, 85, 85, 85, 59, 60, 61, 62, + /* 270 */ 63, 85, 85, 85, 67, 85, 69, 54, 71, 85, + /* 280 */ 85, 58, 59, 85, 77, 78, 79, 80, 81, 85, + /* 290 */ 85, 85, 85, 54, 85, 85, 85, 85, 59, 60, + /* 300 */ 61, 62, 63, 85, 81, 85, 67, 85, 69, 85, + /* 310 */ 71, 85, 85, 85, 85, 85, 77, 78, 79, 80, + /* 320 */ 81, 54, 85, 85, 85, 85, 59, 85, 61, 62, + /* 330 */ 63, 85, 85, 85, 67, 85, 69, 85, 71, 85, + /* 340 */ 85, 85, 85, 85, 77, 78, 79, 80, 81, 85, + /* 350 */ 85, 85, 85, 54, 85, 85, 85, 85, 59, 85, + /* 360 */ 61, 62, 63, 85, 85, 85, 67, 85, 69, 85, + /* 370 */ 85, 85, 85, 85, 85, 85, 77, 78, 79, 80, + /* 380 */ 81, 54, 85, 85, 85, 85, 59, 85, 61, 62, + /* 390 */ 63, 54, 85, 85, 67, 85, 59, 85, 61, 62, + /* 400 */ 63, 85, 85, 85, 77, 78, 79, 80, 81, 85, + /* 410 */ 85, 85, 85, 85, 77, 78, 79, 80, 81, +); + const YY_SHIFT_USE_DFLT = -8; + const YY_SHIFT_MAX = 45; + static public $yy_shift_ofst = array( + /* 0 */ 42, -7, -7, 85, 85, 85, 85, 85, 85, 85, + /* 10 */ 141, 141, 156, 156, -5, -5, 156, 92, 137, 138, + /* 20 */ 138, 156, 168, 156, 156, 168, 156, 54, 77, 77, + /* 30 */ 90, 151, 156, 53, 90, 64, 53, 151, 112, 98, + /* 40 */ 3, 155, 140, 161, 110, 149, +); + const YY_REDUCE_USE_DFLT = -69; + const YY_REDUCE_MAX = 37; + static public $yy_reduce_ofst = array( + /* 0 */ 84, 119, 147, 179, 207, 239, 267, 299, 327, 337, + /* 10 */ -14, -10, 223, -8, -68, -68, 130, 76, 93, -21, + /* 20 */ -21, 86, 38, -37, -9, 56, 61, -66, 109, 109, + /* 30 */ -66, 96, 94, 43, -66, 28, -42, 96, +); + static public $yyExpectedTokens = array( + /* 0 */ array(1, 7, 11, 12, 13, 15, 16, 17, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, ), + /* 1 */ array(7, 10, 11, 12, 13, 15, 16, 17, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, ), + /* 2 */ array(7, 10, 11, 12, 13, 15, 16, 17, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, ), + /* 3 */ array(7, 11, 12, 13, 15, 16, 17, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, ), + /* 4 */ array(7, 11, 12, 13, 15, 16, 17, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, ), + /* 5 */ array(7, 11, 12, 13, 15, 16, 17, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, ), + /* 6 */ array(7, 11, 12, 13, 15, 16, 17, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, ), + /* 7 */ array(7, 11, 12, 13, 15, 16, 17, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, ), + /* 8 */ array(7, 11, 12, 13, 15, 16, 17, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, ), + /* 9 */ array(7, 11, 12, 13, 15, 16, 17, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, ), + /* 10 */ array(16, 17, ), + /* 11 */ array(16, 17, ), + /* 12 */ array(15, ), + /* 13 */ array(15, ), + /* 14 */ array(6, 18, 23, 24, 25, 26, 27, 28, 29, 30, ), + /* 15 */ array(6, 18, 23, 24, 25, 26, 27, 28, 29, 30, ), + /* 16 */ array(15, ), + /* 17 */ array(11, 12, 13, 20, ), + /* 18 */ array(2, 4, ), + /* 19 */ array(21, 22, ), + /* 20 */ array(21, 22, ), + /* 21 */ array(15, ), + /* 22 */ array(4, ), + /* 23 */ array(15, ), + /* 24 */ array(15, ), + /* 25 */ array(4, ), + /* 26 */ array(15, ), + /* 27 */ array(8, 20, ), + /* 28 */ array(31, 32, ), + /* 29 */ array(31, 32, ), + /* 30 */ array(20, ), + /* 31 */ array(19, ), + /* 32 */ array(15, ), + /* 33 */ array(3, ), + /* 34 */ array(20, ), + /* 35 */ array(7, ), + /* 36 */ array(3, ), + /* 37 */ array(19, ), + /* 38 */ array(2, 5, ), + /* 39 */ array(8, 9, ), + /* 40 */ array(8, 9, ), + /* 41 */ array(14, ), + /* 42 */ array(14, ), + /* 43 */ array(5, ), + /* 44 */ array(6, ), + /* 45 */ array(7, ), + /* 46 */ array(), + /* 47 */ array(), + /* 48 */ array(), + /* 49 */ array(), + /* 50 */ array(), + /* 51 */ array(), + /* 52 */ array(), + /* 53 */ array(), + /* 54 */ array(), + /* 55 */ array(), + /* 56 */ array(), + /* 57 */ array(), + /* 58 */ array(), + /* 59 */ array(), + /* 60 */ array(), + /* 61 */ array(), + /* 62 */ array(), + /* 63 */ array(), + /* 64 */ array(), + /* 65 */ array(), + /* 66 */ array(), + /* 67 */ array(), + /* 68 */ array(), + /* 69 */ array(), + /* 70 */ array(), + /* 71 */ array(), + /* 72 */ array(), + /* 73 */ array(), + /* 74 */ array(), + /* 75 */ array(), + /* 76 */ array(), + /* 77 */ array(), + /* 78 */ array(), + /* 79 */ array(), + /* 80 */ array(), + /* 81 */ array(), + /* 82 */ array(), + /* 83 */ array(), + /* 84 */ array(), + /* 85 */ array(), + /* 86 */ array(), + /* 87 */ array(), + /* 88 */ array(), + /* 89 */ array(), + /* 90 */ array(), + /* 91 */ array(), + /* 92 */ array(), + /* 93 */ array(), + /* 94 */ array(), + /* 95 */ array(), + /* 96 */ array(), + /* 97 */ array(), + /* 98 */ array(), + /* 99 */ array(), + /* 100 */ array(), + /* 101 */ array(), + /* 102 */ array(), + /* 103 */ array(), + /* 104 */ array(), + /* 105 */ array(), + /* 106 */ array(), + /* 107 */ array(), + /* 108 */ array(), + /* 109 */ array(), + /* 110 */ array(), + /* 111 */ array(), + /* 112 */ array(), + /* 113 */ array(), + /* 114 */ array(), + /* 115 */ array(), + /* 116 */ array(), +); + static public $yy_default = array( + /* 0 */ 203, 146, 203, 203, 203, 203, 203, 203, 203, 203, + /* 10 */ 203, 203, 203, 203, 140, 139, 203, 203, 125, 138, + /* 20 */ 137, 203, 124, 203, 203, 125, 203, 203, 135, 136, + /* 30 */ 129, 142, 203, 122, 149, 203, 122, 141, 203, 203, + /* 40 */ 203, 158, 203, 203, 203, 203, 176, 177, 127, 178, + /* 50 */ 165, 175, 173, 168, 166, 174, 179, 167, 150, 147, + /* 60 */ 153, 120, 126, 123, 152, 151, 160, 169, 148, 128, + /* 70 */ 180, 194, 157, 156, 155, 154, 162, 163, 159, 182, + /* 80 */ 181, 144, 145, 143, 130, 121, 119, 118, 131, 132, + /* 90 */ 134, 170, 133, 161, 183, 198, 197, 196, 195, 199, + /* 100 */ 200, 171, 164, 202, 201, 117, 193, 187, 186, 185, + /* 110 */ 184, 188, 189, 192, 191, 190, 172, +); +/* The next thing included is series of defines which control +** various aspects of the generated parser. +** self::YYNOCODE is a number which corresponds +** to no legal terminal or nonterminal number. This +** number is used to fill in empty slots of the hash +** table. +** self::YYFALLBACK If defined, this indicates that one or more tokens +** have fall-back values which should be used if the +** original value of the token will not parse. +** self::YYSTACKDEPTH is the maximum depth of the parser's stack. +** self::YYNSTATE the combined number of states. +** self::YYNRULE the number of rules in the grammar +** self::YYERRORSYMBOL is the code number of the error symbol. If not +** defined, then do no error processing. +*/ + const YYNOCODE = 86; + const YYSTACKDEPTH = 100; + const YYNSTATE = 117; + const YYNRULE = 86; + const YYERRORSYMBOL = 50; + const YYERRSYMDT = 'yy0'; + const YYFALLBACK = 0; + /** The next table maps tokens into fallback tokens. If a construct + * like the following: + * + * %fallback ID X Y Z. + * + * appears in the grammer, then ID becomes a fallback token for X, Y, + * and Z. Whenever one of the tokens X, Y, or Z is input to the parser + * but it does not parse, the type of the token is changed to ID and + * the parse is retried before an error is thrown. + */ + static public $yyFallback = array( + ); + /** + * Turn parser tracing on by giving a stream to which to write the trace + * and a prompt to preface each trace message. Tracing is turned off + * by making either argument NULL + * + * Inputs: + * + * - A stream resource to which trace output should be written. + * If NULL, then tracing is turned off. + * - A prefix string written at the beginning of every + * line of trace output. If NULL, then tracing is + * turned off. + * + * Outputs: + * + * - None. + * @param resource + * @param string + */ + static function Trace($TraceFILE, $zTracePrompt) + { + if (!$TraceFILE) { + $zTracePrompt = 0; + } elseif (!$zTracePrompt) { + $TraceFILE = 0; + } + self::$yyTraceFILE = $TraceFILE; + self::$yyTracePrompt = $zTracePrompt; + } + + /** + * Output debug information to output (php://output stream) + */ + static function PrintTrace() + { + self::$yyTraceFILE = fopen('php://output', 'w'); + self::$yyTracePrompt = ''; + } + + /** + * @var resource|0 + */ + static public $yyTraceFILE; + /** + * String to prepend to debug output + * @var string|0 + */ + static public $yyTracePrompt; + /** + * @var int + */ + public $yyidx; /* Index of top element in stack */ + /** + * @var int + */ + public $yyerrcnt; /* Shifts left before out of the error */ + /** + * @var array + */ + public $yystack = array(); /* The parser's stack */ + + /** + * For tracing shifts, the names of all terminals and nonterminals + * are required. The following table supplies these names + * @var array + */ + static public $yyTokenName = array( + '$', 'SELECT', 'AS_ALIAS', 'WHERE', + 'JOIN', 'ON', 'EQ', 'PAR_OPEN', + 'PAR_CLOSE', 'COMA', 'INTERVAL', 'F_DAY', + 'F_MONTH', 'F_YEAR', 'DOT', 'NAME', + 'NUMVAL', 'STRVAL', 'NOT_EQ', 'LOG_AND', + 'LOG_OR', 'MATH_DIV', 'MATH_MULT', 'MATH_PLUS', + 'MATH_MINUS', 'GT', 'LT', 'GE', + 'LE', 'LIKE', 'NOT_LIKE', 'IN', + 'NOT_IN', 'F_IF', 'F_ELT', 'F_COALESCE', + 'F_CONCAT', 'F_SUBSTR', 'F_TRIM', 'F_DATE', + 'F_DATE_FORMAT', 'F_CURRENT_DATE', 'F_NOW', 'F_TIME', + 'F_TO_DAYS', 'F_FROM_DAYS', 'F_DATE_ADD', 'F_DATE_SUB', + 'F_ROUND', 'F_FLOOR', 'error', 'result', + 'query', 'condition', 'class_name', 'join_statement', + 'where_statement', 'join_item', 'join_condition', 'field_id', + 'expression_prio4', 'expression_basic', 'scalar', 'func_name', + 'arg_list', 'list_operator', 'list', 'expression_prio1', + 'operator1', 'expression_prio2', 'operator2', 'expression_prio3', + 'operator3', 'operator4', 'scalar_list', 'argument', + 'interval_unit', 'num_scalar', 'str_scalar', 'num_value', + 'str_value', 'name', 'num_operator1', 'num_operator2', + 'str_operator', + ); + + /** + * For tracing reduce actions, the names of all rules are required. + * @var array + */ + static public $yyRuleName = array( + /* 0 */ "result ::= query", + /* 1 */ "result ::= condition", + /* 2 */ "query ::= SELECT class_name join_statement where_statement", + /* 3 */ "query ::= SELECT class_name AS_ALIAS class_name join_statement where_statement", + /* 4 */ "where_statement ::= WHERE condition", + /* 5 */ "where_statement ::=", + /* 6 */ "join_statement ::= join_item join_statement", + /* 7 */ "join_statement ::= join_item", + /* 8 */ "join_statement ::=", + /* 9 */ "join_item ::= JOIN class_name AS_ALIAS class_name ON join_condition", + /* 10 */ "join_item ::= JOIN class_name ON join_condition", + /* 11 */ "join_condition ::= field_id EQ field_id", + /* 12 */ "condition ::= expression_prio4", + /* 13 */ "expression_basic ::= scalar", + /* 14 */ "expression_basic ::= field_id", + /* 15 */ "expression_basic ::= func_name PAR_OPEN arg_list PAR_CLOSE", + /* 16 */ "expression_basic ::= PAR_OPEN expression_prio4 PAR_CLOSE", + /* 17 */ "expression_basic ::= expression_basic list_operator list", + /* 18 */ "expression_prio1 ::= expression_basic", + /* 19 */ "expression_prio1 ::= expression_prio1 operator1 expression_basic", + /* 20 */ "expression_prio2 ::= expression_prio1", + /* 21 */ "expression_prio2 ::= expression_prio2 operator2 expression_prio1", + /* 22 */ "expression_prio3 ::= expression_prio2", + /* 23 */ "expression_prio3 ::= expression_prio3 operator3 expression_prio2", + /* 24 */ "expression_prio4 ::= expression_prio3", + /* 25 */ "expression_prio4 ::= expression_prio4 operator4 expression_prio3", + /* 26 */ "list ::= PAR_OPEN scalar_list PAR_CLOSE", + /* 27 */ "scalar_list ::= scalar", + /* 28 */ "scalar_list ::= scalar_list COMA scalar", + /* 29 */ "arg_list ::=", + /* 30 */ "arg_list ::= argument", + /* 31 */ "arg_list ::= arg_list COMA argument", + /* 32 */ "argument ::= expression_prio4", + /* 33 */ "argument ::= INTERVAL expression_prio4 interval_unit", + /* 34 */ "interval_unit ::= F_DAY", + /* 35 */ "interval_unit ::= F_MONTH", + /* 36 */ "interval_unit ::= F_YEAR", + /* 37 */ "scalar ::= num_scalar", + /* 38 */ "scalar ::= str_scalar", + /* 39 */ "num_scalar ::= num_value", + /* 40 */ "str_scalar ::= str_value", + /* 41 */ "field_id ::= name", + /* 42 */ "field_id ::= class_name DOT name", + /* 43 */ "class_name ::= name", + /* 44 */ "name ::= NAME", + /* 45 */ "num_value ::= NUMVAL", + /* 46 */ "str_value ::= STRVAL", + /* 47 */ "operator1 ::= num_operator1", + /* 48 */ "operator2 ::= num_operator2", + /* 49 */ "operator2 ::= str_operator", + /* 50 */ "operator2 ::= EQ", + /* 51 */ "operator2 ::= NOT_EQ", + /* 52 */ "operator3 ::= LOG_AND", + /* 53 */ "operator4 ::= LOG_OR", + /* 54 */ "num_operator1 ::= MATH_DIV", + /* 55 */ "num_operator1 ::= MATH_MULT", + /* 56 */ "num_operator2 ::= MATH_PLUS", + /* 57 */ "num_operator2 ::= MATH_MINUS", + /* 58 */ "num_operator2 ::= GT", + /* 59 */ "num_operator2 ::= LT", + /* 60 */ "num_operator2 ::= GE", + /* 61 */ "num_operator2 ::= LE", + /* 62 */ "str_operator ::= LIKE", + /* 63 */ "str_operator ::= NOT_LIKE", + /* 64 */ "list_operator ::= IN", + /* 65 */ "list_operator ::= NOT_IN", + /* 66 */ "func_name ::= F_IF", + /* 67 */ "func_name ::= F_ELT", + /* 68 */ "func_name ::= F_COALESCE", + /* 69 */ "func_name ::= F_CONCAT", + /* 70 */ "func_name ::= F_SUBSTR", + /* 71 */ "func_name ::= F_TRIM", + /* 72 */ "func_name ::= F_DATE", + /* 73 */ "func_name ::= F_DATE_FORMAT", + /* 74 */ "func_name ::= F_CURRENT_DATE", + /* 75 */ "func_name ::= F_NOW", + /* 76 */ "func_name ::= F_TIME", + /* 77 */ "func_name ::= F_TO_DAYS", + /* 78 */ "func_name ::= F_FROM_DAYS", + /* 79 */ "func_name ::= F_YEAR", + /* 80 */ "func_name ::= F_MONTH", + /* 81 */ "func_name ::= F_DAY", + /* 82 */ "func_name ::= F_DATE_ADD", + /* 83 */ "func_name ::= F_DATE_SUB", + /* 84 */ "func_name ::= F_ROUND", + /* 85 */ "func_name ::= F_FLOOR", + ); + + /** + * This function returns the symbolic name associated with a token + * value. + * @param int + * @return string + */ + function tokenName($tokenType) + { + if ($tokenType === 0) { + return 'End of Input'; + } + if ($tokenType > 0 && $tokenType < count(self::$yyTokenName)) { + return self::$yyTokenName[$tokenType]; + } else { + return "Unknown"; + } + } + + /** + * The following function deletes the value associated with a + * symbol. The symbol can be either a terminal or nonterminal. + * @param int the symbol code + * @param mixed the symbol's value + */ + static function yy_destructor($yymajor, $yypminor) + { + switch ($yymajor) { + /* Here is inserted the actions which take place when a + ** terminal or non-terminal is destroyed. This can happen + ** when the symbol is popped from the stack during a + ** reduce or during error processing or when a parser is + ** being destroyed before it is finished parsing. + ** + ** Note: during a reduce, the only symbols destroyed are those + ** which appear on the RHS of the rule, but which are not used + ** inside the C code. + */ + default: break; /* If no destructor action specified: do nothing */ + } + } + + /** + * Pop the parser's stack once. + * + * If there is a destructor routine associated with the token which + * is popped from the stack, then call it. + * + * Return the major token number for the symbol popped. + * @param OQLParser_yyParser + * @return int + */ + function yy_pop_parser_stack() + { + if (!count($this->yystack)) { + return; + } + $yytos = array_pop($this->yystack); + if (self::$yyTraceFILE && $this->yyidx >= 0) { + fwrite(self::$yyTraceFILE, + self::$yyTracePrompt . 'Popping ' . self::$yyTokenName[$yytos->major] . + "\n"); + } + $yymajor = $yytos->major; + self::yy_destructor($yymajor, $yytos->minor); + $this->yyidx--; + return $yymajor; + } + + /** + * Deallocate and destroy a parser. Destructors are all called for + * all stack elements before shutting the parser down. + */ + function __destruct() + { + while ($this->yyidx >= 0) { + $this->yy_pop_parser_stack(); + } + if (is_resource(self::$yyTraceFILE)) { + fclose(self::$yyTraceFILE); + } + } + + /** + * Based on the current state and parser stack, get a list of all + * possible lookahead tokens + * @param int + * @return array + */ + function yy_get_expected_tokens($token) + { + $state = $this->yystack[$this->yyidx]->stateno; + $expected = self::$yyExpectedTokens[$state]; + if (in_array($token, self::$yyExpectedTokens[$state], true)) { + return $expected; + } + $stack = $this->yystack; + $yyidx = $this->yyidx; + do { + $yyact = $this->yy_find_shift_action($token); + if ($yyact >= self::YYNSTATE && $yyact < self::YYNSTATE + self::YYNRULE) { + // reduce action + $done = 0; + do { + if ($done++ == 100) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + // too much recursion prevents proper detection + // so give up + return array_unique($expected); + } + $yyruleno = $yyact - self::YYNSTATE; + $this->yyidx -= self::$yyRuleInfo[$yyruleno]['rhs']; + $nextstate = $this->yy_find_reduce_action( + $this->yystack[$this->yyidx]->stateno, + self::$yyRuleInfo[$yyruleno]['lhs']); + if (isset(self::$yyExpectedTokens[$nextstate])) { + $expected += self::$yyExpectedTokens[$nextstate]; + if (in_array($token, + self::$yyExpectedTokens[$nextstate], true)) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + return array_unique($expected); + } + } + if ($nextstate < self::YYNSTATE) { + // we need to shift a non-terminal + $this->yyidx++; + $x = new OQLParser_yyStackEntry; + $x->stateno = $nextstate; + $x->major = self::$yyRuleInfo[$yyruleno]['lhs']; + $this->yystack[$this->yyidx] = $x; + continue 2; + } elseif ($nextstate == self::YYNSTATE + self::YYNRULE + 1) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + // the last token was just ignored, we can't accept + // by ignoring input, this is in essence ignoring a + // syntax error! + return array_unique($expected); + } elseif ($nextstate === self::YY_NO_ACTION) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + // input accepted, but not shifted (I guess) + return $expected; + } else { + $yyact = $nextstate; + } + } while (true); + } + break; + } while (true); + return array_unique($expected); + } + + /** + * Based on the parser state and current parser stack, determine whether + * the lookahead token is possible. + * + * The parser will convert the token value to an error token if not. This + * catches some unusual edge cases where the parser would fail. + * @param int + * @return bool + */ + function yy_is_expected_token($token) + { + if ($token === 0) { + return true; // 0 is not part of this + } + $state = $this->yystack[$this->yyidx]->stateno; + if (in_array($token, self::$yyExpectedTokens[$state], true)) { + return true; + } + $stack = $this->yystack; + $yyidx = $this->yyidx; + do { + $yyact = $this->yy_find_shift_action($token); + if ($yyact >= self::YYNSTATE && $yyact < self::YYNSTATE + self::YYNRULE) { + // reduce action + $done = 0; + do { + if ($done++ == 100) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + // too much recursion prevents proper detection + // so give up + return true; + } + $yyruleno = $yyact - self::YYNSTATE; + $this->yyidx -= self::$yyRuleInfo[$yyruleno]['rhs']; + $nextstate = $this->yy_find_reduce_action( + $this->yystack[$this->yyidx]->stateno, + self::$yyRuleInfo[$yyruleno]['lhs']); + if (isset(self::$yyExpectedTokens[$nextstate]) && + in_array($token, self::$yyExpectedTokens[$nextstate], true)) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + return true; + } + if ($nextstate < self::YYNSTATE) { + // we need to shift a non-terminal + $this->yyidx++; + $x = new OQLParser_yyStackEntry; + $x->stateno = $nextstate; + $x->major = self::$yyRuleInfo[$yyruleno]['lhs']; + $this->yystack[$this->yyidx] = $x; + continue 2; + } elseif ($nextstate == self::YYNSTATE + self::YYNRULE + 1) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + if (!$token) { + // end of input: this is valid + return true; + } + // the last token was just ignored, we can't accept + // by ignoring input, this is in essence ignoring a + // syntax error! + return false; + } elseif ($nextstate === self::YY_NO_ACTION) { + $this->yyidx = $yyidx; + $this->yystack = $stack; + // input accepted, but not shifted (I guess) + return true; + } else { + $yyact = $nextstate; + } + } while (true); + } + break; + } while (true); + $this->yyidx = $yyidx; + $this->yystack = $stack; + return true; + } + + /** + * Find the appropriate action for a parser given the terminal + * look-ahead token iLookAhead. + * + * If the look-ahead token is YYNOCODE, then check to see if the action is + * independent of the look-ahead. If it is, return the action, otherwise + * return YY_NO_ACTION. + * @param int The look-ahead token + */ + function yy_find_shift_action($iLookAhead) + { + $stateno = $this->yystack[$this->yyidx]->stateno; + + /* if ($this->yyidx < 0) return self::YY_NO_ACTION; */ + if (!isset(self::$yy_shift_ofst[$stateno])) { + // no shift actions + return self::$yy_default[$stateno]; + } + $i = self::$yy_shift_ofst[$stateno]; + if ($i === self::YY_SHIFT_USE_DFLT) { + return self::$yy_default[$stateno]; + } + if ($iLookAhead == self::YYNOCODE) { + return self::YY_NO_ACTION; + } + $i += $iLookAhead; + if ($i < 0 || $i >= self::YY_SZ_ACTTAB || + self::$yy_lookahead[$i] != $iLookAhead) { + if (count(self::$yyFallback) && $iLookAhead < count(self::$yyFallback) + && ($iFallback = self::$yyFallback[$iLookAhead]) != 0) { + if (self::$yyTraceFILE) { + fwrite(self::$yyTraceFILE, self::$yyTracePrompt . "FALLBACK " . + self::$yyTokenName[$iLookAhead] . " => " . + self::$yyTokenName[$iFallback] . "\n"); + } + return $this->yy_find_shift_action($iFallback); + } + return self::$yy_default[$stateno]; + } else { + return self::$yy_action[$i]; + } + } + + /** + * Find the appropriate action for a parser given the non-terminal + * look-ahead token $iLookAhead. + * + * If the look-ahead token is self::YYNOCODE, then check to see if the action is + * independent of the look-ahead. If it is, return the action, otherwise + * return self::YY_NO_ACTION. + * @param int Current state number + * @param int The look-ahead token + */ + function yy_find_reduce_action($stateno, $iLookAhead) + { + /* $stateno = $this->yystack[$this->yyidx]->stateno; */ + + if (!isset(self::$yy_reduce_ofst[$stateno])) { + return self::$yy_default[$stateno]; + } + $i = self::$yy_reduce_ofst[$stateno]; + if ($i == self::YY_REDUCE_USE_DFLT) { + return self::$yy_default[$stateno]; + } + if ($iLookAhead == self::YYNOCODE) { + return self::YY_NO_ACTION; + } + $i += $iLookAhead; + if ($i < 0 || $i >= self::YY_SZ_ACTTAB || + self::$yy_lookahead[$i] != $iLookAhead) { + return self::$yy_default[$stateno]; + } else { + return self::$yy_action[$i]; + } + } + + /** + * Perform a shift action. + * @param int The new state to shift in + * @param int The major token to shift in + * @param mixed the minor token to shift in + */ + function yy_shift($yyNewState, $yyMajor, $yypMinor) + { + $this->yyidx++; + if ($this->yyidx >= self::YYSTACKDEPTH) { + $this->yyidx--; + if (self::$yyTraceFILE) { + fprintf(self::$yyTraceFILE, "%sStack Overflow!\n", self::$yyTracePrompt); + } + while ($this->yyidx >= 0) { + $this->yy_pop_parser_stack(); + } + /* Here code is inserted which will execute if the parser + ** stack ever overflows */ + return; + } + $yytos = new OQLParser_yyStackEntry; + $yytos->stateno = $yyNewState; + $yytos->major = $yyMajor; + $yytos->minor = $yypMinor; + array_push($this->yystack, $yytos); + if (self::$yyTraceFILE && $this->yyidx > 0) { + fprintf(self::$yyTraceFILE, "%sShift %d\n", self::$yyTracePrompt, + $yyNewState); + fprintf(self::$yyTraceFILE, "%sStack:", self::$yyTracePrompt); + for($i = 1; $i <= $this->yyidx; $i++) { + fprintf(self::$yyTraceFILE, " %s", + self::$yyTokenName[$this->yystack[$i]->major]); + } + fwrite(self::$yyTraceFILE,"\n"); + } + } + + /** + * The following table contains information about every rule that + * is used during the reduce. + * + *
    +     * array(
    +     *  array(
    +     *   int $lhs;         Symbol on the left-hand side of the rule
    +     *   int $nrhs;     Number of right-hand side symbols in the rule
    +     *  ),...
    +     * );
    +     * 
    + */ + static public $yyRuleInfo = array( + array( 'lhs' => 51, 'rhs' => 1 ), + array( 'lhs' => 51, 'rhs' => 1 ), + array( 'lhs' => 52, 'rhs' => 4 ), + array( 'lhs' => 52, 'rhs' => 6 ), + array( 'lhs' => 56, 'rhs' => 2 ), + array( 'lhs' => 56, 'rhs' => 0 ), + array( 'lhs' => 55, 'rhs' => 2 ), + array( 'lhs' => 55, 'rhs' => 1 ), + array( 'lhs' => 55, 'rhs' => 0 ), + array( 'lhs' => 57, 'rhs' => 6 ), + array( 'lhs' => 57, 'rhs' => 4 ), + array( 'lhs' => 58, 'rhs' => 3 ), + array( 'lhs' => 53, 'rhs' => 1 ), + array( 'lhs' => 61, 'rhs' => 1 ), + array( 'lhs' => 61, 'rhs' => 1 ), + array( 'lhs' => 61, 'rhs' => 4 ), + array( 'lhs' => 61, 'rhs' => 3 ), + array( 'lhs' => 61, 'rhs' => 3 ), + array( 'lhs' => 67, 'rhs' => 1 ), + array( 'lhs' => 67, 'rhs' => 3 ), + array( 'lhs' => 69, 'rhs' => 1 ), + array( 'lhs' => 69, 'rhs' => 3 ), + array( 'lhs' => 71, 'rhs' => 1 ), + array( 'lhs' => 71, 'rhs' => 3 ), + array( 'lhs' => 60, 'rhs' => 1 ), + array( 'lhs' => 60, 'rhs' => 3 ), + array( 'lhs' => 66, 'rhs' => 3 ), + array( 'lhs' => 74, 'rhs' => 1 ), + array( 'lhs' => 74, 'rhs' => 3 ), + array( 'lhs' => 64, 'rhs' => 0 ), + array( 'lhs' => 64, 'rhs' => 1 ), + array( 'lhs' => 64, 'rhs' => 3 ), + array( 'lhs' => 75, 'rhs' => 1 ), + array( 'lhs' => 75, 'rhs' => 3 ), + array( 'lhs' => 76, 'rhs' => 1 ), + array( 'lhs' => 76, 'rhs' => 1 ), + array( 'lhs' => 76, 'rhs' => 1 ), + array( 'lhs' => 62, 'rhs' => 1 ), + array( 'lhs' => 62, 'rhs' => 1 ), + array( 'lhs' => 77, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 59, 'rhs' => 1 ), + array( 'lhs' => 59, 'rhs' => 3 ), + array( 'lhs' => 54, 'rhs' => 1 ), + array( 'lhs' => 81, 'rhs' => 1 ), + array( 'lhs' => 79, 'rhs' => 1 ), + array( 'lhs' => 80, 'rhs' => 1 ), + array( 'lhs' => 68, 'rhs' => 1 ), + array( 'lhs' => 70, 'rhs' => 1 ), + array( 'lhs' => 70, 'rhs' => 1 ), + array( 'lhs' => 70, 'rhs' => 1 ), + array( 'lhs' => 70, 'rhs' => 1 ), + array( 'lhs' => 72, 'rhs' => 1 ), + array( 'lhs' => 73, 'rhs' => 1 ), + array( 'lhs' => 82, 'rhs' => 1 ), + array( 'lhs' => 82, 'rhs' => 1 ), + array( 'lhs' => 83, 'rhs' => 1 ), + array( 'lhs' => 83, 'rhs' => 1 ), + array( 'lhs' => 83, 'rhs' => 1 ), + array( 'lhs' => 83, 'rhs' => 1 ), + array( 'lhs' => 83, 'rhs' => 1 ), + array( 'lhs' => 83, 'rhs' => 1 ), + array( 'lhs' => 84, 'rhs' => 1 ), + array( 'lhs' => 84, 'rhs' => 1 ), + array( 'lhs' => 65, 'rhs' => 1 ), + array( 'lhs' => 65, 'rhs' => 1 ), + array( 'lhs' => 63, 'rhs' => 1 ), + array( 'lhs' => 63, 'rhs' => 1 ), + array( 'lhs' => 63, 'rhs' => 1 ), + array( 'lhs' => 63, 'rhs' => 1 ), + array( 'lhs' => 63, 'rhs' => 1 ), + array( 'lhs' => 63, 'rhs' => 1 ), + array( 'lhs' => 63, 'rhs' => 1 ), + array( 'lhs' => 63, 'rhs' => 1 ), + array( 'lhs' => 63, 'rhs' => 1 ), + array( 'lhs' => 63, 'rhs' => 1 ), + array( 'lhs' => 63, 'rhs' => 1 ), + array( 'lhs' => 63, 'rhs' => 1 ), + array( 'lhs' => 63, 'rhs' => 1 ), + array( 'lhs' => 63, 'rhs' => 1 ), + array( 'lhs' => 63, 'rhs' => 1 ), + array( 'lhs' => 63, 'rhs' => 1 ), + array( 'lhs' => 63, 'rhs' => 1 ), + array( 'lhs' => 63, 'rhs' => 1 ), + array( 'lhs' => 63, 'rhs' => 1 ), + array( 'lhs' => 63, 'rhs' => 1 ), + ); + + /** + * The following table contains a mapping of reduce action to method name + * that handles the reduction. + * + * If a rule is not set, it has no handler. + */ + static public $yyReduceMap = array( + 0 => 0, + 1 => 0, + 2 => 2, + 3 => 3, + 4 => 4, + 5 => 5, + 8 => 5, + 6 => 6, + 7 => 7, + 9 => 9, + 10 => 10, + 11 => 11, + 12 => 12, + 13 => 12, + 14 => 12, + 18 => 12, + 20 => 12, + 22 => 12, + 24 => 12, + 32 => 12, + 34 => 12, + 35 => 12, + 36 => 12, + 37 => 12, + 38 => 12, + 15 => 15, + 16 => 16, + 17 => 17, + 19 => 17, + 21 => 17, + 23 => 17, + 25 => 17, + 26 => 26, + 27 => 27, + 30 => 27, + 28 => 28, + 31 => 28, + 29 => 29, + 33 => 33, + 39 => 39, + 40 => 39, + 41 => 41, + 42 => 42, + 43 => 43, + 66 => 43, + 67 => 43, + 68 => 43, + 69 => 43, + 70 => 43, + 71 => 43, + 72 => 43, + 73 => 43, + 74 => 43, + 75 => 43, + 76 => 43, + 77 => 43, + 78 => 43, + 79 => 43, + 80 => 43, + 81 => 43, + 82 => 43, + 83 => 43, + 84 => 43, + 85 => 43, + 44 => 44, + 45 => 45, + 47 => 45, + 48 => 45, + 49 => 45, + 50 => 45, + 51 => 45, + 52 => 45, + 53 => 45, + 54 => 45, + 55 => 45, + 56 => 45, + 57 => 45, + 58 => 45, + 59 => 45, + 60 => 45, + 61 => 45, + 62 => 45, + 63 => 45, + 64 => 45, + 65 => 45, + 46 => 46, + ); + /* Beginning here are the reduction cases. A typical example + ** follows: + ** #line + ** function yy_r0($yymsp){ ... } // User supplied code + ** #line + */ +#line 29 "oql-parser.y" + function yy_r0(){ $this->my_result = $this->yystack[$this->yyidx + 0]->minor; } +#line 1230 "oql-parser.php" +#line 32 "oql-parser.y" + function yy_r2(){ + $this->_retvalue = new OqlQuery($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor); + } +#line 1235 "oql-parser.php" +#line 35 "oql-parser.y" + function yy_r3(){ + $this->_retvalue = new OqlQuery($this->yystack[$this->yyidx + -4]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor); + } +#line 1240 "oql-parser.php" +#line 39 "oql-parser.y" + function yy_r4(){ $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; } +#line 1243 "oql-parser.php" +#line 40 "oql-parser.y" + function yy_r5(){ $this->_retvalue = null; } +#line 1246 "oql-parser.php" +#line 42 "oql-parser.y" + function yy_r6(){ + // insert the join statement on top of the existing list + array_unshift($this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor); + // and return the updated array + $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; + } +#line 1254 "oql-parser.php" +#line 48 "oql-parser.y" + function yy_r7(){ + $this->_retvalue = Array($this->yystack[$this->yyidx + 0]->minor); + } +#line 1259 "oql-parser.php" +#line 54 "oql-parser.y" + function yy_r9(){ + // create an array with one single item + $this->_retvalue = new OqlJoinSpec($this->yystack[$this->yyidx + -4]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor); + } +#line 1265 "oql-parser.php" +#line 59 "oql-parser.y" + function yy_r10(){ + // create an array with one single item + $this->_retvalue = new OqlJoinSpec($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor); + } +#line 1271 "oql-parser.php" +#line 64 "oql-parser.y" + function yy_r11(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, '=', $this->yystack[$this->yyidx + 0]->minor); } +#line 1274 "oql-parser.php" +#line 66 "oql-parser.y" + function yy_r12(){ $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; } +#line 1277 "oql-parser.php" +#line 70 "oql-parser.y" + function yy_r15(){ $this->_retvalue = new FunctionOqlExpression($this->yystack[$this->yyidx + -3]->minor, $this->yystack[$this->yyidx + -1]->minor); } +#line 1280 "oql-parser.php" +#line 71 "oql-parser.y" + function yy_r16(){ $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor; } +#line 1283 "oql-parser.php" +#line 72 "oql-parser.y" + function yy_r17(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); } +#line 1286 "oql-parser.php" +#line 87 "oql-parser.y" + function yy_r26(){ + $this->_retvalue = new ListOqlExpression($this->yystack[$this->yyidx + -1]->minor); + } +#line 1291 "oql-parser.php" +#line 90 "oql-parser.y" + function yy_r27(){ + $this->_retvalue = array($this->yystack[$this->yyidx + 0]->minor); + } +#line 1296 "oql-parser.php" +#line 93 "oql-parser.y" + function yy_r28(){ + array_push($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor); + $this->_retvalue = $this->yystack[$this->yyidx + -2]->minor; + } +#line 1302 "oql-parser.php" +#line 98 "oql-parser.y" + function yy_r29(){ + $this->_retvalue = array(); + } +#line 1307 "oql-parser.php" +#line 109 "oql-parser.y" + function yy_r33(){ $this->_retvalue = new IntervalOqlExpression($this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); } +#line 1310 "oql-parser.php" +#line 118 "oql-parser.y" + function yy_r39(){ $this->_retvalue = new ScalarOqlExpression($this->yystack[$this->yyidx + 0]->minor); } +#line 1313 "oql-parser.php" +#line 121 "oql-parser.y" + function yy_r41(){ $this->_retvalue = new FieldOqlExpression($this->yystack[$this->yyidx + 0]->minor); } +#line 1316 "oql-parser.php" +#line 122 "oql-parser.y" + function yy_r42(){ $this->_retvalue = new FieldOqlExpression($this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -2]->minor); } +#line 1319 "oql-parser.php" +#line 123 "oql-parser.y" + function yy_r43(){ $this->_retvalue=$this->yystack[$this->yyidx + 0]->minor; } +#line 1322 "oql-parser.php" +#line 125 "oql-parser.y" + function yy_r44(){ + if ($this->yystack[$this->yyidx + 0]->minor[0] == '`') + { + $name = substr($this->yystack[$this->yyidx + 0]->minor, 1, strlen($this->yystack[$this->yyidx + 0]->minor) - 2); + } + else + { + $name = $this->yystack[$this->yyidx + 0]->minor; + } + $this->_retvalue = new OqlName($name, $this->m_iColPrev); + } +#line 1335 "oql-parser.php" +#line 137 "oql-parser.y" + function yy_r45(){$this->_retvalue=$this->yystack[$this->yyidx + 0]->minor; } +#line 1338 "oql-parser.php" +#line 138 "oql-parser.y" + function yy_r46(){$this->_retvalue=stripslashes(substr($this->yystack[$this->yyidx + 0]->minor, 1, strlen($this->yystack[$this->yyidx + 0]->minor) - 2)); } +#line 1341 "oql-parser.php" + + /** + * placeholder for the left hand side in a reduce operation. + * + * For a parser with a rule like this: + *
    +     * rule(A) ::= B. { A = 1; }
    +     * 
    + * + * The parser will translate to something like: + * + * + * function yy_r0(){$this->_retvalue = 1;} + * + */ + private $_retvalue; + + /** + * Perform a reduce action and the shift that must immediately + * follow the reduce. + * + * For a rule such as: + * + *
    +     * A ::= B blah C. { dosomething(); }
    +     * 
    + * + * This function will first call the action, if any, ("dosomething();" in our + * example), and then it will pop three states from the stack, + * one for each entry on the right-hand side of the expression + * (B, blah, and C in our example rule), and then push the result of the action + * back on to the stack with the resulting state reduced to (as described in the .out + * file) + * @param int Number of the rule by which to reduce + */ + function yy_reduce($yyruleno) + { + //int $yygoto; /* The next state */ + //int $yyact; /* The next action */ + //mixed $yygotominor; /* The LHS of the rule reduced */ + //OQLParser_yyStackEntry $yymsp; /* The top of the parser's stack */ + //int $yysize; /* Amount to pop the stack */ + $yymsp = $this->yystack[$this->yyidx]; + if (self::$yyTraceFILE && $yyruleno >= 0 + && $yyruleno < count(self::$yyRuleName)) { + fprintf(self::$yyTraceFILE, "%sReduce (%d) [%s].\n", + self::$yyTracePrompt, $yyruleno, + self::$yyRuleName[$yyruleno]); + } + + $this->_retvalue = $yy_lefthand_side = null; + if (array_key_exists($yyruleno, self::$yyReduceMap)) { + // call the action + $this->_retvalue = null; + $this->{'yy_r' . self::$yyReduceMap[$yyruleno]}(); + $yy_lefthand_side = $this->_retvalue; + } + $yygoto = self::$yyRuleInfo[$yyruleno]['lhs']; + $yysize = self::$yyRuleInfo[$yyruleno]['rhs']; + $this->yyidx -= $yysize; + for($i = $yysize; $i; $i--) { + // pop all of the right-hand side parameters + array_pop($this->yystack); + } + $yyact = $this->yy_find_reduce_action($this->yystack[$this->yyidx]->stateno, $yygoto); + if ($yyact < self::YYNSTATE) { + /* If we are not debugging and the reduce action popped at least + ** one element off the stack, then we can push the new element back + ** onto the stack here, and skip the stack overflow test in yy_shift(). + ** That gives a significant speed improvement. */ + if (!self::$yyTraceFILE && $yysize) { + $this->yyidx++; + $x = new OQLParser_yyStackEntry; + $x->stateno = $yyact; + $x->major = $yygoto; + $x->minor = $yy_lefthand_side; + $this->yystack[$this->yyidx] = $x; + } else { + $this->yy_shift($yyact, $yygoto, $yy_lefthand_side); + } + } elseif ($yyact == self::YYNSTATE + self::YYNRULE + 1) { + $this->yy_accept(); + } + } + + /** + * The following code executes when the parse fails + * + * Code from %parse_fail is inserted here + */ + function yy_parse_failed() + { + if (self::$yyTraceFILE) { + fprintf(self::$yyTraceFILE, "%sFail!\n", self::$yyTracePrompt); + } + while ($this->yyidx >= 0) { + $this->yy_pop_parser_stack(); + } + /* Here code is inserted which will be executed whenever the + ** parser fails */ + } + + /** + * The following code executes when a syntax error first occurs. + * + * %syntax_error code is inserted here + * @param int The major type of the error token + * @param mixed The minor type of the error token + */ + function yy_syntax_error($yymajor, $TOKEN) + { +#line 25 "oql-parser.y" + +throw new OQLParserException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN); +#line 1457 "oql-parser.php" + } + + /** + * The following is executed when the parser accepts + * + * %parse_accept code is inserted here + */ + function yy_accept() + { + if (self::$yyTraceFILE) { + fprintf(self::$yyTraceFILE, "%sAccept!\n", self::$yyTracePrompt); + } + while ($this->yyidx >= 0) { + $stack = $this->yy_pop_parser_stack(); + } + /* Here code is inserted which will be executed whenever the + ** parser accepts */ + } + + /** + * The main parser program. + * + * The first argument is the major token number. The second is + * the token value string as scanned from the input. + * + * @param int the token number + * @param mixed the token value + * @param mixed any extra arguments that should be passed to handlers + */ + function doParse($yymajor, $yytokenvalue) + { +// $yyact; /* The parser action. */ +// $yyendofinput; /* True if we are at the end of input */ + $yyerrorhit = 0; /* True if yymajor has invoked an error */ + + /* (re)initialize the parser, if necessary */ + if ($this->yyidx === null || $this->yyidx < 0) { + /* if ($yymajor == 0) return; // not sure why this was here... */ + $this->yyidx = 0; + $this->yyerrcnt = -1; + $x = new OQLParser_yyStackEntry; + $x->stateno = 0; + $x->major = 0; + $this->yystack = array(); + array_push($this->yystack, $x); + } + $yyendofinput = ($yymajor==0); + + if (self::$yyTraceFILE) { + fprintf(self::$yyTraceFILE, "%sInput %s\n", + self::$yyTracePrompt, self::$yyTokenName[$yymajor]); + } + + do { + $yyact = $this->yy_find_shift_action($yymajor); + if ($yymajor < self::YYERRORSYMBOL && + !$this->yy_is_expected_token($yymajor)) { + // force a syntax error + $yyact = self::YY_ERROR_ACTION; + } + if ($yyact < self::YYNSTATE) { + $this->yy_shift($yyact, $yymajor, $yytokenvalue); + $this->yyerrcnt--; + if ($yyendofinput && $this->yyidx >= 0) { + $yymajor = 0; + } else { + $yymajor = self::YYNOCODE; + } + } elseif ($yyact < self::YYNSTATE + self::YYNRULE) { + $this->yy_reduce($yyact - self::YYNSTATE); + } elseif ($yyact == self::YY_ERROR_ACTION) { + if (self::$yyTraceFILE) { + fprintf(self::$yyTraceFILE, "%sSyntax Error!\n", + self::$yyTracePrompt); + } + if (self::YYERRORSYMBOL) { + /* A syntax error has occurred. + ** The response to an error depends upon whether or not the + ** grammar defines an error token "ERROR". + ** + ** This is what we do if the grammar does define ERROR: + ** + ** * Call the %syntax_error function. + ** + ** * Begin popping the stack until we enter a state where + ** it is legal to shift the error symbol, then shift + ** the error symbol. + ** + ** * Set the error count to three. + ** + ** * Begin accepting and shifting new tokens. No new error + ** processing will occur until three tokens have been + ** shifted successfully. + ** + */ + if ($this->yyerrcnt < 0) { + $this->yy_syntax_error($yymajor, $yytokenvalue); + } + $yymx = $this->yystack[$this->yyidx]->major; + if ($yymx == self::YYERRORSYMBOL || $yyerrorhit ){ + if (self::$yyTraceFILE) { + fprintf(self::$yyTraceFILE, "%sDiscard input token %s\n", + self::$yyTracePrompt, self::$yyTokenName[$yymajor]); + } + $this->yy_destructor($yymajor, $yytokenvalue); + $yymajor = self::YYNOCODE; + } else { + while ($this->yyidx >= 0 && + $yymx != self::YYERRORSYMBOL && + ($yyact = $this->yy_find_shift_action(self::YYERRORSYMBOL)) >= self::YYNSTATE + ){ + $this->yy_pop_parser_stack(); + } + if ($this->yyidx < 0 || $yymajor==0) { + $this->yy_destructor($yymajor, $yytokenvalue); + $this->yy_parse_failed(); + $yymajor = self::YYNOCODE; + } elseif ($yymx != self::YYERRORSYMBOL) { + $u2 = 0; + $this->yy_shift($yyact, self::YYERRORSYMBOL, $u2); + } + } + $this->yyerrcnt = 3; + $yyerrorhit = 1; + } else { + /* YYERRORSYMBOL is not defined */ + /* This is what we do if the grammar does not define ERROR: + ** + ** * Report an error message, and throw away the input token. + ** + ** * If the input token is $, then fail the parse. + ** + ** As before, subsequent error messages are suppressed until + ** three input tokens have been successfully shifted. + */ + if ($this->yyerrcnt <= 0) { + $this->yy_syntax_error($yymajor, $yytokenvalue); + } + $this->yyerrcnt = 3; + $this->yy_destructor($yymajor, $yytokenvalue); + if ($yyendofinput) { + $this->yy_parse_failed(); + } + $yymajor = self::YYNOCODE; + } + } else { + $this->yy_accept(); + $yymajor = self::YYNOCODE; + } + } while ($yymajor != self::YYNOCODE && $this->yyidx >= 0); + } +}#line 186 "oql-parser.y" + + +class OQLParserException extends OQLException +{ + public function __construct($sInput, $iLine, $iCol, $sTokenName, $sTokenValue) + { + $sIssue = "Unexpected token $sTokenName"; + + parent::__construct($sIssue, $sInput, $iLine, $iCol, $sTokenValue); + } +} + +class OQLParser extends OQLParserRaw +{ + // dirty, but working for us (no other mean to get the final result :-( + protected $my_result; + + public function GetResult() + { + return $this->my_result; + } + + // More info on the source query and the current position while parsing it + // Data used when an exception is raised + protected $m_iLine; // still not used + protected $m_iCol; + protected $m_iColPrev; // this is the interesting one, because the parser will reduce on the next token + protected $m_sSourceQuery; + + public function __construct($sQuery) + { + $this->m_iLine = 0; + $this->m_iCol = 0; + $this->m_iColPrev = 0; + $this->m_sSourceQuery = $sQuery; + // no constructor - parent::__construct(); + } + + public function doParse($token, $value, $iCurrPosition = 0) + { + $this->m_iColPrev = $this->m_iCol; + $this->m_iCol = $iCurrPosition; + + return parent::DoParse($token, $value); + } + + public function doFinish() + { + $this->doParse(0, 0); + return $this->my_result; + } + + public function __destruct() + { + // Bug in the original destructor, causing an infinite loop ! + // This is a real issue when a fatal error occurs on the first token (the error could not be seen) + if (is_null($this->yyidx)) + { + $this->yyidx = -1; + } + parent::__destruct(); + } +} + +#line 1676 "oql-parser.php" diff --git a/core/oql/oql-parser.y b/core/oql/oql-parser.y new file mode 100644 index 000000000..551a82ada --- /dev/null +++ b/core/oql/oql-parser.y @@ -0,0 +1,250 @@ + +/* + +This is a LALR(1) grammar +(seek for Lemon grammar to get some documentation from the Net) +That doc was helpful: http://www.hwaci.com/sw/lemon/lemon.html + +To handle operators precedence we could have used the %left directive +(we took another option, because that one was discovered right after... +which option is the best for us?) +Example: +%left LOG_AND. +%left LOG_OR. +%nonassoc EQ NE GT GE LT LE. +%left PLUS MINUS. +%left TIMES DIVIDE MOD. +%right EXP NOT. + +TODO : solve the 2 remaining shift-reduce conflicts (JOIN) + +*/ + +%name OQLParser_ +%declare_class {class OQLParserRaw} +%syntax_error { +throw new OQLParserException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN); +} + +result ::= query(X). { $this->my_result = X; } +result ::= condition(X). { $this->my_result = X; } + +query(A) ::= SELECT class_name(X) join_statement(J) where_statement(W). { + A = new OqlQuery(X, X, W, J); +} +query(A) ::= SELECT class_name(X) AS_ALIAS class_name(Y) join_statement(J) where_statement(W). { + A = new OqlQuery(X, Y, W, J); +} + +where_statement(A) ::= WHERE condition(C). { A = C;} +where_statement(A) ::= . { A = null;} + +join_statement(A) ::= join_item(J) join_statement(S). { + // insert the join statement on top of the existing list + array_unshift(S, J); + // and return the updated array + A = S; +} +join_statement(A) ::= join_item(J). { + A = Array(J); +} +join_statement(A) ::= . { A = null;} + +join_item(A) ::= JOIN class_name(X) AS_ALIAS class_name(Y) ON join_condition(C). +{ + // create an array with one single item + A = new OqlJoinSpec(X, Y, C); +} +join_item(A) ::= JOIN class_name(X) ON join_condition(C). +{ + // create an array with one single item + A = new OqlJoinSpec(X, X, C); +} + +join_condition(A) ::= field_id(X) EQ field_id(Y). { A = new BinaryOqlExpression(X, '=', Y); } + +condition(A) ::= expression_prio4(X). { A = X; } + +expression_basic(A) ::= scalar(X). { A = X; } +expression_basic(A) ::= field_id(X). { A = X; } +expression_basic(A) ::= func_name(X) PAR_OPEN arg_list(Y) PAR_CLOSE. { A = new FunctionOqlExpression(X, Y); } +expression_basic(A) ::= PAR_OPEN expression_prio4(X) PAR_CLOSE. { A = X; } +expression_basic(A) ::= expression_basic(X) list_operator(Y) list(Z). { A = new BinaryOqlExpression(X, Y, Z); } + +expression_prio1(A) ::= expression_basic(X). { A = X; } +expression_prio1(A) ::= expression_prio1(X) operator1(Y) expression_basic(Z). { A = new BinaryOqlExpression(X, Y, Z); } + +expression_prio2(A) ::= expression_prio1(X). { A = X; } +expression_prio2(A) ::= expression_prio2(X) operator2(Y) expression_prio1(Z). { A = new BinaryOqlExpression(X, Y, Z); } + +expression_prio3(A) ::= expression_prio2(X). { A = X; } +expression_prio3(A) ::= expression_prio3(X) operator3(Y) expression_prio2(Z). { A = new BinaryOqlExpression(X, Y, Z); } + +expression_prio4(A) ::= expression_prio3(X). { A = X; } +expression_prio4(A) ::= expression_prio4(X) operator4(Y) expression_prio3(Z). { A = new BinaryOqlExpression(X, Y, Z); } + + +list(A) ::= PAR_OPEN scalar_list(X) PAR_CLOSE. { + A = new ListOqlExpression(X); +} +scalar_list(A) ::= scalar(X). { + A = array(X); +} +scalar_list(A) ::= scalar_list(L) COMA scalar(X). { + array_push(L, X); + A = L; +} + +arg_list(A) ::= . { + A = array(); +} +arg_list(A) ::= argument(X). { + A = array(X); +} +arg_list(A) ::= arg_list(L) COMA argument(X). { + array_push(L, X); + A = L; +} +argument(A) ::= expression_prio4(X). { A = X; } +argument(A) ::= INTERVAL expression_prio4(X) interval_unit(Y). { A = new IntervalOqlExpression(X, Y); } + +interval_unit(A) ::= F_DAY(X). { A = X; } +interval_unit(A) ::= F_MONTH(X). { A = X; } +interval_unit(A) ::= F_YEAR(X). { A = X; } + +scalar(A) ::= num_scalar(X). { A = X; } +scalar(A) ::= str_scalar(X). { A = X; } + +num_scalar(A) ::= num_value(X). { A = new ScalarOqlExpression(X); } +str_scalar(A) ::= str_value(X). { A = new ScalarOqlExpression(X); } + +field_id(A) ::= name(X). { A = new FieldOqlExpression(X); } +field_id(A) ::= class_name(X) DOT name(Y). { A = new FieldOqlExpression(Y, X); } +class_name(A) ::= name(X). { A=X; } + +name(A) ::= NAME(X). { + if (X[0] == '`') + { + $name = substr(X, 1, strlen(X) - 2); + } + else + { + $name = X; + } + A = new OqlName($name, $this->m_iColPrev); +} + +num_value(A) ::= NUMVAL(X). {A=X;} +str_value(A) ::= STRVAL(X). {A=stripslashes(substr(X, 1, strlen(X) - 2));} + + +operator1(A) ::= num_operator1(X). {A=X;} +operator2(A) ::= num_operator2(X). {A=X;} +operator2(A) ::= str_operator(X). {A=X;} +operator2(A) ::= EQ(X). {A=X;} +operator2(A) ::= NOT_EQ(X). {A=X;} +operator3(A) ::= LOG_AND(X). {A=X;} +operator4(A) ::= LOG_OR(X). {A=X;} + +num_operator1(A) ::= MATH_DIV(X). {A=X;} +num_operator1(A) ::= MATH_MULT(X). {A=X;} +num_operator2(A) ::= MATH_PLUS(X). {A=X;} +num_operator2(A) ::= MATH_MINUS(X). {A=X;} +num_operator2(A) ::= GT(X). {A=X;} +num_operator2(A) ::= LT(X). {A=X;} +num_operator2(A) ::= GE(X). {A=X;} +num_operator2(A) ::= LE(X). {A=X;} + +str_operator(A) ::= LIKE(X). {A=X;} +str_operator(A) ::= NOT_LIKE(X). {A=X;} + +list_operator(A) ::= IN(X). {A=X;} +list_operator(A) ::= NOT_IN(X). {A=X;} + +func_name(A) ::= F_IF(X). { A=X; } +func_name(A) ::= F_ELT(X). { A=X; } +func_name(A) ::= F_COALESCE(X). { A=X; } +func_name(A) ::= F_CONCAT(X). { A=X; } +func_name(A) ::= F_SUBSTR(X). { A=X; } +func_name(A) ::= F_TRIM(X). { A=X; } +func_name(A) ::= F_DATE(X). { A=X; } +func_name(A) ::= F_DATE_FORMAT(X). { A=X; } +func_name(A) ::= F_CURRENT_DATE(X). { A=X; } +func_name(A) ::= F_NOW(X). { A=X; } +func_name(A) ::= F_TIME(X). { A=X; } +func_name(A) ::= F_TO_DAYS(X). { A=X; } +func_name(A) ::= F_FROM_DAYS(X). { A=X; } +func_name(A) ::= F_YEAR(X). { A=X; } +func_name(A) ::= F_MONTH(X). { A=X; } +func_name(A) ::= F_DAY(X). { A=X; } +func_name(A) ::= F_DATE_ADD(X). { A=X; } +func_name(A) ::= F_DATE_SUB(X). { A=X; } +func_name(A) ::= F_ROUND(X). { A=X; } +func_name(A) ::= F_FLOOR(X). { A=X; } + + +%code { + +class OQLParserException extends OQLException +{ + public function __construct($sInput, $iLine, $iCol, $sTokenName, $sTokenValue) + { + $sIssue = "Unexpected token $sTokenName"; + + parent::__construct($sIssue, $sInput, $iLine, $iCol, $sTokenValue); + } +} + +class OQLParser extends OQLParserRaw +{ + // dirty, but working for us (no other mean to get the final result :-( + protected $my_result; + + public function GetResult() + { + return $this->my_result; + } + + // More info on the source query and the current position while parsing it + // Data used when an exception is raised + protected $m_iLine; // still not used + protected $m_iCol; + protected $m_iColPrev; // this is the interesting one, because the parser will reduce on the next token + protected $m_sSourceQuery; + + public function __construct($sQuery) + { + $this->m_iLine = 0; + $this->m_iCol = 0; + $this->m_iColPrev = 0; + $this->m_sSourceQuery = $sQuery; + // no constructor - parent::__construct(); + } + + public function doParse($token, $value, $iCurrPosition = 0) + { + $this->m_iColPrev = $this->m_iCol; + $this->m_iCol = $iCurrPosition; + + return parent::DoParse($token, $value); + } + + public function doFinish() + { + $this->doParse(0, 0); + return $this->my_result; + } + + public function __destruct() + { + // Bug in the original destructor, causing an infinite loop ! + // This is a real issue when a fatal error occurs on the first token (the error could not be seen) + if (is_null($this->yyidx)) + { + $this->yyidx = -1; + } + parent::__destruct(); + } +} + +} diff --git a/core/oql/oqlexception.class.inc.php b/core/oql/oqlexception.class.inc.php new file mode 100644 index 000000000..512a3077f --- /dev/null +++ b/core/oql/oqlexception.class.inc.php @@ -0,0 +1,78 @@ +m_MyIssue = $sIssue; + $this->m_sInput = $sInput; + $this->m_iLine = $iLine; + $this->m_iCol = $iCol; + $this->m_sUnexpected = $sUnexpected; + $this->m_aExpecting = $aExpecting; + + if (is_null($this->m_aExpecting) || (count($this->m_aExpecting) == 0)) + { + $sMessage = "$sIssue - found '{$this->m_sUnexpected}' at $iCol in '$sInput'"; + } + else + { + $sExpectations = '{'.implode(', ', $this->m_aExpecting).'}'; + $sSuggest = self::FindClosestString($this->m_sUnexpected, $this->m_aExpecting); + $sMessage = "$sIssue - found '{$this->m_sUnexpected}' at $iCol in '$sInput', expecting $sExpectations, I would suggest to use '$sSuggest'"; + } + + // make sure everything is assigned properly + parent::__construct($sMessage, 0); + } + + public function getHtmlDesc($sHighlightHtmlBegin = '', $sHighlightHtmlEnd = '') + { + $sRet = htmlentities($this->m_MyIssue.", found '".$this->m_sUnexpected."' in: "); + $sRet .= htmlentities(substr($this->m_sInput, 0, $this->m_iCol)); + $sRet .= $sHighlightHtmlBegin.htmlentities(substr($this->m_sInput, $this->m_iCol, strlen($this->m_sUnexpected))).$sHighlightHtmlEnd; + $sRet .= htmlentities(substr($this->m_sInput, $this->m_iCol + strlen($this->m_sUnexpected))); + + if (!is_null($this->m_aExpecting) && (count($this->m_aExpecting) > 0)) + { + $sExpectations = '{'.implode(', ', $this->m_aExpecting).'}'; + $sRet .= ", expecting ".htmlentities($sExpectations); + $sSuggest = self::FindClosestString($this->m_sUnexpected, $this->m_aExpecting); + if (strlen($sSuggest) > 0) + { + $sRet .= ", I would suggest to use '$sHighlightHtmlBegin".htmlentities($sSuggest)."$sHighlightHtmlEnd'"; + } + } + + return $sRet; + } + + static protected function FindClosestString($sInput, $aDictionary) + { + // no shortest distance found, yet + $fShortest = -1; + $sRet = ''; + + // loop through words to find the closest + foreach ($aDictionary as $sSuggestion) + { + // calculate the distance between the input string and the suggested one + $fDist = levenshtein($sInput, $sSuggestion); + if ($fDist == 0) + { + // Exact match + return $sSuggestion; + } + + if ($fShortest < 0 || ($fDist < 4 && $fDist <= $fShortest)) + { + // set the closest match, and shortest distance + $sRet = $sSuggestion; + $fShortest = $fDist; + } + } + return $sRet; + } +} + +?> diff --git a/core/oql/oqlinterpreter.class.inc.php b/core/oql/oqlinterpreter.class.inc.php new file mode 100644 index 000000000..a464413da --- /dev/null +++ b/core/oql/oqlinterpreter.class.inc.php @@ -0,0 +1,59 @@ +GetPos(), $oName->GetValue(), $aExpecting); + } +} + +class OqlInterpreterException extends OQLException +{ +} + + +class OqlInterpreter +{ + public $m_sQuery; + + public function __construct($sQuery) + { + $this->m_sQuery = $sQuery; + } + + protected function Parse() + { + $oLexer = new OQLLexer($this->m_sQuery); + $oParser = new OQLParser($this->m_sQuery); + + while($oLexer->yylex()) + { + $oParser->doParse($oLexer->token, $oLexer->value, $oLexer->getTokenPos()); + } + $res = $oParser->doFinish(); + return $res; + } + + public function ParseQuery() + { + $oRes = $this->Parse(); + if (!$oRes instanceof OqlQuery) + { + throw new OqlException('Expecting an OQL query', $this->m_sQuery, 0, 0, get_class($oRes), array('OqlQuery')); + } + return $oRes; + } + + public function ParseExpression() + { + $oRes = $this->Parse(); + if (!$oRes instanceof Expression) + { + throw new OqlException('Expecting an OQL expression', $this->m_sQuery, 0, 0, get_class($oRes), array('Expression')); + } + return $oRes; + } +} + +?> diff --git a/core/oql/oqlquery.class.inc.php b/core/oql/oqlquery.class.inc.php new file mode 100644 index 000000000..ad71950b3 --- /dev/null +++ b/core/oql/oqlquery.class.inc.php @@ -0,0 +1,168 @@ +m_iPos = $iPos; + $this->m_sValue = $sValue; + } + + public function GetValue() + { + return $this->m_sValue; + } + + public function GetPos() + { + return $this->m_iPos; + } + + public function __toString() + { + return $this->m_sValue; + } +} + +class OqlJoinSpec +{ + protected $m_oClass; + protected $m_oClassAlias; + protected $m_oLeftField; + protected $m_oRightField; + + protected $m_oNextJoinspec; + + public function __construct($oClass, $oClassAlias, BinaryExpression $oExpression) + { + $this->m_oClass = $oClass; + $this->m_oClassAlias = $oClassAlias; + $this->m_oLeftField = $oExpression->GetLeftExpr(); + $this->m_oRightField = $oExpression->GetRightExpr(); + } + + public function GetClass() + { + return $this->m_oClass->GetValue(); + } + public function GetClassAlias() + { + return $this->m_oClassAlias->GetValue(); + } + + public function GetClassDetails() + { + return $this->m_oClass; + } + public function GetClassAliasDetails() + { + return $this->m_oClassAlias; + } + + public function GetLeftField() + { + return $this->m_oLeftField; + } + public function GetRightField() + { + return $this->m_oRightField; + } +} + +class BinaryOqlExpression extends BinaryExpression +{ +} + +class ScalarOqlExpression extends ScalarExpression +{ +} + +class FieldOqlExpression extends FieldExpression +{ + protected $m_oParent; + protected $m_oName; + + public function __construct($oName, $oParent = null) + { + if (is_null($oParent)) + { + $oParent = new OqlName('', 0); + } + $this->m_oParent = $oParent; + $this->m_oName = $oName; + + parent::__construct($oName->GetValue(), $oParent->GetValue()); + } + + public function GetParentDetails() + { + return $this->m_oParent; + } + + public function GetNameDetails() + { + return $this->m_oName; + } +} + +class ListOqlExpression extends ListExpression +{ +} + +class FunctionOqlExpression extends FunctionExpression +{ +} + +class IntervalOqlExpression extends IntervalExpression +{ +} +class OqlQuery +{ + protected $m_oClass; + protected $m_oClassAlias; + protected $m_aJoins; // array of OqlJoinSpec + protected $m_oCondition; // condition tree (expressions) + + public function __construct($oClass, $oClassAlias = '', $oCondition = null, $aJoins = null) + { + $this->m_oClass = $oClass; + $this->m_oClassAlias = $oClassAlias; + $this->m_aJoins = $aJoins; + $this->m_oCondition = $oCondition; + } + + public function GetClass() + { + return $this->m_oClass->GetValue(); + } + public function GetClassAlias() + { + return $this->m_oClassAlias->GetValue(); + } + + public function GetClassDetails() + { + return $this->m_oClass; + } + public function GetClassAliasDetails() + { + return $this->m_oClassAlias; + } + + public function GetJoins() + { + return $this->m_aJoins; + } + public function GetCondition() + { + return $this->m_oCondition; + } +} + +?> diff --git a/core/sqlquery.class.inc.php b/core/sqlquery.class.inc.php new file mode 100644 index 000000000..8a55d33c8 --- /dev/null +++ b/core/sqlquery.class.inc.php @@ -0,0 +1,413 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ + +require_once('cmdbsource.class.inc.php'); + + +class SQLExpression extends BinaryExpression +{ +} +class ScalarSQLExpression extends ScalarExpression +{ +} +class TrueSQLExpression extends TrueExpression +{ +} +class FieldSQLExpression extends FieldExpression +{ +} + + + +class SQLQuery +{ + private $m_sTable = ''; + private $m_sTableAlias = ''; + private $m_aFields = array(); + private $m_oConditionExpr = null; + private $m_aFullTextNeedles = array(); + private $m_bToDelete = true; // The current table must be listed for deletion ? + private $m_aValues = array(); // Values to set in case of an update query + private $m_aJoinSelects = array(); + + public function __construct($sTable, $sTableAlias, $aFields, $oConditionExpr, $aFullTextNeedles, $bToDelete = true, $aValues = array()) + { + if (!CMDBSource::IsTable($sTable)) + { + throw new CoreException("Unknown table '$sTable'"); + } + // $aFields must be an array of "alias"=>"expr" + // $oConditionExpr must be a condition tree + // $aValues is an array of "alias"=>value + + $this->m_sTable = $sTable; + $this->m_sTableAlias = $sTableAlias; + $this->m_aFields = $aFields; + $this->m_oConditionExpr = $oConditionExpr; + if (is_null($oConditionExpr)) + { + $this->m_oConditionExpr = new TrueExpression; + } + else if (!$oConditionExpr instanceof Expression) + { + throw new CoreException('Invalid type for condition, expecting an Expression', array('class' => get_class($oConditionExpr))); + } + $this->m_aFullTextNeedles = $aFullTextNeedles; + $this->m_bToDelete = $bToDelete; + $this->m_aValues = $aValues; + } + + public function DisplayHtml() + { + if (count($this->m_aFields) == 0) $sFields = ""; + else + { + $aFieldDesc = array(); + foreach ($this->m_aFields as $sAlias => $oExpression) + { + $aFieldDesc[] = $oExpression->Render()." as $sAlias"; + } + $sFields = " => ".implode(', ', $aFieldDesc); + } + echo "$this->m_sTable$sFields
    \n"; + // #@# todo - display html of an expression tree + //$this->m_oConditionExpr->DisplayHtml() + if (count($this->m_aFullTextNeedles) > 0) + { + echo "Full text criteria...
    \n"; + echo "
      \n"; + foreach ($this->m_aFullTextNeedles as $sFTNeedle) + { + echo "
    • $sFTNeedle
    • \n"; + } + echo "
    "; + } + if (count($this->m_aJoinSelects) > 0) + { + echo "Joined to...
    \n"; + echo "
      \n"; + foreach ($this->m_aJoinSelects as $aJoinInfo) + { + $sJoinType = $aJoinInfo["jointype"]; + $oSQLQuery = $aJoinInfo["select"]; + $sLeftField = $aJoinInfo["leftfield"]; + $sRightField = $aJoinInfo["rightfield"]; + $sRightTableAlias = $aJoinInfo["righttablealias"]; + + echo "
    • Join '$sJoinType', $sLeftField, $sRightTableAlias.$sRightField".$oSQLQuery->DisplayHtml()."
    • \n"; + } + echo "
    "; + } + $aFrom = array(); + $aFields = array(); + $oCondition = null; + $aDelTables = array(); + $aSetValues = array(); + $this->privRender($aFrom, $aFields, $oCondition, $aDelTables, $aSetValues); + echo "From ...
    \n"; + echo "
    \n";
    +		print_r($aFrom);
    +		echo "
    "; + } + + public function SetCondition($oConditionExpr) + { + $this->m_oConditionExpr = $oConditionExpr; + } + + public function AddCondition($oConditionExpr) + { + $this->m_oConditionExpr->LogAnd($oConditionExpr); + } + + private function AddJoin($sJoinType, $oSQLQuery, $sLeftField, $sRightField, $sRightTableAlias = '') + { + assert((get_class($oSQLQuery) == __CLASS__) || is_subclass_of($oSQLQuery, __CLASS__)); + if (!CMDBSource::IsField($this->m_sTable, $sLeftField)) + { + throw new CoreException("Unknown field '$sLeftField' in table '".$this->m_sTable); + } + if (empty($sRightTableAlias)) + { + $sRightTableAlias = $oSQLQuery->m_sTableAlias; + } +// #@# Could not be verified here because the namespace is unknown - do we need to check it there? +// +// if (!CMDBSource::IsField($sRightTable, $sRightField)) +// { +// throw new CoreException("Unknown field '$sRightField' in table '".$sRightTable."'"); +// } + + $this->m_aJoinSelects[] = array( + "jointype" => $sJoinType, + "select" => $oSQLQuery, + "leftfield" => $sLeftField, + "rightfield" => $sRightField, + "righttablealias" => $sRightTableAlias + ); + } + public function AddInnerJoin($oSQLQuery, $sLeftField, $sRightField, $sRigthtTable = '') + { + $this->AddJoin("inner", $oSQLQuery, $sLeftField, $sRightField, $sRigthtTable); + } + public function AddLeftJoin($oSQLQuery, $sLeftField, $sRightField) + { + return $this->AddJoin("left", $oSQLQuery, $sLeftField, $sRightField); + } + + // Interface, build the SQL query + public function RenderDelete() + { + // The goal will be to complete the list as we build the Joins + $aFrom = array(); + $aFields = array(); + $oCondition = null; + $aDelTables = array(); + $aSetValues = array(); + $this->privRender($aFrom, $aFields, $oCondition, $aDelTables, $aSetValues); + + // Target: DELETE myAlias1, myAlias2 FROM t1 as myAlias1, t2 as myAlias2, t3 as topreserve WHERE ... + + $sDelete = self::ClauseDelete($aDelTables); + $sFrom = self::ClauseFrom($aFrom); + // #@# safety net to redo ? + /* + if ($this->m_oConditionExpr->IsAny()) + -- if (count($aConditions) == 0) -- + { + throw new CoreException("Building a request wich will delete every object of a given table -looks suspicious- please use truncate instead..."); + } + */ + $sWhere = self::ClauseWhere($oCondition); + return "DELETE $sDelete FROM $sFrom WHERE $sWhere"; + } + + // Interface, build the SQL query + public function RenderUpdate() + { + // The goal will be to complete the list as we build the Joins + $aFrom = array(); + $aFields = array(); + $oCondition = null; + $aDelTables = array(); + $aSetValues = array(); + $this->privRender($aFrom, $aFields, $oCondition, $aDelTables, $aSetValues); + + $sFrom = self::ClauseFrom($aFrom); + $sValues = self::ClauseValues($aSetValues); + $sWhere = self::ClauseWhere($oCondition); + return "UPDATE $sFrom SET $sValues WHERE $sWhere"; + } + + // Interface, build the SQL query + public function RenderSelect($aOrderBy = array()) + { + // The goal will be to complete the lists as we build the Joins + $aFrom = array(); + $aFields = array(); + $oCondition = null; + $aDelTables = array(); + $aSetValues = array(); + $this->privRender($aFrom, $aFields, $oCondition, $aDelTables, $aSetValues); + + $sSelect = self::ClauseSelect($aFields); + $sFrom = self::ClauseFrom($aFrom); + $sWhere = self::ClauseWhere($oCondition); + $sOrderBy = self::ClauseOrderBy($aOrderBy); + if (!empty($sOrderBy)) + { + $sOrderBy = "ORDER BY $sOrderBy"; + } + return "SELECT DISTINCT $sSelect FROM $sFrom WHERE $sWhere $sOrderBy"; + } + + private static function ClauseSelect($aFields) + { + $aSelect = array(); + foreach ($aFields as $sFieldAlias => $sSQLExpr) + { + $aSelect[] = "$sSQLExpr AS $sFieldAlias"; + } + $sSelect = implode(', ', $aSelect); + return $sSelect; + } + + private static function ClauseDelete($aDelTableAliases) + { + $aDelTables = array(); + foreach ($aDelTableAliases as $sTableAlias) + { + $aDelTables[] = "$sTableAlias"; + } + $sDelTables = implode(', ', $aDelTables); + return $sDelTables; + } + + private static function ClauseFrom($aFrom) + { + $sFrom = ""; + foreach ($aFrom as $sTableAlias => $aJoinInfo) + { + switch ($aJoinInfo["jointype"]) + { + case "first": + $sFrom .= "`".$aJoinInfo["tablename"]."` AS `$sTableAlias`"; + $sFrom .= " ".self::ClauseFrom($aJoinInfo["subfrom"]); + break; + case "inner": + $sFrom .= " INNER JOIN (`".$aJoinInfo["tablename"]."` AS `$sTableAlias`"; + $sFrom .= " ".self::ClauseFrom($aJoinInfo["subfrom"]); + $sFrom .= ") ON ".$aJoinInfo["joincondition"]; + break; + case "left": + $sFrom .= " LEFT JOIN (`".$aJoinInfo["tablename"]."` AS `$sTableAlias`"; + $sFrom .= " ".self::ClauseFrom($aJoinInfo["subfrom"]); + $sFrom .= ") ON ".$aJoinInfo["joincondition"]; + break; + default: + throw new CoreException("Unknown jointype: '".$aJoinInfo["jointype"]."'"); + } + } + return $sFrom; + } + + private static function ClauseValues($aValues) + { + $aSetValues = array(); + foreach ($aValues as $sFieldSpec => $value) + { + $aSetValues[] = "$sFieldSpec = ".CMDBSource::Quote($value); + } + $sSetValues = implode(', ', $aSetValues); + return $sSetValues; + } + + private static function ClauseWhere($oConditionExpr) + { + return $oConditionExpr->Render(); + } + + private static function ClauseOrderBy($aOrderBy) + { + $aOrderBySpec = array(); + foreach($aOrderBy as $sFieldAlias => $bAscending) + { + $aOrderBySpec[] = '`'.$sFieldAlias.'`'.($bAscending ? " ASC" : " DESC"); + } + $sOrderBy = implode(", ", $aOrderBySpec); + return $sOrderBy; + } + + // Purpose: prepare the query data, once for all + private function privRender(&$aFrom, &$aFields, &$oCondition, &$aDelTables, &$aSetValues) + { + $sTableAlias = $this->privRenderSingleTable($aFrom, $aFields, $aDelTables, $aSetValues); + + // Add the full text search condition, based on each and every requested field + // + // To be updated with a real full text search based on the mySQL settings + // (then it might move somewhere else !) + // + $oCondition = $this->m_oConditionExpr; + if ((count($aFields) > 0) && (count($this->m_aFullTextNeedles) > 0)) + { + $aFieldExp = array(); + foreach ($aFields as $sField) + { + // This is TEMPORARY (that's why it is weird, actually) + // Full text match will be done as an expression in the filter condition + + // $sField is already a string `table`.`column` + // Let's make an expression out of it (again !) + $aFieldExp[] = Expression::FromOQL($sField); + } + $oFullTextExpr = new CharConcatExpression($aFieldExp); + // The cast is necessary because the CONCAT result in a binary string: + // if any of the field is a binary string => case sensitive comparison + // + foreach($this->m_aFullTextNeedles as $sFTNeedle) + { + $oNewCond = new BinaryExpression($oFullTextExpr, 'LIKE', new ScalarExpression("%$sFTNeedle%")); + $oCondition = $oCondition->LogAnd($oNewCond); + } + } + + return $sTableAlias; + } + + private function privRenderSingleTable(&$aFrom, &$aFields, &$aDelTables, &$aSetValues, $sJoinType = 'first', $sCallerAlias = '', $sLeftField = '', $sRightField = '', $sRightTableAlias = '') + { + $aActualTableFields = CMDBSource::GetTableFieldsList($this->m_sTable); + + $aTranslationTable[$this->m_sTable]['*'] = $this->m_sTableAlias; + + // Handle the various kinds of join (or first table in the list) + // + if (empty($sRightTableAlias)) + { + $sRightTableAlias = $this->m_sTableAlias; + } + $sJoinCond = "`$sCallerAlias`.`$sLeftField` = `$sRightTableAlias`.`$sRightField`"; + switch ($sJoinType) + { + case "first": + $aFrom[$this->m_sTableAlias] = array("jointype"=>"first", "tablename"=>$this->m_sTable, "joincondition"=>""); + break; + case "inner": + case "left": + // table or tablealias ??? + $aFrom[$this->m_sTableAlias] = array("jointype"=>$sJoinType, "tablename"=>$this->m_sTable, "joincondition"=>"$sJoinCond"); + break; + } + + // Given the alias, modify the fields and conditions + // before adding them into the current lists + // + foreach($this->m_aFields as $sAlias => $oExpression) + { + $sTable = $oExpression->GetParent(); + $sColumn = $oExpression->GetName(); + $aFields["`$sAlias`"] = $oExpression->Render(); + } + if ($this->m_bToDelete) + { + $aDelTables[] = "`{$this->m_sTableAlias}`"; + } + foreach($this->m_aValues as $sFieldName=>$value) + { + $aSetValues["`{$this->m_sTableAlias}`.`$sFieldName`"] = $value; // quoted further! + } + + // loop on joins, to complete the list of tables/fields/conditions + // + $aTempFrom = array(); // temporary subset of 'from' specs, to be grouped in the final query + foreach ($this->m_aJoinSelects as $aJoinData) + { + $sJoinType = $aJoinData["jointype"]; + $oRightSelect = $aJoinData["select"]; + $sLeftField = $aJoinData["leftfield"]; + $sRightField = $aJoinData["rightfield"]; + $sRightTableAlias = $aJoinData["righttablealias"]; + + $sJoinTableAlias = $oRightSelect->privRenderSingleTable($aTempFrom, $aFields, $aDelTables, $aSetValues, $sJoinType, $this->m_sTableAlias, $sLeftField, $sRightField, $sRightTableAlias); + } + $aFrom[$this->m_sTableAlias]['subfrom'] = $aTempFrom; + + return $this->m_sTableAlias; + } + +} + +?> diff --git a/core/stimulus.class.inc.php b/core/stimulus.class.inc.php new file mode 100644 index 000000000..d2326f6fc --- /dev/null +++ b/core/stimulus.class.inc.php @@ -0,0 +1,59 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ + +class ObjectStimulus +{ + private $m_aParams = array(); + + public function __construct($aParams) + { + $this->m_aParams = $aParams; + $this->ConsistencyCheck(); + } + + public function Get($sParamName) {return $this->m_aParams[$sParamName];} + + // Note: I could factorize this code with the parameter management made for the AttributeDef class + // to be overloaded + static protected function ListExpectedParams() + { + return array("label", "description"); + } + + private function ConsistencyCheck() + { + + // Check that any mandatory param has been specified + // + $aExpectedParams = $this->ListExpectedParams(); + foreach($aExpectedParams as $sParamName) + { + if (!array_key_exists($sParamName, $this->m_aParams)) + { + $aBacktrace = debug_backtrace(); + $sTargetClass = $aBacktrace[2]["class"]; + $sCodeInfo = $aBacktrace[1]["file"]." - ".$aBacktrace[1]["line"]; + throw new CoreException("missing parameter '$sParamName' in ".get_class($this)." declaration for class $sTargetClass ($sCodeInfo)"); + } + } + } +} + + + +class StimulusUserAction extends ObjectStimulus +{ +} + +?> diff --git a/core/test.class.inc.php b/core/test.class.inc.php new file mode 100644 index 000000000..f20ebec07 --- /dev/null +++ b/core/test.class.inc.php @@ -0,0 +1,504 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ +class ExceptionFromError extends Exception +{ + public function getTraceAsHtml() + { + $aBackTrace = $this->getTrace(); + return MyHelpers::get_callstack_html(0, $this->getTrace()); + // return "
    \n".$this->getTraceAsString()."
    \n"; + } +} + + +/** + * Test handler API and basic helpers + * + * @package iTopORM + * @author Romain Quetiez + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ +abstract class TestHandler +{ + protected $m_aSuccesses; + protected $m_aWarnings; + protected $m_aErrors; + protected $m_sOutput; + + public function __construct() + { + $this->m_aSuccesses = array(); + $this->m_aWarnings = array(); + $this->m_aErrors = array(); + } + + abstract static public function GetName(); + abstract static public function GetDescription(); + + protected function DoPrepare() {return true;} + abstract protected function DoExecute(); + protected function DoCleanup() {return true;} + + protected function ReportSuccess($sMessage, $sSubtestId = '') + { + $this->m_aSuccesses[] = $sMessage; + } + + protected function ReportWarning($sMessage, $sSubtestId = '') + { + $this->m_aWarnings[] = $sMessage; + } + + protected function ReportError($sMessage, $sSubtestId = '') + { + $this->m_aErrors[] = $sMessage; + } + + public function GetResults() + { + return $this->m_aSuccesses; + } + + public function GetWarnings() + { + return $this->m_aWarnings; + } + + public function GetErrors() + { + return $this->m_aErrors; + } + + public function GetOutput() + { + return $this->m_sOutput; + } + + public function error_handler($errno, $errstr, $errfile, $errline) + { + // Note: return false to call the default handler (stop the program if an error) + + switch ($errno) + { + case E_USER_ERROR: + $this->ReportError($errstr); + //throw new ExceptionFromError("Fatal error in line $errline of file $errfile: $errstr"); + break; + case E_USER_WARNING: + $this->ReportWarning($errstr); + break; + case E_USER_NOTICE: + $this->ReportWarning($errstr); + break; + default: + throw new ExceptionFromError("Fatal warning in line $errline of file $errfile: $errstr"); + $this->ReportWarning("Unknown error type: [$errno] $errstr"); + echo "Unknown error type: [$errno] $errstr
    \n"; + break; + } + return true; // do not call the default handler + } + + public function Execute() + { + ob_start(); + set_error_handler(array($this, 'error_handler')); + try + { + $this->DoPrepare(); + $this->DoExecute(); + } + catch (ExceptionFromError $e) + { + $this->ReportError($e->getMessage().' - '.$e->getTraceAsHtml()); + } + catch (CoreException $e) + { + //$this->ReportError($e->getMessage()); + //$this->ReportError($e->__tostring()); + $this->ReportError($e->getMessage().' - '.$e->getTraceAsHtml()); + } + catch (Exception $e) + { + //$this->ReportError($e->getMessage()); + //$this->ReportError($e->__tostring()); + $this->ReportError('class '.get_class($e).' --- '.$e->getMessage().' - '.$e->getTraceAsString()); + } + restore_error_handler(); + $this->m_sOutput = ob_get_clean(); + return (count($this->GetErrors()) == 0); + } +} + + + + +/** + * Test to execute a piece of code (checks if an error occurs) + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +abstract class TestFunction extends TestHandler +{ + // simply overload DoExecute (temporary) +} + + +/** + * Test to execute a piece of code (checks if an error occurs) + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +abstract class TestWebServices extends TestHandler +{ + // simply overload DoExecute (temporary) + + static protected function DoPostRequestAuth($sRelativeUrl, $aData, $sLogin = 'admin', $sPassword = '', $sOptionnalHeaders = null) + { + $aDataAndAuth = $aData; + $aDataAndAuth['operation'] = 'login'; + $aDataAndAuth['auth_user'] = $sLogin; + $aDataAndAuth['auth_pwd'] = $sPassword; + + $sHost = $GLOBALS['_SERVER']['HTTP_HOST']; + $sUrl = "http://$sHost/$sRelativeUrl"; + + return self::DoPostRequest($sUrl, $aDataAndAuth, $sOptionnalHeaders); + } + + // Source: http://netevil.org/blog/2006/nov/http-post-from-php-without-curl + // originaly named after do_post_request + // Partially adapted to our coding conventions + static protected function DoPostRequest($sUrl, $aData, $sOptionnalHeaders = null) + { + // $sOptionnalHeaders is a string containing additional HTTP headers that you would like to send in your request. + + $sData = http_build_query($aData); + + $aParams = array('http' => array( + 'method' => 'POST', + 'content' => $sData, + 'header'=> "Content-type: application/x-www-form-urlencoded\r\nContent-Length: ".strlen($sData)."\r\n", + )); + if ($sOptionnalHeaders !== null) + { + $aParams['http']['header'] .= $sOptionnalHeaders; + } + $ctx = stream_context_create($aParams); + + $fp = @fopen($sUrl, 'rb', false, $ctx); + if (!$fp) + { + throw new Exception("Problem with $sUrl, $php_errormsg"); + } + $response = @stream_get_contents($fp); + if ($response === false) + { + throw new Exception("Problem reading data from $sUrl, $php_errormsg"); + } + return $response; + } +} + +/** + * Test to check that a function outputs some values depending on its input + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +abstract class TestFunctionInOut extends TestFunction +{ + abstract static public function GetCallSpec(); // parameters to call_user_func + abstract static public function GetInOut(); // array of input => output + + protected function DoExecute() + { + $aTests = $this->GetInOut(); + if (is_array($aTests)) + { + foreach ($aTests as $iTestId => $aTest) + { + $ret = call_user_func_array($this->GetCallSpec(), $aTest['args']); + if ($ret != $aTest['output']) + { + // Note: to be improved to cope with non string parameters + $this->ReportError("Found '$ret' while expecting '".$aTest['output']."'", $iTestId); + } + else + { + $this->ReportSuccess("Found the expected output '$ret'", $iTestId); + } + } + } + else + { + $ret = call_user_func($this->GetCallSpec()); + $this->ReportSuccess('Finished successfully'); + } + } +} + + +/** + * Test to check an URL (Searches for Error/Warning/Etc keywords) + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +abstract class TestUrl extends TestHandler +{ + abstract static public function GetUrl(); + abstract static public function GetErrorKeywords(); + abstract static public function GetWarningKeywords(); + + protected function DoExecute() + { + return true; + } +} + + +/** + * Test to check a user management module + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +abstract class TestUserRights extends TestHandler +{ + protected function DoExecute() + { + return true; + } +} + + +/** + * Test to execute a scenario on a given DB + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +abstract class TestScenarioOnDB extends TestHandler +{ + abstract static public function GetDBHost(); + abstract static public function GetDBUser(); + abstract static public function GetDBPwd(); + abstract static public function GetDBName(); + + protected function DoPrepare() + { + $sDBHost = $this->GetDBHost(); + $sDBUser = $this->GetDBUser(); + $sDBPwd = $this->GetDBPwd(); + $sDBName = $this->GetDBName(); + + CMDBSource::Init($sDBHost, $sDBUser, $sDBPwd); + if (CMDBSource::IsDB($sDBName)) + { + CMDBSource::DropDB($sDBName); + } + CMDBSource::CreateDB($sDBName); + } + + protected function DoCleanup() + { + // CMDBSource::DropDB($this->GetDBName()); + } +} + + +/** + * Test to use a business model on a given DB + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +abstract class TestBizModel extends TestHandler +{ +// abstract static public function GetDBSubName(); +// abstract static public function GetBusinessModelFile(); + abstract static public function GetConfigFile(); + + protected function DoPrepare() + { + MetaModel::Startup($this->GetConfigFile(), true); // allow missing DB + MetaModel::CheckDefinitions(); + + // something here to create records... but that's another story + } + + protected function ResetDB() + { + if (MetaModel::DBExists()) + { + MetaModel::DBDrop(); + } + MetaModel::DBCreate(); + } + + static protected function show_list($oObjectSet) + { + $oObjectSet->Rewind(); + $aData = array(); + while ($oItem = $oObjectSet->Fetch()) + { + $aValues = array(); + foreach(MetaModel::GetAttributesList(get_class($oItem)) as $sAttCode) + { + $aValues[$sAttCode] = $oItem->GetAsHTML($sAttCode); + } + //echo $oItem->GetKey()." => ".implode(", ", $aValues)."
    \n"; + $aData[] = $aValues; + } + echo MyHelpers::make_table_from_assoc_array($aData); + } + + static protected function search_and_show_list(DBObjectSearch $oMyFilter) + { + $oObjSet = new CMDBObjectSet($oMyFilter); + echo $oMyFilter->__DescribeHTML()."' - Found ".$oObjSet->Count()." items.
    \n"; + self::show_list($oObjSet); + } + + static protected function search_and_show_list_from_sibusql($sSibuSQL) + { + echo $sSibuSQL."...
    \n"; + $oNewFilter = DBObjectSearch::FromSibuSQL($sSibuSQL); + self::search_and_show_list($oNewFilter); + } +} + + +/** + * Test to execute a scenario common to any business model (tries to build all the possible queries, etc.) + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +abstract class TestBizModelGeneric extends TestBizModel +{ + static public function GetName() + { + return 'Full test on a given business model'; + } + + static public function GetDescription() + { + return 'Systematic tests: gets each and every existing class and tries every attribute, search filters, etc.'; + } + + protected function DoPrepare() + { + parent::DoPrepare(); + + if (!MetaModel::DBExists()) + { + MetaModel::DBCreate(); + } + // something here to create records... but that's another story + } + + protected function DoExecute() + { + foreach(MetaModel::GetClasses() as $sClassName) + { + if (MetaModel::IsAbstract($sClassName)) continue; + + $oNobody = MetaModel::GetObject($sClassName, 123); + $oBaby = new $sClassName; + $oFilter = new DBObjectSearch($sClassName); + + // Challenge reversibility of SibusQL / filter object + // + $sExpr1 = $oFilter->ToSibuSQL(); + $oNewFilter = DBObjectSearch::FromSibuSQL($sExpr1); + $sExpr2 = $oNewFilter->ToSibuSQL(); + if ($sExpr1 != $sExpr2) + { + $this->ReportError("Found two different SibuSQL expression out of the (same?) filter: $sExpr1 != $sExpr2"); + } + + // Use the filter (perform the query) + // + $oSet = new CMDBObjectSet($oFilter); + $this->ReportSuccess('Found '.$oSet->Count()." objects of class $sClassName"); + } + return true; + } +} + + +?> diff --git a/core/userrights.class.inc.php b/core/userrights.class.inc.php new file mode 100644 index 000000000..b62c8a042 --- /dev/null +++ b/core/userrights.class.inc.php @@ -0,0 +1,211 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ + +class UserRightException extends CoreException +{ +} + + +define('UR_ALLOWED_NO', 0); +define('UR_ALLOWED_YES', 1); +define('UR_ALLOWED_DEPENDS', 2); + +define('UR_ACTION_READ', 1); // View an object +define('UR_ACTION_MODIFY', 2); // Create/modify an object/attribute +define('UR_ACTION_DELETE', 3); // Delete an object + +define('UR_ACTION_BULK_READ', 4); // Export multiple objects +define('UR_ACTION_BULK_MODIFY', 5); // Create/modify multiple objects +define('UR_ACTION_BULK_DELETE', 6); // Delete multiple objects + +define('UR_ACTION_APPLICATION_DEFINED', 10000); // Application specific actions (CSV import, View schema...) + +/** + * User management module API + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +abstract class UserRightsAddOnAPI +{ + abstract public function Setup(); // initial installation + abstract public function Init(); // loads data (possible optimizations) + abstract public function CheckCredentials($iUserId, $sPassword); // returns the id of the user or false + abstract public function GetFilter($iUserId, $sClass); // returns a filter object + abstract public function IsActionAllowed($iUserId, $sClass, $iActionCode, dbObjectSet $aInstances); + abstract public function IsStimulusAllowed($iUserId, $sClass, $sStimulusCode, dbObjectSet $aInstances); + abstract public function IsActionAllowedOnAttribute($iUserId, $sClass, $sAttCode, $iActionCode, dbObjectSet $aInstances); +} + + + +/** + * User management core API + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class UserRights +{ + protected static $m_oAddOn; + protected static $m_sUser; + protected static $m_sRealUser; + protected static $m_iUserId; + protected static $m_iRealUserId; + + public static function SelectModule($sModuleName) + { + if (!class_exists($sModuleName)) + { + throw new CoreException("Could not select this module, '$sModuleName' in not a valid class name"); + return; + } + if (!is_subclass_of($sModuleName, 'UserRightsAddOnAPI')) + { + throw new CoreException("Could not select this module, the class '$sModuleName' is not derived from UserRightsAddOnAPI"); + return; + } + self::$m_oAddOn = new $sModuleName; + self::$m_oAddOn->Init(); + self::$m_sUser = ''; + self::$m_sRealUser = ''; + self::$m_iUserId = 0; + self::$m_iRealUserId = 0; + } + + // Installation: create the very first user + public static function CreateAdministrator($sAdminUser, $sAdminPwd) + { + return self::$m_oAddOn->CreateAdministrator($sAdminUser, $sAdminPwd); + } + + // Installation (e.g: give default values for users) + public static function Setup() + { + // to be discussed... + return self::$m_oAddOn->Setup(); + } + + protected static function IsLoggedIn() + { + return (!empty(self::$m_sUser)); + } + + public static function Login($sName, $sPassword) + { + self::$m_iUserId = self::$m_oAddOn->CheckCredentials($sName, $sPassword); + if ( self::$m_iUserId !== false ) + { + self::$m_sUser = $sName; + self::$m_iRealUserId = self::$m_iUserId; + self::$m_sRealUser = $sName; + return true; + } + else + { + return false; + } + } + + public static function Impersonate($sName, $sPassword) + { + if (!self::CheckLogin()) return false; + + self::$m_iRealUserId = self::$m_oAddOn->CheckCredentials($sName, $sPassword); + if ( self::$m_iRealUserId !== false) + { + self::$m_sUser = $sName; + return true; + } + else + { + return false; + } + } + + public static function GetUser() + { + return self::$m_sUser; + } + + public static function GetUserId() + { + return self::$m_iUserId; + } + + public static function GetRealUser() + { + return self::$m_sRealUser; + } + + public static function GetRealUserId() + { + return self::$m_iRealUserId; + } + + protected static function CheckLogin() + { + if (!self::IsLoggedIn()) + { + //throw new UserRightException('No user logged in', array()); + return false; + } + return true; + } + + + public static function GetFilter($sClass) + { + if (!MetaModel::HasCategory($sClass, 'bizModel')) return new DBObjectSearch($sClass); + if (!self::CheckLogin()) return false; + + return self::$m_oAddOn->GetFilter(self::$m_iUserId, $sClass); + } + + public static function IsActionAllowed($sClass, $iActionCode, dbObjectSet $aInstances) + { + if (!MetaModel::HasCategory($sClass, 'bizModel')) return true; + if (!self::CheckLogin()) return false; + + return self::$m_oAddOn->IsActionAllowed(self::$m_iUserId, $sClass, $iActionCode, $aInstances); + } + + public static function IsStimulusAllowed($sClass, $sStimulusCode, dbObjectSet $aInstances) + { + if (!MetaModel::HasCategory($sClass, 'bizModel')) return true; + if (!self::CheckLogin()) return false; + + return self::$m_oAddOn->IsStimulusAllowed(self::$m_iUserId, $sClass, $sStimulusCode, $aInstances); + } + + public static function IsActionAllowedOnAttribute($sClass, $sAttCode, $iActionCode, dbObjectSet $aInstances) + { + if (!MetaModel::HasCategory($sClass, 'bizModel')) return true; + if (!self::CheckLogin()) return false; + + return self::$m_oAddOn->IsActionAllowedOnAttribute(self::$m_iUserId, $sClass, $sAttCode, $iActionCode, $aInstances); + } +} + + +?> diff --git a/core/valuesetdef.class.inc.php b/core/valuesetdef.class.inc.php new file mode 100644 index 000000000..c541683cc --- /dev/null +++ b/core/valuesetdef.class.inc.php @@ -0,0 +1,238 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ + +require_once('MyHelpers.class.inc.php'); + +abstract class ValueSetDefinition +{ + protected $m_bIsLoaded = false; + protected $m_aValues = array(); + protected $m_aArgsObj = array(); + protected $m_aArgsApp = array(); + + + // Displayable description that could be computed out of the std usage context + public function GetValuesDescription() + { + $aValues = $this->GetValues(array(), ''); + $aDisplayedValues = array(); + foreach($aValues as $key => $value) + { + $aDisplayedValues[] = "$key => $value"; + } + $sAllowedValues = implode(', ', $aDisplayedValues); + return $sAllowedValues; + } + + + public function GetValues($aArgs, $sBeginsWith) + { + if (!$this->m_bIsLoaded) + { + $this->LoadValues($aArgs); + $this->m_bIsLoaded = true; + } + if (strlen($sBeginsWith) == 0) + { + $aRet = $this->m_aValues; + } + else + { + $iCheckedLen = strlen($sBeginsWith); + $sBeginsWith = strtolower($sBeginsWith); + $aRet = array(); + foreach ($this->m_aValues as $sKey=>$sValue) + { + if (strtolower(substr($sValue, 0, $iCheckedLen)) == $sBeginsWith) + { + $aRet[$sKey] = $sValue; + } + } + } + return $aRet; + } + + public function ListArgsFromContextApp() + { + return $this->m_aArgsObj; + } + public function ListArgsFromContextObj() + { + return $this->m_aArgsApp; + } + + abstract protected function LoadValues($aArgs); +} + + +/** + * Set of existing values for an attribute, given a search filter + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class ValueSetObjects extends ValueSetDefinition +{ + protected $m_sFilterExpr; // in SibuSQL + protected $m_sValueAttCode; + protected $m_aOrderBy; + + public function __construct($sFilterExp, $sValueAttCode = '', $aOrderBy = array()) + { + $this->m_sFilterExpr = $sFilterExp; + $this->m_sValueAttCode = $sValueAttCode; + $this->m_aOrderBy = $aOrderBy; + } + + protected function LoadValues($aArgs) + { + $this->m_aValues = array(); + + $oFilter = DBObjectSearch::FromSibuSQL($this->m_sFilterExpr, $aArgs); + if (!$oFilter) return false; + + if (empty($this->m_sValueAttCode)) + { + $this->m_sValueAttCode = MetaModel::GetNameAttributeCode($oFilter->GetClass()); + } + $oObjects = new DBObjectSet($oFilter, $this->m_aOrderBy); + while ($oObject = $oObjects->Fetch()) + { + $this->m_aValues[$oObject->GetKey()] = $oObject->GetAsHTML($this->m_sValueAttCode); + } + return true; + } + + public function GetValuesDescription() + { + return 'Filter: '.$this->m_sFilterExpr; + } +} + + +/** + * Set of existing values for an attribute, given a search filter and a relation id + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class ValueSetRelatedObjects extends ValueSetObjects +{ + public function __construct($sFilterExp, $sRelCode, $sClass, $sValueAttCode = '', $aOrderBy = array()) + { + $sFullFilterExp = "$sClass: RELATED ($sRelCode, 1) TO ($sFilterExp)"; + parent::__construct($sFullFilterExp, $sValueAttCode, $aOrderBy); + } +} + + +/** + * Set oof existing values for an attribute, given a set of objects (AttributeLinkedSet) + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class ValueSetRelatedObjectsFromLinkedSet extends ValueSetDefinition +{ + protected $m_sLinkedSetAttCode; + protected $m_sRelCode; + protected $m_sValueAttCode; + protected $m_aOrderBy; + + public function __construct($sLinkedSetAttCode, $sRelCode, $sValueAttCode = '', $aOrderBy = array()) + { + $this->m_sLinkedSetAttCode = $sLinkedSetAttCode; + $this->m_sRelCode = $sRelCode; + $this->m_sValueAttCode = $sValueAttCode; + $this->m_aOrderBy = $aOrderBy; + } + + protected function LoadValues($aArgs) + { + $this->m_aValues = array(); + + if (empty($this->m_sValueAttCode)) + { + $this->m_sValueAttCode = MetaModel::GetNameAttributeCode($oFilter->GetClass()); + } + + $oCurrentObject = @$aArgs['*this*']; + if (!is_object($oCurrentObject)) return false; + + $oObjects = $oCurrentObject->Get($this->m_sLinkedSetAttCode); + while ($oObject = $oObjects->Fetch()) + { + $this->m_aValues[$oObject->GetKey()] = $oObject->Get($this->m_sValueAttCode); + } + return true; + } + + public function GetValuesDescription() + { + return 'Objects related ('.$this->m_sRelCode.') to objects linked through '.$this->m_sLinkedSetAttCode; + } +} + + +/** + * Fixed set values (could be hardcoded in the business model) + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class ValueSetEnum extends ValueSetDefinition +{ + public function __construct($Values) + { + if (is_array($Values)) + { + $aValues = $Values; + } + else + { + $aValues = array(); + foreach (explode(",", $Values) as $sVal) + { + $sVal = trim($sVal); + $sKey = $sVal; + $aValues[$sKey] = $sVal; + } + } + $this->m_aValues = $aValues; + } + + protected function LoadValues($aArgs) + { + return true; + } +} + +?> diff --git a/css/blue_green.css b/css/blue_green.css new file mode 100644 index 000000000..1e51162c3 --- /dev/null +++ b/css/blue_green.css @@ -0,0 +1,222 @@ +/* CSS Document */ +body { + font-family: Verdana, Arial, Helevtica; + font-size: smaller; + background-color: #68a; + color:#000000; + margin: 0; /* Remove body margin/padding */ + padding: 0; + overflow: hidden; /* Remove scroll bars on browser window */ +} + +table { + border: 1px solid #000000; +} + +.raw_output { + font-family: Courier-New, Courier, Arial, Helevtica; + font-size: smaller; + background-color: #eeeeee; + color: #000000; + border: 1px dashed #000000; + padding: 0.25em; + margin-top: 1em; +} + +th { + font-family: Verdana, Arial, Helvetica; + font-size: smaller; + color: #000000; + background-color:#ace27d; +} + +td { + font-family: Verdana, Arial, Helvetica; + font-size: smaller; + background-color: #b7cfe8; +} + +tr.clicked td { + font-family: Verdana, Arial, Helvetica; + font-size: smaller; + background-color: #ffcfe8; +} + +td.label { + font-family: Verdana, Arial, Helvetica; + font-size: smaller; + color: #000000; + background-color:#ace27d; + padding: 0.2em; +} + +td a, td a:visited { + text-decoration:none; + color:#000000; +} +td a:hover { + text-decoration:underline; + color:#FFFFFF; +} + +a.small_action { + font-family: Verdana, Arial, Helvetica; + font-size: smaller; + color: #000000; + text-decoration:none; +} + +.display_block { + noborder: 1px dashed #CCC; + background: #79b; + padding:0.25em; +} +div#TopPane .display_block { + background: #f0eee0; + padding:0.25em; + text-align:center; +} +div#TopPane label { + color:#000; + background: #f0eee0; +} + +div#TopPane td { + color:#000; + background: #f0eee0; +} + +.loading { + noborder: 1px dashed #CCC; + background: #b9c1c8; + padding:0.25em; +} + +label { + font-family:Georgia, "Times New Roman", Times, serif; + color:#FFFFFF; + text-align:right; +} + +input.textSearch { + border:1px solid #333; + noheight:1.2em; + font-size:0.8em; + font-family:Verdana, Arial, Helvetica, sans-serif; + color:#000000; +} + +/* By Rom */ +.csvimport_createobj { + color: #AA0000; + background-color:#EEEEEE; +} +.csvimport_error { + font-weight: bold; + color: #FF0000; + background-color:#EEEEEE; +} +.csvimport_warning { + color: #CC8888; + background-color:#EEEEEE; +} +.csvimport_ok { + color: #00000; + background-color:#BBFFBB; +} +.csvimport_reconkey { + font-style: italic; + color: #888888; + background-color:#FFFFF; +} +.csvimport_extreconkey { + color: #888888; + background-color:#FFFFFF; +} + +.treeview, .treeview ul { + padding: 0; + margin: 0; + list-style: none; +} + +.treeview li { + margin: 0; + padding: 3px 0pt 3px 16px; + font-size:0.9em; +} + +ul.dir li { + padding: 2px 0 0 16px; +} + +.treeview li { background: url(../images/tv-item.gif) 0 0 no-repeat; } +.treeview .collapsable { background-image: url(/images/tv-collapsable.gif); } +.treeview .expandable { background-image: url(/images/tv-expandable.gif); } +.treeview .last { background-image: url(/images/tv-item-last.gif); } +.treeview .lastCollapsable { background-image: url(/images/tv-collapsable-last.gif); } +.treeview .lastExpandable { background-image: url(/images/tv-expandable-last.gif); } + +#Header { padding: 0; background:#ccc url(/images/bandeau2.gif) repeat-x center;} +div.iTopLogo { + background:url(/images/iTop.gif) no-repeat center; + width:100px; + height:56px; +} +div.iTopLogo span { + display:none; +} + +#MySplitter { + /* Height is set to match window size in $().ready() below */ + border:0px; + margin:4px; + padding:0px; + min-width: 100px; /* Splitter can't be too thin ... */ + min-height: 100px; /* ... or too flat */ +} +#LeftPane { + background: #f0eee0; + padding: 4px; + overflow: auto; /* Scroll bars appear as needed */ + color:#666; +} +#TopPane { /* Top nested in right pane */ + background: #f0eee0; + padding: 4px; + height: 150px; /* Initial height */ + min-height: 75px; /* Minimum height */ + overflow: auto; + color:#666; +} +#RightPane { /* Bottom nested in right pane */ + background: #79b; + height:150px; /* Initial height */ + min-height:130px; + no.padding:15px; + no.margin:10px; + overflow:auto; + color:#fff; +} + +#BottomPane { /* Bottom nested in right pane */ + background: #79b; + padding: 4px; + overflow: auto; + color:#fff; +} +#MySplitter .vsplitbar { + width: 7px; + height: 50px; + background: #68a url(/images/vgrabber2.gif) no-repeat center; +} +#MySplitter .vsplitbar.active, #MySplitter .vsplitbar:hover { + background: #68a url(/images/vgrabber2_active.gif) no-repeat center; +} +#MySplitter .hsplitbar { + height: 8px; + background: #68a url(/images/hgrabber2.gif) no-repeat center; +} +#MySplitter .hsplitbar.active, #MySplitter .hsplitbar:hover { + background: #68a url(/images/hgrabber2_active.gif) no-repeat center; +} diff --git a/css/date.picker.css b/css/date.picker.css new file mode 100644 index 000000000..a394482a0 --- /dev/null +++ b/css/date.picker.css @@ -0,0 +1,117 @@ + + +table.jCalendar { + border: 1px solid #000; + background: #aaa; + border-collapse: separate; + border-spacing: 2px; +} +table.jCalendar th { + background: #333; + color: #fff; + font-weight: bold; + padding: 3px 5px; +} +table.jCalendar td { + background: #ccc; + color: #000; + padding: 3px 5px; + text-align: center; +} +table.jCalendar td.other-month { + background: #ddd; + color: #aaa; +} +table.jCalendar td.today { + background: #666; + color: #fff; +} +table.jCalendar td.selected { + background: #f66; + color: #fff; +} +table.jCalendar td.selected:hover { + background: #f33; + color: #fff; +} +table.jCalendar td:hover, table.jCalendar td.dp-hover { + background: #fff; + color: #000; +} +table.jCalendar td.disabled, table.jCalendar td.disabled:hover { + background: #bbb; + color: #888; +} + +/* For the popup */ + +/* NOTE - you will probably want to style a.dp-choose-date - see how I did it in demo.css */ + +div.dp-popup { + position: relative; + background: #ccc; + font-size: 10px; + font-family: arial, sans-serif; + padding: 2px; + width: 171px; + line-height: 1.2em; +} +div#dp-popup { + position: absolute; + z-index: 199; +} +div.dp-popup h2 { + font-size: 12px; + text-align: center; + margin: 2px 0; + padding: 0; +} +a#dp-close { + font-size: 11px; + padding: 4px 0; + text-align: center; + display: block; +} +a#dp-close:hover { + text-decoration: underline; +} +div.dp-popup a { + color: #000; + text-decoration: none; + padding: 3px 2px 0; +} +div.dp-popup div.dp-nav-prev { + position: absolute; + top: 2px; + left: 4px; + width: 100px; +} +div.dp-popup div.dp-nav-prev a { + float: left; +} +/* Opera needs the rules to be this specific otherwise it doesn't change the cursor back to pointer after you have disabled and re-enabled a link */ +div.dp-popup div.dp-nav-prev a, div.dp-popup div.dp-nav-next a { + cursor: pointer; +} +div.dp-popup div.dp-nav-prev a.disabled, div.dp-popup div.dp-nav-next a.disabled { + cursor: default; +} +div.dp-popup div.dp-nav-next { + position: absolute; + top: 2px; + right: 4px; + width: 100px; +} +div.dp-popup div.dp-nav-next a { + float: right; +} +div.dp-popup a.disabled { + cursor: default; + color: #aaa; +} +div.dp-popup td { + cursor: pointer; +} +div.dp-popup td.disabled { + cursor: default; +} \ No newline at end of file diff --git a/css/default.css b/css/default.css new file mode 100644 index 000000000..eaabed838 --- /dev/null +++ b/css/default.css @@ -0,0 +1,165 @@ +/* CSS Document */ +body { + font-family: Verdana, Arial, Helevtica; + font-size: smaller; + background-color: #ffffff; + color:#000000; + margin: 0; /* Remove body margin/padding */ + padding: 0; + overflow: hidden; /* Remove scroll bars on browser window */ +} + +table { + border: 1px solid #000000; +} + +.raw_output { + font-family: Courier-New, Courier, Arial, Helevtica; + font-size: smaller; + background-color: #eeeeee; + color: #000000; + border: 1px dashed #000000; + padding: 0.25em; + margin-top: 1em; +} + +th { + font-family: Verdana, Arial, Helevtica; + font-size: smaller; + color: #000000; + background-color:#E1DEB5; +} + +td { + font-family: Verdana, Arial, Helevtica; + font-size: smaller; +} + +td.label { + font-family: Verdana, Arial, Helevtica; + font-size: smaller; + color: #000000; + background-color:#E1DEB5; + padding: 0.2em; +} + +a.small_action { + font-family: Verdana, Arial, Helvetica; + font-size: smaller; + color: #000000; + text-decoration:none; +} + +.display_block { + border: 1px dashed #CCC; + background: #CFC; + padding:0.25em; +} + +.loading { + border: 1px dashed #CCC; + background: #FCC; + padding:0.25em; +} + +/* By Rom */ +.csvimport_createobj { + color: #AA0000; + background-color:#EEEEEE; +} +.csvimport_error { + font-weight: bold; + color: #FF0000; + background-color:#EEEEEE; +} +.csvimport_warning { + color: #CC8888; + background-color:#EEEEEE; +} +.csvimport_ok { + color: #00000; + background-color:#BBFFBB; +} +.csvimport_reconkey { + font-style: italic; + color: #888888; + background-color:#FFFFF; +} +.csvimport_extreconkey { + color: #888888; + background-color:#FFFFFF; +} + +.treeview, .treeview ul { + padding: 0; + margin: 0; + list-style: none; +} + +.treeview li { + margin: 0; + padding: 3px 0pt 3px 16px; +} + +ul.dir li { padding: 2px 0 0 16px; } + +.treeview li { background: url(../images/tv-item.gif) 0 0 no-repeat; } +.treeview .collapsable { background-image: url(../images/tv-collapsable.gif); } +.treeview .expandable { background-image: url(../images/tv-expandable.gif); } +.treeview .last { background-image: url(../images/tv-item-last.gif); } +.treeview .lastCollapsable { background-image: url(../images/tv-collapsable-last.gif); } +.treeview .lastExpandable { background-image: url(../images/tv-expandable-last.gif); } + +#MySplitter { + /* Height is set to match window size in $().ready() below */ + border:0px; + margin:4px; + padding:0px; + min-width: 100px; /* Splitter can't be too thin ... */ + min-height: 100px; /* ... or too flat */ +} +#LeftPane { + background: #f0eee0; + padding: 4px; + overflow: auto; /* Scroll bars appear as needed */ + color:#666; +} +#TopPane { /* Top nested in right pane */ + background: #f0eee0; + padding: 4px; + height: 150px; /* Initial height */ + min-height: 75px; /* Minimum height */ + overflow: auto; + color:#666; +} +#RightPane { /* Bottom nested in right pane */ + background: #79b; + height:150px; /* Initial height */ + min-height:130px; + no.padding:15px; + no.margin:10px; + overflow:auto; + color:#fff; +} + +#BottomPane { /* Bottom nested in right pane */ + background: #79b; + padding: 4px; + overflow: auto; + color:#fff; +} +#MySplitter .vsplitbar { + width: 7px; + height: 50px; + background: #68a url(../images/vgrabber2.gif) no-repeat center; +} +#MySplitter .vsplitbar.active, #MySplitter .vsplitbar:hover { + background: #68a url(../images/vgrabber2_active.gif) no-repeat center; +} +#MySplitter .hsplitbar { + height: 8px; + background: #68a url(../images/hgrabber2.gif) no-repeat center; +} +#MySplitter .hsplitbar.active, #MySplitter .hsplitbar:hover { + background: #68a url(../images/hgrabber2_active.gif) no-repeat center; +} diff --git a/css/jqModal.css b/css/jqModal.css new file mode 100644 index 000000000..db45b7039 --- /dev/null +++ b/css/jqModal.css @@ -0,0 +1,40 @@ +/* jqModal base Styling courtesy of; + Brice Burgess */ + +/* The Window's CSS z-index value is respected (takes priority). If none is supplied, + the Window's z-index value will be set to 3000 by default (in jqModal.js). You + can change this value by either; + a) supplying one via CSS + b) passing the "zIndex" parameter. E.g. (window).jqm({zIndex: 500}); */ + +.jqmWindow { + display: none; + + position: fixed; + top: 10%; + left: 20%; + + margin-left: -100px; + width: 800px; + + background-color: #FFF; + color: #333; + border: 1px solid black; + padding: 12px; +} + +.jqmOverlay { background-color: #333; } + +/* Background iframe styling for IE6. Prevents ActiveX bleed-through (" ] || + + !tags.indexOf("", "" ] || + + tags.match(/^<(thead|tbody|tfoot|colg|cap)/) && + [ 1, "", "
    " ] || + + !tags.indexOf("", "" ] || + + // matched above + (!tags.indexOf("", "" ] || + + !tags.indexOf("", "" ] || + + // IE can't serialize and \n"); + } + break; + + case 'apply_modify': + $sClass = utils::ReadPostedParam('class', ''); + $id = utils::ReadPostedParam('id', ''); + $sTransactionId = utils::ReadPostedParam('transaction_id', ''); + if ( empty($sClass) || empty($id)) // TO DO: check that the class name is valid ! + { + $oP->add("

    'class' and 'id' parameters must be specifed for this operation.

    \n"); + } + else if (!utils::IsTransactionValid($sTransactionId)) + { + $oP->p("Error: object has already be updated!\n"); + } + else + { + $oObj = $oContext->GetObject($sClass, $id); + if ($oObj != null) + { + $oP->set_title("iTop - ".$oObj->GetName()." - $sClass modification"); + $oP->add("

    ".$oObj->GetName()." - $sClass modification

    \n"); + $bObjectModified = false; + foreach(MetaModel::ListAttributeDefs(get_class($oObj)) as $sAttCode=>$oAttDef) + { + $iFlags = $oObj->GetAttributeFlags($sAttCode); + if ($iFlags & (OPT_ATT_HIDDEN | OPT_ATT_READONLY)) + { + // Non-visible, or read-only attribute, do nothing + } + else if ($oAttDef->IsLinkSet()) + { + // Link set, the data is a set of link objects, encoded in JSON + $aAttributes[$sAttCode] = trim(utils::ReadPostedParam("attr_$sAttCode", '')); + if (!empty($aAttributes[$sAttCode])) + { + $oLinkSet = WizardHelper::ParseJsonSet($oObj, $oAttDef->GetLinkedClass(), $oAttDef->GetExtKeyToMe(), $aAttributes[$sAttCode]); + $oObj->Set($sAttCode, $oLinkSet); + // TO DO: detect a real modification, for now always update !! + $bObjectModified = true; + } + } + else if (!$oAttDef->IsExternalField()) + { + $aAttributes[$sAttCode] = trim(utils::ReadPostedParam("attr_$sAttCode", '')); + $previousValue = $oObj->Get($sAttCode); + if (!empty($aAttributes[$sAttCode]) && ($previousValue != $aAttributes[$sAttCode])) + { + $oObj->Set($sAttCode, $aAttributes[$sAttCode]); + $bObjectModified = true; + } + } + } + if (!$bObjectModified) + { + $oP->p("No modification detected. ".get_class($oObj)." has not been updated.\n"); + } + else if ($oObj->CheckToUpdate()) + { + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + if (UserRights::GetUser() != UserRights::GetRealUser()) + { + $sUserString = UserRights::GetRealUser()." on behalf of ".UserRights::GetUser(); + } + else + { + $sUserString = UserRights::GetUser(); + } + $oMyChange->Set("userinfo", $sUserString); + $iChangeId = $oMyChange->DBInsert(); + $oObj->DBUpdateTracked($oMyChange); + + $oP->p(get_class($oObj)." updated.\n"); + } + else + { + $oP->p("Error: object can not be updated!\n"); + //$oObj->Reload(); // restore default values! + } + } + else + { + $oP->set_title("iTop - Error"); + $oP->add("

    Sorry this object does not exist (or you are not allowed to edit it).

    \n"); + } + } + $oObj->DisplayDetails($oP); + break; + + case 'delete': + $sClass = utils::ReadParam('class', ''); + $id = utils::ReadParam('id', ''); + $oObj = $oContext->GetObject($sClass, $id); + $sName = $oObj->GetName(); + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + if (UserRights::GetUser() != UserRights::GetRealUser()) + { + $sUserString = UserRights::GetRealUser()." on behalf of ".UserRights::GetUser(); + } + else + { + $sUserString = UserRights::GetUser(); + } + $oMyChange->Set("userinfo", $sUserString); + $oMyChange->DBInsert(); + $oObj->DBDeleteTracked($oMyChange); + $oP->add("

    ".$sName." - $sClass deleted

    \n"); + break; + + case 'apply_new': + $oP->p('Creation of the object'); + $oP->p('Obsolete, should now go through the wizard...'); + break; + + case 'apply_clone': + $sClass = utils::ReadPostedParam('class', ''); + $iCloneId = utils::ReadPostedParam('clone_id', ''); + $sTransactionId = utils::ReadPostedParam('transaction_id', ''); + if (!utils::IsTransactionValid($sTransactionId)) + { + $oP->p("Error: object has already be cloned!\n"); + } + else + { + $oObj = $oContext->GetObject($sClass, $iCloneId); + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + if (UserRights::GetUser() != UserRights::GetRealUser()) + { + $sUserString = UserRights::GetRealUser()." on behalf of ".UserRights::GetUser(); + } + else + { + $sUserString = UserRights::GetUser(); + } + $oMyChange->Set("userinfo", $sUserString); + $iChangeId = $oMyChange->DBInsert(); + $sStateAttCode = MetaModel::GetStateAttributeCode(get_class($oObj)); + foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode=>$oAttDef) + { + if ( ('finalclass' != $sAttCode) && // finalclass is a reserved word, hardcoded ! + ($sStateAttCode != $sAttCode) && + (!$oAttDef->IsExternalField()) ) + { + $value = utils::ReadPostedParam('attr_'.$sAttCode, ''); + $oObj->Set($sAttCode, $value); + } + } + $oObj->DBCloneTracked($oMyChange); + $oP->add("

    ".$oObj->GetName()." - $sClass created

    \n"); + $oObj->DisplayDetails($oP); + } + + break; + + case 'wizard_apply_new': + $sJson = utils::ReadPostedParam('json_obj', ''); + $oWizardHelper = WizardHelper::FromJSON($sJson); + $sTransactionId = utils::ReadPostedParam('transaction_id', ''); + if (!utils::IsTransactionValid($sTransactionId)) + { + $oP->p("Error: object has already be created!\n"); + } + else + { + $oObj = $oWizardHelper->GetTargetObject(); + if (is_object($oObj)) + { + $sClass = get_class($oObj); + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + if (UserRights::GetUser() != UserRights::GetRealUser()) + { + $sUserString = UserRights::GetRealUser()." on behalf of ".UserRights::GetUser(); + } + else + { + $sUserString = UserRights::GetUser(); + } + $oMyChange->Set("userinfo", $sUserString); + $iChangeId = $oMyChange->DBInsert(); + $oObj->DBInsertTracked($oMyChange); + $oP->set_title("iTop - ".$oObj->GetName()." - $sClass created"); + $oP->add("

    ".$oObj->GetName()." - $sClass created

    \n"); + $oObj->DisplayDetails($oP); + } + } + break; + + case 'stimulus': + $sClass = utils::ReadParam('class', ''); + $id = utils::ReadParam('id', ''); + $sStimulus = utils::ReadParam('stimulus', ''); + if ( empty($sClass) || empty($id) || empty($sStimulus) ) // TO DO: check that the class name is valid ! + { + $oP->add("

    'class', 'id' and 'stimulus' parameters must be specifed for this operation.

    \n"); + } + else + { + $oObj = $oContext->GetObject($sClass, $id); + if ($oObj != null) + { + $aTransitions = $oObj->EnumTransitions(); + $aStimuli = MetaModel::EnumStimuli($sClass); + if (!isset($aTransitions[$sStimulus])) + { + $oP->add("

    Error: Invalid stimulus: '$sStimulus' on object: {$oObj->GetName()} in state {$oObj->GetState()}.

    \n"); + } + else + { + $sActionLabel = $aStimuli[$sStimulus]->Get('label'); + $sActionDetails = $aStimuli[$sStimulus]->Get('description'); + $aTransition = $aTransitions[$sStimulus]; + $sTargetState = $aTransition['target_state']; + $aTargetStates = MetaModel::EnumStates($sClass); + $oP->add("
    \n"); + $oP->add("

    $sActionLabel - {$oObj->GetName()}

    \n"); + //$oP->add("

    Applying '$sActionLabel' on object: {$oObj->GetName()} in state {$oObj->GetState()} to target state: $sTargetState.

    \n"); + $oP->add("
    \n"); + $oObj->DisplayBareDetails($oP); + $aTargetState = $aTargetStates[$sTargetState]; + //print_r($aTransitions[$sStimulus]); + //print_r($aTargetState); + $aExpectedAttributes = $aTargetState['attribute_list']; + $oP->add("
    \n"); + $oP->add("

    $sActionDetails

    \n"); + $oP->add("
    \n"); + $oP->add("
    \n"); + $aDetails = array(); + foreach($aExpectedAttributes as $sAttCode => $iExpectCode) + { + // Prompt for an attribute if + // - the attribute must be changed or must be displayed to the user for confirmation + // - or the field is mandatory and currently empty + if ( ($iExpectCode & (OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT)) || + (($iExpectCode & OPT_ATT_MANDATORY) && ($oObj->Get($sAttCode) == '')) ) + { + $aAttributesDef = MetaModel::ListAttributeDefs($sClass); + $oAttDef = $aAttributesDef[$sAttCode]; + $sHTMLValue = cmdbAbstractObject::GetFormElementForField($oP, $sClass, $sAttCode, $oAttDef, $oObj->Get($sAttCode), $oObj->GetDisplayValue($sAttCode)); + $aDetails[] = array('label' => $oAttDef->GetLabel(), 'value' => $sHTMLValue); + } + } + $oP->details($aDetails); + $oP->add("\n"); + $oP->add("\n"); + $oP->add("\n"); + $oP->add("\n"); + $oP->add("\n"); + $oP->add($oAppContext->GetForForm()); + $oP->add("    \n"); + $oP->add("\n"); + $oP->add("
    \n"); + $oP->add("
    \n"); + $oP->add("
    \n"); + } + } + else + { + $oP->set_title("iTop - Error"); + $oP->add("

    Sorry this object does not exist (or you are not allowed to edit it).

    \n"); + } + } + break; + + case 'apply_stimulus': + $sClass = utils::ReadPostedParam('class', ''); + $id = utils::ReadPostedParam('id', ''); + $sTransactionId = utils::ReadPostedParam('transaction_id', ''); + $sStimulus = utils::ReadPostedParam('stimulus', ''); + if ( empty($sClass) || empty($id) || empty($sStimulus) ) // TO DO: check that the class name is valid ! + { + $oP->add("

    'class', 'id' and 'stimulus' parameters must be specifed for this operation.

    \n"); + } + else + { + $oObj = $oContext->GetObject($sClass, $id); + if ($oObj != null) + { + $aTransitions = $oObj->EnumTransitions(); + $aStimuli = MetaModel::EnumStimuli($sClass); + if (!isset($aTransitions[$sStimulus])) + { + $oP->add("

    Error: Invalid stimulus: '$sStimulus' on object: {$oObj->GetName()} in state {$oObj->GetState()}.

    \n"); + } + else if (!utils::IsTransactionValid($sTransactionId)) + { + $oP->p("Error: object has already be updated!\n"); + } + else + { + $sActionLabel = $aStimuli[$sStimulus]->Get('label'); + $sActionDetails = $aStimuli[$sStimulus]->Get('description'); + $aTransition = $aTransitions[$sStimulus]; + $sTargetState = $aTransition['target_state']; + $aTargetStates = MetaModel::EnumStates($sClass); + $oP->add("
    \n"); + $oP->add("

    $sActionLabel - {$oObj->GetName()}

    \n"); + $oP->add("

    $sActionDetails

    \n"); + $oP->add("

    Applying '$sActionLabel' on object: {$oObj->GetName()} in state {$oObj->GetState()} to target state: $sTargetState.

    \n"); + $oP->add("
    \n"); + $aTargetState = $aTargetStates[$sTargetState]; + //print_r($aTransitions[$sStimulus]); + //print_r($aTargetState); + $aExpectedAttributes = $aTargetState['attribute_list']; + $aDetails = array(); + foreach($aExpectedAttributes as $sAttCode => $iExpectCode) + { + if (($iExpectCode & OPT_ATT_MUSTCHANGE) || ($oObj->Get($sAttCode) == '') ) + { + $paramValue = utils::ReadPostedParam("attr_$sAttCode", ''); + $oObj->Set($sAttCode, $paramValue); + } + } + if ($oObj->ApplyStimulus($sStimulus) && $oObj->CheckToUpdate()) + { + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + if (UserRights::GetUser() != UserRights::GetRealUser()) + { + $sUserString = UserRights::GetRealUser()." on behalf of ".UserRights::GetUser(); + } + else + { + $sUserString = UserRights::GetUser(); + } + $oMyChange->Set("userinfo", $sUserString); + $iChangeId = $oMyChange->DBInsert(); + $oObj->DBUpdateTracked($oMyChange); + + $oP->p(get_class($oObj)." updated.\n"); + } + $oObj->DisplayDetails($oP); + } + } + else + { + $oP->set_title("iTop - Error"); + $oP->add("

    Sorry this object does not exist (or you are not allowed to edit it).

    \n"); + } + } + break; + + + default: + $oActiveNode->RenderContent($oP, $oAppContext->GetAsHash()); +} +$oP->output(); +?> diff --git a/pages/UniversalSearch.php b/pages/UniversalSearch.php new file mode 100644 index 000000000..ebe762fb3 --- /dev/null +++ b/pages/UniversalSearch.php @@ -0,0 +1,142 @@ +add("
    "); +$oP->add("
    "); +$oP->add(""); +$oP->add("Select the class to search:
    "); + +// Second part: advanced search form: +$oFilter = null; +if (!empty($sFilter)) +{ + $oFilter = CMDBSearchFilter::unserialize($sFilter); +} +else if (!empty($sClassName)) +{ + $oFilter = new CMDBSearchFilter($sClassName); +} + +if ($oFilter != null) +{ + $oSet =new CMDBObjectSet($oFilter); + cmdbAbstractObject::DisplaySearchForm($oP, $oSet, array('org_id' => $currentOrganization, 'class' => $sClassName)); + $oP->add("
    \n"); + + // Search results + $oP->add("
    "); + $oResultBlock = new DisplayBlock($oFilter, 'list', false); + $oResultBlock->RenderContent($oP); + + // Menu node + $sFilter = $oFilter->ToSibusQL(); + $sMenuNodeContent = << +$sFilter +
    +
    +

    +$sFilter +
    +EOF; + + + if ($sOperation == "add_menu") + { + $oMenuNode = MetaModel::NewObject('menuNode'); + $sClass = utils::ReadPostedParam('class', ''); + $sLabel = utils::ReadPostedParam('label', ''); + $sDescription = utils::ReadPostedParam('description', ''); + $iPreviousNodeId = utils::ReadPostedParam('previous_node_id', 1); + $bChildItem = utils::ReadPostedParam('child_item', false); + $oMenuNode->Set('label', $sDescription); + $oMenuNode->Set('name', $sLabel); + $oMenuNode->Set('icon_path', '/images/std_view.gif'); + $oMenuNode->Set('template', $sMenuNodeContent); + $oMenuNode->Set('hyperlink', 'UI.php'); + $oMenuNode->Set('type', 'user'); + $oMenuNode->Set('user_id', UserRights::GetUserId()); + $oPreviousNode = MetaModel::GetObject('menuNode', $iPreviousNodeId); + if ($bChildItem) + { + // Insert the new item as a child of the previous one + $oMenuNode->Set('parent_id', $iPreviousNodeId); + $oMenuNode->Set('rank', 1); // A new child item is the first one, so let's start the numbering at 1 + // If there are already child nodes, shift their rank by one + // to make room for the newly inserted child node + $oNextNodeSet = $oPreviousNode->GetChildNodesSet(null); // null => don't limit ourselves to the user context + // since we need to update all children in order to keep + // the database consistent + while($oNextNode = $oNextNodeSet->Fetch()) + { + $oNextNode->Set('rank', 1 + $oNextNode->Get('rank')); + $oNextNode->DBUpdate(); + } + } + else + { + // Insert the new item as the next sibling of the previous one + $oMenuNode->Set('parent_id', $oPreviousNode->Get('parent_id')); + $oMenuNode->Set('rank', 1 + $oPreviousNode->Get('rank')); // the new item comes immediatly after the selected one + // Add 1 to the rank of all the nodes currently following the 'selected' one + // to make room for the newly inserted node + $oNextNodeSet = $oPreviousNode->GetNextNodesSet(null); // null => don't limit ourselves to the user context + // since we need to update all children in order to keep + // the database consistent + while($oNextNode = $oNextNodeSet->Fetch()) + { + $oNextNode->Set('rank', 1 + $oNextNode->Get('rank')); + $oNextNode->DBUpdate(); + } + + } + if ($oMenuNode->CheckToInsert()) + { + $oMenuNode->DBInsert(); + $oP->add("
    "); + $oP->add("

    Menu item created !

    "); + $oP->add(""); + $oP->add(""); + $oP->add(""); + $oP->add(""); + } + } + + $oP->add("\n"); +} +else +{ + $oP->add("\n"); +} + +$oP->output(); +?> diff --git a/pages/advanced_search.php b/pages/advanced_search.php new file mode 100644 index 000000000..e10a2c095 --- /dev/null +++ b/pages/advanced_search.php @@ -0,0 +1,310 @@ +no_cache(); + + +MetaModel::CheckDefinitions(); +// new API - MetaModel::DBCheckFormat(); +// not necessary, and time consuming! +// MetaModel::DBCheckIntegrity(); + + +function ReadParam($sName, $defaultValue = "") +{ + return isset($_REQUEST[$sName]) ? $_REQUEST[$sName] : $defaultValue; +} + + + +function Page1_AskClass($oPage) +{ + $oPage->add("\n"); + //$oPage->add(""); + $oPage->p("Please select the type of object that you want to look for:"); + $oPage->MakeClassesSelect("class", "", 50); + $oPage->add("\n"); + $oPage->add("
    \n"); +} + + +function Page2_ConfigFilters($oPage, $oFilter) +{ + $sClass = $oFilter->GetClass(); + + $oPage->p("Objects of class $sClass"); + $oPage->add("
    \n"); + $oPage->add("\n"); + + // Full text input + // + $oPage->add("
    \n"); + $oPage->add("Full text: "); + $sFullText = ""; + foreach($oFilter->GetCriteria_FullText() as $sFullText) + { + // #@# Known limitation: do not consider other full text conditions... + continue; + } + + $oPage->add("\n"); + $oPage->add("
    \n"); + + // Attribute-related criteria + // + foreach (MetaModel::GetClassFilterDefs($sClass) as $sFltCode => $oFltDef) + { + // Set its current values + $sOpCode = "__none__"; + $sValue = ""; + foreach($oFilter->GetCriteria() as $aCritInfo) + { + if ($aCritInfo["filtercode"] == "pkey") + { + // ??? + } + elseif ($aCritInfo["filtercode"] == $sFltCode) + { + $sOpCode = $aCritInfo["opcode"]; + $sValue = $aCritInfo["value"]; + break; + } + } + + $oPage->add("
    \n"); + //$oPage->add($oFltDef->GetType()." (".$oFltDef->GetTypeDesc().")"); + $oPage->add(" ".$oFltDef->GetLabel()." "); + + $aOperators = array_merge(array("__none__" => ""), $oFltDef->GetOperators()); + $oPage->add_select($aOperators, "flt_ops[$sFltCode]", $sOpCode, 100); + $oPage->add("\n"); + + $oPage->add("\n"); + $oPage->add("
    \n"); + } + + // Ext key criteria + // + foreach (MetaModel::EnumReferencedClasses($sClass) as $sExtKeyAttCode => $sRemoteClass) + { + // Set its current values + $oSubFilter = $oFilter->GetCriteria_PointingTo($sExtKeyAttCode); + if (!$oSubFilter) + { + $oSubFilter = new CMDBSearchFilter($sRemoteClass); + } + + $oPage->add("
    \n"); + $oAtt = MetaModel::GetAttributeDef($oFilter->GetClass(), $sExtKeyAttCode); + $oPage->add($oAtt->GetLabel()." having ({$oSubFilter->DescribeConditions()})"); + //$oPage->add("having $oFilter->DescribeConditionPointTo($sExtKeyAttCode)); + $oPage->add("\n"); + + $oPage->add(dialogstack::RenderEditableField("Edit...", "flt_pointto[$sExtKeyAttCode]", $oSubFilter->serialize(), true)); + $oPage->add("
    \n"); + } + + // Ext key criteria, the other way + // + foreach (MetaModel::EnumReferencingClasses($sClass, true) as $sRemoteClass => $aRemoteKeys) + { + foreach ($aRemoteKeys as $sExtKeyAttCode) + { + // Set its current values + $oSubFilter = $oFilter->GetCriteria_ReferencedBy($sRemoteClass, $sExtKeyAttCode); + if (!$oSubFilter) + { + $oSubFilter = new CMDBSearchFilter($sRemoteClass); + } + + $oPage->add("
    \n"); + //$oPage->add($oFilter->DescribeConditionRefBy($sRemoteClass, $sExtKeyAttCode)); + $oAtt = MetaModel::GetAttributeDef($sRemoteClass, $sExtKeyAttCode); + $oPage->add("being ".$oAtt->GetLabel()." for ".$sRemoteClass."(e)s in ({$oSubFilter->DescribeConditions()})"); + $oPage->add("\n"); + + $oPage->add(dialogstack::RenderEditableField("Edit...", "flt_refedby[$sRemoteClass][$sExtKeyAttCode]", $oSubFilter->serialize(), true)); + $oPage->add("
    \n"); + } + } + + // Ext key criteria -> link objects + // + foreach (MetaModel::EnumLinkingClasses($sClass) as $sLinkClass => $aRemoteClasses) + { + foreach($aRemoteClasses as $sExtKeyAttCode => $sRemoteClass) + { + // Set its current values + //$oSubFilter = $oFilter->GetCriteria_PointingTo($sExtKeyAttCode); + $oSubFilter = null; + if (!$oSubFilter) + { + $oSubFilter = new CMDBSearchFilter($sRemoteClass); + } + $oPage->add("
    \n"); + //$oPage->add(" ".MetaModel::GetClassLabel($sRemoteClass)." "); + $oPage->add(" Linked to '".MetaModel::GetLinkLabel($sLinkClass, $sExtKeyAttCode)."' by "); + $oSubFilter = new CMDBSearchFilter($sRemoteClass); + $oPage->add($oSubFilter->__DescribeHTML()); + $oPage->add("\n"); + + $oPage->add(dialogstack::RenderEditableField("Edit...", "flt_linkedwith[$sRemoteClass][$sExtKeyAttCode]", $oSubFilter->serialize(), true)); + $oPage->add("
    \n"); + } + } + + $oPage->add("\n"); + $oPage->add("
    \n"); +} + +function MakeFilterFromArgs() +{ + $sClass = ReadParam("class"); + $sFilterFullText = ReadParam("flt_fulltext", ""); + $aFilterOps = ReadParam("flt_ops", array()); + $aFilterValues = ReadParam("flt_values", array()); + $aPointTo = ReadParam("flt_pointto", array()); + $aRefedBy = ReadParam("flt_refedby", array()); + $aLinkedWith = ReadParam("flt_linkedwith", array()); + + $oFilter = new CMDBSearchFilter($sClass); + + if (!empty($sFilterFullText)) + { + $oFilter->AddCondition_FullText($sFilterFullText); + } + + foreach($aFilterOps as $sFltCode=>$sOpCode) + { + if ($sOpCode == "__none__") continue; + $oFilter->AddCondition($sFltCode, $aFilterValues[$sFltCode], $sOpCode); + } + + foreach($aPointTo as $sExtKeyAttCode=>$sFilterShortcut) + { + $oSubFilter = CMDBSearchFilter::unserialize($sFilterShortcut); + $oFilter->AddCondition_PointingTo($oSubFilter, $sExtKeyAttCode); + } + + foreach($aRefedBy as $sForeignClass=>$aExtKeys) + { + foreach($aExtKeys as $sForeignExtKey=>$sFilterShortcut) + { + //MyHelpers::var_dump_html("$sForeignClass / $sForeignExtKey / $sFilterShortcut"); + $oSubFilter = CMDBSearchFilter::unserialize($sFilterShortcut); + //MyHelpers::var_dump_html($oSubFilter); + $oFilter->AddCondition_ReferencedBy($oSubFilter, $sForeignExtKey); + } + } + +// $oFilter->AddCondition_LinkedTo(DBObjectSearch $oLinkFilter, $sExtKeyAttCodeToMe, $sExtKeyAttCodeTarget, DBObjectSearch $oFilterTarget); + + return $oFilter; +} + +function Page3_ViewResults($oPage, $oFilter) +{ + // Output results in various forms... + // + if ($oFilter->IsAny()) + { + $oPage->p("You are considering the ENTIRE set of objects..."); + } + else + { + $oPage->p($oFilter->__DescribeHTML()); + + $oSet = new CMDBObjectSet($oFilter); + $oPage->p("Found ".$oSet->Count()." items"); + + $sFilterPhrase = $oFilter->serialize(); + $oPage->p("See detailed results"); + } +} + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// +// M a i n P r o g r a m +// +/////////////////////////////////////////////////////////////////////////////////////////////////// + + +$oPage->p("

    Advanced search

    "); + + +// Page 1 - Ask class +// +// Page 2 - Class is given, enum existing filters/possible links +// +// Page 3 - Interpret user choices, create a filter and render its string representation +// + +//MyHelpers::arg_dump_html(); +//MyHelpers::var_dump_html($_SESSION); + +if (ReadParam('userconfig', false)) +{ + $sTodo = 'userconfig'; +} +if (ReadParam('makeit', false)) +{ + $sTodo = 'makeit'; +} +else +{ + if (dialogstack::IsDialogStartup()) + { + $sInit = dialogstack::StartDialog(); + $oFilter = CMDBSearchFilter::unserialize($sInit); + $sTodo = 'userconfig'; + } + else + { + $sClass = ReadParam('class', ''); + if (empty($sClass)) + { + $sTodo = 'selectclass'; + } + else + { + $oFilter = MakeFilterFromArgs(); + $sTodo = 'userconfig'; + } + } +} + +switch ($sTodo) +{ +case "selectclass": + Page1_AskClass($oPage); + break; + +case "userconfig": + dialogstack::DeclareCaller("Define filter for ".$oFilter->GetClass()); + $oPage->add(implode(" / ", dialogstack::GetCurrentStack())); + Page2_ConfigFilters($oPage, $oFilter); + break; + +case "makeit": + $oFilter = MakeFilterFromArgs(); + Page3_ViewResults($oPage, $oFilter); + $oPage->add(dialogstack::RenderEndDialogForm(DLGSTACK_OK, "Use filter", $oFilter->serialize())); + $oPage->add(dialogstack::RenderEndDialogForm(DLGSTACK_CANCEL, "Annuler")); + break; + +default: + trigger_error("Wrong value for arg todo ($sTodo)", E_USER_ERROR); +} + +$oPage->output(); + +?> diff --git a/pages/ajax.php b/pages/ajax.php new file mode 100644 index 000000000..1173ddd21 --- /dev/null +++ b/pages/ajax.php @@ -0,0 +1,40 @@ +no_cache(); +$oPage->add("

    Asynchronous versus asynchronous DisplayBlocks

    \n"); + +$oContext = new UserContext(); +$operation = ReadParam('operation', ''); +$sClassName = ReadParam('class', 'bizContact'); +$sOrganizationCode = ReadParam('org', 'ITOP'); + +$oPage->p("[Synchronous] Count of all $sClassName objects for organization '$sOrganizationCode'"); +$oFilter = $oContext->NewFilter($sClassName); +$oFilter ->AddCondition('organization', $sOrganizationCode, '='); +$oBlock = new DisplayBlock($oFilter, 'count', false); +$oBlock->Display($oPage, "block1"); + +$oPage->p("[Asynchronous] All $sClassName objects for organization '$sOrganizationCode'"); +$oFilter = $oContext->NewFilter($sClassName); +$oFilter ->AddCondition('organization', $sOrganizationCode, '='); +$oBlock = new DisplayBlock($oFilter, 'list', true); +$oBlock->Display($oPage, "block2"); + +$oPage->p("[Asynchronous] Details of all $sClassName objects for organization '$sOrganizationCode'"); +$oFilter = $oContext->NewFilter($sClassName); +$oFilter ->AddCondition('organization', $sOrganizationCode, '='); +$oBlock = new DisplayBlock($oFilter, 'details', true); +$oBlock->Display($oPage, "block3"); + +$oPage->output(); +?> diff --git a/pages/ajax.render.php b/pages/ajax.render.php new file mode 100644 index 000000000..176a32128 --- /dev/null +++ b/pages/ajax.render.php @@ -0,0 +1,218 @@ +no_cache(); + +$oContext = new UserContext(); +$operation = utils::ReadParam('operation', ''); +$sFilter = stripslashes(utils::ReadParam('filter', '')); +$sEncoding = utils::ReadParam('encoding', 'serialize'); +$sClass = utils::ReadParam('class', 'bizContact'); +$sStyle = utils::ReadParam('style', 'list'); + +switch($operation) +{ + case 'wizard_helper_preview': + $sJson = utils::ReadParam('json_obj', '', 'post'); + $oWizardHelper = WizardHelper::FromJSON($sJson); + $oObj = $oWizardHelper->GetTargetObject(); + $oObj->DisplayBareDetails($oPage); + break; + + case 'wizard_helper': + $sJson = utils::ReadParam('json_obj', ''); + $oWizardHelper = WizardHelper::FromJSON($sJson); + $oObj = $oWizardHelper->GetTargetObject(); + foreach($oWizardHelper->GetFieldsForDefaultValue() as $sAttCode) + { + $oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode); + $oWizardHelper->SetDefaultValue($sAttCode, $oAttDef->GetDefaultValue()); + } + foreach($oWizardHelper->GetFieldsForAllowedValues() as $sAttCode) + { + $oWizardHelper->SetAllowedValuesHtml($sAttCode, "Possible values ($sAttCode)"); + } + $oPage->add($oWizardHelper->ToJSON()); + break; + + case 'ajax': + if ($sFilter != "") + { + if ($sEncoding == 'sibusql') + { + $oFilter = CMDBSearchFilter::FromSibusQL($sFilter); + } + else + { + $oFilter = CMDBSearchFilter::unserialize($sFilter); + } + $oDisplayBlock = new DisplayBlock($oFilter, $sStyle, false); + $oDisplayBlock->RenderContent($oPage); + } + else + { + $oPage->p("Invalid query (empty filter)."); + } + break; + + case 'details': + $key = utils::ReadParam('id', 0); + $oFilter = $oContext->NewFilter($sClass); + $oFilter->AddCondition('pkey', $key, '='); + $oDisplayBlock = new DisplayBlock($oFilter, 'details', false); + $oDisplayBlock->RenderContent($oPage); + break; + + case 'preview': + $key = utils::ReadParam('id', 0); + $oFilter = $oContext->NewFilter($sClass); + $oFilter->AddCondition('pkey', $key, '='); + $oDisplayBlock = new DisplayBlock($oFilter, 'preview', false); + $oDisplayBlock->RenderContent($oPage); + break; + + case 'pie_chart': + $sGroupBy = utils::ReadParam('group_by', ''); + if ($sFilter != '') + { + if ($sEncoding == 'oql') + { + $oFilter = CMDBSearchFilter::FromOQL($sFilter); + } + else + { + $oFilter = CMDBSearchFilter::unserialize($sFilter); + } + $oDisplayBlock = new DisplayBlock($oFilter, 'pie_chart_ajax', false); + $oDisplayBlock->RenderContent($oPage, array('group_by' => $sGroupBy)); + } + else + { + + $oPage->add("\n3d pie\n."); + } + break; + + case 'open_flash_chart': + $aParams = utils::ReadParam('params', array()); + if ($sFilter != '') + { + if ($sEncoding == 'oql') + { + $oFilter = CMDBSearchFilter::FromOQL($sFilter); + } + else + { + $oFilter = CMDBSearchFilter::unserialize($sFilter); + } + $oDisplayBlock = new DisplayBlock($oFilter, 'open_flash_chart_ajax', false); + $oDisplayBlock->RenderContent($oPage, $aParams); + } + else + { + + $oPage->add("\n3d pie\n."); + } + break; + + case 'modal_details': + $key = utils::ReadParam('id', 0); + $oFilter = $oContext->NewFilter($sClass); + $oFilter->AddCondition('pkey', $key, '='); + $oPage->Add("

    Object Details

    \n"); + $oDisplayBlock = new DisplayBlock($oFilter, 'details', false); + $oDisplayBlock->RenderContent($oPage); + $oPage->Add("\n"); + break; + + case 'ui.linkswidget': + $sClass = utils::ReadParam('sclass', 'bizContact'); + $sAttCode = utils::ReadParam('attCode', 'name'); + $sOrg = utils::ReadParam('org_id', ''); + $sName = utils::ReadParam('q', ''); + $iMaxCount = utils::ReadParam('max', 30); + UILinksWidget::Autocomplete($oPage, $oContext, $sClass, $sAttCode, $sName, $iMaxCount); + break; + + case 'ui.linkswidget.linkedset': + $sClass = utils::ReadParam('sclass', 'bizContact'); + $sJSONSet = stripslashes(utils::ReadParam('sset', '')); + $sExtKeyToMe = utils::ReadParam('sextkeytome', ''); + UILinksWidget::RenderSet($oPage, $sClass, $sJSONSet, $sExtKeyToMe); + break; + + case 'autocomplete': + $key = utils::ReadParam('id', 0); + $sClass = utils::ReadParam('sclass', 'bizContact'); + $sAttCode = utils::ReadParam('attCode', 'name'); + $sOrg = utils::ReadParam('org_id', ''); + $sName = utils::ReadParam('q', ''); + $iMaxCount = utils::ReadParam('max', 30); + $aArgs = array(); + if (!empty($key)) + { + if ($oThis = MetaModel::GetObject($sClass, $key)) + { + $aArgs['*this*'] = $oThis; + } + } + $aAllowedValues = MetaModel::GetAllowedValues_att($sClass, $sAttCode, $aArgs, $sName); + $iCount = 0; + foreach($aAllowedValues as $key => $value) + { + $oPage->add($value."|".$key."\n"); + if ($iCount++) break; + } + break; + + case 'link': + $sClass = utils::ReadParam('sclass', 'logInfra'); + $sAttCode = utils::ReadParam('attCode', 'name'); + //$sOrg = utils::ReadParam('org_id', ''); + $sName = utils::ReadParam('q', ''); + $iMaxCount = utils::ReadParam('max', 30); + $iCount = 0; + $oFilter = $oContext->NewFilter($sClass); + $oFilter->AddCondition($sAttCode, $sName, 'Begins with'); + //$oFilter->AddCondition('org_id', $sOrg); + $oSet = new CMDBObjectSet($oFilter, array($sAttCode => true)); + while( ($iCount < $iMaxCount) && ($oObj = $oSet->fetch()) ) + { + $oPage->add($oObj->GetAsHTML($sAttCode)."|".$oObj->GetKey()."\n"); + $iCount++; + } + break; + + case 'create': + case 'create_menu': + $sClass = utils::ReadParam('class', ''); + $sFilter = utils::ReadParam('filter', ''); + menuNode::DisplayCreationForm($oPage, $sClass, $sFilter); + break; + + case 'combo_options': + $oFilter = CMDBSearchFilter::FromSibusQL($sFilter); + $oSet = new CMDBObjectSet($oFilter); + while( $oObj = $oSet->fetch()) + { + $oPage->add(''); + } + break; + + default: + $oPage->p("Invalid query."); +} +$oPage->output(); +?> diff --git a/pages/audit.php b/pages/audit.php new file mode 100644 index 000000000..6f69cfdbb --- /dev/null +++ b/pages/audit.php @@ -0,0 +1,149 @@ +GetObject('AuditRule', $iRuleId); + $sSibusql = $oRule->Get('query'); + $oRuleFilter = DBObjectSearch::FromSibusQL($sSibusql); + if ($oRule->Get('valid_flag') == 'false') + { + // The query returns directly the invalid elements + $oFilter = $oRuleFilter; + $oFilter->MergeWith($oDefinitionFilter); + $oErrorObjectSet = new CMDBObjectSet($oFilter); + } + else + { + // The query returns only the valid elements, all the others are invalid + $oFilter = $oRuleFilter; + $oErrorObjectSet = new CMDBObjectSet($oFilter); + $aValidIds = array(0); // Make sure that we have at least one value in the list + while($oObj = $oErrorObjectSet->Fetch()) + { + $aValidIds[] = $oObj->GetKey(); + } + $oFilter = $oDefinitionFilter; + $oFilter->AddCondition('pkey', $aValidIds, 'NOTIN'); + $oErrorObjectSet = new CMDBObjectSet($oFilter); + } + return $oErrorObjectSet; +} + +function GetReportColor($iTotal, $iErrors) +{ + $sResult = 'red'; + if ( ($iTotal == 0) || ($iErrors / $iTotal) <= 0.05 ) + { + $sResult = 'green'; + } + else if ( ($iErrors / $iTotal) <= 0.25 ) + { + $sResult = 'orange'; + } + return $sResult; +} + +switch($operation) +{ + case 'errors': + $iCategory = utils::ReadParam('category', ''); + $iRuleIndex = utils::ReadParam('rule', 0); + + $oContext = new UserContext(); + $oAuditCategory = $oContext->GetObject('AuditCategory', $iCategory); + $oDefinitionFilter = DBObjectSearch::FromSibusQL($oAuditCategory->Get('definition_set')); + if (!empty($currentOrganization)) + { + $oDefinitionFilter->AddCondition('org_id', $currentOrganization); + } + $oDefinitionSet = new CMDBObjectSet($oDefinitionFilter); + $oErrorObjectSet = GetRuleResultSet($iRuleIndex, $oDefinitionFilter); + $oAuditRule = $oContext->GetObject('AuditRule', $iRuleIndex); + $oP->add(''); + $oP->p('[Back to audit results]'); + cmdbAbstractObject::DisplaySet($oP, $oErrorObjectSet); + break; + + case 'audit': + default: + $oP->add(''); + $oAuditFilter = new CMDBSearchFilter('AuditCategory'); + $oCategoriesSet = new DBObjectSet($oAuditFilter); + $oP->add("\n"); + $oP->add("\n"); + $oP->add("
    \n"); + $oP->add("\n"); + $oP->add("\n"); + $oP->add("\n"); + $oP->add("\n"); + while($oAuditCategory = $oCategoriesSet->fetch()) + { + $oDefinitionFilter = DBObjectSearch::FromSibusQL($oAuditCategory->Get('definition_set')); + $aObjectsWithErrors = array(); + if (!empty($currentOrganization)) + { + $oDefinitionFilter->AddCondition('org_id', $currentOrganization); + } + $aResults = array(); + $oDefinitionSet = new CMDBObjectSet($oDefinitionFilter); + $iCount = $oDefinitionSet->Count(); + $oRulesFilter = new CMDBSearchFilter('AuditRule'); + $oRulesFilter->AddCondition('category_id', $oAuditCategory->GetKey()); + $oRulesSet = new DBObjectSet($oRulesFilter); + while($oAuditRule = $oRulesSet->fetch() ) + { + $aRow = array(); + $aRow['description'] = $oAuditRule->Get('name'); + if ($iCount == 0) + { + // nothing to check, really ! + $aRow['nb_errors'] = "GetKey()."&rule=".$oAuditRule->GetKey()."\">0"; + $aRow['percent_ok'] = '100.00'; + $aRow['class'] = GetReportColor($iCount, 0); + } + else + { + $oRuleFilter = DBObjectSearch::FromSibusQL($oAuditRule->Get('query')); + $oErrorObjectSet = GetRuleResultSet($oAuditRule->GetKey(), $oDefinitionFilter); + $iErrorsCount = $oErrorObjectSet->Count(); + while($oObj = $oErrorObjectSet->Fetch()) + { + $aObjectsWithErrors[$oObj->GetKey()] = true; + } + $aRow['nb_errors'] = ($iErrorsCount == 0) ? '0' : "GetKey()."&rule=".$oAuditRule->GetKey()."&".$oAppContext->GetForLink()."\">$iErrorsCount"; + $aRow['percent_ok'] = sprintf('%.2f', 100.0 * (($iCount - $iErrorsCount) / $iCount)); + $aRow['class'] = GetReportColor($iCount, $iErrorsCount); + } + $aResults[] = $aRow; + $iTotalErrors = count($aObjectsWithErrors); + $sOverallPercentOk = ($iCount == 0) ? '100.00' : sprintf('%.2f', 100.0 * (($iCount - $iTotalErrors) / $iCount)); + $sClass = GetReportColor($iCount, $iTotalErrors); + + } + $oP->add("\n"); + $oP->add("\n"); + $oP->add("\n"); + foreach($aResults as $aRow) + { + $oP->add("\n"); + $oP->add("\n"); + $oP->add("\n"); + } + } + $oP->add("
    Audit Rule# Objects# Errors% Ok
    ".$oAuditCategory->GetName()."$iCount$iTotalErrors$sOverallPercentOk %
     ".$aRow['description']."".$aRow['nb_errors']."".$aRow['percent_ok']." %
    \n"); + $oP->add("
    \n"); +} + +$oP->output(); +?> diff --git a/pages/csvimport.php b/pages/csvimport.php new file mode 100644 index 000000000..0110e1f53 --- /dev/null +++ b/pages/csvimport.php @@ -0,0 +1,527 @@ + '); + +/////////////////////////////////////////////////////////////////////////////// +// External key/field naming conventions (sharing the naming space with std attributes +/////////////////////////////////////////////////////////////////////////////// + +function IsExtKeyField($sColDesc) +{ + return ($iPos = strpos($sColDesc, EXTKEY_SEP)); +} + +function GetExtKeyFieldCodes($sColDesc) +{ + $iPos = strpos($sColDesc, EXTKEY_SEP); + return array( + substr($sColDesc, 0, $iPos), + substr($sColDesc, $iPos + strlen(EXTKEY_SEP)) + ); +} + +function MakeExtFieldLabel($sClass, $sExtKeyAttCode, $sForeignAttCode) +{ + $oExtKeyAtt = MetaModel::GetAttributeDef($sClass, $sExtKeyAttCode); + $oForeignAtt = MetaModel::GetAttributeDef($oExtKeyAtt->GetTargetClass(), $sForeignAttCode); + + return $oExtKeyAtt->GetLabel().EXTKEY_LABELSEP.$oForeignAtt->GetLabel(); +} + +function MakeExtFieldSelectValue($sAttCode, $sExtAttCode) +{ + return $sAttCode.EXTKEY_SEP.$sExtAttCode; +} + +/////////////////////////////////////////////////////////////////////////////// +/////////////////////////////////////////////////////////////////////////////// + + +function ShowTableForm($oPage, $oCSVParser, $sClass) +{ + $aData = $oCSVParser->ToArray(null, 3); + $aColToRow = array(); + foreach($aData as $aRow) + { + foreach ($aRow as $sFieldId=>$sValue) + { + $aColToRow[$sFieldId][] = $sValue; + } + } + + $aFields = array(); + foreach($oCSVParser->ListFields() as $iFieldIndex=>$sFieldName) + { + $sSelField = ""; + + $sCHECKED = ($sFieldName == "pkey" || MetaModel::IsReconcKey($sClass, $sFoundAttCode)) ? " CHECKED" : ""; + $sSelField .= " "; + + $aFields["field$iFieldIndex"]["label"] = $sSelField; + $aFields["field$iFieldIndex"]["value"] = $aColToRow[$iFieldIndex]; + } + $oPage->details($aFields); +} + + +function ProcessData($oPage, $sClass, $oCSVParser, $aFieldMap, $aIsReconcKey, CMDBChange $oChange = null) +{ + // Note: $oChange can be null, in which case the aim is to check what would be done + + // Setup field mapping: sort out between values and other specific columns + // + $iPKeyId = null; + $aReconcilKeys = array(); + $aAttList = array(); + $aExtKeys = array(); + foreach($aFieldMap as $sFieldId=>$sColDesc) + { + $iFieldId = (int) substr($sFieldId, strlen("field")); + if ($sColDesc == "pkey") + { + // Skip ! + $iPKeyId = $iFieldId; + } + elseif ($sColDesc == "__none__") + { + // Skip ! + } + elseif (IsExtKeyField($sColDesc)) + { + list($sExtKeyAttCode, $sExtReconcKeyAttCode) = GetExtKeyFieldCodes($sColDesc); + $aExtKeys[$sExtKeyAttCode][$sExtReconcKeyAttCode] = $iFieldId; + } + elseif (array_key_exists($sFieldId, $aIsReconcKey)) + { + $aReconcilKeys[$sColDesc] = $iFieldId; + $aAttList[$sColDesc] = $iFieldId; // A reconciliation key is also a field + } + else + { + // $sColDesc is an attribute code + // + $aAttList[$sColDesc] = $iFieldId; + } + } + + // Setup result presentation + // + $aDisplayConfig = array(); + $aDisplayConfig["__RECONCILIATION__"] = array("label"=>"Reconciliation", "description"=>""); + $aDisplayConfig["__STATUS__"] = array("label"=>"Status", "description"=>""); + if (isset($iPKeyId)) + { + $aDisplayConfig["col$iPKeyId"] = array("label"=>"pkey", "description"=>""); + } + foreach($aReconcilKeys as $sAttCode => $iCol) + { + $sLabel = MetaModel::GetAttributeDef($sClass, $sAttCode)->GetLabel(); + $aDisplayConfig["col$iCol"] = array("label"=>"$sLabel", "description"=>""); + } + foreach($aExtKeys as $sAttCode=>$aKeyConfig) + { + $oExtKeyAtt = MetaModel::GetAttributeDef($sClass, $sAttCode); + $sLabel = $oExtKeyAtt->GetLabel(); + $aDisplayConfig[$sAttCode] = array("label"=>"$sLabel", "description"=>""); + foreach ($aKeyConfig as $sForeignAttCode => $iCol) + { + // The foreign attribute is one of our reconciliation key + + $sLabel = MakeExtFieldLabel($sClass, $sAttCode, $sForeignAttCode); + $aDisplayConfig["col$iCol"] = array("label"=>"$sLabel", "description"=>""); + } + } + foreach ($aAttList as $sAttCode => $iCol) + { + $sLabel = MetaModel::GetAttributeDef($sClass, $sAttCode)->GetLabel(); + $aDisplayConfig["col$iCol"] = array("label"=>"$sLabel", "description"=>""); + } + + // Compute the results + // + $aData = $oCSVParser->ToArray(); + + $oBulk = new BulkChange( + $sClass, + $aData, + $aAttList, + array_keys($aReconcilKeys), + $aExtKeys + ); + $aRes = $oBulk->Process($oChange); + + $aResultDisp = array(); // to be displayed + foreach($aRes as $iRow => $aRowData) + { + $aRowDisp = array(); + $aRowDisp["__RECONCILIATION__"] = $aRowData["__RECONCILIATION__"]; + $aRowDisp["__STATUS__"] = $aRowData["__STATUS__"]->GetDescription(); + foreach($aRowData as $sKey => $value) + { + if ($sKey == '__RECONCILIATION__') continue; + if ($sKey == '__STATUS__') continue; + + switch (get_class($value)) + { + case 'CellChangeSpec_Void': + $sClass = ''; + break; + case 'CellChangeSpec_Unchanged': + $sClass = ''; + break; + case 'CellChangeSpec_Modify': + $sClass = 'csvimport_ok'; + break; + case 'CellChangeSpec_Init': + $sClass = 'csvimport_init'; + break; + case 'CellChangeSpec_Issue': + $sClass = 'csvimport_error'; + break; + } + if (empty($sClass)) + { + $aRowDisp[$sKey] = $value->GetDescription(); + } + else + { + $aRowDisp[$sKey] = "
    ".$value->GetDescription()."
    "; + } + } + $aResultDisp[$iRow] = $aRowDisp; + } + $oPage->table($aDisplayConfig, $aResultDisp); +} + +/////////////////////////////////////////////////////////////////////////////// +// Wizard entry points +/////////////////////////////////////////////////////////////////////////////// + +function Do_Welcome($oPage, $sClass) +{ + $sWiztep = "1_welcome"; + $oPage->p("

    Bulk load from CSV data / step 1

    "); + + $sCSVData = utils::ReadPostedParam('csvdata'); + + $oPage->add("
    "); + $oPage->MakeClassesSelect("class", $sClass, 50); + //$oPage->Add(""); + $oPage->add("
    "); + $oPage->add(""); + $oPage->add("
    "); + $oPage->add(""); + $oPage->add("
    \n"); + $oPage->add("
    "); + + // As a help to the end-user, let's display the list of possible fields + // for a class, that can be copied/pasted into the CSV area. + $sCurrentList = ""; + $aHeadersList = array(); + foreach(MetaModel::GetClasses('bizmodel') as $sClassName) + { + $aList = MetaModel::GetZListItems($sClassName, 'details'); + $aHeader = array(); + // $aHeader[] = MetaModel::GetKeyLabel($sClassName); + $aHeader[] = 'pkey'; // Should be what's coded on the line above... but there is a bug + foreach($aList as $sAttCode) + { + $aHeader[] = MetaModel::GetLabel($sClassName, $sAttCode); + } + + $sAttributes = implode(",", $aHeader); + $aHeadersList[$sClassName] = $sAttributes; + + if($sClassName == $sClass) + { + // this class is currently selected + $sCurrentList = $sAttributes; + } + } + // Store all the values in a variable client-side + $aScript = array(); + foreach($aHeadersList as $sClassName => $sAttributes) + { + $aScript[] = "'$sClassName':'$sAttributes'"; + } + $oPage->add("\n"); + + $oPage->add_ready_script("$('#select_class').change( function() {DisplayFields(this.value);} );"); + + $oPage->add("Fields for this object: "); + +} + +function Do_Format($oPage, $sClass) +{ + $oPage->p("

    Bulk load from CSV data / step 2

    "); + $sWiztep = "2_format"; + + $sCSVData = utils::ReadPostedParam('csvdata'); + $oCSVParser = new CSVParser($sCSVData); + $sSep = $oCSVParser->GuessSeparator(); + $iSkip = $oCSVParser->GuessSkipLines(); + + // No data ? + $aData = $oCSVParser->ToArray(null); + $iTarget = count($aData); + if ($iTarget == 0) + { + $oPage->add("Empty data set..."); + $oPage->add("
    "); + $oPage->add(""); + $oPage->add(""); + $oPage->add(""); + $oPage->add("
    "); + } + + // Guess the format : + $oPage->p("Guessed separator: '$sSep' (ASCII=".ord($sSep).")"); + $oPage->p("Guessed # of lines to skip: $iSkip"); + + $oPage->p("Target: $iTarget rows"); + + $oPage->Add("
    "); + ShowTableForm($oPage, $oCSVParser, $sClass); + $oPage->Add(""); + $oPage->Add(""); + $oPage->Add(""); + $oPage->Add(""); + + $oPage->Add(""); + $oPage->add(""); + $oPage->Add(""); + $oPage->Add("
    "); +} + +function DoProcessOrVerify($oPage, $sClass, CMDBChange $oChange = null) +{ + $sCSVData = utils::ReadPostedParam('csvdata'); + $sSep = utils::ReadPostedParam('separator'); + $iSkip = utils::ReadPostedParam('skiplines'); + $aFieldMap = utils::ReadPostedParam('fmap'); + $aIsReconcKey = utils::ReadPostedParam('iskey'); + + $oCSVParser = new CSVParser($sCSVData); + $oCSVParser->SetSeparator($sSep); + $oCSVParser->SetSkipLines($iSkip); + $aData = $oCSVParser->ToArray(null); + $iTarget = count($aData); + + $oPage->p("

    Goal summary

    "); + $oPage->p("Target: $iTarget rows"); + + $aSampleData = $oCSVParser->ToArray(array_keys($aFieldMap), 5); + $aDisplayConfig = array(); + foreach ($aFieldMap as $sFieldId=>$sColDesc) + { + if (array_key_exists($sFieldId, $aIsReconcKey)) + { + $sReconcKey = " [search]"; + } + else + { + $sReconcKey = ""; + } + + if ($sColDesc == "pkey") + { + $aDisplayConfig[$sFieldId] = array("label"=>"Private key $sReconcKey", "description"=>"blah pkey"); + } + elseif ($sColDesc == "__none__") + { + // Skip ! + } + else if (MetaModel::IsValidAttCode($sClass, $sColDesc)) + { + $sAttCode = $sColDesc; + $sLabel = MetaModel::GetAttributeDef($sClass, $sAttCode)->GetLabel(); + $aDisplayConfig[$sFieldId] = array("label"=>"$sLabel$sReconcKey", "description"=>""); + } + elseif (IsExtKeyField($sColDesc)) + { + list($sExtKeyAttCode, $sForeignAttCode) = GetExtKeyFieldCodes($sColDesc); + $aDisplayConfig[$sFieldId] = array("label"=>MakeExtFieldLabel($sClass, $sExtKeyAttCode, $sForeignAttCode), "description"=>""); + } + else + { + // ??? + $aDisplayConfig[$sFieldId] = array("label"=>"-?-?-$sColDesc-?-?-", "description"=>""); + } + } + $oPage->table($aDisplayConfig, $aSampleData); + + if ($oChange) + { + $oPage->p("

    Processing...

    "); + } + else + { + $oPage->p("

    Check...

    "); + } + ProcessData($oPage, $sClass, $oCSVParser, $aFieldMap, $aIsReconcKey, $oChange); + + $oPage->add("
    "); + $oPage->add(""); + $oPage->add(""); + $oPage->add(""); + $oPage->add(""); + $oPage->add_input_hidden("fmap", $aFieldMap); + $oPage->add_input_hidden("iskey", $aIsReconcKey); +} + +function Do_Verify($oPage, $sClass) +{ + $oPage->p("

    Bulk load from CSV data / step 3

    "); + $sWiztep = "3_verify"; + + DoProcessOrVerify($oPage, $sClass, null); + // FORM started by DoProcessOrVerify... + + $oPage->add(""); + $oPage->add(""); + $oPage->add(""); + $oPage->add("
    "); +} + +function Do_Execute($oPage, $sClass) +{ + $oPage->p("

    Bulk load from CSV data / step 4

    "); + $sWiztep = "4_execute"; + + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + $oMyChange->Set("userinfo", "CSV Import"); + $iChangeId = $oMyChange->DBInsert(); + + DoProcessOrVerify($oPage, $sClass, $oMyChange); + + // FORM started by DoProcessOrVerify... + $oPage->add(""); + $oPage->add(""); + $oPage->add(""); +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// +// M a i n P r o g r a m +// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +$sFromWiztep = utils::ReadPostedParam('fromwiztep', ''); +$sClass = utils::ReadPostedParam('class', ''); +$sTodo = utils::ReadPostedParam('todo', ''); + +switch($sFromWiztep) +{ + case '': + Do_Welcome($oPage, $sClass); + break; + + case '1_welcome': + if ($sTodo == "Next") Do_Format($oPage, $sClass); + else trigger_error("Wrong argument todo='$sTodo'", E_USER_ERROR); + break; + + case '2_format': + if ($sTodo == "Next") Do_Verify($oPage, $sClass); + else Do_Welcome($oPage, $sClass); + break; + + case '3_verify': + if ($sTodo == "Next") Do_Execute($oPage, $sClass); + else Do_Format($oPage, $sClass); + break; + + case '4_execute': + if ($sTodo == "Next") trigger_error("Wrong argument todo='$sTodo'", E_USER_ERROR); + else Do_Verify($oPage, $sClass); + break; + + default: + trigger_error("Wrong argument fromwiztep='$sFromWiztep'", E_USER_ERROR); +} + +$oPage->output(); +?> diff --git a/pages/data_generator.php b/pages/data_generator.php new file mode 100644 index 000000000..351269d18 --- /dev/null +++ b/pages/data_generator.php @@ -0,0 +1,321 @@ +\n"; + echo "DEBUG: \$_SERVER\n"; + print_r($_SERVER); + echo "\n"; + exit; +} +if (!UserRights::Login($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW'])) +{ + header('WWW-Authenticate: Basic realm="iTop"'); + header('HTTP/1.0 401 Unauthorized'); + echo 'Authentication failed !'; + exit; +} + +$oPage = new iTopWebPage("iTop Data Generator", $currentOrganization); +$oPage->no_cache(); + +// From now on the context is limited to the the selected organization ?? + +// Retrieve the root node for the menu +$oSearchFilter = $oContext->NewFilter("menuNode"); +$oSearchFilter->AddCondition('parent_id', 0, '='); +// There may be more criteria added later to have a specific menu based on the user's profile +$oSet = new CMDBObjectSet($oSearchFilter, array('rank' => true)); +while ($oRootMenuNode = $oSet->Fetch()) +{ + $oRootMenuNode->DisplayMenu($oPage, $oContext, $iActiveNodeId, null, $oAppContext->GetAsHash()); +} +/** + * The (ordered) list of classes for which to generate objects + * + * Each class in this list must implement a non-static Generate(cmdbDataGenerator $oGenerator) method + */ +//$aClassesToGenerate = array('bizOrganization' /* This one is special and must go first */, 'bizService', 'bizContact', 'bizPC', 'bizNetworkDevice'); +$aClassesToGenerate = array('bizOrganization' /* This one is special and must go first */, 'bizLocation', 'bizPC', 'bizNetworkDevice', 'bizPerson', 'bizIncidentTicket', 'bizInfraGroup', 'bizInfraInfra'); + +///////////////////////////////////////////////////////////////////////////////////// +// Actual actions of the page +///////////////////////////////////////////////////////////////////////////////////// + +/** + * Populate an organization with objects of each class listed in the (global) $aClassesToGenerate array + * + * @param web_page $oPage The object used for the HTML output + * @param cmdbGenerator $oGenerator The object used for the generation of the objects + * @param string $sSize An enum specifying (roughly) how many objects of each class to create: one of 'small', 'medium', 'big', 'huge' or 'max' + */ +function PopulateOrganization(CMDBChange $oMyChange, web_page $oPage, cmdbDataGenerator $oGenerator, $sSize = 'small') +{ + global $aClassesToGenerate; + + for ($i=1 /* skip the first one (i=0) which is the org itself */; $iGenerate($oGenerator); + // By rom + // $oObject->DBInsert(); + $oObject->DBInsertTracked($oMyChange); + + //$oObject->DisplayDetails($oPage); + } + } + $oPage->p("$nbObjects $sClass objects created."); + } +} + +/** + * Delete an organization and all the instances of 'Object' belonging to this organization + * + * @param web_page $oPage The object used for the HTML output + * @param string $sOrganizationCode The code (pkey) of the organization to delete + */ +function DeleteOrganization($oMyChange, $oPage, $sOrganizationCode) +{ + $oOrg = MetaModel::GetObject('bizOrganization', $sOrganizationCode); + if ($oOrg == null) + { + $oPage->p("Organization '$sOrganizationCode' already deleted!!"); + } + else + { + // Delete all the objects linked to this organization + $oFilter = new CMDBSearchFilter('logRealObject'); + $oFilter->AddCondition('organization', $sOrganizationCode, '='); + $oSet = new CMDBObjectSet($oFilter); + $countDeleted = $oSet->Count(); + MetaModel::BulkDeleteTracked($oMyChange, $oFilter); // Should do a one by one delete to let the objects do their own cleanup ! + $oPage->p("$countDeleted object(s) deleted!!"); + + $oOrg->DBDeleteTracked($oMyChange); + $oPage->p("Ok, organization '$sOrganizationCode' deleted!"); + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// M a i n P r o g r a m +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +$operation = utils::ReadParam('operation', ''); + +$oPage->add("
    \n"); +$oPage->add("
    \n"); + +switch($operation) +{ + + case 'specify_generate': + // Display a form to specify what to generate + $oPage->p("iTop Data Generator\n"); + $oPage->add("
    \n"); + $oPage->add("Number of organizations to generate: \n"); + $oPage->add(" (max ".count($aCompanies).") \n"); + $oPage->add("Size of the organizations\n"); + $oPage->add("\n"); + $oPage->add("\n"); + $oPage->add("\n"); + $oPage->add("
    \n"); + break; + + case 'generate': + // perform the actual generation + $iOrgCount = utils::ReadParam('org_count', 0); + $sOrgSize = utils::ReadParam('org_size', 'small'); + // By rom + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + $oMyChange->Set("userinfo", "Made by data generator ($iOrgCount orgs of size '$sOrgSize')"); + $oMyChange->DBInsert(); + while($iOrgCount > 0) + { + set_time_limit(5*60); // let it run for 5 minutes for each organization + $oGenerator = new cmdbDataGenerator(); + // Create the new organization + $oOrg = MetaModel::NewObject('bizOrganization'); + $oOrg->Generate($oGenerator); + + // By rom + // $oOrg->DBInsert(); + $oOrg->DBInsertTracked($oMyChange); + $oGenerator->SetOrganizationId($oOrg->GetKey()); + + $oPage->p("Organization '".$oOrg->GetKey()."' created\n"); + $oOrg->DisplayDetails($oPage); + $oPage->add("
    \n"); + PopulateOrganization($oMyChange, $oPage, $oGenerator, $sOrgSize); + $oPage->add("
    \n"); + unset($oGenerator); + $iOrgCount--; + } + break; + + case 'specify_update': + // Specify which organization to update + $oPage->add("
    \n"); + $oPage->add("Select the organization to update: \n"); + $oPage->add("\n"); + $oPage->add("\n"); + $oPage->p(""); + $oPage->add("Quantity of of objects to add in each class: \n"); + $oPage->add("\n"); + $oPage->p(""); + $oPage->add("  \n"); + $oPage->add("\n"); + $oPage->add("
    \n"); + break; + + case 'update': + // perform the actual update + set_time_limit(5*60); // let it run for 5 minutes + $sOrganizationCode = utils::ReadParam('org', ''); + $sOrgSize = utils::ReadParam('org_size', 'small'); + if ($sOrganizationCode == '') + { + $oPage->p("Error: please specify an organization (org)."); + } + else + { + // By rom + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + $oMyChange->Set("userinfo", "Made by data generator (update org '$sOrganizationCode', size '$sOrgSize')"); + $oMyChange->DBInsert(); + + $oPage->p("Organization '$sOrganizationCode' updated."); + $oGenerator = new cmdbDataGenerator($sOrganizationCode); + PopulateOrganization($oMyChange, $oPage, $oGenerator, $sOrgSize); + } + break; + + case 'specify_delete': + // Select an organization to be deleted + $oPage->add("
    \n"); + $oPage->add("Select the organization to delete: \n"); + $oPage->add("\n"); + $oPage->add("\n"); + $oPage->p(""); + $oPage->add("  \n"); + $oPage->add("\n"); + $oPage->add("
    \n"); + break; + + case 'confirm_delete': + // confirm the dangerous action + $sOrganizationCode = ReadParam('org', ''); + $oPage->p("iTop Data Generator\n"); + $oPage->p("Warning: you are about to delete the organization '$sOrganizationCode' and all its related objects\n"); + $oPage->add("
    \n"); + $oPage->add("\n"); + $oPage->add("\n"); + $oPage->add("  \n"); + $oPage->add("\n"); + $oPage->add("
    \n"); + break; + + case 'delete': + // perform the actual deletion + $sOrganizationCode = ReadParam('org', ''); + if ($sOrganizationCode == '') + { + $oPage->p("Error: please specify an organization (org)."); + } + else + { + // By rom + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + $oMyChange->Set("userinfo", "Made by data generator (delete org '$sOrganizationCode'"); + $oMyChange->DBInsert(); + + $oPage->p("Deleting '$sOrganizationCode'\n"); + DeleteOrganization($oMyChange, $oPage, $sOrganizationCode); + } + break; + + // display the menu of actions + case 'menu': + default: + $oPage->p("Data Generator Menu"); + $oPage->p("Generate one or more organizations"); + $oPage->p("Add more objects into an organization"); + $oPage->p("Delete an organization"); +} +$oPage->add("
    \n"); +$oPage->add("
    \n"); + +$oPage->p("Return to the data generator menu"); + +$oPage->output(); +?> diff --git a/pages/db_importer.php b/pages/db_importer.php new file mode 100644 index 000000000..7d7df9894 --- /dev/null +++ b/pages/db_importer.php @@ -0,0 +1,171 @@ +add('
    '); + $oP->add('

    Creation of an archive for the business model: '.$sBizModel.'

    '); + $oP->p('Title of the archive: '); + $oP->p('Description of this archive: '); + $oP->p('Select the packages you want to include into this archive (When restoring the archive you will prompted to pick a package):'); + $oP->p(' The full database (schema + data).'); + $oP->p(' Only the schema but of the complete database.'); + $oP->p(' The complete business model (all the tables used by the business model, schema + data).'); + $oP->p(' Only the schema of the business model.'); + $oP->add(''); + $oP->add(''); + $oP->add(''); + $oP->add($oAppContext->GetForForm()); + $oP->add('
    '); + $oP->p('Back to menu'); + break; + + case 'do_create': + $sTitle = utils::ReadParam('title', 'Unknown archive'); + $sDescription = utils::ReadParam('description', 'No description provided for this archive.'); + $bfullDump = utils::ReadParam('full', false); + $bfullSchemaDump = utils::ReadParam('full_schema', false); + $bBizDump = utils::ReadParam('biz', false); + $bBizSchemaDump = utils::ReadParam('biz_schema', false); + $sArchiveFile = '../tmp/archive1.zip'; + + $oArchive = new iTopArchive($sArchiveFile, iTopArchive::create); + $oArchive->SetTitle($sTitle); + $oArchive->SetDescription($sDescription); + if ($bfullDump) + { + $oArchive->AddDatabaseDump("Full Database Dump", "Choose this option to completely reload your database. All current data will be deleted and replaced by the backup", "full-db.sql", 'itop', array(), false); + } + + if ($bfullSchemaDump) + { + $oArchive->AddDatabaseDump("Full Schema Dump", "Choose this option to completely wipe out your database and start from an empty database", "full-schema.sql", 'itop', array(), true); + } + + if ($bBizDump || $bBizSchemaDump) + { + // compute the list of the tables involved in the business model + $aBizTables = array(); + foreach(MetaModel::GetClasses('bizmodel') as $sClass) + { + $sTable = MetaModel::DBGetTable($sClass); + $aBizTables[$sTable] = $sTable; + } + unset($aBizTables['']); + if ($bfullDump) + { + $oArchive->AddDatabaseDump("Full Business Model Dump", "Choose this option to completely reload the business model part of your database. All current business data will be deleted and replaced by the backup. Application data (like menus...) are preserved.", "biz-db.sql", 'itop', $aBizTables, false); + } + + if ($bfullSchemaDump) + { + $oArchive->AddDatabaseDump("Full Business Model Schema Dump", "Choose this option to wipe out the business data and start from an empty database. All current business data will be deleted. Application data (like menus...) are preserved.", "biz-schema.sql", 'itop', $aBizTables, true); + } + } + $oArchive->WriteCatalog(); + $oP->p("The archive '$sTitle' has been created in $sArchiveFile."); + $oP->p('Back to menu'); + break; + + case 'select_archive': + $sArchivesDir = '../tmp'; + $oP->add('
    '); + $oP->add('

    Importation of an archive

    '); + $oP->p('Select the archive you want to import:'); + $aArchives = array(); + if ($handle = opendir($sArchivesDir)) + { + while (false !== ($sFileName = readdir($handle))) + { + if (strtolower(substr($sFileName, -3, 3)) == 'zip') + { + $oArchive = new iTopArchive('../tmp/'.$sFileName, iTopArchive::read); + if ($oArchive->IsValid()) + { + $oArchive->ReadCatalog(); + $aArchives['../tmp/'.$sFileName] = $oArchive->GetTitle(); + } + } + } + closedir($handle); + } + foreach($aArchives as $sFileName => $sTitle) + { + $oP->p(''.$sTitle); + } + $oP->add(''); + $oP->add(''); + $oP->add(''); + $oP->add($oAppContext->GetForForm()); + $oP->add('
    '); + $oP->p("(Archives are searched into the directory: $sArchivesDir.)"); + $oP->p('Cancel'); + break; + + case 'select_package': + $sArchiveFile = utils::ReadParam('archive_file', ''); + $oArchive = new iTopArchive($sArchiveFile, iTopArchive::read); + $oArchive->ReadCatalog(); + $oP->add('
    '); + $oP->add('

    Selection of a package inside '.$oArchive->GetTitle().'

    '); + $oP->p('Select the package you want to apply:'); + $aPackages = $oArchive->GetPackages(); + foreach($aPackages as $sPackageName => $aPackage) + { + $oP->p(''.$aPackage['title']); + $oP->p($aPackage['description']); + } + $oP->add(''); + $oP->add(''); + $oP->add(''); + $oP->add(''); + $oP->add($oAppContext->GetForForm()); + $oP->add('
    '); + $oP->p('Cancel'); + break; + + case 'import_package': + $sArchiveFile = utils::ReadParam('archive_file', ''); + $sPackageName = utils::ReadParam('package_name', ''); + $oArchive = new iTopArchive($sArchiveFile, iTopArchive::read); + $oArchive->ReadCatalog(); + $oP->add('

    Applying the package '.$sPackageName.'

    '); + if($oArchive->ImportSQL($sPackageName)) + { + $oP->p('Done.'); + } + else + { + $oP->p('Sorry, an error occured while applying the package...'); + } + $oP->p('Apply another package from the same archive'); + $oP->p('Select another archive'); + $oP->p('Back to the menu'); + break; + + case 'menu': + default: + $oP->add('

    Database backup & restore

    '); + $oP->add('

    Select one of the actions below:

    '); + $oP->add('

    Export the database to an archive

    '); + $oP->add('

    Reload the database from an archive

    '); +} + +$oP->output(); +?> diff --git a/pages/graphviz.php b/pages/graphviz.php new file mode 100644 index 000000000..32181c4ce --- /dev/null +++ b/pages/graphviz.php @@ -0,0 +1,82 @@ +p("no lifecycle for this class"); + } + else + { + $aStates = MetaModel::EnumStates($sClass); + $aStimuli = MetaModel::EnumStimuli($sClass); + $sDotFileContent .= "digraph finite_state_machine { + rankdir=LR; + size=\"12,12\" + node [ fontname=Verdana ]; + edge [ fontname=Verdana ]; +"; + $aStatesLinks = array(); + foreach ($aStates as $sStateCode => $aStateDef) + { + $aStatesLinks[$sStateCode] = array('in' => 0, 'out' => 0); + } + + foreach ($aStates as $sStateCode => $aStateDef) + { + $sStateLabel = $aStates[$sStateCode]['label']; + $sStateDescription = $aStates[$sStateCode]['description']; + foreach(MetaModel::EnumTransitions($sClass, $sStateCode) as $sStimulusCode => $aTransitionDef) + { + $aStatesLinks[$sStateCode]['out']++; + $aStatesLinks[$aTransitionDef['target_state']]['in']++; + $sStimulusLabel = $aStimuli[$sStimulusCode]->Get('label'); + $sTargetStateLabel = $aStates[$aTransitionDef['target_state']]['label']; + $sDotFileContent .= "\t$sStateCode -> {$aTransitionDef['target_state']} [ label=\"$sStimulusLabel\"];\n"; + } + } + foreach($aStates as $sStateCode => $aStateDef) + { + $sStateLabel = str_replace(' ', '\n', $aStates[$sStateCode]['label']); + if ( ($aStatesLinks[$sStateCode]['in'] == 0) || ($aStatesLinks[$sStateCode]['out'] == 0)) + { + $sDotFileContent .= "\t$sStateCode [ shape=doublecircle,label=\"$sStateLabel\"];\n"; + } + else + { + $sDotFileContent .= "\t$sStateCode [ shape=circle,label=\"$sStateLabel\"];\n"; + } + } + $sDotFileContent .= "}\n"; + } + return $sDotFileContent; +} + +$sClass = utils::ReadParam('class', 'bizIncidentTicket'); +$sDir = dirname(__FILE__); +$sImageFilePath = realpath($sDir."/../images/lifecycle/$sClass.png"); +if (!file_exists($sImageFilePath)) +{ + // create the file with Graphviz + $sDotDescription = GraphvizLifecycle($sClass); + $sDotFilePath = $sDir."/tmp-lifecycle.dot"; + $rFile = fopen($sDotFilePath, "w"); + fwrite($rFile, $sDotDescription); + fclose($rFile); + exec("/iTop/Graphviz/bin/dot.exe -Tpng < $sDotFilePath > $sImageFilePath"); +} + +header('Content-type: image/png'); +echo file_get_contents($sImageFilePath); +?> diff --git a/pages/incident.php b/pages/incident.php new file mode 100644 index 000000000..bea0a8951 --- /dev/null +++ b/pages/incident.php @@ -0,0 +1,757 @@ +m_sCurrentStep = $sStep; + } + + protected function GetFields($sStep = '') + { + if ($sStep == '') + { + $sStep = $this->m_sCurrentStep; + } + return $this->m_aSteps[$sStep]; + } + + protected function AddContextToForm(web_page $oPage) + { + // Store as hidden fields in the page all the variables from the previous steps + foreach($this->m_aSteps as $sStep => $aFields) + { + if ($sStep == $this->m_sCurrentStep) continue; + foreach($aFields as $sAttName => $sFieldName) + { + $oPage->add("\n"); + } + } + } + + function GetObjectPicker(web_page $oPage, $sTitle, $sFieldName, $sClass) + { + $sScript = +<< 0) + { + aRelatedObjectIds = sRelatedObjectIds.split(' '); + } + else + { + aRelatedObjectIds = new Array(); + aRelatedObjectIds[0] = 0; + } + var sibusql = sClass+": pkey IN {" + aRelatedObjectIds.join(", ") + "}"; + $.get("ajax.render.php?filter=" + sibusql + "&style=list&encoding=sibusql", + { operation: "ajax" }, + function(data){ + $("#related_objects").empty(); + $("#related_objects").append(data); + $("#related_objects").removeClass("loading"); + }); + } + + function AddObject(sClass) + { + var sRelatedObjectIds = new String($('#related_object_ids').val()); + var sCurrentObjectId = new String($('#ac_current_object_id').val()); + if (sRelatedObjectIds.length > 0) + { + aRelatedObjectIds = sRelatedObjectIds.split(' '); + } + else + { + aRelatedObjectIds = new Array(); + } + // To do: check if the ID is not already in the list... + aRelatedObjectIds[aRelatedObjectIds.length] = sCurrentObjectId; + // Update the form & reload the list + $('#related_object_ids').val(aRelatedObjectIds.join(' ')); + UpdateObjectList(sClass); + } + + function ManageObjects(sTitle, sClass, sInputId) + { + $('#Manage_DlgTitle').text(sTitle); + sObjList = new String($('#'+sInputId).val()); + if (sObjList == '') + { + sObjList = new String('0'); + } + var aObjList = sObjList.split(' '); + Manage_LoadSelect('selected_objects', sClass+': pkey IN {' + aObjList.join(', ') + '}'); + Manage_LoadSelect('available_objects', sClass); + $('#ManageObjectsDlg').jqmShow(); + } + + function Manage_LoadSelect(sSelectedId, sFilter) + { + $('#'+sSelectedId).addClass('loading'); + $.get('ajax.render.php?filter=' + sFilter, + { operation: 'combo_options' }, + function(data){ + $('#'+sSelectedId).empty(); + $('#'+sSelectedId).append(data); + $('#'+sSelectedId).removeClass('loading'); + } + ); + } + + function Manage_SwapSelectedObjects(oSourceSelect, oDestinationSelect) + { + for (i=oSourceSelect.length-1;i>=0;i--) // Count down because we are removing the indexes from the combo + { + if (oSourceSelect.options[i].selected) + { + var newOption = document.createElement('option'); + newOption.text = oSourceSelect.options[i].text; + newOption.value = oSourceSelect.options[i].value; + oDestinationSelect.add(newOption, null); + oSourceSelect.remove(i); + } + } + Manage_UpdateButtons(); + } + + function Manage_UpdateButtons() + { + var oSrc = document.getElementById('available_objects'); + var oAddBtn = document.getElementById('btn_add_objects') + var oDst = document.getElementById('selected_objects'); + var oRemoveBtn = document.getElementById('btn_remove_objects') + if (oSrc.selectedIndex == -1) + { + oAddBtn.disabled = true; + } + else + { + oAddBtn.disabled = false; + } + if (oDst.selectedIndex == -1) + { + oRemoveBtn.disabled = true; + } + else + { + oRemoveBtn.disabled = false; + } + } + + function Manage_AddObjects() + { + var oSrc = document.getElementById('available_objects'); + var oDst = document.getElementById('selected_objects'); + Manage_SwapSelectedObjects(oSrc, oDst); + } + + function Manage_RemoveObjects() + { + var oSrc = document.getElementById('selected_objects'); + var oDst = document.getElementById('available_objects'); + Manage_SwapSelectedObjects(oSrc, oDst); + } + + function Manage_Ok(sClass) + { + var objectsToAdd = document.getElementById('selected_objects'); + var aSelectedObjects = new Array(); + for (i=0; i

    Selected Objects

    + + + + + + + + + +
    +

    Selected objects:

    + +

    +
    +

    +

    +
    +

    Available objects:

    + +

    +
    +       +
    +EOF; + $sHTML = ' + +   +  '; + $sHTML .= ''; + $sHTML .= '
    '.$sManageObjectsDlg.'
    '; + $oPage->add_script($sScript); + $oPage->add_ready_script("\$('#current_object_id').autocomplete('./ajax.render.php', { minChars:3, onItemSelect:selectItem, onFindValue:findValue, formatItem:formatItem, autoFill:true, keyHolder:'#ac_current_object_id', extraParams:{operation:'link', sclass:'$sClass', attCode:'name'}});"); + $oPage->add_ready_script("$('#ManageObjectsDlg').jqm({overlay:70, modal:true, toTop:true});"); // jqModal Window + $oPage->add_ready_script("UpdateObjectList('$sClass');"); + return $sHTML; + } + + function DisplayObjectPickerList(web_page $oPage, $sClass) + { + $oFilter = new CMDBSearchFilter($sClass); + $oFilter->AddCondition('pkey', array(0), 'IN'); + //$oPage->p($oFilter->__DescribeHTML()); + $oBlock = new DisplayBlock($oFilter, 'list', true /* Asynchronous */); + $oBlock->Display($oPage, 'related_objects'); + } +} + +class IncidentCreationWizard extends DialogWizard +{ + public function __construct($sStep) + { + parent::__construct($sStep); + $this->m_aSteps = + array( + '1' => array('title' => 'attr_title', 'customer_id' => 'attr_customer_id', 'initial_situation' => 'attr_initial_situation', 'severity' => 'attr_severity', 'impact' => 'attr_impact', 'workgroup_id' => 'attr_workgroup_id', 'action_log' => 'attr_action_log'), + '2' => array('impacted_infra_ids' => 'impacted_infra_ids'), + '3' => array('additional_impacted_object_ids' => 'additional_impacted_object_ids'), + '4' => array('related_incident_ids' => 'related_incident_ids'), + '5' => array('contact_ids' => 'contact_ids'), + ); + } + + protected function AddContextToForm($oPage) + { + parent::AddContextToForm($oPage); + $oPage->add("\n"); + $oPage->add("m_sNextStep."\" />\n"); + } + + public function DisplayNewTicketForm(web_page $oPage) + { + assert($this->m_sCurrentStep == '1'); + $this->m_sNextStep = '2'; + $aFields = $this->GetFields(); + + $oPage->add('
    '); + $aDetails = array(); + $aAttributesDef = MetaModel::ListAttributeDefs('bizIncidentTicket'); + foreach($aFields as $sAttCode => $sFieldName) + { + $oAttDef = $aAttributesDef[$sAttCode]; + $sHTMLValue = cmdbAbstractObject::GetFormElementForField($oPage, 'bizIncidentTicket', $sAttCode, $oAttDef); + $aDetails[] = array('label' => $oAttDef->GetLabel().' *', 'value' => $sHTMLValue); + } + $oPage->details($aDetails); + $this->AddContextToForm($oPage); + $oPage->add("    \n"); + $oPage->add("\n"); + $oPage->add('
    '); + } + + public function DisplayImpactedInfraForm(web_page $oPage) + { + assert($this->m_sCurrentStep == '2'); + $this->m_sNextStep = '3'; + $oPage->add('
    '); + $aDetails = array(); + $sHTML = $this->GetObjectPicker($oPage, 'Impacted Infrastructure', 'impacted_infra_ids', 'logInfra'); + $aDetails[] = array('label' => 'Impacted element:', 'value' => $sHTML); + $oPage->details($aDetails); + $this->DisplayObjectPickerList($oPage, 'logInfra'); + $this->AddContextToForm($oPage); + $oPage->add("    \n"); + $oPage->add("\n"); + $oPage->add('
    '); + } + + public function DisplayAdditionalImpactedObjectForm(web_page $oPage) + { + assert($this->m_sCurrentStep == '3'); + $this->m_sNextStep = '4'; + $sImpactedInfraIds = Utils::ReadParam('impacted_infra_ids'); + + $sImpactedInfraIds = Utils::ReadParam('impacted_infra_ids', ''); + if (!empty($sImpactedInfraIds)) + { + $oPage->p('Impacted Infrastructure:'); + $oFilter = new CMDBSearchFilter('logRealObject'); + $oFilter->AddCondition('pkey', explode(' ', $sImpactedInfraIds), 'IN'); + $oBlock = new DisplayBlock($oFilter, 'list', false /* Synchronous */); + $oBlock->Display($oPage, 'impacted_infra'); + } + + $aImpactedInfraIds = explode(' ', $sImpactedInfraIds); + $oInfraSet = CMDBObjectSet::FromScratch('logRealObject'); + foreach($aImpactedInfraIds as $id) + { + $oObj = MetaModel::GetObject('logRealObject', $id); + $oInfraSet->AddObject($oObj); + } + $aImpactedObject = $oInfraSet->GetRelatedObjects('impacts'); + $aAdditionalIds = array(); + foreach($aImpactedObject as $sRootClass => $aObjects) + { + foreach($aObjects as $oObj) + { + $aAdditionalIds[] = $oObj->GetKey(); + } + } + $sAdditionalIds = implode(' ', $aAdditionalIds); + $oPage->add_ready_script('$("#related_object_ids").val("'.$sAdditionalIds.'");'); + + $oPage->p('Additional Impact Computed:'); + $this->DisplayObjectPickerList($oPage, 'logRealObject'); + $oPage->add('
    '); + $aDetails = array(); + $sHTML = $this->GetObjectPicker($oPage, 'Additional Impacted Infrastructure', 'additional_impacted_object_ids', 'logRealObject'); + $aDetails[] = array('label' => 'Impacted element:', 'value' => $sHTML); + $oPage->details($aDetails); + $this->AddContextToForm($oPage); + $oPage->add("    \n"); + $oPage->add("\n"); + $oPage->add('
    '); + } + + public function DisplayRelatedTicketsForm(web_page $oPage) + { + assert($this->m_sCurrentStep == '4'); + $this->m_sNextStep = '5'; + $oRelatedTicketsFilter = new DBObjectSearch('bizIncidentTicket'); + $sImpactedInfraIds = Utils::ReadParam('impacted_infra_ids', ''); + $sAdditionalImpactedObjectIds = Utils::ReadParam('additional_impacted_object_ids', ''); + $sIds = trim($sImpactedInfraIds.' '.$sAdditionalImpactedObjectIds); + $aTicketIds = array(); + if (!empty($sIds)) + { + $aIds = explode(' ', $sIds); + $sSibusQL = "bizIncidentTicket: PKEY IS ticket_id IN (lnkInfraTicket: infra_id IN (logRealObject: pkey IN {".implode(',', $aIds)."}))"; + $oTicketSearch = DBObjectSearch::FromSibusQL($sSibusQL); + $oRelatedTicketSet = new DBObjectSet($oTicketSearch); + while ($oTicket = $oRelatedTicketSet->Fetch()) + { + $aTicketIds[] = $oTicket->GetKey(); + } + } + + $sTicketIds = implode(' ', $aTicketIds); + $oPage->add_ready_script('$("#related_object_ids").val("'.$sTicketIds.'");'); + $oPage->p('Potentially related incidents:'); + $this->DisplayObjectPickerList($oPage, 'bizIncidentTicket'); + + $oPage->add('
    '); + $sHTML = $this->GetObjectPicker($oPage, 'Related Incidents', 'related_incident_ids', 'bizIncidentTicket'); + $aDetails[] = array('label' => 'Related Incident:', 'value' => $sHTML); + $oPage->details($aDetails); + $this->AddContextToForm($oPage); + $oPage->add("    \n"); + $oPage->add("\n"); + $oPage->add('
    '); + } + + public function DisplayContactsToNotifyForm(web_page $oPage) + { + assert($this->m_sCurrentStep == '5'); + $this->m_sNextStep = '6'; + $oPage->add('
    '); + $sHTML = $this->GetObjectPicker($oPage, 'Contacts to notify', 'contact_ids', 'bizContact'); + $aDetails[] = array('label' => 'Additional contact:', 'value' => $sHTML); + $oPage->details($aDetails); + $this->DisplayObjectPickerList($oPage, 'bizContact'); + $this->AddContextToForm($oPage); + $oPage->add("    \n"); + $oPage->add("\n"); + $oPage->add('
    '); + } + + function DisplayFinalForm(web_page $oPage) + { + $oAppContext = new ApplicationContext(); + assert($this->m_sCurrentStep == '6'); + $this->m_sNextStep = '7'; + + $aDetails = array(); + $aAttributesDef = MetaModel::ListAttributeDefs('bizIncidentTicket'); + $aFields = $this->GetFields('1'); + foreach($aFields as $sAttCode => $sFieldName) + { + $oAttDef = $aAttributesDef[$sAttCode]; + $sValue = Utils::ReadParam($sFieldName, ''); + if ($oAttDef->IsExternalKey() && isset($sValue) && ($sValue != 0)) + { + $oTargetObj = MetaModel::GetObject($oAttDef->GetTargetClass(), $sValue); + if (!is_object($oTargetObj)) + { + trigger_error("Houston: could not find ".$oAttDef->GetTargetClass()."::$sValue"); + } + $sPage = cmdbAbstractObject::ComputeUIPage($oAttDef->GetTargetClass()); + $sHint = htmlentities($oAttDef->GetTargetClass()."::".$sValue); + $sHTMLValue = "GetTargetClass()."&id=$sValue&".$oAppContext->GetForLink()."\" title=\"$sHint\">".$oTargetObj->GetName().""; + } + else + { + $sHTMLValue = $oAttDef->GetAsHTML($sValue); + } + $aDetails[] = array('label' => $oAttDef->GetLabel(), 'value' => $sHTMLValue); + } + $oPage->details($aDetails); + + $oPage->AddTabContainer('LinkedObjects'); + $oPage->SetCurrentTabContainer('LinkedObjects'); + + $sImpactedInfraIds = Utils::ReadParam('impacted_infra_ids', ''); + $sImpactedInfraIds .= ' '.Utils::ReadParam('additional_impacted_object_ids', ''); + $sImpactedInfraIds = trim($sImpactedInfraIds); + $oPage->SetCurrentTab("Infrastructure impacted"); + if (!empty($sImpactedInfraIds)) + { + $oFilter = new CMDBSearchFilter('logRealObject'); + $oFilter->AddCondition('pkey', explode(' ', $sImpactedInfraIds), 'IN'); + $oBlock = new DisplayBlock($oFilter, 'list', false /* Synchronous */); + $oBlock->Display($oPage, 'related_objects'); + } + else + { + $oPage->p("There is no infrastructure impacted by this incident"); + } + + $sRelatedIncidentIds = Utils::ReadParam('related_incident_ids', ''); + $oPage->SetCurrentTab("Related tickets"); + if (!empty($sRelatedIncidentIds)) + { + $oFilter = new CMDBSearchFilter('bizIncidentTicket'); + $oFilter->AddCondition('pkey', explode(' ', $sRelatedIncidentIds), 'IN'); + $oBlock = new DisplayBlock($oFilter, 'list', false /* Synchronous */); + $oBlock->Display($oPage, 'related_incidents'); + } + else + { + $oPage->p("There is no other incident related to this one"); + } + + $oPage->SetCurrentTab("Contacts to notify"); + $sContactIds = Utils::ReadParam('contact_ids', ''); + if (!empty($sContactIds)) + { + $oFilter = new CMDBSearchFilter('bizContact'); + $oFilter->AddCondition('pkey', explode(' ', $sContactIds), 'IN'); + $oBlock = new DisplayBlock($oFilter, 'list', false /* Synchronous */); + $oBlock->Display($oPage, 'contacts'); + } + else + { + $oPage->p("There is no contact to notify"); + } + $oPage->SetCurrentTab(); + + $oPage->add('
    '); + $this->AddContextToForm($oPage); + $oPage->add("    \n"); + $oPage->add("    \n"); + $oPage->add("\n"); + $oPage->add('
    '); + } + + public function CreateIncident(web_page $oPage) + { + $oAppContext = new ApplicationContext(); + assert($this->m_sCurrentStep == '7'); + $this->m_sNextStep = '1'; + + $oIncident = MetaModel::NewObject('bizIncidentTicket'); + $oPage->p("Creation of Incident Ticket."); + + $aFields = $this->GetFields('1'); + foreach($aFields as $sAttCode => $sFieldName) + { + $sValue = Utils::ReadPostedParam($sFieldName, ''); + $oIncident->Set($sAttCode, $sValue); + } + $oIncident->Set('ticket_status', 'New'); + $oIncident->Set('start_date', time()); + $oIncident->Set('name', 'ID not set'); + + if ($oIncident->CheckToInsert()) + { + // Create the ticket itself + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + $oMyChange->Set("userinfo", "Administrator"); + $iChangeId = $oMyChange->DBInsert(); + $oIncident->DBInsertTracked($oMyChange); + + $sName = sprintf('I-%06d', $oIncident->GetKey()); + $oIncident->Set('name', $sName); + $oIncident->DBUpdateTracked($oMyChange); + $oPage->p("Incident $sName created.\n"); + + // Now link the objects to the Incident: + // 1) the impacted infra + $sImpactedInfraIds = Utils::ReadParam('impacted_infra_ids', ''); + $sImpactedInfraIds .= ' '.Utils::ReadParam('additional_impacted_object_ids', ''); + $sImpactedInfraIds = trim($sImpactedInfraIds); + if (!empty($sImpactedInfraIds)) + { + $aImpactedInfra = explode(' ', $sImpactedInfraIds); + foreach($aImpactedInfra as $iInfraId) + { + $oLink = MetaModel::NewObject('lnkInfraTicket'); + $oLink->Set('infra_id', $iInfraId); + $oLink->Set('ticket_id', $oIncident->GetKey()); + $oLink->Set('impact', 'automatic'); + $oLink->DBInsertTracked($oMyChange); + } + } + // 2) the related incidents + $sRelatedIncidentsIds = Utils::ReadPostedParam('related_incident_ids'); + if (!empty($sRelatedIncidentsIds)) + { + $aRelatedIncidents = explode(' ', $sRelatedIncidentsIds); + foreach($aRelatedIncidents as $iIncidentId) + { + $oLink = MetaModel::NewObject('lnkRelatedTicket'); + $oLink->Set('rel_ticket_id', $iIncidentId); + $oLink->Set('ticket_id', $oIncident->GetKey()); + $oLink->Set('impact', 'automatic'); + $oLink->DBInsertTracked($oMyChange); + } + } + // 3) the contacts to notify + $sContactsIds = Utils::ReadPostedParam('contact_ids'); + if (!empty($sContactsIds)) + { + $aContactsToNotify = explode(' ', $sContactsIds); + foreach($aContactsToNotify as $iContactId) + { + $oLink = MetaModel::NewObject('lnkContactRealObject'); + $oLink->Set('contact_id', $iContactId); + $oLink->Set('object_id', $oIncident->GetKey()); + $oLink->Set('role', 'notification'); + $oLink->DBInsertTracked($oMyChange); + } + } + $oIncident->DisplayDetails($oPage, 'bizIncidentTicket', $oIncident->GetKey()); + } + else + { + $oPage->p("Error: object can not be created!\n"); + } + } +} + + +$oContext = new UserContext(); +$oAppContext = new ApplicationContext(); +$currentOrganization = utils::ReadParam('org_id', ''); +$operation = utils::ReadParam('operation', ''); +$oP = new iTopWebPage("ITop - Incident Management", $currentOrganization); + +switch($operation) +{ + case 'details': + $sClass = utils::ReadParam('class', ''); + $id = utils::ReadParam('id', ''); + if ( empty($sClass) || empty($id)) // TO DO: check that the class name is valid ! + { + $oP->add("

    'class' and 'id' parameters must be specifed for this operation.

    \n"); + } + else + { + $oObj = $oContext->GetObject($sClass, $id); + if ($oObj != null) + { + $oP->set_title("iTop - ".$oObj->GetName()." - $sClass details"); + $oObj->DisplayDetails($oP); + } + else + { + $oP->set_title("iTop - Error"); + $oP->add("

    Sorry this object does not exist (or you are not allowed to view it).

    \n"); + } + } + break; + + case 'new': + $step = utils::ReadParam('step', '1'); + $aSteps = array( + 'Ticket Information', + 'Impacted Infrastructure', + 'Additional Impact', + 'Related Tickets', + 'Contacts to Notify', + 'Confirmation', + 'Ticket Creation' + ); + $oWizard = new IncidentCreationWizard($step); + $oP = new iTopWizardWebPage("ITop - Incident Management", $currentOrganization, $step, $aSteps); + + switch($step) + { + case 1: + default: + //$oP->add(''); + $oWizard->DisplayNewTicketForm($oP); + break; + + case 2: + //$oP->add(''); + $oWizard->DisplayImpactedInfraForm($oP); + break; + + case 3: + //$oP->add(''); + $oWizard->DisplayAdditionalImpactedObjectForm($oP); + break; + + case 4: + //$oP->add(''); + $oWizard->DisplayRelatedTicketsForm($oP); + break; + + case 5: + //$oP->add(''); + $oWizard->DisplayContactsToNotifyForm($oP); + break; + + case 6: + //$oP->add(''); + $oWizard->DisplayFinalForm($oP); + break; + + case 7: + $oWizard->CreateIncident($oP); + break; + } + break; + + case 'modify': + $oP->add_linked_script("../js/json.js"); + $oP->add_linked_script("../js/forms-json-utils.js"); + $oP->add_linked_script("../js/wizardhelper.js"); + $oP->add_linked_script("../js/wizard.utils.js"); + $oP->add_linked_script("../js/linkswidget.js"); + $oP->add_linked_script("../js/jquery.blockUI.js"); + $sClass = utils::ReadParam('class', ''); + $id = utils::ReadParam('id', ''); + if ( empty($sClass) || empty($id)) // TO DO: check that the class name is valid ! + { + $oP->add("

    'class' and 'id' parameters must be specifed for this operation.

    \n"); + } + else + { + $oObj = $oContext->GetObject($sClass, $id); + if ($oObj != null) + { + $oP->set_title("iTop - ".$oObj->GetName()." - $sClass modification"); + $oP->add("

    ".$oObj->GetName()." - $sClass modification

    \n"); + $oObj->DisplayModifyForm($oP); + } + else + { + $oP->set_title("iTop - Error"); + $oP->add("

    Sorry this object does not exist (or you are not allowed to view it).

    \n"); + } + } + break; + + case 'apply_modify': + $sClass = utils::ReadPostedParam('class', ''); + $id = utils::ReadPostedParam('id', ''); + $sTransactionId = utils::ReadPostedParam('transaction_id', ''); + if ( empty($sClass) || empty($id)) // TO DO: check that the class name is valid ! + { + $oP->add("

    'class' and 'id' parameters must be specifed for this operation.

    \n"); + } + else if (!utils::IsTransactionValid($sTransactionId)) + { + $oP->p("Error: object has already be updated!\n"); + } + else + { + $oObj = $oContext->GetObject($sClass, $id); + if ($oObj != null) + { + $oP->set_title("iTop - ".$oObj->GetName()." - $sClass modification"); + $oP->add("

    ".$oObj->GetName()." - $sClass modification

    \n"); + $bObjectModified = false; + foreach(MetaModel::ListAttributeDefs(get_class($oObj)) as $sAttCode=>$oAttDef) + { + $iFlags = $oObj->GetAttributeFlags($sAttCode); + if ($iFlags & (OPT_ATT_HIDDEN | OPT_ATT_READONLY)) + { + // Non-visible, or read-only attribute, do nothing + } + else if (!$oAttDef->IsExternalField()) + { + $aAttributes[$sAttCode] = trim(utils::ReadPostedParam("attr_$sAttCode", '')); + $previousValue = $oObj->Get($sAttCode); + if (!empty($aAttributes[$sAttCode]) && ($previousValue != $aAttributes[$sAttCode])) + { + $oObj->Set($sAttCode, $aAttributes[$sAttCode]); + $bObjectModified = true; + } + } + } + if (!$bObjectModified) + { + $oP->p("No modification detected. ".get_class($oObj)." has not been updated.\n"); + } + else if ($oObj->CheckToUpdate()) + { + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + $oMyChange->Set("userinfo", "Made by somebody"); // TO DO put the correct user info here + $iChangeId = $oMyChange->DBInsert(); + $oObj->DBUpdateTracked($oMyChange); + + $oP->p(get_class($oObj)." updated.\n"); + } + else + { + $oP->p("Error: object can not be updated!\n"); + //$oObj->Reload(); // restore default values! + } + } + else + { + $oP->set_title("iTop - Error"); + $oP->add("

    Sorry this object does not exist (or you are not allowed to edit it).

    \n"); + } + } + $oP->add("

    Alors ça roule ?

    "); + $oObj->DisplayDetails($oP); + break; +} +$oP->output(); +?> diff --git a/pages/index.php b/pages/index.php new file mode 100644 index 000000000..33c4d77c0 --- /dev/null +++ b/pages/index.php @@ -0,0 +1,704 @@ +no_cache(); + + + +MetaModel::CheckDefinitions(); +// new API - MetaModel::DBCheckFormat(); +// not necessary, and time consuming! +// MetaModel::DBCheckIntegrity(); + + +// Comment by Rom: MetaModel::GetSubclasses("logRealObject") retourne la totale +// utiliser IsRootClass pour savoir si une classe obtenue est une classe feuille ou non +$aTopLevelClasses = array('bizService', 'bizLocation', 'bizContact', 'logInfra', 'bizDocument', 'bizObject'); + +function ReadParam($sName, $defaultValue = "") +{ + return isset($_REQUEST[$sName]) ? $_REQUEST[$sName] : $defaultValue; +} + +function DisplaySelectOrg($oPage, $sCurrentOrganization, $iContext) +{ + global $oContext; + + //$oSearchFilter = new CMDBSearchFilter("bizOrganization"); + $oSearchFilter = $oContext->NewFilter("bizOrganization"); + $oPage->p($oSearchFilter->serialize()); + $oSet = new CMDBObjectSet($oSearchFilter); + if ($oSet->Count() == 0) + { + $oPage->add("
    \n"); + $oPage->add("
    \n"); + $oPage->p("No organization found.\n"); + $oPage->p($oSearchFilter->__DescribeHTML()); + $oPage->add("
    \n"); + $oPage->add("
    \n"); + } + else + { + $oCurrentOrganization = null; + $oPage->add("
    \n"); + $oPage->add("
    \n"); + $oPage->add("
    \n"); + $oPage->add("Select the context:\n"); + $oPage->add("\n"); + $oPage->p(""); + $oPage->add("Select an organization: \n"); + $oPage->add("\n"); + $oPage->add("\n"); + $oPage->add("
    \n"); + if ($oCurrentOrganization != null) + { + $oCurrentOrganization->DisplayDetails($oPage); + } + $oPage->add("
    \n"); + $oPage->add("
    \n"); + } +} + +function DisplayDetails(web_page $oPage, $sClassName, $sKey) +{ + global $oContext; + //$oObj = MetaModel::GetObject($sClassName, $sKey); + $oObj = $oContext->GetObject($sClassName, $sKey); + $oPage->p("Details of ".get_class($oObj)." - $sKey"); + + $oObj->DisplayDetails($oPage); + + // Modified by rom + $aLinks = array(); + $aLinks[] = "View changes log"; + $aLinks[] = "Edit this object"; + $aLinks[] = "Delete this object (no confirmation!)"; + // By rom + foreach (MetaModel::EnumLinkingClasses($sClassName) as $sLinkClass => $aRemoteClasses) + { + foreach($aRemoteClasses as $sExtKeyAttCode => $sRemoteClass) + { + // #@# quick and dirty: guess the extkey attcode from link to current class + // later, this information should be part of the biz model + $sExtKeyToMe = ""; + foreach(MetaModel::ListAttributeDefs($sLinkClass) as $sAttCode=>$oAttDef) + { + if ($oAttDef->IsExternalKey() && $oAttDef->GetTargetClass() == $sClassName) + { + $sExtKeyToMe = $sAttCode; + break; + } + } + if (empty($sExtKeyToMe)) + { + $oPage->p("Houston... could not find the external key for $sClassName in $sLinkClass"); + } + else + { + $oFilter = new CMDBSearchFilter($sRemoteClass); // just a dummy empty one for edition + + $sButton = "
    \n"; + $sButton .= "
    \n"; + $aOnOKArgs = array("operation"=>"addlinks", "linkclass"=>$sLinkClass, "extkeytome"=>$sExtKeyToMe, "extkeytopartner"=>$sExtKeyAttCode); + $sButton .= dialogstack::RenderEditableField("Add links with $sRemoteClass", "filter", $oFilter->serialize(), true, "", $aOnOKArgs); + $sButton .= "
    \n"; + $sButton .= "
    \n"; + $aLinks[] = $sButton; + } + } + } + $sLinks = implode(" / ", $aLinks); + $oPage->p($sLinks); +} + +// By Rom +function DisplayChangesLog(web_page $oPage, $sClassName, $sKey) +{ + global $oContext; + //$oObj = MetaModel::GetObject($sClassName, $sKey); + $oObj = $oContext->GetObject($sClassName, $sKey); + $oPage->p("Changes log for ".get_class($oObj)." - $sKey"); + + $oObj->DisplayChangesLog($oPage); + + $oPage->p("View details"); + $oPage->p("Edit this object"); + $oPage->p("Delete this object (no confirmation!)"); +} + +function DumpObjectsAsCSV(web_page $oPage, $sClassName, $oSearchFilter = null, $sSeparator = ",") +{ + global $oContext; + + $aHeader = array(); + $aHeader[] = 'pkey'; + foreach(MetaModel::ListAttributeDefs($sClassName) as $sAttCode=>$oAttDef) + { + $aHeader[] = $oAttDef->GetLabel(); + } + $oPage->Add(join($sSeparator, $aHeader)."\n"); + + if ($oSearchFilter == null) + { + $oSearchFilter = $oContext->NewFilter($sClassName); + } + $oObjectSet = new CMDBObjectSet($oSearchFilter); + + while ($oObj = $oObjectSet->Fetch()) + { + $aRow = array(); + $aRow[] = $oObj->GetKey(); + foreach($oObj->GetAttributesList($sClassName) as $sAttCode) + { + $aRow[] = $oObj->GetAsCSV($sAttCode); + } + $oPage->Add(join($sSeparator, $aRow)."\n"); + } +} + +function DumpObjects(web_page $oPage, $sClassName, CMDBSearchFilter $oSearchFilter = null) +{ + global $oContext; + + if ($oSearchFilter == null) + { + //$oSearchFilter = new CMDBSearchFilter($sClassName); + $oSearchFilter = $oContext->NewFilter($sClassName); + } + $aAttribs = array(); + foreach(MetaModel::ListAttributeDefs($sClassName) as $sAttCode=>$oAttDef) + { + $aAttribs['key'] = array('label' => 'key', 'description' => 'Primary Key'); + $aAttribs[$sAttCode] = array('label' => $oAttDef->GetLabel(), 'description' => $oAttDef->GetDescription()); + } + $oObjectSet = new CMDBObjectSet($oSearchFilter); + + $aValues = array(); + while ($oObj = $oObjectSet->Fetch()) + { + $aRow['key'] = "GetKey()."\">".$oObj->GetKey().""; + foreach($oObj->GetAttributesList($sClassName) as $sAttCode) + { + $aRow[$sAttCode] = $oObj->GetAsHTML($sAttCode); + } + $aValues[] = $aRow; + } + $oPage->table($aAttribs, $aValues); +} + +function DisplayEditForm(web_page $oPage, $sClassName, $sKey) +{ + global $oContext; + //$oObj = MetaModel::GetObject($sClassName, $sKey); + $oObj = $oContext->GetObject($sClassName, $sKey); + if ($oObj == null) + { + $oPage->p("You are not allowed to edit this object."); + return; + } + $oPage->p("Edition of ".get_class($oObj)." - $sKey\n"); + + $aDetails = array(); + $oPage->add("
    \n"); + foreach(MetaModel::ListAttributeDefs(get_class($oObj)) as $sAttCode=>$oAttDef) + { + if (!$oAttDef->IsExternalField()) + { + if ($oAttDef->IsExternalKey()) + { + //External key, display a combo + $sTargetClass = $oAttDef->GetTargetClass(); + //$oFilter = new CMDBSearchFilter($sTargetClass); + $oFilter = $oContext->NewFilter($sTargetClass); + $oSet = new CMDBObjectSet($oFilter); + $sValue = "\n"; + } + else + { + $sValue = "Get($sAttCode))."\">"; + } + $aDetails[] = array('label' => $oAttDef->GetLabel(), 'value' => $sValue); + } + } + $oPage->details($aDetails); + $oPage->add("\n"); + $oPage->add("\n"); + $oPage->add("\n"); + $oPage->add("\n"); + $oPage->add("\n"); +} + +function DisplayCreationForm(web_page $oPage, $sClassName) +{ + global $oContext; + $oPage->p("New $sClassName\n"); + + $aDetails = array(); + $oPage->add("\n"); + foreach(MetaModel::ListAttributeDefs($sClassName) as $sAttCode=>$oAttDef) + { + if (!$oAttDef->IsExternalField()) + { + if ($oAttDef->IsExternalKey()) + { + //External key, display a combo + $sTargetClass = $oAttDef->GetTargetClass(); + //$oFilter = new CMDBSearchFilter($sTargetClass); + $oFilter = $oContext->NewFilter($sTargetClass); + $oSet = new CMDBObjectSet($oFilter); + $sValue = "\n"; + } + else + { + $sValue = "GetDefaultValue()."\">"; + } + $aDetails[] = array('label' => $oAttDef->GetLabel(), 'value' => $sValue); + } + } + $oPage->details($aDetails); + $oPage->add("\n"); + $oPage->add("\n"); + $oPage->add("\n"); + $oPage->add("\n"); +} + +function UpdateObject(web_page $oPage, $sClassName, $sKey, $aAttributes) +{ + global $oContext; + //$oObj = MetaModel::GetObject($sClassName, $sKey); + $oObj = $oContext->GetObject($sClassName, $sKey); + if ($oObj == null) + { + $oPage->p("You are not allowed to edit this object."); + return; + } + $oPage->p("Update of $sClassName - $sKey"); + + foreach(MetaModel::ListAttributeDefs(get_class($oObj)) as $sAttCode=>$oAttDef) + { + if (isset($aAttributes[$sAttCode])) + { + $oObj->Set($sAttCode, $aAttributes[$sAttCode]); + } + } + if ($oObj->CheckToUpdate()) + { + // By rom + // $oObj->DBUpdate(); + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + $oMyChange->Set("userinfo", "Made by somebody"); + $iChangeId = $oMyChange->DBInsert(); + $oObj->DBUpdateTracked($oMyChange); + + $oPage->p(get_class($oObj)." updated\n"); + } + else + { + $oPage->p("Error: object can not be updated!\n"); + $oObj->DBRevert(); // restore default values! + } + // By Rom + // $oObj->DisplayDetails($oPage); + // replaced by... + DisplayDetails($oPage, get_class($oObj), $oObj->GetKey()); + $oPage->p("Return to main page"); +} + +function DeleteObject(web_page $oPage, $sClassName, $sKey) +{ + global $oContext; + //$oObj = MetaModel::GetObject($sClassName, $sKey); + $oObj = $oContext->GetObject($sClassName, $sKey); + if ($oObj == null) + { + $oPage->p("You are not allowed to delete this object."); + return; + } + $oPage->p("Deletion of $sClassName - $sKey"); + + if ($oObj->CheckToDelete()) + { + // By Rom + //$oObj->DBDelete(); + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + $oMyChange->Set("userinfo", "Made by somebody"); + $iChangeId = $oMyChange->DBInsert(); + $oObj->DBDeleteTracked($oMyChange); + + $oPage->p("$sClassName deleted\n"); + } + else + { + $oPage->p("Error: object can not be deleted!\n"); + // By Rom + DisplayDetails($oPage, get_class($oObj), $oObj->GetKey()); + } + $oPage->p("Return to main page"); +} + +function CreateObject(web_page $oPage, $sClassName, $aAttributes) +{ + $oObj = MetaModel::NewObject($sClassName); + $oPage->p("Creation of ".get_class($oObj)." object."); + + foreach(MetaModel::ListAttributeDefs(get_class($oObj)) as $sAttCode=>$oAttDef) + { + if (isset($aAttributes[$sAttCode])) + { + $oObj->Set($sAttCode, $aAttributes[$sAttCode]); + } + } + if ($oObj->CheckToInsert()) + { + // By rom + // $oObj->DBInsert(); + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + $oMyChange->Set("userinfo", "Made by somebody"); + $iChangeId = $oMyChange->DBInsert(); + $oObj->DBInsertTracked($oMyChange); + + $oPage->p(get_class($oObj)." created\n"); + + // By Rom + // $oObj->DisplayDetails($oPage); + // replaced by... + DisplayDetails($oPage, get_class($oObj), $oObj->GetKey()); + } + else + { + $oPage->p("Error: object can not be created!\n"); + } + $oPage->p("Return to main page"); +} + +// By Rom +function AddLinks($oPage, $sClassName, $sKey, $sLinkClass, $sExtKeyToMe, $sExtKeyToPartner, $sFilter) +{ + global $oContext; + //$oObj = MetaModel::GetObject($sClassName, $sKey); + $oObj = $oContext->GetObject($sClassName, $sKey); + if ($oObj == null) + { + $oPage->p("You are not allowed to modify (create links on) this object."); + return; + } + $oPage->p("Creating links for $sClassName - $sKey"); + + $oFilter = CMDBSearchFilter::unserialize($sFilter); + $oPage->p("Linking to ".$oFilter->__DescribeHTML()); + + $oObjSet = new CMDBObjectSet($oFilter); + if ($oObjSet->Count() != 0) + { + while($oPartnerObj = $oObjSet->Fetch()) + { + $oNewLink = MetaModel::NewObject($sLinkClass); + $oNewLink->Set($sExtKeyToMe, $sKey); + $oNewLink->Set($sExtKeyToPartner, $oPartnerObj->GetKey()); + if ($oNewLink->CheckToInsert()) + { + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + $oMyChange->Set("userinfo", "Made by somebody"); + $iChangeId = $oMyChange->DBInsert(); + $oNewLink->DBInsertTracked($oMyChange); + + $oPage->p(get_class($oNewLink)." created\n"); + } + else + { + $oPage->p("Error: link can not be created!\n"); + } + } + } + else + {} + +} + + +/////////////////////////////////////////////////////////////////////////////////////////////////// +// +// M a i n P r o g r a m +// +/////////////////////////////////////////////////////////////////////////////////////////////////// + +$operation = ReadParam('operation', ''); +$iContext = ReadParam('ctx', 1); + +$oContext = new UserContext(); + +switch($iContext) +{ + case 2: // See only the organization 'ITOP' + $oContext->AddCondition('bizOrganization', 'pkey', 'ITOP', '='); + $oContext->AddCondition('logRealObject', 'organization', 'ITOP', '='); + break; + + case 3: // See only the organization containing 'o' and contacts containing +33 + $oContext->AddCondition('Organization', 'name', 'o', 'Contains'); + //$oContext->AddCondition('Object', 'orgname', 'o', 'Contains'); + $oContext->AddCondition('Contact', 'phone', '+33', 'Contains'); + break; + + case 1: // No limitation + default: + // nothing to do +} + +dialogstack::DeclareCaller("Main navigation menu"); + +switch($operation) +{ + case 'details': + $sClass = ReadParam('class'); + $sKey = ReadParam('key'); + DisplayDetails($oPage, $sClass, $sKey); + break; + + // By rom + case 'changeslog': + $sClass = ReadParam('class'); + $sKey = ReadParam('key'); + DisplayChangesLog($oPage, $sClass, $sKey); + break; + + case 'edit': + $sClass = ReadParam('class'); + $sKey = ReadParam('key'); + DisplayEditForm($oPage, $sClass, $sKey); + break; + + case 'update': + $sClass = ReadParam('class'); + $sKey = ReadParam('key'); + $aAttributes = ReadParam('attr', array()); + UpdateObject($oPage, $sClass, $sKey, $aAttributes); + break; + + case 'new': + $sClass = ReadParam('class'); + DisplayCreationForm($oPage, $sClass); + break; + + case 'create': + $sClass = ReadParam('class'); + $aAttributes = ReadParam('attr', array()); + CreateObject($oPage, $sClass, $aAttributes); + break; + + case 'delete': + $sClass = ReadParam('class'); + $sKey = ReadParam('key'); + DeleteObject($oPage, $sClass, $sKey); + break; + + case 'direct': + $sFilter = ReadParam('filter'); + $sFormat = ReadParam('format', 'html'); + $oSearchFilter = CMDBSearchFilter::unserialize($sFilter); + switch($sFormat) + { + case 'csv': + $oPage->small_p($oSearchFilter->__DescribeHTML()); + $oPage->Add(""); + break; + + case 'xls': + $oPage->add_header('Content-disposition: attachment;filename=served.xls'); // Will fool Excel + $oPage->add_header('Content-Type: application/vnd.ms-excel'); // Will fool Excel + DumpObjects($oPage, $oSearchFilter->GetClass(), $oSearchFilter); + break; + + case 'html': + default: + $oSet = new CMDBObjectSet($oSearchFilter); + cmdbAbstractObject::DisplaySet($oPage, $oSet); + } + break; + + case 'addlinks': + $sClass = ReadParam('class'); + $sKey = ReadParam('key'); + $sLinkClass = ReadParam('linkclass'); + $sExtKeyToMe = ReadParam('extkeytome'); + $sExtKeyToPartner = ReadParam('extkeytopartner'); + $sFilter = ReadParam('filter'); + AddLinks($oPage, $sClass, $sKey, $sLinkClass, $sExtKeyToMe, $sExtKeyToPartner, $sFilter); + break; + + default: + $sCurrentOrganization = ReadParam('org', ''); + $sActiveTab = ReadParam('classname', ''); + DisplaySelectOrg($oPage, $sCurrentOrganization, $iContext); + if ($sCurrentOrganization != "") + { + $oPage->add("
    \n"); + $oPage->add("
      \n"); + $index = 1; + $iActiveTabIndex = 1; // By default the first tab is the active one + foreach( $aTopLevelClasses as $sClassName) + { + if ($sClassName == $sActiveTab) + { + $iActiveTabIndex = $index; + } + $oPage->add("\t
    • $sClassName
    • \n"); + $index++; + } + $oPage->add("
    \n"); + foreach( $aTopLevelClasses as $sClassName) + { + $oPage->add("
    "); + if (count(MetaModel::GetSubclasses($sClassName)) > 0) + { + $sActiveSubclass = ReadParam('subclassname', ''); + foreach(MetaModel::GetSubclasses($sClassName) as $sSubclassName) + { + //$oSearchFilter = new CMDBSearchFilter($sSubclassName); + $oSearchFilter = $oContext->NewFilter($sSubclassName); + $oSearchFilter ->AddCondition('org_id', $sCurrentOrganization, '='); + + $oPage->add("
    \n"); + $oPage->add("
    \n"); + $oPage->p("$sSubclassName - ".MetaModel::GetClassDescription($sSubclassName)); + $oPage->add("\n"); + $oPage->add("\n"); + $oPage->add("\n"); + $oPage->add("\n"); + $oPage->add("\n"); + foreach( MetaModel::GetClassFilterDefs($sSubclassName) as $sFilterCode=>$oFilterDef) + { + $sFilterValue = ""; + if (($sActiveTab == $sClassName) && ($sActiveSubclass == $sSubclassName)) + { + $sFilterValue = ReadParam($sFilterCode, ''); + if (!empty($sFilterValue)) + { + $oSearchFilter->AddCondition($sFilterCode, $sFilterValue, 'Contains'); + } + } + $oPage->add($oFilterDef->GetLabel().":  \n"); + } + $oPage->add("\n"); + $oPage->add("\n"); + $oPage->add("
    \n"); + + $oSet = new CMDBObjectSet($oSearchFilter); + $iMatchesCount = $oSet->Count(); + if ($iMatchesCount == 0) + { + $oPage->p("No $sSubclassName matches these criteria."); + $oPage->small_p("(".$oSearchFilter->__DescribeHTML().")"); + } + else + { + $oPage->p("$iMatchesCount item(s) found."); + cmdbAbstractObject::DisplaySet($oPage, $oSet); + } + $oPage->p("Create a new $sSubclassName\n"); + $oPage->add("
    \n"); + } + } + else + { + // No subclasses, list the form directly + //$oSearchFilter = new CMDBSearchFilter($sClassName); + $oSearchFilter = $oContext->NewFilter($sClassName); + $oSearchFilter ->AddCondition('org_id', $sCurrentOrganization, '='); + + $oPage->add("
    \n"); + $oPage->add("
    \n"); + $oPage->p("$sClassName - ".MetaModel::GetClassDescription($sClassName)); + $oPage->add("
    \n"); + $oPage->add("\n"); + $oPage->add("\n"); + $oPage->add("\n"); + foreach( MetaModel::GetClassFilterDefs($sClassName) as $sFilterCode=>$oFilterDef) + { + $sFilterValue = ""; + if ($sActiveTab == $sClassName) + { + $sFilterValue = ReadParam($sFilterCode, ''); + if (!empty($sFilterValue)) + { + $oSearchFilter->AddCondition($sFilterCode, $sFilterValue, 'Contains'); + } + } + $oPage->add($oFilterDef->GetLabel().":  \n"); + } + $oPage->add("\n"); + $oPage->add("
    \n"); + $oPage->add("
    \n"); + $oPage->add("
    \n"); + $oSet = new CMDBObjectSet($oSearchFilter); + $iMatchesCount = $oSet->Count(); + if ($iMatchesCount == 0) + { + $oPage->p("No $sClassName matches these criteria."); + $oPage->small_p("(".$oSearchFilter->__DescribeHTML().")"); + } + else + { + $oPage->p("$iMatchesCount item(s) found."); + cmdbAbstractObject::DisplaySet($oPage, $oSet); + $oPage->small_p("(".$oSearchFilter->__DescribeHTML().")"); + } + $oPage->p("Create a new $sClassName\n"); + $oPage->add("
    \n"); + $oPage->add("
    \n"); + } + $oPage->add("
    \n"); + } + $oPage->add("
    \n"); + $oPage->add_script('$(function() {$("#classesTabs > ul").tabs( '.$iActiveTabIndex.', { fxFade: true, fxSpeed: \'fast\' } );});'); + } +} +$oPage->output(); +?> diff --git a/pages/opensearch.xml b/pages/opensearch.xml new file mode 100644 index 000000000..2620b2d1f --- /dev/null +++ b/pages/opensearch.xml @@ -0,0 +1,8 @@ + +iTop +webmaster@itop.com +Recherche dans iTop +ISO-8859-1 + +http://localhost:81/pages/UI.php + \ No newline at end of file diff --git a/pages/opensearch.xml.php b/pages/opensearch.xml.php new file mode 100644 index 000000000..6d57a98db --- /dev/null +++ b/pages/opensearch.xml.php @@ -0,0 +1,16 @@ + + +iTop +webmaster@itop.com +Search in iTop +ISO-8859-1 + + + + + diff --git a/pages/php-ofc-library/JSON.php b/pages/php-ofc-library/JSON.php new file mode 100644 index 000000000..7260001a2 --- /dev/null +++ b/pages/php-ofc-library/JSON.php @@ -0,0 +1,806 @@ + + * @author Matt Knapp + * @author Brett Stimmerman + * @copyright 2005 Michal Migurski + * @version CVS: $Id: JSON.php,v 1.31 2006/06/28 05:54:17 migurski Exp $ + * @license http://www.opensource.org/licenses/bsd-license.php + * @link http://pear.php.net/pepr/pepr-proposal-show.php?id=198 + */ + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_SLICE', 1); + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_IN_STR', 2); + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_IN_ARR', 3); + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_IN_OBJ', 4); + +/** + * Marker constant for Services_JSON::decode(), used to flag stack state + */ +define('SERVICES_JSON_IN_CMT', 5); + +/** + * Behavior switch for Services_JSON::decode() + */ +define('SERVICES_JSON_LOOSE_TYPE', 16); + +/** + * Behavior switch for Services_JSON::decode() + */ +define('SERVICES_JSON_SUPPRESS_ERRORS', 32); + +/** + * Converts to and from JSON format. + * + * Brief example of use: + * + * + * // create a new instance of Services_JSON + * $json = new Services_JSON(); + * + * // convert a complexe value to JSON notation, and send it to the browser + * $value = array('foo', 'bar', array(1, 2, 'baz'), array(3, array(4))); + * $output = $json->encode($value); + * + * print($output); + * // prints: ["foo","bar",[1,2,"baz"],[3,[4]]] + * + * // accept incoming POST data, assumed to be in JSON notation + * $input = file_get_contents('php://input', 1000000); + * $value = $json->decode($input); + * + */ +class Services_JSON +{ + /** + * constructs a new JSON instance + * + * @param int $use object behavior flags; combine with boolean-OR + * + * possible values: + * - SERVICES_JSON_LOOSE_TYPE: loose typing. + * "{...}" syntax creates associative arrays + * instead of objects in decode(). + * - SERVICES_JSON_SUPPRESS_ERRORS: error suppression. + * Values which can't be encoded (e.g. resources) + * appear as NULL instead of throwing errors. + * By default, a deeply-nested resource will + * bubble up with an error, so all return values + * from encode() should be checked with isError() + */ + function Services_JSON($use = 0) + { + $this->use = $use; + } + + /** + * convert a string from one UTF-16 char to one UTF-8 char + * + * Normally should be handled by mb_convert_encoding, but + * provides a slower PHP-only method for installations + * that lack the multibye string extension. + * + * @param string $utf16 UTF-16 character + * @return string UTF-8 character + * @access private + */ + function utf162utf8($utf16) + { + // oh please oh please oh please oh please oh please + if(function_exists('mb_convert_encoding')) { + return mb_convert_encoding($utf16, 'UTF-8', 'UTF-16'); + } + + $bytes = (ord($utf16{0}) << 8) | ord($utf16{1}); + + switch(true) { + case ((0x7F & $bytes) == $bytes): + // this case should never be reached, because we are in ASCII range + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0x7F & $bytes); + + case (0x07FF & $bytes) == $bytes: + // return a 2-byte UTF-8 character + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0xC0 | (($bytes >> 6) & 0x1F)) + . chr(0x80 | ($bytes & 0x3F)); + + case (0xFFFF & $bytes) == $bytes: + // return a 3-byte UTF-8 character + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0xE0 | (($bytes >> 12) & 0x0F)) + . chr(0x80 | (($bytes >> 6) & 0x3F)) + . chr(0x80 | ($bytes & 0x3F)); + } + + // ignoring UTF-32 for now, sorry + return ''; + } + + /** + * convert a string from one UTF-8 char to one UTF-16 char + * + * Normally should be handled by mb_convert_encoding, but + * provides a slower PHP-only method for installations + * that lack the multibye string extension. + * + * @param string $utf8 UTF-8 character + * @return string UTF-16 character + * @access private + */ + function utf82utf16($utf8) + { + // oh please oh please oh please oh please oh please + if(function_exists('mb_convert_encoding')) { + return mb_convert_encoding($utf8, 'UTF-16', 'UTF-8'); + } + + switch(strlen($utf8)) { + case 1: + // this case should never be reached, because we are in ASCII range + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return $utf8; + + case 2: + // return a UTF-16 character from a 2-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr(0x07 & (ord($utf8{0}) >> 2)) + . chr((0xC0 & (ord($utf8{0}) << 6)) + | (0x3F & ord($utf8{1}))); + + case 3: + // return a UTF-16 character from a 3-byte UTF-8 char + // see: http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + return chr((0xF0 & (ord($utf8{0}) << 4)) + | (0x0F & (ord($utf8{1}) >> 2))) + . chr((0xC0 & (ord($utf8{1}) << 6)) + | (0x7F & ord($utf8{2}))); + } + + // ignoring UTF-32 for now, sorry + return ''; + } + + /** + * encodes an arbitrary variable into JSON format + * + * @param mixed $var any number, boolean, string, array, or object to be encoded. + * see argument 1 to Services_JSON() above for array-parsing behavior. + * if var is a strng, note that encode() always expects it + * to be in ASCII or UTF-8 format! + * + * @return mixed JSON string representation of input var or an error if a problem occurs + * @access public + */ + function encode($var) + { + switch (gettype($var)) { + case 'boolean': + return $var ? 'true' : 'false'; + + case 'NULL': + return 'null'; + + case 'integer': + return (int) $var; + + case 'double': + case 'float': + return (float) $var; + + case 'string': + // STRINGS ARE EXPECTED TO BE IN ASCII OR UTF-8 FORMAT + $ascii = ''; + $strlen_var = strlen($var); + + /* + * Iterate over every character in the string, + * escaping with a slash or encoding to UTF-8 where necessary + */ + for ($c = 0; $c < $strlen_var; ++$c) { + + $ord_var_c = ord($var{$c}); + + switch (true) { + case $ord_var_c == 0x08: + $ascii .= '\b'; + break; + case $ord_var_c == 0x09: + $ascii .= '\t'; + break; + case $ord_var_c == 0x0A: + $ascii .= '\n'; + break; + case $ord_var_c == 0x0C: + $ascii .= '\f'; + break; + case $ord_var_c == 0x0D: + $ascii .= '\r'; + break; + + case $ord_var_c == 0x22: + case $ord_var_c == 0x2F: + case $ord_var_c == 0x5C: + // double quote, slash, slosh + $ascii .= '\\'.$var{$c}; + break; + + case (($ord_var_c >= 0x20) && ($ord_var_c <= 0x7F)): + // characters U-00000000 - U-0000007F (same as ASCII) + $ascii .= $var{$c}; + break; + + case (($ord_var_c & 0xE0) == 0xC0): + // characters U-00000080 - U-000007FF, mask 110XXXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, ord($var{$c + 1})); + $c += 1; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF0) == 0xE0): + // characters U-00000800 - U-0000FFFF, mask 1110XXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2})); + $c += 2; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xF8) == 0xF0): + // characters U-00010000 - U-001FFFFF, mask 11110XXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3})); + $c += 3; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFC) == 0xF8): + // characters U-00200000 - U-03FFFFFF, mask 111110XX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3}), + ord($var{$c + 4})); + $c += 4; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + + case (($ord_var_c & 0xFE) == 0xFC): + // characters U-04000000 - U-7FFFFFFF, mask 1111110X + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $char = pack('C*', $ord_var_c, + ord($var{$c + 1}), + ord($var{$c + 2}), + ord($var{$c + 3}), + ord($var{$c + 4}), + ord($var{$c + 5})); + $c += 5; + $utf16 = $this->utf82utf16($char); + $ascii .= sprintf('\u%04s', bin2hex($utf16)); + break; + } + } + + return '"'.$ascii.'"'; + + case 'array': + /* + * As per JSON spec if any array key is not an integer + * we must treat the the whole array as an object. We + * also try to catch a sparsely populated associative + * array with numeric keys here because some JS engines + * will create an array with empty indexes up to + * max_index which can cause memory issues and because + * the keys, which may be relevant, will be remapped + * otherwise. + * + * As per the ECMA and JSON specification an object may + * have any string as a property. Unfortunately due to + * a hole in the ECMA specification if the key is a + * ECMA reserved word or starts with a digit the + * parameter is only accessible using ECMAScript's + * bracket notation. + */ + + // treat as a JSON object + if (is_array($var) && count($var) && (array_keys($var) !== range(0, sizeof($var) - 1))) { + $properties = array_map(array($this, 'name_value'), + array_keys($var), + array_values($var)); + + foreach($properties as $property) { + if(Services_JSON::isError($property)) { + return $property; + } + } + + return '{' . join(',', $properties) . '}'; + } + + // treat it like a regular array + $elements = array_map(array($this, 'encode'), $var); + + foreach($elements as $element) { + if(Services_JSON::isError($element)) { + return $element; + } + } + + return '[' . join(',', $elements) . ']'; + + case 'object': + $vars = get_object_vars($var); + + $properties = array_map(array($this, 'name_value'), + array_keys($vars), + array_values($vars)); + + foreach($properties as $property) { + if(Services_JSON::isError($property)) { + return $property; + } + } + + return '{' . join(',', $properties) . '}'; + + default: + return ($this->use & SERVICES_JSON_SUPPRESS_ERRORS) + ? 'null' + : new Services_JSON_Error(gettype($var)." can not be encoded as JSON string"); + } + } + + /** + * array-walking function for use in generating JSON-formatted name-value pairs + * + * @param string $name name of key to use + * @param mixed $value reference to an array element to be encoded + * + * @return string JSON-formatted name-value pair, like '"name":value' + * @access private + */ + function name_value($name, $value) + { + $encoded_value = $this->encode($value); + + if(Services_JSON::isError($encoded_value)) { + return $encoded_value; + } + + return $this->encode(strval($name)) . ':' . $encoded_value; + } + + /** + * reduce a string by removing leading and trailing comments and whitespace + * + * @param $str string string value to strip of comments and whitespace + * + * @return string string value stripped of comments and whitespace + * @access private + */ + function reduce_string($str) + { + $str = preg_replace(array( + + // eliminate single line comments in '// ...' form + '#^\s*//(.+)$#m', + + // eliminate multi-line comments in '/* ... */' form, at start of string + '#^\s*/\*(.+)\*/#Us', + + // eliminate multi-line comments in '/* ... */' form, at end of string + '#/\*(.+)\*/\s*$#Us' + + ), '', $str); + + // eliminate extraneous space + return trim($str); + } + + /** + * decodes a JSON string into appropriate variable + * + * @param string $str JSON-formatted string + * + * @return mixed number, boolean, string, array, or object + * corresponding to given JSON input string. + * See argument 1 to Services_JSON() above for object-output behavior. + * Note that decode() always returns strings + * in ASCII or UTF-8 format! + * @access public + */ + function decode($str) + { + $str = $this->reduce_string($str); + + switch (strtolower($str)) { + case 'true': + return true; + + case 'false': + return false; + + case 'null': + return null; + + default: + $m = array(); + + if (is_numeric($str)) { + // Lookie-loo, it's a number + + // This would work on its own, but I'm trying to be + // good about returning integers where appropriate: + // return (float)$str; + + // Return float or int, as appropriate + return ((float)$str == (integer)$str) + ? (integer)$str + : (float)$str; + + } elseif (preg_match('/^("|\').*(\1)$/s', $str, $m) && $m[1] == $m[2]) { + // STRINGS RETURNED IN UTF-8 FORMAT + $delim = substr($str, 0, 1); + $chrs = substr($str, 1, -1); + $utf8 = ''; + $strlen_chrs = strlen($chrs); + + for ($c = 0; $c < $strlen_chrs; ++$c) { + + $substr_chrs_c_2 = substr($chrs, $c, 2); + $ord_chrs_c = ord($chrs{$c}); + + switch (true) { + case $substr_chrs_c_2 == '\b': + $utf8 .= chr(0x08); + ++$c; + break; + case $substr_chrs_c_2 == '\t': + $utf8 .= chr(0x09); + ++$c; + break; + case $substr_chrs_c_2 == '\n': + $utf8 .= chr(0x0A); + ++$c; + break; + case $substr_chrs_c_2 == '\f': + $utf8 .= chr(0x0C); + ++$c; + break; + case $substr_chrs_c_2 == '\r': + $utf8 .= chr(0x0D); + ++$c; + break; + + case $substr_chrs_c_2 == '\\"': + case $substr_chrs_c_2 == '\\\'': + case $substr_chrs_c_2 == '\\\\': + case $substr_chrs_c_2 == '\\/': + if (($delim == '"' && $substr_chrs_c_2 != '\\\'') || + ($delim == "'" && $substr_chrs_c_2 != '\\"')) { + $utf8 .= $chrs{++$c}; + } + break; + + case preg_match('/\\\u[0-9A-F]{4}/i', substr($chrs, $c, 6)): + // single, escaped unicode character + $utf16 = chr(hexdec(substr($chrs, ($c + 2), 2))) + . chr(hexdec(substr($chrs, ($c + 4), 2))); + $utf8 .= $this->utf162utf8($utf16); + $c += 5; + break; + + case ($ord_chrs_c >= 0x20) && ($ord_chrs_c <= 0x7F): + $utf8 .= $chrs{$c}; + break; + + case ($ord_chrs_c & 0xE0) == 0xC0: + // characters U-00000080 - U-000007FF, mask 110XXXXX + //see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 2); + ++$c; + break; + + case ($ord_chrs_c & 0xF0) == 0xE0: + // characters U-00000800 - U-0000FFFF, mask 1110XXXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 3); + $c += 2; + break; + + case ($ord_chrs_c & 0xF8) == 0xF0: + // characters U-00010000 - U-001FFFFF, mask 11110XXX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 4); + $c += 3; + break; + + case ($ord_chrs_c & 0xFC) == 0xF8: + // characters U-00200000 - U-03FFFFFF, mask 111110XX + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 5); + $c += 4; + break; + + case ($ord_chrs_c & 0xFE) == 0xFC: + // characters U-04000000 - U-7FFFFFFF, mask 1111110X + // see http://www.cl.cam.ac.uk/~mgk25/unicode.html#utf-8 + $utf8 .= substr($chrs, $c, 6); + $c += 5; + break; + + } + + } + + return $utf8; + + } elseif (preg_match('/^\[.*\]$/s', $str) || preg_match('/^\{.*\}$/s', $str)) { + // array, or object notation + + if ($str{0} == '[') { + $stk = array(SERVICES_JSON_IN_ARR); + $arr = array(); + } else { + if ($this->use & SERVICES_JSON_LOOSE_TYPE) { + $stk = array(SERVICES_JSON_IN_OBJ); + $obj = array(); + } else { + $stk = array(SERVICES_JSON_IN_OBJ); + $obj = new stdClass(); + } + } + + array_push($stk, array('what' => SERVICES_JSON_SLICE, + 'where' => 0, + 'delim' => false)); + + $chrs = substr($str, 1, -1); + $chrs = $this->reduce_string($chrs); + + if ($chrs == '') { + if (reset($stk) == SERVICES_JSON_IN_ARR) { + return $arr; + + } else { + return $obj; + + } + } + + //print("\nparsing {$chrs}\n"); + + $strlen_chrs = strlen($chrs); + + for ($c = 0; $c <= $strlen_chrs; ++$c) { + + $top = end($stk); + $substr_chrs_c_2 = substr($chrs, $c, 2); + + if (($c == $strlen_chrs) || (($chrs{$c} == ',') && ($top['what'] == SERVICES_JSON_SLICE))) { + // found a comma that is not inside a string, array, etc., + // OR we've reached the end of the character list + $slice = substr($chrs, $top['where'], ($c - $top['where'])); + array_push($stk, array('what' => SERVICES_JSON_SLICE, 'where' => ($c + 1), 'delim' => false)); + //print("Found split at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + if (reset($stk) == SERVICES_JSON_IN_ARR) { + // we are in an array, so just push an element onto the stack + array_push($arr, $this->decode($slice)); + + } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { + // we are in an object, so figure + // out the property name and set an + // element in an associative array, + // for now + $parts = array(); + + if (preg_match('/^\s*(["\'].*[^\\\]["\'])\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { + // "name":value pair + $key = $this->decode($parts[1]); + $val = $this->decode($parts[2]); + + if ($this->use & SERVICES_JSON_LOOSE_TYPE) { + $obj[$key] = $val; + } else { + $obj->$key = $val; + } + } elseif (preg_match('/^\s*(\w+)\s*:\s*(\S.*),?$/Uis', $slice, $parts)) { + // name:value pair, where name is unquoted + $key = $parts[1]; + $val = $this->decode($parts[2]); + + if ($this->use & SERVICES_JSON_LOOSE_TYPE) { + $obj[$key] = $val; + } else { + $obj->$key = $val; + } + } + + } + + } elseif ((($chrs{$c} == '"') || ($chrs{$c} == "'")) && ($top['what'] != SERVICES_JSON_IN_STR)) { + // found a quote, and we are not inside a string + array_push($stk, array('what' => SERVICES_JSON_IN_STR, 'where' => $c, 'delim' => $chrs{$c})); + //print("Found start of string at {$c}\n"); + + } elseif (($chrs{$c} == $top['delim']) && + ($top['what'] == SERVICES_JSON_IN_STR) && + ((strlen(substr($chrs, 0, $c)) - strlen(rtrim(substr($chrs, 0, $c), '\\'))) % 2 != 1)) { + // found a quote, we're in a string, and it's not escaped + // we know that it's not escaped becase there is _not_ an + // odd number of backslashes at the end of the string so far + array_pop($stk); + //print("Found end of string at {$c}: ".substr($chrs, $top['where'], (1 + 1 + $c - $top['where']))."\n"); + + } elseif (($chrs{$c} == '[') && + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { + // found a left-bracket, and we are in an array, object, or slice + array_push($stk, array('what' => SERVICES_JSON_IN_ARR, 'where' => $c, 'delim' => false)); + //print("Found start of array at {$c}\n"); + + } elseif (($chrs{$c} == ']') && ($top['what'] == SERVICES_JSON_IN_ARR)) { + // found a right-bracket, and we're in an array + array_pop($stk); + //print("Found end of array at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } elseif (($chrs{$c} == '{') && + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { + // found a left-brace, and we are in an array, object, or slice + array_push($stk, array('what' => SERVICES_JSON_IN_OBJ, 'where' => $c, 'delim' => false)); + //print("Found start of object at {$c}\n"); + + } elseif (($chrs{$c} == '}') && ($top['what'] == SERVICES_JSON_IN_OBJ)) { + // found a right-brace, and we're in an object + array_pop($stk); + //print("Found end of object at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } elseif (($substr_chrs_c_2 == '/*') && + in_array($top['what'], array(SERVICES_JSON_SLICE, SERVICES_JSON_IN_ARR, SERVICES_JSON_IN_OBJ))) { + // found a comment start, and we are in an array, object, or slice + array_push($stk, array('what' => SERVICES_JSON_IN_CMT, 'where' => $c, 'delim' => false)); + $c++; + //print("Found start of comment at {$c}\n"); + + } elseif (($substr_chrs_c_2 == '*/') && ($top['what'] == SERVICES_JSON_IN_CMT)) { + // found a comment end, and we're in one now + array_pop($stk); + $c++; + + for ($i = $top['where']; $i <= $c; ++$i) + $chrs = substr_replace($chrs, ' ', $i, 1); + + //print("Found end of comment at {$c}: ".substr($chrs, $top['where'], (1 + $c - $top['where']))."\n"); + + } + + } + + if (reset($stk) == SERVICES_JSON_IN_ARR) { + return $arr; + + } elseif (reset($stk) == SERVICES_JSON_IN_OBJ) { + return $obj; + + } + + } + } + } + + /** + * @todo Ultimately, this should just call PEAR::isError() + */ + function isError($data, $code = null) + { + if (class_exists('pear')) { + return PEAR::isError($data, $code); + } elseif (is_object($data) && (get_class($data) == 'services_json_error' || + is_subclass_of($data, 'services_json_error'))) { + return true; + } + + return false; + } +} + +if (class_exists('PEAR_Error')) { + + class Services_JSON_Error extends PEAR_Error + { + function Services_JSON_Error($message = 'unknown error', $code = null, + $mode = null, $options = null, $userinfo = null) + { + parent::PEAR_Error($message, $code, $mode, $options, $userinfo); + } + } + +} else { + + /** + * @todo Ultimately, this class shall be descended from PEAR_Error + */ + class Services_JSON_Error + { + function Services_JSON_Error($message = 'unknown error', $code = null, + $mode = null, $options = null, $userinfo = null) + { + + } + } + +} + +?> diff --git a/pages/php-ofc-library/README.txt b/pages/php-ofc-library/README.txt new file mode 100644 index 000000000..2d8b6297f --- /dev/null +++ b/pages/php-ofc-library/README.txt @@ -0,0 +1,16 @@ +Open Flash Chart - PHP libraries. These help create data files for Open Flash Chart. +Copyright (C) 2007 + +This library is free software; you can redistribute it and/or +modify it under the terms of the GNU Lesser General Public +License as published by the Free Software Foundation; either +version 2.1 of the License, or (at your option) any later version. + +This library is distributed in the hope that it will be useful, +but WITHOUT ANY WARRANTY; without even the implied warranty of +MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU +Lesser General Public License for more details. + +You should have received a copy of the GNU Lesser General Public +License along with this library; if not, write to the Free Software +Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA \ No newline at end of file diff --git a/pages/php-ofc-library/json_format.php b/pages/php-ofc-library/json_format.php new file mode 100644 index 000000000..61a842ea1 --- /dev/null +++ b/pages/php-ofc-library/json_format.php @@ -0,0 +1,86 @@ + 0 && $json[$c-1] != '\\') + { + $in_string = !$in_string; + } + default: + $new_json .= $char; + break; + } + } + + return $new_json; +} \ No newline at end of file diff --git a/pages/php-ofc-library/ofc_area_base.php b/pages/php-ofc-library/ofc_area_base.php new file mode 100644 index 000000000..8d8f7560a --- /dev/null +++ b/pages/php-ofc-library/ofc_area_base.php @@ -0,0 +1,66 @@ +$tmp = 0.35; + $this->values = array(); + } + + function set_width( $w ) + { + $this->width = $w; + } + + function set_colour( $colour ) + { + $this->colour = $colour; + } + + function set_fill_colour( $colour ) + { + $this->fill = $colour; + } + + function set_fill_alpha( $alpha ) + { + $tmp = "fill-alpha"; + $this->$tmp = $alpha; + } + + function set_halo_size( $size ) + { + $tmp = 'halo-size'; + $this->$tmp = $size; + } + + function set_values( $v ) + { + $this->values = $v; + } + + function set_dot_size( $size ) + { + $tmp = 'dot-size'; + $this->$tmp = $size; + } + + function set_key( $text, $font_size ) + { + $this->text = $text; + $tmp = 'font-size'; + $this->$tmp = $font_size; + } + + function set_tooltip( $tip ) + { + $this->tip = $tip; + } + + function set_loop() + { + $this->loop = true; + } +} diff --git a/pages/php-ofc-library/ofc_area_hollow.php b/pages/php-ofc-library/ofc_area_hollow.php new file mode 100644 index 000000000..ba6c20461 --- /dev/null +++ b/pages/php-ofc-library/ofc_area_hollow.php @@ -0,0 +1,10 @@ +type = "area_hollow"; + parent::area_base(); + } +} diff --git a/pages/php-ofc-library/ofc_area_line.php b/pages/php-ofc-library/ofc_area_line.php new file mode 100644 index 000000000..c4b2b167e --- /dev/null +++ b/pages/php-ofc-library/ofc_area_line.php @@ -0,0 +1,10 @@ +type = "area_line"; + parent::area_base(); + } +} diff --git a/pages/php-ofc-library/ofc_bar.php b/pages/php-ofc-library/ofc_bar.php new file mode 100644 index 000000000..0666939ff --- /dev/null +++ b/pages/php-ofc-library/ofc_bar.php @@ -0,0 +1,34 @@ +top = $top; + + if( isset( $bottom ) ) + $this->bottom = $bottom; + } + + function set_colour( $colour ) + { + $this->colour = $colour; + } + + function set_tooltip( $tip ) + { + $this->tip = $tip; + } +} + +class bar extends bar_base +{ + function bar() + { + $this->type = "bar"; + parent::bar_base(); + } +} + diff --git a/pages/php-ofc-library/ofc_bar_3d.php b/pages/php-ofc-library/ofc_bar_3d.php new file mode 100644 index 000000000..4b4d8f4fb --- /dev/null +++ b/pages/php-ofc-library/ofc_bar_3d.php @@ -0,0 +1,31 @@ +top = $top; +// $this->bottom = $bottom; + } + + function set_colour( $colour ) + { + $this->colour = $colour; + } + + function set_tooltip( $tip ) + { + $this->tip = $tip; + } +} + +class bar_3d extends bar_base +{ + function bar_3d() + { + $this->type = "bar_3d"; + parent::bar_base(); + } +} \ No newline at end of file diff --git a/pages/php-ofc-library/ofc_bar_base.php b/pages/php-ofc-library/ofc_bar_base.php new file mode 100644 index 000000000..4ab5c52e5 --- /dev/null +++ b/pages/php-ofc-library/ofc_bar_base.php @@ -0,0 +1,41 @@ +text = $text; + $tmp = 'font-size'; + $this->$tmp = $size; + } + + function set_values( $v ) + { + $this->values = $v; + } + + function append_value( $v ) + { + $this->values[] = $v; + } + + function set_colour( $colour ) + { + $this->colour = $colour; + } + + function set_alpha( $alpha ) + { + $this->alpha = $alpha; + } + + function set_tooltip( $tip ) + { + $this->tip = $tip; + } +} + diff --git a/pages/php-ofc-library/ofc_bar_filled.php b/pages/php-ofc-library/ofc_bar_filled.php new file mode 100644 index 000000000..2008703cd --- /dev/null +++ b/pages/php-ofc-library/ofc_bar_filled.php @@ -0,0 +1,39 @@ +$tmp = $outline_colour; + } +} + +class bar_filled extends bar_base +{ + function bar_filled( $colour=null, $outline_colour=null ) + { + $this->type = "bar_filled"; + parent::bar_base(); + + if( isset( $colour ) ) + $this->set_colour( $colour ); + + if( isset( $outline_colour ) ) + $this->set_outline_colour( $outline_colour ); + } + + function set_outline_colour( $outline_colour ) + { + $tmp = 'outline-colour'; + $this->$tmp = $outline_colour; + } +} + diff --git a/pages/php-ofc-library/ofc_bar_glass.php b/pages/php-ofc-library/ofc_bar_glass.php new file mode 100644 index 000000000..4ca674378 --- /dev/null +++ b/pages/php-ofc-library/ofc_bar_glass.php @@ -0,0 +1,33 @@ +top = $top; +// $this->bottom = $bottom; + } + + function set_colour( $colour ) + { + $this->colour = $colour; + } + + function set_tooltip( $tip ) + { + $this->tip = $tip; + } +} + + +class bar_glass extends bar_base +{ + function bar_glass() + { + $this->type = "bar_glass"; + parent::bar_base(); + } +} diff --git a/pages/php-ofc-library/ofc_bar_sketch.php b/pages/php-ofc-library/ofc_bar_sketch.php new file mode 100644 index 000000000..f39fdcfd9 --- /dev/null +++ b/pages/php-ofc-library/ofc_bar_sketch.php @@ -0,0 +1,23 @@ +type = "bar_sketch"; + parent::bar_base(); + + $this->set_colour( $colour ); + $this->set_outline_colour( $outline_colour ); + $this->offset = $fun_factor; + } + + function set_outline_colour( $outline_colour ) + { + $tmp = 'outline-colour'; + $this->$tmp = $outline_colour; + } +} + diff --git a/pages/php-ofc-library/ofc_bar_stack.php b/pages/php-ofc-library/ofc_bar_stack.php new file mode 100644 index 000000000..945d3d8aa --- /dev/null +++ b/pages/php-ofc-library/ofc_bar_stack.php @@ -0,0 +1,50 @@ +type = "bar_stack"; + parent::bar_base(); + } + + function append_stack( $v ) + { + $this->append_value( $v ); + } + + // an array of HEX colours strings + // e.g. array( '#ff0000', '#00ff00' ); + function set_colours( $colours ) + { + $this->colours = $colours; + } + + // an array of bar_stack_value + function set_keys( $keys ) + { + $this->keys = $keys; + } +} + +class bar_stack_value +{ + function bar_stack_value( $val, $colour ) + { + $this->val = $val; + $this->colour = $colour; + } +} + +class bar_stack_key +{ + function bar_stack_key( $colour, $text, $font_size ) + { + $this->colour = $colour; + $this->text = $text; + $tmp = 'font-size'; + $this->$tmp = $font_size; + } +} \ No newline at end of file diff --git a/pages/php-ofc-library/ofc_hbar.php b/pages/php-ofc-library/ofc_hbar.php new file mode 100644 index 000000000..a0fffbb55 --- /dev/null +++ b/pages/php-ofc-library/ofc_hbar.php @@ -0,0 +1,64 @@ +left = $left; + $this->right = $right; + } + else + $this->right = $left; + } + + function set_colour( $colour ) + { + $this->colour = $colour; + } + + function set_tooltip( $tip ) + { + $this->tip = $tip; + } +} + +class hbar +{ + function hbar( $colour ) + { + $this->type = "hbar"; + $this->values = array(); + $this->set_colour( $colour ); + } + + function append_value( $v ) + { + $this->values[] = $v; + } + + function set_values( $v ) + { + foreach( $v as $val ) + $this->append_value( new hbar_value( $val ) ); + } + + function set_colour( $colour ) + { + $this->colour = $colour; + } + + function set_key( $text, $size ) + { + $this->text = $text; + $tmp = 'font-size'; + $this->$tmp = $size; + } + + function set_tooltip( $tip ) + { + $this->tip = $tip; + } +} + diff --git a/pages/php-ofc-library/ofc_line.php b/pages/php-ofc-library/ofc_line.php new file mode 100644 index 000000000..945af208e --- /dev/null +++ b/pages/php-ofc-library/ofc_line.php @@ -0,0 +1,9 @@ +type = "line"; + } +} \ No newline at end of file diff --git a/pages/php-ofc-library/ofc_line_base.php b/pages/php-ofc-library/ofc_line_base.php new file mode 100644 index 000000000..2e969f9e3 --- /dev/null +++ b/pages/php-ofc-library/ofc_line_base.php @@ -0,0 +1,70 @@ +type = "line_dot"; + $this->text = "Page views"; + $tmp = 'font-size'; + $this->$tmp = 10; + + $this->values = array(9,6,7,9,5,7,6,9,7); + } + + function set_values( $v ) + { + $this->values = $v; + } + + function set_width( $width ) + { + $this->width = $width; + } + + function set_colour( $colour ) + { + $this->colour = $colour; + } + + function set_dot_size( $size ) + { + $tmp = 'dot-size'; + $this->$tmp = $size; + } + + function set_halo_size( $size ) + { + $tmp = 'halo-size'; + $this->$tmp = $size; + } + + function set_key( $text, $font_size ) + { + $this->text = $text; + $tmp = 'font-size'; + $this->$tmp = $font_size; + } + + function set_tooltip( $tip ) + { + $this->tip = $tip; + } + + function set_on_click( $text ) + { + $tmp = 'on-click'; + $this->$tmp = $text; + } + + function loop() + { + $this->loop = true; + } + + function line_style( $s ) + { + $tmp = "line-style"; + $this->$tmp = $s; + } +} \ No newline at end of file diff --git a/pages/php-ofc-library/ofc_line_dot.php b/pages/php-ofc-library/ofc_line_dot.php new file mode 100644 index 000000000..52f2ad0eb --- /dev/null +++ b/pages/php-ofc-library/ofc_line_dot.php @@ -0,0 +1,33 @@ +value = $value; + $this->colour = $colour; + } + + function set_colour( $colour ) + { + $this->colour = $colour; + } + + function set_size( $size ) + { + $this->size = $size; + } + + function set_tooltip( $tip ) + { + $this->tip = $tip; + } +} + +class line_dot extends line_base +{ + function line_dot() + { + $this->type = "line_dot"; + } +} \ No newline at end of file diff --git a/pages/php-ofc-library/ofc_line_hollow.php b/pages/php-ofc-library/ofc_line_hollow.php new file mode 100644 index 000000000..783fdf778 --- /dev/null +++ b/pages/php-ofc-library/ofc_line_hollow.php @@ -0,0 +1,9 @@ +type = "line_hollow"; + } +} \ No newline at end of file diff --git a/pages/php-ofc-library/ofc_line_style.php b/pages/php-ofc-library/ofc_line_style.php new file mode 100644 index 000000000..4ba5ff8b6 --- /dev/null +++ b/pages/php-ofc-library/ofc_line_style.php @@ -0,0 +1,11 @@ +style = "dash"; + $this->on = $on; + $this->off = $off; + } +} \ No newline at end of file diff --git a/pages/php-ofc-library/ofc_pie.php b/pages/php-ofc-library/ofc_pie.php new file mode 100644 index 000000000..be50008e5 --- /dev/null +++ b/pages/php-ofc-library/ofc_pie.php @@ -0,0 +1,109 @@ +value = $value; + $this->label = $label; + } + + function set_colour( $colour ) + { + $this->colour = $colour; + } + + function set_label( $label, $label_colour, $font_size ) + { + $this->label = $label; + + $tmp = 'label-colour'; + $this->$tmp = $label_colour; + + $tmp = 'font-size'; + $this->$tmp = $font_size; + + } + + function set_tooltip( $tip ) + { + $this->tip = $tip; + } + + function on_click( $event ) + { + $tmp = 'on-click'; + $this->$tmp = $event; + } + +} + +class pie +{ + function pie() + { + $this->type = 'pie'; + $this->colours = array("#d01f3c","#356aa0","#C79810"); + $this->border = 2; + } + + function set_colours( $colours ) + { + $this->colours = $colours; + } + + function set_alpha( $alpha ) + { + $this->alpha = $alpha; + } + + function set_values( $v ) + { + $this->values = $v; + } + + // boolean + function set_animate( $animate ) + { + $this->animate = $animate; + } + + // real + function set_start_angle( $angle ) + { + $tmp = 'start-angle'; + $this->$tmp = $angle; + } + + function set_tooltip( $tip ) + { + $this->tip = $tip; + } + + function set_gradient_fill() + { + $tmp = 'gradient-fill'; + $this->$tmp = true; + } + + function set_label_colour( $label_colour ) + { + $tmp = 'label-colour'; + $this->$tmp = $label_colour; + } + + /** + * Turn off the labels + */ + function set_no_labels() + { + $tmp = 'no-labels'; + $this->$tmp = true; + } + + function on_click( $event ) + { + $tmp = 'on-click'; + $this->$tmp = $event; + } +} diff --git a/pages/php-ofc-library/ofc_radar_axis.php b/pages/php-ofc-library/ofc_radar_axis.php new file mode 100644 index 000000000..9c8d7885f --- /dev/null +++ b/pages/php-ofc-library/ofc_radar_axis.php @@ -0,0 +1,47 @@ +set_max( $max ); + } + + function set_max( $max ) + { + $this->max = $max; + } + + function set_steps( $steps ) + { + $this->steps = $steps; + } + + function set_stroke( $s ) + { + $this->stroke = $s; + } + + function set_colour( $colour ) + { + $this->colour = $colour; + } + + function set_grid_colour( $colour ) + { + $tmp = 'grid-colour'; + $this->$tmp = $colour; + } + + function set_labels( $labels ) + { + $this->labels = $labels; + } + + function set_spoke_labels( $labels ) + { + $tmp = 'spoke-labels'; + $this->$tmp = $labels; + } +} + diff --git a/pages/php-ofc-library/ofc_radar_axis_labels.php b/pages/php-ofc-library/ofc_radar_axis_labels.php new file mode 100644 index 000000000..1c4321cce --- /dev/null +++ b/pages/php-ofc-library/ofc_radar_axis_labels.php @@ -0,0 +1,15 @@ +labels = $labels; + } + + function set_colour( $colour ) + { + $this->colour = $colour; + } +} \ No newline at end of file diff --git a/pages/php-ofc-library/ofc_radar_spoke_labels.php b/pages/php-ofc-library/ofc_radar_spoke_labels.php new file mode 100644 index 000000000..11b6682f1 --- /dev/null +++ b/pages/php-ofc-library/ofc_radar_spoke_labels.php @@ -0,0 +1,15 @@ +labels = $labels; + } + + function set_colour( $colour ) + { + $this->colour = $colour; + } +} \ No newline at end of file diff --git a/pages/php-ofc-library/ofc_scatter.php b/pages/php-ofc-library/ofc_scatter.php new file mode 100644 index 000000000..978dc0487 --- /dev/null +++ b/pages/php-ofc-library/ofc_scatter.php @@ -0,0 +1,42 @@ +x = $x; + $this->y = $y; + + if( $dot_size > 0 ) + { + $tmp = 'dot-size'; + $this->$tmp = $dot_size; + } + } +} + +class scatter +{ + function scatter( $colour, $dot_size ) + { + $this->type = "scatter"; + $this->set_colour( $colour ); + $this->set_dot_size( $dot_size ); + } + + function set_colour( $colour ) + { + $this->colour = $colour; + } + + function set_dot_size( $dot_size ) + { + $tmp = 'dot-size'; + $this->$tmp = $dot_size; + } + + function set_values( $values ) + { + $this->values = $values; + } +} diff --git a/pages/php-ofc-library/ofc_scatter_line.php b/pages/php-ofc-library/ofc_scatter_line.php new file mode 100644 index 000000000..3c4e21c0f --- /dev/null +++ b/pages/php-ofc-library/ofc_scatter_line.php @@ -0,0 +1,27 @@ +type = "scatter_line"; + $this->set_colour( $colour ); + $this->set_dot_size( $dot_size ); + } + + function set_colour( $colour ) + { + $this->colour = $colour; + } + + function set_dot_size( $dot_size ) + { + $tmp = 'dot-size'; + $this->$tmp = $dot_size; + } + + function set_values( $values ) + { + $this->values = $values; + } +} \ No newline at end of file diff --git a/pages/php-ofc-library/ofc_shape.php b/pages/php-ofc-library/ofc_shape.php new file mode 100644 index 000000000..3bab90790 --- /dev/null +++ b/pages/php-ofc-library/ofc_shape.php @@ -0,0 +1,25 @@ +x = $x; + $this->y = $y; + } +} + +class shape +{ + function shape( $colour ) + { + $this->type = "shape"; + $this->colour = $colour; + $this->values = array(); + } + + function append_value( $p ) + { + $this->values[] = $p; + } +} \ No newline at end of file diff --git a/pages/php-ofc-library/ofc_title.php b/pages/php-ofc-library/ofc_title.php new file mode 100644 index 000000000..6dd19bbb8 --- /dev/null +++ b/pages/php-ofc-library/ofc_title.php @@ -0,0 +1,15 @@ +text = $text; + } + + function set_style( $css ) + { + $this->style = $css; + //"{font-size: 20px; color:#0000ff; font-family: Verdana; text-align: center;}"; + } +} \ No newline at end of file diff --git a/pages/php-ofc-library/ofc_tooltip.php b/pages/php-ofc-library/ofc_tooltip.php new file mode 100644 index 000000000..161a77ba0 --- /dev/null +++ b/pages/php-ofc-library/ofc_tooltip.php @@ -0,0 +1,51 @@ +shadow = $shadow; + } + + // stroke in pixels (e.g. 5 ) + function set_stroke( $stroke ) + { + $this->stroke = $stroke; + } + + function set_colour( $colour ) + { + $this->colour = $colour; + } + + function set_background_colour( $bg ) + { + $this->background = $bg; + } + + // a css style + function set_title_style( $style ) + { + $this->title = $style; + } + + function set_body_style( $style ) + { + $this->body = $style; + } + + function set_proximity() + { + $this->mouse = 1; + } + + function set_hover() + { + $this->mouse = 2; + } +} + diff --git a/pages/php-ofc-library/ofc_upload_image.php b/pages/php-ofc-library/ofc_upload_image.php new file mode 100644 index 000000000..b726af946 --- /dev/null +++ b/pages/php-ofc-library/ofc_upload_image.php @@ -0,0 +1,61 @@ + save_image debug mode, you +// will see the 'echo' text in a new window. +// + +/* + +print_r( $_GET ); +print_r( $_POST ); +print_r( $_FILES ); + +print_r( $GLOBALS ); +print_r( $GLOBALS["HTTP_RAW_POST_DATA"] ); + +*/ + + +// default path for the image to be stored // +$default_path = '../tmp-upload-images/'; + +if (!file_exists($default_path)) mkdir($default_path, 0777, true); + +// full path to the saved image including filename // +$destination = $default_path . basename( $_GET[ 'name' ] ); + +echo 'Saving your image to: '. $destination; + +$jfh = fopen($destination, 'w') or die("can't open file"); +fwrite($jfh, $GLOBALS['HTTP_RAW_POST_DATA']); +fclose($jfh); + +// +// LOOK: +// +exit(); + + +// +// PHP5: +// + + +// default path for the image to be stored // +$default_path = 'tmp-upload-images/'; + +if (!file_exists($default_path)) mkdir($default_path, 0777, true); + +// full path to the saved image including filename // +$destination = $default_path . basename( $_FILES[ 'Filedata' ][ 'name' ] ); + +// move the image into the specified directory // +if (move_uploaded_file($_FILES[ 'Filedata' ][ 'tmp_name' ], $destination)) { + echo "The file " . basename( $_FILES[ 'Filedata' ][ 'name' ] ) . " has been uploaded;"; +} else { + echo "FILE UPLOAD FAILED"; +} + + +?> diff --git a/pages/php-ofc-library/ofc_x_axis.php b/pages/php-ofc-library/ofc_x_axis.php new file mode 100644 index 000000000..b268bdeb6 --- /dev/null +++ b/pages/php-ofc-library/ofc_x_axis.php @@ -0,0 +1,77 @@ +stroke = $stroke; + } + + function set_colours( $colour, $grid_colour ) + { + $this->set_colour( $colour ); + $this->set_grid_colour( $grid_colour ); + } + + function set_colour( $colour ) + { + $this->colour = $colour; + } + + function set_tick_height( $height ) + { + $tmp = 'tick-height'; + $this->$tmp = $height; + } + + function set_grid_colour( $colour ) + { + $tmp = 'grid-colour'; + $this->$tmp = $colour; + } + + // $o is a boolean + function set_offset( $o ) + { + $this->offset = $o?true:false; + } + + function set_steps( $steps ) + { + $this->steps = $steps; + } + + function set_3d( $val ) + { + $tmp = '3d'; + $this->$tmp = $val; + } + + function set_labels( $x_axis_labels ) + { + //$this->labels = $v; + $this->labels = $x_axis_labels; + } + + // + // helper function to make the examples + // simpler. + // + function set_labels_from_array( $a ) + { + $x_axis_labels = new x_axis_labels(); + $x_axis_labels->set_labels( $a ); + $this->labels = $x_axis_labels; + + if( isset( $this->steps ) ) + $x_axis_labels->set_steps( $this->steps ); + } + + function set_range( $min, $max ) + { + $this->min = $min; + $this->max = $max; + } +} \ No newline at end of file diff --git a/pages/php-ofc-library/ofc_x_axis_label.php b/pages/php-ofc-library/ofc_x_axis_label.php new file mode 100644 index 000000000..11a79d7ed --- /dev/null +++ b/pages/php-ofc-library/ofc_x_axis_label.php @@ -0,0 +1,42 @@ +set_text( $text ); + $this->set_colour( $colour ); + $this->set_size( $size ); + $this->set_rotate( $rotate ); + } + + function set_text( $text ) + { + $this->text = $text; + } + + function set_colour( $colour ) + { + $this->colour = $colour; + } + + function set_size( $size ) + { + $this->size = $size; + } + + function set_rotate( $rotate ) + { + $this->rotate = $rotate; + } + + function set_vertical() + { + $this->rotate = "vertical"; + } + + function set_visible() + { + $this->visible = true; + } +} \ No newline at end of file diff --git a/pages/php-ofc-library/ofc_x_axis_labels.php b/pages/php-ofc-library/ofc_x_axis_labels.php new file mode 100644 index 000000000..9781025ad --- /dev/null +++ b/pages/php-ofc-library/ofc_x_axis_labels.php @@ -0,0 +1,34 @@ +steps = $steps; + } + + // + // An array of [x_axis_label or string] + // + function set_labels( $labels ) + { + $this->labels = $labels; + } + + function set_colour( $colour ) + { + $this->colour = $colour; + } + + function set_size( $size ) + { + $this->size = $size; + } + + function set_vertical() + { + $this->rotate = "vertical"; + } +} \ No newline at end of file diff --git a/pages/php-ofc-library/ofc_x_legend.php b/pages/php-ofc-library/ofc_x_legend.php new file mode 100644 index 000000000..f36c017f4 --- /dev/null +++ b/pages/php-ofc-library/ofc_x_legend.php @@ -0,0 +1,15 @@ +text = $text; + } + + function set_style( $css ) + { + $this->style = $css; + //"{font-size: 20px; color:#0000ff; font-family: Verdana; text-align: center;}"; + } +} \ No newline at end of file diff --git a/pages/php-ofc-library/ofc_y_axis.php b/pages/php-ofc-library/ofc_y_axis.php new file mode 100644 index 000000000..c8720a192 --- /dev/null +++ b/pages/php-ofc-library/ofc_y_axis.php @@ -0,0 +1,17 @@ +$tmp = $colour; + } + +} \ No newline at end of file diff --git a/pages/php-ofc-library/ofc_y_axis_base.php b/pages/php-ofc-library/ofc_y_axis_base.php new file mode 100644 index 000000000..5cfc61f15 --- /dev/null +++ b/pages/php-ofc-library/ofc_y_axis_base.php @@ -0,0 +1,56 @@ +stroke = $s; + } + + function set_tick_length( $val ) + { + $tmp = 'tick-length'; + $this->$tmp = $val; + } + + function set_colours( $colour, $grid_colour ) + { + $this->set_colour( $colour ); + $this->set_grid_colour( $grid_colour ); + } + + function set_colour( $colour ) + { + $this->colour = $colour; + } + + function set_grid_colour( $colour ) + { + $tmp = 'grid-colour'; + $this->$tmp = $colour; + } + + function set_range( $min, $max, $steps=1 ) + { + $this->min = $min; + $this->max = $max; + $this->set_steps( $steps ); + } + + function set_offset( $off ) + { + $this->offset = $off?1:0; + } + + function set_labels( $labels ) + { + $this->labels = $labels; + } + + function set_steps( $steps ) + { + $this->steps = $steps; + } +} \ No newline at end of file diff --git a/pages/php-ofc-library/ofc_y_axis_right.php b/pages/php-ofc-library/ofc_y_axis_right.php new file mode 100644 index 000000000..1e0911194 --- /dev/null +++ b/pages/php-ofc-library/ofc_y_axis_right.php @@ -0,0 +1,6 @@ +text = $text; + } + + function set_style( $css ) + { + $this->style = $css; + //"{font-size: 20px; color:#0000ff; font-family: Verdana; text-align: center;}"; + } +} \ No newline at end of file diff --git a/pages/php-ofc-library/open-flash-chart-object.php b/pages/php-ofc-library/open-flash-chart-object.php new file mode 100644 index 000000000..d3b64aa48 --- /dev/null +++ b/pages/php-ofc-library/open-flash-chart-object.php @@ -0,0 +1,109 @@ +'; + + if( !isset( $open_flash_chart_seqno ) ) + { + $open_flash_chart_seqno = 1; + $out[] = ''; + } + else + { + $open_flash_chart_seqno++; + $obj_id .= '_'. $open_flash_chart_seqno; + $div_name .= '_'. $open_flash_chart_seqno; + } + + if( $use_swfobject ) + { + // Using library for auto-enabling Flash object on IE, disabled-Javascript proof + $out[] = '
    '; + $out[] = ''; + $out[] = ''; + } + + return implode("\n",$out); +} +?> \ No newline at end of file diff --git a/pages/php-ofc-library/open-flash-chart.php b/pages/php-ofc-library/open-flash-chart.php new file mode 100644 index 000000000..6147da531 --- /dev/null +++ b/pages/php-ofc-library/open-flash-chart.php @@ -0,0 +1,138 @@ +title = new title( "Many data lines" ); + $this->elements = array(); + } + + function set_title( $t ) + { + $this->title = $t; + } + + function set_x_axis( $x ) + { + $this->x_axis = $x; + } + + function set_y_axis( $y ) + { + $this->y_axis = $y; + } + + function add_y_axis( $y ) + { + $this->y_axis = $y; + } + + function set_y_axis_right( $y ) + { + $this->y_axis_right = $y; + } + + function add_element( $e ) + { + $this->elements[] = $e; + } + + function set_x_legend( $x ) + { + $this->x_legend = $x; + } + + function set_y_legend( $y ) + { + $this->y_legend = $y; + } + + function set_bg_colour( $colour ) + { + $this->bg_colour = $colour; + } + + function set_radar_axis( $radar ) + { + $this->radar_axis = $radar; + } + + function set_tooltip( $tooltip ) + { + $this->tooltip = $tooltip; + } + + function toString() + { + if (function_exists('json_encode')) + { + return json_encode($this); + } + else + { + $json = new Services_JSON(); + return $json->encode( $this ); + } + } + + function toPrettyString() + { + return json_format( $this->toString() ); + } +} + + + +// +// there is no PHP end tag so we don't mess the headers up! +// \ No newline at end of file diff --git a/pages/schema.php b/pages/schema.php new file mode 100644 index 000000000..ef06f230f --- /dev/null +++ b/pages/schema.php @@ -0,0 +1,475 @@ + link to a class + */ +function MakeClassHLink($sClass) +{ + return "".MetaModel::GetName($sClass).""; +} + +/** + * Helper for this page -> link to a class + */ +function MakeRelationHLink($sRelCode) +{ + $sDec = MetaModel::GetRelationProperty($sRelCode, 'description'); + //$sVerbDown = MetaModel::GetRelationProperty($sRelCode, 'verb_down'); + //$sVerbUp = MetaModel::GetRelationProperty($sRelCode, 'verb_up'); + return "".$sRelCode.""; +} + +/** + * Helper for the global list and the details of a given class + */ +function DisplaySubclasses($oPage, $sClass) +{ + $aChildClasses = MetaModel::EnumChildClasses($sClass); + if (count($aChildClasses) != 0) + { + $oPage->add("
      \n"); + foreach($aChildClasses as $sClassName) + { + // Skip indirect childs, they will be handled somewhere else + if (MetaModel::GetParentPersistentClass($sClassName) == $sClass) + { + $oPage->add("
    • ".MakeClassHLink($sClassName)."\n"); + DisplaySubclasses($oPage, $sClassName); + $oPage->add("
    • \n"); + } + } + $oPage->add("
    \n"); + } +} + +/** + * Helper for the global list and the details of a given class + */ +function DisplayReferencingClasses($oPage, $sClass) +{ + $bSkipLinkingClasses = false; + $aRefs = MetaModel::EnumReferencingClasses($sClass, $bSkipLinkingClasses); + if (count($aRefs) != 0) + { + $oPage->add("
      \n"); + foreach ($aRefs as $sRemoteClass => $aRemoteKeys) + { + foreach ($aRemoteKeys as $sExtKeyAttCode) + { + $oPage->add("
    • ".MakeClassHLink($sRemoteClass)." by $sExtKeyAttCode
    • \n"); + } + } + $oPage->add("
    \n"); + } +} + +/** + * Helper for the global list and the details of a given class + */ +function DisplayLinkingClasses($oPage, $sClass) +{ + $bSkipLinkingClasses = false; + $aRefs = MetaModel::EnumLinkingClasses($sClass); + if (count($aRefs) != 0) + { + $oPage->add("
      \n"); + foreach ($aRefs as $sLinkClass => $aRemoteClasses) + { + foreach($aRemoteClasses as $sExtKeyAttCode => $sRemoteClass) + { + $oPage->add("
    • ".MakeClassHLink($sRemoteClass)." by ".MakeClassHLink($sLinkClass)."::$sExtKeyAttCode
    • \n"); + } + } + $oPage->add("
    \n"); + } +} + +/** + * Helper for the global list and the details of a given class + */ +function DisplayRelatedClassesBestInClass($oPage, $sClass, $iLevels = 20, &$aVisitedClasses = array(), $bSubtree = true) +{ + if ($iLevels <= 0) return; + $iLevels--; + + if (array_key_exists($sClass, $aVisitedClasses)) return; + $aVisitedClasses[$sClass] = true; + + if ($bSubtree) $oPage->add("
      \n"); + foreach (MetaModel::EnumParentClasses($sClass) as $sParentClass) + { + DisplayRelatedClassesBestInClass($oPage, $sParentClass, $iLevels, $aVisitedClasses, false); + } + ////$oPage->add("
      "); + foreach (MetaModel::EnumReferencedClasses($sClass) as $sExtKeyAttCode => $sRemoteClass) + { + $sVisited = (array_key_exists($sRemoteClass, $aVisitedClasses)) ? " ..." : ""; + if (MetaModel::GetAttributeOrigin($sClass, $sExtKeyAttCode) == $sClass) + { + $oPage->add("
    • $sClass| $sExtKeyAttCode =>".MakeClassHLink($sRemoteClass)."$sVisited
    • \n"); + DisplayRelatedClassesBestInClass($oPage, $sRemoteClass, $iLevels, $aVisitedClasses); + } + } + foreach (MetaModel::EnumReferencingClasses($sClass) as $sRemoteClass => $aRemoteKeys) + { + foreach ($aRemoteKeys as $sExtKeyAttCode) + { + $sVisited = (array_key_exists($sRemoteClass, $aVisitedClasses)) ? " ..." : ""; + $oPage->add("
    • $sClass| <=".MakeClassHLink($sRemoteClass)."::$sExtKeyAttCode$sVisited
    • \n"); + DisplayRelatedClassesBestInClass($oPage, $sRemoteClass, $iLevels, $aVisitedClasses); + } + } + ////$oPage->add("
      "); + if ($bSubtree) $oPage->add("
    \n"); +} + +/** + * Helper for the list of classes related to the given class + */ +function DisplayRelatedClasses($oPage, $sClass) +{ + $oPage->add("

    Childs

    \n"); + DisplaySubclasses($oPage, $sClass); + + $oPage->add("

    Pointed to by...

    \n"); + DisplayReferencingClasses($oPage, $sClass); + + $oPage->add("

    Linked to ...

    \n"); + DisplayLinkingClasses($oPage, $sClass); + + $oPage->add("

    ZE Graph ...

    \n"); + DisplayRelatedClassesBestInClass($oPage, $sClass, 4); +} + +/** + * Helper for the lifecycle details of a given class + */ +function DisplayLifecycle($oPage, $sClass) +{ + $sStateAttCode = MetaModel::GetStateAttributeCode($sClass); + if (empty($sStateAttCode)) + { + $oPage->p("no lifecycle for this class"); + } + else + { + $aStates = MetaModel::EnumStates($sClass); + $aStimuli = MetaModel::EnumStimuli($sClass); + $oPage->add("\n"); + $oPage->add("

    Transitions

    \n"); + $oPage->add("
      \n"); + foreach ($aStates as $sStateCode => $aStateDef) + { + $sStateLabel = $aStates[$sStateCode]['label']; + $sStateDescription = $aStates[$sStateCode]['description']; + $oPage->add("
    • $sStateLabel ($sStateDescription)
    • \n"); + $oPage->add("
        \n"); + foreach(MetaModel::EnumTransitions($sClass, $sStateCode) as $sStimulusCode => $aTransitionDef) + { + $sStimulusLabel = $aStimuli[$sStimulusCode]->Get('label'); + $sTargetStateLabel = $aStates[$aTransitionDef['target_state']]['label']; + if (count($aTransitionDef['actions']) > 0) + { + $sActions = " (".implode(', ', $aTransitionDef['actions']).")"; + } + else + { + $sActions = ""; + } + $oPage->add("
      • $sStimulusLabel => $sTargetStateLabel $sActions
      • \n"); + } + $oPage->add("
      \n"); + } + $oPage->add("
    \n"); + + $oPage->add("

    Attribute options

    \n"); + $oPage->add("
      \n"); + foreach ($aStates as $sStateCode => $aStateDef) + { + $sStateLabel = $aStates[$sStateCode]['label']; + $sStateDescription = $aStates[$sStateCode]['description']; + $oPage->add("
    • $sStateLabel ($sStateDescription)
    • \n"); + if (count($aStates[$sStateCode]['attribute_list']) > 0) + { + $oPage->add("
        \n"); + foreach($aStates[$sStateCode]['attribute_list'] as $sAttCode => $iOptions) + { + $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode); + $sAttLabel = $oAttDef->GetLabel(); + + $aOptions = array(); + if ($iOptions & OPT_ATT_HIDDEN) $aOptions[] = 'Hidden'; + if ($iOptions & OPT_ATT_READONLY) $aOptions[] = 'Read-only'; + if ($iOptions & OPT_ATT_MANDATORY) $aOptions[] = 'Mandatory'; + if ($iOptions & OPT_ATT_MUSTCHANGE) $aOptions[] = 'Must change'; + if ($iOptions & OPT_ATT_MUSTPROMPT) $aOptions[] = 'Must be proposed for changing'; + if (count($aOptions)) + { + $sOptions = implode(', ', $aOptions); + } + else + { + $sOptions = ""; + } + + $oPage->add("
      • $sAttLabel $sOptions
      • \n"); + } + $oPage->add("
      \n"); + } + else + { + $oPage->p("empty list"); + } + } + $oPage->add("
    \n"); + } +} + + +/** + * Display the list of classes from the business model + */ +function DisplayClassesList($oPage) +{ + $oPage->add("

    iTop objects schema

    \n"); + + $oPage->add("
      \n"); + foreach(MetaModel::EnumCategories() as $sCategory) + { + if (empty($sCategory)) continue; // means 'all' -> skip + + $sClosed = ($sCategory == 'bizmodel') ? '' : ' class="closed"'; + $oPage->add("Category $sCategory\n"); + + $oPage->add("
        \n"); + foreach(MetaModel::GetClasses($sCategory) as $sClassName) + { + if (MetaModel::IsStandaloneClass($sClassName)) + { + $oPage->add("
      • ".MakeClassHLink($sClassName)."
      • \n"); + } + else if (MetaModel::IsRootClass($sClassName)) + { + $oPage->add("
      • ".MakeClassHLink($sClassName)."\n"); + DisplaySubclasses($oPage, $sClassName); + $oPage->add("
      • \n"); + } + } + $oPage->add("
      \n"); + + $oPage->add("\n"); + } + $oPage->add("
    \n"); + + + $oPage->add("

    Relationships

    \n"); + + $oPage->add("
      \n"); + foreach (MetaModel::EnumRelations() as $sRelCode) + { + $oPage->add("
    • ".MakeRelationHLink($sRelCode)."\n"); + $oPage->add("
        \n"); + foreach (MetaModel::EnumRelationProperties($sRelCode) as $sProp => $sValue) + { + $oPage->add("
      • $sProp: ".htmlentities($sValue)."
      • \n"); + } + $oPage->add("
      \n"); + $oPage->add("
    • \n"); + } + $oPage->add("
    \n"); + $oPage->add_ready_script('$("#ClassesList").treeview();'); + $oPage->add_ready_script('$("#ClassesRelationships").treeview();'); +} + +/** + * Display the details of a given class of objects + */ +function DisplayClassDetails($oPage, $sClass) +{ + $oPage->p("

    $sClass


    \n".MetaModel::GetClassDescription($sClass)."
    \n"); + $oPage->p("

    Class Hierarchy

    "); + $oPage->p("[All classes]"); + // List the parent classes + $sParent = MetaModel::GetParentPersistentClass($sClass); + $aParents = array(); + $aParents[] = $sClass; + while($sParent != "" && $sParent != 'cmdbAbstractObject') + { + $aParents[] = $sParent; + $sParent = MetaModel::GetParentPersistentClass($sParent); + } + $iIndex = count($aParents); + $sSpace =""; + $oPage->add("
      "); + while ($iIndex > 0) + { + $iIndex--; + $oPage->add("
    • ".MakeClassHLink($aParents[$iIndex])."\n"); + $oPage->add("
        \n"); + } + for($iIndex = 0; $iIndex < count($aParents); $iIndex++) + { + $oPage->add("
      \n
    • \n"); + } + $oPage->add("
    \n"); + $oPage->add_ready_script('$("#ClassHierarchy").treeview();'); + $oPage->p(''); + $oPage->AddTabContainer('details'); + $oPage->SetCurrentTabContainer('details'); + // List the attributes of the object + $aDetails = array(); + foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode=>$oAttDef) + { + if ($oAttDef->IsExternalKey()) + { + $sValue = "External key to ".MakeClassHLink($oAttDef->GetTargetClass()); + } + else + { + $sValue = $oAttDef->GetDescription(); + } + $sType = $oAttDef->GetType().' ('.$oAttDef->GetTypeDesc().')'; + $sOrigin = MetaModel::GetAttributeOrigin($sClass, $sAttCode); + $sAllowedValues = ""; + $oAllowedValuesDef = $oAttDef->GetValuesDef(); + $sMoreInfo = ""; + if (is_subclass_of($oAttDef, 'AttributeDBFieldVoid')) + { + $aMoreInfo = array(); + $aMoreInfo[] = "Column: ".$oAttDef->GetSQLExpr().""; + $aMoreInfo[] = "Default: '".$oAttDef->GetDefaultValue()."'"; + $aMoreInfo[] = $oAttDef->IsNullAllowed() ? "Null allowed" : "Null NOT allowed"; + //$aMoreInfo[] = $oAttDef->DBGetUsedFields(); + $sMoreInfo .= implode(', ', $aMoreInfo); + } + + if (is_object($oAllowedValuesDef)) $sAllowedValues = $oAllowedValuesDef->GetValuesDescription(); + else $sAllowedValues = ''; + + $aDetails[] = array('code' => $oAttDef->GetCode(), 'type' => $sType, 'origin' => $sOrigin, 'label' => $oAttDef->GetLabel(), 'description' => $sValue, 'values' => $sAllowedValues, 'moreinfo' => $sMoreInfo); + } + $oPage->SetCurrentTab('Attributes'); + $aConfig = array( 'code' => array('label' => 'Attribute code', 'description' => 'Code of this attribute'), + 'label' => array('label' => 'Label', 'description' => 'Label of this attribute'), + 'type' => array('label' => 'Type', 'description' => 'Data type of this attribute'), + 'origin' => array('label' => 'Origin', 'description' => 'The base class for this attribute'), + 'description' => array('label' => 'Description', 'description' => 'Description of this attribute'), + 'values' => array('label' => 'Allowed Values', 'description' => 'Restrictions on the possible values for this attribute'), + 'moreinfo' => array('label' => 'More info', 'description' => 'More info for the fields related to a Database field'), + ); + $oPage->table($aConfig, $aDetails); + + // List the search criteria for this object + $aDetails = array(); + foreach (MetaModel::GetClassFilterDefs($sClass) as $sFilterCode => $oFilterDef) + { + $aOpDescs = array(); + foreach ($oFilterDef->GetOperators() as $sOpCode => $sOpDescription) + { + $sIsTheLooser = ($sOpCode == $oFilterDef->GetLooseOperator()) ? " (loose search)" : ""; + $aOpDescs[] = "$sOpCode ($sOpDescription)$sIsTheLooser"; + } + $aDetails[] = array( 'code' => $sFilterCode, 'description' => $oFilterDef->GetLabel(),'operators' => implode(" / ", $aOpDescs)); + } + $oPage->SetCurrentTab('Search criteria'); + $aConfig = array( 'code' => array('label' => 'Filter code', 'description' => 'Code of this search criteria'), + 'description' => array('label' => 'Description', 'description' => 'Description of this search criteria'), + 'operators' => array('label' => 'Available operators', 'description' => 'Possible operators for this search criteria') + ); + $oPage->table($aConfig, $aDetails); + + $oPage->SetCurrentTab('Child classes'); + DisplaySubclasses($oPage, $sClass); + + $oPage->SetCurrentTab('Referencing classes'); + DisplayReferencingClasses($oPage, $sClass); + + $oPage->SetCurrentTab('Related classes'); + DisplayRelatedClasses($oPage, $sClass); + + $oPage->SetCurrentTab('Lifecycle'); + DisplayLifecycle($oPage, $sClass); + + $oPage->SetCurrentTab(); + $oPage->SetCurrentTabContainer(); +} + + +/** + * Display the details of a given relation (e.g. "impacts") + */ +function DisplayRelationDetails($oPage, $sRelCode) +{ + $sDesc = MetaModel::GetRelationProperty($sRelCode, 'description'); + $sVerbDown = MetaModel::GetRelationProperty($sRelCode, 'verb_down'); + $sVerbUp = MetaModel::GetRelationProperty($sRelCode, 'verb_up'); + $oPage->add("

    Relation $sRelCode ($sDesc)

    "); + $oPage->p("Down: $sVerbDown"); + $oPage->p("Up: $sVerbUp"); + + $oPage->add("
      \n"); + foreach(MetaModel::GetClasses() as $sClass) + { + $aRelQueries = MetaModel::EnumRelationQueries($sClass, $sRelCode); + if (count($aRelQueries) > 0) + { + $oPage->add("
    • class ".MakeClassHLink($sClass)."\n"); + $oPage->add("
        \n"); + foreach ($aRelQueries as $sRelKey => $aQuery) + { + $sQuery = $aQuery['sQuery']; + $bPropagate = $aQuery['bPropagate'] ? "Propagate" : "Do not propagate"; + $iDistance = $aQuery['iDistance']; + + $oPage->add("
      • $sRelKey: $bPropagate ($iDistance) ".DBObjectSearch::SibuSQLAsHtml($sQuery)."
      • \n"); + } + $oPage->add("
      \n"); + $oPage->add("
    • \n"); + } + } + $oPage->add_ready_script('$("#RelationshipDetails").treeview();'); +} + + +require_once('../application/loginwebpage.class.inc.php'); +login_web_page::DoLogin(); // Check user rights and prompt if needed + +// Display the menu on the left +$oContext = new UserContext(); +$oAppContext = new ApplicationContext(); +$iActiveNodeId = utils::ReadParam('menu', -1); +$currentOrganization = utils::ReadParam('org_id', 1); +$operation = utils::ReadParam('operation', ''); + +$oPage = new iTopWebPage("iTop objects schema", $currentOrganization); +$oPage->no_cache(); + +$operation = utils::ReadParam('operation', ''); + +switch($operation) +{ + case 'details_class': + $sClass = utils::ReadParam('class', 'logRealObject'); + DisplayClassDetails($oPage, $sClass); + break; + + case 'details_relation': + $sRelCode = utils::ReadParam('relcode', ''); + DisplayRelationDetails($oPage, $sRelCode); + break; + + case 'details': + $oPage->p('operation=details has been deprecated, please use details_class'); + break; + case 'list': + default: + DisplayClassesList($oPage); +} + +$oPage->output(); +?> diff --git a/pages/sibusql.php b/pages/sibusql.php new file mode 100644 index 000000000..956d28d4d --- /dev/null +++ b/pages/sibusql.php @@ -0,0 +1,57 @@ +FYI: '$sClearText'
      \n"; + $oFilter = DBObjectSearch::unserialize($sExpression); + $sExpression = $oFilter->ToOQL(); + exit; +} +else +{ + // leave $sExpression as is +} + +$oP->add('
      '."\n"); +$oP->add('Expression to evaluate:
      '."\n"); +$oP->add(''."

      Example:
      SELECT bizPerson AS B WHERE B.name LIKE '%A%'

      \n"); +$oP->add(''."\n"); +$oP->add('
      '."\n"); + +if (!empty($sExpression)) +{ + $oFilter = DBObjectSearch::FromOQL($sExpression); + if ($oFilter) + { + $oP->p('Query expression: '.$oFilter->ToOQL()); + $oP->p('Serialized filter: '.$oFilter->serialize()); + + $oSet = new CMDBObjectSet($oFilter); + $oP->p('The query returned '.$oSet->count().' results(s)'); + cmdbAbstractObject::DisplaySet($oP, $oSet); + } +} + +$oP->output(); +?> diff --git a/pages/test.php b/pages/test.php new file mode 100644 index 000000000..e9e9bbea4 --- /dev/null +++ b/pages/test.php @@ -0,0 +1,126 @@ +Missing mandatory argument $sName

      "; + exit; + } + return $value; +} + +function IsAValidTestClass($sClassName) +{ + // Must be a child of TestHandler + // + if (!is_subclass_of($sClassName, 'TestHandler')) return false; + + // Must not be abstract + // + $oReflectionClass = new ReflectionClass($sClassName); + if (!$oReflectionClass->isInstantiable()) return false; + + return true; +} + +function DisplayEvents($aEvents, $sTitle) +{ + echo "

      $sTitle

      \n"; + if (count($aEvents) > 0) + { + echo "
        \n"; + foreach ($aEvents as $sEvent) + { + echo "
      • $sEvent
      • \n"; + } + echo "
      \n"; + } + else + { + echo "

      none

      \n"; + } +} + +/////////////////////////////////////////////////////////////////////////////// +// Main +/////////////////////////////////////////////////////////////////////////////// + + +require_once('../application/utils.inc.php'); +require_once('../core/test.class.inc.php'); +require_once('testlist.inc.php'); + +require_once('../core/cmdbobject.class.inc.php'); + +$sTodo = utils::ReadParam("todo", ""); +if ($sTodo == '') +{ + // Show the list of tests + // + echo "

      Existing tests

      \n"; + echo "
        \n"; + foreach (get_declared_classes() as $sClassName) + { + if (!IsAValidTestClass($sClassName)) continue; + + $sName = call_user_func(array($sClassName, 'GetName')); + $sDescription = call_user_func(array($sClassName, 'GetDescription')); + echo "
      • $sName ($sDescription)\n"; +} +else if ($sTodo == 'exec') +{ + // Execute a test + // + $sTestClass = ReadMandatoryParam("testid"); + + if (!IsAValidTestClass($sTestClass)) + { + echo "

        Wrong value for testid, expecting a valid class name

        \n"; + } + else + { + $oTest = new $sTestClass(); + echo "

        Testing: ".$oTest->GetName()."

        \n"; + $bRes = $oTest->Execute(); + } + +/* +MyHelpers::var_dump_html($oTest->GetResults()); +MyHelpers::var_dump_html($oTest->GetWarnings()); +MyHelpers::var_dump_html($oTest->GetErrors()); +*/ + + if ($bRes) + { + echo "

        Success :-)

        \n"; + DisplayEvents($oTest->GetResults(), 'Results'); + } + else + { + echo "

        Failure :-(

        \n"; + } + DisplayEvents($oTest->GetErrors(), 'Errors'); + DisplayEvents($oTest->GetWarnings(), 'Warnings'); + + // Render the output + // + echo "

        Actual output

        \n"; + echo "
        \n"; + echo $oTest->GetOutput(); + echo "
        \n"; +} +else +{ +} + + +?> diff --git a/pages/testlist.inc.php b/pages/testlist.inc.php new file mode 100644 index 000000000..947d48489 --- /dev/null +++ b/pages/testlist.inc.php @@ -0,0 +1,1132 @@ +new FieldExpression('column1', 'myTableAlias'), 'column2'=>new FieldExpression('column2', 'myTableAlias')), + $oCondition = new BinaryExpression(new FieldExpression('column1', 'myTableAlias'), 'LIKE', new ScalarExpression('trash')), + $aFullTextNeedles = array('column1'), + $bToDelete = false, + $aValues = array() + ); + $oQuery->AddCondition(Expression::FromOQL('DATE(NOW() - 1200 * 2) > \'2008-07-31\'')); + + $oSubQuery1 = new SQLQuery( + $sTable = 'myTable1', + $sTableAlias = 'myTable1Alias', + $aFields = array('column1_1'=>new FieldExpression('column1', 'myTableAlias'), 'column1_2'=>new FieldExpression('column1', 'myTableAlias')), + $oCondition = new TrueSQLExpression, + $aFullTextNeedles = array(), + $bToDelete = false, + $aValues = array() + ); + + $oSubQuery2 = new SQLQuery( + $sTable = 'myTable2', + $sTableAlias = 'myTable2Alias', + $aFields = array('column2_1'=>new FieldExpression('column2', 'myTableAlias'), 'column2_2'=>new FieldExpression('column2', 'myTableAlias')), + $oCondition = new TrueSQLExpression, + $aFullTextNeedles = array(), + $bToDelete = false, + $aValues = array() + ); + + $oQuery->AddInnerJoin($oSubQuery1, 'column1', 'column1_1'); + $oQuery->AddLeftJoin($oSubQuery2, 'column2', 'column2_2'); + + $oQuery->DisplayHtml(); + $oQuery->RenderDelete(); + $oQuery->RenderUpdate(); + echo '

        '.$oQuery->RenderSelect().'

        '; + $oQuery->RenderSelect(array('column1')); + $oQuery->RenderSelect(array('column1', 'column2')); + } +} + +class TestOQLParser extends TestFunction +{ + static public function GetName() {return 'Check OQL parsing';} + static public function GetDescription() {return 'Attempts a series of queries, and in particular those with a bad syntax';} + + protected function CheckQuery($sQuery, $bIsCorrectQuery) + { + $oOql = new OqlInterpreter($sQuery); + try + { + $oTrash = $oOql->ParseQuery(); + MyHelpers::var_dump_html($oTrash, true); + } + catch (OQLException $OqlException) + { + if ($bIsCorrectQuery) + { + echo "

        More info on this unexpected failure:
        ".$OqlException->getHtmlDesc()."

        \n"; + throw $OqlException; + return false; + } + else + { + // Everything is fine :-) + echo "

        More info on this expected failure:
        ".$OqlException->getHtmlDesc()."

        \n"; + return true; + } + } + // The query was correctly parsed, was it expected to be correct ? + if ($bIsCorrectQuery) + { + return true; + } + else + { + throw new UnitTestException("The query '$sQuery' was parsed with success, while it shouldn't (?)"); + return false; + } + } + + protected function TestQuery($sQuery, $bIsCorrectQuery) + { + if (!$this->CheckQuery($sQuery, $bIsCorrectQuery)) + { + return false; + } + return true; + } + + public function DoExecute() + { + $aQueries = array( + 'SELECT toto' => true, + 'SELECT toto WHERE toto.a = 1' => true, + 'SELECT toto WHERE toto.a=1' => true, + 'SELECT toto WHERE toto.a = "1"' => true, + 'SELECT toto WHHHERE toto.a = "1"' => false, + 'SELECT toto WHERE toto.a == "1"' => false, + 'SELECT toto WHERE toto.a % 1' => false, + //'SELECT toto WHERE toto.a LIKE 1' => false, + 'SELECT toto WHERE toto.a like \'arg\'' => false, + 'SELECT toto WHERE toto.a NOT LIKE "That\'s it"' => true, + 'SELECT toto WHERE toto.a NOT LIKE "That\'s "it""' => false, + 'SELECT toto WHERE toto.a NOT LIKE "That\'s \\"it\\""' => true, + 'SELECT toto WHERE toto.a NOT LIKE \'That"s it\'' => true, + 'SELECT toto WHERE toto.a NOT LIKE \'That\'s it\'' => false, + 'SELECT toto WHERE toto.a NOT LIKE \'That\\\'s it\'' => true, + 'SELECT toto WHERE toto.a NOT LIKE "blah \\ truc"' => false, + 'SELECT toto WHERE toto.a NOT LIKE "blah \\\\ truc"' => true, + 'SELECT toto WHERE toto.a NOT LIKE \'blah \\ truc\'' => false, + 'SELECT toto WHERE toto.a NOT LIKE \'blah \\\\ truc\'' => true, + + 'SELECT toto WHERE toto.a NOT LIKE "\\\\"' => true, + 'SELECT toto WHERE toto.a NOT LIKE "\\""' => true, + 'SELECT toto WHERE toto.a NOT LIKE "\\"\\\\"' => true, + 'SELECT toto WHERE toto.a NOT LIKE "\\\\\\""' => true, + 'SELECT toto WHERE toto.a NOT LIKE ""' => true, + 'SELECT toto WHERE toto.a NOT LIKE "\\\\"' => true, + "SELECT UserRightsMatrixClassGrant WHERE UserRightsMatrixClassGrant.class = 'lnkContactRealObject' AND UserRightsMatrixClassGrant.action = 'modify' AND UserRightsMatrixClassGrant.login = 'Denis'" => true, + "SELECT A WHERE A.col1 = 'lit1' AND A.col2 = 'lit2' AND A.col3 = 'lit3'" => true, + + 'SELECT toto WHERE toto.a NOT LIKE "blah" AND toto.b LIKE "foo"' => true, + + //'SELECT toto WHERE toto.a > \'asd\'' => false, + 'SELECT toto WHERE toto.a = 1 AND toto.b LIKE "x" AND toto.f >= 12345' => true, + 'SELECT Device JOIN Site ON Device.site = Site.id' => true, + 'SELECT Device JOIN Site ON Device.site = Site.id JOIN Country ON Site.location = Country.id' => true, + + "SELECT A JOIN B ON A.myB = B.id WHERE (A.col1 = 123 AND B.col1 = 'aa') OR (A.col3 = 'zzz' AND B.col4 > 100)" => true, + "SELECT A JOIN B ON A.myB = B.id WHERE (A.col1 = B.col2 AND B.col1 = A.col2) OR (A.col3 = '' AND B.col4 > 100)" => true, + "SELECT A JOIN B ON A.myB = B.id WHERE A.col1 + B.col2 * B.col1 = A.col2" => true, + "SELECT A JOIN B ON A.myB = B.id WHERE A.col1 + (B.col2 * B.col1) = A.col2" => true, + "SELECT A JOIN B ON A.myB = B.id WHERE (A.col1 + B.col2) * B.col1 = A.col2" => true, + + 'SELECT Device AS D_ JOIN Site AS S_ ON D_.site = S_.id WHERE S_.country = "Francia"' => true, + ); + + $iErrors = 0; + + foreach($aQueries as $sQuery => $bIsCorrectQuery) + { + $sIsOk = $bIsCorrectQuery ? 'good' : 'bad'; + echo "

        Testing query: $sQuery ($sIsOk)

        \n"; + $bRet = $this->TestQuery($sQuery, $bIsCorrectQuery); + if (!$bRet) $iErrors++; + } + + return ($iErrors == 0); + } +} + + +class TestGenericItoMyModel extends TestBizModelGeneric +{ + static public function GetName() + { + return 'Generic RO test on '.self::GetConfigFile(); + } + + static public function GetConfigFile() {return '../config-test-mymodel.php';} +} + +class TestGenericItopBigModel extends TestBizModelGeneric +{ + static public function GetName() + { + return 'Generic RO test on '.self::GetConfigFile(); + } + + static public function GetConfigFile() {return '../config-test-itopv06.php';} +} + +class TestUserRightsMatrixItop extends TestUserRights +{ + static public function GetName() + { + return 'User rights test on user rights matrix'; + } + + static public function GetDescription() + { + return 'blah blah blah'; + } + + public function DoPrepare() + { + parent::DoPrepare(); + MetaModel::Startup('../config-test-itopv06.php'); + } + + protected function DoExecute() + { + $sUser = 'Romain'; + echo "

        Totor: ".(UserRights::Login('Totor', 'toto') ? 'ok' : 'NO')."

        \n"; + echo "

        Romain: ".(UserRights::Login('Romain', 'toto') ? 'ok' : 'NO')."

        \n"; + echo "

        User: ".UserRights::GetUser()."

        \n"; + echo "

        On behalf of...".UserRights::GetRealUser()."

        \n"; + + echo "

        Denis (impersonate) : ".(UserRights::Impersonate('Denis', 'tutu') ? 'ok' : 'NO')."

        \n"; + echo "

        User: ".UserRights::GetUser()."

        \n"; + echo "

        On behalf of...".UserRights::GetRealUser()."

        \n"; + + UserRights::GetFilter('bizOrganization'); // returns a filter object + + $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT bizOrganization")); + echo "

        IsActionAllowed...".(UserRights::IsActionAllowed('bizOrganization', UR_ACTION_MODIFY, $oSet) == UR_ALLOWED_YES ? 'ok' : 'NO')."

        \n"; + echo "

        IsStimulusAllowed...".(UserRights::IsStimulusAllowed('bizOrganization', 'myStimulus', $oSet) == UR_ALLOWED_YES ? 'ok' : 'NO')."

        \n"; + echo "

        IsActionAllowedOnAttribute...".(UserRights::IsActionAllowedOnAttribute('bizOrganization', 'myattribute', UR_ACTION_MODIFY, $oSet) == UR_ALLOWED_YES ? 'ok' : 'NO')."

        \n"; + return true; + } +} + +/////////////////////////////////////////////////////////////////////////// +// Test a complex biz model on the fly +/////////////////////////////////////////////////////////////////////////// + +class TestMyBizModel extends TestBizModel +{ + static public function GetName() + { + return 'A series of tests on a weird business model'; + } + + static public function GetDescription() + { + return 'Attempts various operations and build complex queries'; + } + + static public function GetConfigFile() {return '../config-test-mymodel.php';} + + function test_linksinfo() + { + echo "

        Enum links

        "; + MyHelpers::var_dump_html(MetaModel::EnumReferencedClasses("cmdbTeam")); + MyHelpers::var_dump_html(MetaModel::EnumReferencingClasses("Organization")); + + MyHelpers::var_dump_html(MetaModel::EnumLinkingClasses()); + MyHelpers::var_dump_html(MetaModel::EnumLinkingClasses("cmdbContact")); + MyHelpers::var_dump_html(MetaModel::EnumLinkingClasses("cmdWorkshop")); + MyHelpers::var_dump_html(MetaModel::GetLinkLabel("Liens_entre_contacts_et_workshop", "toworkshop")); + } + + function test_list_attributes() + { + echo "

        List attributes

        "; + foreach(MetaModel::ListAttributeDefs("cmdbTeam") as $sAttCode=>$oAttDef) + { + echo $oAttDef->GetLabel()." / ".$oAttDef->GetDescription()." / ".$oAttDef->GetType()."
        \n"; + } + } + + function test_search() + { + echo "

        Two searches

        "; + $oFilterAllDevs = new DBObjectSearch("cmdbTeam"); + $oAllDevs = new DBObjectSet($oFilterAllDevs); + + echo "Found ".$oAllDevs->Count()." items.
        \n"; + while ($oDev = $oAllDevs->Fetch()) + { + $aValues = array(); + foreach(MetaModel::GetAttributesList($oAllDevs->GetClass()) as $sAttCode) + { + $aValues[] = MetaModel::GetLabel(get_class($oDev), $sAttCode)." (".MetaModel::GetDescription(get_class($oDev), $sAttCode).") = ".$oDev->GetAsHTML($sAttCode); + } + echo $oDev->GetKey()." => ".implode(", ", $aValues)."
        \n"; + } + + // a second one + $oMyFilter = new DBObjectSearch("cmdbContact"); + //$oMyFilter->AddCondition("name", "aii", "Finishes with"); + $oMyFilter->AddCondition("name", "aii"); + $this->search_and_show_list($oMyFilter); + + } + + function test_reload() + { + echo "

        Reload

        "; + $team = MetaModel::GetObject("cmdbContact", "2"); + echo "Chargement de l'attribut headcount: {$team->Get("headcount")}
        \n"; + MyHelpers::var_dump_html($team); + } + + function test_setattribute() + { + echo "

        Set attribute and update

        "; + $team = MetaModel::GetObject("cmdbTeam", "2"); + $team->Set("headcount", rand(1,1000)); + $team->Set("email", "Luis ".rand(9,250)); + MyHelpers::var_dump_html($team->ListChanges()); + echo "New headcount = {$team->Get("headcount")}
        \n"; + echo "Computed name = {$team->Get("name")}
        \n"; + + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + $oMyChange->Set("userinfo", "test_setattribute / Made by robot #".rand(1,100)); + $iChangeId = $oMyChange->DBInsert(); + + //MetaModel::StartDebugQuery(); + $team->DBUpdateTracked($oMyChange); + //MetaModel::StopDebugQuery(); + + echo "

        Check the modified team

        "; + $oTeam = MetaModel::GetObject("cmdbTeam", "2"); + MyHelpers::var_dump_html($oTeam); + } + function test_newobject() + { + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + $oMyChange->Set("userinfo", "test_newobject / Made by robot #".rand(1,100)); + $iChangeId = $oMyChange->DBInsert(); + + echo "

        Create a new object (team)

        "; + $oNewTeam = MetaModel::NewObject("cmdbTeam"); + $oNewTeam->Set("name", "ekip2choc #".rand(1000, 2000)); + $oNewTeam->Set("email", "machin".rand(1,100)."@tnut.com"); + $oNewTeam->Set("email", null); + $oNewTeam->Set("owner", "ITOP"); + $oNewTeam->Set("headcount", "0".rand(38000, 38999)); // should be reset to an int value + $iId = $oNewTeam->DBInsertTracked($oMyChange); + echo "Created new team: $iId
        "; + echo "

        Delete team #$iId

        "; + $oTeam = MetaModel::GetObject("cmdbTeam", $iId); + $oTeam->DBDeleteTracked($oMyChange); + echo "Deleted team: $iId
        "; + MyHelpers::var_dump_html($oTeam); + } + + + function test_updatecolumn() + { + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + $oMyChange->Set("userinfo", "test_updatecolumn / Made by robot #".rand(1,100)); + $iChangeId = $oMyChange->DBInsert(); + + $sNewEmail = "updatecol".rand(9,250)."@quedlaballe.com"; + echo "

        Update a the email: set to '$sNewEmail'

        "; + $oMyFilter = new DBObjectSearch("cmdbContact"); + $oMyFilter->AddCondition("name", "o", "Contains"); + + echo "Candidates before:
        "; + $this->search_and_show_list($oMyFilter); + + MetaModel::BulkUpdateTracked($oMyChange, $oMyFilter, array("email" => $sNewEmail)); + + echo "Candidates after:
        "; + $this->search_and_show_list($oMyFilter); + } + + function test_error() + { + trigger_error("Stop requested", E_USER_ERROR); + } + + function test_changetracking() + { + echo "

        Create a change

        "; + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + $oMyChange->Set("userinfo", "Made by robot #".rand(1,100)); + $iChangeId = $oMyChange->DBInsert(); + echo "Created new change: $iChangeId
        "; + MyHelpers::var_dump_html($oMyChange); + + echo "

        Create a new object (team)

        "; + $oNewTeam = MetaModel::NewObject("cmdbTeam"); + $oNewTeam->Set("name", "ekip2choc #".rand(1000, 2000)); + $oNewTeam->Set("email", "machin".rand(1,100)."@tnut.com"); + $oNewTeam->Set("email", null); + $oNewTeam->Set("owner", "ITOP"); + $oNewTeam->Set("headcount", "0".rand(38000, 38999)); // should be reset to an int value + $iId = $oNewTeam->DBInsertTracked($oMyChange); + echo "Created new team: $iId
        "; + echo "

        Delete team #$iId

        "; + $oTeam = MetaModel::GetObject("cmdbTeam", $iId); + $oTeam->DBDeleteTracked($oMyChange); + echo "Deleted team: $iId
        "; + MyHelpers::var_dump_html($oTeam); + } + + function test_zlist() + { + echo "

        Test ZLists

        "; + $aZLists = MetaModel::EnumZLists(); + foreach ($aZLists as $sListCode) + { + $aListInfos = MetaModel::GetZListInfo($sListCode); + echo "

        List '".$sListCode."' (".$aListInfos["description"].") of type '".$aListInfos["type"]."'

        \n"; + + foreach (MetaModel::GetSubclasses("cmdbObjectHomeMade") as $sKlass) + { + $aItems = MetaModel::GetZListItems($sKlass, $sListCode); + if (count($aItems) == 0) continue; + + echo "$sKlass - $sListCode : {".implode(", ", $aItems)."}
        \n"; + } + } + + echo "

        IsAttributeInZList()...

        "; + echo "Liens_entre_contacts_et_workshop::ws_info in list1 ? ".(MetaModel::IsAttributeInZList("Liens_entre_contacts_et_workshop", "list1", "ws_info") ? "yes" : "no")."
        \n"; + echo "Liens_entre_contacts_et_workshop::toworkshop in list1 ? ".(MetaModel::IsAttributeInZList("Liens_entre_contacts_et_workshop", "list1", "toworkshop") ? "yes" : "no")."
        \n"; + + } + + function test_SibuSQL() + { + echo "

        Simple But Structured Query Language

        "; + + $oMyFilter = new DBObjectSearch("cmdbContact"); + echo "Tous les contacts: ".$oMyFilter->ToSibuSQL()."
        \n"; + $oNewFilter = DBObjectSearch::FromSibuSQL($oMyFilter->ToSibuSQL()); + echo "En passant par un filtre, ca revient en : ".$oNewFilter->ToSibuSQL()."
        \n"; + $this->search_and_show_list($oNewFilter); + + $sFilterDesc = "cmdbContact: name Begins with '$[debutnom:as:debut du nom]' AND ownername NotLike $[ddd::]"; + echo "Construction d'un filtre a partir de sa description en SibuSQL: $sFilterDesc
        \n"; + + MyHelpers::var_dump_html(DBObjectSearch::ListSibusQLParams($sFilterDesc)); + $oNewFilter = DBObjectSearch::FromSibuSQL($sFilterDesc, array('ddd'=>123)); + echo "Ca revient en: ".$oNewFilter->ToSibuSQL(); + } + + function test_pkey() + { + echo "

        Test search on pkey

        "; + $sExpr1 = "cmdbContact: pkey IN {40, 42}"; + $sExpr2 = "cmdbContact: pkey NOTIN {40, 42}"; + $this->search_and_show_list_from_sibusql($sExpr1); + $this->search_and_show_list_from_sibusql($sExpr2); + + echo "Et maintenant, on fusionne....
        \n"; + $oSet1 = new CMDBObjectSet(DBObjectSearch::FromSibuSQL($sExpr1)); + $oSet2 = new CMDBObjectSet(DBObjectSearch::FromSibuSQL($sExpr2)); + $oIntersect = $oSet1->CreateIntersect($oSet2); + $oDelta = $oSet1->CreateDelta($oSet2); + + $oMerge = clone $oSet1; + $oMerge->Merge($oSet2); + $oMerge->Merge($oSet2); + + echo "Set1 - Found ".$oSet1->Count()." items.
        \n"; + echo "Set2 - Found ".$oSet2->Count()." items.
        \n"; + echo "Intersect - Found ".$oIntersect->Count()." items.
        \n"; + echo "Delta - Found ".$oDelta->Count()." items.
        \n"; + echo "Merge - Found ".$oMerge->Count()." items.
        \n"; + //$this->show_list($oObjSet); + } + + function test_relations() + { + echo "

        Test relations

        "; + + //MyHelpers::var_dump_html(MetaModel::EnumRelationQueries("cmdbObjectHomeMade", "Potes")); + MyHelpers::var_dump_html(MetaModel::EnumRelationQueries("cmdbContact", "Potes")); + + $iMaxDepth = 9; + echo "Max depth = $iMaxDepth
        \n"; + + $oObj = MetaModel::GetObject("cmdbContact", 18); + $aRels = $oObj->GetRelatedObjects("Potes", $iMaxDepth); + echo $oObj->Get('name')." has some 'Potes'...
        \n"; + foreach ($aRels as $sClass => $aObjs) + { + echo "$sClass, count = ".count($aObjs)." => ".implode(', ', array_keys($aObjs))."
        \n"; + $oObjectSet = CMDBObjectSet::FromArray($sClass, $aObjs); + $this->show_list($oObjectSet); + } + + echo "

        Test relations - same results, by the mean of a SibuSQL

        "; + $this->search_and_show_list_from_sibusql("cmdbContact: RELATED (Potes, $iMaxDepth) TO (cmdbContact: pkey = 18)"); + + } + + function test_linkedset() + { + echo "

        Linked set attributes

        \n"; + $oObj = MetaModel::GetObject("cmdbContact", 18); + + echo "
        Current workshops
        \n"; + $oSetWorkshopsCurr = $oObj->Get("myworkshops"); + $this->show_list($oSetWorkshopsCurr); + + echo "
        Setting workshops
        \n"; + $oNewLink = new cmdbLiens(); + $oNewLink->Set('toworkshop', 2); + $oNewLink->Set('function', 'mafonctioooon'); + $oNewLink->Set('a1', 'tralala1'); + $oNewLink->Set('a2', 'F7M'); + $oSetWorkshops = CMDBObjectSet::FromArray("cmdbLiens", array($oNewLink)); + $oObj->Set("myworkshops", $oSetWorkshops); + $this->show_list($oSetWorkshops); + + echo "
        New workshops
        \n"; + $oSetWorkshopsCurr = $oObj->Get("myworkshops"); + $this->show_list($oSetWorkshopsCurr); + + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + $oMyChange->Set("userinfo", "test_linkedset / Made by robot #".rand(1,100)); + $iChangeId = $oMyChange->DBInsert(); + $oObj->DBUpdateTracked($oMyChange); + $oObj = MetaModel::GetObject("cmdbContact", 18); + + echo "
        After the write
        \n"; + $oSetWorkshopsCurr = $oObj->Get("myworkshops"); + $this->show_list($oSetWorkshopsCurr); + } + + function test_object_lifecycle() + { + echo "

        Test object lifecycle

        "; + + + MyHelpers::var_dump_html(MetaModel::GetStateAttributeCode("cmdbContact")); + MyHelpers::var_dump_html(MetaModel::EnumStates("cmdbContact")); + MyHelpers::var_dump_html(MetaModel::EnumStimuli("cmdbContact")); + foreach(MetaModel::EnumStates("cmdbContact") as $sStateCode => $aStateDef) + { + echo "

        Transition from $sStateCode

        \n"; + MyHelpers::var_dump_html(MetaModel::EnumTransitions("cmdbContact", $sStateCode)); + } + + $oObj = MetaModel::GetObject("cmdbContact", 18); + echo "Current state: ".$oObj->GetState()."... let's go to school..."; + MyHelpers::var_dump_html($oObj->EnumTransitions()); + $oObj->ApplyStimulus("toschool"); + echo "New state: ".$oObj->GetState()."... let's get older..."; + MyHelpers::var_dump_html($oObj->EnumTransitions()); + $oObj->ApplyStimulus("raise"); + echo "New state: ".$oObj->GetState()."... let's try to go further... (should give an error)"; + MyHelpers::var_dump_html($oObj->EnumTransitions()); + $oObj->ApplyStimulus("raise"); // should give an error + } + + + protected function DoExecute() + { +// $this->ReportError("Found two different SibuSQL expression out of the (same?) filter: $sExpr1 != $sExpr2"); +// $this->ReportSuccess('Found '.$oSet->Count()." objects of class $sClassName"); + //$this->test_linksinfo(); + //$this->test_list_attributes(); + //$this->test_search(); + //$this->test_reload(); + //$this->test_newobject(); + $this->test_setattribute(); + //$this->test_updatecolumn(); + //$this->test_error(); + //$this->test_changetracking(); + $this->test_zlist(); + $this->test_SibuSQL(); + //$this->test_pkey(); + $this->test_relations(); + $this->test_linkedset(); + $this->test_object_lifecycle(); + return true; + } +} + + +/////////////////////////////////////////////////////////////////////////// +// Test a complex biz model on the fly +/////////////////////////////////////////////////////////////////////////// + +class TestQueriesOnFarm extends TestBizModel +{ + static public function GetName() + { + return 'Farm test'; + } + + static public function GetDescription() + { + return 'A series of tests on the farm business model (SQL generation)'; + } + + static public function GetConfigFile() {return '../config-test-farm.php';} + + protected function DoPrepare() + { + parent::DoPrepare(); + $this->ResetDB(); + MetaModel::DBCheckIntegrity(); + } + + protected $m_oChange; + protected function ObjectToDB(CMDBObject $oNew) + { + if (!isset($this->m_oChange)) + { + new CMDBChange(); + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + $oMyChange->Set("userinfo", "Administrator"); + $iChangeId = $oMyChange->DBInsertNoReload(); + $this->m_oChange = $oMyChange; + } + $iId = $oNew->DBInsertTrackedNoReload($this->m_oChange); + return $iId; +} + + protected function InsertMammal($sSpecies, $sSex, $iSpeed, $iMotherid, $iFatherId, $sName, $iHeight, $sBirth) + { + $oNew = MetaModel::NewObject('Mammal'); + $oNew->Set('species', $sSpecies); + $oNew->Set('sex', $sSex); + $oNew->Set('speed', $iSpeed); + $oNew->Set('mother', $iMotherid); + $oNew->Set('father', $iFatherId); + $oNew->Set('name', $sName); + $oNew->Set('height', $iHeight); + $oNew->Set('birth', $sBirth); + return $this->ObjectToDB($oNew); + } + + protected function InsertBird($sSpecies, $sSex, $iSpeed, $iMotherid, $iFatherId) + { + $oNew = MetaModel::NewObject('Bird'); + $oNew->Set('species', $sSpecies); + $oNew->Set('sex', $sSex); + $oNew->Set('speed', $iSpeed); + $oNew->Set('mother', $iMotherid); + $oNew->Set('father', $iFatherId); + return $this->ObjectToDB($oNew); + } + + protected function InsertFlyingBird($sSpecies, $sSex, $iSpeed, $iMotherid, $iFatherId, $iFlyingSpeed) + { + $oNew = MetaModel::NewObject('FlyingBird'); + $oNew->Set('species', $sSpecies); + $oNew->Set('sex', $sSex); + $oNew->Set('speed', $iSpeed); + $oNew->Set('mother', $iMotherid); + $oNew->Set('father', $iFatherId); + $oNew->Set('flyingspeed', $iFlyingSpeed); + return $this->ObjectToDB($oNew); + } + + private function InsertGroup($sName, $iLeaderId) + { + $oNew = MetaModel::NewObject('Group'); + $oNew->Set('name', $sName); + $oNew->Set('leader', $iLeaderId); + $iId = $oNew->DBInsertNoReload(); + return $iId; + } + + protected function CheckQuery($sQuery, $bIsCorrectQuery) + { + if ($bIsCorrectQuery) + { + echo "

        $sQuery

        \n"; + } + else + { + echo "

        $sQuery

        \n"; + } + try + { + //$oOql = new OqlInterpreter($sQuery); + //$oTrash = $oOql->ParseQuery(); + //MyHelpers::var_dump_html($oTrash, true); + $oMyFilter = DBObjectSearch::FromOQL($sQuery); + } + catch (OQLException $oOqlException) + { + if ($bIsCorrectQuery) + { + echo "

        More info on this unexpected failure:
        ".$oOqlException->getHtmlDesc()."

        \n"; + throw $oOqlException; + return false; + } + else + { + // Everything is fine :-) + echo "

        More info on this expected failure:\n"; + echo "

          \n"; + echo "
        • ".get_class($oOqlException)."
        • \n"; + echo "
        • ".$oOqlException->getMessage()."
        • \n"; + echo "
        • ".$oOqlException->getHtmlDesc()."
        • \n"; + echo "
        \n"; + echo "

        \n"; + return true; + } + } + // The query was correctly parsed, was it expected to be correct ? + if (!$bIsCorrectQuery) + { + throw new UnitTestException("The query '$sQuery' was parsed with success, while it shouldn't (?)"); + return false; + } + echo "

        To OQL: ".$oMyFilter->ToOQL()."

        "; + + $this->search_and_show_list($oMyFilter); + + //echo "

        first pass

        \n"; + //MyHelpers::var_dump_html($oMyFilter, true); + $sQuery1 = MetaModel::MakeSelectQuery($oMyFilter); + //echo "

        second pass

        \n"; + //MyHelpers::var_dump_html($oMyFilter, true); + //$sQuery1 = MetaModel::MakeSelectQuery($oMyFilter); + + $sSerialize = $oMyFilter->serialize(); + echo "

        Serialized:$sSerialize

        \n"; + $oFilter2 = DBObjectSearch::unserialize($sSerialize); + try + { + $sQuery2 = MetaModel::MakeSelectQuery($oFilter2); + } + catch (Exception $e) + { + echo "

        Could not compute the query after unserialize

        \n"; + echo "

        Query 1: $sQuery1

        \n"; + MyHelpers::var_cmp_html($oMyFilter, $oFilter2); + throw $e; + } + //if ($oFilter2 != $oMyFilter) no, they may differ while the resulting query is the same! + if ($sQuery1 != $sQuery2) + { + echo "

        serialize/unserialize mismatch :-(

        \n"; + MyHelpers::var_cmp_html($sQuery1, $sQuery2); + MyHelpers::var_cmp_html($oMyFilter, $oFilter2); + return false; + } + return true; + } + + protected function DoExecute() + { +// $this->ReportError("Found two different SibuSQL expression out of the (same?) filter: $sExpr1 != $sExpr2"); +// $this->ReportSuccess('Found '.$oSet->Count()." objects of class $sClassName"); + echo "

        Create protagonists...

        "; + + $iId1 = $this->InsertMammal('human', 'male', 10, 0, 0, 'romanoff', 192, '1971-07-19'); + $iId2 = $this->InsertMammal('human', 'female', 9, 0, 0, 'rouanita', 165, '1983-01-23'); + $this->InsertMammal('human', 'female', 3, $iId2, $iId1, 'pomme', 169, '2008-02-23'); + $this->InsertMammal('pig', 'female', 3, 0, 0, 'grouinkette', 85, '2006-06-01'); + $this->InsertMammal('donkey', 'female', 3, 0, 0, 'muleta', 124, '2003-11-11'); + + $this->InsertBird('rooster', 'male', 12, 0, 0); + $this->InsertFlyingBird('pie', 'female', 11, 0, 0, 35); + + // Benchmarking + // + if (false) + { + define ('COUNT_BENCHMARK', 10); + echo "

        Parsing a long query, ".COUNT_BENCHMARK." times

        "; + $sQuery = "SELECT Animal AS Child JOIN Mammal AS Dad ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id WHERE Dad.birth < DATE_SUB(CURRENT_DATE(), INTERVAL 10 YEAR) AND Dad.height * 2 <= ROUND(TO_DAYS(Dad.birth) / (3 + 1) * 5 - 3)"; + + $fStart = MyHelpers::getmicrotime(); + for($i=0 ; $i < COUNT_BENCHMARK ; $i++) + { + $oMyFilter = DBObjectSearch::FromOQL($sQuery); + } + $fDuration = MyHelpers::getmicrotime() - $fStart; + $fParsingDuration = $fDuration / COUNT_BENCHMARK; + echo "

        Mean time by op: $fParsingDuration

        "; + } + + echo "

        Test queries...

        "; + + $aQueries = array( + 'SELECT Animal' => true, + 'SELECT Animal WHERE Animal.pkey = 1' => false, + 'SELECT Animal WHERE Animal.id = 1' => true, + 'SELECT Aniiimal' => false, + 'SELECTe Animal' => false, + 'SELECT * FROM Animal' => false, + 'SELECT Animal AS zoo WHERE zoo.species = \'human\'' => true, + 'SELECT Animal AS zoo WHERE species = \'human\'' => true, + 'SELECT Animal AS zoo WHERE espece = \'human\'' => false, + 'SELECT Animal AS zoo WHERE zoo.species IN (\'human\', "pig")' => true, + 'SELECT Animal AS zoo WHERE CONCATENATION(zoo.species, zoo.sex) LIKE "hum%male"' => false, + 'SELECT Animal AS zoo WHERE CONCAT(zoo.species, zoo.sex) LIKE "hum%male"' => true, + 'SELECT Animal AS zoo WHERE zoo.species NOT IN (\'human\', "pig")' => true, + 'SELECT Animal AS zoo WHERE zoo.kind = \'human\'' => false, + 'SELECT Animal WHERE Animal.species = \'human\' AND Animal.sex = \'female\'' => true, + 'SELECT Mammal AS x WHERE (x.species = \'human\' AND x.name LIKE \'ro%\') OR (x.species = \'donkey\' AND x.name LIKE \'po%\')' => true, + 'SELECT Mammal AS x WHERE x.species = \'human\' AND x.name LIKE \'ro%\' OR x.species = \'donkey\' AND x.name LIKE \'po%\'' => true, + 'SELECT Mammal AS m WHERE MONTH(m.birth) = 7' => true, + 'SELECT Mammal AS m WHERE DAY(m.birth) = 19' => true, + 'SELECT Mammal AS m WHERE YEAR(m.birth) = 1971' => true, + 'SELECT Mammal AS m WHERE m.birth < DATE_SUB(CURRENT_DATE(), INTERVAL 10 YEAR)' => true, + 'SELECT Mammal AS m WHERE m.birth > DATE_SUB(NOW(), INTERVAL 2000 DAY)' => true, + 'SELECT Mammal AS m WHERE (TO_DAYS(NOW()) - TO_DAYS(m.birth)) > 2000' => true, + 'SELECT Mammal AS m WHERE m.name = IF(FLOOR(ROUND(m.height)) > 2, "pomme", "romain")' => true, + 'SELECT Mammal AS m WHERE (1 + 2' => false, + 'SELECT Mammal AS m WHERE (1 + 2 * 4 / 23) > 0' => true, + 'SELECT Mammal AS m WHERE (4 / 23 * 2 + 1) > 0' => true, + 'SELECT Mammal AS m WHERE 1/0' => true, + 'SELECT Mammal AS m WHERE MONTH(m.birth) = 7' => true, + 'SELECT Animal JOIN Group ON Group.leader = Animal.id' => true, + 'SELECT Animal AS A JOIN Group AS G1 ON G1.leader = A.id' => true, + 'SELECT Animal AS A JOIN Group AS G ON FooClass.leader = A.id' => false, + 'SELECT Animal AS A JOIN Group AS G ON G.leader = FooClass.id' => false, + 'SELECT Animal AS A JOIN Group AS G ON G.masterchief = A.id' => false, + 'SELECT Animal AS A JOIN Group AS G ON G.leader = A.pkey' => false, + 'SELECT Animal AS A JOIN Group AS G ON A.id = G.leader' => false, + 'SELECT Animal AS A JOIN Group AS G ON G.leader = A.id WHERE A.sex=\'male\' OR G.qwerty = 123' => false, + 'SELECT Animal AS A JOIN Group AS G ON G.leader = A.id WHERE A.sex=\'male\' OR G.name LIKE "a%"' => true, + 'SELECT Animal AS A JOIN Group AS G ON G.leader = A.id WHERE A.id = 1' => true, + 'SELECT Animal AS A JOIN Group AS G ON G.leader = A.id WHERE id = 1' => false, + 'SELECT Animal AS A JOIN Group AS G ON A.member = G.id' => false, + 'SELECT Mammal AS M JOIN Group AS G ON M.member = G.id' => true, + 'SELECT Mammal AS M JOIN Group AS G ON A.member = G.id' => false, + 'SELECT Mammal AS myAlias JOIN Group AS myAlias ON myAlias.member = myAlias.id' => false, + 'SELECT Mammal AS Mammal JOIN Group AS Mammal ON Mammal.member = Mammal.id' => false, + 'SELECT Group AS G WHERE G.leader_name LIKE "%"' => true, + 'SELECT Group AS G WHERE G.leader_speed < 100000' => true, + 'SELECT Mammal AS M JOIN Group AS G ON M.member = G.id WHERE G.leader_name LIKE "%"' => true, + 'SELECT Mammal AS M JOIN Group AS G ON M.member = G.id WHERE G.leader_speed < 100000' => true, + 'SELECT Mammal AS Child JOIN Mammal AS Dad ON Child.father = Dad.id' => true, + 'SELECT Mammal AS Child JOIN Animal AS Dad ON Child.father = Dad.id' => true, + 'SELECT Animal AS Child JOIN Mammal AS Dad ON Child.father = Dad.id' => true, + 'SELECT Animal AS Child JOIN Animal AS Dad ON Child.father = Dad.id' => true, + 'SELECT Animal AS Dad JOIN Animal AS Child ON Child.father = Dad.id' => true, + 'SELECT Animal AS Child JOIN Animal AS Dad ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id' => true, + 'SELECT Animal AS Child JOIN Animal AS Dad ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id WHERE Dad.id = 1' => true, + 'SELECT Animal AS Child JOIN Animal AS Dad ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id WHERE Dad.name = \'romanoff\'' => false, + 'SELECT Animal AS Child JOIN Mammal AS Dad ON Child.father = Dad.id' => true, + 'SELECT Animal AS Child JOIN Mammal AS Dad ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id WHERE Dad.name = \'romanoff\' OR Mum.speed = 0' => true, + 'SELECT Animal AS Dad JOIN Animal AS Child ON Child.father = Dad.id JOIN Animal AS Mum ON Child.mother = Mum.id' => true, + 'SELECT Mammal AS Dad JOIN Mammal AS Child ON Child.father = Dad.id' => true, + 'SELECT Mammal AS Dad JOIN Mammal AS Child ON Child.father = Dad.id JOIN Mammal AS Mum ON Child.mother = Mum.id WHERE Dad.name = \'romanoff\' OR Mum.name=\'chloe\' OR Child.name=\'bizounours\'' => true, + ); + //$aQueries = array( + // 'SELECT Mammal AS M JOIN Group AS G ON M.member = G.id WHERE G.leader_name LIKE "%"' => true, + //); + foreach($aQueries as $sQuery => $bIsCorrect) + { + $this->CheckQuery($sQuery, $bIsCorrect); + } + return true; + } +} + + +/////////////////////////////////////////////////////////////////////////// +// Test data load +/////////////////////////////////////////////////////////////////////////// + +class TestBulkChangeOnFarm extends TestBizModel +{ + static public function GetName() + { + return 'Farm test - data load'; + } + + static public function GetDescription() + { + return 'Bulk load'; + } + + static public function GetConfigFile() {return '../config-test-farm.php';} + + protected function DoPrepare() + { + parent::DoPrepare(); + $this->ResetDB(); + MetaModel::DBCheckIntegrity(); + } + + protected function DoExecute() + { +// $this->ReportError("Found two different SibuSQL expression out of the (same?) filter: $sExpr1 != $sExpr2"); +// $this->ReportSuccess('Found '.$oSet->Count()." objects of class $sClassName"); + + $oParser = new CSVParser("#denomination,hauteur,age + suzy,123,2009-01-01 + chita,456, + "); + $oParser->SetSeparator(','); + $aData = $oParser->ToArray(array('_name', '_height', '_birth')); + MyHelpers::var_dump_html($aData); + + $oBulk = new BulkChange( + 'Mammal', + $aData, + array('name' => '_name', 'height' => '_height', 'birth' => '_birth'), + array('name'), + array() + ); + + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + $oMyChange->Set("userinfo", "Testor"); + $iChangeId = $oMyChange->DBInsert(); +// echo "Created new change: $iChangeId
        "; + + echo "

        Planned for loading...

        "; + $aRes = $oBulk->Process(); + print_r($aRes); + echo "

        Go for loading...

        "; + $aRes = $oBulk->Process($oMyChange); + print_r($aRes); + + return true; + + $oRawData = array( + 'Mammal', + array('species', 'sex', 'speed', 'mother', 'father', 'name', 'height', 'birth'), + "human,male,23,0,0,romulus,192,1971 + human,male,23,0,0,remus,154,-50 + human,male,23,0,0,julius,160,-49 + human,female,23,0,0,cleopatra,142,-50 + pig,female,23,0,0,confucius,50,2003" + ); + } +} + + +/////////////////////////////////////////////////////////////////////////// +// Benchmark queries +/////////////////////////////////////////////////////////////////////////// + +class TestItopEfficiency extends TestBizModel +{ + static public function GetName() + { + return 'Itop - benchmark'; + } + + static public function GetDescription() + { + return 'Measure time to perform the queries'; + } + + static public function GetConfigFile() {return '../config-itop.php';} + + protected function DoBenchmark($sOqlQuery) + { + echo "

        Testing query: $sOqlQuery

        "; + + $fStart = MyHelpers::getmicrotime(); + for($i=0 ; $i < COUNT_BENCHMARK ; $i++) + { + $oFilter = DBObjectSearch::FromOQL($sOqlQuery); + } + $fDuration = MyHelpers::getmicrotime() - $fStart; + $fParsingDuration = $fDuration / COUNT_BENCHMARK; + + $fStart = MyHelpers::getmicrotime(); + for($i=0 ; $i < COUNT_BENCHMARK ; $i++) + { + $sSQL = MetaModel::MakeSelectQuery($oFilter); + } + $fDuration = MyHelpers::getmicrotime() - $fStart; + $fBuildDuration = $fDuration / COUNT_BENCHMARK; + + $fStart = MyHelpers::getmicrotime(); + for($i=0 ; $i < COUNT_BENCHMARK ; $i++) + { + $res = CMDBSource::Query($sSQL); + } + $fDuration = MyHelpers::getmicrotime() - $fStart; + $fQueryDuration = $fDuration / COUNT_BENCHMARK; + + // The fetch could not be repeated with the same results + // But we've seen so far that is was very very quick to exec + // So it makes sense to benchmark it a single time + $fStart = MyHelpers::getmicrotime(); + $aRow = CMDBSource::FetchArray($res); + $fDuration = MyHelpers::getmicrotime() - $fStart; + $fFetchDuration = $fDuration; + + $fStart = MyHelpers::getmicrotime(); + for($i=0 ; $i < COUNT_BENCHMARK ; $i++) + { + $sOql = $oFilter->ToOQL(); + } + $fDuration = MyHelpers::getmicrotime() - $fStart; + $fToOqlDuration = $fDuration / COUNT_BENCHMARK; + + echo "
          \n"; + echo "
        • Parsing: $fParsingDuration
        • \n"; + echo "
        • Build: $fBuildDuration
        • \n"; + echo "
        • Query: $fQueryDuration
        • \n"; + echo "
        • Fetch: $fFetchDuration
        • \n"; + echo "
        • ToOql: $fToOqlDuration
        • \n"; + echo "
        \n"; + + // Everything but the ToOQL (wich is interesting, anyhow) + $fTotal = $fParsingDuration + $fBuildDuration + $fQueryDuration + $fFetchDuration; + + return array( + 'rows' => CMDBSource::NbRows($res), + 'duration (s)' => round($fTotal, 4), + 'parsing (%)' => round(100 * $fParsingDuration / $fTotal, 1), + 'build SQL (%)' => round(100 * $fBuildDuration / $fTotal, 1), + 'query exec (%)' => round(100 * $fQueryDuration / $fTotal, 1), + 'fetch (%)' => round(100 * $fFetchDuration / $fTotal, 1), + 'to OQL (%)' => round(100 * $fToOqlDuration / $fTotal, 1), + 'parsing+build (%)' => round(100 * ($fParsingDuration + $fBuildDuration) / $fTotal, 1), + ); + } + + protected function DoExecute() + { + define ('COUNT_BENCHMARK', 3); + echo "

        The test will be repeated ".COUNT_BENCHMARK." times

        "; + + $aQueries = array( + 'SELECT CMDBChangeOpSetAttribute', + 'SELECT CMDBChangeOpSetAttribute WHERE id=10', + 'SELECT CMDBChangeOpSetAttribute WHERE id=123456789', + 'SELECT CMDBChangeOpSetAttribute WHERE CMDBChangeOpSetAttribute.id=10', + 'SELECT bizIncidentTicket', + 'SELECT bizIncidentTicket WHERE id=1', + 'SELECT bizPerson', + 'SELECT bizPerson WHERE id=1', + 'SELECT bizIncidentTicket JOIN bizPerson ON bizIncidentTicket.agent_id = bizPerson.id WHERE bizPerson.id = 5', + ); + $aStats = array(); + foreach ($aQueries as $sOQL) + { + $aStats[$sOQL] = $this->DoBenchmark($sOQL); + } + + $aData = array(); + foreach ($aStats as $sOQL => $aResults) + { + $aValues = array(); + $aValues['OQL'] = htmlentities($sOQL); + + foreach($aResults as $sDesc => $sInfo) + { + $aValues[$sDesc] = htmlentities($sInfo); + } + $aData[] = $aValues; + } + echo MyHelpers::make_table_from_assoc_array($aData); + return true; + } +} + +/////////////////////////////////////////////////////////////////////////// +// Test data load +/////////////////////////////////////////////////////////////////////////// + +class TestItopWebServices extends TestWebServices +{ + static public function GetName() + { + return 'Itop - web services'; + } + + static public function GetDescription() + { + return 'Bulk load and ???'; + } + + protected function DoExecSingleLoad($aLoadSpec) + { + $sTitle = 'Load: '.$aLoadSpec['class']; + $sClass = $aLoadSpec['class']; + $sCsvData = $aLoadSpec['csvdata']; + + $aPostData = array('class' => $sClass, 'csvdata' => $sCsvData); + $sRes = self::DoPostRequestAuth('webservices/import.php', $aPostData); + + echo "

        $sTitle

        $sCsvData
        $sRes
        "; + } + + protected function DoExecute() + { + + $aLoads = array( + array( + 'class' => 'bizOrganization', + 'csvdata' => "name;code\nWorldCompany;WCY" + ), + array( + 'class' => 'bizLocation', + 'csvdata' => "name;org_id;address\nParis;1;Centre de la Franca" + ), + array( + 'class' => 'bizPerson', + 'csvdata' => "email;name;first_name;org_id;phone\njohn.foo@starac.com;Foo;John;1;+33(1)23456789" + ), + array( + 'class' => 'bizTeam', + 'csvdata' => "name;org_id;location_id\nSquadra Azzura;1;1" + ), + array( + 'class' => 'bizWorkgroup', + 'csvdata' => "name;org_id;team_id\ntravailleurs alpins;1;6" + ), + array( + 'class' => 'bizIncidentTicket', + 'csvdata' => "name;title;type;customer_id;initial_situation;start_date;next_update;caller_id;workgroup_id;agent_id\nOVSD-12345;server down;Network;1;server was found down;2009-04-10 12:00;2009-04-10 15:00;3;317;5" + ), + ); + + foreach ($aLoads as $aLoadSpec) + { + $this->DoExecSingleLoad($aLoadSpec); + } + + return true; + } +} +?> diff --git a/setup/ajax.dataloader.php b/setup/ajax.dataloader.php new file mode 100644 index 000000000..bc8f75f61 --- /dev/null +++ b/setup/ajax.dataloader.php @@ -0,0 +1,64 @@ +Set("date", time()); + $oChange->Set("userinfo", "Initialization"); + $iChangeId = $oChange->DBInsert(); + $oDataLoader->StartSession($oChange); + } + + $oDataLoader->LoadFile($sFileName); + + if ($sSessionStatus == 'end') + { + $oDataLoader->EndSession(); + } + $sResult = sprintf("Info - loading of %s done. (Overall %d %% completed).", basename($sFileName), $iPercent); + echo $sResult; + setup_web_page::log($sResult); +} +catch(Exception $e) +{ + echo "

        An error happened while loading the data

        \n"; + echo '

        '.$e."

        \n"; + setup_web_page::log("Error - An error happened while loading the data. ".$e); + +} + +?> diff --git a/setup/data/01.organizations.xml b/setup/data/01.organizations.xml new file mode 100644 index 000000000..2e0eb1c5b --- /dev/null +++ b/setup/data/01.organizations.xml @@ -0,0 +1,15 @@ + + + +Demo +MCO +implementation +3 + + +Oracle +ORA +production +6 + + \ No newline at end of file diff --git a/setup/data/02.locations.xml b/setup/data/02.locations.xml new file mode 100644 index 000000000..ddfdbf4cf --- /dev/null +++ b/setup/data/02.locations.xml @@ -0,0 +1,22 @@ + + + +Grenoble +production +3 +high +
        5 Avenue de la Poste
        +France +1 +
        + +Paris +implementation +3 +high +
        5 rue de la Paroisse +15eme arrondissement
        +France +1 +
        +
        \ No newline at end of file diff --git a/setup/data/03.persons.xml b/setup/data/03.persons.xml new file mode 100644 index 000000000..fa6317ff5 --- /dev/null +++ b/setup/data/03.persons.xml @@ -0,0 +1,33 @@ + + + +Denis +production +3 +denis.flaven@gmail.com + +29 +Flaven + + + +Quetiez +production +3 +romain.quetiez@gmail.com + +1 +Romain + + + +Taloc +production +3 +erwan.taloc@gmail.com +33172382223 +1 +Erwan +e12345 + + \ No newline at end of file diff --git a/setup/data/04.teams.xml b/setup/data/04.teams.xml new file mode 100644 index 000000000..7773a127e --- /dev/null +++ b/setup/data/04.teams.xml @@ -0,0 +1,19 @@ + + + +Application support +implementation +3 +application@mecanorama.com +33456456788 +29 + + +ITOP admin team +production +3 +combodo@gmail.com +33123453612 +1 + + \ No newline at end of file diff --git a/setup/data/05.pcs.xml b/setup/data/05.pcs.xml new file mode 100644 index 000000000..4af8e9b8a --- /dev/null +++ b/setup/data/05.pcs.xml @@ -0,0 +1,1865 @@ + + + +PC01 +production +3 +high +1 +Compaq +nc6005 + +desktop PC +2048 +Celeron 2x 1.6Ghz +60Gb +Windows +XP + +10.22.32.14 +10.22.31.1 + + +PC02 +production +3 +high +1 +Compaq +nc6000 + +desktop PC +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.32.15 +10.22.31.1 + + +PC03 +production +3 +high +1 +Compaq +nc6000 + +desktop PC +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.32.16 +10.22.31.1 + + +PC04 +production +3 +high +1 +Compaq +nc6000 + +desktop PC +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.32.17 +10.22.31.1 + + +PC05 +production +3 +high +1 +Compaq +nc6000 + +desktop PC +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.32.18 +10.22.31.1 + + +PC06 +production +3 +high +1 +Compaq +nc6000 + +desktop PC +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.32.19 +10.22.31.1 + + +PC07 +production +3 +high +1 +Compaq +nc6000 + +desktop PC +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.32.20 +10.22.31.1 + + +PC08 +production +3 +high +1 +Compaq +nc6000 + +desktop PC +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.32.21 +10.22.31.1 + + +PC09 +production +3 +high +1 +Compaq +nc6000 + +desktop PC +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.32.22 +10.22.31.1 + + +PC10 +production +3 +high +1 +Compaq +nc6000 + +desktop PC +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.32.23 +10.22.31.1 + + +PC11 +production +3 +high +1 +Compaq +nc6000 + +desktop PC +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.32.24 +10.22.31.1 + + +PC12 +production +3 +high +1 +Compaq +nc6008 + +desktop PC +2048 +Celeron 2x 1.6Ghz +60Gb +Windows +XP + +10.22.32.25 +10.22.31.1 + + +PC13 +production +3 +high +1 +Compaq +nc6000 + +desktop PC +2048 +Celeron 2x 1.6Ghz +60Gb +Windows +XP + +10.22.32.26 +10.22.31.1 + + +PC14 +production +3 +high +1 +Compaq +nc6000 + +desktop PC +2048 +Celeron 2x 1.6Ghz +60Gb +Windows +XP + +10.22.32.27 +10.22.31.1 + + +PC15 +production +3 +high +1 +Compaq +nc6000 + +desktop PC +2048 +Celeron 2x 1.6Ghz +60Gb +Windows +XP + +10.22.32.28 +10.22.31.1 + + +PC16 +production +3 +high +1 +Compaq +nc6000 + +desktop PC +2048 +Celeron 2x 1.6Ghz +60Gb +Windows +XP + +10.22.32.29 +10.22.31.1 + + +PC17 +production +3 +high +1 +Compaq +nc6000 + +desktop PC +2048 +Celeron 2x 1.6Ghz +60Gb +Windows +XP + +10.22.32.30 +10.22.31.1 + + +PC18 +production +3 +high +1 +Compaq +nc6000 + +desktop PC +2048 +Celeron 2x 1.6Ghz +60Gb +Windows +XP + +10.22.32.31 +10.22.31.1 + + +PC19 +production +3 +high +1 +Compaq +nc6000 + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.32.32 +10.22.31.1 + + +PC20 +production +3 +high +1 +Compaq +nc6000 + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.32.33 +10.22.31.1 + + +PC21 +production +3 +high +1 +Compaq +nc6000 + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.32.34 +10.22.31.1 + + +PC22 +production +3 +high +1 +Compaq +nc6000 + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.32.35 +10.22.31.1 + + +PC23 +production +3 +medium +1 +Compaq +nc6000 + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.32.36 +10.22.31.1 + + +PC24 +production +3 +medium +1 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.32.37 +10.22.31.1 + + +PC25 +production +3 +medium +1 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.32.38 +10.22.31.1 + + +PC26 +production +3 +medium +1 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.32.39 +10.22.31.1 + + +PC27 +production +3 +medium +1 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.32.40 +10.22.31.1 + + +PC28 +production +3 +medium +1 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.32.41 +10.22.31.1 + + +PC29 +production +3 +medium +1 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.32.42 +10.22.31.1 + + +PC30 +production +3 +medium +1 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.32.43 +10.22.31.1 + + +PC31 +production +3 +medium +1 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.32.44 +10.22.31.1 + + +PC32 +production +3 +medium +1 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.32.45 +10.22.31.1 + + +PC33 +production +3 +medium +1 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.32.46 +10.22.31.1 + + +PC34 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.30.5 +10.22.30.1 + + +PC35 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.30.6 +10.22.30.1 + + +PC36 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.30.7 +10.22.30.1 + + +PC37 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.30.8 +10.22.30.1 + + +PC38 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.30.9 +10.22.30.1 + + +PC39 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.30.10 +10.22.30.1 + + +PC40 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.30.11 +10.22.30.1 + + +PC41 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.30.12 +10.22.30.1 + + +PC42 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.30.13 +10.22.30.1 + + +PC43 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.30.14 +10.22.30.1 + + +PC44 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.30.15 +10.22.30.1 + + +PC45 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.30.16 +10.22.30.1 + + +PC46 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.30.17 +10.22.30.1 + + +PC47 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.30.18 +10.22.30.1 + + +PC48 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.30.19 +10.22.30.1 + + +PC49 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.30.20 +10.22.30.1 + + +PC50 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.30.21 +10.22.30.1 + + +PC5000 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Celeron 2x 1.6Ghz +60Gb +Windows +XP + +10.22.30.33 +10.22.30.1 + + +PC51 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Celeron 2x 1.6Ghz +60Gb +Windows +XP + +10.22.30.22 +10.22.30.1 + + +PC52 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Celeron 2x 1.6Ghz +60Gb +Windows +XP + +10.22.30.23 +10.22.30.1 + + +PC53 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Celeron 2x 1.6Ghz +60Gb +Windows +XP + +10.22.30.24 +10.22.30.1 + + +PC54 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Celeron 2x 1.6Ghz +60Gb +Windows +XP + +10.22.30.25 +10.22.30.1 + + +PC55 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Celeron 2x 1.6Ghz +60Gb +Windows +XP + +10.22.30.26 +10.22.30.1 + + +PC56 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Celeron 2x 1.6Ghz +60Gb +Windows +XP + +10.22.30.27 +10.22.30.1 + + +PC57 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Celeron 2x 1.6Ghz +60Gb +Windows +XP + +10.22.30.28 +10.22.30.1 + + +PC58 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Celeron 2x 1.6Ghz +60Gb +Windows +XP + +10.22.30.29 +10.22.30.1 + + +PC59 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Celeron 2x 1.6Ghz +60Gb +Windows +XP + +10.22.30.30 +10.22.30.1 + + +PC60 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Celeron 2x 1.6Ghz +60Gb +Windows +XP + +10.22.30.31 +10.22.30.1 + + +PC61 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Celeron 2x 1.6Ghz +60Gb +Windows +XP + +10.22.30.32 +10.22.30.1 + + +PC62 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Celeron 2x 1.6Ghz +60Gb +Windows +XP + +10.22.30.33 +10.22.30.1 + + +PC63 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Celeron 2x 1.6Ghz +60Gb +Windows +XP + +10.22.30.34 +10.22.30.1 + + +PC64 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Celeron 2x 1.6Ghz +60Gb +Windows +XP + +10.22.30.35 +10.22.30.1 + + +PC65 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Celeron 2x 1.6Ghz +60Gb +Windows +XP + +10.22.30.36 +10.22.30.1 + + +PC66 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.30.37 +10.22.30.1 + + +PC67 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.30.38 +10.22.30.1 + + +PC68 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.30.39 +10.22.30.1 + + +PC69 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.30.40 +10.22.30.1 + + +PC70 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.30.41 +10.22.30.1 + + +PC71 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.30.42 +10.22.30.1 + + +PC72 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.30.43 +10.22.30.1 + + +PC73 +production +3 +medium +29 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.30.44 +10.22.30.1 + + +PC74 +production +3 +high +29 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.30.45 +10.22.30.1 + + +PC75 +production +3 +high +29 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.30.46 +10.22.30.1 + + +PC76 +production +3 +high +29 +Dell +Studio 15 laptop + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.30.47 +10.22.30.1 + + +PC77 +production +3 +high +29 +Toshiba +Satelite L300D + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.30.48 +10.22.30.1 + + +PC78 +production +3 +high +29 +Toshiba +Satelite L300D + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +XP + +10.22.30.49 +10.22.30.1 + + +PC79 +production +3 +high +29 +Toshiba +Satelite L300D + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +Vista + +10.22.30.50 +10.22.30.1 + + +PC80 +production +3 +high +29 +Toshiba +Satelite L300D + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +Vista + +10.22.30.51 +10.22.30.1 + + +PC81 +production +3 +high +29 +Toshiba +Satelite L300D + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +Vista + +10.22.30.52 +10.22.30.1 + + +PC82 +production +3 +high +29 +Toshiba +Satelite L300D + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +Vista + +10.22.30.53 +10.22.30.1 + + +PC83 +production +3 +high +29 +Toshiba +Satelite L300D + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +Vista + +10.22.30.54 +10.22.30.1 + + +PC84 +production +3 +high +29 +Toshiba +Satelite L300D + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +Vista + +10.22.30.55 +10.22.30.1 + + +PC85 +production +3 +high +29 +Toshiba +Satelite L300D + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +Vista + +10.22.30.56 +10.22.30.1 + + +PC86 +production +3 +low +29 +Toshiba +Satelite L300D + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +Vista + +10.22.30.57 +10.22.30.1 + + +PC87 +production +3 +low +29 +Toshiba +Satelite L300D + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +Vista + +10.22.30.58 +10.22.30.1 + + +PC88 +production +3 +low +29 +Toshiba +Satelite L300D + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +Vista + +10.22.30.59 +10.22.30.1 + + +PC89 +production +3 +low +29 +Toshiba +Satelite L300D + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +Vista + +10.22.30.60 +10.22.30.1 + + +PC90 +production +3 +low +29 +Toshiba +Satelite L300D + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +Vista + +10.22.30.61 +10.22.30.1 + + +PC91 +production +3 +low +29 +Toshiba +Satelite L300D + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +Vista + +10.22.30.62 +10.22.30.1 + + +PC92 +production +3 +low +29 +Toshiba +Satelite L300D + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +Vista + +10.22.30.63 +10.22.30.1 + + +PC93 +production +3 +low +29 +Toshiba +Satelite L300D + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +Vista + +10.22.30.64 +10.22.30.1 + + +PC94 +production +3 +low +29 +Toshiba +Satelite L300D + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +Vista + +10.22.30.65 +10.22.30.1 + + +PC95 +production +3 +low +29 +Toshiba +Satelite L300D + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +Vista + +10.22.30.66 +10.22.30.1 + + +PC96 +production +3 +low +29 +Toshiba +Satelite L300D + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +Vista + +10.22.30.67 +10.22.30.1 + + +PC97 +production +3 +low +29 +Toshiba +Satelite L300D + +laptop +2048 +Pentium Dual Core 1.8Ghz +60Gb +Windows +Vista + +10.22.30.68 +10.22.30.1 + + \ No newline at end of file diff --git a/setup/data/06.servers.xml b/setup/data/06.servers.xml new file mode 100644 index 000000000..d83b26677 --- /dev/null +++ b/setup/data/06.servers.xml @@ -0,0 +1,4063 @@ + + + +domino.combodo.com +In Production +3 +high +1 +IBM +Power 520 Express +S4523 +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.1 + +10.22.28.10 +10.22.28.1 + + +server02 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.1 + +10.22.28.11 +10.22.28.1 + + +server03 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.1 + +10.22.28.12 +10.22.28.1 + + +server04 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.1 + +10.22.28.13 +10.22.28.1 + + +server05 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.1 + +10.22.28.14 +10.22.28.1 + + +server06 +Production Candidate +3 +medium +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.1 + +10.22.28.15 +10.22.28.1 + + +server07 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.1 + +10.22.28.16 +10.22.28.1 + + +server08 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.1 + +10.22.28.17 +10.22.28.1 + + +server09 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.1 + +10.22.28.18 +10.22.28.1 + + +server10 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.1 + +10.22.28.19 +10.22.28.1 + + +server100 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.109 +10.22.28.1 + + +server101 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.110 +10.22.28.1 + + +server102 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.111 +10.22.28.1 + + +server103 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.112 +10.22.28.1 + + +server104 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.113 +10.22.28.1 + + +server105 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.114 +10.22.28.1 + + +server106 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.115 +10.22.28.1 + + +server107 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.116 +10.22.28.1 + + +server108 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.117 +10.22.28.1 + + +server109 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.118 +10.22.28.1 + + +server11 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.1 + +10.22.28.20 +10.22.28.1 + + +server110 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.119 +10.22.28.1 + + +server111 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.120 +10.22.28.1 + + +server112 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.121 +10.22.28.1 + + +server113 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.122 +10.22.28.1 + + +server114 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.123 +10.22.28.1 + + +server115 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.124 +10.22.28.1 + + +server116 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.125 +10.22.28.1 + + +server117 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.126 +10.22.28.1 + + +server118 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.127 +10.22.28.1 + + +server119 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.128 +10.22.28.1 + + +server12 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.1 + +10.22.28.21 +10.22.28.1 + + +server120 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.129 +10.22.28.1 + + +server121 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.130 +10.22.28.1 + + +server122 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.131 +10.22.28.1 + + +server123 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.132 +10.22.28.1 + + +server124 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.133 +10.22.28.1 + + +server125 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.134 +10.22.28.1 + + +server126 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.135 +10.22.28.1 + + +server127 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.136 +10.22.28.1 + + +server128 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.137 +10.22.28.1 + + +server129 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.138 +10.22.28.1 + + +server13 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.1 + +10.22.28.22 +10.22.28.1 + + +server130 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.139 +10.22.28.1 + + +server131 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.140 +10.22.28.1 + + +server132 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.141 +10.22.28.1 + + +server133 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.142 +10.22.28.1 + + +server134 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.143 +10.22.28.1 + + +server135 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.144 +10.22.28.1 + + +server136 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.145 +10.22.28.1 + + +server137 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.146 +10.22.28.1 + + +server138 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.147 +10.22.28.1 + + +server139 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.148 +10.22.28.1 + + +server14 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.1 + +10.22.28.23 +10.22.28.1 + + +server140 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.149 +10.22.28.1 + + +server141 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.150 +10.22.28.1 + + +server142 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.151 +10.22.28.1 + + +server143 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.152 +10.22.28.1 + + +server144 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.153 +10.22.28.1 + + +server145 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.154 +10.22.28.1 + + +server146 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.155 +10.22.28.1 + + +server147 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.156 +10.22.28.1 + + +server148 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.157 +10.22.28.1 + + +server149 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.158 +10.22.28.1 + + +server15 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.1 + +10.22.28.24 +10.22.28.1 + + +server150 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.159 +10.22.28.1 + + +server151 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.160 +10.22.28.1 + + +server152 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.161 +10.22.28.1 + + +server153 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.162 +10.22.28.1 + + +server154 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.163 +10.22.28.1 + + +server155 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.164 +10.22.28.1 + + +server156 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.165 +10.22.28.1 + + +server157 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.166 +10.22.28.1 + + +server158 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.167 +10.22.28.1 + + +server159 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.168 +10.22.28.1 + + +server16 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.1 + +10.22.28.25 +10.22.28.1 + + +server160 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.169 +10.22.28.1 + + +server161 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.170 +10.22.28.1 + + +server162 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.171 +10.22.28.1 + + +server163 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.172 +10.22.28.1 + + +server164 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.173 +10.22.28.1 + + +server165 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.174 +10.22.28.1 + + +server166 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.175 +10.22.28.1 + + +server167 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.176 +10.22.28.1 + + +server168 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.177 +10.22.28.1 + + +server169 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.178 +10.22.28.1 + + +server17 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.1 + +10.22.28.26 +10.22.28.1 + + +server170 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.179 +10.22.28.1 + + +server171 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.180 +10.22.28.1 + + +server172 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.181 +10.22.28.1 + + +server173 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.182 +10.22.28.1 + + +server174 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.183 +10.22.28.1 + + +server175 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.184 +10.22.28.1 + + +server176 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.185 +10.22.28.1 + + +server177 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.186 +10.22.28.1 + + +server178 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.187 +10.22.28.1 + + +server179 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.188 +10.22.28.1 + + +server18 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.1 + +10.22.28.27 +10.22.28.1 + + +server180 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.189 +10.22.28.1 + + +server181 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.190 +10.22.28.1 + + +server182 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.191 +10.22.28.1 + + +server183 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.192 +10.22.28.1 + + +server184 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.193 +10.22.28.1 + + +server185 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.194 +10.22.28.1 + + +server186 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.195 +10.22.28.1 + + +server187 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.196 +10.22.28.1 + + +server188 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.197 +10.22.28.1 + + +server189 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.198 +10.22.28.1 + + +server19 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.1 + +10.22.28.28 +10.22.28.1 + + +server190 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.199 +10.22.28.1 + + +server191 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.200 +10.22.28.1 + + +server192 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.201 +10.22.28.1 + + +server193 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.202 +10.22.28.1 + + +server194 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.203 +10.22.28.1 + + +server195 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.204 +10.22.28.1 + + +server196 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.205 +10.22.28.1 + + +server197 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.206 +10.22.28.1 + + +server198 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.207 +10.22.28.1 + + +server199 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.208 +10.22.28.1 + + +server20 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.1 + +10.22.28.29 +10.22.28.1 + + +server21 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.1 + +10.22.28.30 +10.22.28.1 + + +server22 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.1 + +10.22.28.31 +10.22.28.1 + + +server23 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.1 + +10.22.28.32 +10.22.28.1 + + +server24 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.1 + +10.22.28.33 +10.22.28.1 + + +server25 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.3 + +10.22.28.34 +10.22.28.1 + + +server26 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.3 + +10.22.28.35 +10.22.28.1 + + +server27 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.3 + +10.22.28.36 +10.22.28.1 + + +server28 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.3 + +10.22.28.37 +10.22.28.1 + + +server29 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.3 + +10.22.28.38 +10.22.28.1 + + +server30 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.3 + +10.22.28.39 +10.22.28.1 + + +server31 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.3 + +10.22.28.40 +10.22.28.1 + + +server32 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.3 + +10.22.28.41 +10.22.28.1 + + +server33 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.3 + +10.22.28.42 +10.22.28.1 + + +server34 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.3 + +10.22.28.43 +10.22.28.1 + + +server35 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.3 + +10.22.28.44 +10.22.28.1 + + +server36 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.3 + +10.22.28.45 +10.22.28.1 + + +server37 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.3 + +10.22.28.46 +10.22.28.1 + + +server38 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.3 + +10.22.28.47 +10.22.28.1 + + +server39 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.3 + +10.22.28.48 +10.22.28.1 + + +server40 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.3 + +10.22.28.49 +10.22.28.1 + + +server41 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.3 + +10.22.28.50 +10.22.28.1 + + +server42 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.3 + +10.22.28.51 +10.22.28.1 + + +server43 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.3 + +10.22.28.52 +10.22.28.1 + + +server44 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.3 + +10.22.28.53 +10.22.28.1 + + +server45 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.3 + +10.22.28.54 +10.22.28.1 + + +server46 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.3 + +10.22.28.55 +10.22.28.1 + + +server47 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.3 + +10.22.28.56 +10.22.28.1 + + +server48 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.3 + +10.22.28.57 +10.22.28.1 + + +server49 +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.3 + +10.22.28.58 +10.22.28.1 + + +server50 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.59 +10.22.28.1 + + +server51 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.60 +10.22.28.1 + + +server52 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.61 +10.22.28.1 + + +server53 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.62 +10.22.28.1 + + +server54 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.63 +10.22.28.1 + + +server55 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.64 +10.22.28.1 + + +server56 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.65 +10.22.28.1 + + +server57 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.66 +10.22.28.1 + + +server58 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.67 +10.22.28.1 + + +server59 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.68 +10.22.28.1 + + +server60 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.69 +10.22.28.1 + + +server61 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.70 +10.22.28.1 + + +server62 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.71 +10.22.28.1 + + +server63 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.72 +10.22.28.1 + + +server64 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.73 +10.22.28.1 + + +server65 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.74 +10.22.28.1 + + +server66 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.75 +10.22.28.1 + + +server67 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.76 +10.22.28.1 + + +server68 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.77 +10.22.28.1 + + +server69 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.78 +10.22.28.1 + + +server70 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.79 +10.22.28.1 + + +server71 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.80 +10.22.28.1 + + +server72 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.81 +10.22.28.1 + + +server73 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.82 +10.22.28.1 + + +server74 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.83 +10.22.28.1 + + +server75 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.84 +10.22.28.1 + + +server76 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.85 +10.22.28.1 + + +server77 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.86 +10.22.28.1 + + +server78 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.87 +10.22.28.1 + + +server79 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.88 +10.22.28.1 + + +server80 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.89 +10.22.28.1 + + +server81 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.90 +10.22.28.1 + + +server82 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.91 +10.22.28.1 + + +server83 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.92 +10.22.28.1 + + +server84 +Production Candidate +3 +high +1 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.93 +10.22.28.1 + + +server85 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.94 +10.22.28.1 + + +server86 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.95 +10.22.28.1 + + +server87 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.96 +10.22.28.1 + + +server88 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.97 +10.22.28.1 + + +server89 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.98 +10.22.28.1 + + +server90 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.99 +10.22.28.1 + + +server91 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.100 +10.22.28.1 + + +server92 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.101 +10.22.28.1 + + +server93 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.102 +10.22.28.1 + + +server94 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.103 +10.22.28.1 + + +server95 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.104 +10.22.28.1 + + +server96 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.105 +10.22.28.1 + + +server97 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.11 + +10.22.28.106 +10.22.28.1 + + +server98 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.107 +10.22.28.1 + + +server99 +Production Candidate +3 +high +29 +HP + + +8Gb +rp3440 +4 +4x120Gb +200Gb +HP-UX +11.23 + +10.22.28.108 +10.22.28.1 + + +srv01.combodo.com +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.1 + +10.32.28.11 +10.32.28.1 + + +srv02.combodo.com +Production Candidate +3 +high +1 +HP + + +8Gb +rp3410 +4 +4x120Gb +200Gb +HP UX +11.23 + +10.32.28.12 +10.32.28.1 + + +srv03.combodo.com +Production Candidate +3 +high +1 +IBM + + +8Gb +Power 520 Express +4 +4x120Gb +200Gb +AIX +4.1 + +10.32.28.13 +10.32.28.1 + + +srv04.combodo.com +Production Candidate +3 +high +1 +HP + + +8Gb +rp3410 +4 +4x120Gb +200Gb +HP UX +11.23 + +10.32.28.14 +10.32.28.1 + + \ No newline at end of file diff --git a/setup/data/07.applications.xml b/setup/data/07.applications.xml new file mode 100644 index 000000000..c6adc3609 --- /dev/null +++ b/setup/data/07.applications.xml @@ -0,0 +1,33 @@ + + + +Oracle 10g +production +3 +high +5 +2008-11-05 00:00:00 +10g +Database + + +outlook Office +production +3 +high +2 +2008-12-30 00:00:00 +8.4 +Mail client + + +Outlook server +implementation +3 +high +5 +2008-12-20 00:00:00 +8.1 +Mail Server + + \ No newline at end of file diff --git a/setup/data/08.nw-devices.xml b/setup/data/08.nw-devices.xml new file mode 100644 index 000000000..97e6a2200 --- /dev/null +++ b/setup/data/08.nw-devices.xml @@ -0,0 +1,71 @@ + + + +router01 +production +3 +high +1 +Cisco +6500 +S2345985 +switch +10.25.3.45 +10.25.3.1 +8.2 +flash :1=16.48MB +public +private + + +router02 +production +3 +high +1 +Cisco +6500 +S2345985 +switch +10.25.3.46 +10.25.3.1 +8.2 +flash :1=16.48MB +public +private + + +router03 +production +3 +high +1 +Cisco +6500 +S2345985 +switch +10.25.3.47 +10.25.3.1 +8.2 +flash :1=16.48MB +public +private + + +switch01 +production +3 +high +1 + + + +switch + + + + + + + + \ No newline at end of file diff --git a/setup/data/09.links_contacts.xml b/setup/data/09.links_contacts.xml new file mode 100644 index 000000000..ebd943f13 --- /dev/null +++ b/setup/data/09.links_contacts.xml @@ -0,0 +1,23 @@ + + + +8 +7 +business model expert + + +21 +2 +owner + + +7 +5 +Server Owner + + +8 +21 +Team Leader + + \ No newline at end of file diff --git a/setup/data/10.workgroups.xml b/setup/data/10.workgroups.xml new file mode 100644 index 000000000..714771512 --- /dev/null +++ b/setup/data/10.workgroups.xml @@ -0,0 +1,17 @@ + + + +FLS Desktop +production +3 +8 +1st level support + + +FLS Network +production +3 +8 +2nd level support + + \ No newline at end of file diff --git a/setup/data/11.incidents.xml b/setup/data/11.incidents.xml new file mode 100644 index 000000000..a448488d7 --- /dev/null +++ b/setup/data/11.incidents.xml @@ -0,0 +1,132 @@ + + + +I-000001 +PC issue +Desktop +3 +WorkInProgress +there is an issue with my PC + +2009-02-10 21:30:55 + + + +21 +not able to start IE +19 +7 +We are about to connect to your PC via Netmeeting and troubleshoot what's up. + +Then we will try to contact Microsoft to understand if it is a known issue +critical +1 + + + +I-000002 +Network issue +Network +3 +Assigned +No more access to the network +Network connectivity lost +2009-02-19 19:07:25 +0000-00-00 00:00:00 +0000-00-00 00:00:00 +0000-00-00 00:00:00 +7 +high +24 +21 +need to investigate the network +low +2 + + + +I-000003 +Disk Failure on Server01 +Server +3 +WorkInProgress +Partition /var is no more accessible. +It seems that hard disk is broken +Filesystem is full +2009-03-21 08:27:46 +2009-02-10 22:25:00 +2009-02-10 00:25:00 +0000-00-00 00:00:00 +20 +Really important for the business +19 +7 +There are lot of logs file to be trimed +medium +2 + + + +I-000004 +Oracle Database down on domino +Server +3 +New +Database Oracle is no more accessible on Server01 +Customer cannot access Order web site. +2009-03-17 09:31:09 +0000-00-00 00:00:00 +0000-00-00 00:00:00 +0000-00-00 00:00:00 +7 +Very critical +19 +0 + +low +0 + + + +I-000005 +Issue with my pc +Desktop +3 +Assigned +My PC is locked +We have to analyze +2009-03-01 23:22:38 +0000-00-00 00:00:00 +0000-00-00 00:00:00 +0000-00-00 00:00:00 +7 + +19 +20 + +medium +1 + + + +I-000006 +Demo +Server +3 +New +gdfgl + +2009-03-18 22:00:00 +0000-00-00 00:00:00 +0000-00-00 00:00:00 +0000-00-00 00:00:00 +20 +personne ne peut acceder au site de paris +19 +0 + +low +0 + + + \ No newline at end of file diff --git a/setup/data/12.relatedtickets.xml b/setup/data/12.relatedtickets.xml new file mode 100644 index 000000000..dd5eeaa71 --- /dev/null +++ b/setup/data/12.relatedtickets.xml @@ -0,0 +1,8 @@ + + + +2 +1 +Real cause is a network incident + + \ No newline at end of file diff --git a/setup/data/13.infratickets.xml b/setup/data/13.infratickets.xml new file mode 100644 index 000000000..4d9974843 --- /dev/null +++ b/setup/data/13.infratickets.xml @@ -0,0 +1,33 @@ + + + +125 +6 +c'est down + + +5 +3 +main application is broken + + +5 +2 +no more access to network + + +2 +1 +not able to start IE + + +28 +5 +not able to work + + +5 +4 +Order web site is no more working + + \ No newline at end of file diff --git a/setup/data/14.contacttickets.xml b/setup/data/14.contacttickets.xml new file mode 100644 index 000000000..2b24a6215 --- /dev/null +++ b/setup/data/14.contacttickets.xml @@ -0,0 +1,13 @@ + + + +8 +1 +Team that need to be informed + + +21 +3 +to be informed + + \ No newline at end of file diff --git a/setup/data/15.changetickets.xml b/setup/data/15.changetickets.xml new file mode 100644 index 000000000..bdbb71970 --- /dev/null +++ b/setup/data/15.changetickets.xml @@ -0,0 +1,85 @@ + + + +CM-0000003 +1st change +Routine +Desktop +reason +20 +3 +PlannedScheduled +2009-02-19 21:35:47 +2009-02-18 18:33:14 +0000-00-00 00:00:00 +0000-00-00 00:00:00 +0000-00-00 00:00:00 +There is no impact +19 +21 +19 +20 +19 +7 +No +I would like to install SQL plus on my laptop. +Could you make it please +We will push via netmeeting the package and install it at 10:00 tomorrow +If there is any issue we will uninstall it +0 + + +CM-0000004 +Oracle upgrade on domino +Routine +Application +Patch Installation for Oracle +21 +3 +Approved +2009-03-17 14:50:34 +2009-03-17 14:50:34 +2009-02-24 00:00:00 +2009-02-24 02:00:00 +0000-00-00 00:00:00 +application impacted +19 +20 +19 +21 +19 +7 +Yes +We wan to install last Oracle Patch in order to fix rollback management issues +We will install patch and reboot the server +If not working we will remove this patch +0 + + +CM-0000005 +IOS upgrade +Routine +Desktop +security issue +21 +3 +New +2009-04-04 23:23:37 +0000-00-00 00:00:00 +0000-00-00 00:00:00 +0000-00-00 00:00:00 +0000-00-00 00:00:00 + +19 +21 +19 +20 +19 +7 +No +Upgrade to new IOS due to security reason + + +0 + + \ No newline at end of file diff --git a/setup/data/16.infrachangetickets.xml b/setup/data/16.infrachangetickets.xml new file mode 100644 index 000000000..af099731b --- /dev/null +++ b/setup/data/16.infrachangetickets.xml @@ -0,0 +1,23 @@ + + + +22 +2 +impacted application + + +5 +2 +impacted server + + +2 +1 +PC where to install new appli + + +6 +3 +router not available + + \ No newline at end of file diff --git a/setup/data/17.contactchangetickets.xml b/setup/data/17.contactchangetickets.xml new file mode 100644 index 000000000..d22e88992 --- /dev/null +++ b/setup/data/17.contactchangetickets.xml @@ -0,0 +1,23 @@ + + + +7 +1 +DB admin + + +20 +2 +Db administrator + + +21 +1 +notify me + + +7 +2 +Server Owner for reboot + + \ No newline at end of file diff --git a/setup/data/18.contracts.xml b/setup/data/18.contracts.xml new file mode 100644 index 000000000..4de2eff8e --- /dev/null +++ b/setup/data/18.contracts.xml @@ -0,0 +1,23 @@ + + + +Oracle Support +3 +6 +Software support +8 +Gold +Global +Yearly +30000 +Euros +This contract define Oracle Support contract between Mecanorama and Oracle company. + +Yearly cost is 30.000 euros +2009-02-22 18:54:14 +2009-03-04 00:01:38 +Signed +Software +3 + + \ No newline at end of file diff --git a/setup/data/19.infracontracts.xml b/setup/data/19.infracontracts.xml new file mode 100644 index 000000000..509a3d947 --- /dev/null +++ b/setup/data/19.infracontracts.xml @@ -0,0 +1,9 @@ + + + +22 +1 +Office Hour +Gold + + \ No newline at end of file diff --git a/setup/data/20.contactcontracts.xml b/setup/data/20.contactcontracts.xml new file mode 100644 index 000000000..f33a08332 --- /dev/null +++ b/setup/data/20.contactcontracts.xml @@ -0,0 +1,13 @@ + + + +21 +1 +Contract Manager + + +8 +1 +Team in charge to manage Oracle + + \ No newline at end of file diff --git a/setup/data/21.auditcategories.xml b/setup/data/21.auditcategories.xml new file mode 100644 index 000000000..ae806c855 --- /dev/null +++ b/setup/data/21.auditcategories.xml @@ -0,0 +1,8 @@ + + + +Devices in production +Checking all devices in production +bizDevice: status = 'production' + + \ No newline at end of file diff --git a/setup/data/22.auditrules.xml b/setup/data/22.auditrules.xml new file mode 100644 index 000000000..8298ae182 --- /dev/null +++ b/setup/data/22.auditrules.xml @@ -0,0 +1,17 @@ + + + +Devices in production on a Location not in production + +bizDevice: location_id IN (bizLocation: status != 'production') +false +1 + + +Devices not attached to a monitoring group + +bizDevice: PKEY IS infra_id IN (lnkInfraGrouping: infra_group_id IN (bizInfraGroup: type = 'Monitoring')) +true +1 + + \ No newline at end of file diff --git a/setup/data/export.cmd b/setup/data/export.cmd new file mode 100644 index 000000000..5c95ce860 --- /dev/null +++ b/setup/data/export.cmd @@ -0,0 +1,27 @@ +SET WEBROOT=http://localhost:81 +SET USER=Erwan +SET PWD=Taloc +REM The order (numbering) of the files is important since +REM it dictates the order to import them back +wget --output-document=01.organizations.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT bizOrganization&format=xml" +wget --output-document=02.locations.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT bizLocation&format=xml" +wget --output-document=03.persons.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT bizPerson&format=xml" +wget --output-document=04.teams.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT bizTeam&format=xml" +wget --output-document=05.pcs.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT bizPC&format=xml" +wget --output-document=06.servers.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT bizServer&format=xml" +wget --output-document=07.applications.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT bizApplication&format=xml" +wget --output-document=08.nw-devices.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT bizNetworkDevice&format=xml" +wget --output-document=09.links_contacts.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT lnkContactRealObject&format=xml" +wget --output-document=10.workgroups.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT bizWorkgroup&format=xml" +wget --output-document=11.incidents.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT bizIncidentTicket&format=xml" +wget --output-document=12.relatedtickets.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT lnkRelatedTicket&format=xml" +wget --output-document=13.infratickets.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT lnkInfraTicket&format=xml" +wget --output-document=14.contacttickets.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT lnkContactTicket&format=xml" +wget --output-document=15.changetickets.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT bizChangeTicket&format=xml" +wget --output-document=16.infrachangetickets.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT lnkInfraChangeTicket&format=xml" +wget --output-document=17.contactchangetickets.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT lnkContactChange&format=xml" +wget --output-document=18.contracts.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT bizContract&format=xml" +wget --output-document=19.infracontracts.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT lnkInfraContract&format=xml" +wget --output-document=20.contactcontracts.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT lnkContactContract&format=xml" +wget --output-document=21.auditcategories.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT AuditCategory&format=xml" +wget --output-document=22.auditrules.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT AuditRule&format=xml" diff --git a/setup/export_menus.cmd b/setup/export_menus.cmd new file mode 100644 index 000000000..e7cc514af --- /dev/null +++ b/setup/export_menus.cmd @@ -0,0 +1,6 @@ +SET WEBROOT=http://localhost:81 +SET USER=Erwan +SET PWD=Taloc +REM The order (numbering) of the files is important since +REM it dictates the order to import them back +wget --output-document=1.menus.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT menuNode WHERE type%%3D%%27application%%27&format=xml" diff --git a/setup/index.php b/setup/index.php new file mode 100644 index 000000000..20a5472fb --- /dev/null +++ b/setup/index.php @@ -0,0 +1,536 @@ +log('Info - CheckPHPVersion'); + if (version_compare(phpversion(), PHP_MIN_VERSION, '>=')) + { + $oP->ok("The current PHP Version (".phpversion().") is greater than the minimum required version (".PHP_MIN_VERSION.")"); + } + else + { + $oP->error("Error: The current PHP Version (".phpversion().") is lower than the minimum required version (".PHP_MIN_VERSION.")"); + return false; + } + $aMandatoryExtensions = array('mysql', 'iconv', 'simplexml'); + asort($aMandatoryExtensions); // Sort the list to look clean ! + $aExtensionsOk = array(); + $aMissingExtensions = array(); + $aMissingExtensionsLinks = array(); + foreach($aMandatoryExtensions as $sExtension) + { + if (extension_loaded($sExtension)) + { + $aExtensionsOk[] = $sExtension; + } + else + { + $aMissingExtensions[] = $sExtension; + $aMissingExtensionsLinks[] = "$sExtension"; + } + } + if (count($aExtensionsOk) > 0) + { + $oP->ok("Required PHP extension(s): ".implode(', ', $aExtensionsOk)."."); + } + if (count($aMissingExtensions) > 0) + { + $oP->error("Missing PHP extension(s): ".implode(', ', $aMissingExtensionsLinks)."."); + return false; + } + return true; +} + +/** + * Helper function check the connection to the database and (if connected) to enumerate + * the existing databases + * @return Array The list of databases found in the server + */ +function CheckServerConnection(setup_web_page $oP, $sDBServer, $sDBUser, $sDBPwd) +{ + $aResult = array(); + $oP->log('Info - CheckServerConnection'); + try + { + $oDBSource = new CMDBSource; + $oDBSource->Init($sDBServer, $sDBUser, $sDBPwd); + $oP->ok("Connection to '$sDBServer' as '$sDBUser' successful."); + $sDBVersion = $oDBSource->GetDBVersion(); + if (version_compare($sDBVersion, MYSQL_MIN_VERSION, '>=')) + { + $oP->ok("Current MySQL version ($sDBVersion), greater than minimum required version (".MYSQL_MIN_VERSION.")"); + } + else + { + $oP->error("Error: Current MySQL version is ($sDBVersion), minimum required version (".MYSQL_MIN_VERSION.")"); + return false; + } + try + { + $aResult = $oDBSource->ListDB(); + } + catch(Exception $e) + { + $oP->warning("Warning: unable to enumerate the current databases."); + $aResult = true; // Not an array to differentiate with an empty array + } + } + catch(Exception $e) + { + $oP->error("Error: Connection to '$sDBServer' as '$sDBUser' failed."); + $oP->p($e->GetHtmlDesc()); + $aResult = false; + } + return $aResult; +} + +/** + * Helper function to initialize the ORM and load the data model + * from the given file + * @param $sConfigFileName string The name of the configuration file to load + * @param $bAllowMissingDatabase boolean Whether or not to allow loading a data model with no corresponding DB + * @return none + */ +function InitDataModel(setup_web_page $oP, $sConfigFileName, $bAllowMissingDatabase = true) +{ + require_once('../core/coreexception.class.inc.php'); + require_once('../core/attributedef.class.inc.php'); + require_once('../core/filterdef.class.inc.php'); + require_once('../core/stimulus.class.inc.php'); + require_once('../core/MyHelpers.class.inc.php'); + require_once('../core/expression.class.inc.php'); + require_once('../core/cmdbsource.class.inc.php'); + require_once('../core/sqlquery.class.inc.php'); + require_once('../core/dbobject.class.php'); + require_once('../core/dbobjectsearch.class.php'); + require_once('../core/dbobjectset.class.php'); + require_once('../core/userrights.class.inc.php'); + $oP->log("Info - MetaModel::Startup from file '$sConfigFileName' (AllowMissingDB = $bAllowMissingDatabase)"); + MetaModel::Startup($sConfigFileName, $bAllowMissingDatabase); +} +/** + * Helper function to create the database structure + * @return boolean true on success, false otherwise + */ +function CreateDatabaseStructure(setup_web_page $oP, Config $oConfig, $sDBName, $sDBPrefix) +{ + InitDataModel($oP, TMP_CONFIG_FILE, true); // Allow the DB to NOT exist since we're about to create it ! + $oP->log('Info - CreateDatabaseStructure'); + $oP->info("Creating the structure in '$sDBName' (prefix = '$sDBPrefix')."); + //MetaModel::CheckDefinitions(); + if (!MetaModel::DBExists()) + { + MetaModel::DBCreate(); + $oP->ok("Database structure created in '$sDBName' (prefix = '$sDBPrefix')."); + } + else + { + $oP->error("Error: database '$sDBName' (prefix = '$sDBPrefix') already exists."); + $oP->p("Tables with conflicting names already exist in the database. + Try selecting another database instance or specifiy a prefix to prevent conflicting table names."); + return false; + } + return true; +} + +/** + * Helper function to create and administrator account for iTop + * @return boolean true on success, false otherwise + */ +function CreateAdminAccount(setup_web_page $oP, Config $oConfig, $sAdminUser, $sAdminPwd) +{ + $oP->log('Info - CreateAdminAccount'); + InitDataModel($oP, TMP_CONFIG_FILE, true); // allow missing DB + if (UserRights::CreateAdministrator($sAdminUser, $sAdminPwd)) + { + $oP->ok("Administrator account '$sAdminUser' created."); + return true; + } + else + { + $oP->error("Failed to create the administrator account '$sAdminUser'."); + return false; + } +} + +/** + * Scans the ./data directory for XML files and output them as a Javascript array + */ +function PopulateDataFilesList(setup_web_page $oP) +{ + if ($hDir = @opendir(SETUP_DATA_DIR)) + { + $aFilesToLoad = array(); + // This is the correct way to loop over the directory. (according the documentation) + while (($sFile = readdir($hDir)) !== false) + { + $sExtension = pathinfo($sFile, PATHINFO_EXTENSION ); + if (strcasecmp($sExtension, 'xml') == 0) + { + $aFilesToLoad[] = SETUP_DATA_DIR.'/'.$sFile; + } + } + closedir($hDir); + // Load order is important we expect the files to be ordered + // like numbered 1.Organizations.xml 2.Locations.xml , etc. + asort($aFilesToLoad); + // Menus can be loaded any time... like here at the end + + $oP->add("\n"); + } + else + { + $oP->error("Data directory (".SETUP_DATA_DIR.") no found or not readable."); + } +} + +/** + * Display the form for the first step of the configuration wizard + * which consists in the database server selection + */ +function DisplayStep1(setup_web_page $oP) +{ + $sNextOperation = 'step2'; + $oP->add("

        iTop configuration wizard

        \n"); + $oP->add("

        Checking prerequisites

        \n"); + if (CheckPHPVersion($oP)) + { + $sRedStar = '*'; + $oP->add("

        Step 1: Configuration of the database connection

        \n"); + $oP->add("
        \n"); + // Form goes here + $oP->add("
        Database connection\n"); + $aForm = array(); + $aForm[] = array('label' => "Server name$sRedStar:", 'input' => "", + 'help' => 'E.g. "localhost", "dbserver.mycompany.com" or "192.142.10.23".'); + $aForm[] = array('label' => "User name$sRedStar:", 'input' => ""); + $aForm[] = array('label' => 'Password:', 'input' => ""); + $oP->form($aForm); + $oP->add("
        \n"); + $oP->add("\n"); + $oP->add("\n"); + $oP->add("
        \n"); + } +} + +/** + * Display the form for the second step of the configuration wizard + * which consists in + * 1) Validating the parameters by connecting to the database server + * 2) Prompting to select an existing database or to create a new one + */ +function DisplayStep2(setup_web_page $oP, Config $oConfig, $sDBServer, $sDBUser, $sDBPwd) +{ + $sNextOperation = 'step3'; + $oP->add("

        iTop configuration wizard

        \n"); + $oP->add("

        Step 2: Database selection

        \n"); + $oP->add("
        \n"); + $aDatabases = CheckServerConnection($oP, $sDBServer, $sDBUser, $sDBPwd); + if ($aDatabases === false) + { + // Connection failed, invalid credentials ? Go back + $oP->add("\n"); + } + else + { + // Connection is Ok, save it and continue the setup wizard + $oConfig->SetDBHost($sDBServer); + $oConfig->SetDBUser($sDBUser); + $oConfig->SetDBPwd($sDBPwd); + $oConfig->WriteToFile(); + + $oP->add("
        Specify a database*\n"); + $aForm = array(); + if (is_array($aDatabases)) + { + foreach($aDatabases as $sDBName) + { + $aForm[] = array('label' => ""); + } + } + else + { + $aForm[] = array('label' => " "); + $oP->add_ready_script("$('#current_db_name').click( function() { $('#current_db').attr('checked', true); });"); + } + $aForm[] = array('label' => " "); + $oP->form($aForm); + + $oP->add_ready_script("$('#new_db_name').click( function() { $('#new_db').attr('checked', true); })"); + $oP->add("
        \n"); + $aForm = array(); + $aForm[] = array('label' => "Add a prefix to all the tables: "); + $oP->form($aForm); + + $oP->add("\n"); + $oP->add("\n"); + $oP->add("    \n"); + $oP->add("\n"); + } + $oP->add("
        \n"); +} + +/** + * Display the form for the third step of the configuration wizard + * which consists in + * 1) Validating the parameters by connecting to the database server & selecting the database + * 2) Creating the database structure + * 3) Prompting for the admin account to be created + */ +function DisplayStep3(setup_web_page $oP, Config $oConfig, $sDBName, $sDBPrefix) +{ + $sNextOperation = 'step4'; + $oP->add("

        iTop configuration wizard

        \n"); + $oP->add("

        Creation of the database structure

        \n"); + $oP->add("
        \n"); + $oConfig->SetDBName($sDBName); + $oConfig->SetDBSubname($sDBPrefix); + $oConfig->WriteToFile(TMP_CONFIG_FILE); + if (CreateDatabaseStructure($oP, $oConfig, $sDBName, $sDBPrefix)) + { + $sRedStar = "*"; + $oP->add("

        Step 3: Definition of the administrator account

        \n"); + // Database created, continue with admin creation + $oP->add("
        Administrator account\n"); + $aForm = array(); + $aForm[] = array('label' => "Login$sRedStar:", 'input' => ""); + $aForm[] = array('label' => "Password$sRedStar:", 'input' => ""); + $aForm[] = array('label' => "Retype password$sRedStar:", 'input' => ""); + $oP->form($aForm); + $oP->add("
        \n"); + $oP->add("\n"); + $oP->add("\n"); + $oP->add("    \n"); + $oP->add("\n"); + } + else + { + $oP->add("\n"); + } + // Form goes here + $oP->add("
        \n"); +} + +/** + * Display the form for the fourth step of the configuration wizard + * which consists in + * 1) Creating the admin user account + * 2) Prompting to load some sample data + */ +function DisplayStep4(setup_web_page $oP, Config $oConfig, $sAdminUser, $sAdminPwd) +{ + $sNextOperation = 'step5'; + $oP->add("

        iTop configuration wizard

        \n"); + $oP->add("

        Creation of the administrator account

        \n"); + + $oP->add("
        \n"); + if (CreateAdminAccount($oP, $oConfig, $sAdminUser, $sAdminPwd)) + { + $oP->add("

        Step 4: Loading of sample data

        \n"); + $oP->p("
        Do you want to load sample data into the database ? \n"); + $oP->p(" Yes, for testing purposes, populate the database with sample data.\n"); + $oP->p(" No, this is a production system, I will load real data myself.\n"); + $oP->p("
        \n"); + $oP->add("\n"); + $oP->add("    \n"); + $oP->add("\n"); + } + else + { + // Creation failed + $oP->add("\n"); + } + // End of visible form + $oP->add("
        \n"); + // Hidden form + $oP->add("
        \n"); + $oP->add("\n"); // To be compatible with login page + $oP->add("\n"); // To be compatible with login page + $oP->add("\n"); + $oP->add("
        \n"); + $oP->add_linked_script('./jquery.progression.js'); + + PopulateDataFilesList($oP); +} +/** + * Display the form for the fifth (and final) step of the configuration wizard + * which consists in + * 1) Creating the final configuration file + * 2) Prompting the user to make the file read-only + */ +function DisplayStep5(setup_web_page $oP, Config $oConfig, $sAuthUser, $sAuthPwd) +{ + try + { + session_start(); + + // Write the final configuration file + $oConfig->WriteToFile(FINAL_CONFIG_FILE); + + // Start the application + InitDataModel($oP, FINAL_CONFIG_FILE, false); // DO NOT allow missing DB + if (UserRights::Login($sAuthUser, $sAuthPwd)) + { + $_SESSION['auth_user'] = $sAuthUser; + $_SESSION['auth_pwd'] = $sAuthPwd; + // remove the tmp config file + @unlink(TMP_CONFIG_FILE); + // try to make the final config file read-only + @chmod(FINAL_CONFIG_FILE, 0440); // Read-only for owner and group, nothing for others + + $oP->add("

        iTop configuration wizard

        \n"); + $oP->add("

        Configuration completed

        \n"); + $oP->add("
        \n"); + $oP->ok("The initialization completed successfully."); + // Form goes here + $oP->add("\n"); + $oP->add("    \n"); + $oP->add("\n"); + $oP->add("
        \n"); + } + else + { + $oP->add("

        iTop configuration wizard

        \n"); + $oP->add("

        Step 5: Configuration completed

        \n"); + + @unlink(FINAL_CONFIG_FILE); // remove the aborted config + $oP->error("Error: Failed to login for user: '$sAuthUser'\n"); + + $oP->add("
        \n"); + $oP->add("\n"); + $oP->add("    \n"); + $oP->add("
        \n"); + } + } + catch(Exception $e) + { + $oP->error("Error: unable to create the configuration file."); + $oP->p($e->getHtmlDesc()); + $oP->p("Did you forget to remove the previous (read-only) configuration file ?"); + } +} +/** + * Main program + */ + clearstatcache(); // Make sure we know what we are doing ! +if (file_exists(FINAL_CONFIG_FILE)) +{ + // The configuration file already exists + if (is_writable(FINAL_CONFIG_FILE)) + { + $oP->warning("Warning: a configuration file '".FINAL_CONFIG_FILE."' already exists, and will be overwritten."); + } + else + { + $oP->add("

        iTop configuration wizard

        \n"); + $oP->add("

        Fatal error

        \n"); + $oP->error("Error: the configuration file '".FINAL_CONFIG_FILE."' already exists and cannot be overwritten."); + $oP->p("The wizard cannot create the configuration file for you. Please remove the file '".realpath(FINAL_CONFIG_FILE)."' or change its access-rights/read-only flag before continuing."); + $oP->output(); + exit; + } +} +else +{ + // No configuration file yet + // Check that the wizard can write into the root dir to create the configuration file + if (!is_writable(dirname(FINAL_CONFIG_FILE))) + { + $oP->add("

        iTop configuration wizard

        \n"); + $oP->add("

        Fatal error

        \n"); + $oP->error("Error: the directory where to store the configuration file is not writable."); + $oP->p("The wizard cannot create the configuration file for you. Please make sure that the directory '".realpath(dirname(FINAL_CONFIG_FILE))."' is writable for the web server."); + $oP->output(); + exit; + } + +} +try +{ + $oConfig = new Config(TMP_CONFIG_FILE); +} +catch(Exception $e) +{ + // We'll end here when the tmp config file does not exist. It's normal + $oConfig = new Config(TMP_CONFIG_FILE, false /* Don't try to load it */); +} +switch($sOperation) +{ + case 'step1': + $oP->log("Info - ========= Wizard step 1 ========"); + DisplayStep1($oP); + break; + + case 'step2': + $oP->no_cache(); + $oP->log("Info - ========= Wizard step 2 ========"); + $sDBServer = Utils::ReadParam('db_server'); + $sDBUser = Utils::ReadParam('db_user'); + $sDBPwd = Utils::ReadParam('db_pwd'); + DisplayStep2($oP, $oConfig, $sDBServer, $sDBUser, $sDBPwd); + break; + + case 'step3': + $oP->no_cache(); + $oP->log("Info - ========= Wizard step 3 ========"); + $sDBName = Utils::ReadParam('db_name'); + if (empty($sDBName)) + { + $sDBName = Utils::ReadParam('new_db_name'); + } + $sDBPrefix = Utils::ReadParam('db_prefix'); + DisplayStep3($oP, $oConfig, $sDBName, $sDBPrefix); + break; + + case 'step4': + $oP->no_cache(); + $oP->log("Info - ========= Wizard step 4 ========"); + $sAdminUser = Utils::ReadParam('auth_user'); + $sAdminPwd = Utils::ReadParam('auth_pwd'); + DisplayStep4($oP, $oConfig, $sAdminUser, $sAdminPwd); + break; + + case 'step5': + $oP->no_cache(); + $oP->log("Info - ========= Wizard step 5 ========"); + $sAdminUser = Utils::ReadParam('auth_user'); + $sAdminPwd = Utils::ReadParam('auth_pwd'); + DisplayStep5($oP, $oConfig, $sAdminUser, $sAdminPwd); + break; + + default: + $oP->error("Error: unsupported operation '$sOperation'"); + +} +$oP->output(); +?> diff --git a/setup/jquery.progression.js b/setup/jquery.progression.js new file mode 100644 index 000000000..9f9d4ea52 --- /dev/null +++ b/setup/jquery.progression.js @@ -0,0 +1,193 @@ +/* + * Progression - jQuery plugin for Progress Bar 1.3 + * + * http://www.anthor.net/fr/jquery-progression.html + * + * Copyright (c) 2008 FOURNET Loïc + * + * Dual licensed under the MIT and GPL licenses: + * http://www.opensource.org/licenses/mit-license.php + * http://www.gnu.org/licenses/gpl.html + * + */ + +(function($) { + $.fn.progression = function(options) { + // Récupération des options par défaut + var opts = $.extend({ + Current: 50, + Maximum: 100, + Background: '#FFFFFF', + TextColor: '#000000', + aBackground: '#FF0000', + aTextColor: '#FFFFFF', + BorderColor: '#000000', + Animate: true, + AnimateTimeOut: 3000, + Easing: 'linear', + startFct : null, + endFct : null + }, $.fn.progression.defaults, options); + + if(options) + var newCurrent = options.Current; + + // Boucle sur les éléments appelés + return this.each(function() { + $this = $(this); // Elément en cours + $innerdiv=$this.find(".progress"); // On recherche si l'élément a déjà été traité + + // Options Spécifiques + Métadata ? + var o = $.metadata ? $.extend({}, opts, $this.metadata()) : opts; + + // Premier traitement de l'élément, pour la mise en place + if($innerdiv.length!=1) + BuildBarre($this, o); + else + { + // Si c'est une nouvelle valeur, la fonction doit avoir la priorité sur les métadata + if(newCurrent) + o.Current = newCurrent; + o.Maximum = parseInt($this.attr('pmax')); + } + + // Valeur supérieur au maximum ? + if( o.Current > o.Maximum ) + { + debug('La valeur demandee doit etre inférieur ou egale a la valeur maximale.') + return false; + } + + // Calcul du pourcentage actuel + var aWidth = Math.round(parseInt($this.attr('pcur'))/o.Maximum*100); + // Calcul du nouveaux pourcentage + var Width = Math.round(parseInt(o.Current)/o.Maximum*100); + + //Start Callback + if (typeof o.startFct == 'function') + o.startFct(o); + + if(o.Animate) + { + var oldCurrent = parseInt($this.attr('pcur')); + var Steps = Math.abs(oldCurrent - o.Current); + var StepsTimeOut = Math.floor(o.AnimateTimeOut/o.Maximum); + + $innerdiv.queue("fx", []); + $innerdiv.stop(); + $innerdiv.animate({ width: Width+"%" }, { + duration: Math.round(StepsTimeOut*(Steps+1)), + queue: false, + easing: o.Easing, + complete: function(){ + if (typeof o.endFct == 'function') + o.endFct(o); + } + }); + + for (i=0; i<=Steps; i++) { + $innerdiv.animate({opacity: 1},{ + duration: Math.round(StepsTimeOut*i), + queue: false, + complete: function(){ + if(oldCurrent<=o.Current) + $(this).progressionSetTextTo(oldCurrent++); + else + $(this).progressionSetTextTo(oldCurrent--); + } + }); + } + } + else + { + $innerdiv.css({ width: Width+'%' }); + $innerdiv.progressionSetTextTo(o.Current); + + if (typeof o.endFct == 'function') + o.endFct(o); + } + }); + }; + + // Fonction de création de la barre + function BuildBarre($this, o) { + // On vide la barre + $this.html(''); + + $this.css({ + textAlign: 'left', + position: 'relative', + overflow: 'hidden', + backgroundColor: o.Background, + borderColor: o.BorderColor, + color: o.TextColor + }); + // Si largeur Spécifique + if(o.Width) + $this.css('width', o.Width); + // Si hauteur Spécifique + if(o.Height) + $this.css({ height: o.Height, lineHeight: o.Height }); + // Si image de fond + if(o.BackgroundImg) + $this.css({ backgroundImage: 'url(' + o.BackgroundImg + ')' }); + + $innerdiv=$("
        "); + + $("
         
        ").css({ + position: 'absolute', + width: '100%', + height: '100%', + textAlign: 'center' + }).appendTo($this); + + $(" ") + .css({ + position: 'absolute', + width: $this.width(), + textAlign: 'center' + }) + .appendTo($innerdiv); + + $this.append($innerdiv); + + // On applique le CSS de $innerdiv + $innerdiv.css({ + position: 'absolute', + width: 0, + height: '100%', + overflow: 'hidden', + backgroundColor: o.aBackground, + color: o.aTextColor + }); + // Si image de fond active + if(o.aBackgroundImg) + $innerdiv.css({ backgroundImage: 'url(' + o.aBackgroundImg + ')' }); + + $this.attr('pmax', o.Maximum); + $this.attr('pcur', 0); + }; + + // Fonction pour aller à une valeur précise + $.fn.progressionSetTextTo = function(i) { + return this.each(function() { + $this = $(this).parent(); + if($this.attr('pmax')!=100) + $this.find(".text").html(i+"/"+$this.attr('pmax')); + else + $this.find(".text").html(i+" %"); + + $this.attr('pcur', i); + }); + }; + + // Fonction d'impression dans la console javascript + function debug($txt) { + if (window.console && window.console.log) + window.console.log('jQuery Progression: ' + $txt); + }; + + + $.fn.progression.defaults = {}; + +})(jQuery); \ No newline at end of file diff --git a/setup/menus.xml b/setup/menus.xml new file mode 100644 index 000000000..ca6bdeb5d --- /dev/null +++ b/setup/menus.xml @@ -0,0 +1,610 @@ + + + +0 +Admin Tools + +UI.php + +7 +application + + +5 +All Applications + +UI.php + +999 +application + + +5 +All Circuits + +UI.php + +999 +application + + +64 +All Contracts + +./UI.php + +2 +application + + +5 +All Interfaces + +UI.php + +999 +application + + +5 +All Network devices + +UI.php + +999 +application + + +5 +All Patches + +UI.php + +999 +application + + +5 +All PCs + +UI.php + +999 +application + + +5 +All Servers + +UI.php + +999 +application + + +1 +Audit + +/pages/audit.php + +4 +application + + +17 +Backup & Restore + +./db_importer.php + +998 +application + + +0 +Change Management + +./UI.php + +4 +application + + +66 +Closed Changes + +UI.php + +2 +application + + +61 +Closed Incident + +./UI.php + +2 +application + + +1 +Configuration Items + +UI.php + +2 +application + + +0 +Configuration Management + +UI.php + +2 +application + + +1 +Contacts + +UI.php + +1 +application + + +17 +CSV import + +csvimport.php + +998 +application + + +17 +Data Model + +schema.php + +999 +application + + +1 +Document + +UI.php + +6 +application + + +17 +Export + +./export.php + +1000 +application + + +1 +Grouping + +UI.php + +3 +application + + +0 +Incident Management + +./UI.php + +3 +application + + +61 +Known Errors + +./UI.php + +999 +application + + +1 +Locations + +UI.php + +5 +application + + +64 +Negociating contracts + +UI.php + +1 +application + + +66 +Open Changes + +./UI.php + +1 +application + + +61 +Open Incidents + +UI.php + +1 +application + + +2 +Persons + +UI.php + +7 +application + + +17 +Run queries + +./sibusql.php + +1001 +application + + +66 +Scheduled Outages + +./UI.php + +999 +application + + +0 +Service Management + +./UI.php + +5 +application + + +2 +Teams + +UI.php + +8 +application + + +17 +Universal Search + +UniversalSearch.php + +999 +application + + +0 +Welcome + +./UI.php + +1 +application + + diff --git a/setup/orange-progress.gif b/setup/orange-progress.gif new file mode 100644 index 0000000000000000000000000000000000000000..eec03c09e7b0208580b12b39b42f6e93844489ae GIT binary patch literal 40302 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 '+sMsg}); + } + return bResult; +} + +var aFilesToLoad = new Array(); + +function DoLoadDataAsynchronous() +{ + // Check if sample data must be loaded, or just the menus + aFilesToLoad[aFilesToLoad.length] = './menus.xml'; // First load the menus + if (($("#sample_data:checked").length == 1)) + { + PopulateDataFilesList(); // Function created in PHP to get the list of XML files on the server + } + $('#setup').block({message: '

        Loading data...

        0%

        '}); + $('#progress').progression( {Current:0, Maximum: 100, aBackgroundImg: 'orange-progress.gif', aTextColor: '#000000'} ); + LoadNextDataFile('', ''); + return false; // Stop here for now +} + +var iCounter = 0; + +function LoadNextDataFile(sData, sTextStatus) +{ + //$("#progress").html(sData); + if (iCounter < aFilesToLoad.length) + { + if (iCounter == (aFilesToLoad.length - 1)) + { + // Last file in the list (or only 1 file), this completes the session + sSessionStatus = 'end'; + } + else if (iCounter == 0) + { + // First file in the list, start the session + sSessionStatus = 'start'; + } + else + { + sSessionStatus = 'continue'; + } + iPercent = Math.round((100.0 * (1+iCounter)) / aFilesToLoad.length); + sFileName = aFilesToLoad[iCounter]; + //alert('Loading file '+sFileName+' ('+iPercent+' %) - '+sSessionStatus); + $("#progress").progression({ Current: iPercent }); + iCounter++; + $.get( 'ajax.dataloader.php', { 'file': sFileName, 'percent': iPercent, 'session_status': sSessionStatus }, LoadNextDataFile, 'html'); + } + else + { + // We're done + $('#setup').unblock(); + $('#GoToNextStep').submit(); // Use the hidden form to navigate to the next step + } +} diff --git a/setup/setuppage.class.inc.php b/setup/setuppage.class.inc.php new file mode 100644 index 000000000..4ed7aade4 --- /dev/null +++ b/setup/setuppage.class.inc.php @@ -0,0 +1,176 @@ +add_linked_script("../js/jquery.blockUI.js"); + $this->add_linked_script("./setup.js"); + $this->add_style(" +body { + background-color: #eee; + margin: 0; + padding: 0; + font-size: 10pt; +} +#setup { + width: 600px; + margin-left: auto; + margin-right: auto; + margin-top: 50px; + padding: 20px; + background-color: #fff; + border: 1px solid #000; +} +.center { + text-align: center; +} + +h1 { + color: #83b217; + font-size: 16pt; +} +h2 { + color: #000; + font-size: 14pt; +} +.v-spacer { + padding-top: 1em; +} +button { + margin-top: 1em; + padding-left: 1em; + padding-right: 1em; +} +p.info { + padding-left: 50px; + background: url(../images/info-mid.png) no-repeat top left; + height: 48px; + line-height: 48px; +} +p.ok { + padding-left: 50px; + background: url(../images/clean-mid.png) no-repeat top left; + height: 48px; + line-height: 48px; +} +p.warning { + padding-left: 50px; + background: url(../images/messagebox_warning-mid.png) no-repeat top left; + height: 48px; + line-height: 48px; +} +p.error { + padding-left: 50px; + background: url(../images/stop-mid.png) no-repeat top left; + height: 48px; + line-height: 48px; +} +td.label { + text-align: left; +} +td.input { + text-align: left; +} +table.formTable { + border: 0; + cellpadding: 2px; + cellspacing: 0; +} +.wizlabel, .wizinput { + color: #000; + font-size: 10pt; +} +.wizhelp { + color: #333; + font-size: 8pt; +} +#progress { + border:1px solid #000000; + width: 180px; + height: 20px; + line-height: 20px; + text-align: center; + margin: 5px; +} + "); + } + public function info($sText) + { + $this->add("

        $sText

        \n"); + $this->log("Info - ".$sText); + } + + public function ok($sText) + { + $this->add("

        $sText

        \n"); + $this->log("Ok - ".$sText); + } + + public function warning($sText) + { + $this->add("

        $sText

        \n"); + $this->log("Warning - ".$sText); + } + + public function error($sText) + { + $this->add("

        $sText

        \n"); + $this->log("Error - ".$sText); + } + + public function form($aData) + { + $this->add("\n"); + foreach($aData as $aRow) + { + $this->add("\n"); + if (isset($aRow['label']) && isset($aRow['label']) && isset($aRow['help'])) + { + $this->add("\n"); + $this->add("\n"); + $this->add("\n"); + } + else if (isset($aRow['label']) && isset($aRow['help'])) + { + $this->add("\n"); + $this->add("\n"); + } + else if (isset($aRow['label']) && isset($aRow['input'])) + { + $this->add("\n"); + $this->add("\n"); + } + else if (isset($aRow['label'])) + { + $this->add("\n"); + } + $this->add("\n"); + } + $this->add("
        {$aRow['label']}{$aRow['input']}{$aRow['help']}{$aRow['label']}{$aRow['help']}{$aRow['label']}{$aRow['input']}{$aRow['label']}
        \n"); + } + + public function output() + { + $this->s_content = "
        {$this->s_content}\n
        \n"; + return parent::output(); + } + + public static function log($sText) + { + $hLogFile = @fopen(INSTALL_LOG_FILE, 'a'); + if ($hLogFile !== false) + { + $sDate = date('Y-m-d H:i:s'); + fwrite($hLogFile, "$sDate - $sText\n"); + fclose($hLogFile); + } + } +} // End of class +?> diff --git a/setup/xmldataloader.class.inc.php b/setup/xmldataloader.class.inc.php new file mode 100644 index 000000000..f4149aa41 --- /dev/null +++ b/setup/xmldataloader.class.inc.php @@ -0,0 +1,282 @@ +StartSession(); + * $oLoader->LoadFile('./organizations.xml'); + * $oLoader->LoadFile('./locations.xml'); + * $oLoader->EndSession(); + */ +class XMLDataLoader +{ + protected $m_aKeys; + protected $m_aObjectsCache; + protected $m_bSessionActive; + protected $m_oChange; + protected $m_sCacheFileName; + + public function __construct($sConfigFileName) + { + $this->m_aKeys = array(); + $this->m_aObjectsCache = array(); + $this->m_oChange = null; + $this->m_sCacheFileName = dirname(__FILE__).'/'.KEYS_CACHE_FILE; + $this->InitDataModel($sConfigFileName); + $this->LoadKeysCache(); + $this->m_bSessionActive = true; + + } + + public function StartSession($oChange) + { + $this->m_oChange = $oChange; + $this->m_bSessionActive = true; + } + + public function EndSession() + { + $this->ResolveExternalKeys(); + $this->m_bSessionActive = false; + } + + public function __destruct() + { + // Stopping in the middle of a session, let's save the context information + if ($this->m_bSessionActive) + { + $this->SaveKeysCache(); + } + else + { + $this->ClearKeysCache(); + } + } + + /** + * Initializes the ORM (MetaModel) + */ + protected function InitDataModel($sConfigFileName, $bAllowMissingDatabase = true) + { + require_once('../core/coreexception.class.inc.php'); + require_once('../core/attributedef.class.inc.php'); + require_once('../core/filterdef.class.inc.php'); + require_once('../core/stimulus.class.inc.php'); + require_once('../core/MyHelpers.class.inc.php'); + require_once('../core/expression.class.inc.php'); + require_once('../core/cmdbsource.class.inc.php'); + require_once('../core/sqlquery.class.inc.php'); + require_once('../core/dbobject.class.php'); + require_once('../core/dbobjectsearch.class.php'); + require_once('../core/dbobjectset.class.php'); + require_once('../core/userrights.class.inc.php'); + MetaModel::Startup($sConfigFileName, $bAllowMissingDatabase); + } + + /** + * Stores the keys & object cache in a file + */ + protected function SaveKeysCache() + { + $hFile = @fopen($this->m_sCacheFileName, 'w'); + if ($hFile !== false) + { + $sData = serialize( array('keys' => $this->m_aKeys, + 'objects' => $this->m_aObjectsCache, + 'change' => $this->m_oChange)); + fwrite($hFile, $sData); + fclose($hFile); + } + else + { + echo "

        Error: Cannot write to file: '{$this->m_sCacheFileName}'!

        "; + } + } + + /** + * Loads the keys & object cache from the tmp file + */ + protected function LoadKeysCache() + { + $sFileContent = @file_get_contents($this->m_sCacheFileName); + if (!empty($sFileContent)) + { + $aCache = unserialize($sFileContent); + $this->m_aKeys = $aCache['keys']; + $this->m_aObjectsCache = $aCache['objects']; + $this->m_oChange = $aCache['change']; + } + } + + /** + * Remove the tmp file used to store the keys cache + */ + protected function ClearKeysCache() + { + if(is_file($this->m_sCacheFileName)) + { + unlink($this->m_sCacheFileName); + } + else + { + //echo "

        Hm, it looks like the file does not exist!!!

        "; + } + $this->m_aKeys = array(); + $this->m_aObjectsCache = array(); + } + + /** + * Helper function to load the objects from a standard XML file into the database + */ + function LoadFile($sFilePath) + { + global $aKeys; + + $oXml = simplexml_load_file($sFilePath); + + $aReplicas = array(); + foreach($oXml as $sClass => $oXmlObj) + { + $iSrcId = (integer)$oXmlObj['id']; // Mandatory to cast + + // Import algorithm + // Here enumerate all the attributes of the object + // for all attribute that is neither an external field + // not an external key, assign it + // Store all external keys for further reference + // Create the object an store the correspondence between its newly created Id + // and its original Id + // Once all the objects have been created re-assign all the external keys to + // their actual Ids + $oTargetObj = MetaModel::NewObject($sClass); + foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode=>$oAttDef) + { + if (($oAttDef->IsWritable()) && ($oAttDef->IsScalar()) && ($sAttCode != 'finalclass') ) + { + if ($oAttDef->IsExternalKey()) + { + $iDstObj = (integer)($oXmlObj->$sAttCode); + $iExtKey = $this->GetObjectKey($oAttDef->GetTargetClass(), $iDstObj); + if ($iExtKey == 0) + { + $iExtKey = -$iDstObj; // Convention: Unresolved keys are stored as negative ! + } + // tested by Romain, little impact on perf (not significant on the intial setup) + //$oTargetObj->CheckValue($sAttCode, $iExtKey); + $oTargetObj->Set($sAttCode, $iExtKey); + } + else + { + // tested by Romain, little impact on perf (not significant on the intial setup) + //$oTargetObj->CheckValue($sAttCode, (string)$oXmlObj->$sAttCode); + $oTargetObj->Set($sAttCode, (string)$oXmlObj->$sAttCode); + } + } + } + $this->StoreObject($sClass, $oTargetObj, $iSrcId); + } + return true; + } + + /** + * Get the new ID of an object in the database given its original ID + * This may fail (return 0) if the object has not yet been created in the database + * This is why the order of the import may be important + */ + protected function GetObjectKey($sClass, $iSrcId) + { + if (isset($this->m_aKeys[$sClass]) && isset($this->m_aKeys[$sClass][$iSrcId])) + { + return $this->m_aKeys[$sClass][$iSrcId]; + } + return 0; + } + + /** + * Store an object in the database and remember the mapping + * between its original ID and the newly created ID in the database + */ + protected function StoreObject($sClass, $oTargetObj, $iSrcId) + { + try + { + if (is_subclass_of($oTargetObj, 'CMDBObject')) + { + $iObjId = $oTargetObj->DBInsertTrackedNoReload($this->m_oChange); + } + else + { + $iObjId = $oTargetObj->DBInsertNoReload(); + } + + } + catch(Exception $e) + { + echo $e->GetHtmlDesc(); + } + $aParentClasses = MetaModel::EnumParentClasses($sClass); + $aParentClasses[] = $sClass; + foreach($aParentClasses as $sObjClass) + { + $this->m_aKeys[$sObjClass][$iSrcId] = $iObjId; + } + $this->m_aObjectsCache[$sClass][$iObjId] = $oTargetObj; + } + + /** + * Maps an external key to its (newly created) value + */ + protected function ResolveExternalKeys() + { + foreach($this->m_aObjectsCache as $sClass => $oObjList) + { + foreach($oObjList as $oTargetObj) + { + $bChanged = false; + $sClass = get_class($oTargetObj); + foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode=>$oAttDef) + { + if ( ($oAttDef->IsExternalKey()) && ($oTargetObj->Get($sAttCode) < 0) ) // Convention unresolved key = negative + { + $iExtKey = $this->GetObjectKey($oAttDef->GetTargetClass(), -$oTargetObj->Get($sAttCode)); + if ($iExtKey == 0) + { + echo "Warning: unresolved extkey in $sClass::".$oTargetObj->GetName()."(".$oTargetObj->GetKey().")::$sAttCode=".$oAttDef->GetTargetClass()."[".$oTargetObj->Get($sAttCode)."]
        \n"; + echo "
        aKeys[".$oAttDef->GetTargetClass()."]:\n";
        +							print_r($this->m_aKeys[$oAttDef->GetTargetClass()]);
        +							echo "
        \n"; + } + else + { + $bChanged = true; + $oTargetObj->Set($sAttCode, $iExtKey); + } + } + } + if ($bChanged) + { + try + { + if (is_subclass_of($oTargetObj, 'CMDBObject')) + { + $oTargetObj->DBUpdateTracked($this->m_oChange); + } + else + { + $oTargetObj->DBUpdate(); + } + } + catch(Exception $e) + { + echo $e->GetHtmlDesc(); + } + } + } + } + + return true; + } +} +?> diff --git a/toolkit.php b/toolkit.php new file mode 100644 index 000000000..b71abae3c --- /dev/null +++ b/toolkit.php @@ -0,0 +1,53 @@ +iTop"; + +$sStyle = " + border: 1px dashed #CCC; + background: #CCF; + padding:1.5em; +"; +echo "
        \n"; +echo "Main page
        \n"; +echo "CSV import (shortcut)
        \n"; +echo "Universal search (shortcut)
        \n"; +echo "

        Itop consultant

        \n"; +echo "Check model, Create DB, Update DB (new class, new attribute)
        \n"; +echo "Backup and restore (shortcut)
        \n"; +echo "Objects schema (shortcut)
        \n"; +echo "

        Not working or deprecated

        \n"; +echo "Data generator
        \n"; +echo "ITop finder
        \n"; +echo "navITop
        \n"; +echo "@ITop
        \n"; +echo "
        \n"; + +echo "

        phpMyORM

        "; + + +$sStyle = " + border: 1px dashed #CCC; + background: #CFC; + padding:1.5em; +"; +echo "
        \n"; + + +echo "Manage configurations
        \n"; +echo "Core unit tests
        \n"; + + +echo "
        \n"; + +echo "

        phpinfo()

        "; + + +$sStyle = " + border: 1px dashed #CCC; + background: #FCC; + padding:1.5em; +"; +echo "
        \n"; +phpinfo(); +echo "
        \n"; +?> diff --git a/webservices/export.php b/webservices/export.php new file mode 100644 index 000000000..530f95279 --- /dev/null +++ b/webservices/export.php @@ -0,0 +1,73 @@ +add("Unsupported format '$sFormat'. Possible values are: html, csv or xml."); + } + } + } + catch(Exception $e) + { + $oP = new web_page("iTop - Export"); + $oP->p("Error the query can not be executed."); + $oP->p($e->GetHtmlDesc()); + } +} +if (!$oP) +{ + // Display a short message about how to use this page + $oP = new web_page("iTop - Export"); + $oP->p("General purpose export page."); + $oP->p("Parameters:"); + $oP->p("
        • expression: an OQL expression (URL encoded if needed)
        • +
        • format: (optional, default is html) the desired output format. Can be one of 'html', 'csv' or 'xml'
        • +
        "); +} + +$oP->output(); +?> diff --git a/webservices/import.php b/webservices/import.php new file mode 100644 index 000000000..59b9a2cc6 --- /dev/null +++ b/webservices/import.php @@ -0,0 +1,168 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ + +// +// Known limitations +// - output still in html, because the errors are not displayed in xml +// - could not set the external keys by the mean of a reconciliation (the exact key must be given) +// - reconciliation is made on the first column +// - no option to force 'always create' or 'never create' +// +// Known issues +// - ALMOST impossible to troubleshoot when an externl key has a wrong value +// - no character escaping in the xml output (yes !?!?!) +// - not outputing xml when a wrong input is given (class, attribute names) +// - for a bizIncidentTicket you may use the name as the reconciliation key, +// but that attribute is in fact recomputed by the application! An error should be raised somewhere +// + +require_once('../application/application.inc.php'); +require_once('../application/webpage.class.inc.php'); +require_once('../application/csvpage.class.inc.php'); +require_once('../application/xmlpage.class.inc.php'); + +require_once('../application/startup.inc.php'); + +require_once('../application/loginwebpage.class.inc.php'); +login_web_page::DoLogin(); // Check user rights and prompt if needed + +$oContext = new UserContext(); +$oAppContext = new ApplicationContext(); +//$iActiveNodeId = utils::ReadParam('menu', -1); +//$currentOrganization = utils::ReadParam('org_id', ''); + +// Main program + +//$oP = new XMLPage("iTop - Bulk import"); +$oP = new web_page("iTop - Bulk import"); +try +{ + $sClass = utils::ReadParam('class', ''); + $sSep = utils::ReadParam('separator', ';'); + $sCSVData = utils::ReadPostedParam('csvdata'); + + $oCSVParser = new CSVParser($sCSVData); + $oCSVParser->SetSeparator($sSep); + $oCSVParser->SetSkipLines(1); + + // Limitation: as the attribute list is in the first line, we can not match external key by an third-party attribute + $sRawFieldList = $oCSVParser->ListFields(); + $aAttList = array(); + foreach($sRawFieldList as $iField => $sFieldName) + { + $aAttList[$sFieldName] = $iField; + } + $aExtKeys = array(); + + // Limitation: the reconciliation key is the first attribute + $aReconcilKeys = array($sRawFieldList[0]); + +// print_r($oCSVParser->ListFields()); +// print_r($oCSVParser->ToArray($oCSVParser->ListFields())); + + $aData = $oCSVParser->ToArray(); + $oBulk = new BulkChange( + $sClass, + $aData, + $aAttList, + $aReconcilKeys, + $aExtKeys + ); + + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + if (UserRights::GetUser() != UserRights::GetRealUser()) + { + $sUserString = UserRights::GetRealUser()." on behalf of ".UserRights::GetUser(); + } + else + { + $sUserString = UserRights::GetUser(); + } + $oMyChange->Set("userinfo", $sUserString.' (bulk load by web service)'); + $iChangeId = $oMyChange->DBInsert(); + + $aRes = $oBulk->Process($oMyChange); + + // Setup result presentation + // + $aDisplayConfig = array(); + $aDisplayConfig["__RECONCILIATION__"] = array("label"=>"Reconciliation", "description"=>""); + $aDisplayConfig["__STATUS__"] = array("label"=>"Status", "description"=>""); + if (isset($iPKeyId)) + { + $aDisplayConfig["col$iPKeyId"] = array("label"=>"pkey", "description"=>""); + } + foreach($aReconcilKeys as $iCol => $sAttCode) + { + $sLabel = MetaModel::GetAttributeDef($sClass, $sAttCode)->GetLabel(); + $aDisplayConfig["col$iCol"] = array("label"=>"$sLabel", "description"=>""); + } + foreach ($aAttList as $sAttCode => $iCol) + { + $sLabel = MetaModel::GetAttributeDef($sClass, $sAttCode)->GetLabel(); + $aDisplayConfig["col$iCol"] = array("label"=>"$sLabel", "description"=>""); + } + + + $aResultDisp = array(); // to be displayed + foreach($aRes as $iRow => $aRowData) + { + $aRowDisp = array(); + $aRowDisp["__RECONCILIATION__"] = $aRowData["__RECONCILIATION__"]; + $aRowDisp["__STATUS__"] = $aRowData["__STATUS__"]->GetDescription(); + foreach($aRowData as $sKey => $value) + { + if ($sKey == '__RECONCILIATION__') continue; + if ($sKey == '__STATUS__') continue; + + switch (get_class($value)) + { + case 'CellChangeSpec_Void': + $sClass = ''; + break; + case 'CellChangeSpec_Unchanged': + $sClass = ''; + break; + case 'CellChangeSpec_Modify': + $sClass = 'csvimport_ok'; + break; + case 'CellChangeSpec_Init': + $sClass = 'csvimport_init'; + break; + case 'CellChangeSpec_Issue': + $sClass = 'csvimport_error'; + break; + } + if (empty($sClass)) + { + $aRowDisp[$sKey] = $value->GetDescription(); + } + else + { + $aRowDisp[$sKey] = "
        ".$value->GetDescription()."
        "; + } + } + $aResultDisp[$iRow] = $aRowDisp; + } + $oP->table($aDisplayConfig, $aResultDisp); + +} +catch(Exception $e) +{ + $oP->add(''.((string)$e).''); +} + +$oP->output(); +?> From 0957c555c33dccb82128525412c79af90dbd9db8 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Tue, 28 Apr 2009 13:24:12 +0000 Subject: [PATCH 003/227] Fixed bugs linked to the bookmark creation: - There must be a user specific menu entry in the list of menuNodes - The asynchronous ajax page must use the authentication stored in the session - The query must be stored in OQL instead of sibusQL SVN:trunk[58] --- .../userrights/userrightsmatrix.class.inc.php | 23 +++++++++++++++ application/menunode.class.inc.php | 29 ++++++++++++------- pages/UniversalSearch.php | 6 ++-- pages/ajax.render.php | 12 ++++++-- 4 files changed, 55 insertions(+), 15 deletions(-) diff --git a/addons/userrights/userrightsmatrix.class.inc.php b/addons/userrights/userrightsmatrix.class.inc.php index de90baa47..75becda13 100644 --- a/addons/userrights/userrightsmatrix.class.inc.php +++ b/addons/userrights/userrightsmatrix.class.inc.php @@ -268,6 +268,29 @@ class UserRightsMatrix extends UserRightsAddOnAPI } } } + // Create the "My Bookmarks" menu item (parent_id = 0, rank = 6) + if ($bNewUser) + { + $bAddMenu = true; + } + else + { + $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT menuNode WHERE type = 'user' AND parent_id = 0 AND user_id = $iUserId")); + $bAddMenu = ($oSet->Count() < 1); + } + if ($bAddMenu) + { + $oMenu = MetaModel::NewObject('menuNode'); + $oMenu->Set('type', 'user'); + $oMenu->Set('parent_id', 0); // It's a toplevel entry + $oMenu->Set('rank', 6); // Located just above the Admin Tools section (=7) + $oMenu->Set('name', 'My Bookmarks'); + $oMenu->Set('label', 'My Favorite Items'); + $oMenu->Set('hyperlink', 'UI.php'); + $oMenu->Set('template', '

        My bookmarks

        This section contains my most favorite search results

        '); + $oMenu->Set('user_id', $iUserId); + $oMenu->DBInsert(); + } } diff --git a/application/menunode.class.inc.php b/application/menunode.class.inc.php index f571f40f3..e0b72ec62 100644 --- a/application/menunode.class.inc.php +++ b/application/menunode.class.inc.php @@ -89,14 +89,17 @@ class menuNode extends DBObject return $this->Get('hyperlink')."?".implode("&", $aParams); } - public function GetChildNodesSet($sType) + public function GetChildNodesSet($sType = null) { $oSearchFilter = new DBObjectSearch("menuNode"); $oSearchFilter->AddCondition('parent_id', $this->GetKey(), '='); - $oSearchFilter->AddCondition('type', $sType, '='); - if ($sType == 'user') + if ($sType != null) { - $oSearchFilter->AddCondition('user_id', UserRights::GetUserId(), '='); + $oSearchFilter->AddCondition('type', $sType, '='); + if ($sType == 'user') + { + $oSearchFilter->AddCondition('user_id', UserRights::GetUserId(), '='); + } } $oSet = new CMDBObjectSet($oSearchFilter, array('rank' => true)); return $oSet; @@ -184,10 +187,13 @@ class menuNode extends DBObject { $oSearchFilter = new DbObjectSearch("menuNode"); $oSearchFilter->AddCondition('parent_id', 0, '='); - $oSearchFilter->AddCondition('type', $sType, '='); - if ($sType == 'user') + if ($sType != null) { - $oSearchFilter->AddCondition('user_id', UserRights::GetUserId(), '='); + $oSearchFilter->AddCondition('type', $sType, '='); + if ($sType == 'user') + { + $oSearchFilter->AddCondition('user_id', UserRights::GetUserId(), '='); + } } $oRootSet = new CMDBObjectSet($oSearchFilter, array('rank' => true)); while($oNode = $oRootSet->Fetch()) @@ -207,10 +213,13 @@ class menuNode extends DBObject $oSearchFilter = new DBObjectSearch("menuNode"); $oSearchFilter->AddCondition('parent_id', $this->Get('parent_id')); $oSearchFilter->AddCondition('rank', $this->Get('rank'), '>'); - $oSearchFilter->AddCondition('type', $sType, '='); - if ($sType == 'user') + if ($sType != null) { - $oSearchFilter->AddCondition('user_id', UserRights::GetUserId(), '='); + $oSearchFilter->AddCondition('type', $sType, '='); + if ($sType == 'user') + { + $oSearchFilter->AddCondition('user_id', UserRights::GetUserId(), '='); + } } $oSet = new DBObjectSet($oSearchFilter, array('rank'=> true)); // Order by rank (true means ascending) return $oSet; diff --git a/pages/UniversalSearch.php b/pages/UniversalSearch.php index ebe762fb3..e649f8d2b 100644 --- a/pages/UniversalSearch.php +++ b/pages/UniversalSearch.php @@ -58,14 +58,14 @@ if ($oFilter != null) $oResultBlock->RenderContent($oP); // Menu node - $sFilter = $oFilter->ToSibusQL(); + $sFilter = $oFilter->ToOQL(); $sMenuNodeContent = << -$sFilter +$sFilter

        -$sFilter +$sFilter
        EOF; diff --git a/pages/ajax.render.php b/pages/ajax.render.php index 176a32128..35af6a858 100644 --- a/pages/ajax.render.php +++ b/pages/ajax.render.php @@ -6,10 +6,18 @@ require_once('../application/wizardhelper.class.inc.php'); require_once('../application/ui.linkswidget.class.inc.php'); require_once('../application/startup.inc.php'); -if (isset($_SERVER['PHP_AUTH_USER'])) +session_start(); +if (isset($_SESSION['auth_user'])) { + $sAuthUser = $_SESSION['auth_user']; + $sAuthPwd = $_SESSION['auth_pwd']; // Attempt to login, fails silently - UserRights::Login($_SERVER['PHP_AUTH_USER'], $_SERVER['PHP_AUTH_PW']); + UserRights::Login($sAuthUser, $sAuthPwd); +} +else +{ + // No session information + echo "

        No session information

        \n"; } $oPage = new ajax_page(""); From da72a5c778cbcd76f118b0b5563b4788bf318831 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Fri, 26 Jun 2009 14:33:48 +0000 Subject: [PATCH 004/227] Fix for Ticket #22 (impossible to create change or incident tickets) SVN:trunk[68] --- js/{LinksWidget.js => linkswidget.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename js/{LinksWidget.js => linkswidget.js} (100%) diff --git a/js/LinksWidget.js b/js/linkswidget.js similarity index 100% rename from js/LinksWidget.js rename to js/linkswidget.js From 08573d2491d8343468da03a9eb671fe68775438c Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Wed, 8 Jul 2009 14:53:53 +0000 Subject: [PATCH 005/227] Fixed bug #24: choice of organizations was limited to organization in "implementation". SVN:trunk[75] --- business/itop.business.class.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/business/itop.business.class.inc.php b/business/itop.business.class.inc.php index 31e561e92..f4ca40d13 100644 --- a/business/itop.business.class.inc.php +++ b/business/itop.business.class.inc.php @@ -146,7 +146,7 @@ class logRealObject extends cmdbAbstractObject MetaModel::Init_Params($aParams); MetaModel::Init_AddAttribute(new AttributeString("name", array("label"=>"Name", "description"=>"Common name", "allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeEnum("status", array("label"=>"Status", "description"=>"Lifecycle status", "allowed_values"=>$oAllowedStatuses, "sql"=>"status", "default_value"=>"implementation", "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeExternalKey("org_id", array("targetclass"=>"bizOrganization", "label"=>"Organization Id", "description"=>"ID of the object owner organization", "allowed_values"=>new ValueSetObjects("bizOrganization: status Contains 'implementation'", 'name', array('name'=>true)), "sql"=>"org_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("org_id", array("targetclass"=>"bizOrganization", "label"=>"Organization Id", "description"=>"ID of the object owner organization", "allowed_values"=>null, "sql"=>"org_id", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("org_name", array("label"=>"Organization", "description"=>"Company / Department owning this object", "allowed_values"=>null, "extkey_attcode"=> 'org_id', "target_attcode"=>"name"))); MetaModel::Init_AddFilterFromAttribute("name"); From a3345bc02107720e4d1ea951a6dad07ac2194419 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Wed, 8 Jul 2009 16:10:17 +0000 Subject: [PATCH 006/227] - The page was broken because there was no user login ! SVN:trunk[79] --- pages/audit.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/pages/audit.php b/pages/audit.php index 6f69cfdbb..88ce7c90a 100644 --- a/pages/audit.php +++ b/pages/audit.php @@ -7,6 +7,9 @@ $currentOrganization = utils::ReadParam('org_id', ''); $operation = utils::ReadParam('operation', ''); $oAppContext = new ApplicationContext(); +require_once('../application/loginwebpage.class.inc.php'); +login_web_page::DoLogin(); // Check user rights and prompt if needed + $oP = new iTopWebPage("iTop - CMDB Audit", $currentOrganization); function GetRuleResultSet($iRuleId, $oDefinitionFilter) From 10442e65faa77ed7147778ac8903f5ec8e610b48 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Wed, 8 Jul 2009 16:13:10 +0000 Subject: [PATCH 007/227] - Fixed audit menu - Removed backup/restore menu that is not working... - Moved 'export' to the 'webservices' directory SVN:trunk[80] --- setup/menus.xml | 13 ++----------- 1 file changed, 2 insertions(+), 11 deletions(-) diff --git a/setup/menus.xml b/setup/menus.xml index ca6bdeb5d..087d64ea3 100644 --- a/setup/menus.xml +++ b/setup/menus.xml @@ -151,20 +151,11 @@ 1 Audit -/pages/audit.php +./audit.php 4 application - -17 -Backup & Restore - -./db_importer.php - -998 -application - 0 Change Management @@ -367,7 +358,7 @@ td.dashboard { 17 Export -./export.php +../webservices/export.php 1000 application From 665d90175fd5f9ecd6ab074aea63881247d25a69 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Sat, 18 Jul 2009 17:24:25 +0000 Subject: [PATCH 008/227] - reintegrated the changes (mostly bug fixes) from the 0.7.2 branch SVN:trunk[83] --- .../userrights/userrightsmatrix.class.inc.php | 14 ++-- application/cmdbabstract.class.inc.php | 18 ++--- application/displayblock.class.inc.php | 30 +++++++-- application/itopwebpage.class.inc.php | 3 +- application/ui.linkswidget.class.inc.php | 2 +- application/uiwizard.class.inc.php | 67 ++++++++++--------- business/templates/Circuits.html | 2 +- business/templates/application.html | 2 +- business/templates/change.html | 2 +- business/templates/contract.html | 2 +- business/templates/default.html | 2 +- business/templates/document.html | 2 +- business/templates/group.html | 2 +- business/templates/interface.html | 2 +- business/templates/knownError.html | 2 +- business/templates/location.html | 2 +- business/templates/network.device.html | 2 +- business/templates/pc.html | 2 +- business/templates/person.html | 2 +- business/templates/server.html | 2 +- business/templates/service.html | 2 +- business/templates/software.html | 2 +- business/templates/team.html | 2 +- business/templates/ticket.html | 2 +- core/userrights.class.inc.php | 8 +-- pages/UI.php | 9 +-- pages/opensearch.xml | 8 --- pages/schema.php | 2 +- setup/data/03.persons.xml | 20 +++--- setup/menus.xml | 2 +- 30 files changed, 114 insertions(+), 105 deletions(-) delete mode 100644 pages/opensearch.xml diff --git a/addons/userrights/userrightsmatrix.class.inc.php b/addons/userrights/userrightsmatrix.class.inc.php index 75becda13..8bbe15cc8 100644 --- a/addons/userrights/userrightsmatrix.class.inc.php +++ b/addons/userrights/userrightsmatrix.class.inc.php @@ -312,7 +312,7 @@ class UserRightsMatrix extends UserRightsAddOnAPI $oLogin = $oSet->Fetch(); if ($oLogin->Get('password') == $sPassword) { - return true; + return $oLogin->Get('userid'); } // todo: throw an exception? return false; @@ -324,7 +324,7 @@ class UserRightsMatrix extends UserRightsAddOnAPI return $oNullFilter; } - public function IsActionAllowed($sUserName, $sClass, $iActionCode, dbObjectSet $aInstances) + public function IsActionAllowed($iUserId, $sClass, $iActionCode, dbObjectSet $aInstances) { if (!array_key_exists($iActionCode, self::$m_aActionCodes)) { @@ -332,7 +332,7 @@ class UserRightsMatrix extends UserRightsAddOnAPI } $sAction = self::$m_aActionCodes[$iActionCode]; - $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixClassGrant WHERE class = '$sClass' AND action = '$sAction' AND login = '$sUserName'")); + $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixClassGrant WHERE class = '$sClass' AND action = '$sAction' AND userid = '$iUserId'")); if ($oSet->Count() < 1) { return UR_ALLOWED_NO; @@ -352,7 +352,7 @@ class UserRightsMatrix extends UserRightsAddOnAPI return $iRetCode; } - public function IsActionAllowedOnAttribute($sUserName, $sClass, $sAttCode, $iActionCode, dbObjectSet $aInstances) + public function IsActionAllowedOnAttribute($iUserId, $sClass, $sAttCode, $iActionCode, dbObjectSet $aInstances) { if (!array_key_exists($iActionCode, self::$m_aActionCodes)) { @@ -360,7 +360,7 @@ class UserRightsMatrix extends UserRightsAddOnAPI } $sAction = self::$m_aActionCodes[$iActionCode]; - $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixAttributeGrant WHERE UserRightsMatrixAttributeGrant.class = '$sClass' AND UserRightsMatrixAttributeGrant.attcode = '$sAttCode' AND UserRightsMatrixAttributeGrant.action = '$sAction' AND UserRightsMatrixAttributeGrant.login = '$sUserName'")); + $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixAttributeGrant WHERE class = '$sClass' AND attcode = '$sAttCode' AND action = '$sAction' AND userid = '$iUserId'")); if ($oSet->Count() < 1) { return UR_ALLOWED_NO; @@ -380,9 +380,9 @@ class UserRightsMatrix extends UserRightsAddOnAPI return $iRetCode; } - public function IsStimulusAllowed($sUserName, $sClass, $sStimulusCode, dbObjectSet $aInstances) + public function IsStimulusAllowed($iUserId, $sClass, $sStimulusCode, dbObjectSet $aInstances) { - $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixClassStimulusGrant WHERE class = '$sClass' AND stimulus = '$sStimulusCode' AND login = '$sUserName'")); + $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixClassStimulusGrant WHERE class = '$sClass' AND stimulus = '$sStimulusCode' AND userid = '$iUserId'")); if ($oSet->Count() < 1) { return UR_ALLOWED_NO; diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index d29008dd2..3bd6401a5 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -142,7 +142,7 @@ abstract class cmdbAbstractObject extends CMDBObject if (!empty($sTemplate)) { $oTemplate = new DisplayTemplate($sTemplate); - $oTemplate->Render($oPage, array('class'=> get_class($this),'pkey'=> $this->GetKey(), 'name' => $this->GetName())); + $oTemplate->Render($oPage, array('class_name'=> MetaModel::GetName(get_class($this)),'class'=> get_class($this),'pkey'=> $this->GetKey(), 'name' => $this->GetName())); } else { @@ -152,7 +152,7 @@ abstract class cmdbAbstractObject extends CMDBObject $oSingletonFilter->AddCondition('pkey', array($this->GetKey())); $oBlock = new MenuBlock($oSingletonFilter, 'popup', false); $oBlock->Display($oPage, -1); - $oPage->add("

        ".Metamodel::GetName(get_class($this)).": ".$this->GetDisplayName()."

        \n"); + $oPage->add("

        ".Metamodel::GetName(MetaModel::GetName(get_class($this))).": ".$this->GetDisplayName()."

        \n"); $oHistoryFilter = new DBObjectSearch('CMDBChangeOpSetAttribute'); $oHistoryFilter->AddCondition('objkey', $this->GetKey()); $oBlock = new HistoryBlock($oHistoryFilter, 'toggle', false); @@ -276,13 +276,15 @@ abstract class cmdbAbstractObject extends CMDBObject } $oMenuBlock = new MenuBlock($oSet->GetFilter()); $sHtml .= ''; + $sColspan = ''; if ($bDisplayMenu) { - $sHtml .= ''; } - $sHtml .= ''; $sHtml .= '
        '; + $sColspan = 'colspan="2"'; + $sHtml .= '
         '.$oSet->Count().' object(s)'; $sHtml .= $oMenuBlock->GetRenderContent($oPage, $sLinkageAttribute); $sHtml .= '
        '; + $sHtml .= "
        "; $sHtml .= $oPage->GetTable($aAttribs, $aValues, array('class'=>$sClassName, 'filter'=>$oSet->GetFilter()->serialize(), 'preview' => true)); $sHtml .= '
        '; @@ -519,14 +521,14 @@ abstract class cmdbAbstractObject extends CMDBObject { $sHTMLValue = ""; } - else if (count($aAllowedValues) > 20) + else if (count($aAllowedValues) > 50) { // too many choices, use an autocomplete // The input for the auto complete - $sHTMLValue = ""; + $sHTMLValue = ""; // another hidden input to store & pass the object's Id - $sHTMLValue .= "\n"; - $oPage->add_ready_script("\$('#$iInputId').autocomplete('./ajax.render.php', { minChars:3, onItemSelect:selectItem, onFindValue:findValue, formatItem:formatItem, autoFill:true, keyHolder:'#id_ac_$iInputId', extraParams:{operation:'autocomplete', sclass:'$sClass',attCode:'".$sAttCode."'}});"); + $sHTMLValue .= "\n"; + $oPage->add_ready_script("\$('#label_$iInputId').autocomplete('./ajax.render.php', { minChars:3, onItemSelect:selectItem, onFindValue:findValue, formatItem:formatItem, autoFill:true, keyHolder:'#$iInputId', extraParams:{operation:'autocomplete', sclass:'$sClass',attCode:'".$sAttCode."'}});"); } else { diff --git a/application/displayblock.class.inc.php b/application/displayblock.class.inc.php index d93cb7cd4..f41ab6a28 100644 --- a/application/displayblock.class.inc.php +++ b/application/displayblock.class.inc.php @@ -248,10 +248,6 @@ class DisplayBlock $bDashboardMode = isset($aExtraParams['dashboard']) ? ($aExtraParams['dashboard'] == 'true') : false; if ( ($this->m_oSet->Count()> 0) && (UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_READ, $this->m_oSet) == UR_ALLOWED_YES) ) { - if (!$bDashboardMode) - { - $sHtml .= $oPage->GetP($this->m_oSet->Count()." object(s)."); - } $sLinkage = isset($aExtraParams['linkage']) ? $aExtraParams['linkage'] : ''; $sHtml .= cmdbAbstractObject::GetDisplaySet($oPage, $this->m_oSet, $sLinkage, !$bDashboardMode /* bDisplayMenu */); } @@ -616,7 +612,8 @@ class MenuBlock extends DisplayBlock } else { - $aActions[] = array ('label' => 'eMail', 'url' => "mailto:?subject=".$oSet->GetFilter()->__DescribeHTML()."&body=".urlencode("http://localhost:81/pages/UI.php?operation=search&filter=$sFilter&$sContext")); + $sUrl = self::GetAbsoluteUrl(); + $aActions[] = array ('label' => 'eMail', 'url' => "mailto:?subject=".$oSet->GetFilter()->__DescribeHTML()."&body=".urlencode("$sUrl?operation=search&filter=$sFilter&$sContext")); $aActions[] = array ('label' => 'CSV Export', 'url' => "../pages/$sUIPage?operation=search&filter=$sFilter&format=csv&$sContext"); $aActions[] = array ('label' => 'Bookmark...', 'url' => "../pages/ajax.render.php?operation=create&class=$sClass&filter=$sFilter", 'class' => 'jqmTrigger'); if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'New...', 'url' => "../pages/$sUIPage?operation=new&class=$sClass&$sContext"); } @@ -663,7 +660,8 @@ class MenuBlock extends DisplayBlock else { // many objects in the set, possible actions are: new / modify all / delete all - $aActions[] = array ('label' => 'eMail', 'url' => "mailto:?subject=".$oSet->GetFilter()->__DescribeHTML()."&body=".urlencode("http://localhost:81/pages/UI.php?operation=search&filter=$sFilter&$sContext")); + $sUrl = self::GetAbsoluteUrl(); + $aActions[] = array ('label' => 'eMail', 'url' => "mailto:?subject=".$oSet->GetFilter()->__DescribeHTML()."&body=".urlencode("$sUrl?operation=search&filter=$sFilter&$sContext")); $aActions[] = array ('label' => 'CSV Export', 'url' => "../pages/$sUIPage?operation=search&filter=$sFilter&format=csv&$sContext"); $aActions[] = array ('label' => 'Bookmark...', 'url' => "../pages/ajax.render.php?operation=create&class=$sClass&filter=$sFilter", 'class' => 'jqmTrigger'); if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'New...', 'url' => "../pages/$sUIPage?operation=new&class=$sClass&$sContext"); } @@ -680,7 +678,25 @@ class MenuBlock extends DisplayBlock $sHtml .= "
      \n\n
    \n"; $oPage->add_ready_script("$(\"ul.jd_menu\").jdMenu();\n"); return $sHtml; - } + } + static public function GetAbsoluteUrl() + { + // Build an absolute URL to this page on this server/port + $sServerName = $_SERVER['SERVER_NAME']; + $sProtocol = isset($_SERVER['HTTPS']) ? 'https' : 'http'; + if ($sProtocol == 'http') + { + $sPort = ($_SERVER['SERVER_PORT'] == 80) ? '' : ':'.$_SERVER['SERVER_PORT']; + } + else + { + $sPort = ($_SERVER['SERVER_PORT'] == 443) ? '' : ':'.$_SERVER['SERVER_PORT']; + } + $sPath = $_SERVER['REQUEST_URI']; + $sUrl = "$sProtocol://{$sServerName}{$sPort}{$sPath}"; + + return $sUrl; + } } ?> diff --git a/application/itopwebpage.class.inc.php b/application/itopwebpage.class.inc.php index de1cd14ca..0b69f6669 100644 --- a/application/itopwebpage.class.inc.php +++ b/application/itopwebpage.class.inc.php @@ -255,7 +255,8 @@ EOF $sText = "Your search"; $sOnClick = " onclick=\"this.value='';this.onclick=null;\""; } - echo "
    "; + $sUserName = UserRights::GetUser(); + echo "
    Logged as '$sUserName'   "; echo "
    \n"; echo "
    \n"; diff --git a/application/ui.linkswidget.class.inc.php b/application/ui.linkswidget.class.inc.php index f980adec1..31be4bef4 100644 --- a/application/ui.linkswidget.class.inc.php +++ b/application/ui.linkswidget.class.inc.php @@ -61,7 +61,7 @@ class UILinksWidget } // Many values (or even a unknown list) display an autocomplete - if ( (count($aAllowedValues) == 0) || (count($aAllowedValues) > 20) ) + if ( (count($aAllowedValues) == 0) || (count($aAllowedValues) > 50) ) { // too many choices, use an autocomplete // The input for the auto complete diff --git a/application/uiwizard.class.inc.php b/application/uiwizard.class.inc.php index 3c8a61192..53ef12a88 100644 --- a/application/uiwizard.class.inc.php +++ b/application/uiwizard.class.inc.php @@ -34,42 +34,45 @@ class UIWizard $sJSHandlerCode = ''; // Javascript code to be executed each time this step of the wizard is entered foreach($aStep as $sAttCode) { - $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode); - $sAttLabel = $oAttDef->GetLabel(); - $iOptions = isset($aStates[$this->m_sTargetState]['attribute_list'][$sAttCode]) ? $aStates[$this->m_sTargetState]['attribute_list'][$sAttCode] : 0; - - $aPrerequisites = $oAttDef->GetPrerequisiteAttributes(); - if ($iOptions & (OPT_ATT_MANDATORY | OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT)) + if ($sAttCode != 'finalclass') // Do not displa the attribute that stores the actual class name { - $aFields[$sAttCode] = array(); - foreach($aPrerequisites as $sCode) + $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode); + $sAttLabel = $oAttDef->GetLabel(); + $iOptions = isset($aStates[$this->m_sTargetState]['attribute_list'][$sAttCode]) ? $aStates[$this->m_sTargetState]['attribute_list'][$sAttCode] : 0; + + $aPrerequisites = $oAttDef->GetPrerequisiteAttributes(); + if ($iOptions & (OPT_ATT_MANDATORY | OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT)) { - $aFields[$sAttCode][$sCode] = ''; + $aFields[$sAttCode] = array(); + foreach($aPrerequisites as $sCode) + { + $aFields[$sAttCode][$sCode] = ''; + } } + if (count($aPrerequisites) > 0) + { + $aOptions[] = 'Prerequisites: '.implode(', ', $aPrerequisites); + } + + $sFieldFlag = ($iOptions & (OPT_ATT_MANDATORY | OPT_ATT_MUSTCHANGE)) ? ' *' : ''; + $oDefaultValuesSet = $oAttDef->GetDefaultValue(); // @@@ TO DO: get the object's current value if the object exists + $sHTMLValue = cmdbAbstractObject::GetFormElementForField($this->m_oPage, $this->m_sClass, $sAttCode, $oAttDef, $oDefaultValuesSet, '', "att_$iMaxInputId"); + $aFieldsMap[$iMaxInputId] = $sAttCode; + $aDetails[] = array('label' => $oAttDef->GetLabel().$sFieldFlag, 'value' => "
    $sHTMLValue
    "); + if ($oAttDef->GetValuesDef() != null) + { + $sJSHandlerCode .= "\toWizardHelper.RequestAllowedValues('$sAttCode');\n"; + } + if ($oAttDef->GetDefaultValue() != null) + { + $sJSHandlerCode .= "\toWizardHelper.RequestDefaultValue('$sAttCode');\n"; + } + if ($oAttDef->IsLinkSet()) + { + $sJSHandlerCode .= "\toLinkWidgetatt_$iMaxInputId.Init();"; + } + $iMaxInputId++; } - if (count($aPrerequisites) > 0) - { - $aOptions[] = 'Prerequisites: '.implode(', ', $aPrerequisites); - } - - $sFieldFlag = ($iOptions & (OPT_ATT_MANDATORY | OPT_ATT_MUSTCHANGE)) ? ' *' : ''; - $oDefaultValuesSet = $oAttDef->GetDefaultValue(); // @@@ TO DO: get the object's current value if the object exists - $sHTMLValue = cmdbAbstractObject::GetFormElementForField($this->m_oPage, $this->m_sClass, $sAttCode, $oAttDef, $oDefaultValuesSet, '', "att_$iMaxInputId"); - $aFieldsMap[$iMaxInputId] = $sAttCode; - $aDetails[] = array('label' => $oAttDef->GetLabel().$sFieldFlag, 'value' => "
    $sHTMLValue
    "); - if ($oAttDef->GetValuesDef() != null) - { - $sJSHandlerCode .= "\toWizardHelper.RequestAllowedValues('$sAttCode');\n"; - } - if ($oAttDef->GetDefaultValue() != null) - { - $sJSHandlerCode .= "\toWizardHelper.RequestDefaultValue('$sAttCode');\n"; - } - if ($oAttDef->IsLinkSet()) - { - $sJSHandlerCode .= "\toLinkWidgetatt_$iMaxInputId.Init();"; - } - $iMaxInputId++; } //$aDetails[] = array('label' => '', 'value' => ''); $this->m_oPage->details($aDetails); diff --git a/business/templates/Circuits.html b/business/templates/Circuits.html index c3de83c89..f04fe3a87 100644 --- a/business/templates/Circuits.html +++ b/business/templates/Circuits.html @@ -1,6 +1,6 @@ diff --git a/business/templates/application.html b/business/templates/application.html index 4e3c7575e..f934b7b48 100644 --- a/business/templates/application.html +++ b/business/templates/application.html @@ -1,6 +1,6 @@ diff --git a/business/templates/change.html b/business/templates/change.html index 694b26fd6..f2bcd67d7 100644 --- a/business/templates/change.html +++ b/business/templates/change.html @@ -1,6 +1,6 @@ diff --git a/business/templates/contract.html b/business/templates/contract.html index 574c6e37e..110fcdb0e 100644 --- a/business/templates/contract.html +++ b/business/templates/contract.html @@ -1,6 +1,6 @@ diff --git a/business/templates/default.html b/business/templates/default.html index 238a714d8..564b009b5 100644 --- a/business/templates/default.html +++ b/business/templates/default.html @@ -1,6 +1,6 @@ diff --git a/business/templates/document.html b/business/templates/document.html index 470559e8f..cd40cfe78 100644 --- a/business/templates/document.html +++ b/business/templates/document.html @@ -1,6 +1,6 @@ diff --git a/business/templates/group.html b/business/templates/group.html index 11bc83845..fca5fb154 100644 --- a/business/templates/group.html +++ b/business/templates/group.html @@ -1,6 +1,6 @@ diff --git a/business/templates/interface.html b/business/templates/interface.html index 3f1fe7f95..ffa8c7495 100644 --- a/business/templates/interface.html +++ b/business/templates/interface.html @@ -1,6 +1,6 @@ diff --git a/business/templates/knownError.html b/business/templates/knownError.html index 3c321384d..75bcc9eaf 100644 --- a/business/templates/knownError.html +++ b/business/templates/knownError.html @@ -1,6 +1,6 @@ bizKnownError: pkey = $pkey$ diff --git a/business/templates/location.html b/business/templates/location.html index af2df49e0..748ccd739 100644 --- a/business/templates/location.html +++ b/business/templates/location.html @@ -1,6 +1,6 @@ diff --git a/business/templates/network.device.html b/business/templates/network.device.html index cb3a32385..01085e2ec 100644 --- a/business/templates/network.device.html +++ b/business/templates/network.device.html @@ -1,6 +1,6 @@ diff --git a/business/templates/pc.html b/business/templates/pc.html index 55597a4ac..9323a913d 100644 --- a/business/templates/pc.html +++ b/business/templates/pc.html @@ -1,6 +1,6 @@ diff --git a/business/templates/person.html b/business/templates/person.html index 9bc09ad33..567f7b543 100644 --- a/business/templates/person.html +++ b/business/templates/person.html @@ -1,6 +1,6 @@ diff --git a/business/templates/server.html b/business/templates/server.html index f6f5793ed..98185836b 100644 --- a/business/templates/server.html +++ b/business/templates/server.html @@ -1,6 +1,6 @@ diff --git a/business/templates/service.html b/business/templates/service.html index 747ab187a..7e6443eba 100644 --- a/business/templates/service.html +++ b/business/templates/service.html @@ -1,6 +1,6 @@ diff --git a/business/templates/software.html b/business/templates/software.html index ee51b9651..3f690daad 100644 --- a/business/templates/software.html +++ b/business/templates/software.html @@ -1,6 +1,6 @@ diff --git a/business/templates/team.html b/business/templates/team.html index 05e0ec2d2..7e1e5cd6e 100644 --- a/business/templates/team.html +++ b/business/templates/team.html @@ -1,6 +1,6 @@ diff --git a/business/templates/ticket.html b/business/templates/ticket.html index 1c034cc7b..425c92840 100644 --- a/business/templates/ticket.html +++ b/business/templates/ticket.html @@ -1,6 +1,6 @@ diff --git a/core/userrights.class.inc.php b/core/userrights.class.inc.php index b62c8a042..01428ae73 100644 --- a/core/userrights.class.inc.php +++ b/core/userrights.class.inc.php @@ -176,7 +176,7 @@ class UserRights public static function GetFilter($sClass) { - if (!MetaModel::HasCategory($sClass, 'bizModel')) return new DBObjectSearch($sClass); + if (!MetaModel::HasCategory($sClass, 'bizmodel')) return new DBObjectSearch($sClass); if (!self::CheckLogin()) return false; return self::$m_oAddOn->GetFilter(self::$m_iUserId, $sClass); @@ -184,7 +184,7 @@ class UserRights public static function IsActionAllowed($sClass, $iActionCode, dbObjectSet $aInstances) { - if (!MetaModel::HasCategory($sClass, 'bizModel')) return true; + if (!MetaModel::HasCategory($sClass, 'bizmodel')) return true; if (!self::CheckLogin()) return false; return self::$m_oAddOn->IsActionAllowed(self::$m_iUserId, $sClass, $iActionCode, $aInstances); @@ -192,7 +192,7 @@ class UserRights public static function IsStimulusAllowed($sClass, $sStimulusCode, dbObjectSet $aInstances) { - if (!MetaModel::HasCategory($sClass, 'bizModel')) return true; + if (!MetaModel::HasCategory($sClass, 'bizmodel')) return true; if (!self::CheckLogin()) return false; return self::$m_oAddOn->IsStimulusAllowed(self::$m_iUserId, $sClass, $sStimulusCode, $aInstances); @@ -200,7 +200,7 @@ class UserRights public static function IsActionAllowedOnAttribute($sClass, $sAttCode, $iActionCode, dbObjectSet $aInstances) { - if (!MetaModel::HasCategory($sClass, 'bizModel')) return true; + if (!MetaModel::HasCategory($sClass, 'bizmodel')) return true; if (!self::CheckLogin()) return false; return self::$m_oAddOn->IsActionAllowedOnAttribute(self::$m_iUserId, $sClass, $sAttCode, $iActionCode, $aInstances); diff --git a/pages/UI.php b/pages/UI.php index 3b77f4ce7..48bd6081b 100644 --- a/pages/UI.php +++ b/pages/UI.php @@ -243,22 +243,17 @@ switch($operation) $oP->add_linked_script("../js/jquery.blockUI.js"); $oWizard = new UIWizard($oP, $sClass, $sStateCode); $sStateCode = $oWizard->GetTargetState(); // Will computes the default state if none was supplied + $sClassLabel = MetaModel::GetName($sClass); + $oP->p("

    Creation of a new $sClassLabel

    "); if (!empty($sStateCode)) { $aStates = MetaModel::EnumStates($sClass); $sStateLabel = $aStates[$sStateCode]['label']; - $oP->p("Wizard for creating an object of class '$sClass' in state '$sStateCode'."); - } - else - { - // Stateless object - $oP->p("Wizard for creating an object of class '$sClass'."); } $aWizardSteps = $oWizard->GetWizardStructure(); // Display the structure of the wizard $iStepIndex = 1; - $oP->p("

    Wizard Steps for creating an object of class '$sClass' in state '$sStateCode'

    \n"); $iMaxInputId = 0; $aFieldsMap = array(); foreach($aWizardSteps['mandatory'] as $aSteps) diff --git a/pages/opensearch.xml b/pages/opensearch.xml deleted file mode 100644 index 2620b2d1f..000000000 --- a/pages/opensearch.xml +++ /dev/null @@ -1,8 +0,0 @@ - -iTop -webmaster@itop.com -Recherche dans iTop -ISO-8859-1 - -http://localhost:81/pages/UI.php - \ No newline at end of file diff --git a/pages/schema.php b/pages/schema.php index ef06f230f..f3a4ba52e 100644 --- a/pages/schema.php +++ b/pages/schema.php @@ -160,7 +160,7 @@ function DisplayLifecycle($oPage, $sClass) { $aStates = MetaModel::EnumStates($sClass); $aStimuli = MetaModel::EnumStimuli($sClass); - $oPage->add("\n"); + $oPage->add("\n"); $oPage->add("

    Transitions

    \n"); $oPage->add("
      \n"); foreach ($aStates as $sStateCode => $aStateDef) diff --git a/setup/data/03.persons.xml b/setup/data/03.persons.xml index fa6317ff5..9a102ad6e 100644 --- a/setup/data/03.persons.xml +++ b/setup/data/03.persons.xml @@ -1,33 +1,33 @@ -Denis +Verne production 3 -denis.flaven@gmail.com +jules.verne@gmail.com 29 -Flaven +Jules -Quetiez +Dumas production 3 -romain.quetiez@gmail.com +alexandre.dumas@gmail.com 1 -Romain +Dumas -Taloc +Hugo production 3 -erwan.taloc@gmail.com +victor.hugo@gmail.com 33172382223 1 -Erwan +Victor e12345 - \ No newline at end of file + diff --git a/setup/menus.xml b/setup/menus.xml index 087d64ea3..756cfd4ba 100644 --- a/setup/menus.xml +++ b/setup/menus.xml @@ -592,7 +592,7 @@ text-align:center; <p></p> <p style="text-align:center; font-family:Georgia, 'Times New Roman', Times, serif; font-size:32px;">Welcome to iTop</p> <p></p> -<p style="text-align:center; font-family:Georgia, 'Times New Roman', Times, serif; font-size:14px;"><i>Version 0.7</i></p> +<p style="text-align:center; font-family:Georgia, 'Times New Roman', Times, serif; font-size:14px;"><i>Version 0.8</i></p> 1 From bce801021a786207eba162efa79b80e5ee96a9fe Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Sun, 19 Jul 2009 09:32:44 +0000 Subject: [PATCH 009/227] - enhancement: trap exceptions to keep a nice display in case of error SVN:trunk[84] --- pages/sibusql.php | 71 ++++++++++++++++++++++++++++------------------- 1 file changed, 42 insertions(+), 29 deletions(-) diff --git a/pages/sibusql.php b/pages/sibusql.php index 956d28d4d..3780896f2 100644 --- a/pages/sibusql.php +++ b/pages/sibusql.php @@ -19,38 +19,51 @@ $oP = new iTopWebPage("iTop - Expression Evaluation", $currentOrganization); $sExpression = utils::ReadParam('expression', ''); $sEncoding = utils::ReadParam('encoding', 'oql'); -if ($sEncoding == 'crypted') +try { - // Translate $sExpression into a oql expression - $sClearText = base64_decode($sExpression); - echo "FYI: '$sClearText'
      \n"; - $oFilter = DBObjectSearch::unserialize($sExpression); - $sExpression = $oFilter->ToOQL(); - exit; -} -else -{ - // leave $sExpression as is -} - -$oP->add('
      '."\n"); -$oP->add('Expression to evaluate:
      '."\n"); -$oP->add(''."

      Example:
      SELECT bizPerson AS B WHERE B.name LIKE '%A%'

      \n"); -$oP->add(''."\n"); -$oP->add('
      '."\n"); - -if (!empty($sExpression)) -{ - $oFilter = DBObjectSearch::FromOQL($sExpression); - if ($oFilter) + if ($sEncoding == 'crypted') { - $oP->p('Query expression: '.$oFilter->ToOQL()); - $oP->p('Serialized filter: '.$oFilter->serialize()); - - $oSet = new CMDBObjectSet($oFilter); - $oP->p('The query returned '.$oSet->count().' results(s)'); - cmdbAbstractObject::DisplaySet($oP, $oSet); + // Translate $sExpression into a oql expression + $sClearText = base64_decode($sExpression); + echo "FYI: '$sClearText'
      \n"; + $oFilter = DBObjectSearch::unserialize($sExpression); + $sExpression = $oFilter->ToOQL(); + exit; } + else + { + // leave $sExpression as is + } + + $oP->add('
      '."\n"); + $oP->add('Expression to evaluate:
      '."\n"); + $oP->add(''."

      Example:
      SELECT bizPerson AS B WHERE B.name LIKE '%A%'

      \n"); + $oP->add(''."\n"); + $oP->add('
      '."\n"); + + if (!empty($sExpression)) + { + $oFilter = DBObjectSearch::FromOQL($sExpression); + if ($oFilter) + { + $oP->p('Query expression: '.$oFilter->ToOQL()); + $oP->p('Serialized filter: '.$oFilter->serialize()); + + $oSet = new CMDBObjectSet($oFilter); + $oP->p('The query returned '.$oSet->count().' results(s)'); + cmdbAbstractObject::DisplaySet($oP, $oSet); + } + } +} +catch(CoreException $e) +{ + $oP->p('An error occured while running the query:'); + $oP->p($e->getHtmlDesc()); +} +catch(Exception $e) +{ + $oP->p('An error occured while running the query:'); + $oP->p($e->getMessage()); } $oP->output(); From 05a236f06a61c22cfdad13a82c5521de3b02ee10 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Sun, 19 Jul 2009 09:34:32 +0000 Subject: [PATCH 010/227] - Fixed bug #31: HTML display in the "export" page uses the appropriate stylesheet. SVN:trunk[85] --- application/cmdbabstract.class.inc.php | 4 ++-- webservices/export.php | 6 +++--- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index 3bd6401a5..aaf8bad4f 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -213,9 +213,9 @@ abstract class cmdbAbstractObject extends CMDBObject // Comment by Rom: this helper may be used to display objects of class DBObject // -> I am using this to display the changes history - public static function DisplaySet(web_page $oPage, CMDBObjectSet $oSet, $sLinkageAttribute = '') + public static function DisplaySet(web_page $oPage, CMDBObjectSet $oSet, $sLinkageAttribute = '', $bDisplayMenu = true) { - $oPage->add(self::GetDisplaySet($oPage, $oSet, $sLinkageAttribute)); + $oPage->add(self::GetDisplaySet($oPage, $oSet, $sLinkageAttribute, $bDisplayMenu)); } public static function GetDisplaySet(web_page $oPage, CMDBObjectSet $oSet, $sLinkageAttribute = '', $bDisplayMenu = true) diff --git a/webservices/export.php b/webservices/export.php index 530f95279..f6645e81a 100644 --- a/webservices/export.php +++ b/webservices/export.php @@ -1,6 +1,6 @@ Date: Mon, 20 Jul 2009 13:41:21 +0000 Subject: [PATCH 011/227] - new test case for JOINs SVN:trunk[86] --- pages/testlist.inc.php | 1 + 1 file changed, 1 insertion(+) diff --git a/pages/testlist.inc.php b/pages/testlist.inc.php index 947d48489..db9b448d8 100644 --- a/pages/testlist.inc.php +++ b/pages/testlist.inc.php @@ -820,6 +820,7 @@ class TestQueriesOnFarm extends TestBizModel 'SELECT Mammal AS m WHERE 1/0' => true, 'SELECT Mammal AS m WHERE MONTH(m.birth) = 7' => true, 'SELECT Animal JOIN Group ON Group.leader = Animal.id' => true, + 'SELECT Group JOIN Animal ON Group.leader = Animal.id' => true, 'SELECT Animal AS A JOIN Group AS G1 ON G1.leader = A.id' => true, 'SELECT Animal AS A JOIN Group AS G ON FooClass.leader = A.id' => false, 'SELECT Animal AS A JOIN Group AS G ON G.leader = FooClass.id' => false, From 2a7c89af215b200050f3435e078b1326358b50c3 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Tue, 21 Jul 2009 08:48:30 +0000 Subject: [PATCH 012/227] Initialized dev of the user rights managed by profiles, it is still not usable by the application, but its data model is likely to be stable. SVN:trunk[87] --- .../userrightsprofile.class.inc.php | 659 ++++++++++++++++++ application/menunode.class.inc.php | 2 +- config-dist.php | 2 +- 3 files changed, 661 insertions(+), 2 deletions(-) create mode 100644 addons/userrights/userrightsprofile.class.inc.php diff --git a/addons/userrights/userrightsprofile.class.inc.php b/addons/userrights/userrightsprofile.class.inc.php new file mode 100644 index 000000000..3fd9c9f24 --- /dev/null +++ b/addons/userrights/userrightsprofile.class.inc.php @@ -0,0 +1,659 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ + + +class URP_Users extends DBObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "addon/userrights", + "name" => "user", + "description" => "users and credentials", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "login", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_urp_users", + "db_key_field" => "id", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + //MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeInteger("userid", array("label"=>"User id", "description"=>"User identifier (depends on the business model)", "allowed_values"=>null, "sql"=>"userid", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("login", array("label"=>"login", "description"=>"user identification string", "allowed_values"=>null, "sql"=>"login", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("password", array("label"=>"password", "description"=>"user authentication string", "allowed_values"=>null, "sql"=>"pwd", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("email", array("label"=>"email", "description"=>"email address", "allowed_values"=>null, "sql"=>"email", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("firstname", array("label"=>"firstname", "description"=>"first name", "allowed_values"=>null, "sql"=>"firstname", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("lastname", array("label"=>"lastname", "description"=>"last name", "allowed_values"=>null, "sql"=>"lastname", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + + //MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("userid"); + MetaModel::Init_AddFilterFromAttribute("login"); + } +} + +class URP_Profiles extends DBObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "addon/userrights", + "name" => "profile", + "description" => "usage profiles", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "name", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_urp_profiles", + "db_key_field" => "id", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + //MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeString("name", array("label"=>"name", "description"=>"label", "allowed_values"=>null, "sql"=>"name", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("description", array("label"=>"description", "description"=>"one line description", "allowed_values"=>null, "sql"=>"description", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + + //MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("name"); + MetaModel::Init_AddFilterFromAttribute("description"); + } +} + +class URP_Dimensions extends DBObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "addon/userrights", + "name" => "dimension", + "description" => "application dimension (defining silos)", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "name", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_urp_dimensions", + "db_key_field" => "id", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + //MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeString("name", array("label"=>"name", "description"=>"label", "allowed_values"=>null, "sql"=>"name", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("description", array("label"=>"description", "description"=>"one line description", "allowed_values"=>null, "sql"=>"description", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + + //MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("name"); + MetaModel::Init_AddFilterFromAttribute("description"); + } +} + +class URP_UserProfile extends DBObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "addon/userrights", + "name" => "user_profile", + "description" => "user profiles", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_urp_userprofile", + "db_key_field" => "id", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + //MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", array("targetclass"=>"URP_Users", "jointype"=> "", "label"=>"User", "description"=>"user account", "allowed_values"=>null, "sql"=>"userid", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("userlogin", array("label"=>"Login", "description"=>"User's login", "allowed_values"=>null, "extkey_attcode"=> 'userid', "target_attcode"=>"login"))); + + MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass"=>"URP_Profiles", "jointype"=> "", "label"=>"profile", "description"=>"usage profile", "allowed_values"=>null, "sql"=>"profileid", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("profile", array("label"=>"Profile", "description"=>"Profile name", "allowed_values"=>null, "extkey_attcode"=> 'profileid', "target_attcode"=>"name"))); + + //MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("userid"); + MetaModel::Init_AddFilterFromAttribute("profileid"); + } +} + +class URP_ProfileProjection extends DBObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "addon/userrights", + "name" => "profile_projection", + "description" => "profile projections", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_urp_profileprojection", + "db_key_field" => "id", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + //MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeExternalKey("dimensionid", array("targetclass"=>"URP_Dimensions", "jointype"=> "", "label"=>"Dimension", "description"=>"application dimension", "allowed_values"=>null, "sql"=>"dimensionid", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("dimension", array("label"=>"Dimension", "description"=>"application dimension", "allowed_values"=>null, "extkey_attcode"=> 'dimensionid', "target_attcode"=>"name"))); + + MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass"=>"URP_Profiles", "jointype"=> "", "label"=>"profile", "description"=>"usage profile", "allowed_values"=>null, "sql"=>"profileid", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("profile", array("label"=>"Profile", "description"=>"Profile name", "allowed_values"=>null, "extkey_attcode"=> 'profileid', "target_attcode"=>"name"))); + + MetaModel::Init_AddAttribute(new AttributeString("value", array("label"=>"Value expression", "description"=>"OQL expression (using \$user) | constant", "allowed_values"=>null, "sql"=>"value", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + + //MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("dimensionid"); + MetaModel::Init_AddFilterFromAttribute("profileid"); + } + + public function ProjectUser(URP_Users $oUser) + { + // #@# to be implemented + } +} + +class URP_ClassProjection extends DBObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "addon/userrights", + "name" => "class_projection", + "description" => "class projections", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_urp_classprojection", + "db_key_field" => "id", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + //MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeExternalKey("dimensionid", array("targetclass"=>"URP_Dimensions", "jointype"=> "", "label"=>"Dimension", "description"=>"application dimension", "allowed_values"=>null, "sql"=>"dimensionid", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("dimension", array("label"=>"Dimension", "description"=>"application dimension", "allowed_values"=>null, "extkey_attcode"=> 'dimensionid', "target_attcode"=>"name"))); + + MetaModel::Init_AddAttribute(new AttributeString("class", array("label"=>"Class", "description"=>"Target class", "allowed_values"=>null, "sql"=>"class", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_AddAttribute(new AttributeString("value", array("label"=>"Value expression", "description"=>"OQL expression (using \$this) | constant", "allowed_values"=>null, "sql"=>"value", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + + //MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("dimensionid"); + MetaModel::Init_AddFilterFromAttribute("class"); + } +} + +class URP_ClassGrant extends DBObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "addon/userrights", + "name" => "class_permission", + "description" => "permissions on classes", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_urp_grant_classes", + "db_key_field" => "id", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + //MetaModel::Init_InheritAttributes(); + + // Common to all grant classes (could be factorized by class inheritence, but this has to be benchmarked) + MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass"=>"URP_Profiles", "jointype"=> "", "label"=>"Profile", "description"=>"usage profile", "allowed_values"=>null, "sql"=>"profileid", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("profile", array("label"=>"Profile", "description"=>"usage profile", "allowed_values"=>null, "extkey_attcode"=> 'profileid', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeString("class", array("label"=>"class", "description"=>"class name", "allowed_values"=>null, "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("label"=>"permission", "description"=>"allowed or not allowed?", "allowed_values"=>new ValueSetEnum('yes,no'), "sql"=>"permission", "default_value"=>"yes", "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_AddAttribute(new AttributeString("action", array("label"=>"action", "description"=>"operations to perform on the given class", "allowed_values"=>null, "sql"=>"action", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + + //MetaModel::Init_InheritFilters(); + // Common to all grant classes (could be factorized by class inheritence, but this has to be benchmarked) + MetaModel::Init_AddFilterFromAttribute("profileid"); + MetaModel::Init_AddFilterFromAttribute("profile"); + MetaModel::Init_AddFilterFromAttribute("class"); + + MetaModel::Init_AddFilterFromAttribute("action"); + } +} + +class URP_StimulusGrant extends DBObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "addon/userrights", + "name" => "stimulus_permission", + "description" => "permissions on stimilus in the life cycle of the object", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_urp_grant_stimulus", + "db_key_field" => "id", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + //MetaModel::Init_InheritAttributes(); + + // Common to all grant classes (could be factorized by class inheritence, but this has to be benchmarked) + MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass"=>"URP_Profiles", "jointype"=> "", "label"=>"Profile", "description"=>"usage profile", "allowed_values"=>null, "sql"=>"profileid", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("profile", array("label"=>"Profile", "description"=>"usage profile", "allowed_values"=>null, "extkey_attcode"=> 'profileid', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeString("class", array("label"=>"class", "description"=>"class name", "allowed_values"=>null, "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("label"=>"permission", "description"=>"allowed or not allowed?", "allowed_values"=>new ValueSetEnum('yes,no'), "sql"=>"permission", "default_value"=>"yes", "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_AddAttribute(new AttributeString("stimulus", array("label"=>"action", "description"=>"operations to perform on the given class", "allowed_values"=>null, "sql"=>"action", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + + //MetaModel::Init_InheritFilters(); + // Common to all grant classes (could be factorized by class inheritence, but this has to be benchmarked) + MetaModel::Init_AddFilterFromAttribute("profileid"); + MetaModel::Init_AddFilterFromAttribute("profile"); + MetaModel::Init_AddFilterFromAttribute("class"); + + MetaModel::Init_AddFilterFromAttribute("stimulus"); + } +} + +class URP_AttributeGrant extends DBObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "addon/userrights", + "name" => "attribute_permission", + "description" => "permissions at the attributes level", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_urp_grant_attributes", + "db_key_field" => "id", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + //MetaModel::Init_InheritAttributes(); + + // Common to all grant classes (could be factorized by class inheritence, but this has to be benchmarked) + MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass"=>"URP_Profiles", "jointype"=> "", "label"=>"Profile", "description"=>"usage profile", "allowed_values"=>null, "sql"=>"profileid", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("profile", array("label"=>"Profile", "description"=>"usage profile", "allowed_values"=>null, "extkey_attcode"=> 'profileid', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeString("class", array("label"=>"class", "description"=>"class name", "allowed_values"=>null, "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("label"=>"permission", "description"=>"allowed or not allowed?", "allowed_values"=>new ValueSetEnum('yes,no'), "sql"=>"permission", "default_value"=>"yes", "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_AddAttribute(new AttributeString("attcode", array("label"=>"attribute", "description"=>"attribute code", "allowed_values"=>null, "sql"=>"attcode", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("action", array("label"=>"action", "description"=>"operations to perform on the given class", "allowed_values"=>null, "sql"=>"action", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + + //MetaModel::Init_InheritFilters(); + // Common to all grant classes (could be factorized by class inheritence, but this has to be benchmarked) + MetaModel::Init_AddFilterFromAttribute("profileid"); + MetaModel::Init_AddFilterFromAttribute("profile"); + MetaModel::Init_AddFilterFromAttribute("class"); + + MetaModel::Init_AddFilterFromAttribute("attcode"); + MetaModel::Init_AddFilterFromAttribute("action"); + } +} + + + + +class UserRightsProfile extends UserRightsAddOnAPI +{ + static public $m_aActionCodes = array( + UR_ACTION_READ => 'read', + UR_ACTION_MODIFY => 'modify', + UR_ACTION_DELETE => 'delete', + UR_ACTION_BULK_READ => 'bulk read', + UR_ACTION_BULK_MODIFY => 'bulk modify', + UR_ACTION_BULK_DELETE => 'bulk delete', + ); + + // Installation: create the very first user + public function CreateAdministrator($sAdminUser, $sAdminPwd, $sAdminEmail, $sFirstName, $sLastName, $sPhoneNumber) + { + // Maybe we should check that no other user with userid == 0 exists + $oUser = new URP_Users(); + $oUser->Set('login', $sAdminUser); + $oUser->Set('password', $sAdminPwd); + $oUser->Set('email', $sAdminEmail); + $oUser->Set('firstname', $sFirstName); + $oUser->Set('lastname', $sLastName); + $oUser->Set('phonenumber', $sPhoneNumber); + $oUser->Set('userid', 1); // one is for root ! + $iUserId = $oUser->DBInsertNoReload(); + $this->SetupUser($iUserId, true); + return true; + } + + public function Setup() + { + // Dimensions/Profiles/Classes/Attributes/Stimuli could be added anytime + // This procedure will then update the matrix with expected default values + // + + $oProfileSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_Profiles")); + while ($oProfile = $oProfileSet->Fetch()) + { + $this->SetupProfile($oProfile); + } + + $oDimensionSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_Dimensions")); + while ($oDimension = $oDimensionSet->Fetch()) + { + $this->SetupDimension($oDimension); + } + return true; + } + + protected function SetupDimension($oDimension, $bNewDimension = false) + { + $iDimensionId = $oDimension->GetKey(); + + // Create projections, for any class where it applies + // + foreach(array('bizmodel', 'application', 'gui', 'core/cmdb') as $sCategory) + { + foreach (MetaModel::GetClasses($sCategory) as $sClass) + { + if ($bNewDimension) + { + $bAddCell = true; + } + else + { + $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_ClassProjection WHERE class = '$sClass' AND dimensionid = $iDimensionId")); + $bAddCell = ($oSet->Count() < 1); + } + if ($bAddCell) + { + // Create a new entry + $oCProj = MetaModel::NewObject("URP_ClassProjection"); + $oCProj->Set("dimensionid", $iDimensionId); + $oCProj->Set("class", $sClass); + $oCProj->Set("value", "true"); + $iId = $oCProj->DBInsertNoReload(); + } + } + } + // Create projections, for any existing profile + // + $oProfileSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_Profiles")); + while ($oProfile = $oProfileSet->Fetch()) + { + $iProfileId = $oProfile->GetKey(); + if ($bNewDimension) + { + $bAddCell = true; + } + else + { + $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_ProfileProjection WHERE dimensionid = $iDimensionId AND profileid = $iProfileId")); + $bAddCell = ($oSet->Count() < 1); + } + if ($bAddCell) + { + // Create a new entry + $oDProj = MetaModel::NewObject("URP_ProfileProjection"); + $oDProj->Set("dimensionid", $iDimensionId); + $oDProj->Set("profileid", $iProfileId); + $oDProj->Set("value", "true"); + $iId = $oDProj->DBInsertNoReload(); + } + } + } + + protected function SetupProfile($oProfile, $bNewProfile = false) + { + $iProfileId = $oProfile->GetKey(); + + // Create grant records, for any class where it applies + // + foreach(array('bizmodel', 'application', 'gui', 'core/cmdb') as $sCategory) + { + foreach (MetaModel::GetClasses($sCategory) as $sClass) + { + foreach (self::$m_aActionCodes as $iActionCode => $sAction) + { + if ($bNewProfile) + { + $bAddCell = true; + } + else + { + $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_ClassGrant WHERE class = '$sClass' AND action = '$sAction' AND profileid = $iProfileId")); + $bAddCell = ($oSet->Count() < 1); + } + if ($bAddCell) + { + // Create a new entry + $oMyClassGrant = MetaModel::NewObject("URP_ClassGrant"); + $oMyClassGrant->Set("profileid", $iProfileId); + $oMyClassGrant->Set("class", $sClass); + $oMyClassGrant->Set("action", $sAction); + $oMyClassGrant->Set("permission", "yes"); + $iId = $oMyClassGrant->DBInsertNoReload(); + } + } + foreach (MetaModel::EnumStimuli($sClass) as $sStimulusCode => $oStimulus) + { + if ($bNewProfile) + { + $bAddCell = true; + } + else + { + $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_StimulusGrant WHERE class = '$sClass' AND stimulus = '$sStimulusCode' AND profileid = $iProfileId")); + $bAddCell = ($oSet->Count() < 1); + } + if ($bAddCell) + { + // Create a new entry + $oMyClassGrant = MetaModel::NewObject("URP_StimulusGrant"); + $oMyClassGrant->Set("profileid", $iProfileId); + $oMyClassGrant->Set("class", $sClass); + $oMyClassGrant->Set("stimulus", $sStimulusCode); + $oMyClassGrant->Set("permission", "yes"); + $iId = $oMyClassGrant->DBInsertNoReload(); + } + } + foreach (MetaModel::GetAttributesList($sClass) as $sAttCode) + { + if ($bNewProfile) + { + $bAddCell = true; + } + else + { + $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_AttributeGrant WHERE class = '$sClass' AND attcode = '$sAttCode' AND profileid = $iProfileId")); + $bAddCell = ($oSet->Count() < 1); + } + if ($bAddCell) + { + foreach (array('read', 'modify') as $sAction) + { + // Create a new entry + $oMyAttGrant = MetaModel::NewObject("URP_AttributeGrant"); + $oMyAttGrant->Set("profileid", $iProfileId); + $oMyAttGrant->Set("class", $sClass); + $oMyAttGrant->Set("attcode", $sAttCode); + $oMyAttGrant->Set("action", $sAction); + $oMyAttGrant->Set("permission", "yes"); + $iId = $oMyAttGrant->DBInsertNoReload(); + } + } + } + } + } + // Create the "My Bookmarks" menu item (parent_id = 0, rank = 6) + if ($bNewProfile) + { + $bAddMenu = true; + } + else + { + //$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT menuNode WHERE type = 'user' AND parent_id = 0 AND user_id = $iUserId")); + //$bAddMenu = ($oSet->Count() < 1); + } + $bAddMenu = false; + if ($bAddMenu) + { + $oMenu = MetaModel::NewObject('menuNode'); + $oMenu->Set('type', 'user'); + $oMenu->Set('parent_id', 0); // It's a toplevel entry + $oMenu->Set('rank', 6); // Located just above the Admin Tools section (=7) + $oMenu->Set('name', 'My Bookmarks'); + $oMenu->Set('label', 'My Favorite Items'); + $oMenu->Set('hyperlink', 'UI.php'); + $oMenu->Set('template', '

      My bookmarks

      This section contains my most favorite search results

      '); + $oMenu->Set('user_id', $iUserId); + $oMenu->DBInsert(); + } + } + + + public function Init() + { + // Could be loaded in a shared memory (?) + return true; + } + + public function CheckCredentials($sUserName, $sPassword) + { + $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_Users WHERE login = '$sUserName'")); + if ($oSet->Count() < 1) + { + // todo: throw an exception? + return false; + } + + $oLogin = $oSet->Fetch(); + if ($oLogin->Get('password') == $sPassword) + { + return true; + } + // todo: throw an exception? + return false; + } + + public function GetFilter($sUserName, $sClass) + { + $oNullFilter = new DBObjectSearch($sClass); + return $oNullFilter; + } + + public function IsActionAllowed($sUserName, $sClass, $iActionCode, dbObjectSet $aInstances) + { + if (!array_key_exists($iActionCode, self::$m_aActionCodes)) + { + return UR_ALLOWED_NO; + } + $sAction = self::$m_aActionCodes[$iActionCode]; + + $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_ClassGrant WHERE class = '$sClass' AND action = '$sAction' AND login = '$sUserName'")); + if ($oSet->Count() < 1) + { + return UR_ALLOWED_NO; + } + + $oGrantRecord = $oSet->Fetch(); + switch ($oGrantRecord->Get('permission')) + { + case 'yes': + $iRetCode = UR_ALLOWED_YES; + break; + case 'no': + default: + $iRetCode = UR_ALLOWED_NO; + break; + } + return $iRetCode; + } + + public function IsActionAllowedOnAttribute($sUserName, $sClass, $sAttCode, $iActionCode, dbObjectSet $aInstances) + { + if (!array_key_exists($iActionCode, self::$m_aActionCodes)) + { + return UR_ALLOWED_NO; + } + $sAction = self::$m_aActionCodes[$iActionCode]; + + $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_AttributeGrant WHERE URP_AttributeGrant.class = '$sClass' AND URP_AttributeGrant.attcode = '$sAttCode' AND URP_AttributeGrant.action = '$sAction' AND URP_AttributeGrant.login = '$sUserName'")); + if ($oSet->Count() < 1) + { + return UR_ALLOWED_NO; + } + + $oGrantRecord = $oSet->Fetch(); + switch ($oGrantRecord->Get('permission')) + { + case 'yes': + $iRetCode = UR_ALLOWED_YES; + break; + case 'no': + default: + $iRetCode = UR_ALLOWED_NO; + break; + } + return $iRetCode; + } + + public function IsStimulusAllowed($sUserName, $sClass, $sStimulusCode, dbObjectSet $aInstances) + { + $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_StimulusGrant WHERE class = '$sClass' AND stimulus = '$sStimulusCode' AND login = '$sUserName'")); + if ($oSet->Count() < 1) + { + return UR_ALLOWED_NO; + } + + $oGrantRecord = $oSet->Fetch(); + switch ($oGrantRecord->Get('permission')) + { + case 'yes': + $iRetCode = UR_ALLOWED_YES; + break; + case 'no': + default: + $iRetCode = UR_ALLOWED_NO; + break; + } + return $iRetCode; + } +} + +UserRights::SelectModule('UserRightsProfile'); + +?> diff --git a/application/menunode.class.inc.php b/application/menunode.class.inc.php index e0b72ec62..72ddfeb23 100644 --- a/application/menunode.class.inc.php +++ b/application/menunode.class.inc.php @@ -46,7 +46,7 @@ class menuNode extends DBObject MetaModel::Init_AddAttribute(new AttributeInteger("rank", array("label"=>"Display rank", "description"=>"Sort order for displaying the menu", "allowed_values"=>null, "sql"=>"rank", "default_value" => 999, "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalKey("parent_id", array("label"=>"Parent Menu Item", "description"=>"Parent Menu Item", "allowed_values"=>null, "sql"=>"parent_id", "targetclass"=>"menuNode", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("parent_name", array("label"=>"Parent Menu Item", "description"=>"Parent Menu Item", "allowed_values"=>null, "extkey_attcode"=>"parent_id", "target_attcode"=>"name"))); - MetaModel::Init_AddAttribute(new AttributeExternalKey("user_id", array("label"=>"Owner of the menu", "description"=>"User who owns this menu (for user defined menus)", "allowed_values"=>null, "sql"=>"user_id", "targetclass"=>"UserRightsMatrixUsers", "is_null_allowed"=>true, "depends_on"=>array('type')))); + MetaModel::Init_AddAttribute(new AttributeInteger("user_id", array("label"=>"Owner of the menu", "description"=>"User who owns this menu (for user defined menus)", "allowed_values"=>null, "sql"=>"user_id", "default_value" => 0, "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddFilterFromAttribute("label"); MetaModel::Init_AddFilterFromAttribute("parent_id"); diff --git a/config-dist.php b/config-dist.php index db5babff2..58fea1767 100644 --- a/config-dist.php +++ b/config-dist.php @@ -29,7 +29,7 @@ $MyModules = array( // to be continued... ), 'addons' => array ( - 'user rights' => '../addons/userrights/userrightsmatrix.class.inc.php', + 'user rights' => '../addons/userrights/userrightsprofile.class.inc.php', // other modules to come later ) ); From 6b6f89f1c1e1f42051bd0d40f82d70997ff4dcf7 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Fri, 24 Jul 2009 13:14:51 +0000 Subject: [PATCH 013/227] User management by profile moving forward: pages to check the projection of objects/users in user defined dimensions Introduced parameters in OQL (:myvar) Added the verb MetaModel::IsValidObject($oMyObj) SVN:trunk[88] --- .../userrightsprofile.class.inc.php | 201 ++-- business/itop.business.class.inc.php | 2 + core/config.class.inc.php | 2 +- core/coreexception.class.inc.php | 4 +- core/dbobjectsearch.class.php | 17 +- core/dbobjectset.class.php | 7 +- core/expression.class.inc.php | 65 +- core/metamodel.class.php | 32 +- core/oql/oql-lexer.php | 106 +- core/oql/oql-lexer.plex | 10 +- core/oql/oql-parser.php | 956 +++++++++--------- core/oql/oql-parser.y | 17 +- core/oql/oqlinterpreter.class.inc.php | 18 +- core/oql/oqlquery.class.inc.php | 58 +- core/sqlquery.class.inc.php | 11 +- core/valuesetdef.class.inc.php | 44 +- pages/testlist.inc.php | 4 +- pages/usermanagement_classproj.php | 90 ++ pages/usermanagement_profileproj.php | 100 ++ 19 files changed, 1107 insertions(+), 637 deletions(-) create mode 100644 pages/usermanagement_classproj.php create mode 100644 pages/usermanagement_profileproj.php diff --git a/addons/userrights/userrightsprofile.class.inc.php b/addons/userrights/userrightsprofile.class.inc.php index 3fd9c9f24..7b72df453 100644 --- a/addons/userrights/userrightsprofile.class.inc.php +++ b/addons/userrights/userrightsprofile.class.inc.php @@ -98,10 +98,81 @@ class URP_Dimensions extends DBObject //MetaModel::Init_InheritAttributes(); MetaModel::Init_AddAttribute(new AttributeString("name", array("label"=>"name", "description"=>"label", "allowed_values"=>null, "sql"=>"name", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("description", array("label"=>"description", "description"=>"one line description", "allowed_values"=>null, "sql"=>"description", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("type", array("label"=>"type", "description"=>"class name or data type (projection unit)", "allowed_values"=>new ValueSetEnumClasses('bizmodel', 'String,Integer'), "sql"=>"type", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); //MetaModel::Init_InheritFilters(); MetaModel::Init_AddFilterFromAttribute("name"); MetaModel::Init_AddFilterFromAttribute("description"); + MetaModel::Init_AddFilterFromAttribute("type"); + } + + public function CheckProjectionSpec($oProjectionSpec) + { + $sExpression = $oProjectionSpec->Get('value'); + $sAttribute = $oProjectionSpec->Get('attribute'); + + // Shortcut: "any value" or "no value" means no projection + if (empty($sExpression)) return; + if ($sExpression == '') return; + + // 1st - compute the data type for the dimension + // + $sType = $this->Get('type'); + if (MetaModel::IsValidClass($sType)) + { + $sExpectedType = $sType; + } + else + { + $sExpectedType = '_scalar_'; + } + + // 2nd - compute the data type for the projection + // + $bIsOql = true; + $sExpressionClass = ''; + try + { + $oObjectSearch = DBObjectSearch::FromOQL($sExpression); + $sExpressionClass = $oObjectSearch->GetClass(); + } + catch (OqlException $e) + { + $bIsOql = false; + } + if ($bIsOql) + { + if (empty($sAttribute)) + { + $sFoundType = $sExpressionClass; + } + else + { + if (!MetaModel::IsValidAttCode($sExpressionClass, $sAttribute)) + { + throw new CoreException('Unkown attribute code in projection specification', array('found' => $sAttribute, 'expecting' => MetaModel::GetAttributesList($sExpressionClass), 'class' => $sExpressionClass, 'projection' => $oProjectionSpec)); + } + $oAttDef = MetaModel::GetAttributeDef($sExpressionClass, $sAttribute); + if ($oAttDef->IsExternalKey()) + { + $sFoundType = $oAttDef->GetTargetClass(); + } + else + { + $sFoundType = '_scalar_'; + } + } + } + else + { + $sFoundType = '_scalar_'; + } + + // Compare the dimension type and projection type + if ($sFoundType != $sExpectedType) + { + throw new CoreException('Wrong type in projection specification', array('found' => $sFoundType, 'expecting' => $sExpectedType, 'expression' => $sExpression, 'attribute' => $sAttribute, 'projection' => $oProjectionSpec)); + } } } @@ -163,7 +234,8 @@ class URP_ProfileProjection extends DBObject MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass"=>"URP_Profiles", "jointype"=> "", "label"=>"profile", "description"=>"usage profile", "allowed_values"=>null, "sql"=>"profileid", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("profile", array("label"=>"Profile", "description"=>"Profile name", "allowed_values"=>null, "extkey_attcode"=> 'profileid', "target_attcode"=>"name"))); - MetaModel::Init_AddAttribute(new AttributeString("value", array("label"=>"Value expression", "description"=>"OQL expression (using \$user) | constant", "allowed_values"=>null, "sql"=>"value", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("value", array("label"=>"Value expression", "description"=>"OQL expression (using \$user) | constant | ", "allowed_values"=>null, "sql"=>"value", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("attribute", array("label"=>"Attribute", "description"=>"Target attribute code (optional)", "allowed_values"=>null, "sql"=>"attribute", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); //MetaModel::Init_InheritFilters(); MetaModel::Init_AddFilterFromAttribute("dimensionid"); @@ -172,7 +244,25 @@ class URP_ProfileProjection extends DBObject public function ProjectUser(URP_Users $oUser) { - // #@# to be implemented + $sExpr = $this->Get('value'); + if (preg_match('/^\s*([a-zA-Z0-9;]+)\s*$/', $sExpr, $aMatches)) + { + // Constant value(s) + $aRes = explode(';', $aMatches[1]); + } + elseif ($sExpr == '') + { + $aRes = array(''); + } + else + { + $sColumn = $this->Get('attribute'); + // SELECT... + $oValueSetDef = new ValueSetObjects($sExpr, $sColumn); + $aValues = $oValueSetDef->GetValues(array('user' => $oUser), ''); + $aRes = array_values($aValues); + } + return $aRes; } } @@ -201,29 +291,53 @@ class URP_ClassProjection extends DBObject MetaModel::Init_AddAttribute(new AttributeString("class", array("label"=>"Class", "description"=>"Target class", "allowed_values"=>null, "sql"=>"class", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeString("value", array("label"=>"Value expression", "description"=>"OQL expression (using \$this) | constant", "allowed_values"=>null, "sql"=>"value", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("value", array("label"=>"Value expression", "description"=>"OQL expression (using \$this) | constant | ", "allowed_values"=>null, "sql"=>"value", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("attribute", array("label"=>"Attribute", "description"=>"Target attribute code (optional)", "allowed_values"=>null, "sql"=>"attribute", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); //MetaModel::Init_InheritFilters(); MetaModel::Init_AddFilterFromAttribute("dimensionid"); MetaModel::Init_AddFilterFromAttribute("class"); } + + public function ProjectObject($oObject) + { + $sExpr = $this->Get('value'); + if (preg_match('/^\s*([a-zA-Z0-9;]+)\s*$/', $sExpr, $aMatches)) + { + // Constant value(s) + $aRes = explode(';', $aMatches[1]); + } + elseif ($sExpr == '') + { + $aRes = array(''); + } + else + { + $sColumn = $this->Get('attribute'); + // SELECT... + $oValueSetDef = new ValueSetObjects($sExpr, $sColumn); + $aValues = $oValueSetDef->GetValues(array('this' => $oObject), ''); + $aRes = array_values($aValues); + } + return $aRes; + } } -class URP_ClassGrant extends DBObject +class URP_ActionGrant extends DBObject { public static function Init() { $aParams = array ( "category" => "addon/userrights", - "name" => "class_permission", + "name" => "action_permission", "description" => "permissions on classes", "key_type" => "autoincrement", "key_label" => "", "name_attcode" => "", "state_attcode" => "", "reconc_keys" => array(), - "db_table" => "priv_urp_grant_classes", + "db_table" => "priv_urp_grant_actions", "db_key_field" => "id", "db_finalclass_field" => "", ); @@ -308,23 +422,12 @@ class URP_AttributeGrant extends DBObject MetaModel::Init_Params($aParams); //MetaModel::Init_InheritAttributes(); - // Common to all grant classes (could be factorized by class inheritence, but this has to be benchmarked) - MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass"=>"URP_Profiles", "jointype"=> "", "label"=>"Profile", "description"=>"usage profile", "allowed_values"=>null, "sql"=>"profileid", "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeExternalField("profile", array("label"=>"Profile", "description"=>"usage profile", "allowed_values"=>null, "extkey_attcode"=> 'profileid', "target_attcode"=>"name"))); - MetaModel::Init_AddAttribute(new AttributeString("class", array("label"=>"class", "description"=>"class name", "allowed_values"=>null, "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("label"=>"permission", "description"=>"allowed or not allowed?", "allowed_values"=>new ValueSetEnum('yes,no'), "sql"=>"permission", "default_value"=>"yes", "is_null_allowed"=>false, "depends_on"=>array()))); - + MetaModel::Init_AddAttribute(new AttributeExternalKey("actiongrantid", array("targetclass"=>"URP_ActionGrant", "jointype"=> "", "label"=>"Action grant", "description"=>"action grant", "allowed_values"=>null, "sql"=>"actiongrantid", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("attcode", array("label"=>"attribute", "description"=>"attribute code", "allowed_values"=>null, "sql"=>"attcode", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeString("action", array("label"=>"action", "description"=>"operations to perform on the given class", "allowed_values"=>null, "sql"=>"action", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); //MetaModel::Init_InheritFilters(); - // Common to all grant classes (could be factorized by class inheritence, but this has to be benchmarked) - MetaModel::Init_AddFilterFromAttribute("profileid"); - MetaModel::Init_AddFilterFromAttribute("profile"); - MetaModel::Init_AddFilterFromAttribute("class"); - + MetaModel::Init_AddFilterFromAttribute("actiongrantid"); MetaModel::Init_AddFilterFromAttribute("attcode"); - MetaModel::Init_AddFilterFromAttribute("action"); } } @@ -454,18 +557,18 @@ class UserRightsProfile extends UserRightsAddOnAPI } else { - $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_ClassGrant WHERE class = '$sClass' AND action = '$sAction' AND profileid = $iProfileId")); + $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_ActionGrant WHERE class = '$sClass' AND action = '$sAction' AND profileid = $iProfileId")); $bAddCell = ($oSet->Count() < 1); } if ($bAddCell) { // Create a new entry - $oMyClassGrant = MetaModel::NewObject("URP_ClassGrant"); - $oMyClassGrant->Set("profileid", $iProfileId); - $oMyClassGrant->Set("class", $sClass); - $oMyClassGrant->Set("action", $sAction); - $oMyClassGrant->Set("permission", "yes"); - $iId = $oMyClassGrant->DBInsertNoReload(); + $oMyActionGrant = MetaModel::NewObject("URP_ActionGrant"); + $oMyActionGrant->Set("profileid", $iProfileId); + $oMyActionGrant->Set("class", $sClass); + $oMyActionGrant->Set("action", $sAction); + $oMyActionGrant->Set("permission", "yes"); + $iId = $oMyActionGrant->DBInsertNoReload(); } } foreach (MetaModel::EnumStimuli($sClass) as $sStimulusCode => $oStimulus) @@ -482,38 +585,12 @@ class UserRightsProfile extends UserRightsAddOnAPI if ($bAddCell) { // Create a new entry - $oMyClassGrant = MetaModel::NewObject("URP_StimulusGrant"); - $oMyClassGrant->Set("profileid", $iProfileId); - $oMyClassGrant->Set("class", $sClass); - $oMyClassGrant->Set("stimulus", $sStimulusCode); - $oMyClassGrant->Set("permission", "yes"); - $iId = $oMyClassGrant->DBInsertNoReload(); - } - } - foreach (MetaModel::GetAttributesList($sClass) as $sAttCode) - { - if ($bNewProfile) - { - $bAddCell = true; - } - else - { - $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_AttributeGrant WHERE class = '$sClass' AND attcode = '$sAttCode' AND profileid = $iProfileId")); - $bAddCell = ($oSet->Count() < 1); - } - if ($bAddCell) - { - foreach (array('read', 'modify') as $sAction) - { - // Create a new entry - $oMyAttGrant = MetaModel::NewObject("URP_AttributeGrant"); - $oMyAttGrant->Set("profileid", $iProfileId); - $oMyAttGrant->Set("class", $sClass); - $oMyAttGrant->Set("attcode", $sAttCode); - $oMyAttGrant->Set("action", $sAction); - $oMyAttGrant->Set("permission", "yes"); - $iId = $oMyAttGrant->DBInsertNoReload(); - } + $oMyStGrant = MetaModel::NewObject("URP_StimulusGrant"); + $oMyStGrant->Set("profileid", $iProfileId); + $oMyStGrant->Set("class", $sClass); + $oMyStGrant->Set("stimulus", $sStimulusCode); + $oMyStGrant->Set("permission", "yes"); + $iId = $oMyStGrant->DBInsertNoReload(); } } } @@ -577,13 +654,15 @@ class UserRightsProfile extends UserRightsAddOnAPI public function IsActionAllowed($sUserName, $sClass, $iActionCode, dbObjectSet $aInstances) { + // #@# temporary + return true; if (!array_key_exists($iActionCode, self::$m_aActionCodes)) { return UR_ALLOWED_NO; } $sAction = self::$m_aActionCodes[$iActionCode]; - $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_ClassGrant WHERE class = '$sClass' AND action = '$sAction' AND login = '$sUserName'")); + $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_ActionGrant WHERE class = '$sClass' AND action = '$sAction' AND login = '$sUserName'")); if ($oSet->Count() < 1) { return UR_ALLOWED_NO; @@ -605,6 +684,8 @@ class UserRightsProfile extends UserRightsAddOnAPI public function IsActionAllowedOnAttribute($sUserName, $sClass, $sAttCode, $iActionCode, dbObjectSet $aInstances) { + // #@# temporary + return true; if (!array_key_exists($iActionCode, self::$m_aActionCodes)) { return UR_ALLOWED_NO; @@ -633,6 +714,8 @@ class UserRightsProfile extends UserRightsAddOnAPI public function IsStimulusAllowed($sUserName, $sClass, $sStimulusCode, dbObjectSet $aInstances) { + // #@# temporary + return true; $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_StimulusGrant WHERE class = '$sClass' AND stimulus = '$sStimulusCode' AND login = '$sUserName'")); if ($oSet->Count() < 1) { diff --git a/business/itop.business.class.inc.php b/business/itop.business.class.inc.php index f4ca40d13..dcad88f70 100644 --- a/business/itop.business.class.inc.php +++ b/business/itop.business.class.inc.php @@ -260,6 +260,8 @@ class bizPerson extends bizContact MetaModel::Init_AddAttribute(new AttributeString("first_name", array("label"=>"first Name", "description"=>"First name", "allowed_values"=>null, "sql"=>"first_name", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("employe_number", array("label"=>"Employe Number", "description"=>"employe number", "allowed_values"=>null, "sql"=>"employe_number", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("login_id", array("targetclass"=>"URP_Users", "label"=>"Login", "description"=>"Login information", "allowed_values"=>null, "sql"=>"login_id", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_InheritFilters(); MetaModel::Init_AddFilterFromAttribute("first_name"); MetaModel::Init_AddFilterFromAttribute("employe_number"); diff --git a/core/config.class.inc.php b/core/config.class.inc.php index 94bfe8717..c18c716ff 100644 --- a/core/config.class.inc.php +++ b/core/config.class.inc.php @@ -256,7 +256,7 @@ class Config fwrite($hFile, "\t\t// to be continued...\n"); fwrite($hFile, "\t),\n"); fwrite($hFile, "\t'addons' => array (\n"); - fwrite($hFile, "\t\t'user rights' => '../addons/userrights/userrightsmatrix.class.inc.php',\n"); + fwrite($hFile, "\t\t'user rights' => '../addons/userrights/userrightsprofile.class.inc.php',\n"); fwrite($hFile, "\t\t// other modules to come later\n"); fwrite($hFile, "\t)\n"); fwrite($hFile, ");\n"); diff --git a/core/coreexception.class.inc.php b/core/coreexception.class.inc.php index eb5ae8b9d..a31f87d66 100644 --- a/core/coreexception.class.inc.php +++ b/core/coreexception.class.inc.php @@ -23,11 +23,11 @@ class CoreException extends Exception { if (is_array($val)) { - $aPairs[$key] = '('.implode(', ', $val).')'; + $aPairs[] = $key.'=>('.implode(', ', $val).')'; } else { - $aPairs[$key] = $val; + $aPairs[] = $key.'=>'.$val; } } $sValue = '{'.implode('; ', $aPairs).'}'; diff --git a/core/dbobjectsearch.class.php b/core/dbobjectsearch.class.php index 86d6e057a..236a0b721 100644 --- a/core/dbobjectsearch.class.php +++ b/core/dbobjectsearch.class.php @@ -662,7 +662,7 @@ class DBObjectSearch } if (count($aParams) > 0) { - throw new CoreException("Unused parameter(s) for this SibusQL expression: (".implode(', ', array_keys($aParams)).")"); +// throw new CoreException("Unused parameter(s) for this SibusQL expression: (".implode(', ', array_keys($aParams)).")"); } return $sQuery; } @@ -736,6 +736,10 @@ class DBObjectSearch return new FieldExpression($sFltCode, $sClassAlias); } + elseif ($oExpression instanceof VariableOqlExpression) + { + return new VariableExpression($oExpression->GetName()); + } elseif ($oExpression instanceof TrueOqlExpression) { return new TrueExpression; @@ -763,7 +767,7 @@ class DBObjectSearch if (empty($sQuery)) return null; $oOql = new OqlInterpreter($sQuery); - $oOqlQuery = $oOql->ParseQuery(); + $oOqlQuery = $oOql->ParseObjectQuery(); $sClass = $oOqlQuery->GetClass(); $sClassAlias = $oOqlQuery->GetClassAlias(); @@ -860,13 +864,14 @@ class DBObjectSearch if (empty($sQuery)) return null; $sQuery = self::privProcessParams($sQuery, $aParams, $oObject); + if (preg_match('@^\\s*SELECT@', $sQuery)) + { + return self::FromOQL($sQuery, $aParams, $oObject); + } + $iSepPos = strpos($sQuery, ":"); if ($iSepPos === false) { - if (preg_match('@^\\s*SELECT@', $sQuery)) - { - return self::FromOQL($sQuery, $aParams, $oObject); - } // Only the class was specified -> all rows are required $sClass = trim($sQuery); $oFilter = new DBObjectSearch($sClass); diff --git a/core/dbobjectset.class.php b/core/dbobjectset.class.php index d2719c5cd..9e6cf5459 100644 --- a/core/dbobjectset.class.php +++ b/core/dbobjectset.class.php @@ -21,10 +21,11 @@ class DBObjectSet private $m_aId2Row; private $m_iCurrRow; - public function __construct($oFilter, $aOrderBy = array()) + public function __construct($oFilter, $aOrderBy = array(), $aArgs = array()) { $this->m_oFilter = $oFilter; $this->m_aOrderBy = $aOrderBy; + $this->m_aArgs = $aArgs; $this->m_bLoaded = false; $this->m_aData = array(); @@ -109,8 +110,8 @@ class DBObjectSet public function Load() { if ($this->m_bLoaded) return; -// #@# debug - echo "Loading (".$this->m_oFilter->ToSibuSQL().")....
      \n"; - $sSQL = MetaModel::MakeSelectQuery($this->m_oFilter, $this->m_aOrderBy); + + $sSQL = MetaModel::MakeSelectQuery($this->m_oFilter, $this->m_aOrderBy, $this->m_aArgs); $resQuery = CMDBSource::Query($sSQL); if (!$resQuery) return; diff --git a/core/expression.class.inc.php b/core/expression.class.inc.php index 76409cdc1..f2646802e 100644 --- a/core/expression.class.inc.php +++ b/core/expression.class.inc.php @@ -17,7 +17,7 @@ abstract class Expression abstract public function Translate($aTranslationData, $bMatchAll = true); // recursive rendering - abstract public function Render(); + abstract public function Render($aArgs = array()); // recursively builds an array of class => fieldname abstract public function ListRequiredFields(); @@ -119,11 +119,11 @@ class BinaryExpression extends Expression } // recursive rendering - public function Render() + public function Render($aArgs = array()) { $sOperator = $this->GetOperator(); - $sLeft = $this->GetLeftExpr()->Render(); - $sRight = $this->GetRightExpr()->Render(); + $sLeft = $this->GetLeftExpr()->Render($aArgs); + $sRight = $this->GetRightExpr()->Render($aArgs); return "($sLeft $sOperator $sRight)"; } @@ -164,7 +164,7 @@ class UnaryExpression extends Expression } // recursive rendering - public function Render() + public function Render($aArgs = array()) { return CMDBSource::Quote($this->m_value); } @@ -228,7 +228,7 @@ class FieldExpression extends UnaryExpression public function GetName() {return $this->m_sName;} // recursive rendering - public function Render() + public function Render($aArgs = array()) { if (empty($this->m_sParent)) { @@ -270,6 +270,43 @@ class FieldExpression extends UnaryExpression } +class VariableExpression extends UnaryExpression +{ + protected $m_sName; + + public function __construct($sName) + { + parent::__construct($sName); + + $this->m_sName = $sName; + } + + public function IsTrue() + { + // return true if we are certain that it will be true + return false; + } + + public function GetName() {return $this->m_sName;} + + // recursive rendering + public function Render($aArgs = array()) + { + if (array_key_exists($this->m_sName, $aArgs)) + { + return $aArgs[$this->m_sName]; + } + elseif (is_null($aArgs)) + { + return ':'.$this->m_sName; + } + else + { + throw new CoreException('Missing query argument', array('expecting'=>$this->m_sName, 'available'=>$aArgs)); + } + } +} + // Temporary, until we implement functions and expression casting! // ... or until we implement a real full text search based in the MATCH() expression class ListExpression extends Expression @@ -293,12 +330,12 @@ class ListExpression extends Expression } // recursive rendering - public function Render() + public function Render($aArgs = array()) { $aRes = array(); foreach ($this->m_aExpressions as $oExpr) { - $aRes[] = $oExpr->Render(); + $aRes[] = $oExpr->Render($aArgs); } return '('.implode(', ', $aRes).')'; } @@ -353,12 +390,12 @@ class FunctionExpression extends Expression } // recursive rendering - public function Render() + public function Render($aArgs = array()) { $aRes = array(); foreach ($this->m_aArgs as $oExpr) { - $aRes[] = $oExpr->Render(); + $aRes[] = $oExpr->Render($aArgs); } return $this->m_sVerb.'('.implode(', ', $aRes).')'; } @@ -412,9 +449,9 @@ class IntervalExpression extends Expression } // recursive rendering - public function Render() + public function Render($aArgs = array()) { - return 'INTERVAL '.$this->m_oValue->Render().' '.$this->m_sUnit; + return 'INTERVAL '.$this->m_oValue->Render($aArgs).' '.$this->m_sUnit; } public function Translate($aTranslationData, $bMatchAll = true) @@ -449,12 +486,12 @@ class CharConcatExpression extends Expression } // recursive rendering - public function Render() + public function Render($aArgs = array()) { $aRes = array(); foreach ($this->m_aExpressions as $oExpr) { - $sCol = $oExpr->Render(); + $sCol = $oExpr->Render($aArgs); // Concat will be globally NULL if one single argument is null ! $aRes[] = "COALESCE($sCol, '')"; } diff --git a/core/metamodel.class.php b/core/metamodel.class.php index 275ad454a..bb7e92a05 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -394,6 +394,12 @@ abstract class MetaModel return (array_key_exists($sClass, self::$m_aAttribDefs)); } + public static function IsValidObject($oObject) + { + if (!is_object($oObject)) return false; + return (self::IsValidClass(get_class($oObject))); + } + public static function IsReconcKey($sClass, $sAttCode) { return (in_array($sAttCode, self::GetReconcKeys($sClass))); @@ -1161,7 +1167,7 @@ abstract class MetaModel return false; } - public static function MakeSelectQuery(DBObjectSearch $oFilter, $aOrderBy = array()) + public static function MakeSelectQuery(DBObjectSearch $oFilter, $aOrderBy = array(), $aArgs = array()) { $aTranslation = array(); $aClassAliases = array(); @@ -1188,8 +1194,30 @@ abstract class MetaModel } } + // Prepare arguments (translate any object into scalars) + // + $aScalarArgs = array(); + foreach($aArgs as $sArgName => $value) + { + if (self::IsValidObject($value)) + { + $aScalarArgs[$sArgName] = $value->GetKey(); + $aScalarArgs[$sArgName.'->id'] = $value->GetKey(); + + $sClass = get_class($value); + foreach(self::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) + { + $aScalarArgs[$sArgName.'->'.$sAttCode] = $value->Get($sAttCode); + } + } + else + { + $aScalarArgs[$sArgName] = (string) $value; + } + } + //MyHelpers::var_dump_html($oSelect->RenderSelect($aOrderBy)); - return $oSelect->RenderSelect($aOrderBy); + return $oSelect->RenderSelect($aOrderBy, $aScalarArgs); } public static function MakeDeleteQuery(DBObjectSearch $oFilter) diff --git a/core/oql/oql-lexer.php b/core/oql/oql-lexer.php index dede6ca1f..8853786ff 100644 --- a/core/oql/oql-lexer.php +++ b/core/oql/oql-lexer.php @@ -84,6 +84,7 @@ class OQLLexerRaw $rules = array( '/^[ \t\n]+/', '/^SELECT/', + '/^FROM/', '/^AS/', '/^WHERE/', '/^JOIN/', @@ -131,6 +132,7 @@ class OQLLexerRaw '/^[0-9]+|0x[0-9a-fA-F]+/', '/^\"([^\\\\\"]|\\\\\"|\\\\\\\\)*\"|'.chr(94).chr(39).'([^\\\\'.chr(39).']|\\\\'.chr(39).'|\\\\\\\\)*'.chr(39).'/', '/^([_a-zA-Z][_a-zA-Z0-9]*|`[^`]+`)/', + '/^:([_a-zA-Z][_a-zA-Z0-9]*->[_a-zA-Z][_a-zA-Z0-9]*|[_a-zA-Z][_a-zA-Z0-9]*)/', '/^\\./', ); $match = false; @@ -237,239 +239,249 @@ class OQLLexerRaw function yy_r1_2($yy_subpatterns) { - $this->token = OQLParser::AS_ALIAS; + $this->token = OQLParser::FROM; } function yy_r1_3($yy_subpatterns) { - $this->token = OQLParser::WHERE; + $this->token = OQLParser::AS_ALIAS; } function yy_r1_4($yy_subpatterns) { - $this->token = OQLParser::JOIN; + $this->token = OQLParser::WHERE; } function yy_r1_5($yy_subpatterns) { - $this->token = OQLParser::ON; + $this->token = OQLParser::JOIN; } function yy_r1_6($yy_subpatterns) { - $this->token = OQLParser::MATH_DIV; + $this->token = OQLParser::ON; } function yy_r1_7($yy_subpatterns) { - $this->token = OQLParser::MATH_MULT; + $this->token = OQLParser::MATH_DIV; } function yy_r1_8($yy_subpatterns) { - $this->token = OQLParser::MATH_PLUS; + $this->token = OQLParser::MATH_MULT; } function yy_r1_9($yy_subpatterns) { - $this->token = OQLParser::MATH_MINUS; + $this->token = OQLParser::MATH_PLUS; } function yy_r1_10($yy_subpatterns) { - $this->token = OQLParser::LOG_AND; + $this->token = OQLParser::MATH_MINUS; } function yy_r1_11($yy_subpatterns) { - $this->token = OQLParser::LOG_OR; + $this->token = OQLParser::LOG_AND; } function yy_r1_12($yy_subpatterns) { - $this->token = OQLParser::COMA; + $this->token = OQLParser::LOG_OR; } function yy_r1_13($yy_subpatterns) { - $this->token = OQLParser::PAR_OPEN; + $this->token = OQLParser::COMA; } function yy_r1_14($yy_subpatterns) { - $this->token = OQLParser::PAR_CLOSE; + $this->token = OQLParser::PAR_OPEN; } function yy_r1_15($yy_subpatterns) { - $this->token = OQLParser::EQ; + $this->token = OQLParser::PAR_CLOSE; } function yy_r1_16($yy_subpatterns) { - $this->token = OQLParser::NOT_EQ; + $this->token = OQLParser::EQ; } function yy_r1_17($yy_subpatterns) { - $this->token = OQLParser::GT; + $this->token = OQLParser::NOT_EQ; } function yy_r1_18($yy_subpatterns) { - $this->token = OQLParser::LT; + $this->token = OQLParser::GT; } function yy_r1_19($yy_subpatterns) { - $this->token = OQLParser::GE; + $this->token = OQLParser::LT; } function yy_r1_20($yy_subpatterns) { - $this->token = OQLParser::LE; + $this->token = OQLParser::GE; } function yy_r1_21($yy_subpatterns) { - $this->token = OQLParser::LIKE; + $this->token = OQLParser::LE; } function yy_r1_22($yy_subpatterns) { - $this->token = OQLParser::NOT_LIKE; + $this->token = OQLParser::LIKE; } function yy_r1_23($yy_subpatterns) { - $this->token = OQLParser::IN; + $this->token = OQLParser::NOT_LIKE; } function yy_r1_24($yy_subpatterns) { - $this->token = OQLParser::NOT_IN; + $this->token = OQLParser::IN; } function yy_r1_25($yy_subpatterns) { - $this->token = OQLParser::INTERVAL; + $this->token = OQLParser::NOT_IN; } function yy_r1_26($yy_subpatterns) { - $this->token = OQLParser::F_IF; + $this->token = OQLParser::INTERVAL; } function yy_r1_27($yy_subpatterns) { - $this->token = OQLParser::F_ELT; + $this->token = OQLParser::F_IF; } function yy_r1_28($yy_subpatterns) { - $this->token = OQLParser::F_COALESCE; + $this->token = OQLParser::F_ELT; } function yy_r1_29($yy_subpatterns) { - $this->token = OQLParser::F_CONCAT; + $this->token = OQLParser::F_COALESCE; } function yy_r1_30($yy_subpatterns) { - $this->token = OQLParser::F_SUBSTR; + $this->token = OQLParser::F_CONCAT; } function yy_r1_31($yy_subpatterns) { - $this->token = OQLParser::F_TRIM; + $this->token = OQLParser::F_SUBSTR; } function yy_r1_32($yy_subpatterns) { - $this->token = OQLParser::F_DATE; + $this->token = OQLParser::F_TRIM; } function yy_r1_33($yy_subpatterns) { - $this->token = OQLParser::F_DATE_FORMAT; + $this->token = OQLParser::F_DATE; } function yy_r1_34($yy_subpatterns) { - $this->token = OQLParser::F_CURRENT_DATE; + $this->token = OQLParser::F_DATE_FORMAT; } function yy_r1_35($yy_subpatterns) { - $this->token = OQLParser::F_NOW; + $this->token = OQLParser::F_CURRENT_DATE; } function yy_r1_36($yy_subpatterns) { - $this->token = OQLParser::F_TIME; + $this->token = OQLParser::F_NOW; } function yy_r1_37($yy_subpatterns) { - $this->token = OQLParser::F_TO_DAYS; + $this->token = OQLParser::F_TIME; } function yy_r1_38($yy_subpatterns) { - $this->token = OQLParser::F_FROM_DAYS; + $this->token = OQLParser::F_TO_DAYS; } function yy_r1_39($yy_subpatterns) { - $this->token = OQLParser::F_YEAR; + $this->token = OQLParser::F_FROM_DAYS; } function yy_r1_40($yy_subpatterns) { - $this->token = OQLParser::F_MONTH; + $this->token = OQLParser::F_YEAR; } function yy_r1_41($yy_subpatterns) { - $this->token = OQLParser::F_DAY; + $this->token = OQLParser::F_MONTH; } function yy_r1_42($yy_subpatterns) { - $this->token = OQLParser::F_DATE_ADD; + $this->token = OQLParser::F_DAY; } function yy_r1_43($yy_subpatterns) { - $this->token = OQLParser::F_DATE_SUB; + $this->token = OQLParser::F_DATE_ADD; } function yy_r1_44($yy_subpatterns) { - $this->token = OQLParser::F_ROUND; + $this->token = OQLParser::F_DATE_SUB; } function yy_r1_45($yy_subpatterns) { - $this->token = OQLParser::F_FLOOR; + $this->token = OQLParser::F_ROUND; } function yy_r1_46($yy_subpatterns) { - $this->token = OQLParser::NUMVAL; + $this->token = OQLParser::F_FLOOR; } function yy_r1_47($yy_subpatterns) { - $this->token = OQLParser::STRVAL; + $this->token = OQLParser::NUMVAL; } function yy_r1_48($yy_subpatterns) { - $this->token = OQLParser::NAME; + $this->token = OQLParser::STRVAL; } function yy_r1_49($yy_subpatterns) + { + + $this->token = OQLParser::NAME; + } + function yy_r1_50($yy_subpatterns) + { + + $this->token = OQLParser::VARNAME; + } + function yy_r1_51($yy_subpatterns) { $this->token = OQLParser::DOT; diff --git a/core/oql/oql-lexer.plex b/core/oql/oql-lexer.plex index 8196f540a..c8e90dda1 100644 --- a/core/oql/oql-lexer.plex +++ b/core/oql/oql-lexer.plex @@ -55,7 +55,8 @@ class OQLLexerRaw %line $this->line %matchlongest 1 whitespace = /[ \t\n]+/ -select = "SELECT" +select = "SELECT" +from = "FROM" as_alias = "AS" where = "WHERE" join = "JOIN" @@ -103,6 +104,7 @@ f_floor = "FLOOR" numval = /[0-9]+|0x[0-9a-fA-F]+/ strval = /"([^\\"]|\\"|\\\\)*"|'.chr(94).chr(39).'([^\\'.chr(39).']|\\'.chr(39).'|\\\\)*'.chr(39).'/ name = /([_a-zA-Z][_a-zA-Z0-9]*|`[^`]+`)/ +varname = /:([_a-zA-Z][_a-zA-Z0-9]*->[_a-zA-Z][_a-zA-Z0-9]*|[_a-zA-Z][_a-zA-Z0-9]*)/ dot = "." */ @@ -113,6 +115,9 @@ whitespace { select { $this->token = OQLParser::SELECT; } +from { + $this->token = OQLParser::FROM; +} as_alias { $this->token = OQLParser::AS_ALIAS; } @@ -254,6 +259,9 @@ strval { name { $this->token = OQLParser::NAME; } +varname { + $this->token = OQLParser::VARNAME; +} dot { $this->token = OQLParser::DOT; } diff --git a/core/oql/oql-parser.php b/core/oql/oql-parser.php index c3995650c..e66f48274 100644 --- a/core/oql/oql-parser.php +++ b/core/oql/oql-parser.php @@ -125,44 +125,45 @@ class OQLParserRaw#line 102 "oql-parser.php" const F_MONTH = 12; const F_YEAR = 13; const DOT = 14; - const NAME = 15; - const NUMVAL = 16; - const STRVAL = 17; - const NOT_EQ = 18; - const LOG_AND = 19; - const LOG_OR = 20; - const MATH_DIV = 21; - const MATH_MULT = 22; - const MATH_PLUS = 23; - const MATH_MINUS = 24; - const GT = 25; - const LT = 26; - const GE = 27; - const LE = 28; - const LIKE = 29; - const NOT_LIKE = 30; - const IN = 31; - const NOT_IN = 32; - const F_IF = 33; - const F_ELT = 34; - const F_COALESCE = 35; - const F_CONCAT = 36; - const F_SUBSTR = 37; - const F_TRIM = 38; - const F_DATE = 39; - const F_DATE_FORMAT = 40; - const F_CURRENT_DATE = 41; - const F_NOW = 42; - const F_TIME = 43; - const F_TO_DAYS = 44; - const F_FROM_DAYS = 45; - const F_DATE_ADD = 46; - const F_DATE_SUB = 47; - const F_ROUND = 48; - const F_FLOOR = 49; - const YY_NO_ACTION = 205; - const YY_ACCEPT_ACTION = 204; - const YY_ERROR_ACTION = 203; + const VARNAME = 15; + const NAME = 16; + const NUMVAL = 17; + const STRVAL = 18; + const NOT_EQ = 19; + const LOG_AND = 20; + const LOG_OR = 21; + const MATH_DIV = 22; + const MATH_MULT = 23; + const MATH_PLUS = 24; + const MATH_MINUS = 25; + const GT = 26; + const LT = 27; + const GE = 28; + const LE = 29; + const LIKE = 30; + const NOT_LIKE = 31; + const IN = 32; + const NOT_IN = 33; + const F_IF = 34; + const F_ELT = 35; + const F_COALESCE = 36; + const F_CONCAT = 37; + const F_SUBSTR = 38; + const F_TRIM = 39; + const F_DATE = 40; + const F_DATE_FORMAT = 41; + const F_CURRENT_DATE = 42; + const F_NOW = 43; + const F_TIME = 44; + const F_TO_DAYS = 45; + const F_FROM_DAYS = 46; + const F_DATE_ADD = 47; + const F_DATE_SUB = 48; + const F_ROUND = 49; + const F_FLOOR = 50; + const YY_NO_ACTION = 209; + const YY_ACCEPT_ACTION = 208; + const YY_ERROR_ACTION = 207; /* Next are that tables used to determine what action to take based on the ** current state and lookahead token. These tables are used to implement @@ -214,159 +215,161 @@ class OQLParserRaw#line 102 "oql-parser.php" ** shifting non-terminals after a reduce. ** self::$yy_default Default action for each state. */ - const YY_SZ_ACTTAB = 419; + const YY_SZ_ACTTAB = 429; static public $yy_action = array( - /* 0 */ 5, 57, 8, 4, 95, 96, 97, 6, 93, 76, - /* 10 */ 77, 89, 2, 53, 86, 50, 54, 25, 52, 55, - /* 20 */ 51, 46, 47, 49, 56, 70, 94, 110, 109, 108, - /* 30 */ 107, 111, 112, 115, 114, 113, 106, 71, 98, 99, - /* 40 */ 100, 104, 103, 26, 66, 38, 42, 9, 81, 5, - /* 50 */ 62, 44, 82, 95, 96, 97, 3, 93, 76, 77, - /* 60 */ 39, 102, 92, 75, 74, 73, 72, 75, 74, 73, - /* 70 */ 72, 10, 66, 41, 91, 94, 110, 109, 108, 107, - /* 80 */ 111, 112, 115, 114, 113, 106, 71, 98, 99, 100, - /* 90 */ 104, 103, 5, 63, 90, 22, 95, 96, 97, 61, - /* 100 */ 93, 76, 77, 65, 64, 60, 83, 11, 80, 79, - /* 110 */ 91, 33, 91, 22, 21, 18, 16, 12, 94, 110, - /* 120 */ 109, 108, 107, 111, 112, 115, 114, 113, 106, 71, - /* 130 */ 98, 99, 100, 104, 103, 204, 105, 87, 42, 23, - /* 140 */ 43, 24, 66, 88, 30, 28, 84, 45, 36, 6, - /* 150 */ 22, 20, 58, 15, 32, 37, 1, 76, 77, 101, - /* 160 */ 116, 75, 74, 73, 72, 41, 13, 66, 7, 160, - /* 170 */ 67, 93, 24, 42, 35, 78, 173, 173, 88, 34, - /* 180 */ 28, 84, 45, 40, 42, 173, 20, 173, 15, 69, - /* 190 */ 37, 173, 173, 173, 59, 173, 75, 74, 73, 72, - /* 200 */ 41, 42, 173, 173, 173, 173, 88, 34, 28, 84, - /* 210 */ 45, 41, 173, 173, 20, 173, 15, 173, 37, 173, - /* 220 */ 173, 173, 68, 173, 75, 74, 73, 72, 41, 173, - /* 230 */ 173, 173, 85, 42, 173, 173, 173, 173, 88, 30, - /* 240 */ 28, 84, 45, 173, 173, 173, 20, 173, 15, 173, - /* 250 */ 37, 173, 173, 173, 173, 173, 75, 74, 73, 72, - /* 260 */ 41, 42, 173, 173, 173, 173, 88, 17, 28, 84, - /* 270 */ 45, 173, 173, 173, 20, 173, 15, 42, 37, 173, - /* 280 */ 173, 48, 44, 173, 75, 74, 73, 72, 41, 173, - /* 290 */ 173, 173, 173, 42, 173, 173, 173, 173, 88, 27, - /* 300 */ 28, 84, 45, 173, 41, 173, 20, 173, 15, 173, - /* 310 */ 37, 173, 173, 173, 173, 173, 75, 74, 73, 72, - /* 320 */ 41, 42, 173, 173, 173, 173, 88, 173, 28, 84, - /* 330 */ 45, 173, 173, 173, 20, 173, 15, 173, 31, 173, - /* 340 */ 173, 173, 173, 173, 75, 74, 73, 72, 41, 173, - /* 350 */ 173, 173, 173, 42, 173, 173, 173, 173, 88, 173, - /* 360 */ 28, 84, 45, 173, 173, 173, 20, 173, 14, 173, - /* 370 */ 173, 173, 173, 173, 173, 173, 75, 74, 73, 72, - /* 380 */ 41, 42, 173, 173, 173, 173, 88, 173, 28, 84, - /* 390 */ 45, 42, 173, 173, 19, 173, 88, 173, 29, 84, - /* 400 */ 45, 173, 173, 173, 75, 74, 73, 72, 41, 173, - /* 410 */ 173, 173, 173, 173, 75, 74, 73, 72, 41, + /* 0 */ 4, 54, 8, 5, 96, 97, 98, 86, 95, 94, + /* 10 */ 76, 77, 80, 79, 53, 49, 51, 25, 45, 52, + /* 20 */ 55, 57, 46, 47, 50, 56, 70, 111, 110, 109, + /* 30 */ 108, 112, 113, 117, 116, 115, 114, 71, 106, 99, + /* 40 */ 100, 101, 105, 104, 26, 66, 66, 41, 67, 81, + /* 50 */ 4, 48, 43, 82, 96, 97, 98, 3, 95, 94, + /* 60 */ 76, 77, 38, 83, 11, 75, 74, 73, 72, 75, + /* 70 */ 74, 73, 72, 91, 63, 42, 22, 111, 110, 109, + /* 80 */ 108, 112, 113, 117, 116, 115, 114, 71, 106, 99, + /* 90 */ 100, 101, 105, 104, 4, 35, 6, 22, 96, 97, + /* 100 */ 98, 7, 95, 94, 76, 77, 65, 64, 60, 23, + /* 110 */ 6, 12, 13, 58, 41, 41, 91, 18, 61, 43, + /* 120 */ 69, 111, 110, 109, 108, 112, 113, 117, 116, 115, + /* 130 */ 114, 71, 106, 99, 100, 101, 105, 104, 208, 107, + /* 140 */ 87, 41, 42, 42, 39, 66, 88, 34, 27, 84, + /* 150 */ 89, 44, 93, 2, 36, 20, 22, 15, 92, 37, + /* 160 */ 76, 77, 102, 118, 9, 75, 74, 73, 72, 42, + /* 170 */ 94, 91, 66, 16, 24, 163, 21, 41, 103, 32, + /* 180 */ 1, 62, 88, 33, 27, 84, 89, 44, 40, 21, + /* 190 */ 31, 20, 78, 15, 90, 37, 10, 177, 177, 59, + /* 200 */ 177, 75, 74, 73, 72, 42, 41, 177, 177, 177, + /* 210 */ 177, 88, 33, 27, 84, 89, 44, 177, 177, 177, + /* 220 */ 20, 177, 15, 177, 37, 177, 177, 177, 68, 177, + /* 230 */ 75, 74, 73, 72, 42, 177, 177, 85, 41, 177, + /* 240 */ 177, 177, 177, 88, 34, 27, 84, 89, 44, 177, + /* 250 */ 177, 177, 20, 177, 15, 177, 37, 177, 177, 177, + /* 260 */ 177, 177, 75, 74, 73, 72, 42, 41, 177, 177, + /* 270 */ 177, 177, 88, 17, 27, 84, 89, 44, 177, 177, + /* 280 */ 177, 20, 177, 15, 177, 37, 177, 177, 177, 177, + /* 290 */ 177, 75, 74, 73, 72, 42, 177, 177, 177, 41, + /* 300 */ 177, 177, 177, 177, 88, 29, 27, 84, 89, 44, + /* 310 */ 177, 177, 177, 20, 177, 15, 177, 37, 177, 177, + /* 320 */ 177, 177, 177, 75, 74, 73, 72, 42, 41, 177, + /* 330 */ 177, 177, 177, 88, 177, 27, 84, 89, 44, 177, + /* 340 */ 177, 177, 20, 177, 15, 177, 30, 177, 177, 177, + /* 350 */ 177, 177, 75, 74, 73, 72, 42, 177, 177, 177, + /* 360 */ 41, 177, 177, 177, 177, 88, 177, 27, 84, 89, + /* 370 */ 44, 177, 177, 177, 20, 177, 14, 177, 177, 177, + /* 380 */ 177, 177, 177, 177, 75, 74, 73, 72, 42, 41, + /* 390 */ 177, 177, 177, 177, 88, 177, 27, 84, 89, 44, + /* 400 */ 41, 177, 177, 19, 177, 88, 177, 28, 84, 89, + /* 410 */ 44, 177, 177, 75, 74, 73, 72, 42, 177, 177, + /* 420 */ 177, 177, 177, 177, 75, 74, 73, 72, 42, ); static public $yy_lookahead = array( - /* 0 */ 7, 6, 70, 10, 11, 12, 13, 73, 15, 16, - /* 10 */ 17, 8, 9, 18, 56, 83, 84, 54, 23, 24, - /* 20 */ 25, 26, 27, 28, 29, 30, 33, 34, 35, 36, + /* 0 */ 7, 6, 72, 10, 11, 12, 13, 57, 15, 16, + /* 10 */ 17, 18, 32, 33, 19, 85, 86, 55, 55, 24, + /* 20 */ 25, 26, 27, 28, 29, 30, 31, 34, 35, 36, /* 30 */ 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, - /* 40 */ 47, 48, 49, 1, 81, 54, 54, 68, 62, 7, - /* 50 */ 58, 59, 62, 11, 12, 13, 3, 15, 16, 17, - /* 60 */ 74, 82, 8, 77, 78, 79, 80, 77, 78, 79, - /* 70 */ 80, 7, 81, 81, 20, 33, 34, 35, 36, 37, - /* 80 */ 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, - /* 90 */ 48, 49, 7, 55, 66, 57, 11, 12, 13, 56, - /* 100 */ 15, 16, 17, 11, 12, 13, 8, 9, 31, 32, - /* 110 */ 20, 55, 20, 57, 2, 54, 6, 5, 33, 34, - /* 120 */ 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, - /* 130 */ 45, 46, 47, 48, 49, 51, 52, 53, 54, 2, - /* 140 */ 54, 4, 81, 59, 60, 61, 62, 63, 55, 73, - /* 150 */ 57, 67, 76, 69, 14, 71, 7, 16, 17, 21, - /* 160 */ 22, 77, 78, 79, 80, 81, 5, 81, 72, 14, - /* 170 */ 19, 15, 4, 54, 65, 81, 85, 85, 59, 60, - /* 180 */ 61, 62, 63, 64, 54, 85, 67, 85, 69, 59, - /* 190 */ 71, 85, 85, 85, 75, 85, 77, 78, 79, 80, - /* 200 */ 81, 54, 85, 85, 85, 85, 59, 60, 61, 62, - /* 210 */ 63, 81, 85, 85, 67, 85, 69, 85, 71, 85, - /* 220 */ 85, 85, 75, 85, 77, 78, 79, 80, 81, 85, - /* 230 */ 85, 85, 53, 54, 85, 85, 85, 85, 59, 60, - /* 240 */ 61, 62, 63, 85, 85, 85, 67, 85, 69, 85, - /* 250 */ 71, 85, 85, 85, 85, 85, 77, 78, 79, 80, - /* 260 */ 81, 54, 85, 85, 85, 85, 59, 60, 61, 62, - /* 270 */ 63, 85, 85, 85, 67, 85, 69, 54, 71, 85, - /* 280 */ 85, 58, 59, 85, 77, 78, 79, 80, 81, 85, - /* 290 */ 85, 85, 85, 54, 85, 85, 85, 85, 59, 60, - /* 300 */ 61, 62, 63, 85, 81, 85, 67, 85, 69, 85, - /* 310 */ 71, 85, 85, 85, 85, 85, 77, 78, 79, 80, - /* 320 */ 81, 54, 85, 85, 85, 85, 59, 85, 61, 62, - /* 330 */ 63, 85, 85, 85, 67, 85, 69, 85, 71, 85, - /* 340 */ 85, 85, 85, 85, 77, 78, 79, 80, 81, 85, - /* 350 */ 85, 85, 85, 54, 85, 85, 85, 85, 59, 85, - /* 360 */ 61, 62, 63, 85, 85, 85, 67, 85, 69, 85, - /* 370 */ 85, 85, 85, 85, 85, 85, 77, 78, 79, 80, - /* 380 */ 81, 54, 85, 85, 85, 85, 59, 85, 61, 62, - /* 390 */ 63, 54, 85, 85, 67, 85, 59, 85, 61, 62, - /* 400 */ 63, 85, 85, 85, 77, 78, 79, 80, 81, 85, - /* 410 */ 85, 85, 85, 85, 77, 78, 79, 80, 81, + /* 40 */ 47, 48, 49, 50, 1, 83, 83, 55, 20, 63, + /* 50 */ 7, 59, 60, 63, 11, 12, 13, 3, 15, 16, + /* 60 */ 17, 18, 76, 8, 9, 79, 80, 81, 82, 79, + /* 70 */ 80, 81, 82, 21, 56, 83, 58, 34, 35, 36, + /* 80 */ 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, + /* 90 */ 47, 48, 49, 50, 7, 56, 75, 58, 11, 12, + /* 100 */ 13, 74, 15, 16, 17, 18, 11, 12, 13, 2, + /* 110 */ 75, 5, 5, 78, 55, 55, 21, 55, 59, 60, + /* 120 */ 60, 34, 35, 36, 37, 38, 39, 40, 41, 42, + /* 130 */ 43, 44, 45, 46, 47, 48, 49, 50, 52, 53, + /* 140 */ 54, 55, 83, 83, 55, 83, 60, 61, 62, 63, + /* 150 */ 64, 65, 8, 9, 56, 69, 58, 71, 8, 73, + /* 160 */ 17, 18, 22, 23, 70, 79, 80, 81, 82, 83, + /* 170 */ 16, 21, 83, 6, 2, 14, 4, 55, 84, 14, + /* 180 */ 7, 57, 60, 61, 62, 63, 64, 65, 66, 4, + /* 190 */ 67, 69, 83, 71, 68, 73, 7, 87, 87, 77, + /* 200 */ 87, 79, 80, 81, 82, 83, 55, 87, 87, 87, + /* 210 */ 87, 60, 61, 62, 63, 64, 65, 87, 87, 87, + /* 220 */ 69, 87, 71, 87, 73, 87, 87, 87, 77, 87, + /* 230 */ 79, 80, 81, 82, 83, 87, 87, 54, 55, 87, + /* 240 */ 87, 87, 87, 60, 61, 62, 63, 64, 65, 87, + /* 250 */ 87, 87, 69, 87, 71, 87, 73, 87, 87, 87, + /* 260 */ 87, 87, 79, 80, 81, 82, 83, 55, 87, 87, + /* 270 */ 87, 87, 60, 61, 62, 63, 64, 65, 87, 87, + /* 280 */ 87, 69, 87, 71, 87, 73, 87, 87, 87, 87, + /* 290 */ 87, 79, 80, 81, 82, 83, 87, 87, 87, 55, + /* 300 */ 87, 87, 87, 87, 60, 61, 62, 63, 64, 65, + /* 310 */ 87, 87, 87, 69, 87, 71, 87, 73, 87, 87, + /* 320 */ 87, 87, 87, 79, 80, 81, 82, 83, 55, 87, + /* 330 */ 87, 87, 87, 60, 87, 62, 63, 64, 65, 87, + /* 340 */ 87, 87, 69, 87, 71, 87, 73, 87, 87, 87, + /* 350 */ 87, 87, 79, 80, 81, 82, 83, 87, 87, 87, + /* 360 */ 55, 87, 87, 87, 87, 60, 87, 62, 63, 64, + /* 370 */ 65, 87, 87, 87, 69, 87, 71, 87, 87, 87, + /* 380 */ 87, 87, 87, 87, 79, 80, 81, 82, 83, 55, + /* 390 */ 87, 87, 87, 87, 60, 87, 62, 63, 64, 65, + /* 400 */ 55, 87, 87, 69, 87, 60, 87, 62, 63, 64, + /* 410 */ 65, 87, 87, 79, 80, 81, 82, 83, 87, 87, + /* 420 */ 87, 87, 87, 87, 79, 80, 81, 82, 83, ); - const YY_SHIFT_USE_DFLT = -8; + const YY_SHIFT_USE_DFLT = -21; const YY_SHIFT_MAX = 45; static public $yy_shift_ofst = array( - /* 0 */ 42, -7, -7, 85, 85, 85, 85, 85, 85, 85, - /* 10 */ 141, 141, 156, 156, -5, -5, 156, 92, 137, 138, - /* 20 */ 138, 156, 168, 156, 156, 168, 156, 54, 77, 77, - /* 30 */ 90, 151, 156, 53, 90, 64, 53, 151, 112, 98, - /* 40 */ 3, 155, 140, 161, 110, 149, + /* 0 */ 43, -7, -7, 87, 87, 87, 87, 87, 87, 87, + /* 10 */ 143, 143, 154, 154, -5, -5, 154, 95, 172, 140, + /* 20 */ 140, 154, 185, 154, 154, 185, 154, -20, -20, 150, + /* 30 */ 28, 189, 154, 52, 52, 54, 54, 28, 55, 107, + /* 40 */ 144, 165, 161, 167, 173, 106, ); - const YY_REDUCE_USE_DFLT = -69; + const YY_REDUCE_USE_DFLT = -71; const YY_REDUCE_MAX = 37; static public $yy_reduce_ofst = array( - /* 0 */ 84, 119, 147, 179, 207, 239, 267, 299, 327, 337, - /* 10 */ -14, -10, 223, -8, -68, -68, 130, 76, 93, -21, - /* 20 */ -21, 86, 38, -37, -9, 56, 61, -66, 109, 109, - /* 30 */ -66, 96, 94, 43, -66, 28, -42, 96, + /* 0 */ 86, 122, 151, 183, 244, 212, 273, 305, 334, 345, + /* 10 */ -14, -10, 59, -8, -70, -70, 60, 35, 98, 94, + /* 20 */ 94, 89, 18, -37, -38, 39, 62, 123, 123, 21, + /* 30 */ 27, 126, 109, 21, 21, 124, -50, 27, ); static public $yyExpectedTokens = array( - /* 0 */ array(1, 7, 11, 12, 13, 15, 16, 17, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, ), - /* 1 */ array(7, 10, 11, 12, 13, 15, 16, 17, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, ), - /* 2 */ array(7, 10, 11, 12, 13, 15, 16, 17, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, ), - /* 3 */ array(7, 11, 12, 13, 15, 16, 17, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, ), - /* 4 */ array(7, 11, 12, 13, 15, 16, 17, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, ), - /* 5 */ array(7, 11, 12, 13, 15, 16, 17, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, ), - /* 6 */ array(7, 11, 12, 13, 15, 16, 17, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, ), - /* 7 */ array(7, 11, 12, 13, 15, 16, 17, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, ), - /* 8 */ array(7, 11, 12, 13, 15, 16, 17, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, ), - /* 9 */ array(7, 11, 12, 13, 15, 16, 17, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, ), - /* 10 */ array(16, 17, ), - /* 11 */ array(16, 17, ), - /* 12 */ array(15, ), - /* 13 */ array(15, ), - /* 14 */ array(6, 18, 23, 24, 25, 26, 27, 28, 29, 30, ), - /* 15 */ array(6, 18, 23, 24, 25, 26, 27, 28, 29, 30, ), - /* 16 */ array(15, ), - /* 17 */ array(11, 12, 13, 20, ), + /* 0 */ array(1, 7, 11, 12, 13, 15, 16, 17, 18, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, ), + /* 1 */ array(7, 10, 11, 12, 13, 15, 16, 17, 18, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, ), + /* 2 */ array(7, 10, 11, 12, 13, 15, 16, 17, 18, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, ), + /* 3 */ array(7, 11, 12, 13, 15, 16, 17, 18, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, ), + /* 4 */ array(7, 11, 12, 13, 15, 16, 17, 18, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, ), + /* 5 */ array(7, 11, 12, 13, 15, 16, 17, 18, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, ), + /* 6 */ array(7, 11, 12, 13, 15, 16, 17, 18, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, ), + /* 7 */ array(7, 11, 12, 13, 15, 16, 17, 18, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, ), + /* 8 */ array(7, 11, 12, 13, 15, 16, 17, 18, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, ), + /* 9 */ array(7, 11, 12, 13, 15, 16, 17, 18, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, ), + /* 10 */ array(17, 18, ), + /* 11 */ array(17, 18, ), + /* 12 */ array(16, ), + /* 13 */ array(16, ), + /* 14 */ array(6, 19, 24, 25, 26, 27, 28, 29, 30, 31, ), + /* 15 */ array(6, 19, 24, 25, 26, 27, 28, 29, 30, 31, ), + /* 16 */ array(16, ), + /* 17 */ array(11, 12, 13, 21, ), /* 18 */ array(2, 4, ), - /* 19 */ array(21, 22, ), - /* 20 */ array(21, 22, ), - /* 21 */ array(15, ), + /* 19 */ array(22, 23, ), + /* 20 */ array(22, 23, ), + /* 21 */ array(16, ), /* 22 */ array(4, ), - /* 23 */ array(15, ), - /* 24 */ array(15, ), + /* 23 */ array(16, ), + /* 24 */ array(16, ), /* 25 */ array(4, ), - /* 26 */ array(15, ), - /* 27 */ array(8, 20, ), - /* 28 */ array(31, 32, ), - /* 29 */ array(31, 32, ), + /* 26 */ array(16, ), + /* 27 */ array(32, 33, ), + /* 28 */ array(32, 33, ), + /* 29 */ array(8, 21, ), /* 30 */ array(20, ), - /* 31 */ array(19, ), - /* 32 */ array(15, ), - /* 33 */ array(3, ), - /* 34 */ array(20, ), - /* 35 */ array(7, ), + /* 31 */ array(7, ), + /* 32 */ array(16, ), + /* 33 */ array(21, ), + /* 34 */ array(21, ), + /* 35 */ array(3, ), /* 36 */ array(3, ), - /* 37 */ array(19, ), - /* 38 */ array(2, 5, ), - /* 39 */ array(8, 9, ), + /* 37 */ array(20, ), + /* 38 */ array(8, 9, ), + /* 39 */ array(2, 5, ), /* 40 */ array(8, 9, ), /* 41 */ array(14, ), /* 42 */ array(14, ), - /* 43 */ array(5, ), - /* 44 */ array(6, ), - /* 45 */ array(7, ), + /* 43 */ array(6, ), + /* 44 */ array(7, ), + /* 45 */ array(5, ), /* 46 */ array(), /* 47 */ array(), /* 48 */ array(), @@ -438,20 +441,22 @@ static public $yy_action = array( /* 114 */ array(), /* 115 */ array(), /* 116 */ array(), + /* 117 */ array(), + /* 118 */ array(), ); static public $yy_default = array( - /* 0 */ 203, 146, 203, 203, 203, 203, 203, 203, 203, 203, - /* 10 */ 203, 203, 203, 203, 140, 139, 203, 203, 125, 138, - /* 20 */ 137, 203, 124, 203, 203, 125, 203, 203, 135, 136, - /* 30 */ 129, 142, 203, 122, 149, 203, 122, 141, 203, 203, - /* 40 */ 203, 158, 203, 203, 203, 203, 176, 177, 127, 178, - /* 50 */ 165, 175, 173, 168, 166, 174, 179, 167, 150, 147, - /* 60 */ 153, 120, 126, 123, 152, 151, 160, 169, 148, 128, - /* 70 */ 180, 194, 157, 156, 155, 154, 162, 163, 159, 182, - /* 80 */ 181, 144, 145, 143, 130, 121, 119, 118, 131, 132, - /* 90 */ 134, 170, 133, 161, 183, 198, 197, 196, 195, 199, - /* 100 */ 200, 171, 164, 202, 201, 117, 193, 187, 186, 185, - /* 110 */ 184, 188, 189, 192, 191, 190, 172, + /* 0 */ 207, 149, 207, 207, 207, 207, 207, 207, 207, 207, + /* 10 */ 207, 207, 207, 207, 143, 142, 207, 207, 127, 141, + /* 20 */ 140, 207, 126, 207, 207, 127, 207, 138, 139, 207, + /* 30 */ 145, 207, 207, 152, 131, 124, 124, 144, 207, 207, + /* 40 */ 207, 207, 161, 207, 207, 207, 180, 181, 129, 169, + /* 50 */ 182, 170, 177, 172, 171, 178, 183, 179, 153, 150, + /* 60 */ 156, 128, 122, 125, 155, 154, 163, 173, 151, 130, + /* 70 */ 184, 197, 160, 159, 158, 157, 166, 167, 162, 186, + /* 80 */ 185, 147, 148, 146, 132, 123, 121, 120, 133, 134, + /* 90 */ 137, 174, 136, 135, 165, 164, 202, 201, 200, 199, + /* 100 */ 203, 204, 175, 168, 206, 205, 198, 119, 190, 189, + /* 110 */ 188, 187, 191, 192, 196, 195, 194, 193, 176, ); /* The next thing included is series of defines which control ** various aspects of the generated parser. @@ -468,11 +473,11 @@ static public $yy_action = array( ** self::YYERRORSYMBOL is the code number of the error symbol. If not ** defined, then do no error processing. */ - const YYNOCODE = 86; + const YYNOCODE = 88; const YYSTACKDEPTH = 100; - const YYNSTATE = 117; - const YYNRULE = 86; - const YYERRORSYMBOL = 50; + const YYNSTATE = 119; + const YYNRULE = 88; + const YYERRORSYMBOL = 51; const YYERRSYMDT = 'yy0'; const YYFALLBACK = 0; /** The next table maps tokens into fallback tokens. If a construct @@ -557,25 +562,25 @@ static public $yy_action = array( '$', 'SELECT', 'AS_ALIAS', 'WHERE', 'JOIN', 'ON', 'EQ', 'PAR_OPEN', 'PAR_CLOSE', 'COMA', 'INTERVAL', 'F_DAY', - 'F_MONTH', 'F_YEAR', 'DOT', 'NAME', - 'NUMVAL', 'STRVAL', 'NOT_EQ', 'LOG_AND', - 'LOG_OR', 'MATH_DIV', 'MATH_MULT', 'MATH_PLUS', - 'MATH_MINUS', 'GT', 'LT', 'GE', - 'LE', 'LIKE', 'NOT_LIKE', 'IN', - 'NOT_IN', 'F_IF', 'F_ELT', 'F_COALESCE', - 'F_CONCAT', 'F_SUBSTR', 'F_TRIM', 'F_DATE', - 'F_DATE_FORMAT', 'F_CURRENT_DATE', 'F_NOW', 'F_TIME', - 'F_TO_DAYS', 'F_FROM_DAYS', 'F_DATE_ADD', 'F_DATE_SUB', - 'F_ROUND', 'F_FLOOR', 'error', 'result', - 'query', 'condition', 'class_name', 'join_statement', - 'where_statement', 'join_item', 'join_condition', 'field_id', - 'expression_prio4', 'expression_basic', 'scalar', 'func_name', - 'arg_list', 'list_operator', 'list', 'expression_prio1', - 'operator1', 'expression_prio2', 'operator2', 'expression_prio3', - 'operator3', 'operator4', 'scalar_list', 'argument', - 'interval_unit', 'num_scalar', 'str_scalar', 'num_value', - 'str_value', 'name', 'num_operator1', 'num_operator2', - 'str_operator', + 'F_MONTH', 'F_YEAR', 'DOT', 'VARNAME', + 'NAME', 'NUMVAL', 'STRVAL', 'NOT_EQ', + 'LOG_AND', 'LOG_OR', 'MATH_DIV', 'MATH_MULT', + 'MATH_PLUS', 'MATH_MINUS', 'GT', 'LT', + 'GE', 'LE', 'LIKE', 'NOT_LIKE', + 'IN', 'NOT_IN', 'F_IF', 'F_ELT', + 'F_COALESCE', 'F_CONCAT', 'F_SUBSTR', 'F_TRIM', + 'F_DATE', 'F_DATE_FORMAT', 'F_CURRENT_DATE', 'F_NOW', + 'F_TIME', 'F_TO_DAYS', 'F_FROM_DAYS', 'F_DATE_ADD', + 'F_DATE_SUB', 'F_ROUND', 'F_FLOOR', 'error', + 'result', 'query', 'condition', 'class_name', + 'join_statement', 'where_statement', 'join_item', 'join_condition', + 'field_id', 'expression_prio4', 'expression_basic', 'scalar', + 'var_name', 'func_name', 'arg_list', 'list_operator', + 'list', 'expression_prio1', 'operator1', 'expression_prio2', + 'operator2', 'expression_prio3', 'operator3', 'operator4', + 'scalar_list', 'argument', 'interval_unit', 'num_scalar', + 'str_scalar', 'num_value', 'str_value', 'name', + 'num_operator1', 'num_operator2', 'str_operator', ); /** @@ -598,77 +603,79 @@ static public $yy_action = array( /* 12 */ "condition ::= expression_prio4", /* 13 */ "expression_basic ::= scalar", /* 14 */ "expression_basic ::= field_id", - /* 15 */ "expression_basic ::= func_name PAR_OPEN arg_list PAR_CLOSE", - /* 16 */ "expression_basic ::= PAR_OPEN expression_prio4 PAR_CLOSE", - /* 17 */ "expression_basic ::= expression_basic list_operator list", - /* 18 */ "expression_prio1 ::= expression_basic", - /* 19 */ "expression_prio1 ::= expression_prio1 operator1 expression_basic", - /* 20 */ "expression_prio2 ::= expression_prio1", - /* 21 */ "expression_prio2 ::= expression_prio2 operator2 expression_prio1", - /* 22 */ "expression_prio3 ::= expression_prio2", - /* 23 */ "expression_prio3 ::= expression_prio3 operator3 expression_prio2", - /* 24 */ "expression_prio4 ::= expression_prio3", - /* 25 */ "expression_prio4 ::= expression_prio4 operator4 expression_prio3", - /* 26 */ "list ::= PAR_OPEN scalar_list PAR_CLOSE", - /* 27 */ "scalar_list ::= scalar", - /* 28 */ "scalar_list ::= scalar_list COMA scalar", - /* 29 */ "arg_list ::=", - /* 30 */ "arg_list ::= argument", - /* 31 */ "arg_list ::= arg_list COMA argument", - /* 32 */ "argument ::= expression_prio4", - /* 33 */ "argument ::= INTERVAL expression_prio4 interval_unit", - /* 34 */ "interval_unit ::= F_DAY", - /* 35 */ "interval_unit ::= F_MONTH", - /* 36 */ "interval_unit ::= F_YEAR", - /* 37 */ "scalar ::= num_scalar", - /* 38 */ "scalar ::= str_scalar", - /* 39 */ "num_scalar ::= num_value", - /* 40 */ "str_scalar ::= str_value", - /* 41 */ "field_id ::= name", - /* 42 */ "field_id ::= class_name DOT name", - /* 43 */ "class_name ::= name", - /* 44 */ "name ::= NAME", - /* 45 */ "num_value ::= NUMVAL", - /* 46 */ "str_value ::= STRVAL", - /* 47 */ "operator1 ::= num_operator1", - /* 48 */ "operator2 ::= num_operator2", - /* 49 */ "operator2 ::= str_operator", - /* 50 */ "operator2 ::= EQ", - /* 51 */ "operator2 ::= NOT_EQ", - /* 52 */ "operator3 ::= LOG_AND", - /* 53 */ "operator4 ::= LOG_OR", - /* 54 */ "num_operator1 ::= MATH_DIV", - /* 55 */ "num_operator1 ::= MATH_MULT", - /* 56 */ "num_operator2 ::= MATH_PLUS", - /* 57 */ "num_operator2 ::= MATH_MINUS", - /* 58 */ "num_operator2 ::= GT", - /* 59 */ "num_operator2 ::= LT", - /* 60 */ "num_operator2 ::= GE", - /* 61 */ "num_operator2 ::= LE", - /* 62 */ "str_operator ::= LIKE", - /* 63 */ "str_operator ::= NOT_LIKE", - /* 64 */ "list_operator ::= IN", - /* 65 */ "list_operator ::= NOT_IN", - /* 66 */ "func_name ::= F_IF", - /* 67 */ "func_name ::= F_ELT", - /* 68 */ "func_name ::= F_COALESCE", - /* 69 */ "func_name ::= F_CONCAT", - /* 70 */ "func_name ::= F_SUBSTR", - /* 71 */ "func_name ::= F_TRIM", - /* 72 */ "func_name ::= F_DATE", - /* 73 */ "func_name ::= F_DATE_FORMAT", - /* 74 */ "func_name ::= F_CURRENT_DATE", - /* 75 */ "func_name ::= F_NOW", - /* 76 */ "func_name ::= F_TIME", - /* 77 */ "func_name ::= F_TO_DAYS", - /* 78 */ "func_name ::= F_FROM_DAYS", - /* 79 */ "func_name ::= F_YEAR", - /* 80 */ "func_name ::= F_MONTH", - /* 81 */ "func_name ::= F_DAY", - /* 82 */ "func_name ::= F_DATE_ADD", - /* 83 */ "func_name ::= F_DATE_SUB", - /* 84 */ "func_name ::= F_ROUND", - /* 85 */ "func_name ::= F_FLOOR", + /* 15 */ "expression_basic ::= var_name", + /* 16 */ "expression_basic ::= func_name PAR_OPEN arg_list PAR_CLOSE", + /* 17 */ "expression_basic ::= PAR_OPEN expression_prio4 PAR_CLOSE", + /* 18 */ "expression_basic ::= expression_basic list_operator list", + /* 19 */ "expression_prio1 ::= expression_basic", + /* 20 */ "expression_prio1 ::= expression_prio1 operator1 expression_basic", + /* 21 */ "expression_prio2 ::= expression_prio1", + /* 22 */ "expression_prio2 ::= expression_prio2 operator2 expression_prio1", + /* 23 */ "expression_prio3 ::= expression_prio2", + /* 24 */ "expression_prio3 ::= expression_prio3 operator3 expression_prio2", + /* 25 */ "expression_prio4 ::= expression_prio3", + /* 26 */ "expression_prio4 ::= expression_prio4 operator4 expression_prio3", + /* 27 */ "list ::= PAR_OPEN scalar_list PAR_CLOSE", + /* 28 */ "scalar_list ::= scalar", + /* 29 */ "scalar_list ::= scalar_list COMA scalar", + /* 30 */ "arg_list ::=", + /* 31 */ "arg_list ::= argument", + /* 32 */ "arg_list ::= arg_list COMA argument", + /* 33 */ "argument ::= expression_prio4", + /* 34 */ "argument ::= INTERVAL expression_prio4 interval_unit", + /* 35 */ "interval_unit ::= F_DAY", + /* 36 */ "interval_unit ::= F_MONTH", + /* 37 */ "interval_unit ::= F_YEAR", + /* 38 */ "scalar ::= num_scalar", + /* 39 */ "scalar ::= str_scalar", + /* 40 */ "num_scalar ::= num_value", + /* 41 */ "str_scalar ::= str_value", + /* 42 */ "field_id ::= name", + /* 43 */ "field_id ::= class_name DOT name", + /* 44 */ "class_name ::= name", + /* 45 */ "var_name ::= VARNAME", + /* 46 */ "name ::= NAME", + /* 47 */ "num_value ::= NUMVAL", + /* 48 */ "str_value ::= STRVAL", + /* 49 */ "operator1 ::= num_operator1", + /* 50 */ "operator2 ::= num_operator2", + /* 51 */ "operator2 ::= str_operator", + /* 52 */ "operator2 ::= EQ", + /* 53 */ "operator2 ::= NOT_EQ", + /* 54 */ "operator3 ::= LOG_AND", + /* 55 */ "operator4 ::= LOG_OR", + /* 56 */ "num_operator1 ::= MATH_DIV", + /* 57 */ "num_operator1 ::= MATH_MULT", + /* 58 */ "num_operator2 ::= MATH_PLUS", + /* 59 */ "num_operator2 ::= MATH_MINUS", + /* 60 */ "num_operator2 ::= GT", + /* 61 */ "num_operator2 ::= LT", + /* 62 */ "num_operator2 ::= GE", + /* 63 */ "num_operator2 ::= LE", + /* 64 */ "str_operator ::= LIKE", + /* 65 */ "str_operator ::= NOT_LIKE", + /* 66 */ "list_operator ::= IN", + /* 67 */ "list_operator ::= NOT_IN", + /* 68 */ "func_name ::= F_IF", + /* 69 */ "func_name ::= F_ELT", + /* 70 */ "func_name ::= F_COALESCE", + /* 71 */ "func_name ::= F_CONCAT", + /* 72 */ "func_name ::= F_SUBSTR", + /* 73 */ "func_name ::= F_TRIM", + /* 74 */ "func_name ::= F_DATE", + /* 75 */ "func_name ::= F_DATE_FORMAT", + /* 76 */ "func_name ::= F_CURRENT_DATE", + /* 77 */ "func_name ::= F_NOW", + /* 78 */ "func_name ::= F_TIME", + /* 79 */ "func_name ::= F_TO_DAYS", + /* 80 */ "func_name ::= F_FROM_DAYS", + /* 81 */ "func_name ::= F_YEAR", + /* 82 */ "func_name ::= F_MONTH", + /* 83 */ "func_name ::= F_DAY", + /* 84 */ "func_name ::= F_DATE_ADD", + /* 85 */ "func_name ::= F_DATE_SUB", + /* 86 */ "func_name ::= F_ROUND", + /* 87 */ "func_name ::= F_FLOOR", ); /** @@ -1033,92 +1040,94 @@ static public $yy_action = array( * */ static public $yyRuleInfo = array( - array( 'lhs' => 51, 'rhs' => 1 ), - array( 'lhs' => 51, 'rhs' => 1 ), - array( 'lhs' => 52, 'rhs' => 4 ), - array( 'lhs' => 52, 'rhs' => 6 ), + array( 'lhs' => 52, 'rhs' => 1 ), + array( 'lhs' => 52, 'rhs' => 1 ), + array( 'lhs' => 53, 'rhs' => 4 ), + array( 'lhs' => 53, 'rhs' => 6 ), + array( 'lhs' => 57, 'rhs' => 2 ), + array( 'lhs' => 57, 'rhs' => 0 ), array( 'lhs' => 56, 'rhs' => 2 ), + array( 'lhs' => 56, 'rhs' => 1 ), array( 'lhs' => 56, 'rhs' => 0 ), - array( 'lhs' => 55, 'rhs' => 2 ), - array( 'lhs' => 55, 'rhs' => 1 ), - array( 'lhs' => 55, 'rhs' => 0 ), - array( 'lhs' => 57, 'rhs' => 6 ), - array( 'lhs' => 57, 'rhs' => 4 ), - array( 'lhs' => 58, 'rhs' => 3 ), - array( 'lhs' => 53, 'rhs' => 1 ), - array( 'lhs' => 61, 'rhs' => 1 ), - array( 'lhs' => 61, 'rhs' => 1 ), - array( 'lhs' => 61, 'rhs' => 4 ), - array( 'lhs' => 61, 'rhs' => 3 ), - array( 'lhs' => 61, 'rhs' => 3 ), - array( 'lhs' => 67, 'rhs' => 1 ), - array( 'lhs' => 67, 'rhs' => 3 ), + array( 'lhs' => 58, 'rhs' => 6 ), + array( 'lhs' => 58, 'rhs' => 4 ), + array( 'lhs' => 59, 'rhs' => 3 ), + array( 'lhs' => 54, 'rhs' => 1 ), + array( 'lhs' => 62, 'rhs' => 1 ), + array( 'lhs' => 62, 'rhs' => 1 ), + array( 'lhs' => 62, 'rhs' => 1 ), + array( 'lhs' => 62, 'rhs' => 4 ), + array( 'lhs' => 62, 'rhs' => 3 ), + array( 'lhs' => 62, 'rhs' => 3 ), array( 'lhs' => 69, 'rhs' => 1 ), array( 'lhs' => 69, 'rhs' => 3 ), array( 'lhs' => 71, 'rhs' => 1 ), array( 'lhs' => 71, 'rhs' => 3 ), - array( 'lhs' => 60, 'rhs' => 1 ), - array( 'lhs' => 60, 'rhs' => 3 ), + array( 'lhs' => 73, 'rhs' => 1 ), + array( 'lhs' => 73, 'rhs' => 3 ), + array( 'lhs' => 61, 'rhs' => 1 ), + array( 'lhs' => 61, 'rhs' => 3 ), + array( 'lhs' => 68, 'rhs' => 3 ), + array( 'lhs' => 76, 'rhs' => 1 ), + array( 'lhs' => 76, 'rhs' => 3 ), + array( 'lhs' => 66, 'rhs' => 0 ), + array( 'lhs' => 66, 'rhs' => 1 ), array( 'lhs' => 66, 'rhs' => 3 ), - array( 'lhs' => 74, 'rhs' => 1 ), - array( 'lhs' => 74, 'rhs' => 3 ), - array( 'lhs' => 64, 'rhs' => 0 ), - array( 'lhs' => 64, 'rhs' => 1 ), - array( 'lhs' => 64, 'rhs' => 3 ), - array( 'lhs' => 75, 'rhs' => 1 ), - array( 'lhs' => 75, 'rhs' => 3 ), - array( 'lhs' => 76, 'rhs' => 1 ), - array( 'lhs' => 76, 'rhs' => 1 ), - array( 'lhs' => 76, 'rhs' => 1 ), - array( 'lhs' => 62, 'rhs' => 1 ), - array( 'lhs' => 62, 'rhs' => 1 ), array( 'lhs' => 77, 'rhs' => 1 ), + array( 'lhs' => 77, 'rhs' => 3 ), array( 'lhs' => 78, 'rhs' => 1 ), - array( 'lhs' => 59, 'rhs' => 1 ), - array( 'lhs' => 59, 'rhs' => 3 ), - array( 'lhs' => 54, 'rhs' => 1 ), - array( 'lhs' => 81, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 78, 'rhs' => 1 ), + array( 'lhs' => 63, 'rhs' => 1 ), + array( 'lhs' => 63, 'rhs' => 1 ), array( 'lhs' => 79, 'rhs' => 1 ), array( 'lhs' => 80, 'rhs' => 1 ), - array( 'lhs' => 68, 'rhs' => 1 ), - array( 'lhs' => 70, 'rhs' => 1 ), - array( 'lhs' => 70, 'rhs' => 1 ), - array( 'lhs' => 70, 'rhs' => 1 ), + array( 'lhs' => 60, 'rhs' => 1 ), + array( 'lhs' => 60, 'rhs' => 3 ), + array( 'lhs' => 55, 'rhs' => 1 ), + array( 'lhs' => 64, 'rhs' => 1 ), + array( 'lhs' => 83, 'rhs' => 1 ), + array( 'lhs' => 81, 'rhs' => 1 ), + array( 'lhs' => 82, 'rhs' => 1 ), array( 'lhs' => 70, 'rhs' => 1 ), array( 'lhs' => 72, 'rhs' => 1 ), - array( 'lhs' => 73, 'rhs' => 1 ), - array( 'lhs' => 82, 'rhs' => 1 ), - array( 'lhs' => 82, 'rhs' => 1 ), - array( 'lhs' => 83, 'rhs' => 1 ), - array( 'lhs' => 83, 'rhs' => 1 ), - array( 'lhs' => 83, 'rhs' => 1 ), - array( 'lhs' => 83, 'rhs' => 1 ), - array( 'lhs' => 83, 'rhs' => 1 ), - array( 'lhs' => 83, 'rhs' => 1 ), + array( 'lhs' => 72, 'rhs' => 1 ), + array( 'lhs' => 72, 'rhs' => 1 ), + array( 'lhs' => 72, 'rhs' => 1 ), + array( 'lhs' => 74, 'rhs' => 1 ), + array( 'lhs' => 75, 'rhs' => 1 ), array( 'lhs' => 84, 'rhs' => 1 ), array( 'lhs' => 84, 'rhs' => 1 ), + array( 'lhs' => 85, 'rhs' => 1 ), + array( 'lhs' => 85, 'rhs' => 1 ), + array( 'lhs' => 85, 'rhs' => 1 ), + array( 'lhs' => 85, 'rhs' => 1 ), + array( 'lhs' => 85, 'rhs' => 1 ), + array( 'lhs' => 85, 'rhs' => 1 ), + array( 'lhs' => 86, 'rhs' => 1 ), + array( 'lhs' => 86, 'rhs' => 1 ), + array( 'lhs' => 67, 'rhs' => 1 ), + array( 'lhs' => 67, 'rhs' => 1 ), + array( 'lhs' => 65, 'rhs' => 1 ), + array( 'lhs' => 65, 'rhs' => 1 ), + array( 'lhs' => 65, 'rhs' => 1 ), + array( 'lhs' => 65, 'rhs' => 1 ), + array( 'lhs' => 65, 'rhs' => 1 ), + array( 'lhs' => 65, 'rhs' => 1 ), + array( 'lhs' => 65, 'rhs' => 1 ), + array( 'lhs' => 65, 'rhs' => 1 ), + array( 'lhs' => 65, 'rhs' => 1 ), + array( 'lhs' => 65, 'rhs' => 1 ), + array( 'lhs' => 65, 'rhs' => 1 ), + array( 'lhs' => 65, 'rhs' => 1 ), + array( 'lhs' => 65, 'rhs' => 1 ), + array( 'lhs' => 65, 'rhs' => 1 ), + array( 'lhs' => 65, 'rhs' => 1 ), + array( 'lhs' => 65, 'rhs' => 1 ), + array( 'lhs' => 65, 'rhs' => 1 ), + array( 'lhs' => 65, 'rhs' => 1 ), array( 'lhs' => 65, 'rhs' => 1 ), array( 'lhs' => 65, 'rhs' => 1 ), - array( 'lhs' => 63, 'rhs' => 1 ), - array( 'lhs' => 63, 'rhs' => 1 ), - array( 'lhs' => 63, 'rhs' => 1 ), - array( 'lhs' => 63, 'rhs' => 1 ), - array( 'lhs' => 63, 'rhs' => 1 ), - array( 'lhs' => 63, 'rhs' => 1 ), - array( 'lhs' => 63, 'rhs' => 1 ), - array( 'lhs' => 63, 'rhs' => 1 ), - array( 'lhs' => 63, 'rhs' => 1 ), - array( 'lhs' => 63, 'rhs' => 1 ), - array( 'lhs' => 63, 'rhs' => 1 ), - array( 'lhs' => 63, 'rhs' => 1 ), - array( 'lhs' => 63, 'rhs' => 1 ), - array( 'lhs' => 63, 'rhs' => 1 ), - array( 'lhs' => 63, 'rhs' => 1 ), - array( 'lhs' => 63, 'rhs' => 1 ), - array( 'lhs' => 63, 'rhs' => 1 ), - array( 'lhs' => 63, 'rhs' => 1 ), - array( 'lhs' => 63, 'rhs' => 1 ), - array( 'lhs' => 63, 'rhs' => 1 ), ); /** @@ -1143,77 +1152,79 @@ static public $yy_action = array( 12 => 12, 13 => 12, 14 => 12, - 18 => 12, - 20 => 12, - 22 => 12, - 24 => 12, - 32 => 12, - 34 => 12, + 15 => 12, + 19 => 12, + 21 => 12, + 23 => 12, + 25 => 12, + 33 => 12, 35 => 12, 36 => 12, 37 => 12, 38 => 12, - 15 => 15, + 39 => 12, 16 => 16, 17 => 17, - 19 => 17, - 21 => 17, - 23 => 17, - 25 => 17, - 26 => 26, + 18 => 18, + 20 => 18, + 22 => 18, + 24 => 18, + 26 => 18, 27 => 27, - 30 => 27, 28 => 28, 31 => 28, 29 => 29, - 33 => 33, - 39 => 39, - 40 => 39, - 41 => 41, + 32 => 29, + 30 => 30, + 34 => 34, + 40 => 40, + 41 => 40, 42 => 42, 43 => 43, - 66 => 43, - 67 => 43, - 68 => 43, - 69 => 43, - 70 => 43, - 71 => 43, - 72 => 43, - 73 => 43, - 74 => 43, - 75 => 43, - 76 => 43, - 77 => 43, - 78 => 43, - 79 => 43, - 80 => 43, - 81 => 43, - 82 => 43, - 83 => 43, - 84 => 43, - 85 => 43, 44 => 44, + 68 => 44, + 69 => 44, + 70 => 44, + 71 => 44, + 72 => 44, + 73 => 44, + 74 => 44, + 75 => 44, + 76 => 44, + 77 => 44, + 78 => 44, + 79 => 44, + 80 => 44, + 81 => 44, + 82 => 44, + 83 => 44, + 84 => 44, + 85 => 44, + 86 => 44, + 87 => 44, 45 => 45, - 47 => 45, - 48 => 45, - 49 => 45, - 50 => 45, - 51 => 45, - 52 => 45, - 53 => 45, - 54 => 45, - 55 => 45, - 56 => 45, - 57 => 45, - 58 => 45, - 59 => 45, - 60 => 45, - 61 => 45, - 62 => 45, - 63 => 45, - 64 => 45, - 65 => 45, 46 => 46, + 47 => 47, + 49 => 47, + 50 => 47, + 51 => 47, + 52 => 47, + 53 => 47, + 54 => 47, + 55 => 47, + 56 => 47, + 57 => 47, + 58 => 47, + 59 => 47, + 60 => 47, + 61 => 47, + 62 => 47, + 63 => 47, + 64 => 47, + 65 => 47, + 66 => 47, + 67 => 47, + 48 => 48, ); /* Beginning here are the reduction cases. A typical example ** follows: @@ -1223,101 +1234,104 @@ static public $yy_action = array( */ #line 29 "oql-parser.y" function yy_r0(){ $this->my_result = $this->yystack[$this->yyidx + 0]->minor; } -#line 1230 "oql-parser.php" +#line 1241 "oql-parser.php" #line 32 "oql-parser.y" function yy_r2(){ - $this->_retvalue = new OqlQuery($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor); + $this->_retvalue = new OqlObjectQuery($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor); } -#line 1235 "oql-parser.php" +#line 1246 "oql-parser.php" #line 35 "oql-parser.y" function yy_r3(){ - $this->_retvalue = new OqlQuery($this->yystack[$this->yyidx + -4]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor); + $this->_retvalue = new OqlObjectQuery($this->yystack[$this->yyidx + -4]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor); } -#line 1240 "oql-parser.php" -#line 39 "oql-parser.y" +#line 1251 "oql-parser.php" +#line 48 "oql-parser.y" function yy_r4(){ $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; } -#line 1243 "oql-parser.php" -#line 40 "oql-parser.y" +#line 1254 "oql-parser.php" +#line 49 "oql-parser.y" function yy_r5(){ $this->_retvalue = null; } -#line 1246 "oql-parser.php" -#line 42 "oql-parser.y" +#line 1257 "oql-parser.php" +#line 51 "oql-parser.y" function yy_r6(){ // insert the join statement on top of the existing list array_unshift($this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -1]->minor); // and return the updated array $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; } -#line 1254 "oql-parser.php" -#line 48 "oql-parser.y" +#line 1265 "oql-parser.php" +#line 57 "oql-parser.y" function yy_r7(){ $this->_retvalue = Array($this->yystack[$this->yyidx + 0]->minor); } -#line 1259 "oql-parser.php" -#line 54 "oql-parser.y" +#line 1270 "oql-parser.php" +#line 63 "oql-parser.y" function yy_r9(){ // create an array with one single item $this->_retvalue = new OqlJoinSpec($this->yystack[$this->yyidx + -4]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor); } -#line 1265 "oql-parser.php" -#line 59 "oql-parser.y" +#line 1276 "oql-parser.php" +#line 68 "oql-parser.y" function yy_r10(){ // create an array with one single item $this->_retvalue = new OqlJoinSpec($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor); } -#line 1271 "oql-parser.php" -#line 64 "oql-parser.y" +#line 1282 "oql-parser.php" +#line 73 "oql-parser.y" function yy_r11(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, '=', $this->yystack[$this->yyidx + 0]->minor); } -#line 1274 "oql-parser.php" -#line 66 "oql-parser.y" +#line 1285 "oql-parser.php" +#line 75 "oql-parser.y" function yy_r12(){ $this->_retvalue = $this->yystack[$this->yyidx + 0]->minor; } -#line 1277 "oql-parser.php" -#line 70 "oql-parser.y" - function yy_r15(){ $this->_retvalue = new FunctionOqlExpression($this->yystack[$this->yyidx + -3]->minor, $this->yystack[$this->yyidx + -1]->minor); } -#line 1280 "oql-parser.php" -#line 71 "oql-parser.y" - function yy_r16(){ $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor; } -#line 1283 "oql-parser.php" -#line 72 "oql-parser.y" - function yy_r17(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); } -#line 1286 "oql-parser.php" -#line 87 "oql-parser.y" - function yy_r26(){ +#line 1288 "oql-parser.php" +#line 80 "oql-parser.y" + function yy_r16(){ $this->_retvalue = new FunctionOqlExpression($this->yystack[$this->yyidx + -3]->minor, $this->yystack[$this->yyidx + -1]->minor); } +#line 1291 "oql-parser.php" +#line 81 "oql-parser.y" + function yy_r17(){ $this->_retvalue = $this->yystack[$this->yyidx + -1]->minor; } +#line 1294 "oql-parser.php" +#line 82 "oql-parser.y" + function yy_r18(){ $this->_retvalue = new BinaryOqlExpression($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); } +#line 1297 "oql-parser.php" +#line 97 "oql-parser.y" + function yy_r27(){ $this->_retvalue = new ListOqlExpression($this->yystack[$this->yyidx + -1]->minor); } -#line 1291 "oql-parser.php" -#line 90 "oql-parser.y" - function yy_r27(){ +#line 1302 "oql-parser.php" +#line 100 "oql-parser.y" + function yy_r28(){ $this->_retvalue = array($this->yystack[$this->yyidx + 0]->minor); } -#line 1296 "oql-parser.php" -#line 93 "oql-parser.y" - function yy_r28(){ +#line 1307 "oql-parser.php" +#line 103 "oql-parser.y" + function yy_r29(){ array_push($this->yystack[$this->yyidx + -2]->minor, $this->yystack[$this->yyidx + 0]->minor); $this->_retvalue = $this->yystack[$this->yyidx + -2]->minor; } -#line 1302 "oql-parser.php" -#line 98 "oql-parser.y" - function yy_r29(){ +#line 1313 "oql-parser.php" +#line 108 "oql-parser.y" + function yy_r30(){ $this->_retvalue = array(); } -#line 1307 "oql-parser.php" -#line 109 "oql-parser.y" - function yy_r33(){ $this->_retvalue = new IntervalOqlExpression($this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); } -#line 1310 "oql-parser.php" -#line 118 "oql-parser.y" - function yy_r39(){ $this->_retvalue = new ScalarOqlExpression($this->yystack[$this->yyidx + 0]->minor); } -#line 1313 "oql-parser.php" -#line 121 "oql-parser.y" - function yy_r41(){ $this->_retvalue = new FieldOqlExpression($this->yystack[$this->yyidx + 0]->minor); } -#line 1316 "oql-parser.php" -#line 122 "oql-parser.y" - function yy_r42(){ $this->_retvalue = new FieldOqlExpression($this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -2]->minor); } -#line 1319 "oql-parser.php" -#line 123 "oql-parser.y" - function yy_r43(){ $this->_retvalue=$this->yystack[$this->yyidx + 0]->minor; } -#line 1322 "oql-parser.php" -#line 125 "oql-parser.y" - function yy_r44(){ +#line 1318 "oql-parser.php" +#line 119 "oql-parser.y" + function yy_r34(){ $this->_retvalue = new IntervalOqlExpression($this->yystack[$this->yyidx + -1]->minor, $this->yystack[$this->yyidx + 0]->minor); } +#line 1321 "oql-parser.php" +#line 128 "oql-parser.y" + function yy_r40(){ $this->_retvalue = new ScalarOqlExpression($this->yystack[$this->yyidx + 0]->minor); } +#line 1324 "oql-parser.php" +#line 131 "oql-parser.y" + function yy_r42(){ $this->_retvalue = new FieldOqlExpression($this->yystack[$this->yyidx + 0]->minor); } +#line 1327 "oql-parser.php" +#line 132 "oql-parser.y" + function yy_r43(){ $this->_retvalue = new FieldOqlExpression($this->yystack[$this->yyidx + 0]->minor, $this->yystack[$this->yyidx + -2]->minor); } +#line 1330 "oql-parser.php" +#line 133 "oql-parser.y" + function yy_r44(){ $this->_retvalue=$this->yystack[$this->yyidx + 0]->minor; } +#line 1333 "oql-parser.php" +#line 136 "oql-parser.y" + function yy_r45(){ $this->_retvalue = new VariableOqlExpression(substr($this->yystack[$this->yyidx + 0]->minor, 1)); } +#line 1336 "oql-parser.php" +#line 138 "oql-parser.y" + function yy_r46(){ if ($this->yystack[$this->yyidx + 0]->minor[0] == '`') { $name = substr($this->yystack[$this->yyidx + 0]->minor, 1, strlen($this->yystack[$this->yyidx + 0]->minor) - 2); @@ -1328,13 +1342,13 @@ static public $yy_action = array( } $this->_retvalue = new OqlName($name, $this->m_iColPrev); } -#line 1335 "oql-parser.php" -#line 137 "oql-parser.y" - function yy_r45(){$this->_retvalue=$this->yystack[$this->yyidx + 0]->minor; } -#line 1338 "oql-parser.php" -#line 138 "oql-parser.y" - function yy_r46(){$this->_retvalue=stripslashes(substr($this->yystack[$this->yyidx + 0]->minor, 1, strlen($this->yystack[$this->yyidx + 0]->minor) - 2)); } -#line 1341 "oql-parser.php" +#line 1349 "oql-parser.php" +#line 150 "oql-parser.y" + function yy_r47(){$this->_retvalue=$this->yystack[$this->yyidx + 0]->minor; } +#line 1352 "oql-parser.php" +#line 151 "oql-parser.y" + function yy_r48(){$this->_retvalue=stripslashes(substr($this->yystack[$this->yyidx + 0]->minor, 1, strlen($this->yystack[$this->yyidx + 0]->minor) - 2)); } +#line 1355 "oql-parser.php" /** * placeholder for the left hand side in a reduce operation. @@ -1449,7 +1463,7 @@ static public $yy_action = array( #line 25 "oql-parser.y" throw new OQLParserException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCol, $this->tokenName($yymajor), $TOKEN); -#line 1457 "oql-parser.php" +#line 1471 "oql-parser.php" } /** @@ -1601,7 +1615,7 @@ throw new OQLParserException($this->m_sSourceQuery, $this->m_iLine, $this->m_iCo } } while ($yymajor != self::YYNOCODE && $this->yyidx >= 0); } -}#line 186 "oql-parser.y" +}#line 199 "oql-parser.y" class OQLParserException extends OQLException @@ -1666,4 +1680,4 @@ class OQLParser extends OQLParserRaw } } -#line 1676 "oql-parser.php" +#line 1690 "oql-parser.php" diff --git a/core/oql/oql-parser.y b/core/oql/oql-parser.y index 551a82ada..6d0a5c555 100644 --- a/core/oql/oql-parser.y +++ b/core/oql/oql-parser.y @@ -30,12 +30,21 @@ result ::= query(X). { $this->my_result = X; } result ::= condition(X). { $this->my_result = X; } query(A) ::= SELECT class_name(X) join_statement(J) where_statement(W). { - A = new OqlQuery(X, X, W, J); + A = new OqlObjectQuery(X, X, W, J); } query(A) ::= SELECT class_name(X) AS_ALIAS class_name(Y) join_statement(J) where_statement(W). { - A = new OqlQuery(X, Y, W, J); + A = new OqlObjectQuery(X, Y, W, J); } +/* +query(A) ::= SELECT field_id(E) FROM class_name(X) join_statement(J) where_statement(W). { + A = new OqlValueSetQuery(E, X, X, W, J); +} +query(A) ::= SELECT field_id(E) FROM class_name(X) AS_ALIAS class_name(Y) join_statement(J) where_statement(W). { + A = new OqlValueSetQuery(E, X, Y, W, J); +} +*/ + where_statement(A) ::= WHERE condition(C). { A = C;} where_statement(A) ::= . { A = null;} @@ -67,6 +76,7 @@ condition(A) ::= expression_prio4(X). { A = X; } expression_basic(A) ::= scalar(X). { A = X; } expression_basic(A) ::= field_id(X). { A = X; } +expression_basic(A) ::= var_name(X). { A = X; } expression_basic(A) ::= func_name(X) PAR_OPEN arg_list(Y) PAR_CLOSE. { A = new FunctionOqlExpression(X, Y); } expression_basic(A) ::= PAR_OPEN expression_prio4(X) PAR_CLOSE. { A = X; } expression_basic(A) ::= expression_basic(X) list_operator(Y) list(Z). { A = new BinaryOqlExpression(X, Y, Z); } @@ -122,6 +132,9 @@ field_id(A) ::= name(X). { A = new FieldOqlExpression(X); } field_id(A) ::= class_name(X) DOT name(Y). { A = new FieldOqlExpression(Y, X); } class_name(A) ::= name(X). { A=X; } + +var_name(A) ::= VARNAME(X). { A = new VariableOqlExpression(substr(X, 1)); } + name(A) ::= NAME(X). { if (X[0] == '`') { diff --git a/core/oql/oqlinterpreter.class.inc.php b/core/oql/oqlinterpreter.class.inc.php index a464413da..ac72a12d9 100644 --- a/core/oql/oqlinterpreter.class.inc.php +++ b/core/oql/oqlinterpreter.class.inc.php @@ -35,16 +35,28 @@ class OqlInterpreter return $res; } - public function ParseQuery() + public function ParseObjectQuery() { $oRes = $this->Parse(); - if (!$oRes instanceof OqlQuery) + if (!$oRes instanceof OqlObjectQuery) { - throw new OqlException('Expecting an OQL query', $this->m_sQuery, 0, 0, get_class($oRes), array('OqlQuery')); + throw new OqlException('Expecting an OQL query', $this->m_sQuery, 0, 0, get_class($oRes), array('OqlObjectQuery')); } return $oRes; } +/* + public function ParseValueSetQuery() + { + $oRes = $this->Parse(); + if (!$oRes instanceof OqlValueSetQuery) + { + throw new OqlException('Expecting a value set query', $this->m_sQuery, 0, 0, get_class($oRes), array('OqlValueSetQuery')); + } + return $oRes; + } +*/ + public function ParseExpression() { $oRes = $this->Parse(); diff --git a/core/oql/oqlquery.class.inc.php b/core/oql/oqlquery.class.inc.php index ad71950b3..f38cb2e02 100644 --- a/core/oql/oqlquery.class.inc.php +++ b/core/oql/oqlquery.class.inc.php @@ -111,6 +111,10 @@ class FieldOqlExpression extends FieldExpression } } +class VariableOqlExpression extends VariableExpression +{ +} + class ListOqlExpression extends ListExpression { } @@ -122,19 +126,38 @@ class FunctionOqlExpression extends FunctionExpression class IntervalOqlExpression extends IntervalExpression { } -class OqlQuery + +abstract class OqlQuery +{ + protected $m_aJoins; // array of OqlJoinSpec + protected $m_oCondition; // condition tree (expressions) + + public function __construct($oCondition = null, $aJoins = null) + { + $this->m_aJoins = $aJoins; + $this->m_oCondition = $oCondition; + } + + public function GetJoins() + { + return $this->m_aJoins; + } + public function GetCondition() + { + return $this->m_oCondition; + } +} + +class OqlObjectQuery extends OqlQuery { protected $m_oClass; protected $m_oClassAlias; - protected $m_aJoins; // array of OqlJoinSpec - protected $m_oCondition; // condition tree (expressions) public function __construct($oClass, $oClassAlias = '', $oCondition = null, $aJoins = null) { $this->m_oClass = $oClass; $this->m_oClassAlias = $oClassAlias; - $this->m_aJoins = $aJoins; - $this->m_oCondition = $oCondition; + parent::__construct($oCondition, $aJoins); } public function GetClass() @@ -154,15 +177,22 @@ class OqlQuery { return $this->m_oClassAlias; } - - public function GetJoins() - { - return $this->m_aJoins; - } - public function GetCondition() - { - return $this->m_oCondition; - } } + +class OqlValueSetQuery extends OqlObjectQuery +{ + protected $m_oSelectExpr; + + public function __construct($oSelectExpr, $oClass, $oClassAlias = '', $oCondition = null, $aJoins = null) + { + $this->m_oSelectExpr = $oSelectExpr; + parent::__construct($oClass, $oClassAlias, $oCondition, $aJoins); + } + + public function GetSelectExpression() + { + return $this->m_oSelectExpr; + } +} ?> diff --git a/core/sqlquery.class.inc.php b/core/sqlquery.class.inc.php index 8a55d33c8..1661ed4a2 100644 --- a/core/sqlquery.class.inc.php +++ b/core/sqlquery.class.inc.php @@ -28,6 +28,9 @@ class TrueSQLExpression extends TrueExpression class FieldSQLExpression extends FieldExpression { } +class VariableSQLExpression extends VariableExpression +{ +} @@ -212,7 +215,7 @@ class SQLQuery } // Interface, build the SQL query - public function RenderSelect($aOrderBy = array()) + public function RenderSelect($aOrderBy = array(), $aArgs = array()) { // The goal will be to complete the lists as we build the Joins $aFrom = array(); @@ -224,7 +227,7 @@ class SQLQuery $sSelect = self::ClauseSelect($aFields); $sFrom = self::ClauseFrom($aFrom); - $sWhere = self::ClauseWhere($oCondition); + $sWhere = self::ClauseWhere($oCondition, $aArgs); $sOrderBy = self::ClauseOrderBy($aOrderBy); if (!empty($sOrderBy)) { @@ -294,9 +297,9 @@ class SQLQuery return $sSetValues; } - private static function ClauseWhere($oConditionExpr) + private static function ClauseWhere($oConditionExpr, $aArgs = array()) { - return $oConditionExpr->Render(); + return $oConditionExpr->Render($aArgs); } private static function ClauseOrderBy($aOrderBy) diff --git a/core/valuesetdef.class.inc.php b/core/valuesetdef.class.inc.php index c541683cc..2adae3aff 100644 --- a/core/valuesetdef.class.inc.php +++ b/core/valuesetdef.class.inc.php @@ -104,14 +104,15 @@ class ValueSetObjects extends ValueSetDefinition { $this->m_aValues = array(); - $oFilter = DBObjectSearch::FromSibuSQL($this->m_sFilterExpr, $aArgs); + $oFilter = DBObjectSearch::FromSibusQL($this->m_sFilterExpr, $aArgs); if (!$oFilter) return false; - if (empty($this->m_sValueAttCode)) - { - $this->m_sValueAttCode = MetaModel::GetNameAttributeCode($oFilter->GetClass()); - } - $oObjects = new DBObjectSet($oFilter, $this->m_aOrderBy); + if (empty($this->m_sValueAttCode)) + { + $this->m_sValueAttCode = MetaModel::GetNameAttributeCode($oFilter->GetClass()); + } + + $oObjects = new DBObjectSet($oFilter, $this->m_aOrderBy, $aArgs); while ($oObject = $oObjects->Fetch()) { $this->m_aValues[$oObject->GetKey()] = $oObject->GetAsHTML($this->m_sValueAttCode); @@ -235,4 +236,35 @@ class ValueSetEnum extends ValueSetDefinition } } + +/** + * Data model classes + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class ValueSetEnumClasses extends ValueSetEnum +{ + public function __construct($sCategory = '', $sAdditionalValues = '') + { + // First, build it from the series of additional values + parent::__construct($sAdditionalValues); + + // Second: add the list of classes + foreach (MetaModel::GetClasses($sCategory) as $sClass) + { + $this->m_aValues[$sClass] = MetaModel::GetName($sClass); + } + } + + protected function LoadValues($aArgs) + { + return true; + } +} + ?> diff --git a/pages/testlist.inc.php b/pages/testlist.inc.php index db9b448d8..fcdb246cd 100644 --- a/pages/testlist.inc.php +++ b/pages/testlist.inc.php @@ -75,7 +75,7 @@ class TestOQLParser extends TestFunction $oOql = new OqlInterpreter($sQuery); try { - $oTrash = $oOql->ParseQuery(); + $oTrash = $oOql->Parse(); // Not expecting a given format, otherwise use ParseExpression/ParseObjectQuery/ParseValueSetQuery MyHelpers::var_dump_html($oTrash, true); } catch (OQLException $OqlException) @@ -687,7 +687,7 @@ class TestQueriesOnFarm extends TestBizModel try { //$oOql = new OqlInterpreter($sQuery); - //$oTrash = $oOql->ParseQuery(); + //$oTrash = $oOql->ParseObjectQuery(); //MyHelpers::var_dump_html($oTrash, true); $oMyFilter = DBObjectSearch::FromOQL($sQuery); } diff --git a/pages/usermanagement_classproj.php b/pages/usermanagement_classproj.php new file mode 100644 index 000000000..a2e8f7195 --- /dev/null +++ b/pages/usermanagement_classproj.php @@ -0,0 +1,90 @@ +Fetch()) + { + $aDimensions[$oDimension->GetKey()] = $oDimension; + } + + // Load the class projections for a further usage + // + $aClassProj = array(); + $oClassProjSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_ClassProjection")); + while ($oClassProj = $oClassProjSet->Fetch()) + { + $aClassProjs[$oClassProj->Get('class')][$oClassProj->Get('dimensionid')] = $oClassProj; + } + + // Setup display structure + // + $aDisplayConfig = array(); + $aDisplayConfig['class'] = array('label' => 'Class', 'description' => 'Class'); + $aDisplayConfig['object'] = array('label' => 'Object', 'description' => 'Projected object'); + foreach ($aDimensions as $iDimension => $oDimension) + { + $aDisplayConfig['dim'.$oDimension->GetKey()] = array('label' => $oDimension->GetName(), 'description' => $oDimension->Get('description')); + } + + // Load objects + // + $aDisplayData = array(); + $oObjectSet = new DBObjectSet(DBObjectSearch::FromOQL($sScope)); + $sClass = $oObjectSet->GetClass(); + while ($oObject = $oObjectSet->Fetch()) + { + $aObjectProj = array(); + $oObjectProj['class'] = $sClass; + $oObjectProj['object'] = $oObject->GetName(); + foreach ($aDimensions as $iDimension => $oDimension) + { + // #@# to be moved, may be time consuming + $oDimension->CheckProjectionSpec($aClassProjs[$sClass][$iDimension]); + + $aValues = $aClassProjs[$sClass][$iDimension]->ProjectObject($oObject); + $sValues = implode(', ', $aValues); + $oObjectProj['dim'.$oDimension->GetKey()] = htmlentities($sValues); + } + + $aDisplayData[] = $oObjectProj; + } + + $oPage->table($aDisplayConfig, $aDisplayData); + +//$oPage->SetCurrentTab('Attributes'); +//$oPage->p("[All classes]"); +//$oPage->add("
    \n"); + +} + + +require_once('../application/loginwebpage.class.inc.php'); +login_web_page::DoLogin(); // Check user rights and prompt if needed + +// Display the menu on the left +$oContext = new UserContext(); +$oAppContext = new ApplicationContext(); +$iActiveNodeId = utils::ReadParam('menu', -1); +$currentOrganization = utils::ReadParam('org_id', 1); +$sScope = utils::ReadParam('scope', 'SELECT bizDevice'); + +$oPage = new iTopWebPage("iTop user management - class projections", $currentOrganization); +$oPage->no_cache(); + +ComputeProjections($oPage, $sScope); +$oPage->output(); + +?> diff --git a/pages/usermanagement_profileproj.php b/pages/usermanagement_profileproj.php new file mode 100644 index 000000000..a7497d0e0 --- /dev/null +++ b/pages/usermanagement_profileproj.php @@ -0,0 +1,100 @@ +Fetch()) + { + $aProfiles[$oProfile->GetKey()] = $oProfile; + } + + // Load the dimensions for a further usage + // + $aDimensions = array(); + $oDimensionSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_Dimensions")); + while ($oDimension = $oDimensionSet->Fetch()) + { + $aDimensions[$oDimension->GetKey()] = $oDimension; + } + + // Load the profile projections for a further usage + // + $aProPro = array(); + $oProProSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_ProfileProjection")); + while ($oProPro = $oProProSet->Fetch()) + { + $aProPros[$oProPro->Get('profileid')][$oProPro->Get('dimensionid')] = $oProPro; + } + + // Setup display structure + // + $aDisplayConfig = array(); + $aDisplayConfig['user'] = array('label' => 'User', 'description' => 'User concerned by the projection'); + $aDisplayConfig['profile'] = array('label' => 'Profile', 'description' => 'Profile in which the projection is specified'); + foreach ($aDimensions as $iDimension => $oDimension) + { + $aDisplayConfig['dim'.$oDimension->GetKey()] = array('label' => $oDimension->GetName(), 'description' => $oDimension->Get('description')); + } + + // Load users, and create a record per couple user/profile + // + $aDisplayData = array(); + $oUserSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_Users")); + while ($oUser = $oUserSet->Fetch()) + { + $oUserProfileSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_UserProfile WHERE userid = :user->id"), array(), array('user' => $oUser)); + while ($oUserProfile = $oUserProfileSet->Fetch()) + { + $iProfile = $oUserProfile->Get('profileid'); + $oProfile = $aProfiles[$iProfile]; + + $aUserProfileProj = array(); + $aUserProfileProj['user'] = $oUser->GetName(); + $aUserProfileProj['profile'] = $oProfile->GetName(); + foreach ($aDimensions as $iDimension => $oDimension) + { + // #@# to be moved, may be time consuming + $oDimension->CheckProjectionSpec($aProPros[$iProfile][$iDimension]); + + $aValues = $aProPros[$iProfile][$iDimension]->ProjectUser($oUser); + $sValues = implode(', ', $aValues); + $aUserProfileProj['dim'.$oDimension->GetKey()] = htmlentities($sValues); + } + + $aDisplayData[] = $aUserProfileProj; + } + } + + $oPage->table($aDisplayConfig, $aDisplayData); + +//$oPage->SetCurrentTab('Attributes'); +//$oPage->p("[All classes]"); +//$oPage->add("\n"); + +} + + +require_once('../application/loginwebpage.class.inc.php'); +login_web_page::DoLogin(); // Check user rights and prompt if needed + +// Display the menu on the left +$oContext = new UserContext(); +$oAppContext = new ApplicationContext(); +$iActiveNodeId = utils::ReadParam('menu', -1); +$currentOrganization = utils::ReadParam('org_id', 1); + +$oPage = new iTopWebPage("iTop user management - profile projections", $currentOrganization); +$oPage->no_cache(); + +ComputeProjections($oPage); +$oPage->output(); + +?> From 1fdd65939513a633aa505068996793122ed7c00e Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Fri, 24 Jul 2009 15:44:51 +0000 Subject: [PATCH 014/227] - Better display of the archives/change log on each object (Trac ticket #9) SVN:trunk[89] --- application/displayblock.class.inc.php | 56 ++++++++++++++++++----- application/template.class.inc.php | 2 +- application/templates/audit_category.html | 2 +- business/itop.business.class.inc.php | 3 +- business/templates/Circuits.html | 2 +- business/templates/application.html | 2 +- business/templates/change.html | 2 +- business/templates/contract.html | 2 +- business/templates/default.html | 2 +- business/templates/document.html | 2 +- business/templates/group.html | 2 +- business/templates/interface.html | 2 +- business/templates/knownError.html | 2 +- business/templates/location.html | 2 +- business/templates/network.device.html | 7 ++- business/templates/pc.html | 4 +- business/templates/person.html | 2 +- business/templates/server.html | 4 +- business/templates/service.html | 2 +- business/templates/software.html | 2 +- business/templates/team.html | 2 +- business/templates/ticket.html | 2 +- core/cmdbchangeop.class.inc.php | 43 +++++++++++++++++ 23 files changed, 116 insertions(+), 35 deletions(-) diff --git a/application/displayblock.class.inc.php b/application/displayblock.class.inc.php index f41ab6a28..b12486d5e 100644 --- a/application/displayblock.class.inc.php +++ b/application/displayblock.class.inc.php @@ -553,7 +553,13 @@ class HistoryBlock extends DisplayBlock switch($this->m_sStyle) { case 'toggle': - $oLatestChangeOp = $oSet->Fetch(); + // First the latest change that the user is allowed to see + do + { + $oLatestChangeOp = $oSet->Fetch(); + } + while(is_object($oLatestChangeOp) && ($oLatestChangeOp->GetDescription() == '')); + if (is_object($oLatestChangeOp)) { global $oContext; // User Context.. should be statis instead of global... @@ -561,9 +567,36 @@ class HistoryBlock extends DisplayBlock $sDate = $oLatestChangeOp->GetAsHTML('date'); $oChange = $oContext->GetObject('CMDBChange', $oLatestChangeOp->Get('change')); $sUserInfo = $oChange->GetAsHTML('userinfo'); - $oSet->Load(); // Reset the pointer to the beginning of the set: there should be a better way to do this... + $oSet->Rewind(); // Reset the pointer to the beginning of the set $sHtml .= $oPage->GetStartCollapsibleSection("Last modified on $sDate by $sUserInfo."); - $sHtml .= cmdbAbstractObject::GetDisplaySet($oPage, $oSet); + //$sHtml .= cmdbAbstractObject::GetDisplaySet($oPage, $oSet); + $aChanges = array(); + while($oChangeOp = $oSet->Fetch()) + { + $sChangeDescription = $oChangeOp->GetDescription(); + if ($sChangeDescription != '') + { + // The change is visible for the current user + $changeId = $oChangeOp->Get('change'); + $aChanges[$changeId]['date'] = $oChangeOp->Get('date'); + $aChanges[$changeId]['userinfo'] = $oChangeOp->Get('userinfo'); + if (!isset($aChanges[$changeId]['log'])) + { + $aChanges[$changeId]['log'] = array(); + } + $aChanges[$changeId]['log'][] = $sChangeDescription; + } + } + $aAttribs = array('date' => array('label' => 'Date', 'description' => 'Date of the change'), + 'userinfo' => array('label' => 'User', 'description' => 'User who made the change'), + 'log' => array('label' => 'Changes', 'description' => 'Changes made to the object'), + ); + $aValues = array(); + foreach($aChanges as $aChange) + { + $aValues[] = array('date' => $aChange['date'], 'userinfo' => $aChange['userinfo'], 'log' => "
    • ".implode('
    • ', $aChange['log'])."
    "); + } + $sHtml .= $oPage->GetTable($aAttribs, $aValues); $sHtml .= $oPage->GetEndCollapsibleSection(); } break; @@ -605,10 +638,10 @@ class MenuBlock extends DisplayBlock // Just one object in the set, possible actions are "new / clone / modify and delete" if (isset($aExtraParams['linkage'])) { - if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'New...', 'url' => "#"); } - if ($bIsBulkModifyAllowed) { $aActions[] = array ('label' => 'Modify All...', 'url' => "#"); } - if ($bIsBulkDeleteAllowed) { $aActions[] = array ('label' => 'Remove All', 'url' => "#"); } - if ($bIsModifyAllowed | $bIsDeleteAllowed) { $aActions[] = array ('label' => 'Manage Links...', 'url' => "#"); } + if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'Add #...', 'url' => "../pages/$sUIPage?operation=new&class=$sClass&$sContext"); } + if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'Clone #...', 'url' => "../pages/$sUIPage?operation=clone&class=$sClass&id=$id&$sContext"); } + if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'Modify #...', 'url' => "../pages/$sUIPage?operation=modify&class=$sClass&id=$id&$sContext"); } + if ($bIsDeleteAllowed) { $aActions[] = array ('label' => 'Remove #', 'url' => "../pages/$sUIPage?operation=delete&class=$sClass&id=$id&$sContext"); } } else { @@ -652,10 +685,11 @@ class MenuBlock extends DisplayBlock if (isset($aExtraParams['linkage'])) { $bIsDeleteAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_DELETE, $oSet); - if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'New...', 'url' => "#"); } - if ($bIsBulkModifyAllowed) { $aActions[] = array ('label' => 'Modify All...', 'url' => "#"); } - if ($bIsBulkDeleteAllowed) { $aActions[] = array ('label' => 'Remove All', 'url' => "#"); } - if ($bIsModifyAllowed | $bIsDeleteAllowed) { $aActions[] = array ('label' => 'Manage Links...', 'url' => "#"); } + if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'Add #...', 'url' => "../pages/$sUIPage?operation=new&class=$sClass&$sContext"); } + if ($bIsDeleteAllowed) { $aActions[] = array ('label' => 'Remove #', 'url' => "../pages/$sUIPage?operation=delete&class=$sClass&id=$id&$sContext"); } + if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'Modify #...', 'url' => "../pages/$sUIPage?operation=modify&class=$sClass&id=$id&$sContext"); } + if ($bIsBulkModifyAllowed) { $aActions[] = array ('label' => 'Modify All #...', 'url' => "../pages/$sUIPage?operation=new&class=$sClass&$sContext"); } + if ($bIsBulkDeleteAllowed) { $aActions[] = array ('label' => 'Remove All #...', 'url' => "#"); } } else { diff --git a/application/template.class.inc.php b/application/template.class.inc.php index b6bdd8a61..db768c3af 100644 --- a/application/template.class.inc.php +++ b/application/template.class.inc.php @@ -195,7 +195,7 @@ class DisplayTemplate $sTemplate = ' bizNetworkDevice: pkey = $pkey$ diff --git a/application/templates/audit_category.html b/application/templates/audit_category.html index 7a25e8791..a5e1c89c5 100644 --- a/application/templates/audit_category.html +++ b/application/templates/audit_category.html @@ -1,7 +1,7 @@ $class$: pkey = $pkey$ diff --git a/business/itop.business.class.inc.php b/business/itop.business.class.inc.php index dcad88f70..764149007 100644 --- a/business/itop.business.class.inc.php +++ b/business/itop.business.class.inc.php @@ -260,7 +260,7 @@ class bizPerson extends bizContact MetaModel::Init_AddAttribute(new AttributeString("first_name", array("label"=>"first Name", "description"=>"First name", "allowed_values"=>null, "sql"=>"first_name", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("employe_number", array("label"=>"Employe Number", "description"=>"employe number", "allowed_values"=>null, "sql"=>"employe_number", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeExternalKey("login_id", array("targetclass"=>"URP_Users", "label"=>"Login", "description"=>"Login information", "allowed_values"=>null, "sql"=>"login_id", "is_null_allowed"=>true, "depends_on"=>array()))); +// MetaModel::Init_AddAttribute(new AttributeExternalKey("login_id", array("targetclass"=>"URP_Users", "label"=>"Login", "description"=>"Login information", "allowed_values"=>null, "sql"=>"login_id", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_InheritFilters(); MetaModel::Init_AddFilterFromAttribute("first_name"); @@ -1425,6 +1425,7 @@ class bizApplication extends logInfra MetaModel::Init_AddFilterFromAttribute("function"); MetaModel::Init_AddFilterFromAttribute("version"); MetaModel::Init_AddFilterFromAttribute("device_id"); + MetaModel::Init_AddFilterFromAttribute("device_name"); diff --git a/business/templates/Circuits.html b/business/templates/Circuits.html index f04fe3a87..3af174814 100644 --- a/business/templates/Circuits.html +++ b/business/templates/Circuits.html @@ -1,7 +1,7 @@ $class$: pkey = $pkey$ diff --git a/business/templates/application.html b/business/templates/application.html index f934b7b48..a17bdd05b 100644 --- a/business/templates/application.html +++ b/business/templates/application.html @@ -1,7 +1,7 @@ bizApplication: pkey = $pkey$ diff --git a/business/templates/change.html b/business/templates/change.html index f2bcd67d7..700a83983 100644 --- a/business/templates/change.html +++ b/business/templates/change.html @@ -1,7 +1,7 @@ $class$: pkey = $pkey$ diff --git a/business/templates/contract.html b/business/templates/contract.html index 110fcdb0e..e6c5fdcc8 100644 --- a/business/templates/contract.html +++ b/business/templates/contract.html @@ -1,7 +1,7 @@ bizContract: pkey = $pkey$ diff --git a/business/templates/default.html b/business/templates/default.html index 564b009b5..2464cd216 100644 --- a/business/templates/default.html +++ b/business/templates/default.html @@ -1,7 +1,7 @@ $class$: pkey = $pkey$ diff --git a/business/templates/document.html b/business/templates/document.html index cd40cfe78..b957b2457 100644 --- a/business/templates/document.html +++ b/business/templates/document.html @@ -1,7 +1,7 @@ $class$: pkey = $pkey$ diff --git a/business/templates/group.html b/business/templates/group.html index fca5fb154..e8741bf00 100644 --- a/business/templates/group.html +++ b/business/templates/group.html @@ -1,7 +1,7 @@ bizInfraGroup: pkey = $pkey$ diff --git a/business/templates/interface.html b/business/templates/interface.html index ffa8c7495..81bb5fbba 100644 --- a/business/templates/interface.html +++ b/business/templates/interface.html @@ -1,7 +1,7 @@ bizInterface: pkey = $pkey$ diff --git a/business/templates/knownError.html b/business/templates/knownError.html index 75bcc9eaf..87d4184f1 100644 --- a/business/templates/knownError.html +++ b/business/templates/knownError.html @@ -1,7 +1,7 @@ bizKnownError: pkey = $pkey$ diff --git a/business/templates/location.html b/business/templates/location.html index 748ccd739..82f8c4377 100644 --- a/business/templates/location.html +++ b/business/templates/location.html @@ -1,7 +1,7 @@ bizLocation: pkey = $pkey$ diff --git a/business/templates/network.device.html b/business/templates/network.device.html index 01085e2ec..cbad128d7 100644 --- a/business/templates/network.device.html +++ b/business/templates/network.device.html @@ -1,13 +1,13 @@ bizNetworkDevice: pkey = $pkey$ - bizInterface: device_id = $pkey$ + SELECT bizInterface WHERE device_id = $pkey$ lnkContactRealObject: object_id = $pkey$ @@ -21,5 +21,8 @@ lnkDocumentRealObject: object_id = $pkey$ + + + diff --git a/business/templates/pc.html b/business/templates/pc.html index 9323a913d..41dc445c3 100644 --- a/business/templates/pc.html +++ b/business/templates/pc.html @@ -1,7 +1,7 @@ bizPC: pkey = $pkey$ @@ -16,7 +16,7 @@ lnkContactRealObject: object_id = $pkey$ - bizInterface: device_id = $pkey$ + SELECT bizInterface WHERE device_id = $pkey$ bizIncidentTicket: PKEY IS ticket_id IN (lnkInfraTicket: infra_id = $pkey$) diff --git a/business/templates/person.html b/business/templates/person.html index 567f7b543..6bc94916d 100644 --- a/business/templates/person.html +++ b/business/templates/person.html @@ -1,7 +1,7 @@ bizPerson: pkey = $pkey$ diff --git a/business/templates/server.html b/business/templates/server.html index 98185836b..8f72fd878 100644 --- a/business/templates/server.html +++ b/business/templates/server.html @@ -1,7 +1,7 @@ bizServer: pkey = $pkey$ @@ -13,7 +13,7 @@ bizPatch: device_id = $pkey$ - bizInterface: device_id = $pkey$ + SELECT bizInterface WHERE device_id = $pkey$ lnkContactRealObject: object_id = $pkey$ diff --git a/business/templates/service.html b/business/templates/service.html index 7e6443eba..a99953b4a 100644 --- a/business/templates/service.html +++ b/business/templates/service.html @@ -1,7 +1,7 @@ $class$: pkey = $pkey$ diff --git a/business/templates/software.html b/business/templates/software.html index 3f690daad..fb50db6a6 100644 --- a/business/templates/software.html +++ b/business/templates/software.html @@ -1,7 +1,7 @@ bizSoftware: pkey = $pkey$ diff --git a/business/templates/team.html b/business/templates/team.html index 7e1e5cd6e..12537bdd5 100644 --- a/business/templates/team.html +++ b/business/templates/team.html @@ -1,7 +1,7 @@ $class$: pkey = $pkey$ diff --git a/business/templates/ticket.html b/business/templates/ticket.html index 425c92840..efce97e2d 100644 --- a/business/templates/ticket.html +++ b/business/templates/ticket.html @@ -1,7 +1,7 @@ $class$: pkey = $pkey$ diff --git a/core/cmdbchangeop.class.inc.php b/core/cmdbchangeop.class.inc.php index 0ed8776de..05da24db5 100644 --- a/core/cmdbchangeop.class.inc.php +++ b/core/cmdbchangeop.class.inc.php @@ -42,6 +42,16 @@ class CMDBChangeOp extends DBObject MetaModel::Init_AddFilterFromAttribute("objkey"); MetaModel::Init_AddFilterFromAttribute("date"); MetaModel::Init_AddFilterFromAttribute("userinfo"); + + MetaModel::Init_SetZListItems('details', array('change', 'date', 'userinfo')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('change', 'date', 'userinfo')); // Attributes to be displayed for the complete details + } + /** + * Describe (as a text string) the modifications corresponding to this change + */ + public function GetDescription() + { + return ''; } } @@ -80,6 +90,14 @@ class CMDBChangeOpCreate extends CMDBChangeOp MetaModel::Init_InheritFilters(); } + + /** + * Describe (as a text string) the modifications corresponding to this change + */ + public function GetDescription() + { + return 'Object created'; + } } @@ -116,6 +134,13 @@ class CMDBChangeOpDelete extends CMDBChangeOp MetaModel::Init_InheritFilters(); } + /** + * Describe (as a text string) the modifications corresponding to this change + */ + public function GetDescription() + { + return 'Object deleted'; + } } @@ -162,6 +187,24 @@ class CMDBChangeOpSetAttribute extends CMDBChangeOp MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'attcode', 'oldvalue', 'newvalue')); // Attributes to be displayed for the complete details MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'attcode', 'oldvalue', 'newvalue')); // Attributes to be displayed for a list } + + /** + * Describe (as a text string) the modifications corresponding to this change + */ + public function GetDescription() + { + $sResult = ''; + $oEmptySet = new DBObjectSet($this->Get('objclass')); + if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oEmptySet) == UR_ALLOWED_YES) + { + $oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode')); + $sAttName = $oAttDef->GetLabel(); + $sNewValue = $this->Get('newvalue'); + $sOldValue = $this->Get('oldvalue'); + $sResult = "$sAttName set to $sNewValue (previous value: $sOldValue)"; + } + return $sResult; + } } ?> From b5c7cbf5092053fef37af1eee1f447412ad192aa Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Fri, 31 Jul 2009 13:18:44 +0000 Subject: [PATCH 015/227] User management by profile ready for integration with the UI and some caching has been implemented for the building of queries (both from an OQL or from a programmatic query) SVN:trunk[90] --- .../userrights/userrightsmatrix.class.inc.php | 13 + .../userrights/userrightsnull.class.inc.php | 13 +- .../userrightsprofile.class.inc.php | 433 ++++++++++++++---- core/dbobjectsearch.class.php | 42 +- core/dbobjectset.class.php | 18 + core/expression.class.inc.php | 53 ++- core/metamodel.class.php | 102 ++++- core/userrights.class.inc.php | 57 ++- pages/usermanagement_classproj.php | 9 +- pages/usermanagement_profileproj.php | 9 +- pages/usermanagement_userstatus.php | 285 ++++++++++++ 11 files changed, 884 insertions(+), 150 deletions(-) create mode 100644 pages/usermanagement_userstatus.php diff --git a/addons/userrights/userrightsmatrix.class.inc.php b/addons/userrights/userrightsmatrix.class.inc.php index 8bbe15cc8..80a4b97ee 100644 --- a/addons/userrights/userrightsmatrix.class.inc.php +++ b/addons/userrights/userrightsmatrix.class.inc.php @@ -318,6 +318,19 @@ class UserRightsMatrix extends UserRightsAddOnAPI return false; } + public function GetUserId($sUserName) + { + $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixUsers WHERE login = '$sUserName'")); + if ($oSet->Count() < 1) + { + // todo: throw an exception? + return false; + } + + $oLogin = $oSet->Fetch(); + return $oLogin->Get('userid'); + } + public function GetFilter($sUserName, $sClass) { $oNullFilter = new DBObjectSearch($sClass); diff --git a/addons/userrights/userrightsnull.class.inc.php b/addons/userrights/userrightsnull.class.inc.php index c5480f7ea..0d9656fef 100644 --- a/addons/userrights/userrightsnull.class.inc.php +++ b/addons/userrights/userrightsnull.class.inc.php @@ -34,7 +34,12 @@ class UserRightsNull extends UserRightsAddOnAPI public function CheckCredentials($sUserName, $sPassword) { - return true; + return 1; + } + + public function GetUserId($sUserName) + { + return 1; } public function GetFilter($sUserName, $sClass) @@ -43,17 +48,17 @@ class UserRightsNull extends UserRightsAddOnAPI return $oNullFilter; } - public function IsActionAllowed($sUserName, $sClass, $iActionCode, dbObjectSet $aInstances) + public function IsActionAllowed($iUserId, $sClass, $iActionCode, dbObjectSet $aInstances) { return UR_ALLOWED_YES; } - public function IsStimulusAllowed($sUserName, $sClass, $sStimulusCode, dbObjectSet $aInstances) + public function IsStimulusAllowed($iUserId, $sClass, $sStimulusCode, dbObjectSet $aInstances) { return UR_ALLOWED_YES; } - public function IsActionAllowedOnAttribute($sUserName, $sClass, $sAttCode, $iActionCode, dbObjectSet $aInstances) + public function IsActionAllowedOnAttribute($iUserId, $sClass, $sAttCode, $iActionCode, dbObjectSet $aInstances) { return UR_ALLOWED_YES; } diff --git a/addons/userrights/userrightsprofile.class.inc.php b/addons/userrights/userrightsprofile.class.inc.php index 7b72df453..df80a8caf 100644 --- a/addons/userrights/userrightsprofile.class.inc.php +++ b/addons/userrights/userrightsprofile.class.inc.php @@ -245,23 +245,23 @@ class URP_ProfileProjection extends DBObject public function ProjectUser(URP_Users $oUser) { $sExpr = $this->Get('value'); - if (preg_match('/^\s*([a-zA-Z0-9;]+)\s*$/', $sExpr, $aMatches)) - { - // Constant value(s) - $aRes = explode(';', $aMatches[1]); - } - elseif ($sExpr == '') - { - $aRes = array(''); - } - else - { + if (strtolower(substr($sExpr, 0, 6)) == 'select') + { $sColumn = $this->Get('attribute'); // SELECT... $oValueSetDef = new ValueSetObjects($sExpr, $sColumn); $aValues = $oValueSetDef->GetValues(array('user' => $oUser), ''); $aRes = array_values($aValues); } + elseif ($sExpr == '') + { + $aRes = null; + } + else + { + // Constant value(s) + $aRes = explode(';', trim($sExpr)); + } return $aRes; } } @@ -302,23 +302,23 @@ class URP_ClassProjection extends DBObject public function ProjectObject($oObject) { $sExpr = $this->Get('value'); - if (preg_match('/^\s*([a-zA-Z0-9;]+)\s*$/', $sExpr, $aMatches)) - { - // Constant value(s) - $aRes = explode(';', $aMatches[1]); - } - elseif ($sExpr == '') - { - $aRes = array(''); - } - else - { + if (strtolower(substr($sExpr, 0, 6)) == 'select') + { $sColumn = $this->Get('attribute'); // SELECT... $oValueSetDef = new ValueSetObjects($sExpr, $sColumn); - $aValues = $oValueSetDef->GetValues(array('this' => $oObject), ''); + $aValues = $oValueSetDef->GetValues(array('user' => $oObject), ''); $aRes = array_values($aValues); } + elseif ($sExpr == '') + { + $aRes = null; + } + else + { + // Constant value(s) + $aRes = explode(';', trim($sExpr)); + } return $aRes; } } @@ -389,7 +389,7 @@ class URP_StimulusGrant extends DBObject MetaModel::Init_AddAttribute(new AttributeString("class", array("label"=>"class", "description"=>"class name", "allowed_values"=>null, "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("label"=>"permission", "description"=>"allowed or not allowed?", "allowed_values"=>new ValueSetEnum('yes,no'), "sql"=>"permission", "default_value"=>"yes", "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeString("stimulus", array("label"=>"action", "description"=>"operations to perform on the given class", "allowed_values"=>null, "sql"=>"action", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("stimulus", array("label"=>"stimulus", "description"=>"stimulus code", "allowed_values"=>null, "sql"=>"action", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); //MetaModel::Init_InheritFilters(); // Common to all grant classes (could be factorized by class inheritence, but this has to be benchmarked) @@ -498,7 +498,7 @@ class UserRightsProfile extends UserRightsAddOnAPI } else { - $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_ClassProjection WHERE class = '$sClass' AND dimensionid = $iDimensionId")); + $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_ClassProjection WHERE class = :class AND dimensionid = :dimension"), array(), array('class'=>$sClass, 'dimension'=>$iDimensionId)); $bAddCell = ($oSet->Count() < 1); } if ($bAddCell) @@ -524,7 +524,7 @@ class UserRightsProfile extends UserRightsAddOnAPI } else { - $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_ProfileProjection WHERE dimensionid = $iDimensionId AND profileid = $iProfileId")); + $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_ProfileProjection WHERE dimensionid = :dimension AND profileid = :profile"), array(), array('dimension'=>$iDimensionId, 'profile'=>$iProfileId)); $bAddCell = ($oSet->Count() < 1); } if ($bAddCell) @@ -557,7 +557,7 @@ class UserRightsProfile extends UserRightsAddOnAPI } else { - $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_ActionGrant WHERE class = '$sClass' AND action = '$sAction' AND profileid = $iProfileId")); + $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_ActionGrant WHERE class = :class AND action = :action AND profileid = :profile"), array(), array('class'=>$sClass, 'action'=>$sAction, 'profile'=>$iProfileId)); $bAddCell = ($oSet->Count() < 1); } if ($bAddCell) @@ -579,7 +579,7 @@ class UserRightsProfile extends UserRightsAddOnAPI } else { - $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_StimulusGrant WHERE class = '$sClass' AND stimulus = '$sStimulusCode' AND profileid = $iProfileId")); + $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_StimulusGrant WHERE class = :class AND stimulus = :stimulus AND profileid = :profile"), array(), array('class'=>$sClass, 'stimulus'=>$sStimulusCode, 'profile'=>$iProfileId)); $bAddCell = ($oSet->Count() < 1); } if ($bAddCell) @@ -621,119 +621,368 @@ class UserRightsProfile extends UserRightsAddOnAPI } } - public function Init() + { + MetaModel::RegisterPlugin('userrights', 'ACbyProfile', array($this, 'CacheData')); + } + + protected $m_aUsers = array(); // id -> object + protected $m_aDimensions = array(); // id -> object + protected $m_aClassProj = array(); // class,dimensionid -> object + protected $m_aProfiles = array(); // id -> object + protected $m_aUserProfiles = array(); // userid,profileid -> object + protected $m_aProPro = array(); // profileid,dimensionid -> object + + protected $m_aClassActionGrants = array(); // profile, class, action -> permission + protected $m_aObjectActionGrants = array(); // userid, class, id, action -> permission, list of attributes + + public function CacheData() { // Could be loaded in a shared memory (?) + + $oUserSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_Users")); + while ($oUser = $oUserSet->Fetch()) + { + $this->m_aUsers[$oUser->GetKey()] = $oUser; + } + + $oDimensionSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_Dimensions")); + while ($oDimension = $oDimensionSet->Fetch()) + { + $this->m_aDimensions[$oDimension->GetKey()] = $oDimension; + } + + $oClassProjSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_ClassProjection")); + while ($oClassProj = $oClassProjSet->Fetch()) + { + $this->m_aClassProjs[$oClassProj->Get('class')][$oClassProj->Get('dimensionid')] = $oClassProj; + } + + $oProfileSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_Profiles")); + while ($oProfile = $oProfileSet->Fetch()) + { + $this->m_aProfiles[$oProfile->GetKey()] = $oProfile; + } + + $oUserProfileSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_UserProfile")); + while ($oUserProfile = $oUserProfileSet->Fetch()) + { + $this->m_aUserProfiles[$oUserProfile->Get('userid')][$oUserProfile->Get('profileid')] = $oUserProfile; + } + + $oProProSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_ProfileProjection")); + while ($oProPro = $oProProSet->Fetch()) + { + $this->m_aProPros[$oProPro->Get('profileid')][$oProPro->Get('dimensionid')] = $oProPro; + } + +/* + echo "
    \n";
    +		print_r($this->m_aUsers);
    +		print_r($this->m_aDimensions);
    +		print_r($this->m_aClassProjs);
    +		print_r($this->m_aProfiles);
    +		print_r($this->m_aUserProfiles);
    +		print_r($this->m_aProPros);
    +		echo "
    \n"; +exit; +*/ + return true; } public function CheckCredentials($sUserName, $sPassword) { - $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_Users WHERE login = '$sUserName'")); + $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_Users WHERE login = :login"), array(), array('login' => $sUserName)); if ($oSet->Count() < 1) { // todo: throw an exception? return false; } - $oLogin = $oSet->Fetch(); - if ($oLogin->Get('password') == $sPassword) + $oUser = $oSet->Fetch(); + if ($oUser->Get('password') == $sPassword) { - return true; + return $oUser->GetKey(); } // todo: throw an exception? return false; } + public function GetUserId($sUserName) + { + $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_Users WHERE login = :login"), array(), array('login' => $sUserName)); + if ($oSet->Count() < 1) + { + // todo: throw an exception? + return false; + } + + $oUser = $oSet->Fetch(); + return $oUser->GetKey(); + } + public function GetFilter($sUserName, $sClass) { $oNullFilter = new DBObjectSearch($sClass); return $oNullFilter; } - public function IsActionAllowed($sUserName, $sClass, $iActionCode, dbObjectSet $aInstances) + protected function GetClassActionGrant($iProfile, $sClass, $sAction) { - // #@# temporary - return true; - if (!array_key_exists($iActionCode, self::$m_aActionCodes)) - { - return UR_ALLOWED_NO; - } - $sAction = self::$m_aActionCodes[$iActionCode]; + $aTest = @$this->m_aClassActionGrants[$iProfile][$sClass][$sAction]; + if (isset($aTest)) return $aTest; - $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_ActionGrant WHERE class = '$sClass' AND action = '$sAction' AND login = '$sUserName'")); + // Get the permission for this profile/class/action + $oSearch = DBObjectSearch::FromOQL("SELECT URP_ActionGrant WHERE class = :class AND action = :action AND profileid = :profile"); + $oSet = new DBObjectSet($oSearch, array(), array('class'=>$sClass, 'action'=>$sAction, 'profile'=>$iProfile)); if ($oSet->Count() < 1) { - return UR_ALLOWED_NO; + return null; } - $oGrantRecord = $oSet->Fetch(); - switch ($oGrantRecord->Get('permission')) - { - case 'yes': - $iRetCode = UR_ALLOWED_YES; - break; - case 'no': - default: - $iRetCode = UR_ALLOWED_NO; - break; - } - return $iRetCode; + + $this->m_aClassActionGrants[$iProfile][$sClass][$sAction] = $oGrantRecord; + return $oGrantRecord; } - public function IsActionAllowedOnAttribute($sUserName, $sClass, $sAttCode, $iActionCode, dbObjectSet $aInstances) + protected function GetObjectActionGrant($oUser, $sClass, $iActionCode, $oObject) { - // #@# temporary - return true; - if (!array_key_exists($iActionCode, self::$m_aActionCodes)) - { - return UR_ALLOWED_NO; - } + // load and cache permissions for the current user on the given object + // + $aTest = @$this->m_aObjectActionGrants[$oUser->GetKey()][$sClass][$oObject->GetKey][$iActionCode]; + if (is_array($aTest)) return $aTest; + $sAction = self::$m_aActionCodes[$iActionCode]; - $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_AttributeGrant WHERE URP_AttributeGrant.class = '$sClass' AND URP_AttributeGrant.attcode = '$sAttCode' AND URP_AttributeGrant.action = '$sAction' AND URP_AttributeGrant.login = '$sUserName'")); - if ($oSet->Count() < 1) + $iInstancePermission = UR_ALLOWED_NO; + $aAttributes = array(); + foreach($this->GetMatchingProfiles($oUser, $oObject) as $iProfile) + { + $oGrantRecord = $this->GetClassActionGrant($iProfile, $sClass, $sAction); + if (is_null($oGrantRecord)) + { + continue; // loop to the next profile + } + elseif ($oGrantRecord->Get('permission') == 'yes') + { + $iInstancePermission = UR_ALLOWED_YES; + + // merge the list of attributes allowed for this profile + $oSearch = DBObjectSearch::FromOQL("SELECT URP_AttributeGrant WHERE actiongrantid = :actiongrantid"); + $oSet = new DBObjectSet($oSearch, array(), array('actiongrantid' => $oGrantRecord->GetKey())); + $aAttributes = array_merge($aAttributes, $oSet->GetColumnAsArray('attcode', false)); + } + } + + $aRes = array( + 'permission' => $iInstancePermission, + 'attributes' => $aAttributes, + ); + $this->m_aObjectActionGrants[$oUser->GetKey()][$sClass][$oObject->GetKey()][$iActionCode] = $aRes; + return $aRes; + } + + public function IsActionAllowed($iUserId, $sClass, $iActionCode, dbObjectSet $oInstances) + { + $oUser = $this->m_aUsers[$iUserId]; + + $oInstances->Rewind(); + while($oObject = $oInstances->Fetch()) + { + $aObjectPermissions = $this->GetObjectActionGrant($oUser, $sClass, $iActionCode, $oObject); + + $iInstancePermission = $aObjectPermissions['permission']; + if (isset($iGlobalPermission)) + { + if ($iInstancePermission != $iGlobalPermission) + { + $iGlobalPermission = UR_ALLOWED_DEPENDS; + } + } + else + { + $iGlobalPermission = $iInstancePermission; + } + } + if (isset($iGlobalPermission)) + { + return $iGlobalPermission; + } + else { return UR_ALLOWED_NO; } - - $oGrantRecord = $oSet->Fetch(); - switch ($oGrantRecord->Get('permission')) - { - case 'yes': - $iRetCode = UR_ALLOWED_YES; - break; - case 'no': - default: - $iRetCode = UR_ALLOWED_NO; - break; - } - return $iRetCode; } - public function IsStimulusAllowed($sUserName, $sClass, $sStimulusCode, dbObjectSet $aInstances) + public function IsActionAllowedOnAttribute($iUserId, $sClass, $sAttCode, $iActionCode, dbObjectSet $oInstances) { - // #@# temporary - return true; - $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_StimulusGrant WHERE class = '$sClass' AND stimulus = '$sStimulusCode' AND login = '$sUserName'")); - if ($oSet->Count() < 1) + $oUser = $this->m_aUsers[$iUserId]; + + $oInstances->Rewind(); + while($oObject = $oInstances->Fetch()) + { + $aObjectPermissions = $this->GetObjectActionGrant($oUser, $sClass, $iActionCode, $oObject); + + $aAttributes = $aObjectPermissions['attributes']; + if (in_array($sAttCode, $aAttributes)) + { + $iInstancePermission = $aObjectPermissions['permission']; + } + else + { + $iInstancePermission = UR_ALLOWED_NO; + } + + if (isset($iGlobalPermission)) + { + if ($iInstancePermission != $iGlobalPermission) + { + $iGlobalPermission = UR_ALLOWED_DEPENDS; + } + } + else + { + $iGlobalPermission = $iInstancePermission; + } + } + if (isset($iGlobalPermission)) + { + return $iGlobalPermission; + } + else { return UR_ALLOWED_NO; } + } - $oGrantRecord = $oSet->Fetch(); - switch ($oGrantRecord->Get('permission')) + public function IsStimulusAllowed($iUserId, $sClass, $sStimulusCode, dbObjectSet $oInstances) + { + $oUser = $this->m_aUsers[$iUserId]; + + // Note: this code is VERY close to the code of IsActionAllowed() + + $oInstances->Rewind(); + while($oObject = $oInstances->Fetch()) { - case 'yes': - $iRetCode = UR_ALLOWED_YES; - break; - case 'no': - default: - $iRetCode = UR_ALLOWED_NO; - break; + $iInstancePermission = UR_ALLOWED_NO; + foreach($this->GetMatchingProfiles($oUser, $oObject) as $iProfile) + { + // Get the permission for this profile/class/stimulus + $oSearch = DBObjectSearch::FromOQL("SELECT URP_StimulusGrant WHERE class = :class AND stimulus = :stimulus AND profileid = :profile"); + $oSet = new DBObjectSet($oSearch, array(), array('class'=>$sClass, 'stimulus'=>$sStimulusCode, 'profile'=>$iProfile)); + if ($oSet->Count() < 1) + { + return UR_ALLOWED_NO; + } + + $oGrantRecord = $oSet->Fetch(); + $sPermission = $oGrantRecord->Get('permission'); + if ($sPermission == 'yes') + { + $iInstancePermission = UR_ALLOWED_YES; + } + } + if (isset($iGlobalPermission)) + { + if ($iInstancePermission != $iGlobalPermission) + { + $iGlobalPermission = UR_ALLOWED_DEPENDS; + } + } + else + { + $iGlobalPermission = $iInstancePermission; + } } - return $iRetCode; + if (isset($iGlobalPermission)) + { + return $iGlobalPermission; + } + else + { + return UR_ALLOWED_NO; + } + } + + protected function GetMatchingProfilesByDim($oUser, $oObject, $oDimension) + { + // + // List profiles for which the user projection overlaps the object projection in the given dimension + // + $iUser = $oUser->GetKey(); + $sClass = get_class($oObject); + $iPKey = $oObject->GetKey(); + $iDimension = $oDimension->GetKey(); + + $aObjectProjection = $this->m_aClassProjs[$sClass][$iDimension]->ProjectObject($oObject); + + $aRes = array(); + foreach ($this->m_aUserProfiles[$iUser] as $iProfile => $oProfile) + { + if (is_null($aObjectProjection)) + { + $aRes[] = $iProfile; + } + else + { + // user projection to be cached on a given page ! + $aUserProjection = $this->m_aProPros[$iProfile][$iDimension]->ProjectUser($oUser); + + if (is_null($aUserProjection)) + { + $aRes[] = $iProfile; + } + else + { + $aMatchingValues = array_intersect($aObjectProjection, $aUserProjection); + if (count($aMatchingValues) > 0) + { + $aRes[] = $iProfile; + } + } + } + } + return $aRes; + } + + protected $m_aMatchingProfiles = array(); // cache of the matching profiles for a given user/object + + protected function GetMatchingProfiles($oUser, $oObject) + { + $iUser = $oUser->GetKey(); + $sClass = get_class($oObject); + $iObject = $oObject->GetKey(); + // + // List profiles for which the user projection overlaps the object projection in each and every dimension + // Caches the result + // + $aTest = @$this->m_aMatchingProfiles[$iUser][$sClass][$iObject]; + if (is_array($aTest)) + { + return $aTest; + } + + $aProfileRes = array(); + foreach ($this->m_aDimensions as $iDimension => $oDimension) + { + foreach ($this->GetMatchingProfilesByDim($oUser, $oObject, $oDimension) as $iProfile) + { + @$aProfileRes[$iProfile] += 1; + } + } + + $aRes = array(); + $iDimCount = count($this->m_aDimensions); + foreach ($aProfileRes as $iProfile => $iMatches) + { + if ($iMatches == $iDimCount) + { + $aRes[] = $iProfile; + } + } + $this->m_aMatchingProfiles[$iUser][$sClass][$iObject] = $aRes; + return $aRes; } } diff --git a/core/dbobjectsearch.class.php b/core/dbobjectsearch.class.php index 236a0b721..e30d3a5fa 100644 --- a/core/dbobjectsearch.class.php +++ b/core/dbobjectsearch.class.php @@ -48,6 +48,7 @@ class DBObjectSearch private $m_sClassAlias; private $m_aClasses; // queried classes (alias => class name) private $m_oSearchCondition; + private $m_aParams; private $m_aFullText; private $m_aPointingTo; private $m_aReferencedBy; @@ -64,6 +65,7 @@ class DBObjectSearch $this->m_sClassAlias = $sClassAlias; $this->m_aClasses = array($sClassAlias => $sClass); $this->m_oSearchCondition = new TrueExpression; + $this->m_aParams = array(); $this->m_aFullText = array(); $this->m_aPointingTo = array(); $this->m_aReferencedBy = array(); @@ -174,6 +176,8 @@ class DBObjectSearch { $oTranslated = $oFilter->GetCriteria()->Translate($aTranslation, false); $this->AddConditionExpression($oTranslated); + // #@# what about collisions in parameter names ??? + $this->m_aParams = array_merge($this->m_aParams, $oFilter->m_aParams); } public function ResetCondition() @@ -232,22 +236,22 @@ class DBObjectSearch break; case 'Contains': - $oRightExpr = new ScalarExpression("%$value%"); + $this->m_aParams[$sFilterCode] = "%$value%"; $sOperator = 'LIKE'; break; case 'Begins with': - $oRightExpr = new ScalarExpression("$value%"); + $this->m_aParams[$sFilterCode] = "$value%"; $sOperator = 'LIKE'; break; case 'Finishes with': - $oRightExpr = new ScalarExpression("%$value"); + $this->m_aParams[$sFilterCode] = "%$value"; $sOperator = 'LIKE'; break; default: - $oRightExpr = new ScalarExpression($value); + $this->m_aParams[$sFilterCode] = $value; $sOperator = $sOpCode; } @@ -262,6 +266,7 @@ class DBObjectSearch case 'Begins with': case 'Finishes with': default: + $oRightExpr = new VariableExpression($sFilterCode); $oNewCondition = new BinaryExpression($oField, $sOperator, $oRightExpr); } @@ -453,10 +458,14 @@ class DBObjectSearch { return $this->m_aRelatedTo; } + public function GetInternalParams() + { + return $this->m_aParams; + } public function RenderCondition() { - return $this->m_oSearchCondition->Render(); + return $this->m_oSearchCondition->Render($this->m_aParams); } public function serialize() @@ -588,11 +597,13 @@ class DBObjectSearch return $retValue; } - public function ToOQL() + public function ToOQL(&$aParams = null) { + $bRetrofitParams = (!is_null($aParams)); + $sRes = "SELECT ".$this->GetClass().' AS '.$this->GetClassAlias(); $sRes .= $this->ToOQL_Joins(); - $sRes .= " WHERE ".$this->m_oSearchCondition->Render(); + $sRes .= " WHERE ".$this->m_oSearchCondition->Render($aParams, $bRetrofitParams); return $sRes; } @@ -762,10 +773,20 @@ class DBObjectSearch } } - static public function FromOQL($sQuery, array $aParams = array(), $oObject = null) + static protected $m_aOQLQueries = array(); + + static public function FromOQL($sQuery) { if (empty($sQuery)) return null; + // Query caching + $bOQLCacheEnabled = true; + if ($bOQLCacheEnabled && array_key_exists($sQuery, self::$m_aOQLQueries)) + { + // hit! + return clone self::$m_aOQLQueries[$sQuery]; + } + $oOql = new OqlInterpreter($sQuery); $oOqlQuery = $oOql->ParseObjectQuery(); @@ -856,6 +877,11 @@ class DBObjectSearch $oResultFilter->m_oSearchCondition = $oResultFilter->OQLExpressionToCondition($sQuery, $oConditionTree, $aAliases); } + if ($bOQLCacheEnabled) + { + self::$m_aOQLQueries[$sQuery] = clone $oResultFilter; + } + return $oResultFilter; } diff --git a/core/dbobjectset.class.php b/core/dbobjectset.class.php index 9e6cf5459..6384864d8 100644 --- a/core/dbobjectset.class.php +++ b/core/dbobjectset.class.php @@ -92,6 +92,24 @@ class DBObjectSet return $aRet; } + public function GetColumnAsArray($sAttCode, $bWithId = true) + { + $aRet = array(); + $this->Rewind(); + while ($oObject = $this->Fetch()) + { + if ($bWithId) + { + $aRet[$oObject->GetKey()] = $oObject->Get($sAttCode); + } + else + { + $aRet[] = $oObject->Get($sAttCode); + } + } + return $aRet; + } + public function GetFilter() { return $this->m_oFilter; diff --git a/core/expression.class.inc.php b/core/expression.class.inc.php index f2646802e..834ac47b7 100644 --- a/core/expression.class.inc.php +++ b/core/expression.class.inc.php @@ -16,8 +16,8 @@ abstract class Expression // recursive translation of identifiers abstract public function Translate($aTranslationData, $bMatchAll = true); - // recursive rendering - abstract public function Render($aArgs = array()); + // recursive rendering (aArgs used as input by default, or used as output if bRetrofitParams set to True + abstract public function Render(&$aArgs = null, $bRetrofitParams = false); // recursively builds an array of class => fieldname abstract public function ListRequiredFields(); @@ -119,11 +119,11 @@ class BinaryExpression extends Expression } // recursive rendering - public function Render($aArgs = array()) + public function Render(&$aArgs = null, $bRetrofitParams = false) { $sOperator = $this->GetOperator(); - $sLeft = $this->GetLeftExpr()->Render($aArgs); - $sRight = $this->GetRightExpr()->Render($aArgs); + $sLeft = $this->GetLeftExpr()->Render($aArgs, $bRetrofitParams); + $sRight = $this->GetRightExpr()->Render($aArgs, $bRetrofitParams); return "($sLeft $sOperator $sRight)"; } @@ -164,9 +164,18 @@ class UnaryExpression extends Expression } // recursive rendering - public function Render($aArgs = array()) + public function Render(&$aArgs = null, $bRetrofitParams = false) { - return CMDBSource::Quote($this->m_value); + if ($bRetrofitParams) + { + $iParamIndex = count($aArgs) + 1; // 1-based indexation + $aArgs['param'.$iParamIndex] = $this->m_value; + return ':param'.$iParamIndex; + } + else + { + return CMDBSource::Quote($this->m_value); + } } public function Translate($aTranslationData, $bMatchAll = true) @@ -228,7 +237,7 @@ class FieldExpression extends UnaryExpression public function GetName() {return $this->m_sName;} // recursive rendering - public function Render($aArgs = array()) + public function Render(&$aArgs = null, $bRetrofitParams = false) { if (empty($this->m_sParent)) { @@ -290,16 +299,16 @@ class VariableExpression extends UnaryExpression public function GetName() {return $this->m_sName;} // recursive rendering - public function Render($aArgs = array()) + public function Render(&$aArgs = null, $bRetrofitParams = false) { - if (array_key_exists($this->m_sName, $aArgs)) - { - return $aArgs[$this->m_sName]; - } - elseif (is_null($aArgs)) + if (is_null($aArgs) || $bRetrofitParams) { return ':'.$this->m_sName; } + elseif (array_key_exists($this->m_sName, $aArgs)) + { + return CMDBSource::Quote($aArgs[$this->m_sName]); + } else { throw new CoreException('Missing query argument', array('expecting'=>$this->m_sName, 'available'=>$aArgs)); @@ -330,12 +339,12 @@ class ListExpression extends Expression } // recursive rendering - public function Render($aArgs = array()) + public function Render(&$aArgs = null, $bRetrofitParams = false) { $aRes = array(); foreach ($this->m_aExpressions as $oExpr) { - $aRes[] = $oExpr->Render($aArgs); + $aRes[] = $oExpr->Render($aArgs, $bRetrofitParams); } return '('.implode(', ', $aRes).')'; } @@ -390,12 +399,12 @@ class FunctionExpression extends Expression } // recursive rendering - public function Render($aArgs = array()) + public function Render(&$aArgs = null, $bRetrofitParams = false) { $aRes = array(); foreach ($this->m_aArgs as $oExpr) { - $aRes[] = $oExpr->Render($aArgs); + $aRes[] = $oExpr->Render($aArgs, $bRetrofitParams); } return $this->m_sVerb.'('.implode(', ', $aRes).')'; } @@ -449,9 +458,9 @@ class IntervalExpression extends Expression } // recursive rendering - public function Render($aArgs = array()) + public function Render(&$aArgs = null, $bRetrofitParams = false) { - return 'INTERVAL '.$this->m_oValue->Render($aArgs).' '.$this->m_sUnit; + return 'INTERVAL '.$this->m_oValue->Render($aArgs, $bRetrofitParams).' '.$this->m_sUnit; } public function Translate($aTranslationData, $bMatchAll = true) @@ -486,12 +495,12 @@ class CharConcatExpression extends Expression } // recursive rendering - public function Render($aArgs = array()) + public function Render(&$aArgs = null, $bRetrofitParams = false) { $aRes = array(); foreach ($this->m_aExpressions as $oExpr) { - $sCol = $oExpr->Render($aArgs); + $sCol = $oExpr->Render($aArgs, $bRetrofitParams); // Concat will be globally NULL if one single argument is null ! $aRes[] = "COALESCE($sCol, '')"; } diff --git a/core/metamodel.class.php b/core/metamodel.class.php index bb7e92a05..cbb0b1637 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -164,6 +164,10 @@ abstract class MetaModel } } + private static $m_bTraceQueries = true; + private static $m_aQueriesLog = array(); + + private static $m_sDBName = ""; private static $m_sTablePrefix = ""; // table prefix for the current application instance (allow several applications on the same DB) private static $m_Category2Class = array(); @@ -1167,15 +1171,36 @@ abstract class MetaModel return false; } + protected static $m_aQueryStructCache = array(); + public static function MakeSelectQuery(DBObjectSearch $oFilter, $aOrderBy = array(), $aArgs = array()) { - $aTranslation = array(); - $aClassAliases = array(); - $aTableAliases = array(); - $oConditionTree = $oFilter->GetCriteria(); - $oSelect = self::MakeQuery($oFilter->GetClassAlias(), $oConditionTree, $aClassAliases, $aTableAliases, $aTranslation, $oFilter); + // Query caching + // + $bQueryCacheEnabled = true; + $sOqlQuery = $oFilter->ToOql(); + if ($bQueryCacheEnabled) + { + if (array_key_exists($sOqlQuery, self::$m_aQueryStructCache)) + { + // hit! + $oSelect = clone self::$m_aQueryStructCache[$sOqlQuery]; + } + } + + if (!isset($oSelect)) + { + $aTranslation = array(); + $aClassAliases = array(); + $aTableAliases = array(); + $oConditionTree = $oFilter->GetCriteria(); + $oSelect = self::MakeQuery($oFilter->GetClassAlias(), $oConditionTree, $aClassAliases, $aTableAliases, $aTranslation, $oFilter); + + self::$m_aQueryStructCache[$sOqlQuery] = clone $oSelect; + } // Check the order by specification + // foreach ($aOrderBy as $sFieldAlias => $bAscending) { MyHelpers::CheckValueInArray('field name in ORDER BY spec', $sFieldAlias, self::GetAttributesList($oFilter->GetClass())); @@ -1196,7 +1221,7 @@ abstract class MetaModel // Prepare arguments (translate any object into scalars) // - $aScalarArgs = array(); + $aScalarArgs = $oFilter->GetInternalParams(); foreach($aArgs as $sArgName => $value) { if (self::IsValidObject($value)) @@ -1215,9 +1240,41 @@ abstract class MetaModel $aScalarArgs[$sArgName] = (string) $value; } } - - //MyHelpers::var_dump_html($oSelect->RenderSelect($aOrderBy)); - return $oSelect->RenderSelect($aOrderBy, $aScalarArgs); + + // Go + // + $sRes = $oSelect->RenderSelect($aOrderBy, $aScalarArgs); + + if (self::$m_bTraceQueries) + { + $aParams = array(); + if (!array_key_exists($sOqlQuery, self::$m_aQueriesLog)) + { + self::$m_aQueriesLog[$sOqlQuery] = array( + 'sql' => array(), + 'count' => 0, + ); + } + self::$m_aQueriesLog[$sOqlQuery]['count']++; + self::$m_aQueriesLog[$sOqlQuery]['sql'][] = $sRes; + } + + return $sRes; + } + + public static function ShowQueryTrace() + { + $iTotal = 0; + foreach (self::$m_aQueriesLog as $sOql => $aQueryData) + { + echo "

    $sOql

    \n"; + $iTotal += $aQueryData['count']; + echo '

    '.$aQueryData['count'].'

    '; + echo '

    Example: '.$aQueryData['sql'][0].'

    '; + } + echo "

    Total

    \n"; + echo "

    Count of executed queries: $iTotal

    "; + echo "

    Count of built queries: ".count(self::$m_aQueriesLog)."

    "; } public static function MakeDeleteQuery(DBObjectSearch $oFilter) @@ -2390,6 +2447,8 @@ abstract class MetaModel throw new CoreException('Database not found, check your configuration file', array('config_file'=>$sConfigFile, 'db_name'=>self::$m_sDBName)); } } + // Some of the init could not be done earlier (requiring classes to be declared and DB to be accessible) + self::InitPlugins(); } public static function LoadConfig($sConfigFile) @@ -2425,6 +2484,15 @@ abstract class MetaModel CMDBSource::Init($sServer, $sUser, $sPwd); // do not select the DB (could not exist) } + protected static $m_aPlugins = array(); + public static function RegisterPlugin($sType, $sName, $aInitCallSpec = array()) + { + self::$m_aPlugins[$sName] = array( + 'type' => $sType, + 'init' => $aInitCallSpec, + ); + } + protected static function Plugin($sConfigFile, $sModuleType, $sToInclude) { if (!file_exists($sToInclude)) @@ -2434,6 +2502,22 @@ abstract class MetaModel require_once($sToInclude); } + protected static function InitPlugins() + { + foreach(self::$m_aPlugins as $sName => $aData) + { + $aCallSpec = @$aData['init']; + if (count($aCallSpec) == 2) + { + if (!is_callable($aCallSpec)) + { + throw new CoreException('Wrong declaration in plugin', array('plugin' => $aData['name'], 'type' => $aData['type'], 'class' => $aData['class'], 'init' => $aData['init'])); + } + call_user_func($aCallSpec); + } + } + } + // Building an object // // diff --git a/core/userrights.class.inc.php b/core/userrights.class.inc.php index 01428ae73..25b546920 100644 --- a/core/userrights.class.inc.php +++ b/core/userrights.class.inc.php @@ -46,11 +46,12 @@ abstract class UserRightsAddOnAPI { abstract public function Setup(); // initial installation abstract public function Init(); // loads data (possible optimizations) - abstract public function CheckCredentials($iUserId, $sPassword); // returns the id of the user or false - abstract public function GetFilter($iUserId, $sClass); // returns a filter object - abstract public function IsActionAllowed($iUserId, $sClass, $iActionCode, dbObjectSet $aInstances); - abstract public function IsStimulusAllowed($iUserId, $sClass, $sStimulusCode, dbObjectSet $aInstances); - abstract public function IsActionAllowedOnAttribute($iUserId, $sClass, $sAttCode, $iActionCode, dbObjectSet $aInstances); + abstract public function CheckCredentials($sLogin, $sPassword); // returns the id of the user or false + abstract public function GetUserId($sLogin); // returns the id of the user or false + abstract public function GetFilter($sLogin, $sClass); // returns a filter object + abstract public function IsActionAllowed($iUserId, $sClass, $iActionCode, dbObjectSet $oInstances); + abstract public function IsStimulusAllowed($iUserId, $sClass, $sStimulusCode, dbObjectSet $oInstances); + abstract public function IsActionAllowedOnAttribute($iUserId, $sClass, $sAttCode, $iActionCode, dbObjectSet $oInstances); } @@ -148,9 +149,18 @@ class UserRights return self::$m_sUser; } - public static function GetUserId() + public static function GetUserId($sName = '') { - return self::$m_iUserId; + if (empty($sName)) + { + // return current user id + return self::$m_iUserId; + } + else + { + // find the id out of the login string + return self::$m_oAddOn->GetUserId($sName); + } } public static function GetRealUser() @@ -182,28 +192,49 @@ class UserRights return self::$m_oAddOn->GetFilter(self::$m_iUserId, $sClass); } - public static function IsActionAllowed($sClass, $iActionCode, dbObjectSet $aInstances) + public static function IsActionAllowed($sClass, $iActionCode, dbObjectSet $oInstances, $iUserId = null) { if (!MetaModel::HasCategory($sClass, 'bizmodel')) return true; if (!self::CheckLogin()) return false; - return self::$m_oAddOn->IsActionAllowed(self::$m_iUserId, $sClass, $iActionCode, $aInstances); + if (is_null($iUserId)) + { + return self::$m_oAddOn->IsActionAllowed(self::$m_iUserId, $sClass, $iActionCode, $oInstances); + } + else + { + return self::$m_oAddOn->IsActionAllowed($iUserId, $sClass, $iActionCode, $oInstances); + } } - public static function IsStimulusAllowed($sClass, $sStimulusCode, dbObjectSet $aInstances) + public static function IsStimulusAllowed($sClass, $sStimulusCode, dbObjectSet $oInstances, $iUserId = null) { if (!MetaModel::HasCategory($sClass, 'bizmodel')) return true; if (!self::CheckLogin()) return false; - return self::$m_oAddOn->IsStimulusAllowed(self::$m_iUserId, $sClass, $sStimulusCode, $aInstances); + if (is_null($iUserId)) + { + return self::$m_oAddOn->IsStimulusAllowed(self::$m_iUserId, $sClass, $sStimulusCode, $oInstances); + } + else + { + return self::$m_oAddOn->IsStimulusAllowed($iUserId, $sClass, $sStimulusCode, $oInstances); + } } - public static function IsActionAllowedOnAttribute($sClass, $sAttCode, $iActionCode, dbObjectSet $aInstances) + public static function IsActionAllowedOnAttribute($sClass, $sAttCode, $iActionCode, dbObjectSet $oInstances, $iUserId = null) { if (!MetaModel::HasCategory($sClass, 'bizmodel')) return true; if (!self::CheckLogin()) return false; - return self::$m_oAddOn->IsActionAllowedOnAttribute(self::$m_iUserId, $sClass, $sAttCode, $iActionCode, $aInstances); + if (is_null($iUserId)) + { + return self::$m_oAddOn->IsActionAllowedOnAttribute(self::$m_iUserId, $sClass, $sAttCode, $iActionCode, $oInstances); + } + else + { + return self::$m_oAddOn->IsActionAllowedOnAttribute($iUserId, $sClass, $sAttCode, $iActionCode, $oInstances); + } } } diff --git a/pages/usermanagement_classproj.php b/pages/usermanagement_classproj.php index a2e8f7195..826f04111 100644 --- a/pages/usermanagement_classproj.php +++ b/pages/usermanagement_classproj.php @@ -55,7 +55,14 @@ function ComputeProjections($oPage, $sScope) $oDimension->CheckProjectionSpec($aClassProjs[$sClass][$iDimension]); $aValues = $aClassProjs[$sClass][$iDimension]->ProjectObject($oObject); - $sValues = implode(', ', $aValues); + if (is_null($aValues)) + { + $sValues = ''; + } + else + { + $sValues = implode(', ', $aValues); + } $oObjectProj['dim'.$oDimension->GetKey()] = htmlentities($sValues); } diff --git a/pages/usermanagement_profileproj.php b/pages/usermanagement_profileproj.php index a7497d0e0..4ca6d6aa5 100644 --- a/pages/usermanagement_profileproj.php +++ b/pages/usermanagement_profileproj.php @@ -65,7 +65,14 @@ function ComputeProjections($oPage) $oDimension->CheckProjectionSpec($aProPros[$iProfile][$iDimension]); $aValues = $aProPros[$iProfile][$iDimension]->ProjectUser($oUser); - $sValues = implode(', ', $aValues); + if (is_null($aValues)) + { + $sValues = ''; + } + else + { + $sValues = implode(', ', $aValues); + } $aUserProfileProj['dim'.$oDimension->GetKey()] = htmlentities($sValues); } diff --git a/pages/usermanagement_userstatus.php b/pages/usermanagement_userstatus.php new file mode 100644 index 000000000..475e39ad8 --- /dev/null +++ b/pages/usermanagement_userstatus.php @@ -0,0 +1,285 @@ +Fetch()) + { + $aDimensions[$oDimension->GetKey()] = $oDimension; + } + + // Load the class projections for a further usage + // + $aClassProj = array(); + $oClassProjSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_ClassProjection")); + while ($oClassProj = $oClassProjSet->Fetch()) + { + $aClassProjs[$oClassProj->Get('class')][$oClassProj->Get('dimensionid')] = $oClassProj; + } + + // Setup display structure + // + $aDisplayConfig = array(); + foreach ($aDimensions as $iDimension => $oDimension) + { + $aDisplayConfig['dim'.$oDimension->GetKey()] = array('label' => $oDimension->GetName(), 'description' => $oDimension->Get('description')); + } + + // Load objects + // + $aDisplayData = array(); + $sClass = get_class($oObject); + $aObjectProj = array(); + foreach ($aDimensions as $iDimension => $oDimension) + { + // #@# to be moved, may be time consuming + $oDimension->CheckProjectionSpec($aClassProjs[$sClass][$iDimension]); + + $aValues = $aClassProjs[$sClass][$iDimension]->ProjectObject($oObject); + if (is_null($aValues)) + { + $sValues = ''; + } + else + { + $sValues = implode(', ', $aValues); + } + $oObjectProj['dim'.$oDimension->GetKey()] = htmlentities($sValues); + } + + $aDisplayData[] = $oObjectProj; + + $oPage->table($aDisplayConfig, $aDisplayData); +} + + +function ComputeUserProjections($oPage, $oUser) +{ + // Load the profiles for a further usage + // + $aProfiles = array(); + $oProfileSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_Profiles")); + while ($oProfile = $oProfileSet->Fetch()) + { + $aProfiles[$oProfile->GetKey()] = $oProfile; + } + + // Load the dimensions for a further usage + // + $aDimensions = array(); + $oDimensionSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_Dimensions")); + while ($oDimension = $oDimensionSet->Fetch()) + { + $aDimensions[$oDimension->GetKey()] = $oDimension; + } + + // Load the profile projections for a further usage + // + $aProPro = array(); + $oProProSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_ProfileProjection")); + while ($oProPro = $oProProSet->Fetch()) + { + $aProPros[$oProPro->Get('profileid')][$oProPro->Get('dimensionid')] = $oProPro; + } + + // Setup display structure + // + $aDisplayConfig = array(); + $aDisplayConfig['profile'] = array('label' => 'Profile', 'description' => 'Profile in which the projection is specified'); + foreach ($aDimensions as $iDimension => $oDimension) + { + $aDisplayConfig['dim'.$oDimension->GetKey()] = array('label' => $oDimension->GetName(), 'description' => $oDimension->Get('description')); + } + + // Create a record per profile + // + $aDisplayData = array(); + $oUserProfileSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_UserProfile WHERE userid = :user->id"), array(), array('user' => $oUser)); + while ($oUserProfile = $oUserProfileSet->Fetch()) + { + $iProfile = $oUserProfile->Get('profileid'); + $oProfile = $aProfiles[$iProfile]; + + $aUserProfileProj = array(); + $aUserProfileProj['profile'] = $oProfile->GetName(); + foreach ($aDimensions as $iDimension => $oDimension) + { + // #@# to be moved, may be time consuming + $oDimension->CheckProjectionSpec($aProPros[$iProfile][$iDimension]); + + $aValues = $aProPros[$iProfile][$iDimension]->ProjectUser($oUser); + if (is_null($aValues)) + { + $sValues = ''; + } + else + { + $sValues = implode(', ', $aValues); + } + $aUserProfileProj['dim'.$oDimension->GetKey()] = htmlentities($sValues); + } + + $aDisplayData[] = $aUserProfileProj; + } + + $oPage->table($aDisplayConfig, $aDisplayData); +} + + +function ComputeUserRights($oPage, $oUser, $oObject) +{ + // Set the stage + // + $iUser = $oUser->GetKey(); + $sClass = get_class($oObject); + $iPKey = $oObject->GetKey(); + $oInstances = DBObjectSet::FromArray($sClass, array($oObject)); + $aPermissions = array( + UR_ALLOWED_NO => 'UR_ALLOWED_NO', + UR_ALLOWED_YES => 'UR_ALLOWED_YES', + UR_ALLOWED_DEPENDS => 'UR_ALLOWED_DEPENDS', + ); + $aActions = array( + UR_ACTION_READ => 'Read', + UR_ACTION_MODIFY => 'Modify', + UR_ACTION_DELETE => 'Delete', + UR_ACTION_BULK_READ => 'Bulk Read', + UR_ACTION_BULK_MODIFY => 'Bulk Modify', + UR_ACTION_BULK_DELETE => 'Bulk Delete', + ); + $aAttributeActions = array( + UR_ACTION_READ => 'Read', + UR_ACTION_MODIFY => 'Modify', + UR_ACTION_BULK_READ => 'Bulk Read', + UR_ACTION_BULK_MODIFY => 'Bulk Modify', + ); + + // Determine allowed actions for the object + // + $aDisplayData = array(); + foreach($aActions as $iActionCode => $sActionDesc) + { + $iPermission = UserRights::IsActionAllowed($sClass, $iActionCode, $oInstances, $iUser); + $aDisplayData[] = array( + 'action' => $sActionDesc, + 'permission' => $aPermissions[$iPermission], + ); + } + $aDisplayConfig = array(); + $aDisplayConfig['action'] = array('label' => 'Action', 'description' => ''); + $aDisplayConfig['permission'] = array('label' => 'Permission', 'description' => ''); + $oPage->p('

    Actions

    '); + $oPage->table($aDisplayConfig, $aDisplayData); + + + // Determine allowed actions for the object + // + $aDisplayData = array(); + foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) + { + if (!$oAttDef->IsDirectField()) continue; + + foreach($aAttributeActions as $iActionCode => $sActionDesc) + { + $iPermission = UserRights::IsActionAllowedOnAttribute($sClass, $sAttCode, $iActionCode, $oInstances, $iUser); + $aDisplayData[] = array( + 'attribute' => $sAttCode, + 'action' => $sActionDesc, + 'permission' => $aPermissions[$iPermission], + ); + } + } + $oPage->p('

    Attributes

    '); + if (count($aDisplayData) > 0) + { + $aDisplayConfig = array(); + $aDisplayConfig['attribute'] = array('label' => 'Attribute', 'description' => ''); + $aDisplayConfig['action'] = array('label' => 'Action', 'description' => ''); + $aDisplayConfig['permission'] = array('label' => 'Permission', 'description' => ''); + $oPage->table($aDisplayConfig, $aDisplayData); + } + else + { + $oPage->p('none'); + } + + // Determine allowed stimuli + // + $aDisplayData = array(); + foreach(MetaModel::EnumStimuli($sClass) as $sStimulusCode => $oStimulus) + { + $iPermission = UserRights::IsStimulusAllowed($sClass, $sStimulusCode, $oInstances, $iUser); + $aDisplayData[] = array( + 'stimulus' => $sStimulusCode, + 'permission' => $aPermissions[$iPermission], + ); + } + $oPage->p('

    Stimuli

    '); + if (count($aDisplayData) > 0) + { + $aDisplayConfig = array(); + $aDisplayConfig['stimulus'] = array('label' => 'Stimulus', 'description' => ''); + $aDisplayConfig['permission'] = array('label' => 'Permission', 'description' => ''); + $oPage->table($aDisplayConfig, $aDisplayData); + } + else + { + $oPage->p('none'); + } +} + + +require_once('../application/loginwebpage.class.inc.php'); +login_web_page::DoLogin(); // Check user rights and prompt if needed + +// Display the menu on the left +$oContext = new UserContext(); +$oAppContext = new ApplicationContext(); +$iActiveNodeId = utils::ReadParam('menu', -1); +$currentOrganization = utils::ReadParam('org_id', 1); +$iUser = utils::ReadParam('user_id', -1); +$sObjectClass = utils::ReadParam('object_class', ''); +$iObjectId = utils::ReadParam('object_id', 0); + +$oPage = new iTopWebPage("iTop user management - user status", $currentOrganization); +$oPage->no_cache(); + + +if ($iUser == -1) +{ + $oPage->p('Missing parameter "user_id" - current user is '.UserRights::GetUserId()); +} +else +{ + $oUser = MetaModel::GetObject('URP_Users', $iUser); + + $oPage->p('

    Projections for user '.$oUser->GetName().'

    '); + ComputeUserProjections($oPage, $oUser); + + if (strlen($sObjectClass) != 0) + { + $oObject = MetaModel::GetObject($sObjectClass, $iObjectId); + + $oPage->p('

    Projections for object '.$oObject->GetName().'

    '); + ComputeObjectProjections($oPage, $oObject); + + $oPage->p('

    Resulting rights

    '); + ComputeUserRights($oPage, $oUser, $oObject); + } +} + +$oPage->output(); + +?> From 3544b38ab9edb30a9601e251bc89336da3f67ea6 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Mon, 17 Aug 2009 18:24:26 +0000 Subject: [PATCH 016/227] - Improvements to the history log for large text fields SVN:trunk[91] --- core/cmdbchangeop.class.inc.php | 24 +++++++++++++++++++++++- 1 file changed, 23 insertions(+), 1 deletion(-) diff --git a/core/cmdbchangeop.class.inc.php b/core/cmdbchangeop.class.inc.php index 05da24db5..aadbc656d 100644 --- a/core/cmdbchangeop.class.inc.php +++ b/core/cmdbchangeop.class.inc.php @@ -201,7 +201,29 @@ class CMDBChangeOpSetAttribute extends CMDBChangeOp $sAttName = $oAttDef->GetLabel(); $sNewValue = $this->Get('newvalue'); $sOldValue = $this->Get('oldvalue'); - $sResult = "$sAttName set to $sNewValue (previous value: $sOldValue)"; + if ( (($oAttDef->GetType() == 'String') || ($oAttDef->GetType() == 'Text')) && + (strlen($sNewValue) > strlen($sOldValue)) ) + { + // Check if some text was not appended to the field + if (substr($sNewValue,0, strlen($sOldValue)) == $sOldValue) // Text added at the end + { + $sDelta = substr($sNewValue, strlen($sOldValue)); + $sResult = "$sDelta appended to $sAttName"; + } + else if (substr($sNewValue, -strlen($sOldValue)) == $sOldValue) // Text added at the beginning + { + $sDelta = substr($sNewValue, 0, strlen($sNewValue) - strlen($sOldValue)); + $sResult = "$sDelta appended to $sAttName"; + } + else + { + $sResult = "$sAttName set to $sNewValue (previous value: $sOldValue)"; + } + } + else + { + $sResult = "$sAttName set to $sNewValue (previous value: $sOldValue)"; + } } return $sResult; } From ea59fa945a13586a7b38750f36aefa5f8da8531c Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Tue, 18 Aug 2009 18:51:17 +0000 Subject: [PATCH 017/227] - New search form allowing to perform OQL queries for any class of object SVN:trunk[92] --- application/cmdbabstract.class.inc.php | 46 ++++++++++++++++++---- application/displayblock.class.inc.php | 1 - css/light-grey.css | 24 ++++++++++++ pages/UI.php | 54 ++++++++++++++++++++++++++ pages/UniversalSearch.php | 40 +++++++++++++------ 5 files changed, 144 insertions(+), 21 deletions(-) diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index aaf8bad4f..f95a03eab 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -386,9 +386,19 @@ abstract class cmdbAbstractObject extends CMDBObject public static function GetSearchForm(web_page $oPage, CMDBObjectSet $oSet, $aExtraParams = array()) { + static $iSearchFormId = 0; $sHtml = ''; $numCols=4; + $iSearchFormId++; $sClassName = $oSet->GetFilter()->GetClass(); + + $sHtml .= "\n"; + // Simple search form + $sHtml .= "
    \n"; + $sHtml .= "

    Search for ".MetaModel::GetName($sClassName)." Objects

    \n"; $oUnlimitedFilter = new DBObjectSearch($sClassName); $sHtml .= "
    \n"; $index = 0; @@ -472,14 +482,34 @@ abstract class cmdbAbstractObject extends CMDBObject $sHtml .= "\n"; } $sHtml .= "\n"; - $sHtml .= "
    \n"; - // Soem Debug dumps... - //$sHtml .= "".$oSet->GetFilter()->__DescribeHTML()."
    \n"; - //$sHtml .= "encoding=\"text/serialize\" : ".$oSet->GetFilter()->serialize()."
    \n"; - //$sHtml .= "encoding=\"text/sibusql\" : ".$oSet->GetFilter()->ToSibusQL()."
    \n"; - //$sHtml .= "(Unlimited) ".$oUnlimitedFilter->__DescribeHTML()."
    \n"; - //$sHtml .= "encoding=\"text/serialize\" : ".$oUnlimitedFilter->serialize()."
    \n"; - //$sHtml .= "encoding=\"text/sibusql\" : ".$oUnlimitedFilter->ToSibusQL()."\n"; + $sHtml .= "\n"; + $sHtml .= "
    \n"; + + // OQL query builder + $sHtml .= "
    \n"; + $sHtml .= "

    OQL Query Builder

    \n"; + $sHtml .= "
    \n"; + $sHtml .= "\n"; + $sHtml .= "\n"; + $sHtml .= "\n"; + foreach($aExtraParams as $sName => $sValue) + { + $sHtml .= "\n"; + } + $sHtml .= "\n"; + $sHtml .= "
     \n"; + $sHtml .= "
    \n"; + $sHtml .= "
    \n"; return $sHtml; } diff --git a/application/displayblock.class.inc.php b/application/displayblock.class.inc.php index b12486d5e..33e639952 100644 --- a/application/displayblock.class.inc.php +++ b/application/displayblock.class.inc.php @@ -308,7 +308,6 @@ class DisplayBlock $iSearchSectionId = 1; $sStyle = (isset($aExtraParams['open']) && ($aExtraParams['open'] == 'true')) ? 'SearchDrawer' : 'SearchDrawer DrawerClosed'; $sHtml .= "
    \n"; - $sHtml .= "

    Search form for ".Metamodel::GetName($this->m_oSet->GetClass())."

    \n"; $oPage->add_ready_script("\$(\"#LnkSearch_$iSearchSectionId\").click(function() {\$(\"#Search_$iSearchSectionId\").slideToggle('normal'); $(\"#LnkSearch_$iSearchSectionId\").toggleClass('open');});"); $sHtml .= cmdbAbstractObject::GetSearchForm($oPage, $this->m_oSet, $aExtraParams); $sHtml .= "
    \n"; diff --git a/css/light-grey.css b/css/light-grey.css index 18f72c2e6..85717cb88 100644 --- a/css/light-grey.css +++ b/css/light-grey.css @@ -631,3 +631,27 @@ div.HRDrawer { border: 0; display: block; } +.mini_tabs a { + text-decoration: none; + font-weight:bold; + color: #ccc; + background-color:#333; + padding-left: 1em; + padding-right: 1em; + padding-bottom: 0.25em; +} +.mini_tabs a.selected { + color: #fff; + background-color: #83b217; + padding-top: 0.25em; +} +.mini_tabs ul { + margin: -10px; +} +.mini_tabs ul li { + float: right; + list-style: none; + nopadding-left: 1em; + nopadding-right: 1em; + margin-top: 0; +} diff --git a/pages/UI.php b/pages/UI.php index 48bd6081b..45631dd69 100644 --- a/pages/UI.php +++ b/pages/UI.php @@ -73,6 +73,60 @@ switch($operation) } break; + case 'search_form': + $sOQLClass = utils::ReadParam('oql_class', ''); + $sOQLClause = utils::ReadParam('oql_clause', ''); + $sFormat = utils::ReadParam('format', ''); + $bSearchForm = utils::ReadParam('search_form', true); + if (empty($sOQLClass)) + { + $oP->set_title("iTop - Error"); + $oP->add("

    'oql_class' must be specifed for this operation.

    \n"); + } + else + { + $oP->set_title("iTop - Search results"); + $sOQL = "SELECT $sOQLClass $sOQLClause"; + try + { + $oFilter = DBObjectSearch::FromOQL($sOQL); // To Do: Make sure we don't bypass security + $oSet = new DBObjectSet($oFilter); + if ($bSearchForm) + { + $oBlock = new DisplayBlock($oFilter, 'search', false); + $oBlock->Display($oP, 0); + } + if (strtolower($sFormat) == 'csv') + { + $oBlock = new DisplayBlock($oFilter, 'csv', false); + $oBlock->Display($oP, 0); + } + else + { + $oBlock = new DisplayBlock($oFilter, 'list', false); + $oBlock->Display($oP, 0); + } + } + catch(CoreException $e) + { + $oFilter = new DBObjectSearch($sOQLClass); // To Do: Make sure we don't bypass security + $oSet = new DBObjectSet($oFilter); + if ($bSearchForm) + { + $oBlock = new DisplayBlock($oFilter, 'search', false); + $oBlock->Display($oP, 0); + } + $oP->P("Error incorrect OQL query:"); + $oP->P($e->getHtmlDesc()); + } + catch(Exception $e) + { + $oP->p('An error occured while running the query:'); + $oP->p($e->getMessage()); + } + } + break; + case 'search': $sFilter = utils::ReadParam('filter', ''); $sFormat = utils::ReadParam('format', ''); diff --git a/pages/UniversalSearch.php b/pages/UniversalSearch.php index e649f8d2b..bb4e15647 100644 --- a/pages/UniversalSearch.php +++ b/pages/UniversalSearch.php @@ -18,12 +18,13 @@ $oP = new iTopWebPage("iTop - Universal search", $currentOrganization); // From now on the context is limited to the the selected organization ?? // Now render the content of the page -$sClassName = utils::ReadParam('class', 'bizOrganization'); +$sOQLClass = utils::ReadParam('oql_class', 'bizOrganization'); +$sOQLClause = utils::ReadParam('oql_clause', ''); +$sClassName = utils::ReadParam('class', $sOQLClass); $sFilter = utils::ReadParam('filter', ''); $sOperation = utils::ReadParam('operation', ''); // First part: select the class to search for -$oP->add("
    "); $oP->add("
    "); $oP->add(""); $oP->add("Select the class to search:
    "); -// Second part: advanced search form: -$oFilter = null; -if (!empty($sFilter)) +try { - $oFilter = CMDBSearchFilter::unserialize($sFilter); + if ($sOperation == 'search_form') + { + $sOQL = "SELECT $sOQLClass $sOQLClause"; + $oFilter = DBObjectSearch::FromOQL($sOQL); + } + else + { + // Second part: advanced search form: + if (!empty($sFilter)) + { + $oFilter = CMDBSearchFilter::unserialize($sFilter); + } + else if (!empty($sClassName)) + { + $oFilter = new CMDBSearchFilter($sClassName); + } + } } -else if (!empty($sClassName)) +catch (CoreException $e) { $oFilter = new CMDBSearchFilter($sClassName); + $oP->P("Error:"); + $oP->P($e->getHtmlDesc()); } if ($oFilter != null) { - $oSet =new CMDBObjectSet($oFilter); - cmdbAbstractObject::DisplaySearchForm($oP, $oSet, array('org_id' => $currentOrganization, 'class' => $sClassName)); - $oP->add("
    \n"); + $oSet = new CMDBObjectSet($oFilter); + $oBlock = new DisplayBlock($oFilter, 'search', false); + $oBlock->Display($oP, 0); // Search results - $oP->add("
    "); $oResultBlock = new DisplayBlock($oFilter, 'list', false); $oResultBlock->RenderContent($oP); From daa9657e08ac656de7bdb8223384ed8f37d0adb5 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Thu, 20 Aug 2009 13:09:41 +0000 Subject: [PATCH 018/227] Finalized the module "user rights by profile". It is fully integrated with the application setup (inc. sample data). and it it now ready for developing the user management GUI SVN:trunk[93] --- .../userrightsprofile.class.inc.php | 211 ++- business/itop.business.class.inc.php | 15 +- core/cmdbchangeop.class.inc.php | 9 +- core/dbobjectset.class.php | 2 +- core/metamodel.class.php | 64 +- core/sqlquery.class.inc.php | 8 +- pages/usermanagement_classproj.php | 6 +- pages/usermanagement_profileproj.php | 6 +- pages/usermanagement_userstatus.php | 12 +- setup/data/03.persons.xml | 9 +- setup/data/23.dimensions.xml | 13 + setup/data/24.profiles.xml | 11 + setup/data/25.classprojection.xml | 555 ++++++ setup/data/26.profileprojection.xml | 15 + setup/data/27.actiongrant.xml | 1659 +++++++++++++++++ setup/data/28.attributegrant.xml | 3 + setup/data/29.stimulusgrant.xml | 189 ++ setup/data/export.cmd | 53 +- 18 files changed, 2697 insertions(+), 143 deletions(-) create mode 100644 setup/data/23.dimensions.xml create mode 100644 setup/data/24.profiles.xml create mode 100644 setup/data/25.classprojection.xml create mode 100644 setup/data/26.profileprojection.xml create mode 100644 setup/data/27.actiongrant.xml create mode 100644 setup/data/28.attributegrant.xml create mode 100644 setup/data/29.stimulusgrant.xml diff --git a/addons/userrights/userrightsprofile.class.inc.php b/addons/userrights/userrightsprofile.class.inc.php index df80a8caf..10528bc90 100644 --- a/addons/userrights/userrightsprofile.class.inc.php +++ b/addons/userrights/userrightsprofile.class.inc.php @@ -14,6 +14,11 @@ */ +// It is supposed that this profile does exist in the DB +// Possible improvement: add it when executing the setup procedure +// +define('ADMIN_PROFILE_ID', 1); + class URP_Users extends DBObject { public static function Init() @@ -106,7 +111,7 @@ class URP_Dimensions extends DBObject MetaModel::Init_AddFilterFromAttribute("type"); } - public function CheckProjectionSpec($oProjectionSpec) + public function CheckProjectionSpec($oProjectionSpec, $sProjectedClass) { $sExpression = $oProjectionSpec->Get('value'); $sAttribute = $oProjectionSpec->Get('attribute'); @@ -129,30 +134,46 @@ class URP_Dimensions extends DBObject // 2nd - compute the data type for the projection // - $bIsOql = true; - $sExpressionClass = ''; - try + $sTargetClass = ''; + if (($sExpression == '') || ($sExpression == '')) { - $oObjectSearch = DBObjectSearch::FromOQL($sExpression); - $sExpressionClass = $oObjectSearch->GetClass(); + $sTargetClass = $sProjectedClass; } - catch (OqlException $e) + elseif ($sExpression == '') { - $bIsOql = false; + $sTargetClass = ''; } - if ($bIsOql) + else + { + // Evaluate wether it is a constant or not + try + { + $oObjectSearch = DBObjectSearch::FromOQL($sExpression); + + $sTargetClass = $oObjectSearch->GetClass(); + } + catch (OqlException $e) + { + } + } + + if (empty($sTargetClass)) + { + $sFoundType = '_void_'; + } + else { if (empty($sAttribute)) { - $sFoundType = $sExpressionClass; + $sFoundType = $sTargetClass; } else { - if (!MetaModel::IsValidAttCode($sExpressionClass, $sAttribute)) + if (!MetaModel::IsValidAttCode($sTargetClass, $sAttribute)) { - throw new CoreException('Unkown attribute code in projection specification', array('found' => $sAttribute, 'expecting' => MetaModel::GetAttributesList($sExpressionClass), 'class' => $sExpressionClass, 'projection' => $oProjectionSpec)); + throw new CoreException('Unkown attribute code in projection specification', array('found' => $sAttribute, 'expecting' => MetaModel::GetAttributesList($sTargetClass), 'class' => $sTargetClass, 'projection' => $oProjectionSpec)); } - $oAttDef = MetaModel::GetAttributeDef($sExpressionClass, $sAttribute); + $oAttDef = MetaModel::GetAttributeDef($sTargetClass, $sAttribute); if ($oAttDef->IsExternalKey()) { $sFoundType = $oAttDef->GetTargetClass(); @@ -163,13 +184,9 @@ class URP_Dimensions extends DBObject } } } - else - { - $sFoundType = '_scalar_'; - } // Compare the dimension type and projection type - if ($sFoundType != $sExpectedType) + if (($sFoundType != '_void_') && ($sFoundType != $sExpectedType)) { throw new CoreException('Wrong type in projection specification', array('found' => $sFoundType, 'expecting' => $sExpectedType, 'expression' => $sExpression, 'attribute' => $sAttribute, 'projection' => $oProjectionSpec)); } @@ -234,7 +251,7 @@ class URP_ProfileProjection extends DBObject MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass"=>"URP_Profiles", "jointype"=> "", "label"=>"profile", "description"=>"usage profile", "allowed_values"=>null, "sql"=>"profileid", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("profile", array("label"=>"Profile", "description"=>"Profile name", "allowed_values"=>null, "extkey_attcode"=> 'profileid', "target_attcode"=>"name"))); - MetaModel::Init_AddAttribute(new AttributeString("value", array("label"=>"Value expression", "description"=>"OQL expression (using \$user) | constant | ", "allowed_values"=>null, "sql"=>"value", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("value", array("label"=>"Value expression", "description"=>"OQL expression (using \$user) | constant | | +attribute code", "allowed_values"=>null, "sql"=>"value", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("attribute", array("label"=>"Attribute", "description"=>"Target attribute code (optional)", "allowed_values"=>null, "sql"=>"attribute", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); //MetaModel::Init_InheritFilters(); @@ -245,17 +262,30 @@ class URP_ProfileProjection extends DBObject public function ProjectUser(URP_Users $oUser) { $sExpr = $this->Get('value'); - if (strtolower(substr($sExpr, 0, 6)) == 'select') + if ($sExpr == '') + { + $sColumn = $this->Get('attribute'); + if (empty($sColumn)) + { + $aRes = array($oUser->GetKey()); + } + else + { + $aRes = array($oUser->Get($sColumn)); + } + + } + elseif ($sExpr == '') + { + $aRes = null; + } + elseif (strtolower(substr($sExpr, 0, 6)) == 'select') { $sColumn = $this->Get('attribute'); // SELECT... $oValueSetDef = new ValueSetObjects($sExpr, $sColumn); $aValues = $oValueSetDef->GetValues(array('user' => $oUser), ''); - $aRes = array_values($aValues); - } - elseif ($sExpr == '') - { - $aRes = null; + $aRes = array_keys($aValues); } else { @@ -291,24 +321,42 @@ class URP_ClassProjection extends DBObject MetaModel::Init_AddAttribute(new AttributeString("class", array("label"=>"Class", "description"=>"Target class", "allowed_values"=>null, "sql"=>"class", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeString("value", array("label"=>"Value expression", "description"=>"OQL expression (using \$this) | constant | ", "allowed_values"=>null, "sql"=>"value", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("value", array("label"=>"Value expression", "description"=>"OQL expression (using \$this) | constant | | +attribute code", "allowed_values"=>null, "sql"=>"value", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("attribute", array("label"=>"Attribute", "description"=>"Target attribute code (optional)", "allowed_values"=>null, "sql"=>"attribute", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); //MetaModel::Init_InheritFilters(); MetaModel::Init_AddFilterFromAttribute("dimensionid"); + // #@# verifier MetaModel::Init_AddFilterFromAttribute("class"); } public function ProjectObject($oObject) { $sExpr = $this->Get('value'); - if (strtolower(substr($sExpr, 0, 6)) == 'select') + if ($sExpr == '') + { + $sColumn = $this->Get('attribute'); + if (empty($sColumn)) + { + $aRes = array($oObject->GetKey()); + } + else + { + $aRes = array($oObject->Get($sColumn)); + } + + } + elseif ($sExpr == '') + { + $aRes = null; + } + elseif (strtolower(substr($sExpr, 0, 6)) == 'select') { $sColumn = $this->Get('attribute'); // SELECT... $oValueSetDef = new ValueSetObjects($sExpr, $sColumn); - $aValues = $oValueSetDef->GetValues(array('user' => $oObject), ''); - $aRes = array_values($aValues); + $aValues = $oValueSetDef->GetValues(array('this' => $oObject), ''); + $aRes = array_keys($aValues); } elseif ($sExpr == '') { @@ -357,6 +405,7 @@ class URP_ActionGrant extends DBObject MetaModel::Init_AddFilterFromAttribute("profileid"); MetaModel::Init_AddFilterFromAttribute("profile"); MetaModel::Init_AddFilterFromAttribute("class"); + MetaModel::Init_AddFilterFromAttribute("permission"); MetaModel::Init_AddFilterFromAttribute("action"); } @@ -396,6 +445,7 @@ class URP_StimulusGrant extends DBObject MetaModel::Init_AddFilterFromAttribute("profileid"); MetaModel::Init_AddFilterFromAttribute("profile"); MetaModel::Init_AddFilterFromAttribute("class"); + MetaModel::Init_AddFilterFromAttribute("permission"); MetaModel::Init_AddFilterFromAttribute("stimulus"); } @@ -446,19 +496,22 @@ class UserRightsProfile extends UserRightsAddOnAPI ); // Installation: create the very first user - public function CreateAdministrator($sAdminUser, $sAdminPwd, $sAdminEmail, $sFirstName, $sLastName, $sPhoneNumber) + public function CreateAdministrator($sAdminUser, $sAdminPwd) { - // Maybe we should check that no other user with userid == 0 exists $oUser = new URP_Users(); $oUser->Set('login', $sAdminUser); $oUser->Set('password', $sAdminPwd); - $oUser->Set('email', $sAdminEmail); - $oUser->Set('firstname', $sFirstName); - $oUser->Set('lastname', $sLastName); - $oUser->Set('phonenumber', $sPhoneNumber); - $oUser->Set('userid', 1); // one is for root ! + $oUser->Set('email', 'n/a'); + $oUser->Set('firstname', 'administrator'); + $oUser->Set('lastname', 'itop'); + $oUser->Set('userid', 1); // let's mark it as #1 (for what purpose?) $iUserId = $oUser->DBInsertNoReload(); - $this->SetupUser($iUserId, true); + + // Add this user to the very specific 'admin' profile + $oUserProfile = new URP_UserProfile(); + $oUserProfile->Set('userid', $iUserId); + $oUserProfile->Set('profileid', ADMIN_PROFILE_ID); + $oUserProfile->DBInsertNoReload(); return true; } @@ -543,9 +596,9 @@ class UserRightsProfile extends UserRightsAddOnAPI { $iProfileId = $oProfile->GetKey(); - // Create grant records, for any class where it applies + // Create grant records, for any class where it matters (the rest could be done later on) // - foreach(array('bizmodel', 'application', 'gui', 'core/cmdb') as $sCategory) + foreach(array('bizmodel') as $sCategory) { foreach (MetaModel::GetClasses($sCategory) as $sClass) { @@ -633,6 +686,8 @@ class UserRightsProfile extends UserRightsAddOnAPI protected $m_aUserProfiles = array(); // userid,profileid -> object protected $m_aProPro = array(); // profileid,dimensionid -> object + protected $m_aAdmins = array(); // id of users being linked to the profile #ADMIN_PROFILE_ID + protected $m_aClassActionGrants = array(); // profile, class, action -> permission protected $m_aObjectActionGrants = array(); // userid, class, id, action -> permission, list of attributes @@ -667,7 +722,11 @@ class UserRightsProfile extends UserRightsAddOnAPI $oUserProfileSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_UserProfile")); while ($oUserProfile = $oUserProfileSet->Fetch()) { - $this->m_aUserProfiles[$oUserProfile->Get('userid')][$oUserProfile->Get('profileid')] = $oUserProfile; + $this->m_aUserProfiles[$oUserProfile->Get('userid')][$oUserProfile->Get('profileid')] = $oUserProfile; + if ($oUserProfile->Get('profileid') == ADMIN_PROFILE_ID) + { + $this->m_aAdmins[] = $oUserProfile->Get('userid'); + } } $oProProSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_ProfileProjection")); @@ -734,7 +793,7 @@ exit; if (isset($aTest)) return $aTest; // Get the permission for this profile/class/action - $oSearch = DBObjectSearch::FromOQL("SELECT URP_ActionGrant WHERE class = :class AND action = :action AND profileid = :profile"); + $oSearch = DBObjectSearch::FromOQL("SELECT URP_ActionGrant WHERE class = :class AND action = :action AND profileid = :profile AND permission = 'yes'"); $oSet = new DBObjectSet($oSearch, array(), array('class'=>$sClass, 'action'=>$sAction, 'profile'=>$iProfile)); if ($oSet->Count() < 1) { @@ -764,14 +823,24 @@ exit; { continue; // loop to the next profile } - elseif ($oGrantRecord->Get('permission') == 'yes') + else { $iInstancePermission = UR_ALLOWED_YES; - // merge the list of attributes allowed for this profile + // update the list of attributes with those allowed for this profile + // $oSearch = DBObjectSearch::FromOQL("SELECT URP_AttributeGrant WHERE actiongrantid = :actiongrantid"); $oSet = new DBObjectSet($oSearch, array(), array('actiongrantid' => $oGrantRecord->GetKey())); - $aAttributes = array_merge($aAttributes, $oSet->GetColumnAsArray('attcode', false)); + $aProfileAttributes = $oSet->GetColumnAsArray('attcode', false); + if (count($aProfileAttributes) == 0) + { + $aAllAttributes = array_keys(MetaModel::ListAttributeDefs($sClass)); + $aAttributes = array_merge($aAttributes, $aAllAttributes); + } + else + { + $aAttributes = array_merge($aAttributes, $aProfileAttributes); + } } } @@ -785,6 +854,9 @@ exit; public function IsActionAllowed($iUserId, $sClass, $iActionCode, dbObjectSet $oInstances) { + // super admin rights + if (in_array($iUserId, $this->m_aAdmins)) return true; + $oUser = $this->m_aUsers[$iUserId]; $oInstances->Rewind(); @@ -798,6 +870,7 @@ exit; if ($iInstancePermission != $iGlobalPermission) { $iGlobalPermission = UR_ALLOWED_DEPENDS; + break; } } else @@ -805,6 +878,8 @@ exit; $iGlobalPermission = $iInstancePermission; } } + $oInstances->Rewind(); + if (isset($iGlobalPermission)) { return $iGlobalPermission; @@ -817,6 +892,9 @@ exit; public function IsActionAllowedOnAttribute($iUserId, $sClass, $sAttCode, $iActionCode, dbObjectSet $oInstances) { + // super admin rights + if (in_array($iUserId, $this->m_aAdmins)) return true; + $oUser = $this->m_aUsers[$iUserId]; $oInstances->Rewind(); @@ -846,6 +924,8 @@ exit; $iGlobalPermission = $iInstancePermission; } } + $oInstances->Rewind(); + if (isset($iGlobalPermission)) { return $iGlobalPermission; @@ -858,6 +938,9 @@ exit; public function IsStimulusAllowed($iUserId, $sClass, $sStimulusCode, dbObjectSet $oInstances) { + // super admin rights + if (in_array($iUserId, $this->m_aAdmins)) return true; + $oUser = $this->m_aUsers[$iUserId]; // Note: this code is VERY close to the code of IsActionAllowed() @@ -869,19 +952,14 @@ exit; foreach($this->GetMatchingProfiles($oUser, $oObject) as $iProfile) { // Get the permission for this profile/class/stimulus - $oSearch = DBObjectSearch::FromOQL("SELECT URP_StimulusGrant WHERE class = :class AND stimulus = :stimulus AND profileid = :profile"); + $oSearch = DBObjectSearch::FromOQL("SELECT URP_StimulusGrant WHERE class = :class AND stimulus = :stimulus AND profileid = :profile AND permission = 'yes'"); $oSet = new DBObjectSet($oSearch, array(), array('class'=>$sClass, 'stimulus'=>$sStimulusCode, 'profile'=>$iProfile)); if ($oSet->Count() < 1) { return UR_ALLOWED_NO; } - - $oGrantRecord = $oSet->Fetch(); - $sPermission = $oGrantRecord->Get('permission'); - if ($sPermission == 'yes') - { - $iInstancePermission = UR_ALLOWED_YES; - } + // no need to fetch the record, we've requested the records having permission = 'yes' + $iInstancePermission = UR_ALLOWED_YES; } if (isset($iGlobalPermission)) { @@ -895,6 +973,8 @@ exit; $iGlobalPermission = $iInstancePermission; } } + $oInstances->Rewind(); + if (isset($iGlobalPermission)) { return $iGlobalPermission; @@ -918,28 +998,31 @@ exit; $aObjectProjection = $this->m_aClassProjs[$sClass][$iDimension]->ProjectObject($oObject); $aRes = array(); - foreach ($this->m_aUserProfiles[$iUser] as $iProfile => $oProfile) + if (array_key_exists($iUser, $this->m_aUserProfiles) > 0) { - if (is_null($aObjectProjection)) + foreach ($this->m_aUserProfiles[$iUser] as $iProfile => $oProfile) { - $aRes[] = $iProfile; - } - else - { - // user projection to be cached on a given page ! - $aUserProjection = $this->m_aProPros[$iProfile][$iDimension]->ProjectUser($oUser); - - if (is_null($aUserProjection)) + if (is_null($aObjectProjection)) { $aRes[] = $iProfile; } else { - $aMatchingValues = array_intersect($aObjectProjection, $aUserProjection); - if (count($aMatchingValues) > 0) + // user projection to be cached on a given page ! + $aUserProjection = $this->m_aProPros[$iProfile][$iDimension]->ProjectUser($oUser); + + if (is_null($aUserProjection)) { $aRes[] = $iProfile; } + else + { + $aMatchingValues = array_intersect($aObjectProjection, $aUserProjection); + if (count($aMatchingValues) > 0) + { + $aRes[] = $iProfile; + } + } } } } diff --git a/business/itop.business.class.inc.php b/business/itop.business.class.inc.php index 764149007..2015210b4 100644 --- a/business/itop.business.class.inc.php +++ b/business/itop.business.class.inc.php @@ -249,7 +249,7 @@ class bizPerson extends bizContact "name_attcode" => "name", "state_attcode" => "", "reconc_keys" => array("org_name", "first_name", "name"), // comment en définir plusieurs - // "reconc_keys" => array("org_name", "employe_number"), + // "reconc_keys" => array("org_name", "employee_number"), "db_table" => "persons", // Can it use the same physical DB table as any contact ? "db_key_field" => "id", "db_finalclass_field" => "", @@ -258,20 +258,21 @@ class bizPerson extends bizContact MetaModel::Init_Params($aParams); MetaModel::Init_InheritAttributes(); MetaModel::Init_AddAttribute(new AttributeString("first_name", array("label"=>"first Name", "description"=>"First name", "allowed_values"=>null, "sql"=>"first_name", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeString("employe_number", array("label"=>"Employe Number", "description"=>"employe number", "allowed_values"=>null, "sql"=>"employe_number", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("employee_number", array("label"=>"Employee Number", "description"=>"employee number", "allowed_values"=>null, "sql"=>"employee_number", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); -// MetaModel::Init_AddAttribute(new AttributeExternalKey("login_id", array("targetclass"=>"URP_Users", "label"=>"Login", "description"=>"Login information", "allowed_values"=>null, "sql"=>"login_id", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("login_id", array("targetclass"=>"URP_Users", "label"=>"Login", "description"=>"Login information", "allowed_values"=>null, "sql"=>"login_id", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_InheritFilters(); MetaModel::Init_AddFilterFromAttribute("first_name"); - MetaModel::Init_AddFilterFromAttribute("employe_number"); + MetaModel::Init_AddFilterFromAttribute("employee_number"); + MetaModel::Init_AddFilterFromAttribute("login_id"); // Display lists - MetaModel::Init_SetZListItems('details', array('first_name', 'name', 'status', 'org_id', 'email', 'location_id', 'phone', 'employe_number')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('details', array('first_name', 'name', 'status', 'org_id', 'email', 'location_id', 'phone', 'employee_number', 'login_id')); // Attributes to be displayed for the complete details MetaModel::Init_SetZListItems('list', array('first_name', 'name', 'status', 'org_id', 'email', 'location_id', 'phone')); // Attributes to be displayed for a list // Search criteria - MetaModel::Init_SetZListItems('standard_search', array('first_name', 'name', 'status', 'email', 'location_id', 'phone', 'employe_number')); // Criteria of the std search form - MetaModel::Init_SetZListItems('advanced_search', array('first_name', 'name', 'status', 'email', 'location_id', 'phone', 'employe_number')); // Criteria of the advanced search form + MetaModel::Init_SetZListItems('standard_search', array('first_name', 'name', 'status', 'email', 'location_id', 'phone', 'employee_number', 'login_id')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('first_name', 'name', 'status', 'email', 'location_id', 'phone', 'employee_number', 'login_id')); // Criteria of the advanced search form } public function Generate(cmdbDataGenerator $oGenerator) diff --git a/core/cmdbchangeop.class.inc.php b/core/cmdbchangeop.class.inc.php index aadbc656d..2c0913910 100644 --- a/core/cmdbchangeop.class.inc.php +++ b/core/cmdbchangeop.class.inc.php @@ -194,8 +194,13 @@ class CMDBChangeOpSetAttribute extends CMDBChangeOp public function GetDescription() { $sResult = ''; - $oEmptySet = new DBObjectSet($this->Get('objclass')); - if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oEmptySet) == UR_ALLOWED_YES) + $oTargetObjectClass = $this->Get('objclass'); + $oTargetObjectKey = $this->Get('objkey'); + $oTargetSearch = new DBObjectSearch($oTargetObjectClass); + $oTargetSearch->AddCondition('id', $oTargetObjectKey); + + $oMonoObjectSet = new DBObjectSet($oTargetSearch); + if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES) { $oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode')); $sAttName = $oAttDef->GetLabel(); diff --git a/core/dbobjectset.class.php b/core/dbobjectset.class.php index 6384864d8..d1b80429c 100644 --- a/core/dbobjectset.class.php +++ b/core/dbobjectset.class.php @@ -21,7 +21,7 @@ class DBObjectSet private $m_aId2Row; private $m_iCurrRow; - public function __construct($oFilter, $aOrderBy = array(), $aArgs = array()) + public function __construct(DBObjectSearch $oFilter, $aOrderBy = array(), $aArgs = array()) { $this->m_oFilter = $oFilter; $this->m_aOrderBy = $aOrderBy; diff --git a/core/metamodel.class.php b/core/metamodel.class.php index cbb0b1637..5fe13c140 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -1173,6 +1173,32 @@ abstract class MetaModel protected static $m_aQueryStructCache = array(); + protected static function PrepareQueryArguments($aArgs) + { + // Translate any object into scalars + // + $aScalarArgs = array(); + foreach($aArgs as $sArgName => $value) + { + if (self::IsValidObject($value)) + { + $aScalarArgs[$sArgName] = $value->GetKey(); + $aScalarArgs[$sArgName.'->id'] = $value->GetKey(); + + $sClass = get_class($value); + foreach(self::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) + { + $aScalarArgs[$sArgName.'->'.$sAttCode] = $value->Get($sAttCode); + } + } + else + { + $aScalarArgs[$sArgName] = (string) $value; + } + } + return $aScalarArgs; + } + public static function MakeSelectQuery(DBObjectSearch $oFilter, $aOrderBy = array(), $aArgs = array()) { // Query caching @@ -1219,30 +1245,9 @@ abstract class MetaModel } } - // Prepare arguments (translate any object into scalars) - // - $aScalarArgs = $oFilter->GetInternalParams(); - foreach($aArgs as $sArgName => $value) - { - if (self::IsValidObject($value)) - { - $aScalarArgs[$sArgName] = $value->GetKey(); - $aScalarArgs[$sArgName.'->id'] = $value->GetKey(); - - $sClass = get_class($value); - foreach(self::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) - { - $aScalarArgs[$sArgName.'->'.$sAttCode] = $value->Get($sAttCode); - } - } - else - { - $aScalarArgs[$sArgName] = (string) $value; - } - } - // Go // + $aScalarArgs = array_merge(self::PrepareQueryArguments($aArgs), $oFilter->GetInternalParams()); $sRes = $oSelect->RenderSelect($aOrderBy, $aScalarArgs); if (self::$m_bTraceQueries) @@ -1277,17 +1282,18 @@ abstract class MetaModel echo "

    Count of built queries: ".count(self::$m_aQueriesLog)."

    "; } - public static function MakeDeleteQuery(DBObjectSearch $oFilter) + public static function MakeDeleteQuery(DBObjectSearch $oFilter, $aArgs = array()) { $aTranslation = array(); $aClassAliases = array(); $aTableAliases = array(); $oConditionTree = $oFilter->GetCriteria(); $oSelect = self::MakeQuery($oFilter->GetClassAlias(), $oConditionTree, $aClassAliases, $aTableAliases, $aTranslation, $oFilter); - return $oSelect->RenderDelete(); + $aScalarArgs = array_merge(self::PrepareQueryArguments($aArgs), $oFilter->GetInternalParams()); + return $oSelect->RenderDelete($aScalarArgs); } - public static function MakeUpdateQuery(DBObjectSearch $oFilter, $aValues) + public static function MakeUpdateQuery(DBObjectSearch $oFilter, $aValues, $aArgs = array()) { // $aValues is an array of $sAttCode => $value $aTranslation = array(); @@ -1295,7 +1301,8 @@ abstract class MetaModel $aTableAliases = array(); $oConditionTree = $oFilter->GetCriteria(); $oSelect = self::MakeQuery($oFilter->GetClassAlias(), $oConditionTree, $aClassAliases, $aTableAliases, $aTranslation, $oFilter, array(), $aValues); - return $oSelect->RenderUpdate(); + $aScalarArgs = array_merge(self::PrepareQueryArguments($aArgs), $oFilter->GetInternalParams()); + return $oSelect->RenderUpdate($aScalarArgs); } private static function MakeQuery($sGlobalTargetAlias, &$oConditionTree, &$aClassAliases, &$aTableAliases, &$aTranslation, DBObjectSearch $oFilter, $aExpectedAtts = array(), $aValues = array()) @@ -2439,6 +2446,9 @@ abstract class MetaModel if (self::DBExists()) { CMDBSource::SelectDB(self::$m_sDBName); + + // Some of the init could not be done earlier (requiring classes to be declared and DB to be accessible) + self::InitPlugins(); } else { @@ -2447,8 +2457,6 @@ abstract class MetaModel throw new CoreException('Database not found, check your configuration file', array('config_file'=>$sConfigFile, 'db_name'=>self::$m_sDBName)); } } - // Some of the init could not be done earlier (requiring classes to be declared and DB to be accessible) - self::InitPlugins(); } public static function LoadConfig($sConfigFile) diff --git a/core/sqlquery.class.inc.php b/core/sqlquery.class.inc.php index 1661ed4a2..700d0b33b 100644 --- a/core/sqlquery.class.inc.php +++ b/core/sqlquery.class.inc.php @@ -171,7 +171,7 @@ class SQLQuery } // Interface, build the SQL query - public function RenderDelete() + public function RenderDelete($aArgs = array()) { // The goal will be to complete the list as we build the Joins $aFrom = array(); @@ -193,12 +193,12 @@ class SQLQuery throw new CoreException("Building a request wich will delete every object of a given table -looks suspicious- please use truncate instead..."); } */ - $sWhere = self::ClauseWhere($oCondition); + $sWhere = self::ClauseWhere($oCondition, $aArgs); return "DELETE $sDelete FROM $sFrom WHERE $sWhere"; } // Interface, build the SQL query - public function RenderUpdate() + public function RenderUpdate($aArgs = array()) { // The goal will be to complete the list as we build the Joins $aFrom = array(); @@ -210,7 +210,7 @@ class SQLQuery $sFrom = self::ClauseFrom($aFrom); $sValues = self::ClauseValues($aSetValues); - $sWhere = self::ClauseWhere($oCondition); + $sWhere = self::ClauseWhere($oCondition, $aArgs); return "UPDATE $sFrom SET $sValues WHERE $sWhere"; } diff --git a/pages/usermanagement_classproj.php b/pages/usermanagement_classproj.php index 826f04111..18ee898bb 100644 --- a/pages/usermanagement_classproj.php +++ b/pages/usermanagement_classproj.php @@ -52,18 +52,18 @@ function ComputeProjections($oPage, $sScope) foreach ($aDimensions as $iDimension => $oDimension) { // #@# to be moved, may be time consuming - $oDimension->CheckProjectionSpec($aClassProjs[$sClass][$iDimension]); + $oDimension->CheckProjectionSpec($aClassProjs[$sClass][$iDimension], $sClass); $aValues = $aClassProjs[$sClass][$iDimension]->ProjectObject($oObject); if (is_null($aValues)) { - $sValues = ''; + $sValues = htmlentities(''); } else { $sValues = implode(', ', $aValues); } - $oObjectProj['dim'.$oDimension->GetKey()] = htmlentities($sValues); + $oObjectProj['dim'.$oDimension->GetKey()] = $sValues; } $aDisplayData[] = $oObjectProj; diff --git a/pages/usermanagement_profileproj.php b/pages/usermanagement_profileproj.php index 4ca6d6aa5..c29f80849 100644 --- a/pages/usermanagement_profileproj.php +++ b/pages/usermanagement_profileproj.php @@ -62,18 +62,18 @@ function ComputeProjections($oPage) foreach ($aDimensions as $iDimension => $oDimension) { // #@# to be moved, may be time consuming - $oDimension->CheckProjectionSpec($aProPros[$iProfile][$iDimension]); + $oDimension->CheckProjectionSpec($aProPros[$iProfile][$iDimension], get_class($oUser)); $aValues = $aProPros[$iProfile][$iDimension]->ProjectUser($oUser); if (is_null($aValues)) { - $sValues = ''; + $sValues = htmlentities(''); } else { $sValues = implode(', ', $aValues); } - $aUserProfileProj['dim'.$oDimension->GetKey()] = htmlentities($sValues); + $aUserProfileProj['dim'.$oDimension->GetKey()] = $sValues; } $aDisplayData[] = $aUserProfileProj; diff --git a/pages/usermanagement_userstatus.php b/pages/usermanagement_userstatus.php index 475e39ad8..8b243ff2a 100644 --- a/pages/usermanagement_userstatus.php +++ b/pages/usermanagement_userstatus.php @@ -45,18 +45,18 @@ function ComputeObjectProjections($oPage, $oObject) foreach ($aDimensions as $iDimension => $oDimension) { // #@# to be moved, may be time consuming - $oDimension->CheckProjectionSpec($aClassProjs[$sClass][$iDimension]); + $oDimension->CheckProjectionSpec($aClassProjs[$sClass][$iDimension], $sClass); $aValues = $aClassProjs[$sClass][$iDimension]->ProjectObject($oObject); if (is_null($aValues)) { - $sValues = ''; + $sValues = htmlentities(''); } else { $sValues = implode(', ', $aValues); } - $oObjectProj['dim'.$oDimension->GetKey()] = htmlentities($sValues); + $oObjectProj['dim'.$oDimension->GetKey()] = $sValues; } $aDisplayData[] = $oObjectProj; @@ -117,18 +117,18 @@ function ComputeUserProjections($oPage, $oUser) foreach ($aDimensions as $iDimension => $oDimension) { // #@# to be moved, may be time consuming - $oDimension->CheckProjectionSpec($aProPros[$iProfile][$iDimension]); + $oDimension->CheckProjectionSpec($aProPros[$iProfile][$iDimension], get_class($oUser)); $aValues = $aProPros[$iProfile][$iDimension]->ProjectUser($oUser); if (is_null($aValues)) { - $sValues = ''; + $sValues = htmlentities(''); } else { $sValues = implode(', ', $aValues); } - $aUserProfileProj['dim'.$oDimension->GetKey()] = htmlentities($sValues); + $aUserProfileProj['dim'.$oDimension->GetKey()] = $sValues; } $aDisplayData[] = $aUserProfileProj; diff --git a/setup/data/03.persons.xml b/setup/data/03.persons.xml index 9a102ad6e..edee771b0 100644 --- a/setup/data/03.persons.xml +++ b/setup/data/03.persons.xml @@ -8,7 +8,8 @@ 29 Jules - + + Dumas @@ -18,7 +19,8 @@ 1 Dumas - + + Hugo @@ -28,6 +30,7 @@ 33172382223 1 Victor -e12345 +e12345 + diff --git a/setup/data/23.dimensions.xml b/setup/data/23.dimensions.xml new file mode 100644 index 000000000..3104cd3d5 --- /dev/null +++ b/setup/data/23.dimensions.xml @@ -0,0 +1,13 @@ + + + +organization + +bizOrganization + + +site + + + + \ No newline at end of file diff --git a/setup/data/24.profiles.xml b/setup/data/24.profiles.xml new file mode 100644 index 000000000..77121719e --- /dev/null +++ b/setup/data/24.profiles.xml @@ -0,0 +1,11 @@ + + + +Administrator +Has the rights on everything (ignores the grant records) + + +Delivery Manager France +Persons in charge of the operations for French customers + + diff --git a/setup/data/25.classprojection.xml b/setup/data/25.classprojection.xml new file mode 100644 index 000000000..267714046 --- /dev/null +++ b/setup/data/25.classprojection.xml @@ -0,0 +1,555 @@ + + + +1 +bizOrganization +<this> + + + +1 +logRealObject +<this> +org_id + + +1 +bizContact +<this> +org_id + + +1 +bizPerson +<this> +org_id + + +1 +bizTeam +<this> +org_id + + +1 +bizDocument +<this> +org_id + + +1 +bizDocVersion +<any> + + + +1 +lnkDocumentRealObject +<any> + + + +1 +lnkContactRealObject +<any> + + + +1 +logInfra +<this> +org_id + + +1 +bizLocation +<this> +org_id + + +1 +bizCircuit +<this> +org_id + + +1 +bizInterface +<this> +org_id + + +1 +lnkInterfaces +<this> +org_id + + +1 +bizDevice +<this> +org_id + + +1 +bizPC +<this> +org_id + + +1 +bizServer +<this> +org_id + + +1 +bizNetworkDevice +<this> +org_id + + +1 +bizInfraGroup +<this> +org_id + + +1 +bizApplication +<this> +org_id + + +1 +lnkInfraGrouping +<any> + + + +1 +lnkClientServer +<any> + + + +1 +bizPatch +<this> +org_id + + +1 +bizIncidentTicket +<this> +customer_id + + +1 +lnkRelatedTicket +<any> + + + +1 +lnkInfraTicket +<any> + + + +1 +lnkContactTicket +<any> + + + +1 +bizWorkgroup +<this> +org_id + + +1 +bizContract +<this> +org_id + + +1 +lnkInfraContract +<any> + + + +1 +lnkContactContract +<any> + + + +1 +lnkDocumentContract +<any> + + + +1 +bizChangeTicket +<this> +customer_id + + +1 +lnkInfraChangeTicket +<any> + + + +1 +lnkContactChange +<any> + + + +1 +bizKnownError +<this> +cust_id + + +1 +lnkInfraError +<any> + + + +1 +lnkDocumentError +<any> + + + +1 +AuditCategory +<any> + + + +1 +AuditRule +<any> + + + +1 +menuNode +<any> + + + +1 +CMDBChange +<any> + + + +1 +CMDBChangeOp +<any> + + + +1 +CMDBChangeOpCreate +<any> + + + +1 +CMDBChangeOpDelete +<any> + + + +1 +CMDBChangeOpSetAttribute +<any> + + + +2 +bizOrganization +<any> + + + +2 +logRealObject +<any> + + + +2 +bizContact +<any> + + + +2 +bizPerson +<any> + + + +2 +bizTeam +<any> + + + +2 +bizDocument +<any> + + + +2 +bizDocVersion +<any> + + + +2 +lnkDocumentRealObject +<any> + + + +2 +lnkContactRealObject +<any> + + + +2 +logInfra +<any> + + + +2 +bizLocation +<any> + + + +2 +bizCircuit +<any> + + + +2 +bizInterface +<any> + + + +2 +lnkInterfaces +<any> + + + +2 +bizDevice +<any> + + + +2 +bizPC +<any> + + + +2 +bizServer +<any> + + + +2 +bizNetworkDevice +<any> + + + +2 +bizInfraGroup +<any> + + + +2 +bizApplication +<any> + + + +2 +lnkInfraGrouping +<any> + + + +2 +lnkClientServer +<any> + + + +2 +bizPatch +<any> + + + +2 +bizIncidentTicket +<any> + + + +2 +lnkRelatedTicket +<any> + + + +2 +lnkInfraTicket +<any> + + + +2 +lnkContactTicket +<any> + + + +2 +bizWorkgroup +<any> + + + +2 +bizContract +<any> + + + +2 +lnkInfraContract +<any> + + + +2 +lnkContactContract +<any> + + + +2 +lnkDocumentContract +<any> + + + +2 +bizChangeTicket +<any> + + + +2 +lnkInfraChangeTicket +<any> + + + +2 +lnkContactChange +<any> + + + +2 +bizKnownError +<any> + + + +2 +lnkInfraError +<any> + + + +2 +lnkDocumentError +<any> + + + +2 +AuditCategory +<any> + + + +2 +AuditRule +<any> + + + +2 +menuNode +<any> + + + +2 +CMDBChange +<any> + + + +2 +CMDBChangeOp +<any> + + + +2 +CMDBChangeOpCreate +<any> + + + +2 +CMDBChangeOpDelete +<any> + + + +2 +CMDBChangeOpSetAttribute +<any> + + + \ No newline at end of file diff --git a/setup/data/26.profileprojection.xml b/setup/data/26.profileprojection.xml new file mode 100644 index 000000000..6d9446278 --- /dev/null +++ b/setup/data/26.profileprojection.xml @@ -0,0 +1,15 @@ + + + +1 +2 +1;2 + + + +2 +2 +<any> + + + diff --git a/setup/data/27.actiongrant.xml b/setup/data/27.actiongrant.xml new file mode 100644 index 000000000..6375b1b8d --- /dev/null +++ b/setup/data/27.actiongrant.xml @@ -0,0 +1,1659 @@ + + + +2 +bizOrganization +yes +read + + +2 +bizOrganization +yes +modify + + +2 +bizOrganization +yes +delete + + +2 +bizOrganization +yes +bulk read + + +2 +bizOrganization +yes +bulk modify + + +2 +bizOrganization +yes +bulk delete + + +2 +logRealObject +yes +read + + +2 +logRealObject +yes +modify + + +2 +logRealObject +yes +delete + + +2 +logRealObject +yes +bulk read + + +2 +logRealObject +yes +bulk modify + + +2 +logRealObject +yes +bulk delete + + +2 +bizContact +yes +read + + +2 +bizContact +yes +modify + + +2 +bizContact +yes +delete + + +2 +bizContact +yes +bulk read + + +2 +bizContact +yes +bulk modify + + +2 +bizContact +yes +bulk delete + + +2 +bizPerson +yes +read + + +2 +bizPerson +yes +modify + + +2 +bizPerson +yes +delete + + +2 +bizPerson +yes +bulk read + + +2 +bizPerson +yes +bulk modify + + +2 +bizPerson +yes +bulk delete + + +2 +bizTeam +yes +read + + +2 +bizTeam +yes +modify + + +2 +bizTeam +yes +delete + + +2 +bizTeam +yes +bulk read + + +2 +bizTeam +yes +bulk modify + + +2 +bizTeam +yes +bulk delete + + +2 +bizDocument +yes +read + + +2 +bizDocument +yes +modify + + +2 +bizDocument +yes +delete + + +2 +bizDocument +yes +bulk read + + +2 +bizDocument +yes +bulk modify + + +2 +bizDocument +yes +bulk delete + + +2 +bizDocVersion +yes +read + + +2 +bizDocVersion +yes +modify + + +2 +bizDocVersion +yes +delete + + +2 +bizDocVersion +yes +bulk read + + +2 +bizDocVersion +yes +bulk modify + + +2 +bizDocVersion +yes +bulk delete + + +2 +lnkDocumentRealObject +yes +read + + +2 +lnkDocumentRealObject +yes +modify + + +2 +lnkDocumentRealObject +yes +delete + + +2 +lnkDocumentRealObject +yes +bulk read + + +2 +lnkDocumentRealObject +yes +bulk modify + + +2 +lnkDocumentRealObject +yes +bulk delete + + +2 +lnkContactRealObject +yes +read + + +2 +lnkContactRealObject +yes +modify + + +2 +lnkContactRealObject +yes +delete + + +2 +lnkContactRealObject +yes +bulk read + + +2 +lnkContactRealObject +yes +bulk modify + + +2 +lnkContactRealObject +yes +bulk delete + + +2 +logInfra +yes +read + + +2 +logInfra +yes +modify + + +2 +logInfra +yes +delete + + +2 +logInfra +yes +bulk read + + +2 +logInfra +yes +bulk modify + + +2 +logInfra +yes +bulk delete + + +2 +bizLocation +yes +read + + +2 +bizLocation +yes +modify + + +2 +bizLocation +yes +delete + + +2 +bizLocation +yes +bulk read + + +2 +bizLocation +yes +bulk modify + + +2 +bizLocation +yes +bulk delete + + +2 +bizCircuit +yes +read + + +2 +bizCircuit +yes +modify + + +2 +bizCircuit +yes +delete + + +2 +bizCircuit +yes +bulk read + + +2 +bizCircuit +yes +bulk modify + + +2 +bizCircuit +yes +bulk delete + + +2 +bizInterface +yes +read + + +2 +bizInterface +yes +modify + + +2 +bizInterface +yes +delete + + +2 +bizInterface +yes +bulk read + + +2 +bizInterface +yes +bulk modify + + +2 +bizInterface +yes +bulk delete + + +2 +lnkInterfaces +yes +read + + +2 +lnkInterfaces +yes +modify + + +2 +lnkInterfaces +yes +delete + + +2 +lnkInterfaces +yes +bulk read + + +2 +lnkInterfaces +yes +bulk modify + + +2 +lnkInterfaces +yes +bulk delete + + +2 +bizDevice +yes +read + + +2 +bizDevice +yes +modify + + +2 +bizDevice +yes +delete + + +2 +bizDevice +yes +bulk read + + +2 +bizDevice +yes +bulk modify + + +2 +bizDevice +yes +bulk delete + + +2 +bizPC +yes +read + + +2 +bizPC +yes +modify + + +2 +bizPC +yes +delete + + +2 +bizPC +yes +bulk read + + +2 +bizPC +yes +bulk modify + + +2 +bizPC +yes +bulk delete + + +2 +bizServer +yes +read + + +2 +bizServer +yes +modify + + +2 +bizServer +yes +delete + + +2 +bizServer +yes +bulk read + + +2 +bizServer +yes +bulk modify + + +2 +bizServer +yes +bulk delete + + +2 +bizNetworkDevice +yes +read + + +2 +bizNetworkDevice +yes +modify + + +2 +bizNetworkDevice +yes +delete + + +2 +bizNetworkDevice +yes +bulk read + + +2 +bizNetworkDevice +yes +bulk modify + + +2 +bizNetworkDevice +yes +bulk delete + + +2 +bizInfraGroup +yes +read + + +2 +bizInfraGroup +yes +modify + + +2 +bizInfraGroup +yes +delete + + +2 +bizInfraGroup +yes +bulk read + + +2 +bizInfraGroup +yes +bulk modify + + +2 +bizInfraGroup +yes +bulk delete + + +2 +bizApplication +yes +read + + +2 +bizApplication +yes +modify + + +2 +bizApplication +yes +delete + + +2 +bizApplication +yes +bulk read + + +2 +bizApplication +yes +bulk modify + + +2 +bizApplication +yes +bulk delete + + +2 +lnkInfraGrouping +yes +read + + +2 +lnkInfraGrouping +yes +modify + + +2 +lnkInfraGrouping +yes +delete + + +2 +lnkInfraGrouping +yes +bulk read + + +2 +lnkInfraGrouping +yes +bulk modify + + +2 +lnkInfraGrouping +yes +bulk delete + + +2 +lnkClientServer +yes +read + + +2 +lnkClientServer +yes +modify + + +2 +lnkClientServer +yes +delete + + +2 +lnkClientServer +yes +bulk read + + +2 +lnkClientServer +yes +bulk modify + + +2 +lnkClientServer +yes +bulk delete + + +2 +bizPatch +yes +read + + +2 +bizPatch +yes +modify + + +2 +bizPatch +yes +delete + + +2 +bizPatch +yes +bulk read + + +2 +bizPatch +yes +bulk modify + + +2 +bizPatch +yes +bulk delete + + +2 +bizIncidentTicket +yes +read + + +2 +bizIncidentTicket +no +modify + + +2 +bizIncidentTicket +no +delete + + +2 +bizIncidentTicket +yes +bulk read + + +2 +bizIncidentTicket +no +bulk modify + + +2 +bizIncidentTicket +no +bulk delete + + +2 +lnkRelatedTicket +yes +read + + +2 +lnkRelatedTicket +yes +modify + + +2 +lnkRelatedTicket +yes +delete + + +2 +lnkRelatedTicket +yes +bulk read + + +2 +lnkRelatedTicket +yes +bulk modify + + +2 +lnkRelatedTicket +yes +bulk delete + + +2 +lnkInfraTicket +yes +read + + +2 +lnkInfraTicket +yes +modify + + +2 +lnkInfraTicket +yes +delete + + +2 +lnkInfraTicket +yes +bulk read + + +2 +lnkInfraTicket +yes +bulk modify + + +2 +lnkInfraTicket +yes +bulk delete + + +2 +lnkContactTicket +yes +read + + +2 +lnkContactTicket +yes +modify + + +2 +lnkContactTicket +yes +delete + + +2 +lnkContactTicket +yes +bulk read + + +2 +lnkContactTicket +yes +bulk modify + + +2 +lnkContactTicket +yes +bulk delete + + +2 +bizWorkgroup +yes +read + + +2 +bizWorkgroup +yes +modify + + +2 +bizWorkgroup +yes +delete + + +2 +bizWorkgroup +yes +bulk read + + +2 +bizWorkgroup +yes +bulk modify + + +2 +bizWorkgroup +yes +bulk delete + + +2 +bizContract +yes +read + + +2 +bizContract +yes +modify + + +2 +bizContract +yes +delete + + +2 +bizContract +yes +bulk read + + +2 +bizContract +yes +bulk modify + + +2 +bizContract +yes +bulk delete + + +2 +lnkInfraContract +yes +read + + +2 +lnkInfraContract +yes +modify + + +2 +lnkInfraContract +yes +delete + + +2 +lnkInfraContract +yes +bulk read + + +2 +lnkInfraContract +yes +bulk modify + + +2 +lnkInfraContract +yes +bulk delete + + +2 +lnkContactContract +yes +read + + +2 +lnkContactContract +yes +modify + + +2 +lnkContactContract +yes +delete + + +2 +lnkContactContract +yes +bulk read + + +2 +lnkContactContract +yes +bulk modify + + +2 +lnkContactContract +yes +bulk delete + + +2 +lnkDocumentContract +yes +read + + +2 +lnkDocumentContract +yes +modify + + +2 +lnkDocumentContract +yes +delete + + +2 +lnkDocumentContract +yes +bulk read + + +2 +lnkDocumentContract +yes +bulk modify + + +2 +lnkDocumentContract +yes +bulk delete + + +2 +bizChangeTicket +yes +read + + +2 +bizChangeTicket +no +modify + + +2 +bizChangeTicket +no +delete + + +2 +bizChangeTicket +yes +bulk read + + +2 +bizChangeTicket +no +bulk modify + + +2 +bizChangeTicket +no +bulk delete + + +2 +lnkInfraChangeTicket +yes +read + + +2 +lnkInfraChangeTicket +yes +modify + + +2 +lnkInfraChangeTicket +yes +delete + + +2 +lnkInfraChangeTicket +yes +bulk read + + +2 +lnkInfraChangeTicket +yes +bulk modify + + +2 +lnkInfraChangeTicket +yes +bulk delete + + +2 +lnkContactChange +yes +read + + +2 +lnkContactChange +yes +modify + + +2 +lnkContactChange +yes +delete + + +2 +lnkContactChange +yes +bulk read + + +2 +lnkContactChange +yes +bulk modify + + +2 +lnkContactChange +yes +bulk delete + + +2 +bizKnownError +yes +read + + +2 +bizKnownError +yes +modify + + +2 +bizKnownError +yes +delete + + +2 +bizKnownError +yes +bulk read + + +2 +bizKnownError +yes +bulk modify + + +2 +bizKnownError +yes +bulk delete + + +2 +lnkInfraError +yes +read + + +2 +lnkInfraError +yes +modify + + +2 +lnkInfraError +yes +delete + + +2 +lnkInfraError +yes +bulk read + + +2 +lnkInfraError +yes +bulk modify + + +2 +lnkInfraError +yes +bulk delete + + +2 +lnkDocumentError +yes +read + + +2 +lnkDocumentError +yes +modify + + +2 +lnkDocumentError +yes +delete + + +2 +lnkDocumentError +yes +bulk read + + +2 +lnkDocumentError +yes +bulk modify + + +2 +lnkDocumentError +yes +bulk delete + + +2 +AuditCategory +yes +read + + +2 +AuditCategory +yes +modify + + +2 +AuditCategory +yes +delete + + +2 +AuditCategory +yes +bulk read + + +2 +AuditCategory +yes +bulk modify + + +2 +AuditCategory +yes +bulk delete + + +2 +AuditRule +yes +read + + +2 +AuditRule +yes +modify + + +2 +AuditRule +yes +delete + + +2 +AuditRule +yes +bulk read + + +2 +AuditRule +yes +bulk modify + + +2 +AuditRule +yes +bulk delete + + +2 +menuNode +yes +read + + +2 +menuNode +yes +modify + + +2 +menuNode +yes +delete + + +2 +menuNode +yes +bulk read + + +2 +menuNode +yes +bulk modify + + +2 +menuNode +yes +bulk delete + + +2 +CMDBChange +yes +read + + +2 +CMDBChange +yes +modify + + +2 +CMDBChange +yes +delete + + +2 +CMDBChange +yes +bulk read + + +2 +CMDBChange +yes +bulk modify + + +2 +CMDBChange +yes +bulk delete + + +2 +CMDBChangeOp +yes +read + + +2 +CMDBChangeOp +yes +modify + + +2 +CMDBChangeOp +yes +delete + + +2 +CMDBChangeOp +yes +bulk read + + +2 +CMDBChangeOp +yes +bulk modify + + +2 +CMDBChangeOp +yes +bulk delete + + +2 +CMDBChangeOpCreate +yes +read + + +2 +CMDBChangeOpCreate +yes +modify + + +2 +CMDBChangeOpCreate +yes +delete + + +2 +CMDBChangeOpCreate +yes +bulk read + + +2 +CMDBChangeOpCreate +yes +bulk modify + + +2 +CMDBChangeOpCreate +yes +bulk delete + + +2 +CMDBChangeOpDelete +yes +read + + +2 +CMDBChangeOpDelete +yes +modify + + +2 +CMDBChangeOpDelete +yes +delete + + +2 +CMDBChangeOpDelete +yes +bulk read + + +2 +CMDBChangeOpDelete +yes +bulk modify + + +2 +CMDBChangeOpDelete +yes +bulk delete + + +2 +CMDBChangeOpSetAttribute +yes +read + + +2 +CMDBChangeOpSetAttribute +yes +modify + + +2 +CMDBChangeOpSetAttribute +yes +delete + + +2 +CMDBChangeOpSetAttribute +yes +bulk read + + +2 +CMDBChangeOpSetAttribute +yes +bulk modify + + +2 +CMDBChangeOpSetAttribute +yes +bulk delete + + diff --git a/setup/data/28.attributegrant.xml b/setup/data/28.attributegrant.xml new file mode 100644 index 000000000..cf1461167 --- /dev/null +++ b/setup/data/28.attributegrant.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/setup/data/29.stimulusgrant.xml b/setup/data/29.stimulusgrant.xml new file mode 100644 index 000000000..974b929ea --- /dev/null +++ b/setup/data/29.stimulusgrant.xml @@ -0,0 +1,189 @@ + + + +2 +bizServer +no +ev_store + + +2 +bizServer +no +ev_ship + + +2 +bizServer +no +ev_plug + + +2 +bizServer +no +ev_configuration_finished + + +2 +bizServer +no +ev_val_failed + + +2 +bizServer +no +ev_mtp + + +2 +bizServer +no +ev_start_change + + +2 +bizServer +no +ev_end_change + + +2 +bizServer +no +ev_decommission + + +2 +bizServer +no +ev_obsolete + + +2 +bizServer +no +ev_recycle + + +2 +bizIncidentTicket +no +ev_assign + + +2 +bizIncidentTicket +no +ev_reassign + + +2 +bizIncidentTicket +no +ev_start_working + + +2 +bizIncidentTicket +no +ev_close + + +2 +bizContract +no +ev_freeze_version + + +2 +bizContract +no +ev_sign + + +2 +bizContract +no +ev_begin + + +2 +bizContract +no +ev_notice + + +2 +bizContract +no +ev_terminate + + +2 +bizContract +no +ev_elapsed + + +2 +bizChangeTicket +yes +ev_validate + + +2 +bizChangeTicket +yes +ev_reject + + +2 +bizChangeTicket +no +ev_reopen + + +2 +bizChangeTicket +no +ev_plan + + +2 +bizChangeTicket +yes +ev_approve + + +2 +bizChangeTicket +no +ev_replan + + +2 +bizChangeTicket +yes +ev_notapprove + + +2 +bizChangeTicket +no +ev_implement + + +2 +bizChangeTicket +no +ev_monitor + + +2 +bizChangeTicket +yes +ev_finish + + diff --git a/setup/data/export.cmd b/setup/data/export.cmd index 5c95ce860..923d30177 100644 --- a/setup/data/export.cmd +++ b/setup/data/export.cmd @@ -1,27 +1,36 @@ SET WEBROOT=http://localhost:81 SET USER=Erwan SET PWD=Taloc + REM The order (numbering) of the files is important since REM it dictates the order to import them back -wget --output-document=01.organizations.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT bizOrganization&format=xml" -wget --output-document=02.locations.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT bizLocation&format=xml" -wget --output-document=03.persons.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT bizPerson&format=xml" -wget --output-document=04.teams.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT bizTeam&format=xml" -wget --output-document=05.pcs.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT bizPC&format=xml" -wget --output-document=06.servers.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT bizServer&format=xml" -wget --output-document=07.applications.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT bizApplication&format=xml" -wget --output-document=08.nw-devices.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT bizNetworkDevice&format=xml" -wget --output-document=09.links_contacts.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT lnkContactRealObject&format=xml" -wget --output-document=10.workgroups.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT bizWorkgroup&format=xml" -wget --output-document=11.incidents.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT bizIncidentTicket&format=xml" -wget --output-document=12.relatedtickets.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT lnkRelatedTicket&format=xml" -wget --output-document=13.infratickets.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT lnkInfraTicket&format=xml" -wget --output-document=14.contacttickets.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT lnkContactTicket&format=xml" -wget --output-document=15.changetickets.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT bizChangeTicket&format=xml" -wget --output-document=16.infrachangetickets.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT lnkInfraChangeTicket&format=xml" -wget --output-document=17.contactchangetickets.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT lnkContactChange&format=xml" -wget --output-document=18.contracts.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT bizContract&format=xml" -wget --output-document=19.infracontracts.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT lnkInfraContract&format=xml" -wget --output-document=20.contactcontracts.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT lnkContactContract&format=xml" -wget --output-document=21.auditcategories.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT AuditCategory&format=xml" -wget --output-document=22.auditrules.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT AuditRule&format=xml" +wget --output-document=01.organizations.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT bizOrganization&format=xml" +wget --output-document=02.locations.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT bizLocation&format=xml" +wget --output-document=03.persons.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT bizPerson&format=xml" +wget --output-document=04.teams.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT bizTeam&format=xml" +wget --output-document=05.pcs.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT bizPC&format=xml" +wget --output-document=06.servers.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT bizServer&format=xml" +wget --output-document=07.applications.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT bizApplication&format=xml" +wget --output-document=08.nw-devices.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT bizNetworkDevice&format=xml" +wget --output-document=09.links_contacts.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT lnkContactRealObject&format=xml" +wget --output-document=10.workgroups.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT bizWorkgroup&format=xml" +wget --output-document=11.incidents.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT bizIncidentTicket&format=xml" +wget --output-document=12.relatedtickets.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT lnkRelatedTicket&format=xml" +wget --output-document=13.infratickets.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT lnkInfraTicket&format=xml" +wget --output-document=14.contacttickets.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT lnkContactTicket&format=xml" +wget --output-document=15.changetickets.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT bizChangeTicket&format=xml" +wget --output-document=16.infrachangetickets.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT lnkInfraChangeTicket&format=xml" +wget --output-document=17.contactchangetickets.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT lnkContactChange&format=xml" +wget --output-document=18.contracts.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT bizContract&format=xml" +wget --output-document=19.infracontracts.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT lnkInfraContract&format=xml" +wget --output-document=20.contactcontracts.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT lnkContactContract&format=xml" +wget --output-document=21.auditcategories.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT AuditCategory&format=xml" +wget --output-document=22.auditrules.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT AuditRule&format=xml" +wget --output-document=23.dimensions.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT URP_Dimensions&format=xml" +wget --output-document=24.profiles.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT URP_Profiles&format=xml" +wget --output-document=25.classprojection.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT URP_ClassProjection&format=xml" +wget --output-document=26.profileprojection.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT URP_ProfileProjection&format=xml" +wget --output-document=27.actiongrant.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT URP_ActionGrant&format=xml" +wget --output-document=28.attributegrant.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT URP_AttributeGrant&format=xml" +wget --output-document=29.stimulusgrant.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT URP_StimulusGrant&format=xml" +pause From 12e433846f9cb80ec22be42d91c238b88ac65427 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Fri, 21 Aug 2009 13:57:38 +0000 Subject: [PATCH 019/227] session_is_register is deprecated since PHP 5.3.0 SVN:trunk[94] --- application/loginwebpage.class.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/loginwebpage.class.inc.php b/application/loginwebpage.class.inc.php index 32e7ad3fb..29a1e33a4 100644 --- a/application/loginwebpage.class.inc.php +++ b/application/loginwebpage.class.inc.php @@ -68,7 +68,7 @@ h1 { $operation = utils::ReadParam('operation', ''); session_start(); - if (!session_is_registered('auth_user') || !session_is_registered('auth_pwd')) + if (!isset($_SESSION['auth_user']) || !isset($_SESSION['auth_pwd'])) { if ($operation == 'login') { From ff6a5a45eee0119f3f725c5c66bdcda9f52805a8 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Fri, 21 Aug 2009 15:20:35 +0000 Subject: [PATCH 020/227] Fixed issue #28, objects not created by the CSV import because mandatory external keys are not in the column set. SVN:trunk[95] --- core/bulkchange.class.inc.php | 18 ++++++++++++--- pages/csvimport.php | 42 +++++++++++++++++++++++++++++------ 2 files changed, 50 insertions(+), 10 deletions(-) diff --git a/core/bulkchange.class.inc.php b/core/bulkchange.class.inc.php index 491852a22..89bfdd0fa 100644 --- a/core/bulkchange.class.inc.php +++ b/core/bulkchange.class.inc.php @@ -335,10 +335,22 @@ class BulkChange } // Check that any external key will have a value proposed - // Could be said once for all rows !!! - foreach(MetaModel::ListAttributeDefs($this->m_sClass) as $sAttCode=>$oAtt) + $aMissingKeys = array(); + foreach (MetaModel::GetExternalKeys($this->m_sClass) as $sExtKeyAttCode => $oExtKey) { - if (!$oAtt->IsExternalKey()) continue; + if (!$oExtKey->IsNullAllowed()) + { + if (!array_key_exists($sExtKeyAttCode, $this->m_aExtKeys) && !array_key_exists($sExtKeyAttCode, $this->m_aAttList)) + { + $aMissingKeys[] = $oExtKey->GetLabel(); + } + } + } + if (count($aMissingKeys) > 0) + { + $sMissingKeys = implode(', ', $aMissingKeys); + $aResult[$iRow]["__STATUS__"] = new RowStatus_Issue("Could not be created, due to missing external key(s): $sMissingKeys"); + return; } // Optionaly record the results diff --git a/pages/csvimport.php b/pages/csvimport.php index 0110e1f53..4d8911e77 100644 --- a/pages/csvimport.php +++ b/pages/csvimport.php @@ -393,6 +393,7 @@ function DoProcessOrVerify($oPage, $sClass, CMDBChange $oChange = null) $aSampleData = $oCSVParser->ToArray(array_keys($aFieldMap), 5); $aDisplayConfig = array(); + $aExtKeys = array(); foreach ($aFieldMap as $sFieldId=>$sColDesc) { if (array_key_exists($sFieldId, $aIsReconcKey)) @@ -417,11 +418,16 @@ function DoProcessOrVerify($oPage, $sClass, CMDBChange $oChange = null) $sAttCode = $sColDesc; $sLabel = MetaModel::GetAttributeDef($sClass, $sAttCode)->GetLabel(); $aDisplayConfig[$sFieldId] = array("label"=>"$sLabel$sReconcKey", "description"=>""); + if (MetaModel::IsValidKeyAttCode($sClass, $sAttCode)) + { + $aExtKeys[] = $sAttCode; + } } elseif (IsExtKeyField($sColDesc)) { list($sExtKeyAttCode, $sForeignAttCode) = GetExtKeyFieldCodes($sColDesc); $aDisplayConfig[$sFieldId] = array("label"=>MakeExtFieldLabel($sClass, $sExtKeyAttCode, $sForeignAttCode), "description"=>""); + $aExtKeys[] = $sExtKeyAttCode; } else { @@ -437,17 +443,39 @@ function DoProcessOrVerify($oPage, $sClass, CMDBChange $oChange = null) } else { + $oPage->p("

    Column consistency

    "); + $aMissingKeys = array(); + foreach (MetaModel::GetExternalKeys($sClass) as $sExtKeyAttCode => $oExtKey) + { + if (!in_array($sExtKeyAttCode, $aExtKeys) && !$oExtKey->IsNullAllowed()) + { + $aMissingKeys[$sExtKeyAttCode] = $oExtKey; + } + } + if (count($aMissingKeys) > 0) + { + $oPage->p("Warning: the objects could not be created, due to some missing mandatory external keys in the field list: "); + $oPage->p("
      "); + foreach($aMissingKeys as $sAttCode => $oAttDef) + { + $oPage->p("
    • ".$oAttDef->GetLabel()."
    • "); + } + $oPage->p("
    "); + } + $oPage->p("

    Check...

    "); } ProcessData($oPage, $sClass, $oCSVParser, $aFieldMap, $aIsReconcKey, $oChange); - $oPage->add("
    "); - $oPage->add(""); - $oPage->add(""); - $oPage->add(""); - $oPage->add(""); + $oPage->add(""); + $oPage->add(""); + $oPage->add(""); + $oPage->add(""); + $oPage->add(""); $oPage->add_input_hidden("fmap", $aFieldMap); $oPage->add_input_hidden("iskey", $aIsReconcKey); + + return; } function Do_Verify($oPage, $sClass) @@ -459,8 +487,8 @@ function Do_Verify($oPage, $sClass) // FORM started by DoProcessOrVerify... $oPage->add(""); - $oPage->add(""); - $oPage->add(""); + $oPage->add(""); + $oPage->add(""); $oPage->add("
    "); } From 23afe7525eac21ed7e10945534f537b4d03b207d Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Sat, 22 Aug 2009 09:48:03 +0000 Subject: [PATCH 021/227] Make sure that we catch exceptions that may occur during the setup. SVN:trunk[96] --- setup/index.php | 99 +++++++++++++++++++++++++++---------------------- 1 file changed, 55 insertions(+), 44 deletions(-) diff --git a/setup/index.php b/setup/index.php index 20a5472fb..12b255c38 100644 --- a/setup/index.php +++ b/setup/index.php @@ -484,53 +484,64 @@ catch(Exception $e) // We'll end here when the tmp config file does not exist. It's normal $oConfig = new Config(TMP_CONFIG_FILE, false /* Don't try to load it */); } -switch($sOperation) +try { - case 'step1': - $oP->log("Info - ========= Wizard step 1 ========"); - DisplayStep1($oP); - break; - - case 'step2': - $oP->no_cache(); - $oP->log("Info - ========= Wizard step 2 ========"); - $sDBServer = Utils::ReadParam('db_server'); - $sDBUser = Utils::ReadParam('db_user'); - $sDBPwd = Utils::ReadParam('db_pwd'); - DisplayStep2($oP, $oConfig, $sDBServer, $sDBUser, $sDBPwd); - break; - - case 'step3': - $oP->no_cache(); - $oP->log("Info - ========= Wizard step 3 ========"); - $sDBName = Utils::ReadParam('db_name'); - if (empty($sDBName)) + switch($sOperation) { - $sDBName = Utils::ReadParam('new_db_name'); - } - $sDBPrefix = Utils::ReadParam('db_prefix'); - DisplayStep3($oP, $oConfig, $sDBName, $sDBPrefix); - break; - - case 'step4': - $oP->no_cache(); - $oP->log("Info - ========= Wizard step 4 ========"); - $sAdminUser = Utils::ReadParam('auth_user'); - $sAdminPwd = Utils::ReadParam('auth_pwd'); - DisplayStep4($oP, $oConfig, $sAdminUser, $sAdminPwd); - break; - - case 'step5': - $oP->no_cache(); - $oP->log("Info - ========= Wizard step 5 ========"); - $sAdminUser = Utils::ReadParam('auth_user'); - $sAdminPwd = Utils::ReadParam('auth_pwd'); - DisplayStep5($oP, $oConfig, $sAdminUser, $sAdminPwd); - break; - - default: - $oP->error("Error: unsupported operation '$sOperation'"); + case 'step1': + $oP->log("Info - ========= Wizard step 1 ========"); + DisplayStep1($oP); + break; + + case 'step2': + $oP->no_cache(); + $oP->log("Info - ========= Wizard step 2 ========"); + $sDBServer = Utils::ReadParam('db_server'); + $sDBUser = Utils::ReadParam('db_user'); + $sDBPwd = Utils::ReadParam('db_pwd'); + DisplayStep2($oP, $oConfig, $sDBServer, $sDBUser, $sDBPwd); + break; + case 'step3': + $oP->no_cache(); + $oP->log("Info - ========= Wizard step 3 ========"); + $sDBName = Utils::ReadParam('db_name'); + if (empty($sDBName)) + { + $sDBName = Utils::ReadParam('new_db_name'); + } + $sDBPrefix = Utils::ReadParam('db_prefix'); + DisplayStep3($oP, $oConfig, $sDBName, $sDBPrefix); + break; + + case 'step4': + $oP->no_cache(); + $oP->log("Info - ========= Wizard step 4 ========"); + $sAdminUser = Utils::ReadParam('auth_user'); + $sAdminPwd = Utils::ReadParam('auth_pwd'); + DisplayStep4($oP, $oConfig, $sAdminUser, $sAdminPwd); + break; + + case 'step5': + $oP->no_cache(); + $oP->log("Info - ========= Wizard step 5 ========"); + $sAdminUser = Utils::ReadParam('auth_user'); + $sAdminPwd = Utils::ReadParam('auth_pwd'); + DisplayStep5($oP, $oConfig, $sAdminUser, $sAdminPwd); + break; + + default: + $oP->error("Error: unsupported operation '$sOperation'"); + + } +} +catch(Exception $e) +{ + $oP->error("Error: '".$e->getMessage()."'"); +} +catch(CoreException $e) +{ + $oP->error("Error: '".$e->getHtmlDesc()."'"); } $oP->output(); ?> From ef27d068effd513225787b6f30f22720260d3e07 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Sat, 22 Aug 2009 09:49:13 +0000 Subject: [PATCH 022/227] Do NOT use PHP short open tags for a better compatibility SVN:trunk[97] --- core/oql/oqlinterpreter.class.inc.php | 2 +- core/oql/oqlquery.class.inc.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/oql/oqlinterpreter.class.inc.php b/core/oql/oqlinterpreter.class.inc.php index ac72a12d9..ef55d0309 100644 --- a/core/oql/oqlinterpreter.class.inc.php +++ b/core/oql/oqlinterpreter.class.inc.php @@ -1,4 +1,4 @@ - Date: Sat, 22 Aug 2009 09:49:44 +0000 Subject: [PATCH 023/227] Do NOT use PHP short open tags for a better compatibility SVN:trunk[98] --- application/startup.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/startup.inc.php b/application/startup.inc.php index 83ea90c2a..7f3448f30 100644 --- a/application/startup.inc.php +++ b/application/startup.inc.php @@ -1,4 +1,4 @@ - Date: Sat, 22 Aug 2009 09:52:12 +0000 Subject: [PATCH 024/227] - Do NOT rely on PHP short open tags, for a better compatibility - In case of exception, display the whole error string even if it contains HTML tags, sometimes ugly but sometimes VERY useful SVN:trunk[99] --- core/config.class.inc.php | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/core/config.class.inc.php b/core/config.class.inc.php index c18c716ff..ca2d4a5e9 100644 --- a/core/config.class.inc.php +++ b/core/config.class.inc.php @@ -70,8 +70,7 @@ class Config try { ob_start(); - $sCode = str_replace('<'.'?php','<'.'?', $sConfigCode); - eval('?'.'>'.trim($sCode).'<'.'?'); + eval('?'.'>'.trim($sConfigCode)); $sNoise = trim(ob_get_contents()); ob_end_clean(); } @@ -84,7 +83,7 @@ class Config if (strlen($sNoise) > 0) { // Note: sNoise is an html output, but so far it was ok for me (e.g. showing the entire call stack) - throw new ConfigException('Syntax error in configuration file', array('file' => $sConfigFile, 'error' => $sNoise)); + throw new ConfigException('Syntax error in configuration file', array('file' => $sConfigFile, 'error' => ''.htmlentities($sNoise).'')); } if (!isset($MySettings) || !is_array($MySettings)) @@ -235,7 +234,7 @@ class Config fwrite($hFile, "\$MySettings = array(\n"); fwrite($hFile, "\t'db_host' => '{$this->m_sDBHost}',\n"); fwrite($hFile, "\t'db_user' => '{$this->m_sDBUser}',\n"); - fwrite($hFile, "\t'db_pwd' => '{$this->m_sDBPwd}',\n"); + fwrite($hFile, "\t'db_pwd' => '".addslashes($this->m_sDBPwd)."',\n"); fwrite($hFile, "\t'db_name' => '{$this->m_sDBName}',\n"); fwrite($hFile, "\t'db_subname' => '{$this->m_sDBSubname}',\n"); fwrite($hFile, ");\n"); From bed32711d69a634d8df9987fcd97157bdd112b4b Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Mon, 24 Aug 2009 16:40:11 +0000 Subject: [PATCH 025/227] Improved the data load during the setup: - Sample data are clearly separated from the structure data (inc. menus and user profiles) - Both categories of data are enumerated from specific folders SVN:trunk[100] --- setup/data/export.cmd | 8 -- .../{menus.xml => data/structure/1.menus.xml} | 0 .../10.dimensions.xml} | 0 .../11.profiles.xml} | 0 .../12.classprojection.xml} | 0 .../13.profileprojection.xml} | 0 .../14.actiongrant.xml} | 0 .../15.attributegrant.xml} | 0 .../16.stimulusgrant.xml} | 0 setup/{ => data/structure}/export_menus.cmd | 5 +- setup/data/structure/export_profiles.cmd | 13 +++ setup/index.php | 84 ++++++++++++------- setup/setup.js | 9 +- 13 files changed, 72 insertions(+), 47 deletions(-) rename setup/{menus.xml => data/structure/1.menus.xml} (100%) rename setup/data/{23.dimensions.xml => structure/10.dimensions.xml} (100%) rename setup/data/{24.profiles.xml => structure/11.profiles.xml} (100%) rename setup/data/{25.classprojection.xml => structure/12.classprojection.xml} (100%) rename setup/data/{26.profileprojection.xml => structure/13.profileprojection.xml} (100%) rename setup/data/{27.actiongrant.xml => structure/14.actiongrant.xml} (100%) rename setup/data/{28.attributegrant.xml => structure/15.attributegrant.xml} (100%) rename setup/data/{29.stimulusgrant.xml => structure/16.stimulusgrant.xml} (100%) rename setup/{ => data/structure}/export_menus.cmd (90%) create mode 100644 setup/data/structure/export_profiles.cmd diff --git a/setup/data/export.cmd b/setup/data/export.cmd index 923d30177..d7200a315 100644 --- a/setup/data/export.cmd +++ b/setup/data/export.cmd @@ -26,11 +26,3 @@ wget --output-document=19.infracontracts.xml --post-data="auth_user=%USER%&auth_ wget --output-document=20.contactcontracts.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT lnkContactContract&format=xml" wget --output-document=21.auditcategories.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT AuditCategory&format=xml" wget --output-document=22.auditrules.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT AuditRule&format=xml" -wget --output-document=23.dimensions.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT URP_Dimensions&format=xml" -wget --output-document=24.profiles.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT URP_Profiles&format=xml" -wget --output-document=25.classprojection.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT URP_ClassProjection&format=xml" -wget --output-document=26.profileprojection.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT URP_ProfileProjection&format=xml" -wget --output-document=27.actiongrant.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT URP_ActionGrant&format=xml" -wget --output-document=28.attributegrant.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT URP_AttributeGrant&format=xml" -wget --output-document=29.stimulusgrant.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT URP_StimulusGrant&format=xml" -pause diff --git a/setup/menus.xml b/setup/data/structure/1.menus.xml similarity index 100% rename from setup/menus.xml rename to setup/data/structure/1.menus.xml diff --git a/setup/data/23.dimensions.xml b/setup/data/structure/10.dimensions.xml similarity index 100% rename from setup/data/23.dimensions.xml rename to setup/data/structure/10.dimensions.xml diff --git a/setup/data/24.profiles.xml b/setup/data/structure/11.profiles.xml similarity index 100% rename from setup/data/24.profiles.xml rename to setup/data/structure/11.profiles.xml diff --git a/setup/data/25.classprojection.xml b/setup/data/structure/12.classprojection.xml similarity index 100% rename from setup/data/25.classprojection.xml rename to setup/data/structure/12.classprojection.xml diff --git a/setup/data/26.profileprojection.xml b/setup/data/structure/13.profileprojection.xml similarity index 100% rename from setup/data/26.profileprojection.xml rename to setup/data/structure/13.profileprojection.xml diff --git a/setup/data/27.actiongrant.xml b/setup/data/structure/14.actiongrant.xml similarity index 100% rename from setup/data/27.actiongrant.xml rename to setup/data/structure/14.actiongrant.xml diff --git a/setup/data/28.attributegrant.xml b/setup/data/structure/15.attributegrant.xml similarity index 100% rename from setup/data/28.attributegrant.xml rename to setup/data/structure/15.attributegrant.xml diff --git a/setup/data/29.stimulusgrant.xml b/setup/data/structure/16.stimulusgrant.xml similarity index 100% rename from setup/data/29.stimulusgrant.xml rename to setup/data/structure/16.stimulusgrant.xml diff --git a/setup/export_menus.cmd b/setup/data/structure/export_menus.cmd similarity index 90% rename from setup/export_menus.cmd rename to setup/data/structure/export_menus.cmd index e7cc514af..2370101e8 100644 --- a/setup/export_menus.cmd +++ b/setup/data/structure/export_menus.cmd @@ -1,6 +1,7 @@ SET WEBROOT=http://localhost:81 -SET USER=Erwan -SET PWD=Taloc +SET USER=admin +SET PWD=admin + REM The order (numbering) of the files is important since REM it dictates the order to import them back wget --output-document=1.menus.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%WEBROOT%/pages/export.php?expression=SELECT menuNode WHERE type%%3D%%27application%%27&format=xml" diff --git a/setup/data/structure/export_profiles.cmd b/setup/data/structure/export_profiles.cmd new file mode 100644 index 000000000..3d2ea0aec --- /dev/null +++ b/setup/data/structure/export_profiles.cmd @@ -0,0 +1,13 @@ +SET WEBROOT=http://localhost:81 +SET USER=admin +SET PWD=admin + +REM The order (numbering) of the files is important since +REM it dictates the order to import them back +wget --output-document=10.dimensions.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT URP_Dimensions&format=xml" +wget --output-document=11.profiles.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT URP_Profiles&format=xml" +wget --output-document=12.classprojection.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT URP_ClassProjection&format=xml" +wget --output-document=13.profileprojection.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT URP_ProfileProjection&format=xml" +wget --output-document=14.actiongrant.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT URP_ActionGrant&format=xml" +wget --output-document=15.attributegrant.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT URP_AttributeGrant&format=xml" +wget --output-document=16.stimulusgrant.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT URP_StimulusGrant&format=xml" diff --git a/setup/index.php b/setup/index.php index 12b255c38..0696dfd18 100644 --- a/setup/index.php +++ b/setup/index.php @@ -8,7 +8,8 @@ require_once('../core/cmdbsource.class.inc.php'); require_once('./setuppage.class.inc.php'); define('TMP_CONFIG_FILE', '../tmp-config-itop.php'); define('FINAL_CONFIG_FILE', '../config-itop.php'); -define('SETUP_DATA_DIR', './data'); +define('SETUP_STRUCTURE_DATA_DIR', './data/structure'); +define('SETUP_SAMPLE_DATA_DIR', './data'); define('PHP_MIN_VERSION', '5.2.0'); define('MYSQL_MIN_VERSION', '5.0.0'); @@ -173,45 +174,66 @@ function CreateAdminAccount(setup_web_page $oP, Config $oConfig, $sAdminUser, $s } } +//aFilesToLoad[aFilesToLoad.length] = './menus.xml'; // First load the menus + +function ListDataFiles($sDirectory, setup_web_page $oP) +{ + $aFilesToLoad = array(); + if ($hDir = @opendir($sDirectory)) + { + // This is the correct way to loop over the directory. (according to the documentation) + while (($sFile = readdir($hDir)) !== false) + { + $sExtension = pathinfo($sFile, PATHINFO_EXTENSION ); + if (strcasecmp($sExtension, 'xml') == 0) + { + $aFilesToLoad[] = $sDirectory.'/'.$sFile; + } + } + closedir($hDir); + // Load order is important we expect the files to be ordered + // like numbered 1.Organizations.xml 2.Locations.xml , etc. + asort($aFilesToLoad); + } + else + { + $oP->error("Data directory (".$sDirectory.") not found or not readable."); + } + return $aFilesToLoad; +} + + /** * Scans the ./data directory for XML files and output them as a Javascript array */ function PopulateDataFilesList(setup_web_page $oP) { - if ($hDir = @opendir(SETUP_DATA_DIR)) + + $oP->add("\n"); + $oP->add("aFilesToLoad[aFilesToLoad.length] = '$sFile';\n"); } - else + + // Sample data - loaded IIF wished by the user + // + $oP->add("if (($(\"#sample_data:checked\").length == 1))"); + $oP->add("{"); + $aSampleDataFiles = ListDataFiles(SETUP_SAMPLE_DATA_DIR, $oP); + foreach($aSampleDataFiles as $sFile) { - $oP->error("Data directory (".SETUP_DATA_DIR.") no found or not readable."); + $oP->add("aFilesToLoad[aFilesToLoad.length] = '$sFile';\n"); } + $oP->add("}\n"); + + $oP->add("}\n"); + $oP->add("\n"); } /** diff --git a/setup/setup.js b/setup/setup.js index f407a199d..8c9e46008 100644 --- a/setup/setup.js +++ b/setup/setup.js @@ -89,12 +89,9 @@ var aFilesToLoad = new Array(); function DoLoadDataAsynchronous() { - // Check if sample data must be loaded, or just the menus - aFilesToLoad[aFilesToLoad.length] = './menus.xml'; // First load the menus - if (($("#sample_data:checked").length == 1)) - { - PopulateDataFilesList(); // Function created in PHP to get the list of XML files on the server - } + // The array aFilesToLoad is populated by this function dynamically written on the server + PopulateDataFilesList(); + $('#setup').block({message: '

    Loading data...

    0%

    '}); $('#progress').progression( {Current:0, Maximum: 100, aBackgroundImg: 'orange-progress.gif', aTextColor: '#000000'} ); LoadNextDataFile('', ''); From 802e06888a45fed6218af303e51f22ec916eb870 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Tue, 25 Aug 2009 13:52:13 +0000 Subject: [PATCH 026/227] New helper function IsModified SVN:trunk[101] --- core/dbobject.class.php | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/core/dbobject.class.php b/core/dbobject.class.php index c9d69a0b9..9a52b562d 100644 --- a/core/dbobject.class.php +++ b/core/dbobject.class.php @@ -533,6 +533,13 @@ abstract class DBObject return $this->ListChangedValues($this->m_aCurrValues); } + // Tells whether or not an object was modified + public function IsModified() + { + $aChanges = $this->ListChanges(); + return (count($aChanges) != 0); + } + // used both by insert/update private function DBWriteLinks() { From 78e5b79893f5031dbd88162dc255e99fa4b98160 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Tue, 25 Aug 2009 19:48:37 +0000 Subject: [PATCH 027/227] Fixed the disappearance of tabs when doing a 'reload' SVN:trunk[102] --- application/itopwebpage.class.inc.php | 27 +++++++++++++++------------ 1 file changed, 15 insertions(+), 12 deletions(-) diff --git a/application/itopwebpage.class.inc.php b/application/itopwebpage.class.inc.php index 0b69f6669..b606a68a7 100644 --- a/application/itopwebpage.class.inc.php +++ b/application/itopwebpage.class.inc.php @@ -91,6 +91,7 @@ class iTopWebPage extends nice_web_page $(".listResults").tablesorter( { headers: { 0:{sorter: false }}, widgets: ['zebra']} ); // sortable and zebra tables $(".date-pick").datePicker( {clickInput: false, createButton: true, startDate: '2000-01-01'} ); // Date picker $('#ModalDlg').jqm({ajax: '@href', trigger: 'a.jqmTrigger', overlay:70, modal:true, toTop:true}); // jqModal Window + //$('.display_block').draggable(); // make the blocks draggable EOF ); @@ -198,6 +199,20 @@ EOF echo "\n"; echo "\n"; echo "{$this->s_title}\n"; + // Stylesheets MUST be loaded before any scripts otherwise + // jQuery scripts may face some spurious problems (like failing on a 'reload') + foreach($this->a_linked_stylesheets as $a_stylesheet) + { + if ($a_stylesheet['condition'] != "") + { + echo "\n"; + } + } foreach($this->a_linked_scripts as $s_script) { echo "\n"; @@ -215,18 +230,6 @@ EOF } echo "\n"; } - foreach($this->a_linked_stylesheets as $a_stylesheet) - { - if ($a_stylesheet['condition'] != "") - { - echo "\n"; - } - } if (count($this->a_styles)>0) { From 3c3a1967b6995a6ed03613c0c854461fc81f3431 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Wed, 26 Aug 2009 21:32:32 +0000 Subject: [PATCH 028/227] Fixed a missing display template SVN:trunk[103] --- business/incident.business.php | 1 + 1 file changed, 1 insertion(+) diff --git a/business/incident.business.php b/business/incident.business.php index c41d7dd6b..5a130959a 100644 --- a/business/incident.business.php +++ b/business/incident.business.php @@ -186,6 +186,7 @@ class lnkRelatedTicket extends cmdbAbstractObject "db_table" => "related_ticket", "db_key_field" => "link_id", "db_finalclass_field" => "", + "display_template" => "../business/templates/default.html", ); MetaModel::Init_Params($aParams); MetaModel::Init_AddAttribute(new AttributeExternalKey("rel_ticket_id", array("targetclass"=>"bizIncidentTicket", "jointype"=> '', "label"=>"Related Ticket id", "description"=>"The related ticket", "allowed_values"=>null, "sql"=>"rel_ticket_id", "is_null_allowed"=>false, "depends_on"=>array()))); From e0b307ef488865517308c1d53f3fa9883c13b08c Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Wed, 26 Aug 2009 21:52:34 +0000 Subject: [PATCH 029/227] - New user interface to manage n-n links SVN:trunk[104] --- application/application.inc.php | 4 + application/cmdbabstract.class.inc.php | 73 ++++- application/displayblock.class.inc.php | 89 ++++-- application/template.class.inc.php | 19 +- application/ui.linkswidget.class.inc.php | 6 +- application/uilinkswizard.class.inc.php | 380 +++++++++++++++++++++++ business/templates/server.html | 4 +- business/templates/team.html | 2 +- business/templates/ticket.html | 2 +- pages/UI.php | 119 ++++++- pages/ajax.render.php | 34 ++ 11 files changed, 688 insertions(+), 44 deletions(-) create mode 100644 application/uilinkswizard.class.inc.php diff --git a/application/application.inc.php b/application/application.inc.php index 86f381396..d15464c19 100644 --- a/application/application.inc.php +++ b/application/application.inc.php @@ -9,4 +9,8 @@ require_once('../application/audit.category.class.inc.php'); require_once('../application/audit.rule.class.inc.php'); //require_once('../application/menunode.class.inc.php'); require_once('../application/utils.inc.php'); + +class ApplicationException extends CoreException +{ +} ?> diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index f95a03eab..59dd5a48b 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -51,6 +51,12 @@ abstract class cmdbAbstractObject extends CMDBObject $sHint = htmlentities("$sObjClass::$sObjKey"); return "GetForLink()."\" title=\"$sHint\">$sLabel"; } + + public function GetHyperlink() + { + $aAvailableFields[MetaModel::GetNameAttributeCode(get_class($this))] = $this->GetName(); + return $this->MakeHyperLink(get_class($this), $this->GetKey(), $aAvailableFields); + } public function GetDisplayValue($sAttCode) { @@ -213,13 +219,37 @@ abstract class cmdbAbstractObject extends CMDBObject // Comment by Rom: this helper may be used to display objects of class DBObject // -> I am using this to display the changes history - public static function DisplaySet(web_page $oPage, CMDBObjectSet $oSet, $sLinkageAttribute = '', $bDisplayMenu = true) + public static function DisplaySet(web_page $oPage, CMDBObjectSet $oSet, $sLinkageAttribute = '', $bDisplayMenu = true, $bSelectMode = false, $iObjectId = 0) { - $oPage->add(self::GetDisplaySet($oPage, $oSet, $sLinkageAttribute, $bDisplayMenu)); + $oPage->add(self::GetDisplaySet($oPage, $oSet, array( 'link_attr' => $sLinkageAttribute, 'object_id' => $iObjectId, 'menu' => $bDisplayMenu, 'selection_mode' => $bSelectMode))); } - public static function GetDisplaySet(web_page $oPage, CMDBObjectSet $oSet, $sLinkageAttribute = '', $bDisplayMenu = true) + //public static function GetDisplaySet(web_page $oPage, CMDBObjectSet $oSet, $sLinkageAttribute = '', $bDisplayMenu = true, $bSelectMode = false) + public static function GetDisplaySet(web_page $oPage, CMDBObjectSet $oSet, $aExtraParams = array()) { + static $iListId = 0; + $iListId++; + + // Initialize and check the parameters + $sLinkageAttribute = isset($aExtraParams['link_attr']) ? $aExtraParams['link_attr'] : ''; + $iLinkedObjectId = isset($aExtraParams['object_id']) ? $aExtraParams['object_id'] : 0; + $sTargetAttr = isset($aExtraParams['target_attr']) ? $aExtraParams['target_attr'] : ''; + if (!empty($sLinkageAttribute)) + { + if($iLinkedObjectId == 0) + { + // if 'links' mode is requested the d of the object to link to must be specified + throw new ApplicationException("Parameter object_id is mandatory when link_attr is specified. Check the definition of the display template."); + } + if($sTargetAttr == '') + { + // if 'links' mode is requested the d of the object to link to must be specified + throw new ApplicationException("Parameter target_attr is mandatory when link_attr is specified. Check the definition of the display template."); + } + } + $bDisplayMenu = isset($aExtraParams['menu']) ? $aExtraParams['menu'] == true : true; + $bSelectMode = isset($aExtraParams['selection_mode']) ? $aExtraParams['selection_mode'] == true : false; + $sHtml = ''; $oAppContext = new ApplicationContext(); $sClassName = $oSet->GetFilter()->GetClass(); @@ -260,6 +290,10 @@ abstract class cmdbAbstractObject extends CMDBObject } foreach($aList as $sAttCode) { + if ($bSelectMode) + { + $aAttribs['form::select'] = array('label' => "", 'description' => 'Select / Deselect All'); + } $aAttribs['key'] = array('label' => '', 'description' => 'Click to display'); $aAttribs[$sAttCode] = array('label' => MetaModel::GetLabel($sClassName, $sAttCode), 'description' => MetaModel::GetDescription($sClassName, $sAttCode)); } @@ -267,6 +301,11 @@ abstract class cmdbAbstractObject extends CMDBObject $oSet->Seek(0); while ($oObj = $oSet->Fetch()) { + $aRow['key'] = $oObj->GetKey(); + if ($bSelectMode) + { + $aRow['form::select'] = "GetKey()."\">"; + } $aRow['key'] = $oObj->GetKey(); foreach($aList as $sAttCode) { @@ -280,8 +319,14 @@ abstract class cmdbAbstractObject extends CMDBObject if ($bDisplayMenu) { $sColspan = 'colspan="2"'; + $aMenuExtraParams = array(); + if (!empty($sLinkageAttribute)) + { + //$aMenuExtraParams['linkage'] = $sLinkageAttribute; + $aMenuExtraParams = $aExtraParams; + } $sHtml .= ' '.$oSet->Count().' object(s)'; - $sHtml .= $oMenuBlock->GetRenderContent($oPage, $sLinkageAttribute); + $sHtml .= $oMenuBlock->GetRenderContent($oPage, $aMenuExtraParams); $sHtml .= ''; } $sHtml .= ""; @@ -400,7 +445,7 @@ abstract class cmdbAbstractObject extends CMDBObject $sHtml .= "
    \n"; $sHtml .= "

    Search for ".MetaModel::GetName($sClassName)." Objects

    \n"; $oUnlimitedFilter = new DBObjectSearch($sClassName); - $sHtml .= "
    \n"; + $sHtml .= "\n"; $index = 0; $sHtml .= "\n"; $aFilterCriteria = $oSet->GetFilter()->GetCriteria(); @@ -488,7 +533,7 @@ abstract class cmdbAbstractObject extends CMDBObject // OQL query builder $sHtml .= "
    \n"; $sHtml .= "

    OQL Query Builder

    \n"; - $sHtml .= "
    \n"; + $sHtml .= "
    \n"; $sHtml .= "\n"); + foreach($aConfig as $sName=>$void) + { + $oP->add("\n"); + } + $oP->add("\n"); + } + + public function DisplayAddForm(web_page $oP, UserContext $oContext) + { + $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sLinkageAttr); + $sTargetClass = $oAttDef->GetTargetClass(); + $oTargetObj = $oContext->GetObject($sTargetClass, $this->m_iObjectId); + $oP->add("
    \n"); + $oP->add("
    \n"); + $oP->add("

    Add ".MetaModel::GetName($this->m_sLinkedClass)."s to ".MetaModel::GetName(get_class($oTargetObj)).": ".$oTargetObj->GetHyperlink()."

    \n"); + $oP->add("
    \n"); + + $oFilter = $oContext->NewFilter($this->m_sLinkedClass); + $oSet = new CMDBObjectSet($oFilter); + $oBlock = new DisplayBlock($oFilter, 'search', false); + $oBlock->Display($oP, 'SearchFormToAdd', array('open' => true)); + $oP->Add("
    \n"); + $oP->Add("
    \n"); + $oP->Add("

    Use the search form above to search for objects to be added.

    \n"); + $oP->Add("
    \n"); + $oP->add("  "); + $oP->Add("
    \n"); + $oP->Add("\n"); + $oP->add_ready_script("$('div#SearchFormToAdd form').attr('onSubmit', 'var the_form = this; return SearchObjectsToAdd(the_form.id);');"); + } + + public function SearchObjectsToAdd(web_page $oP, UserContext $oContext) + { + $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sLinkageAttr); + + $oFilter = $oContext->NewFilter($this->m_sLinkedClass); + $oSet = new CMDBObjectSet($oFilter); + $oBlock = new DisplayBlock($oFilter, 'list', false); + $oBlock->Display($oP, 'ResultsToAdd', array('menu' => false, 'selection_mode' => true)); // Don't display the 'Actions' menu on the results + } + + public function DoAddObjects(web_page $oP, UserContext $oContext, $aLinkedObjectIds = array()) + { + //$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sLinkageAttr); + //$sTargetClass = $oAttDef->GetTargetClass(); + //$oP->Add("\n"); // Just to make sure it's not empty + $aTable = array(); + foreach($aLinkedObjectIds as $iObjectId) + { + $oLinkedObj = $oContext->GetObject($this->m_sLinkedClass, $iObjectId); + if (is_object($oLinkedObj)) + { + $aRow = $this->GetFormRow($oP, $oLinkedObj, -$iObjectId ); // Not yet created link get negative Ids + $this->DisplayFormRow($oP, $this->m_aTableConfig, $aRow, -$iObjectId); + } + else + { + echo "Object: $sTargetClass - Id: $iObjectId not found
    \n"; + } + } + //var_dump($aTable); + //$oP->Add("\n"); // Just to make sure it's not empty + } +} +?> diff --git a/business/templates/server.html b/business/templates/server.html index 8f72fd878..1989ac935 100644 --- a/business/templates/server.html +++ b/business/templates/server.html @@ -16,10 +16,10 @@ SELECT bizInterface WHERE device_id = $pkey$ - lnkContactRealObject: object_id = $pkey$ + lnkContactRealObject: object_id = $pkey$ - bizIncidentTicket: PKEY IS ticket_id IN (lnkInfraTicket: infra_id = $pkey$) + lnkInfraTicket: infra_id = $pkey$ bizChangeTicket: PKEY IS ticket_id IN (lnkInfraChangeTicket: infra_id = $pkey$) diff --git a/business/templates/team.html b/business/templates/team.html index 12537bdd5..a9601ee03 100644 --- a/business/templates/team.html +++ b/business/templates/team.html @@ -7,7 +7,7 @@ $class$: pkey = $pkey$ - bizContact: PKEY IS object_id IN (lnkContactRealObject: contact_id = $pkey$) + SELECT lnkContactRealObject WHERE contact_id=$pkey$ bizTeam: PKEY IS object_id IN (lnkContactRealObject: contact_id = $pkey$) diff --git a/business/templates/ticket.html b/business/templates/ticket.html index efce97e2d..b1c0ae548 100644 --- a/business/templates/ticket.html +++ b/business/templates/ticket.html @@ -10,7 +10,7 @@ lnkInfraTicket: ticket_id = $pkey$ - lnkRelatedTicket: ticket_id = $pkey$ + lnkRelatedTicket: ticket_id = $pkey$ lnkContactTicket: ticket_id = $pkey$ diff --git a/pages/UI.php b/pages/UI.php index 45631dd69..338fdf406 100644 --- a/pages/UI.php +++ b/pages/UI.php @@ -335,7 +335,7 @@ switch($operation) } if (!is_object($oObj)) { - // new object or or that can't be retrieved (corrupted id or object not allowed to this user) + // new object or that can't be retrieved (corrupted id or object not allowed to this user) $id = ''; $oObj = MetaModel::NewObject($sClass); } @@ -701,6 +701,123 @@ switch($operation) } break; + case 'modify_links': + $sClass = utils::ReadParam('class', ''); + $sLinkAttr = utils::ReadParam('link_attr', ''); + $sTargetClass = utils::ReadParam('target_class', ''); + $id = utils::ReadParam('id', ''); + $bAddObjects = utils::ReadParam('addObjects', false); + if ( empty($sClass) || empty($id) || empty($sLinkAttr) || empty($sTargetClass)) // TO DO: check that the class name is valid ! + { + $oP->set_title("iTop - Error"); + $oP->add("

    4 parameters are mandatory for this operation: class, id, target_class and link_attr.

    \n"); + } + else + { + require_once('../application/uilinkswizard.class.inc.php'); + $oWizard = new UILinksWizard($sClass, $sLinkAttr, $id, $sTargetClass); + $oWizard->Display($oP, $oContext, array('StartWithAdd' => $bAddObjects)); + } + break; + + case 'do_modify_links': + $aLinks = utils::ReadParam('linkId', array(), 'post'); + $sLinksToRemove = trim(utils::ReadParam('linksToRemove', '', 'post')); + $aLinksToRemove = array(); + if (!empty($sLinksToRemove)) + { + $aLinksToRemove = explode(' ', trim($sLinksToRemove)); + } + $sClass = utils::ReadParam('class', '', 'post'); + $sLinkageAtt = utils::ReadParam('linkage', '', 'post'); + $iObjectId = utils::ReadParam('object_id', '', 'post'); + $sLinkingAttCode = utils::ReadParam('linking_attcode', '', 'post'); + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + if (UserRights::GetUser() != UserRights::GetRealUser()) + { + $sUserString = UserRights::GetRealUser()." on behalf of ".UserRights::GetUser(); + } + else + { + $sUserString = UserRights::GetUser(); + } + $oMyChange->Set("userinfo", $sUserString); + $iChangeId = $oMyChange->DBInsert(); + + // Delete links that are to be deleted + foreach($aLinksToRemove as $iLinkId) + { + if ($iLinkId > 0) // Negative IDs are objects that were not even created + { + $oLink = $oContext->GetObject($sClass, $iLinkId); + $oLink->DBDeleteTracked($oMyChange); + } + } + + $aEditableFields = array(); + $aData = array(); + foreach(MetaModel::GetAttributesList($sClass) as $sAttCode) + { + $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode); + if ( (!$oAttDef->IsExternalKey()) && (!$oAttDef->IsExternalField())) + { + $aEditableFields[] = $sAttCode; + $aData[$sAttCode] = utils::ReadParam('attr_'.$sAttCode, array(), 'post'); + } + } + + // Update existing links or create new links + foreach($aLinks as $iLinkId) + { + if ($iLinkId > 0) + { + // This is an existing link to be modified + $oLink = $oContext->GetObject($sClass, $iLinkId); + + // Update all the attributes of the link + foreach($aEditableFields as $sAttCode) + { + $value = $aData[$sAttCode][$iLinkId]; + $oLink->Set($sAttCode, $value); + } + if ($oLink->IsModified()) + { + $oLink->DBUpdateTracked($oMyChange); + } + //echo "Updated link:
    \n"; + //var_dump($oLink); + } + else + { + // A new link must be created + $oLink = MetaModel::NewObject($sClass); + $oLinkedObjectId = -$iLinkId; + // Set all the attributes of the link + foreach($aEditableFields as $sAttCode) + { + $value = $aData[$sAttCode][$iLinkId]; + $oLink->Set($sAttCode, $value); + } + // And the two external keys + $oLink->Set($sLinkageAtt, $iObjectId); + $oLink->Set($sLinkingAttCode, $oLinkedObjectId); + // then save it + //echo "Created link:
    \n"; + //var_dump($oLink); + $oLink->DBInsertTracked($oMyChange); + } + } + // Display again the details of the linked object + $oAttDef = MetaModel::GetAttributeDef($sClass, $sLinkageAtt); + $sTargetClass = $oAttDef->GetTargetClass(); + $oObj = $oContext->GetObject($sTargetClass, $iObjectId); + + $oSearch = $oContext->NewFilter(get_class($oObj)); + $oBlock = new DisplayBlock($oSearch, 'search', false); + $oBlock->Display($oP, 0); + $oObj->DisplayDetails($oP); + break; default: $oActiveNode->RenderContent($oP, $oAppContext->GetAsHash()); diff --git a/pages/ajax.render.php b/pages/ajax.render.php index 35af6a858..a90059eeb 100644 --- a/pages/ajax.render.php +++ b/pages/ajax.render.php @@ -32,6 +32,40 @@ $sStyle = utils::ReadParam('style', 'list'); switch($operation) { + case 'addObjects': + require_once('../application/uilinkswizard.class.inc.php'); + + $sClass = utils::ReadParam('class', '', 'get'); + $sLinkedClass = utils::ReadParam('linkedClass', '', 'get'); + $sLinkageAttr = utils::ReadParam('linkageAttr', '', 'get'); + $iObjectId = utils::ReadParam('objectId', '', 'get'); + $oLinksWizard = new UILinksWizard($sClass, $sLinkageAttr, $iObjectId, $sLinkedClass); + $oLinksWizard->DisplayAddForm($oPage, $oContext); + break; + + case 'searchObjectsToAdd': + require_once('../application/uilinkswizard.class.inc.php'); + + $sClass = utils::ReadParam('class', '', 'get'); + $sLinkedClass = utils::ReadParam('linkedClass', '', 'get'); + $sLinkageAttr = utils::ReadParam('linkageAttr', '', 'get'); + $iObjectId = utils::ReadParam('objectId', '', 'get'); + $oLinksWizard = new UILinksWizard($sClass, $sLinkageAttr, $iObjectId, $sLinkedClass); + $oLinksWizard->SearchObjectsToAdd($oPage, $oContext); + break; + + case 'doAddObjects': + require_once('../application/uilinkswizard.class.inc.php'); + + $sClass = utils::ReadParam('class', '', 'get'); + $sLinkedClass = utils::ReadParam('linkedClass', '', 'get'); + $sLinkageAttr = utils::ReadParam('linkageAttr', '', 'get'); + $iObjectId = utils::ReadParam('objectId', '', 'get'); + $aLinkedObjectIds = utils::ReadParam('selectObject', array(), 'get'); + $oLinksWizard = new UILinksWizard($sClass, $sLinkageAttr, $iObjectId, $sLinkedClass); + $oLinksWizard->DoAddObjects($oPage, $oContext, $aLinkedObjectIds); + break; + case 'wizard_helper_preview': $sJson = utils::ReadParam('json_obj', '', 'post'); $oWizardHelper = WizardHelper::FromJSON($sJson); From e3a402677f23c40299b238bf9a7aed2003255a79 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Thu, 27 Aug 2009 12:37:21 +0000 Subject: [PATCH 030/227] Workaround for the missing REQUEST_URI on IIS SVN:trunk[105] --- application/displayblock.class.inc.php | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/application/displayblock.class.inc.php b/application/displayblock.class.inc.php index 8eebeeae0..8d131427d 100644 --- a/application/displayblock.class.inc.php +++ b/application/displayblock.class.inc.php @@ -775,7 +775,22 @@ class MenuBlock extends DisplayBlock { $sPort = ($_SERVER['SERVER_PORT'] == 443) ? '' : ':'.$_SERVER['SERVER_PORT']; } - $sPath = $_SERVER['REQUEST_URI']; + // $_SERVER['REQUEST_URI'] is empty when running on IIS + // Let's use Ivan Tcholakov's fix (found on www.dokeos.com) + if (!empty($_SERVER['REQUEST_URI'])) + { + $sPath = $_SERVER['REQUEST_URI']; + } + else + { + $sPath = $_SERVER['SCRIPT_NAME']; + if (!empty($_SERVER['QUERY_STRING'])) + { + $sPath .= '?'.$_SERVER['QUERY_STRING']; + } + $_SERVER['REQUEST_URI'] = $sPath; + } + $sPath = $_SERVER['REQUEST_URI']; $sUrl = "$sProtocol://{$sServerName}{$sPort}{$sPath}"; return $sUrl; From e345b516c434e9b0efea25a90c9299d89ff32c06 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Thu, 27 Aug 2009 13:52:31 +0000 Subject: [PATCH 031/227] Fixed bug provoking a warning unseen so far but it was causing a fatal error during the installation, with IIS as web server SVN:trunk[106] --- core/cmdbsource.class.inc.php | 19 +++---------------- 1 file changed, 3 insertions(+), 16 deletions(-) diff --git a/core/cmdbsource.class.inc.php b/core/cmdbsource.class.inc.php index 2f76df24f..ea06e112d 100644 --- a/core/cmdbsource.class.inc.php +++ b/core/cmdbsource.class.inc.php @@ -168,23 +168,10 @@ class CMDBSource public static function Query($sSQLQuery) { // Add info into the query as a comment, for easier error tracking - // - //if ($user_contact) $aTraceInf['userID'] = $user_contact->get_key(); - //$aTraceInf['file'] = __FILE__; - if ($_SERVER['REQUEST_URI']) $aTraceInf['requestURI'] = $_SERVER['REQUEST_URI']; - $i = 0; - foreach(debug_backtrace() as $aCallData) - { - $sClass = key_exists("class", $aCallData) ? $aCallData["class"]."::" : ""; - //if ($aCallData['function'] !== 'mysql_simple_query' AND $sClass !== 'r2_set::') - //{ - if ($i == 3) break; - $aTraceInf['function'.$i] = $sClass.$aCallData["function"]." on line ".$aCallData['line']; - $i++; - //} - } // disabled until we need it really! - // $sSQLQuery = $sSQLQuery.MyHelpers::MakeSQLComment($aTraceInf); + // + //$aTraceInf['file'] = __FILE__; + // $sSQLQuery .= MyHelpers::MakeSQLComment($aTraceInf); $mu_t1 = MyHelpers::getmicrotime(); $result = mysql_query($sSQLQuery, self::$m_resDBLink); From 0900b5f6de388be1774d28beffbaa98500ed1c95 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Thu, 27 Aug 2009 13:56:57 +0000 Subject: [PATCH 032/227] Changed images paths into relative path (../images where it was still /images) SVN:trunk[107] --- application/itopwebpage.class.inc.php | 2 +- application/itopwizardwebpage.class.inc.php | 2 +- css/blue_green.css | 22 ++++++++++----------- css/jquery.treeview.css | 16 +++++++-------- css/light-grey.css | 8 ++++---- 5 files changed, 25 insertions(+), 25 deletions(-) diff --git a/application/itopwebpage.class.inc.php b/application/itopwebpage.class.inc.php index b606a68a7..b315d17f4 100644 --- a/application/itopwebpage.class.inc.php +++ b/application/itopwebpage.class.inc.php @@ -247,7 +247,7 @@ EOF // Display the header echo "
    \n"; echo "
    iTop
    \n"; - //echo "
    \n"; + //echo "
    \n"; $sText = Utils::ReadParam('text', ''); $sOnClick = ""; if (empty($sText)) diff --git a/application/itopwizardwebpage.class.inc.php b/application/itopwizardwebpage.class.inc.php index aadaf30c7..ba5655ae2 100644 --- a/application/itopwizardwebpage.class.inc.php +++ b/application/itopwizardwebpage.class.inc.php @@ -24,7 +24,7 @@ class iTopWizardWebPage extends iTopWebPage $sStyle = ($iIndex == $this->m_iCurrentStep) ? 'wizActiveStep' : 'wizStep'; $aSteps[] = "
    $sStepTitle
    "; } - $sWizardHeader = "

    {$this->s_title}

    \n".implode("
    ", $aSteps)."
    \n"; + $sWizardHeader = "

    {$this->s_title}

    \n".implode("
    ", $aSteps)."
    \n"; $this->s_content = "$sWizardHeader
    ".$this->s_content."
    "; parent::output(); } diff --git a/css/blue_green.css b/css/blue_green.css index 1e51162c3..18585c3cd 100644 --- a/css/blue_green.css +++ b/css/blue_green.css @@ -151,15 +151,15 @@ ul.dir li { } .treeview li { background: url(../images/tv-item.gif) 0 0 no-repeat; } -.treeview .collapsable { background-image: url(/images/tv-collapsable.gif); } -.treeview .expandable { background-image: url(/images/tv-expandable.gif); } -.treeview .last { background-image: url(/images/tv-item-last.gif); } -.treeview .lastCollapsable { background-image: url(/images/tv-collapsable-last.gif); } -.treeview .lastExpandable { background-image: url(/images/tv-expandable-last.gif); } +.treeview .collapsable { background-image: url(../images/tv-collapsable.gif); } +.treeview .expandable { background-image: url(../images/tv-expandable.gif); } +.treeview .last { background-image: url(../images/tv-item-last.gif); } +.treeview .lastCollapsable { background-image: url(../images/tv-collapsable-last.gif); } +.treeview .lastExpandable { background-image: url(../images/tv-expandable-last.gif); } -#Header { padding: 0; background:#ccc url(/images/bandeau2.gif) repeat-x center;} +#Header { padding: 0; background:#ccc url(../images/bandeau2.gif) repeat-x center;} div.iTopLogo { - background:url(/images/iTop.gif) no-repeat center; + background:url(../images/iTop.gif) no-repeat center; width:100px; height:56px; } @@ -208,15 +208,15 @@ div.iTopLogo span { #MySplitter .vsplitbar { width: 7px; height: 50px; - background: #68a url(/images/vgrabber2.gif) no-repeat center; + background: #68a url(../images/vgrabber2.gif) no-repeat center; } #MySplitter .vsplitbar.active, #MySplitter .vsplitbar:hover { - background: #68a url(/images/vgrabber2_active.gif) no-repeat center; + background: #68a url(../images/vgrabber2_active.gif) no-repeat center; } #MySplitter .hsplitbar { height: 8px; - background: #68a url(/images/hgrabber2.gif) no-repeat center; + background: #68a url(../images/hgrabber2.gif) no-repeat center; } #MySplitter .hsplitbar.active, #MySplitter .hsplitbar:hover { - background: #68a url(/images/hgrabber2_active.gif) no-repeat center; + background: #68a url(../images/hgrabber2_active.gif) no-repeat center; } diff --git a/css/jquery.treeview.css b/css/jquery.treeview.css index 6ae8d7087..dccad6c76 100644 --- a/css/jquery.treeview.css +++ b/css/jquery.treeview.css @@ -34,14 +34,14 @@ .treeview .hover { color: red; cursor: pointer; } -.treeview li { background: url(/images/tv-item.gif) 0 0 no-repeat; } -.treeview .collapsable { background-image: url(/images/tv-collapsable.gif); } -.treeview .expandable { background-image: url(/images/tv-expandable.gif); } -.treeview .last { background-image: url(/images/tv-item-last.gif); } -.treeview .lastCollapsable { background-image: url(/images/tv-collapsable-last.gif); } -.treeview .lastExpandable { background-image: url(/images/tv-expandable-last.gif); } +.treeview li { background: url(../images/tv-item.gif) 0 0 no-repeat; } +.treeview .collapsable { background-image: url(../images/tv-collapsable.gif); } +.treeview .expandable { background-image: url(../images/tv-expandable.gif); } +.treeview .last { background-image: url(../images/tv-item-last.gif); } +.treeview .lastCollapsable { background-image: url(../images/tv-collapsable-last.gif); } +.treeview .lastExpandable { background-image: url(../images/tv-expandable-last.gif); } .filetree li { padding: 3px 0 1px 16px; } .filetree span.folder, .filetree span.file { padding-left: 16px; display: block; height: 15px; } -.filetree span.folder { background: url(/images/folder.gif) 0 0 no-repeat; } -.filetree span.file { background: url(/images/file.gif) 0 0 no-repeat; } +.filetree span.folder { background: url(../images/folder.gif) 0 0 no-repeat; } +.filetree span.file { background: url(../images/file.gif) 0 0 no-repeat; } diff --git a/css/light-grey.css b/css/light-grey.css index 85717cb88..6bd833171 100644 --- a/css/light-grey.css +++ b/css/light-grey.css @@ -482,7 +482,7 @@ ul.jd_menu_itop ul li.jd_menu_hover a.jd_menu_hover { } div.wizActiveStep { height: 29px; - background: url(/images/wizActiveStepLeft.gif) no-repeat left; + background: url(../images/wizActiveStepLeft.gif) no-repeat left; color: #d81515; padding-left: 8px; margin-top: 5px; @@ -491,7 +491,7 @@ div.wizActiveStep { } div.wizActiveStep span { height: 29px; - background: url(/images/wizActiveStepRight.gif) no-repeat top right; + background: url(../images/wizActiveStepRight.gif) no-repeat top right; padding-right: 8px; padding-top: 8px; float:left; @@ -499,7 +499,7 @@ div.wizActiveStep span { div.wizStep { height: 29px; - background: url(/images/wizStepLeft.gif) no-repeat left; + background: url(../images/wizStepLeft.gif) no-repeat left; padding-left: 8px; vertical-align: middle; margin-top: 5px; @@ -507,7 +507,7 @@ div.wizStep { } div.wizStep span { height: 29px; - background: url(/images/wizStepRight.gif) no-repeat top right; + background: url(../images/wizStepRight.gif) no-repeat top right; padding-right: 8px; padding-top: 8px; float:left; From 5df39c5837ad33d85864a3e58ba9ed42b8dad299 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Wed, 2 Sep 2009 13:18:59 +0000 Subject: [PATCH 033/227] In the UI, the class names have been replaced by the class labels SVN:trunk[108] --- pages/UI.php | 38 +++++++++++++++++++++++--------------- pages/advanced_search.php | 2 +- 2 files changed, 24 insertions(+), 16 deletions(-) diff --git a/pages/UI.php b/pages/UI.php index 338fdf406..c8708213f 100644 --- a/pages/UI.php +++ b/pages/UI.php @@ -49,6 +49,7 @@ switch($operation) { case 'details': $sClass = utils::ReadParam('class', ''); + $sClassLabel = MetaModel::GetName($sClass); $id = utils::ReadParam('id', ''); $oSearch = new DBObjectSearch($sClass); $oBlock = new DisplayBlock($oSearch, 'search', false); @@ -62,7 +63,7 @@ switch($operation) $oObj = $oContext->GetObject($sClass, $id); if ($oObj != null) { - $oP->set_title("iTop - ".$oObj->GetDisplayName()." - $sClass details"); + $oP->set_title("iTop - ".$oObj->GetDisplayName()." - $sClassLabel details"); $oObj->DisplayDetails($oP); } else @@ -214,6 +215,7 @@ switch($operation) $oP->add_linked_script("../js/linkswidget.js"); $oP->add_linked_script("../js/jquery.blockUI.js"); $sClass = utils::ReadParam('class', ''); + $sClassLabel = MetaModel::GetName($sClass); $id = utils::ReadParam('id', ''); if ( empty($sClass) || empty($id)) // TO DO: check that the class name is valid ! { @@ -234,8 +236,8 @@ switch($operation) $bIsReadAllowed = (UserRights::IsActionAllowed($sClass, UR_ACTION_READ, $oSet) == UR_ALLOWED_YES); if( ($oObj != null) && ($bIsModifiedAllowed) && ($bIsReadAllowed)) { - $oP->set_title("iTop - ".$oObj->GetName()." - $sClass modification"); - $oP->add("

    ".$oObj->GetName()." - $sClass modification

    \n"); + $oP->set_title("iTop - ".$oObj->GetName()." - $sClassLabel modification"); + $oP->add("

    ".$oObj->GetName()." - $sClassLabel modification

    \n"); $oObj->DisplayModifyForm($oP); } else @@ -248,6 +250,7 @@ switch($operation) case 'clone': $sClass = utils::ReadParam('class', ''); + $sClassLabel = MetaModel::GetName($sClass); $id = utils::ReadParam('id', ''); if ( empty($sClass) || empty($id)) // TO DO: check that the class name is valid ! { @@ -268,8 +271,8 @@ switch($operation) $bIsReadAllowed = (UserRights::IsActionAllowed($sClass, UR_ACTION_READ, $oSet) == UR_ALLOWED_YES); if( ($oObjToClone != null) && ($bIsModifiedAllowed) && ($bIsReadAllowed)) { - $oP->set_title("iTop - ".$oObjToClone->GetName()." - $sClass clone"); - $oP->add("

    ".$oObjToClone->GetName()." - $sClass clone

    \n"); + $oP->set_title("iTop - ".$oObjToClone->GetName()." - $sClassLabel clone"); + $oP->add("

    ".$oObjToClone->GetName()." - $sClassLabel clone

    \n"); cmdbAbstractObject::DisplayCreationForm($oP, $sClass, $oObjToClone); } else @@ -361,6 +364,7 @@ switch($operation) case 'apply_modify': $sClass = utils::ReadPostedParam('class', ''); + $sClassLabel = MetaModel::GetName($sClass); $id = utils::ReadPostedParam('id', ''); $sTransactionId = utils::ReadPostedParam('transaction_id', ''); if ( empty($sClass) || empty($id)) // TO DO: check that the class name is valid ! @@ -376,8 +380,8 @@ switch($operation) $oObj = $oContext->GetObject($sClass, $id); if ($oObj != null) { - $oP->set_title("iTop - ".$oObj->GetName()." - $sClass modification"); - $oP->add("

    ".$oObj->GetName()." - $sClass modification

    \n"); + $oP->set_title("iTop - ".$oObj->GetName()." - $sClassLabel modification"); + $oP->add("

    ".$oObj->GetName()." - $sClassLabel modification

    \n"); $bObjectModified = false; foreach(MetaModel::ListAttributeDefs(get_class($oObj)) as $sAttCode=>$oAttDef) { @@ -411,7 +415,7 @@ switch($operation) } if (!$bObjectModified) { - $oP->p("No modification detected. ".get_class($oObj)." has not been updated.\n"); + $oP->p("No modification detected. ".MetaModel::GetName(get_class($oObj))." has not been updated.\n"); } else if ($oObj->CheckToUpdate()) { @@ -429,7 +433,7 @@ switch($operation) $iChangeId = $oMyChange->DBInsert(); $oObj->DBUpdateTracked($oMyChange); - $oP->p(get_class($oObj)." updated.\n"); + $oP->p(MetaModel::GetName(get_class($oObj))." updated.\n"); } else { @@ -448,6 +452,7 @@ switch($operation) case 'delete': $sClass = utils::ReadParam('class', ''); + $sClassLabel = MetaModel::GetName($sClass); $id = utils::ReadParam('id', ''); $oObj = $oContext->GetObject($sClass, $id); $sName = $oObj->GetName(); @@ -464,7 +469,7 @@ switch($operation) $oMyChange->Set("userinfo", $sUserString); $oMyChange->DBInsert(); $oObj->DBDeleteTracked($oMyChange); - $oP->add("

    ".$sName." - $sClass deleted

    \n"); + $oP->add("

    ".$sName." - $sClassLabel deleted

    \n"); break; case 'apply_new': @@ -474,6 +479,7 @@ switch($operation) case 'apply_clone': $sClass = utils::ReadPostedParam('class', ''); + $sClassLabel = MetaModel::GetName($sClass); $iCloneId = utils::ReadPostedParam('clone_id', ''); $sTransactionId = utils::ReadPostedParam('transaction_id', ''); if (!utils::IsTransactionValid($sTransactionId)) @@ -507,7 +513,7 @@ switch($operation) } } $oObj->DBCloneTracked($oMyChange); - $oP->add("

    ".$oObj->GetName()." - $sClass created

    \n"); + $oP->add("

    ".$oObj->GetName()." - $sClassLabel created

    \n"); $oObj->DisplayDetails($oP); } @@ -527,6 +533,7 @@ switch($operation) if (is_object($oObj)) { $sClass = get_class($oObj); + $sClassLabel = MetaModel::GetName($sClass); $oMyChange = MetaModel::NewObject("CMDBChange"); $oMyChange->Set("date", time()); if (UserRights::GetUser() != UserRights::GetRealUser()) @@ -540,8 +547,8 @@ switch($operation) $oMyChange->Set("userinfo", $sUserString); $iChangeId = $oMyChange->DBInsert(); $oObj->DBInsertTracked($oMyChange); - $oP->set_title("iTop - ".$oObj->GetName()." - $sClass created"); - $oP->add("

    ".$oObj->GetName()." - $sClass created

    \n"); + $oP->set_title("iTop - ".$oObj->GetName()." - $sClassLabel created"); + $oP->add("

    ".$oObj->GetName()." - $sClassLabel created

    \n"); $oObj->DisplayDetails($oP); } } @@ -645,7 +652,7 @@ switch($operation) } else if (!utils::IsTransactionValid($sTransactionId)) { - $oP->p("Error: object has already be updated!\n"); + $oP->p("Error: object has already been updated!\n"); } else { @@ -688,7 +695,7 @@ switch($operation) $iChangeId = $oMyChange->DBInsert(); $oObj->DBUpdateTracked($oMyChange); - $oP->p(get_class($oObj)." updated.\n"); + $oP->p(MetaModel::GetName(get_class($oObj))." updated.\n"); } $oObj->DisplayDetails($oP); } @@ -822,5 +829,6 @@ switch($operation) default: $oActiveNode->RenderContent($oP, $oAppContext->GetAsHash()); } +////MetaModel::ShowQueryTrace(); $oP->output(); ?> diff --git a/pages/advanced_search.php b/pages/advanced_search.php index e10a2c095..c5b5fbfb8 100644 --- a/pages/advanced_search.php +++ b/pages/advanced_search.php @@ -151,7 +151,7 @@ function Page2_ConfigFilters($oPage, $oFilter) $oSubFilter = new CMDBSearchFilter($sRemoteClass); } $oPage->add("
    \n"); - //$oPage->add(" ".MetaModel::GetClassLabel($sRemoteClass)." "); + //$oPage->add(" ".MetaModel::GetName($sRemoteClass)." "); $oPage->add(" Linked to '".MetaModel::GetLinkLabel($sLinkClass, $sExtKeyAttCode)."' by "); $oSubFilter = new CMDBSearchFilter($sRemoteClass); $oPage->add($oSubFilter->__DescribeHTML()); From e1be74457abef094063ccbbad262abe080ef9535 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Wed, 2 Sep 2009 13:39:20 +0000 Subject: [PATCH 034/227] Improved the behavior when a link class to not have any other attributes than the external keys. Still, it requires some work to have this configuration working. SVN:trunk[109] --- application/ui.linkswidget.class.inc.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/application/ui.linkswidget.class.inc.php b/application/ui.linkswidget.class.inc.php index 1db8517e5..de602bdd0 100644 --- a/application/ui.linkswidget.class.inc.php +++ b/application/ui.linkswidget.class.inc.php @@ -21,13 +21,14 @@ class UILinksWidget { $sHTMLValue = ''; $sTargetClass = self::GetTargetClass($this->m_sClass, $this->m_sAttCode); - $aAllowedValues = MetaModel::GetAllowedValues_att($this->m_sClass, $this->m_sAttCode, array(), ''); + $aAllowedValues = MetaModel::GetAllowedValues_att($this->m_sClass, $this->m_sAttCode, array(), ''); $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sAttCode); $sExtKeyToRemote = $oAttDef->GetExtKeyToRemote(); $sExtKeyToMe = $oAttDef->GetExtKeyToMe(); $sStateAttCode = MetaModel::GetStateAttributeCode($this->m_sClass); $sDefaultState = MetaModel::GetDefaultState($this->m_sClass); + $aAttributes = array(); $sLinkedClass = $oAttDef->GetLinkedClass(); foreach(MetaModel::ListAttributeDefs($sLinkedClass) as $sAttCode=>$oAttDef) { @@ -256,6 +257,7 @@ EOF; $sHTML .= "
    \n"; $index = 0; $aAttrsMap = array(); + $aDetails = array(); foreach(MetaModel::ListAttributeDefs($sLinkedClass) as $sAttCode=>$oAttDef) { if ($sStateAttCode == $sAttCode) From 2f26ebe54cce6033f09306b9bdde68915ce4f3fb Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Fri, 4 Sep 2009 15:22:40 +0000 Subject: [PATCH 035/227] Finalization of the user management by profile (UI to manage the accounts), and some unrelated changes: - Using class labels in the UI - Data model: you may specify a set of allowed values from a query (see caller_id in bizIncident class), still not 100% used in the UI but does not generate any error - Data model: you may specify a password field (AttributePassword replacing AttributeString) - Setup: calling UserRight::Setup() right after calling UserRight::CreateAdministrator() - Setup: administrator account created with "my organization" and a dedicated contact - Menus: optimized the load of std menus (queries written in OQL to get the benefit of the query cache) - Menus: admin tools, seen only by people having the "admin" profile - Object edition: fixed bug with the display of N-N links in the form SVN:trunk[110] --- .../userrights/userrightsmatrix.class.inc.php | 5 + .../userrights/userrightsnull.class.inc.php | 5 + .../userrightsprofile.class.inc.php | 757 ++++++-- application/cmdbabstract.class.inc.php | 240 +-- application/iotask.class.inc.php | 2 +- application/itopwebpage.class.inc.php | 61 +- application/menunode.class.inc.php | 28 +- application/ui.linkswidget.class.inc.php | 11 +- business/incident.business.php | 4 +- core/attributedef.class.inc.php | 53 +- core/dbobjectsearch.class.php | 34 +- core/expression.class.inc.php | 14 +- core/filterdef.class.inc.php | 66 +- core/metamodel.class.php | 22 +- core/userrights.class.inc.php | 22 + core/valuesetdef.class.inc.php | 85 +- js/linkswidget.js | 2 +- pages/UniversalSearch.php | 2 +- pages/ajax.render.php | 7 +- pages/index.php | 41 +- pages/schema.php | 4 +- pages/usermanagement_userstatus.php | 17 + setup/data/30.profiles.xml | 7 + setup/data/31.profileprojection.xml | 15 + setup/data/32.actiongrant.xml | 1659 +++++++++++++++++ setup/data/33.attributegrant.xml | 3 + setup/data/34.stimulusgrant.xml | 189 ++ setup/data/export.cmd | 6 +- setup/data/structure/1.menus.xml | 407 ++-- setup/data/structure/11.profiles.xml | 4 - setup/data/structure/13.profileprojection.xml | 10 +- setup/data/structure/14.actiongrant.xml | 1656 ---------------- setup/data/structure/16.stimulusgrant.xml | 186 -- setup/data/structure/export_menus.cmd | 5 +- setup/data/structure/export_profiles.cmd | 5 +- setup/index.php | 2 +- 36 files changed, 3112 insertions(+), 2524 deletions(-) create mode 100644 setup/data/30.profiles.xml create mode 100644 setup/data/31.profileprojection.xml create mode 100644 setup/data/32.actiongrant.xml create mode 100644 setup/data/33.attributegrant.xml create mode 100644 setup/data/34.stimulusgrant.xml diff --git a/addons/userrights/userrightsmatrix.class.inc.php b/addons/userrights/userrightsmatrix.class.inc.php index 80a4b97ee..8341f6cdf 100644 --- a/addons/userrights/userrightsmatrix.class.inc.php +++ b/addons/userrights/userrightsmatrix.class.inc.php @@ -178,6 +178,11 @@ class UserRightsMatrix extends UserRightsAddOnAPI return true; } + public function IsAdministrator($iUserId) + { + return ($iUserId == 1); + } + public function Setup() { // Users must be added manually diff --git a/addons/userrights/userrightsnull.class.inc.php b/addons/userrights/userrightsnull.class.inc.php index 0d9656fef..79a8600a7 100644 --- a/addons/userrights/userrightsnull.class.inc.php +++ b/addons/userrights/userrightsnull.class.inc.php @@ -22,6 +22,11 @@ class UserRightsNull extends UserRightsAddOnAPI return true; } + public function IsAdministrator($iUserId) + { + return true; + } + public function Setup() { return true; diff --git a/addons/userrights/userrightsprofile.class.inc.php b/addons/userrights/userrightsprofile.class.inc.php index 10528bc90..ca8fbaa2f 100644 --- a/addons/userrights/userrightsprofile.class.inc.php +++ b/addons/userrights/userrightsprofile.class.inc.php @@ -14,12 +14,14 @@ */ -// It is supposed that this profile does exist in the DB -// Possible improvement: add it when executing the setup procedure -// define('ADMIN_PROFILE_ID', 1); -class URP_Users extends DBObject +class UserRightsBaseClass extends cmdbAbstractObject +{ +} + + +class URP_Users extends UserRightsBaseClass { public static function Init() { @@ -36,23 +38,38 @@ class URP_Users extends DBObject "db_table" => "priv_urp_users", "db_key_field" => "id", "db_finalclass_field" => "", + "display_template" => "", ); MetaModel::Init_Params($aParams); //MetaModel::Init_InheritAttributes(); - MetaModel::Init_AddAttribute(new AttributeInteger("userid", array("label"=>"User id", "description"=>"User identifier (depends on the business model)", "allowed_values"=>null, "sql"=>"userid", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeString("login", array("label"=>"login", "description"=>"user identification string", "allowed_values"=>null, "sql"=>"login", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeString("password", array("label"=>"password", "description"=>"user authentication string", "allowed_values"=>null, "sql"=>"pwd", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeString("email", array("label"=>"email", "description"=>"email address", "allowed_values"=>null, "sql"=>"email", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeString("firstname", array("label"=>"firstname", "description"=>"first name", "allowed_values"=>null, "sql"=>"firstname", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeString("lastname", array("label"=>"lastname", "description"=>"last name", "allowed_values"=>null, "sql"=>"lastname", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", array("targetclass"=>"bizPerson", "label"=>"Contact (person)", "description"=>"Personal details from the business data", "allowed_values"=>null, "sql"=>"userid", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("last_name", array("label"=>"Last name", "description"=>"Name of the corresponding contact", "allowed_values"=>null, "extkey_attcode"=> 'userid', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeExternalField("first_name", array("label"=>"First name", "description"=>"First name of the corresponding contact", "allowed_values"=>null, "extkey_attcode"=> 'userid', "target_attcode"=>"first_name"))); + MetaModel::Init_AddAttribute(new AttributeExternalField("email", array("label"=>"Email", "description"=>"Email of the corresponding contact", "allowed_values"=>null, "extkey_attcode"=> 'userid', "target_attcode"=>"email"))); + + MetaModel::Init_AddAttribute(new AttributeString("login", array("label"=>"Login", "description"=>"user identification string", "allowed_values"=>null, "sql"=>"login", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributePassword("password", array("label"=>"Password", "description"=>"user authentication string", "allowed_values"=>null, "sql"=>"pwd", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("profiles", array("label"=>"Profiles", "description"=>"roles, granting rights for that person", "linked_class"=>"URP_UserProfile", "ext_key_to_me"=>"userid", "ext_key_to_remote"=>"profileid", "allowed_values"=>null, "count_min"=>1, "count_max"=>0, "depends_on"=>array()))); //MetaModel::Init_InheritFilters(); MetaModel::Init_AddFilterFromAttribute("userid"); MetaModel::Init_AddFilterFromAttribute("login"); + MetaModel::Init_AddFilterFromAttribute("password"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('userid', 'first_name', 'email', 'login')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('first_name', 'last_name', 'login')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('login', 'userid')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('login', 'userid')); // Criteria of the advanced search form } + } -class URP_Profiles extends DBObject + +class URP_Profiles extends UserRightsBaseClass { public static function Init() { @@ -69,19 +86,93 @@ class URP_Profiles extends DBObject "db_table" => "priv_urp_profiles", "db_key_field" => "id", "db_finalclass_field" => "", + "display_template" => "", ); MetaModel::Init_Params($aParams); //MetaModel::Init_InheritAttributes(); - MetaModel::Init_AddAttribute(new AttributeString("name", array("label"=>"name", "description"=>"label", "allowed_values"=>null, "sql"=>"name", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeString("description", array("label"=>"description", "description"=>"one line description", "allowed_values"=>null, "sql"=>"description", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("name", array("label"=>"Name", "description"=>"label", "allowed_values"=>null, "sql"=>"name", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("description", array("label"=>"Description", "description"=>"one line description", "allowed_values"=>null, "sql"=>"description", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("users", array("label"=>"Users", "description"=>"persons having this role", "linked_class"=>"URP_UserProfile", "ext_key_to_me"=>"profileid", "ext_key_to_remote"=>"userid", "allowed_values"=>null, "count_min"=>1, "count_max"=>0, "depends_on"=>array()))); //MetaModel::Init_InheritFilters(); MetaModel::Init_AddFilterFromAttribute("name"); MetaModel::Init_AddFilterFromAttribute("description"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('name', 'description')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('name', 'description')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form + } + + function GetGrantAsHtml($oUserRights, $sClass, $sAction) + { + $oGrant = $oUserRights->GetClassActionGrant($this->GetKey(), $sClass, $sAction); + if (is_object($oGrant) && ($oGrant->Get('permission') == 'yes')) + { + return 'yes'; + } + else + { + return 'no'; + } + } + + function DoShowGrantSumary($oPage) + { + + // Note: for sure, we assume that the instance is derived from UserRightsProfile + $oUserRights = UserRights::GetModuleInstance(); + + $aDisplayData = array(); + foreach (MetaModel::GetClasses('bizmodel') as $sClass) + { + $aStimuli = array(); + foreach (array_keys(MetaModel::EnumStimuli($sClass)) as $sStimulusCode) + { + $oGrant = $oUserRights->GetClassStimulusGrant($this->GetKey(), $sClass, $sStimulusCode); + if (is_object($oGrant) && ($oGrant->Get('permission') == 'yes')) + { + $aStimuli[] = $sStimulusCode; + } + } + $sStimuli = implode(', ', $aStimuli); + + $aDisplayData[] = array( + 'class' => MetaModel::GetName($sClass), + 'read' => $this->GetGrantAsHtml($oUserRights, $sClass, 'Read'), + 'bulkread' => $this->GetGrantAsHtml($oUserRights, $sClass, 'Bulk Read'), + 'write' => $this->GetGrantAsHtml($oUserRights, $sClass, 'Modify'), + 'bulkwrite' => $this->GetGrantAsHtml($oUserRights, $sClass, 'Bulk Modify'), + 'stimuli' => $sStimuli, + ); + } + + $aDisplayConfig = array(); + $aDisplayConfig['class'] = array('label' => 'Class', 'description' => ''); + $aDisplayConfig['read'] = array('label' => 'Read', 'description' => ''); + $aDisplayConfig['bulkread'] = array('label' => 'Bulk read', 'description' => 'List objects or export massively'); + $aDisplayConfig['write'] = array('label' => 'Write', 'description' => 'Create and edit (modify)'); + $aDisplayConfig['bulkwrite'] = array('label' => 'Bulk write', 'description' => 'Massively create/edit (CSV import)'); + $aDisplayConfig['stimuli'] = array('label' => 'Stimuli', 'description' => 'Allowed (compound) actions'); + $oPage->table($aDisplayConfig, $aDisplayData); + } + + function DisplayBareRelations(web_page $oPage) + { + parent::DisplayBareRelations($oPage); + + $oPage->SetCurrentTabContainer('Related Objects'); + + $oPage->SetCurrentTab('Grants matrix'); + $this->DoShowGrantSumary($oPage); } } -class URP_Dimensions extends DBObject + +class URP_Dimensions extends UserRightsBaseClass { public static function Init() { @@ -98,17 +189,25 @@ class URP_Dimensions extends DBObject "db_table" => "priv_urp_dimensions", "db_key_field" => "id", "db_finalclass_field" => "", + "display_template" => "../business/templates/default.html", ); MetaModel::Init_Params($aParams); //MetaModel::Init_InheritAttributes(); - MetaModel::Init_AddAttribute(new AttributeString("name", array("label"=>"name", "description"=>"label", "allowed_values"=>null, "sql"=>"name", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeString("description", array("label"=>"description", "description"=>"one line description", "allowed_values"=>null, "sql"=>"description", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeString("type", array("label"=>"type", "description"=>"class name or data type (projection unit)", "allowed_values"=>new ValueSetEnumClasses('bizmodel', 'String,Integer'), "sql"=>"type", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("name", array("label"=>"Name", "description"=>"label", "allowed_values"=>null, "sql"=>"name", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("description", array("label"=>"Description", "description"=>"one line description", "allowed_values"=>null, "sql"=>"description", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("type", array("label"=>"Type", "description"=>"class name or data type (projection unit)", "allowed_values"=>new ValueSetEnumClasses('bizmodel', 'String,Integer'), "sql"=>"type", "default_value"=>'String', "is_null_allowed"=>false, "depends_on"=>array()))); //MetaModel::Init_InheritFilters(); MetaModel::Init_AddFilterFromAttribute("name"); MetaModel::Init_AddFilterFromAttribute("description"); MetaModel::Init_AddFilterFromAttribute("type"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('name', 'description', 'type')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('name', 'description')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form } public function CheckProjectionSpec($oProjectionSpec, $sProjectedClass) @@ -193,39 +292,51 @@ class URP_Dimensions extends DBObject } } -class URP_UserProfile extends DBObject + +class URP_UserProfile extends UserRightsBaseClass { public static function Init() { $aParams = array ( "category" => "addon/userrights", - "name" => "user_profile", + "name" => "User to profile", "description" => "user profiles", "key_type" => "autoincrement", "key_label" => "", - "name_attcode" => "", + "name_attcode" => "userid", "state_attcode" => "", "reconc_keys" => array(), "db_table" => "priv_urp_userprofile", "db_key_field" => "id", "db_finalclass_field" => "", + "display_template" => "../business/templates/default.html", ); MetaModel::Init_Params($aParams); //MetaModel::Init_InheritAttributes(); MetaModel::Init_AddAttribute(new AttributeExternalKey("userid", array("targetclass"=>"URP_Users", "jointype"=> "", "label"=>"User", "description"=>"user account", "allowed_values"=>null, "sql"=>"userid", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("userlogin", array("label"=>"Login", "description"=>"User's login", "allowed_values"=>null, "extkey_attcode"=> 'userid', "target_attcode"=>"login"))); - MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass"=>"URP_Profiles", "jointype"=> "", "label"=>"profile", "description"=>"usage profile", "allowed_values"=>null, "sql"=>"profileid", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass"=>"URP_Profiles", "jointype"=> "", "label"=>"Profile", "description"=>"usage profile", "allowed_values"=>null, "sql"=>"profileid", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("profile", array("label"=>"Profile", "description"=>"Profile name", "allowed_values"=>null, "extkey_attcode"=> 'profileid', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeString("reason", array("label"=>"Reason", "description"=>"explain why this person may have this role", "allowed_values"=>null, "sql"=>"description", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + //MetaModel::Init_InheritFilters(); MetaModel::Init_AddFilterFromAttribute("userid"); MetaModel::Init_AddFilterFromAttribute("profileid"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('userid', 'profileid', 'reason')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('userid', 'profileid', 'reason')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('userid', 'profileid')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('userid', 'profileid')); // Criteria of the advanced search form } } -class URP_ProfileProjection extends DBObject + +class URP_ProfileProjection extends UserRightsBaseClass { public static function Init() { @@ -236,19 +347,20 @@ class URP_ProfileProjection extends DBObject "description" => "profile projections", "key_type" => "autoincrement", "key_label" => "", - "name_attcode" => "", + "name_attcode" => "profileid", "state_attcode" => "", "reconc_keys" => array(), "db_table" => "priv_urp_profileprojection", "db_key_field" => "id", "db_finalclass_field" => "", + "display_template" => "../business/templates/default.html", ); MetaModel::Init_Params($aParams); //MetaModel::Init_InheritAttributes(); MetaModel::Init_AddAttribute(new AttributeExternalKey("dimensionid", array("targetclass"=>"URP_Dimensions", "jointype"=> "", "label"=>"Dimension", "description"=>"application dimension", "allowed_values"=>null, "sql"=>"dimensionid", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("dimension", array("label"=>"Dimension", "description"=>"application dimension", "allowed_values"=>null, "extkey_attcode"=> 'dimensionid', "target_attcode"=>"name"))); - MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass"=>"URP_Profiles", "jointype"=> "", "label"=>"profile", "description"=>"usage profile", "allowed_values"=>null, "sql"=>"profileid", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass"=>"URP_Profiles", "jointype"=> "", "label"=>"Profile", "description"=>"usage profile", "allowed_values"=>null, "sql"=>"profileid", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("profile", array("label"=>"Profile", "description"=>"Profile name", "allowed_values"=>null, "extkey_attcode"=> 'profileid', "target_attcode"=>"name"))); MetaModel::Init_AddAttribute(new AttributeString("value", array("label"=>"Value expression", "description"=>"OQL expression (using \$user) | constant | | +attribute code", "allowed_values"=>null, "sql"=>"value", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); @@ -257,6 +369,13 @@ class URP_ProfileProjection extends DBObject //MetaModel::Init_InheritFilters(); MetaModel::Init_AddFilterFromAttribute("dimensionid"); MetaModel::Init_AddFilterFromAttribute("profileid"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('dimensionid', 'profileid', 'value', 'attribute')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('dimensionid', 'profileid', 'value', 'attribute')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('dimensionid', 'profileid')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('dimensionid', 'profileid')); // Criteria of the advanced search form } public function ProjectUser(URP_Users $oUser) @@ -296,7 +415,8 @@ class URP_ProfileProjection extends DBObject } } -class URP_ClassProjection extends DBObject + +class URP_ClassProjection extends UserRightsBaseClass { public static function Init() { @@ -307,12 +427,13 @@ class URP_ClassProjection extends DBObject "description" => "class projections", "key_type" => "autoincrement", "key_label" => "", - "name_attcode" => "", + "name_attcode" => "dimensionid", "state_attcode" => "", "reconc_keys" => array(), "db_table" => "priv_urp_classprojection", "db_key_field" => "id", "db_finalclass_field" => "", + "display_template" => "../business/templates/default.html", ); MetaModel::Init_Params($aParams); //MetaModel::Init_InheritAttributes(); @@ -328,6 +449,13 @@ class URP_ClassProjection extends DBObject MetaModel::Init_AddFilterFromAttribute("dimensionid"); // #@# verifier MetaModel::Init_AddFilterFromAttribute("class"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('dimensionid', 'class', 'value', 'attribute')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('dimensionid', 'class', 'value', 'attribute')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('dimensionid', 'class')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('dimensionid', 'class')); // Criteria of the advanced search form } public function ProjectObject($oObject) @@ -371,7 +499,8 @@ class URP_ClassProjection extends DBObject } } -class URP_ActionGrant extends DBObject + +class URP_ActionGrant extends UserRightsBaseClass { public static function Init() { @@ -382,12 +511,13 @@ class URP_ActionGrant extends DBObject "description" => "permissions on classes", "key_type" => "autoincrement", "key_label" => "", - "name_attcode" => "", + "name_attcode" => "profileid", "state_attcode" => "", "reconc_keys" => array(), "db_table" => "priv_urp_grant_actions", "db_key_field" => "id", "db_finalclass_field" => "", + "display_template" => "../business/templates/default.html", ); MetaModel::Init_Params($aParams); //MetaModel::Init_InheritAttributes(); @@ -395,10 +525,10 @@ class URP_ActionGrant extends DBObject // Common to all grant classes (could be factorized by class inheritence, but this has to be benchmarked) MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass"=>"URP_Profiles", "jointype"=> "", "label"=>"Profile", "description"=>"usage profile", "allowed_values"=>null, "sql"=>"profileid", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("profile", array("label"=>"Profile", "description"=>"usage profile", "allowed_values"=>null, "extkey_attcode"=> 'profileid', "target_attcode"=>"name"))); - MetaModel::Init_AddAttribute(new AttributeString("class", array("label"=>"class", "description"=>"class name", "allowed_values"=>null, "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("label"=>"permission", "description"=>"allowed or not allowed?", "allowed_values"=>new ValueSetEnum('yes,no'), "sql"=>"permission", "default_value"=>"yes", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("class", array("label"=>"Class", "description"=>"class name", "allowed_values"=>null, "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("label"=>"Permission", "description"=>"allowed or not allowed?", "allowed_values"=>new ValueSetEnum('yes,no'), "sql"=>"permission", "default_value"=>"yes", "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeString("action", array("label"=>"action", "description"=>"operations to perform on the given class", "allowed_values"=>null, "sql"=>"action", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("action", array("label"=>"Action", "description"=>"operations to perform on the given class", "allowed_values"=>null, "sql"=>"action", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); //MetaModel::Init_InheritFilters(); // Common to all grant classes (could be factorized by class inheritence, but this has to be benchmarked) @@ -408,10 +538,18 @@ class URP_ActionGrant extends DBObject MetaModel::Init_AddFilterFromAttribute("permission"); MetaModel::Init_AddFilterFromAttribute("action"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('profileid', 'class', 'permission', 'action')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('profileid', 'class', 'permission', 'action')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('profileid', 'class', 'permission', 'action')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('profileid', 'class', 'permission', 'action')); // Criteria of the advanced search form } } -class URP_StimulusGrant extends DBObject + +class URP_StimulusGrant extends UserRightsBaseClass { public static function Init() { @@ -422,12 +560,13 @@ class URP_StimulusGrant extends DBObject "description" => "permissions on stimilus in the life cycle of the object", "key_type" => "autoincrement", "key_label" => "", - "name_attcode" => "", + "name_attcode" => "profileid", "state_attcode" => "", "reconc_keys" => array(), "db_table" => "priv_urp_grant_stimulus", "db_key_field" => "id", "db_finalclass_field" => "", + "display_template" => "../business/templates/default.html", ); MetaModel::Init_Params($aParams); //MetaModel::Init_InheritAttributes(); @@ -435,10 +574,10 @@ class URP_StimulusGrant extends DBObject // Common to all grant classes (could be factorized by class inheritence, but this has to be benchmarked) MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass"=>"URP_Profiles", "jointype"=> "", "label"=>"Profile", "description"=>"usage profile", "allowed_values"=>null, "sql"=>"profileid", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("profile", array("label"=>"Profile", "description"=>"usage profile", "allowed_values"=>null, "extkey_attcode"=> 'profileid', "target_attcode"=>"name"))); - MetaModel::Init_AddAttribute(new AttributeString("class", array("label"=>"class", "description"=>"class name", "allowed_values"=>null, "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("label"=>"permission", "description"=>"allowed or not allowed?", "allowed_values"=>new ValueSetEnum('yes,no'), "sql"=>"permission", "default_value"=>"yes", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("class", array("label"=>"Class", "description"=>"class name", "allowed_values"=>null, "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("label"=>"Permission", "description"=>"allowed or not allowed?", "allowed_values"=>new ValueSetEnum('yes,no'), "sql"=>"permission", "default_value"=>"yes", "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeString("stimulus", array("label"=>"stimulus", "description"=>"stimulus code", "allowed_values"=>null, "sql"=>"action", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("stimulus", array("label"=>"Stimulus", "description"=>"stimulus code", "allowed_values"=>null, "sql"=>"action", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); //MetaModel::Init_InheritFilters(); // Common to all grant classes (could be factorized by class inheritence, but this has to be benchmarked) @@ -448,10 +587,18 @@ class URP_StimulusGrant extends DBObject MetaModel::Init_AddFilterFromAttribute("permission"); MetaModel::Init_AddFilterFromAttribute("stimulus"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('profileid', 'class', 'permission', 'stimulus')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('profileid', 'class', 'permission', 'stimulus')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('profileid', 'class', 'permission', 'stimulus')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('profileid', 'class', 'permission', 'stimulus')); // Criteria of the advanced search form } } -class URP_AttributeGrant extends DBObject + +class URP_AttributeGrant extends UserRightsBaseClass { public static function Init() { @@ -462,22 +609,30 @@ class URP_AttributeGrant extends DBObject "description" => "permissions at the attributes level", "key_type" => "autoincrement", "key_label" => "", - "name_attcode" => "", + "name_attcode" => "actiongrantid", "state_attcode" => "", "reconc_keys" => array(), "db_table" => "priv_urp_grant_attributes", "db_key_field" => "id", "db_finalclass_field" => "", + "display_template" => "../business/templates/default.html", ); MetaModel::Init_Params($aParams); //MetaModel::Init_InheritAttributes(); MetaModel::Init_AddAttribute(new AttributeExternalKey("actiongrantid", array("targetclass"=>"URP_ActionGrant", "jointype"=> "", "label"=>"Action grant", "description"=>"action grant", "allowed_values"=>null, "sql"=>"actiongrantid", "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeString("attcode", array("label"=>"attribute", "description"=>"attribute code", "allowed_values"=>null, "sql"=>"attcode", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("attcode", array("label"=>"Attribute", "description"=>"attribute code", "allowed_values"=>null, "sql"=>"attcode", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); //MetaModel::Init_InheritFilters(); MetaModel::Init_AddFilterFromAttribute("actiongrantid"); MetaModel::Init_AddFilterFromAttribute("attcode"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('actiongrantid', 'attcode')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('actiongrantid', 'attcode')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('actiongrantid', 'attcode')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('actiongrantid', 'attcode')); // Criteria of the advanced search form } } @@ -498,182 +653,68 @@ class UserRightsProfile extends UserRightsAddOnAPI // Installation: create the very first user public function CreateAdministrator($sAdminUser, $sAdminPwd) { + $oOrg = new bizOrganization(); + $oOrg->Set('name', 'My Company/Department'); + $oOrg->Set('code', 'SOMECODE'); + $oOrg->Set('status', 'implementation'); + //$oOrg->Set('parent_id', xxx); + $iOrgId = $oOrg->DBInsertNoReload(); + + // Location : optional + //$oLocation = new bizLocation(); + //$oLocation->Set('name', 'MyOffice'); + //$oLocation->Set('status', 'implementation'); + //$oLocation->Set('org_id', $iOrgId); + //$oLocation->Set('severity', 'high'); + //$oLocation->Set('address', 'my building in my city'); + //$oLocation->Set('country', 'my country'); + //$oLocation->Set('parent_location_id', xxx); + //$iLocationId = $oLocation->DBInsertNoReload(); + + $oContact = new bizPerson(); + $oContact->Set('name', 'My last name'); + $oContact->Set('first_name', 'My first name'); + $oContact->Set('status', 'production'); + $oContact->Set('org_id', $iOrgId); + $oContact->Set('email', 'my.email@foo.org'); + $oContact->Set('phone', ''); + //$oContact->Set('location_id', $iLocationId); + $oContact->Set('employee_number', ''); + $iContactId = $oContact->DBInsertNoReload(); + $oUser = new URP_Users(); $oUser->Set('login', $sAdminUser); $oUser->Set('password', $sAdminPwd); - $oUser->Set('email', 'n/a'); - $oUser->Set('firstname', 'administrator'); - $oUser->Set('lastname', 'itop'); - $oUser->Set('userid', 1); // let's mark it as #1 (for what purpose?) + $oUser->Set('userid', $iContactId); $iUserId = $oUser->DBInsertNoReload(); // Add this user to the very specific 'admin' profile $oUserProfile = new URP_UserProfile(); $oUserProfile->Set('userid', $iUserId); $oUserProfile->Set('profileid', ADMIN_PROFILE_ID); + $oUserProfile->Set('reason', 'By definition, the administrator must have the administrator profile'); $oUserProfile->DBInsertNoReload(); return true; } - public function Setup() + public function IsAdministrator($iUserId) { - // Dimensions/Profiles/Classes/Attributes/Stimuli could be added anytime - // This procedure will then update the matrix with expected default values - // - - $oProfileSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_Profiles")); - while ($oProfile = $oProfileSet->Fetch()) + if (in_array($iUserId, $this->m_aAdmins)) { - $this->SetupProfile($oProfile); - } - - $oDimensionSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_Dimensions")); - while ($oDimension = $oDimensionSet->Fetch()) - { - $this->SetupDimension($oDimension); - } - return true; - } - - protected function SetupDimension($oDimension, $bNewDimension = false) - { - $iDimensionId = $oDimension->GetKey(); - - // Create projections, for any class where it applies - // - foreach(array('bizmodel', 'application', 'gui', 'core/cmdb') as $sCategory) - { - foreach (MetaModel::GetClasses($sCategory) as $sClass) - { - if ($bNewDimension) - { - $bAddCell = true; - } - else - { - $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_ClassProjection WHERE class = :class AND dimensionid = :dimension"), array(), array('class'=>$sClass, 'dimension'=>$iDimensionId)); - $bAddCell = ($oSet->Count() < 1); - } - if ($bAddCell) - { - // Create a new entry - $oCProj = MetaModel::NewObject("URP_ClassProjection"); - $oCProj->Set("dimensionid", $iDimensionId); - $oCProj->Set("class", $sClass); - $oCProj->Set("value", "true"); - $iId = $oCProj->DBInsertNoReload(); - } - } - } - // Create projections, for any existing profile - // - $oProfileSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_Profiles")); - while ($oProfile = $oProfileSet->Fetch()) - { - $iProfileId = $oProfile->GetKey(); - if ($bNewDimension) - { - $bAddCell = true; - } - else - { - $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_ProfileProjection WHERE dimensionid = :dimension AND profileid = :profile"), array(), array('dimension'=>$iDimensionId, 'profile'=>$iProfileId)); - $bAddCell = ($oSet->Count() < 1); - } - if ($bAddCell) - { - // Create a new entry - $oDProj = MetaModel::NewObject("URP_ProfileProjection"); - $oDProj->Set("dimensionid", $iDimensionId); - $oDProj->Set("profileid", $iProfileId); - $oDProj->Set("value", "true"); - $iId = $oDProj->DBInsertNoReload(); - } - } - } - - protected function SetupProfile($oProfile, $bNewProfile = false) - { - $iProfileId = $oProfile->GetKey(); - - // Create grant records, for any class where it matters (the rest could be done later on) - // - foreach(array('bizmodel') as $sCategory) - { - foreach (MetaModel::GetClasses($sCategory) as $sClass) - { - foreach (self::$m_aActionCodes as $iActionCode => $sAction) - { - if ($bNewProfile) - { - $bAddCell = true; - } - else - { - $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_ActionGrant WHERE class = :class AND action = :action AND profileid = :profile"), array(), array('class'=>$sClass, 'action'=>$sAction, 'profile'=>$iProfileId)); - $bAddCell = ($oSet->Count() < 1); - } - if ($bAddCell) - { - // Create a new entry - $oMyActionGrant = MetaModel::NewObject("URP_ActionGrant"); - $oMyActionGrant->Set("profileid", $iProfileId); - $oMyActionGrant->Set("class", $sClass); - $oMyActionGrant->Set("action", $sAction); - $oMyActionGrant->Set("permission", "yes"); - $iId = $oMyActionGrant->DBInsertNoReload(); - } - } - foreach (MetaModel::EnumStimuli($sClass) as $sStimulusCode => $oStimulus) - { - if ($bNewProfile) - { - $bAddCell = true; - } - else - { - $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_StimulusGrant WHERE class = :class AND stimulus = :stimulus AND profileid = :profile"), array(), array('class'=>$sClass, 'stimulus'=>$sStimulusCode, 'profile'=>$iProfileId)); - $bAddCell = ($oSet->Count() < 1); - } - if ($bAddCell) - { - // Create a new entry - $oMyStGrant = MetaModel::NewObject("URP_StimulusGrant"); - $oMyStGrant->Set("profileid", $iProfileId); - $oMyStGrant->Set("class", $sClass); - $oMyStGrant->Set("stimulus", $sStimulusCode); - $oMyStGrant->Set("permission", "yes"); - $iId = $oMyStGrant->DBInsertNoReload(); - } - } - } - } - // Create the "My Bookmarks" menu item (parent_id = 0, rank = 6) - if ($bNewProfile) - { - $bAddMenu = true; + return true; } else { - //$oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT menuNode WHERE type = 'user' AND parent_id = 0 AND user_id = $iUserId")); - //$bAddMenu = ($oSet->Count() < 1); - } - $bAddMenu = false; - if ($bAddMenu) - { - $oMenu = MetaModel::NewObject('menuNode'); - $oMenu->Set('type', 'user'); - $oMenu->Set('parent_id', 0); // It's a toplevel entry - $oMenu->Set('rank', 6); // Located just above the Admin Tools section (=7) - $oMenu->Set('name', 'My Bookmarks'); - $oMenu->Set('label', 'My Favorite Items'); - $oMenu->Set('hyperlink', 'UI.php'); - $oMenu->Set('template', '

    My bookmarks

    This section contains my most favorite search results

    '); - $oMenu->Set('user_id', $iUserId); - $oMenu->DBInsert(); + return false; } } + public function Setup() + { + SetupITILProfiles::DoCreateProfiles(); + return true; + } + public function Init() { MetaModel::RegisterPlugin('userrights', 'ACbyProfile', array($this, 'CacheData')); @@ -689,6 +730,7 @@ class UserRightsProfile extends UserRightsAddOnAPI protected $m_aAdmins = array(); // id of users being linked to the profile #ADMIN_PROFILE_ID protected $m_aClassActionGrants = array(); // profile, class, action -> permission + protected $m_aClassStimulusGrants = array(); // profile, class, stimulus -> permission protected $m_aObjectActionGrants = array(); // userid, class, id, action -> permission, list of attributes public function CacheData() @@ -787,19 +829,33 @@ exit; return $oNullFilter; } - protected function GetClassActionGrant($iProfile, $sClass, $sAction) + // This verb has been made public to allow the development of an accurate feedback for the current configuration + public function GetClassActionGrant($iProfile, $sClass, $sAction) { - $aTest = @$this->m_aClassActionGrants[$iProfile][$sClass][$sAction]; - if (isset($aTest)) return $aTest; + if (isset($this->m_aClassActionGrants[$iProfile][$sClass][$sAction])) + { + return $this->m_aClassActionGrants[$iProfile][$sClass][$sAction]; + } // Get the permission for this profile/class/action $oSearch = DBObjectSearch::FromOQL("SELECT URP_ActionGrant WHERE class = :class AND action = :action AND profileid = :profile AND permission = 'yes'"); $oSet = new DBObjectSet($oSearch, array(), array('class'=>$sClass, 'action'=>$sAction, 'profile'=>$iProfile)); - if ($oSet->Count() < 1) + if ($oSet->Count() >= 1) { - return null; + $oGrantRecord = $oSet->Fetch(); + } + else + { + $sParentClass = MetaModel::GetParentPersistentClass($sClass); + if (empty($sParentClass)) + { + $oGrantRecord = null; + } + else + { + $oGrantRecord = $this->GetClassActionGrant($iProfile, $sParentClass, $sAction); + } } - $oGrantRecord = $oSet->Fetch(); $this->m_aClassActionGrants[$iProfile][$sClass][$sAction] = $oGrantRecord; return $oGrantRecord; @@ -854,8 +910,7 @@ exit; public function IsActionAllowed($iUserId, $sClass, $iActionCode, dbObjectSet $oInstances) { - // super admin rights - if (in_array($iUserId, $this->m_aAdmins)) return true; + if ($this->IsAdministrator($iUserId)) return true; $oUser = $this->m_aUsers[$iUserId]; @@ -892,8 +947,7 @@ exit; public function IsActionAllowedOnAttribute($iUserId, $sClass, $sAttCode, $iActionCode, dbObjectSet $oInstances) { - // super admin rights - if (in_array($iUserId, $this->m_aAdmins)) return true; + if ($this->IsAdministrator($iUserId)) return true; $oUser = $this->m_aUsers[$iUserId]; @@ -936,10 +990,33 @@ exit; } } + // This verb has been made public to allow the development of an accurate feedback for the current configuration + public function GetClassStimulusGrant($iProfile, $sClass, $sStimulusCode) + { + if (isset($this->m_aClassStimulusGrants[$iProfile][$sClass][$sStimulusCode])) + { + return $this->m_aClassStimulusGrants[$iProfile][$sClass][$sStimulusCode]; + } + + // Get the permission for this profile/class/stimulus + $oSearch = DBObjectSearch::FromOQL("SELECT URP_StimulusGrant WHERE class = :class AND stimulus = :stimulus AND profileid = :profile AND permission = 'yes'"); + $oSet = new DBObjectSet($oSearch, array(), array('class'=>$sClass, 'stimulus'=>$sStimulusCode, 'profile'=>$iProfile)); + if ($oSet->Count() >= 1) + { + $oGrantRecord = $oSet->Fetch(); + } + else + { + $oGrantRecord = null; + } + + $this->m_aClassStimulusGrants[$iProfile][$sClass][$sStimulusCode] = $oGrantRecord; + return $oGrantRecord; + } + public function IsStimulusAllowed($iUserId, $sClass, $sStimulusCode, dbObjectSet $oInstances) { - // super admin rights - if (in_array($iUserId, $this->m_aAdmins)) return true; + if ($this->IsAdministrator($iUserId)) return true; $oUser = $this->m_aUsers[$iUserId]; @@ -1069,6 +1146,274 @@ exit; } } +// +// Here is the code that will create some profiles into our user management model, given an ITIL representation of the user profiles +// +class SetupITILProfiles +{ + /* + Later, maybe ? + + protected static $m_aDimensions = array( + 'organization' => array( + 'description' => '', + 'type' => 'bizOrganization', + ), + 'site' => array( + 'description' => '', + 'type' => '', + ), + ); + */ + protected static $m_aActions = array( + UR_ACTION_READ => 'Read', + UR_ACTION_MODIFY => 'Modify', + UR_ACTION_DELETE => 'Delete', + UR_ACTION_BULK_READ => 'Bulk Read', + UR_ACTION_BULK_MODIFY => 'Bulk Modify', + UR_ACTION_BULK_DELETE => 'Bulk Delete', + ); + + // It is possible to specify the same class in several modules + // + protected static $m_aModules = array( + 'General' => array( + 'bizOrganization', + ), + 'Documentation' => array( + 'bizDocVersion', + 'lnkDocumentRealObject', + 'lnkDocumentContract', + 'lnkDocumentError', + ), + 'Configuration' => array( + 'logRealObject', + 'lnkContactRealObject', + 'lnkInterfaces', + 'ClientServerLinks', + 'lnkInfraGrouping', + ), + 'Incident' => array( + 'bizIncidentTicket', + 'lnkRelatedTicket', + 'lnkInfraTicket', + 'lnkContactTicket', + ), + 'Problem' => array( + 'bizKnownError', + 'lnkInfraError', + 'lnkDocumentError', + ), + 'Change' => array( + 'bizChangeTicket', + 'lnkInfraChangeTicket', + 'lnkContactChange', + ), + 'Service' => array( + 'bizContract', + 'lnkInfraContract', + 'lnkContactContract', + 'lnkDocumentContract', + ), + ); + + protected static $m_aProfiles = array( + 'Configuration Manager' => array( + 'description' => 'Person in charge of the documentation of the managed CIs', + 'write_modules' => 'Documentation,Configuration', + 'stimuli' => array( + 'bizServer' => 'any', + //'bizServer' => 'ev_store,ev_ship,ev_plug,ev_configuration_finished,ev_val_failed,ev_mtp,ev_start_change,ev_end_change,ev_decomission,ev_obsolete,ev_recycle', + 'bizContract' => 'none', + 'bizIncidentTicket' => 'none', + 'bizChangeTicket' => 'none', + ), + ), + /* 'Requestor pb granularite actions (create/delete)' => array( + 'description' => 'Person notifying an incident', + 'write_modules' => 'Incident', + 'stimuli' => array( + 'bizServer' => 'none', + 'bizContract' => 'none', + 'bizIncidentTicket' => 'none', + 'bizChangeTicket' => 'none', + ), + ), + */ + 'Service Desk Agent' => array( + 'description' => 'Person in charge of creating incident reports', + 'write_modules' => 'Documentation,Incident', + 'stimuli' => array( + 'bizServer' => 'none', + 'bizContract' => 'none', + 'bizIncidentTicket' => 'ev_assign', + 'bizChangeTicket' => 'none', + ), + ), + 'Support Agent' => array( + 'description' => 'Person analyzing and solving the current incidents or problems', + 'write_modules' => 'Documentation,Incident,Problem', + 'stimuli' => array( + 'bizIncidentTicket' => 'any', + //'bizIncidentTicket' => 'ev_assign,ev_reassign,ev_start_working,ev_close', + ), + ), + 'Change Implementor' => array( + 'description' => 'Person executing the changes', + 'write_modules' => 'Documentation,Configuration,Change', + 'stimuli' => array( + 'bizServer' => 'none', + 'bizContract' => 'none', + 'bizIncidentTicket' => 'none', + 'bizChangeTicket' => 'ev_plan,ev_replan,ev_implement,ev_monitor', + ), + ), + 'Change Supervisor' => array( + 'description' => 'Person responsible for the overall change execution', + 'write_modules' => 'Documentation,Change', + 'stimuli' => array( + 'bizServer' => 'none', + 'bizContract' => 'none', + 'bizIncidentTicket' => 'none', + 'bizChangeTicket' => 'ev_validate,ev_reject,ev_reopen,ev_finish', + ), + ), + 'Change Approver' => array( + 'description' => 'Person who could be impacted by some changes', + 'write_modules' => 'Documentation,Change', + 'stimuli' => array( + 'bizServer' => 'none', + 'bizContract' => 'none', + 'bizIncidentTicket' => 'none', + 'bizChangeTicket' => 'ev_approve,ev_notapprove', + ), + ), + 'Service Manager' => array( + 'description' => 'Person responsible for the service delivered to the [internal] customer', + 'write_modules' => 'Documentation,Service', + 'stimuli' => array( + 'bizServer' => 'none', + 'bizContract' => 'any', + //'bizContract' => 'ev_freeze_version,ev_sign,ev_begin,ev_notice,ev_terminate,ev_elapsed', + 'bizIncidentTicket' => 'none', + 'bizChangeTicket' => 'none', + ), + ), + ); + + + protected static function DoCreateProfileProjection($iProfile, $iDimension) + { + $oNewObj = MetaModel::NewObject("URP_ProfileProjection"); + $oNewObj->Set('profileid', $iProfile); + $oNewObj->Set('dimensionid', $iDimension); + $oNewObj->Set('value', ''); + $oNewObj->Set('attribute', ''); + $iId = $oNewObj->DBInsertNoReload(); + return $iId; + } + + + protected static function DoCreateActionGrant($iProfile, $iAction, $sClass) + { + $oNewObj = MetaModel::NewObject("URP_ActionGrant"); + $oNewObj->Set('profileid', $iProfile); + $oNewObj->Set('permission', true); + $oNewObj->Set('class', $sClass); + $oNewObj->Set('action', self::$m_aActions[$iAction]); + $iId = $oNewObj->DBInsertNoReload(); + return $iId; + } + + protected static function DoCreateStimulusGrant($iProfile, $sStimulusCode, $sClass) + { + $oNewObj = MetaModel::NewObject("URP_StimulusGrant"); + $oNewObj->Set('profileid', $iProfile); + $oNewObj->Set('permission', true); + $oNewObj->Set('class', $sClass); + $oNewObj->Set('stimulus', $sStimulusCode); + $iId = $oNewObj->DBInsertNoReload(); + return $iId; + } + + protected static function DoCreateOneProfile($sName, $aProfileData) + { + $sDescription = $aProfileData['description']; + $aWriteModules = explode(',', $aProfileData['write_modules']); + $aStimuli = $aProfileData['stimuli']; + + $oNewObj = MetaModel::NewObject("URP_Profiles"); + $oNewObj->Set('name', $sName); + $oNewObj->Set('description', $sDescription); + $iProfile = $oNewObj->DBInsertNoReload(); + + // Project in every dimension + // + $oDimensionSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_Dimensions")); + while ($oDimension = $oDimensionSet->Fetch()) + { + $iDimension = $oDimension->GetKey(); + self::DoCreateProfileProjection($iProfile, $iDimension); + } + + // Grant read rights for everything + // + foreach (MetaModel::GetClasses('bizmodel') as $sClass) + { + self::DoCreateActionGrant($iProfile, UR_ACTION_READ, $sClass); + self::DoCreateActionGrant($iProfile, UR_ACTION_BULK_READ, $sClass); + } + + // Grant write for given modules + // Start by compiling the information, because some modules may overlap + $aWriteableClasses = array(); + foreach ($aWriteModules as $sModule) + { +// $oPage->p('Granting write access for the module"'.$sModule.'" - '.count(self::$m_aModules[$sModule]).' classes'); + foreach (self::$m_aModules[$sModule] as $sClass) + { + $aWriteableClasses[] = $sClass; + } + } + foreach ($aWriteableClasses as $sClass) + { + self::DoCreateActionGrant($iProfile, UR_ACTION_MODIFY, $sClass); + self::DoCreateActionGrant($iProfile, UR_ACTION_DELETE, $sClass); + self::DoCreateActionGrant($iProfile, UR_ACTION_BULK_MODIFY, $sClass); + self::DoCreateActionGrant($iProfile, UR_ACTION_BULK_DELETE, $sClass); + } + + // Grant stimuli for given classes + foreach ($aStimuli as $sClass => $sAllowedStimuli) + { + if ($sAllowedStimuli == 'any') + { + $aAllowedStimuli = array_keys(MetaModel::EnumStimuli($sClass)); + } + elseif ($sAllowedStimuli == 'none') + { + $aAllowedStimuli = array(); + } + else + { + $aAllowedStimuli = explode(',', $sAllowedStimuli); + } + foreach ($aAllowedStimuli as $sStimulusCode) + { + self::DoCreateStimulusGrant($iProfile, $sStimulusCode, $sClass); + } + } + } + + public static function DoCreateProfiles() + { + foreach(self::$m_aProfiles as $sName => $aProfileData) + { + self::DoCreateOneProfile($sName, $aProfileData); + } + } +} + UserRights::SelectModule('UserRightsProfile'); ?> diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index 59dd5a48b..e26d0647e 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -100,11 +100,71 @@ abstract class cmdbAbstractObject extends CMDBObject return $sDisplayValue; } + function DisplayBareHeader(web_page $oPage) + { + // Standard Header with name, actions menu and history block + // + $oPage->add("
    \n"); + + // action menu + $oSingletonFilter = new DBObjectSearch(get_class($this)); + $oSingletonFilter->AddCondition('pkey', array($this->GetKey())); + $oBlock = new MenuBlock($oSingletonFilter, 'popup', false); + $oBlock->Display($oPage, -1); + + $oPage->add("

    ".MetaModel::GetName(get_class($this)).": ".$this->GetDisplayName()."

    \n"); + + // history block (with toggle) + $oHistoryFilter = new DBObjectSearch('CMDBChangeOpSetAttribute'); + $oHistoryFilter->AddCondition('objkey', $this->GetKey()); + $oBlock = new HistoryBlock($oHistoryFilter, 'toggle', false); + $oBlock->Display($oPage, -1); + + $oPage->add("
    \n"); + } + function DisplayBareDetails(web_page $oPage) { $oPage->add($this->GetBareDetails($oPage)); } + function DisplayBareRelations(web_page $oPage) + { + // Related objects + $oPage->AddTabContainer('Related Objects'); + $oPage->SetCurrentTabContainer('Related Objects'); + foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef) + { + if ((get_class($oAttDef) == 'AttributeLinkedSetIndirect') || (get_class($oAttDef) == 'AttributeLinkedSet')) + { + $oPage->SetCurrentTab($oAttDef->GetLabel()); + $oPage->p($oAttDef->GetDescription()); + + if (get_class($oAttDef) == 'AttributeLinkedSet') + { + $sTargetClass = $oAttDef->GetLinkedClass(); + $oFilter = new DBObjectSearch($sTargetClass); + $oFilter->AddCondition($oAttDef->GetExtKeyToMe(), $this->GetKey()); // @@@ condition has same name as field ?? + + $oBlock = new DisplayBlock($oFilter, 'list', false); + $oBlock->Display($oPage, 0); + } + else // get_class($oAttDef) == 'AttributeLinkedSetIndirect' + { + $sLinkClass = $oAttDef->GetLinkedClass(); + // Transform the DBObjectSet into a CMBDObjectSet !!! + $aLinkedObjects = $this->Get($sAttCode)->ToArray(false); + if (count($aLinkedObjects) > 0) + { + $oSet = CMDBObjectSet::FromArray($sLinkClass, $aLinkedObjects); + $this->DisplaySet($oPage, $oSet, $oAttDef->GetExtKeyToMe(), true, false, $this->GetKey(), $oAttDef->GetExtKeyToRemote()); + } + } + } + } + $oPage->SetCurrentTab(''); + } + function GetDisplayName() { return $this->GetAsHTML(MetaModel::GetNameAttributeCode(get_class($this))); @@ -152,56 +212,11 @@ abstract class cmdbAbstractObject extends CMDBObject } else { - // Standard Header with name, actions menu and history block - $oPage->add("
    \n"); - $oSingletonFilter = new DBObjectSearch(get_class($this)); - $oSingletonFilter->AddCondition('pkey', array($this->GetKey())); - $oBlock = new MenuBlock($oSingletonFilter, 'popup', false); - $oBlock->Display($oPage, -1); - $oPage->add("

    ".Metamodel::GetName(MetaModel::GetName(get_class($this))).": ".$this->GetDisplayName()."

    \n"); - $oHistoryFilter = new DBObjectSearch('CMDBChangeOpSetAttribute'); - $oHistoryFilter->AddCondition('objkey', $this->GetKey()); - $oBlock = new HistoryBlock($oHistoryFilter, 'toggle', false); - $oBlock->Display($oPage, -1); - $oPage->add("
    \n"); - // Object's details // template not found display the object using the *old style* - self::DisplayBareDetails($oPage); - - // Related objects - $oPage->AddTabContainer('Related Objects'); - $oPage->SetCurrentTabContainer('Related Objects'); - foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef) - { - if ((get_class($oAttDef) == 'AttributeLinkedSetIndirect') || (get_class($oAttDef) == 'AttributeLinkedSet')) - { - $oPage->SetCurrentTab($oAttDef->GetLabel()); - $oPage->p($oAttDef->GetDescription()); - - if (get_class($oAttDef) == 'AttributeLinkedSet') - { - $sTargetClass = $oAttDef->GetLinkedClass(); - $oFilter = new DBObjectSearch($sTargetClass); - $oFilter->AddCondition($oAttDef->GetExtKeyToMe(), $this->GetKey()); // @@@ condition has same name as field ?? - - $oBlock = new DisplayBlock($oFilter, 'list', false); - $oBlock->Display($oPage, 0); - } - else // get_class($oAttDef) == 'AttributeLinkedSetIndirect' - { - $sLinkClass = $oAttDef->GetLinkedClass(); - // Transform the DBObjectSet into a CMBDObjectSet !!! - $aLinkedObjects = $this->Get($sAttCode)->ToArray(false); - if (count($aLinkedObjects) > 0) - { - $oSet = CMDBObjectSet::FromArray($sLinkClass, $aLinkedObjects); - $this->DisplaySet($oPage, $oSet, $oAttDef->GetExtKeyToMe()); - } - } - } - } - $oPage->SetCurrentTab(''); + $this->DisplayBareHeader($oPage); + $this->DisplayBareDetails($oPage); + $this->DisplayBareRelations($oPage); } } @@ -218,10 +233,10 @@ abstract class cmdbAbstractObject extends CMDBObject } // Comment by Rom: this helper may be used to display objects of class DBObject - // -> I am using this to display the changes history - public static function DisplaySet(web_page $oPage, CMDBObjectSet $oSet, $sLinkageAttribute = '', $bDisplayMenu = true, $bSelectMode = false, $iObjectId = 0) + // -> I am using this to display the changes history + public static function DisplaySet(web_page $oPage, CMDBObjectSet $oSet, $sLinkageAttribute = '', $bDisplayMenu = true, $bSelectMode = false, $iObjectId = 0, $sTargetAttribute = '') { - $oPage->add(self::GetDisplaySet($oPage, $oSet, array( 'link_attr' => $sLinkageAttribute, 'object_id' => $iObjectId, 'menu' => $bDisplayMenu, 'selection_mode' => $bSelectMode))); + $oPage->add(self::GetDisplaySet($oPage, $oSet, array( 'link_attr' => $sLinkageAttribute, 'object_id' => $iObjectId, 'target_attr' => $sTargetAttribute, 'menu' => $bDisplayMenu, 'selection_mode' => $bSelectMode))); } //public static function GetDisplaySet(web_page $oPage, CMDBObjectSet $oSet, $sLinkageAttribute = '', $bDisplayMenu = true, $bSelectMode = false) @@ -238,7 +253,7 @@ abstract class cmdbAbstractObject extends CMDBObject { if($iLinkedObjectId == 0) { - // if 'links' mode is requested the d of the object to link to must be specified + // if 'links' mode is requested the id of the object to link to must be specified throw new ApplicationException("Parameter object_id is mandatory when link_attr is specified. Check the definition of the display template."); } if($sTargetAttr == '') @@ -438,9 +453,9 @@ abstract class cmdbAbstractObject extends CMDBObject $sClassName = $oSet->GetFilter()->GetClass(); $sHtml .= "\n"; +
  • OQL Query
  • +
  • Simple Search
  • +
    \n"; // Simple search form $sHtml .= "
    \n"; $sHtml .= "

    Search for ".MetaModel::GetName($sClassName)." Objects

    \n"; @@ -487,32 +502,33 @@ abstract class cmdbAbstractObject extends CMDBObject } } } - $aAllowedValues = MetaModel::GetAllowedValues_flt($sClassName, $sFilterCode, array(), ''); - if ($aAllowedValues != null) - { - //Enum field or external key, display a combo - $sValue = "\n"; - $sHtml .= "
    \n"; - } - else - { - // Any value is possible, display an input box - $sHtml .= "\n"; - } + // #@# todo - add context information, otherwise any value will be authorized for external keys + $aAllowedValues = MetaModel::GetAllowedValues_flt($sClassName, $sFilterCode, array()); + if ($aAllowedValues != null) + { + //Enum field or external key, display a combo + $sValue = "\n"; + $sHtml .= "\n"; + } + else + { + // Any value is possible, display an input box + $sHtml .= "\n"; + } $index++; } if (($index % $numCols) != 0) @@ -577,6 +593,10 @@ abstract class cmdbAbstractObject extends CMDBObject $sHTMLValue = ""; break; + case 'Password': + $sHTMLValue = ""; + break; + case 'Text': $sHTMLValue = ""; break; @@ -588,39 +608,41 @@ abstract class cmdbAbstractObject extends CMDBObject case 'String': default: - $aAllowedValues = MetaModel::GetAllowedValues_att($sClass, $sAttCode, array(), ''); - if ($aAllowedValues !== null) - { - //Enum field or external key, display a combo - if (count($aAllowedValues) == 0) + // #@# todo - add context information (depending on dimensions) + $aAllowedValues = MetaModel::GetAllowedValues_att($sClass, $sAttCode, array()); + if ($aAllowedValues !== null) { - $sHTMLValue = ""; - } - else if (count($aAllowedValues) > 50) - { - // too many choices, use an autocomplete - // The input for the auto complete - $sHTMLValue = ""; - // another hidden input to store & pass the object's Id - $sHTMLValue .= "\n"; - $oPage->add_ready_script("\$('#label_$iInputId').autocomplete('./ajax.render.php', { minChars:3, onItemSelect:selectItem, onFindValue:findValue, formatItem:formatItem, autoFill:true, keyHolder:'#$iInputId', extraParams:{operation:'autocomplete', sclass:'$sClass',attCode:'".$sAttCode."'}});"); + //Enum field or external key, display a combo + if (count($aAllowedValues) == 0) + { + $sHTMLValue = ""; + } + else if (count($aAllowedValues) > 50) + { + // too many choices, use an autocomplete + // The input for the auto complete + $sHTMLValue = ""; + // another hidden input to store & pass the object's Id + $sHTMLValue .= "\n"; + $oPage->add_ready_script("\$('#label_$iInputId').autocomplete('./ajax.render.php', { minChars:3, onItemSelect:selectItem, onFindValue:findValue, formatItem:formatItem, autoFill:true, keyHolder:'#$iInputId', extraParams:{operation:'autocomplete', sclass:'$sClass',attCode:'".$sAttCode."'}});"); + } + else + { + // Few choices, use a normal 'select' + $sHTMLValue = "\n"; + } } else { - // Few choices, use a normal 'select' - $sHTMLValue = "\n"; + $sHTMLValue = ""; } - } - else - { - $sHTMLValue = ""; - } + break; } } return $sHTMLValue; diff --git a/application/iotask.class.inc.php b/application/iotask.class.inc.php index 32eb33400..f1f62f8c7 100644 --- a/application/iotask.class.inc.php +++ b/application/iotask.class.inc.php @@ -31,7 +31,7 @@ class InputOutputTask extends cmdbAbstractObject MetaModel::Init_AddAttribute(new AttributeEnum("source_type", array("label"=>"Source Type", "description"=>"Type of data source", "allowed_values"=>new ValueSetEnum('File, Database, Web Service'), "sql"=>"source_type", "default_value"=>"File", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeEnum("source_subtype", array("label"=>"Source Subtype", "description"=>"Subtype of Data Source", "allowed_values"=>new ValueSetEnum('Oracle, MySQL, Postgress, MSSQL, SOAP, HTTP-Get, HTTP-Post, XML/RPC, CSV, XML, Excel'), "sql"=>"source_subtype", "default_value"=>"CSV", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("source_path", array("label"=>"Source Path", "description"=>"Path to the icon o the menu", "allowed_values"=>null, "sql"=>"source_path", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeEnum("objects_class", array("label"=>"Objects Class", "description"=>"Class of the objects processed by this task", "allowed_values"=>new ValueSetEnum('bizOrganization, bizContact, bizTeam, bizPerson, bizLocation, bizServer, bizPC, bizNetworkDevice, bizInterface, bizService, bizContract, bizInfraGroup, bizIncidentTicket, bizSoftware, bizApplication, bizPatch, bizWorkgroup, lnkContactRealObject, lnkInterfaces, bizInfraGrouping' ), "sql"=>"objects_class", "default_value"=>'', "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("objects_class", array("label"=>"Objects Class", "description"=>"Class of the objects processed by this task", "allowed_values"=>new ValueSetEnumClasses(), "sql"=>"objects_class", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeEnum("test_mode", array("label"=>"Test Mode", "description"=>"If set to 'Yes' the modifications are not applied", "allowed_values"=>new ValueSetEnum('Yes,No'), "sql"=>"test_mode", "default_value"=>'No', "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeEnum("verbose_mode", array("label"=>"Verbose Mode", "description"=>"If set to 'Yes' extra debug information is added to the log", "allowed_values"=>new ValueSetEnum('Yes,No'), "sql"=>"verbose_mode", "default_value" => 'No', "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeEnum("options", array("label"=>"Options", "description"=>"Reconciliation options", "allowed_values"=>new ValueSetEnum('Full, Update Only, Creation Only'), "sql"=>"options", "default_value"=> 'Full', "is_null_allowed"=>true, "depends_on"=>array()))); diff --git a/application/itopwebpage.class.inc.php b/application/itopwebpage.class.inc.php index b315d17f4..37590b6a3 100644 --- a/application/itopwebpage.class.inc.php +++ b/application/itopwebpage.class.inc.php @@ -158,29 +158,44 @@ EOF $this->AddToMenu("\n"); $this->AddToMenu("\n"); $this->AddToMenu("
      \n"); - $oAppContext = new ApplicationContext(); - // Display the menu - // 1) Application defined menus - $oSearchFilter = $oContext->NewFilter("menuNode"); - $oSearchFilter->AddCondition('parent_id', 0, '='); - $oSearchFilter->AddCondition('type', 'application', '='); - // There may be more criteria added later to have a specific menu based on the user's profile - $oSet = new CMDBObjectSet($oSearchFilter, array('rank' => true)); - while ($oRootMenuNode = $oSet->Fetch()) - { - $oRootMenuNode->DisplayMenu($this, 'application', $oAppContext->GetAsHash()); - } - // 2) User defined menus (Bookmarks) - $oSearchFilter = $oContext->NewFilter("menuNode"); - $oSearchFilter->AddCondition('parent_id', 0, '='); - $oSearchFilter->AddCondition('type', 'user', '='); - $oSearchFilter->AddCondition('user_id', UserRights::GetUserId(), '='); - // There may be more criteria added later to have a specific menu based on the user's profile - $oSet = new CMDBObjectSet($oSearchFilter, array('rank' => true)); - while ($oRootMenuNode = $oSet->Fetch()) - { - $oRootMenuNode->DisplayMenu($this, 'user', $oAppContext->GetAsHash()); - } + + // Display the menu + $oAppContext = new ApplicationContext(); + // 1) Application defined menus + $oSearchFilter = $oContext->NewFilter("menuNode"); + $oSearchFilter->AddCondition('parent_id', 0, '='); + $oSearchFilter->AddCondition('type', 'application', '='); + // There may be more criteria added later to have a specific menu based on the user's profile + $oSet = new CMDBObjectSet($oSearchFilter, array('rank' => true)); + while ($oRootMenuNode = $oSet->Fetch()) + { + $oRootMenuNode->DisplayMenu($this, 'application', $oAppContext->GetAsHash()); + } + // 2) User defined menus (Bookmarks) + $oSearchFilter = $oContext->NewFilter("menuNode"); + $oSearchFilter->AddCondition('parent_id', 0, '='); + $oSearchFilter->AddCondition('type', 'user', '='); + $oSearchFilter->AddCondition('user_id', UserRights::GetUserId(), '='); + // There may be more criteria added later to have a specific menu based on the user's profile + $oSet = new CMDBObjectSet($oSearchFilter, array('rank' => true)); + while ($oRootMenuNode = $oSet->Fetch()) + { + $oRootMenuNode->DisplayMenu($this, 'user', $oAppContext->GetAsHash()); + } + // 3) Administrator menu + if (userRights::IsAdministrator()) + { + $oSearchFilter = $oContext->NewFilter("menuNode"); + $oSearchFilter->AddCondition('parent_id', 0, '='); + $oSearchFilter->AddCondition('type', 'administrator', '='); + // There may be more criteria added later to have a specific menu based on the user's profile + $oSet = new CMDBObjectSet($oSearchFilter, array('rank' => true)); + while ($oRootMenuNode = $oSet->Fetch()) + { + $oRootMenuNode->DisplayMenu($this, 'administrator', $oAppContext->GetAsHash()); + } + } + $this->AddToMenu("
    \n"); } diff --git a/application/menunode.class.inc.php b/application/menunode.class.inc.php index 72ddfeb23..23cf78ead 100644 --- a/application/menunode.class.inc.php +++ b/application/menunode.class.inc.php @@ -42,7 +42,7 @@ class menuNode extends DBObject MetaModel::Init_AddAttribute(new AttributeString("hyperlink", array("label"=>"Hyperlink", "description"=>"Hyperlink to the page", "allowed_values"=>null, "sql"=>"hyperlink", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("icon_path", array("label"=>"Menu Icon", "description"=>"Path to the icon o the menu", "allowed_values"=>null, "sql"=>"icon_path", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeText("template", array("label"=>"Template", "description"=>"HTML template for the view", "allowed_values"=>null, "sql"=>"template", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeEnum("type", array("label"=>"Type", "description"=>"Type of menu", "allowed_values"=>new ValueSetEnum('application,user'), "sql"=>"type", "default_value"=>"application", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("type", array("label"=>"Type", "description"=>"Type of menu", "allowed_values"=>new ValueSetEnum('application,user,administrator'), "sql"=>"type", "default_value"=>"application", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeInteger("rank", array("label"=>"Display rank", "description"=>"Sort order for displaying the menu", "allowed_values"=>null, "sql"=>"rank", "default_value" => 999, "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalKey("parent_id", array("label"=>"Parent Menu Item", "description"=>"Parent Menu Item", "allowed_values"=>null, "sql"=>"parent_id", "targetclass"=>"menuNode", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("parent_name", array("label"=>"Parent Menu Item", "description"=>"Parent Menu Item", "allowed_values"=>null, "extkey_attcode"=>"parent_id", "target_attcode"=>"name"))); @@ -91,17 +91,25 @@ class menuNode extends DBObject public function GetChildNodesSet($sType = null) { - $oSearchFilter = new DBObjectSearch("menuNode"); - $oSearchFilter->AddCondition('parent_id', $this->GetKey(), '='); - if ($sType != null) + $aParams = array(); + + if ($sType == 'user') { - $oSearchFilter->AddCondition('type', $sType, '='); - if ($sType == 'user') - { - $oSearchFilter->AddCondition('user_id', UserRights::GetUserId(), '='); - } + $sSelectChilds = 'SELECT menuNode AS m WHERE m.parent_id = :parent AND type = :type AND m.user_id = :user'; + $aParams = array('parent' => $this->GetKey(), 'type' => $sType, 'user' => UserRights::GetUserId()); } - $oSet = new CMDBObjectSet($oSearchFilter, array('rank' => true)); + elseif ($sType != null) + { + $sSelectChilds = 'SELECT menuNode AS m WHERE m.parent_id = :parent AND type = :type'; + $aParams = array('parent' => $this->GetKey(), 'type' => $sType); + } + else + { + $sSelectChilds = 'SELECT menuNode AS m WHERE m.parent_id = :parent'; + $aParams = array('parent' => $this->GetKey()); + } + $oSearchFilter = DBObjectSearch::FromOQL($sSelectChilds); + $oSet = new CMDBObjectSet($oSearchFilter, array('rank' => true), $aParams); return $oSet; } diff --git a/application/ui.linkswidget.class.inc.php b/application/ui.linkswidget.class.inc.php index de602bdd0..5c4bd7d6c 100644 --- a/application/ui.linkswidget.class.inc.php +++ b/application/ui.linkswidget.class.inc.php @@ -21,6 +21,7 @@ class UILinksWidget { $sHTMLValue = ''; $sTargetClass = self::GetTargetClass($this->m_sClass, $this->m_sAttCode); + // #@# todo - add context information, otherwise any value will be authorized for external keys $aAllowedValues = MetaModel::GetAllowedValues_att($this->m_sClass, $this->m_sAttCode, array(), ''); $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sAttCode); $sExtKeyToRemote = $oAttDef->GetExtKeyToRemote(); @@ -120,6 +121,7 @@ class UILinksWidget */ static public function Autocomplete(web_page $oPage, UserContext $oContext, $sClass, $sAttCode, $sName, $iMaxCount) { + // #@# todo - add context information, otherwise any value will be authorized for external keys $aAllowedValues = MetaModel::GetAllowedValues_att($sClass, $sAttCode, array() /* $aArgs */, $sName); if ($aAllowedValues != null) { @@ -154,12 +156,13 @@ class UILinksWidget * This static function is called by the Ajax Page display a set of objects being linked * to the object being created * @param $oPage web_page The ajax page used for the put^put (sent back to the browser - * @param $sClass string The name of the class 'linking class' which is the class of the objects to display - * @param $sAttCode string The name of the attribute is the main object being created + * @param $sClass string The name of the 'linking class' which is the class of the objects to display * @param $sSet JSON serialized set of objects + * @param $sExtKeyToMe Name of the attribute in sClass that is pointing to a given object + * @param $iObjectId The id of the object $sExtKeyToMe is pointing to * @return void */ - static public function RenderSet($oPage, $sClass, $sJSONSet, $sExtKeyToMe) + static public function RenderSet($oPage, $sClass, $sJSONSet, $sExtKeyToMe, $sExtKeyToRemote, $iObjectId) { $aSet = json_decode($sJSONSet, true); // true means hash array instead of object $oSet = CMDBObjectSet::FromScratch($sClass); @@ -182,7 +185,7 @@ class UILinksWidget } $oSet->AddObject($oObj); } - cmdbAbstractObject::DisplaySet($oPage, $oSet, $sExtKeyToMe); + cmdbAbstractObject::DisplaySet($oPage, $oSet, $sExtKeyToMe, true /*menu*/, false /*select*/, $iObjectId, $sExtKeyToRemote); } diff --git a/business/incident.business.php b/business/incident.business.php index 5a130959a..fa275b150 100644 --- a/business/incident.business.php +++ b/business/incident.business.php @@ -55,7 +55,9 @@ class bizIncidentTicket extends cmdbAbstractObject MetaModel::Init_AddAttribute(new AttributeDate("next_update", array("label"=>"Next update", "description"=>"next time the Ticket is expected to be modified", "allowed_values"=>null, "sql"=>"next_update", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeDate("end_date", array("label"=>"Closed Date", "description"=>"Date when the Ticket was closed", "allowed_values"=>null, "sql"=>"closed_date", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeExternalKey("caller_id", array("targetclass"=>"bizPerson", "jointype"=> "", "label"=>"Caller", "description"=>"person that trigger incident", "allowed_values"=>null, "sql"=>"caller_id", "is_null_allowed"=>false, "depends_on"=>array()))); + //MetaModel::Init_AddAttribute(new AttributeExternalKey("caller_id", array("targetclass"=>"bizPerson", "jointype"=> "", "label"=>"Caller", "description"=>"person that trigger incident", "allowed_values"=>null, "sql"=>"caller_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("caller_id", array("targetclass"=>"bizPerson", "jointype"=> "", "label"=>"Caller", "description"=>"person that trigger incident", "allowed_values"=>new ValueSetObjects('SELECT bizPerson AS p WHERE p.org_id = :this->customer_id'), "sql"=>"caller_id", "is_null_allowed"=>false, "depends_on"=>array('customer_id')))); + //MetaModel::Init_AddAttribute(new AttributeExternalKey("caller_id", array("targetclass"=>"bizPerson", "jointype"=> "", "label"=>"Caller", "description"=>"person that trigger incident", "allowed_values"=>new ValueSetObjects('SELECT bizPerson AS p WHERE p.org_id = 1'), "sql"=>"caller_id", "is_null_allowed"=>false, "depends_on"=>array('customer_id')))); MetaModel::Init_AddAttribute(new AttributeExternalField("caller_mail", array("label"=>"Caller", "description"=>"Person that trigger this incident", "allowed_values"=>null, "extkey_attcode"=> 'caller_id', "target_attcode"=>"email"))); MetaModel::Init_AddAttribute(new AttributeString("impact", array("label"=>"Impact", "description"=>"Impact of the Incident", "allowed_values"=>null, "sql"=>"impact", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php index d1af378a4..865da0625 100644 --- a/core/attributedef.class.inc.php +++ b/core/attributedef.class.inc.php @@ -178,6 +178,13 @@ abstract class AttributeDefinition { return str_replace($sSeparator, $sSepEscape, $sValue); } + + public function GetAllowedValues($aArgs = array(), $sBeginsWith = '') + { + $oValSetDef = $this->GetValuesDef(); + if (!$oValSetDef) return null; + return $oValSetDef->GetValues($aArgs, $sBeginsWith); + } } /** @@ -490,7 +497,10 @@ class AttributeString extends AttributeDBField } public function RealValueToSQLValue($value) { - assert(is_string($value)); + if (!is_string($value)) + { + throw new CoreWarning('Expected the attribute value to be a string', array('found_type' => gettype($value), 'value' => $value, 'class' => $this->GetCode(), 'attribute' => $this->GetHostClass())); + } return $value; } public function SQLValueToRealValue($value) @@ -499,6 +509,29 @@ class AttributeString extends AttributeDBField } } + +/** + * Map a varchar column (size < ?) to an attribute that must never be shown to the user + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class AttributePassword extends AttributeString +{ + static protected function ListExpectedParams() + { + return parent::ListExpectedParams(); + //return array_merge(parent::ListExpectedParams(), array()); + } + + public function GetEditClass() {return "Password";} + public function GetDBFieldType() {return "VARCHAR(64)";} +} + /** * Map a text column (size > ?) to an attribute * @@ -778,7 +811,7 @@ class AttributeExternalKey extends AttributeDBFieldVoid } // overloaded here so that an ext key always have the answer to - // "what are you possible values?" + // "what are your possible values?" public function GetValuesDef() { $oValSetDef = $this->Get("allowed_values"); @@ -788,7 +821,21 @@ class AttributeExternalKey extends AttributeDBFieldVoid $oValSetDef = new ValueSetObjects($this->GetTargetClass()); } return $oValSetDef; - } + } + + public function GetAllowedValues($aArgs = array(), $sBeginsWith = '') + { + try + { + return parent::GetAllowedValues($aArgs, $sBeginsWith); + } + catch (MissingQueryArgument $e) + { + // Some required arguments could not be found, enlarge to any existing value + $oValSetDef = new ValueSetObjects($this->GetTargetClass()); + return $oValSetDef->GetValues($aArgs, $sBeginsWith); + } + } } /** diff --git a/core/dbobjectsearch.class.php b/core/dbobjectsearch.class.php index e30d3a5fa..03020556f 100644 --- a/core/dbobjectsearch.class.php +++ b/core/dbobjectsearch.class.php @@ -16,7 +16,6 @@ /** * Sibusql - value set start * @package iTopORM - * @info zis is private */ define('VS_START', '{'); /** @@ -465,7 +464,7 @@ class DBObjectSearch public function RenderCondition() { - return $this->m_oSearchCondition->Render($this->m_aParams); + return $this->m_oSearchCondition->Render($this->m_aParams, true); } public function serialize() @@ -507,7 +506,15 @@ class DBObjectSearch $sRelCode = $aRelatedTo['relcode']; $iMaxDepth = $aRelatedTo['maxdepth']; - $sValue .= "T:".$oFilter->serialize().":$sRelCode:$iMaxDepth"; + $sValue .= "T:".$oFilter->serialize().":$sRelCode:$iMaxDepth\n"; + } + if (count($this->m_aParams) > 0) + { + foreach($this->m_aParams as $sName => $sArgValue) + { + // G stands for arGument + $sValue .= "G:$sName:$sArgValue\n"; + } } return base64_encode($sValue); } @@ -554,6 +561,11 @@ class DBObjectSearch $sRelCode = $aCondition[2]; $iMaxDepth = $aCondition[3]; $oFilter->AddCondition_RelatedTo($oSubFilter, $sRelCode, $iMaxDepth); + break; + case "G": + $oFilter->m_aParams[$aCondition[1]] = $aCondition[2]; + break; + default: throw new CoreException("invalid filter definition (cannot unserialize the data, clear text = '$sClearText')"); } @@ -600,6 +612,22 @@ class DBObjectSearch public function ToOQL(&$aParams = null) { $bRetrofitParams = (!is_null($aParams)); + if (is_null($aParams)) + { + if (count($this->m_aParams) > 0) + { + $aParams = $this->m_aParams; + } + $bRetrofitParams = false; + } + else + { + if (count($this->m_aParams) > 0) + { + $aParams = array_merge($aParams, $this->m_aParams); + } + $bRetrofitParams = true; + } $sRes = "SELECT ".$this->GetClass().' AS '.$this->GetClassAlias(); $sRes .= $this->ToOQL_Joins(); diff --git a/core/expression.class.inc.php b/core/expression.class.inc.php index 834ac47b7..82b3e61b3 100644 --- a/core/expression.class.inc.php +++ b/core/expression.class.inc.php @@ -11,6 +11,11 @@ * @since 1.0 * @version 1.1.1.1 $ */ + +class MissingQueryArgument extends CoreException +{ +} + abstract class Expression { // recursive translation of identifiers @@ -301,7 +306,7 @@ class VariableExpression extends UnaryExpression // recursive rendering public function Render(&$aArgs = null, $bRetrofitParams = false) { - if (is_null($aArgs) || $bRetrofitParams) + if (is_null($aArgs)) { return ':'.$this->m_sName; } @@ -309,9 +314,14 @@ class VariableExpression extends UnaryExpression { return CMDBSource::Quote($aArgs[$this->m_sName]); } + elseif ($bRetrofitParams) + { + //$aArgs[$this->m_sName] = null; + return ':'.$this->m_sName; + } else { - throw new CoreException('Missing query argument', array('expecting'=>$this->m_sName, 'available'=>$aArgs)); + throw new MissingQueryArgument('Missing query argument', array('expecting'=>$this->m_sName, 'available'=>$aArgs)); } } } diff --git a/core/filterdef.class.inc.php b/core/filterdef.class.inc.php index 371eadbd5..8a2d7d096 100644 --- a/core/filterdef.class.inc.php +++ b/core/filterdef.class.inc.php @@ -209,7 +209,13 @@ class FilterFromAttribute extends FilterDefinition { $oAttDef = $this->Get("refattribute"); return $oAttDef->GetValuesDef(); - } + } + + public function GetAllowedValues($aArgs = array(), $sBeginsWith = '') + { + $oAttDef = $this->Get("refattribute"); + return $oAttDef->GetAllowedValues($aArgs, $sBeginsWith); + } public function GetOperators() { @@ -235,62 +241,4 @@ class FilterFromAttribute extends FilterDefinition } } -/** - * Match against a given column (experimental -to be cleaned up later) - * - * @package iTopORM - * @author Romain Quetiez - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL - * @link www.itop.com - * @since 1.0 - * @version $itopversion$ - */ -class FilterDBValues extends FilterDefinition -{ - static protected function ListExpectedParams() - { - return array_merge(parent::ListExpectedParams(), array("dbfield")); - } - - public function GetType() {return "Values from DB";} - public function GetTypeDesc() {return "Match against the existing values in a field";} - - public function GetLabel() - { - return "enum de valeurs DB"; - } - - public function GetValuesDef() - { - return null; - } - - public function GetOperators() - { - return array( - "IN"=>"in", - ); - } - public function GetLooseOperator() - { - return "IN"; - } - - public function GetFilterSQLExpr($sOpCode, $value) - { - $sFieldName = $this->Get("dbfield"); - if (is_array($value) && !empty($value)) - { - $sValueList = "'".implode("', '", $value)."'"; - return "$sFieldName IN ($sValueList)"; - } - return "1=1"; - } - - public function TemporaryGetSQLCol() - { - return $this->Get("dbfield"); - } -} - ?> diff --git a/core/metamodel.class.php b/core/metamodel.class.php index 5fe13c140..6d24d113e 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -665,19 +665,13 @@ abstract class MetaModel public static function GetAllowedValues_att($sClass, $sAttCode, $aArgs = array(), $sBeginsWith = '') { $oAttDef = self::GetAttributeDef($sClass, $sAttCode); - if (!$oAttDef) return null; - $oValSetDef = $oAttDef->GetValuesDef(); - if (!$oValSetDef) return null; - return $oValSetDef->GetValues($aArgs, $sBeginsWith); + return $oAttDef->GetAllowedValues($aArgs, $sBeginsWith); } public static function GetAllowedValues_flt($sClass, $sFltCode, $aArgs = array(), $sBeginsWith = '') { $oFltDef = self::GetClassFilterDef($sClass, $sFltCode); - if (!$oFltDef) return null; - $oValSetDef = $oFltDef->GetValuesDef(); - if (!$oValSetDef) return null; - return $oValSetDef->GetValues($aArgs, $sBeginsWith); + return $oFltDef->GetAllowedValues($aArgs, $sBeginsWith); } // @@ -820,6 +814,18 @@ abstract class MetaModel self::$m_aFilterDefs[$sClass][$sClassAttCode] = $oClassFlt; self::$m_aFilterOrigins[$sClass][$sClassAttCode] = self::GetRootClass($sClass); } + + // Define defaults values for the standard ZLists + // + foreach (self::$m_aListInfos as $sListCode => $aListConfig) + { + if (!isset(self::$m_aListData[$sClass][$sListCode])) + { + $aAllAttributes = array_keys(self::$m_aAttribDefs[$sClass]); + self::$m_aListData[$sClass][$sListCode] = $aAllAttributes; + //echo "

    $sClass: $sListCode (".count($aAllAttributes)." attributes)

    \n"; + } + } } } diff --git a/core/userrights.class.inc.php b/core/userrights.class.inc.php index 25b546920..0e8cfb21f 100644 --- a/core/userrights.class.inc.php +++ b/core/userrights.class.inc.php @@ -45,6 +45,8 @@ define('UR_ACTION_APPLICATION_DEFINED', 10000); // Application specific actions abstract class UserRightsAddOnAPI { abstract public function Setup(); // initial installation + abstract public function CreateAdministrator($sAdminUser, $sAdminPwd); // could be used during initial installation + abstract public function Init(); // loads data (possible optimizations) abstract public function CheckCredentials($sLogin, $sPassword); // returns the id of the user or false abstract public function GetUserId($sLogin); // returns the id of the user or false @@ -52,6 +54,7 @@ abstract class UserRightsAddOnAPI abstract public function IsActionAllowed($iUserId, $sClass, $iActionCode, dbObjectSet $oInstances); abstract public function IsStimulusAllowed($iUserId, $sClass, $sStimulusCode, dbObjectSet $oInstances); abstract public function IsActionAllowedOnAttribute($iUserId, $sClass, $sAttCode, $iActionCode, dbObjectSet $oInstances); + abstract public function IsAdministrator($iUserId); } @@ -94,6 +97,11 @@ class UserRights self::$m_iRealUserId = 0; } + public static function GetModuleInstance() + { + return self::$m_oAddOn; + } + // Installation: create the very first user public static function CreateAdministrator($sAdminUser, $sAdminPwd) { @@ -236,6 +244,20 @@ class UserRights return self::$m_oAddOn->IsActionAllowedOnAttribute($iUserId, $sClass, $sAttCode, $iActionCode, $oInstances); } } + + public static function IsAdministrator($iUserId = null) + { + if (!self::CheckLogin()) return false; + + if (is_null($iUserId)) + { + return self::$m_oAddOn->IsAdministrator(self::$m_iUserId); + } + else + { + return self::$m_oAddOn->IsAdministrator($iUserId); + } + } } diff --git a/core/valuesetdef.class.inc.php b/core/valuesetdef.class.inc.php index 2adae3aff..393a06baa 100644 --- a/core/valuesetdef.class.inc.php +++ b/core/valuesetdef.class.inc.php @@ -19,8 +19,6 @@ abstract class ValueSetDefinition { protected $m_bIsLoaded = false; protected $m_aValues = array(); - protected $m_aArgsObj = array(); - protected $m_aArgsApp = array(); // Displayable description that could be computed out of the std usage context @@ -64,15 +62,6 @@ abstract class ValueSetDefinition return $aRet; } - public function ListArgsFromContextApp() - { - return $this->m_aArgsObj; - } - public function ListArgsFromContextObj() - { - return $this->m_aArgsApp; - } - abstract protected function LoadValues($aArgs); } @@ -109,7 +98,7 @@ class ValueSetObjects extends ValueSetDefinition if (empty($this->m_sValueAttCode)) { - $this->m_sValueAttCode = MetaModel::GetNameAttributeCode($oFilter->GetClass()); + $this->m_sValueAttCode = MetaModel::GetNameAttributeCode($oFilter->GetClass()); } $oObjects = new DBObjectSet($oFilter, $this->m_aOrderBy, $aArgs); @@ -127,78 +116,6 @@ class ValueSetObjects extends ValueSetDefinition } -/** - * Set of existing values for an attribute, given a search filter and a relation id - * - * @package iTopORM - * @author Romain Quetiez - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL - * @link www.itop.com - * @since 1.0 - * @version $itopversion$ - */ -class ValueSetRelatedObjects extends ValueSetObjects -{ - public function __construct($sFilterExp, $sRelCode, $sClass, $sValueAttCode = '', $aOrderBy = array()) - { - $sFullFilterExp = "$sClass: RELATED ($sRelCode, 1) TO ($sFilterExp)"; - parent::__construct($sFullFilterExp, $sValueAttCode, $aOrderBy); - } -} - - -/** - * Set oof existing values for an attribute, given a set of objects (AttributeLinkedSet) - * - * @package iTopORM - * @author Romain Quetiez - * @license http://www.opensource.org/licenses/lgpl-license.php LGPL - * @link www.itop.com - * @since 1.0 - * @version $itopversion$ - */ -class ValueSetRelatedObjectsFromLinkedSet extends ValueSetDefinition -{ - protected $m_sLinkedSetAttCode; - protected $m_sRelCode; - protected $m_sValueAttCode; - protected $m_aOrderBy; - - public function __construct($sLinkedSetAttCode, $sRelCode, $sValueAttCode = '', $aOrderBy = array()) - { - $this->m_sLinkedSetAttCode = $sLinkedSetAttCode; - $this->m_sRelCode = $sRelCode; - $this->m_sValueAttCode = $sValueAttCode; - $this->m_aOrderBy = $aOrderBy; - } - - protected function LoadValues($aArgs) - { - $this->m_aValues = array(); - - if (empty($this->m_sValueAttCode)) - { - $this->m_sValueAttCode = MetaModel::GetNameAttributeCode($oFilter->GetClass()); - } - - $oCurrentObject = @$aArgs['*this*']; - if (!is_object($oCurrentObject)) return false; - - $oObjects = $oCurrentObject->Get($this->m_sLinkedSetAttCode); - while ($oObject = $oObjects->Fetch()) - { - $this->m_aValues[$oObject->GetKey()] = $oObject->Get($this->m_sValueAttCode); - } - return true; - } - - public function GetValuesDescription() - { - return 'Objects related ('.$this->m_sRelCode.') to objects linked through '.$this->m_sLinkedSetAttCode; - } -} - - /** * Fixed set values (could be hardcoded in the business model) * diff --git a/js/linkswidget.js b/js/linkswidget.js index 61f63247d..69d890351 100644 --- a/js/linkswidget.js +++ b/js/linkswidget.js @@ -29,7 +29,7 @@ function LinksWidget(id, sLinkedClass, sExtKeyToMe, sExtKeyToRemote, aAttributes { sLinks = JSON.stringify(this.aLinks); $('#'+this.id).val(sLinks); - $('#'+this.id+'_values').load('ajax.render.php?operation=ui.linkswidget.linkedset&sclass='+this.sLinkedClass+'&sextkeytome='+this.sExtKeyToMe, + $('#'+this.id+'_values').load('ajax.render.php?operation=ui.linkswidget.linkedset&sclass='+this.sLinkedClass+'&sextkeytome='+this.sExtKeyToMe+'&sextkeytoremote='+this.sExtKeyToRemote+'&myid='+this.id, {'sset' : sLinks}); } } diff --git a/pages/UniversalSearch.php b/pages/UniversalSearch.php index bb4e15647..a12ab5292 100644 --- a/pages/UniversalSearch.php +++ b/pages/UniversalSearch.php @@ -96,7 +96,7 @@ EOF; $bChildItem = utils::ReadPostedParam('child_item', false); $oMenuNode->Set('label', $sDescription); $oMenuNode->Set('name', $sLabel); - $oMenuNode->Set('icon_path', '/images/std_view.gif'); + $oMenuNode->Set('icon_path', '../images/std_view.gif'); $oMenuNode->Set('template', $sMenuNodeContent); $oMenuNode->Set('hyperlink', 'UI.php'); $oMenuNode->Set('type', 'user'); diff --git a/pages/ajax.render.php b/pages/ajax.render.php index a90059eeb..4461cef6b 100644 --- a/pages/ajax.render.php +++ b/pages/ajax.render.php @@ -84,6 +84,8 @@ switch($operation) } foreach($oWizardHelper->GetFieldsForAllowedValues() as $sAttCode) { + // MetaModel::GetAllowedValues_att() => array(id => value) + // Improvement: what if the list is too long? $oWizardHelper->SetAllowedValuesHtml($sAttCode, "Possible values ($sAttCode)"); } $oPage->add($oWizardHelper->ToJSON()); @@ -192,7 +194,9 @@ switch($operation) $sClass = utils::ReadParam('sclass', 'bizContact'); $sJSONSet = stripslashes(utils::ReadParam('sset', '')); $sExtKeyToMe = utils::ReadParam('sextkeytome', ''); - UILinksWidget::RenderSet($oPage, $sClass, $sJSONSet, $sExtKeyToMe); + $sExtKeyToRemote = utils::ReadParam('sextkeytoremote', ''); + $iObjectId = utils::ReadParam('id', -1); + UILinksWidget::RenderSet($oPage, $sClass, $sJSONSet, $sExtKeyToMe, $sExtKeyToRemote, $iObjectId); break; case 'autocomplete': @@ -208,6 +212,7 @@ switch($operation) if ($oThis = MetaModel::GetObject($sClass, $key)) { $aArgs['*this*'] = $oThis; + $aArgs['this'] = $oThis; } } $aAllowedValues = MetaModel::GetAllowedValues_att($sClass, $sAttCode, $aArgs, $sName); diff --git a/pages/index.php b/pages/index.php index 33c4d77c0..f8d93ec62 100644 --- a/pages/index.php +++ b/pages/index.php @@ -87,7 +87,7 @@ function DisplayDetails(web_page $oPage, $sClassName, $sKey) global $oContext; //$oObj = MetaModel::GetObject($sClassName, $sKey); $oObj = $oContext->GetObject($sClassName, $sKey); - $oPage->p("Details of ".get_class($oObj)." - $sKey"); + $oPage->p("Details of ".MetaModel::GetName($sClassName)." - $sKey"); $oObj->DisplayDetails($oPage); @@ -140,7 +140,7 @@ function DisplayChangesLog(web_page $oPage, $sClassName, $sKey) global $oContext; //$oObj = MetaModel::GetObject($sClassName, $sKey); $oObj = $oContext->GetObject($sClassName, $sKey); - $oPage->p("Changes log for ".get_class($oObj)." - $sKey"); + $oPage->p("Changes log for ".MetaModel::GetName($sClassName)." - $sKey"); $oObj->DisplayChangesLog($oPage); @@ -219,7 +219,7 @@ function DisplayEditForm(web_page $oPage, $sClassName, $sKey) $oPage->p("You are not allowed to edit this object."); return; } - $oPage->p("Edition of ".get_class($oObj)." - $sKey\n"); + $oPage->p("Edition of ".MetaModel::GetName($sClassName)." - $sKey\n"); $aDetails = array(); $oPage->add("
    \n"); @@ -332,7 +332,7 @@ function UpdateObject(web_page $oPage, $sClassName, $sKey, $aAttributes) $iChangeId = $oMyChange->DBInsert(); $oObj->DBUpdateTracked($oMyChange); - $oPage->p(get_class($oObj)." updated\n"); + $oPage->p(MetaModel::GetName($sClassName)." updated\n"); } else { @@ -342,13 +342,14 @@ function UpdateObject(web_page $oPage, $sClassName, $sKey, $aAttributes) // By Rom // $oObj->DisplayDetails($oPage); // replaced by... - DisplayDetails($oPage, get_class($oObj), $oObj->GetKey()); + DisplayDetails($oPage, $sClassName, $sKey); $oPage->p("Return to main page"); } function DeleteObject(web_page $oPage, $sClassName, $sKey) { global $oContext; + $sClassLabel = MetaModel::GetName($sClassName); //$oObj = MetaModel::GetObject($sClassName, $sKey); $oObj = $oContext->GetObject($sClassName, $sKey); if ($oObj == null) @@ -356,7 +357,7 @@ function DeleteObject(web_page $oPage, $sClassName, $sKey) $oPage->p("You are not allowed to delete this object."); return; } - $oPage->p("Deletion of $sClassName - $sKey"); + $oPage->p("Deletion of $sClassLabel - $sKey"); if ($oObj->CheckToDelete()) { @@ -368,13 +369,13 @@ function DeleteObject(web_page $oPage, $sClassName, $sKey) $iChangeId = $oMyChange->DBInsert(); $oObj->DBDeleteTracked($oMyChange); - $oPage->p("$sClassName deleted\n"); + $oPage->p("$sClassLabel deleted\n"); } else { $oPage->p("Error: object can not be deleted!\n"); // By Rom - DisplayDetails($oPage, get_class($oObj), $oObj->GetKey()); + DisplayDetails($oPage, $sClassName, $sKey); } $oPage->p("Return to main page"); } @@ -382,7 +383,8 @@ function DeleteObject(web_page $oPage, $sClassName, $sKey) function CreateObject(web_page $oPage, $sClassName, $aAttributes) { $oObj = MetaModel::NewObject($sClassName); - $oPage->p("Creation of ".get_class($oObj)." object."); + $sClassLabel = MetaModel::GetName(get_class($oObj)); + $oPage->p("Creation of $sClassLabel object."); foreach(MetaModel::ListAttributeDefs(get_class($oObj)) as $sAttCode=>$oAttDef) { @@ -401,7 +403,7 @@ function CreateObject(web_page $oPage, $sClassName, $aAttributes) $iChangeId = $oMyChange->DBInsert(); $oObj->DBInsertTracked($oMyChange); - $oPage->p(get_class($oObj)." created\n"); + $oPage->p($sClassLabel." created\n"); // By Rom // $oObj->DisplayDetails($oPage); @@ -419,6 +421,7 @@ function CreateObject(web_page $oPage, $sClassName, $aAttributes) function AddLinks($oPage, $sClassName, $sKey, $sLinkClass, $sExtKeyToMe, $sExtKeyToPartner, $sFilter) { global $oContext; + $sClassLabel = MetaModel::GetName($sClassName); //$oObj = MetaModel::GetObject($sClassName, $sKey); $oObj = $oContext->GetObject($sClassName, $sKey); if ($oObj == null) @@ -426,7 +429,7 @@ function AddLinks($oPage, $sClassName, $sKey, $sLinkClass, $sExtKeyToMe, $sExtKe $oPage->p("You are not allowed to modify (create links on) this object."); return; } - $oPage->p("Creating links for $sClassName - $sKey"); + $oPage->p("Creating links for $sClassLabel - $sKey"); $oFilter = CMDBSearchFilter::unserialize($sFilter); $oPage->p("Linking to ".$oFilter->__DescribeHTML()); @@ -447,7 +450,7 @@ function AddLinks($oPage, $sClassName, $sKey, $sLinkClass, $sExtKeyToMe, $sExtKe $iChangeId = $oMyChange->DBInsert(); $oNewLink->DBInsertTracked($oMyChange); - $oPage->p(get_class($oNewLink)." created\n"); + $oPage->p(MetaModel::GetName($sLinkClass)." created\n"); } else { @@ -595,19 +598,21 @@ switch($operation) $oPage->add("\n"); foreach( $aTopLevelClasses as $sClassName) { + $sClassLabel = MetaModel::GetName($sClassName); $oPage->add("
    "); if (count(MetaModel::GetSubclasses($sClassName)) > 0) { $sActiveSubclass = ReadParam('subclassname', ''); foreach(MetaModel::GetSubclasses($sClassName) as $sSubclassName) { + $sSubclassLabel = MetaModel::GetName($sSubclassName); //$oSearchFilter = new CMDBSearchFilter($sSubclassName); $oSearchFilter = $oContext->NewFilter($sSubclassName); $oSearchFilter ->AddCondition('org_id', $sCurrentOrganization, '='); $oPage->add("
    \n"); $oPage->add("
    \n"); - $oPage->p("$sSubclassName - ".MetaModel::GetClassDescription($sSubclassName)); + $oPage->p("$sSubclassLabel - ".MetaModel::GetClassDescription($sSubclassName)); $oPage->add("\n"); $oPage->add("\n"); $oPage->add("\n"); @@ -634,7 +639,7 @@ switch($operation) $iMatchesCount = $oSet->Count(); if ($iMatchesCount == 0) { - $oPage->p("No $sSubclassName matches these criteria."); + $oPage->p("No $sSubclassLabel matches these criteria."); $oPage->small_p("(".$oSearchFilter->__DescribeHTML().")"); } else @@ -642,7 +647,7 @@ switch($operation) $oPage->p("$iMatchesCount item(s) found."); cmdbAbstractObject::DisplaySet($oPage, $oSet); } - $oPage->p("Create a new $sSubclassName\n"); + $oPage->p("Create a new $sSubclassLabel\n"); $oPage->add("
    \n"); } } @@ -655,7 +660,7 @@ switch($operation) $oPage->add("
    \n"); $oPage->add("
    \n"); - $oPage->p("$sClassName - ".MetaModel::GetClassDescription($sClassName)); + $oPage->p("$sClassLabel - ".MetaModel::GetClassDescription($sClassName)); $oPage->add("\n"); $oPage->add("\n"); $oPage->add("\n"); @@ -681,7 +686,7 @@ switch($operation) $iMatchesCount = $oSet->Count(); if ($iMatchesCount == 0) { - $oPage->p("No $sClassName matches these criteria."); + $oPage->p("No $sClassLabel matches these criteria."); $oPage->small_p("(".$oSearchFilter->__DescribeHTML().")"); } else @@ -690,7 +695,7 @@ switch($operation) cmdbAbstractObject::DisplaySet($oPage, $oSet); $oPage->small_p("(".$oSearchFilter->__DescribeHTML().")"); } - $oPage->p("Create a new $sClassName\n"); + $oPage->p("Create a new $sClassLabel\n"); $oPage->add("
    \n"); $oPage->add("
    \n"); } diff --git a/pages/schema.php b/pages/schema.php index f3a4ba52e..e985d1908 100644 --- a/pages/schema.php +++ b/pages/schema.php @@ -333,8 +333,8 @@ function DisplayClassDetails($oPage, $sClass) { $sValue = $oAttDef->GetDescription(); } - $sType = $oAttDef->GetType().' ('.$oAttDef->GetTypeDesc().')'; - $sOrigin = MetaModel::GetAttributeOrigin($sClass, $sAttCode); + $sType = $oAttDef->GetType().' ('.$oAttDef->GetTypeDesc().')'; + $sOrigin = MetaModel::GetAttributeOrigin($sClass, $sAttCode); $sAllowedValues = ""; $oAllowedValuesDef = $oAttDef->GetValuesDef(); $sMoreInfo = ""; diff --git a/pages/usermanagement_userstatus.php b/pages/usermanagement_userstatus.php index 8b243ff2a..4468a0cca 100644 --- a/pages/usermanagement_userstatus.php +++ b/pages/usermanagement_userstatus.php @@ -263,6 +263,23 @@ if ($iUser == -1) } else { + $oPage->p('

    How is it computing the user rights?

    '); + + $oPage->p('

    1st, find the profiles that apply

    '); + $oPage->p('

    Project the current object in every existing dimension

    '); + $oPage->p('

    Project the observed profile in every existing dimension (might depend on the user)

    '); + $oPage->p('

    If an overlap is found in any dimension, then the profile applies

    '); + + $oPage->p('

    2nd, interpret the profiles

    '); + $oPage->p('

    Note: granting rights for specific attributes is not fully implemented. It is still not taking into account the inheritance of rights AND the UI will not take that information into account!

    '); + $oPage->p('

    Actions: looks into URP_ActionGrant for a permission (yes or no) and goes up into the class hierarchy until an answer is found, defaults to no

    '); + $oPage->p('

    Stimuli: looks into URP_StimulusGrant for a permission (yes or no), defaults to no

    '); + + + $oPage->p('

    3rd, keep the most permissive one

    '); + $oPage->p('

    If one profile says YES, then the answer is YES

    '); + + $oUser = MetaModel::GetObject('URP_Users', $iUser); $oPage->p('

    Projections for user '.$oUser->GetName().'

    '); diff --git a/setup/data/30.profiles.xml b/setup/data/30.profiles.xml new file mode 100644 index 000000000..85c32a027 --- /dev/null +++ b/setup/data/30.profiles.xml @@ -0,0 +1,7 @@ + + + +Delivery Manager France +Persons in charge of the operations for French customers + + diff --git a/setup/data/31.profileprojection.xml b/setup/data/31.profileprojection.xml new file mode 100644 index 000000000..4a16290fe --- /dev/null +++ b/setup/data/31.profileprojection.xml @@ -0,0 +1,15 @@ + + + +1 +100 +1;2 + + + +2 +100 +<any> + + + diff --git a/setup/data/32.actiongrant.xml b/setup/data/32.actiongrant.xml new file mode 100644 index 000000000..6d1d83633 --- /dev/null +++ b/setup/data/32.actiongrant.xml @@ -0,0 +1,1659 @@ + + + +100 +bizOrganization +yes +read + + +100 +bizOrganization +yes +modify + + +100 +bizOrganization +yes +delete + + +100 +bizOrganization +yes +bulk read + + +100 +bizOrganization +yes +bulk modify + + +100 +bizOrganization +yes +bulk delete + + +100 +logRealObject +yes +read + + +100 +logRealObject +yes +modify + + +100 +logRealObject +yes +delete + + +100 +logRealObject +yes +bulk read + + +100 +logRealObject +yes +bulk modify + + +100 +logRealObject +yes +bulk delete + + +100 +bizContact +yes +read + + +100 +bizContact +yes +modify + + +100 +bizContact +yes +delete + + +100 +bizContact +yes +bulk read + + +100 +bizContact +yes +bulk modify + + +100 +bizContact +yes +bulk delete + + +100 +bizPerson +yes +read + + +100 +bizPerson +yes +modify + + +100 +bizPerson +yes +delete + + +100 +bizPerson +yes +bulk read + + +100 +bizPerson +yes +bulk modify + + +100 +bizPerson +yes +bulk delete + + +100 +bizTeam +yes +read + + +100 +bizTeam +yes +modify + + +100 +bizTeam +yes +delete + + +100 +bizTeam +yes +bulk read + + +100 +bizTeam +yes +bulk modify + + +100 +bizTeam +yes +bulk delete + + +100 +bizDocument +yes +read + + +100 +bizDocument +yes +modify + + +100 +bizDocument +yes +delete + + +100 +bizDocument +yes +bulk read + + +100 +bizDocument +yes +bulk modify + + +100 +bizDocument +yes +bulk delete + + +100 +bizDocVersion +yes +read + + +100 +bizDocVersion +yes +modify + + +100 +bizDocVersion +yes +delete + + +100 +bizDocVersion +yes +bulk read + + +100 +bizDocVersion +yes +bulk modify + + +100 +bizDocVersion +yes +bulk delete + + +100 +lnkDocumentRealObject +yes +read + + +100 +lnkDocumentRealObject +yes +modify + + +100 +lnkDocumentRealObject +yes +delete + + +100 +lnkDocumentRealObject +yes +bulk read + + +100 +lnkDocumentRealObject +yes +bulk modify + + +100 +lnkDocumentRealObject +yes +bulk delete + + +100 +lnkContactRealObject +yes +read + + +100 +lnkContactRealObject +yes +modify + + +100 +lnkContactRealObject +yes +delete + + +100 +lnkContactRealObject +yes +bulk read + + +100 +lnkContactRealObject +yes +bulk modify + + +100 +lnkContactRealObject +yes +bulk delete + + +100 +logInfra +yes +read + + +100 +logInfra +yes +modify + + +100 +logInfra +yes +delete + + +100 +logInfra +yes +bulk read + + +100 +logInfra +yes +bulk modify + + +100 +logInfra +yes +bulk delete + + +100 +bizLocation +yes +read + + +100 +bizLocation +yes +modify + + +100 +bizLocation +yes +delete + + +100 +bizLocation +yes +bulk read + + +100 +bizLocation +yes +bulk modify + + +100 +bizLocation +yes +bulk delete + + +100 +bizCircuit +yes +read + + +100 +bizCircuit +yes +modify + + +100 +bizCircuit +yes +delete + + +100 +bizCircuit +yes +bulk read + + +100 +bizCircuit +yes +bulk modify + + +100 +bizCircuit +yes +bulk delete + + +100 +bizInterface +yes +read + + +100 +bizInterface +yes +modify + + +100 +bizInterface +yes +delete + + +100 +bizInterface +yes +bulk read + + +100 +bizInterface +yes +bulk modify + + +100 +bizInterface +yes +bulk delete + + +100 +lnkInterfaces +yes +read + + +100 +lnkInterfaces +yes +modify + + +100 +lnkInterfaces +yes +delete + + +100 +lnkInterfaces +yes +bulk read + + +100 +lnkInterfaces +yes +bulk modify + + +100 +lnkInterfaces +yes +bulk delete + + +100 +bizDevice +yes +read + + +100 +bizDevice +yes +modify + + +100 +bizDevice +yes +delete + + +100 +bizDevice +yes +bulk read + + +100 +bizDevice +yes +bulk modify + + +100 +bizDevice +yes +bulk delete + + +100 +bizPC +yes +read + + +100 +bizPC +yes +modify + + +100 +bizPC +yes +delete + + +100 +bizPC +yes +bulk read + + +100 +bizPC +yes +bulk modify + + +100 +bizPC +yes +bulk delete + + +100 +bizServer +yes +read + + +100 +bizServer +yes +modify + + +100 +bizServer +yes +delete + + +100 +bizServer +yes +bulk read + + +100 +bizServer +yes +bulk modify + + +100 +bizServer +yes +bulk delete + + +100 +bizNetworkDevice +yes +read + + +100 +bizNetworkDevice +yes +modify + + +100 +bizNetworkDevice +yes +delete + + +100 +bizNetworkDevice +yes +bulk read + + +100 +bizNetworkDevice +yes +bulk modify + + +100 +bizNetworkDevice +yes +bulk delete + + +100 +bizInfraGroup +yes +read + + +100 +bizInfraGroup +yes +modify + + +100 +bizInfraGroup +yes +delete + + +100 +bizInfraGroup +yes +bulk read + + +100 +bizInfraGroup +yes +bulk modify + + +100 +bizInfraGroup +yes +bulk delete + + +100 +bizApplication +yes +read + + +100 +bizApplication +yes +modify + + +100 +bizApplication +yes +delete + + +100 +bizApplication +yes +bulk read + + +100 +bizApplication +yes +bulk modify + + +100 +bizApplication +yes +bulk delete + + +100 +lnkInfraGrouping +yes +read + + +100 +lnkInfraGrouping +yes +modify + + +100 +lnkInfraGrouping +yes +delete + + +100 +lnkInfraGrouping +yes +bulk read + + +100 +lnkInfraGrouping +yes +bulk modify + + +100 +lnkInfraGrouping +yes +bulk delete + + +100 +lnkClientServer +yes +read + + +100 +lnkClientServer +yes +modify + + +100 +lnkClientServer +yes +delete + + +100 +lnkClientServer +yes +bulk read + + +100 +lnkClientServer +yes +bulk modify + + +100 +lnkClientServer +yes +bulk delete + + +100 +bizPatch +yes +read + + +100 +bizPatch +yes +modify + + +100 +bizPatch +yes +delete + + +100 +bizPatch +yes +bulk read + + +100 +bizPatch +yes +bulk modify + + +100 +bizPatch +yes +bulk delete + + +100 +bizIncidentTicket +yes +read + + +100 +bizIncidentTicket +no +modify + + +100 +bizIncidentTicket +no +delete + + +100 +bizIncidentTicket +yes +bulk read + + +100 +bizIncidentTicket +no +bulk modify + + +100 +bizIncidentTicket +no +bulk delete + + +100 +lnkRelatedTicket +yes +read + + +100 +lnkRelatedTicket +yes +modify + + +100 +lnkRelatedTicket +yes +delete + + +100 +lnkRelatedTicket +yes +bulk read + + +100 +lnkRelatedTicket +yes +bulk modify + + +100 +lnkRelatedTicket +yes +bulk delete + + +100 +lnkInfraTicket +yes +read + + +100 +lnkInfraTicket +yes +modify + + +100 +lnkInfraTicket +yes +delete + + +100 +lnkInfraTicket +yes +bulk read + + +100 +lnkInfraTicket +yes +bulk modify + + +100 +lnkInfraTicket +yes +bulk delete + + +100 +lnkContactTicket +yes +read + + +100 +lnkContactTicket +yes +modify + + +100 +lnkContactTicket +yes +delete + + +100 +lnkContactTicket +yes +bulk read + + +100 +lnkContactTicket +yes +bulk modify + + +100 +lnkContactTicket +yes +bulk delete + + +100 +bizWorkgroup +yes +read + + +100 +bizWorkgroup +yes +modify + + +100 +bizWorkgroup +yes +delete + + +100 +bizWorkgroup +yes +bulk read + + +100 +bizWorkgroup +yes +bulk modify + + +100 +bizWorkgroup +yes +bulk delete + + +100 +bizContract +yes +read + + +100 +bizContract +yes +modify + + +100 +bizContract +yes +delete + + +100 +bizContract +yes +bulk read + + +100 +bizContract +yes +bulk modify + + +100 +bizContract +yes +bulk delete + + +100 +lnkInfraContract +yes +read + + +100 +lnkInfraContract +yes +modify + + +100 +lnkInfraContract +yes +delete + + +100 +lnkInfraContract +yes +bulk read + + +100 +lnkInfraContract +yes +bulk modify + + +100 +lnkInfraContract +yes +bulk delete + + +100 +lnkContactContract +yes +read + + +100 +lnkContactContract +yes +modify + + +100 +lnkContactContract +yes +delete + + +100 +lnkContactContract +yes +bulk read + + +100 +lnkContactContract +yes +bulk modify + + +100 +lnkContactContract +yes +bulk delete + + +100 +lnkDocumentContract +yes +read + + +100 +lnkDocumentContract +yes +modify + + +100 +lnkDocumentContract +yes +delete + + +100 +lnkDocumentContract +yes +bulk read + + +100 +lnkDocumentContract +yes +bulk modify + + +100 +lnkDocumentContract +yes +bulk delete + + +100 +bizChangeTicket +yes +read + + +100 +bizChangeTicket +no +modify + + +100 +bizChangeTicket +no +delete + + +100 +bizChangeTicket +yes +bulk read + + +100 +bizChangeTicket +no +bulk modify + + +100 +bizChangeTicket +no +bulk delete + + +100 +lnkInfraChangeTicket +yes +read + + +100 +lnkInfraChangeTicket +yes +modify + + +100 +lnkInfraChangeTicket +yes +delete + + +100 +lnkInfraChangeTicket +yes +bulk read + + +100 +lnkInfraChangeTicket +yes +bulk modify + + +100 +lnkInfraChangeTicket +yes +bulk delete + + +100 +lnkContactChange +yes +read + + +100 +lnkContactChange +yes +modify + + +100 +lnkContactChange +yes +delete + + +100 +lnkContactChange +yes +bulk read + + +100 +lnkContactChange +yes +bulk modify + + +100 +lnkContactChange +yes +bulk delete + + +100 +bizKnownError +yes +read + + +100 +bizKnownError +yes +modify + + +100 +bizKnownError +yes +delete + + +100 +bizKnownError +yes +bulk read + + +100 +bizKnownError +yes +bulk modify + + +100 +bizKnownError +yes +bulk delete + + +100 +lnkInfraError +yes +read + + +100 +lnkInfraError +yes +modify + + +100 +lnkInfraError +yes +delete + + +100 +lnkInfraError +yes +bulk read + + +100 +lnkInfraError +yes +bulk modify + + +100 +lnkInfraError +yes +bulk delete + + +100 +lnkDocumentError +yes +read + + +100 +lnkDocumentError +yes +modify + + +100 +lnkDocumentError +yes +delete + + +100 +lnkDocumentError +yes +bulk read + + +100 +lnkDocumentError +yes +bulk modify + + +100 +lnkDocumentError +yes +bulk delete + + +100 +AuditCategory +yes +read + + +100 +AuditCategory +yes +modify + + +100 +AuditCategory +yes +delete + + +100 +AuditCategory +yes +bulk read + + +100 +AuditCategory +yes +bulk modify + + +100 +AuditCategory +yes +bulk delete + + +100 +AuditRule +yes +read + + +100 +AuditRule +yes +modify + + +100 +AuditRule +yes +delete + + +100 +AuditRule +yes +bulk read + + +100 +AuditRule +yes +bulk modify + + +100 +AuditRule +yes +bulk delete + + +100 +menuNode +yes +read + + +100 +menuNode +yes +modify + + +100 +menuNode +yes +delete + + +100 +menuNode +yes +bulk read + + +100 +menuNode +yes +bulk modify + + +100 +menuNode +yes +bulk delete + + +100 +CMDBChange +yes +read + + +100 +CMDBChange +yes +modify + + +100 +CMDBChange +yes +delete + + +100 +CMDBChange +yes +bulk read + + +100 +CMDBChange +yes +bulk modify + + +100 +CMDBChange +yes +bulk delete + + +100 +CMDBChangeOp +yes +read + + +100 +CMDBChangeOp +yes +modify + + +100 +CMDBChangeOp +yes +delete + + +100 +CMDBChangeOp +yes +bulk read + + +100 +CMDBChangeOp +yes +bulk modify + + +100 +CMDBChangeOp +yes +bulk delete + + +100 +CMDBChangeOpCreate +yes +read + + +100 +CMDBChangeOpCreate +yes +modify + + +100 +CMDBChangeOpCreate +yes +delete + + +100 +CMDBChangeOpCreate +yes +bulk read + + +100 +CMDBChangeOpCreate +yes +bulk modify + + +100 +CMDBChangeOpCreate +yes +bulk delete + + +100 +CMDBChangeOpDelete +yes +read + + +100 +CMDBChangeOpDelete +yes +modify + + +100 +CMDBChangeOpDelete +yes +delete + + +100 +CMDBChangeOpDelete +yes +bulk read + + +100 +CMDBChangeOpDelete +yes +bulk modify + + +100 +CMDBChangeOpDelete +yes +bulk delete + + +100 +CMDBChangeOpSetAttribute +yes +read + + +100 +CMDBChangeOpSetAttribute +yes +modify + + +100 +CMDBChangeOpSetAttribute +yes +delete + + +100 +CMDBChangeOpSetAttribute +yes +bulk read + + +100 +CMDBChangeOpSetAttribute +yes +bulk modify + + +100 +CMDBChangeOpSetAttribute +yes +bulk delete + + diff --git a/setup/data/33.attributegrant.xml b/setup/data/33.attributegrant.xml new file mode 100644 index 000000000..cf1461167 --- /dev/null +++ b/setup/data/33.attributegrant.xml @@ -0,0 +1,3 @@ + + + \ No newline at end of file diff --git a/setup/data/34.stimulusgrant.xml b/setup/data/34.stimulusgrant.xml new file mode 100644 index 000000000..47e7e7ecd --- /dev/null +++ b/setup/data/34.stimulusgrant.xml @@ -0,0 +1,189 @@ + + + +100 +bizServer +no +ev_store + + +100 +bizServer +no +ev_ship + + +100 +bizServer +no +ev_plug + + +100 +bizServer +no +ev_configuration_finished + + +100 +bizServer +no +ev_val_failed + + +100 +bizServer +no +ev_mtp + + +100 +bizServer +no +ev_start_change + + +100 +bizServer +no +ev_end_change + + +100 +bizServer +no +ev_decommission + + +100 +bizServer +no +ev_obsolete + + +100 +bizServer +no +ev_recycle + + +100 +bizIncidentTicket +no +ev_assign + + +100 +bizIncidentTicket +no +ev_reassign + + +100 +bizIncidentTicket +no +ev_start_working + + +100 +bizIncidentTicket +no +ev_close + + +100 +bizContract +no +ev_freeze_version + + +100 +bizContract +no +ev_sign + + +100 +bizContract +no +ev_begin + + +100 +bizContract +no +ev_notice + + +100 +bizContract +no +ev_terminate + + +100 +bizContract +no +ev_elapsed + + +100 +bizChangeTicket +yes +ev_validate + + +100 +bizChangeTicket +yes +ev_reject + + +100 +bizChangeTicket +no +ev_reopen + + +100 +bizChangeTicket +no +ev_plan + + +100 +bizChangeTicket +yes +ev_approve + + +100 +bizChangeTicket +no +ev_replan + + +100 +bizChangeTicket +yes +ev_notapprove + + +100 +bizChangeTicket +no +ev_implement + + +100 +bizChangeTicket +no +ev_monitor + + +100 +bizChangeTicket +yes +ev_finish + + diff --git a/setup/data/export.cmd b/setup/data/export.cmd index d7200a315..5e54a4dec 100644 --- a/setup/data/export.cmd +++ b/setup/data/export.cmd @@ -1,6 +1,8 @@ SET WEBROOT=http://localhost:81 -SET USER=Erwan -SET PWD=Taloc +SET EXPORT=%WEBROOT%/webservices/export.php + +SET USER=admin +SET PWD=admin REM The order (numbering) of the files is important since REM it dictates the order to import them back diff --git a/setup/data/structure/1.menus.xml b/setup/data/structure/1.menus.xml index 756cfd4ba..cfea4a792 100644 --- a/setup/data/structure/1.menus.xml +++ b/setup/data/structure/1.menus.xml @@ -1,25 +1,25 @@ - -0 -Admin Tools - + +Tools + UI.php + -7 application +7 +0 +0 - -5 + All Applications UI.php + -999 application +999 +14 +0 - -5 + All Circuits UI.php + -999 application +999 +14 +0 - -64 + All Contracts ./UI.php + -2 application +2 +31 +0 - -5 + All Interfaces UI.php + -999 application +999 +14 +0 - -5 + All Network devices UI.php + -999 application +999 +14 +0 - -5 + All Patches UI.php + -999 application +999 +14 +0 - -5 + All PCs UI.php + -999 application +999 +14 +0 - -5 + All Servers UI.php + -999 application +999 +14 +0 - -1 + Audit ./audit.php + -4 application +4 +15 +0 - -0 + Change Management ./UI.php + -4 application +4 +0 +0 - -66 + Closed Changes UI.php + -2 application +2 +11 +0 - -61 + Closed Incident ./UI.php + -2 application +2 +22 +0 - -1 + Configuration Items UI.php + -2 application +2 +15 +0 - -0 + Configuration Management UI.php + -2 application +2 +0 +0 - -1 + Contacts UI.php - +application +999 +14 +0 + + +All Subnets + +UI.php + + application From e2d60e7c8f5a2b639b1db3e3f1f9354974864c5b Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Fri, 11 Sep 2009 20:35:16 +0000 Subject: [PATCH 073/227] Implementation of AllowedValues for fields that depend on other fields SVN:trunk[148] --- application/cmdbabstract.class.inc.php | 12 +++++----- application/uiwizard.class.inc.php | 1 + application/wizardhelper.class.inc.php | 12 +++++++++- js/forms-json-utils.js | 11 ++++++++- js/wizardhelper.js | 31 +++++++++++++++++++++++++- pages/UI.php | 2 ++ pages/ajax.render.php | 22 ++++++++---------- 7 files changed, 69 insertions(+), 22 deletions(-) diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index 10664f92d..146f88e42 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -601,7 +601,7 @@ abstract class cmdbAbstractObject extends CMDBObject return $sHtml; } - public static function GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $value = '', $sDisplayValue = '', $iId = '', $sNameSuffix = '', $iFlags = 0) + public static function GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $value = '', $sDisplayValue = '', $iId = '', $sNameSuffix = '', $iFlags = 0, $aArgs = array()) { static $iInputId = 0; if (!empty($iId)) @@ -630,7 +630,7 @@ abstract class cmdbAbstractObject extends CMDBObject break; case 'Password': - $sHTMLValue = ""; + $sHTMLValue = ""; break; case 'Text': @@ -645,19 +645,19 @@ abstract class cmdbAbstractObject extends CMDBObject case 'String': default: // #@# todo - add context information (depending on dimensions) - $aAllowedValues = MetaModel::GetAllowedValues_att($sClass, $sAttCode, array()); + $aAllowedValues = MetaModel::GetAllowedValues_att($sClass, $sAttCode, $aArgs); if ($aAllowedValues !== null) { //Enum field or external key, display a combo if (count($aAllowedValues) == 0) { - $sHTMLValue = ""; + $sHTMLValue = ""; } else if (count($aAllowedValues) > 50) { // too many choices, use an autocomplete // The input for the auto complete - $sHTMLValue = ""; + $sHTMLValue = ""; // another hidden input to store & pass the object's Id $sHTMLValue .= "\n"; $oPage->add_ready_script("\$('#label_$iInputId').autocomplete('./ajax.render.php', { minChars:3, onItemSelect:selectItem, onFindValue:findValue, formatItem:formatItem, autoFill:true, keyHolder:'#$iInputId', extraParams:{operation:'autocomplete', sclass:'$sClass',attCode:'".$sAttCode."'}});"); @@ -678,7 +678,7 @@ abstract class cmdbAbstractObject extends CMDBObject } else { - $sHTMLValue = ""; + $sHTMLValue = ""; } break; } diff --git a/application/uiwizard.class.inc.php b/application/uiwizard.class.inc.php index 886b8fd39..f49228dbc 100644 --- a/application/uiwizard.class.inc.php +++ b/application/uiwizard.class.inc.php @@ -89,6 +89,7 @@ class UIWizard function OnEnterStep{$iStepIndex}() { oWizardHelper.ResetQuery(); + oWizardHelper.UpdateWizard(); $sJSHandlerCode diff --git a/application/wizardhelper.class.inc.php b/application/wizardhelper.class.inc.php index 3ab7b5c1c..578932e18 100644 --- a/application/wizardhelper.class.inc.php +++ b/application/wizardhelper.class.inc.php @@ -60,7 +60,7 @@ class WizardHelper $oSet = DBObjectSet::FromArray($sLinkedClass, $aLinkedObjectsArray); $oObj->Set($sAttCode, $oSet); } - else if (($oAttDef->IsExternalKey()) && ($value != '') ) + else if (($oAttDef->IsExternalKey()) && (!empty($value)) ) { // For external keys: load the target object so that external fields // get filled too @@ -176,6 +176,16 @@ class WizardHelper return $aFields; } + public function GetTargetClass() + { + return $this->m_aData['m_sClass']; + } + + public function GetIdForField($sFieldName) + { + return $this->m_aData['m_oFieldsMap'][$sFieldName]; + } + static function ParseJsonSet($oMe, $sLinkClass, $sExtKeyToMe, $sJsonSet) { $aSet = json_decode($sJsonSet, true); // true means hash array instead of object diff --git a/js/forms-json-utils.js b/js/forms-json-utils.js index 1c6b5b1e3..60754bd07 100644 --- a/js/forms-json-utils.js +++ b/js/forms-json-utils.js @@ -57,7 +57,16 @@ function ReloadObjectFromServer(sJSON) function GoToStep(iCurrentStep, iNextStep) { var oCurrentStep = document.getElementById('wizStep'+iCurrentStep); - if (CheckMandatoryFields('wizStep'+iCurrentStep)) + if (iNextStep > iCurrentStep) + { + // Check the values when moving forward + if (CheckMandatoryFields('wizStep'+iCurrentStep)) + { + oCurrentStep.style.display = 'none'; + ActivateStep(iNextStep); + } + } + else { oCurrentStep.style.display = 'none'; ActivateStep(iNextStep); diff --git a/js/wizardhelper.js b/js/wizardhelper.js index e1b50bcba..89377316b 100644 --- a/js/wizardhelper.js +++ b/js/wizardhelper.js @@ -9,6 +9,7 @@ function WizardHelper(sClass) 'm_aAllowedValuesRequested': [], 'm_aDefaultValue': [], 'm_aAllowedValues': [], + 'm_iFieldsCount' : 0, }; this.m_oData.m_sClass = sClass; @@ -18,6 +19,12 @@ function WizardHelper(sClass) this.m_oData.m_oFieldsMap = oFieldsMap; } + this.SetFieldsCount = function (count) + { + this.m_oData.m_iFieldsCount = count; + + } + this.RequestDefaultValue = function (sFieldName) { currentValue = this.UpdateCurrentValue(sFieldName); @@ -50,12 +57,20 @@ function WizardHelper(sClass) this.ResetQuery = function () { this.m_oData.m_aDefaultValueRequested = []; + this.m_oData.m_aDefaultValue = []; this.m_oData.m_aAllowedValuesRequested = []; + this.m_oData.m_aAllowedValues = []; } this.UpdateFields = function () { //console.log('** UpdateFields **') + for(i=0; i< this.m_oData.m_aAllowedValuesRequested.length; i++) + { + sAttCode = this.m_oData.m_aAllowedValuesRequested[i]; + sFieldId = this.m_oData.m_oFieldsMap[sAttCode]; + $('#field_'+sFieldId).html(this.m_oData.m_aAllowedValues[sFieldId]); + } for(i=0; i< this.m_oData.m_aDefaultValueRequested.length; i++) { sAttCode = this.m_oData.m_aDefaultValueRequested[i]; @@ -67,10 +82,24 @@ function WizardHelper(sClass) } } + this.UpdateWizard = function () + { + //console.log('** UpdateWizard **') + for(i=0; i< this.m_oData.m_iFieldsCount; i++) + { + value = $('#att_'+i).val(); + if (value == '') + { + value = null; + } + this.m_oData.m_aCurrentValues[i] = value; + } + } + this.AjaxQueryServer = function () { //console.log('data sent:', this.ToJSON()); - //console.log('oWizard:', this); + console.log('oWizard:', this); $.get('ajax.render.php?json_obj=' + this.ToJSON(), { operation: 'wizard_helper' }, function(json_data){ diff --git a/pages/UI.php b/pages/UI.php index ec6136aac..0de789f0d 100644 --- a/pages/UI.php +++ b/pages/UI.php @@ -368,12 +368,14 @@ switch($operation) { $aNewFieldsMap[$sFieldCode] = $id; } + $iFieldsCount = count($aFieldsMap); $sJsonFieldsMap = json_encode($aNewFieldsMap); $oP->add(" // Initializes the object once at the beginning of the page... var oWizardHelper = new WizardHelper('$sClass'); oWizardHelper.SetFieldsMap($sJsonFieldsMap); + oWizardHelper.SetFieldsCount($iFieldsCount); ActivateStep(1); \n"); diff --git a/pages/ajax.render.php b/pages/ajax.render.php index 9d4365f76..b5e892a44 100644 --- a/pages/ajax.render.php +++ b/pages/ajax.render.php @@ -77,25 +77,21 @@ switch($operation) $sJson = utils::ReadParam('json_obj', ''); $oWizardHelper = WizardHelper::FromJSON($sJson); $oObj = $oWizardHelper->GetTargetObject(); + $sClass = $oWizardHelper->GetTargetClass(); foreach($oWizardHelper->GetFieldsForDefaultValue() as $sAttCode) { - $oAttDef = MetaModel::GetAttributeDef(get_class($oObj), $sAttCode); - $oWizardHelper->SetDefaultValue($sAttCode, $oAttDef->GetDefaultValue()); + $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode); + $defaultValue = $oAttDef->GetDefaultValue(); + $oWizardHelper->SetDefaultValue($sAttCode, $defaultValue); + $oObj->Set($sAttCode, $defaultValue); } foreach($oWizardHelper->GetFieldsForAllowedValues() as $sAttCode) { - $aAllowedValues = MetaModel::GetAllowedValues_att(get_class($oObj), $sAttCode, array('this' => $oObj)); - // Few choices, use a normal 'select' - $sHTMLValue = "\n"; + $sId = $oWizardHelper->GetIdForField($sAttCode); + $value = $oObj->Get($sAttCode); + $oAttDef = MetaModel::GetAttributeDef($sClass, $sAttCode); + $sHTMLValue = cmdbAbstractObject::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $value, '', 'att_'.$sId, '', 0, array('this' => $oObj)); - // Improvement: what if the list is too long? $oWizardHelper->SetAllowedValuesHtml($sAttCode, $sHTMLValue); } $oPage->add($oWizardHelper->ToJSON()); From 3c8f7cf08ab7ea65fa11088f10a974f1d0e42704 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Fri, 11 Sep 2009 20:36:34 +0000 Subject: [PATCH 074/227] - Automatic adjustment of the width and height of the ModalDlg to fit the document's size SVN:trunk[149] --- application/uilinkswizard.class.inc.php | 11 +++++++++-- 1 file changed, 9 insertions(+), 2 deletions(-) diff --git a/application/uilinkswizard.class.inc.php b/application/uilinkswizard.class.inc.php index ff56db516..7fdc3a508 100644 --- a/application/uilinkswizard.class.inc.php +++ b/application/uilinkswizard.class.inc.php @@ -115,6 +115,8 @@ class UILinksWizard function(data) { $('#ModalDlg').html(data); + dlgWidth = $(document).width() - 100; + $('#ModalDlg').css('width', dlgWidth); $('#ModalDlg').jqmShow(); }, 'html' @@ -149,8 +151,13 @@ class UILinksWizard nb_rows = $('#SearchResultsToAdd table.listResults tr').length; if(nb_rows > 10) { - $('#SearchResultsToAdd table.listResults tbody').attr('height', '300px'); - $('#SearchResultsToAdd .listResults tbody').css('overflow', 'auto'); + yOffset = $('#ModalDlg').height() - $('#SearchResultsToAdd table.listResults tbody').height(); + tbodyHeight = $(document).height() - 100 - yOffset; + if ($('#ModalDlg').height() > ($(document).height() - 100)) + { + $('#SearchResultsToAdd table.listResults tbody').attr('height', tbodyHeight); + $('#SearchResultsToAdd .listResults tbody').css('overflow', 'auto'); + } } }, From e4f4b66722633fe7eac19151e5d7c1961903fe43 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Fri, 11 Sep 2009 21:20:45 +0000 Subject: [PATCH 075/227] Field validation now preserves background images SVN:trunk[150] --- js/forms-json-utils.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/js/forms-json-utils.js b/js/forms-json-utils.js index 60754bd07..2a0140fff 100644 --- a/js/forms-json-utils.js +++ b/js/forms-json-utils.js @@ -127,7 +127,7 @@ function CheckMandatoryFields(sFormId) $('#'+sFormId+' :input.mandatory').each( function() { if (( this.value == '') || (this.value == 0)) { - this.style.background = '#fcc'; + this.style.backgroundColor = '#fcc'; iErrorsCount++; if (iErrorsCount == 1) { @@ -136,7 +136,7 @@ function CheckMandatoryFields(sFormId) } else { - this.style.background = '#fff'; + this.style.backgroundColor = '#fff'; } } ); From 61df1d7babea6877ac987b17561606a2bde5ca59 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Fri, 11 Sep 2009 21:23:24 +0000 Subject: [PATCH 076/227] - 'Add...' butotn is now grayed when there is nothing to Add - Better handling of AllowedValues, supporting autocompletes - Cleanup unused code in wizard.utils.js SVN:trunk[151] --- application/ui.linkswidget.class.inc.php | 15 ++++++++++----- css/jqModal.css | 11 ++++++----- js/linkswidget.js | 4 ++++ js/wizard.utils.js | 17 ----------------- js/wizardhelper.js | 2 +- pages/ajax.render.php | 2 +- 6 files changed, 22 insertions(+), 29 deletions(-) diff --git a/application/ui.linkswidget.class.inc.php b/application/ui.linkswidget.class.inc.php index 929403adf..e2e5aa72d 100644 --- a/application/ui.linkswidget.class.inc.php +++ b/application/ui.linkswidget.class.inc.php @@ -75,14 +75,15 @@ class UILinksWidget $sHTMLValue .= "\n"; $sHTMLValue .= $this->GetObjectPickerDialog($oPage, $sTargetClass, 'oLinkWidget'.$this->m_iInputId.'.OnOk'); $sHTMLValue .= $this->GetLinkObjectDialog($oPage, $this->m_iInputId); - $sHTMLValue .= "m_iInputId}\" size=\"35\" name=\"\" value=\"\" title=\"Type the first 3 characters\" />"; - $sHTMLValue .= "m_iInputId}.AddObject();\"/>"; + $sHTMLValue .= "m_iInputId}\" size=\"35\" name=\"\" value=\"\" title=\"Type the first 3 characters\"/>"; + $sHTMLValue .= "m_iInputId}\" value=\" Add... \" class=\"action\" onClick=\"oLinkWidget{$this->m_iInputId}.AddObject();\"/>"; $sHTMLValue .= " m_iInputId', '$sExtKeyToRemote');\"/>"; // another hidden input to store & pass the object's Id - $sHTMLValue .= "m_iInputId}\"/>\n"; + $sHTMLValue .= "m_iInputId}\" onChange=\"EnableAddButton('{$this->m_iInputId}');\"/>\n"; $sHTMLValue .= "m_iInputId}\" name=\"attr_{$this->m_sAttCode}{$this->m_sNameSuffix}\" value=\"\"/>\n"; $oPage->add_ready_script("\$('#{$this->m_iInputId}').val('$sJSON');\noLinkWidget{$this->m_iInputId}.Init();\n\$('#ac_{$this->m_iInputId}').autocomplete('./ajax.render.php', { minChars:3, onItemSelect:selectItem, onFindValue:findValue, formatItem:formatItem, autoFill:true, keyHolder:'#id_ac_{$this->m_iInputId}', extraParams:{operation:'ui.linkswidget', sclass:'{$this->m_sClass}', attCode:'{$this->m_sAttCode}', max:30}});"); - $oPage->add_ready_script("\$('#ac_{$this->m_iInputId}').result( function(event, data, formatted) { if (data) { $('#id_ac_{$this->m_iInputId}').val(data[1]); } } );"); + $oPage->add_ready_script("\$('#ac_add_{$this->m_iInputId}').attr('disabled', 'disabled');"); + $oPage->add_ready_script("\$('#ac_{$this->m_iInputId}').result( function(event, data, formatted) { if (data) { $('#id_ac_{$this->m_iInputId}').val(data[1]); $('#ac_add_{$this->m_iInputId}').attr('disabled', ''); } else { $('#ac_add_{$this->m_iInputId}').attr('disabled', 'disabled'); } } );"); } else { @@ -221,6 +222,7 @@ class UILinksWidget { $sHTML = <<< EOF
    +
    "; + $sHTMLValue = ""; break; case 'Text': - $sHTMLValue = ""; + $sHTMLValue = ""; break; case 'List': - $oWidget = new UILinksWidget($sClass, $sAttCode, $iInputId); + $oWidget = new UILinksWidget($sClass, $sAttCode, $iInputId, $sNameSuffix); $sHTMLValue = $oWidget->Display($oPage, $value); break; @@ -549,7 +594,7 @@ abstract class cmdbAbstractObject extends CMDBObject //Enum field or external key, display a combo if (count($aAllowedValues) == 0) { - $sHTMLValue = ""; + $sHTMLValue = ""; } else if (count($aAllowedValues) > 50) { @@ -557,13 +602,13 @@ abstract class cmdbAbstractObject extends CMDBObject // The input for the auto complete $sHTMLValue = ""; // another hidden input to store & pass the object's Id - $sHTMLValue .= "\n"; + $sHTMLValue .= "\n"; $oPage->add_ready_script("\$('#label_$iInputId').autocomplete('./ajax.render.php', { minChars:3, onItemSelect:selectItem, onFindValue:findValue, formatItem:formatItem, autoFill:true, keyHolder:'#$iInputId', extraParams:{operation:'autocomplete', sclass:'$sClass',attCode:'".$sAttCode."'}});"); } else { // Few choices, use a normal 'select' - $sHTMLValue = "\n"; foreach($aAllowedValues as $key => $display_value) { $sSelected = ($value == $key) ? ' selected' : ''; @@ -574,7 +619,7 @@ abstract class cmdbAbstractObject extends CMDBObject } else { - $sHTMLValue = ""; + $sHTMLValue = ""; } } } diff --git a/application/displayblock.class.inc.php b/application/displayblock.class.inc.php index 33e639952..8eebeeae0 100644 --- a/application/displayblock.class.inc.php +++ b/application/displayblock.class.inc.php @@ -85,11 +85,15 @@ class DisplayBlock { $sEncoding = strtolower($aMatches[1]); } - if (preg_match('/ linkage="(.*)"/U',$sITopTag, $aMatches)) + if (preg_match('/ link_attr="(.*)"/U',$sITopTag, $aMatches)) { // The list to display is a list of links to the specified object - $sExtKey = strtolower($aMatches[1]); - $aParams['linkage'] = $sExtKey; // Name of the Ext. Key that make this linkage + $aParams['link_attr'] = $aMatches[1]; // Name of the Ext. Key that make this linkage + } + if (preg_match('/ object_id="(.*)"/U',$sITopTag, $aMatches)) + { + // The list to display is a list of links to the specified object + $aParams['object_id'] = $aMatches[1]; // Id of the object to be linked to } // Parameters contains a list of extra parameters for the block // the syntax is param_name1:value1;param_name2:value2;... @@ -106,6 +110,21 @@ class DisplayBlock } } } + if (!empty($aParams['link_attr'])) + { + // Check that all mandatory parameters are present: + if(empty($aParams['object_id'])) + { + // if 'links' mode is requested the d of the object to link to must be specified + throw new ApplicationException("Parameter object_id is mandatory when link_attr is specified. Check the definition of the display template."); + } + if(empty($aParams['target_attr'])) + { + // if 'links' mode is requested the d of the object to link to must be specified + throw new ApplicationException("Parameter target_attr is mandatory when link_attr is specified. Check the definition of the display template."); + } + + } switch($sEncoding) { case 'text/serialize': @@ -245,17 +264,16 @@ class DisplayBlock break; case 'list': - $bDashboardMode = isset($aExtraParams['dashboard']) ? ($aExtraParams['dashboard'] == 'true') : false; if ( ($this->m_oSet->Count()> 0) && (UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_READ, $this->m_oSet) == UR_ALLOWED_YES) ) { - $sLinkage = isset($aExtraParams['linkage']) ? $aExtraParams['linkage'] : ''; - $sHtml .= cmdbAbstractObject::GetDisplaySet($oPage, $this->m_oSet, $sLinkage, !$bDashboardMode /* bDisplayMenu */); + $sHtml .= cmdbAbstractObject::GetDisplaySet($oPage, $this->m_oSet, $aExtraParams); } else { $sHtml .= $oPage->GetP("No object to display."); $sClass = $this->m_oFilter->GetClass(); - if (!$bDashboardMode) + $bDisplayMenu = isset($this->m_aParams['menu']) ? $this->m_aParams['menu'] == true : true; + if ($bDisplayMenu) { if (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY, $this->m_oSet) == UR_ALLOWED_YES) { @@ -265,12 +283,37 @@ class DisplayBlock } break; + case 'links': + //$bDashboardMode = isset($aExtraParams['dashboard']) ? ($aExtraParams['dashboard'] == 'true') : false; + //$bSelectMode = isset($aExtraParams['select']) ? ($aExtraParams['select'] == 'true') : false; + if ( ($this->m_oSet->Count()> 0) && (UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_READ, $this->m_oSet) == UR_ALLOWED_YES) ) + { + //$sLinkage = isset($aExtraParams['linkage']) ? $aExtraParams['linkage'] : ''; + $sHtml .= cmdbAbstractObject::GetDisplaySet($oPage, $this->m_oSet, $aExtraParams); + } + else + { + $sHtml .= $oPage->GetP("No object to display."); + $sClass = $this->m_oFilter->GetClass(); + $bDisplayMenu = isset($this->m_aParams['menu']) ? $this->m_aParams['menu'] == true : true; + if ($bDisplayMenu) + { + if (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY, $this->m_oSet) == UR_ALLOWED_YES) + { + $oAttDef = MetaModel::GetAttributeDef($sClass, $this->m_aParams['target_attr']); + $sTargetClass = $oAttDef->GetTargetClass(); + $sHtml .= $oPage->GetP("Click here to add new ".Metamodel::GetName($sTargetClass)."s\n"); + } + } + } + break; + case 'details': if (UserRights::IsActionAllowed($this->m_oSet->GetClass(), UR_ACTION_READ, $this->m_oSet) == UR_ALLOWED_YES) { while($oObj = $this->m_oSet->Fetch()) { - $sHtml .= $oObj->GetDetails($oPage); + $sHtml .= $oObj->GetDetails($oPage); // Still used ??? } } break; @@ -311,7 +354,7 @@ class DisplayBlock $oPage->add_ready_script("\$(\"#LnkSearch_$iSearchSectionId\").click(function() {\$(\"#Search_$iSearchSectionId\").slideToggle('normal'); $(\"#LnkSearch_$iSearchSectionId\").toggleClass('open');});"); $sHtml .= cmdbAbstractObject::GetSearchForm($oPage, $this->m_oSet, $aExtraParams); $sHtml .= "\n"; - $sHtml .= "
    \n"; + $sHtml .= "
    \n"; $sHtml .= "
    Search
    \n"; break; @@ -635,12 +678,15 @@ class MenuBlock extends DisplayBlock $bIsBulkModifyAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_MODIFY, $oSet); $bIsBulkDeleteAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_DELETE, $oSet); // Just one object in the set, possible actions are "new / clone / modify and delete" - if (isset($aExtraParams['linkage'])) + if (isset($aExtraParams['link_attr'])) { - if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'Add #...', 'url' => "../pages/$sUIPage?operation=new&class=$sClass&$sContext"); } - if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'Clone #...', 'url' => "../pages/$sUIPage?operation=clone&class=$sClass&id=$id&$sContext"); } - if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'Modify #...', 'url' => "../pages/$sUIPage?operation=modify&class=$sClass&id=$id&$sContext"); } - if ($bIsDeleteAllowed) { $aActions[] = array ('label' => 'Remove #', 'url' => "../pages/$sUIPage?operation=delete&class=$sClass&id=$id&$sContext"); } + $id = $aExtraParams['object_id']; + $sTargetAttr = $aExtraParams['target_attr']; + $oAttDef = MetaModel::GetAttributeDef($sClass, $sTargetAttr); + $sTargetClass = $oAttDef->GetTargetClass(); + if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'Add...', 'url' => "../pages/$sUIPage?operation=modify_links&class=$sClass&link_attr=".$aExtraParams['link_attr']."&target_class=$sTargetClass&id=$id&addObjects=true&$sContext"); } + if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'Manage...', 'url' => "../pages/$sUIPage?operation=modify_links&class=$sClass&link_attr=".$aExtraParams['link_attr']."&target_class=$sTargetClass&id=$id&sContext"); } + if ($bIsDeleteAllowed) { $aActions[] = array ('label' => 'Remove All', 'url' => "#"); } } else { @@ -681,14 +727,17 @@ class MenuBlock extends DisplayBlock $bIsModifyAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY, $oSet); $bIsBulkModifyAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_MODIFY, $oSet); $bIsBulkDeleteAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_BULK_DELETE, $oSet); - if (isset($aExtraParams['linkage'])) + if (isset($aExtraParams['link_attr'])) { + $id = $aExtraParams['object_id']; + $sTargetAttr = $aExtraParams['target_attr']; + $oAttDef = MetaModel::GetAttributeDef($sClass, $sTargetAttr); + $sTargetClass = $oAttDef->GetTargetClass(); $bIsDeleteAllowed = UserRights::IsActionAllowed($sClass, UR_ACTION_DELETE, $oSet); - if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'Add #...', 'url' => "../pages/$sUIPage?operation=new&class=$sClass&$sContext"); } - if ($bIsDeleteAllowed) { $aActions[] = array ('label' => 'Remove #', 'url' => "../pages/$sUIPage?operation=delete&class=$sClass&id=$id&$sContext"); } - if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'Modify #...', 'url' => "../pages/$sUIPage?operation=modify&class=$sClass&id=$id&$sContext"); } - if ($bIsBulkModifyAllowed) { $aActions[] = array ('label' => 'Modify All #...', 'url' => "../pages/$sUIPage?operation=new&class=$sClass&$sContext"); } - if ($bIsBulkDeleteAllowed) { $aActions[] = array ('label' => 'Remove All #...', 'url' => "#"); } + if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'Add...', 'url' => "../pages/$sUIPage?operation=modify_links&class=$sClass&link_attr=".$aExtraParams['link_attr']."&target_class=$sTargetClass&id=$id&addObjects=true&$sContext"); } + //if ($bIsBulkModifyAllowed) { $aActions[] = array ('label' => 'Add...', 'url' => "../pages/$sUIPage?operation=modify_links&class=$sClass&linkage=".$aExtraParams['linkage']."&id=$id&addObjects=true&$sContext"); } + if ($bIsBulkModifyAllowed) { $aActions[] = array ('label' => 'Manage...', 'url' => "../pages/$sUIPage?operation=modify_links&class=$sClass&link_attr=".$aExtraParams['link_attr']."&target_class=$sTargetClass&id=$id&sContext"); } + if ($bIsBulkDeleteAllowed) { $aActions[] = array ('label' => 'Remove All...', 'url' => "#"); } } else { diff --git a/application/template.class.inc.php b/application/template.class.inc.php index db768c3af..6436dfc52 100644 --- a/application/template.class.inc.php +++ b/application/template.class.inc.php @@ -134,7 +134,7 @@ class DisplayTemplate break; case 'itoptab': - $oPage->SetCurrentTab($aAttributes['name']); + $oPage->SetCurrentTab(str_replace('_', ' ', $aAttributes['name'])); $oTemplate = new DisplayTemplate($sContent); $oTemplate->Render($oPage, array()); // no params to apply, they have already been applied //$oPage->p('iTop Tab Content:
    '.htmlentities($sContent).'
    '); @@ -153,9 +153,22 @@ class DisplayTemplate $sBlockClass = $aAttributes['blockclass']; $sBlockType = $aAttributes['type']; $aExtraParams = array(); - if (isset($aAttributes['linkage'])) + if (isset($aAttributes['link_attr'])) { - $aExtraParams['linkage'] = $aAttributes['linkage']; + $aExtraParams['link_attr'] = $aAttributes['link_attr']; + // Check that all mandatory parameters are present: + if(empty($aAttributes['object_id'])) + { + // if 'links' mode is requested the d of the object to link to must be specified + throw new ApplicationException("Parameter object_id is mandatory when link_attr is specified. Check the definition of the display template."); + } + if(empty($aAttributes['target_attr'])) + { + // if 'links' mode is requested the d of the object to link to must be specified + throw new ApplicationException("Parameter target_attr is mandatory when link_attr is specified. Check the definition of the display template."); + } + $aExtraParams['object_id'] = $aAttributes['object_id']; + $aExtraParams['target_attr'] = $aAttributes['target_attr']; } switch($aAttributes['encoding']) diff --git a/application/ui.linkswidget.class.inc.php b/application/ui.linkswidget.class.inc.php index 31be4bef4..1db8517e5 100644 --- a/application/ui.linkswidget.class.inc.php +++ b/application/ui.linkswidget.class.inc.php @@ -6,12 +6,14 @@ class UILinksWidget { protected $m_sClass; protected $m_sAttCode; + protected $m_sNameSuffix; protected $m_iInputId; - public function __construct($sClass, $sAttCode, $iInputId) + public function __construct($sClass, $sAttCode, $iInputId, $sNameSuffix = '') { $this->m_sClass = $sClass; $this->m_sAttCode = $sAttCode; + $this->m_sNameSuffix = $sNameSuffix; $this->m_iInputId = $iInputId; } @@ -76,7 +78,7 @@ class UILinksWidget $sHTMLValue .= " m_iInputId', '$sExtKeyToRemote');\"/>"; // another hidden input to store & pass the object's Id $sHTMLValue .= "m_iInputId}\"/>\n"; - $sHTMLValue .= "m_iInputId}\" name=\"attr_{$this->m_sAttCode}\" value=\"\"/>\n"; + $sHTMLValue .= "m_iInputId}\" name=\"attr_{$this->m_sAttCode}{$this->m_sNameSuffix}\" value=\"\"/>\n"; $oPage->add_ready_script("\$('#{$this->m_iInputId}').val('$sJSON');\n\$('#ac_{$this->m_iInputId}').autocomplete('./ajax.render.php', { minChars:3, onItemSelect:selectItem, onFindValue:findValue, formatItem:formatItem, autoFill:true, keyHolder:'#id_ac_{$this->m_iInputId}', extraParams:{operation:'ui.linkswidget', sclass:'{$this->m_sClass}', attCode:'{$this->m_sAttCode}', max:30}});"); } else diff --git a/application/uilinkswizard.class.inc.php b/application/uilinkswizard.class.inc.php new file mode 100644 index 000000000..3a4cb450b --- /dev/null +++ b/application/uilinkswizard.class.inc.php @@ -0,0 +1,380 @@ +m_sClass = $sClass; + $this->m_sLinkageAttr = $sLinkageAttr; + $this->m_iObjectId = $iObjectId; + $this->m_sLinkedClass = $sLinkedClass; // Will try to guess below, if it's empty + $this->m_sLinkingAttCode = ''; // Will be filled once we've found the attribute corresponding to the linked class + + $this->m_aEditableFields = array(); + $this->m_aTableConfig = array(); + $this->m_aTableConfig['form::checkbox'] = array( 'label' => "", 'description' => "Select / Deselect All"); + foreach(MetaModel::GetAttributesList($this->m_sClass) as $sAttCode) + { + $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode); + if ($oAttDef->IsExternalKey() && ($sAttCode != $this->m_sLinkageAttr)) + { + if (empty($this->m_sLinkedClass)) + { + // This is a class of objects we can manage ! + // Since nothing was specify, any class will do ! + $this->m_sLinkedClass = $oAttDef->GetTargetClass(); + $this->m_sLinkingAttCode = $sAttCode; + } + else if ($this->m_sLinkedClass == $oAttDef->GetTargetClass()) + { + // This is the class of objects we want to manage ! + $this->m_sLinkingAttCode = $sAttCode; + } + } + else if ( (!$oAttDef->IsExternalKey()) && (!$oAttDef->IsExternalField())) + { + $this->m_aEditableFields[] = $sAttCode; + $this->m_aTableConfig[$sAttCode] = array( 'label' => $oAttDef->GetLabel(), 'description' => $oAttDef->GetDescription()); + } + } + if (empty($this->m_sLinkedClass)) + { + throw( new Exception("Incorrect link definition: the class of objects to manage: '$sLinkedClass' was not found as an external key in the class '$sClass'")); + } + foreach(MetaModel::GetZListItems($this->m_sLinkedClass, 'list') as $sFieldCode) + { + $oAttDef = MetaModel::GetAttributeDef($this->m_sLinkedClass, $sFieldCode); + $this->m_aTableConfig['static::'.$sFieldCode] = array( 'label' => $oAttDef->GetLabel(), 'description' => $oAttDef->GetDescription()); + } + } + + public function Display(web_page $oP, UserContext $oContext, $aExtraParams = array()) + { + $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sLinkageAttr); + $sTargetClass = $oAttDef->GetTargetClass(); + $oTargetObj = $oContext->GetObject($sTargetClass, $this->m_iObjectId); + + $oP->set_title("iTop - ".MetaModel::GetName($this->m_sLinkedClass)." objects linked with ".MetaModel::GetName(get_class($oTargetObj)).": ".$oTargetObj->GetName()); + $oP->add("
    \n"); + $oP->add("\n"); + $oP->add("
    \n"); + $oP->add("\n"); + $oP->add("\n"); + $oP->add("m_sClass}\">\n"); + $oP->add("m_sLinkageAttr}\">\n"); + $oP->add("m_iObjectId}\">\n"); + $oP->add("m_sLinkingAttCode}\">\n"); + $oP->add("

    Manage ".MetaModel::GetName($this->m_sLinkedClass)."s linked with ".MetaModel::GetName(get_class($oTargetObj)).": ".$oTargetObj->GetHyperlink()."

    \n"); + $oP->add("
    \n"); + $oP->add("\n"); + $oP->add_ready_script("InitForm();"); + $oFilter = $oContext->NewFilter($this->m_sClass); + $oFilter->AddCondition($this->m_sLinkageAttr, $this->m_iObjectId, '='); + $oSet = new DBObjectSet($oFilter); + $aForm = array(); + while($oCurrentLink = $oSet->Fetch()) + { + $aRow = array(); + $key = $oCurrentLink->GetKey(); + $oLinkedObj = $oContext->GetObject($this->m_sLinkedClass, $oCurrentLink->Get($this->m_sLinkingAttCode)); + + $aForm[$key] = $this->GetFormRow($oP, $oLinkedObj, $oCurrentLink); + } + //var_dump($aTableLabels); + //var_dump($aForm); + $this->DisplayFormTable($oP, $this->m_aTableConfig, $aForm); + $oP->add("     m_sLinkedClass)."s \" onClick=\"RemoveSelected();\" >"); + $oP->add("   m_sLinkedClass)."s... \" onClick=\"AddObjects();\">\n"); + $oP->add(""); + $oP->add("   \n"); + $oP->add("

     

    \n"); + $oP->add("
    \n"); + $oP->add("\n"); + if (isset($aExtraParams['StartWithAdd']) && ($aExtraParams['StartWithAdd'])) + { + $oP->add_ready_script("AddObjects();"); + } + } + + protected function GetFormRow($oP, $oLinkedObj, $currentLink = null ) + { + $aRow = array(); + if(is_object($currentLink)) + { + $key = $currentLink->GetKey(); + $sNameSuffix = "[$key]"; // To make a tabular form + $aRow['form::checkbox'] = ""; + $aRow['form::checkbox'] .= ""; + foreach($this->m_aEditableFields as $sFieldCode) + { + $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sFieldCode); + $aRow[$sFieldCode] = cmdbAbstractObject::GetFormElementForField($oP, $this->m_sClass, $sFieldCode, $oAttDef, $currentLink->Get($sFieldCode), '' /* DisplayValue */, $key, $sNameSuffix); + } + } + else + { + // form for creating a new record + $sNameSuffix = "[$currentLink]"; // To make a tabular form + $aRow['form::checkbox'] = ""; + $aRow['form::checkbox'] .= ""; + foreach($this->m_aEditableFields as $sFieldCode) + { + $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sFieldCode); + $aRow[$sFieldCode] = cmdbAbstractObject::GetFormElementForField($oP, $this->m_sClass, $sFieldCode, $oAttDef, '' /* TO DO/ call GetDefaultValue */, '' /* DisplayValue */, '' /* id */, $sNameSuffix); + } + } + foreach(MetaModel::GetZListItems($this->m_sLinkedClass, 'list') as $sFieldCode) + { + $aRow['static::'.$sFieldCode] = $oLinkedObj->GetAsHTML($sFieldCode); + } + return $aRow; + } + + protected function DisplayFormTable(web_page $oP, $aConfig, $aData) + { + $oP->add("\n"); + // Header + $oP->add("\n"); + $oP->add("\n"); + foreach($aConfig as $sName=>$aDef) + { + $oP->add("\n"); + } + $oP->add("\n"); + $oP->add("\n"); + + // Content + $oP->add("\n"); + if (count($aData) == 0) + { + $oP->add(""); + } + else + { + foreach($aData as $iRowId => $aRow) + { + $this->DisplayFormRow($oP, $aConfig, $aRow, $iRowId); + } + } + $oP->add("\n"); + + // Footer + $oP->add("
    ".$aDef['label']."
    The list is empty, use 'Add...' to add elements.
    \n"); + } + + protected function DisplayFormRow(web_page $oP, $aConfig, $aRow, $iRowId) + { + $oP->add("
    ".$aRow[$sName]."
    $sValue$sValue
    @@ -248,6 +250,7 @@ class UILinksWidget
    +
    EOF; $oPage->add_ready_script("$('#ManageObjectsDlg_$this->m_iInputId').jqm({overlay:70, modal:true, toTop:true});"); // jqModal Window //$oPage->add_ready_script("UpdateObjectList('$sClass');"); @@ -265,6 +268,7 @@ EOF; $sExtKeyToRemote = $oAttDef->GetExtKeyToRemote(); $sHTML = "
    \n"; + $sHTML .= "
    \n"; $sHTML .= "

    $sLinkedClass attributes

    \n"; $sHTML .= "\n"; $index = 0; @@ -306,9 +310,10 @@ EOF; } } $sHTML .= $oPage->GetDetails($aDetails); - $sHTML .= "     \n"; + $sHTML .= "     \n"; $sHTML .= "\n"; $sHTML .= "
    \n"; + $sHTML .= "
    \n"; $oPage->add_ready_script("$('#LinkDlg_$sId').jqm({overlay:70, modal:true, toTop:true});"); // jqModal Window //$oPage->add_ready_script("UpdateObjectList('$sClass');"); return $sHTML; diff --git a/css/jqModal.css b/css/jqModal.css index db45b7039..288981f2a 100644 --- a/css/jqModal.css +++ b/css/jqModal.css @@ -11,11 +11,12 @@ display: none; position: fixed; - top: 10%; - left: 20%; + top: 25px; + left: 25px; - margin-left: -100px; - width: 800px; + nomargin-left: 25px; + nomargin-right: 25px; + nowidth: 800px; background-color: #FFF; color: #333; @@ -37,4 +38,4 @@ * removed.html .jqmWindow { position: absolute; top: expression((document.documentElement.scrollTop || document.body.scrollTop) + Math.round(17 * (document.documentElement.offsetHeight || document.body.clientHeight) / 100) + 'px'); -} \ No newline at end of file +} diff --git a/js/linkswidget.js b/js/linkswidget.js index 47f65fc3b..d7027f77e 100644 --- a/js/linkswidget.js +++ b/js/linkswidget.js @@ -74,12 +74,16 @@ function LinksWidget(id, sLinkedClass, sExtKeyToMe, sExtKeyToRemote, aAttributes this.aLinks.push(oLink); } this.Refresh(); + // Grey out the 'Add...' button + $('#ac_add_'+this.id).attr('disabled', 'disabled'); } this.OnLinkCancel = function() { // Restore the links to their previous value (just in case) this.aLinks = this.aPreviousLinks; + // Grey out the 'Add...' button + $('#ac_add_'+this.id).attr('disabled', 'disabled'); } this.RemoveLink = function(index) diff --git a/js/wizard.utils.js b/js/wizard.utils.js index 21cdd6e77..dc58f8b5f 100644 --- a/js/wizard.utils.js +++ b/js/wizard.utils.js @@ -24,22 +24,6 @@ function AddObject(sClass, sId, sExtKeyToRemote) // Display the additional dialog $('#LinkDlg_'+sId).jqmShow(); return; - - // TO BE REWRITTEN - var sRelatedObjectIds = new String($('#related_object_ids_'+sId).val()); - if (sRelatedObjectIds.length > 0) - { - aRelatedObjectIds = sRelatedObjectIds.split(' '); - } - else - { - aRelatedObjectIds = new Array(); - } - // To do: check if the ID is not already in the list... - aRelatedObjectIds[aRelatedObjectIds.length] = sCurrentObjectId; - // Update the form & reload the list - $('#related_object_ids').val(aRelatedObjectIds.join(' ')); - UpdateObjectList(sClass, sExtKeyToRemote); } function ManageObjects(sTitle, sClass, sId, sExtKeyToRemote) @@ -158,6 +142,5 @@ function GetObjectIds(sInputId, sExtKeyToRemote) } } return aLinkedIds; - } diff --git a/js/wizardhelper.js b/js/wizardhelper.js index 89377316b..57ff91761 100644 --- a/js/wizardhelper.js +++ b/js/wizardhelper.js @@ -104,7 +104,7 @@ function WizardHelper(sClass) { operation: 'wizard_helper' }, function(json_data){ //console.log('data received:', json_data); - oWizardHelper.FromJSON(json_data); + //oWizardHelper.FromJSON(json_data); oWizardHelper.UpdateFields(); //console.log(oWizardHelper); $('#wizStep'+ G_iCurrentStep).unblock( {fadeOut: 0} ); diff --git a/pages/ajax.render.php b/pages/ajax.render.php index b5e892a44..c43924d60 100644 --- a/pages/ajax.render.php +++ b/pages/ajax.render.php @@ -94,7 +94,7 @@ switch($operation) $oWizardHelper->SetAllowedValuesHtml($sAttCode, $sHTMLValue); } - $oPage->add($oWizardHelper->ToJSON()); + $oPage->add("\n"); break; case 'ajax': From b98b1219eeffb4ac914e86fb89d8268632eb9fc1 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Sat, 12 Sep 2009 10:08:13 +0000 Subject: [PATCH 077/227] - Removed debug output into the console thats breaks when Firebug is not installed (or on IE) SVN:trunk[152] --- js/wizardhelper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/wizardhelper.js b/js/wizardhelper.js index 57ff91761..031571592 100644 --- a/js/wizardhelper.js +++ b/js/wizardhelper.js @@ -99,7 +99,7 @@ function WizardHelper(sClass) this.AjaxQueryServer = function () { //console.log('data sent:', this.ToJSON()); - console.log('oWizard:', this); + //console.log('oWizard:', this); $.get('ajax.render.php?json_obj=' + this.ToJSON(), { operation: 'wizard_helper' }, function(json_data){ From c45352bcc5f923c952043f841d8d1d636713760b Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Sat, 12 Sep 2009 19:58:20 +0000 Subject: [PATCH 078/227] Disabled automatic population of Zlists which was buggy. Not all ZLists are made of AttCode ! SVN:trunk[153] --- core/metamodel.class.php | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/core/metamodel.class.php b/core/metamodel.class.php index 285db9460..51aaadbdb 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -817,15 +817,15 @@ abstract class MetaModel // Define defaults values for the standard ZLists // - foreach (self::$m_aListInfos as $sListCode => $aListConfig) - { - if (!isset(self::$m_aListData[$sClass][$sListCode])) - { - $aAllAttributes = array_keys(self::$m_aAttribDefs[$sClass]); - self::$m_aListData[$sClass][$sListCode] = $aAllAttributes; - //echo "

    $sClass: $sListCode (".count($aAllAttributes)." attributes)

    \n"; - } - } + //foreach (self::$m_aListInfos as $sListCode => $aListConfig) + //{ + // if (!isset(self::$m_aListData[$sClass][$sListCode])) + // { + // $aAllAttributes = array_keys(self::$m_aAttribDefs[$sClass]); + // self::$m_aListData[$sClass][$sListCode] = $aAllAttributes; + // //echo "

    $sClass: $sListCode (".count($aAllAttributes)." attributes)

    \n"; + // } + //} } } From 9ca940a41ef0ae789f27636fcc597619af937a41 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Mon, 14 Sep 2009 08:37:10 +0000 Subject: [PATCH 079/227] Revised the CSV import: fixed issues with the external keys, added hyperlinks to the found objects, obsoleted the word 'pkey', replaced by 'id' SVN:trunk[154] --- application/cmdbabstract.class.inc.php | 6 - core/bulkchange.class.inc.php | 103 ++++++++++++----- core/cmdbobject.class.inc.php | 29 ++++- core/dbobject.class.php | 32 ++++-- core/metamodel.class.php | 6 + pages/csvimport.php | 153 +++++++++++++++++-------- 6 files changed, 231 insertions(+), 98 deletions(-) diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index 146f88e42..bc1008ed3 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -74,12 +74,6 @@ abstract class cmdbAbstractObject extends CMDBObject return "GetForLink()."\" title=\"$sHint\">$sLabel"; } - public function GetHyperlink() - { - $aAvailableFields[MetaModel::GetNameAttributeCode(get_class($this))] = $this->GetName(); - return $this->MakeHyperLink(get_class($this), $this->GetKey(), $aAvailableFields); - } - public function GetDisplayValue($sAttCode) { $sDisplayValue = ""; diff --git a/core/bulkchange.class.inc.php b/core/bulkchange.class.inc.php index 89bfdd0fa..6b4332d52 100644 --- a/core/bulkchange.class.inc.php +++ b/core/bulkchange.class.inc.php @@ -37,36 +37,55 @@ abstract class CellChangeSpec $this->m_proposedValue = $proposedValue; } - public function GetValue() + static protected function ValueAsHtml($value) { - return $this->m_proposedValue; + if (MetaModel::IsValidObject($value)) + { + return $value->GetHyperLink(); + } + else + { + return htmlentities($value); + } } - abstract public function GetDescription(); + public function GetValue($bHtml = false) + { + if ($bHtml) + { + return self::ValueAsHtml($this->m_proposedValue); + } + else + { + return $this->m_proposedValue; + } + } + + abstract public function GetDescription($bHtml = false); } class CellChangeSpec_Void extends CellChangeSpec { - public function GetDescription() + public function GetDescription($bHtml = false) { - return $this->GetValue(); + return $this->GetValue($bHtml); } } class CellChangeSpec_Unchanged extends CellChangeSpec { - public function GetDescription() + public function GetDescription($bHtml = false) { - return $this->GetValue()." (unchanged)"; + return $this->GetValue($bHtml)." (unchanged)"; } } class CellChangeSpec_Init extends CellChangeSpec { - public function GetDescription() + public function GetDescription($bHtml = false) { - return $this->GetValue(); + return $this->GetValue($bHtml); } } @@ -80,9 +99,9 @@ class CellChangeSpec_Modify extends CellChangeSpec parent::__construct($proposedValue); } - public function GetDescription() + public function GetDescription($bHtml = false) { - return $this->GetValue()." (previous: ".$this->m_previousValue.")"; + return $this->GetValue($bHtml)." (previous: ".self::ValueAsHtml($this->m_previousValue).")"; } } @@ -96,13 +115,13 @@ class CellChangeSpec_Issue extends CellChangeSpec_Modify parent::__construct($proposedValue, $previousValue); } - public function GetDescription() + public function GetDescription($bHtml = false) { if (is_null($this->m_proposedValue)) { return 'Could not be changed - reason: '.$this->m_sReason; } - return 'Could not be changed to "'.$this->GetValue().'" - reason: '.$this->m_sReason.' (previous: '.$this->m_previousValue.')'; + return 'Could not be changed to "'.$this->GetValue($bHtml).'" - reason: '.$this->m_sReason.' (previous: '.$this->m_previousValue.')'; } } @@ -124,12 +143,12 @@ abstract class RowStatus { } - abstract public function GetDescription(); + abstract public function GetDescription($bHtml = false); } class RowStatus_NoChange extends RowStatus { - public function GetDescription() + public function GetDescription($bHtml = false) { return "unchanged"; } @@ -139,12 +158,13 @@ class RowStatus_NewObj extends RowStatus { protected $m_iObjKey; - public function __construct($iObjKey = null) + public function __construct($sClass = '', $iObjKey = null) { $this->m_iObjKey = $iObjKey; + $this->m_sClass = $sClass; } - public function GetDescription() + public function GetDescription($bHtml = false) { if (is_null($this->m_iObjKey)) { @@ -152,7 +172,15 @@ class RowStatus_NewObj extends RowStatus } else { - return 'Created ('.$this->m_iObjKey.')'; + if (empty($this->m_sClass)) + { + $oObj = MetaModel::GetObject($this->m_sClass, $this->m_iObjKey); + return 'Created '.$oObj->GetHyperLink(); + } + else + { + return 'Created (id: '.$this->m_iObjKey.')'; + } } } } @@ -166,7 +194,7 @@ class RowStatus_Modify extends RowStatus $this->m_iChanged = $iChanged; } - public function GetDescription() + public function GetDescription($bHtml = false) { return "update ".$this->m_iChanged." cols"; } @@ -181,7 +209,7 @@ class RowStatus_Issue extends RowStatus $this->m_sReason = $sReason; } - public function GetDescription() + public function GetDescription($bHtml = false) { return 'Skipped - reason:'.$this->m_sReason; } @@ -217,6 +245,19 @@ class BulkChange $this->m_aExtKeys = $aExtKeys; } + static protected function MakeSpecObject($sClass, $iId) + { + $oObj = MetaModel::GetObject($sClass, $iId); + if (is_null($oObj)) + { + return $iId; + } + else + { + return $oObj; + } + } + protected function PrepareObject(&$oTargetObj, $aRowData, &$aErrors) { $aResults = array(); @@ -250,23 +291,26 @@ class BulkChange // Report it if (array_key_exists($sAttCode, $oTargetObj->ListChanges())) { + if ($oTargetObj->IsNew()) { - $aResults[$sAttCode]= new CellChangeSpec_Init($oForeignObj->GetKey(), $oTargetObj->Get($sAttCode), $oTargetObj->GetOriginal($sAttCode)); + $aResults[$sAttCode]= new CellChangeSpec_Init($oForeignObj); } else { - $aResults[$sAttCode]= new CellChangeSpec_Modify($oForeignObj->GetKey(), $oTargetObj->Get($sAttCode), $oTargetObj->GetOriginal($sAttCode)); + $previousValue = self::MakeSpecObject($oExtKey->GetTargetClass(), $oTargetObj->GetOriginal($sAttCode)); + $aResults[$sAttCode]= new CellChangeSpec_Modify($oForeignObj, $previousValue); } } else { - $aResults[$sAttCode]= new CellChangeSpec_Unchanged($oTargetObj->Get($sAttCode)); + $aResults[$sAttCode]= new CellChangeSpec_Unchanged($oForeignObj); } break; default: $aErrors[$sAttCode] = "Found ".$oExtObjects->Count()." matches"; - $aResults[$sAttCode]= new CellChangeSpec_Issue(null, $oTargetObj->Get($sAttCode), "Found ".$oExtObjects->Count()." matches"); + $previousValue = self::MakeSpecObject($oExtKey->GetTargetClass(), $oTargetObj->Get($sAttCode)); + $aResults[$sAttCode]= new CellChangeSpec_Issue(null, $previousValue, "Found ".$oExtObjects->Count()." matches"); } } @@ -291,18 +335,17 @@ class BulkChange { if (isset($aErrors[$sAttCode])) { - $aResults["col$iCol"]= new CellChangeSpec_Issue($aRowData[$iCol], $oTargetObj->Get($sAttCode), $aErrors[$sAttCode]); + $aResults["col$iCol"]= new CellChangeSpec_Issue($oTargetObj->Get($sAttCode), $oTargetObj->GetOriginal($sAttCode), $aErrors[$sAttCode]); } elseif (array_key_exists($sAttCode, $aChangedFields)) { - $originalValue = $oTargetObj->GetOriginal($sAttCode); if ($oTargetObj->IsNew()) { - $aResults["col$iCol"]= new CellChangeSpec_Init($aRowData[$iCol], $oTargetObj->Get($sAttCode), $originalValue); + $aResults["col$iCol"]= new CellChangeSpec_Init($oTargetObj->Get($sAttCode)); } else { - $aResults["col$iCol"]= new CellChangeSpec_Modify($aRowData[$iCol], $oTargetObj->Get($sAttCode), $originalValue); + $aResults["col$iCol"]= new CellChangeSpec_Modify($oTargetObj->Get($sAttCode), $oTargetObj->GetOriginal($sAttCode)); } } else @@ -358,7 +401,7 @@ class BulkChange if ($oChange) { $newID = $oTargetObj->DBInsertTrackedNoReload($oChange); - $aResult[$iRow]["__STATUS__"] = new RowStatus_NewObj($newID); + $aResult[$iRow]["__STATUS__"] = new RowStatus_NewObj($this->m_sClass, $newID); } else { @@ -424,7 +467,7 @@ class BulkChange case 1: $oTargetObj = $oReconciliationSet->Fetch(); $this->UpdateObject($aResult, $iRow, $oTargetObj, $aRowData, $oChange); - $aResult[$iRow]["__RECONCILIATION__"] = "Found a match ".$oTargetObj->GetKey(); + $aResult[$iRow]["__RECONCILIATION__"] = "Found a match ".$oTargetObj->GetHyperLink(); // $aResult[$iRow]["__STATUS__"]=> set in UpdateObject break; default: diff --git a/core/cmdbobject.class.inc.php b/core/cmdbobject.class.inc.php index d49f7280b..6608f3218 100644 --- a/core/cmdbobject.class.inc.php +++ b/core/cmdbobject.class.inc.php @@ -154,7 +154,7 @@ abstract class CMDBObject extends DBObject $oMyChangeOp->Set("objkey", $objkey); $iId = $oMyChangeOp->DBInsertNoReload(); } - private function RecordAttChanges(CMDBChange $oChange, array $aValues) + private function RecordAttChanges(CMDBChange $oChange, array $aValues, array $aOrigValues) { // $aValues is an array of $sAttCode => $value // @@ -167,7 +167,15 @@ abstract class CMDBObject extends DBObject $oMyChangeOp->Set("objclass", get_class($this)); $oMyChangeOp->Set("objkey", $this->GetKey()); $oMyChangeOp->Set("attcode", $sAttCode); - $oMyChangeOp->Set("oldvalue", $this->GetOriginal($sAttCode)); + if (array_key_exists($sAttCode, $aOrigValues)) + { + $sOriginalValue = $aOrigValues[$sAttCode]; + } + else + { + $sOriginalValue = 'undefined'; + } + $oMyChangeOp->Set("oldvalue", $sOriginalValue); $oMyChangeOp->Set("newvalue", $value); $iId = $oMyChangeOp->DBInsertNoReload(); } @@ -262,9 +270,11 @@ abstract class CMDBObject extends DBObject throw new CoreWarning("Attempting to update an unchanged object"); return; } - + + // Save the original values (will be reset to the new values when the object get written to the DB) + $aOriginalValues = $this->m_aOrigValues; $ret = parent::DBUpdate(); - $this->RecordAttChanges(self::$m_oCurrChange, $aChanges); + $this->RecordAttChanges(self::$m_oCurrChange, $aChanges, $aOriginalValues); return $ret; } @@ -355,14 +365,23 @@ abstract class CMDBObject extends DBObject $oObjSet = new CMDBObjectSet($oFilter); $oObjSet->Load(); + // Keep track of the previous values (will be overwritten when the objects are synchronized with the DB) + $aOriginalValues = array(); + $oObjSet->Rewind(); + while ($oItem = $oObjSet->Fetch()) + { + $aOriginalValues[$oItem->GetKey()] = $oItem->m_aOrigValues; + } + // Update in one single efficient query $ret = parent::BulkUpdate($oFilter, $aValues); // Record... in many queries !!! + $oObjSet->Rewind(); while ($oItem = $oObjSet->Fetch()) { $aChangedValues = $oItem->ListChangedValues($aValues); - $oItem->RecordAttChanges(self::$m_oCurrChange, $aChangedValues); + $oItem->RecordAttChanges(self::$m_oCurrChange, $aChangedValues, $aOriginalValues[$oItem->GetKey()]); } return $ret; } diff --git a/core/dbobject.class.php b/core/dbobject.class.php index eaca961f4..c74652755 100644 --- a/core/dbobject.class.php +++ b/core/dbobject.class.php @@ -33,7 +33,7 @@ abstract class DBObject private $m_bIsInDB = false; // true IIF the object is mapped to a DB record private $m_iKey = null; private $m_aCurrValues = array(); - private $m_aOrigValues = array(); + protected $m_aOrigValues = array(); private $m_bFullyLoaded = false; // Compound objects can be partially loaded private $m_aLoadedAtt = array(); // Compound objects can be partially loaded, array of sAttCode @@ -217,6 +217,12 @@ abstract class DBObject public function Set($sAttCode, $value) { + if ($sAttCode == 'finalclass') + { + // Ignore it - this attribute is set upon object creation and that's it + //throw new CoreWarning('Attempting to set the value for the internal attribute \"finalclass\"', array('current value'=>$this->Get('finalclass'), 'new value'=>$value)); + return; + } if (!array_key_exists($sAttCode, MetaModel::ListAttributeDefs(get_class($this)))) { throw new CoreException("Unknown attribute code '$sAttCode' for the class ".get_class($this)); @@ -348,15 +354,7 @@ abstract class DBObject } $sTargetClass = $oAtt->GetTargetClass(EXTKEY_ABSOLUTE); - $aMakeHLink = array(get_class($this), 'MakeHyperLink'); - if (is_callable($aMakeHLink)) - { - return call_user_func($aMakeHLink, $sTargetClass, $this->Get($sAttCode), $aAvailableFields); - } - else - { - return $this->Get($sAttCode); - } + return $this->MakeHyperLink($sTargetClass, $this->Get($sAttCode), $aAvailableFields); } // That's a standard attribute (might be an ext field or a direct field, etc.) @@ -375,6 +373,20 @@ abstract class DBObject return $oAtt->GetAsCSV($this->Get($sAttCode), $sSeparator, $sSepEscape); } + protected static function MakeHyperLink($sObjClass, $sObjKey, $aAvailableFields) + { + if ($sObjKey == 0) return 'undefined'; + + return MetaModel::GetName($sObjClass)."::$sObjKey"; + } + + public function GetHyperlink() + { + $aAvailableFields[MetaModel::GetNameAttributeCode(get_class($this))] = $this->GetName(); + return $this->MakeHyperLink(get_class($this), $this->GetKey(), $aAvailableFields); + } + + // could be in the metamodel ? public static function IsValidPKey($value) { diff --git a/core/metamodel.class.php b/core/metamodel.class.php index 51aaadbdb..39d116f49 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -2636,6 +2636,12 @@ abstract class MetaModel return self::GetObjectByRow($sClass, $aRow); } + public static function GetHyperLink($sTargetClass, $sOldValue) + { + $oObj = self::GetObject($sTargetClass, $sOldValue); + return $oObj->GetHyperLink(); + } + public static function NewObject($sClass) { self::_check_subclass($sClass); diff --git a/pages/csvimport.php b/pages/csvimport.php index 4d8911e77..24bf03798 100644 --- a/pages/csvimport.php +++ b/pages/csvimport.php @@ -38,9 +38,17 @@ function GetExtKeyFieldCodes($sColDesc) function MakeExtFieldLabel($sClass, $sExtKeyAttCode, $sForeignAttCode) { $oExtKeyAtt = MetaModel::GetAttributeDef($sClass, $sExtKeyAttCode); - $oForeignAtt = MetaModel::GetAttributeDef($oExtKeyAtt->GetTargetClass(), $sForeignAttCode); + if ($sForeignAttCode == 'id') + { + $sForeignAttLabel = 'id'; + } + else + { + $oForeignAtt = MetaModel::GetAttributeDef($oExtKeyAtt->GetTargetClass(), $sForeignAttCode); + $sForeignAttLabel = $oForeignAtt->GetLabel(); + } - return $oExtKeyAtt->GetLabel().EXTKEY_LABELSEP.$oForeignAtt->GetLabel(); + return $oExtKeyAtt->GetLabel().EXTKEY_LABELSEP.$sForeignAttLabel; } function MakeExtFieldSelectValue($sAttCode, $sExtAttCode) @@ -67,27 +75,33 @@ function ShowTableForm($oPage, $oCSVParser, $sClass) $aFields = array(); foreach($oCSVParser->ListFields() as $iFieldIndex=>$sFieldName) { - $sSelField = ""; - - $sCHECKED = ($sFieldName == "pkey" || MetaModel::IsReconcKey($sClass, $sFoundAttCode)) ? " CHECKED" : ""; - $sSelField .= " "; - $aFields["field$iFieldIndex"]["label"] = $sSelField; - $aFields["field$iFieldIndex"]["value"] = $aColToRow[$iFieldIndex]; + // Find the best match + $iMin = strlen($sFieldName); + $sBestValue = null; + foreach ($aOptions as $sValue => $aData) + { + $iDist = levenshtein(strtolower($sFieldName), strtolower($aData['LabelRef'])); + if (($iDist != -1) && ($iDist < $iMin)) + { + $iMin = $iDist; + $sBestValue = $sValue; + } + } + + $sSelField = ""; + $aFields["field$iFieldIndex"]["label"] = $sSelField; + + $sCHECKED = ($sFieldName == "id" || MetaModel::IsReconcKey($sClass, $sFoundAttCode)) ? " CHECKED" : ""; + $aFields["field$iFieldIndex"]["label"] .= ""; + + if (array_key_exists($iFieldIndex, $aColToRow)) + { + $aFields["field$iFieldIndex"]["value"] = $aColToRow[$iFieldIndex]; + } } $oPage->details($aFields); } @@ -151,7 +209,7 @@ function ProcessData($oPage, $sClass, $oCSVParser, $aFieldMap, $aIsReconcKey, CM foreach($aFieldMap as $sFieldId=>$sColDesc) { $iFieldId = (int) substr($sFieldId, strlen("field")); - if ($sColDesc == "pkey") + if ($sColDesc == "id") { // Skip ! $iPKeyId = $iFieldId; @@ -162,18 +220,19 @@ function ProcessData($oPage, $sClass, $oCSVParser, $aFieldMap, $aIsReconcKey, CM } elseif (IsExtKeyField($sColDesc)) { + // This field is value to search on, to find a value for an external key list($sExtKeyAttCode, $sExtReconcKeyAttCode) = GetExtKeyFieldCodes($sColDesc); $aExtKeys[$sExtKeyAttCode][$sExtReconcKeyAttCode] = $iFieldId; } elseif (array_key_exists($sFieldId, $aIsReconcKey)) { + // This value is a reconciliation key $aReconcilKeys[$sColDesc] = $iFieldId; $aAttList[$sColDesc] = $iFieldId; // A reconciliation key is also a field } else { // $sColDesc is an attribute code - // $aAttList[$sColDesc] = $iFieldId; } } @@ -182,10 +241,10 @@ function ProcessData($oPage, $sClass, $oCSVParser, $aFieldMap, $aIsReconcKey, CM // $aDisplayConfig = array(); $aDisplayConfig["__RECONCILIATION__"] = array("label"=>"Reconciliation", "description"=>""); - $aDisplayConfig["__STATUS__"] = array("label"=>"Status", "description"=>""); + $aDisplayConfig["__STATUS__"] = array("label"=>"Import status", "description"=>""); if (isset($iPKeyId)) { - $aDisplayConfig["col$iPKeyId"] = array("label"=>"pkey", "description"=>""); + $aDisplayConfig["col$iPKeyId"] = array("label"=>"id", "description"=>""); } foreach($aReconcilKeys as $sAttCode => $iCol) { @@ -223,13 +282,12 @@ function ProcessData($oPage, $sClass, $oCSVParser, $aFieldMap, $aIsReconcKey, CM $aExtKeys ); $aRes = $oBulk->Process($oChange); - $aResultDisp = array(); // to be displayed foreach($aRes as $iRow => $aRowData) { $aRowDisp = array(); $aRowDisp["__RECONCILIATION__"] = $aRowData["__RECONCILIATION__"]; - $aRowDisp["__STATUS__"] = $aRowData["__STATUS__"]->GetDescription(); + $aRowDisp["__STATUS__"] = $aRowData["__STATUS__"]->GetDescription(true); foreach($aRowData as $sKey => $value) { if ($sKey == '__RECONCILIATION__') continue; @@ -255,11 +313,11 @@ function ProcessData($oPage, $sClass, $oCSVParser, $aFieldMap, $aIsReconcKey, CM } if (empty($sClass)) { - $aRowDisp[$sKey] = $value->GetDescription(); + $aRowDisp[$sKey] = $value->GetDescription(true); } else { - $aRowDisp[$sKey] = "
    ".$value->GetDescription()."
    "; + $aRowDisp[$sKey] = "
    ".$value->GetDescription(true)."
    "; } } $aResultDisp[$iRow] = $aRowDisp; @@ -297,7 +355,7 @@ function Do_Welcome($oPage, $sClass) $aList = MetaModel::GetZListItems($sClassName, 'details'); $aHeader = array(); // $aHeader[] = MetaModel::GetKeyLabel($sClassName); - $aHeader[] = 'pkey'; // Should be what's coded on the line above... but there is a bug + $aHeader[] = 'id'; // Should be what's coded on the line above... but there is a bug foreach($aList as $sAttCode) { $aHeader[] = MetaModel::GetLabel($sClassName, $sAttCode); @@ -347,12 +405,9 @@ function Do_Format($oPage, $sClass) $iTarget = count($aData); if ($iTarget == 0) { - $oPage->add("Empty data set..."); - $oPage->add("
    "); - $oPage->add(""); - $oPage->add(""); - $oPage->add(""); - $oPage->add("
    "); + $oPage->p("Empty data set..., please provide some data!"); + $oPage->add("\n"); + return; } // Guess the format : @@ -405,9 +460,9 @@ function DoProcessOrVerify($oPage, $sClass, CMDBChange $oChange = null) $sReconcKey = ""; } - if ($sColDesc == "pkey") + if ($sColDesc == "id") { - $aDisplayConfig[$sFieldId] = array("label"=>"Private key $sReconcKey", "description"=>"blah pkey"); + $aDisplayConfig[$sFieldId] = array("label"=>"Private key $sReconcKey", "description"=>""); } elseif ($sColDesc == "__none__") { @@ -455,12 +510,16 @@ function DoProcessOrVerify($oPage, $sClass, CMDBChange $oChange = null) if (count($aMissingKeys) > 0) { $oPage->p("Warning: the objects could not be created, due to some missing mandatory external keys in the field list: "); - $oPage->p("
      "); + $oPage->add("
        "); foreach($aMissingKeys as $sAttCode => $oAttDef) { - $oPage->p("
      • ".$oAttDef->GetLabel()."
      • "); + $oPage->add("
      • ".$oAttDef->GetLabel()."
      • "); } - $oPage->p("
      "); + $oPage->add("
    "); + } + else + { + $oPage->p("ok - required external keys (if any) have been found in the field list"); } $oPage->p("

    Check...

    "); From 85ad73419eee94247ac7cd06db7fa206b5134b64 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Mon, 14 Sep 2009 08:38:52 +0000 Subject: [PATCH 080/227] Hyperlinks in the history logs presentation SVN:trunk[155] --- core/cmdbchangeop.class.inc.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/core/cmdbchangeop.class.inc.php b/core/cmdbchangeop.class.inc.php index 2c0913910..c7ff31d5d 100644 --- a/core/cmdbchangeop.class.inc.php +++ b/core/cmdbchangeop.class.inc.php @@ -193,6 +193,9 @@ class CMDBChangeOpSetAttribute extends CMDBChangeOp */ public function GetDescription() { + // Temporary, until we change the options of GetDescription() -needs a more global revision + $bIsHtml = true; + $sResult = ''; $oTargetObjectClass = $this->Get('objclass'); $oTargetObjectKey = $this->Get('objkey'); @@ -225,9 +228,16 @@ class CMDBChangeOpSetAttribute extends CMDBChangeOp $sResult = "$sAttName set to $sNewValue (previous value: $sOldValue)"; } } + elseif($bIsHtml && $oAttDef->IsExternalKey()) + { + $sTargetClass = $oAttDef->GetTargetClass(); + $sFrom = MetaModel::GetHyperLink($sTargetClass, $sOldValue); + $sTo = MetaModel::GetHyperLink($sTargetClass, $sNewValue); + $sResult = "$sAttName set to $sTo (previous: $sFrom)"; + } else { - $sResult = "$sAttName set to $sNewValue (previous value: $sOldValue)"; + $sResult = "$sAttName set too $sNewValue (previous value: $sOldValue)"; } } return $sResult; From 0e4e0f07a0646442ca88c54b23b6e8ba19898049 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Mon, 14 Sep 2009 08:40:28 +0000 Subject: [PATCH 081/227] Fixed bug: could not empty a field SVN:trunk[156] --- pages/UI.php | 8 ++++++-- pages/incident.php | 8 ++++++-- 2 files changed, 12 insertions(+), 4 deletions(-) diff --git a/pages/UI.php b/pages/UI.php index 0de789f0d..6a02f911c 100644 --- a/pages/UI.php +++ b/pages/UI.php @@ -410,6 +410,10 @@ switch($operation) { // Non-visible, or read-only attribute, do nothing } + else if ($sAttCode == 'finalclass') + { + // This very specific field is read-only + } else if ($oAttDef->IsLinkSet()) { // Link set, the data is a set of link objects, encoded in JSON @@ -424,9 +428,9 @@ switch($operation) } else if (!$oAttDef->IsExternalField()) { - $aAttributes[$sAttCode] = trim(utils::ReadPostedParam("attr_$sAttCode", '')); + $aAttributes[$sAttCode] = trim(utils::ReadPostedParam("attr_$sAttCode", null)); $previousValue = $oObj->Get($sAttCode); - if (!empty($aAttributes[$sAttCode]) && ($previousValue != $aAttributes[$sAttCode])) + if (!is_null($aAttributes[$sAttCode]) && ($previousValue != $aAttributes[$sAttCode])) { $oObj->Set($sAttCode, $aAttributes[$sAttCode]); $bObjectModified = true; diff --git a/pages/incident.php b/pages/incident.php index bea0a8951..ff9fe3df9 100644 --- a/pages/incident.php +++ b/pages/incident.php @@ -712,11 +712,15 @@ switch($operation) { // Non-visible, or read-only attribute, do nothing } + else if ($sAttCode == 'finalclass') + { + // This very specific field is read-only + } else if (!$oAttDef->IsExternalField()) { - $aAttributes[$sAttCode] = trim(utils::ReadPostedParam("attr_$sAttCode", '')); + $aAttributes[$sAttCode] = trim(utils::ReadPostedParam("attr_$sAttCode", null)); $previousValue = $oObj->Get($sAttCode); - if (!empty($aAttributes[$sAttCode]) && ($previousValue != $aAttributes[$sAttCode])) + if (!is_null($aAttributes[$sAttCode]) && ($previousValue != $aAttributes[$sAttCode])) { $oObj->Set($sAttCode, $aAttributes[$sAttCode]); $bObjectModified = true; From 13600893ba00aba942b1e9a82477b69f84e1d81d Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Mon, 14 Sep 2009 08:55:41 +0000 Subject: [PATCH 082/227] Cosmetic: display the stimulus label in the profile grants sumary SVN:trunk[157] --- addons/userrights/userrightsprofile.class.inc.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/addons/userrights/userrightsprofile.class.inc.php b/addons/userrights/userrightsprofile.class.inc.php index cb2aa797d..a0fe498c7 100644 --- a/addons/userrights/userrightsprofile.class.inc.php +++ b/addons/userrights/userrightsprofile.class.inc.php @@ -136,12 +136,12 @@ class URP_Profiles extends UserRightsBaseClass foreach (MetaModel::GetClasses('bizmodel') as $sClass) { $aStimuli = array(); - foreach (array_keys(MetaModel::EnumStimuli($sClass)) as $sStimulusCode) + foreach (MetaModel::EnumStimuli($sClass) as $sStimulusCode => $oStimulus) { $oGrant = $oUserRights->GetClassStimulusGrant($this->GetKey(), $sClass, $sStimulusCode); if (is_object($oGrant) && ($oGrant->Get('permission') == 'yes')) { - $aStimuli[] = $sStimulusCode; + $aStimuli[] = ''.htmlentities($oStimulus->Get('label')).''; } } $sStimuli = implode(', ', $aStimuli); From 7ed603b7e94f8d4dd18f3b9a27e9c204adfdb36c Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Mon, 14 Sep 2009 16:04:12 +0000 Subject: [PATCH 083/227] New business model (Erwan) SVN:trunk[158] --- business/ChangeMgmt.business.php | 29 ++++++++-------- business/ServiceDesk.business.php | 11 +++--- business/ServiceMgmt.business.php | 46 ++++++++++++-------------- business/incidentMgmt.business.php | 35 ++++++++++++-------- business/itop.business.class.inc.php | 11 ++++-- business/templates/application.html | 11 ++++++ business/templates/interface.html | 6 +++- business/templates/network.device.html | 8 ++--- business/templates/pc.html | 3 ++ business/templates/person.html | 8 ++++- business/templates/server.html | 7 ++-- business/templates/team.html | 4 +++ 12 files changed, 111 insertions(+), 68 deletions(-) diff --git a/business/ChangeMgmt.business.php b/business/ChangeMgmt.business.php index 8ed0ead72..412af9cbf 100644 --- a/business/ChangeMgmt.business.php +++ b/business/ChangeMgmt.business.php @@ -27,7 +27,7 @@ class bizChangeTicket extends cmdbAbstractObject //MetaModel::Init_InheritAttributes(); MetaModel::Init_AddAttribute(new AttributeString("name", array("label"=>"Ticket Ref", "description"=>"Refence number ofr this change", "allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("title", array("label"=>"Title", "description"=>"Overview of the Change", "allowed_values"=>null, "sql"=>"title", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeString("type", array("label"=>"Change Type", "description"=>"Type of the Change", "allowed_values"=>new ValueSetEnum("Routine, Normal, Emergency"), "sql"=>"type", "default_value"=>"Routine", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("type", array("label"=>"Change Type", "description"=>"Type of the Change", "allowed_values"=>new ValueSetEnum("Routine, Normal, Emergency"), "sql"=>"type", "default_value"=>"Routine", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("domain", array("label"=>"Domain", "description"=>"Domain for the Change", "allowed_values"=>new ValueSetEnum("Network,Server,Desktop,Application"), "sql"=>"domain", "default_value"=>"Desktop", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("reason", array("label"=>"Reason For Change", "description"=>"Reason for the Change", "allowed_values"=>null, "sql"=>"reason", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); @@ -47,7 +47,7 @@ class bizChangeTicket extends cmdbAbstractObject MetaModel::Init_AddAttribute(new AttributeDate("start_date", array("label"=>"Start Date", "description"=>"Time the change is expected to start", "allowed_values"=>null, "sql"=>"start_date", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeDate("end_date", array("label"=>"End Date", "description"=>"Date when the change is supposed to end", "allowed_values"=>null, "sql"=>"end_date", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeDate("close_date", array("label"=>"Closure Date", "description"=>"Date when the Ticket was closed", "allowed_values"=>null, "sql"=>"closed_date", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeString("impact", array("label"=>"Risk Assessment", "description"=>"Impact of the change", "allowed_values"=>null, "sql"=>"impact", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("impact", array("label"=>"Risk Assessment", "description"=>"Impact of the change", "allowed_values"=>null, "sql"=>"impact", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalKey("workgroup_id", array("targetclass"=>"bizWorkgroup", "jointype"=> "", "label"=>"Workgroup", "description"=>"which workgroup is owning ticket", "allowed_values"=>null, "sql"=>"workgroup_id", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("workgroup_name", array("label"=>"Workgroup", "description"=>"name of workgroup managing the Ticket", "allowed_values"=>null, "extkey_attcode"=> 'workgroup_id', "target_attcode"=>"name"))); MetaModel::Init_AddAttribute(new AttributeExternalKey("agent_id", array("targetclass"=>"bizPerson", "jointype"=> "", "label"=>"Agent", "description"=>"who is managing the ticket", "allowed_values"=>null, "sql"=>"agent_id", "is_null_allowed"=>true, "depends_on"=>array('workgroup_id')))); @@ -62,7 +62,7 @@ class bizChangeTicket extends cmdbAbstractObject MetaModel::Init_AddAttribute(new AttributeExternalField("managergroup_name", array("label"=>"Manager Group", "description"=>"name of workgroup approving the Ticket", "allowed_values"=>null, "extkey_attcode"=> 'managergroup_id', "target_attcode"=>"name"))); MetaModel::Init_AddAttribute(new AttributeExternalKey("manager_id", array("targetclass"=>"bizPerson", "jointype"=> "", "label"=>"Manager", "description"=>"who is approving the ticket", "allowed_values"=>null, "sql"=>"manager_id", "is_null_allowed"=>true, "depends_on"=>array('managergroup_id')))); MetaModel::Init_AddAttribute(new AttributeExternalField("manager_mail", array("label"=>"Manager", "description"=>"name of agent approving the Ticket", "allowed_values"=>null, "extkey_attcode"=> 'manager_id', "target_attcode"=>"email"))); - MetaModel::Init_AddAttribute(new AttributeEnum("outage", array("label"=>"Planned Outage", "description"=>"Flag to define if there is a planned outage", "allowed_values"=>new ValueSetEnum("Yes,No"), "sql"=>"outage", "default_value"=>"No", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("outage", array("label"=>"Planned Outage", "description"=>"Flag to define if there is a planned outage", "allowed_values"=>new ValueSetEnum("Yes,No"), "sql"=>"outage", "default_value"=>"No", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeText("change_request", array("label"=>"Change Request", "description"=>"Description of Change required", "allowed_values"=>null, "sql"=>"change_req", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); @@ -108,22 +108,23 @@ class bizChangeTicket extends cmdbAbstractObject // State machine MetaModel::Init_DefineState("New", array("label"=>"New (Unassigned)", "description"=>"Newly created ticket", "attribute_inherit"=>null, "attribute_list"=>array('name' => OPT_ATT_READONLY,'org_id' => OPT_ATT_MANDATORY, 'title' => OPT_ATT_MANDATORY, 'reason' => OPT_ATT_MANDATORY, 'impacted_infra_manual' => OPT_ATT_MANDATORY, - 'assignment_count' => OPT_ATT_HIDDEN, 'end_date' => OPT_ATT_HIDDEN))); + 'assignment_count' => OPT_ATT_HIDDEN, 'end_date' => OPT_ATT_HIDDEN, 'workgroup_id' => OPT_ATT_HIDDEN, 'supervisorgroup_id' => OPT_ATT_HIDDEN, 'managergroup_id' => OPT_ATT_HIDDEN, 'supervisor_id' => OPT_ATT_HIDDEN, 'manager_id' => OPT_ATT_HIDDEN, 'agent_id' => OPT_ATT_HIDDEN ))); MetaModel::Init_DefineState("Validated", array("label"=>"Validated", "description"=>"Ticket is approved", "attribute_inherit"=>null, - "attribute_list"=>array('name' => OPT_ATT_READONLY, 'org_id' => OPT_ATT_READONLY,'assignment_count' => OPT_ATT_READONLY,'managergroup_id' => OPT_ATT_MANDATORY, 'supervisorgroup_id' => OPT_ATT_MANDATORY))); + "attribute_list"=>array('name' => OPT_ATT_READONLY,'org_id' => OPT_ATT_READONLY,'change_request' => OPT_ATT_READONLY,'creation_date' => OPT_ATT_READONLY, 'title' => OPT_ATT_READONLY, 'reason' => OPT_ATT_READONLY, 'impacted_infra_manual' => OPT_ATT_MANDATORY, + 'assignment_count' => OPT_ATT_HIDDEN, 'end_date' => OPT_ATT_HIDDEN, 'workgroup_id' => OPT_ATT_MANDATORY, 'supervisorgroup_id' => OPT_ATT_MANDATORY, 'managergroup_id' => OPT_ATT_MANDATORY, 'supervisor_id' => OPT_ATT_HIDDEN, 'manager_id' => OPT_ATT_HIDDEN, 'agent_id' => OPT_ATT_HIDDEN))); MetaModel::Init_DefineState("Rejected", array("label"=>"Rejected", "description"=>"This ticket is not approved", "attribute_inherit"=>null, - "attribute_list"=>array('name' => OPT_ATT_READONLY, 'org_id' => OPT_ATT_READONLY,'assignment_count' => OPT_ATT_HIDDEN, 'end_date' => OPT_ATT_HIDDEN))); + "attribute_list"=>array('name' => OPT_ATT_READONLY, 'org_id' => OPT_ATT_READONLY,'creation_date' => OPT_ATT_READONLY,'assignment_count' => OPT_ATT_HIDDEN, 'end_date' => OPT_ATT_HIDDEN))); MetaModel::Init_DefineState("PlannedScheduled", array("label"=>"Planned&Scheduled", "description"=>"Evaluation is done for this change", "attribute_inherit"=>null, - "attribute_list"=>array('name' => OPT_ATT_READONLY, 'org_id' => OPT_ATT_READONLY,'assignment_count' => OPT_ATT_HIDDEN, 'end_date' => OPT_ATT_MANDATORY, 'impact' => OPT_ATT_MANDATORY, 'workgroup_id' => OPT_ATT_MANDATORY, 'change_log' => OPT_ATT_MUSTCHANGE,'fallback' => OPT_ATT_MANDATORY))); + "attribute_list"=>array('name' => OPT_ATT_READONLY, 'org_id' => OPT_ATT_READONLY,'assignment_count' => OPT_ATT_HIDDEN,'start_date' => OPT_ATT_MANDATORY, 'end_date' => OPT_ATT_MANDATORY, 'impact' => OPT_ATT_MANDATORY, 'workgroup_id' => OPT_ATT_READONLY,'supervisorgroup_id' => OPT_ATT_READONLY, 'managergroup_id' => OPT_ATT_READONLY, 'supervisor_id' => OPT_ATT_MANDATORY, 'manager_id' => OPT_ATT_MANDATORY, 'agent_id' => OPT_ATT_MANDATORY, 'change_log' => OPT_ATT_MANDATORY,'fallback' => OPT_ATT_MANDATORY,'title' => OPT_ATT_READONLY, 'reason' => OPT_ATT_READONLY,'type' => OPT_ATT_READONLY,'requestor_id' => OPT_ATT_READONLY,'domain' => OPT_ATT_READONLY,'change_request' => OPT_ATT_READONLY,'creation_date' => OPT_ATT_READONLY))); MetaModel::Init_DefineState("Approved", array("label"=>"Approved", "description"=>"Ticket is approved by CAB", "attribute_inherit"=>null, - "attribute_list"=>array('name' => OPT_ATT_READONLY,'org_id' => OPT_ATT_READONLY, 'assignment_count' => OPT_ATT_HIDDEN, 'end_date' => OPT_ATT_HIDDEN))); + "attribute_list"=>array('name' => OPT_ATT_READONLY, 'org_id' => OPT_ATT_READONLY,'outage' => OPT_ATT_READONLY,'assignment_count' => OPT_ATT_HIDDEN,'start_date' => OPT_ATT_READONLY, 'end_date' => OPT_ATT_MANDATORY, 'impact' => OPT_ATT_READONLY, 'workgroup_id' => OPT_ATT_READONLY,'supervisorgroup_id' => OPT_ATT_READONLY, 'managergroup_id' => OPT_ATT_READONLY, 'supervisor_id' => OPT_ATT_READONLY, 'manager_id' => OPT_ATT_READONLY, 'agent_id' => OPT_ATT_MANDATORY, 'change_log' => OPT_ATT_MANDATORY,'fallback' => OPT_ATT_MANDATORY,'title' => OPT_ATT_READONLY, 'reason' => OPT_ATT_READONLY,'type' => OPT_ATT_READONLY,'requestor_id' => OPT_ATT_READONLY,'domain' => OPT_ATT_READONLY,'change_request' => OPT_ATT_READONLY,'creation_date' => OPT_ATT_READONLY))); MetaModel::Init_DefineState("NotApproved", array("label"=>"Not Approved", "description"=>"Ticket has not been approved by CAB", "attribute_inherit"=>null, - "attribute_list"=>array('name' => OPT_ATT_READONLY,'org_id' => OPT_ATT_READONLY, 'assignment_count' => OPT_ATT_HIDDEN, 'end_date' => OPT_ATT_HIDDEN))); + "attribute_list"=>array('name' => OPT_ATT_READONLY, 'org_id' => OPT_ATT_READONLY,'outage' => OPT_ATT_READONLY,'assignment_count' => OPT_ATT_HIDDEN,'start_date' => OPT_ATT_READONLY, 'end_date' => OPT_ATT_MANDATORY, 'impact' => OPT_ATT_READONLY, 'workgroup_id' => OPT_ATT_READONLY,'supervisorgroup_id' => OPT_ATT_READONLY, 'managergroup_id' => OPT_ATT_READONLY, 'supervisor_id' => OPT_ATT_READONLY, 'manager_id' => OPT_ATT_READONLY, 'agent_id' => OPT_ATT_MANDATORY, 'change_log' => OPT_ATT_MANDATORY,'fallback' => OPT_ATT_MANDATORY,'title' => OPT_ATT_READONLY, 'reason' => OPT_ATT_READONLY,'type' => OPT_ATT_READONLY,'requestor_id' => OPT_ATT_READONLY,'domain' => OPT_ATT_READONLY,'change_request' => OPT_ATT_READONLY,'creation_date' => OPT_ATT_READONLY))); MetaModel::Init_DefineState("Implemented", array("label"=>"Implementation", "description"=>"Work is in progress for this ticket", "attribute_inherit"=>null, - "attribute_list"=>array('name' => OPT_ATT_READONLY,'org_id' => OPT_ATT_READONLY, 'assignment_count' => OPT_ATT_HIDDEN, 'end_date' => OPT_ATT_HIDDEN))); + "attribute_list"=>array('name' => OPT_ATT_READONLY, 'org_id' => OPT_ATT_READONLY,'assignment_count' => OPT_ATT_HIDDEN,'start_date' => OPT_ATT_READONLY,'outage' => OPT_ATT_READONLY, 'end_date' => OPT_ATT_MANDATORY, 'impact' => OPT_ATT_READONLY, 'workgroup_id' => OPT_ATT_READONLY,'supervisorgroup_id' => OPT_ATT_READONLY, 'managergroup_id' => OPT_ATT_READONLY, 'supervisor_id' => OPT_ATT_READONLY, 'manager_id' => OPT_ATT_READONLY, 'agent_id' => OPT_ATT_MANDATORY, 'change_log' => OPT_ATT_MANDATORY,'fallback' => OPT_ATT_MANDATORY,'title' => OPT_ATT_READONLY, 'reason' => OPT_ATT_READONLY,'type' => OPT_ATT_READONLY,'requestor_id' => OPT_ATT_READONLY,'domain' => OPT_ATT_READONLY,'change_request' => OPT_ATT_READONLY,'creation_date' => OPT_ATT_READONLY))); MetaModel::Init_DefineState("Monitored", array("label"=>"Monitored", "description"=>"Change performed is now monitored", "attribute_inherit"=>null, - "attribute_list"=>array('name' => OPT_ATT_READONLY,'org_id' => OPT_ATT_READONLY,'assignment_count' => OPT_ATT_HIDDEN, 'end_date' => OPT_ATT_HIDDEN))); - MetaModel::Init_DefineState("Closed", array("label"=>"Closed", "description"=>"Ticket is closed", "attribute_inherit"=>null, "attribute_list"=>array('org_id' => OPT_ATT_READONLY,"workgroup_id"=>OPT_ATT_MANDATORY, "agent_id"=>OPT_ATT_MANDATORY))); + "attribute_list"=>array('name' => OPT_ATT_READONLY, 'org_id' => OPT_ATT_READONLY,'assignment_count' => OPT_ATT_HIDDEN,'start_date' => OPT_ATT_READONLY, 'end_date' => OPT_ATT_READONLY, 'impact' => OPT_ATT_READONLY, 'workgroup_id' => OPT_ATT_READONLY,'supervisorgroup_id' => OPT_ATT_READONLY, 'managergroup_id' => OPT_ATT_READONLY, 'supervisor_id' => OPT_ATT_READONLY, 'manager_id' => OPT_ATT_READONLY, 'agent_id' => OPT_ATT_READONLY, 'change_log' => OPT_ATT_READONLY,'fallback' => OPT_ATT_READONLY,'title' => OPT_ATT_READONLY, 'reason' => OPT_ATT_READONLY,'type' => OPT_ATT_READONLY,'requestor_id' => OPT_ATT_READONLY,'domain' => OPT_ATT_READONLY,'change_request' => OPT_ATT_READONLY,'creation_date' => OPT_ATT_READONLY))); + MetaModel::Init_DefineState("Closed", array("label"=>"Closed", "description"=>"Ticket is closed", "attribute_inherit"=>null, "attribute_list"=>array('name' => OPT_ATT_READONLY,'outage' => OPT_ATT_READONLY,'org_id' => OPT_ATT_READONLY,'assignment_count' => OPT_ATT_HIDDEN,'start_date' => OPT_ATT_READONLY,'end_date' => OPT_ATT_READONLY,'last_update' => OPT_ATT_READONLY,'close_date' => OPT_ATT_READONLY, 'impact' => OPT_ATT_READONLY, 'workgroup_id' => OPT_ATT_READONLY,'supervisorgroup_id' => OPT_ATT_READONLY, 'managergroup_id' => OPT_ATT_READONLY, 'supervisor_id' => OPT_ATT_READONLY, 'manager_id' => OPT_ATT_READONLY, 'agent_id' => OPT_ATT_READONLY, 'change_log' => OPT_ATT_READONLY,'fallback' => OPT_ATT_READONLY,'title' => OPT_ATT_READONLY, 'reason' => OPT_ATT_READONLY,'type' => OPT_ATT_READONLY,'requestor_id' => OPT_ATT_READONLY,'domain' => OPT_ATT_READONLY,'change_request' => OPT_ATT_READONLY,'creation_date' => OPT_ATT_READONLY))); MetaModel::Init_DefineStimulus("ev_validate", new StimulusUserAction(array("label"=>"Validate this change", "description"=>"Make sure it is a valid change request"))); MetaModel::Init_DefineStimulus("ev_reject", new StimulusUserAction(array("label"=>"Reject this change", "description"=>"This change request is rejected because it is a non valid one"))); @@ -145,8 +146,8 @@ class bizChangeTicket extends cmdbAbstractObject MetaModel::Init_DefineTransition("NotApproved", "ev_replan", array("target_state"=>"PlannedScheduled", "actions"=>array('SetLastUpDate'), "user_restriction"=>null)); MetaModel::Init_DefineTransition("Approved", "ev_implement", array("target_state"=>"Implemented", "actions"=>array('SetLastUpDate'), "user_restriction"=>null)); MetaModel::Init_DefineTransition("Implemented", "ev_monitor", array("target_state"=>"Monitored", "actions"=>array('SetLastUpDate'), "user_restriction"=>null)); - MetaModel::Init_DefineTransition("Implemented", "ev_finish", array("target_state"=>"Closed", "actions"=>array('SetLastUpDate'), "user_restriction"=>null)); - MetaModel::Init_DefineTransition("Monitored", "ev_finish", array("target_state"=>"Closed", "actions"=>array(), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Implemented", "ev_finish", array("target_state"=>"Closed", "actions"=>array('SetClosureDate','SetLastUpDate'), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Monitored", "ev_finish", array("target_state"=>"Closed", "actions"=>array('SetClosureDate','SetLastUpDate'), "user_restriction"=>null)); } public function Generate(cmdbDataGenerator $oGenerator) diff --git a/business/ServiceDesk.business.php b/business/ServiceDesk.business.php index 9ecd9022a..26d4a724f 100644 --- a/business/ServiceDesk.business.php +++ b/business/ServiceDesk.business.php @@ -45,7 +45,7 @@ class bizServiceCall extends cmdbAbstractObject MetaModel::Init_AddAttribute(new AttributeEnum("type", array("label"=>"Type", "description"=>"Type of the Incident", "allowed_values"=>new ValueSetEnum("Network,Server,Desktop,Application"), "sql"=>"type", "default_value"=>"Server", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalKey("org_id", array("targetclass"=>"bizOrganization", "label"=>"Customer", "description"=>"Customer concerned by this service call", "allowed_values"=>null, "sql"=>"customer", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("customer_name", array("label"=>"Customer", "description"=>"Name of the customer raising this service call", "allowed_values"=>null, "extkey_attcode"=> 'org_id', "target_attcode"=>"name"))); - MetaModel::Init_AddAttribute(new AttributeEnum("call_status", array("label"=>"Status", "description"=>"Status of the ticket", "allowed_values"=>new ValueSetEnum("New, Assigned, WorkInProgress,Closed"), "sql"=>"call_status", "default_value"=>"New", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("call_status", array("label"=>"Status", "description"=>"Status of the ticket", "allowed_values"=>new ValueSetEnum("New, Assigned, WorkInProgress,Resolved,Closed"), "sql"=>"call_status", "default_value"=>"New", "is_null_allowed"=>false, "depends_on"=>array()))); // SetPossibleValues("status",array("Open","Monitored","Closed")); MetaModel::Init_AddAttribute(new AttributeText("call_description", array("label"=>"Description", "description"=>"Description of the call as describe by caller", "allowed_values"=>null, "sql"=>"call_description", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeDate("creation_date", array("label"=>"Creation date", "description"=>"Call creation date", "allowed_values"=>null, "sql"=>"creation_date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); @@ -104,18 +104,21 @@ class bizServiceCall extends cmdbAbstractObject MetaModel::Init_DefineState("Assigned", array("label"=>"Assigned", "description"=>"Call is assigned to somebody", "attribute_inherit"=>null, "attribute_list"=>array('name' => OPT_ATT_READONLY, "title"=>OPT_ATT_READONLY, "org_id"=>OPT_ATT_READONLY, "caller_id"=>OPT_ATT_READONLY, "call_description"=>OPT_ATT_READONLY, "creation_date"=>OPT_ATT_READONLY,'end_date' => OPT_ATT_HIDDEN, "workgroup_id"=>OPT_ATT_MUSTCHANGE, "agent_id"=>OPT_ATT_MUSTCHANGE))); MetaModel::Init_DefineState("WorkInProgress", array("label"=>"Work In Progress", "description"=>"Work is in progress", "attribute_inherit"=>null, "attribute_list"=>array("title"=>OPT_ATT_READONLY, "org_id"=>OPT_ATT_READONLY, "caller_id"=>OPT_ATT_READONLY, "call_description"=>OPT_ATT_READONLY,'end_date' => OPT_ATT_HIDDEN, "creation_date"=>OPT_ATT_READONLY,"workgroup_id"=>OPT_ATT_MANDATORY, "agent_id"=>OPT_ATT_MANDATORY))); - MetaModel::Init_DefineState("Closed", array("label"=>"Closed", "description"=>"Call is closed", "attribute_inherit"=>null, "attribute_list"=>array("workgroup_id"=>OPT_ATT_MANDATORY, "agent_id"=>OPT_ATT_MANDATORY, "resolution"=>OPT_ATT_MANDATORY, "end_date"=>OPT_ATT_MANDATORY))); + MetaModel::Init_DefineState("Resolved", array("label"=>"Resolved", "description"=>"Call is resolved", "attribute_inherit"=>null, "attribute_list"=>array("workgroup_id"=>OPT_ATT_MANDATORY, "agent_id"=>OPT_ATT_MANDATORY, "resolution"=>OPT_ATT_MANDATORY, "end_date"=>OPT_ATT_MANDATORY,"resolution"=>OPT_ATT_MANDATORY))); + MetaModel::Init_DefineState("Closed", array("label"=>"Closed", "description"=>"Call is closed", "attribute_inherit"=>null, "attribute_list"=>array("workgroup_id"=>OPT_ATT_READONLY, "agent_id"=>OPT_ATT_READONLY, "resolution"=>OPT_ATT_READONLY, "end_date"=>OPT_ATT_READONLY))); MetaModel::Init_DefineStimulus("ev_assign", new StimulusUserAction(array("label"=>"Assign this call", "description"=>"Assign this call to a group and an agent"))); MetaModel::Init_DefineStimulus("ev_reassign", new StimulusUserAction(array("label"=>"Reassign this call", "description"=>"Reassign this call to a different group and agent"))); MetaModel::Init_DefineStimulus("ev_start_working", new StimulusUserAction(array("label"=>"Work on this call", "description"=>"Start working on this call"))); - MetaModel::Init_DefineStimulus("ev_close", new StimulusUserAction(array("label"=>"Close this call", "description"=>"Close/resolve this call"))); + MetaModel::Init_DefineStimulus("ev_resolve", new StimulusUserAction(array("label"=>"Resolve this call", "description"=>"Resolve this call"))); + MetaModel::Init_DefineStimulus("ev_close", new StimulusUserAction(array("label"=>"Close this call", "description"=>"Close this call"))); MetaModel::Init_DefineTransition("New", "ev_assign", array("target_state"=>"Assigned", "actions"=>array(), "user_restriction"=>null)); MetaModel::Init_DefineTransition("Assigned", "ev_reassign", array("target_state"=>"Assigned", "actions"=>array(), "user_restriction"=>null)); MetaModel::Init_DefineTransition("Assigned", "ev_start_working", array("target_state"=>"WorkInProgress", "actions"=>array(), "user_restriction"=>null)); MetaModel::Init_DefineTransition("WorkInProgress", "ev_reassign", array("target_state"=>"Assigned", "actions"=>array(), "user_restriction"=>null)); - MetaModel::Init_DefineTransition("WorkInProgress", "ev_close", array("target_state"=>"Closed", "actions"=>array('SetClosureDate'), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("WorkInProgress", "ev_resolve", array("target_state"=>"Resolved", "actions"=>array(), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Resolved", "ev_close", array("target_state"=>"Closed", "actions"=>array('SetClosureDate'), "user_restriction"=>null)); } diff --git a/business/ServiceMgmt.business.php b/business/ServiceMgmt.business.php index 9f6d6621c..cfc028b3c 100644 --- a/business/ServiceMgmt.business.php +++ b/business/ServiceMgmt.business.php @@ -110,17 +110,17 @@ class bizContract extends cmdbAbstractObject MetaModel::Init_AddAttribute(new AttributeExternalKey("service_id", array("targetclass"=>"bizService", "label"=>"Service", "description"=>"Provider for this contract", "allowed_values"=>null, "sql"=>"service_id", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("provider_name", array("label"=>"Provider", "description"=>"name of the service provider", "allowed_values"=>null, "extkey_attcode"=> 'service_id', "target_attcode"=>"provider_name"))); MetaModel::Init_AddAttribute(new AttributeExternalField("service_name", array("label"=>"Service", "description"=>"name of the service", "allowed_values"=>null, "extkey_attcode"=> 'service_id', "target_attcode"=>"name"))); - MetaModel::Init_AddAttribute(new AttributeExternalKey("team_id", array("targetclass"=>"bizTeam", "label"=>"Team", "description"=>"Team managing this contract", "allowed_values"=>null, "sql"=>"team_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("team_id", array("targetclass"=>"bizTeam", "label"=>"Team", "description"=>"Team managing this contract", "allowed_values"=>null, "sql"=>"team_id", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("team_name", array("label"=>"Team", "description"=>"name of the team managing this contract", "allowed_values"=>null, "extkey_attcode"=> 'team_id', "target_attcode"=>"name"))); - MetaModel::Init_AddAttribute(new AttributeEnum("service_level", array("label"=>"Service Level", "description"=>"Level of service for this contract", "allowed_values"=>new ValueSetEnum("Gold,Silver,Bronze"), "sql"=>"service_level", "default_value"=>"Bronze", "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeEnum("cost_unit", array("label"=>"Cost Unit", "description"=>"Cost unit to compute global cost for this contract", "allowed_values"=>new ValueSetEnum("Devices,Persons,Applications,Global"), "sql"=>"cost_unit", "default_value"=>"Global", "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeEnum("cost_freq", array("label"=>"Billing frequency", "description"=>"Frequency of cost for this contract", "allowed_values"=>new ValueSetEnum("Monthly,Yearly,Once"), "sql"=>"cost_freq", "default_value"=>"Once", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("service_level", array("label"=>"Service Level", "description"=>"Level of service for this contract", "allowed_values"=>new ValueSetEnum("Gold,Silver,Bronze"), "sql"=>"service_level", "default_value"=>"Bronze", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("cost_unit", array("label"=>"Cost Unit", "description"=>"Cost unit to compute global cost for this contract", "allowed_values"=>new ValueSetEnum("Devices,Persons,Applications,Global"), "sql"=>"cost_unit", "default_value"=>"Global", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("cost_freq", array("label"=>"Billing frequency", "description"=>"Frequency of cost for this contract", "allowed_values"=>new ValueSetEnum("Monthly,Yearly,Once"), "sql"=>"cost_freq", "default_value"=>"Once", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("cost", array("label"=>"Cost", "description"=>"Cost of this contract", "allowed_values"=>null, "sql"=>"cost", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeEnum("currency", array("label"=>"Currency", "description"=>"Currency of cost for this contract", "allowed_values"=>new ValueSetEnum("Euros,Dollars"), "sql"=>"currency", "default_value"=>"Euros", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("currency", array("label"=>"Currency", "description"=>"Currency of cost for this contract", "allowed_values"=>new ValueSetEnum("Euros,Dollars"), "sql"=>"currency", "default_value"=>"Euros", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeText("description", array("label"=>"Description", "description"=>"Description of this contract", "allowed_values"=>null, "sql"=>"description", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeDate("move2prod_date", array("label"=>"Date of Move To Production", "description"=>"Date when the contract is on production", "allowed_values"=>null, "sql"=>"move2prod_date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeDate("move2prod_date", array("label"=>"Date of Move To Production", "description"=>"Date when the contract is on production", "allowed_values"=>null, "sql"=>"move2prod_date", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeDate("end_prod", array("label"=>"Date of End Of Production", "description"=>"Date when the contract is stopped", "allowed_values"=>null, "sql"=>"end_date", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeEnum("status", array("label"=>"Status", "description"=>"Status of the contract", "allowed_values"=>new ValueSetEnum("New, Negotiating, Signed, Production, Notice, Finished"), "sql"=>"status", "default_value"=>"New", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("status", array("label"=>"Status", "description"=>"Status of the contract", "allowed_values"=>new ValueSetEnum("New, Negotiating, Signed, Production,Finished"), "sql"=>"status", "default_value"=>"New", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeEnum("type", array("label"=>"Type", "description"=>"Type of the contract", "allowed_values"=>new ValueSetEnum("Hardware,Software,Support,Licence"), "sql"=>"type", "default_value"=>"Support", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeInteger("version_number", array("label"=>"Version", "description"=>"Revision number for this contract", "allowed_values"=>null, "sql"=>"version_number", "default_value"=>1, "is_null_allowed"=>false, "depends_on"=>array()))); @@ -141,37 +141,27 @@ class bizContract extends cmdbAbstractObject // Life cycle MetaModel::Init_DefineState("New", array("label"=>"New", "description"=>"Newly created contract", "attribute_inherit"=>null, - "attribute_list"=>array())); + "attribute_list"=>array('name' => OPT_ATT_MANDATORY,'org_id' => OPT_ATT_MANDATORY, 'service_id' => OPT_ATT_MANDATORY,'type' => OPT_ATT_MANDATORY, 'description' => OPT_ATT_MANDATORY))); MetaModel::Init_DefineState("Negotiating", array("label"=>"Negotiating", "description"=>"The contract is being worked on", "attribute_inherit"=>null, - "attribute_list"=>array())); + "attribute_list"=>array('name' => OPT_ATT_READONLY,'org_id' => OPT_ATT_READONLY))); MetaModel::Init_DefineState("Signed", array("label"=>"Signed", "description"=>"The contract has been signed", "attribute_inherit"=>null, - "attribute_list"=>array())); + "attribute_list"=>array( 'name' => OPT_ATT_READONLY,'org_id' => OPT_ATT_READONLY, 'service_id' => OPT_ATT_READONLY,'type' => OPT_ATT_READONLY, 'service_level' => OPT_ATT_MANDATORY , 'cost_unit' => OPT_ATT_MANDATORY , 'cost_freq' => OPT_ATT_MANDATORY , 'cost' => OPT_ATT_MANDATORY, 'currency' => OPT_ATT_MANDATORY))); MetaModel::Init_DefineState("Production", array("label"=>"Production", "description"=>"The contract is effective in production", "attribute_inherit"=>null, - "attribute_list"=>array())); - MetaModel::Init_DefineState("Notice", array("label"=>"Notice", "description"=>"The contract is about to be terminated", "attribute_inherit"=>null, - "attribute_list"=>array())); + "attribute_list"=>array('name' => OPT_ATT_READONLY,'org_id' => OPT_ATT_READONLY, 'service_id' => OPT_ATT_READONLY,'type' => OPT_ATT_READONLY, 'service_level' => OPT_ATT_READONLY , 'cost_unit' => OPT_ATT_READONLY , 'cost_freq' => OPT_ATT_READONLY , 'cost' => OPT_ATT_READONLY, 'currency' => OPT_ATT_READONLY,'move2prod_date' => OPT_ATT_MANDATORY,'end_prod' => OPT_ATT_MANDATORY))); MetaModel::Init_DefineState("Finished", array("label"=>"Finished", "description"=>"The contract is terminated", "attribute_inherit"=>null, - "attribute_list"=>array())); + "attribute_list"=>array('name' => OPT_ATT_READONLY,'org_id' => OPT_ATT_READONLY, 'service_id' => OPT_ATT_READONLY,'type' => OPT_ATT_READONLY, 'service_level' => OPT_ATT_READONLY , 'cost_unit' => OPT_ATT_READONLY , 'cost_freq' => OPT_ATT_READONLY , 'cost' => OPT_ATT_READONLY, 'currency' => OPT_ATT_READONLY,'move2prod_date' => OPT_ATT_READONLY,'end_prod' => OPT_ATT_READONLY,'team_id' => OPT_ATT_READONLY,'description' => OPT_ATT_READONLY))); - MetaModel::Init_DefineStimulus("ev_freeze_version", new StimulusUserAction(array("label"=>"Freeze this version", "description"=>"This version of the contract is published"))); + MetaModel::Init_DefineStimulus("ev_negociate", new StimulusUserAction(array("label"=>"Negociate this contract", "description"=>"This version of the contract is published"))); MetaModel::Init_DefineStimulus("ev_sign", new StimulusUserAction(array("label"=>"Sign this contract", "description"=>"This contract is being signed"))); MetaModel::Init_DefineStimulus("ev_begin", new StimulusUserAction(array("label"=>"Move to production", "description"=>"The contract becomes applicable in production"))); - MetaModel::Init_DefineStimulus("ev_notice", new StimulusUserAction(array("label"=>"Start notice period", "description"=>"The end date of the contract is approaching"))); MetaModel::Init_DefineStimulus("ev_terminate", new StimulusUserAction(array("label"=>"Ends this contract", "description"=>"The contract is ending"))); - MetaModel::Init_DefineStimulus("ev_elapsed", new StimulusUserAction(array("label"=>"Times up [Do not click!]", "description"=>"The contract over"))); - MetaModel::Init_DefineTransition("New", "ev_freeze_version", array("target_state"=>"Negotiating", "actions"=>array('IncrementVersion'), "user_restriction"=>null)); - MetaModel::Init_DefineTransition("Negotiating", "ev_freeze_version", array("target_state"=>"Negotiating", "actions"=>array('IncrementVersion'), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("New", "ev_negociate", array("target_state"=>"Negotiating", "actions"=>array('IncrementVersion'), "user_restriction"=>null)); MetaModel::Init_DefineTransition("Negotiating", "ev_sign", array("target_state"=>"Signed", "actions"=>array(), "user_restriction"=>null)); MetaModel::Init_DefineTransition("Negotiating", "ev_terminate", array("target_state"=>"Finished", "actions"=>array(), "user_restriction"=>null)); - MetaModel::Init_DefineTransition("Signed", "ev_freeze_version", array("target_state"=>"Signed", "actions"=>array('IncrementVersion'), "user_restriction"=>null)); - MetaModel::Init_DefineTransition("Signed", "ev_begin", array("target_state"=>"Production", "actions"=>array(), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Signed", "ev_begin", array("target_state"=>"Production", "actions"=>array('SetProdDate'), "user_restriction"=>null)); MetaModel::Init_DefineTransition("Signed", "ev_terminate", array("target_state"=>"Finished", "actions"=>array(), "user_restriction"=>null)); - MetaModel::Init_DefineTransition("Production", "ev_freeze_version", array("target_state"=>"Production", "actions"=>array('IncrementVersion'), "user_restriction"=>null)); - MetaModel::Init_DefineTransition("Production", "ev_elapsed", array("target_state"=>"Notice", "actions"=>array(), "user_restriction"=>null)); MetaModel::Init_DefineTransition("Production", "ev_terminate", array("target_state"=>"Finished", "actions"=>array(), "user_restriction"=>null)); - MetaModel::Init_DefineTransition("Notice", "ev_elapsed", array("target_state"=>"Finished", "actions"=>array(), "user_restriction"=>null)); - MetaModel::Init_DefineTransition("Notice", "ev_terminate", array("target_state"=>"Finished", "actions"=>array(), "user_restriction"=>null)); MetaModel::Init_SetZListItems('details', array('name', 'status', 'org_id', 'service_id','provider_name','type','description','team_id','service_level','cost','currency','cost_unit','cost_freq','move2prod_date','end_prod', 'version_number')); // Attributes to be displayed for the complete details @@ -188,6 +178,12 @@ class bizContract extends cmdbAbstractObject $this->Set('version_number', $this->Get('version_number') + 1); return true; } + + public function SetProdDate($sStimulusCode) + { + $this->Set('move2prod_date', time()); + return true; + } } diff --git a/business/incidentMgmt.business.php b/business/incidentMgmt.business.php index bccc74ba8..77f1c0477 100644 --- a/business/incidentMgmt.business.php +++ b/business/incidentMgmt.business.php @@ -45,21 +45,21 @@ class bizIncidentTicket extends cmdbAbstractObject MetaModel::Init_AddAttribute(new AttributeEnum("type", array("label"=>"Type", "description"=>"Type of the Incident", "allowed_values"=>new ValueSetEnum("Network,Server,Desktop,Application"), "sql"=>"type", "default_value"=>"Server", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalKey("org_id", array("targetclass"=>"bizOrganization", "label"=>"Customer", "description"=>"who is impacted by the ticket", "allowed_values"=>null, "sql"=>"customer", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("customer_name", array("label"=>"Customer", "description"=>"Name of the customer impacted by this ticket", "allowed_values"=>null, "extkey_attcode"=> 'org_id', "target_attcode"=>"name"))); - MetaModel::Init_AddAttribute(new AttributeEnum("ticket_status", array("label"=>"Status", "description"=>"Status of the ticket", "allowed_values"=>new ValueSetEnum("New, Assigned, WorkInProgress, Closed"), "sql"=>"ticket_status", "default_value"=>"New", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("ticket_status", array("label"=>"Status", "description"=>"Status of the ticket", "allowed_values"=>new ValueSetEnum("New, Assigned, WorkInProgress, Resolved, Closed"), "sql"=>"ticket_status", "default_value"=>"New", "is_null_allowed"=>false, "depends_on"=>array()))); // SetPossibleValues("status",array("Open","Monitored","Closed")); MetaModel::Init_AddAttribute(new AttributeText("initial_situation", array("label"=>"Initial Situation", "description"=>"Initial situation of the Incident", "allowed_values"=>null, "sql"=>"initial_situation", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeText("current_situation", array("label"=>"Current Situation", "description"=>"Current situation of the Incident", "allowed_values"=>null, "sql"=>"current_situation", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeDate("start_date", array("label"=>"Starting date", "description"=>"Incident starting date", "allowed_values"=>null, "sql"=>"start_date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); // définir une date de défaut à maintenant, alias creation ou modification du ticket - MetaModel::Init_AddAttribute(new AttributeDate("last_update", array("label"=>"Last update", "description"=>"last time the Ticket was modified", "allowed_values"=>null, "sql"=>"last_update", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeDate("last_update", array("label"=>"Last update", "description"=>"last time the Ticket was modified", "allowed_values"=>null, "sql"=>"last_update", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeDate("next_update", array("label"=>"Next update", "description"=>"next time the Ticket is expected to be modified", "allowed_values"=>null, "sql"=>"next_update", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeDate("end_date", array("label"=>"Closure Date", "description"=>"Date when the Ticket was closed", "allowed_values"=>null, "sql"=>"closed_date", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeExternalKey("caller_id", array("targetclass"=>"bizPerson", "jointype"=> "", "label"=>"Caller", "description"=>"person that trigger incident", "allowed_values"=>new ValueSetObjects('SELECT bizPerson AS p WHERE p.org_id = :this->org_id'), "sql"=>"caller_id", "is_null_allowed"=>false, "depends_on"=>array('org_id')))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("caller_id", array("targetclass"=>"bizPerson", "jointype"=> "", "label"=>"Caller", "description"=>"person that trigger incident", "allowed_values"=>new ValueSetObjects('SELECT bizPerson AS p WHERE p.org_id = :this->org_id'), "sql"=>"caller_id", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("caller_mail", array("label"=>"Caller", "description"=>"Person that trigger this incident", "allowed_values"=>null, "extkey_attcode"=> 'caller_id', "target_attcode"=>"email"))); MetaModel::Init_AddAttribute(new AttributeString("impact", array("label"=>"Impact", "description"=>"Impact of the Incident", "allowed_values"=>null, "sql"=>"impact", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeExternalKey("workgroup_id", array("targetclass"=>"bizWorkgroup", "jointype"=> "", "label"=>"Workgroup", "description"=>"which workgroup is owning ticket", "allowed_values"=>null, "sql"=>"workgroup_id", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("workgroup_id", array("targetclass"=>"bizWorkgroup", "jointype"=> "", "label"=>"Workgroup", "description"=>"which workgroup is owning ticket", "allowed_values"=>null, "sql"=>"workgroup_id", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("workgroup_name", array("label"=>"Workgroup", "description"=>"name of workgroup managing the Ticket", "allowed_values"=>null, "extkey_attcode"=> 'workgroup_id', "target_attcode"=>"name"))); MetaModel::Init_AddAttribute(new AttributeExternalKey("agent_id", array("targetclass"=>"bizPerson", "jointype"=> "", "label"=>"Agent", "description"=>"who is managing the ticket", "allowed_values"=>null, "sql"=>"agent_id", "is_null_allowed"=>true, "depends_on"=>array("workgroup_id")))); MetaModel::Init_AddAttribute(new AttributeExternalField("agent_mail", array("label"=>"Agent", "description"=>"mail of agent managing the Ticket", "allowed_values"=>null, "extkey_attcode"=> 'agent_id', "target_attcode"=>"email"))); @@ -101,22 +101,25 @@ class bizIncidentTicket extends cmdbAbstractObject MetaModel::Init_DefineState("New", array("label"=>"New (Unassigned)", "description"=>"Newly created ticket", "attribute_inherit"=>null, "attribute_list"=>array('name' => OPT_ATT_READONLY, 'assignment_count' => OPT_ATT_HIDDEN, 'end_date' => OPT_ATT_HIDDEN, 'next_update' => OPT_ATT_HIDDEN, 'last_update' => OPT_ATT_HIDDEN, "title"=>OPT_ATT_MANDATORY, "org_id"=>OPT_ATT_MANDATORY, "caller_id"=>OPT_ATT_MANDATORY, "initial_situation"=>OPT_ATT_MANDATORY, "start_date"=>OPT_ATT_MANDATORY, "workgroup_id"=>OPT_ATT_MANDATORY, - "severity"=>OPT_ATT_MANDATORY, "agent_id"=>OPT_ATT_HIDDEN,"impacted_infra_manual"=>OPT_ATT_MANDATORY, "related_tickets"=>OPT_ATT_MUSTPROMPT))); + "severity"=>OPT_ATT_MANDATORY, "agent_id"=>OPT_ATT_HIDDEN,"impacted_infra_manual"=>OPT_ATT_MANDATORY, "related_tickets"=>OPT_ATT_MUSTPROMPT,"resolution"=>OPT_ATT_HIDDEN))); MetaModel::Init_DefineState("Assigned", array("label"=>"Assigned", "description"=>"Ticket is assigned to somebody", "attribute_inherit"=>null, - "attribute_list"=>array('name' => OPT_ATT_READONLY, "title"=>OPT_ATT_READONLY, "org_id"=>OPT_ATT_READONLY, "caller_id"=>OPT_ATT_READONLY, "initial_situation"=>OPT_ATT_READONLY, "start_date"=>OPT_ATT_READONLY,'assignment_count' => OPT_ATT_READONLY,'end_date' => OPT_ATT_HIDDEN, "workgroup_id"=>OPT_ATT_MUSTCHANGE, "agent_id"=>OPT_ATT_MUSTCHANGE))); - MetaModel::Init_DefineState("WorkInProgress", array("label"=>"Work In Progress", "description"=>"Work is in progress", "attribute_inherit"=>null, "attribute_list"=>array("title"=>OPT_ATT_READONLY, "org_id"=>OPT_ATT_READONLY, "caller_id"=>OPT_ATT_READONLY, "initial_situation"=>OPT_ATT_READONLY,'end_date' => OPT_ATT_HIDDEN, "start_date"=>OPT_ATT_READONLY,"workgroup_id"=>OPT_ATT_MANDATORY, "agent_id"=>OPT_ATT_MANDATORY))); - MetaModel::Init_DefineState("Closed", array("label"=>"Closed", "description"=>"Ticket is closed", "attribute_inherit"=>null, "attribute_list"=>array("workgroup_id"=>OPT_ATT_MANDATORY, "agent_id"=>OPT_ATT_MANDATORY, "resolution"=>OPT_ATT_MANDATORY, "end_date"=>OPT_ATT_MANDATORY))); + "attribute_list"=>array('name' => OPT_ATT_READONLY, "title"=>OPT_ATT_READONLY, "org_id"=>OPT_ATT_READONLY, "caller_id"=>OPT_ATT_READONLY, "initial_situation"=>OPT_ATT_READONLY, "start_date"=>OPT_ATT_READONLY,'assignment_count' => OPT_ATT_READONLY,'end_date' => OPT_ATT_HIDDEN, "workgroup_id"=>OPT_ATT_MUSTCHANGE, "agent_id"=>OPT_ATT_MUSTCHANGE,"resolution"=>OPT_ATT_HIDDEN))); + MetaModel::Init_DefineState("WorkInProgress", array("label"=>"Work In Progress", "description"=>"Work is in progress", "attribute_inherit"=>null, "attribute_list"=>array("title"=>OPT_ATT_READONLY, "org_id"=>OPT_ATT_READONLY, "caller_id"=>OPT_ATT_READONLY, "initial_situation"=>OPT_ATT_READONLY,'end_date' => OPT_ATT_HIDDEN, "start_date"=>OPT_ATT_READONLY,"workgroup_id"=>OPT_ATT_MANDATORY, "agent_id"=>OPT_ATT_MANDATORY,"action_log"=>OPT_ATT_MANDATORY,"impact"=>OPT_ATT_READONLY,"severity"=>OPT_ATT_READONLY))); + MetaModel::Init_DefineState("Resolved", array("label"=>"Resolved", "description"=>"Ticket is resolved", "attribute_inherit"=>null, "attribute_list"=>array('name' => OPT_ATT_READONLY,'type' => OPT_ATT_READONLY, "title"=>OPT_ATT_READONLY, "org_id"=>OPT_ATT_READONLY, "caller_id"=>OPT_ATT_READONLY, "initial_situation"=>OPT_ATT_READONLY,"current_situation"=>OPT_ATT_READONLY,'end_date' => OPT_ATT_READONLY,'last_update' => OPT_ATT_READONLY,'next_update' => OPT_ATT_READONLY, "start_date"=>OPT_ATT_READONLY,"workgroup_id"=>OPT_ATT_READONLY, "agent_id"=>OPT_ATT_READONLY,"action_log"=>OPT_ATT_READONLY,"impact"=>OPT_ATT_READONLY,"severity"=>OPT_ATT_READONLY,"resolution"=>OPT_ATT_MUSTCHANGE))); + MetaModel::Init_DefineState("Closed", array("label"=>"Closed", "description"=>"Ticket is closed", "attribute_inherit"=>null, "attribute_list"=>array('name' => OPT_ATT_READONLY,'type' => OPT_ATT_READONLY, "title"=>OPT_ATT_READONLY, "org_id"=>OPT_ATT_READONLY, "caller_id"=>OPT_ATT_READONLY, "initial_situation"=>OPT_ATT_READONLY,"current_situation"=>OPT_ATT_READONLY,'end_date' => OPT_ATT_READONLY,'last_update' => OPT_ATT_READONLY,'next_update' => OPT_ATT_READONLY, "start_date"=>OPT_ATT_READONLY,"workgroup_id"=>OPT_ATT_READONLY, "agent_id"=>OPT_ATT_READONLY,"action_log"=>OPT_ATT_READONLY,"impact"=>OPT_ATT_READONLY,"severity"=>OPT_ATT_READONLY,"resolution"=>OPT_ATT_READONLY))); MetaModel::Init_DefineStimulus("ev_assign", new StimulusUserAction(array("label"=>"Assign this ticket", "description"=>"Assign this ticket to a group and an agent"))); MetaModel::Init_DefineStimulus("ev_reassign", new StimulusUserAction(array("label"=>"Reassign this ticket", "description"=>"Reassign this ticket to a different group and agent"))); MetaModel::Init_DefineStimulus("ev_start_working", new StimulusUserAction(array("label"=>"Work on this ticket", "description"=>"Start working on this ticket"))); - MetaModel::Init_DefineStimulus("ev_close", new StimulusUserAction(array("label"=>"Close this ticket", "description"=>"Close/resolve this ticket"))); + MetaModel::Init_DefineStimulus("ev_resolve", new StimulusUserAction(array("label"=>"Resolve this ticket", "description"=>"Resolve this ticket"))); + MetaModel::Init_DefineStimulus("ev_close", new StimulusUserAction(array("label"=>"Close this ticket", "description"=>"Close this ticket"))); MetaModel::Init_DefineTransition("New", "ev_assign", array("target_state"=>"Assigned", "actions"=>array('IncrementAssignmentCount'), "user_restriction"=>null)); - MetaModel::Init_DefineTransition("Assigned", "ev_reassign", array("target_state"=>"Assigned", "actions"=>array('IncrementAssignmentCount'), "user_restriction"=>null)); - MetaModel::Init_DefineTransition("Assigned", "ev_start_working", array("target_state"=>"WorkInProgress", "actions"=>array(), "user_restriction"=>null)); - MetaModel::Init_DefineTransition("WorkInProgress", "ev_reassign", array("target_state"=>"Assigned", "actions"=>array('IncrementAssignmentCount'), "user_restriction"=>null)); - MetaModel::Init_DefineTransition("WorkInProgress", "ev_close", array("target_state"=>"Closed", "actions"=>array('SetClosureDate'), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Assigned", "ev_reassign", array("target_state"=>"Assigned", "actions"=>array('IncrementAssignmentCount','SetLastUpdate'), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Assigned", "ev_start_working", array("target_state"=>"WorkInProgress", "actions"=>array('SetLastUpdate'), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("WorkInProgress", "ev_reassign", array("target_state"=>"Assigned", "actions"=>array('IncrementAssignmentCount','SetLastUpdate'), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("WorkInProgress", "ev_resolve", array("target_state"=>"Resolved", "actions"=>array('SetLastUpdate'), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Resolved", "ev_close", array("target_state"=>"Closed", "actions"=>array('SetClosureDate','SetLastUpdate'), "user_restriction"=>null)); } @@ -149,6 +152,12 @@ class bizIncidentTicket extends cmdbAbstractObject return true; } + public function SetLastUpdate($sStimulusCode) + { + $this->Set('last_update', time()); + return true; + } + public function ComputeValues() { $iKey = $this->GetKey(); diff --git a/business/itop.business.class.inc.php b/business/itop.business.class.inc.php index dca260fc0..bdc2327b9 100644 --- a/business/itop.business.class.inc.php +++ b/business/itop.business.class.inc.php @@ -879,7 +879,10 @@ class bizInterface extends logInfra MetaModel::Init_AddAttribute(new AttributeString("mac", array("label"=>"MAC address", "description"=>"MAC address for this interface", "allowed_values"=>null, "sql"=>"mac", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("speed", array("label"=>"Speed (Kb/s)", "description"=>"speed of this interface", "allowed_values"=>null, "sql"=>"speed", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeEnum("duplex", array("label"=>"Duplex", "description"=>"Duplex configured for this interface", "allowed_values"=>new ValueSetEnum("half,full,unknown"), "sql"=>"duplex", "default_value"=>"unknown", "is_null_allowed"=>true, "depends_on"=>array()))); - + MetaModel::Init_AddAttribute(new AttributeExternalKey("if_connected_id", array("targetclass"=>"bizInterface", "label"=>"Connected interface", "description"=>"interface connected to this one", "allowed_values"=>null, "sql"=>"ext_if_id", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("if_connected_name", array("label"=>"Connected interface", "description"=>"name of the interface connected to this one", "allowed_values"=>null, "extkey_attcode"=> 'if_connected_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeExternalField("if_connected_device", array("label"=>"Connected device", "description"=>"name of the device connected to this interface", "allowed_values"=>null, "extkey_attcode"=> 'if_connected_id', "target_attcode"=>"device_name"))); + MetaModel::Init_InheritFilters(); MetaModel::Init_AddFilterFromAttribute("device_id"); MetaModel::Init_AddFilterFromAttribute("device_name"); @@ -888,8 +891,10 @@ class bizInterface extends logInfra MetaModel::Init_AddFilterFromAttribute("physical_type"); MetaModel::Init_AddFilterFromAttribute("ip_address"); MetaModel::Init_AddFilterFromAttribute("mac"); + MetaModel::Init_AddFilterFromAttribute("if_connected_id"); + // Display lists - MetaModel::Init_SetZListItems('details', array('name', 'status', 'org_id', 'device_id', 'device_location_id','severity','logical_type','physical_type','ip_address','mask','mac','speed','duplex')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('details', array('name', 'status', 'org_id', 'device_id', 'device_location_id','severity','logical_type','physical_type','ip_address','mask','mac','speed','duplex','if_connected_name','if_connected_device')); // Attributes to be displayed for the complete details MetaModel::Init_SetZListItems('list', array('name', 'status', 'org_id', 'device_id','severity')); // Attributes to be displayed for a list // Search criteria MetaModel::Init_SetZListItems('standard_search', array('name', 'status', 'ip_address','mac','device_id')); // Criteria of the std search form @@ -937,6 +942,7 @@ class bizInterface extends logInfra * n-n link between any Interfaces */ //////////////////////////////////////////////////////////////////////////////////// +/* class lnkInterfaces extends cmdbAbstractObject { public static function Init() @@ -979,6 +985,7 @@ class lnkInterfaces extends cmdbAbstractObject MetaModel::Init_SetZListItems('list', array('interface1_id', 'interface1_device_id', 'interface2_id', 'interface2_device_id', 'link_type')); // Attributes to be displayed for the complete details } } +*/ //////////////////////////////////////////////////////////////////////////////////// /** diff --git a/business/templates/application.html b/business/templates/application.html index 9540a63bd..eeb4afd9b 100644 --- a/business/templates/application.html +++ b/business/templates/application.html @@ -12,8 +12,19 @@ SELECT lnkClientServer WHERE client_id = $pkey$ + + lnkInfraTicket: infra_id = $pkey$ + + + bizChangeTicket: PKEY IS ticket_id IN (lnkInfraChangeTicket: infra_id = $pkey$) + + SELECT lnkInfraContract WHERE infra_id = $pkey$ + + SELECT lnkInfraGrouping WHERE infra_id = $pkey$ + +
    diff --git a/business/templates/interface.html b/business/templates/interface.html index dca953a93..1911d01df 100644 --- a/business/templates/interface.html +++ b/business/templates/interface.html @@ -7,7 +7,11 @@ SELECT bizInterface WHERE id = $pkey$ - SELECT lnkInterfaces WHERE interface1_id = $pkey$ + SELECT bizInterface WHERE if_connected_id = $pkey$ + + SELECT lnkInfraGrouping WHERE infra_id = $pkey$ + + diff --git a/business/templates/network.device.html b/business/templates/network.device.html index 276247d60..56f9000a2 100644 --- a/business/templates/network.device.html +++ b/business/templates/network.device.html @@ -18,11 +18,11 @@ bizChangeTicket: PKEY IS ticket_id IN (lnkInfraChangeTicket: infra_id = $pkey$) + + SELECT lnkInfraGrouping WHERE infra_id = $pkey$ + SELECT lnkDocumentRealObject WHERE object_id = $pkey$ - - - - + diff --git a/business/templates/pc.html b/business/templates/pc.html index f283c13e9..fcb8427b9 100644 --- a/business/templates/pc.html +++ b/business/templates/pc.html @@ -21,6 +21,9 @@ bizIncidentTicket: PKEY IS ticket_id IN (lnkInfraTicket: infra_id = $pkey$) + + SELECT lnkInfraGrouping WHERE infra_id = $pkey$ + SELECT lnkDocumentRealObject WHERE object_id = $pkey$ diff --git a/business/templates/person.html b/business/templates/person.html index ad36b55e2..15b519fc9 100644 --- a/business/templates/person.html +++ b/business/templates/person.html @@ -9,7 +9,13 @@ SELECT lnkContactTeam WHERE contact_id = $pkey$ - SELECT lnkContactRealObject WHERE contact_id = $pkey$ + + SELECT lnkContactRealObject WHERE contact_id = $pkey$ + + + SELECT bizServiceCall WHERE caller_id = $pkey$ + + SELECT lnkDocumentRealObject WHERE object_id = $pkey$ diff --git a/business/templates/server.html b/business/templates/server.html index c0bf05476..30597b06d 100644 --- a/business/templates/server.html +++ b/business/templates/server.html @@ -24,12 +24,11 @@ bizChangeTicket: PKEY IS ticket_id IN (lnkInfraChangeTicket: infra_id = $pkey$) + + SELECT lnkInfraGrouping WHERE infra_id = $pkey$ + SELECT lnkDocumentRealObject WHERE object_id = $pkey$ - - - - diff --git a/business/templates/team.html b/business/templates/team.html index 042a984ac..5a0edcf37 100644 --- a/business/templates/team.html +++ b/business/templates/team.html @@ -12,6 +12,10 @@ SELECT lnkContactInfra WHERE contact_id = $pkey$ + + SELECT bizWorkgroup WHERE team_id = $pkey$ + + SELECT lnkDocumentRealObject WHERE object_id = $pkey$ From 043552536d3a734230139a56418966196a35703e Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Mon, 14 Sep 2009 16:06:04 +0000 Subject: [PATCH 084/227] Cosmetics on the bulk load (followed new test plan) SVN:trunk[159] --- core/bulkchange.class.inc.php | 4 +- core/metamodel.class.php | 20 ++++++--- pages/csvimport.php | 80 +++++++++++++++++++++++------------ 3 files changed, 68 insertions(+), 36 deletions(-) diff --git a/core/bulkchange.class.inc.php b/core/bulkchange.class.inc.php index 6b4332d52..fae8db1eb 100644 --- a/core/bulkchange.class.inc.php +++ b/core/bulkchange.class.inc.php @@ -172,7 +172,7 @@ class RowStatus_NewObj extends RowStatus } else { - if (empty($this->m_sClass)) + if (!empty($this->m_sClass)) { $oObj = MetaModel::GetObject($this->m_sClass, $this->m_iObjKey); return 'Created '.$oObj->GetHyperLink(); @@ -281,7 +281,7 @@ class BulkChange { case 0: $aErrors[$sAttCode] = "Object not found"; - $aResults[$sAttCode]= new CellChangeSpec_Issue(null, $oTargetObj->Get($sAttCode), 'Object not found'); + $aResults[$sAttCode]= new CellChangeSpec_Issue(null, $oTargetObj->Get($sAttCode), 'Object not found - check the spelling (no space before/after)'); break; case 1: // Do change the external key attribute diff --git a/core/metamodel.class.php b/core/metamodel.class.php index 39d116f49..f93f8a897 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -2564,7 +2564,7 @@ abstract class MetaModel return $iTotalHits.' ('.implode(', ', $aRes).')'; } - public static function MakeSingleRow($sClass, $iKey) + public static function MakeSingleRow($sClass, $iKey, $bMustBeFound = true) { if (!array_key_exists($sClass, self::$aQueryCacheGetObject)) { @@ -2591,7 +2591,7 @@ abstract class MetaModel $aRow = CMDBSource::FetchArray($res); CMDBSource::FreeResult($res); - if (empty($aRow)) + if ($bMustBeFound && empty($aRow)) { throw new CoreException("No result for the single row query: '$sSQL'"); } @@ -2625,10 +2625,10 @@ abstract class MetaModel return new $sClass($aRow); } - public static function GetObject($sClass, $iKey) + public static function GetObject($sClass, $iKey, $bMustBeFound = true) { self::_check_subclass($sClass); - $aRow = self::MakeSingleRow($sClass, $iKey); + $aRow = self::MakeSingleRow($sClass, $iKey, $bMustBeFound); if (empty($aRow)) { return null; @@ -2636,9 +2636,17 @@ abstract class MetaModel return self::GetObjectByRow($sClass, $aRow); } - public static function GetHyperLink($sTargetClass, $sOldValue) + public static function GetHyperLink($sTargetClass, $iKey) { - $oObj = self::GetObject($sTargetClass, $sOldValue); + if ($iKey < 0) + { + return "$sTargetClass: $iKey (invalid value)"; + } + $oObj = self::GetObject($sTargetClass, $iKey, false); + if (is_null($oObj)) + { + return "$sTargetClass: $iKey (not found)"; + } return $oObj->GetHyperLink(); } diff --git a/pages/csvimport.php b/pages/csvimport.php index 24bf03798..256aa2957 100644 --- a/pages/csvimport.php +++ b/pages/csvimport.php @@ -75,6 +75,8 @@ function ShowTableForm($oPage, $oCSVParser, $sClass) $aFields = array(); foreach($oCSVParser->ListFields() as $iFieldIndex=>$sFieldName) { + $sFieldName = trim($sFieldName); + $aOptions = array(); $aOptions['id'] = array( 'LabelHtml' => "Private key", @@ -336,18 +338,17 @@ function Do_Welcome($oPage, $sClass) $sCSVData = utils::ReadPostedParam('csvdata'); - $oPage->add("
    "); - $oPage->MakeClassesSelect("class", $sClass, 50); - //$oPage->Add(""); - $oPage->add("
    "); - $oPage->add(""); - $oPage->add("
    "); - $oPage->add(""); - $oPage->add("
    \n"); - $oPage->add("
    "); + $oPage->add("
    "); + $oPage->MakeClassesSelect("class", $sClass, 50); + $oPage->add("
    "); + $oPage->add(""); + $oPage->add("
    "); + $oPage->add(""); + $oPage->add("
    \n"); + $oPage->add("
    "); - // As a help to the end-user, let's display the list of possible fields - // for a class, that can be copied/pasted into the CSV area. + // As a help to the end-user, let's display the list of possible fields + // for a class, that can be copied/pasted into the CSV area. $sCurrentList = ""; $aHeadersList = array(); foreach(MetaModel::GetClasses('bizmodel') as $sClassName) @@ -385,8 +386,8 @@ function Do_Welcome($oPage, $sClass) \n"); $oPage->add_ready_script("$('#select_class').change( function() {DisplayFields(this.value);} );"); - - $oPage->add("Fields for this object: "); + $oPage->add("
    "); + $oPage->add("Fields for this object
    "); } @@ -437,6 +438,15 @@ function DoProcessOrVerify($oPage, $sClass, CMDBChange $oChange = null) $aFieldMap = utils::ReadPostedParam('fmap'); $aIsReconcKey = utils::ReadPostedParam('iskey'); + if (empty($aIsReconcKey)) + { + $oPage->p("Error: no reconciliation key has been specified. Please specify which field(s) will be used to identify the object"); + + $oPage->add("\n"); + $oPage->add("\n"); + return; + } + $oCSVParser = new CSVParser($sCSVData); $oCSVParser->SetSeparator($sSep); $oCSVParser->SetSkipLines($iSkip); @@ -521,6 +531,7 @@ function DoProcessOrVerify($oPage, $sClass, CMDBChange $oChange = null) { $oPage->p("ok - required external keys (if any) have been found in the field list"); } + $oPage->p("Note: the procedure will fail if any line has not the same number of columns as the first line"); $oPage->p("

    Check...

    "); } @@ -534,7 +545,7 @@ function DoProcessOrVerify($oPage, $sClass, CMDBChange $oChange = null) $oPage->add_input_hidden("fmap", $aFieldMap); $oPage->add_input_hidden("iskey", $aIsReconcKey); - return; + return true; } function Do_Verify($oPage, $sClass) @@ -542,13 +553,14 @@ function Do_Verify($oPage, $sClass) $oPage->p("

    Bulk load from CSV data / step 3

    "); $sWiztep = "3_verify"; - DoProcessOrVerify($oPage, $sClass, null); - // FORM started by DoProcessOrVerify... - - $oPage->add(""); - $oPage->add(""); - $oPage->add(""); - $oPage->add(""); + if (DoProcessOrVerify($oPage, $sClass, null)) + { + // FORM started by DoProcessOrVerify... + $oPage->add(""); + $oPage->add(""); + $oPage->add(""); + $oPage->add(""); + } } function Do_Execute($oPage, $sClass) @@ -558,15 +570,27 @@ function Do_Execute($oPage, $sClass) $oMyChange = MetaModel::NewObject("CMDBChange"); $oMyChange->Set("date", time()); - $oMyChange->Set("userinfo", "CSV Import"); + $iUser = UserRights::GetContactId(); + if ($iUser != null) + { + // Ok, that's dirty, I admit :-) + $oUser = MetaModel::GetObject('bizContact', $iUser); + $sUser = $oUser->GetName(); + $oMyChange->Set("userinfo", "CSV Import, by ".$sUser); + } + else + { + $oMyChange->Set("userinfo", "CSV Import"); + } $iChangeId = $oMyChange->DBInsert(); - DoProcessOrVerify($oPage, $sClass, $oMyChange); - - // FORM started by DoProcessOrVerify... - $oPage->add(""); - $oPage->add(""); - $oPage->add(""); + if (DoProcessOrVerify($oPage, $sClass, $oMyChange)) + { + // FORM started by DoProcessOrVerify... + $oPage->add(""); + $oPage->add(""); + $oPage->add(""); + } } From 6e4da54706276226218edc3a6dc98a0af7360571 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Mon, 14 Sep 2009 16:50:51 +0000 Subject: [PATCH 085/227] - New cache management for the lifecycle graphs SVN:trunk[160] --- pages/graphviz.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/pages/graphviz.php b/pages/graphviz.php index 32181c4ce..c04919144 100644 --- a/pages/graphviz.php +++ b/pages/graphviz.php @@ -65,8 +65,8 @@ function GraphvizLifecycle($sClass) $sClass = utils::ReadParam('class', 'bizIncidentTicket'); $sDir = dirname(__FILE__); -$sImageFilePath = realpath($sDir."/../images/lifecycle/$sClass.png"); -if (!file_exists($sImageFilePath)) +$sImageFilePath = $sDir."\\..\\images\\lifecycle\\".$sClass.".png"; +if (file_exists("/iTop/Graphviz/bin/dot.exe")) { // create the file with Graphviz $sDotDescription = GraphvizLifecycle($sClass); From 7b6295f0a0c0f5124d5dd0ea40a8f24d80c092f3 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Wed, 16 Sep 2009 07:53:12 +0000 Subject: [PATCH 086/227] Fixed regression: server status was reset SVN:trunk[161] --- core/metamodel.class.php | 4 ++++ pages/UI.php | 14 +++++++++----- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/core/metamodel.class.php b/core/metamodel.class.php index f93f8a897..6299b46a3 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -649,6 +649,10 @@ abstract class MetaModel if (!empty($sStateAttCode)) { $aStates = MetaModel::EnumStates($sClass); + if (!array_key_exists($sState, $aStates)) + { + throw new CoreException("Invalid state '$sState' for class '$sClass', expecting a value in {".implode(', ', array_keys($aStates))."}"); + } $aCurrentState = $aStates[$sState]; if ( (array_key_exists('attribute_list', $aCurrentState)) && (array_key_exists($sAttCode, $aCurrentState['attribute_list'])) ) { diff --git a/pages/UI.php b/pages/UI.php index 6a02f911c..fc2700b71 100644 --- a/pages/UI.php +++ b/pages/UI.php @@ -428,12 +428,16 @@ switch($operation) } else if (!$oAttDef->IsExternalField()) { - $aAttributes[$sAttCode] = trim(utils::ReadPostedParam("attr_$sAttCode", null)); - $previousValue = $oObj->Get($sAttCode); - if (!is_null($aAttributes[$sAttCode]) && ($previousValue != $aAttributes[$sAttCode])) + $rawValue = utils::ReadPostedParam("attr_$sAttCode", null); + if (!is_null($rawValue)) { - $oObj->Set($sAttCode, $aAttributes[$sAttCode]); - $bObjectModified = true; + $aAttributes[$sAttCode] = trim($rawValue); + $previousValue = $oObj->Get($sAttCode); + if ($previousValue != $aAttributes[$sAttCode]) + { + $oObj->Set($sAttCode, $aAttributes[$sAttCode]); + $bObjectModified = true; + } } } } From 66f1622554005070a081ce845ee88f4cbe5bdc65 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Wed, 16 Sep 2009 07:56:14 +0000 Subject: [PATCH 087/227] Fixed regression: ticket "impact" was reset ; resulting from a conflict in the form fields names (impact used both in the main detail form and into hidden links forms) SVN:trunk[162] --- application/ui.linkswidget.class.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/ui.linkswidget.class.inc.php b/application/ui.linkswidget.class.inc.php index e2e5aa72d..074207ff2 100644 --- a/application/ui.linkswidget.class.inc.php +++ b/application/ui.linkswidget.class.inc.php @@ -303,7 +303,7 @@ EOF; $sSubId = $sId.'_'.$index; $aAttrsMap[$sAttCode] = $sSubId; $index++; - $sHTMLValue = cmdbAbstractObject::GetFormElementForField($oPage, $sLinkedClass, $sAttCode, $oAttDef, $sValue, $sDisplayValue, $sSubId); + $sHTMLValue = cmdbAbstractObject::GetFormElementForField($oPage, $sLinkedClass, $sAttCode, $oAttDef, $sValue, $sDisplayValue, $sSubId, $this->m_sAttCode); } $aDetails[] = array('label' => $oAttDef->GetLabel(), 'value' => $sHTMLValue); } From 0669db07f3a41d8e8a0ebecc001fab86c8c57817 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Wed, 16 Sep 2009 07:59:11 +0000 Subject: [PATCH 088/227] Disabled the features not finalized (cloning, bookmarking, bulk operations) SVN:trunk[163] --- application/displayblock.class.inc.php | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/application/displayblock.class.inc.php b/application/displayblock.class.inc.php index d7840f6c8..b78267081 100644 --- a/application/displayblock.class.inc.php +++ b/application/displayblock.class.inc.php @@ -689,16 +689,16 @@ class MenuBlock extends DisplayBlock $sTargetClass = $oAttDef->GetTargetClass(); if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'Add...', 'url' => "../pages/$sUIPage?operation=modify_links&class=$sClass&link_attr=".$aExtraParams['link_attr']."&target_class=$sTargetClass&id=$id&addObjects=true&$sContext"); } if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'Manage...', 'url' => "../pages/$sUIPage?operation=modify_links&class=$sClass&link_attr=".$aExtraParams['link_attr']."&target_class=$sTargetClass&id=$id&sContext"); } - if ($bIsDeleteAllowed) { $aActions[] = array ('label' => 'Remove All', 'url' => "#"); } + //if ($bIsDeleteAllowed) { $aActions[] = array ('label' => 'Remove All', 'url' => "#"); } } else { $sUrl = self::GetAbsoluteUrl(); $aActions[] = array ('label' => 'eMail', 'url' => "mailto:?subject=".$oSet->GetFilter()->__DescribeHTML()."&body=".urlencode("$sUrl?operation=search&filter=$sFilter&$sContext")); $aActions[] = array ('label' => 'CSV Export', 'url' => "../pages/$sUIPage?operation=search&filter=$sFilter&format=csv&$sContext"); - $aActions[] = array ('label' => 'Bookmark...', 'url' => "../pages/ajax.render.php?operation=create&class=$sClass&filter=$sFilter", 'class' => 'jqmTrigger'); + //$aActions[] = array ('label' => 'Bookmark...', 'url' => "../pages/ajax.render.php?operation=create&class=$sClass&filter=$sFilter", 'class' => 'jqmTrigger'); if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'New...', 'url' => "../pages/$sUIPage?operation=new&class=$sClass&$sContext"); } - if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'Clone...', 'url' => "../pages/$sUIPage?operation=clone&class=$sClass&id=$id&$sContext"); } + //if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'Clone...', 'url' => "../pages/$sUIPage?operation=clone&class=$sClass&id=$id&$sContext"); } if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'Modify...', 'url' => "../pages/$sUIPage?operation=modify&class=$sClass&id=$id&$sContext"); } if ($bIsDeleteAllowed) { $aActions[] = array ('label' => 'Delete', 'url' => "../pages/$sUIPage?operation=delete&class=$sClass&id=$id&$sContext"); } } @@ -740,7 +740,7 @@ class MenuBlock extends DisplayBlock if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'Add...', 'url' => "../pages/$sUIPage?operation=modify_links&class=$sClass&link_attr=".$aExtraParams['link_attr']."&target_class=$sTargetClass&id=$id&addObjects=true&$sContext"); } //if ($bIsBulkModifyAllowed) { $aActions[] = array ('label' => 'Add...', 'url' => "../pages/$sUIPage?operation=modify_links&class=$sClass&linkage=".$aExtraParams['linkage']."&id=$id&addObjects=true&$sContext"); } if ($bIsBulkModifyAllowed) { $aActions[] = array ('label' => 'Manage...', 'url' => "../pages/$sUIPage?operation=modify_links&class=$sClass&link_attr=".$aExtraParams['link_attr']."&target_class=$sTargetClass&id=$id&sContext"); } - if ($bIsBulkDeleteAllowed) { $aActions[] = array ('label' => 'Remove All...', 'url' => "#"); } + //if ($bIsBulkDeleteAllowed) { $aActions[] = array ('label' => 'Remove All...', 'url' => "#"); } } else { @@ -748,10 +748,10 @@ class MenuBlock extends DisplayBlock $sUrl = self::GetAbsoluteUrl(); $aActions[] = array ('label' => 'eMail', 'url' => "mailto:?subject=".$oSet->GetFilter()->__DescribeHTML()."&body=".urlencode("$sUrl?operation=search&filter=$sFilter&$sContext")); $aActions[] = array ('label' => 'CSV Export', 'url' => "../pages/$sUIPage?operation=search&filter=$sFilter&format=csv&$sContext"); - $aActions[] = array ('label' => 'Bookmark...', 'url' => "../pages/ajax.render.php?operation=create&class=$sClass&filter=$sFilter", 'class' => 'jqmTrigger'); + //$aActions[] = array ('label' => 'Bookmark...', 'url' => "../pages/ajax.render.php?operation=create&class=$sClass&filter=$sFilter", 'class' => 'jqmTrigger'); if ($bIsModifyAllowed) { $aActions[] = array ('label' => 'New...', 'url' => "../pages/$sUIPage?operation=new&class=$sClass&$sContext"); } - if ($bIsBulkModifyAllowed) { $aActions[] = array ('label' => 'Modify All...', 'url' => "../pages/$sUIPage?operation=modify_all&filter=$sFilter&$sContext"); } - if ($bIsBulkDeleteAllowed) { $aActions[] = array ('label' => 'Delete All', 'url' => "../pages/$sUIPage?operation=delete_all&filter=$sFilter&$sContext"); } + //if ($bIsBulkModifyAllowed) { $aActions[] = array ('label' => 'Modify All...', 'url' => "../pages/$sUIPage?operation=modify_all&filter=$sFilter&$sContext"); } + //if ($bIsBulkDeleteAllowed) { $aActions[] = array ('label' => 'Delete All', 'url' => "../pages/$sUIPage?operation=delete_all&filter=$sFilter&$sContext"); } } } $sHtml .= "
      \n
    • Actions\n
        \n"; From 347021676765c6c36239c2cf3f55cf83661e9043 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Wed, 16 Sep 2009 08:03:42 +0000 Subject: [PATCH 089/227] Fixed a regression on the management of the allowed values and attribute dependencies (symptom: Browse button not working, error when creating a ticket with no CI attached to it) SVN:trunk[164] --- js/wizardhelper.js | 9 ++++++--- pages/ajax.render.php | 2 +- 2 files changed, 7 insertions(+), 4 deletions(-) diff --git a/js/wizardhelper.js b/js/wizardhelper.js index 031571592..09ad3556e 100644 --- a/js/wizardhelper.js +++ b/js/wizardhelper.js @@ -64,7 +64,8 @@ function WizardHelper(sClass) this.UpdateFields = function () { - //console.log('** UpdateFields **') + //console.log('** UpdateFields **'); + //console.log(this.m_oData); for(i=0; i< this.m_oData.m_aAllowedValuesRequested.length; i++) { sAttCode = this.m_oData.m_aAllowedValuesRequested[i]; @@ -79,6 +80,7 @@ function WizardHelper(sClass) //console.log('Setting field:'+sFieldId+' ('+sAttCode+') to: '+defaultValue); var oElement = document.getElementById('att_'+sFieldId); oElement.value = defaultValue; + //console.log('att_'+sFieldId+', set to '+defaultValue); } } @@ -102,8 +104,9 @@ function WizardHelper(sClass) //console.log('oWizard:', this); $.get('ajax.render.php?json_obj=' + this.ToJSON(), { operation: 'wizard_helper' }, - function(json_data){ - //console.log('data received:', json_data); + function(html){ + $('body').append(html); + console.log('data received:', oWizardHelper); //oWizardHelper.FromJSON(json_data); oWizardHelper.UpdateFields(); //console.log(oWizardHelper); diff --git a/pages/ajax.render.php b/pages/ajax.render.php index c43924d60..0edce8c5e 100644 --- a/pages/ajax.render.php +++ b/pages/ajax.render.php @@ -94,7 +94,7 @@ switch($operation) $oWizardHelper->SetAllowedValuesHtml($sAttCode, $sHTMLValue); } - $oPage->add("\n"); + $oPage->add("\n"); break; case 'ajax': From 93e629593c07c84bb86274e4af3cd4cc603bca3a Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Wed, 16 Sep 2009 08:39:02 +0000 Subject: [PATCH 090/227] New menus (source: Erwan) SVN:trunk[165] --- setup/data/structure/1.menus.xml | 1625 +++++++++++++------------ setup/data/structure/export_menus.cmd | 4 +- 2 files changed, 865 insertions(+), 764 deletions(-) diff --git a/setup/data/structure/1.menus.xml b/setup/data/structure/1.menus.xml index 4b7198209..79c989337 100644 --- a/setup/data/structure/1.menus.xml +++ b/setup/data/structure/1.menus.xml @@ -1,762 +1,863 @@ - - - -Tools - -UI.php - - -application -7 -0 -0 - - -All Applications - -UI.php - - -application -999 -14 -0 - - -All Circuits - -UI.php - - -application -999 -14 -0 - - -All Contracts - -./UI.php - - -application -2 -31 -0 - - -All Interfaces - -UI.php - - -application -999 -14 -0 - - -All Network devices - -UI.php - - -application -999 -14 -0 - - -All Patches - -UI.php - - -application -999 -14 -0 - - -All PCs - -UI.php - - -application -999 -14 -0 - - -All Servers - -UI.php - - -application -999 -14 -0 - - -All Subnets - -UI.php - - -application -999 -14 -0 - - -Audit - -./audit.php - - -application -4 -15 -0 - - -Change Management - -./UI.php - - -application -4 -0 -0 - - -Closed Changes - -UI.php - - -application -2 -11 -0 - - -Closed Incident - -./UI.php - - -application -2 -22 -0 - - -Configuration Items - -UI.php - - -application -2 -15 -0 - - -Configuration Management - -UI.php - - -application -2 -0 -0 - - -Contacts - -UI.php - - -application -1 -15 -0 - - -CSV import - -csvimport.php - - -application -999 -1 -0 - - -Document - -UI.php - - -application -6 -15 -0 - - - -Grouping - -UI.php - - -application -3 -15 -0 - - -Incident Management - -./UI.php - - -application -3 -0 -0 - - -Known Errors - -./UI.php - - -application -999 -22 -0 - - -Locations - -UI.php - - -application -5 -15 -0 - - -Negociating contracts - -UI.php - - -application -1 -31 -0 - - -Open Changes - -./UI.php - - -application -1 -11 -0 - - -Open Incidents - -UI.php - - -application -1 -22 -0 - - -Persons - -UI.php - - -application -7 -16 -0 - - - -Scheduled Outages - -./UI.php - - -application -999 -11 -0 - - -Service Management - -./UI.php - - -application -5 -0 -0 - - -Teams - -UI.php - - -application -8 -16 -0 - - - -Welcome - -./UI.php - - -application -1 -0 -0 - - - -Admin tools - -UI.php - - -administrator -1000 -0 -0 - - - -Data Model - -schema.php - - -administrator -1500 -100 -0 - - - -Universal Search - -UniversalSearch.php - - -administrator -1600 -100 -0 - - - -User management - -UI.php - - -administrator -1 -100 -0 - - - -User logins - -UI.php -../images/std_view.gif - -administrator -10 -110 -0 - - - -Profiles - -UI.php -../images/std_view.gif - -administrator -11 -110 -0 - - - -Export - -../webservices/export.php - - -administrator -1001 -100 -0 - - - -Run queries - -./run_query.php - - -administrator -1002 -100 -0 - - - + + + +Admin tools + +UI.php + + +administrator +1000 +0 +0 + + +All Applications + +UI.php + + +application +999 +15 +0 + + +All Circuits + +UI.php + + +application +999 +15 +0 + + +All Contracts + +./UI.php + + +application +2 +33 +0 + + +All Interfaces + +UI.php + + +application +999 +15 +0 + + +All Network devices + +UI.php + + +application +999 +15 +0 + + +All Patches + +UI.php + + +application +999 +15 +0 + + +All PCs + +UI.php + + +application +999 +15 +0 + + +All Servers + +UI.php + + +application +999 +15 +0 + + +All Services + +UI.php + + +application +1 +33 +0 + + +All Subnets + +UI.php + + +application +999 +15 +0 + + +Audit + +./audit.php + + +application +4 +16 +0 + + +Change Management + +./UI.php + + +application +7 +0 +0 + + +Closed Changes + +UI.php + + +application +5 +12 +0 + + +Closed Incident + +./UI.php + + +application +5 +21 +0 + + +Configuration Items + +UI.php + + +application +2 +16 +0 + + +Configuration Management + +UI.php + + +application +2 +0 +0 + + +Contacts + +UI.php + + +application +1 +16 +0 + + +CSV import + +csvimport.php + + +application +999 +35 +0 + + +Data Model + +schema.php + + +administrator +1500 +37 +0 + + +Document + +UI.php + + +application +6 +16 +0 + + +Export + +../webservices/export.php + + +administrator +1001 +37 +0 + + +Grouping + +UI.php + + +application +3 +16 +0 + + +Incident Management + +./UI.php + + +application +5 +0 +0 + + +Known Errors + +./UI.php + + +application +999 +21 +0 + + +Locations + +UI.php + + +application +5 +16 +0 + + +My Changes + +UI.php + + +application +1 +12 +0 + + +My Incidents + +UI.php + + +application +1 +21 +0 + + +My Service calls + +UI.php + + +application +1 +32 +0 + + +Open Changes + +./UI.php + + +application +3 +12 +0 + + +Open Incidents + +UI.php + + +application +3 +21 +0 + + +Open Service calls + +UI.php + + +application +2 +32 +0 + + +Persons + +UI.php + + +application +7 +17 +0 + + +Profiles + +UI.php +../images/std_view.gif + +administrator +11 +40 +0 + + +Run queries + +./run_query.php + + +administrator +1002 +37 +0 + + +Scheduled Outages + +./UI.php + + +application +7 +12 +0 + + +Service Desk + +UI.php + + +application +3 +0 +0 + + +Service Management + +./UI.php + + +application +8 +0 +0 + + +Teams + +UI.php + + +application +8 +17 +0 + + +Tools + +UI.php + + +application +9 +0 +0 + + +Universal Search + +UniversalSearch.php + + +administrator +1600 +37 +0 + + +User logins + +UI.php +../images/std_view.gif + +administrator +10 +40 +0 + + +User management + +UI.php + + +administrator +1 +37 +0 + + +Welcome + +./UI.php + + +application +1 +0 +0 + + \ No newline at end of file diff --git a/setup/data/structure/export_menus.cmd b/setup/data/structure/export_menus.cmd index c034b0bb6..29ac490fe 100644 --- a/setup/data/structure/export_menus.cmd +++ b/setup/data/structure/export_menus.cmd @@ -1,4 +1,4 @@ -SET WEBROOT=http://localhost:81 +SET WEBROOT=http://localhost:81/trunk SET EXPORT=%WEBROOT%/webservices/export.php SET USER=admin @@ -6,5 +6,5 @@ SET PWD=admin REM The order (numbering) of the files is important since REM it dictates the order to import them back -wget --output-document=1.menus.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT menuNode WHERE type%%3D%%27application%%27&format=xml" +wget --output-document=1.menus.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT menuNode WHERE type%%3D%%27application%%27 OR type%%3D%%27administrator%%27&format=xml" pause \ No newline at end of file From 84c636fc0768abe8c07831ebd10acb2220406049 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Wed, 16 Sep 2009 08:48:54 +0000 Subject: [PATCH 091/227] Fixed wrong file path for the graphs (lifecyle), could not work but on a Windows platform SVN:trunk[166] --- pages/graphviz.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/graphviz.php b/pages/graphviz.php index c04919144..6552db085 100644 --- a/pages/graphviz.php +++ b/pages/graphviz.php @@ -65,7 +65,7 @@ function GraphvizLifecycle($sClass) $sClass = utils::ReadParam('class', 'bizIncidentTicket'); $sDir = dirname(__FILE__); -$sImageFilePath = $sDir."\\..\\images\\lifecycle\\".$sClass.".png"; +$sImageFilePath = $sDir."/../images/lifecycle/".$sClass.".png"; if (file_exists("/iTop/Graphviz/bin/dot.exe")) { // create the file with Graphviz From 6964ca3003fb73e323bdbfe0fad35485631fec30 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Wed, 16 Sep 2009 14:14:15 +0000 Subject: [PATCH 092/227] - removed debug trace SVN:trunk[167] --- js/wizardhelper.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/js/wizardhelper.js b/js/wizardhelper.js index 09ad3556e..13f0e78e9 100644 --- a/js/wizardhelper.js +++ b/js/wizardhelper.js @@ -106,7 +106,7 @@ function WizardHelper(sClass) { operation: 'wizard_helper' }, function(html){ $('body').append(html); - console.log('data received:', oWizardHelper); + //console.log('data received:', oWizardHelper); //oWizardHelper.FromJSON(json_data); oWizardHelper.UpdateFields(); //console.log(oWizardHelper); From 5efd4f10f5c2d8b90d9b2d78235019f706cb0539 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Wed, 16 Sep 2009 15:00:40 +0000 Subject: [PATCH 093/227] Fixed a regression: autocomplete not working due to a change in the execution of the script rendered in Ajax SVN:trunk[168] --- application/cmdbabstract.class.inc.php | 4 ++-- js/wizardhelper.js | 2 +- pages/ajax.render.php | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index bc1008ed3..3f9a3cd00 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -645,13 +645,13 @@ abstract class cmdbAbstractObject extends CMDBObject //Enum field or external key, display a combo if (count($aAllowedValues) == 0) { - $sHTMLValue = ""; + $sHTMLValue = ""; } else if (count($aAllowedValues) > 50) { // too many choices, use an autocomplete // The input for the auto complete - $sHTMLValue = ""; + $sHTMLValue = ""; // another hidden input to store & pass the object's Id $sHTMLValue .= "\n"; $oPage->add_ready_script("\$('#label_$iInputId').autocomplete('./ajax.render.php', { minChars:3, onItemSelect:selectItem, onFindValue:findValue, formatItem:formatItem, autoFill:true, keyHolder:'#$iInputId', extraParams:{operation:'autocomplete', sclass:'$sClass',attCode:'".$sAttCode."'}});"); diff --git a/js/wizardhelper.js b/js/wizardhelper.js index 13f0e78e9..3a3d870d8 100644 --- a/js/wizardhelper.js +++ b/js/wizardhelper.js @@ -108,7 +108,7 @@ function WizardHelper(sClass) $('body').append(html); //console.log('data received:', oWizardHelper); //oWizardHelper.FromJSON(json_data); - oWizardHelper.UpdateFields(); + //oWizardHelper.UpdateFields(); // Is done directly in the html provided by ajax.render.php //console.log(oWizardHelper); $('#wizStep'+ G_iCurrentStep).unblock( {fadeOut: 0} ); }); diff --git a/pages/ajax.render.php b/pages/ajax.render.php index 0edce8c5e..c3d783340 100644 --- a/pages/ajax.render.php +++ b/pages/ajax.render.php @@ -94,7 +94,7 @@ switch($operation) $oWizardHelper->SetAllowedValuesHtml($sAttCode, $sHTMLValue); } - $oPage->add("\n"); + $oPage->add("\n"); break; case 'ajax': From 0232d2dadced04e143cd3430a867e57931649f39 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Wed, 16 Sep 2009 16:01:12 +0000 Subject: [PATCH 094/227] User Management: removed limitation on the user rights that could not be established without a given object set, and added a feedback in the user details page (grant summary, based on the results returned by the std user management API, thus is reliable) SVN:trunk[169] --- .../userrights/userrightsmatrix.class.inc.php | 6 +- .../userrights/userrightsnull.class.inc.php | 6 +- .../userrightsprofile.class.inc.php | 215 ++++++++++++++---- application/nicewebpage.class.inc.php | 9 +- core/userrights.class.inc.php | 24 +- pages/advanced_search.php | 2 +- pages/csvimport.php | 2 +- 7 files changed, 198 insertions(+), 66 deletions(-) diff --git a/addons/userrights/userrightsmatrix.class.inc.php b/addons/userrights/userrightsmatrix.class.inc.php index aacdd2020..08b9eab17 100644 --- a/addons/userrights/userrightsmatrix.class.inc.php +++ b/addons/userrights/userrightsmatrix.class.inc.php @@ -348,7 +348,7 @@ class UserRightsMatrix extends UserRightsAddOnAPI return $oNullFilter; } - public function IsActionAllowed($iUserId, $sClass, $iActionCode, dbObjectSet $aInstances) + public function IsActionAllowed($iUserId, $sClass, $iActionCode, $oInstanceSet = null) { if (!array_key_exists($iActionCode, self::$m_aActionCodes)) { @@ -376,7 +376,7 @@ class UserRightsMatrix extends UserRightsAddOnAPI return $iRetCode; } - public function IsActionAllowedOnAttribute($iUserId, $sClass, $sAttCode, $iActionCode, dbObjectSet $aInstances) + public function IsActionAllowedOnAttribute($iUserId, $sClass, $sAttCode, $iActionCode, $oInstanceSet = null) { if (!array_key_exists($iActionCode, self::$m_aActionCodes)) { @@ -404,7 +404,7 @@ class UserRightsMatrix extends UserRightsAddOnAPI return $iRetCode; } - public function IsStimulusAllowed($iUserId, $sClass, $sStimulusCode, dbObjectSet $aInstances) + public function IsStimulusAllowed($iUserId, $sClass, $sStimulusCode, $oInstanceSet = null) { $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT UserRightsMatrixClassStimulusGrant WHERE class = '$sClass' AND stimulus = '$sStimulusCode' AND userid = '$iUserId'")); if ($oSet->Count() < 1) diff --git a/addons/userrights/userrightsnull.class.inc.php b/addons/userrights/userrightsnull.class.inc.php index 2d58eac41..981ef8ccf 100644 --- a/addons/userrights/userrightsnull.class.inc.php +++ b/addons/userrights/userrightsnull.class.inc.php @@ -59,17 +59,17 @@ class UserRightsNull extends UserRightsAddOnAPI return $oNullFilter; } - public function IsActionAllowed($iUserId, $sClass, $iActionCode, dbObjectSet $aInstances) + public function IsActionAllowed($iUserId, $sClass, $iActionCode, $oInstanceSet = null) { return UR_ALLOWED_YES; } - public function IsStimulusAllowed($iUserId, $sClass, $sStimulusCode, dbObjectSet $aInstances) + public function IsStimulusAllowed($iUserId, $sClass, $sStimulusCode, $oInstanceSet = null) { return UR_ALLOWED_YES; } - public function IsActionAllowedOnAttribute($iUserId, $sClass, $sAttCode, $iActionCode, dbObjectSet $aInstances) + public function IsActionAllowedOnAttribute($iUserId, $sClass, $sAttCode, $iActionCode, $oInstanceSet = null) { return UR_ALLOWED_YES; } diff --git a/addons/userrights/userrightsprofile.class.inc.php b/addons/userrights/userrightsprofile.class.inc.php index a0fe498c7..9edf1b153 100644 --- a/addons/userrights/userrightsprofile.class.inc.php +++ b/addons/userrights/userrightsprofile.class.inc.php @@ -66,6 +66,70 @@ class URP_Users extends UserRightsBaseClass MetaModel::Init_SetZListItems('advanced_search', array('login', 'userid')); // Criteria of the advanced search form } + function GetGrantAsHtml($sClass, $iAction) + { + if (UserRights::IsActionAllowed($sClass, $iAction, null, $this->GetKey())) + { + return 'yes'; + } + else + { + return 'no'; + } + } + + function DoShowGrantSumary($oPage) + { + $iUserId = $this->GetKey(); + if (UserRights::IsAdministrator($iUserId)) + { + // Looks dirty, but ok that's THE ONE + $oPage->p('Has Read/Write access to any object in the database.'); + return; + } + + $aDisplayData = array(); + foreach (MetaModel::GetClasses('bizmodel') as $sClass) + { + $aStimuli = array(); + foreach (MetaModel::EnumStimuli($sClass) as $sStimulusCode => $oStimulus) + { + if (UserRights::IsStimulusAllowed($sClass, $sStimulusCode, null, $iUserId)) + { + $aStimuli[] = ''.htmlentities($oStimulus->Get('label')).''; + } + } + $sStimuli = implode(', ', $aStimuli); + + $aDisplayData[] = array( + 'class' => MetaModel::GetName($sClass), + 'read' => $this->GetGrantAsHtml($sClass, UR_ACTION_READ), + 'bulkread' => $this->GetGrantAsHtml($sClass, UR_ACTION_BULK_READ), + 'write' => $this->GetGrantAsHtml($sClass, UR_ACTION_MODIFY), + 'bulkwrite' => $this->GetGrantAsHtml($sClass, UR_ACTION_BULK_MODIFY), + 'stimuli' => $sStimuli, + ); + } + + $aDisplayConfig = array(); + $aDisplayConfig['class'] = array('label' => 'Class', 'description' => ''); + $aDisplayConfig['read'] = array('label' => 'Read', 'description' => ''); + $aDisplayConfig['bulkread'] = array('label' => 'Bulk read', 'description' => 'List objects or export massively'); + $aDisplayConfig['write'] = array('label' => 'Write', 'description' => 'Create and edit (modify)'); + $aDisplayConfig['bulkwrite'] = array('label' => 'Bulk write', 'description' => 'Massively create/edit (CSV import)'); + $aDisplayConfig['stimuli'] = array('label' => 'Stimuli', 'description' => 'Allowed (compound) actions'); + $oPage->table($aDisplayConfig, $aDisplayData); + } + + function DisplayBareRelations(web_page $oPage) + { + parent::DisplayBareRelations($oPage); + + $oPage->SetCurrentTabContainer('Related Objects'); + + $oPage->SetCurrentTab('Grants matrix'); + $this->DoShowGrantSumary($oPage); + } } @@ -881,18 +945,26 @@ exit; return $oGrantRecord; } - protected function GetObjectActionGrant($oUser, $sClass, $iActionCode, $oObject) + protected function GetObjectActionGrant($oUser, $sClass, $iActionCode, /*DBObject*/ $oObject = null) { + if(is_null($oObject)) + { + $iObjectRef = -999; + } + else + { + $iObjectRef = $oObject->GetKey(); + } // load and cache permissions for the current user on the given object // - $aTest = @$this->m_aObjectActionGrants[$oUser->GetKey()][$sClass][$oObject->GetKey][$iActionCode]; + $aTest = @$this->m_aObjectActionGrants[$oUser->GetKey()][$sClass][$iObjectRef][$iActionCode]; if (is_array($aTest)) return $aTest; $sAction = self::$m_aActionCodes[$iActionCode]; $iInstancePermission = UR_ALLOWED_NO; $aAttributes = array(); - foreach($this->GetMatchingProfiles($oUser, $oObject) as $iProfile) + foreach($this->GetMatchingProfiles($oUser, $sClass, $oObject) as $iProfile) { $oGrantRecord = $this->GetClassActionGrant($iProfile, $sClass, $sAction); if (is_null($oGrantRecord)) @@ -924,18 +996,24 @@ exit; 'permission' => $iInstancePermission, 'attributes' => $aAttributes, ); - $this->m_aObjectActionGrants[$oUser->GetKey()][$sClass][$oObject->GetKey()][$iActionCode] = $aRes; + $this->m_aObjectActionGrants[$oUser->GetKey()][$sClass][$iObjectRef][$iActionCode] = $aRes; return $aRes; } - public function IsActionAllowed($iUserId, $sClass, $iActionCode, dbObjectSet $oInstances) + public function IsActionAllowed($iUserId, $sClass, $iActionCode, $oInstanceSet = null) { if ($this->IsAdministrator($iUserId)) return true; $oUser = $this->m_aUsers[$iUserId]; - $oInstances->Rewind(); - while($oObject = $oInstances->Fetch()) + if (is_null($oInstanceSet)) + { + $aObjectPermissions = $this->GetObjectActionGrant($oUser, $sClass, $iActionCode); + return $aObjectPermissions['permission']; + } + + $oInstanceSet->Rewind(); + while($oObject = $oInstanceSet->Fetch()) { $aObjectPermissions = $this->GetObjectActionGrant($oUser, $sClass, $iActionCode, $oObject); @@ -953,7 +1031,7 @@ exit; $iGlobalPermission = $iInstancePermission; } } - $oInstances->Rewind(); + $oInstanceSet->Rewind(); if (isset($iGlobalPermission)) { @@ -965,14 +1043,28 @@ exit; } } - public function IsActionAllowedOnAttribute($iUserId, $sClass, $sAttCode, $iActionCode, dbObjectSet $oInstances) + public function IsActionAllowedOnAttribute($iUserId, $sClass, $sAttCode, $iActionCode, $oInstanceSet = null) { if ($this->IsAdministrator($iUserId)) return true; $oUser = $this->m_aUsers[$iUserId]; - $oInstances->Rewind(); - while($oObject = $oInstances->Fetch()) + if (is_null($oInstanceSet)) + { + $aObjectPermissions = $this->GetObjectActionGrant($oUser, $sClass, $iActionCode); + $aAttributes = $aObjectPermissions['attributes']; + if (in_array($sAttCode, $aAttributes)) + { + return $aObjectPermissions['permission']; + } + else + { + return UR_ALLOWED_NO; + } + } + + $oInstanceSet->Rewind(); + while($oObject = $oInstanceSet->Fetch()) { $aObjectPermissions = $this->GetObjectActionGrant($oUser, $sClass, $iActionCode, $oObject); @@ -998,7 +1090,7 @@ exit; $iGlobalPermission = $iInstancePermission; } } - $oInstances->Rewind(); + $oInstanceSet->Rewind(); if (isset($iGlobalPermission)) { @@ -1034,7 +1126,7 @@ exit; return $oGrantRecord; } - public function IsStimulusAllowed($iUserId, $sClass, $sStimulusCode, dbObjectSet $oInstances) + public function IsStimulusAllowed($iUserId, $sClass, $sStimulusCode, $oInstanceSet = null) { if ($this->IsAdministrator($iUserId)) return true; @@ -1042,21 +1134,33 @@ exit; // Note: this code is VERY close to the code of IsActionAllowed() - $oInstances->Rewind(); - while($oObject = $oInstances->Fetch()) + if (is_null($oInstanceSet)) { $iInstancePermission = UR_ALLOWED_NO; - foreach($this->GetMatchingProfiles($oUser, $oObject) as $iProfile) + foreach($this->GetMatchingProfiles($oUser, $sClass) as $iProfile) { - // Get the permission for this profile/class/stimulus - $oSearch = DBObjectSearch::FromOQL("SELECT URP_StimulusGrant WHERE class = :class AND stimulus = :stimulus AND profileid = :profile AND permission = 'yes'"); - $oSet = new DBObjectSet($oSearch, array(), array('class'=>$sClass, 'stimulus'=>$sStimulusCode, 'profile'=>$iProfile)); - if ($oSet->Count() < 1) + $oGrantRecord = $this->GetClassStimulusGrant($iProfile, $sClass, $sStimulusCode); + if (!is_null($oGrantRecord)) { - return UR_ALLOWED_NO; + // no need to fetch the record, we've requested the records having permission = 'yes' + $iInstancePermission = UR_ALLOWED_YES; + } + } + return $iInstancePermission; + } + + $oInstanceSet->Rewind(); + while($oObject = $oInstanceSet->Fetch()) + { + $iInstancePermission = UR_ALLOWED_NO; + foreach($this->GetMatchingProfiles($oUser, $sClass, $oObject) as $iProfile) + { + $oGrantRecord = $this->GetClassStimulusGrant($iProfile, $sClass, $sStimulusCode); + if (is_null($oGrantRecord)) + { + // no need to fetch the record, we've requested the records having permission = 'yes' + $iInstancePermission = UR_ALLOWED_YES; } - // no need to fetch the record, we've requested the records having permission = 'yes' - $iInstancePermission = UR_ALLOWED_YES; } if (isset($iGlobalPermission)) { @@ -1070,7 +1174,7 @@ exit; $iGlobalPermission = $iInstancePermission; } } - $oInstances->Rewind(); + $oInstanceSet->Rewind(); if (isset($iGlobalPermission)) { @@ -1095,7 +1199,7 @@ exit; $aObjectProjection = $this->m_aClassProjs[$sClass][$iDimension]->ProjectObject($oObject); $aRes = array(); - if (array_key_exists($iUser, $this->m_aUserProfiles) > 0) + if (array_key_exists($iUser, $this->m_aUserProfiles)) { foreach ($this->m_aUserProfiles[$iUser] as $iProfile => $oProfile) { @@ -1128,40 +1232,65 @@ exit; protected $m_aMatchingProfiles = array(); // cache of the matching profiles for a given user/object - protected function GetMatchingProfiles($oUser, $oObject) + protected function GetMatchingProfiles($oUser, $sClass, /*DBObject*/ $oObject = null) { $iUser = $oUser->GetKey(); - $sClass = get_class($oObject); - $iObject = $oObject->GetKey(); + + if(is_null($oObject)) + { + $iObjectRef = -999; + } + else + { + $iObjectRef = $oObject->GetKey(); + } + // // List profiles for which the user projection overlaps the object projection in each and every dimension // Caches the result // - $aTest = @$this->m_aMatchingProfiles[$iUser][$sClass][$iObject]; + $aTest = @$this->m_aMatchingProfiles[$iUser][$sClass][$iObjectRef]; if (is_array($aTest)) { return $aTest; } - $aProfileRes = array(); - foreach ($this->m_aDimensions as $iDimension => $oDimension) + if (is_null($oObject)) { - foreach ($this->GetMatchingProfilesByDim($oUser, $oObject, $oDimension) as $iProfile) + if (array_key_exists($iUser, $this->m_aUserProfiles)) { - @$aProfileRes[$iProfile] += 1; + $aRes = array_keys($this->m_aUserProfiles[$iUser]); + } + else + { + // no profile has been defined for this user + $aRes = array(); + } + } + else + { + $aProfileRes = array(); + foreach ($this->m_aDimensions as $iDimension => $oDimension) + { + foreach ($this->GetMatchingProfilesByDim($oUser, $oObject, $oDimension) as $iProfile) + { + @$aProfileRes[$iProfile] += 1; + } + } + + $aRes = array(); + $iDimCount = count($this->m_aDimensions); + foreach ($aProfileRes as $iProfile => $iMatches) + { + if ($iMatches == $iDimCount) + { + $aRes[] = $iProfile; + } } } - $aRes = array(); - $iDimCount = count($this->m_aDimensions); - foreach ($aProfileRes as $iProfile => $iMatches) - { - if ($iMatches == $iDimCount) - { - $aRes[] = $iProfile; - } - } - $this->m_aMatchingProfiles[$iUser][$sClass][$iObject] = $aRes; + // store into the cache + $this->m_aMatchingProfiles[$iUser][$sClass][$iObjectRef] = $aRes; return $aRes; } } diff --git a/application/nicewebpage.class.inc.php b/application/nicewebpage.class.inc.php index bd29966ed..63cfd4cc8 100644 --- a/application/nicewebpage.class.inc.php +++ b/application/nicewebpage.class.inc.php @@ -30,15 +30,18 @@ class nice_web_page extends web_page } // By Rom, used by CSVImport and Advanced search - public function MakeClassesSelect($sName, $sDefaultValue, $iWidthPx) + public function MakeClassesSelect($sName, $sDefaultValue, $iWidthPx, $iActionCode = null) { // $aTopLevelClasses = array('bizService', 'bizContact', 'logInfra', 'bizDocument'); // These are classes wich root class is cmdbAbstractObject ! $this->add(""); } diff --git a/core/userrights.class.inc.php b/core/userrights.class.inc.php index e831e9363..d3071333b 100644 --- a/core/userrights.class.inc.php +++ b/core/userrights.class.inc.php @@ -52,9 +52,9 @@ abstract class UserRightsAddOnAPI abstract public function GetUserId($sLogin); // returns the id of the user or false abstract public function GetContactId($sLogin); // returns the id of the "business" user or false abstract public function GetFilter($sLogin, $sClass); // returns a filter object - abstract public function IsActionAllowed($iUserId, $sClass, $iActionCode, dbObjectSet $oInstances); - abstract public function IsStimulusAllowed($iUserId, $sClass, $sStimulusCode, dbObjectSet $oInstances); - abstract public function IsActionAllowedOnAttribute($iUserId, $sClass, $sAttCode, $iActionCode, dbObjectSet $oInstances); + abstract public function IsActionAllowed($iUserId, $sClass, $iActionCode, /*dbObjectSet*/ $oInstanceSet = null); + abstract public function IsStimulusAllowed($iUserId, $sClass, $sStimulusCode, /*dbObjectSet*/ $oInstanceSet = null); + abstract public function IsActionAllowedOnAttribute($iUserId, $sClass, $sAttCode, $iActionCode, /*dbObjectSet*/ $oInstanceSet = null); abstract public function IsAdministrator($iUserId); } @@ -211,48 +211,48 @@ class UserRights return self::$m_oAddOn->GetFilter(self::$m_iUserId, $sClass); } - public static function IsActionAllowed($sClass, $iActionCode, dbObjectSet $oInstances, $iUserId = null) + public static function IsActionAllowed($sClass, $iActionCode, /*dbObjectSet*/ $oInstanceSet = null, $iUserId = null) { if (!MetaModel::HasCategory($sClass, 'bizmodel')) return true; if (!self::CheckLogin()) return false; if (is_null($iUserId)) { - return self::$m_oAddOn->IsActionAllowed(self::$m_iUserId, $sClass, $iActionCode, $oInstances); + return self::$m_oAddOn->IsActionAllowed(self::$m_iUserId, $sClass, $iActionCode, $oInstanceSet); } else { - return self::$m_oAddOn->IsActionAllowed($iUserId, $sClass, $iActionCode, $oInstances); + return self::$m_oAddOn->IsActionAllowed($iUserId, $sClass, $iActionCode, $oInstanceSet); } } - public static function IsStimulusAllowed($sClass, $sStimulusCode, dbObjectSet $oInstances, $iUserId = null) + public static function IsStimulusAllowed($sClass, $sStimulusCode, /*dbObjectSet*/ $oInstanceSet = null, $iUserId = null) { if (!MetaModel::HasCategory($sClass, 'bizmodel')) return true; if (!self::CheckLogin()) return false; if (is_null($iUserId)) { - return self::$m_oAddOn->IsStimulusAllowed(self::$m_iUserId, $sClass, $sStimulusCode, $oInstances); + return self::$m_oAddOn->IsStimulusAllowed(self::$m_iUserId, $sClass, $sStimulusCode, $oInstanceSet); } else { - return self::$m_oAddOn->IsStimulusAllowed($iUserId, $sClass, $sStimulusCode, $oInstances); + return self::$m_oAddOn->IsStimulusAllowed($iUserId, $sClass, $sStimulusCode, $oInstanceSet); } } - public static function IsActionAllowedOnAttribute($sClass, $sAttCode, $iActionCode, dbObjectSet $oInstances, $iUserId = null) + public static function IsActionAllowedOnAttribute($sClass, $sAttCode, $iActionCode, /*dbObjectSet*/ $oInstanceSet = null, $iUserId = null) { if (!MetaModel::HasCategory($sClass, 'bizmodel')) return true; if (!self::CheckLogin()) return false; if (is_null($iUserId)) { - return self::$m_oAddOn->IsActionAllowedOnAttribute(self::$m_iUserId, $sClass, $sAttCode, $iActionCode, $oInstances); + return self::$m_oAddOn->IsActionAllowedOnAttribute(self::$m_iUserId, $sClass, $sAttCode, $iActionCode, $oInstanceSet); } else { - return self::$m_oAddOn->IsActionAllowedOnAttribute($iUserId, $sClass, $sAttCode, $iActionCode, $oInstances); + return self::$m_oAddOn->IsActionAllowedOnAttribute($iUserId, $sClass, $sAttCode, $iActionCode, $oInstanceSet); } } diff --git a/pages/advanced_search.php b/pages/advanced_search.php index c5b5fbfb8..fa29c2c88 100644 --- a/pages/advanced_search.php +++ b/pages/advanced_search.php @@ -31,7 +31,7 @@ function Page1_AskClass($oPage) $oPage->add("
        \n"); //$oPage->add(""); $oPage->p("Please select the type of object that you want to look for:"); - $oPage->MakeClassesSelect("class", "", 50); + $oPage->MakeClassesSelect("class", "", 50, UR_ACTION_READ); $oPage->add("\n"); $oPage->add("
        \n"); } diff --git a/pages/csvimport.php b/pages/csvimport.php index 256aa2957..e18aa4bda 100644 --- a/pages/csvimport.php +++ b/pages/csvimport.php @@ -339,7 +339,7 @@ function Do_Welcome($oPage, $sClass) $sCSVData = utils::ReadPostedParam('csvdata'); $oPage->add("
        "); - $oPage->MakeClassesSelect("class", $sClass, 50); + $oPage->MakeClassesSelect("class", $sClass, 50, UR_ACTION_MODIFY); $oPage->add("
        "); $oPage->add(""); $oPage->add("
        "); From 7c00f42d5c5e3ca29ad464c891c664d4a4fc9626 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Wed, 16 Sep 2009 16:07:28 +0000 Subject: [PATCH 095/227] - Fix for Internet Explorer: Arrays must be properly closed! SVN:trunk[170] --- js/wizardhelper.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/js/wizardhelper.js b/js/wizardhelper.js index 3a3d870d8..c42beded4 100644 --- a/js/wizardhelper.js +++ b/js/wizardhelper.js @@ -1,5 +1,4 @@ // Wizard Helper JavaScript class to communicate with the WizardHelper PHP class - function WizardHelper(sClass) { this.m_oData = { 'm_sClass' : '', @@ -9,7 +8,7 @@ function WizardHelper(sClass) 'm_aAllowedValuesRequested': [], 'm_aDefaultValue': [], 'm_aAllowedValues': [], - 'm_iFieldsCount' : 0, + 'm_iFieldsCount' : 0 }; this.m_oData.m_sClass = sClass; From d3b76d3b238ab10408bdd0a44fba3208b9f70639 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Wed, 16 Sep 2009 16:08:52 +0000 Subject: [PATCH 096/227] - Allowed values is now implemented, so I removed the comment ! SVN:trunk[171] --- application/cmdbabstract.class.inc.php | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index 3f9a3cd00..ad852fc79 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -523,8 +523,7 @@ abstract class cmdbAbstractObject extends CMDBObject } } } - // #@# todo - add context information, otherwise any value will be authorized for external keys - $aAllowedValues = MetaModel::GetAllowedValues_flt($sClassName, $sFilterCode, array()); + $aAllowedValues = MetaModel::GetAllowedValues_flt($sClassName, $sFilterCode, $aExtraParams); if ($aAllowedValues != null) { //Enum field or external key, display a combo From 127e77858ebace9ed1c8d4803a1a69c54389427f Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Wed, 16 Sep 2009 16:09:46 +0000 Subject: [PATCH 097/227] Correct "script" tag... just in case... SVN:trunk[172] --- pages/UI.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/UI.php b/pages/UI.php index fc2700b71..b1324936c 100644 --- a/pages/UI.php +++ b/pages/UI.php @@ -360,7 +360,7 @@ switch($operation) $id = ''; $oObj = MetaModel::NewObject($sClass); } - $oP->add("\n"; $sHTMLValue .= $this->GetObjectPickerDialog($oPage, $sTargetClass, 'oLinkWidget'.$this->m_iInputId.'.OnOk'); $sHTMLValue .= $this->GetLinkObjectDialog($oPage, $this->m_iInputId); - $sHTMLValue .= "m_iInputId}\" size=\"35\" name=\"\" value=\"\" title=\"Type the first 3 characters\"/>"; + $sHTMLValue .= "m_iInputId}\" size=\"35\" value=\"\" title=\"Type the first 3 characters\"/>"; $sHTMLValue .= "m_iInputId}\" value=\" Add... \" class=\"action\" onClick=\"oLinkWidget{$this->m_iInputId}.AddObject();\"/>"; $sHTMLValue .= " m_iInputId', '$sExtKeyToRemote');\"/>"; // another hidden input to store & pass the object's Id @@ -275,7 +275,7 @@ EOF; $sHTML = "
        \n"; $sHTML .= "
        \n"; $sHTML .= "

        ".MetaModel::GetName($sLinkedClass)." attributes

        \n"; - $sHTML .= "\n"; + $sHTML .= "\n"; $index = 0; $aAttrsMap = array(); $aDetails = array(); diff --git a/application/uilinkswizard.class.inc.php b/application/uilinkswizard.class.inc.php index 7fdc3a508..db57e2075 100644 --- a/application/uilinkswizard.class.inc.php +++ b/application/uilinkswizard.class.inc.php @@ -117,6 +117,8 @@ class UILinksWizard $('#ModalDlg').html(data); dlgWidth = $(document).width() - 100; $('#ModalDlg').css('width', dlgWidth); + $('#ModalDlg').css('left', 50); + $('#ModalDlg').css('top', 50); $('#ModalDlg').jqmShow(); }, 'html' @@ -355,7 +357,7 @@ class UILinksWizard $oP->add("  "); $oP->Add("
        \n"); $oP->Add("\n"); - $oP->add_ready_script("$('div#SearchFormToAdd form').attr('onSubmit', 'var the_form = this; return SearchObjectsToAdd(the_form.id);');"); + $oP->add_ready_script("$('div#SearchFormToAdd form').bind('submit', function() {var the_form = this; SearchObjectsToAdd(the_form.id); return false;});"); } public function SearchObjectsToAdd(web_page $oP, UserContext $oContext) diff --git a/application/uiwizard.class.inc.php b/application/uiwizard.class.inc.php index f49228dbc..307bdb50a 100644 --- a/application/uiwizard.class.inc.php +++ b/application/uiwizard.class.inc.php @@ -80,12 +80,12 @@ class UIWizard $sDisabled = $bFinishEnabled ? '' : 'disabled'; $nbSteps = count($this->m_aWizardSteps['mandatory']) + count($this->m_aWizardSteps['optional']); $this->m_oPage->add("
        - - - + + +
        \n"); $this->m_oPage->add(" -\n"); $this->m_oPage->add("
        \n"); $this->m_oPage->add("
        \n"); - $this->m_oPage->add("m_sClass)."\">\n"); + $this->m_oPage->add("m_sClass)."\" />\n"); $this->m_oPage->add("\n"); $this->m_oPage->add("
        \n"); } diff --git a/core/cmdbsource.class.inc.php b/core/cmdbsource.class.inc.php index cdf31ab80..3474033fa 100644 --- a/core/cmdbsource.class.inc.php +++ b/core/cmdbsource.class.inc.php @@ -41,7 +41,7 @@ class CMDBSource self::$m_sDBName = $sSource; if (!self::$m_resDBLink = @mysql_pconnect($sServer, $sUser, $sPwd)) { - throw new MySQLException('Could not connect to the DB server', array('host'=>$sServer, 'user'=>$sUser)); + throw new MySQLException('Could not connect to the DB server', array('host'=>$sServer)); } if (!empty($sSource)) { diff --git a/css/jqModal.css b/css/jqModal.css index 288981f2a..41e059343 100644 --- a/css/jqModal.css +++ b/css/jqModal.css @@ -2,32 +2,30 @@ Brice Burgess */ /* The Window's CSS z-index value is respected (takes priority). If none is supplied, - the Window's z-index value will be set to 3000 by default (in jqModal.js). You - can change this value by either; - a) supplying one via CSS - b) passing the "zIndex" parameter. E.g. (window).jqm({zIndex: 500}); */ + the Window's z-index value will be set to 3000 by default (via jqModal.js). */ .jqmWindow { display: none; position: fixed; - top: 25px; - left: 25px; + no.top: 17%; + no.left: 50%; - nomargin-left: 25px; - nomargin-right: 25px; - nowidth: 800px; + no.margin-left: -300px; + no.width: 700px; - background-color: #FFF; + background-color: #EEE; color: #333; border: 1px solid black; padding: 12px; + + z-index:9999; } -.jqmOverlay { background-color: #333; } +.jqmOverlay { background-color: #000; } /* Background iframe styling for IE6. Prevents ActiveX bleed-through ("); } } + + protected function get_base_tag() + { + $sTag = ''; + if (($this->a_base['href'] != '') || ($this->a_base['target'] != '')) + { + $sTag = 'a_base['href'] != '')) + { + $sTag .= "href =\"{$this->a_base['href']}\" "; + } + if (($this->a_base['target'] != '')) + { + $sTag .= "target =\"{$this->a_base['target']}\" "; + } + $sTag .= " />\n"; + } + return $sTag; + } } ?> diff --git a/webservices/export.php b/webservices/export.php index b7c7ad2e7..912ddebfa 100644 --- a/webservices/export.php +++ b/webservices/export.php @@ -32,6 +32,21 @@ if (!empty($sExpression)) { case 'html': $oP = new nice_web_page("iTop - Export"); + // The HTML output is made for pages located in the /pages/ folder + // since this page is in a different folder, let's adjust the HTML 'base' attribute + // to make the relative hyperlinks in the page work + $sServerName = $_SERVER['SERVER_NAME']; + $sProtocol = isset($_SERVER['HTTPS']) ? 'https' : 'http'; + if ($sProtocol == 'http') + { + $sPort = ($_SERVER['SERVER_PORT'] == 80) ? '' : ':'.$_SERVER['SERVER_PORT']; + } + else + { + $sPort = ($_SERVER['SERVER_PORT'] == 443) ? '' : ':'.$_SERVER['SERVER_PORT']; + } + $sUrl = "$sProtocol://{$sServerName}{$sPort}/pages/"; + $oP->set_base($sUrl); cmdbAbstractObject::DisplaySet($oP, $oSet, array('menu' => false)); break; From 89396151c51afb7c0a3ab58d82c4b5271acc88bf Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Mon, 21 Dec 2009 15:52:23 +0000 Subject: [PATCH 123/227] Fixed bug (Trac #53), I don't know exactly what I did (!) but I've copied what's done in __DescribeHTML and this makes the full text search work for organizations ! Note that __DescribeHTML actually modifies the objects it is applied to (via a referenced passed to the Describe method) SVN:trunk[200] --- core/dbobjectsearch.class.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/dbobjectsearch.class.php b/core/dbobjectsearch.class.php index 03020556f..ae1ffbdff 100644 --- a/core/dbobjectsearch.class.php +++ b/core/dbobjectsearch.class.php @@ -275,6 +275,8 @@ class DBObjectSearch public function AddCondition_FullText($sFullText) { $this->m_aFullText[] = $sFullText; + $index = count($this->m_aParams) + 1; + $this->m_aParams['param'.$index] = 1; } protected function AddToNameSpace(&$aClassAliases, &$aAliasTranslation) From 97761de870ff431b5d0e29abffbfedcb693c5926 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Mon, 21 Dec 2009 19:16:27 +0000 Subject: [PATCH 124/227] - completed the implementaiton o the 'is dirty' flag to prevent reloading an object being modified. - added a protection against reading an uninitialized field that triggered a PHP 'notice' error. SVN:trunk[201] --- core/dbobject.class.php | 12 +++++++++--- 1 file changed, 9 insertions(+), 3 deletions(-) diff --git a/core/dbobject.class.php b/core/dbobject.class.php index 431132097..00568551c 100644 --- a/core/dbobject.class.php +++ b/core/dbobject.class.php @@ -302,6 +302,7 @@ abstract class DBObject } } $this->m_aCurrValues[$sAttCode] = $oAttDef->MakeRealValue($value); + $this->RegisterAsDirty(); // Make sure we do not reload it anymore... before saving it } public function Get($sAttCode) @@ -310,13 +311,13 @@ abstract class DBObject { throw new CoreException("Unknown attribute code '$sAttCode' for the class ".get_class($this)); } - if ($this->m_bIsInDB && !$this->m_aLoadedAtt[$sAttCode]) + if ($this->m_bIsInDB && !$this->m_aLoadedAtt[$sAttCode] && !$this->m_bDirty) { // #@# non-scalar attributes.... handle that differentely $this->Reload(); } $this->ComputeFields(); - return $this->m_aCurrValues[$sAttCode]; + return isset($this->m_aCurrValues[$sAttCode]) ? $this->m_aCurrValues[$sAttCode] : ''; } public function GetOriginal($sAttCode) @@ -696,6 +697,7 @@ abstract class DBObject public function DBInsert() { $this->DBInsertNoReload(); + $this->m_bDirty = false; $this->Reload(); return $this->m_iKey; } @@ -741,9 +743,13 @@ abstract class DBObject } $this->DBWriteLinks(); + $this->m_bDirty = false; // Reload to get the external attributes - if ($bHasANewExternalKeyValue) $this->Reload(); + if ($bHasANewExternalKeyValue) + { + $this->Reload(); + } return $this->m_iKey; } From 8bb9176d3613511840d8927bfdc5f2935dee9af1 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Mon, 21 Dec 2009 19:53:13 +0000 Subject: [PATCH 125/227] - fixed the display of the history log of the 'user' class (Trac #48), and more generally the history log of all classes not derived from logRealObject. SVN:trunk[202] --- application/cmdbabstract.class.inc.php | 1 + 1 file changed, 1 insertion(+) diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index a707dff3e..85334ffb0 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -133,6 +133,7 @@ abstract class cmdbAbstractObject extends CMDBObject // history block (with toggle) $oHistoryFilter = new DBObjectSearch('CMDBChangeOpSetAttribute'); $oHistoryFilter->AddCondition('objkey', $this->GetKey()); + $oHistoryFilter->AddCondition('objclass', get_class($this)); $oBlock = new HistoryBlock($oHistoryFilter, 'toggle', false); $oBlock->Display($oPage, -1); From d3d617fabe53d2480f114c25d6667551b857cad5 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Mon, 21 Dec 2009 20:11:29 +0000 Subject: [PATCH 126/227] - fixed bug #52 (display of lnkInfraError objects) by adding a protection in GetDisplayName against objects that actually do not have any name. SVN:trunk[203] --- application/cmdbabstract.class.inc.php | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index 85334ffb0..18d2f4a93 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -189,7 +189,12 @@ abstract class cmdbAbstractObject extends CMDBObject function GetDisplayName() { - return $this->GetAsHTML(MetaModel::GetNameAttributeCode(get_class($this))); + $sDisplayName = ''; + if (MetaModel::GetNameAttributeCode(get_class($this)) != '') + { + $sDisplayName = $this->GetAsHTML(MetaModel::GetNameAttributeCode(get_class($this))); + } + return $sDisplayName; } function GetBareDetails(web_page $oPage) From 37c5880ced0fab20e101acca182d0247c944c0e0 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Sat, 26 Dec 2009 13:06:28 +0000 Subject: [PATCH 127/227] #53 Global search always returning all organization, reviewed Denis's fix SVN:trunk[204] --- core/dbobjectsearch.class.php | 10 +++++++--- 1 file changed, 7 insertions(+), 3 deletions(-) diff --git a/core/dbobjectsearch.class.php b/core/dbobjectsearch.class.php index ae1ffbdff..8cf4ccf15 100644 --- a/core/dbobjectsearch.class.php +++ b/core/dbobjectsearch.class.php @@ -275,8 +275,6 @@ class DBObjectSearch public function AddCondition_FullText($sFullText) { $this->m_aFullText[] = $sFullText; - $index = count($this->m_aParams) + 1; - $this->m_aParams['param'.$index] = 1; } protected function AddToNameSpace(&$aClassAliases, &$aAliasTranslation) @@ -466,7 +464,7 @@ class DBObjectSearch public function RenderCondition() { - return $this->m_oSearchCondition->Render($this->m_aParams, true); + return $this->m_oSearchCondition->Render($this->m_aParams, false); } public function serialize() @@ -634,6 +632,12 @@ class DBObjectSearch $sRes = "SELECT ".$this->GetClass().' AS '.$this->GetClassAlias(); $sRes .= $this->ToOQL_Joins(); $sRes .= " WHERE ".$this->m_oSearchCondition->Render($aParams, $bRetrofitParams); + + // Temporary: add more info about other conditions, necessary to avoid strange behaviors with the cache + foreach($this->m_aFullText as $sFullText) + { + $sRes .= " AND MATCHES '$sFullText'"; + } return $sRes; } From ae1e7e224a07b5621f2fe01a306b6dc985f20456 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Sat, 26 Dec 2009 13:18:56 +0000 Subject: [PATCH 128/227] Reviewed and changed a bug fix (No TRAC entry) on the display of external fields when the external key is null. The actual fix is to consider that null is a value. Isset() should be used with care because one could not differentiate a missing value (in an array or a value that is null) SVN:trunk[205] --- core/dbobject.class.php | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/core/dbobject.class.php b/core/dbobject.class.php index 00568551c..45b02c0c1 100644 --- a/core/dbobject.class.php +++ b/core/dbobject.class.php @@ -209,7 +209,9 @@ abstract class DBObject // Note: we assume that, for a given attribute, if it can be loaded, // then one column will be found with an empty suffix, the others have a suffix - if (isset($aRow[$sAttCode])) + // Take care: the function isset will return false in case the value is null, + // which is something that could happen on open joins + if (array_key_exists($sAttCode, $aRow)) { $value = $oAttDef->FromSQLToValue($aRow, $sAttCode); @@ -317,7 +319,7 @@ abstract class DBObject $this->Reload(); } $this->ComputeFields(); - return isset($this->m_aCurrValues[$sAttCode]) ? $this->m_aCurrValues[$sAttCode] : ''; + return $this->m_aCurrValues[$sAttCode]; } public function GetOriginal($sAttCode) From 193aef8a63044d3321a148ea17650217891b6b87 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Sat, 26 Dec 2009 13:30:16 +0000 Subject: [PATCH 129/227] Trac #51 - Implemented external key feed by the mean of reconciliation on external fields SVN:trunk[206] --- core/bulkchange.class.inc.php | 4 ++-- webservices/import.php | 35 ++++++++++++++++++++++++++++++----- 2 files changed, 32 insertions(+), 7 deletions(-) diff --git a/core/bulkchange.class.inc.php b/core/bulkchange.class.inc.php index 995bce92b..f2641afeb 100644 --- a/core/bulkchange.class.inc.php +++ b/core/bulkchange.class.inc.php @@ -229,7 +229,8 @@ class BulkChange protected $m_sClass; protected $m_aData; // Note: hereafter, iCol maybe actually be any acceptable key (string) // #@# todo: rename the variables to sColIndex - protected $m_aAttList; // attcode => iCol protected $m_aReconcilKeys;// iCol => attcode (attcode = 'id' for the pkey) + protected $m_aAttList; // attcode => iCol + protected $m_aReconcilKeys;// iCol => attcode (attcode = 'id' for the pkey) protected $m_aExtKeys; // aExtKeys[sExtKeyAttCode][sExtReconcKeyAttCode] = iCol; public function __construct($sClass, $aData, $aAttList, $aReconcilKeys, $aExtKeys) @@ -268,7 +269,6 @@ class BulkChange foreach ($aKeyConfig as $sForeignAttCode => $iCol) { // The foreign attribute is one of our reconciliation key - $sFieldId = MakeExtFieldSelectValue($sAttCode, $sForeignAttCode); $oReconFilter->AddCondition($sForeignAttCode, $aRowData[$iCol], '='); $aResults["col$iCol"] = new CellChangeSpec_Void($aRowData[$iCol]); } diff --git a/webservices/import.php b/webservices/import.php index 59b9a2cc6..9d91bc016 100644 --- a/webservices/import.php +++ b/webservices/import.php @@ -15,7 +15,7 @@ // // Known limitations // - output still in html, because the errors are not displayed in xml -// - could not set the external keys by the mean of a reconciliation (the exact key must be given) +// - only external fields attributes could be used as reconciliation keys for external keys // - reconciliation is made on the first column // - no option to force 'always create' or 'never create' // @@ -35,6 +35,11 @@ require_once('../application/xmlpage.class.inc.php'); require_once('../application/startup.inc.php'); require_once('../application/loginwebpage.class.inc.php'); + +class WebServiceException extends Exception +{ +} + login_web_page::DoLogin(); // Check user rights and prompt if needed $oContext = new UserContext(); @@ -46,6 +51,7 @@ $oAppContext = new ApplicationContext(); //$oP = new XMLPage("iTop - Bulk import"); $oP = new web_page("iTop - Bulk import"); +$oP->add('This is a prototype, I repeat: PRO-TO-TYPE, therefore it suffers bugs and limitations, documented in the code. Next step: specify...'); try { $sClass = utils::ReadParam('class', ''); @@ -59,11 +65,30 @@ try // Limitation: as the attribute list is in the first line, we can not match external key by an third-party attribute $sRawFieldList = $oCSVParser->ListFields(); $aAttList = array(); - foreach($sRawFieldList as $iField => $sFieldName) - { - $aAttList[$sFieldName] = $iField; - } $aExtKeys = array(); + foreach($sRawFieldList as $iFieldId => $sFieldName) + { + if (!MetaModel::IsValidAttCode($sClass, $sFieldName)) + { + throw new WebServiceException("Unknown attribute '$sFieldName' (class: '$sClass')"); + } + $oAtt = MetaModel::GetAttributeDef($sClass, $sFieldName); + if ($oAtt->IsExternalKey()) + { + $aExtKeys[$sFieldName]['id'] = $iFieldId; + $aAttList[$sFieldName] = $iFieldId; + } + elseif ($oAtt->IsExternalField()) + { + $sExtKeyAttCode = $oAtt->GetKeyAttCode(); + $sRemoteAttCode = $oAtt->GetExtAttCode(); + $aExtKeys[$sExtKeyAttCode][$sRemoteAttCode] = $iFieldId; + } + else + { + $aAttList[$sFieldName] = $iFieldId; + } + } // Limitation: the reconciliation key is the first attribute $aReconcilKeys = array($sRawFieldList[0]); From 18f209a2f74bfa4a333cb9a86fb5f90a93a2dc5a Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Sat, 26 Dec 2009 13:36:59 +0000 Subject: [PATCH 130/227] Trac #50 - Interactive bulk load (CSV) was not doing well when using several reconciliation keys SVN:trunk[207] --- business/itop.business.class.inc.php | 48 ++++++++++++++-------------- pages/csvimport.php | 35 +++++++++++++------- 2 files changed, 47 insertions(+), 36 deletions(-) diff --git a/business/itop.business.class.inc.php b/business/itop.business.class.inc.php index caed4161b..a863167f4 100644 --- a/business/itop.business.class.inc.php +++ b/business/itop.business.class.inc.php @@ -191,7 +191,7 @@ class bizContact extends logRealObject "key_label" => "id", "name_attcode" => "name", "state_attcode" => "", - "reconc_keys" => array("org_name", "name"), // inherited attributes + "reconc_keys" => array("org_id", "name"), // inherited attributes "db_table" => "contacts", "db_key_field" => "id", "db_finalclass_field" => "", @@ -249,8 +249,8 @@ class bizPerson extends bizContact "key_label" => "id", "name_attcode" => "name", "state_attcode" => "", - "reconc_keys" => array("org_name", "first_name", "name"), // comment en définir plusieurs - // "reconc_keys" => array("org_name", "employee_number"), + "reconc_keys" => array("org_id", "first_name", "name"), // comment en définir plusieurs + // "reconc_keys" => array("org_id", "employee_number"), "db_table" => "persons", // Can it use the same physical DB table as any contact ? "db_key_field" => "id", "db_finalclass_field" => "", @@ -260,7 +260,7 @@ class bizPerson extends bizContact MetaModel::Init_InheritAttributes(); MetaModel::Init_AddAttribute(new AttributeString("first_name", array("label"=>"First Name", "description"=>"First name", "allowed_values"=>null, "sql"=>"first_name", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("employee_number", array("label"=>"Employee Number", "description"=>"employee number", "allowed_values"=>null, "sql"=>"employee_number", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeEnum("status", array("label"=>"Status", "description"=>"Lifecycle status", "allowed_values"=>$oAllowedEmployeeStatuses, "sql"=>"status", "default_value"=>"implementation", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("status", array("label"=>"Status", "description"=>"Lifecycle status", "allowed_values"=>$oAllowedEmployeeStatuses, "sql"=>"status", "default_value"=>"available", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_InheritFilters(); MetaModel::Init_AddFilterFromAttribute("first_name"); @@ -303,7 +303,7 @@ class bizTeam extends bizContact "key_label" => "id", "name_attcode" => "name", "state_attcode" => "", - "reconc_keys" => array("org_name", "name"), // inherited attributes + "reconc_keys" => array("org_id", "name"), // inherited attributes "db_table" => "teams", "db_key_field" => "id", "db_finalclass_field" => "", @@ -340,7 +340,7 @@ class lnkContactTeam extends cmdbAbstractObject "key_label" => "link_id", "name_attcode" => "role", "state_attcode" => "", - "reconc_keys" => array("contact_name", "team_name"), + "reconc_keys" => array("contact_id", "team_name"), "db_table" => "teams_links", "db_key_field" => "link_id", "db_finalclass_field" => "", @@ -387,7 +387,7 @@ class bizDocument extends logRealObject "key_label" => "id", "name_attcode" => "name", "state_attcode" => "", - "reconc_keys" => array("org_name", "name"), // inherited attributes + "reconc_keys" => array("org_id", "name"), // inherited attributes "db_table" => "documents", "db_key_field" => "id", "db_finalclass_field" => "", @@ -434,7 +434,7 @@ class bizDocVersion extends cmdbAbstractObject "key_label" => "id", "name_attcode" => "version_number", "state_attcode" => "", - "reconc_keys" => array("docname", "version_number"), + "reconc_keys" => array("document", "version_number"), "db_table" => "document_versions", "db_key_field" => "id", "db_finalclass_field" => "", @@ -482,7 +482,7 @@ class lnkDocumentRealObject extends cmdbAbstractObject "key_label" => "link_id", "name_attcode" => "link_type", "state_attcode" => "", - "reconc_keys" => array("doc_name", "object_name"), + "reconc_keys" => array("doc_id", "object_name"), "db_table" => "documents_links", "db_key_field" => "link_id", "db_finalclass_field" => "", @@ -528,7 +528,7 @@ class lnkContactRealObject extends cmdbAbstractObject "key_label" => "link_id", "name_attcode" => "role", "state_attcode" => "", - "reconc_keys" => array("contact_name", "object_name"), + "reconc_keys" => array("contact_id", "object_name"), "db_table" => "contacts_links", "db_key_field" => "link_id", "db_finalclass_field" => "", @@ -577,7 +577,7 @@ class logInfra extends logRealObject "key_label" => "id", "name_attcode" => "name", "state_attcode" => "", - "reconc_keys" => array("org_name", "name"), // inherited attributes + "reconc_keys" => array("org_id", "name"), // inherited attributes "db_table" => "infra", "db_key_field" => "id", "db_finalclass_field" => "", @@ -610,7 +610,7 @@ class lnkContactInfra extends cmdbAbstractObject "key_label" => "link_id", "name_attcode" => "role", "state_attcode" => "", - "reconc_keys" => array("contact_name", "object_name"), + "reconc_keys" => array("contact_id", "infra_id"), "db_table" => "contacts_infra_links", "db_key_field" => "link_id", "db_finalclass_field" => "", @@ -657,7 +657,7 @@ class bizLocation extends logInfra "key_label" => "id", "name_attcode" => "name", "state_attcode" => "", - "reconc_keys" => array("org_name", "name"), // inherited attributes + "reconc_keys" => array("org_id", "name"), // inherited attributes "db_table" => "location", "db_key_field" => "id", "db_finalclass_field" => "", @@ -789,7 +789,7 @@ class bizCircuit extends logInfra "key_label" => "id", "name_attcode" => "name", "state_attcode" => "", - "reconc_keys" => array("org_name", "carrier_name", "carrier_ref", "name"), // inherited attributes + "reconc_keys" => array("org_id", "provider_id", "carrier_ref", "name"), // inherited attributes "db_table" => "circuits", "db_key_field" => "id", "db_finalclass_field" => "", @@ -872,7 +872,7 @@ class bizInterface extends logInfra "key_label" => "id", "name_attcode" => "name", "state_attcode" => "", - "reconc_keys" => array("org_name", "device_name", "name"), + "reconc_keys" => array("org_id", "device_id", "name"), "db_table" => "interfaces", "db_key_field" => "id", "db_finalclass_field" => "", @@ -1018,7 +1018,7 @@ class bizSubnet extends logInfra "key_label" => "id", "name_attcode" => "name", "state_attcode" => "", - "reconc_keys" => array("org_name", "name"), // inherited attributes + "reconc_keys" => array("org_id", "name"), // inherited attributes "db_table" => "subnets", "db_key_field" => "id", "db_finalclass_field" => "", @@ -1101,7 +1101,7 @@ class bizDevice extends logInfra "key_label" => "id", "name_attcode" => "name", "state_attcode" => "", - "reconc_keys" => array("org_name", "name"), // inherited attributes + "reconc_keys" => array("org_id", "name"), // inherited attributes "db_table" => "devices", "db_key_field" => "id", "db_finalclass_field" => "", @@ -1174,7 +1174,7 @@ class bizPC extends bizDevice "key_label" => "id", "name_attcode" => "name", "state_attcode" => "", - "reconc_keys" => array("org_name", "name"), // inherited attributes + "reconc_keys" => array("org_id", "name"), // inherited attributes "db_table" => "pcs", "db_key_field" => "id", "db_finalclass_field" => "", @@ -1268,7 +1268,7 @@ class bizServer extends bizDevice "key_label" => "id", "name_attcode" => "name", "state_attcode" => "status", - "reconc_keys" => array("org_name", "name"), // inherited attributes + "reconc_keys" => array("org_id", "name"), // inherited attributes "db_table" => "servers", "db_key_field" => "id", "db_finalclass_field" => "", @@ -1276,7 +1276,7 @@ class bizServer extends bizDevice ); MetaModel::Init_Params($aParams); MetaModel::Init_InheritAttributes(); - MetaModel::Init_AddAttribute(new AttributeEnum("status", array("label"=>"Status", "description"=>"Status of the server", "allowed_values"=>new ValueSetEnum("InStore,Shipped,Plugged,ProductionCandidate,InProduction,Being Deconfigured,Obsolete"), "sql"=>"status", "default_value"=>"InStore", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("status", array("label"=>"Status", "description"=>"Status of the server", "allowed_values"=>new ValueSetEnum("InStore,Shipped,Plugged,ProductionCandidate,InProduction,BeingDeconfigured,Obsolete"), "sql"=>"status", "default_value"=>"InStore", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("memory_size", array("label"=>"Memory Size", "description"=>"Size of the memory", "allowed_values"=>null, "sql"=>"memory_size", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("cpu", array("label"=>"CPU type", "description"=>"CPU type", "allowed_values"=>null, "sql"=>"cpu_type", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("number_of_cpus", array("label"=>"Number of CPUs", "description"=>"Number of CPUs", "allowed_values"=>null, "sql"=>"number_of_cpus", "default_value"=>"1", "is_null_allowed"=>true, "depends_on"=>array()))); @@ -1442,7 +1442,7 @@ class bizNetworkDevice extends bizDevice "key_label" => "id", "name_attcode" => "name", "state_attcode" => "", - "reconc_keys" => array("org_name", "name"), // inherited attributes + "reconc_keys" => array("org_id", "name"), // inherited attributes "db_table" => "network_devices", "db_key_field" => "id", "db_finalclass_field" => "", @@ -1506,7 +1506,7 @@ class bizInfraGroup extends logInfra "key_label" => "id", "name_attcode" => "name", "state_attcode" => "", - "reconc_keys" => array("org_name", "name"), // inherited attributes + "reconc_keys" => array("org_id", "name"), // inherited attributes "db_table" => "infra_group", "db_key_field" => "id", "db_finalclass_field" => "", @@ -1589,7 +1589,7 @@ class bizApplication extends logInfra "key_label" => "id", "name_attcode" => "name", "state_attcode" => "", - "reconc_keys" => array("device_name", "name"), // inherited attributes + "reconc_keys" => array("device_id", "name"), // inherited attributes "db_table" => "applications", "db_key_field" => "id", "db_finalclass_field" => "", @@ -1776,7 +1776,7 @@ class bizPatch extends logRealObject "key_label" => "id", "name_attcode" => "name", "state_attcode" => "", - "reconc_keys" => array("device_name", "name"), // inherited attributes + "reconc_keys" => array("device_id", "name"), // inherited attributes "db_table" => "patches", "db_key_field" => "id", "db_finalclass_field" => "", diff --git a/pages/csvimport.php b/pages/csvimport.php index f20e28eb5..b617192e9 100644 --- a/pages/csvimport.php +++ b/pages/csvimport.php @@ -210,13 +210,25 @@ function ProcessData($oPage, $sClass, $oCSVParser, $aFieldMap, $aIsReconcKey, CM foreach($aFieldMap as $sFieldId=>$sColDesc) { $iFieldId = (int) substr($sFieldId, strlen("field")); + + if (array_key_exists($sFieldId, $aIsReconcKey)) + { + // This column will be used as a reconciliation key + + if (IsExtKeyField($sColDesc)) + { + list($sAttCode, $sExtReconcKeyAttCode) = GetExtKeyFieldCodes($sColDesc); + } + else + { + $sAttCode = $sColDesc; + } + $aReconcilKeys[$sAttCode] = $iFieldId; + } + if ($sColDesc == "id") { $aAttList['id'] = $iFieldId; - if (array_key_exists($sFieldId, $aIsReconcKey)) - { - $aReconcilKeys['id'] = $iFieldId; - } } elseif ($sColDesc == "__none__") { @@ -226,14 +238,12 @@ function ProcessData($oPage, $sClass, $oCSVParser, $aFieldMap, $aIsReconcKey, CM { // This field is value to search on, to find a value for an external key list($sExtKeyAttCode, $sExtReconcKeyAttCode) = GetExtKeyFieldCodes($sColDesc); + if ($sExtKeyAttCode == $sExtReconcKeyAttCode) + { + $aAttList[$sExtKeyAttCode] = $iFieldId; + } $aExtKeys[$sExtKeyAttCode][$sExtReconcKeyAttCode] = $iFieldId; } - elseif (array_key_exists($sFieldId, $aIsReconcKey)) - { - // This value is a reconciliation key - $aReconcilKeys[$sColDesc] = $iFieldId; - $aAttList[$sColDesc] = $iFieldId; // A reconciliation key is also a field - } else { // $sColDesc is an attribute code @@ -471,7 +481,7 @@ function DoProcessOrVerify($oPage, $sClass, CMDBChange $oChange = null) { if (array_key_exists($sFieldId, $aIsReconcKey)) { - $sReconcKey = " [search]"; + $sReconcKey = "
        [key]"; } else { @@ -499,7 +509,8 @@ function DoProcessOrVerify($oPage, $sClass, CMDBChange $oChange = null) elseif (IsExtKeyField($sColDesc)) { list($sExtKeyAttCode, $sForeignAttCode) = GetExtKeyFieldCodes($sColDesc); - $aDisplayConfig[$sFieldId] = array("label"=>MakeExtFieldLabel($sClass, $sExtKeyAttCode, $sForeignAttCode), "description"=>""); + $sLabel = MakeExtFieldLabel($sClass, $sExtKeyAttCode, $sForeignAttCode); + $aDisplayConfig[$sFieldId] = array("label"=>"$sLabel$sReconcKey", "description"=>""); $aExtKeys[] = $sExtKeyAttCode; } else From 002c1bf869841baed88bd720c838bbcdade73e60 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Sat, 26 Dec 2009 13:39:16 +0000 Subject: [PATCH 131/227] Fixed bug impacting only unit testing procedures SVN:trunk[208] --- addons/userrights/userrightsnull.class.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/addons/userrights/userrightsnull.class.inc.php b/addons/userrights/userrightsnull.class.inc.php index da3f35548..cb8d351d2 100644 --- a/addons/userrights/userrightsnull.class.inc.php +++ b/addons/userrights/userrightsnull.class.inc.php @@ -74,7 +74,7 @@ class UserRightsNull extends UserRightsAddOnAPI return UR_ALLOWED_YES; } - public static function FlushPrivileges() + public function FlushPrivileges() { } } From d8269cc11bee4c33fd5da21570feeaf538afa50c Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Mon, 28 Dec 2009 11:22:54 +0000 Subject: [PATCH 132/227] Trac #56 - Implemented and usable but still not a complete and dynamically generated WSDL file. Unit tests are in place (through client/server SOAP protocol, or directly onto the internal API) SVN:trunk[209] --- core/test.class.inc.php | 30 ++- pages/testlist.inc.php | 156 +++++++++++-- webservices/itop.wsdl | 4 + webservices/soapserver.php | 56 +++++ webservices/webservices.class.inc.php | 323 ++++++++++++++++++++++++++ 5 files changed, 552 insertions(+), 17 deletions(-) create mode 100644 webservices/itop.wsdl create mode 100644 webservices/soapserver.php create mode 100644 webservices/webservices.class.inc.php diff --git a/core/test.class.inc.php b/core/test.class.inc.php index f20ebec07..905e7ad06 100644 --- a/core/test.class.inc.php +++ b/core/test.class.inc.php @@ -16,6 +16,7 @@ require_once('dbobjectset.class.php'); require_once('userrights.class.inc.php'); +require_once('../webservices/webservices.class.inc.php'); // Just to differentiate programmatically triggered exceptions and other kind of errors (usefull?) @@ -201,15 +202,16 @@ abstract class TestWebServices extends TestHandler { // simply overload DoExecute (temporary) - static protected function DoPostRequestAuth($sRelativeUrl, $aData, $sLogin = 'admin', $sPassword = '', $sOptionnalHeaders = null) + static protected function DoPostRequestAuth($sRelativeUrl, $aData, $sLogin = 'admin', $sPassword = 'admin', $sOptionnalHeaders = null) { $aDataAndAuth = $aData; $aDataAndAuth['operation'] = 'login'; $aDataAndAuth['auth_user'] = $sLogin; $aDataAndAuth['auth_pwd'] = $sPassword; - - $sHost = $GLOBALS['_SERVER']['HTTP_HOST']; - $sUrl = "http://$sHost/$sRelativeUrl"; + $sHost = $_SERVER['HTTP_HOST']; + $sRawPath = $_SERVER['SCRIPT_NAME']; + $sPath = dirname($sRawPath); + $sUrl = "http://$sHost/$sPath/$sRelativeUrl"; return self::DoPostRequest($sUrl, $aDataAndAuth, $sOptionnalHeaders); } @@ -248,6 +250,26 @@ abstract class TestWebServices extends TestHandler } } +/** + * Test to execute a piece of code (checks if an error occurs) + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +abstract class TestSoapWebService extends TestHandler +{ + // simply overload DoExecute (temporary) + + function __construct() + { + parent::__construct(); + } +} + /** * Test to check that a function outputs some values depending on its input * diff --git a/pages/testlist.inc.php b/pages/testlist.inc.php index a0332e1f7..81803829d 100644 --- a/pages/testlist.inc.php +++ b/pages/testlist.inc.php @@ -591,18 +591,8 @@ class TestMyBizModel extends TestBizModel // Test a complex biz model on the fly /////////////////////////////////////////////////////////////////////////// -class TestQueriesOnFarm extends TestBizModel +abstract class MyFarm extends TestBizModel { - static public function GetName() - { - return 'Farm test'; - } - - static public function GetDescription() - { - return 'A series of tests on the farm business model (SQL generation)'; - } - static public function GetConfigFile() {return '../config-test-farm.php';} protected function DoPrepare() @@ -673,6 +663,20 @@ class TestQueriesOnFarm extends TestBizModel $iId = $oNew->DBInsertNoReload(); return $iId; } +} + + +class TestQueriesOnFarm extends MyFarm +{ + static public function GetName() + { + return 'Farm test'; + } + + static public function GetDescription() + { + return 'A series of tests on the farm business model (SQL generation)'; + } protected function CheckQuery($sQuery, $bIsCorrectQuery) { @@ -940,6 +944,45 @@ class TestBulkChangeOnFarm extends TestBizModel } +/////////////////////////////////////////////////////////////////////////// +// Test data load +/////////////////////////////////////////////////////////////////////////// + +class TestFullTextSearchOnFarm extends MyFarm +{ + static public function GetName() + { + return 'Farm test - full text search'; + } + + static public function GetDescription() + { + return 'Focus on the full text search feature'; + } + + protected function DoExecute() + { + echo "

        Create protagonists...

        "; + + $iId1 = $this->InsertMammal('human', 'male', 10, 0, 0, 'romanoff', 192, '1971-07-19'); + $iId2 = $this->InsertMammal('human', 'female', 9, 0, 0, 'rouanita', 165, '1983-01-23'); + $this->InsertMammal('human', 'female', 3, $iId2, $iId1, 'pomme', 169, '2008-02-23'); + $this->InsertMammal('pig', 'female', 3, 0, 0, 'grouinkette', 85, '2006-06-01'); + $this->InsertMammal('donkey', 'female', 3, 0, 0, 'muleta', 124, '2003-11-11'); + + $this->InsertBird('rooster', 'male', 12, 0, 0); + $this->InsertFlyingBird('pie', 'female', 11, 0, 0, 35); + + echo "

        Search...

        "; + $oSearch = new DBObjectSearch('Mammal'); + $oSearch->AddCondition_FullText('manof'); + //$oResultSet = new DBObjectSet($oSearch); + $this->search_and_show_list($oSearch); + return true; + } +} + + /////////////////////////////////////////////////////////////////////////// // Benchmark queries /////////////////////////////////////////////////////////////////////////// @@ -1087,7 +1130,7 @@ class TestItopWebServices extends TestWebServices $sCsvData = $aLoadSpec['csvdata']; $aPostData = array('class' => $sClass, 'csvdata' => $sCsvData); - $sRes = self::DoPostRequestAuth('webservices/import.php', $aPostData); + $sRes = self::DoPostRequestAuth('../webservices/import.php', $aPostData); echo "

        $sTitle

        $sCsvData
        $sRes
        "; } @@ -1110,7 +1153,7 @@ class TestItopWebServices extends TestWebServices ), array( 'class' => 'bizTeam', - 'csvdata' => "name;org_id;location_id\nSquadra Azzura;1;1" + 'csvdata' => "name;org_id;location_name\nSquadra Azzura2;1;Paris" ), array( 'class' => 'bizWorkgroup', @@ -1130,4 +1173,91 @@ class TestItopWebServices extends TestWebServices return true; } } + +$aWebServices = array( + array( + 'verb' => 'CreateIncidentTicket', + 'args' => array( + 'desc of ticket', /* sDescription */ + 'initial situation blah blah blah', /* sInitialSituation */ + array('id' => 1), /* aCallerDesc */ + array('id' => 2), /* aCustomerDesc */ + array('id' => 1), /* aWorkgroupDesc */ + array( + array( + 'class' => 'logInfra', + 'search' => array('id' => 108), + 'link_values' => array('impactoche' => 'plus que critique'), + ), + array( + 'class' => 'bizDevice', + 'search' => array('name' => 'Router03'), + 'link_values' => array('impact' => 'ouais bof'), + ), + ), /* aImpact */ + 'low' /* sSeverity */ + ), + ), +); + +class TestSoap extends TestSoapWebService +{ + static public function GetName() {return 'Test SOAP';} + static public function GetDescription() {return 'Do basic stuff to test the SOAP capability';} + + protected function DoExecute() + { + $this->m_SoapClient = new SoapClient( +// null, + "http://localhost:81/trunk/webservices/Itop.wsdl", + array( + //'location' => 'http://localhost:81/trunk/webservices/soapserver.php', + //'uri' => 'http://test-itop/', + 'login' => 'admin', + 'password' => 'admin', + // note: using the classmap functionality lead to APACHE fault on the server side + //'classmap' => array('stdClass' => 'ItopError'), + 'trace' => 1, + ) + ); + + global $aWebServices; + $aWebService = $aWebServices[0]; + +// $oRes = $this->m_SoapClient->CreateIncidentTicket(); + $oRes = call_user_func_array(array($this->m_SoapClient, $aWebService['verb']), $aWebService['args']); + + echo "
        \n";
        +		print_r($oRes);
        +		echo "
        \n"; + +print "
        \n"; 
        +print "Request: \n".htmlspecialchars($this->m_SoapClient->__getLastRequest()) ."\n"; 
        +print "Response: \n".htmlspecialchars($this->m_SoapClient->__getLastResponse())."\n"; 
        +print "
        "; + + return true; + } +} + +class TestWebServicesDirect extends TestBizModel +{ + static public function GetName() {return 'Test web services locally';} + static public function GetDescription() {return 'Invoke the service directly (troubleshooting)';} + + static public function GetConfigFile() {return '../config-itop.php';} + + protected function DoExecute() + { + global $aWebServices; + $aWebService = $aWebServices[0]; + + $oWebServices = new WebServices(); + $oRes = call_user_func_array(array($oWebServices, $aWebService['verb']), $aWebService['args']); + echo "
        \n";
        +		print_r($oRes);
        +		echo "
        \n"; + return true; + } +} ?> diff --git a/webservices/itop.wsdl b/webservices/itop.wsdl new file mode 100644 index 000000000..ba9027337 --- /dev/null +++ b/webservices/itop.wsdl @@ -0,0 +1,4 @@ + + +Create an incident ticket from a monitoring system +Some CIs might be specified (by their name/IP) \ No newline at end of file diff --git a/webservices/soapserver.php b/webservices/soapserver.php new file mode 100644 index 000000000..c644cb1b3 --- /dev/null +++ b/webservices/soapserver.php @@ -0,0 +1,56 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ + +// Important note: if some required includes are missing, this might result +// in the error "looks like we got no XML document"... + +require_once('../application/application.inc.php'); +require_once('../application/startup.inc.php'); + +require('./webservices.class.inc.php'); + + +// pb ? - login_web_page::DoLogin(); // Check user rights and prompt if needed + +// Main program + +$oSoapServer = new SoapServer( + null, + //"http://localhost:81/trunk/webservices/Itop.wsdl", // to be a file generated dynamically with location = here + array( + 'uri' => 'http://test-itop/', + // note: using the classmap and no WSDL spec causes a fault in APACHE (looks like an infinite loop) + //'classmap' => array('ItopErrorSOAP' => 'ItopError') + ) +); +// $oSoapServer->setPersistence(SOAP_PERSISTENCE_SESSION); +$oSoapServer->setClass('WebServices', null); + +if ($_SERVER["REQUEST_METHOD"] == "POST") +{ + $oSoapServer->handle(); +} +else +{ + echo "This SOAP server can handle the following functions: "; + $aFunctions = $oSoapServer->getFunctions(); + echo "
          \n"; + foreach($aFunctions as $sFunc) + { + echo "
        • $sFunc
        • \n"; + } + echo "
        \n"; + echo ""; +} +?> diff --git a/webservices/webservices.class.inc.php b/webservices/webservices.class.inc.php new file mode 100644 index 000000000..f8f1864ec --- /dev/null +++ b/webservices/webservices.class.inc.php @@ -0,0 +1,323 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ + +class WebServiceResult +{ + + /** + * Overall status + * + * @var m_bStatus + */ + public $m_bStatus; + + /** + * Error log + * + * @var m_aErrors + */ + public $m_aErrors; + + /** + * Warning log + * + * @var m_aWarnings + */ + public $m_aWarnings; + + /** + * Information log + * + * @var m_aInfos + */ + public $m_aInfos; + + /** + * Constructor + * + * @param status $bStatus + */ + public function __construct() + { + $this->m_bStatus = true; + $this->m_aErrors = array(); + $this->m_aWarnings = array(); + $this->m_aInfos = array(); + } + + /** + * Did the current processing encounter a stopper issue ? + * + * @return bool + */ + public function IsOk() + { + return $this->m_bStatus; + } + + /** + * Log an error + * + * @param description $sDescription + */ + public function LogError($sDescription) + { + $this->m_aErrors[] = $sDescription; + $this->m_bStatus = false; + } + + /** + * Log a warning + * + * @param description $sDescription + */ + public function LogWarning($sDescription) + { + $this->m_aWarnings[] = $sDescription; + } + + /** + * Log an error or a warning + * + * @param string $sDescription + * @param boolean $bIsStopper + */ + public function LogIssue($sDescription, $bIsStopper = true) + { + if ($bIsStopper) $this->LogError($sDescription); + else $this->LogWarning($sDescription); + } + + /** + * Log operation details + * + * @param description $sDescription + */ + public function LogInfo($sDescription) + { + $this->m_aInfos[] = $sDescription; + } +} + +class WebServices +{ + /** + * Helper to set an external key + * + * @param string sAttCode + * @param array aCallerDesc + * @param DBObject oTargetObj + * @param WebServiceResult oRes + * + */ + protected function SetExternalKey($sAttCode, $aExtKeyDesc, &$oTargetObj, &$oRes) + { + $oExtKey = MetaModel::GetAttributeDef(get_class($oTargetObj), $sAttCode); + + $bIsMandatory = !$oExtKey->IsNullAllowed(); + if (count($aExtKeyDesc) == 0) + { + $oRes->LogIssue("Ext key $sAttCode: no data was given to give a value to the key", $bIsMandatory); + return; + } + + $sKeyClass = $oExtKey->GetTargetClass(); + $oReconFilter = new CMDBSearchFilter($sKeyClass); + foreach ($aExtKeyDesc as $sForeignAttCode => $value) + { + if (!MetaModel::IsValidFilterCode($sKeyClass, $sForeignAttCode)) + { + $sMsg = "Ext key $sAttCode: '$sForeignAttCode' is not a valid filter code for class '$sKeyClass'"; + $oRes->LogIssue($sMsg, $bIsMandatory); + } + // The foreign attribute is one of our reconciliation key + $oReconFilter->AddCondition($sForeignAttCode, $value, '='); + } + $oExtObjects = new CMDBObjectSet($oReconFilter); + switch($oExtObjects->Count()) + { + case 0: + $sMsg = "External key $sAttCode could not be found (searched: '".$oReconFilter->ToOQL()."')"; + $oRes->LogIssue($sMsg, $bIsMandatory); + break; + case 1: + // Do change the external key attribute + $oForeignObj = $oExtObjects->Fetch(); + $oTargetObj->Set($sAttCode, $oForeignObj->GetKey()); + + // Report it (no need to report if the object already had this value + if (array_key_exists($sAttCode, $oTargetObj->ListChanges())) + { + $oRes->LogInfo("$sAttCode has been set to ".$oForeignObj->GetKey()); + } + break; + default: + $sMsg = "Found ".$oExtObjects->Count()." matches for external key $sAttCode (searched: '".$oReconFilter->ToOQL()."')"; + $oRes->LogIssue($sMsg, $bIsMandatory); + } + } + + /** + * Helper to link objects + * + * @param string sLinkAttCode + * @param string sLinkedClass + * @param array $aLinkList + * @param DBObject oTargetObj + * @param WebServiceResult oRes + * + * @return array List of objects that could not be found + */ + protected function AddLinkedObjects($sLinkAttCode, $sLinkedClass, $aLinkList, &$oTargetObj, &$oRes) + { + $oLinkAtt = MetaModel::GetAttributeDef(get_class($oTargetObj), $sLinkAttCode); + $sLinkClass = $oLinkAtt->GetLinkedClass(); + $sExtKeyToItem = $oLinkAtt->GetExtKeyToRemote(); + + $aItemsFound = array(); + $aItemsNotFound = array(); + foreach ($aLinkList as $aItemData) + { + $sTargetClass = $aItemData['class']; + if (!MetaModel::IsValidClass($sTargetClass)) + { + $oRes->LogError("Invalid class $sTargetClass for impacted item"); + continue; // skip + } + if (!MetaModel::IsParentClass($sLinkedClass, $sTargetClass)) + { + $oRes->LogError("$sTargetClass is not a child class of $sLinkedClass"); + continue; // skip + } + $oReconFilter = new CMDBSearchFilter($sTargetClass); + $aCIStringDesc = array(); + foreach ($aItemData['search'] as $sAttCode => $value) + { + if (!MetaModel::IsValidFilterCode($sTargetClass, $sAttCode)) + { + $oRes->LogError("Invalid filter code $sAttCode for class $sTargetClass"); + continue; // skip + } + $aCIStringDesc[] = "$sAttCode: $value"; + + // The attribute is one of our reconciliation key + $oReconFilter->AddCondition($sAttCode, $value, '='); + } + if (count($aCIStringDesc) == 1) + { + // take the last and unique value to describe the object + $sItemDesc = $value; + } + else + { + // describe the object by the given keys + $sItemDesc = $sTargetClass.'('.implode('/', $aCIStringDesc).')'; + } + + $oExtObjects = new CMDBObjectSet($oReconFilter); + switch($oExtObjects->Count()) + { + case 0: + $oRes->LogWarning("Object to link $sLinkedClass / $sItemDesc could not be found (searched: '".$oReconFilter->ToOQL()."')"); + $aItemsNotFound[] = $sItemDesc; + break; + case 1: + $aItemsFound[] = array ( + 'object' => $oExtObjects->Fetch(), + 'link_values' => @$aItemData['link_values'], + 'desc' => $sItemDesc, + ); + break; + default: + $oRes->LogWarning("Found ".$oExtObjects->Count()." matches for external key $sAttCode (searched: '".$oReconFilter->ToOQL()."')"); + $aItemsNotFound[] = $sItemDesc; + } + } + + if (count($aItemsFound) > 0) + { + $aLinks = array(); + foreach($aItemsFound as $aItemData) + { + $oLink = MetaModel::NewObject($sLinkClass); + $oLink->Set($sExtKeyToItem, $aItemData['object']->GetKey()); + foreach($aItemData['link_values'] as $sKey => $value) + { + if(!MetaModel::IsValidAttCode($sLinkClass, $sKey)) + { + $oRes->LogWarning("Attaching item '".$aItemData['desc']."', the attribute code '$sKey' is not valid ; check the class '$sLinkClass'"); + } + else + { + $oLink->Set($sKey, $value); + } + } + $aLinks[] = $oLink; + } + $oImpactedInfraSet = DBObjectSet::FromArray($sLinkClass, $aLinks); + $oTargetObj->Set($sLinkAttCode, $oImpactedInfraSet); + } + + return $aItemsNotFound; + } + + /** + * Create an incident ticket from a monitoring system + * Some CIs might be specified (by their name/IP) + * + * @param string sDecription + * @param string sInitialSituation + * @param array aCallerDesc + * @param array aCustomerDesc + * @param array aWorkgroupDesc + * @param array aImpactedCIs + * @param string sSeverity + * + * @return WebServiceResult + */ + function CreateIncidentTicket($sDescription, $sInitialSituation, $aCallerDesc, $aCustomerDesc, $aWorkgroupDesc, $aImpactedCIs, $sSeverity) + { + $oRes = new WebServiceResult(); + + new CMDBChange(); + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + $oMyChange->Set("userinfo", "Administrator"); + $iChangeId = $oMyChange->DBInsertNoReload(); + + $oNewTicket = MetaModel::NewObject('bizIncidentTicket'); + $oNewTicket->Set('title', $sDescription); + $oNewTicket->Set('initial_situation', $sInitialSituation); + $oNewTicket->Set('severity', $sSeverity); + + $this->SetExternalKey('org_id', $aCustomerDesc, $oNewTicket, $oRes); + $this->SetExternalKey('caller_id', $aCallerDesc, $oNewTicket, $oRes); + $this->SetExternalKey('workgroup_id', $aWorkgroupDesc, $oNewTicket, $oRes); + + $aDevicesNotFound = $this->AddLinkedObjects('impacted_infra_manual', 'logInfra', $aImpactedCIs, $oNewTicket, $oRes); + if (count($aDevicesNotFound) > 0) + { + $oTargetObj->Set('impact', implode(', ', $aDevicesNotFound)); + } + + if ($oRes->IsOk()) + { + $iId = $oNewTicket->DBInsertTrackedNoReload($oMyChange); + $oRes->LogInfo("Created ticket #$iId"); + } + return $oRes; + } +} +?> From 9e475054478965fd5bd4b150491fe5b01224c7a3 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Mon, 28 Dec 2009 16:51:58 +0000 Subject: [PATCH 133/227] Fixed regression on the bug fix [207] SVN:trunk[210] --- pages/csvimport.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/csvimport.php b/pages/csvimport.php index b617192e9..377b2a7c3 100644 --- a/pages/csvimport.php +++ b/pages/csvimport.php @@ -238,7 +238,7 @@ function ProcessData($oPage, $sClass, $oCSVParser, $aFieldMap, $aIsReconcKey, CM { // This field is value to search on, to find a value for an external key list($sExtKeyAttCode, $sExtReconcKeyAttCode) = GetExtKeyFieldCodes($sColDesc); - if ($sExtKeyAttCode == $sExtReconcKeyAttCode) + if ($sExtReconcKeyAttCode == 'id') { $aAttList[$sExtKeyAttCode] = $iFieldId; } From 4d1d6bf6b0e4573a7672216a226b3336062c55fe Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Tue, 29 Dec 2009 13:38:02 +0000 Subject: [PATCH 134/227] - Track the changes/history when creating the administrator account (Trac #48) SVN:trunk[211] --- addons/userrights/userrightsmatrix.class.inc.php | 10 +++++++++- addons/userrights/userrightsprofile.class.inc.php | 14 ++++++++++---- 2 files changed, 19 insertions(+), 5 deletions(-) diff --git a/addons/userrights/userrightsmatrix.class.inc.php b/addons/userrights/userrightsmatrix.class.inc.php index dfb8bb7aa..eda597811 100644 --- a/addons/userrights/userrightsmatrix.class.inc.php +++ b/addons/userrights/userrightsmatrix.class.inc.php @@ -173,7 +173,15 @@ class UserRightsMatrix extends UserRightsAddOnAPI $oUser->Set('login', $sAdminUser); $oUser->Set('password', $sAdminPwd); $oUser->Set('userid', 1); // one is for root ! - $iUserId = $oUser->DBInsertNoReload(); + + // Create a change to record the history of the User object + $oChange = MetaModel::NewObject("CMDBChange"); + $oChange->Set("date", time()); + $oChange->Set("userinfo", "Initialization"); + $iChangeId = $oChange->DBInsert(); + + // Now record the admin user object + $iUserId = $oUser->DBInsertTrackedNoReload($oChange); $this->SetupUser($iUserId, true); return true; } diff --git a/addons/userrights/userrightsprofile.class.inc.php b/addons/userrights/userrightsprofile.class.inc.php index d648dc06b..64eaedfaf 100644 --- a/addons/userrights/userrightsprofile.class.inc.php +++ b/addons/userrights/userrightsprofile.class.inc.php @@ -757,12 +757,18 @@ class UserRightsProfile extends UserRightsAddOnAPI // Installation: create the very first user public function CreateAdministrator($sAdminUser, $sAdminPwd) { + // Create a change to record the history of the User object + $oChange = MetaModel::NewObject("CMDBChange"); + $oChange->Set("date", time()); + $oChange->Set("userinfo", "Initialization"); + $iChangeId = $oChange->DBInsert(); + $oOrg = new bizOrganization(); $oOrg->Set('name', 'My Company/Department'); $oOrg->Set('code', 'SOMECODE'); $oOrg->Set('status', 'implementation'); //$oOrg->Set('parent_id', xxx); - $iOrgId = $oOrg->DBInsertNoReload(); + $iOrgId = $oOrg->DBInsertTrackedNoReload($oChange); // Location : optional //$oLocation = new bizLocation(); @@ -784,20 +790,20 @@ class UserRightsProfile extends UserRightsAddOnAPI $oContact->Set('phone', ''); //$oContact->Set('location_id', $iLocationId); $oContact->Set('employee_number', ''); - $iContactId = $oContact->DBInsertNoReload(); + $iContactId = $oContact->DBInsertTrackedNoReload($oChange); $oUser = new URP_Users(); $oUser->Set('login', $sAdminUser); $oUser->Set('password', $sAdminPwd); $oUser->Set('userid', $iContactId); - $iUserId = $oUser->DBInsertNoReload(); + $iUserId = $oUser->DBInsertTrackedNoReload($oChange); // Add this user to the very specific 'admin' profile $oUserProfile = new URP_UserProfile(); $oUserProfile->Set('userid', $iUserId); $oUserProfile->Set('profileid', ADMIN_PROFILE_ID); $oUserProfile->Set('reason', 'By definition, the administrator must have the administrator profile'); - $oUserProfile->DBInsertNoReload(); + $oUserProfile->DBInsertTrackedNoReload($oChange); return true; } From adeb7b17b1d77caf2007f0354191e8af226ea070 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Tue, 29 Dec 2009 13:40:29 +0000 Subject: [PATCH 135/227] - Added 2 new parameters to the config file to manage the display_limit (Trac #61) SVN:trunk[212] --- core/config.class.inc.php | 41 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 41 insertions(+) diff --git a/core/config.class.inc.php b/core/config.class.inc.php index ca2d4a5e9..6d6e5e982 100644 --- a/core/config.class.inc.php +++ b/core/config.class.inc.php @@ -16,6 +16,9 @@ class ConfigException extends CoreException { } +define ('DEFAULT_MIN_DISPLAY_LIMIT', 10); +define ('DEFAULT_MAX_DISPLAY_LIMIT', 15); + class Config { //protected $m_bIsLoaded = false; @@ -31,6 +34,15 @@ class Config protected $m_sDBName; protected $m_sDBSubname; + /** + * @var integer Number of elements to be displayed when there are more than m_iMaxDisplayLimit elements + */ + protected $m_iMinDisplayLimit; + /** + * @var integer Max number of elements before truncating the display + */ + protected $m_iMaxDisplayLimit; + public function __construct($sConfigFile, $bLoadConfig = true) { $this->m_sFile = $sConfigFile; @@ -43,6 +55,8 @@ class Config $this->m_sDBPwd = ''; $this->m_sDBName = ''; $this->m_sDBSubname = ''; + $this->m_iMinDisplayLimit = DEFAULT_MIN_DISPLAY_LIMIT; + $this->m_iMaxDisplayLimit = DEFAULT_MAX_DISPLAY_LIMIT; if ($bLoadConfig) { $this->Load($sConfigFile); @@ -119,6 +133,9 @@ class Config $this->m_sDBPwd = trim($MySettings['db_pwd']); $this->m_sDBName = trim($MySettings['db_name']); $this->m_sDBSubname = trim($MySettings['db_subname']); + + $this->m_iMinDisplayLimit = isset($MySettings['min_display_limit']) ? trim($MySettings['min_display_limit']) : DEFAULT_MIN_DISPLAY_LIMIT; + $this->m_iMaxDisplayLimit = isset($MySettings['max_display_limit']) ? trim($MySettings['max_display_limit']) : DEFAULT_MAX_DISPLAY_LIMIT; } protected function Verify() @@ -177,6 +194,16 @@ class Config return $this->m_sDBPwd; } + public function GetMinDisplayLimit() + { + return $this->m_iMinDisplayLimit; + } + + public function GetMaxDisplayLimit() + { + return $this->m_iMaxDisplayLimit; + } + public function SetDBHost($sDBHost) { $this->m_sDBHost = $sDBHost; @@ -201,6 +228,17 @@ class Config { $this->m_sDBPwd = $sPwd; } + + public function SetMinDisplayLimit($iMinDisplayLimit) + { + $this->m_iMinDisplayLimit = $iMinDisplayLimit; + } + + public function SetMaxDisplayLimit($iMaxDisplayLimit) + { + $this->m_iMaxDisplayLimit = $iMaxDisplayLimit; + } + public function FileIsWritable() { return is_writable($this->m_sFile); @@ -237,6 +275,9 @@ class Config fwrite($hFile, "\t'db_pwd' => '".addslashes($this->m_sDBPwd)."',\n"); fwrite($hFile, "\t'db_name' => '{$this->m_sDBName}',\n"); fwrite($hFile, "\t'db_subname' => '{$this->m_sDBSubname}',\n"); + fwrite($hFile, "\n"); + fwrite($hFile, "\t'min_display_limit' => {$this->m_iMinDisplayLimit},\n"); + fwrite($hFile, "\t'max_display_limit' => {$this->m_iMaxDisplayLimit},\n"); fwrite($hFile, ");\n"); fwrite($hFile, "\n/**\n"); From e91b81c777f393e33bf19d2a567a863c62a323b1 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Tue, 29 Dec 2009 13:41:44 +0000 Subject: [PATCH 136/227] SVN:trunk[213] --- images/Thumbs.db | Bin 187392 -> 0 bytes 1 file changed, 0 insertions(+), 0 deletions(-) delete mode 100644 images/Thumbs.db diff --git a/images/Thumbs.db b/images/Thumbs.db deleted file mode 100644 index 812e6e80ae43e6019a8a2e828d2a6b65fde2019f..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 187392 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 T5Sn+Y{v-=Ye$#Y9t?>T Date: Tue, 29 Dec 2009 14:02:18 +0000 Subject: [PATCH 137/227] - completed the fix of the user object history (Trac #48) - completed the implementation of truncated lists (Trac #61) - Fixed the handling of the search form in the details page (Trac #29) SVN:trunk[214] --- application/cmdbabstract.class.inc.php | 41 +++++++++++++++++++++---- application/displayblock.class.inc.php | 9 +++++- application/itopwebpage.class.inc.php | 1 + application/startup.inc.php | 2 ++ application/uilinkswizard.class.inc.php | 7 +++-- css/light-grey.css | 21 +++++++++++++ js/utils.js | 19 ++++++++++++ pages/UI.php | 41 ++++++++++++++++++++++--- pages/UniversalSearch.php | 4 +-- pages/ajax.render.php | 8 ++++- pages/run_query.php | 4 +-- webservices/export.php | 2 +- 12 files changed, 139 insertions(+), 20 deletions(-) create mode 100644 js/utils.js diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index 18d2f4a93..c288b7b26 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -131,7 +131,7 @@ abstract class cmdbAbstractObject extends CMDBObject $oPage->add("

        ".MetaModel::GetName(get_class($this)).": ".$this->GetDisplayName()."

        \n"); // history block (with toggle) - $oHistoryFilter = new DBObjectSearch('CMDBChangeOpSetAttribute'); + $oHistoryFilter = new DBObjectSearch('CMDBChangeOp'); $oHistoryFilter->AddCondition('objkey', $this->GetKey()); $oHistoryFilter->AddCondition('objclass', get_class($this)); $oBlock = new HistoryBlock($oHistoryFilter, 'toggle', false); @@ -269,6 +269,8 @@ abstract class cmdbAbstractObject extends CMDBObject //public static function GetDisplaySet(web_page $oPage, CMDBObjectSet $oSet, $sLinkageAttribute = '', $bDisplayMenu = true, $bSelectMode = false) public static function GetDisplaySet(web_page $oPage, CMDBObjectSet $oSet, $aExtraParams = array()) { + global $g_oConfig; + static $iListId = 0; $iListId++; @@ -341,7 +343,16 @@ abstract class cmdbAbstractObject extends CMDBObject } $aValues = array(); $oSet->Seek(0); - while ($oObj = $oSet->Fetch()) + $bDisplayLimit = isset($aExtraParams['display_limit']) ? $aExtraParams['display_limit'] : true; + $iMaxObjects = -1; + if ($bDisplayLimit) + { + if ($oSet->Count() > $g_oConfig->GetMaxDisplayLimit()) + { + $iMaxObjects = $g_oConfig->GetMinDisplayLimit(); + } + } + while (($oObj = $oSet->Fetch()) && ($iMaxObjects != 0)) { $aRow['key'] = $oObj->GetKey(); if ($bSelectMode) @@ -354,6 +365,7 @@ abstract class cmdbAbstractObject extends CMDBObject $aRow[$sAttCode] = $oObj->GetAsHTML($sAttCode); } $aValues[] = $aRow; + $iMaxObjects--; } $oMenuBlock = new MenuBlock($oSet->GetFilter()); $sHtml .= ''; @@ -367,7 +379,22 @@ abstract class cmdbAbstractObject extends CMDBObject //$aMenuExtraParams['linkage'] = $sLinkageAttribute; $aMenuExtraParams = $aExtraParams; } - $sHtml .= ''; } @@ -570,9 +597,11 @@ abstract class cmdbAbstractObject extends CMDBObject $sHtml .= "
         '.$oSet->Count().' object(s)'; + if ($bDisplayLimit && ($oSet->Count() > $g_oConfig->GetMaxDisplayLimit())) + { + // list truncated + $divId = $aExtraParams['block_id']; + $sFilter = $oSet->GetFilter()->serialize(); + $aExtraParams['display_limit'] = false; // To expand the full list + $sExtraParams = addslashes(str_replace('"', "'", json_encode($aExtraParams))); // JSON encode, change the style of the quotes and escape them + $sHtml .= '
        '.$g_oConfig->GetMinDisplayLimit().' object(s) displayed out of '.$oSet->Count().'  Display All'; + $oPage->add_ready_script("$('#{$divId} table.listResults').addClass('truncated');"); + $oPage->add_ready_script("$('#{$divId} table.listResults tr:last td').addClass('truncated');"); + } + else + { + // Full list + $sHtml .= '
         '.$oSet->Count().' object(s)'; + } $sHtml .= $oMenuBlock->GetRenderContent($oPage, $aMenuExtraParams); $sHtml .= '
        \n"; foreach($aExtraParams as $sName => $sValue) { - $sHtml .= "\n"; + $sHtml .= "\n"; } - $sHtml .= "\n"; + $sHtml .= "\n"; + $sHtml .= "\n"; + $sHtml .= "\n"; $sHtml .= "\n"; $sHtml .= "
    \n"; @@ -598,7 +627,7 @@ abstract class cmdbAbstractObject extends CMDBObject { $sHtml .= "\n"; } - $sHtml .= "\n"; + $sHtml .= "\n"; $sHtml .= "\n"; $sHtml .= "
    \n"; return $sHtml; diff --git a/application/displayblock.class.inc.php b/application/displayblock.class.inc.php index f05b65e6a..c006bd91a 100644 --- a/application/displayblock.class.inc.php +++ b/application/displayblock.class.inc.php @@ -144,7 +144,10 @@ class DisplayBlock public function Display(web_page $oPage, $sId, $aExtraParams = array()) { + $oPage->add($this->GetDisplay($oPage, $sId, $aExtraParams)); + /* $aExtraParams = array_merge($aExtraParams, $this->m_aParams); + $aExtraParams['block_id'] = $sId; if (!$this->m_bAsynchronous) { // render now @@ -171,12 +174,14 @@ class DisplayBlock ); '); // TO DO: add support for $aExtraParams in asynchronous/Ajax mode } + */ } public function GetDisplay(web_page $oPage, $sId, $aExtraParams = array()) { $sHtml = ''; $aExtraParams = array_merge($aExtraParams, $this->m_aParams); + $aExtraParams['block_id'] = $sId; if (!$this->m_bAsynchronous) { // render now @@ -188,17 +193,19 @@ class DisplayBlock { // render it as an Ajax (asynchronous) call $sFilter = $this->m_oFilter->serialize(); + $sExtraParams = addslashes(str_replace('"', "'", json_encode($aExtraParams))); // JSON encode, change the style of the quotes and escape them $sHtml .= "
    \n"; $sHtml .= $oPage->GetP(" Loading..."); $sHtml .= "
    \n"; $sHtml .= ' '; // TO DO: add support for $aExtraParams in asynchronous/Ajax mode diff --git a/application/itopwebpage.class.inc.php b/application/itopwebpage.class.inc.php index 1b1bca3d8..6f8698deb 100644 --- a/application/itopwebpage.class.inc.php +++ b/application/itopwebpage.class.inc.php @@ -35,6 +35,7 @@ class iTopWebPage extends nice_web_page $this->add_linked_script("../js/date.js"); $this->add_linked_script("../js/jquery.date.picker.js"); $this->add_linked_script("../js/jquery.tablesorter.min.js"); + $this->add_linked_script("../js/utils.js"); //$this->add_linked_script("../js/jquery-ui-personalized-1.5.3.js"); $this->add_linked_script("../js/swfobject.js"); $this->add_linked_stylesheet("../css/jquery.treeview.css"); diff --git a/application/startup.inc.php b/application/startup.inc.php index 7f3448f30..89b091117 100644 --- a/application/startup.inc.php +++ b/application/startup.inc.php @@ -2,6 +2,8 @@ require_once('../application/utils.inc.php'); +$g_oConfig = new Config('../config-itop.php'); + MetaModel::Startup('../config-itop.php'); ?> diff --git a/application/uilinkswizard.class.inc.php b/application/uilinkswizard.class.inc.php index db57e2075..3779efac1 100644 --- a/application/uilinkswizard.class.inc.php +++ b/application/uilinkswizard.class.inc.php @@ -159,6 +159,7 @@ class UILinksWizard { $('#SearchResultsToAdd table.listResults tbody').attr('height', tbodyHeight); $('#SearchResultsToAdd .listResults tbody').css('overflow', 'auto'); + $('#SearchResultsToAdd .listResults').tablesorter( { headers: { 0:{sorter: false }}, widgets: ['zebra']} ); // sortable and zebra tables } } @@ -214,7 +215,7 @@ class UILinksWizard } $('.listResults tbody').append(data); $('.listResults').trigger('update'); - $('.listResults').tablesorter( { headers: { 0:{sorter: false }}, widgets: ['zebra']} ); // sortable and zebra tables + $('.listResults').tablesorter( { headers: { 0:{sorter: false }}, widgets: ['zebra']} ); // sortable and zebra tables }, 'html' ); @@ -362,12 +363,12 @@ class UILinksWizard public function SearchObjectsToAdd(web_page $oP, UserContext $oContext) { - $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sLinkageAttr); + //$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sLinkageAttr); $oFilter = $oContext->NewFilter($this->m_sLinkedClass); $oSet = new CMDBObjectSet($oFilter); $oBlock = new DisplayBlock($oFilter, 'list', false); - $oBlock->Display($oP, 'ResultsToAdd', array('menu' => false, 'selection_mode' => true)); // Don't display the 'Actions' menu on the results + $oBlock->Display($oP, 'ResultsToAdd', array('menu' => false, 'selection_mode' => true, 'display_limit' => false)); // Don't display the 'Actions' menu on the results } public function DoAddObjects(web_page $oP, UserContext $oContext, $aLinkedObjectIds = array()) diff --git a/css/light-grey.css b/css/light-grey.css index 994bf7a3b..ac9df52d8 100644 --- a/css/light-grey.css +++ b/css/light-grey.css @@ -663,3 +663,24 @@ div.HRDrawer { .mandatory { border: 1px solid #f00; } +table.listResults tr td.truncated { + background: transparent; +} + +table.listResults tr td.truncated { + background: url(../images/truncated.png) bottom repeat-x; + margin-bottom: -3px; +} + +table.listResults tr.even td.truncated { + background: #f9f9f1 url(../images/truncated.png) bottom repeat-x; +} + +table.listResults tr.even td.truncated.hover { + background: #E8FFD3 url(../images/truncated.png) bottom repeat-x; +} + +table.listResults.truncated { + border-bottom: 0; + padding-bottom: 0; +} diff --git a/js/utils.js b/js/utils.js new file mode 100644 index 000000000..9cdf7f9fd --- /dev/null +++ b/js/utils.js @@ -0,0 +1,19 @@ +// Some general purpose JS functions for the iTop application +/** + * Reload a truncated list + */ +function ReloadTruncatedList(divId, sSerializedFilter, sExtraParams) +{ + $('#'+divId).addClass('loading'); + //$('#'+divId).blockUI(); + $.get('ajax.render.php?filter='+sSerializedFilter+'&style=list', + { operation: 'ajax', extra_params: sExtraParams }, + function(data){ + $('#'+divId).empty(); + $('#'+divId).append(data); + $('#'+divId).removeClass('loading'); + $('#'+divId+' .listResults').tablesorter( { headers: { 0:{sorter: false }}, widgets: ['zebra']} ); // sortable and zebra tables + //$('#'+divId).unblockUI(); + } + ); +} diff --git a/pages/UI.php b/pages/UI.php index b20da6808..aeaf59fde 100644 --- a/pages/UI.php +++ b/pages/UI.php @@ -74,7 +74,7 @@ switch($operation) } break; - case 'search_form': + case 'search_oql': $sOQLClass = utils::ReadParam('oql_class', ''); $sOQLClause = utils::ReadParam('oql_clause', ''); $sFormat = utils::ReadParam('format', ''); @@ -128,7 +128,39 @@ switch($operation) } } break; - + case 'search_form': + $sClass = utils::ReadParam('class', ''); + $sFormat = utils::ReadParam('format', 'html'); + $bSearchForm = utils::ReadParam('search_form', true); + if (empty($sClass)) + { + $oP->set_title("iTop - Error"); + $oP->add("

    'class' must be specifed for this operation.

    \n"); + } + else + { + $oP->set_title("iTop - Search results"); + $oFilter = $oContext->NewFilter($sClass); + $oSet = new DBObjectSet($oFilter); + if ($bSearchForm) + { + $oBlock = new DisplayBlock($oFilter, 'search', false /* Asynchronous */, array('open' => true)); + $oBlock->Display($oP, 0); + } + if (strtolower($sFormat) == 'csv') + { + $oBlock = new DisplayBlock($oFilter, 'csv', false); + $oBlock->Display($oP, 1); + $oP->add_ready_script(" $('#csv').css('height', '95%');"); // adjust the size of the block + } + else + { + $oBlock = new DisplayBlock($oFilter, 'list', false); + $oBlock->Display($oP, 1); + } + } + break; + case 'search': $sFilter = utils::ReadParam('filter', ''); $sFormat = utils::ReadParam('format', ''); @@ -158,7 +190,7 @@ switch($operation) else { $oBlock = new DisplayBlock($oFilter, 'list', false); - $oBlock->Display($oP, 0); + $oBlock->Display($oP, 1); } } break; @@ -173,6 +205,7 @@ switch($operation) { $oP->p("

    Results for '$sFullText':

    \n"); $iCount = 0; + $iBlock = 0; // Search in full text mode in all the classes foreach(MetaModel::GetClasses('bizmodel') as $sClassName) { @@ -198,7 +231,7 @@ switch($operation) $oP->add("\n"); $oLeafsFilter->AddCondition('pkey', $aLeafs, 'IN'); $oBlock = new DisplayBlock($oLeafsFilter, 'list', false); - $oBlock->Display($oP, 0); + $oBlock->Display($oP, $iBlock++); } } } diff --git a/pages/UniversalSearch.php b/pages/UniversalSearch.php index 3fe8fe246..05333aa7a 100644 --- a/pages/UniversalSearch.php +++ b/pages/UniversalSearch.php @@ -73,11 +73,11 @@ if ($oFilter != null) { $oSet = new CMDBObjectSet($oFilter); $oBlock = new DisplayBlock($oFilter, 'search', false); - $oBlock->Display($oP, 0); + $oBlock->Display($oP, 0, array('open' => true)); // Search results $oResultBlock = new DisplayBlock($oFilter, 'list', false); - $oResultBlock->RenderContent($oP); + $oResultBlock->Display($oP, 1); // Menu node $sFilter = $oFilter->ToOQL(); diff --git a/pages/ajax.render.php b/pages/ajax.render.php index c3d783340..682e3efca 100644 --- a/pages/ajax.render.php +++ b/pages/ajax.render.php @@ -100,6 +100,12 @@ switch($operation) case 'ajax': if ($sFilter != "") { + $sExtraParams = stripslashes(utils::ReadParam('extra_params', '')); + $aExtraParams = array(); + if (!empty($sExtraParams)) + { + $aExtraParams = json_decode(str_replace("'", '"', $sExtraParams), true /* associative array */); + } if ($sEncoding == 'sibusql') { $oFilter = CMDBSearchFilter::FromSibusQL($sFilter); @@ -109,7 +115,7 @@ switch($operation) $oFilter = CMDBSearchFilter::unserialize($sFilter); } $oDisplayBlock = new DisplayBlock($oFilter, $sStyle, false); - $oDisplayBlock->RenderContent($oPage); + $oDisplayBlock->RenderContent($oPage, $aExtraParams); } else { diff --git a/pages/run_query.php b/pages/run_query.php index 8214bd8ed..2457121bf 100644 --- a/pages/run_query.php +++ b/pages/run_query.php @@ -109,8 +109,8 @@ try { $oP->add("

    Query results

    \n"); - $oSet = new CMDBObjectSet($oFilter); - cmdbAbstractObject::DisplaySet($oP, $oSet); + $oResultBlock = new DisplayBlock($oFilter, 'list', false); + $oResultBlock->Display($oP, 1); $oP->p(''); $oP->StartCollapsibleSection('More info on the query', false); diff --git a/webservices/export.php b/webservices/export.php index 912ddebfa..302b88566 100644 --- a/webservices/export.php +++ b/webservices/export.php @@ -47,7 +47,7 @@ if (!empty($sExpression)) } $sUrl = "$sProtocol://{$sServerName}{$sPort}/pages/"; $oP->set_base($sUrl); - cmdbAbstractObject::DisplaySet($oP, $oSet, array('menu' => false)); + cmdbAbstractObject::DisplaySet($oP, $oSet, array('menu' => false, 'display_limit' => false)); // no menu, no truncated list break; case 'csv': From c9f8417f7689250870b06a5f32fbd226616d59f9 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Tue, 29 Dec 2009 15:15:36 +0000 Subject: [PATCH 138/227] _ Fixed Trac #46: when a mandatory field has no valid value, an empty drop-down list is displayed, this prevents the user from validating the from. SVN:trunk[215] --- application/cmdbabstract.class.inc.php | 12 +++++++----- 1 file changed, 7 insertions(+), 5 deletions(-) diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index c288b7b26..26080b1d0 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -681,11 +681,12 @@ abstract class cmdbAbstractObject extends CMDBObject if ($aAllowedValues !== null) { //Enum field or external key, display a combo - if (count($aAllowedValues) == 0) - { - $sHTMLValue = ""; - } - else if (count($aAllowedValues) > 50) + //if (count($aAllowedValues) == 0) + //{ + // $sHTMLValue = ""; + //} + //else if (count($aAllowedValues) > 50) + if (count($aAllowedValues) > 50) { // too many choices, use an autocomplete // The input for the auto complete @@ -698,6 +699,7 @@ abstract class cmdbAbstractObject extends CMDBObject else { // Few choices, use a normal 'select' + // In case there are no valid values, the select will be empty, thus blocking the user from validating the form $sHTMLValue = "m_iInputId}\" size=\"35\" value=\"\" title=\"Type the first 3 characters\"/>"; $sHTMLValue .= "m_iInputId}\" value=\" Add... \" class=\"action\" onClick=\"oLinkWidget{$this->m_iInputId}.AddObject();\"/>"; $sHTMLValue .= " m_iInputId', '$sExtKeyToRemote');\"/>"; @@ -94,10 +94,13 @@ class UILinksWidget { // Few choices, use a normal 'select' $sHTMLValue = "\n"; } @@ -274,7 +277,7 @@ EOF; $sHTML = "
    \n"; $sHTML .= "
    \n"; - $sHTML .= "

    ".MetaModel::GetName($sLinkedClass)." attributes

    \n"; + $sHTML .= "

    ".MetaModel::GetName($sLinkedClass)." attributes

    \n"; $sHTML .= "
    \n"; $index = 0; $aAttrsMap = array(); diff --git a/application/webpage.class.inc.php b/application/webpage.class.inc.php index e33e7235c..b7cafa940 100644 --- a/application/webpage.class.inc.php +++ b/application/webpage.class.inc.php @@ -14,6 +14,7 @@ class web_page { protected $s_title; protected $s_content; + protected $s_deferred_content; protected $a_scripts; protected $a_styles; protected $a_include_scripts; @@ -25,6 +26,7 @@ class web_page { $this->s_title = $s_title; $this->s_content = ""; + $this->s_deferred_content = ''; $this->a_scripts = array(); $this->a_styles = array(); $this->a_linked_scripts = array(); @@ -59,6 +61,16 @@ class web_page $this->s_content .= $s_html; } + /** + * Add any text or HTML fragment at the end of the body of the page + * This is useful to add hidden content, DIVs or FORMs that should not + * be embedded into each other. + */ + public function add_at_the_end($s_html) + { + $this->s_deferred_content .= $s_html; + } + /** * Add a paragraph to the body of the page */ @@ -281,6 +293,7 @@ class web_page { echo "
    $s_captured_output
    \n"; } + echo $this->s_deferred_content; echo "\n"; echo "\n"; } From 7efeec5acdc9423880660ec89d2afd98c8fee743 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Tue, 29 Dec 2009 17:08:04 +0000 Subject: [PATCH 140/227] - Fixed bug Trac #65: incorrect display of non-ASCII characters. htmlentities must be aware of the charset used ! SVN:trunk[217] --- core/MyHelpers.class.inc.php | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/core/MyHelpers.class.inc.php b/core/MyHelpers.class.inc.php index 8ee89a5ff..16e149cd9 100644 --- a/core/MyHelpers.class.inc.php +++ b/core/MyHelpers.class.inc.php @@ -434,9 +434,10 @@ class Str } public static function pure2html($pure, $maxLength = false) { + // Check for HTML entities, but be careful the DB is in UTF-8 return $maxLength - ? htmlentities(substr($pure, 0, $maxLength)) - : htmlentities($pure); + ? htmlentities(substr($pure, 0, $maxLength), ENT_COMPAT, 'UTF-8') + : htmlentities($pure, ENT_COMPAT, 'UTF-8'); } public static function pure2sql($pure, $maxLength = false) { From 61874fbea30b4a7c3d338efea6afa01372ccc254 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Tue, 29 Dec 2009 17:32:18 +0000 Subject: [PATCH 141/227] - Fixed bug Trac #43 ('links attribute' dialog can now be validated by pressing Enter) SVN:trunk[218] --- application/ui.linkswidget.class.inc.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/ui.linkswidget.class.inc.php b/application/ui.linkswidget.class.inc.php index 8109dc649..8100f9ee8 100644 --- a/application/ui.linkswidget.class.inc.php +++ b/application/ui.linkswidget.class.inc.php @@ -253,7 +253,7 @@ class UILinksWidget -       +       @@ -318,7 +318,7 @@ EOF; } } $sHTML .= $oPage->GetDetails($aDetails); - $sHTML .= "     \n"; + $sHTML .= "     \n"; $sHTML .= "\n"; $sHTML .= "
    \n"; $sHTML .= "
    \n"; From 9247b326c87e3e960ca3a54d17467f5dd6c95ca8 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Tue, 29 Dec 2009 18:18:17 +0000 Subject: [PATCH 142/227] - Fixed bug (Trac #49) display of user's grant matrix not refreshed properly. Actually it was not a display issue but the cache was not flushed at the right time and the flush was just loading more entries in the cache without clearing the current ones. SVN:trunk[219] --- .../userrightsprofile.class.inc.php | 33 +++++++++++++++---- 1 file changed, 27 insertions(+), 6 deletions(-) diff --git a/addons/userrights/userrightsprofile.class.inc.php b/addons/userrights/userrightsprofile.class.inc.php index 64eaedfaf..bc99de3a2 100644 --- a/addons/userrights/userrightsprofile.class.inc.php +++ b/addons/userrights/userrightsprofile.class.inc.php @@ -18,6 +18,25 @@ define('ADMIN_PROFILE_ID', 1); class UserRightsBaseClass extends cmdbAbstractObject { + // Whenever something changes, reload the privileges + + public function DBInsertTracked(CMDBChange $oChange) + { + parent::DBInsertTracked($oChange); + UserRights::FlushPrivileges(); + } + + public function DBUpdateTracked(CMDBChange $oChange) + { + parent::DBUpdateTracked($oChange); + UserRights::FlushPrivileges(); + } + + public function DBDeleteTracked(CMDBChange $oChange) + { + parent::DBDeleteTracked($oChange); + UserRights::FlushPrivileges(); + } } @@ -131,9 +150,6 @@ class URP_Users extends UserRightsBaseClass function DisplayBareRelations(web_page $oPage) { - // We may have just added a user, then we have to reset any existing cache - UserRights::FlushPrivileges(); - parent::DisplayBareRelations($oPage); $oPage->SetCurrentTabContainer('Related Objects'); @@ -258,9 +274,6 @@ class URP_Profiles extends UserRightsBaseClass function DisplayBareRelations(web_page $oPage) { - // We may have just added a user, then we have to reset any existing cache - UserRights::FlushPrivileges(); - parent::DisplayBareRelations($oPage); $oPage->SetCurrentTabContainer('Related Objects'); @@ -851,6 +864,8 @@ class UserRightsProfile extends UserRightsAddOnAPI // Could be loaded in a shared memory (?) $oUserSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_Users")); + $this->m_aUsers = array(); + $this->m_aLogin2UserId = array(); while ($oUser = $oUserSet->Fetch()) { $this->m_aUsers[$oUser->GetKey()] = $oUser; @@ -858,24 +873,29 @@ class UserRightsProfile extends UserRightsAddOnAPI } $oDimensionSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_Dimensions")); + $this->m_aDimensions = array(); while ($oDimension = $oDimensionSet->Fetch()) { $this->m_aDimensions[$oDimension->GetKey()] = $oDimension; } $oClassProjSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_ClassProjection")); + $this->m_aClassProjs = array(); while ($oClassProj = $oClassProjSet->Fetch()) { $this->m_aClassProjs[$oClassProj->Get('class')][$oClassProj->Get('dimensionid')] = $oClassProj; } $oProfileSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_Profiles")); + $this->m_aProfiles = array(); while ($oProfile = $oProfileSet->Fetch()) { $this->m_aProfiles[$oProfile->GetKey()] = $oProfile; } $oUserProfileSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_UserProfile")); + $this->m_aUserProfiles = array(); + $this->m_aAdmins = array(); while ($oUserProfile = $oUserProfileSet->Fetch()) { $this->m_aUserProfiles[$oUserProfile->Get('userid')][$oUserProfile->Get('profileid')] = $oUserProfile; @@ -886,6 +906,7 @@ class UserRightsProfile extends UserRightsAddOnAPI } $oProProSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT URP_ProfileProjection")); + $this->m_aProPros = array(); while ($oProPro = $oProProSet->Fetch()) { $this->m_aProPros[$oProPro->Get('profileid')][$oProPro->Get('dimensionid')] = $oProPro; From 966d4820caf658621a6f14791de8c52332b5bc0e Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Thu, 31 Dec 2009 09:08:09 +0000 Subject: [PATCH 143/227] - First implementation of documents - small cleanup to remove a global Config variable (and removed some deprecated utils:: functions) - removed the 'DocumentVersion' object since previous versions are now tracked as normal change objects. SVN:trunk[220] --- application/cmdbabstract.class.inc.php | 50 ++++++--- application/startup.inc.php | 4 +- application/uiwizard.class.inc.php | 9 +- application/utils.inc.php | 147 ++++++++++++++++++------- application/wizardhelper.class.inc.php | 22 +++- business/itop.business.class.inc.php | 51 +-------- business/templates/document.html | 6 - core/attributedef.class.inc.php | 15 ++- core/cmdbchangeop.class.inc.php | 6 +- core/cmdbsource.class.inc.php | 17 +++ core/ormdocument.class.inc.php | 77 +++++++++++-- js/utils.js | 12 ++ js/wizardhelper.js | 1 + pages/UI.php | 13 ++- pages/ajax.render.php | 51 +++++++++ setup/index.php | 54 ++++++++- 16 files changed, 403 insertions(+), 132 deletions(-) diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index 26080b1d0..ccd5c9e55 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -225,6 +225,21 @@ abstract class cmdbAbstractObject extends CMDBObject } } $sHtml .= $oPage->GetDetails($aDetails); + // Documents displayed inline (when possible: images, html...) + foreach($aList as $sAttCode) + { + $oAttDef = Metamodel::GetAttributeDef($sClass, $sAttCode); + if ( $oAttDef->GetEditClass() == 'Document') + { + $oDoc = $this->Get($sAttCode); + if (is_object($oDoc) && !$oDoc->IsEmpty()) + { + $sHtml .= "

    Open in New Window: ".$oDoc->GetDisplayLink($sClass, $this->GetKey(), $sAttCode).", \n"; + $sHtml .= "Download: ".$oDoc->GetDownloadLink($sClass, $this->GetKey(), $sAttCode)."

    \n"; + $sHtml .= "
    ".$oDoc->GetDisplayInline($sClass, $this->GetKey(), $sAttCode)."
    \n"; + } + } + } return $sHtml; } @@ -269,8 +284,6 @@ abstract class cmdbAbstractObject extends CMDBObject //public static function GetDisplaySet(web_page $oPage, CMDBObjectSet $oSet, $sLinkageAttribute = '', $bDisplayMenu = true, $bSelectMode = false) public static function GetDisplaySet(web_page $oPage, CMDBObjectSet $oSet, $aExtraParams = array()) { - global $g_oConfig; - static $iListId = 0; $iListId++; @@ -347,9 +360,9 @@ abstract class cmdbAbstractObject extends CMDBObject $iMaxObjects = -1; if ($bDisplayLimit) { - if ($oSet->Count() > $g_oConfig->GetMaxDisplayLimit()) + if ($oSet->Count() > utils::GetConfig()->GetMaxDisplayLimit()) { - $iMaxObjects = $g_oConfig->GetMinDisplayLimit(); + $iMaxObjects = utils::GetConfig()->GetMinDisplayLimit(); } } while (($oObj = $oSet->Fetch()) && ($iMaxObjects != 0)) @@ -379,14 +392,14 @@ abstract class cmdbAbstractObject extends CMDBObject //$aMenuExtraParams['linkage'] = $sLinkageAttribute; $aMenuExtraParams = $aExtraParams; } - if ($bDisplayLimit && ($oSet->Count() > $g_oConfig->GetMaxDisplayLimit())) + if ($bDisplayLimit && ($oSet->Count() > utils::GetConfig()->GetMaxDisplayLimit())) { // list truncated $divId = $aExtraParams['block_id']; $sFilter = $oSet->GetFilter()->serialize(); $aExtraParams['display_limit'] = false; // To expand the full list $sExtraParams = addslashes(str_replace('"', "'", json_encode($aExtraParams))); // JSON encode, change the style of the quotes and escape them - $sHtml .= ''.$g_oConfig->GetMinDisplayLimit().' object(s) displayed out of '.$oSet->Count().'  
    Display All'; + $sHtml .= ''.utils::GetConfig()->GetMinDisplayLimit().' object(s) displayed out of '.$oSet->Count().'  Display All'; $oPage->add_ready_script("$('#{$divId} table.listResults').addClass('truncated');"); $oPage->add_ready_script("$('#{$divId} table.listResults tr:last td').addClass('truncated');"); } @@ -434,14 +447,7 @@ abstract class cmdbAbstractObject extends CMDBObject $aRow[] = $oObj->GetKey(); foreach($aList as $sAttCode) { - if (strstr($oObj->Get($sAttCode), $sSeparator)) // Escape the text only when it contains the separator - { - $aRow[] = $sTextQualifier.$oObj->Get($sAttCode).$sTextQualifier; - } - else - { - $aRow[] = $oObj->Get($sAttCode); - } + $aRow[] = $oObj->GetAsCSV($sAttCode, $sSeparator, '\\'); } $sHtml .= implode($sSeparator, $aRow)."\n"; } @@ -674,6 +680,20 @@ abstract class cmdbAbstractObject extends CMDBObject $sHTMLValue = $oWidget->Display($oPage, $value); break; + case 'Document': + $oDocument = $value; // Value is an ormDocument object + $sFileName = ''; + if (is_object($oDocument)) + { + $sFileName = $oDocument->GetFileName(); + } + $iMaxFileSize = utils::ConvertToBytes(ini_get('upload_max_filesize')); + $sHTMLValue = "\n"; + $sHTMLValue .= "\n"; + $sHTMLValue .= "$sFileName
    \n"; + $sHTMLValue .= "\n"; + break; + case 'String': default: // #@# todo - add context information (depending on dimensions) @@ -728,7 +748,7 @@ abstract class cmdbAbstractObject extends CMDBObject $sStateAttCode = MetaModel::GetStateAttributeCode(get_class($this)); $iKey = $this->GetKey(); $aDetails = array(); - $oPage->add("
    \n"); + $oPage->add("\n"); foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef) { if ('finalclass' == $sAttCode) // finalclass is a reserved word, hardcoded ! diff --git a/application/startup.inc.php b/application/startup.inc.php index 89b091117..97969a34b 100644 --- a/application/startup.inc.php +++ b/application/startup.inc.php @@ -2,8 +2,6 @@ require_once('../application/utils.inc.php'); -$g_oConfig = new Config('../config-itop.php'); - -MetaModel::Startup('../config-itop.php'); +MetaModel::Startup(ITOP_CONFIG_FILE); ?> diff --git a/application/uiwizard.class.inc.php b/application/uiwizard.class.inc.php index 307bdb50a..1ae24cabe 100644 --- a/application/uiwizard.class.inc.php +++ b/application/uiwizard.class.inc.php @@ -27,6 +27,10 @@ class UIWizard */ public function DisplayWizardStep($aStep, $iStepIndex, &$iMaxInputId, &$aFieldsMap, $bFinishEnabled = false) { + if ($iStepIndex == 1) // one big form that contains everything, to make sure that the uploaded files are posted too + { + $this->m_oPage->add("\n"); + } $this->m_oPage->add("
    \n"); $this->m_oPage->add("\n"); $aStates = MetaModel::EnumStates($this->m_sClass); @@ -34,7 +38,7 @@ class UIWizard $sJSHandlerCode = ''; // Javascript code to be executed each time this step of the wizard is entered foreach($aStep as $sAttCode) { - if ($sAttCode != 'finalclass') // Do not displa the attribute that stores the actual class name + if ($sAttCode != 'finalclass') // Do not display the attribute that stores the actual class name { $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode); $sAttLabel = $oAttDef->GetLabel(); @@ -107,7 +111,6 @@ $sJSHandlerCode $this->m_oPage->add("\n"); + $this->m_oPage->add("\n"); } /** * Compute the order of the fields & pages in the wizard diff --git a/application/utils.inc.php b/application/utils.inc.php index 00aa4c8d3..3330714fb 100644 --- a/application/utils.inc.php +++ b/application/utils.inc.php @@ -1,10 +1,15 @@ 0)) + { + $sMimeType = $sType; + } + } + @finfo_close($finfo); + } + $oDocument = new ormDocument($doc_content, $sMimeType, $_FILES[$sName]['name']); + break; + + case UPLOAD_ERR_NO_FILE: + // no file to load, it's a normal case, just return an empty document + break; + + case UPLOAD_ERR_FORM_SIZE: + case UPLOAD_ERR_INI_SIZE: + throw new FileUploadException("Uploaded file is too big. (Max allowed size is ".ini_get('upload_max_filesize').". Check you PHP configuration for upload_max_filesize."); + break; + + case UPLOAD_ERR_PARTIAL: + throw new FileUploadException("File upload failed, file has been truncated."); + break; + + case UPLOAD_ERR_NO_TMP_DIR: + throw new FileUploadException("Missing a temporary folder."); + break; + + case UPLOAD_ERR_CANT_WRITE: + throw new FileUploadException("Unable to write the temporary file to the disk (upload_tmp_dir = ".ini_get('upload_tmp_dir').")"); + break; + + case UPLOAD_ERR_EXTENSION: + throw new FileUploadException("File upload stopped by extension. (Original file name: ".$_FILES[$sName]['name'].")"); + break; + + default: + throw new FileUploadException("File upload failed, unknown cause (Error code = ".$_FILES[$sName]['error'].")"); + break; + + } + } + return $oDocument; + } + public static function GetNewTransactionId() { // TO DO implement the real mechanism here @@ -34,47 +108,44 @@ class utils return file_get_contents($sFileName); } - public static function ReadConfig() + /** + * Get access to the application config file + * @param none + * @return Config The Config object initialized from the application config file + */ + public static function GetConfig() { - self::$m_aConfig = array(); - - $sConfigContents = self::ReadFromFile(CONFIGFILE); - if (!$sConfigContents) throw new Exception("Could not load file ".CONFIGFILE); - - foreach (explode("\n", $sConfigContents) as $sLine) + if (self::$m_oConfig == null) { - $sLine = trim($sLine); - if (($iPos = strpos($sLine, '#')) !== false) - { - // strip out the end of the line right after the # - $sLine = substr($sLine, 0, $iPos); - } - - $aMatches = array(); - if (preg_match("@(\\S+.*)=\s*(\S+.*)@", $sLine, $aMatches)) - { - $sParamName = trim($aMatches[1]); - $sParamValue = trim($aMatches[2]); - self::$m_aConfig[$sParamName] = $sParamValue; - } + self::$m_oConfig = new Config(ITOP_CONFIG_FILE); } + return self::$m_oConfig; } - - public static function GetConfig($sParamName, $defaultValue = "") + /** + * Helper function to convert a value expressed in a 'user friendly format' + * as in php.ini, e.g. 256k, 2M, 1G etc. Into a number of bytes + * @param mixed $value The value as read from php.ini + * @return number + */ + public static function ConvertToBytes( $value ) { - if (is_null(self::$m_aConfig)) + $iReturn = $value; + if ( !is_numeric( $value ) ) { - self::ReadConfig(); - } - - if (array_key_exists($sParamName, self::$m_aConfig)) - { - return self::$m_aConfig[$sParamName]; - } - else - { - return $defaultValue; - } - } + $iLength = strlen( $value ); + $iReturn = substr( $value, 0, $iLength - 1 ); + $sUnit = strtoupper( substr( $value, $iLength - 1 ) ); + switch ( $sUnit ) + { + case 'G': + $iReturn *= 1024; + case 'M': + $iReturn *= 1024; + case 'K': + $iReturn *= 1024; + } + } + return $iReturn; + } } ?> diff --git a/application/wizardhelper.class.inc.php b/application/wizardhelper.class.inc.php index 578932e18..656b74589 100644 --- a/application/wizardhelper.class.inc.php +++ b/application/wizardhelper.class.inc.php @@ -8,8 +8,12 @@ class WizardHelper public function __construct() { } - - public function GetTargetObject() + /** + * Constructs the PHP target object from the parameters sent to the web page by the wizard + * @param boolean $bReadUploadedFiles True to also ready any uploaded file (for blob/document fields) + * @return object + */ + public function GetTargetObject($bReadUploadedFiles = false) { $oObj = MetaModel::NewObject($this->m_aData['m_sClass']); foreach($this->m_aData['m_aCurrentValues'] as $iIndex => $value) @@ -60,6 +64,20 @@ class WizardHelper $oSet = DBObjectSet::FromArray($sLinkedClass, $aLinkedObjectsArray); $oObj->Set($sAttCode, $oSet); } + else if ( $oAttDef->GetEditClass() == 'Document' ) + { + if ($bReadUploadedFiles) + { + $oDocument = utils::ReadPostedDocument('file_'.$sAttCode); + $oObj->Set($sAttCode, $oDocument); + } + else + { + // Create a new empty document, just for displaying the file name + $oDocument = new ormDocument(null, '', $value); + $oObj->Set($sAttCode, $oDocument); + } + } else if (($oAttDef->IsExternalKey()) && (!empty($value)) ) { // For external keys: load the target object so that external fields diff --git a/business/itop.business.class.inc.php b/business/itop.business.class.inc.php index a863167f4..1ce270925 100644 --- a/business/itop.business.class.inc.php +++ b/business/itop.business.class.inc.php @@ -406,7 +406,7 @@ class bizDocument extends logRealObject MetaModel::Init_AddFilterFromAttribute("description"); MetaModel::Init_SetZListItems('details', array('name', 'status', 'org_id', 'scope', 'description', 'contents')); // Attributes to be displayed for the complete details - MetaModel::Init_SetZListItems('list', array('name', 'status', 'org_id', 'scope')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('list', array('name', 'status', 'org_id', 'scope', 'contents')); // Attributes to be displayed for a list // Search criteria MetaModel::Init_SetZListItems('standard_search', array('name', 'status', 'scope')); // Criteria of the std search form MetaModel::Init_SetZListItems('advanced_search', array('name', 'status', 'scope')); // Criteria of the advanced search form @@ -415,55 +415,6 @@ class bizDocument extends logRealObject } -//////////////////////////////////////////////////////////////////////////////////// -/** -* A version of an electronic document -*/ -//////////////////////////////////////////////////////////////////////////////////// -class bizDocVersion extends cmdbAbstractObject -{ - public static function Init() - { - global $oAllowedStatuses; - $aParams = array - ( - "category" => "bizmodel,searchable", - "name" => "DocumentVersion", - "description" => "A version of a document", - "key_type" => "autoincrement", - "key_label" => "id", - "name_attcode" => "version_number", - "state_attcode" => "", - "reconc_keys" => array("document", "version_number"), - "db_table" => "document_versions", - "db_key_field" => "id", - "db_finalclass_field" => "", - "display_template" => "../business/templates/default.html", - ); - MetaModel::Init_Params($aParams); - MetaModel::Init_AddAttribute(new AttributeExternalKey("document", array("targetclass"=>"bizDocument", "label"=>"Document", "description"=>"The main document", "allowed_values"=>null, "sql"=>"document_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeExternalField("docname", array("label"=>"Document Name", "description"=>"name of the document", "allowed_values"=>null, "extkey_attcode"=> 'document', "target_attcode"=>"name"))); - MetaModel::Init_AddAttribute(new AttributeString("version_number", array("label"=>"Version", "description"=>"Version identifier", "allowed_values"=>null, "sql"=>"version_number", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeEnum("status", array("label"=>"Status", "description"=>"Status", "allowed_values"=>$oAllowedStatuses, "sql"=>"status", "default_value"=>"implementation", "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeEnum("type", array("label"=>"Type", "description"=>"Type", "allowed_values"=>new ValueSetEnum("local,draft"), "sql"=>"type", "default_value"=>"local", "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeURL("url", array("label"=>"URL", "description"=>"Hyperlink to the version", "allowed_values"=>null, "target"=>"_blank", "sql"=>"url", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeString("description", array("label"=>"Description", "description"=>"Service Description", "allowed_values"=>null, "sql"=>"description", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); - - MetaModel::Init_AddFilterFromAttribute("document"); - MetaModel::Init_AddFilterFromAttribute("docname"); - MetaModel::Init_AddFilterFromAttribute("version_number"); - MetaModel::Init_AddFilterFromAttribute("status"); - MetaModel::Init_AddFilterFromAttribute("type"); - MetaModel::Init_AddFilterFromAttribute("description"); - - MetaModel::Init_SetZListItems('details', array('document', 'status', 'version_number', 'type','url','description')); // Attributes to be displayed for the complete details - MetaModel::Init_SetZListItems('list', array('version_number', 'status', 'type', 'url')); // Attributes to be displayed for a list - // Search criteria - MetaModel::Init_SetZListItems('standard_search', array('docname', 'type')); // Criteria of the std search form - MetaModel::Init_SetZListItems('advanced_search', array('docname', 'type')); // Criteria o - } -} - //////////////////////////////////////////////////////////////////////////////////// /** * n-n link between any Object and a Document diff --git a/business/templates/document.html b/business/templates/document.html index 125c2b00e..a0c5a8322 100644 --- a/business/templates/document.html +++ b/business/templates/document.html @@ -5,9 +5,3 @@
    SELECT $class$ WHERE id = $pkey$ - - - SELECT bizDocVersion WHERE document = $pkey$ - - - diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php index df2db249d..6f920f7bc 100644 --- a/core/attributedef.class.inc.php +++ b/core/attributedef.class.inc.php @@ -1233,7 +1233,20 @@ class AttributeBlob extends AttributeDefinition public function GetAsHTML($value) { - return $value->GetAsHTML(); + if (is_object($value)) + { + return $value->GetAsHTML(); + } + } + + public function GetAsCSV($sValue, $sSeparator = ';', $sSepEscape = ',') + { + return ''; // Not exportable in CSV ! + } + + public function GetAsXML($value) + { + return ''; // Not exportable in XML, or as CDATA + some subtags ?? } } diff --git a/core/cmdbchangeop.class.inc.php b/core/cmdbchangeop.class.inc.php index 5c4fc1a07..ebbfc173b 100644 --- a/core/cmdbchangeop.class.inc.php +++ b/core/cmdbchangeop.class.inc.php @@ -347,7 +347,11 @@ class CMDBChangeOpSetAttributeBlob extends CMDBChangeOpSetAttribute $sAttName = $oAttDef->GetLabel(); $oPrevDoc = $this->Get('prevdata'); $sDocView = $oPrevDoc->GetAsHtml(); - $sResult = "$sAttName changed (previous value: $sDocView)"; + $sDocView .= "
    Open in New Window: ".$oPrevDoc->GetDisplayLink(get_class($this), $this->GetKey(), 'prevdata').", \n"; + $sDocView .= "Download: ".$oPrevDoc->GetDownloadLink(get_class($this), $this->GetKey(), 'prevdata')."\n"; + + //$sDocView = $oPrevDoc->GetDisplayInline(get_class($this), $this->GetKey(), 'prevdata'); + $sResult = "$sAttName changed, previous value: $sDocView"; } return $sResult; } diff --git a/core/cmdbsource.class.inc.php b/core/cmdbsource.class.inc.php index 3474033fa..b68474d3e 100644 --- a/core/cmdbsource.class.inc.php +++ b/core/cmdbsource.class.inc.php @@ -428,6 +428,23 @@ class CMDBSource mysql_free_result($result); return $aRows; } + + /** + * Returns the value of the specified server variable + * @param string $sVarName Name of the server variable + * @return mixed Current value of the variable + */ + public static function GetServerVariable($sVarName) + { + $result = ''; + $sSql = "SELECT @@$sVarName as theVar"; + $aRows = self::QueryToArray($sSql); + if (count($aRows) > 0) + { + $result = $aRows[0]['theVar']; + } + return $result; + } } diff --git a/core/ormdocument.class.inc.php b/core/ormdocument.class.inc.php index 54b8cecb7..4ffb3ace4 100644 --- a/core/ormdocument.class.inc.php +++ b/core/ormdocument.class.inc.php @@ -33,6 +33,11 @@ class ormDocument return MyHelpers::beautifulstr($this->m_data, 100, true); } + public function IsEmpty() + { + return ($this->m_data == null); + } + public function GetMimeType() { return $this->m_sMimeType; @@ -59,20 +64,72 @@ class ormDocument public function GetAsHTML() { - $data = $this->GetData(); - + $sResult = ''; + if ($this->IsEmpty()) + { + // If the filename is not empty, display it, this is used + // by the creation wizard while the file has not yet been uploaded + $sResult = $this->GetFileName(); + } + else + { + $data = $this->GetData(); + $sResult = $this->GetFileName().' [ '.$this->GetMimeType().', size: '.strlen($data).' byte(s) ]'; + } + return $sResult; + } + + /** + * Returns an HTML fragment that will display the document *inline* (if possible) + * @return string + */ + public function GetDisplayInline($sClass, $Id, $sAttCode) + { switch ($this->GetMainMimeType()) { - case 'text': - return "
    ".htmlentities(MyHelpers::beautifulstr($data, 1000, true))."
    \n"; + case 'text': + case 'html': + $data = $this->GetData(); + switch($this->GetMimeType()) + { + case 'text/html': + case 'text/xml': + return "\n"; + + default: + return "
    ".htmlentities(MyHelpers::beautifulstr($data, 1000, true))."
    \n"; + } + break; // Not really needed, but... - case 'application': - return "binary data for ".$this->GetMimeType().', size: '.strlen($data).' byte(s).'; - - case 'html': - default: - return "
    ".htmlentities(MyHelpers::beautifulstr($data, 1000, true))."
    \n"; + case 'application': + switch($this->GetMimeType()) + { + case 'application/pdf': + return "\n"; + } + break; + + case 'image': + return "\n"; } } + + /** + * Returns an hyperlink to display the document *inline* + * @return string + */ + public function GetDisplayLink($sClass, $Id, $sAttCode) + { + return "
    ".$this->GetFileName()."\n"; + } + + /** + * Returns an hyperlink to download the document (content-disposition: attachment) + * @return string + */ + public function GetDownloadLink($sClass, $Id, $sAttCode) + { + return "".$this->GetFileName()."\n"; + } } ?> diff --git a/js/utils.js b/js/utils.js index 9cdf7f9fd..7efa4a208 100644 --- a/js/utils.js +++ b/js/utils.js @@ -17,3 +17,15 @@ function ReloadTruncatedList(divId, sSerializedFilter, sExtraParams) } ); } + +/** + * Update the display and value of a file input widget when the user picks a new file + */ +function UpdateFileName(id, sNewFileName) +{ + var aPath = sNewFileName.split('\\'); + var sNewFileName = aPath[aPath.length-1]; + + $('#'+id).val(sNewFileName); + $('#name_'+id).text(sNewFileName); +} diff --git a/js/wizardhelper.js b/js/wizardhelper.js index c42beded4..9e8831ee9 100644 --- a/js/wizardhelper.js +++ b/js/wizardhelper.js @@ -18,6 +18,7 @@ function WizardHelper(sClass) this.m_oData.m_oFieldsMap = oFieldsMap; } + this.SetFieldsCount = function (count) { this.m_oData.m_iFieldsCount = count; diff --git a/pages/UI.php b/pages/UI.php index aeaf59fde..ba8dc8969 100644 --- a/pages/UI.php +++ b/pages/UI.php @@ -459,6 +459,17 @@ switch($operation) $bObjectModified = true; } } + else if ($oAttDef->GetEditClass() == 'Document') + { + // There should be an uploaded file with the named attr_ + $oDocument = utils::ReadPostedDocument('file_'.$sAttCode); + if (!$oDocument->IsEmpty()) + { + // A new file has been uploaded + $oObj->Set($sAttCode, $oDocument); + $bObjectModified = true; + } + } else if (!$oAttDef->IsExternalField()) { $rawValue = utils::ReadPostedParam("attr_$sAttCode", null); @@ -826,7 +837,7 @@ switch($operation) } else { - $oObj = $oWizardHelper->GetTargetObject(); + $oObj = $oWizardHelper->GetTargetObject(true /* read uploaded files */); if (is_object($oObj)) { $sClass = get_class($oObj); diff --git a/pages/ajax.render.php b/pages/ajax.render.php index 682e3efca..0c52e4d15 100644 --- a/pages/ajax.render.php +++ b/pages/ajax.render.php @@ -269,9 +269,60 @@ switch($operation) $oPage->add(''); } break; + + case 'display_document': + $id = utils::ReadParam('id', ''); + $sField = utils::ReadParam('field', ''); + if (!empty($sClass) && !empty($id) && !empty($sField)) + { + DownloadDocument($oPage, $oContext, $sClass, $id, $sField, 'inline'); + } + break; + + case 'download_document': + $id = utils::ReadParam('id', ''); + $sField = utils::ReadParam('field', ''); + if (!empty($sClass) && !empty($id) && !empty($sField)) + { + DownloadDocument($oPage, $oContext, $sClass, $id, $sField, 'attachement'); + } + break; default: $oPage->p("Invalid query."); } $oPage->output(); + +/** + * Downloads a document to the browser, either as 'inline' or 'attachment' + * + * @param web_page $oPage The web page for the output + * @param UserContext $oContext The current User/security context to retreive the objects + * @param string $sClass Class name of the object + * @param mixed $id Identifier of the object + * @param string $sAttCode Name of the attribute containing the document to download + * @param string $sContentDisposition Either 'inline' or 'attachment' + * @return none + */ +function DownloadDocument(web_page $oPage, UserContext $oContext, $sClass, $id, $sAttCode, $sContentDisposition = 'attachement') +{ + try + { + $oObj = $oContext->GetObject($sClass, $id); + if (is_object($oObj)) + { + $oDocument = $oObj->Get($sAttCode); + if (is_object($oDocument)) + { + $oPage->add_header('Content-type: '.$oDocument->GetMimeType()); + $oPage->add_header('Content-Disposition: '.$sContentDisposition.'; filename="'.$oDocument->GetFileName().'"'); + $oPage->add($oDocument->GetData()); + } + } + } + catch(Exception $e) + { + $oPage->p($e->getMessage()); + } +} ?> diff --git a/setup/index.php b/setup/index.php index 91fd34717..7cecf8f2d 100644 --- a/setup/index.php +++ b/setup/index.php @@ -23,6 +23,7 @@ $oP = new setup_web_page('iTop configuration wizard'); */ function CheckPHPVersion(setup_web_page $oP) { + $bResult = true; $oP->log('Info - CheckPHPVersion'); if (version_compare(phpversion(), PHP_MIN_VERSION, '>=')) { @@ -57,9 +58,38 @@ function CheckPHPVersion(setup_web_page $oP) if (count($aMissingExtensions) > 0) { $oP->error("Missing PHP extension(s): ".implode(', ', $aMissingExtensionsLinks)."."); - return false; + $bResult = false; } - return true; + // Check some ini settings here + if (!ini_get('file_uploads')) + { + $oP->error("Files upload is not allowed on this server (file_uploads = ".ini_get('file_uploads').")."); + $bResult = false; + } + + $sUploadTmpDir = ini_get('upload_tmp_dir'); + if (empty($sUploadTmpDir)) + { + $oP->error("Temporary directory for files upload is not defined (upload_tmp_dir)"); + $bResult = false; + } + + if (!ini_get('upload_max_filesize')) + { + $oP->error("File upload is not allowed on this server (file_uploads = ".ini_get('file_uploads').")."); + } + + $iMaxFileUploads = ini_get('max_file_uploads'); + if (!empty($iMaxFileUploads) && ($iMaxFileUploads < 1)) + { + $oP->error("File upload is not allowed on this server (max_file_uploads = ".ini_get('max_file_uploads').")."); + $bResult = false; + } + $oP->log("Info - upload_max_filesize: ".ini_get('upload_max_filesize')); + $oP->log("Info - upload_tmp_dir: $sUploadTmpDir"); + $oP->log("Info - max_file_uploads: ".ini_get('max_file_uploads')); + + return $bResult; } /** @@ -80,6 +110,26 @@ function CheckServerConnection(setup_web_page $oP, $sDBServer, $sDBUser, $sDBPwd if (version_compare($sDBVersion, MYSQL_MIN_VERSION, '>=')) { $oP->ok("Current MySQL version ($sDBVersion), greater than minimum required version (".MYSQL_MIN_VERSION.")"); + // Check some server variables + $iMaxAllowedPacket = $oDBSource->GetServerVariable('max_allowed_packet'); + $iMaxUploadSize = utils::ConvertToBytes(ini_get('upload_max_filesize')); + if ($iMaxAllowedPacket >= (500 + $iMaxUploadSize)) // Allow some space for the query + the file to upload + { + $oP->ok("MySQL server's max_allowed_packet is big enough."); + } + else if($iMaxAllowedPacket < $iMaxUploadSize) + { + $oP->warning("MySQL server's max_allowed_packet ($iMaxAllowedPacket) is not big enough."); + $oP->warning("Consider setting it to at least ".(500 + $iMaxUploadSize)."."); + } + $oP->log("Info - MySQL max_allowed_packet: $iMaxAllowedPacket"); + $iMaxConnections = $oDBSource->GetServerVariable('max_connections'); + if ($iMaxConnections < 5) + { + $oP->warning("MySQL server's max_connections ($iMaxConnections) is not enough."); + $oP->warning("Consider setting it to at least 5."); + } + $oP->log("Info - MySQL max_connections: ".($oDBSource->GetServerVariable('max_connections'))); } else { From 015f44784d1306a64d92995aec0f97b04e4f0bc1 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Thu, 31 Dec 2009 09:36:57 +0000 Subject: [PATCH 144/227] - Check that the temporary directory used by files upload exists and is writable from PHP. (As required in Trac #15). SVN:trunk[221] --- setup/index.php | 20 +++++++++++++++++++- 1 file changed, 19 insertions(+), 1 deletion(-) diff --git a/setup/index.php b/setup/index.php index 7cecf8f2d..8d429361a 100644 --- a/setup/index.php +++ b/setup/index.php @@ -73,6 +73,25 @@ function CheckPHPVersion(setup_web_page $oP) $oP->error("Temporary directory for files upload is not defined (upload_tmp_dir)"); $bResult = false; } + // check that the upload directory is indeed writable from PHP + if (!empty($sUploadTmpDir)) + { + if (!file_exists($sUploadTmpDir)) + { + $oP->error("Temporary directory for files upload ($sUploadTmpDir) does not exist or cannot be read by PHP."); + $bResult = false; + } + else if (!is_writable($sUploadTmpDir)) + { + $oP->error("Temporary directory for files upload ($sUploadTmpDir) is not writable."); + $bResult = false; + } + else + { + $oP->log("Info - Temporary directory for files upload ($sUploadTmpDir) is writable."); + } + } + if (!ini_get('upload_max_filesize')) { @@ -86,7 +105,6 @@ function CheckPHPVersion(setup_web_page $oP) $bResult = false; } $oP->log("Info - upload_max_filesize: ".ini_get('upload_max_filesize')); - $oP->log("Info - upload_tmp_dir: $sUploadTmpDir"); $oP->log("Info - max_file_uploads: ".ini_get('max_file_uploads')); return $bResult; From 95b1ce9030946cc3952dada4f2286cebb98fc273 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Sun, 3 Jan 2010 13:39:56 +0000 Subject: [PATCH 145/227] - Fix the dialog to manage n-n links: don't display again objects that are already linked. - Now use OQL instead of SibusQL SVN:trunk[222] --- js/wizard.utils.js | 4 ++-- pages/ajax.render.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/js/wizard.utils.js b/js/wizard.utils.js index fcb873863..02a7d00c4 100644 --- a/js/wizard.utils.js +++ b/js/wizard.utils.js @@ -34,8 +34,8 @@ function ManageObjects(sTitle, sClass, sId, sExtKeyToRemote) { aObjList[0] = 0; } - Manage_LoadSelect('selected_objects_'+sId, sClass+': pkey IN {' + aObjList.join(', ') + '}'); - Manage_LoadSelect('available_objects_'+sId, sClass); + Manage_LoadSelect('selected_objects_'+sId, 'SELECT '+sClass+' WHERE id IN (' + aObjList.join(', ') + ')'); // id is a reserved keyword always representing the primary key + Manage_LoadSelect('available_objects_'+sId, 'SELECT '+sClass+' WHERE id NOT IN (' + aObjList.join(', ') + ')'); $('#ManageObjectsDlg_'+sId).jqmShow(); } diff --git a/pages/ajax.render.php b/pages/ajax.render.php index 0c52e4d15..3ac20e6a9 100644 --- a/pages/ajax.render.php +++ b/pages/ajax.render.php @@ -262,7 +262,7 @@ switch($operation) break; case 'combo_options': - $oFilter = CMDBSearchFilter::FromSibusQL($sFilter); + $oFilter = CMDBSearchFilter::FromOQL($sFilter); $oSet = new CMDBObjectSet($oFilter); while( $oObj = $oSet->fetch()) { From 905b32eac43b4cac935c4a2dca3e12ce92029c91 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Sun, 3 Jan 2010 19:14:10 +0000 Subject: [PATCH 146/227] - First (partial) implementation of context and default values. SVN:trunk[223] --- application/applicationcontext.class.inc.php | 30 ++++++++++++++++---- application/cmdbabstract.class.inc.php | 16 +++++++++-- application/displayblock.class.inc.php | 12 ++++++-- application/uiwizard.class.inc.php | 6 ++-- pages/UI.php | 8 +++--- 5 files changed, 54 insertions(+), 18 deletions(-) diff --git a/application/applicationcontext.class.inc.php b/application/applicationcontext.class.inc.php index e3422e02e..e23ddd390 100644 --- a/application/applicationcontext.class.inc.php +++ b/application/applicationcontext.class.inc.php @@ -14,6 +14,7 @@ class ApplicationContext { protected $aNames; protected $aValues; + protected static $aDefaultValues; // Cache shared among all instances public function __construct() { @@ -29,16 +30,20 @@ class ApplicationContext */ protected function ReadContext() { - $this->aValues = array(); - foreach($this->aNames as $sName) + if (empty(self::$aDefaultValues)) { - $sValue = utils::ReadParam($sName, ''); - // TO DO: check if some of the context parameters are mandatory (or have default values) - if (!empty($sValue)) + self::$aDefaultValues = array(); + foreach($this->aNames as $sName) { - $this->aValues[$sName] = $sValue; + $sValue = utils::ReadParam($sName, ''); + // TO DO: check if some of the context parameters are mandatory (or have default values) + if (!empty($sValue)) + { + self::$aDefaultValues[$sName] = $sValue; + } } } + $this->aValues = self::$aDefaultValues; } /** @@ -77,5 +82,18 @@ class ApplicationContext { return $this->aValues; } + /** + * Removes the specified parameter from the context, for example when the same parameter + * is already a search parameter + * @param string $sParamName Name of the parameter to remove + * @return none + */ + public function Reset($sParamName) + { + if (isset($this->aValues[$sParamName])) + { + unset($this->aValues[$sParamName]); + } + } } ?> diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index ccd5c9e55..dd4782afc 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -507,12 +507,13 @@ abstract class cmdbAbstractObject extends CMDBObject public static function GetSearchForm(web_page $oPage, CMDBObjectSet $oSet, $aExtraParams = array()) { static $iSearchFormId = 0; + $oAppContext = new ApplicationContext(); $sHtml = ''; $numCols=4; $iSearchFormId++; $sClassName = $oSet->GetFilter()->GetClass(); - // Romain: temporariy removed the tab "OQL query" because it was not finalized + // Romain: temporarily removed the tab "OQL query" because it was not finalized // (especially when used to add a link) /* $sHtml .= "
      @@ -536,6 +537,7 @@ abstract class cmdbAbstractObject extends CMDBObject $aList = MetaModel::GetZListItems($sClassName, 'standard_search'); foreach($aList as $sFilterCode) { + $oAppContext->Reset($sFilterCode); // Make sure the same parameter will not be passed twice if (($index % $numCols) == 0) { if ($index != 0) @@ -608,6 +610,7 @@ abstract class cmdbAbstractObject extends CMDBObject $sHtml .= "\n"; $sHtml .= "\n"; $sHtml .= "\n"; + $sHtml .= $oAppContext->GetForForm(); $sHtml .= "\n"; $sHtml .= "
    \n"; @@ -634,6 +637,7 @@ abstract class cmdbAbstractObject extends CMDBObject $sHtml .= "\n"; } $sHtml .= "\n"; + $sHtml .= $oAppContext->GetForForm(); $sHtml .= "\n"; $sHtml .= "\n"; return $sHtml; @@ -642,6 +646,12 @@ abstract class cmdbAbstractObject extends CMDBObject public static function GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $value = '', $sDisplayValue = '', $iId = '', $sNameSuffix = '', $iFlags = 0, $aArgs = array()) { static $iInputId = 0; + if (isset($aArgs[$sAttCode]) && empty($value)) + { + // default value passed by the context (either the app context of the operation) + $value = $aArgs[$sAttCode]; + } + if (!empty($iId)) { $iInputId = $iId; @@ -797,7 +807,7 @@ abstract class cmdbAbstractObject extends CMDBObject $oPage->add("\n"); } - public static function DisplayCreationForm(web_page $oPage, $sClass, $oObjectToClone = null) + public static function DisplayCreationForm(web_page $oPage, $sClass, $oObjectToClone = null, $aArgs = array()) { static $iCreationFormId = 0; @@ -835,7 +845,7 @@ abstract class cmdbAbstractObject extends CMDBObject $sDisplayValue = ($oObjectToClone == null) ? '' : $oObjectToClone->GetDisplayValue($sAttCode); $iOptions = isset($aStates[$sTargetState]['attribute_list'][$sAttCode]) ? $aStates[$sTargetState]['attribute_list'][$sAttCode] : 0; - $sHTMLValue = self::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $sValue, $sDisplayValue, '', '', $iOptions); + $sHTMLValue = self::GetFormElementForField($oPage, $sClass, $sAttCode, $oAttDef, $sValue, $sDisplayValue, '', '', $iOptions, $aArgs); $aDetails[] = array('label' => $oAttDef->GetLabel(), 'value' => $sHTMLValue); } } diff --git a/application/displayblock.class.inc.php b/application/displayblock.class.inc.php index c006bd91a..f744cf6c4 100644 --- a/application/displayblock.class.inc.php +++ b/application/displayblock.class.inc.php @@ -257,10 +257,12 @@ class DisplayBlock } $sFilter = urlencode($this->m_oFilter->serialize()); $aData = array(); + $oAppContext = new ApplicationContext(); + $sParams = $oAppContext->GetForLink(); foreach($aGroupBy as $sValue => $iCount) { $aData[] = array ( 'group' => $sValue, - 'value' => "$iCount"); // TO DO: add the context information + 'value' => "$iCount"); // TO DO: add the context information } $sHtml .= $oPage->GetTable(array('group' => array('label' => MetaModel::GetLabel($this->m_oFilter->GetClass(), $sGroupByField), 'description' => ''), 'value' => array('label'=>'Count', 'description' => 'Number of elements')), $aData); } @@ -287,7 +289,9 @@ class DisplayBlock { if (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES) { - $sHtml .= $oPage->GetP("Click here to create a new ".Metamodel::GetName($sClass)."\n"); + $oAppContext = new ApplicationContext(); + $sParams = $oAppContext->GetForLink(); + $sHtml .= $oPage->GetP("Click here to create a new ".Metamodel::GetName($sClass)."\n"); } } } @@ -312,7 +316,9 @@ class DisplayBlock { if (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES) { - $sHtml .= $oPage->GetP("Click here to add new ".Metamodel::GetName($sTargetClass)."s\n"); + $oAppContext = new ApplicationContext(); + $sParams = $oAppContext->GetForLink(); + $sHtml .= $oPage->GetP("Click here to add new ".Metamodel::GetName($sTargetClass)."s\n"); } } } diff --git a/application/uiwizard.class.inc.php b/application/uiwizard.class.inc.php index 1ae24cabe..9ee6c1d83 100644 --- a/application/uiwizard.class.inc.php +++ b/application/uiwizard.class.inc.php @@ -25,7 +25,7 @@ class UIWizard /** * Displays one step of the wizard */ - public function DisplayWizardStep($aStep, $iStepIndex, &$iMaxInputId, &$aFieldsMap, $bFinishEnabled = false) + public function DisplayWizardStep($aStep, $iStepIndex, &$iMaxInputId, &$aFieldsMap, $bFinishEnabled = false, $aArgs = array()) { if ($iStepIndex == 1) // one big form that contains everything, to make sure that the uploaded files are posted too { @@ -60,7 +60,7 @@ class UIWizard $sFieldFlag = ($iOptions & (OPT_ATT_MANDATORY | OPT_ATT_MUSTCHANGE)) ? ' *' : ''; $oDefaultValuesSet = $oAttDef->GetDefaultValue(); // @@@ TO DO: get the object's current value if the object exists - $sHTMLValue = cmdbAbstractObject::GetFormElementForField($this->m_oPage, $this->m_sClass, $sAttCode, $oAttDef, $oDefaultValuesSet, '', "att_$iMaxInputId", '', $iOptions); + $sHTMLValue = cmdbAbstractObject::GetFormElementForField($this->m_oPage, $this->m_sClass, $sAttCode, $oAttDef, $oDefaultValuesSet, '', "att_$iMaxInputId", '', $iOptions, $aArgs); $aFieldsMap[$iMaxInputId] = $sAttCode; $aDetails[] = array('label' => $oAttDef->GetLabel().$sFieldFlag, 'value' => "
    $sHTMLValue
    "); if ($oAttDef->GetValuesDef() != null) @@ -108,6 +108,7 @@ $sJSHandlerCode */ public function DisplayFinalStep($iStepIndex, $aFieldsMap) { + $oAppContext = new ApplicationContext(); $this->m_oPage->add("\n"); $this->m_oPage->add("\n"); diff --git a/pages/UI.php b/pages/UI.php index ba8dc8969..7478805d3 100644 --- a/pages/UI.php +++ b/pages/UI.php @@ -350,6 +350,8 @@ switch($operation) $oP->add_linked_script("../js/linkswidget.js"); $oP->add_linked_script("../js/jquery.blockUI.js"); $oWizard = new UIWizard($oP, $sClass, $sStateCode); + $oContext = new UserContext(); + $aArgs = array_merge($oAppContext->GetAsHash(), utils::ReadParam('default', array())); $sStateCode = $oWizard->GetTargetState(); // Will computes the default state if none was supplied $sClassLabel = MetaModel::GetName($sClass); $oP->add("

    Creation of a new $sClassLabel

    "); @@ -367,21 +369,19 @@ switch($operation) foreach($aWizardSteps['mandatory'] as $aSteps) { $oP->SetCurrentTab("Step $iStepIndex *"); - $oWizard->DisplayWizardStep($aSteps, $iStepIndex, $iMaxInputId, $aFieldsMap); + $oWizard->DisplayWizardStep($aSteps, $iStepIndex, $iMaxInputId, $aFieldsMap, false /* no finish button */, $aArgs); //$oP->add("\n"); $iStepIndex++; } foreach($aWizardSteps['optional'] as $aSteps) { $oP->SetCurrentTab("Step $iStepIndex *"); - $oWizard->DisplayWizardStep($aSteps, $iStepIndex, $iMaxInputId, $aFieldsMap, true); // true means enable the finish button + $oWizard->DisplayWizardStep($aSteps, $iStepIndex, $iMaxInputId, $aFieldsMap, true, $aArgs); // true means enable the finish button //$oP->add("\n"); $iStepIndex++; } $oWizard->DisplayFinalStep($iStepIndex, $aFieldsMap); - $oAppContext = new ApplicationContext(); - $oContext = new UserContext(); $oObj = null; if (!empty($id)) { From 644d936c2653f0cb0125692b4c8645fb73ed0abd Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Tue, 5 Jan 2010 09:05:12 +0000 Subject: [PATCH 147/227] #47 Replaced every occurence of deprecated verb split() by explode() SVN:trunk[224] --- core/csvparser.class.inc.php | 10 +++++----- core/data.generator.class.inc.php | 4 ++-- core/dbobjectsearch.class.php | 15 +++++++++++---- 3 files changed, 18 insertions(+), 11 deletions(-) diff --git a/core/csvparser.class.inc.php b/core/csvparser.class.inc.php index c1e49f10f..b6c6334a3 100644 --- a/core/csvparser.class.inc.php +++ b/core/csvparser.class.inc.php @@ -70,7 +70,7 @@ class CSVParser $aStatsBySeparator[$sSep] = array(); } - foreach(split("\n", $this->m_sCSVData) as $sLine) + foreach(explode("\n", $this->m_sCSVData) as $sLine) { $sLine = trim($sLine); if (substr($sLine, 0, 1) == '#') continue; @@ -106,13 +106,13 @@ class CSVParser // Take the FIRST -valuable- LINE ONLY // If there is a number, then for sure this is not a header line // Otherwise, we may consider that there is one line to skip - foreach(split("\n", $this->m_sCSVData) as $sLine) + foreach(explode("\n", $this->m_sCSVData) as $sLine) { $sLine = trim($sLine); if (substr($sLine, 0, 1) == '#') continue; if (empty($sLine)) continue; - foreach (split($this->m_sSep, $sLine) as $value) + foreach (explode($this->m_sSep, $sLine) as $value) { if (is_numeric($value)) { @@ -133,7 +133,7 @@ class CSVParser $iCount = 0; $iSkipped = 0; - foreach(split("\n", $this->m_sCSVData) as $sLine) + foreach(explode("\n", $this->m_sCSVData) as $sLine) { $sLine = trim($sLine); if (substr($sLine, 0, 1) == '#') continue; @@ -145,7 +145,7 @@ class CSVParser continue; } - foreach (split($this->m_sSep, $sLine) as $iCol=>$sValue) + foreach (explode($this->m_sSep, $sLine) as $iCol=>$sValue) { if (is_array($aFieldMap)) $sColRef = $aFieldMap[$iCol]; else $sColRef = $iCol; diff --git a/core/data.generator.class.inc.php b/core/data.generator.class.inc.php index f9d284e3e..03cb9db64 100644 --- a/core/data.generator.class.inc.php +++ b/core/data.generator.class.inc.php @@ -206,7 +206,7 @@ class cmdbDataGenerator function GenerateString($sTemplate) { $sResult = ""; - $aParts = split("\|", $sTemplate); + $aParts = explode("\|", $sTemplate); foreach($aParts as $sPart) { if (preg_match("/domain\(\)/", $sPart, $aMatches)) @@ -216,7 +216,7 @@ class cmdbDataGenerator elseif (preg_match("/enum\((.+)\)/", $sPart, $aMatches)) { $sEnumValues = $aMatches[1]; - $aEnumValues = split(",", $sEnumValues); + $aEnumValues = explode(",", $sEnumValues); $sResult .= $aEnumValues[rand(0, count($aEnumValues) - 1)]; } elseif (preg_match("/number\((\d+)-(\d+)\)/", $sPart, $aMatches)) diff --git a/core/dbobjectsearch.class.php b/core/dbobjectsearch.class.php index 8cf4ccf15..71dda9508 100644 --- a/core/dbobjectsearch.class.php +++ b/core/dbobjectsearch.class.php @@ -163,7 +163,14 @@ class DBObjectSearch public function __DescribeHTML() { - $sConditionDesc = $this->DescribeConditions(); + try + { + $sConditionDesc = $this->DescribeConditions(); + } + catch (MissingQueryArgument $e) + { + $sConditionDesc = '?missing query argument?'; + } if (!empty($sConditionDesc)) { return "Objects of class '$this->m_sClass', $sConditionDesc"; @@ -525,14 +532,14 @@ class DBObjectSearch // -> return (unserialize(base64_decode($sValue))); $sClearText = base64_decode($sValue); - $aValues = split("\n", $sClearText); + $aValues = explode("\n", $sClearText); $i = 0; $sClass = $aValues[$i++]; $sClassAlias = $aValues[$i++]; $oFilter = new DBObjectSearch($sClass, $sClassAlias); while($i < count($aValues) && !empty($aValues[$i])) { - $aCondition = split(":", $aValues[$i++]); + $aCondition = explode(":", $aValues[$i++]); switch ($aCondition[0]) { case "A": @@ -940,7 +947,7 @@ class DBObjectSearch { $sClass = trim(substr($sQuery, 0, $iSepPos)); $sConds = trim(substr($sQuery, $iSepPos + 1)); - $aValues = split(" AND ", $sConds); + $aValues = explode(" AND ", $sConds); $oFilter = new DBObjectSearch($sClass); From 5a15dcb8490d1a3ecc8c64a0bf66b723d7db28d3 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Wed, 6 Jan 2010 08:51:08 +0000 Subject: [PATCH 148/227] Trac #57 - Implemented beta version of email notifications (triggers and actions) SVN:trunk[225] --- application/template.class.inc.php | 17 +----- business/itop.business.class.inc.php | 2 +- core/attributedef.class.inc.php | 62 +++++++++++++++++++++- core/cmdbobject.class.inc.php | 10 ++++ core/config.class.inc.php | 8 +++ core/dbobject.class.php | 38 +++++++++++-- core/metamodel.class.php | 24 ++++++--- pages/schema.php | 13 +++++ setup/data/structure/1.menus.xml | 79 ++++++++++++++++++++++++++++ 9 files changed, 224 insertions(+), 29 deletions(-) diff --git a/application/template.class.inc.php b/application/template.class.inc.php index 6436dfc52..20d57620c 100644 --- a/application/template.class.inc.php +++ b/application/template.class.inc.php @@ -16,7 +16,7 @@ class DisplayTemplate public function Render(web_page $oPage, $aParams = array()) { - $this->ApplyParams($aParams); + $this->m_sTemplate = MetaModel::ApplyParams($this->m_sTemplate, $aParams); $iStart = 0; $iEnd = strlen($this->m_sTemplate); $iCount = 0; @@ -40,21 +40,6 @@ class DisplayTemplate $oPage->add(substr($this->m_sTemplate, $iAfterTagPos)); } - /** - * Replaces all the parameters by the values passed in the hash array - */ - public function ApplyParams($aParams) - { - $aSearches = array(); - $aReplacements = array(); - foreach($aParams as $sSearch => $sReplace) - { - $aSearches[] = '$'.$sSearch.'$'; - $aReplacements[] = $sReplace; - } - $this->m_sTemplate = str_replace($aSearches, $aReplacements, $this->m_sTemplate); - } - public function GetNextTag(&$iStartPos, &$iEndPos) { $iChunkStartPos = $iStartPos; diff --git a/business/itop.business.class.inc.php b/business/itop.business.class.inc.php index 1ce270925..ceda3e8e2 100644 --- a/business/itop.business.class.inc.php +++ b/business/itop.business.class.inc.php @@ -200,7 +200,7 @@ class bizContact extends logRealObject MetaModel::Init_Params($aParams); MetaModel::Init_InheritAttributes(); MetaModel::Init_AddAttribute(new AttributeExternalField("org_name", array("label"=>"Organization", "description"=>"Company / Department of the contact", "allowed_values"=>null, "extkey_attcode"=> 'org_id', "target_attcode"=>"name"))); - MetaModel::Init_AddAttribute(new AttributeString("email", array("label"=>"eMail", "description"=>"Email address", "allowed_values"=>null, "sql"=>"email", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEmailAddress("email", array("label"=>"eMail", "description"=>"Email address", "allowed_values"=>null, "sql"=>"email", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("phone", array("label"=>"Phone", "description"=>"Telephone", "allowed_values"=>null, "sql"=>"telephone", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalKey("location_id", array("targetclass"=>"bizLocation", "label"=>"Location", "description"=>"Id of the location where the contact is located", "allowed_values"=>null, "sql"=>"location_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("location_name", array("label"=>"Location Name", "description"=>"Name of the location where the contact is located", "allowed_values"=>null, "extkey_attcode"=> 'location_id', "target_attcode"=>"name"))); diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php index 6f920f7bc..c73479268 100644 --- a/core/attributedef.class.inc.php +++ b/core/attributedef.class.inc.php @@ -547,7 +547,7 @@ class AttributeString extends AttributeDBField } public function ScalarToSQL($value) { - if (!is_string($value)) + if (!is_string($value) && !is_null($value)) { throw new CoreWarning('Expected the attribute value to be a string', array('found_type' => gettype($value), 'value' => $value, 'class' => $this->GetCode(), 'attribute' => $this->GetHostClass())); } @@ -615,6 +615,66 @@ class AttributeText extends AttributeString } } +/** + * Specialization of a string: email + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class AttributeEmailAddress extends AttributeString +{ + public function GetTypeDesc() {return "Email address(es)";} +} + +/** + * Specialization of a string: OQL expression + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class AttributeOQL extends AttributeString +{ + public function GetTypeDesc() {return "OQL expression";} +} + +/** + * Specialization of a string: template + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class AttributeTemplateString extends AttributeString +{ + public function GetTypeDesc() {return "Template string";} +} + +/** + * Specialization of a text: template + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class AttributeTemplateText extends AttributeText +{ + public function GetTypeDesc() {return "Multiline template string";} +} + /** * Map a enum column to an attribute * diff --git a/core/cmdbobject.class.inc.php b/core/cmdbobject.class.inc.php index 79b448071..997f605d4 100644 --- a/core/cmdbobject.class.inc.php +++ b/core/cmdbobject.class.inc.php @@ -37,9 +37,19 @@ require_once('dbobject.class.php'); require_once('dbobjectsearch.class.php'); require_once('dbobjectset.class.php'); +// db change tracking data model require_once('cmdbchange.class.inc.php'); require_once('cmdbchangeop.class.inc.php'); +// customization data model +// Romain: temporary moved into application.inc.php (see explanations there) +//require_once('trigger.class.inc.php'); +//require_once('action.class.inc.php'); + +// application log +// Romain: temporary moved into application.inc.php (see explanations there) +//require_once('event.class.inc.php'); + require_once('csvparser.class.inc.php'); require_once('bulkchange.class.inc.php'); diff --git a/core/config.class.inc.php b/core/config.class.inc.php index 6d6e5e982..aa7aa5289 100644 --- a/core/config.class.inc.php +++ b/core/config.class.inc.php @@ -289,6 +289,14 @@ class Config fwrite($hFile, "\t'application' => array (\n"); fwrite($hFile, "\t\t'../application/menunode.class.inc.php',\n"); fwrite($hFile, "\t\t'../application/audit.rule.class.inc.php',\n"); +// Romain - That's dirty, because those 3 classes are in fact part of the core +// but I needed those classes to be derived from cmdbAbstractObject +// (to be managed via the GUI) and this class in not really known from +// the core, PLUS I needed the includes to be there also for the setup +// to create the tables. + fwrite($hFile, "\t\t'../core/event.class.inc.php',\n"); + fwrite($hFile, "\t\t'../core/action.class.inc.php',\n"); + fwrite($hFile, "\t\t'../core/trigger.class.inc.php',\n"); fwrite($hFile, "\t\t// to be continued...\n"); fwrite($hFile, "\t),\n"); fwrite($hFile, "\t'business' => array (\n"); diff --git a/core/dbobject.class.php b/core/dbobject.class.php index 45b02c0c1..d309ca4db 100644 --- a/core/dbobject.class.php +++ b/core/dbobject.class.php @@ -611,7 +611,7 @@ abstract class DBObject if (!empty($this->m_iKey) && ($this->m_iKey >= 0)) { // Add it to the list of fields to write - $aFieldsToWrite[] = MetaModel::DBGetKey($sTableClass); + $aFieldsToWrite[] = '`'.MetaModel::DBGetKey($sTableClass).'`'; $aValuesToWrite[] = CMDBSource::Quote($this->m_iKey); } @@ -622,7 +622,7 @@ abstract class DBObject $aAttColumns = $oAttDef->GetSQLValues($this->m_aCurrValues[$sAttCode]); foreach($aAttColumns as $sColumn => $sValue) { - $aFieldsToWrite[] = $sColumn; + $aFieldsToWrite[] = "`$sColumn`"; $aValuesToWrite[] = CMDBSource::Quote($sValue); } } @@ -803,7 +803,9 @@ abstract class DBObject // Change the state before proceeding to the actions, this is necessary because an action might // trigger another stimuli (alternative: push the stimuli into a queue) - $this->Set($sStateAttCode, $aTransitionDef['target_state']); + $sPreviousState = $this->Get($sStateAttCode); + $sNewState = $aTransitionDef['target_state']; + $this->Set($sStateAttCode, $sNewState); // $aTransitionDef is an // array('target_state'=>..., 'actions'=>array of handlers procs, 'user_restriction'=>TBD @@ -824,9 +826,39 @@ abstract class DBObject if (!$bRet) $bSuccess = false; } + // Change state triggers... + $sClass = get_class($this); + $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnStateLeave AS t WHERE t.target_class='$sClass' AND t.state='$sPreviousState'")); + while ($oTrigger = $oSet->Fetch()) + { + $oTrigger->DoActivate($this->ToArgs('this')); + } + + $oSet = new DBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnStateEnter AS t WHERE t.target_class='$sClass' AND t.state='$sNewState'")); + while ($oTrigger = $oSet->Fetch()) + { + $oTrigger->DoActivate($this->ToArgs('this')); + } + return $bSuccess; } + // Make standard context arguments + public function ToArgs($sArgName) + { + $aScalarArgs = array(); + $aScalarArgs[$sArgName] = $this->GetKey(); + $aScalarArgs[$sArgName.'->id'] = $this->GetKey(); + + $sClass = get_class($this); + foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) + { + $aScalarArgs[$sArgName.'->'.$sAttCode] = $this->Get($sAttCode); + } + return $aScalarArgs; + } + + // Return an empty set for the parent of all public static function GetRelationQueries($sRelCode) { diff --git a/core/metamodel.class.php b/core/metamodel.class.php index c40bc220b..c54e828f9 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -1222,14 +1222,7 @@ abstract class MetaModel { if (self::IsValidObject($value)) { - $aScalarArgs[$sArgName] = $value->GetKey(); - $aScalarArgs[$sArgName.'->id'] = $value->GetKey(); - - $sClass = get_class($value); - foreach(self::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) - { - $aScalarArgs[$sArgName.'->'.$sAttCode] = $value->Get($sAttCode); - } + $aScalarArgs = array_merge($aScalarArgs, $value->ToArgs($sArgName)); } else { @@ -2849,6 +2842,21 @@ abstract class MetaModel return self::GetLabel($sLinkClass, $sAttCode); } + /** + * Replaces all the parameters by the values passed in the hash array + */ + static public function ApplyParams($aInput, $aParams) + { + $aSearches = array(); + $aReplacements = array(); + foreach($aParams as $sSearch => $sReplace) + { + $aSearches[] = '$'.$sSearch.'$'; + $aReplacements[] = $sReplace; + } + return str_replace($aSearches, $aReplacements, $aInput); + } + } // class MetaModel diff --git a/pages/schema.php b/pages/schema.php index b7162f5f4..1162f1ecd 100644 --- a/pages/schema.php +++ b/pages/schema.php @@ -231,6 +231,16 @@ function DisplayLifecycle($oPage, $sClass) } +/** + * Helper for the trigger + */ +function DisplayTriggers($oPage, $sClass) +{ + $oSet = new CMDBObjectSet(DBObjectSearch::FromOQL("SELECT TriggerOnStateChange WHERE target_class = '$sClass'")); + cmdbAbstractObject::DisplaySet($oPage, $oSet); +} + + /** * Display the list of classes from the business model */ @@ -402,6 +412,9 @@ function DisplayClassDetails($oPage, $sClass) $oPage->SetCurrentTab('Lifecycle'); DisplayLifecycle($oPage, $sClass); + $oPage->SetCurrentTab('Triggers'); + DisplayTriggers($oPage, $sClass); + $oPage->SetCurrentTab(); $oPage->SetCurrentTabContainer(); } diff --git a/setup/data/structure/1.menus.xml b/setup/data/structure/1.menus.xml index df61e6cb1..7c78a33e0 100644 --- a/setup/data/structure/1.menus.xml +++ b/setup/data/structure/1.menus.xml @@ -918,4 +918,83 @@ text-align:left; 0 0 + +Customization + +UI.php + + +administrator +2 +1 +0 + + +Triggers - entering + +UI.php +../images/std_view.gif + +administrator +2 +500 +0 + + +Triggers - leaving + +UI.php +../images/std_view.gif + +administrator +2 +500 +0 + + +Actions + +UI.php +../images/std_view.gif + +administrator +2 +500 +0 + + +Application log + +UI.php +../images/std_view.gif + +administrator +2 +1 +0 + From 9440b599214b35610fab32d31e94df0cd25fdd2f Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Wed, 6 Jan 2010 12:57:40 +0000 Subject: [PATCH 149/227] Forgot to add 3 files in the previous commit SVN:trunk[226] --- core/action.class.inc.php | 238 +++++++++++++++++++++++++++++++++++++ core/event.class.inc.php | 94 +++++++++++++++ core/trigger.class.inc.php | 204 +++++++++++++++++++++++++++++++ 3 files changed, 536 insertions(+) create mode 100644 core/action.class.inc.php create mode 100644 core/event.class.inc.php create mode 100644 core/trigger.class.inc.php diff --git a/core/action.class.inc.php b/core/action.class.inc.php new file mode 100644 index 000000000..80bdb5861 --- /dev/null +++ b/core/action.class.inc.php @@ -0,0 +1,238 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ +abstract class Action extends cmdbAbstractObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "core/cmdb", + "name" => "action", + "description" => "Custom action", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "name", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_action", + "db_key_field" => "id", + "db_finalclass_field" => "realclass", + "display_template" => "", + ); + MetaModel::Init_Params($aParams); + //MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeString("name", array("label"=>"Name", "description"=>"label", "allowed_values"=>null, "sql"=>"name", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("description", array("label"=>"Description", "description"=>"one line description", "allowed_values"=>null, "sql"=>"description", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); + + //MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("name"); + MetaModel::Init_AddFilterFromAttribute("description"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('name', 'description')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('name', 'description')); // Attributes to be displayed for a list + // Search criteria +// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form +// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form + } + + abstract public function DoExecute($oTrigger, $aContextArgs); +} + +/** + * A notification + * + * @package iTopORM + * @author Romain Quetiez + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ +abstract class ActionNotification extends Action +{ + public static function Init() + { + $aParams = array + ( + "category" => "core/cmdb", + "name" => "notification", + "description" => "Notification (abstract)", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_action_notification", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + + MetaModel::Init_InheritFilters(); + + // Display lists + MetaModel::Init_SetZListItems('details', array('name', 'description')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('finalclass', 'name', 'description')); // Attributes to be displayed for a list + // Search criteria +// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form +// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form + } +} + +/** + * An email notification + * + * @package iTopORM + * @author Romain Quetiez + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ +class ActionEmail extends ActionNotification +{ + public static function Init() + { + $aParams = array + ( + "category" => "core/cmdb", + "name" => "email notification", + "description" => "Action: Email notification", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_action_email", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + + MetaModel::Init_AddAttribute(new AttributeString("from", array("label"=>"From", "description"=>"Will be sent into the email header", "allowed_values"=>null, "sql"=>"from", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("reply_to", array("label"=>"Reply to", "description"=>"Will be sent into the email header", "allowed_values"=>null, "sql"=>"reply_to", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeOQL("to", array("label"=>"To", "description"=>"Destination of the email", "allowed_values"=>null, "sql"=>"to", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeOQL("cc", array("label"=>"Cc", "description"=>"Carbon Copy", "allowed_values"=>null, "sql"=>"cc", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeOQL("bcc", array("label"=>"bcc", "description"=>"Blind Carbon Copy", "allowed_values"=>null, "sql"=>"bcc", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeTemplateString("subject", array("label"=>"subject", "description"=>"Title of the email", "allowed_values"=>null, "sql"=>"subject", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeTemplateText("body", array("label"=>"body", "description"=>"Contents of the email", "allowed_values"=>null, "sql"=>"body", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("importance", array("label"=>"importance", "description"=>"Importance flag", "allowed_values"=>new ValueSetEnum('low,normal,high'), "sql"=>"importance", "default_value"=>'normal', "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_InheritFilters(); + + // Display lists + MetaModel::Init_SetZListItems('details', array('name', 'description', 'from', 'reply_to', 'to', 'cc', 'bcc', 'subject', 'body', 'importance')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('name', 'to', 'subject')); // Attributes to be displayed for a list + // Search criteria +// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form +// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form + } + + // args: a search object + // returns an array of emails + protected function FindRecipients($sAttCode, $aArgs) + { + $sOQL = $this->Get($sAttCode); + if (strlen($sOQL) == '') return ''; + + $oSearch = DBObjectSearch::FromOQL($sOQL); + $sClass = $oSearch->GetClass(); + // Determine the email attribute (the first one will be our choice) + foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) + { + if ($oAttDef instanceof AttributeEmailAddress) + { + $sEmailAttCode = $sAttCode; + // we've got one, exit the loop + break; + } + } + + $oSet = new DBObjectSet($oSearch, array() /* order */, $aArgs); + $aRecipients = array(); + while ($oObj = $oSet->Fetch()) + { + $aRecipients[] = $oObj->Get($sEmailAttCode); + } + return implode(', ', $aRecipients); + } + + public function DoExecute($oTrigger, $aContextArgs) + { + // Determine recicipients + // + $sTo = $this->FindRecipients('to', $aContextArgs); + $sCC = $this->FindRecipients('cc', $aContextArgs); + $sBCC = $this->FindRecipients('bcc', $aContextArgs); + + $sFrom = $this->Get('from'); + $sReplyTo = $this->Get('reply_to'); + + $sSubject = MetaModel::ApplyParams($this->Get('subject'), $aContextArgs); + $sBody = MetaModel::ApplyParams($this->Get('body'), $aContextArgs); + + // To send HTML mail, the Content-type header must be set + $sHeaders = 'MIME-Version: 1.0' . "\r\n"; + $sHeaders .= 'Content-type: text/html; charset=iso-8859-1' . "\r\n"; + + // Additional headers + if (strlen($sFrom) > 0) + { + $sHeaders .= "From: $sFrom\r\n"; + // This is required on Windows because otherwise I would get the error + // "sendmail_from" not set in php.ini" even if it is correctly working + // (apparently, once it worked the SMTP server won't claim anymore for it) + ini_set("sendmail_from", $sFrom); + } + if (strlen($sReplyTo) > 0) + { + $sHeaders .= "Reply-To: $sReplyTo\r\n"; + } + if (strlen($sCC) > 0) + { + $sHeaders .= "Cc: $sCC\r\n"; + } + if (strlen($sBCC) > 0) + { + $sHeaders .= "Bcc: $sBCC\r\n"; + } + + $sOverview = "TO:$sTo, FROM:$sFrom, REPLY-TO:$sReplyTo, CC:$sCC, BCC:$sBCC, SUBJECT:$sSubject, BODY:$sBody"; + + // Mail it + if (mail($sTo, $sSubject, $sBody, $sHeaders)) + { + $oLog = new EventNotification(); + $oLog->Set('message', 'Email sent successfully'); + $oLog->Set('userinfo', UserRights::GetUser()); + $oLog->Set('trigger_id', $oTrigger->GetKey()); + $oLog->Set('action_id', $this->GetKey()); + $oLog->Set('object_id', $aContextArgs['this->id']); + $oLog->Set('overview', $sOverview); + $oLog->DBInsertNoReload(); + } + else + { + throw new CoreException('mail not sent', array('action'=>$this->GetKey(), 'to'=>$sTo, 'subject'=>$sSubject, 'headers'=>$sHeaders)); + } + } +} +?> diff --git a/core/event.class.inc.php b/core/event.class.inc.php new file mode 100644 index 000000000..b50e7a4c5 --- /dev/null +++ b/core/event.class.inc.php @@ -0,0 +1,94 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ +class Event extends cmdbAbstractObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "core/cmdb", + "name" => "Log Event", + "description" => "An application internal event", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_event", + "db_key_field" => "id", + "db_finalclass_field" => "realclass", + "display_template" => "", + ); + MetaModel::Init_Params($aParams); + //MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeString("message", array("label"=>"Message", "description"=>"one line description", "allowed_values"=>null, "sql"=>"message", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_AddAttribute(new AttributeDate("date", array("label"=>"date", "description"=>"date and time at which the changes have been recorded", "allowed_values"=>null, "sql"=>"date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("userinfo", array("label"=>"misc. info", "description"=>"caller's defined information", "allowed_values"=>null, "sql"=>"userinfo", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); + + //MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("message"); + MetaModel::Init_AddFilterFromAttribute("date"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('finalclass', 'date', 'message')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('finalclass', 'date', 'message')); // Attributes to be displayed for a list + // Search criteria +// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form +// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form + } +} + +class EventNotification extends Event +{ + public static function Init() + { + $aParams = array + ( + "category" => "core/cmdb", + "name" => "Notification event", + "description" => "Trace of a notification that has been sent", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_event_notification", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeExternalKey("trigger_id", array("targetclass"=>"Trigger", "jointype"=> "", "label"=>"Trigger", "description"=>"user account", "allowed_values"=>null, "sql"=>"trigger_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("action_id", array("targetclass"=>"Action", "jointype"=> "", "label"=>"user", "description"=>"user account", "allowed_values"=>null, "sql"=>"action_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeInteger("object_id", array("label"=>"Object id", "description"=>"object id (class defined by the trigger ?)", "allowed_values"=>null, "sql"=>"object_id", "default_value"=>0, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeText("overview", array("label"=>"Overview", "description"=>"Complete view of the resulting email", "allowed_values"=>null, "sql"=>"overview", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("trigger_id"); + MetaModel::Init_AddFilterFromAttribute("action_id"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('date', 'message', 'userinfo', 'trigger_id', 'action_id', 'object_id', 'overview')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('date', 'message')); // Attributes to be displayed for a list + // Search criteria +// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form +// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form + } + +} + + +?> diff --git a/core/trigger.class.inc.php b/core/trigger.class.inc.php new file mode 100644 index 000000000..ffc043a9f --- /dev/null +++ b/core/trigger.class.inc.php @@ -0,0 +1,204 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ +class Trigger extends cmdbAbstractObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "core/cmdb", + "name" => "trigger", + "description" => "Custom event handler", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_trigger", + "db_key_field" => "id", + "db_finalclass_field" => "realclass", + "display_template" => "", + ); + MetaModel::Init_Params($aParams); + //MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeString("description", array("label"=>"Description", "description"=>"one line description", "allowed_values"=>null, "sql"=>"description", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); + + MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("linked_actions", array("label"=>"Triggered actions", "description"=>"Actions performed when the trigger is activated", "linked_class"=>"lnkTriggerAction", "ext_key_to_me"=>"trigger_id", "ext_key_to_remote"=>"action_id", "allowed_values"=>null, "count_min"=>1, "count_max"=>0, "depends_on"=>array()))); + + //MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("description"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('finalclass', 'description')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('finalclass', 'description')); // Attributes to be displayed for a list + // Search criteria +// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form +// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form + } + + public function DoActivate($aContextArgs) + { + // Find the related + $oLinkedActions = $this->Get('linked_actions'); + while ($oLink = $oLinkedActions->Fetch()) + { + $iActionId = $oLink->Get('action_id'); + $oAction = MetaModel::GetObject('Action', $iActionId); + $oAction->DoExecute($this, $aContextArgs); + } + } +} + +class TriggerOnStateChange extends Trigger +{ + public static function Init() + { + $aParams = array + ( + "category" => "core/cmdb", + "name" => "Trigger on object state change", + "description" => "Trigger on object state change", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_trigger_onstatechange", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeString("target_class", array("label"=>"Target class", "description"=>"label", "allowed_values"=>null, "sql"=>"target_class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("state", array("label"=>"State", "description"=>"label", "allowed_values"=>null, "sql"=>"state", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("target_class"); + MetaModel::Init_AddFilterFromAttribute("state"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('description', 'target_class', 'state')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class', 'state')); // Attributes to be displayed for a list + // Search criteria +// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form +// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form + } + +} + +class TriggerOnStateEnter extends TriggerOnStateChange +{ + public static function Init() + { + $aParams = array + ( + "category" => "core/cmdb", + "name" => "Trigger on object entering a state", + "description" => "Trigger on object state change - entering", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_trigger_onstateenter", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + + MetaModel::Init_InheritFilters(); + + // Display lists + MetaModel::Init_SetZListItems('details', array('description', 'target_class', 'state')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('target_class', 'state')); // Attributes to be displayed for a list + // Search criteria +// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form +// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form + } +} + +class TriggerOnStateLeave extends TriggerOnStateChange +{ + public static function Init() + { + $aParams = array + ( + "category" => "core/cmdb", + "name" => "Trigger on object leaving a state", + "description" => "Trigger on object state change - leaving", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_trigger_onstateleave", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + + MetaModel::Init_InheritFilters(); + + // Display lists + MetaModel::Init_SetZListItems('details', array('description', 'target_class', 'state')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('target_class', 'state')); // Attributes to be displayed for a list + // Search criteria +// MetaModel::Init_SetZListItems('standard_search', array('')); // Criteria of the std search form +// MetaModel::Init_SetZListItems('advanced_search', array('')); // Criteria of the advanced search form + } +} + +class lnkTriggerAction extends cmdbAbstractObject +{ + public static function Init() + { + $aParams = array + ( + "category" => "core/cmdb", + "name" => "Actions-Trigger", + "description" => "Link between a trigger and an action", + "key_type" => "autoincrement", + "key_label" => "Link ID", + "name_attcode" => "", + "state_attcode" => "", + "reconc_keys" => array(""), + "db_table" => "priv_link_action_trigger", + "db_key_field" => "link_id", + "db_finalclass_field" => "", + "display_template" => "", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_AddAttribute(new AttributeExternalKey("action_id", array("targetclass"=>"Action", "jointype"=> '', "label"=>"Action", "description"=>"The action to be executed", "allowed_values"=>null, "sql"=>"action_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("action_name", array("label"=>"Action Name", "description"=>"Name of the action", "allowed_values"=>null, "extkey_attcode"=> 'action_id', "target_attcode"=>"name"))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("trigger_id", array("targetclass"=>"Trigger", "jointype"=> '', "label"=>"Trigger", "description"=>"Trigger", "allowed_values"=>null, "sql"=>"trigger_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeInteger("order", array("label"=>"Order", "description"=>"Actions execution order", "allowed_values"=>null, "sql"=>"order", "default_value"=>0, "is_null_allowed"=>true, "depends_on"=>array()))); + + MetaModel::Init_AddFilterFromAttribute("action_id"); + MetaModel::Init_AddFilterFromAttribute("trigger_id"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('action_id', 'trigger_id', 'order')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('list', array('action_id', 'trigger_id', 'order')); // Attributes to be displayed for a list + // Search criteria + MetaModel::Init_SetZListItems('standard_search', array('action_id', 'trigger_id', 'order')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('action_id', 'trigger_id', 'order')); // Criteria of the advanced search form + } +} +?> From 58ab73c7181cc955fb75d8bd9054477e746eb29d Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Wed, 6 Jan 2010 19:27:24 +0000 Subject: [PATCH 150/227] - For truncated lists SVN:trunk[227] --- images/truncated.png | Bin 0 -> 1429 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 images/truncated.png diff --git a/images/truncated.png b/images/truncated.png new file mode 100644 index 0000000000000000000000000000000000000000..57d3e77eae6a4956ecde31a0268b844cd70b6c98 GIT binary patch literal 1429 zcmc(e`!^E`0LLeV>u}W~Q99^xN~f-rlj|fYn)P6jBDW6i*64&hN=4rGWDKKim{+!v zM_$9so6Y-~VY7^R??E2JJnsIB`#tCT{haU5-}6O796*}t`sx4xKoe|l>!cu5VO=$4 zMRsEJ|4`r{+~%PT08qldeh9Om=z76+uEEa!FM~ro17QFgsJ|D?5bWy-hdIGKp&tS| zU^f5&B?RQbW4nlmh}qd$u~_``=g*Xsl-1Q$JRVOV5D*9iolZxgP*G7)xw*MZOH1M5 z;a|Rd85$ZI92{gY7%M9)ySuw}b#){XiAtr8j*cRc$f>ER@$vEA-rmT_$c2Rk91a&9 z9W4@x&}j6=#s-haYieprO-*fYZ~y-Ndqze^Yinz4Y%B(Y+1uNjnVDH!TwGpWPE1VX z^ZCii$rBS3)6>%l2?_D>@g*fCGMOwZE32%mjLYSgmX>B_W|GO|_4W0+xw(dhhP1S_ zk&zLhP}tYk_x0;n7K??&VvCB33JMBpYHEs$i`i`UuV25Ko12+T=I76!b8>Rx;^L%I zX-`iNg+i&WuAZNt&&$gzEG#4viE_ETs;cVu@84@{Ye`8-9UUDKi6kZ_Mj#Lj4-a>B zbu~6N?(FPzc6L@&RM2R&+S=NIfdLMO)6&w?*4DPYySSPyr*L6HFOQGI?hEaMm zf~30!rdwJnb$9qi(xedfz8Y{~uSmR|?tj%Jaz`1;T|ttZqb42KsWbWNI|M5#DPm~( zFGjYR8(f$R2lDm~ZGF&ttTa*Rq%_m?-sEx=*oPTv4s)ij-!U%HOuqH8AbiPhuwI-X zzMx@u<^HXB%DuyRP;z79?V+Q8n-gD~5LCS^F+R`VZMhb2CP~183xG~NJ4=%z+SAH~ zE+5Zdbz^N*w3#JM&96Pl86|oX?)mgZ%k4?$;JW?ZCLRYZ(#o)bH8f%?TnMzr87ajX z8*nbA^wsNEVFf47J}=64X3tB%gsST4x=ultCAipnnTG6f`qxMd%?9reM-%m)PU}Y@?vopKDb}|geQ`v zvfY37EG+oYDKq^4uWU)XD%d6dj_pa+}#Sss3snu zW*0-XSM#c9*Fg=?G2cRnw+{bZSXUZdSJ&JOG-AZPj$8wyFsbEZWhw>d$(jY-kke2; zrCp! Date: Wed, 6 Jan 2010 19:39:03 +0000 Subject: [PATCH 151/227] - Fixing styles issues found on the truncated lists (Ticket #61) SVN:trunk[228] --- css/light-grey.css | 4 ++++ js/utils.js | 1 + 2 files changed, 5 insertions(+) diff --git a/css/light-grey.css b/css/light-grey.css index ac9df52d8..ea901d9fe 100644 --- a/css/light-grey.css +++ b/css/light-grey.css @@ -680,6 +680,10 @@ table.listResults tr.even td.truncated.hover { background: #E8FFD3 url(../images/truncated.png) bottom repeat-x; } +table.listResults tr.odd td.truncated.hover { + background: #E8FFD3 url(../images/truncated.png) bottom repeat-x; +} + table.listResults.truncated { border-bottom: 0; padding-bottom: 0; diff --git a/js/utils.js b/js/utils.js index 7efa4a208..0f2e795ab 100644 --- a/js/utils.js +++ b/js/utils.js @@ -12,6 +12,7 @@ function ReloadTruncatedList(divId, sSerializedFilter, sExtraParams) $('#'+divId).empty(); $('#'+divId).append(data); $('#'+divId).removeClass('loading'); + $('#'+divId+' .listResults').tableHover(); // hover tables $('#'+divId+' .listResults').tablesorter( { headers: { 0:{sorter: false }}, widgets: ['zebra']} ); // sortable and zebra tables //$('#'+divId).unblockUI(); } From 9fc06ff6e4af5ca8e8e4ac34a53846de2a9f6cee Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Wed, 6 Jan 2010 20:06:16 +0000 Subject: [PATCH 152/227] - Fixing file upload/download troubles: make sure that magic_quotes_gpc is turned off and that the output is not truncated by AjaxWebPage. SVN:trunk[229] --- application/ajaxwebpage.class.inc.php | 2 +- setup/index.php | 16 ++++++++++++++++ 2 files changed, 17 insertions(+), 1 deletion(-) diff --git a/application/ajaxwebpage.class.inc.php b/application/ajaxwebpage.class.inc.php index 3d061f69a..fbacc0058 100644 --- a/application/ajaxwebpage.class.inc.php +++ b/application/ajaxwebpage.class.inc.php @@ -42,7 +42,7 @@ class ajax_page extends web_page } $s_captured_output = ob_get_contents(); ob_end_clean(); - echo trim($this->s_content); + echo $this->s_content; if (!empty($this->m_sReadyScript)) { echo "\n"; - $sHtml .= "
    Here goes the chart
    \n"; + $sHtml .= "
    \n"; $iChartCounter++; break; From bdaf63c681c19edb90792564f6bcf109d2b7d36f Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Tue, 12 Jan 2010 08:58:42 +0000 Subject: [PATCH 158/227] Data model changed: incident::workgroup becomes mandatory, and document::scope replaced by document::type SVN:trunk[235] --- business/incidentMgmt.business.php | 2 +- business/itop.business.class.inc.php | 14 +++++++------- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/business/incidentMgmt.business.php b/business/incidentMgmt.business.php index 0e386c174..2a44a4672 100644 --- a/business/incidentMgmt.business.php +++ b/business/incidentMgmt.business.php @@ -59,7 +59,7 @@ class bizIncidentTicket extends cmdbAbstractObject MetaModel::Init_AddAttribute(new AttributeExternalField("caller_mail", array("label"=>"Caller", "description"=>"Person that trigger this incident", "allowed_values"=>null, "extkey_attcode"=> 'caller_id', "target_attcode"=>"email"))); MetaModel::Init_AddAttribute(new AttributeString("impact", array("label"=>"Impact", "description"=>"Impact of the Incident", "allowed_values"=>null, "sql"=>"impact", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeExternalKey("workgroup_id", array("targetclass"=>"bizWorkgroup", "jointype"=> "", "label"=>"Workgroup", "description"=>"which workgroup is owning ticket", "allowed_values"=>null, "sql"=>"workgroup_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("workgroup_id", array("targetclass"=>"bizWorkgroup", "jointype"=> "", "label"=>"Workgroup", "description"=>"which workgroup is owning ticket", "allowed_values"=>null, "sql"=>"workgroup_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("workgroup_name", array("label"=>"Workgroup", "description"=>"name of workgroup managing the Ticket", "allowed_values"=>null, "extkey_attcode"=> 'workgroup_id', "target_attcode"=>"name"))); MetaModel::Init_AddAttribute(new AttributeExternalKey("agent_id", array("targetclass"=>"bizPerson", "jointype"=> "", "label"=>"Agent", "description"=>"who is managing the ticket", "allowed_values"=>new ValueSetObjects('SELECT bizPerson AS p JOIN lnkContactTeam AS l ON l.contact_id=p.id JOIN bizTeam AS t ON l.team_id=t.id JOIN bizWorkgroup AS w ON w.team_id=t.id WHERE w.id = :this->workgroup_id'), "sql"=>"agent_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array('workgroup_id')))); MetaModel::Init_AddAttribute(new AttributeExternalField("agent_mail", array("label"=>"Agent", "description"=>"mail of agent managing the Ticket", "allowed_values"=>null, "extkey_attcode"=> 'agent_id', "target_attcode"=>"email"))); diff --git a/business/itop.business.class.inc.php b/business/itop.business.class.inc.php index ceda3e8e2..49c6ed8d3 100644 --- a/business/itop.business.class.inc.php +++ b/business/itop.business.class.inc.php @@ -396,20 +396,20 @@ class bizDocument extends logRealObject MetaModel::Init_Params($aParams); MetaModel::Init_InheritAttributes(); MetaModel::Init_AddAttribute(new AttributeExternalField("org_name", array("label"=>"Organization", "description"=>"Company / Department owning the document", "allowed_values"=>null, "extkey_attcode"=> 'org_id', "target_attcode"=>"name"))); - MetaModel::Init_AddAttribute(new AttributeEnum("scope", array("label"=>"scope", "description"=>"Scope of this document", "allowed_values"=>new ValueSetEnum("organization,hardware support"), "sql"=>"scope", "default_value"=>"organization", "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeString("description", array("label"=>"Description", "description"=>"Service Description", "allowed_values"=>null, "sql"=>"description", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("type", array("label"=>"type", "description"=>"type of this document", "allowed_values"=>new ValueSetEnum("documentation,contract,working instruction,network maps,white paper,presentation,training"), "sql"=>"type", "default_value"=>"documentation", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeText("description", array("label"=>"Description", "description"=>"Service Description", "allowed_values"=>null, "sql"=>"description", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeBlob("contents", array("label"=>"Contents", "description"=>"File content", "depends_on"=>array()))); MetaModel::Init_InheritFilters(); - MetaModel::Init_AddFilterFromAttribute("scope"); + MetaModel::Init_AddFilterFromAttribute("type"); MetaModel::Init_AddFilterFromAttribute("description"); - MetaModel::Init_SetZListItems('details', array('name', 'status', 'org_id', 'scope', 'description', 'contents')); // Attributes to be displayed for the complete details - MetaModel::Init_SetZListItems('list', array('name', 'status', 'org_id', 'scope', 'contents')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('details', array('name', 'status', 'org_id', 'type', 'description', 'contents')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('name', 'status', 'org_id', 'type', 'contents')); // Attributes to be displayed for a list // Search criteria - MetaModel::Init_SetZListItems('standard_search', array('name', 'status', 'scope')); // Criteria of the std search form - MetaModel::Init_SetZListItems('advanced_search', array('name', 'status', 'scope')); // Criteria of the advanced search form + MetaModel::Init_SetZListItems('standard_search', array('name', 'status', 'type')); // Criteria of the std search form + MetaModel::Init_SetZListItems('advanced_search', array('name', 'status', 'type')); // Criteria of the advanced search form } From 3b8928bf743771606b5a225d5eaf02bb3c0a0bb6 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Tue, 12 Jan 2010 09:05:50 +0000 Subject: [PATCH 159/227] Fixed typo in the enums of the new field document::type SVN:trunk[236] --- business/itop.business.class.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/business/itop.business.class.inc.php b/business/itop.business.class.inc.php index 49c6ed8d3..e870ec4f4 100644 --- a/business/itop.business.class.inc.php +++ b/business/itop.business.class.inc.php @@ -396,7 +396,7 @@ class bizDocument extends logRealObject MetaModel::Init_Params($aParams); MetaModel::Init_InheritAttributes(); MetaModel::Init_AddAttribute(new AttributeExternalField("org_name", array("label"=>"Organization", "description"=>"Company / Department owning the document", "allowed_values"=>null, "extkey_attcode"=> 'org_id', "target_attcode"=>"name"))); - MetaModel::Init_AddAttribute(new AttributeEnum("type", array("label"=>"type", "description"=>"type of this document", "allowed_values"=>new ValueSetEnum("documentation,contract,working instruction,network maps,white paper,presentation,training"), "sql"=>"type", "default_value"=>"documentation", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("type", array("label"=>"type", "description"=>"usage of the document", "allowed_values"=>new ValueSetEnum("documentation,contract,working instructions,network map,white paper,presentation,training"), "sql"=>"type", "default_value"=>"documentation", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeText("description", array("label"=>"Description", "description"=>"Service Description", "allowed_values"=>null, "sql"=>"description", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeBlob("contents", array("label"=>"Contents", "description"=>"File content", "depends_on"=>array()))); From 2565d21c402f5cf562165ba81cd40904274797fe Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Tue, 12 Jan 2010 10:13:33 +0000 Subject: [PATCH 160/227] New menu for the configuration of notifications SVN:trunk[237] --- setup/data/structure/1.menus.xml | 55 ++++++++++++++++++++++++++++++++ 1 file changed, 55 insertions(+) diff --git a/setup/data/structure/1.menus.xml b/setup/data/structure/1.menus.xml index 7c78a33e0..64820cb60 100644 --- a/setup/data/structure/1.menus.xml +++ b/setup/data/structure/1.menus.xml @@ -997,4 +997,59 @@ text-align:left; 1 0 + +Notifications + +UI.php + + +administrator +2 +1 +0 + From 29a4ce205192a9eeb0403ced70539a142fd48ab3 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Tue, 12 Jan 2010 10:16:45 +0000 Subject: [PATCH 161/227] Factorized the processing of templates SVN:trunk[238] --- application/menunode.class.inc.php | 27 +++------------------------ 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/application/menunode.class.inc.php b/application/menunode.class.inc.php index d6337c718..19b54b245 100644 --- a/application/menunode.class.inc.php +++ b/application/menunode.class.inc.php @@ -116,30 +116,9 @@ class menuNode extends DBObject public function RenderContent(web_page $oPage, $aExtraParams = array()) { $sTemplate = $this->Get('template'); - $this->ProcessTemplate($sTemplate, $oPage, $aExtraParams); - } - - protected function ProcessTemplate($sTemplate, web_page $oPage, $aExtraParams = array()) - { - $iStartPos = stripos($sTemplate, '<'.DisplayBlock::TAG_BLOCK.' ',0); - $index = 0; - while(($iStartPos = stripos($sTemplate, '<'.DisplayBlock::TAG_BLOCK.' ',0)) !== false) - { - $iEndPos = stripos($sTemplate, '', $iStartPos); - - $sBlockDefinition = substr($sTemplate, $iStartPos, $iEndPos - $iStartPos + strlen('')); - $oBlock = DisplayBlock::FromTemplate($sBlockDefinition); - - $oPage->add(substr($sTemplate, 0, $iStartPos)); - if ($oBlock) // Protects agains invalid XML templates - { - $oBlock->Display($oPage, "block{$index}", $aExtraParams); // Values from $aExtraParams have precedence over $aParams - } - $index++; - $sTemplate = substr($sTemplate, $iEndPos + strlen('')); - } - // What remains is purely static (without any block inside), just output as it is - $oPage->add($sTemplate); + $oTemplate = new DisplayTemplate($sTemplate); + $oTemplate->Render($oPage, $aExtraParams); + //$this->ProcessTemplate($sTemplate, $oPage, $aExtraParams); } public function DisplayMenu(iTopWebPage $oP, $sType, $aExtraParams) From 423216e4aebdd2ce21727d1345beca91f326c0af Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Tue, 12 Jan 2010 10:18:18 +0000 Subject: [PATCH 162/227] The label of the active menu is now used as the title of the web page. SVN:trunk[239] --- pages/UI.php | 1 + 1 file changed, 1 insertion(+) diff --git a/pages/UI.php b/pages/UI.php index 7478805d3..e3a34c623 100644 --- a/pages/UI.php +++ b/pages/UI.php @@ -1139,6 +1139,7 @@ switch($operation) if (is_object($oActiveNode)) { $oActiveNode->RenderContent($oP, $oAppContext->GetAsHash()); + $oP->set_title($oActiveNode->Get('label')); } } ////MetaModel::ShowQueryTrace(); From 532ce97fd117d53fec44d60d8d2f11a055a46f3a Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Tue, 12 Jan 2010 10:20:01 +0000 Subject: [PATCH 163/227] Cosmetic bug fix: the triangle image of the collapsible section was not displayed properly if the "section" was initially open. SVN:trunk[240] --- application/template.class.inc.php | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/application/template.class.inc.php b/application/template.class.inc.php index e7b30dcae..4ebfdf429 100644 --- a/application/template.class.inc.php +++ b/application/template.class.inc.php @@ -141,7 +141,9 @@ class DisplayTemplate break; case 'itoptoggle': - $oPage->StartCollapsibleSection($aAttributes['name']); + $sName = isset($aAttributes['name']) ? $aAttributes['name'] : 'Tagada'; + $bOpen = isset($aAttributes['open']) ? $aAttributes['open'] : true; + $oPage->StartCollapsibleSection($sName, $bOpen); $oTemplate = new DisplayTemplate($sContent); $oTemplate->Render($oPage, array()); // no params to apply, they have already been applied //$oPage->p('iTop Tab Content:
    '.htmlentities($sContent).'
    '); From 068c6916d6f0dcddd978dd005b7f1d0ef599bc52 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Tue, 12 Jan 2010 20:12:00 +0000 Subject: [PATCH 164/227] - Removed the organization "My Company/Department" since it is always created by the setup. - Do not set a parent for each organization (was usually set to themselves) SVN:trunk[241] --- setup/data/01.organizations.xml | 16 +++++----------- 1 file changed, 5 insertions(+), 11 deletions(-) diff --git a/setup/data/01.organizations.xml b/setup/data/01.organizations.xml index 9c43f85be..a8426abf1 100644 --- a/setup/data/01.organizations.xml +++ b/setup/data/01.organizations.xml @@ -4,24 +4,18 @@ Demo MCO implementation -2 +0 - + France Telecom FT production 0 - -My Company/Department -SOMECODE -implementation -0 - - + Oracle ORA production -3 +0 - \ No newline at end of file + From 2fe861a0d554af7cd26c39ae7177816d6a403049 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Tue, 12 Jan 2010 20:14:13 +0000 Subject: [PATCH 165/227] - Exclude "My Company/Department" from the export of organizations, since this organization is always created by the setup program. SVN:trunk[242] --- setup/data/export.cmd | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/setup/data/export.cmd b/setup/data/export.cmd index 33a1e6d2b..c36e5a35c 100644 --- a/setup/data/export.cmd +++ b/setup/data/export.cmd @@ -6,7 +6,7 @@ SET PWD=admin REM The order (numbering) of the files is important since REM it dictates the order to import them back -wget --output-document=01.organizations.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT bizOrganization&format=xml" +wget --output-document=01.organizations.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT bizOrganization WHERE name !='My Company/Department'&format=xml" wget --output-document=02.locations.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT bizLocation&format=xml" wget --output-document=03.persons.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT bizPerson WHERE id !%%3D 1&format=xml" wget --output-document=04.teams.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT bizTeam&format=xml" From c800b9d41e7f5941ba0ff144f98e2cf41916e5f6 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Tue, 12 Jan 2010 21:02:42 +0000 Subject: [PATCH 166/227] - Added some variables about the version, build revision and build date to be populated automatically by the build system. SVN:trunk[243] --- application/startup.inc.php | 3 +++ 1 file changed, 3 insertions(+) diff --git a/application/startup.inc.php b/application/startup.inc.php index 97969a34b..3b4f86b88 100644 --- a/application/startup.inc.php +++ b/application/startup.inc.php @@ -1,4 +1,7 @@ Date: Tue, 12 Jan 2010 21:10:08 +0000 Subject: [PATCH 167/227] - Added display of the version / revision and build date for "official builds" - Fixed the display of the triangle image of the "collapsible section" when it is initially "open" SVN:trunk[244] --- application/itopwebpage.class.inc.php | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/application/itopwebpage.class.inc.php b/application/itopwebpage.class.inc.php index dfe51047e..16e897823 100644 --- a/application/itopwebpage.class.inc.php +++ b/application/itopwebpage.class.inc.php @@ -262,8 +262,18 @@ EOF echo "\n"; // Display the header + if (ITOP_REVISION == '$WCREV$') + { + // This is NOT a version built using the buil system, just display the main version + $sVersionString = "iTop Version ".ITOP_VERSION; + } + else + { + // This is a build made from SVN, let display the full information + $sVersionString = "iTop Version ".ITOP_VERSION." revision ".ITOP_REVISION.", built on: ".ITOP_BUILD_DATE; + } echo "
    \n"; - echo "
    iTop
    \n"; + echo "
    iTop
    \n"; //echo "
    \n"; $sText = Utils::ReadParam('text', ''); $sOnClick = ""; @@ -384,7 +394,8 @@ EOF { $sHtml = ''; static $iSectionId = 0; - $sHtml .= "$sSectionLabel
    \n"; + $sImgStyle = $bOpen ? ' open' : ''; + $sHtml .= "$sSectionLabel
    \n"; $sStyle = $bOpen ? '' : 'style="display:none" '; $sHtml .= "
    "; $this->add_ready_script("\$(\"#LnkCollapse_$iSectionId\").click(function() {\$(\"#Collapse_$iSectionId\").slideToggle('normal'); $(\"#LnkCollapse_$iSectionId\").toggleClass('open');});"); From 89fa70d755b49e3e206310c486553526abdd89e0 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Tue, 12 Jan 2010 21:47:14 +0000 Subject: [PATCH 168/227] New icon for the notifications SVN:trunk[245] --- images/bell.png | Bin 0 -> 12978 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 images/bell.png diff --git a/images/bell.png b/images/bell.png new file mode 100644 index 0000000000000000000000000000000000000000..34fb9b5d8683eee635fa9ea4124c829d2e640166 GIT binary patch literal 12978 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 literal 0 HcmV?d00001 From 8964b13a2e7b1440d0495bfc6f215a224ca3a7d8 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Wed, 13 Jan 2010 13:30:08 +0000 Subject: [PATCH 169/227] - Fixed bug with change tracking of TEXT attributes - Log any email notification (successful or not) - Class selection always made through a Combo box - Automatic creation of indexes for external keys - New trigger: on object creation - Application log: added a status string - Added documentation to the WSDL (+ anyType replaced by string) - SOAP: improved handling of enumeration attributes - SOAP: returned logs to mention the name of the parameter as advertised in the WSDL file (different than the name of the attribute in Itop) - Finalized SOAP tests - Added a SOAP client example SVN:trunk[246] --- .../userrightsprofile.class.inc.php | 2 +- application/iotask.class.inc.php | 2 +- core/action.class.inc.php | 29 ++-- core/attributedef.class.inc.php | 24 ++++ core/cmdbchangeop.class.inc.php | 70 ++++++++- core/cmdbobject.class.inc.php | 20 +++ core/cmdbsource.class.inc.php | 10 ++ core/dbobject.class.php | 27 +++- core/event.class.inc.php | 6 +- core/metamodel.class.php | 30 ++-- core/trigger.class.inc.php | 38 ++++- core/valuesetdef.class.inc.php | 42 +++--- pages/testlist.inc.php | 115 ++++++++++++--- webservices/itop.wsdl.tpl | 51 +++++-- webservices/itopsoap.examples.php | 69 +++++++++ webservices/webservices.class.inc.php | 136 +++++++++++++----- 16 files changed, 554 insertions(+), 117 deletions(-) create mode 100644 webservices/itopsoap.examples.php diff --git a/addons/userrights/userrightsprofile.class.inc.php b/addons/userrights/userrightsprofile.class.inc.php index bc99de3a2..d6c33e24c 100644 --- a/addons/userrights/userrightsprofile.class.inc.php +++ b/addons/userrights/userrightsprofile.class.inc.php @@ -307,7 +307,7 @@ class URP_Dimensions extends UserRightsBaseClass //MetaModel::Init_InheritAttributes(); MetaModel::Init_AddAttribute(new AttributeString("name", array("label"=>"Name", "description"=>"label", "allowed_values"=>null, "sql"=>"name", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("description", array("label"=>"Description", "description"=>"one line description", "allowed_values"=>null, "sql"=>"description", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeString("type", array("label"=>"Type", "description"=>"class name or data type (projection unit)", "allowed_values"=>new ValueSetEnumClasses('bizmodel', 'String,Integer'), "sql"=>"type", "default_value"=>'String', "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeClass("type", array("label"=>"Type", "description"=>"class name or data type (projection unit)", "class_category"=>"bizmodel", "more_values"=>"String,Integer", "sql"=>"type", "default_value"=>'String', "is_null_allowed"=>false, "depends_on"=>array()))); //MetaModel::Init_InheritFilters(); MetaModel::Init_AddFilterFromAttribute("name"); diff --git a/application/iotask.class.inc.php b/application/iotask.class.inc.php index f1f62f8c7..e882a48ee 100644 --- a/application/iotask.class.inc.php +++ b/application/iotask.class.inc.php @@ -31,7 +31,7 @@ class InputOutputTask extends cmdbAbstractObject MetaModel::Init_AddAttribute(new AttributeEnum("source_type", array("label"=>"Source Type", "description"=>"Type of data source", "allowed_values"=>new ValueSetEnum('File, Database, Web Service'), "sql"=>"source_type", "default_value"=>"File", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeEnum("source_subtype", array("label"=>"Source Subtype", "description"=>"Subtype of Data Source", "allowed_values"=>new ValueSetEnum('Oracle, MySQL, Postgress, MSSQL, SOAP, HTTP-Get, HTTP-Post, XML/RPC, CSV, XML, Excel'), "sql"=>"source_subtype", "default_value"=>"CSV", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("source_path", array("label"=>"Source Path", "description"=>"Path to the icon o the menu", "allowed_values"=>null, "sql"=>"source_path", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeEnum("objects_class", array("label"=>"Objects Class", "description"=>"Class of the objects processed by this task", "allowed_values"=>new ValueSetEnumClasses(), "sql"=>"objects_class", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeClass("objects_class", array("label"=>"Objects Class", "description"=>"Class of the objects processed by this task", "class_category"=>"", "more_values"=>"", "sql"=>"objects_class", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeEnum("test_mode", array("label"=>"Test Mode", "description"=>"If set to 'Yes' the modifications are not applied", "allowed_values"=>new ValueSetEnum('Yes,No'), "sql"=>"test_mode", "default_value"=>'No', "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeEnum("verbose_mode", array("label"=>"Verbose Mode", "description"=>"If set to 'Yes' extra debug information is added to the log", "allowed_values"=>new ValueSetEnum('Yes,No'), "sql"=>"verbose_mode", "default_value" => 'No', "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeEnum("options", array("label"=>"Options", "description"=>"Reconciliation options", "allowed_values"=>new ValueSetEnum('Full, Update Only, Creation Only'), "sql"=>"options", "default_value"=> 'Full', "is_null_allowed"=>true, "depends_on"=>array()))); diff --git a/core/action.class.inc.php b/core/action.class.inc.php index 68c12a759..51cee9af5 100644 --- a/core/action.class.inc.php +++ b/core/action.class.inc.php @@ -215,25 +215,28 @@ class ActionEmail extends ActionNotification $sHeaders .= "Bcc: $sBCC\r\n"; } - // Mail it + $oLog = new EventNotificationEmail(); if (mail($sTo, $sSubject, $sBody, $sHeaders)) { - $oLog = new EventNotificationEmail(); - $oLog->Set('userinfo', UserRights::GetUser()); - $oLog->Set('trigger_id', $oTrigger->GetKey()); - $oLog->Set('action_id', $this->GetKey()); - $oLog->Set('object_id', $aContextArgs['this->id']); - $oLog->Set('to', $sTo); - $oLog->Set('cc', $sCC); - $oLog->Set('bcc', $sBCC); - $oLog->Set('subject', $sSubject); - $oLog->Set('body', $sBody); - $oLog->DBInsertNoReload(); + $oLog->Set('message', 'Notification sent'); } else { - throw new CoreException('mail not sent', array('action'=>$this->GetKey(), 'to'=>$sTo, 'subject'=>$sSubject, 'headers'=>$sHeaders)); + $aLastError = error_get_last(); + $oLog->Set('message', 'Mail could not be sent: '.$aLastError['message']); + //throw new CoreException('mail not sent', array('action'=>$this->GetKey(), 'to'=>$sTo, 'subject'=>$sSubject, 'headers'=>$sHeaders)); } + + $oLog->Set('userinfo', UserRights::GetUser()); + $oLog->Set('trigger_id', $oTrigger->GetKey()); + $oLog->Set('action_id', $this->GetKey()); + $oLog->Set('object_id', $aContextArgs['this->id']); + $oLog->Set('to', $sTo); + $oLog->Set('cc', $sCC); + $oLog->Set('bcc', $sBCC); + $oLog->Set('subject', $sSubject); + $oLog->Set('body', $sBody); + $oLog->DBInsertNoReload(); } } ?> diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php index 6beed31b2..145020461 100644 --- a/core/attributedef.class.inc.php +++ b/core/attributedef.class.inc.php @@ -600,6 +600,30 @@ class AttributeString extends AttributeDBField } } +/** + * An attibute that matches an object class + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class AttributeClass extends AttributeString +{ + static protected function ListExpectedParams() + { + return array_merge(parent::ListExpectedParams(), array("class_category", "more_values")); + } + + public function __construct($sCode, $aParams) + { + $this->m_sCode = $sCode; + $aParams["allowed_values"] = new ValueSetEnumClasses($aParams['class_category'], $aParams['more_values']); + parent::__construct($sCode, $aParams); + } +} /** * Map a varchar column (size < ?) to an attribute that must never be shown to the user diff --git a/core/cmdbchangeop.class.inc.php b/core/cmdbchangeop.class.inc.php index ebbfc173b..2835992ed 100644 --- a/core/cmdbchangeop.class.inc.php +++ b/core/cmdbchangeop.class.inc.php @@ -304,8 +304,8 @@ class CMDBChangeOpSetAttributeBlob extends CMDBChangeOpSetAttribute $aParams = array ( "category" => "core/cmdb", - "name" => "object data change", - "description" => "Object data change tracking", + "name" => "data change", + "description" => "data change tracking", "key_type" => "", "key_label" => "", "name_attcode" => "change", @@ -357,5 +357,71 @@ class CMDBChangeOpSetAttributeBlob extends CMDBChangeOpSetAttribute } } +/** + * Record the modification of a multiline string (text) + * + * @package iTopORM + * @author Romain Quetiez + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version $itopversion$ + */ +class CMDBChangeOpSetAttributeText extends CMDBChangeOpSetAttribute +{ + public static function Init() + { + $aParams = array + ( + "category" => "core/cmdb", + "name" => "text change", + "description" => "text change tracking", + "key_type" => "", + "key_label" => "", + "name_attcode" => "change", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_changeop_setatt_text", + "db_key_field" => "id", + "db_finalclass_field" => "", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeText("prevdata", array("label"=>"Previous data", "description"=>"previous contents of the attribute", "allowed_values"=>null, "sql"=>"prevdata", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + + MetaModel::Init_InheritFilters(); + + // Display lists + MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'attcode')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'attcode')); // Attributes to be displayed for a list + } + + /** + * Describe (as a text string) the modifications corresponding to this change + */ + public function GetDescription() + { + // Temporary, until we change the options of GetDescription() -needs a more global revision + $bIsHtml = true; + + $sResult = ''; + $oTargetObjectClass = $this->Get('objclass'); + $oTargetObjectKey = $this->Get('objkey'); + $oTargetSearch = new DBObjectSearch($oTargetObjectClass); + $oTargetSearch->AddCondition('id', $oTargetObjectKey); + + $oMonoObjectSet = new DBObjectSet($oTargetSearch); + if (UserRights::IsActionAllowedOnAttribute($this->Get('objclass'), $this->Get('attcode'), UR_ACTION_READ, $oMonoObjectSet) == UR_ALLOWED_YES) + { + $oAttDef = MetaModel::GetAttributeDef($this->Get('objclass'), $this->Get('attcode')); + $sAttName = $oAttDef->GetLabel(); + $sTextView = '
    '.$this->GetAsHtml('prevdata').'
    '; + + //$sDocView = $oPrevDoc->GetDisplayInline(get_class($this), $this->GetKey(), 'prevdata'); + $sResult = "$sAttName changed, previous value: $sTextView"; + } + return $sResult; + } +} ?> diff --git a/core/cmdbobject.class.inc.php b/core/cmdbobject.class.inc.php index 997f605d4..5f490e9e5 100644 --- a/core/cmdbobject.class.inc.php +++ b/core/cmdbobject.class.inc.php @@ -193,6 +193,26 @@ abstract class CMDBObject extends DBObject $oMyChangeOp->Set("prevdata", $original); $iId = $oMyChangeOp->DBInsertNoReload(); } + elseif ($oAttDef instanceOf AttributeText) + { + // Data blobs + $oMyChangeOp = MetaModel::NewObject("CMDBChangeOpSetAttributeText"); + $oMyChangeOp->Set("change", $oChange->GetKey()); + $oMyChangeOp->Set("objclass", get_class($this)); + $oMyChangeOp->Set("objkey", $this->GetKey()); + $oMyChangeOp->Set("attcode", $sAttCode); + + if (array_key_exists($sAttCode, $aOrigValues)) + { + $original = $aOrigValues[$sAttCode]; + } + else + { + $original = null; + } + $oMyChangeOp->Set("prevdata", $original); + $iId = $oMyChangeOp->DBInsertNoReload(); + } else { // Scalars diff --git a/core/cmdbsource.class.inc.php b/core/cmdbsource.class.inc.php index b68474d3e..20f3fed9e 100644 --- a/core/cmdbsource.class.inc.php +++ b/core/cmdbsource.class.inc.php @@ -342,6 +342,16 @@ class CMDBSource return (strtolower($aFieldData["Null"]) == "yes"); } + public static function HasIndex($sTable, $sField) + { + $aTableInfo = self::GetTableInfo($sTable); + if (empty($aTableInfo)) return false; + if (!array_key_exists($sField, $aTableInfo["Fields"])) return false; + $aFieldData = $aTableInfo["Fields"][$sField]; + // $aFieldData could be 'PRI' for the primary key, or 'MUL', or ? + return (strlen($aFieldData["Key"]) > 0); + } + // Returns an array of (fieldname => array of field info) public static function GetTableFieldsList($sTable) { diff --git a/core/dbobject.class.php b/core/dbobject.class.php index d309ca4db..e20943a7a 100644 --- a/core/dbobject.class.php +++ b/core/dbobject.class.php @@ -497,6 +497,17 @@ abstract class DBObject } } } + elseif ($oAtt->IsWritable() && $oAtt->IsScalar()) + { + $aValues = $oAtt->GetAllowedValues(); + if (count($aValues) > 0) + { + if (!array_key_exists($toCheck, $aValues)) + { + return false; + } + } + } return true; } @@ -690,9 +701,19 @@ abstract class DBObject } $this->DBWriteLinks(); - - // Reload to update the external attributes $this->m_bIsInDB = true; + + // Activate any existing trigger + $sClass = get_class($this); + $oSet = new DBObjectSet(new DBObjectSearch('TriggerOnObjectCreate')); + while ($oTrigger = $oSet->Fetch()) + { + if (MetaModel::IsParentClass($oTrigger->Get('target_class'), $sClass)) + { + $oTrigger->DoActivate($this->ToArgs('this')); + } + } + return $this->m_iKey; } @@ -849,6 +870,8 @@ abstract class DBObject $aScalarArgs = array(); $aScalarArgs[$sArgName] = $this->GetKey(); $aScalarArgs[$sArgName.'->id'] = $this->GetKey(); + $aScalarArgs[$sArgName.'->hyperlink()'] = $this->GetHyperlink(); + $aScalarArgs[$sArgName.'->name()'] = $this->GetName(); $sClass = get_class($this); foreach(MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) diff --git a/core/event.class.inc.php b/core/event.class.inc.php index 833879e54..79b2efbe0 100644 --- a/core/event.class.inc.php +++ b/core/event.class.inc.php @@ -32,15 +32,17 @@ class Event extends cmdbAbstractObject ); MetaModel::Init_Params($aParams); //MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeString("message", array("label"=>"message", "description"=>"short description of the event", "allowed_values"=>null, "sql"=>"message", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeDate("date", array("label"=>"date", "description"=>"date and time at which the changes have been recorded", "allowed_values"=>null, "sql"=>"date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("userinfo", array("label"=>"user info", "description"=>"identification of the user that was doing the action that triggered this event", "allowed_values"=>null, "sql"=>"userinfo", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); //MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("message"); MetaModel::Init_AddFilterFromAttribute("date"); // Display lists - MetaModel::Init_SetZListItems('details', array('finalclass', 'date', 'userinfo')); // Attributes to be displayed for the complete details - MetaModel::Init_SetZListItems('list', array('finalclass', 'date')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('details', array('message', 'date', 'userinfo')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('date', 'finalclass', 'message')); // Attributes to be displayed for a list // Search criteria // MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form // MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form diff --git a/core/metamodel.class.php b/core/metamodel.class.php index c54e828f9..6177cace2 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -2092,18 +2092,30 @@ abstract class MetaModel { $aErrors[$sClass][] = "field '$sField' could not be found in table '$sTable'"; $aSugFix[$sClass][] = "ALTER TABLE `$sTable` ADD `$sField` $sFieldSpecs"; - } - elseif ($oAttDef->IsNullAllowed() != CMDBSource::IsNullAllowed($sTable, $sField)) - { - if ($oAttDef->IsNullAllowed()) + if ($oAttDef->IsExternalKey()) { - $aErrors[$sClass][] = "field '$sField' in table '$sTable' could be NULL"; - $aSugFix[$sClass][] = "ALTER TABLE `$sTable` CHANGE `$sField` `$sField` $sFieldSpecs"; + $aSugFix[$sClass][] = "ALTER TABLE `$sTable` ADD INDEX (`$sField`)"; } - else + } + else + { + if ($oAttDef->IsNullAllowed() != CMDBSource::IsNullAllowed($sTable, $sField)) { - $aErrors[$sClass][] = "field '$sField' in table '$sTable' could NOT be NULL"; - $aSugFix[$sClass][] = "ALTER TABLE `$sTable` CHANGE `$sField` `$sField` $sFieldSpecs"; + if ($oAttDef->IsNullAllowed()) + { + $aErrors[$sClass][] = "field '$sField' in table '$sTable' could be NULL"; + $aSugFix[$sClass][] = "ALTER TABLE `$sTable` CHANGE `$sField` `$sField` $sFieldSpecs"; + } + else + { + $aErrors[$sClass][] = "field '$sField' in table '$sTable' could NOT be NULL"; + $aSugFix[$sClass][] = "ALTER TABLE `$sTable` CHANGE `$sField` `$sField` $sFieldSpecs"; + } + } + if ($oAttDef->IsExternalKey() && !CMDBSource::HasIndex($sTable, $sField)) + { + $aErrors[$sClass][] = "Foreign key '$sField' in table '$sTable' should have an index"; + $aSugFix[$sClass][] = "ALTER TABLE `$sTable` ADD INDEX (`$sField`)"; } } } diff --git a/core/trigger.class.inc.php b/core/trigger.class.inc.php index ffc043a9f..6d9d1a637 100644 --- a/core/trigger.class.inc.php +++ b/core/trigger.class.inc.php @@ -82,7 +82,7 @@ class TriggerOnStateChange extends Trigger ); MetaModel::Init_Params($aParams); MetaModel::Init_InheritAttributes(); - MetaModel::Init_AddAttribute(new AttributeString("target_class", array("label"=>"Target class", "description"=>"label", "allowed_values"=>null, "sql"=>"target_class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeClass("target_class", array("label"=>"Target class", "description"=>"label", "class_category"=>"bizmodel", "more_values"=>null, "sql"=>"target_class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("state", array("label"=>"State", "description"=>"label", "allowed_values"=>null, "sql"=>"state", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_InheritFilters(); @@ -96,7 +96,6 @@ class TriggerOnStateChange extends Trigger // MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form // MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form } - } class TriggerOnStateEnter extends TriggerOnStateChange @@ -165,6 +164,41 @@ class TriggerOnStateLeave extends TriggerOnStateChange } } +class TriggerOnObjectCreate extends Trigger +{ + public static function Init() + { + $aParams = array + ( + "category" => "core/cmdb", + "name" => "Trigger on object creation", + "description" => "Trigger on object creation of [a child class of] the given class", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_trigger_onobjcreate", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeClass("target_class", array("label"=>"Target class", "description"=>"label", "class_category"=>"bizmodel", "more_values"=>null, "sql"=>"target_class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("target_class"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('description', 'target_class')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class')); // Attributes to be displayed for a list + // Search criteria +// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form +// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form + } +} + class lnkTriggerAction extends cmdbAbstractObject { public static function Init() diff --git a/core/valuesetdef.class.inc.php b/core/valuesetdef.class.inc.php index 393a06baa..e84b0c2b5 100644 --- a/core/valuesetdef.class.inc.php +++ b/core/valuesetdef.class.inc.php @@ -128,16 +128,23 @@ class ValueSetObjects extends ValueSetDefinition */ class ValueSetEnum extends ValueSetDefinition { + protected $m_values; + public function __construct($Values) { - if (is_array($Values)) + $this->m_values = $Values; + } + + protected function LoadValues($aArgs) + { + if (is_array($this->m_values)) { - $aValues = $Values; + $aValues = $this->m_values; } else { $aValues = array(); - foreach (explode(",", $Values) as $sVal) + foreach (explode(",", $this->m_values) as $sVal) { $sVal = trim($sVal); $sKey = $sVal; @@ -145,10 +152,6 @@ class ValueSetEnum extends ValueSetDefinition } } $this->m_aValues = $aValues; - } - - protected function LoadValues($aArgs) - { return true; } } @@ -166,20 +169,25 @@ class ValueSetEnum extends ValueSetDefinition */ class ValueSetEnumClasses extends ValueSetEnum { - public function __construct($sCategory = '', $sAdditionalValues = '') - { - // First, build it from the series of additional values - parent::__construct($sAdditionalValues); + protected $m_sCategories; - // Second: add the list of classes - foreach (MetaModel::GetClasses($sCategory) as $sClass) - { - $this->m_aValues[$sClass] = MetaModel::GetName($sClass); - } + public function __construct($sCategories = '', $sAdditionalValues = '') + { + $this->m_sCategories = $sCategories; + parent::__construct($sAdditionalValues); } protected function LoadValues($aArgs) - { + { + // First, get the additional values + parent::LoadValues($aArgs); + + // Then, add the classes from the category definition + foreach (MetaModel::GetClasses($this->m_sCategories) as $sClass) + { + $this->m_aValues[$sClass] = MetaModel::GetName($sClass); + } + return true; } } diff --git a/pages/testlist.inc.php b/pages/testlist.inc.php index 862e41206..42d137528 100644 --- a/pages/testlist.inc.php +++ b/pages/testlist.inc.php @@ -582,7 +582,6 @@ class TestMyBizModel extends TestBizModel $this->test_relations(); $this->test_linkedset(); $this->test_object_lifecycle(); - return true; } } @@ -929,7 +928,7 @@ class TestBulkChangeOnFarm extends TestBizModel $aRes = $oBulk->Process($oMyChange); print_r($aRes); - return true; + return; $oRawData = array( 'Mammal', @@ -978,7 +977,6 @@ class TestFullTextSearchOnFarm extends MyFarm $oSearch->AddCondition_FullText('manof'); //$oResultSet = new DBObjectSet($oSearch); $this->search_and_show_list($oSearch); - return true; } } @@ -1103,7 +1101,6 @@ class TestItopEfficiency extends TestBizModel $aData[] = $aValues; } echo MyHelpers::make_table_from_assoc_array($aData); - return true; } } @@ -1169,8 +1166,6 @@ class TestItopWebServices extends TestWebServices { $this->DoExecSingleLoad($aLoadSpec); } - - return true; } } @@ -1178,14 +1173,14 @@ class TestItopWebServices extends TestWebServices $aWebServices = array( array( 'verb' => 'GetVersion', - 'expected result' => true, + 'expected result' => '0.8', 'explain result' => 'n/a', 'args' => array(), ), array( 'verb' => 'CreateIncidentTicket', 'expected result' => true, - 'explain result' => 'ok, but link attribute unknown', + 'explain result' => 'link attribute unknown + a CI not found', 'args' => array( 'admin', /* sLogin */ 'admin', /* sPassword */ @@ -1195,7 +1190,7 @@ $aWebServices = array( 'very grave', /* sImpact */ new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */ new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 2))), /* aCustomerDesc */ - new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aWorkgroupDesc */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'FLS Desktop'))), /* aWorkgroupDesc */ array( new SOAPLinkCreationSpec( 'logInfra', @@ -1207,14 +1202,38 @@ $aWebServices = array( array(new SOAPSearchCondition('name', 'Router03')), array(new SOAPAttributeValue('impact', 'who cares')) ), + new SOAPLinkCreationSpec( + 'bizDevice', + array(new SOAPSearchCondition('name', 'thisone')), + array(new SOAPAttributeValue('impact', 'our lives')) + ), ), /* aImpact */ 'low' /* sSeverity */ ), ), array( 'verb' => 'CreateIncidentTicket', - 'expected result' => true, - 'explain result' => 'ok, but CI unknown', + 'expected result' => false, + 'explain result' => 'caller not specified', + 'args' => array( + 'admin', /* sLogin */ + 'admin', /* sPassword */ + 'Desktop', /* sType */ + 'PC burning', /* sDescription */ + 'The power supply suddenly started to warm up', /* sInitialSituation */ + 'The agent could not do his job', /* sImpact */ + null, /* aCallerDesc */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 2))), /* aCustomerDesc */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'FLS Desktop'))), /* aWorkgroupDesc */ + array( + ), /* aImpact */ + 'low' /* sSeverity */ + ), + ), + array( + 'verb' => 'CreateIncidentTicket', + 'expected result' => false, + 'explain result' => 'wrong condition on CI to attach', 'args' => array( 'admin', /* sLogin */ 'admin', /* sPassword */ @@ -1224,11 +1243,11 @@ $aWebServices = array( 'The agent could not do his job', /* sImpact */ new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */ new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 2))), /* aCustomerDesc */ - new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aWorkgroupDesc */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'FLS Desktop'))), /* aWorkgroupDesc */ array( new SOAPLinkCreationSpec( 'logInfra', - array(new SOAPSearchCondition('id', 99999)), + array(new SOAPSearchCondition('dummyfiltercode', 2)), array(new SOAPAttributeValue('impact', 'very critical')) ), ), /* aImpact */ @@ -1237,8 +1256,8 @@ $aWebServices = array( ), array( 'verb' => 'CreateIncidentTicket', - 'expected result' => false, - 'explain result' => 'ok, no CI to attach', + 'expected result' => true, + 'explain result' => 'no CI to attach (empty array)', 'args' => array( 'admin', /* sLogin */ 'admin', /* sPassword */ @@ -1248,12 +1267,30 @@ $aWebServices = array( 'Could not talk to my wife', /* sImpact */ new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */ new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 2))), /* aCustomerDesc */ - new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aWorkgroupDesc */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'FLS Desktop'))), /* aWorkgroupDesc */ array( ), /* aImpact */ 'low' /* sSeverity */ ), ), + array( + 'verb' => 'CreateIncidentTicket', + 'expected result' => true, + 'explain result' => 'no CI to attach (null)', + 'args' => array( + 'admin', /* sLogin */ + 'admin', /* sPassword */ + 'Network', /* sType */ + 'Houston not reachable', /* sDescription */ + 'Tried to join the shuttle', /* sInitialSituation */ + 'Could not talk to my wife', /* sImpact */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 2))), /* aCustomerDesc */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'FLS Desktop'))), /* aWorkgroupDesc */ + null, /* aImpact */ + 'low' /* sSeverity */ + ), + ), array( 'verb' => 'CreateIncidentTicket', 'expected result' => false, @@ -1267,12 +1304,31 @@ $aWebServices = array( 'Could not talk to my wife', /* sImpact */ new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1000))), /* aCallerDesc */ new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 2))), /* aCustomerDesc */ - new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aWorkgroupDesc */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'FLS Desktop'))), /* aWorkgroupDesc */ array( ), /* aImpact */ 'low' /* sSeverity */ ), ), + array( + 'verb' => 'CreateIncidentTicket', + 'expected result' => false, + 'explain result' => 'wrong values for type and severity', + 'args' => array( + 'admin', /* sLogin */ + 'admin', /* sPassword */ + 'my type', /* sType */ + 'Houston not reachable', /* sDescription */ + 'Tried to join the shuttle', /* sInitialSituation */ + 'Could not talk to my wife', /* sImpact */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 2))), /* aCustomerDesc */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'FLS Desktop'))), /* aWorkgroupDesc */ + array( + ), /* aImpact */ + 'my severity' /* sSeverity */ + ), + ), array( 'verb' => 'CreateIncidentTicket', 'expected result' => false, @@ -1286,7 +1342,7 @@ $aWebServices = array( 'Could not talk to my wife', /* sImpact */ new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */ new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 2))), /* aCustomerDesc */ - new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aWorkgroupDesc */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'FLS Desktop'))), /* aWorkgroupDesc */ array( ), /* aImpact */ 'low' /* sSeverity */ @@ -1305,7 +1361,7 @@ $aWebServices = array( 'Could not talk to my wife', /* sImpact */ new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aCallerDesc */ new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 2))), /* aCustomerDesc */ - new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* aWorkgroupDesc */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'FLS Desktop'))), /* aWorkgroupDesc */ array( ), /* aImpact */ 'low' /* sSeverity */ @@ -1321,6 +1377,8 @@ class TestSoap extends TestSoapWebService protected function DoExecute() { + echo "

    Note: You may also want to try the sample SOAP client itopsoap.examples.php

    \n"; + global $aSOAPMapping; // this file is generated dynamically with location = here @@ -1331,7 +1389,6 @@ class TestSoap extends TestSoapWebService ( $sWsdlUri, array( - //'uri' => 'http://soap-itop/', 'classmap' => $aSOAPMapping, 'trace' => 1, ) @@ -1348,6 +1405,7 @@ class TestSoap extends TestSoapWebService foreach ($aWebServices as $iPos => $aWebService) { echo "

    SOAP call #$iPos ".$aWebService['explain result']."

    \n"; + try { $oRes = call_user_func_array(array($this->m_SoapClient, $aWebService['verb']), $aWebService['args']); @@ -1359,7 +1417,7 @@ class TestSoap extends TestSoapWebService print "Response: \n".htmlspecialchars($this->m_SoapClient->__getLastResponse())."\n"; print ""; print "Response in HTML:

    ".$this->m_SoapClient->__getLastResponse()."

    "; - return false; + throw $e; } echo "
    \n";
    @@ -1370,9 +1428,20 @@ class TestSoap extends TestSoapWebService
     			print "Request: \n".htmlspecialchars($this->m_SoapClient->__getLastRequest()) ."\n"; 
     			print "Response: \n".htmlspecialchars($this->m_SoapClient->__getLastResponse())."\n"; 
     			print "
    "; - } - return true; + if ($oRes instanceof SOAPResult) + { + $res = $oRes->status; + } + else + { + $res = $oRes; + } + if ($res != $aWebService['expected result']) + { + throw new UnitTestException("Expecting result '{$aWebService['expected result']}', but got '$res'"); + } + } } } diff --git a/webservices/itop.wsdl.tpl b/webservices/itop.wsdl.tpl index 32f6edf19..c3e4289e3 100644 --- a/webservices/itop.wsdl.tpl +++ b/webservices/itop.wsdl.tpl @@ -1,6 +1,6 @@ - + @@ -8,9 +8,13 @@ + - + @@ -21,14 +25,23 @@ + + - + @@ -39,6 +52,9 @@ + @@ -53,6 +69,9 @@ + @@ -65,6 +84,9 @@ + @@ -72,7 +94,7 @@ - + @@ -83,6 +105,10 @@ + @@ -96,6 +122,12 @@ + @@ -129,17 +161,17 @@ - + Get the current version of Itop As this service is very simple, it is a test to get trained for more complex operations - + --> - + Create a ticket, return information about reconciliation on external keys and the created ticket - + --> @@ -166,6 +198,9 @@ + + ITop is the central solution for managing your IT infrastructure + diff --git a/webservices/itopsoap.examples.php b/webservices/itopsoap.examples.php new file mode 100644 index 000000000..c93be4333 --- /dev/null +++ b/webservices/itopsoap.examples.php @@ -0,0 +1,69 @@ + 1, + 'classmap' => $aSOAPMapping, // defined in itopsoaptypes.class.inc.php + ) +); + +try +{ + // The most simple service, returning a string + // + $sServerVersion = $oSoapClient->GetVersion(); + echo "

    GetVersion() returned $sServerVersion

    "; + + // More complex ones, returning a SOAPResult structure + // (run the page to know more about the returned data) + // + $oRes = $oSoapClient->CreateIncidentTicket + ( + 'admin', /* login */ + 'admin', /* password */ + 'Server', /* type */ + 'Email server down', /* description */ + 'HW found shutdown', /* initial situation */ + 'Email not working', /* impact */ + null, /* caller */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('name', 'Demo'))), /* customer */ + new SOAPExternalKeySearch(array(new SOAPSearchCondition('id', 1))), /* workgroup */ + array( + new SOAPLinkCreationSpec( + 'bizDevice', + array(new SOAPSearchCondition('name', 'Router03')), + array(new SOAPAttributeValue('impact', 'root cause')) + ), + new SOAPLinkCreationSpec( + 'bizServer', + array(new SOAPSearchCondition('name', 'Server01')), + array(new SOAPAttributeValue('impact', '')) + ), + ), /* impact */ + 'high' /* severity */ + ); + + echo "

    CreateIncidentTicket() returned:\n"; + echo "

    \n";
    +	print_r($oRes);
    +	echo "
    \n"; + echo "

    \n"; +} +catch(SoapFault $e) +{ + echo "

    SoapFault Exception: {$e->getMessage()}

    \n"; + echo "

    Request

    \n"; + echo "
    \n"; 
    +	echo htmlspecialchars($oSoapClient->__getLastRequest())."\n"; 
    +	echo "
    "; + echo "

    Response

    "; + echo $oSoapClient->__getLastResponse()."\n"; +} +?> diff --git a/webservices/webservices.class.inc.php b/webservices/webservices.class.inc.php index e17163ed0..5595f7113 100644 --- a/webservices/webservices.class.inc.php +++ b/webservices/webservices.class.inc.php @@ -223,6 +223,14 @@ class WebServices protected function LogUsage($sVerb, $oRes) { $oLog = new EventWebService(); + if ($oRes->IsOk()) + { + $oLog->Set('message', $sVerb.' was successfully invoked'); + } + else + { + $oLog->Set('message', $sVerb.' returned errors'); + } $oLog->Set('userinfo', UserRights::GetUser()); $oLog->Set('verb', $sVerb); $oLog->Set('result', $oRes->IsOk()); @@ -233,6 +241,29 @@ class WebServices $oLog->DBInsertNoReload(); } + /** + * Helper to set a scalar attribute + * + * @param string sAttCode + * @param scalar value + * @param DBObject oTargetObj + * @param WebServiceResult oRes + * + */ + protected function MyObjectSetScalar($sAttCode, $sParamName, $value, &$oTargetObj, &$oRes) + { + if ($oTargetObj->CheckValue($sAttCode, $value)) + { + $oTargetObj->Set($sAttCode, $value); + } + else + { + $aAllowedValues = MetaModel::GetAllowedValues_att(get_class($oTargetObj), $sAttCode); + $sValues = implode(', ', $aAllowedValues); + $oRes->LogError("Parameter $sParamName: found '$value' while expecting a value in {".$sValues."}"); + } + } + /** * Helper to set an external key * @@ -242,14 +273,28 @@ class WebServices * @param WebServiceResult oRes * */ - protected function SetExternalKey($sAttCode, $aExtKeyDesc, &$oTargetObj, &$oRes) + protected function MyObjectSetExternalKey($sAttCode, $sParamName, $aExtKeyDesc, &$oTargetObj, &$oRes) { $oExtKey = MetaModel::GetAttributeDef(get_class($oTargetObj), $sAttCode); $bIsMandatory = !$oExtKey->IsNullAllowed(); + + if (is_null($aExtKeyDesc)) + { + if ($bIsMandatory) + { + $oRes->LogError("Parameter $sParamName: found null for a mandatory key"); + } + else + { + // skip silently + return; + } + } + if (count($aExtKeyDesc) == 0) { - $oRes->LogIssue("Ext key $sAttCode: no data was given to give a value to the key", $bIsMandatory); + $oRes->LogIssue("Parameter $sParamName: no search condition has been specified", $bIsMandatory); return; } @@ -259,7 +304,8 @@ class WebServices { if (!MetaModel::IsValidFilterCode($sKeyClass, $sForeignAttCode)) { - $sMsg = "Ext key $sAttCode: '$sForeignAttCode' is not a valid filter code for class '$sKeyClass'"; + $aCodes = array_keys(MetaModel::GetClassFilterDefs($sKeyClass)); + $sMsg = "Parameter $sParamName: '$sForeignAttCode' is not a valid filter code for class '$sKeyClass', expecting a value in {".implode(', ', $aCodes)."}"; $oRes->LogIssue($sMsg, $bIsMandatory); } // The foreign attribute is one of our reconciliation key @@ -269,7 +315,7 @@ class WebServices switch($oExtObjects->Count()) { case 0: - $sMsg = "External key $sAttCode could not be found (searched: '".$oReconFilter->ToOQL()."')"; + $sMsg = "Parameter $sParamName: no match (searched: '".$oReconFilter->ToOQL()."')"; $oRes->LogIssue($sMsg, $bIsMandatory); break; case 1: @@ -280,11 +326,11 @@ class WebServices // Report it (no need to report if the object already had this value if (array_key_exists($sAttCode, $oTargetObj->ListChanges())) { - $oRes->LogInfo("$sAttCode has been set to ".$oForeignObj->GetKey()); + $oRes->LogInfo("Parameter $sParamName: found match ".get_class($oForeignObj)."::".$oForeignObj->GetKey()." '".$oForeignObj->GetName()."'"); } break; default: - $sMsg = "Found ".$oExtObjects->Count()." matches for external key $sAttCode (searched: '".$oReconFilter->ToOQL()."')"; + $sMsg = "Parameter $sParamName: Found ".$oExtObjects->Count()." matches (searched: '".$oReconFilter->ToOQL()."')"; $oRes->LogIssue($sMsg, $bIsMandatory); } } @@ -300,7 +346,7 @@ class WebServices * * @return array List of objects that could not be found */ - protected function AddLinkedObjects($sLinkAttCode, $sLinkedClass, $aLinkList, &$oTargetObj, &$oRes) + protected function AddLinkedObjects($sLinkAttCode, $sParamName, $sLinkedClass, $aLinkList, &$oTargetObj, &$oRes) { $oLinkAtt = MetaModel::GetAttributeDef(get_class($oTargetObj), $sLinkAttCode); $sLinkClass = $oLinkAtt->GetLinkedClass(); @@ -308,22 +354,28 @@ class WebServices $aItemsFound = array(); $aItemsNotFound = array(); + + if (is_null($aLinkList)) + { + return $aItemsNotFound; + } + foreach ($aLinkList as $aItemData) { if (!array_key_exists('class', $aItemData)) { - $oRes->LogWarning("Linked object descriptor: missing 'class' specification"); + $oRes->LogWarning("Parameter $sParamName: missing 'class' specification"); continue; // skip } $sTargetClass = $aItemData['class']; if (!MetaModel::IsValidClass($sTargetClass)) { - $oRes->LogError("Invalid class $sTargetClass for impacted item"); + $oRes->LogError("Parameter $sParamName: invalid class '$sTargetClass'"); continue; // skip } if (!MetaModel::IsParentClass($sLinkedClass, $sTargetClass)) { - $oRes->LogError("$sTargetClass is not a child class of $sLinkedClass"); + $oRes->LogError("Parameter $sParamName: '$sTargetClass' is not a child class of '$sLinkedClass'"); continue; // skip } $oReconFilter = new CMDBSearchFilter($sTargetClass); @@ -332,8 +384,9 @@ class WebServices { if (!MetaModel::IsValidFilterCode($sTargetClass, $sAttCode)) { - $oRes->LogError("Invalid filter code $sAttCode for class $sTargetClass"); - continue; // skip + $aCodes = array_keys(MetaModel::GetClassFilterDefs($sTargetClass)); + $oRes->LogError("Parameter $sParamName: '$sAttCode' is not a valid filter code for class '$sTargetClass', expecting a value in {".implode(', ', $aCodes)."}"); + continue 2; // skip the entire item } $aCIStringDesc[] = "$sAttCode: $value"; @@ -355,7 +408,7 @@ class WebServices switch($oExtObjects->Count()) { case 0: - $oRes->LogWarning("Object to link $sLinkedClass / $sItemDesc could not be found (searched: '".$oReconFilter->ToOQL()."')"); + $oRes->LogWarning("Parameter $sParamName: object to link $sLinkedClass / $sItemDesc could not be found (searched: '".$oReconFilter->ToOQL()."')"); $aItemsNotFound[] = $sItemDesc; break; case 1: @@ -366,7 +419,7 @@ class WebServices ); break; default: - $oRes->LogWarning("Found ".$oExtObjects->Count()." matches for external key $sAttCode (searched: '".$oReconFilter->ToOQL()."')"); + $oRes->LogWarning("Parameter $sParamName: Found ".$oExtObjects->Count()." matches for item '$sItemDesc' (searched: '".$oReconFilter->ToOQL()."')"); $aItemsNotFound[] = $sItemDesc; } } @@ -382,7 +435,7 @@ class WebServices { if(!MetaModel::IsValidAttCode($sLinkClass, $sKey)) { - $oRes->LogWarning("Attaching item '".$aItemData['desc']."', the attribute code '$sKey' is not valid ; check the class '$sLinkClass'"); + $oRes->LogWarning("Parameter $sParamName: Attaching item '".$aItemData['desc']."', the attribute code '$sKey' is not valid ; check the class '$sLinkClass'"); } else { @@ -398,9 +451,28 @@ class WebServices return $aItemsNotFound; } + protected function MyObjectInsert($oTargetObj, $sResultLabel, $oChange, &$oRes) + { + if ($oRes->IsOk()) + { + if ($oTargetObj->CheckToInsert()) + { + $iId = $oTargetObj->DBInsertTrackedNoReload($oChange); + $oRes->LogInfo("Created object ".get_class($$oTargetObj)."::$iId"); + $oRes->AddResultObject($sResultLabel, $oTargetObj); + } + else + { + $oRes->LogError("The ticket could not be created due to forbidden values (or inconsistent values)"); + } + } + } + static protected function SoapStructToExternalKeySearch(SoapExternalKeySearch $oExternalKeySearch) { + if (is_null($oExternalKeySearch)) return null; + $aRes = array(); foreach($oExternalKeySearch->conditions as $oSearchCondition) { @@ -504,36 +576,26 @@ class WebServices $iChangeId = $oMyChange->DBInsertNoReload(); $oNewTicket = MetaModel::NewObject('bizIncidentTicket'); - $oNewTicket->Set('type', $sType); - $oNewTicket->Set('title', $sDescription); - $oNewTicket->Set('initial_situation', $sInitialSituation); - $oNewTicket->Set('severity', $sSeverity); + $this->MyObjectSetScalar('type', 'type', $sType, $oNewTicket, $oRes); + $this->MyObjectSetScalar('title', 'title', $sDescription, $oNewTicket, $oRes); + $this->MyObjectSetScalar('initial_situation', 'initialsituation', $sInitialSituation, $oNewTicket, $oRes); + $this->MyObjectSetScalar('severity', 'severity', $sSeverity, $oNewTicket, $oRes); - $this->SetExternalKey('org_id', $aCustomerDesc, $oNewTicket, $oRes); - $this->SetExternalKey('caller_id', $aCallerDesc, $oNewTicket, $oRes); - $this->SetExternalKey('workgroup_id', $aWorkgroupDesc, $oNewTicket, $oRes); + $this->MyObjectSetExternalKey('org_id', 'customer', $aCustomerDesc, $oNewTicket, $oRes); + $this->MyObjectSetExternalKey('caller_id', 'caller', $aCallerDesc, $oNewTicket, $oRes); + $this->MyObjectSetExternalKey('workgroup_id', 'workgroup', $aWorkgroupDesc, $oNewTicket, $oRes); - $aDevicesNotFound = $this->AddLinkedObjects('impacted_infra_manual', 'logInfra', $aImpactedCIs, $oNewTicket, $oRes); + $aDevicesNotFound = $this->AddLinkedObjects('impacted_infra_manual', 'impacted_cis', 'logInfra', $aImpactedCIs, $oNewTicket, $oRes); if (count($aDevicesNotFound) > 0) { - $oNewTicket->Set('impact', $sImpact.' - Related CIs: '.implode(', ', $aDevicesNotFound)); + $this->MyObjectSetScalar('impact', 'n/a', $sImpact.' - Related CIs: '.implode(', ', $aDevicesNotFound), $oNewTicket, $oRes); } else { - $oNewTicket->Set('impact', $sImpact); - } - - if (!$oNewTicket->CheckToInsert()) - { - $oRes->LogError("The ticket could not be created due to forbidden values (or inconsistent values)"); - } - - if ($oRes->IsOk()) - { - $iId = $oNewTicket->DBInsertTrackedNoReload($oMyChange); - $oRes->LogInfo("Created ticket #$iId"); - $oRes->AddResultObject('created', $oNewTicket); + $this->MyObjectSetScalar('impact', 'n/a', $sImpact, $oNewTicket, $oRes); } + + $this->MyObjectInsert($oNewTicket, 'created', $oMyChange, $oRes); } catch (CoreException $e) { From cf92f5df2d8494cc4cca74549eb8054316c63796 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Wed, 13 Jan 2010 16:14:45 +0000 Subject: [PATCH 170/227] Profiles reviewed (+ secured the setup procedure against wrong class names) SVN:trunk[247] --- .../userrightsprofile.class.inc.php | 43 +++++++++++++------ 1 file changed, 31 insertions(+), 12 deletions(-) diff --git a/addons/userrights/userrightsprofile.class.inc.php b/addons/userrights/userrightsprofile.class.inc.php index d6c33e24c..c3455ac6f 100644 --- a/addons/userrights/userrightsprofile.class.inc.php +++ b/addons/userrights/userrightsprofile.class.inc.php @@ -557,7 +557,7 @@ class URP_ClassProjection extends UserRightsBaseClass MetaModel::Init_AddAttribute(new AttributeExternalKey("dimensionid", array("targetclass"=>"URP_Dimensions", "jointype"=> "", "label"=>"Dimension", "description"=>"application dimension", "allowed_values"=>null, "sql"=>"dimensionid", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("dimension", array("label"=>"Dimension", "description"=>"application dimension", "allowed_values"=>null, "extkey_attcode"=> 'dimensionid', "target_attcode"=>"name"))); - MetaModel::Init_AddAttribute(new AttributeString("class", array("label"=>"Class", "description"=>"Target class", "allowed_values"=>null, "sql"=>"class", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeClass("class", array("label"=>"Class", "description"=>"Target class", "class_category"=>"", "more_values"=>"", "sql"=>"class", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("value", array("label"=>"Value expression", "description"=>"OQL expression (using \$this) | constant | | +attribute code", "allowed_values"=>null, "sql"=>"value", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("attribute", array("label"=>"Attribute", "description"=>"Target attribute code (optional)", "allowed_values"=>null, "sql"=>"attribute", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); @@ -642,7 +642,7 @@ class URP_ActionGrant extends UserRightsBaseClass // Common to all grant classes (could be factorized by class inheritence, but this has to be benchmarked) MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass"=>"URP_Profiles", "jointype"=> "", "label"=>"Profile", "description"=>"usage profile", "allowed_values"=>null, "sql"=>"profileid", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("profile", array("label"=>"Profile", "description"=>"usage profile", "allowed_values"=>null, "extkey_attcode"=> 'profileid', "target_attcode"=>"name"))); - MetaModel::Init_AddAttribute(new AttributeString("class", array("label"=>"Class", "description"=>"class name", "allowed_values"=>null, "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeClass("class", array("label"=>"Class", "description"=>"Target class", "class_category"=>"", "more_values"=>"", "sql"=>"class", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("label"=>"Permission", "description"=>"allowed or not allowed?", "allowed_values"=>new ValueSetEnum('yes,no'), "sql"=>"permission", "default_value"=>"yes", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("action", array("label"=>"Action", "description"=>"operations to perform on the given class", "allowed_values"=>null, "sql"=>"action", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); @@ -691,7 +691,7 @@ class URP_StimulusGrant extends UserRightsBaseClass // Common to all grant classes (could be factorized by class inheritence, but this has to be benchmarked) MetaModel::Init_AddAttribute(new AttributeExternalKey("profileid", array("targetclass"=>"URP_Profiles", "jointype"=> "", "label"=>"Profile", "description"=>"usage profile", "allowed_values"=>null, "sql"=>"profileid", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("profile", array("label"=>"Profile", "description"=>"usage profile", "allowed_values"=>null, "extkey_attcode"=> 'profileid', "target_attcode"=>"name"))); - MetaModel::Init_AddAttribute(new AttributeString("class", array("label"=>"Class", "description"=>"class name", "allowed_values"=>null, "sql"=>"class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeClass("class", array("label"=>"Class", "description"=>"Target class", "class_category"=>"", "more_values"=>"", "sql"=>"class", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeEnum("permission", array("label"=>"Permission", "description"=>"allowed or not allowed?", "allowed_values"=>new ValueSetEnum('yes,no'), "sql"=>"permission", "default_value"=>"yes", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("stimulus", array("label"=>"Stimulus", "description"=>"stimulus code", "allowed_values"=>null, "sql"=>"action", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); @@ -1398,7 +1398,7 @@ class SetupITILProfiles 'bizOrganization', ), 'Documentation' => array( - 'bizDocVersion', + 'bizDocument', 'lnkDocumentRealObject', 'lnkDocumentContract', 'lnkDocumentError', @@ -1406,8 +1406,8 @@ class SetupITILProfiles 'Configuration' => array( 'logRealObject', 'lnkContactRealObject', - 'lnkInterfaces', - 'ClientServerLinks', +// 'lnkInterfaces', + 'lnkClientServer', 'lnkInfraGrouping', ), 'Incident' => array( @@ -1464,7 +1464,7 @@ class SetupITILProfiles */ 'Service Desk Agent' => array( 'description' => 'Person in charge of creating incident reports', - 'write_modules' => 'Documentation,Incident,Call', + 'write_modules' => 'Incident,Call', 'stimuli' => array( 'bizServer' => 'none', 'bizContract' => 'none', @@ -1475,7 +1475,7 @@ class SetupITILProfiles ), 'Support Agent' => array( 'description' => 'Person analyzing and solving the current incidents or problems', - 'write_modules' => 'Documentation,Incident,Problem', + 'write_modules' => 'Incident,Problem', 'stimuli' => array( 'bizIncidentTicket' => 'any', //'bizIncidentTicket' => 'ev_assign,ev_reassign,ev_start_working,ev_close', @@ -1483,7 +1483,7 @@ class SetupITILProfiles ), 'Change Implementor' => array( 'description' => 'Person executing the changes', - 'write_modules' => 'Documentation,Configuration,Change', + 'write_modules' => 'Change', 'stimuli' => array( 'bizServer' => 'none', 'bizContract' => 'none', @@ -1493,7 +1493,7 @@ class SetupITILProfiles ), 'Change Supervisor' => array( 'description' => 'Person responsible for the overall change execution', - 'write_modules' => 'Documentation,Change', + 'write_modules' => 'Change', 'stimuli' => array( 'bizServer' => 'none', 'bizContract' => 'none', @@ -1503,7 +1503,7 @@ class SetupITILProfiles ), 'Change Approver' => array( 'description' => 'Person who could be impacted by some changes', - 'write_modules' => 'Documentation,Change', + 'write_modules' => 'Change', 'stimuli' => array( 'bizServer' => 'none', 'bizContract' => 'none', @@ -1513,7 +1513,7 @@ class SetupITILProfiles ), 'Service Manager' => array( 'description' => 'Person responsible for the service delivered to the [internal] customer', - 'write_modules' => 'Documentation,Service', + 'write_modules' => 'Service', 'stimuli' => array( 'bizServer' => 'none', 'bizContract' => 'any', @@ -1522,6 +1522,12 @@ class SetupITILProfiles 'bizChangeTicket' => 'none', ), ), + 'Document author' => array( + 'description' => 'Any person who could contribute to documentation', + 'write_modules' => 'Documentation', + 'stimuli' => array( + ), + ), ); protected static function DoCreateClassProjection($iDimension, $sClass) @@ -1632,6 +1638,10 @@ class SetupITILProfiles } foreach ($aWriteableClasses as $sClass) { + if (!MetaModel::IsValidClass($sClass)) + { + throw new CoreException("Invalid class name '$sClass'"); + } self::DoCreateActionGrant($iProfile, UR_ACTION_MODIFY, $sClass); self::DoCreateActionGrant($iProfile, UR_ACTION_DELETE, $sClass); self::DoCreateActionGrant($iProfile, UR_ACTION_BULK_MODIFY, $sClass); @@ -1641,6 +1651,11 @@ class SetupITILProfiles // Grant stimuli for given classes foreach ($aStimuli as $sClass => $sAllowedStimuli) { + if (!MetaModel::IsValidClass($sClass)) + { + throw new CoreException("Invalid class name '$sClass'"); + } + if ($sAllowedStimuli == 'any') { $aAllowedStimuli = array_keys(MetaModel::EnumStimuli($sClass)); @@ -1669,6 +1684,10 @@ class SetupITILProfiles foreach($aClass as $sClass) { + if (!MetaModel::IsValidClass($sClass)) + { + throw new CoreException("Invalid class name '$sClass'"); + } self::DoCreateClassProjection($iDimension, $sClass); } } From e621c65910d19d732f42082e29b6952765f7aadf Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Wed, 13 Jan 2010 17:36:35 +0000 Subject: [PATCH 171/227] Changes on the data model SVN:trunk[248] --- .../userrightsprofile.class.inc.php | 2 +- business/ChangeMgmt.business.php | 26 ++++--- business/ServiceMgmt.business.php | 2 +- business/itop.business.class.inc.php | 71 +++---------------- 4 files changed, 30 insertions(+), 71 deletions(-) diff --git a/addons/userrights/userrightsprofile.class.inc.php b/addons/userrights/userrightsprofile.class.inc.php index c3455ac6f..396766dba 100644 --- a/addons/userrights/userrightsprofile.class.inc.php +++ b/addons/userrights/userrightsprofile.class.inc.php @@ -1498,7 +1498,7 @@ class SetupITILProfiles 'bizServer' => 'none', 'bizContract' => 'none', 'bizIncidentTicket' => 'none', - 'bizChangeTicket' => 'ev_validate,ev_reject,ev_reopen,ev_finish', + 'bizChangeTicket' => 'ev_assign,ev_validate,ev_reject,ev_reopen,ev_finish', ), ), 'Change Approver' => array( diff --git a/business/ChangeMgmt.business.php b/business/ChangeMgmt.business.php index 415ce72a3..e7ad10ffc 100644 --- a/business/ChangeMgmt.business.php +++ b/business/ChangeMgmt.business.php @@ -32,13 +32,13 @@ class bizChangeTicket extends cmdbAbstractObject MetaModel::Init_AddAttribute(new AttributeString("reason", array("label"=>"Reason For Change", "description"=>"Reason for the Change", "allowed_values"=>null, "sql"=>"reason", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeExternalKey("requestor_id", array("targetclass"=>"bizPerson", "jointype"=> "", "label"=>"Requestor", "description"=>"who is requesting this change", "allowed_values"=>null, "sql"=>"requestor_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("requestor_id", array("targetclass"=>"bizPerson", "jointype"=> "", "label"=>"Requestor", "description"=>"who is requesting this change", "allowed_values"=>new ValueSetObjects('SELECT bizPerson AS p WHERE p.org_id = :this->org_id'), "sql"=>"requestor_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array("org_id")))); MetaModel::Init_AddAttribute(new AttributeExternalField("requestor_mail", array("label"=>"Requestor", "description"=>"mail of user requesting this change", "allowed_values"=>null, "extkey_attcode"=> 'requestor_id', "target_attcode"=>"email"))); MetaModel::Init_AddAttribute(new AttributeExternalKey("org_id", array("targetclass"=>"bizOrganization", "label"=>"Customer", "description"=>"who is impacted by the ticket", "allowed_values"=>null, "sql"=>"customer", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("customer_name", array("label"=>"Customer", "description"=>"Name of the customer impacted by this ticket", "allowed_values"=>null, "extkey_attcode"=> 'org_id', "target_attcode"=>"name"))); - MetaModel::Init_AddAttribute(new AttributeEnum("ticket_status", array("label"=>"Status", "description"=>"Status of the ticket", "allowed_values"=>new ValueSetEnum("New, Validated,Rejected,PlannedScheduled,Approved,NotApproved,Implemented,Monitored, Closed"), "sql"=>"change_status", "default_value"=>"New", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("ticket_status", array("label"=>"Status", "description"=>"Status of the ticket", "allowed_values"=>new ValueSetEnum("New, Validated,Rejected,Assigned,PlannedScheduled,Approved,NotApproved,Implemented,Monitored, Closed"), "sql"=>"change_status", "default_value"=>"New", "is_null_allowed"=>false, "depends_on"=>array()))); // SetPossibleValues("status",array("Open","Monitored","Closed")); MetaModel::Init_AddAttribute(new AttributeDate("creation_date", array("label"=>"Creation Date", "description"=>"Change creation date", "allowed_values"=>null, "sql"=>"creation_date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); @@ -50,17 +50,17 @@ class bizChangeTicket extends cmdbAbstractObject MetaModel::Init_AddAttribute(new AttributeString("impact", array("label"=>"Risk Assessment", "description"=>"Impact of the change", "allowed_values"=>null, "sql"=>"impact", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalKey("workgroup_id", array("targetclass"=>"bizWorkgroup", "jointype"=> "", "label"=>"Workgroup", "description"=>"which workgroup is owning ticket", "allowed_values"=>null, "sql"=>"workgroup_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("workgroup_name", array("label"=>"Workgroup", "description"=>"name of workgroup managing the Ticket", "allowed_values"=>null, "extkey_attcode"=> 'workgroup_id', "target_attcode"=>"name"))); - MetaModel::Init_AddAttribute(new AttributeExternalKey("agent_id", array("targetclass"=>"bizPerson", "jointype"=> "", "label"=>"Agent", "description"=>"who is managing the ticket", "allowed_values"=>null, "sql"=>"agent_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array('workgroup_id')))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("agent_id", array("targetclass"=>"bizPerson", "jointype"=> "", "label"=>"Agent", "description"=>"who is managing the ticket", "allowed_values"=>new ValueSetObjects('SELECT bizPerson AS p JOIN lnkContactTeam AS l ON l.contact_id=p.id JOIN bizTeam AS t ON l.team_id=t.id JOIN bizWorkgroup AS w ON w.team_id=t.id WHERE w.id = :this->workgroup_id'), "sql"=>"agent_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array('workgroup_id')))); MetaModel::Init_AddAttribute(new AttributeExternalField("agent_mail", array("label"=>"Agent", "description"=>"name of agent managing the Ticket", "allowed_values"=>null, "extkey_attcode"=> 'agent_id', "target_attcode"=>"email"))); MetaModel::Init_AddAttribute(new AttributeExternalKey("supervisorgroup_id", array("targetclass"=>"bizWorkgroup", "jointype"=> "", "label"=>"Supervisor Group", "description"=>"which workgroup is supervising ticket", "allowed_values"=>null, "sql"=>"supervisorgroup_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("supervisorgroup_name", array("label"=>"Supervisor Group", "description"=>"name of the group supervising the Ticket", "allowed_values"=>null, "extkey_attcode"=> 'supervisorgroup_id', "target_attcode"=>"name"))); - MetaModel::Init_AddAttribute(new AttributeExternalKey("supervisor_id", array("targetclass"=>"bizPerson", "jointype"=> "", "label"=>"Supervisor", "description"=>"who is managing the ticket", "allowed_values"=>null, "sql"=>"supervisor_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array('supervisorgroup_id')))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("supervisor_id", array("targetclass"=>"bizPerson", "jointype"=> "", "label"=>"Supervisor", "description"=>"who is managing the ticket", "allowed_values"=>new ValueSetObjects('SELECT bizPerson AS p JOIN lnkContactTeam AS l ON l.contact_id=p.id JOIN bizTeam AS t ON l.team_id=t.id JOIN bizWorkgroup AS w ON w.team_id=t.id WHERE w.id = :this->supervisorgroup_id'), "sql"=>"supervisor_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array('supervisorgroup_id')))); MetaModel::Init_AddAttribute(new AttributeExternalField("supervisor_mail", array("label"=>"Supervisor", "description"=>"name of agent supervising the Ticket", "allowed_values"=>null, "extkey_attcode"=> 'supervisor_id', "target_attcode"=>"email"))); MetaModel::Init_AddAttribute(new AttributeExternalKey("managergroup_id", array("targetclass"=>"bizWorkgroup", "jointype"=> "", "label"=>"Manager Group", "description"=>"which workgroup is approving ticket", "allowed_values"=>null, "sql"=>"managergroup_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("managergroup_name", array("label"=>"Manager Group", "description"=>"name of workgroup approving the Ticket", "allowed_values"=>null, "extkey_attcode"=> 'managergroup_id', "target_attcode"=>"name"))); - MetaModel::Init_AddAttribute(new AttributeExternalKey("manager_id", array("targetclass"=>"bizPerson", "jointype"=> "", "label"=>"Manager", "description"=>"who is approving the ticket", "allowed_values"=>null, "sql"=>"manager_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array('managergroup_id')))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("manager_id", array("targetclass"=>"bizPerson", "jointype"=> "", "label"=>"Manager", "description"=>"who is approving the ticket", "allowed_values"=>new ValueSetObjects('SELECT bizPerson AS p JOIN lnkContactTeam AS l ON l.contact_id=p.id JOIN bizTeam AS t ON l.team_id=t.id JOIN bizWorkgroup AS w ON w.team_id=t.id WHERE w.id = :this->managergroup_id'), "sql"=>"manager_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array('managergroup_id')))); MetaModel::Init_AddAttribute(new AttributeExternalField("manager_mail", array("label"=>"Manager", "description"=>"name of agent approving the Ticket", "allowed_values"=>null, "extkey_attcode"=> 'manager_id', "target_attcode"=>"email"))); MetaModel::Init_AddAttribute(new AttributeEnum("outage", array("label"=>"Planned Outage", "description"=>"Flag to define if there is a planned outage", "allowed_values"=>new ValueSetEnum("Yes,No"), "sql"=>"outage", "default_value"=>"No", "is_null_allowed"=>true, "depends_on"=>array()))); @@ -109,12 +109,16 @@ class bizChangeTicket extends cmdbAbstractObject MetaModel::Init_DefineState("New", array("label"=>"New (Unassigned)", "description"=>"Newly created ticket", "attribute_inherit"=>null, "attribute_list"=>array('name' => OPT_ATT_READONLY,'org_id' => OPT_ATT_MANDATORY, 'title' => OPT_ATT_MANDATORY, 'reason' => OPT_ATT_MANDATORY, 'impacted_infra_manual' => OPT_ATT_MANDATORY, 'assignment_count' => OPT_ATT_HIDDEN, 'end_date' => OPT_ATT_HIDDEN, 'workgroup_id' => OPT_ATT_HIDDEN, 'supervisorgroup_id' => OPT_ATT_HIDDEN, 'managergroup_id' => OPT_ATT_HIDDEN, 'supervisor_id' => OPT_ATT_HIDDEN, 'manager_id' => OPT_ATT_HIDDEN, 'agent_id' => OPT_ATT_HIDDEN ))); - MetaModel::Init_DefineState("Validated", array("label"=>"Validated", "description"=>"Ticket is approved", "attribute_inherit"=>null, + MetaModel::Init_DefineState("Validated", array("label"=>"Validated", "description"=>"Ticket is validated", "attribute_inherit"=>null, "attribute_list"=>array('name' => OPT_ATT_READONLY,'org_id' => OPT_ATT_READONLY,'change_request' => OPT_ATT_READONLY,'creation_date' => OPT_ATT_READONLY, 'title' => OPT_ATT_READONLY, 'reason' => OPT_ATT_READONLY, 'impacted_infra_manual' => OPT_ATT_MANDATORY, - 'assignment_count' => OPT_ATT_HIDDEN, 'end_date' => OPT_ATT_HIDDEN, 'workgroup_id' => OPT_ATT_MANDATORY, 'supervisorgroup_id' => OPT_ATT_MANDATORY, 'managergroup_id' => OPT_ATT_MANDATORY, 'supervisor_id' => OPT_ATT_HIDDEN, 'manager_id' => OPT_ATT_HIDDEN, 'agent_id' => OPT_ATT_HIDDEN))); + 'assignment_count' => OPT_ATT_HIDDEN, 'end_date' => OPT_ATT_HIDDEN, 'workgroup_id' => OPT_ATT_MUSTCHANGE, 'supervisorgroup_id' => OPT_ATT_MUSTCHANGE, 'managergroup_id' => OPT_ATT_MUSTCHANGE, 'supervisor_id' => OPT_ATT_HIDDEN, 'manager_id' => OPT_ATT_HIDDEN, 'agent_id' => OPT_ATT_HIDDEN))); MetaModel::Init_DefineState("Rejected", array("label"=>"Rejected", "description"=>"This ticket is not approved", "attribute_inherit"=>null, "attribute_list"=>array('name' => OPT_ATT_READONLY, 'org_id' => OPT_ATT_READONLY,'creation_date' => OPT_ATT_READONLY,'assignment_count' => OPT_ATT_HIDDEN, 'end_date' => OPT_ATT_HIDDEN))); - MetaModel::Init_DefineState("PlannedScheduled", array("label"=>"Planned&Scheduled", "description"=>"Evaluation is done for this change", "attribute_inherit"=>null, + MetaModel::Init_DefineState("Assigned", array("label"=>"Assigned", "description"=>"Ticket is assigned", "attribute_inherit"=>null, + "attribute_list"=>array('name' => OPT_ATT_READONLY,'org_id' => OPT_ATT_READONLY,'change_request' => OPT_ATT_READONLY,'creation_date' => OPT_ATT_READONLY, 'title' => OPT_ATT_READONLY, 'reason' => OPT_ATT_READONLY, 'impacted_infra_manual' => OPT_ATT_MANDATORY, + 'assignment_count' => OPT_ATT_HIDDEN, 'end_date' => OPT_ATT_HIDDEN, 'workgroup_id' => OPT_ATT_MANDATORY, 'supervisorgroup_id' => OPT_ATT_MANDATORY, 'managergroup_id' => OPT_ATT_MANDATORY, 'supervisor_id' => OPT_ATT_MUSTCHANGE, 'manager_id' => OPT_ATT_MUSTCHANGE, 'agent_id' => OPT_ATT_MUSTCHANGE))); + + MetaModel::Init_DefineState("PlannedScheduled", array("label"=>"Planned&Scheduled", "description"=>"Evaluation is done for this change", "attribute_inherit"=>null, "attribute_list"=>array('name' => OPT_ATT_READONLY, 'org_id' => OPT_ATT_READONLY,'assignment_count' => OPT_ATT_HIDDEN,'start_date' => OPT_ATT_MANDATORY, 'end_date' => OPT_ATT_MANDATORY, 'impact' => OPT_ATT_MANDATORY, 'workgroup_id' => OPT_ATT_READONLY,'supervisorgroup_id' => OPT_ATT_READONLY, 'managergroup_id' => OPT_ATT_READONLY, 'supervisor_id' => OPT_ATT_MANDATORY, 'manager_id' => OPT_ATT_MANDATORY, 'agent_id' => OPT_ATT_MANDATORY, 'change_log' => OPT_ATT_MANDATORY,'fallback' => OPT_ATT_MANDATORY,'title' => OPT_ATT_READONLY, 'reason' => OPT_ATT_READONLY,'type' => OPT_ATT_READONLY,'requestor_id' => OPT_ATT_READONLY,'domain' => OPT_ATT_READONLY,'change_request' => OPT_ATT_READONLY,'creation_date' => OPT_ATT_READONLY))); MetaModel::Init_DefineState("Approved", array("label"=>"Approved", "description"=>"Ticket is approved by CAB", "attribute_inherit"=>null, "attribute_list"=>array('name' => OPT_ATT_READONLY, 'org_id' => OPT_ATT_READONLY,'outage' => OPT_ATT_READONLY,'assignment_count' => OPT_ATT_HIDDEN,'start_date' => OPT_ATT_READONLY, 'end_date' => OPT_ATT_MANDATORY, 'impact' => OPT_ATT_READONLY, 'workgroup_id' => OPT_ATT_READONLY,'supervisorgroup_id' => OPT_ATT_READONLY, 'managergroup_id' => OPT_ATT_READONLY, 'supervisor_id' => OPT_ATT_READONLY, 'manager_id' => OPT_ATT_READONLY, 'agent_id' => OPT_ATT_MANDATORY, 'change_log' => OPT_ATT_MANDATORY,'fallback' => OPT_ATT_MANDATORY,'title' => OPT_ATT_READONLY, 'reason' => OPT_ATT_READONLY,'type' => OPT_ATT_READONLY,'requestor_id' => OPT_ATT_READONLY,'domain' => OPT_ATT_READONLY,'change_request' => OPT_ATT_READONLY,'creation_date' => OPT_ATT_READONLY))); @@ -128,6 +132,8 @@ class bizChangeTicket extends cmdbAbstractObject MetaModel::Init_DefineStimulus("ev_validate", new StimulusUserAction(array("label"=>"Validate this change", "description"=>"Make sure it is a valid change request"))); MetaModel::Init_DefineStimulus("ev_reject", new StimulusUserAction(array("label"=>"Reject this change", "description"=>"This change request is rejected because it is a non valid one"))); + MetaModel::Init_DefineStimulus("ev_assign", new StimulusUserAction(array("label"=>"Assign this change", "description"=>"This change request is assigned"))); + MetaModel::Init_DefineStimulus("ev_reopen", new StimulusUserAction(array("label"=>"Modify this change", "description"=>"Update change request to make it valid"))); MetaModel::Init_DefineStimulus("ev_plan", new StimulusUserAction(array("label"=>"Plan this change", "description"=>"Plan and Schedule this change for validation"))); MetaModel::Init_DefineStimulus("ev_approve", new StimulusUserAction(array("label"=>"Approve this change", "description"=>"This change is approved by CAB"))); @@ -140,7 +146,9 @@ class bizChangeTicket extends cmdbAbstractObject MetaModel::Init_DefineTransition("New", "ev_validate", array("target_state"=>"Validated", "actions"=>array('SetLastUpDate'), "user_restriction"=>null)); MetaModel::Init_DefineTransition("New", "ev_reject", array("target_state"=>"Rejected", "actions"=>array('SetLastUpDate'), "user_restriction"=>null)); MetaModel::Init_DefineTransition("Rejected", "ev_reopen", array("target_state"=>"New", "actions"=>array('SetLastUpDate'), "user_restriction"=>null)); - MetaModel::Init_DefineTransition("Validated", "ev_plan", array("target_state"=>"PlannedScheduled", "actions"=>array('SetLastUpDate'), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Validated", "ev_assign", array("target_state"=>"Assigned", "actions"=>array('SetLastUpDate'), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("Assigned", "ev_plan", array("target_state"=>"PlannedScheduled", "actions"=>array('SetLastUpDate'), "user_restriction"=>null)); + MetaModel::Init_DefineTransition("PlannedScheduled", "ev_approve", array("target_state"=>"Approved", "actions"=>array('SetLastUpDate'), "user_restriction"=>null)); MetaModel::Init_DefineTransition("PlannedScheduled", "ev_notapprove", array("target_state"=>"NotApproved", "actions"=>array('SetLastUpDate'), "user_restriction"=>null)); MetaModel::Init_DefineTransition("NotApproved", "ev_replan", array("target_state"=>"PlannedScheduled", "actions"=>array('SetLastUpDate'), "user_restriction"=>null)); diff --git a/business/ServiceMgmt.business.php b/business/ServiceMgmt.business.php index 68f6f91d0..dd8865a05 100644 --- a/business/ServiceMgmt.business.php +++ b/business/ServiceMgmt.business.php @@ -22,7 +22,7 @@ class bizService extends cmdbAbstractObject "db_table" => "services", "db_key_field" => "id", "db_finalclass_field" => "", - "display_template" => "../business/templates/default.html", + "display_template" => "../business/templates/service.html", ); MetaModel::Init_Params($aParams); MetaModel::Init_InheritAttributes(); diff --git a/business/itop.business.class.inc.php b/business/itop.business.class.inc.php index e870ec4f4..a13f76f31 100644 --- a/business/itop.business.class.inc.php +++ b/business/itop.business.class.inc.php @@ -202,7 +202,7 @@ class bizContact extends logRealObject MetaModel::Init_AddAttribute(new AttributeExternalField("org_name", array("label"=>"Organization", "description"=>"Company / Department of the contact", "allowed_values"=>null, "extkey_attcode"=> 'org_id', "target_attcode"=>"name"))); MetaModel::Init_AddAttribute(new AttributeEmailAddress("email", array("label"=>"eMail", "description"=>"Email address", "allowed_values"=>null, "sql"=>"email", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("phone", array("label"=>"Phone", "description"=>"Telephone", "allowed_values"=>null, "sql"=>"telephone", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeExternalKey("location_id", array("targetclass"=>"bizLocation", "label"=>"Location", "description"=>"Id of the location where the contact is located", "allowed_values"=>null, "sql"=>"location_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("location_id", array("targetclass"=>"bizLocation", "label"=>"Location", "description"=>"Id of the location where the contact is located", "allowed_values"=>new ValueSetObjects('SELECT bizLocation AS p WHERE p.org_id = :this->org_id'), "sql"=>"location_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array("org_id")))); MetaModel::Init_AddAttribute(new AttributeExternalField("location_name", array("label"=>"Location Name", "description"=>"Name of the location where the contact is located", "allowed_values"=>null, "extkey_attcode"=> 'location_id', "target_attcode"=>"name"))); MetaModel::Init_InheritFilters(); @@ -750,16 +750,16 @@ class bizCircuit extends logInfra MetaModel::Init_Params($aParams); MetaModel::Init_InheritAttributes(); MetaModel::Init_AddAttribute(new AttributeString("speed", array("label"=>"speed", "description"=>"speed of the circuit", "allowed_values"=>null, "sql"=>"speed", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeExternalKey("location1_id", array("targetclass"=>"bizLocation", "label"=>"Location 1", "description"=>"Id of the location 1", "allowed_values"=>null, "sql"=>"location1_id", "is_null_allowed"=>false,"on_target_delete"=>DEL_MANUAL, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("location1_id", array("targetclass"=>"bizLocation", "label"=>"Location 1", "description"=>"Id of the location 1", "allowed_values"=>new ValueSetObjects('SELECT bizLocation AS p WHERE p.org_id = :this->org_id'), "sql"=>"location1_id", "is_null_allowed"=>false,"on_target_delete"=>DEL_MANUAL, "depends_on"=>array("org_id")))); MetaModel::Init_AddAttribute(new AttributeExternalField("location1_name", array("label"=>"Location 1", "description"=>"Name of the location", "allowed_values"=>null, "extkey_attcode"=> 'location1_id', "target_attcode"=>"name"))); - MetaModel::Init_AddAttribute(new AttributeExternalKey("location2_id", array("targetclass"=>"bizLocation", "label"=>"Location 2", "description"=>"Id of the location 2", "allowed_values"=>null, "sql"=>"location2_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL,"depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("location2_id", array("targetclass"=>"bizLocation", "label"=>"Location 2", "description"=>"Id of the location 2", "allowed_values"=>new ValueSetObjects('SELECT bizLocation AS p WHERE p.org_id = :this->org_id'), "sql"=>"location2_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL,"depends_on"=>array("org_id")))); MetaModel::Init_AddAttribute(new AttributeExternalField("location2_name", array("label"=>"Location 2", "description"=>"Name of the location", "allowed_values"=>null, "extkey_attcode"=> 'location2_id', "target_attcode"=>"name"))); - MetaModel::Init_AddAttribute(new AttributeExternalKey("interface1_id", array("targetclass"=>"bizInterface", "label"=>"Interface 1", "description"=>"id of the interface 1", "allowed_values"=>null, "sql"=>"interface1_id", "is_null_allowed"=>false,"on_target_delete"=>DEL_MANUAL, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("interface1_id", array("targetclass"=>"bizInterface", "label"=>"Interface 1", "description"=>"id of the interface 1", "allowed_values"=>new ValueSetObjects('SELECT bizInterface AS p WHERE p.org_id = :this->org_id'), "sql"=>"interface1_id", "is_null_allowed"=>false,"on_target_delete"=>DEL_MANUAL, "depends_on"=>array("org_id")))); MetaModel::Init_AddAttribute(new AttributeExternalField("interface1_name", array("label"=>"Interface", "description"=>"Name of the interface 1", "allowed_values"=>null, "extkey_attcode"=> 'interface1_id', "target_attcode"=>"name"))); MetaModel::Init_AddAttribute(new AttributeExternalField("device1_name", array("label"=>"Device 1", "description"=>"Name of the device 1", "allowed_values"=>null, "extkey_attcode"=> 'interface1_id', "target_attcode"=>"device_name"))); - MetaModel::Init_AddAttribute(new AttributeExternalKey("interface2_id", array("targetclass"=>"bizInterface", "label"=>"Interface 2", "description"=>"id of the interface 2", "allowed_values"=>null, "sql"=>"interface2_id", "is_null_allowed"=>false,"on_target_delete"=>DEL_MANUAL, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("interface2_id", array("targetclass"=>"bizInterface", "label"=>"Interface 2", "description"=>"id of the interface 2", "allowed_values"=>new ValueSetObjects('SELECT bizInterface AS p WHERE p.org_id = :this->org_id'), "sql"=>"interface2_id", "is_null_allowed"=>false,"on_target_delete"=>DEL_MANUAL, "depends_on"=>array("org_id")))); MetaModel::Init_AddAttribute(new AttributeExternalField("interface2_name", array("label"=>"Interface", "description"=>"Name of the interface 2", "allowed_values"=>null, "extkey_attcode"=> 'interface2_id', "target_attcode"=>"name"))); MetaModel::Init_AddAttribute(new AttributeExternalField("device2_name", array("label"=>"Interface", "description"=>"Name of the device 2", "allowed_values"=>null, "extkey_attcode"=> 'interface2_id', "target_attcode"=>"device_name"))); @@ -901,56 +901,6 @@ class bizInterface extends logInfra } } -//////////////////////////////////////////////////////////////////////////////////// -/** -* n-n link between any Interfaces -*/ -//////////////////////////////////////////////////////////////////////////////////// -/* -class lnkInterfaces extends cmdbAbstractObject -{ - public static function Init() - { - $aParams = array - ( - "category" => "bizmodel,searchable", - "name" => "InterfacesLinks", - "description" => "A link between two interfaces", - "key_type" => "autoincrement", - "key_label" => "link_id", - "name_attcode" => "link_type", - "state_attcode" => "", - "reconc_keys" => array("interface1_id", "interface2_id"), - "db_table" => "interfaces_links", - "db_key_field" => "link_id", - "db_finalclass_field" => "", - "display_template" => "../business/templates/default.html", - ); - MetaModel::Init_Params($aParams); - MetaModel::Init_AddAttribute(new AttributeExternalKey("interface1_id", array("targetclass"=>"bizInterface", "label"=>"Interface", "description"=>"The interface1", "sql"=>"interface1_id", "allowed_values"=> null, "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeExternalField("interface1_name", array("label"=>"Interface1 Name", "description"=>"name of the interface1", "extkey_attcode"=> 'interface1_id', "allowed_values"=> null, "target_attcode"=>"name"))); - MetaModel::Init_AddAttribute(new AttributeExternalKey("interface2_id", array("targetclass"=>"bizInterface", "label"=>"Interface", "description"=>"The interface2", "sql"=>"interface2_id", "allowed_values"=> null, "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeExternalField("interface2_name", array("label"=>"Interface2 Name", "description"=>"name of the interface2", "extkey_attcode"=> 'interface2_id', "allowed_values"=> null, "target_attcode"=>"name"))); - MetaModel::Init_AddAttribute(new AttributeExternalField("interface1_device_id", array("label"=>"Device", "description"=>"device", "extkey_attcode"=> 'interface1_id', "allowed_values"=> null, "target_attcode"=>"device_id"))); - MetaModel::Init_AddAttribute(new AttributeExternalField("interface1_device_name", array("label"=>"Device Name", "description"=>"name of the device", "extkey_attcode"=> 'interface1_id', "allowed_values"=> null, "target_attcode"=>"device_name"))); - MetaModel::Init_AddAttribute(new AttributeExternalField("interface2_device_id", array("label"=>"Device", "description"=>"device", "extkey_attcode"=> 'interface2_id', "allowed_values"=> null, "target_attcode"=>"device_id"))); - MetaModel::Init_AddAttribute(new AttributeExternalField("interface2_device_name", array("label"=>"Device Name", "description"=>"name of the device", "extkey_attcode"=> 'interface2_id', "allowed_values"=> null, "target_attcode"=>"device_name"))); - MetaModel::Init_AddAttribute(new AttributeString("link_type", array("label"=>"Link Type", "description"=>"More information", "sql"=>"link_type", "default_value"=>"", "allowed_values"=> null, "is_null_allowed"=>false, "depends_on"=>array()))); - - MetaModel::Init_AddFilterFromAttribute("interface1_id"); - MetaModel::Init_AddFilterFromAttribute("interface1_name"); - MetaModel::Init_AddFilterFromAttribute("interface2_id"); - MetaModel::Init_AddFilterFromAttribute("interface2_name"); - MetaModel::Init_AddFilterFromAttribute("interface2_device_name"); - MetaModel::Init_AddFilterFromAttribute("link_type"); - - // Display lists - MetaModel::Init_SetZListItems('details', array('interface1_id', 'interface2_id', 'link_type')); // Attributes to be displayed for the complete details - MetaModel::Init_SetZListItems('list', array('interface1_id', 'interface1_device_id', 'interface2_id', 'interface2_device_id', 'link_type')); // Attributes to be displayed for the complete details - } -} -*/ - //////////////////////////////////////////////////////////////////////////////////// /** * A subnet @@ -1060,13 +1010,13 @@ class bizDevice extends logInfra ); MetaModel::Init_Params($aParams); MetaModel::Init_InheritAttributes(); - MetaModel::Init_AddAttribute(new AttributeExternalKey("location_id", array("targetclass"=>"bizLocation", "label"=>"Location", "description"=>"where is the located object physically located", "allowed_values"=>null, "sql"=>"location_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("location_id", array("targetclass"=>"bizLocation", "label"=>"Location", "description"=>"where is the located object physically located", "allowed_values"=>new ValueSetObjects('SELECT bizLocation AS p WHERE p.org_id = :this->org_id'), "sql"=>"location_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array("org_id")))); MetaModel::Init_AddAttribute(new AttributeExternalField("location_name", array("label"=>"Location Name", "description"=>"name of the location", "allowed_values"=>null, "extkey_attcode"=> 'location_id', "target_attcode"=>"name"))); MetaModel::Init_AddAttribute(new AttributeExternalField("country", array("label"=>"Country", "description"=>"country where the device is located", "allowed_values"=>null, "extkey_attcode"=> 'location_id', "target_attcode"=>"country"))); MetaModel::Init_AddAttribute(new AttributeString("brand", array("label"=>"Brand", "description"=>"The manufacturer of the device", "allowed_values"=>null, "sql"=>"brand", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("model", array("label"=>"Model", "description"=>"The model number of the device", "allowed_values"=>null, "sql"=>"model", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("serial_number", array("label"=>"Serial Number", "description"=>"The serial number of the device", "allowed_values"=>null, "sql"=>"serial_number", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeString("mgmt_ip", array("label"=>"Mgmt IP", "description"=>"Management IP", "allowed_values"=>null, "sql"=>"mgmt_ip", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("mgmt_ip", array("label"=>"Mgmt IP", "description"=>"Management IP", "allowed_values"=>null, "sql"=>"mgmt_ip", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_InheritFilters(); MetaModel::Init_AddFilterFromAttribute("location_id"); @@ -1083,7 +1033,8 @@ class bizDevice extends logInfra { case "impacts": $aRels = array( - "connected device" => array("sQuery"=>"bizDevice: PKEY IS device_id IN (bizInterface: PKEY IS interface2_id IN (lnkInterfaces: interface1_id IN (bizInterface: device_id = \$[this.pkey::])))", "bPropagate"=>true, "iDistance"=>3), +// commented out, because lnkInterfaces does not exist anymore +// "connected device" => array("sQuery"=>"bizDevice: PKEY IS device_id IN (bizInterface: PKEY IS interface2_id IN (lnkInterfaces: interface1_id IN (bizInterface: device_id = \$[this.pkey::])))", "bPropagate"=>true, "iDistance"=>3), "hosted app" => array("sQuery"=>"bizApplication: infra_id = \$[this.pkey::]", "bPropagate"=>true, "iDistance"=>3), ); return array_merge($aRels, parent::GetRelationQueries($sRelCode)); @@ -1548,7 +1499,7 @@ class bizApplication extends logInfra ); MetaModel::Init_Params($aParams); MetaModel::Init_InheritAttributes(); - MetaModel::Init_AddAttribute(new AttributeExternalKey("device_id", array("targetclass"=>"bizDevice", "jointype"=> '', "label"=>"Hosting device", "description"=>"The device where application is installed", "allowed_values"=>null, "sql"=>"device_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("device_id", array("targetclass"=>"bizDevice", "jointype"=> '', "label"=>"Hosting device", "description"=>"The device where application is installed", "allowed_values"=>new ValueSetObjects('SELECT bizDevice AS p WHERE p.org_id = :this->org_id'), "sql"=>"device_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array("org_id")))); MetaModel::Init_AddAttribute(new AttributeExternalField("device_name", array("label"=>"Hosting device", "description"=>"Name of the device where application is installed", "allowed_values"=>null, "extkey_attcode"=> 'device_id', "target_attcode"=>"name"))); MetaModel::Init_AddAttribute(new AttributeDate("install_date", array("label"=>"Installation Date", "description"=>"Date when application was installed", "allowed_values"=>null, "sql"=>"install_date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); @@ -1735,7 +1686,7 @@ class bizPatch extends logRealObject ); MetaModel::Init_Params($aParams); MetaModel::Init_InheritAttributes(); - MetaModel::Init_AddAttribute(new AttributeExternalKey("device_id", array("targetclass"=>"bizDevice", "jointype"=> '', "label"=>"Device", "description"=>"The Device where patch is installed", "allowed_values"=>null, "sql"=>"device_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("device_id", array("targetclass"=>"bizDevice", "jointype"=> '', "label"=>"Device", "description"=>"The Device where patch is installed", "allowed_values"=>new ValueSetObjects('SELECT bizDevice AS p WHERE p.org_id = :this->org_id'), "sql"=>"device_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array("org_id")))); MetaModel::Init_AddAttribute(new AttributeExternalField("device_name", array("label"=>"Device Name", "description"=>"Name of the impacted device", "allowed_values"=>null, "extkey_attcode"=> 'device_id', "target_attcode"=>"name"))); MetaModel::Init_AddAttribute(new AttributeDate("install_date", array("label"=>"Installation Date", "description"=>"Date when application was installed", "allowed_values"=>null, "sql"=>"install_date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); From 7e35b6c3718d43f87a985554d8c4b047207c1a8c Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Thu, 14 Jan 2010 12:27:45 +0000 Subject: [PATCH 172/227] Setup page: added a scroll bar and grouped some warnings into one message that would fit on two lines SVN:trunk[249] --- setup/index.php | 6 ++---- setup/setuppage.class.inc.php | 13 +++++-------- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/setup/index.php b/setup/index.php index 03e49d5d9..2bc1f9118 100644 --- a/setup/index.php +++ b/setup/index.php @@ -153,15 +153,13 @@ function CheckServerConnection(setup_web_page $oP, $sDBServer, $sDBUser, $sDBPwd } else if($iMaxAllowedPacket < $iMaxUploadSize) { - $oP->warning("MySQL server's max_allowed_packet ($iMaxAllowedPacket) is not big enough."); - $oP->warning("Consider setting it to at least ".(500 + $iMaxUploadSize)."."); + $oP->warning("MySQL server's max_allowed_packet ($iMaxAllowedPacket) is not big enough. Please, consider setting it to at least ".(500 + $iMaxUploadSize)."."); } $oP->log("Info - MySQL max_allowed_packet: $iMaxAllowedPacket"); $iMaxConnections = $oDBSource->GetServerVariable('max_connections'); if ($iMaxConnections < 5) { - $oP->warning("MySQL server's max_connections ($iMaxConnections) is not enough."); - $oP->warning("Consider setting it to at least 5."); + $oP->warning("MySQL server's max_connections ($iMaxConnections) is not enough. Please, consider setting it to at least 5."); } $oP->log("Info - MySQL max_connections: ".($oDBSource->GetServerVariable('max_connections'))); } diff --git a/setup/setuppage.class.inc.php b/setup/setuppage.class.inc.php index 4ed7aade4..51a12ed49 100644 --- a/setup/setuppage.class.inc.php +++ b/setup/setuppage.class.inc.php @@ -18,6 +18,7 @@ body { margin: 0; padding: 0; font-size: 10pt; + overflow-y: auto; } #setup { width: 600px; @@ -50,27 +51,23 @@ button { } p.info { padding-left: 50px; - background: url(../images/info-mid.png) no-repeat top left; + background: url(../images/info-mid.png) no-repeat left -5px; height: 48px; - line-height: 48px; } p.ok { padding-left: 50px; - background: url(../images/clean-mid.png) no-repeat top left; + background: url(../images/clean-mid.png) no-repeat left -8px; height: 48px; - line-height: 48px; } p.warning { padding-left: 50px; - background: url(../images/messagebox_warning-mid.png) no-repeat top left; + background: url(../images/messagebox_warning-mid.png) no-repeat left -5px; height: 48px; - line-height: 48px; } p.error { padding-left: 50px; - background: url(../images/stop-mid.png) no-repeat top left; + background: url(../images/stop-mid.png) no-repeat left -5px; height: 48px; - line-height: 48px; } td.label { text-align: left; From 0a413a32f6f2e4ff86cdf399de8dc9bee8f6c895 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Sun, 17 Jan 2010 09:07:10 +0000 Subject: [PATCH 173/227] Renaming of the WebPage class (and its derived classes) to stick to the same naming convention as other classes. SVN:trunk[250] --- .../userrightsprofile.class.inc.php | 4 +-- application/ajaxwebpage.class.inc.php | 2 +- application/cmdbabstract.class.inc.php | 32 +++++++++---------- application/csvpage.class.inc.php | 2 +- application/displayblock.class.inc.php | 12 +++---- application/loginwebpage.class.inc.php | 6 ++-- application/menunode.class.inc.php | 4 +-- application/nicewebpage.class.inc.php | 2 +- application/template.class.inc.php | 2 +- application/ui.linkswidget.class.inc.php | 8 ++--- application/uilinkswizard.class.inc.php | 12 +++---- application/webpage.class.inc.php | 4 +-- application/xmlpage.class.inc.php | 2 +- business/itop.business.class.inc.php | 14 ++++---- pages/UI.php | 2 +- pages/advanced_search.php | 2 +- pages/ajax.php | 2 +- pages/ajax.render.php | 4 +-- pages/audit.php | 2 +- pages/csvimport.php | 2 +- pages/data_generator.php | 6 ++-- pages/db_importer.php | 2 +- pages/incident.php | 20 ++++++------ pages/index.php | 20 ++++++------ pages/run_query.php | 2 +- pages/schema.php | 2 +- pages/usermanagement_classproj.php | 2 +- pages/usermanagement_profileproj.php | 2 +- pages/usermanagement_userstatus.php | 2 +- setup/ajax.dataloader.php | 10 +++--- setup/index.php | 26 +++++++-------- setup/setuppage.class.inc.php | 2 +- setup/xmldataloader.class.inc.php | 2 +- webservices/export.php | 10 +++--- webservices/import.php | 4 +-- 35 files changed, 116 insertions(+), 116 deletions(-) diff --git a/addons/userrights/userrightsprofile.class.inc.php b/addons/userrights/userrightsprofile.class.inc.php index 396766dba..f65b15145 100644 --- a/addons/userrights/userrightsprofile.class.inc.php +++ b/addons/userrights/userrightsprofile.class.inc.php @@ -148,7 +148,7 @@ class URP_Users extends UserRightsBaseClass $oPage->table($aDisplayConfig, $aDisplayData); } - function DisplayBareRelations(web_page $oPage) + function DisplayBareRelations(WebPage $oPage) { parent::DisplayBareRelations($oPage); @@ -272,7 +272,7 @@ class URP_Profiles extends UserRightsBaseClass $oPage->table($aDisplayConfig, $aDisplayData); } - function DisplayBareRelations(web_page $oPage) + function DisplayBareRelations(WebPage $oPage) { parent::DisplayBareRelations($oPage); diff --git a/application/ajaxwebpage.class.inc.php b/application/ajaxwebpage.class.inc.php index fbacc0058..3c1ace518 100644 --- a/application/ajaxwebpage.class.inc.php +++ b/application/ajaxwebpage.class.inc.php @@ -11,7 +11,7 @@ require_once("../application/webpage.class.inc.php"); * @license http://www.opensource.org/licenses/lgpl-license.php LGPL */ -class ajax_page extends web_page +class ajax_page extends WebPage { /** * Jquery style ready script diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index 454cbd8e8..08617ed31 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -116,7 +116,7 @@ abstract class cmdbAbstractObject extends CMDBObject return $sDisplayValue; } - function DisplayBareHeader(web_page $oPage) + function DisplayBareHeader(WebPage $oPage) { // Standard Header with name, actions menu and history block // @@ -140,12 +140,12 @@ abstract class cmdbAbstractObject extends CMDBObject $oPage->add("
    \n"); } - function DisplayBareDetails(web_page $oPage) + function DisplayBareDetails(WebPage $oPage) { $oPage->add($this->GetBareDetails($oPage)); } - function DisplayBareRelations(web_page $oPage) + function DisplayBareRelations(WebPage $oPage) { // Related objects $oPage->AddTabContainer('Related Objects'); @@ -197,7 +197,7 @@ abstract class cmdbAbstractObject extends CMDBObject return $sDisplayName; } - function GetBareDetails(web_page $oPage) + function GetBareDetails(WebPage $oPage) { $sHtml = ''; $oAppContext = new ApplicationContext(); @@ -244,7 +244,7 @@ abstract class cmdbAbstractObject extends CMDBObject } - function DisplayDetails(web_page $oPage) + function DisplayDetails(WebPage $oPage) { $sTemplate = Utils::ReadFromFile(MetaModel::GetDisplayTemplate(get_class($this))); if (!empty($sTemplate)) @@ -262,7 +262,7 @@ abstract class cmdbAbstractObject extends CMDBObject } } - function DisplayPreview(web_page $oPage) + function DisplayPreview(WebPage $oPage) { $aDetails = array(); $sClass = get_class($this); @@ -276,13 +276,13 @@ abstract class cmdbAbstractObject extends CMDBObject // Comment by Rom: this helper may be used to display objects of class DBObject // -> I am using this to display the changes history - public static function DisplaySet(web_page $oPage, CMDBObjectSet $oSet, $aExtraParams = array()) + public static function DisplaySet(WebPage $oPage, CMDBObjectSet $oSet, $aExtraParams = array()) { $oPage->add(self::GetDisplaySet($oPage, $oSet, $aExtraParams)); } - //public static function GetDisplaySet(web_page $oPage, CMDBObjectSet $oSet, $sLinkageAttribute = '', $bDisplayMenu = true, $bSelectMode = false) - public static function GetDisplaySet(web_page $oPage, CMDBObjectSet $oSet, $aExtraParams = array()) + //public static function GetDisplaySet(WebPage $oPage, CMDBObjectSet $oSet, $sLinkageAttribute = '', $bDisplayMenu = true, $bSelectMode = false) + public static function GetDisplaySet(WebPage $oPage, CMDBObjectSet $oSet, $aExtraParams = array()) { static $iListId = 0; $iListId++; @@ -418,7 +418,7 @@ abstract class cmdbAbstractObject extends CMDBObject return $sHtml; } - static function DisplaySetAsCSV(web_page $oPage, CMDBObjectSet $oSet, $aParams = array()) + static function DisplaySetAsCSV(WebPage $oPage, CMDBObjectSet $oSet, $aParams = array()) { $oPage->add(self::GetSetAsCSV($oSet, $aParams)); } @@ -455,7 +455,7 @@ abstract class cmdbAbstractObject extends CMDBObject return $sHtml; } - static function DisplaySetAsXML(web_page $oPage, CMDBObjectSet $oSet, $aParams = array()) + static function DisplaySetAsXML(WebPage $oPage, CMDBObjectSet $oSet, $aParams = array()) { $oAppContext = new ApplicationContext(); $sClassName = $oSet->GetFilter()->GetClass(); @@ -480,7 +480,7 @@ abstract class cmdbAbstractObject extends CMDBObject } // By rom - function DisplayChangesLog(web_page $oPage) + function DisplayChangesLog(WebPage $oPage) { $oFltChangeOps = new CMDBSearchFilter('CMDBChangeOpSetAttribute'); $oFltChangeOps->AddCondition('objkey', $this->GetKey(), '='); @@ -498,13 +498,13 @@ abstract class cmdbAbstractObject extends CMDBObject } } - public static function DisplaySearchForm(web_page $oPage, CMDBObjectSet $oSet, $aExtraParams = array()) + public static function DisplaySearchForm(WebPage $oPage, CMDBObjectSet $oSet, $aExtraParams = array()) { $oPage->add(self::GetSearchForm($oPage, $oSet, $aExtraParams)); } - public static function GetSearchForm(web_page $oPage, CMDBObjectSet $oSet, $aExtraParams = array()) + public static function GetSearchForm(WebPage $oPage, CMDBObjectSet $oSet, $aExtraParams = array()) { static $iSearchFormId = 0; $oAppContext = new ApplicationContext(); @@ -755,7 +755,7 @@ abstract class cmdbAbstractObject extends CMDBObject return $sHTMLValue; } - public function DisplayModifyForm(web_page $oPage) + public function DisplayModifyForm(WebPage $oPage) { static $iFormId = 0; $iFormId++; @@ -812,7 +812,7 @@ abstract class cmdbAbstractObject extends CMDBObject $oPage->add("\n"); } - public static function DisplayCreationForm(web_page $oPage, $sClass, $oObjectToClone = null, $aArgs = array()) + public static function DisplayCreationForm(WebPage $oPage, $sClass, $oObjectToClone = null, $aArgs = array()) { static $iCreationFormId = 0; diff --git a/application/csvpage.class.inc.php b/application/csvpage.class.inc.php index ed1fb724b..2912f62a8 100644 --- a/application/csvpage.class.inc.php +++ b/application/csvpage.class.inc.php @@ -4,7 +4,7 @@ require_once("../application/webpage.class.inc.php"); * Simple web page with no includes or fancy formatting, useful to generateXML documents * The page adds the content-type text/XML and the encoding into the headers */ -class CSVPage extends web_page +class CSVPage extends WebPage { function __construct($s_title) { diff --git a/application/displayblock.class.inc.php b/application/displayblock.class.inc.php index 42fea0d48..198bbebaf 100644 --- a/application/displayblock.class.inc.php +++ b/application/displayblock.class.inc.php @@ -149,7 +149,7 @@ class DisplayBlock return new $sBlockClass($oFilter, $sBlockType, $bAsynchronous, $aParams); } - public function Display(web_page $oPage, $sId, $aExtraParams = array()) + public function Display(WebPage $oPage, $sId, $aExtraParams = array()) { $oPage->add($this->GetDisplay($oPage, $sId, $aExtraParams)); /* @@ -184,7 +184,7 @@ class DisplayBlock */ } - public function GetDisplay(web_page $oPage, $sId, $aExtraParams = array()) + public function GetDisplay(WebPage $oPage, $sId, $aExtraParams = array()) { $sHtml = ''; $aExtraParams = array_merge($aExtraParams, $this->m_aParams); @@ -220,12 +220,12 @@ class DisplayBlock return $sHtml; } - public function RenderContent(web_page $oPage, $aExtraParams = array()) + public function RenderContent(WebPage $oPage, $aExtraParams = array()) { $oPage->add($this->GetRenderContent($oPage, $aExtraParams)); } - public function GetRenderContent(web_page $oPage, $aExtraParams = array()) + public function GetRenderContent(WebPage $oPage, $aExtraParams = array()) { $sHtml = ''; // Add the extra params into the filter if they make sense for such a filter @@ -615,7 +615,7 @@ class DisplayBlock */ class HistoryBlock extends DisplayBlock { - public function GetRenderContent(web_page $oPage, $aExtraParams = array()) + public function GetRenderContent(WebPage $oPage, $aExtraParams = array()) { $sHtml = ''; // Add the extra params into the filter if they make sense for such a filter @@ -689,7 +689,7 @@ class HistoryBlock extends DisplayBlock class MenuBlock extends DisplayBlock { - public function GetRenderContent(web_page $oPage, $aExtraParams = array()) + public function GetRenderContent(WebPage $oPage, $aExtraParams = array()) { $sHtml = ''; $oAppContext = new ApplicationContext(); diff --git a/application/loginwebpage.class.inc.php b/application/loginwebpage.class.inc.php index 32a330be0..ae8ae075d 100644 --- a/application/loginwebpage.class.inc.php +++ b/application/loginwebpage.class.inc.php @@ -3,7 +3,7 @@ require_once("../application/nicewebpage.class.inc.php"); /** * Web page used for displaying the login form */ -class login_web_page extends nice_web_page +class LoginWebPage extends NiceWebPage { public function __construct() { @@ -96,7 +96,7 @@ h1 { } else { - $oPage = new login_web_page(); + $oPage = new LoginWebPage(); $oPage->DisplayLoginForm(); $oPage->output(); exit; @@ -110,7 +110,7 @@ h1 { if (!UserRights::Login($sAuthUser, $sAuthPwd)) { self::ResetSession(); - $oPage = new login_web_page(); + $oPage = new LoginWebPage(); $oPage->DisplayLoginForm( true /* failed attempt */); $oPage->output(); exit; diff --git a/application/menunode.class.inc.php b/application/menunode.class.inc.php index 19b54b245..943bd393c 100644 --- a/application/menunode.class.inc.php +++ b/application/menunode.class.inc.php @@ -113,7 +113,7 @@ class menuNode extends DBObject return $oSet; } - public function RenderContent(web_page $oPage, $aExtraParams = array()) + public function RenderContent(WebPage $oPage, $aExtraParams = array()) { $sTemplate = $this->Get('template'); $oTemplate = new DisplayTemplate($sTemplate); @@ -136,7 +136,7 @@ class menuNode extends DBObject } $oP->AddToMenu("\n"); } - static public function DisplayCreationForm(web_page $oP, $sClass, $sFilter, $aExtraParams = array()) + static public function DisplayCreationForm(WebPage $oP, $sClass, $sFilter, $aExtraParams = array()) { $oFilter = DBObjectSearch::unserialize($sFilter); $oP->p('Create a new menu item for: '.$oFilter->__DescribeHTML()); diff --git a/application/nicewebpage.class.inc.php b/application/nicewebpage.class.inc.php index 63cfd4cc8..e8e48e07c 100644 --- a/application/nicewebpage.class.inc.php +++ b/application/nicewebpage.class.inc.php @@ -3,7 +3,7 @@ require_once("../application/webpage.class.inc.php"); /** * Web page with some associated CSS and scripts (jquery) for a fancier display */ -class nice_web_page extends web_page +class NiceWebPage extends WebPage { var $m_aReadyScripts; diff --git a/application/template.class.inc.php b/application/template.class.inc.php index 4ebfdf429..807ffdf30 100644 --- a/application/template.class.inc.php +++ b/application/template.class.inc.php @@ -15,7 +15,7 @@ class DisplayTemplate $this->m_sTemplate = $sTemplate; } - public function Render(web_page $oPage, $aParams = array()) + public function Render(WebPage $oPage, $aParams = array()) { $this->m_sTemplate = MetaModel::ApplyParams($this->m_sTemplate, $aParams); $iStart = 0; diff --git a/application/ui.linkswidget.class.inc.php b/application/ui.linkswidget.class.inc.php index 8100f9ee8..aa6b47684 100644 --- a/application/ui.linkswidget.class.inc.php +++ b/application/ui.linkswidget.class.inc.php @@ -17,7 +17,7 @@ class UILinksWidget $this->m_iInputId = $iInputId; } - public function Display(web_page $oPage, $oCurrentValuesSet = null) + public function Display(WebPage $oPage, $oCurrentValuesSet = null) { $sHTMLValue = ''; $sTargetClass = self::GetTargetClass($this->m_sClass, $this->m_sAttCode); @@ -122,7 +122,7 @@ class UILinksWidget } /** * This static function is called by the Ajax Page when there is a need to fill an autocomplete combo - * @param $oPage web_page The ajax page used for the put^put (sent back to the browser + * @param $oPage WebPage The ajax page used for the put^put (sent back to the browser * @param $oContext UserContext The context of the user (for limiting the search) * @param $sClass string The name of the class of the current object being edited * @param $sAttCode string The name of the attribute being edited @@ -130,7 +130,7 @@ class UILinksWidget * @param $iMaxCount integer The maximum number of items to return * @return void */ - static public function Autocomplete(web_page $oPage, UserContext $oContext, $sClass, $sAttCode, $sName, $iMaxCount) + static public function Autocomplete(WebPage $oPage, UserContext $oContext, $sClass, $sAttCode, $sName, $iMaxCount) { // #@# todo - add context information, otherwise any value will be authorized for external keys $aAllowedValues = MetaModel::GetAllowedValues_att($sClass, $sAttCode, array() /* $aArgs */, $sName); @@ -166,7 +166,7 @@ class UILinksWidget /** * This static function is called by the Ajax Page display a set of objects being linked * to the object being created - * @param $oPage web_page The ajax page used for the put^put (sent back to the browser + * @param $oPage WebPage The ajax page used for the put^put (sent back to the browser * @param $sClass string The name of the 'linking class' which is the class of the objects to display * @param $sSet JSON serialized set of objects * @param $sExtKeyToMe Name of the attribute in sClass that is pointing to a given object diff --git a/application/uilinkswizard.class.inc.php b/application/uilinkswizard.class.inc.php index 3779efac1..ede866c45 100644 --- a/application/uilinkswizard.class.inc.php +++ b/application/uilinkswizard.class.inc.php @@ -55,7 +55,7 @@ class UILinksWizard } } - public function Display(web_page $oP, UserContext $oContext, $aExtraParams = array()) + public function Display(WebPage $oP, UserContext $oContext, $aExtraParams = array()) { $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sLinkageAttr); $sTargetClass = $oAttDef->GetTargetClass(); @@ -295,7 +295,7 @@ class UILinksWizard return $aRow; } - protected function DisplayFormTable(web_page $oP, $aConfig, $aData) + protected function DisplayFormTable(WebPage $oP, $aConfig, $aData) { $oP->add("\n"); // Header @@ -327,7 +327,7 @@ class UILinksWizard $oP->add("
    \n"); } - protected function DisplayFormRow(web_page $oP, $aConfig, $aRow, $iRowId) + protected function DisplayFormRow(WebPage $oP, $aConfig, $aRow, $iRowId) { $oP->add("\n"); foreach($aConfig as $sName=>$void) @@ -337,7 +337,7 @@ class UILinksWizard $oP->add("\n"); } - public function DisplayAddForm(web_page $oP, UserContext $oContext) + public function DisplayAddForm(WebPage $oP, UserContext $oContext) { $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sLinkageAttr); $sTargetClass = $oAttDef->GetTargetClass(); @@ -361,7 +361,7 @@ class UILinksWizard $oP->add_ready_script("$('div#SearchFormToAdd form').bind('submit', function() {var the_form = this; SearchObjectsToAdd(the_form.id); return false;});"); } - public function SearchObjectsToAdd(web_page $oP, UserContext $oContext) + public function SearchObjectsToAdd(WebPage $oP, UserContext $oContext) { //$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sLinkageAttr); @@ -371,7 +371,7 @@ class UILinksWizard $oBlock->Display($oP, 'ResultsToAdd', array('menu' => false, 'selection_mode' => true, 'display_limit' => false)); // Don't display the 'Actions' menu on the results } - public function DoAddObjects(web_page $oP, UserContext $oContext, $aLinkedObjectIds = array()) + public function DoAddObjects(WebPage $oP, UserContext $oContext, $aLinkedObjectIds = array()) { //$oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $this->m_sLinkageAttr); //$sTargetClass = $oAttDef->GetTargetClass(); diff --git a/application/webpage.class.inc.php b/application/webpage.class.inc.php index b7cafa940..42d7135cc 100644 --- a/application/webpage.class.inc.php +++ b/application/webpage.class.inc.php @@ -6,11 +6,11 @@ * and renders the full web page by putting the elements in the proper place & order * when the output() method is called. * Usage: - * $oPage = new web_page("Title of my page"); + * $oPage = new WebPage("Title of my page"); * $oPage->p("Hello World !"); * $oPage->output(); */ -class web_page +class WebPage { protected $s_title; protected $s_content; diff --git a/application/xmlpage.class.inc.php b/application/xmlpage.class.inc.php index d3a62adfe..24a81c562 100644 --- a/application/xmlpage.class.inc.php +++ b/application/xmlpage.class.inc.php @@ -4,7 +4,7 @@ require_once("../application/webpage.class.inc.php"); * Simple web page with no includes or fancy formatting, useful to generateXML documents * The page adds the content-type text/XML and the encoding into the headers */ -class XMLPage extends web_page +class XMLPage extends WebPage { function __construct($s_title) { diff --git a/business/itop.business.class.inc.php b/business/itop.business.class.inc.php index a13f76f31..17ae3d77c 100644 --- a/business/itop.business.class.inc.php +++ b/business/itop.business.class.inc.php @@ -646,7 +646,7 @@ class bizLocation extends logInfra */ } - function DisplayDetails(web_page $oPage) + function DisplayDetails(WebPage $oPage) { parent::DisplayDetails($oPage); /* @@ -865,7 +865,7 @@ class bizInterface extends logInfra MetaModel::Init_SetZListItems('advanced_search', array('name', 'status', 'device_id', 'org_id')); // Criteria of the advanced search form } - function DisplayDetails(web_page $oPage) + function DisplayDetails(WebPage $oPage) { parent::DisplayDetails($oPage); /* @@ -942,7 +942,7 @@ class bizSubnet extends logInfra MetaModel::Init_SetZListItems('advanced_search', array('name', 'ip','mask')); // Criteria of the advanced search form } - function DisplayBareRelations(web_page $oPage) + function DisplayBareRelations(WebPage $oPage) { parent::DisplayBareRelations($oPage); @@ -1109,7 +1109,7 @@ class bizPC extends bizDevice MetaModel::Init_SetZListItems('advanced_search', array('name', 'status', 'type', 'brand', 'model', 'cpu', 'memory_size', 'hdd_size')); // Criteria of the advanced search form } - function DisplayDetails(web_page $oPage) + function DisplayDetails(WebPage $oPage) { parent::DisplayDetails($oPage); /* @@ -1256,7 +1256,7 @@ class bizServer extends bizDevice MetaModel::Init_SetZListItems('advanced_search', array('name', 'status','brand', 'model', 'os_family', 'os_version', 'location_id', 'cpu', 'number_of_cpus', 'memory_size', 'hdd_size', 'hdd_free_size')); // Criteria of the advanced search form } - function DisplayDetails(web_page $oPage) + function DisplayDetails(WebPage $oPage) { parent::DisplayDetails($oPage); /* @@ -1436,7 +1436,7 @@ class bizInfraGroup extends logInfra MetaModel::Init_SetZListItems('advanced_search', array('name', 'status', 'type', 'description', 'org_id')); // Criteria of the advanced search form } - function DisplayDetails(web_page $oPage) + function DisplayDetails(WebPage $oPage) { parent::DisplayDetails($oPage); /* @@ -1537,7 +1537,7 @@ class bizApplication extends logInfra } } - function DisplayDetails(web_page $oPage) + function DisplayDetails(WebPage $oPage) { parent::DisplayDetails($oPage); /* diff --git a/pages/UI.php b/pages/UI.php index e3a34c623..2bcb89fb7 100644 --- a/pages/UI.php +++ b/pages/UI.php @@ -30,7 +30,7 @@ $currentOrganization = utils::ReadParam('org_id', ''); $operation = utils::ReadParam('operation', ''); require_once('../application/loginwebpage.class.inc.php'); -login_web_page::DoLogin(); // Check user rights and prompt if needed +LoginWebPage::DoLogin(); // Check user rights and prompt if needed $oP = new iTopWebPage("Welcome to ITop", $currentOrganization); diff --git a/pages/advanced_search.php b/pages/advanced_search.php index 80ec35657..aa503c610 100644 --- a/pages/advanced_search.php +++ b/pages/advanced_search.php @@ -9,7 +9,7 @@ require_once('../application/dialogstack.class.inc.php'); require_once('../application/startup.inc.php'); // #@# not used, but... require_once('../classes/usercontext.class.inc.php'); -$oPage = new nice_web_page("ITop finder"); +$oPage = new NiceWebPage("ITop finder"); $oPage->no_cache(); diff --git a/pages/ajax.php b/pages/ajax.php index 1173ddd21..76cef861a 100644 --- a/pages/ajax.php +++ b/pages/ajax.php @@ -9,7 +9,7 @@ function ReadParam($sName, $defaultValue = "") return isset($_REQUEST[$sName]) ? $_REQUEST[$sName] : $defaultValue; } -$oPage = new nice_web_page("Asynchronous versus asynchronous DisplayBlocks"); +$oPage = new NiceWebPage("Asynchronous versus asynchronous DisplayBlocks"); $oPage->no_cache(); $oPage->add("

    Asynchronous versus asynchronous DisplayBlocks

    \n"); diff --git a/pages/ajax.render.php b/pages/ajax.render.php index 3ac20e6a9..477643d55 100644 --- a/pages/ajax.render.php +++ b/pages/ajax.render.php @@ -296,7 +296,7 @@ $oPage->output(); /** * Downloads a document to the browser, either as 'inline' or 'attachment' * - * @param web_page $oPage The web page for the output + * @param WebPage $oPage The web page for the output * @param UserContext $oContext The current User/security context to retreive the objects * @param string $sClass Class name of the object * @param mixed $id Identifier of the object @@ -304,7 +304,7 @@ $oPage->output(); * @param string $sContentDisposition Either 'inline' or 'attachment' * @return none */ -function DownloadDocument(web_page $oPage, UserContext $oContext, $sClass, $id, $sAttCode, $sContentDisposition = 'attachement') +function DownloadDocument(WebPage $oPage, UserContext $oContext, $sClass, $id, $sAttCode, $sContentDisposition = 'attachement') { try { diff --git a/pages/audit.php b/pages/audit.php index 88ce7c90a..edfa99a3e 100644 --- a/pages/audit.php +++ b/pages/audit.php @@ -8,7 +8,7 @@ $operation = utils::ReadParam('operation', ''); $oAppContext = new ApplicationContext(); require_once('../application/loginwebpage.class.inc.php'); -login_web_page::DoLogin(); // Check user rights and prompt if needed +LoginWebPage::DoLogin(); // Check user rights and prompt if needed $oP = new iTopWebPage("iTop - CMDB Audit", $currentOrganization); diff --git a/pages/csvimport.php b/pages/csvimport.php index 377b2a7c3..8ba2e60f4 100644 --- a/pages/csvimport.php +++ b/pages/csvimport.php @@ -5,7 +5,7 @@ require_once('../application/itopwebpage.class.inc.php'); require_once('../application/startup.inc.php'); require_once('../application/loginwebpage.class.inc.php'); -login_web_page::DoLogin(); // Check user rights and prompt if needed +LoginWebPage::DoLogin(); // Check user rights and prompt if needed $oContext = new UserContext(); $oAppContext = new ApplicationContext(); diff --git a/pages/data_generator.php b/pages/data_generator.php index 351269d18..c80478812 100644 --- a/pages/data_generator.php +++ b/pages/data_generator.php @@ -62,11 +62,11 @@ $aClassesToGenerate = array('bizOrganization' /* This one is special and must go /** * Populate an organization with objects of each class listed in the (global) $aClassesToGenerate array * - * @param web_page $oPage The object used for the HTML output + * @param WebPage $oPage The object used for the HTML output * @param cmdbGenerator $oGenerator The object used for the generation of the objects * @param string $sSize An enum specifying (roughly) how many objects of each class to create: one of 'small', 'medium', 'big', 'huge' or 'max' */ -function PopulateOrganization(CMDBChange $oMyChange, web_page $oPage, cmdbDataGenerator $oGenerator, $sSize = 'small') +function PopulateOrganization(CMDBChange $oMyChange, WebPage $oPage, cmdbDataGenerator $oGenerator, $sSize = 'small') { global $aClassesToGenerate; @@ -111,7 +111,7 @@ function PopulateOrganization(CMDBChange $oMyChange, web_page $oPage, cmdbDataGe /** * Delete an organization and all the instances of 'Object' belonging to this organization * - * @param web_page $oPage The object used for the HTML output + * @param WebPage $oPage The object used for the HTML output * @param string $sOrganizationCode The code (pkey) of the organization to delete */ function DeleteOrganization($oMyChange, $oPage, $sOrganizationCode) diff --git a/pages/db_importer.php b/pages/db_importer.php index 7d7df9894..cb56bf4e7 100644 --- a/pages/db_importer.php +++ b/pages/db_importer.php @@ -5,7 +5,7 @@ require_once('../application/itopwebpage.class.inc.php'); require_once('../application/startup.inc.php'); require_once('../application/loginwebpage.class.inc.php'); -login_web_page::DoLogin(); // Check user rights and prompt if needed +LoginWebPage::DoLogin(); // Check user rights and prompt if needed $sOperation = utils::ReadParam('operation', 'menu'); $oContext = new UserContext(); diff --git a/pages/incident.php b/pages/incident.php index ff9fe3df9..8024cd77b 100644 --- a/pages/incident.php +++ b/pages/incident.php @@ -24,7 +24,7 @@ abstract class DialogWizard return $this->m_aSteps[$sStep]; } - protected function AddContextToForm(web_page $oPage) + protected function AddContextToForm(WebPage $oPage) { // Store as hidden fields in the page all the variables from the previous steps foreach($this->m_aSteps as $sStep => $aFields) @@ -37,7 +37,7 @@ abstract class DialogWizard } } - function GetObjectPicker(web_page $oPage, $sTitle, $sFieldName, $sClass) + function GetObjectPicker(WebPage $oPage, $sTitle, $sFieldName, $sClass) { $sScript = <<AddCondition('pkey', array(0), 'IN'); @@ -258,7 +258,7 @@ class IncidentCreationWizard extends DialogWizard $oPage->add("m_sNextStep."\" />\n"); } - public function DisplayNewTicketForm(web_page $oPage) + public function DisplayNewTicketForm(WebPage $oPage) { assert($this->m_sCurrentStep == '1'); $this->m_sNextStep = '2'; @@ -280,7 +280,7 @@ class IncidentCreationWizard extends DialogWizard $oPage->add(''); } - public function DisplayImpactedInfraForm(web_page $oPage) + public function DisplayImpactedInfraForm(WebPage $oPage) { assert($this->m_sCurrentStep == '2'); $this->m_sNextStep = '3'; @@ -296,7 +296,7 @@ class IncidentCreationWizard extends DialogWizard $oPage->add(''); } - public function DisplayAdditionalImpactedObjectForm(web_page $oPage) + public function DisplayAdditionalImpactedObjectForm(WebPage $oPage) { assert($this->m_sCurrentStep == '3'); $this->m_sNextStep = '4'; @@ -344,7 +344,7 @@ class IncidentCreationWizard extends DialogWizard $oPage->add(''); } - public function DisplayRelatedTicketsForm(web_page $oPage) + public function DisplayRelatedTicketsForm(WebPage $oPage) { assert($this->m_sCurrentStep == '4'); $this->m_sNextStep = '5'; @@ -380,7 +380,7 @@ class IncidentCreationWizard extends DialogWizard $oPage->add(''); } - public function DisplayContactsToNotifyForm(web_page $oPage) + public function DisplayContactsToNotifyForm(WebPage $oPage) { assert($this->m_sCurrentStep == '5'); $this->m_sNextStep = '6'; @@ -395,7 +395,7 @@ class IncidentCreationWizard extends DialogWizard $oPage->add(''); } - function DisplayFinalForm(web_page $oPage) + function DisplayFinalForm(WebPage $oPage) { $oAppContext = new ApplicationContext(); assert($this->m_sCurrentStep == '6'); @@ -483,7 +483,7 @@ class IncidentCreationWizard extends DialogWizard $oPage->add(''); } - public function CreateIncident(web_page $oPage) + public function CreateIncident(WebPage $oPage) { $oAppContext = new ApplicationContext(); assert($this->m_sCurrentStep == '7'); diff --git a/pages/index.php b/pages/index.php index f8d93ec62..5d5b81581 100644 --- a/pages/index.php +++ b/pages/index.php @@ -5,7 +5,7 @@ require_once('../application/dialogstack.class.inc.php'); require_once('../application/startup.inc.php'); -$oPage = new nice_web_page("The very first iTop page"); +$oPage = new NiceWebPage("The very first iTop page"); $oPage->no_cache(); @@ -82,7 +82,7 @@ function DisplaySelectOrg($oPage, $sCurrentOrganization, $iContext) } } -function DisplayDetails(web_page $oPage, $sClassName, $sKey) +function DisplayDetails(WebPage $oPage, $sClassName, $sKey) { global $oContext; //$oObj = MetaModel::GetObject($sClassName, $sKey); @@ -135,7 +135,7 @@ function DisplayDetails(web_page $oPage, $sClassName, $sKey) } // By Rom -function DisplayChangesLog(web_page $oPage, $sClassName, $sKey) +function DisplayChangesLog(WebPage $oPage, $sClassName, $sKey) { global $oContext; //$oObj = MetaModel::GetObject($sClassName, $sKey); @@ -149,7 +149,7 @@ function DisplayChangesLog(web_page $oPage, $sClassName, $sKey) $oPage->p("Delete this object (no confirmation!)"); } -function DumpObjectsAsCSV(web_page $oPage, $sClassName, $oSearchFilter = null, $sSeparator = ",") +function DumpObjectsAsCSV(WebPage $oPage, $sClassName, $oSearchFilter = null, $sSeparator = ",") { global $oContext; @@ -179,7 +179,7 @@ function DumpObjectsAsCSV(web_page $oPage, $sClassName, $oSearchFilter = null, } } -function DumpObjects(web_page $oPage, $sClassName, CMDBSearchFilter $oSearchFilter = null) +function DumpObjects(WebPage $oPage, $sClassName, CMDBSearchFilter $oSearchFilter = null) { global $oContext; @@ -209,7 +209,7 @@ function DumpObjects(web_page $oPage, $sClassName, CMDBSearchFilter $oSearchFilt $oPage->table($aAttribs, $aValues); } -function DisplayEditForm(web_page $oPage, $sClassName, $sKey) +function DisplayEditForm(WebPage $oPage, $sClassName, $sKey) { global $oContext; //$oObj = MetaModel::GetObject($sClassName, $sKey); @@ -264,7 +264,7 @@ function DisplayEditForm(web_page $oPage, $sClassName, $sKey) $oPage->add("
    \n"); } -function DisplayCreationForm(web_page $oPage, $sClassName) +function DisplayCreationForm(WebPage $oPage, $sClassName) { global $oContext; $oPage->p("New $sClassName\n"); @@ -303,7 +303,7 @@ function DisplayCreationForm(web_page $oPage, $sClassName) $oPage->add("\n"); } -function UpdateObject(web_page $oPage, $sClassName, $sKey, $aAttributes) +function UpdateObject(WebPage $oPage, $sClassName, $sKey, $aAttributes) { global $oContext; //$oObj = MetaModel::GetObject($sClassName, $sKey); @@ -346,7 +346,7 @@ function UpdateObject(web_page $oPage, $sClassName, $sKey, $aAttributes) $oPage->p("Return to main page"); } -function DeleteObject(web_page $oPage, $sClassName, $sKey) +function DeleteObject(WebPage $oPage, $sClassName, $sKey) { global $oContext; $sClassLabel = MetaModel::GetName($sClassName); @@ -380,7 +380,7 @@ function DeleteObject(web_page $oPage, $sClassName, $sKey) $oPage->p("Return to main page"); } -function CreateObject(web_page $oPage, $sClassName, $aAttributes) +function CreateObject(WebPage $oPage, $sClassName, $aAttributes) { $oObj = MetaModel::NewObject($sClassName); $sClassLabel = MetaModel::GetName(get_class($oObj)); diff --git a/pages/run_query.php b/pages/run_query.php index 2457121bf..6c75ee4ae 100644 --- a/pages/run_query.php +++ b/pages/run_query.php @@ -5,7 +5,7 @@ require_once('../application/itopwebpage.class.inc.php'); require_once('../application/startup.inc.php'); require_once('../application/loginwebpage.class.inc.php'); -login_web_page::DoLogin(); // Check user rights and prompt if needed +LoginWebPage::DoLogin(); // Check user rights and prompt if needed function ShowExamples($oP, $sExpression) diff --git a/pages/schema.php b/pages/schema.php index 1162f1ecd..d472424d4 100644 --- a/pages/schema.php +++ b/pages/schema.php @@ -457,7 +457,7 @@ function DisplayRelationDetails($oPage, $sRelCode) require_once('../application/loginwebpage.class.inc.php'); -login_web_page::DoLogin(); // Check user rights and prompt if needed +LoginWebPage::DoLogin(); // Check user rights and prompt if needed // Display the menu on the left $oContext = new UserContext(); diff --git a/pages/usermanagement_classproj.php b/pages/usermanagement_classproj.php index 18ee898bb..e5ca28392 100644 --- a/pages/usermanagement_classproj.php +++ b/pages/usermanagement_classproj.php @@ -79,7 +79,7 @@ function ComputeProjections($oPage, $sScope) require_once('../application/loginwebpage.class.inc.php'); -login_web_page::DoLogin(); // Check user rights and prompt if needed +LoginWebPage::DoLogin(); // Check user rights and prompt if needed // Display the menu on the left $oContext = new UserContext(); diff --git a/pages/usermanagement_profileproj.php b/pages/usermanagement_profileproj.php index c29f80849..a5cfb2e94 100644 --- a/pages/usermanagement_profileproj.php +++ b/pages/usermanagement_profileproj.php @@ -90,7 +90,7 @@ function ComputeProjections($oPage) require_once('../application/loginwebpage.class.inc.php'); -login_web_page::DoLogin(); // Check user rights and prompt if needed +LoginWebPage::DoLogin(); // Check user rights and prompt if needed // Display the menu on the left $oContext = new UserContext(); diff --git a/pages/usermanagement_userstatus.php b/pages/usermanagement_userstatus.php index 4468a0cca..34cf804a8 100644 --- a/pages/usermanagement_userstatus.php +++ b/pages/usermanagement_userstatus.php @@ -242,7 +242,7 @@ function ComputeUserRights($oPage, $oUser, $oObject) require_once('../application/loginwebpage.class.inc.php'); -login_web_page::DoLogin(); // Check user rights and prompt if needed +LoginWebPage::DoLogin(); // Check user rights and prompt if needed // Display the menu on the left $oContext = new UserContext(); diff --git a/setup/ajax.dataloader.php b/setup/ajax.dataloader.php index 17f899a5e..66ab56195 100644 --- a/setup/ajax.dataloader.php +++ b/setup/ajax.dataloader.php @@ -26,7 +26,7 @@ header("Expires: Fri, 17 Jul 1970 05:00:00 GMT"); // Date in the past $sFileName = Utils::ReadParam('file', ''); $sSessionStatus = Utils::ReadParam('session_status', ''); $iPercent = (integer)Utils::ReadParam('percent', 0); -setup_web_page::log("Info - Loading file: $sFileName"); +SetupWebPage::log("Info - Loading file: $sFileName"); try { @@ -42,26 +42,26 @@ try $oChange->Set("date", time()); $oChange->Set("userinfo", "Initialization"); $iChangeId = $oChange->DBInsert(); - setup_web_page::log("Info - starting data load session"); + SetupWebPage::log("Info - starting data load session"); $oDataLoader->StartSession($oChange); } $oDataLoader->LoadFile($sFileName); $sResult = sprintf("Info - loading of %s done. (Overall %d %% completed).", basename($sFileName), $iPercent); echo $sResult; - setup_web_page::log($sResult); + SetupWebPage::log($sResult); if ($sSessionStatus == 'end') { $oDataLoader->EndSession(); - setup_web_page::log("Info - ending data load session"); + SetupWebPage::log("Info - ending data load session"); } } catch(Exception $e) { echo "

    An error happened while loading the data

    \n"; echo '

    '.$e."

    \n"; - setup_web_page::log("Error - An error happened while loading the data. ".$e); + SetupWebPage::log("Error - An error happened while loading the data. ".$e); } ?> diff --git a/setup/index.php b/setup/index.php index 2bc1f9118..811f08287 100644 --- a/setup/index.php +++ b/setup/index.php @@ -14,14 +14,14 @@ define('PHP_MIN_VERSION', '5.2.0'); define('MYSQL_MIN_VERSION', '5.0.0'); $sOperation = Utils::ReadParam('operation', 'step1'); -$oP = new setup_web_page('iTop configuration wizard'); +$oP = new SetupWebPage('iTop configuration wizard'); /** * Helper function to check if the current version of PHP * is compatible with the application * @return boolean true if this is Ok, false otherwise */ -function CheckPHPVersion(setup_web_page $oP) +function CheckPHPVersion(SetupWebPage $oP) { $bResult = true; $oP->log('Info - CheckPHPVersion'); @@ -131,7 +131,7 @@ function CheckPHPVersion(setup_web_page $oP) * the existing databases * @return Array The list of databases found in the server */ -function CheckServerConnection(setup_web_page $oP, $sDBServer, $sDBUser, $sDBPwd) +function CheckServerConnection(SetupWebPage $oP, $sDBServer, $sDBUser, $sDBPwd) { $aResult = array(); $oP->log('Info - CheckServerConnection'); @@ -194,7 +194,7 @@ function CheckServerConnection(setup_web_page $oP, $sDBServer, $sDBUser, $sDBPwd * @param $bAllowMissingDatabase boolean Whether or not to allow loading a data model with no corresponding DB * @return none */ -function InitDataModel(setup_web_page $oP, $sConfigFileName, $bAllowMissingDatabase = true) +function InitDataModel(SetupWebPage $oP, $sConfigFileName, $bAllowMissingDatabase = true) { require_once('../core/coreexception.class.inc.php'); require_once('../core/attributedef.class.inc.php'); @@ -215,7 +215,7 @@ function InitDataModel(setup_web_page $oP, $sConfigFileName, $bAllowMissingDatab * Helper function to create the database structure * @return boolean true on success, false otherwise */ -function CreateDatabaseStructure(setup_web_page $oP, Config $oConfig, $sDBName, $sDBPrefix) +function CreateDatabaseStructure(SetupWebPage $oP, Config $oConfig, $sDBName, $sDBPrefix) { InitDataModel($oP, TMP_CONFIG_FILE, true); // Allow the DB to NOT exist since we're about to create it ! $oP->log('Info - CreateDatabaseStructure'); @@ -240,7 +240,7 @@ function CreateDatabaseStructure(setup_web_page $oP, Config $oConfig, $sDBName, * Helper function to create and administrator account for iTop * @return boolean true on success, false otherwise */ -function CreateAdminAccount(setup_web_page $oP, Config $oConfig, $sAdminUser, $sAdminPwd) +function CreateAdminAccount(SetupWebPage $oP, Config $oConfig, $sAdminUser, $sAdminPwd) { $oP->log('Info - CreateAdminAccount'); InitDataModel($oP, TMP_CONFIG_FILE, true); // allow missing DB @@ -258,7 +258,7 @@ function CreateAdminAccount(setup_web_page $oP, Config $oConfig, $sAdminUser, $s //aFilesToLoad[aFilesToLoad.length] = './menus.xml'; // First load the menus -function ListDataFiles($sDirectory, setup_web_page $oP) +function ListDataFiles($sDirectory, SetupWebPage $oP) { $aFilesToLoad = array(); if ($hDir = @opendir($sDirectory)) @@ -288,7 +288,7 @@ function ListDataFiles($sDirectory, setup_web_page $oP) /** * Scans the ./data directory for XML files and output them as a Javascript array */ -function PopulateDataFilesList(setup_web_page $oP) +function PopulateDataFilesList(SetupWebPage $oP) { $oP->add("'; // TO DO: add support for $aExtraParams in asynchronous/Ajax mode + '; + } + if ($bAutoReload) + { + $sHtml .= ' + '; } return $sHtml; } diff --git a/core/config.class.inc.php b/core/config.class.inc.php index aa7aa5289..62107892e 100644 --- a/core/config.class.inc.php +++ b/core/config.class.inc.php @@ -18,6 +18,8 @@ class ConfigException extends CoreException define ('DEFAULT_MIN_DISPLAY_LIMIT', 10); define ('DEFAULT_MAX_DISPLAY_LIMIT', 15); +define ('DEFAULT_STANDARD_RELOAD_INTERVAL', 5*60); +define ('DEFAULT_FAST_RELOAD_INTERVAL', 1*60); class Config { @@ -43,6 +45,15 @@ class Config */ protected $m_iMaxDisplayLimit; + /** + * @var integer Number of seconds between two reloads of the display (standard) + */ + protected $m_iStandardReloadInterval; + /** + * @var integer Number of seconds between two reloads of the display (fast) + */ + protected $m_iFastReloadInterval; + public function __construct($sConfigFile, $bLoadConfig = true) { $this->m_sFile = $sConfigFile; @@ -136,6 +147,8 @@ class Config $this->m_iMinDisplayLimit = isset($MySettings['min_display_limit']) ? trim($MySettings['min_display_limit']) : DEFAULT_MIN_DISPLAY_LIMIT; $this->m_iMaxDisplayLimit = isset($MySettings['max_display_limit']) ? trim($MySettings['max_display_limit']) : DEFAULT_MAX_DISPLAY_LIMIT; + $this->m_iStandardReloadInterval = isset($MySettings['standard_reload_interval']) ? trim($MySettings['standard_reload_interval']) : DEFAULT_STANDARD_RELOAD_INTERVAL; + $this->m_iFastReloadInterval = isset($MySettings['fast_reload_interval']) ? trim($MySettings['fast_reload_interval']) : DEFAULT_FAST_RELOAD_INTERVAL; } protected function Verify() @@ -204,6 +217,16 @@ class Config return $this->m_iMaxDisplayLimit; } + public function GetStandardReloadInterval() + { + return $this->m_iStandardReloadInterval; + } + + public function GetFastReloadInterval() + { + return $this->m_iFastReloadInterval; + } + public function SetDBHost($sDBHost) { $this->m_sDBHost = $sDBHost; @@ -239,6 +262,16 @@ class Config $this->m_iMaxDisplayLimit = $iMaxDisplayLimit; } + public function SetStandardReloadInterval($iStandardReloadInterval) + { + $this->m_iStandardReloadInterval = $iStandardReloadInterval; + } + + public function SetFastReloadInterval($iFastReloadInterval) + { + $this->m_iFastReloadInterval = $iFastReloadInterval; + } + public function FileIsWritable() { return is_writable($this->m_sFile); @@ -278,6 +311,8 @@ class Config fwrite($hFile, "\n"); fwrite($hFile, "\t'min_display_limit' => {$this->m_iMinDisplayLimit},\n"); fwrite($hFile, "\t'max_display_limit' => {$this->m_iMaxDisplayLimit},\n"); + fwrite($hFile, "\t'standard_reload_interval' => {$this->m_iStandardReloadInterval},\n"); + fwrite($hFile, "\t'fast_reload_interval' => {$this->m_iFastReloadInterval},\n"); fwrite($hFile, ");\n"); fwrite($hFile, "\n/**\n"); diff --git a/js/utils.js b/js/utils.js index 0f2e795ab..c97e95992 100644 --- a/js/utils.js +++ b/js/utils.js @@ -18,6 +18,25 @@ function ReloadTruncatedList(divId, sSerializedFilter, sExtraParams) } ); } +/** + * Reload any block -- used for periodic auto-reload + */ +function ReloadBlock(divId, sStyle, sSerializedFilter, sExtraParams) +{ + $('#'+divId).addClass('loading'); + //$('#'+divId).blockUI(); + $.get('ajax.render.php?filter='+sSerializedFilter+'&style='+sStyle, + { operation: 'ajax', extra_params: sExtraParams }, + function(data){ + $('#'+divId).empty(); + $('#'+divId).append(data); + $('#'+divId).removeClass('loading'); + $('#'+divId+' .listResults').tableHover(); // hover tables + $('#'+divId+' .listResults').tablesorter( { headers: { 0:{sorter: false }}, widgets: ['zebra']} ); // sortable and zebra tables + //$('#'+divId).unblockUI(); + } + ); +} /** * Update the display and value of a file input widget when the user picks a new file From 23c7d036816312d7975e725c750cbbe9bc88b797 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Sun, 17 Jan 2010 16:37:04 +0000 Subject: [PATCH 180/227] Background image for the welcome screen SVN:trunk[257] --- images/welcome.jpg | Bin 0 -> 37902 bytes 1 file changed, 0 insertions(+), 0 deletions(-) create mode 100644 images/welcome.jpg diff --git a/images/welcome.jpg b/images/welcome.jpg new file mode 100644 index 0000000000000000000000000000000000000000..5eedefc4bd879690daf9d9bd098a0b8857aca533 GIT binary patch literal 37902 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 Lz-uY9U%meavS%+^ literal 0 HcmV?d00001 From 6b357322800e461c55570b2a111ee4423e28d4aa Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Sun, 17 Jan 2010 16:58:19 +0000 Subject: [PATCH 181/227] - Removed the version number from the welcome page - Nicer welcome page - Removed old notification menus SVN:trunk[258] --- setup/data/structure/1.menus.xml | 128 +++++++++---------------------- 1 file changed, 38 insertions(+), 90 deletions(-) diff --git a/setup/data/structure/1.menus.xml b/setup/data/structure/1.menus.xml index 64820cb60..d421cd77d 100644 --- a/setup/data/structure/1.menus.xml +++ b/setup/data/structure/1.menus.xml @@ -849,15 +849,16 @@ text-align:center; ./UI.php - application 1 0 0 - -Customization - -UI.php - - -administrator -2 -1 -0 - - -Triggers - entering - -UI.php -../images/std_view.gif - -administrator -2 -500 -0 - - -Triggers - leaving - -UI.php -../images/std_view.gif - -administrator -2 -500 -0 - - -Actions - -UI.php -../images/std_view.gif - -administrator -2 -500 -0 - Application log From 07d2f5e763e60ebe396587b8fa17baf378ea9742 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Sun, 17 Jan 2010 17:33:04 +0000 Subject: [PATCH 182/227] - New datas for the setup: default trigger & email action SVN:trunk[259] --- setup/data/structure/23.triggers.xml | 7 +++++++ setup/data/structure/24.actions.xml | 21 +++++++++++++++++++++ 2 files changed, 28 insertions(+) create mode 100644 setup/data/structure/23.triggers.xml create mode 100644 setup/data/structure/24.actions.xml diff --git a/setup/data/structure/23.triggers.xml b/setup/data/structure/23.triggers.xml new file mode 100644 index 000000000..06b285828 --- /dev/null +++ b/setup/data/structure/23.triggers.xml @@ -0,0 +1,7 @@ + + + +Incident Creation +bizIncidentTicket + + \ No newline at end of file diff --git a/setup/data/structure/24.actions.xml b/setup/data/structure/24.actions.xml new file mode 100644 index 000000000..53563f544 --- /dev/null +++ b/setup/data/structure/24.actions.xml @@ -0,0 +1,21 @@ + + + +Simple eMail +Default eMail action, tailor it to fit your needs + + +SELECT bizContact AS C WHERE C.id = :this->caller_id + + +New iTop ticket [$this->severity$]: $this->name$, $this->title$ +<h1>A new iTop incident has been created: $this->name$</h1> +<p>Description: $this->title$</p> +<p>Initial situation: <pre>$this->initial_situation$</pre></p> +<p>Ticket Type: $this->type$</p> +<p>Ticket assigned to: $this->workgroup_name$</p> +<hr/> +<p>More information: $this->hyperlink()$</p> +normal + + From c0545aa9ccf54fefe135fc5e84b106e0ddfde547 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Sun, 17 Jan 2010 17:34:28 +0000 Subject: [PATCH 183/227] - Log the 'From' field for email notifications SVN:trunk[260] --- core/action.class.inc.php | 1 + core/event.class.inc.php | 3 ++- 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/core/action.class.inc.php b/core/action.class.inc.php index 51cee9af5..ccd747e95 100644 --- a/core/action.class.inc.php +++ b/core/action.class.inc.php @@ -231,6 +231,7 @@ class ActionEmail extends ActionNotification $oLog->Set('trigger_id', $oTrigger->GetKey()); $oLog->Set('action_id', $this->GetKey()); $oLog->Set('object_id', $aContextArgs['this->id']); + $oLog->Set('from', $sFrom); $oLog->Set('to', $sTo); $oLog->Set('cc', $sCC); $oLog->Set('bcc', $sBCC); diff --git a/core/event.class.inc.php b/core/event.class.inc.php index 79b2efbe0..77a164892 100644 --- a/core/event.class.inc.php +++ b/core/event.class.inc.php @@ -112,13 +112,14 @@ class EventNotificationEmail extends EventNotification MetaModel::Init_AddAttribute(new AttributeText("to", array("label"=>"TO", "description"=>"TO", "allowed_values"=>null, "sql"=>"to", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeText("cc", array("label"=>"CC", "description"=>"CC", "allowed_values"=>null, "sql"=>"cc", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeText("bcc", array("label"=>"BCC", "description"=>"BCC", "allowed_values"=>null, "sql"=>"bcc", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeText("from", array("label"=>"From", "description"=>"Sender of the message", "allowed_values"=>null, "sql"=>"from", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeText("subject", array("label"=>"Subject", "description"=>"Subject", "allowed_values"=>null, "sql"=>"subject", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeText("body", array("label"=>"Body", "description"=>"Body", "allowed_values"=>null, "sql"=>"body", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_InheritFilters(); // Display lists - MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'trigger_id', 'action_id', 'object_id', 'to', 'cc', 'bcc', 'subject', 'body')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'trigger_id', 'action_id', 'object_id', 'from', 'to', 'cc', 'bcc', 'subject', 'body')); // Attributes to be displayed for the complete details MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'subject')); // Attributes to be displayed for a list // Search criteria // MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form From cdb68e9159a4d13406c5b651fd9c42eb948fe612 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Sun, 17 Jan 2010 17:55:57 +0000 Subject: [PATCH 184/227] Bug fix: initialize properly auto_reload timeouts SVN:trunk[261] --- core/config.class.inc.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/config.class.inc.php b/core/config.class.inc.php index 62107892e..7f32ff615 100644 --- a/core/config.class.inc.php +++ b/core/config.class.inc.php @@ -68,6 +68,8 @@ class Config $this->m_sDBSubname = ''; $this->m_iMinDisplayLimit = DEFAULT_MIN_DISPLAY_LIMIT; $this->m_iMaxDisplayLimit = DEFAULT_MAX_DISPLAY_LIMIT; + $this->m_iStandardReloadInterval = DEFAULT_STANDARD_RELOAD_INTERVAL; + $this->m_iFastReloadInterval = DEFAULT_FAST_RELOAD_INTERVAL; if ($bLoadConfig) { $this->Load($sConfigFile); From 9250cb3c130c2bdb6a0eb98309ab2450db399e80 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Sun, 17 Jan 2010 18:03:04 +0000 Subject: [PATCH 185/227] - Latest "Notifications" admin page. SVN:trunk[262] --- setup/data/structure/1.menus.xml | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/setup/data/structure/1.menus.xml b/setup/data/structure/1.menus.xml index d421cd77d..6b5317bc7 100644 --- a/setup/data/structure/1.menus.xml +++ b/setup/data/structure/1.menus.xml @@ -954,7 +954,7 @@ display: list-item; <h1>Configuration of the <span class="hilite">Notifications</span></h1> </div> <itoptoggle name="Help" open="true"> -<div style="padding: 1em; font-size:1.25em;background: #E8F3CF;margin-top: 0.25em;"> +<div style="padding: 1em; font-size:1.25em;background:#E8F3CF;margin-top: 0.25em;"> <img src="../images/bell.png" style="margin-top: -60px; margin-right: 10px; float: right;"> <p>In iTop the notifications are fully customizable. They are based on two sets of objects: <i>triggers and actions</i>.</p> <p><i><b>Triggers</b></i> define when a notification will be executed. There are 3 types of triggers for covering 3 differents phases of an object life cycle: @@ -976,14 +976,18 @@ When associated with a trigger, each action is given an 'order' number <itoptabs> <itoptab name="Triggers"> <h3>Available Triggers</h3> - <table> + <table style="border:0;border-spacing:10px;width:100%;"> <tr> - <td style="vertical-align:top;width:50%;padding:1em;"> - <h4>When entering a state</h4> + <td style="vertical-align:top;width:33%;padding:10px;background:#f8f8f8;"> + <h4>When an object is created</h4> + <itopblock BlockClass="DisplayBlock" type="list" asynchronous="false" encoding="text/oql">SELECT TriggerOnObjectCreate</itopblock> + </td> + <td style="vertical-align:top;width:33%;padding:10px;background:#f8f8f8;"> + <h4>When an object enters a state</h4> <itopblock BlockClass="DisplayBlock" type="list" asynchronous="false" encoding="text/oql">SELECT TriggerOnStateEnter</itopblock> </td> - <td style="vertical-align:top;width:50%;padding:1em;"> - <h4>When leaving a state</h4> + <td style="vertical-align:top;width:33%;padding:10px;background:#f8f8f8;"> + <h4>When an object leaves a state</h4> <itopblock BlockClass="DisplayBlock" type="list" asynchronous="false" encoding="text/oql">SELECT TriggerOnStateLeave</itopblock> </td> </tr> @@ -993,8 +997,7 @@ When associated with a trigger, each action is given an 'order' number <h3>Available Actions</h3> <itopblock BlockClass="DisplayBlock" type="list" asynchronous="false" encoding="text/oql">SELECT ActionEmail</itopblock> </itoptab> -</itoptabs> - +</itoptabs> administrator 2 1 From 2e26062560f9b86552cfb9e482d688c5dc055a44 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Mon, 18 Jan 2010 07:43:26 +0000 Subject: [PATCH 186/227] Finalized the display of Enumeration attributes (the label may now be different than the value) Finalized the verification of the DB structure, which could not detect wrong field types so far SVN:trunk[263] --- core/attributedef.class.inc.php | 31 +++++++++++++++++++++++++++---- core/cmdbsource.class.inc.php | 9 +++++++++ core/metamodel.class.php | 19 +++++++++++++++++-- 3 files changed, 53 insertions(+), 6 deletions(-) diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php index 145020461..8f5937e04 100644 --- a/core/attributedef.class.inc.php +++ b/core/attributedef.class.inc.php @@ -406,7 +406,7 @@ class AttributeInteger extends AttributeDBField public function GetType() {return "Integer";} public function GetTypeDesc() {return "Numeric value (could be negative)";} public function GetEditClass() {return "String";} - protected function GetSQLCol() {return "INT";} + protected function GetSQLCol() {return "INT(11)";} public function GetBasicFilterOperators() { @@ -766,7 +766,7 @@ class AttributeEnum extends AttributeString $oValDef = $this->GetValuesDef(); if ($oValDef) { - $aValues = CMDBSource::Quote($oValDef->GetValues(array(), ""), true); + $aValues = CMDBSource::Quote(array_keys($oValDef->GetValues(array(), "")), true); } else { @@ -774,7 +774,11 @@ class AttributeEnum extends AttributeString } if (count($aValues) > 0) { - return "ENUM(".implode(", ", $aValues).")"; + // The syntax used here is matters + // In particular, I had to remove unnecessary spaces to stick to + // make sure that this string will match the field type returned by the DB + // (used to perform a comparison between the current DB format and the data model) + return "ENUM(".implode(",", $aValues).")"; } else { @@ -795,6 +799,25 @@ class AttributeEnum extends AttributeString { return parent::GetBasicFilterSQLExpr($sOpCode, $value); } + + public function GetAsHTML($sValue) + { + $oValDef = $this->GetValuesDef(); + if ($oValDef) + { + $aValues = $oValDef->GetValues(array(), ""); + } + if (!empty($aValues) && array_key_exists($sValue, $aValues)) + { + $sLabel = $aValues[$sValue]; + } + else + { + $sLabel = $sValue.' ERROR could not find'; + } + // later, we could imagine a detailed description in the title + return "".parent::GetAsHtml($sLabel).""; + } } /** @@ -985,7 +1008,7 @@ class AttributeExternalKey extends AttributeDBFieldVoid public function GetType() {return "Extkey";} public function GetTypeDesc() {return "Link to another object";} public function GetEditClass() {return "ExtKey";} - protected function GetSQLCol() {return "INT";} + protected function GetSQLCol() {return "INT(11)";} public function IsExternalKey($iType = EXTKEY_RELATIVE) {return true;} public function GetTargetClass($iType = EXTKEY_RELATIVE) {return $this->Get("targetclass");} diff --git a/core/cmdbsource.class.inc.php b/core/cmdbsource.class.inc.php index 20f3fed9e..3543349e6 100644 --- a/core/cmdbsource.class.inc.php +++ b/core/cmdbsource.class.inc.php @@ -342,6 +342,15 @@ class CMDBSource return (strtolower($aFieldData["Null"]) == "yes"); } + public static function GetFieldType($sTable, $sField) + { + $aTableInfo = self::GetTableInfo($sTable); + if (empty($aTableInfo)) return false; + if (!array_key_exists($sField, $aTableInfo["Fields"])) return false; + $aFieldData = $aTableInfo["Fields"][$sField]; + return ($aFieldData["Type"]); + } + public static function HasIndex($sTable, $sField) { $aTableInfo = self::GetTableInfo($sTable); diff --git a/core/metamodel.class.php b/core/metamodel.class.php index 6177cace2..28a5fcce6 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -2099,19 +2099,34 @@ abstract class MetaModel } else { + // The field already exists, does it have the relevant properties? + // + $bToBeChanged = false; if ($oAttDef->IsNullAllowed() != CMDBSource::IsNullAllowed($sTable, $sField)) { + $bToBeChanged = true; if ($oAttDef->IsNullAllowed()) { $aErrors[$sClass][] = "field '$sField' in table '$sTable' could be NULL"; - $aSugFix[$sClass][] = "ALTER TABLE `$sTable` CHANGE `$sField` `$sField` $sFieldSpecs"; } else { $aErrors[$sClass][] = "field '$sField' in table '$sTable' could NOT be NULL"; - $aSugFix[$sClass][] = "ALTER TABLE `$sTable` CHANGE `$sField` `$sField` $sFieldSpecs"; } } + $sActualFieldType = CMDBSource::GetFieldType($sTable, $sField); + if (strcasecmp($sDBFieldType, $sActualFieldType) != 0) + { + $bToBeChanged = true; + $aErrors[$sClass][] = "field '$sField' in table '$sTable' has a wrong type: found '$sActualFieldType' while expecting '$sDBFieldType'"; + } + if ($bToBeChanged) + { + $aSugFix[$sClass][] = "ALTER TABLE `$sTable` CHANGE `$sField` `$sField` $sFieldSpecs"; + } + + // Create indexes (external keys only... so far) + // if ($oAttDef->IsExternalKey() && !CMDBSource::HasIndex($sTable, $sField)) { $aErrors[$sClass][] = "Foreign key '$sField' in table '$sTable' should have an index"; From 7989d8dc7a8dd39dce08384bb35cf2c5f0fc51e1 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Mon, 18 Jan 2010 15:20:38 +0000 Subject: [PATCH 187/227] - Display the notifications sent for an Incident ticket SVN:trunk[264] --- business/templates/ticket.html | 3 +++ 1 file changed, 3 insertions(+) diff --git a/business/templates/ticket.html b/business/templates/ticket.html index fe8f2d5c7..10df94386 100644 --- a/business/templates/ticket.html +++ b/business/templates/ticket.html @@ -15,6 +15,9 @@ SELECT lnkContactTicket WHERE ticket_id = $pkey$ + + SELECT EventNotificationEmail AS Ev JOIN TriggerOnObject AS T ON Ev.trigger_id = T.id WHERE T.target_class = 'bizIncidentTicket' AND Ev.object_id = $pkey$ + From 871b511368cbe0f7747272927509e49b180c4ba6 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Mon, 18 Jan 2010 15:22:26 +0000 Subject: [PATCH 188/227] Disabled (commented out) the lifecycle declaration for bizServer, bizContract and bizService SVN:trunk[265] --- business/ServiceMgmt.business.php | 12 ++++++++---- business/itop.business.class.inc.php | 15 +++++++-------- 2 files changed, 15 insertions(+), 12 deletions(-) diff --git a/business/ServiceMgmt.business.php b/business/ServiceMgmt.business.php index dd8865a05..0319fba53 100644 --- a/business/ServiceMgmt.business.php +++ b/business/ServiceMgmt.business.php @@ -17,7 +17,8 @@ class bizService extends cmdbAbstractObject "key_type" => "autoincrement", "key_label" => "id", "name_attcode" => "name", - "state_attcode" => "status", + //"state_attcode" => "status", + "state_attcode" => "", "reconc_keys" => array("org_id", "name"), // inherited attributes "db_table" => "services", "db_key_field" => "id", @@ -42,6 +43,7 @@ class bizService extends cmdbAbstractObject MetaModel::Init_AddFilterFromAttribute("type"); MetaModel::Init_AddFilterFromAttribute("status"); +/* // Life cycle MetaModel::Init_DefineState("New", array("label"=>"New", "description"=>"Newly created service", "attribute_inherit"=>null, "attribute_list"=>array())); @@ -59,7 +61,7 @@ class bizService extends cmdbAbstractObject MetaModel::Init_DefineTransition("New", "ev_implement", array("target_state"=>"Implementation", "actions"=>array(), "user_restriction"=>null)); MetaModel::Init_DefineTransition("Implementation", "ev_move2prod", array("target_state"=>"Production", "actions"=>array(), "user_restriction"=>null)); MetaModel::Init_DefineTransition("Production", "ev_obsolete", array("target_state"=>"Obsolete", "actions"=>array('IncrementVersion'), "user_restriction"=>null)); - +*/ MetaModel::Init_SetZListItems('details', array('name', 'status', 'org_id','service_category','type','status','description')); // Attributes to be displayed for the complete details MetaModel::Init_SetZListItems('list', array('name', 'status', 'org_id','service_category','type')); // Attributes to be displayed for a list @@ -95,7 +97,8 @@ class bizContract extends cmdbAbstractObject "key_type" => "autoincrement", "key_label" => "id", "name_attcode" => "name", - "state_attcode" => "status", + //"state_attcode" => "status", + "state_attcode" => "", "reconc_keys" => array("org_id", "name"), // inherited attributes "db_table" => "contracts", "db_key_field" => "id", @@ -139,6 +142,7 @@ class bizContract extends cmdbAbstractObject MetaModel::Init_AddFilterFromAttribute("type"); +/* // Life cycle MetaModel::Init_DefineState("New", array("label"=>"New", "description"=>"Newly created contract", "attribute_inherit"=>null, "attribute_list"=>array('name' => OPT_ATT_MANDATORY,'org_id' => OPT_ATT_MANDATORY, 'service_id' => OPT_ATT_MANDATORY,'type' => OPT_ATT_MANDATORY, 'description' => OPT_ATT_MANDATORY))); @@ -162,7 +166,7 @@ class bizContract extends cmdbAbstractObject MetaModel::Init_DefineTransition("Signed", "ev_begin", array("target_state"=>"Production", "actions"=>array('SetProdDate'), "user_restriction"=>null)); MetaModel::Init_DefineTransition("Signed", "ev_terminate", array("target_state"=>"Finished", "actions"=>array(), "user_restriction"=>null)); MetaModel::Init_DefineTransition("Production", "ev_terminate", array("target_state"=>"Finished", "actions"=>array(), "user_restriction"=>null)); - +*/ MetaModel::Init_SetZListItems('details', array('name', 'status', 'org_id', 'service_id','provider_name','type','description','team_id','service_level','cost','currency','cost_unit','cost_freq','move2prod_date','end_prod', 'version_number')); // Attributes to be displayed for the complete details MetaModel::Init_SetZListItems('list', array('name', 'status', 'org_id', 'service_id','provider_name','service_name','service_level','type')); // Attributes to be displayed for a list diff --git a/business/itop.business.class.inc.php b/business/itop.business.class.inc.php index 17ae3d77c..a9de9bbb7 100644 --- a/business/itop.business.class.inc.php +++ b/business/itop.business.class.inc.php @@ -249,7 +249,7 @@ class bizPerson extends bizContact "key_label" => "id", "name_attcode" => "name", "state_attcode" => "", - "reconc_keys" => array("org_id", "first_name", "name"), // comment en définir plusieurs + "reconc_keys" => array("org_id", "first_name", "name"), // comment en définir plusieurs // "reconc_keys" => array("org_id", "employee_number"), "db_table" => "persons", // Can it use the same physical DB table as any contact ? "db_key_field" => "id", @@ -591,7 +591,7 @@ class lnkContactInfra extends cmdbAbstractObject //////////////////////////////////////////////////////////////////////////////////// /** * bizLocation (Region, Country, City, Site, Building, Floor, Room, Rack,...) -* pourrait être mis en plusieurs sous objects, puisqu'une adresse sur region n'a pas trop de sens +* pourrait être mis en plusieurs sous objects, puisqu'une adresse sur region n'a pas trop de sens * */ //////////////////////////////////////////////////////////////////////////////////// @@ -1170,6 +1170,7 @@ class bizServer extends bizDevice "key_label" => "id", "name_attcode" => "name", "state_attcode" => "status", + "state_attcode" => "", "reconc_keys" => array("org_id", "name"), // inherited attributes "db_table" => "servers", "db_key_field" => "id", @@ -1190,6 +1191,7 @@ class bizServer extends bizDevice MetaModel::Init_AddAttribute(new AttributeString("default_gateway", array("label"=>"Default Gateway", "description"=>"Default Gateway for this device", "allowed_values"=>null, "sql"=>"default_gateway", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("status"); MetaModel::Init_AddFilterFromAttribute("memory_size"); MetaModel::Init_AddFilterFromAttribute("cpu"); MetaModel::Init_AddFilterFromAttribute("number_of_cpus"); @@ -1199,6 +1201,7 @@ class bizServer extends bizDevice MetaModel::Init_AddFilterFromAttribute("os_version"); +/* // Life cycle MetaModel::Init_DefineState("InStore", array("label"=>"InStore", "description"=>"Device in store", "attribute_inherit"=>null, "attribute_list"=>array())); @@ -1215,7 +1218,6 @@ class bizServer extends bizDevice MetaModel::Init_DefineState("Obsolete", array("label"=>"Obsolete", "description"=>"The device is no more used", "attribute_inherit"=>null, "attribute_list"=>array())); - MetaModel::Init_DefineStimulus("ev_store", new StimulusUserAction(array("label"=>"Store this server", "description"=>"This server is move to storage"))); MetaModel::Init_DefineStimulus("ev_ship", new StimulusUserAction(array("label"=>"Ship this server", "description"=>"This server is shipped to futur location"))); MetaModel::Init_DefineStimulus("ev_plug", new StimulusUserAction(array("label"=>"Plug this server", "description"=>"The server is pluuged on the network"))); @@ -1242,14 +1244,11 @@ class bizServer extends bizDevice MetaModel::Init_DefineTransition("BeingDeconfigured", "ev_store", array("target_state"=>"InStore", "actions"=>array(), "user_restriction"=>null)); MetaModel::Init_DefineTransition("BeingDeconfigured", "ev_obsolete", array("target_state"=>"Obsolete", "actions"=>array(), "user_restriction"=>null)); MetaModel::Init_DefineTransition("Obsolete", "ev_recycle", array("target_state"=>"BeingDeconfigured", "actions"=>array(), "user_restriction"=>null)); - - - - +*/ // Display lists - MetaModel::Init_SetZListItems('details', array('name', 'mgmt_ip','default_gateway','status', 'severity','org_id', 'location_id', 'brand', 'model', 'os_family', 'os_version','serial_number','shipment_number', 'cpu', 'number_of_cpus', 'memory_size', 'hdd_size', 'hdd_free_size')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('details', array('name', 'status', 'mgmt_ip','default_gateway', 'severity','org_id', 'location_id', 'brand', 'model', 'os_family', 'os_version','serial_number','shipment_number', 'cpu', 'number_of_cpus', 'memory_size', 'hdd_size', 'hdd_free_size')); // Attributes to be displayed for the complete details MetaModel::Init_SetZListItems('list', array('name', 'status','severity', 'org_id', 'location_id', 'brand', 'model', 'os_family', 'os_version')); // Attributes to be displayed for a list // Search criteria MetaModel::Init_SetZListItems('standard_search', array('name', 'status','severity', 'brand', 'model', 'os_family', 'location_id')); // Criteria of the std search form From bda8f0b8149204c5039ad7a4b30f16d485784aaa Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Mon, 18 Jan 2010 15:22:48 +0000 Subject: [PATCH 189/227] - Adapted the triggers hierarchy and the EventLog search capabilities to be able to display all the notifications sent for a given object (a ticket for instance) SVN:trunk[266] --- core/action.class.inc.php | 1 + core/event.class.inc.php | 1 + core/trigger.class.inc.php | 57 ++++++++++++++++++++++++++++++-------- 3 files changed, 47 insertions(+), 12 deletions(-) diff --git a/core/action.class.inc.php b/core/action.class.inc.php index ccd747e95..dbeaeeea8 100644 --- a/core/action.class.inc.php +++ b/core/action.class.inc.php @@ -34,6 +34,7 @@ abstract class Action extends cmdbAbstractObject //MetaModel::Init_InheritAttributes(); MetaModel::Init_AddAttribute(new AttributeString("name", array("label"=>"Name", "description"=>"label", "allowed_values"=>null, "sql"=>"name", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("description", array("label"=>"Description", "description"=>"one line description", "allowed_values"=>null, "sql"=>"description", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("related_triggers", array("label"=>"Related Triggers", "description"=>"Triggers linked to this action", "linked_class"=>"lnkTriggerAction", "ext_key_to_me"=>"action_id", "ext_key_to_remote"=>"trigger_id", "allowed_values"=>null, "count_min"=>0, "count_max"=>0, "depends_on"=>array()))); //MetaModel::Init_InheritFilters(); MetaModel::Init_AddFilterFromAttribute("name"); diff --git a/core/event.class.inc.php b/core/event.class.inc.php index 77a164892..9955dddbd 100644 --- a/core/event.class.inc.php +++ b/core/event.class.inc.php @@ -77,6 +77,7 @@ class EventNotification extends Event MetaModel::Init_InheritFilters(); MetaModel::Init_AddFilterFromAttribute("trigger_id"); MetaModel::Init_AddFilterFromAttribute("action_id"); + MetaModel::Init_AddFilterFromAttribute("object_id"); // Display lists MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'trigger_id', 'action_id', 'object_id')); // Attributes to be displayed for the complete details diff --git a/core/trigger.class.inc.php b/core/trigger.class.inc.php index 6d9d1a637..e9de6eaa3 100644 --- a/core/trigger.class.inc.php +++ b/core/trigger.class.inc.php @@ -23,7 +23,7 @@ class Trigger extends cmdbAbstractObject "description" => "Custom event handler", "key_type" => "autoincrement", "key_label" => "", - "name_attcode" => "", + "name_attcode" => "description", "state_attcode" => "", "reconc_keys" => array(), "db_table" => "priv_trigger", @@ -61,7 +61,42 @@ class Trigger extends cmdbAbstractObject } } -class TriggerOnStateChange extends Trigger +class TriggerOnObject extends Trigger +{ + public static function Init() + { + $aParams = array + ( + "category" => "core/cmdb", + "name" => "Trigger on a class of objects", + "description" => "Trigger on a given class of objects", + "key_type" => "autoincrement", + "key_label" => "", + "name_attcode" => "", + "state_attcode" => "", + "reconc_keys" => array(), + "db_table" => "priv_trigger_onobject", + "db_key_field" => "id", + "db_finalclass_field" => "", + "display_template" => "", + ); + MetaModel::Init_Params($aParams); + MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeClass("target_class", array("label"=>"Target class", "description"=>"label", "class_category"=>"bizmodel", "more_values"=>null, "sql"=>"target_class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + + MetaModel::Init_InheritFilters(); + MetaModel::Init_AddFilterFromAttribute("target_class"); + + // Display lists + MetaModel::Init_SetZListItems('details', array('description', 'target_class')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class', 'description')); // Attributes to be displayed for a list + // Search criteria +// MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form +// MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form + } +} + +class TriggerOnStateChange extends TriggerOnObject { public static function Init() { @@ -82,16 +117,14 @@ class TriggerOnStateChange extends Trigger ); MetaModel::Init_Params($aParams); MetaModel::Init_InheritAttributes(); - MetaModel::Init_AddAttribute(new AttributeClass("target_class", array("label"=>"Target class", "description"=>"label", "class_category"=>"bizmodel", "more_values"=>null, "sql"=>"target_class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("state", array("label"=>"State", "description"=>"label", "allowed_values"=>null, "sql"=>"state", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_InheritFilters(); - MetaModel::Init_AddFilterFromAttribute("target_class"); MetaModel::Init_AddFilterFromAttribute("state"); // Display lists MetaModel::Init_SetZListItems('details', array('description', 'target_class', 'state')); // Attributes to be displayed for the complete details - MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class', 'state')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class', 'state', 'description')); // Attributes to be displayed for a list // Search criteria // MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form // MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form @@ -124,7 +157,7 @@ class TriggerOnStateEnter extends TriggerOnStateChange // Display lists MetaModel::Init_SetZListItems('details', array('description', 'target_class', 'state')); // Attributes to be displayed for the complete details - MetaModel::Init_SetZListItems('list', array('target_class', 'state')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('list', array('target_class', 'state', 'description')); // Attributes to be displayed for a list // Search criteria // MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form // MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form @@ -157,14 +190,14 @@ class TriggerOnStateLeave extends TriggerOnStateChange // Display lists MetaModel::Init_SetZListItems('details', array('description', 'target_class', 'state')); // Attributes to be displayed for the complete details - MetaModel::Init_SetZListItems('list', array('target_class', 'state')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('list', array('target_class', 'state', 'description')); // Attributes to be displayed for a list // Search criteria // MetaModel::Init_SetZListItems('standard_search', array('')); // Criteria of the std search form // MetaModel::Init_SetZListItems('advanced_search', array('')); // Criteria of the advanced search form } } -class TriggerOnObjectCreate extends Trigger +class TriggerOnObjectCreate extends TriggerOnObject { public static function Init() { @@ -185,14 +218,12 @@ class TriggerOnObjectCreate extends Trigger ); MetaModel::Init_Params($aParams); MetaModel::Init_InheritAttributes(); - MetaModel::Init_AddAttribute(new AttributeClass("target_class", array("label"=>"Target class", "description"=>"label", "class_category"=>"bizmodel", "more_values"=>null, "sql"=>"target_class", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_InheritFilters(); - MetaModel::Init_AddFilterFromAttribute("target_class"); // Display lists MetaModel::Init_SetZListItems('details', array('description', 'target_class')); // Attributes to be displayed for the complete details - MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('list', array('finalclass', 'target_class', 'description')); // Attributes to be displayed for a list // Search criteria // MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form // MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form @@ -222,14 +253,16 @@ class lnkTriggerAction extends cmdbAbstractObject MetaModel::Init_AddAttribute(new AttributeExternalKey("action_id", array("targetclass"=>"Action", "jointype"=> '', "label"=>"Action", "description"=>"The action to be executed", "allowed_values"=>null, "sql"=>"action_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("action_name", array("label"=>"Action Name", "description"=>"Name of the action", "allowed_values"=>null, "extkey_attcode"=> 'action_id', "target_attcode"=>"name"))); MetaModel::Init_AddAttribute(new AttributeExternalKey("trigger_id", array("targetclass"=>"Trigger", "jointype"=> '', "label"=>"Trigger", "description"=>"Trigger", "allowed_values"=>null, "sql"=>"trigger_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeExternalField("trigger_name", array("label"=>"Trigger Name", "description"=>"Name of the trigger", "allowed_values"=>null, "extkey_attcode"=> 'trigger_id', "target_attcode"=>"description"))); MetaModel::Init_AddAttribute(new AttributeInteger("order", array("label"=>"Order", "description"=>"Actions execution order", "allowed_values"=>null, "sql"=>"order", "default_value"=>0, "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddFilterFromAttribute("action_id"); MetaModel::Init_AddFilterFromAttribute("trigger_id"); + MetaModel::Init_AddFilterFromAttribute("order"); // Display lists MetaModel::Init_SetZListItems('details', array('action_id', 'trigger_id', 'order')); // Attributes to be displayed for a list - MetaModel::Init_SetZListItems('list', array('action_id', 'trigger_id', 'order')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('list', array('action_name', 'trigger_name', 'order')); // Attributes to be displayed for a list // Search criteria MetaModel::Init_SetZListItems('standard_search', array('action_id', 'trigger_id', 'order')); // Criteria of the std search form MetaModel::Init_SetZListItems('advanced_search', array('action_id', 'trigger_id', 'order')); // Criteria of the advanced search form From 808c95531eee8e95060cc1154f9dcc3537249dd1 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Mon, 18 Jan 2010 15:23:55 +0000 Subject: [PATCH 190/227] Setup: added missing link classes to the configuration manager profile SVN:trunk[267] --- addons/userrights/userrightsprofile.class.inc.php | 2 ++ 1 file changed, 2 insertions(+) diff --git a/addons/userrights/userrightsprofile.class.inc.php b/addons/userrights/userrightsprofile.class.inc.php index f65b15145..295528987 100644 --- a/addons/userrights/userrightsprofile.class.inc.php +++ b/addons/userrights/userrightsprofile.class.inc.php @@ -1409,6 +1409,8 @@ class SetupITILProfiles // 'lnkInterfaces', 'lnkClientServer', 'lnkInfraGrouping', + 'lnkContactInfra', + 'lnkContactTeam', ), 'Incident' => array( 'bizIncidentTicket', From 920a98a902cb35e726050f387c34e816ffbd5a14 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Mon, 18 Jan 2010 15:50:18 +0000 Subject: [PATCH 191/227] Fixed bug in the data model: organization status was not an enum SVN:trunk[268] --- business/KEDB.business.php | 2 -- business/itop.business.class.inc.php | 8 +++----- core/attributedef.class.inc.php | 4 +++- 3 files changed, 6 insertions(+), 8 deletions(-) diff --git a/business/KEDB.business.php b/business/KEDB.business.php index 8d814eaf7..23ee63e73 100644 --- a/business/KEDB.business.php +++ b/business/KEDB.business.php @@ -1,7 +1,5 @@ "Name", "description"=>"Common name", "allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array() ))); MetaModel::Init_AddAttribute(new AttributeString("code", array("label"=>"Code", "description"=>"Organization code (Siret, DUNS,...)", "allowed_values"=>null, "sql"=>"code", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array() ))); - MetaModel::Init_AddAttribute(new AttributeEnum("status", array("label"=>"Status", "description"=>"Lifecycle status", "allowed_values"=>$oAllowedStatuses, "sql"=>"status", "default_value"=>"implementation", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("status", array("label"=>"Status", "description"=>"Lifecycle status", "allowed_values"=>new ValueSetEnum(STANDARD_STATUSES), "sql"=>"status", "default_value"=>"implementation", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalKey("parent_id", array("targetclass"=>"bizOrganization", "label"=>"Parent", "description"=>"Parent organization", "allowed_values"=>null, "sql"=>"parent_id", "is_null_allowed"=>true, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("parent_name", array("label"=>"Parent Name", "description"=>"Name of the parent organization", "allowed_values"=>null, "extkey_attcode"=> 'parent_id', "target_attcode"=>"name"))); @@ -126,8 +126,6 @@ class logRealObject extends cmdbAbstractObject { public static function Init() { - global $oAllowedStatuses; - $aParams = array ( "category" => "bizmodel,searchable", @@ -145,7 +143,7 @@ class logRealObject extends cmdbAbstractObject ); MetaModel::Init_Params($aParams); MetaModel::Init_AddAttribute(new AttributeString("name", array("label"=>"Name", "description"=>"Common name", "allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeEnum("status", array("label"=>"Status", "description"=>"Lifecycle status", "allowed_values"=>$oAllowedStatuses, "sql"=>"status", "default_value"=>"implementation", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("status", array("label"=>"Status", "description"=>"Lifecycle status", "allowed_values"=>new ValueSetEnum(STANDARD_STATUSES), "sql"=>"status", "default_value"=>"implementation", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalKey("org_id", array("targetclass"=>"bizOrganization", "label"=>"Organization", "description"=>"ID of the object owner organization", "allowed_values"=>null, "sql"=>"org_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("org_name", array("label"=>"Organization", "description"=>"Company / Department owning this object", "allowed_values"=>null, "extkey_attcode"=> 'org_id', "target_attcode"=>"name"))); diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php index 8f5937e04..ea207937d 100644 --- a/core/attributedef.class.inc.php +++ b/core/attributedef.class.inc.php @@ -813,7 +813,8 @@ class AttributeEnum extends AttributeString } else { - $sLabel = $sValue.' ERROR could not find'; + // #@# todo - record an error silently... + $sLabel = $sValue; } // later, we could imagine a detailed description in the title return "".parent::GetAsHtml($sLabel).""; @@ -843,6 +844,7 @@ class AttributeDate extends AttributeDBField self::$const_TIMEZONE = new DateTimeZone(self::MYDATETIMEZONE); // #@# Init default timezone -> do not get a notice... to be improved !!! + // duplicated in the email test page (the mail function does trigger a notice as well) date_default_timezone_set(self::MYDATETIMEZONE); } From 327696186617b8fab2499ef9eb4e360acc182e17 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Mon, 18 Jan 2010 16:06:02 +0000 Subject: [PATCH 192/227] - MakeHyperlink now returns absolute URLs (including server name & port) - Fixed the handling of the "menu" option in the DisplayBlocks - removed unneeded GetUIPage functions SVN:trunk[269] --- application/cmdbabstract.class.inc.php | 7 ++-- application/displayblock.class.inc.php | 40 ++------------------- application/utils.inc.php | 48 ++++++++++++++++++++++++++ business/ServiceDesk.business.php | 5 --- business/incidentMgmt.business.php | 5 --- 5 files changed, 56 insertions(+), 49 deletions(-) diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index dbe5665c2..83e54a317 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -14,7 +14,7 @@ abstract class cmdbAbstractObject extends CMDBObject public static function GetUIPage() { - return './UI.php'; + return '../pages/UI.php'; } public static function ComputeUIPage($sClass) @@ -40,6 +40,9 @@ abstract class cmdbAbstractObject extends CMDBObject $oAppContext = new ApplicationContext(); $sExtClassNameAtt = MetaModel::GetNameAttributeCode($sObjClass); $sPage = self::ComputeUIPage($sObjClass); + $sAbsoluteUrl = utils::GetAbsoluteUrl(false); // False => Don't get the query string + $sAbsoluteUrl = substr($sAbsoluteUrl, 0, 1+strrpos($sAbsoluteUrl, '/')); // remove the current page, keep just the path, up to the last / + // Use the "name" of the target class as the label of the hyperlink // unless it's not available in the external attributes... if (isset($aAvailableFields[$sExtClassNameAtt])) @@ -71,7 +74,7 @@ abstract class cmdbAbstractObject extends CMDBObject $sLabel = MetaModel::GetName($sObjClass)." #$sObjKey"; } $sHint = MetaModel::GetName($sObjClass)."::$sObjKey"; - return "GetForLink()."\" title=\"$sHint\">$sLabel"; + return "GetForLink()."\" title=\"$sHint\">$sLabel"; } public function GetDisplayValue($sAttCode) diff --git a/application/displayblock.class.inc.php b/application/displayblock.class.inc.php index 10438b921..74d9f9e23 100644 --- a/application/displayblock.class.inc.php +++ b/application/displayblock.class.inc.php @@ -330,7 +330,7 @@ class DisplayBlock { $sHtml .= $oPage->GetP("No object to display."); $sClass = $this->m_oFilter->GetClass(); - $bDisplayMenu = isset($this->m_aParams['menu']) ? $this->m_aParams['menu'] == true : true; + $bDisplayMenu = isset($aExtraParams['menu']) ? $aExtraParams['menu'] == true : true; if ($bDisplayMenu) { if (UserRights::IsActionAllowed($sClass, UR_ACTION_MODIFY) == UR_ALLOWED_YES) @@ -779,7 +779,7 @@ class MenuBlock extends DisplayBlock } else { - $sUrl = self::GetAbsoluteUrl(); + $sUrl = utils::GetAbsoluteUrl(); $aActions[] = array ('label' => 'eMail', 'url' => "mailto:?subject=".$oSet->GetFilter()->__DescribeHTML()."&body=".urlencode("$sUrl?operation=search&filter=$sFilter&$sContext")); $aActions[] = array ('label' => 'CSV Export', 'url' => "../pages/$sUIPage?operation=search&filter=$sFilter&format=csv&$sContext"); //$aActions[] = array ('label' => 'Bookmark...', 'url' => "../pages/ajax.render.php?operation=create&class=$sClass&filter=$sFilter", 'class' => 'jqmTrigger'); @@ -831,7 +831,7 @@ class MenuBlock extends DisplayBlock else { // many objects in the set, possible actions are: new / modify all / delete all - $sUrl = self::GetAbsoluteUrl(); + $sUrl = utils::GetAbsoluteUrl(); $aActions[] = array ('label' => 'eMail', 'url' => "mailto:?subject=".$oSet->GetFilter()->__DescribeHTML()."&body=".urlencode("$sUrl?operation=search&filter=$sFilter&$sContext")); $aActions[] = array ('label' => 'CSV Export', 'url' => "../pages/$sUIPage?operation=search&filter=$sFilter&format=csv&$sContext"); //$aActions[] = array ('label' => 'Bookmark...', 'url' => "../pages/ajax.render.php?operation=create&class=$sClass&filter=$sFilter", 'class' => 'jqmTrigger'); @@ -850,39 +850,5 @@ class MenuBlock extends DisplayBlock $oPage->add_ready_script("$(\"ul.jd_menu\").jdMenu();\n"); return $sHtml; } - - static public function GetAbsoluteUrl() - { - // Build an absolute URL to this page on this server/port - $sServerName = $_SERVER['SERVER_NAME']; - $sProtocol = isset($_SERVER['HTTPS']) ? 'https' : 'http'; - if ($sProtocol == 'http') - { - $sPort = ($_SERVER['SERVER_PORT'] == 80) ? '' : ':'.$_SERVER['SERVER_PORT']; - } - else - { - $sPort = ($_SERVER['SERVER_PORT'] == 443) ? '' : ':'.$_SERVER['SERVER_PORT']; - } - // $_SERVER['REQUEST_URI'] is empty when running on IIS - // Let's use Ivan Tcholakov's fix (found on www.dokeos.com) - if (!empty($_SERVER['REQUEST_URI'])) - { - $sPath = $_SERVER['REQUEST_URI']; - } - else - { - $sPath = $_SERVER['SCRIPT_NAME']; - if (!empty($_SERVER['QUERY_STRING'])) - { - $sPath .= '?'.$_SERVER['QUERY_STRING']; - } - $_SERVER['REQUEST_URI'] = $sPath; - } - $sPath = $_SERVER['REQUEST_URI']; - $sUrl = "$sProtocol://{$sServerName}{$sPort}{$sPath}"; - - return $sUrl; - } } ?> diff --git a/application/utils.inc.php b/application/utils.inc.php index 3330714fb..6eb172280 100644 --- a/application/utils.inc.php +++ b/application/utils.inc.php @@ -147,5 +147,53 @@ class utils } return $iReturn; } + + /** + * Returns an absolute URL to the current page + * @param $bQueryString bool True to also get the query string, false otherwise + * @return string The absolute URL to the current page + */ + static public function GetAbsoluteUrl($bQueryString = true) + { + // Build an absolute URL to this page on this server/port + $sServerName = $_SERVER['SERVER_NAME']; + $sProtocol = isset($_SERVER['HTTPS']) ? 'https' : 'http'; + if ($sProtocol == 'http') + { + $sPort = ($_SERVER['SERVER_PORT'] == 80) ? '' : ':'.$_SERVER['SERVER_PORT']; + } + else + { + $sPort = ($_SERVER['SERVER_PORT'] == 443) ? '' : ':'.$_SERVER['SERVER_PORT']; + } + // $_SERVER['REQUEST_URI'] is empty when running on IIS + // Let's use Ivan Tcholakov's fix (found on www.dokeos.com) + if (!empty($_SERVER['REQUEST_URI'])) + { + $sPath = $_SERVER['REQUEST_URI']; + } + else + { + $sPath = $_SERVER['SCRIPT_NAME']; + if (!empty($_SERVER['QUERY_STRING'])) + { + $sPath .= '?'.$_SERVER['QUERY_STRING']; + } + $_SERVER['REQUEST_URI'] = $sPath; + } + $sPath = $_SERVER['REQUEST_URI']; + if (!$bQueryString) + { + // remove all the parameters from the query string + $iQuestionMarkPos = strpos($sPath, '?'); + if ($iQuestionMarkPos !== false) + { + $sPath = substr($sPath, 0, $iQuestionMarkPos); + } + } + $sUrl = "$sProtocol://{$sServerName}{$sPort}{$sPath}"; + + return $sUrl; + } } ?> diff --git a/business/ServiceDesk.business.php b/business/ServiceDesk.business.php index bdcd80347..ddb7244b6 100644 --- a/business/ServiceDesk.business.php +++ b/business/ServiceDesk.business.php @@ -139,11 +139,6 @@ class bizServiceCall extends cmdbAbstractObject $this->Set('end_date', $oGenerator->GenerateString("2007-|number(07-12)|-|number(01-30)| |number(07-12)|:|number(00-59)|:|number(00-59)")); } - public static function GetUIPage() - { - return './UI.php'; - } - // State machine actions diff --git a/business/incidentMgmt.business.php b/business/incidentMgmt.business.php index 2a44a4672..b4b8c6d0a 100644 --- a/business/incidentMgmt.business.php +++ b/business/incidentMgmt.business.php @@ -134,11 +134,6 @@ class bizIncidentTicket extends cmdbAbstractObject $this->Set('end_date', $oGenerator->GenerateString("2007-|number(07-12)|-|number(01-30)| |number(07-12)|:|number(00-59)|:|number(00-59)")); } - public static function GetUIPage() - { - return './UI.php'; - } - // State machine actions public function IncrementAssignmentCount($sStimulusCode) { From aa6d6029d952e7f48cec483d3075e1ca44dd47ad Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Mon, 18 Jan 2010 16:42:00 +0000 Subject: [PATCH 193/227] Finalized the email error management: - errors in OQL - no recipient - smtp server response (windows only) - test option (status) - test page SVN:trunk[270] --- core/MyHelpers.class.inc.php | 9 -- core/action.class.inc.php | 215 +++++++++++++++++++------- core/dbobject.class.php | 21 ++- core/event.class.inc.php | 20 +-- core/test.class.inc.php | 47 +++++- core/trigger.class.inc.php | 5 +- pages/UniversalSearch.php | 3 +- pages/index.php | 18 ++- pages/testlist.inc.php | 152 ++++++++++++++++-- setup/email.test.php | 184 ++++++++++++++++++++++ setup/setup.js | 9 ++ toolkit.php | 1 + webservices/webservices.class.inc.php | 3 +- 13 files changed, 585 insertions(+), 102 deletions(-) create mode 100644 setup/email.test.php diff --git a/core/MyHelpers.class.inc.php b/core/MyHelpers.class.inc.php index 16e149cd9..fded6913a 100644 --- a/core/MyHelpers.class.inc.php +++ b/core/MyHelpers.class.inc.php @@ -331,15 +331,6 @@ class MyHelpers exit; } - /** - * utf8... converts non ASCII chars into '?' - * Decided after some complex investigations, to have the tools work fine (Oracle+Perl vs mySQL+PHP...) - */ - public static function utf8($strText) - { - return iconv("WINDOWS-1252", "ASCII//TRANSLIT", $strText); - } - /** * xmlentities() * ... same as htmlentities, but designed for xml ! diff --git a/core/action.class.inc.php b/core/action.class.inc.php index dbeaeeea8..6f8dfcb0f 100644 --- a/core/action.class.inc.php +++ b/core/action.class.inc.php @@ -1,5 +1,7 @@ "Name", "description"=>"label", "allowed_values"=>null, "sql"=>"name", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("description", array("label"=>"Description", "description"=>"one line description", "allowed_values"=>null, "sql"=>"description", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("status", array("label"=>"Status", "description"=>"In production or ?", "allowed_values"=>new ValueSetEnum(array('test'=>'Being tested' ,'enabled'=>'In production', 'disabled'=>'Inactive')), "sql"=>"status", "default_value"=>"test", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeLinkedSetIndirect("related_triggers", array("label"=>"Related Triggers", "description"=>"Triggers linked to this action", "linked_class"=>"lnkTriggerAction", "ext_key_to_me"=>"action_id", "ext_key_to_remote"=>"trigger_id", "allowed_values"=>null, "count_min"=>0, "count_max"=>0, "depends_on"=>array()))); //MetaModel::Init_InheritFilters(); @@ -41,14 +44,39 @@ abstract class Action extends cmdbAbstractObject MetaModel::Init_AddFilterFromAttribute("description"); // Display lists - MetaModel::Init_SetZListItems('details', array('name', 'description')); // Attributes to be displayed for the complete details - MetaModel::Init_SetZListItems('list', array('name', 'description')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('details', array('name', 'description', 'status')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('finalclass', 'name', 'description', 'status')); // Attributes to be displayed for a list // Search criteria // MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form // MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form } abstract public function DoExecute($oTrigger, $aContextArgs); + + public function IsActive() + { + switch($this->Get('status')) + { + case 'enabled': + case 'test': + return true; + + default: + return false; + } + } + + public function IsBeingTested() + { + switch($this->Get('status')) + { + case 'test': + return true; + + default: + return false; + } + } } /** @@ -87,8 +115,8 @@ abstract class ActionNotification extends Action MetaModel::Init_InheritFilters(); // Display lists - MetaModel::Init_SetZListItems('details', array('name', 'description')); // Attributes to be displayed for the complete details - MetaModel::Init_SetZListItems('list', array('finalclass', 'name', 'description')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('details', array('name', 'description', 'status')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('finalclass', 'name', 'description', 'status')); // Attributes to be displayed for a list // Search criteria // MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form // MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form @@ -128,6 +156,8 @@ class ActionEmail extends ActionNotification MetaModel::Init_Params($aParams); MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeEmailAddress("test_recipient", array("label"=>"Test recipient", "description"=>"Detination in case status is set to \"Test\"", "allowed_values"=>null, "sql"=>"test_recipient", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeString("from", array("label"=>"From", "description"=>"Will be sent into the email header", "allowed_values"=>null, "sql"=>"from", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("reply_to", array("label"=>"Reply to", "description"=>"Will be sent into the email header", "allowed_values"=>null, "sql"=>"reply_to", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeOQL("to", array("label"=>"To", "description"=>"Destination of the email", "allowed_values"=>null, "sql"=>"to", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); @@ -140,21 +170,36 @@ class ActionEmail extends ActionNotification MetaModel::Init_InheritFilters(); // Display lists - MetaModel::Init_SetZListItems('details', array('name', 'description', 'from', 'reply_to', 'to', 'cc', 'bcc', 'subject', 'body', 'importance')); // Attributes to be displayed for the complete details - MetaModel::Init_SetZListItems('list', array('name', 'to', 'subject')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('details', array('name', 'description', 'status', 'test_recipient', 'from', 'reply_to', 'to', 'cc', 'bcc', 'subject', 'body', 'importance')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('name', 'status', 'to', 'subject')); // Attributes to be displayed for a list // Search criteria // MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form // MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form } - // args: a search object - // returns an array of emails - protected function FindRecipients($sAttCode, $aArgs) + // count the recipients found + protected $m_iRecipients; + + // Errors management : not that simple because we need that function to be + // executed in the background, while making sure that any issue would be reported clearly + protected $m_aMailErrors; //array of strings explaining the issue + + // returns a the list of emails as a string, or a detailed error description + protected function FindRecipients($sRecipAttCode, $aArgs) { - $sOQL = $this->Get($sAttCode); + $sOQL = $this->Get($sRecipAttCode); if (strlen($sOQL) == '') return ''; - $oSearch = DBObjectSearch::FromOQL($sOQL); + try + { + $oSearch = DBObjectSearch::FromOQL($sOQL); + } + catch (OqlException $e) + { + $this->m_aMailErrors[] = "query syntax error for recipient '$sRecipAttCode'"; + return $e->getMessage(); + } + $sClass = $oSearch->GetClass(); // Determine the email attribute (the first one will be our choice) foreach (MetaModel::ListAttributeDefs($sClass) as $sAttCode => $oAttDef) @@ -166,78 +211,138 @@ class ActionEmail extends ActionNotification break; } } + if (!isset($sEmailAttCode)) + { + $this->m_aMailErrors[] = "wrong target for recipient '$sRecipAttCode'"; + return "The objects of the class '$sClass' do not have any email attribute"; + } $oSet = new DBObjectSet($oSearch, array() /* order */, $aArgs); $aRecipients = array(); while ($oObj = $oSet->Fetch()) { $aRecipients[] = $oObj->Get($sEmailAttCode); + $this->m_iRecipients++; } return implode(', ', $aRecipients); } + public function DoExecute($oTrigger, $aContextArgs) { - // Determine recicipients - // - $sTo = $this->FindRecipients('to', $aContextArgs); - $sCC = $this->FindRecipients('cc', $aContextArgs); - $sBCC = $this->FindRecipients('bcc', $aContextArgs); - - $sFrom = $this->Get('from'); - $sReplyTo = $this->Get('reply_to'); - - $sSubject = MetaModel::ApplyParams($this->Get('subject'), $aContextArgs); - $sBody = MetaModel::ApplyParams($this->Get('body'), $aContextArgs); - - // To send HTML mail, the Content-type header must be set - $sHeaders = 'MIME-Version: 1.0' . "\r\n"; - $sHeaders .= 'Content-type: text/html; charset=iso-8859-1' . "\r\n"; - - // Additional headers - if (strlen($sFrom) > 0) + $this->m_iRecipients = 0; + $this->m_aMailErrors = array(); + $bRes = false; // until we do succeed in sending the email + try { - $sHeaders .= "From: $sFrom\r\n"; - // This is required on Windows because otherwise I would get the error - // "sendmail_from" not set in php.ini" even if it is correctly working - // (apparently, once it worked the SMTP server won't claim anymore for it) - ini_set("sendmail_from", $sFrom); + // Determine recicipients + // + $sTo = $this->FindRecipients('to', $aContextArgs); + $sCC = $this->FindRecipients('cc', $aContextArgs); + $sBCC = $this->FindRecipients('bcc', $aContextArgs); + + $sFrom = $this->Get('from'); + $sReplyTo = $this->Get('reply_to'); + + $sSubject = MetaModel::ApplyParams($this->Get('subject'), $aContextArgs); + $sBody = MetaModel::ApplyParams($this->Get('body'), $aContextArgs); + + $oEmail = new Email(); + + if ($this->IsBeingTested()) + { + $oEmail->SetSubject('TEST['.$sSubject.']'); + $sTestBody = $sBody; + $sTestBody .= "
    \n"; + $sTestBody .= "

    Testing email notification ".$this->GetHyperlink()."

    \n"; + $sTestBody .= "

    The email should be sent with the following properties\n"; + $sTestBody .= "

      \n"; + $sTestBody .= "
    • TO: $sTo
    • \n"; + $sTestBody .= "
    • CC: $sCC
    • \n"; + $sTestBody .= "
    • BCC: $sBCC
    • \n"; + $sTestBody .= "
    • From: $sFrom
    • \n"; + $sTestBody .= "
    • Reply-To: $sReplyTo
    • \n"; + $sTestBody .= "
    \n"; + $sTestBody .= "

    \n"; + $sTestBody .= "
    \n"; + $oEmail->SetBody($sTestBody); + $oEmail->SetRecipientTO($this->Get('test_recipient')); + $oEmail->SetRecipientFrom($this->Get('test_recipient')); + } + else + { + $oEmail->SetSubject($sSubject); + $oEmail->SetBody($sBody); + $oEmail->SetRecipientTO($sTo); + $oEmail->SetRecipientCC($sCC); + $oEmail->SetRecipientBCC($sBCC); + $oEmail->SetRecipientFrom($sFrom); + $oEmail->SetRecipientReplyTo($sReplyTo); + } + + if (empty($this->m_aMailErrors)) + { + if ($this->m_iRecipients == 0) + { + $this->m_aMailErrors[] = 'No recipient'; + } + else + { + $this->m_aMailErrors = array_merge($this->m_aMailErrors, $oEmail->Send()); + } + } } - if (strlen($sReplyTo) > 0) + catch (Exception $e) { - $sHeaders .= "Reply-To: $sReplyTo\r\n"; - } - if (strlen($sCC) > 0) - { - $sHeaders .= "Cc: $sCC\r\n"; - } - if (strlen($sBCC) > 0) - { - $sHeaders .= "Bcc: $sBCC\r\n"; + $this->m_aMailErrors[] = $e->getMessage(); } $oLog = new EventNotificationEmail(); - if (mail($sTo, $sSubject, $sBody, $sHeaders)) + if (empty($this->m_aMailErrors)) { - $oLog->Set('message', 'Notification sent'); + if ($this->IsBeingTested()) + { + $oLog->Set('message', 'TEST - Notification sent ('.$this->Get('test_recipient').')'); + } + else + { + $oLog->Set('message', 'Notification sent'); + } } else { - $aLastError = error_get_last(); - $oLog->Set('message', 'Mail could not be sent: '.$aLastError['message']); - //throw new CoreException('mail not sent', array('action'=>$this->GetKey(), 'to'=>$sTo, 'subject'=>$sSubject, 'headers'=>$sHeaders)); + if (is_array($this->m_aMailErrors) && count($this->m_aMailErrors) > 0) + { + $sError = implode(', ', $this->m_aMailErrors); + } + else + { + $sError = 'Unknown reason'; + } + if ($this->IsBeingTested()) + { + $oLog->Set('message', 'TEST - Notification was not sent: '.$sError); + } + else + { + $oLog->Set('message', 'Notification was not sent: '.$sError); + } } $oLog->Set('userinfo', UserRights::GetUser()); $oLog->Set('trigger_id', $oTrigger->GetKey()); $oLog->Set('action_id', $this->GetKey()); $oLog->Set('object_id', $aContextArgs['this->id']); - $oLog->Set('from', $sFrom); - $oLog->Set('to', $sTo); - $oLog->Set('cc', $sCC); - $oLog->Set('bcc', $sBCC); - $oLog->Set('subject', $sSubject); - $oLog->Set('body', $sBody); + + // Note: we have to secure this because those values are calculated + // inside the try statement, and we would like to keep track of as + // many data as we could while some variables may still be undefined + if (isset($sTo)) $oLog->Set('to', $sTo); + if (isset($sCC)) $oLog->Set('cc', $sCC); + if (isset($sBCC)) $oLog->Set('bcc', $sBCC); + if (isset($sFrom)) $oLog->Set('from', $sFrom); + if (isset($sSubject)) $oLog->Set('subject', $sSubject); + if (isset($sBody)) $oLog->Set('body', $sBody); $oLog->DBInsertNoReload(); } } diff --git a/core/dbobject.class.php b/core/dbobject.class.php index e20943a7a..68479ca11 100644 --- a/core/dbobject.class.php +++ b/core/dbobject.class.php @@ -522,14 +522,27 @@ abstract class DBObject // Note: checks the values and consistency public function CheckToInsert() { + $aIssues = array(); foreach(MetaModel::ListAttributeDefs(get_class($this)) as $sAttCode=>$oAttDef) { - if (!$this->CheckValue($sAttCode)) return false; + if (!$this->CheckValue($sAttCode)) + { + $aIssues[$sAttCode] = array( + 'issue' => 'unexpected value' + ); + } } - if (!$this->CheckConsistency()) return false; - return true; + if (count($aIssues) > 0) + { + return array(false, $aIssues); + } + if (!$this->CheckConsistency()) + { + return array(false, $aIssues); + } + return array(true, $aIssues); } - + // check if it is allowed to update the existing object into the database // a displayable error is returned // Note: checks the values and consistency diff --git a/core/event.class.inc.php b/core/event.class.inc.php index 9955dddbd..cbac9e792 100644 --- a/core/event.class.inc.php +++ b/core/event.class.inc.php @@ -32,7 +32,7 @@ class Event extends cmdbAbstractObject ); MetaModel::Init_Params($aParams); //MetaModel::Init_InheritAttributes(); - MetaModel::Init_AddAttribute(new AttributeString("message", array("label"=>"message", "description"=>"short description of the event", "allowed_values"=>null, "sql"=>"message", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeText("message", array("label"=>"message", "description"=>"short description of the event", "allowed_values"=>null, "sql"=>"message", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeDate("date", array("label"=>"date", "description"=>"date and time at which the changes have been recorded", "allowed_values"=>null, "sql"=>"date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("userinfo", array("label"=>"user info", "description"=>"identification of the user that was doing the action that triggered this event", "allowed_values"=>null, "sql"=>"userinfo", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); @@ -110,18 +110,18 @@ class EventNotificationEmail extends EventNotification ); MetaModel::Init_Params($aParams); MetaModel::Init_InheritAttributes(); - MetaModel::Init_AddAttribute(new AttributeText("to", array("label"=>"TO", "description"=>"TO", "allowed_values"=>null, "sql"=>"to", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeText("cc", array("label"=>"CC", "description"=>"CC", "allowed_values"=>null, "sql"=>"cc", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeText("bcc", array("label"=>"BCC", "description"=>"BCC", "allowed_values"=>null, "sql"=>"bcc", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeText("from", array("label"=>"From", "description"=>"Sender of the message", "allowed_values"=>null, "sql"=>"from", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeText("subject", array("label"=>"Subject", "description"=>"Subject", "allowed_values"=>null, "sql"=>"subject", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeText("body", array("label"=>"Body", "description"=>"Body", "allowed_values"=>null, "sql"=>"body", "default_value"=>null, "is_null_allowed"=>false, "depends_on"=>array()))); - + MetaModel::Init_AddAttribute(new AttributeText("to", array("label"=>"TO", "description"=>"TO", "allowed_values"=>null, "sql"=>"to", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeText("cc", array("label"=>"CC", "description"=>"CC", "allowed_values"=>null, "sql"=>"cc", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeText("bcc", array("label"=>"BCC", "description"=>"BCC", "allowed_values"=>null, "sql"=>"bcc", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeText("from", array("label"=>"From", "description"=>"Sender of the message", "allowed_values"=>null, "sql"=>"from", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeText("subject", array("label"=>"Subject", "description"=>"Subject", "allowed_values"=>null, "sql"=>"subject", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeText("body", array("label"=>"Body", "description"=>"Body", "allowed_values"=>null, "sql"=>"body", "default_value"=>null, "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_InheritFilters(); // Display lists - MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'trigger_id', 'action_id', 'object_id', 'from', 'to', 'cc', 'bcc', 'subject', 'body')); // Attributes to be displayed for the complete details - MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'subject')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('details', array('date', 'userinfo', 'message', 'trigger_id', 'action_id', 'object_id', 'to', 'cc', 'bcc', 'from', 'subject', 'body')); // Attributes to be displayed for the complete details + MetaModel::Init_SetZListItems('list', array('date', 'userinfo', 'message', 'subject')); // Attributes to be displayed for a list + // Search criteria // MetaModel::Init_SetZListItems('standard_search', array('name')); // Criteria of the std search form // MetaModel::Init_SetZListItems('advanced_search', array('name')); // Criteria of the advanced search form diff --git a/core/test.class.inc.php b/core/test.class.inc.php index 905e7ad06..87af7e165 100644 --- a/core/test.class.inc.php +++ b/core/test.class.inc.php @@ -130,9 +130,8 @@ abstract class TestHandler $this->ReportWarning($errstr); break; default: - throw new ExceptionFromError("Fatal warning in line $errline of file $errfile: $errstr"); - $this->ReportWarning("Unknown error type: [$errno] $errstr"); - echo "Unknown error type: [$errno] $errstr
    \n"; + $this->ReportWarning("Unknown error type: [$errno] $errstr in $errfile at $errline"); + echo "Unknown error type: [$errno] $errstr in $errfile at $errline
    \n"; break; } return true; // do not call the default handler @@ -418,6 +417,48 @@ abstract class TestBizModel extends TestHandler // something here to create records... but that's another story } + protected $m_oChange; + protected function ObjectToDB($oNew, $bReload = false) + { + list($bRes, $aIssues) = $oNew->CheckToInsert(); + if (!$bRes) + { + throw new CoreException('Could not create object, unexpected values', array('attributes' => $aIssues)); + } + if ($oNew instanceof CMDBObject) + { + if (!isset($this->m_oChange)) + { + new CMDBChange(); + $oMyChange = MetaModel::NewObject("CMDBChange"); + $oMyChange->Set("date", time()); + $oMyChange->Set("userinfo", "Someone doing some tests"); + $iChangeId = $oMyChange->DBInsertNoReload(); + $this->m_oChange = $oMyChange; + } + if ($bReload) + { + $iId = $oNew->DBInsertTracked($this->m_oChange); + } + else + { + $iId = $oNew->DBInsertTrackedNoReload($this->m_oChange); + } + } + else + { + if ($bReload) + { + $iId = $oNew->DBInsert(); + } + else + { + $iId = $oNew->DBInsertNoReload(); + } + } + return $iId; + } + protected function ResetDB() { if (MetaModel::DBExists()) diff --git a/core/trigger.class.inc.php b/core/trigger.class.inc.php index e9de6eaa3..511bb42a3 100644 --- a/core/trigger.class.inc.php +++ b/core/trigger.class.inc.php @@ -56,7 +56,10 @@ class Trigger extends cmdbAbstractObject { $iActionId = $oLink->Get('action_id'); $oAction = MetaModel::GetObject('Action', $iActionId); - $oAction->DoExecute($this, $aContextArgs); + if ($oAction->IsActive()) + { + $oAction->DoExecute($this, $aContextArgs); + } } } } diff --git a/pages/UniversalSearch.php b/pages/UniversalSearch.php index b8b300ce6..4a9c9b077 100644 --- a/pages/UniversalSearch.php +++ b/pages/UniversalSearch.php @@ -145,7 +145,8 @@ EOF; } } - if ($oMenuNode->CheckToInsert()) + list($bRes, $aIssues) = $oMenuNode->CheckToInsert(); + if ($bRes) { $oMenuNode->DBInsert(); $oP->add(""); diff --git a/pages/index.php b/pages/index.php index 5d5b81581..4cbac19c2 100644 --- a/pages/index.php +++ b/pages/index.php @@ -393,7 +393,8 @@ function CreateObject(WebPage $oPage, $sClassName, $aAttributes) $oObj->Set($sAttCode, $aAttributes[$sAttCode]); } } - if ($oObj->CheckToInsert()) + list($bRes, $aIssues) = $oObj->CheckToInsert(); + if ($bRes) { // By rom // $oObj->DBInsert(); @@ -413,6 +414,12 @@ function CreateObject(WebPage $oPage, $sClassName, $aAttributes) else { $oPage->p("Error: object can not be created!\n"); + $oPage->add("
      Issues:"); + foreach($aIssues as $sErrorMsg) + { + $oPage->add("
    • $sErrorMsg
    • "); + } + $oPage->add("
    "); } $oPage->p("Return to main page"); } @@ -442,7 +449,8 @@ function AddLinks($oPage, $sClassName, $sKey, $sLinkClass, $sExtKeyToMe, $sExtKe $oNewLink = MetaModel::NewObject($sLinkClass); $oNewLink->Set($sExtKeyToMe, $sKey); $oNewLink->Set($sExtKeyToPartner, $oPartnerObj->GetKey()); - if ($oNewLink->CheckToInsert()) + list($bRes, $aIssues) = $oNewLink->CheckToInsert(); + if ($bRes) { $oMyChange = MetaModel::NewObject("CMDBChange"); $oMyChange->Set("date", time()); @@ -455,6 +463,12 @@ function AddLinks($oPage, $sClassName, $sKey, $sLinkClass, $sExtKeyToMe, $sExtKe else { $oPage->p("Error: link can not be created!\n"); + $oPage->add("
      Issues:"); + foreach($aIssues as $sErrorMsg) + { + $oPage->add("
    • $sErrorMsg
    • "); + } + $oPage->add("
    "); } } } diff --git a/pages/testlist.inc.php b/pages/testlist.inc.php index 42d137528..800ac969f 100644 --- a/pages/testlist.inc.php +++ b/pages/testlist.inc.php @@ -601,22 +601,6 @@ abstract class MyFarm extends TestBizModel MetaModel::DBCheckIntegrity(); } - protected $m_oChange; - protected function ObjectToDB(CMDBObject $oNew) - { - if (!isset($this->m_oChange)) - { - new CMDBChange(); - $oMyChange = MetaModel::NewObject("CMDBChange"); - $oMyChange->Set("date", time()); - $oMyChange->Set("userinfo", "Administrator"); - $iChangeId = $oMyChange->DBInsertNoReload(); - $this->m_oChange = $oMyChange; - } - $iId = $oNew->DBInsertTrackedNoReload($this->m_oChange); - return $iId; -} - protected function InsertMammal($sSpecies, $sSex, $iSpeed, $iMotherid, $iFatherId, $sName, $iHeight, $sBirth) { $oNew = MetaModel::NewObject('Mammal'); @@ -1467,4 +1451,140 @@ class TestWebServicesDirect extends TestBizModel return true; } } + +class TestTriggerAndEmail extends TestBizModel +{ + static public function GetName() {return 'Test trigger and email';} + static public function GetDescription() {return 'Create a trigger and an email, then activates the trigger';} + + static public function GetConfigFile() {return '../config-itop.php';} + + protected function CreateEmailSpec($oTrigger, $sStatus, $sTo, $sCC, $sTesterEmail) + { + $oAction = MetaModel::NewObject("ActionEmail"); + $oAction->Set("status", $sStatus); + $oAction->Set("name", "New server"); + $oAction->Set("test_recipient", $sTesterEmail); + $oAction->Set("from", $sTesterEmail); + $oAction->Set("reply_to", $sTesterEmail); + $oAction->Set("to", $sTo); + $oAction->Set("cc", $sCC); + $oAction->Set("bcc", ""); + $oAction->Set("subject", "New server: '\$this->name()$'"); + $oAction->Set("body", "

    Dear customer,

    We have created the server \$this->hyperlink()$ in the IT infrastructure database.

    You will be further notified when it is in Production.

    The IT infrastructure management team.

    Here are some accentuated characters for french people: 'ééà'

    "); + $oAction->Set("importance", "low"); + $iActionId = $this->ObjectToDB($oAction, true); + + $oLink = MetaModel::NewObject("lnkTriggerAction"); + $oLink->Set("trigger_id", $oTrigger->GetKey()); + $oLink->Set("action_id", $iActionId); + $oLink->Set("order", "1"); + $iLink = $this->ObjectToDB($oLink, true); + } + + protected function DoExecute() + { + $oMyPerson = MetaModel::NewObject("bizPerson"); + $oMyPerson->Set("name", "testemail1"); + $oMyPerson->Set("org_id", "1"); + $oMyPerson->Set("email", "romain.quetiez@hp.com"); + $iPersonId = $this->ObjectToDB($oMyPerson, true); + + $oMyPerson = MetaModel::NewObject("bizPerson"); + $oMyPerson->Set("name", "testemail2"); + $oMyPerson->Set("org_id", "1"); + $oMyPerson->Set("email", "denis.flaven@hp.com"); + $iPersonId = $this->ObjectToDB($oMyPerson, true); + + $oMyPerson = MetaModel::NewObject("bizPerson"); + $oMyPerson->Set("name", "testemail3"); + $oMyPerson->Set("org_id", "1"); + $oMyPerson->Set("email", "erwan.taloc@hp.com"); + $iPersonId = $this->ObjectToDB($oMyPerson, true); + + $oMyServer = MetaModel::NewObject("bizServer"); + $oMyServer->Set("name", "wfr.terminator.com"); + $oMyServer->Set("severity", "low"); + $oMyServer->Set("status", "InStore"); + $oMyServer->Set("org_id", 2); + $oMyServer->Set("location_id", 2); + $iServerId = $this->ObjectToDB($oMyServer, true); + + $oMyTrigger = MetaModel::NewObject("TriggerOnStateEnter"); + $oMyTrigger->Set("description", "Testor"); + $oMyTrigger->Set("target_class", "bizServer"); + $oMyTrigger->Set("state", "Shipped"); + $iTriggerId = $this->ObjectToDB($oMyTrigger, true); + + // Error in OQL field(s) + // + $this->CreateEmailSpec + ( + $oMyTrigger, + 'test', + "SELECT bizPerson WHERE naime = 'Dali'", + "SELECT bizServer", + 'romain.quetiez@hp.com' + ); + + // Error: no recipient + // + $this->CreateEmailSpec + ( + $oMyTrigger, + 'test', + "", + "", + 'romain.quetiez@hp.com' + ); + + // Test + // + $this->CreateEmailSpec + ( + $oMyTrigger, + 'test', + "SELECT bizPerson WHERE name LIKE 'testemail%'", + "SELECT bizPerson", + 'romain.quetiez@hp.com' + ); + + // Test failing because of a wrong test recipient address + // + $this->CreateEmailSpec + ( + $oMyTrigger, + 'test', + "SELECT bizPerson WHERE name LIKE 'testemail%'", + "", + 'toto@walibi.bg' + ); + + // Normal behavior + // + $this->CreateEmailSpec + ( + $oMyTrigger, + 'enabled', + "SELECT bizPerson WHERE name LIKE 'testemail%'", + "", + 'romain.quetiez@hp.com' + ); + + // Does nothing, because it is disabled + // + $this->CreateEmailSpec + ( + $oMyTrigger, + 'disabled', + "SELECT bizPerson WHERE name = 'testemail%'", + "", + 'romain.quetiez@hp.com' + ); + + $oMyTrigger->DoActivate($oMyServer->ToArgs('this')); + + return true; + } +} ?> diff --git a/setup/email.test.php b/setup/email.test.php new file mode 100644 index 000000000..76df1e542 --- /dev/null +++ b/setup/email.test.php @@ -0,0 +1,184 @@ += 5.2.4 + { + $sPhpIniFile = php_ini_loaded_file(); + } + else + { + $sPhpIniFile = 'php.ini'; + } + + $bIsWindows = (array_key_exists('WINDIR', $_SERVER)); + if ($bIsWindows) + { + $sSmtpServer = ini_get('SMTP'); + if (empty($sSmtpServer)) + { + $oP->error("The SMTP server is not defined. Please add the 'SMTP' directive into $sPhpIniFile"); + $bRet = false; + } + else if (strcasecmp($sSmtpServer, 'localhost') == 0) + { + $oP->warning("Your SMTP server is configured to 'localhost'. You might want to set or change the 'SMTP' directive into $sPhpIniFile"); + } + else + { + $oP->info("Your SMTP server: $sSmtpServer. To change this value, modify the 'SMTP' directive into $sPhpIniFile"); + } + + $iSmtpPort = (int) ini_get('smtp_port'); + if (empty($iSmtpPort)) + { + $oP->info("The SMTP port is not defined. Please add the 'smtp_port' directive into $sPhpIniFile"); + $bRet = false; + } + else if ($iSmtpPort = 25) + { + $oP->info("Your SMTP port is configured to the default value: 25. You might want to set or change the 'smtp_port' directive into $sPhpIniFile"); + } + else + { + $oP->info("Your SMTP port is configured to $iSmtpPort. You might want to set or change the 'smtp_port' directive into $sPhpIniFile"); + } + } + else + { + // Not a windows system + } + if ($bRet) + { + $oP->ok("PHP settings are ok to proceed with a test of the email"); + } + return $bRet; +} + + +/** + * Display the form for the first step of the test wizard + * which consists in a basic check of the configuration and display of a form for testing + */ +function DisplayStep1(SetupWebPage $oP) +{ + $sNextOperation = 'step2'; + $oP->add("

    iTop email test

    \n"); + $oP->add("

    Checking prerequisites

    \n"); + if (CheckEmailSetting($oP)) + { + $sRedStar = '*'; + $oP->add("

    Try to send an email

    \n"); + $oP->add("\n"); + // Form goes here + $oP->add("
    Test configuration\n"); + $aForm = array(); + $aForm[] = array( + 'label' => "To$sRedStar:", + 'input' => "", + 'help' => ' pure email address (john.foo@worldcompany.com)', + ); + $aForm[] = array( + 'label' => "From:", + 'input' => "", + 'help' => ' defaults to \'To\'', + ); + $oP->form($aForm); + $oP->add("
    \n"); + $oP->add("\n"); + $oP->add("\n"); + $oP->add("\n"); + } +} + +/** + * Display the form for the second step of the configuration wizard + * which consists in sending an email, which maybe a problem under Windows + */ +function DisplayStep2(SetupWebPage $oP, $sFrom, $sTo) +{ + //$sNextOperation = 'step3'; + $oP->add("

    iTop configuration wizard

    \n"); + $oP->add("

    Step 2: send an email

    \n"); + $oP->add("

    Sending an email to '$sTo'... (From: '$sFrom')

    \n"); + $oP->add("
    \n"); + + $oEmail = new Email(); + $oEmail->SetRecipientTO($sTo); + $oEmail->SetRecipientFrom($sFrom); + $oEmail->SetSubject("Test iTop"); + $oEmail->SetBody("

    Hello,

    The email function is now working fine.

    You may now be able to use the notification function.

    iTop

    "); + $aIssues = $oEmail->send(); + if (count($aIssues) > 0) + { + foreach ($aIssues as $sError) + { + $oP->error($sError); + } + $oP->add("\n"); + } + else + { + $oP->ok("The email has been sent, you may now check that the email will arrive..."); + } +} + +/** + * Main program + */ + +// #@# Init default timezone -> do not get a notice... to be improved !!! +// duplicated from 'attributedef.class.inc.php', needed here because mail() does +// generate a notice +date_default_timezone_set('Europe/Paris'); + + +try +{ + switch($sOperation) + { + case 'step1': + DisplayStep1($oP); + break; + + case 'step2': + $oP->no_cache(); + $sTo = Utils::ReadParam('to'); + $sFrom = Utils::ReadParam('from'); + if (strlen($sFrom) == 0) + { + $sFrom = $sTo; + } + DisplayStep2($oP, $sFrom, $sTo); + break; + + default: + $oP->error("Error: unsupported operation '$sOperation'"); + + } +} +catch(Exception $e) +{ + $oP->error("Error: '".$e->getMessage()."'"); +} +catch(CoreException $e) +{ + $oP->error("Error: '".$e->getHtmlDesc()."'"); +} +$oP->output(); +?> diff --git a/setup/setup.js b/setup/setup.js index 8c9e46008..de99764ed 100644 --- a/setup/setup.js +++ b/setup/setup.js @@ -77,6 +77,15 @@ function DoSubmit(sMsg, iStep) case 4: bResult = DoLoadDataAsynchronous(); + break; + + // Email test page + case 10: + if ($('#to').val() == '') + { + alert('Please specify a destination address'); + bResult = false; + } } if (bResult) { diff --git a/toolkit.php b/toolkit.php index 707bdb520..d816778e3 100644 --- a/toolkit.php +++ b/toolkit.php @@ -15,6 +15,7 @@ echo "

    Itop consultant

    \n"; echo "Check model, Create DB, Update DB (new class, new attribute)
    \n"; echo "Backup and restore (shortcut)
    \n"; echo "Objects schema (shortcut)
    \n"; +echo "Setup the email
    \n"; echo "

    Web services

    \n"; echo "Available functions
    \n"; echo "WSDL (dynamically generated)
    \n"; diff --git a/webservices/webservices.class.inc.php b/webservices/webservices.class.inc.php index 5595f7113..b525a6948 100644 --- a/webservices/webservices.class.inc.php +++ b/webservices/webservices.class.inc.php @@ -455,7 +455,8 @@ class WebServices { if ($oRes->IsOk()) { - if ($oTargetObj->CheckToInsert()) + list($bRes, $aIssues) = $oTargetObj->CheckToInsert(); + if ($bRes) { $iId = $oTargetObj->DBInsertTrackedNoReload($oChange); $oRes->LogInfo("Created object ".get_class($$oTargetObj)."::$iId"); From 4267218e49730e1182c89c4c8e84c2ea871b74c6 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Mon, 18 Jan 2010 16:55:00 +0000 Subject: [PATCH 194/227] Email test: added a check for linux (sendmail_path) SVN:trunk[271] --- setup/email.test.php | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/setup/email.test.php b/setup/email.test.php index 76df1e542..bcf7935d8 100644 --- a/setup/email.test.php +++ b/setup/email.test.php @@ -26,7 +26,7 @@ function CheckEmailSetting($oP) $sPhpIniFile = 'php.ini'; } - $bIsWindows = (array_key_exists('WINDIR', $_SERVER)); + $bIsWindows = (array_key_exists('WINDIR', $_SERVER)); if ($bIsWindows) { $sSmtpServer = ini_get('SMTP'); @@ -62,6 +62,16 @@ function CheckEmailSetting($oP) else { // Not a windows system + $sSendMail = ini_get('sendmail_path'); + if (empty($sSendMail)) + { + $oP->error("The command to send mail is not defined. Please add the 'sendmail_path' directive into $sPhpIniFile. A recommended setting is sendmail_path=sendmail -t -i"); + $bRet = false; + } + else + { + $oP->info("The command to send mail: $sSendMail. To change this value, modify the 'sendmail_path' directive into $sPhpIniFile"); + } } if ($bRet) { From f12b6d5c8ab99762652d025d99867fbc25495bae Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Mon, 18 Jan 2010 17:17:49 +0000 Subject: [PATCH 195/227] Sample data for triggers and actions SVN:trunk[272] --- setup/data/structure/23.triggers.xml | 10 +++ setup/data/structure/24.actions.xml | 80 ++++++++++++++++----- setup/data/structure/25.trigger-actions.xml | 23 ++++++ 3 files changed, 94 insertions(+), 19 deletions(-) create mode 100644 setup/data/structure/25.trigger-actions.xml diff --git a/setup/data/structure/23.triggers.xml b/setup/data/structure/23.triggers.xml index 06b285828..51ac5676d 100644 --- a/setup/data/structure/23.triggers.xml +++ b/setup/data/structure/23.triggers.xml @@ -4,4 +4,14 @@ Incident Creation bizIncidentTicket + +Incident ticket assigned to agent +bizIncidentTicket +Assigned + + +Incident ticket resolved +bizIncidentTicket +Resolved + \ No newline at end of file diff --git a/setup/data/structure/24.actions.xml b/setup/data/structure/24.actions.xml index 53563f544..3bd64856b 100644 --- a/setup/data/structure/24.actions.xml +++ b/setup/data/structure/24.actions.xml @@ -1,21 +1,63 @@ - - - -Simple eMail -Default eMail action, tailor it to fit your needs - - -SELECT bizContact AS C WHERE C.id = :this->caller_id - - -New iTop ticket [$this->severity$]: $this->name$, $this->title$ -<h1>A new iTop incident has been created: $this->name$</h1> + + + +Incident Notification to a Workgroup +This action informs a team that a ticket has been assigned their workgroup + + +SELECT bizTeam AS t JOIN bizWorkgroup AS w ON w.team_id = t.id WHERE w.id=:this->workgroup_id + + +The ticket $this->name()$, severity $this->severity$ has been assigned to the workgroup $this->workgroup_name$ +<html> +<body> +<p>The incident ticket $this->name()$ has been assigned to the workgroup $this->workgroup_name$.</p> <p>Description: $this->title$</p> -<p>Initial situation: <pre>$this->initial_situation$</pre></p> -<p>Ticket Type: $this->type$</p> -<p>Ticket assigned to: $this->workgroup_name$</p> +<p>Initial Situation: $this->initial_situation$</p> <hr/> -<p>More information: $this->hyperlink()$</p> -normal - - +<p>for more information on this ticket, click here: $this->hyperlink()$</p> +</body> +</html> +normal + + +Incident notification to Agent +This action informs an agent that a ticket has been assigned to her/him + + +SELECT bizPerson WHERE id=:this->agent_id + + +The ticket $this->name()$, severity $this->severity$ has been assigned to you +<html> +<body> +<p>The incident ticket $this->name()$ has been assigned to you.</p> +<p>Description: $this->title$</p> +<p>Initial Situation: $this->initial_situation$</p> +<hr/> +<p>for more information on this ticket, click here: $this->hyperlink()$</p> +</body> +</html> +normal + + +Incident Notification to caller +This action is used to inform the caller + + +SELECT bizPerson WHERE id=:this->caller_id + + +Ticket $this->name()$, severity $this->severity$ - $this->ticket_status$ +<html> +<body> +<p>The incident ticket $this->name()$ has changed to status $this->ticket_status$</p> +<p>Current situation: $this->current_situation$</p> +<p>Last update: $this->last_update$</p> +<hr/> +<p>for more information on this ticket, click here: $this->hyperlink()$</p> +</body> +</html> +normal + + \ No newline at end of file diff --git a/setup/data/structure/25.trigger-actions.xml b/setup/data/structure/25.trigger-actions.xml new file mode 100644 index 000000000..bd58a7d67 --- /dev/null +++ b/setup/data/structure/25.trigger-actions.xml @@ -0,0 +1,23 @@ + + + +2 +5 +1 + + +3 +5 +2 + + +3 +6 +1 + + +4 +2 +1 + + \ No newline at end of file From ab1318291053bbe0c240f301b12f76bc2c03c235 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Mon, 18 Jan 2010 17:19:57 +0000 Subject: [PATCH 196/227] Export script for triggers, actions and their link SVN:trunk[273] --- setup/data/structure/export_triggers_actions.cmd | 12 ++++++++++++ 1 file changed, 12 insertions(+) create mode 100644 setup/data/structure/export_triggers_actions.cmd diff --git a/setup/data/structure/export_triggers_actions.cmd b/setup/data/structure/export_triggers_actions.cmd new file mode 100644 index 000000000..44958ad06 --- /dev/null +++ b/setup/data/structure/export_triggers_actions.cmd @@ -0,0 +1,12 @@ +SET WEBROOT=http://localhost:81/ +SET EXPORT=%WEBROOT%/webservices/export.php + +SET USER=admin +SET PWD=test + +REM The order (numbering) of the files is important since +REM it dictates the order to import them back +wget --output-document=23.triggers.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT Trigger&format=xml" +wget --output-document=24.actions.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT ActionEmail&format=xml" +wget --output-document=25.trigger-actions.xml --post-data="auth_user=%USER%&auth_pwd=%PWD%&operation=login" "%EXPORT%?expression=SELECT lnkTriggerAction&format=xml" +pause From ac931fb7ec5150695c3af86c145376d9a78989ab Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Mon, 18 Jan 2010 17:33:14 +0000 Subject: [PATCH 197/227] - Export in XML each object according to its actual class SVN:trunk[274] --- application/cmdbabstract.class.inc.php | 1 + 1 file changed, 1 insertion(+) diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index 83e54a317..3f3dbc2d8 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -482,6 +482,7 @@ abstract class cmdbAbstractObject extends CMDBObject $oSet->Seek(0); while ($oObj = $oSet->Fetch()) { + $sClassName = get_class($oObj); $oPage->add("<$sClassName id=\"".$oObj->GetKey()."\">\n"); foreach(MetaModel::ListAttributeDefs($sClassName) as $sAttCode=>$oAttDef) { From cd0271c8c1c6433d4dcbe83fcf97e617f3a6f66a Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Mon, 18 Jan 2010 17:35:05 +0000 Subject: [PATCH 198/227] Fixed bug revealed by a recent fix: the various sets of values allowed for the status fields were not consistent regarding the object hierarchy SVN:trunk[275] --- business/incidentMgmt.business.php | 1 + business/itop.business.class.inc.php | 9 ++++++--- 2 files changed, 7 insertions(+), 3 deletions(-) diff --git a/business/incidentMgmt.business.php b/business/incidentMgmt.business.php index b4b8c6d0a..b1f4df026 100644 --- a/business/incidentMgmt.business.php +++ b/business/incidentMgmt.business.php @@ -345,6 +345,7 @@ class bizWorkgroup extends logRealObject ); MetaModel::Init_Params($aParams); MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeEnum("status", array("label"=>"Status", "description"=>"Lifecycle status", "allowed_values"=>new ValueSetEnum('production,implementation,obsolete'), "sql"=>"status", "default_value"=>"implementation", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("org_name", array("label"=>"Organization", "description"=>"Company / Department owning this object", "allowed_values"=>null, "extkey_attcode"=> 'org_id', "target_attcode"=>"name"))); MetaModel::Init_AddAttribute(new AttributeExternalKey("team_id", array("targetclass"=>"bizTeam", "label"=>"Team", "description"=>"Team owning the workgroup", "allowed_values"=>null, "sql"=>"team_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("team_name", array("label"=>"Team Name", "description"=>"name of the team", "allowed_values"=>null, "extkey_attcode"=> 'team_id', "target_attcode"=>"name"))); diff --git a/business/itop.business.class.inc.php b/business/itop.business.class.inc.php index 47a6cf566..20fc38047 100644 --- a/business/itop.business.class.inc.php +++ b/business/itop.business.class.inc.php @@ -143,7 +143,7 @@ class logRealObject extends cmdbAbstractObject ); MetaModel::Init_Params($aParams); MetaModel::Init_AddAttribute(new AttributeString("name", array("label"=>"Name", "description"=>"Common name", "allowed_values"=>null, "sql"=>"name", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeEnum("status", array("label"=>"Status", "description"=>"Lifecycle status", "allowed_values"=>new ValueSetEnum(STANDARD_STATUSES), "sql"=>"status", "default_value"=>"implementation", "is_null_allowed"=>false, "depends_on"=>array()))); + MetaModel::Init_AddAttribute(new AttributeEnum("status", array("label"=>"Status", "description"=>"Lifecycle status", "allowed_values"=>new ValueSetEnum('production,implementation,obsolete,off,left company,available'), "sql"=>"status", "default_value"=>"implementation", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalKey("org_id", array("targetclass"=>"bizOrganization", "label"=>"Organization", "description"=>"ID of the object owner organization", "allowed_values"=>null, "sql"=>"org_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("org_name", array("label"=>"Organization", "description"=>"Company / Department owning this object", "allowed_values"=>null, "extkey_attcode"=> 'org_id', "target_attcode"=>"name"))); @@ -197,6 +197,7 @@ class bizContact extends logRealObject ); MetaModel::Init_Params($aParams); MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeEnum("status", array("label"=>"Status", "description"=>"Lifecycle status", "allowed_values"=>new ValueSetEnum('off,left company,available'), "sql"=>"status", "default_value"=>"available", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("org_name", array("label"=>"Organization", "description"=>"Company / Department of the contact", "allowed_values"=>null, "extkey_attcode"=> 'org_id', "target_attcode"=>"name"))); MetaModel::Init_AddAttribute(new AttributeEmailAddress("email", array("label"=>"eMail", "description"=>"Email address", "allowed_values"=>null, "sql"=>"email", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("phone", array("label"=>"Phone", "description"=>"Telephone", "allowed_values"=>null, "sql"=>"telephone", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); @@ -237,7 +238,6 @@ class bizPerson extends bizContact { public static function Init() { - $oAllowedEmployeeStatuses = new ValueSetEnum('off,left company,available'); $aParams = array ( "category" => "bizmodel,searchable", @@ -258,7 +258,6 @@ class bizPerson extends bizContact MetaModel::Init_InheritAttributes(); MetaModel::Init_AddAttribute(new AttributeString("first_name", array("label"=>"First Name", "description"=>"First name", "allowed_values"=>null, "sql"=>"first_name", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("employee_number", array("label"=>"Employee Number", "description"=>"employee number", "allowed_values"=>null, "sql"=>"employee_number", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); - MetaModel::Init_AddAttribute(new AttributeEnum("status", array("label"=>"Status", "description"=>"Lifecycle status", "allowed_values"=>$oAllowedEmployeeStatuses, "sql"=>"status", "default_value"=>"available", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_InheritFilters(); MetaModel::Init_AddFilterFromAttribute("first_name"); @@ -393,6 +392,7 @@ class bizDocument extends logRealObject ); MetaModel::Init_Params($aParams); MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeEnum("status", array("label"=>"Status", "description"=>"Lifecycle status", "allowed_values"=>new ValueSetEnum('production,implementation,obsolete'), "sql"=>"status", "default_value"=>"implementation", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("org_name", array("label"=>"Organization", "description"=>"Company / Department owning the document", "allowed_values"=>null, "extkey_attcode"=> 'org_id', "target_attcode"=>"name"))); MetaModel::Init_AddAttribute(new AttributeEnum("type", array("label"=>"type", "description"=>"usage of the document", "allowed_values"=>new ValueSetEnum("documentation,contract,working instructions,network map,white paper,presentation,training"), "sql"=>"type", "default_value"=>"documentation", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeText("description", array("label"=>"Description", "description"=>"Service Description", "allowed_values"=>null, "sql"=>"description", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); @@ -534,6 +534,7 @@ class logInfra extends logRealObject ); MetaModel::Init_Params($aParams); MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeEnum("status", array("label"=>"Status", "description"=>"Lifecycle status", "allowed_values"=>new ValueSetEnum('production,implementation,obsolete'), "sql"=>"status", "default_value"=>"implementation", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeEnum("severity", array("label"=>"Business Criticity", "description"=>"Severity for this infrastructure", "allowed_values"=>new ValueSetEnum("high,medium,low"), "sql"=>"severity", "default_value"=>"low", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_InheritFilters(); @@ -1637,6 +1638,7 @@ class lnkClientServer extends logRealObject ); MetaModel::Init_Params($aParams); + MetaModel::Init_AddAttribute(new AttributeEnum("status", array("label"=>"Status", "description"=>"Lifecycle status", "allowed_values"=>new ValueSetEnum('production,implementation,obsolete'), "sql"=>"status", "default_value"=>"production", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalKey("client_id", array("targetclass"=>"bizApplication", "jointype"=> '', "label"=>"Client", "description"=>"The client part of the link", "allowed_values"=>null, "sql"=>"client_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalField("client_name", array("label"=>"Client", "description"=>"Name of the client", "allowed_values"=>null, "extkey_attcode"=> 'client_id', "target_attcode"=>"name"))); MetaModel::Init_AddAttribute(new AttributeExternalKey("server_id", array("targetclass"=>"bizApplication", "jointype"=> '', "label"=>"Server", "description"=>"the server part of the link", "allowed_values"=>null, "sql"=>"server_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_AUTO, "depends_on"=>array()))); @@ -1683,6 +1685,7 @@ class bizPatch extends logRealObject ); MetaModel::Init_Params($aParams); MetaModel::Init_InheritAttributes(); + MetaModel::Init_AddAttribute(new AttributeEnum("status", array("label"=>"Status", "description"=>"Lifecycle status", "allowed_values"=>new ValueSetEnum('production,obsolete'), "sql"=>"status", "default_value"=>"production", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeExternalKey("device_id", array("targetclass"=>"bizDevice", "jointype"=> '', "label"=>"Device", "description"=>"The Device where patch is installed", "allowed_values"=>new ValueSetObjects('SELECT bizDevice AS p WHERE p.org_id = :this->org_id'), "sql"=>"device_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL, "depends_on"=>array("org_id")))); MetaModel::Init_AddAttribute(new AttributeExternalField("device_name", array("label"=>"Device Name", "description"=>"Name of the impacted device", "allowed_values"=>null, "extkey_attcode"=> 'device_id', "target_attcode"=>"name"))); MetaModel::Init_AddAttribute(new AttributeDate("install_date", array("label"=>"Installation Date", "description"=>"Date when application was installed", "allowed_values"=>null, "sql"=>"install_date", "default_value"=>"", "is_null_allowed"=>false, "depends_on"=>array()))); From 5f17dcf2dbf9a62db912e748c3679ef943517e2c Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Mon, 18 Jan 2010 17:43:37 +0000 Subject: [PATCH 199/227] Forgot to commit a new include SVN:trunk[276] --- core/email.class.inc.php | 121 +++++++++++++++++++++++++++++++++++++++ 1 file changed, 121 insertions(+) create mode 100644 core/email.class.inc.php diff --git a/core/email.class.inc.php b/core/email.class.inc.php new file mode 100644 index 000000000..3a8a23e88 --- /dev/null +++ b/core/email.class.inc.php @@ -0,0 +1,121 @@ + + * @author Denis Flaven + * @license http://www.opensource.org/licenses/lgpl-license.php LGPL + * @link www.itop.com + * @since 1.0 + * @version 1.1.1.1 $ + */ +class EMail +{ + protected $m_sBody; + protected $m_sSubject; + protected $m_sTo; + protected $m_aHeaders; // array of key=>value + + public function __construct() + { + $this->m_sTo = ''; + $this->m_sSubject = ''; + $this->m_sBody = ''; + $this->m_aHeaders = array(); + } + + + // Errors management : not that simple because we need that function to be + // executed in the background, while making sure that any issue would be reported clearly + protected $m_aMailErrors; //array of strings explaining the issues + + public function mail_error_handler($errno, $errstr, $errfile, $errline) + { + $sCleanMessage= str_replace("mail() [function.mail]: ", "", $errstr); + $this->m_aMailErrors[] = $sCleanMessage; + } + + + // returns a list of issues if any + public function Send() + { + $sHeaders = 'MIME-Version: 1.0' . "\r\n"; + $sHeaders .= 'Content-type: text/html; charset=utf-8' . "\r\n"; + foreach ($this->m_aHeaders as $sKey => $sValue) + { + $sHeaders .= "$sKey: $sValue\r\n"; + } + + // Under Windows (not yet proven for Linux/PHP) mail may issue a warning + // that I could not mask (tried error_reporting(), etc.) + $this->m_aMailErrors = array(); + set_error_handler(array($this, 'mail_error_handler')); + $bRes = mail + ( + $this->m_sTo, + $this->m_sSubject, + $this->m_sBody, + $sHeaders + ); + restore_error_handler(); + if (!$bRes && empty($this->m_aMailErrors)) + { + $this->m_aMailErrors[] = 'Unknown reason'; + } + return $this->m_aMailErrors; + } + + protected function AddToHeader($sKey, $sValue) + { + if (strlen($sValue) > 0) + { + $this->m_aHeaders[$sKey] = $sValue; + } + } + + public function SetBody($sBody) + { + $this->m_sBody = $sBody; + } + + public function SetSubject($aSubject) + { + $this->m_sSubject = $aSubject; + } + + public function SetRecipientTO($sAddress) + { + $this->m_sTo = $sAddress; + } + + public function SetRecipientCC($sAddress) + { + $this->AddToHeader('Cc', $sAddress); + } + + public function SetRecipientBCC($sAddress) + { + $this->AddToHeader('Bcc', $sAddress); + } + + public function SetRecipientFrom($sAddress) + { + $this->AddToHeader('From', $sAddress); + + // This is required on Windows because otherwise I would get the error + // "sendmail_from" not set in php.ini" even if it is correctly working + // (apparently, once it worked the SMTP server won't claim anymore for it) + ini_set("sendmail_from", $sAddress); + } + + public function SetRecipientReplyTo($sAddress) + { + $this->AddToHeader('Reply-To', $sAddress); + } + +} + +?> From defe8fff82ac11b1a9a16c8e38b97696ba12e805 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Mon, 18 Jan 2010 17:54:53 +0000 Subject: [PATCH 200/227] Removed status declaration for the class bizServer SVN:trunk[277] --- business/itop.business.class.inc.php | 4 ++-- pages/testlist.inc.php | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/business/itop.business.class.inc.php b/business/itop.business.class.inc.php index 20fc38047..d4d589dc1 100644 --- a/business/itop.business.class.inc.php +++ b/business/itop.business.class.inc.php @@ -1168,7 +1168,7 @@ class bizServer extends bizDevice "key_type" => "", "key_label" => "id", "name_attcode" => "name", - "state_attcode" => "status", + //"state_attcode" => "status", "state_attcode" => "", "reconc_keys" => array("org_id", "name"), // inherited attributes "db_table" => "servers", @@ -1178,7 +1178,7 @@ class bizServer extends bizDevice ); MetaModel::Init_Params($aParams); MetaModel::Init_InheritAttributes(); - MetaModel::Init_AddAttribute(new AttributeEnum("status", array("label"=>"Status", "description"=>"Status of the server", "allowed_values"=>new ValueSetEnum("InStore,Shipped,Plugged,ProductionCandidate,InProduction,BeingDeconfigured,Obsolete"), "sql"=>"status", "default_value"=>"InStore", "is_null_allowed"=>false, "depends_on"=>array()))); +// MetaModel::Init_AddAttribute(new AttributeEnum("status", array("label"=>"Status", "description"=>"Status of the server", "allowed_values"=>new ValueSetEnum("InStore,Shipped,Plugged,ProductionCandidate,InProduction,BeingDeconfigured,Obsolete"), "sql"=>"status", "default_value"=>"InStore", "is_null_allowed"=>false, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("memory_size", array("label"=>"Memory Size", "description"=>"Size of the memory", "allowed_values"=>null, "sql"=>"memory_size", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("cpu", array("label"=>"CPU type", "description"=>"CPU type", "allowed_values"=>null, "sql"=>"cpu_type", "default_value"=>"", "is_null_allowed"=>true, "depends_on"=>array()))); MetaModel::Init_AddAttribute(new AttributeString("number_of_cpus", array("label"=>"Number of CPUs", "description"=>"Number of CPUs", "allowed_values"=>null, "sql"=>"number_of_cpus", "default_value"=>"1", "is_null_allowed"=>true, "depends_on"=>array()))); diff --git a/pages/testlist.inc.php b/pages/testlist.inc.php index 800ac969f..cd0a15de2 100644 --- a/pages/testlist.inc.php +++ b/pages/testlist.inc.php @@ -1505,7 +1505,7 @@ class TestTriggerAndEmail extends TestBizModel $oMyServer = MetaModel::NewObject("bizServer"); $oMyServer->Set("name", "wfr.terminator.com"); $oMyServer->Set("severity", "low"); - $oMyServer->Set("status", "InStore"); + $oMyServer->Set("status", "production"); $oMyServer->Set("org_id", 2); $oMyServer->Set("location_id", 2); $iServerId = $this->ObjectToDB($oMyServer, true); From a9838035c20a4b7213ca566bc94d78be0bffc306 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Mon, 18 Jan 2010 18:04:32 +0000 Subject: [PATCH 201/227] Updated templates for a better consistency SVN:trunk[278] --- business/templates/network.device.html | 4 ++-- business/templates/pc.html | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/business/templates/network.device.html b/business/templates/network.device.html index b18fb73af..182cf984d 100644 --- a/business/templates/network.device.html +++ b/business/templates/network.device.html @@ -7,13 +7,13 @@ SELECT bizNetworkDevice WHERE id = $pkey$ - SELECT bizInterface WHERE device_id = $pkey$ + SELECT bizInterface WHERE device_id = $pkey$ SELECT lnkContactInfra WHERE infra_id = $pkey$ - bizIncidentTicket:PKEY IS ticket_id IN (lnkInfraTicket: infra_id = $pkey$) + SELECT lnkInfraTicket WHERE infra_id = $pkey$ bizChangeTicket: PKEY IS ticket_id IN (lnkInfraChangeTicket: infra_id = $pkey$) diff --git a/business/templates/pc.html b/business/templates/pc.html index 3b0112983..077b9cba1 100644 --- a/business/templates/pc.html +++ b/business/templates/pc.html @@ -19,7 +19,7 @@ SELECT lnkContactRealObject WHERE object_id = $pkey$ - bizIncidentTicket: PKEY IS ticket_id IN (lnkInfraTicket: infra_id = $pkey$) + SELECT lnkInfraTicket WHERE infra_id = $pkey$ SELECT lnkInfraGrouping WHERE infra_id = $pkey$ From 8f12cd39c50d9da5ca54caae574797f25064ea7a Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Mon, 18 Jan 2010 18:06:40 +0000 Subject: [PATCH 202/227] - New link to the email test page SVN:trunk[279] --- setup/data/structure/1.menus.xml | 1 + 1 file changed, 1 insertion(+) diff --git a/setup/data/structure/1.menus.xml b/setup/data/structure/1.menus.xml index 6b5317bc7..67f55994a 100644 --- a/setup/data/structure/1.menus.xml +++ b/setup/data/structure/1.menus.xml @@ -968,6 +968,7 @@ display: list-item; <i><b>Actions</b></i> define the actions to be performed when the triggers execute. For now there is only one kind of action consisting in sending an email message. Such actions also define the template to be used for sending the email as well as the other parameters of the message like the recipients, importance, etc. </p> +<p>A special page: <a href="../setup/email.test.php" target="_blank">email.test.php</a> is available for testing and troubleshooting your PHP mail configuration.</p> <p>To be executed, actions must be associated to triggers. When associated with a trigger, each action is given an 'order' number, specifying in which order the actions are to be executed.</p> </div> From d2f3f51daa3cd0e20a31a663b2427acdd4183c0e Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Mon, 18 Jan 2010 18:40:45 +0000 Subject: [PATCH 203/227] New actions consistent wit the updated DB model SVN:trunk[280] --- setup/data/structure/24.actions.xml | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/setup/data/structure/24.actions.xml b/setup/data/structure/24.actions.xml index 3bd64856b..465e9b2d8 100644 --- a/setup/data/structure/24.actions.xml +++ b/setup/data/structure/24.actions.xml @@ -3,6 +3,8 @@ Incident Notification to a Workgroup This action informs a team that a ticket has been assigned their workgroup +enabled + SELECT bizTeam AS t JOIN bizWorkgroup AS w ON w.team_id = t.id WHERE w.id=:this->workgroup_id @@ -23,6 +25,8 @@ Incident notification to Agent This action informs an agent that a ticket has been assigned to her/him +enabled + SELECT bizPerson WHERE id=:this->agent_id @@ -43,6 +47,8 @@ Incident Notification to caller This action is used to inform the caller +enabled + SELECT bizPerson WHERE id=:this->caller_id From 7fe876b98317ae83d7d9bb920ddab50724daf0c4 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Tue, 19 Jan 2010 07:50:52 +0000 Subject: [PATCH 204/227] - Fixed status enumerations SVN:trunk[281] --- setup/data/04.teams.xml | 4 +- setup/data/06.servers.xml | 408 +++++++++++++++++++------------------- 2 files changed, 206 insertions(+), 206 deletions(-) diff --git a/setup/data/04.teams.xml b/setup/data/04.teams.xml index f9f64f1c1..c4b005e43 100644 --- a/setup/data/04.teams.xml +++ b/setup/data/04.teams.xml @@ -2,7 +2,7 @@ Application support -implementation +available 2 application@mecanorama.com 33456456788 @@ -10,7 +10,7 @@ ITOP admin team -production +available 2 combodo@gmail.com 33123453612 diff --git a/setup/data/06.servers.xml b/setup/data/06.servers.xml index 1e082ac82..3343dc83a 100644 --- a/setup/data/06.servers.xml +++ b/setup/data/06.servers.xml @@ -2,7 +2,7 @@ domino.combodo.com -InProduction +production 2 high 3 @@ -22,7 +22,7 @@ server02 -ProductionCandidate +production 2 high 3 @@ -42,7 +42,7 @@ server03 -ProductionCandidate +production 2 high 3 @@ -62,7 +62,7 @@ server04 -ProductionCandidate +production 2 high 3 @@ -82,7 +82,7 @@ server05 -ProductionCandidate +production 2 high 3 @@ -102,7 +102,7 @@ server06 -ProductionCandidate +production 2 medium 3 @@ -122,7 +122,7 @@ server07 -ProductionCandidate +production 2 high 3 @@ -142,7 +142,7 @@ server08 -ProductionCandidate +production 2 high 3 @@ -162,7 +162,7 @@ server09 -ProductionCandidate +production 2 high 3 @@ -182,7 +182,7 @@ server10 -ProductionCandidate +production 2 high 3 @@ -202,7 +202,7 @@ server100 -ProductionCandidate +production 2 high 2 @@ -222,7 +222,7 @@ server101 -ProductionCandidate +production 2 high 2 @@ -242,7 +242,7 @@ server102 -ProductionCandidate +production 2 high 2 @@ -262,7 +262,7 @@ server103 -ProductionCandidate +production 2 high 2 @@ -282,7 +282,7 @@ server104 -ProductionCandidate +production 2 high 2 @@ -302,7 +302,7 @@ server105 -ProductionCandidate +production 2 high 2 @@ -322,7 +322,7 @@ server106 -ProductionCandidate +production 2 high 2 @@ -342,7 +342,7 @@ server107 -ProductionCandidate +production 2 high 2 @@ -362,7 +362,7 @@ server108 -ProductionCandidate +production 2 high 2 @@ -382,7 +382,7 @@ server109 -ProductionCandidate +production 2 high 2 @@ -402,7 +402,7 @@ server11 -ProductionCandidate +production 2 high 3 @@ -422,7 +422,7 @@ server110 -ProductionCandidate +production 2 high 2 @@ -442,7 +442,7 @@ server111 -ProductionCandidate +production 2 high 2 @@ -462,7 +462,7 @@ server112 -ProductionCandidate +production 2 high 2 @@ -482,7 +482,7 @@ server113 -ProductionCandidate +production 2 high 2 @@ -502,7 +502,7 @@ server114 -ProductionCandidate +production 2 high 2 @@ -522,7 +522,7 @@ server115 -ProductionCandidate +production 2 high 2 @@ -542,7 +542,7 @@ server116 -ProductionCandidate +production 2 high 2 @@ -562,7 +562,7 @@ server117 -ProductionCandidate +production 2 high 2 @@ -582,7 +582,7 @@ server118 -ProductionCandidate +production 2 high 2 @@ -602,7 +602,7 @@ server119 -ProductionCandidate +production 2 high 2 @@ -622,7 +622,7 @@ server12 -ProductionCandidate +production 2 high 3 @@ -642,7 +642,7 @@ server120 -ProductionCandidate +production 2 high 2 @@ -662,7 +662,7 @@ server121 -ProductionCandidate +production 2 high 2 @@ -682,7 +682,7 @@ server122 -ProductionCandidate +production 2 high 2 @@ -702,7 +702,7 @@ server123 -ProductionCandidate +production 2 high 2 @@ -722,7 +722,7 @@ server124 -ProductionCandidate +production 2 high 2 @@ -742,7 +742,7 @@ server125 -ProductionCandidate +production 2 high 2 @@ -762,7 +762,7 @@ server126 -ProductionCandidate +production 2 high 2 @@ -782,7 +782,7 @@ server127 -ProductionCandidate +production 2 high 2 @@ -802,7 +802,7 @@ server128 -ProductionCandidate +production 2 high 2 @@ -822,7 +822,7 @@ server129 -ProductionCandidate +production 2 high 2 @@ -842,7 +842,7 @@ server13 -ProductionCandidate +production 2 high 3 @@ -862,7 +862,7 @@ server130 -ProductionCandidate +production 2 high 2 @@ -882,7 +882,7 @@ server131 -ProductionCandidate +production 2 high 2 @@ -902,7 +902,7 @@ server132 -ProductionCandidate +production 2 high 2 @@ -922,7 +922,7 @@ server133 -ProductionCandidate +production 2 high 2 @@ -942,7 +942,7 @@ server134 -ProductionCandidate +production 2 high 2 @@ -962,7 +962,7 @@ server135 -ProductionCandidate +production 2 high 2 @@ -982,7 +982,7 @@ server136 -ProductionCandidate +production 2 high 2 @@ -1002,7 +1002,7 @@ server137 -ProductionCandidate +production 2 high 2 @@ -1022,7 +1022,7 @@ server138 -ProductionCandidate +production 2 high 2 @@ -1042,7 +1042,7 @@ server139 -ProductionCandidate +production 2 high 2 @@ -1062,7 +1062,7 @@ server14 -ProductionCandidate +production 2 high 3 @@ -1082,7 +1082,7 @@ server140 -ProductionCandidate +production 2 high 2 @@ -1102,7 +1102,7 @@ server141 -ProductionCandidate +production 2 high 2 @@ -1122,7 +1122,7 @@ server142 -ProductionCandidate +production 2 high 2 @@ -1142,7 +1142,7 @@ server143 -ProductionCandidate +production 2 high 2 @@ -1162,7 +1162,7 @@ server144 -ProductionCandidate +production 2 high 2 @@ -1182,7 +1182,7 @@ server145 -ProductionCandidate +production 2 high 2 @@ -1202,7 +1202,7 @@ server146 -ProductionCandidate +production 2 high 2 @@ -1222,7 +1222,7 @@ server147 -ProductionCandidate +production 2 high 2 @@ -1242,7 +1242,7 @@ server148 -ProductionCandidate +production 2 high 2 @@ -1262,7 +1262,7 @@ server149 -ProductionCandidate +production 2 high 2 @@ -1282,7 +1282,7 @@ server15 -ProductionCandidate +production 2 high 3 @@ -1302,7 +1302,7 @@ server150 -ProductionCandidate +production 2 high 2 @@ -1322,7 +1322,7 @@ server151 -ProductionCandidate +production 2 high 2 @@ -1342,7 +1342,7 @@ server152 -ProductionCandidate +production 2 high 2 @@ -1362,7 +1362,7 @@ server153 -ProductionCandidate +production 2 high 2 @@ -1382,7 +1382,7 @@ server154 -ProductionCandidate +production 2 high 2 @@ -1402,7 +1402,7 @@ server155 -ProductionCandidate +production 2 high 2 @@ -1422,7 +1422,7 @@ server156 -ProductionCandidate +production 2 high 2 @@ -1442,7 +1442,7 @@ server157 -ProductionCandidate +production 2 high 2 @@ -1462,7 +1462,7 @@ server158 -ProductionCandidate +production 2 high 2 @@ -1482,7 +1482,7 @@ server159 -ProductionCandidate +production 2 high 2 @@ -1502,7 +1502,7 @@ server16 -ProductionCandidate +production 2 high 3 @@ -1522,7 +1522,7 @@ server160 -ProductionCandidate +production 2 high 2 @@ -1542,7 +1542,7 @@ server161 -ProductionCandidate +production 2 high 2 @@ -1562,7 +1562,7 @@ server162 -ProductionCandidate +production 2 high 2 @@ -1582,7 +1582,7 @@ server163 -ProductionCandidate +production 2 high 2 @@ -1602,7 +1602,7 @@ server164 -ProductionCandidate +production 2 high 2 @@ -1622,7 +1622,7 @@ server165 -ProductionCandidate +production 2 high 2 @@ -1642,7 +1642,7 @@ server166 -ProductionCandidate +production 2 high 2 @@ -1662,7 +1662,7 @@ server167 -ProductionCandidate +production 2 high 2 @@ -1682,7 +1682,7 @@ server168 -ProductionCandidate +production 2 high 2 @@ -1702,7 +1702,7 @@ server169 -ProductionCandidate +production 2 high 2 @@ -1722,7 +1722,7 @@ server17 -ProductionCandidate +production 2 high 3 @@ -1742,7 +1742,7 @@ server170 -ProductionCandidate +production 2 high 2 @@ -1762,7 +1762,7 @@ server171 -ProductionCandidate +production 2 high 2 @@ -1782,7 +1782,7 @@ server172 -ProductionCandidate +production 2 high 2 @@ -1802,7 +1802,7 @@ server173 -ProductionCandidate +production 2 high 2 @@ -1822,7 +1822,7 @@ server174 -ProductionCandidate +production 2 high 2 @@ -1842,7 +1842,7 @@ server175 -ProductionCandidate +production 2 high 2 @@ -1862,7 +1862,7 @@ server176 -ProductionCandidate +production 2 high 2 @@ -1882,7 +1882,7 @@ server177 -ProductionCandidate +production 2 high 2 @@ -1902,7 +1902,7 @@ server178 -ProductionCandidate +production 2 high 2 @@ -1922,7 +1922,7 @@ server179 -ProductionCandidate +production 2 high 2 @@ -1942,7 +1942,7 @@ server18 -ProductionCandidate +production 2 high 3 @@ -1962,7 +1962,7 @@ server180 -ProductionCandidate +production 2 high 2 @@ -1982,7 +1982,7 @@ server181 -ProductionCandidate +production 2 high 2 @@ -2002,7 +2002,7 @@ server182 -ProductionCandidate +production 2 high 2 @@ -2022,7 +2022,7 @@ server183 -ProductionCandidate +production 2 high 2 @@ -2042,7 +2042,7 @@ server184 -ProductionCandidate +production 2 high 2 @@ -2062,7 +2062,7 @@ server185 -ProductionCandidate +production 2 high 2 @@ -2082,7 +2082,7 @@ server186 -ProductionCandidate +production 2 high 2 @@ -2102,7 +2102,7 @@ server187 -ProductionCandidate +production 2 high 2 @@ -2122,7 +2122,7 @@ server188 -ProductionCandidate +production 2 high 2 @@ -2142,7 +2142,7 @@ server189 -ProductionCandidate +production 2 high 2 @@ -2162,7 +2162,7 @@ server19 -ProductionCandidate +production 2 high 3 @@ -2182,7 +2182,7 @@ server190 -ProductionCandidate +production 2 high 2 @@ -2202,7 +2202,7 @@ server191 -ProductionCandidate +production 2 high 2 @@ -2222,7 +2222,7 @@ server192 -ProductionCandidate +production 2 high 2 @@ -2242,7 +2242,7 @@ server193 -ProductionCandidate +production 2 high 2 @@ -2262,7 +2262,7 @@ server194 -ProductionCandidate +production 2 high 2 @@ -2282,7 +2282,7 @@ server195 -ProductionCandidate +production 2 high 2 @@ -2302,7 +2302,7 @@ server196 -ProductionCandidate +production 2 high 2 @@ -2322,7 +2322,7 @@ server197 -ProductionCandidate +production 2 high 2 @@ -2342,7 +2342,7 @@ server198 -ProductionCandidate +production 2 high 2 @@ -2362,7 +2362,7 @@ server199 -ProductionCandidate +production 2 high 2 @@ -2382,7 +2382,7 @@ server20 -ProductionCandidate +production 2 high 3 @@ -2402,7 +2402,7 @@ server21 -ProductionCandidate +production 2 high 3 @@ -2422,7 +2422,7 @@ server22 -ProductionCandidate +production 2 high 3 @@ -2442,7 +2442,7 @@ server23 -ProductionCandidate +production 2 high 3 @@ -2462,7 +2462,7 @@ server24 -ProductionCandidate +production 2 high 3 @@ -2482,7 +2482,7 @@ server25 -ProductionCandidate +production 2 high 3 @@ -2502,7 +2502,7 @@ server26 -ProductionCandidate +production 2 high 3 @@ -2522,7 +2522,7 @@ server27 -ProductionCandidate +production 2 high 3 @@ -2542,7 +2542,7 @@ server28 -ProductionCandidate +production 2 high 3 @@ -2562,7 +2562,7 @@ server29 -ProductionCandidate +production 2 high 3 @@ -2582,7 +2582,7 @@ server30 -ProductionCandidate +production 2 high 3 @@ -2602,7 +2602,7 @@ server31 -ProductionCandidate +production 2 high 3 @@ -2622,7 +2622,7 @@ server32 -ProductionCandidate +production 2 high 3 @@ -2642,7 +2642,7 @@ server33 -ProductionCandidate +production 2 high 3 @@ -2662,7 +2662,7 @@ server34 -ProductionCandidate +production 2 high 3 @@ -2682,7 +2682,7 @@ server35 -ProductionCandidate +production 2 high 3 @@ -2702,7 +2702,7 @@ server36 -ProductionCandidate +production 2 high 3 @@ -2722,7 +2722,7 @@ server37 -ProductionCandidate +production 2 high 3 @@ -2742,7 +2742,7 @@ server38 -ProductionCandidate +production 2 high 3 @@ -2762,7 +2762,7 @@ server39 -ProductionCandidate +production 2 high 3 @@ -2782,7 +2782,7 @@ server40 -ProductionCandidate +production 2 high 3 @@ -2802,7 +2802,7 @@ server41 -ProductionCandidate +production 2 high 3 @@ -2822,7 +2822,7 @@ server42 -ProductionCandidate +production 2 high 3 @@ -2842,7 +2842,7 @@ server43 -ProductionCandidate +production 2 high 3 @@ -2862,7 +2862,7 @@ server44 -ProductionCandidate +production 2 high 3 @@ -2882,7 +2882,7 @@ server45 -ProductionCandidate +production 2 high 3 @@ -2902,7 +2902,7 @@ server46 -ProductionCandidate +production 2 high 3 @@ -2922,7 +2922,7 @@ server47 -ProductionCandidate +production 2 high 3 @@ -2942,7 +2942,7 @@ server48 -ProductionCandidate +production 2 high 3 @@ -2962,7 +2962,7 @@ server49 -ProductionCandidate +production 2 high 3 @@ -2982,7 +2982,7 @@ server50 -ProductionCandidate +production 2 high 3 @@ -3002,7 +3002,7 @@ server51 -ProductionCandidate +production 2 high 3 @@ -3022,7 +3022,7 @@ server52 -ProductionCandidate +production 2 high 3 @@ -3042,7 +3042,7 @@ server53 -ProductionCandidate +production 2 high 3 @@ -3062,7 +3062,7 @@ server54 -ProductionCandidate +production 2 high 3 @@ -3082,7 +3082,7 @@ server55 -ProductionCandidate +production 2 high 3 @@ -3102,7 +3102,7 @@ server56 -ProductionCandidate +production 2 high 3 @@ -3122,7 +3122,7 @@ server57 -ProductionCandidate +production 2 high 3 @@ -3142,7 +3142,7 @@ server58 -ProductionCandidate +production 2 high 3 @@ -3162,7 +3162,7 @@ server59 -ProductionCandidate +production 2 high 3 @@ -3182,7 +3182,7 @@ server60 -ProductionCandidate +production 2 high 3 @@ -3202,7 +3202,7 @@ server61 -ProductionCandidate +production 2 high 3 @@ -3222,7 +3222,7 @@ server62 -ProductionCandidate +production 2 high 3 @@ -3242,7 +3242,7 @@ server63 -ProductionCandidate +production 2 high 3 @@ -3262,7 +3262,7 @@ server64 -ProductionCandidate +production 2 high 3 @@ -3282,7 +3282,7 @@ server65 -ProductionCandidate +production 2 high 3 @@ -3302,7 +3302,7 @@ server66 -ProductionCandidate +production 2 high 3 @@ -3322,7 +3322,7 @@ server67 -ProductionCandidate +production 2 high 3 @@ -3342,7 +3342,7 @@ server68 -ProductionCandidate +production 2 high 3 @@ -3362,7 +3362,7 @@ server69 -ProductionCandidate +production 2 high 3 @@ -3382,7 +3382,7 @@ server70 -ProductionCandidate +production 2 high 3 @@ -3402,7 +3402,7 @@ server71 -ProductionCandidate +production 2 high 3 @@ -3422,7 +3422,7 @@ server72 -ProductionCandidate +production 2 high 3 @@ -3442,7 +3442,7 @@ server73 -ProductionCandidate +production 2 high 3 @@ -3462,7 +3462,7 @@ server74 -ProductionCandidate +production 2 high 3 @@ -3482,7 +3482,7 @@ server75 -ProductionCandidate +production 2 high 3 @@ -3502,7 +3502,7 @@ server76 -ProductionCandidate +production 2 high 3 @@ -3522,7 +3522,7 @@ server77 -ProductionCandidate +production 2 high 3 @@ -3542,7 +3542,7 @@ server78 -ProductionCandidate +production 2 high 3 @@ -3562,7 +3562,7 @@ server79 -ProductionCandidate +production 2 high 3 @@ -3582,7 +3582,7 @@ server80 -ProductionCandidate +production 2 high 3 @@ -3602,7 +3602,7 @@ server81 -ProductionCandidate +production 2 high 3 @@ -3622,7 +3622,7 @@ server82 -ProductionCandidate +production 2 high 3 @@ -3642,7 +3642,7 @@ server83 -ProductionCandidate +production 2 high 3 @@ -3662,7 +3662,7 @@ server84 -ProductionCandidate +production 2 high 3 @@ -3682,7 +3682,7 @@ server85 -ProductionCandidate +production 2 high 2 @@ -3702,7 +3702,7 @@ server86 -ProductionCandidate +production 2 high 2 @@ -3722,7 +3722,7 @@ server87 -ProductionCandidate +production 2 high 2 @@ -3742,7 +3742,7 @@ server88 -ProductionCandidate +production 2 high 2 @@ -3762,7 +3762,7 @@ server89 -ProductionCandidate +production 2 high 2 @@ -3782,7 +3782,7 @@ server90 -ProductionCandidate +production 2 high 2 @@ -3802,7 +3802,7 @@ server91 -ProductionCandidate +production 2 high 2 @@ -3822,7 +3822,7 @@ server92 -ProductionCandidate +production 2 high 2 @@ -3842,7 +3842,7 @@ server93 -ProductionCandidate +production 2 high 2 @@ -3862,7 +3862,7 @@ server94 -ProductionCandidate +production 2 high 2 @@ -3882,7 +3882,7 @@ server95 -ProductionCandidate +production 2 high 2 @@ -3902,7 +3902,7 @@ server96 -ProductionCandidate +production 2 high 2 @@ -3922,7 +3922,7 @@ server97 -ProductionCandidate +production 2 high 2 @@ -3942,7 +3942,7 @@ server98 -ProductionCandidate +production 2 high 2 @@ -3962,7 +3962,7 @@ server99 -ProductionCandidate +production 2 high 2 @@ -3982,7 +3982,7 @@ srv01 -Plugged +production 2 low 3 @@ -4002,7 +4002,7 @@ srv01.combodo.com -ProductionCandidate +production 2 high 3 @@ -4022,7 +4022,7 @@ srv02.combodo.com -ProductionCandidate +production 2 high 3 @@ -4042,7 +4042,7 @@ srv03.combodo.com -ProductionCandidate +production 2 high 3 @@ -4062,7 +4062,7 @@ srv04.combodo.com -ProductionCandidate +production 2 high 3 From 1fb8c78fe0f8543c7379eccdf2ecfe72b015ab6a Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Tue, 19 Jan 2010 09:37:29 +0000 Subject: [PATCH 205/227] Installation: improved the reporting during data load SVN:trunk[282] --- setup/xmldataloader.class.inc.php | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/setup/xmldataloader.class.inc.php b/setup/xmldataloader.class.inc.php index 3cd29cab1..46ee3acec 100644 --- a/setup/xmldataloader.class.inc.php +++ b/setup/xmldataloader.class.inc.php @@ -168,14 +168,18 @@ class XMLDataLoader $iExtKey = -$iDstObj; // Convention: Unresolved keys are stored as negative ! $oTargetObj->RegisterAsDirty(); } - // tested by Romain, little impact on perf (not significant on the intial setup) + // here we allow external keys to be invalid because we will resolve them later on... //$oTargetObj->CheckValue($sAttCode, $iExtKey); $oTargetObj->Set($sAttCode, $iExtKey); } else { // tested by Romain, little impact on perf (not significant on the intial setup) - //$oTargetObj->CheckValue($sAttCode, (string)$oXmlObj->$sAttCode); + if (!$oTargetObj->CheckValue($sAttCode, (string)$oXmlObj->$sAttCode)) + { + SetupWebPage::log("Error - Value not allowed - $sClass/$iSrcId - $sAttCode: '".$oXmlObj->$sAttCode."'"); + echo "Wrong value for attribute $sAttCode: '".$oXmlObj->$sAttCode."'"; + } $oTargetObj->Set($sAttCode, (string)$oXmlObj->$sAttCode); } } @@ -219,6 +223,7 @@ class XMLDataLoader } catch(Exception $e) { + SetupWebPage::log("Error - An object could not be loaded - $sClass/$iSrcId - ".$e->getMessage()); echo $e->GetHtmlDesc(); } $aParentClasses = MetaModel::EnumParentClasses($sClass); From 8078e2ce2613979658c12a97d0dcb0c881fd7584 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Tue, 19 Jan 2010 09:38:58 +0000 Subject: [PATCH 206/227] Installation: allow a service manager to write bizService objects SVN:trunk[283] --- addons/userrights/userrightsprofile.class.inc.php | 1 + 1 file changed, 1 insertion(+) diff --git a/addons/userrights/userrightsprofile.class.inc.php b/addons/userrights/userrightsprofile.class.inc.php index 295528987..b127f3831 100644 --- a/addons/userrights/userrightsprofile.class.inc.php +++ b/addons/userrights/userrightsprofile.class.inc.php @@ -1429,6 +1429,7 @@ class SetupITILProfiles 'lnkContactChange', ), 'Service' => array( + 'bizService', 'bizContract', 'lnkInfraContract', 'lnkContactContract', From 24713204afa42cc98088b5b9a2cd88b3024855b2 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Tue, 19 Jan 2010 09:50:51 +0000 Subject: [PATCH 207/227] Installation: disabled the notification templates (was attempting to send emails when loading sample data) SVN:trunk[284] --- setup/data/structure/24.actions.xml | 106 ++++++++++++++-------------- 1 file changed, 53 insertions(+), 53 deletions(-) diff --git a/setup/data/structure/24.actions.xml b/setup/data/structure/24.actions.xml index 465e9b2d8..1b7af12f5 100644 --- a/setup/data/structure/24.actions.xml +++ b/setup/data/structure/24.actions.xml @@ -1,16 +1,16 @@ - - - -Incident Notification to a Workgroup -This action informs a team that a ticket has been assigned their workgroup -enabled - - - -SELECT bizTeam AS t JOIN bizWorkgroup AS w ON w.team_id = t.id WHERE w.id=:this->workgroup_id - - -The ticket $this->name()$, severity $this->severity$ has been assigned to the workgroup $this->workgroup_name$ + + + +Incident Notification to a Workgroup +This action informs a team that a ticket has been assigned their workgroup +disabled + + + +SELECT bizTeam AS t JOIN bizWorkgroup AS w ON w.team_id = t.id WHERE w.id=:this->workgroup_id + + +The ticket $this->name()$, severity $this->severity$ has been assigned to the workgroup $this->workgroup_name$ <html> <body> <p>The incident ticket $this->name()$ has been assigned to the workgroup $this->workgroup_name$.</p> @@ -19,20 +19,20 @@ <hr/> <p>for more information on this ticket, click here: $this->hyperlink()$</p> </body> -</html> -normal - - -Incident notification to Agent -This action informs an agent that a ticket has been assigned to her/him -enabled - - - -SELECT bizPerson WHERE id=:this->agent_id - - -The ticket $this->name()$, severity $this->severity$ has been assigned to you +</html> +normal + + +Incident notification to Agent +This action informs an agent that a ticket has been assigned to her/him +disabled + + + +SELECT bizPerson WHERE id=:this->agent_id + + +The ticket $this->name()$, severity $this->severity$ has been assigned to you <html> <body> <p>The incident ticket $this->name()$ has been assigned to you.</p> @@ -41,29 +41,29 @@ <hr/> <p>for more information on this ticket, click here: $this->hyperlink()$</p> </body> -</html> -normal - - -Incident Notification to caller -This action is used to inform the caller -enabled - - - -SELECT bizPerson WHERE id=:this->caller_id - - -Ticket $this->name()$, severity $this->severity$ - $this->ticket_status$ -<html> -<body> -<p>The incident ticket $this->name()$ has changed to status $this->ticket_status$</p> -<p>Current situation: $this->current_situation$</p> -<p>Last update: $this->last_update$</p> -<hr/> -<p>for more information on this ticket, click here: $this->hyperlink()$</p> -</body> -</html> -normal - - \ No newline at end of file +</html> +normal + + +Incident Notification to caller +This action is used to inform the caller +disabled + + + +SELECT bizPerson WHERE id=:this->caller_id + + +Ticket $this->name()$, severity $this->severity$ - $this->ticket_status$ +<html> +<body> +<p>The incident ticket $this->name()$ has changed to status $this->ticket_status$</p> +<p>Current situation: $this->current_situation$</p> +<p>Last update: $this->last_update$</p> +<hr/> +<p>for more information on this ticket, click here: $this->hyperlink()$</p> +</body> +</html> +normal + + From 0e7297087b54d991b87b5a41cb2d45a6e8ede803 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Tue, 19 Jan 2010 12:39:28 +0000 Subject: [PATCH 208/227] - Don't loose the current context (current Org) - When switching Org, reload the current pages SVN:trunk[285] --- application/itopwebpage.class.inc.php | 2 +- application/template.class.inc.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/application/itopwebpage.class.inc.php b/application/itopwebpage.class.inc.php index 07ffba979..f89976d07 100644 --- a/application/itopwebpage.class.inc.php +++ b/application/itopwebpage.class.inc.php @@ -135,7 +135,7 @@ EOF { // Combo box to select the organization $this->AddToMenu("
    - \n"); // List of visible Organizations $oContext = new UserContext(); $oSearchFilter = $oContext->NewFilter("bizOrganization"); diff --git a/application/template.class.inc.php b/application/template.class.inc.php index 807ffdf30..6d13ff67a 100644 --- a/application/template.class.inc.php +++ b/application/template.class.inc.php @@ -34,7 +34,7 @@ class DisplayTemplate $oBlock = DisplayBlock::FromTemplate($sOuterTag); if (is_object($oBlock)) { - $oBlock->Display($oPage, 'block_'.self::$iBlockCount); + $oBlock->Display($oPage, 'block_'.self::$iBlockCount, $aParams); } self::$iBlockCount++; } From 07ebe739db008efdba41bf7230581029d9e7d5c2 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Tue, 19 Jan 2010 12:40:44 +0000 Subject: [PATCH 209/227] - Add the org_id in the shortlist of the Network Device SVN:trunk[286] --- business/itop.business.class.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/business/itop.business.class.inc.php b/business/itop.business.class.inc.php index d4d589dc1..09887c586 100644 --- a/business/itop.business.class.inc.php +++ b/business/itop.business.class.inc.php @@ -1364,7 +1364,7 @@ class bizNetworkDevice extends bizDevice // Display lists MetaModel::Init_SetZListItems('details', array('name', 'status','severity','org_id', 'location_id', 'brand','model','type','mgmt_ip','default_gateway','serial_number','ios_version','memory','snmp_read','snmp_write')); // Attributes to be displayed for the complete details - MetaModel::Init_SetZListItems('list', array('name', 'status','brand','model','type','mgmt_ip')); // Attributes to be displayed for a list + MetaModel::Init_SetZListItems('list', array('name', 'status','org_id','brand','model','type','mgmt_ip')); // Attributes to be displayed for a list // Search criteria MetaModel::Init_SetZListItems('standard_search', array('name', 'status', 'location_id', 'brand','model','type','mgmt_ip')); // Criteria of the std search form From f6b572ebcdcb904af68ddd8ebf1862fe4bd0ce3b Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Tue, 19 Jan 2010 15:32:09 +0000 Subject: [PATCH 210/227] - Fixed the display of audit errors SVN:trunk[287] --- pages/audit.php | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/pages/audit.php b/pages/audit.php index edfa99a3e..af388e3a8 100644 --- a/pages/audit.php +++ b/pages/audit.php @@ -75,7 +75,10 @@ switch($operation) $oAuditRule = $oContext->GetObject('AuditRule', $iRuleIndex); $oP->add(''); $oP->p('[Back to audit results]'); - cmdbAbstractObject::DisplaySet($oP, $oErrorObjectSet); + $sBlockId = 'audit_errors'; + $oP->p("
    \n"); + cmdbAbstractObject::DisplaySet($oP, $oErrorObjectSet, array('block_id' => $sBlockId)); + $oP->p("
    \n"); break; case 'audit': From 73f0ac4e1c9ad8a7f47d871388d2caf779b36c8f Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Mon, 25 Jan 2010 16:12:18 +0000 Subject: [PATCH 211/227] Trac#4 Improved the display of finalclass field (search results and graphs) SVN:trunk[288] --- application/displayblock.class.inc.php | 4 ++-- core/attributedef.class.inc.php | 5 +++++ core/metamodel.class.php | 5 +++-- 3 files changed, 10 insertions(+), 4 deletions(-) diff --git a/application/displayblock.class.inc.php b/application/displayblock.class.inc.php index 74d9f9e23..2f07a53e9 100644 --- a/application/displayblock.class.inc.php +++ b/application/displayblock.class.inc.php @@ -298,7 +298,7 @@ class DisplayBlock $aGroupBy = array(); while($oObj = $this->m_oSet->Fetch()) { - $sValue = $oObj->Get($sGroupByField); + $sValue = $oObj->GetAsHtml($sGroupByField); $aGroupBy[$sValue] = isset($aGroupBy[$sValue]) ? $aGroupBy[$sValue]+1 : 1; } $sFilter = urlencode($this->m_oFilter->serialize()); @@ -552,7 +552,7 @@ class DisplayBlock $aGroupBy = array(); while($oObj = $this->m_oSet->Fetch()) { - $sValue = $oObj->Get($sGroupByField); + $sValue = $oObj->GetAsHtml($sGroupByField); $aGroupBy[$sValue] = isset($aGroupBy[$sValue]) ? $aGroupBy[$sValue]+1 : 1; } $sFilter = urlencode($this->m_oFilter->serialize()); diff --git a/core/attributedef.class.inc.php b/core/attributedef.class.inc.php index ea207937d..076453ade 100644 --- a/core/attributedef.class.inc.php +++ b/core/attributedef.class.inc.php @@ -623,6 +623,11 @@ class AttributeClass extends AttributeString $aParams["allowed_values"] = new ValueSetEnumClasses($aParams['class_category'], $aParams['more_values']); parent::__construct($sCode, $aParams); } + + public function GetAsHTML($sValue) + { + return MetaModel::GetName($sValue); + } } /** diff --git a/core/metamodel.class.php b/core/metamodel.class.php index 28a5fcce6..1725daaab 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -832,10 +832,11 @@ abstract class MetaModel $sClassAttCode = 'finalclass'; $sRootClass = self::GetRootClass($sClass); $sDbFinalClassField = self::DBGetClassField($sRootClass); - $oClassAtt = new AttributeString($sClassAttCode, array( + $oClassAtt = new AttributeClass($sClassAttCode, array( "label"=>"Class", "description"=>"Real (final) object class", - "allowed_values"=>null, + "class_category"=>null, + "more_values"=>'', "sql"=>$sDbFinalClassField, "default_value"=>$sClass, "is_null_allowed"=>false, From 24e4e40e59a669468262f7970b71e28ef2b4f5a1 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Mon, 25 Jan 2010 17:30:52 +0000 Subject: [PATCH 212/227] - fixed detection of the upload_tmp_dir (Trac #76 ) - added the check of the soap extension (Trac #68 ) SVN:trunk[289] --- setup/index.php | 56 +++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 50 insertions(+), 6 deletions(-) diff --git a/setup/index.php b/setup/index.php index 811f08287..722cd012f 100644 --- a/setup/index.php +++ b/setup/index.php @@ -16,6 +16,50 @@ define('MYSQL_MIN_VERSION', '5.0.0'); $sOperation = Utils::ReadParam('operation', 'step1'); $oP = new SetupWebPage('iTop configuration wizard'); +/** + * Helper function to retrieve the system's temporary directory + * Emulates sys_get_temp_dir if neeed (PHP < 5.2.1) + * @return string Path to the system's temp directory + */ +function GetTmpDir() +{ + // try to figure out what is the temporary directory + // prior to PHP 5.2.1 the function sys_get_temp_dir + // did not exist + if ( !function_exists('sys_get_temp_dir')) + { + if( $temp=getenv('TMP') ) return realpath($temp); + if( $temp=getenv('TEMP') ) return realpath($temp); + if( $temp=getenv('TMPDIR') ) return realpath($temp); + $temp=tempnam(__FILE__,''); + if (file_exists($temp)) + { + unlink($temp); + return realpath(dirname($temp)); + } + return null; + } + else + { + return realpath(sys_get_temp_dir()); + } +} + + +/** + * Helper function to retrieve the directory where files are to be uploaded + * @return string Path to the temp directory used for uploading files + */ +function GetUploadTmpDir() +{ + $sPath = ini_get('upload_tmp_dir'); + if (empty($sPath)) + { + $sPath = GetTmpDir(); + } + return $sPath; +} + /** * Helper function to check if the current version of PHP * is compatible with the application @@ -34,7 +78,7 @@ function CheckPHPVersion(SetupWebPage $oP) $oP->error("Error: The current PHP Version (".phpversion().") is lower than the minimum required version (".PHP_MIN_VERSION.")"); return false; } - $aMandatoryExtensions = array('mysql', 'iconv', 'simplexml'); + $aMandatoryExtensions = array('mysql', 'iconv', 'simplexml', 'soap'); asort($aMandatoryExtensions); // Sort the list to look clean ! $aExtensionsOk = array(); $aMissingExtensions = array(); @@ -76,11 +120,11 @@ function CheckPHPVersion(SetupWebPage $oP) $bResult = false; } - $sUploadTmpDir = ini_get('upload_tmp_dir'); - if (empty($sUploadTmpDir)) - { - $oP->error("Temporary directory for files upload is not defined (upload_tmp_dir)"); - $bResult = false; + $sUploadTmpDir = GetUploadTmpDir(); + if (empty($sUploadTmpDir)) + { + $sUploadTmpDir = '/tmp'; + $oP->warning("Temporary directory for files upload is not defined (upload_tmp_dir), assuming that $sUploadTmpDir is used."); } // check that the upload directory is indeed writable from PHP if (!empty($sUploadTmpDir)) From 68b9446fdd0b44c0309dbc9f2b13b906126c2d9b Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Wed, 27 Jan 2010 17:13:55 +0000 Subject: [PATCH 213/227] - Fixed IE6/CSS bugs: #77 (calendar icon) and #78 (torn paper, workaround only) SVN:trunk[290] --- css/light-grey.css | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/css/light-grey.css b/css/light-grey.css index a3d4ef1ca..3a768a5d7 100644 --- a/css/light-grey.css +++ b/css/light-grey.css @@ -262,6 +262,7 @@ a.CollapsibleLabel, td a.CollapsibleLabel { background: url(../images/mini-arrow-green.gif) no-repeat left; } +/* Beware: IE6 does not support multiple selector with multiple classes, only the last class is used */ a.CollapsibleLabel.open, td a.CollapsibleLabel.open { margin: 0; padding: 0px 0pt 0px 16px; @@ -337,6 +338,7 @@ div.iTopLogo span { height: 50px; background: url(../images/vsplitter-grey.gif) repeat top; } +/* Beware: IE6 does not support multiple selector with multiple classes, only the last class is used */ #MySplitter .vsplitbar.active, #MySplitter .vsplitbar:hover { background-color: #fff; } @@ -344,6 +346,7 @@ div.iTopLogo span { height: 8px; background-color: #fff; } +/* Beware: IE6 does not support multiple selector with multiple classes, only the last class is used */ #MySplitter .hsplitbar.active, #MySplitter .hsplitbar:hover { background-color: #fff; } @@ -566,7 +569,7 @@ th.orange { /* For Date Picker: Creates a little calendar icon * instead of a text link for "Choose date" */ -td a.dp-choose-date, a.dp-choose-date, td a.dp-choose-date:hover, a.dp-choose-date:hover { +td a.dp-choose-date, a.dp-choose-date, td a.dp-choose-date:hover, a.dp-choose-date:hover, td a.dp-choose-date:visited, a.dp-choose-date:visited { float: left; width: 16px; height: 16px; @@ -664,24 +667,24 @@ div.HRDrawer { .mandatory { border: 1px solid #f00; } -table.listResults tr td.truncated { - background: transparent; -} -table.listResults tr td.truncated { +/* Beware: IE6 does not support multiple selector with multiple classes, only the last class is used */ +table.listResults tr.odd td.truncated { background: url(../images/truncated.png) bottom repeat-x; - margin-bottom: -3px; } +/* Beware: IE6 does not support multiple selector with multiple classes, only the last class is used */ table.listResults tr.even td.truncated { background: #f9f9f1 url(../images/truncated.png) bottom repeat-x; } -table.listResults tr.even td.truncated.hover { +/* Beware: IE6 does not support multiple selector with multiple classes, only the last class is used */ +table.listResults tr.even td.hover.truncated { background: #E8FFD3 url(../images/truncated.png) bottom repeat-x; } -table.listResults tr.odd td.truncated.hover { +/* Beware: IE6 does not support multiple selector with multiple classes, only the last class is used */ +table.listResults tr.odd td.hover.truncated { background: #E8FFD3 url(../images/truncated.png) bottom repeat-x; } From f61ac3f4a0155ddc3420445a5ff959c906c3a743 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Thu, 28 Jan 2010 17:10:39 +0000 Subject: [PATCH 214/227] Fixed two bugs: - Trac#80 Misleading messages when using the back button during the setup (added back button in any error case) - Trac#72 Revised the setup to consider any installation scenario regarding Database and user privileges (user rights tracked into the log file) SVN:trunk[291] --- application/application.inc.php | 1 - core/cmdbsource.class.inc.php | 32 ++++++++++++++++-- core/metamodel.class.php | 55 +++++++++++++++++++++++-------- core/test.class.inc.php | 4 +-- pages/ITopConsultant.php | 4 +-- setup/index.php | 57 ++++++++++++++++++++++----------- 6 files changed, 114 insertions(+), 39 deletions(-) diff --git a/application/application.inc.php b/application/application.inc.php index d15464c19..f613ff1e6 100644 --- a/application/application.inc.php +++ b/application/application.inc.php @@ -4,7 +4,6 @@ require_once('../application/applicationcontext.class.inc.php'); require_once('../application/usercontext.class.inc.php'); require_once('../application/cmdbabstract.class.inc.php'); require_once('../application/displayblock.class.inc.php'); -require_once('../application/iotask.class.inc.php'); require_once('../application/audit.category.class.inc.php'); require_once('../application/audit.rule.class.inc.php'); //require_once('../application/menunode.class.inc.php'); diff --git a/core/cmdbsource.class.inc.php b/core/cmdbsource.class.inc.php index 3543349e6..8c0e72409 100644 --- a/core/cmdbsource.class.inc.php +++ b/core/cmdbsource.class.inc.php @@ -20,6 +20,7 @@ class MySQLException extends CoreException public function __construct($sIssue, $aContext) { $aContext['mysql_error'] = mysql_error(); + $aContext['mysql_errno'] = mysql_errno(); parent::__construct($sIssue, $aContext); } } @@ -41,13 +42,13 @@ class CMDBSource self::$m_sDBName = $sSource; if (!self::$m_resDBLink = @mysql_pconnect($sServer, $sUser, $sPwd)) { - throw new MySQLException('Could not connect to the DB server', array('host'=>$sServer)); + throw new MySQLException('Could not connect to the DB server', array('host'=>$sServer, 'user'=>$sUser)); } if (!empty($sSource)) { if (!mysql_select_db($sSource, self::$m_resDBLink)) { - throw new MySQLException('Could not select DB', array('db_name'=>$sSource)); + throw new MySQLException('Could not select DB', array('host'=>$sServer, 'user'=>$sUser, 'db_name'=>$sSource)); } } } @@ -464,6 +465,33 @@ class CMDBSource } return $result; } + + + /** + * Returns the privileges of the current user + * @return string privileges in a raw format + */ + public static function GetRawPrivileges() + { + try + { + $result = self::Query('SHOW GRANTS'); // [ FOR CURRENT_USER()] + } + catch(MySQLException $e) + { + return "Current user not allowed to see his own privileges (could not access to the database 'mysql' - $iCode)"; + } + + $aRes = array(); + while ($aRow = mysql_fetch_array($result, MYSQL_NUM)) + { + // so far, only one column... + $aRes[] = implode('/', $aRow); + } + mysql_free_result($result); + // so far, only one line... + return implode(', ', $aRes); + } } diff --git a/core/metamodel.class.php b/core/metamodel.class.php index 1725daaab..6e053ea56 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -1941,29 +1941,56 @@ abstract class MetaModel echo "\n"; } - public static function DBExists() + public static function DBExists($bMustBeComplete = true) { - // returns true if at least one table exists (taking into account the DB sharing) - // then some tables might be missing, but that is made in DBCheckFormat + // returns true if at least one table exists // - if (empty(self::$m_sTablePrefix)) - { - return CMDBSource::IsDB(self::$m_sDBName); - } - // DB sharing - // Check if there is at least one table with the prefix - // if (!CMDBSource::IsDB(self::$m_sDBName)) { return false; } CMDBSource::SelectDB(self::$m_sDBName); - // If a table matches the prefix, then consider that the database already exists - $sSQL = "SHOW TABLES LIKE '".strtolower(self::$m_sTablePrefix)."%' "; - $result = CMDBSource::Query($sSQL); - return (CMDBSource::NbRows($result) > 0); + $aFound = array(); + $aMissing = array(); + foreach (self::DBEnumTables() as $sTable => $aClasses) + { + if (CMDBSource::IsTable($sTable)) + { + $aFound[] = $sTable; + } + else + { + $aMissing[] = $sTable; + } + } + + if (count($aFound) == 0) + { + // no expected table has been found + return false; + } + else + { + if (count($aMissing) == 0) + { + // the database is complete (still, could be some fields missing!) + return true; + } + else + { + // not all the tables, could be an older version + if ($bMustBeComplete) + { + return false; + } + else + { + return true; + } + } + } } public static function DBDrop() diff --git a/core/test.class.inc.php b/core/test.class.inc.php index 87af7e165..1b1a6cdf0 100644 --- a/core/test.class.inc.php +++ b/core/test.class.inc.php @@ -461,7 +461,7 @@ abstract class TestBizModel extends TestHandler protected function ResetDB() { - if (MetaModel::DBExists()) + if (MetaModel::DBExists(false)) { MetaModel::DBDrop(); } @@ -527,7 +527,7 @@ abstract class TestBizModelGeneric extends TestBizModel { parent::DoPrepare(); - if (!MetaModel::DBExists()) + if (!MetaModel::DBExists(false)) { MetaModel::DBCreate(); } diff --git a/pages/ITopConsultant.php b/pages/ITopConsultant.php index 68dcb6dca..022788c8c 100644 --- a/pages/ITopConsultant.php +++ b/pages/ITopConsultant.php @@ -151,9 +151,9 @@ function ShowDatabaseInfo() function CreateDB() { $sRes = "

    Creating the DB...

    \n"; - if (MetaModel::DBExists()) + if (MetaModel::DBExists(false)) { - $sRes .= "

    It appears that the DB already exists.

    \n"; + $sRes .= "

    It appears that the DB already exists (at least one table).

    \n"; } else { diff --git a/setup/index.php b/setup/index.php index 722cd012f..07abf9f49 100644 --- a/setup/index.php +++ b/setup/index.php @@ -184,6 +184,9 @@ function CheckServerConnection(SetupWebPage $oP, $sDBServer, $sDBUser, $sDBPwd) $oDBSource = new CMDBSource; $oDBSource->Init($sDBServer, $sDBUser, $sDBPwd); $oP->ok("Connection to '$sDBServer' as '$sDBUser' successful."); + + $oP->log("Info - User privileges: ".($oDBSource->GetRawPrivileges())); + $sDBVersion = $oDBSource->GetDBVersion(); if (version_compare($sDBVersion, MYSQL_MIN_VERSION, '>=')) { @@ -262,19 +265,33 @@ function InitDataModel(SetupWebPage $oP, $sConfigFileName, $bAllowMissingDatabas function CreateDatabaseStructure(SetupWebPage $oP, Config $oConfig, $sDBName, $sDBPrefix) { InitDataModel($oP, TMP_CONFIG_FILE, true); // Allow the DB to NOT exist since we're about to create it ! + $oP->log('Info - CreateDatabaseStructure'); - $oP->info("Creating the structure in '$sDBName' (prefix = '$sDBPrefix')."); - //MetaModel::CheckDefinitions(); - if (!MetaModel::DBExists()) + if (strlen($sDBPrefix) > 0) { - MetaModel::DBCreate(); - $oP->ok("Database structure created in '$sDBName' (prefix = '$sDBPrefix')."); + $oP->info("Creating the structure in '$sDBName' (table names prefixed by '$sDBPrefix')."); } else { - $oP->error("Error: database '$sDBName' (prefix = '$sDBPrefix') already exists."); - $oP->p("Tables with conflicting names already exist in the database. - Try selecting another database instance or specifiy a prefix to prevent conflicting table names."); + $oP->info("Creating the structure in '$sDBName'."); + } + + //MetaModel::CheckDefinitions(); + if (!MetaModel::DBExists(/* bMustBeComplete */ false)) + { + MetaModel::DBCreate(); + $oP->ok("Database structure successfuly created."); + } + else + { + if (strlen($sDBPrefix) > 0) + { + $oP->error("Error: found iTop tables into the database '$sDBName' (prefix: '$sDBPrefix'). Please, try selecting another database instance or specify another prefix to prevent conflicting table names."); + } + else + { + $oP->error("Error: found iTop tables into the database '$sDBName'. Please, try selecting another database instance or specify a prefix to prevent conflicting table names."); + } return false; } return true; @@ -380,8 +397,9 @@ function DisplayStep1(SetupWebPage $oP) $oP->add("
    Database connection\n"); $aForm = array(); $aForm[] = array('label' => "Server name$sRedStar:", 'input' => "", - 'help' => 'E.g. "localhost", "dbserver.mycompany.com" or "192.142.10.23".'); - $aForm[] = array('label' => "User name$sRedStar:", 'input' => ""); + 'help' => 'E.g. "localhost", "dbserver.mycompany.com" or "192.142.10.23"'); + $aForm[] = array('label' => "User name$sRedStar:", 'input' => "", + 'help' => 'The account must have the following privileges: SELECT, INSERT, UPDATE, DELETE, CREATE, ALTER'); $aForm[] = array('label' => 'Password:', 'input' => ""); $oP->form($aForm); $oP->add("
    \n"); @@ -407,7 +425,7 @@ function DisplayStep2(SetupWebPage $oP, Config $oConfig, $sDBServer, $sDBUser, $ if ($aDatabases === false) { // Connection failed, invalid credentials ? Go back - $oP->add("\n"); + $oP->add("\n"); } else { @@ -441,7 +459,7 @@ function DisplayStep2(SetupWebPage $oP, Config $oConfig, $sDBServer, $sDBUser, $ $oP->form($aForm); $oP->add("\n"); - $oP->add("\n"); + $oP->add("\n"); $oP->add("    \n"); $oP->add("\n"); } @@ -477,13 +495,13 @@ function DisplayStep3(SetupWebPage $oP, Config $oConfig, $sDBName, $sDBPrefix) $oP->form($aForm); $oP->add("\n"); $oP->add("\n"); - $oP->add("\n"); + $oP->add("\n"); $oP->add("    \n"); $oP->add("\n"); } else { - $oP->add("\n"); + $oP->add("\n"); } // Form goes here $oP->add("\n"); @@ -509,14 +527,14 @@ function DisplayStep4(SetupWebPage $oP, Config $oConfig, $sAdminUser, $sAdminPwd $oP->p(" Yes, for testing purposes, populate the database with sample data.\n"); $oP->p(" No, this is a production system, load only the data required by the application.\n"); $oP->p("\n"); - $oP->add("\n"); + $oP->add("\n"); $oP->add("    \n"); $oP->add("\n"); } else { // Creation failed - $oP->add("\n"); + $oP->add("\n"); } // End of visible form $oP->add("\n"); @@ -561,7 +579,7 @@ function DisplayStep5(SetupWebPage $oP, Config $oConfig, $sAuthUser, $sAuthPwd) $oP->add("
    \n"); $oP->ok("The initialization completed successfully."); // Form goes here - $oP->add("\n"); + $oP->add("\n"); $oP->add("    \n"); $oP->add("\n"); $oP->add("
    \n"); @@ -575,7 +593,7 @@ function DisplayStep5(SetupWebPage $oP, Config $oConfig, $sAuthUser, $sAuthPwd) $oP->error("Error: Failed to login for user: '$sAuthUser'\n"); $oP->add("
    \n"); - $oP->add("\n"); + $oP->add("\n"); $oP->add("    \n"); $oP->add("
    \n"); } @@ -585,6 +603,7 @@ function DisplayStep5(SetupWebPage $oP, Config $oConfig, $sAuthUser, $sAuthPwd) $oP->error("Error: unable to create the configuration file."); $oP->p($e->getHtmlDesc()); $oP->p("Did you forget to remove the previous (read-only) configuration file ?"); + $oP->add("\n"); } } /** @@ -686,10 +705,12 @@ try catch(Exception $e) { $oP->error("Error: '".$e->getMessage()."'"); + $oP->add("\n"); } catch(CoreException $e) { $oP->error("Error: '".$e->getHtmlDesc()."'"); + $oP->add("\n"); } $oP->output(); ?> From e55a11df4bb19b483d4cb52c4fd7ef51520da5f5 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Fri, 29 Jan 2010 17:52:47 +0000 Subject: [PATCH 215/227] Fixed #69 Accentuated chars now in utf-8 for real in the unit test page! BE CARREFUL WITH PSPAD!!! SVN:trunk[292] --- pages/testlist.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/pages/testlist.inc.php b/pages/testlist.inc.php index cd0a15de2..341d313e5 100644 --- a/pages/testlist.inc.php +++ b/pages/testlist.inc.php @@ -1471,7 +1471,7 @@ class TestTriggerAndEmail extends TestBizModel $oAction->Set("cc", $sCC); $oAction->Set("bcc", ""); $oAction->Set("subject", "New server: '\$this->name()$'"); - $oAction->Set("body", "

    Dear customer,

    We have created the server \$this->hyperlink()$ in the IT infrastructure database.

    You will be further notified when it is in Production.

    The IT infrastructure management team.

    Here are some accentuated characters for french people: 'ééà'

    "); + $oAction->Set("body", "

    Dear customer,

    We have created the server \$this->hyperlink()$ in the IT infrastructure database.

    You will be further notified when it is in Production.

    The IT infrastructure management team.

    Here are some accentuated characters for french people: 'ééà'

    "); $oAction->Set("importance", "low"); $iActionId = $this->ObjectToDB($oAction, true); From f8d6ef24072202855769bdb94192fd72e5dc1eab Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Fri, 29 Jan 2010 18:18:18 +0000 Subject: [PATCH 216/227] Fixed #81, character set and collation set to utf-8 by default SVN:trunk[293] --- core/cmdbsource.class.inc.php | 2 +- core/metamodel.class.php | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/core/cmdbsource.class.inc.php b/core/cmdbsource.class.inc.php index 8c0e72409..13ac98f8f 100644 --- a/core/cmdbsource.class.inc.php +++ b/core/cmdbsource.class.inc.php @@ -98,7 +98,7 @@ class CMDBSource public static function CreateDB($sSource) { - self::Query("CREATE DATABASE `$sSource`"); + self::Query("CREATE DATABASE `$sSource` CHARACTER SET utf8 COLLATE utf8_unicode_ci"); self::SelectDB($sSource); } diff --git a/core/metamodel.class.php b/core/metamodel.class.php index 6e053ea56..acd8003c4 100644 --- a/core/metamodel.class.php +++ b/core/metamodel.class.php @@ -2079,7 +2079,7 @@ abstract class MetaModel if (!CMDBSource::IsTable($sTable)) { $aErrors[$sClass][] = "table '$sTable' could not be found into the DB"; - $aSugFix[$sClass][] = "CREATE TABLE `$sTable` (`$sKeyField` INT(11) NOT NULL $sAutoIncrement PRIMARY KEY) ENGINE = innodb"; + $aSugFix[$sClass][] = "CREATE TABLE `$sTable` (`$sKeyField` INT(11) NOT NULL $sAutoIncrement PRIMARY KEY) ENGINE = innodb CHARACTER SET utf8 COLLATE utf8_unicode_ci"; } // Check that the key field exists // From 1237fb92d55b90285debecf0ab43faff34753bd1 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Fri, 29 Jan 2010 18:22:00 +0000 Subject: [PATCH 217/227] Fixed #82, could not use wget due to a conflict with page argument 'operation' SVN:trunk[294] --- application/loginwebpage.class.inc.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/application/loginwebpage.class.inc.php b/application/loginwebpage.class.inc.php index ae8ae075d..16195c513 100644 --- a/application/loginwebpage.class.inc.php +++ b/application/loginwebpage.class.inc.php @@ -58,7 +58,7 @@ h1 { $this->add("\n"); $this->add(" \n"); $this->add("\n"); - $this->add("\n"); + $this->add("\n"); $this->add("\n"); $this->add("
    \n"); } @@ -79,7 +79,7 @@ h1 { static function DoLogin() { - $operation = utils::ReadParam('operation', ''); + $operation = utils::ReadParam('loginop', ''); session_start(); if ($operation == 'logoff') From 46c4d2f419ae3b49f8e59f74f3a5f6a1e44c5751 Mon Sep 17 00:00:00 2001 From: Romain Quetiez Date: Fri, 29 Jan 2010 18:26:03 +0000 Subject: [PATCH 218/227] Make sure that HTTP responses and the emails are considered as being utf-8... not sure of the real benefit, but we've seen a number of things about this so Denis and I decided to more forward and take the chance. SVN:trunk[295] --- application/itopwebpage.class.inc.php | 1 + core/email.class.inc.php | 4 +++- 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/application/itopwebpage.class.inc.php b/application/itopwebpage.class.inc.php index f89976d07..97f720d15 100644 --- a/application/itopwebpage.class.inc.php +++ b/application/itopwebpage.class.inc.php @@ -220,6 +220,7 @@ EOF echo "\n"; echo "\n"; echo "{$this->s_title}\n"; + echo "\n"; echo $this->get_base_tag(); // Stylesheets MUST be loaded before any scripts otherwise // jQuery scripts may face some spurious problems (like failing on a 'reload') diff --git a/core/email.class.inc.php b/core/email.class.inc.php index 3a8a23e88..136c6e585 100644 --- a/core/email.class.inc.php +++ b/core/email.class.inc.php @@ -43,7 +43,9 @@ class EMail public function Send() { $sHeaders = 'MIME-Version: 1.0' . "\r\n"; - $sHeaders .= 'Content-type: text/html; charset=utf-8' . "\r\n"; + // ! the case is important for MS-Outlook + $sHeaders .= 'Content-Type: text/html; charset=UTF-8' . "\r\n"; + $sHeaders .= 'Content-Transfer-Encoding: 8bit' . "\r\n"; foreach ($this->m_aHeaders as $sKey => $sValue) { $sHeaders .= "$sKey: $sValue\r\n"; From 9952b9608d39f13fe30a6bf2ee3de41ad5b08eec Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Wed, 17 Feb 2010 09:10:24 +0000 Subject: [PATCH 219/227] - Removed the limit to the number of elements displayed in an autocomplete input. The maximum number of elements in the autocomplete is now 150 (scrolling enabled). (Trac #83) SVN:trunk[296] --- application/cmdbabstract.class.inc.php | 2 +- application/ui.linkswidget.class.inc.php | 2 +- pages/ajax.render.php | 1 - 3 files changed, 2 insertions(+), 3 deletions(-) diff --git a/application/cmdbabstract.class.inc.php b/application/cmdbabstract.class.inc.php index 3f3dbc2d8..c14eaa9f7 100644 --- a/application/cmdbabstract.class.inc.php +++ b/application/cmdbabstract.class.inc.php @@ -741,7 +741,7 @@ abstract class cmdbAbstractObject extends CMDBObject $sHTMLValue = ""; // another hidden input to store & pass the object's Id $sHTMLValue .= "\n"; - $oPage->add_ready_script("\$('#label_$iInputId').autocomplete('./ajax.render.php', { minChars:3, onItemSelect:selectItem, onFindValue:findValue, formatItem:formatItem, autoFill:true, keyHolder:'#$iInputId', extraParams:{operation:'autocomplete', sclass:'$sClass',attCode:'".$sAttCode."'}});"); + $oPage->add_ready_script("\$('#label_$iInputId').autocomplete('./ajax.render.php', { scroll:true, minChars:3, onItemSelect:selectItem, onFindValue:findValue, formatItem:formatItem, autoFill:true, keyHolder:'#$iInputId', extraParams:{operation:'autocomplete', sclass:'$sClass',attCode:'".$sAttCode."'}});"); $oPage->add_ready_script("\$('#label_$iInputId').result( function(event, data, formatted) { if (data) { $('#{$iInputId}').val(data[1]); } } );"); // Prepopulate with a default value -- but no display value... //if (!empty($value)) diff --git a/application/ui.linkswidget.class.inc.php b/application/ui.linkswidget.class.inc.php index aa6b47684..c01ea9d22 100644 --- a/application/ui.linkswidget.class.inc.php +++ b/application/ui.linkswidget.class.inc.php @@ -86,7 +86,7 @@ class UILinksWidget // another hidden input to store & pass the object's Id $sHTMLValue .= "m_iInputId}\" onChange=\"EnableAddButton('{$this->m_iInputId}');\"/>\n"; $sHTMLValue .= "m_iInputId}\" name=\"attr_{$this->m_sAttCode}{$this->m_sNameSuffix}\" value=\"\"/>\n"; - $oPage->add_ready_script("\$('#{$this->m_iInputId}').val('$sJSON');\noLinkWidget{$this->m_iInputId}.Init();\n\$('#ac_{$this->m_iInputId}').autocomplete('./ajax.render.php', { minChars:3, onItemSelect:selectItem, onFindValue:findValue, formatItem:formatItem, autoFill:true, keyHolder:'#id_ac_{$this->m_iInputId}', extraParams:{operation:'ui.linkswidget', sclass:'{$this->m_sClass}', attCode:'{$this->m_sAttCode}', max:30}});"); + $oPage->add_ready_script("\$('#{$this->m_iInputId}').val('$sJSON');\noLinkWidget{$this->m_iInputId}.Init();\n\$('#ac_{$this->m_iInputId}').autocomplete('./ajax.render.php', { scroll:true, minChars:3, onItemSelect:selectItem, onFindValue:findValue, formatItem:formatItem, autoFill:true, keyHolder:'#id_ac_{$this->m_iInputId}', extraParams:{operation:'ui.linkswidget', sclass:'{$this->m_sClass}', attCode:'{$this->m_sAttCode}', max:30}});"); $oPage->add_ready_script("\$('#ac_add_{$this->m_iInputId}').attr('disabled', 'disabled');"); $oPage->add_ready_script("\$('#ac_{$this->m_iInputId}').result( function(event, data, formatted) { if (data) { $('#id_ac_{$this->m_iInputId}').val(data[1]); $('#ac_add_{$this->m_iInputId}').attr('disabled', ''); } else { $('#ac_add_{$this->m_iInputId}').attr('disabled', 'disabled'); } } );"); } diff --git a/pages/ajax.render.php b/pages/ajax.render.php index fcc2e0e90..6a44e7e1a 100644 --- a/pages/ajax.render.php +++ b/pages/ajax.render.php @@ -233,7 +233,6 @@ switch($operation) foreach($aAllowedValues as $key => $value) { $oPage->add($value."|".$key."\n"); - if ($iCount++) break; } break; From 0fe146ecfd35820a3b3d9e0c07bd0ea03806a69b Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Wed, 17 Feb 2010 09:52:23 +0000 Subject: [PATCH 220/227] Fixed bug #79: When creating a new object, all mandatory attributes must be filled before the "Finish" button can be used (even for stateless objects). SVN:trunk[297] --- application/uiwizard.class.inc.php | 110 ++++++++++++++++++----------- 1 file changed, 68 insertions(+), 42 deletions(-) diff --git a/application/uiwizard.class.inc.php b/application/uiwizard.class.inc.php index 9ee6c1d83..61933198a 100644 --- a/application/uiwizard.class.inc.php +++ b/application/uiwizard.class.inc.php @@ -146,58 +146,85 @@ $sJSHandlerCode $aStates = MetaModel::EnumStates($this->m_sClass); - if ( (!empty($this->m_sTargetState)) && (count($aStates[$this->m_sTargetState]['attribute_list']) > 0) ) + $aMandatoryAttributes = array(); + // Some attributes are always mandatory independently of the state machine (if any) + foreach(MetaModel::GetAttributesList($this->m_sClass) as $sAttCode) + { + $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode); + if (!$oAttDef->IsExternalField() && !$oAttDef->IsNullAllowed()) + { + $aMandatoryAttributes[$sAttCode] = OPT_ATT_MANDATORY; + } + } + + // Now check the attributes that are mandatory in the specified state + if ( (!empty($this->m_sTargetState)) && (count($aStates[$this->m_sTargetState]['attribute_list']) > 0) ) { // Check all the fields that *must* be included in the wizard for this // particular target state $aFields = array(); foreach($aStates[$this->m_sTargetState]['attribute_list'] as $sAttCode => $iOptions) { - $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode); - $sAttLabel = $oAttDef->GetLabel(); - - if ($iOptions & (OPT_ATT_MANDATORY | OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT)) - { - $aPrerequisites = $oAttDef->GetPrerequisiteAttributes(); - $aFields[$sAttCode] = array(); - foreach($aPrerequisites as $sCode) - { - $aFields[$sAttCode][$sCode] = ''; - } - } - } - - // Now use the dependencies between the fields to order them - while(count($aFields) > 0) + if (isset($aMandatoryAttributes[$sAttCode])) + { + $aMandatoryAttributes[$sAttCode] |= $iOptions; + } + else + { + $aMandatoryAttributes[$sAttCode] = $iOptions; + } + } + } + + // Check all the fields that *must* be included in the wizard + $aFields = array(); + foreach($aMandatoryAttributes as $sAttCode => $iOptions) + { + $oAttDef = MetaModel::GetAttributeDef($this->m_sClass, $sAttCode); + $sAttLabel = $oAttDef->GetLabel(); + + if ( ($iOptions & (OPT_ATT_MANDATORY | OPT_ATT_MUSTCHANGE | OPT_ATT_MUSTPROMPT)) ) { - $aCurrentStep = array(); - foreach($aFields as $sAttCode => $aDependencies) + $aPrerequisites = $oAttDef->GetPrerequisiteAttributes(); + $aFields[$sAttCode] = array(); + foreach($aPrerequisites as $sCode) { - // All fields with no remaining dependencies can be entered at this - // step of the wizard - if (count($aDependencies) == 0) - { - $aCurrentStep[] = $sAttCode; - $aFieldsDone[$sAttCode] = ''; - unset($aFields[$sAttCode]); - // Remove this field from the dependencies of the other fields - foreach($aFields as $sUpdatedCode => $aDummy) - { - // remove the dependency - unset($aFields[$sUpdatedCode][$sAttCode]); - } - } + $aFields[$sAttCode][$sCode] = ''; } - if (count($aCurrentStep) == 0) - { - // This step of the wizard would contain NO field ! - echo "Error: Circular reference in the dependencies between the fields."; - print_r($aFields); - break; - } - $aWizardSteps['mandatory'][] = $aCurrentStep; } } + + // Now use the dependencies between the fields to order them + while(count($aFields) > 0) + { + $aCurrentStep = array(); + foreach($aFields as $sAttCode => $aDependencies) + { + // All fields with no remaining dependencies can be entered at this + // step of the wizard + if (count($aDependencies) == 0) + { + $aCurrentStep[] = $sAttCode; + $aFieldsDone[$sAttCode] = ''; + unset($aFields[$sAttCode]); + // Remove this field from the dependencies of the other fields + foreach($aFields as $sUpdatedCode => $aDummy) + { + // remove the dependency + unset($aFields[$sUpdatedCode][$sAttCode]); + } + } + } + if (count($aCurrentStep) == 0) + { + // This step of the wizard would contain NO field ! + echo "Error: Circular reference in the dependencies between the fields."; + print_r($aFields); + break; + } + $aWizardSteps['mandatory'][] = $aCurrentStep; + } + // Now computes the steps to fill the optional fields $sStateAttCode = MetaModel::GetStateAttributeCode($this->m_sClass); @@ -258,7 +285,6 @@ $sJSHandlerCode } $aWizardSteps['optional'][] = $aCurrentStep; } - return $aWizardSteps; } From 4928220098c8b38649269c225daeead1e8d9d135 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Wed, 17 Feb 2010 22:45:17 +0000 Subject: [PATCH 221/227] - Try to protect as much as possible from fatal errors occuring during the data load: 1) Make sure we have enough memory to perform the task 2) In case of fatal error in the Ajax call, catch it, display it to the end-user and stop the installation. See Trac #84 for more information. SVN:trunk[298] --- setup/ajax.dataloader.php | 53 +++++++++++++++++++++++++++++++++++---- setup/index.php | 1 + setup/setup.js | 3 ++- 3 files changed, 51 insertions(+), 6 deletions(-) diff --git a/setup/ajax.dataloader.php b/setup/ajax.dataloader.php index 66ab56195..fab829719 100644 --- a/setup/ajax.dataloader.php +++ b/setup/ajax.dataloader.php @@ -5,11 +5,51 @@ * 'file' string Name of the file to load * 'session_status' string 'start', 'continue' or 'end' * 'percent' integer 0..100 the percentage of completion once the file has been loaded - */ + */ +define('SAFE_MINIMUM_MEMORY', 32*1024*1024); require_once('../application/utils.inc.php'); +require_once('./setuppage.class.inc.php'); + +$iMemoryLimit = utils::ConvertToBytes(ini_get('memory_limit')); +if ($iMemoryLimit < SAFE_MINIMUM_MEMORY) +{ + if (ini_set('memory_limit', SAFE_MINIMUM_MEMORY) === FALSE) + { + SetupWebPage::error("memory_limit is too small: $iMemoryLimit and can not be increased by the script itself."); + } + else + { + SetupWebPage::log("memory_limit increased from $iMemoryLimit to ".SAFE_MINIMUM_MEMORY."."); + } +} + +function FatalErrorCatcher($sOutput) +{ + if ( preg_match('|.*|s', $sOutput, &$aMatches) ) + { + header("HTTP/1.0 500 Internal server error."); + foreach ($aMatches as $sMatch) + { + $errors .= strip_tags($sMatch)."\n"; + } + $sOutput = "$errors\n"; + // Logging to a file does not work if the whole memory is exhausted... + //SetupWebPage::error("Fatal error - in $__FILE__ , $errors"); + } + return $sOutput; +} + +//Define some bogus, invalid HTML tags that no sane +//person would ever put in an actual document and tell +//PHP to delimit fatal error warnings with them. +ini_set('error_prepend_string', ''); +ini_set('error_append_string', ''); + +// Starts the capture of the ouput, and sets a filter to capture the fatal errors. +ob_start('FatalErrorCatcher'); // Start capturing the output, and pass it through the fatal error catcher + require_once('../core/config.class.inc.php'); require_once('../core/cmdbsource.class.inc.php'); -require_once('./setuppage.class.inc.php'); require_once('./xmldataloader.class.inc.php'); define('TMP_CONFIG_FILE', '../tmp-config-itop.php'); @@ -18,8 +58,7 @@ define('TMP_CONFIG_FILE', '../tmp-config-itop.php'); // Never cache this page header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1 header("Expires: Fri, 17 Jul 1970 05:00:00 GMT"); // Date in the past - - + /** * Main program */ @@ -62,6 +101,10 @@ catch(Exception $e) echo "

    An error happened while loading the data

    \n"; echo '

    '.$e."

    \n"; SetupWebPage::log("Error - An error happened while loading the data. ".$e); +} + +if (function_exists('memory_get_peak_usage')) +{ + SetupWebPage::log("Info - loading file '$sFileName', peak memory usage. ".memory_get_peak_usage()); } - ?> diff --git a/setup/index.php b/setup/index.php index 07abf9f49..7afc7f5e4 100644 --- a/setup/index.php +++ b/setup/index.php @@ -544,6 +544,7 @@ function DisplayStep4(SetupWebPage $oP, Config $oConfig, $sAdminUser, $sAdminPwd $oP->add("\n"); // To be compatible with login page $oP->add("\n"); $oP->add("\n"); + $oP->add("
    \n"); $oP->add_linked_script('./jquery.progression.js'); PopulateDataFilesList($oP); diff --git a/setup/setup.js b/setup/setup.js index de99764ed..d9fc63c5f 100644 --- a/setup/setup.js +++ b/setup/setup.js @@ -103,7 +103,8 @@ function DoLoadDataAsynchronous() $('#setup').block({message: '

    Loading data...

    0%

    '}); $('#progress').progression( {Current:0, Maximum: 100, aBackgroundImg: 'orange-progress.gif', aTextColor: '#000000'} ); - LoadNextDataFile('', ''); + $('#log').ajaxError(function(e, xhr, settings, exception) { alert('Fatal error detected: '+ xhr.responseText); $('#log').append(xhr.responseText); $('#setup').unblock(); } ); + LoadNextDataFile('', ''); return false; // Stop here for now } From 6556f893767e018ef54998b22b41aa0f1e5bcdbc Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Thu, 18 Feb 2010 08:47:46 +0000 Subject: [PATCH 222/227] - Release of version 0.9 SVN:trunk[299] --- application/startup.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/startup.inc.php b/application/startup.inc.php index 3b4f86b88..d7a3854cb 100644 --- a/application/startup.inc.php +++ b/application/startup.inc.php @@ -1,5 +1,5 @@ Date: Fri, 19 Feb 2010 10:10:10 +0000 Subject: [PATCH 223/227] - When creating a new object linked to the location (contact, server, PC), the location must be pre-filled automatically. SVN:trunk[300] --- business/templates/location.html | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/business/templates/location.html b/business/templates/location.html index 04d0609cf..2e3e67591 100644 --- a/business/templates/location.html +++ b/business/templates/location.html @@ -10,13 +10,13 @@ SELECT bizContact WHERE location_id = $pkey$
    - SELECT bizServer WHERE location_id = $pkey$ + SELECT bizServer WHERE location_id = $pkey$ - SELECT bizPC WHERE location_id = $pkey$ + SELECT bizPC WHERE location_id = $pkey$ - SELECT bizNetworkDevice WHERE location_id = $pkey$ + SELECT bizNetworkDevice WHERE location_id = $pkey$ From f1226ca65e40cf7154c8237a8f24e35f3b71d134 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Fri, 19 Feb 2010 10:11:41 +0000 Subject: [PATCH 224/227] - cleanup of the "bizCircuit" object and its associated template. SVN:trunk[301] --- business/itop.business.class.inc.php | 6 +++--- business/templates/Circuits.html | 9 --------- business/templates/circuit.html | 21 +++++++++++++++++++++ 3 files changed, 24 insertions(+), 12 deletions(-) delete mode 100644 business/templates/Circuits.html create mode 100644 business/templates/circuit.html diff --git a/business/itop.business.class.inc.php b/business/itop.business.class.inc.php index 09887c586..61ef31a3b 100644 --- a/business/itop.business.class.inc.php +++ b/business/itop.business.class.inc.php @@ -743,7 +743,7 @@ class bizCircuit extends logInfra "db_table" => "circuits", "db_key_field" => "id", "db_finalclass_field" => "", - "display_template" => "../business/templates/Circuits.html", + "display_template" => "../business/templates/circuit.html", ); MetaModel::Init_Params($aParams); @@ -754,11 +754,11 @@ class bizCircuit extends logInfra MetaModel::Init_AddAttribute(new AttributeExternalKey("location2_id", array("targetclass"=>"bizLocation", "label"=>"Location 2", "description"=>"Id of the location 2", "allowed_values"=>new ValueSetObjects('SELECT bizLocation AS p WHERE p.org_id = :this->org_id'), "sql"=>"location2_id", "is_null_allowed"=>false, "on_target_delete"=>DEL_MANUAL,"depends_on"=>array("org_id")))); MetaModel::Init_AddAttribute(new AttributeExternalField("location2_name", array("label"=>"Location 2", "description"=>"Name of the location", "allowed_values"=>null, "extkey_attcode"=> 'location2_id', "target_attcode"=>"name"))); - MetaModel::Init_AddAttribute(new AttributeExternalKey("interface1_id", array("targetclass"=>"bizInterface", "label"=>"Interface 1", "description"=>"id of the interface 1", "allowed_values"=>new ValueSetObjects('SELECT bizInterface AS p WHERE p.org_id = :this->org_id'), "sql"=>"interface1_id", "is_null_allowed"=>false,"on_target_delete"=>DEL_MANUAL, "depends_on"=>array("org_id")))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("interface1_id", array("targetclass"=>"bizInterface", "label"=>"Interface 1", "description"=>"id of the interface 1", "allowed_values"=>new ValueSetObjects('SELECT bizInterface AS Intf JOIN bizDevice AS Dev ON Intf.device_id = Dev.id WHERE Intf.org_id = :this->org_id AND Dev.location_id = :this->location1_id'), "sql"=>"interface1_id", "is_null_allowed"=>false,"on_target_delete"=>DEL_MANUAL, "depends_on"=>array("org_id", "location1_id")))); MetaModel::Init_AddAttribute(new AttributeExternalField("interface1_name", array("label"=>"Interface", "description"=>"Name of the interface 1", "allowed_values"=>null, "extkey_attcode"=> 'interface1_id', "target_attcode"=>"name"))); MetaModel::Init_AddAttribute(new AttributeExternalField("device1_name", array("label"=>"Device 1", "description"=>"Name of the device 1", "allowed_values"=>null, "extkey_attcode"=> 'interface1_id', "target_attcode"=>"device_name"))); - MetaModel::Init_AddAttribute(new AttributeExternalKey("interface2_id", array("targetclass"=>"bizInterface", "label"=>"Interface 2", "description"=>"id of the interface 2", "allowed_values"=>new ValueSetObjects('SELECT bizInterface AS p WHERE p.org_id = :this->org_id'), "sql"=>"interface2_id", "is_null_allowed"=>false,"on_target_delete"=>DEL_MANUAL, "depends_on"=>array("org_id")))); + MetaModel::Init_AddAttribute(new AttributeExternalKey("interface2_id", array("targetclass"=>"bizInterface", "label"=>"Interface 2", "description"=>"id of the interface 2", "allowed_values"=>new ValueSetObjects('SELECT bizInterface AS Intf JOIN bizDevice AS Dev ON Intf.device_id = Dev.id WHERE Intf.org_id = :this->org_id AND Dev.location_id = :this->location2_id'), "sql"=>"interface2_id", "is_null_allowed"=>false,"on_target_delete"=>DEL_MANUAL, "depends_on"=>array("org_id", "location2_id")))); MetaModel::Init_AddAttribute(new AttributeExternalField("interface2_name", array("label"=>"Interface", "description"=>"Name of the interface 2", "allowed_values"=>null, "extkey_attcode"=> 'interface2_id', "target_attcode"=>"name"))); MetaModel::Init_AddAttribute(new AttributeExternalField("device2_name", array("label"=>"Interface", "description"=>"Name of the device 2", "allowed_values"=>null, "extkey_attcode"=> 'interface2_id', "target_attcode"=>"device_name"))); diff --git a/business/templates/Circuits.html b/business/templates/Circuits.html deleted file mode 100644 index e01fc9fec..000000000 --- a/business/templates/Circuits.html +++ /dev/null @@ -1,9 +0,0 @@ - - -$class$: pkey = $pkey$ - - diff --git a/business/templates/circuit.html b/business/templates/circuit.html new file mode 100644 index 000000000..2c2102c44 --- /dev/null +++ b/business/templates/circuit.html @@ -0,0 +1,21 @@ + + +$class$: pkey = $pkey$ + + + SELECT lnkContactRealObject WHERE object_id = $pkey$ + + + SELECT lnkInfraTicket WHERE infra_id = $pkey$ + + + SELECT bizChangeTicket AS Change JOIN lnkInfraChangeTicket AS Link ON Link.ticket_id = Change.id WHERE Link.infra_id = $pkey$ + + + SELECT lnkDocumentRealObject WHERE object_id = $pkey$ + + From ee2c9d6c373c2d4814c0f4c4c14b764135a811df Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Fri, 19 Feb 2010 14:45:06 +0000 Subject: [PATCH 225/227] - pie chart labels do not support HTML, only plain text (at least in this version of OFC) SVN:trunk[302] --- application/displayblock.class.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/displayblock.class.inc.php b/application/displayblock.class.inc.php index 2f07a53e9..90ddbbcad 100644 --- a/application/displayblock.class.inc.php +++ b/application/displayblock.class.inc.php @@ -552,7 +552,7 @@ class DisplayBlock $aGroupBy = array(); while($oObj = $this->m_oSet->Fetch()) { - $sValue = $oObj->GetAsHtml($sGroupByField); + $sValue = $oObj->Get($sGroupByField); $aGroupBy[$sValue] = isset($aGroupBy[$sValue]) ? $aGroupBy[$sValue]+1 : 1; } $sFilter = urlencode($this->m_oFilter->serialize()); From 00ca7bb885d2f3766126a4f25e9d4e8b4ed0eacd Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Fri, 19 Feb 2010 17:00:29 +0000 Subject: [PATCH 226/227] - Cosmetic cleanup of menus to align them to the same presentation SVN:trunk[303] --- setup/data/structure/1.menus.xml | 1921 +++++++++++++++--------------- 1 file changed, 967 insertions(+), 954 deletions(-) diff --git a/setup/data/structure/1.menus.xml b/setup/data/structure/1.menus.xml index 67f55994a..c1e725471 100644 --- a/setup/data/structure/1.menus.xml +++ b/setup/data/structure/1.menus.xml @@ -1,325 +1,306 @@ - - - -Admin tools - -UI.php - - -administrator -1000 -0 -0 - - -All Applications - -UI.php - - -application -999 -16 -0 - - -All Circuits - -UI.php - - -application -999 -16 -0 - - -All Contracts - -./UI.php - - -application -2 -38 -0 - - -All Interfaces - -UI.php - - -application -999 -16 -0 - - -All Network devices - -UI.php - - -application -999 -16 -0 - - -All Patches - -UI.php - - -application -999 -16 -0 - - -All PCs - -UI.php - - -application -999 -16 -0 - - -All Servers - -UI.php - - -application -999 -16 -0 - - -All Services - -UI.php - - -application -1 -38 -0 - - -All Subnets - -UI.php - - -application -999 -16 -0 - - -Audit - -./audit.php - - -application -4 -17 -0 - - -Change Management - -./UI.php - - -application -7 -0 -0 - - -Closed Changes - -UI.php - - -application -5 -13 -0 - - -Closed Incident - -./UI.php - - -application -5 -24 -0 - - -Configuration Items - -UI.php - - -application -2 -17 -0 - - -Configuration Management - -UI.php - + + + +Admin tools + +UI.php + + +administrator +1000 +0 +0 + + +All Applications + +UI.php + + +application +999 +16 +0 + + +All Circuits + +UI.php + + +application +999 +16 +0 + + +All Contracts + +./UI.php + + +application +2 +38 +0 + + +All Interfaces + +UI.php + + +application +999 +16 +0 + + +All Network devices + +UI.php + + +application +999 +16 +0 + + +All Patches + +UI.php + + +application +999 +16 +0 + + +All PCs + +UI.php + + +application +999 +16 +0 + + +All Servers + +UI.php + + +application +999 +16 +0 + + +All Services + +UI.php + + +application +1 +38 +0 + + +All Subnets + +UI.php + + +application +999 +16 +0 + + +Application log + +UI.php +../images/std_view.gif + +administrator +2 +1 +0 + + +Audit + +./audit.php + + +application +4 +17 +0 + + +Change Management + +./UI.php + + +application +7 +0 +0 + + +Closed Changes + +UI.php + + +application +5 +13 +0 + + +Closed Incident + +./UI.php + + +application +5 +24 +0 + + +Configuration Items + +UI.php + -application -2 -0 -0 - - -Contacts - -UI.php - - +application +2 +17 +0 + + +Configuration Management + +UI.php + + +application +2 +0 +0 + + +Contacts + +UI.php + + -application -1 -17 -0 - - -CSV import - -csvimport.php - - -application -999 -40 -0 - - -Data Model - -schema.php - - -administrator -1500 -1 -0 - - -Document - -UI.php - - -application -6 -17 -0 - - -Export - -../webservices/export.php - - -administrator -1001 -1 -0 - - -Grouping - -UI.php - - -application -3 -17 -0 - - -Incident Management - -./UI.php - +<td class="dashboard"> +<p style="text-align:left; font-family:Verdana, Arial, sans-serif; font-size:16px;">Contacts by Status</p> +<itopblock BlockClass="DisplayBlock" objectclass="bizContact" type="count" parameters="group_by:status" asynchronous="false" encoding="text/oql">SELECT bizContact</itopblock> +</td> +</tr> +</table> +application +1 +17 +0 + + +CSV import + +csvimport.php + + +application +999 +40 +0 + + +Data Model + +schema.php + + +administrator +1500 +1 +0 + + +Document + +UI.php + + +application +6 +17 +0 + + +Export + +../webservices/export.php + + +administrator +1001 +1 +0 + + +Grouping + +UI.php + + +application +3 +17 +0 + + +Incident Management + +./UI.php + + +application +5 +0 +0 + + +Known Errors + +./UI.php + + +application +999 +24 +0 + + +Locations + +UI.php + + +application +5 +17 +0 + + +My Changes + +UI.php + + +application +1 +13 +0 + + +My Incidents + +UI.php + + +application +1 +24 +0 + + +My Service calls + +UI.php + + +application +1 +37 +0 + + +Notifications + +UI.php + + +administrator +2 +1 +0 + + +Open Changes + +./UI.php + + +application +3 +13 +0 + + +Open Incidents + +UI.php + + +application +3 +24 +0 + + +Open Service calls + +UI.php + + +application +2 +37 +0 + + +Persons + +UI.php + + +application +7 +18 +0 + + +Profiles + +UI.php +../images/std_view.gif + +administrator +11 +43 +0 + + +Run queries + +./run_query.php + + +administrator +1002 +1 +0 + + +Scheduled Outages + +./UI.php + + +application +7 +13 +0 + + +Service Desk + +UI.php + + +application +3 +0 +0 + + +Service Management + +./UI.php + -application -5 -0 -0 - - -Known Errors - -./UI.php - - -application -999 -24 -0 - - -Locations - -UI.php - - -application -5 -17 -0 - - -My Changes - -UI.php - - -application -1 -13 -0 - - -My Incidents - -UI.php - - -application -1 -24 -0 - - -My Service calls - -UI.php - - -application -1 -37 -0 - - -Open Changes - -./UI.php - - -application -3 -13 -0 - - -Open Incidents - -UI.php - - -application -3 -24 -0 - - -Open Service calls - -UI.php - - -application -2 -37 -0 - - -Persons - -UI.php - - -application -7 -18 -0 - - -Profiles - -UI.php -../images/std_view.gif - -administrator -11 -43 -0 - - -Run queries - -./run_query.php - - -administrator -1002 -1 -0 - - -Scheduled Outages - -./UI.php - - -application -7 -13 -0 - - -Service Desk - -UI.php - - -application -3 -0 -0 - - -Service Management - -./UI.php - - -application -8 -0 -0 - - -Teams - -UI.php - - -application -8 -18 -0 - - -Tools - -UI.php - - -application -9 -0 -0 - - -Universal Search - -UniversalSearch.php - - -administrator -1600 -1 -0 - - -User logins - -UI.php -../images/std_view.gif - -administrator -10 -43 -0 - - -User management - -UI.php - - -administrator -1 -1 -0 - - -Welcome - -./UI.php - - -application -1 -0 -0 - - -Application log - -UI.php -../images/std_view.gif - -administrator -2 -1 -0 - - -Notifications - -UI.php - - -administrator -2 -1 -0 - - + +application +8 +0 +0 + + +Teams + +UI.php + + +application +8 +18 +0 + + +Tools + +UI.php + + +application +9 +0 +0 + + +Universal Search + +UniversalSearch.php + + +administrator +1600 +1 +0 + + +User logins + +UI.php +../images/std_view.gif + +administrator +10 +43 +0 + + +User management + +UI.php + + +administrator +1 +1 +0 + + +Welcome + +./UI.php + + +application +1 +0 +0 + + From b163e2a606654725c7b62a59bd87741074e59805 Mon Sep 17 00:00:00 2001 From: Denis Flaven Date: Mon, 22 Feb 2010 17:06:25 +0000 Subject: [PATCH 227/227] - Fixed logout bug introduced by revision [294] SVN:trunk[306] --- application/itopwebpage.class.inc.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/application/itopwebpage.class.inc.php b/application/itopwebpage.class.inc.php index 97f720d15..53b861809 100644 --- a/application/itopwebpage.class.inc.php +++ b/application/itopwebpage.class.inc.php @@ -296,7 +296,7 @@ EOF echo "
    Logged in as '$sUserName' $sIsAdmin  "; echo "
    \n"; echo "\n"; - echo "\n"; + echo "\n"; echo "
    \n"; echo "
    \n";