├── wwwroot ├── pix │ ├── 1x1t.gif │ ├── slb.png │ ├── 8021q.png │ ├── checked.png │ ├── favicon.ico │ ├── files.png │ ├── link-up.png │ ├── racks.png │ ├── report.png │ ├── router.png │ ├── server.png │ ├── download.png │ ├── link-down.png │ ├── unchecked.png │ ├── addressspace.png │ ├── ajax-loader.gif │ ├── bg_selection.gif │ ├── checkbox_yes.png │ ├── download-big.png │ ├── kterm-16x16.png │ ├── loadbalancer.png │ ├── mitsudragon.png │ ├── patch_cables.png │ ├── pencil-icon.png │ ├── serverpool.png │ ├── servicesign.png │ ├── addressspacev6.png │ ├── bracket-16x16.png │ ├── bracket-32x32.png │ ├── configuration.png │ ├── link-disabled.png │ ├── node-collapsed.png │ ├── node-expanded.png │ ├── smiley_knight.png │ ├── tango-computer.png │ ├── tango-go-next.png │ ├── tango-list-add.png │ ├── crystal-ok-32x32.png │ ├── pgadmin3-viewdata.png │ ├── tango-edit-clear.png │ ├── tango-go-previous.png │ ├── tango-list-remove.png │ ├── tango-text-html.png │ ├── virtualresources.png │ ├── tango-dialog-error.png │ ├── tango-document-new.png │ ├── tango-emblem-system.png │ ├── tango-list-add-big.png │ ├── tango-network-wired.png │ ├── tango-system-search.png │ ├── node-expanded-static.png │ ├── tango-dialog-error-big.png │ ├── tango-document-new-big.png │ ├── tango-edit-clear-big.png │ ├── tango-edit-copy-16x16.png │ ├── tango-edit-copy-32x32.png │ ├── tango-edit-cut-16x16.png │ ├── tango-edit-cut-22x22.png │ ├── tango-edit-cut-32x32.png │ ├── tango-emblem-favorite.png │ ├── tango-internet-32x32.png │ ├── tango-user-trash-16x16.png │ ├── tango-user-trash-32x32.png │ ├── crystal-edit-user-32x32.png │ ├── crystal-file-empty-32x32.png │ ├── crystal-file-image-32x32.png │ ├── crystal-file-text-32x32.png │ ├── tango-appointment-32x32.png │ ├── tango-go-prev-next-32x32.png │ ├── tango-list-remove-32x32.png │ ├── tango-list-remove-shadow.png │ ├── tango-view-refresh-16x16.png │ ├── tango-view-refresh-32x32.png │ ├── crystal-network_local-16x16.png │ ├── crystal-network_local-32x32.png │ ├── pgadmin3-viewdata-grayscale.png │ ├── pgadmin3-viewfiltereddata.png │ ├── tango-document-save-16x16.png │ ├── tango-document-save-32x32.png │ ├── tango-edit-clear-gray-32x32.png │ ├── tango-edit-cut-22x22-gray.png │ ├── tango-emblem-readonly-32x32.png │ ├── tango-emblem-system-32x32.png │ ├── tango-mail-attachment-16x16.png │ ├── tango-mail-attachment-22x22.png │ ├── tango-mail-attachment-32x32.png │ ├── tango-system-search-22x22.png │ ├── tango-text-x-generic-16x16.png │ ├── tango-user-trash-16x16-gray.png │ ├── tango-user-trash-32x32-gray.png │ ├── tango-view-fullscreen-22x22.png │ ├── crystal-actions-unlock-32x32.png │ ├── tango-emblem-important-32x32.png │ ├── tango-emblem-unreadable-32x32.png │ ├── tango-go-prev-next-gray-32x32.png │ ├── tango-document-save-32x32-gray.png │ ├── crystal-mimetypes-shellscript-218x200.png │ ├── pgadmin3-viewfiltereddata-grayscale.png │ └── jquery-ui-1.8.22.redmond │ │ ├── ui-icons_217bc0_256x240.png │ │ ├── ui-icons_2e83ff_256x240.png │ │ ├── ui-icons_469bdd_256x240.png │ │ ├── ui-icons_6da8d5_256x240.png │ │ ├── ui-icons_cd0a0a_256x240.png │ │ ├── ui-icons_d8e7f3_256x240.png │ │ ├── ui-icons_f9bd01_256x240.png │ │ ├── ui-bg_flat_0_aaaaaa_40x100.png │ │ ├── ui-bg_flat_55_fbec88_40x100.png │ │ ├── ui-bg_glass_75_d0e5f5_1x400.png │ │ ├── ui-bg_glass_85_dfeffc_1x400.png │ │ ├── ui-bg_glass_95_fef1ec_1x400.png │ │ ├── ui-bg_gloss-wave_55_5c9ccc_500x100.png │ │ ├── ui-bg_inset-hard_100_f5f8f9_1x100.png │ │ └── ui-bg_inset-hard_100_fcfdfd_1x100.png ├── js │ ├── bulkselector.js │ ├── net-usage.js │ ├── codemirror │ │ ├── rackcode.js │ │ ├── htmlmixed.js │ │ ├── codemirror.css │ │ └── php.js │ ├── vst_editor.js │ ├── slb_editor.js │ ├── racktables.js │ ├── tag-cb.js │ ├── inplace-edit.js │ ├── jquery.thumbhover.js │ ├── live_validation.js │ ├── portinfo.js │ ├── jquery.optionTree.js │ └── jquery.contextmenu.js ├── inc │ ├── config.php │ ├── pre-init.php │ ├── caching.php │ ├── init.php │ ├── triggers.php │ └── ajax-interface.php ├── css │ ├── tagit.css │ └── jquery.contextmenu.css └── index.php ├── tests ├── phpunit.xml ├── README ├── bootstrap.php ├── RenderDepotTest.php ├── StringInsertHrefsTest.php ├── TagTreeCircularReferenceTest.php ├── GetChildrenListTest.php ├── LinkTriggerTest.php └── ObjectCircularReferenceTest.php ├── plugins └── README ├── gateways ├── ssh ├── netcat ├── telnet ├── ucssdk └── sshnokey ├── Makefile ├── README.Fedora ├── COPYING └── scripts └── syncdomain.php /wwwroot/pix/1x1t.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/1x1t.gif -------------------------------------------------------------------------------- /wwwroot/pix/slb.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/slb.png -------------------------------------------------------------------------------- /wwwroot/pix/8021q.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/8021q.png -------------------------------------------------------------------------------- /wwwroot/pix/checked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/checked.png -------------------------------------------------------------------------------- /wwwroot/pix/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/favicon.ico -------------------------------------------------------------------------------- /wwwroot/pix/files.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/files.png -------------------------------------------------------------------------------- /wwwroot/pix/link-up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/link-up.png -------------------------------------------------------------------------------- /wwwroot/pix/racks.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/racks.png -------------------------------------------------------------------------------- /wwwroot/pix/report.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/report.png -------------------------------------------------------------------------------- /wwwroot/pix/router.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/router.png -------------------------------------------------------------------------------- /wwwroot/pix/server.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/server.png -------------------------------------------------------------------------------- /wwwroot/pix/download.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/download.png -------------------------------------------------------------------------------- /wwwroot/pix/link-down.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/link-down.png -------------------------------------------------------------------------------- /wwwroot/pix/unchecked.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/unchecked.png -------------------------------------------------------------------------------- /wwwroot/pix/addressspace.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/addressspace.png -------------------------------------------------------------------------------- /wwwroot/pix/ajax-loader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/ajax-loader.gif -------------------------------------------------------------------------------- /wwwroot/pix/bg_selection.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/bg_selection.gif -------------------------------------------------------------------------------- /wwwroot/pix/checkbox_yes.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/checkbox_yes.png -------------------------------------------------------------------------------- /wwwroot/pix/download-big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/download-big.png -------------------------------------------------------------------------------- /wwwroot/pix/kterm-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/kterm-16x16.png -------------------------------------------------------------------------------- /wwwroot/pix/loadbalancer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/loadbalancer.png -------------------------------------------------------------------------------- /wwwroot/pix/mitsudragon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/mitsudragon.png -------------------------------------------------------------------------------- /wwwroot/pix/patch_cables.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/patch_cables.png -------------------------------------------------------------------------------- /wwwroot/pix/pencil-icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/pencil-icon.png -------------------------------------------------------------------------------- /wwwroot/pix/serverpool.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/serverpool.png -------------------------------------------------------------------------------- /wwwroot/pix/servicesign.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/servicesign.png -------------------------------------------------------------------------------- /wwwroot/pix/addressspacev6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/addressspacev6.png -------------------------------------------------------------------------------- /wwwroot/pix/bracket-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/bracket-16x16.png -------------------------------------------------------------------------------- /wwwroot/pix/bracket-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/bracket-32x32.png -------------------------------------------------------------------------------- /wwwroot/pix/configuration.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/configuration.png -------------------------------------------------------------------------------- /wwwroot/pix/link-disabled.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/link-disabled.png -------------------------------------------------------------------------------- /wwwroot/pix/node-collapsed.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/node-collapsed.png -------------------------------------------------------------------------------- /wwwroot/pix/node-expanded.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/node-expanded.png -------------------------------------------------------------------------------- /wwwroot/pix/smiley_knight.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/smiley_knight.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-computer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-computer.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-go-next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-go-next.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-list-add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-list-add.png -------------------------------------------------------------------------------- /wwwroot/pix/crystal-ok-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/crystal-ok-32x32.png -------------------------------------------------------------------------------- /wwwroot/pix/pgadmin3-viewdata.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/pgadmin3-viewdata.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-edit-clear.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-edit-clear.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-go-previous.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-go-previous.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-list-remove.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-list-remove.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-text-html.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-text-html.png -------------------------------------------------------------------------------- /wwwroot/pix/virtualresources.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/virtualresources.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-dialog-error.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-dialog-error.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-document-new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-document-new.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-emblem-system.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-emblem-system.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-list-add-big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-list-add-big.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-network-wired.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-network-wired.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-system-search.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-system-search.png -------------------------------------------------------------------------------- /wwwroot/pix/node-expanded-static.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/node-expanded-static.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-dialog-error-big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-dialog-error-big.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-document-new-big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-document-new-big.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-edit-clear-big.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-edit-clear-big.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-edit-copy-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-edit-copy-16x16.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-edit-copy-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-edit-copy-32x32.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-edit-cut-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-edit-cut-16x16.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-edit-cut-22x22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-edit-cut-22x22.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-edit-cut-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-edit-cut-32x32.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-emblem-favorite.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-emblem-favorite.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-internet-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-internet-32x32.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-user-trash-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-user-trash-16x16.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-user-trash-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-user-trash-32x32.png -------------------------------------------------------------------------------- /wwwroot/pix/crystal-edit-user-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/crystal-edit-user-32x32.png -------------------------------------------------------------------------------- /wwwroot/pix/crystal-file-empty-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/crystal-file-empty-32x32.png -------------------------------------------------------------------------------- /wwwroot/pix/crystal-file-image-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/crystal-file-image-32x32.png -------------------------------------------------------------------------------- /wwwroot/pix/crystal-file-text-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/crystal-file-text-32x32.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-appointment-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-appointment-32x32.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-go-prev-next-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-go-prev-next-32x32.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-list-remove-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-list-remove-32x32.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-list-remove-shadow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-list-remove-shadow.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-view-refresh-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-view-refresh-16x16.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-view-refresh-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-view-refresh-32x32.png -------------------------------------------------------------------------------- /wwwroot/pix/crystal-network_local-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/crystal-network_local-16x16.png -------------------------------------------------------------------------------- /wwwroot/pix/crystal-network_local-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/crystal-network_local-32x32.png -------------------------------------------------------------------------------- /wwwroot/pix/pgadmin3-viewdata-grayscale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/pgadmin3-viewdata-grayscale.png -------------------------------------------------------------------------------- /wwwroot/pix/pgadmin3-viewfiltereddata.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/pgadmin3-viewfiltereddata.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-document-save-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-document-save-16x16.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-document-save-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-document-save-32x32.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-edit-clear-gray-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-edit-clear-gray-32x32.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-edit-cut-22x22-gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-edit-cut-22x22-gray.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-emblem-readonly-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-emblem-readonly-32x32.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-emblem-system-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-emblem-system-32x32.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-mail-attachment-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-mail-attachment-16x16.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-mail-attachment-22x22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-mail-attachment-22x22.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-mail-attachment-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-mail-attachment-32x32.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-system-search-22x22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-system-search-22x22.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-text-x-generic-16x16.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-text-x-generic-16x16.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-user-trash-16x16-gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-user-trash-16x16-gray.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-user-trash-32x32-gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-user-trash-32x32-gray.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-view-fullscreen-22x22.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-view-fullscreen-22x22.png -------------------------------------------------------------------------------- /wwwroot/pix/crystal-actions-unlock-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/crystal-actions-unlock-32x32.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-emblem-important-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-emblem-important-32x32.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-emblem-unreadable-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-emblem-unreadable-32x32.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-go-prev-next-gray-32x32.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-go-prev-next-gray-32x32.png -------------------------------------------------------------------------------- /wwwroot/pix/tango-document-save-32x32-gray.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/tango-document-save-32x32-gray.png -------------------------------------------------------------------------------- /wwwroot/pix/crystal-mimetypes-shellscript-218x200.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/crystal-mimetypes-shellscript-218x200.png -------------------------------------------------------------------------------- /wwwroot/pix/pgadmin3-viewfiltereddata-grayscale.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/pgadmin3-viewfiltereddata-grayscale.png -------------------------------------------------------------------------------- /wwwroot/pix/jquery-ui-1.8.22.redmond/ui-icons_217bc0_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/jquery-ui-1.8.22.redmond/ui-icons_217bc0_256x240.png -------------------------------------------------------------------------------- /wwwroot/pix/jquery-ui-1.8.22.redmond/ui-icons_2e83ff_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/jquery-ui-1.8.22.redmond/ui-icons_2e83ff_256x240.png -------------------------------------------------------------------------------- /wwwroot/pix/jquery-ui-1.8.22.redmond/ui-icons_469bdd_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/jquery-ui-1.8.22.redmond/ui-icons_469bdd_256x240.png -------------------------------------------------------------------------------- /wwwroot/pix/jquery-ui-1.8.22.redmond/ui-icons_6da8d5_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/jquery-ui-1.8.22.redmond/ui-icons_6da8d5_256x240.png -------------------------------------------------------------------------------- /wwwroot/pix/jquery-ui-1.8.22.redmond/ui-icons_cd0a0a_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/jquery-ui-1.8.22.redmond/ui-icons_cd0a0a_256x240.png -------------------------------------------------------------------------------- /wwwroot/pix/jquery-ui-1.8.22.redmond/ui-icons_d8e7f3_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/jquery-ui-1.8.22.redmond/ui-icons_d8e7f3_256x240.png -------------------------------------------------------------------------------- /wwwroot/pix/jquery-ui-1.8.22.redmond/ui-icons_f9bd01_256x240.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/jquery-ui-1.8.22.redmond/ui-icons_f9bd01_256x240.png -------------------------------------------------------------------------------- /wwwroot/pix/jquery-ui-1.8.22.redmond/ui-bg_flat_0_aaaaaa_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/jquery-ui-1.8.22.redmond/ui-bg_flat_0_aaaaaa_40x100.png -------------------------------------------------------------------------------- /wwwroot/pix/jquery-ui-1.8.22.redmond/ui-bg_flat_55_fbec88_40x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/jquery-ui-1.8.22.redmond/ui-bg_flat_55_fbec88_40x100.png -------------------------------------------------------------------------------- /wwwroot/pix/jquery-ui-1.8.22.redmond/ui-bg_glass_75_d0e5f5_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/jquery-ui-1.8.22.redmond/ui-bg_glass_75_d0e5f5_1x400.png -------------------------------------------------------------------------------- /wwwroot/pix/jquery-ui-1.8.22.redmond/ui-bg_glass_85_dfeffc_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/jquery-ui-1.8.22.redmond/ui-bg_glass_85_dfeffc_1x400.png -------------------------------------------------------------------------------- /wwwroot/pix/jquery-ui-1.8.22.redmond/ui-bg_glass_95_fef1ec_1x400.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/jquery-ui-1.8.22.redmond/ui-bg_glass_95_fef1ec_1x400.png -------------------------------------------------------------------------------- /wwwroot/pix/jquery-ui-1.8.22.redmond/ui-bg_gloss-wave_55_5c9ccc_500x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/jquery-ui-1.8.22.redmond/ui-bg_gloss-wave_55_5c9ccc_500x100.png -------------------------------------------------------------------------------- /wwwroot/pix/jquery-ui-1.8.22.redmond/ui-bg_inset-hard_100_f5f8f9_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/jquery-ui-1.8.22.redmond/ui-bg_inset-hard_100_f5f8f9_1x100.png -------------------------------------------------------------------------------- /wwwroot/pix/jquery-ui-1.8.22.redmond/ui-bg_inset-hard_100_fcfdfd_1x100.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ibettinger/racktables/HEAD/wwwroot/pix/jquery-ui-1.8.22.redmond/ui-bg_inset-hard_100_fcfdfd_1x100.png -------------------------------------------------------------------------------- /tests/phpunit.xml: -------------------------------------------------------------------------------- 1 | 4 | 5 | 6 | ./ 7 | 8 | 9 | 10 | -------------------------------------------------------------------------------- /wwwroot/js/bulkselector.js: -------------------------------------------------------------------------------- 1 | $(function () { 2 | $("#selectableRack tbody").selectable({ 3 | filter: 'td.atom', 4 | stop: function () { 5 | $(".ui-selected input:enabled", this).each(function () { 6 | this.checked = !this.checked 7 | }); 8 | } 9 | }); 10 | }); -------------------------------------------------------------------------------- /wwwroot/js/net-usage.js: -------------------------------------------------------------------------------- 1 | $(document).ready (function () { 2 | $('div.net-usage.pending').click(function(e) { 3 | var div = this; 4 | $.get( 5 | 'index.php', 6 | { 7 | module: 'ajax', 8 | ac: 'net-usage', 9 | net_id: div.id 10 | }, 11 | function (data, textStatus, jqXHR) { 12 | $(div) 13 | .removeClass('pending') 14 | .unbind(e) 15 | .replaceWith(data); 16 | } 17 | ) 18 | }); 19 | }); 20 | -------------------------------------------------------------------------------- /tests/README: -------------------------------------------------------------------------------- 1 | This directory stores PHPUnit tests. 2 | 3 | ############################# 4 | # WARNING # 5 | ############################# 6 | Do not run these tests against a production instance of RackTables! 7 | Data may be added, modified or deleted as part of the tests. 8 | If you understand the risk, edit bootstrap.php and delete the warning lines. 9 | 10 | To run a specific test: 11 | $ phpunit TestName 12 | 13 | To run all tests: 14 | $ phpunit 15 | -------------------------------------------------------------------------------- /tests/bootstrap.php: -------------------------------------------------------------------------------- 1 | 11 | -------------------------------------------------------------------------------- /wwwroot/inc/config.php: -------------------------------------------------------------------------------- 1 | 19 | -------------------------------------------------------------------------------- /tests/RenderDepotTest.php: -------------------------------------------------------------------------------- 1 | shelf_id = commitAddObject (NULL, NULL, 3, NULL); 13 | $this->modem_id = commitAddObject ('unit test modem', NULL, 13, NULL); 14 | commitLinkEntities ('object', $this->shelf_id, 'object', $this->modem_id); 15 | } 16 | 17 | public function tearDown () 18 | { 19 | // remove sample data 20 | commitDeleteObject ($this->modem_id); 21 | commitDeleteObject ($this->shelf_id); 22 | } 23 | 24 | public function testRenderDepot () 25 | { 26 | try { 27 | ob_start (); 28 | renderDepot (); 29 | ob_clean (); 30 | $this->assertTrue (TRUE); 31 | } 32 | catch (Exception $e) { 33 | ob_clean (); 34 | $this->assertTrue (FALSE); 35 | } 36 | } 37 | } 38 | ?> 39 | -------------------------------------------------------------------------------- /plugins/README: -------------------------------------------------------------------------------- 1 | Put your custom RackTables add-ons into this folder. 2 | 3 | Old local.php file is the sample of such add-ons and should be put here 4 | to continue working. 5 | 6 | Files should be named with .php extension to be loaded automatically. 7 | Files are loaded in alphabetical order. 8 | 9 | It is recommended to override existing tabhandlers and ophandlers using 10 | registerTabHandler and registerOpHandler functions, respectively. 11 | This is preferred over overwriting of items in $tabhandler and $ophandler, 12 | because allows overriding the same tab or operation in multiple plugins. 13 | 14 | Consider the following sample: 15 | 16 | 34 | -------------------------------------------------------------------------------- /wwwroot/js/codemirror/rackcode.js: -------------------------------------------------------------------------------- 1 | CodeMirror.defineMode('rackcode', function() { 2 | var allowkeywords = /^(allow)\b/i; 3 | var denykeywords = /^(deny)\b/i; 4 | var operatorkeywords = /^(and|or|not|true|false)\b/i; 5 | 6 | return { 7 | token: function(stream, state) { 8 | 9 | if (stream.eatSpace()) 10 | return null; 11 | 12 | var w; 13 | 14 | if (stream.eatWhile(/\w/)) { 15 | w = stream.current(); 16 | 17 | 18 | if (allowkeywords.test(w)) { 19 | return 'positive'; 20 | } else if (denykeywords.test(w)) { 21 | return 'negative'; 22 | } else if (operatorkeywords.test(w)) { 23 | return 'operator'; 24 | } 25 | 26 | } else if (stream.eat('#')) { 27 | stream.skipToEnd(); 28 | return 'comment'; 29 | } else if (stream.eat('{')) { 30 | while (w = stream.next()) { 31 | if (w == '}') 32 | break; 33 | 34 | if (w == '\\') 35 | stream.next(); 36 | } 37 | return 'tag'; 38 | } else { 39 | stream.next(); 40 | } 41 | return null; 42 | } 43 | }; 44 | }); 45 | 46 | CodeMirror.defineMIME("text/x-rackcode", "rackcode"); 47 | -------------------------------------------------------------------------------- /gateways/ssh: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | # This file is a part of RackTables, a datacenter and server room management 4 | # framework. See accompanying file "COPYING" for the full copyright and 5 | # licensing information. 6 | 7 | use strict; 8 | use Getopt::Long; 9 | 10 | # fetch command-line parameters 11 | my $op_help; 12 | my $op_as_user; 13 | GetOptions ( 14 | 'h' => \$op_help, 15 | 'sudo-user:s' => \$op_as_user, 16 | ); 17 | if ($op_help) { 18 | &display_help; 19 | exit; 20 | } 21 | 22 | &become_user; 23 | exec ('ssh', @ARGV) or die "exec ssh: $!"; 24 | 25 | sub display_help { 26 | print <] [ -- ] [ ssh params ] 31 | --as-user sudo self as specified username 32 | END 33 | } 34 | 35 | sub become_user { 36 | if (defined $op_as_user) { 37 | my $uid = getpwnam ($op_as_user); 38 | if (! defined $uid) { 39 | die "getpwnam $op_as_user: $!"; 40 | } 41 | if ($uid != $>) { 42 | exec ('sudo', '-u', "#$uid", $0, '--', @ARGV) or die "exec sudo: $!"; 43 | } 44 | } 45 | } 46 | -------------------------------------------------------------------------------- /tests/StringInsertHrefsTest.php: -------------------------------------------------------------------------------- 1 | assertEquals (string_insert_hrefs ($input), $output); 28 | } 29 | 30 | public function provider () 31 | { 32 | return array 33 | ( 34 | array 35 | ( 36 | 'This is a string with no links.', 37 | 'This is a string with no links.' 38 | ), 39 | array 40 | ( 41 | 'http://server/wiki/index.php/objectname', 42 | 'http://server/wiki/index.php/objectname [^]' 43 | ), 44 | array 45 | ( 46 | 'http://user:pass@www.example.tld/', 47 | 'http://user:pass@www.example.tld/ [^]' 48 | ), 49 | array 50 | ( 51 | 'username@example.tld', 52 | 'username@example.tld' 53 | ) 54 | ); 55 | } 56 | } 57 | ?> 58 | -------------------------------------------------------------------------------- /tests/TagTreeCircularReferenceTest.php: -------------------------------------------------------------------------------- 1 | 'unit test tag a')); 13 | $this->taga_id = lastInsertID (); 14 | usePreparedInsertBlade ('TagTree', array ('tag' => 'unit test tag b')); 15 | $this->tagb_id = lastInsertID (); 16 | usePreparedInsertBlade ('TagTree', array ('tag' => 'unit test tag c')); 17 | $this->tagc_id = lastInsertID (); 18 | } 19 | 20 | public function tearDown () 21 | { 22 | // remove sample data 23 | usePreparedExecuteBlade 24 | ( 25 | 'UPDATE TagTree SET parent_id = NULL WHERE id IN (?,?,?)', 26 | array ($this->taga_id, $this->tagb_id, $this->tagc_id) 27 | ); 28 | usePreparedDeleteBlade ('TagTree', array ('id' => $this->taga_id)); 29 | usePreparedDeleteBlade ('TagTree', array ('id' => $this->tagb_id)); 30 | usePreparedDeleteBlade ('TagTree', array ('id' => $this->tagc_id)); 31 | } 32 | 33 | /** 34 | * @expectedException RackTablesError 35 | */ 36 | public function testCreateCircularReference () 37 | { 38 | // set A as the parent of B, and B as the parent of C 39 | commitUpdateTag ($this->tagb_id, 'unit test tag b', $this->taga_id, 'yes'); 40 | commitUpdateTag ($this->tagc_id, 'unit test tag c', $this->tagb_id, 'yes'); 41 | // setting C as the parent of A should fail 42 | commitUpdateTag ($this->taga_id, 'unit test tag a', $this->tagc_id, 'yes'); 43 | } 44 | } 45 | ?> 46 | -------------------------------------------------------------------------------- /gateways/netcat: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | 3 | # This file is a part of RackTables, a datacenter and server room management 4 | # framework. See accompanying file "COPYING" for the full copyright and 5 | # licensing information. 6 | 7 | use strict; 8 | use Getopt::Long; 9 | use POSIX ":sys_wait_h"; 10 | 11 | # fetch command-line parameters 12 | my $op_help; 13 | my $op_port = 23; 14 | my $op_ncbin = 'nc'; 15 | my $op_timeout = 30; 16 | GetOptions ( 17 | 'h' => \$op_help, 18 | 'port|p:i' => \$op_port, 19 | 'ncbin|b:s' => \$op_ncbin, 20 | 'timeout|w:i' => \$op_timeout, 21 | ); 22 | if ($op_help) { 23 | &display_help; 24 | exit; 25 | } 26 | my $op_host = $ARGV[0]; 27 | defined $op_host or die "ERROR: missing hostname argument (-h for help)"; 28 | 29 | sub display_help { 30 | print < [-b ] [-p ] [--connect-timeout=] 34 | -p, --port TCP port number to connect to (defaults to 23) 35 | -b, --ncbin Full path to netcat binary (defaults to $op_ncbin) 36 | -w, --connect-timeout netcat timeout (defaults to $op_timeout) 37 | 38 | END 39 | } 40 | 41 | my @params = ('-w', $op_timeout, '-v'); 42 | 43 | pipe (RD, WR); 44 | my $child_pid = fork(); 45 | defined $child_pid or die "fork: $!"; 46 | if (! $child_pid) { 47 | close RD; 48 | open (STDERR, ">&WR") or die "open: $!"; 49 | exec ($op_ncbin, $op_host, $op_port, @params) or die "exec $op_ncbin: $!"; 50 | } 51 | close WR; 52 | 53 | # suppress error output if nc returns 0 54 | my $error_buff = ''; 55 | while () { 56 | $error_buff .= $_; 57 | } 58 | waitpid $child_pid, 0; 59 | if ($?) { 60 | print STDERR $error_buff; 61 | $? = $? >> 8; 62 | exit $? ? $? : 1; 63 | } 64 | -------------------------------------------------------------------------------- /wwwroot/js/vst_editor.js: -------------------------------------------------------------------------------- 1 | // var new_vst_row is set through inline JS script 2 | 3 | $(document).ready(function() { 4 | $('a.vst-add-rule').click(AddVSTRule) 5 | $('a.vst-del-rule').click(RemoveVSTRule) 6 | $('form#upd') 7 | .submit(VSTSubmit) 8 | .change(highlightUnsavedVST); 9 | }); 10 | 11 | function AddVSTRule(event) { 12 | var tr = $(this).closest('tr'); 13 | var new_tr; 14 | if ($(event.target).parents('.vst-add-rule.initial').length) 15 | new_tr = $('').html(new_vst_row); 16 | else 17 | new_tr = tr.clone(); 18 | tr.after(new_tr); 19 | new_tr.find('a.vst-add-rule').click(AddVSTRule); 20 | new_tr.find('a.vst-del-rule').click(RemoveVSTRule); 21 | 22 | highlightUnsavedVST(); 23 | return false; 24 | } 25 | 26 | function RemoveVSTRule(event) { 27 | $(this).closest('tr').remove(); 28 | highlightUnsavedVST(); 29 | return false; 30 | } 31 | 32 | function VSTSubmit() { 33 | var result = []; 34 | $('table.template-rules tr').each(function(i, item) { 35 | var line = {}; 36 | $(item).find('input , select').each(function(i, item) { line[item.name] = $(item).val(); }); 37 | if (line['rule_no'] != undefined) 38 | result.push(line); 39 | }); 40 | if (! result.length && ! confirm('The template is empty. Do you really want to save it?')) 41 | return false; 42 | $(this).find('input[name|="template_json"]').val(JSON.stringify(result)); 43 | return true; 44 | } 45 | 46 | function highlightUnsavedVST() { 47 | // highlight only on first call of this function 48 | if ( typeof highlightUnsavedVST.isAlreadyCalled == 'undefined' ) { 49 | highlightUnsavedVST.isAlreadyCalled = true; 50 | $('form#upd input[name|="submit"]').before('
Template is unsaved. Click here to save it
'); 51 | console.log('a'); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | DESTDIR ?= 2 | prefix ?= /usr/local 3 | docdir ?= $(prefix)/share/doc/RackTables 4 | datadir ?= $(prefix)/share 5 | # Default values reproduce the layout present in tar.gz, which concurs with 6 | # the default values of $racktables_gwdir and $racktables_staticdir. But 7 | # once the latter are patched for a shared deploy, it is enough to execute 8 | # "make install" with appropriate arguments to split the filesystem. 9 | staticdir ?= $(datadir)/RackTables/wwwroot 10 | applibdir ?= $(datadir)/RackTables/wwwroot/inc 11 | indexdir ?= $(datadir)/RackTables/wwwroot 12 | scriptdir ?= $(datadir)/RackTables 13 | 14 | INSTALL := install 15 | INSTALL_DATA := $(INSTALL) -m 644 16 | INSTALL_DIR := $(INSTALL) -m 755 -d 17 | INSTALL_PROGRAM := $(INSTALL) -m 755 18 | 19 | install-docs: COPYING ChangeLog LICENSE README 20 | $(INSTALL_DIR) $(DESTDIR)$(docdir) 21 | $(INSTALL_DATA) $^ $(DESTDIR)$(docdir) 22 | 23 | install-helpers: scripts gateways 24 | $(INSTALL_DIR) $(DESTDIR)$(scriptdir) 25 | cp -r $^ $(DESTDIR)$(scriptdir) 26 | find $(DESTDIR)$(scriptdir)/scripts -type d -a -name '.git' -exec rm -rf \{\} \; -prune 27 | find $(DESTDIR)$(scriptdir)/gateways -type d -a -name '.git' -exec rm -rf \{\} \; -prune 28 | 29 | install-static: wwwroot/css wwwroot/js wwwroot/pix 30 | $(INSTALL_DIR) $(DESTDIR)$(staticdir) 31 | cp -r $^ $(DESTDIR)$(staticdir) 32 | find $(DESTDIR)$(staticdir) -type d -a -name '.git' -exec rm -rf \{\} \; -prune 33 | 34 | install-applib: wwwroot/inc 35 | $(INSTALL_DIR) $(DESTDIR)$(applibdir)/inc 36 | $(INSTALL_DATA) wwwroot/inc/*.php $(DESTDIR)$(applibdir) 37 | 38 | install-index: wwwroot/index.php 39 | $(INSTALL_DIR) $(DESTDIR)$(indexdir) 40 | $(INSTALL_DATA) wwwroot/index.php $(DESTDIR)$(indexdir) 41 | 42 | install: install-helpers install-static install-applib install-index 43 | -------------------------------------------------------------------------------- /wwwroot/js/slb_editor.js: -------------------------------------------------------------------------------- 1 | $(document).ready (function () { 2 | // popup form for port/ip config update 3 | $('.slb-checks.editable li').click (function (e) { 4 | if (e.target.tagName != 'LI') 5 | return true; 6 | // clone form into new_form, re-setting all selectbox values which are cleared by clone() 7 | var old_form = $(this).find('form'); 8 | var new_form = old_form.clone(); 9 | new_form.find('select').each (function (i, elem) { 10 | var corresponding = old_form.find('select[name="' + $(this).attr('name') + '"]'); 11 | if (corresponding.length) 12 | $(this).val(corresponding.val()); 13 | }); 14 | var form = $('
').addClass ('popup-box').appendTo ('body').append (new_form); 15 | // confirmation box on port/vip deletion 16 | $('a.del-used-slb').click (function (e) { 17 | return confirm ('This entity is used. Please confirm the deletion'); 18 | }); 19 | 20 | $(this).thumbPopup (form, { event: e }); 21 | return false; 22 | }); 23 | 24 | // confirmation box on triplet deletion 25 | $('form#del input').click (function (e) { 26 | return confirm ('Please confirm the deletion of triplet'); 27 | }); 28 | 29 | // new triplet form stage1 handler 30 | var forms = $('form#addLink'); 31 | forms.submit (function() { return false }); 32 | forms.find('input[name="submit"]').click (function (e) { 33 | var form = $(this).parents('form'); 34 | $.ajax ({ 35 | type: 'POST', 36 | url: 'index.php', 37 | data: { 38 | 'module': 'ajax', 39 | 'ac': 'get-slb-form', 40 | 'form': form.serialize(), 41 | 'action': form.attr('action') 42 | }, 43 | 'success': function (data) { 44 | form.replaceWith (data); 45 | } 46 | }); 47 | 48 | return false; 49 | }); 50 | }); 51 | 52 | function slb_config_preview (e, object_id, vs_id, rspool_id) { 53 | $.ajax ({ 54 | type: 'POST', 55 | url: 'index.php', 56 | data: { 57 | module: 'ajax', 58 | ac: 'triplet-preview', 59 | object_id: object_id, 60 | vs_id: vs_id, 61 | rspool_id: rspool_id 62 | }, 63 | success: function (data) { 64 | var div = $('
').addClass('popup-box').appendTo('body'); 65 | div.html(data); 66 | $(this).thumbPopup (div, { event: e }); 67 | } 68 | }); 69 | } 70 | -------------------------------------------------------------------------------- /README.Fedora: -------------------------------------------------------------------------------- 1 | RUNNING RACKTABLES ON A FEDORA SYSTEM 2 | ===================================== 3 | 4 | Most files in Fedora package of RackTables application reside in the 5 | /usr/share/RackTables directory regardless of package version. It is 6 | assumed, that this directory is never directly used in httpd's "document 7 | root" setting. Instead a symlink or a PHP "require" construct should be 8 | used for the index.php (and ONLY index.php) file based on some other 9 | directory, which is actually used as wwwroot. This greatly improves 10 | security and allows for the two use cases described below. 11 | 12 | 1. Single RackTables instance per server. By default the application 13 | expects its configuration file (secret.php) in /etc/RackTables directory, 14 | which is a part of the RPM. In a fresh installation the files is missing, 15 | no MySQL database is setup and there is no symlink. 16 | 17 | Setting up the symlink is simple: one needs to decide on the URL of the 18 | RackTables system and map it to filesystem path. For example, the commands 19 | below are likely to enable access through "http://localhost/racktables/": 20 | 21 | # mkdir /var/www/html/racktables 22 | # ln -s /usr/share/RackTables/wwwroot/index.php /var/www/html/racktables 23 | 24 | After that opening the URL in a browser will bring up RackTables installer, 25 | which will fill in /etc/RackTables/secret.php file and the contents of the 26 | MySQL database. After that the setup is complete. 27 | 28 | 29 | 2. Multiple RackTables instances per server. The application is designed 30 | to be able to serve many independent MySQL databases with the same 31 | codebase, but different configuration files. This requires a separate 32 | config directory per each instance. Each of the directories will feature 33 | an own secret.php file configured for its own MySQL database. 34 | 35 | For example, 3 virtual hosts should provide an independent RackTables 36 | application each. It is assumed, that httpd is already configured for the 37 | virtual hosts using respective directories: 38 | 39 | /var/www/vhosts/racktables.example1.com 40 | /var/www/vhosts/racktables.example2.com 41 | /var/www/vhosts/racktables.example3.com 42 | 43 | The configuration directories could be: 44 | 45 | # mkdir /etc/RackTables/example[123] 46 | 47 | The "wwwroot" directories will require a short PHP wrapper file each. The 48 | wrappers will differ only in the value of $racktables_confdir setting: 49 | 50 | # cat > /var/www/vhosts/racktables.example1.com/index.php << 57 | EOF 58 | 59 | Once the wrapper files are installed, setup of each instance must be 60 | completed independently through the HTTP installer. Once this is done, 61 | every instance can be independently maintained, backed up and even deleted. 62 | -------------------------------------------------------------------------------- /wwwroot/js/racktables.js: -------------------------------------------------------------------------------- 1 | // JavaScript functions 2 | 3 | // Used for (un)checking an entire row of rackspace atoms 4 | function toggleRowOfAtoms (rackId, rowId) { 5 | var checkboxId; 6 | var toSet; 7 | toSet = null; 8 | for (var i=0; i<=2; i++) { 9 | checkboxId = "atom_" + rackId + "_" + rowId + "_" + i; 10 | 11 | // Abort if the box is disabled 12 | if (document.getElementById(checkboxId).disabled == true) continue; 13 | 14 | // Box isn't disabled, toggle it 15 | if (toSet == null) { 16 | toSet = !document.getElementById(checkboxId).checked; 17 | } 18 | document.getElementById(checkboxId).checked = toSet; 19 | } 20 | } 21 | 22 | // Used for (un)checking an entire column of rackspace atoms 23 | function toggleColumnOfAtoms (rackId, columnId, numRows) { 24 | var checkboxId; 25 | var toSet; 26 | toSet = null; 27 | for (var i=1; i \$op_help, 20 | 'port:i' => \$op_port, 21 | 'connect-timeout:i' => \$op_connect_timeout, 22 | 'timeout:i' => \$op_timeout, 23 | 'prompt-delay:f' => \$op_delay, 24 | 'prompt:s' => \$op_prompt, 25 | ); 26 | if ($op_help) { 27 | &display_help; 28 | exit; 29 | } 30 | my $op_host = $ARGV[0]; 31 | defined $op_host or die "ERROR: please specify remote host (-h for help)"; 32 | defined $op_prompt or die "ERROR: please specify prompt regexp (-h for help)"; 33 | my $prompt_re = qr/$op_prompt/; 34 | 35 | sub display_help { 36 | print <new ( 54 | Host => $op_host, 55 | Port => $port, 56 | Timeout => $op_connect_timeout, 57 | ); 58 | 59 | use IO::Select; 60 | my $sel = new IO::Select($session); 61 | 62 | my $buff = ''; 63 | my $nohang_read; 64 | until ($session->eof) { 65 | # read output from the device 66 | eval { 67 | $buff .= $session->get (Timeout => $nohang_read ? 0 : $op_timeout, Errmode => $nohang_read ? 'return' : 'die'); 68 | }; 69 | if ($@) { 70 | # check if there is something else in 71 | if (defined ) { 72 | die $@; 73 | } 74 | else { 75 | last; # no more input, seems like session was closed remotely by our last command 76 | } 77 | } 78 | $nohang_read = 0; 79 | print $1 if ($buff =~ s/(.*\n)//s); 80 | 81 | next unless ($buff =~ $prompt_re); 82 | # send pending commands to the device 83 | if ($op_delay and IO::Select->select ($sel, undef, undef, $op_delay)) { 84 | # something is received, no prompt detection at this time 85 | # set NOHANG options for next reading, cause it can be telnet control sequence 86 | $nohang_read = 1; 87 | } 88 | elsif (defined ($_ = )) { 89 | # replace all CR and LF symbols with single trailing LF 90 | s/[\015\012]//g; 91 | $session->put($_ . "\012"); 92 | } 93 | else { 94 | # no more commands in input 95 | last; 96 | } 97 | } 98 | print $buff; 99 | -------------------------------------------------------------------------------- /gateways/ucssdk: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python 2 | 3 | # This file is a part of RackTables, a datacenter and server room management 4 | # framework. See accompanying file "COPYING" for the full copyright and 5 | # licensing information. 6 | 7 | # This script provides an online stdin/stdout interface to the list of an UCS 8 | # domain components. This is done through Python UcsSdk interfacing with the UCS 9 | # manager (domain controller) instance, a virtual service provided by physical 10 | # UCS Fabric Interconnect switches. The list is output in a text format and is 11 | # eventually available as PHP array through queryDevice() RackTables function. 12 | # 13 | # Script stdin is a multiline text in the following format: 14 | # 15 | # login 16 | # getmo 17 | # 18 | # Script stdout is also a multiline text consisting of OK/ERR lines optionally 19 | # preceded with other content. In the case of enumeration the content is a 20 | # series of COLUMNS and ROW lines with CSV data. 21 | 22 | import sys 23 | try: 24 | from UcsSdk import * 25 | handle = UcsHandle() 26 | except Exception, err: 27 | sys.stderr.write('UCS Python SDK is missing\n') 28 | sys.exit(2) 29 | 30 | loggedin = 0 31 | for line in sys.stdin: 32 | words = line.split() 33 | if len(words) == 0: 34 | continue 35 | elif len(words) >= 1 and words[0] == "help": 36 | print "HELP: login " 37 | print "HELP: Immediately try to log into specified UCS manager instance with given credentials." 38 | print "HELP: getmo" 39 | print "HELP: Retrieve lists of managed objects and output as a set of tables (requires active login)." 40 | print "OK" 41 | # endif "help" 42 | elif len(words) == 4 and words[0] == "login": 43 | try: 44 | if loggedin == 1: 45 | print "INFO Closing previous connection..." 46 | handle.Logout() 47 | if (handle.Login(words[1], words[2], words[3]) == False): 48 | loggedin = 0 49 | print "ERR could not log into " + words[1] 50 | sys.exit(0) 51 | loggedin = 1 52 | print "OK logged into " + words[1] 53 | except Exception, err: 54 | loggedin = 0 55 | print "ERR could not log into " + words[1] 56 | # endif "login" 57 | elif len(words) == 1 and words[0] == "getmo": 58 | if (loggedin != 1): 59 | print "ERR not logged in" 60 | else: 61 | try: 62 | print "COLUMNS type,serial,DN,model,OOB" 63 | for mo in handle.GetManagedObject(None, NetworkElement.ClassId()): 64 | print "ROW NetworkElement," + mo.Serial + "," + mo.Dn + "," + mo.Model + "," + mo.OobIfIp 65 | print "COLUMNS type,serial,DN,model" 66 | for ch in handle.GetManagedObject(None, EquipmentChassis.ClassId()): 67 | print "ROW EquipmentChassis," + ch.Serial + "," + ch.Dn + "," + ch.Model 68 | print "COLUMNS type,serial,DN,model,assigned,slot" 69 | for bl in handle.GetManagedObject(None, ComputeBlade.ClassId()): 70 | print "ROW ComputeBlade," + bl.Serial + "," + bl.Dn + "," + bl.Model + "," + bl.AssignedToDn + "," + bl.SlotId 71 | print "OK enumeration complete" 72 | except Exception, err: 73 | print "ERR exception occured, logging out" 74 | loggedin = 0 75 | # endif "getmo" 76 | else: 77 | print "ERR command not supported (type \"help\" for help)" 78 | # endfor 79 | sys.exit(0) 80 | 81 | -------------------------------------------------------------------------------- /wwwroot/inc/pre-init.php: -------------------------------------------------------------------------------- 1 | PDO::ERRMODE_EXCEPTION, 61 | PDO::MYSQL_ATTR_INIT_COMMAND => 'set names "utf8"', 62 | ); 63 | if (isset ($pdo_bufsize)) 64 | $drvoptions[PDO::MYSQL_ATTR_MAX_BUFFER_SIZE] = $pdo_bufsize; 65 | try 66 | { 67 | $dbxlink = new PDO ($pdo_dsn, $db_username, $db_password, $drvoptions); 68 | } 69 | catch (PDOException $e) 70 | { 71 | throw new RackTablesError ("Database connection failed:\n\n" . $e->getMessage(), RackTablesError::INTERNAL); 72 | } 73 | } 74 | 75 | // tries to guess the existance of the file before the php's include using the same searching method. 76 | // in addition to calling file_exists, searches the current file's directory if the path is not looks 77 | // like neither absolute nor relative. 78 | function fileSearchExists ($filename) 79 | { 80 | if (! preg_match ('@^(\.+)?/@', $filename)) 81 | { 82 | $this_file_dir = dirname (__FILE__); 83 | if (file_exists ($this_file_dir . '/' . $filename)) 84 | return TRUE; 85 | } 86 | return file_exists ($filename); 87 | } 88 | 89 | ?> 90 | -------------------------------------------------------------------------------- /wwwroot/inc/caching.php: -------------------------------------------------------------------------------- 1 | = $creation_ts && // not modified since 29 | (! $expire || $client_time + $expire >= $server_time) // expiration timeout is not set, or not expired 30 | ); 31 | $last_modified = $creation_ts > 0 ? $creation_ts : ($client_time > 0 ? $client_time : $server_time); 32 | 33 | header ("Cache-Control: private, max-age=$expire, pre-check=$expire"); 34 | if ($result) 35 | header ('Last-Modified: ' . gmdate (DATE_RFC1123, $last_modified), TRUE, 304); 36 | else 37 | header ('Last-Modified: ' . gmdate (DATE_RFC1123, $last_modified)); 38 | return $result; 39 | } 40 | 41 | function HTTPDateToUnixTime ($string) 42 | { 43 | //Written per RFC 2616 3.3.1 - Full Date 44 | //http://www.w3.org/Protocols/rfc2616/rfc2616-sec3.html 45 | $month_number = array 46 | ( 47 | 'Jan' => 1, 48 | 'Feb' => 2, 49 | 'Mar' => 3, 50 | 'Apr' => 4, 51 | 'May' => 5, 52 | 'Jun' => 6, 53 | 'Jul' => 7, 54 | 'Aug' => 8, 55 | 'Sep' => 9, 56 | 'Oct' => 10, 57 | 'Nov' => 11, 58 | 'Dec' => 12, 59 | ); 60 | 61 | $formats = array(); 62 | # RFC2616 dictates exchanged timestamps to be in GMT TZ, and RFC822 63 | # (which RFC1123 relies on) explicitly defines, that "GMT" is equivalent 64 | # to "-0000" and "+0000". 65 | $formats['rfc1123'] = '/^(Sun|Mon|Tue|Wed|Thu|Fri|Sat), (\d{2}) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\d{4}) (\d{2}):(\d{2}):(\d{2}) (?:GMT|[-+]0000)$/'; 66 | $formats['rfc850'] = '/^(Sunday|Monday|Tuesday|Wednesday|Thursday|Friday|Saturday), (\d{2})-(Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)-(\d{2}) (\d{2}):(\d{2}):(\d{2}) (?:GMT|[-+]0000)$/'; 67 | $formats['asctime'] = '/^(Sun|Mon|Tue|Wed|Thu|Fri|Sat) (Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec) (\d{2}|\d{1}) (\d{2}):(\d{2}):(\d{2}) (\d{4})$/'; 68 | 69 | $matches = array(); 70 | if (preg_match ($formats['rfc1123'], $string, $matches)) 71 | { 72 | $hours = $matches[5]; 73 | $minutes = $matches[6]; 74 | $seconds = $matches[7]; 75 | $month = $month_number[$matches[3]]; 76 | $day = $matches[2]; 77 | $year = $matches[4]; 78 | } 79 | elseif (preg_match ($formats['rfc850'], $string, $matches)) 80 | { 81 | $hours = $matches[5]; 82 | $minutes = $matches[6]; 83 | $seconds = $matches[7]; 84 | $month = $month_number[substr($matches[3],0,3)]; 85 | $day = $matches[2]; 86 | $year = $matches[4]; 87 | } 88 | elseif (preg_match ($formats['asctime'], $string, $matches)) 89 | { 90 | $hours = $matches[4]; 91 | $minutes = $matches[5]; 92 | $seconds = $matches[6]; 93 | $month = $month_number[$matches[2]]; 94 | $day = $matches[3]; 95 | $year = $matches[7]; 96 | } 97 | else 98 | return false; 99 | return gmmktime ($hours, $minutes, $seconds, $month, $day, $year); 100 | } 101 | 102 | ?> 103 | -------------------------------------------------------------------------------- /wwwroot/js/tag-cb.js: -------------------------------------------------------------------------------- 1 | var tag_cb = new function () { // singletone class 2 | 3 | var self = this; 4 | this.enableNegation = function() { enableNegation = true; }; 5 | var enableNegation = false; 6 | 7 | // called from interface.php:renderCellFilterPortlet() if SHRINK_TAG_TREE_ON_CLICK is set 8 | this.enableSubmitOnClick = function() { enableSubmitOnClick = true; }; 9 | var enableSubmitOnClick = false; 10 | 11 | this.setTagShortList = function(list) { tagShortList = list; }; 12 | var tagShortList = {}; 13 | 14 | $(document).ready (function () { 15 | if (! enableNegation) 16 | return; 17 | $('.tag-cb').click(function (event) { 18 | cbClick(event, false); 19 | }); 20 | $('.tag-cb').closest('label') 21 | .mousedown(function(){return false}) 22 | .click(function (event) { 23 | cbClick(event, true); 24 | return false; 25 | }); 26 | $('input[name=andor]').click(function (event) { 27 | if ($('.tag-cb:checked').size() != 0 && event.target.value == 'and') { 28 | $(event.target).closest('form')[0].submit(); 29 | } 30 | }); 31 | }); 32 | 33 | // this is a handler for both checkbox click and label click 34 | // suppresses the default action, sets cb checked state and brings input focus on int 35 | // changes the name of checkbox if it is negated 36 | // submits form if this feature is enabled, and filter combination function is 'and' 37 | function cbClick (event, bInvert) { 38 | event.stopImmediatePropagation(); 39 | var td = $(event.target).closest('td'); 40 | var cb = td.find('.tag-cb'); 41 | if (cb.length != 1) 42 | return; 43 | var tagId = cb.attr('value'); 44 | var checked = cb.attr('checked'); 45 | if (bInvert) 46 | checked = ! checked; 47 | if (event.ctrlKey && ! td.hasClass('inverted') && checked) { 48 | var name = cb.attr('name'); 49 | if (name.match(/^cf([tp]\[\])$/)) 50 | cb.attr('name', 'nf' + RegExp.$1); 51 | td.addClass ('inverted'); 52 | } 53 | else { 54 | var name = cb.attr('name'); 55 | if (name.match(/^nf([tp]\[\])$/)) 56 | cb.attr('name', 'cf' + RegExp.$1); 57 | td.removeClass('inverted'); 58 | } 59 | if (bInvert) 60 | cb.attr('checked', checked ? 'checked' : ''); 61 | cb.focus(); 62 | 63 | if (enableSubmitOnClick) { 64 | var and_check = $('input[name=andor][value=and]')[0]; 65 | if (and_check.checked) 66 | $(event.target).closest('form')[0].submit(); 67 | } 68 | } 69 | 70 | this.compactTreeMode = function() { 71 | // reconfigure toggle link 72 | var link = $('a.toggleTreeMode')[0]; 73 | if ($(link).filter(':visible')) { 74 | $(link).after('

