├── .gitignore ├── .pylintrc ├── INSTALL.md ├── LICENSE ├── README.md ├── __init__.py ├── bin ├── __init__.py ├── build_cruise_tracks.py.dist ├── build_lowering_tracks.py.dist └── build_remote_directory.py.dist ├── database ├── OpenVDMv2_db.sql └── README.md ├── docs ├── OVDM_Config_Main.png ├── OVDM_DataDashboard_Main.png ├── OpenVDMv2-Flyer_RVTEC2019.pdf ├── OpenVDMv2_UserGuide.odt ├── OpenVDMv2_UserGuide.pdf ├── Shipboard_Dataflow.png └── UI_ScreenShots.pdf ├── requirements.txt ├── server ├── __init__.py ├── etc │ └── openvdm.yaml.dist ├── lib │ ├── __init__.py │ ├── check_filenames.py │ ├── create_directories.py │ ├── geojson_utils.py │ ├── hooks.py │ ├── openvdm.py │ ├── openvdm_plugin.py │ ├── output_json_data_to_file.py │ ├── read_config.py │ └── set_owner_group_permissions.py ├── plugins │ ├── __init__.py │ ├── em302_plugin.py.dist │ ├── parsers │ │ ├── __init__.py │ │ ├── comp_pres_parser.py.dist │ │ ├── ctd_parser.py.dist │ │ ├── dpt_parser.py.dist │ │ ├── flowrate_parser.py.dist │ │ ├── fluoro_parser.py.dist │ │ ├── geotiff_parser.py.dist │ │ ├── gga_parser.py.dist │ │ ├── gnss_parser.py.dist │ │ ├── hdt_parser.py.dist │ │ ├── hpr_parser.py.dist │ │ ├── met_parser.py.dist │ │ ├── metpakpro_parser.py.dist │ │ ├── o2_parser.py.dist │ │ ├── par2_parser.py.dist │ │ ├── par_parser.py.dist │ │ ├── paro_parser.py.dist │ │ ├── pashr_parser.py.dist │ │ ├── ph_parser.py.dist │ │ ├── sprint_parser.py.dist │ │ ├── svp_parser.py.dist │ │ ├── tsg21_parser.py.dist │ │ ├── tsg45_parser.py.dist │ │ ├── tsg_parser.py.dist │ │ ├── twind_parser.py.dist │ │ ├── vtg_parser.py.dist │ │ └── xdr_parser.py.dist │ ├── rov_scs_plugin.py.dist │ └── scs_plugin.py.dist └── workers │ ├── __init__.py │ ├── cruise.py │ ├── cruise_directory.py │ ├── data_dashboard.py │ ├── lowering.py │ ├── lowering_directory.py │ ├── md5_summary.py │ ├── post_hooks.py │ ├── reboot_reset.py │ ├── run_collection_system_transfer.py │ ├── run_cruise_data_transfer.py │ ├── run_ship_to_shore_transfer.py │ ├── scheduler.py │ ├── size_cacher.py │ ├── stop_job.py │ ├── test_collection_system_transfer.py │ └── test_cruise_data_transfer.py ├── utils ├── README.md └── install-openvdm-ubuntu20.04.sh └── www ├── .htaccess ├── .htaccess.dist ├── README.md ├── app ├── Controllers │ ├── Api │ │ ├── CollectionSystemTransfers.php │ │ ├── CruiseDataTransfers.php │ │ ├── DashboardData.php │ │ ├── ExtraDirectories.php │ │ ├── Gearman.php │ │ ├── Messages.php │ │ ├── ShipToShoreTransfers.php │ │ ├── Tasks.php │ │ ├── TransferLogs.php │ │ └── Warehouse.php │ ├── Config │ │ ├── Auth.php │ │ ├── CollectionSystemTransfers.php │ │ ├── CruiseDataTransfers.php │ │ ├── ExtraDirectories.php │ │ ├── Main.php │ │ ├── Messages.php │ │ ├── ShipToShoreTransfers.php │ │ ├── System.php │ │ └── Users.php │ ├── DataDashboard │ │ └── DataDashboard.php │ └── Welcome.php ├── Core │ ├── Config.php.dist │ ├── Controller.php │ ├── Error.php │ ├── Language.php │ ├── Logger.php │ ├── Model.php │ ├── Router.php │ ├── View.php │ └── routes.php ├── Helpers │ ├── Arr.php │ ├── Assets.php │ ├── Csrf.php │ ├── Data.php │ ├── Database.php │ ├── Date.php │ ├── Document.php │ ├── Form.php │ ├── FormCustom.php │ ├── Ftp.php │ ├── GeoCode.php │ ├── Gump.php │ ├── Hooks.php │ ├── Number.php │ ├── Paginator.php │ ├── Password.php │ ├── PhpMailer │ │ ├── Exception.php │ │ ├── Mail.php │ │ ├── PhpMailer.php │ │ ├── Pop3.php │ │ └── Smtp.php │ ├── RainCaptcha.php │ ├── Request.php │ ├── Session.php │ ├── SimpleCurl.php │ ├── TableBuilder.php │ ├── Tags.php │ ├── Url.php │ └── page_eloq.md ├── Models │ ├── Api │ │ └── Gearman.php │ ├── Config │ │ ├── Auth.php │ │ ├── CollectionSystemTransfers.php │ │ ├── CruiseDataTransfers.php │ │ ├── ExtraDirectories.php │ │ ├── Links.php │ │ ├── Messages.php │ │ ├── ShipToShoreTransfers.php │ │ ├── Tasks.php │ │ ├── TransferTypes.php │ │ └── Users.php │ ├── DashboardData.php │ ├── DataDashboard.php │ ├── TransferLogs.php │ └── Warehouse.php ├── Modules │ └── index.html ├── language │ ├── cs │ │ └── Welcome.php │ ├── de │ │ └── Welcome.php │ ├── en │ │ └── Welcome.php │ ├── fr │ │ └── Welcome.php │ ├── it │ │ └── Welcome.php │ ├── nl │ │ └── Welcome.php │ ├── pl │ │ └── Welcome.php │ ├── ro │ │ └── Welcome.php │ └── ru │ │ └── Welcome.php ├── templates │ └── default │ │ ├── css │ │ ├── custom1.css.dist │ │ ├── sb-admin-2.css │ │ ├── style.css │ │ └── timeline.css │ │ ├── dataDashboardHeader.php │ │ ├── footer.php │ │ ├── header.php │ │ ├── js │ │ ├── SSDWFormHelper.js │ │ ├── collectionSystemTransfers.js │ │ ├── collectionSystemTransfersFormHelper.js │ │ ├── cruiseDataTransfers.js │ │ ├── cruiseDataTransfersFormHelper.js │ │ ├── cruiseIDFormHelper.js │ │ ├── custom1.js.dist │ │ ├── dataDashboardDefault.js │ │ ├── dataDashboardMain.js │ │ ├── dataDashboardMainCustom.js.dist │ │ ├── dataDashboardQuality.js │ │ ├── datepicker.js │ │ ├── datetimepicker.js │ │ ├── esriCredit.js │ │ ├── extraDirectoriesFormHelper.js │ │ ├── header.js │ │ ├── highcharts-fa-plugin.js │ │ ├── lowering.js │ │ ├── loweringIDFormHelper.js │ │ ├── main_config.js │ │ ├── main_dataDashboard.js │ │ ├── modals.js │ │ ├── sb-admin-2.js │ │ ├── shipToShoreTransfers.js │ │ ├── shipToShoreTransfersFormHelper.js │ │ ├── system.js │ │ └── welcome.js │ │ └── loginheader.php └── views │ ├── Config │ ├── addCollectionSystemTransfers.php │ ├── addCruiseDataTransfers.php │ ├── addExtraDirectories.php │ ├── addLink.php │ ├── addShipToShoreTransfers.php │ ├── addUser.php │ ├── collectionSystemTransfers.php │ ├── cruiseDataTransfers.php │ ├── editCollectionSystemTransfers.php │ ├── editCruise.php │ ├── editCruiseDataTransfers.php │ ├── editExtraDirectories.php │ ├── editLink.php │ ├── editLowering.php │ ├── editMD5FilesizeLimit.php │ ├── editRequiredExtraDirectories.php │ ├── editRequiredShipToShoreTransfers.php │ ├── editShipToShoreBWLimit.php │ ├── editShipToShoreTransfers.php │ ├── editShipboardDataWarehouse.php │ ├── editShoresideDataWarehouse.php │ ├── editUser.php │ ├── extraDirectories.php │ ├── login.php │ ├── main.php │ ├── mainOrig.php │ ├── messages.php │ ├── newCruise.php │ ├── newLowering.php │ ├── shipToShoreTransfers.php │ ├── system.php │ └── users.php │ ├── DataDashboard │ ├── custom1.php.dist │ ├── dataDashboard.php │ ├── dataQuality.php │ ├── default.php │ ├── lowering.php │ ├── main.php │ └── noData.php │ ├── Welcome │ └── index.php │ └── error │ └── 404.php ├── bower.json ├── composer.json ├── etc └── datadashboard.yaml.dist ├── index.php └── license.txt /.gitignore: -------------------------------------------------------------------------------- 1 | __pycache__ 2 | **/.DS_Store 3 | database/OpenVDMv2_db_custom.sql 4 | bin/*.py 5 | server/plugins/*.py 6 | server/plugins/parsers/*.py 7 | server/etc/openvdm.yaml 8 | www/app/Core/Config.php 9 | www/app/templates/default/js/dataDashboardMainCustom.js 10 | www/bower_components 11 | www/etc/datadashboard.yaml 12 | www/vendor 13 | www/.htaccess 14 | www/composer.lock 15 | www/errorlog.html 16 | venv 17 | -------------------------------------------------------------------------------- /INSTALL.md: -------------------------------------------------------------------------------- 1 | # Open Vessel Data Management v2.5 2 | 3 | ## Installation Guide 4 | At the time of this writing OpenVDMv2 was built and tested against the Ubuntu 18.04 LTS operating system. It may be possible to build against other linux-based operating systems however for the purposes of this guide the instructions will assume Ubuntu 18.04 LTS is used. 5 | 6 | ### Operating System 7 | Goto 8 | 9 | ### If you are installing OpenVDM remotely 10 | 11 | If this is going to be a remote install then SSH Server must be installed. 12 | ``` 13 | apt install -y ssh 14 | ``` 15 | 16 | ### Install OpenVDM and it's dependencies 17 | Log into the Server as root 18 | 19 | Download the install script 20 | ``` 21 | cd ~ 22 | curl https://raw.githubusercontent.com/webbpinner/OpenVDMv2/v2.x/install-openvdm-ubuntu20.04.sh > ~/install-openvdm-ubuntu20.04.sh 23 | ``` 24 | 25 | Run the install script 26 | ``` 27 | chmod +x ~/install-openvdm-ubuntu20.04.sh 28 | ~/install-openvdm-ubuntu20.04.sh 29 | ``` 30 | 31 | You will need to answer some questions about your configuration. 32 | 33 | - Name to assign to host? --> *This is the host name of the server* 34 | 35 | - Repository to install from? --> *This is the which OpenVDM repo you want to install from* 36 | 37 | - Repository branch to install? --> *This is the branch of the specified repo to download* 38 | 39 | - OpenVDM user to create? --> *This is the system user that will own the cruise data files. This is also the username used to connect the OpenVDM web-app to the backend database* 40 | 41 | - OpenVDMv2 Database password to use for user ? --> *This is the DATABASE user password for the database user.* 42 | 43 | - Current database password for root (hit return if this is the initial installation)? --> *This is the root password for the database* 44 | 45 | - Root data directory for OpenVDM? --> *This is the root directory that will contain all the cruise data for all cruises managed by OpenVDM. If this directory does not already exist you will be asked if you want it created.* 46 | 47 | ### All done... almost ### 48 | At this point the warehouse should have a working installation of OpenVDMv2 however the vessel operator will still need to configure data dashboard collection system transfers, cruise data transfers and the shoreside data warehouse. 49 | 50 | To access the OpenVDM web-application goto: /OpenVDMv2/> 51 | The default username/passwd is admin/demo 52 | 53 | #### Reset the default password 54 | #Goto /OpenVDMv2> and login (user icon, upper-right) 55 | #Click the user icon again and select "User Settings" 56 | #Set the desired password and optional change the admin username. 57 | 58 | ### An error has been reported ### 59 | If at anypoint you see this message in the OpenVDM web-interface you can see what the error was by going to: /OpenVDMv2/errorlog.html>. That should hopefully provide you with enough information as to what's gone wrong. 60 | 61 | 62 | 63 | 64 | -------------------------------------------------------------------------------- /__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webbpinner/OpenVDMv2/20440013fe17cd748fd28186fb1155f8441964e4/__init__.py -------------------------------------------------------------------------------- /bin/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webbpinner/OpenVDMv2/20440013fe17cd748fd28186fb1155f8441964e4/bin/__init__.py -------------------------------------------------------------------------------- /database/README.md: -------------------------------------------------------------------------------- 1 | Directory contains the SQL templates used to initialize the OpenVDM database -------------------------------------------------------------------------------- /docs/OVDM_Config_Main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webbpinner/OpenVDMv2/20440013fe17cd748fd28186fb1155f8441964e4/docs/OVDM_Config_Main.png -------------------------------------------------------------------------------- /docs/OVDM_DataDashboard_Main.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webbpinner/OpenVDMv2/20440013fe17cd748fd28186fb1155f8441964e4/docs/OVDM_DataDashboard_Main.png -------------------------------------------------------------------------------- /docs/OpenVDMv2-Flyer_RVTEC2019.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webbpinner/OpenVDMv2/20440013fe17cd748fd28186fb1155f8441964e4/docs/OpenVDMv2-Flyer_RVTEC2019.pdf -------------------------------------------------------------------------------- /docs/OpenVDMv2_UserGuide.odt: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webbpinner/OpenVDMv2/20440013fe17cd748fd28186fb1155f8441964e4/docs/OpenVDMv2_UserGuide.odt -------------------------------------------------------------------------------- /docs/OpenVDMv2_UserGuide.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webbpinner/OpenVDMv2/20440013fe17cd748fd28186fb1155f8441964e4/docs/OpenVDMv2_UserGuide.pdf -------------------------------------------------------------------------------- /docs/Shipboard_Dataflow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webbpinner/OpenVDMv2/20440013fe17cd748fd28186fb1155f8441964e4/docs/Shipboard_Dataflow.png -------------------------------------------------------------------------------- /docs/UI_ScreenShots.pdf: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webbpinner/OpenVDMv2/20440013fe17cd748fd28186fb1155f8441964e4/docs/UI_ScreenShots.pdf -------------------------------------------------------------------------------- /requirements.txt: -------------------------------------------------------------------------------- 1 | certifi==2020.12.5 2 | chardet==4.0.0 3 | geographiclib==1.50 4 | geopy==2.1.0 5 | idna==2.10 6 | numpy==1.19.5 7 | pandas==1.2.1 8 | pkg-resources==0.0.0 9 | python-dateutil==2.8.1 10 | python3-gearman==0.1.0 11 | pytz==2020.5 12 | PyYAML==5.4.1 13 | requests==2.25.1 14 | six==1.15.0 15 | urllib3==1.26.4 16 | -------------------------------------------------------------------------------- /server/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webbpinner/OpenVDMv2/20440013fe17cd748fd28186fb1155f8441964e4/server/__init__.py -------------------------------------------------------------------------------- /server/lib/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webbpinner/OpenVDMv2/20440013fe17cd748fd28186fb1155f8441964e4/server/lib/__init__.py -------------------------------------------------------------------------------- /server/lib/check_filenames.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """Utilities for determining invalid filenames. 3 | """ 4 | import logging 5 | 6 | def is_ascii(test_str): 7 | """Check if the characters in string s are in ASCII, U+0-U+7F.""" 8 | return len(test_str) == len(test_str.encode()) 9 | 10 | def bad_filename(filename): 11 | """Verify the filename contains only valid ASCii characters""" 12 | try: 13 | str(filename) 14 | except Exception as err: 15 | logging.debug(str(err)) 16 | return True 17 | return False 18 | 19 | def bad_filenames(files): 20 | """ 21 | Return a list of files that contain non-ASCii chanacters from the 22 | list of provided filenames 23 | """ 24 | 25 | problem_files = list(filter(bad_filename, files)) 26 | 27 | if len(problem_files) > 0: 28 | logging.debug("Problem Files:") 29 | logging.debug("\t %s", "\n\t".join(problem_files)) 30 | 31 | return problem_files 32 | -------------------------------------------------------------------------------- /server/lib/create_directories.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """Utilities for determining invalid filenames. 3 | """ 4 | import os 5 | import json 6 | import errno 7 | import logging 8 | 9 | def create_directories(directory_list): 10 | """ 11 | Create the directories defined in the directory_list 12 | """ 13 | reasons = [] 14 | for directory in directory_list: 15 | try: 16 | os.makedirs(directory) 17 | except OSError as exception: 18 | if exception.errno != errno.EEXIST: 19 | logging.error("Unable to create directory: %s", directory) 20 | reasons.append("Unable to create directory: %s", directory) 21 | 22 | if len(reasons) > 0: 23 | return {'verdict': False, 'reason': '\n'.join(reasons)} 24 | 25 | return {'verdict': True} 26 | 27 | def create_parent_directories(file_list): 28 | """ 29 | Create the parent directories for the directories defined in the 30 | directory_list 31 | """ 32 | dirs = list({os.path.dirname(filename) for filename in file_list}) 33 | 34 | logging.debug("Directories to create: %s", json.dumps(dirs)) 35 | return create_directories(dirs) 36 | -------------------------------------------------------------------------------- /server/lib/geojson_utils.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """Utilities for working with geojson and kml files. 3 | """ 4 | 5 | import json 6 | import logging 7 | from xml.etree.ElementTree import Element, SubElement, tostring 8 | 9 | def combine_geojson_files(input_files, prefix, device_name): 10 | """ 11 | Function to combine all the geoJSON-formatted files listed in the 'files' array 12 | command-line argument. The function is also passed the cruiseID and device name so 13 | that this information can be added as a property to the final geoJSON file. 14 | 15 | If the raw datafile cannot be processed the function returns false. If there were no 16 | files to process the fuction returns Null. Otherwise the fuction returns the 17 | combined geoJSON object 18 | """ 19 | # Blank geoJson object 20 | returned_geojson_obj = { 21 | "type":"FeatureCollection", 22 | "features":[ 23 | { 24 | "type":"Feature", 25 | "geometry":{ 26 | "type":"LineString", 27 | "coordinates":[] 28 | }, 29 | "properties": { 30 | "name": prefix + '_' + device_name, 31 | "coordTimes":[] 32 | } 33 | } 34 | ] 35 | } 36 | 37 | if len(input_files) == 0: 38 | return None 39 | 40 | for file in input_files: 41 | #print file 42 | 43 | # Open the dashboardData file 44 | try: 45 | with open(file, 'r') as geojson_file: 46 | geojson_obj = json.load(geojson_file) 47 | 48 | returned_geojson_obj['features'][0]['geometry']['coordinates'] += geojson_obj['visualizerData'][0]['features'][0]['geometry']['coordinates'] 49 | returned_geojson_obj['features'][0]['properties']['coordTimes'] += geojson_obj['visualizerData'][0]['features'][0]['properties']['coordTimes'] 50 | 51 | # If the file cannot be processed return false. 52 | except Exception as err: 53 | logging.error("ERROR: Could not proccess file: %s", file) 54 | logging.debug(str(err)) 55 | return None 56 | 57 | # If processing is successful, return the (geo)json object 58 | return returned_geojson_obj 59 | 60 | def convert_to_kml(geojson_obj): 61 | """ 62 | Function to convert a geoJSON object to a KML (v2.2) string. 63 | Function returns a KML-formatted string 64 | """ 65 | 66 | kml = Element('kml') 67 | kml.set('xmlns', 'http://www.opengis.net/kml/2.2') 68 | kml.set('xmlns:gx','http://www.google.com/kml/ext/2.2') 69 | kml.set('xmlns:kml','http://www.opengis.net/kml/2.2') 70 | kml.set('xmlns:atom','http://www.w3.org/2005/Atom') 71 | document = SubElement(kml, 'Document') 72 | name = SubElement(document, 'name') 73 | name.text = geojson_obj['features'][0]['properties']['name'] + "_Trackline.kml" 74 | placemark = SubElement(document, 'Placemark') 75 | name2 = SubElement(placemark, 'name') 76 | name2.text = "path1" 77 | linestring = SubElement(placemark, 'LineString') 78 | tessellate = SubElement(linestring, 'tessellate') 79 | tessellate.text = "1" 80 | coordinates = SubElement(linestring, 'coordinates') 81 | 82 | coordinates_text = '' 83 | 84 | for coordinate in geojson_obj['features'][0]['geometry']['coordinates']: 85 | coordinates_text += str(coordinate[0]) + ',' + str(coordinate[1]) + ',0 ' 86 | 87 | coordinates_text = coordinates_text.rstrip(' ') 88 | coordinates.text = coordinates_text 89 | 90 | return '' + tostring(kml).decode('utf8') 91 | -------------------------------------------------------------------------------- /server/lib/output_json_data_to_file.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """Utilities for writing JSON-formatted data to file. 3 | """ 4 | import os 5 | import json 6 | import errno 7 | import logging 8 | 9 | def output_json_data_to_file(file_path, contents): 10 | """ 11 | Write contents to the specified file_path. Assumes contents is a json 12 | string-able object 13 | """ 14 | try: 15 | os.makedirs(os.path.dirname(file_path)) 16 | except OSError as exception: 17 | if exception.errno != errno.EEXIST: 18 | logging.error("Unable to create parent directory for data file") 19 | return {'verdict': False, 'reason': 'Unable to create parent directory(ies) for data file: {}'.format(file_path) } 20 | except Exception as err: 21 | raise err 22 | 23 | with open(file_path, 'w') as json_file: 24 | logging.debug("Saving JSON file: %s", file_path) 25 | try: 26 | json.dump(contents, json_file, indent=4) 27 | 28 | except IOError: 29 | logging.error("Error Saving JSON file: %s", file_path) 30 | return {'verdict': False, 'reason': 'Unable to create data file: {}'.format(file_path) } 31 | 32 | return {'verdict': True} 33 | -------------------------------------------------------------------------------- /server/lib/read_config.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """Utilities for reading/processing JSON data. 3 | """ 4 | import logging 5 | 6 | try: 7 | from yaml import load, YAMLError, FullLoader 8 | except ModuleNotFoundError: 9 | pass 10 | 11 | 12 | def parse_yaml(source): 13 | """Read the passed text/stream assuming it's YAML or JSON (a subset of 14 | YAML) and try to parse it into a Python dict. 15 | """ 16 | try: 17 | return load(source, Loader=FullLoader) 18 | except NameError as name_error: 19 | raise ImportError('No YAML module available. Please ensure that ' 20 | 'PyYAML or equivalent is installed (e.g. via ' 21 | '"pip3 install PyYAML"') from name_error 22 | except YAMLError as err: 23 | logging.error("Unable to parse configuration file: %s", source) 24 | raise err 25 | except Exception as err: # handle other exceptions such as attribute errors 26 | raise err 27 | 28 | 29 | def read_config(filename): 30 | """Read the passed text/stream assuming it's a valid OpenVDM configuration 31 | file 32 | """ 33 | try: 34 | with open(filename, 'r') as file: 35 | return parse_yaml(file) 36 | except IOError as err: 37 | logging.error("Unable to open configuration file: %s", filename) 38 | raise err 39 | except Exception as err: # handle other exceptions such as attribute errors 40 | raise err 41 | -------------------------------------------------------------------------------- /server/lib/set_owner_group_permissions.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """Utilities for setting file permissions/ownership. 3 | """ 4 | from os import chown, chmod, walk 5 | from os.path import isfile, join, basename, dirname 6 | from grp import getgrnam 7 | from pwd import getpwnam 8 | import logging 9 | 10 | def remove_prefix(text, prefix): 11 | """ 12 | Remove the specified prefix from the provided text if it exists 13 | """ 14 | return text[text.startswith(prefix) and len(prefix):] 15 | 16 | def set_owner_group_permissions(user, path): 17 | """ 18 | Recursively set the ownership and permissions for the files and sub- 19 | directories for the given path. 20 | """ 21 | reasons = [] 22 | 23 | basename_path = basename(path) 24 | root_dirname = dirname(path) 25 | uid = getpwnam(user).pw_uid 26 | gid = getgrnam(user).gr_gid 27 | # Set the file permission and ownership for the current directory 28 | 29 | logging.debug("Setting ownership/permissions for %s", basename_path) 30 | if isfile(path): 31 | try: 32 | chown(path, uid, gid) 33 | chmod(path, 0o644) 34 | except OSError: 35 | logging.error("Unable to set ownership/permissions for /%s", basename_path) 36 | reasons.append("Unable to set ownership/permissions for /{}".format(basename_path)) 37 | 38 | else: #directory 39 | try: 40 | chown(path, uid, gid) 41 | chmod(path, 0o755) 42 | except OSError: 43 | logging.error("Unable to set ownership/permissions for /%s", basename_path) 44 | reasons.append("Unable to set ownership/permissions for /{}".format(basename_path)) 45 | 46 | for root, dirs, files in walk(path): 47 | for file in files: 48 | fname = join(root, file) 49 | logging.debug("Setting ownership/permissions for %s", remove_prefix(join(root,file), root_dirname)) 50 | try: 51 | chown(fname, uid, gid) 52 | chmod(fname, 0o644) 53 | except OSError: 54 | logging.error("Unable to set ownership/permissions for %s", remove_prefix(join(root,file), root_dirname)) 55 | reasons.append("Unable to set ownership/permissions for {}".format(remove_prefix(join(root,file), root_dirname))) 56 | 57 | for directory in dirs: 58 | dname = join(root, directory) 59 | logging.debug("Setting ownership/permissions for %s", remove_prefix(join(root,directory),root_dirname)) 60 | try: 61 | chown(dname, uid, gid) 62 | chmod(dname, 0o755) 63 | except OSError: 64 | logging.error("Unable to set ownership/permissions for %s", remove_prefix(join(root,directory), root_dirname)) 65 | reasons.append("Unable to set ownership/permissions for {}".format(remove_prefix(join(root,directory), root_dirname))) 66 | 67 | if len(reasons) > 0: 68 | return {'verdict': False, 'reason': '\n'.join(reasons)} 69 | 70 | return {'verdict': True} 71 | -------------------------------------------------------------------------------- /server/plugins/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webbpinner/OpenVDMv2/20440013fe17cd748fd28186fb1155f8441964e4/server/plugins/__init__.py -------------------------------------------------------------------------------- /server/plugins/parsers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webbpinner/OpenVDMv2/20440013fe17cd748fd28186fb1155f8441964e4/server/plugins/parsers/__init__.py -------------------------------------------------------------------------------- /server/workers/__init__.py: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/webbpinner/OpenVDMv2/20440013fe17cd748fd28186fb1155f8441964e4/server/workers/__init__.py -------------------------------------------------------------------------------- /server/workers/reboot_reset.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | 4 | FILE: reboot_reset.py 5 | 6 | DESCRIPTION: This program resets OVDM state information in the database. 7 | 8 | BUGS: 9 | NOTES: 10 | AUTHOR: Webb Pinner 11 | COMPANY: Capable Solutions 12 | VERSION: 2.5 13 | CREATED: 2015-06-22 14 | REVISION: 2020-12-30 15 | 16 | LICENSE INFO: Open Vessel Data Management v2.5 (OpenVDMv2) 17 | Copyright (C) OceanDataRat.org 2021 18 | 19 | This program is free software: you can redistribute it and/or modify 20 | it under the terms of the GNU General Public License as published by 21 | the Free Software Foundation, either version 3 of the License, or 22 | (at your option) any later version. 23 | 24 | This program is distributed in the hope that it will be useful, 25 | but WITHOUT ANY WARRANTY; without even the implied warranty of 26 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 27 | GNU General Public License for more details. 28 | 29 | You should have received a copy of the GNU General Public License 30 | along with this program. If not, see . 31 | 32 | """ 33 | 34 | import sys 35 | import time 36 | import argparse 37 | import logging 38 | 39 | from os.path import dirname, realpath 40 | sys.path.append(dirname(dirname(dirname(realpath(__file__))))) 41 | 42 | from server.lib.openvdm import OpenVDM 43 | 44 | # ------------------------------------------------------------------------------------- 45 | # Required python code for running the script as a stand-alone utility 46 | # ------------------------------------------------------------------------------------- 47 | if __name__ == "__main__": 48 | parser = argparse.ArgumentParser(description='Handle resetting OpenVDM database after an unscheduled system reboot') 49 | parser.add_argument('-v', '--verbosity', dest='verbosity', 50 | default=0, action='count', 51 | help='Increase output verbosity') 52 | 53 | parsed_args = parser.parse_args() 54 | 55 | ############################ 56 | # Set up logging before we do any other argument parsing (so that we 57 | # can log problems with argument parsing). 58 | 59 | LOGGING_FORMAT = '%(asctime)-15s %(levelname)s - %(message)s' 60 | logging.basicConfig(format=LOGGING_FORMAT) 61 | 62 | LOG_LEVELS = {0: logging.WARNING, 1: logging.INFO, 2: logging.DEBUG} 63 | parsed_args.verbosity = min(parsed_args.verbosity, max(LOG_LEVELS)) 64 | logging.getLogger().setLevel(LOG_LEVELS[parsed_args.verbosity]) 65 | 66 | openVDM = OpenVDM() 67 | 68 | time.sleep(5) 69 | 70 | logging.info("Setting all tasks to idle.") 71 | tasks = openVDM.get_tasks() 72 | for task in tasks: 73 | openVDM.set_idle_task(task['taskID']) 74 | 75 | logging.info("Setting all Collection System Transfers to idle.") 76 | collection_system_transfers = openVDM.get_collection_system_transfers() 77 | for collection_system_transfer in collection_system_transfers: 78 | if not collection_system_transfer['status'] == '3': 79 | openVDM.set_idle_collection_system_transfer(collection_system_transfer['collectionSystemTransferID']) 80 | 81 | logging.info("Setting all Cruise Data Transfers to idle.") 82 | cruise_data_transfers = openVDM.get_cruise_data_transfers() 83 | for cruise_data_transfer in cruise_data_transfers: 84 | if not cruise_data_transfer['status'] == '3': 85 | openVDM.set_idle_cruise_data_transfer(cruise_data_transfer['cruiseDataTransferID']) 86 | 87 | required_cruise_data_transfers = openVDM.get_required_cruise_data_transfers() 88 | for required_cruise_data_transfer in required_cruise_data_transfers: 89 | if not required_cruise_data_transfer['status'] == '3': 90 | openVDM.set_idle_cruise_data_transfer(required_cruise_data_transfer['cruiseDataTransferID']) 91 | 92 | logging.info("Clearing all jobs from Gearman.") 93 | openVDM.clear_gearman_jobs_from_db() 94 | 95 | logging.info("Done!") 96 | -------------------------------------------------------------------------------- /server/workers/size_cacher.py: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env python3 2 | """ 3 | 4 | FILE: size_cacher.py 5 | 6 | DESCRIPTION: This program handles calculating the cruise and lowering 7 | directory sizes. 8 | 9 | USAGE: size_cacher.py [--interval ] 10 | 11 | ARGUMENTS: --interval The minimum interval in second between directory 12 | size calculations. 13 | 14 | BUGS: 15 | NOTES: 16 | AUTHOR: Webb Pinner 17 | COMPANY: Capable Solutions 18 | VERSION: 2.5 19 | CREATED: 2017-09-30 20 | REVISION: 2021-01-02 21 | 22 | LICENSE INFO: Open Vessel Data Management v2.5 (OpenVDMv2) 23 | Copyright (C) OceanDataRat.org 2021 24 | 25 | This program is free software: you can redistribute it and/or modify 26 | it under the terms of the GNU General Public License as published by 27 | the Free Software Foundation, either version 3 of the License, or 28 | (at your option) any later version. 29 | 30 | This program is distributed in the hope that it will be useful, 31 | but WITHOUT ANY WARRANTY; without even the implied warranty of 32 | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 33 | GNU General Public License for more details. 34 | 35 | You should have received a copy of the GNU General Public License 36 | along with this program. If not, see . 37 | """ 38 | 39 | import sys 40 | import time 41 | import datetime 42 | import argparse 43 | import subprocess 44 | import logging 45 | 46 | from os.path import dirname, realpath, join, isdir 47 | sys.path.append(dirname(dirname(dirname(realpath(__file__))))) 48 | 49 | from server.lib.openvdm import OpenVDM 50 | 51 | if __name__ == "__main__": 52 | 53 | parser = argparse.ArgumentParser(description='OpenVDM Directory Size Cacher') 54 | parser.add_argument('--interval', default=10, metavar='interval', type=int, help='Maximum update rate in seconds') 55 | parser.add_argument('-v', '--verbosity', dest='verbosity', 56 | default=0, action='count', 57 | help='Increase output verbosity') 58 | 59 | parsed_args = parser.parse_args() 60 | 61 | ############################ 62 | # Set up logging before we do any other argument parsing (so that we 63 | # can log problems with argument parsing). 64 | 65 | LOGGING_FORMAT = '%(asctime)-15s %(levelname)s - %(message)s' 66 | logging.basicConfig(format=LOGGING_FORMAT) 67 | 68 | LOG_LEVELS = {0: logging.WARNING, 1: logging.INFO, 2: logging.DEBUG} 69 | parsed_args.verbosity = min(parsed_args.verbosity, max(LOG_LEVELS)) 70 | logging.getLogger().setLevel(LOG_LEVELS[parsed_args.verbosity]) 71 | 72 | openVDM = OpenVDM() 73 | 74 | while True: 75 | 76 | start_t = datetime.datetime.utcnow() 77 | 78 | warehouse_config = openVDM.get_shipboard_data_warehouse_config() 79 | cruise_dir = join(warehouse_config['shipboardDataWarehouseBaseDir'], openVDM.get_cruise_id()) 80 | 81 | lowering_id = openVDM.get_lowering_id() if openVDM.get_show_lowering_components() else None 82 | lowering_dir = join(cruise_dir, warehouse_config['loweringDataBaseDir'], lowering_id) if lowering_id else None 83 | 84 | logging.debug("Cruise Directory: %s", cruise_dir) 85 | logging.debug("Lowering Directory: %s", lowering_dir) 86 | 87 | if isdir(cruise_dir): 88 | logging.debug("Calculating Cruise Size...") 89 | cruise_size_proc = subprocess.run(['du','-sb', cruise_dir], capture_output=True, text=True, check=False) 90 | if cruise_size_proc.returncode == 0: 91 | logging.info("Cruise Size: %s", cruise_size_proc.stdout.split()[0]) 92 | openVDM.set_cruise_size(cruise_size_proc.stdout.split()[0]) 93 | 94 | if lowering_dir and isdir(lowering_dir): 95 | logging.debug("Calculating Lowering Size...") 96 | loweringSizeProc = subprocess.run(['du','-sb', lowering_dir], capture_output=True, text=True, check=False) 97 | if loweringSizeProc.returncode == 0: 98 | logging.info("Lowering Size: %s", loweringSizeProc.stdout.split()[0]) 99 | openVDM.set_lowering_size(loweringSizeProc.stdout.split()[0]) 100 | 101 | end_t = datetime.datetime.utcnow() 102 | 103 | elapse_t = end_t - start_t 104 | logging.debug("Total Seconds: %s", elapse_t.total_seconds()) 105 | 106 | if (elapse_t.total_seconds()) >= parsed_args.interval: 107 | continue 108 | 109 | logging.info("Calculating size again in %s seconds", parsed_args.interval - elapse_t.total_seconds()) 110 | time.sleep(parsed_args.interval - elapse_t.total_seconds()) 111 | -------------------------------------------------------------------------------- /utils/README.md: -------------------------------------------------------------------------------- 1 | Directory contains scripts used to install/update OpenVDM -------------------------------------------------------------------------------- /www/.htaccess: -------------------------------------------------------------------------------- 1 | Options -Indexes 2 | 3 | 4 | 5 | RewriteEngine On 6 | RewriteBase /OpenVDMv2/ 7 | 8 | # Force to exclude the trailing slash 9 | RewriteCond %{REQUEST_FILENAME} !-d 10 | RewriteCond %{REQUEST_URI} (.*)/$ 11 | RewriteRule ^(.+)/$ $1 [R=307,L] 12 | 13 | # Restrict php files direct access 14 | RewriteCond %{THE_REQUEST} ^.+?\ [^?]+\.php[?\ ] 15 | RewriteRule \.php$ - [F] 16 | 17 | # Allow any files or directories that exist to be displayed directly 18 | RewriteCond %{REQUEST_FILENAME} !-f 19 | RewriteCond %{REQUEST_FILENAME} !-d 20 | 21 | RewriteRule ^(.*)$ index.php?$1 [QSA,L] 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /www/.htaccess.dist: -------------------------------------------------------------------------------- 1 | Options -Indexes 2 | 3 | 4 | 5 | RewriteEngine On 6 | RewriteBase /OpenVDMv2/ 7 | 8 | # Force to exclude the trailing slash 9 | RewriteCond %{REQUEST_FILENAME} !-d 10 | RewriteCond %{REQUEST_URI} (.*)/$ 11 | RewriteRule ^(.+)/$ $1 [R=307,L] 12 | 13 | # Restrict php files direct access 14 | RewriteCond %{THE_REQUEST} ^.+?\ [^?]+\.php[?\ ] 15 | RewriteRule \.php$ - [F] 16 | 17 | # Allow any files or directories that exist to be displayed directly 18 | RewriteCond %{REQUEST_FILENAME} !-f 19 | RewriteCond %{REQUEST_FILENAME} !-d 20 | 21 | RewriteRule ^(.*)$ index.php?$1 [QSA,L] 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /www/README.md: -------------------------------------------------------------------------------- 1 | Directory contains the OpenVDM web-interface code -------------------------------------------------------------------------------- /www/app/Controllers/Api/CollectionSystemTransfers.php: -------------------------------------------------------------------------------- 1 | 38 | * 39 | * @edit 2012-07-08
40 | * John Doe
41 | * Changed some essential 42 | * functionality for the better
43 | * #edit3392 44 | */ 45 | public function __construct(){ 46 | $this->_collectionSystemTransfersModel = new \Models\Config\CollectionSystemTransfers(); 47 | } 48 | 49 | public function getCollectionSystemTransfers(){ 50 | echo json_encode($this->_collectionSystemTransfersModel->getCollectionSystemTransfers()); 51 | } 52 | 53 | public function getActiveCollectionSystemTransfers(){ 54 | echo json_encode($this->_collectionSystemTransfersModel->getActiveCollectionSystemTransfers()); 55 | } 56 | 57 | public function getCruiseOnlyCollectionSystemTransfers(){ 58 | echo json_encode($this->_collectionSystemTransfersModel->getCruiseOnlyCollectionSystemTransfers()); 59 | } 60 | 61 | public function getLoweringOnlyCollectionSystemTransfers(){ 62 | echo json_encode($this->_collectionSystemTransfersModel->getLoweringOnlyCollectionSystemTransfers()); 63 | } 64 | 65 | public function getCollectionSystemTransfer($id){ 66 | echo json_encode($this->_collectionSystemTransfersModel->getCollectionSystemTransfer($id)); 67 | } 68 | 69 | // getCollectionSystemTransfersStatuses - return the names and statuses of the collection system transfers. 70 | public function getCollectionSystemTransfersStatuses() { 71 | echo json_encode($this->_collectionSystemTransfersModel->getCollectionSystemTransfersStatuses()); 72 | } 73 | 74 | // setErrorCollectionSystemTransfersStatuses 75 | public function setErrorCollectionSystemTransfer($id) { 76 | $this->_collectionSystemTransfersModel->setErrorCollectionSystemTransfer($id); 77 | } 78 | 79 | // setRunningCollectionSystemTransfersStatuses 80 | public function setRunningCollectionSystemTransfer($id) { 81 | $return = array(); 82 | if(isset($_POST['jobPid'])){ 83 | $this->_collectionSystemTransfersModel->setRunningCollectionSystemTransfer($id, $_POST['jobPid']); 84 | $return['status'] = 'success'; 85 | } else { 86 | $return['status'] = 'error'; 87 | $return['message'] = 'missing POST data'; 88 | } 89 | echo json_encode($return); 90 | } 91 | 92 | 93 | // setIdleCollectionSystemTransfersStatuses 94 | public function setIdleCollectionSystemTransfer($id) { 95 | $this->_collectionSystemTransfersModel->setIdleCollectionSystemTransfer($id); 96 | } 97 | 98 | } -------------------------------------------------------------------------------- /www/app/Controllers/Api/CruiseDataTransfers.php: -------------------------------------------------------------------------------- 1 | _cruiseDataTransfersModel = new \Models\Config\CruiseDataTransfers(); 20 | } 21 | 22 | public function getCruiseDataTransfers(){ 23 | 24 | echo json_encode($this->_cruiseDataTransfersModel->getCruiseDataTransfers()); 25 | } 26 | 27 | public function getCruiseDataTransfer($id){ 28 | 29 | echo json_encode($this->_cruiseDataTransfersModel->getCruiseDataTransfer($id)); 30 | } 31 | 32 | public function getRequiredCruiseDataTransfers(){ 33 | 34 | echo json_encode($this->_cruiseDataTransfersModel->getRequiredCruiseDataTransfers()); 35 | } 36 | 37 | public function getRequiredCruiseDataTransfer($id){ 38 | 39 | echo json_encode($this->_cruiseDataTransfersModel->getRequiredCruiseDataTransfer($id)); 40 | } 41 | 42 | // getCruiseDataTransfersStatuses - return the names and statuses of the cruise data transfers. 43 | public function getCruiseDataTransfersStatuses() { 44 | echo json_encode($this->_cruiseDataTransfersModel->getCruiseDataTransfersStatuses()); 45 | } 46 | 47 | // getCruiseDataTransfersStatuses - return the names and statuses of the cruise data transfers. 48 | public function getRequiredCruiseDataTransfersStatuses() { 49 | echo json_encode($this->_cruiseDataTransfersModel->getRequiredCruiseDataTransfersStatuses()); 50 | } 51 | 52 | // setStoppingCruiseDataTransfer 53 | public function setStoppingCruiseDataTransfer($id) { 54 | $this->_cruiseDataTransfersModel->setStoppingCruiseDataTransfer($id); 55 | } 56 | 57 | // setErrorCruiseDataTransfer 58 | public function setErrorCruiseDataTransfer($id) { 59 | $this->_cruiseDataTransfersModel->setErrorCruiseDataTransfer($id); 60 | } 61 | 62 | // setRunningCruiseDataTransfer 63 | public function setRunningCruiseDataTransfer($id) { 64 | $return = array(); 65 | if(isset($_POST['jobPid'])){ 66 | $this->_cruiseDataTransfersModel->setRunningCruiseDataTransfer($id, $_POST['jobPid']); 67 | $return['status'] = 'success'; 68 | } else { 69 | $return['status'] = 'error'; 70 | $return['message'] = 'missing POST data'; 71 | } 72 | echo json_encode($return); 73 | } 74 | 75 | // setIdlerCruiseDataTransfer 76 | public function setIdleCruiseDataTransfer($id) { 77 | $this->_cruiseDataTransfersModel->setIdleCruiseDataTransfer($id); 78 | } 79 | 80 | } 81 | -------------------------------------------------------------------------------- /www/app/Controllers/Api/ExtraDirectories.php: -------------------------------------------------------------------------------- 1 | _model = new \Models\Config\ExtraDirectories(); 20 | } 21 | 22 | public function getExtraDirectories(){ 23 | 24 | echo json_encode($this->_model->getExtraDirectories()); 25 | } 26 | 27 | public function getExtraDirectory($id){ 28 | 29 | echo json_encode($this->_model->getExtraDirectory($id)); 30 | } 31 | 32 | public function getRequiredExtraDirectories(){ 33 | 34 | echo json_encode($this->_model->getRequiredExtraDirectories()); 35 | } 36 | 37 | } -------------------------------------------------------------------------------- /www/app/Controllers/Api/Gearman.php: -------------------------------------------------------------------------------- 1 | _model = new \Models\Api\Gearman(); 20 | } 21 | 22 | public function newJob($handle){ 23 | 24 | $return = array(); 25 | if(isset($_POST['jobPid'])){ 26 | $data['jobHandle'] = $handle; 27 | $data['jobPid'] = $_POST['jobPid']; 28 | 29 | (isset($_POST['jobName']) ? $data['jobName'] = $_POST['jobName'] : $data['jobName'] = $handle ); 30 | $this->_model->insertJob($data); 31 | $return['status'] = 'success'; 32 | } else { 33 | $return['status'] = 'error'; 34 | $return['message'] = 'missing POST data'; 35 | } 36 | echo json_encode($return); 37 | } 38 | 39 | public function getJob($id){ 40 | 41 | echo json_encode($this->_model->getJob($id)); 42 | 43 | } 44 | 45 | public function getJobs(){ 46 | 47 | echo json_encode($this->_model->getJobs()); 48 | } 49 | 50 | public function clearAllJobsFromDB(){ 51 | 52 | echo json_encode($this->_model->clearAllJobsFromDB()); 53 | } 54 | 55 | } -------------------------------------------------------------------------------- /www/app/Controllers/Api/Messages.php: -------------------------------------------------------------------------------- 1 | _messageModel = new \Models\Config\Messages(); 21 | } 22 | 23 | public function newMessage(){ 24 | 25 | //var_dump($_POST); 26 | 27 | if(isset($_POST['messageTitle']) && isset($_POST['messageBody'])) { 28 | $this->_messageModel->insertMessage(array('messageTitle'=>$_POST['messageTitle'], 'messageBody'=>$_POST['messageBody'])); 29 | $return['status'] = 'success'; 30 | } elseif(isset($_POST['messageTitle'])) { 31 | $this->_messageModel->insertMessage(array('messageTitle'=>$_POST['messageTitle'], 'messageBody'=>'')); 32 | $return['status'] = 'success'; 33 | } else { 34 | $return['status'] = 'error'; 35 | $return['error'] = 'missing POST data'; 36 | } 37 | echo json_encode($return); 38 | } 39 | 40 | public function viewedMessage($id){ 41 | 42 | $this->_messageModel->viewedMessage($id); 43 | 44 | $return['status'] = 'success'; 45 | 46 | echo json_encode($return); 47 | } 48 | 49 | public function getRecentMessages(){ 50 | 51 | echo json_encode($this->_messageModel->getNewMessages($this->_messageLimit)); 52 | } 53 | 54 | public function getNewMessagesTotal(){ 55 | 56 | echo json_encode($this->_messageModel->getNewMessagesTotal()); 57 | } 58 | 59 | 60 | } 61 | -------------------------------------------------------------------------------- /www/app/Controllers/Api/ShipToShoreTransfers.php: -------------------------------------------------------------------------------- 1 | _model = new \Models\Config\ShipToShoreTransfers(); 21 | } 22 | 23 | public function getShipToShoreTransfers(){ 24 | 25 | echo json_encode($this->_model->getShipToShoreTransfers()); 26 | } 27 | 28 | public function getRequiredShipToShoreTransfers(){ 29 | 30 | echo json_encode($this->_model->getRequiredShipToShoreTransfers()); 31 | } 32 | } -------------------------------------------------------------------------------- /www/app/Controllers/Api/Tasks.php: -------------------------------------------------------------------------------- 1 | _tasksModel = new \Models\Config\Tasks(); 20 | } 21 | 22 | public function getTasks(){ 23 | echo json_encode($this->_tasksModel->getTasks()); 24 | } 25 | 26 | public function getActiveTasks(){ 27 | echo json_encode($this->_tasksModel->getActiveTasks()); 28 | } 29 | 30 | public function getCruiseOnlyTasks(){ 31 | echo json_encode($this->_tasksModel->getCruiseOnlyTasks()); 32 | } 33 | 34 | public function getLoweringOnlyTasks(){ 35 | echo json_encode($this->_tasksModel->getLoweringOnlyTasks()); 36 | } 37 | 38 | public function getTask($id){ 39 | echo json_encode($this->_tasksModel->getTask($id)); 40 | } 41 | 42 | // getProcessesStatuses - return the names and statuses of the collection system transfers. 43 | public function getTaskStatuses() { 44 | echo json_encode($this->_tasksModel->getTaskStatuses()); 45 | } 46 | 47 | // setErrorProcess 48 | public function setErrorTask($id) { 49 | $this->_tasksModel->setErrorTask($id); 50 | } 51 | 52 | // setRunningProcess 53 | public function setRunningTask($id) { 54 | $return = array(); 55 | if(isset($_POST['jobPid'])){ 56 | $this->_tasksModel->setRunningTask($id, $_POST['jobPid']); 57 | $return['status'] = 'success'; 58 | } else { 59 | $return['status'] = 'error'; 60 | $return['message'] = 'missing POST data'; 61 | } 62 | echo json_encode($return); 63 | } 64 | 65 | // setIdleProcess 66 | public function setIdleTask($id) { 67 | $this->_tasksModel->setIdleTask($id); 68 | } 69 | 70 | } -------------------------------------------------------------------------------- /www/app/Controllers/Api/TransferLogs.php: -------------------------------------------------------------------------------- 1 | _model = new \Models\TransferLogs(); 22 | } 23 | 24 | public function getExcludeLogsSummary() { 25 | echo json_encode($this->_model->getExcludeLogsSummary()); 26 | } 27 | 28 | public function getShipboardLogsSummary($count = 0) { 29 | echo json_encode($this->_model->getShipboardLogsSummary($count)); 30 | } 31 | 32 | public function getShipToShoreLogsSummary($count = 0) { 33 | echo json_encode($this->_model->getShipToShoreLogsSummary($count)); 34 | } 35 | } -------------------------------------------------------------------------------- /www/app/Controllers/Config/Auth.php: -------------------------------------------------------------------------------- 1 | _model = new \Models\Config\Auth(); 16 | } 17 | 18 | public function login() { 19 | 20 | if(Session::get('loggedin')){ 21 | Url::redirect('config'); 22 | } 23 | 24 | if(isset($_POST['submit'])){ 25 | 26 | $username = $_POST['username']; 27 | $password = $_POST['password']; 28 | $referrer = $_POST['referrer']; 29 | 30 | //validation 31 | if(Password::verify($password, $this->_model->getHash($username)) == false) { 32 | $error[] = 'Wrong username or password'; 33 | } 34 | 35 | //if validation has passed, carry on 36 | if(!$error) { 37 | Session::set('loggedin',true); 38 | Session::set('userID', $this->_model->getUserID($username)); 39 | 40 | $data = array('lastLogin' => date('Y-m-d G:i:s')); 41 | $where = array('userID' => $this->_model->getUserID($username)); 42 | $this->_model->updateUser($data,$where); 43 | 44 | //Url::redirect($referrer, true); 45 | Url::redirect('config'); 46 | } 47 | } 48 | 49 | $data['title'] = 'Login'; 50 | $data['referrer'] = $_SERVER['HTTP_REFERER']; 51 | View::rendertemplate('loginheader', $data); 52 | View::render('Config/login', $data,$error); 53 | View::rendertemplate('footer', $data); 54 | } 55 | 56 | public function logout() { 57 | Session::destroy(); 58 | Url::redirect(''); 59 | } 60 | 61 | } 62 | -------------------------------------------------------------------------------- /www/app/Controllers/Config/Messages.php: -------------------------------------------------------------------------------- 1 | _messagesModel = new \Models\Config\Messages(); 19 | } 20 | 21 | public function index(){ 22 | $data['title'] = 'Messages'; 23 | 24 | $pages = new Paginator('15','page'); 25 | 26 | $pages->setTotal($this->_messagesModel->getMessagesTotal()); 27 | $data['messages'] = $this->_messagesModel->getMessages($pages->getLimit()); 28 | $data['page_links'] = $pages->pageLinks(); 29 | 30 | $data['javascript'] = array(); 31 | 32 | View::rendertemplate('header',$data); 33 | View::render('Config/messages',$data); 34 | View::rendertemplate('footer',$data); 35 | } 36 | 37 | public function deleteMessage($id){ 38 | $where = array('messageID' => $id); 39 | $this->_messagesModel->deleteMessage($where); 40 | Session::set('message','Message Deleted'); 41 | Url::redirect('config/messages'); 42 | } 43 | 44 | public function viewedMessage($id){ 45 | $this->_messagesModel->viewedMessage($id); 46 | Url::redirect('config/messages'); 47 | } 48 | 49 | public function viewAllMessages(){ 50 | $this->_messagesModel->viewAllMessages(); 51 | Url::redirect('config/messages'); 52 | } 53 | 54 | public function deleteAllMessages(){ 55 | $this->_messagesModel->deleteAllMessages(); 56 | Session::set('message','Messages Deleted'); 57 | Url::redirect('config/messages'); 58 | } 59 | 60 | } 61 | -------------------------------------------------------------------------------- /www/app/Controllers/Config/Users.php: -------------------------------------------------------------------------------- 1 | _model = new \Models\Config\Users(); 20 | } 21 | 22 | public function index(){ 23 | $data['title'] = 'Configuration'; 24 | $data['users'] = $this->_model->getUsers(); 25 | //$data['javascript'] = array(''); 26 | View::rendertemplate('header',$data); 27 | View::render('Config/users',$data); 28 | View::rendertemplate('footer',$data); 29 | } 30 | 31 | public function add(){ 32 | $data['title'] = 'Add User'; 33 | 34 | if(isset($_POST['submit'])){ 35 | $username = $_POST['username']; 36 | $password = $_POST['password']; 37 | 38 | if($username == ''){ 39 | $error[] = 'Username is required'; 40 | } 41 | 42 | if($password == ''){ 43 | $error[] = 'Password is required'; 44 | } 45 | 46 | if(strcmp($password, $_POST['password2']) !== 0) { 47 | $error[] = 'Passwords must match'; 48 | } 49 | 50 | if(!$error){ 51 | $postdata = array( 52 | 'username' => $username, 53 | 'password' => Password::make($password)//, 54 | ); 55 | 56 | $this->_model->insertUser($postdata); 57 | Session::set('message','User Added'); 58 | Url::redirect('config/users'); 59 | } 60 | } 61 | 62 | View::rendertemplate('header',$data); 63 | View::render('config/addUser',$data,$error); 64 | View::rendertemplate('footer',$data); 65 | } 66 | 67 | public function edit($id){ 68 | $data['title'] = 'Edit User'; 69 | $data['row'] = $this->_model->getUser($id); 70 | 71 | if(isset($_POST['submit'])){ 72 | $username = $_POST['username']; 73 | $password = $_POST['password']; 74 | 75 | if($username == ''){ 76 | $error[] = 'Username is required'; 77 | } 78 | 79 | if($password == ''){ 80 | $error[] = 'Password is required'; 81 | } 82 | 83 | if(strcmp($password, $_POST['password2']) !== 0) { 84 | $error[] = 'Passwords must match'; 85 | } 86 | 87 | 88 | 89 | if(!$error){ 90 | $postdata = array( 91 | 'username' => $username, 92 | 'password' => Password::make($password) 93 | ); 94 | 95 | $where = array('userID' => $id); 96 | $this->_model->updateUser($postdata,$where); 97 | Session::set('message','User Updated'); 98 | Url::redirect('config'); 99 | } 100 | } 101 | 102 | View::rendertemplate('header',$data); 103 | View::render('Config/editUser',$data,$error); 104 | View::rendertemplate('footer',$data); 105 | } 106 | 107 | public function delete($id){ 108 | $where = array('userID' => $id); 109 | $this->_model->deleteUser($where); 110 | Session::set('message','User Deleted'); 111 | Url::redirect('config/users'); 112 | } 113 | } -------------------------------------------------------------------------------- /www/app/Controllers/Welcome.php: -------------------------------------------------------------------------------- 1 | _warehouseModel = new \Models\Warehouse(); 17 | $this->_collectionSystemTransfersModel = new \Models\Config\CollectionSystemTransfers(); 18 | $this->_cruiseDataTransfersModel = new \Models\Config\CruiseDataTransfers(); 19 | $this->_transferLogsModel = new \Models\TransferLogs(); 20 | $this->_extraDirectoriesModel = new \Models\Config\ExtraDirectories(); 21 | } 22 | 23 | public function index(){ 24 | $data['title'] = 'Dashboard'; 25 | 26 | if($this->_warehouseModel->getSystemStatus()) { 27 | $data['systemStatus'] = "On"; 28 | } else { 29 | $data['systemStatus'] = "Off"; 30 | } 31 | 32 | $data['cruiseID'] = $this->_warehouseModel->getCruiseID(); 33 | $data['collectionSystemTransfers'] = $this->_collectionSystemTransfersModel->getActiveCollectionSystemTransfers(); 34 | $data['requiredCruiseDataTransfers'] = $this->_cruiseDataTransfersModel->getRequiredCruiseDataTransfers(); 35 | $data['cruiseDataTransfers'] = $this->_cruiseDataTransfersModel->getCruiseDataTransfers(); 36 | 37 | $data['filenameErrors'] = $this->_transferLogsModel->getExcludeLogsSummary(); 38 | $data['shipboardTransfers'] = $this->_transferLogsModel->getShipboardLogsSummary(5); 39 | $data['shipToShoreTransfers'] = $this->_transferLogsModel->getShipToShoreLogsSummary(5); 40 | $data['javascript'] = array('welcome'); 41 | 42 | View::rendertemplate('header',$data); 43 | View::render('Welcome/index',$data); 44 | View::rendertemplate('footer',$data); 45 | } 46 | } 47 | -------------------------------------------------------------------------------- /www/app/Core/Config.php.dist: -------------------------------------------------------------------------------- 1 | view = new View(); 39 | 40 | /* initialise the language object */ 41 | $this->language = new Language(); 42 | } 43 | } 44 | -------------------------------------------------------------------------------- /www/app/Core/Error.php: -------------------------------------------------------------------------------- 1 | error. 27 | * 28 | * @param string $error 29 | */ 30 | public function __construct($error) 31 | { 32 | parent::__construct(); 33 | $this->error = $error; 34 | } 35 | 36 | /** 37 | * Load a 404 page with the error message. 38 | */ 39 | public function index() 40 | { 41 | header('HTTP/1.0 404 Not Found'); 42 | 43 | $data['title'] = '404'; 44 | $data['error'] = $this->error; 45 | 46 | View::renderTemplate('header', $data); 47 | View::render('error/404', $data); 48 | View::renderTemplate('footer', $data); 49 | } 50 | 51 | /** 52 | * Display errors. 53 | * 54 | * @param array $error an error of errors 55 | * @param string $class name of class to apply to div 56 | * 57 | * @return string return the errors inside divs 58 | */ 59 | public static function display($error, $class = 'alert alert-danger') 60 | { 61 | if (is_array($error)) { 62 | foreach ($error as $error) { 63 | $row .= "
$error
"; 64 | } 65 | 66 | return $row; 67 | } else { 68 | if (isset($error)) { 69 | return "
$error
"; 70 | } 71 | } 72 | } 73 | } 74 | -------------------------------------------------------------------------------- /www/app/Core/Language.php: -------------------------------------------------------------------------------- 1 | array = include $file; 40 | } else { 41 | /* display error */ 42 | echo Error::display("Could not load language file '$code/$name.php'"); 43 | die; 44 | } 45 | } 46 | 47 | /** 48 | * Get element from language array by key. 49 | * 50 | * @param string $value 51 | * 52 | * @return string 53 | */ 54 | public function get($value) 55 | { 56 | if (!empty($this->array[$value])) { 57 | return $this->array[$value]; 58 | } else { 59 | return $value; 60 | } 61 | } 62 | 63 | /** 64 | * Get lang for views. 65 | * 66 | * @param string $value this is "word" value from language file 67 | * @param string $name name of file with language 68 | * @param string $code optional, language code 69 | * 70 | * @return string 71 | */ 72 | public static function show($value, $name, $code = LANGUAGE_CODE) 73 | { 74 | /* lang file */ 75 | $file = SMVC."app/language/$code/$name.php"; 76 | 77 | /* check if is readable */ 78 | if (is_readable($file)) { 79 | /* require file */ 80 | $array = include $file; 81 | } else { 82 | /* display error */ 83 | echo Error::display("Could not load language file '$code/$name.php'"); 84 | die; 85 | } 86 | 87 | if (!empty($array[$value])) { 88 | return $array[$value]; 89 | } else { 90 | return $value; 91 | } 92 | } 93 | } 94 | -------------------------------------------------------------------------------- /www/app/Core/Model.php: -------------------------------------------------------------------------------- 1 | db = Database::get(); 34 | } 35 | } 36 | -------------------------------------------------------------------------------- /www/app/Core/View.php: -------------------------------------------------------------------------------- 1 | 1) { 29 | $segment = array_shift($segments); 30 | if (!isset($array[$segment]) || !is_array($array[$segment])) { 31 | $array[$segment] = []; 32 | } 33 | $array = &$array[$segment]; 34 | } 35 | $array[array_shift($segments)] = $value; 36 | } 37 | 38 | /** 39 | * Search for an array value. Returns TRUE if the array key exists and FALSE if not. 40 | * 41 | * @param array $array 42 | * @param string $path 43 | * 44 | * @return bool 45 | */ 46 | public static function has(array $array, $path) 47 | { 48 | $segments = explode('.', $path); 49 | foreach ($segments as $segment) { 50 | if (!is_array($array) || !isset($array[$segment])) { 51 | return false; 52 | } 53 | $array = $array[$segment]; 54 | } 55 | 56 | return true; 57 | } 58 | 59 | /** 60 | * Returns value from array. 61 | * 62 | * @param array $array 63 | * @param string $path 64 | * @param mixed $default 65 | * 66 | * @return array|null 67 | */ 68 | public static function get(array $array, $path, $default = null) 69 | { 70 | $segments = explode('.', $path); 71 | foreach ($segments as $segment) { 72 | if (!is_array($array) || !isset($array[$segment])) { 73 | return $default; 74 | } 75 | $array = $array[$segment]; 76 | } 77 | 78 | return $array; 79 | } 80 | 81 | /** 82 | * Remove an array value. 83 | * 84 | * @param array $array Array you want to modify 85 | * @param string $path Array path 86 | * 87 | * @return bool 88 | */ 89 | public static function remove(array &$array, $path) 90 | { 91 | $segments = explode('.', $path); 92 | while (count($segments) > 1) { 93 | $segment = array_shift($segments); 94 | if (!isset($array[$segment]) || !is_array($array[$segment])) { 95 | return false; 96 | } 97 | $array = &$array[$segment]; 98 | } 99 | unset($array[array_shift($segments)]); 100 | 101 | return true; 102 | } 103 | 104 | /** 105 | * Returns a random value from an array. 106 | * 107 | * @param array $array Array you want to pick a random value from 108 | * 109 | * @return mixed 110 | */ 111 | public static function rand(array $array) 112 | { 113 | return $array[array_rand($array)]; 114 | } 115 | 116 | /** 117 | * Returns TRUE if the array is associative and FALSE if not. 118 | * 119 | * @param array $array Array to check 120 | * 121 | * @return bool 122 | */ 123 | public static function isAssoc(array $array) 124 | { 125 | return count(array_filter(array_keys($array), 'is_string')) === count($array); 126 | } 127 | 128 | /** 129 | * Returns the values from a single column of the input array, identified by the key. 130 | * 131 | * @param array $array Array to pluck from 132 | * @param string $key Array key 133 | * 134 | * @return array 135 | */ 136 | public static function value(array $array, $key) 137 | { 138 | return array_map(function ($value) use ($key) { 139 | return is_object($value) ? $value->$key : $value[$key]; 140 | }, $array); 141 | } 142 | } 143 | -------------------------------------------------------------------------------- /www/app/Helpers/Assets.php: -------------------------------------------------------------------------------- 1 | '', 23 | 'css' => '', 24 | ]; 25 | 26 | /** 27 | * Common templates for assets. 28 | * 29 | * @param string|array $files 30 | * @param string $template 31 | */ 32 | protected static function resource($files, $template) 33 | { 34 | $template = self::$templates[$template]; 35 | 36 | if (is_array($files)) { 37 | foreach ($files as $file) { 38 | echo sprintf($template, $file)."\n"; 39 | } 40 | } else { 41 | echo sprintf($template, $files)."\n"; 42 | } 43 | } 44 | 45 | /** 46 | * Output script. 47 | * 48 | * @param array|string $file/s 49 | */ 50 | public static function js($files) 51 | { 52 | static::resource($files, 'js'); 53 | } 54 | 55 | /** 56 | * Output stylesheet. 57 | * 58 | * @param string $file 59 | */ 60 | public static function css($files) 61 | { 62 | static::resource($files, 'css'); 63 | } 64 | } 65 | -------------------------------------------------------------------------------- /www/app/Helpers/Csrf.php: -------------------------------------------------------------------------------- 1 | view->renderTemplate('header', $data); 19 | * $this->view->render('pet/edit', $data, $error); // as an example 20 | * $this->view->renderTemplate('footer', $data); 21 | * 22 | * At the bottom of your form, before the submit button put: 23 | * 24 | * 25 | * These lines need to be placed in the controller action to validate CSRF token submitted with the form: 26 | * if (!Csrf::isTokenValid()) { 27 | * Url::redirect('admin/login'); // or wherever you want to redirect to. 28 | * } 29 | * And that's all 30 | */ 31 | class Csrf 32 | { 33 | /** 34 | * get CSRF token and generate a new one if expired. 35 | * 36 | * @static static method 37 | * 38 | * @return string 39 | */ 40 | public static function makeToken() 41 | { 42 | $max_time = 60 * 60 * 24; // token is valid for 1 day 43 | $csrf_token = Session::get('csrf_token'); 44 | $stored_time = Session::get('csrf_token_time'); 45 | 46 | if ($max_time + $stored_time <= time() || empty($csrf_token)) { 47 | Session::set('csrf_token', md5(uniqid(rand(), true))); 48 | Session::set('csrf_token_time', time()); 49 | } 50 | 51 | return Session::get('csrf_token'); 52 | } 53 | 54 | /** 55 | * checks if CSRF token in session is same as in the form submitted. 56 | * 57 | * @static static method 58 | * 59 | * @return bool 60 | */ 61 | public static function isTokenValid() 62 | { 63 | return $_POST['csrf_token'] === Session::get('csrf_token'); 64 | } 65 | } 66 | -------------------------------------------------------------------------------- /www/app/Helpers/Data.php: -------------------------------------------------------------------------------- 1 | '; 26 | print_r($data); 27 | echo ''; 28 | } 29 | 30 | /** 31 | * var_dump call. 32 | * 33 | * @param string or array $data 34 | */ 35 | public static function vd($data) 36 | { 37 | var_dump($data); 38 | } 39 | 40 | /** 41 | * strlen call - count the lengh of the string. 42 | * 43 | * @param string $data 44 | * 45 | * @return string return the count 46 | */ 47 | public static function sl($data) 48 | { 49 | return strlen($data); 50 | } 51 | 52 | /** 53 | * strtoupper - convert string to uppercase. 54 | * 55 | * @param string $data 56 | * 57 | * @return string 58 | */ 59 | public static function stu($data) 60 | { 61 | return strtoupper($data); 62 | } 63 | 64 | /** 65 | * strtolower - convert string to lowercase. 66 | * 67 | * @param string $data 68 | * 69 | * @return string 70 | */ 71 | public static function stl($data) 72 | { 73 | return strtolower($data); 74 | } 75 | 76 | /** 77 | * ucwords - the first letter of each word to be a capital. 78 | * 79 | * @param string $data 80 | * 81 | * @return string 82 | */ 83 | public static function ucw($data) 84 | { 85 | return ucwords($data); 86 | } 87 | 88 | /** 89 | * key - this will generate a 32 character key. 90 | * 91 | * @return string 92 | */ 93 | public static function create_key($length = 32) 94 | { 95 | $chars = '!@#$%^&*()_+-=ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz1234567890'; 96 | $key = ''; 97 | 98 | for ($i = 0; $i < $length; $i++) { 99 | $key .= $chars{rand(0, strlen($chars) - 1)}; 100 | } 101 | 102 | return $key; 103 | } 104 | } 105 | -------------------------------------------------------------------------------- /www/app/Helpers/Date.php: -------------------------------------------------------------------------------- 1 | diff($d1); 32 | if ($type == null) { 33 | //return array 34 | return $diff; 35 | } else { 36 | return $diff->$type; 37 | } 38 | } 39 | 40 | /** 41 | * Business Days. 42 | * 43 | * Get number of working days between 2 dates 44 | * 45 | * Taken from http://mugurel.sumanariu.ro/php-2/php-how-to-calculate-number-of-work-days-between-2-dates/ 46 | * 47 | * @param date $startDate date in the format of Y-m-d 48 | * @param date $endDate date in the format of Y-m-d 49 | * @param booleen $weekendDays returns the number of weekends 50 | * 51 | * @return int returns the total number of days 52 | */ 53 | public static function businessDays($startDate, $endDate, $weekendDays = false) 54 | { 55 | $begin = strtotime($startDate); 56 | $end = strtotime($endDate); 57 | 58 | if ($begin > $end) { 59 | //startDate is in the future 60 | return 0; 61 | } else { 62 | $numDays = 0; 63 | $weekends = 0; 64 | 65 | while ($begin <= $end) { 66 | $numDays++; // no of days in the given interval 67 | $whatDay = date('N', $begin); 68 | 69 | if ($whatDay > 5) { // 6 and 7 are weekend days 70 | $weekends++; 71 | } 72 | $begin += 86400; // +1 day 73 | }; 74 | 75 | if ($weekendDays == true) { 76 | return $weekends; 77 | } 78 | 79 | $working_days = $numDays - $weekends; 80 | 81 | return $working_days; 82 | } 83 | } 84 | 85 | /** 86 | * get an array of dates between 2 dates (not including weekends). 87 | * 88 | * @param date $startDate start date 89 | * @param date $endDate end date 90 | * @param int $nonWork day of week(int) where weekend begins - 5 = fri -> sun, 6 = sat -> sun, 7 = sunday 91 | * 92 | * @return array list of dates between $startDate and $endDate 93 | */ 94 | public static function businessDates($startDate, $endDate, $nonWork = 6) 95 | { 96 | $begin = new \DateTime($startDate); 97 | $end = new \DateTime($endDate); 98 | $holiday = []; 99 | $interval = new \DateInterval('P1D'); 100 | $dateRange = new \DatePeriod($begin, $interval, $end); 101 | foreach ($dateRange as $date) { 102 | if ($date->format('N') < $nonWork and !in_array($date->format('Y-m-d'), $holiday)) { 103 | $dates[] = $date->format('Y-m-d'); 104 | } 105 | } 106 | 107 | return $dates; 108 | } 109 | 110 | /** 111 | * Takes a month/year as input and returns the number of days 112 | * for the given month/year. Takes leap years into consideration. 113 | * 114 | * @param int $month 115 | * @param int $year 116 | * 117 | * @return int 118 | */ 119 | public static function daysInMonth($month = 0, $year = '') 120 | { 121 | if ($month < 1 or $month > 12) { 122 | return date('t'); 123 | } 124 | if (defined('CAL_GREGORIAN')) { 125 | return cal_days_in_month(CAL_GREGORIAN, $month, $year); 126 | } 127 | if ($year >= 1970) { 128 | return (int) date('t', mktime(12, 0, 0, $month, 1, $year)); 129 | } 130 | if ($month == 2) { 131 | if ($year % 400 === 0 or ($year % 4 === 0 && $year % 100 !== 0)) { 132 | return 29; 133 | } 134 | } 135 | $days_in_month = [31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31]; 136 | 137 | return $days_in_month[$month - 1]; 138 | } 139 | } 140 | -------------------------------------------------------------------------------- /www/app/Helpers/FormCustom.php: -------------------------------------------------------------------------------- 1 | array('id'=>'1', 'name'=>'rd[]', 'value'=>'x', 'label'=>'label_text' )) 19 | * @param array(array(id, name, value, class, checked, disabled, label)) 20 | * @return string 21 | */ 22 | public function radioInline($params = array(), $value = '') 23 | { 24 | $o = '
'; 25 | if (!empty($params)) { 26 | if(strcmp($value, '') === 0) { $value = $params[0]['value']; } 27 | $x = 0; 28 | foreach ($params as $k => $v) { 29 | $v['id'] = (isset($v['id'])) ? $v['id'] : "rd_id_{$x}_".rand(1000,9999); 30 | $o .= "