├── images ├── example.png ├── tuxlogo.png ├── example2.png └── README.md ├── HTML_Pages ├── home.html ├── README.md ├── index.html ├── newscript.html ├── profileupload.html ├── scriptupload.html └── pkgupload.html ├── PHP_Pages ├── README.md ├── getsites.php ├── getscripts.php ├── getcategories.php ├── scriptdownload.php ├── createscript.php ├── pkgupload.php ├── scriptupload.php └── profileupload.php ├── uploadstylesheet.css └── README.md /images/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/therealmacjeezy/JamfProUploadPage_v1/HEAD/images/example.png -------------------------------------------------------------------------------- /images/tuxlogo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/therealmacjeezy/JamfProUploadPage_v1/HEAD/images/tuxlogo.png -------------------------------------------------------------------------------- /images/example2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/therealmacjeezy/JamfProUploadPage_v1/HEAD/images/example2.png -------------------------------------------------------------------------------- /images/README.md: -------------------------------------------------------------------------------- 1 | ## File Contents 2 | 3 | * **tuxlogo.png** 4 | * Image that is displayed in the header of the index.html 5 | -------------------------------------------------------------------------------- /HTML_Pages/home.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 |
10 | Select an option from the list above to get started. 11 |
12 | 13 | -------------------------------------------------------------------------------- /HTML_Pages/README.md: -------------------------------------------------------------------------------- 1 | ## File Contents 2 | 3 | * **index.html** 4 | * Main / Landing Page 5 | 6 | * **newscript.html** 7 | * Page for uploading new scripts to the JSS 8 | 9 | * **pkgupload.html** 10 | * Page for uploading a package to the JSS. 11 | 12 | * **profileupload.html** 13 | * Page for uploading a profile to the JSS. 14 | 15 | * **scriptupload.html** 16 | * Page for downloading and uploading an existing script to the JSS. 17 | 18 | * **home.html** 19 | * Default page that displays the text *"Select an option from the list above to get started."* 20 | -------------------------------------------------------------------------------- /PHP_Pages/README.md: -------------------------------------------------------------------------------- 1 | ## File Contents 2 | 3 | * **createscript.php** 4 | * Handles the uploading of new scripts to the JSS. 5 | 6 | * **getscripts.php** 7 | * Creates an xml file (ScriptList.xml) via an API Call that is used to populate the dropdown menu with script names 8 | 9 | * **getsites.php** 10 | * Creates an xml file (SiteList.xml) via an API Call that is used to populate the dropdown menu with site names 11 | 12 | * **getcategories.php** 13 | * Creates an xml file (CategoriesList.xml) via an API Call that is used to populate the dropdown menu with categories 14 | 15 | * **pkgupload.php** 16 | * Handles the uploading of packages to the JSS. 17 | 18 | * **profileupload.php** 19 | * Handles the uploading of profiles to the JSS. 20 | 21 | * **scriptdownload.php** 22 | * Handles the downloading of scripts from the JSS. 23 | 24 | * **scriptupload.php** 25 | * Handles the uploading of scripts to the JSS. 26 | -------------------------------------------------------------------------------- /HTML_Pages/index.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Jamf Pro File Upload 9 | 18 | 19 | 20 | 21 |
22 |
23 |

Jamf Pro File Upload Page

24 |
25 | 26 |
27 | Package 28 | Profiles 29 | 36 | Home 37 |
38 | 39 |
40 |
41 | Select an option from the list above to get started. 42 |

