release 2.011000
Squashed commit of the following: commit 3f1730957b6accbc11737e46c201453d7219d03e Author: Oliver Gorwits <oliver@cpan.org> Date: Mon Jul 29 08:01:59 2013 +0100 ready for 2.011000 commit e1873ca58375b458d9543576951f1003e1c28d35 Author: Oliver Gorwits <oliver@cpan.org> Date: Sun Jul 28 00:02:20 2013 +0100 Find the RW snmp community string correctly now commit 039780bc66ca0d8b19767c38a21aa208feafeaf7 Author: Oliver Gorwits <oliver@cpan.org> Date: Sat Jul 27 23:07:05 2013 +0100 User Management (for admins only) commit 213352d54ee8e71cbca5ae2c1c75696800c4216b Author: Oliver Gorwits <oliver@cpan.org> Date: Sat Jul 27 21:17:57 2013 +0100 Table headers float on the page when scrolling commit 598960e9141b0d9fc4f9a234a7d8fe02a81ba0f9 Author: Oliver Gorwits <oliver@cpan.org> Date: Sat Jul 27 18:05:00 2013 +0100 Port Utilization report commit d25e41894476c74bee747e38960a277e2f5b2072 Author: Oliver Gorwits <oliver@cpan.org> Date: Sat Jul 27 15:20:23 2013 +0100 Button to empty the job queue, and improve display when the queue is empty commit 18125d1a758b5707ab4c0ff8b65dfdd90dc32664 Author: Oliver Gorwits <oliver@cpan.org> Date: Sat Jul 27 14:23:48 2013 +0100 Swap play/pause icons in jobqueue commit 9eead5328a127689701ac28d5bcf1cfa39edaf99 Author: Oliver Gorwits <oliver@cpan.org> Date: Thu Jul 25 23:11:17 2013 +0100 Revert "No longer depend on Moo" This reverts commit 0a87ad4b410fa784bfbe823f3e6ede7c979144f3. Conflicts: Netdisco/Changes commit d0c31effa834201f1592c1fc3da9a6a689a3a43c Author: Oliver Gorwits <oliver@cpan.org> Date: Thu Jul 25 23:01:32 2013 +0100 REMOTE_USER is an env var, not an HTTP Header commit 0a87ad4b410fa784bfbe823f3e6ede7c979144f3 Author: Oliver Gorwits <oliver@cpan.org> Date: Thu Jul 25 22:35:05 2013 +0100 No longer depend on Moo commit 7ccbb04e6f7c1701194d996baa557affcda48103 Author: Oliver Gorwits <oliver@cpan.org> Date: Wed Jul 24 23:44:42 2013 +0100 ready for 2.010004 commit 6314c5a054d56d7829797d37c6627b2cbccde4ab Author: Oliver Gorwits <oliver@cpan.org> Date: Wed Jul 24 23:41:51 2013 +0100 Navbar query box was being cleared sometimes under admin task panels commit 271a5d9db17b288aeff43ee29a6bbf753bf823de Author: Oliver Gorwits <oliver@cpan.org> Date: Wed Jul 24 00:03:05 2013 +0100 update TODO commit 3103f968a9fb128726ed929589137cb6011e2591 Author: Oliver Gorwits <oliver@cpan.org> Date: Tue Jul 23 23:58:19 2013 +0100 ready for 2.010002 commit 0368df1dbdfe6d764eec05f2bf37587fff795995 Author: Oliver Gorwits <oliver@cpan.org> Date: Tue Jul 23 23:17:17 2013 +0100 fix bugs in topo update code commit 43b7203ca3270dc2e02a097472179517087522d2 Author: Oliver Gorwits <oliver@cpan.org> Date: Tue Jul 23 23:12:36 2013 +0100 fix FF bug with forms embedded in tables commit f86c5d7d3d8d293a781c2ec7dc7a18bfb3c8bf78 Author: Oliver Gorwits <oliver@cpan.org> Date: Tue Jul 23 17:15:35 2013 +0100 Nullify unused schema changes commit 649e4c471d524013f87257e11fffa7789dccd01d Author: Oliver Gorwits <oliver@cpan.org> Date: Thu Jun 20 13:48:20 2013 +0100 version bump commit ac6ce399b2bd596444a629f24ddea5eca0fff56a Author: Oliver Gorwits <oliver@cpan.org> Date: Thu Jun 20 13:41:08 2013 +0100 Handle UTF-8 data in the device port remote_id commit c73b86c0204ddd98e9d27437028a7000d70338bf Author: Oliver Gorwits <oliver@cpan.org> Date: Thu Jun 20 13:35:32 2013 +0100 revert bytea conversion on remote_id commit a144f42cf93803882bb8492cd3ce3a8e5679d383 Author: Oliver Gorwits <oliver@cpan.org> Date: Mon Jun 17 23:09:50 2013 +0100 bump version for beta release commit 3b791c93d7d9b7358bf46f31e322a9b807823d9d Author: Oliver Gorwits <oliver@cpan.org> Date: Mon Jun 17 22:57:59 2013 +0100 Pass event param to all js functions which require it commit da38badef893fc1503a797a99c34504db71e7c20 Author: Oliver Gorwits <oliver@cpan.org> Date: Mon Jun 17 22:35:48 2013 +0100 Change data type on device_port remote_id to bytea commit 727237951a5576b476dee127b3cef777afb51df8 Author: Oliver Gorwits <oliver@cpan.org> Date: Mon Jun 17 10:52:28 2013 +0100 fix help message in netdisco-web-fg commit 25bc026dc5e0177cd3aa81c11cdace091eb68f36 Author: Oliver Gorwits <oliver@cpan.org> Date: Mon Jun 17 08:16:56 2013 +0100 bump version for new release commit d4042f6e8db42c7a85df4dcf9690fec72ad2db69 Author: Oliver Gorwits <oliver@cpan.org> Date: Sun Jun 16 18:27:52 2013 +0100 Job Queue page play/pause/refresh controls commit b6c9152516d7800409b7a73c5d0cdce6dd405492 Author: Oliver Gorwits <oliver@cpan.org> Date: Sun Jun 16 17:50:06 2013 +0100 limit size of job queue table commit ac9e5feb8b774071fcf4423dd862dced74dee9e6 Author: Oliver Gorwits <oliver@cpan.org> Date: Sun Jun 16 17:47:55 2013 +0100 update bugs link commit 9c0fb0e9aedc6297f4462c3cf88343f6d0df40b6 Author: Oliver Gorwits <oliver@cpan.org> Date: Sun Jun 16 17:41:29 2013 +0100 update MANIFEST commit 7aaa2fff91ed2b1839bdbb79081d90ad3e144f47 Author: Oliver Gorwits <oliver@cpan.org> Date: Sun Jun 16 17:40:35 2013 +0100 Fix Plack middleware config for Expiry commit 313e2cf014cf0da7cf85074e390ad394b28bf42d Author: Oliver Gorwits <oliver@cpan.org> Date: Sun Jun 16 17:23:00 2013 +0100 Support for delegated authentication with REMOTE_USER and X-REMOTE_USER commit 85e21f2bf296c4a5ca6b5afb5091694e56e3031f Author: Oliver Gorwits <oliver@cpan.org> Date: Sun Jun 16 14:24:08 2013 +0100 Add tooltip showing the job queue item logged status message commit 9b14f53ebed51eb46ea278807cfe8a2fbd28743c Author: Oliver Gorwits <oliver@cpan.org> Date: Sun Jun 16 13:43:26 2013 +0100 Increase default frequency of job queue polling to 2 seconds commit 6ba46818d8ab2100c652c8eb8e98bc6f5a54e273 Author: Oliver Gorwits <oliver@cpan.org> Date: Sun Jun 16 12:57:43 2013 +0100 workaround for https://github.com/PerlDancer/Dancer/issues/935 commit c7a2d8a9d45716959bedbbb8db4cdd82a5950642 Author: Oliver Gorwits <oliver@cpan.org> Date: Sun Jun 16 11:54:18 2013 +0100 Fix hyperlinks when running behind reverse proxy on custom path commit 0620efa404bc25cb0a9ada5aa6f1b092d5c4d482 Author: Oliver Gorwits <oliver@cpan.org> Date: Sat Jun 15 18:31:19 2013 +0100 update deploy docs commit 857b1c7aa0fe832f8948349eda5211eb38ba3099 Author: Oliver Gorwits <oliver@cpan.org> Date: Sat Jun 15 18:16:50 2013 +0100 add note about compiler dependency commit 02a2ad6b2c52db9fbc1e24bc8888f658dc7084ad Author: Oliver Gorwits <oliver@cpan.org> Date: Sat Jun 15 17:44:29 2013 +0100 sort vlans, macs, ips in device port view commit 097bad77310728a98b261a2cfca4de7ab50be94b Author: Oliver Gorwits <oliver@cpan.org> Date: Sat Jun 15 16:32:20 2013 +0100 hint when calling web in fg without starman commit 6425d89ddb2b56129c610482134482d8f9455d40 Author: Oliver Gorwits <oliver@cpan.org> Date: Sat Jun 15 15:53:26 2013 +0100 macwalk and arpwalk refactored commit d527b9d05addc82fb38c84f6fea1aa5818fc68d5 Author: Oliver Gorwits <oliver@cpan.org> Date: Thu Jun 13 22:27:34 2013 +0100 implement is_macsuckable and is_arpnipable commit 7af10ed313e25f5d99a22b53ba438225c2259069 Author: Oliver Gorwits <oliver@cpan.org> Date: Thu Jun 13 22:17:39 2013 +0100 version bump commit 8ace3bf8fa48cf3e14bdf86fad5a4862aad50a4b Author: Oliver Gorwits <oliver@cpan.org> Date: Thu Jun 13 22:14:05 2013 +0100 tidy up user menu commit e6eef605c248471dbfe7ec62cd04d73d653523ca Author: Oliver Gorwits <oliver@cpan.org> Date: Thu Jun 13 22:02:52 2013 +0100 Add discoverall, macwalk, arpwalk items to the Admin Tasks menu commit 2631fabd1eccd8a3971e4762eebe57f406623bee Author: Oliver Gorwits <oliver@cpan.org> Date: Thu Jun 13 21:21:50 2013 +0100 remove length() which only became sane in 5.12 commit a7b7169070a58685cacde26a3b6d462e74be9928 Author: Oliver Gorwits <oliver@cpan.org> Date: Thu Jun 13 19:07:56 2013 +0100 Use DBIx::Class new collapsed query support when we can commit 77cddab8ba7033ccb1ecae257bafa4eef8f99f47 Author: Oliver Gorwits <oliver@cpan.org> Date: Wed Jun 12 17:26:47 2013 +0100 Database config simplified to only four essential settings commit 6ed0802bf2ab0fd898ce6945451b8ca6566ae551 Author: Oliver Gorwits <oliver@cpan.org> Date: Wed Jun 12 13:03:20 2013 +0100 Ask to set up guest user for Admin/Port Control rights in deploy script
This commit is contained in:
@@ -1,3 +1,22 @@
|
|||||||
|
2.011000 - 2013-07-29
|
||||||
|
|
||||||
|
[NEW FEATURES]
|
||||||
|
|
||||||
|
* Port Utilization report
|
||||||
|
* User Management (for admins only)
|
||||||
|
|
||||||
|
[ENHANCEMENTS]
|
||||||
|
|
||||||
|
* Add docs note about SSL support
|
||||||
|
* Button to empty the job queue, and improve display when the queue is empty
|
||||||
|
* Table headers float on the page when scrolling
|
||||||
|
|
||||||
|
[BUG FIXES]
|
||||||
|
|
||||||
|
* REMOTE_USER is an env var, not an HTTP Header
|
||||||
|
* Swap play/pause icons in jobqueue
|
||||||
|
* Find the RW snmp community string correctly now
|
||||||
|
|
||||||
2.010004 - 2013-07-24
|
2.010004 - 2013-07-24
|
||||||
|
|
||||||
[BUG FIXES]
|
[BUG FIXES]
|
||||||
|
|||||||
@@ -72,6 +72,7 @@ lib/App/Netdisco/DB/Result/Virtual/DevicePortVlanNative.pm
|
|||||||
lib/App/Netdisco/DB/Result/Virtual/DevicePortVlanTagged.pm
|
lib/App/Netdisco/DB/Result/Virtual/DevicePortVlanTagged.pm
|
||||||
lib/App/Netdisco/DB/Result/Virtual/DuplexMismatch.pm
|
lib/App/Netdisco/DB/Result/Virtual/DuplexMismatch.pm
|
||||||
lib/App/Netdisco/DB/Result/Virtual/NodeWithAge.pm
|
lib/App/Netdisco/DB/Result/Virtual/NodeWithAge.pm
|
||||||
|
lib/App/Netdisco/DB/Result/Virtual/PortUtilization.pm
|
||||||
lib/App/Netdisco/DB/ResultSet/Admin.pm
|
lib/App/Netdisco/DB/ResultSet/Admin.pm
|
||||||
lib/App/Netdisco/DB/ResultSet/Device.pm
|
lib/App/Netdisco/DB/ResultSet/Device.pm
|
||||||
lib/App/Netdisco/DB/ResultSet/DevicePort.pm
|
lib/App/Netdisco/DB/ResultSet/DevicePort.pm
|
||||||
@@ -98,6 +99,7 @@ lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-2-PostgreSQL.sql
|
|||||||
lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-20-21-PostgreSQL.sql
|
lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-20-21-PostgreSQL.sql
|
||||||
lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-21-22-PostgreSQL.sql
|
lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-21-22-PostgreSQL.sql
|
||||||
lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-22-23-PostgreSQL.sql
|
lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-22-23-PostgreSQL.sql
|
||||||
|
lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-23-24-PostgreSQL.sql
|
||||||
lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-3-4-PostgreSQL.sql
|
lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-3-4-PostgreSQL.sql
|
||||||
lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-4-5-PostgreSQL.sql
|
lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-4-5-PostgreSQL.sql
|
||||||
lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-5-6-PostgreSQL.sql
|
lib/App/Netdisco/DB/schema_versions/App-Netdisco-DB-5-6-PostgreSQL.sql
|
||||||
@@ -127,6 +129,7 @@ lib/App/Netdisco/Web/Plugin.pm
|
|||||||
lib/App/Netdisco/Web/Plugin/AdminTask/JobQueue.pm
|
lib/App/Netdisco/Web/Plugin/AdminTask/JobQueue.pm
|
||||||
lib/App/Netdisco/Web/Plugin/AdminTask/PseudoDevice.pm
|
lib/App/Netdisco/Web/Plugin/AdminTask/PseudoDevice.pm
|
||||||
lib/App/Netdisco/Web/Plugin/AdminTask/Topology.pm
|
lib/App/Netdisco/Web/Plugin/AdminTask/Topology.pm
|
||||||
|
lib/App/Netdisco/Web/Plugin/AdminTask/Users.pm
|
||||||
lib/App/Netdisco/Web/Plugin/Device/Addresses.pm
|
lib/App/Netdisco/Web/Plugin/Device/Addresses.pm
|
||||||
lib/App/Netdisco/Web/Plugin/Device/Details.pm
|
lib/App/Netdisco/Web/Plugin/Device/Details.pm
|
||||||
lib/App/Netdisco/Web/Plugin/Device/Modules.pm
|
lib/App/Netdisco/Web/Plugin/Device/Modules.pm
|
||||||
@@ -134,6 +137,7 @@ lib/App/Netdisco/Web/Plugin/Device/Neighbors.pm
|
|||||||
lib/App/Netdisco/Web/Plugin/Device/Ports.pm
|
lib/App/Netdisco/Web/Plugin/Device/Ports.pm
|
||||||
lib/App/Netdisco/Web/Plugin/Inventory.pm
|
lib/App/Netdisco/Web/Plugin/Inventory.pm
|
||||||
lib/App/Netdisco/Web/Plugin/Report/DuplexMismatch.pm
|
lib/App/Netdisco/Web/Plugin/Report/DuplexMismatch.pm
|
||||||
|
lib/App/Netdisco/Web/Plugin/Report/PortUtilization.pm
|
||||||
lib/App/Netdisco/Web/Plugin/Search/Device.pm
|
lib/App/Netdisco/Web/Plugin/Search/Device.pm
|
||||||
lib/App/Netdisco/Web/Plugin/Search/Node.pm
|
lib/App/Netdisco/Web/Plugin/Search/Node.pm
|
||||||
lib/App/Netdisco/Web/Plugin/Search/Port.pm
|
lib/App/Netdisco/Web/Plugin/Search/Port.pm
|
||||||
@@ -194,19 +198,23 @@ share/public/javascripts/jquery-deserialize.js
|
|||||||
share/public/javascripts/jquery-history.js
|
share/public/javascripts/jquery-history.js
|
||||||
share/public/javascripts/jquery-latest.min.js
|
share/public/javascripts/jquery-latest.min.js
|
||||||
share/public/javascripts/jquery-ui.custom.min.js
|
share/public/javascripts/jquery-ui.custom.min.js
|
||||||
|
share/public/javascripts/jquery.floatThead.js
|
||||||
share/public/javascripts/jquery.qtip.min.js
|
share/public/javascripts/jquery.qtip.min.js
|
||||||
share/public/javascripts/netdisco.js
|
share/public/javascripts/netdisco.js
|
||||||
share/public/javascripts/netdisco_portcontrol.js
|
share/public/javascripts/netdisco_portcontrol.js
|
||||||
share/public/javascripts/toastr.js
|
share/public/javascripts/toastr.js
|
||||||
|
share/public/javascripts/underscore.min.js
|
||||||
share/views/admintask.tt
|
share/views/admintask.tt
|
||||||
share/views/ajax/admintask/jobqueue.tt
|
share/views/ajax/admintask/jobqueue.tt
|
||||||
share/views/ajax/admintask/pseudodevice.tt
|
share/views/ajax/admintask/pseudodevice.tt
|
||||||
share/views/ajax/admintask/topology.tt
|
share/views/ajax/admintask/topology.tt
|
||||||
|
share/views/ajax/admintask/users.tt
|
||||||
share/views/ajax/device/addresses.tt
|
share/views/ajax/device/addresses.tt
|
||||||
share/views/ajax/device/details.tt
|
share/views/ajax/device/details.tt
|
||||||
share/views/ajax/device/netmap.tt
|
share/views/ajax/device/netmap.tt
|
||||||
share/views/ajax/device/ports.tt
|
share/views/ajax/device/ports.tt
|
||||||
share/views/ajax/report/duplexmismatch.tt
|
share/views/ajax/report/duplexmismatch.tt
|
||||||
|
share/views/ajax/report/portutilization.tt
|
||||||
share/views/ajax/search/device.tt
|
share/views/ajax/search/device.tt
|
||||||
share/views/ajax/search/node_by_ip.tt
|
share/views/ajax/search/node_by_ip.tt
|
||||||
share/views/ajax/search/node_by_mac.tt
|
share/views/ajax/search/node_by_mac.tt
|
||||||
|
|||||||
@@ -60,4 +60,4 @@ resources:
|
|||||||
homepage: http://netdisco.org/
|
homepage: http://netdisco.org/
|
||||||
license: http://opensource.org/licenses/bsd-license.php
|
license: http://opensource.org/licenses/bsd-license.php
|
||||||
repository: git://git.code.sf.net/p/netdisco/netdisco-ng
|
repository: git://git.code.sf.net/p/netdisco/netdisco-ng
|
||||||
version: 2.010004
|
version: 2.011000
|
||||||
|
|||||||
@@ -7,7 +7,7 @@ use 5.010_000;
|
|||||||
use File::ShareDir 'dist_dir';
|
use File::ShareDir 'dist_dir';
|
||||||
use Path::Class;
|
use Path::Class;
|
||||||
|
|
||||||
our $VERSION = '2.010004';
|
our $VERSION = '2.011000';
|
||||||
|
|
||||||
BEGIN {
|
BEGIN {
|
||||||
if (not ($ENV{DANCER_APPDIR} || '')
|
if (not ($ENV{DANCER_APPDIR} || '')
|
||||||
|
|||||||
@@ -8,7 +8,7 @@ use base 'DBIx::Class::Schema';
|
|||||||
|
|
||||||
__PACKAGE__->load_namespaces;
|
__PACKAGE__->load_namespaces;
|
||||||
|
|
||||||
our $VERSION = 23; # schema version used for upgrades, keep as integer
|
our $VERSION = 24; # schema version used for upgrades, keep as integer
|
||||||
|
|
||||||
use Path::Class;
|
use Path::Class;
|
||||||
use File::Basename;
|
use File::Basename;
|
||||||
|
|||||||
@@ -65,6 +65,8 @@ __PACKAGE__->add_columns(
|
|||||||
{ data_type => "integer", is_nullable => 1 },
|
{ data_type => "integer", is_nullable => 1 },
|
||||||
"snmp_comm",
|
"snmp_comm",
|
||||||
{ data_type => "text", is_nullable => 1 },
|
{ data_type => "text", is_nullable => 1 },
|
||||||
|
"snmp_comm_rw",
|
||||||
|
{ data_type => "text", is_nullable => 1 },
|
||||||
"snmp_class",
|
"snmp_class",
|
||||||
{ data_type => "text", is_nullable => 1 },
|
{ data_type => "text", is_nullable => 1 },
|
||||||
"vtp_domain",
|
"vtp_domain",
|
||||||
|
|||||||
@@ -0,0 +1,46 @@
|
|||||||
|
package App::Netdisco::DB::Result::Virtual::PortUtilization;
|
||||||
|
|
||||||
|
use strict;
|
||||||
|
use warnings;
|
||||||
|
|
||||||
|
use base 'DBIx::Class::Core';
|
||||||
|
|
||||||
|
__PACKAGE__->table_class('DBIx::Class::ResultSource::View');
|
||||||
|
|
||||||
|
__PACKAGE__->table('port_utilization');
|
||||||
|
__PACKAGE__->result_source_instance->is_virtual(1);
|
||||||
|
__PACKAGE__->result_source_instance->view_definition(<<ENDSQL
|
||||||
|
SELECT d.dns AS dns, d.ip as ip,
|
||||||
|
sum(CASE WHEN (dp.type != 'propVirtual') THEN 1 ELSE 0 END) as port_count,
|
||||||
|
sum(CASE WHEN (dp.type != 'propVirtual' AND dp.up_admin = 'up' AND dp.up = 'up') THEN 1 ELSE 0 END) as ports_in_use,
|
||||||
|
sum(CASE WHEN (dp.type != 'propVirtual' AND dp.up_admin != 'up') THEN 1 ELSE 0 END) as ports_shutdown,
|
||||||
|
sum(CASE WHEN (dp.type != 'propVirtual' AND dp.up_admin = 'up' AND dp.up != 'up') THEN 1 ELSE 0 END) as ports_free
|
||||||
|
FROM device d LEFT JOIN device_port dp
|
||||||
|
ON d.ip = dp.ip
|
||||||
|
GROUP BY d.dns, d.ip
|
||||||
|
ORDER BY d.dns, d.ip
|
||||||
|
ENDSQL
|
||||||
|
);
|
||||||
|
|
||||||
|
__PACKAGE__->add_columns(
|
||||||
|
'dns' => {
|
||||||
|
data_type => 'text',
|
||||||
|
},
|
||||||
|
'ip' => {
|
||||||
|
data_type => 'inet',
|
||||||
|
},
|
||||||
|
'port_count' => {
|
||||||
|
data_type => 'integer',
|
||||||
|
},
|
||||||
|
'ports_in_use' => {
|
||||||
|
data_type => 'integer',
|
||||||
|
},
|
||||||
|
'ports_shutdown' => {
|
||||||
|
data_type => 'integer',
|
||||||
|
},
|
||||||
|
'ports_free' => {
|
||||||
|
data_type => 'integer',
|
||||||
|
},
|
||||||
|
);
|
||||||
|
|
||||||
|
1;
|
||||||
@@ -511,12 +511,16 @@ sub with_port_count {
|
|||||||
->search_rs($cond, $attrs)
|
->search_rs($cond, $attrs)
|
||||||
->search({},
|
->search({},
|
||||||
{
|
{
|
||||||
'+columns' => { port_count =>
|
'+columns' => {
|
||||||
|
port_count =>
|
||||||
$rs->result_source->schema->resultset('DevicePort')
|
$rs->result_source->schema->resultset('DevicePort')
|
||||||
->search(
|
->search(
|
||||||
{ 'dp.ip' => { -ident => 'me.ip' } },
|
{
|
||||||
|
'dp.ip' => { -ident => 'me.ip' },
|
||||||
|
'dp.type' => { '!=' => 'propVirtual' },
|
||||||
|
},
|
||||||
{ alias => 'dp' }
|
{ alias => 'dp' }
|
||||||
)->count_rs->as_query
|
)->count_rs->as_query,
|
||||||
},
|
},
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,5 @@
|
|||||||
|
BEGIN;
|
||||||
|
|
||||||
|
ALTER TABLE device ADD COLUMN snmp_comm_rw text;
|
||||||
|
|
||||||
|
COMMIT;
|
||||||
@@ -101,8 +101,9 @@ Value: Boolean. Default: C<false>.
|
|||||||
|
|
||||||
Enable this if Netdisco is running within another web server such as Apache,
|
Enable this if Netdisco is running within another web server such as Apache,
|
||||||
and you want that server to handle user authentication. Normally the
|
and you want that server to handle user authentication. Normally the
|
||||||
authenticated username will automatically be set in the C<REMOTE_USER> HTTP
|
authenticated username will automatically be set in the C<REMOTE_USER>
|
||||||
Header. See L<Dancer::Deployment/Running from Apache> for further details.
|
environment variable. See L<Dancer::Deployment/Running from Apache> for
|
||||||
|
further details.
|
||||||
|
|
||||||
=head3 C<trust_x_remote_user>
|
=head3 C<trust_x_remote_user>
|
||||||
|
|
||||||
@@ -110,8 +111,8 @@ Value: Boolean. Default: C<false>.
|
|||||||
|
|
||||||
Enable this if you proxy requests to Netdisco via another web server such as
|
Enable this if you proxy requests to Netdisco via another web server such as
|
||||||
Apache, and you want that server to handle user authentication. You need to
|
Apache, and you want that server to handle user authentication. You need to
|
||||||
configure the authorized username to be passed in the C<X-REMOTE_USER> HTTP
|
configure the authorized username to be passed from the frontend environment
|
||||||
Header. For example with Apache:
|
to Netdisco in the C<X-REMOTE_USER> HTTP Header. For example with Apache:
|
||||||
|
|
||||||
RequestHeader unset X-REMOTE_USER
|
RequestHeader unset X-REMOTE_USER
|
||||||
RequestHeader set X-REMOTE_USER "%{REMOTE_USER}e" env=REMOTE_USER
|
RequestHeader set X-REMOTE_USER "%{REMOTE_USER}e" env=REMOTE_USER
|
||||||
|
|||||||
@@ -75,6 +75,16 @@ To delegate user authentication to Apache, use the C<trust_remote_user> or
|
|||||||
C<trust_x_remote_user> settings. See L<App::Netdisco::Manual::Configuration>
|
C<trust_x_remote_user> settings. See L<App::Netdisco::Manual::Configuration>
|
||||||
for more details.
|
for more details.
|
||||||
|
|
||||||
|
=head1 SSL Support
|
||||||
|
|
||||||
|
There is no SSL support in the built-in web server. This is because it's not
|
||||||
|
straightforward to support all the SSL options, and using port 443 requires
|
||||||
|
root privilege, which the Netdisco application should not have.
|
||||||
|
|
||||||
|
You are instead recommended to run C<netdisco-web> behind a reverse proxy as
|
||||||
|
described elsewhere in this document. Apache can easily act as an SSL reverse
|
||||||
|
proxy.
|
||||||
|
|
||||||
=head1 SQL and HTTP Trace
|
=head1 SQL and HTTP Trace
|
||||||
|
|
||||||
For SQL debugging try the following commands:
|
For SQL debugging try the following commands:
|
||||||
|
|||||||
@@ -21,8 +21,8 @@ the more verbose C<plugins/DBIC> setting which was there before:
|
|||||||
user: 'someuser'
|
user: 'someuser'
|
||||||
pass: 'somepass'
|
pass: 'somepass'
|
||||||
|
|
||||||
Also, the C<REMOTE_USER> and C<X-REMOTE_USER> environment variables are now
|
Also, the C<REMOTE_USER> environment variable and C<X-REMOTE_USER> HTTP Header
|
||||||
supported for delegating authentication to another web server. See the
|
are now supported for delegating authentication to another web server. See the
|
||||||
Deployment and Configuration documentation for further details.
|
Deployment and Configuration documentation for further details.
|
||||||
|
|
||||||
=head1 2.008000
|
=head1 2.008000
|
||||||
|
|||||||
@@ -90,6 +90,9 @@ sub _snmp_connect_generic {
|
|||||||
unshift @communities, $device->snmp_comm
|
unshift @communities, $device->snmp_comm
|
||||||
if defined $device->snmp_comm
|
if defined $device->snmp_comm
|
||||||
and defined $comm_type and $comm_type eq 'community';
|
and defined $comm_type and $comm_type eq 'community';
|
||||||
|
unshift @communities, $device->snmp_comm_rw
|
||||||
|
if defined $device->snmp_comm_rw
|
||||||
|
and defined $comm_type and $comm_type eq 'community_rw';
|
||||||
|
|
||||||
my $info = undef;
|
my $info = undef;
|
||||||
VERSION: foreach my $ver (@versions) {
|
VERSION: foreach my $ver (@versions) {
|
||||||
@@ -101,8 +104,13 @@ sub _snmp_connect_generic {
|
|||||||
COMMUNITY: foreach my $comm (@communities) {
|
COMMUNITY: foreach my $comm (@communities) {
|
||||||
next unless $comm;
|
next unless $comm;
|
||||||
|
|
||||||
$info = _try_connect($ver, $class, $comm, \%snmp_args)
|
$info = _try_connect($ver, $class, $comm, \%snmp_args);
|
||||||
and last VERSION;
|
|
||||||
|
if ($comm_type eq 'community_rw') {
|
||||||
|
_try_write($info, $comm, $device) or next COMMUNITY;
|
||||||
|
}
|
||||||
|
|
||||||
|
last VERSION if $info;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -110,6 +118,25 @@ sub _snmp_connect_generic {
|
|||||||
return $info;
|
return $info;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
sub _try_write {
|
||||||
|
my ($info, $comm, $device) = @_;
|
||||||
|
my $happy = 0;
|
||||||
|
|
||||||
|
try {
|
||||||
|
debug sprintf '[%s] try_write with comm: %s', $device->ip, $comm;
|
||||||
|
$info->clear_cache;
|
||||||
|
my $rv = $info->set_location( $info->location );
|
||||||
|
$device->update({snmp_comm_rw => $comm})
|
||||||
|
if $device->in_storage;
|
||||||
|
$happy = 1 if $rv;
|
||||||
|
}
|
||||||
|
catch {
|
||||||
|
debug $_;
|
||||||
|
};
|
||||||
|
|
||||||
|
return $happy;
|
||||||
|
}
|
||||||
|
|
||||||
sub _try_connect {
|
sub _try_connect {
|
||||||
my ($ver, $class, $comm, $snmp_args) = @_;
|
my ($ver, $class, $comm, $snmp_args) = @_;
|
||||||
my $info = undef;
|
my $info = undef;
|
||||||
@@ -154,7 +181,7 @@ sub _build_mibdirs {
|
|||||||
|
|
||||||
sub _get_mibdirs_content {
|
sub _get_mibdirs_content {
|
||||||
my $home = shift;
|
my $home = shift;
|
||||||
warning 'Netdisco SNMP work will be slow - loading ALL MIBs. Consider setting mibdirs.';
|
# warning 'Netdisco SNMP work will be slow - loading ALL MIBs. Consider setting mibdirs.';
|
||||||
my @list = map {s|$home/||; $_} grep {-d} glob("$home/*");
|
my @list = map {s|$home/||; $_} grep {-d} glob("$home/*");
|
||||||
return \@list;
|
return \@list;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -10,8 +10,8 @@ hook 'before' => sub {
|
|||||||
if (setting('trust_x_remote_user') and scalar request->header('X-REMOTE_USER')) {
|
if (setting('trust_x_remote_user') and scalar request->header('X-REMOTE_USER')) {
|
||||||
session(user => scalar request->header('X-REMOTE_USER'));
|
session(user => scalar request->header('X-REMOTE_USER'));
|
||||||
}
|
}
|
||||||
elsif (setting('trust_remote_user') and scalar request->header('REMOTE_USER')) {
|
elsif (setting('trust_remote_user') and $ENV{REMOTE_USER}) {
|
||||||
session(user => scalar request->header('REMOTE_USER'));
|
session(user => $ENV{REMOTE_USER});
|
||||||
}
|
}
|
||||||
elsif (setting('no_auth')) {
|
elsif (setting('no_auth')) {
|
||||||
session(user => 'guest');
|
session(user => 'guest');
|
||||||
|
|||||||
@@ -21,6 +21,14 @@ ajax '/ajax/control/admin/jobqueue/del' => sub {
|
|||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
||||||
|
ajax '/ajax/control/admin/jobqueue/delall' => sub {
|
||||||
|
send_error('Forbidden', 403) unless var('user')->admin;
|
||||||
|
|
||||||
|
schema('netdisco')->txn_do(sub {
|
||||||
|
my $device = schema('netdisco')->resultset('Admin')->delete;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
ajax '/ajax/content/admin/jobqueue' => sub {
|
ajax '/ajax/content/admin/jobqueue' => sub {
|
||||||
send_error('Forbidden', 403) unless var('user')->admin;
|
send_error('Forbidden', 403) unless var('user')->admin;
|
||||||
|
|
||||||
|
|||||||
80
Netdisco/lib/App/Netdisco/Web/Plugin/AdminTask/Users.pm
Normal file
80
Netdisco/lib/App/Netdisco/Web/Plugin/AdminTask/Users.pm
Normal file
@@ -0,0 +1,80 @@
|
|||||||
|
package App::Netdisco::Web::Plugin::AdminTask::Users;
|
||||||
|
|
||||||
|
use Dancer ':syntax';
|
||||||
|
use Dancer::Plugin::Ajax;
|
||||||
|
use Dancer::Plugin::DBIC;
|
||||||
|
|
||||||
|
use App::Netdisco::Web::Plugin;
|
||||||
|
use Digest::MD5 ();
|
||||||
|
|
||||||
|
register_admin_task({
|
||||||
|
tag => 'users',
|
||||||
|
label => 'User Management',
|
||||||
|
});
|
||||||
|
|
||||||
|
sub _sanity_ok {
|
||||||
|
return 0 unless var('user') and var('user')->admin;
|
||||||
|
|
||||||
|
return 0 unless param('username')
|
||||||
|
and param('username') =~ m/^[[:print:]]+$/
|
||||||
|
and param('username') !~ m/[[:space:]]/;
|
||||||
|
|
||||||
|
return 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
ajax '/ajax/control/admin/users/add' => sub {
|
||||||
|
send_error('Bad Request', 400) unless _sanity_ok();
|
||||||
|
|
||||||
|
schema('netdisco')->txn_do(sub {
|
||||||
|
my $user = schema('netdisco')->resultset('User')
|
||||||
|
->create({
|
||||||
|
username => param('username'),
|
||||||
|
password => Digest::MD5::md5_hex(param('password')),
|
||||||
|
fullname => param('fullname'),
|
||||||
|
port_control => (param('port_control') ? \'true' : \'false'),
|
||||||
|
admin => (param('admin') ? \'true' : \'false'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
ajax '/ajax/control/admin/users/del' => sub {
|
||||||
|
send_error('Bad Request', 400) unless _sanity_ok();
|
||||||
|
|
||||||
|
schema('netdisco')->txn_do(sub {
|
||||||
|
schema('netdisco')->resultset('User')
|
||||||
|
->find({username => param('username')})->delete;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
ajax '/ajax/control/admin/users/update' => sub {
|
||||||
|
send_error('Bad Request', 400) unless _sanity_ok();
|
||||||
|
|
||||||
|
schema('netdisco')->txn_do(sub {
|
||||||
|
my $user = schema('netdisco')->resultset('User')
|
||||||
|
->find({username => param('username')});
|
||||||
|
return unless $user;
|
||||||
|
|
||||||
|
$user->update({
|
||||||
|
((param('password') ne '********')
|
||||||
|
? (password => Digest::MD5::md5_hex(param('password')))
|
||||||
|
: ()),
|
||||||
|
fullname => param('fullname'),
|
||||||
|
port_control => (param('port_control') ? \'true' : \'false'),
|
||||||
|
admin => (param('admin') ? \'true' : \'false'),
|
||||||
|
});
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
ajax '/ajax/content/admin/users' => sub {
|
||||||
|
send_error('Forbidden', 403) unless var('user')->admin;
|
||||||
|
|
||||||
|
my $set = schema('netdisco')->resultset('User')
|
||||||
|
->search(undef, { order_by => [qw/fullname username/]});
|
||||||
|
|
||||||
|
content_type('text/html');
|
||||||
|
template 'ajax/admintask/users.tt', {
|
||||||
|
results => $set,
|
||||||
|
}, { layout => undef };
|
||||||
|
};
|
||||||
|
|
||||||
|
true;
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
package App::Netdisco::Web::Plugin::Report::PortUtilization;
|
||||||
|
|
||||||
|
use Dancer ':syntax';
|
||||||
|
use Dancer::Plugin::Ajax;
|
||||||
|
use Dancer::Plugin::DBIC;
|
||||||
|
|
||||||
|
use App::Netdisco::Web::Plugin;
|
||||||
|
|
||||||
|
register_report({
|
||||||
|
category => 'Device',
|
||||||
|
tag => 'portutilization',
|
||||||
|
label => 'Port Utilization',
|
||||||
|
});
|
||||||
|
|
||||||
|
ajax '/ajax/content/report/portutilization' => sub {
|
||||||
|
return unless schema('netdisco')->resultset('Device')->count;
|
||||||
|
my $set = schema('netdisco')->resultset('Virtual::PortUtilization');
|
||||||
|
|
||||||
|
content_type('text/html');
|
||||||
|
template 'ajax/report/portutilization.tt', {
|
||||||
|
results => $set,
|
||||||
|
}, { layout => undef };
|
||||||
|
};
|
||||||
|
|
||||||
|
true;
|
||||||
@@ -23,10 +23,12 @@ path: '/'
|
|||||||
behind_proxy: false
|
behind_proxy: false
|
||||||
web_plugins:
|
web_plugins:
|
||||||
- Inventory
|
- Inventory
|
||||||
|
- Report::PortUtilization
|
||||||
- Report::DuplexMismatch
|
- Report::DuplexMismatch
|
||||||
- AdminTask::PseudoDevice
|
- AdminTask::PseudoDevice
|
||||||
- AdminTask::Topology
|
- AdminTask::Topology
|
||||||
- AdminTask::JobQueue
|
- AdminTask::JobQueue
|
||||||
|
- AdminTask::Users
|
||||||
- Search::Device
|
- Search::Device
|
||||||
- Search::Node
|
- Search::Node
|
||||||
- Search::VLAN
|
- Search::VLAN
|
||||||
|
|||||||
@@ -28,6 +28,11 @@ body {
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* results table header should have a background, for floatThead */
|
||||||
|
div.content > div.tab-content table.nd_floatinghead thead {
|
||||||
|
background-color: floralWhite;
|
||||||
|
}
|
||||||
|
|
||||||
/* jquery ui autocomplete scrollable */
|
/* jquery ui autocomplete scrollable */
|
||||||
.ui-autocomplete {
|
.ui-autocomplete {
|
||||||
max-height: 200px;
|
max-height: 200px;
|
||||||
@@ -70,6 +75,11 @@ body {
|
|||||||
line-height: 8px;
|
line-height: 8px;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* for where min-width is set but we don't want it */
|
||||||
|
.nd_no-min-width {
|
||||||
|
min-width: 0px;
|
||||||
|
}
|
||||||
|
|
||||||
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
/* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ */
|
||||||
/* styles to adjust the hero box used for homepage + login */
|
/* styles to adjust the hero box used for homepage + login */
|
||||||
|
|
||||||
|
|||||||
612
Netdisco/share/public/javascripts/jquery.floatThead.js
Normal file
612
Netdisco/share/public/javascripts/jquery.floatThead.js
Normal file
@@ -0,0 +1,612 @@
|
|||||||
|
/*!
|
||||||
|
* jQuery.floatThead
|
||||||
|
* Copyright (c) 2012 - 2013 Misha Koryak - https://github.com/mkoryak/floatThead
|
||||||
|
* Licensed under Creative Commons Attribution-NonCommercial 3.0 Unported - http://creativecommons.org/licenses/by-sa/3.0/
|
||||||
|
* Date: 8/25/13
|
||||||
|
*
|
||||||
|
* @projectDescription lock a table header in place while scrolling - without breaking styles or events bound to the header
|
||||||
|
*
|
||||||
|
* Dependencies:
|
||||||
|
* jquery 1.9.0 + [required] OR jquery 1.7.0 + jquery UI core
|
||||||
|
* underscore.js 1.3.0 + [required]
|
||||||
|
*
|
||||||
|
* http://notetodogself.blogspot.com
|
||||||
|
* http://programmingdrunk.com/floatThead/
|
||||||
|
*
|
||||||
|
* Tested on FF13+, Chrome 21+, IE9, IE8
|
||||||
|
*
|
||||||
|
* @author Misha Koryak
|
||||||
|
* @version 1.0.0
|
||||||
|
*/
|
||||||
|
// ==ClosureCompiler==
|
||||||
|
// @compilation_level SIMPLE_OPTIMIZATIONS
|
||||||
|
// @output_file_name jquery.floatThead.min.js
|
||||||
|
// ==/ClosureCompiler==
|
||||||
|
/**
|
||||||
|
* @preserve jQuery.floatThead 1.0.0
|
||||||
|
* Copyright (c) 2013 Misha Koryak - https://github.com/mkoryak/floatThead
|
||||||
|
* Licensed under Creative Commons Attribution-NonCommercial 3.0 Unported - http://creativecommons.org/licenses/by-sa/3.0/
|
||||||
|
*/
|
||||||
|
(function( $ ) {
|
||||||
|
|
||||||
|
//browser stuff
|
||||||
|
var ieVersion = function(){for(var a=3,b=document.createElement("b"),c=b.all||[];b.innerHTML="<!--[if gt IE "+ ++a+"]><i><![endif]-->",c[0];);return 4<a?a:document.documentMode}();
|
||||||
|
var ifChrome = function(){
|
||||||
|
var $table = $("<table><colgroup><col></colgroup><tbody><tr><td style='width:10px'></td></tbody></table>");
|
||||||
|
$('body').append($table);
|
||||||
|
var width = $table.find('col').width();
|
||||||
|
$table.remove();
|
||||||
|
return width == 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
/**
|
||||||
|
* provides a default config object. You can modify this after including this script if you want to change the init defaults
|
||||||
|
* @type {Object}
|
||||||
|
*/
|
||||||
|
$.floatThead = {
|
||||||
|
defaults: {
|
||||||
|
cellTag: 'th',
|
||||||
|
zIndex: 1001, //zindex of the floating thead (actually a container div)
|
||||||
|
debounceResizeMs: 1,
|
||||||
|
useAbsolutePositioning: true, //if set to NULL - defaults: has scrollContainer=true, doesnt have scrollContainer=false
|
||||||
|
scrollingTop: 0, //String or function($table) - offset from top of window where the header should not pass above
|
||||||
|
//TODO: this got lost somewhere - needs to be re-implemented
|
||||||
|
scrollingBottom: 0, //String or function($table) - offset from the bottom of the table where the header should stop scrolling
|
||||||
|
scrollContainer: function($table){
|
||||||
|
return $([]); //if the table has horizontal scroll bars then this is the container that has overflow:auto and causes those scroll bars
|
||||||
|
},
|
||||||
|
floatTableClass: 'floatThead-table'
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
var $window = $(window);
|
||||||
|
var floatTheadCreated = 0;
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* debounce and fix window resize event for ie7. ie7 is evil and will fire window resize event when ANY dom element is resized.
|
||||||
|
* @param debounceMs
|
||||||
|
* @param cb
|
||||||
|
*/
|
||||||
|
|
||||||
|
function windowResize(debounceMs, cb){
|
||||||
|
var winWidth = $window.width();
|
||||||
|
var debouncedCb = _.debounce(function(){
|
||||||
|
var winWidthNew = $window.width();
|
||||||
|
if(winWidth != winWidthNew){
|
||||||
|
winWidth = winWidthNew;
|
||||||
|
cb();
|
||||||
|
}
|
||||||
|
}, debounceMs);
|
||||||
|
$window.bind('resize.floatTHead', debouncedCb);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* try to calculate the scrollbar width for your browser/os
|
||||||
|
* @return {Number}
|
||||||
|
*/
|
||||||
|
function scrollbarWidth() {
|
||||||
|
var $div = $('<div/>')
|
||||||
|
.css({ width: 100, height: 100, overflow: 'auto', position: 'absolute', top: -1000, left: -1000 })
|
||||||
|
.prependTo('body').append('<div/>').find('div')
|
||||||
|
.css({ width: '100%', height: 200 });
|
||||||
|
var scrollbarWidth = 100 - $div.width();
|
||||||
|
$div.parent().remove();
|
||||||
|
return scrollbarWidth;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Check if a given table has been datatableized (http://datatables.net)
|
||||||
|
* @param $table
|
||||||
|
* @return {Boolean}
|
||||||
|
*/
|
||||||
|
function isDatatable($table){
|
||||||
|
if($table.dataTableSettings){
|
||||||
|
for(var i = 0; i < $table.dataTableSettings.length; i++){
|
||||||
|
var table = $table.dataTableSettings[i].nTable;
|
||||||
|
if($table[0] == table){
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
$.fn.floatThead = function(map){
|
||||||
|
if(ieVersion < 8){
|
||||||
|
return this; //no more crappy browser support.
|
||||||
|
}
|
||||||
|
|
||||||
|
isChrome = ifChrome(); //need to call this after dom ready, and now it is.
|
||||||
|
if(isChrome){
|
||||||
|
//because chrome cant read <col> width, these elements are used for sizing the table. Need to create new elements because they must be unstyled by user's css.
|
||||||
|
document.createElement('fthtr'); //tr
|
||||||
|
document.createElement('fthtd'); //td
|
||||||
|
document.createElement('fthfoot'); //tfoot
|
||||||
|
}
|
||||||
|
if(_.isString(map)){
|
||||||
|
var command = map;
|
||||||
|
var ret = this;
|
||||||
|
this.filter('table').each(function(){
|
||||||
|
var obj = $(this).data('floatThead-attached');
|
||||||
|
if(obj && _.isFunction(obj[command])){
|
||||||
|
r = obj[command]();
|
||||||
|
if(typeof r !== 'undefined'){
|
||||||
|
ret = r;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
return ret;
|
||||||
|
}
|
||||||
|
var opts = $.extend({}, $.floatThead.defaults, map);
|
||||||
|
|
||||||
|
|
||||||
|
this.filter(':not(.'+opts.floatTableClass+')').each(function(){
|
||||||
|
var $table = $(this);
|
||||||
|
if($table.data('floatThead-attached')){
|
||||||
|
return true; //continue the each loop
|
||||||
|
}
|
||||||
|
if(!$table.is('table')){
|
||||||
|
throw new Error('jQuery.floatThead must be run on a table element. ex: $("table").floatThead();');
|
||||||
|
}
|
||||||
|
var $header = $table.find('thead:first');
|
||||||
|
var $tbody = $table.find('tbody:first');
|
||||||
|
if($header.length == 0){
|
||||||
|
throw new Error('jQuery.floatThead must be run on a table that contains a <thead> element');
|
||||||
|
}
|
||||||
|
var headerFloated = true;
|
||||||
|
var scrollingTop, scrollingBottom;
|
||||||
|
var scrollbarOffset = {vertical: 0, horizontal: 0};
|
||||||
|
var scWidth = scrollbarWidth();
|
||||||
|
var lastColumnCount = 0; //used by columnNum()
|
||||||
|
var $scrollContainer = opts.scrollContainer($table) || $([]); //guard against returned nulls
|
||||||
|
|
||||||
|
var useAbsolutePositioning = opts.useAbsolutePositioning;
|
||||||
|
if(useAbsolutePositioning == null){ //defaults: locked=true, !locked=false
|
||||||
|
useAbsolutePositioning = opts.scrollContainer($table).length;
|
||||||
|
}
|
||||||
|
|
||||||
|
var $fthGrp = $('<fthfoot style="display:table-footer-group;"/>');
|
||||||
|
|
||||||
|
var locked = $scrollContainer.length > 0;
|
||||||
|
var wrappedContainer = false; //used with absolute positioning enabled. did we need to wrap the scrollContainer/table with a relative div?
|
||||||
|
var absoluteToFixedOnScroll = ieVersion && !locked && useAbsolutePositioning; //on ie using absolute positioning doesnt look good with window scrolling, so we change positon to fixed on scroll, and then change it back to absolute when done.
|
||||||
|
var $floatTable = $("<table/>");
|
||||||
|
var $floatColGroup = $("<colgroup/>");
|
||||||
|
var $tableColGroup = $("<colgroup/>");
|
||||||
|
var $fthRow = $('<fthrow style="display:table-row;height:0;"/>'); //created unstyled elements
|
||||||
|
var $floatContainer = $('<div style="overflow: hidden;"></div>');
|
||||||
|
var $newHeader = $("<thead/>");
|
||||||
|
var $sizerRow = $('<tr class="size-row"/>');
|
||||||
|
var $sizerCells = $([]);
|
||||||
|
var $tableCells = $([]); //used for sizing - either $sizerCells or $tableColGroup cols. $tableColGroup cols are only created in chrome for borderCollapse:collapse because of a chrome bug.
|
||||||
|
var $headerCells = $([]);
|
||||||
|
var $fthCells = $([]); //created elements
|
||||||
|
|
||||||
|
$newHeader.append($sizerRow);
|
||||||
|
$header.detach();
|
||||||
|
|
||||||
|
$table.prepend($newHeader);
|
||||||
|
$table.prepend($tableColGroup);
|
||||||
|
if(isChrome){
|
||||||
|
$fthGrp.append($fthRow);
|
||||||
|
$table.append($fthGrp);
|
||||||
|
}
|
||||||
|
|
||||||
|
$floatTable.append($floatColGroup);
|
||||||
|
$floatContainer.append($floatTable);
|
||||||
|
$floatTable.attr('class', $table.attr('class'));
|
||||||
|
$floatTable.addClass(opts.floatTableClass).css('margin', 0); //must have no margins or you wont be able to click on things under floating table
|
||||||
|
|
||||||
|
if(useAbsolutePositioning){
|
||||||
|
var makeRelative = function($container, alwaysWrap){
|
||||||
|
var positionCss = $container.css('position');
|
||||||
|
var relativeToScrollContainer = (positionCss == "relative" || positionCss == "absolute");
|
||||||
|
if(!relativeToScrollContainer || alwaysWrap){
|
||||||
|
var css = {"paddingLeft": $container.css('paddingLeft'), "paddingRight": $container.css('paddingRight')};
|
||||||
|
$floatContainer.css(css);
|
||||||
|
$container = $container.wrap("<div style='position: relative; clear:both;'></div>").parent();
|
||||||
|
wrappedContainer = true;
|
||||||
|
}
|
||||||
|
return $container;
|
||||||
|
};
|
||||||
|
if(locked){
|
||||||
|
var $relative = makeRelative($scrollContainer, true);
|
||||||
|
$relative.append($floatContainer);
|
||||||
|
} else {
|
||||||
|
makeRelative($table);
|
||||||
|
$table.after($floatContainer);
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$table.after($floatContainer);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
$floatContainer.css({
|
||||||
|
position: useAbsolutePositioning ? 'absolute' : 'fixed',
|
||||||
|
marginTop: 0,
|
||||||
|
top: useAbsolutePositioning ? 0 : 'auto',
|
||||||
|
zIndex: opts.zIndex
|
||||||
|
});
|
||||||
|
updateScrollingOffsets();
|
||||||
|
|
||||||
|
var layoutFixed = {'table-layout': 'fixed'};
|
||||||
|
var layoutAuto = {'table-layout': $table.css('tableLayout') || 'auto'};
|
||||||
|
|
||||||
|
function setHeaderHeight(){
|
||||||
|
var headerHeight = $header.outerHeight(true);
|
||||||
|
$sizerRow.outerHeight(headerHeight);
|
||||||
|
$sizerCells.outerHeight(headerHeight);
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function setFloatWidth(){
|
||||||
|
var tableWidth = $table.outerWidth();
|
||||||
|
var width = $scrollContainer.width() || tableWidth;
|
||||||
|
$floatContainer.width(width - scrollbarOffset.vertical);
|
||||||
|
if(locked){
|
||||||
|
var percent = 100 * tableWidth / (width - scrollbarOffset.vertical);
|
||||||
|
$floatTable.css('width', percent+'%');
|
||||||
|
} else {
|
||||||
|
$floatTable.outerWidth(tableWidth);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateScrollingOffsets(){
|
||||||
|
scrollingTop = (_.isFunction(opts.scrollingTop) ? opts.scrollingTop($table) : opts.scrollingTop) || 0;
|
||||||
|
scrollingBottom = (_.isFunction(opts.scrollingBottom) ? opts.scrollingBottom($table) : opts.scrollingBottom) || 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* get the number of columns and also rebuild resizer rows if the count is different then the last count
|
||||||
|
*/
|
||||||
|
function columnNum(){
|
||||||
|
var $headerColumns = $header.find('tr:first>'+opts.cellTag);
|
||||||
|
|
||||||
|
var count = _.reduce($headerColumns, function(sum, cell){
|
||||||
|
var colspan = parseInt(($(cell).attr('colspan') || 1), 10);
|
||||||
|
return sum + colspan;
|
||||||
|
}, 0);
|
||||||
|
if(count != lastColumnCount){
|
||||||
|
lastColumnCount = count;
|
||||||
|
var cells = [], cols = [], psuedo = [];
|
||||||
|
for(var x = 0; x < count; x++){
|
||||||
|
cells.push('<'+opts.cellTag+' class="floatThead-col-'+x+'"/>');
|
||||||
|
cols.push('<col/>');
|
||||||
|
psuedo.push("<fthcell style='display:table-cell;height:0;width:auto;'/>");
|
||||||
|
}
|
||||||
|
|
||||||
|
cols = cols.join('');
|
||||||
|
cells = cells.join('');
|
||||||
|
|
||||||
|
if(isChrome){
|
||||||
|
psuedo = psuedo.join('');
|
||||||
|
$fthRow.html(psuedo);
|
||||||
|
$fthCells = $fthRow.find('fthcell')
|
||||||
|
}
|
||||||
|
|
||||||
|
$sizerRow.html(cells);
|
||||||
|
$tableColGroup.html(cols);
|
||||||
|
$tableCells = $tableColGroup.find('col');
|
||||||
|
$floatColGroup.html(cols);
|
||||||
|
$headerCells = $floatColGroup.find("col");
|
||||||
|
|
||||||
|
}
|
||||||
|
return count;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
function refloat(){
|
||||||
|
if(!headerFloated){
|
||||||
|
headerFloated = true;
|
||||||
|
$table.css(layoutFixed);
|
||||||
|
$floatTable.css(layoutFixed);
|
||||||
|
$floatTable.append($header); //append because colgroup must go first in chrome
|
||||||
|
$tbody.before($newHeader);
|
||||||
|
setHeaderHeight();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function unfloat(){
|
||||||
|
if(headerFloated){
|
||||||
|
headerFloated = false;
|
||||||
|
$newHeader.detach();
|
||||||
|
$table.prepend($header);
|
||||||
|
$table.css(layoutAuto);
|
||||||
|
$floatTable.css(layoutAuto);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
function changePositioning(isAbsolute){
|
||||||
|
if(useAbsolutePositioning != isAbsolute){
|
||||||
|
useAbsolutePositioning = isAbsolute;
|
||||||
|
$floatContainer.css({
|
||||||
|
position: useAbsolutePositioning ? 'absolute' : 'fixed'
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* returns a function that updates the floating header's cell widths.
|
||||||
|
* @return {Function}
|
||||||
|
*/
|
||||||
|
function reflow(){
|
||||||
|
var numCols = columnNum(); //if the tables columns change dynamically since last time (datatables) we need to rebuild the sizer rows and get new count
|
||||||
|
var flow = function(){
|
||||||
|
var badReflow = false;
|
||||||
|
var $rowCells = isChrome ? $fthCells : $tableCells;
|
||||||
|
if($rowCells.length == numCols && numCols > 0){
|
||||||
|
unfloat();
|
||||||
|
for(var i=0; i < numCols; i++){
|
||||||
|
var $rowCell = $rowCells.eq(i);
|
||||||
|
var rowWidth = $rowCell.outerWidth(true);
|
||||||
|
$headerCells.eq(i).outerWidth(rowWidth);
|
||||||
|
$tableCells.eq(i).outerWidth(rowWidth);
|
||||||
|
}
|
||||||
|
refloat();
|
||||||
|
for(var i=0; i < numCols; i++){
|
||||||
|
var hw = $headerCells.eq(i).outerWidth(true);
|
||||||
|
var tw = $tableCells.eq(i).outerWidth(true);
|
||||||
|
if(hw != tw){
|
||||||
|
badReflow = true;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
$floatTable.append($header);
|
||||||
|
$table.css(layoutAuto);
|
||||||
|
$floatTable.css(layoutAuto);
|
||||||
|
setHeaderHeight();
|
||||||
|
}
|
||||||
|
return badReflow;
|
||||||
|
};
|
||||||
|
return flow;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* first performs initial calculations that we expect to not change when the table, window, or scrolling container are scrolled.
|
||||||
|
* returns a function that calculates the floating container's top and left coords. takes into account if we are using page scrolling or inner scrolling
|
||||||
|
* @return {Function}
|
||||||
|
*/
|
||||||
|
function calculateFloatContainerPosFn(){
|
||||||
|
var scrollingContainerTop = $scrollContainer.scrollTop();
|
||||||
|
|
||||||
|
//this floatEnd calc was moved out of the returned function because we assume the table height doesnt change (otherwise we must reinit by calling calculateFloatContainerPosFn)
|
||||||
|
var floatEnd;
|
||||||
|
var tableContainerGap = 0;
|
||||||
|
|
||||||
|
var floatContainerHeight = $floatContainer.height();
|
||||||
|
var tableOffset = $table.offset();
|
||||||
|
if(locked){
|
||||||
|
var containerOffset = $scrollContainer.offset();
|
||||||
|
tableContainerGap = tableOffset.top - containerOffset.top + scrollingContainerTop;
|
||||||
|
} else {
|
||||||
|
floatEnd = tableOffset.top - scrollingTop - floatContainerHeight + scrollingBottom + scrollbarOffset.horizontal;
|
||||||
|
}
|
||||||
|
var windowTop = $window.scrollTop();
|
||||||
|
var windowLeft = $window.scrollLeft();
|
||||||
|
var scrollContainerLeft = $scrollContainer.scrollLeft();
|
||||||
|
scrollingContainerTop = $scrollContainer.scrollTop();
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
var positionFn = function(eventType){
|
||||||
|
|
||||||
|
if(eventType == 'windowScroll'){
|
||||||
|
windowTop = $window.scrollTop();
|
||||||
|
windowLeft = $window.scrollLeft();
|
||||||
|
} else if(eventType == 'containerScroll'){
|
||||||
|
scrollingContainerTop = $scrollContainer.scrollTop();
|
||||||
|
scrollContainerLeft = $scrollContainer.scrollLeft();
|
||||||
|
} else if(eventType != 'init') {
|
||||||
|
windowTop = $window.scrollTop();
|
||||||
|
windowLeft = $window.scrollLeft();
|
||||||
|
scrollingContainerTop = $scrollContainer.scrollTop();
|
||||||
|
scrollContainerLeft = $scrollContainer.scrollLeft();
|
||||||
|
}
|
||||||
|
if(absoluteToFixedOnScroll){
|
||||||
|
if(eventType == 'windowScrollDone'){
|
||||||
|
changePositioning(true); //change to absolute
|
||||||
|
} else {
|
||||||
|
changePositioning(false); //change to fixed
|
||||||
|
}
|
||||||
|
} else if(eventType == 'windowScrollDone'){
|
||||||
|
return null; //event is fired when they stop scrolling. ignore it if not 'absoluteToFixedOnScroll'
|
||||||
|
}
|
||||||
|
|
||||||
|
tableOffset = $table.offset();
|
||||||
|
var top, left, tableHeight;
|
||||||
|
|
||||||
|
//absolute positioning
|
||||||
|
if(locked && useAbsolutePositioning){ //inner scrolling
|
||||||
|
if (tableContainerGap >= scrollingContainerTop) {
|
||||||
|
var gap = tableContainerGap - scrollingContainerTop;
|
||||||
|
gap = gap > 0 ? gap : 0;
|
||||||
|
top = gap;
|
||||||
|
// unfloat(); //more trouble than its worth
|
||||||
|
} else {
|
||||||
|
top = wrappedContainer ? 0 : scrollingContainerTop;
|
||||||
|
// refloat(); //more trouble than its worth
|
||||||
|
//headers stop at the top of the viewport
|
||||||
|
}
|
||||||
|
left = 0;
|
||||||
|
} else if(!locked && useAbsolutePositioning) { //window scrolling
|
||||||
|
tableHeight = $table.outerHeight();
|
||||||
|
if(windowTop > floatEnd + tableHeight){
|
||||||
|
top = tableHeight - floatContainerHeight; //scrolled past table
|
||||||
|
} else if (tableOffset.top > windowTop + scrollingTop) {
|
||||||
|
top = 0; //scrolling to table
|
||||||
|
unfloat();
|
||||||
|
} else {
|
||||||
|
top = scrollingTop + windowTop - tableOffset.top + tableContainerGap;
|
||||||
|
refloat(); //scrolling within table. header floated
|
||||||
|
}
|
||||||
|
left = 0;
|
||||||
|
|
||||||
|
//fixed positioning:
|
||||||
|
} else if(locked && !useAbsolutePositioning){ //inner scrolling
|
||||||
|
if (tableContainerGap > scrollingContainerTop) {
|
||||||
|
top = tableOffset.top - windowTop;
|
||||||
|
unfloat();
|
||||||
|
} else {
|
||||||
|
top = tableOffset.top + scrollingContainerTop - windowTop - tableContainerGap;
|
||||||
|
refloat();
|
||||||
|
//headers stop at the top of the viewport
|
||||||
|
}
|
||||||
|
left = tableOffset.left + scrollContainerLeft - windowLeft;
|
||||||
|
} else if(!locked && !useAbsolutePositioning) { //window scrolling
|
||||||
|
tableHeight = $table.outerHeight();
|
||||||
|
if(windowTop > floatEnd + tableHeight){
|
||||||
|
top = tableHeight + scrollingTop - windowTop + floatEnd;
|
||||||
|
unfloat();
|
||||||
|
} else if (tableOffset.top > windowTop + scrollingTop) {
|
||||||
|
top = tableOffset.top - windowTop;
|
||||||
|
refloat();
|
||||||
|
} else {
|
||||||
|
top = scrollingTop;
|
||||||
|
}
|
||||||
|
left = tableOffset.left - windowLeft;
|
||||||
|
}
|
||||||
|
|
||||||
|
return {top: top, left: left};
|
||||||
|
};
|
||||||
|
return positionFn;
|
||||||
|
}
|
||||||
|
/**
|
||||||
|
* returns a function that caches old floating container position and only updates css when the position changes
|
||||||
|
* @return {Function}
|
||||||
|
*/
|
||||||
|
function repositionFloatContainerFn(){
|
||||||
|
var oldTop = null;
|
||||||
|
var oldLeft = null;
|
||||||
|
var oldScrollLeft = null;
|
||||||
|
return function(pos, setWidth, setHeight){
|
||||||
|
if(pos != null && (oldTop != pos.top || oldLeft != pos.left)){
|
||||||
|
$floatContainer.css({
|
||||||
|
top: pos.top,
|
||||||
|
left: pos.left
|
||||||
|
});
|
||||||
|
oldTop = pos.top;
|
||||||
|
oldLeft = pos.left;
|
||||||
|
}
|
||||||
|
if(setWidth){
|
||||||
|
setFloatWidth();
|
||||||
|
}
|
||||||
|
if(setHeight){
|
||||||
|
setHeaderHeight();
|
||||||
|
}
|
||||||
|
var scrollLeft = $scrollContainer.scrollLeft();
|
||||||
|
if(oldScrollLeft != scrollLeft){
|
||||||
|
$floatContainer.scrollLeft(scrollLeft);
|
||||||
|
oldScrollLeft = scrollLeft;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* checks if THIS table has scrollbars, and finds their widths
|
||||||
|
*/
|
||||||
|
function calculateScrollBarSize(){ //this should happen after the floating table has been positioned
|
||||||
|
if($scrollContainer.length){
|
||||||
|
scrollbarOffset.horizontal = $scrollContainer.width() < $table.width() ? scWidth : 0;
|
||||||
|
scrollbarOffset.vertical = $scrollContainer.height() < $table.height() ? scWidth: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//finish up. create all calculation functions and bind them to events
|
||||||
|
calculateScrollBarSize();
|
||||||
|
|
||||||
|
var flow = reflow();
|
||||||
|
flow();
|
||||||
|
var calculateFloatContainerPos = calculateFloatContainerPosFn();
|
||||||
|
var repositionFloatContainer = repositionFloatContainerFn();
|
||||||
|
|
||||||
|
repositionFloatContainer(calculateFloatContainerPos('init'), true, true); //this must come after reflow because reflow changes scrollLeft back to 0 when it rips out the thead
|
||||||
|
|
||||||
|
var windowScrollDoneEvent = _.debounce(function(){
|
||||||
|
repositionFloatContainer(calculateFloatContainerPos('windowScrollDone'), false);
|
||||||
|
}, 300);
|
||||||
|
|
||||||
|
var windowScrollEvent = function(){
|
||||||
|
repositionFloatContainer(calculateFloatContainerPos('windowScroll'), false);
|
||||||
|
windowScrollDoneEvent();
|
||||||
|
};
|
||||||
|
var containerScrollEvent = function(){
|
||||||
|
repositionFloatContainer(calculateFloatContainerPos('containerScroll'), false);
|
||||||
|
};
|
||||||
|
|
||||||
|
var windowResizeEvent = function(){
|
||||||
|
updateScrollingOffsets();
|
||||||
|
calculateScrollBarSize();
|
||||||
|
flow = reflow();
|
||||||
|
var badReflow = flow();
|
||||||
|
if(badReflow){
|
||||||
|
flow();
|
||||||
|
}
|
||||||
|
calculateFloatContainerPos = calculateFloatContainerPosFn();
|
||||||
|
repositionFloatContainer = repositionFloatContainerFn();
|
||||||
|
repositionFloatContainer(calculateFloatContainerPos('resize'), true, true);
|
||||||
|
};
|
||||||
|
var reflowEvent = _.debounce(function(){
|
||||||
|
calculateScrollBarSize();
|
||||||
|
updateScrollingOffsets();
|
||||||
|
flow = reflow();
|
||||||
|
var badReflow = flow();
|
||||||
|
if(badReflow){
|
||||||
|
flow();
|
||||||
|
}
|
||||||
|
calculateFloatContainerPos = calculateFloatContainerPosFn();
|
||||||
|
repositionFloatContainer(calculateFloatContainerPos('reflow'), true);
|
||||||
|
}, 1);
|
||||||
|
if(locked){ //internal scrolling
|
||||||
|
if(useAbsolutePositioning){
|
||||||
|
$scrollContainer.bind('scroll.floatTHead', containerScrollEvent);
|
||||||
|
} else {
|
||||||
|
$scrollContainer.bind('scroll.floatTHead', containerScrollEvent);
|
||||||
|
$window.bind('scroll.floatTHead', windowScrollEvent);
|
||||||
|
}
|
||||||
|
} else { //window scrolling
|
||||||
|
$window.bind('scroll.floatTHead', windowScrollEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
$window.bind('load.floatTHead', reflowEvent); //for tables with images
|
||||||
|
|
||||||
|
windowResize(opts.debounceResizeMs, windowResizeEvent);
|
||||||
|
$table.bind('reflow', reflowEvent);
|
||||||
|
if(isDatatable($table)){
|
||||||
|
$table
|
||||||
|
.bind('filter', reflowEvent)
|
||||||
|
.bind('sort', reflowEvent)
|
||||||
|
.bind('page', reflowEvent);
|
||||||
|
}
|
||||||
|
|
||||||
|
//attach some useful functions to the table.
|
||||||
|
$table.data('floatThead-attached', {
|
||||||
|
destroy: function(){
|
||||||
|
$table.css(layoutAuto);
|
||||||
|
$tableColGroup.remove();
|
||||||
|
$fthGrp.remove();
|
||||||
|
$newHeader.replaceWith($header);
|
||||||
|
$table.unbind('reflow');
|
||||||
|
reflowEvent = windowResizeEvent = containerScrollEvent = windowScrollEvent = function() {};
|
||||||
|
$scrollContainer.unbind('scroll.floatTHead');
|
||||||
|
$floatContainer.remove();
|
||||||
|
$table.data('floatThead-attached', false);
|
||||||
|
floatTheadCreated--;
|
||||||
|
if(floatTheadCreated == 0){
|
||||||
|
$window.unbind('scroll.floatTHead');
|
||||||
|
$window.unbind('resize.floatTHead');
|
||||||
|
$window.unbind('load.floatTHead');
|
||||||
|
}
|
||||||
|
},
|
||||||
|
reflow: function(){
|
||||||
|
reflowEvent();
|
||||||
|
},
|
||||||
|
setHeaderHeight: function(){
|
||||||
|
setHeaderHeight();
|
||||||
|
},
|
||||||
|
getFloatContainer: function(){
|
||||||
|
return $floatContainer;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
floatTheadCreated++;
|
||||||
|
});
|
||||||
|
return this;
|
||||||
|
};
|
||||||
|
})(jQuery);
|
||||||
@@ -78,6 +78,10 @@ function do_search (event, tab) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// delegate to any [device|search] specific JS code
|
// delegate to any [device|search] specific JS code
|
||||||
|
$('div.content > div.tab-content table.nd_floatinghead').floatThead({
|
||||||
|
scrollingTop: 40
|
||||||
|
,useAbsolutePositioning: false
|
||||||
|
});
|
||||||
inner_view_processing(tab);
|
inner_view_processing(tab);
|
||||||
}
|
}
|
||||||
);
|
);
|
||||||
@@ -216,11 +220,21 @@ $(document).ready(function() {
|
|||||||
$('.nd_sidebar').toggle(250);
|
$('.nd_sidebar').toggle(250);
|
||||||
$('#nd_sidebar-toggle-img-out').toggle();
|
$('#nd_sidebar-toggle-img-out').toggle();
|
||||||
$('.content').css('margin-right', '10px');
|
$('.content').css('margin-right', '10px');
|
||||||
|
$('div.content > div.tab-content table.nd_floatinghead').floatThead('destroy');
|
||||||
|
$('div.content > div.tab-content table.nd_floatinghead').floatThead({
|
||||||
|
scrollingTop: 40
|
||||||
|
,useAbsolutePositioning: false
|
||||||
|
});
|
||||||
sidebar_hidden = 1;
|
sidebar_hidden = 1;
|
||||||
});
|
});
|
||||||
$('#nd_sidebar-toggle-img-out').click(function() {
|
$('#nd_sidebar-toggle-img-out').click(function() {
|
||||||
$('#nd_sidebar-toggle-img-out').toggle();
|
$('#nd_sidebar-toggle-img-out').toggle();
|
||||||
$('.content').css('margin-right', '215px');
|
$('.content').css('margin-right', '215px');
|
||||||
|
$('div.content > div.tab-content table.nd_floatinghead').floatThead('destroy');
|
||||||
|
$('div.content > div.tab-content table.nd_floatinghead').floatThead({
|
||||||
|
scrollingTop: 40
|
||||||
|
,useAbsolutePositioning: false
|
||||||
|
});
|
||||||
$('.nd_sidebar').toggle(250);
|
$('.nd_sidebar').toggle(250);
|
||||||
if (! $('.nd_sidebar').hasClass('nd_sidebar-pinned')) {
|
if (! $('.nd_sidebar').hasClass('nd_sidebar-pinned')) {
|
||||||
$(window).scrollTop(0);
|
$(window).scrollTop(0);
|
||||||
|
|||||||
6
Netdisco/share/public/javascripts/underscore.min.js
vendored
Normal file
6
Netdisco/share/public/javascripts/underscore.min.js
vendored
Normal file
File diff suppressed because one or more lines are too long
@@ -32,7 +32,7 @@
|
|||||||
<span id="nd_device-name">
|
<span id="nd_device-name">
|
||||||
<a id="nd_countdown-refresh" href="#"><i class="text-success icon-refresh"></i></a>
|
<a id="nd_countdown-refresh" href="#"><i class="text-success icon-refresh"></i></a>
|
||||||
<a id="nd_countdown-control" href="#">
|
<a id="nd_countdown-control" href="#">
|
||||||
<i id="nd_countdown-control-icon" class="text-error icon-pause"></i></a>
|
<i id="nd_countdown-control-icon" class="text-success icon-play"></i></a>
|
||||||
<span id="nd_countdown"></span>
|
<span id="nd_countdown"></span>
|
||||||
</span>
|
</span>
|
||||||
[% END %]
|
[% END %]
|
||||||
|
|||||||
@@ -1,4 +1,7 @@
|
|||||||
<table class="table table-bordered table-condensed table-hover">
|
[% IF results.count == 0 %]
|
||||||
|
<div class="span2 alert alert-info">The job queue is empty.</div>
|
||||||
|
[% ELSE %]
|
||||||
|
<table class="table table-bordered table-condensed table-hover nd_floatinghead">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="nd_center-cell">Entered</th>
|
<th class="nd_center-cell">Entered</th>
|
||||||
@@ -10,7 +13,21 @@
|
|||||||
<th class="nd_center-cell">User</th>
|
<th class="nd_center-cell">User</th>
|
||||||
<th class="nd_center-cell">Started</th>
|
<th class="nd_center-cell">Started</th>
|
||||||
<th class="nd_center-cell">Finished</th>
|
<th class="nd_center-cell">Finished</th>
|
||||||
<th class="nd_center-cell">Action</th>
|
<th class="nd_center-cell">
|
||||||
|
<div class="btn-group">
|
||||||
|
<button class="btn dropdown-toggle" data-toggle="dropdown">
|
||||||
|
Action
|
||||||
|
<span class="caret"></span>
|
||||||
|
</button>
|
||||||
|
<ul class="dropdown-menu nd_no-min-width">
|
||||||
|
<li>
|
||||||
|
<a class="nd_adminbutton" name="delall" href="#">
|
||||||
|
<i class="icon-trash text-error"></i> Empty Queue
|
||||||
|
</a>
|
||||||
|
</li>
|
||||||
|
</ul>
|
||||||
|
</div>
|
||||||
|
</th>
|
||||||
</tr>
|
</tr>
|
||||||
</thead>
|
</thead>
|
||||||
</tbody>
|
</tbody>
|
||||||
@@ -47,4 +64,5 @@
|
|||||||
[% END %]
|
[% END %]
|
||||||
</tbody>
|
</tbody>
|
||||||
</table>
|
</table>
|
||||||
|
[% END %]
|
||||||
|
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<table class="table table-bordered table-striped">
|
<table class="table table-bordered table-striped nd_floatinghead">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="nd_center-cell">Device Name</th>
|
<th class="nd_center-cell">Device Name</th>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<table class="table table-bordered table-striped">
|
<table class="table table-bordered table-striped nd_floatinghead">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="nd_center-cell">Left Device</th>
|
<th class="nd_center-cell">Left Device</th>
|
||||||
|
|||||||
52
Netdisco/share/views/ajax/admintask/users.tt
Normal file
52
Netdisco/share/views/ajax/admintask/users.tt
Normal file
@@ -0,0 +1,52 @@
|
|||||||
|
<table class="table table-bordered table-striped nd_floatinghead">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th class="nd_center-cell">Full Name</th>
|
||||||
|
<th class="nd_center-cell">Username</th>
|
||||||
|
<th class="nd_center-cell">Password</th>
|
||||||
|
<!-- <th class="nd_center-cell">External Auth</th> -->
|
||||||
|
<th class="nd_center-cell">Port Control</th>
|
||||||
|
<th class="nd_center-cell">Administrator</th>
|
||||||
|
<th class="nd_center-cell">Action</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
</tbody>
|
||||||
|
<tr>
|
||||||
|
<td class="nd_center-cell"><input data-form="add" name="fullname" type="text"></td>
|
||||||
|
<td class="nd_center-cell"><input data-form="add" name="username" type="text"></td>
|
||||||
|
<td class="nd_center-cell"><input data-form="add" name="password" type="text"></td>
|
||||||
|
<td class="nd_center-cell"><input data-form="add" type="checkbox" name="port_control"></td>
|
||||||
|
<td class="nd_center-cell"><input data-form="add" type="checkbox" name="admin"></td>
|
||||||
|
<td class="nd_center-cell">
|
||||||
|
<button class="btn btn-small nd_adminbutton" name="add" type="submit"><i class="icon-plus-sign"></i> Add</button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
|
||||||
|
[% WHILE (row = results.next) %]
|
||||||
|
<tr>
|
||||||
|
<td class="nd_center-cell">
|
||||||
|
<input data-form="update" name="fullname" type="text" value="[% row.fullname | html_entity %]">
|
||||||
|
</td>
|
||||||
|
<td class="nd_center-cell">
|
||||||
|
<input data-form="update" name="username" type="text" value="[% row.username | html_entity %]">
|
||||||
|
</td>
|
||||||
|
<td class="nd_center-cell">
|
||||||
|
<input data-form="update" name="password" type="text" value="********">
|
||||||
|
</td>
|
||||||
|
<td class="nd_center-cell">
|
||||||
|
<input data-form="update" name="port_control" type="checkbox" [% 'checked="checked"' IF row.port_control %]>
|
||||||
|
</td>
|
||||||
|
<td class="nd_center-cell">
|
||||||
|
<input data-form="update" name="admin" type="checkbox" [% 'checked="checked"' IF row.admin %]>
|
||||||
|
</td>
|
||||||
|
|
||||||
|
<td class="nd_center-cell">
|
||||||
|
<button class="btn nd_adminbutton" name="update" type="submit"><i class="icon-save text-warning"></i></button>
|
||||||
|
<input data-form="del" name="username" type="hidden" value="[% row.username | html_entity %]">
|
||||||
|
<button class="btn nd_adminbutton" name="del" type="submit"><i class="icon-trash text-error"></i></button>
|
||||||
|
</td>
|
||||||
|
</tr>
|
||||||
|
[% END %]
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<table class="table table-bordered table-condensed table-striped">
|
<table class="table table-bordered table-condensed table-striped nd_floatinghead">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Address</th>
|
<th>Address</th>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<table class="table table-bordered table-striped">
|
<table class="table table-bordered table-striped nd_floatinghead">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th></th>
|
<th></th>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<table class="table table-bordered table-condensed table-striped">
|
<table class="table table-bordered table-condensed table-striped nd_floatinghead">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th class="nd_center-cell">Left Device</th>
|
<th class="nd_center-cell">Left Device</th>
|
||||||
@@ -12,7 +12,7 @@
|
|||||||
</tbody>
|
</tbody>
|
||||||
[% WHILE (row = results.next) %]
|
[% WHILE (row = results.next) %]
|
||||||
<tr>
|
<tr>
|
||||||
<td class="nd_center-cell">[% row.left_dns || row.left_ip | html_entity %]</a>
|
<td class="nd_center-cell">[% row.left_dns || row.left_ip | html_entity %]</td>
|
||||||
<td class="nd_center-cell"><a class="nd_linkcell"
|
<td class="nd_center-cell"><a class="nd_linkcell"
|
||||||
href="[% device_ports %]&q=[% row.left_dns || row.left_ip | uri %]&f=[% row.left_port | uri %]&c_duplex=on">
|
href="[% device_ports %]&q=[% row.left_dns || row.left_ip | uri %]&f=[% row.left_port | uri %]&c_duplex=on">
|
||||||
[% row.left_port | html_entity %]</a></td>
|
[% row.left_port | html_entity %]</a></td>
|
||||||
|
|||||||
23
Netdisco/share/views/ajax/report/portutilization.tt
Normal file
23
Netdisco/share/views/ajax/report/portutilization.tt
Normal file
@@ -0,0 +1,23 @@
|
|||||||
|
<table class="table table-bordered table-condensed table-hover nd_floatinghead">
|
||||||
|
<thead>
|
||||||
|
<tr>
|
||||||
|
<th>Device</th>
|
||||||
|
<th class="nd_center-cell">Total Ports</th>
|
||||||
|
<th class="nd_center-cell">In Use</th>
|
||||||
|
<th class="nd_center-cell">Shutdown</th>
|
||||||
|
<th class="nd_center-cell">Free</th>
|
||||||
|
</tr>
|
||||||
|
</thead>
|
||||||
|
</tbody>
|
||||||
|
[% WHILE (row = results.next) %]
|
||||||
|
<tr>
|
||||||
|
<td><a href="[% device_ports %]&q=[% row.dns || row.ip | uri %]">[% row.dns || row.ip | html_entity %]</a></td>
|
||||||
|
<td class="nd_center-cell">[% row.port_count %]</td>
|
||||||
|
<td class="nd_center-cell">[% row.ports_in_use %]</td>
|
||||||
|
<td class="nd_center-cell">[% row.ports_shutdown %]</td>
|
||||||
|
<td class="nd_center-cell">[% row.ports_free %]</td>
|
||||||
|
</tr>
|
||||||
|
[% END %]
|
||||||
|
</tbody>
|
||||||
|
</table>
|
||||||
|
|
||||||
@@ -1,4 +1,4 @@
|
|||||||
<table class="table table-bordered table-condensed table-striped">
|
<table class="table table-bordered table-condensed table-striped nd_floatinghead">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Device</th>
|
<th>Device</th>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<table class="table table-bordered table-hover">
|
<table class="table table-bordered table-hover nd_floatinghead">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>MAC</th>
|
<th>MAC</th>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<table class="table table-bordered table-hover">
|
<table class="table table-bordered table-hover nd_floatinghead">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>MAC</th>
|
<th>MAC</th>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<table class="table table-bordered table-condensed table-striped">
|
<table class="table table-bordered table-condensed table-striped nd_floatinghead">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Description</th>
|
<th>Description</th>
|
||||||
|
|||||||
@@ -1,4 +1,4 @@
|
|||||||
<table class="table table-bordered table-condensed table-striped">
|
<table class="table table-bordered table-condensed table-striped nd_floatinghead">
|
||||||
<thead>
|
<thead>
|
||||||
<tr>
|
<tr>
|
||||||
<th>Vlan</th>
|
<th>Vlan</th>
|
||||||
|
|||||||
@@ -12,7 +12,7 @@
|
|||||||
|
|
||||||
// reload this table every 5 seconds
|
// reload this table every 5 seconds
|
||||||
if (tab == 'jobqueue'
|
if (tab == 'jobqueue'
|
||||||
&& $('#nd_countdown-control-icon').hasClass('icon-pause')) {
|
&& $('#nd_countdown-control-icon').hasClass('icon-play')) {
|
||||||
|
|
||||||
$('#nd_countdown').text('5');
|
$('#nd_countdown').text('5');
|
||||||
nd_timers.push(setTimeout(function() { $('#nd_countdown').text('4') }, 1000 ));
|
nd_timers.push(setTimeout(function() { $('#nd_countdown').text('4') }, 1000 ));
|
||||||
@@ -94,7 +94,7 @@
|
|||||||
var icon = $('#nd_countdown-control-icon');
|
var icon = $('#nd_countdown-control-icon');
|
||||||
icon.toggleClass('icon-pause icon-play text-error text-success');
|
icon.toggleClass('icon-pause icon-play text-error text-success');
|
||||||
|
|
||||||
if (icon.hasClass('icon-play')) {
|
if (icon.hasClass('icon-pause')) {
|
||||||
for (var i = 0; i < nd_timers.length; i++) {
|
for (var i = 0; i < nd_timers.length; i++) {
|
||||||
clearTimeout(nd_timers[i]);
|
clearTimeout(nd_timers[i]);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,9 +17,11 @@
|
|||||||
<script type="text/javascript" src="[% uri_base %]/javascripts/jquery-history.js"></script>
|
<script type="text/javascript" src="[% uri_base %]/javascripts/jquery-history.js"></script>
|
||||||
<script type="text/javascript" src="[% uri_base %]/javascripts/jquery-deserialize.js"></script>
|
<script type="text/javascript" src="[% uri_base %]/javascripts/jquery-deserialize.js"></script>
|
||||||
<script type="text/javascript" src="[% uri_base %]/javascripts/bootstrap.min.js"></script>
|
<script type="text/javascript" src="[% uri_base %]/javascripts/bootstrap.min.js"></script>
|
||||||
|
<script type="text/javascript" src="[% uri_base %]/javascripts/underscore.min.js"></script>
|
||||||
<script type="text/javascript" src="[% uri_base %]/javascripts/jquery.qtip.min.js"></script>
|
<script type="text/javascript" src="[% uri_base %]/javascripts/jquery.qtip.min.js"></script>
|
||||||
<script type="text/javascript" src="[% uri_base %]/javascripts/d3.min.js"></script>
|
<script type="text/javascript" src="[% uri_base %]/javascripts/d3.min.js"></script>
|
||||||
<script type="text/javascript" src="[% uri_base %]/javascripts/toastr.js"></script>
|
<script type="text/javascript" src="[% uri_base %]/javascripts/toastr.js"></script>
|
||||||
|
<script type="text/javascript" src="[% uri_base %]/javascripts/jquery.floatThead.js"></script>
|
||||||
|
|
||||||
<script type="text/javascript">
|
<script type="text/javascript">
|
||||||
var uri_base = '[% uri_base %]';
|
var uri_base = '[% uri_base %]';
|
||||||
|
|||||||
4
TODO
4
TODO
@@ -2,9 +2,11 @@ FRONTEND
|
|||||||
========
|
========
|
||||||
|
|
||||||
* report on port usage
|
* report on port usage
|
||||||
* SSL support
|
|
||||||
* user management admin task
|
* user management admin task
|
||||||
|
|
||||||
|
* LDAP authentication and RBAC
|
||||||
|
- http://advent.perldancer.org/2012/2
|
||||||
|
|
||||||
* moar reports
|
* moar reports
|
||||||
* (jeneric) device module tab
|
* (jeneric) device module tab
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user