'); 75 | } 76 | link.onclick = function () {self.fullTreeMode(); return false;}; 77 | $(link).html('show full tree').show(); 78 | 79 | $('.tagtree').addClass('compact'); // disable hierachical padding 80 | 81 | var separator = false; // next visible row is separator 82 | var bPrevSeparator = true; // prev visible row was separator 83 | $('input.tag-cb').each(function (i, item) { 84 | var tr = $(item).closest('tr'); 85 | 86 | if ($(item).hasClass('root')) 87 | separator = true; 88 | 89 | if (! item.checked && ! tagShortList[item.value]) { 90 | tr.hide(); 91 | return; 92 | } 93 | 94 | if (separator && ! bPrevSeparator) { // do not draw two separators together or very first separator 95 | tr.addClass('separator'); 96 | bPrevSeparator = true; 97 | } 98 | else { 99 | tr.removeClass('separator'); 100 | bPrevSeparator = false; 101 | } 102 | separator = false; 103 | }); 104 | } 105 | 106 | this.fullTreeMode = function() { 107 | // reconfigure toggle link 108 | var link = $('a.toggleTreeMode')[0]; 109 | link.onclick = function () {self.compactTreeMode(); return false;}; 110 | $(link).html('show compact tree').show(); 111 | 112 | $('.tagtree').removeClass('compact'); // restore hierachical padding 113 | 114 | var bPrevSeparator = true; // prev visible row was separator 115 | $('input.tag-cb').each(function (i, item) { // // do not draw two separators together or very first separator 116 | var tr = $(item).closest('tr'); 117 | tr.removeClass('separator'); 118 | 119 | var separator = $(item).hasClass('root'); 120 | if (separator && ! bPrevSeparator) 121 | tr.addClass('separator'); 122 | bPrevSeparator = separator; 123 | 124 | tr.show(); 125 | }); 126 | } 127 | 128 | }; 129 | -------------------------------------------------------------------------------- /tests/GetChildrenListTest.php: -------------------------------------------------------------------------------- 1 | 1, 'dict_value' => 'unit test object type')); 26 | self::$objtype_id = lastInsertID (); 27 | commitSupplementOPC (self::$objtype_id, self::$objtype_id); 28 | self::$first_object_id = $parent_object_id = commitAddObject ('unit test object 0', NULL, self::$objtype_id, NULL); 29 | for ($i=1; $i<=self::$num_children; $i++) 30 | { 31 | $child_object_id = commitAddObject ("unit test object ${i}", NULL, self::$objtype_id, NULL); 32 | commitLinkEntities ('object', $parent_object_id, 'object', $child_object_id); 33 | $parent_object_id = $child_object_id; 34 | } 35 | self::$last_object_id = $parent_object_id; 36 | 37 | // add sample tags 38 | usePreparedInsertBlade ('TagTree', array ('tag' => 'unit test tag 0')); 39 | self::$first_tag_id = $parent_tag_id = lastInsertID (); 40 | for ($i=1; $i<=self::$num_children; $i++) 41 | { 42 | usePreparedInsertBlade ('TagTree', array ('parent_id' => $parent_tag_id, 'tag' => "unit test tag ${i}")); 43 | $parent_tag_id = lastInsertID (); 44 | } 45 | self::$last_tag_id = $parent_tag_id; 46 | } 47 | 48 | public static function tearDownAfterClass () 49 | { 50 | usePreparedExecuteBlade ('SET foreign_key_checks=0'); 51 | 52 | // remove sample locations 53 | usePreparedExecuteBlade 54 | ( 55 | 'DELETE FROM Object WHERE id BETWEEN ? AND ?', 56 | array (self::$first_location_id, self::$last_location_id) 57 | ); 58 | usePreparedExecuteBlade 59 | ( 60 | "DELETE FROM EntityLink WHERE parent_entity_type='location' AND child_entity_type='location' " . 61 | 'AND ((parent_entity_id BETWEEN ? AND ?) OR (child_entity_id BETWEEN ? AND ?))', 62 | array 63 | ( 64 | self::$first_location_id, self::$last_location_id, 65 | self::$first_location_id, self::$last_location_id 66 | ) 67 | ); 68 | 69 | // remove sample objects 70 | usePreparedExecuteBlade 71 | ( 72 | 'DELETE FROM Object WHERE id BETWEEN ? AND ?', 73 | array (self::$first_object_id, self::$last_object_id) 74 | ); 75 | usePreparedExecuteBlade 76 | ( 77 | "DELETE FROM EntityLink WHERE parent_entity_type='object' AND child_entity_type='object' " . 78 | 'AND ((parent_entity_id BETWEEN ? AND ?) OR (child_entity_id BETWEEN ? AND ?))', 79 | array 80 | ( 81 | self::$first_object_id, self::$last_object_id, 82 | self::$first_object_id, self::$last_object_id 83 | ) 84 | ); 85 | commitReduceOPC (self::$objtype_id, self::$objtype_id); 86 | usePreparedDeleteBlade ('Dictionary', array ('dict_key' => self::$objtype_id)); 87 | 88 | // remove sample tags 89 | usePreparedExecuteBlade 90 | ( 91 | 'DELETE FROM TagTree WHERE id BETWEEN ? AND ?', 92 | array (self::$first_tag_id, self::$last_tag_id) 93 | ); 94 | } 95 | 96 | public function testGetLocationChildrenList () 97 | { 98 | $children = getLocationChildrenList (self::$first_location_id); 99 | $this->assertCount (self::$num_children, $children); 100 | } 101 | 102 | public function testGetObjectChildrenList () 103 | { 104 | $children = getObjectContentsList (self::$first_object_id); 105 | $this->assertCount (self::$num_children, $children); 106 | } 107 | 108 | public function testGetTagChildrenList () 109 | { 110 | $children = getTagChildrenList (self::$first_tag_id); 111 | $this->assertCount (self::$num_children, $children); 112 | } 113 | } 114 | ?> 115 | -------------------------------------------------------------------------------- /wwwroot/js/inplace-edit.js: -------------------------------------------------------------------------------- 1 | (function ($) { //unnamed singletone class 2 | 3 | var editMode = false; 4 | var visible_pen = $(); 5 | var stop_propagation = false; 6 | var waiting_response = false; 7 | var editable_filter = 'span.editable'; 8 | 9 | var span = null; 10 | var group = null; 11 | var input = null; 12 | var btn = null; 13 | 14 | $(document).ready (function() { 15 | $(editable_filter).each (function (i, iSpan) { 16 | var container = $('