43 |
44 | 45 | 50 | 51 | 52 | -------------------------------------------------------------------------------- /PHP_Pages/getsites.php: -------------------------------------------------------------------------------- 1 | 3 | // November 2018 4 | // GitHub: github.com/therealmacjeezy 5 | // JamfNation: therealmacjeezy 6 | 7 | libxml_use_internal_errors(true); 8 | 9 | function callAPI($method, $url, $data){ 10 | $curl = curl_init(); 11 | 12 | switch ($method){ 13 | case "POST": 14 | curl_setopt($curl, CURLOPT_POST, 1); 15 | if ($data) 16 | curl_setopt($curl, CURLOPT_POSTFIELDS, $data); 17 | break; 18 | case "PUT": 19 | curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "PUT"); 20 | if ($data) 21 | curl_setopt($curl, CURLOPT_POSTFIELDS, $data); 22 | break; 23 | default: 24 | if ($data) 25 | $url = sprintf("%s?%s", $url, http_build_query($data)); 26 | } 27 | 28 | // OPTIONS: 29 | curl_setopt($curl, CURLOPT_URL, $url); 30 | curl_setopt($curl, CURLOPT_HTTPHEADER, array( 31 | 'APIKEY: 111111111111111111111', 32 | 'Content-Type: application/xml', 33 | )); 34 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); 35 | curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); 36 | // Change service account before production 37 | curl_setopt($curl, CURLOPT_USERPWD, "apiuser:apipass"); 38 | curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); 39 | 40 | // EXECUTE: 41 | $result = curl_exec($curl); 42 | if(!$result){die("Connection Failure");} 43 | curl_close($curl); 44 | return $result; 45 | } 46 | 47 | $get_data = callAPI('GET', 'https://jamfurl/JSSResource/sites', false); 48 | 49 | $xml = simplexml_load_string($get_data); 50 | 51 | $fileName = "SiteList.xml"; 52 | 53 | $xml->asXML($fileName); 54 | 55 | ?> 56 | -------------------------------------------------------------------------------- /PHP_Pages/getscripts.php: -------------------------------------------------------------------------------- 1 | 3 | // November 2018 4 | // GitHub: github.com/therealmacjeezy 5 | // JamfNation: therealmacjeezy 6 | 7 | libxml_use_internal_errors(true); 8 | 9 | function callAPI($method, $url, $data){ 10 | $curl = curl_init(); 11 | 12 | switch ($method){ 13 | case "POST": 14 | curl_setopt($curl, CURLOPT_POST, 1); 15 | if ($data) 16 | curl_setopt($curl, CURLOPT_POSTFIELDS, $data); 17 | break; 18 | case "PUT": 19 | curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "PUT"); 20 | if ($data) 21 | curl_setopt($curl, CURLOPT_POSTFIELDS, $data); 22 | break; 23 | default: 24 | if ($data) 25 | $url = sprintf("%s?%s", $url, http_build_query($data)); 26 | } 27 | 28 | // OPTIONS: 29 | curl_setopt($curl, CURLOPT_URL, $url); 30 | curl_setopt($curl, CURLOPT_HTTPHEADER, array( 31 | 'APIKEY: 111111111111111111111', 32 | 'Content-Type: application/xml', 33 | )); 34 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); 35 | curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); 36 | // Change service account before production 37 | curl_setopt($curl, CURLOPT_USERPWD, "apiuser:apipass"); 38 | curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); 39 | 40 | // EXECUTE: 41 | $result = curl_exec($curl); 42 | if(!$result){die("Connection Failure");} 43 | curl_close($curl); 44 | return $result; 45 | } 46 | 47 | $get_data = callAPI('GET', 'https://jamfurl/JSSResource/scripts', false); 48 | 49 | $xml = simplexml_load_string($get_data); 50 | 51 | $fileName = "ScriptList.xml"; 52 | 53 | $xml->asXML($fileName); 54 | 55 | ?> -------------------------------------------------------------------------------- /PHP_Pages/getcategories.php: -------------------------------------------------------------------------------- 1 | 3 | // November 2018 4 | // GitHub: github.com/therealmacjeezy 5 | // JamfNation: therealmacjeezy 6 | 7 | libxml_use_internal_errors(true); 8 | 9 | function callAPI($method, $url, $data){ 10 | $curl = curl_init(); 11 | 12 | switch ($method){ 13 | case "POST": 14 | curl_setopt($curl, CURLOPT_POST, 1); 15 | if ($data) 16 | curl_setopt($curl, CURLOPT_POSTFIELDS, $data); 17 | break; 18 | case "PUT": 19 | curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "PUT"); 20 | if ($data) 21 | curl_setopt($curl, CURLOPT_POSTFIELDS, $data); 22 | break; 23 | default: 24 | if ($data) 25 | $url = sprintf("%s?%s", $url, http_build_query($data)); 26 | } 27 | 28 | // OPTIONS: 29 | curl_setopt($curl, CURLOPT_URL, $url); 30 | curl_setopt($curl, CURLOPT_HTTPHEADER, array( 31 | 'APIKEY: 111111111111111111111', 32 | 'Accept: application/xml', 33 | )); 34 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); 35 | curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); 36 | // Change service account before production 37 | curl_setopt($curl, CURLOPT_USERPWD, "apiuser:apipass"); 38 | curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); 39 | 40 | // EXECUTE: 41 | $result = curl_exec($curl); 42 | if(!$result){die("Connection Failure");} 43 | curl_close($curl); 44 | return $result; 45 | } 46 | 47 | $get_data = callAPI('GET', 'https://jamfurl/JSSResource/categories', false); 48 | 49 | $xml = simplexml_load_string($get_data); 50 | 51 | $fileName = "CategoriesList.xml"; 52 | 53 | //file_put_contents($fileName, $get_data); 54 | 55 | $xml->asXML($fileName); 56 | 57 | ?> 58 | -------------------------------------------------------------------------------- /HTML_Pages/newscript.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Jamf Pro File Upload - Script 10 | 11 | 35 | 36 | 37 |

Create Script

38 | 39 |

This page allows you to upload a new script to the JSS.
To upload a script, select your site from the "Select jamf Site" dropdown menu, then attach your script via the "Choose File" option where it says "Script to Upload"

40 |

** All fields are required **
41 | ** NOTE: If the script already exists in the JSS, use the "Edit Existing Script" option from the "Scripts" link above. **

42 |

43 |
44 | Select jamf Site: 45 |
48 |
Script to Upload: 49 | 50 | 51 |
52 |
Note: No progress bar will appear during the upload. Please do not refresh or navigate away from this page until the upload is completed. 53 | 54 | 55 | 56 | -------------------------------------------------------------------------------- /PHP_Pages/scriptdownload.php: -------------------------------------------------------------------------------- 1 | 3 | // November 2018 4 | // GitHub: github.com/therealmacjeezy 5 | // JamfNation: therealmacjeezy 6 | 7 | $errors = []; // Store all foreseen and unforseen errors here 8 | // File Extensions go here. 9 | $fileExtensions = ['sh', 'py']; 10 | $uploadDirectory = "/Scripts/"; 11 | $currentDir = getcwd(); 12 | 13 | libxml_use_internal_errors(true); 14 | 15 | function callAPI($method, $url, $data){ 16 | $curl = curl_init(); 17 | 18 | switch ($method){ 19 | case "POST": 20 | curl_setopt($curl, CURLOPT_POST, 1); 21 | if ($data) 22 | curl_setopt($curl, CURLOPT_POSTFIELDS, $data); 23 | break; 24 | case "PUT": 25 | curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "PUT"); 26 | if ($data) 27 | curl_setopt($curl, CURLOPT_POSTFIELDS, $data); 28 | break; 29 | default: 30 | if ($data) 31 | $url = sprintf("%s?%s", $url, http_build_query($data)); 32 | } 33 | 34 | // OPTIONS: 35 | curl_setopt($curl, CURLOPT_URL, $url); 36 | curl_setopt($curl, CURLOPT_HTTPHEADER, array( 37 | #'APIKEY: 111111111111111111111', 38 | 'Accept: application/xml', 39 | 'Content-Type: application/xml', 40 | )); 41 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); 42 | curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); 43 | // Change service account before production 44 | curl_setopt($curl, CURLOPT_USERPWD, "apiuser:apipass"); 45 | curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); 46 | 47 | // EXECUTE: 48 | $result = curl_exec($curl); 49 | if(!$result){die("Connection Failure");} 50 | curl_close($curl); 51 | return $result; 52 | } 53 | 54 | if (isset($_POST['download'])) { 55 | 56 | $scriptName = $_POST['jamf_script_name']; 57 | 58 | if (empty($scriptName)) { 59 | $errors[] = "The download option was selected, but the script to download was left blank. Please select a script to download and try again."; 60 | } 61 | 62 | if (empty($errors)) { 63 | $siteURL = urlencode($scriptName); 64 | $get_data = callAPI('GET', 'https://jamfurl/JSSResource/scripts/name/' . $siteURL , false); 65 | 66 | $xml = simplexml_load_string($get_data); 67 | 68 | header("Content-Disposition: attachment; filename=\"$scriptName.sh\""); 69 | echo $xml->script_contents; 70 | } 71 | 72 | if (!empty($errors)) { 73 | foreach ($errors as $error) { 74 | echo $error . "\n"; 75 | echo "Try Again"; 76 | } 77 | } 78 | } 79 | ?> 80 | -------------------------------------------------------------------------------- /HTML_Pages/profileupload.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | Jamf Pro File Upload - - Configuration Profile 10 | 11 | 49 | 50 | 51 | 52 |

