diff --git a/.gitignore b/.gitignore
new file mode 100644
index 000000000..f7e953cf5
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,5 @@
+; Ignore modules from make file.
+modules/contrib
+modules/aegir
+themes/contrib
+libraries
diff --git a/.travis.yml b/.travis.yml
new file mode 100644
index 000000000..3798a26ad
--- /dev/null
+++ b/.travis.yml
@@ -0,0 +1,97 @@
+language: php
+
+sudo: required
+
+# Only run test when committing to 1.x branch.
+branches:
+ only:
+ - 7.x-1.x
+
+env:
+ global:
+ - DEVSHOP_VERSION=1.x
+ - SITE_HOSTS='dev.drup.devshop.travis dev.projectname.devshop.travis live.projectname.devshop.travis testenv.drpl8.devshop.travis'
+
+ matrix:
+ - test="Upgrade"
+ COMMAND="robo up --test-upgrade"
+ UPGRADE_FROM_VERSION="1.0.0-beta10"
+
+ - test="Docker Install"
+ COMMAND="robo up --test"
+
+ - test="Ansible on Ubuntu 14.04 Apache"
+ COMMAND="robo up --mode=install.sh --test"
+
+ - test="Ansible on Ubuntu 16.04 Apache"
+ COMMAND='robo up --mode=install.sh --install-sh-image=geerlingguy/docker-ubuntu1604-ansible --test'
+
+# - test="Install with Ansible on Ubuntu 14.04 with NGINX"
+# COMMAND="robo up --mode=install.sh --test --install-sh-options='--server-webserver=nginx'"
+
+# - test="Ansible Install on CentOS 7"
+# COMMAND="robo up --mode=install.sh --install-sh-image=geerlingguy/docker-centos7-ansible --test"
+
+# - test="Install with Ansible on Fedora 25"
+# COMMAND="robo up --mode=install.sh --install-sh-image=centos:7 --test"
+
+services:
+ - docker
+
+before_install:
+ - pwd
+ - env
+
+ # Install Robo
+ - wget https://github.com/consolidation/Robo/releases/download/1.0.5/robo.phar
+ - sudo mv robo.phar /usr/local/bin/robo
+ - sudo chmod +x /usr/local/bin/robo
+
+ # Install Drush
+ - wget https://github.com/drush-ops/drush/releases/download/8.1.9/drush.phar
+ - sudo mv drush.phar /usr/local/bin/drush
+ - sudo chmod +x /usr/local/bin/drush
+
+ # Install drupalorg_drush, for validating the makefile.
+ - drush dl drupalorg_drush-7.x-1.x
+ - drush verify-makefile
+
+ # Clone devshop
+ - cd ..
+ - git clone http://github.com/opendevshop/devshop
+ - cd devshop
+ - git checkout -qf ${DEVSHOP_VERSION}
+ - git status
+ - pwd
+ - cp build-devmaster-travis-forks.make.yml build-devmaster-dev.make.yml
+ - cat build-devmaster-dev.make.yml
+
+ # Prepare devshop CLI.
+ - composer install
+
+script:
+ - ${COMMAND} -n --fork
+
+# - container_id=$(mktemp)
+# # Run container in detached state
+# - 'sudo docker run --detach --name devshop_container --volume="${PWD}/devshop":/usr/share/devshop:rw --volume="${PWD}":/usr/share/devshop/devmaster:rw ${run_opts} -h devshop.travis --add-host "${SITE_HOSTS}":127.0.0.1 ${distribution}-${version}:ansible "${init}" > "${container_id}"'
+#
+# # Install script.
+# - 'sudo docker exec devshop_container env TRAVIS=true TERM=xterm TRAVIS_BRANCH=$TRAVIS_BRANCH TRAVIS_REPO_SLUG=$TRAVIS_REPO_SLUG DEVSHOP_UPGRADE_TO_VERSION=$DEVSHOP_UPGRADE_TO_VERSION TRAVIS_PULL_REQUEST_BRANCH=$TRAVIS_PULL_REQUEST_BRANCH ${install_command} --makefile=/usr/share/devshop/devmaster/build-devmaster-test.make'
+#
+# # Hostmaster Status
+# - 'sudo docker exec devshop_container env TERM=xterm sudo su - aegir -c "drush @hostmaster status"'
+#
+# # Turn off hosting queued, and the hosting task queue.
+# - 'sudo docker exec devshop_container env sudo su - aegir -c "drush @hostmaster dis hosting_queued -y -v"'
+# - 'sudo docker exec devshop_container env sudo su - aegir -c "drush @hostmaster vset hosting_queue_tasks_enabled 0 -y"'
+#
+# # Build and Run Tests
+# - 'sudo docker exec devshop_container env TERM=xterm sudo su - -c "cd /usr/share/devshop/tests && composer update"'
+# - 'sudo docker exec devshop_container env TERM=xterm sudo su - aegir -c "devshop devmaster:test"'
+#
+# # Stop container.
+# - 'sudo docker stop devshop_container'
+
+notifications:
+ slack: thinkdrop:pb05x3ZL3qumHs0RjqEXvYfA
diff --git a/API.txt b/API.txt
new file mode 100644
index 000000000..fbb7fb258
--- /dev/null
+++ b/API.txt
@@ -0,0 +1,13 @@
+DevShop Provision API
+=====================
+
+Document in Progress.
+
+Drush Hooks
+-----------
+
+You can add drush hooks to act before or after devshop tasks.
+
+An example of a post-deploy hook is in the file deploy_hooks_examples/devshop.drush.inc
+
+Copy this file to sites/all/drush and use it for your specific site.
\ No newline at end of file
diff --git a/CODE-OF-CONDUCT.md b/CODE-OF-CONDUCT.md
new file mode 100644
index 000000000..b31d53387
--- /dev/null
+++ b/CODE-OF-CONDUCT.md
@@ -0,0 +1,74 @@
+# Contributor Covenant Code of Conduct
+
+## Our Pledge
+
+In the interest of fostering an open and welcoming environment, we as
+contributors and maintainers pledge to making participation in our project and
+our community a harassment-free experience for everyone, regardless of age, body
+size, disability, ethnicity, sex characteristics, gender identity and expression,
+level of experience, education, socio-economic status, nationality, personal
+appearance, race, religion, or sexual identity and orientation.
+
+## Our Standards
+
+Examples of behavior that contributes to creating a positive environment
+include:
+
+* Using welcoming and inclusive language
+* Being respectful of differing viewpoints and experiences
+* Gracefully accepting constructive criticism
+* Focusing on what is best for the community
+* Showing empathy towards other community members
+
+Examples of unacceptable behavior by participants include:
+
+* The use of sexualized language or imagery and unwelcome sexual attention or
+ advances
+* Trolling, insulting/derogatory comments, and personal or political attacks
+* Public or private harassment
+* Publishing others' private information, such as a physical or electronic
+ address, without explicit permission
+* Other conduct which could reasonably be considered inappropriate in a
+ professional setting
+
+## Our Responsibilities
+
+Project maintainers are responsible for clarifying the standards of acceptable
+behavior and are expected to take appropriate and fair corrective action in
+response to any instances of unacceptable behavior.
+
+Project maintainers have the right and responsibility to remove, edit, or
+reject comments, commits, code, wiki edits, issues, and other contributions
+that are not aligned to this Code of Conduct, or to ban temporarily or
+permanently any contributor for other behaviors that they deem inappropriate,
+threatening, offensive, or harmful.
+
+## Scope
+
+This Code of Conduct applies both within project spaces and in public spaces
+when an individual is representing the project or its community. Examples of
+representing a project or community include using an official project e-mail
+address, posting via an official social media account, or acting as an appointed
+representative at an online or offline event. Representation of a project may be
+further defined and clarified by project maintainers.
+
+## Enforcement
+
+Instances of abusive, harassing, or otherwise unacceptable behavior may be
+reported by contacting the project team at [INSERT EMAIL ADDRESS]. All
+complaints will be reviewed and investigated and will result in a response that
+is deemed necessary and appropriate to the circumstances. The project team is
+obligated to maintain confidentiality with regard to the reporter of an incident.
+Further details of specific enforcement policies may be posted separately.
+
+Project maintainers who do not follow or enforce the Code of Conduct in good
+faith may face temporary or permanent repercussions as determined by other
+members of the project's leadership.
+
+## Attribution
+
+This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4,
+available at https://www.contributor-covenant.org/version/1/4/code-of-conduct.html
+
+[homepage]: https://www.contributor-covenant.org
+
diff --git a/README.md b/README.md
new file mode 100644
index 000000000..76f721ca9
--- /dev/null
+++ b/README.md
@@ -0,0 +1,17 @@
+DevShop DevMaster
+=================
+
+This is the DevShop web-based front-end, called Devmaster.
+
+It is a Drupal Install Profile and Makefile, otherwise known as a "Distribution".
+
+Please fork this repo if you wish to contribute to the Drupal based front-end of DevShop.
+
+More information about DevShop can be found in the [main project repository](https://github.com/opendevshop/devshop).
+
+Issues & Development
+====================
+
+All issues for any DevShop repository are located in the main project: [https://github.com/opendevshop/devshop/issues](https://github.com/opendevshop/devshop/issues)
+
+To contribute to development, please see the [Development](https://docs.opendevshop.com/development.html) section of the documentation.
diff --git a/VERSION.txt b/VERSION.txt
new file mode 100644
index 000000000..525a4bd0e
--- /dev/null
+++ b/VERSION.txt
@@ -0,0 +1 @@
+1.x
\ No newline at end of file
diff --git a/build-devmaster.make b/build-devmaster.make
new file mode 100644
index 000000000..850ba28dc
--- /dev/null
+++ b/build-devmaster.make
@@ -0,0 +1,13 @@
+;
+; Loads the DevMaster install profile from drupal.org.
+;
+; This makefile is used by the DevShop standalone installer to build devmaster.
+;
+
+core = 7.x
+api = 2
+
+includes[] = drupal-org-core.make
+projects[devmaster][type] = profile
+projects[devmaster][download][type] = git
+projects[devmaster][download][branch] = 7.x-1.x
\ No newline at end of file
diff --git a/composer.json b/composer.json
new file mode 100644
index 000000000..cd017603a
--- /dev/null
+++ b/composer.json
@@ -0,0 +1,14 @@
+{
+ "require": {
+ "cpliakas/git-wrapper": "~1.0",
+ "knplabs/github-api": "~1.2",
+ "sensiolabs/ansi-to-html": "^1.1",
+ "symfony/yaml": "^3.0",
+ "symfony/process": "^2.7"
+ },
+ "config": {
+ "platform": {
+ "php": "5.6.0"
+ }
+ }
+}
\ No newline at end of file
diff --git a/composer.lock b/composer.lock
new file mode 100644
index 000000000..2e353da8e
--- /dev/null
+++ b/composer.lock
@@ -0,0 +1,495 @@
+{
+ "_readme": [
+ "This file locks the dependencies of your project to a known state",
+ "Read more about it at https://getcomposer.org/doc/01-basic-usage.md#installing-dependencies",
+ "This file is @generated automatically"
+ ],
+ "content-hash": "3177468b6bfc86758c5512500dda5296",
+ "packages": [
+ {
+ "name": "cpliakas/git-wrapper",
+ "version": "1.7.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/cpliakas/git-wrapper.git",
+ "reference": "1a2f1131ec9ebe04a0b729b141396fa55f992d44"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/cpliakas/git-wrapper/zipball/1a2f1131ec9ebe04a0b729b141396fa55f992d44",
+ "reference": "1a2f1131ec9ebe04a0b729b141396fa55f992d44",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0",
+ "symfony/event-dispatcher": "~2.3|~3.0",
+ "symfony/process": "~2.3|~3.0"
+ },
+ "require-dev": {
+ "pdepend/pdepend": "~1.0",
+ "phploc/phploc": "~2.0",
+ "phpmd/phpmd": "~1.0",
+ "phpunit/phpunit": "~3.0",
+ "psr/log": "~1.0",
+ "scrutinizer/ocular": "~1.0",
+ "sebastian/phpcpd": "~2.0",
+ "symfony/filesystem": "~2.0"
+ },
+ "suggest": {
+ "monolog/monolog": "Enables logging of executed git commands"
+ },
+ "type": "library",
+ "autoload": {
+ "psr-0": {
+ "GitWrapper": "src/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Chris Pliakas",
+ "email": "opensource@chrispliakas.com"
+ }
+ ],
+ "description": "A PHP wrapper around the Git command line utility.",
+ "homepage": "https://github.com/cpliakas/git-wrapper",
+ "keywords": [
+ "git"
+ ],
+ "time": "2016-04-19T16:12:33+00:00"
+ },
+ {
+ "name": "guzzle/guzzle",
+ "version": "v3.9.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/guzzle/guzzle3.git",
+ "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/guzzle/guzzle3/zipball/0645b70d953bc1c067bbc8d5bc53194706b628d9",
+ "reference": "0645b70d953bc1c067bbc8d5bc53194706b628d9",
+ "shasum": ""
+ },
+ "require": {
+ "ext-curl": "*",
+ "php": ">=5.3.3",
+ "symfony/event-dispatcher": "~2.1"
+ },
+ "replace": {
+ "guzzle/batch": "self.version",
+ "guzzle/cache": "self.version",
+ "guzzle/common": "self.version",
+ "guzzle/http": "self.version",
+ "guzzle/inflection": "self.version",
+ "guzzle/iterator": "self.version",
+ "guzzle/log": "self.version",
+ "guzzle/parser": "self.version",
+ "guzzle/plugin": "self.version",
+ "guzzle/plugin-async": "self.version",
+ "guzzle/plugin-backoff": "self.version",
+ "guzzle/plugin-cache": "self.version",
+ "guzzle/plugin-cookie": "self.version",
+ "guzzle/plugin-curlauth": "self.version",
+ "guzzle/plugin-error-response": "self.version",
+ "guzzle/plugin-history": "self.version",
+ "guzzle/plugin-log": "self.version",
+ "guzzle/plugin-md5": "self.version",
+ "guzzle/plugin-mock": "self.version",
+ "guzzle/plugin-oauth": "self.version",
+ "guzzle/service": "self.version",
+ "guzzle/stream": "self.version"
+ },
+ "require-dev": {
+ "doctrine/cache": "~1.3",
+ "monolog/monolog": "~1.0",
+ "phpunit/phpunit": "3.7.*",
+ "psr/log": "~1.0",
+ "symfony/class-loader": "~2.1",
+ "zendframework/zend-cache": "2.*,<2.3",
+ "zendframework/zend-log": "2.*,<2.3"
+ },
+ "suggest": {
+ "guzzlehttp/guzzle": "Guzzle 5 has moved to a new package name. The package you have installed, Guzzle 3, is deprecated."
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "3.9-dev"
+ }
+ },
+ "autoload": {
+ "psr-0": {
+ "Guzzle": "src/",
+ "Guzzle\\Tests": "tests/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Michael Dowling",
+ "email": "mtdowling@gmail.com",
+ "homepage": "https://github.com/mtdowling"
+ },
+ {
+ "name": "Guzzle Community",
+ "homepage": "https://github.com/guzzle/guzzle/contributors"
+ }
+ ],
+ "description": "PHP HTTP client. This library is deprecated in favor of https://packagist.org/packages/guzzlehttp/guzzle",
+ "homepage": "http://guzzlephp.org/",
+ "keywords": [
+ "client",
+ "curl",
+ "framework",
+ "http",
+ "http client",
+ "rest",
+ "web service"
+ ],
+ "abandoned": "guzzlehttp/guzzle",
+ "time": "2015-03-18T18:23:50+00:00"
+ },
+ {
+ "name": "knplabs/github-api",
+ "version": "1.7.1",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/KnpLabs/php-github-api.git",
+ "reference": "98d0bcd2c4c96a40ded9081f8f6289907f73823c"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/KnpLabs/php-github-api/zipball/98d0bcd2c4c96a40ded9081f8f6289907f73823c",
+ "reference": "98d0bcd2c4c96a40ded9081f8f6289907f73823c",
+ "shasum": ""
+ },
+ "require": {
+ "ext-curl": "*",
+ "guzzle/guzzle": "~3.7",
+ "php": ">=5.3.2"
+ },
+ "require-dev": {
+ "phpunit/phpunit": "~4.0",
+ "sllh/php-cs-fixer-styleci-bridge": "~1.3"
+ },
+ "suggest": {
+ "knplabs/gaufrette": "Needed for optional Gaufrette cache"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.8.x-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Github\\": "lib/Github/"
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Thibault Duplessis",
+ "email": "thibault.duplessis@gmail.com",
+ "homepage": "http://ornicar.github.com"
+ },
+ {
+ "name": "KnpLabs Team",
+ "homepage": "http://knplabs.com"
+ }
+ ],
+ "description": "GitHub API v3 client",
+ "homepage": "https://github.com/KnpLabs/php-github-api",
+ "keywords": [
+ "api",
+ "gh",
+ "gist",
+ "github"
+ ],
+ "time": "2016-07-26T08:49:38+00:00"
+ },
+ {
+ "name": "sensiolabs/ansi-to-html",
+ "version": "v1.1.3",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/sensiolabs/ansi-to-html.git",
+ "reference": "8b5d787dca714bd98dd770c078d76528320a8286"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/sensiolabs/ansi-to-html/zipball/8b5d787dca714bd98dd770c078d76528320a8286",
+ "reference": "8b5d787dca714bd98dd770c078d76528320a8286",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.0"
+ },
+ "suggest": {
+ "twig/twig": "Provides nice templating features"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.1-dev"
+ }
+ },
+ "autoload": {
+ "psr-0": {
+ "SensioLabs\\AnsiConverter": "."
+ }
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ }
+ ],
+ "description": "A library to convert a text with ANSI codes to HTML",
+ "time": "2017-05-02T00:53:29+00:00"
+ },
+ {
+ "name": "symfony/event-dispatcher",
+ "version": "v2.8.48",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/event-dispatcher.git",
+ "reference": "a77e974a5fecb4398833b0709210e3d5e334ffb0"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/event-dispatcher/zipball/a77e974a5fecb4398833b0709210e3d5e334ffb0",
+ "reference": "a77e974a5fecb4398833b0709210e3d5e334ffb0",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.9"
+ },
+ "require-dev": {
+ "psr/log": "~1.0",
+ "symfony/config": "^2.0.5|~3.0.0",
+ "symfony/dependency-injection": "~2.6|~3.0.0",
+ "symfony/expression-language": "~2.6|~3.0.0",
+ "symfony/stopwatch": "~2.3|~3.0.0"
+ },
+ "suggest": {
+ "symfony/dependency-injection": "",
+ "symfony/http-kernel": ""
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.8-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\EventDispatcher\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony EventDispatcher Component",
+ "homepage": "https://symfony.com",
+ "time": "2018-11-21T14:20:20+00:00"
+ },
+ {
+ "name": "symfony/polyfill-ctype",
+ "version": "v1.10.0",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/polyfill-ctype.git",
+ "reference": "e3d826245268269cd66f8326bd8bc066687b4a19"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/polyfill-ctype/zipball/e3d826245268269cd66f8326bd8bc066687b4a19",
+ "reference": "e3d826245268269cd66f8326bd8bc066687b4a19",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.3"
+ },
+ "suggest": {
+ "ext-ctype": "For best performance"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "1.9-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Polyfill\\Ctype\\": ""
+ },
+ "files": [
+ "bootstrap.php"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ },
+ {
+ "name": "Gert de Pagter",
+ "email": "BackEndTea@gmail.com"
+ }
+ ],
+ "description": "Symfony polyfill for ctype functions",
+ "homepage": "https://symfony.com",
+ "keywords": [
+ "compatibility",
+ "ctype",
+ "polyfill",
+ "portable"
+ ],
+ "time": "2018-08-06T14:22:27+00:00"
+ },
+ {
+ "name": "symfony/process",
+ "version": "v2.8.48",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/process.git",
+ "reference": "c3591a09c78639822b0b290d44edb69bf9f05dc8"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/process/zipball/c3591a09c78639822b0b290d44edb69bf9f05dc8",
+ "reference": "c3591a09c78639822b0b290d44edb69bf9f05dc8",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.9"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.8-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Process\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony Process Component",
+ "homepage": "https://symfony.com",
+ "time": "2018-11-11T11:18:13+00:00"
+ },
+ {
+ "name": "symfony/yaml",
+ "version": "v2.8.48",
+ "source": {
+ "type": "git",
+ "url": "https://github.com/symfony/yaml.git",
+ "reference": "02c1859112aa779d9ab394ae4f3381911d84052b"
+ },
+ "dist": {
+ "type": "zip",
+ "url": "https://api.github.com/repos/symfony/yaml/zipball/02c1859112aa779d9ab394ae4f3381911d84052b",
+ "reference": "02c1859112aa779d9ab394ae4f3381911d84052b",
+ "shasum": ""
+ },
+ "require": {
+ "php": ">=5.3.9",
+ "symfony/polyfill-ctype": "~1.8"
+ },
+ "type": "library",
+ "extra": {
+ "branch-alias": {
+ "dev-master": "2.8-dev"
+ }
+ },
+ "autoload": {
+ "psr-4": {
+ "Symfony\\Component\\Yaml\\": ""
+ },
+ "exclude-from-classmap": [
+ "/Tests/"
+ ]
+ },
+ "notification-url": "https://packagist.org/downloads/",
+ "license": [
+ "MIT"
+ ],
+ "authors": [
+ {
+ "name": "Fabien Potencier",
+ "email": "fabien@symfony.com"
+ },
+ {
+ "name": "Symfony Community",
+ "homepage": "https://symfony.com/contributors"
+ }
+ ],
+ "description": "Symfony Yaml Component",
+ "homepage": "https://symfony.com",
+ "time": "2018-11-11T11:18:13+00:00"
+ }
+ ],
+ "packages-dev": [],
+ "aliases": [],
+ "minimum-stability": "stable",
+ "stability-flags": [],
+ "prefer-stable": false,
+ "prefer-lowest": false,
+ "platform": [],
+ "platform-dev": [],
+ "platform-overrides": {
+ "php": "5.3.9"
+ }
+}
diff --git a/devmaster.info b/devmaster.info
new file mode 100644
index 000000000..713f7d7c2
--- /dev/null
+++ b/devmaster.info
@@ -0,0 +1,79 @@
+name = OpenDevShop DevMaster
+description = Web interface for OpenDevShop.
+core = 7.x
+
+; Drupal core
+dependencies[] = block
+dependencies[] = help
+dependencies[] = menu
+dependencies[] = node
+dependencies[] = system
+dependencies[] = user
+dependencies[] = update
+
+; Aegir core
+dependencies[] = hosting
+dependencies[] = hosting_task
+dependencies[] = hosting_client
+dependencies[] = hosting_db_server
+dependencies[] = hosting_site
+dependencies[] = hosting_package
+dependencies[] = hosting_platform
+dependencies[] = hosting_web_server
+dependencies[] = hosting_server
+dependencies[] = hosting_clone
+dependencies[] = hosting_cron
+dependencies[] = hosting_migrate
+dependencies[] = hosting_git_checkout
+dependencies[] = hosting_git_tag
+dependencies[] = hosting_git_commit
+
+; DevShop Aegir
+dependencies[] = hosting_alias
+dependencies[] = hosting_queued
+dependencies[] = hosting_http_basic_auth
+dependencies[] = hosting_sync
+
+dependencies[] = hosting_filemanager
+dependencies[] = hosting_logs
+dependencies[] = hosting_tasks_extra
+dependencies[] = hosting_backup_queue
+dependencies[] = hosting_site_backup_manager
+dependencies[] = hosting_https
+dependencies[] = hosting_apache_https
+
+;dependencies[] = fix_permissions
+;dependencies[] = fix_ownership
+
+; Drupal contrib
+dependencies[] = navbar
+dependencies[] = betterlogin
+dependencies[] = chosen
+dependencies[] = composer_manager
+dependencies[] = views
+dependencies[] = views_bulk_operations
+dependencies[] = actions_permissions
+dependencies[] = r4032login
+dependencies[] = jquery_update
+dependencies[] = module_filter
+dependencies[] = hosting_letsencrypt
+
+; This breaks hosting.install. Removing it for now.
+;dependencies[] = adminrole
+
+; DevShop
+dependencies[] = devshop_hosting
+dependencies[] = devshop_dothooks
+dependencies[] = devshop_projects
+dependencies[] = devshop_pull
+dependencies[] = devshop_github
+dependencies[] = devshop_testing
+dependencies[] = aegir_ssh
+dependencies[] = aegir_download
+dependencies[] = aegir_update
+dependencies[] = aegir_config
+dependencies[] = devshop_permissions
+dependencies[] = devshop_stats
+dependencies[] = devshop_remotes
+dependencies[] = devshop_support_network_client
+
diff --git a/devmaster.make b/devmaster.make
new file mode 100644
index 000000000..b59a66edc
--- /dev/null
+++ b/devmaster.make
@@ -0,0 +1,8 @@
+core = 7.x
+api = 2
+
+; Includes
+
+; This makefile will make sure we get the development code from the Aegir
+; modules instead of the tagged releases.
+includes[devmaster] = drupal-org.make
diff --git a/devmaster.profile b/devmaster.profile
new file mode 100644
index 000000000..1be0e6507
--- /dev/null
+++ b/devmaster.profile
@@ -0,0 +1,352 @@
+platform->server->http_service_type === 'nginx') {
+ module_enable(array('hosting_nginx'));
+ }
+
+ // Bootstrap and create all the initial nodes
+ devmaster_bootstrap();
+
+ // Finalize and setup themes, menus, optional modules etc
+ devmaster_task_finalize();
+}
+
+function devmaster_bootstrap() {
+ /* Default node types and default node */
+ $types = node_types_rebuild();
+
+ variable_set('install_profile', 'devmaster');
+
+ // Initialize the hosting defines
+ hosting_init();
+
+ /* Default client */
+ $node = new stdClass();
+ $node->uid = 1;
+ $node->type = 'client';
+ $node->title = drush_get_option('client_name', 'admin');
+ $node->status = 1;
+ node_save($node);
+ variable_set('hosting_default_client', $node->nid);
+ variable_set('hosting_admin_client', $node->nid);
+
+ $client_id = $node->nid;
+
+ /* Default server */
+ $node = new stdClass();
+ $node->uid = 1;
+ $node->type = 'server';
+ $node->title = php_uname('n');
+ $node->status = 1;
+ $node->hosting_name = 'server_master';
+ $node->services = array();
+
+ /* Make it compatible with more than apache and nginx */
+ $master_server = d()->platform->server;
+
+ // Force https_apache
+ hosting_services_add($node, 'http', 'https_apache', array(
+ 'restart_cmd' => $master_server->http_restart_cmd,
+ 'port' => 80,
+ 'https_port' => 443,
+ 'available' => 1,
+ ));
+
+ // Add Certificate service.
+ hosting_services_add($node, 'Certificate', 'LetsEncrypt', array(
+ 'letsencrypt_ca' => 'production'
+ ));
+
+ /* examine the db server associated with the hostmaster site */
+ $db_server = d()->db_server;
+ $master_db = parse_url($db_server->master_db);
+ /* if it's not the same server as the master server, create a new node
+ * for it */
+ if ($db_server->remote_host == $master_server->remote_host) {
+ $db_node = $node;
+ } else {
+ $db_node = new stdClass();
+ $db_node->uid = 1;
+ $db_node->type = 'server';
+ $db_node->title = $master_db['host'];
+ $db_node->status = 1;
+ $db_node->hosting_name = 'server_' . $db_server->remote_host;
+ $db_node->services = array();
+ }
+ hosting_services_add($db_node, 'db', $db_server->db_service_type, array(
+ 'db_type' => $master_db['scheme'],
+ 'db_user' => urldecode($master_db['user']),
+ 'db_passwd' => isset($master_db['pass']) ? urldecode($master_db['pass']) : '',
+ 'port' => 3306,
+ 'available' => 1,
+ ));
+
+ drupal_set_message(st('Creating master server node'));
+ node_save($node);
+ if ($db_server->remote_host != $master_server->remote_host) {
+ drupal_set_message(st('Creating db server node'));
+ node_save($db_node);
+ }
+ variable_set('hosting_default_web_server', $node->nid);
+ variable_set('hosting_own_web_server', $node->nid);
+
+ variable_set('hosting_default_db_server', $db_node->nid);
+ variable_set('hosting_own_db_server', $db_node->nid);
+
+ // Create the hostmaster platform & packages
+ $node = new stdClass();
+ $node->uid = 1;
+ $node->title = 'Drupal';
+ $node->type = 'package';
+ $node->package_type = 'platform';
+ $node->short_name = 'drupal';
+ $node->old_short_name = 'drupal';
+ $node->description = 'Drupal code-base.';
+ $node->status = 1;
+ node_save($node);
+ $package_id = $node->nid;
+
+ // @TODO: We need to still call these nodes "hostmaster" because the aliases are still @hostmaster and @platform_hostmaster
+ $node = new stdClass();
+ $node->uid = 1;
+ $node->type = 'platform';
+ $node->title = 'hostmaster';
+ $node->publish_path = d()->root;
+ $node->makefile = '';
+ $node->verified = 1;
+ $node->web_server = variable_get('hosting_default_web_server', 2);
+ $node->platform_status = 1;
+ $node->status = 1;
+ $node->make_working_copy = 0;
+ node_save($node);
+ $platform_id = $node->nid;
+ variable_set('hosting_own_platform', $node->nid);
+
+ $instance = new stdClass();
+ $instance->rid = $node->nid;
+ $instance->version = VERSION;
+ $instance->filename = '';
+ $instance->version_code = 1;
+ //$instance->schema_version = drupal_get_installed_schema_version('system');
+ $instance->schema_version = 0;
+ $instance->package_id = $package_id;
+ $instance->status = 0;
+ $instance->platform = $platform_id;
+ hosting_package_instance_save($instance);
+
+ // Create the hostmaster profile package node
+ $node = new stdClass();
+ $node->uid = 1;
+ $node->title = 'devmaster';
+ $node->type = 'package';
+ $node->old_short_name = 'devmaster';
+ $node->description = 'The Devmaster profile.';
+ $node->package_type = 'profile';
+ $node->short_name = 'devmaster';
+ $node->status = 1;
+ node_save($node);
+ $profile_id = $node->nid;
+
+ $instance = new stdClass();
+ $instance->rid = $node->nid;
+ $instance->version = VERSION;
+ $instance->filename = '';
+ $instance->version_code = 1;
+ //$instance->schema_version = drupal_get_installed_schema_version('system');
+ $instance->schema_version = 0;
+ $instance->package_id = $profile_id;
+ $instance->status = 0;
+ $instance->platform = $platform_id;
+ hosting_package_instance_save($instance);
+
+ // Create the main Aegir site node
+ $node = new stdClass();
+ $node->uid = 1;
+ $node->type = 'site';
+ $node->title = d()->uri;
+ $node->platform = $platform_id;
+ $node->client = $client_id;
+ $node->db_name = '';
+ $node->db_server = $db_node->nid;
+ $node->profile = $profile_id;
+ $node->import = true;
+ $node->hosting_name = 'hostmaster';
+ $node->site_status = 1;
+ $node->verified = 1;
+ $node->status = 1;
+
+ // If this site's hostname has a public DNS record, then LetsEncrypt will
+ // work, so set the hostmaster site node https_enabled = HOSTING_HTTPS_REQUIRED
+ $records = dns_get_record($node->title);
+ foreach ($records as $record) {
+ if (
+ ($record['type'] == 'A' || $record['type'] == 'CNAME') &&
+ $record['ip'] != '127.0.0.1' &&
+ $record['ip'] != '127.0.1.1') {
+ $node->https_enabled = HOSTING_HTTPS_ENABLED;
+
+ drupal_set_message(t('Public DNS found for !url. Enabling HTTPS with LetsEncrypt.', array(
+ '!url' => $node->title,
+ )), 'success');
+ }
+ }
+ if ($node->https_enabled != HOSTING_HTTPS_ENABLED) {
+ drupal_set_message(t('No public DNS record found for !url. Not enabling LetsEncrypt HTTPS for Hostmaster site.', array(
+ '!url' => $node->title,
+ )), 'warning');
+ $node->https_enabled = HOSTING_HTTPS_DISABLED;
+ }
+
+ node_save($node);
+
+ // Save the hostmaster site nid.
+ variable_set('aegir_hostmaster_site_nid', $node->nid);
+
+ // Enable the hosting features of modules that we enable by default.
+ // The module will already be enabled,
+ // this makes sure we also set the default permissions.
+ $default_hosting_features = array(
+ 'hosting_web_server' => 'web_server',
+ 'hosting_db_server' => 'db_server',
+ 'hosting_platform' => 'platform',
+ 'hosting_client' => 'client',
+ 'hosting_task' => 'task',
+ 'hosting_server' => 'server',
+ 'hosting_package' => 'package',
+ 'hosting_site' => 'site',
+ 'hosting' => 'hosting',
+ );
+ hosting_features_enable($default_hosting_features, $rebuild = TRUE, $enable = FALSE);
+
+ // Set the frontpage
+ variable_set('site_frontpage', 'devshop');
+
+ // Set the sitename
+ variable_set('site_name', 'DevShop');
+
+ // do not allow user registration: the signup form will do that
+ variable_set('user_register', 0);
+
+ // This is saved because the config generation script is running via drush, and does not have access to this value
+ variable_set('install_url' , $GLOBALS['base_url']);
+
+ // Disable backup queue for sites by default.
+ variable_set('hosting_backup_queue_default_enabled', 0);
+
+ // Set hosting_logs default folder.
+ variable_set('provision_logs_file_path', '/var/log/aegir');
+}
+
+function devmaster_task_finalize() {
+
+ // Enable "boots" theme.
+ drupal_set_message(st('Enabling "boots" theme'));
+ $theme = 'boots';
+ theme_enable(array($theme));
+ variable_set('theme_default', $theme);
+
+ // Disable the default Bartik theme
+ theme_disable(array('bartik'));
+
+ drupal_set_message(st('Configuring default blocks'));
+ devmaster_place_blocks($theme);
+
+ // Save "menu_options" for our content types, so they don't offer to be put in menus.
+ variable_set('menu_options_client', '');
+ variable_set('menu_options_platform', '');
+ variable_set('menu_options_server', '');
+ variable_set('menu_options_site', '');
+
+// drupal_set_message(st('Configuring default blocks'));
+// install_add_block('devshop_hosting', 'devshop_tasks', $theme, 1, 5, 'header', 1);
+//
+// // @TODO: CREATE DEVSHOP ROLES!
+// drupal_set_message(st('Configuring roles'));
+// install_remove_permissions(install_get_rid('anonymous user'), array('access content', 'access all views'));
+// install_remove_permissions(install_get_rid('authenticated user'), array('access content', 'access all views'));
+// install_add_permissions(install_get_rid('anonymous user'), array('access disabled sites'));
+// install_add_permissions(install_get_rid('authenticated user'), array('access disabled sites'));
+//
+// // Create administrator role
+// $rid = install_add_role('administrator');
+// variable_set('user_admin_role', $rid);
+
+ // Hide errors from the screen.
+ variable_set('error_level', 0);
+
+ // Disable Aegir's "Welcome" page
+ variable_set('hosting_welcome_page', 0);
+
+ // Disable menu settings for projects
+ variable_set('menu_options_project', '');
+
+ // Force things to delete even if things fail.
+ variable_set('hosting_delete_force', 1);
+
+ // Don't require users to have a client to create a site.
+ variable_set('hosting_client_require_client_to_create_site', 0);
+
+ // Don't automatically import sites.
+ variable_set('hosting_platform_automatic_site_import', 0);
+
+ // Make sure "chosen" widget allows "contains" string searching.
+ variable_set('chosen_search_contains', 1);
+ variable_set('chosen_jquery_selector', 'select:visible');
+ variable_set('chosen_minimum_single', 0);
+ variable_set('chosen_minimum_multiple', 0);
+
+ // Make sure blocks are setup properly.
+// _block_rehash();
+
+ // Rebuild node access permissions.
+ node_access_rebuild();
+}
+
+/**
+ * Helper function to place block.
+ */
+function devmaster_place_blocks($theme) {
+ $blocks = array(
+ array(
+ 'module' => 'devshop_projects',
+ 'delta' => 'project_nav',
+ 'theme' => $theme,
+ 'status' => 1,
+ 'weight' => -1,
+ 'region' => 'header',
+ 'visibility' => 0,
+ 'pages' => '',
+ 'cache' => -1,
+ ),
+ array(
+ 'module' => 'devshop_projects',
+ 'delta' => 'project_create',
+ 'theme' => $theme,
+ 'status' => 1,
+ 'weight' => -1,
+ 'region' => 'sidebar_first',
+ 'visibility' => 0,
+ 'pages' => '',
+ 'cache' => -1,
+ ),
+ );
+
+ $query = db_insert('block')->fields(array('module', 'delta', 'theme', 'status', 'weight', 'region', 'visibility', 'pages', 'cache'));
+
+ foreach ($blocks as $block) {
+ $query->values($block);
+ }
+ $query->execute();
+
+}
diff --git a/drupal-org-core.make b/drupal-org-core.make
new file mode 100644
index 000000000..c9bcc1b0c
--- /dev/null
+++ b/drupal-org-core.make
@@ -0,0 +1,11 @@
+core = 7.x
+api = 2
+
+; PLEASE NOTE: DevShop installs from the makefile https://github.com/opendevshop/devshop/blob/1.x/build-devmaster.make
+; THIS VERSION NUMBER HAS NO EFFECT when using the devshop install process. This only affects drupal.org tarballs.
+; Make sure you change Drupal core version number in the files:
+; - https://github.com/opendevshop/devshop/blob/1.x/build-devmaster.make
+; - https://github.com/opendevshop/devshop/blob/1.x/build-devmaster-dev.yml
+; - https://github.com/opendevshop/devshop/blob/1.x/build-devmaster-travis-forks.make.yml
+projects[drupal][type] = core
+projects[drupal][version] = 7.65
diff --git a/drupal-org.make b/drupal-org.make
new file mode 100644
index 000000000..1ab390b71
--- /dev/null
+++ b/drupal-org.make
@@ -0,0 +1,114 @@
+core = 7.x
+api = 2
+
+defaults[projects][subdir] = "contrib"
+defaults[projects][type] = "module"
+
+; Update this with each new release of devshop
+projects[devshop_stats][version] = 1.x
+projects[devshop_stats][subdir] = "contrib"
+
+; Aegir Modules
+; For development, use latest branch.
+; For release, use tagged version
+projects[hosting][subdir] = aegir
+projects[hosting][version] = "3.170"
+projects[hosting][patch][] = "https://www.drupal.org/files/issues/2018-12-12/3020169-permission-check.patch"
+projects[hosting][patch][] = "https://www.drupal.org/files/issues/2018-12-05/3018114-client-optional.patch"
+projects[hosting][patch][] = "https://www.drupal.org/files/issues/2018-12-10/3019462-administer-servers.patch"
+
+; Aegir Core not included in hosting.module
+projects[eldir][type] = theme
+projects[eldir][version] = "3.170"
+
+projects[hosting_git][subdir] = aegir
+projects[hosting_git][version] = "3.170"
+
+projects[hosting_https][subdir] = aegir
+projects[hosting_https][version] = "3.171"
+
+projects[hosting_remote_import][subdir] = aegir
+projects[hosting_remote_import][version] = "3.170"
+
+projects[hosting_site_backup_manager][subdir] = aegir
+projects[hosting_site_backup_manager][version] = "3.170"
+
+projects[hosting_tasks_extra][subdir] = aegir
+projects[hosting_tasks_extra][version] = "3.170"
+
+projects[hosting_logs][subdir] = aegir
+projects[hosting_logs][version] = "3.170"
+
+projects[hosting_filemanager][subdir] = aegir
+projects[hosting_filemanager][version] = "1.x"
+
+projects[aegir_ssh][subdir] = aegir
+projects[aegir_ssh][version] = 1.0
+
+projects[aegir_config][subdir] = aegir
+projects[aegir_config][version] = 1.00-beta1
+
+; Not working yet.
+;projects[hosting_solr][version] = "1"
+
+; Contrib Modules
+projects[sshkey][version] = "2.0"
+projects[betterlogin][version] = 1.5
+projects[composer_manager][version] = 1.8
+projects[entity][version] = 1.9
+projects[openidadmin][version] = 1.0
+projects[overlay_paths][version] = 1.3
+projects[r4032login][version] = 1.8
+projects[admin_menu][version] = "3.0-rc6"
+projects[adminrole][version] = "1.1"
+projects[jquery_update][version] = "3.0-alpha5"
+projects[views][version] = "3.22"
+projects[views_bulk_operations][version] = "3.5"
+projects[ctools][version] = "1.15"
+projects[features][version] = "2.11"
+projects[distro_update][version] = "1"
+projects[module_filter][version] = "2.2"
+projects[libraries][version] = 2.5
+projects[token][version] = 1.7
+; projects[hybridauth][version] = 2.15
+projects[statsd][version] = 1.1
+projects[hosting_statsd][version] = 1.0-beta1
+projects[intercomio][version] = 1.0-beta2
+projects[navbar][version] = 1.7
+projects[chosen][version] = 2.1
+
+projects[cas][version] = 1.7
+projects[cas][patch][] = "https://www.drupal.org/files/issues/2018-12-13/3020349-cas-library-path.patch"
+projects[cas_attributes][version] = 1.0-rc3
+
+; Bootstrap base theme
+projects[bootstrap][type] = theme
+projects[bootstrap][version] = "3.23"
+
+; Timeago module
+projects[timeago][version] = 2.3
+
+; JQuery TimeAgo plugin
+libraries[timeago][download][type] = get
+libraries[timeago][download][url] = https://raw.githubusercontent.com/rmm5t/jquery-timeago/v1.5.3/jquery.timeago.js
+libraries[timeago][destination] = libraries
+
+; @TODO: Uncomment once it is in the whitelist: https://www.drupal.org/project/drupalorg_whitelist/issues/3024898
+; Library: Modernizr
+; libraries[modernizr][download][type] = git
+; libraries[modernizr][download][url] = https://github.com/BrianGilbert/modernizer-navbar.git
+; libraries[modernizr][download][revision] = 5b89d9225320e88588f1cdc43b8b1e373fa4c60f
+
+; Library: Backbone
+libraries[backbone][download][type] = git
+libraries[backbone][download][url] = https://github.com/jashkenas/backbone.git
+libraries[backbone][download][tag] = 1.0.0
+
+; Library: Underscore
+libraries[underscore][download][type] = git
+libraries[underscore][download][url] = https://github.com/jashkenas/underscore.git
+libraries[underscore][download][tag] = 1.5.0
+
+; Library: Chosen
+libraries[chosen][download][type] = "git"
+libraries[chosen][download][url] = "https://github.com/harvesthq/chosen-package.git"
diff --git a/logo.png b/logo.png
new file mode 100644
index 000000000..13c356bc0
Binary files /dev/null and b/logo.png differ
diff --git a/modules/devshop/README.txt b/modules/devshop/README.txt
new file mode 100644
index 000000000..95fcdd2c7
--- /dev/null
+++ b/modules/devshop/README.txt
@@ -0,0 +1,59 @@
+
+DevShop Hosting
+===============
+Drupal development infrastructure made easy.
+
+This module provides the front-end interface needed to
+deploy and manage sites using the DevShop git and features
+based development workflow.
+
+About DevShop
+-------------
+The goals of DevShop are...
+
+1. to simplify management of multiple environments for multiple Drupal projects.
+2. to provide web-based tools that streamline the Drupal site building workflow.
+3. to provide a common, open-source infrastructure for Drupal development shops.
+
+
+Installation
+------------
+For installation instructions, see INSTALL.txt.
+
+
+DevShop Projects
+----------------
+DevShop functionality centers around "Projects". Aegir Project nodes store a
+Git URL, the code path, the "base url", and the branches of the remote
+repository.
+
+DevShop allows multiple platforms and sites (for dev, test, or live purposes)
+to be created very easily. Platforms can be easily created from existing
+branches of your git repositories.
+
+
+Creating Projects
+-----------------
+
+To create a new project, visit either the Projects page or click
+"Create Content" > "DevShop Project".
+
+### Step 1: Git URL and project name.
+
+Enter your project's Git URL and Project Name.
+
+NOTE: Your project's git repo must be a complete drupal core file set. It
+should match the structure of Drupal core's git repository, and can be a clone
+of http://git.drupalcode.org/project/drupal.git
+
+### Step 2: File Path and Base URL
+
+Enter the base path to the Project's code. Recommended is /var/aegir/projects
+
+Enter the base URL for the project. All Project Sites will be on a subdomain of this base URL.
+
+### Step 3: Choose Platforms
+
+To complete this step, the Verify Project task must finish.
+
+On this page, you choose if you want dev, test, or live platforms and what branches each should live on. You can also choose the branches of your git repository you wish to create platforms and sites for.
diff --git a/modules/devshop/aegir_download/aegir_download.info b/modules/devshop/aegir_download/aegir_download.info
new file mode 100644
index 000000000..6a3ff40ed
--- /dev/null
+++ b/modules/devshop/aegir_download/aegir_download.info
@@ -0,0 +1,4 @@
+name = Aegir Download
+description = Adds the ability to download packages to aegir hosted sites.
+core = 7.x
+dependencies[] = hosting_site
\ No newline at end of file
diff --git a/modules/devshop/aegir_download/aegir_download.module b/modules/devshop/aegir_download/aegir_download.module
new file mode 100644
index 000000000..9c2e6ada7
--- /dev/null
+++ b/modules/devshop/aegir_download/aegir_download.module
@@ -0,0 +1,84 @@
+ t('Download Modules'),
+ 'description' => t('Add modules or themes to your git repository.'),
+ 'dialog' => TRUE,
+ 'icon' => 'download'
+ );
+ return $tasks;
+}
+
+/**
+ * Implements hook_permission().
+ * @return array
+ */
+function aegir_download_permission() {
+ return array(
+ 'create download task' => array(
+ 'title' => t('create download task'),
+ 'description' => t('Create "download" task.'),
+ ),
+ );
+}
+
+/**
+ * @return mixed
+ */
+function hosting_task_download_form() {
+
+ $form['packages'] = array(
+ '#type' => 'textarea',
+ '#title' => t('Drupal modules or themes to download'),
+ '#description' => '
' . t('Enter the names of the drupal module or themes you would like to download to your project. The names must match the package system name: If you want the module from http://drupal.org/project/views, enter "views" into this field. Separate multiple packages with a space.') . '
' . t('If you enter the name of an existing module, it will overwrite your old version. This is a good way to update your modules. Run Update.php to ensure smooth deployment.') . '
',
+ );
+ $form['commit'] = array(
+ '#title' => t('Commit to git.'),
+ '#type' => 'checkbox',
+ '#default_value' => 1,
+ );
+ $form['message'] = array(
+ '#type' => 'textarea',
+ '#title' => t('Git Commit Message'),
+ '#description' => t('Enter a message to use in the git commit message.'),
+ );
+ $form['update'] = array(
+ '#title' => t('Run Database Updates'),
+ '#type' => 'checkbox',
+ '#default_value' => 1,
+ '#description' => t('If module updates occur, run update.php to update the database. Only use if you trust the modules you are downloading.'),
+ );
+ return $form;
+}
+
+/**
+ * Implements hook_devshop_environment_menu().
+ *
+ * Defines the list of tasks that appear under the gear icon.
+ */
+function aegir_download_devshop_environment_menu($environment) {
+
+ if ($environment->site && $environment->site_status == HOSTING_SITE_ENABLED) {
+ $items[] = 'download';
+ }
+ return $items;
+}
+
+function drush_aegir_download_pre_hosting_task()
+{
+ $task =& drush_get_context('HOSTING_TASK');
+ if ($task->task_type != 'download') {
+ return;
+ }
+
+ drush_log('[AEGIR] Download package enabled...', 'ok');
+
+ $task->options['packages'] = $task->task_args['packages'];
+ $task->options['commit'] = $task->task_args['commit'];
+ $task->options['message'] = $task->task_args['message'];
+ $task->options['update'] = $task->task_args['update'];
+}
diff --git a/modules/devshop/aegir_download/hosting.feature.aegir_download.inc b/modules/devshop/aegir_download/hosting.feature.aegir_download.inc
new file mode 100644
index 000000000..c5d501920
--- /dev/null
+++ b/modules/devshop/aegir_download/hosting.feature.aegir_download.inc
@@ -0,0 +1,16 @@
+ t('Download Modules'),
+ 'description' => t('Add modules or themes to your git repository.'),
+ 'status' => HOSTING_FEATURE_ENABLED,
+ 'module' => 'aegir_download',
+ 'group' => 'advanced',
+ );
+ return $features;
+}
diff --git a/modules/devshop/aegir_update/aegir_update.info b/modules/devshop/aegir_update/aegir_update.info
new file mode 100644
index 000000000..7482630bc
--- /dev/null
+++ b/modules/devshop/aegir_update/aegir_update.info
@@ -0,0 +1,4 @@
+name = Aegir Update
+description = Adds the ability to update your Drupal site using Aegir.
+core = 7.x
+dependencies[] = hosting_site
\ No newline at end of file
diff --git a/modules/devshop/aegir_update/aegir_update.module b/modules/devshop/aegir_update/aegir_update.module
new file mode 100644
index 000000000..70a4bc6d7
--- /dev/null
+++ b/modules/devshop/aegir_update/aegir_update.module
@@ -0,0 +1,84 @@
+ t('Update Drupal'),
+ 'description' => t('Upgrades drupal core and contrib to the latest versions.'),
+ 'dialog' => TRUE,
+ 'icon' => 'wrench'
+ );
+ return $tasks;
+}
+
+/**
+ * Implements hook_permission().
+ * @return array
+ */
+function aegir_update_permission() {
+ return array(
+ 'create update_drupal task' => array(
+ 'title' => t('create update_drupal task'),
+ 'description' => t('Create "Update Drupal" task.'),
+ ),
+ );
+}
+
+/**
+ * @return mixed
+ */
+function hosting_task_update_drupal_form() {
+ $form = array();
+ $form['note'] = array(
+ '#markup' => t('This will run the command drush pm-update on your Drupal site.'),
+ '#prefix' => '
',
+ '#suffix' => '
',
+ );
+ $form['warning'] = array(
+ '#markup' => t('Running this task may have unexpected consequences. It is not recommended to run on a production site.'),
+ '#prefix' => '
',
+ '#suffix' => '
',
+ );
+ $form['commit'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Commit & Push all changes'),
+ '#description' => t('After running the update, commit and push all changes.'),
+ '#default_value' => 1,
+ );
+ return $form;
+}
+
+/**
+ * Implements hook_post_hosting_TASK_TYPE_task().
+ *
+ * Trigger a verify task for the site and the platform.
+ */
+function aegir_update_post_hosting_update_drupal_task($task, $data) {
+
+ $account = user_load($task->uid);
+ if (isset($task->task_args['commit']) && $task->task_args['commit']) {
+ hosting_add_task($task->ref->nid, 'commit', array(
+ 'push' => 1,
+ 'name' => $account->name,
+ 'mail' => $account->mail,
+ ));
+ }
+
+ hosting_add_task($task->ref->platform, 'verify');
+ hosting_add_task($task->ref->nid, 'verify');
+}
+
+/**
+ * Implements hook_devshop_environment_menu().
+ *
+ * Defines the list of tasks that appear under the gear icon.
+ */
+function aegir_update_devshop_environment_menu($environment) {
+
+ if ($environment->site && $environment->site_status == HOSTING_SITE_ENABLED) {
+ $items[] = 'update_drupal';
+ }
+ return $items;
+}
diff --git a/modules/devshop/aegir_update/drush/aegir_update.drush.inc b/modules/devshop/aegir_update/drush/aegir_update.drush.inc
new file mode 100644
index 000000000..c3cbce0fb
--- /dev/null
+++ b/modules/devshop/aegir_update/drush/aegir_update.drush.inc
@@ -0,0 +1,55 @@
+ 'Updates drupal and contributed packages.',
+ 'bootstrap' => DRUSH_BOOTSTRAP_DRUSH,
+ );
+ return $items;
+}
+
+/**
+ * Implements the provision-update command.
+ */
+function drush_aegir_update_provision_update_drupal() {
+ drush_errors_on();
+
+ if (drush_drupal_major_version(d()->root) == 8) {
+// drush_log('Drush command provision-update_drupal is not tet compatible with Drupal 8. Run the command `composer update drupal/core webflo/drupal-core-require-dev symfony/* --with-dependencies');
+
+ $path = d()->platform->repo_path? d()->platform->repo_path: d()->platform->root;
+ if (file_exists($path . '/composer.json')) {
+ provision_process('composer update drupal/core webflo/drupal-core-require-dev symfony/* --with-dependencies', $path);
+ }
+ else {
+ drush_set_error('PROVISION_ERROR', dt('Drupal 8 was detected, but a composer.json file was not found in %path. Updating Drupal 8 automatically requires using a composer-based platform. See https://github.com/drupal-composer/drupal-project for the starting template.', [
+ '%path' => $path,
+ ]));
+ }
+ }
+ elseif (drush_drupal_major_version(d()->root) <= 7) {
+ provision_backend_invoke(d()->name, 'pm-update');
+ }
+ drush_log(dt('Drush pm-update task completed'));
+}
+
+/**
+ * Map values of site node into command line arguments.
+ */
+function drush_aegir_update_pre_hosting_task($task) {
+ $task = &drush_get_context('HOSTING_TASK');
+ if ($task->task_type == 'update_drupal' && !empty($task->task_args['commit'])) {
+ // Pass the argument provision_git expects.
+ $task->options['commit'] = $task->task_args['commit'];
+ }
+}
diff --git a/modules/devshop/aegir_update/hosting.feature.aegir_update.inc b/modules/devshop/aegir_update/hosting.feature.aegir_update.inc
new file mode 100644
index 000000000..d6d4e07ec
--- /dev/null
+++ b/modules/devshop/aegir_update/hosting.feature.aegir_update.inc
@@ -0,0 +1,16 @@
+ t('Update Drupal'),
+ 'description' => t('Upgrades drupal core and contrib to the latest versions.'),
+ 'status' => HOSTING_FEATURE_DISABLED,
+ 'module' => 'aegir_update',
+ 'group' => 'advanced',
+ );
+ return $features;
+}
diff --git a/modules/devshop/devshop.css b/modules/devshop/devshop.css
new file mode 100644
index 000000000..31fd15076
--- /dev/null
+++ b/modules/devshop/devshop.css
@@ -0,0 +1,130 @@
+div#header div.logo a {
+ text-indent: -999px;
+ overflow: hidden;
+ height: 72px !important;
+ width: 72px !important;
+ margin: 10px;
+}
+
+#devshop-project-create-step-environments .platform {
+ clear: both;
+ border-bottom: 1px solid #e8e8e8;
+}
+#devshop-project-create-step-environments .platform .form-item {
+ float: left;
+ width: 46%;
+ clear: none;
+ height: 2.0em;
+ border-bottom: none;
+}
+body.aegir #page div.node div.content fieldset.project-environments div.form-item {
+ padding: 5px;
+}
+
+fieldset.project-environments {
+ clear: both;
+}
+
+#project-settings-table .description,
+#project-settings-table .form-item-labeled label
+{
+ display: none;
+}
+
+textarea#rsa {
+ display: none;
+}
+
+.form-submit {
+ margin-top: 10px;
+}
+#edit-cancel {
+ float: right;
+}
+.command input {
+ font-family: Courier New, monospace;
+ width: 100%;
+}
+#footer {
+ height: 100px !important;
+ line-height: inherit !important;
+}
+#footer h2 {
+ border: none;
+ font-weight: normal;
+ text-transform: uppercase;
+}
+#footer .block {
+ float: left;
+ margin-right: 20px;
+
+}
+
+#block-system-0.block {
+ float: right;
+}
+
+body.node-type-project a.hosting-goto-site-link {
+ background: none;
+ padding: inherit;
+}
+
+/**
+ * Task Block!
+ */
+#block-devshop_hosting-devshop_tasks {
+ position: absolute;
+ top: 0px;
+ margin: 0px auto;
+ width: 940px;
+}
+
+
+#block-devshop_hosting-devshop_tasks .content {
+ position: absolute;
+ right: 0px;
+ top: 40px;
+}
+
+#block-devshop_hosting-devshop_tasks #devshop-task-status {
+ font: bold 13px/20px "Helvetica Neue",Arial,sans-serif;
+ border: 0px;
+ padding: 5px 10px 5px 30px;
+ color: #fff;
+ background: #666;
+ cursor: pointer;
+ -moz-border-radius: 3px;
+ -webkit-border-radius: 3px;
+ width: 200px;
+}
+
+#block-devshop_hosting-devshop_tasks #devshop-task-status.active {
+ background: #666 5px no-repeat url('devshop_projects/images/spinner-white.gif');
+}
+
+#block-devshop_hosting-devshop_tasks #devshop-task-status.inactive {
+ background: #666;
+}
+
+
+#hosting-task-queue-block {
+ display: none;
+ width: 96%;
+ margin: 0px auto;
+}
+
+
+#block-devshop_hosting-devshop_tasks:hover #hosting-task-queue-block {
+ display: block;
+}
+
+
+#block-devshop_hosting-devshop_tasks .task-logs-link {
+ display: block;
+ padding: 5px 10px;
+ background: #999;
+ color: white;
+ text-align: center;
+ font-size: 10pt;
+ text-transform: uppercase;
+}
diff --git a/modules/devshop/devshop_acquia/README.md b/modules/devshop/devshop_acquia/README.md
new file mode 100644
index 000000000..b9257134a
--- /dev/null
+++ b/modules/devshop/devshop_acquia/README.md
@@ -0,0 +1,50 @@
+Acquia Cloud Hooks Integration
+==============================
+
+DevShop now supports firing Acquia Cloud Hooks when deploying to environments.
+
+The code in acquia.drush.inc will detect an acquia repo and trigger your hooks
+to run.
+
+See the Acquia Cloud Hooks repository for more information and samples: https://github.com/acquia/cloud-hooks
+
+Currently, only the `post-code-update` hook is supported. More to come.
+
+Hooks
+=====
+
+### post-code-deploy
+
+Changing branch or tag manually.
+
+DevShop Equivalent: provision-devshop-deploy when triggered from front-end.
+
+```
+post-code-deploy site target-env source-branch deployed-tag repo-url repo-type
+```
+
+### post-code-update
+
+Deploying code to a branch environment automatically.
+
+DevShop Equivalent: provision-devshop-deploy
+
+```
+post-code-deploy site target-env source-branch deployed-tag repo-url repo-type
+```
+
+### post-db-copy
+
+**Coming Soon...*
+
+```
+post-db-copy site target-env db-name source-env
+```
+
+### post-files-copy
+
+**Coming Soon...*
+
+```
+post-files-copy mysite prod dev
+```
\ No newline at end of file
diff --git a/modules/devshop/devshop_acquia/devshop_acquia.info b/modules/devshop/devshop_acquia/devshop_acquia.info
new file mode 100644
index 000000000..46150d210
--- /dev/null
+++ b/modules/devshop/devshop_acquia/devshop_acquia.info
@@ -0,0 +1,5 @@
+name = DevShop Acquia Integration
+description = Allows for Acquia Cloud Hooks to be used as deploy hooks in DevShop.
+core = 7.x
+package = DevShop
+dependencies[] = devshop_hosting
diff --git a/modules/devshop/devshop_acquia/devshop_acquia.module b/modules/devshop/devshop_acquia/devshop_acquia.module
new file mode 100644
index 000000000..34487da19
--- /dev/null
+++ b/modules/devshop/devshop_acquia/devshop_acquia.module
@@ -0,0 +1,151 @@
+ref->type != 'site') {
+ return;
+ }
+
+ // If this is a deploy task, and acquia hooks is not configured to run, return.
+ if ($task->task_type == 'devshop-deploy' && (!isset($task->task_args['acquia_hooks']) || $task->task_args['acquia_hooks'] != 1)) {
+ return;
+ }
+
+ $environment = (object) $task->ref->environment;
+ $project = (object) $task->ref->project;
+
+ // If project has no path to drupal, we know it's not acquia.
+ if ($project->drupal_path != 'docroot' || !file_exists($environment->repo_path . '/hooks')) {
+ drush_log('Acquia Cloud Hooks error', 'p_command');
+ drush_log('./docroot or ./hooks folder is missing, but Acquia hooks are configured to run. Please create the hooks or turn off Acquia Cloud Hooks for the project.', 'p_error');
+ return drush_set_error('DRUSH_ERROR');
+ }
+
+ // Determine the hook to run
+ $hooks = array();
+
+ // If "manual" deployment, it's a post-code-deploy
+ if (isset($task->task_args['manual']) && $task->task_args['manual'] == 1) {
+ $hooks[] = 'post-code-deploy';
+ }
+ // If it is automatic, it's a "post-code-update"
+ elseif ($task->task_type == 'devshop_deploy') {
+ $hooks[] = 'post-code-update';
+ }
+ // If task is sync...
+ elseif ($task->task_type == 'sync') {
+ // If database is copied, run post-db-copy hook.
+ if ($task->task_args['database']) {
+ $hooks[] = 'post-db-copy';
+ }
+ // If files are copied, run post-files-copy hook.
+ if ($task->task_args['files']) {
+ $hooks[] = 'post-files-copy';
+ }
+ }
+
+ foreach ($hooks as $hook) {
+
+ drush_log("[DEVSHOP] Invoking acquia hooks for hook $hook in environment {$environment->name} ... Scanning {$environment->repo_path}/hooks/common/{$hook} & {$environment->repo_path}/hooks/{$environment->name}/{$hook} ", 'ok');
+
+ // Collect cloud hook scripts to run for all environments "common".
+ $files = scandir("{$environment->repo_path}/hooks/common/{$hook}");
+ if (empty($files)) $files = array();
+
+ // The list of files we will run.
+ $scripts = array();
+
+ // Remove incorrect entries from scandir.
+ $files = array_diff($files, array('..', '.', '.gitignore'));
+
+ // Collect the full path to each script.
+ foreach ($files as $script) {
+ $scripts[] = realpath("{$environment->repo_path}/hooks/common/{$hook}/{$script}");
+ }
+
+ // Collect cloud hook scripts to run for this environment.
+ if (file_exists("{$environment->repo_path}/hooks/{$environment->name}/{$hook}")) {
+
+ $files = scandir("{$environment->repo_path}/hooks/{$environment->name}/{$hook}");
+
+ // Remove incorrect entries from scandir.
+ $files= array_diff($files, array('..', '.', '.gitignore'));
+
+ // Collect the full path to each script.
+ foreach ($files as $script) {
+ $scripts[] = realpath("{$environment->repo_path}/hooks/{$environment->name}/{$hook}/{$script}");
+ }
+ }
+
+ // Notify logs how many scripts we've found
+ $count = count($scripts);
+ drush_log("[DEVSHOP] Found $count scripts to run...", 'ok');
+
+ // Run Scripts
+ // @TODO: Implement using symfony:Process component.
+ // Usage: post-code-deploy site target-env source-branch deployed-tag repo-url repo-type
+ foreach ($scripts as $file) {
+ drush_log('[DEVSHOP] Running Acquia Cloud Hook: ' . $file, 'ok');
+ $hook_path = str_replace($environment->repo_path, '', $file);
+ drush_log("[Acquia Cloud Hook $hook] $hook_path", 'p_command');
+
+ // This is a trick. DevShop doesn't have aliases like $project.$environment.
+ // When Acquia cloud hook writers create a script, they will use something like:
+ //
+ // drush_alias=$site'.'$target_env
+ // drush @$drush_alias cc all
+ //
+ // So, by passing "environment->name" as $site, and project.hostname as $target_env,
+ // we can trick the Acquia cloud script to use the right alias.
+ $alias = explode('.', $environment->drush_alias);
+ $environment_name = substr($alias[0], 1);
+
+ $command = "sh $file {$environment_name} {$project->base_url} {$environment_name} old_branch {$environment->git_ref} repo_url repo_type";
+
+ drush_log('[Running] ' . $command, 'p_log');
+
+ $process = new Process($command);
+ $process->setTimeout(NULL);
+ $exit_code = $process->run(function ($type, $buffer) {
+ if (Process::ERR === $type) {
+ drush_log($buffer, 'p_ok');
+ }
+ else {
+ drush_log($buffer, 'p_ok');
+ }
+ });
+
+ // check exit code
+ if ($exit_code === 0) {
+ drush_log("[.hooks] Command executed successfully.", 'ok');
+ }
+ else {
+ drush_log("[.hooks] Hook command failed: $command", 'error');
+ drush_log($command, 'p_error');
+ }
+ }
+ }
+}
diff --git a/modules/devshop/devshop_acquia/example.slack.sh b/modules/devshop/devshop_acquia/example.slack.sh
new file mode 100644
index 000000000..3c4f848d1
--- /dev/null
+++ b/modules/devshop/devshop_acquia/example.slack.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+#
+# Altered from https://github.com/acquia/cloud-hooks/tree/master/samples/slack
+# Requires a /var/aegir/slack_settings file that contains:
+#
+# SLACK_WEBHOOK_URL=https://example.slack.com/services/hooks/incoming-webhook?token=TOKEN
+#
+# Cloud Hook: post-code-deploy
+#
+# The post-code-deploy hook is run whenever you use the Workflow page to
+# deploy new code to an environment, either via drag-drop or by selecting
+# an existing branch or tag from the Code drop-down list. See
+# ../README.md for details.
+#
+# Usage: post-code-deploy site target-env source-branch deployed-tag repo-url
+# repo-type
+
+site="$1"
+target_env="$2"
+source_branch="$3"
+deployed_tag="$4"
+repo_url="$5"
+repo_type="$6"
+source="$7"
+site_url="$8"
+
+# Load the Slack webhook URL (which is not stored in this repo).
+. $HOME/slack_settings
+
+if [ $source = 'devshop' ]; then
+ source="DevShop"
+ image="https://www.drupal.org/files/project-images/devshop-icon.png"
+else
+ source="Acquia Cloud"
+ image="https://pbs.twimg.com/profile_images/1901642489/cloud_icon_150.png"
+fi
+
+# Post deployment notice to Slack
+curl -X POST --data-urlencode "payload={\"channel\": \"#dev-engageny\", \"username\": \"$source\", \"text\": \"Git branch \`$deployed_tag\` updated on *$target_env*: $site_url.\", \"icon_url\": \"$image\"}" $SLACK_WEBHOOK_URL
diff --git a/modules/devshop/devshop_bitbucket/devshop_bitbucket.drush.inc b/modules/devshop/devshop_bitbucket/devshop_bitbucket.drush.inc
new file mode 100644
index 000000000..cb5400855
--- /dev/null
+++ b/modules/devshop/devshop_bitbucket/devshop_bitbucket.drush.inc
@@ -0,0 +1,122 @@
+task_type == 'clone') {
+// $platform = node_load($task->task_args['target_platform']);
+// $environment = $platform->environment;
+// $project = $platform->project;
+// }
+// // Subsequent tasks are "deploy" and "verify"
+// elseif (($task->task_type == 'verify' && $task->ref->type == 'site') || $task->task_type == 'devshop-deploy' || $task->task_type == 'test') {
+// $environment = $task->ref->environment;
+// $project = $task->ref->project;
+// }
+// else {
+// return;
+// }
+//
+// // If a pull request object is available...
+// if (isset($environment->bitbucket_pull_request->pull_request_object->deployment)) {
+//
+// // Create a deployment status
+// $owner = $project->bitbucket_owner;
+// $repo = $project->bitbucket_repo;
+// $deployment_id = $environment->bitbucket_pull_request->pull_request_object->deployment->id;
+//
+// try {
+// $token = variable_get('devshop_bitbucket_token', '');
+// $client = new \bitbucket\Client();
+// $client->authenticate($token, bitbucket\Client::AUTH_HTTP_TOKEN);
+//
+// $params = new stdClass();
+// if ($status == HOSTING_TASK_SUCCESS || $status == HOSTING_TASK_WARNING) {
+// $params->state = $state = 'success';
+// }
+// else {
+// $params->state = $state = 'failure';
+// }
+//
+// // If task is a test run, only submit a commit status for devshop/tests context.
+// if ($task->task_type == 'test') {
+// $sha = $environment->bitbucket_pull_request->pull_request_object->head->sha;
+//
+// $params = new stdClass();
+// $params->state = $state;
+// $params->target_url = url("node/{$task->nid}/revisions/{$task->vid}/view",array('absolute' => TRUE));
+//
+// if ($status == HOSTING_TASK_WARNING) {
+// $params->description = t('DevShop: Tests passed with warnings');
+// }
+// else {
+// $params->description = t('DevShop: Tests !status!', array('!status' => $state));
+// }
+// $params->context = 'devshop/tests';
+//
+// $status = $client->getHttpClient()->post("/repos/$owner/$repo/statuses/$sha", json_encode($params));
+// drush_log('Commit status created for devshop/tests!', 'success');
+// }
+// // Otherwise we create a deployment and a commit status.
+// else {
+//
+// $params->target_url = $environment->url;
+// $params->description = t('Visit !url', array('!url' => $task->ref->environment->url));
+// $post_url = "/repos/$owner/$repo/deployments/{$deployment_id}/statuses";
+//
+// drush_log('Attempting to create bitbucket deployment status: ' . $post_url, 'success');
+//
+// $deployment_status = $client->getHttpClient()->post($post_url, json_encode($params));
+// drush_log('Deployment status created!', 'success');
+//
+//
+// // Update Status API
+//
+// // Create a status
+// $sha = $environment->bitbucket_pull_request->pull_request_object->head->sha;
+//
+// $params = new stdClass();
+// $params->state = $state;
+// $params->target_url = url("node/{$task->nid}", array('absolute' => TRUE));;
+//
+// if ($status == HOSTING_TASK_WARNING) {
+// $params->description = t('DevShop: Deploy success with warnings. [!url]', array(
+// '!url' => $environment->url,
+// ));
+// }
+// else {
+// $params->description = t('DevShop: Deploy !status [!url]', array(
+// '!status' => $state,
+// '!url' => $environment->url,
+// ));
+// }
+// $params->context = 'devshop/deploy';
+//
+// $deployment_status = $client->getHttpClient()->post("/repos/$owner/$repo/statuses/$sha", json_encode($params));
+// drush_log('Commit status created!', 'success');
+//
+// // If deploy task fails, tests won't run.
+// if ($environment->settings->deploy['test'] && $status == HOSTING_TASK_ERROR) {
+//
+// $params = new stdClass();
+// $params->state = $state;
+// $params->description = t('DevShop: Tests not run due to Deploy Fail');
+// $params->context = 'devshop/tests';
+//
+// $deployment_status = $client->getHttpClient()->post("/repos/$owner/$repo/statuses/$sha", json_encode($params));
+// drush_log('Commit status created for devshop/tests', 'success');
+// }
+// }
+// } catch (bitbucket\Exception\RuntimeException $e) {
+// drush_log('bitbucket API Error: ' . $e->getMessage(), 'error');
+// }
+// }
+//}
diff --git a/modules/devshop/devshop_bitbucket/devshop_bitbucket.info b/modules/devshop/devshop_bitbucket/devshop_bitbucket.info
new file mode 100644
index 000000000..648e8f885
--- /dev/null
+++ b/modules/devshop/devshop_bitbucket/devshop_bitbucket.info
@@ -0,0 +1,7 @@
+name = DevShop BitBucket
+description = Support for BitBucket Webhooks
+core = 7.x
+package = DevShop
+files[] = includes/add-key.inc
+files[] = includes/admin.inc
+dependencies[] = devshop_projects
diff --git a/modules/devshop/devshop_bitbucket/devshop_bitbucket.install b/modules/devshop/devshop_bitbucket/devshop_bitbucket.install
new file mode 100644
index 000000000..cee08e732
--- /dev/null
+++ b/modules/devshop/devshop_bitbucket/devshop_bitbucket.install
@@ -0,0 +1,95 @@
+ array(
+ 'id' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'Pull Request ID',
+ ),
+ 'number' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'Pull Request Number',
+ ),
+ 'project_nid' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => "The project's Node ID.",
+ ),
+ 'environment_name' => array(
+ 'type' => 'varchar',
+ 'not null' => TRUE,
+ 'length' => 64,
+ 'default' => '',
+ 'description' => 'Environment name for this pull request environment.',
+ ),
+ 'pull_request_object' => array(
+ 'type' => 'text',
+ 'not null' => FALSE,
+ 'size' => 'big',
+ 'description' => 'A serialized array of settings for this environment.',
+ ),
+ ),
+ 'primary key' => array('id'),
+ );
+ return $schema;
+}
+
+/**
+ * Implements hook_install().
+ */
+function devshop_bitbucket_install() {
+
+ // Push devshop_bitbucket's system weight to 1.
+ db_update('system')
+ ->fields(array(
+ 'weight' => 2
+ ))
+ ->condition('name', 'devshop_bitbucket')
+ ->execute();
+
+ // Display a message about setting a bitbucket personal token.
+ drupal_set_message(t('DevShop bitbucket module has been enabled. You must add an access token to enable full functionality at !link.', array(
+ '!link' => l(t('the settings page'), 'admin/devshop/bitbucket'),
+ )));
+}
+
+/**
+ * Set a weight higher than devshop_project so our form doesn't get obliterated by
+ * devshop_projects_form_project_node_form_alter()
+ */
+function devshop_bitbucket_update_7000() {
+ db_update('system')
+ ->fields(array(
+ 'weight' => 1
+ ))
+ ->condition('name', 'devshop_bitbucket')
+ ->execute();
+}
+
+/**
+ * Set a weight higher than devshop_project so our form doesn't get obliterated by
+ * devshop_projects_form_project_node_form_alter()
+ */
+function devshop_bitbucket_update_7001() {
+ db_update('system')
+ ->fields(array(
+ 'weight' => 2
+ ))
+ ->condition('name', 'devshop_bitbucket')
+ ->execute();
+}
diff --git a/modules/devshop/devshop_bitbucket/devshop_bitbucket.module b/modules/devshop/devshop_bitbucket/devshop_bitbucket.module
new file mode 100644
index 000000000..d7284e800
--- /dev/null
+++ b/modules/devshop/devshop_bitbucket/devshop_bitbucket.module
@@ -0,0 +1,744 @@
+ 'BitBucket',
+// 'description' => 'DevShop BitBucket Integration Settings',
+// 'page callback' => 'drupal_get_form',
+// 'page arguments' => array('devshop_bitbucket_settings_form'),
+// 'access arguments' => array('administer projects'),
+// 'file' => 'admin.inc',
+// 'file path' => drupal_get_path('module', 'devshop_bitbucket') . '/includes',
+// 'type' => MENU_LOCAL_TASK,
+// );
+// $items['admin/devshop/bitbucket/add-key'] = array(
+// 'title' => 'Add public key to bitbucket Account',
+// 'page callback' => 'drupal_get_form',
+// 'page arguments' => array('devshop_bitbucket_add_key_to_account'),
+// 'access arguments' => array('administer projects'),
+// 'file' => 'add-key.inc',
+// 'file path' => drupal_get_path('module', 'devshop_bitbucket') . '/includes',
+// 'type' => MENU_CALLBACK,
+// );
+ return $items;
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter() for project_node_form().
+ */
+function devshop_bitbucket_form_project_node_form_alter(&$form, &$form_state, $form_id) {
+ $node = $form['#node'];
+
+ if ($node->project->git_provider != 'bitbucket') {
+ return;
+ }
+
+ //All settings git pull in project page
+ $form['project']['settings']['bitbucket'] = array(
+ '#type' => 'fieldset',
+ '#group' => 'project_settings',
+ '#collapsible' => TRUE,
+ '#collapsed' => arg(1) != $node->nid,
+ '#title' => t('BitBucket Integration'),
+ );
+
+ // Pull Requests create environments?
+ // $form['bitbucket']['pull_request_environments'] = array(
+ $form['project']['settings']['bitbucket']['pull_request_environments'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Create Environments for Pull Requests'),
+ '#default_value' => isset($node->project->settings->bitbucket) ? $node->project->settings->bitbucket['pull_request_environments'] : FALSE,
+ '#description' => t('If using bitbucket, create a new environment when a new Pull Request is created.'),
+ );
+
+ // Delete Pull Request environments?
+ // $form['bitbucket']['pull_request_environments_delete'] = array(
+ $form['project']['settings']['bitbucket']['pull_request_environments_delete'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Delete Pull Request Environments'),
+ '#default_value' => isset($node->project->settings->bitbucket) ? $node->project->settings->bitbucket['pull_request_environments_delete'] : FALSE,
+ '#description' => t('When Pull Requests are closed, delete the environment.'),
+ );
+
+ // Pull Request Environment method.
+ // $form['bitbucket']['pull_request_environments_method'] = array(
+
+ $environments = array_keys($node->project->environments);
+ $options = array(
+ t('Install Drupal') => array(
+ 'devshop__bitbucket__install' => empty($node->project->install_profile)? t('Default install profile.'): $node->project->install_profile,
+ ),
+ t('Clone another environment') => array(),
+ );
+
+ if (empty($environments)) {
+ $options[t('Clone another environment')][] = t('No environments available.');
+ }
+ else {
+ $options[t('Clone another environment')] = array_combine($environments, $environments);
+ }
+ $form['project']['settings']['bitbucket']['pull_request_environments_method'] = array(
+ '#type' => 'select',
+ '#title' => t('Pull Request Environment Creation Method'),
+ '#default_value' => isset($node->project->settings->bitbucket) ?
+ $node->project->settings->bitbucket['pull_request_environments_method'] : 'devshop__bitbucket__install',
+ '#description' => t('Select the method for creating the pull request environments.'),
+ '#options' => $options,
+ );
+}
+
+/**
+ * Implements hook_form_alter().
+ */
+function devshop_bitbucket_form_devshop_project_create_step_git_alter(&$form, &$form_state, $form_id) {
+
+ // Look for Token
+// $token = variable_get('devshop_bitbucket_token', '');
+//
+// if (empty($token)) {
+// $form['connect']['#description'] = '
' . t('bitbucket API Token was not found.') . ' ' . l(t('Configure DevShop bitbucket Settings'), 'admin/devshop/bitbucket') . '
';
+ }
+ }
+
+ return system_settings_form($form);
+}
diff --git a/modules/devshop/devshop_cloud/drush/LICENSE b/modules/devshop/devshop_cloud/drush/LICENSE
deleted file mode 100644
index d6a93266f..000000000
--- a/modules/devshop/devshop_cloud/drush/LICENSE
+++ /dev/null
@@ -1,340 +0,0 @@
-GNU GENERAL PUBLIC LICENSE
- Version 2, June 1991
-
- Copyright (C) 1989, 1991 Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
- Everyone is permitted to copy and distribute verbatim copies
- of this license document, but changing it is not allowed.
-
- Preamble
-
- The licenses for most software are designed to take away your
-freedom to share and change it. By contrast, the GNU General Public
-License is intended to guarantee your freedom to share and change free
-software--to make sure the software is free for all its users. This
-General Public License applies to most of the Free Software
-Foundation's software and to any other program whose authors commit to
-using it. (Some other Free Software Foundation software is covered by
-the GNU Lesser General Public License instead.) You can apply it to
-your programs, too.
-
- When we speak of free software, we are referring to freedom, not
-price. Our General Public Licenses are designed to make sure that you
-have the freedom to distribute copies of free software (and charge for
-this service if you wish), that you receive source code or can get it
-if you want it, that you can change the software or use pieces of it
-in new free programs; and that you know you can do these things.
-
- To protect your rights, we need to make restrictions that forbid
-anyone to deny you these rights or to ask you to surrender the rights.
-These restrictions translate to certain responsibilities for you if you
-distribute copies of the software, or if you modify it.
-
- For example, if you distribute copies of such a program, whether
-gratis or for a fee, you must give the recipients all the rights that
-you have. You must make sure that they, too, receive or can get the
-source code. And you must show them these terms so they know their
-rights.
-
- We protect your rights with two steps: (1) copyright the software, and
-(2) offer you this license which gives you legal permission to copy,
-distribute and/or modify the software.
-
- Also, for each author's protection and ours, we want to make certain
-that everyone understands that there is no warranty for this free
-software. If the software is modified by someone else and passed on, we
-want its recipients to know that what they have is not the original, so
-that any problems introduced by others will not reflect on the original
-authors' reputations.
-
- Finally, any free program is threatened constantly by software
-patents. We wish to avoid the danger that redistributors of a free
-program will individually obtain patent licenses, in effect making the
-program proprietary. To prevent this, we have made it clear that any
-patent must be licensed for everyone's free use or not licensed at all.
-
- The precise terms and conditions for copying, distribution and
-modification follow.
-
- GNU GENERAL PUBLIC LICENSE
- TERMS AND CONDITIONS FOR COPYING, DISTRIBUTION AND MODIFICATION
-
- 0. This License applies to any program or other work which contains
-a notice placed by the copyright holder saying it may be distributed
-under the terms of this General Public License. The "Program", below,
-refers to any such program or work, and a "work based on the Program"
-means either the Program or any derivative work under copyright law:
-that is to say, a work containing the Program or a portion of it,
-either verbatim or with modifications and/or translated into another
-language. (Hereinafter, translation is included without limitation in
-the term "modification".) Each licensee is addressed as "you".
-
-Activities other than copying, distribution and modification are not
-covered by this License; they are outside its scope. The act of
-running the Program is not restricted, and the output from the Program
-is covered only if its contents constitute a work based on the
-Program (independent of having been made by running the Program).
-Whether that is true depends on what the Program does.
-
- 1. You may copy and distribute verbatim copies of the Program's
-source code as you receive it, in any medium, provided that you
-conspicuously and appropriately publish on each copy an appropriate
-copyright notice and disclaimer of warranty; keep intact all the
-notices that refer to this License and to the absence of any warranty;
-and give any other recipients of the Program a copy of this License
-along with the Program.
-
-You may charge a fee for the physical act of transferring a copy, and
-you may at your option offer warranty protection in exchange for a fee.
-
- 2. You may modify your copy or copies of the Program or any portion
-of it, thus forming a work based on the Program, and copy and
-distribute such modifications or work under the terms of Section 1
-above, provided that you also meet all of these conditions:
-
- a) You must cause the modified files to carry prominent notices
- stating that you changed the files and the date of any change.
-
- b) You must cause any work that you distribute or publish, that in
- whole or in part contains or is derived from the Program or any
- part thereof, to be licensed as a whole at no charge to all third
- parties under the terms of this License.
-
- c) If the modified program normally reads commands interactively
- when run, you must cause it, when started running for such
- interactive use in the most ordinary way, to print or display an
- announcement including an appropriate copyright notice and a
- notice that there is no warranty (or else, saying that you provide
- a warranty) and that users may redistribute the program under
- these conditions, and telling the user how to view a copy of this
- License. (Exception: if the Program itself is interactive but
- does not normally print such an announcement, your work based on
- the Program is not required to print an announcement.)
-
-These requirements apply to the modified work as a whole. If
-identifiable sections of that work are not derived from the Program,
-and can be reasonably considered independent and separate works in
-themselves, then this License, and its terms, do not apply to those
-sections when you distribute them as separate works. But when you
-distribute the same sections as part of a whole which is a work based
-on the Program, the distribution of the whole must be on the terms of
-this License, whose permissions for other licensees extend to the
-entire whole, and thus to each and every part regardless of who wrote it.
-
-Thus, it is not the intent of this section to claim rights or contest
-your rights to work written entirely by you; rather, the intent is to
-exercise the right to control the distribution of derivative or
-collective works based on the Program.
-
-In addition, mere aggregation of another work not based on the Program
-with the Program (or with a work based on the Program) on a volume of
-a storage or distribution medium does not bring the other work under
-the scope of this License.
-
- 3. You may copy and distribute the Program (or a work based on it,
-under Section 2) in object code or executable form under the terms of
-Sections 1 and 2 above provided that you also do one of the following:
-
- a) Accompany it with the complete corresponding machine-readable
- source code, which must be distributed under the terms of Sections
- 1 and 2 above on a medium customarily used for software interchange; or,
-
- b) Accompany it with a written offer, valid for at least three
- years, to give any third party, for a charge no more than your
- cost of physically performing source distribution, a complete
- machine-readable copy of the corresponding source code, to be
- distributed under the terms of Sections 1 and 2 above on a medium
- customarily used for software interchange; or,
-
- c) Accompany it with the information you received as to the offer
- to distribute corresponding source code. (This alternative is
- allowed only for noncommercial distribution and only if you
- received the program in object code or executable form with such
- an offer, in accord with Subsection b above.)
-
-The source code for a work means the preferred form of the work for
-making modifications to it. For an executable work, complete source
-code means all the source code for all modules it contains, plus any
-associated interface definition files, plus the scripts used to
-control compilation and installation of the executable. However, as a
-special exception, the source code distributed need not include
-anything that is normally distributed (in either source or binary
-form) with the major components (compiler, kernel, and so on) of the
-operating system on which the executable runs, unless that component
-itself accompanies the executable.
-
-If distribution of executable or object code is made by offering
-access to copy from a designated place, then offering equivalent
-access to copy the source code from the same place counts as
-distribution of the source code, even though third parties are not
-compelled to copy the source along with the object code.
-
- 4. You may not copy, modify, sublicense, or distribute the Program
-except as expressly provided under this License. Any attempt
-otherwise to copy, modify, sublicense or distribute the Program is
-void, and will automatically terminate your rights under this License.
-However, parties who have received copies, or rights, from you under
-this License will not have their licenses terminated so long as such
-parties remain in full compliance.
-
- 5. You are not required to accept this License, since you have not
-signed it. However, nothing else grants you permission to modify or
-distribute the Program or its derivative works. These actions are
-prohibited by law if you do not accept this License. Therefore, by
-modifying or distributing the Program (or any work based on the
-Program), you indicate your acceptance of this License to do so, and
-all its terms and conditions for copying, distributing or modifying
-the Program or works based on it.
-
- 6. Each time you redistribute the Program (or any work based on the
-Program), the recipient automatically receives a license from the
-original licensor to copy, distribute or modify the Program subject to
-these terms and conditions. You may not impose any further
-restrictions on the recipients' exercise of the rights granted herein.
-You are not responsible for enforcing compliance by third parties to
-this License.
-
- 7. If, as a consequence of a court judgment or allegation of patent
-infringement or for any other reason (not limited to patent issues),
-conditions are imposed on you (whether by court order, agreement or
-otherwise) that contradict the conditions of this License, they do not
-excuse you from the conditions of this License. If you cannot
-distribute so as to satisfy simultaneously your obligations under this
-License and any other pertinent obligations, then as a consequence you
-may not distribute the Program at all. For example, if a patent
-license would not permit royalty-free redistribution of the Program by
-all those who receive copies directly or indirectly through you, then
-the only way you could satisfy both it and this License would be to
-refrain entirely from distribution of the Program.
-
-If any portion of this section is held invalid or unenforceable under
-any particular circumstance, the balance of the section is intended to
-apply and the section as a whole is intended to apply in other
-circumstances.
-
-It is not the purpose of this section to induce you to infringe any
-patents or other property right claims or to contest validity of any
-such claims; this section has the sole purpose of protecting the
-integrity of the free software distribution system, which is
-implemented by public license practices. Many people have made
-generous contributions to the wide range of software distributed
-through that system in reliance on consistent application of that
-system; it is up to the author/donor to decide if he or she is willing
-to distribute software through any other system and a licensee cannot
-impose that choice.
-
-This section is intended to make thoroughly clear what is believed to
-be a consequence of the rest of this License.
-
- 8. If the distribution and/or use of the Program is restricted in
-certain countries either by patents or by copyrighted interfaces, the
-original copyright holder who places the Program under this License
-may add an explicit geographical distribution limitation excluding
-those countries, so that distribution is permitted only in or among
-countries not thus excluded. In such case, this License incorporates
-the limitation as if written in the body of this License.
-
- 9. The Free Software Foundation may publish revised and/or new versions
-of the General Public License from time to time. Such new versions will
-be similar in spirit to the present version, but may differ in detail to
-address new problems or concerns.
-
-Each version is given a distinguishing version number. If the Program
-specifies a version number of this License which applies to it and "any
-later version", you have the option of following the terms and conditions
-either of that version or of any later version published by the Free
-Software Foundation. If the Program does not specify a version number of
-this License, you may choose any version ever published by the Free Software
-Foundation.
-
- 10. If you wish to incorporate parts of the Program into other free
-programs whose distribution conditions are different, write to the author
-to ask for permission. For software which is copyrighted by the Free
-Software Foundation, write to the Free Software Foundation; we sometimes
-make exceptions for this. Our decision will be guided by the two goals
-of preserving the free status of all derivatives of our free software and
-of promoting the sharing and reuse of software generally.
-
- NO WARRANTY
-
- 11. BECAUSE THE PROGRAM IS LICENSED FREE OF CHARGE, THERE IS NO WARRANTY
-FOR THE PROGRAM, TO THE EXTENT PERMITTED BY APPLICABLE LAW. EXCEPT WHEN
-OTHERWISE STATED IN WRITING THE COPYRIGHT HOLDERS AND/OR OTHER PARTIES
-PROVIDE THE PROGRAM "AS IS" WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED
-OR IMPLIED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
-MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. THE ENTIRE RISK AS
-TO THE QUALITY AND PERFORMANCE OF THE PROGRAM IS WITH YOU. SHOULD THE
-PROGRAM PROVE DEFECTIVE, YOU ASSUME THE COST OF ALL NECESSARY SERVICING,
-REPAIR OR CORRECTION.
-
- 12. IN NO EVENT UNLESS REQUIRED BY APPLICABLE LAW OR AGREED TO IN WRITING
-WILL ANY COPYRIGHT HOLDER, OR ANY OTHER PARTY WHO MAY MODIFY AND/OR
-REDISTRIBUTE THE PROGRAM AS PERMITTED ABOVE, BE LIABLE TO YOU FOR DAMAGES,
-INCLUDING ANY GENERAL, SPECIAL, INCIDENTAL OR CONSEQUENTIAL DAMAGES ARISING
-OUT OF THE USE OR INABILITY TO USE THE PROGRAM (INCLUDING BUT NOT LIMITED
-TO LOSS OF DATA OR DATA BEING RENDERED INACCURATE OR LOSSES SUSTAINED BY
-YOU OR THIRD PARTIES OR A FAILURE OF THE PROGRAM TO OPERATE WITH ANY OTHER
-PROGRAMS), EVEN IF SUCH HOLDER OR OTHER PARTY HAS BEEN ADVISED OF THE
-POSSIBILITY OF SUCH DAMAGES.
-
- END OF TERMS AND CONDITIONS
-
- How to Apply These Terms to Your New Programs
-
- If you develop a new program, and you want it to be of the greatest
-possible use to the public, the best way to achieve this is to make it
-free software which everyone can redistribute and change under these terms.
-
- To do so, attach the following notices to the program. It is safest
-to attach them to the start of each source file to most effectively
-convey the exclusion of warranty; and each file should have at least
-the "copyright" line and a pointer to where the full notice is found.
-
- {description}
- Copyright (C) {year} {fullname}
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License along
- with this program; if not, write to the Free Software Foundation, Inc.,
- 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
-
-Also add information on how to contact you by electronic and paper mail.
-
-If the program is interactive, make it output a short notice like this
-when it starts in an interactive mode:
-
- Gnomovision version 69, Copyright (C) year name of author
- Gnomovision comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
- This is free software, and you are welcome to redistribute it
- under certain conditions; type `show c' for details.
-
-The hypothetical commands `show w' and `show c' should show the appropriate
-parts of the General Public License. Of course, the commands you use may
-be called something other than `show w' and `show c'; they could even be
-mouse-clicks or menu items--whatever suits your program.
-
-You should also get your employer (if you work as a programmer) or your
-school, if any, to sign a "copyright disclaimer" for the program, if
-necessary. Here is a sample; alter the names:
-
- Yoyodyne, Inc., hereby disclaims all copyright interest in the program
- `Gnomovision' (which makes passes at compilers) written by James Hacker.
-
- {signature of Ty Coon}, 1 April 1989
- Ty Coon, President of Vice
-
-This General Public License does not permit incorporating your program into
-proprietary programs. If your program is a subroutine library, you may
-consider it more useful to permit linking proprietary applications with the
-library. If this is what you want to do, use the GNU Lesser General
-Public License instead of this License.
-
diff --git a/modules/devshop/devshop_cloud/drush/Provision/Service/provider.php b/modules/devshop/devshop_cloud/drush/Provision/Service/provider.php
deleted file mode 100644
index d22ffecb4..000000000
--- a/modules/devshop/devshop_cloud/drush/Provision/Service/provider.php
+++ /dev/null
@@ -1,50 +0,0 @@
-setProperty('provider');
- $context->setProperty('provider_options');
- $context->setProperty('provider_data');
- $context->setProperty('provider_server_identifier');
- }
-
- /**
- * Stub for init_server();
- *
- * Call from child classes:
- * parent::init_server();
- *
- * This function is called many times during a server verify.
- * Use sparingly.
- */
- function init_server() {
- }
-
- /**
- * Saves server options to drush options so they will be picked up by
- * devshop_cloud_post_hosting_verify_task()
- */
- function verify_server_cmd() {
- drush_set_option('provider_data', $this->server->provider_data);
- drush_set_option('provider_server_identifier', $this->server->provider_server_identifier);
-
- if (!empty($this->server->ip_addresses)) {
- drush_set_option('ip_addresses', $this->server->ip_addresses);
- }
- }
-
- static function option_documentation() {
- return array(
- '--provider' => 'The provider of this server. Must match an available Provision_Service_provider',
- '--provider_options' => 'An array of options to send to the provider.',
- );
- }
-}
diff --git a/modules/devshop/devshop_cloud/drush/Provision/Service/provider/SoftLayer/softlayer-api-php-client/.gitignore b/modules/devshop/devshop_cloud/drush/Provision/Service/provider/SoftLayer/softlayer-api-php-client/.gitignore
deleted file mode 100644
index be7384552..000000000
--- a/modules/devshop/devshop_cloud/drush/Provision/Service/provider/SoftLayer/softlayer-api-php-client/.gitignore
+++ /dev/null
@@ -1,5 +0,0 @@
-.DS_Store
-*.swp
-Thumbs.db
-.svn
-._*
diff --git a/modules/devshop/devshop_cloud/drush/Provision/Service/provider/SoftLayer/softlayer-api-php-client/LICENSE.textile b/modules/devshop/devshop_cloud/drush/Provision/Service/provider/SoftLayer/softlayer-api-php-client/LICENSE.textile
deleted file mode 100644
index cd0f96d85..000000000
--- a/modules/devshop/devshop_cloud/drush/Provision/Service/provider/SoftLayer/softlayer-api-php-client/LICENSE.textile
+++ /dev/null
@@ -1,9 +0,0 @@
-Copyright (c) 2009 - 2010, "SoftLayer Technologies, Inc.":http://www.softlayer.com/ All rights reserved.
-
-Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
-
- * Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
- * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
- * Neither SoftLayer Technologies, Inc. nor the names of its contributors may be used to endorse or promote products derived from this software without specific prior written permission.
-
-THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
diff --git a/modules/devshop/devshop_cloud/drush/Provision/Service/provider/SoftLayer/softlayer-api-php-client/README.textile b/modules/devshop/devshop_cloud/drush/Provision/Service/provider/SoftLayer/softlayer-api-php-client/README.textile
deleted file mode 100644
index b3f1851af..000000000
--- a/modules/devshop/devshop_cloud/drush/Provision/Service/provider/SoftLayer/softlayer-api-php-client/README.textile
+++ /dev/null
@@ -1,98 +0,0 @@
-h1. A SoftLayer API PHP client.
-
-h2. Overview
-
-The SoftLayer API PHP client classes provide a simple method for connecting to and making calls from the SoftLayer API and provides support for many of the SoftLayer API's features. Method calls and client management are handled by the PHP SOAP and XML-RPC extensions.
-
-Making API calls using the SoftLayer_SoapClient or SoftLayer_XmlrpcClient classes is done in the following steps:
-
-# Instantiate a new SoftLayer_SoapClient or SoftLayer_XmlrpcClient object using the SoftLayer_SoapClient::getClient() or SoftLayer_XmlrpcClient::getClient() methods. Provide the name of the service that you wish to query, an optional id number of the object that you wish to instantiate, your SoftLayer API username, your SoftLayer API key, and an optional API endpoint base URL. The client classes default to connect over the public Internet. Enter SoftLayer_SoapClient::API_PRIVATE_ENDPOINT or SoftLayer_XmlrpcClient::API_PRIVATE_ENDPOINT to connect to the API over SoftLayer's private network. The system making API calls must be connected to SoftLayer's private network (eg. purchased from SoftLayer or connected via VPN) in order to use the private network API endpoints.
-# Define and add optional headers to the client, such as object masks and result limits.
-# Call the API method you wish to call as if it were local to your client object. This class throws exceptions if it's unable to execute a query, so it's best to place API method calls in try / catch statements for proper error handling.
-
-Once your method is executed you may continue using the same client if you need to connect to the same service or define another client object if you wish to work with multiple services at once.
-
-The most up to date version of this library can be found on the SoftLayer github public repositories: "http://github.com/softlayer/":http://github.com/softlayer/ . Please post to the SoftLayer forums <"http://forums.softlayer.com/":http://forums.softlayer.com/> or open a support ticket in the SoftLayer customer portal if you have any questions regarding use of this library.
-
-h2. System Requirements
-
-The SoftLayer_SoapClient class requires at least PHP 5.2.3 and the PHP SOAP enxtension installed. The SoftLayer_Xmlrpc class requires PHP at least PHP 5 and the PHP XML-RPC extension installed.
-
-A valid API username and key are required to call the SoftLayer API. A connection to the SoftLayer private network is required to connect to SoftLayer's private network API endpopints.
-
-h2. Installation
-
-Download and copy the SoftLayer API client to a directory local to your PHP project or a path within your PHP installation's include_path.
-
-h2. Usage
-
-These examples use the SoftLayer_SoapClient class. If you wish to use the XML-RPC API then replace mentions of SoapClient.class.php with XmlrpcClient.class.php and SoftLayer_SoapClient with SoftLayer_XmlrpcClient.
-
-Here's a simple usage example that retrieves account information by calling the "getObject()":http://sldn.softlayer.com/reference/services/SoftLayer_Account/getObject method in the "SoftLayer_Account":http://sldn.softlayer.com/reference/services/SoftLayer_Account service:
-
-
-
-For a more complex example we'll retrieve a support ticket with id 123456 along with the ticket's updates, the user it's assigned to, the servers attached to it, and the datacenter those servers are in. We'll retrieve our extra information using a nested object mask. After we have the ticket we'll update it with the text 'Hello!'.
-
-
-updates;
-$objectMask->assignedUser;
-$objectMask->attachedHardware->datacenter;
-$client->setObjectMask($objectMask);
-
-// Retrieve the ticket record
-try {
- $ticket = $client->getObject();
-} catch (Exception $e) {
- die('Unable to retrieve ticket record: ' . $e->getMessage());
-}
-
-// Update the ticket
-$update = new stdClass();
-$update->entry = 'Hello!';
-
-try {
- $update = $client->addUpdate($update);
- echo "Updated ticket 123456. The new update's id is " . $update[0]->id . '.');
-} catch (Exception $e) {
- die('Unable to update ticket: ' . $e->getMessage());
-}
-
-
-h2. Author
-
-This software is written by the SoftLayer Development Team <"sldn@softlayer.com":mailto:sldn@softlayer.com>.
-
-h2. Copyright
-
-This software is Copyright (c) 2009 - 2010 "SoftLayer Technologies, Inc":http://www.softlayer.com/. See the bundled LICENSE.textile file for more information.
diff --git a/modules/devshop/devshop_cloud/drush/Provision/Service/provider/SoftLayer/softlayer-api-php-client/SoftLayer/Common/ObjectMask.class.php b/modules/devshop/devshop_cloud/drush/Provision/Service/provider/SoftLayer/softlayer-api-php-client/SoftLayer/Common/ObjectMask.class.php
deleted file mode 100644
index b470c6df2..000000000
--- a/modules/devshop/devshop_cloud/drush/Provision/Service/provider/SoftLayer/softlayer-api-php-client/SoftLayer/Common/ObjectMask.class.php
+++ /dev/null
@@ -1,84 +0,0 @@
-datacenter = new StdClass();
- * $objectMask->serverRoom = new StdClass();
- * $objectMask->provisionDate = new StdClass();
- * $objectMask->softwareComponents = new StdClass();
- * $objectMask->softwareComponents->passwords = new StdClass();
- *
- * Building an object mask using SoftLayer_ObjectMask is a bit easier to
- * type:
- *
- * $objectMask = new SoftLayer_ObjectMask();
- * $objectMask->datacenter;
- * $objectMask->serverRoom;
- * $objectMask->provisionDate;
- * $objectMask->sofwareComponents->passwords;
- *
- * Use SoftLayer_SoapClient::setObjectMask() to set these object masks before
- * making your SoftLayer API calls.
- *
- * For more on object mask usage in the SoftLayer API please see
- * http://sldn.softlayer.com/article/Using_Object_Masks_in_the_SoftLayer_API .
- *
- * The most up to date version of this library can be found on the SoftLayer
- * github public repositories: http://github.com/softlayer/ . Please post to
- * the SoftLayer forums or open a support ticket
- * in the SoftLayer customer portal if you have any questions regarding use of
- * this library.
- *
- * @author SoftLayer Technologies, Inc.
- * @copyright Copyright (c) 2009 - 2010, Softlayer Technologies, Inc
- * @license http://sldn.softlayer.com/article/License
- * @see SoftLayer_SoapClient::setObjectMask()
- * @see SoftLayer_XmlrpcClient::setObjectMask()
- */
-class SoftLayer_ObjectMask
-{
- /**
- * Define an object mask value
- *
- * @param string $var
- */
- public function __get($var)
- {
- $this->{$var} = new SoftLayer_ObjectMask();
-
- return $this->{$var};
- }
-}
diff --git a/modules/devshop/devshop_cloud/drush/Provision/Service/provider/SoftLayer/softlayer-api-php-client/SoftLayer/SoapClient.class.php b/modules/devshop/devshop_cloud/drush/Provision/Service/provider/SoftLayer/softlayer-api-php-client/SoftLayer/SoapClient.class.php
deleted file mode 100644
index 68b3c9700..000000000
--- a/modules/devshop/devshop_cloud/drush/Provision/Service/provider/SoftLayer/softlayer-api-php-client/SoftLayer/SoapClient.class.php
+++ /dev/null
@@ -1,503 +0,0 @@
- or open a support ticket
- * in the SoftLayer customer portal if you have any questions regarding use of
- * this library.
- *
- * @author SoftLayer Technologies, Inc.
- * @copyright Copyright (c) 2009 - 2010, Softlayer Technologies, Inc
- * @license http://sldn.softlayer.com/article/License
- * @link http://sldn.softlayer.com/article/The_SoftLayer_API The SoftLayer API
- * @see SoftLayer_SoapClient_AsynchronousAction
- */
-class Softlayer_SoapClient extends SoapClient
-{
- /**
- * Your SoftLayer API username. You may overide this value when calling
- * getClient().
- *
- * @var string
- */
- const API_USER = 'set me';
-
- /**
- * Your SoftLayer API user's authentication key. You may overide this value
- * when calling getClient().
- *
- * @link https://manage.softlayer.com/Administrative/apiKeychain API key management in the SoftLayer customer portal
- * @var string
- */
- const API_KEY = 'set me';
-
- /**
- * The base URL of the SoftLayer SOAP API's WSDL files over the public
- * Internet.
- *
- * @var string
- */
- const API_PUBLIC_ENDPOINT = 'https://api.softlayer.com/soap/v3/';
-
- /**
- * The base URL of the SoftLayer SOAP API's WSDL files over SoftLayer's
- * private network.
- *
- * @var string
- */
- const API_PRIVATE_ENDPOINT = 'http://api.service.softlayer.com/soap/v3/';
-
- /**
- * The namespace to use for calls to the API
- *
- * $var string
- */
- const DEFAULT_NAMESPACE = 'http://api.service.softlayer.com/soap/v3/';
-
-
- /**
- * The API endpoint base URL used by the client.
- *
- * @var string
- */
- const API_BASE_URL = SoftLayer_SoapClient::API_PUBLIC_ENDPOINT;
-
- /**
- * An optional SOAP timeout if you want to set a timeout independent of
- * PHP's socket timeout.
- *
- * @var int
- */
- const SOAP_TIMEOUT = null;
-
- /**
- * The SOAP headers to send along with a SoftLayer API call
- *
- * @var array
- */
- protected $_headers = array();
-
- /**
- * The name of the SoftLayer API service you wish to query.
- *
- * @link http://sldn.softlayer.com/reference/services A list of SoftLayer API services
- * @var string
- */
- protected $_serviceName;
-
- /**
- * The base URL to the SoftLayer API's WSDL files being used by this
- * client.
- *
- * @var string
- */
- protected $_endpointUrl;
-
- /**
- * Whether or not the current call is an asynchronous call.
- *
- * @var bool
- */
- protected $_asynchronous = false;
-
- /**
- * The object that handles asynchronous calls if the current call is an
- * asynchronous call.
- *
- * @var SoftLayer_SoapClient_AsynchronousAction
- */
- private $_asyncAction = null;
-
- /**
- * If making an asynchronous call, then this is the name of the function
- * we're calling.
- *
- * @var string
- */
- public $asyncFunctionName = null;
-
- /**
- * If making an asynchronous call, then this is the result of an
- * asynchronous call as retuned from the
- * SoftLayer_SoapClient_AsynchronousAction class.
- *
- * @var object
- */
- private $_asyncResult = null;
-
- /**
- * Used when making asynchronous calls.
- *
- * @var bool
- */
- public $oneWay;
-
- /**
- * Execute a SoftLayer API method
- *
- * @return object
- */
- public function __call($functionName, $arguments = null)
- {
- // Determine if we shoud be making an asynchronous call. If so strip
- // "Async" from the end of the method name.
- if ($this->_asyncResult == null) {
- $this->_asynchronous = false;
- $this->_asyncAction = null;
-
- if (preg_match('/Async$/', $functionName) == 1) {
- $this->_asynchronous = true;
- $functionName = str_replace('Async', '', $functionName);
-
- $this->asyncFunctionName = $functionName;
- }
- }
-
- try {
- $result = parent::__call($functionName, $arguments, null, $this->_headers, null);
- } catch (SoapFault $e) {
- throw new Exception('There was an error querying the SoftLayer API: ' . $e->getMessage());
- }
-
- if ($this->_asynchronous == true) {
- return $this->_asyncAction;
- }
-
- // remove the resultLimit header if they set it
- $this->removeHeader('resultLimit');
-
- return $result;
- }
-
- /**
- * Create a SoftLayer API SOAP Client
- *
- * Retrieve a new SoftLayer_SoapClient object for a specific SoftLayer API
- * service using either the class' constants API_USER and API_KEY or a
- * custom username and API key for authentication. Provide an optional id
- * value if you wish to instantiate a particular SoftLayer API object.
- *
- * @param string $serviceName The name of the SoftLayer API service you wish to query
- * @param int $id An optional object id if you're instantiating a particular SoftLayer API object. Setting an id defines this client's initialization parameter header.
- * @param string $username An optional API username if you wish to bypass SoftLayer_SoapClient's built-in username.
- * @param string $username An optional API key if you wish to bypass SoftLayer_SoapClient's built-in API key.
- * @param string $endpointUrl The API endpoint base URL you wish to connect to. Set this to SoftLayer_SoapClient::API_PRIVATE_ENDPOINT to connect via SoftLayer's private network.
- * @return SoftLayer_SoapClient
- */
- public static function getClient($serviceName, $id = null, $username = null, $apiKey = null, $endpointUrl = null)
- {
- $serviceName = trim($serviceName);
-
- if ($serviceName == null) {
- throw new Exception('Please provide a SoftLayer API service name.');
- }
-
- /*
- * Default to use the public network API endpoint, otherwise use the
- * endpoint defined in API_PUBLIC_ENDPOINT, otherwise use the one
- * provided by the user.
- */
- if (isset($endpointUrl)) {
- $endpointUrl = trim($endpointUrl);
-
- if ($endpointUrl == null) {
- throw new Exception('Please provide a valid API endpoint.');
- }
- } elseif (self::API_BASE_URL != null) {
- $endpointUrl = self::API_BASE_URL;
- } else {
- $endpointUrl = SoftLayer_SoapClient::API_PUBLIC_ENDPOINT;
- }
-
- if (is_null(self::SOAP_TIMEOUT)) {
- $soapClient = new SoftLayer_SoapClient($endpointUrl . $serviceName . '?wsdl');
- } else {
- $soapClient = new SoftLayer_SoapClient($endpointUrl . $serviceName . '?wsdl', array('connection_timeout' => self::SOAP_TIMEOUT));
- }
-
- $soapClient->_serviceName = $serviceName;
- $soapClient->_endpointUrl = $endpointUrl;
-
- if ($username != null && $apiKey != null) {
- $soapClient->setAuthentication($username, $apiKey);
- } else {
- $soapClient->setAuthentication(self::API_USER, self::API_KEY);
- }
-
- if ($id !== null) {
- $soapClient->setInitParameter($id);
- }
-
- return $soapClient;
- }
-
- /**
- * Set a SoftLayer API call header
- *
- * Every header defines a customization specific to an SoftLayer API call.
- * Most API calls require authentication and initialization parameter
- * headers, but can also include optional headers such as object masks and
- * result limits if they're supported by the API method you're calling.
- *
- * @see removeHeader()
- * @param string $name The name of the header you wish to set
- * @param object $value The object you wish to set in this header
- * @return SoftLayer_SoapClient
- */
- public function addHeader($name, $value)
- {
- $this->_headers[$name] = new SoapHeader(self::DEFAULT_NAMESPACE, $name, $value);
- return $this;
- }
-
- /**
- * Remove a SoftLayer API call header
- *
- * Removing headers may cause API queries to fail.
- *
- * @see addHeader()
- * @param string $name The name of the header you wish to remove
- * @return SoftLayer_SoapClient
- */
- public function removeHeader($name)
- {
- unset($this->_headers[$name]);
- return $this;
- }
-
- /**
- * Set a user and key to authenticate a SoftLayer API call
- *
- * Use this method if you wish to bypass the API_USER and API_KEY class
- * constants and set custom authentication per API call.
- *
- * @link https://manage.softlayer.com/Administrative/apiKeychain API key management in the SoftLayer customer portal
- * @param string $username
- * @param string $apiKey
- * @return SoftLayer_SoapClient
- */
- public function setAuthentication($username, $apiKey)
- {
- $username = trim($username);
- $apiKey = trim($apiKey);
-
- if ($username == null) {
- throw new Exception('Please provide a SoftLayer API username.');
- }
-
- if ($apiKey == null) {
- throw new Exception('Please provide a SoftLayer API key.');
- }
-
- $header = new stdClass();
- $header->username = $username;
- $header->apiKey = $apiKey;
-
- $this->addHeader('authenticate', $header);
- return $this;
- }
-
-
- /**
- * Set an initialization parameter header on a SoftLayer API call
- *
- * Initialization parameters instantiate a SoftLayer API service object to
- * act upon during your API method call. For instance, if your account has a
- * server with id number 1234, then setting an initialization parameter of
- * 1234 in the SoftLayer_Hardware_Server Service instructs the API to act on
- * server record 1234 in your method calls.
- *
- * @link http://sldn.softlayer.com/article/Using_Initialization_Parameters_in_the_SoftLayer_API Using Initialization Parameters in the SoftLayer API
- * @param int $id The ID number of the SoftLayer API object you wish to instantiate.
- * @return SoftLayer_SoapClient
- */
- public function setInitParameter($id)
- {
- $id = trim($id);
-
- if (!is_null($id)) {
- $initParameters = new stdClass();
- $initParameters->id = $id;
- $this->addHeader($this->_serviceName . 'InitParameters', $initParameters);
- }
-
- return $this;
- }
-
- /**
- * Set an object mask to a SoftLayer API call
- *
- * Use an object mask to retrieve data related your API call's result.
- * Object masks are skeleton objects or strings that define nested relational
- * properties to retrieve along with an object's local properties.
- *
- * @see SoftLayer_ObjectMask
- * @link http://sldn.softlayer.com/article/Using-Object-Masks-SoftLayer-API Using object masks in the SoftLayer API
- * @param object $mask The object mask you wish to define
- * @return SoftLayer_SoapClient
- */
- public function setObjectMask($mask)
- {
- if (!is_null($mask)) {
- $header = 'SoftLayer_ObjectMask';
-
- if ($mask instanceof SoftLayer_ObjectMask) {
- $header = sprintf('%sObjectMask', $this->_serviceName);
- }
-
- $objectMask = new stdClass();
- $objectMask->mask = $mask;
- $this->addHeader($header, $objectMask);
- }
-
- return $this;
- }
-
- /**
- * Set an object filter to a SoftLayer API call
- *
- * Use an object filter to limit what data you get back
- * from the API. Very similar to objectMasks
- *
- * @see SoftLayer_ObjectMask
- * @param object $filter The object filter you wish to define
- * @return SoftLayer_SoapClient
- */
- public function setObjectFilter($objectFilter)
- {
- if (!is_null($objectFilter)) {
- $header = sprintf('%sObjectFilter', $this->_serviceName);
- $this->addHeader($header, $objectFilter);
- }
- return $this;
- }
- /**
- * Set a result limit on a SoftLayer API call
- *
- * Many SoftLayer API methods return a group of results. These methods
- * support a way to limit the number of results retrieved from the SoftLayer
- * API in a way akin to an SQL LIMIT statement.
- *
- * @link http://sldn.softlayer.com/article/Using_Result_Limits_in_the_SoftLayer_API Using Result Limits in the SoftLayer API
- * @param int $limit The number of results to limit your SoftLayer API call to.
- * @param int $offset An optional offset to begin your SoftLayer API call's returned result set at.
- * @return SoftLayer_SoapClient
- */
- public function setResultLimit($limit, $offset = 0)
- {
- $resultLimit = new stdClass();
- $resultLimit->limit = intval($limit);
- $resultLimit->offset = intval($offset);
-
- $this->addHeader('resultLimit', $resultLimit);
- return $this;
- }
-
- /**
- * Process a SOAP request
- *
- * We've overwritten the PHP SoapClient's __doRequest() to allow processing
- * asynchronous SOAP calls. If an asynchronous call was deected in the
- * __call() method then send processing to the
- * SoftLayer_SoapClient_AsynchronousAction class. Otherwise use the
- * SoapClient's built-in __doRequest() method. The results of this method
- * are sent back to __call() for post-processing. Asynchronous calls use
- * handleAsyncResult() to send he results of the call back to __call().
- *
- * @return object
- */
- public function __doRequest($request, $location, $action, $version, $one_way = false)
- {
- // Don't make a call if we already have an asynchronous result.
- if ($this->_asyncResult != null) {
- $result = $this->_asyncResult;
- unset($this->_asyncResult);
-
- return $result;
- }
-
- if ($this->oneWay == true) {
- $one_way = true;
- $this->oneWay = false;
- }
-
- // Use either the SoapClient or SoftLayer_SoapClient_AsynchronousAction
- // class to handle the call.
- if ($this->_asynchronous == false) {
- $result = parent::__doRequest($request, $location, $action, $version, $one_way);
-
- return $result;
- } else {
- $this->_asyncAction = new SoftLayer_SoapClient_AsynchronousAction($this, $this->asyncFunctionName, $request, $location, $action);
- return '';
- }
- }
-
- /**
- * Process the results of an asynchronous call.
- *
- * The SoftLayer_SoapClient_AsynchronousAction class uses
- * handleAsyncResult() to return it's call resuls back to this classes'
- * __call() method for post-pocessing.
- *
- * @param string $functionName The name of the SOAP method called.
- * @param string $result The raw SOAP XML output from a SOAP call
- * @return object
- */
- public function handleAsyncResult($functionName, $result)
- {
- $this->_asynchronous = false;
- $this->_asyncResult = $result;
-
- return $this->__call($functionName, array());
- }
-}
diff --git a/modules/devshop/devshop_cloud/drush/Provision/Service/provider/SoftLayer/softlayer-api-php-client/SoftLayer/SoapClient/AsynchronousAction.class.php b/modules/devshop/devshop_cloud/drush/Provision/Service/provider/SoftLayer/softlayer-api-php-client/SoftLayer/SoapClient/AsynchronousAction.class.php
deleted file mode 100644
index 416ad3a24..000000000
--- a/modules/devshop/devshop_cloud/drush/Provision/Service/provider/SoftLayer/softlayer-api-php-client/SoftLayer/SoapClient/AsynchronousAction.class.php
+++ /dev/null
@@ -1,224 +0,0 @@
- receive style of transmission. Response
- * time for a call is dependent on the latency between the SOAP client and SOAP
- * server and the time required by the server to process the SOAP call. Sending
- * multiple SOAP calls in serial over the public Internet can be a time
- * consuming process.
- *
- * Asynchronous calls allow you to send multiple SOAP calls in parallel to the
- * SoftLayer API. Parallel calls reduce the latency involved handling multiple
- * calls to the time it takes for the longest SOAP call to execute, dramatically
- * reducing the time it takes to send multiple SOAP calls in most cases.
- *
- * Asynchronous calls are handled identically to standard API calls with two
- * differences:
- *
- * 1) The SoftLayer_SoapClient class knows to make an asynchronous call when the
- * method called ends with "Async". For example, to make a standard call to the
- * method getObject() you would execute $client->geObject(). It's asynchronous
- * counterpart is execued with the code $client->getObjectAsync(). Once the
- * asynchronous call is made the results of your API command are sent to this
- * classes' socket property.
- *
- * 2) The results of an asynchronous method call are stored in a
- * SoftLayer_SoapClient_AsynchronousAction object. Use the wait() method to
- * retrieve data off the internal socket and return the result back to the
- * SoftLayer_SoapClient for processing. For example if you wish to retrieve the
- * results of the method getObject() execute the following statements:
- *
- * $result = $client->getObjectAsync(); // Make the call and start geting data back.
- * $result = $result->wait(); // Return the results of the API call
- *
- * To chain multiple asynchronous requests together call multiple Async requests
- * in succession then call their associated wait() methods in succession.
- *
- * Here's a simple usage example that retrieves account information, a PDF of an
- * account's next invoice and enables VLAN spanning on that same account by
- * calling three methods in the SoftLayer_Account service in parallel:
- *
- * ----------
- *
- * // Initialize an API client for the SoftLayer_Account service.
- * $client = SoftLayer_SoapClient::getClient('SoftLayer_Account');
- *
- * try {
- * // Request our account information.
- * $account = $client->getObjectAsync();
- *
- * // Request a PDF of our next invoice. This can take much longer than
- * // getting simple account information.
- * $nextInvoicePdf = $client->getNextInvoicePdfAsync();
- *
- * // While we're at it we'll enable VLAN spanning on our account.
- * $vlanSpanResult = $client->setVlanSpanAsync(true);
- *
- * // The three requests are now processing in parallel. Use the wait()
- * // method to retrieve the resuls of our requests. The wait time involved
- * // is roughly the same time as the longest API call.
- * $account = $account->wait();
- * $nextInvoicePdf = $nextInvoicePdf->wait();
- * $vlanSpanResult = $vlanSpanResult->wait();
- *
- * // Finally, display our results.
- * var_dump($account);
- * var_dump($nextInvoicePdf);
- * var_dump($vlanSpanResult);
- * } catch (Exception $e) {
- * die('Unable to retrieve account information: ' . $e->getMessage());
- * }
- *
- * ----------
- *
- * The most up to date version of this library can be found on the SoftLayer
- * github public repositories: http://github.com/softlayer/ . Please post to
- * the SoftLayer forums or open a support ticket
- * in the SoftLayer customer portal if you have any questions regarding use of
- * this library.
- *
- * @author SoftLayer Technologies, Inc.
- * @copyright Copyright (c) 2009 - 2010, Softlayer Technologies, Inc
- * @license http://sldn.softlayer.com/article/License
- * @see SoftLayer_SoapClient
- */
-class SoftLayer_SoapClient_AsynchronousAction
-{
- /**
- * The SoftLayer SOAP client making an asynchronous call
- *
- * @var SoftLayer_SoapClient
- */
- protected $_soapClient;
-
- /**
- * The name of the function we're calling
- *
- * @var string
- */
- protected $_functionName;
-
- /**
- * A socket connection to the SoftLayer SOAP API
- *
- * @var resource
- */
- protected $_socket;
-
- /**
- * Perform an asynchgronous SoftLayer SOAP call
- *
- * Create a raw socket connection to the URL specified by the
- * SoftLayer_SoapClient class and send SOAP HTTP headers and request XML to
- * that socket. Throw exceptions if we're unable to make the socket
- * connection or send data to that socket.
- *
- * @param SoftLayer_SoapClient $soapClient The SoftLayer SOAP client making the asynchronous call.
- * @param string $functionName The name of the function we're calling.
- * @param string $request The full XML SOAP request we wish to make.
- * @param string $location The URL of the web service we wish to call.
- * @param string $action The value of the HTTP SOAPAction header in our SOAP call.
- */
- public function __construct($soapClient, $functionName, $request, $location, $action)
- {
- preg_match('%^(http(?:s)?)://(.*?)(/.*?)$%', $location, $matches);
-
- $this->_soapClient = $soapClient;
- $this->_functionName = $functionName;
-
- $protocol = $matches[1];
- $host = $matches[2];
- $endpoint = $matches[3];
-
- $headers = array(
- 'POST ' . $endpoint . ' HTTP/1.1',
- 'Host: ' . $host,
- 'User-Agent: PHP-SOAP/' . phpversion(),
- 'Content-Type: text/xml; charset=utf-8',
- 'SOAPAction: "' . $action . '"',
- 'Content-Length: ' . strlen($request),
- 'Connection: close',
- );
-
- if ($protocol == 'https') {
- $host = 'ssl://' . $host;
- $port = 443;
- } else {
- $port = 80;
- }
-
- $data = implode("\r\n", $headers) . "\r\n\r\n" . $request . "\r\n";
- $this->_socket = fsockopen($host, $port, $errorNumber, $errorMessage);
-
- if ($this->_socket === false) {
- $this->_socket = null;
- throw new Exception('Unable to make an asynchronous SoftLayer API call: ' . $errorNumber . ': ' . $errorMessage);
- }
-
- if (fwrite($this->_socket, $data) === false) {
- throw new Exception('Unable to write data to an asynchronous SoftLayer API call.');
- }
- }
-
- /**
- * Process and return the results of an asyncrhonous SoftLayer API call
- *
- * Read data from our socket and process the raw SOAP result from the
- * SoftLayer_SoapClient instance that made the asynchronous call. wait()
- * *must* be called in order to recieve the results from your API call.
- *
- * @return object
- */
- public function wait()
- {
- $soapResult = '';
-
- while (!feof($this->_socket)) {
- $soapResult .= fread($this->_socket, 8192);
- }
-
- // separate the SOAP result into headers and data.
- list($headers, $data) = explode("\r\n\r\n", $soapResult);
-
- return $this->_soapClient->handleAsyncResult($this->_functionName, $data);
- }
-
- /**
- * Close the socket created when the SOAP request was created.
- */
- public function __destruct()
- {
- if ($this->_socket != null) {
- fclose($this->_socket);
- }
- }
-}
diff --git a/modules/devshop/devshop_cloud/drush/Provision/Service/provider/SoftLayer/softlayer-api-php-client/SoftLayer/XmlrpcClient.class.php b/modules/devshop/devshop_cloud/drush/Provision/Service/provider/SoftLayer/softlayer-api-php-client/SoftLayer/XmlrpcClient.class.php
deleted file mode 100644
index 31db1313e..000000000
--- a/modules/devshop/devshop_cloud/drush/Provision/Service/provider/SoftLayer/softlayer-api-php-client/SoftLayer/XmlrpcClient.class.php
+++ /dev/null
@@ -1,437 +0,0 @@
- or open a support ticket
- * in the SoftLayer customer portal if you have any questions regarding use of
- * this library.
- *
- * @author SoftLayer Technologies, Inc.
- * @copyright Copyright (c) 2009 - 2010, Softlayer Technologies, Inc
- * @license http://sldn.softlayer.com/article/License
- * @link http://sldn.softlayer.com/article/The_SoftLayer_API The SoftLayer API
- */
-class Softlayer_XmlrpcClient
-{
- /**
- * Your SoftLayer API username. You may overide this value when calling
- * getClient().
- *
- * @var string
- */
- const API_USER = 'set me';
-
- /**
- * Your SoftLayer API user's authentication key. You may overide this value
- * when calling getClient().
- *
- * @link https://manage.softlayer.com/Administrative/apiKeychain API key management in the SoftLayer customer portal
- * @var string
- */
- const API_KEY = 'set me';
-
- /**
- * The base URL of SoftLayer XML-RPC API's public network endpoints.
- *
- * @var string
- */
- const API_PUBLIC_ENDPOINT = 'https://api.softlayer.com/xmlrpc/v3/';
-
- /**
- * The base URL of SoftLayer XML-RPC API's private network endpoints.
- *
- * @var string
- */
- const API_PRIVATE_ENDPOINT = 'http://api.service.softlayer.com/xmlrpc/v3/';
-
- /**
- * The API endpoint base URL used by the client.
- *
- * @var string
- */
- const API_BASE_URL = SoftLayer_XmlrpcClient::API_PUBLIC_ENDPOINT;
-
- /**
- * The headers to send along with a SoftLayer API call
- *
- * @var array
- */
- protected $_headers = array();
-
- /**
- * The name of the SoftLayer API service you wish to query.
- *
- * @link http://sldn.softlayer.com/reference/services A list of SoftLayer API services
- * @var string
- */
- protected $_serviceName;
-
- /**
- * The base URL of SoftLayer XML-RPC API's endpoints used by this client.
- *
- * @var string
- */
- protected $_endpointUrl;
-
- /**
- * Execute a SoftLayer API method
- *
- * @return object
- */
- public function __call($functionName, $arguments = null)
- {
- $request = array();
- $request[0] = array('headers' => $this->_headers);
- $request = array_merge($request, $arguments);
-
- try {
- $encodedRequest = xmlrpc_encode_request($functionName, $request);
-
- // Making the XML-RPC call and interpreting the response is adapted
- // from the PHP manual:
- // http://www.php.net/manual/en/function.xmlrpc-encode-request.php
- $context = stream_context_create(array(
- 'http' => array(
- 'method' => 'POST',
- 'header' => 'Content-Type: text/xml',
- 'content' => $encodedRequest
- )));
-
- $file = file_get_contents($this->_endpointUrl . $this->_serviceName, false, $context);
-
- if ($file === false) {
- throw new Exception('Unable to contact the SoftLayer API at ' . $this->_endpointUrl . $serviceName . '.');
- }
-
- $result = xmlrpc_decode($file);
- } catch (Exception $e) {
- throw new Exception('There was an error querying the SoftLayer API: ' . $e->getMessage());
- }
-
- if (is_array($result) && xmlrpc_is_fault($result)) {
- throw new Exception('There was an error querying the SoftLayer API: ' . $result['faultString']);
- }
-
- // remove the resultLimit header if they set it
- $this->removeHeader('resultLimit');
-
- return self::_convertToObject(self::_convertXmlrpcTypes($result));
- }
-
- /**
- * Create a SoftLayer API XML-RPC Client
- *
- * Retrieve a new SoftLayer_XmlrpcClient object for a specific SoftLayer API
- * service using either the class' constants API_USER and API_KEY or a
- * custom username and API key for authentication. Provide an optional id
- * value if you wish to instantiate a particular SoftLayer API object.
- *
- * @param string $serviceName The name of the SoftLayer API service you wish to query
- * @param int $id An optional object id if you're instantiating a particular SoftLayer API object. Setting an id defines this client's initialization parameter header.
- * @param string $username An optional API username if you wish to bypass SoftLayer_XmlrpcClient's built-in username.
- * @param string $username An optional API key if you wish to bypass SoftLayer_XmlrpcClient's built-in API key.
- * @param string $endpointUrl The API endpoint base URL you wish to connect to. Set this to SoftLayer_XmlrpcClient::API_PRIVATE_ENDPOINT to connect via SoftLayer's private network.
- * @return SoftLayer_XmlrpcClient
- */
- public static function getClient($serviceName, $id = null, $username = null, $apiKey = null, $endpointUrl = null)
- {
- $serviceName = trim($serviceName);
- $id = trim($id);
- $username = trim($username);
- $apiKey = trim($apiKey);
-
- if ($serviceName == null) {
- throw new Exception('Please provide a SoftLayer API service name.');
- }
-
- $client = new Softlayer_XmlrpcClient();
-
- /*
- * Default to use the public network API endpoint, otherwise use the
- * endpoint defined in API_PUBLIC_ENDPOINT, otherwise use the one
- * provided by the user.
- */
- if (isset($endpointUrl)) {
- $endpointUrl = trim($endpointUrl);
-
- if ($endpointUrl == null) {
- throw new Exception('Please provide a valid API endpoint.');
- }
-
- $client->_endpointUrl = $endpointUrl;
- } elseif (self::API_BASE_URL != null) {
- $client->_endpointUrl = self::API_BASE_URL;
- } else {
- $client->_endpointUrl = SoftLayer_XmlrpcClient::API_PUBLIC_ENDPOINT;
- }
-
- if ($username != null && $apiKey != null) {
- $client->setAuthentication($username, $apiKey);
- } else {
- $client->setAuthentication(self::API_USER, self::API_KEY);
- }
-
- $client->_serviceName = $serviceName;
-
- if ($id != null) {
- $client->setInitParameter($id);
- }
-
- return $client;
- }
-
- /**
- * Set a SoftLayer API call header
- *
- * Every header defines a customization specific to an SoftLayer API call.
- * Most API calls require authentication and initialization parameter
- * headers, but can also include optional headers such as object masks and
- * result limits if they're supported by the API method you're calling.
- *
- * @see removeHeader()
- * @param string $name The name of the header you wish to set
- * @param object $value The object you wish to set in this header
- * @return SoftLayer_XmlrpcClient
- */
- public function addHeader($name, $value)
- {
- if (is_object($value)) {
- $value = (array)$value;
- }
-
- $this->_headers[$name] = $value;
- return $this;
- }
-
- /**
- * Remove a SoftLayer API call header
- *
- * Removing headers may cause API queries to fail.
- *
- * @see addHeader()
- * @param string $name The name of the header you wish to remove
- * @return SoftLayer_XmlrpcClient
- */
- public function removeHeader($name)
- {
- unset($this->_headers[$name]);
- return $this;
- }
-
- /**
- * Set a user and key to authenticate a SoftLayer API call
- *
- * Use this method if you wish to bypass the API_USER and API_KEY class
- * constants and set custom authentication per API call.
- *
- * @link https://manage.softlayer.com/Administrative/apiKeychain API key management in the SoftLayer customer portal
- * @param string $username
- * @param string $apiKey
- * @return SoftLayer_XmlrpcClient
- */
- public function setAuthentication($username, $apiKey)
- {
- $username = trim($username);
- $apiKey = trim($apiKey);
-
- if ($username == null) {
- throw new Exception('Please provide a SoftLayer API username.');
- }
-
- if ($apiKey == null) {
- throw new Exception('Please provide a SoftLayer API key.');
- }
-
- $header = new stdClass();
- $header->username = $username;
- $header->apiKey = $apiKey;
-
- $this->addHeader('authenticate', $header);
- return $this;
- }
-
- /**
- * Set an initialization parameter header on a SoftLayer API call
- *
- * Initialization parameters instantiate a SoftLayer API service object to
- * act upon during your API method call. For instance, if your account has a
- * server with id number 1234, then setting an initialization parameter of
- * 1234 in the SoftLayer_Hardware_Server Service instructs the API to act on
- * server record 1234 in your method calls.
- *
- * @link http://sldn.softlayer.com/article/Using_Initialization_Parameters_in_the_SoftLayer_API Using Initialization Parameters in the SoftLayer API
- * @param int $id The ID number of the SoftLayer API object you wish to instantiate.
- * @return SoftLayer_XmlrpcClient
- */
- public function setInitParameter($id)
- {
- $id = trim($id);
-
- if (!is_null($id)) {
- $initParameters = new stdClass();
- $initParameters->id = $id;
- $this->addHeader($this->_serviceName . 'InitParameters', $initParameters);
- }
-
- return $this;
- }
-
- /**
- * Set an object mask to a SoftLayer API call
- *
- * Use an object mask to retrieve data related your API call's result.
- * Object masks are skeleton objects or strings that define nested relational
- * properties to retrieve along with an object's local properties.
- *
- * @see SoftLayer_ObjectMask
- * @link http://sldn.softlayer.com/article/Using-Object-Masks-SoftLayer-API Using object masks in the SoftLayer API
- * @param object $mask The object mask you wish to define
- * @return SoftLayer_SoapClient
- */
- public function setObjectMask($mask)
- {
- if (!is_null($mask)) {
- $header = 'SoftLayer_ObjectMask';
-
- if ($mask instanceof SoftLayer_ObjectMask) {
- $header = sprintf('%sObjectMask', $this->_serviceName);
- }
-
- $objectMask = new stdClass();
- $objectMask->mask = $mask;
- $this->addHeader($header, $objectMask);
- }
-
- return $this;
- }
-
- /**
- * Set a result limit on a SoftLayer API call
- *
- * Many SoftLayer API methods return a group of results. These methods
- * support a way to limit the number of results retrieved from the SoftLayer
- * API in a way akin to an SQL LIMIT statement.
- *
- * @link http://sldn.softlayer.com/article/Using_Result_Limits_in_the_SoftLayer_API Using Result Limits in the SoftLayer API
- * @param int $limit The number of results to limit your SoftLayer API call to.
- * @param int $offset An optional offset to begin your SoftLayer API call's returned result set at.
- * @return SoftLayer_XmlrpcClient
- */
- public function setResultLimit($limit, $offset = 0)
- {
- $resultLimit = new stdClass();
- $resultLimit->limit = intval($limit);
- $resultLimit->offset = intval($offset);
-
- $this->addHeader('resultLimit', $resultLimit);
- return $this;
- }
-
- /**
- * Remove PHP xmlrpc type definition structures from a decoded request array
- *
- * Certain xmlrpc types like base64 are decoded in PHP to a stdClass with a
- * scalar property containing the decoded value of the xmlrpc member and an
- * xmlrpc_type property describing which xmlrpc type is being described. This
- * function removes xmlrpc_type data and moves the scalar value into the root of
- * the xmlrpc value for known xmlrpc types.
- *
- * @param mixed $result The decoded xmlrpc request to process
- * @return mixed
- */
- private static function _convertXmlrpcTypes($result) {
- if (is_array($result)) {
-
- // Return case 1: The result is an empty array. Return the empty
- // array.
- if (count($result) == 0) {
- return $result;
- } else {
-
- // Return case 2: The result is a non-empty array. Loop through
- // array elements and recursively translate every element.
- // Return the fully translated array.
- foreach ($result as $key => $value) {
- $result[$key] = self::_convertXmlrpcTypes($value);
- }
-
- return $result;
- }
-
- // Return case 3: The result is an xmlrpc scalar. Convert it to a normal
- // variable and return it.
- } elseif (is_object($result) && $result->scalar != null && $result->xmlrpc_type != null) {
-
- // Convert known xmlrpc types, otherwise unset the value.
- switch ($result->xmlrpc_type) {
- case 'base64':
- return $result->scalar;
- break;
- default:
- return null;
- break;
- }
-
- // Return case 4: Otherwise the result is a non-array and non xml-rpc
- // scalar variable. Return it unmolested.
- } else {
- return $result;
- }
- }
-
- /**
- * Recursively convert an array to an object
- *
- * Since xmlrpc_decode_result returns an array, but we want an object
- * result, so cast all array parts in our result set as objects.
- *
- * @param mixed $result A result or portion of a result to convert
- * @return mixed
- */
- private static function _convertToObject($result) {
- return is_array($result) ? (object) array_map('SoftLayer_XmlrpcClient::_convertToObject', $result) : $result;
- }
-}
diff --git a/modules/devshop/devshop_cloud/drush/Provision/Service/provider/SoftLayer/softlayer-api-php-client/example.php b/modules/devshop/devshop_cloud/drush/Provision/Service/provider/SoftLayer/softlayer-api-php-client/example.php
deleted file mode 100644
index 9c4665257..000000000
--- a/modules/devshop/devshop_cloud/drush/Provision/Service/provider/SoftLayer/softlayer-api-php-client/example.php
+++ /dev/null
@@ -1,113 +0,0 @@
-, [username], [API key]);
- *
- * API Service: The name of the API service you wish to connect to.
- * id: An optional id to initialize your API service with, if you're
- * interacting with a specific object. If you don't need to specify
- * an id then pass null to the client.
- * username: Your SoftLayer API username.
- * API key: Your SoftLayer API key,
- */
-$client = SoftLayer_SoapClient::getClient('SoftLayer_Account', null, $apiUsername, $apiKey);
-
-/**
- * Once your client object is created you can call API methods for that service
- * directly against your client object. A call may throw an exception on error,
- * so it's best to try your call and catch exceptions.
- *
- * This example calls the getObject() method in the SoftLayer_Account API
- * service.
- * It retrieves basic account information, and is a great way to test your API
- * account and connectivity.
- */
-try {
- print_r($client->getObject());
-} catch (Exception $e) {
- die($e->getMessage());
-}
-
-/**
- * For a more complex example we’ll retrieve a support ticket with id 123456
- * along with the ticket’s updates, the user it’s assigned to, the servers
- * attached to it, and the datacenter those servers are in. We’ll retrieve our
- * extra information using a nested object mask. After we have the ticket we’ll
- * update it with the text ‘Hello!’.
- */
-
-// Declare an API client to connect to the SoftLayer_Ticket API service.
-$client = SoftLayer_SoapClient::getClient('SoftLayer_Ticket', 123456, $apiUsername, $apiKey);
-
-// Assign an object mask to our API client:
-$objectMask = new SoftLayer_ObjectMask();
-$objectMask->updates;
-$objectMask->assignedUser;
-$objectMask->attachedHardware->datacenter;
-$client->setObjectMask($objectMask);
-
-// Retrieve the ticket record.
-try {
- $ticket = $client->getObject();
- print_r($ticket);
-} catch (Exception $e) {
- die('Unable to retrieve ticket record: ' . $e->getMessage());
-}
-
-// Now update the ticket.
-$update = new stdClass();
-$update->entry = 'Hello!';
-
-try {
- $update = $client->addUpdate($update);
- echo "Updated ticket 123456. The new update's id is " . $update[0]->id . '.';
-} catch (Exception $e) {
- die('Unable to update ticket: ' . $e->getMessage());
-}
diff --git a/modules/devshop/devshop_cloud/drush/Provision/Service/provider/digitalocean.php b/modules/devshop/devshop_cloud/drush/Provision/Service/provider/digitalocean.php
deleted file mode 100644
index 48921b85b..000000000
--- a/modules/devshop/devshop_cloud/drush/Provision/Service/provider/digitalocean.php
+++ /dev/null
@@ -1,42 +0,0 @@
-server: Provision_Context_server
- */
- function save_server() {
-
- // Look for provider_server_identifier
- $server_identifier = $this->server->provider_server_identifier;
-
- // If server ID is already found, move on.
- if (!empty($server_identifier)) {
- drush_log('[DEVSHOP] Server Identifier Found: ' . $server_identifier . ' Not creating new server.', 'ok');
- }
- // If there is no server ID, create the server.
- else {
-
- drush_log('[DEVSHOP] Server Identifier not found. Creating new server!', 'ok');
-
- // Faking our provider_data response.
- $this->server->setProperty('provider_data', array(
- 'hello' => 'do',
- 'fake data' => 'from digitalocean',
- ));
-
- // Faking our provider server identifier.
- $this->server->setProperty('provider_server_identifier', '123456789');
-
- $this->server->setProperty('ip_addresses', array(
- '1.2.3.4'
- ));
-
- drush_log('[DEVSHOP] Server Identifier found: 123456789. Assumed server was created.', 'ok');
- }
- }
-}
\ No newline at end of file
diff --git a/modules/devshop/devshop_cloud/drush/Provision/Service/provider/rackspace.php b/modules/devshop/devshop_cloud/drush/Provision/Service/provider/rackspace.php
deleted file mode 100644
index e4641db63..000000000
--- a/modules/devshop/devshop_cloud/drush/Provision/Service/provider/rackspace.php
+++ /dev/null
@@ -1,137 +0,0 @@
-server->rackspace_id = drush_get_option('rackspace_id', '');
-// $this->server->rackspace_image = drush_get_option('rackspace_image', '');
-// $this->server->rackspace_flavor = drush_get_option('rackspace_flavor', '');
-// $this->server->attributes_json = drush_get_option('attributes_json', '');
-// $this->server->role = drush_get_option('role', '');
-//
-// }
-//
-// /**
-// * This is run immediately after provision saves the server config files.
-// *
-// *
-// * Provision client home path /var/aegir/clients is writable.
-// * [DEVUDO] Verifying Server anotherfaker
-// */
-// function verify_server_cmd() {
-// drush_log('[DEVUDO] Verifying Server ' . d()->remote_host, 'ok');
-//
-// $server_fqdn = d()->remote_host;
-// $role = $this->server->role;
-// $rackspace_flavor = $this->server->rackspace_flavor; // 2
-// $rackspace_image = $this->server->rackspace_image;
-// $rackspace_id = $this->server->rackspace_id;
-// $attributes = $this->server->attributes_json;
-//
-// $ips = array();
-//
-// // Look for this chef node on Chef Server
-// drush_log("[DEVUDO] Looking for chef node $server_fqdn on chef server", 'ok');
-// $chef_node = shop_get_server($server_fqdn);
-//
-// // If shop_get_server() returns a string, knife node show didn't work.
-// if (is_string($chef_node)) {
-//
-// // If the error is NOT object not found, there was a more serious error
-// if (strpos($chef_node, 'ERROR: The object you are looking for could not be found') !== 0){
-// return drush_set_error(DRUSH_DEVUDO_ERROR, '[DEVUDO] knife failed: ' . $chef_node);
-// }
-// // Otherwise, we just don't have a chef node of that name yet.
-// // So, create a new server.
-// drush_log("[DEVUDO] Chef Node not found. Creating server...", 'ok');
-//
-// drush_log("[DEVUDO] Running: drush server-create $server_fqdn --role=$role --rackspace_flavor=$rackspace_flavor --rackspace_image=$rackspace_image --attributes=$attributes", 'ok');
-//
-// drush_set_option('rackspace_flavor', $rackspace_flavor);
-// drush_set_option('rackspace_image', $rackspace_image);
-// drush_set_option('role', $role);
-// drush_set_option('attributes', $attributes);
-//
-// $data = drush_shop_provision_server_create($server_fqdn);
-// $ips[] = $data['Public IP Address'];
-// $ips[] = $data['Private IP Address'];
-//
-// // Save for shop_hosting_post_hosting_verify_task()
-// drush_set_option('rackspace_id', $data['Instance ID']);
-// drush_set_option('ip_addresses', $ips);
-// }
-// // If we got a server node... run chef-client to update it.
-// else {
-// $ip = $chef_node->automatic->ipaddress;
-// drush_log("[DEVUDO] Chef node found with name:$server_fqdn ip:$ip Preparing attributes...", 'ok');
-//
-// // @TODO: Copy the attributes file and run chef-client again.
-// // Save new json data to file
-//
-// $json_path = "/tmp/$server_fqdn.json";
-// $attributes_json = $attributes;
-// file_put_contents($json_path, $attributes_json);
-//
-// // Sync file to server
-// // Use IP in case something is wrong with DNS
-// if (!empty($ip)){
-// $host = $ip;
-// }
-// else {
-// $host = $server_fqdn;
-// }
-//
-// // @TODO: This line implies that aegir already has ssh access to devudo@host
-// shop_exec("scp $json_path devudo@$host:~/attributes.json");
-//
-// // Run chef-client to update the server itself.
-// $chef_client_cmd = "sudo /usr/bin/chef-client -j attributes.json";
-// $chef_client_cmd_exec = escapeshellarg($chef_client_cmd);
-// drush_log("[DEVUDO] Running chef-client on $server_fqdn:", 'ok');
-// shop_exec("knife ssh name:$server_fqdn -x devudo $chef_client_cmd_exec -a ipaddress");
-// }
-// parent::verify_server_cmd();
-// }
-//
-//
-//
-// function config_data($config = null, $class = null) {
-// $data = parent::config_data($config, $class);
-// $data['rackspace_id'] = $this->server->rackspace_id;
-// $data['rackspace_image'] = $this->server->rackspace_image;
-// $data['rackspace_flavor'] = $this->server->rackspace_flavor;
-// $data['role'] = $this->server->role;
-// $data['attributes_json'] = $this->server->attributes_json;
-// return $data;
-// }
-//
-// static function option_documentation() {
-// return array(
-// '--rackspace_id' => 'The unique rackspace server ID.',
-// '--rackspace_image' => 'The rackspace server image.',
-// '--rackspace_flavor' => 'The rackspace server flavor.',
-// '--role' => 'The chef role.',
-// '--attributes_json' => 'JSON encoded attributes.',
-// );
-// }
-//
-// /**
-// * Ask the web server to check for and load configuration changes.
-// */
-// function parse_configs() {
-// return TRUE;
-// }
-//}
diff --git a/modules/devshop/devshop_cloud/drush/Provision/Service/provider/softlayer.php b/modules/devshop/devshop_cloud/drush/Provision/Service/provider/softlayer.php
deleted file mode 100644
index 0782615a9..000000000
--- a/modules/devshop/devshop_cloud/drush/Provision/Service/provider/softlayer.php
+++ /dev/null
@@ -1,88 +0,0 @@
-server->provider_server_identifier;
-
- // If server ID is already found, move on.
- if ($server_identifier) {
- drush_log('[DEVSHOP] Server Identifier Found. Not creating new server.', 'ok');
- }
- // If there is no server ID, create the server.
- else {
-
- drush_log('[DEVSHOP] Server Identifier not found. Creating new server!', 'ok');
-
- $server_fqdn = d()->remote_host;
-
- drush_log('[DEVSHOP|softlayer] Creating Server ' . $server_fqdn . '...', 'notice');
-
- // Initialize an API client for the SoftLayer_Account service.
- $virtual_guest = $this->softlayer_client('SoftLayer_Virtual_Guest');
- $provider_options = $this->prepare_provider_options();
-
- // Retrieve our account record
- try {
-
- // @TODO: Add more robust simulation.
- //$server = array(
- // 'id' => '00000',
- // 'stuff' => 'from softlayer',
- //);
- $server = (array) $virtual_guest->createObject($provider_options);
-
- drush_log('[DEVSHOP|softlayer] Created server in softlayer: ' . $server['id'], 'ok');
- } catch (Exception $e) {
- return drush_set_error('DEVSHOP_CLOUD_API_ACCESS_DENIED', $e->getMessage());
- }
-
- $provider_data = (array) $server;
- $this->server->setProperty('provider_data', $provider_data);
- $this->server->setProperty('provider_server_identifier', $provider_data['id']);
- }
- }
-
- function prepare_provider_options() {
- $devshop_cloud_provider_options = (object) drush_get_option('provider_options', '');
-
- // Break up title into hostname (subdomain) and domain.
- $provider_options = new stdClass();
- $domain = explode('.', d()->remote_host);
- $provider_options->hostname = $domain[0];
- $provider_options->domain = implode('.', array_slice($domain, 1));
- $provider_options->startCpus = $devshop_cloud_provider_options->processors;
- $provider_options->maxMemory = $devshop_cloud_provider_options->memory;
- $provider_options->hourlyBillingFlag = TRUE;
- $provider_options->localDiskFlag = TRUE;
- $provider_options->dedicatedAccountHostOnlyFlag = FALSE;
- $provider_options->operatingSystemReferenceCode = $devshop_cloud_provider_options->operatingSystems;
-
- $provider_options->datacenter = new stdClass();
- $provider_options->datacenter->name = $devshop_cloud_provider_options->datacenter;
-
- return $provider_options;
- }
-
- /**
- * Helper for getting a softlayer client.
- * @param $service
- * @return \Softlayer_SoapClient
- */
- private function softlayer_client($service, $id = null) {
- $api_key = drush_get_option('softlayer_api_key');
- $username = drush_get_option('softlayer_api_username');
-
- // Initialize an API client for the SoftLayer_Account service.
- $client = SoftLayer_SoapClient::getClient($service, $id, $username, $api_key);
- return $client;
- }
-}
\ No newline at end of file
diff --git a/modules/devshop/devshop_cloud/drush/README.md b/modules/devshop/devshop_cloud/drush/README.md
deleted file mode 100644
index 591c74eb7..000000000
--- a/modules/devshop/devshop_cloud/drush/README.md
+++ /dev/null
@@ -1,2 +0,0 @@
-# devshop_cloud
-Cloud server provisioning for devshop.
diff --git a/modules/devshop/devshop_cloud/drush/devshop_cloud.drush.inc b/modules/devshop/devshop_cloud/drush/devshop_cloud.drush.inc
deleted file mode 100644
index e44fe5fb5..000000000
--- a/modules/devshop/devshop_cloud/drush/devshop_cloud.drush.inc
+++ /dev/null
@@ -1,43 +0,0 @@
- NULL);
-}
diff --git a/modules/devshop/devshop_cloud/drush/tools.inc b/modules/devshop/devshop_cloud/drush/tools.inc
deleted file mode 100644
index bce0161d3..000000000
--- a/modules/devshop/devshop_cloud/drush/tools.inc
+++ /dev/null
@@ -1,45 +0,0 @@
- 'devshop_dothooks'));
+}
diff --git a/modules/devshop/devshop_dothooks/devshop_dothooks.module b/modules/devshop/devshop_dothooks/devshop_dothooks.module
new file mode 100644
index 000000000..6bfc6bb5b
--- /dev/null
+++ b/modules/devshop/devshop_dothooks/devshop_dothooks.module
@@ -0,0 +1,151 @@
+settings->deploy['dothooks']) || empty($environment->settings->deploy['dothooks'])){
+ return;
+ }
+
+ if (file_exists($environment->repo_path . '/.hooks.yaml')) {
+ $hooks_file = '.hooks.yaml';
+ $hooks_path = $environment->repo_path . '/.hooks.yaml';
+ }
+ elseif (file_exists($environment->repo_path . '/.hooks.yml')) {
+ $hooks_file = '.hooks.yml';
+ $hooks_path = $environment->repo_path . '/.hooks.yml';
+ }
+ elseif (file_exists($environment->repo_path . '/.hooks')) {
+ $hooks_file = '.hooks';
+ $hooks_path = $environment->repo_path . '/.hooks';
+ }
+
+ $environment->dothooks_file_name = $hooks_file;
+ $environment->dothooks_file_path = $hooks_path;
+
+ // Attempt to parse
+ if (!empty($environment->dothooks_file_name)) {
+ try {
+ $environment->dothooks = $yaml->parse(file_get_contents($hooks_path));
+ } catch (\Symfony\Component\Yaml\Exception\ParseException $e) {
+ $environment->warnings[] = array(
+ 'text' => t('Invalid YAML in !file: !message', array(
+ '!file' => $hooks_path,
+ '!message' => $e->getMessage(),
+ )),
+ 'type' => 'error',
+ );
+ }
+ }
+}
+
+/**
+ * Implements hook_help()
+ * @param $path
+ * @param $arg
+ * @return string
+ */
+function devshop_dothooks_help($path, $arg)
+{
+ switch ($path) {
+ // Main module help for the block module
+ case 'admin/help#devshop_dothooks':
+ $note = t(
+ 'You can control what happens on deploy through a .hooks file in your repository.'
+ );
+
+ return <<$note
+
+# Fires after an environment is installed.
+install: |
+ drush {{alias}} vset site_name "Hooks Hooks Hooks"
+
+# Fires after code is deployed. A "deployment" happens when you push to your
+# git repository or select a new branch or tag for your environment.
+deploy: |
+ drush {{alias}} updb -y
+ drush {{alias}} cc all
+
+# Fires after "verify" task.
+verify: |
+ drush {{alias}} status
+
+# Fires after "Run Tests" task.
+test: |
+ drush {{alias}} uli
+
+
+# Fires after "Deploy Data (Sync)" task.
+sync: |
+ drush {{alias}} en devel -y
+
+
+HTML;
+
+ }
+}
+
+
+/**
+ * Runs a hook for a task.
+ * @param $hook
+ * @param $task
+ */
+function devshop_dothooks_run_hook($hook, $environment) {
+
+ // Reload the alter hook in case there are new hooks.
+ devshop_dothooks_devshop_environment_alter($environment, $environment->project);
+
+ // Respect drush option, but default to environment settings.
+ if (!drush_get_option('dothooks', $environment->settings->deploy['dothooks'])) {
+ drush_log('[.hooks] Environment not configured to run .hooks commands.', 'info');
+ return;
+ }
+
+ // If no dothooks file is found, throw an error.
+ if (empty($environment->dothooks)) {
+ drush_log(dt('Hook file not found, but the project is configured to use them. Create a .hooks file or turn off "Run deploy commands in the .hooks file".'), 'notice');
+ return;
+ }
+
+ drush_log('[.hooks] Hook file found: ' . $environment->dothooks_file_name, 'ok');
+
+ // Allow big string or lists of commands.
+ if (is_array($environment->dothooks[$hook])) {
+ $hooks = array_filter($environment->dothooks[$hook]);
+ }
+ else {
+ $hooks = array_filter(explode("\n", $environment->dothooks[$hook]));
+ }
+
+ $env = $_SERVER;
+ $env['DEVSHOP_PROJECT'] = $environment->project_name;
+ $env['DEVSHOP_ENVIRONMENT'] = $environment->name;
+
+ // Prepare and run each command.
+ foreach ($hooks as $hook_line) {
+ $hook_line = strtr($hook_line, array(
+ '{{alias}}' => $environment->system_alias,
+ ));
+ provision_process($hook_line, $environment->repo_path, 'DevShop .hooks.yml');
+ }
+}
+
diff --git a/modules/devshop/devshop_dothooks/hooks.php b/modules/devshop/devshop_dothooks/hooks.php
new file mode 100644
index 000000000..ccc9830ce
--- /dev/null
+++ b/modules/devshop/devshop_dothooks/hooks.php
@@ -0,0 +1,53 @@
+ref->type == 'site') {
+ devshop_dothooks_run_hook('verify', $task->ref->environment);
+ }
+}
+
+/**
+ * Implementation of hook_post_hosting_TASK_TYPE_task()
+ * for DevShop Deploy tasks.
+ *
+ * Runs the "deploy" dotHook
+ */
+function devshop_dothooks_post_hosting_devshop_deploy_task($task, $data) {
+ devshop_dothooks_run_hook('deploy', $task->ref->environment);
+}
+
+/**
+ * Implementation of hook_post_hosting_TASK_TYPE_task()
+ * for Run Tests tasks.
+ *
+ * Runs the "test" dotHook
+ */
+function devshop_dothooks_post_hosting_test_task($task, $data) {
+ devshop_dothooks_run_hook('test', $task->ref->environment);
+}
+
+/**
+ * Implementation of hook_post_hosting_TASK_TYPE_task()
+ * for Sync tasks.
+ *
+ * Runs the "sync" dotHook
+ */
+function devshop_dothooks_post_hosting_sync_task($task, $data) {
+ devshop_dothooks_run_hook('sync', $task->ref->environment);
+}
+
+/**
+ * Implementation of hook_post_hosting_TASK_TYPE_task()
+ * for Install tasks.
+ *
+ * Runs the "install" dotHook
+ */
+function devshop_dothooks_post_hosting_install_task($task, $data) {
+ devshop_dothooks_run_hook('install', $task->ref->environment);
+}
\ No newline at end of file
diff --git a/modules/devshop/devshop_extra_users/README.md b/modules/devshop/devshop_extra_users/README.md
new file mode 100644
index 000000000..9abb25762
--- /dev/null
+++ b/modules/devshop/devshop_extra_users/README.md
@@ -0,0 +1,18 @@
+DevShop Extras: Users
+=====================
+
+This module provides an example of how to use the DevShop front-end to take action
+after an environment is installed.
+
+Enable it, then visit the "Create Environment" form for a project.
+
+You will see a form field for "Manager Email". This field gets saved into the environment settings automatically.
+
+Then, in `devshop_extras_users.drush.inc` during the `hook_post_hosting_TASKTYPE_task`
+hook, this module creates a new user in your drupal site immediately after the
+installation.
+
+Nothing else happens here. Typically you would want to set a role or send an
+email.
+
+Use this code as an example for extending your own environments.
\ No newline at end of file
diff --git a/modules/devshop/devshop_extra_users/devshop_extra_users.drush.inc b/modules/devshop/devshop_extra_users/devshop_extra_users.drush.inc
new file mode 100644
index 000000000..9b9b75e27
--- /dev/null
+++ b/modules/devshop/devshop_extra_users/devshop_extra_users.drush.inc
@@ -0,0 +1,34 @@
+ref->type == 'site' && isset($task->ref->environment->settings->manager_email)) {
+
+ // Create an extra user.
+ $email = $task->ref->environment->settings->manager_email;
+ $password = provision_password();
+ $arguments = array('name' => $email);
+ $data = array(
+ 'mail' => $email,
+ 'password' => $password,
+ );
+
+ drush_log('Manager email is found! running user-create...', 'ok');
+ provision_backend_invoke($task->ref->title, 'user-create', $arguments, $data);
+
+ drush_log(dt('User !user has been created with password !password', array(
+ '!user' => $email,
+ '!password' => $password,
+ )), 'ok');
+
+ drush_log('No email has been sent! Please notify your new user.');
+ }
+}
diff --git a/modules/devshop/devshop_extra_users/devshop_extra_users.info b/modules/devshop/devshop_extra_users/devshop_extra_users.info
new file mode 100644
index 000000000..bcc11ca1d
--- /dev/null
+++ b/modules/devshop/devshop_extra_users/devshop_extra_users.info
@@ -0,0 +1,5 @@
+name = DevShop Extra Install
+description = Example module for loading extra info for an install profile.
+core = 7.x
+package = DevShop
+
diff --git a/modules/devshop/devshop_extra_users/devshop_extra_users.module b/modules/devshop/devshop_extra_users/devshop_extra_users.module
new file mode 100644
index 000000000..e1d13d86a
--- /dev/null
+++ b/modules/devshop/devshop_extra_users/devshop_extra_users.module
@@ -0,0 +1,15 @@
+ 'textfield',
+ '#title' => t('Manager Email'),
+ '#description' => t('Enter an email address and a user will be created.'),
+ );
+ }
+}
diff --git a/modules/devshop/devshop_github/.gitignore b/modules/devshop/devshop_github/.gitignore
new file mode 100644
index 000000000..e69de29bb
diff --git a/modules/devshop/devshop_github/composer.json b/modules/devshop/devshop_github/composer.json
new file mode 100644
index 000000000..56e09bfdb
--- /dev/null
+++ b/modules/devshop/devshop_github/composer.json
@@ -0,0 +1,5 @@
+{
+ "require": {
+ "knplabs/github-api": "~1.2"
+ }
+}
\ No newline at end of file
diff --git a/modules/devshop/devshop_github/devshop_github.drush.inc b/modules/devshop/devshop_github/devshop_github.drush.inc
new file mode 100644
index 000000000..01f7ad763
--- /dev/null
+++ b/modules/devshop/devshop_github/devshop_github.drush.inc
@@ -0,0 +1,117 @@
+task_type, $types) && isset($task->ref->project) && !empty($task->ref->project) && isset($task->ref->environment) && !empty($task->ref->environment)) {
+ $project = $task->ref->project;
+ $environment = $task->ref->environment;
+ }
+ else {
+ return;
+ }
+
+ // If a pull request object is available...
+ if (isset($environment->github_pull_request->pull_request_object->deployment)) {
+
+ // If project is configured to reinstall every time, only react on "install" tasks. Otherwise, we get two github deployments because both a "deploy" (git pull) and a "install" task are run on each git push.
+ if ($project->settings->github['pull_request_reinstall'] && $task->task_type == 'devshop-deploy') {
+ return;
+ }
+
+ // Create a deployment status
+ $owner = $project->github_owner;
+ $repo = $project->github_repo;
+ $deployment_id = $environment->github_pull_request->pull_request_object->deployment->id;
+
+ try {
+ $token = variable_get('devshop_github_token', '');
+ $client = new \Github\Client();
+ $client->authenticate($token, Github\Client::AUTH_HTTP_TOKEN);
+
+ $params = new stdClass();
+ if ($status == HOSTING_TASK_SUCCESS || $status == HOSTING_TASK_WARNING) {
+ $params->state = $state = 'success';
+ }
+ else {
+ $params->state = $state = 'failure';
+ }
+
+ // If task is a test run, only submit a commit status for devshop/tests context.
+ if ($task->task_type == 'test') {
+ $sha = $environment->github_pull_request->pull_request_object->head->sha;
+
+ $params = new stdClass();
+ $params->state = $state;
+ $params->target_url = url("node/{$task->nid}/view", array('absolute' => TRUE));
+
+ if ($status == HOSTING_TASK_WARNING) {
+ $params->description = t('DevShop: Tests passed with warnings');
+ }
+ else {
+ $params->description = t('DevShop: Tests !status!', array('!status' => $state));
+ }
+ $params->context = "devshop/{$project->name}/tests";
+
+ $status = $client->getHttpClient()->post("/repos/$owner/$repo/statuses/$sha", json_encode($params));
+ drush_log('Commit status created for devshop/tests!', 'success');
+ }
+ // Otherwise we create a deployment and a commit status.
+ else {
+
+ $params->target_url = $environment->url;
+ $params->description = t('Visit !url', array('!url' => $task->ref->environment->url));
+ $post_url = "/repos/$owner/$repo/deployments/{$deployment_id}/statuses";
+
+ drush_log('Attempting to create github deployment status: ' . $post_url, 'success');
+
+ $deployment_status = $client->getHttpClient()->post($post_url, json_encode($params));
+ drush_log('Deployment status created!', 'success');
+
+
+ // Update Status API
+
+ // Create a status
+ $sha = $environment->github_pull_request->pull_request_object->head->sha;
+
+ $params = new stdClass();
+ $params->state = $state;
+ $params->target_url = url("node/{$task->nid}", array('absolute' => TRUE));;
+
+ if ($status == HOSTING_TASK_WARNING) {
+ $params->description = t('DevShop: Deploy success with warnings. [!url]', array(
+ '!url' => $environment->url,
+ ));
+ }
+ else {
+ $params->description = t('DevShop: Deploy !status [!url]', array(
+ '!status' => $state,
+ '!url' => $environment->url,
+ ));
+ }
+ $params->context = "devshop/{$project->name}/deploy";
+
+ $deployment_status = $client->getHttpClient()->post("/repos/$owner/$repo/statuses/$sha", json_encode($params));
+ drush_log('Commit status created!', 'success');
+
+ // If deploy task fails, tests won't run.
+ if ($environment->settings->deploy['test'] && $status == HOSTING_TASK_ERROR) {
+
+ $params = new stdClass();
+ $params->state = $state;
+ $params->description = t('DevShop: Tests not run due to Deploy Fail');
+ $params->context = "devshop/{$project->name}/tests";
+
+ $deployment_status = $client->getHttpClient()->post("/repos/$owner/$repo/statuses/$sha", json_encode($params));
+ drush_log('Commit status created for devshop/tests', 'success');
+ }
+ }
+ } catch (Github\Exception\RuntimeException $e) {
+ drush_log('GitHub API Error: ' . $e->getMessage(), 'error');
+ }
+ }
+}
diff --git a/modules/devshop/devshop_github/devshop_github.info b/modules/devshop/devshop_github/devshop_github.info
new file mode 100644
index 000000000..00126595b
--- /dev/null
+++ b/modules/devshop/devshop_github/devshop_github.info
@@ -0,0 +1,8 @@
+name = DevShop GitHub
+description = Integration with GitHub
+core = 7.x
+package = DevShop
+files[] = includes/add-key.inc
+files[] = includes/admin.inc
+dependencies[] = devshop_projects
+dependencies[] = composer_manager
diff --git a/modules/devshop/devshop_github/devshop_github.install b/modules/devshop/devshop_github/devshop_github.install
new file mode 100644
index 000000000..23b3482ec
--- /dev/null
+++ b/modules/devshop/devshop_github/devshop_github.install
@@ -0,0 +1,94 @@
+ array(
+ 'id' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'Pull Request ID',
+ ),
+ 'number' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'Pull Request Number',
+ ),
+ 'project_nid' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => "The project's Node ID.",
+ ),
+ 'environment_name' => array(
+ 'type' => 'varchar',
+ 'not null' => TRUE,
+ 'length' => 64,
+ 'default' => '',
+ 'description' => 'Environment name for this pull request environment.',
+ ),
+ 'pull_request_object' => array(
+ 'type' => 'text',
+ 'not null' => FALSE,
+ 'size' => 'big',
+ 'description' => 'A serialized array of settings for this environment.',
+ ),
+ ),
+ 'primary key' => array('id'),
+ );
+ return $schema;
+}
+
+/**
+ * Implements hook_install().
+ */
+function devshop_github_install() {
+
+ // Push devshop_github's system weight to 1.
+ db_update('system')
+ ->fields(array(
+ 'weight' => 2
+ ))
+ ->condition('name', 'devshop_github')
+ ->execute();
+
+ // Display a message about setting a github personal token.
+ drupal_set_message(t('DevShop GitHub module has been enabled. You must add an access token to enable full functionality at !link.', array(
+ '!link' => l(t('the settings page'), 'admin/devshop/github'),
+ )));
+}
+
+/**
+ * Set a weight higher than devshop_project so our form doesn't get obliterated by
+ * devshop_projects_form_project_node_form_alter()
+ */
+function devshop_github_update_7000() {
+ db_update('system')
+ ->fields(array(
+ 'weight' => 1
+ ))
+ ->condition('name', 'devshop_github')
+ ->execute();
+}
+
+/**
+ * Set a weight higher than devshop_project module.
+ */
+function devshop_github_update_7001() {
+ db_update('system')
+ ->fields(array(
+ 'weight' => 2
+ ))
+ ->condition('name', 'devshop_github')
+ ->execute();
+}
\ No newline at end of file
diff --git a/modules/devshop/devshop_github/devshop_github.module b/modules/devshop/devshop_github/devshop_github.module
new file mode 100644
index 000000000..d176cc7fc
--- /dev/null
+++ b/modules/devshop/devshop_github/devshop_github.module
@@ -0,0 +1,1326 @@
+ 'GitHub',
+ 'description' => 'DevShop GitHub Integration Settings',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('devshop_github_settings_form'),
+ 'access arguments' => array('administer projects'),
+ 'file' => 'admin.inc',
+ 'file path' => drupal_get_path('module', 'devshop_github') . '/includes',
+ 'type' => MENU_LOCAL_TASK,
+ );
+ $items['admin/devshop/github/add-key'] = array(
+ 'title' => 'Add public key to GitHub Account',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('devshop_github_add_key_to_account'),
+ 'access arguments' => array('administer projects'),
+ 'file' => 'add-key.inc',
+ 'file path' => drupal_get_path('module', 'devshop_github') . '/includes',
+ 'type' => MENU_CALLBACK,
+ );
+ $items['admin/devshop/github/load-repos'] = array(
+ 'title' => 'Load all repos that the user has access to.',
+ 'page callback' => 'devshop_github_get_repositories_page',
+ 'access arguments' => array('administer projects'),
+ 'file' => 'admin.inc',
+ 'file path' => drupal_get_path('module', 'devshop_github') . '/includes',
+ 'type' => MENU_CALLBACK,
+ );
+ return $items;
+}
+
+/**
+ * Implements hook_form_FORM_ID_alter() for project_node_form().
+ */
+function devshop_github_form_project_node_form_alter(&$form, &$form_state, $form_id) {
+ $node = $form['#node'];
+
+ if ($node->project->git_provider != 'github') {
+ return;
+ }
+
+ //All settings git pull in project page
+ $form['project']['settings']['github'] = array(
+ '#type' => 'fieldset',
+ '#group' => 'project_settings',
+ '#collapsible' => TRUE,
+ '#collapsed' => arg(1) != $node->nid,
+ '#title' => t('GitHub Integration'),
+ );
+
+ // Pull Requests create environments?
+ // $form['github']['pull_request_environments'] = array(
+ $form['project']['settings']['github']['pull_request_environments'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Create Environments for Pull Requests'),
+ '#default_value' => isset($node->project->settings->github) ? $node->project->settings->github['pull_request_environments'] : FALSE,
+ '#description' => t('If using GitHub, create a new environment when a new Pull Request is created.'),
+ );
+
+ // Delete Pull Request environments?
+ // $form['github']['pull_request_environments_delete'] = array(
+ $form['project']['settings']['github']['pull_request_environments_delete'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Delete Pull Request Environments'),
+ '#default_value' => isset($node->project->settings->github) ? $node->project->settings->github['pull_request_environments_delete'] : FALSE,
+ '#description' => t('When Pull Requests are closed, delete the environment.'),
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="project[settings][github][pull_request_environments]"]' => array('checked' => TRUE),
+ ),
+ ),
+ );
+
+ // Pull Request Environment method.
+ // $form['github']['pull_request_environments_method'] = array(
+
+ $environments = array_keys($node->project->environments);
+ $options = array(
+ t('Install Drupal') => array(
+ 'devshop__github__install' => empty($node->project->install_profile)? t('Default install profile.'): $node->project->install_profile,
+ ),
+ t('Clone another environment') => array(),
+ );
+
+ if (empty($environments)) {
+ $options[t('Clone another environment')][] = t('No environments available. Check Project settings when you have one.');
+ }
+ else {
+ $options[t('Clone another environment')] = array_combine($environments, $environments);
+ }
+ $form['project']['settings']['github']['pull_request_environments_method'] = array(
+ '#type' => 'select',
+ '#title' => t('Pull Request Environment Creation Method'),
+ '#default_value' => isset($node->project->settings->github) ?
+ $node->project->settings->github['pull_request_environments_method'] : 'devshop__github__install',
+ '#description' => t('Select the method for creating the pull request environments.'),
+ '#options' => $options,
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="project[settings][github][pull_request_environments]"]' => array('checked' => TRUE),
+ ),
+ ),
+ );
+ $form['project']['settings']['github']['pull_request_reinstall'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Reinstall Pull Request Environments on every git push.'),
+ '#default_value' => isset($node->project->settings->github) ?
+ $node->project->settings->github['pull_request_reinstall'] : 0,
+ '#description' => t('Destroy and reinstall Pull Request environments on every code push. All data in environments created via Pull Request will be destroyed.'),
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="project[settings][github][pull_request_environments]"]' => array('checked' => TRUE),
+ ),
+ ),
+ );
+}
+
+/**
+ * Implements hook_form_alter().
+ */
+function devshop_github_form_devshop_project_create_step_git_alter(&$form, &$form_state, $form_id) {
+
+ module_load_include('inc', 'devshop_github', 'includes/admin');
+
+ if (!devshop_github_check_key(TRUE)) {
+ $form['connect'] = array(
+ '#type' => 'container',
+ '#markup' => t('DevShop Git Integration is not set up. Check settings and try again.'),
+ '#prefix' => "
';
+ $form['devshop_github_ssh_key']['ssh_key_status'] ['#markup'] = t('You must enter a GitHub token before SSH access can be setup and confirmed.');
+ }
+ elseif (!$ssh_key_exists) {
+ $form['devshop_github_ssh_key']['ssh_key_status'] ['#prefix'] = '
';
+ $form['devshop_github_ssh_key']['ssh_key_status'] ['#markup'] = t('DevShop does not know what this server\'s public SSH Key is. Run the following command on the server to import the aegir user\'s public key ');
+ }
+ elseif (devshop_github_check_key()) {
+ $form['devshop_github_ssh_key']['ssh_key_status'] ['#prefix'] = '
';
+ $form['devshop_github_ssh_key']['ssh_key_status'] ['#markup'] = t("This DevShop's public SSH Key is in your GitHub account.");
+ }
+ else {
+ $form['devshop_github_ssh_key']['ssh_key_status'] ['#markup'] = t("This DevShop's public SSH Key was not found in your GitHub account.");
+ $form['devshop_github_ssh_key']['ssh_key_status_buttons'] ['#markup'] .= '
+
+
+HTML;
+
+}
+
+/**
+ * Replacement for hosting_get_tasks() that will load tasks for the platform and the site, as well as clone targets.
+ * @param $environment
+ * The environment object.
+ *
+ * @param null $type
+ * The task type you wish to retrieve.
+ *
+ * @param null $key
+ *
+ * If $key is specified, the return array will be re-ordered, with the key of
+ * the array being set to $task_node->$key.
+ *
+ * For example, if you want all tasks for an environment, sorted by date, use
+ * 'vid' for the $key parameter:
+ *
+ * $tasks = devshop_get_tasks($environment, NULL, 'vid');
+ *
+ * @return array
+ *
+ * By default, an array is returned with Task types as the keys. Each element
+ * in the array is another array, keyed by VID.
+ *
+ */
+function devshop_get_tasks($environment, $type = NULL, $key = NULL, $limit = 20) {
+
+ // Load tasks from hosting_task table for both site and platform.
+ $args = array();
+ if ($environment->site) {
+ if ($type) {
+ $where = "(t.rid = :site OR t.rid = :platform) AND t.task_type = :type";
+ $args = array(
+ ':site' => $environment->site,
+ ':platform' => $environment->platform,
+ ':type' => $type
+ );
+ }
+ else {
+ $where = "(t.rid = :site OR t.rid = :platform)";
+ $args = array(
+ ':site' => $environment->site,
+ ':platform' => $environment->platform,
+ );
+ }
+ }
+ else {
+ if ($type) {
+ $where = "(t.rid = :platform AND t.task_type = :type)";
+ $args = array(
+ ':platform' => $environment->platform,
+ ':type' => $type
+ );
+ }
+ else {
+ $where = "(t.rid = :platform)";
+ $args = array(
+ ':platform' => $environment->platform,
+ );
+ }
+ }
+
+ // Ensure limit is safe.
+ if (!is_int($limit) || $limit <= 0) {
+ $limit = 20;
+ }
+ $results = db_query("
+ SELECT t.nid, t.task_type
+ FROM {node} tn
+ INNER JOIN {hosting_task} t ON tn.nid = t.nid
+ WHERE
+ $where
+ ORDER BY t.vid DESC
+ LIMIT $limit
+ ",
+ $args
+ );
+ $tasks = array();
+ foreach ($results as $result) {
+ $tasks[$result->task_type][$result->nid] = node_load($result->nid);
+ }
+
+ // Load "Clone" tasks
+ if ($type == NULL || $type == 'clone') {
+ $results = db_query(
+ "
+ SELECT ta.nid
+ FROM {hosting_task_arguments} ta
+ INNER JOIN {hosting_task} t ON ta.nid = t.nid
+ WHERE
+ name = :name AND value = :platform
+ LIMIT 1
+ ",
+ array(
+ ':platform' => $environment->platform,
+ ':name' => 'target_platform',
+ )
+ );
+ foreach ($results as $result) {
+ $task_node = node_load($result->nid);
+ $tasks[$task_node->task_type][$task_node->nid] = $task_node;
+ }
+ }
+
+ // If key is requested...
+ if ($key) {
+ foreach ($tasks as $task_type => $task_list) {
+ foreach ($task_list as $task) {
+ $new_task_list[$task->{$key}] = $task;
+ }
+ }
+ if ($key == 'nid') {
+ krsort($new_task_list);
+ }
+ else {
+ ksort($new_task_list);
+ }
+ return $new_task_list;
+ }
+ else {
+ ksort($tasks);
+ return $tasks;
+ }
+}
diff --git a/modules/devshop/devshop_permissions/devshop_permissions.features.user_permission.inc b/modules/devshop/devshop_permissions/devshop_permissions.features.user_permission.inc
new file mode 100644
index 000000000..67985b79f
--- /dev/null
+++ b/modules/devshop/devshop_permissions/devshop_permissions.features.user_permission.inc
@@ -0,0 +1,1254 @@
+ 'access content',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir account manager' => 'aegir account manager',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir client' => 'aegir client',
+ 'aegir platform manager' => 'aegir platform manager',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'node',
+ );
+
+ // Exported permission: 'access content overview'.
+ $permissions['access content overview'] = array(
+ 'name' => 'access content overview',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ ),
+ 'module' => 'node',
+ );
+
+ // Exported permission: 'access disabled sites'.
+ $permissions['access disabled sites'] = array(
+ 'name' => 'access disabled sites',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'anonymous user' => 'anonymous user',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting',
+ );
+
+ // Exported permission: 'access filemanager'.
+ $permissions['access filemanager'] = array(
+ 'name' => 'access filemanager',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_filemanager',
+ );
+
+ // Exported permission: 'access hosting logs'.
+ $permissions['access hosting logs'] = array(
+ 'name' => 'access hosting logs',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_logs',
+ );
+
+ // Exported permission: 'access hosting wizard'.
+ $permissions['access hosting wizard'] = array(
+ 'name' => 'access hosting wizard',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting',
+ );
+
+ // Exported permission: 'access task logs'.
+ $permissions['access task logs'] = array(
+ 'name' => 'access task logs',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir client' => 'aegir client',
+ 'aegir platform manager' => 'aegir platform manager',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_task',
+ );
+
+ // Exported permission: 'access test results'.
+ $permissions['access test results'] = array(
+ 'name' => 'access test results',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'devshop_testing',
+ );
+
+ // Exported permission: 'add remote aliases to projects'.
+ $permissions['add remote aliases to projects'] = array(
+ 'name' => 'add remote aliases to projects',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'devshop_remotes',
+ );
+
+ // Exported permission: 'administer SSH public keys'.
+ $permissions['administer SSH public keys'] = array(
+ 'name' => 'administer SSH public keys',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ ),
+ 'module' => 'sshkey',
+ );
+
+ // Exported permission: 'administer clients'.
+ $permissions['administer clients'] = array(
+ 'name' => 'administer clients',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir account manager' => 'aegir account manager',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir platform manager' => 'aegir platform manager',
+ ),
+ 'module' => 'hosting_client',
+ );
+
+ // Exported permission: 'administer content types'.
+ $permissions['administer content types'] = array(
+ 'name' => 'administer content types',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ ),
+ 'module' => 'node',
+ );
+
+ // Exported permission: 'administer hosting'.
+ $permissions['administer hosting'] = array(
+ 'name' => 'administer hosting',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting',
+ );
+
+ // Exported permission: 'administer hosting aliases'.
+ $permissions['administer hosting aliases'] = array(
+ 'name' => 'administer hosting aliases',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_alias',
+ );
+
+ // Exported permission: 'administer hosting backup queue'.
+ $permissions['administer hosting backup queue'] = array(
+ 'name' => 'administer hosting backup queue',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_backup_queue',
+ );
+
+ // Exported permission: 'administer hosting features'.
+ $permissions['administer hosting features'] = array(
+ 'name' => 'administer hosting features',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting',
+ );
+
+ // Exported permission: 'administer hosting queues'.
+ $permissions['administer hosting queues'] = array(
+ 'name' => 'administer hosting queues',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting',
+ );
+
+ // Exported permission: 'administer hosting settings'.
+ $permissions['administer hosting settings'] = array(
+ 'name' => 'administer hosting settings',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting',
+ );
+
+ // Exported permission: 'administer hosting site backup manager'.
+ $permissions['administer hosting site backup manager'] = array(
+ 'name' => 'administer hosting site backup manager',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_site_backup_manager',
+ );
+
+ // Exported permission: 'administer nodes'.
+ $permissions['administer nodes'] = array(
+ 'name' => 'administer nodes',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ ),
+ 'module' => 'node',
+ );
+
+ // Exported permission: 'administer platforms'.
+ $permissions['administer platforms'] = array(
+ 'name' => 'administer platforms',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir platform manager' => 'aegir platform manager',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_platform',
+ );
+
+ // Exported permission: 'administer projects'.
+ $permissions['administer projects'] = array(
+ 'name' => 'administer projects',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'devshop_projects',
+ );
+
+ // Exported permission: 'administer sites'.
+ $permissions['administer sites'] = array(
+ 'name' => 'administer sites',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir platform manager' => 'aegir platform manager',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_site',
+ );
+
+ // Exported permission: 'administer tasks'.
+ $permissions['administer tasks'] = array(
+ 'name' => 'administer tasks',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir platform manager' => 'aegir platform manager',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_task',
+ );
+
+ // Exported permission: 'bypass node access'.
+ $permissions['bypass node access'] = array(
+ 'name' => 'bypass node access',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ ),
+ 'module' => 'node',
+ );
+
+ // Exported permission: 'cancel own tasks'.
+ $permissions['cancel own tasks'] = array(
+ 'name' => 'cancel own tasks',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir client' => 'aegir client',
+ 'aegir platform manager' => 'aegir platform manager',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_task',
+ );
+
+ // Exported permission: 'change site domain name'.
+ $permissions['change site domain name'] = array(
+ 'name' => 'change site domain name',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'devshop_projects',
+ );
+
+ // Exported permission: 'configure devshop pull'.
+ $permissions['configure devshop pull'] = array(
+ 'name' => 'configure devshop pull',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'devshop_pull',
+ );
+
+ // Exported permission: 'create backup schedules'.
+ $permissions['create backup schedules'] = array(
+ 'name' => 'create backup schedules',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_backup_queue',
+ );
+
+ // Exported permission: 'create backup task'.
+ $permissions['create backup task'] = array(
+ 'name' => 'create backup task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir client' => 'aegir client',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_task',
+ );
+
+ // Exported permission: 'create backup-delete task'.
+ $permissions['create backup-delete task'] = array(
+ 'name' => 'create backup-delete task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_task',
+ );
+
+ // Exported permission: 'create client'.
+ $permissions['create client'] = array(
+ 'name' => 'create client',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir account manager' => 'aegir account manager',
+ 'aegir administrator' => 'aegir administrator',
+ ),
+ 'module' => 'hosting_client',
+ );
+
+ // Exported permission: 'create clone task'.
+ $permissions['create clone task'] = array(
+ 'name' => 'create clone task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_clone',
+ );
+
+ // Exported permission: 'create config_export task'.
+ $permissions['create config_export task'] = array(
+ 'name' => 'create config_export task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ ),
+ 'module' => 'aegir_config',
+ );
+
+ // Exported permission: 'create config_import task'.
+ $permissions['create config_import task'] = array(
+ 'name' => 'create config_import task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ ),
+ 'module' => 'aegir_config',
+ );
+
+ // Exported permission: 'create delete task'.
+ $permissions['create delete task'] = array(
+ 'name' => 'create delete task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir client' => 'aegir client',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_task',
+ );
+
+ // Exported permission: 'create devshop-delete task'.
+ $permissions['create devshop-delete task'] = array(
+ 'name' => 'create devshop-delete task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'devshop_projects',
+ );
+
+ // Exported permission: 'create devshop-deploy task'.
+ $permissions['create devshop-deploy task'] = array(
+ 'name' => 'create devshop-deploy task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'devshop_projects',
+ );
+
+ // Exported permission: 'create disable task'.
+ $permissions['create disable task'] = array(
+ 'name' => 'create disable task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir client' => 'aegir client',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_task',
+ );
+
+ // Exported permission: 'create download task'.
+ $permissions['create download task'] = array(
+ 'name' => 'create download task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'aegir_download',
+ );
+
+ // Exported permission: 'create enable task'.
+ $permissions['create enable task'] = array(
+ 'name' => 'create enable task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir client' => 'aegir client',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_task',
+ );
+
+ // Exported permission: 'create features_revert_all task'.
+ $permissions['create features_revert_all task'] = array(
+ 'name' => 'create features_revert_all task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_tasks_extra',
+ );
+
+ // Exported permission: 'create features_update_all task'.
+ $permissions['create features_update_all task'] = array(
+ 'name' => 'create features_update_all task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_tasks_extra',
+ );
+
+ // Exported permission: 'create flush_cache task'.
+ $permissions['create flush_cache task'] = array(
+ 'name' => 'create flush_cache task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_tasks_extra',
+ );
+
+ // Exported permission: 'create flush_drush_cache task'.
+ $permissions['create flush_drush_cache task'] = array(
+ 'name' => 'create flush_drush_cache task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_tasks_extra',
+ );
+
+ // Exported permission: 'create git-pull task'.
+ $permissions['create git-pull task'] = array(
+ 'name' => 'create git-pull task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir account manager' => 'aegir account manager',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir client' => 'aegir client',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_git_pull',
+ );
+
+ // Exported permission: 'create lock task'.
+ $permissions['create lock task'] = array(
+ 'name' => 'create lock task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir platform manager' => 'aegir platform manager',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_task',
+ );
+
+ // Exported permission: 'create login-reset task'.
+ $permissions['create login-reset task'] = array(
+ 'name' => 'create login-reset task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir client' => 'aegir client',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_task',
+ );
+
+ // Exported permission: 'create migrate task'.
+ $permissions['create migrate task'] = array(
+ 'name' => 'create migrate task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_migrate',
+ );
+
+ // Exported permission: 'create package'.
+ $permissions['create package'] = array(
+ 'name' => 'create package',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ ),
+ 'module' => 'hosting_package',
+ );
+
+ // Exported permission: 'create platform'.
+ $permissions['create platform'] = array(
+ 'name' => 'create platform',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir platform manager' => 'aegir platform manager',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_platform',
+ );
+
+ // Exported permission: 'create platform git-checkout task'.
+ $permissions['create platform git-checkout task'] = array(
+ 'name' => 'create platform git-checkout task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir platform manager' => 'aegir platform manager',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_git_checkout',
+ );
+
+ // Exported permission: 'create platform git-commit task'.
+ $permissions['create platform git-commit task'] = array(
+ 'name' => 'create platform git-commit task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir platform manager' => 'aegir platform manager',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_git_commit',
+ );
+
+ // Exported permission: 'create platform git-pull task'.
+ $permissions['create platform git-pull task'] = array(
+ 'name' => 'create platform git-pull task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir platform manager' => 'aegir platform manager',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_git_pull',
+ );
+
+ // Exported permission: 'create platform git-tag task'.
+ $permissions['create platform git-tag task'] = array(
+ 'name' => 'create platform git-tag task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir platform manager' => 'aegir platform manager',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_git_tag',
+ );
+
+ // Exported permission: 'create project'.
+ $permissions['create project'] = array(
+ 'name' => 'create project',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'devshop_projects',
+ );
+
+ // Exported permission: 'create project content'.
+ $permissions['create project content'] = array(
+ 'name' => 'create project content',
+ 'roles' => array(
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'node',
+ );
+
+ // Exported permission: 'create rebuild_registry task'.
+ $permissions['create rebuild_registry task'] = array(
+ 'name' => 'create rebuild_registry task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_tasks_extra',
+ );
+
+ // Exported permission: 'create restore task'.
+ $permissions['create restore task'] = array(
+ 'name' => 'create restore task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir client' => 'aegir client',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_task',
+ );
+
+ // Exported permission: 'create run_cron task'.
+ $permissions['create run_cron task'] = array(
+ 'name' => 'create run_cron task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_tasks_extra',
+ );
+
+ // Exported permission: 'create server'.
+ $permissions['create server'] = array(
+ 'name' => 'create server',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ ),
+ 'module' => 'hosting_server',
+ );
+
+ // Exported permission: 'create site'.
+ $permissions['create site'] = array(
+ 'name' => 'create site',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir client' => 'aegir client',
+ 'aegir platform manager' => 'aegir platform manager',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_site',
+ );
+
+ // Exported permission: 'create site aliases'.
+ $permissions['create site aliases'] = array(
+ 'name' => 'create site aliases',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_alias',
+ );
+
+ // Exported permission: 'create site git-checkout task'.
+ $permissions['create site git-checkout task'] = array(
+ 'name' => 'create site git-checkout task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir account manager' => 'aegir account manager',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir client' => 'aegir client',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_git_checkout',
+ );
+
+ // Exported permission: 'create site git-commit task'.
+ $permissions['create site git-commit task'] = array(
+ 'name' => 'create site git-commit task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir account manager' => 'aegir account manager',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir client' => 'aegir client',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_git_commit',
+ );
+
+ // Exported permission: 'create site git-tag task'.
+ $permissions['create site git-tag task'] = array(
+ 'name' => 'create site git-tag task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir account manager' => 'aegir account manager',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir client' => 'aegir client',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_git_tag',
+ );
+
+ // Exported permission: 'create sites on locked platforms'.
+ $permissions['create sites on locked platforms'] = array(
+ 'name' => 'create sites on locked platforms',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir platform manager' => 'aegir platform manager',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_platform',
+ );
+
+ // Exported permission: 'create sync task'.
+ $permissions['create sync task'] = array(
+ 'name' => 'create sync task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_sync',
+ );
+
+ // Exported permission: 'create test task'.
+ $permissions['create test task'] = array(
+ 'name' => 'create test task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'devshop_testing',
+ );
+
+ // Exported permission: 'create unlock task'.
+ $permissions['create unlock task'] = array(
+ 'name' => 'create unlock task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir platform manager' => 'aegir platform manager',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_task',
+ );
+
+ // Exported permission: 'create update task'.
+ $permissions['create update task'] = array(
+ 'name' => 'create update task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_tasks_extra',
+ );
+
+ // Exported permission: 'create update_drupal task'.
+ $permissions['create update_drupal task'] = array(
+ 'name' => 'create update_drupal task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ ),
+ 'module' => 'aegir_update',
+ );
+
+ // Exported permission: 'create update_translations task'.
+ $permissions['create update_translations task'] = array(
+ 'name' => 'create update_translations task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_tasks_extra',
+ );
+
+ // Exported permission: 'create verify task'.
+ $permissions['create verify task'] = array(
+ 'name' => 'create verify task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir client' => 'aegir client',
+ 'aegir platform manager' => 'aegir platform manager',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_task',
+ );
+
+ // Exported permission: 'delete any project content'.
+ $permissions['delete any project content'] = array(
+ 'name' => 'delete any project content',
+ 'roles' => array(),
+ 'module' => 'node',
+ );
+
+ // Exported permission: 'delete own client'.
+ $permissions['delete own client'] = array(
+ 'name' => 'delete own client',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ ),
+ 'module' => 'hosting_client',
+ );
+
+ // Exported permission: 'delete own project content'.
+ $permissions['delete own project content'] = array(
+ 'name' => 'delete own project content',
+ 'roles' => array(),
+ 'module' => 'node',
+ );
+
+ // Exported permission: 'delete package'.
+ $permissions['delete package'] = array(
+ 'name' => 'delete package',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ ),
+ 'module' => 'hosting_package',
+ );
+
+ // Exported permission: 'delete platform'.
+ $permissions['delete platform'] = array(
+ 'name' => 'delete platform',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir platform manager' => 'aegir platform manager',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_platform',
+ );
+
+ // Exported permission: 'delete projects'.
+ $permissions['delete projects'] = array(
+ 'name' => 'delete projects',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'devshop_projects',
+ );
+
+ // Exported permission: 'delete revisions'.
+ $permissions['delete revisions'] = array(
+ 'name' => 'delete revisions',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ ),
+ 'module' => 'node',
+ );
+
+ // Exported permission: 'delete server'.
+ $permissions['delete server'] = array(
+ 'name' => 'delete server',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ ),
+ 'module' => 'hosting_server',
+ );
+
+ // Exported permission: 'delete site'.
+ $permissions['delete site'] = array(
+ 'name' => 'delete site',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir client' => 'aegir client',
+ 'aegir platform manager' => 'aegir platform manager',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_site',
+ );
+
+ // Exported permission: 'edit any project content'.
+ $permissions['edit any project content'] = array(
+ 'name' => 'edit any project content',
+ 'roles' => array(),
+ 'module' => 'node',
+ );
+
+ // Exported permission: 'edit client uname'.
+ $permissions['edit client uname'] = array(
+ 'name' => 'edit client uname',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir account manager' => 'aegir account manager',
+ 'aegir administrator' => 'aegir administrator',
+ ),
+ 'module' => 'hosting_client',
+ );
+
+ // Exported permission: 'edit client users'.
+ $permissions['edit client users'] = array(
+ 'name' => 'edit client users',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir account manager' => 'aegir account manager',
+ 'aegir administrator' => 'aegir administrator',
+ ),
+ 'module' => 'hosting_client',
+ );
+
+ // Exported permission: 'edit own client'.
+ $permissions['edit own client'] = array(
+ 'name' => 'edit own client',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir client' => 'aegir client',
+ ),
+ 'module' => 'hosting_client',
+ );
+
+ // Exported permission: 'edit own project content'.
+ $permissions['edit own project content'] = array(
+ 'name' => 'edit own project content',
+ 'roles' => array(
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'node',
+ );
+
+ // Exported permission: 'edit package'.
+ $permissions['edit package'] = array(
+ 'name' => 'edit package',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ ),
+ 'module' => 'hosting_package',
+ );
+
+ // Exported permission: 'edit platform'.
+ $permissions['edit platform'] = array(
+ 'name' => 'edit platform',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir platform manager' => 'aegir platform manager',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_platform',
+ );
+
+ // Exported permission: 'edit project'.
+ $permissions['edit project'] = array(
+ 'name' => 'edit project',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'devshop_projects',
+ );
+
+ // Exported permission: 'edit server'.
+ $permissions['edit server'] = array(
+ 'name' => 'edit server',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ ),
+ 'module' => 'hosting_server',
+ );
+
+ // Exported permission: 'edit site'.
+ $permissions['edit site'] = array(
+ 'name' => 'edit site',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir client' => 'aegir client',
+ 'aegir platform manager' => 'aegir platform manager',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_site',
+ );
+
+ // Exported permission: 'manage any SSH public keys'.
+ $permissions['manage any SSH public keys'] = array(
+ 'name' => 'manage any SSH public keys',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ ),
+ 'module' => 'sshkey',
+ );
+
+ // Exported permission: 'manage own SSH public keys'.
+ $permissions['manage own SSH public keys'] = array(
+ 'name' => 'manage own SSH public keys',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'sshkey',
+ );
+
+ // Exported permission: 'manage site HTTPS settings'.
+ $permissions['manage site HTTPS settings'] = array(
+ 'name' => 'manage site HTTPS settings',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir client' => 'aegir client',
+ ),
+ 'module' => 'hosting_https',
+ );
+
+ // Exported permission: 'remove remote aliases from projects'.
+ $permissions['remove remote aliases from projects'] = array(
+ 'name' => 'remove remote aliases from projects',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'devshop_remotes',
+ );
+
+ // Exported permission: 'retry failed tasks'.
+ $permissions['retry failed tasks'] = array(
+ 'name' => 'retry failed tasks',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir platform manager' => 'aegir platform manager',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_task',
+ );
+
+ // Exported permission: 'revert revisions'.
+ $permissions['revert revisions'] = array(
+ 'name' => 'revert revisions',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ ),
+ 'module' => 'node',
+ );
+
+ // Exported permission: 'update status of tasks'.
+ $permissions['update status of tasks'] = array(
+ 'name' => 'update status of tasks',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_task',
+ );
+
+ // Exported permission: 'view any SSH public keys'.
+ $permissions['view any SSH public keys'] = array(
+ 'name' => 'view any SSH public keys',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ ),
+ 'module' => 'sshkey',
+ );
+
+ // Exported permission: 'view client'.
+ $permissions['view client'] = array(
+ 'name' => 'view client',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir account manager' => 'aegir account manager',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir client' => 'aegir client',
+ ),
+ 'module' => 'hosting_client',
+ );
+
+ // Exported permission: 'view locked platforms'.
+ $permissions['view locked platforms'] = array(
+ 'name' => 'view locked platforms',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir platform manager' => 'aegir platform manager',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_platform',
+ );
+
+ // Exported permission: 'view own SSH public keys'.
+ $permissions['view own SSH public keys'] = array(
+ 'name' => 'view own SSH public keys',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'sshkey',
+ );
+
+ // Exported permission: 'view own tasks'.
+ $permissions['view own tasks'] = array(
+ 'name' => 'view own tasks',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir client' => 'aegir client',
+ 'aegir platform manager' => 'aegir platform manager',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_task',
+ );
+
+ // Exported permission: 'view own unpublished content'.
+ $permissions['view own unpublished content'] = array(
+ 'name' => 'view own unpublished content',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'node',
+ );
+
+ // Exported permission: 'view package'.
+ $permissions['view package'] = array(
+ 'name' => 'view package',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir client' => 'aegir client',
+ 'aegir platform manager' => 'aegir platform manager',
+ ),
+ 'module' => 'hosting_package',
+ );
+
+ // Exported permission: 'view platform'.
+ $permissions['view platform'] = array(
+ 'name' => 'view platform',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir client' => 'aegir client',
+ 'aegir platform manager' => 'aegir platform manager',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_platform',
+ );
+
+ // Exported permission: 'view project'.
+ $permissions['view project'] = array(
+ 'name' => 'view project',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'devshop_projects',
+ );
+
+ // Exported permission: 'view projects'.
+ $permissions['view projects'] = array(
+ 'name' => 'view projects',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'devshop_projects',
+ );
+
+ // Exported permission: 'view revisions'.
+ $permissions['view revisions'] = array(
+ 'name' => 'view revisions',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir client' => 'aegir client',
+ 'aegir platform manager' => 'aegir platform manager',
+ ),
+ 'module' => 'node',
+ );
+
+ // Exported permission: 'view server'.
+ $permissions['view server'] = array(
+ 'name' => 'view server',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_server',
+ );
+
+ // Exported permission: 'view site'.
+ $permissions['view site'] = array(
+ 'name' => 'view site',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir account manager' => 'aegir account manager',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir client' => 'aegir client',
+ 'aegir platform manager' => 'aegir platform manager',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_site',
+ );
+
+ // Exported permission: 'view task'.
+ $permissions['view task'] = array(
+ 'name' => 'view task',
+ 'roles' => array(
+ 'administrator' => 'administrator',
+ 'aegir administrator' => 'aegir administrator',
+ 'aegir client' => 'aegir client',
+ 'aegir platform manager' => 'aegir platform manager',
+ 'authenticated user' => 'authenticated user',
+ ),
+ 'module' => 'hosting_task',
+ );
+
+ return $permissions;
+}
diff --git a/modules/devshop/devshop_permissions/devshop_permissions.info b/modules/devshop/devshop_permissions/devshop_permissions.info
new file mode 100644
index 000000000..a3c0aba29
--- /dev/null
+++ b/modules/devshop/devshop_permissions/devshop_permissions.info
@@ -0,0 +1,150 @@
+name = DevShop Permissions
+description = Default user permissions for devshop.
+core = 7.x
+package = Features
+dependencies[] = aegir_config
+dependencies[] = aegir_download
+dependencies[] = aegir_update
+dependencies[] = devshop_projects
+dependencies[] = devshop_pull
+dependencies[] = devshop_remotes
+dependencies[] = devshop_testing
+dependencies[] = features
+dependencies[] = hosting
+dependencies[] = hosting_alias
+dependencies[] = hosting_backup_queue
+dependencies[] = hosting_client
+dependencies[] = hosting_clone
+dependencies[] = hosting_filemanager
+dependencies[] = hosting_git_checkout
+dependencies[] = hosting_git_commit
+dependencies[] = hosting_git_pull
+dependencies[] = hosting_git_tag
+dependencies[] = hosting_https
+dependencies[] = hosting_logs
+dependencies[] = hosting_migrate
+dependencies[] = hosting_package
+dependencies[] = hosting_platform
+dependencies[] = hosting_server
+dependencies[] = hosting_site
+dependencies[] = hosting_site_backup_manager
+dependencies[] = hosting_sync
+dependencies[] = hosting_task
+dependencies[] = hosting_tasks_extra
+dependencies[] = node
+dependencies[] = sshkey
+features[features_api][] = api:2
+features[user_permission][] = access content
+features[user_permission][] = access content overview
+features[user_permission][] = access disabled sites
+features[user_permission][] = access filemanager
+features[user_permission][] = access hosting logs
+features[user_permission][] = access hosting wizard
+features[user_permission][] = access task logs
+features[user_permission][] = access test results
+features[user_permission][] = add remote aliases to projects
+features[user_permission][] = administer SSH public keys
+features[user_permission][] = administer clients
+features[user_permission][] = administer content types
+features[user_permission][] = administer hosting
+features[user_permission][] = administer hosting aliases
+features[user_permission][] = administer hosting backup queue
+features[user_permission][] = administer hosting features
+features[user_permission][] = administer hosting queues
+features[user_permission][] = administer hosting settings
+features[user_permission][] = administer hosting site backup manager
+features[user_permission][] = administer nodes
+features[user_permission][] = administer platforms
+features[user_permission][] = administer projects
+features[user_permission][] = administer sites
+features[user_permission][] = administer tasks
+features[user_permission][] = bypass node access
+features[user_permission][] = cancel own tasks
+features[user_permission][] = change site domain name
+features[user_permission][] = configure devshop pull
+features[user_permission][] = create backup schedules
+features[user_permission][] = create backup task
+features[user_permission][] = create backup-delete task
+features[user_permission][] = create client
+features[user_permission][] = create clone task
+features[user_permission][] = create config_export task
+features[user_permission][] = create config_import task
+features[user_permission][] = create delete task
+features[user_permission][] = create devshop-delete task
+features[user_permission][] = create devshop-deploy task
+features[user_permission][] = create disable task
+features[user_permission][] = create download task
+features[user_permission][] = create enable task
+features[user_permission][] = create features_revert_all task
+features[user_permission][] = create features_update_all task
+features[user_permission][] = create flush_cache task
+features[user_permission][] = create flush_drush_cache task
+features[user_permission][] = create git-pull task
+features[user_permission][] = create lock task
+features[user_permission][] = create login-reset task
+features[user_permission][] = create migrate task
+features[user_permission][] = create package
+features[user_permission][] = create platform
+features[user_permission][] = create platform git-checkout task
+features[user_permission][] = create platform git-commit task
+features[user_permission][] = create platform git-pull task
+features[user_permission][] = create platform git-tag task
+features[user_permission][] = create project
+features[user_permission][] = create project content
+features[user_permission][] = create rebuild_registry task
+features[user_permission][] = create restore task
+features[user_permission][] = create run_cron task
+features[user_permission][] = create server
+features[user_permission][] = create site
+features[user_permission][] = create site aliases
+features[user_permission][] = create site git-checkout task
+features[user_permission][] = create site git-commit task
+features[user_permission][] = create site git-tag task
+features[user_permission][] = create sites on locked platforms
+features[user_permission][] = create sync task
+features[user_permission][] = create test task
+features[user_permission][] = create unlock task
+features[user_permission][] = create update task
+features[user_permission][] = create update_drupal task
+features[user_permission][] = create update_translations task
+features[user_permission][] = create verify task
+features[user_permission][] = delete any project content
+features[user_permission][] = delete own client
+features[user_permission][] = delete own project content
+features[user_permission][] = delete package
+features[user_permission][] = delete platform
+features[user_permission][] = delete projects
+features[user_permission][] = delete revisions
+features[user_permission][] = delete server
+features[user_permission][] = delete site
+features[user_permission][] = edit any project content
+features[user_permission][] = edit client uname
+features[user_permission][] = edit client users
+features[user_permission][] = edit own client
+features[user_permission][] = edit own project content
+features[user_permission][] = edit package
+features[user_permission][] = edit platform
+features[user_permission][] = edit project
+features[user_permission][] = edit server
+features[user_permission][] = edit site
+features[user_permission][] = manage any SSH public keys
+features[user_permission][] = manage own SSH public keys
+features[user_permission][] = manage site HTTPS settings
+features[user_permission][] = remove remote aliases from projects
+features[user_permission][] = retry failed tasks
+features[user_permission][] = revert revisions
+features[user_permission][] = update status of tasks
+features[user_permission][] = view any SSH public keys
+features[user_permission][] = view client
+features[user_permission][] = view locked platforms
+features[user_permission][] = view own SSH public keys
+features[user_permission][] = view own tasks
+features[user_permission][] = view own unpublished content
+features[user_permission][] = view package
+features[user_permission][] = view platform
+features[user_permission][] = view project
+features[user_permission][] = view projects
+features[user_permission][] = view revisions
+features[user_permission][] = view server
+features[user_permission][] = view site
+features[user_permission][] = view task
diff --git a/modules/devshop/devshop_permissions/devshop_permissions.module b/modules/devshop/devshop_permissions/devshop_permissions.module
new file mode 100644
index 000000000..5f0757208
--- /dev/null
+++ b/modules/devshop/devshop_permissions/devshop_permissions.module
@@ -0,0 +1,3 @@
+tagline = t('Hosted by DevShop');
+}
\ No newline at end of file
diff --git a/modules/devshop/devshop_projects/devshop_projects.drush.inc b/modules/devshop/devshop_projects/devshop_projects.drush.inc
new file mode 100644
index 000000000..bcb2af4fd
--- /dev/null
+++ b/modules/devshop/devshop_projects/devshop_projects.drush.inc
@@ -0,0 +1,218 @@
+ref;
+ if (($ref->type == 'site' || $ref->type == 'platform') && isset($ref->environment)) {
+ $ref->environment->last_task = $task->nid;
+ devshop_environment_save_last_task($ref->environment);
+ }
+}
+
+/**
+ * Implements drush_HOOK_pre_COMMAND()
+ *
+ * This runs for each task during the command
+ * drush @hostmaster hosting-tasks
+ *
+ * NOTE: This ONLY runs when being called from a hostmaster task.
+ * This hook should ONLY be used to pass options from a hostmaster task form to
+ * the $task object, or if you don't need this functionality from the command
+ * line.
+ */
+function drush_devshop_projects_pre_hosting_task()
+{
+
+ $task =& drush_get_context('HOSTING_TASK');
+
+ // For all tasks
+ if ($task->ref->type == 'site' || $task->ref->type == 'platform' || $task->ref->type == 'project') {
+ $drush_aliases = new Provision_Config_ProjectAliases($task->ref->project->name, $task->ref->project);
+ }
+
+ // Only write drush aliases if project name was found.
+ if (!empty($drush_aliases->data['name'])) {
+ $drush_aliases->write();
+ drush_log(dt('Drush aliases written for %name project.', [
+ '%name' => $drush_aliases->data['name'],
+ ]), 'p_log');
+ }
+
+
+ // Passing options for Download task. This should go in aegir_download.drush.inc but I can't get that hook working right now.
+ if ($task->task_type == 'download') {
+ drush_log('[AEGIR DEVSHOP_PROJECTS] Download package enabled...', 'ok');
+
+ $task->options['packages'] = $task->task_args['packages'];
+ $task->options['commit'] = $task->task_args['update'];
+ $task->options['message'] = $task->task_args['message'];
+ $task->options['update'] = $task->task_args['update'];
+ }
+
+ // Verify Platform
+ // Here is where we hook in and clone the site
+ // @TODO: This can be removed once hosting_git migration is complete.
+// if ($task->ref->type == 'platform' && $task->task_type == 'verify' && !empty($task->ref->project->git_url)) {
+// drush_devshop_platform_verify();
+// }
+
+ // Pull
+ if ($task->ref->type == 'project' && $task->task_type == 'devshop-pull') {
+ $task->args['environments'] = $task->task_args['environments'];
+ $task->options['update'] = $task->task_args['update'];
+ $task->options['revert'] = !empty($task->task_args['revert']);
+ $task->options['cache'] = $task->task_args['cache'];
+ $task->options['force'] = FALSE;
+ }
+
+ // Deploy
+ if ($task->ref->type == 'site' && $task->task_type == 'devshop-deploy') {
+
+ // The git_ref is the only argument
+ $task->args['git_ref'] = $task->task_args['git_ref'];
+
+ // Just load in all of the task_args as options(they come from the task form.)
+ foreach ($task->task_args as $arg => $value) {
+ $task->options[$arg] = $value;
+ }
+ }
+
+ // Run tests
+ if ($task->ref->type == 'site' && $task->task_type == 'test') {
+
+ $task->options['tests-to-run'] = $task->task_args['tests_to_run'];
+ $task->options['test-type'] = $task->task_args['test_type'];
+ }
+
+ // On every task triggered by devshop, set this option.
+ $task->options['is-devshop'] = TRUE;
+}
+
+/**
+ * Implementation of hook_post_hosting_TASK_TYPE_task()
+ * for Verify tasks.
+ *
+ * Provides the "Fork environment" feature: Checks out a new branch and pushes it.
+ */
+function devshop_projects_post_hosting_verify_task($task, $data) {
+ if ($task->ref->type == 'platform') {
+
+ // Fork Environment:
+ // If this is the first verification, and branch needs to be created (for "fork environment")
+ $platform = $task->ref;
+ if (!$platform->verified && !empty($platform->environment->settings->branch_to_fork)) {
+ provision_process("git checkout -b {$platform->environment->git_ref}", $platform->repo_path, dt('Creating a new branch.'));
+
+ // Push the branch
+ provision_process("git push -u origin {$platform->environment->git_ref}", $platform->repo_path, dt('Pushing new branch.'));
+ }
+ }
+}
+
+/**
+ * Implements hook_post_hosting_TASK_TYPE_task() for delete tasks().
+ */
+function devshop_projects_post_hosting_delete_task($task, $data) {
+
+ // When a project is deleted...
+ if ($task->ref->type == 'project') {
+ // Queue site deletion for each environment.
+ $project = $task->ref->project;
+ foreach ($project->environments as $environment) {
+ // If site exists, trigger deletion.
+ if ($environment->site) {
+ hosting_add_task($environment->site, 'delete');
+ }
+ // If platform exists, trigger deletion.
+ elseif ($environment->platform) {
+ hosting_add_task($environment->platform, 'delete', array('force' => 1));
+ }
+ }
+
+ // @TODO: Should probably add our own status column
+ // Unpublish the project node.
+ $task->ref->status = 0;
+ $task->ref->no_verify = TRUE;
+ node_save($task->ref);
+ }
+
+ // When a site is deleted (if it is in a project, delete the platform it is on.
+ // @TODO: Check for another site on this platform?
+ if ($task->ref->type == 'site' && !empty($task->ref->project)) {
+ // We trigger platform deletion here.
+ hosting_add_task($task->ref->platform, 'delete');
+ }
+
+ // When a platform is deleted (if it is in a project), delete the environment record.
+ if ($task->ref->type == 'platform' && !empty($task->ref->project)) {
+ db_delete('hosting_devshop_project_environment')
+ ->condition('project_nid', $task->ref->project->nid)
+ ->condition('platform', $task->ref->platform)
+ ->execute();
+
+ // If drupal root is not repo root, delete folder at code_path.
+ $repo_path = str_replace($task->ref->project->drupal_path, '', $task->ref->publish_path);
+ if (file_exists($repo_path) ) {
+ _provision_recursive_delete($repo_path);
+ }
+ }
+
+ // When a platform is deleted, if it is the last in the project,
+ // and the project has been unpublished, delete the directory.
+ if ($task->ref->type == 'platform' && !empty($task->ref->project)) {
+ if ($task->ref->project->status == 0 && count($task->ref->project->environments) == 1) {
+ // Delete the project folder.
+ exec("rm -rf {$project->code_path}");
+ }
+ }
+}
+
+/**
+ * Implements hook_post_hosting_TASK_TYPE_task() for install.
+ */
+function devshop_projects_post_hosting_install_task($task, $data) {
+
+ // Queue up a verification of the site.
+ hosting_add_task($task->ref->nid, 'verify');
+
+}
+
+ /**
+ * Implements hook_post_hosting_TASK_TYPE_task() for devshop deploy tasks().
+ */
+function devshop_projects_post_hosting_devshop_deploy_task($task, $data) {
+
+ // Save the deployed git ref to the environment record.
+ // Doing this post deploy ensures it was actually checked out.
+ drush_log('[DEVSHOP] Environment Deployed. Saving new git ref to project settings.');
+
+ $git_ref = trim(str_replace('refs/heads/', '', shell_exec("cd {$task->ref->environment->repo_path}; git describe --tags --exact-match || git symbolic-ref -q HEAD")));;
+
+ $args = array(
+ ':git' => $git_ref,
+ ':site' => $task->ref->environment->site,
+ );
+
+ if (db_query('UPDATE {hosting_git} SET git_ref = :git WHERE nid = :site', $args)) {
+ drush_log("[DEVSHOP] Git reference {$git_ref} saved to {$task->ref->environment->name} environment record.");
+ }
+ else {
+ return drush_set_error('[DEVSHOP] Environment update failed! Unable to save git ref to hosting_devshop_project_environment table.');
+ }
+
+ // Queue up a verification of the platform.
+ hosting_add_task($task->ref->environment->platform, 'verify');
+
+ // Queue up a verification of the site.
+ hosting_add_task($task->ref->nid, 'verify');
+}
diff --git a/modules/devshop/devshop_projects/devshop_projects.info b/modules/devshop/devshop_projects/devshop_projects.info
new file mode 100644
index 000000000..38f30d85a
--- /dev/null
+++ b/modules/devshop/devshop_projects/devshop_projects.info
@@ -0,0 +1,29 @@
+name = DevShop Projects
+description = A DevShop module to sites/platforms together into projects.
+core = 7.x
+package = DevShop
+
+files[] = inc/create/create.inc
+files[] = inc/create/step-1.inc
+files[] = inc/create/step-2.inc
+files[] = inc/create/step-3.inc
+files[] = inc/create/step-4.inc
+files[] = inc/admin.inc
+files[] = inc/create-wizard.inc
+files[] = inc/forms.inc
+files[] = inc/logs.inc
+files[] = inc/nodes.inc
+files[] = inc/queue.inc
+files[] = inc/tasks-ajax.inc
+files[] = inc/ui.inc
+files[] = tasks/commit.inc
+files[] = tasks/create.inc
+files[] = tasks/deploy.inc
+files[] = tasks/pull.inc
+files[] = tasks/tasks.inc
+dependencies[] = hosting
+dependencies[] = hosting_alias
+dependencies[] = hosting_package
+dependencies[] = hosting_git
+dependencies[] = hosting_git_pull
+dependencies[] = ctools
diff --git a/modules/devshop/devshop_projects/devshop_projects.install b/modules/devshop/devshop_projects/devshop_projects.install
new file mode 100644
index 000000000..cc605b868
--- /dev/null
+++ b/modules/devshop/devshop_projects/devshop_projects.install
@@ -0,0 +1,478 @@
+ 7300,
+ );
+ return $dependencies;
+}
+
+/**
+ * Implements hook_install()
+ */
+function devshop_projects_install() {
+
+ // Push devshop_projects's system weight to 1.
+ db_update('system')
+ ->fields(array(
+ 'weight' => 1
+ ))
+ ->condition('name', 'devshop_projects')
+ ->execute();
+}
+
+/**
+ * Implements hook_schema().
+ */
+function devshop_projects_schema() {
+ $schema['hosting_devshop_project'] = array(
+ 'fields' => array(
+ 'nid' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => 'Project/Node ID.',
+ ),
+ 'git_url' => array(
+ 'type' => 'text',
+ 'size' => 'big',
+ 'not null' => FALSE,
+ ),
+ 'code_path' => array(
+ 'type' => 'text',
+ 'size' => 'big',
+ 'not null' => FALSE,
+ ),
+ 'drupal_path' => array(
+ 'type' => 'text',
+ 'size' => 'big',
+ 'not null' => FALSE,
+ ),
+ 'base_url' => array(
+ 'type' => 'text',
+ 'size' => 'big',
+ 'not null' => FALSE,
+ ),
+ 'install_profile' => array(
+ 'type' => 'text',
+ 'size' => 'big',
+ 'not null' => FALSE,
+ ),
+ 'settings' => array(
+ 'type' => 'text',
+ 'not null' => FALSE,
+ 'size' => 'big',
+ 'description' => 'A serialized array of settings for this project.',
+ ),
+ ),
+ 'primary key' => array('nid'),
+ );
+
+ // Table for tracking environments
+ $schema['hosting_devshop_project_environment'] = array(
+ 'fields' => array(
+ 'project_nid' => array(
+ 'type' => 'int',
+ 'unsigned' => TRUE,
+ 'not null' => TRUE,
+ 'default' => 0,
+ 'description' => "The project's Node ID.",
+ ),
+ 'name' => array(
+ 'type' => 'varchar',
+ 'not null' => TRUE,
+ 'length' => 64,
+ 'default' => '',
+ 'description' => 'Environment name.',
+ ),
+ 'site' => array(
+ 'type' => 'int',
+ 'not null' => FALSE,
+ 'default' => 0,
+ 'unsigned' => TRUE,
+ 'description' => 'The node ID of the site for this environment.',
+ ),
+ 'platform' => array(
+ 'type' => 'int',
+ 'not null' => FALSE,
+ 'default' => 0,
+ 'unsigned' => TRUE,
+ 'description' => 'The node ID of the platform for this environment.',
+ ),
+ 'settings' => array(
+ 'type' => 'text',
+ 'not null' => FALSE,
+ 'size' => 'big',
+ 'description' => 'A serialized array of settings for this environment.',
+ ),
+ 'last_task' => array(
+ 'type' => 'text',
+ 'not null' => FALSE,
+ 'size' => 'big',
+ 'description' => 'The NID of the last task that should be displayed on an environment.',
+ ),
+ ),
+ 'indexes' => array(
+ 'project_environment' => array('project_nid', 'name'),
+ ),
+ );
+ return $schema;
+}
+
+/**
+ * Add the "data" column to projects.
+ */
+function devshop_projects_update_1() {
+ $ret = array();
+ $ret[] = update_sql("ALTER TABLE {hosting_devshop_project} " .
+ "ADD COLUMN data longtext NOT NULL default ''");
+ return $ret;
+}
+
+/*
+ * Update 2: Delete rows in the hosting_devshop_project_objects table
+ * that the node pointed to by object nid no longer exists.
+ */
+function devshop_projects_update_2() {
+ $ret = array();
+ $query = db_query("SELECT object_nid " .
+ "FROM {hosting_devshop_project_object}");
+
+ while($proj = db_fetch_object($query)) {
+ $count = db_result(db_query("SELECT COUNT(*) FROM {node} " .
+ "WHERE nid = %d", $proj->object_nid));
+ if ($count != 1) {
+ $ret[] = update_sql('DELETE FROM {hosting_devshop_project_object} ' .
+ 'WHERE object_nid = %d', $proj->object_nid);
+ }
+ }
+
+ return $ret;
+}
+
+/**
+ * Adds git_branch column to hosting_devshop_project_objects table.
+ */
+function devshop_projects_update_3() {
+ $ret = array();
+ db_add_field($ret, 'hosting_devshop_project_object', 'git_branch', array('type' => 'varchar', 'length' => 16, 'not null' => FALSE));
+ return $ret;
+}
+
+/**
+ * Changes env_type to environment in {hosting_devshop_project_object}.
+ */
+function devshop_projects_update_4() {
+ $ret = array();
+ $schema = devshop_projects_schema();
+ $spec = $schema['hosting_devshop_project_object']['fields']['environment'];
+ db_change_field($ret, 'hosting_devshop_project_object', 'env_type', 'environment', $spec);
+ return $ret;
+}
+
+/**
+ * Makes "git_branch" field larger.
+ */
+function devshop_projects_update_5() {
+ $ret = array();
+ $ret[] = update_sql("ALTER TABLE {hosting_devshop_project_object} CHANGE git_branch git_branch VARCHAR(128) NULL");
+ return $ret;
+}
+
+/**
+ * Adds drupal_path column to hosting_devshop_project table.
+ */
+function devshop_projects_update_6() {
+ $ret = array();
+ db_add_field($ret, 'hosting_devshop_project', 'drupal_path', array('type' => 'text', 'size' => 'big', 'not null' => FALSE));
+ return $ret;
+}
+
+/**
+ * Adds drupal_path column to hosting_devshop_project table.
+ */
+function devshop_projects_update_7() {
+ $ret = array();
+ db_add_field($ret, 'hosting_devshop_project_object', 'drupal_path', array('type' => 'text', 'size' => 'big', 'not null' => FALSE));
+ return $ret;
+}
+
+/**
+ * Makes "environment" field larger.
+ */
+function devshop_projects_update_8() {
+ $ret = array();
+ $ret[] = update_sql("ALTER TABLE {hosting_devshop_project_object} CHANGE environment environment VARCHAR(64) NOT NULL");
+
+ return $ret;
+}
+
+/**
+ * Add clone_nid column to hosting_devshop_project_object.
+ */
+function devshop_projects_update_9() {
+ $ret = array();
+ $ret[] = db_add_field($ret, 'hosting_devshop_project_object', 'clone_nid', array('type' => 'int', 'not null' => FALSE, 'default' => 0, 'unsigned' => TRUE));
+
+ return $ret;
+}
+
+/**
+ * 6.x-1.9-beta4 -^
+ *
+ * 6.x-1.9-rc1 -v (FUTURE RELEASE)
+ */
+
+/**
+ * Remove live_domain from hosting_devshop_project schema.
+ */
+function devshop_projects_update_6000() {
+ $ret = array();
+ if (db_column_exists('hosting_devshop_project', 'live_domain')){
+ $ret[] = db_drop_field($ret, 'hosting_devshop_project', 'live_domain');
+ }
+ return $ret;
+}
+
+/**
+ * Create new hosting_devshop_environment table.
+ */
+function devshop_projects_update_6001() {
+ // Create only the new table
+ // Clone of drupal_install_schema('devshop_projects');
+ $module = 'devshop_projects';
+ $name = 'hosting_devshop_project_environment';
+
+ $schema = drupal_get_schema_unprocessed($module);
+ _drupal_initialize_schema($module, $schema);
+
+ $ret = array();
+ db_create_table($ret, $name, $schema['hosting_devshop_project_environment']);
+ return $ret;
+}
+
+/**
+ * Enable hosting_alias module to allow custom domains on sites.
+ */
+function devshop_projects_update_6002() {
+ module_enable(array('hosting_alias'));
+ return array();
+}
+
+/**
+ * Remove legacy `hosting_devshop_project_object` table.
+ */
+function devshop_projects_update_6003(){
+ $ret = array();
+ db_drop_table($ret, 'hosting_devshop_project_object');
+ return $ret;
+}
+
+/**
+ * Change "data" column to "settings" column.
+ */
+function devshop_projects_update_6004(){
+ $ret = array();
+ db_change_column($ret, 'hosting_devshop_project', 'data', 'settings', 'text', array(
+ 'not null' => FALSE,
+ 'size' => 'big',
+ 'description' => 'A serialized array of settings for this project.',
+ ));
+ return $ret;
+}
+
+/**
+ * Turn on the new Boots theme.
+ * Uncomment once the theme is ready.
+function devshop_projects_update_600X() {
+ variable_set('theme_default','boots');
+ return array();
+}
+ */
+
+/**
+ * Add "last_task" column for environments.
+ */
+function devshop_projects_update_6008(){
+ $schema = devshop_projects_schema();
+ $ret = array();
+ $ret[] = db_add_field(
+ $ret,
+ 'hosting_devshop_project_environment',
+ 'last_task', $schema['hosting_devshop_project_environment']['fields']['last_task']
+ );
+ return $ret;
+}
+
+/**
+ * Update all environments with their last_task();
+ */
+function devshop_projects_update_6009 () {
+
+ // Lookup all environments
+ $query = db_query('SELECT * FROM {hosting_devshop_project_environment}');
+ while ($result = db_fetch_object($query)) {
+ $site = node_load($result->site);
+ $result = db_fetch_object(db_query("SELECT t.nid, t.vid FROM hosting_task t INNER JOIN node n ON n.vid = t.vid WHERE rid = %d GROUP BY t.nid ORDER BY vid DESC", $site->nid));
+
+ $task = node_load($result->nid);
+ $site->environment->last_task = array(
+ 'status' => $task->task_status,
+ 'type' => $task->task_type,
+ 'nid' => $task->nid,
+ );
+ devshop_environment_save_last_task($site->environment);
+ }
+}
+
+
+/**
+ * Reset all environment's "last task" to the NID.
+ */
+function devshop_projects_update_6010 () {
+
+ // Lookup all environments
+ $query = db_query('SELECT * FROM {hosting_devshop_project_environment}');
+ while ($result = db_fetch_object($query)) {
+ $site = node_load($result->site);
+ $result = db_fetch_object(db_query("SELECT t.nid, t.vid FROM hosting_task t INNER JOIN node n ON n.vid = t.vid WHERE rid = %d GROUP BY t.nid ORDER BY vid DESC", $site->nid));
+ $site->environment->last_task = $result->nid;
+ devshop_environment_save_last_task($site->environment);
+ }
+}
+
+/**
+ * Reset all environment's "last task" to the NID, again.
+ */
+function devshop_projects_update_6011 () {
+ devshop_reset_last_tasks();
+}
+
+/**
+ * Remove the menu form from the project node form.
+ */
+function devshop_projects_update_7000 () {
+ variable_set('menu_options_project', array());
+}
+
+/**
+ * Remove the menu form from the site node form.
+ */
+function devshop_projects_update_7001 () {
+ variable_set('menu_options_site', array());
+}
+
+// @TODO: This breaks upgrades. Figure out why and in include it in an upcoming release.
+///**
+// * Enable devshop_testing.
+// */
+//function devshop_projects_update_7002 () {
+// module_enable(array(
+// 'devshop_testing',
+// ));
+//}
+
+/**
+ * Fully resetting project and site node menu settings.
+ */
+function devshop_projects_update_7003 () {
+ variable_set('menu_options_site', '');
+ variable_set('menu_options_project', '');
+}
+
+/**
+ * Enable Hosting Git and Hosting Git Checkout.
+ */
+function devshop_projects_update_7004 () {
+ module_enable(array(
+ 'hosting_git',
+ 'hosting_git_pull',
+ ));
+}
+
+/**
+ * Save git_ref data to hosting_git table and drop the devshop environment's git_ref field.
+ */
+function devshop_projects_update_7005 () {
+ $environments = db_query('SELECT * FROM {hosting_devshop_project_environment} e LEFT JOIN {hosting_devshop_project} p ON p.nid = e.project_nid');
+ foreach ($environments as $environment) {
+ if ($environment->platform) {
+ db_merge('hosting_git')
+ ->key(array('nid' => $environment->platform))
+ ->fields(array(
+ 'repo_url' => $environment->git_url,
+ 'git_ref' => $environment->git_ref,
+ ))
+ ->execute();
+ drupal_set_message(t("Updated Platform $environment->platform with $environment->git_url and $environment->git_ref"));
+ }
+ else {
+ drupal_set_message(t("No Platform found for environment $environment->name for site $environment->site."), 'warning');
+ }
+ }
+
+ if (db_drop_field('hosting_devshop_project_environment', 'git_ref')) {
+ drupal_set_message(t("Git ref information has been migrated and the field has been removed."));
+ }
+ else {
+ drupal_set_message(t("Unable to remove git_ref field from hosting_devshop_project_environment table."), 'error');
+ }
+}
+
+/**
+ * Reduce the character limit for bootstrap to turn form element descriptions into tooltips.
+ */
+function devshop_projects_update_7007 () {
+ // Get the settings
+ $settings = variable_get('theme_boots_settings', array());
+
+ // Set the variable
+ $settings['bootstrap_forms_smart_descriptions_limit'] = 40;
+
+ // Save our settings
+ variable_set('theme_boots_settings', $settings);
+}
+
+/**
+ * Set weight of devshop_projects module to 1 so it loads alters after hosting_git.
+ */
+function devshop_projects_update_7008 () {
+
+ // Push devshop_projects's system weight to 1.
+ db_update('system')
+ ->fields(array(
+ 'weight' => 1
+ ))
+ ->condition('name', 'devshop_projects')
+ ->execute();
+
+}
+
+/**
+ * Update hosting contexts for project node to ensure they all begin with "project_".
+ */
+function devshop_projects_update_7009 () {
+ $query = "UPDATE hosting_context c JOIN node n ON n.nid=c.nid SET c.name = CONCAT('project_', name) WHERE n.type='project' AND c.name NOT LIKE 'project_'";
+ if (!db_query($query)) {
+ drupal_set_message(t('Something went wrong when running a query to update hosting_context table to ensure all projects are prefixed with "project_":') . $query);
+ }
+}
\ No newline at end of file
diff --git a/modules/devshop/devshop_projects/devshop_projects.module b/modules/devshop/devshop_projects/devshop_projects.module
new file mode 100644
index 000000000..886ccb991
--- /dev/null
+++ b/modules/devshop/devshop_projects/devshop_projects.module
@@ -0,0 +1,903 @@
+step)) {
+
+ // Make sure we don't show this message twice.
+ $message = t('You have an unfinished project. You should !link.', array(
+ '!link' => l(t('finish creating your project'), 'projects/add'),
+ ));
+
+ $show_message = TRUE;
+ $messages = drupal_get_messages('status');
+
+ if (isset($messages['status'])) {
+ foreach ($messages['status'] as $m) {
+ if (strpos($m, $message) !== FALSE) {
+ $show_message = FALSE;
+ }
+ else {
+ drupal_set_message($m);
+ }
+ }
+ }
+
+ if ($show_message) {
+ drupal_set_message($message);
+ }
+ }
+ }
+
+ // If on the projects page, or a project or site node...
+ if (arg(0) == 'node' && is_numeric(arg(1))) {
+ $node = node_load(arg(1));
+ if ($node->type == 'project' || $node->type == 'site' || $node->type == 'task') {
+ drupal_add_js(drupal_get_path('module', 'devshop_projects') . '/inc/task-ajax.js');
+
+ if ($node->type == 'project') {
+ drupal_add_js(array('devshopProject' => arg(1)), 'setting');
+ }
+ if ($node->type == 'task') {
+ drupal_add_js(array('devshopTask' => arg(1)), 'setting');
+ }
+ }
+ }
+ // If on the projects list page...
+ else {
+ drupal_add_js(drupal_get_path('module', 'devshop_projects') . '/inc/task-ajax.js');
+ }
+}
+
+/**
+ * Implements hook_permission().
+ *
+ * Since we have a node type, "create project content permission is
+ * automatically added by Drupal
+ */
+function devshop_projects_permission() {
+ return array(
+ 'view projects' => array(
+ 'title' => t('view projects'),
+ 'description' => t('Access the projects page.'),
+ ),
+ 'create devshop-delete task' => array(
+ 'title' => t('create devshop-delete task'),
+ 'description' => t('Create "devshop-delete" task.'),
+ ),
+ 'create devshop-deploy task' => array(
+ 'title' => t('create devshop-deploy task'),
+ 'description' => t('Create "devshop-deploy" task.'),
+ ),
+ 'create project' => array(
+ 'title' => t('create project'),
+ 'description' => t('Create new DevShop projects.'),
+ ),
+ 'view project' => array(
+ 'title' => t('view project'),
+ 'description' => t('Access DevShop project dashboards.'),
+ ),
+ 'edit project' => array(
+ 'title' => t('edit project'),
+ 'description' => t('Edit DevShop projects.'),
+ ),
+ 'delete projects' => array(
+ 'title' => t('delete projects'),
+ 'description' => t('Delete DevShop projects.'),
+ ),
+ 'administer projects' => array(
+ 'title' => t('administer projects'),
+ 'description' => t('Administer DevShop projects.'),
+ ),
+ 'change site domain name' => array(
+ 'title' => t("Change a site's primary domain name."),
+ 'description' => t('Alter the main domain name the site responds to.'),
+ ),
+ );
+}
+
+/**
+ * Implements hook_devshop_environment_menu().
+ *
+ * Defines the list of tasks that appear under the gear icon.
+ */
+function devshop_environment_menu_first_items($environment, $tasks) {
+ $site = null;
+ $platform = null;
+ $items = array();
+ $aegir_items = array();
+
+ global $user;
+
+ // Show for every site, even disabled ones.
+ if ($environment->site) {
+ $site = node_load($environment->site);
+ if (node_access('update', $site)) {
+ $items[] = l(' '.t('Environment Settings'),
+ "node/{$environment->site}/edit",
+ array(
+ 'html' => TRUE,
+ ));
+ $items[] = '';
+
+ }
+
+ if (drupal_valid_path("hosting_confirm/{$environment->site}/site_migrate") && user_access('change site domain names')) {
+ $href = "hosting_confirm/{$environment->site}/site_migrate";
+ $items[] = l(' '.t('Change Domain Name'),
+ $href, array(
+ 'query' => array(
+ 'rename' => 1,
+ 'token' => drupal_get_token($user->uid),
+ ),
+ 'html' => TRUE,
+ ));
+ }
+ if (node_access('view', $site)) {
+ $aegir_items[] = l(' '.t('Aegir Site'),
+ "node/{$environment->site}",
+ array(
+ 'html' => TRUE,
+ ));
+ }
+ }
+
+ // If platform exists
+ if ($environment->platform) {
+ $platform = node_load($environment->platform);
+ if (node_access('view', $platform)) {
+ $aegir_items[] = l(' '.t('Aegir Platform'),
+ "node/{$environment->platform}",
+ array(
+ 'query' => array(
+ 'rename' => 1,
+ 'token' => drupal_get_token($user->uid),
+ ),
+ 'html' => TRUE,
+ ));
+ }
+ }
+
+ // Load test task, if it exists
+ if (drupal_valid_path("hosting_confirm/{$environment->site}/site_test")) {
+ $href = "hosting_confirm/{$environment->site}/site_test";
+ $url = url($href, array(
+ 'query' => array(
+ 'token' => drupal_get_token($user->uid),
+ 'redirect' => 'task-page',
+ )
+ ));
+ $title = $tasks['test']['title'];
+ if (isset($tasks['test']['icon'])) {
+ $icon = '';
+ }
+ else {
+ $icon = '';
+ }
+ $items[] = <<
+ $icon $title
+
+HTML;
+ $items[] = '';
+ }
+
+ // Add aegir items to items
+ if (!empty($aegir_items)) {
+ $items = array_merge($items, $aegir_items);
+
+ if (!empty($actions)) {
+ $items[] = '';
+ }
+ }
+
+ // Site specific actions.
+ if ($environment->site && $environment->site_status == HOSTING_SITE_ENABLED) {
+ if (node_access('create', 'site')) {
+ $items[] = l(' '.t('Clone Environment'),
+ "node/add/site/{$environment->project_name}/clone/{$environment->name}",
+ array(
+ 'html' => TRUE,
+ )
+ );
+
+// Removing "Fork Environment" because it's a decent amount of work to re-do now
+// now that the site node form is being used. It was confusing to users , and we will
+// have a new beta shortly after this one anyway.
+// @TODO: Un-deprecate Fork Environment.
+// $items[] = l(' '.t('Fork Environment'),
+// "node/add/site/{$environment->project_name}/fork/{$environment->name}",
+// array(
+// 'html' => TRUE,
+// )
+// );
+ }
+
+ // Add disable or delete task based on hosting variable.
+ if (!variable_get('hosting_require_disable_before_delete', TRUE)) {
+ $items[] = 'disable';
+ $items[] = 'delete';
+ } else {
+
+ if ($environment->site_status == HOSTING_SITE_DISABLED) {
+ $items[] = 'enable';
+ $items[] = 'delete';
+ } elseif (empty($environment->settings->locked)) {
+ $items[] = 'disable';
+ }
+ }
+
+ $items[] = '';
+
+ // Add export and import config buttons to Drupal 8 sites.
+ if (module_exists('aegir_config') && strpos($environment->version, '8') === 0) {
+ $items[] = 'config_export';
+ $items[] = 'config_import';
+ $items[] = '';
+ }
+ elseif (module_exists('hosting_tasks_extra') && strpos($environment->version, '7') === 0) {
+ $items[] = 'features_update_all';
+ $items[] = 'features_revert_all';
+ $items[] = '';
+ }
+ }
+ elseif ($environment->site && $environment->site_status == HOSTING_SITE_DISABLED) {
+ $items[] = 'enable';
+ $items[] = 'delete';
+ }
+
+ return $items;
+}
+
+/**
+ * Implements hook_node_access().
+ */
+function devshop_projects_node_access($node, $op, $account) {
+ switch ($op) {
+ case 'create':
+ return user_access('create project', $account);
+ break;
+ case 'update':
+ return user_access('edit project', $account);
+ break;
+ case 'delete':
+ return user_access('delete projects', $account);
+ break;
+ case 'view':
+ return user_access('view project', $account);
+ break;
+ }
+}
+
+/**
+ * Implements hook_menu().
+ */
+function devshop_projects_menu() {
+ //Settings page.
+ $items['admin/devshop'] = array(
+ 'title' => 'DevShop Settings',
+ 'description' => 'Default values for use in creation project',
+ 'page callback' => 'drupal_get_form',
+ 'page arguments' => array('devshop_projects_settings_form'),
+ 'access arguments' => array('administer projects'),
+ 'file' => 'admin.inc',
+ 'file path' => drupal_get_path('module', 'devshop_projects') . '/inc',
+ 'type' => MENU_NORMAL_ITEM,
+ );
+ $items['admin/devshop/settings'] = array(
+ 'title' => t('DevShop Settings'),
+ 'type' => MENU_DEFAULT_LOCAL_TASK,
+ );
+
+ $items['projects'] = array(
+ 'title' => t('Projects'),
+ 'description' => 'Display a list of all projects',
+ 'page callback' => 'devshop_projects_projects_page',
+ 'access arguments' => array('view projects'),
+ 'menu_name' => 'main-menu',
+ 'type' => MENU_NORMAL_ITEM,
+ 'weight' => 1,
+ );
+ $items['projects/list'] = array(
+ 'title' => t('All Projects'),
+ 'description' => '',
+ 'type' => MENU_DEFAULT_LOCAL_TASK,
+ );
+ $items['projects/add'] = array(
+ 'title' => 'Start New Project',
+ 'type' => MENU_LOCAL_TASK,
+ 'title' => t('Start a new Project'),
+ 'title callback' => 'check_plain',
+ 'page callback' => 'devshop_projects_add',
+ 'page arguments' => array(2),
+ 'access arguments' => array('create project'),
+ 'description' => 'Start a new Drupal website project.',
+ 'file' => 'create.inc',
+ 'file path' => drupal_get_path('module', 'devshop_projects') . '/inc/create',
+ );
+
+ // Ajax endpoint for reloads
+ $items['projects/add/status'] = array(
+ 'page callback' => 'devshop_projects_add_status',
+ 'access callback' => 'node_access',
+ 'access arguments' => array('create', 'project'),
+ 'file' => 'create.inc',
+ 'file path' => drupal_get_path('module', 'devshop_projects') . '/inc/create',
+ );
+
+ // hosting tasks ajax pages.
+ $tasks = hosting_available_tasks('project') ;
+ if (!empty($tasks)) {
+ foreach (hosting_available_tasks('project') as $task => $info) {
+ $path = 'hosting_confirm/%/project_' . $task;
+ $items[$path] = array(
+ 'title' => $info['title'],
+ 'description' => $info['description'],
+ 'page callback' => 'devshop_projects_hosting_task_confirm_form_page',
+ 'page arguments' => array(1, $task),
+ 'access callback' => 'hosting_task_menu_access_csrf',
+ 'access arguments' => array(1, $task),
+ 'type' => MENU_CALLBACK,
+ );
+ $items[$path] = array_merge($items[$path], $info);
+ }
+ }
+
+ // Drush aliases download.
+ $items['node/%node/aliases'] = array(
+ 'title' => 'Drush Aliases',
+ 'description' => '',
+ 'page callback' => 'devshop_project_drush_aliases_page',
+ 'page arguments' => array(1),
+ 'access callback' => 'node_access',
+ 'access arguments' => array('update', 1),
+ 'weight' => 1,
+ 'file' => 'inc/drush.inc',
+ 'type' => MENU_CALLBACK,
+ );
+
+// // Add Environment page
+// $items['project/%/add-environment'] = array(
+// 'title' => 'Add Environment',
+// 'type' => MENU_CALLBACK,
+// 'page callback' => 'devshop_projects_environment_add_page',
+// 'page arguments' => array(1),
+// 'access callback' => 'devshop_projects_environment_add_access',
+// 'access arguments' => array(1),
+// );
+
+ // Ajax endpoint for reloads
+ $items['devshop/tasks'] = array(
+ 'page callback' => 'devshop_projects_tasks_status_json',
+ 'page arguments' => array(2),
+ 'description' => '',
+ 'access arguments' => array('access task logs'),
+ 'file' => 'tasks-ajax.inc',
+ 'file path' => drupal_get_path('module', 'devshop_projects') . '/inc',
+ );
+
+ return $items;
+}
+
+/**
+ * Get the bootstrap class from hosting task status.
+ */
+function devshop_task_status_class($status) {
+ $codes = array(
+ HOSTING_TASK_SUCCESS => 'success',
+ HOSTING_TASK_QUEUED => 'queued',
+ HOSTING_TASK_ERROR => 'danger',
+ HOSTING_TASK_PROCESSING => 'processing',
+ HOSTING_TASK_WARNING => 'warning',
+ );
+ return $codes[$status];
+}
+
+/**
+ * Get the fontawesome icon from hosting task status.
+ */
+function devshop_task_status_icon($status) {
+ $icons = array(
+ HOSTING_TASK_SUCCESS => 'check',
+ HOSTING_TASK_QUEUED => 'cog',
+ HOSTING_TASK_ERROR => 'exclamation-circle',
+ HOSTING_TASK_PROCESSING => 'cog fa-spin',
+ HOSTING_TASK_WARNING => 'warning',
+ );
+ return $icons[$status];
+}
+
+function devshop_project_logs_access() {
+ return TRUE;
+}
+
+/**
+ * Implements hook_block_info_alter()
+ *
+ * Disables Aegir's queue and "supporting aegir" block.
+ *
+ * @param $blocks
+ * @param $theme
+ * @param $code_blocks
+ */
+function devshop_projects_block_info_alter(&$blocks, $theme, $code_blocks) {
+ $blocks['hosting']['hosting_queues']['status'] = 0;
+ $blocks['hosting']['hosting_queues_summary']['status'] = 0;
+ $blocks['hosting']['hosting_supporting_aegir']['status'] = 0;
+}
+
+/**
+ * Implements hook_block_info().
+ */
+function devshop_projects_block_info() {
+ $blocks['project_nav'] = array(
+ 'info' => t('DevShop Project Nav'),
+ 'weight' => '-10',
+ 'cache' => DRUPAL_CACHE_GLOBAL,
+ 'status' => 1,
+ 'region' => 'header',
+ );
+ $blocks['project_create'] = array(
+ 'info' => t('DevShop Create Project'),
+ 'weight' => '-10',
+ 'cache' => DRUPAL_CACHE_PER_USER,
+ 'status' => 1,
+ 'region' => 'sidebar_first',
+ );
+ return $blocks;
+}
+
+/**
+ * Implements hook_block_view().
+ */
+function devshop_projects_block_view($delta) {
+ if ($delta == 'project_nav' && (
+ arg(0) == 'node' || arg(0) == 'hosting_confirm'
+ )) {
+
+ if (is_numeric(arg(1))) {
+ $node = node_load(arg(1));
+ }
+ elseif (arg(1) == 'add') {
+ $node = devshop_projects_load_by_name(arg(3));
+ }
+
+ if (node_access('view', $node) && ($node->type == 'project' || !empty($node->project))) {
+
+ // If viewed node is project, use that.
+ if (is_numeric($node->project)) {
+ if ($project_node = node_load($node->project)) {
+ $project = $project_node->project;
+ }
+ }
+ elseif (!empty($node->project)) {
+ $project = $node->project;
+ }
+ else {
+ return '';
+ }
+
+ $block['subject'] = '';
+ $block['content'] = theme('devshop_project_nav', array('project' => $project));
+ }
+ }
+ elseif ($delta == 'project_create') {
+ if (arg(0) == 'projects' && arg(1) == 'add') {
+ ctools_include('object-cache');
+ $project = ctools_object_cache_get('project', 'devshop_project');
+ $block['subject'] = '';
+ $block['content'] = theme('devshop_project_add_status', array('project' => $project));
+ }
+ }
+ if (isset($block)) {
+ return $block;
+ }
+}
+
+/**
+ * Page Callback for projects/add
+ */
+function devshop_projects_add($step = NULL) {
+ if ($step == NULL) {
+ // Check to see if this project is still in the wizard
+ ctools_include('object-cache');
+ $project_wizard_cache = ctools_object_cache_get('project', 'devshop_project');
+ if (!empty($project_wizard_cache->step)) {
+ drupal_goto('projects/add/' . $project_wizard_cache->step);
+ }
+ }
+ return devshop_projects_create_wizard($step);
+}
+
+/**
+ * Replacement for hosting_task_confirm_form()
+ *
+ * @TODO: Remove once http://drupal.org/node/1861898 is committed.
+ */
+function devshop_projects_hosting_task_confirm_form_page($nid, $task) {
+ $node = node_load($nid);
+ return drupal_get_form('hosting_task_confirm_form', $node, $task);
+}
+
+/**
+ * Implements hook_menu_alter().
+ *
+ * Replaces node/add/project with a ctools wizard.
+ */
+function devshop_projects_menu_alter(&$items) {
+ $items['node/add/project']['page callback'] = 'devshop_projects_create_wizard';
+ $items['node/add/project']['page arguments'] = array(3);
+ $items['node/add/project']['file'] = 'create-wizard.inc';
+ $items['node/add/project']['file path'] = drupal_get_path('module', 'devshop_projects') . '/inc';
+
+ // Make project node pages more user-friendly.
+ $items['node/%node/view']['title callback'] = 'devshop_hosting_project_tab_title';
+ $items['node/%node/view']['title arguments'] = array('View', 1);
+
+ $items['node/%node/edit']['title callback'] = 'devshop_hosting_project_tab_title';
+ $items['node/%node/edit']['title arguments'] = array('Edit', 1);
+
+ $items['node/%node/edit']['access callback'] = 'devshop_hosting_project_tab_access';
+
+
+
+ $items['node/%node/view']['weight'] = -10;
+ $items['node/%node/edit']['weight'] = -9;
+
+ // Hosting now hides all but migrate and clone tasks.
+ $path = sprintf("hosting_confirm/%hosting_%s_wildcard/site_devshop-deploy", 'deploy', 'deploy', 'devshop-deploy');
+ $items[$path]['access callback'] = 'devshop_hosting_task_menu_access_csrf';
+
+ // Redirect hosting/task/% to the node
+ $items['hosting/task/%'] = array(
+ 'page callback' => 'drupal_goto_nid',
+ 'page arguments' => array(2),
+ 'access callback' => TRUE,
+ );
+}
+
+/**
+ * Helper to redirect to a node page.
+ * @param $nid
+ */
+function drupal_goto_nid($nid) {
+ drupal_goto("node/{$nid}");
+}
+
+/**
+ * Access callback helper for hosting task menu items.
+ * A copy of hosting_task_menu_access() except we allow devshop-deploy tasks.
+ * @see hosting_task_menu_access()
+ */
+function devshop_hosting_task_menu_access_csrf($node, $task) {
+ global $user;
+
+ $interactive_tasks = array('migrate', 'clone', 'devshop-deploy');
+
+ // To prevent CSRF attacks, a unique token based upon user is used. Deny
+ // access if the token is missing or invalid. We only do this on
+ // non-interactive tasks.
+ if (!in_array($task, $interactive_tasks) && (!isset($_GET['token']) || !drupal_valid_token($_GET['token'], $user->uid))) {
+ return FALSE;
+ }
+ // Call the main menu access handler.
+ return hosting_task_menu_access($node, $task);
+}
+
+/**
+ * Tab title replacer
+ */
+function devshop_hosting_project_tab_title($default, $node) {
+ if ($default == 'View' && $node->type == 'project') {
+ return t('Project Dashboard');
+ }
+ if ($default == 'Edit' && $node->type == 'project') {
+ return t('Project Settings');
+ }
+ if ($default == 'View' && $node->type == 'site' && isset($node->project)) {
+ return t('Environment Dashboard');
+ }
+ if ($default == 'Edit' && $node->type == 'site' && isset($node->project)) {
+ return t('Environment Settings');
+ }
+ if ($node->type == 'task') {
+ if ($default == 'View') {
+ $tasks = hosting_available_tasks('site');
+ $label = drupal_ucfirst($tasks[$node->task_type]['title']);
+ return t('Task: !type', array('!type' => $label));
+ }
+ }
+
+ // Otherwise, just return the page text
+ return t($default);
+}
+
+/**
+ * replacement for node_access
+ */
+function devshop_hosting_project_tab_access($type, $node) {
+ if ($node->type == 'task') {
+ return FALSE;
+ }
+ return node_access($type, $node);
+}
+
+/**
+ * Access Callback for Aegir Tasks
+ *
+ * This function defines access to the various aegir tasks.
+ *
+ * @arg $node object
+ * The node object is running the task. (Site, Platform, Server)
+ *
+ * @arg $task string
+ * The type of the task that is running.
+ *
+ * @see hosting_task_menu_access()
+ *
+ * @TODO: This NEVER runs for verify! Only for devshop-xyz tasks.
+ * project verify task is defined in devshop_projects_hosting_tasks() in
+ * inc/tasks.inc, and has this function as it's access callback. But this
+ * function seems to never run.
+ */
+function devshop_hosting_task_menu_access($node, $task) {
+ // If we are passed the nid by mistake
+ if (!isset($node->nid)) {
+ $node = node_load($node);
+ }
+
+ if ($node->type != 'project' && $task != 'delete' && !$node->status) {
+ return FALSE;
+ }
+
+ if (user_access("create " . $task . " task")) {
+ return TRUE;
+ }
+}
+
+
+
+/**
+ * Implements hook_hosting_drush_aliases_name().
+ *
+ * See http://drupal.org/project/hosting_drush_aliases
+ */
+function devshop_projects_hosting_drush_aliases_name($node) {
+ if (isset($node->project_name)) {
+ return $node->project_name . "." . $node->project_environment;
+ }
+}
+
+/**
+ * Helper function to create a site in a project.
+ * Used by Wizard & "Create Platform" > Post Verify
+ */
+function devshop_projects_create_site($project, $platform_node, $environment_name, $db_server = NULL) {
+ global $user;
+
+ // Create the site nodes
+ $node = new stdClass();
+ $node->type = 'site';
+ $node->status = 1;
+ $node->uid = $user->uid;
+ $node->title = devshop_environment_url($project, $environment_name);
+
+ // Aegir properties
+ // @TODO: better client & DB support
+ $node->client = HOSTING_DEFAULT_CLIENT;
+ $servers = hosting_get_servers('db', FALSE);
+ $server = $db_server ? $db_server : key($servers);
+ $node->db_server = $server;
+
+ $node->platform = $platform_node->nid;
+
+ // Lookup this platforms install profile
+ $node->profile = db_query('SELECT nid FROM {hosting_package} WHERE short_name = :short_name', array(':short_name' => $project->install_profile))->fetchField();
+
+ // @TODO: Improve site language handling?
+ $node->site_language = !empty($user->language) ? $user->language : 'en';
+
+ // Subdomain alias, if configured.
+ if ($project->settings->live['environment_aliases'] && !empty($project->settings->live['live_domain'])) {
+ $node->aliases = array($environment_name . '.' . $project->settings->live['live_domain']);
+ }
+
+ // Save the site node
+ try {
+ if ($node = node_submit($node)) {
+ node_save($node);
+ }
+ } catch (PDOException $e) {
+ drupal_set_message(t('Unable to save environment: %message', array(
+ '%message' => $e->getMessage(),
+ )));
+ }
+
+ // Lookup install task to use it for last task.
+ $tasks = hosting_get_tasks('rid', $node->nid);
+
+ // Update environment with our site
+ db_update('hosting_devshop_project_environment')
+ ->fields(array(
+ 'site' => $node->nid,
+ 'last_task' => current($tasks)->nid,
+ ))
+ ->condition('project_nid', $project->nid)
+ ->condition('name', $environment_name)
+ ->execute();
+
+ return $node;
+}
+
+/**
+ * Helper to get select #options for git ref.
+ */
+function devshop_projects_git_ref_options($project, $current_ref = '') {
+
+ // Build branch options
+ if (is_array($project->settings->git['branches']) && !empty($project->settings->git['branches'])) {
+ $options = array(
+ 'Branches' => array_combine($project->settings->git['branches'], $project->settings->git['branches']),
+ );
+
+ // If there are tags...
+ if (is_array($project->settings->git['tags']) && !empty($project->settings->git['tags'])) {
+ $options['Tags'] = array_combine($project->settings->git['tags'], $project->settings->git['tags']);
+ }
+ }
+
+ // If there are none...
+ if (!isset($options)) {
+ $options = array(t('No branches or tags! Re-validate the project.'));
+ }
+
+ // Set "current" label.
+ if (isset($options['Branches'][$current_ref])) {
+ $options['Branches'][$current_ref] .= ' (' . t('current') . ')';
+ }
+ elseif (isset($options['Tags'][$current_ref])) {
+ $options['Tags'][$current_ref] .= ' (' . t('current') . ')';
+ }
+
+
+ return $options;
+}
+
+/**
+ * Check if a site has features diff enabled.
+ */
+function _devshop_projects_site_has_module($nid, $module) {
+ if (is_object($nid) && isset($nid->nid)) {
+ $nid = $nid->nid;
+ }
+
+ $query = db_select('hosting_package_instance', 'i')
+ ->fields('i', array('status'));
+ $query->leftJoin('hosting_package', 'p', 'i.package_id = p.nid');
+
+ $query->condition('p.short_name', $module);
+ $query->condition('rid', $nid);
+
+ $result = $query->execute()->fetchField();
+ return $result;
+}
+
+/**
+ * Check if a site has features diff enabled.
+ */
+function _devshop_projects_project_has_module($node, $module) {
+ $environment = key($node->project->environments);
+
+ if (isset($node->project->environments[$environment]->site)) {
+ return _devshop_projects_site_has_module($node->project->environments[$environment]->site, $module);
+ }
+}
+
+/**
+ * Reset all environment's "last task" to the NID.
+ */
+function devshop_reset_last_tasks () {
+
+ // Lookup all environments
+ $results = db_query('SELECT * FROM {hosting_devshop_project_environment}')->fetchAllAssoc('site');
+ foreach ($results as $result) {
+ $nid = db_query("SELECT t.nid FROM hosting_task t WHERE rid = :platform OR rid = :site ORDER BY t.vid DESC", array(
+ ':site' => $result->site,
+ ':platform' => $result->platform,
+ ))->fetchField();
+
+ if (!empty($result->name)) {
+ $result->last_task = $nid;
+ devshop_environment_save_last_task($result);
+ }
+ }
+}
+
+/**
+ * Helper to get a project node by name.
+ */
+function devshop_load_project_by_name($name) {
+ $nid = db_select('node', 'n')
+ ->fields('n', array('nid'))
+ ->condition('type', 'project')
+ ->condition('title', $name)
+ ->condition('status', 1)
+ ->execute()
+ ->fetchField()
+ ;
+ $node = node_load($nid);
+ return $node;
+}
+
+/**
+ * Generate an environment's URL from project and environment name using the domain name pattern.
+ *
+ * @param $project
+ * @param $environment_name
+ * @return string
+ */
+function devshop_environment_url($project, $environment_name) {
+
+ // Generate field prefix and suffix using domain name pattern.
+ if (variable_get('devshop_projects_allow_custom_base_url')) {
+ $pattern = $project->base_url;
+ }
+ else {
+ $pattern = variable_get('devshop_project_environment_url_pattern', '@project.@environment.@hostname');
+ }
+ $domain = strtr($pattern, array(
+ '@environment' => $environment_name,
+ '@project' => $project->name,
+ '@hostname' => $_SERVER['SERVER_NAME'],
+ ));
+ return $domain;
+}
+
+/**
+ * Retrieve a project node by name.
+ *
+ * @param $name
+ * @return bool|mixed
+ */
+function devshop_projects_load_by_name($name) {
+ $nid = db_select('node', 'n')
+ ->fields('n', array('nid'))
+ ->condition('title', $name)
+ ->condition('type', 'project')
+ ->condition('status', 1)
+ ->execute()
+ ->fetchField();
+
+ return node_load($nid);
+}
diff --git a/modules/devshop/devshop_projects/drush/Provision/Config/ProjectAliases.php b/modules/devshop/devshop_projects/drush/Provision/Config/ProjectAliases.php
new file mode 100644
index 000000000..555c244b9
--- /dev/null
+++ b/modules/devshop/devshop_projects/drush/Provision/Config/ProjectAliases.php
@@ -0,0 +1,31 @@
+data = array(
+ 'name' => strtr($context, array('@project_' => '')),
+ 'project' => $project,
+ );
+ }
+
+ function filename() {
+ return drush_server_home() . '/.drush/project_aliases/' . $this->data['name'] . '.aliases.drushrc.php';
+ }
+}
diff --git a/modules/devshop/devshop_projects/drush/Provision/Config/project_aliases.tpl.php b/modules/devshop/devshop_projects/drush/Provision/Config/project_aliases.tpl.php
new file mode 100644
index 000000000..19202aae2
--- /dev/null
+++ b/modules/devshop/devshop_projects/drush/Provision/Config/project_aliases.tpl.php
@@ -0,0 +1,39 @@
+environments as $name => $environment) {
+
+ if ($environment->site_status != 1) {
+ continue;
+ }
+ // Tell drush to inherit from the provision site alias record.
+ $alias = array(
+ 'parent' => '@' . $environment->uri
+ );
+
+ // If web server is not server master, add "remote host and user.
+ if (d($environment->drush_alias)->platform->web_server->name != '@server_master') {
+ $alias['remote-host'] = d($environment->drush_alias)->platform->web_server->remote_host;
+ $alias['remote-user'] = d($environment->drush_alias)->platform->web_server->script_user;
+ }
+
+ $export = var_export($alias, TRUE);
+ ?>
+
+$aliases[''] = ;
+
+ settings->aliases as $name => $remote_alias) {
+ $export = var_export($remote_alias, TRUE);
+ ?>
+
+$aliases[''] = ;
+
+
diff --git a/modules/devshop/devshop_projects/drush/Provision/Context/project.php b/modules/devshop/devshop_projects/drush/Provision/Context/project.php
new file mode 100644
index 000000000..abd16a6f1
--- /dev/null
+++ b/modules/devshop/devshop_projects/drush/Provision/Context/project.php
@@ -0,0 +1,46 @@
+project['environments'][$name])) {
+ return (object) $this->project['environments'][$name];
+ }
+ else {
+ return drush_set_error('DEVSHOP_PROJECT_ERROR', dt('Environment %name not found.', array(
+ '%name' => $name,
+ )));
+ }
+ }
+
+ static function option_documentation() {
+ return array(
+ '--project_name' => 'Project: The codename for this project.',
+ '--project' => 'Project: JSON encoded data about the project.',
+
+ //@TODO: Document this!
+// '--code_path' => 'Project: The path to the project codebases. (NOT the Drupal root)',
+// '--drupal_path' => 'Project: The path to the drupal root.',
+// '--git_url' => 'Project: The Git URL for this project.',
+// '--git_branches' => 'Project: The available Git branches in the remote repository for this project.',
+// '--git_tags' => 'Project: The available Git tags in the remote repository for this project.',
+// '--base_url' => 'Project: the base URL that the dev/test/live subdomains will be attached to.',
+// '--server' => 'Project: The server hosting this project. (Default is @server_master)',
+// '--install_profile' => 'Project: The desired installation profile for all sites.',
+ );
+ }
+
+ function init_project() {
+ $this->setProperty('project_name');
+ $this->setProperty('project');
+ }
+
+ function verify() {
+ $this->type_invoke('verify');
+ drush_log('[DEVSHOP] verify()', 'ok');
+ }
+}
diff --git a/modules/devshop/devshop_projects/drush/Provision/Service/Process.php b/modules/devshop/devshop_projects/drush/Provision/Service/Process.php
new file mode 100644
index 000000000..bb8e70e75
--- /dev/null
+++ b/modules/devshop/devshop_projects/drush/Provision/Service/Process.php
@@ -0,0 +1,26 @@
+server->remote_host)) {
+ return provision_process($command, $cwd, $label, $env, $log_output);
+ }
+ else {
+ return provision_process('ssh ' . drush_get_option('ssh-options', '-o PasswordAuthentication=no') . ' ' . $this->server->script_user . '@' . $this->server->remote_host . ' ' . $command, $cwd, $label, $env);
+ }
+ }
+}
diff --git a/modules/devshop/devshop_projects/drush/Provision/Service/project.php b/modules/devshop/devshop_projects/drush/Provision/Service/project.php
new file mode 100644
index 000000000..b20d70be7
--- /dev/null
+++ b/modules/devshop/devshop_projects/drush/Provision/Service/project.php
@@ -0,0 +1,26 @@
+setProperty('environment');
+ $context->setProperty('environment_settings');
+ $context->setProperty('project');
+ }
+
+ /**
+ * Add environment, project, and git ref to platform aliases.
+ */
+ static function subscribe_platform($context) {
+ $context->setProperty('environment');
+ $context->setProperty('project');
+ }
+
+}
diff --git a/modules/devshop/devshop_projects/drush/acquia/README.txt b/modules/devshop/devshop_projects/drush/acquia/README.txt
new file mode 100644
index 000000000..be1b71e65
--- /dev/null
+++ b/modules/devshop/devshop_projects/drush/acquia/README.txt
@@ -0,0 +1,9 @@
+Acquia Cloud Hooks Integration
+==============================
+
+DevShop now supports firing Acquia Cloud Hooks when deploying to environments.
+
+The code in acquia.drush.inc will detect an acquia repo and trigger your hooks
+to run.
+
+Currently, only the `post-code-update` hook is supported. More to come.
\ No newline at end of file
diff --git a/modules/devshop/devshop_projects/drush/acquia/acquia.drush.inc b/modules/devshop/devshop_projects/drush/acquia/acquia.drush.inc
new file mode 100644
index 000000000..59346a401
--- /dev/null
+++ b/modules/devshop/devshop_projects/drush/acquia/acquia.drush.inc
@@ -0,0 +1,76 @@
+type == 'site'){
+
+ $project_name = d()->project;
+ $project = (object) d("@project_{$project_name}")->project;
+ $environment = (object) $project->environments[d()->environment];
+
+ // Respect environment settings.
+ if ($environment->settings['deploy']['acquia_hooks'] != 1) {
+ return;
+ }
+
+ // If project has no path to drupal, we know it's not acquia.
+ if ($project->drupal_path != 'docroot') {
+ drush_log('[DEVSHOP] Acquia Cloud skipped.', 'ok');
+ return;
+ }
+
+ // Detect hook files.
+ $cloud_hooks_path = $environment->repo_path . '/hooks';
+ if (file_exists($cloud_hooks_path)) {
+ drush_log('[DEVSHOP] Acquia Cloud Hooks detected...', 'ok');
+ }
+
+ // Collect scripts to run.
+ // Common scripts.
+ $files = scandir($cloud_hooks_path . '/common/post-code-update');
+ if (empty($files)) $files = array();
+ $scripts = array_diff($files, array('..', '.'));
+ foreach ($scripts as &$script) {
+ $script = realpath($cloud_hooks_path . '/common/post-code-update/' . $script);
+ }
+
+ // Environment scripts: Post Code Update
+ if (file_exists($cloud_hooks_path . '/' . $environment->name)) {
+ $files = scandir($cloud_hooks_path . '/' . $environment->name . '/post-code-update');
+ if (empty($files)) $files = array();
+ $scripts += array_diff($files, array('..', '.'));
+ foreach ($scripts as &$script) {
+ $script = realpath($cloud_hooks_path . '/' . $environment->name . '/post-code-update/' . $script);
+ }
+ }
+
+ // @TODO: Post Code Deploy & Post DB Copy
+
+ drush_log(implode("\n", $scripts), 'ok');
+
+ // Run Scripts
+ // Usage: post-code-deploy site target-env source-branch deployed-tag repo-url repo-type
+ foreach ($scripts as $file) {
+ drush_log('[DEVSHOP] Running Acquia Cloud Hook: ' . $file, 'ok');
+
+ if (drush_shell_exec("sh $file {$project_name} {$environment->name} old_branch {$branch} repo_url repo_type devshop $environment->url") !== 0) {
+ $output = drush_shell_exec_output();
+ drush_log(implode("\n", $output), 'ok');
+ }
+ else {
+ return drush_set_error(DRUSH_FRAMEWORK_ERROR, 'The last cloud hook returned a non-zero exit code. Remaining hooks skipped.');
+ }
+ }
+ }
+}
\ No newline at end of file
diff --git a/modules/devshop/devshop_projects/drush/acquia/example.slack.sh b/modules/devshop/devshop_projects/drush/acquia/example.slack.sh
new file mode 100644
index 000000000..3c4f848d1
--- /dev/null
+++ b/modules/devshop/devshop_projects/drush/acquia/example.slack.sh
@@ -0,0 +1,39 @@
+#!/bin/sh
+#
+# Altered from https://github.com/acquia/cloud-hooks/tree/master/samples/slack
+# Requires a /var/aegir/slack_settings file that contains:
+#
+# SLACK_WEBHOOK_URL=https://example.slack.com/services/hooks/incoming-webhook?token=TOKEN
+#
+# Cloud Hook: post-code-deploy
+#
+# The post-code-deploy hook is run whenever you use the Workflow page to
+# deploy new code to an environment, either via drag-drop or by selecting
+# an existing branch or tag from the Code drop-down list. See
+# ../README.md for details.
+#
+# Usage: post-code-deploy site target-env source-branch deployed-tag repo-url
+# repo-type
+
+site="$1"
+target_env="$2"
+source_branch="$3"
+deployed_tag="$4"
+repo_url="$5"
+repo_type="$6"
+source="$7"
+site_url="$8"
+
+# Load the Slack webhook URL (which is not stored in this repo).
+. $HOME/slack_settings
+
+if [ $source = 'devshop' ]; then
+ source="DevShop"
+ image="https://www.drupal.org/files/project-images/devshop-icon.png"
+else
+ source="Acquia Cloud"
+ image="https://pbs.twimg.com/profile_images/1901642489/cloud_icon_150.png"
+fi
+
+# Post deployment notice to Slack
+curl -X POST --data-urlencode "payload={\"channel\": \"#dev-engageny\", \"username\": \"$source\", \"text\": \"Git branch \`$deployed_tag\` updated on *$target_env*: $site_url.\", \"icon_url\": \"$image\"}" $SLACK_WEBHOOK_URL
diff --git a/modules/devshop/devshop_projects/drush/aegir_extras/aegir_download.drush.inc b/modules/devshop/devshop_projects/drush/aegir_extras/aegir_download.drush.inc
new file mode 100644
index 000000000..8840c3e04
--- /dev/null
+++ b/modules/devshop/devshop_projects/drush/aegir_extras/aegir_download.drush.inc
@@ -0,0 +1,157 @@
+ 'Downloads drupal modules and themes, and optionally commits them to git.',
+ 'bootstrap' => DRUSH_BOOTSTRAP_DRUSH,
+ 'options' => array(
+ 'packages' => 'This list of modules and themes to download.',
+ 'commit' => 'Commit the downloaded code to git.',
+ 'message' => 'The message to use when committing.',
+ 'update' => 'Run update.php after the download.',
+ 'test' => 'Queue a test run after the download.',
+ ),
+ 'arguments' => array(
+ ),
+ 'examples' => array(
+ 'drush @env.project.domain.com provision-download views ctools --update --commit --message="the usuals"' => 'Downloads views & ctools, commits them to the repo with a message, and run drush updb.',
+ ),
+ 'aliases' => array('pdl'),
+ );
+ return $items;
+}
+
+/**
+ * Implements the provision-download command.
+ */
+function drush_aegir_download_provision_download() {
+ $packages = drush_get_option('packages');
+ $composer_build = false;
+
+ // Use composer require if there is a composer.json file and packages.drupal.org is a repo...
+ if (file_exists(d()->platform->repo_path . '/composer.json')) {
+ $composer_json = json_decode(file_get_contents(d()->platform->repo_path . '/composer.json'));
+ $cmd = '';
+
+ if (is_string($packages)) {
+ $packages = explode(' ', $packages);
+ }
+
+ if (isset($composer_json->repositories)) {
+ foreach ($composer_json->repositories as $repo) {
+ if (strpos($repo->url, 'https://packages.drupal.org') === 0) {
+ $cmd = "composer require ";
+ foreach ($packages as $package) {
+ $cmd .= "drupal/$package ";
+ $composer_build = true;
+ }
+ continue;
+ }
+ }
+ }
+
+ if (empty($cmd)) {
+ drush_log(dt('The Drupal packagist repository was not found: unable to use `composer require` to add modules. Please see https://www.drupal.org/docs/develop/using-composer/using-composer-to-manage-drupal-site-dependencies#drupal-packagist'), 'warning');
+ }
+ }
+ else {
+
+ if (is_array($packages)) {
+ $packages = implode($packages);
+ }
+
+ // No modules, no more.
+ if (empty($packages)) {
+ return drush_set_error('No packages defined.');
+ }
+
+ // Run `drush dl $modules`
+ $target = d()->name;
+ $cmd = "drush $target dl $packages --yes --strict=0";
+
+ // Pass through options to the drush dl command.
+ if (drush_get_option('commit', FALSE)) {
+ $cmd .= ' --commit ';
+ }
+ if (drush_get_option('message', '')) {
+ $message = escapeshellarg(drush_get_option('message', dt('Committed by DevShop: ') . $packages));
+ $cmd .= " --message='$message' ";
+ }
+ if (drush_get_option('update', FALSE)) {
+ $cmd .= " --update";
+ }
+ }
+
+ provision_process($cmd, d()->platform->repo_path);
+
+ // Commit and push has to happen here when using composer, there are no post-composer hooks here.
+ if ($composer_build && drush_get_option('commit', FALSE)) {
+ $message = escapeshellarg(drush_get_option('message', dt('Committed by DevShop: ') . implode(" ", $packages)));
+
+ $files = 'composer.json';
+ if (file_exists(d()->platform->repo_path . '/composer.lock')) {
+ $files .= ' composer.lock';
+ }
+
+ $cmd = "git commit $files --message=$message";
+ provision_process($cmd, d()->platform->repo_path);
+
+ $process = drush_get_context('provision_process_result');
+ if ($process->getExitCode() == 0) {
+ provision_process('git push', d()->platform->repo_path);
+ }
+ }
+}
+
+/**
+ * Implements hook_drush_pm_post_download()
+ *
+ * Runs after a project has been downloaded.
+ *
+ * This is needed for devshop because we want to commit the new module we just
+ * downloaded, and only this hook knows about the path.
+ *
+ * @param $drupal_project
+ * @param $release
+ */
+function aegir_download_drush_pm_post_download($drupal_project, $release) {
+
+ $project_name = d()->project;
+ $project = (object) d("@project_{$project_name}")->project;
+ $environment = (object) $project->environments[d()->environment];
+
+ if (d()->environment && drush_get_option('commit', FALSE)) {
+ drush_log(dt('[DEVSHOP] Committing new module...'), 'ok');
+
+ $message = drush_get_option('message', dt('Committed by DevShop: ') . $drupal_project['name']);
+
+ $wrapper = new GitWrapper();
+ $wrapper->streamOutput();
+
+ try {
+ $git = $wrapper->workingCopy($environment->repo_path);
+
+ $download_path = $drupal_project['full_project_path'];
+ $git
+ ->add($download_path)
+ ->commit($message)
+ ->push();
+ }
+ catch (GitException $e) {
+ return drush_set_error('DRUSH_ERROR', "Git Exception: " . $e->getMessage());
+ }
+ }
+ else {
+ drush_log(dt('Skipping Commit. Make sure you commit or delete this module at some point.'), 'notice');
+ }
+}
diff --git a/modules/devshop/devshop_projects/drush/contexts.inc b/modules/devshop/devshop_projects/drush/contexts.inc
new file mode 100644
index 000000000..fd7fb8ef5
--- /dev/null
+++ b/modules/devshop/devshop_projects/drush/contexts.inc
@@ -0,0 +1,360 @@
+ref->project->git_url);
+
+ $home = variable_get('aegir_home', '/var/aegir');
+ $clone_dir = "$home/repos/{$task->ref->project->name}";
+
+ // If something went wrong connecting to the git repo, don't wipe out our branches.
+ if (!empty($branches['branches'])) {
+ $task->ref->project->settings->git['branches'] = $branches['branches'];
+ $task->ref->project->settings->git['tags'] = $branches['tags'];
+ $task->ref->project->settings->git['refs'] = $branches['refs'];
+
+ // Save the project node now that we have branches and tags.
+ // Don't verify again, this is the verification process.
+ $task->ref->no_verify = TRUE;
+ node_save($task->ref);
+ }
+
+ // If branches are empty, and "github_create" was selected...
+ elseif (!file_exists($clone_dir) && $task->ref->project->settings->create_project_settings['git_source'] == 'github_create') {
+
+ $git_url = $task->ref->project->git_url;
+
+ $account = user_load($task->uid);
+ $name = $account->name;
+ $mail = $account->mail;
+ $env = array();
+
+ // Make commit using environment variables for NAME and EMAIL, if specified.
+ if (!empty($name)) {
+ $env['GIT_AUTHOR_NAME'] = $name;
+ $env['GIT_COMMITTER_NAME'] = $name;
+ }
+ if (!empty($mail)) {
+ $env['GIT_AUTHOR_MAIL'] = $mail;
+ $env['GIT_COMMITTER_MAIL'] = $mail;
+ }
+
+ $hostname = $_SERVER['HOSTNAME'];
+
+ if (!file_exists("$home/repos")) {
+ provision_process("mkdir -p $home/repos", null, t('Creating repos directory'));
+ }
+
+ if ($task->ref->project->settings->create_project_settings['github_repository_source']['populate_choice'] == 'import') {
+ $source_url = $task->ref->project->settings->create_project_settings['github_repository_source']['import'];
+ provision_process("git clone $source_url $clone_dir");
+ provision_process("git remote set-url origin $git_url", $clone_dir);
+ }
+ else {
+
+ $composer_project = $task->ref->project->settings->create_project_settings['github_repository_source']['composer_project'];
+ $composer_create_project_command = "composer create-project {$composer_project} {$task->ref->project->name} --no-interaction --ansi";
+
+ provision_process($composer_create_project_command,"$home/repos", t('Creating composer project...'));
+ $result = drush_get_context('provision_process_result');
+ if (!$result->isSuccessful()) {
+ return drush_set_error('DEVSHOP_ERROR', 'Composer create-project failed.');
+ }
+
+ if (!file_exists($clone_dir)) {
+ return drush_set_error('DEVSHOP_ERROR', 'Unable to find codebase in ' . $clone_dir);
+ }
+
+ provision_process("git init", $clone_dir);
+
+ provision_process("git add .gitignore", $clone_dir);
+ provision_process("git add -A", $clone_dir);
+ provision_process("git status", $clone_dir);
+ provision_process("git commit -m 'Repo created by DevShop on $hostname using the command `$composer_create_project_command`'", $clone_dir, t('Committing codebase...'), $env);
+ provision_process("git remote add origin $git_url", $clone_dir);
+ }
+
+ $branch = trim(str_replace('refs/heads/', '', shell_exec("cd {$clone_dir}; git describe --tags --exact-match 2> /dev/null || git symbolic-ref -q HEAD 2> /dev/null")));
+
+ provision_process("git push -u origin $branch", $clone_dir);
+
+ if (file_exists($clone_dir)) {
+ provision_process("rm -rf $clone_dir", null, t('Removing temporary git clone...'));
+ }
+
+ $branches = getBranchesAndTags($task->ref->project->git_url);
+ $task->ref->project->settings->git['branches'] = $branches['branches'];
+ $task->ref->project->settings->git['tags'] = $branches['tags'];
+ $task->ref->project->settings->git['refs'] = $branches['refs'];
+
+ // Save the project node now that we have branches and tags.
+ // Don't verify again, this is the verification process.
+ $task->ref->no_verify = TRUE;
+ node_save($task->ref);
+
+ }
+
+ // Save project object to drush alias (aegir context).
+ if (isset($task->ref->project)) {
+ $task->context_options['server'] = '@server_master';
+ $task->context_options['project_name'] = $task->ref->title;
+
+ // Clean up project and environment objects.
+ $project = $task->ref->project;
+ unset($project->tasks);
+ unset($project->verify);
+ unset($project->messages);
+
+ foreach ($project->environments as &$environment) {
+ unset($environment->tasks);
+ unset($environment->tasks_list);
+ unset($environment->last_task_node);
+ }
+
+ $task->context_options['project'] = $project;
+ }
+}
+
+/**
+ * Implements hook_hosting_site_context_options().
+ *
+ * Runs on verify task. Passes data to the drush alias.
+ * Save environment name, project name, and git ref to site aliases.
+ */
+function devshop_projects_hosting_site_context_options(&$task) {
+
+ if (isset($task->ref->environment)) {
+ $task->context_options['environment'] = $task->ref->environment->name;
+ $task->context_options['environment_settings'] = $task->ref->environment->settings;
+ $task->context_options['project'] = $task->ref->project->name;
+ $task->context_options['git_ref'] = $task->ref->environment->git_ref;
+ }
+
+ // If install task is set to force-reinstall, pass to drush options.
+ if ($task->task_type == 'install' && $task->task_args['force-reinstall']) {
+ $task->options['force-reinstall'] = 1;
+ }
+
+}
+
+/**
+ * Implements hook_hosting_site_context_options().
+ *
+ * Runs on verify task. Passes data to the drush alias.
+ * Save environment name, project name, and git ref to site aliases.
+ */
+function devshop_projects_hosting_platform_context_options(&$task) {
+
+ if (isset($task->ref->environment)) {
+ $task->context_options['environment'] = $task->ref->environment->name;
+ $task->context_options['project'] = $task->ref->project->name;
+ $task->context_options['git_ref'] = $task->ref->environment->git_ref;
+ }
+}
+
+/**
+ * Implements hook_drush_context_import().
+ *
+ * This allows project nodes to be created from contexts (aliases)
+ */
+function devshop_projects_drush_context_import($context, &$node) {
+ if ($context->type == 'project') {
+
+ $node->title = $context->project_name;
+ $node->type = 'project';
+
+ $project = (object) $context->project;
+ $node->project = new stdClass();
+ $node->project->git_url = $project->git_url;
+ $node->project->code_path = $project->code_path;
+ $node->project->drupal_path = $project->drupal_path;
+ $node->project->base_url = $project->base_url;
+ $node->project->install_profile = $project->install_profile;
+ $node->project->settings = $project->settings;
+
+ foreach ($project->environments as $name => $environment) {
+ $environment = (object) $environment;
+
+ // Backwards compatibility: *_context properties were added, may be missing.
+ if (empty($environment->site_context)) {
+ $environment->site_context = $environment->uri;
+ }
+ if (empty($environment->platform_context)) {
+ $environment->platform_context = "platform_{$project->name}_{$environment->name}";
+ }
+
+ // Load site and platform nodes.
+ $site_node = hosting_context_load($environment->site_context);
+ $platform_node = hosting_context_load($environment->platform_context);
+
+ // If both site and platform nodes were found, load them in.
+ if ($site_node && $platform_node) {
+ drush_log(dt('Site and Environment nodes found. Saving environment data for site "!site_context" (nid:!site) and platform "!platform_context" (nid:!platform)', array(
+ '!site' => $site_node->nid,
+ '!platform' => $platform_node->nid,
+ '!site_context' => $environment->site_context,
+ '!platform_context' => $environment->platform_context,
+ )), 'success');
+ $environment_object = new stdClass();
+ $environment_object->name = $name;
+ $environment_object->site = $site_node->nid;
+ $environment_object->platform = $platform_node->nid;
+ $environment_object->settings = $environment->settings;
+
+ // Project environments only get automatically added on project insert.
+ if (!empty($node->nid)) {
+ $environment_object->project_nid = $node->nid;
+ $environment_object->settings = serialize($environment_object->settings);
+ devshop_environment_save($environment_object);
+ }
+ else {
+ // Project is being created, we can just append the environment.
+ $node->project->environments[$name] = $environment_object;
+ }
+ }
+ else {
+ // If site or platform is missing, look for drush context. If found, trigger import of that.
+ $t = array(
+ '!context' => $environment->site_context,
+ );
+ if (empty($site_node) && $context = d($environment->site_context) && $context->type == 'site') {
+ drush_log(dt('Site node not found, using context "!context" instead.', $t), 'warning');
+ hosting_drush_import($context);
+ }
+ else {
+ // @TODO: Should we create site context here?
+ drush_set_error(dt('No site node or context found for "!context". Create one with that name first if you want environments to be created.', $t));
+ }
+ if (empty($platform_node) && $context = d($environment->platform_context) && $context->type == 'platform') {
+ drush_log(dt('Platform node not found, using context "!context" instead.', array('!context' => $environment->platform_context)), 'warning');
+ hosting_drush_import($context);
+ }
+ else {
+ // @TODO: Should we create platform context here?
+ drush_set_error(dt('No platform node or context found for "!context". Create one with that name first if you want environments to be created.', array('!context' => $environment->platform_context)));
+ }
+ }
+ }
+ devshop_reset_last_tasks();
+ }
+
+ // If importing a site that has environment and project assigned,
+ elseif ($context->type == 'site' && !empty($context->environment) && !empty($context->project)) {
+
+ // If site, platform, and project already exist, update environment info from alias.
+ $project_nid = hosting_context_nid("project_{$context->project}");
+ $platform_nid = hosting_context_nid($context->platform);
+
+ if (!empty($node->nid) && $project_nid && $platform_nid) {
+
+ drush_log(dt('Found existing site environment (!environment), and project properties (!project). Saving environment.', array(
+ '!environment' => $context->environment,
+ '!project' => $context->project,
+ )), 'success');
+
+ $environment_object = new stdClass();
+ $environment_object->name = $context->environment;
+ $environment_object->site = $node->nid;
+ $environment_object->platform = $platform_nid;
+ $environment_object->settings = $context->environment_settings;
+ $environment_object->project_nid = $project_nid;
+
+ if (devshop_environment_save($environment_object)) {
+ drush_log(dt('Environment saved.'), 'success');
+ }
+ else {
+ drush_set_error('DEVSHOP_ENVIRONMENT_NOT_SAVED', dt('Environment not saved.'));
+ }
+ }
+
+ // Site node doesn't exist, will be created after this function.
+ elseif (empty($node->nid)) {
+
+ // If project node not found, try to load the context and import that.
+ if (empty($project_nid) && $context = d("project_{$context->project}") && $context->type == 'project') {
+ drush_log(dt('Project node not found, importing context "!context".', array('!context' => "project_{$context->project}")), 'warning');
+ hosting_drush_import($context);
+
+ }
+ // If there is a project nid, apply it as a property of the node so the insert hook can handle it.
+ elseif (!empty($project_nid)) {
+ $node->project_nid = $project_nid;
+ }
+ // If there is no project nid, and no project context, throw a warning.
+ else {
+ drush_log(dt('Site property "project" is set to "!project", but there is no project node or context by that name.'), 'warning');
+ }
+ }
+
+ devshop_reset_last_tasks();
+ }
+}
+
+/**
+ * Helpfer for getting branches and tags from a git URL
+ */
+function getBranchesAndTags($git_url = NULL) {
+ if (is_null($git_url)) {
+ $git_url = drush_get_option('git_url');
+ }
+ $command = "git ls-remote {$git_url}";
+ $output = provision_process($command);
+ $lines = explode("\n", $output);
+
+ // Build tag and branch list
+ $branches = array();
+ $tags = array();
+ $refs = array();
+
+ foreach ($lines as $line_string) {
+
+ // "annotated" tags come with an extra row and these characters at the end.
+ // See http://stackoverflow.com/questions/15472107/when-listing-git-ls-remote-why-theres-after-the-tag-name
+ if (substr($line_string, -3, 3) == '^{}') {
+ continue;
+ }
+
+ // Example remote line:
+ // 9fc5727c0823d8d3300ba5aae3328d5998033e45 refs/heads/master
+ // 9fc5727c0823d8d3300ba5aae3328d5998033e45 refs/tags/1.0
+ $line = trim(substr($line_string, 40));
+ if (empty($line) || $line == 'HEAD') {
+ continue;
+ }
+
+ // If branch
+ if (strpos($line, 'refs/heads/') === 0) {
+ $git_ref = str_replace('refs/heads/', '', $line);
+ $branches[] = $git_ref;
+ $refs[$git_ref] = 'branch';
+ }
+ // else if tag
+ elseif (strpos($line, 'refs/tags/') === 0) {
+ $git_ref = str_replace('refs/tags/', '', $line);
+ $tags[] = $git_ref;
+ $refs[$git_ref] = 'tag';
+ }
+ // If not a tag or a head, continue.
+ // @TODO: Should we store alternative types? GitHub Pull Requests use this.
+ else {
+ continue;
+ }
+ }
+ drush_log(dt('Found !count branches: !list', array(
+ '!count' => count($branches),
+ '!list' => implode(', ', $branches),
+ )), 'p_log');
+ drush_log(dt('Found !count tags: !list', array(
+ '!count' => count($tags),
+ '!list' => implode(', ', $tags),
+ )), 'p_log');
+
+ return array('branches' => $branches, 'tags' => $tags, 'refs' => $refs);
+}
diff --git a/modules/devshop/devshop_projects/drush/deploy.devshop.provision.inc b/modules/devshop/devshop_projects/drush/deploy.devshop.provision.inc
new file mode 100644
index 000000000..bb0aa4ca9
--- /dev/null
+++ b/modules/devshop/devshop_projects/drush/deploy.devshop.provision.inc
@@ -0,0 +1,149 @@
+type == 'site') {
+ if (empty(d()->environment)) {
+ return drush_set_error('DEVSHOP_FRAMEWORK_ERROR', 'This site is not a part of a project. You cannot use this command.');
+ }
+ }
+ else {
+ return drush_set_error('DEVSHOP_FRAMEWORK_ERROR', 'provision-devshop-deploy must be run on a site context.');
+ }
+
+ // Verify that the branch or tag exists
+ if (empty($git_ref)) {
+ return drush_set_error('DEVSHOP_FRAMEWORK_ERROR', 'You must specify a valid branch or tag.');
+ }
+
+ $project_alias = '@project_' . d()->project;
+ $project = (object) d($project_alias)->project;
+
+ if (!isset($project->settings['git']['refs'][$git_ref])) {
+ $drush_command = "drush $project_alias provision-verify";
+ return drush_set_error('DEVSHOP_FRAMEWORK_ERROR', "Branch or tag '$git_ref' not found. Try running '$drush_command' to fetch new remote branches or tags.");
+ }
+}
+
+/**
+ * Implements the provision-devshop-deploy command.
+ */
+function drush_devshop_provision_provision_devshop_deploy($git_ref = '')
+{
+ $project_name = d()->project;
+ $project = (object) d("@project_{$project_name}")->project;
+ $environment = (object) $project->environments[d()->environment];
+ $desired_ref_type = $project->settings['git']['refs'][$git_ref];
+
+ drush_log('[Current Working Directory]' . d()->platform->repo_path, 'p_log');
+
+ // Stash any changes? Not sure if we should do this anymore...
+ // $git->command('stash');
+
+ // Fetch
+ provision_process("git fetch --all", d()->platform->repo_path, dt('DevShop Deploy'));
+
+ // Checkout the chosen ref
+ $git_checkout_output = provision_process("git checkout {$git_ref}", d()->platform->repo_path, dt('DevShop Deploy'));
+ $git_checkout_output_lines = explode("\n", $git_checkout_output);
+
+ // Run Git Pull, if on a branch.
+ if ($desired_ref_type == 'branch') {
+ $git_pull_output = provision_process("git pull", d()->platform->repo_path, dt('DevShop Deploy'));
+ }
+
+ // Run a submodule update and init.
+ provision_process("git submodule update --init --recursive --force ", d()->platform->repo_path, dt('DevShop Deploy'));
+
+ provision_process("git status", d()->platform->repo_path, dt('DevShop Deploy'));
+}
+
+/**
+ * Post provision-devshop-deploy
+ */
+function drush_devshop_provision_post_provision_devshop_deploy($git_ref = '') {
+
+ // Get post deploy options
+ $revert = drush_get_option('revert');
+ $update = drush_get_option('update');
+ $cache = drush_get_option('cache');
+ $composer = drush_get_option('composer', TRUE);
+
+ $project_name = d()->project;
+ $project = (object) d("@project_{$project_name}")->project;
+ $environment = (object) $project->environments[d()->environment];
+ $desired_ref_type = $project->settings['git']['refs'][$git_ref];
+
+ // Ensure drush_alias exists. Not sure why it was missing for me.
+ if (empty($environment->drush_alias)) {
+ $environment->drush_alias = d()->name;
+ }
+
+ $commands = array();
+
+ drush_log("[{$project_name}] {$environment->name}: " . dt('Running deploy hooks.'), 'notice');
+
+ // Built in Hooks
+ if ($composer) {
+ if (file_exists(d()->platform->repo_path . '/composer.json')) {
+ $commands[] = "composer install";
+ }
+ elseif (file_exists($environment->root . '/composer.json')) {
+ $commands[] = "cd {$environment->root} && composer install";
+ }
+ }
+ else {
+ drush_log(dt('[DEVSHOP] Skipped running composer install...'), 'ok');
+ }
+
+ // Built in Hooks
+ if ($update) {
+ $commands[] = "drush {$environment->drush_alias} updatedb --yes";
+ }
+ else {
+ drush_log(dt('[DEVSHOP] Skipped updating database...'), 'ok');
+ }
+
+ // Clear the whole cache, unless option is false
+ if ($cache) {
+ if (drush_drupal_major_version(d()->root) == 8) {
+ $commands[] = "drush {$environment->drush_alias} cache-rebuild";
+ }
+ else {
+ $commands[] = "drush {$environment->drush_alias} cache-clear all";
+ }
+ }
+ else {
+ drush_log(dt('[DEVSHOP] Skipped clearing all caches...'), 'ok');
+ }
+
+ // Revert All Features, unless option is false
+ if ($revert) {
+ $commands[] = "drush {$environment->drush_alias} features-revert-all --yes";
+ }
+ else {
+ drush_log(dt('[DEVSHOP] Skipped reverting all features...'), 'ok');
+ }
+
+ foreach ($commands as $command) {
+ $output = provision_process($command, d()->platform->repo_path, 'DevShop Deploy Hook');
+
+ // Detect common errors and help the user.
+ // Composer install with incorrect PHP version.
+ if (strpos($command, 'composer install') !== FALSE && strpos($output, 'your PHP version') !== FALSE && strpos($output, 'does not satisfy that requirement') !== FALSE) {
+ drush_log(dt('Composer indicated that it could not install packages because the site codebase requires a higher version of PHP than what is running on the server.'), 'p_log');
+ drush_log(dt('You can either upgrade your version of PHP on this server or set the maximum PHP version in your composer.json file. See !link for instructions for how to modify your composer.json file, then make sure to run `composer update` on your codebase to pin your packages to a lower PHP version.', [
+ '!link' => 'https://getcomposer.org/doc/06-config.md#platform',
+ ]), 'p_log');
+ }
+
+ }
+}
+
diff --git a/modules/devshop/devshop_projects/drush/deploy_hooks_example/devshop.drush.inc b/modules/devshop/devshop_projects/drush/deploy_hooks_example/devshop.drush.inc
new file mode 100644
index 000000000..bb6fceb5e
--- /dev/null
+++ b/modules/devshop/devshop_projects/drush/deploy_hooks_example/devshop.drush.inc
@@ -0,0 +1,39 @@
+type == 'site'){
+
+ $project_name = d()->project;
+ $project = (object) d("@project_{$project_name}")->project;
+ $environment = (object) $project->environments[d()->environment];
+
+ drush_log("[CUSTOM] Successfully checked out $branch to environment $environment->name in project $project_name.", 'ok');
+
+// provision_backend_invoke(d()->name, 'status');
+
+ }
+}
diff --git a/modules/devshop/devshop_projects/drush/devshop-dev.make b/modules/devshop/devshop_projects/drush/devshop-dev.make
new file mode 100644
index 000000000..b9808342b
--- /dev/null
+++ b/modules/devshop/devshop_projects/drush/devshop-dev.make
@@ -0,0 +1,19 @@
+;
+; This makefile is used by the DevShop standalone installer to build devmaster.
+;
+
+core = 6.x
+api = 2
+
+projects[drupal][type] = "core"
+
+; DEVELOPMENT MODE:
+; When in development, use this:
+projects[devmaster][type] = "profile"
+projects[devmaster][download][type] = "git"
+projects[devmaster][download][url] = "git@git.drupal.org:project/devmaster.git"
+projects[devmaster][download][branch] = "6.x-1.x"
+
+; RELEASE:
+; When releasing, lock in the devmaster version.
+;projects[devmaster][version] = "6.x-1.0"
\ No newline at end of file
diff --git a/modules/devshop/devshop_projects/drush/devshop_provision.drush.inc b/modules/devshop/devshop_projects/drush/devshop_provision.drush.inc
new file mode 100644
index 000000000..664d637ab
--- /dev/null
+++ b/modules/devshop/devshop_projects/drush/devshop_provision.drush.inc
@@ -0,0 +1,555 @@
+ 'Deploys a tag or branch to an environment and (optionally) run update.php, clear cache, and revert features.',
+ 'bootstrap' => DRUSH_BOOTSTRAP_DRUSH,
+ 'options' => array(
+ 'update' => 'Run update.php after code pull.',
+ 'revert' => 'Revert all features after code pull.',
+ 'cache' => 'Clear all caches after code pull.',
+ 'reset' => 'Runs "git reset --hard" before pulling.',
+ 'force' => "Runs always update,revert and cache options, even if files don't change.",
+ 'test' => 'Queue a test run after the deploy. (Only works from Hostmaster)',
+ ),
+ 'arguments' => array(
+ 'git_ref' => 'The git branch or tag to deploy.',
+ ),
+ 'examples' => array(
+ 'drush @env.project.domain.com provision-devshop-deploy master --cache --update' => 'Triggers a git checkout & pull of branch master to the dev environment, clearing caches and running updates.',
+ ),
+ 'aliases' => array('deploy'),
+ );
+ $items['provision-test'] = array(
+ 'description' => 'Run a set of tests.',
+ 'bootstrap' => DRUSH_BOOTSTRAP_DRUSH,
+ 'options' => array(
+ 'tests-to-run' => 'The list of tests to run, separated by comma.',
+ 'test-type' => 'The type of tests to run. simpletest, behat',
+ 'behat-folder-path' => 'The path to this sites behat tests.',
+ 'behat-bin-path' => 'The path to the behat executable within behat-folder-path',
+ 'output-path' => 'The path to a folder to store results in. Will be created if it doesn\'t exist',
+ ),
+ 'aliases' => array('test'),
+ );
+ return $items;
+}
+
+/**
+ * Function for checking if this is a project and we have a repo.
+ *
+ * Used in pre drush command hooks
+ */
+function devshop_provision_pre_flight($platform_name = NULL){
+ if (d()->type != 'Project'){
+ return drush_set_error(DEVSHOP_FRAMEWORK_ERROR, 'All provision-devshop-* commands must be run on a project alias.');
+ }
+}
+
+/**
+ * Append PHP code to Drupal's settings.php file.
+ *
+ * To use templating, return an include statement for the template.
+ *
+ * @param $uri
+ * URI for the site.
+ * @param $data
+ * Associative array of data from provisionConfig_drupal_settings::data.
+ *
+ * @return
+ * Lines to add to the site's settings.php file.
+ *
+ * @see provisionConfig_drupal_settings
+ */
+function devshop_provision_provision_drupal_config($uri, $data, $config = NULL) {
+
+ $environment = d()->environment;
+ $project = d()->project;
+
+ return << NULL,
+ 'Process' => 'Process',
+ );
+}
+
+/**
+ * Implements hook_provision_apache_vhost_config()
+ *
+ * Adds "devshop_project" and "devshop_environment" server variables.
+ *
+ * @param $uri
+ * URI for the site.
+ * @param $data
+ * Associative array of data from Provision_Config_Apache_Site::data.
+ *
+ * @return
+ * Lines to add to the configuration file.
+ *
+ * @see Provision_Config_Apache_Site
+ * provision_logs_provision_apache_vhost_config
+ */
+function devshop_provision_provision_apache_vhost_config($uri, $data) {
+
+ $environment = d()->environment;
+ $project = d()->project;
+
+ if (empty($environment) || empty($project)) {
+ return;
+ }
+
+ return <<environment;
+ $project = d()->project;
+
+ if (empty($environment) || empty($project)) {
+ return;
+ }
+
+ return <<project) && empty(d()->environment)) {
+ drush_log('[DEVSHOP] New environment detected. Rebuilding Registry.', 'ok');
+ drush_invoke('registry-rebuild');
+ }
+}
+
+/**
+ * Find the username of the current running procses
+ *
+ * This will return the username of the current running user (as seen
+ * from posix_geteuid()) and should be used instead of
+ * get_current_user() (which looks at the file owner instead).
+ *
+ * @see get_current_user()
+ * @see posix_geteuid()
+ *
+ * @return
+ * String. The username.
+ */
+function devshop_current_user() {
+ return devshop_posix_username(posix_geteuid());
+}
+
+/**
+ * Check whether a user is a member of a group.
+ *
+ * @param user
+ * username or user id of user.
+ * @param group
+ * groupname or group id of group.
+ *
+ * @return
+ * Boolean. True if user does belong to group,
+ * and FALSE if the user does not belong to the group, or either the user or group do not exist.
+ */
+function devshop_user_in_group($user, $group) {
+ // TODO: make these singletons with static variables for caching.
+ $user = devshop_posix_username($user);
+ $group = devshop_posix_groupname($group);
+ if ($user && $group) {
+ $info = posix_getgrnam($group);
+ if (in_array($user, $info['members'])) {
+ return TRUE;
+ }
+ }
+ return FALSE;
+}
+
+/**
+ * Return the valid system username for $user.
+ *
+ * @return
+ * Returns the username if found, otherwise returns FALSE
+ */
+function devshop_posix_username($user) {
+ // TODO: make these singletons with static variables for caching.
+ // we do this both ways, so that the function returns NULL if no such user was found.
+ if (is_numeric($user)) {
+ $info = posix_getpwuid($user);
+ $user = $info['name'];
+ }
+ else {
+ $info = posix_getpwnam($user);
+ $user = $info['name'];
+ }
+ return $user;
+}
+
+/**
+ * Return the valid system groupname for $group.
+ *
+ * @return
+ * Returns the groupname if found, otherwise returns FALSE
+ */
+function devshop_posix_groupname($group) {
+ // TODO: make these singletons with static variables for caching.
+ // we do this both ways, so that the function returns NULL if no such user was found.
+ if (is_numeric($group)) {
+ $info = posix_getgrgid($group);
+ $group = $info['name'];
+ }
+ else {
+ $info = posix_getgrnam($group);
+ $group = $info['name'];
+ }
+ return $group;
+}
+
+/**
+ * Generate a random alphanumeric password.
+ *
+ * This is a copy of Drupal core's user_password() function. We keep it
+ * here in case we need this and don't have a bootstrapped Drupal
+ * around.
+ *
+ * @see user_password()
+ */
+function devshop_password($length = 10) {
+ // This variable contains the list of allowable characters for the
+ // password. Note that the number 0 and the letter 'O' have been
+ // removed to avoid confusion between the two. The same is true
+ // of 'I', 1, and 'l'.
+ $allowable_characters = 'abcdefghijkmnopqrstuvwxyzABCDEFGHJKLMNPQRSTUVWXYZ23456789';
+
+ // Zero-based count of characters in the allowable list:
+ $len = strlen($allowable_characters) - 1;
+
+ // Declare the password as a blank string.
+ $pass = '';
+
+ // Loop the number of times specified by $length.
+ for ($i = 0; $i < $length; $i++) {
+
+ // Each iteration, pick a random character from the
+ // allowable string and append it to the password:
+ $pass .= $allowable_characters[mt_rand(0, $len)];
+ }
+
+ return $pass;
+}
+
+function devshop_default_web_group() {
+ $info = posix_getgrgid(posix_getgid());
+ $common_groups = array(
+ 'www-data',
+ 'apache',
+ 'nginx',
+ 'www',
+ '_www',
+ 'webservd',
+ 'httpd',
+ 'nogroup',
+ 'nobody',
+ $info['name']);
+
+ foreach ($common_groups as $group) {
+ if (devshop_posix_groupname($group)) {
+ return $group;
+ break;
+ }
+ }
+ return NULL;
+}
+
+/**
+ * return the FQDN of the machine or provided host
+ *
+ * this replicates hostname -f, which is not portable
+ */
+function devshop_fqdn($host = NULL) {
+ if (is_null($host)) {
+ $host = php_uname('n');
+ }
+ return strtolower(gethostbyaddr(gethostbyname($host)));
+}
+
+//
+//if (!function_exists('provision_autoload_register_prefix')) {
+// /**
+// * Register a PECL style prefix with the provision autoloader.
+// *
+// * @param string $prefix
+// * The class prefix to register.
+// * @param string $dir
+// * The directory to search for the classes in.
+// * @param bool $prepend
+// * If the directory should be searched first for the classes in the given
+// * prefix, set this to TRUE, otherwise, the default, FALSE, is fine.
+// */
+// function provision_autoload_register_prefix($prefix, $dir, $prepend = FALSE) {
+//
+// // Get any current directories set for this prefix.
+// $current_prefixes = provision_autoload()->getPrefixes();
+// if (isset($current_prefixes[$prefix])) {
+// $dirs = $current_prefixes[$prefix];
+// }
+// else {
+// $dirs = array();
+// }
+//
+// // Now add the new one.
+// if ($prepend) {
+// array_unshift($dirs, $dir);
+// }
+// else {
+// array_push($dirs, $dir);
+// }
+//
+// // Set the prefixes.
+// provision_autoload()->addPrefix($prefix, $dirs);
+// }
+//}
+//
+//if (!function_exists('provision_autoload')) {
+// /**
+// * Return an instance of the provision autoloader.
+// *
+// * This will instiatate an instance if it needs to.
+// */
+// function provision_autoload() {
+// static $instance = NULL;
+//
+// if (is_null($instance)) {
+// $instance = new ClassLoader();
+// // Activate the autoloader.
+// $instance->register();
+// }
+//
+// return $instance;
+// }
+//}
+
+/**
+ * Implementation of drush_hook_provision_pre_COMMAND()
+ * Calls drush_devshop_provision_pre_provision_verify()
+ */
+function drush_devshop_provision_pre_provision_deploy() {
+ drush_devshop_provision_pre_provision_verify();
+}
+
+/**
+ * Implementation of drush_hook_provision_pre_COMMAND()
+ * for Verify tasks: Writes easier to use drush aliases for each project.
+ */
+function drush_devshop_provision_pre_provision_verify() {
+ if (d()->type === 'project' || (!empty(d()->project) && d()->type === 'site' || d()->type === 'platform')) {
+
+ }
+}
+
+/**
+ * Implementation of drush_hook_post_provision_install()
+ *
+ * This function handles all of the alternative environment install methods.
+ */
+function drush_devshop_provision_post_provision_install() {
+
+ // Install Method: Clone
+ // Sync from a drush alias.
+ drush_log('Installing environment with method: ' . d()->install_method , 'p_log');
+ if (d()->install_method == 'clone') {
+ drush_log(dt('Environment Clone initiated...'), 'p_log');
+ $clone_source = d()->environment_settings['install_method']['clone_source'];
+
+ // If "clone_source" is set to "_other", use the drush alias in "clone_source_drush".
+ if ($clone_source == '_other') {
+ $source = d()->environment_settings['install_method']['clone_source_drush'];
+ }
+ elseif (!empty($clone_source)) {
+ $source = d()->environment_settings['install_method']['clone_source'];
+ }
+ else {
+ return drush_set_error('DEVSHOP_ERROR', dt('Install_method"clone_source" was not set.'));
+ }
+
+ $destination = d()->name;
+ $command = "drush provision-sync $source $destination --database --files --update-uri --disable-rollback-backup --registry-rebuild";
+
+ // Build command based on environment settings.
+ if (d()->environment_settings['deploy']['update']) {
+ $command .= " --updatedb";
+ }
+ if (d()->environment_settings['deploy']['cache']) {
+ $command .= " --cache-clear";
+ }
+ if (d()->environment_settings['deploy']['revert']) {
+ $command .= " --features-revert-all";
+ }
+
+ provision_process($command, NULL, 'Cloning site from ' . d($source)->uri);
+ provision_process('drush cc drush', NULL, 'Clearing drush caches');
+// devshop_drush_process_cache_clear($destination);
+ }
+
+ // Install Method: import
+ elseif (d()->install_method == 'import') {
+
+
+ // If import source is a mysql URL:
+ $path_or_url = d()->environment_settings['install_method']['import'];
+ $url = parse_url($path_or_url);
+ if ($url['scheme'] == 'mysql') {
+
+ // Dump the file
+ $temp_file_name = tempnam('/tmp', 'devshop_remote_db.sql.');
+ $db_name = ltrim($url['path'], '/');
+ $command = "mysqldump -u{$url['user']} -p{$url['pass']} -h{$url['host']} {$db_name} --result-file={$temp_file_name}";
+ provision_process($command, NULL, dt('Saving database file...'));
+
+ $file_to_import = $temp_file_name;
+ }
+
+ // If file exists but is not readable, alert the user.
+ elseif (file_exists($path_or_url) && !is_readable($path_or_url)){
+ return drush_set_error('DEVSHOP_IMPORT_ERROR', dt('Unable to import SQL file @path: not readable.'));
+ }
+
+ // If file exists and is readable, set it as the file to import.
+ elseif (file_exists($path_or_url) && is_readable($path_or_url)){
+ $file_to_import = $path_or_url;
+ }
+
+ // Import the file
+ $alias = d()->name;
+ $command = "drush {$alias} sqlq 'SOURCE {$file_to_import}'";
+ provision_process($command, NULL, dt('Importing database file...'));
+ provision_process('drush cc drush', NULL, 'Clearing drush caches');
+ devshop_drush_process_cache_clear($alias);
+ provision_process("drush {$alias} sqlq 'SHOW TABLES'", NULL, dt('Show Tables'));
+ }
+}
+
+/**
+ * Runs drush cache-clear or drush cache-rebuild on the chosen alias.
+ *
+ * @param $alias
+ * A drush alias.
+ */
+function devshop_drush_process_cache_clear($alias) {
+
+ // Clear all caches.
+ if (drush_drupal_major_version(d()->root) == 8) {
+ provision_process("drush {$alias} cache-rebuild", NULL, dt('Clear all Caches'));
+ }
+ else {
+ provision_process("drush {$alias} cache-clear all", NULL, dt('Clear all Caches'));
+ }
+}
diff --git a/modules/devshop/devshop_projects/drush/install.devshop.inc b/modules/devshop/devshop_projects/drush/install.devshop.inc
new file mode 100644
index 000000000..e1e09b454
--- /dev/null
+++ b/modules/devshop/devshop_projects/drush/install.devshop.inc
@@ -0,0 +1,289 @@
+ join("\n", drush_shell_exec_output()))));
+ }
+}
+
+function drush_provision_devshop_install_validate($site = NULL) {
+ // set defaults for this whole script
+ // those are settings that are not prompted to the user but still overridable
+ drush_set_default('version', provision_version());
+ drush_set_default('profile', 'devmaster');
+
+ // Get values needed to set other defaults
+ $version = drush_get_option('version');
+ $aegir_root = drush_set_default('aegir_root', drush_server_home());
+ $profile = drush_get_option('profile');
+
+ drush_set_default('root', $aegir_root . '/' . $profile . '-' . $version);
+ drush_set_default('r', drush_get_option('root'));
+ drush_set_default('script_user', devshop_current_user());
+ drush_set_default('web_group', _provision_default_web_group());
+ drush_set_default('http_service_type', 'apache');
+ drush_set_default('http_port', '80');
+ drush_set_default('aegir_db_user', 'root');
+ drush_set_default('aegir_db_port', '3306');
+ drush_set_default('client_name', 'admin');
+ $aegir_db_user = drush_get_option('aegir_db_user');
+
+ // Generate "makefile" message only if there is one set.
+ $root = drush_get_option(array('r', 'root'));
+ if (is_dir($root) && !drush_get_option('makefile', FALSE)) {
+ // Don't assume we know the makefile used to build an existing platform
+ $makefile_msg = '';
+ }
+ else {
+ drush_set_default('makefile', dirname(__FILE__) . '/devshop-dev.make');
+ $makefile_msg = dt("Aegir makefile: !makefile\n", array('!makefile' => drush_get_option('makefile')));
+ }
+
+ // Generate "profile" message only if there is one set.
+ if (!drush_get_option('profile', FALSE)) {
+ $profile_msg = '';
+ }
+ else {
+ $profile_msg = dt("Aegir install profile: !profile\n", array('!profile' => $profile));
+ }
+
+ drush_print("Aegir $version automated install script");
+ drush_print("==============================================================================");
+
+ if (!$site || !drush_get_option('aegir_host', NULL) || !drush_get_option('aegir_db_pass', NULL) || filter_var(drush_get_option('client_email'), FILTER_VALIDATE_EMAIL)) {
+ drush_print("Some settings have not been provided and will now be prompted.
+Don't worry: you will get to review those settings after the final install");
+ }
+ // now we prompt the user for settings if not provided or not sane
+ if (!$site) {
+ $site = drush_prompt(dt("Aegir frontend URL"), get_fqdn());
+ }
+ drush_set_option('site', $site);
+
+ drush_set_default('aegir_host', get_fqdn());
+ drush_set_default('aegir_db_host', 'localhost');
+
+ if (is_null(drush_get_option('aegir_db_pass', NULL))) {
+ // XXX: may not be portable everywhere?
+ system('stty -echo');
+ drush_set_option('aegir_db_pass', drush_prompt(dt('MySQL privileged user ("!root") password', array('!root' => $aegir_db_user))));
+ system('stty echo');
+ print "\n"; // add a newline since the user's didn't print
+ }
+
+ if (drush_get_option('aegir_host') == 'localhost') {
+ $default_email = 'webmaster@example.com';
+ } else {
+ $default_email = 'webmaster@' . drush_get_option('aegir_host');
+ }
+ drush_set_default('client_email', $default_email);
+ while (!filter_var(drush_get_option('client_email'), FILTER_VALIDATE_EMAIL) && !drush_get_context('DRUSH_AFFIRMATIVE')) {
+ drush_set_option('client_email', drush_prompt(dt("Admin user e-mail"), $default_email));
+ }
+
+ drush_print(dt('
+This script will operate the following changes in your system:
+
+1. Create server-level configuration directories
+2. Create the Hostmaster frontend platform
+3. Install the frontend site
+4. Setup the dispatcher (a user cron job)
+
+We are making the following assumptions:
+ * you have read and are following the install instructions at:
+ http://community.aegirproject.org/installing
+ * the FQDN of this machine is valid and resolves
+ * you are executing this script as your "aegir" user
+
+The following settings will be used:
+ Aegir frontend URL: !site
+ Master server FQDN: !fqdn
+ Aegir root: !home
+ Aegir user: !user
+ Web group: !web
+ Web server: !web_server
+ Web server port: !web_server_port
+ Aegir DB host: !db_host
+ Aegir DB user: !db_user
+ Aegir DB password: !db_pass
+ Aegir DB port: !db_port
+ Aegir version: !version
+ Aegir platform path: !root
+ Admin email: !email
+ !makefile !profile',
+ array(
+ '!site' => $site,
+ '!fqdn' => drush_get_option('aegir_host'),
+ '!home' => drush_get_option('aegir_root'),
+ '!user' => drush_get_option('script_user'),
+ '!web' => drush_get_option('web_group'),
+ '!web_server' => drush_get_option('http_service_type'),
+ '!web_server_port' => drush_get_option('http_port'),
+ '!db_host' => drush_get_option('aegir_db_host'),
+ '!db_user' => drush_get_option('aegir_db_user'),
+ '!db_pass' => is_null(drush_get_option('aegir_db_pass', NULL, 'process')) ? '' : '',
+ '!db_port' => drush_get_option('aegir_db_port'),
+ '!version' => drush_get_option('version'),
+ '!root' => $root,
+ '!makefile' => $makefile_msg,
+ '!profile' => $profile_msg,
+ '!email' => drush_get_option('client_email'),
+ )));
+
+ if (!drush_confirm(dt('Do you really want to proceed with the install'))) {
+ return drush_set_error('PROVISION_CANCEL_INSTALL', dt('Installation aborted'));
+ }
+
+ return TRUE;
+}
+
+/**
+ * Drush command to install hostmaster.
+ */
+function drush_provision_devshop_install($site = NULL) {
+ $version = drush_get_option('version');
+ $site = drush_get_option('site', get_fqdn());
+ $aegir_root = drush_get_option('aegir_root');
+ $platform = drush_get_option(array('r', 'root'));
+
+ $aegir_http_host = drush_get_option('aegir_host');
+ $aegir_http_port = drush_get_option('http_port');
+ $aegir_db_user = drush_get_option('aegir_db_user');
+ $aegir_db_pass = drush_get_option('aegir_db_pass');
+ $aegir_db_port = drush_get_option('aegir_db_port');
+ $aegir_db_host = drush_get_option('aegir_db_host');
+
+ $server = '@server_master';
+ $master_context = array(
+ 'context_type' => 'server',
+ // files
+ 'remote_host' => $aegir_http_host,
+ 'aegir_root' => $aegir_root,
+ 'script_user' => drush_get_option('script_user'),
+ // apache or nginx or..
+ 'http_service_type' => drush_get_option('http_service_type'),
+ 'http_port' => $aegir_http_port,
+ 'web_group' => drush_get_option('web_group'),
+ 'master_url' => "http://" . $site,
+ 'db_port' => $aegir_db_port,
+ );
+
+ $master_db = sprintf("mysql://%s:%s@%s:%s", urlencode($aegir_db_user), urlencode($aegir_db_pass), $aegir_db_host, $aegir_db_port);
+ if ($aegir_http_host == $aegir_db_host) {
+ $master_context['db_service_type'] = 'mysql';
+ $master_context['master_db'] = $master_db;
+ $dbserver = $server;
+ } else {
+ $dbserver = '@server_' . $aegir_db_host;
+ $dbserver_context = array(
+ 'remote_host' => $aegir_db_host,
+ 'context_type' => 'server',
+ 'db_service_type' => 'mysql',
+ 'master_db' => $master_db,
+ 'db_port' => $aegir_db_port,
+ );
+ drush_invoke_process('@none', "provision-save", array($dbserver), $dbserver_context);
+ provision_backend_invoke($dbserver, 'provision-verify');
+ }
+ drush_invoke_process('@none', "provision-save", array($server), $master_context);
+ provision_backend_invoke($server, 'provision-verify');
+
+ // exit if an error has occured.
+ if (drush_get_error()) {
+ return false;
+ }
+
+ if (drush_get_option('backend-only')) {
+ return;
+ }
+
+ $platform_name = '@platform_hostmaster';
+ drush_invoke_process('@none', "provision-save", array($platform_name), array(
+ 'context_type' => 'platform',
+ 'server' => $server,
+ 'web_server' => $server,
+ 'root' => $platform,
+ 'makefile' => drush_get_option('makefile'),
+ ));
+ // propagate working-copy args downward
+ $options = array();
+ if (drush_get_option('working-copy')) {
+ $options['working-copy'] = 1;
+ }
+ provision_backend_invoke($platform_name, 'provision-verify', array(), $options);
+
+ // exit if an error has occured.
+ if (drush_get_error()) {
+ return false;
+ }
+
+ drush_set_default('profile', 'devmaster');
+ $profile = drush_get_option('profile');
+
+ $site_name = '@hostmaster';
+ drush_invoke_process('@none', "provision-save", array($site_name), array(
+ 'context_type' => 'site',
+ 'platform' => $platform_name,
+ 'db_server' => $dbserver,
+ 'uri' => $site,
+ 'client_name' => drush_get_option('client_name'),
+ 'profile' => $profile,
+ ));
+ $data = provision_backend_invoke($site_name, 'provision-install', array(), array('client_email' => drush_get_option('client_email')));
+ provision_backend_invoke($site_name, 'provision-verify');
+
+ // exit if an error has occured.
+ if (drush_get_error()) {
+ return false;
+ }
+
+
+ drush_print(dt("Initializing the hosting system"));
+ drush_invoke_process('@none', 'cache-clear', array('drush'));
+ provision_backend_invoke($site_name, 'hosting-setup');
+
+ drush_print("");
+ drush_print("==============================================================================");
+ drush_print("");
+ drush_print("");
+ drush_print(dt("Congratulations, Aegir has now been installed."));
+ drush_print("");
+ drush_print(dt("You should now log in to the Aegir frontend by opening the following link in your web browser:"));
+ drush_print("");
+ drush_print($data['context']['login_link']);
+ drush_print("");
+ drush_print("");
+ drush_print("==============================================================================");
+ drush_print("");
+}
+
+/**
+ * Implements drush_hook_post_hostmaster_install().
+ */
+function drush_provision_post_devshop_install() {
+ $backend_only = drush_get_option('backend-only');
+ if (empty($backend_only)) {
+ drush_invoke_process('@hostmaster', 'cache-clear', array('drush'));
+ }
+}
diff --git a/modules/devshop/devshop_projects/drush/pull.devshop.provision.inc b/modules/devshop/devshop_projects/drush/pull.devshop.provision.inc
new file mode 100644
index 000000000..2d2e7fdc4
--- /dev/null
+++ b/modules/devshop/devshop_projects/drush/pull.devshop.provision.inc
@@ -0,0 +1,123 @@
+project_name;
+ $platform_alias = '@' . implode('_', array('platform', $project_name, $platform_name));
+ $platform = d($platform_alias);
+
+ $site_alias = '@' . $platform_name . '.' . d()->project['base_url'];
+ $site = d($site_alias);
+
+ // Find repo path
+ $repo_path = $platform->root;
+
+ // Ensure it's a git repo.
+ provision_git_is_repo($repo_path);
+
+ // Determine revert setting from project settings (or if set as option, always)
+ $reset = drush_get_option('reset') ? drush_get_option('reset') : FALSE;
+
+ // If reset is true, do a git reset --hard first.
+ if ($reset) {
+ drush_shell_cd_and_exec($repo_path, 'git reset --hard');
+ drush_log(dt('[DEVSHOP] Git repository reset.'), 'ok');
+ }
+
+ // Pull latest version of site
+ // Execute git pull --rebase
+ if (drush_shell_cd_and_exec($repo_path, 'git pull --rebase')) {
+ drush_log(dt('[DEVSHOP] Git repository pulled.', array('!path' => $repo_path)), 'ok');
+ $output = drush_shell_exec_output();
+ drush_log(implode("\n", drush_shell_exec_output()), 'ok');
+ }
+ else {
+ drush_set_error('DRUSH_PROVISION_GIT_PULL_FAILED', dt("Git pull failed in !path.\nThe specific errors are below:\n!errors", array('!path' => $repo_path, '!errors' => implode("\n", drush_shell_exec_output()))));
+ continue;
+ }
+
+ // If no new files were detected... and force is false then skip out.
+ if (count($output) == 1 && !$force) {
+ drush_log('[DEVSHOP] No changes detected. Nothing else needs to be done', 'ok');
+ continue;
+ }
+
+ // Verify the platform.
+ provision_backend_invoke($platform_alias, 'provision-verify');
+
+ // update db, unless option is false.
+ if ($update){
+ drush_log(dt('[DEVSHOP] Updating database...'), 'ok');
+ provision_backend_invoke($site_alias, 'updb');
+ }
+ else {
+ drush_log(dt('[DEVSHOP] Skipped updating database...'), 'ok');
+ }
+
+ // Revert All Features, unless option is false
+ if ($revert){
+ drush_log(dt('[DEVSHOP] Reverting all features...'), 'ok');
+ provision_backend_invoke($site_alias, 'features-revert-all');
+ }
+ else {
+ drush_log(dt('[DEVSHOP] Skipped reverting all features...'), 'ok');
+ }
+
+ // Clear the whole cache, unless option is false
+ // Seriously, lets do this twice. Go Drupal!
+ if ($cache){
+ drush_log(dt('[DEVSHOP] Clearing all caches...'), 'ok');
+ provision_backend_invoke($site_alias, 'cc all');
+ provision_backend_invoke($site_alias, 'cc all');
+ }
+ else {
+ drush_log(dt('[DEVSHOP] Skipped clearing all caches...'), 'ok');
+ }
+ }
+}
+
diff --git a/modules/devshop/devshop_projects/drush/test.devshop.provision.inc b/modules/devshop/devshop_projects/drush/test.devshop.provision.inc
new file mode 100644
index 000000000..e69de29bb
diff --git a/modules/devshop/devshop_projects/drush/test.provision.inc b/modules/devshop/devshop_projects/drush/test.provision.inc
new file mode 100644
index 000000000..e46580e3d
--- /dev/null
+++ b/modules/devshop/devshop_projects/drush/test.provision.inc
@@ -0,0 +1,182 @@
+project;
+ $project = (object) d("@project_{$project_name}")->project;
+ $environment = (object) $project->environments[d()->environment];
+
+ drush_log(dt('Provision DevShop Run Tests started...'), 'status');
+
+ // Get tests to run
+ if (!empty($project->settings['testing']['test_type'])) {
+ $type = $project->settings['testing']['test_type'];
+ }
+ else {
+ $type = drush_get_option('test-type', NULL);
+ }
+
+ if ($project->settings['testing']['tests_to_run'] === NULL) {
+ $tests = array();
+ }
+ else {
+ $tests = array_filter($project->settings['testing']['tests_to_run']);
+ }
+ $tests_to_run = drush_get_option('tests-to-run', $tests);
+
+ // Run Simpletest
+ if ($type == 'simpletest') {
+ drush_log(dt("Running $type tests $tests_to_run"), 'ok');
+ provision_backend_invoke('@self', 'en simpletest');
+ provision_backend_invoke('@self', 'test-run', array($tests_to_run));
+ }
+ elseif ($type == 'behat') {
+ // Get paths from options or site context.
+ $repo_path = d()->platform->repo_path? d()->platform->repo_path: d()->platform->root;
+ $behat_folder_path = drush_get_option('behat-folder-path', $project->settings['testing']['behat_folder_path']);
+
+ // If no repo at that path, error.
+ if (!file_exists($repo_path)){
+ return drush_set_error('DEVSHOP_MISSING_FILE', dt('repo_path does not exist.'));
+ }
+ // If no file at behat bin path, error.
+ $tests_folder = $repo_path . '/' .$behat_folder_path;
+ $yml_path = $repo_path . '/' .$behat_folder_path . '/behat.yml';
+ if (!file_exists($yml_path)){
+
+ $message = << Testing.
+ 2. Copy the files from https://github.com/opendevshop/devmaster/tree/1.x/modules/devshop/devshop_testing/tests_example
+ 3. Write more tests!
+
+TXT;
+
+
+ drush_log(dt($message, array('!path' => $tests_folder)), 'error');
+ return drush_set_error('DEVSHOP_MISSING_FILE', dt('Your project is not yet ready to run Behat tests. Please follow the instructions and try again.'));
+ }
+
+ // Prepare path and command
+ $full_behat_folder_path = $repo_path . '/' . $behat_folder_path;
+ $full_behat_bin_path = $repo_path . '/' . $behat_folder_path . '/bin/behat';
+ $full_behat_yml_path = $full_behat_folder_path . '/behat.yml';
+
+ // Load the behat.yml from behat_folder_path.
+ if (file_exists($full_behat_yml_path)) {
+ $behat_yml = file_get_contents($full_behat_yml_path);
+ $behat_yml_data = \Symfony\Component\Yaml\Yaml::parse($behat_yml);
+ }
+ elseif (file_exists($full_behat_folder_path . '/config/behat.yml')) {
+ $full_behat_yml_path = $full_behat_folder_path . '/config/behat.yml';
+ $behat_yml = file_get_contents($full_behat_yml_path);
+ $behat_yml_data = \Symfony\Component\Yaml\Yaml::parse($behat_yml);
+ }
+ else {
+ return drush_set_error('DEVSHOP_MISSING_FILE', dt('behat.yml file not found in behat_folder_path: ') . $behat_folder_path);
+ }
+
+ // Run composer install.
+ provision_process('composer install', $full_behat_folder_path, 'DevShop Testing');
+
+ // Write custom behat.yml to temporary folder.
+ $environment_name = $environment->name;
+ $alias = d()->name;
+ $root = d()->root;
+
+ $username = d()->http_basic_auth_username;
+ $password = d()->http_basic_auth_password;
+ $uri = d()->uri;
+ $url = d()->ssl_enabled?
+ "https://$uri":
+ "http://$uri";
+
+ if (!empty($username)) {
+ $url = d()->ssl_enabled?
+ "https://$username:$password@$uri":
+ "http://$username:$password@$uri";
+ }
+
+ $behat_params = array(
+ 'extensions' => array(
+ 'Behat\\MinkExtension' => array(
+ 'base_url' => $url,
+ ),
+ 'Drupal\\DrupalExtension' => array(
+ 'drupal' => array(
+ "drupal_root" => $root,
+ ),
+ 'drush' => array(
+ "alias" => $alias,
+ )
+ ),
+ ),
+ );
+
+ // Check the behat.yml file for options that will break automation.
+ if ($behat_yml_data['default']['extensions']['Behat\MinkExtension']['base_url']) {
+ drush_log(dt("Your project's behat.yml file includes the entry default.extensions.Behat\MinkExtension.base_url, preventing devshop from automating this test run. Please remove this entry to continue."), 'error');
+ }
+
+ if ($behat_yml_data['default']['extensions']['Drupal\DrupalExtension']['drupal']['drupal_root']) {
+ drush_log(dt("Your project's behat.yml file includes the entry default.extensions.Drupal\DrupalExtension.drupal.drupal_root, preventing devshop from automating this test run. Please remove this entry to continue."), 'error');
+ }
+
+ if ($behat_yml_data['default']['extensions']['Drupal\DrupalExtension']['drush']['drupal_root']) {
+ drush_log(dt("Your project's behat.yml file includes the entry default.extensions.Drupal\DrupalExtension.drupal.drupal_root, preventing devshop from automating this test run. Please remove this entry to continue."), 'error');
+ }
+
+ // Run behat tests for each feature.
+ $no_errors = TRUE;
+ $test_result = '';
+
+ // Fill an empty item if empty so we run all the tests.
+ if (empty($tests_to_run)) {
+ $tests_to_run[] = '';
+ }
+
+ // Foreach test to run...
+ $i = 0;
+ foreach ($tests_to_run as $feature) {
+ $i++;
+
+ // Check for path.
+ if (!empty($feature) && substr($feature, 0, 1) !== '/') {
+ $feature = '/'.$feature;
+ }
+ $feature_path = empty($feature) ? '' : "features{$feature}";
+
+ // Create Command
+ // The extra profile is used so we can dynamically set the URL and drush alias of the behat.yml.
+ // the "colors" option is to force it to output ANSI colors. "format-settings" "expand: true" is so
+ // Behat will output all of the steps when using "Scenario Outlines".
+ $cmd = "$full_behat_bin_path $feature_path --colors --strict --format-settings='{\"expand\": true}'";
+
+ // Run behat command
+ provision_process($cmd, $full_behat_folder_path, 'DevShop Testing', array(
+ 'BEHAT_PARAMS' => json_encode($behat_params, JSON_FORCE_OBJECT),
+ ), TRUE, 'Test Run Failed');
+ }
+ }
+}
+
diff --git a/modules/devshop/devshop_projects/images/loader.gif b/modules/devshop/devshop_projects/images/loader.gif
new file mode 100644
index 000000000..5b33f7e54
Binary files /dev/null and b/modules/devshop/devshop_projects/images/loader.gif differ
diff --git a/modules/devshop/devshop_projects/images/loading.gif b/modules/devshop/devshop_projects/images/loading.gif
new file mode 100644
index 000000000..bd6dd9741
Binary files /dev/null and b/modules/devshop/devshop_projects/images/loading.gif differ
diff --git a/modules/devshop/devshop_projects/images/spinner-white.gif b/modules/devshop/devshop_projects/images/spinner-white.gif
new file mode 100644
index 000000000..f27d7cd48
Binary files /dev/null and b/modules/devshop/devshop_projects/images/spinner-white.gif differ
diff --git a/modules/devshop/devshop_projects/inc/add-environment-options.tpl.php b/modules/devshop/devshop_projects/inc/add-environment-options.tpl.php
new file mode 100644
index 000000000..e69de29bb
diff --git a/modules/devshop/devshop_projects/inc/admin.inc b/modules/devshop/devshop_projects/inc/admin.inc
new file mode 100644
index 000000000..4896a0ab2
--- /dev/null
+++ b/modules/devshop/devshop_projects/inc/admin.inc
@@ -0,0 +1,134 @@
+ 1) {
+ $options[$line[0]] = $line[1];
+ }
+ else {
+ $options[] = $line[0];
+ }
+ }
+ form_set_value($element, array_filter($options),$form_state);
+}
+
+/**
+ * Projects Settings Page
+ *
+ * All code for admin interface.
+ */
+function devshop_projects_settings_form($form, &$form_state) {
+ $form = array();
+
+ $form['projects'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Projects'),
+ );
+ $form['projects']['devshop_git_repo_suggestions'] = array(
+ '#title' => t('Suggested Git Repositories'),
+ '#description' => t('Enter Git URLs, one per line. When creating a new project, these Git Repositories will available to use as a starting codebase.'),
+ '#type' => 'textarea',
+ '#default_value' => implode(PHP_EOL, variable_get('devshop_git_repo_suggestions', array(
+ 'git@github.com:opendevshop/devshop-composer-template.git'
+ ))),
+ '#element_validate' => array('devshop_element_validate_explode_array'),
+ );
+ $form['projects']['devshop_composer_project_suggestions'] = array(
+ '#title' => t('Suggested Composer Project'),
+ '#description' => t('Enter composer project names, one per line. These projects will be available to users creating new DevShop Project Git repositories.'),
+ '#type' => 'textarea',
+ '#default_value' => implode(PHP_EOL, variable_get('devshop_composer_project_suggestions', array(
+ 'devshop/composer-template:8.x-dev'
+ ))),
+ '#element_validate' => array('devshop_element_validate_explode_array'),
+ );
+
+ $form['paths'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Paths'),
+ );
+ $form['paths']['devshop_projects_allow_custom_code_path'] = array(
+ '#title' => t('Allow custom code path per project.'),
+ '#description' => t('Allow each project to have a custom "Code Path". If not checked, project paths are set as "/var/aegir/projects/{PROJECT_NAME}.'),
+ '#type' => 'checkbox',
+ '#default_value' => variable_get('devshop_projects_allow_custom_code_path', FALSE),
+ );
+ $form['paths']['devshop_project_base_path'] = array(
+ '#title' => t('Projects Base Path'),
+ '#type' => 'textfield',
+ '#description' => t('The default base path that all projects will be created in. Projects each get their own folder inside this path.'),
+ '#default_value' => variable_get('devshop_project_base_path', '/var/aegir/projects'),
+ );
+ $form['paths']['devshop_project_default_drupal_path'] = array(
+ '#title' => t('Default Document Root'),
+ '#type' => 'textfield',
+ '#description' => t("If index.php isn't in the root of the git repo, you can edit the 'Path to Drupal' setting on each project. Set a default 'Path to Drupal' here. (For example, an Acquia hosted repo uses 'docroot'.)"),
+ '#default_value' => variable_get('devshop_project_default_drupal_path', ''),
+ );
+
+
+ $form['urls'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('URLs'),
+ );
+
+ $form['urls']['devshop_projects_allow_changing_project_git_url'] = array(
+ '#title' => t("Allow changing a project's Default Git URL."),
+ '#description' => t("When editing a project, do not allow changes to the Git URL."),
+ '#type' => 'checkbox',
+ '#default_value' => variable_get('devshop_projects_allow_changing_project_git_url', TRUE),
+ );
+ $form['urls']['devshop_projects_allow_custom_base_url'] = array(
+ '#title' => t('Allow custom Environment Domain Name Pattern per project.'),
+ '#description' => t('If enabled each project can set an Environment URL Pattern.'),
+ '#type' => 'checkbox',
+ '#default_value' => variable_get('devshop_projects_allow_custom_base_url', FALSE),
+ );
+ $form['urls']['devshop_project_environment_url_pattern'] = array(
+ '#title' => t('Environment Domain Name Pattern'),
+ '#type' => 'textfield',
+ '#description' => t("Each environment will have a system domain name generated for it based on it's name. Use @project for project name, @hostname for '%host', @environment for the environment's name.", array('%host' => $_SERVER['SERVER_NAME'])),
+ '#default_value' => variable_get('devshop_project_environment_url_pattern', '@project.@environment.@hostname'),
+ '#element_validate' => array(
+ 'devshop_project_settings_validate_environment_url_pattern',
+ ),
+ );
+
+// $form['support'] = array(
+// '#type' => 'fieldset',
+// '#title' => t('DevShop Support'),
+// );
+// $form['support']['devshop_support_widget_enable'] = array(
+// '#title' => t('Show Help Widget'),
+// '#description' => t('Uncheck this box if you want to hide the Help widget that appears at the bottom right of the page.'),
+// '#type' => 'checkbox',
+// '#default_value' => variable_get('devshop_support_widget_enable', TRUE),
+// );
+
+ // Server settings.
+ $form['server'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('DevShop Server'),
+ );
+ $form['server']['devshop_public_key'] = array(
+ '#title' => t('Aegir User Public Key'),
+ '#description' => t('The public key of the aegir user on this server. If you change the SSH keys located at /var/aegir/.ssh/id_rsa.pub, then you should update this variable. This variable is for user reference only. If using GitHub API, then devshop will check to make sure the server has access using this public key.'),
+ '#type' => 'textarea',
+ '#default_value' => variable_get('devshop_public_key', ''),
+ );
+ return system_settings_form($form);
+}
+
+/**
+ * Validate that the default environment domain pattern has @environment and @project.
+ */
+function devshop_project_settings_validate_environment_url_pattern($element, &$form_state, $form) {
+ if (strpos($form_state['values']['devshop_project_environment_url_pattern'], '@environment') === FALSE || strpos($form_state['values']['devshop_project_environment_url_pattern'], '@project') === FALSE) {
+ form_error($element, t('The placeholders @project and @environment must be in the Environment Domain Name Pattern.'));
+ }
+}
\ No newline at end of file
diff --git a/modules/devshop/devshop_projects/inc/create-wizard.inc b/modules/devshop/devshop_projects/inc/create-wizard.inc
new file mode 100644
index 000000000..a4abe2daf
--- /dev/null
+++ b/modules/devshop/devshop_projects/inc/create-wizard.inc
@@ -0,0 +1,2 @@
+ NULL,
+ );
+
+ // Setup project
+ $project = ctools_object_cache_get('project', 'devshop_project');
+
+ // Setup Step.
+ if ($step == NULL) {
+ drupal_goto('projects/add/' . current(array_keys($form_info['order'])));
+ }
+
+ // Create default project object
+ if (empty($project) || !isset($project->git_url)) {
+ // set form to first step -- we have no data
+ $step = current(array_keys($form_info['order']));
+ $project = new stdClass();
+ $project->step = $step;
+ $project->git_url = '';
+ $project->nid = NULL;
+ $project->name = '';
+
+ // These are empty until we have our project name
+ $project->code_path = '';
+ $project->base_url = '';
+
+ // ** set the storage object so its ready for whatever comes next
+ ctools_object_cache_set('project', 'devshop_project', $project);
+ }
+ else {
+ // Load latest project object from database and use that for the ctools cache.
+ $project_node = node_load($project->nid);
+ $project = $project_node->project;
+
+ // If we have a name but no code_path or base URL.
+ if (!empty($project->name) && empty($project->code_path)) {
+ $project->code_path = variable_get('devshop_project_base_path', '/var/aegir/projects') . '/' . $project->name;
+ }
+ if (!empty($project->name) && empty($project->base_url)) {
+ $project->base_url = variable_get('devshop_project_environment_url_pattern', "@project.@environment.@hostname");
+ }
+
+ // Save the current step
+ $project->step = $step;
+ ctools_object_cache_set('project', 'devshop_project', $project);
+ }
+
+ // Check verification status
+ if (!empty($project->nid)) {
+ $tasks = hosting_task_fetch_tasks($project->nid);
+ }
+ if (isset($tasks['verify']['nid'])) {
+ // Get "verify" task status for the project
+ $project->verify_task_status = isset($tasks['verify']['task_status']) ? $tasks['verify']['task_status'] : HOSTING_TASK_ERROR;
+ $project->verify_task_nid = $tasks['verify']['nid'];
+ }
+ else {
+ $project->verify_task_status = NULL;
+ $project->verify_task_nid = NULL;
+ }
+
+ // If project verification failed, we need to ask for a new git url.
+ if ($project->verify_task_status == HOSTING_TASK_ERROR && !empty($project_node->nid)) {
+ $messages = db_query("SELECT * FROM {hosting_task_log} WHERE nid = :nid AND type LIKE :type ORDER BY vid, lid", array(':nid' => $project->verify_task_nid, ':type' => 'p_%'));
+
+ foreach ($messages as $message) {
+ if ($message->type == 'p_info') {
+ $messages_to_user[] = $message->message;
+ }
+ }
+ $project->verify_error = theme('devshop_ascii', array('output' => implode('\n', $messages_to_user)));
+
+
+ // If not on the first step, go to it.
+ if ($step != current(array_keys($form_info['order']))) {
+ drupal_goto('projects/add/' . current(array_keys($form_info['order'])));
+ }
+ }
+ else {
+ $project->verify_error = NULL;
+ }
+
+ // All forms can access $form_state['project'];
+ $form_state['project'] = $project;
+
+ // Saving the last visited step for redirects from node
+ $_SESSION['last_step'] = $step;
+
+ // Generate our ctools form and output
+ $output = ctools_wizard_multistep_form($form_info, $step, $form_state);
+ return $output;
+}
+
+/**
+ * The form_info for the ctools wizard
+ */
+function devshop_projects_create_wizard_info() {
+ return array(
+ 'id' => 'devshop_project_create',
+ 'path' => "projects/add/%step",
+ 'show trail' => TRUE,
+ 'show back' => TRUE,
+ 'show cancel' => TRUE,
+ 'show return' => FALSE,
+ 'next text' => 'Next',
+ 'next callback' => 'devshop_projects_create_wizard_next',
+ 'finish callback' => 'devshop_projects_create_wizard_finish',
+ 'cancel callback' => 'devshop_projects_create_wizard_cancel',
+ 'order' => array(
+ 'git_url' => t('Step 1: Source Code'),
+ 'settings' => t('Step 2: Settings'),
+ 'environments' => t('Step 3: Environments'),
+ 'sites' => t('Step 4: Install Profile'),
+ ),
+ 'forms' => array(
+ 'git_url' => array(
+ 'form id' => 'devshop_project_create_step_git'
+ ),
+ 'settings' => array(
+ 'form id' => 'devshop_project_create_step_settings'
+ ),
+ 'environments' => array(
+ 'form id' => 'devshop_project_create_step_environments'
+ ),
+ 'sites' => array(
+ 'form id' => 'devshop_project_create_step_sites'
+ ),
+ ),
+ );
+}
+
+/**
+ * WIZARD TOOLS
+ */
+
+
+/**
+ * NEXT callback
+ * Saves anything in $form_state['project'] to ctools cache.
+ *
+ * The form submit callbacks are responsible for putting data into
+ * $form_state['project'].
+ */
+function devshop_projects_create_wizard_next(&$form_state) {
+ $project = &$form_state['project'];
+ $cache = ctools_object_cache_set('project', 'devshop_project', $project);
+}
+
+
+
+
+/**
+ * CANCEL callback
+ * Callback generated when the 'cancel' button is clicked.
+ * Remove the project data cache and send back to projects page.
+ */
+function devshop_projects_create_wizard_cancel(&$form_state) {
+ // Update the cache with changes.
+ $project = &$form_state['project'];
+ ctools_object_cache_clear('project', 'devshop_project');
+
+ // Redirect to projects list
+ $form_state['redirect'] = 'projects';
+
+ // If we have a project node, create a "delete" hosting task
+ if (!empty($project->nid)) {
+ hosting_add_task($project->nid, 'delete');
+ }
+
+ // Message
+ drupal_set_message(t('Project creation cancelled.'));
+
+ // Removing last step session variable.
+ unset($_SESSION['last_step']);
+
+}
+
+/**
+ * FINISH callback
+ * Callback generated when the add page process is finished.
+ */
+function devshop_projects_create_wizard_finish(&$form_state) {
+
+ $project = &$form_state['project'];
+ $project_node = node_load($project->nid);
+
+ // Save the extra options to the project node.
+ $project_node->project->install_profile = $form_state['values']['install_profile'];
+
+ // Create the site nodes, saving to the environment.
+ // @TODO: Can we speed things up here by only running install for the first,
+ // then "Cloning" to create the rest?
+ foreach ($project_node->project->environments as $environment_name => &$environment) {
+ // @TODO: Does this set the http_server as well?? Doesn't look like it.
+ $db_server = $environment->db_server_nid;
+ $site_node = devshop_projects_create_site($project_node->project, node_load($environment->platform), $environment_name, $db_server);
+ $environment->site = $site_node->nid;
+ }
+
+ // Set to not verify and to publish.
+ $project_node->no_verify = TRUE;
+ $project_node->status = 1;
+ node_save($project_node);
+
+ ctools_object_cache_clear('project', 'devshop_project');
+ $form_state['redirect'] = 'node/' . $project_node->nid;
+
+ // Removing last step session variable.
+ unset($_SESSION['last_step']);
+
+ // Friendly message
+ drupal_get_messages('status');
+ drupal_set_message(t('Your project has been created. Your sites are being installed.'));
+}
+
+/**
+ * Returns JSON showing the state of the project
+ */
+function devshop_projects_add_status($type = 'platform') {
+ $return = array();
+
+ // Get Project Cache
+ ctools_include('wizard');
+ ctools_include('object-cache');
+ $project = ctools_object_cache_get('project', 'devshop_project');
+
+
+ $project_node = node_load($project->nid);
+
+ $all_tasks_completed = TRUE;
+ $nids = array();
+
+ // When checking project...
+ if ($type == 'project') {
+ $nids = array($project_node->nid);
+ $tasks = hosting_get_tasks(array('rid' => $project_node->nid));
+ $last_task = array_shift($tasks);
+ $status = _hosting_parse_error_code($last_task->task_status);
+
+ $return['tasks'][$last_task->nid] = array(
+ 'status' => $last_task->task_status,
+ );
+
+ $return['tasks_complete'] = $last_task->task_status == HOSTING_TASK_SUCCESS || $last_task->task_status == HOSTING_TASK_ERROR || $last_task->task_status == HOSTING_TASK_WARNING;
+ drupal_json_output($return);
+ exit;
+ }
+
+ // When checking platforms...
+ if ($type == 'platform') {
+ foreach ($project_node->project->environments as $name => $environment) {
+ $nids[] = $environment->platform;
+ }
+ }
+
+ // Check verification task for all nids
+ foreach ($nids as $nid) {
+ $platform = node_load($nid);
+ $profiles_shortnames = hosting_get_profiles($platform->nid, 'short_name');
+ if (is_array($profiles_shortnames) && !empty($profiles_shortnames)) {
+ $install_profiles = array_combine($profiles_shortnames, (array) hosting_get_profiles($platform->nid));
+ }
+ else {
+ $install_profiles = array();
+ }
+
+ $status = _hosting_parse_error_code($platform->environment->last_task_node->task_status);
+ if ($status == 'Queued') {
+ $status = '...';
+ }
+ $return['tasks'][$nid] = array(
+ 'status' => $status,
+ 'version' => !empty($platform->release->version)? $platform->release->version: '',
+ 'profiles' => implode(', ', $install_profiles),
+ );
+ // If task is not completed, mark all tasks not complete.
+ if ($platform->environment->last_task_node->task_status == HOSTING_TASK_SUCCESS || $platform->environment->last_task_node->task_status == HOSTING_TASK_ERROR || $platform->environment->last_task_node->task_status == HOSTING_TASK_WARNING) {
+ continue;
+ }
+ else {
+ $all_tasks_completed = FALSE;
+ }
+
+ }
+ $return['tasks_complete'] = $all_tasks_completed;
+ drupal_json_output($return);
+ exit;
+}
+
+/**
+ * Helper for adding reloading feature to form
+ */
+function devshop_form_reloader(&$form, $type = 'platform') {
+ // Add JS that reloads the page when tasks finish.
+ $form['item'] = array(
+ '#type' => 'item',
+ '#value' => '',
+ '#weight' => 10
+ );
+ $settings['devshopReload'] = array(
+ 'type' => $type,
+ 'delay' => 1000,
+ );
+
+ drupal_add_js($settings, array('type' => 'setting'));
+ drupal_add_js(drupal_get_path('module', 'devshop_projects') . '/inc/reload.js');
+}
diff --git a/modules/devshop/devshop_projects/inc/create/create.js b/modules/devshop/devshop_projects/inc/create/create.js
new file mode 100644
index 000000000..fc750ea3a
--- /dev/null
+++ b/modules/devshop/devshop_projects/inc/create/create.js
@@ -0,0 +1,99 @@
+/**
+ * Start New Project:
+ *
+ * Step 1: Takes the entered name and creates a base url and code path from it.
+ */
+(function ($) {
+
+ Drupal.devshopFilterProjectName = function (inputElement) {
+ return inputElement.val().split("/").pop().replace('.git', '').replace(/[^a-z0-9]/gi, '').toLowerCase()
+ }
+
+ Drupal.devshopSetProjectName = function (inputElement) {
+ var fixedProjectName = Drupal.devshopFilterProjectName(inputElement);
+ if (fixedProjectName != '') {
+ $('#edit-title').val(fixedProjectName);
+ }
+ }
+
+ Drupal.behaviors.devshopSourceSelect = {
+ attach: function (context, settings) {
+ Drupal.settings.devshop.projectNameSourceElements.forEach(function( selector ) {
+
+ if ($(selector).prop("tagName") == 'SELECT') {
+ var eventName = 'change';
+ }
+ else {
+ var eventName = 'keyup';
+ }
+
+ $(selector).bind(eventName, function(event) {
+ Drupal.devshopSetProjectName($(this));
+ });
+ });
+ }
+ };
+
+ Drupal.behaviors.devshopShowSelect = {
+ attach: function (context, settings) {
+ $('a.put-back-suggested-projects').click(function(e) {
+ e.preventDefault();
+ var optionVal = $('#edit-settings-github-repository-source-composer-project-suggestions option').val();
+ $('#edit-settings-github-repository-source-composer-project-suggestions').val(optionVal).change();
+ });
+ }
+ }
+ Drupal.behaviors.devshopShowSelectRepos = {
+ attach: function (context, settings) {
+ $('a.put-back-suggested-repos').click(function(e) {
+ e.preventDefault();
+ var optionVal = $('#edit-settings-github-repository-source-import-suggestions option').val();
+ $('#edit-settings-github-repository-source-import-suggestions').val(optionVal).change();
+ });
+ }
+ }
+
+ Drupal.behaviors.autoSelect = {
+ attach: function (context, settings) {
+ $('select#edit-settings-github-repository-source-composer-project-suggestions').change(function(e) {
+ if ($(this).val() == 'custom') {
+ $('#edit-settings-github-repository-source-composer-project').select();
+ }
+
+ });
+ $('select#edit-settings-github-repository-source-import-suggestions').change(function(e) {
+ if ($(this).val() == 'custom') {
+ $('#edit-settings-github-repository-source-import').select();
+ }
+
+ });
+ }
+ }
+
+}(jQuery));
+
+/**
+ * Step 2: Settings
+ */
+(function ($) {
+ Drupal.behaviors.createStep2 = {
+ attach: function (context, settings) {
+
+ // Hide unless
+ $('#edit-project-settings-live-live-domain-www-wrapper').hide();
+ $('#edit-project-settings-live-environment-aliases-wrapper').hide();
+
+ $('#edit-project-settings-live-live-domain').keyup(function(){
+ if ($(this).val()){
+ $('#edit-project-settings-live-live-domain-www-wrapper').show();
+ $('#edit-project-settings-live-environment-aliases-wrapper').show();
+ }
+ else {
+ $('#edit-project-settings-live-live-domain-www-wrapper').hide();
+ $('#edit-project-settings-live-environment-aliases-wrapper').hide();
+ }
+ });
+
+ }
+ };
+}(jQuery));
diff --git a/modules/devshop/devshop_projects/inc/create/step-1.inc b/modules/devshop/devshop_projects/inc/create/step-1.inc
new file mode 100644
index 000000000..49dbc7719
--- /dev/null
+++ b/modules/devshop/devshop_projects/inc/create/step-1.inc
@@ -0,0 +1,334 @@
+verify_error) {
+ $form['note'] = array(
+ '#markup' => t('Something went wrong. Check the errors and settings and try again.'),
+ '#prefix' => '
',
+ '#suffix' => '
',
+ '#weight' => -999,
+ );
+ $form['error'] = array(
+ '#markup' => $project->verify_error,
+ '#weight' => -998,
+ );
+
+ // Check for "host key"
+ if (strpos($project->verify_error, 'Host key verification failed')) {
+ $form['help'] = array(
+ '#markup' => t('Looks like you need to authorize this host. SSH into the server as aegir user and run the command git ls-remote !repo. Add StrictHostKeyChecking no to your ~/.ssh/config file to avoid this for all domains in the future.', array(
+ '!repo' => $project->git_url,
+ )),
+ '#prefix' => '
' . t('Choose a unique name for your project. Only lowercase letters and numbers are allowed. No punctuation, spaces or special characters.') . '
',
+ '#size' => 40,
+ '#maxlength' => 255,
+ '#attributes'=> array(
+ 'placeholder' => t('myproject')
+ ),
+ '#weight' => 1,
+ );
+ }
+ else {
+ $form['title'] = array(
+ '#type' => 'value',
+ '#value' => $project->name,
+ );
+ $form['title_display'] = array(
+ '#type' => 'item',
+ '#title' => t('Project Code Name'),
+ '#markup' => $project->name,
+ '#weight' => 1,
+ );
+ }
+
+ $username = variable_get('aegir_user', 'aegir');
+
+//
+// // Project code path.
+// $form['code_path'] = array(
+// '#type' => variable_get('devshop_projects_allow_custom_code_path', FALSE) ? 'textfield' : 'value',
+// '#title' => t('Code path'),
+// '#description' => t('The absolute path on the filesystem that will be used to create all platforms within this project. There must not be a file or directory at this path.'),
+// '#required' => variable_get('devshop_projects_allow_custom_code_path', FALSE),
+// '#size' => 40,
+// '#default_value' => $project->code_path,
+// '#maxlength' => 255,
+// '#attributes' => array(
+// 'data-base_path' => variable_get('devshop_project_base_path', '/var/aegir/projects'),
+// ),
+// );
+//
+// // Project base url
+// $form['base_url'] = array(
+// '#type' => variable_get('devshop_projects_allow_custom_base_url', FALSE) ? 'textfield' : 'value',
+// '#title' => t('Base URL'),
+// '#description' => t('All sites will be under a subdomain of this domain.'),
+// '#required' => variable_get('devshop_projects_allow_custom_base_url', FALSE),
+// '#size' => 40,
+// '#default_value' => $project->base_url,
+// '#maxlength' => 255,
+// '#attributes' => array(
+// 'data-base_url' => devshop_projects_url($project->name, ''),
+// ),
+// );
+
+ // Display helpful tips for connecting.
+ $pubkey = variable_get('devshop_public_key', '');
+
+ // If we don't yet have the server's public key saved as a variable...
+ if (empty($pubkey)) {
+ $output = t("This DevShop doesn't yet know your server's public SSH key. To import it, run the following command on your server as aegir user:");
+ $command = 'drush @hostmaster vset devshop_public_key "$(cat ~/.ssh/id_rsa.pub)" --yes';
+ $output .= "";
+ }
+ else {
+ $msg = t('If you haven\'t granted this server access to your Git repository, you should do so now using it\'s public SSH key:');
+
+ $link = l(t('Edit devshop public key'), 'admin/devshop', array(
+ 'query' => array(
+ 'destination' => $_GET['q'],
+ ),
+ 'attributes' => array(
+ 'class' => 'btn btn-default'
+ ),
+ ));
+ $output = <<
$msg
+ $link
+
+HTML;
+ }
+
+ // Add info about connecting to Repo
+ $form['connect'] = array(
+ '#markup'=> $output,
+ );
+
+ $tips[] = t('For best results, use the !link project as a starting point for Composer based projects.', [
+ '!link' => l(t('DevShop Composer Template'), 'https://github.com/opendevshop/devshop-composer-template', array(
+ 'attributes' => array(
+ 'target' => '_blank',
+ )
+ )),
+ ]);
+
+ $form['welcome'] = array(
+ '#markup' => '
' . t("Start a new DevShop Project.") . '
' . t("A project represents your website. DevShop keeps track of the source code for your website so it can launch copies of it quickly.") . '
',
+ '#prefix' => '
',
+ '#suffix' => '
',
+ '#weight' => -1000,
+ );
+
+ $options = module_invoke_all('devshop_project_git_repo_options');
+ $form['source'] = array(
+ '#type' => 'container',
+ '#attributes' => array(
+ 'class' => array(
+ 'well'
+ ),
+ ),
+ );
+ $form['source']['git_source'] = array(
+ '#title' => t('Choose the source of your code'),
+ '#type' => 'radios',
+ '#options' => $options,
+ '#description' => t(''),
+ '#default_value'=> 'custom',
+ );
+
+ $form['source']['git_url'] = array(
+ '#type' => 'textfield',
+ '#required' => 1,
+ '#title' => t('Git Repository URL'),
+ '#default_value' => $project->git_url,
+ '#maxlength' => 1024,
+ '#attributes' => array(
+ 'placeholder' => 'git@githost.com:myteam/myproject.git'
+ ),
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="git_source"]' => array(
+ 'value' => 'custom'
+ ),
+ ),
+ ),
+ );
+
+
+ // We have to tell the front-end what type of field so we can assign the right event.
+ drupal_add_js(array(
+ 'devshop' => array(
+ 'projectNameSourceElements' => array(
+ '#edit-git-url'
+ ),
+ ),
+ ), 'setting');
+
+ $tips[] = t('Use the "ssh" url whenever possible. For example: git@github.com:opendevshop/repo.git');
+ $tips[] = t('You can use a repository with a full Drupal stack committed, in the root or in a sub folder');
+ $tips[] = t('If a composer.json file is found in the root of the project, composer install will be run automatically.');
+ $tips[] = t("If your repository is not public, make sure your server has SSH access. Check the !link to see this server's public key.", array(
+ '!link' => l(t('DevShop Settings'), 'admin/devshop'),
+ ));
+ $tips = theme('item_list', array('items' => $tips));
+
+
+ $form['tips'] = array(
+ '#markup' => $tips,
+ '#prefix' => '
',
+ '#suffix' => '
',
+ '#weight' => 10,
+ );
+ return $form;
+}
+
+/**
+ * Implements hook_devshop_project_git_repo_options().
+ */
+function devshop_projects_devshop_project_git_repo_options() {
+ return array(
+ 'custom' => t('Git repository URL'),
+ );
+}
+
+
+/**
+ * Force the project to be lowercase and remove any non numbers or letters.
+ *
+ * @param $element
+ * @param $form_state
+ * @param $form
+ */
+function devshop_project_wizard_title_validate($element, &$form_state, $form) {
+ // No spaces or special characters allowed.
+ // Set the value to our trimmed and lowercased project name.
+ $project_name = strtolower(preg_replace('/[^a-z0-9]/gi', '', $element['value']));
+ form_set_value($form['title'], $project_name, $form_state);
+}
+
+/**
+ * STEP 1: Validate
+ */
+function devshop_project_create_step_git_validate(&$form, &$form_state) {
+ $project = &$form_state['project'];
+
+ if (empty($project->nid)) {
+
+ $project_name = $form_state['values']['title']; // domain names are case-insensitive
+
+ // Check for duplicate project name here.
+ // hosting_context_load() only works if a node exists with that ID.
+ if (db_query("SELECT nid FROM {hosting_context} WHERE name = :name", array(':name' => 'project_' . $project_name))->fetch()) {
+ form_set_error('title', t('The name @project is in use. Please try again.', array(
+ '@project' => $project_name,
+ )));
+ }
+ }
+
+//
+// // CODE PATH & BASE URL
+// // Code path and base url must be unique
+// if (db_query('SELECT n.nid FROM {hosting_devshop_project} d LEFT JOIN {node} n ON d.nid = n.nid WHERE status = :status AND code_path = :code_path;', array(':status' => 1, ':code_path' => $form_state['values']['code_path']))->fetchField()) {
+// form_set_error('code_path', t('Another project already has this code path. Code path must be unique.'));
+// }
+// if (db_query('SELECT n.nid FROM {hosting_devshop_project} d LEFT JOIN {node} n ON d.nid = n.nid WHERE status = :status AND base_url = :base_url;', array(':status' => 1, ':base_url' => $form_state['values']['base_url']))->fetchField()) {
+// form_set_error('base_url', t('Another project already has this base url. Base URL must be unique.'));
+// }
+//
+// // If custom code paths are not allowed, set the value here using the project name.
+// if (!variable_get('devshop_projects_allow_custom_code_path', FALSE)) {
+// form_set_value($form['code_path'], variable_get('devshop_project_base_path', '/var/aegir/projects') . '/' . $project_name, $form_state);
+// }
+//
+// // If custom base URLs are not allowed, set the value here using the project name.
+// if (!variable_get('devshop_projects_allow_custom_base_url', FALSE)) {
+// form_set_value($form['base_url'], devshop_projects_url($project_name), $form_state);
+// }
+}
+
+/**
+ * STEP 1: Submit
+ */
+function devshop_project_create_step_git_submit(&$from, &$form_state) {
+ global $user;
+
+ $project = &$form_state['project'];
+
+ // If the project already exists, this means the git url has changed...
+ if ($project->nid) {
+ // Change the git url and save the node. Verification will run again.
+ $node = node_load($project->nid);
+ $node->project->git_url = $form_state['values']['git_url'];
+ $node->project->code_path = $form_state['values']['code_path'];
+ $node->project->base_url = $form_state['values']['base_url'];
+
+ $project->settings->create_project_settings = $form_state['values']['settings'];
+
+ node_save($node);
+ }
+ // Create new project node
+ elseif (empty($project->nid)) {
+ // Create the project node now. We will update it with the chosen path.
+ // This is so we can check for branches and save the hosting_context as soon
+ // as possible.
+ $node = new stdClass;
+ $node->title = $form_state['values']['title'];
+
+ $node->type = 'project';
+ $node->status = 0;
+ $node->uid = $user->uid;
+ $node->name = $user->name;
+
+ // Create project object
+ $project->name = $form_state['values']['title'];
+ $project->git_url = $form_state['values']['git_url'];
+ $project->code_path = $form_state['values']['code_path'];
+ $project->base_url = $form_state['values']['base_url'];
+
+ $project->settings->create_project_settings = $form_state['values']['settings'];
+
+ // @TODO: We will clone the code for step 2 and look for drupal.
+ $project->drupal_path = variable_get('devshop_projects_default_drupal_path', '');
+
+ // Attach to node
+ $node->project = $project;
+
+ // Save project node, triggering verification.
+ if ($node = node_submit($node)) {
+ node_save($node);
+ }
+
+ // Save NID to ctools object cache.
+ if ($node->nid) {
+ $project->nid = $node->nid;
+ }
+ }
+
+ // Remove default "task" messages.
+ drupal_get_messages();
+}
diff --git a/modules/devshop/devshop_projects/inc/create/step-2.inc b/modules/devshop/devshop_projects/inc/create/step-2.inc
new file mode 100644
index 000000000..862ab7e9c
--- /dev/null
+++ b/modules/devshop/devshop_projects/inc/create/step-2.inc
@@ -0,0 +1,107 @@
+verify_task_status == HOSTING_TASK_QUEUED || $project->verify_task_status == HOSTING_TASK_PROCESSING) {
+ $note = '
' . t('Please wait while we connect and analyze your repository.') . '
',
+ );
+
+ // If there was a problem with loading branches or tags, hide the form and show a link to re-verify.
+ if (empty($project->settings->git['branches']) && empty($project->settings->git['tags'])) {
+ $form['error'] = array(
+ '#markup' => t('Unable to find any branches or tags. Please !verify.', array(
+ '!verify' => l(t('Re-verify the project'), "hosting_confirm/{$project->nid}/project_verify", array(
+ 'query' => array('token' => drupal_get_token($user->uid)),
+ 'attributes' => array(
+ 'class' => array('btn btn-success'),
+ ),
+ ))
+ )),
+ '#prefix' => '
',
+ );
+ $form['#no_finish'] = TRUE;
+ }
+
+ // If ready...
+ else {
+
+ // If no common profiles found, just set to standard.
+ if (count($available_profiles) == 0) {
+ $available_profiles['standard'] = 'No default profile.';
+ $default_profile = 'standard';
+ }
+
+ $project->no_finish = FALSE;
+
+ // Install Profile
+ // Sensible default?
+ // Lets go with standard for now... we can update later.
+ if (isset($available_profiles['standard'])) {
+ $default_profile = 'standard';
+ }
+ // If 'drupal' profile exists, it is likely drupal6!
+ elseif (isset($available_profiles['drupal'])) {
+ $default_profile = 'drupal';
+ }
+
+ $form['install_profile'] = array(
+ '#type' => 'radios',
+ '#options' => $available_profiles,
+ '#title' => t('Project Install Profile'),
+ '#required' => 1,
+ '#description' => t('Choose the default installation profile for this project.'),
+ '#default_value' => $default_profile,
+ );
+ }
+
+ $form['#suffix'] = $modals;
+ return $form;
+}
+
+/**
+ * STEP 4: Validate
+ */
+function devshop_project_create_step_sites_validate(&$from, &$form_state) {
+ if ($form_state['triggering_element']['#value'] == t('Retry')) {
+ foreach ($form_state['project']->environments as $name => $environment) {
+ $task = current($environment->tasks['verify']);
+ if (isset($task->nid)) {
+ hosting_task_retry($task->nid);
+ }
+ }
+ }
+ elseif (empty($form_state['values']['install_profile'])) {
+ form_set_error('install_profile', t('You must choose an install profile. Please wait for all environments to verify.'));
+ }
+}
diff --git a/modules/devshop/devshop_projects/inc/drush.inc b/modules/devshop/devshop_projects/inc/drush.inc
new file mode 100644
index 000000000..f807a7c90
--- /dev/null
+++ b/modules/devshop/devshop_projects/inc/drush.inc
@@ -0,0 +1,45 @@
+environments as $name => $environment) {
+
+ $remote_user = 'aegir';
+ $remote_host = $environment->remote_host;
+ $root = $environment->root;
+ $uri = $environment->uri;
+ $file_path = "sites/{$environment->uri}/files";
+
+ $output .= << '$root',
+ 'uri' => '$uri',
+ 'remote-user' => '$remote_user',
+ 'remote-host' => '$remote_host',
+ 'path-aliases' => array(
+ '%files' => '$file_path',
+ ),
+);
+
+PHP;
+ }
+
+ return $output;
+}
+
+
+/**
+ * Downloads the drush aliases for this site.
+ * @param $project
+ */
+function devshop_project_drush_aliases_page($node) {
+ $project = $node->project;
+ $filename = $project->name . '.aliases.drushrc.php';
+ header("Content-Disposition: attachment; filename='$filename'");
+ print devshop_project_aliases($project);
+}
diff --git a/modules/devshop/devshop_projects/inc/forms.inc b/modules/devshop/devshop_projects/inc/forms.inc
new file mode 100644
index 000000000..773e1a59c
--- /dev/null
+++ b/modules/devshop/devshop_projects/inc/forms.inc
@@ -0,0 +1,1755 @@
+project;
+
+ // Save last project data
+ $form['old'] = array(
+ '#value' => $node,
+ '#type' => 'value',
+ );
+
+ // Project Settings
+ // Every value under $form['project'] gets serialized and saved into a project's "data" column.
+ $form['project'] = array(
+ '#tree' => TRUE,
+ );
+
+ // Hidden fields that can't change.
+ $form['title'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Project Code Name'),
+ '#required' => TRUE,
+ '#description' => t('Choose a unique name for your project.'),
+ '#size' => 40,
+ '#default_value' => $node->title,
+ '#maxlength' => 255,
+ );
+ $form['project']['codebase'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('General Settings'),
+ '#group' => 'project_settings',
+ '#tree' => FALSE,
+ '#weight' => -11,
+ );
+ $form['project']['codebase']['git_url'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Default Git Repository URL'),
+ '#description' => t('The SSH or HTTP url for the git repository to use for new environments. Changing this will not change the git remote for existing environments.'),
+ '#default_value' => $project->git_url,
+ '#parents' => array(
+ 'project',
+ 'git_url',
+ ),
+ );
+ $form['project']['codebase']['code_path'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Base path'),
+ '#description' => t('All environments in this project will be installed inside this path. Changing this will only affect new environments. Existing environments will not change.'),
+ '#required' => TRUE,
+ '#size' => 40,
+ '#default_value' => $project->code_path,
+ '#maxlength' => 255,
+ '#weight' => 2,
+ '#parents' => array(
+ 'project',
+ 'code_path',
+ ),
+ );
+
+ $items = [];
+ $items[] = t('Enter the relative path to the exposed document root within your repository. Leave blank if index.php is in the root. Common paths are docroot or web.');
+ $items[] = t('If you are using the drupal-composer project for Drupal 8, the default document root is "web".');
+
+ $form['project']['codebase']['drupal_path'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Document Root'),
+ '#description' => theme('item_list', ['items' => $items]),
+ '#size' => 40,
+ '#default_value' => $project->drupal_path,
+ '#maxlength' => 255,
+ '#weight' => 3,
+ '#parents' => array(
+ 'project',
+ 'drupal_path',
+ ),
+ );
+// This was accidentally merged from the makefiles branch.
+// $form['project']['codebase']['makefile_path'] = array(
+// '#type' => 'textfield',
+// '#title' => t('Path to Makefile'),
+// '#description' => t('Enter the relative path to a makefile inside your repository. Leave blank if a full Drupal codebasee is in the repository.'),
+// '#size' => 40,
+// '#default_value' => $project->settings->makefile_path,
+// '#maxlength' => 255,
+// '#weight' => 4,
+// '#parents' => array(
+// 'project',
+// 'settings',
+// 'makefile_path',
+// ),
+// );
+ $form['project']['codebase']['base_url'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Environment Domain Name Pattern'),
+ '#description' => t("Each environment will have a system domain name generated for it based on it's name. Use @project for project name, @hostname for '%host', @environment for the environment's name.", array('%host' => $_SERVER['SERVER_NAME'])) . '' . t('Changing this will only affect new environments.'). '',
+ '#required' => TRUE,
+ '#size' => 40,
+ '#default_value' => $project->base_url,
+ '#maxlength' => 255,
+ '#weight' => 4,
+ '#access' => variable_get('devshop_projects_allow_custom_base_url', FALSE),
+ '#parents' => array(
+ 'project',
+ 'base_url',
+ ),
+ );
+
+ // Don't allow editing
+ if ($node->nid) {
+
+ // Title
+ $form['title']['#value'] = $form['title']['#default_value'];
+ $form['title']['#type'] = 'value';
+
+ }
+
+ // Prevent editing code path unless allowed.
+ if (!variable_get('devshop_projects_allow_custom_code_path', FALSE)) {
+ $form['project']['codebase']['code_path']['#type'] = 'value';
+ $form['project']['codebase']['code_path']['#value'] = $form['project']['codebase']['code_path']['#default_value'];
+ }
+
+ // Prevent editing git url path unless allowed.
+ if (!variable_get('devshop_projects_allow_changing_project_git_url', TRUE)) {
+ $form['project']['codebase']['git_url']['#type'] = 'value';
+ $form['project']['codebase']['git_url']['#value'] = $form['project']['codebase']['git_url']['#default_value'];
+ }
+
+ // Project Settings
+ $form['project']['settings'] = array(
+ '#weight' => -10,
+ );
+
+ // Project Settings Vertical Tabs
+ $form['project_settings'] = array(
+ '#type' => 'vertical_tabs',
+ '#weight' => -11,
+ );
+
+ // Save git branches and tags
+ $form['project']['settings']['git']['branches'] = array(
+ '#type' => 'value',
+ '#value' => isset($project->settings->git['branches']) ? $project->settings->git['branches'] : NULL,
+ );
+ $form['project']['settings']['git']['tags'] = array(
+ '#type' => 'value',
+ '#value' => isset($project->settings->git['tags']) ? $project->settings->git['tags'] : NULL,
+ );
+
+ // Live Environment settings.
+ $form['project']['settings']['live'] = array(
+ '#type' => 'fieldset',
+ '#collapsible' => TRUE,
+ '#collapsed' => arg(1) != $project->nid,
+ '#title' => t('Domain Name Settings'),
+ '#group' => 'project_settings',
+ );
+
+ // Live Environment
+ $environments = array_keys($project->environments);
+ if (empty($environments)) {
+ $environments_options = array();
+ }
+ else {
+ $environments_options = array_combine($environments, $environments);
+ $environments_options[''] = t('None');
+ }
+
+ $form['project']['codebase']['live_environment'] = array(
+ '#type' => 'select',
+ '#title' => t('Primary Environment'),
+ '#suffix' => '
' . t('Select the primary environment for this project, typically the live environment. This environment will be marked with a icon. This is the environment that will be cloned when pull requests are created.') . '
',
+ '#options' => $environments_options,
+ '#default_value' => isset($project->settings->live) ? $project->settings->live['live_environment'] : '',
+ '#parents' => array(
+ 'project',
+ 'settings',
+ 'live',
+ 'live_environment',
+ ),
+ );
+
+ // Live Domain
+ $form['project']['settings']['live']['live_domain'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Live Domain'),
+ '#description' => t('The live domain for this project. Used only for links and when creating subdomain aliases for other environments. You must still add the Domain to your live environment manually.'),
+ '#size' => 40,
+ '#default_value' => isset($project->settings->live) ? $node->project->settings->live['live_domain'] : '',
+ );
+
+ // Use aliases
+ $form['project']['settings']['live']['environment_aliases'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('For new environments, create subdomains under Live Domain.'),
+ '#description' => t('When new environments are created, automatically add a domain name such as http://ENVIRONMENT.LIVEDOMAIN.com. Does not affect existing environments. Does not remove domains when disabled.'),
+ '#default_value' => isset($project->settings->live) ? $project->settings->live['environment_aliases'] : FALSE,
+ );
+
+ // Pull Code Method
+ $form['project']['settings']['deploy'] = array(
+ '#type' => 'fieldset',
+ '#collapsible' => TRUE,
+ '#collapsed' => arg(1) != $project->nid,
+ '#title' => t('Deployment Automation'),
+ '#description' => t('Configure how code is delivered to the servers. Post Deploy hooks are configured per environment.'),
+ '#weight' => -9,
+ '#group' => 'project_settings',
+ );
+
+ $form['project']['settings']['deploy']['method'] = array(
+ '#title' => 'Deploy Code Method',
+ '#type' => 'radios',
+ '#description' => t('Choose the method used to deploy code to the server.'),
+ '#default_value' => isset($project->settings->deploy['method']) ? $project->settings->deploy['method'] : 'webhook',
+ );
+
+ // Commit Webhook
+ $form['project']['settings']['deploy']['method']['#options']['webhook'] = t('Immediate Deployment');
+ $form['project']['settings']['deploy']['method']['#options']['webhook'] .= '
' . t('Recommended. Deploy code as it is delivered to your repository.') . ' ' . t('Requires setting up a webhook with your git repository host.') . '' . '
' . t('Deploy code to servers manually via devshop or drush.');
+
+ $form['project']['settings']['deploy']['method']['#options']['manual'] .= ' ' . t('Not recommended. All environments must be manually updated.') . '' . '
';
+
+ // Add link to hosting queues admin if the user can access them.
+ if (!$queues['deploy']['enabled'] && user_access('administer hosting queues')) {
+ $form['project']['settings']['deploy']['queue_admin'] = array(
+ '#value' => t('The !link is disabled. Enable it to allow projects to pull code in the queue.', array(
+ '!link' => l(t('Pull Queue'), 'admin/hosting/queues'),
+ )),
+ '#prefix' => '
',
+ '#suffix' => '
',
+ );
+ }
+
+ // Load deploy hooks form element.
+ $form['project']['settings']['deploy']['default_hooks'] = devshop_environment_deploy_hooks_form($project);
+
+ // Deploy hooks configuration
+ $form['project']['settings']['deploy']['default_hooks']['label'] = array(
+ '#value' => t('Default Deploy Hooks:'),
+ '#prefix' => '',
+ '#weight' => -100,
+ );
+ $form['project']['settings']['deploy']['default_hooks']['description'] = array(
+ '#value' => t('New environments will run these actions when new code or data is deployed.'),
+ '#prefix' => '
',
+ '#suffix' => '
',
+ '#weight' => -100,
+ );
+
+ $form['project']['settings']['deploy']['default_hooks']['allow_environment_deploy_config'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Allow environment-specific deploy hook configuration.'),
+ '#default_value' => isset($node->project->settings->deploy) ? $node->project->settings->deploy['allow_environment_deploy_config'] : FALSE,
+ '#description' => t('Each environment can be configured to have different deploy hooks. Be sure to check your environments settings if you enable this.' ),
+ '#parents' => array(
+ 'project',
+ 'settings',
+ 'deploy',
+ 'allow_environment_deploy_config',
+ ),
+ );
+ $form['project']['settings']['deploy']['default_hooks']['allow_environment_deploy_hooks_override'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Allow users to override hooks when deploying manually.'),
+ '#default_value' => isset($node->project->settings->deploy) ? $node->project->settings->deploy['allow_environment_deploy_hooks_override'] : FALSE,
+ '#description' => t('Check this box to allow users to override the hooks that are run on manual deployments. If left unchecked, all environments will run the deploy hooks configured above on every deployment.' ),
+ '#parents' => array(
+ 'project',
+ 'settings',
+ 'deploy',
+ 'allow_environment_deploy_hooks_override',
+ ),
+ );
+
+ // @TODO: is there a better way to save certain values? We lose data without these.
+ $form['project']['settings']['deploy']['last_webhook'] = array(
+ '#type' => 'value',
+ '#value' => isset($node->project->settings->deploy) ? $node->project->settings->deploy['last_webhook'] : NULL,
+ );
+ $form['project']['settings']['deploy']['last_webhook_status'] = array(
+ '#type' => 'value',
+ '#value' => isset($node->project->settings->deploy) ? $node->project->settings->deploy['last_webhook_status'] : NULL,
+ );
+ $form['project']['settings']['deploy']['last_webhook_ip'] = array(
+ '#type' => 'value',
+ '#value' => isset($node->project->settings->deploy) ? $node->project->settings->deploy['last_webhook_ip'] : NULL,
+ );
+
+ //All settings git pull in project page
+ $form['project']['settings']['default_environment'] = array(
+ '#type' => 'fieldset',
+ '#collapsible' => TRUE,
+ '#collapsed' => arg(1) != $project->nid,
+ '#title' => t('Server Stack'),
+ '#description' => t('The servers to use when creating new environments.'),
+ '#group' => 'project_settings',
+ );
+
+ // Install Profile
+ $available_profiles = array();
+ foreach ($project->environments as $name => $environment) {
+ // Passing null to hosting_get_profiles returns all profiles.
+ if (is_null($environment->platform)) {
+ continue;
+ }
+ $profiles_shortnames = hosting_get_profiles($environment->platform, 'short_name');
+ if (is_array($profiles_shortnames) && !empty($profiles_shortnames)) {
+ $profiles[$name] = array_combine($profiles_shortnames, (array)hosting_get_profiles($environment->platform));
+ } else {
+ $profiles[$name] = array();
+ }
+ if (empty($available_profiles)) {
+ $available_profiles = $profiles[$name];
+ } else {
+ $available_profiles = array_intersect_key($available_profiles, $profiles[$name]);
+ }
+ }
+
+ $form['project']['settings']['default_environment']['install_profile'] = array(
+ '#type' => 'radios',
+ '#options' => $available_profiles,
+ '#title' => t('Default Install Profile'),
+ '#required' => 1,
+ '#description' => t('New environments will be created using this install profile. Existing environments will not be affected.'),
+ '#default_value' => $node->project->install_profile,
+ '#access' => count($available_profiles),
+ );
+
+ // HTTP Server select.
+ $http_servers = hosting_get_servers('http', FALSE);
+ if (count($http_servers)) {
+ $form['project']['settings']['default_environment']['web_server'] = array(
+ '#title' => t('Web server'),
+ '#type' => 'select',
+ '#options' => $http_servers,
+ '#default_value' => isset($project->settings->default_environment) ? $project->settings->default_environment['web_server'] : current($http_servers),
+ );
+ }
+
+ // DB Server select.
+ $db_servers = hosting_get_servers('db', FALSE);
+ if (count($db_servers)) {
+ $form['project']['settings']['default_environment']['db_server'] = array(
+ '#title' => t('Database server'),
+ '#type' => 'select',
+ '#options' => $db_servers,
+ '#default_value' => isset($project->settings->default_environment) ? $project->settings->default_environment['db_server'] : current($db_servers),
+ );
+ }
+
+ // Solr Server Select
+ $solr_servers = hosting_get_servers('solr', FALSE);
+ if (count($solr_servers)) {
+ $form['project']['settings']['default_environment']['solr_server'] = array(
+ '#title' => t('Solr server'),
+ '#type' => 'select',
+ '#options' => $solr_servers,
+ '#default_value' => $project->settings->default_environment['solr_server'],
+ );
+ }
+
+ // Force the servers for this project's environments
+ $form['project']['settings']['default_environment']['force_default_servers'] = array(
+ '#title' => t('Force new environments to use these servers.'),
+ '#description' => t('When new environments are created, they will use the servers selected above.'),
+ '#type' => 'checkbox',
+ '#default_value' => $project->settings->default_environment['force_default_servers'],
+ );
+ return $form;
+}
+
+/**
+ * Implementation of hook_validate().
+ *
+ * This function is no longer used since we have a ctools wizard for
+ * project creation.
+ */
+function devshop_projects_validate($node, &$form, &$form_state) {
+
+ // Code path and base url must be unique
+ if (db_query('SELECT n.nid FROM {hosting_devshop_project} d LEFT JOIN {node} n ON d.nid = n.nid WHERE status = :status AND code_path = :code_path AND n.nid != :nid;', array(':status' => 1, ':code_path' => $form_state['values']['project']['code_path'], ':nid' => $form_state['values']['nid']))->fetchField()) {
+ form_set_error('code_path', t('The code path %path is in use by another project. Please enter a different path.', array(
+ '%path' => $form_state['values']['project']['code_path'],
+ )));
+ }
+ // If custom domain pattern is allowed, AND domain pattern does not have "@project" in it, AND domain pattern is not the default AND it is found on another project, throw an error.
+ if (variable_get('devshop_projects_allow_custom_base_url', FALSE) && strpos($form_state['values']['project']['base_url'], '@project') === FALSE && $form_state['values']['project']['base_url'] != variable_get('devshop_project_environment_url_pattern', '@project.@environment.@hostname') && db_query('SELECT n.nid FROM {hosting_devshop_project} d LEFT JOIN {node} n ON d.nid = n.nid WHERE status = :status AND base_url = :base_url AND n.nid != :nid;;', array(':status' => 1, ':base_url' => $form_state['values']['project']['base_url'], ':nid' => $form_state['values']['nid']))->fetchField()) {
+ form_set_error('base_url', t('The Environment Domain Pattern %url is in use by another project. Please enter a different Environment Domain Pattern.', array(
+ '%url' => $form_state['values']['project']['base_url'],
+ )));
+ }
+
+ // "Environment Domain Name Pattern" (Base URL)
+ // Ensure @environment is included.
+ if (strpos($form_state['values']['project']['base_url'], '@environment') === FALSE ) {
+ form_set_error('project][base_url', t('Environment Domain Name Pattern must include @environment placeholder.'));
+ }
+}
+
+/**
+ * Implements hook_form_alter().
+ */
+function devshop_projects_form_alter(&$form, &$form_state, $form_id) {
+
+ // Removing unneccesary fieldgroups
+ if ($form_id == 'project_node_form') {
+ global $user;
+
+ $project = $form['#node'];
+ unset($form['menu']);
+ unset($form['revision_information']);
+ unset($form['author']);
+ unset($form['options']);
+ unset($form['actions']['delete']);
+ unset($form['actions']['preview']);
+ if (isset($form['retry']['#value'])) {
+ $form['actions']['submit']['#value'] = t('Save and Retry');
+ }
+
+ //Add button for delete project
+ $form['actions']['delete'] = array(
+ '#markup' => l(' ' . t('Delete Project'), 'hosting_confirm/' . $project->nid . '/project_delete', array(
+ 'query' => array('token' => drupal_get_token($user->uid)),
+ 'attributes' => array(
+ 'class' => array('btn btn-danger pull-right'),
+ ),
+ 'html' => TRUE,
+ )),
+ '#weight' => 10,
+ '#access' => drupal_valid_path('hosting_confirm/' . $project->nid . '/project_delete'),
+ );
+ }
+
+ // Create Project Wizard
+ if (
+ $form_id == 'devshop_project_create_step_git' ||
+ $form_id == 'devshop_project_create_step_sites' ||
+ $form_id == 'devshop_project_create_step_settings' ||
+ $form_id == 'devshop_project_create_step_environments'
+ ){
+ if (isset($form['#no_finish'])) {
+ unset($form['buttons']['return']);
+ }
+ if (isset($form['#no_next'])) {
+ unset($form['buttons']['next']);
+ }
+
+ if (isset($form['buttons']['next']['#value'])) {
+ $form['buttons']['next']['#value'] .= ' ';
+ }
+
+ if (isset($form['buttons']['return']['#value'])) {
+ $form['buttons']['return']['#value'] = ' ' . t('Create Project & Environments');
+ }
+
+ $form['buttons']['next']['#attributes'] =
+ $form['buttons']['return']['#attributes'] = array(
+ 'class' => array(
+ 'btn btn-success',
+ ),
+ );
+ $form['buttons']['previous']['#attributes'] = array(
+ 'class' => array(
+ 'btn btn-default',
+ ),
+ );
+ $form['buttons']['cancel']['#attributes'] = array(
+ 'class' => array(
+ 'btn btn-link',
+ ),
+ );
+
+ }
+
+ // Hosting Task Forms
+ if ($form_id == 'hosting_task_confirm_form') {
+ switch ($form['task']['#value']) {
+
+ // Migrate Form: used for changing database server.
+ case 'migrate':
+
+ // To change the database server, we use the migrate form.
+ if ($_GET['deploy'] == 'stack') {
+ drupal_set_title(t('Change Database Server'));
+ $site_node = node_load($form['nid']['#value']);
+ $environment = $site_node->environment;
+ $form['help']['#weight'] = 100;
+ $form['help']['#value'] = t("Are you sure you want to change this environment's database server?");
+
+ // Hide "URI" field unless "rename" GET parameter is added.
+ if (!isset($_GET['rename'])) {
+ $form['parameters']['new_uri']['#type'] = 'value';
+ $form['parameters']['new_uri']['#value'] = $form['parameters']['new_uri']['#default_value'];
+ }
+
+ // Display something helpful
+ $form['old'] = array(
+ '#type' => 'item',
+ '#title' => t('Current Database Server'),
+ '#value' => l($environment->servers['db']['name'], 'hosting/c/server_' . $environment->servers['db']['nid']),
+ '#weight' => '-1',
+ );
+ }
+ if (isset($_GET['rename'])) {
+ drupal_set_title(t('Change Domain Name for environment @env in project @proj?', array(
+ '@env' => $environment->name,
+ '@proj' => $environment->project_name,
+ )));
+ $form['help']['#value'] = t("Are you sure you want to change this environment's domain name?");
+ $form['submit']['#value'] = t('Change Domain');
+ }
+ if (isset($_GET['rename']) || $_GET['deploy'] == 'stack') {
+ foreach ($form['parameters'] as $key => $element) {
+ if (is_int($key)) {
+ // Don't unset the target platform. Just hide it.
+ if (isset($element['target_platform']['#default_value'])) {
+ $form['parameters'][$key]['target_platform']['#type'] = 'value';
+ $form['parameters'][$key]['target_platform']['#value'] = $form['parameters'][$key]['target_platform']['#default_value'];
+ }
+ else {
+ unset($form['parameters'][$key]);
+ }
+ }
+ }
+ }
+ break;
+
+ // Deploy task form
+ case 'devshop-deploy':
+
+ // Alter title of deploy task.
+ $node = node_load($form['nid']['#value']);
+ drupal_set_title(t('Deploy code to Environment "@env"', array('@env' => $node->environment->name)), PASS_THROUGH);
+ $form['actions']['cancel']['#value'] = l(t('Cancel'), "node/{$node->project->nid}");
+ break;
+
+ // Sync task form
+ case 'sync':
+ if (isset($_GET['source'])) {
+ $node = node_load($form['nid']['#value']);
+
+ if ($_GET['source'] == 'other') {
+ $source = '';
+ }
+ elseif (is_int($_GET['source'])) {
+ $source = node_load($_GET['source']);
+ if (isset($source->nid) && $source->type != 'site') {
+ break;
+ }
+ $source_alias = $source->environment->system_alias;
+ }
+ elseif (isset($node->project->settings->aliases[$_GET['source']])) {
+ $alias_name = $source = $_GET['source'];
+ $source_alias = "@{$node->project->name}.{$alias_name}";
+ $alias_data = $node->project->settings->aliases[$_GET['source']];
+ }
+ else {
+ return;
+ }
+
+// print_r($source); die;
+// print $source_alias ; die;
+
+ drupal_set_title(t('Sync Data to Environment "@env"', array('@env' => $node->environment->name)), PASS_THROUGH);
+ $environment = $node->environment;
+
+ // If source is empty, ask for an alias.
+ if (empty($source)) {
+ $form['parameters']['source'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Drush Alias for Source'),
+ '#description' => t('Enter a full drush alias to use as the source. Make sure to include the "@" character at the beginning.'),
+ );
+ $form['parameters']['destination'] = array(
+ '#type' => 'value',
+ '#value' => $node->environment->system_alias,
+ );
+ }
+ // If a source is specified, add help and set it as a value.
+ else {
+
+ $source_environment = $source->environment;
+ $form['parameters']['source'] = array(
+ '#type' => 'value',
+ '#value' => $source_alias,
+ );
+ $form['parameters']['destination'] = array(
+ '#type' => 'value',
+ '#value' => $node->environment->system_alias,
+ );
+ }
+
+ // Don't copy modules and themes.
+ unset($form['parameters']['modules']);
+ unset($form['parameters']['themes']);
+ unset($form['parameters']['libraries']);
+
+ // Better output
+ $form['parameters']['database']['#prefix'] = '';
+ $form['parameters']['registry-rebuild']['#prefix'] = '';
+ $form['parameters']['backup']['#weight'] = -11;
+
+ $form['actions']['submit']['#value'] = '' . t('Sync Data');
+ $form['actions']['submit']['#attributes'] = array(
+ 'class' => array(
+ 'btn-success'
+ ),
+ );
+ $form['actions']['cancel']['#value'] = l(t('Cancel'), "node/{$node->project->nid}");
+
+ $form['warning']['#markup'] = '
' . t('Clicking "Sync Data" will DESTROY the database for the environment !link.', array('!link' => l($environment->url, $environment->url, array('attributes' => array('target' => '_blank'))))) . '
';
+
+
+ $destroyed = t('This environment will be destroyed.');
+ $source = t('This environment will be copied.');
+
+ $source_environment_url = $source_environment->url? $source_environment->url: 'http://' . $alias_data['uri'];
+ $source_environment_name = $source_environment->name? $source_environment->name: $alias_name;
+ $html = <<
+
'
+ );
+
+ // Get a list of environments.
+ foreach ($node->project->environments as $environment) {
+ $items[] = l($environment->uri, 'http://' . $environment->uri);
+
+ }
+ $environments = theme('item_list', array('items' => $items));
+
+ // Display a scary warning message.
+ $form['help']['#markup'] = '
' . t('Are you sure you wish to destroy the project !project and all of these environments?', array(
+ '!project' => l($node->project->name, "node/{$node->nid}"),
+ )) . $environments . '
',
+ '#weight' => -1000,
+ );
+
+ // Add our own validator and submit handlers.
+ $form['actions']['submit']['#value'] = t('Create New Environment');
+// array_unshift($form['actions']['submit']['#submit'], 'devshop_projects_create_environment_form_submit');
+
+ // Generate field prefix and suffix using domain name pattern.
+ if (variable_get('devshop_projects_allow_custom_base_url')) {
+ $pattern = $project->base_url;
+ }
+ else {
+ $pattern = variable_get('devshop_project_environment_url_pattern', '@project.@environment.@hostname');
+ }
+
+ $labels = explode('@environment', strtr($pattern, array(
+ '@project' => $project_node->title,
+ '@hostname' => $_SERVER['SERVER_NAME'],
+ )));
+
+ // Hide the "Domain Name" field, as this will be generated
+ $form['title']['#access'] = false;
+ $form['title']['#required'] = false;
+
+ $form['environment_name'] = array(
+ '#title' => t('Environment Name'),
+ '#type' => 'textfield',
+ '#description' => t('Enter a system name for your environment. For consistency, you might want to match the branch name.'),
+ '#required' => TRUE,
+ '#field_prefix' => '
http://' . $labels[0] . '
',
+ '#field_suffix' => '
' . $labels[1] .'
+
',
+ '#size' => 10,
+ '#maxlength' => 64,
+ '#weight' => -101,
+ '#attributes' => array(
+ 'data-placement' => 'bottom',
+ ),
+ '#element_validate' => array(
+ 'devshop_projects_create_environment_form_validate_name'
+ ),
+ '#wrapper_attributes' => array(
+ 'class' => array('col-sm-8 col-md-8'),
+ ),
+ );
+
+ $branch_options = devshop_projects_git_ref_options($project);
+ $form['git_ref'] = array(
+ '#title' => t('Branch or Tag'),
+ '#type' => 'select',
+ '#description' => t('The git reference to checkout for this environment. You can change this later using the "Deploy Code" button.'),
+ '#options' => $branch_options,
+ '#required' => TRUE,
+ '#weight' => -100,
+ '#wrapper_attributes' => array(
+ 'class' => array('col-sm-4 col-md-4'),
+ ),
+ );
+
+ $form['install_method'] = array(
+ '#type' => 'fieldset',
+ '#weight' => -99,
+ '#tree' => TRUE,
+ '#attributes' => array(
+ 'class' => array('clearfix'),
+ ),
+ );
+ $form['install_method']['method'] = array(
+ '#type' => 'radios',
+ '#required' => TRUE,
+ '#title' => t('Install Method'),
+ '#process' => array('devshop_environment_method_process'),
+ '#weight' => -99,
+ '#options' => array(
+ 'clone' => t('Clone Environment'),
+ 'profile' => t('Drupal Profile'),
+ 'manual' => t('Empty Database'),
+ 'import' => t('Import Database'),
+ ),
+ '#default_value' => 'clone',
+ );
+
+ $form['install_method']['none'] = array(
+ '#type' => 'container',
+ '#weight' => -98,
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="install_method[method]"]' => array('value' => 'manual'),
+ ),
+ ),
+ 'note' => array(
+ '#markup' => t('An empty database will be created. You can install Drupal manually by visiting install.php, by using Drush, or you can manually import a database.'),
+ ),
+ );
+
+ $form['install_method']['profile'] = array(
+ '#type' => 'radios',
+ '#title' => t('Drupal Install Profile'),
+ '#description' => t('These install profiles were found in other environments in this project. If you choose an install profile that does not exist in the codebase, the "Standard" profile will be used.'),
+ '#description_display' => 'before',
+ '#weight' => -98,
+ '#required' => TRUE,
+ '#options' => devshop_environment_add_form_profile_options($project),
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="install_method[method]"]' => array('value' => 'profile'),
+ ),
+ ),
+ );
+
+ // Set default profile.
+ if (count($form['install_method']['profile']['#options']) == 1 || empty($project->install_profile)) {
+ $form['install_method']['profile']['#default_value'] = '_other';
+ }
+ else {
+ $form['install_method']['profile']['#default_value'] = $project->install_profile;
+ }
+
+ $form['install_method']['profile_other'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Other Install Profile'),
+ '#title_display' => 'invisible',
+ '#description' => t('Enter the exact name the install profile to use. It must already exist in your git branch or tag.'),
+ '#element_validate' => array(
+ 'devshop_projects_create_environment_form_validate_profile'
+ ),
+ '#attributes' => array(
+ 'placeholder' => t('Profile Name'),
+ ),
+ '#weight' => -97,
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="install_method[method]"]' => array('value' => 'profile'),
+ ':input[name="install_method[profile]"]' => array('value' => '_other'),
+ ),
+ ),
+ );
+
+ // Generate environments options.
+ foreach ($project->environments as $e => $environment) {
+ $link = l(' ' . $environment->uri, $environment->url, array(
+ 'html' => TRUE,
+ 'attributes' => array(
+ 'target' => '_blank',
+ 'class' => array('btn-text'),
+ ),
+ )) . '';
+ $git_ref = $environment->git_ref;
+ $icon = $environment->git_ref_type == 'tag'? 'tag': 'code-fork';
+ $environment_options[$environment->system_alias] = "{$environment->name} $git_ref $link";
+ }
+
+ // Add other aliases to the clone source.
+ if (isset($project->settings->aliases)) {
+ foreach ($project->settings->aliases as $alias => $data) {
+ $link = l(' ' . $data['uri'], 'http://' . $data['uri'], array(
+ 'html' => TRUE,
+ 'attributes' => array(
+ 'target' => '_blank',
+ 'class' => array('btn-text'),
+ ),
+ )) . '';
+ $remote = t('Unknown');
+ $environment_options["@{$project->name}.{$alias}"] = "{$alias} $remote $link";
+ }
+ }
+
+ $environment_options['_other'] = t('Other Drush Alias');
+
+ if (count($environment_options)) {
+ $form['install_method']['clone_source'] = array(
+ '#type' => 'radios',
+ '#title' => t('Environment to clone'),
+ '#weight' => -98,
+ '#required' => TRUE,
+ '#options' => $environment_options,
+ '#default_value' => key($environment_options),
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="install_method[method]"]' => array('value' => 'clone'),
+ ),
+ ),
+ );
+ }
+
+ // Detect clone link. Set defaults.
+ if (arg(4) == 'clone') {
+ $environment_to_clone = arg(5);
+ $form['install_method']['method']['#default_value'] = 'clone';
+ $form['install_method']['clone_source']['#default_value'] = $project->environments[$environment_to_clone]->system_alias;
+ $form['git_ref']['#default_value'] = $project->environments[$environment_to_clone]->git_ref;
+ }
+
+ // Detect fork link.
+ // @TODO: Get "forking" back in place for the next beta.
+// if (arg(4) == 'fork') {
+// $environment_to_clone = arg(5);
+// $form['install_method']['method']['#default_value'] = 'clone';
+// $form['install_method']['clone_source']['#default_value'] = $project->environments[$environment_to_clone]->system_alias;
+// $form['git_ref']['#default_value'] = $project->environments[$environment_to_clone]->git_ref;
+// $form['git_ref']['#title'] = t('Base Branch or Tag');
+// $form['git_ref']['#description'] = t('The git reference you want to create a new branch from.');
+//
+// $form['git_ref_new'] = array(
+// '#title' => t('New branch name'),
+// '#description' => t('New branch name'),
+// );
+// }
+
+
+ $form['install_method']['clone_source_drush'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Drush Alias'),
+ '#description' => t('Enter a Drush alias you would like to Sync your database and files from. It must already exist on the server.'),
+ '#element_validate' => array(
+ 'devshop_projects_create_environment_form_validate_clone_source'
+ ),
+ '#weight' => -98,
+ '#attributes' => array(
+ 'placeholder' => t('@drush.alias'),
+ ),
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="install_method[clone_source]"]' => array('value' => '_other'),
+ ':input[name="install_method[method]"]' => array('value' => 'clone'),
+ ),
+ ),
+ );
+
+ $form['install_method']['import'] = array(
+ '#type' => 'textfield',
+ '#title' => t('Path to SQL'),
+ '#description' =>
+ t('Enter either a remote MySQL address (such as mysql://username:password@host/database), or an absolute path to an SQL dump (such as /var/aegir/site-backup.sql).') .
+ '' . t('This string is stored in plain text. Use with caution.') . ''
+ ,
+ '#weight' => -98,
+ '#states' => array(
+ 'visible' => array(
+ ':input[name="install_method[method]"]' => array('value' => 'import'),
+ ),
+ ),
+ '#element_validate' => array(
+ 'devshop_projects_create_environment_form_validate_import'
+ ),
+ );
+
+ $form['server_stack'] = array(
+ '#type' => 'fieldset',
+ '#title' => t('Server Stack'),
+ '#group' => 'environment_settings',
+ );
+
+ $form['server_stack']['db_server'] = $form['db_server'];
+ $form['server_stack']['db_server']['#group'] = 'environment_settings';
+ $form['server_stack']['db_server']['#tree'] = FALSE;
+ unset($form['db_server']);
+
+ $web_servers = hosting_get_servers('http', FALSE);
+ $form['server_stack']['web_server'] = array(
+ '#type' => 'radios',
+ '#title' => t('Web server'),
+ '#description' => t('The web server the site will be hosted on.'),
+ '#options' => $web_servers,
+ '#required' => TRUE,
+ '#tree' => FALSE,
+ '#default_value' => $project->settings->default_environment['web_server'],
+ );
+
+ // Force the new environment to use the "default" servers.
+ if ($project->settings->default_environment['force_default_servers']) {
+ $form['server_stack']['#description'] = t('All environments in this project must use this server stack.');
+
+ $web_server_nid = $project->settings->default_environment['web_server'];
+ $form['server_stack']['web_server']['#type'] = 'value';
+ $form['server_stack']['web_server']['#value'] = $web_server_nid;
+ $form['server_stack']['web_server_label'] = array(
+ '#type' => 'item',
+ '#title' => t('Web server'),
+ '#markup' => l($web_servers[$web_server_nid], "node/$web_server_nid", array('attributes' => array('target' => '_blank'))),
+ );
+
+ $db_servers = hosting_get_servers('db', FALSE);
+ $db_server_nid = $project->settings->default_environment['db_server'];
+ $form['server_stack']['db_server']['#type'] = 'value';
+ $form['server_stack']['db_server']['#value'] = $db_server_nid;
+ $form['server_stack']['db_server_label'] = array(
+ '#type' => 'item',
+ '#title' => t('Database server'),
+ '#markup' => l($db_servers[$db_server_nid], "node/$db_server_nid", array('attributes' => array('target' => '_blank'))),
+ );
+ }
+
+ $form['project_nid'] = array(
+ '#value' => $project->nid,
+ '#type' => 'value',
+ );
+
+ $form['platform_node'] = array(
+ '#type' => 'value',
+ '#value' => NULL,
+ );
+
+
+ // Remove platform and profile options.
+ // @TODO: Patch Aegir so we don't have to load all of these platform options.
+ $form['platform']['#access'] = FALSE;
+ $form['profile']['#access'] = FALSE;
+ $form['git']['#access'] = FALSE;
+}
+
+/**
+ * Validator for the domain name field: Sets domain according to pattern.
+ * @param $element
+ * @param $form_state
+ * @param $form
+ */
+function devshop_projects_create_environment_form_validate_name($element, &$form_state, $form) {
+
+ if (!empty($element['#value'])) {
+ $project_node = node_load($form_state['values']['project_nid']);
+ $project = $project_node->project;
+
+ // Check existence of the environment name
+ if (!empty($project->environments[$element['#value']])) {
+ form_set_error('environment_name', t('There is already an environment named %name in this project. Please choose a different name.', array('%name' => $form_state['values']['environment_name'])));
+ }
+
+ // Check for illegal chars
+ if (!preg_match('!^[a-z0-9_]+$!', $element['#value'])) {
+ form_set_error('environment_name', t('The environment name must contain only lowercase letters, numbers, and underscores.'));
+ }
+
+ // Generate field prefix and suffix using domain name pattern.
+ form_set_value($form['title'], devshop_environment_url($project, $element['#value']), $form_state);
+
+ // Attach a new platform node.
+ $platform_node = devshop_prepare_platform_node($project, $element['#value'], $form_state['values']['git_ref'], $form_state['values']['web_server']);
+
+ // Set value in form_state.
+ form_set_value($form['platform_node'], $platform_node, $form_state);
+ form_set_value($form['profile'], 0, $form_state);
+ form_set_value($form['platform'], 0, $form_state);
+
+ }
+}
+
+/**
+ * Helper to prepare the platform object to be attached to the site node.
+ *
+ * Example:
+ *
+ * $platform = devshop_prepare_platform_node($project, $environment_name, $git_ref, $web_server, $prepare_node);
+ * if ($platform = node_submit($platform)) {
+ * node_save($platform);
+ * }
+ *
+ * @param $project
+ * A fully loaded project object.
+ *
+ * @param $environment_name
+ * The name of the environment.
+ *
+ * @param $git_ref
+ * The desired git reference for the environment.
+ *
+ * @param $web_server
+ * The NID of a web server to use for this platform.
+ *
+ * @return stdClass
+ * A populated object ready for node_submit and node_save.
+ */
+function devshop_prepare_platform_node($project, $environment_name, $git_ref, $web_server) {
+ $platform_node = new stdClass();
+ $platform_node->type = 'platform';
+ $platform_node->title = $project->name . '_' . $environment_name;
+
+ // Specify GIT Url for hosting_git.module
+ $platform_node->git['repo_url'] = $project->git_url;
+ $platform_node->git['git_ref'] = $git_ref;
+
+ // Enable git pull queue for this platform if chosen.
+ if ($project->settings->deploy['method'] == 'queue') {
+ $platform_node->git['pull_method'] = HOSTING_GIT_PULL_QUEUE;
+ }
+ // For devshop "webhook" and "manual" methods, disable git pull for now.
+ // @TODO: Remove devshop "code deploy" task in exchange for hosting_git_pull module.
+ else {
+ $platform_node->git['pull_method'] = HOSTING_GIT_PULL_DISABLED;
+ }
+
+
+ // Determine which web server to use.
+ // If no web server was set and project has a default, make sure we set it.
+ if (empty($web_server) && !empty($project->settings->default_environment['web_server'])) {
+ $platform_node->web_server = $project->settings->default_environment['web_server'];
+ }
+ else {
+ $platform_node->web_server = $web_server;
+ }
+
+ $platform_node->git['repo_path'] = $project->code_path . '/' . $environment_name;
+
+ // Append drupal_path to repo_path if there is one. If not, repo_path is publish_path.
+ if ($project->drupal_path) {
+ $platform_node->publish_path = $platform_node->git['repo_path'] . '/' . $project->drupal_path;
+ $platform_node->git['repo_docroot'] = $project->drupal_path;
+ }
+ else {
+ $platform_node->publish_path = $platform_node->git['repo_path'];
+ }
+ return $platform_node;
+}
+
+/**
+ * Validator for the "other profile" field.
+ *
+ * @param $element
+ * @param $form_state
+ * @param $form
+ */
+function devshop_projects_create_environment_form_validate_profile($element, &$form_state, &$form) {
+ if ($form_state['values']['install_method']['method'] == 'profile' && $form_state['values']['install_method']['profile'] =='_other' && empty($element['#value'])) {
+ form_set_error('install_method][profile_other', t('You must enter a profile name.'));
+ }
+ elseif ($form_state['values']['install_method']['method'] == 'profile' && $form_state['values']['install_method']['profile'] =='_other') {
+ form_set_value($form['install_method']['profile'], $element['#value'], $form_state);
+ }
+}
+
+/**
+ * Validator for the "clone_source" field.
+ *
+ * @param $element
+ * @param $form_state
+ * @param $form
+ */
+function devshop_projects_create_environment_form_validate_clone_source($element, &$form_state, $form) {
+ if ($form_state['values']['install_method']['method'] == 'clone' && $form_state['values']['install_method']['clone_source'] =='_other' && empty($element['#value'])) {
+ form_set_error('install_method][clone_source_drush', t('You must enter a drush alias.'));
+ }
+}
+
+/**
+ * Validator for the "Path to SQL" field.
+ *
+ * @param $element
+ * @param $form_state
+ * @param $form
+ */
+function devshop_projects_create_environment_form_validate_import($element, &$form_state, $form) {
+
+ $url_components = parse_url($element['#value']);
+
+ if ($form_state['values']['install_method']['method'] == 'import' && empty($element['#value'])) {
+ form_set_error('install_method][import', t('If using the "Import Database" Install Method, you must enter a MySQL connection URL or absolute path to an SQL file.'));
+ }
+ elseif ($url_components['scheme'] == 'mysql') {
+ $database = array(
+ 'database' => ltrim($url_components['path'], '/'),
+ 'username' => $url_components['user'],
+ 'password' => $url_components['pass'],
+ 'host' => $url_components['host'],
+ 'driver' => 'mysql', // replace with your database driver
+ );
+ Database::addConnectionInfo('devshop_remote_db', 'default', $database);
+
+ try {
+ db_set_active('devshop_remote_db');
+ $tables = db_query('SHOW TABLES');
+ drupal_set_message(t('Database connection successful.'));
+ }
+ catch (\PDOException $e) {
+ form_set_error('install_method][import', t('Unable to connect to the database: %e', array('%e' => $e->getMessage())));
+ }
+ finally {
+ db_set_active();
+ }
+ }
+}
+
+/**
+ * Validator for site_node_form for node creation, when creating an environment
+ * in a project.
+ */
+function devshop_projects_create_environment_form_submit($form, &$form_state) {
+
+ // @TODO: Clean up install_method values.
+
+}
+
+/**
+ * Form element processor for Install Method radio buttons.
+ * @param $element
+ * @return array
+ */
+function devshop_environment_method_process($element) {
+ $element = form_process_radios($element);
+
+ foreach (element_children($element) as $i) {
+ $element[$i]['#label_attributes']['class'] = array(
+ 'btn btn-link'
+ );
+ $element[$i]['#wrapper_attributes']['class'] = array(
+ 'install-method-wrapper'
+ );
+ }
+
+ return $element;
+}
+
+/**
+ * Return all available install profiles found in all platforms for this project.
+ */
+function devshop_environment_add_form_profile_options($project) {
+
+
+ foreach ($project->environments as $e => $environment) {
+
+ $packages = hosting_package_instances_load(array(
+ 'package_type' => 'profile',
+ 'rid' => $environment->platform,
+ ));
+
+ foreach ($packages as $instance) {
+ $options[$instance->short_name] = $instance->title;
+ $options[$instance->short_name] .= ' ' . $instance->description . '';
+ }
+ }
+ $options['_other'] = t('Other') . ' ' . t('Choose another profile.') . '';
+ return $options;
+}
+
+/**
+ * Helper to output the forms selection for Deploy Hooks.
+ *
+ * Used in the Projects settings form, environment settings form, deploy task form, and sync form.
+ *
+ * @param $project
+ * @param null $environment
+ * @return array
+ */
+function devshop_environment_deploy_hooks_form($project, $environment = NULL, $task_type = NULL) {
+
+ // If environment not specified, just grab one.
+ if (!$environment) {
+ $environment = current($project->environments);
+ $is_environment_form = FALSE;
+ }
+ else {
+ $is_environment_form = TRUE;
+ }
+ $return = array(
+ '#type' => 'fieldset',
+ '#group' => 'project_settings',
+ '#title' => t('Deployment Hooks'),
+ '#description' => t('Deployment hooks are run whenever your codebase changes. It is recommended to always enable database updates and cache clearing.'),
+ '#weight' => -10,
+ );
+
+ $return['#project'] = $project;
+ $return['#environment'] = $environment;
+
+ // If we are on the project creation wizard, set some sane defaults
+ if (current_path() == 'projects/add/settings') {
+ $environment_update = FALSE;
+ $environment_cache = FALSE;
+ $environment_revert = FALSE;
+ $environment_dothooks = FALSE;
+ $environment_acquia_hooks = FALSE;
+ $environment_composer_install = FALSE;
+ $project_update = TRUE;
+ $project_cache = TRUE;
+ $project_revert = FALSE;
+ $project_dothooks = FALSE;
+ $project_acquia_hooks = FALSE;
+ $project_composer_install = FALSE;
+ }
+ else {
+ $environment_update = isset($environment->settings->deploy) ? $environment->settings->deploy['update'] : FALSE;
+ $environment_cache = isset($environment->settings->deploy) ? $environment->settings->deploy['cache'] : FALSE;
+ $environment_revert = isset($environment->settings->deploy) ? $environment->settings->deploy['revert'] : FALSE;
+ $environment_dothooks = isset($environment->settings->deploy) ? $environment->settings->deploy['dothooks'] : FALSE;
+ $environment_acquia_hooks = isset($environment->settings->deploy) ? $environment->settings->deploy['acquia_hooks'] : FALSE;
+ $environment_composer_install = isset($environment->settings->deploy) ? $environment->settings->deploy['composer'] : FALSE;
+ $project_update = isset($project->settings->deploy) ? $project->settings->deploy['default_hooks']['update'] : FALSE;
+ $project_cache = isset($project->settings->deploy) ? $project->settings->deploy['default_hooks']['cache'] : FALSE;
+ $project_revert = isset($project->settings->deploy) ? $project->settings->deploy['default_hooks']['revert'] : FALSE;
+ $project_dothooks = isset($project->settings->deploy) ? $project->settings->deploy['default_hooks']['dothooks'] : FALSE;
+ $project_acquia_hooks = isset($project->settings->deploy) ? $project->settings->deploy['default_hooks']['acquia_hooks'] : FALSE;
+ $project_composer_install = isset($project->settings->deploy) ? $project->settings->deploy['default_hooks']['composer'] : FALSE;
+ }
+
+ $return['update'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Run database updates.'),
+ '#default_value' => empty($environment->settings->deploy) ? $project_update : $environment_update,
+ );
+ $return['cache'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Clear all caches.'),
+ '#default_value' => empty($environment->settings->deploy)? $project_cache : $environment_cache,
+ );
+ $return['revert'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Revert all features.'),
+ '#description' => t('If features is enabled, revert all of them.'),
+ '#default_value' => empty($environment->settings->deploy)? $project_revert : $environment_revert,
+ );
+ // Look for .hooks or .hooks.yml
+ if (!$is_environment_form || isset($project->settings->deploy, $project->settings->deploy['default_hooks']['dothooks'])) {
+ $return['dothooks'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Run deploy commands in the .hooks file.'),
+ '#default_value' => empty($environment->settings->deploy) ? $project_dothooks : $environment_dothooks,
+ '#description' => t('You can add your desired deploy hooks to a file in the root folder of your project. This is recommended as it gives your developers control over what happens when their code is deployed. See the !dothooks for more information. If you use a .hooks file you probably want to uncheck the deploy hooks here.', array(
+ '!dothooks' => l(t('.hooks documentation'), 'admin/help/devshop_dothooks'),
+ )),
+ );
+ }
+
+ // Look for acquia cloud hooks.
+ if (module_exists('devshop_acquia') && (!$is_environment_form || isset($project->settings->deploy, $project->settings->deploy['default_hooks']['acquia_hooks']))) {
+ $return['acquia_hooks'] = array(
+ '#type' => 'checkbox',
+ '#title' => t('Run Acquia Cloud Hooks'),
+ '#description' => '
' . t('Acquia Cloud Hooks were detected in your project. Check this box to run all acquia cloud hooks.') . '
' . t('Available cloud hooks are: "post-code-update" when deploying code via git, "post-code-deploy" when deploying code manually, "post-db-copy" when running a sync task with a database, and "post-files-copy" when running a sync task with files.') . '
',
+ '#default_value' => empty($environment->settings->deploy) ? $project_acquia_hooks : $environment_acquia_hooks,
+ );
+ }
+
+ // Allow other modules to alter the deploy hooks form elements.
+ // @TODO: Create a new hook: "hook_devshop_deploy_hooks()"
+ drupal_alter('devshop_deploy_hooks_form_elements', $return, $is_environment_form);
+
+ // Disable deploy hooks on environment form, deploy task form ,and sync task form.
+ if (
+ // Environment form
+ ($is_environment_form && !$project->settings->deploy['allow_environment_deploy_config']) ||
+ ($task_type == 'devshop-deploy' && !$project->settings->deploy['allow_environment_deploy_hooks_override'])
+ ) {
+
+ foreach (element_children($return) as $i) {
+ $return[$i]['#type'] = 'value';
+ $return[$i]['#value'] = $project->settings->deploy['default_hooks'][$i];
+
+ $return["{$i}_display"] = array(
+ '#type' => 'markup',
+ '#markup' => t($return[$i]['#title']),
+ '#prefix' => '