├── .gitignore ├── 1AF1BD94-9430-4FA7-911C-2123C29C8B63.png ├── 5D87F5EF-371D-4C05-A410-30A647BD83EA.png ├── 66C1B8E8-FFF4-4200-BCFF-7B45F7B8F5B8.png ├── LICENSE ├── Packal Updater.alfredworkflow ├── Readme.md ├── alfred.bundler.php ├── alfred.bundler.sh ├── assets ├── alfred.cron.script.php └── icons │ ├── applications-education-miscellaneous.png │ ├── applications-system.png │ ├── chronometer.png │ ├── code-context.png │ ├── dialog-cancel.png │ ├── dialog-close.png │ ├── dialog-ok-apply.png │ ├── dialog-ok.png │ ├── document-revert.png │ ├── download-later.png │ ├── edit-delete.png │ ├── flag-black.png │ ├── flag-blue.png │ ├── flag-green.png │ ├── flag-red.png │ ├── flag-yellow.png │ ├── flag.png │ ├── get-hot-new-stuff.png │ ├── help-about.png │ ├── im-ban-kick-user.png │ ├── im-ban-user.png │ ├── im-invisible-user.png │ ├── im-kick-user.png │ ├── im-user-busy.png │ ├── im-user-offline.png │ ├── im-user.png │ ├── list-add-user.png │ ├── list-add.png │ ├── list-remove-user.png │ ├── list-resource-add.png │ ├── mail-mark-notjunk.png │ ├── meeting-chair.png │ ├── package.png │ ├── player-time.png │ ├── process-stop.png │ ├── run-build-configure.png │ ├── run-build-install-root.png │ ├── run-build-install.png │ ├── run-build.png │ ├── security-high.png │ ├── security-low.png │ ├── security-medium.png │ ├── svn-commit.png │ ├── system-help.png │ ├── system-suspend-hibernate.png │ ├── task-accepted.png │ ├── task-attempt.png │ ├── task-attention.png │ ├── task-complete.png │ ├── task-delegate.png │ ├── task-ongoing.png │ ├── task-reject.png │ ├── user-away.png │ ├── user-busy.png │ ├── user-invisible.png │ ├── user-offline.png │ ├── user-online.png │ ├── user-properties.png │ ├── view-media-artist.png │ ├── view-web-browser-dom-tree.png │ └── window-close.png ├── check-and-kill-webserver.sh ├── cli ├── includes │ ├── plist-functions.php │ └── plist-migration.php ├── packal.php └── packal.sh ├── config.php ├── favicon.ico ├── functions.php ├── gui ├── assets │ ├── css │ │ ├── font-awesome.min.css │ │ ├── normalize.css │ │ └── style.css │ ├── fonts │ │ ├── Montserrat │ │ │ ├── IQHow_FEYlDC4Gzy_m8fcnbFhgvWbfSbdVg11QabG8w.woff │ │ │ ├── montserrat.css │ │ │ └── zhcz-_WihjSQC0oHJ9TCYL3hpw3pgy2gAi-Ip7WPMi0.woff │ │ ├── Source Sans Pro │ │ │ ├── M2Jd71oPJhLKp0zdtTvoM_bce-VcyLJMRR1FC9qWbU8.woff │ │ │ ├── ODelI1aHBYDBqgeIAH2zlNHq-FFgoDNV3GTKpHwuvtI.woff │ │ │ ├── fpTVHK8qsXbIeTHTrnQH6H7XMO6S-yJpg1torQFmnv33rGVtsTkPsbDajuO5ueQw.woff │ │ │ ├── fpTVHK8qsXbIeTHTrnQH6J4LxuTxC3JbLqy8IUt0K1T3rGVtsTkPsbDajuO5ueQw.woff │ │ │ ├── fpTVHK8qsXbIeTHTrnQH6MJvEBP8Mx4XcuTrPnlPwTr3rGVtsTkPsbDajuO5ueQw.woff │ │ │ ├── fpTVHK8qsXbIeTHTrnQH6OE3EfhCMemg4BxrY2mmJD_3rGVtsTkPsbDajuO5ueQw.woff │ │ │ ├── fpTVHK8qsXbIeTHTrnQH6PgYMAt3u4NmhhzLLLPJ5qH3rGVtsTkPsbDajuO5ueQw.woff │ │ │ ├── source.sans.pro.css │ │ │ ├── toadOcfmlt9b38dHJxOBGC-IiwWNzQT_LXAtr-Ee9Pw.woff │ │ │ ├── toadOcfmlt9b38dHJxOBGC7eGk4PJkA83J1AXCmgUfc.woff │ │ │ ├── toadOcfmlt9b38dHJxOBGCmgpmuQqK2I-L2S9cF65Ek.woff │ │ │ ├── toadOcfmlt9b38dHJxOBGIqjGYJUyOXcBwUQbRaNH6c.woff │ │ │ └── toadOcfmlt9b38dHJxOBGMVNtom4QlEDNJaqqqzqdSs.woff │ │ ├── fontawesome-webfont.woff │ │ └── fonts.css │ ├── images │ │ ├── circle_down.svg │ │ ├── circle_ok.svg │ │ ├── cloud_fail.svg │ │ ├── cloud_ok.svg │ │ ├── cog.svg │ │ ├── package.png │ │ ├── packal-man.jpg │ │ ├── paypal.gif │ │ ├── preloader.gif │ │ ├── refresh.svg │ │ ├── settings.svg │ │ ├── user-no.svg │ │ ├── user-yes.svg │ │ └── warning.svg │ └── js │ │ ├── borderMenu.js │ │ ├── classie.js │ │ ├── jquery-ui │ │ └── js │ │ │ └── jquery-ui.min.js │ │ ├── jquery.min.js │ │ └── keep-alive.js ├── index.php ├── packal.php ├── serve_image.php └── webserver-keep-alive-update.php ├── icon.png ├── info.plist ├── init.php ├── install-alfred-cron-script.sh ├── libraries └── workflows.php ├── notes.txt ├── packal-controller-script.php ├── report-usage-data.php ├── screenshots ├── gui.png ├── packal-blacklist.png ├── packal-setup.png ├── packal-start.png ├── packal-update-no-internet.png ├── packal-update.png ├── packal-updater-alfred.gif ├── packal-updater-gui.gif └── reporting-unique-id.png ├── script-set-option.php ├── script.php └── workflow └── Packal Updater.alfredworkflow /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | packal 3 | packal/* 4 | -------------------------------------------------------------------------------- /1AF1BD94-9430-4FA7-911C-2123C29C8B63.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/1AF1BD94-9430-4FA7-911C-2123C29C8B63.png -------------------------------------------------------------------------------- /5D87F5EF-371D-4C05-A410-30A647BD83EA.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/5D87F5EF-371D-4C05-A410-30A647BD83EA.png -------------------------------------------------------------------------------- /66C1B8E8-FFF4-4200-BCFF-7B45F7B8F5B8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/66C1B8E8-FFF4-4200-BCFF-7B45F7B8F5B8.png -------------------------------------------------------------------------------- /Packal Updater.alfredworkflow: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/Packal Updater.alfredworkflow -------------------------------------------------------------------------------- /Readme.md: -------------------------------------------------------------------------------- 1 | Packal Update Workflow 2 | ==== 3 | 4 | By Shawn Patrick Rice 5 | 6 | This Alfred 2 workflow simply keeps your other Alfred 2/3 workflows that you've downloaded from http://www.packal.org up to date. -------------------------------------------------------------------------------- /alfred.bundler.php: -------------------------------------------------------------------------------- 1 | /dev/null 2>&1"; 27 | exec( $cmd ); 28 | 29 | /** 30 | * This is the only function the workflow author needs to invoke. 31 | * If the asset to be loaded is a PHP library, then you just need to call the function, 32 | * and the files will be required automatically. 33 | * 34 | * If you are loading a "utility" application, then the function will return the full 35 | * path to the function so that you can invoke it. 36 | * 37 | * If you are passing your own json, then include it as a file path. 38 | * 39 | **/ 40 | function __load( $name, $version = 'default', $type = 'php', $json = '' ) { 41 | if ( file_exists( 'info.plist' ) ) { 42 | // Grab the bundle ID from the plist file. 43 | $bundle = exec( "/usr/libexec/PlistBuddy -c 'print :bundleid' 'info.plist'" ); 44 | } elseif ( file_exists( '../info.plist' ) ) { 45 | $bundle = exec( "/usr/libexec/PlistBuddy -c 'print :bundleid' '../info.plist'" ); 46 | } else { 47 | $bundle = ''; 48 | } 49 | 50 | if ( $type == 'php' ) { 51 | $assets = __loadAsset( $name , $version , $bundle , strtolower( $type ) , $json ); 52 | foreach ( $assets as $asset ) { 53 | require_once( $asset ); 54 | } 55 | return true; 56 | } elseif ( $type == 'utility' ) { 57 | $asset = __loadAsset( $name , $version , $bundle , strtolower( $type ) , $json ); 58 | return str_replace( ' ' , '\ ' , $asset[0] ); 59 | } else { 60 | return __loadAsset( $name , $version , $bundle , strtolower( $type ) , $json ); 61 | } 62 | 63 | // We shouldn't get here. 64 | return false; 65 | 66 | } // End __load() 67 | 68 | /** 69 | * Installs the Alfred Bundler utility. 70 | **/ 71 | function __installBundler() { 72 | // Install the Alfred Bundler 73 | 74 | global $bundler_version, $__data; 75 | echo 'here'; 76 | $installer = "https://raw.githubusercontent.com/shawnrice/alfred-bundler/$bundler_version/meta/installer.sh"; 77 | $__cache = exec( 'echo $HOME' ) . "/Library/Caches/com.runningwithcrayons.Alfred-2/Workflow Data/alfred.bundler-$bundler_version"; 78 | 79 | // Make the directories 80 | if ( ! file_exists( $__cache ) ) { 81 | mkdir( $__cache, 0775, true ); 82 | } 83 | if ( ! file_exists( "$__cache/installer" ) ) { 84 | mkdir( "$__cache/installer", 0775, true ); 85 | } 86 | // Download the installer 87 | // I'm throwing in the second bash command to delay the execution of the next 88 | // exec() command. I'm not sure if that's necessary. 89 | exec( "curl -sL '$installer' > '$__cache/installer/installer.sh'" ); 90 | // Run the installer 91 | exec( "sh '$__cache/installer/installer.sh'" ); 92 | 93 | } // End __installBundler() 94 | -------------------------------------------------------------------------------- /alfred.bundler.sh: -------------------------------------------------------------------------------- 1 | #!/bin/sh 2 | 3 | # Main Bash interface for the Alfred Dependency Bundler. This file should be 4 | # the only one from the bundler that is distributed with your workflow. 5 | # 6 | # See documentation on how to use: http://shawnrice.github.io/alfred-bundler/ 7 | # 8 | # License: GPLv3 9 | 10 | 11 | # Define the global bundler version. 12 | bundler_version="aries"; 13 | __data="$HOME/Library/Application Support/Alfred 2/Workflow Data/alfred.bundler-$bundler_version" 14 | __cache="$HOME/Library/Caches/com.runningwithcrayons.Alfred-2/Workflow Data/alfred.bundler-$bundler_version" 15 | 16 | function __load { 17 | # $1 -- asset name 18 | # $2 -- version 19 | # $3 -- type 20 | # $4 -- json -- this is a file path 21 | 22 | if [ -z "$1" ]; then 23 | echo "You need to pass at minimum one argument to use the __load function." 24 | exit 1 25 | fi 26 | 27 | local name="$1" 28 | local version="$2" 29 | local type="$3" 30 | local json="$4" 31 | 32 | if [ -z $version ]; then 33 | version="default" 34 | fi 35 | if [ -z $type ]; then 36 | type="bash" 37 | fi 38 | 39 | # Grab the bundle id. 40 | if [ -f 'info.plist' ]; then 41 | local bundle=`/usr/libexec/PlistBuddy -c 'print :bundleid' 'info.plist'` 42 | elif [ -f '../info.plist' ]; then 43 | local bundle=`/usr/libexec/PlistBuddy -c 'print :bundleid' 'info.plist'` 44 | else 45 | local bundle='..' 46 | fi 47 | 48 | __asset=`__loadAsset "$name" "$version" "$bundle" "$type" "$json"` 49 | status=$? 50 | echo "$__asset" 51 | exit "$status" 52 | } 53 | 54 | # This just downloads the install script and starts it up. 55 | function __installBundler { 56 | local installer="https://raw.githubusercontent.com/shawnrice/alfred-bundler/$bundler_version/meta/installer.sh" 57 | dir "$__cache" 58 | dir "$__cache/installer" 59 | dir "$__data" 60 | curl -sL "$installer" > "$__cache/installer/installer.sh" 61 | sh "$__cache/installer/installer.sh" 62 | } 63 | 64 | # Just a helper function to make a directory if it doesn't exist. 65 | function dir { 66 | if [ ! -d "$1" ]; then 67 | mkdir -p "$1" 68 | fi 69 | } 70 | 71 | if [ ! -f "$__data/bundler.sh" ]; then 72 | __installBundler 73 | fi 74 | 75 | # Include the bundler. 76 | . "$__data/bundler.sh" 77 | 78 | # Check for updates. 79 | sh "$__data/meta/update.sh" > /dev/null 2>&1 80 | 81 | 82 | # exit $? -------------------------------------------------------------------------------- /assets/alfred.cron.script.php: -------------------------------------------------------------------------------- 1 | workflowReporting ) && ( $config->workflowReporting == 1 ) ) 34 | exec( "php '" . $endpoints[ 'com.packal' ] . "/report-usage-data.php'" ); 35 | 36 | 37 | // Grab manifest information. 38 | foreach ( $manifest as $m ) : 39 | $manifestBundles[] = $m->bundle; 40 | $wf[ "$m->bundle" ][ 'name' ] = $m->name; 41 | $wf[ "$m->bundle" ][ 'author' ] = $m->author; 42 | $wf[ "$m->bundle" ][ 'version' ] = $m->version; 43 | endforeach; 44 | 45 | $updates = false; 46 | foreach ( $endpoints as $k => $v ) : 47 | if ( file_exists( "$v/packal/package.xml" ) ) { 48 | $w = simplexml_load_file( "$v/packal/package.xml" ); 49 | 50 | if ( in_array( $k, $manifestBundles ) ) { 51 | if ( $wf[ "$k" ][ 'version' ] != (string) $w->version ) { 52 | if ( ! in_array( $k, $blacklist ) ) { 53 | $updates = true; 54 | break; 55 | } 56 | } 57 | } 58 | } 59 | endforeach; 60 | 61 | if ( $updates ) { 62 | require_once( $endpoints[ 'com.packal'] . '/alfred.bundler.php' ); 63 | $tn = __load( 'terminal-notifier' , 'default' , 'utility' ); 64 | $cmd = 'osascript -e "tell application \"Alfred 2\" to run trigger \"com.packal.start\" in workflow \"com.packal\" with argument \"updates\""'; 65 | exec( "$tn -title 'Packal Updater' -message 'You have updates for your Alfred 2 workflows available.' -group 'packal-updater' -execute '$cmd'" ); 66 | } -------------------------------------------------------------------------------- /assets/icons/applications-education-miscellaneous.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/applications-education-miscellaneous.png -------------------------------------------------------------------------------- /assets/icons/applications-system.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/applications-system.png -------------------------------------------------------------------------------- /assets/icons/chronometer.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/chronometer.png -------------------------------------------------------------------------------- /assets/icons/code-context.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/code-context.png -------------------------------------------------------------------------------- /assets/icons/dialog-cancel.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/dialog-cancel.png -------------------------------------------------------------------------------- /assets/icons/dialog-close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/dialog-close.png -------------------------------------------------------------------------------- /assets/icons/dialog-ok-apply.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/dialog-ok-apply.png -------------------------------------------------------------------------------- /assets/icons/dialog-ok.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/dialog-ok.png -------------------------------------------------------------------------------- /assets/icons/document-revert.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/document-revert.png -------------------------------------------------------------------------------- /assets/icons/download-later.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/download-later.png -------------------------------------------------------------------------------- /assets/icons/edit-delete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/edit-delete.png -------------------------------------------------------------------------------- /assets/icons/flag-black.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/flag-black.png -------------------------------------------------------------------------------- /assets/icons/flag-blue.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/flag-blue.png -------------------------------------------------------------------------------- /assets/icons/flag-green.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/flag-green.png -------------------------------------------------------------------------------- /assets/icons/flag-red.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/flag-red.png -------------------------------------------------------------------------------- /assets/icons/flag-yellow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/flag-yellow.png -------------------------------------------------------------------------------- /assets/icons/flag.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/flag.png -------------------------------------------------------------------------------- /assets/icons/get-hot-new-stuff.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/get-hot-new-stuff.png -------------------------------------------------------------------------------- /assets/icons/help-about.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/help-about.png -------------------------------------------------------------------------------- /assets/icons/im-ban-kick-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/im-ban-kick-user.png -------------------------------------------------------------------------------- /assets/icons/im-ban-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/im-ban-user.png -------------------------------------------------------------------------------- /assets/icons/im-invisible-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/im-invisible-user.png -------------------------------------------------------------------------------- /assets/icons/im-kick-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/im-kick-user.png -------------------------------------------------------------------------------- /assets/icons/im-user-busy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/im-user-busy.png -------------------------------------------------------------------------------- /assets/icons/im-user-offline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/im-user-offline.png -------------------------------------------------------------------------------- /assets/icons/im-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/im-user.png -------------------------------------------------------------------------------- /assets/icons/list-add-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/list-add-user.png -------------------------------------------------------------------------------- /assets/icons/list-add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/list-add.png -------------------------------------------------------------------------------- /assets/icons/list-remove-user.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/list-remove-user.png -------------------------------------------------------------------------------- /assets/icons/list-resource-add.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/list-resource-add.png -------------------------------------------------------------------------------- /assets/icons/mail-mark-notjunk.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/mail-mark-notjunk.png -------------------------------------------------------------------------------- /assets/icons/meeting-chair.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/meeting-chair.png -------------------------------------------------------------------------------- /assets/icons/package.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/package.png -------------------------------------------------------------------------------- /assets/icons/player-time.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/player-time.png -------------------------------------------------------------------------------- /assets/icons/process-stop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/process-stop.png -------------------------------------------------------------------------------- /assets/icons/run-build-configure.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/run-build-configure.png -------------------------------------------------------------------------------- /assets/icons/run-build-install-root.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/run-build-install-root.png -------------------------------------------------------------------------------- /assets/icons/run-build-install.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/run-build-install.png -------------------------------------------------------------------------------- /assets/icons/run-build.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/run-build.png -------------------------------------------------------------------------------- /assets/icons/security-high.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/security-high.png -------------------------------------------------------------------------------- /assets/icons/security-low.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/security-low.png -------------------------------------------------------------------------------- /assets/icons/security-medium.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/security-medium.png -------------------------------------------------------------------------------- /assets/icons/svn-commit.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/svn-commit.png -------------------------------------------------------------------------------- /assets/icons/system-help.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/system-help.png -------------------------------------------------------------------------------- /assets/icons/system-suspend-hibernate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/system-suspend-hibernate.png -------------------------------------------------------------------------------- /assets/icons/task-accepted.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/task-accepted.png -------------------------------------------------------------------------------- /assets/icons/task-attempt.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/task-attempt.png -------------------------------------------------------------------------------- /assets/icons/task-attention.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/task-attention.png -------------------------------------------------------------------------------- /assets/icons/task-complete.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/task-complete.png -------------------------------------------------------------------------------- /assets/icons/task-delegate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/task-delegate.png -------------------------------------------------------------------------------- /assets/icons/task-ongoing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/task-ongoing.png -------------------------------------------------------------------------------- /assets/icons/task-reject.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/task-reject.png -------------------------------------------------------------------------------- /assets/icons/user-away.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/user-away.png -------------------------------------------------------------------------------- /assets/icons/user-busy.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/user-busy.png -------------------------------------------------------------------------------- /assets/icons/user-invisible.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/user-invisible.png -------------------------------------------------------------------------------- /assets/icons/user-offline.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/user-offline.png -------------------------------------------------------------------------------- /assets/icons/user-online.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/user-online.png -------------------------------------------------------------------------------- /assets/icons/user-properties.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/user-properties.png -------------------------------------------------------------------------------- /assets/icons/view-media-artist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/view-media-artist.png -------------------------------------------------------------------------------- /assets/icons/view-web-browser-dom-tree.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/view-web-browser-dom-tree.png -------------------------------------------------------------------------------- /assets/icons/window-close.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/assets/icons/window-close.png -------------------------------------------------------------------------------- /check-and-kill-webserver.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # Set the cache directory 4 | bundle='com.packal' 5 | file="/tmp/$bundle/zombie" 6 | # See if the time file exists; if so, check the time 7 | # Otherwise, kill the server (just in case) 8 | # And exit the script 9 | 10 | # Sleep for an initial 30 seconds to make sure that everything launches before this script goes bye-bye 11 | echo "Sleeping for thirty seconds." 12 | sleep 30 13 | 14 | alive=1 15 | echo "Waking up and starting the checks" 16 | while [ $alive -eq 1 ] 17 | do 18 | echo "Doing a check..." 19 | if [ -e "$file" ] 20 | then 21 | # Find the UNIX Epoch time 22 | now=`date +%s` 23 | # Get the last update of the webserver in Unix Epoch time (read from file) 24 | # file="/Users/Sven/Library/Caches/com.runningwithcrayons.Alfred-2/Workflow\\ Data/com.packal/webserver/zombie" 25 | date=`cat "$file"` 26 | 27 | # Find the difference 28 | diff=`expr $now - $date` 29 | # If the difference is greater than two minutes, then kill the webserver 30 | if [ $diff -gt 120 ] 31 | then 32 | pid=`ps aux | grep 'php -S localhost:7893 -t gui' | grep -v grep | awk '{print $2}'` 33 | rm "$file" 34 | kill "$pid" 35 | echo "Killing the webserver" 36 | alive=0 37 | else 38 | echo "Webserver will live just longer" 39 | sleep 60 40 | fi 41 | else 42 | pid=`ps aux | grep 'php -S localhost:7893 -t gui' | grep -v grep | awk '{print $2}'` 43 | rm "$file" 44 | kill "$pid" 45 | echo "Killing the webserver" 46 | alive=0 47 | fi 48 | done 49 | -------------------------------------------------------------------------------- /cli/includes/plist-functions.php: -------------------------------------------------------------------------------- 1 | toArray(); 61 | 62 | if ( ! isset( $tmp['objects'] ) ) { 63 | // This is a blank Workflow. Why are you updating it? 64 | return true; 65 | } 66 | 67 | foreach ( $tmp['objects'] as $key => $object ) { 68 | if ( $object['type'] == 'alfred.workflow.trigger.hotkey' ) { 69 | // We've found a hotkey, so let's strip it. 70 | $location = ":objects:$key:config"; 71 | stripHotkey( $location, $plist ); 72 | } 73 | } 74 | 75 | return true; 76 | } 77 | 78 | /** 79 | * Gets the name of a workflow from its info.plist file 80 | * 81 | * @param string $dir the directory name of the workflow 82 | * @return string the name of the workflow 83 | */ 84 | function getName( $dir ) { 85 | $PlistBuddy = '/usr/libexec/PlistBuddy -c '; 86 | $cmd = $PlistBuddy . "'print :name' $dir/info.plist"; 87 | return exec( "$cmd" ); 88 | } 89 | 90 | /** 91 | * Gets the bundle of a workflow from its info.plist file 92 | * 93 | * @param string $dir the directory name of the workflow 94 | * @return string the bundleid of the workflow 95 | */ 96 | function getBundle( $dir ) { 97 | $PlistBuddy = '/usr/libexec/PlistBuddy -c '; 98 | $cmd = $PlistBuddy . "'print :bundleid' $dir/info.plist"; 99 | return exec( "$cmd" ); 100 | } 101 | -------------------------------------------------------------------------------- /cli/includes/plist-migration.php: -------------------------------------------------------------------------------- 1 | toArray(); 40 | /** 41 | * Load the objects array, scan through it to get everything migratable. 42 | * Store the values as an array with the UIDs as the keys. 43 | * @var array 44 | */ 45 | foreach ( $tmp['objects'] as $o ) { 46 | if ( isset( $o['config'] ) ) { 47 | $original[ $o['uid'] ]['type'] = $o['type']; 48 | 49 | switch ( $o['type'] ) : 50 | case 'alfred.workflow.trigger.hotkey' : 51 | $value = array(); 52 | if ( isset( $o['config']['hotkey'] ) ) { 53 | $value['hotkey'] = $o['config']['hotkey']; 54 | } 55 | if ( isset( $o['config']['hotmod'] ) ) { 56 | $value['hotmod'] = $o['config']['hotmod']; 57 | } 58 | if ( isset( $o['config']['hotstring'] ) ) { 59 | $value['hotstring'] = $o['config']['hotstring']; 60 | } 61 | 62 | $original[ $o['uid'] ]['config'] = $value; 63 | break; 64 | case 'alfred.workflow.input.filefilter' : 65 | case 'alfred.workflow.input.scriptfilter' : 66 | case 'alfred.workflow.input.keyword' : 67 | $value = array( 68 | 'keyword' => $o['config']['keyword'], 69 | ); 70 | $original[ $o['uid'] ]['config'] = $value; 71 | break; 72 | endswitch; 73 | 74 | } 75 | } 76 | 77 | $tmp = $import->toArray(); 78 | 79 | // The uids are stored as the keys of the $original array, 80 | // so let's grab them to check if we need to migrate anything. 81 | $uids = array_keys( $original ); 82 | 83 | // These are the only types of objects that we need to migrate 84 | $objects = array( 85 | 'alfred.workflow.trigger.hotkey', 86 | 'alfred.workflow.input.filefilter', 87 | 'alfred.workflow.input.scriptfilter', 88 | 'alfred.workflow.input.keyword', 89 | ); 90 | 91 | // We need the key(order) so that we can set things properly 92 | foreach ( $tmp['objects'] as $key => $o ) { 93 | // Use only the things in the types above 94 | if ( in_array( $o['type'] , $objects ) ) { 95 | // Check to see if the objects is one of the original objects with a uid. 96 | if ( in_array( $o['uid'] , $uids ) ) { 97 | echo $o['uid'] . '
'; 98 | echo $o['type'] . '
'; 99 | if ( $o['type'] == 'alfred.workflow.trigger.hotkey' ) { 100 | // We're not really going to bother to check to see if the values match; 101 | // we'll just migrate them instead. 102 | //setPlistValue( $location, $value, $plist) 103 | setPlistValue( ":objects:$key:config:hotmod", $original[ $o['uid'] ]['config']['hotmod'], $new ); 104 | setPlistValue( ":objects:$key:config:hotkey", $original[ $o['uid'] ]['config']['hotkey'], $new ); 105 | setPlistValue( ":objects:$key:config:hotstring", $original[ $o['uid'] ]['config']['hotstring'], $new ); 106 | } else { 107 | // At this point, the only other thing to migrate is the keyword 108 | // Check to see if they match; if they don't, then set them to the new one 109 | if ( $o['config']['keyword'] != $original[ $o['uid'] ]['config']['keyword'] ) { 110 | setPlistValue( ":objects:$key:config:keyword", $original[ $o['uid'] ]['config']['keyword'], $new ); 111 | } 112 | } 113 | } 114 | } 115 | } 116 | return true; 117 | 118 | /** 119 | * 120 | * Todo: 121 | * -- add in error checking for correct plist syntax. 122 | * -- expand migration for desired but non-essential settings 123 | * 124 | */ 125 | } 126 | 127 | -------------------------------------------------------------------------------- /cli/packal.php: -------------------------------------------------------------------------------- 1 | $opt[0]; 45 | } 46 | echo $config->$opt[0]; 47 | 48 | } 49 | 50 | function setOption( $opt = array() ) { 51 | global $config; 52 | 53 | $value = $opt[1]; 54 | if ( $value == 'null' ) { 55 | $value = ''; 56 | } 57 | 58 | $options = array( 59 | 'backups', 60 | 'auto_add', 61 | 'workflowReporting', 62 | 'notify', 63 | 'username', 64 | 'authorName', 65 | 'api_key', 66 | ); 67 | 68 | $bool = array( 69 | 'auto_add', 70 | 'report', 71 | 'notify', 72 | 'packalAccount', 73 | ); 74 | 75 | if ( ( ! in_array( $opt[0], $options ) ) && ( ! in_array( $opt[0], $bool ) ) ) { 76 | echo 'Error: invalid option.'; 77 | return false; 78 | } elseif ( in_array( $opt[0], $bool ) && ( ! ( $value == 0 || $value == 1 ) ) ) { 79 | echo "Error: $opt[0] value must be 0 or 1."; 80 | return false; 81 | } elseif ( ( $opt[0] == 'backup' ) && ( ! is_numeric( $value ) ) ) { 82 | echo "Error: $opt[0] value must be numeric."; 83 | } 84 | 85 | // Load the config 86 | $xml = simplexml_load_file( $config ); 87 | 88 | $xml->$opt[0] = $value; 89 | 90 | // Save the config again. 91 | $xml->asXml( $config ); 92 | } 93 | 94 | function validateAPI( $key ) { 95 | // To be written, but not needed for this version. 96 | } 97 | 98 | //////////////////////////////////////////////////////////////////////////////// 99 | /// Check single update 100 | 101 | function checkUpdate( $wf ) { 102 | global $manifest, $cliDir; 103 | 104 | $wf = $wf[0]; 105 | $dir = trim( `"$cliDir/packal.sh" getDir "$wf" 2> /dev/null` ); 106 | $xml = simplexml_load_file( "$dir/packal/package.xml" ); 107 | $last = $xml->updated; 108 | 109 | $xml = simplexml_load_file( "$manifest" ); 110 | 111 | foreach ( $xml as $w ) : 112 | if ( $w->bundle == $wf ) { 113 | if ( "$w->updated" > "$last" ) { 114 | echo 'Needs update.'; 115 | } 116 | } 117 | endforeach; 118 | } 119 | 120 | //////////////////////////////////////////////////////////////////////////////// 121 | /// Check and do all updates 122 | 123 | /** 124 | * Checks updates for all workflows (that are on Packal) 125 | */ 126 | function checkUpdates( $opt = array() ) { 127 | global $manifest, $cache, $cliDir; 128 | 129 | if ( isset( $opt[0] ) ) { 130 | if ( $opt[0] == 1 ) { 131 | $yes = true; 132 | } else { $yes = false; 133 | } 134 | } else { 135 | $yes = false; 136 | } 137 | if ( isset( $opt[1] ) ) { 138 | if ( $opt[1] == 1 ) { 139 | $force = true; 140 | } else { $force = false; 141 | } 142 | } else { 143 | $force = false; 144 | } 145 | 146 | $xml = simplexml_load_file( "$manifest" ); 147 | $me = getOption( array( 'username', true ) ); 148 | 149 | if ( file_exists( "$cache/updates" ) ) { 150 | unlink( "$cache/updates" ); 151 | } 152 | 153 | $i = 1; 154 | foreach ( $xml as $w ) : 155 | $dir = trim( `"$cliDir/packal.sh" getDir "$w->bundle" 2> /dev/null` ); 156 | 157 | if ( $dir == 'FALSE' ) { 158 | continue; 159 | } 160 | 161 | if ( "$w->author" == "$me" ) { 162 | echo "* Skipping $i... $w->name ($w->bundle)\n"; 163 | $i++; 164 | continue; 165 | } 166 | 167 | if ( file_exists( "$dir/packal/package.xml" ) ) { 168 | echo "Checking $i... $w->name ($w->bundle)"; 169 | 170 | $wf = simplexml_load_file( "$dir/packal/package.xml" ); 171 | $wf->updated += 120; // Compensation for time in the generated packages. 172 | 173 | if ( "$w->updated" > "$wf->updated" ) { 174 | echo ' — Update Available'; 175 | file_put_contents( "$cache/updates", $w->bundle . "\n", FILE_APPEND ); 176 | $updatable[] = array( (string) $w->name, (string) $wf->version, (string) $w->version, (string) $w->bundle ); 177 | } 178 | echo "\n"; 179 | $i++; 180 | } else { 181 | if ( $force == false ) { 182 | continue; 183 | } 184 | 185 | echo "Checking $i... Forcing Update for $w->name ($w->bundle)"; 186 | 187 | file_put_contents( "$cache/updates", $w->bundle . "\n", FILE_APPEND ); 188 | $updatable[] = array( (string) $w->name, 'Forced Update', (string) $w->version, (string) $w->bundle ); 189 | echo "\n"; 190 | $i++; 191 | 192 | } 193 | 194 | endforeach; 195 | 196 | if ( ( ! isset( $updatable ) ) || ( ! count( $updatable > 0 ) ) ) { 197 | echo "Everything is up to date.\n"; 198 | return false; 199 | } 200 | 201 | echo 'Updates available for: '; 202 | $count = count( $updatable ) - 1; 203 | foreach ( $updatable as $u ) : 204 | echo "$u[0] ($u[1] -> $u[2])"; 205 | if ( $count > 0 ) { 206 | echo ', '; 207 | } else { echo ".\n"; 208 | } 209 | $count--; 210 | endforeach; 211 | echo "\n"; 212 | echo "* Note: Packal tracks updates on timestamps not version numbers, so if these seem off, then the workflow author did not update the version number.\n"; 213 | echo "\n"; 214 | $conf = getConfirmation( $yes ); 215 | 216 | if ( $conf ) { 217 | foreach ( $updatable as $u ) : 218 | echo "Trying to update $u[0] ($u[3])..."; 219 | if ( doUpdate( $u[3] ) ) { 220 | echo " Success.\n"; 221 | } else { 222 | echo ' ERROR.'; 223 | } 224 | endforeach; 225 | } else { 226 | echo "Aborting.\n"; 227 | } 228 | 229 | // While this method is incomplete, it doesn't quite matter for current functionality. 230 | 231 | } 232 | 233 | /** 234 | * Get update confirmation from the user via command line 235 | * @param bool $yes = false Automatically confirm. 236 | */ 237 | function getConfirmation( $yes = false ) { 238 | 239 | if ( $yes ) { 240 | echo "Update? (Y/n): y\n"; 241 | return true; 242 | } 243 | 244 | $conf = readline( 'Update? (Y/n): ' ); 245 | 246 | if ( empty( $conf ) || ( $conf == 'y' ) || ( $conf == 'Y' ) ) { 247 | $response = true; 248 | } elseif ( $conf == 'n' || $conf == 'N' ) { 249 | $response = false; 250 | } else { 251 | echo "Invalid entry. Please choose y or n.\n"; 252 | $response = getConfirmation(); 253 | } 254 | 255 | return $response; 256 | } 257 | 258 | /** 259 | * [doUpdate description] 260 | * @param {[type]} $bundle [description] 261 | * @param {[type]} $force = false [description] 262 | */ 263 | function doUpdate( $bundle, $force = false ) { 264 | 265 | global $data, $cache, $manifest, $repo, $cliDir; 266 | 267 | if ( is_array( $bundle ) ) { 268 | $bundle = $bundle[0]; 269 | } 270 | 271 | $dir = trim( `"$cliDir/packal.sh" getDir "$bundle"` ); 272 | // echo $dir; 273 | // The force variable means to download even if the original 274 | // is not from Packal. Obviously, since we don't have the 275 | // update data, this just means to download the update no 276 | // matter what and to get everything. 277 | if ( ! $force ) { 278 | if ( ! file_exists( "$dir/packal/package.xml" ) ) { 279 | echo 'Error: No package information exists.'; 280 | return false; 281 | } 282 | } 283 | 284 | $xml = simplexml_load_file( "$manifest" ); 285 | 286 | foreach ( $xml as $x ) : 287 | if ( "$x->bundle" != "$bundle" ) { 288 | continue; 289 | } 290 | $xml = $x; 291 | break; 292 | endforeach; 293 | 294 | // Make the temporary directories. 295 | if ( ! file_exists( "$cache/update" ) ) { 296 | mkdir( "$cache/update" ); 297 | } 298 | if ( ! file_exists( "$cache/update/$bundle" ) ) { 299 | mkdir( "$cache/update/$bundle" ); 300 | } 301 | if ( ! file_exists( "$cache/update/$bundle/tmp" ) ) { 302 | mkdir( "$cache/update/$bundle/tmp" ); 303 | } 304 | 305 | `curl -sL "$repo/$bundle/$xml->file" > "$cache/update/$bundle/$xml->file"`; 306 | `curl -sL "$repo/$bundle/appcast.xml" > "$cache/update/$bundle/appcast.xml"`; 307 | `unzip -qo "$cache/update/$bundle/$xml->file" -d "$cache/update/$bundle/tmp"`; 308 | 309 | $valid = verifySignature( "$cache/update/$bundle/appcast.xml", 310 | "$cache/update/$bundle/$xml->file", 311 | "$dir/packal/$bundle.pub" 312 | ); 313 | 314 | if ( ! $valid ) { 315 | echo "Error: Cannot verify signature for $xml->file from $bundle."; 316 | return false; 317 | } 318 | 319 | // Why can't I just include the function, call it, and be done with it? 320 | // I think it might be a namespacing issue or something. 321 | // migratePlist( "$dir/info.plist", "$cache/update/$bundle/tmp/info.plist" ); 322 | 323 | // It's too late to figure that out. Maybe tomorrow. 324 | $cmd = 'php ' . __DIR__ . "/includes/plist-migration.php \"$dir/info.plist\" \"$cache/update/$bundle/tmp/info.plist\""; 325 | exec( "$cmd" ); 326 | 327 | // Backup the bundle. 328 | echo `"$cliDir/packal.sh" backup "$bundle"`; 329 | $cmd = "'" . __DIR__ . "/packal.sh' replaceFiles " . escapeshellarg( $dir ) . ' ' . escapeshellarg( $cache . '/update/' . "$bundle/tmp/" ); 330 | 331 | exec( "$cmd" ); 332 | 333 | `rm -fR "$cache/update/$bundle"`; 334 | 335 | $tn = __load( 'terminal-notifier' , 'default' , 'utility' ); 336 | exec( "$tn -title 'Packal Updater' -message '$xml->name has been updated to version $xml->version'" ); 337 | 338 | echo 'TRUE'; 339 | return true; 340 | 341 | } 342 | 343 | function doUpdateAll( $force = false ) { 344 | global $manifest, $cache, $cliDir; 345 | 346 | $xml = simplexml_load_file( "$manifest" ); 347 | $me = getOption( array( 'username', true ) ); 348 | 349 | foreach ( $xml as $w ) : 350 | $dir = trim( `"$cliDir/packal.sh" getDir "$w->bundle" 2> /dev/null` ); 351 | 352 | if ( $dir === 'FALSE' ) { 353 | continue; 354 | } 355 | 356 | if ( "$w->author" == "$me" ) { 357 | continue; 358 | } 359 | 360 | if ( file_exists( "$dir/packal/package.xml" ) ) { 361 | $wf = simplexml_load_file( "$dir/packal/package.xml" ); 362 | $wf->updated += 120; // Compensation for time in the generated packages. 363 | 364 | if ( "$w->version" != "$wf->version" ) { 365 | $updatable[] = array( (string) $w->name, (string) $wf->version, (string) $w->version, (string) $w->bundle ); 366 | } 367 | } else { 368 | if ( $force == false ) { 369 | continue; 370 | } 371 | 372 | $updatable[] = array( (string) $w->name, 'Forced Update', (string) $w->version, (string) $w->bundle ); 373 | 374 | } 375 | 376 | endforeach; 377 | 378 | if ( ( ! isset( $updatable ) ) || ( ! count( $updatable > 0 ) ) ) { 379 | return false; 380 | } 381 | 382 | foreach ( $updatable as $u ) : 383 | doUpdate( $u[3] ); 384 | endforeach; 385 | } 386 | 387 | /** 388 | * Checks to the signature of a package 389 | * @param string $appcast an xml file containing the signature (path) 390 | * @param string $package a file that has been signed (path) 391 | * @param string $key the public key to use for checking (path) 392 | * @return [type] [description] 393 | */ 394 | function verifySignature( $appcast, $package, $key ) { 395 | 396 | $appcast = simplexml_load_file( $appcast ); 397 | $signature = $appcast->signature; 398 | 399 | $data = sha1_file( $package , false ); 400 | 401 | // fetch public key from certificate and ready it 402 | $fp = fopen( $key , 'r' ); 403 | $cert = fread( $fp , filesize( $key ) ); 404 | fclose( $fp ); 405 | 406 | // Get the public key 407 | $id = openssl_get_publickey( $cert ); 408 | 409 | // Get the result of the signature 410 | $result = openssl_verify( $data , base64_decode( $signature ) , $id , OPENSSL_ALGO_SHA1 ); 411 | 412 | // Free key from memory 413 | openssl_free_key( $id ); 414 | 415 | // Return the result 416 | return $result; 417 | 418 | } 419 | 420 | //////////////////////////////////////////////////////////////////////////////// 421 | /// Strongarm functions 422 | // This isn't implemented currently. For the future. 423 | function forcePackal() { 424 | global $manifest, $cache; 425 | 426 | $xml = simplexml_load_file( "$manifest" ); 427 | $me = getOption( array( 'username', true ) ); 428 | 429 | } 430 | -------------------------------------------------------------------------------- /cli/packal.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # important global variables 4 | bundle="com.packal" 5 | manifest="https://raw.github.com/packal/repository/master/manifest.xml" 6 | path="$( cd "$( dirname "${BASH_SOURCE[0]}" )/../.." && pwd -P )" 7 | 8 | # Folders 9 | data="$HOME/Library/Application Support/Alfred 2/Workflow Data/$bundle" 10 | cache="$HOME/Library/Caches/com.runningwithcrayons.Alfred-2/Workflow Data/$bundle" 11 | backups="$data/backups" 12 | endpoints="$data/endpoints" 13 | 14 | # Utilities 15 | pb="/usr/libexec/PlistBuddy" 16 | 17 | ################################################################################ 18 | # Functions 19 | ################################################################################ 20 | 21 | setupDirs() { 22 | dir "$data" 23 | dir "$cache" 24 | dir "$backups" 25 | dir "$endpoints" 26 | } 27 | 28 | dir() { 29 | if [ ! -d "$1" ]; then 30 | mkdir "$1" 31 | fi 32 | } 33 | 34 | getManifest() { 35 | curl -sL "$manifest" > "$cache/manifest.xml" 36 | if [ `echo $?` = 0 ]; then 37 | if [ -f "$data/manifest.xml" ]; then 38 | rm "$data/manifest.xml" 39 | mv "$cache/manifest.xml" "$data/manifest.xml" 40 | else 41 | mv "$cache/manifest.xml" "$data/manifest.xml" 42 | fi 43 | else 44 | echo "Error: Server not reachable." 45 | fi 46 | echo "TRUE" 47 | 48 | } 49 | 50 | checkModified() { 51 | if [ -f "$1" ]; then 52 | echo `stat -f "%m" "$1"` 53 | else 54 | # File doesn't exist, so we'll just give it a 0 response. 55 | echo 0 56 | fi 57 | } 58 | 59 | isCacheValid() { 60 | 61 | # Set the TTL variable from argument, fallback to 24 hours. 62 | if [ -z "$1" ]; then 63 | local $ttl = 86400 64 | else 65 | local $ttl = $1 66 | fi 67 | 68 | # Check if modified within the TTL parameter 69 | if [ `checkModified "$data/manifest.xml"` -lt $ttl ]; then 70 | echo "FALSE" 71 | else 72 | echo "TRUE" 73 | fi 74 | } 75 | 76 | generateMap() { 77 | # Creates a map of bundles and directories. 78 | endpoints="$data/endpoints" 79 | 80 | if [ ! "$1" = "TRUE" ]; then 81 | if [[ ! `checkModified "$endpoints/endpoints.json"` -lt `stat -f "%m" "$path"` ]]; then 82 | # Nothing new has been installed, so there is no reason to update the map 83 | exit 0 84 | fi 85 | fi 86 | 87 | echo "{" > "$endpoints/endpoints.json" 88 | for w in "$path/"* 89 | do 90 | local bundle=`$pb -c "Print :bundleid" "$w/info.plist" 2> /dev/null` 91 | if [ ! -z "$bundle" ]; then 92 | echo "\"$bundle\": \"$w\"," >> "$endpoints/endpoints.json" 93 | fi 94 | done 95 | 96 | echo `cat "$endpoints/endpoints.json" | sed -e :a -e '/^\n*$/{$d;N;ba' -e '}' | sed -e '$s|,$|}|'` > "$endpoints/endpoints.json" 97 | 98 | if [ -f "$endpoints/endpoints.list" ]; then 99 | rm "$endpoints/endpoints.list" 100 | fi 101 | for w in "$path/"* 102 | do 103 | local bundle=`$pb -c "Print :bundleid" "$w/info.plist" 2> /dev/null` 104 | if [ ! -z "$bundle" ]; then 105 | echo "\"$bundle\"=\"$w\"" >> "$endpoints/endpoints.list" 106 | fi 107 | done 108 | 109 | } 110 | 111 | getDir() { 112 | if [ ! -f "$endpoints/endpoints.list" ]; then 113 | generateMap 114 | fi 115 | 116 | line=`cat "$endpoints/endpoints.list" | grep "$1"` 117 | local bundle="$line" 118 | 119 | line=${line#*=} 120 | line=`echo $line | sed -e 's|\"||g'` 121 | 122 | bundle=${bundle%=*} 123 | bundle=`echo $bundle | sed -e 's|\"||g'` 124 | 125 | if [ "$bundle" = "$1" ]; then 126 | echo $line 127 | else 128 | echo "FALSE" 129 | fi 130 | 131 | } 132 | 133 | checkUpdate() { 134 | dir=`getDir "$1"` 135 | if [ -d "$dir/packal" ]; then 136 | echo "Packal WF" 137 | fi 138 | } 139 | 140 | getOpt() { 141 | dir=`getDir "$1"` 142 | echo `$pb -c "Print :$2" "$dir/info.plist"` 143 | } 144 | 145 | backup() { 146 | 147 | name=`getOpt "$1" "name"` 148 | dir=`getDir "$1"` 149 | date=`date +"%Y-%m-%d-%H.%M.%S"` 150 | 151 | file=`echo $date-$name.alfredworkflow | tr '&' '_' | tr ' ' '_'` 152 | cd "$dir" 153 | zip -q -r "$cache/$file" * 154 | cd - 155 | dir "$data/backups/$name" 156 | 157 | backups=`ls "$data/backups/$name" | grep alfredworkflow | wc -l` 158 | me="$( cd "$( dirname "${BASH_SOURCE[0]}" )/.." && pwd -P )" 159 | 160 | keep=`php "$me/cli/packal.php" getOption backups` 161 | 162 | 163 | if [[ $backups -ge $keep ]]; then 164 | count=$backups 165 | files=`ls "$data/backups/$name" | grep alfredworkflow` 166 | while [[ $count -gt $(( keep-1 )) ]]; do 167 | for f in $files; do 168 | if [[ $count -gt $(( keep-1 )) ]]; then 169 | f=`echo $f | sed 's|'\\\n'||g'` 170 | rm "$data/backups/$name/$f" 171 | count=$(( count-1 )) 172 | fi 173 | done 174 | done 175 | fi 176 | 177 | mv "$cache/$file" "$data/backups/$name/" 178 | 179 | } 180 | 181 | replaceFiles() { 182 | old="$1" 183 | new="$2" 184 | cd "$old" 185 | rm -fR * 186 | mv -f "$new"* . 187 | cd - 188 | } 189 | 190 | restoreBackup() { 191 | # Right now this function is empty. 192 | a=1 193 | } 194 | 195 | ################################################################################ 196 | # Start Script 197 | ################################################################################ 198 | 199 | setupDirs 200 | 201 | if [ "$1" = "update" ]; then 202 | if [ "$2" = "TRUE" ]; then 203 | getManifest 204 | generateMap TRUE 205 | else 206 | if [[ `checkModified "$data/manifest.xml"` -eq 0 ]]; then 207 | getManifest 208 | fi 209 | generateMap 210 | fi 211 | 212 | elif [ "$1" = "getDir" ]; then 213 | getDir "$2" 214 | 215 | 216 | elif [ "$1" = "checkUpdate" ]; then 217 | checkUpdate "$2" 218 | 219 | elif [ "$1" = "backup" ]; then 220 | backup "$2" 221 | 222 | elif [ "$1" = "replaceFiles" ]; then 223 | replaceFiles "$2" "$3" 224 | 225 | else 226 | echo "Error: invalid command ($1)" 227 | exit 1 228 | 229 | fi 230 | 231 | exit 0 232 | -------------------------------------------------------------------------------- /config.php: -------------------------------------------------------------------------------- 1 | $val ) { 6 | $$var = $val; 7 | } 8 | $_SERVER['alfred_workflow_data'] = $data_dir; 9 | $_SERVER['alfred_workflow_cache'] = $cache_dir; 10 | $_SERVER['HOME'] = $HOME; 11 | $_SERVER['alfred_version'] = $alfred_version; 12 | $_SERVER['alfred_preferences'] = $alfred_preferences; 13 | } 14 | 15 | // These are just here for now... 16 | $bundle = isset( $_SERVER['alfred_workflow_bundleid'] ) ? $_SERVER['alfred_workflow_bundleid'] : 'com.packal'; 17 | $home = $_SERVER['HOME']; 18 | 19 | function guess_alfred_version() { 20 | if ( isset( $_SERVER['alfred_version'] ) ) { 21 | return floor( floatval( $_SERVER['alfred_version'] ) ); 22 | } 23 | if ( file_exists( '/Applications/Alfred 3.app' ) ) { 24 | return 3; 25 | } 26 | if ( file_exists( "{$_SERVER['HOME']}/Applications/Alfred 3.app" ) ) { 27 | return 3; 28 | } 29 | if ( file_exists( '/Applications/Alfred 2.app' ) ) { 30 | return 2; 31 | } 32 | if ( file_exists( "{$_SERVER['HOME']}/Applications/Alfred 2.app" ) ) { 33 | return 2; 34 | } 35 | throw new Exception( 'Cannot guess what version of Alfred to use' ); 36 | } 37 | 38 | function data() { 39 | global $bundle, $home; 40 | 41 | if ( isset( $_SERVER['alfred_workflow_data'] ) ) { 42 | return $_SERVER['alfred_workflow_data']; 43 | } 44 | 45 | $v = guess_alfred_version(); 46 | return "{$home}/Library/Application Support/Alfred {$v}/Workflow Data/{$bundle}"; 47 | } 48 | 49 | function cache() { 50 | global $bundle, $home; 51 | 52 | if ( isset( $_SERVER['alfred_workflow_cache'] ) ) { 53 | return $_SERVER['alfred_workflow_cache']; 54 | } 55 | 56 | $v = guess_alfred_version(); 57 | return "{$home}/Library/Caches/com.runningwithcrayons.Alfred-{$v}/Workflow Data/{$bundle}"; 58 | } 59 | 60 | define( '__DATA__', data() ); 61 | define( '__CACHE__', cache() ); 62 | define( '__BUNDLE__', $bundle ); 63 | define( '__HOME__', $home ); 64 | 65 | $environments = [ 66 | 'development' => 'http://localhost:3000', // Local Passenger Server 67 | 'dev-staging' => 'http://packal.dev', // Local nginx proxying to Passenger 68 | 'staging' => 'https://mellifluously.org', // Staging Server 69 | 'production' => 'https://www.packal.org', // Actual Production (not setup yet) 70 | ]; 71 | 72 | // Turns on Development Code (extra logging, etc...) 73 | define( 'DEVELOPMENT_TESTING', true ); 74 | define( '__LEGACY__', true ); 75 | 76 | // The current environment is defined in environment.txt 77 | define( 'BUNDLE', 'com.packal' ); 78 | 79 | define( 'CACHE', __CACHE__ ); 80 | define( 'DATA', __DATA__ ); 81 | define( 'ERROR_ICON', '/System/Library/CoreServices/CoreTypes.bundle/Contents/Resources/Unsupported.icns' ); 82 | 83 | if ( ! isset( $_SERVER['alfred_workflow_cache'] ) ) { 84 | $_SERVER['alfred_workflow_cache'] = __CACHE__; 85 | } 86 | if ( ! isset( $_SERVER['alfred_workflow_data'] ) ) { 87 | $_SERVER['alfred_workflow_data'] = __DATA__; 88 | } 89 | if ( ! isset( $_SERVER['alfred_workflow_bundleid'] ) ) { 90 | $_SERVER['alfred_workflow_bundleid'] = $bundle; 91 | } 92 | 93 | foreach ( [ __CACHE__, __DATA__ ] as $dir ) : 94 | if ( ! file_exists( $dir ) ) { 95 | mkdir( $dir, 0775, true ); 96 | } 97 | endforeach; 98 | -------------------------------------------------------------------------------- /favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/favicon.ico -------------------------------------------------------------------------------- /functions.php: -------------------------------------------------------------------------------- 1 | '; 40 | $config = new SimpleXMLElement( $d ); 41 | $config->packalAccount = 0; 42 | $config->forcePackal = 0; 43 | $config->backups = 3; 44 | $config->username = ''; 45 | $config->authorName = ''; 46 | $config->notifications = '2'; 47 | $config->workflowReporting = '1'; 48 | $config->apiKey = ''; 49 | $config->asXML( "$data/config/config.xml" ); 50 | unset( $config ); 51 | } 52 | 53 | // Generate Empty Blacklist File 54 | if ( ! file_exists( "$data/config/blacklist.json" ) ) { 55 | file_put_contents( "$data/config/blacklist.json", utf8_encode( json_encode( array() ) ) ); 56 | } 57 | 58 | if ( ! file_exists( "$data/manifest.xml" ) ) { 59 | // Get the manifest with error handling. 60 | if ( getManifest() == false ) { 61 | // So, we're getting the manifest, but 62 | // there was a problem, so we're going 63 | // to communicate that. 64 | return false; 65 | } 66 | } 67 | return true; 68 | } 69 | 70 | function generateEndpoints( $force = false ) { 71 | global $bundle; 72 | 73 | $HOME = exec( 'echo $HOME' ); 74 | $data = DATA_DIR; 75 | 76 | if ( ! file_exists( "$data/endpoints" ) && is_dir( "$data/endpoints" ) ) { 77 | mkdir( "$data/endpoints" ); 78 | } 79 | 80 | if ( ( ! ( file_exists( "$data/endpoints/endpoints.json" ) && file_exists( "$data/endpoints/endpoints.list" ) ) ) 81 | || ( filemtime( "$data/endpoints/endpoints.json" ) < filemtime( dirname( __DIR__ ) ) ) || ( $force !== false ) ) { 82 | 83 | // Okay, we need to update the files. 84 | $dirs = array_diff( scandir( WORKFLOWS_DIR ), array( '.', '..', '.git', '.DS_Store' ) ); 85 | 86 | if ( file_exists( "$data/endpoints/endpoints.list" ) ) { 87 | unlink( "$data/endpoints/endpoints.list" ); 88 | } 89 | $fp = fopen( "$data/endpoints/endpoints.list", 'w' ); 90 | 91 | $me = basename( __DIR__ ); 92 | 93 | foreach ( $dirs as $d ) : 94 | $d = str_replace( '//', '/', str_replace( $me, '', __DIR__ ) . "/$d" ); 95 | $bundle = readPlistValue( 'bundleid', "$d/info.plist" ); 96 | if ( empty( $bundle ) ) { 97 | continue; 98 | } 99 | 100 | $endpoints[ $bundle ] = $d; 101 | fwrite( $fp , "\"$bundle\"=\"$d\"\n" ); 102 | endforeach; 103 | 104 | file_put_contents( "$data/endpoints/endpoints.json", utf8_encode( json_encode( $endpoints ) ) ); 105 | fclose( $fp ); 106 | } else { return false; 107 | } 108 | } 109 | 110 | function readPlistValue( $key, $plist ) { 111 | return exec( "/usr/libexec/PlistBuddy -c \"Print :$key\" '$plist' 2> /dev/null" ); 112 | } 113 | 114 | function checkConnection() { 115 | ini_set( 'default_socket_timeout', 1 ); 116 | 117 | // First test 118 | exec( 'ping -c 1 -t 1 www.github.com', $pingResponse, $pingError ); 119 | if ( $pingError == 14 ) { 120 | return false; 121 | } 122 | 123 | // Second Test 124 | $connection = @fsockopen( 'www.github.com', 80, $errno, $errstr, 1 ); 125 | 126 | if ( $connection ) { 127 | $status = true; 128 | fclose( $connection ); 129 | } else { 130 | $status = false; 131 | } 132 | return $status; 133 | } 134 | 135 | function countFiles( $dir ) { 136 | $fi = new FilesystemIterator( "$dir" , FilesystemIterator::SKIP_DOTS ); 137 | return iterator_count( $fi ); 138 | } 139 | 140 | function getFiles( $dir ) { 141 | return array_diff( scandir( $dir ), array( '..', '.', '.DS_Store' ) ); 142 | } 143 | 144 | function returnBackups( $dir, &$w ) { 145 | global $data; 146 | 147 | $backupDirs = getFiles( "$data/backups" ); 148 | foreach ( $backupDirs as $b ) : 149 | if ( is_dir( "$data/backups/$b" ) ) { 150 | $backups[ $b ] = countFiles( "$data/backups/$b" ); 151 | } 152 | endforeach; 153 | 154 | if ( count( $backups ) > 0 ) { 155 | ksort( $backups ); 156 | foreach ( $backups as $name => $count ) : 157 | $w->result( '', '', $name, "$count backups.", '', 'no', '' ); 158 | endforeach; 159 | } 160 | } 161 | 162 | function getManifestModTime() { 163 | global $data; 164 | 165 | // Set date/time things here. 166 | $m = time() - filemtime( "$data/manifest.xml" ); 167 | $days = floor( $m / 86400 ); 168 | $hours = floor( ( $m - ( $days * 86400 ) ) / 3600 ); 169 | $mins = floor( ( $m - ( $hours * 3600 ) ) / 60 ); 170 | $secs = floor( $m % 60 ); 171 | 172 | if ( $m > 60 * 60 * 24 * 7 * 120 ) { 173 | return 'a really long time ago.'; 174 | } 175 | if ( $m > 60 * 60 * 24 * 7 * 30 ) { 176 | return 'over a month ago.'; 177 | } 178 | if ( $m > 60 * 60 * 24 * 7 * 30 ) { 179 | return 'over a week ago.'; 180 | } 181 | if ( $m > 60 * 60 * 24 ) { 182 | return 'over a day ago.'; 183 | } 184 | if ( $m === 0 ) { 185 | $time = 'just now.'; 186 | } 187 | $time = ''; 188 | if ( $hours > 0 ) { 189 | $time .= $hours . ' hour'; 190 | if ( $hours > 1 ) { 191 | $time .= 's, '; 192 | } else { $time .= ', '; 193 | } 194 | } 195 | if ( $mins > 0 ) { 196 | $time .= $mins . ' minute'; 197 | if ( $mins > 1 ) { 198 | $time .= 's'; 199 | } else { $time .= ''; 200 | } 201 | } 202 | if ( $hours > 0 && $mins > 0 ) { 203 | $time .= ', and '; 204 | } elseif ( $hours > 0 || $mins > 0 ) { 205 | $time .= ' and '; 206 | } 207 | if ( $secs > 0 ) { 208 | $time .= $secs . ' second'; 209 | if ( $time > 1 ) { 210 | $time .= 's'; 211 | } 212 | } 213 | $time .= ' ago.'; 214 | 215 | return $time; 216 | } 217 | 218 | function sortWorkflowByName( $a, $b ) { 219 | return $a['name'] > $b['name']; 220 | } 221 | -------------------------------------------------------------------------------- /gui/assets/css/font-awesome.min.css: -------------------------------------------------------------------------------- 1 | /*! 2 | * Font Awesome 4.1.0 by @davegandy - http://fontawesome.io - @fontawesome 3 | * License - http://fontawesome.io/license (Font: SIL OFL 1.1, CSS: MIT License) 4 | */@font-face{font-family:'FontAwesome';src:url('../fonts/fontawesome-webfont.eot?v=4.1.0');src:url('../fonts/fontawesome-webfont.eot?#iefix&v=4.1.0') format('embedded-opentype'),url('../fonts/fontawesome-webfont.woff?v=4.1.0') format('woff'),url('../fonts/fontawesome-webfont.ttf?v=4.1.0') format('truetype'),url('../fonts/fontawesome-webfont.svg?v=4.1.0#fontawesomeregular') format('svg');font-weight:normal;font-style:normal}.fa{display:inline-block;font-family:FontAwesome;font-style:normal;font-weight:normal;line-height:1;-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale}.fa-lg{font-size:1.33333333em;line-height:.75em;vertical-align:-15%}.fa-2x{font-size:2em}.fa-3x{font-size:3em}.fa-4x{font-size:4em}.fa-5x{font-size:5em}.fa-fw{width:1.28571429em;text-align:center}.fa-ul{padding-left:0;margin-left:2.14285714em;list-style-type:none}.fa-ul>li{position:relative}.fa-li{position:absolute;left:-2.14285714em;width:2.14285714em;top:.14285714em;text-align:center}.fa-li.fa-lg{left:-1.85714286em}.fa-border{padding:.2em .25em .15em;border:solid .08em #eee;border-radius:.1em}.pull-right{float:right}.pull-left{float:left}.fa.pull-left{margin-right:.3em}.fa.pull-right{margin-left:.3em}.fa-spin{-webkit-animation:spin 2s infinite linear;-moz-animation:spin 2s infinite linear;-o-animation:spin 2s infinite linear;animation:spin 2s infinite linear}@-moz-keyframes spin{0%{-moz-transform:rotate(0deg)}100%{-moz-transform:rotate(359deg)}}@-webkit-keyframes spin{0%{-webkit-transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg)}}@-o-keyframes spin{0%{-o-transform:rotate(0deg)}100%{-o-transform:rotate(359deg)}}@keyframes spin{0%{-webkit-transform:rotate(0deg);transform:rotate(0deg)}100%{-webkit-transform:rotate(359deg);transform:rotate(359deg)}}.fa-rotate-90{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=1);-webkit-transform:rotate(90deg);-moz-transform:rotate(90deg);-ms-transform:rotate(90deg);-o-transform:rotate(90deg);transform:rotate(90deg)}.fa-rotate-180{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2);-webkit-transform:rotate(180deg);-moz-transform:rotate(180deg);-ms-transform:rotate(180deg);-o-transform:rotate(180deg);transform:rotate(180deg)}.fa-rotate-270{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=3);-webkit-transform:rotate(270deg);-moz-transform:rotate(270deg);-ms-transform:rotate(270deg);-o-transform:rotate(270deg);transform:rotate(270deg)}.fa-flip-horizontal{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=0, mirror=1);-webkit-transform:scale(-1, 1);-moz-transform:scale(-1, 1);-ms-transform:scale(-1, 1);-o-transform:scale(-1, 1);transform:scale(-1, 1)}.fa-flip-vertical{filter:progid:DXImageTransform.Microsoft.BasicImage(rotation=2, mirror=1);-webkit-transform:scale(1, -1);-moz-transform:scale(1, -1);-ms-transform:scale(1, -1);-o-transform:scale(1, -1);transform:scale(1, -1)}.fa-stack{position:relative;display:inline-block;width:2em;height:2em;line-height:2em;vertical-align:middle}.fa-stack-1x,.fa-stack-2x{position:absolute;left:0;width:100%;text-align:center}.fa-stack-1x{line-height:inherit}.fa-stack-2x{font-size:2em}.fa-inverse{color:#fff}.fa-glass:before{content:"\f000"}.fa-music:before{content:"\f001"}.fa-search:before{content:"\f002"}.fa-envelope-o:before{content:"\f003"}.fa-heart:before{content:"\f004"}.fa-star:before{content:"\f005"}.fa-star-o:before{content:"\f006"}.fa-user:before{content:"\f007"}.fa-film:before{content:"\f008"}.fa-th-large:before{content:"\f009"}.fa-th:before{content:"\f00a"}.fa-th-list:before{content:"\f00b"}.fa-check:before{content:"\f00c"}.fa-times:before{content:"\f00d"}.fa-search-plus:before{content:"\f00e"}.fa-search-minus:before{content:"\f010"}.fa-power-off:before{content:"\f011"}.fa-signal:before{content:"\f012"}.fa-gear:before,.fa-cog:before{content:"\f013"}.fa-trash-o:before{content:"\f014"}.fa-home:before{content:"\f015"}.fa-file-o:before{content:"\f016"}.fa-clock-o:before{content:"\f017"}.fa-road:before{content:"\f018"}.fa-download:before{content:"\f019"}.fa-arrow-circle-o-down:before{content:"\f01a"}.fa-arrow-circle-o-up:before{content:"\f01b"}.fa-inbox:before{content:"\f01c"}.fa-play-circle-o:before{content:"\f01d"}.fa-rotate-right:before,.fa-repeat:before{content:"\f01e"}.fa-refresh:before{content:"\f021"}.fa-list-alt:before{content:"\f022"}.fa-lock:before{content:"\f023"}.fa-flag:before{content:"\f024"}.fa-headphones:before{content:"\f025"}.fa-volume-off:before{content:"\f026"}.fa-volume-down:before{content:"\f027"}.fa-volume-up:before{content:"\f028"}.fa-qrcode:before{content:"\f029"}.fa-barcode:before{content:"\f02a"}.fa-tag:before{content:"\f02b"}.fa-tags:before{content:"\f02c"}.fa-book:before{content:"\f02d"}.fa-bookmark:before{content:"\f02e"}.fa-print:before{content:"\f02f"}.fa-camera:before{content:"\f030"}.fa-font:before{content:"\f031"}.fa-bold:before{content:"\f032"}.fa-italic:before{content:"\f033"}.fa-text-height:before{content:"\f034"}.fa-text-width:before{content:"\f035"}.fa-align-left:before{content:"\f036"}.fa-align-center:before{content:"\f037"}.fa-align-right:before{content:"\f038"}.fa-align-justify:before{content:"\f039"}.fa-list:before{content:"\f03a"}.fa-dedent:before,.fa-outdent:before{content:"\f03b"}.fa-indent:before{content:"\f03c"}.fa-video-camera:before{content:"\f03d"}.fa-photo:before,.fa-image:before,.fa-picture-o:before{content:"\f03e"}.fa-pencil:before{content:"\f040"}.fa-map-marker:before{content:"\f041"}.fa-adjust:before{content:"\f042"}.fa-tint:before{content:"\f043"}.fa-edit:before,.fa-pencil-square-o:before{content:"\f044"}.fa-share-square-o:before{content:"\f045"}.fa-check-square-o:before{content:"\f046"}.fa-arrows:before{content:"\f047"}.fa-step-backward:before{content:"\f048"}.fa-fast-backward:before{content:"\f049"}.fa-backward:before{content:"\f04a"}.fa-play:before{content:"\f04b"}.fa-pause:before{content:"\f04c"}.fa-stop:before{content:"\f04d"}.fa-forward:before{content:"\f04e"}.fa-fast-forward:before{content:"\f050"}.fa-step-forward:before{content:"\f051"}.fa-eject:before{content:"\f052"}.fa-chevron-left:before{content:"\f053"}.fa-chevron-right:before{content:"\f054"}.fa-plus-circle:before{content:"\f055"}.fa-minus-circle:before{content:"\f056"}.fa-times-circle:before{content:"\f057"}.fa-check-circle:before{content:"\f058"}.fa-question-circle:before{content:"\f059"}.fa-info-circle:before{content:"\f05a"}.fa-crosshairs:before{content:"\f05b"}.fa-times-circle-o:before{content:"\f05c"}.fa-check-circle-o:before{content:"\f05d"}.fa-ban:before{content:"\f05e"}.fa-arrow-left:before{content:"\f060"}.fa-arrow-right:before{content:"\f061"}.fa-arrow-up:before{content:"\f062"}.fa-arrow-down:before{content:"\f063"}.fa-mail-forward:before,.fa-share:before{content:"\f064"}.fa-expand:before{content:"\f065"}.fa-compress:before{content:"\f066"}.fa-plus:before{content:"\f067"}.fa-minus:before{content:"\f068"}.fa-asterisk:before{content:"\f069"}.fa-exclamation-circle:before{content:"\f06a"}.fa-gift:before{content:"\f06b"}.fa-leaf:before{content:"\f06c"}.fa-fire:before{content:"\f06d"}.fa-eye:before{content:"\f06e"}.fa-eye-slash:before{content:"\f070"}.fa-warning:before,.fa-exclamation-triangle:before{content:"\f071"}.fa-plane:before{content:"\f072"}.fa-calendar:before{content:"\f073"}.fa-random:before{content:"\f074"}.fa-comment:before{content:"\f075"}.fa-magnet:before{content:"\f076"}.fa-chevron-up:before{content:"\f077"}.fa-chevron-down:before{content:"\f078"}.fa-retweet:before{content:"\f079"}.fa-shopping-cart:before{content:"\f07a"}.fa-folder:before{content:"\f07b"}.fa-folder-open:before{content:"\f07c"}.fa-arrows-v:before{content:"\f07d"}.fa-arrows-h:before{content:"\f07e"}.fa-bar-chart-o:before{content:"\f080"}.fa-twitter-square:before{content:"\f081"}.fa-facebook-square:before{content:"\f082"}.fa-camera-retro:before{content:"\f083"}.fa-key:before{content:"\f084"}.fa-gears:before,.fa-cogs:before{content:"\f085"}.fa-comments:before{content:"\f086"}.fa-thumbs-o-up:before{content:"\f087"}.fa-thumbs-o-down:before{content:"\f088"}.fa-star-half:before{content:"\f089"}.fa-heart-o:before{content:"\f08a"}.fa-sign-out:before{content:"\f08b"}.fa-linkedin-square:before{content:"\f08c"}.fa-thumb-tack:before{content:"\f08d"}.fa-external-link:before{content:"\f08e"}.fa-sign-in:before{content:"\f090"}.fa-trophy:before{content:"\f091"}.fa-github-square:before{content:"\f092"}.fa-upload:before{content:"\f093"}.fa-lemon-o:before{content:"\f094"}.fa-phone:before{content:"\f095"}.fa-square-o:before{content:"\f096"}.fa-bookmark-o:before{content:"\f097"}.fa-phone-square:before{content:"\f098"}.fa-twitter:before{content:"\f099"}.fa-facebook:before{content:"\f09a"}.fa-github:before{content:"\f09b"}.fa-unlock:before{content:"\f09c"}.fa-credit-card:before{content:"\f09d"}.fa-rss:before{content:"\f09e"}.fa-hdd-o:before{content:"\f0a0"}.fa-bullhorn:before{content:"\f0a1"}.fa-bell:before{content:"\f0f3"}.fa-certificate:before{content:"\f0a3"}.fa-hand-o-right:before{content:"\f0a4"}.fa-hand-o-left:before{content:"\f0a5"}.fa-hand-o-up:before{content:"\f0a6"}.fa-hand-o-down:before{content:"\f0a7"}.fa-arrow-circle-left:before{content:"\f0a8"}.fa-arrow-circle-right:before{content:"\f0a9"}.fa-arrow-circle-up:before{content:"\f0aa"}.fa-arrow-circle-down:before{content:"\f0ab"}.fa-globe:before{content:"\f0ac"}.fa-wrench:before{content:"\f0ad"}.fa-tasks:before{content:"\f0ae"}.fa-filter:before{content:"\f0b0"}.fa-briefcase:before{content:"\f0b1"}.fa-arrows-alt:before{content:"\f0b2"}.fa-group:before,.fa-users:before{content:"\f0c0"}.fa-chain:before,.fa-link:before{content:"\f0c1"}.fa-cloud:before{content:"\f0c2"}.fa-flask:before{content:"\f0c3"}.fa-cut:before,.fa-scissors:before{content:"\f0c4"}.fa-copy:before,.fa-files-o:before{content:"\f0c5"}.fa-paperclip:before{content:"\f0c6"}.fa-save:before,.fa-floppy-o:before{content:"\f0c7"}.fa-square:before{content:"\f0c8"}.fa-navicon:before,.fa-reorder:before,.fa-bars:before{content:"\f0c9"}.fa-list-ul:before{content:"\f0ca"}.fa-list-ol:before{content:"\f0cb"}.fa-strikethrough:before{content:"\f0cc"}.fa-underline:before{content:"\f0cd"}.fa-table:before{content:"\f0ce"}.fa-magic:before{content:"\f0d0"}.fa-truck:before{content:"\f0d1"}.fa-pinterest:before{content:"\f0d2"}.fa-pinterest-square:before{content:"\f0d3"}.fa-google-plus-square:before{content:"\f0d4"}.fa-google-plus:before{content:"\f0d5"}.fa-money:before{content:"\f0d6"}.fa-caret-down:before{content:"\f0d7"}.fa-caret-up:before{content:"\f0d8"}.fa-caret-left:before{content:"\f0d9"}.fa-caret-right:before{content:"\f0da"}.fa-columns:before{content:"\f0db"}.fa-unsorted:before,.fa-sort:before{content:"\f0dc"}.fa-sort-down:before,.fa-sort-desc:before{content:"\f0dd"}.fa-sort-up:before,.fa-sort-asc:before{content:"\f0de"}.fa-envelope:before{content:"\f0e0"}.fa-linkedin:before{content:"\f0e1"}.fa-rotate-left:before,.fa-undo:before{content:"\f0e2"}.fa-legal:before,.fa-gavel:before{content:"\f0e3"}.fa-dashboard:before,.fa-tachometer:before{content:"\f0e4"}.fa-comment-o:before{content:"\f0e5"}.fa-comments-o:before{content:"\f0e6"}.fa-flash:before,.fa-bolt:before{content:"\f0e7"}.fa-sitemap:before{content:"\f0e8"}.fa-umbrella:before{content:"\f0e9"}.fa-paste:before,.fa-clipboard:before{content:"\f0ea"}.fa-lightbulb-o:before{content:"\f0eb"}.fa-exchange:before{content:"\f0ec"}.fa-cloud-download:before{content:"\f0ed"}.fa-cloud-upload:before{content:"\f0ee"}.fa-user-md:before{content:"\f0f0"}.fa-stethoscope:before{content:"\f0f1"}.fa-suitcase:before{content:"\f0f2"}.fa-bell-o:before{content:"\f0a2"}.fa-coffee:before{content:"\f0f4"}.fa-cutlery:before{content:"\f0f5"}.fa-file-text-o:before{content:"\f0f6"}.fa-building-o:before{content:"\f0f7"}.fa-hospital-o:before{content:"\f0f8"}.fa-ambulance:before{content:"\f0f9"}.fa-medkit:before{content:"\f0fa"}.fa-fighter-jet:before{content:"\f0fb"}.fa-beer:before{content:"\f0fc"}.fa-h-square:before{content:"\f0fd"}.fa-plus-square:before{content:"\f0fe"}.fa-angle-double-left:before{content:"\f100"}.fa-angle-double-right:before{content:"\f101"}.fa-angle-double-up:before{content:"\f102"}.fa-angle-double-down:before{content:"\f103"}.fa-angle-left:before{content:"\f104"}.fa-angle-right:before{content:"\f105"}.fa-angle-up:before{content:"\f106"}.fa-angle-down:before{content:"\f107"}.fa-desktop:before{content:"\f108"}.fa-laptop:before{content:"\f109"}.fa-tablet:before{content:"\f10a"}.fa-mobile-phone:before,.fa-mobile:before{content:"\f10b"}.fa-circle-o:before{content:"\f10c"}.fa-quote-left:before{content:"\f10d"}.fa-quote-right:before{content:"\f10e"}.fa-spinner:before{content:"\f110"}.fa-circle:before{content:"\f111"}.fa-mail-reply:before,.fa-reply:before{content:"\f112"}.fa-github-alt:before{content:"\f113"}.fa-folder-o:before{content:"\f114"}.fa-folder-open-o:before{content:"\f115"}.fa-smile-o:before{content:"\f118"}.fa-frown-o:before{content:"\f119"}.fa-meh-o:before{content:"\f11a"}.fa-gamepad:before{content:"\f11b"}.fa-keyboard-o:before{content:"\f11c"}.fa-flag-o:before{content:"\f11d"}.fa-flag-checkered:before{content:"\f11e"}.fa-terminal:before{content:"\f120"}.fa-code:before{content:"\f121"}.fa-mail-reply-all:before,.fa-reply-all:before{content:"\f122"}.fa-star-half-empty:before,.fa-star-half-full:before,.fa-star-half-o:before{content:"\f123"}.fa-location-arrow:before{content:"\f124"}.fa-crop:before{content:"\f125"}.fa-code-fork:before{content:"\f126"}.fa-unlink:before,.fa-chain-broken:before{content:"\f127"}.fa-question:before{content:"\f128"}.fa-info:before{content:"\f129"}.fa-exclamation:before{content:"\f12a"}.fa-superscript:before{content:"\f12b"}.fa-subscript:before{content:"\f12c"}.fa-eraser:before{content:"\f12d"}.fa-puzzle-piece:before{content:"\f12e"}.fa-microphone:before{content:"\f130"}.fa-microphone-slash:before{content:"\f131"}.fa-shield:before{content:"\f132"}.fa-calendar-o:before{content:"\f133"}.fa-fire-extinguisher:before{content:"\f134"}.fa-rocket:before{content:"\f135"}.fa-maxcdn:before{content:"\f136"}.fa-chevron-circle-left:before{content:"\f137"}.fa-chevron-circle-right:before{content:"\f138"}.fa-chevron-circle-up:before{content:"\f139"}.fa-chevron-circle-down:before{content:"\f13a"}.fa-html5:before{content:"\f13b"}.fa-css3:before{content:"\f13c"}.fa-anchor:before{content:"\f13d"}.fa-unlock-alt:before{content:"\f13e"}.fa-bullseye:before{content:"\f140"}.fa-ellipsis-h:before{content:"\f141"}.fa-ellipsis-v:before{content:"\f142"}.fa-rss-square:before{content:"\f143"}.fa-play-circle:before{content:"\f144"}.fa-ticket:before{content:"\f145"}.fa-minus-square:before{content:"\f146"}.fa-minus-square-o:before{content:"\f147"}.fa-level-up:before{content:"\f148"}.fa-level-down:before{content:"\f149"}.fa-check-square:before{content:"\f14a"}.fa-pencil-square:before{content:"\f14b"}.fa-external-link-square:before{content:"\f14c"}.fa-share-square:before{content:"\f14d"}.fa-compass:before{content:"\f14e"}.fa-toggle-down:before,.fa-caret-square-o-down:before{content:"\f150"}.fa-toggle-up:before,.fa-caret-square-o-up:before{content:"\f151"}.fa-toggle-right:before,.fa-caret-square-o-right:before{content:"\f152"}.fa-euro:before,.fa-eur:before{content:"\f153"}.fa-gbp:before{content:"\f154"}.fa-dollar:before,.fa-usd:before{content:"\f155"}.fa-rupee:before,.fa-inr:before{content:"\f156"}.fa-cny:before,.fa-rmb:before,.fa-yen:before,.fa-jpy:before{content:"\f157"}.fa-ruble:before,.fa-rouble:before,.fa-rub:before{content:"\f158"}.fa-won:before,.fa-krw:before{content:"\f159"}.fa-bitcoin:before,.fa-btc:before{content:"\f15a"}.fa-file:before{content:"\f15b"}.fa-file-text:before{content:"\f15c"}.fa-sort-alpha-asc:before{content:"\f15d"}.fa-sort-alpha-desc:before{content:"\f15e"}.fa-sort-amount-asc:before{content:"\f160"}.fa-sort-amount-desc:before{content:"\f161"}.fa-sort-numeric-asc:before{content:"\f162"}.fa-sort-numeric-desc:before{content:"\f163"}.fa-thumbs-up:before{content:"\f164"}.fa-thumbs-down:before{content:"\f165"}.fa-youtube-square:before{content:"\f166"}.fa-youtube:before{content:"\f167"}.fa-xing:before{content:"\f168"}.fa-xing-square:before{content:"\f169"}.fa-youtube-play:before{content:"\f16a"}.fa-dropbox:before{content:"\f16b"}.fa-stack-overflow:before{content:"\f16c"}.fa-instagram:before{content:"\f16d"}.fa-flickr:before{content:"\f16e"}.fa-adn:before{content:"\f170"}.fa-bitbucket:before{content:"\f171"}.fa-bitbucket-square:before{content:"\f172"}.fa-tumblr:before{content:"\f173"}.fa-tumblr-square:before{content:"\f174"}.fa-long-arrow-down:before{content:"\f175"}.fa-long-arrow-up:before{content:"\f176"}.fa-long-arrow-left:before{content:"\f177"}.fa-long-arrow-right:before{content:"\f178"}.fa-apple:before{content:"\f179"}.fa-windows:before{content:"\f17a"}.fa-android:before{content:"\f17b"}.fa-linux:before{content:"\f17c"}.fa-dribbble:before{content:"\f17d"}.fa-skype:before{content:"\f17e"}.fa-foursquare:before{content:"\f180"}.fa-trello:before{content:"\f181"}.fa-female:before{content:"\f182"}.fa-male:before{content:"\f183"}.fa-gittip:before{content:"\f184"}.fa-sun-o:before{content:"\f185"}.fa-moon-o:before{content:"\f186"}.fa-archive:before{content:"\f187"}.fa-bug:before{content:"\f188"}.fa-vk:before{content:"\f189"}.fa-weibo:before{content:"\f18a"}.fa-renren:before{content:"\f18b"}.fa-pagelines:before{content:"\f18c"}.fa-stack-exchange:before{content:"\f18d"}.fa-arrow-circle-o-right:before{content:"\f18e"}.fa-arrow-circle-o-left:before{content:"\f190"}.fa-toggle-left:before,.fa-caret-square-o-left:before{content:"\f191"}.fa-dot-circle-o:before{content:"\f192"}.fa-wheelchair:before{content:"\f193"}.fa-vimeo-square:before{content:"\f194"}.fa-turkish-lira:before,.fa-try:before{content:"\f195"}.fa-plus-square-o:before{content:"\f196"}.fa-space-shuttle:before{content:"\f197"}.fa-slack:before{content:"\f198"}.fa-envelope-square:before{content:"\f199"}.fa-wordpress:before{content:"\f19a"}.fa-openid:before{content:"\f19b"}.fa-institution:before,.fa-bank:before,.fa-university:before{content:"\f19c"}.fa-mortar-board:before,.fa-graduation-cap:before{content:"\f19d"}.fa-yahoo:before{content:"\f19e"}.fa-google:before{content:"\f1a0"}.fa-reddit:before{content:"\f1a1"}.fa-reddit-square:before{content:"\f1a2"}.fa-stumbleupon-circle:before{content:"\f1a3"}.fa-stumbleupon:before{content:"\f1a4"}.fa-delicious:before{content:"\f1a5"}.fa-digg:before{content:"\f1a6"}.fa-pied-piper-square:before,.fa-pied-piper:before{content:"\f1a7"}.fa-pied-piper-alt:before{content:"\f1a8"}.fa-drupal:before{content:"\f1a9"}.fa-joomla:before{content:"\f1aa"}.fa-language:before{content:"\f1ab"}.fa-fax:before{content:"\f1ac"}.fa-building:before{content:"\f1ad"}.fa-child:before{content:"\f1ae"}.fa-paw:before{content:"\f1b0"}.fa-spoon:before{content:"\f1b1"}.fa-cube:before{content:"\f1b2"}.fa-cubes:before{content:"\f1b3"}.fa-behance:before{content:"\f1b4"}.fa-behance-square:before{content:"\f1b5"}.fa-steam:before{content:"\f1b6"}.fa-steam-square:before{content:"\f1b7"}.fa-recycle:before{content:"\f1b8"}.fa-automobile:before,.fa-car:before{content:"\f1b9"}.fa-cab:before,.fa-taxi:before{content:"\f1ba"}.fa-tree:before{content:"\f1bb"}.fa-spotify:before{content:"\f1bc"}.fa-deviantart:before{content:"\f1bd"}.fa-soundcloud:before{content:"\f1be"}.fa-database:before{content:"\f1c0"}.fa-file-pdf-o:before{content:"\f1c1"}.fa-file-word-o:before{content:"\f1c2"}.fa-file-excel-o:before{content:"\f1c3"}.fa-file-powerpoint-o:before{content:"\f1c4"}.fa-file-photo-o:before,.fa-file-picture-o:before,.fa-file-image-o:before{content:"\f1c5"}.fa-file-zip-o:before,.fa-file-archive-o:before{content:"\f1c6"}.fa-file-sound-o:before,.fa-file-audio-o:before{content:"\f1c7"}.fa-file-movie-o:before,.fa-file-video-o:before{content:"\f1c8"}.fa-file-code-o:before{content:"\f1c9"}.fa-vine:before{content:"\f1ca"}.fa-codepen:before{content:"\f1cb"}.fa-jsfiddle:before{content:"\f1cc"}.fa-life-bouy:before,.fa-life-saver:before,.fa-support:before,.fa-life-ring:before{content:"\f1cd"}.fa-circle-o-notch:before{content:"\f1ce"}.fa-ra:before,.fa-rebel:before{content:"\f1d0"}.fa-ge:before,.fa-empire:before{content:"\f1d1"}.fa-git-square:before{content:"\f1d2"}.fa-git:before{content:"\f1d3"}.fa-hacker-news:before{content:"\f1d4"}.fa-tencent-weibo:before{content:"\f1d5"}.fa-qq:before{content:"\f1d6"}.fa-wechat:before,.fa-weixin:before{content:"\f1d7"}.fa-send:before,.fa-paper-plane:before{content:"\f1d8"}.fa-send-o:before,.fa-paper-plane-o:before{content:"\f1d9"}.fa-history:before{content:"\f1da"}.fa-circle-thin:before{content:"\f1db"}.fa-header:before{content:"\f1dc"}.fa-paragraph:before{content:"\f1dd"}.fa-sliders:before{content:"\f1de"}.fa-share-alt:before{content:"\f1e0"}.fa-share-alt-square:before{content:"\f1e1"}.fa-bomb:before{content:"\f1e2"} -------------------------------------------------------------------------------- /gui/assets/css/normalize.css: -------------------------------------------------------------------------------- 1 | article,aside,details,figcaption,figure,footer,header,hgroup,main,nav,section,summary{display:block;}audio,canvas,video{display:inline-block;}audio:not([controls]){display:none;height:0;}[hidden]{display:none;}html{font-family:sans-serif;-ms-text-size-adjust:100%;-webkit-text-size-adjust:100%;}body{margin:0;}a:focus{outline:thin dotted;}a:active,a:hover{outline:0;}h1{font-size:2em;margin:0.67em 0;}abbr[title]{border-bottom:1px dotted;}b,strong{font-weight:bold;}dfn{font-style:italic;}hr{-moz-box-sizing:content-box;box-sizing:content-box;height:0;}mark{background:#ff0;color:#000;}code,kbd,pre,samp{font-family:monospace,serif;font-size:1em;}pre{white-space:pre-wrap;}q{quotes:"\201C" "\201D" "\2018" "\2019";}small{font-size:80%;}sub,sup{font-size:75%;line-height:0;position:relative;vertical-align:baseline;}sup{top:-0.5em;}sub{bottom:-0.25em;}img{border:0;}svg:not(:root){overflow:hidden;}figure{margin:0;}fieldset{border:1px solid #c0c0c0;margin:0 2px;padding:0.35em 0.625em 0.75em;}legend{border:0;padding:0;}button,input,select,textarea{font-family:inherit;font-size:100%;margin:0;}button,input{line-height:normal;}button,select{text-transform:none;}button,html input[type="button"],input[type="reset"],input[type="submit"]{-webkit-appearance:button;cursor:pointer;}button[disabled],html input[disabled]{cursor:default;}input[type="checkbox"],input[type="radio"]{box-sizing:border-box;padding:0;}input[type="search"]{-webkit-appearance:textfield;-moz-box-sizing:content-box;-webkit-box-sizing:content-box;box-sizing:content-box;}input[type="search"]::-webkit-search-cancel-button,input[type="search"]::-webkit-search-decoration{-webkit-appearance:none;}button::-moz-focus-inner,input::-moz-focus-inner{border:0;padding:0;}textarea{overflow:auto;vertical-align:top;}table{border-collapse:collapse;border-spacing:0;} -------------------------------------------------------------------------------- /gui/assets/css/style.css: -------------------------------------------------------------------------------- 1 | html { 2 | font-size: 12px; 3 | font-family: 'Source Sans Pro', Georgia; 4 | line-height: 16px; 5 | margin: none; 6 | padding: none; 7 | } 8 | 9 | body { 10 | background: rgba(233, 233, 233, .5); 11 | user-select: none; 12 | -webkit-user-select: none; 13 | } 14 | 15 | pre { 16 | white-space: pre-wrap; 17 | } 18 | 19 | h1, h2, h3, h4, h5, h6 { 20 | font-family: 'Montserrat'; 21 | color: #333; 22 | } 23 | 24 | .title h1 { 25 | color: purple; 26 | } 27 | 28 | table tr:nth-child(even) { 29 | background: rgba(0,0,0,0.1); 30 | } 31 | 32 | a { 33 | position: relative; 34 | margin: -.5em; 35 | padding: .5em; 36 | color: #08C; 37 | text-decoration: none; 38 | transition: all 0.3s ease-out; 39 | } 40 | a:hover { color: #0AF; } 41 | 42 | *, *:before, *:after { 43 | -webkit-box-sizing: border-box; 44 | box-sizing: border-box; 45 | } 46 | 47 | code { 48 | font-family: Monaco; 49 | font-size: .75em; 50 | font-weight: 300; 51 | } 52 | .clearfix:before, 53 | .clearfix:after { 54 | display: table; 55 | content: " "; 56 | } 57 | 58 | .clearfix:after { 59 | clear: both; 60 | } 61 | 62 | /******************************************************************************* 63 | General Layout 64 | *******************************************************************************/ 65 | .viewport { 66 | position: fixed; 67 | width: 100%; 68 | min-width: 600px; 69 | height: 100%; 70 | background: rgba(253, 253, 253, .5); 71 | } 72 | 73 | .header { 74 | position: relative; 75 | padding-bottom: 20px; 76 | max-height: 100px; 77 | width: inherit; 78 | height: 17%; 79 | min-width: 600px; 80 | border-bottom: 5px #900080 solid !important; 81 | padding: 0 10px 0 35px; 82 | } 83 | 84 | .pane { 85 | overflow: scroll; 86 | width: 100%; 87 | height: 83%; 88 | border-top: 4px solid black; 89 | padding: 2em 35px 3em 35px; 90 | } 91 | 92 | .preloader { 93 | padding: 10% 0 0 48%; 94 | width: 100%; 95 | height: 100%; 96 | } 97 | 98 | .preloader h2 { 99 | margin-bottom: 28px; 100 | margin-left: -32px; 101 | color: black; 102 | } 103 | 104 | #updating-overlay { 105 | position: fixed; 106 | display: none; 107 | margin: 0 25%; 108 | z-index: 200; 109 | width: 45%; 110 | height: 30%; 111 | min-height: 400px; 112 | border: 5px solid black; 113 | background-color: #efefef; 114 | text-align: center; 115 | opacity: .8; 116 | top: 15%; 117 | } 118 | 119 | #updating-overlay h2 { 120 | color: black; 121 | margin-top: 20%; 122 | } 123 | 124 | .box { 125 | position: relative; 126 | z-index: 0; 127 | float: left; 128 | margin: 1em; 129 | padding: 1em; 130 | width: 30%; 131 | height: 200px; 132 | border: 1px black solid; 133 | } 134 | 135 | .disabled { 136 | opacity: 0.5; 137 | pointer-events: none; 138 | } 139 | 140 | .header h1 { 141 | margin: 10px 0 0 0; 142 | font-size: 3em; 143 | } 144 | 145 | .title { 146 | padding: 2em 0 0 0em; 147 | height: 100%; 148 | } 149 | 150 | .author { 151 | content: 'Me.'; 152 | } 153 | 154 | .wficon { 155 | z-index: 0; 156 | width: 72px; 157 | height: 72px; 158 | } 159 | 160 | .wficon-container { 161 | position: absolute; 162 | bottom: 17px; 163 | padding-right: 25px; 164 | width: 100%; 165 | text-align: right; 166 | } 167 | 168 | .separator { 169 | margin: 0; 170 | width: 100%; 171 | height: 3px; 172 | border: 0px; 173 | background: black; 174 | text-align: center; 175 | } 176 | 177 | .blacklisted { 178 | position: absolute; 179 | z-index: 1000; 180 | margin: -1em 0 0 -1em; 181 | width: 100%; 182 | height: 100%; 183 | background: #ccc; 184 | opacity: .3; 185 | display: block; 186 | text-align: center; 187 | padding: 0; 188 | } 189 | 190 | .disabled-x { 191 | /*float: left;*/ 192 | 193 | font-size: 18em; 194 | /*margin: 0 30%;*/ 195 | } 196 | /*'line-height: 190px; font-size: 250px; padding-left: 20%;'*/ 197 | 198 | .hide { 199 | display: none; 200 | } 201 | 202 | .blist, .open-directory, #open-backup-dir { 203 | cursor: pointer; 204 | } 205 | 206 | .icon { 207 | margin-right: 1em; 208 | margin-bottom: 1em; 209 | width: 24px; 210 | } 211 | 212 | .nav > ul > li > div > div { 213 | position: relative; 214 | float: left; 215 | 216 | } 217 | 218 | .nav > ul > li > div { 219 | padding: .25em; 220 | min-height: 20px; 221 | border-top: 1px black solid; 222 | } 223 | 224 | .nav ul li { 225 | clear: both; 226 | margin: 1em 1em; 227 | list-style: none; 228 | line-height: 24px; 229 | 230 | } 231 | 232 | .short { 233 | float: left; 234 | overflow: hidden; 235 | padding: 0; 236 | max-width: 100%; 237 | height: 3em; 238 | text-overflow: ellipsis; 239 | white-space: nowrap; 240 | } 241 | .short p { 242 | overflow: hidden; 243 | margin: 0; 244 | height: 3em; 245 | text-overflow: ellipsis; 246 | } 247 | 248 | .backups li { 249 | margin: 1em; 250 | list-style: none; 251 | font-size: 1.2em; 252 | 253 | } 254 | 255 | .backup-box h2 { 256 | color: black; 257 | } 258 | 259 | .backup-box { 260 | border-color: black; 261 | } 262 | 263 | .delete-backup { 264 | cursor: pointer; 265 | } 266 | 267 | .open-backup-dir h3 { 268 | color: black; 269 | cursor: pointer; 270 | } 271 | 272 | .open-directory:hover { 273 | color: #800080; 274 | transition: color .25s; 275 | } 276 | 277 | .open-directory { 278 | color: black; 279 | margin-left: .5em; 280 | transition: color .25s; 281 | } 282 | 283 | /**** Dialogs ****/ 284 | .ui-dialog { 285 | border: 4px solid black; 286 | border-top: 5px solid black; 287 | padding: 1em 3em; 288 | width: auto; 289 | max-width: 100%; 290 | background: rgba(233, 233, 233, 1); 291 | } 292 | 293 | .ui-dialog-titlebar, .ui-dialog-titlebar-close, .ui-dialog-title { 294 | display: none; 295 | } 296 | 297 | .ui-dialog-content { 298 | text-align: center; 299 | font-size: 2em; 300 | line-height: 1em; 301 | padding: .5em 0 0 0; 302 | font-family: 'Source Sans Pro'; 303 | } 304 | 305 | #delete-backup-dialog { 306 | font-size: 1.5em; 307 | } 308 | 309 | .ui-dialog-buttonpane { 310 | text-align: right; 311 | } 312 | 313 | .ui-dialog-buttonset { 314 | outline: none; 315 | } 316 | 317 | .ui-dialog-buttonset button { 318 | outline: none; 319 | background: none; 320 | font-size: 1.5em; 321 | border-radius:6px; 322 | border: 3px solid black; 323 | padding: .15em 1em .25em 1em; 324 | margin: 0 1em 0 0; 325 | background: #800080; 326 | color: #efefef; 327 | transition: background .25s,color .25s; 328 | } 329 | 330 | .ui-dialog-buttonset button:hover { 331 | background: #efefef; 332 | color: #800080; 333 | transition: background .25s,color .25s; 334 | } 335 | 336 | .fa { 337 | opacity: .8; 338 | } 339 | 340 | 341 | 342 | .delete-backup:hover { 343 | color: #A60000; 344 | -webkit-transition: color .25s ease-in; 345 | transition: color .25s ease-in; 346 | } 347 | 348 | /******************************************************************************* 349 | * Status 350 | *******************************************************************************/ 351 | .updates-available { 352 | cursor: pointer 353 | } 354 | .detail-accordion > h3 { 355 | margin-bottom: 1em; 356 | margin-left: 2em; 357 | padding: 2px 0; 358 | max-width: 75px; 359 | border-top: 1px dashed black; 360 | border-bottom: 1px dashed black; 361 | color: gray; 362 | text-align: center; 363 | letter-spacing: 1px; 364 | font-weight: normal; 365 | font-size: 1em !important; 366 | font-family: 'Montserrat', Georgia !important; 367 | line-height: 1.2em; 368 | cursor: pointer; 369 | } 370 | .detail-accordion > div > div, .detail-accordion > p { 371 | position: relative; 372 | float: left; 373 | clear: both; 374 | margin: .25em 0 1em 2.5em; 375 | padding: .75em 1em; 376 | width: 600px; 377 | border: 1px solid black; 378 | font-size: .75em; 379 | line-height: 1.5em; 380 | } 381 | 382 | .ui-accordion .ui-accordion-content{ 383 | overflow: auto; 384 | } 385 | 386 | .url-open, .update-manifest { 387 | clear: none !important; 388 | width: 90px; 389 | border: none !important; 390 | border-bottom: 1px gray dashed !important; 391 | color: gray; 392 | cursor: pointer; 393 | } 394 | 395 | .update-manifest { 396 | width: 126px !important; 397 | border: none !important; 398 | border-bottom: 1px gray dashed !important; 399 | color: gray; 400 | cursor: pointer; 401 | } 402 | 403 | .status-body { 404 | margin: 3em 0 0 2%; 405 | min-width: 600px; 406 | width: 95%; 407 | font-size: 1.5em; 408 | } 409 | 410 | .status-body > div { 411 | margin-bottom: 1em; 412 | } 413 | 414 | /******************************************************************************* 415 | * About 416 | ******************************************************************************/ 417 | 418 | #about-section { 419 | width: 95%; 420 | font-size: 1.5em; 421 | line-height: 1.5em; 422 | } 423 | 424 | #about-section p { 425 | margin-left: 2em; 426 | } 427 | 428 | /******************************************************************************* 429 | * Updates 430 | ******************************************************************************/ 431 | 432 | .updates-complete-backdrop { 433 | position: absolute; 434 | clear: none; 435 | width: 93%; 436 | height: 60%; 437 | background: url( '../../assets/images/packal-man.jpg' ) no-repeat center; 438 | background-size: contain; 439 | opacity: .25; 440 | } 441 | 442 | .center-me-please { 443 | float: left; 444 | width: 100%; 445 | padding: 15%; 446 | display: table-cell; 447 | text-align: center; 448 | font-size: 1.5em; 449 | line-height: 2em; 450 | vertical-align: middle; 451 | } 452 | 453 | .update-box { 454 | margin: 25px 0 0 2%; 455 | padding: .1em; 456 | width: 90%; 457 | height: 110px; 458 | border: 1px solid black; 459 | } 460 | 461 | .update-icon { 462 | position: relative; 463 | display: inline-block; 464 | float: left; 465 | padding: 25px 0; 466 | width: 125px; 467 | height: 110px; 468 | text-align: center; 469 | opacity: .9; 470 | 471 | } 472 | 473 | .update-icon img { 474 | max-width: 48px; 475 | } 476 | 477 | .update-content { 478 | position: relative; 479 | float: left; 480 | } 481 | 482 | .update-button { 483 | /*border: 2px black solid;*/ 484 | /*font-size: 1.5em;*/ 485 | float: right; 486 | margin: 1em 1em 0 0; 487 | padding: 15px 50px 0 0; 488 | /*width: 100px;*/ 489 | height: 110px; 490 | text-align: center; 491 | } 492 | 493 | .update-box button:focus { 494 | outline: none; 495 | } 496 | 497 | button.css3button { 498 | padding: 10px 20px; 499 | border: 3px solid black; 500 | background: #800080; 501 | color: #e6e6e6; 502 | font-size: 1.25em; 503 | transition: background .25s, color .25s, border-color .25s; 504 | } 505 | 506 | button.css3button:hover { 507 | background: #ccc; 508 | color: black; 509 | border-color: black; 510 | transition: background .25s, color .25s, border-color .25s; 511 | } 512 | 513 | /******************************************************************************* 514 | Backups 515 | *******************************************************************************/ 516 | 517 | .backup-box { 518 | margin: 25px 0 0 2%; 519 | padding: 1em; 520 | width: 90%; 521 | border: 1px solid black; 522 | } 523 | 524 | /******************************************************************************* 525 | Settings 526 | *******************************************************************************/ 527 | /* general style for the form */ 528 | .settings-form { 529 | margin: 0.3em auto 0 auto; 530 | width: 95%; 531 | font-size: 1.5em; 532 | line-height: 2em; 533 | } 534 | 535 | .firstRun { 536 | font-size: 1.5em; 537 | margin: 2em 0 -1em 2em; 538 | background: #ddd; 539 | width: 40em; 540 | padding: .25em 2em; 541 | text-align: center; 542 | border: 3px dashed gray; 543 | } 544 | 545 | /* normalize the input elements, make them look like everything else */ 546 | .settings-form input, 547 | .settings-form select, 548 | .settings-form button { 549 | display: inline-block; 550 | margin: 0; 551 | padding: 0; 552 | border: none; 553 | background: transparent; 554 | color: inherit; 555 | font-weight: inherit; 556 | font-size: inherit; 557 | font-family: inherit; 558 | line-height: inherit; 559 | -webkit-appearance: none; 560 | } 561 | 562 | .settings-form input:focus { 563 | outline: none; 564 | } 565 | 566 | .settings-form input, 567 | .settings-form select { 568 | display: inline-block; 569 | margin: 0 .25em; 570 | padding: 0; 571 | border-bottom: 1px dashed #b14943; 572 | /*color: #b14943;*/ 573 | color: purple; 574 | line-height: 1.2em; 575 | cursor: pointer; 576 | } 577 | 578 | /* These are blank spans to help resize form elements. */ 579 | #username, 580 | #authorName, 581 | #packal-username, 582 | #number-of-backups, 583 | #workflow-reporting, 584 | #force-packal, 585 | #notification-options { 586 | display: none; 587 | } 588 | 589 | /******************************************************************************* 590 | Tooltips 591 | *******************************************************************************/ 592 | .ui-tooltip, .arrow:after { 593 | background-color: rgba(128, 0, 128, 0.8); 594 | border: 2px solid #cfcfcf; 595 | } 596 | .ui-tooltip { 597 | padding: 10px 10px; 598 | color: white; 599 | border-radius: 20px; 600 | max-width: 20em; 601 | text-align: justify; 602 | /*font: bold 14px "Helvetica Neue", Sans-Serif;*/ 603 | /*text-transform: uppercase;*/ 604 | box-shadow: 0 0 3px black; 605 | } 606 | .arrow { 607 | width: 70px; 608 | height: 16px; 609 | overflow: hidden; 610 | position: absolute; 611 | left: 50%; 612 | margin-left: -35px; 613 | bottom: -16px; 614 | } 615 | .arrow.top { 616 | top: -16px; 617 | bottom: auto; 618 | } 619 | .arrow.left { 620 | left: 20%; 621 | } 622 | .arrow:after { 623 | content: ""; 624 | position: absolute; 625 | left: 20px; 626 | top: -20px; 627 | width: 25px; 628 | height: 25px; 629 | box-shadow: 6px 5px 9px -9px black; 630 | -webkit-transform: rotate(45deg); 631 | -moz-transform: rotate(45deg); 632 | -ms-transform: rotate(45deg); 633 | -o-transform: rotate(45deg); 634 | tranform: rotate(45deg); 635 | } 636 | .arrow.top:after { 637 | bottom: -20px; 638 | top: auto; 639 | } 640 | 641 | 642 | 643 | /******************************************************************************* 644 | Menu 645 | *******************************************************************************/ 646 | 647 | .bt-menu { 648 | position: fixed; 649 | top: 0; 650 | left: 0; 651 | width: 100%; 652 | min-width: 600px; 653 | height: 0; 654 | border-width: 0px; 655 | border-style: solid; 656 | border-color: #333; 657 | background-color: rgba(0,0,0,0); 658 | -webkit-transition: border-width 0.3s, background-color 0.3s, height 0s 0.3s; 659 | transition: border-width 0.3s, background-color 0.3s, height 0s 0.3s; 660 | -webkit-backface-visibility: hidden; 661 | } 662 | 663 | .bt-menu.bt-menu-open { 664 | height: 100%; 665 | border-width: 90px 30px 30px 30px; 666 | background-color: rgba(0,0,0,0.3); 667 | -webkit-transition: border-width 0.3s, background-color 0.3s; 668 | transition: border-width 0.3s, background-color 0.3s; 669 | } 670 | 671 | .bt-menu-open { 672 | z-index: 1000; 673 | } 674 | 675 | .bt-overlay { 676 | position: absolute; 677 | width: 100%; 678 | min-width: 600px; 679 | } 680 | 681 | .bt-menu-open .bt-overlay { 682 | height: 100%; 683 | } 684 | 685 | .bt-menu-trigger { 686 | position: fixed; 687 | top: 22px; 688 | left: 50%; 689 | z-index: 100; 690 | display: block; 691 | margin-left: -25px; 692 | width: 50px; 693 | height: 50px; 694 | cursor: pointer; 695 | } 696 | 697 | .bt-menu-trigger span { 698 | position: absolute; 699 | top: 50%; 700 | left: 0; 701 | display: block; 702 | width: 100%; 703 | height: 8%; 704 | background-color: purple; 705 | font-size: 0px; 706 | -webkit-transition: background-color 0.3s; 707 | transition: background-color 0.3s; 708 | -webkit-touch-callout: none; 709 | -webkit-user-select: none; 710 | user-select: none; 711 | } 712 | 713 | .bt-menu-open .bt-menu-trigger span { 714 | background-color: #fff; 715 | } 716 | 717 | .bt-menu-trigger span:before, 718 | .bt-menu-trigger span:after { 719 | position: absolute; 720 | left: 0; 721 | width: 100%; 722 | height: 100%; 723 | background: purple; 724 | content: ''; 725 | -webkit-transition: -webkit-transform 0.3s, background-color 0.3s; 726 | transition: transform 0.3s, background-color 0.3s; 727 | } 728 | 729 | .bt-menu-trigger span:before { 730 | -webkit-transform: translateY(-250%); 731 | transform: translateY(-250%); 732 | } 733 | 734 | .bt-menu-trigger span:after { 735 | -webkit-transform: translateY(250%); 736 | transform: translateY(250%); 737 | } 738 | 739 | .bt-menu-open .bt-menu-trigger span:before, 740 | .bt-menu-open .bt-menu-trigger span:after { 741 | background-color: #fff; 742 | } 743 | 744 | .bt-menu-open .bt-menu-trigger span:before { 745 | -webkit-transform: translateY(-350%); 746 | transform: translateY(-350%); 747 | } 748 | 749 | .bt-menu-open .bt-menu-trigger span:after { 750 | -webkit-transform: translateY(350%); 751 | transform: translateY(350%); 752 | } 753 | 754 | .bt-menu ul { 755 | position: fixed; 756 | top: 0px; 757 | left: 0; 758 | margin: 0; 759 | padding: 0; 760 | width: 100%; 761 | height: 0; 762 | list-style: none; 763 | text-align: center; 764 | white-space: nowrap; 765 | -webkit-transition: height 0s 0.3s; 766 | transition: height 0s 0.3s; 767 | } 768 | 769 | .bt-menu.bt-menu-open ul { 770 | height: 90px; 771 | -webkit-transition: none; 772 | transition: none; 773 | } 774 | 775 | .bt-menu ul li, 776 | .bt-menu ul li a { 777 | text-align: center; 778 | } 779 | 780 | .bt-menu ul li { 781 | display: inline-block; 782 | margin: 0 2%; 783 | width: 5%; 784 | height: 90px; 785 | line-height: 90px; 786 | opacity: 0; 787 | -webkit-transition: -webkit-transform 0.3s, opacity 0.3s; 788 | transition: transform 0.3s, opacity 0.3s; 789 | -webkit-transform: translate3d(0,-100%,0); 790 | transform: translate3d(0,-100%,0); 791 | } 792 | 793 | .bt-menu ul li:nth-child(3) { 794 | margin-right: 70px; 795 | } 796 | 797 | .bt-menu ul li:nth-child(4) { 798 | margin-left: 70px; 799 | } 800 | 801 | .bt-menu.bt-menu-open ul li { 802 | opacity: 1; 803 | -webkit-transform: translate3d(0,0,0); 804 | transform: translate3d(0,0,0); 805 | } 806 | 807 | .bt-menu.bt-menu-open ul li:first-child, 808 | .bt-menu.bt-menu-open ul li:last-child { 809 | -webkit-transition-delay: 0.15s; 810 | transition-delay: 0.15s; 811 | } 812 | 813 | .bt-menu.bt-menu-open ul li:nth-child(2), 814 | .bt-menu.bt-menu-open ul li:nth-last-child(2) { 815 | -webkit-transition-delay: 0.1s; 816 | transition-delay: 0.1s; 817 | } 818 | 819 | .bt-menu.bt-menu-open ul li:nth-child(3), 820 | .bt-menu.bt-menu-open ul li:nth-last-child(3) { 821 | -webkit-transition-delay: 0.05s; 822 | transition-delay: 0.05s; 823 | } 824 | 825 | .bt-menu ul li a { 826 | display: block; 827 | outline: none; 828 | color: #fff; 829 | text-decoration: none; 830 | text-transform: uppercase; 831 | letter-spacing: 1px; 832 | font-size: 1.15em; 833 | -webkit-transition: color 0.2s; 834 | transition: color 0.2s; 835 | } 836 | 837 | .bt-menu ul li a:before { 838 | display: none; 839 | font-size: 32px; 840 | } 841 | 842 | .bt-menu ul li a:hover, 843 | .bt-menu ul li a:focus { 844 | color: #2980b9; 845 | } 846 | 847 | /************ /MENU *************/ 848 | 849 | #fusion_ad { 850 | font-size: .7em; 851 | max-width: 135px; 852 | position: absolute; 853 | right: 5em; 854 | } 855 | .fusionentire > a { 856 | clear: both; 857 | display: inline-block; 858 | 859 | } 860 | .fusionimg { 861 | margin-bottom: 5px; 862 | } 863 | .bsap { 864 | margin-top: 2px; 865 | margin-bottom: 1em; 866 | } -------------------------------------------------------------------------------- /gui/assets/fonts/Montserrat/IQHow_FEYlDC4Gzy_m8fcnbFhgvWbfSbdVg11QabG8w.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/gui/assets/fonts/Montserrat/IQHow_FEYlDC4Gzy_m8fcnbFhgvWbfSbdVg11QabG8w.woff -------------------------------------------------------------------------------- /gui/assets/fonts/Montserrat/montserrat.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Montserrat'; 3 | font-style: normal; 4 | font-weight: 400; 5 | src: local('Montserrat-Regular'), url('zhcz-_WihjSQC0oHJ9TCYL3hpw3pgy2gAi-Ip7WPMi0.woff') format('woff'); 6 | } 7 | @font-face { 8 | font-family: 'Montserrat'; 9 | font-style: normal; 10 | font-weight: 700; 11 | src: local('Montserrat-Bold'), url('IQHow_FEYlDC4Gzy_m8fcnbFhgvWbfSbdVg11QabG8w.woff') format('woff'); 12 | } 13 | -------------------------------------------------------------------------------- /gui/assets/fonts/Montserrat/zhcz-_WihjSQC0oHJ9TCYL3hpw3pgy2gAi-Ip7WPMi0.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/gui/assets/fonts/Montserrat/zhcz-_WihjSQC0oHJ9TCYL3hpw3pgy2gAi-Ip7WPMi0.woff -------------------------------------------------------------------------------- /gui/assets/fonts/Source Sans Pro/M2Jd71oPJhLKp0zdtTvoM_bce-VcyLJMRR1FC9qWbU8.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/gui/assets/fonts/Source Sans Pro/M2Jd71oPJhLKp0zdtTvoM_bce-VcyLJMRR1FC9qWbU8.woff -------------------------------------------------------------------------------- /gui/assets/fonts/Source Sans Pro/ODelI1aHBYDBqgeIAH2zlNHq-FFgoDNV3GTKpHwuvtI.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/gui/assets/fonts/Source Sans Pro/ODelI1aHBYDBqgeIAH2zlNHq-FFgoDNV3GTKpHwuvtI.woff -------------------------------------------------------------------------------- /gui/assets/fonts/Source Sans Pro/fpTVHK8qsXbIeTHTrnQH6H7XMO6S-yJpg1torQFmnv33rGVtsTkPsbDajuO5ueQw.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/gui/assets/fonts/Source Sans Pro/fpTVHK8qsXbIeTHTrnQH6H7XMO6S-yJpg1torQFmnv33rGVtsTkPsbDajuO5ueQw.woff -------------------------------------------------------------------------------- /gui/assets/fonts/Source Sans Pro/fpTVHK8qsXbIeTHTrnQH6J4LxuTxC3JbLqy8IUt0K1T3rGVtsTkPsbDajuO5ueQw.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/gui/assets/fonts/Source Sans Pro/fpTVHK8qsXbIeTHTrnQH6J4LxuTxC3JbLqy8IUt0K1T3rGVtsTkPsbDajuO5ueQw.woff -------------------------------------------------------------------------------- /gui/assets/fonts/Source Sans Pro/fpTVHK8qsXbIeTHTrnQH6MJvEBP8Mx4XcuTrPnlPwTr3rGVtsTkPsbDajuO5ueQw.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/gui/assets/fonts/Source Sans Pro/fpTVHK8qsXbIeTHTrnQH6MJvEBP8Mx4XcuTrPnlPwTr3rGVtsTkPsbDajuO5ueQw.woff -------------------------------------------------------------------------------- /gui/assets/fonts/Source Sans Pro/fpTVHK8qsXbIeTHTrnQH6OE3EfhCMemg4BxrY2mmJD_3rGVtsTkPsbDajuO5ueQw.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/gui/assets/fonts/Source Sans Pro/fpTVHK8qsXbIeTHTrnQH6OE3EfhCMemg4BxrY2mmJD_3rGVtsTkPsbDajuO5ueQw.woff -------------------------------------------------------------------------------- /gui/assets/fonts/Source Sans Pro/fpTVHK8qsXbIeTHTrnQH6PgYMAt3u4NmhhzLLLPJ5qH3rGVtsTkPsbDajuO5ueQw.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/gui/assets/fonts/Source Sans Pro/fpTVHK8qsXbIeTHTrnQH6PgYMAt3u4NmhhzLLLPJ5qH3rGVtsTkPsbDajuO5ueQw.woff -------------------------------------------------------------------------------- /gui/assets/fonts/Source Sans Pro/source.sans.pro.css: -------------------------------------------------------------------------------- 1 | @font-face { 2 | font-family: 'Source Sans Pro'; 3 | font-style: normal; 4 | font-weight: 200; 5 | src: url('toadOcfmlt9b38dHJxOBGC-IiwWNzQT_LXAtr-Ee9Pw.woff') format('woff'); 6 | } 7 | @font-face { 8 | font-family: 'Source Sans Pro'; 9 | font-style: normal; 10 | font-weight: 300; 11 | src: url('toadOcfmlt9b38dHJxOBGMVNtom4QlEDNJaqqqzqdSs.woff') format('woff'); 12 | } 13 | @font-face { 14 | font-family: 'Source Sans Pro'; 15 | font-style: normal; 16 | font-weight: 400; 17 | src: url('ODelI1aHBYDBqgeIAH2zlNHq-FFgoDNV3GTKpHwuvtI.woff') format('woff'); 18 | } 19 | @font-face { 20 | font-family: 'Source Sans Pro'; 21 | font-style: normal; 22 | font-weight: 600; 23 | src: url('toadOcfmlt9b38dHJxOBGCmgpmuQqK2I-L2S9cF65Ek.woff') format('woff'); 24 | } 25 | @font-face { 26 | font-family: 'Source Sans Pro'; 27 | font-style: normal; 28 | font-weight: 700; 29 | src: url('toadOcfmlt9b38dHJxOBGIqjGYJUyOXcBwUQbRaNH6c.woff') format('woff'); 30 | } 31 | @font-face { 32 | font-family: 'Source Sans Pro'; 33 | font-style: normal; 34 | font-weight: 900; 35 | src: url('toadOcfmlt9b38dHJxOBGC7eGk4PJkA83J1AXCmgUfc.woff') format('woff'); 36 | } 37 | @font-face { 38 | font-family: 'Source Sans Pro'; 39 | font-style: italic; 40 | font-weight: 200; 41 | src: url('fpTVHK8qsXbIeTHTrnQH6OE3EfhCMemg4BxrY2mmJD_3rGVtsTkPsbDajuO5ueQw.woff') format('woff'); 42 | } 43 | @font-face { 44 | font-family: 'Source Sans Pro'; 45 | font-style: italic; 46 | font-weight: 300; 47 | src: url('fpTVHK8qsXbIeTHTrnQH6H7XMO6S-yJpg1torQFmnv33rGVtsTkPsbDajuO5ueQw.woff') format('woff'); 48 | } 49 | @font-face { 50 | font-family: 'Source Sans Pro'; 51 | font-style: italic; 52 | font-weight: 400; 53 | src: url('M2Jd71oPJhLKp0zdtTvoM_bce-VcyLJMRR1FC9qWbU8.woff') format('woff'); 54 | } 55 | @font-face { 56 | font-family: 'Source Sans Pro'; 57 | font-style: italic; 58 | font-weight: 600; 59 | src: url('fpTVHK8qsXbIeTHTrnQH6J4LxuTxC3JbLqy8IUt0K1T3rGVtsTkPsbDajuO5ueQw.woff') format('woff'); 60 | } 61 | @font-face { 62 | font-family: 'Source Sans Pro'; 63 | font-style: italic; 64 | font-weight: 700; 65 | src: url('fpTVHK8qsXbIeTHTrnQH6PgYMAt3u4NmhhzLLLPJ5qH3rGVtsTkPsbDajuO5ueQw.woff') format('woff'); 66 | } 67 | @font-face { 68 | font-family: 'Source Sans Pro'; 69 | font-style: italic; 70 | font-weight: 900; 71 | src: url('fpTVHK8qsXbIeTHTrnQH6MJvEBP8Mx4XcuTrPnlPwTr3rGVtsTkPsbDajuO5ueQw.woff') format('woff'); 72 | } -------------------------------------------------------------------------------- /gui/assets/fonts/Source Sans Pro/toadOcfmlt9b38dHJxOBGC-IiwWNzQT_LXAtr-Ee9Pw.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/gui/assets/fonts/Source Sans Pro/toadOcfmlt9b38dHJxOBGC-IiwWNzQT_LXAtr-Ee9Pw.woff -------------------------------------------------------------------------------- /gui/assets/fonts/Source Sans Pro/toadOcfmlt9b38dHJxOBGC7eGk4PJkA83J1AXCmgUfc.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/gui/assets/fonts/Source Sans Pro/toadOcfmlt9b38dHJxOBGC7eGk4PJkA83J1AXCmgUfc.woff -------------------------------------------------------------------------------- /gui/assets/fonts/Source Sans Pro/toadOcfmlt9b38dHJxOBGCmgpmuQqK2I-L2S9cF65Ek.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/gui/assets/fonts/Source Sans Pro/toadOcfmlt9b38dHJxOBGCmgpmuQqK2I-L2S9cF65Ek.woff -------------------------------------------------------------------------------- /gui/assets/fonts/Source Sans Pro/toadOcfmlt9b38dHJxOBGIqjGYJUyOXcBwUQbRaNH6c.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/gui/assets/fonts/Source Sans Pro/toadOcfmlt9b38dHJxOBGIqjGYJUyOXcBwUQbRaNH6c.woff -------------------------------------------------------------------------------- /gui/assets/fonts/Source Sans Pro/toadOcfmlt9b38dHJxOBGMVNtom4QlEDNJaqqqzqdSs.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/gui/assets/fonts/Source Sans Pro/toadOcfmlt9b38dHJxOBGMVNtom4QlEDNJaqqqzqdSs.woff -------------------------------------------------------------------------------- /gui/assets/fonts/fontawesome-webfont.woff: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/gui/assets/fonts/fontawesome-webfont.woff -------------------------------------------------------------------------------- /gui/assets/fonts/fonts.css: -------------------------------------------------------------------------------- 1 | /*@font-face { 2 | font-family: 'Source Sans Pro'; 3 | font-style: normal; 4 | font-weight: 200; 5 | src: local('Source Sans Pro ExtraLight'), local('SourceSansPro-ExtraLight'), url('assets/fonts/Source Sans Pro/toadOcfmlt9b38dHJxOBGC-IiwWNzQT_LXAtr-Ee9Pw.woff') format('woff'); 6 | } 7 | @font-face { 8 | font-family: 'Source Sans Pro'; 9 | font-style: normal; 10 | font-weight: 300; 11 | src: local('Source Sans Pro Light'), local('SourceSansPro-Light'), url('assets/fonts/Source Sans Pro/toadOcfmlt9b38dHJxOBGMVNtom4QlEDNJaqqqzqdSs.woff') format('woff'); 12 | }*/ 13 | @font-face { 14 | font-family: 'Source Sans Pro'; 15 | font-style: normal; 16 | font-weight: 400; 17 | src: local('Source Sans Pro'), local('SourceSansPro-Regular'), url('assets/fonts/Source Sans Pro/ODelI1aHBYDBqgeIAH2zlNHq-FFgoDNV3GTKpHwuvtI.woff') format('woff'); 18 | } 19 | /*@font-face { 20 | font-family: 'Source Sans Pro'; 21 | font-style: normal; 22 | font-weight: 600; 23 | src: local('Source Sans Pro Semibold'), local('SourceSansPro-Semibold'), url('assets/fonts/Source Sans Pro/toadOcfmlt9b38dHJxOBGCmgpmuQqK2I-L2S9cF65Ek.woff') format('woff'); 24 | }*/ 25 | @font-face { 26 | font-family: 'Source Sans Pro'; 27 | font-style: normal; 28 | font-weight: 700; 29 | src: local('Source Sans Pro Bold'), local('SourceSansPro-Bold'), url('assets/fonts/Source Sans Pro/toadOcfmlt9b38dHJxOBGIqjGYJUyOXcBwUQbRaNH6c.woff') format('woff'); 30 | } 31 | @font-face { 32 | font-family: 'Source Sans Pro'; 33 | font-style: normal; 34 | font-weight: 900; 35 | src: local('Source Sans Pro Black'), local('SourceSansPro-Black'), url('assets/fonts/Source Sans Pro/toadOcfmlt9b38dHJxOBGC7eGk4PJkA83J1AXCmgUfc.woff') format('woff'); 36 | } 37 | /*@font-face { 38 | font-family: 'Source Sans Pro'; 39 | font-style: italic; 40 | font-weight: 200; 41 | src: local('Source Sans Pro ExtraLight Italic'), local('SourceSansPro-ExtraLightIt'), url('assets/fonts/Source Sans Pro/fpTVHK8qsXbIeTHTrnQH6OE3EfhCMemg4BxrY2mmJD_3rGVtsTkPsbDajuO5ueQw.woff') format('woff'); 42 | } 43 | @font-face { 44 | font-family: 'Source Sans Pro'; 45 | font-style: italic; 46 | font-weight: 300; 47 | src: local('Source Sans Pro Light Italic'), local('SourceSansPro-LightIt'), url('assets/fonts/Source Sans Pro/fpTVHK8qsXbIeTHTrnQH6H7XMO6S-yJpg1torQFmnv33rGVtsTkPsbDajuO5ueQw.woff') format('woff'); 48 | }*/ 49 | @font-face { 50 | font-family: 'Source Sans Pro'; 51 | font-style: italic; 52 | font-weight: 400; 53 | src: local('Source Sans Pro Italic'), local('SourceSansPro-It'), url('assets/fonts/Source Sans Pro/M2Jd71oPJhLKp0zdtTvoM_bce-VcyLJMRR1FC9qWbU8.woff') format('woff'); 54 | } 55 | /*@font-face { 56 | font-family: 'Source Sans Pro'; 57 | font-style: italic; 58 | font-weight: 600; 59 | src: local('Source Sans Pro Semibold Italic'), local('SourceSansPro-SemiboldIt'), url('assets/fonts/Source Sans Pro/fpTVHK8qsXbIeTHTrnQH6J4LxuTxC3JbLqy8IUt0K1T3rGVtsTkPsbDajuO5ueQw.woff') format('woff'); 60 | } 61 | @font-face { 62 | font-family: 'Source Sans Pro'; 63 | font-style: italic; 64 | font-weight: 700; 65 | src: local('Source Sans Pro Bold Italic'), local('SourceSansPro-BoldIt'), url('assets/fonts/Source Sans Pro/fpTVHK8qsXbIeTHTrnQH6PgYMAt3u4NmhhzLLLPJ5qH3rGVtsTkPsbDajuO5ueQw.woff') format('woff'); 66 | } 67 | @font-face { 68 | font-family: 'Source Sans Pro'; 69 | font-style: italic; 70 | font-weight: 900; 71 | src: local('Source Sans Pro Black Italic'), local('SourceSansPro-BlackIt'), url('assets/fonts/Source Sans Pro/fpTVHK8qsXbIeTHTrnQH6MJvEBP8Mx4XcuTrPnlPwTr3rGVtsTkPsbDajuO5ueQw.woff') format('woff'); 72 | }*/ 73 | 74 | 75 | 76 | @font-face { 77 | font-family: 'Montserrat'; 78 | font-style: normal; 79 | font-weight: 400; 80 | src: local('Montserrat-Regular'), url('zhcz-_WihjSQC0oHJ9TCYL3hpw3pgy2gAi-Ip7WPMi0.woff') format('woff'); 81 | } 82 | @font-face { 83 | font-family: 'Montserrat'; 84 | font-style: normal; 85 | font-weight: 700; 86 | src: local('Montserrat-Bold'), url('IQHow_FEYlDC4Gzy_m8fcnbFhgvWbfSbdVg11QabG8w.woff') format('woff'); 87 | } 88 | -------------------------------------------------------------------------------- /gui/assets/images/circle_down.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /gui/assets/images/circle_ok.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /gui/assets/images/cloud_fail.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 32 | 33 | 34 | 35 | 36 | -------------------------------------------------------------------------------- /gui/assets/images/cloud_ok.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 30 | 31 | 32 | 33 | 34 | -------------------------------------------------------------------------------- /gui/assets/images/cog.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /gui/assets/images/package.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/gui/assets/images/package.png -------------------------------------------------------------------------------- /gui/assets/images/packal-man.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/gui/assets/images/packal-man.jpg -------------------------------------------------------------------------------- /gui/assets/images/paypal.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/gui/assets/images/paypal.gif -------------------------------------------------------------------------------- /gui/assets/images/preloader.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/gui/assets/images/preloader.gif -------------------------------------------------------------------------------- /gui/assets/images/refresh.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 14 | 15 | 16 | 17 | 18 | -------------------------------------------------------------------------------- /gui/assets/images/settings.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 15 | 16 | 17 | 18 | 19 | -------------------------------------------------------------------------------- /gui/assets/images/user-no.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 20 | 21 | 22 | 23 | 24 | -------------------------------------------------------------------------------- /gui/assets/images/user-yes.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 21 | 22 | 23 | 24 | 25 | -------------------------------------------------------------------------------- /gui/assets/images/warning.svg: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 6 | 7 | 8 | 9 | 13 | 14 | 15 | 16 | 17 | -------------------------------------------------------------------------------- /gui/assets/js/borderMenu.js: -------------------------------------------------------------------------------- 1 | /** 2 | * borderMenu.js v1.0.0 3 | * http://www.codrops.com 4 | * 5 | * Licensed under the MIT license. 6 | * http://www.opensource.org/licenses/mit-license.php 7 | * 8 | * Copyright 2013, Codrops 9 | * http://www.codrops.com 10 | */ 11 | (function() { 12 | 13 | function init() { 14 | 15 | var menu = document.getElementById( 'bt-menu' ), 16 | trigger = menu.querySelector( 'a.bt-menu-trigger' ), 17 | eventtype = 'click', 18 | resetMenu = function() { 19 | classie.remove( menu, 'bt-menu-open' ); 20 | classie.add( menu, 'bt-menu-close' ); 21 | }, 22 | closeClickFn = function( ev ) { 23 | resetMenu(); 24 | overlay.removeEventListener( eventtype, closeClickFn ); 25 | }; 26 | 27 | var overlay = document.createElement('div'); 28 | overlay.className = 'bt-overlay'; 29 | menu.appendChild( overlay ); 30 | 31 | trigger.addEventListener( eventtype, function( ev ) { 32 | ev.stopPropagation(); 33 | ev.preventDefault(); 34 | 35 | if( classie.has( menu, 'bt-menu-open' ) ) { 36 | resetMenu(); 37 | } 38 | else { 39 | classie.remove( menu, 'bt-menu-close' ); 40 | classie.add( menu, 'bt-menu-open' ); 41 | overlay.addEventListener( eventtype, closeClickFn ); 42 | } 43 | }); 44 | 45 | } 46 | 47 | init(); 48 | 49 | })(); 50 | -------------------------------------------------------------------------------- /gui/assets/js/classie.js: -------------------------------------------------------------------------------- 1 | /*! 2 | * classie - class helper functions 3 | * from bonzo https://github.com/ded/bonzo 4 | * 5 | * classie.has( elem, 'my-class' ) -> true/false 6 | * classie.add( elem, 'my-new-class' ) 7 | * classie.remove( elem, 'my-unwanted-class' ) 8 | * classie.toggle( elem, 'my-class' ) 9 | */ 10 | 11 | /*jshint browser: true, strict: true, undef: true */ 12 | /*global define: false */ 13 | 14 | ( function( window ) { 15 | 16 | 'use strict'; 17 | 18 | // class helper functions from bonzo https://github.com/ded/bonzo 19 | 20 | function classReg( className ) { 21 | return new RegExp("(^|\\s+)" + className + "(\\s+|$)"); 22 | } 23 | 24 | // classList support for class management 25 | // altho to be fair, the api sucks because it won't accept multiple classes at once 26 | var hasClass, addClass, removeClass; 27 | 28 | if ( 'classList' in document.documentElement ) { 29 | hasClass = function( elem, c ) { 30 | return elem.classList.contains( c ); 31 | }; 32 | addClass = function( elem, c ) { 33 | elem.classList.add( c ); 34 | }; 35 | removeClass = function( elem, c ) { 36 | elem.classList.remove( c ); 37 | }; 38 | } 39 | else { 40 | hasClass = function( elem, c ) { 41 | return classReg( c ).test( elem.className ); 42 | }; 43 | addClass = function( elem, c ) { 44 | if ( !hasClass( elem, c ) ) { 45 | elem.className = elem.className + ' ' + c; 46 | } 47 | }; 48 | removeClass = function( elem, c ) { 49 | elem.className = elem.className.replace( classReg( c ), ' ' ); 50 | }; 51 | } 52 | 53 | function toggleClass( elem, c ) { 54 | var fn = hasClass( elem, c ) ? removeClass : addClass; 55 | fn( elem, c ); 56 | } 57 | 58 | var classie = { 59 | // full names 60 | hasClass: hasClass, 61 | addClass: addClass, 62 | removeClass: removeClass, 63 | toggleClass: toggleClass, 64 | // short names 65 | has: hasClass, 66 | add: addClass, 67 | remove: removeClass, 68 | toggle: toggleClass 69 | }; 70 | 71 | // transport 72 | if ( typeof define === 'function' && define.amd ) { 73 | // AMD 74 | define( classie ); 75 | } else { 76 | // browser global 77 | window.classie = classie; 78 | } 79 | 80 | })( window ); 81 | -------------------------------------------------------------------------------- /gui/assets/js/keep-alive.js: -------------------------------------------------------------------------------- 1 | 2 | /** 3 | * Set a variable and invoke the keepAlive function and execute every 20 seconds 4 | * @param {function} keepAlive() call to function 5 | * @param {int} 20000 try every 20 seconds 6 | * @return {void} returns nothing, just runs every 20 seconds 7 | */ 8 | var keepMeAlive=setInterval( function() { keepAlive() }, 20000 ); 9 | 10 | /** 11 | * A callback to a keep alive script that updates a file to let a bash script know 12 | * not to kill the webserver 13 | * @return {void} no return value 14 | */ 15 | function keepAlive() { 16 | xmlhttp=new XMLHttpRequest(); 17 | xmlhttp.open( "GET","http://localhost:7893/webserver-keep-alive-update.php" ); 18 | xmlhttp.send(); 19 | console.log( "Calling home."); 20 | } 21 | -------------------------------------------------------------------------------- /gui/index.php: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | Packal Updater 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 |
13 |
14 |
15 |
16 |

Packal

17 | 28 |
29 |
30 |
31 |

Loading...

32 | preloader 33 |
34 |
35 |

Updating...

36 |
37 | 38 | 39 | 40 | 41 | 42 | 43 | 115 | 116 | -------------------------------------------------------------------------------- /gui/serve_image.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /gui/webserver-keep-alive-update.php: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/icon.png -------------------------------------------------------------------------------- /info.plist: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | bundleid 6 | com.packal 7 | category 8 | Tools 9 | connections 10 | 11 | 1F4D4C11-58BD-413E-9E17-D93F880D403C 12 | 13 | 14 | destinationuid 15 | C1FFB920-0CA6-4611-ACCE-9D08E0295195 16 | modifiers 17 | 0 18 | modifiersubtext 19 | 20 | 21 | 22 | 2BF4F0C5-F584-44E2-9216-78FF884C965C 23 | 24 | 25 | destinationuid 26 | DC36F13D-1A70-4093-948E-569723637CD2 27 | modifiers 28 | 0 29 | modifiersubtext 30 | 31 | 32 | 33 | 47D96588-97DE-4BD3-BCD4-E23FC751B881 34 | 35 | 36 | destinationuid 37 | 75EA4D1A-6D61-4028-8023-81F9AFFEA680 38 | modifiers 39 | 0 40 | modifiersubtext 41 | 42 | 43 | 44 | 75EA4D1A-6D61-4028-8023-81F9AFFEA680 45 | 46 | 47 | destinationuid 48 | F3825148-0DEF-4920-8AA9-2675D7B90496 49 | modifiers 50 | 0 51 | modifiersubtext 52 | 53 | 54 | 55 | 7955C2E8-A67A-423F-AF99-078BB269996F 56 | 57 | 58 | destinationuid 59 | 47D96588-97DE-4BD3-BCD4-E23FC751B881 60 | modifiers 61 | 0 62 | modifiersubtext 63 | 64 | 65 | 66 | BD4CD001-D2F2-4403-ADAE-991B88EF9C43 67 | 68 | 69 | destinationuid 70 | F3825148-0DEF-4920-8AA9-2675D7B90496 71 | modifiers 72 | 0 73 | modifiersubtext 74 | 75 | 76 | 77 | C1FFB920-0CA6-4611-ACCE-9D08E0295195 78 | 79 | 80 | destinationuid 81 | BD4CD001-D2F2-4403-ADAE-991B88EF9C43 82 | modifiers 83 | 0 84 | modifiersubtext 85 | 86 | 87 | 88 | DC36F13D-1A70-4093-948E-569723637CD2 89 | 90 | 91 | destinationuid 92 | EEB94641-1F4D-4E1C-82DD-69E4E582A9E4 93 | modifiers 94 | 0 95 | modifiersubtext 96 | 97 | 98 | 99 | 100 | createdby 101 | Shawn Patrick Rice 102 | description 103 | A package manager for Alfred 104 | disabled 105 | 106 | name 107 | Packal Updater 108 | objects 109 | 110 | 111 | config 112 | 113 | concurrently 114 | 115 | escaping 116 | 127 117 | script 118 | php packal-controller-script.php "{query}" 119 | type 120 | 0 121 | 122 | type 123 | alfred.workflow.action.script 124 | uid 125 | 75EA4D1A-6D61-4028-8023-81F9AFFEA680 126 | version 127 | 0 128 | 129 | 130 | config 131 | 132 | triggerid 133 | com.packal.start 134 | 135 | type 136 | alfred.workflow.trigger.external 137 | uid 138 | 1F4D4C11-58BD-413E-9E17-D93F880D403C 139 | version 140 | 0 141 | 142 | 143 | config 144 | 145 | argumenttype 146 | 1 147 | escaping 148 | 127 149 | keyword 150 | packal 151 | queuedelaycustom 152 | 1 153 | queuedelayimmediatelyinitially 154 | 155 | queuedelaymode 156 | 0 157 | queuemode 158 | 1 159 | runningsubtext 160 | Talking to server and processing data... 161 | script 162 | php script.php "{query}" 163 | title 164 | Update your packages 165 | type 166 | 0 167 | withspace 168 | 169 | 170 | type 171 | alfred.workflow.input.scriptfilter 172 | uid 173 | C1FFB920-0CA6-4611-ACCE-9D08E0295195 174 | version 175 | 0 176 | 177 | 178 | config 179 | 180 | concurrently 181 | 182 | escaping 183 | 127 184 | script 185 | php packal-controller-script.php "{query}" 186 | type 187 | 0 188 | 189 | type 190 | alfred.workflow.action.script 191 | uid 192 | BD4CD001-D2F2-4403-ADAE-991B88EF9C43 193 | version 194 | 0 195 | 196 | 197 | config 198 | 199 | argumenttype 200 | 0 201 | escaping 202 | 127 203 | queuedelaycustom 204 | 1 205 | queuedelayimmediatelyinitially 206 | 207 | queuedelaymode 208 | 0 209 | queuemode 210 | 1 211 | script 212 | php script-set-option.php "{query}" 213 | title 214 | Set option 215 | type 216 | 0 217 | withspace 218 | 219 | 220 | type 221 | alfred.workflow.input.scriptfilter 222 | uid 223 | 47D96588-97DE-4BD3-BCD4-E23FC751B881 224 | version 225 | 0 226 | 227 | 228 | config 229 | 230 | triggerid 231 | set-option 232 | 233 | type 234 | alfred.workflow.trigger.external 235 | uid 236 | 7955C2E8-A67A-423F-AF99-078BB269996F 237 | version 238 | 0 239 | 240 | 241 | config 242 | 243 | lastpathcomponent 244 | 245 | onlyshowifquerypopulated 246 | 247 | output 248 | 2 249 | removeextension 250 | 251 | sticky 252 | 253 | text 254 | {query} 255 | title 256 | Packal 257 | 258 | type 259 | alfred.workflow.output.notification 260 | uid 261 | F3825148-0DEF-4920-8AA9-2675D7B90496 262 | version 263 | 0 264 | 265 | 266 | config 267 | 268 | concurrently 269 | 270 | escaping 271 | 102 272 | script 273 | env 274 | type 275 | 0 276 | 277 | type 278 | alfred.workflow.action.script 279 | uid 280 | DC36F13D-1A70-4093-948E-569723637CD2 281 | version 282 | 0 283 | 284 | 285 | config 286 | 287 | argumenttype 288 | 2 289 | keyword 290 | packal 291 | text 292 | Packal Info 293 | withspace 294 | 295 | 296 | type 297 | alfred.workflow.input.keyword 298 | uid 299 | 2BF4F0C5-F584-44E2-9216-78FF884C965C 300 | version 301 | 0 302 | 303 | 304 | config 305 | 306 | autopaste 307 | 308 | clipboardtext 309 | {query} 310 | 311 | type 312 | alfred.workflow.output.clipboard 313 | uid 314 | EEB94641-1F4D-4E1C-82DD-69E4E582A9E4 315 | version 316 | 0 317 | 318 | 319 | readme 320 | See https://github.com/shawnrice/packal 321 | Or http://www.packal.org 322 | uidata 323 | 324 | 1F4D4C11-58BD-413E-9E17-D93F880D403C 325 | 326 | ypos 327 | 130 328 | 329 | 2BF4F0C5-F584-44E2-9216-78FF884C965C 330 | 331 | ypos 332 | 430 333 | 334 | 47D96588-97DE-4BD3-BCD4-E23FC751B881 335 | 336 | ypos 337 | 310 338 | 339 | 75EA4D1A-6D61-4028-8023-81F9AFFEA680 340 | 341 | ypos 342 | 10 343 | 344 | 7955C2E8-A67A-423F-AF99-078BB269996F 345 | 346 | ypos 347 | 310 348 | 349 | BD4CD001-D2F2-4403-ADAE-991B88EF9C43 350 | 351 | ypos 352 | 250 353 | 354 | C1FFB920-0CA6-4611-ACCE-9D08E0295195 355 | 356 | ypos 357 | 130 358 | 359 | DC36F13D-1A70-4093-948E-569723637CD2 360 | 361 | ypos 362 | 420 363 | 364 | EEB94641-1F4D-4E1C-82DD-69E4E582A9E4 365 | 366 | ypos 367 | 450 368 | 369 | F3825148-0DEF-4920-8AA9-2675D7B90496 370 | 371 | ypos 372 | 330 373 | 374 | 375 | webaddress 376 | http://www.packal.org 377 | 378 | 379 | -------------------------------------------------------------------------------- /init.php: -------------------------------------------------------------------------------- 1 | $alfred_version, 30 | 'cache_dir' => $cache_dir, 31 | 'data_dir' => $data_dir, 32 | 'workflows_dir' => $workflows_dir, 33 | 'alfred_preferences' => $_SERVER['alfred_preferences'], 34 | 'HOME' => $_SERVER['HOME'], 35 | ]; 36 | if ( ! file_exists( '/tmp/com.packal' ) ) { 37 | mkdir( '/tmp/com.packal' ); 38 | } 39 | file_put_contents( '/tmp/com.packal/config.json', json_encode( $operation_data ) ); 40 | define( 'ALFRED_VERSION', 'Alfred ' . guess_alfred_version() ); 41 | define( 'CACHE_DIR', __CACHE__ ); 42 | define( 'DATA_DIR', __DATA__ ); 43 | define( 'WORKFLOWS_DIR', $workflows_dir ); 44 | } else { 45 | // we're now somewhere else and need this data because it doesn't appear 46 | if ( file_exists( '/tmp/com.packal' ) ) { 47 | $operation_data = json_decode( file_get_contents( '/tmp/com.packal/config.json' ), true ); 48 | define( 'ALFRED_VERSION', $operation_data['alfred_version'] ); 49 | define( 'CACHE_DIR', $operation_data['cache_dir'] ); 50 | define( 'DATA_DIR', $operation_data['data_dir'] ); 51 | define( 'WORKFLOWS_DIR', $operation_data['workflows_dir'] ); 52 | } 53 | } 54 | -------------------------------------------------------------------------------- /install-alfred-cron-script.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | 3 | # This installs the check updates script into Alfred Cron 4 | # alfred.cron.spr 5 | 6 | bundle="alfred.cron.spr" 7 | 8 | data="$HOME/Library/Application Support/Alfred 2/Workflow Data/$bundle" 9 | cache="$HOME/Library/Caches/com.runningwithcrayons.Alfred-2/Workflow Data/$bundle" 10 | scriptDir="$data/scripts" 11 | enabledScriptDir="$data/scripts/enabled" 12 | 13 | name='packal_updater' 14 | 15 | head='#!/bin/bash\nset -o errexit' 16 | interval='86400' 17 | 18 | dir=`cat "$HOME/Library/Application Support/Alfred 2/Workflow Data/com.packal/endpoints/endpoints.list"|grep '"com.packal"='` 19 | dir=`echo "$dir"|tr '"' ' '` 20 | dir=`echo ${dir# com.packal = }` 21 | command="#!/bin/bash 22 | set -o errexit 23 | 24 | php '$dir/assets/alfred.cron.script.php'" 25 | 26 | if [[ ! "$command" =~ "#!/bin/bash" ]]; then 27 | command=`echo "#!/bin/bash\nset -o errexit\n$command"` 28 | fi 29 | echo "$command" > "$scriptDir/$name" 30 | 31 | # Delete an job if the name is already in there. This error should already 32 | # have been accounted for. 33 | awk '!/'"$name"'/' "$data/registry" > "$cache/registry" && mv "$cache/registry" "$data/registry" 34 | echo "$name"="$interval" >> "$data/registry" 35 | 36 | # Enable the script 37 | ln "$scriptDir/$name" "$enabledScriptDir/$name" -------------------------------------------------------------------------------- /libraries/workflows.php: -------------------------------------------------------------------------------- 1 | path = exec('pwd'); 34 | $this->home = exec('printf "$HOME"'); 35 | 36 | if ( file_exists( 'info.plist' ) ): 37 | $this->bundle = $this->get( 'bundleid', 'info.plist' ); 38 | endif; 39 | 40 | if ( !is_null( $bundleid ) ): 41 | $this->bundle = $bundleid; 42 | endif; 43 | 44 | $this->cache = CACHE_DIR; 45 | $this->data = DATA_DIR; 46 | 47 | if ( !file_exists( $this->cache ) ): 48 | exec("mkdir '".$this->cache."'"); 49 | endif; 50 | 51 | if ( !file_exists( $this->data ) ): 52 | exec("mkdir '".$this->data."'"); 53 | endif; 54 | 55 | $this->results = array(); 56 | } 57 | 58 | /** 59 | * Description: 60 | * Accepts no parameter and returns the value of the bundle id for the current workflow. 61 | * If no value is available, then false is returned. 62 | * 63 | * @param none 64 | * @return false if not available, bundle id value if available. 65 | */ 66 | public function bundle() 67 | { 68 | if ( is_null( $this->bundle ) ): 69 | return false; 70 | else: 71 | return $this->bundle; 72 | endif; 73 | } 74 | 75 | /** 76 | * Description: 77 | * Accepts no parameter and returns the value of the path to the cache directory for your 78 | * workflow if it is available. Returns false if the value isn't available. 79 | * 80 | * @param none 81 | * @return false if not available, path to the cache directory for your workflow if available. 82 | */ 83 | public function cache() 84 | { 85 | if ( is_null( $this->bundle ) ): 86 | return false; 87 | else: 88 | if ( is_null( $this->cache ) ): 89 | return false; 90 | else: 91 | return $this->cache; 92 | endif; 93 | endif; 94 | } 95 | 96 | /** 97 | * Description: 98 | * Accepts no parameter and returns the value of the path to the storage directory for your 99 | * workflow if it is available. Returns false if the value isn't available. 100 | * 101 | * @param none 102 | * @return false if not available, path to the storage directory for your workflow if available. 103 | */ 104 | public function data() 105 | { 106 | if ( is_null( $this->bundle ) ): 107 | return false; 108 | else: 109 | if ( is_null( $this->data ) ): 110 | return false; 111 | else: 112 | return $this->data; 113 | endif; 114 | endif; 115 | } 116 | 117 | /** 118 | * Description: 119 | * Accepts no parameter and returns the value of the path to the current directory for your 120 | * workflow if it is available. Returns false if the value isn't available. 121 | * 122 | * @param none 123 | * @return false if not available, path to the current directory for your workflow if available. 124 | */ 125 | public function path() 126 | { 127 | if ( is_null( $this->path ) ): 128 | return false; 129 | else: 130 | return $this->path; 131 | endif; 132 | } 133 | 134 | /** 135 | * Description: 136 | * Accepts no parameter and returns the value of the home path for the current user 137 | * Returns false if the value isn't available. 138 | * 139 | * @param none 140 | * @return false if not available, home path for the current user if available. 141 | */ 142 | public function home() 143 | { 144 | if ( is_null( $this->home ) ): 145 | return false; 146 | else: 147 | return $this->home; 148 | endif; 149 | } 150 | 151 | /** 152 | * Description: 153 | * Returns an array of available result items 154 | * 155 | * @param none 156 | * @return array - list of result items 157 | */ 158 | public function results() 159 | { 160 | return $this->results; 161 | } 162 | 163 | /** 164 | * Description: 165 | * Convert an associative array into XML format 166 | * 167 | * @param $a - An associative array to convert 168 | * @param $format - format of data being passed (json or array), defaults to array 169 | * @return - XML string representation of the array 170 | */ 171 | public function toxml( $a=null, $format='array' ) { 172 | 173 | if ( $format == 'json' ): 174 | $a = json_decode( $a, TRUE ); 175 | endif; 176 | 177 | if ( is_null( $a ) && !empty( $this->results ) ): 178 | $a = $this->results; 179 | elseif ( is_null( $a ) && empty( $this->results ) ): 180 | return false; 181 | endif; 182 | 183 | $items = new SimpleXMLElement(""); // Create new XML element 184 | 185 | foreach( $a as $b ): // Lop through each object in the array 186 | $c = $items->addChild( 'item' ); // Add a new 'item' element for each object 187 | $c_keys = array_keys( $b ); // Grab all the keys for that item 188 | foreach( $c_keys as $key ): // For each of those keys 189 | if ( $key == 'uid' ): 190 | if ( $b[$key] === null || $b[$key] === '' ): 191 | continue; 192 | else: 193 | $c->addAttribute( 'uid', $b[$key] ); 194 | endif; 195 | elseif ( $key == 'arg' ): 196 | $c->addAttribute( 'arg', $b[$key] ); 197 | $c->$key = $b[$key]; 198 | elseif ( $key == 'type' ): 199 | $c->addAttribute( 'type', $b[$key] ); 200 | elseif ( $key == 'valid' ): 201 | if ( $b[$key] == 'yes' || $b[$key] == 'no' ): 202 | $c->addAttribute( 'valid', $b[$key] ); 203 | endif; 204 | elseif ( $key == 'autocomplete' ): 205 | if ( $b[$key] === null || $b[$key] === '' ): 206 | continue; 207 | else: 208 | $c->addAttribute( 'autocomplete', $b[$key] ); 209 | endif; 210 | elseif ( $key == 'icon' ): 211 | if ( substr( $b[$key], 0, 9 ) == 'fileicon:' ): 212 | $val = substr( $b[$key], 9 ); 213 | $c->$key = $val; 214 | $c->$key->addAttribute( 'type', 'fileicon' ); 215 | elseif ( substr( $b[$key], 0, 9 ) == 'filetype:' ): 216 | $val = substr( $b[$key], 9 ); 217 | $c->$key = $val; 218 | $c->$key->addAttribute( 'type', 'filetype' ); 219 | else: 220 | $c->$key = $b[$key]; 221 | endif; 222 | else: 223 | $c->$key = $b[$key]; 224 | endif; 225 | endforeach; 226 | endforeach; 227 | 228 | return $items->asXML(); // Return XML string representation of the array 229 | 230 | } 231 | 232 | /** 233 | * Description: 234 | * Remove all items from an associative array that do not have a value 235 | * 236 | * @param $a - Associative array 237 | * @return bool 238 | */ 239 | private function empty_filter( $a ) { 240 | if ( $a == '' || $a == null ): // if $a is empty or null 241 | return false; // return false, else, return true 242 | else: 243 | return true; 244 | endif; 245 | } 246 | 247 | /** 248 | * Description: 249 | * Save values to a specified plist. If the first parameter is an associative 250 | * array, then the second parameter becomes the plist file to save to. If the 251 | * first parameter is string, then it is assumed that the first parameter is 252 | * the label, the second parameter is the value, and the third parameter is 253 | * the plist file to save the data to. 254 | * 255 | * @param $a - associative array of values to save 256 | * @param $b - the value of the setting 257 | * @param $c - the plist to save the values into 258 | * @return string - execution output 259 | */ 260 | public function set( $a=null, $b=null, $c=null ) 261 | { 262 | if ( is_array( $a ) ): 263 | if ( file_exists( $b ) ): 264 | if ( file_exists( $this->path.'/'.$b ) ): 265 | $b = $this->path.'/'.$b; 266 | endif; 267 | elseif ( file_exists( $this->data."/".$b ) ): 268 | $b = $this->data."/".$b; 269 | elseif ( file_exists( $this->cache."/".$b ) ): 270 | $b = $this->cache."/".$b; 271 | else: 272 | $b = $this->data."/".$b; 273 | endif; 274 | else: 275 | if ( file_exists( $c ) ): 276 | if ( file_exists( $this->path.'/'.$c ) ): 277 | $c = $this->path.'/'.$c; 278 | endif; 279 | elseif ( file_exists( $this->data."/".$c ) ): 280 | $c = $this->data."/".$c; 281 | elseif ( file_exists( $this->cache."/".$c ) ): 282 | $c = $this->cache."/".$c; 283 | else: 284 | $c = $this->data."/".$c; 285 | endif; 286 | endif; 287 | 288 | if ( is_array( $a ) ): 289 | foreach( $a as $k => $v ): 290 | exec( 'defaults write "'. $b .'" '. $k .' "'. $v .'"'); 291 | endforeach; 292 | else: 293 | exec( 'defaults write "'. $c .'" '. $a .' "'. $b .'"'); 294 | endif; 295 | } 296 | 297 | /** 298 | * Description: 299 | * Read a value from the specified plist 300 | * 301 | * @param $a - the value to read 302 | * @param $b - plist to read the values from 303 | * @return bool false if not found, string if found 304 | */ 305 | public function get( $a, $b ) { 306 | 307 | if ( file_exists( $b ) ): 308 | if ( file_exists( $this->path.'/'.$b ) ): 309 | $b = $this->path.'/'.$b; 310 | endif; 311 | elseif ( file_exists( $this->data."/".$b ) ): 312 | $b = $this->data."/".$b; 313 | elseif ( file_exists( $this->cache."/".$b ) ): 314 | $b = $this->cache."/".$b; 315 | else: 316 | return false; 317 | endif; 318 | 319 | exec( 'defaults read "'. $b .'" '.$a, $out ); // Execute system call to read plist value 320 | 321 | if ( $out == "" ): 322 | return false; 323 | endif; 324 | 325 | $out = $out[0]; 326 | return $out; // Return item value 327 | } 328 | 329 | /** 330 | * Description: 331 | * Read data from a remote file/url, essentially a shortcut for curl 332 | * 333 | * @param $url - URL to request 334 | * @param $options - Array of curl options 335 | * @return result from curl_exec 336 | */ 337 | public function request( $url=null, $options=null ) 338 | { 339 | if ( is_null( $url ) ): 340 | return false; 341 | endif; 342 | 343 | $defaults = array( // Create a list of default curl options 344 | CURLOPT_RETURNTRANSFER => true, // Returns the result as a string 345 | CURLOPT_URL => $url, // Sets the url to request 346 | CURLOPT_FRESH_CONNECT => true 347 | ); 348 | 349 | if ( $options ): 350 | foreach( $options as $k => $v ): 351 | $defaults[$k] = $v; 352 | endforeach; 353 | endif; 354 | 355 | array_filter( $defaults, // Filter out empty options from the array 356 | array( $this, 'empty_filter' ) ); 357 | 358 | $ch = curl_init(); // Init new curl object 359 | curl_setopt_array( $ch, $defaults ); // Set curl options 360 | $out = curl_exec( $ch ); // Request remote data 361 | $err = curl_error( $ch ); 362 | curl_close( $ch ); // End curl request 363 | 364 | if ( $err ): 365 | return $err; 366 | else: 367 | return $out; 368 | endif; 369 | } 370 | 371 | /** 372 | * Description: 373 | * Allows searching the local hard drive using mdfind 374 | * 375 | * @param $query - search string 376 | * @return array - array of search results 377 | */ 378 | public function mdfind( $query ) 379 | { 380 | exec('mdfind "'.$query.'"', $results); 381 | return $results; 382 | } 383 | 384 | /** 385 | * Description: 386 | * Accepts data and a string file name to store data to local file as cache 387 | * 388 | * @param array - data to save to file 389 | * @param file - filename to write the cache data to 390 | * @return none 391 | */ 392 | public function write( $a, $b ) 393 | { 394 | if ( file_exists( $b ) ): 395 | if ( file_exists( $this->path.'/'.$b ) ): 396 | $b = $this->path.'/'.$b; 397 | endif; 398 | elseif ( file_exists( $this->data."/".$b ) ): 399 | $b = $this->data."/".$b; 400 | elseif ( file_exists( $this->cache."/".$b ) ): 401 | $b = $this->cache."/".$b; 402 | else: 403 | $b = $this->data."/".$b; 404 | endif; 405 | 406 | if ( is_array( $a ) ): 407 | $a = json_encode( $a ); 408 | file_put_contents( $b, $a ); 409 | return true; 410 | elseif ( is_string( $a ) ): 411 | file_put_contents( $b, $a ); 412 | return true; 413 | else: 414 | return false; 415 | endif; 416 | } 417 | 418 | /** 419 | * Description: 420 | * Returns data from a local cache file 421 | * 422 | * @param file - filename to read the cache data from 423 | * @return false if the file cannot be found, the file data if found. If the file 424 | * format is json encoded, then a json object is returned. 425 | */ 426 | public function read( $a, $array = false ) 427 | { 428 | if ( file_exists( $a ) ): 429 | if ( file_exists( $this->path.'/'.$a ) ): 430 | $a = $this->path.'/'.$a; 431 | endif; 432 | elseif ( file_exists( $this->data."/".$a ) ): 433 | $a = $this->data."/".$a; 434 | elseif ( file_exists( $this->cache."/".$a ) ): 435 | $a = $this->cache."/".$a; 436 | else: 437 | return false; 438 | endif; 439 | 440 | $out = file_get_contents( $a ); 441 | if ( !is_null( json_decode( $out ) ) && !$array ): 442 | $out = json_decode( $out ); 443 | elseif ( !is_null( json_decode( $out ) ) && !$array ): 444 | $out = json_decode( $out, true ); 445 | endif; 446 | 447 | return $out; 448 | } 449 | 450 | /** 451 | * Description: 452 | * Helper function that just makes it easier to pass values into a function 453 | * and create an array result to be passed back to Alfred 454 | * 455 | * @param $uid - the uid of the result, should be unique 456 | * @param $arg - the argument that will be passed on 457 | * @param $title - The title of the result item 458 | * @param $sub - The subtitle text for the result item 459 | * @param $icon - the icon to use for the result item 460 | * @param $valid - sets whether the result item can be actioned 461 | * @param $auto - the autocomplete value for the result item 462 | * @return array - array item to be passed back to Alfred 463 | */ 464 | public function result( $uid, $arg, $title, $sub, $icon, $valid='yes', $auto=null, $type=null ) 465 | { 466 | $temp = array( 467 | 'uid' => $uid, 468 | 'arg' => $arg, 469 | 'title' => $title, 470 | 'subtitle' => $sub, 471 | 'icon' => $icon, 472 | 'valid' => $valid, 473 | 'autocomplete' => $auto, 474 | 'type' => $type 475 | ); 476 | 477 | if ( is_null( $type ) ): 478 | unset( $temp['type'] ); 479 | endif; 480 | 481 | array_push( $this->results, $temp ); 482 | 483 | return $temp; 484 | } 485 | 486 | } -------------------------------------------------------------------------------- /notes.txt: -------------------------------------------------------------------------------- 1 | Note to self and anyone else who happens to read this: 2 | 3 | The code here is a bit messy and uncommented. Some of it is hacky. Some extra images are included, but those are for possible future use. They're small. 4 | 5 | @TODO: Comment Code 6 | @TODO: Rewrite as class to reuse code and limit outgoing calls to bare minimum 7 | @TODO: Finish non-essential CLI functionality 8 | @TODO: Clean up CLI scripts 9 | @TODO: Re-implement 'Notification Options' 10 | @TODO: Re-implement 'Force Packal' option 11 | @TODO: Create viable webserver that works Pre-Mavericks 12 | @TODO: Reorganize paths and prune extra files 13 | @TODO: Better error handling (fail more gracefully) 14 | @TODO: Make sorting better in workflow 15 | @TODO: Implement workflow reporting more elegantly 16 | @TODO: Re-implement API key functionality 17 | @TODO: Create Push-to-Packal functionality 18 | @TODO: Add in more extenal triggers for better manipulation of workflow 19 | @TODO: Refine language used throughout GUI and workflow 20 | @TODO: Reorganize for better lazy-loading of longer calls 21 | @TODO: Implement Alphred library 22 | @TODO: Finish writing Alphred library 23 | @TODO: Get better icons 24 | 25 | @TODO: Fix typo when no Internet connection is available on status page of GUI 26 | 27 | 28 | nohup sleep 10 > /dev/null 2>&1 ; '/Users/Sven/Library/Application Support/Alfred 2/Workflow Data/alfred.bundler-aries/assets/utility/terminal-notifier/default/terminal-notifier.app/Contents/MacOS/terminal-notifier' -message 'packal-updater-settings' > /dev/null 2>&1 & -------------------------------------------------------------------------------- /packal-controller-script.php: -------------------------------------------------------------------------------- 1 | DATA_DIR, 'cache_dir' => CACHE_DIR, 'alfred_version' => ALFRED_VERSION ]; 39 | // if ( ! file_exists( '/tmp/com.packal' ) ) { 40 | // mkdir( '/tmp/com.packal' ); 41 | // } 42 | // file_put_contents( '/tmp/com.packal/config.json', json_encode( $operation_data ) ); 43 | 44 | // Since we're using Mavericks or greater, we can just use the native php 5.4 binary. 45 | exec( 'nohup php -S localhost:7893 -t gui/ > /dev/null 2>&1 &' ); 46 | } else { 47 | // Not using Mavericks, so php 5.3 is installed. 48 | // For now, fuck it. I've tried to come up with too many workarounds. 49 | // No GUI for you. 50 | die(); 51 | } 52 | 53 | // Start the webserver kill script 54 | exec( "nohup '$dir/check-and-kill-webserver.sh' > /dev/null 2>&1 &" ); 55 | exec( "nohup php '$dir/gui/webserver-keep-alive-update.php' > /dev/null 2>&1 &" ); 56 | 57 | // Wait a second so that we can make sure that the webserver has started before we open the viewer. 58 | sleep( 2 ); 59 | 60 | $viewer = __load( 'viewer', 'default', 'utility' ); 61 | // Okay, so if there is an old version of the bundler, then we'll do the correction for this. 62 | if ( strpos( $viewer, '--args' ) ) { 63 | $viewer = substr( $viewer, 0, strlen( $viewer ) - 6 ); 64 | } 65 | 66 | // Open the gui in the viewer 67 | exec( "nohup open $viewer --args http://localhost:7893 > /dev/null 2>&1 &" ); 68 | 69 | $script = 'tell application "System Events" to set frontmost of process "Application Stub" to true'; 70 | exec( "osascript -e '$script'" ); 71 | 72 | die(); 73 | } 74 | 75 | 76 | if ( strpos( $q, 'update-' ) !== false ) { 77 | $workflows = json_decode( file_get_contents( "$data/endpoints/endpoints.json" ), true ); 78 | $wf = array_keys( $workflows ); 79 | if ( str_replace( 'update-', '', $q ) == 'all' ) { 80 | $result = exec( 'php cli/packal.php doUpdateAll ' ); 81 | } else { 82 | if ( in_array( str_replace( 'update-', '', $q ), $wf ) ) { 83 | $cmd = 'php cli/packal.php doUpdate ' . str_replace( 'update-', '', $q ); 84 | $result = exec( $cmd ); 85 | if ( $result == 'TRUE' ) { 86 | $plist = $workflows[ str_replace( 'update-', '', $q ) ] . '/info.plist'; 87 | $name = exec( "/usr/libexec/PlistBuddy -c \"Print :name\" '$plist' 2> /dev/null" ); 88 | // echo $name . " has been successfully updated."; 89 | } else { 90 | $plist = $workflows[ str_replace( 'update-', '', $q ) ] . '/info.plist'; 91 | $name = exec( "/usr/libexec/PlistBuddy -c \"Print :name\" '$plist' 2> /dev/null" ); 92 | echo 'Error updating ' . $name; 93 | } 94 | } 95 | } 96 | } 97 | 98 | // We haven't possibly needed these yet, so we'll load them here. 99 | $tn = __load( 'terminal-notifier' , 'default' , 'utility' ); 100 | $endpoints = json_decode( file_get_contents( "$data/endpoints/endpoints.json" ), true ); 101 | 102 | 103 | if ( strpos( $q, 'option-set-' ) !== false ) { 104 | $set = str_replace( 'option-set-', '', $q ); 105 | $set = explode( '-', $set ); 106 | $set[1] = (string) $set[1]; 107 | 108 | if ( count( $set ) > 2 ) { 109 | echo 'Too many hyphens.'; 110 | die(); 111 | } 112 | 113 | if ( empty( $set[1] ) ) { 114 | $set[1] = 'null'; 115 | } 116 | 117 | // Just in case something wasn't quoted correctly. 118 | $set[1] = str_replace( '\ ', ' ', $set[1] ); 119 | 120 | if ( ( $set[0] == 'username' ) && ( $set[1] == 'null' ) ) { 121 | $cmd = ( 'php cli/packal.php setOption packalAccount 0' ); 122 | exec( $cmd ); 123 | $cmd = ( "php cli/packal.php setOption username ''" ); 124 | exec( $cmd ); 125 | exec( "$tn -title 'Packal Updater' -message 'You no longer have a Packal username set.' -group 'packal-updater-settings'" ); 126 | 127 | die(); 128 | } 129 | if ( ( $set[0] == 'packalAccount') && ( $set[1] == '1' ) ) { 130 | $cmd = ( 'php cli/packal.php setOption packalAccount 1' ); 131 | exec( $cmd ); 132 | $script = 'tell application "' . ALFRED_VERSION . '" to run trigger "set-option" in workflow "com.packal" with argument "username:"'; 133 | exec( "osascript -e '$script'" ); 134 | die(); 135 | } 136 | 137 | if ( ( $set[0] == 'packalAccount') && ( $set[1] == '0' ) ) { 138 | $cmd = ( 'php cli/packal.php setOption packalAccount 0' ); 139 | exec( $cmd ); 140 | $cmd = ( "php cli/packal.php setOption username ''" ); 141 | exec( $cmd ); 142 | exec( "$tn -title 'Packal Updater' -message 'You no longer have a Packal username set.' -group 'packal-updater-settings'" ); 143 | die(); 144 | } 145 | if ( ( $set[0] == 'authorName' ) && ( $set[1] == 'null' ) ) { 146 | $cmd = ( "php cli/packal.php setOption authorName ''" ); 147 | exec( $cmd ); 148 | exec( "$tn -title 'Packal Updater' -message 'You no longer have a author name set.' -group 'packal-updater-settings'" ); 149 | 150 | die(); 151 | } 152 | 153 | if ( $set[0] == 'workflowReporting' ) { 154 | 155 | // Possible Data Correction 156 | if ( $set[1] == 'null' ) { 157 | $set[1] = 0; 158 | } 159 | 160 | // Set the value 161 | $cmd = 'php cli/packal.php setOption workflowReporting ' . $set[1]; 162 | exec( $cmd ); 163 | 164 | // Send the notification 165 | if ( $set[1] == 1 ) { 166 | exec( "$tn -title 'Packal Updater' -message 'You will now send anonymous usage data to Packal.org.' -group 'packal-updater-settings'" ); 167 | } else { exec( "$tn -title 'Packal Updater' -message 'You will __not__ send anonymous usage data to Packal.org.' -group 'packal-updater-settings'" ); 168 | } 169 | 170 | // Die. Die. Die. 171 | die(); 172 | } 173 | 174 | $cmd = ( 'php cli/packal.php setOption ' . $set[0] . " '" . $set[1] . "'" ); 175 | exec( $cmd ); 176 | 177 | switch ( $set[0] ) : 178 | case 'backups' : 179 | $message = 'The updater will now keep ' . $set[1] . ' backups of workflows.'; 180 | break; 181 | case 'authorName' : 182 | $message = 'Your author name is now set to ' . $set[1] . '.'; 183 | break; 184 | case 'username' : 185 | $message = 'Your Packal.org username is now set to ' . $set[1] . '.'; 186 | break; 187 | endswitch; 188 | 189 | if ( isset( $message ) ) { 190 | exec( "$tn -title 'Packal Updater' -message '$message' -group 'packal-updater-settings'" ); 191 | } 192 | 193 | // Option to set a custom icon. It doesn't seem to be working right now, so commented out. 194 | // if ( $osx == '10.9' || $osx == '10.10' ) 195 | // $tnicon = "-appIcon '" . __DIR__ . "assets/icons/package.png'"; 196 | 197 | 198 | 199 | 200 | // @TODO: Add in Terminal Notifier notification. 201 | die(); 202 | 203 | } 204 | 205 | if ( strpos( $q, 'set-' ) !== false ) { 206 | $option = str_replace( 'set-', '', $q ); 207 | $script = 'tell application "' . ALFRED_VERSION . '" to run trigger "set-option" in workflow "com.packal" with argument "' . $option . ': "'; 208 | exec( "osascript -e '$script'" ); 209 | die(); 210 | } 211 | 212 | if ( strpos( $q, 'blacklist-' ) !== false ) { 213 | $workflow = str_replace( 'blacklist-', '', $q ); 214 | $blacklist = json_decode( file_get_contents( "$data/config/blacklist.json" ), true ); 215 | if ( ! in_array( $workflow, $blacklist ) ) { 216 | $blacklist[] = $workflow; 217 | file_put_contents( "$data/config/blacklist.json", utf8_encode( json_encode( $blacklist ) ) ); 218 | } 219 | $plist = $endpoints[ $workflow ] . '/info.plist'; 220 | $name = exec( "/usr/libexec/PlistBuddy -c \"Print :name\" '$plist' 2> /dev/null" ); 221 | exec( "$tn -title 'Packal Updater' -message '$name has been put on the blacklist.'" ); 222 | die(); 223 | } 224 | 225 | if ( strpos( $q, 'whitelist-' ) !== false ) { 226 | $workflow = str_replace( 'whitelist-', '', $q ); 227 | $blacklist = json_decode( file_get_contents( "$data/config/blacklist.json" ), true ); 228 | if ( in_array( $workflow, $blacklist ) ) { 229 | unset( $blacklist[ array_search( $workflow, $blacklist ) ] ); 230 | file_put_contents( "$data/config/blacklist.json", utf8_encode( json_encode( $blacklist ) ) ); 231 | } 232 | $plist = $endpoints[ $workflow ] . '/info.plist'; 233 | $name = exec( "/usr/libexec/PlistBuddy -c \"Print :name\" '$plist' 2> /dev/null" ); 234 | exec( "$tn -title 'Packal Updater' -message '$name has been removed from the blacklist.'" ); 235 | die(); 236 | } 237 | 238 | if ( strpos( $q, 'install-cron-script' ) !== false ) { 239 | $dir = escapeshellcmd( exec( 'pwd' ) ); 240 | exec( "'$dir/install-alfred-cron-script.sh'" ); 241 | exec( "$tn -title 'Packal Updater' -message 'Alfred Cron will now check for workflow updates.'" ); 242 | } 243 | 244 | 245 | -------------------------------------------------------------------------------- /report-usage-data.php: -------------------------------------------------------------------------------- 1 | workflowReporting ) && ( (string) $config->workflowReporting == '1' ) ) ) { 47 | die(); 48 | } 49 | 50 | // Make sure that we have an Internet connection before continuing. 51 | if ( checkConnection() == false ) { 52 | die(); 53 | } 54 | 55 | // Check if the usage directory exists; make it if it doesn't. 56 | if ( ! file_exists( "$data/usage" ) ) { 57 | mkdir( "$data/usage" ); 58 | } 59 | 60 | // This is your unique identifier. If you're reading this, just run the command and see what's up. 61 | $unique = hash( 'sha256', 62 | exec( 'ioreg -rd1 -c IOPlatformExpertDevice | awk \'/IOPlatformUUID/ { split($0, line, "\""); printf("%s\n", line[4]); }\'' ) ); 63 | 64 | $unique = utf8_encode( $unique ); 65 | 66 | // Create the filename 67 | $date = date( 'Y-W' , time() ); 68 | $file = $data . '/usage/' . $date . "-data-$unique.json"; 69 | 70 | // This means that we've already generated and sent the file. So, let's just stop now. 71 | if ( file_exists( $file ) ) { 72 | echo 'Already submitted this week.'; 73 | die(); 74 | } 75 | 76 | 77 | // $me = substr( __DIR__, strrpos( __DIR__, '/' ) + 1 ); 78 | 79 | // If the endpoints file does not exist, then we're calling this too early. Just wait a bit. 80 | if ( ! file_exists( "$data/endpoints/endpoints.json" ) ) { 81 | die(); 82 | } 83 | 84 | $workflows = json_decode( file_get_contents( "$data/endpoints/endpoints.json" ) ); 85 | 86 | foreach ( $workflows as $dir ) : 87 | $w = getWorkflowData( $dir ); 88 | if ( is_array( $w ) ) { 89 | $report[] = $w; 90 | } 91 | endforeach; 92 | 93 | 94 | // Total number of workflows installed. 95 | $total = count( $report ); 96 | 97 | // We'll send the post array to server. Here's the identifying information for the server 98 | $post = array(); 99 | $post['meta'] = array( 'date' => $date , 'total' => $total ); 100 | 101 | // Add the Workflows to the post array 102 | $post['workflows'] = $report; 103 | $json = utf8_encode( json_encode( $post ) ); 104 | 105 | $data = array( 'info' => $json, 'unique' => $unique, 'task' => 'reporting', 'date' => $date ); 106 | 107 | $ch = curl_init(); 108 | curl_setopt( $ch, CURLOPT_URL, 'http://api.packal.org' ); 109 | curl_setopt( $ch, CURLOPT_USERAGENT, 'packal-workflow-' . hash( 'sha256', $unique ) ); 110 | curl_setopt( $ch, CURLOPT_POST, 1 ); 111 | curl_setopt( $ch, CURLOPT_POSTFIELDS, $data ); 112 | curl_setopt( $ch, CURLOPT_TIMEOUT, 15 ); 113 | // curl_setopt( $ch, CURLOPT_HEADER, 1 ); 114 | curl_setopt( $ch, CURLOPT_FOLLOWLOCATION, 1 ); 115 | curl_setopt( $ch, CURLOPT_RETURNTRANSFER, true ); 116 | curl_setopt( $ch, CURLOPT_ENCODING, 'gzip' ); 117 | 118 | $header = curl_getinfo( $ch ); 119 | $result = curl_exec( $ch ); 120 | curl_close( $ch ); 121 | 122 | if ( $result == "It's all good." ) { 123 | // It went through. Yay! 124 | if ( file_put_contents( $file , $json ) !== false ) { 125 | // Recorded the file, so let's move on. 126 | die(); 127 | } else { 128 | echo 'We ran into an error when recording the file.'; 129 | die(); 130 | } 131 | } 132 | 133 | echo 'Error: request not accepted.'; 134 | // We're done. 135 | die(); 136 | 137 | // Some helpful functions. 138 | 139 | function getWorkflowData( $dir ) { 140 | $plist = "$dir/info.plist"; 141 | if ( ! file_exists( $plist ) ) { 142 | return false; 143 | } 144 | 145 | $bundle = utf8_encode( trim( readPlistValue( 'bundleid', $plist ) ) ); 146 | 147 | if ( ! $bundle ) { 148 | return false; 149 | } 150 | 151 | // Get the Workflow name 152 | $name = utf8_encode( trim( readPlistValue( 'name', $plist ) ) ); 153 | 154 | // Get the author 155 | $author = utf8_encode( trim( readPlistValue( 'createdby', $plist ) ) ); 156 | 157 | // Figure out if it's disabled. 158 | $disabled = utf8_encode( readPlistValue( 'disabled', $plist ) ); 159 | 160 | // Strangely, sometimes these weren't set on my computer, so, well, that's strange. 161 | // My guess is that they were older workflows, so maybe the problems was older plist 162 | // structures from way-back Alfred 2. 163 | if ( empty( $disabled ) ) { $disabled = 'TRUE'; 164 | } 165 | $disabled = strtoupper( $disabled ); 166 | if ( file_exists( $dir . '/packal/package.xml' ) ) { 167 | $packal = 'TRUE'; 168 | } else { $packal = 'FALSE'; 169 | } 170 | 171 | return array( 'name' => $name , 'bundle' => $bundle , 'author' => $author, 'disabled' => $disabled, 'packal' => $packal ); 172 | 173 | } 174 | 175 | function checkConnection() { 176 | ini_set( 'default_socket_timeout', 1 ); 177 | 178 | // First test 179 | exec( 'ping -c 1 -t 1 www.github.com', $pingResponse, $pingError ); 180 | if ( $pingError == 14 ) { 181 | return false; 182 | } 183 | 184 | // Second Test 185 | $connection = @fsockopen( 'www.github.com', 80, $errno, $errstr, 1 ); 186 | 187 | if ( $connection ) { 188 | $status = true; 189 | fclose( $connection ); 190 | } else { 191 | $status = false; 192 | } 193 | return $status; 194 | } 195 | 196 | function readPlistValue( $key, $plist ) { 197 | return exec( "/usr/libexec/PlistBuddy -c \"Print :$key\" '$plist' 2> /dev/null" ); 198 | } 199 | -------------------------------------------------------------------------------- /screenshots/gui.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/screenshots/gui.png -------------------------------------------------------------------------------- /screenshots/packal-blacklist.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/screenshots/packal-blacklist.png -------------------------------------------------------------------------------- /screenshots/packal-setup.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/screenshots/packal-setup.png -------------------------------------------------------------------------------- /screenshots/packal-start.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/screenshots/packal-start.png -------------------------------------------------------------------------------- /screenshots/packal-update-no-internet.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/screenshots/packal-update-no-internet.png -------------------------------------------------------------------------------- /screenshots/packal-update.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/screenshots/packal-update.png -------------------------------------------------------------------------------- /screenshots/packal-updater-alfred.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/screenshots/packal-updater-alfred.gif -------------------------------------------------------------------------------- /screenshots/packal-updater-gui.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/screenshots/packal-updater-gui.gif -------------------------------------------------------------------------------- /screenshots/reporting-unique-id.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/screenshots/reporting-unique-id.png -------------------------------------------------------------------------------- /script-set-option.php: -------------------------------------------------------------------------------- 1 | $option ) && ( ! empty( $config->$option ) ) ) { 21 | if ( $option == 'backups' ) { 22 | $value = (int) $config->$option; 23 | } else { 24 | $value = $config->$option; 25 | } 26 | } 27 | } 28 | $options = array( 29 | 'authorName' => 'Please enter the name you use when you write workflows', 30 | 'packalAccount' => 'Do you have an account on Packal? Please answer yes or no.', 31 | 'username' => 'Please enter your Packal username', 32 | 'workflowReporting' => 'Would you like to send anonymous data about your installed workflows to Packal.org? Please enter yes or no.', 33 | 'backups' => 'Please enter the number of backups you would like to keep', 34 | ); 35 | 36 | $bool = array( 'packalAccount', 'workflowReporting' ); 37 | 38 | if ( in_array( $option, $bool ) ) { 39 | if ( $option == 'workflowReporting' ) { 40 | if ( $config->workflowReporting == 1 ) { 41 | $w->result( 'option-set-workflowReporting-1', 'option-set-workflowReporting-1', 'Send anonymous data to Packal.org.', 'Currently selected', 'assets/icons/svn-commit.png', 'yes', '' ); 42 | $w->result( 'option-set-workflowReporting-0', 'option-set-workflowReporting-0', 'Do not send anonymous data to Packal.org.', '', '', 'yes', '' ); 43 | } else { 44 | $w->result( 'option-set-workflowReporting-1', 'option-set-workflowReporting-1', 'Send anonymous data to Packal.org.', '', 'assets/icons/svn-commit.png', 'yes', '' ); 45 | $w->result( 'option-set-workflowReporting-0', 'option-set-workflowReporting-0', 'Do not send anonymous data to Packal.org.', 'Currently selected', '', 'yes', '' ); 46 | } 47 | } elseif ( $option == 'packalAccount' ) { 48 | $w->result( 'option-set-packalAccount-1', 'option-set-packalAccount-1', 'I do have an account on Packal.org', '', '', 'yes', '' ); 49 | $w->result( 'option-set-packalAccount-0', 'option-set-packalAccount-0', 'I do not have an account on Packal.org', '', '', 'yes', '' ); 50 | } 51 | } else { 52 | if ( empty( $value ) ) { 53 | $w->result( "option-set-$option-$value", "option-set-$option-$value", $options[ $option ], "$value", '', 'no', '' ); 54 | } elseif ( $option == 'backups' ) { 55 | if ( ( $value > 9 ) || ( $value < 0 ) ) { 56 | $w->result( "option-set-$option-$value", "option-set-$option-$value", 'Error: The number of backups must be positive and less than 10', '', '', 'no', '' ); 57 | } elseif ( ! is_numeric( $value ) ) { 58 | $w->result( "option-set-$option-$value", "option-set-$option-$value", 'Error: The number of backups must be an integer', '', '', 'no', '' ); 59 | } else { 60 | $value = (int) $value; 61 | $w->result( "option-set-$option-$value", "option-set-$option-$value", "Set the number of backups to keep to '$value'", "$value", '', 'yes', '' ); 62 | } 63 | } else { 64 | if ( $option == 'username' ) { 65 | if ( $config->packalAccount == 1 ) { 66 | $w->result( '', "option-set-$option-$value", 'Set your ' . $option . " to '$value'", "$value", '', 'yes', '' ); 67 | $w->result( '', "option-set-$option-", 'Clear Packal Username', '', '', 'yes', '' ); 68 | } 69 | } elseif ( $option == 'authorName' ) { 70 | if ( empty( $config->authorName ) ) { 71 | $w->result( '', "option-set-$option-$value", "Set your author name to '$value'", "$value", '', 'yes', '' ); 72 | } else { 73 | $w->result( '', "option-set-$option-$value", "Set your author name to '$value'", "$value", '', 'yes', '' ); 74 | $w->result( '', "option-set-$option-", 'Clear Author Name', '', '', 'yes', '' ); 75 | } 76 | } else { 77 | $w->result( "option-set-$option-$value", "option-set-$option-$value", 'Set your ' . $option . " to '$value'", $value, '', 'yes', '' ); 78 | } 79 | } 80 | } 81 | } 82 | 83 | setOption(); 84 | 85 | echo $w->toxml(); 86 | 87 | 88 | -------------------------------------------------------------------------------- /script.php: -------------------------------------------------------------------------------- 1 | result( '', '', 'Warning: no viable Internet connection', 40 | 'Some features of this workflow will be unavailable without a solid Internet connection', 'assets/icons/task-attention.png', 'no', ''); 41 | } 42 | 43 | if ( ! file_exists( $data ) ) { 44 | mkdir( "$data", 0775, true ); 45 | } 46 | if ( ! file_exists( $cache ) ) { 47 | mkdir( "$cache", 0775, true ); 48 | } 49 | if ( ! file_exists( "$data/config" ) ) { 50 | mkdir( "$data/config", 0775, true ); 51 | } 52 | if ( ! file_exists( "$data/endpoints" ) ) { 53 | mkdir( "$data/endpoints", 0775, true ); 54 | } 55 | 56 | if ( ! file_exists( "$data/config/blacklist.json" ) ) { 57 | // Make an empty blacklist file if one doesn't exist. 58 | $blacklist = array(); 59 | file_put_contents( "$data/config/blacklist.json", utf8_encode( json_encode( $blacklist ) ) ); 60 | } 61 | 62 | generateEndpoints(); 63 | 64 | // Not all of these are used right now. 65 | if ( ! file_exists( "$data/config/config.xml" ) ) { 66 | $d = ''; 67 | $config = new SimpleXMLElement( $d ); 68 | $config->packalAccount = 0; 69 | $config->forcePackal = 0; 70 | $config->backups = 3; 71 | $config->username = ''; 72 | $config->authorName = ''; 73 | $config->workflowReporting = '1'; 74 | $config->notifications = '2'; 75 | $config->apiKey = ''; 76 | $config->asXML( "$data/config/config.xml" ); 77 | unset( $config ); 78 | } else { 79 | $config = simplexml_load_file( "$data/config/config.xml" ); 80 | } 81 | 82 | if ( ! file_exists( "$data/manifest.xml" ) ) { 83 | // This should be taken care of in the firstRun() call, 84 | // but the redundancy is fine. 85 | if ( getManifest() == false ) { 86 | $w->result( '', '', 'Error: Packal Updater', 87 | 'The workflow manifest is not valid, and there is no valid Internet connection to retrieve a new one.', 'assets/icons/task-reject.png', 'no', ''); 88 | echo $w->toxml(); 89 | die(); 90 | } 91 | } 92 | 93 | // Update the manifest if past cache. 94 | // This is a potential failure spot. 95 | if ( $connection !== false ) { 96 | exec( "'" . __DIR__ . "/cli/packal.sh' update" ); 97 | } 98 | 99 | // Do the workflow reporting script as long as the config option is set. 100 | // if ( $config->workflowReporting == '1' ) { 101 | // exec( "nohup php '" . __DIR__ . "/report-usage-data.php' > /dev/null 2>&1 &" ); 102 | // } 103 | 104 | $blacklist = json_decode( file_get_contents( "$data/config/blacklist.json" ), true ); 105 | $manifest = @simplexml_load_file( "$data/manifest.xml" ); 106 | 107 | // The manifest is not valid. Let's try to get it before we fail. 108 | if ( empty( $manifest ) ) { 109 | if ( getManifest() === false ) { 110 | $w->result( '', '', 'Error: Packal Updater', 111 | 'The workflow manifest is not valid, and there is no valid Internet connection to retrieve a new one.', 'assets/icons/task-reject.png', 'no', ''); 112 | echo $w->toxml(); 113 | die(); 114 | } 115 | // So, the we didn't get an error, but let's try to make sure it is valid XML. 116 | $manifest = @simplexml_load_file( "$data/manifest.xml" ); 117 | if ( empty( $manifest ) ) { 118 | $w->result( '', '', 'Error: Packal Updater', 119 | 'The workflow manifest is not valid, and there is no valid Internet connection to retrieve a new one.', 'assets/icons/task-reject.png', 'no', ''); 120 | echo $w->toxml(); 121 | die(); 122 | } 123 | } 124 | 125 | // Okay, we're good. Or, at least we should be. 126 | 127 | $json = json_decode( file_get_contents( "$data/endpoints/endpoints.json" ), true ); 128 | $mine = array_keys( $json ); 129 | $count = count( $manifest->workflow ); 130 | $me = exec( "php '" . __DIR__ . "/cli/packal.php' getOption username" ); 131 | 132 | foreach ( $manifest->workflow as $wf ) : 133 | 134 | if ( in_array( $wf->bundle, $mine ) ) { 135 | $packal[] = (string) $wf->bundle; 136 | 137 | if ( isset( $me ) && ( ! empty( $me ) ) && ( $wf->author == $me ) ) { 138 | $mywf[] = (string) $wf->bundle; 139 | } 140 | } 141 | endforeach; 142 | unset( $wf ); 143 | 144 | // Grab manifest information. 145 | foreach ( $manifest as $m ) : 146 | $manifestBundles[] = $m->bundle; 147 | $wf[ "$m->bundle" ]['name'] = $m->name; 148 | $wf[ "$m->bundle" ]['author'] = $m->author; 149 | $wf[ "$m->bundle" ]['version'] = $m->version; 150 | $wf[ "$m->bundle" ]['short'] = $m->short; 151 | $wf[ "$m->bundle" ]['updated'] = $m->updated; 152 | $wf[ "$m->bundle" ]['url'] = $m->url; 153 | endforeach; 154 | 155 | foreach ( $json as $k => $v ) : 156 | $pattern = '/[\/A-Za-z0-9-_ .]{1,}\/user\.workflow\.([0-9A-Z.-]{1,})/'; 157 | preg_match( $pattern, $v, $matches ); 158 | if ( isset( $matches[1] ) ) { 159 | $json[ $k ] = 'user.workflow.' . $matches[1]; 160 | } 161 | endforeach; 162 | foreach ( $json as $k => $v ) : 163 | if ( file_exists( "../$v/packal/package.xml" ) ) { 164 | $p = simplexml_load_file( "../$v/packal/package.xml" ); 165 | if ( in_array( $k, $manifestBundles ) ) { 166 | if ( (string) $p->version != $wf[ "$k" ]['version'] ) { 167 | if ( ! in_array( $k, $blacklist ) ) { 168 | $updates[ $k ]['name'] = (string) $p->name; 169 | $updates[ $k ]['path'] = "../$v"; 170 | $updates[ $k ]['version'] = (string) $p->version; 171 | } 172 | } 173 | } 174 | } 175 | endforeach; 176 | 177 | if ( empty( $q[1] ) ) { 178 | if ( ! isset( $updates ) ) { 179 | $w->result( 'updates', 'updates', 'All of your workflows are up to date.', '', 'assets/icons/task-complete.png', 'no', '' ); 180 | } else { 181 | if ( count( $updates ) > 1 ) { 182 | $message = 'There are ' . count( $updates ) . ' updates pending.'; 183 | } else { $message = 'There is 1 update pending.'; 184 | } 185 | 186 | $w->result( 'updates', 'updates', 'Updates available', $message, '', 'no', 'update' ); 187 | } 188 | 189 | if ( ( time() - filemtime( "$data/manifest.xml" ) < 86400 ) ) { 190 | $manifestTime = getManifestModTime(); 191 | $w->result( '', 'manifest-update', 'The manifest is up to date.', "Last updated $manifestTime", 'assets/icons/task-complete.png', 'yes', '' ); 192 | } else { 193 | $manifestTime = getManifestModTime(); 194 | $w->result( '', 'manifest-update', 'The manifest is out of date.', "Last updated $manifestTime", 'assets/icons/task-attention.png', 'yes', '' ); 195 | } 196 | 197 | if ( $gui === true ) { 198 | $w->result( 'open-gui', 'open-gui', "Open Graphical 'Application'", 199 | 'Configure this workflow, Update workflows, and learn about this workflow. (RECOMMENDED)', 'assets/icons/applications-education-miscellaneous.png', 'yes', ''); 200 | } else { 201 | $w->result( '', '', "GUI Not Available'", 202 | 'A GUI to configure and operate this workflow is available if you have OS X 10.9 or 10.10.', 'assets/icons/applications-education-miscellaneous.png', 'no', ''); 203 | } 204 | 205 | if ( isset( $mywf ) ) { 206 | $mywf = count( $mywf ); 207 | } else { $mywf = 0; 208 | } 209 | 210 | // if ( isset( $config->authorName ) && ( ! empty( $config->authorName ) ) { 211 | // $w->result( '', '', 'Informational', 212 | // "There are $count workflows in the manifest," . 213 | // " of which, you have " . count( $packal ) . 214 | // " installed, and you wrote " . $mywf . " of those.", '', 'no', ''); 215 | // } 216 | 217 | $w->result( 'blacklist', 'blacklist', 'Manage Blacklist', 'Configure which workflows Packal updates', 'assets/icons/flag-black.png', 'no', 'blacklist' ); 218 | 219 | $w->result( '', '', 'Informational', 220 | "There are $count workflows in the manifest," . 221 | ' of which, you have ' . count( $packal ) . 222 | ' installed, and you wrote ' . $mywf . ' of those.', 'assets/icons/help-about.png', 'no', ''); 223 | 224 | $w->result( '', 'setup', 'Configure', 'Make this workflow work best for you.', 'assets/icons/applications-system.png', 'no', 'setup' ); 225 | 226 | echo $w->toxml(); 227 | die(); 228 | } 229 | 230 | if ( strpos( $q[1], 'update' ) !== false ) { 231 | if ( ! isset( $updates ) ) { 232 | $updates = array(); 233 | } 234 | if ( count( $updates ) > 0 ) { 235 | if ( count( $updates ) > 1 ) { 236 | // Don't allow the updates if there is no internet connection. 237 | if ( $connection !== false ) { 238 | $w->result( 'update-all', 'update-all', 'Update all workflows', '', '', 'yes', '' ); 239 | } 240 | } 241 | foreach ( $updates as $k => $v ) : 242 | 243 | // Get the workflow icon, if it exists, otherwise, fallback to package icon 244 | if ( file_exists( $v['path'] . '/icon.png' ) ) { 245 | $icon = $v['path'] . '/icon.png'; 246 | } else { $icon = 'assets/icons/package.png'; 247 | } 248 | if ( $connection !== false ) { 249 | $w->result( "update-$k", "update-$k", 'Update ' . $v['name'], 'Update version ' . $v['version'] . ' => ' . $wf[ $k ]['version'], $v['path'] . '/icon.png', '', '' ); 250 | } else { $w->result( "update-$k", '', 'An update for ' . $v['name'] . ' is available.', 'Update version ' . $v['version'] . ' => ' . $wf[ $k ]['version'] . '. << Exception: no viable Internet connection. Update impossible. >>' , $v['path'] . '/icon.png', 'no', '' ); 251 | } 252 | endforeach; 253 | } else { 254 | $w->result( '', '', 'All of your workflows are up to date.', '', 'assets/icons/task-complete.png', 'no', '' ); 255 | } 256 | 257 | echo $w->toxml(); 258 | // We're done here. 259 | die(); 260 | } 261 | 262 | if ( strpos( $q[1], 'setup' ) !== false ) { 263 | 264 | // The next argument removes the forced-setup option. 265 | if ( ( ! file_exists( "$data/config/first-run-alfred" ) ) && ( ! file_exists( "$data/config/first-run" ) ) ) { 266 | file_put_contents( "$data/config/first-run-alfred", 'done' ); 267 | } 268 | 269 | $options = array( 270 | 'authorName' => 'What name do you use when you write workflows?', 271 | 'packalAccount' => 'Do you have an account on Packal?', 272 | 'username' => 'What is your Packal username?', 273 | 'workflowReporting' => 'Would you like to send anonymous data about your installed workflows to Packal.org?', 274 | 'backups' => 'How many backups of workflows would you like to keep?', 275 | ); 276 | foreach ( $options as $k => $v ) : 277 | if ( isset( $config->$k ) ) { 278 | if ( ( (string) $config->$k == '1' ) && ( $k != 'backups' ) ) { 279 | $message = 'Current value: Yes'; 280 | } elseif ( ( (string) $config->$k == '0' ) && ( $k != 'backups' ) ) { 281 | $message = 'Current value: No'; 282 | } else { 283 | if ( ! empty( $config->$k ) ) { 284 | $message = 'Current value: ' . $config->$k; 285 | } else { $message = '< not set >'; 286 | } 287 | } 288 | } else { 289 | $message = 'Not set.'; 290 | } 291 | 292 | // Setup icons per option 293 | $icon = 'assets/icons/'; 294 | switch ( $k ) : 295 | case 'authorName': 296 | $icon .= 'code-context.png'; 297 | break; 298 | case 'username' : 299 | $icon .= 'im-user.png'; 300 | break; 301 | case 'workflowReporting' : 302 | $icon .= 'svn-commit.png'; 303 | break; 304 | case 'backups' : 305 | $icon .= 'player-time.png'; 306 | break; 307 | case 'packalAccount' : 308 | $icon .= 'flag.png'; 309 | break; 310 | default: 311 | $icon = ''; 312 | break; 313 | endswitch; 314 | 315 | if ( ( $k == 'username' ) && ( $config->packalAccount == 1 ) ) { 316 | $w->result( "set-$k", "set-$k", $v, $message, $icon, 'yes', '' ); 317 | } elseif ( ( $k == 'packalAccount' ) && ( $config->packalAccount == 1 ) ) { 318 | continue; 319 | } elseif ( ! ( $k == 'username' ) ) { 320 | $w->result( "set-$k", "set-$k", $v, $message, $icon, 'yes', '' ); 321 | } 322 | endforeach; 323 | 324 | echo $w->toxml(); 325 | die(); 326 | 327 | } elseif ( strpos( $q[1], 'blacklist' ) !== false ) { 328 | // Option to Blacklist a workflow 329 | 330 | // @TODO: Sort the workflows by name. 331 | foreach ( $json as $k => $v ) : 332 | if ( file_exists( "../$v/packal/package.xml" ) ) { 333 | if ( in_array( $k, $manifestBundles ) ) { 334 | if ( ! in_array( $k, $blacklist ) ) { 335 | $w->result( "blacklist-$k", "blacklist-$k", "Add '" . $wf[ $k ]['name'] . "' to blacklist", "Prevent Packal from updating '" . $wf[ $k ]['name'] . "' ($k)", 'assets/icons/user-online.png', 'yes', '' ); 336 | } else { 337 | $w->result( "whitelist-$k", "whitelist-$k", "Remove '" . $wf[ $k ]['name'] . "' from blacklist", "Let Packal update '" . $wf[ $k ]['name'] . "' ($k)", 'assets/icons/user-offline.png', 'yes', '' ); 338 | } 339 | } 340 | } 341 | endforeach; 342 | 343 | echo $w->toxml(); 344 | die(); 345 | } 346 | 347 | // For all good measures.... 348 | echo $w->toxml(); 349 | die(); 350 | -------------------------------------------------------------------------------- /workflow/Packal Updater.alfredworkflow: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/shawnrice/packal-updater/3e7481e04aeece6cf2792a93906735cfecac04d3/workflow/Packal Updater.alfredworkflow --------------------------------------------------------------------------------