Upload Configuration Profile

53 |

This page allows you to upload unsigned Configuration Profiles. To start, select your site from the "Select jamf Site"dropdown menu. 54 |
Once you have selected your site, choose the configuration profile you want to upload and press the upload button. 55 |

56 |

NOTE: The configuration profile MUST be unsigned before it is uploaded. If the configuration profile is SIGNED, only a blank profile will be uploaded.

57 |
58 |
59 | Select jamf Site: 60 | 63 |

64 |
70 |
Note: No progress bar will appear during the upload. Please do not refresh or navigate away from this page until the upload is completed. 71 | 72 | 73 | 74 | -------------------------------------------------------------------------------- /HTML_Pages/scriptupload.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Jamf Pro File Upload - Script 9 | 10 | 38 | 39 | 40 | 41 |

Edit Script

42 |

This page allows you to download, edit and reupload existing scripts. To start, select a script from the "Select Script to Edit / Replace"dropdown menu. 43 |
Once you have selected the script, you will have the option of downloading the current uploaded version. 44 |
45 | When you are ready to upload the script, select the script you are replacing from the "Select Script to Edit / Replace" dropdown menu (if not already selected) and select the updated file from the "Select Script to Upload" section. When ready, select the "Upload Script" button.

46 |

NOTE: The uploaded script will immediately replace the version in use in the JSS. Be sure to verify both script names match before uploading.