') 17 | .mouseover(onSpanMouseOver); 18 | $(iSpan) 19 | .after (container) 20 | .detach() 21 | .appendTo (container); 22 | container 23 | .append 24 | ( 25 | $('
') 26 | .click(onPencilClick) 27 | ); 28 | }); 29 | $('body').mouseover(onBodyMouseOver); 30 | }); 31 | 32 | function onSpanMouseOver (event) { 33 | if (editMode) 34 | return; 35 | stop_propagation = true; 36 | var pen = $(event.target).closest('.edit-container').find('.edit-btn'); 37 | if (! visible_pen.length || visible_pen[0] != pen[0]) { 38 | visible_pen.hide(); 39 | visible_pen = pen; 40 | visible_pen.show(); 41 | } 42 | } 43 | 44 | function onBodyMouseOver () { 45 | if (!editMode && !stop_propagation) { 46 | visible_pen.hide(); 47 | visible_pen = $(); 48 | } 49 | stop_propagation = false; 50 | } 51 | 52 | function hideEditForm () { 53 | input = null; 54 | btn = null; 55 | group.remove(); 56 | span.show(); 57 | editMode = false; 58 | }; 59 | 60 | function onPencilClick (event) { 61 | // hide original plain text comment 62 | span = $(event.target).closest('.edit-container').find(editable_filter); 63 | var width = span[0].offsetWidth + 50; 64 | if (width < 150) 65 | width = 150; 66 | span.hide(); 67 | 68 | // hide pencil 69 | onBodyMouseOver(); 70 | editMode = true; 71 | 72 | // construct editor form 73 | group = $(''); 74 | input = $('') 75 | .val(span.html().replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>')) 76 | .css('width', '' + width + 'px') 77 | .keydown( 78 | function (event) { 79 | var code = event.keyCode ? event.keyCode : event.which; 80 | if (code == 13) 81 | onFormSubmit(); 82 | else if (code == 27) 83 | hideEditForm(); 84 | }) 85 | .appendTo(group); 86 | group.append(' '); 87 | btn = $('') 88 | .css('cursor', 'pointer') 89 | .click( onFormSubmit ) 90 | .appendTo(group); 91 | span.after(group); 92 | input[0].focus(); 93 | doSetCaretPosition (input[0], input[0].value.length); 94 | 95 | group.click(function(event) { event.stopPropagation(); }); 96 | $('body').one('click', 97 | function (event) { 98 | if (! waiting_response) 99 | hideEditForm(); 100 | onSpanMouseOver(event); 101 | }); 102 | event.stopPropagation(); // prevent the initial click to immediately close edit form 103 | } 104 | 105 | function doSetCaretPosition (input, iCaretPos) { 106 | if (input.setSelectionRange) { 107 | input.setSelectionRange(iCaretPos, iCaretPos); 108 | } 109 | else if (input.createTextRange) { 110 | var range = input.createTextRange(); 111 | range.moveEnd("character", iCaretPos); 112 | range.moveStart("character", iCaretPos); 113 | range.select(); 114 | } 115 | } 116 | 117 | function onFormSubmit () { 118 | var text = input.val(); 119 | input.attr('disabled', 'true'); 120 | btn.replaceWith(''); 121 | waiting_response = true; 122 | 123 | var data = { 124 | 'module': 'ajax', 125 | 'text': text 126 | }; 127 | var list = span[0].className.split (/\s+/); 128 | for (var i in list) { 129 | var cn = list[i]; 130 | var m; 131 | if (m = cn.match (/^(.+?)-(.*)/)) { 132 | data[m[1]] = m[2]; 133 | } 134 | } 135 | if (! ('ac' in data)) 136 | data['ac'] = data['op']; 137 | 138 | $.ajax({ 139 | type: 'POST', 140 | url: 'index.php', 141 | data: data, 142 | success: function(data, textStatus, XMLHttpRequest) { 143 | if (data == 'OK') 144 | span.html(text); 145 | else 146 | alert ('Error updating port: ' + data); 147 | }, 148 | error: function(jqXHR, textStatus, errorThrown) { 149 | alert ('Error updating port: ' + textStatus); 150 | }, 151 | complete: function() { 152 | hideEditForm(); 153 | waiting_response = false; 154 | } 155 | }); 156 | } 157 | 158 | })(jQuery); 159 | -------------------------------------------------------------------------------- /gateways/sshnokey: -------------------------------------------------------------------------------- 1 | #!/usr/bin/perl 2 | # This file is a part of RackTables, a datacenter and server room management 3 | # framework. See accompanying file "COPYING" for the full copyright and 4 | # licensing information. 5 | # 6 | # NOTE: Logging in via ssh using a password, whilst better then doing 7 | # the same via telnet/netcat, is still a bad idea when your network is 8 | # not properly secured. If you try to login to a compromised system 9 | # using a password, this password is now in the hands of the attackers. 10 | # 11 | # If possible, use the public key authenticated version instead. In case 12 | # that is not an option, consider different passwords for every system. 13 | # 14 | # To summerise, treat this as a slighhtly-better telnet client and make 15 | # sure the network this runs in is secure. 16 | 17 | use strict; 18 | use Getopt::Long; 19 | use Net::Telnet; 20 | use Net::OpenSSH; 21 | 22 | # fetch command-line parameters 23 | my $op_help; 24 | my $op_port; 25 | my $op_username; 26 | my $op_password; 27 | my $op_connect_timeout = 2; 28 | my $op_timeout = 10; 29 | my $op_prompt; 30 | my $op_delay = 0.01; 31 | GetOptions ( 32 | 'h' => \$op_help, 33 | 'port:i' => \$op_port, 34 | 'connect-timeout:i' => \$op_connect_timeout, 35 | 'timeout:i' => \$op_timeout, 36 | 'prompt-delay:f' => \$op_delay, 37 | 'prompt:s' => \$op_prompt, 38 | 'username:s' => \$op_username, 39 | 'password:s' => \$op_password 40 | ); 41 | if ($op_help) { 42 | &display_help; 43 | exit; 44 | } 45 | my $op_host = $ARGV[0]; 46 | defined $op_host or die "ERROR: please specify remote host (-h for help)"; 47 | defined $op_prompt or die "ERROR: please specify prompt regexp (-h for help)"; 48 | my $prompt_re = qr/$op_prompt/; 49 | 50 | sub display_help { 51 | print <new( 70 | $op_host, 71 | 'port' => $op_port, 72 | 'user' => $op_username, 73 | 'password' => $op_password 74 | ); 75 | $ssh->error and 76 | die "Couldn't establish SSH connection: ". $ssh->error; 77 | 78 | my ($pty, $pid) = $ssh->open2pty({stderr_to_stdout => 1}) 79 | or die "unable to start remote shell: " . $ssh->error; 80 | 81 | my $session = Net::Telnet->new ( 82 | Fhopen => $pty, 83 | # Host => $op_host, 84 | # Port => $port, 85 | # Timeout => $op_connect_timeout, 86 | Prompt => "/$op_prompt/", 87 | Telnetmode => 0, 88 | Cmd_remove_mode => 1, 89 | Output_record_separator => "\r" 90 | ); 91 | 92 | #$session->cmd("term len 0"); 93 | 94 | 95 | use IO::Select; 96 | my $sel = new IO::Select($session); 97 | 98 | my $buff = ''; 99 | my $nohang_read; 100 | until ($session->eof) { 101 | # read output from the device 102 | eval { 103 | $buff .= $session->get (Timeout => $nohang_read ? 0 : $op_timeout, Errmode => $nohang_read ? 'return' : 'die'); 104 | }; 105 | if ($@) { 106 | # check if there is something else in 107 | if (defined ) { 108 | die $@; 109 | } 110 | else { 111 | last; # no more input, seems like session was closed remotely by our last command 112 | } 113 | } 114 | $nohang_read = 0; 115 | print $1 if ($buff =~ s/(.*\n)//s); 116 | 117 | next unless ($buff =~ $prompt_re); 118 | # send pending commands to the device 119 | if ($op_delay and IO::Select->select ($sel, undef, undef, $op_delay)) { 120 | # something is received, no prompt detection at this time 121 | # set NOHANG options for next reading, cause it can be telnet control sequence 122 | $nohang_read = 1; 123 | } 124 | elsif (defined ($_ = )) { 125 | # replace all CR and LF symbols with single trailing LF 126 | s/[\015\012]//g; 127 | $session->put($_ . "\012"); 128 | } 129 | else { 130 | # no more commands in input 131 | last; 132 | } 133 | } 134 | print $buff; 135 | -------------------------------------------------------------------------------- /tests/LinkTriggerTest.php: -------------------------------------------------------------------------------- 1 | fetchColumn (); 29 | 30 | // add sample data 31 | // - set port a & b's type to 1000Base-T 32 | // - set port c's type to the incompatible one 33 | self::$object_id = commitAddObject ('unit test object', NULL, 4, NULL); 34 | self::$porta = commitAddPort (self::$object_id, 'test porta', '1-24', NULL, NULL); 35 | self::$portb = commitAddPort (self::$object_id, 'test portb', '1-24', NULL, NULL); 36 | self::$portc = commitAddPort (self::$object_id, 'test portc', self::$portc_type, NULL, NULL); 37 | } 38 | 39 | public static function tearDownAfterClass () 40 | { 41 | // restore AUTOPORTS_CONFIG to original setting 42 | if (self::$autoports_config_var != '') 43 | setConfigVar ('AUTOPORTS_CONFIG', self::$autoports_config_var); 44 | 45 | // remove sample data 46 | commitDeleteObject (self::$object_id); 47 | } 48 | 49 | public function tearDown () 50 | { 51 | // delete any links created during the test 52 | usePreparedExecuteBlade 53 | ( 54 | 'DELETE FROM Link WHERE porta IN (?,?,?) OR portb IN (?,?,?)', 55 | array (self::$porta, self::$portb, self::$portc, self::$porta, self::$portb, self::$portc) 56 | ); 57 | } 58 | 59 | /** 60 | * @expectedException PDOException 61 | */ 62 | public function testCreateLinkToSelf () 63 | { 64 | usePreparedInsertBlade 65 | ( 66 | 'Link', 67 | array ('porta' => self::$porta, 'portb' => self::$porta) 68 | ); 69 | } 70 | 71 | /** 72 | * @expectedException PDOException 73 | */ 74 | public function testUpdateLinkToSelf () 75 | { 76 | usePreparedInsertBlade 77 | ( 78 | 'Link', 79 | array ('porta' => self::$porta, 'portb' => self::$portb) 80 | ); 81 | usePreparedUpdateBlade 82 | ( 83 | 'Link', 84 | array ('porta' => self::$porta, 'portb' => self::$porta), 85 | array ('porta' => self::$porta, 'portb' => self::$portb) 86 | ); 87 | } 88 | 89 | public function testCreateLinkWithPortAGreaterThanPortB () 90 | { 91 | usePreparedInsertBlade 92 | ( 93 | 'Link', 94 | array ('porta' => self::$portb, 'portb' => self::$porta) 95 | ); 96 | $result = usePreparedSelectBlade 97 | ( 98 | 'SELECT COUNT(*) FROM Link WHERE porta=? AND portb=?', 99 | array (self::$porta, self::$portb) 100 | ); 101 | $this->assertEquals ($result->fetchColumn (), 1); 102 | } 103 | 104 | public function testUpdateLinkWithPortAGreaterThanPortB () 105 | { 106 | usePreparedInsertBlade 107 | ( 108 | 'Link', 109 | array ('porta' => self::$porta, 'portb' => self::$portb) 110 | ); 111 | usePreparedUpdateBlade 112 | ( 113 | 'Link', 114 | array ('porta' => self::$portb, 'portb' => self::$porta), 115 | array ('porta' => self::$porta, 'portb' => self::$portb) 116 | ); 117 | $result = usePreparedSelectBlade 118 | ( 119 | 'SELECT COUNT(*) FROM Link WHERE porta=? AND portb=?', 120 | array (self::$porta, self::$portb) 121 | ); 122 | $this->assertEquals ($result->fetchColumn (), 1); 123 | } 124 | 125 | /** 126 | * @expectedException PDOException 127 | */ 128 | public function testCreateLinkBetweenIncompatiblePorts () 129 | { 130 | usePreparedInsertBlade 131 | ( 132 | 'Link', 133 | array ('porta' => self::$porta, 'portb' => self::$portc) 134 | ); 135 | } 136 | 137 | /** 138 | * @expectedException PDOException 139 | */ 140 | public function testUpdateLinkBetweenIncompatiblePorts () 141 | { 142 | usePreparedInsertBlade 143 | ( 144 | 'Link', 145 | array ('porta' => self::$porta, 'portb' => self::$portb) 146 | ); 147 | usePreparedUpdateBlade 148 | ( 149 | 'Link', 150 | array ('porta' => self::$porta, 'portb' => self::$portc), 151 | array ('porta' => self::$porta, 'portb' => self::$portb) 152 | ); 153 | } 154 | } 155 | ?> 156 | -------------------------------------------------------------------------------- /wwwroot/js/jquery.thumbhover.js: -------------------------------------------------------------------------------- 1 | // This library is based on jquery.thumbhover.js from unknown author. 2 | // Far and away modified for using in RackTables. 3 | // Usage: jquery_object_a.thumbPopup(jquery_object_b, [options]) 4 | // after that call, object_b will be hidden 5 | // object_a, when hovered, will show object_b floating and moving by the cursor. 6 | // When clicked on object_a, object_b will stay visible until outside click 7 | (function($) { 8 | $.fn.thumbPopup = function(popup, options) 9 | { 10 | //Combine the passed in options with the default settings 11 | var settings = jQuery.extend({ 12 | //popupId: "thumbPopup", 13 | cursorTopOffset: 15, 14 | cursorLeftOffset: 15, 15 | event: null, // if set, the popup will be displayed immediately, and no mouse handlers will be binded to target 16 | showFreezeHint: true 17 | }, options); 18 | 19 | var freezeHint = null; 20 | var timeout_id = null; 21 | var hintHeight = 0; 22 | if (! freezeHint && settings.showFreezeHint && settings.event == null) { 23 | freezeHint = $('
').html('click to freeze').css('position', 'absolute').css('border', '1px solid black').css('background-color', 'white').appendTo('body'); 24 | hintHeight = freezeHint[0].offsetHeight; 25 | freezeHint.hide(); 26 | } 27 | 28 | if (! popup.length) 29 | return false; 30 | popup.css("position", "absolute").hide(); 31 | $(this).css("cursor", "pointer"); 32 | 33 | //Attach hover events that manage the popup 34 | if (settings.event != null) 35 | { 36 | setPopup (settings.event); 37 | stickPopup (settings.event); 38 | } 39 | else { 40 | $(this) 41 | .bind('mouseenter', setPopup) 42 | .bind('mousemove', updatePopupPosition) 43 | .bind('mouseleave', hidePopup) 44 | .click(stickPopup); 45 | } 46 | 47 | function stickPopup(event) { 48 | popup.data("sticked", true); 49 | if (freezeHint) 50 | freezeHint.hide(); 51 | $('body').bind('click', function(event) { 52 | if (! (event.target == popup[0] || $(event.target).parents().filter(popup).length)) 53 | { 54 | popup.data("sticked", false); 55 | hidePopup (event); 56 | } 57 | return true; 58 | }); 59 | return false; 60 | } 61 | 62 | function ShowFreezeHint() { 63 | freezeHint.show(); 64 | } 65 | 66 | function setPopup(event) { 67 | if (popup.data("sticked")) 68 | return; 69 | popup.data("hovered", true); 70 | updatePopupPosition(event); 71 | $(popup).show(); 72 | } 73 | 74 | function updatePopupPosition(event) { 75 | var windowSize = getWindowSize(); 76 | var popupSize = getPopupSize(); 77 | var bPopupShownAbove = windowSize.height + windowSize.scrollTop < event.pageY + popupSize.height + settings.cursorTopOffset; 78 | if (freezeHint) { 79 | var deltaHint = bPopupShownAbove ? 20 : - 5 - hintHeight; 80 | freezeHint.hide(); 81 | freezeHint.css('top', event.pageY + deltaHint).css('left', event.pageX); 82 | clearTimeout(timeout_id); 83 | timeout_id = setTimeout(ShowFreezeHint, 1000); 84 | } 85 | if (popup.data("sticked")) 86 | return; 87 | var left; 88 | if (windowSize.width + windowSize.scrollLeft < event.pageX + popupSize.width + settings.cursorLeftOffset){ 89 | left = event.pageX - popupSize.width - settings.cursorLeftOffset; 90 | } else { 91 | left = event.pageX + settings.cursorLeftOffset; 92 | } 93 | // center pop-up if it does not fit entirely into window 94 | if ( 95 | left < windowSize.scrollLeft || 96 | left + popupSize.width > windowSize.scrollLeft + windowSize.width 97 | ) { 98 | left = (windowSize.width - popupSize.width) / 2; 99 | } 100 | $(popup).css("left", left); 101 | 102 | if (bPopupShownAbove) { 103 | $(popup).css("top", event.pageY - popupSize.height - settings.cursorTopOffset); 104 | } else { 105 | $(popup).css("top", event.pageY + settings.cursorTopOffset); 106 | } 107 | } 108 | 109 | function hidePopup(event) { 110 | popup.data("hovered", false); 111 | if (freezeHint) { 112 | freezeHint.hide(); 113 | clearTimeout(timeout_id); 114 | } 115 | if (popup.data("sticked")) 116 | return; 117 | $(popup).hide(); 118 | if (settings.event != null) 119 | $(popup).remove(); 120 | } 121 | 122 | function getWindowSize() { 123 | return { 124 | scrollLeft: $(window).scrollLeft(), 125 | scrollTop: $(window).scrollTop(), 126 | width: $(window).width(), 127 | height: $(window).height() 128 | }; 129 | } 130 | 131 | function getPopupSize() { 132 | return { 133 | width: $(popup).width(), 134 | height: $(popup).height() 135 | }; 136 | } 137 | 138 | //Return original selection for chaining 139 | return this; 140 | }; 141 | })(jQuery); 142 | -------------------------------------------------------------------------------- /wwwroot/js/codemirror/htmlmixed.js: -------------------------------------------------------------------------------- 1 | CodeMirror.defineMode("htmlmixed", function(config, parserConfig) { 2 | var htmlMode = CodeMirror.getMode(config, {name: "xml", 3 | htmlMode: true, 4 | multilineTagIndentFactor: parserConfig.multilineTagIndentFactor, 5 | multilineTagIndentPastTag: parserConfig.multilineTagIndentPastTag}); 6 | var cssMode = CodeMirror.getMode(config, "css"); 7 | 8 | var scriptTypes = [], scriptTypesConf = parserConfig && parserConfig.scriptTypes; 9 | scriptTypes.push({matches: /^(?:text|application)\/(?:x-)?(?:java|ecma)script$|^$/i, 10 | mode: CodeMirror.getMode(config, "javascript")}); 11 | if (scriptTypesConf) for (var i = 0; i < scriptTypesConf.length; ++i) { 12 | var conf = scriptTypesConf[i]; 13 | scriptTypes.push({matches: conf.matches, mode: conf.mode && CodeMirror.getMode(config, conf.mode)}); 14 | } 15 | scriptTypes.push({matches: /./, 16 | mode: CodeMirror.getMode(config, "text/plain")}); 17 | 18 | function html(stream, state) { 19 | var tagName = state.htmlState.tagName; 20 | var style = htmlMode.token(stream, state.htmlState); 21 | if (tagName == "script" && /\btag\b/.test(style) && stream.current() == ">") { 22 | // Script block: mode to change to depends on type attribute 23 | var scriptType = stream.string.slice(Math.max(0, stream.pos - 100), stream.pos).match(/\btype\s*=\s*("[^"]+"|'[^']+'|\S+)[^<]*$/i); 24 | scriptType = scriptType ? scriptType[1] : ""; 25 | if (scriptType && /[\"\']/.test(scriptType.charAt(0))) scriptType = scriptType.slice(1, scriptType.length - 1); 26 | for (var i = 0; i < scriptTypes.length; ++i) { 27 | var tp = scriptTypes[i]; 28 | if (typeof tp.matches == "string" ? scriptType == tp.matches : tp.matches.test(scriptType)) { 29 | if (tp.mode) { 30 | state.token = script; 31 | state.localMode = tp.mode; 32 | state.localState = tp.mode.startState && tp.mode.startState(htmlMode.indent(state.htmlState, "")); 33 | } 34 | break; 35 | } 36 | } 37 | } else if (tagName == "style" && /\btag\b/.test(style) && stream.current() == ">") { 38 | state.token = css; 39 | state.localMode = cssMode; 40 | state.localState = cssMode.startState(htmlMode.indent(state.htmlState, "")); 41 | } 42 | return style; 43 | } 44 | function maybeBackup(stream, pat, style) { 45 | var cur = stream.current(); 46 | var close = cur.search(pat), m; 47 | if (close > -1) stream.backUp(cur.length - close); 48 | else if (m = cur.match(/<\/?$/)) { 49 | stream.backUp(cur.length); 50 | if (!stream.match(pat, false)) stream.match(cur); 51 | } 52 | return style; 53 | } 54 | function script(stream, state) { 55 | if (stream.match(/^<\/\s*script\s*>/i, false)) { 56 | state.token = html; 57 | state.localState = state.localMode = null; 58 | return html(stream, state); 59 | } 60 | return maybeBackup(stream, /<\/\s*script\s*>/, 61 | state.localMode.token(stream, state.localState)); 62 | } 63 | function css(stream, state) { 64 | if (stream.match(/^<\/\s*style\s*>/i, false)) { 65 | state.token = html; 66 | state.localState = state.localMode = null; 67 | return html(stream, state); 68 | } 69 | return maybeBackup(stream, /<\/\s*style\s*>/, 70 | cssMode.token(stream, state.localState)); 71 | } 72 | 73 | return { 74 | startState: function() { 75 | var state = htmlMode.startState(); 76 | return {token: html, localMode: null, localState: null, htmlState: state}; 77 | }, 78 | 79 | copyState: function(state) { 80 | if (state.localState) 81 | var local = CodeMirror.copyState(state.localMode, state.localState); 82 | return {token: state.token, localMode: state.localMode, localState: local, 83 | htmlState: CodeMirror.copyState(htmlMode, state.htmlState)}; 84 | }, 85 | 86 | token: function(stream, state) { 87 | return state.token(stream, state); 88 | }, 89 | 90 | indent: function(state, textAfter) { 91 | if (!state.localMode || /^\s*<\//.test(textAfter)) 92 | return htmlMode.indent(state.htmlState, textAfter); 93 | else if (state.localMode.indent) 94 | return state.localMode.indent(state.localState, textAfter); 95 | else 96 | return CodeMirror.Pass; 97 | }, 98 | 99 | innerMode: function(state) { 100 | return {state: state.localState || state.htmlState, mode: state.localMode || htmlMode}; 101 | } 102 | }; 103 | }, "xml", "javascript", "css"); 104 | 105 | CodeMirror.defineMIME("text/html", "htmlmixed"); 106 | -------------------------------------------------------------------------------- /tests/ObjectCircularReferenceTest.php: -------------------------------------------------------------------------------- 1 | 1, 'dict_value' => 'unit test object type')); 16 | self::$objtype_id = lastInsertID (); 17 | commitSupplementOPC (self::$objtype_id, self::$objtype_id); 18 | self::$objecta_id = commitAddObject ('unit test object a', NULL, self::$objtype_id, NULL); 19 | self::$objectb_id = commitAddObject ('unit test object b', NULL, self::$objtype_id, NULL); 20 | self::$objectc_id = commitAddObject ('unit test object c', NULL, self::$objtype_id, NULL); 21 | self::$locationa_id = commitAddObject ('unit test location a', NULL, 1562, NULL); 22 | self::$locationb_id = commitAddObject ('unit test location b', NULL, 1562, NULL); 23 | self::$locationc_id = commitAddObject ('unit test location c', NULL, 1562, NULL); 24 | } 25 | 26 | public static function tearDownAfterClass () 27 | { 28 | // remove sample data 29 | commitDeleteObject (self::$objecta_id); 30 | commitDeleteObject (self::$objectb_id); 31 | commitDeleteObject (self::$objectc_id); 32 | commitReduceOPC (self::$objtype_id, self::$objtype_id); 33 | usePreparedDeleteBlade ('Dictionary', array ('dict_key' => self::$objtype_id)); 34 | commitDeleteObject (self::$locationa_id); 35 | commitDeleteObject (self::$locationb_id); 36 | commitDeleteObject (self::$locationc_id); 37 | } 38 | 39 | public function tearDown () 40 | { 41 | // delete any links created during the test 42 | usePreparedExecuteBlade 43 | ( 44 | "DELETE FROM EntityLink WHERE parent_entity_type='location' AND child_entity_type='location' " . 45 | 'AND (parent_entity_id IN (?,?,?) OR child_entity_id IN (?,?,?))', 46 | array 47 | ( 48 | self::$locationa_id, self::$locationb_id, self::$locationc_id, 49 | self::$locationa_id, self::$locationb_id, self::$locationc_id 50 | ) 51 | ); 52 | usePreparedExecuteBlade 53 | ( 54 | "DELETE FROM EntityLink WHERE parent_entity_type='object' AND child_entity_type='object' " . 55 | 'AND (parent_entity_id IN (?,?,?) OR child_entity_id IN (?,?,?))', 56 | array 57 | ( 58 | self::$objecta_id, self::$objectb_id, self::$objectc_id, 59 | self::$objecta_id, self::$objectb_id, self::$objectc_id 60 | ) 61 | ); 62 | } 63 | 64 | /** 65 | * @expectedException RackTablesError 66 | */ 67 | public function testCreateObjectCircularReference () 68 | { 69 | // set A as the parent of B, and B as the parent of C 70 | commitLinkEntities ('object', self::$objecta_id, 'object', self::$objectb_id); 71 | commitLinkEntities ('object', self::$objectb_id, 'object', self::$objectc_id); 72 | // setting C as the parent of A should fail 73 | commitLinkEntities ('object', self::$objectc_id, 'object', self::$objecta_id); 74 | } 75 | 76 | /** 77 | * @expectedException RackTablesError 78 | */ 79 | public function testUpdateObjectCircularReference () 80 | { 81 | // set A as the parent of B, and B as the parent of C 82 | commitLinkEntities ('object', self::$objecta_id, 'object', self::$objectb_id); 83 | commitLinkEntities ('object', self::$objectb_id, 'object', self::$objectc_id); 84 | // reversing the link between B and C should fail 85 | commitUpdateEntityLink 86 | ( 87 | 'object', self::$objectb_id, 'object', self::$objectc_id, 88 | 'object', self::$objectc_id, 'object', self::$objectb_id 89 | ); 90 | } 91 | 92 | /** 93 | * @expectedException RackTablesError 94 | */ 95 | public function testCreateLocationCircularReference () 96 | { 97 | // set A as the parent of B, and B as the parent of C 98 | commitLinkEntities ('location', self::$locationa_id, 'location', self::$locationb_id); 99 | commitLinkEntities ('location', self::$locationb_id, 'location', self::$locationc_id); 100 | // setting C as the parent of A should fail 101 | commitLinkEntities ('location', self::$locationc_id, 'location', self::$locationa_id); 102 | } 103 | 104 | /** 105 | * @expectedException RackTablesError 106 | */ 107 | public function testUpdateLocationCircularReference () 108 | { 109 | // set A as the parent of B, and B as the parent of C 110 | commitLinkEntities ('location', self::$locationa_id, 'location', self::$locationb_id); 111 | commitLinkEntities ('location', self::$locationb_id, 'location', self::$locationc_id); 112 | // reversing the link between B and C should fail 113 | commitUpdateEntityLink 114 | ( 115 | 'location', self::$locationb_id, 'location', self::$locationc_id, 116 | 'location', self::$locationc_id, 'location', self::$locationb_id 117 | ); 118 | } 119 | } 120 | ?> 121 | -------------------------------------------------------------------------------- /wwwroot/js/live_validation.js: -------------------------------------------------------------------------------- 1 | // This file has been downloaded from the following URL: 2 | // http://www.openjs.com/scripts/forms/live_validation/ 3 | // (Live Validator 1.00.A Beta) 4 | // It was declared as released under BSD license at that moment. 5 | // The content below is a verbatim copy. 6 | 7 | var Validate = { 8 | elements:[], 9 | //Some General purpose functions 10 | hasClass:function(ele,cls) { 11 | return ele.className.match(new RegExp('(\\s|^)'+cls+'(\\s|$)')); 12 | }, 13 | addClass:function (ele,cls) { 14 | if (!this.hasClass(ele,cls)) ele.className += " "+cls; 15 | }, 16 | removeClass:function (ele,cls) { 17 | if (this.hasClass(ele,cls)) { 18 | var reg = new RegExp('(\\s|^)'+cls+'(\\s|$)'); 19 | ele.className=ele.className.replace(reg,' '); 20 | } 21 | }, 22 | stopEvent:function(e) { 23 | //e.cancelBubble is supported by IE - this will kill the bubbling process. 24 | e.cancelBubble = true; 25 | e.returnValue = false; 26 | //e.stopPropagation works in Mozilla. 27 | if (e.stopPropagation) { 28 | e.stopPropagation(); 29 | e.preventDefault(); 30 | } 31 | }, 32 | success:function(ele) { 33 | this.removeClass(ele,"validation-error"); 34 | this.addClass(ele,"validation-success"); 35 | }, 36 | failure:function(ele) { 37 | this.removeClass(ele,"validation-success"); 38 | this.addClass(ele,"validation-error"); 39 | }, 40 | //Logic starts here... 41 | //Deny keyboard input for some specific chars. 42 | validateKeys : function(e,ele) { 43 | //Find Which key is pressed 44 | if (e.keyCode) code = e.keyCode; 45 | else if (e.which) code = e.which; 46 | var character = String.fromCharCode(code); 47 | 48 | //We need only valid keypresses - not stuff like shift, Enter, Backspace etc. 49 | if( e.ctrlKey || 50 | code == 46 //Delete Key 51 | ) return; 52 | if(e.shiftKey) { 53 | if(!(code >= 37 && code <= 40)) return; //Up,Down,Right and Left - same keycode as %,&,' and ( 54 | } else { 55 | if(!(code >= 41 && code <= 126)) return; 56 | } 57 | 58 | if(ele.getAttribute("allowedkeys")) { 59 | var allowed_chars = new RegExp(ele.getAttribute("allowedkeys")); 60 | if(!allowed_chars.test(character)) { //If a character was entered that is not allowed. 61 | this.stopEvent(e); 62 | } 63 | } 64 | }, 65 | //See if the match is made - this is called on every keyup event. 66 | testMatch : function(ele) { 67 | if(ele.getAttribute("match")) { 68 | var match_reg = new RegExp(ele.getAttribute("match")); 69 | if(match_reg.test(ele.value)) this.success(ele); 70 | else { 71 | this.failure(ele); 72 | return false;//Don't continue if it is a faluire 73 | } 74 | } 75 | if(ele.getAttribute("equals")) { 76 | if(ele.value == eval(ele.getAttribute("equals"))) this.success(ele);//Yes I know, 'evil eval'. 77 | else { 78 | this.failure(ele); 79 | return false; 80 | } 81 | } 82 | if(ele.getAttribute("istrue")) { 83 | if(eval(ele.getAttribute("istrue"))) this.success(ele);//Again, eval. 84 | else { 85 | this.failure(ele); 86 | return false; 87 | } 88 | } 89 | return true; 90 | }, 91 | //Show error message on form submission 92 | attachForm : function(form_id) { 93 | var ths = this;//Closure 94 | $(form_id).onsubmit=function(e) { 95 | if(!e) var e = window.event; 96 | var ele = this; 97 | var err = 0; 98 | for(var i=0,len=ths.elements.length; i${path_to_secret_php}).
" . 35 | "The configuration file is usually generated by RackTables installer, which " . 36 | "can be launched here.", 37 | RackTablesError::MISCONFIGURED 38 | ); 39 | 40 | connectDB(); 41 | transformRequestData(); 42 | $configCache = loadConfigDefaults(); 43 | $tab['reports']['local'] = getConfigVar ('enterprise'); 44 | 45 | if (getConfigVar ('DB_VERSION') != CODE_VERSION) 46 | { 47 | echo '

This Racktables installation seems to be ' . 48 | 'just upgraded to version ' . CODE_VERSION . ', while the '. 49 | 'database version is ' . getConfigVar ('DB_VERSION') . '.
No user will be ' . 50 | 'either authenticated or shown any page until the upgrade is ' . 51 | "finished.
Follow this link and " . 52 | 'authenticate as administrator to finish the upgrade.

'; 53 | exit (1); 54 | } 55 | 56 | if (!mb_internal_encoding ('UTF-8')) 57 | throw new RackTablesError ('Failed setting multibyte string encoding to UTF-8', RackTablesError::INTERNAL); 58 | 59 | $rackCodeCache = loadScript ('RackCodeCache'); 60 | if ($rackCodeCache == NULL or !strlen ($rackCodeCache)) 61 | { 62 | $rackCode = getRackCode (loadScript ('RackCode')); 63 | saveScript ('RackCodeCache', base64_encode (serialize ($rackCode))); 64 | } 65 | else 66 | { 67 | $rackCode = unserialize (base64_decode ($rackCodeCache)); 68 | if ($rackCode === FALSE) // invalid cache 69 | { 70 | saveScript ('RackCodeCache', ''); 71 | $rackCode = getRackCode (loadScript ('RackCode')); 72 | } 73 | } 74 | 75 | // avoid notices being thrown 76 | date_default_timezone_set (getConfigVar ('DATETIME_ZONE')); 77 | 78 | // Depending on the 'result' value the 'load' carries either the 79 | // parse tree or error message. The latter case is a bug, because 80 | // RackCode saving function was supposed to validate its input. 81 | if ($rackCode['result'] != 'ACK') 82 | throw new RackTablesError ($rackCode['load'], RackTablesError::INTERNAL); 83 | $rackCode = $rackCode['load']; 84 | // Only call buildPredicateTable() once and save the result, because it will remain 85 | // constant during one execution for constraints processing. 86 | $pTable = buildPredicateTable ($rackCode); 87 | // Constraints parse trees aren't cached in the database, so the least to keep 88 | // things running is to maintain application cache for them. 89 | $entityCache = array(); 90 | // used by getExplicitTagsOnly() 91 | $tagRelCache = array(); 92 | 93 | $taglist = getTagList(); 94 | $tagtree = treeFromList ($taglist); 95 | 96 | $auto_tags = array(); 97 | if (! isCLIMode() && isset ($_SERVER['REMOTE_ADDR'])) 98 | $auto_tags[] = array ('tag' => '$client_' . $_SERVER['REMOTE_ADDR']); 99 | 100 | // Initial chain for the current user. 101 | $user_given_tags = array(); 102 | 103 | // list of regexps used in findAutoTagWarnings to check RackCode. 104 | // add your regexps here to suppress 'Martian autotag' warnings 105 | $user_defined_atags = array(); 106 | 107 | // This also can be modified in local.php. 108 | $pageheaders = array 109 | ( 110 | 100 => "", 111 | ); 112 | addCSS ('css/pi.css'); 113 | 114 | if (!isset ($script_mode) or $script_mode !== TRUE) 115 | { 116 | // A successful call to authenticate() always generates autotags and somethimes 117 | // even given/implicit tags. It also sets remote_username and remote_displayname. 118 | authenticate(); 119 | // Authentication passed. 120 | // Note that we don't perform autorization here, so each 1st level page 121 | // has to do it in its way, e.g. by calling authorize() after fixContext(). 122 | } 123 | elseif (! isset ($remote_username)) 124 | { 125 | // Some functions require remote_username to be set to something to act correctly, 126 | // even though they don't use the value itself. 127 | $admin_account = spotEntity ('user', 1); 128 | if (isCLIMode() && FALSE !== $env_user = getenv('USER')) 129 | // use USER env var if we are in CLI mode 130 | $remote_username = $env_user; 131 | else 132 | $remote_username = $admin_account['user_name']; 133 | unset ($env_user); 134 | unset ($admin_account); 135 | } 136 | 137 | $virtual_obj_types = explode (',', getConfigVar ('VIRTUAL_OBJ_LISTSRC')); 138 | 139 | alterConfigWithUserPreferences(); 140 | $op = ''; 141 | 142 | // load additional plugins 143 | ob_start(); 144 | foreach (glob("$racktables_plugins_dir/*.php") as $filename) 145 | require_once $filename; 146 | // display plugins output if it contains something but newlines 147 | $tmp = ob_get_clean(); 148 | if ($tmp != '' and ! preg_match ("/^\n+$/D", $tmp)) 149 | echo $tmp; 150 | unset ($tmp); 151 | 152 | // These will be filled in by fixContext() 153 | $expl_tags = array(); 154 | $impl_tags = array(); 155 | // Initial chain for the current target. 156 | $target_given_tags = array(); 157 | 158 | callHook ('initFinished'); 159 | 160 | ?> 161 | -------------------------------------------------------------------------------- /scripts/syncdomain.php: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env php 2 | \n"; 14 | echo "\t\t--vdid=\n"; 15 | echo "\t\t--mode=pull\n"; 16 | echo "\t\t--mode=pullall\n"; 17 | echo "\t\t--mode=push\n"; 18 | echo "\t\t[--max=]\n"; 19 | echo "\t\t[--verbose]\n"; 20 | echo "\t\t[--nolock]\n"; 21 | echo "\t\t[--stderr]\n"; 22 | exit (1); 23 | } 24 | 25 | define ('PML_VERBOSE', 1 << 0); // display message only if --verbose option specified 26 | define ('PML_NOTICE', 1 << 1); // the message is informational, do not write to STDERR 27 | function print_message_line($text, $flags = 0) 28 | { 29 | global $options; 30 | if (! array_key_exists ('verbose', $options) and $flags & PML_VERBOSE) 31 | return; 32 | $buff = date (DATE_RFC1123) . ": ${text}\n"; 33 | echo $buff; 34 | if (array_key_exists ('stderr', $options) and ! ($flags & PML_NOTICE)) 35 | fwrite (STDERR, $buff); 36 | } 37 | 38 | $options = getopt ('', array ('vdid:', 'max::', 'mode:', 'verbose', 'nolock', 'stderr')); 39 | if (!array_key_exists ('mode', $options)) 40 | usage(); 41 | 42 | switch ($options['mode']) 43 | { 44 | case 'pullall': 45 | $do_push = FALSE; 46 | break; 47 | case 'pull': 48 | $do_push = FALSE; 49 | break; 50 | case 'push': 51 | $do_push = TRUE; 52 | break; 53 | default: 54 | usage(); 55 | } 56 | 57 | $max = array_fetch ($options, 'max', 0); 58 | $nolock = array_key_exists ('nolock', $options); 59 | 60 | $switch_list = array(); 61 | if (! isset ($options['vdid'])) 62 | $switch_list = getVLANSwitches(); 63 | else 64 | try 65 | { 66 | $mydomain = getVLANDomain ($options['vdid']); 67 | foreach ($mydomain['switchlist'] as $switch) 68 | $switch_list[] = $switch['object_id']; 69 | } 70 | catch (RackTablesError $e) 71 | { 72 | print_message_line ("Cannot load domain data with ID ${options['vdid']}"); 73 | print_message_line ($e->getMessage()); 74 | exit (1); 75 | } 76 | 77 | $todo = array 78 | ( 79 | 'pull' => array ('sync_ready', 'resync_ready'), 80 | 'push' => array ('sync_ready', 'resync_ready'), 81 | 'pullall' => array ('sync_ready', 'resync_ready', 'sync_aging', 'resync_aging', 'done'), 82 | ); 83 | 84 | if (! $nolock) 85 | { 86 | $domain_key = isset ($options['vdid']) ? $options['vdid'] : 0; 87 | $filename = '/var/tmp/RackTables-syncdomain-' . $domain_key . '.pid'; 88 | if (FALSE === $fp = @fopen ($filename, 'c+')) 89 | { 90 | print_message_line ("Failed to open ${filename}"); 91 | exit (1); 92 | } 93 | $wouldblock = 0; 94 | if (! flock ($fp, LOCK_EX|LOCK_NB, $wouldblock) || $wouldblock) 95 | { 96 | $current_time = time(); 97 | $stat = fstat ($fp); 98 | if (! isset ($stat['mtime'])) 99 | { 100 | print_message_line ("Failed to obtain mtime of ${filename}"); 101 | exit (1); 102 | } 103 | $pidfile_mtime = $stat['mtime']; 104 | if ($current_time < $pidfile_mtime) 105 | { 106 | print_message_line ("Warning: pidfile ${filename} mtime is in future!"); 107 | exit (1); 108 | } 109 | // don't indicate failure unless the pidfile is 15 minutes or more old 110 | if ($current_time < $pidfile_mtime + 15 * 60) 111 | exit (0); 112 | print_message_line ("Failed to lock ${filename}, already locked by PID " . trim (fgets ($fp, 10))); 113 | exit (1); 114 | } 115 | 116 | ftruncate ($fp, 0); 117 | fwrite ($fp, getmypid() . "\n"); 118 | // don't close $fp yet: we need to keep an flock 119 | } 120 | 121 | // fetch all the needed data from DB (preparing for DB connection loss) 122 | $switch_queue = array(); 123 | foreach ($switch_list as $object_id) 124 | { 125 | $cell = spotEntity ('object', $object_id); 126 | $new_disabled = ! considerConfiguredConstraint ($cell, 'SYNC_802Q_LISTSRC'); 127 | $queue = detectVLANSwitchQueue (getVLANSwitchInfo ($object_id)); 128 | if ($queue == 'disabled' xor $new_disabled) 129 | usePreparedExecuteBlade 130 | ( 131 | 'UPDATE VLANSwitch SET out_of_sync="yes", last_error_ts=NOW(), last_errno=? WHERE object_id=?', 132 | array ($new_disabled ? E_8021Q_SYNC_DISABLED : E_8021Q_NOERROR, $object_id) 133 | ); 134 | elseif (in_array ($queue, $todo[$options['mode']])) 135 | $switch_queue[] = $cell; 136 | } 137 | 138 | // YOU SHOULD NOT USE DB FUNCTIONS BELOW IN THE PARENT PROCESS 139 | // THE PARENT'S DB CONNECTION IS LOST DUE TO RECONNECTING IN THE CHILD 140 | $fork_slots = getConfigVar ('SYNCDOMAIN_MAX_PROCESSES'); 141 | $do_fork = ($fork_slots > 1) and extension_loaded ('pcntl'); 142 | if ($fork_slots > 1 and ! $do_fork) 143 | throw new RackTablesError ('PHP extension \'pcntl\' not found, can not use childs', RackTablesError::MISCONFIGURED); 144 | $switches_working = 0; 145 | $switchesdone = 0; 146 | foreach ($switch_queue as $object) 147 | { 148 | if ($do_fork) 149 | { 150 | // wait for the next free slot 151 | while ($fork_slots <= $switches_working) 152 | { 153 | pcntl_waitpid (-1, $wait_status); 154 | --$switches_working; 155 | } 156 | $i_am_child = (0 === $fork_res = pcntl_fork()); 157 | } 158 | if (! $do_fork or $i_am_child) 159 | { 160 | try 161 | { 162 | // make a separate DB connection for correct concurrent transactions handling 163 | if ($i_am_child) 164 | connectDB(); 165 | $portsdone = exec8021QDeploy ($object['id'], $do_push); 166 | $flags = PML_NOTICE; 167 | if (! $portsdone) 168 | $flags |= PML_VERBOSE; 169 | print_message_line ("Done '${object['dname']}': ${portsdone}", $flags); 170 | } 171 | catch (RackTablesError $e) 172 | { 173 | print_message_line ("FAILED '${object['dname']}': " . $e->getMessage()); 174 | } 175 | if ($i_am_child) 176 | exit (0); 177 | } 178 | if (isset ($fork_res) and $fork_res > 0) 179 | ++$switches_working; 180 | 181 | if (++$switchesdone == $max) 182 | { 183 | print_message_line ("Maximum of ${max} items reached, terminating", PML_NOTICE|PML_VERBOSE); 184 | break; 185 | } 186 | } 187 | 188 | // wait for all childs to exit 189 | while ($switches_working > 0) 190 | { 191 | --$switches_working; 192 | pcntl_waitpid (-1, $wait_status); 193 | } 194 | 195 | if (! $nolock) 196 | { 197 | flock ($fp, LOCK_UN); // explicitly unlock file as PHP 5.3.2 made it mandatory 198 | if (FALSE === unlink ($filename)) 199 | { 200 | print_message_line ("Failed removing pidfile ${filename}"); 201 | exit (1); 202 | } 203 | } 204 | exit (0); 205 | ?> 206 | -------------------------------------------------------------------------------- /wwwroot/css/jquery.contextmenu.css: -------------------------------------------------------------------------------- 1 | /* Classic Windows Theme (default) */ 2 | /* =============================== */ 3 | .context-menu-theme-default { 4 | border:2px outset white; 5 | background-color:#D4D0C8; 6 | } 7 | .context-menu-theme-default .context-menu-item { 8 | text-align:left; 9 | cursor:pointer; 10 | padding:4px 28px 4px 16px; 11 | color:black; 12 | font-family:Tahoma,Arial; 13 | font-size:11px; 14 | } 15 | .context-menu-theme-default .context-menu-separator { 16 | margin:4px 2px; 17 | font-size:0px; 18 | border-top:1px solid #808080; 19 | border-bottom:1px solid white; 20 | } 21 | .context-menu-theme-default .context-menu-item-disabled { 22 | color:#808080; 23 | } 24 | .context-menu-theme-default .context-menu-item .context-menu-item-inner { 25 | background:none no-repeat fixed 999px 999px; /* Make sure icons don't appear */ 26 | } 27 | .context-menu-theme-default .context-menu-item-hover { 28 | background-color:#0A246A; 29 | color:white; 30 | } 31 | .context-menu-theme-default .context-menu-item-disabled-hover { 32 | background-color:#0A246A; 33 | } 34 | 35 | /* Windows XP Theme */ 36 | /* ================ */ 37 | .context-menu-theme-xp { 38 | border:1px solid #666; 39 | padding:1px; 40 | background:#F9F8F7 repeat-y top left; 41 | } 42 | .context-menu-theme-xp .context-menu-separator { 43 | margin:4px 2px; 44 | font-size:0px; 45 | border-top:1px solid #808080; 46 | border-bottom:1px solid white; 47 | } 48 | .context-menu-theme-xp .context-menu-item { 49 | text-align:left; 50 | color:black; 51 | font-family:arial; 52 | font-size:11px; 53 | cursor:pointer; 54 | } 55 | .context-menu-theme-xp .context-menu-item .context-menu-item-inner { 56 | background:none no-repeat 2px center; 57 | padding:4px 10px 4px 30px; 58 | } 59 | .context-menu-theme-xp .context-menu-item-hover .context-menu-item-inner { 60 | background:#B6BDD2 none no-repeat 2px center; 61 | padding:3px 9px 3px 29px; 62 | border:1px solid #0A246A; 63 | } 64 | 65 | /* Windows Vista Theme */ 66 | /* =================== */ 67 | .context-menu-theme-vista { 68 | background:#FAFAFA repeat-y left top; 69 | border:1px solid #868686; 70 | } 71 | .context-menu-theme-vista .context-menu-item { 72 | text-align:left; 73 | cursor:pointer; 74 | color:black; 75 | font-family:Tahoma,Arial; 76 | font-size:11px; 77 | } 78 | .context-menu-theme-vista .context-menu-separator { 79 | margin:0px 0px 0px 32px; 80 | font-size:0px; 81 | border-top:1px solid #C5C5C5; 82 | border-bottom:1px solid #F5F5F5; 83 | } 84 | .context-menu-theme-vista .context-menu-item-hover { 85 | background:transparent repeat-x left center; 86 | border:1px solid #D7D0B3; 87 | } 88 | .context-menu-theme-vista .context-menu-item .context-menu-item-inner { 89 | padding:4px 16px 4px 35px; 90 | margin-left:1px; 91 | background-color:none; 92 | background-repeat:no-repeat; 93 | background-position:3px center; 94 | background-image:none; 95 | } 96 | .context-menu-theme-vista .context-menu-item-hover .context-menu-item-inner { 97 | padding:3px 15px 3px 35px; 98 | margin-left:0px; 99 | } 100 | .context-menu-theme-vista .context-menu-item-disabled { 101 | color:#A7A7A7; 102 | } 103 | 104 | /* OSX Theme */ 105 | /* ========= */ 106 | .context-menu-theme-osx { 107 | background-color:white; 108 | opacity: .93; 109 | filter: alpha(opacity=93); 110 | zoom:1.0; 111 | border:1px solid #b2b2b2; 112 | } 113 | .context-menu-theme-osx .context-menu-item { 114 | text-align:left; 115 | cursor:pointer; 116 | color:black; 117 | font-family:Lucida Grande,Arial; 118 | font-weight:700; 119 | font-size:12px; 120 | opacity: 1.0; 121 | filter: alpha(opacity=100); 122 | z-index:1; 123 | } 124 | .context-menu-theme-osx .context-menu-separator { 125 | margin:5px 1px 4px 1px; 126 | font-size:0px; 127 | border-top:1px solid #e4e4e4; 128 | } 129 | .context-menu-theme-osx .context-menu-item-hover { 130 | background-color:#1C44F2; 131 | color:white; 132 | } 133 | .context-menu-theme-osx .context-menu-item .context-menu-item-inner { 134 | padding:2px 10px 2px 22px; 135 | background-color:none; 136 | background-repeat:no-repeat; 137 | background-position:4px center; 138 | background-image:none; 139 | } 140 | .context-menu-theme-osx .context-menu-item-disabled { 141 | color:#939393; 142 | } 143 | 144 | /* Linux Human Theme */ 145 | /* ================= */ 146 | .context-menu-theme-human { 147 | background:#F9F5F2; 148 | border:1px solid #963; 149 | } 150 | .context-menu-theme-human .context-menu-item { 151 | text-align:left; 152 | cursor:pointer; 153 | color:black; 154 | font-family:Helvetica,DejaVu Sans,Arial; 155 | font-size:12px; 156 | line-height:20px; 157 | height:28px; 158 | border:1px solid #F9F5F2; 159 | border-left:0; 160 | border-right:0; 161 | } 162 | .context-menu-theme-human .context-menu-separator { 163 | margin:0px 0px 0px 32px; 164 | font-size:0px; 165 | border-top:1px solid #C5C5C5; 166 | border-bottom:1px solid #F5F5F5; 167 | } 168 | .context-menu-theme-human .context-menu-item-hover { 169 | background:transparent repeat-x left center; 170 | border-color:#963; 171 | } 172 | .context-menu-theme-human .context-menu-item .context-menu-item-inner { 173 | padding:4px 16px 4px 35px; 174 | margin-left:0px; 175 | background-color:none; 176 | background-repeat:no-repeat; 177 | background-position:3px center; 178 | background-image:none; 179 | } 180 | .context-menu-theme-human .context-menu-item-hover .context-menu-item-inner { 181 | } 182 | .context-menu-theme-human .context-menu-item-disabled { 183 | color:#A7A7A7; 184 | } 185 | 186 | /* Gloss Theme */ 187 | /* =========== */ 188 | .context-menu-theme-gloss { 189 | background:#f4f4f4 repeat-y left center; 190 | border:1px solid #f4f4f4; 191 | padding:1px; 192 | padding-right:0; 193 | } 194 | .context-menu-theme-gloss .context-menu-item { 195 | text-align:left; 196 | cursor:pointer; 197 | color:black; 198 | font-family:Helvetica,DejaVu Sans,Arial; 199 | font-size:12px; 200 | line-height:20px; 201 | height:27px; 202 | border:1px solid transparent; 203 | } 204 | .context-menu-theme-gloss .context-menu-separator { 205 | margin:0px 0px 0px 32px; 206 | font-size:0px; 207 | border-top:1px solid #C5C5C5; 208 | border-bottom:1px solid #F5F5F5; 209 | } 210 | .context-menu-theme-gloss .context-menu-item-hover { 211 | background:transparent repeat-x left center; 212 | color:#fff; 213 | border-color:#000; 214 | border-radius: 3px; 215 | -webkit-border-radius: 3px; 216 | -moz-border-radius: 3px; 217 | } 218 | .context-menu-theme-gloss .context-menu-item .context-menu-item-inner { 219 | padding:4px 16px 4px 35px; 220 | margin-left:0px; 221 | background-color:none; 222 | background-repeat:no-repeat; 223 | background-position:3px center; 224 | background-image:none; 225 | } 226 | .context-menu-theme-gloss .context-menu-item-hover .context-menu-item-inner { 227 | } 228 | .context-menu-theme-gloss .context-menu-item-disabled { 229 | color:#A7A7A7; 230 | } 231 | 232 | .context-menu-theme-gloss-cyan .context-menu-item-hover { 233 | border-color:#00c; 234 | } 235 | 236 | .context-menu-theme-gloss-semitransparent .context-menu-item-hover { 237 | border-color:#00c; 238 | background-color:#30f; 239 | } 240 | 241 | -------------------------------------------------------------------------------- /wwwroot/js/codemirror/codemirror.css: -------------------------------------------------------------------------------- 1 | /* BASICS */ 2 | 3 | .CodeMirror { 4 | /* Set height, width, borders, and global font properties here */ 5 | font-family: monospace; 6 | height: 500px; 7 | width:100%; 8 | } 9 | .CodeMirror-scroll { 10 | /* Set scrolling behaviour here */ 11 | overflow: auto; 12 | } 13 | 14 | /* PADDING */ 15 | 16 | .CodeMirror-lines { 17 | padding: 4px 0; /* Vertical padding around content */ 18 | } 19 | .CodeMirror pre { 20 | padding: 0 4px; /* Horizontal padding of content */ 21 | } 22 | 23 | .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 24 | background-color: white; /* The little square between H and V scrollbars */ 25 | } 26 | 27 | /* GUTTER */ 28 | 29 | .CodeMirror-gutters { 30 | border-right: 1px solid #ddd; 31 | background-color: #f7f7f7; 32 | white-space: nowrap; 33 | } 34 | .CodeMirror-linenumbers {} 35 | .CodeMirror-linenumber { 36 | padding: 0 3px 0 5px; 37 | min-width: 20px; 38 | text-align: right; 39 | color: #999; 40 | -moz-box-sizing: content-box; 41 | box-sizing: content-box; 42 | } 43 | 44 | /* CURSOR */ 45 | 46 | .CodeMirror div.CodeMirror-cursor { 47 | border-left: 1px solid black; 48 | z-index: 3; 49 | } 50 | /* Shown when moving in bi-directional text */ 51 | .CodeMirror div.CodeMirror-secondarycursor { 52 | border-left: 1px solid silver; 53 | } 54 | .CodeMirror.cm-keymap-fat-cursor div.CodeMirror-cursor { 55 | width: auto; 56 | border: 0; 57 | background: #7e7; 58 | z-index: 1; 59 | } 60 | /* Can style cursor different in overwrite (non-insert) mode */ 61 | .CodeMirror div.CodeMirror-cursor.CodeMirror-overwrite {} 62 | 63 | .cm-tab { display: inline-block; } 64 | 65 | .CodeMirror-ruler { 66 | border-left: 1px solid #ccc; 67 | position: absolute; 68 | } 69 | 70 | /* DEFAULT THEME */ 71 | 72 | .cm-s-default .cm-keyword {color: #708;} 73 | .cm-s-default .cm-atom {color: #219;} 74 | .cm-s-default .cm-number {color: #164;} 75 | .cm-s-default .cm-def {color: #00f;} 76 | .cm-s-default .cm-variable {color: black;} 77 | .cm-s-default .cm-variable-2 {color: #05a;} 78 | .cm-s-default .cm-variable-3 {color: #085;} 79 | .cm-s-default .cm-property {color: black;} 80 | .cm-s-default .cm-operator {color: blue;} 81 | .cm-s-default .cm-comment {color: #a50;} 82 | .cm-s-default .cm-string {color: #a11;} 83 | .cm-s-default .cm-string-2 {color: #f50;} 84 | .cm-s-default .cm-meta {color: #555;} 85 | .cm-s-default .cm-qualifier {color: #555;} 86 | .cm-s-default .cm-builtin {color: #30a;} 87 | .cm-s-default .cm-bracket {color: #997;} 88 | .cm-s-default .cm-tag {color: orange;} 89 | .cm-s-default .cm-attribute {color: #00c;} 90 | .cm-s-default .cm-header {color: blue;} 91 | .cm-s-default .cm-quote {color: #090;} 92 | .cm-s-default .cm-hr {color: #999;} 93 | .cm-s-default .cm-link {color: #00c;} 94 | 95 | .cm-negative {color: #d44;} 96 | .cm-positive {color: #292;} 97 | .cm-header, .cm-strong {font-weight: bold;} 98 | .cm-em {font-style: italic;} 99 | .cm-link {text-decoration: underline;} 100 | 101 | .cm-s-default .cm-error {color: #f00;} 102 | .cm-invalidchar {color: #f00;} 103 | 104 | div.CodeMirror span.CodeMirror-matchingbracket {color: #0f0;} 105 | div.CodeMirror span.CodeMirror-nonmatchingbracket {color: #f22;} 106 | .CodeMirror-activeline-background {background: #e8f2ff;} 107 | 108 | /* STOP */ 109 | 110 | /* The rest of this file contains styles related to the mechanics of 111 | the editor. You probably shouldn't touch them. */ 112 | 113 | .CodeMirror { 114 | line-height: 1; 115 | position: relative; 116 | overflow: hidden; 117 | background: white; 118 | color: black; 119 | } 120 | 121 | .CodeMirror-scroll { 122 | /* 30px is the magic margin used to hide the element's real scrollbars */ 123 | /* See overflow: hidden in .CodeMirror */ 124 | margin-bottom: -30px; margin-right: -30px; 125 | padding-bottom: 30px; 126 | height: 100%; 127 | outline: none; /* Prevent dragging from highlighting the element */ 128 | position: relative; 129 | -moz-box-sizing: content-box; 130 | box-sizing: content-box; 131 | } 132 | .CodeMirror-sizer { 133 | position: relative; 134 | border-right: 30px solid transparent; 135 | -moz-box-sizing: content-box; 136 | box-sizing: content-box; 137 | } 138 | 139 | /* The fake, visible scrollbars. Used to force redraw during scrolling 140 | before actuall scrolling happens, thus preventing shaking and 141 | flickering artifacts. */ 142 | .CodeMirror-vscrollbar, .CodeMirror-hscrollbar, .CodeMirror-scrollbar-filler, .CodeMirror-gutter-filler { 143 | position: absolute; 144 | z-index: 6; 145 | display: none; 146 | } 147 | .CodeMirror-vscrollbar { 148 | right: 0; top: 0; 149 | overflow-x: hidden; 150 | overflow-y: scroll; 151 | } 152 | .CodeMirror-hscrollbar { 153 | bottom: 0; left: 0; 154 | overflow-y: hidden; 155 | overflow-x: scroll; 156 | } 157 | .CodeMirror-scrollbar-filler { 158 | right: 0; bottom: 0; 159 | } 160 | .CodeMirror-gutter-filler { 161 | left: 0; bottom: 0; 162 | } 163 | 164 | .CodeMirror-gutters { 165 | position: absolute; left: 0; top: 0; 166 | padding-bottom: 30px; 167 | z-index: 3; 168 | } 169 | .CodeMirror-gutter { 170 | white-space: normal; 171 | height: 100%; 172 | -moz-box-sizing: content-box; 173 | box-sizing: content-box; 174 | padding-bottom: 30px; 175 | margin-bottom: -32px; 176 | display: inline-block; 177 | /* Hack to make IE7 behave */ 178 | *zoom:1; 179 | *display:inline; 180 | } 181 | .CodeMirror-gutter-elt { 182 | position: absolute; 183 | cursor: default; 184 | z-index: 4; 185 | } 186 | 187 | .CodeMirror-lines { 188 | cursor: text; 189 | } 190 | .CodeMirror pre { 191 | /* Reset some styles that the rest of the page might have set */ 192 | -moz-border-radius: 0; -webkit-border-radius: 0; border-radius: 0; 193 | border-width: 0; 194 | background: transparent; 195 | font-family: inherit; 196 | font-size: inherit; 197 | margin: 0; 198 | white-space: pre; 199 | word-wrap: normal; 200 | line-height: inherit; 201 | color: inherit; 202 | z-index: 2; 203 | position: relative; 204 | overflow: visible; 205 | } 206 | .CodeMirror-wrap pre { 207 | word-wrap: break-word; 208 | white-space: pre-wrap; 209 | word-break: normal; 210 | } 211 | 212 | .CodeMirror-linebackground { 213 | position: absolute; 214 | left: 0; right: 0; top: 0; bottom: 0; 215 | z-index: 0; 216 | } 217 | 218 | .CodeMirror-linewidget { 219 | position: relative; 220 | z-index: 2; 221 | overflow: auto; 222 | } 223 | 224 | .CodeMirror-widget {} 225 | 226 | .CodeMirror-wrap .CodeMirror-scroll { 227 | overflow-x: hidden; 228 | } 229 | 230 | .CodeMirror-measure { 231 | position: absolute; 232 | width: 100%; 233 | height: 0; 234 | overflow: hidden; 235 | visibility: hidden; 236 | } 237 | .CodeMirror-measure pre { position: static; } 238 | 239 | .CodeMirror div.CodeMirror-cursor { 240 | position: absolute; 241 | visibility: hidden; 242 | border-right: none; 243 | width: 0; 244 | } 245 | .CodeMirror-focused div.CodeMirror-cursor { 246 | visibility: visible; 247 | } 248 | 249 | .CodeMirror-selected { background: #d9d9d9; } 250 | .CodeMirror-focused .CodeMirror-selected { background: #d7d4f0; } 251 | 252 | .cm-searching { 253 | background: #ffa; 254 | background: rgba(255, 255, 0, .4); 255 | } 256 | 257 | /* IE7 hack to prevent it from returning funny offsetTops on the spans */ 258 | .CodeMirror span { *vertical-align: text-bottom; } 259 | 260 | @media print { 261 | /* Hide the cursor when printing */ 262 | .CodeMirror div.CodeMirror-cursor { 263 | visibility: hidden; 264 | } 265 | } 266 | -------------------------------------------------------------------------------- /wwwroot/js/portinfo.js: -------------------------------------------------------------------------------- 1 | if (enabled_elements == null) 2 | enabled_elements = []; 3 | enabled_elements_count = enabled_elements.length; 4 | port_cmenu_items = []; 5 | wait_count = 0; 6 | bk_event = null; 7 | port_clicked = null; 8 | 9 | (function () { 10 | var menu_item_candidates = { 11 | link: {'Show links status': {onclick: menuItemClicked, className: 'itemname-link'}}, 12 | conf: {'Show ports configuration': {onclick: menuItemClicked, className: 'itemname-conf'}}, 13 | mac: {'Show device learned MACs': {onclick: menuItemClicked, className: 'itemname-mac'}}, 14 | portmac: {'Show port learned MACs': {onclick: menuItemClicked, className: 'itemname-portmac'}} 15 | }; 16 | var portinfo_enabled = false; 17 | for (var i in enabled_elements) { 18 | var name = enabled_elements[i]; 19 | port_cmenu_items.push(menu_item_candidates[name]); 20 | if (menu_item_candidates[name] != null && name.match (/^(link|conf|mac)$/)) 21 | portinfo_enabled = true; 22 | } 23 | if (portinfo_enabled) { 24 | port_cmenu_items.unshift($.contextMenu.separator); 25 | port_cmenu_items.unshift({'Show all info': {onclick: showAllClicked, className: 'itemname-all'}}); 26 | } 27 | })(); 28 | 29 | function showAllClicked(menuItem, menu) { 30 | for (var i in enabled_elements) { 31 | var name = enabled_elements[i]; 32 | if (name.match (/^(link|conf|mac)$/)) { 33 | port_clicked = this; 34 | menuItemClicked($('.context-menu-item.itemname-' + name)[0], menu); 35 | } 36 | } 37 | } 38 | 39 | $(document).ready(function () { 40 | // create global object port_cmenu 41 | if (port_cmenu_items.length) { 42 | port_cmenu = $.contextMenu.create(port_cmenu_items, {theme:'vista'}); 43 | // add popup button after every portname 44 | $('.interactive-portname.port-menu').after(''); 45 | $('
').hide().addClass('cursor-shadow').css('position', 'absolute').css('width', 16).css('height', 16).css("background-image", "url(index.php?module=chrome&uri=pix/ajax-loader.gif").appendTo('body'); 46 | 47 | // bind popup menu to every link with class port-popup 48 | $('a.port-popup').bind('click', function(e){port_cmenu.show(this,e);return false;}); 49 | $('body').bind('mouseup', rememberCursorPosition); 50 | } 51 | }); 52 | 53 | function disableMenuItem(menuItem) { 54 | setTimeout (function() { 55 | if (! $(menuItem).hasClass($.contextMenu.disabledItemClassName)) { 56 | $(menuItem).addClass($.contextMenu.disabledItemClassName); 57 | if (--enabled_elements_count <= 0) { 58 | $('.context-menu-item.itemname-all').addClass($.contextMenu.disabledItemClassName); 59 | } 60 | } 61 | }, 50); 62 | } 63 | 64 | function setItemIcon(menuItem, iconName) { 65 | var iconURL; 66 | if (iconName == 'wait') 67 | iconURL = 'index.php?module=chrome&uri=pix/ajax-loader.gif'; 68 | else if (iconName == 'ok') 69 | iconURL = 'index.php?module=chrome&uri=pix/checkbox_yes.png'; 70 | else 71 | iconURL = ''; 72 | $(menuItem).children('.' + $.contextMenu.innerDivClassName).css("background-image", "url(" + iconURL + ")"); 73 | } 74 | 75 | function menuItemClicked(menuItem, menu) { 76 | if (!port_clicked) 77 | port_clicked = this; 78 | // if the item is already disabled, do not react on click 79 | if ($(menuItem).hasClass($.contextMenu.disabledItemClassName)) 80 | return; 81 | 82 | // determine the typename of menuItem ('link', 'mac' or 'conf') 83 | var matches = menuItem.className.match(/itemname-([^ ]+)/); 84 | if (! matches) 85 | return; 86 | var type = matches[1]; 87 | var per_port_cmd = type == 'portmac'; 88 | var portnameElem = $(port_clicked).siblings('.interactive-portname')[0]; 89 | port_clicked = null; 90 | 91 | var bSuccessIcon = false; 92 | $.ajax({ 93 | async: true, 94 | beforeSend: function(XMLHttpRequest) { 95 | if (++wait_count > 0) { 96 | cursor_shadow = $('.cursor-shadow'); 97 | $('body').bind('mousemove', waitCursorMove); 98 | if (bk_event) 99 | waitCursorMove(bk_event); 100 | } 101 | setItemIcon(menuItem, 'wait'); 102 | }, 103 | complete: function(XMLHttpRequest, textStatus) { 104 | if (--wait_count <= 0) { 105 | $('.cursor-shadow').hide(); 106 | $('body').unbind('mousemove', waitCursorMove); 107 | $('body').unbind('click', rememberCursorPosition); 108 | } 109 | if (! bSuccessIcon) 110 | setItemIcon(menuItem, ''); 111 | }, 112 | type: 'GET', 113 | url: 'index.php', 114 | data: { 115 | 'module': 'ajax', 116 | 'ac': 'get-port-' + type, 117 | 'object_id': getQueryString('object_id'), 118 | 'port_name': getPortName(portnameElem) 119 | }, 120 | dataType: 'json', 121 | success: function(data, textStatus, XMLHttpRequest) { 122 | if (! data) 123 | return; 124 | bSuccessIcon = true; 125 | 126 | if (per_port_cmd) 127 | setItemIcon(menuItem, ''); 128 | else 129 | setItemIcon(menuItem, 'ok'); 130 | 131 | if (type == 'link') 132 | applyLinkData(data); 133 | else if (type == 'mac' || type == 'portmac') 134 | applyMacData(data); 135 | else if (type == 'conf') 136 | applyConfData(data); 137 | } 138 | }); 139 | if (!per_port_cmd) 140 | disableMenuItem(menuItem); 141 | } 142 | 143 | function rememberCursorPosition(event) { 144 | bk_event = event; 145 | } 146 | 147 | function waitCursorMove(event) { 148 | cursor_shadow.css("left", event.pageX + 15).css("top", event.pageY + 15).show(); 149 | } 150 | 151 | function applyLinkData(data) { 152 | var seen_portnames = {}; 153 | $('.interactive-portname').each(function() { 154 | var portname = getPortName (this); 155 | var item = data[portname]; 156 | if (item != null) { 157 | var prepended; 158 | if (item['inline']) 159 | prepended = $('').addClass('port-link').html(item['inline']).insertBefore(this); 160 | if (item['popup'] && prepended) { 161 | if (! seen_portnames[portname]) 162 | seen_portnames[portname] = $('
').addClass('popup-box').html(item['popup']).appendTo('body'); 163 | prepended.thumbPopup(seen_portnames[portname]); 164 | } 165 | } 166 | }); 167 | } 168 | 169 | function applyMacData(data) { 170 | var seen_portnames = {}; 171 | $('.interactive-portname').each(function() { 172 | var portname = getPortName (this); 173 | var item = data[portname]; 174 | if (item != null) { 175 | var prepended; 176 | if (item['inline']) 177 | { 178 | var obj = $(this).parent().find('.mac-count'); 179 | if (!obj.length) 180 | appended = $('
').addClass('mac-count').html(item['inline']).appendTo($(this).parent()); 181 | else 182 | appended = obj.html(item['inline']); 183 | } 184 | if (item['popup'] && appended) { 185 | var obj = $("[id='maclist-popup-"+portname+"']"); 186 | if (! obj.size()) 187 | obj = $('
').addClass('popup-box mac-list').attr('id', 'maclist-popup-'+portname).html(item['popup']).appendTo('body'); 188 | else 189 | obj.html(item['popup']); 190 | appended.thumbPopup(obj); 191 | } 192 | } 193 | }); 194 | } 195 | 196 | function applyConfData(data) { 197 | var seen_portnames = {}; 198 | $('.interactive-portname').each(function() { 199 | var portname = getPortName (this); 200 | var item = data[portname]; 201 | if (item != null) { 202 | if (! seen_portnames[portname]) 203 | seen_portnames[portname] = $('
').addClass('popup-box port-config').html(item['popup']).appendTo('body'); 204 | $(this).thumbPopup(seen_portnames[portname]); 205 | } 206 | }); 207 | } 208 | 209 | function getPortName (element) { 210 | if (element.type == 'text') 211 | return $(element).val(); 212 | else 213 | return $(element).html(); 214 | } 215 | -------------------------------------------------------------------------------- /wwwroot/js/jquery.optionTree.js: -------------------------------------------------------------------------------- 1 | /* 2 | * jQuery optionTree Plugin 3 | * version: 1.3 4 | * @requires jQuery v1.3 or later 5 | * 6 | * Dual licensed under the MIT and GPL licenses: 7 | * http://www.opensource.org/licenses/mit-license.php 8 | * http://www.gnu.org/licenses/gpl.html 9 | * 10 | * @version $Id: jquery.optionTree.js 13 2011-03-31 08:51:59Z kkotowicz $ 11 | * @author Krzysztof Kotowicz 12 | * @see http://code.google.com/p/jquery-option-tree/ 13 | * @see http://blog.kotowicz.net/search/label/option 14 | */ 15 | 16 | /** 17 | * Converts passed JSON option tree into dynamically created ").attr('name',name) 109 | .change(function() { 110 | if (this.options[this.selectedIndex].value !== '') { 111 | if ($.isFunction(options.on_each_change)) { 112 | removeNested(name + '_'); 113 | options.on_each_change.apply(this, [this.options[this.selectedIndex].value, tree]); 114 | } else { 115 | // call with value as a first parameter 116 | $(this).optionTree(tree[this.options[this.selectedIndex].value], options); 117 | } 118 | if (options.set_value_on === 'each') { 119 | setValue(name, this.options[this.selectedIndex].value); 120 | } 121 | } else { 122 | removeNested(name + '_'); 123 | setValue(name, options.empty_value); 124 | } 125 | }); 126 | 127 | var text_to_choose = ''; 128 | 129 | if (jQuery.isFunction(options.choose)) { 130 | var level = $(this).siblings().andSelf().filter('select').length; 131 | text_to_choose = options.choose.apply(this, [level]); 132 | } else if ( options.choose !== '' ) { 133 | text_to_choose = options.choose; 134 | } 135 | 136 | // if show multiple -> show open select 137 | var count_tree_objects = 0; 138 | if ( text_to_choose !== '' ) { 139 | // we have a default value 140 | count_tree_objects++; 141 | } 142 | if (options.show_multiple > 1) { 143 | count_tree_objects = options.show_multiple; 144 | } else if (options.show_multiple === true) { 145 | $.each(tree, function() { 146 | count_tree_objects++; 147 | }); 148 | } 149 | if ( count_tree_objects > 1 ){ 150 | $select.attr('size', count_tree_objects); 151 | } 152 | 153 | if ($(this).is('input')) { 154 | $select.insertBefore(this); 155 | } else { 156 | $select.insertAfter(this); 157 | } 158 | 159 | if (options.select_class) { 160 | $select.addClass(options.select_class); 161 | } 162 | 163 | if (options.tabindex) { 164 | $select.attr('tabindex', options.tabindex); 165 | } 166 | 167 | if ( text_to_choose !== '' ) { 168 | $("