├── .dockerignore ├── .gitignore ├── Dockerfile ├── LICENSE ├── README.md ├── __init__.py ├── aspromGUI.py ├── aspromMetrics.py ├── aspromNagiosCheck.py ├── aspromScan.py ├── db ├── clear.sql ├── createdatabase.sql ├── ddl.sql └── example_filled.sql ├── doc ├── Doxyfile └── refman.pdf ├── docker-compose.yml ├── docker ├── my_mem.cnf ├── patch-crontab.py └── start.sh ├── env.example ├── etc └── asprom.cfg ├── inc ├── __init__.py └── asprom.py ├── requirements.txt ├── static ├── css │ ├── asprom.css │ ├── bootstrap-dialog.min.css │ ├── bootstrap-editable.css │ ├── bootstrap-table.css │ ├── bootstrap-table.min.css │ ├── bootstrap-theme.css │ ├── bootstrap-theme.min.css │ ├── bootstrap.css │ ├── bootstrap.min.css │ └── jquery-cron.css ├── fonts │ ├── glyphicons-halflings-regular.eot │ ├── glyphicons-halflings-regular.svg │ ├── glyphicons-halflings-regular.ttf │ └── glyphicons-halflings-regular.woff └── js │ ├── asprom.js │ ├── bootstrap-dialog.min.js │ ├── bootstrap-editable.min.js │ ├── bootstrap-table.min.js │ ├── bootstrap.js │ ├── bootstrap.min.js │ ├── jquery-cron-min.js │ ├── jquery.min.js │ ├── later.js │ ├── moment.min.js │ └── prettycron.js ├── supplemental ├── apache-definition └── init-script └── views ├── addjob.tpl ├── alerts-closed.tpl ├── alerts-exposed.tpl ├── base.tpl ├── baseline.tpl ├── editjob.tpl ├── posture.tpl └── schedule.tpl /.dockerignore: -------------------------------------------------------------------------------- 1 | Dockerfile 2 | supplemental 3 | doc 4 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .env 2 | *.swp 3 | *.bak 4 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM debian:bookworm-slim 2 | ENV TINI_VERSION=v0.19.0 3 | ENV DEBIAN_FRONTEND=noninteractive 4 | ADD https://github.com/krallin/tini/releases/download/${TINI_VERSION}/tini /tini 5 | RUN chmod +x /tini 6 | COPY requirements.txt /tmp/ 7 | RUN apt-get update && \ 8 | apt-get -y install cron nmap patch libmariadb3 python3-minimal python3-pip \ 9 | default-libmysqlclient-dev build-essential pkg-config && \ 10 | pip install --break-system-packages -r /tmp/requirements.txt && \ 11 | apt-get -y autoremove python3-dev python3-pip default-libmysqlclient-dev \ 12 | build-essential pkg-config && \ 13 | rm -rf /var/lib/apt/lists/* 14 | WORKDIR /asprom 15 | COPY . . 16 | # fix old style class in python-crontab leading to exception 17 | RUN patch -p0 /usr/local/lib/python3.11/dist-packages/crontab.py < docker/patch-crontab.py 18 | RUN chmod 640 aspromNagiosCheck.py 19 | EXPOSE 8080 20 | ENTRYPOINT ["/tini", "--"] 21 | CMD ["docker/start.sh"] 22 | 23 | -------------------------------------------------------------------------------- /LICENSE: -------------------------------------------------------------------------------- 1 | Apache License 2 | Version 2.0, January 2004 3 | http://www.apache.org/licenses/ 4 | 5 | TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 6 | 7 | 1. Definitions. 8 | 9 | "License" shall mean the terms and conditions for use, reproduction, 10 | and distribution as defined by Sections 1 through 9 of this document. 11 | 12 | "Licensor" shall mean the copyright owner or entity authorized by 13 | the copyright owner that is granting the License. 14 | 15 | "Legal Entity" shall mean the union of the acting entity and all 16 | other entities that control, are controlled by, or are under common 17 | control with that entity. For the purposes of this definition, 18 | "control" means (i) the power, direct or indirect, to cause the 19 | direction or management of such entity, whether by contract or 20 | otherwise, or (ii) ownership of fifty percent (50%) or more of the 21 | outstanding shares, or (iii) beneficial ownership of such entity. 22 | 23 | "You" (or "Your") shall mean an individual or Legal Entity 24 | exercising permissions granted by this License. 25 | 26 | "Source" form shall mean the preferred form for making modifications, 27 | including but not limited to software source code, documentation 28 | source, and configuration files. 29 | 30 | "Object" form shall mean any form resulting from mechanical 31 | transformation or translation of a Source form, including but 32 | not limited to compiled object code, generated documentation, 33 | and conversions to other media types. 34 | 35 | "Work" shall mean the work of authorship, whether in Source or 36 | Object form, made available under the License, as indicated by a 37 | copyright notice that is included in or attached to the work 38 | (an example is provided in the Appendix below). 39 | 40 | "Derivative Works" shall mean any work, whether in Source or Object 41 | form, that is based on (or derived from) the Work and for which the 42 | editorial revisions, annotations, elaborations, or other modifications 43 | represent, as a whole, an original work of authorship. For the purposes 44 | of this License, Derivative Works shall not include works that remain 45 | separable from, or merely link (or bind by name) to the interfaces of, 46 | the Work and Derivative Works thereof. 47 | 48 | "Contribution" shall mean any work of authorship, including 49 | the original version of the Work and any modifications or additions 50 | to that Work or Derivative Works thereof, that is intentionally 51 | submitted to Licensor for inclusion in the Work by the copyright owner 52 | or by an individual or Legal Entity authorized to submit on behalf of 53 | the copyright owner. For the purposes of this definition, "submitted" 54 | means any form of electronic, verbal, or written communication sent 55 | to the Licensor or its representatives, including but not limited to 56 | communication on electronic mailing lists, source code control systems, 57 | and issue tracking systems that are managed by, or on behalf of, the 58 | Licensor for the purpose of discussing and improving the Work, but 59 | excluding communication that is conspicuously marked or otherwise 60 | designated in writing by the copyright owner as "Not a Contribution." 61 | 62 | "Contributor" shall mean Licensor and any individual or Legal Entity 63 | on behalf of whom a Contribution has been received by Licensor and 64 | subsequently incorporated within the Work. 65 | 66 | 2. Grant of Copyright License. Subject to the terms and conditions of 67 | this License, each Contributor hereby grants to You a perpetual, 68 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 69 | copyright license to reproduce, prepare Derivative Works of, 70 | publicly display, publicly perform, sublicense, and distribute the 71 | Work and such Derivative Works in Source or Object form. 72 | 73 | 3. Grant of Patent License. Subject to the terms and conditions of 74 | this License, each Contributor hereby grants to You a perpetual, 75 | worldwide, non-exclusive, no-charge, royalty-free, irrevocable 76 | (except as stated in this section) patent license to make, have made, 77 | use, offer to sell, sell, import, and otherwise transfer the Work, 78 | where such license applies only to those patent claims licensable 79 | by such Contributor that are necessarily infringed by their 80 | Contribution(s) alone or by combination of their Contribution(s) 81 | with the Work to which such Contribution(s) was submitted. If You 82 | institute patent litigation against any entity (including a 83 | cross-claim or counterclaim in a lawsuit) alleging that the Work 84 | or a Contribution incorporated within the Work constitutes direct 85 | or contributory patent infringement, then any patent licenses 86 | granted to You under this License for that Work shall terminate 87 | as of the date such litigation is filed. 88 | 89 | 4. Redistribution. You may reproduce and distribute copies of the 90 | Work or Derivative Works thereof in any medium, with or without 91 | modifications, and in Source or Object form, provided that You 92 | meet the following conditions: 93 | 94 | (a) You must give any other recipients of the Work or 95 | Derivative Works a copy of this License; and 96 | 97 | (b) You must cause any modified files to carry prominent notices 98 | stating that You changed the files; and 99 | 100 | (c) You must retain, in the Source form of any Derivative Works 101 | that You distribute, all copyright, patent, trademark, and 102 | attribution notices from the Source form of the Work, 103 | excluding those notices that do not pertain to any part of 104 | the Derivative Works; and 105 | 106 | (d) If the Work includes a "NOTICE" text file as part of its 107 | distribution, then any Derivative Works that You distribute must 108 | include a readable copy of the attribution notices contained 109 | within such NOTICE file, excluding those notices that do not 110 | pertain to any part of the Derivative Works, in at least one 111 | of the following places: within a NOTICE text file distributed 112 | as part of the Derivative Works; within the Source form or 113 | documentation, if provided along with the Derivative Works; or, 114 | within a display generated by the Derivative Works, if and 115 | wherever such third-party notices normally appear. The contents 116 | of the NOTICE file are for informational purposes only and 117 | do not modify the License. You may add Your own attribution 118 | notices within Derivative Works that You distribute, alongside 119 | or as an addendum to the NOTICE text from the Work, provided 120 | that such additional attribution notices cannot be construed 121 | as modifying the License. 122 | 123 | You may add Your own copyright statement to Your modifications and 124 | may provide additional or different license terms and conditions 125 | for use, reproduction, or distribution of Your modifications, or 126 | for any such Derivative Works as a whole, provided Your use, 127 | reproduction, and distribution of the Work otherwise complies with 128 | the conditions stated in this License. 129 | 130 | 5. Submission of Contributions. Unless You explicitly state otherwise, 131 | any Contribution intentionally submitted for inclusion in the Work 132 | by You to the Licensor shall be under the terms and conditions of 133 | this License, without any additional terms or conditions. 134 | Notwithstanding the above, nothing herein shall supersede or modify 135 | the terms of any separate license agreement you may have executed 136 | with Licensor regarding such Contributions. 137 | 138 | 6. Trademarks. This License does not grant permission to use the trade 139 | names, trademarks, service marks, or product names of the Licensor, 140 | except as required for reasonable and customary use in describing the 141 | origin of the Work and reproducing the content of the NOTICE file. 142 | 143 | 7. Disclaimer of Warranty. Unless required by applicable law or 144 | agreed to in writing, Licensor provides the Work (and each 145 | Contributor provides its Contributions) on an "AS IS" BASIS, 146 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or 147 | implied, including, without limitation, any warranties or conditions 148 | of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A 149 | PARTICULAR PURPOSE. You are solely responsible for determining the 150 | appropriateness of using or redistributing the Work and assume any 151 | risks associated with Your exercise of permissions under this License. 152 | 153 | 8. Limitation of Liability. In no event and under no legal theory, 154 | whether in tort (including negligence), contract, or otherwise, 155 | unless required by applicable law (such as deliberate and grossly 156 | negligent acts) or agreed to in writing, shall any Contributor be 157 | liable to You for damages, including any direct, indirect, special, 158 | incidental, or consequential damages of any character arising as a 159 | result of this License or out of the use or inability to use the 160 | Work (including but not limited to damages for loss of goodwill, 161 | work stoppage, computer failure or malfunction, or any and all 162 | other commercial damages or losses), even if such Contributor 163 | has been advised of the possibility of such damages. 164 | 165 | 9. Accepting Warranty or Additional Liability. While redistributing 166 | the Work or Derivative Works thereof, You may choose to offer, 167 | and charge a fee for, acceptance of support, warranty, indemnity, 168 | or other liability obligations and/or rights consistent with this 169 | License. However, in accepting such obligations, You may act only 170 | on Your own behalf and on Your sole responsibility, not on behalf 171 | of any other Contributor, and only if You agree to indemnify, 172 | defend, and hold each Contributor harmless for any liability 173 | incurred by, or claims asserted against, such Contributor by reason 174 | of your accepting any such warranty or additional liability. 175 | 176 | END OF TERMS AND CONDITIONS 177 | 178 | APPENDIX: How to apply the Apache License to your work. 179 | 180 | To apply the Apache License to your work, attach the following 181 | boilerplate notice, with the fields enclosed by brackets "{}" 182 | replaced with your own identifying information. (Don't include 183 | the brackets!) The text should be enclosed in the appropriate 184 | comment syntax for the file format. We also recommend that a 185 | file or class name and description of purpose be included on the 186 | same "printed page" as the copyright notice for easier 187 | identification within third-party archives. 188 | 189 | Copyright {yyyy} {name of copyright owner} 190 | 191 | Licensed under the Apache License, Version 2.0 (the "License"); 192 | you may not use this file except in compliance with the License. 193 | You may obtain a copy of the License at 194 | 195 | http://www.apache.org/licenses/LICENSE-2.0 196 | 197 | Unless required by applicable law or agreed to in writing, software 198 | distributed under the License is distributed on an "AS IS" BASIS, 199 | WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 200 | See the License for the specific language governing permissions and 201 | limitations under the License. 202 | 203 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # asprom - Assault Profile Monitor 2 | 3 | asprom is a network security compliance scanner that monitors Layer 4 firewall configurations. It allows you to define service profiles for your networks and automatically scans them using nmap to detect any deviations from the established baseline. 4 | 5 | This tool helps ensure compliance with security standards such as PCI-DSS, BSI-Grundschutz, and ISO/IEC 27001. 6 | 7 | ## Quick Start with Docker 8 | 9 | The easiest way to get started is using Docker, which includes all dependencies: 10 | 11 | ```bash 12 | cp env.example .env 13 | docker-compose up -d 14 | ``` 15 | 16 | Once running, access the GUI at [http://localhost:8080](http://localhost:8080). 17 | 18 | ## Getting Started 19 | 20 | 1. Navigate to the "Schedule" tab 21 | 2. Configure a scan target: 22 | - Enter a hostname, IP address, or IP range 23 | - Leave "port range" and "extra parameters" empty initially 24 | 3. Click "Add Job" to create the scan 25 | 4. Click the magnifying glass icon to execute the scan immediately 26 | 5. Wait for the scan completion notification 27 | 28 | ## Managing Alerts 29 | 30 | After scanning, you'll find detected services under the "Alerts: Exposed" tab. For each alert, you can: 31 | 32 | - Click the "star" icon to mark for mitigation 33 | - Click the "approve" icon to accept it as part of your baseline (requires business justification) 34 | 35 | Once you've processed alerts for all your IP ranges, your initial configuration is complete. asprom will now monitor these ranges and alert you to any new services that appear. 36 | 37 | ## Monitoring Integration 38 | 39 | ### Prometheus Metrics 40 | Access metrics about open ports and baseline deviations at: 41 | [http://localhost:5000/metrics](http://localhost:5000/metrics) 42 | 43 | ### Nagios Integration 44 | Use `aspromNagiosCheck.py` as a standard Nagios plugin to receive active alerts. The plugin will return CRITICAL status when unauthorized services are detected. 45 | 46 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- 1 | # __init__.py file 2 | 3 | import pymysql 4 | pymysql.install_as_MySQLdb() 5 | -------------------------------------------------------------------------------- /aspromGUI.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Oct 19, 2014 3 | 4 | @author stefankn 5 | @namespace asprom.aspromGUI 6 | Main Script for the asprom GUI. This script presents a webserver socket to 7 | which client browsers can connect to. 8 | Also, it orchestrates URL calls between the model, view and controller classes. 9 | ''' 10 | from bottle import (route, run, static_file, abort, redirect, template, 11 | post, request, hook, response) 12 | from inc.asprom import (AspromModel, AspromScheduleModel, Controller, Machine, initDB, 13 | closeDB, Cfg) 14 | 15 | # Variable definitions 16 | 17 | ## relative path to static files 18 | sr = 'static/' 19 | 20 | ## main model 21 | M = None 22 | 23 | ## schedule model 24 | SM = None 25 | 26 | localconf = Cfg() 27 | 28 | # 29 | # hooks 30 | # 31 | 32 | 33 | @hook('before_request') 34 | def before_request(): 35 | ''' 36 | before each dynamic request, create DB connection and Model instances. 37 | ''' 38 | username = None 39 | try: 40 | username = request.get_header("X-Forwarded-User", request.auth[0]) 41 | except: 42 | pass 43 | 44 | p = request.path 45 | if p.startswith('/' + sr): 46 | return 47 | global M, SM 48 | try: 49 | initDB(localconf) 50 | M = AspromModel(username=username) 51 | SM = AspromScheduleModel(user=True) 52 | except: 53 | raise 54 | 55 | # 56 | # routes 57 | # 58 | 59 | 60 | @route('/') 61 | def serve_homepage(): 62 | ''' 63 | HTTP Redirect to http:///alerts-exposed. 64 | ''' 65 | redirect('/alerts-exposed') 66 | 67 | # main views 68 | 69 | 70 | @route('/alerts-exposed') 71 | def serve_alertsexposed(): 72 | ''' 73 | Presents view: http:///alerts-exposed. 74 | ''' 75 | return template('views/alerts-exposed') 76 | 77 | 78 | @route('/alerts-closed') 79 | def serve_alertsclosed(): 80 | ''' 81 | Presents view: http:///alerts-closed. 82 | ''' 83 | return template('views/alerts-closed') 84 | 85 | 86 | @route('/baseline') 87 | def serve_baseline(): 88 | ''' 89 | Presents view: http:///baseline. 90 | ''' 91 | return template('views/baseline') 92 | 93 | 94 | @route('/posture') 95 | def serve_forensic(): 96 | ''' 97 | Presents view: http:///posture. 98 | ''' 99 | return template('views/posture') 100 | 101 | 102 | @route('/schedule') 103 | def serve_schedule(): 104 | ''' 105 | Presents view: http:///schedule. 106 | ''' 107 | return template('views/schedule') 108 | 109 | 110 | @route('/log') 111 | def serve_log(): 112 | ''' 113 | Presents view: http:///log. 114 | ''' 115 | return M.getLastLog(10) 116 | 117 | 118 | # dialog views 119 | @route('/dia/editjob/') 121 | def serve_editjob_view(jobid): 122 | ''' 123 | Presents view: http:///dia/editjob. 124 | This is meant to be used as a dialog popup in the schedule view. 125 | On this dialog, the parameters of an existing job can be edited. 126 | 127 | @param jobid the job ID to be edited. 128 | ''' 129 | 130 | j = SM.getScheduleEntryByID(jobid) 131 | return template('views/editjob', jobid=jobid, initial=j['when'], iprange=j 132 | ['iprange'], portrange=j['ports'], extraparams=j['params']) 133 | 134 | 135 | @route('/dia/addjob') 136 | def serve_addjob_view(): 137 | ''' 138 | Presents view: http:///dia/addjob. 139 | This is meant to be used as a dialog popup in the schedule view. 140 | On this dialog, the parameters of a new job can be entered. 141 | ''' 142 | from uuid import uuid4 143 | return template('views/addjob', jobid=str(uuid4()), initial='0 1 * * *', 144 | iprange='192.168.0.0/24', portrange='0-1024', 145 | extraparams='-sV') 146 | 147 | 148 | # JSON Views 149 | @route('/json/') 150 | def returnjson(filename): 151 | ''' 152 | Presents all json views: http:///json/*. 153 | These are used by the tables embedded in the main html views. 154 | The data is aquired using ajax calls. 155 | The data is pulled from the model in dict format and then converted 156 | to json. 157 | 158 | @param filename the json view to be shown. can be any of 159 | alerts-exposed, alerts-closed, baseline, posture or schedule. 160 | ''' 161 | if filename == 'alerts-exposed': 162 | return M.tojson(M.getAlertsExposed()) 163 | elif filename == 'alerts-closed': 164 | return M.tojson(M.getAlertsClosed()) 165 | elif filename == 'baseline': 166 | return M.tojson(M.getNeatline()) 167 | elif filename == 'posture': 168 | return M.tojson(M.getForensic()) 169 | elif filename == 'schedule': 170 | return M.tojson(SM.getSchedule()) 171 | else: 172 | abort(404, "undefined json") 173 | closeDB() 174 | 175 | 176 | # JSON Views 177 | @route('/plain/') 178 | def returnplain(filename): 179 | ''' 180 | Presents all plaintext views: http:///plain/*. 181 | These are used by other scripts, like markusk's openvas-config-script 182 | 183 | @param filename the plaintext view to be shown. 184 | ''' 185 | response.content_type = 'text/plain' 186 | if filename == 'scanned-ranges': 187 | return SM.getScannedRanges() 188 | else: 189 | abort(404, "undefined url") 190 | closeDB() 191 | 192 | 193 | # controller 194 | # rescan 195 | @route('/controller/rescanjob/') 197 | def serve_rescanController(jobid): 198 | ''' 199 | Activates controller: http:///controller/rescanjob/. 200 | Instructs the controller to perform a forensic rescan of 201 | the job with id now. 202 | 203 | @param jobid the job ID to be scanned. 204 | ''' 205 | rv = Controller.rescanJob(jobid) 206 | closeDB() 207 | return rv 208 | 209 | 210 | @route('/controller/rescanmachine/') 211 | @route('/controller/rescanmachine//') 212 | def serve_rescanMachine(host, port=None): 213 | ''' 214 | Activates controller: http:///controller/rescanmachine/[/]. 215 | Instructs the controller to perform a forensic rescan of the machine 216 | with id now. 217 | If is present, only this single port is being rescanned. 218 | 219 | @param host the host ID to be scanned. 220 | @param port the port to be rescanned on the specified machine. 221 | ''' 222 | assert host.isdigit() 223 | rv = Controller.rescanMachine(int(host), int(port) if port else None) 224 | closeDB() 225 | return rv 226 | 227 | 228 | @route('/controller/rescanservice/') 229 | def serve_rescanService(serviceid): 230 | ''' 231 | Activates controller: http:///controller/rescanservice/. 232 | Instructs the controller to perform a forensic rescan of the service 233 | with id now. 234 | 235 | @param serviceid The Service ID to be scanned. 236 | ''' 237 | assert serviceid.isdigit() 238 | rv = Controller.rescanService(int(serviceid)) 239 | closeDB() 240 | return rv 241 | 242 | @route('/controller/deletemachine/') 243 | def serve_deleteMachine(machineid): 244 | ''' 245 | Activates controller: http:///controller/deletemachine/. 246 | Deletes the machine and all its associated services from inventory. 247 | 248 | @param machineid The Machine ID to be deleted. 249 | ''' 250 | assert machineid.isdigit() 251 | machine = Machine(int(machineid)) 252 | 253 | # Delete all services first 254 | for service in machine.getServices(): 255 | service.delete() 256 | 257 | # Then delete the machine 258 | machine.delete() 259 | 260 | closeDB() 261 | return "ok" 262 | 263 | @route('/controller/deletejob/') 265 | def serve_deleteJob(jobid): 266 | ''' 267 | Activates controller: http:///controller/deletejob/. 268 | Instructs the controller to delete the job with id . 269 | 270 | @param jobid The Job ID to be scanned. 271 | ''' 272 | rv = SM.deleteJob(jobid) 273 | closeDB() 274 | return rv 275 | 276 | 277 | # flipcrit 278 | @route('/controller/flipcrit//') 279 | def serve_flipCrit(page, serviceid): 280 | ''' 281 | Activates controller: 282 | http:///controller/flipcrit//. 283 | Instructs the controller to flip the criticality of service on 284 | the alerts- view. 285 | Flipping sets the service criticality to WARNING if it was CRITICAL before 286 | and the other way round. 287 | 288 | @param page Either "exposed" or "closed". Denominates the view 289 | on which the criticality of the service should be flipped. 290 | @param serviceid The Service whose criticality should be flipped. 291 | ''' 292 | exposed = True if page == "exposed" else False 293 | Controller.flipCrit(serviceid, exposed) 294 | closeDB() 295 | 296 | # approve 297 | 298 | 299 | @post('/controller/approve') 300 | def serve_approve(): 301 | ''' 302 | Activates controller: http:///controller/approve. 303 | The arguments are to be passed by using the HTTP POST method. 304 | Using this method, a service can be approved to the baseline. 305 | 306 | @param pk The Service ID to be approved. 307 | @param value a business justification for the service to be approved. 308 | ''' 309 | serviceid = request.forms.get('pk') 310 | justification = request.forms.get('value') 311 | Controller.approve(int(serviceid), justification, M.username) 312 | closeDB() 313 | 314 | # remove 315 | 316 | 317 | @post('/controller/remove') 318 | def serve_remove(): 319 | ''' 320 | Activates controller: http:///controller/remove. 321 | The arguments are to be passed by using the HTTP POST method. 322 | Using this method, a service can be removed from the baseline. 323 | 324 | @param pk The Service ID to be removed. 325 | @param value a business justification for the service to be removed. 326 | ''' 327 | serviceid = request.forms.get('pk') 328 | justification = request.forms.get('value') 329 | Controller.remove(int(serviceid), justification, M.username) 330 | closeDB() 331 | 332 | # edit job 333 | 334 | 335 | @post('/controller/editjob/') 337 | def serve_changejob(jobid): 338 | ''' 339 | Activates controller: http:///controller/editjob/. 340 | This method tells the controller to set or change the parameters 341 | of the specified job. 342 | 343 | @param jobid the job to be edited. 344 | 345 | The following arguments are to be passed by using the HTTP POST method. 346 | 347 | @param cronval the cron schedule string. 348 | @param iprange a CIDR range, single IP or hostname. 349 | @param portrange a single port or port range in the format 350 | - to be scanned. 351 | @param extraparams extra command line parameters for nmap. 352 | ''' 353 | rv = SM.changeJob(jobid=jobid, 354 | cronval=request.forms.get('cronval'), 355 | iprange=request.forms.get('iprange'), 356 | portrange=request.forms.get('portrange'), 357 | extraparams=request.forms.get('extraparams') 358 | ) 359 | closeDB() 360 | return rv 361 | 362 | # edit job 363 | 364 | 365 | @post('/controller/addjob/') 367 | def serve_addjob(jobid): 368 | ''' 369 | Activates controller: http:///controller/addjob/. 370 | This method tells the controller to set the parameters of the specified job 371 | and add it to crontab. 372 | 373 | @param jobid the job to be edited. 374 | 375 | The following arguments are to be passed by using the HTTP POST method. 376 | 377 | @param cronval the cron schedule string. 378 | @param iprange a CIDR range, single IP or hostname. 379 | @param portrange a single port or port range in the format 380 | - to be scanned. 381 | @param extraparams extra command line parameters for nmap. 382 | ''' 383 | rv = SM.addJob(jobid=jobid, 384 | cronval=request.forms.get('cronval'), 385 | iprange=request.forms.get('iprange'), 386 | portrange=request.forms.get('portrange'), 387 | extraparams=request.forms.get('extraparams') 388 | ) 389 | closeDB() 390 | return rv 391 | 392 | 393 | # static files 394 | @route('/' + sr + '') 395 | def static(filename): 396 | ''' 397 | returns static files from the path defined by variable SR. 398 | 399 | @param filename path to the static file relative to the SR directory. 400 | ''' 401 | return static_file(filename, root=sr) 402 | 403 | 404 | # run the service! 405 | run(host=localconf['server']['listen'], port=localconf['server']['port'], 406 | debug=localconf['server']['debug'], server='paste') 407 | -------------------------------------------------------------------------------- /aspromMetrics.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Sep 05, 2024 3 | 4 | @author stefankn 5 | @namespace asprom.aspromNagiosCheck 6 | small and nice metrics server for asprom 7 | ''' 8 | from inc.asprom import initDB, closeDB, AspromModel, Cfg 9 | from time import sleep 10 | from prometheus_client import start_http_server, Gauge 11 | from pprint import pprint 12 | 13 | localconf = Cfg() 14 | 15 | alertsExposed = Gauge('alerts_exposed', 'These Ports are unintentionally open and therefore to be checked with the highest priority.') 16 | alertsClosed = Gauge('alerts_closed', 'These Ports are unintentionally open and therefore to be checked with the highest priority.') 17 | 18 | initDB(localconf) 19 | M = AspromModel() 20 | 21 | def refreshMetrics(): 22 | 23 | alertsExposed.set(len(M.getAlertsExposed())) 24 | alertsClosed.set(len(M.getAlertsClosed())) 25 | 26 | if __name__ == '__main__': 27 | 28 | pprint("starting asprom metrics server") 29 | # Start up the server to expose the metrics. 30 | start_http_server(5000) 31 | 32 | while True: 33 | sleep(5) 34 | refreshMetrics() 35 | -------------------------------------------------------------------------------- /aspromNagiosCheck.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Oct 23, 2014 3 | 4 | @author stefankn 5 | @namespace asprom.aspromNagiosCheck 6 | This file is invoked from the CLI and can be directly used as a nagios plugin. 7 | if any of the services on the alerts-exposed or alerts-closed views 8 | are marked as critical, 9 | this script terminates with a return value of 2. 10 | if none are marked as critical, but at least one is marked as warning, 11 | this script terminates with a return value of 1. 12 | Else, it terminates with a value of 0 signalling everything is alright. 13 | ''' 14 | from inc.asprom import initDB, closeDB, AspromModel, Cfg, genMessages 15 | import sys 16 | 17 | def main(): 18 | exitstate = 0 19 | msg = "" 20 | 21 | localconf = Cfg() 22 | initDB(localconf) 23 | M = AspromModel() 24 | 25 | #exposed services 26 | messageCritExposed, messageWarnExposed = genMessages(M.getAlertsExposed()) 27 | 28 | #closed services 29 | messageCritClosed, messageWarnClosed = genMessages(M.getAlertsClosed()) 30 | 31 | closeDB() 32 | 33 | # Start up the server to expose the metrics. 34 | start_http_server(5000) 35 | if len(messageCritExposed): 36 | msg += 'CRITICAL-EXPOSED: ' + " | ".join(messageCritExposed) + "\n" 37 | exitstate = 2 38 | if len(messageCritClosed): 39 | msg += 'CRITICAL-CLOSED: ' + " | ".join(messageCritClosed) + "\n" 40 | exitstate = 2 41 | if len(messageWarnExposed): 42 | msg += 'WARNING-EXPOSED: ' + " | ".join(messageWarnExposed) + "\n" 43 | exitstate = exitstate or 1 44 | if len(messageWarnClosed): 45 | msg += 'WARNING-CLOSED: ' + " | ".join(messageWarnClosed) + "\n" 46 | exitstate = exitstate or 1 47 | 48 | if not exitstate: 49 | msg = 'all Profiles nominal.' 50 | 51 | msg += 'Profiling URL: ' + localconf['misc']['url'] 52 | 53 | print(msg) 54 | sys.exit(exitstate) 55 | 56 | 57 | if __name__ == '__main__': 58 | main() 59 | -------------------------------------------------------------------------------- /aspromScan.py: -------------------------------------------------------------------------------- 1 | ''' 2 | Created on Oct 23, 2014 3 | 4 | @author stefankn 5 | @namespace asprom.aspromScan 6 | this file is invoked on the CLI as a wrapper script to nmap. 7 | when invoked from the command line, the scan() method is called. 8 | ''' 9 | import argparse 10 | import re 11 | from inc.asprom import scan, initDB, closeDB, Cfg 12 | 13 | 14 | def main(): 15 | ''' 16 | parse arguments from command line. 17 | 18 | 19 | > usage: 20 | 21 | aspromScan.py [-h] [-o EXTRA_OPTIONS] [-s SENSOR] [-p PORT_RANGE] 22 | [-j JOB_ID] TARGET 23 | 24 | Scans an IP Range for asprom. Needs nmap installed on the sensor host. 25 | 26 | 27 | positional arguments: 28 | TARGET the hostname/ip/ip range to be scanned 29 | 30 | optional arguments: 31 | 32 | -h, --help show this help message and exit 33 | 34 | -o EXTRA_OPTIONS, --extra-options EXTRA_OPTIONS 35 | extra options to be passed to nmap 36 | 37 | -s SENSOR, --sensor SENSOR 38 | start scanning on another sensor 39 | 40 | -p PORT_RANGE, --port-range PORT_RANGE 41 | set custom port range to be scanned 42 | 43 | -j JOB_ID, --job-id JOB_ID 44 | set arbitrary job id (used by aspromGUI and cron) 45 | ''' 46 | parser = argparse.ArgumentParser(description='''Scans an IP Range for 47 | asprom. Needs nmap installed on the sensor host.''') 48 | parser.add_argument('target', metavar="TARGET", 49 | help='the hostname/ip/ip range to be scanned') 50 | parser.add_argument('-o', '--extra-options', default='', 51 | help='extra options to be passed to nmap') 52 | parser.add_argument('-s', '--sensor', default='localhost', 53 | help='start scanning on another sensor') 54 | parser.add_argument('-p', '--port-range', default=None, 55 | help='set custom port range to be scanned') 56 | parser.add_argument('-j', '--job-id', default=None, 57 | help='set arbitrary job id (used by aspromGUI and cron)') 58 | 59 | args = parser.parse_args() 60 | 61 | localconf = Cfg() 62 | initDB(localconf) 63 | scan(args.target, args.port_range, args.extra_options, args.job_id) 64 | closeDB() 65 | 66 | if __name__ == '__main__': 67 | main() 68 | -------------------------------------------------------------------------------- /db/clear.sql: -------------------------------------------------------------------------------- 1 | truncate table criticality; 2 | truncate table servicelog; 3 | truncate table changelog; 4 | truncate table machinelog;; 5 | delete from services; 6 | delete from machines; 7 | truncate table scanlog; 8 | 9 | -------------------------------------------------------------------------------- /db/createdatabase.sql: -------------------------------------------------------------------------------- 1 | create database asprom; 2 | grant all privileges on asprom.* to asprom@localhost identified by 'asprom'; 3 | 4 | -------------------------------------------------------------------------------- /db/ddl.sql: -------------------------------------------------------------------------------- 1 | -- MySQL dump 10.14 Distrib 5.5.39-MariaDB, for debian-linux-gnu (x86_64) 2 | -- 3 | -- Host: localhost Database: asprom 4 | -- ------------------------------------------------------ 5 | -- Server version 5.5.39-MariaDB-2 6 | 7 | /*!40101 SET @OLD_CHARACTER_SET_CLIENT=@@CHARACTER_SET_CLIENT */; 8 | /*!40101 SET @OLD_CHARACTER_SET_RESULTS=@@CHARACTER_SET_RESULTS */; 9 | /*!40101 SET @OLD_COLLATION_CONNECTION=@@COLLATION_CONNECTION */; 10 | /*!40101 SET NAMES utf8 */; 11 | /*!40103 SET @OLD_TIME_ZONE=@@TIME_ZONE */; 12 | /*!40103 SET TIME_ZONE='+00:00' */; 13 | /*!40014 SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0 */; 14 | /*!40014 SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0 */; 15 | /*!40101 SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='NO_AUTO_VALUE_ON_ZERO' */; 16 | /*!40111 SET @OLD_SQL_NOTES=@@SQL_NOTES, SQL_NOTES=0 */; 17 | 18 | -- 19 | -- Table structure for table `changelog` 20 | -- 21 | 22 | DROP TABLE IF EXISTS `changelog`; 23 | /*!40101 SET @saved_cs_client = @@character_set_client */; 24 | /*!40101 SET character_set_client = utf8 */; 25 | CREATE TABLE `changelog` ( 26 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 27 | `serviceId` int(10) unsigned NOT NULL, 28 | `neat` tinyint(1) unsigned NOT NULL, 29 | `justification` varchar(500) COLLATE utf8_roman_ci DEFAULT NULL, 30 | `username` VARCHAR(50) NULL DEFAULT NULL COLLATE 'utf8_roman_ci', 31 | `date` datetime NOT NULL, 32 | PRIMARY KEY (`id`,`serviceId`), 33 | KEY `FK__servicesa` (`serviceId`), 34 | CONSTRAINT `FK__servicesa` FOREIGN KEY (`serviceId`) REFERENCES `services` (`id`) 35 | ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_roman_ci; 36 | /*!40101 SET character_set_client = @saved_cs_client */; 37 | 38 | -- 39 | -- Temporary table structure for view `closed` 40 | -- 41 | 42 | DROP TABLE IF EXISTS `closed`; 43 | /*!50001 DROP VIEW IF EXISTS `closed`*/; 44 | SET @saved_cs_client = @@character_set_client; 45 | SET character_set_client = utf8; 46 | /*!50001 CREATE TABLE `closed` ( 47 | `id` tinyint NOT NULL, 48 | `hostname` tinyint NOT NULL, 49 | `ip` tinyint NOT NULL, 50 | `port` tinyint NOT NULL, 51 | `product` tinyint NOT NULL, 52 | `version` tinyint NOT NULL, 53 | `extrainfo` tinyint NOT NULL, 54 | `justification` tinyint NOT NULL, 55 | `lsdate` tinyint NOT NULL, 56 | `ffdate` tinyint NOT NULL, 57 | `approvaldate` tinyint NOT NULL, 58 | `crit` tinyint NOT NULL 59 | ) ENGINE=MyISAM */; 60 | SET character_set_client = @saved_cs_client; 61 | 62 | -- 63 | -- Table structure for table `criticality` 64 | -- 65 | 66 | DROP TABLE IF EXISTS `criticality`; 67 | /*!40101 SET @saved_cs_client = @@character_set_client */; 68 | /*!40101 SET character_set_client = utf8 */; 69 | CREATE TABLE `criticality` ( 70 | `serviceId` int(11) unsigned NOT NULL, 71 | `flipExposed` tinyint(1) NOT NULL DEFAULT '0', 72 | `flipClosed` tinyint(1) NOT NULL DEFAULT '0', 73 | PRIMARY KEY (`serviceId`), 74 | CONSTRAINT `FK_criticality_services` FOREIGN KEY (`serviceId`) REFERENCES `services` (`id`) ON DELETE CASCADE 75 | ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_roman_ci; 76 | /*!40101 SET character_set_client = @saved_cs_client */; 77 | 78 | -- 79 | -- Temporary table structure for view `exposed` 80 | -- 81 | 82 | DROP TABLE IF EXISTS `exposed`; 83 | /*!50001 DROP VIEW IF EXISTS `exposed`*/; 84 | SET @saved_cs_client = @@character_set_client; 85 | SET character_set_client = utf8; 86 | /*!50001 CREATE TABLE `exposed` ( 87 | `id` tinyint NOT NULL, 88 | `hostname` tinyint NOT NULL, 89 | `ip` tinyint NOT NULL, 90 | `port` tinyint NOT NULL, 91 | `product` tinyint NOT NULL, 92 | `version` tinyint NOT NULL, 93 | `extrainfo` tinyint NOT NULL, 94 | `lsdate` tinyint NOT NULL, 95 | `ffdate` tinyint NOT NULL, 96 | `crit` tinyint NOT NULL 97 | ) ENGINE=MyISAM */; 98 | SET character_set_client = @saved_cs_client; 99 | 100 | -- 101 | -- Table structure for table `machinelog` 102 | -- 103 | 104 | DROP TABLE IF EXISTS `machinelog`; 105 | /*!40101 SET @saved_cs_client = @@character_set_client */; 106 | /*!40101 SET character_set_client = utf8 */; 107 | CREATE TABLE `machinelog` ( 108 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 109 | `machineId` int(10) unsigned NOT NULL, 110 | `exposed` tinyint(1) unsigned NOT NULL, 111 | `date` datetime NOT NULL, 112 | PRIMARY KEY (`id`,`machineId`), 113 | KEY `FK__machines` (`machineId`), 114 | CONSTRAINT `FK__machines` FOREIGN KEY (`machineId`) REFERENCES `machines` (`id`) 115 | ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_roman_ci; 116 | /*!40101 SET character_set_client = @saved_cs_client */; 117 | 118 | -- 119 | -- Table structure for table `machines` 120 | -- 121 | 122 | DROP TABLE IF EXISTS `machines`; 123 | /*!40101 SET @saved_cs_client = @@character_set_client */; 124 | /*!40101 SET character_set_client = utf8 */; 125 | CREATE TABLE `machines` ( 126 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 127 | `hostname` char(200) COLLATE utf8_roman_ci DEFAULT NULL, 128 | `ip` char(15) COLLATE utf8_roman_ci NOT NULL, 129 | `rangeId` int(10) unsigned NOT NULL, 130 | `lsdate` datetime NOT NULL, 131 | `ffdate` datetime NOT NULL, 132 | PRIMARY KEY (`id`), 133 | UNIQUE KEY `Schlüssel 2` (`ip`) 134 | ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_roman_ci; 135 | /*!40101 SET character_set_client = @saved_cs_client */; 136 | 137 | -- 138 | -- Temporary table structure for view `neatline` 139 | -- 140 | 141 | DROP TABLE IF EXISTS `neatline`; 142 | /*!50001 DROP VIEW IF EXISTS `neatline`*/; 143 | SET @saved_cs_client = @@character_set_client; 144 | SET character_set_client = utf8; 145 | /*!50001 CREATE TABLE `neatline` ( 146 | `id` tinyint NOT NULL, 147 | `serviceId` tinyint NOT NULL, 148 | `neat` tinyint NOT NULL, 149 | `justification` tinyint NOT NULL, 150 | `date` tinyint NOT NULL 151 | ) ENGINE=MyISAM */; 152 | SET character_set_client = @saved_cs_client; 153 | 154 | -- 155 | -- Table structure for table `scanlog` 156 | -- 157 | 158 | DROP TABLE IF EXISTS `scanlog`; 159 | /*!40101 SET @saved_cs_client = @@character_set_client */; 160 | /*!40101 SET character_set_client = utf8 */; 161 | CREATE TABLE `scanlog` ( 162 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 163 | `jobid` char(36) CHARACTER SET latin1 COLLATE latin1_general_ci DEFAULT NULL, 164 | `state` varchar(100) COLLATE utf8_roman_ci NOT NULL DEFAULT 'IN PROGRESS', 165 | `startdate` datetime NOT NULL, 166 | `enddate` datetime DEFAULT NULL, 167 | `output` varchar(20000) COLLATE utf8_roman_ci DEFAULT NULL, 168 | `iprange` varchar(200) COLLATE utf8_roman_ci DEFAULT NULL, 169 | `portrange` varchar(20) COLLATE utf8_roman_ci DEFAULT NULL, 170 | `extraoptions` varchar(300) COLLATE utf8_roman_ci DEFAULT NULL, 171 | PRIMARY KEY (`id`) 172 | ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_roman_ci; 173 | /*!40101 SET character_set_client = @saved_cs_client */; 174 | 175 | -- 176 | -- Table structure for table `servicelog` 177 | -- 178 | 179 | DROP TABLE IF EXISTS `servicelog`; 180 | /*!40101 SET @saved_cs_client = @@character_set_client */; 181 | /*!40101 SET character_set_client = utf8 */; 182 | CREATE TABLE `servicelog` ( 183 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 184 | `serviceId` int(10) unsigned NOT NULL, 185 | `openp` tinyint(1) unsigned NOT NULL, 186 | `date` datetime NOT NULL, 187 | PRIMARY KEY (`id`,`serviceId`), 188 | KEY `FK__services` (`serviceId`), 189 | CONSTRAINT `FK__services` FOREIGN KEY (`serviceId`) REFERENCES `services` (`id`) 190 | ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_roman_ci; 191 | /*!40101 SET character_set_client = @saved_cs_client */; 192 | 193 | -- 194 | -- Temporary table structure for view `servicelogCur` 195 | -- 196 | 197 | DROP TABLE IF EXISTS `servicelogCur`; 198 | /*!50001 DROP VIEW IF EXISTS `servicelogCur`*/; 199 | SET @saved_cs_client = @@character_set_client; 200 | SET character_set_client = utf8; 201 | /*!50001 CREATE TABLE `servicelogCur` ( 202 | `serviceId` tinyint NOT NULL, 203 | `openp` tinyint NOT NULL, 204 | `date` tinyint NOT NULL 205 | ) ENGINE=MyISAM */; 206 | SET character_set_client = @saved_cs_client; 207 | 208 | -- 209 | -- Table structure for table `services` 210 | -- 211 | 212 | DROP TABLE IF EXISTS `services`; 213 | /*!40101 SET @saved_cs_client = @@character_set_client */; 214 | /*!40101 SET character_set_client = utf8 */; 215 | CREATE TABLE `services` ( 216 | `id` int(10) unsigned NOT NULL AUTO_INCREMENT, 217 | `port` mediumint(8) unsigned NOT NULL, 218 | `protocolId` tinyint(3) unsigned NOT NULL, 219 | `machineId` int(10) unsigned NOT NULL, 220 | `product` varchar(250) COLLATE utf8_roman_ci DEFAULT NULL, 221 | `extrainfo` varchar(250) COLLATE utf8_roman_ci DEFAULT NULL, 222 | `version` varchar(250) COLLATE utf8_roman_ci DEFAULT NULL, 223 | `lsdate` datetime NOT NULL, 224 | `ffdate` datetime NOT NULL, 225 | PRIMARY KEY (`id`), 226 | UNIQUE KEY `Schlüssel 2` (`port`,`machineId`), 227 | KEY `serviceToMachine` (`machineId`), 228 | CONSTRAINT `serviceToMachine` FOREIGN KEY (`machineId`) REFERENCES `machines` (`id`) 229 | ) ENGINE=InnoDB AUTO_INCREMENT=1 DEFAULT CHARSET=utf8 COLLATE=utf8_roman_ci COMMENT='port, protocolId, machineId, product, extrainfo, version, lsdate, ffdate'; 230 | /*!40101 SET character_set_client = @saved_cs_client */; 231 | 232 | -- 233 | -- Final view structure for view `closed` 234 | -- 235 | 236 | /*!50001 DROP TABLE IF EXISTS `closed`*/; 237 | /*!50001 DROP VIEW IF EXISTS `closed`*/; 238 | /*!50001 SET @saved_cs_client = @@character_set_client */; 239 | /*!50001 SET @saved_cs_results = @@character_set_results */; 240 | /*!50001 SET @saved_col_connection = @@collation_connection */; 241 | /*!50001 SET character_set_client = utf8 */; 242 | /*!50001 SET character_set_results = utf8 */; 243 | /*!50001 SET collation_connection = utf8_general_ci */; 244 | /*!50001 CREATE ALGORITHM=UNDEFINED */ 245 | /*!50001 VIEW `closed` AS select `s`.`id` AS `id`,`m`.`hostname` AS `hostname`,`m`.`ip` AS `ip`,`s`.`port` AS `port`,`s`.`product` AS `product`,`s`.`version` AS `version`,`s`.`extrainfo` AS `extrainfo`,`n`.`justification` AS `justification`,`s`.`lsdate` AS `lsdate`,`s`.`ffdate` AS `ffdate`,`n`.`date` AS `approvaldate`,(not(coalesce(`c`.`flipClosed`,0))) AS `crit` from ((((`machines` `m` join `services` `s` on((`m`.`id` = `s`.`machineId`))) join `servicelogCur` `l` on((`s`.`id` = `l`.`serviceId`))) left join `criticality` `c` on((`s`.`id` = `c`.`serviceId`))) left join `neatline` `n` on((`n`.`serviceId` = `s`.`id`))) where ((`l`.`openp` = 0) and exists(select 1 from `neatline` where ((`neatline`.`neat` = 1) and (`s`.`machineId` = `m`.`id`) and (`neatline`.`serviceId` = `s`.`id`)))) */; 246 | /*!50001 SET character_set_client = @saved_cs_client */; 247 | /*!50001 SET character_set_results = @saved_cs_results */; 248 | /*!50001 SET collation_connection = @saved_col_connection */; 249 | 250 | -- 251 | -- Final view structure for view `exposed` 252 | -- 253 | 254 | /*!50001 DROP TABLE IF EXISTS `exposed`*/; 255 | /*!50001 DROP VIEW IF EXISTS `exposed`*/; 256 | /*!50001 SET @saved_cs_client = @@character_set_client */; 257 | /*!50001 SET @saved_cs_results = @@character_set_results */; 258 | /*!50001 SET @saved_col_connection = @@collation_connection */; 259 | /*!50001 SET character_set_client = utf8 */; 260 | /*!50001 SET character_set_results = utf8 */; 261 | /*!50001 SET collation_connection = utf8_general_ci */; 262 | /*!50001 CREATE ALGORITHM=UNDEFINED */ 263 | /*!50001 VIEW `exposed` AS select `s`.`id` AS `id`,`m`.`hostname` AS `hostname`,`m`.`ip` AS `ip`,`s`.`port` AS `port`,`s`.`product` AS `product`,`s`.`version` AS `version`,`s`.`extrainfo` AS `extrainfo`,`s`.`lsdate` AS `lsdate`,`s`.`ffdate` AS `ffdate`,`c`.`flipExposed` AS `crit` from (((`machines` `m` join `services` `s` on((`m`.`id` = `s`.`machineId`))) join `servicelogCur` `l` on((`s`.`id` = `l`.`serviceId`))) left join `criticality` `c` on((`s`.`id` = `c`.`serviceId`))) where ((`l`.`openp` = 1) and (not(exists(select 1 from `neatline` where ((`neatline`.`neat` = 1) and (`s`.`machineId` = `m`.`id`) and (`neatline`.`serviceId` = `s`.`id`)))))) */; 264 | /*!50001 SET character_set_client = @saved_cs_client */; 265 | /*!50001 SET character_set_results = @saved_cs_results */; 266 | /*!50001 SET collation_connection = @saved_col_connection */; 267 | 268 | -- 269 | -- Final view structure for view `neatline` 270 | -- 271 | 272 | /*!50001 DROP TABLE IF EXISTS `neatline`*/; 273 | /*!50001 DROP VIEW IF EXISTS `neatline`*/; 274 | /*!50001 SET @saved_cs_client = @@character_set_client */; 275 | /*!50001 SET @saved_cs_results = @@character_set_results */; 276 | /*!50001 SET @saved_col_connection = @@collation_connection */; 277 | /*!50001 SET character_set_client = utf8 */; 278 | /*!50001 SET character_set_results = utf8 */; 279 | /*!50001 SET collation_connection = utf8_general_ci */; 280 | /*!50001 CREATE ALGORITHM=UNDEFINED */ 281 | /*!50001 VIEW `neatline` AS select `l`.`id` AS `id`,`l`.`serviceId` AS `serviceId`,`l`.`neat` AS `neat`,`l`.`justification` AS `justification`,`l`.`date` AS `date` from `changelog` `l` where `l`.`id` in (select max(`changelog`.`id`) from `changelog` group by `changelog`.`serviceId`) */; 282 | /*!50001 SET character_set_client = @saved_cs_client */; 283 | /*!50001 SET character_set_results = @saved_cs_results */; 284 | /*!50001 SET collation_connection = @saved_col_connection */; 285 | 286 | -- 287 | -- Final view structure for view `servicelogCur` 288 | -- 289 | 290 | /*!50001 DROP TABLE IF EXISTS `servicelogCur`*/; 291 | /*!50001 DROP VIEW IF EXISTS `servicelogCur`*/; 292 | /*!50001 SET @saved_cs_client = @@character_set_client */; 293 | /*!50001 SET @saved_cs_results = @@character_set_results */; 294 | /*!50001 SET @saved_col_connection = @@collation_connection */; 295 | /*!50001 SET character_set_client = utf8 */; 296 | /*!50001 SET character_set_results = utf8 */; 297 | /*!50001 SET collation_connection = utf8_general_ci */; 298 | /*!50001 CREATE ALGORITHM=UNDEFINED */ 299 | /*!50001 VIEW `servicelogCur` AS select `l`.`serviceId` AS `serviceId`,`l`.`openp` AS `openp`,`l`.`date` AS `date` from `servicelog` `l` where `l`.`id` in (select max(`servicelog`.`id`) from `servicelog` group by `servicelog`.`serviceId`) */; 300 | /*!50001 SET character_set_client = @saved_cs_client */; 301 | /*!50001 SET character_set_results = @saved_cs_results */; 302 | /*!50001 SET collation_connection = @saved_col_connection */; 303 | /*!40103 SET TIME_ZONE=@OLD_TIME_ZONE */; 304 | 305 | /*!40101 SET SQL_MODE=@OLD_SQL_MODE */; 306 | /*!40014 SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS */; 307 | /*!40014 SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS */; 308 | /*!40101 SET CHARACTER_SET_CLIENT=@OLD_CHARACTER_SET_CLIENT */; 309 | /*!40101 SET CHARACTER_SET_RESULTS=@OLD_CHARACTER_SET_RESULTS */; 310 | /*!40101 SET COLLATION_CONNECTION=@OLD_COLLATION_CONNECTION */; 311 | /*!40111 SET SQL_NOTES=@OLD_SQL_NOTES */; 312 | 313 | -- Dump completed on 2015-01-02 14:34:32 314 | -------------------------------------------------------------------------------- /doc/refman.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daimoniac/asprom/aa37661f137d926e114292a0a75b70e9fc80d8a6/doc/refman.pdf -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | version: "3.7" 2 | services: 3 | asprom: 4 | restart: always 5 | environment: 6 | PYTHONUNBUFFERED: 1 7 | image: asprom 8 | build: 9 | context: . 10 | dockerfile: Dockerfile 11 | depends_on: 12 | - mysql 13 | volumes: 14 | - crontabs:/var/spool/cron/crontabs 15 | ports: 16 | - 8080:8080 17 | asprom-metrics: 18 | restart: always 19 | environment: 20 | PYTHONUNBUFFERED: 1 21 | image: asprom 22 | command: 23 | - python3 24 | - aspromMetrics.py 25 | depends_on: 26 | - mysql 27 | ports: 28 | - 5000:5000 29 | mysql: 30 | restart: always 31 | image: mysql:8.0 32 | cap_add: 33 | - SYS_NICE 34 | environment: 35 | MYSQL_USER: 'asprom' 36 | MYSQL_PASSWORD: 'asprom' 37 | MYSQL_DATABASE: 'asprom' 38 | MYSQL_RANDOM_ROOT_PASSWORD: 'true' 39 | volumes: 40 | - "mysqldata:/var/lib/mysql" 41 | - "./db/ddl.sql:/docker-entrypoint-initdb.d/1.sql" 42 | - "./docker/my_mem.cnf:/etc/mysql/conf.d/my_mem.cnf" 43 | volumes: 44 | mysqldata: 45 | crontabs: 46 | 47 | -------------------------------------------------------------------------------- /docker/my_mem.cnf: -------------------------------------------------------------------------------- 1 | [mysqld] 2 | performance_schema = off 3 | key_buffer_size = 16M 4 | tmp_table_size = 1M 5 | innodb_buffer_pool_size = 1M 6 | innodb_log_buffer_size = 1M 7 | max_connections = 20 8 | sort_buffer_size = 64M 9 | read_buffer_size = 256K 10 | read_rnd_buffer_size = 512K 11 | join_buffer_size = 128K 12 | thread_stack = 196K 13 | max-heap-table-size = 32M 14 | thread-cache-size = 50 15 | open-files-limit = 65535 16 | table-definition-cache = 1024 17 | table-open-cache = 2048 18 | 19 | -------------------------------------------------------------------------------- /docker/patch-crontab.py: -------------------------------------------------------------------------------- 1 | --- crontab.py 2024-09-17 06:56:19.752610239 +0000 2 | +++ 2.py 2024-09-17 06:57:56.279127134 +0000 3 | @@ -215,7 +215,7 @@ 4 | return str(self) == other 5 | 6 | 7 | -class CronTab: 8 | +class CronTab(object): 9 | """ 10 | Crontab object which can access any time based cron using the standard. 11 | 12 | -------------------------------------------------------------------------------- /docker/start.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | cron 3 | python3 aspromGUI.py 4 | -------------------------------------------------------------------------------- /env.example: -------------------------------------------------------------------------------- 1 | DOCKER_SOCKET=/var/run/docker.sock 2 | -------------------------------------------------------------------------------- /etc/asprom.cfg: -------------------------------------------------------------------------------- 1 | # database 2 | db: { 3 | 'host':"mysql" 4 | 'user':"asprom" 5 | 'passwd':"asprom" 6 | 'db':"asprom" 7 | } 8 | # webserver parameters 9 | server: { 10 | 'listen': '0.0.0.0' 11 | 'port': 8080 12 | 'debug': True 13 | } 14 | 15 | # miscellaneous 16 | misc: { 17 | 'url':"http://localhost:8080" 18 | } 19 | 20 | -------------------------------------------------------------------------------- /inc/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daimoniac/asprom/aa37661f137d926e114292a0a75b70e9fc80d8a6/inc/__init__.py -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | anyascii==0.3.2 2 | mysqlclient==2.2.4 3 | python-crontab==3.2.0 4 | netaddr==1.3.0 5 | paste==3.10.1 6 | bottle==0.13.1 7 | config==0.4.2 8 | croniter==3.0.3 9 | prometheus-client==0.20.0 10 | python-nmap==0.7.1 11 | -------------------------------------------------------------------------------- /static/css/asprom.css: -------------------------------------------------------------------------------- 1 | .ml10 { 2 | margin-left: 10px; 3 | } 4 | 5 | .infobox { 6 | margin-top: 15px; 7 | margin-bottom: 10px !important; 8 | } 9 | 10 | .glyphicon { 11 | text-shadow: 0 0 1px black; 12 | } 13 | .glyphicon-ok { 14 | color: #00EE00; 15 | } 16 | 17 | .glyphicon-remove { 18 | color: #FF3300; 19 | } 20 | 21 | .glyphicon-star { 22 | color: #FFCC00; 23 | } 24 | 25 | .glyphicon-search { 26 | color: #0000EE; 27 | } 28 | .glyphicon-edit { 29 | color: #0000EE; 30 | } 31 | 32 | 33 | .row-critical, .row-critical input { 34 | background-color: #FFAAAA; 35 | } 36 | 37 | .row-warning, .row-warning input { 38 | background-color: #FFFFAA; 39 | } 40 | 41 | .nopad { 42 | vertical-align: middle; 43 | padding: 0 !important; 44 | } 45 | 46 | .nopad input { 47 | box-shadow: none !important; 48 | border-radius: none !important; 49 | border: 0; 50 | 51 | } 52 | 53 | toggle { 54 | cursor: pointer; 55 | } 56 | 57 | .danger-2 { 58 | background-image: linear-gradient(to bottom, #FFEEEE 0px, #F7D3D3 100%); 59 | } 60 | 61 | .warning-2 { 62 | background-image: linear-gradient(to bottom, #FAFAF3 0px, #FAFAD0 100%); 63 | } 64 | 65 | .aspinfo { 66 | background-image: linear-gradient(to bottom, #F9FAFF 0px, #D9E0F0 100%); 67 | } 68 | 69 | .aspinfo-2 { 70 | background-image: linear-gradient(to bottom, #FFFFFF 0px, #E9EEFF 100%); 71 | } 72 | 73 | .moretolearn { 74 | display: none; 75 | } 76 | 77 | .container-fluid { 78 | margin-left: 15%; 79 | margin-right: 15%; 80 | margin-top: 10px; 81 | } 82 | 83 | 84 | .xlarge { 85 | font-size: 50px; 86 | text-shadow: 0 0 5px #FF8800; 87 | } -------------------------------------------------------------------------------- /static/css/bootstrap-dialog.min.css: -------------------------------------------------------------------------------- 1 | .bootstrap-dialog .modal-header{border-top-left-radius:4px;border-top-right-radius:4px}.bootstrap-dialog .bootstrap-dialog-title{color:#fff;display:inline-block}.bootstrap-dialog.type-default .bootstrap-dialog-title{color:#333}.bootstrap-dialog.size-normal .bootstrap-dialog-title{font-size:16px}.bootstrap-dialog.size-large .bootstrap-dialog-title{font-size:24px}.bootstrap-dialog .bootstrap-dialog-close-button{float:right;filter:alpha(opacity=90);-moz-opacity:.9;-khtml-opacity:.9;opacity:.9}.bootstrap-dialog.size-normal .bootstrap-dialog-close-button{font-size:20px}.bootstrap-dialog.size-large .bootstrap-dialog-close-button{font-size:30px}.bootstrap-dialog .bootstrap-dialog-close-button:hover{cursor:pointer;filter:alpha(opacity=100);-moz-opacity:1;-khtml-opacity:1;opacity:1}.bootstrap-dialog.size-normal .bootstrap-dialog-message{font-size:14px}.bootstrap-dialog.size-large .bootstrap-dialog-message{font-size:18px}.bootstrap-dialog.type-default .modal-header{background-color:#fff}.bootstrap-dialog.type-info .modal-header{background-color:#5bc0de}.bootstrap-dialog.type-primary .modal-header{background-color:#428bca}.bootstrap-dialog.type-success .modal-header{background-color:#5cb85c}.bootstrap-dialog.type-warning .modal-header{background-color:#f0ad4e}.bootstrap-dialog.type-danger .modal-header{background-color:#d9534f}.bootstrap-dialog .bootstrap-dialog-button-icon{margin-right:3px}.icon-spin{display:inline-block;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;-webkit-animation:spin 2s infinite linear;animation:spin 2s infinite linear}@-moz-keyframes spin{0{-moz-transform:rotate(0)}100%{-moz-transform:rotate(359deg)}}@-webkit-keyframes spin{0{-webkit-transform:rotate(0)}100%{-webkit-transform:rotate(359deg)}}@-o-keyframes spin{0{-o-transform:rotate(0)}100%{-o-transform:rotate(359deg)}}@-ms-keyframes spin{0{-ms-transform:rotate(0)}100%{-ms-transform:rotate(359deg)}}@keyframes spin{0{transform:rotate(0)}100%{transform:rotate(359deg)}} -------------------------------------------------------------------------------- /static/css/bootstrap-editable.css: -------------------------------------------------------------------------------- 1 | /*! X-editable - v1.5.1 2 | * In-place editing with Twitter Bootstrap, jQuery UI or pure jQuery 3 | * http://github.com/vitalets/x-editable 4 | * Copyright (c) 2013 Vitaliy Potapov; Licensed MIT */ 5 | .editableform { 6 | margin-bottom: 0; /* overwrites bootstrap margin */ 7 | } 8 | 9 | .editableform .control-group { 10 | margin-bottom: 0; /* overwrites bootstrap margin */ 11 | white-space: nowrap; /* prevent wrapping buttons on new line */ 12 | line-height: 20px; /* overwriting bootstrap line-height. See #133 */ 13 | } 14 | 15 | /* 16 | BS3 width:1005 for inputs breaks editable form in popup 17 | See: https://github.com/vitalets/x-editable/issues/393 18 | */ 19 | .editableform .form-control { 20 | width: auto; 21 | } 22 | 23 | .editable-buttons { 24 | display: inline-block; /* should be inline to take effect of parent's white-space: nowrap */ 25 | vertical-align: top; 26 | margin-left: 7px; 27 | /* inline-block emulation for IE7*/ 28 | zoom: 1; 29 | *display: inline; 30 | } 31 | 32 | .editable-buttons.editable-buttons-bottom { 33 | display: block; 34 | margin-top: 7px; 35 | margin-left: 0; 36 | } 37 | 38 | .editable-input { 39 | vertical-align: top; 40 | display: inline-block; /* should be inline to take effect of parent's white-space: nowrap */ 41 | width: auto; /* bootstrap-responsive has width: 100% that breakes layout */ 42 | white-space: normal; /* reset white-space decalred in parent*/ 43 | /* display-inline emulation for IE7*/ 44 | zoom: 1; 45 | *display: inline; 46 | } 47 | 48 | .editable-buttons .editable-cancel { 49 | margin-left: 7px; 50 | } 51 | 52 | /*for jquery-ui buttons need set height to look more pretty*/ 53 | .editable-buttons button.ui-button-icon-only { 54 | height: 24px; 55 | width: 30px; 56 | } 57 | 58 | .editableform-loading { 59 | /* background: url('../img/loading.gif') center center no-repeat;*/ 60 | height: 25px; 61 | width: auto; 62 | min-width: 25px; 63 | } 64 | 65 | .editable-inline .editableform-loading { 66 | background-position: left 5px; 67 | } 68 | 69 | .editable-error-block { 70 | max-width: 300px; 71 | margin: 5px 0 0 0; 72 | width: auto; 73 | white-space: normal; 74 | } 75 | 76 | /*add padding for jquery ui*/ 77 | .editable-error-block.ui-state-error { 78 | padding: 3px; 79 | } 80 | 81 | .editable-error { 82 | color: red; 83 | } 84 | 85 | /* ---- For specific types ---- */ 86 | 87 | .editableform .editable-date { 88 | padding: 0; 89 | margin: 0; 90 | float: left; 91 | } 92 | 93 | /* move datepicker icon to center of add-on button. See https://github.com/vitalets/x-editable/issues/183 */ 94 | .editable-inline .add-on .icon-th { 95 | margin-top: 3px; 96 | margin-left: 1px; 97 | } 98 | 99 | 100 | /* checklist vertical alignment */ 101 | .editable-checklist label input[type="checkbox"], 102 | .editable-checklist label span { 103 | vertical-align: middle; 104 | margin: 0; 105 | } 106 | 107 | .editable-checklist label { 108 | white-space: nowrap; 109 | } 110 | 111 | /* set exact width of textarea to fit buttons toolbar */ 112 | .editable-wysihtml5 { 113 | width: 566px; 114 | height: 250px; 115 | } 116 | 117 | /* clear button shown as link in date inputs */ 118 | .editable-clear { 119 | clear: both; 120 | font-size: 0.9em; 121 | text-decoration: none; 122 | text-align: right; 123 | } 124 | 125 | /* IOS-style clear button for text inputs */ 126 | .editable-clear-x { 127 | /* background: url('../img/clear.png') center center no-repeat;*/ 128 | display: block; 129 | width: 13px; 130 | height: 13px; 131 | position: absolute; 132 | opacity: 0.6; 133 | z-index: 100; 134 | 135 | top: 50%; 136 | right: 6px; 137 | margin-top: -6px; 138 | 139 | } 140 | 141 | .editable-clear-x:hover { 142 | opacity: 1; 143 | } 144 | 145 | .editable-pre-wrapped { 146 | white-space: pre-wrap; 147 | } 148 | .editable-container.editable-popup { 149 | max-width: none !important; /* without this rule poshytip/tooltip does not stretch */ 150 | } 151 | 152 | .editable-container.popover { 153 | width: auto; /* without this rule popover does not stretch */ 154 | } 155 | 156 | .editable-container.editable-inline { 157 | display: inline-block; 158 | vertical-align: middle; 159 | width: auto; 160 | /* inline-block emulation for IE7*/ 161 | zoom: 1; 162 | *display: inline; 163 | } 164 | 165 | .editable-container.ui-widget { 166 | font-size: inherit; /* jqueryui widget font 1.1em too big, overwrite it */ 167 | z-index: 9990; /* should be less than select2 dropdown z-index to close dropdown first when click */ 168 | } 169 | .editable-click, 170 | a.editable-click, 171 | a.editable-click:hover { 172 | text-decoration: none; 173 | border-bottom: dashed 1px #0088cc; 174 | } 175 | 176 | .editable-click.editable-disabled, 177 | a.editable-click.editable-disabled, 178 | a.editable-click.editable-disabled:hover { 179 | color: #585858; 180 | cursor: default; 181 | border-bottom: none; 182 | } 183 | 184 | .editable-empty, .editable-empty:hover, .editable-empty:focus{ 185 | font-style: italic; 186 | color: #DD1144; 187 | /* border-bottom: none; */ 188 | text-decoration: none; 189 | } 190 | 191 | .editable-unsaved { 192 | font-weight: bold; 193 | } 194 | 195 | .editable-unsaved:after { 196 | /* content: '*'*/ 197 | } 198 | 199 | .editable-bg-transition { 200 | -webkit-transition: background-color 1400ms ease-out; 201 | -moz-transition: background-color 1400ms ease-out; 202 | -o-transition: background-color 1400ms ease-out; 203 | -ms-transition: background-color 1400ms ease-out; 204 | transition: background-color 1400ms ease-out; 205 | } 206 | 207 | /*see https://github.com/vitalets/x-editable/issues/139 */ 208 | .form-horizontal .editable 209 | { 210 | padding-top: 5px; 211 | display:inline-block; 212 | } 213 | 214 | 215 | /*! 216 | * Datepicker for Bootstrap 217 | * 218 | * Copyright 2012 Stefan Petre 219 | * Improvements by Andrew Rowls 220 | * Licensed under the Apache License v2.0 221 | * http://www.apache.org/licenses/LICENSE-2.0 222 | * 223 | */ 224 | .datepicker { 225 | padding: 4px; 226 | -webkit-border-radius: 4px; 227 | -moz-border-radius: 4px; 228 | border-radius: 4px; 229 | direction: ltr; 230 | /*.dow { 231 | border-top: 1px solid #ddd !important; 232 | }*/ 233 | 234 | } 235 | .datepicker-inline { 236 | width: 220px; 237 | } 238 | .datepicker.datepicker-rtl { 239 | direction: rtl; 240 | } 241 | .datepicker.datepicker-rtl table tr td span { 242 | float: right; 243 | } 244 | .datepicker-dropdown { 245 | top: 0; 246 | left: 0; 247 | } 248 | .datepicker-dropdown:before { 249 | content: ''; 250 | display: inline-block; 251 | border-left: 7px solid transparent; 252 | border-right: 7px solid transparent; 253 | border-bottom: 7px solid #ccc; 254 | border-bottom-color: rgba(0, 0, 0, 0.2); 255 | position: absolute; 256 | top: -7px; 257 | left: 6px; 258 | } 259 | .datepicker-dropdown:after { 260 | content: ''; 261 | display: inline-block; 262 | border-left: 6px solid transparent; 263 | border-right: 6px solid transparent; 264 | border-bottom: 6px solid #ffffff; 265 | position: absolute; 266 | top: -6px; 267 | left: 7px; 268 | } 269 | .datepicker > div { 270 | display: none; 271 | } 272 | .datepicker.days div.datepicker-days { 273 | display: block; 274 | } 275 | .datepicker.months div.datepicker-months { 276 | display: block; 277 | } 278 | .datepicker.years div.datepicker-years { 279 | display: block; 280 | } 281 | .datepicker table { 282 | margin: 0; 283 | } 284 | .datepicker td, 285 | .datepicker th { 286 | text-align: center; 287 | width: 20px; 288 | height: 20px; 289 | -webkit-border-radius: 4px; 290 | -moz-border-radius: 4px; 291 | border-radius: 4px; 292 | border: none; 293 | } 294 | .table-striped .datepicker table tr td, 295 | .table-striped .datepicker table tr th { 296 | background-color: transparent; 297 | } 298 | .datepicker table tr td.day:hover { 299 | background: #eeeeee; 300 | cursor: pointer; 301 | } 302 | .datepicker table tr td.old, 303 | .datepicker table tr td.new { 304 | color: #999999; 305 | } 306 | .datepicker table tr td.disabled, 307 | .datepicker table tr td.disabled:hover { 308 | background: none; 309 | color: #999999; 310 | cursor: default; 311 | } 312 | .datepicker table tr td.today, 313 | .datepicker table tr td.today:hover, 314 | .datepicker table tr td.today.disabled, 315 | .datepicker table tr td.today.disabled:hover { 316 | background-color: #fde19a; 317 | background-image: -moz-linear-gradient(top, #fdd49a, #fdf59a); 318 | background-image: -ms-linear-gradient(top, #fdd49a, #fdf59a); 319 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#fdd49a), to(#fdf59a)); 320 | background-image: -webkit-linear-gradient(top, #fdd49a, #fdf59a); 321 | background-image: -o-linear-gradient(top, #fdd49a, #fdf59a); 322 | background-image: linear-gradient(top, #fdd49a, #fdf59a); 323 | background-repeat: repeat-x; 324 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fdd49a', endColorstr='#fdf59a', GradientType=0); 325 | border-color: #fdf59a #fdf59a #fbed50; 326 | border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); 327 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); 328 | color: #000; 329 | } 330 | .datepicker table tr td.today:hover, 331 | .datepicker table tr td.today:hover:hover, 332 | .datepicker table tr td.today.disabled:hover, 333 | .datepicker table tr td.today.disabled:hover:hover, 334 | .datepicker table tr td.today:active, 335 | .datepicker table tr td.today:hover:active, 336 | .datepicker table tr td.today.disabled:active, 337 | .datepicker table tr td.today.disabled:hover:active, 338 | .datepicker table tr td.today.active, 339 | .datepicker table tr td.today:hover.active, 340 | .datepicker table tr td.today.disabled.active, 341 | .datepicker table tr td.today.disabled:hover.active, 342 | .datepicker table tr td.today.disabled, 343 | .datepicker table tr td.today:hover.disabled, 344 | .datepicker table tr td.today.disabled.disabled, 345 | .datepicker table tr td.today.disabled:hover.disabled, 346 | .datepicker table tr td.today[disabled], 347 | .datepicker table tr td.today:hover[disabled], 348 | .datepicker table tr td.today.disabled[disabled], 349 | .datepicker table tr td.today.disabled:hover[disabled] { 350 | background-color: #fdf59a; 351 | } 352 | .datepicker table tr td.today:active, 353 | .datepicker table tr td.today:hover:active, 354 | .datepicker table tr td.today.disabled:active, 355 | .datepicker table tr td.today.disabled:hover:active, 356 | .datepicker table tr td.today.active, 357 | .datepicker table tr td.today:hover.active, 358 | .datepicker table tr td.today.disabled.active, 359 | .datepicker table tr td.today.disabled:hover.active { 360 | background-color: #fbf069 \9; 361 | } 362 | .datepicker table tr td.today:hover:hover { 363 | color: #000; 364 | } 365 | .datepicker table tr td.today.active:hover { 366 | color: #fff; 367 | } 368 | .datepicker table tr td.range, 369 | .datepicker table tr td.range:hover, 370 | .datepicker table tr td.range.disabled, 371 | .datepicker table tr td.range.disabled:hover { 372 | background: #eeeeee; 373 | -webkit-border-radius: 0; 374 | -moz-border-radius: 0; 375 | border-radius: 0; 376 | } 377 | .datepicker table tr td.range.today, 378 | .datepicker table tr td.range.today:hover, 379 | .datepicker table tr td.range.today.disabled, 380 | .datepicker table tr td.range.today.disabled:hover { 381 | background-color: #f3d17a; 382 | background-image: -moz-linear-gradient(top, #f3c17a, #f3e97a); 383 | background-image: -ms-linear-gradient(top, #f3c17a, #f3e97a); 384 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#f3c17a), to(#f3e97a)); 385 | background-image: -webkit-linear-gradient(top, #f3c17a, #f3e97a); 386 | background-image: -o-linear-gradient(top, #f3c17a, #f3e97a); 387 | background-image: linear-gradient(top, #f3c17a, #f3e97a); 388 | background-repeat: repeat-x; 389 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#f3c17a', endColorstr='#f3e97a', GradientType=0); 390 | border-color: #f3e97a #f3e97a #edde34; 391 | border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); 392 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); 393 | -webkit-border-radius: 0; 394 | -moz-border-radius: 0; 395 | border-radius: 0; 396 | } 397 | .datepicker table tr td.range.today:hover, 398 | .datepicker table tr td.range.today:hover:hover, 399 | .datepicker table tr td.range.today.disabled:hover, 400 | .datepicker table tr td.range.today.disabled:hover:hover, 401 | .datepicker table tr td.range.today:active, 402 | .datepicker table tr td.range.today:hover:active, 403 | .datepicker table tr td.range.today.disabled:active, 404 | .datepicker table tr td.range.today.disabled:hover:active, 405 | .datepicker table tr td.range.today.active, 406 | .datepicker table tr td.range.today:hover.active, 407 | .datepicker table tr td.range.today.disabled.active, 408 | .datepicker table tr td.range.today.disabled:hover.active, 409 | .datepicker table tr td.range.today.disabled, 410 | .datepicker table tr td.range.today:hover.disabled, 411 | .datepicker table tr td.range.today.disabled.disabled, 412 | .datepicker table tr td.range.today.disabled:hover.disabled, 413 | .datepicker table tr td.range.today[disabled], 414 | .datepicker table tr td.range.today:hover[disabled], 415 | .datepicker table tr td.range.today.disabled[disabled], 416 | .datepicker table tr td.range.today.disabled:hover[disabled] { 417 | background-color: #f3e97a; 418 | } 419 | .datepicker table tr td.range.today:active, 420 | .datepicker table tr td.range.today:hover:active, 421 | .datepicker table tr td.range.today.disabled:active, 422 | .datepicker table tr td.range.today.disabled:hover:active, 423 | .datepicker table tr td.range.today.active, 424 | .datepicker table tr td.range.today:hover.active, 425 | .datepicker table tr td.range.today.disabled.active, 426 | .datepicker table tr td.range.today.disabled:hover.active { 427 | background-color: #efe24b \9; 428 | } 429 | .datepicker table tr td.selected, 430 | .datepicker table tr td.selected:hover, 431 | .datepicker table tr td.selected.disabled, 432 | .datepicker table tr td.selected.disabled:hover { 433 | background-color: #9e9e9e; 434 | background-image: -moz-linear-gradient(top, #b3b3b3, #808080); 435 | background-image: -ms-linear-gradient(top, #b3b3b3, #808080); 436 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#b3b3b3), to(#808080)); 437 | background-image: -webkit-linear-gradient(top, #b3b3b3, #808080); 438 | background-image: -o-linear-gradient(top, #b3b3b3, #808080); 439 | background-image: linear-gradient(top, #b3b3b3, #808080); 440 | background-repeat: repeat-x; 441 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#b3b3b3', endColorstr='#808080', GradientType=0); 442 | border-color: #808080 #808080 #595959; 443 | border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); 444 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); 445 | color: #fff; 446 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); 447 | } 448 | .datepicker table tr td.selected:hover, 449 | .datepicker table tr td.selected:hover:hover, 450 | .datepicker table tr td.selected.disabled:hover, 451 | .datepicker table tr td.selected.disabled:hover:hover, 452 | .datepicker table tr td.selected:active, 453 | .datepicker table tr td.selected:hover:active, 454 | .datepicker table tr td.selected.disabled:active, 455 | .datepicker table tr td.selected.disabled:hover:active, 456 | .datepicker table tr td.selected.active, 457 | .datepicker table tr td.selected:hover.active, 458 | .datepicker table tr td.selected.disabled.active, 459 | .datepicker table tr td.selected.disabled:hover.active, 460 | .datepicker table tr td.selected.disabled, 461 | .datepicker table tr td.selected:hover.disabled, 462 | .datepicker table tr td.selected.disabled.disabled, 463 | .datepicker table tr td.selected.disabled:hover.disabled, 464 | .datepicker table tr td.selected[disabled], 465 | .datepicker table tr td.selected:hover[disabled], 466 | .datepicker table tr td.selected.disabled[disabled], 467 | .datepicker table tr td.selected.disabled:hover[disabled] { 468 | background-color: #808080; 469 | } 470 | .datepicker table tr td.selected:active, 471 | .datepicker table tr td.selected:hover:active, 472 | .datepicker table tr td.selected.disabled:active, 473 | .datepicker table tr td.selected.disabled:hover:active, 474 | .datepicker table tr td.selected.active, 475 | .datepicker table tr td.selected:hover.active, 476 | .datepicker table tr td.selected.disabled.active, 477 | .datepicker table tr td.selected.disabled:hover.active { 478 | background-color: #666666 \9; 479 | } 480 | .datepicker table tr td.active, 481 | .datepicker table tr td.active:hover, 482 | .datepicker table tr td.active.disabled, 483 | .datepicker table tr td.active.disabled:hover { 484 | background-color: #006dcc; 485 | background-image: -moz-linear-gradient(top, #0088cc, #0044cc); 486 | background-image: -ms-linear-gradient(top, #0088cc, #0044cc); 487 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); 488 | background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); 489 | background-image: -o-linear-gradient(top, #0088cc, #0044cc); 490 | background-image: linear-gradient(top, #0088cc, #0044cc); 491 | background-repeat: repeat-x; 492 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0); 493 | border-color: #0044cc #0044cc #002a80; 494 | border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); 495 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); 496 | color: #fff; 497 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); 498 | } 499 | .datepicker table tr td.active:hover, 500 | .datepicker table tr td.active:hover:hover, 501 | .datepicker table tr td.active.disabled:hover, 502 | .datepicker table tr td.active.disabled:hover:hover, 503 | .datepicker table tr td.active:active, 504 | .datepicker table tr td.active:hover:active, 505 | .datepicker table tr td.active.disabled:active, 506 | .datepicker table tr td.active.disabled:hover:active, 507 | .datepicker table tr td.active.active, 508 | .datepicker table tr td.active:hover.active, 509 | .datepicker table tr td.active.disabled.active, 510 | .datepicker table tr td.active.disabled:hover.active, 511 | .datepicker table tr td.active.disabled, 512 | .datepicker table tr td.active:hover.disabled, 513 | .datepicker table tr td.active.disabled.disabled, 514 | .datepicker table tr td.active.disabled:hover.disabled, 515 | .datepicker table tr td.active[disabled], 516 | .datepicker table tr td.active:hover[disabled], 517 | .datepicker table tr td.active.disabled[disabled], 518 | .datepicker table tr td.active.disabled:hover[disabled] { 519 | background-color: #0044cc; 520 | } 521 | .datepicker table tr td.active:active, 522 | .datepicker table tr td.active:hover:active, 523 | .datepicker table tr td.active.disabled:active, 524 | .datepicker table tr td.active.disabled:hover:active, 525 | .datepicker table tr td.active.active, 526 | .datepicker table tr td.active:hover.active, 527 | .datepicker table tr td.active.disabled.active, 528 | .datepicker table tr td.active.disabled:hover.active { 529 | background-color: #003399 \9; 530 | } 531 | .datepicker table tr td span { 532 | display: block; 533 | width: 23%; 534 | height: 54px; 535 | line-height: 54px; 536 | float: left; 537 | margin: 1%; 538 | cursor: pointer; 539 | -webkit-border-radius: 4px; 540 | -moz-border-radius: 4px; 541 | border-radius: 4px; 542 | } 543 | .datepicker table tr td span:hover { 544 | background: #eeeeee; 545 | } 546 | .datepicker table tr td span.disabled, 547 | .datepicker table tr td span.disabled:hover { 548 | background: none; 549 | color: #999999; 550 | cursor: default; 551 | } 552 | .datepicker table tr td span.active, 553 | .datepicker table tr td span.active:hover, 554 | .datepicker table tr td span.active.disabled, 555 | .datepicker table tr td span.active.disabled:hover { 556 | background-color: #006dcc; 557 | background-image: -moz-linear-gradient(top, #0088cc, #0044cc); 558 | background-image: -ms-linear-gradient(top, #0088cc, #0044cc); 559 | background-image: -webkit-gradient(linear, 0 0, 0 100%, from(#0088cc), to(#0044cc)); 560 | background-image: -webkit-linear-gradient(top, #0088cc, #0044cc); 561 | background-image: -o-linear-gradient(top, #0088cc, #0044cc); 562 | background-image: linear-gradient(top, #0088cc, #0044cc); 563 | background-repeat: repeat-x; 564 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#0088cc', endColorstr='#0044cc', GradientType=0); 565 | border-color: #0044cc #0044cc #002a80; 566 | border-color: rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.1) rgba(0, 0, 0, 0.25); 567 | filter: progid:DXImageTransform.Microsoft.gradient(enabled=false); 568 | color: #fff; 569 | text-shadow: 0 -1px 0 rgba(0, 0, 0, 0.25); 570 | } 571 | .datepicker table tr td span.active:hover, 572 | .datepicker table tr td span.active:hover:hover, 573 | .datepicker table tr td span.active.disabled:hover, 574 | .datepicker table tr td span.active.disabled:hover:hover, 575 | .datepicker table tr td span.active:active, 576 | .datepicker table tr td span.active:hover:active, 577 | .datepicker table tr td span.active.disabled:active, 578 | .datepicker table tr td span.active.disabled:hover:active, 579 | .datepicker table tr td span.active.active, 580 | .datepicker table tr td span.active:hover.active, 581 | .datepicker table tr td span.active.disabled.active, 582 | .datepicker table tr td span.active.disabled:hover.active, 583 | .datepicker table tr td span.active.disabled, 584 | .datepicker table tr td span.active:hover.disabled, 585 | .datepicker table tr td span.active.disabled.disabled, 586 | .datepicker table tr td span.active.disabled:hover.disabled, 587 | .datepicker table tr td span.active[disabled], 588 | .datepicker table tr td span.active:hover[disabled], 589 | .datepicker table tr td span.active.disabled[disabled], 590 | .datepicker table tr td span.active.disabled:hover[disabled] { 591 | background-color: #0044cc; 592 | } 593 | .datepicker table tr td span.active:active, 594 | .datepicker table tr td span.active:hover:active, 595 | .datepicker table tr td span.active.disabled:active, 596 | .datepicker table tr td span.active.disabled:hover:active, 597 | .datepicker table tr td span.active.active, 598 | .datepicker table tr td span.active:hover.active, 599 | .datepicker table tr td span.active.disabled.active, 600 | .datepicker table tr td span.active.disabled:hover.active { 601 | background-color: #003399 \9; 602 | } 603 | .datepicker table tr td span.old, 604 | .datepicker table tr td span.new { 605 | color: #999999; 606 | } 607 | .datepicker th.datepicker-switch { 608 | width: 145px; 609 | } 610 | .datepicker thead tr:first-child th, 611 | .datepicker tfoot tr th { 612 | cursor: pointer; 613 | } 614 | .datepicker thead tr:first-child th:hover, 615 | .datepicker tfoot tr th:hover { 616 | background: #eeeeee; 617 | } 618 | .datepicker .cw { 619 | font-size: 10px; 620 | width: 12px; 621 | padding: 0 2px 0 5px; 622 | vertical-align: middle; 623 | } 624 | .datepicker thead tr:first-child th.cw { 625 | cursor: default; 626 | background-color: transparent; 627 | } 628 | .input-append.date .add-on i, 629 | .input-prepend.date .add-on i { 630 | display: block; 631 | cursor: pointer; 632 | width: 16px; 633 | height: 16px; 634 | } 635 | .input-daterange input { 636 | text-align: center; 637 | } 638 | .input-daterange input:first-child { 639 | -webkit-border-radius: 3px 0 0 3px; 640 | -moz-border-radius: 3px 0 0 3px; 641 | border-radius: 3px 0 0 3px; 642 | } 643 | .input-daterange input:last-child { 644 | -webkit-border-radius: 0 3px 3px 0; 645 | -moz-border-radius: 0 3px 3px 0; 646 | border-radius: 0 3px 3px 0; 647 | } 648 | .input-daterange .add-on { 649 | display: inline-block; 650 | width: auto; 651 | min-width: 16px; 652 | height: 18px; 653 | padding: 4px 5px; 654 | font-weight: normal; 655 | line-height: 18px; 656 | text-align: center; 657 | text-shadow: 0 1px 0 #ffffff; 658 | vertical-align: middle; 659 | background-color: #eeeeee; 660 | border: 1px solid #ccc; 661 | margin-left: -5px; 662 | margin-right: -5px; 663 | } 664 | -------------------------------------------------------------------------------- /static/css/bootstrap-table.css: -------------------------------------------------------------------------------- 1 | .table { 2 | margin-bottom: 0 !important; 3 | border-bottom: 1px solid #dddddd; 4 | border-collapse: collapse !important; 5 | border-radius: 1px; 6 | } 7 | 8 | .fixed-table-container { 9 | position: relative; 10 | clear: both; 11 | border: 1px solid #dddddd; 12 | border-radius: 4px; 13 | -webkit-border-radius: 4px; 14 | -moz-border-radius: 4px; 15 | } 16 | 17 | .fixed-table-header { 18 | overflow: hidden; 19 | border-radius: 4px 4px 0 0; 20 | -webkit-border-radius: 4px 4px 0 0; 21 | -moz-border-radius: 4px 4px 0 0; 22 | } 23 | 24 | .fixed-table-body { 25 | overflow-x: auto; 26 | overflow-y: auto; 27 | height: 100%; 28 | } 29 | 30 | .fixed-table-container table { 31 | width: 100%; 32 | } 33 | 34 | .fixed-table-container thead th { 35 | height: 0; 36 | padding: 0; 37 | margin: 0; 38 | border-left: 1px solid #dddddd; 39 | } 40 | 41 | .fixed-table-container thead th:first-child { 42 | border-left: none; 43 | border-top-left-radius: 4px; 44 | -webkit-border-top-left-radius: 4px; 45 | -moz-border-radius-topleft: 4px; 46 | } 47 | 48 | .fixed-table-container thead th .th-inner { 49 | padding: 8px; 50 | line-height: 24px; 51 | vertical-align: top; 52 | overflow: hidden; 53 | text-overflow: ellipsis; 54 | white-space: nowrap; 55 | } 56 | 57 | .fixed-table-container thead th .sortable { 58 | cursor: pointer; 59 | } 60 | 61 | .fixed-table-container tbody td { 62 | border-left: 1px solid #dddddd; 63 | } 64 | 65 | .fixed-table-container tbody tr:first-child td { 66 | border-top: none; 67 | } 68 | 69 | .fixed-table-container tbody td:first-child { 70 | border-left: none; 71 | } 72 | 73 | /* the same color with .active */ 74 | .fixed-table-container tbody .selected td { 75 | background-color: #f5f5f5; 76 | } 77 | 78 | .fixed-table-container .bs-checkbox { 79 | text-align: center; 80 | } 81 | 82 | .fixed-table-container .bs-checkbox .th-inner { 83 | padding: 8px 0; 84 | } 85 | 86 | .fixed-table-container input[type="radio"], 87 | .fixed-table-container input[type="checkbox"] { 88 | margin: 0 auto !important; 89 | } 90 | 91 | .fixed-table-container .no-records-found { 92 | text-align: center; 93 | } 94 | 95 | 96 | .fixed-table-pagination .pagination, 97 | .fixed-table-pagination .pagination-detail { 98 | margin-top: 10px; 99 | margin-bottom: 10px; 100 | } 101 | 102 | .fixed-table-pagination .pagination a { 103 | padding: 6px 12px; 104 | line-height: 1.428571429; 105 | } 106 | 107 | .fixed-table-pagination .pagination-info { 108 | line-height: 34px; 109 | margin-right: 5px; 110 | } 111 | 112 | .fixed-table-pagination .btn-group { 113 | position: relative; 114 | display: inline-block; 115 | vertical-align: middle; 116 | } 117 | 118 | .fixed-table-pagination .dropup .dropdown-menu { 119 | margin-bottom: 0; 120 | } 121 | 122 | .fixed-table-pagination .page-list { 123 | display: inline-block; 124 | } 125 | 126 | .fixed-table-toolbar .columns { 127 | margin-left: 5px; 128 | } 129 | 130 | .fixed-table-toolbar .columns label { 131 | display: block; 132 | padding: 3px 20px; 133 | clear: both; 134 | font-weight: normal; 135 | line-height: 1.428571429; 136 | } 137 | 138 | .fixed-table-toolbar .bars, 139 | .fixed-table-toolbar .search, 140 | .fixed-table-toolbar .columns { 141 | position: relative; 142 | margin-top: 10px; 143 | margin-bottom: 10px; 144 | line-height: 34px; 145 | } 146 | 147 | .fixed-table-pagination li.disabled a { 148 | pointer-events: none; 149 | cursor: default; 150 | } 151 | 152 | .fixed-table-loading { 153 | display: none; 154 | position: absolute; 155 | top: 42px; 156 | right: 0; 157 | bottom: 0; 158 | left: 0; 159 | z-index: 99; 160 | background-color: #fff; 161 | text-align: center; 162 | } 163 | 164 | .fixed-table-body .card-view .title { 165 | font-weight: bold; 166 | display: inline-block; 167 | min-width: 30%; 168 | text-align: left !important; 169 | } 170 | 171 | /* support bootstrap 2 */ 172 | .fixed-table-body thead th .th-inner { 173 | box-sizing: border-box; 174 | } 175 | 176 | .table th, .table td { 177 | vertical-align: middle; 178 | box-sizing: border-box; 179 | } 180 | 181 | .fixed-table-toolbar .dropdown-menu { 182 | text-align: left; 183 | max-height: 300px; 184 | overflow: auto; 185 | } 186 | 187 | .fixed-table-toolbar .btn-group>.btn-group { 188 | display: inline-block; 189 | margin-left: -1px !important; 190 | } 191 | 192 | .fixed-table-toolbar .btn-group>.btn-group>.btn { 193 | border-radius: 0; 194 | } 195 | 196 | .fixed-table-toolbar .btn-group>.btn-group:first-child>.btn { 197 | border-top-left-radius: 4px; 198 | border-bottom-left-radius: 4px; 199 | } 200 | 201 | .fixed-table-toolbar .btn-group>.btn-group:last-child>.btn { 202 | border-top-right-radius: 4px; 203 | border-bottom-right-radius: 4px; 204 | } 205 | 206 | .table>thead>tr>th { 207 | vertical-align: bottom; 208 | border-bottom: 2px solid #ddd; 209 | } 210 | 211 | /* support bootstrap 3 */ 212 | .table thead>tr>th { 213 | padding: 0; 214 | margin: 0; 215 | } 216 | 217 | .pull-right .dropdown-menu { 218 | right: 0; 219 | left: auto; 220 | } 221 | 222 | /* calculate scrollbar width */ 223 | p.fixed-table-scroll-inner { 224 | width: 100%; 225 | height: 200px; 226 | } 227 | 228 | div.fixed-table-scroll-outer { 229 | top: 0; 230 | left: 0; 231 | visibility: hidden; 232 | width: 200px; 233 | height: 150px; 234 | overflow: hidden; 235 | } 236 | -------------------------------------------------------------------------------- /static/css/bootstrap-table.min.css: -------------------------------------------------------------------------------- 1 | /* 2 | * bootstrap-table - v1.3.0 - 2014-10-16 3 | * https://github.com/wenzhixin/bootstrap-table 4 | * Copyright (c) 2014 zhixin wen 5 | * Licensed MIT License 6 | */ 7 | 8 | .table{margin-bottom:0!important;border-bottom:1px solid #ddd;border-collapse:collapse!important;border-radius:1px}.fixed-table-container{position:relative;clear:both;border:1px solid #ddd;border-radius:4px;-webkit-border-radius:4px;-moz-border-radius:4px}.fixed-table-header{overflow:hidden;border-radius:4px 4px 0 0;-webkit-border-radius:4px 4px 0 0;-moz-border-radius:4px 4px 0 0}.fixed-table-body{overflow-x:auto;overflow-y:auto;height:100%}.fixed-table-container table{width:100%}.fixed-table-container thead th{height:0;padding:0;margin:0;border-left:1px solid #ddd}.fixed-table-container thead th:first-child{border-left:none;border-top-left-radius:4px;-webkit-border-top-left-radius:4px;-moz-border-radius-topleft:4px}.fixed-table-container thead th .th-inner{padding:8px;line-height:24px;vertical-align:top;overflow:hidden;text-overflow:ellipsis;white-space:nowrap}.fixed-table-container thead th .sortable{cursor:pointer}.fixed-table-container tbody td{border-left:1px solid #ddd}.fixed-table-container tbody tr:first-child td{border-top:none}.fixed-table-container tbody td:first-child{border-left:none}.fixed-table-container tbody .selected td{background-color:#f5f5f5}.fixed-table-container .bs-checkbox{text-align:center}.fixed-table-container .bs-checkbox .th-inner{padding:8px 0}.fixed-table-container input[type=checkbox],.fixed-table-container input[type=radio]{margin:0 auto!important}.fixed-table-container .no-records-found{text-align:center}.fixed-table-pagination .pagination,.fixed-table-pagination .pagination-detail{margin-top:10px;margin-bottom:10px}.fixed-table-pagination .pagination a{padding:6px 12px;line-height:1.428571429}.fixed-table-pagination .pagination-info{line-height:34px;margin-right:5px}.fixed-table-pagination .btn-group{position:relative;display:inline-block;vertical-align:middle}.fixed-table-pagination .dropup .dropdown-menu{margin-bottom:0}.fixed-table-pagination .page-list{display:inline-block}.fixed-table-toolbar .columns{margin-left:5px}.fixed-table-toolbar .columns label{display:block;padding:3px 20px;clear:both;font-weight:400;line-height:1.428571429}.fixed-table-toolbar .bars,.fixed-table-toolbar .columns,.fixed-table-toolbar .search{position:relative;margin-top:10px;margin-bottom:10px;line-height:34px}.fixed-table-pagination li.disabled a{pointer-events:none;cursor:default}.fixed-table-loading{display:none;position:absolute;top:42px;right:0;bottom:0;left:0;z-index:99;background-color:#fff;text-align:center}.fixed-table-body .card-view .title{font-weight:700;display:inline-block;min-width:30%;text-align:left!important}.fixed-table-body thead th .th-inner{box-sizing:border-box}.table td,.table th{vertical-align:middle;box-sizing:border-box}.fixed-table-toolbar .dropdown-menu{text-align:left;max-height:300px;overflow:auto}.fixed-table-toolbar .btn-group>.btn-group{display:inline-block;margin-left:-1px!important}.fixed-table-toolbar .btn-group>.btn-group>.btn{border-radius:0}.fixed-table-toolbar .btn-group>.btn-group:first-child>.btn{border-top-left-radius:4px;border-bottom-left-radius:4px}.fixed-table-toolbar .btn-group>.btn-group:last-child>.btn{border-top-right-radius:4px;border-bottom-right-radius:4px}.table>thead>tr>th{vertical-align:bottom;border-bottom:2px solid #ddd}.table thead>tr>th{padding:0;margin:0}.pull-right .dropdown-menu{right:0;left:auto}p.fixed-table-scroll-inner{width:100%;height:200px}div.fixed-table-scroll-outer{top:0;left:0;visibility:hidden;width:200px;height:150px;overflow:hidden} 9 | -------------------------------------------------------------------------------- /static/css/bootstrap-theme.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.2.0 (http://getbootstrap.com) 3 | * Copyright 2011-2014 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */ 6 | 7 | .btn-default, 8 | .btn-primary, 9 | .btn-success, 10 | .btn-info, 11 | .btn-warning, 12 | .btn-danger { 13 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .2); 14 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); 15 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 1px rgba(0, 0, 0, .075); 16 | } 17 | .btn-default:active, 18 | .btn-primary:active, 19 | .btn-success:active, 20 | .btn-info:active, 21 | .btn-warning:active, 22 | .btn-danger:active, 23 | .btn-default.active, 24 | .btn-primary.active, 25 | .btn-success.active, 26 | .btn-info.active, 27 | .btn-warning.active, 28 | .btn-danger.active { 29 | -webkit-box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); 30 | box-shadow: inset 0 3px 5px rgba(0, 0, 0, .125); 31 | } 32 | .btn:active, 33 | .btn.active { 34 | background-image: none; 35 | } 36 | .btn-default { 37 | text-shadow: 0 1px 0 #fff; 38 | background-image: -webkit-linear-gradient(top, #fff 0%, #e0e0e0 100%); 39 | background-image: -o-linear-gradient(top, #fff 0%, #e0e0e0 100%); 40 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#e0e0e0)); 41 | background-image: linear-gradient(to bottom, #fff 0%, #e0e0e0 100%); 42 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0); 43 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 44 | background-repeat: repeat-x; 45 | border-color: #dbdbdb; 46 | border-color: #ccc; 47 | } 48 | .btn-default:hover, 49 | .btn-default:focus { 50 | background-color: #e0e0e0; 51 | background-position: 0 -15px; 52 | } 53 | .btn-default:active, 54 | .btn-default.active { 55 | background-color: #e0e0e0; 56 | border-color: #dbdbdb; 57 | } 58 | .btn-default:disabled, 59 | .btn-default[disabled] { 60 | background-color: #e0e0e0; 61 | background-image: none; 62 | } 63 | .btn-primary { 64 | background-image: -webkit-linear-gradient(top, #428bca 0%, #2d6ca2 100%); 65 | background-image: -o-linear-gradient(top, #428bca 0%, #2d6ca2 100%); 66 | background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#2d6ca2)); 67 | background-image: linear-gradient(to bottom, #428bca 0%, #2d6ca2 100%); 68 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0); 69 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 70 | background-repeat: repeat-x; 71 | border-color: #2b669a; 72 | } 73 | .btn-primary:hover, 74 | .btn-primary:focus { 75 | background-color: #2d6ca2; 76 | background-position: 0 -15px; 77 | } 78 | .btn-primary:active, 79 | .btn-primary.active { 80 | background-color: #2d6ca2; 81 | border-color: #2b669a; 82 | } 83 | .btn-primary:disabled, 84 | .btn-primary[disabled] { 85 | background-color: #2d6ca2; 86 | background-image: none; 87 | } 88 | .btn-success { 89 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #419641 100%); 90 | background-image: -o-linear-gradient(top, #5cb85c 0%, #419641 100%); 91 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#419641)); 92 | background-image: linear-gradient(to bottom, #5cb85c 0%, #419641 100%); 93 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0); 94 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 95 | background-repeat: repeat-x; 96 | border-color: #3e8f3e; 97 | } 98 | .btn-success:hover, 99 | .btn-success:focus { 100 | background-color: #419641; 101 | background-position: 0 -15px; 102 | } 103 | .btn-success:active, 104 | .btn-success.active { 105 | background-color: #419641; 106 | border-color: #3e8f3e; 107 | } 108 | .btn-success:disabled, 109 | .btn-success[disabled] { 110 | background-color: #419641; 111 | background-image: none; 112 | } 113 | .btn-info { 114 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); 115 | background-image: -o-linear-gradient(top, #5bc0de 0%, #2aabd2 100%); 116 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#2aabd2)); 117 | background-image: linear-gradient(to bottom, #5bc0de 0%, #2aabd2 100%); 118 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0); 119 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 120 | background-repeat: repeat-x; 121 | border-color: #28a4c9; 122 | } 123 | .btn-info:hover, 124 | .btn-info:focus { 125 | background-color: #2aabd2; 126 | background-position: 0 -15px; 127 | } 128 | .btn-info:active, 129 | .btn-info.active { 130 | background-color: #2aabd2; 131 | border-color: #28a4c9; 132 | } 133 | .btn-info:disabled, 134 | .btn-info[disabled] { 135 | background-color: #2aabd2; 136 | background-image: none; 137 | } 138 | .btn-warning { 139 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); 140 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #eb9316 100%); 141 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#eb9316)); 142 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #eb9316 100%); 143 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0); 144 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 145 | background-repeat: repeat-x; 146 | border-color: #e38d13; 147 | } 148 | .btn-warning:hover, 149 | .btn-warning:focus { 150 | background-color: #eb9316; 151 | background-position: 0 -15px; 152 | } 153 | .btn-warning:active, 154 | .btn-warning.active { 155 | background-color: #eb9316; 156 | border-color: #e38d13; 157 | } 158 | .btn-warning:disabled, 159 | .btn-warning[disabled] { 160 | background-color: #eb9316; 161 | background-image: none; 162 | } 163 | .btn-danger { 164 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c12e2a 100%); 165 | background-image: -o-linear-gradient(top, #d9534f 0%, #c12e2a 100%); 166 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c12e2a)); 167 | background-image: linear-gradient(to bottom, #d9534f 0%, #c12e2a 100%); 168 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0); 169 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 170 | background-repeat: repeat-x; 171 | border-color: #b92c28; 172 | } 173 | .btn-danger:hover, 174 | .btn-danger:focus { 175 | background-color: #c12e2a; 176 | background-position: 0 -15px; 177 | } 178 | .btn-danger:active, 179 | .btn-danger.active { 180 | background-color: #c12e2a; 181 | border-color: #b92c28; 182 | } 183 | .btn-danger:disabled, 184 | .btn-danger[disabled] { 185 | background-color: #c12e2a; 186 | background-image: none; 187 | } 188 | .thumbnail, 189 | .img-thumbnail { 190 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 191 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 192 | } 193 | .dropdown-menu > li > a:hover, 194 | .dropdown-menu > li > a:focus { 195 | background-color: #e8e8e8; 196 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 197 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 198 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); 199 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 200 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 201 | background-repeat: repeat-x; 202 | } 203 | .dropdown-menu > .active > a, 204 | .dropdown-menu > .active > a:hover, 205 | .dropdown-menu > .active > a:focus { 206 | background-color: #357ebd; 207 | background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%); 208 | background-image: -o-linear-gradient(top, #428bca 0%, #357ebd 100%); 209 | background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#357ebd)); 210 | background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%); 211 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0); 212 | background-repeat: repeat-x; 213 | } 214 | .navbar-default { 215 | background-image: -webkit-linear-gradient(top, #fff 0%, #f8f8f8 100%); 216 | background-image: -o-linear-gradient(top, #fff 0%, #f8f8f8 100%); 217 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fff), to(#f8f8f8)); 218 | background-image: linear-gradient(to bottom, #fff 0%, #f8f8f8 100%); 219 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0); 220 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 221 | background-repeat: repeat-x; 222 | border-radius: 4px; 223 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); 224 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .15), 0 1px 5px rgba(0, 0, 0, .075); 225 | } 226 | .navbar-default .navbar-nav > .active > a { 227 | background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f3f3f3 100%); 228 | background-image: -o-linear-gradient(top, #ebebeb 0%, #f3f3f3 100%); 229 | background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f3f3f3)); 230 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f3f3f3 100%); 231 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0); 232 | background-repeat: repeat-x; 233 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); 234 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .075); 235 | } 236 | .navbar-brand, 237 | .navbar-nav > li > a { 238 | text-shadow: 0 1px 0 rgba(255, 255, 255, .25); 239 | } 240 | .navbar-inverse { 241 | background-image: -webkit-linear-gradient(top, #3c3c3c 0%, #222 100%); 242 | background-image: -o-linear-gradient(top, #3c3c3c 0%, #222 100%); 243 | background-image: -webkit-gradient(linear, left top, left bottom, from(#3c3c3c), to(#222)); 244 | background-image: linear-gradient(to bottom, #3c3c3c 0%, #222 100%); 245 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0); 246 | filter: progid:DXImageTransform.Microsoft.gradient(enabled = false); 247 | background-repeat: repeat-x; 248 | } 249 | .navbar-inverse .navbar-nav > .active > a { 250 | background-image: -webkit-linear-gradient(top, #222 0%, #282828 100%); 251 | background-image: -o-linear-gradient(top, #222 0%, #282828 100%); 252 | background-image: -webkit-gradient(linear, left top, left bottom, from(#222), to(#282828)); 253 | background-image: linear-gradient(to bottom, #222 0%, #282828 100%); 254 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0); 255 | background-repeat: repeat-x; 256 | -webkit-box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); 257 | box-shadow: inset 0 3px 9px rgba(0, 0, 0, .25); 258 | } 259 | .navbar-inverse .navbar-brand, 260 | .navbar-inverse .navbar-nav > li > a { 261 | text-shadow: 0 -1px 0 rgba(0, 0, 0, .25); 262 | } 263 | .navbar-static-top, 264 | .navbar-fixed-top, 265 | .navbar-fixed-bottom { 266 | border-radius: 0; 267 | } 268 | .alert { 269 | text-shadow: 0 1px 0 rgba(255, 255, 255, .2); 270 | -webkit-box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); 271 | box-shadow: inset 0 1px 0 rgba(255, 255, 255, .25), 0 1px 2px rgba(0, 0, 0, .05); 272 | } 273 | .alert-success { 274 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); 275 | background-image: -o-linear-gradient(top, #dff0d8 0%, #c8e5bc 100%); 276 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#c8e5bc)); 277 | background-image: linear-gradient(to bottom, #dff0d8 0%, #c8e5bc 100%); 278 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0); 279 | background-repeat: repeat-x; 280 | border-color: #b2dba1; 281 | } 282 | .alert-info { 283 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #b9def0 100%); 284 | background-image: -o-linear-gradient(top, #d9edf7 0%, #b9def0 100%); 285 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#b9def0)); 286 | background-image: linear-gradient(to bottom, #d9edf7 0%, #b9def0 100%); 287 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0); 288 | background-repeat: repeat-x; 289 | border-color: #9acfea; 290 | } 291 | .alert-warning { 292 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); 293 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #f8efc0 100%); 294 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#f8efc0)); 295 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #f8efc0 100%); 296 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0); 297 | background-repeat: repeat-x; 298 | border-color: #f5e79e; 299 | } 300 | .alert-danger { 301 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); 302 | background-image: -o-linear-gradient(top, #f2dede 0%, #e7c3c3 100%); 303 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#e7c3c3)); 304 | background-image: linear-gradient(to bottom, #f2dede 0%, #e7c3c3 100%); 305 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0); 306 | background-repeat: repeat-x; 307 | border-color: #dca7a7; 308 | } 309 | .progress { 310 | background-image: -webkit-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); 311 | background-image: -o-linear-gradient(top, #ebebeb 0%, #f5f5f5 100%); 312 | background-image: -webkit-gradient(linear, left top, left bottom, from(#ebebeb), to(#f5f5f5)); 313 | background-image: linear-gradient(to bottom, #ebebeb 0%, #f5f5f5 100%); 314 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0); 315 | background-repeat: repeat-x; 316 | } 317 | .progress-bar { 318 | background-image: -webkit-linear-gradient(top, #428bca 0%, #3071a9 100%); 319 | background-image: -o-linear-gradient(top, #428bca 0%, #3071a9 100%); 320 | background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#3071a9)); 321 | background-image: linear-gradient(to bottom, #428bca 0%, #3071a9 100%); 322 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0); 323 | background-repeat: repeat-x; 324 | } 325 | .progress-bar-success { 326 | background-image: -webkit-linear-gradient(top, #5cb85c 0%, #449d44 100%); 327 | background-image: -o-linear-gradient(top, #5cb85c 0%, #449d44 100%); 328 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5cb85c), to(#449d44)); 329 | background-image: linear-gradient(to bottom, #5cb85c 0%, #449d44 100%); 330 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0); 331 | background-repeat: repeat-x; 332 | } 333 | .progress-bar-info { 334 | background-image: -webkit-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 335 | background-image: -o-linear-gradient(top, #5bc0de 0%, #31b0d5 100%); 336 | background-image: -webkit-gradient(linear, left top, left bottom, from(#5bc0de), to(#31b0d5)); 337 | background-image: linear-gradient(to bottom, #5bc0de 0%, #31b0d5 100%); 338 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0); 339 | background-repeat: repeat-x; 340 | } 341 | .progress-bar-warning { 342 | background-image: -webkit-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 343 | background-image: -o-linear-gradient(top, #f0ad4e 0%, #ec971f 100%); 344 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f0ad4e), to(#ec971f)); 345 | background-image: linear-gradient(to bottom, #f0ad4e 0%, #ec971f 100%); 346 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0); 347 | background-repeat: repeat-x; 348 | } 349 | .progress-bar-danger { 350 | background-image: -webkit-linear-gradient(top, #d9534f 0%, #c9302c 100%); 351 | background-image: -o-linear-gradient(top, #d9534f 0%, #c9302c 100%); 352 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9534f), to(#c9302c)); 353 | background-image: linear-gradient(to bottom, #d9534f 0%, #c9302c 100%); 354 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0); 355 | background-repeat: repeat-x; 356 | } 357 | .progress-bar-striped { 358 | background-image: -webkit-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 359 | background-image: -o-linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 360 | background-image: linear-gradient(45deg, rgba(255, 255, 255, .15) 25%, transparent 25%, transparent 50%, rgba(255, 255, 255, .15) 50%, rgba(255, 255, 255, .15) 75%, transparent 75%, transparent); 361 | } 362 | .list-group { 363 | border-radius: 4px; 364 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 365 | box-shadow: 0 1px 2px rgba(0, 0, 0, .075); 366 | } 367 | .list-group-item.active, 368 | .list-group-item.active:hover, 369 | .list-group-item.active:focus { 370 | text-shadow: 0 -1px 0 #3071a9; 371 | background-image: -webkit-linear-gradient(top, #428bca 0%, #3278b3 100%); 372 | background-image: -o-linear-gradient(top, #428bca 0%, #3278b3 100%); 373 | background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#3278b3)); 374 | background-image: linear-gradient(to bottom, #428bca 0%, #3278b3 100%); 375 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0); 376 | background-repeat: repeat-x; 377 | border-color: #3278b3; 378 | } 379 | .panel { 380 | -webkit-box-shadow: 0 1px 2px rgba(0, 0, 0, .05); 381 | box-shadow: 0 1px 2px rgba(0, 0, 0, .05); 382 | } 383 | .panel-default > .panel-heading { 384 | background-image: -webkit-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 385 | background-image: -o-linear-gradient(top, #f5f5f5 0%, #e8e8e8 100%); 386 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f5f5f5), to(#e8e8e8)); 387 | background-image: linear-gradient(to bottom, #f5f5f5 0%, #e8e8e8 100%); 388 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0); 389 | background-repeat: repeat-x; 390 | } 391 | .panel-primary > .panel-heading { 392 | background-image: -webkit-linear-gradient(top, #428bca 0%, #357ebd 100%); 393 | background-image: -o-linear-gradient(top, #428bca 0%, #357ebd 100%); 394 | background-image: -webkit-gradient(linear, left top, left bottom, from(#428bca), to(#357ebd)); 395 | background-image: linear-gradient(to bottom, #428bca 0%, #357ebd 100%); 396 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0); 397 | background-repeat: repeat-x; 398 | } 399 | .panel-success > .panel-heading { 400 | background-image: -webkit-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); 401 | background-image: -o-linear-gradient(top, #dff0d8 0%, #d0e9c6 100%); 402 | background-image: -webkit-gradient(linear, left top, left bottom, from(#dff0d8), to(#d0e9c6)); 403 | background-image: linear-gradient(to bottom, #dff0d8 0%, #d0e9c6 100%); 404 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0); 405 | background-repeat: repeat-x; 406 | } 407 | .panel-info > .panel-heading { 408 | background-image: -webkit-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); 409 | background-image: -o-linear-gradient(top, #d9edf7 0%, #c4e3f3 100%); 410 | background-image: -webkit-gradient(linear, left top, left bottom, from(#d9edf7), to(#c4e3f3)); 411 | background-image: linear-gradient(to bottom, #d9edf7 0%, #c4e3f3 100%); 412 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0); 413 | background-repeat: repeat-x; 414 | } 415 | .panel-warning > .panel-heading { 416 | background-image: -webkit-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); 417 | background-image: -o-linear-gradient(top, #fcf8e3 0%, #faf2cc 100%); 418 | background-image: -webkit-gradient(linear, left top, left bottom, from(#fcf8e3), to(#faf2cc)); 419 | background-image: linear-gradient(to bottom, #fcf8e3 0%, #faf2cc 100%); 420 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0); 421 | background-repeat: repeat-x; 422 | } 423 | .panel-danger > .panel-heading { 424 | background-image: -webkit-linear-gradient(top, #f2dede 0%, #ebcccc 100%); 425 | background-image: -o-linear-gradient(top, #f2dede 0%, #ebcccc 100%); 426 | background-image: -webkit-gradient(linear, left top, left bottom, from(#f2dede), to(#ebcccc)); 427 | background-image: linear-gradient(to bottom, #f2dede 0%, #ebcccc 100%); 428 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0); 429 | background-repeat: repeat-x; 430 | } 431 | .well { 432 | background-image: -webkit-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); 433 | background-image: -o-linear-gradient(top, #e8e8e8 0%, #f5f5f5 100%); 434 | background-image: -webkit-gradient(linear, left top, left bottom, from(#e8e8e8), to(#f5f5f5)); 435 | background-image: linear-gradient(to bottom, #e8e8e8 0%, #f5f5f5 100%); 436 | filter: progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0); 437 | background-repeat: repeat-x; 438 | border-color: #dcdcdc; 439 | -webkit-box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); 440 | box-shadow: inset 0 1px 3px rgba(0, 0, 0, .05), 0 1px 0 rgba(255, 255, 255, .1); 441 | } 442 | /*# sourceMappingURL=bootstrap-theme.css.map */ 443 | -------------------------------------------------------------------------------- /static/css/bootstrap-theme.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Bootstrap v3.2.0 (http://getbootstrap.com) 3 | * Copyright 2011-2014 Twitter, Inc. 4 | * Licensed under MIT (https://github.com/twbs/bootstrap/blob/master/LICENSE) 5 | */.btn-default,.btn-primary,.btn-success,.btn-info,.btn-warning,.btn-danger{text-shadow:0 -1px 0 rgba(0,0,0,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 1px rgba(0,0,0,.075)}.btn-default:active,.btn-primary:active,.btn-success:active,.btn-info:active,.btn-warning:active,.btn-danger:active,.btn-default.active,.btn-primary.active,.btn-success.active,.btn-info.active,.btn-warning.active,.btn-danger.active{-webkit-box-shadow:inset 0 3px 5px rgba(0,0,0,.125);box-shadow:inset 0 3px 5px rgba(0,0,0,.125)}.btn:active,.btn.active{background-image:none}.btn-default{text-shadow:0 1px 0 #fff;background-image:-webkit-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-o-linear-gradient(top,#fff 0,#e0e0e0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#e0e0e0));background-image:linear-gradient(to bottom,#fff 0,#e0e0e0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#ffe0e0e0', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#dbdbdb;border-color:#ccc}.btn-default:hover,.btn-default:focus{background-color:#e0e0e0;background-position:0 -15px}.btn-default:active,.btn-default.active{background-color:#e0e0e0;border-color:#dbdbdb}.btn-default:disabled,.btn-default[disabled]{background-color:#e0e0e0;background-image:none}.btn-primary{background-image:-webkit-linear-gradient(top,#428bca 0,#2d6ca2 100%);background-image:-o-linear-gradient(top,#428bca 0,#2d6ca2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#2d6ca2));background-image:linear-gradient(to bottom,#428bca 0,#2d6ca2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff2d6ca2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#2b669a}.btn-primary:hover,.btn-primary:focus{background-color:#2d6ca2;background-position:0 -15px}.btn-primary:active,.btn-primary.active{background-color:#2d6ca2;border-color:#2b669a}.btn-primary:disabled,.btn-primary[disabled]{background-color:#2d6ca2;background-image:none}.btn-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#419641 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#419641));background-image:linear-gradient(to bottom,#5cb85c 0,#419641 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff419641', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#3e8f3e}.btn-success:hover,.btn-success:focus{background-color:#419641;background-position:0 -15px}.btn-success:active,.btn-success.active{background-color:#419641;border-color:#3e8f3e}.btn-success:disabled,.btn-success[disabled]{background-color:#419641;background-image:none}.btn-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#2aabd2 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#2aabd2));background-image:linear-gradient(to bottom,#5bc0de 0,#2aabd2 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff2aabd2', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#28a4c9}.btn-info:hover,.btn-info:focus{background-color:#2aabd2;background-position:0 -15px}.btn-info:active,.btn-info.active{background-color:#2aabd2;border-color:#28a4c9}.btn-info:disabled,.btn-info[disabled]{background-color:#2aabd2;background-image:none}.btn-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#eb9316 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#eb9316));background-image:linear-gradient(to bottom,#f0ad4e 0,#eb9316 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffeb9316', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#e38d13}.btn-warning:hover,.btn-warning:focus{background-color:#eb9316;background-position:0 -15px}.btn-warning:active,.btn-warning.active{background-color:#eb9316;border-color:#e38d13}.btn-warning:disabled,.btn-warning[disabled]{background-color:#eb9316;background-image:none}.btn-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c12e2a 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c12e2a));background-image:linear-gradient(to bottom,#d9534f 0,#c12e2a 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc12e2a', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-color:#b92c28}.btn-danger:hover,.btn-danger:focus{background-color:#c12e2a;background-position:0 -15px}.btn-danger:active,.btn-danger.active{background-color:#c12e2a;border-color:#b92c28}.btn-danger:disabled,.btn-danger[disabled]{background-color:#c12e2a;background-image:none}.thumbnail,.img-thumbnail{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.dropdown-menu>li>a:hover,.dropdown-menu>li>a:focus{background-color:#e8e8e8;background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.dropdown-menu>.active>a,.dropdown-menu>.active>a:hover,.dropdown-menu>.active>a:focus{background-color:#357ebd;background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:-o-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#357ebd));background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);background-repeat:repeat-x}.navbar-default{background-image:-webkit-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-o-linear-gradient(top,#fff 0,#f8f8f8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fff),to(#f8f8f8));background-image:linear-gradient(to bottom,#fff 0,#f8f8f8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffffffff', endColorstr='#fff8f8f8', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x;border-radius:4px;-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075);box-shadow:inset 0 1px 0 rgba(255,255,255,.15),0 1px 5px rgba(0,0,0,.075)}.navbar-default .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f3f3f3 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f3f3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f3f3f3));background-image:linear-gradient(to bottom,#ebebeb 0,#f3f3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff3f3f3', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.075);box-shadow:inset 0 3px 9px rgba(0,0,0,.075)}.navbar-brand,.navbar-nav>li>a{text-shadow:0 1px 0 rgba(255,255,255,.25)}.navbar-inverse{background-image:-webkit-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-o-linear-gradient(top,#3c3c3c 0,#222 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#3c3c3c),to(#222));background-image:linear-gradient(to bottom,#3c3c3c 0,#222 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff3c3c3c', endColorstr='#ff222222', GradientType=0);filter:progid:DXImageTransform.Microsoft.gradient(enabled=false);background-repeat:repeat-x}.navbar-inverse .navbar-nav>.active>a{background-image:-webkit-linear-gradient(top,#222 0,#282828 100%);background-image:-o-linear-gradient(top,#222 0,#282828 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#222),to(#282828));background-image:linear-gradient(to bottom,#222 0,#282828 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff222222', endColorstr='#ff282828', GradientType=0);background-repeat:repeat-x;-webkit-box-shadow:inset 0 3px 9px rgba(0,0,0,.25);box-shadow:inset 0 3px 9px rgba(0,0,0,.25)}.navbar-inverse .navbar-brand,.navbar-inverse .navbar-nav>li>a{text-shadow:0 -1px 0 rgba(0,0,0,.25)}.navbar-static-top,.navbar-fixed-top,.navbar-fixed-bottom{border-radius:0}.alert{text-shadow:0 1px 0 rgba(255,255,255,.2);-webkit-box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05);box-shadow:inset 0 1px 0 rgba(255,255,255,.25),0 1px 2px rgba(0,0,0,.05)}.alert-success{background-image:-webkit-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#c8e5bc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#c8e5bc));background-image:linear-gradient(to bottom,#dff0d8 0,#c8e5bc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffc8e5bc', GradientType=0);background-repeat:repeat-x;border-color:#b2dba1}.alert-info{background-image:-webkit-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#b9def0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#b9def0));background-image:linear-gradient(to bottom,#d9edf7 0,#b9def0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffb9def0', GradientType=0);background-repeat:repeat-x;border-color:#9acfea}.alert-warning{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#f8efc0 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#f8efc0));background-image:linear-gradient(to bottom,#fcf8e3 0,#f8efc0 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fff8efc0', GradientType=0);background-repeat:repeat-x;border-color:#f5e79e}.alert-danger{background-image:-webkit-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-o-linear-gradient(top,#f2dede 0,#e7c3c3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#e7c3c3));background-image:linear-gradient(to bottom,#f2dede 0,#e7c3c3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffe7c3c3', GradientType=0);background-repeat:repeat-x;border-color:#dca7a7}.progress{background-image:-webkit-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#ebebeb 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#ebebeb),to(#f5f5f5));background-image:linear-gradient(to bottom,#ebebeb 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffebebeb', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x}.progress-bar{background-image:-webkit-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:-o-linear-gradient(top,#428bca 0,#3071a9 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#3071a9));background-image:linear-gradient(to bottom,#428bca 0,#3071a9 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3071a9', GradientType=0);background-repeat:repeat-x}.progress-bar-success{background-image:-webkit-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-o-linear-gradient(top,#5cb85c 0,#449d44 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5cb85c),to(#449d44));background-image:linear-gradient(to bottom,#5cb85c 0,#449d44 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5cb85c', endColorstr='#ff449d44', GradientType=0);background-repeat:repeat-x}.progress-bar-info{background-image:-webkit-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-o-linear-gradient(top,#5bc0de 0,#31b0d5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#5bc0de),to(#31b0d5));background-image:linear-gradient(to bottom,#5bc0de 0,#31b0d5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff5bc0de', endColorstr='#ff31b0d5', GradientType=0);background-repeat:repeat-x}.progress-bar-warning{background-image:-webkit-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-o-linear-gradient(top,#f0ad4e 0,#ec971f 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f0ad4e),to(#ec971f));background-image:linear-gradient(to bottom,#f0ad4e 0,#ec971f 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff0ad4e', endColorstr='#ffec971f', GradientType=0);background-repeat:repeat-x}.progress-bar-danger{background-image:-webkit-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-o-linear-gradient(top,#d9534f 0,#c9302c 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9534f),to(#c9302c));background-image:linear-gradient(to bottom,#d9534f 0,#c9302c 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9534f', endColorstr='#ffc9302c', GradientType=0);background-repeat:repeat-x}.progress-bar-striped{background-image:-webkit-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:-o-linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent);background-image:linear-gradient(45deg,rgba(255,255,255,.15) 25%,transparent 25%,transparent 50%,rgba(255,255,255,.15) 50%,rgba(255,255,255,.15) 75%,transparent 75%,transparent)}.list-group{border-radius:4px;-webkit-box-shadow:0 1px 2px rgba(0,0,0,.075);box-shadow:0 1px 2px rgba(0,0,0,.075)}.list-group-item.active,.list-group-item.active:hover,.list-group-item.active:focus{text-shadow:0 -1px 0 #3071a9;background-image:-webkit-linear-gradient(top,#428bca 0,#3278b3 100%);background-image:-o-linear-gradient(top,#428bca 0,#3278b3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#3278b3));background-image:linear-gradient(to bottom,#428bca 0,#3278b3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff3278b3', GradientType=0);background-repeat:repeat-x;border-color:#3278b3}.panel{-webkit-box-shadow:0 1px 2px rgba(0,0,0,.05);box-shadow:0 1px 2px rgba(0,0,0,.05)}.panel-default>.panel-heading{background-image:-webkit-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-o-linear-gradient(top,#f5f5f5 0,#e8e8e8 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f5f5f5),to(#e8e8e8));background-image:linear-gradient(to bottom,#f5f5f5 0,#e8e8e8 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff5f5f5', endColorstr='#ffe8e8e8', GradientType=0);background-repeat:repeat-x}.panel-primary>.panel-heading{background-image:-webkit-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:-o-linear-gradient(top,#428bca 0,#357ebd 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#428bca),to(#357ebd));background-image:linear-gradient(to bottom,#428bca 0,#357ebd 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ff428bca', endColorstr='#ff357ebd', GradientType=0);background-repeat:repeat-x}.panel-success>.panel-heading{background-image:-webkit-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-o-linear-gradient(top,#dff0d8 0,#d0e9c6 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#dff0d8),to(#d0e9c6));background-image:linear-gradient(to bottom,#dff0d8 0,#d0e9c6 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffdff0d8', endColorstr='#ffd0e9c6', GradientType=0);background-repeat:repeat-x}.panel-info>.panel-heading{background-image:-webkit-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-o-linear-gradient(top,#d9edf7 0,#c4e3f3 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#d9edf7),to(#c4e3f3));background-image:linear-gradient(to bottom,#d9edf7 0,#c4e3f3 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffd9edf7', endColorstr='#ffc4e3f3', GradientType=0);background-repeat:repeat-x}.panel-warning>.panel-heading{background-image:-webkit-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-o-linear-gradient(top,#fcf8e3 0,#faf2cc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#fcf8e3),to(#faf2cc));background-image:linear-gradient(to bottom,#fcf8e3 0,#faf2cc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fffcf8e3', endColorstr='#fffaf2cc', GradientType=0);background-repeat:repeat-x}.panel-danger>.panel-heading{background-image:-webkit-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-o-linear-gradient(top,#f2dede 0,#ebcccc 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#f2dede),to(#ebcccc));background-image:linear-gradient(to bottom,#f2dede 0,#ebcccc 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#fff2dede', endColorstr='#ffebcccc', GradientType=0);background-repeat:repeat-x}.well{background-image:-webkit-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-o-linear-gradient(top,#e8e8e8 0,#f5f5f5 100%);background-image:-webkit-gradient(linear,left top,left bottom,from(#e8e8e8),to(#f5f5f5));background-image:linear-gradient(to bottom,#e8e8e8 0,#f5f5f5 100%);filter:progid:DXImageTransform.Microsoft.gradient(startColorstr='#ffe8e8e8', endColorstr='#fff5f5f5', GradientType=0);background-repeat:repeat-x;border-color:#dcdcdc;-webkit-box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1);box-shadow:inset 0 1px 3px rgba(0,0,0,.05),0 1px 0 rgba(255,255,255,.1)} -------------------------------------------------------------------------------- /static/css/jquery-cron.css: -------------------------------------------------------------------------------- 1 | .cron-button { 2 | height: 16px; 3 | padding-left: 20px; 4 | margin-left: 5px; 5 | background-repeat: no-repeat; 6 | background-position: center center; 7 | cursor: pointer; 8 | } 9 | .cron-changed { 10 | padding-top: 5px; 11 | padding-bottom: 5px; 12 | background-color: #fdd; 13 | } 14 | .cron-controls { 15 | margin-left: 10px; 16 | color: #c77; 17 | font-size: 0.9em; 18 | } 19 | .cron-controls > span.cron-loading { 20 | background-image: url('img/loading.gif'); 21 | } -------------------------------------------------------------------------------- /static/fonts/glyphicons-halflings-regular.eot: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daimoniac/asprom/aa37661f137d926e114292a0a75b70e9fc80d8a6/static/fonts/glyphicons-halflings-regular.eot -------------------------------------------------------------------------------- /static/fonts/glyphicons-halflings-regular.ttf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daimoniac/asprom/aa37661f137d926e114292a0a75b70e9fc80d8a6/static/fonts/glyphicons-halflings-regular.ttf -------------------------------------------------------------------------------- /static/fonts/glyphicons-halflings-regular.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/daimoniac/asprom/aa37661f137d926e114292a0a75b70e9fc80d8a6/static/fonts/glyphicons-halflings-regular.woff -------------------------------------------------------------------------------- /static/js/asprom.js: -------------------------------------------------------------------------------- 1 | function operateFormatterExposed(value, row, index) { 2 | return [ 3 | '', 4 | '', 5 | '', 6 | '', 7 | '', 8 | '', 9 | '', 10 | '', 11 | '' 12 | ].join(''); 13 | } 14 | 15 | function operateFormatterClosed(value, row, index) { 16 | return [ 17 | '', 18 | '', 19 | '', 20 | '', 21 | '', 22 | '', 23 | '', 24 | '', 25 | '' 26 | ].join(''); 27 | } 28 | 29 | function operateFormatterNeatline(value, row, index) { 30 | return [ 31 | '', 32 | '', 33 | '', 34 | '', 35 | '', 36 | '' 37 | ].join(''); 38 | } 39 | 40 | 41 | function operateFormatterSchedule(value, row, index) { 42 | return [ 43 | '', 44 | '', 45 | '', 46 | '', 47 | '', 48 | '', 49 | '', 50 | '', 51 | '' 52 | ].join(''); 53 | } 54 | 55 | function operateFormatterForensic(value, row, index) { 56 | return [ 57 | '', 58 | '', 59 | '', 60 | '', 61 | '', 62 | '' 63 | ].join(''); 64 | } 65 | 66 | 67 | function whenFormatter(value, row) { 68 | return '' + value + ''; 69 | } 70 | 71 | 72 | function rowStyle(row, index) { 73 | if ((typeof(row.crit) !== 'undefined') && (row.crit !== null)) { 74 | if ( row.crit ) { 75 | if (index % 2) { 76 | return {classes:'alert-danger'}; 77 | } else { 78 | return {classes:'alert-danger danger-2'} 79 | } 80 | } else { 81 | if (index % 2) { 82 | return {classes:'alert-warning'}; 83 | } else { 84 | return {classes:'alert-warning warning-2'} 85 | } 86 | } 87 | } 88 | else { 89 | if (index % 2) { 90 | return {classes:'alert-info aspinfo'}; 91 | } else { 92 | return {classes:'alert-info aspinfo-2'}; 93 | } 94 | } 95 | return{classes:''}; 96 | } 97 | 98 | window.operateEvents = { 99 | 'click .rescanjob': function (e, value, row, index) { 100 | subRescan("job", row.id); 101 | console.log("rescan scheduled for" + row.id); 102 | }, 103 | 'click .rescan': function (e, value, row, index) { 104 | subRescan("service", row.id); 105 | console.log("rescan scheduled for" + row.id); 106 | }, 107 | 'click .critEx': function (e, value, row, index) { 108 | subFlipCrit('exposed', row.id) 109 | console.log(value, row, index); 110 | }, 111 | 'click .critCl': function (e, value, row, index) { 112 | subFlipCrit('closed', row.id) 113 | console.log(value, row, index); 114 | }, 115 | 'click .editjob': function (e, value, row, index) { 116 | subEditJob(row.id); 117 | console.log(value, row, index); 118 | }, 119 | 'click .deletejob': function (e, value, row, index) { 120 | subDeleteJob(row.id); 121 | console.log(value, row, index); 122 | }, 123 | 'click .deletemachine': function (e, value, row, index) { 124 | if (confirm('The whole machine will be deleted (all service entries). Are you sure you want to delete this machine and all its services?')) { 125 | subDeleteMachine(row.machineId); 126 | } 127 | } 128 | }; 129 | 130 | // Button events 131 | 132 | function subEditJob(argId) { 133 | BootstrapDialog.show({ 134 | message: $('
').load('/dia/editjob/' + argId) 135 | })}; 136 | 137 | function subDeleteJob(argId) { 138 | $.ajax({ 139 | url: "/controller/deletejob/" + argId, 140 | type: "GET", 141 | cache: false, 142 | success: function( json ) { 143 | $('#datatable').bootstrapTable('refresh'); 144 | }, 145 | error: function( xhr, status, errorThrown ) { 146 | addAlert("Error deleting job " + argId + ".", 'danger'); 147 | }, 148 | })}; 149 | 150 | 151 | // AJAX Stuff 152 | function subRescan(type, argId) { 153 | //Using the core $.ajax() method 154 | $.ajax({ 155 | // the URL for the request 156 | url: "/controller/rescan" + type + "/" + argId, 157 | type: "GET", 158 | cache: false, 159 | // the type of data we expect back 160 | //dataType : "json", 161 | beforeSend: function () { 162 | addAlert("Commencing rescan of " + type + " " + argId + ".", 'info', true); 163 | }, 164 | // code to run if the request succeeds; 165 | // the response is passed to the function 166 | success: function( json ) { 167 | $('#datatable').bootstrapTable('refresh'); 168 | addAlert("Successfully rescanned " + type + " " + argId + ".", 'success', true); 169 | }, 170 | // code to run if the request fails; the raw request and 171 | // status codes are passed to the function 172 | error: function( xhr, status, errorThrown ) { 173 | addAlert("Error rescanning " + type + " " + argId + ".", 'danger'); 174 | }, 175 | // code to run regardless of success or failure 176 | //always: function( xhr, status ) { 177 | //alert( "The request is complete!" ); 178 | //} 179 | })}; 180 | 181 | 182 | function subFlipCrit(page, argId) { 183 | $.ajax({ 184 | url: "/controller/flipcrit/" + page + "/" + argId, 185 | type: "GET", 186 | cache: false, 187 | success: function( json ) { 188 | $('#datatable').bootstrapTable('refresh'); 189 | }, 190 | error: function( xhr, status, errorThrown ) { 191 | addAlert("Error flipping criticality of ID " + argId + ".", 'danger'); 192 | }, 193 | })}; 194 | 195 | 196 | function alertTimeout(wait){ 197 | setTimeout(function(){ 198 | $('#notification-area').children('.autoclose:first-child').fadeTo(500, 0).slideUp(500, function(){ 199 | $(this).remove(); 200 | }); 201 | }, wait); 202 | } 203 | 204 | 205 | function addAlert(message, clas, autoclose) { 206 | if (autoclose == true) { 207 | clas = clas + " autoclose"; 208 | } 209 | $('#notification-area').append( 210 | ''); 213 | if (autoclose == true) { 214 | alertTimeout(12000); 215 | } 216 | } 217 | 218 | 219 | 220 | $(document).ready(function() { 221 | //x-editable settings 222 | $.fn.editable.defaults.mode = 'popup'; 223 | 224 | $('#dataform').editable({ 225 | selector: '.approve i', 226 | url: '/controller/approve', 227 | type: 'text', 228 | emptytext: '', 229 | onblur: 'submit', 230 | showbuttons: true, 231 | 232 | ajaxOptions: { 233 | success: function( json ) { 234 | $('#datatable').bootstrapTable('refresh'); 235 | } 236 | } 237 | 238 | }); 239 | $('#dataform').editable({ 240 | selector: '.remove i', 241 | url: '/controller/remove', 242 | type: 'text', 243 | emptytext: '', 244 | onblur: 'submit', 245 | showbuttons: true, 246 | 247 | ajaxOptions: { 248 | success: function( json ) { 249 | $('#datatable').bootstrapTable('refresh'); 250 | } 251 | } 252 | 253 | }); 254 | 255 | $('#learnmore').click(function() { 256 | $('#learnmore').fadeOut('slow'); 257 | $('.moretolearn').fadeIn('slow'); 258 | }); 259 | 260 | $('#log').load('/log'); 261 | 262 | }); 263 | 264 | function subDeleteMachine(machineId) { 265 | $.ajax({ 266 | url: "/controller/deletemachine/" + machineId, 267 | type: "GET", 268 | cache: false, 269 | success: function(json) { 270 | $('#datatable').bootstrapTable('refresh'); 271 | addAlert("Successfully deleted machine and its services", 'success', true); 272 | }, 273 | error: function(xhr, status, errorThrown) { 274 | addAlert("Error deleting machine", 'danger'); 275 | } 276 | }); 277 | } 278 | 279 | -------------------------------------------------------------------------------- /static/js/bootstrap-dialog.min.js: -------------------------------------------------------------------------------- 1 | (function(a,b){if(typeof module!=="undefined"&&module.exports){module.exports=b(require("jquery")(a))}else{if(typeof define==="function"&&define.amd){define("bootstrap-dialog",["jquery"],function(c){return b(c)})}else{a.BootstrapDialog=b(a.jQuery)}}}(this,function(b){var a=function(c){this.defaultOptions=b.extend(true,{id:a.newGuid(),buttons:[],data:{},onshow:null,onshown:null,onhide:null,onhidden:null},a.defaultOptions);this.indexedButtons={};this.registeredButtonHotkeys={};this.draggableData={isMouseDown:false,mouseOffset:{}};this.realized=false;this.opened=false;this.initOptions(c);this.holdThisInstance()};a.NAMESPACE="bootstrap-dialog";a.TYPE_DEFAULT="type-default";a.TYPE_INFO="type-info";a.TYPE_PRIMARY="type-primary";a.TYPE_SUCCESS="type-success";a.TYPE_WARNING="type-warning";a.TYPE_DANGER="type-danger";a.DEFAULT_TEXTS={};a.DEFAULT_TEXTS[a.TYPE_DEFAULT]="Information";a.DEFAULT_TEXTS[a.TYPE_INFO]="Information";a.DEFAULT_TEXTS[a.TYPE_PRIMARY]="Information";a.DEFAULT_TEXTS[a.TYPE_SUCCESS]="Success";a.DEFAULT_TEXTS[a.TYPE_WARNING]="Warning";a.DEFAULT_TEXTS[a.TYPE_DANGER]="Danger";a.DEFAULT_TEXTS.OK="OK";a.DEFAULT_TEXTS.CANCEL="Cancel";a.SIZE_NORMAL="size-normal";a.SIZE_WIDE="size-wide";a.SIZE_LARGE="size-large";a.BUTTON_SIZES={};a.BUTTON_SIZES[a.SIZE_NORMAL]="";a.BUTTON_SIZES[a.SIZE_WIDE]="";a.BUTTON_SIZES[a.SIZE_LARGE]="btn-lg";a.ICON_SPINNER="glyphicon glyphicon-asterisk";a.ZINDEX_BACKDROP=1040;a.ZINDEX_MODAL=1050;a.defaultOptions={type:a.TYPE_PRIMARY,size:a.SIZE_NORMAL,cssClass:"",title:null,message:null,nl2br:true,closable:true,closeByBackdrop:true,closeByKeyboard:true,spinicon:a.ICON_SPINNER,autodestroy:true,draggable:false,animate:true,description:""};a.configDefaultOptions=function(c){a.defaultOptions=b.extend(true,a.defaultOptions,c)};a.dialogs={};a.openAll=function(){b.each(a.dialogs,function(d,c){c.open()})};a.closeAll=function(){b.each(a.dialogs,function(d,c){c.close()})};a.moveFocus=function(){var c=null;b.each(a.dialogs,function(e,d){c=d});if(c!==null&&c.isRealized()){c.getModal().focus()}};a.showScrollbar=function(){var c=null;b.each(a.dialogs,function(f,e){c=e});if(c!==null&&c.isRealized()&&c.isOpened()){var d=c.getModal().data("bs.modal");d.checkScrollbar();b("body").addClass("modal-open");d.setScrollbar()}};a.prototype={constructor:a,initOptions:function(c){this.options=b.extend(true,this.defaultOptions,c);return this},holdThisInstance:function(){a.dialogs[this.getId()]=this;return this},initModalStuff:function(){this.setModal(this.createModal()).setModalDialog(this.createModalDialog()).setModalContent(this.createModalContent()).setModalHeader(this.createModalHeader()).setModalBody(this.createModalBody()).setModalFooter(this.createModalFooter());this.getModal().append(this.getModalDialog());this.getModalDialog().append(this.getModalContent());this.getModalContent().append(this.getModalHeader()).append(this.getModalBody()).append(this.getModalFooter());return this},createModal:function(){var c=b('');c.prop("id",this.getId()).attr("aria-labelledby",this.getId()+"_title");return c},getModal:function(){return this.$modal},setModal:function(c){this.$modal=c;return this},createModalDialog:function(){return b('')},getModalDialog:function(){return this.$modalDialog},setModalDialog:function(c){this.$modalDialog=c;return this},createModalContent:function(){return b('')},getModalContent:function(){return this.$modalContent},setModalContent:function(c){this.$modalContent=c;return this},createModalHeader:function(){return b('')},getModalHeader:function(){return this.$modalHeader},setModalHeader:function(c){this.$modalHeader=c;return this},createModalBody:function(){return b('')},getModalBody:function(){return this.$modalBody},setModalBody:function(c){this.$modalBody=c;return this},createModalFooter:function(){return b('')},getModalFooter:function(){return this.$modalFooter},setModalFooter:function(c){this.$modalFooter=c;return this},createDynamicContent:function(d){var c=null;if(typeof d==="function"){c=d.call(d,this)}else{c=d}if(typeof c==="string"){c=this.formatStringContent(c)}return c},formatStringContent:function(c){if(this.options.nl2br){return c.replace(/\r\n/g,"
").replace(/[\r\n]/g,"
")}return c},setData:function(c,d){this.options.data[c]=d;return this},getData:function(c){return this.options.data[c]},setId:function(c){this.options.id=c;return this},getId:function(){return this.options.id},getType:function(){return this.options.type},setType:function(c){this.options.type=c;this.updateType();return this},updateType:function(){if(this.isRealized()){var c=[a.TYPE_DEFAULT,a.TYPE_INFO,a.TYPE_PRIMARY,a.TYPE_SUCCESS,a.TYPE_WARNING,a.TYPE_DANGER];this.getModal().removeClass(c.join(" ")).addClass(this.getType())}return this},getSize:function(){return this.options.size},setSize:function(c){this.options.size=c;this.updateSize();return this},updateSize:function(){if(this.isRealized()){var c=this;this.getModal().removeClass(a.SIZE_NORMAL).removeClass(a.SIZE_WIDE).removeClass(a.SIZE_LARGE);this.getModal().addClass(this.getSize());this.getModalDialog().removeClass("modal-lg");if(this.getSize()===a.SIZE_WIDE){this.getModalDialog().addClass("modal-lg")}b.each(this.options.buttons,function(e,g){var i=c.getButton(g.id);var d=["btn-lg","btn-sm","btn-xs"];var h=false;if(typeof g.cssClass==="string"){var f=g.cssClass.split(" ");b.each(f,function(j,k){if(b.inArray(k,d)!==-1){h=true}})}if(!h){i.removeClass(d.join(" "));i.addClass(c.getButtonSize())}})}return this},getCssClass:function(){return this.options.cssClass},setCssClass:function(c){this.options.cssClass=c;return this},getTitle:function(){return this.options.title},setTitle:function(c){this.options.title=c;this.updateTitle();return this},updateTitle:function(){if(this.isRealized()){var c=this.getTitle()!==null?this.createDynamicContent(this.getTitle()):this.getDefaultText();this.getModalHeader().find("."+this.getNamespace("title")).html("").append(c).prop("id",this.getId()+"_title")}return this},getMessage:function(){return this.options.message},setMessage:function(c){this.options.message=c;this.updateMessage();return this},updateMessage:function(){if(this.isRealized()){var c=this.createDynamicContent(this.getMessage());this.getModalBody().find("."+this.getNamespace("message")).html("").append(c)}return this},isClosable:function(){return this.options.closable},setClosable:function(c){this.options.closable=c;this.updateClosable();return this},setCloseByBackdrop:function(c){this.options.closeByBackdrop=c;return this},canCloseByBackdrop:function(){return this.options.closeByBackdrop},setCloseByKeyboard:function(c){this.options.closeByKeyboard=c;return this},canCloseByKeyboard:function(){return this.options.closeByKeyboard},isAnimate:function(){return this.options.animate},setAnimate:function(c){this.options.animate=c;return this},updateAnimate:function(){if(this.isRealized()){this.getModal().toggleClass("fade",this.isAnimate())}return this},getSpinicon:function(){return this.options.spinicon},setSpinicon:function(c){this.options.spinicon=c;return this},addButton:function(c){this.options.buttons.push(c);return this},addButtons:function(d){var c=this;b.each(d,function(e,f){c.addButton(f)});return this},getButtons:function(){return this.options.buttons},setButtons:function(c){this.options.buttons=c;this.updateButtons();return this},getButton:function(c){if(typeof this.indexedButtons[c]!=="undefined"){return this.indexedButtons[c]}return null},getButtonSize:function(){if(typeof a.BUTTON_SIZES[this.getSize()]!=="undefined"){return a.BUTTON_SIZES[this.getSize()]}return""},updateButtons:function(){if(this.isRealized()){if(this.getButtons().length===0){this.getModalFooter().hide()}else{this.getModalFooter().find("."+this.getNamespace("footer")).html("").append(this.createFooterButtons())}}return this},isAutodestroy:function(){return this.options.autodestroy},setAutodestroy:function(c){this.options.autodestroy=c},getDescription:function(){return this.options.description},setDescription:function(c){this.options.description=c;return this},getDefaultText:function(){return a.DEFAULT_TEXTS[this.getType()]},getNamespace:function(c){return a.NAMESPACE+"-"+c},createHeaderContent:function(){var c=b("
");c.addClass(this.getNamespace("header"));c.append(this.createTitleContent());c.prepend(this.createCloseButton());return c},createTitleContent:function(){var c=b("
");c.addClass(this.getNamespace("title"));return c},createCloseButton:function(){var d=b("
");d.addClass(this.getNamespace("close-button"));var c=b('');d.append(c);d.on("click",{dialog:this},function(e){e.data.dialog.close()});return d},createBodyContent:function(){var c=b("
");c.addClass(this.getNamespace("body"));c.append(this.createMessageContent());return c},createMessageContent:function(){var c=b("
");c.addClass(this.getNamespace("message"));return c},createFooterContent:function(){var c=b("
");c.addClass(this.getNamespace("footer"));return c},createFooterButtons:function(){var c=this;var d=b("
");d.addClass(this.getNamespace("footer-buttons"));this.indexedButtons={};b.each(this.options.buttons,function(e,f){if(!f.id){f.id=a.newGuid()}var g=c.createButton(f);c.indexedButtons[f.id]=g;d.append(g)});return d},createButton:function(c){var d=b('');d.prop("id",c.id);if(typeof c.icon!=="undefined"&&b.trim(c.icon)!==""){d.append(this.createButtonIcon(c.icon))}if(typeof c.label!=="undefined"){d.append(c.label)}if(typeof c.cssClass!=="undefined"&&b.trim(c.cssClass)!==""){d.addClass(c.cssClass)}else{d.addClass("btn-default")}if(typeof c.hotkey!=="undefined"){this.registeredButtonHotkeys[c.hotkey]=d}d.on("click",{dialog:this,$button:d,button:c},function(g){var f=g.data.dialog;var h=g.data.$button;var e=g.data.button;if(typeof e.action==="function"){e.action.call(h,f)}if(e.autospin){h.toggleSpin(true)}});this.enhanceButton(d);return d},enhanceButton:function(c){c.dialog=this;c.toggleEnable=function(d){var e=this;if(typeof d!=="undefined"){e.prop("disabled",!d).toggleClass("disabled",!d)}else{e.prop("disabled",!e.prop("disabled"))}return e};c.enable=function(){var d=this;d.toggleEnable(true);return d};c.disable=function(){var d=this;d.toggleEnable(false);return d};c.toggleSpin=function(g){var f=this;var e=f.dialog;var d=f.find("."+e.getNamespace("button-icon"));if(typeof g==="undefined"){g=!(c.find(".icon-spin").length>0)}if(g){d.hide();c.prepend(e.createButtonIcon(e.getSpinicon()).addClass("icon-spin"))}else{d.show();c.find(".icon-spin").remove()}return f};c.spin=function(){var d=this;d.toggleSpin(true);return d};c.stopSpin=function(){var d=this;d.toggleSpin(false);return d};return this},createButtonIcon:function(d){var c=b("");c.addClass(this.getNamespace("button-icon")).addClass(d);return c},enableButtons:function(c){b.each(this.indexedButtons,function(e,d){d.toggleEnable(c)});return this},updateClosable:function(){if(this.isRealized()){this.getModalHeader().find("."+this.getNamespace("close-button")).toggle(this.isClosable())}return this},onShow:function(c){this.options.onshow=c;return this},onShown:function(c){this.options.onshown=c;return this},onHide:function(c){this.options.onhide=c;return this},onHidden:function(c){this.options.onhidden=c;return this},isRealized:function(){return this.realized},setRealized:function(c){this.realized=c;return this},isOpened:function(){return this.opened},setOpened:function(c){this.opened=c;return this},handleModalEvents:function(){this.getModal().on("show.bs.modal",{dialog:this},function(d){var c=d.data.dialog;if(c.isModalEvent(d)&&typeof c.options.onshow==="function"){return c.options.onshow(c)}});this.getModal().on("shown.bs.modal",{dialog:this},function(d){var c=d.data.dialog;c.isModalEvent(d)&&typeof c.options.onshown==="function"&&c.options.onshown(c)});this.getModal().on("hide.bs.modal",{dialog:this},function(d){var c=d.data.dialog;if(c.isModalEvent(d)&&typeof c.options.onhide==="function"){return c.options.onhide(c)}});this.getModal().on("hidden.bs.modal",{dialog:this},function(d){var c=d.data.dialog;c.isModalEvent(d)&&typeof c.options.onhidden==="function"&&c.options.onhidden(c);c.isAutodestroy()&&b(this).remove();a.moveFocus()});this.getModal().on("click",{dialog:this},function(c){c.target===this&&c.data.dialog.isClosable()&&c.data.dialog.canCloseByBackdrop()&&c.data.dialog.close()});this.getModal().on("keyup",{dialog:this},function(c){c.which===27&&c.data.dialog.isClosable()&&c.data.dialog.canCloseByKeyboard()&&c.data.dialog.close()});this.getModal().on("keyup",{dialog:this},function(d){var c=d.data.dialog;if(typeof c.registeredButtonHotkeys[d.which]!=="undefined"){var e=b(c.registeredButtonHotkeys[d.which]);!e.prop("disabled")&&e.focus().trigger("click")}});return this},isModalEvent:function(c){return typeof c.namespace!=="undefined"&&c.namespace==="bs.modal"},makeModalDraggable:function(){if(this.options.draggable){this.getModalHeader().addClass(this.getNamespace("draggable")).on("mousedown",{dialog:this},function(e){var d=e.data.dialog;d.draggableData.isMouseDown=true;var c=d.getModalDialog().offset();d.draggableData.mouseOffset={top:e.clientY-c.top,left:e.clientX-c.left}});this.getModal().on("mouseup mouseleave",{dialog:this},function(c){c.data.dialog.draggableData.isMouseDown=false});b("body").on("mousemove",{dialog:this},function(d){var c=d.data.dialog;if(!c.draggableData.isMouseDown){return}c.getModalDialog().offset({top:d.clientY-c.draggableData.mouseOffset.top,left:d.clientX-c.draggableData.mouseOffset.left})})}return this},updateZIndex:function(){var e=0;b.each(a.dialogs,function(f,g){e++});var d=this.getModal();var c=d.data("bs.modal").$backdrop;d.css("z-index",a.ZINDEX_MODAL+(e-1)*20);c.css("z-index",a.ZINDEX_BACKDROP+(e-1)*20);return this},realize:function(){this.initModalStuff();this.getModal().addClass(a.NAMESPACE).addClass(this.getCssClass());this.updateSize();if(this.getDescription()){this.getModal().attr("aria-describedby",this.getDescription())}this.getModalFooter().append(this.createFooterContent());this.getModalHeader().append(this.createHeaderContent());this.getModalBody().append(this.createBodyContent());this.getModal().modal({backdrop:"static",keyboard:false,show:false});this.makeModalDraggable();this.handleModalEvents();this.setRealized(true);this.updateButtons();this.updateType();this.updateTitle();this.updateMessage();this.updateClosable();this.updateAnimate();this.updateSize();return this},open:function(){!this.isRealized()&&this.realize();this.getModal().modal("show");this.updateZIndex();this.setOpened(true);return this},close:function(){this.getModal().modal("hide");if(this.isAutodestroy()){delete a.dialogs[this.getId()]}this.setOpened(false);a.showScrollbar();return this}};a.newGuid=function(){return"xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g,function(f){var e=Math.random()*16|0,d=f==="x"?e:(e&3|8);return d.toString(16)})};a.show=function(c){return new a(c).open()};a.alert=function(){var d={};var c={type:a.TYPE_PRIMARY,title:null,message:null,closable:true,buttonLabel:a.DEFAULT_TEXTS.OK,callback:null};if(typeof arguments[0]==="object"&&arguments[0].constructor==={}.constructor){d=b.extend(true,c,arguments[0])}else{d=b.extend(true,c,{message:arguments[0],closable:false,buttonLabel:a.DEFAULT_TEXTS.OK,callback:typeof arguments[1]!=="undefined"?arguments[1]:null})}return new a({type:d.type,title:d.title,message:d.message,closable:d.closable,data:{callback:d.callback},onhide:function(e){!e.getData("btnClicked")&&e.isClosable()&&typeof e.getData("callback")==="function"&&e.getData("callback")(false)},buttons:[{label:d.buttonLabel,action:function(e){e.setData("btnClicked",true);typeof e.getData("callback")==="function"&&e.getData("callback")(true);e.close()}}]}).open()};a.confirm=function(c,d){return new a({title:"Confirmation",message:c,closable:false,data:{callback:d},buttons:[{label:a.DEFAULT_TEXTS.CANCEL,action:function(e){typeof e.getData("callback")==="function"&&e.getData("callback")(false);e.close()}},{label:a.DEFAULT_TEXTS.OK,cssClass:"btn-primary",action:function(e){typeof e.getData("callback")==="function"&&e.getData("callback")(true);e.close()}}]}).open()};a.warning=function(c,d){return new a({type:a.TYPE_WARNING,message:c}).open()};a.danger=function(c,d){return new a({type:a.TYPE_DANGER,message:c}).open()};a.success=function(c,d){return new a({type:a.TYPE_SUCCESS,message:c}).open()};return a})); -------------------------------------------------------------------------------- /static/js/bootstrap-table.min.js: -------------------------------------------------------------------------------- 1 | /* 2 | * bootstrap-table - v1.3.0 - 2014-10-16 3 | * https://github.com/wenzhixin/bootstrap-table 4 | * Copyright (c) 2014 zhixin wen 5 | * Licensed MIT License 6 | */ 7 | !function(a){"use strict";var b=function(a){var b=arguments,c=!0,d=1;return a=a.replace(/%s/g,function(){var a=b[d++];return"undefined"==typeof a?(c=!1,""):a}),c?a:""},c=function(b,c,d,e){var f="";return a.each(b,function(a,b){return b[c]===e?(f=b[d],!1):!0}),f},d=function(b,c){var d=-1;return a.each(b,function(a,b){return b.field===c?(d=a,!1):!0}),d},e=function(){var b,c,d=a("

").addClass("fixed-table-scroll-inner"),e=a("

").addClass("fixed-table-scroll-outer");return e.append(d),a("body").append(e),b=d[0].offsetWidth,e.css("overflow","scroll"),c=d[0].offsetWidth,b==c&&(c=e[0].clientWidth),e.remove(),b-c},f=function(b,c,d,e){if("string"==typeof c){var f=c.split(".");f.length>1?(c=window,a.each(f,function(a,b){c=c[b]})):c=window[c]}return"object"==typeof c?c:"function"==typeof c?c.apply(b,d):e},g=function(b,c){this.options=c,this.$el=a(b),this.$el_=this.$el.clone(),this.timeoutId_=0,this.init()};g.DEFAULTS={classes:"table table-hover",height:void 0,undefinedText:"-",sortName:void 0,sortOrder:"asc",striped:!1,columns:[],data:[],method:"get",url:void 0,cache:!0,contentType:"application/json",queryParams:function(a){return a},queryParamsType:"limit",responseHandler:function(a){return a},pagination:!1,sidePagination:"client",totalRows:0,pageNumber:1,pageSize:10,pageList:[10,25,50,100],search:!1,selectItemName:"btSelectItem",showHeader:!0,showColumns:!1,showRefresh:!1,showToggle:!1,minimumCountColumns:1,idField:void 0,cardView:!1,clickToSelect:!1,singleSelect:!1,toolbar:void 0,checkboxHeader:!0,sortable:!0,maintainSelected:!1,rowStyle:function(){return{}},formatLoadingMessage:function(){return"Loading, please wait…"},formatRecordsPerPage:function(a){return b("%s records per page",a)},formatShowingRows:function(a,c,d){return b("Showing %s to %s of %s rows",a,c,d)},formatSearch:function(){return"Search"},formatNoMatches:function(){return"No matching records found"},onAll:function(){return!1},onClickRow:function(){return!1},onDblClickRow:function(){return!1},onSort:function(){return!1},onCheck:function(){return!1},onUncheck:function(){return!1},onCheckAll:function(){return!1},onUncheckAll:function(){return!1},onLoadSuccess:function(){return!1},onLoadError:function(){return!1},onColumnSwitch:function(){return!1}},g.COLUMN_DEFAULTS={radio:!1,checkbox:!1,checkboxEnabled:!0,field:void 0,title:void 0,"class":void 0,align:void 0,halign:void 0,valign:void 0,width:void 0,sortable:!1,order:"asc",visible:!0,switchable:!0,clickToSelect:!0,formatter:void 0,events:void 0,sorter:void 0,cellStyle:void 0},g.EVENTS={"all.bs.table":"onAll","click-row.bs.table":"onClickRow","dbl-click-row.bs.table":"onDblClickRow","sort.bs.table":"onSort","check.bs.table":"onCheck","uncheck.bs.table":"onUncheck","check-all.bs.table":"onCheckAll","uncheck-all.bs.table":"onUncheckAll","load-success.bs.table":"onLoadSuccess","load-error.bs.table":"onLoadError","column-switch.bs.table":"onColumnSwitch"},g.prototype.init=function(){this.initContainer(),this.initTable(),this.initHeader(),this.initData(),this.initToolbar(),this.initPagination(),this.initBody(),this.initServer()},g.prototype.initContainer=function(){this.$container=a(['
','
','
','
','
','
',this.options.formatLoadingMessage(),"
","
",'
',"
","
"].join("")),this.$container.insertAfter(this.$el),this.$container.find(".fixed-table-body").append(this.$el),this.$container.after('
'),this.$loading=this.$container.find(".fixed-table-loading"),this.$el.addClass(this.options.classes),this.options.striped&&this.$el.addClass("table-striped")},g.prototype.initTable=function(){var b=this,c=[],d=[];this.$header=this.$el.find("thead"),this.$header.length||(this.$header=a("").appendTo(this.$el)),this.$header.find("tr").length||this.$header.append(""),this.$header.find("th").each(function(){var b=a.extend({},{title:a(this).html(),"class":a(this).attr("class")},a(this).data());c.push(b)}),this.options.columns=a.extend([],c,this.options.columns),a.each(this.options.columns,function(c,d){b.options.columns[c]=a.extend({},g.COLUMN_DEFAULTS,{field:c},d)}),this.options.data.length||(this.$el.find("tbody tr").each(function(){var c={};a(this).find("td").each(function(d){c[b.options.columns[d].field]=a(this).html()}),d.push(c)}),this.options.data=d)},g.prototype.initHeader=function(){var c=this,d=[],e=[];this.header={fields:[],styles:[],classes:[],formatters:[],events:[],sorters:[],cellStyles:[],clickToSelects:[]},a.each(this.options.columns,function(a,f){{var g="",h=b("text-align: %s; ",f.align)+b("vertical-align: %s; ",f.valign),i=b(' class="%s"',f["class"]);c.options.sortOrder||f.order}f.visible&&(d.push(f),c.header.fields.push(f.field),c.header.styles.push(h),c.header.classes.push(i),c.header.formatters.push(f.formatter),c.header.events.push(f.events),c.header.sorters.push(f.sorter),c.header.cellStyles.push(f.cellStyle),c.header.clickToSelects.push(f.clickToSelect),f.halign&&(h=b("text-align: %s; ",f.halign)+b("vertical-align: %s; ",f.valign)),h+=b("width: %spx; ",f.checkbox||f.radio?36:f.width),e.push(""),e.push(b('
',c.options.sortable&&f.sortable?"sortable":"")),g=f.title,c.options.sortName===f.field&&c.options.sortable&&f.sortable&&(g+=c.getCaretHtml()),f.checkbox&&(!c.options.singleSelect&&c.options.checkboxHeader&&(g=''),c.header.stateField=f.field),f.radio&&(g="",c.header.stateField=f.field,c.options.singleSelect=!0),e.push(g),e.push("
"),e.push('
'),e.push(""))}),this.$header.find("tr").html(e.join("")),this.$header.find("th").each(function(b){a(this).data(d[b])}),this.$container.off("click","th").on("click","th",function(b){c.options.sortable&&a(this).data().sortable&&c.onSort(b)}),!this.options.showHeader||this.options.cardView?(this.$header.hide(),this.$container.find(".fixed-table-header").hide(),this.$loading.css("top",0)):(this.$header.show(),this.$container.find(".fixed-table-header").show(),this.$loading.css("top","37px")),this.$selectAll=this.$header.find('[name="btSelectAll"]'),this.$container.off("click",'[name="btSelectAll"]').on("click",'[name="btSelectAll"]',function(){var b=a(this).prop("checked");c[b?"checkAll":"uncheckAll"]()})},g.prototype.initData=function(a,b){this.data=b?this.data.concat(a):a||this.options.data,this.options.data=this.data,"server"!==this.options.sidePagination&&this.initSort()},g.prototype.initSort=function(){var b=this,c=this.options.sortName,d="desc"===this.options.sortOrder?-1:1,e=a.inArray(this.options.sortName,this.header.fields);-1!==e&&this.data.sort(function(a,g){var h=f(b.header,b.header.sorters[e],[a[c],g[c]]);return void 0!==h?d*h:a[c]===g[c]?0:a[c]
').appendTo(this.$toolbar).append(a(this.options.toolbar)),f=['
'],this.options.showRefresh&&f.push('"),this.options.showToggle&&f.push('"),this.options.showColumns&&(f.push(b('
',this.options.showRefresh||this.options.showToggle?"btn-group":""),'",'","
")),f.push("
"),f.length>2&&this.$toolbar.append(f.join("")),this.options.showRefresh&&this.$toolbar.find('button[name="refresh"]').off("click").on("click",a.proxy(this.refresh,this)),this.options.showToggle&&this.$toolbar.find('button[name="toggle"]').off("click").on("click",function(){e.options.cardView=!e.options.cardView,e.initHeader(),e.initBody()}),this.options.showColumns&&(c=this.$toolbar.find(".keep-open"),c.find("li").off("click").on("click",function(a){a.stopImmediatePropagation()}),c.find("input").off("click").on("click",function(){var b=a(this);e.toggleColumn(b.val(),b.prop("checked"),!1),e.trigger("column-switch",a(this).data("field"),b.prop("checked"))})),this.options.search&&(f=[],f.push('"),this.$toolbar.append(f.join("")),d=this.$toolbar.find(".search input"),d.off("keyup").on("keyup",function(a){clearTimeout(g),g=setTimeout(function(){e.onSearch(a)},500)}))},g.prototype.onSearch=function(b){var c=a.trim(a(b.currentTarget).val());a(b.currentTarget).val(c),c!==this.searchText&&(this.searchText=c,this.options.pageNumber=1,this.initSearch(),this.updatePagination())},g.prototype.initSearch=function(){var b=this;if("server"!==this.options.sidePagination){var c=this.searchText&&this.searchText.toLowerCase();this.data=c?a.grep(this.options.data,function(d,e){g=a.isNumeric(g)?parseInt(g,10):g;for(var g in d){var h=d[g];if(h=f(b.header,b.header.formatters[a.inArray(g,b.header.fields)],[h,d,e],h),-1!==a.inArray(g,b.header.fields)&&("string"==typeof h||"number"==typeof h)&&-1!==(h+"").toLowerCase().indexOf(c))return!0}return!1}):this.options.data}},g.prototype.initPagination=function(){if(this.$pagination=this.$container.find(".fixed-table-pagination"),this.options.pagination){var c,d,e,f,g,h,i,j,k,l=this,m=[],n=this.searchText?this.data:this.options.data;"server"!==this.options.sidePagination&&(this.options.totalRows=n.length),this.totalPages=0,this.options.totalRows&&(this.totalPages=~~((this.options.totalRows-1)/this.options.pageSize)+1),this.totalPages>0&&this.options.pageNumber>this.totalPages&&(this.options.pageNumber=this.totalPages),this.pageFrom=(this.options.pageNumber-1)*this.options.pageSize+1,this.pageTo=this.options.pageNumber*this.options.pageSize,this.pageTo>this.options.totalRows&&(this.pageTo=this.options.totalRows),m.push('
','',this.options.formatShowingRows(this.pageFrom,this.pageTo,this.options.totalRows),""),m.push('');var o=['','",'"),m.push(this.options.formatRecordsPerPage(o.join(""))),m.push(""),m.push("
",'"),this.$pagination.html(m.join("")),f=this.$pagination.find(".page-list a"),g=this.$pagination.find(".page-first"),h=this.$pagination.find(".page-pre"),i=this.$pagination.find(".page-next"),j=this.$pagination.find(".page-last"),k=this.$pagination.find(".page-number"),this.options.pageNumber<=1&&(g.addClass("disabled"),h.addClass("disabled")),this.options.pageNumber>=this.totalPages&&(i.addClass("disabled"),j.addClass("disabled")),f.off("click").on("click",a.proxy(this.onPageListChange,this)),g.off("click").on("click",a.proxy(this.onPageFirst,this)),h.off("click").on("click",a.proxy(this.onPagePre,this)),i.off("click").on("click",a.proxy(this.onPageNext,this)),j.off("click").on("click",a.proxy(this.onPageLast,this)),k.off("click").on("click",a.proxy(this.onPageNumber,this))}},g.prototype.updatePagination=function(){this.options.maintainSelected||this.resetRows(),this.initPagination(),"server"===this.options.sidePagination?this.initServer():this.initBody()},g.prototype.onPageListChange=function(b){var c=a(b.currentTarget);c.parent().addClass("active").siblings().removeClass("active"),this.options.pageSize=+c.text(),this.$toolbar.find(".page-size").text(this.options.pageSize),this.updatePagination()},g.prototype.onPageFirst=function(){this.options.pageNumber=1,this.updatePagination()},g.prototype.onPagePre=function(){this.options.pageNumber--,this.updatePagination()},g.prototype.onPageNext=function(){this.options.pageNumber++,this.updatePagination()},g.prototype.onPageLast=function(){this.options.pageNumber=this.totalPages,this.updatePagination()},g.prototype.onPageNumber=function(b){this.options.pageNumber!==+a(b.currentTarget).text()&&(this.options.pageNumber=+a(b.currentTarget).text(),this.updatePagination())},g.prototype.initBody=function(d){var e=this,g=[],h=this.getData();this.$body=this.$el.find("tbody"),this.$body.length||(this.$body=a("").appendTo(this.$el)),"server"===this.options.sidePagination&&(h=this.data),this.options.pagination&&"server"!==this.options.sidePagination||(this.pageFrom=1,this.pageTo=h.length);for(var i=this.pageFrom-1;i"),this.options.cardView&&g.push(b('',this.header.fields.length)),a.each(this.header.fields,function(a,d){var h="",m=j[d],n="",o={},p=e.header.classes[a];if(k=b('style="%s"',l.concat(e.header.styles[a]).join("; ")),m=f(e.header,e.header.formatters[a],[m,j,i],m),o=f(e.header,e.header.cellStyles[a],[m,j,i],o),o.classes&&(p=b(' class="%s"',o.classes)),o.css){l=[];for(var q in o.css)l.push(q+": "+o.css[q]);k=b('style="%s"',l.concat(e.header.styles[a]).join("; "))}if(e.options.columns[a].checkbox||e.options.columns[a].radio){if(e.options.cardView)return!0;n=e.options.columns[a].checkbox?"checkbox":n,n=e.options.columns[a].radio?"radio":n,h=['',"",""].join("")}else m="undefined"==typeof m?e.options.undefinedText:m,h=e.options.cardView?['
',e.options.showHeader?b('%s',k,c(e.options.columns,"field","title",d)):"",b('%s',m),"
"].join(""):[b("",p,k),m,""].join("");g.push(h)}),this.options.cardView&&g.push(""),g.push("")}g.length||g.push('',b('%s',this.header.fields.length,this.options.formatNoMatches()),""),this.$body.html(g.join("")),d||this.$container.find(".fixed-table-body").scrollTop(0),this.$body.find("> tr > td").off("click").on("click",function(){var c=a(this).parent();e.trigger("click-row",e.data[c.data("index")],c),e.options.clickToSelect&&e.header.clickToSelects[c.children().index(a(this))]&&c.find(b('[name="%s"]',e.options.selectItemName)).trigger("click")}),this.$body.find("tr").off("dblclick").on("dblclick",function(){e.trigger("dbl-click-row",e.data[a(this).data("index")],a(this))}),this.$selectItem=this.$body.find(b('[name="%s"]',this.options.selectItemName)),this.$selectItem.off("click").on("click",function(b){b.stopImmediatePropagation(),a(this).is(":radio")&&a(this).prop("checked",!0);var c=e.$selectItem.filter(":enabled").length===e.$selectItem.filter(":enabled").filter(":checked").length,d=a(this).prop("checked"),f=e.data[a(this).data("index")];e.$selectAll.add(e.$selectAll_).prop("checked",c),f[e.header.stateField]=d,e.trigger(d?"check":"uncheck",f),e.options.singleSelect&&(e.$selectItem.not(this).each(function(){e.data[a(this).data("index")][e.header.stateField]=!1}),e.$selectItem.filter(":checked").not(this).prop("checked",!1)),e.updateSelected()}),a.each(this.header.events,function(b,c){if(c){"string"==typeof c&&(c=f(null,c));for(var d in c)e.$body.find("tr").each(function(){var f=a(this),g=f.find("td").eq(b),h=d.indexOf(" "),i=d.substring(0,h),j=d.substring(h+1),k=c[d];g.find(j).off(i).on(i,function(a){var c=f.data("index"),d=e.data[c],g=d[e.header.fields[b]];k(a,g,d,c)})})}}),this.updateSelected(),this.resetView()},g.prototype.initServer=function(b){var c=this,d={},e={pageSize:this.options.pageSize,pageNumber:this.options.pageNumber,searchText:this.searchText,sortName:this.options.sortName,sortOrder:this.options.sortOrder};this.options.url&&("limit"===this.options.queryParamsType&&(e={limit:e.pageSize,offset:e.pageSize*(e.pageNumber-1),search:e.searchText,sort:e.sortName,order:e.sortOrder}),d=f(this.options,this.options.queryParams,[e],d),d!==!1&&(b||this.$loading.show(),a.ajax({type:this.options.method,url:this.options.url,data:d,cache:this.options.cache,contentType:this.options.contentType,dataType:"json",success:function(a){a=f(c.options,c.options.responseHandler,[a],a);var b=a;"server"===c.options.sidePagination&&(c.options.totalRows=a.total,b=a.rows),c.load(b),c.trigger("load-success",b)},error:function(a){c.trigger("load-error",a.status)},complete:function(){b||c.$loading.hide()}})))},g.prototype.getCaretHtml=function(){return['','',""].join("")},g.prototype.updateSelected=function(){this.$selectItem.each(function(){a(this).parents("tr")[a(this).prop("checked")?"addClass":"removeClass"]("selected")})},g.prototype.updateRows=function(b){var c=this;this.$selectItem.each(function(){c.data[a(this).data("index")][c.header.stateField]=b})},g.prototype.resetRows=function(){var b=this;a.each(this.data,function(a,c){b.$selectAll.prop("checked",!1),b.$selectItem.prop("checked",!1),c[b.header.stateField]=!1})},g.prototype.trigger=function(b){var c=Array.prototype.slice.call(arguments,1);b+=".bs.table",this.options[g.EVENTS[b]].apply(this.options,c),this.$el.trigger(a.Event(b),c),this.options.onAll(b,c),this.$el.trigger(a.Event("all.bs.table"),[b,c])},g.prototype.resetHeader=function(){var b=this,c=this.$container.find(".fixed-table-header"),d=this.$container.find(".fixed-table-body"),f=this.$el.width()>d.width()?e():0;return this.$el.is(":hidden")?(clearTimeout(this.timeoutId_),void(this.timeoutId_=setTimeout(a.proxy(this.resetHeader,this),100))):(this.$header_=this.$header.clone(!0,!0),this.$selectAll_=this.$header_.find('[name="btSelectAll"]'),void setTimeout(function(){c.css({height:"37px","border-bottom":"1px solid #dddddd","margin-right":f}).find("table").css("width",b.$el.css("width")).html("").attr("class",b.$el.attr("class")).append(b.$header_),b.$header.find("th").each(function(c){b.$header_.find("th").eq(c).data(a(this).data())}),b.$body.find("tr:first-child:not(.no-records-found) > *").each(function(c){b.$header_.find("div.fht-cell").eq(c).width(a(this).innerWidth())}),b.$el.css("margin-top",-b.$header.height()),d.off("scroll").on("scroll",function(){c.scrollLeft(a(this).scrollLeft())})}))},g.prototype.toggleColumn=function(a,c,d){if(-1!==a&&(this.options.columns[a].visible=c,this.initHeader(),this.initSearch(),this.initPagination(),this.initBody(),this.options.showColumns)){var e=this.$toolbar.find(".keep-open input").prop("disabled",!1);d&&e.filter(b('[value="%s"]',a)).prop("checked",c),e.filter(":checked").length<=this.options.minimumCountColumns&&e.filter(":checked").prop("disabled",!0)}},g.prototype.resetView=function(a){{var b=this;this.header}if(a&&a.height&&(this.options.height=a.height),this.$selectAll.prop("checked",this.$selectItem.length>0&&this.$selectItem.length===this.$selectItem.filter(":checked").length),this.options.height){var c=+this.$toolbar.children().outerHeight(!0),d=+this.$pagination.children().outerHeight(!0),e=this.options.height-c-d;this.$container.find(".fixed-table-container").css("height",e+"px")}return this.options.cardView?(b.$el.css("margin-top","0"),void b.$container.find(".fixed-table-container").css("padding-bottom","0")):(this.options.showHeader&&this.options.height&&this.resetHeader(),void(this.options.height&&this.options.showHeader&&this.$container.find(".fixed-table-container").css("padding-bottom","37px")))},g.prototype.getData=function(){return this.searchText?this.data:this.options.data},g.prototype.load=function(a){this.initData(a),this.initSearch(),this.initPagination(),this.initBody()},g.prototype.append=function(a){this.initData(a,!0),this.initSearch(),this.initPagination(),this.initBody(!0)},g.prototype.remove=function(b){var c,d,e=this.options.data.length;if(b.hasOwnProperty("field")&&b.hasOwnProperty("values")){for(c=e-1;c>=0;c--){if(d=this.options.data[c],!d.hasOwnProperty(b.field))return;-1!==a.inArray(d[b.field],b.values)&&this.options.data.splice(c,1)}e!==this.options.data.length&&(this.initSearch(),this.initPagination(),this.initBody(!0))}},g.prototype.updateRow=function(b){b.hasOwnProperty("index")&&b.hasOwnProperty("row")&&(a.extend(this.data[b.index],b.row),this.initBody())},g.prototype.mergeCells=function(b){var c,d,e=b.index,f=a.inArray(b.field,this.header.fields),g=b.rowspan||1,h=b.colspan||1,i=this.$body.find("tr"),j=i.eq(e).find("td").eq(f);if(!(0>e||0>f||e>=this.data.length)){for(c=e;e+g>c;c++)for(d=f;f+h>d;d++)i.eq(c).find("td").eq(d).hide();j.attr("rowspan",g).attr("colspan",h).show(10,a.proxy(this.resetView,this))}},g.prototype.getSelections=function(){var b=this;return a.grep(this.data,function(a){return a[b.header.stateField]})},g.prototype.checkAll=function(){this.$selectAll.add(this.$selectAll_).prop("checked",!0),this.$selectItem.filter(":enabled").prop("checked",!0),this.updateRows(!0),this.updateSelected(),this.trigger("check-all")},g.prototype.uncheckAll=function(){this.$selectAll.add(this.$selectAll_).prop("checked",!1),this.$selectItem.filter(":enabled").prop("checked",!1),this.updateRows(!1),this.updateSelected(),this.trigger("uncheck-all")},g.prototype.destroy=function(){this.$el.insertBefore(this.$container),a(this.options.toolbar).insertBefore(this.$el),this.$container.next().remove(),this.$container.remove(),this.$el.html(this.$el_.html()).attr("class",this.$el_.attr("class")||"")},g.prototype.showLoading=function(){this.$loading.show()},g.prototype.hideLoading=function(){this.$loading.hide()},g.prototype.refresh=function(a){a&&a.url&&(this.options.url=a.url),this.initServer(a&&a.silent)},g.prototype.showColumn=function(a){this.toggleColumn(d(this.options.columns,a),!0,!0)},g.prototype.hideColumn=function(a){this.toggleColumn(d(this.options.columns,a),!1,!0)},a.fn.bootstrapTable=function(b,c){var d,e=["getSelections","getData","load","append","remove","updateRow","mergeCells","checkAll","uncheckAll","refresh","resetView","destroy","showLoading","hideLoading","showColumn","hideColumn"];return this.each(function(){var f=a(this),h=f.data("bootstrap.table"),i=a.extend({},g.DEFAULTS,f.data(),"object"==typeof b&&b);if("string"==typeof b){if(a.inArray(b,e)<0)throw"Unknown method: "+b;if(!h)return;d=h[b](c),"destroy"===b&&f.removeData("bootstrap.table")}h||f.data("bootstrap.table",h=new g(this,i))}),"undefined"==typeof d?this:d},a.fn.bootstrapTable.Constructor=g,a.fn.bootstrapTable.defaults=g.DEFAULTS,a.fn.bootstrapTable.columnDefaults=g.COLUMN_DEFAULTS,a(function(){a('[data-toggle="table"]').bootstrapTable()})}(jQuery); 8 | 9 | -------------------------------------------------------------------------------- /static/js/jquery-cron-min.js: -------------------------------------------------------------------------------- 1 | (function(e){var n={initial:"* * * * *",minuteOpts:{minWidth:100,itemWidth:30,columns:4,rows:undefined,title:"Minutes Past the Hour"},timeHourOpts:{minWidth:100,itemWidth:20,columns:2,rows:undefined,title:"Time: Hour"},domOpts:{minWidth:100,itemWidth:30,columns:undefined,rows:10,title:"Day of Month"},monthOpts:{minWidth:100,itemWidth:100,columns:2,rows:undefined,title:undefined},dowOpts:{minWidth:100,itemWidth:undefined,columns:undefined,rows:undefined,title:undefined},timeMinuteOpts:{minWidth:100,itemWidth:20,columns:4,rows:undefined,title:"Time: Minute"},effectOpts:{openSpeed:400,closeSpeed:400,openEffect:"slide",closeEffect:"slide",hideOnMouseOut:true},url_set:undefined,customValues:undefined,onChange:undefined,useGentleSelect:false};var y="";for(var u=0;u<60;u++){var t=(u<10)?"0":"";y+="\n"}var d="";for(var u=0;u<24;u++){var t=(u<10)?"0":"";d+="\n"}var v="";for(var u=1;u<32;u++){if(u==1||u==21||u==31){var c="st"}else{if(u==2||u==22){var c="nd"}else{if(u==3||u==23){var c="rd"}else{var c="th"}}}v+="\n"}var h="";var l=["January","February","March","April","May","June","July","August","September","October","November","December"];for(var u=0;u"+l[u]+"\n"}var s="";var g=["Sunday","Monday","Tuesday","Wednesday","Thursday","Friday","Saturday"];for(var u=0;u"+g[u]+"\n"}var r="";var b=["minute","hour","day","week","month","year"];for(var u=0;u"+b[u]+"\n"}var p={minute:[],hour:["mins"],day:["time"],week:["dow","time"],month:["dom","time"],year:["dom","month","time"]};var w={minute:/^(\*\s){4}\*$/,hour:/^\d{1,2}\s(\*\s){3}\*$/,day:/^(\d{1,2}\s){2}(\*\s){2}\*$/,week:/^(\d{1,2}\s){2}(\*\s){2}\d{1,2}$/,month:/^(\d{1,2}\s){3}\*\s\*$/,year:/^(\d{1,2}\s){4}\*$/};function a(i){if(typeof i=="undefined"){return false}else{return true}}function q(i){return(!a(i)||typeof i=="object")}function z(A,j){if(a(j.customValues)){for(key in j.customValues){if(A==j.customValues[key]){return key}}}var E=/^((\d{1,2}|\*)\s){4}(\d{1,2}|\*)$/;if(typeof A!="string"||!E.test(A)){e.error("cron: invalid initial value");return undefined}var C=A.split(" ");var D=[0,0,1,1,0];var G=[59,23,31,12,6];for(var B=0;B=D[B]){continue}e.error("cron: invalid value found (col "+(B+1)+") in "+o.initial);return undefined}for(var H in w){if(w[H].test(A)){return H}}e.error("cron: valid but unsupported cron format. sorry.");return undefined}function f(j,i){if(!a(z(i.initial,i))){return true}if(!q(i.customValues)){return true}if(a(i.customValues)){for(key in i.customValues){if(w.hasOwnProperty(key)){e.error("cron: reserved keyword '"+key+"' should not be used as customValues key.");return true}}}return false}function k(B){var i=B.data("block");var j=hour=day=month=dow="*";var A=i.period.find("select").val();switch(A){case"minute":break;case"hour":j=i.mins.find("select").val();break;case"day":j=i.time.find("select.cron-time-min").val();hour=i.time.find("select.cron-time-hour").val();break;case"week":j=i.time.find("select.cron-time-min").val();hour=i.time.find("select.cron-time-hour").val();dow=i.dow.find("select").val();break;case"month":j=i.time.find("select.cron-time-min").val();hour=i.time.find("select.cron-time-hour").val();day=i.dom.find("select").val();break;case"year":j=i.time.find("select.cron-time-min").val();hour=i.time.find("select.cron-time-hour").val();day=i.dom.find("select").val();month=i.month.find("select").val();break;default:return A}return[j,hour,day,month,dow].join(" ")}var x={init:function(i){var G=i?i:{};var B=e.extend([],n,G);var j=e.extend({},n.effectOpts,G.effectOpts);e.extend(B,{minuteOpts:e.extend({},n.minuteOpts,j,G.minuteOpts),domOpts:e.extend({},n.domOpts,j,G.domOpts),monthOpts:e.extend({},n.monthOpts,j,G.monthOpts),dowOpts:e.extend({},n.dowOpts,j,G.dowOpts),timeHourOpts:e.extend({},n.timeHourOpts,j,G.timeHourOpts),timeMinuteOpts:e.extend({},n.timeMinuteOpts,j,G.timeMinuteOpts)});if(f(this,B)){return this}var C=[],A="",D=B.customValues;if(a(D)){for(var F in D){A+="\n"}}C.period=e("Every ").appendTo(this).data("root",this);var E=C.period.find("select");E.bind("change.cron",m.periodChanged).data("root",this);if(B.useGentleSelect){E.gentleSelect(j)}C.dom=e(" on the ").appendTo(this).data("root",this);E=C.dom.find("select").data("root",this);if(B.useGentleSelect){E.gentleSelect(B.domOpts)}C.month=e(" of ").appendTo(this).data("root",this);E=C.month.find("select").data("root",this);if(B.useGentleSelect){E.gentleSelect(B.monthOpts)}C.mins=e(" at minutes past the hour ").appendTo(this).data("root",this);E=C.mins.find("select").data("root",this);if(B.useGentleSelect){E.gentleSelect(B.minuteOpts)}C.dow=e(" on ").appendTo(this).data("root",this);E=C.dow.find("select").data("root",this);if(B.useGentleSelect){E.gentleSelect(B.dowOpts)}C.time=e(" at : 53 |
54 | 55 |
56 |
57 | IP Range 58 | 59 |
60 |
61 |
62 | Port Range 63 | 64 |
65 |
66 |
67 | extra Parameters 68 | 69 |
70 |
71 | 85 |
86 | 87 | 88 | -------------------------------------------------------------------------------- /views/alerts-closed.tpl: -------------------------------------------------------------------------------- 1 | % rebase('base.tpl', title='alerts-closed') 2 |
3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 18 | 19 | 20 | 21 |
IDIPHostnamePortServiceextra infodate of approvalbusiness justification
22 |
23 |
24 |
25 |

About this view

26 |

These Ports are closed, but the "baseline" specifies a business justification for them to be open.

27 |

Normally, this is not a security risk, but may hint at a service failure. These alerts are non-critical by default. 28 | You can mark an alert as critical by clicking the star button . 29 | To get completely rid of an alert, reopen the port and rescan using the magnifying glass - 30 | or remove it from the "baseline" by clicking the red delete button and entering a business justification. 31 |

Learn more

32 |
33 | -------------------------------------------------------------------------------- /views/alerts-exposed.tpl: -------------------------------------------------------------------------------- 1 | % rebase('base.tpl', title='alerts-exposed') 2 |
3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 17 | 18 | 19 | 20 |
IDIPHostnamePortServiceextra infofirst found
21 |
22 |
23 |
24 |

About this view

25 |

These Ports are unintentionally open and therefore to be checked with the highest priority.

26 |

You can temporarily mark an alert as non-critical during your research by clicking the star button . 27 | To get completely rid of an alert, close the port and rescan using the magnifying glass - or add it to the "baseline" by 28 | clicking the green approval button and specifying a business justification (case id, ticket number etc.).

29 |

Learn more

30 |
31 | -------------------------------------------------------------------------------- /views/base.tpl: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | asprom: {{title}} 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 21 | 22 | 23 | 27 | 28 | 29 |
30 |
31 |
32 |
33 |

asprom assault profile monitor

34 | 41 |
42 |
43 |
44 |
45 |

46 |
47 |
48 |
49 |
50 |
51 |
52 | {{!base}} 53 |

last changes

54 |

55 |
56 |
57 |
58 |
59 |
60 | 61 | -------------------------------------------------------------------------------- /views/baseline.tpl: -------------------------------------------------------------------------------- 1 | % rebase('base.tpl', title='baseline') 2 |
3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 18 | 19 | 20 | 21 |
IDIPHostnamePortServiceextra infodate of approvalbusiness justification
22 |
23 |
24 |
25 |

About this view

26 |

The "baseline" speficies the attack profile your network presents as it should be.

27 |

It lists only the services that should be available including the reason why (business justification).
28 | You may rescan single ports by clicking "rescan" or delete them using "delete" .

29 |

Learn more

30 |
31 | -------------------------------------------------------------------------------- /views/editjob.tpl: -------------------------------------------------------------------------------- 1 |

Edit Schedule for {{iprange}}

2 | 3 | 49 | 50 |
51 | Schedule 52 | 53 |
54 |
55 |
56 |
57 | IP Range 58 | 59 |
60 |
61 |
62 | Port Range 63 | 64 |
65 |
66 |
67 | extra Parameters 68 | 69 |
70 |
71 | 85 |
86 | 87 | 88 | -------------------------------------------------------------------------------- /views/posture.tpl: -------------------------------------------------------------------------------- 1 | % rebase('base.tpl', title='posture') 2 |
3 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 17 | 18 | 19 | 20 |
IDIPHostnamePortServiceextra infofirst found
21 |
22 |
23 |
24 |

About this view

25 |

The security posture is the actual profile of your networks found by forensic means.

26 |

You may rescan single ports by clicking the "rescan" button .

27 |

Learn more

28 |
29 | -------------------------------------------------------------------------------- /views/schedule.tpl: -------------------------------------------------------------------------------- 1 | % rebase('base.tpl', title='schedule') 2 | 3 | 4 | 5 |
6 | 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | 22 | 23 | 24 |
IDwhenIP RangeSensorLast RunNext RunLast Statusextra ParametersPort range
25 | 26 |
27 |
28 |
29 |

About this view

30 |

In the schedule, you define which network ranges should be checked, and when they should be checked.

31 |

You can change the settings of each job by clicking the edit button . 32 | You can also run each scan right now by clicking the magnifying glass . 33 | When you delete a job in this interface, asprom will comment it out in the users' crontab.

34 |

Learn more

35 |
36 | --------------------------------------------------------------------------------