47 | 48 |
49 |
50 | Select Script to Edit / Replace: 51 | 54 | 55 |
56 |
57 |
58 | Select Script to Upload: 59 | 60 | 61 |
62 |
Note: No progress bar will appear during the upload. Please do not refresh or navigate away from this page until the upload is completed. 63 | 64 | 65 | 66 | -------------------------------------------------------------------------------- /uploadstylesheet.css: -------------------------------------------------------------------------------- 1 | /* Joshua Harvey */ 2 | /* November 2018 */ 3 | /* GitHub: github.com/therealmacjeezy */ 4 | /* JamfNation: therealmacjeezy */ 5 | 6 | .header { 7 | background-color: #A9A9A9; 8 | text-align: center; 9 | font-family: Arial, Helvetica, sans-serif; 10 | padding: 10px; 11 | } 12 | 13 | .header h3 { 14 | text-shadow: 2px 2px 4px #D3D3D3; 15 | } 16 | 17 | body { 18 | font-family: Arial, Helvetica, sans-serif; 19 | font-size: 14px; 20 | text-align: center; 21 | } 22 | 23 | .footer { 24 | background-color: #A9A9A9; 25 | text-align: left; 26 | font-family: Arial, Helvetica, sans-serif; 27 | font-size: 14px; 28 | padding: 20px; 29 | } 30 | 31 | .footer a { 32 | color: rgb(19, 114, 158); 33 | font-family: Arial, Helvetica, sans-serif; 34 | text-decoration: none; 35 | } 36 | 37 | /* Navbar Container */ 38 | .topnav { 39 | /*overflow: hidden;*/ 40 | text-align: center; 41 | background-color:#000000; 42 | } 43 | 44 | /* Navbar Links */ 45 | .topnav a { 46 | float: center; 47 | text-align: center; 48 | padding: 14px 14px; 49 | color: #D3D3D3; 50 | font-family: Arial, Helvetica, sans-serif; 51 | text-decoration: none; 52 | font-size: 14px; 53 | } 54 | 55 | .topnav a:hover { 56 | background-color: #2959a1; 57 | color: rgb(255, 255, 255); 58 | font-family: Arial, Helvetica, sans-serif; 59 | text-decoration: none 60 | } 61 | 62 | /* Dropdown Button */ 63 | .dropbtn { 64 | background-color: #000000; 65 | color: #D3D3D3; 66 | padding: 14px; 67 | font-size: 14px; 68 | font-family: Arial, Helvetica, sans-serif; 69 | border: none; 70 | cursor: pointer; 71 | } 72 | 73 | .dropdown { 74 | position: relative; 75 | display: inline-block; 76 | } 77 | 78 | /* Dropdown Content (Hidden by Default) */ 79 | .dropdown-content { 80 | display: none; 81 | position: absolute; 82 | background-color: #2959a1; 83 | min-width: 200px; 84 | box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.452); 85 | z-index: 1; 86 | } 87 | 88 | /* Links inside the dropdown */ 89 | .dropdown-content a { 90 | color: white; 91 | padding: 12px 16px; 92 | text-decoration: none; 93 | font-family: Arial, Helvetica, sans-serif; 94 | display: block; 95 | border-color: white; 96 | border-style: solid; 97 | border-width: thin; 98 | } 99 | 100 | /* Change color of dropdown links on hover */ 101 | .dropdown-content a:hover { 102 | background-color: #87CEEB; 103 | color: black; 104 | text-transform: uppercase; 105 | /*font-family: Arial, Helvetica, sans-serif;*/ 106 | font-family: monospace; 107 | word-spacing: -2px; 108 | font-weight: bold; 109 | } 110 | 111 | /* Show the dropdown menu on hover */ 112 | .dropdown:hover .dropdown-content { 113 | display: block; 114 | } 115 | 116 | /* Change the background color of the dropdown button when the dropdown content is shown */ 117 | .dropdown:hover .dropbtn { 118 | background-color: #2959a1; 119 | } 120 | 121 | .button { 122 | background-color: #e7e7e7; 123 | color: black; 124 | font-size: 14px; 125 | font-family: Arial, Helvetica, sans-serif; 126 | border-radius: 4px; 127 | } 128 | 129 | .button:hover { 130 | box-shadow: 0 12px 16px 0 rgba(0,0,0,0.24), 0 17px 50px 0 rgba(0,0,0,0.19); 131 | } 132 | 133 | /* Content Container Columns */ 134 | .column { 135 | float: center; 136 | width: 100%; 137 | background-color: white; 138 | font-family: Arial, Helvetica, sans-serif; 139 | font-size: 14px; 140 | text-align: center; 141 | padding-top: 15px; 142 | padding-bottom: 15px; 143 | } 144 | -------------------------------------------------------------------------------- /PHP_Pages/createscript.php: -------------------------------------------------------------------------------- 1 | 3 | // November 2018 4 | // GitHub: github.com/therealmacjeezy 5 | // JamfNation: therealmacjeezy 6 | 7 | $errors = []; // Store all foreseen and unforseen errors here 8 | // File Extensions go here. 9 | $fileExtensions = ['sh', 'py']; 10 | $uploadDirectory = "/Scripts/"; 11 | $currentDir = getcwd(); 12 | 13 | libxml_use_internal_errors(true); 14 | 15 | function callAPI($method, $url, $data){ 16 | $curl = curl_init(); 17 | 18 | switch ($method){ 19 | case "POST": 20 | curl_setopt($curl, CURLOPT_POST, 1); 21 | if ($data) 22 | curl_setopt($curl, CURLOPT_POSTFIELDS, $data); 23 | break; 24 | case "PUT": 25 | curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "PUT"); 26 | if ($data) 27 | curl_setopt($curl, CURLOPT_POSTFIELDS, $data); 28 | break; 29 | default: 30 | if ($data) 31 | $url = sprintf("%s?%s", $url, http_build_query($data)); 32 | } 33 | 34 | // OPTIONS: 35 | curl_setopt($curl, CURLOPT_URL, $url); 36 | curl_setopt($curl, CURLOPT_HTTPHEADER, array( 37 | #'APIKEY: 111111111111111111111', 38 | 'Accept: application/xml', 39 | 'Content-Type: application/xml', 40 | )); 41 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); 42 | curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); 43 | // Change service account before production 44 | curl_setopt($curl, CURLOPT_USERPWD, "apiuser:apipass"); 45 | curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); 46 | 47 | // EXECUTE: 48 | $result = curl_exec($curl); 49 | if(!$result){die("Connection Failure");} 50 | curl_close($curl); 51 | return $result; 52 | } 53 | 54 | 55 | 56 | if (isset($_POST['submit'])) { 57 | 58 | $siteName = $_POST['jamf_site']; 59 | $fileName = $_FILES['jamf_script']['name']; 60 | $fileSize = $_FILES['jamf_script']['size']; 61 | $fileTmpName = $_FILES['jamf_script']['tmp_name']; 62 | $fileType = $_FILES['jamf_script']['type']; 63 | 64 | if (empty($siteName)) { 65 | $errors[] = "No Site was selected. Please select a site and try again"; 66 | } 67 | 68 | //$fileExtension = strtolower(end(explode('.',$fileName))); 69 | //if (! in_array($fileExtension, $fileExtensions)) { 70 | // $errors[] = "This file extension is not allowed. Please upload a valid file type (.sh, .py)."; 71 | //} 72 | 73 | // This limits the size of the upload to 2.5 MB via bytes 74 | if ($fileSize > 2500000) { 75 | $errors[] = "This file is more than 2.5 MB limit. Please adjust the size or contact helpdesk@it.com for help."; 76 | } 77 | 78 | if (empty($errors)) { 79 | $scriptName = $siteName . "-" . basename($fileName); 80 | 81 | $uploadPath = $currentDir . $uploadDirectory . $scriptName; 82 | $didUpload = move_uploaded_file($fileTmpName, $uploadPath); 83 | $scriptContent = file_get_contents($uploadPath); 84 | //$siteURL = urlencode($scriptName); 85 | $updateXML = ""; 86 | 87 | 88 | if ($didUpload) { 89 | $get_data = callAPI('POST', 'https://jamfurl/JSSResource/scripts/id/0', $updateXML); 90 | header('Refresh:0; url=index.html'); 91 | echo ""; 92 | } else { 93 | $errors[] = "An error occurred with the upload process. Try again or contact helpdesk@it.com for help."; 94 | } 95 | } 96 | 97 | if (!empty($errors)) { 98 | foreach ($errors as $error) { 99 | echo $error . "\n"; 100 | echo "Try Again"; 101 | } 102 | } 103 | 104 | } 105 | ?> 106 | -------------------------------------------------------------------------------- /PHP_Pages/pkgupload.php: -------------------------------------------------------------------------------- 1 | 3 | // November 2018 4 | // GitHub: github.com/therealmacjeezy 5 | // JamfNation: therealmacjeezy 6 | 7 | $errors = []; // Store all foreseen and unforseen errors here 8 | // File Extensions go here. 9 | $fileExtensions = ['pkg', 'dmg', 'mpkg']; 10 | $uploadDirectory = "/Packages/"; 11 | $currentDir = getcwd(); 12 | 13 | libxml_use_internal_errors(true); 14 | 15 | function callAPI($method, $url, $data){ 16 | $curl = curl_init(); 17 | 18 | switch ($method){ 19 | case "POST": 20 | curl_setopt($curl, CURLOPT_POST, 1); 21 | if ($data) 22 | curl_setopt($curl, CURLOPT_POSTFIELDS, $data); 23 | break; 24 | case "PUT": 25 | curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "PUT"); 26 | if ($data) 27 | curl_setopt($curl, CURLOPT_POSTFIELDS, $data); 28 | break; 29 | default: 30 | if ($data) 31 | $url = sprintf("%s?%s", $url, http_build_query($data)); 32 | } 33 | 34 | // OPTIONS: 35 | curl_setopt($curl, CURLOPT_URL, $url); 36 | curl_setopt($curl, CURLOPT_HTTPHEADER, array( 37 | #'APIKEY: 111111111111111111111', 38 | 'Accept: application/xml', 39 | 'Content-Type: application/xml', 40 | )); 41 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); 42 | curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); 43 | // Change service account before production 44 | curl_setopt($curl, CURLOPT_USERPWD, "apiuser:apipass"); 45 | curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); 46 | 47 | // EXECUTE: 48 | $result = curl_exec($curl); 49 | if(!$result){die("Connection Failure");} 50 | curl_close($curl); 51 | return $result; 52 | } 53 | 54 | 55 | 56 | if (isset($_POST['submit'])) { 57 | 58 | $siteName = $_POST['jamf_site']; 59 | $fileName = $_FILES['jamf_package']['name']; 60 | $fileSize = $_FILES['jamf_package']['size']; 61 | $fileTmpName = $_FILES['jamf_package']['tmp_name']; 62 | $fileType = $_FILES['jamf_package']['type']; 63 | 64 | if (empty($siteName)) { 65 | $errors[] = "No Site was selected. Please select a site and try again"; 66 | } 67 | 68 | $fileExtension = strtolower(end(explode('.',$fileName))); 69 | if (! in_array($fileExtension, $fileExtensions)) { 70 | $errors[] = "This file extension is not allowed. Please upload a valid file type (.pkg, .mpkg, .dmg)."; 71 | } 72 | 73 | // This limits the size of the upload to 6.5 GB via bytes 74 | if ($fileSize > 6500000000) { 75 | $errors[] = "This file is more than 6.5 GB limit. Please adjust the size or contact helpdesk@it.com for help."; 76 | } 77 | 78 | if (empty($errors)) { 79 | $pkgName = $siteName . "-" . basename($fileName); 80 | $updateXML = "$pkgName$pkgNameUploads$siteName15"; 81 | 82 | $uploadPath = $uploadDirectory . $siteName . "-" . basename($fileName); 83 | $didUpload = move_uploaded_file($fileTmpName, $uploadPath); 84 | 85 | if ($didUpload) { 86 | //echo "The file " . $siteName . "-" . basename($fileName) . " has been uploaded and is available for use in the Jamf Pro Server.\r\n"; 87 | $get_data = callAPI('POST', 'https://jamfurl/JSSResource/packages/id/0', $updateXML); 88 | header('Refresh:0; url=index.html'); 89 | echo ""; 90 | } else { 91 | $errors[] = "An error occurred with the upload process. Try again or contact helpdesk@it.com for help."; 92 | } 93 | } 94 | 95 | if (!empty($errors)) { 96 | foreach ($errors as $error) { 97 | echo $error . "\n"; 98 | echo "Try Again"; 99 | } 100 | } 101 | } 102 | ?> 103 | -------------------------------------------------------------------------------- /PHP_Pages/scriptupload.php: -------------------------------------------------------------------------------- 1 | 3 | // November 2018 4 | // GitHub: github.com/therealmacjeezy 5 | // JamfNation: therealmacjeezy 6 | 7 | $errors = []; // Store all foreseen and unforseen errors here 8 | // File Extensions go here. 9 | $fileExtensions = ['sh', 'py']; 10 | $uploadDirectory = "/Scripts/"; 11 | $currentDir = getcwd(); 12 | 13 | libxml_use_internal_errors(true); 14 | 15 | function callAPI($method, $url, $data){ 16 | $curl = curl_init(); 17 | 18 | switch ($method){ 19 | case "POST": 20 | curl_setopt($curl, CURLOPT_POST, 1); 21 | if ($data) 22 | curl_setopt($curl, CURLOPT_POSTFIELDS, $data); 23 | break; 24 | case "PUT": 25 | curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "PUT"); 26 | if ($data) 27 | curl_setopt($curl, CURLOPT_POSTFIELDS, $data); 28 | break; 29 | default: 30 | if ($data) 31 | $url = sprintf("%s?%s", $url, http_build_query($data)); 32 | } 33 | 34 | // OPTIONS: 35 | curl_setopt($curl, CURLOPT_URL, $url); 36 | curl_setopt($curl, CURLOPT_HTTPHEADER, array( 37 | #'APIKEY: 111111111111111111111', 38 | 'Accept: application/xml', 39 | 'Content-Type: application/xml', 40 | )); 41 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); 42 | curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); 43 | // Change service account before production 44 | curl_setopt($curl, CURLOPT_USERPWD, "apiuser:apipass"); 45 | curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); 46 | 47 | // EXECUTE: 48 | $result = curl_exec($curl); 49 | if(!$result){die("Connection Failure");} 50 | curl_close($curl); 51 | return $result; 52 | } 53 | 54 | if (isset($_POST['submit'])) { 55 | 56 | $siteName = $_POST['jamf_script_name']; 57 | //$fileName = $_FILES['jamf_script']['name']; 58 | //$fileSize = $_FILES['jamf_script']['size']; 59 | $fileTmpName = $_FILES['jamf_script']['tmp_name']; 60 | //$fileType = $_FILES['jamf_script']['type']; 61 | 62 | if (empty($siteName)) { 63 | $errors[] = "No Site was selected. Please select a site and try again"; 64 | } 65 | 66 | //$fileExtension = strtolower(end(explode('.',$fileName))); 67 | //if (! in_array($fileExtension, $fileExtensions)) { 68 | // $errors[] = "This file extension is not allowed. Please upload a valid file type (.sh, .py)."; 69 | //} 70 | 71 | // This limits the size of the upload to 2.5 MB via bytes 72 | //if ($fileSize > 2500000) { 73 | // $errors[] = "This file is more than 2.5 MB limit. Please adjust the size or contact helpdesk@it.com for help."; 74 | //} 75 | 76 | if (empty($errors)) { 77 | $scriptName = $siteName . "-" . basename($siteName); 78 | 79 | $uploadPath = $currentDir . $uploadDirectory . $scriptName; 80 | $didUpload = move_uploaded_file($fileTmpName, $uploadPath); 81 | $scriptContent = file_get_contents($uploadPath); 82 | $siteURL = urlencode($siteName); 83 | $updateXML = ""; 84 | 85 | 86 | if ($didUpload) { 87 | //echo "The file " . $siteName . "-" . basename($fileName) . " has been uploaded and is available for use in the Jamf Pro Server.\r\n"; 88 | $get_data = callAPI('PUT', 'https://jamfurl/JSSResource/scripts/name/' . $siteURL , $updateXML); 89 | header('Refresh:0; url=index.html'); 90 | echo ""; 91 | } else { 92 | $errors[] = "An error occurred with the upload process. Try again or contact helpdesk@it.com for help."; 93 | } 94 | } 95 | 96 | if (!empty($errors)) { 97 | foreach ($errors as $error) { 98 | echo $error . "\n"; 99 | echo "Try Again"; 100 | } 101 | } 102 | } 103 | 104 | ?> 105 | -------------------------------------------------------------------------------- /PHP_Pages/profileupload.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 5 | // November 2018 6 | // GitHub: github.com/therealmacjeezy 7 | // JamfNation: therealmacjeezy 8 | 9 | $errors = []; // Store all foreseen and unforseen errors here 10 | // File Extensions go here. 11 | $fileExtensions = ['xml', 'mobileconfig']; 12 | $uploadDirectory = "/Profiles/"; 13 | $currentDir = getcwd(); 14 | 15 | libxml_use_internal_errors(true); 16 | 17 | function callAPI($method, $url, $data){ 18 | $curl = curl_init(); 19 | 20 | switch ($method){ 21 | case "POST": 22 | curl_setopt($curl, CURLOPT_POST, 1); 23 | if ($data) 24 | curl_setopt($curl, CURLOPT_POSTFIELDS, $data); 25 | break; 26 | case "PUT": 27 | curl_setopt($curl, CURLOPT_CUSTOMREQUEST, "PUT"); 28 | if ($data) 29 | curl_setopt($curl, CURLOPT_POSTFIELDS, $data); 30 | break; 31 | default: 32 | if ($data) 33 | $url = sprintf("%s?%s", $url, http_build_query($data)); 34 | } 35 | 36 | // OPTIONS: 37 | curl_setopt($curl, CURLOPT_URL, $url); 38 | curl_setopt($curl, CURLOPT_HTTPHEADER, array( 39 | #'APIKEY: 111111111111111111111', 40 | 'Accept: application/xml', 41 | 'Content-Type: application/xml', 42 | )); 43 | curl_setopt($curl, CURLOPT_RETURNTRANSFER, 1); 44 | curl_setopt($curl, CURLOPT_HTTPAUTH, CURLAUTH_BASIC); 45 | // Change service account before production 46 | curl_setopt($curl, CURLOPT_USERPWD, "apiuser:apipass"); 47 | curl_setopt($curl, CURLOPT_SSL_VERIFYPEER, false); 48 | 49 | // EXECUTE: 50 | $result = curl_exec($curl); 51 | if(!$result){die("Connection Failure");} 52 | curl_close($curl); 53 | return $result; 54 | } 55 | 56 | if (isset($_POST['submit'])) { 57 | 58 | $siteName = $_POST['jamf_site']; 59 | $fileName = $_FILES['jamf_script']['name']; 60 | $fileInfo = pathinfo($fileName); 61 | $fileSize = $_FILES['jamf_script']['size']; 62 | $fileTmpName = $_FILES['jamf_script']['tmp_name']; 63 | $fileType = $_FILES['jamf_script']['type']; 64 | 65 | if (empty($siteName)) { 66 | $errors[] = "No Site was selected. Please select a site and try again"; 67 | } 68 | 69 | $fileExtension = $fileInfo['extension']; 70 | if (! in_array($fileExtension, $fileExtensions)) { 71 | $errors[] = "This file extension is not allowed. Please upload a valid file type (.xml, .mobileconfig)."; 72 | } 73 | 74 | // This limits the size of the upload to 2.5 MB via bytes 75 | //if ($fileSize > 2500000) { 76 | // $errors[] = "This file is more than 2.5 MB limit. Please adjust the size or contact agcy-mosm@mail.nasa.gov for help."; 77 | //} 78 | 79 | if (empty($errors)) { 80 | $scriptName = $siteName . "-" . $fileInfo['filename']; 81 | 82 | $uploadPath = $currentDir . $uploadDirectory . $scriptName; 83 | $didUpload = move_uploaded_file($fileTmpName, $uploadPath); 84 | $scriptContent = file_get_contents($uploadPath); 85 | $filterContent = htmlentities($scriptContent); 86 | $updateXML = <<< XML 87 | 88 | 89 | $scriptName 90 | 91 | $siteName 92 | 93 | 94 | Uploads 95 | 96 | $filterContent 97 | 98 | 99 | XML; 100 | 101 | 102 | if ($didUpload) { 103 | //echo $updateXML; 104 | //echo "The file " . $siteName . "-" . basename($fileName) . " has been uploaded and is available for use in the Jamf Pro Server.\r\n"; 105 | $get_data = callAPI('POST', 'https://your.jamf.pro/JSSResource/osxconfigurationprofiles/id/0', $updateXML); 106 | preg_match('/\([0-9]+)\Click Here To Edit The Profile in a new tab"; 109 | echo "
"; 110 | echo "Click Here to Go Back."; 111 | } else { 112 | $errors[] = "An error occurred with the upload process. Try again or contact agcy-mosm@mail.nasa.gov for help."; 113 | } 114 | } 115 | 116 | if (!empty($errors)) { 117 | foreach ($errors as $error) { 118 | echo $error . "\n"; 119 | echo "Try Again"; 120 | } 121 | } 122 | } 123 | 124 | ?> 125 | 126 | -------------------------------------------------------------------------------- /HTML_Pages/pkgupload.html: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | Jamf Pro File Upload - Packages 11 | 12 | 66 | 67 | 68 |

Upload Package

69 |

This page was created to make uploading packages to Jamf Pro easier and more reliable.
Please select your site from the drop down menu below then select the package you want to upload.

70 | 71 |

**All fields are required**

72 |
73 | Package Requirements:
74 | File Size Limit: 6GB | Accepted File Types: .pkg, .mpkg, .dmg | Package Upload Limit: 5

75 |
76 | Select jamf Site: 77 |
80 | 81 | Select Category (Optional - Defaults to Uploads): 82 | 85 | 86 |
138 | 139 | 140 | 141 | 142 | 143 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Note: 2 | **11/15/19** 3 | 4 | Version 2 is currently being sanitized. Once I am finished, I'll be uploading it and creating version 1 and version 2 directories. Version 2 is the one that was mentioned at JNUC 2019 during the **Cats in Space** presentation. Version 2 will contain the following changes: 5 | - Upgraded UI 6 | - Built in Script Editor 7 | - Option for Site Level Selection via drop down menu 8 | - New Package Options (Delete / Download) 9 | - New Script Options (Edit / Create / Download) 10 | - Upload Chunking 11 | - Removes the Package Upload Size Limit 12 | 13 | 14 | # Jamf Pro Upload Page Version 1 15 | 16 | Josh Harvey | Nov 2018 17 | Updated: Jan 2019 18 | 19 | josh@macjeezy.com 20 | 21 | ## **[Jamf Pro Upload Page Example](http://macjeezy.com/JamfProUploads/)** 22 | 23 | 24 | Table of Contents 25 | ================= 26 | - [Overview](#overview) 27 | - [Features](#features) 28 | - [Packages](#packages) 29 | - [Profiles](#profiles) 30 | - [Scripts](#scripts) 31 | - [Install/Setup](#setup) 32 | - [Troubleshooting](#troubleshooting) 33 | 34 | ## Overview 35 | 36 | This page was created to allow jamf admins the access they need to upload packages and scripts to the JSS, without having to give them full access to Jamf Admin. 37 | 38 | Besides being able to upload new scripts, packages and profiles, your admins will also have the ability to download any script that is being used in your JSS *(With the option to add a filter if needed)*, make any edits to that script, then reupload it to the JSS. This allows for any changes to be made on the most up to date version. 39 | 40 | 41 | ## Features 42 | *(and things I am really proud of)* 43 | * **Up to date Site Lists** *(Site Selection Dropdowns)* 44 | - Updated each time the page is reloaded. Uses an API call to create an xml file with all the sites *(getsites.php)*. Then the html page uses jquery to parse the xml *(SiteList.xml)* and loads them as options in the dropdown menu. 45 | * **Up to date Script List** *(Script Selection Dropdown - Edit Existing Script Option)* 46 | - Updated each time the page is reloaded. Uses an API call to create an xml file with all of the scripts in the JSS *(getscripts.php)*. Then the html page uses jquery to parse the xml *(ScriptList.xml)* and loads them as options in the dropdown menu. You also have the ability to filter the list of scripts based off its naming prefix. This can keep your "Full Jamf Pro" scripts from being downloaded and edited, and only allow sites to edit their own scripts. 47 | * **Configuration Profile Uploads** *(Unsigned Profiles Only currently)* 48 | - This section is a workaround for a open PI with jamf and allows for your admins to upload *unsigned* configuration profiles to their site. 49 | 50 | 51 | ![Jamf Pro Upload Form Home Page](/images/example2.png) 52 | 53 | **Example of the main page** 54 | 55 | 56 | 57 | ### Packages 58 | **Options** | **Info** 59 | ------------ | ------------- 60 | **File Size Limit** | 6GB 61 | **Allowed File Types** | .pkg, .mpkg, .dmg 62 | 63 | **Uploading a Package** 64 | 65 | Select the site you manage via the dropdown menu. Once a site is selected, add the package you want to upload then select the "Upload" button. Once the package has been uploaded successfully, an API call will be made to update the JSS and a javascript message will appear informing you of the successful upload and that it is ready to use. 66 | 67 | Your new package will appear in the JSS under the Uploads category and use the following naming convention 68 | 69 | sitename - packagename.pkg 70 | 71 | ### Profiles 72 | **Options** | **Info** 73 | ------------ | ------------- 74 | **File Size Limit** | N/A 75 | **Allowed File Types** | .mobileconfig 76 | 77 | **Uploading a Profile** 78 | 79 | Select the site you manage via the dropdown menu. Once a site is selected, add the profile you want to upload then select the "Upload" button. Once the profile has been uploaded successfully, an API call will be made to update the JSS and you will be provided with two links. One to open the profile in a new tab and the other to go back to the Upload Form Home. 80 | 81 | Your new profile will appear in the JSS under the Uploads category and use the following naming convention 82 | 83 | sitename - name.of.profile 84 | 85 | ### Scripts 86 | **Options** | **Info** 87 | ------------ | ------------- 88 | **File Size Limit** | 2MB 89 | **Allowed File Types** | .sh, .py 90 | 91 | **Creating a new script** 92 | 93 | Select "Create New Script" from the Scripts Dropdown. Once the page has loaded, select the site you manage from the dropdown menu and add the script you are uploading to the JSS. When ready, select the "Upload" button and the script will be uploaded to the DP and an API call will then be ran to create the script in the database. Once the script has been successfully created, a javascript message will appear informing you that the script is ready for use. 94 | 95 | Your new script will appear in the JSS under the Uploads category and use the following naming convention 96 | 97 | sitename - scriptname 98 | 99 | **Editing an exiting script** 100 | 101 | Select "Edit Exitising Script" from the Script Dropdown. Once the page has loaded, you will see a dropdown menu of all of the scripts that currently exist in the JSS. Select the script you want to edit/replace. Once you have selected the script, you have the option to download it. This allows for you to make changes on the most current and updated version of that script. After you have completed editing your script, double check the dropdown menu still has the name of the script you are editing selected. If so, use the "Choose File" button to add your script and select the "Upload" button. Once the script has been successfully uploaded, a javascript message will appear informing you that the script is ready for use. 102 | 103 | Your edited script will appear in the JSS under the Uploads category and use the previous name 104 | 105 | 106 | 107 | ### Setup 108 | 109 | * **IIS Only** 110 | * For packages larger then 30MB, you will need to change the Request Limits inside of IIS to a value higher then the largest package you think will be used. The default setting is 30MB and the value is read as Bytes *(eg: 3000000000 Bytes = 3 Gigabytes)* [Request Limits Documentation](https://docs.microsoft.com/en-us/iis/configuration/system.webserver/security/requestfiltering/requestlimits/) 111 | 112 | * **Requirements** 113 | * Webserver with PHP installed 114 | * [Install PHP on IIS](https://docs.microsoft.com/en-us/iis/application-frameworks/scenario-build-a-php-website-on-iis/configuring-step-1-install-iis-and-php) 115 | * [Install PHP on CentOS](https://www.tecmint.com/install-php-7-in-centos-7/) 116 | * API Access to your Jamf Pro Instance 117 | 118 | 119 | **Setup Steps** 120 | 1. Copy the files to your webserver into the same directory. If you choose to use different directories *(eg: /PHP/getscripts.php, /images/tuxlogo.png, etc..)* be sure to update the source paths for each of the files that have been moved. 121 | * **PHP Pages** */JamfProUploadPage/PHP_Pages/* 122 | * createscript.php 123 | * getscript.php 124 | * getsites.php 125 | * getcategories.php 126 | * pkgupload.php 127 | * profileupload.php 128 | * scriptdownload.php 129 | * scriptupload.php 130 | * **HTML Pages** */JamfProUploadPage/HTML_Pages/* 131 | * index.html 132 | * home.html 133 | * newscript.html 134 | * pkgupload.html 135 | * profileupload.html 136 | * scriptupload.html 137 | * **Images** */JamfProUploadPage/images/* 138 | * tuxlogo.png *(You can use any icon, just be sure to change the reference in the index.html)* 139 | * **CSS** */JamfProUploadPage/* 140 | * uploadstylesheet.css 141 | 2. Create the following directories on your webserver *(Note: Be sure to change the Packages path in pkguploads.php to the path of the DP you use with Jamf Pro)* 142 | * Packages *(Used for testing the upload process)* 143 | * Scripts 144 | * Profiles 145 | 3. Modify the following files and information to match your Jamf Pro setup *(Note: You may also want to change the text of the error messages and contact email that gets displayed also. The fields mentioned below are only the required changes needed to make the scripts function)* 146 | * **createscript.php** 147 | 148 | `$uploadDirectory = "/Scripts/";` 149 | 150 | `curl_setopt($curl, CURLOPT_USERPWD, "apiuser:apipass");` 151 | 152 | `$get_data = callAPI('POST', 'https://jamfurl/JSSResource/scripts/id/0', $updateXML);` 153 | * **profileupload.php** 154 | 155 | `$uploadDirectory = "/Profile/";` 156 | 157 | `curl_setopt($curl, CURLOPT_USERPWD, "apiuser:apipass");` 158 | 159 | `$get_data = callAPI('POST', 'https://jamfurl/JSSResource/osxconfigurationprofiles/id/0', $updateXML);` 160 | 161 | `echo "Click Here To Edit The Profile in a new tab";` 162 | * **getscript.php** 163 | 164 | `curl_setopt($curl, CURLOPT_USERPWD, "apiuser:apipass");` 165 | 166 | `$get_data = callAPI('GET', 'https://jamfurl/JSSResource/scripts', false);` 167 | * **getsites.php** 168 | 169 | `curl_setopt($curl, CURLOPT_USERPWD, "apiuser:apipass");` 170 | 171 | `$get_data = callAPI('GET', 'https://jamfurl/JSSResource/sites', false);` 172 | * **pkgupload.php** 173 | 174 | `$uploadDirectory = "/Path/To/Packages/";` 175 | 176 | `curl_setopt($curl, CURLOPT_USERPWD, "apiuser:apipass");` 177 | 178 | `$get_data = callAPI('POST', 'https://jamfurl/JSSResource/packages/id/0', $updateXML);` 179 | * **scriptdownload.php** 180 | 181 | `curl_setopt($curl, CURLOPT_USERPWD, "apiuser:apipass");` 182 | 183 | `$get_data = callAPI('GET', 'https://jamfurl/JSSResource/scripts/name/' . $siteURL , false);` 184 | * **scriptupload.php** 185 | 186 | `$uploadDirectory = "/Scripts/";` 187 | 188 | `curl_setopt($curl, CURLOPT_USERPWD, "apiuser:apipass");` 189 | 190 | `$get_data = callAPI('PUT', 'https://jamfurl/JSSResource/scripts/name/' . $siteURL , $updateXML);` 191 | * **index.html** 192 | * Customize the title, header and footer to display your information 193 | * **scriptupload.html** 194 | * Lines 25 - 28 195 | *Uncomment these lines and change the 'keyword' to match a naming prefix you use with scripts to filter and "restrict" them from being editing/replaced.* 196 | 4. Modify the php.ini file to allow file uploads by changing the following values to fit your environment 197 | * **post_max_size** [PHP Manual](http://php.net/post-max-size) 198 | * **upload_max_filesize** [PHP Manual](http://php.net/upload-max-filesize) 199 | * **memory_limit** [PHP Manual](http://php.net/memory-limit) 200 | 5. Once the above files have been copied over and modified to fit your environment, your upload page should be up and running. Just use the FQDN of your webserver and add the directory you saved the files in (eg: https://yourwebserver.com/JamfUploads/) 201 | 202 | ### Troubleshooting 203 | * **Dropdown menus stuck at "loading"** 204 | * This is the default text used for the dropdown menus. If you see this, verify the SiteList.xml or ScriptList.xml exist in the same directory as the page is in. If missing, verify the settings in the php page creating the xml file are correct. 205 | 206 | * **404 Error Page with larger uploads** 207 | * This error usually occurs when the Request Limit header is set to a smaller value then the package you are trying to upload. To verify this is the cause of the issue, look in your webserver logs and find the substatus of the error. If it is 404.13, changing the Request Limit header will resolve this issue. 208 | 209 | * **Configuration Profile gets created without a payload** 210 | * This will occur if the Configuration Profile was uploaded as a SIGNED profile. Verify the Configuration Profile is UNSIGNED and try the upload again. This will also occur if you are uploading a PPPC profile to a jamf instance older then 10.9. 211 | --------------------------------------------------------------------------------