Merge branch 'master' of feistymeow.org:feisty_meow
authorChris Koeritz <fred@gruntose.com>
Thu, 16 Nov 2017 19:27:11 +0000 (14:27 -0500)
committerChris Koeritz <fred@gruntose.com>
Thu, 16 Nov 2017 19:27:11 +0000 (14:27 -0500)
47 files changed:
documentation/feisty_meow_command_reference.txt [new file with mode: 0644]
infobase/feisty_inits/dot.bashrc-root
infobase/fortunes.dat
infobase/sounds/were_sorry_youre_a_spammer.mp3 [new file with mode: 0644]
production/feisty_meow_config.ini
production/sites/cakelampvm.com/docs/manual/cakelampvm_guide_v002.html [new file with mode: 0644]
production/sites/cakelampvm.com/docs/manual/images/host_only_adapter_dhcp_server.png [new file with mode: 0644]
production/sites/cakelampvm.com/docs/manual/images/host_only_network_adapter.png [new file with mode: 0644]
production/sites/cakelampvm.com/docs/manual/images/nat_network_config.png [new file with mode: 0644]
production/sites/cakelampvm.com/goog_maps_helper_mod/GoogleMapHelper.php [new file with mode: 0644]
production/sites/cakelampvm.com/goog_maps_helper_mod/compare_with_install.sh [new file with mode: 0644]
production/sites/cakelampvm.com/hello.txt [new file with mode: 0644]
production/sites/cakelampvm.com/images/green-check-mark-in-circle-hi.png [new file with mode: 0644]
production/sites/cakelampvm.com/images/green_globe_exclamation_point_570.jpg [new file with mode: 0644]
production/sites/cakelampvm.com/images/red_exclamation_mark_icon_256.png [new file with mode: 0644]
production/sites/cakelampvm.com/images/red_x_1194985626525719339tasto_11_architetto_fran_01.svg.med.png [new file with mode: 0644]
production/sites/cakelampvm.com/index.html [new file with mode: 0644]
production/sites/cakelampvm.com/vm_index.html [new file with mode: 0644]
readme.txt
scripts/agenda/info_overload_report.sh
scripts/archival/snarf_feisty_meow.pl
scripts/core/common.alias
scripts/core/connect_feisty_meow.sh
scripts/core/functions.sh
scripts/core/inventory.sh
scripts/core/launch_feisty_meow.sh
scripts/core/reconfigure_feisty_meow.sh
scripts/core/variables.sh
scripts/files/remove_here_if_not_there.sh
scripts/rev_control/puffer.sh [new file with mode: 0644]
scripts/rev_control/version_control.sh
scripts/site_avenger/avcoreup.sh
scripts/site_avenger/config/default.app
scripts/site_avenger/powerup.sh
scripts/site_avenger/revamp_cakelampvm.sh
scripts/site_avenger/shared_site_mgr.sh
scripts/site_avenger/sitepush.sh
scripts/site_avenger/siteup.sh
scripts/site_avenger/standup.sh
scripts/site_avenger/teardown.sh
scripts/system/add_apache_site.sh
scripts/system/add_domain.sh
scripts/system/common_sysadmin.sh [new file with mode: 0644]
scripts/system/remove_apache_site.sh [new file with mode: 0644]
scripts/system/remove_domain.sh [new file with mode: 0644]
scripts/tty/terminal_titler.sh
walrus/utilities/multimedia/multimedia_tools.txt

diff --git a/documentation/feisty_meow_command_reference.txt b/documentation/feisty_meow_command_reference.txt
new file mode 100644 (file)
index 0000000..5cbd70e
--- /dev/null
@@ -0,0 +1,158 @@
+
+Welcome, adventurous script user.
+
+These are the handiest commands available in the Feisty Meow scripts.
+Note that each script is expected to be self-documenting.  Try running it
+with a "--help" flag (or with no parameters in some cases) to print the
+built-in docs.  At worst, you may have to read the script (documentation
+fail--please let us know).
+
+setup and loading commands
+==========================
+
++ read "readme.txt" in the top of the feisty meow codebase, or
++ read it online at: https://feistymeow.org/feisty_meow/readme.txt
+
+revision control commands
+=========================
+
+all revision control commands bring up the editor in the EDITOR environment
+variable when creating commit messages.  you need to actually save and quit
+from that editor when you're done writing your commit message.
+
+  here's a guide to writing good commit messages:
+  + https://robots.thoughtbot.com/5-useful-tips-for-a-better-commit-message
+
+========
+the first suite of commands takes a list of directory names as parameters and
+then operates on those names.
+========
+
+  rgetem:
+  does a simple update (or pull) of the repository paths provided on the
+  command line.  this will only get things from the main origin that the
+  repository is hooked up with, so it is super quick compared to the next
+  couple commands.
+
+  rpuffer:
+  update the repositories provided on the command line by "puffing them out",
+  which means that the upstream repositories that feed the local one will be
+  synched up with it.  this is quite important to do when a git repository has
+  multiple branches, since unmerged changes upstream can really snarl up your
+  checkin.  this is basically a heavyweight version of rgetem.
+
+  rcheckin:
+  checks in the list of repositories passed on the command line.  in git
+  parlance, this adds all modified or untracked files, then commits all
+  changes in the repository, and finally pushes up the changes to the remote
+  online repository.  before doing the checkin, this will do a full "rpuffer"
+  update on the repository to ensure that there are no unmerged upstream
+  changes that could cause problems later.
+
+========
+the next suite of commands uses the REPOSITORY_LIST environment variable as
+the set of revision controlled folders to operate on.  the feisty meow scripts
+automatically add the feisty meow top-level (the apex) to this list to ensure
+that updates are received when available.
+========
+
+  getem:
+  update all repositories in the REPOSITORY_LIST from their upstream remote
+  counterparts.  fast.
+
+  puffer:
+  puffs out the REPOSITORY_LIST items to merge upstream changes.
+
+  checkin:
+  checks in all changes in the REPOSITORY_LIST to their remote repositories.
+
+========
+some assorted other revision control commands:
+========
+
+  feisty_branch:
+  shows the current branch that is checked out.
+
+  this command will move your feisty meow codebase to the development branch:
+  pushd $FEISTY_MEOW_APEX; git checkout dev; popd
+
+  and this command will get you back onto the mainline branch:
+  pushd $FEISTY_MEOW_APEX; git checkout master; popd
+
+=============================
+the site avenger script suite
+=============================
+
+the site avenger tools (inherited from the avbash project) are commands for
+managing web sites.  these scripts offer a lot of power to the developer, and
+of course that comes with great responsibility...
+
+the site avenger scripts are configured by "app" files stored in the "config"
+directory (in $FEISTY_MEOW_SCRIPTS/scripts/site_avenger/config).  the scripts
+seek out a config file named after the application, e.g. they look for
+"winterportlibrary.app" if the application name is "winterportlibrary".
+the basic config file "default.app" is used for any application that is unknown
+in the config directory.  any of the variable definitions provided in
+default.app can be overridden to change how the applications, and associated
+web site and domain, are configured.  see "mapsdemo.app" for an example of
+overriding the domain name for the mapsdemo application.
+
+  revamp_cakelampvm:
+  establishes permissions and ownership to make the virtual machine and its
+  services behave properly.  if something goes wonky, try running this script.
+  this script is also the main vehicle for delivering configuration changes
+  to the cakelampvm.  we are trying really hard to never release a version 2
+  of the vm, since we can patch it as needed using the revamp script.  let's
+  see how well that works out...
+
+  standup:
+  brings up an application or web site from scratch (potentially) by creating
+  an appropriate domain name, writing a basic apache site config file, pulling
+  the application from a git repository, and "powering up" the application via
+  composer.  this is most powerful and effective on php sites, but can also be
+  used for other types of websites.  note that this, and all of the scripts
+  here, are heavily biased for site avenger based development at saco designs.
+  to make these scripts truly your own, write configuration files (see above)
+  that define the proper folders and repository for your applications.
+
+  teardown:
+  takes down a site previously brought up by the standup command.  this just
+  eliminates the domain and the apache site though; the code is left in place
+  to prevent disaster.
+  
+  powerup:
+  similar to standup, but just gets the application source out and powers it
+  up with composer.
+(note: automatic database configuration and inflation is in the pipeline but
+is not ready yet.  that will go into powerup.)
+  
+  avcoreup:
+  updates the avcore portion of a site avenger application.  this command can
+  accept an application name within which to update, or it can auto-pick the
+  applicatin for you from the available checked out ones in ~/apps (the default
+  storage folder for all site avenger style sites).
+
+  siteup:
+  updates the entire checked out repository for a site avenger application.
+  supports app name on the command line, or auto-picks the app.
+
+  sitepush:
+  checks in the source code and other site assets for a site avenger app.
+  supports passing an app name on the command line, or auto-picks the app.
+
+
+  satis-refresh:
+  updates satis for a site(?).
+note: this is the one site avenger command that hasn't been "feisty meowicized" yet.
+
+lower level scripts used by site avenger scripts:
+
+  add_domain / remove_domain: (from system script collection)
+  adds a DNS domain to the bind9 configuration.
+  (the domain tools, for example, are
+    very sensitive to edits within the chunks of code they have written.  if you
+  need to edit bind config files, be sure to do it way above or way below the
+  auto-generated domains.)
+
+  add_apache_site / remove_apache_site:
+
index 453a0fbd472e64b4557e61728850842eb2dd7cc5..e0facb150007d8b81a7e37f0802fc31e5fccf806 100644 (file)
@@ -24,7 +24,7 @@ export FEISTY_MEOW_APEX="/opt/feistymeow.org/feisty_meow"
 
 # the "fredme" macro enables the feisty_meow environment.
 alias fredme='source "$FEISTY_MEOW_APEX/scripts/core/launch_feisty_meow.sh"'
-# synonym for fredme.
+# synonym for fredme which makes more sense to most people.
 alias feistyme='source "$FEISTY_MEOW_APEX/scripts/core/launch_feisty_meow.sh"'
 
 # if not commented out, then feisty meow will run all the unit tests during builds.
index 4b508630323329b784086d78e521b67815242972..1f462d80b874fe56a077245b02073bdd8f2508a6 100644 (file)
@@ -43018,3 +43018,11 @@ elements into enlightenment by means of that connection.   
 ~
 Never admit defeat.  Just move the front.
   -- fred t. hamster
+~
+down came eddy from his heady,
+where he dwells often unsteady,
+you see he gets so high,
+mind expanded to sky;
+real world grokking just not ready.
+  -- fred t. hamster
+
diff --git a/infobase/sounds/were_sorry_youre_a_spammer.mp3 b/infobase/sounds/were_sorry_youre_a_spammer.mp3
new file mode 100644 (file)
index 0000000..57f1eaf
Binary files /dev/null and b/infobase/sounds/were_sorry_youre_a_spammer.mp3 differ
index de81f33b401d455bde0eabe1c347cc6f7642b2b2..7abba84985ba34a03701cc1c71eb7d042579530e 100644 (file)
@@ -3,7 +3,7 @@
 # specifies the version of the code that is being constructed here.
 major=2
 minor=140
-revision=98
+revision=100
 build=420
 
 # specifies the remainder of the version record info.
diff --git a/production/sites/cakelampvm.com/docs/manual/cakelampvm_guide_v002.html b/production/sites/cakelampvm.com/docs/manual/cakelampvm_guide_v002.html
new file mode 100644 (file)
index 0000000..0106350
--- /dev/null
@@ -0,0 +1,355 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta content="text/html; charset=windows-1252" http-equiv="content-type">
+    <title>Cake LAMP VM Documentation</title>
+  </head>
+  <body>
+    <h1 style="text-align: center;">The cakelampvm VM:<br>
+      Configuration and Usage</h1>
+    <h2 style=" text-align: center;">By Chris Koeritz</h2>
+    <h3 style="   text-align: center;"> Vintage: cakelampvm v002 &nbsp;&nbsp;
+      Updated: 2017-11-16</h3>
+    <p>The cakelampvm project provides a virtualbox VM that acts as an "internet
+      in a bottle".&nbsp; The virtual machine provides DNS services (<a title="dns server"
+        href="http://www.bind9.net/">bind9</a>), a Web server (<a title="patchy"
+        href="https://httpd.apache.org/">Apache2</a>), a full <a title="ubuntu means compassion and humanity"
+        href="https://www.ubuntu.com/">Ubuntu</a> <a title="it's pronounced leenoox"
+        href="https://www.linuxfoundation.org/">Linux</a> desktop environment,
+      the <a title="flux is change" href="http://fluxbox.org/">Fluxbox</a> <a
+        title="a better windows" href="https://www.x.org/">X window manager</a>,
+      and a suite of tools called the <a title="feisty meow® concerns ltd. website"
+        href="https://feistymeow.org/">Feisty Meow® codebase</a>
+      <meta http-equiv="content-type" content="text/html; charset=utf-8">
+      .&nbsp; Together, these services provide you with a very flexible and
+      powerful testbed for web development, especially suited for <a title="it's cake"
+        href="https://cakephp.org/">CakePHP</a>.</p>
+    <p>todo: arrange gritty details to back.</p>
+    <p>TOC GOES HERE.</p>
+    <h6> </h6>
+    <h2><span style="text-decoration: underline;">G</span>uest VM Configuration</h2>
+    <ul>
+      <li>hostname: <a title="the vm's website, when configured properly" href="https://cakelampvm.com/">cakelampvm.com</a></li>
+      <li>local IP address: 10.28.42.20</li>
+      <li>services: DNS (bind9), apache2, fluxbox X windowing system, <a title="not just in the garden"
+          href="https://www.gnome.org/">gnome display manager</a></li>
+      <li>main user: developer (password distributed separately)</li>
+      <li>mysql root password: (password distributed separately)</li>
+    </ul>
+    <h2>Powering up with the Feisty Meow® scripts</h2>
+    <p>[First, let me drop the registered trademark symbol from here on
+      in.&nbsp; I hope its presence above has been sufficiently clear for legal
+      purposes, but now it will just get in the way.&nbsp; Also, capitalization
+      really bores me, and it's the feisty meow codebase anyhow, so that's how
+      it will be written henceforth.]</p>
+    <p>The feisty meow scripts are a cohesive bash scripting environment for
+      getting a variety of tasks done.&nbsp; The scripts recently incorporated
+      the 'avbash' collection from Saco Designs, which provides tools for
+      bringing up CakePHP web sites and managing the collection of repositories
+      for those sites.&nbsp; Each website is considered an "application", and
+      the application name itself (e.g. "winterportlibrary") can often provide
+      all the details for "powering up" the site.&nbsp; The feisty meow team has
+      added additional scripts for managing DNS domains and Apache websites that
+      provide the capability to "stand up" an entire website around an
+      application, with accompanying domain.</p>
+    <p>The scripts for now are documented separately within the Feisty Meow
+      codebase.&nbsp; The Feisty Meow readme file provides some valuable
+      information on configuring the codebase.&nbsp; If you have the cakelampvm,
+      then this has already been done for you on the vm in the developer account.&nbsp;
+      The script documentation is available in the UHHHHHH page of something..</p>
+    <h2>How to set up virtualbox for your host PC</h2>
+    <ol>
+      <li>Download and install virtualbox:
+        https://www.virtualbox.org/wiki/Downloads</li>
+      <li>Install the extension pack for virtualbox: This provides USB drivers
+        and other features.&nbsp; This is installed on virtualbox itself, not on
+        the guests.</li>
+      <ol>
+        <li>Download the extension pack at
+          https://www.virtualbox.org/wiki/Downloads</li>
+        <li>Stop any running virtualbox vms.</li>
+        <li>Close virtualbox control panel.</li>
+        <li>Double-click on the downloaded extensions package (in a file
+          explorer) and virtualbox should install it.</li>
+      </ol>
+      <li>Run the virtualbox control panel.</li>
+      <li>Download the cakelampvm guest vm package and unzip it.&nbsp; Store the
+        unzipped version in some appropriate place where you want the virtual
+        machine to reside on your host's hard drive.</li>
+      <li>Add the guest VM to your list of VMs.&nbsp; From the virtualbox menus,
+        choose the "Machine" menu and select "Add".&nbsp; Point the selector
+        dialog at the cakelampvm folder you created above and open the
+        cakelampvm.vbox file.</li>
+      <li>Now the cakelampvm should show up in the list of virtual
+        machines.&nbsp; Before starting it, perform the following network
+        configuration sections.</li>
+    </ol>
+    <h3>Configure the Host-Only network on virtualbox</h3>
+    <p>Configuring host-only networking for the VM makes the VM completely local
+      to your machine.&nbsp; The cakelampvm will not be accessible on the
+      internet or from the LAN, and can only be accessed by your host PC.</p>
+    <ol>
+      <li> Go to virtual box "Preferences" (global preferences, not for a
+        specific vm).</li>
+      <li> Click on the "Network" tab.</li>
+      <li> Choose the "Host-only Networks" tab from within "Network".</li>
+      <li> Click the plus icon to add a new host-only network, or if there is
+        already a Host-only network, then edit it.</li>
+      <li>Set the "Adapter" parameters:<br>
+        IPv4 Address: 10.28.42.1<br>
+        IPv4 Network Mask: 255.255.255.0<br>
+        IPv6 Address: (leave blank)<br>
+        IPv6 Prefix Length: 0<br>
+        Virtualbox will fill in the other details like so:<br>
+        <p><img alt="host only network adapter" src="images/host_only_network_adapter.png"></p>
+      </li>
+      <li>Set the "DHCP Server Settings" to disabled, e.g.<br>
+        <img alt="host only dhcp" src="images/host_only_adapter_dhcp_server.png"><br>
+        This is disabled because we will be using statically assigned addresses
+        for convenience and stability.</li>
+    </ol>
+    <p>Additional information on host-only (and other) network adapter types is
+      at: https://www.virtualbox.org/manual/ch06.html#network_nat_service<br>
+    </p>
+    <h3>Configure the Nat Network on virtualbox</h3>
+    <ol>
+      <li> Go to virtual box "Preferences" (global preferences, not for a
+        specific vm).</li>
+      <li> Click on the "Network" tab.</li>
+      <li> Choose the "Nat Networks" tab from within "Network".</li>
+      <li> Click the plus icon to add a new host-only network.</li>
+      <li>Set the "NAT Network Details" parameters:<br>
+        Network Name: NatNetwork<br>
+        Network CIDR: 10.0.2.0/24<br>
+        Supports DHCP: checked<br>
+        Supports IPv6: optionally checked<br>
+        These are my settings, with IPv6 left disabled:<br>
+        <img alt="nat net config" src="images/nat_network_config.png"></li>
+    </ol>
+    <h2>Start up the VM</h2>
+    <p>Using the virtualbox interface, you should now be able to start your
+      virtual machine.&nbsp; Virtualbox will complain if it detects any
+      remaining configuration problems in the VM, but it should start
+      normally.&nbsp; The Linux boot sequence will show many lines of text,
+      before bringing up a black console window with a login dialog.</p>
+    <p>You can log in directly on the VM console with the developer account, but
+      it is generally more useful to connect to the cakelampvm over ssh.&nbsp;
+      If the networking has been established properly, you should be able to do
+      this with:</p>
+    <pre>ssh developer@cakelampvm.com&nbsp; (or equivalent with your ssh client)</pre>
+    <p>And then provide the password to log in.</p>
+    <p>A feature called "X forwarding" is enabled, so if you start graphical
+      applications on the VM, you can display them from an appropriately
+      configured host.&nbsp; (If you're running Linux as the host for the VM,
+      you can definitely run remote windows.&nbsp; Windows may not support
+      that.)</p>
+    <p>#### check this!!!</p>
+    <h2>Using the guest VM's DNS services</h2>
+    <p>The cakelampvm has been set up to provide a DNS server which will answer
+      name requests for all of the sites that the VM hosts.</p>
+    <p>To use the cakelampvm DNS, modify your host operating system network
+      configuration by adding or changing the DNS server to use the guest VM's
+      DNS service.&nbsp; This is available at the local address
+      10.28.42.20.&nbsp; The DNS server can be tested with nslookup, dig and
+      other tools.</p>
+    <p>Note that the cakelampvm DNS should be listed first, if one intends to
+      override any DNS names that actually exist out on the internet.</p>
+    <p>If the DNS server is properly set up, then these ping commands should get
+      answering responses:</p>
+    <pre>ping cakelampvm.com</pre>
+    <pre>ping defaultcake.cakelampvm.com</pre>
+    <pre>ping mapsdemo.cakelampvm.com</pre>
+    <h3>Setting up DNS on Windows</h3>
+    <p>The ipconfig tool will provide helpful information about your current
+      networking and DNS configuration:</p>
+    <pre>ipconfig --all</pre>
+    <p>The DNS configuration on Windows is somewhat byzantine.&nbsp; The pipe
+      characters ('|') below are used to separate the menus or tabs or dialogs
+      to traverse.&nbsp; Follow this path to get to the DNS config:</p>
+    <pre>Control Panel | Network &amp; Sharing | click WiFI or Ethernet link near top right | click Adapter Settings on left | click on specific network device to modify | select Properties</pre>
+    <p><br>
+    </p>
+    <p><br>
+    </p>
+    <p><br>
+    </p>
+    <h2>Editing files on the guest VM from the host</h2>
+    <p>On the host computer, look for the guest vm as a networked computer
+      called cakelampvm.&nbsp; This should provide some network shares using
+      Microsoft SMB protocol, and they can be attached to using the "developer"
+      user and its password.</p>
+    <p>On windows, one may want to mount this network location as a drive letter
+      for easier access.</p>
+    <p>Currently, the root of all web servers is exposed as "www".&nbsp; Editing
+      the files in those folders requires ownership by the developer user.&nbsp;
+      Currently the defaultcake server is owned by developer.</p>
+    <p>One should be able to create a new directory in the www folder owned by
+      the developer user over the network also, which can be used for creating
+      new projects.&nbsp; However, there is a config issue in the current vm
+      (v001) about this; to fix, run this command on the guest vm as the
+      developer user:</p>
+    <pre>sudo chmod g+w /var/www</pre>
+    <p>Afterwards, the www folder should allow the developer user to create new
+      folders at will.</p>
+    <h2>Accessing files on the host PC from the guest VM</h2>
+    <p>If you want to share a folder from the host to the guest, perhaps for
+      driver updates or other conveniences, then make the share with these
+      steps:</p>
+    <ol>
+      <li>Create a folder on the host that is to be shared.</li>
+      <li>Right-click on the vm in virtualbox manager and choose "Settings".</li>
+      <li>In the "Shared Folders" tab of the settings, go to "Machine Folders".</li>
+      <li>Click the folder plus icon to create a new share.</li>
+      <li>Fill in the "Folder Path" on the host PC to the folder that will be
+        shared, and give it a name for the guest.&nbsp; We assume the folder
+        name will be "myshare".</li>
+      <li>On the guest vm, run the following commands to mount the share:<br>
+        <pre>mkdir ~/shared&nbsp;&nbsp;&nbsp; # for the guest's version of the shared folder</pre>
+        <pre>sudo mount -t vboxsf myshare ~/shared&nbsp;&nbsp;&nbsp; # mount the vm's share name onto the folder on the vm.</pre>
+      </li>
+    </ol>
+    <h2>Adding a new website and domain on the guest VM</h2>
+    <p>To add a new website, you will need to pick one of the DNS options below
+      (A or B) depending on how you want to name the site.&nbsp; After the DNS
+      is updated, then follow the section after for creating a new apache conf
+      file.</p>
+    <p>Assuming one has created a new folder in "www" called "greatsite", then
+      the new web site can be brought online on the vm with one of the following
+      options.</p>
+    <h3>DNS Option A: Using a sub-domain in the cakelampvm.com domain</h3>
+    Connect to the cakelampvm via ssh as the developer user, e.g.: ssh
+    developer@cakelampvm.com
+    <p>Execute the following command to edit the DNS file for the cakelampvm
+      domain:</p>
+    <pre>sudo vi /etc/bind/cakelampvm.com.conf</pre>
+    <p>Add a stanza for the new site at the end of this file:</p>
+    <pre>greatsite.cakelampvm.com&nbsp;&nbsp;&nbsp; IN A&nbsp;&nbsp;&nbsp; 10.28.42.20<br>&nbsp;&nbsp;&nbsp; &nbsp;&nbsp;&nbsp; IN HINFO "linux server" "ubuntu"</pre>
+    <p>Restart the DNS server: sudo service bind9 restart</p>
+    <p>Afterwards, pinging greatsite.cakelampvm.com should work from either the
+      guest or the host.</p>
+    <h3>DNS Option B: Using an entirely new domain for the site</h3>
+    <p>Similar procedure to above, but we will create a new file for the new
+      domain and add it to the bind directory.&nbsp; For this example, we will
+      create a file called /etc/bind/greatsite.tv.conf for our new domain
+      greatsite.tv with these contents:</p>
+    <pre>$TTL 1W<br>@&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IN SOA&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; @&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; fred.cakelampvm.com. (<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2017100801&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; serial<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 2H&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; refresh<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 8M&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; retry<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 14D&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; expiry<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 6H )&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ; minimum<br><br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IN NS&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; ns.cakelampvm.com.<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IN MX&nbsp;&nbsp; 10&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; mail.cakelampvm.com.<br><br># main domain for machine.<br>greatsite.tv.&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IN A&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; 10.28.42.20<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; IN HINFO&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; "linux server" "ubuntu"</pre>
+    The gnarly prefix stuff above the "greatsite.tv." listing establishes
+    configuration info for the new domain.&nbsp; This file relies on the
+    existing cakelampvm.com infrastructure in DNS, such as the "ns" host, which
+    is the domain's name server.
+    <p>Now that the config file is in place, edit "named.conf.local" to add the
+      new file by adding this bit of configuration at the end:</p>
+    <pre>zone "greatsite.tv" in {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; file "/etc/bind/greatsite.tv.conf";<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; type master;<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; allow-query { any; };<br>};</pre>
+    <p>Restart the DNS server: sudo service bind9 restart</p>
+    <p>Afterwards, pinging greatsite.tv should work from either the guest or the
+      host.</p>
+    <h3>Create a new apache configuration file and load it</h3>
+    <p>Start with the following template file for the new website, and modify it
+      for the appropriate host name:</p>
+    <pre>&lt;VirtualHost *:80&gt;<br>&nbsp;&nbsp;&nbsp; ServerName greatsite.cakelampvm.com<br>&nbsp;&nbsp;&nbsp; ServerAlias greatsite.cakelampvm.com *.greatsite.cakelampvm.com<br>&nbsp;&nbsp;&nbsp; DocumentRoot /var/www/greatsite<br>&nbsp;&nbsp;&nbsp; ErrorLog ${APACHE_LOG_DIR}/greatsite.cakelampvm.com-error.log<br>&nbsp;&nbsp;&nbsp; CustomLog ${APACHE_LOG_DIR}/greatsite.cakelampvm.com-access.log combined<br>&nbsp;&nbsp;&nbsp; Alias /statistics "/var/www/webwork.repository/webwork/maps_demo/webroot/statistics"<br>&nbsp;&nbsp;&nbsp; Include /etc/apache2/conf-library/basic-options.conf<br>&nbsp;&nbsp;&nbsp; Include /etc/apache2/conf-library/rewrite-enabling.conf<br>&lt;/VirtualHost&gt;</pre>
+    <p>The above example is pre-modified for DNS Option A above, the
+      greatsite.cakelampvm.com name.&nbsp; Switching all of those to
+      "greatsite.tv" instead would support DNS option B.</p>
+    <p>Copy that file into /etc/apache/available-sites under an appropriate
+      name, which here we will call "greatsite.conf".</p>
+    <p>Tell apache to use the new file:</p>
+    <pre>a2ensite greatsite.conf</pre>
+    <p>Finally, restart apache to get it to begin serving the site:</p>
+    <pre>sudo service apache2 restart</pre>
+    <h3>Test the new web site</h3>
+    <p>Given the configuration above, your host PC should now be able to access
+      the new website.</p>
+    <p>To test this, first try pinging the hostname, e.g.: ping
+      greatsite.cakelampvm.com or ping greatsite.tv</p>
+    <p>Then, if there are responses to the ping, it means the DNS is
+      working.&nbsp; If there are no responses, check the instructions in the
+      above DNS option section.</p>
+    <p>Once the DNS is working, one can try browsing to the site at:
+      http://greatsite.cakelampvm.com or http://greatsite.tv (depending on the
+      DNS option chosen).</p>
+    <p>If the site is not showing up properly, try examining the apache logs for
+      error messages that can be corrected.&nbsp; The log files are stored in
+      /var/log/apache2 and are generally named after the website.</p>
+    <h2>Configuring the guest VM</h2>
+    <p>The guest VM should already be set up appropriately.&nbsp; These steps
+      are provided for reference and updates.</p>
+    <h3>Set up virtualbox guest additions for the VM</h3>
+    <p>** note for v001 of cakelampvm: the below steps are still needed on the
+      shipped image.</p>
+    <p>This procedure is needed if the guest provides an older or incompatible
+      version of the guest additions (which have already been installed on the
+      guest vm).&nbsp; It may also be necessary when a new version of the guest
+      additions becomes available.</p>
+    <ol>
+      <li>To install the guest additions, open the guest VM and have its window
+        in focus.</li>
+      <li>Choose the "Devices" menu and select "Insert Guest Additions CD
+        Image".&nbsp; This will mount the CD's ISO image on the VM.</li>
+      <li>On the guest VM, it may be necessary to mount the CD image that's now
+        available:<br>
+        sudo mount /dev/sr0 /media/cdrom</li>
+      <li>Since the VM currently has no windowing system installed, one must
+        start the Guest Additions install manually:<br>
+        cd /media/cdrom<br>
+        sudo sh VBoxLinuxAdditions.run</li>
+      <li>This should install the guest additions.</li>
+    </ol>
+    <h3>Set up network adapters on guest VM</h3>
+    <p>The network interfaces should already be configured on the guest within
+      the virtualbox configuration.&nbsp; This is available by clicking on the
+      VM in the virtualbox manager and selecting "Settings".&nbsp; These are the
+      configuration settings used:</p>
+    Adapter 1:<br>
+    &nbsp; Attached to: Host-only Adapter<br>
+    &nbsp; Name: vboxnet0&nbsp; <br>
+    <p>Adapter 2:<br>
+      &nbsp; Attached to: Nat Network<br>
+      &nbsp; Name: NatNetwork</p>
+    <p>On the guest VM itself, the network settings are specified in a file
+      called /etc/network/interfaces.&nbsp; Here are the current contents of
+      that file:</p>
+    <pre>source /etc/network/interfaces.d/*<br><br>auto lo<br>iface lo inet loopback<br><br>auto enp0s3<br>iface enp0s3 inet static<br>&nbsp; address 10.28.42.20<br>&nbsp; netmask 255.255.255.0<br>&nbsp; network 10.28.42.0<br>&nbsp; broadcast 10.28.42.255<br>&nbsp; dns-domain cakelampvm.com<br>&nbsp; dns-search cakelampvm.com<br>&nbsp; dns-nameservers 127.0.0.1 8.8.8.8</pre>
+    <pre>auto enp0s8</pre>
+    <pre>iface enp0s8 inet dhcp</pre>
+    <p> </p>
+    <h2>Handy Techniques</h2>
+    <h3>Get the network address on the guest vm</h3>
+    <p>Run this command: ifconfig</p>
+    <p>In the results, look for "inet addr".&nbsp; There may be more than one,
+      if there are multiple network interfaces.</p>
+    <h3>How to cleanly reboot or shut down the guest VM</h3>
+    <p>When you've got the DNS and everything integrated, these commands will
+      manage the vm's state:</p>
+    <p>First, log into the guest VM: ssh developer@cakelampvm.com</p>
+    <p>Then, reboot the guest VM: sudo reboot</p>
+    <p>Or, halt the guest VM: sudo shutdown -h now</p>
+    <p>Using these commands is better than just cycling the power from the
+      virtualbox control panel.</p>
+    <h2>Notes on building the Cake Lamp VM</h2>
+    <p>This is all work that should already have been done.&nbsp; It is
+      mentioned here just as breadcrumbs for a future vm builder.</p>
+    <ul>
+      <li>Downloaded and installed virtualbox for host computer (where the vm
+        image will be built).</li>
+      <li>Downloaded ubuntu server 16.04 iso.
+        (https://www.ubuntu.com/download/server)</li>
+      <li>Created a new vm in virtualbox, telling it to start from the ubuntu
+        server iso.</li>
+      <li>Installed LAMP stack on guest VM.&nbsp; Some help here:
+        http://howtoubuntu.org/how-to-install-lamp-on-ubuntu</li>
+      <li>Configured CAKE on the guest VM.&nbsp; Useful link:
+        https://askubuntu.com/questions/628938/how-to-install-cakephp-in-ubuntu-14-04</li>
+      <li>Configured the two network adapters as needed (one for host-only
+        network and one for nat network).&nbsp; Here's some info about
+        virtualbox networking with two adapters similar to our setup:
+https://askubuntu.com/questions/293816/in-virtualbox-how-do-i-set-up-host-only-virtual-machines-that-can-access-the-in<br>
+      </li>
+      <li>Installed and configured Samba service for the guest VM.&nbsp; The
+        main config file lives in "/etc/samba/smb.conf".&nbsp; Some pointers
+        here:
+https://help.ubuntu.com/community/How%20to%20Create%20a%20Network%20Share%20Via%20Samba%20Via%20CLI%20%28Command-line%20interface/Linux%20Terminal%29%20-%20Uncomplicated%2C%20Simple%20and%20Brief%20Way%21</li>
+    </ul>
+    <p><br>
+    </p>
+  </body>
+</html>
diff --git a/production/sites/cakelampvm.com/docs/manual/images/host_only_adapter_dhcp_server.png b/production/sites/cakelampvm.com/docs/manual/images/host_only_adapter_dhcp_server.png
new file mode 100644 (file)
index 0000000..53db956
Binary files /dev/null and b/production/sites/cakelampvm.com/docs/manual/images/host_only_adapter_dhcp_server.png differ
diff --git a/production/sites/cakelampvm.com/docs/manual/images/host_only_network_adapter.png b/production/sites/cakelampvm.com/docs/manual/images/host_only_network_adapter.png
new file mode 100644 (file)
index 0000000..dd87e5f
Binary files /dev/null and b/production/sites/cakelampvm.com/docs/manual/images/host_only_network_adapter.png differ
diff --git a/production/sites/cakelampvm.com/docs/manual/images/nat_network_config.png b/production/sites/cakelampvm.com/docs/manual/images/nat_network_config.png
new file mode 100644 (file)
index 0000000..3299a3b
Binary files /dev/null and b/production/sites/cakelampvm.com/docs/manual/images/nat_network_config.png differ
diff --git a/production/sites/cakelampvm.com/goog_maps_helper_mod/GoogleMapHelper.php b/production/sites/cakelampvm.com/goog_maps_helper_mod/GoogleMapHelper.php
new file mode 100644 (file)
index 0000000..074707f
--- /dev/null
@@ -0,0 +1,1728 @@
+<?php
+namespace Geo\View\Helper;
+
+use Cake\Core\Configure;
+use Cake\Core\Exception\Exception;
+use Cake\Routing\Router;
+use Cake\Utility\Hash;
+use Cake\View\Helper;
+use Cake\View\View;
+use Geo\View\Helper\JsBaseEngineTrait;
+
+/**
+ * This is a CakePHP helper that helps users to integrate GoogleMap v3
+ * into their application by only writing PHP code. This helper depends on jQuery.
+ *
+ * Capable of resetting itself (full or partly) for multiple maps on a single view.
+ *
+ * CodeAPI: http://code.google.com/intl/de-DE/apis/maps/documentation/javascript/basics.html
+ * Icons/Images: http://gmapicons.googlepages.com/home
+ *
+ * @author Rajib Ahmed
+ * @author Mark Scherer
+ * @link http://www.dereuromark.de/2010/12/21/googlemapsv3-cakephp-helper/
+ * @license MIT License (http://www.opensource.org/licenses/mit-license.php)
+ * @property \Cake\View\Helper\HtmlHelper $Html
+ */
+class GoogleMapHelper extends Helper {
+
+       use JsBaseEngineTrait;
+
+       const API = 'maps.google.com/maps/api/js';
+
+       const STATIC_API = 'maps.google.com/maps/api/staticmap';
+
+       /**
+        * @var int
+        */
+       public static $mapCount = 0;
+
+       /**
+        * @var int
+        */
+       public static $markerCount = 0;
+
+       /**
+        * @var int
+        */
+       public static $iconCount = 0;
+
+       /**
+        * @var int
+        */
+       public static $infoWindowCount = 0;
+
+       /**
+        * @var int
+        */
+       public static $infoContentCount = 0;
+
+       const TYPE_ROADMAP = 'R';
+
+       const TYPE_HYBRID = 'H';
+
+       const TYPE_SATELLITE = 'S';
+
+       const TYPE_TERRAIN = 'T';
+
+       /**
+        * @var array
+        */
+       public $types = [
+               self::TYPE_ROADMAP => 'ROADMAP',
+               self::TYPE_HYBRID => 'HYBRID',
+               self::TYPE_SATELLITE => 'SATELLITE',
+               self::TYPE_TERRAIN => 'TERRAIN'
+       ];
+
+       const TRAVEL_MODE_DRIVING = 'D';
+
+       const TRAVEL_MODE_BICYCLING = 'B';
+
+       const TRAVEL_MODE_TRANSIT = 'T';
+
+       const TRAVEL_MODE_WALKING = 'W';
+
+       /**
+        * @var array
+        */
+       public $travelModes = [
+               self::TRAVEL_MODE_DRIVING => 'DRIVING',
+               self::TRAVEL_MODE_BICYCLING => 'BICYCLING',
+               self::TRAVEL_MODE_TRANSIT => 'TRANSIT',
+               self::TRAVEL_MODE_WALKING => 'WALKING'
+       ];
+
+       /**
+        * Needed helpers
+        *
+        * @var array
+        */
+       public $helpers = ['Html'];
+
+       /**
+        * Google maker config instance variable
+        *
+        * @var array
+        */
+       public $markers = [];
+
+       /**
+        * @var array
+        */
+       public $infoWindows = [];
+
+       /**
+        * @var array
+        */
+       public $infoContents = [];
+
+       /**
+        * @var array
+        */
+       public $icons = [];
+
+       /**
+        * @var array
+        */
+       public $matching = [];
+
+       /**
+        * @var string
+        */
+       public $map = '';
+
+       /**
+        * @var array
+        */
+       protected $_mapIds = []; // Remember already used ones (valid xhtml contains ids not more than once)
+
+       /**
+        * Default settings
+        *
+        * @var array
+        */
+       protected $_defaultConfig = [
+               'zoom' => null, // global, both map and staticMap
+               'lat' => null, // global, both map and staticMap
+               'lng' => null, // global, both map and staticMap
+               'api' => '3',
+               'type' => self::TYPE_ROADMAP,
+               'map' => [
+                       'api' => null,
+                       'zoom' => null,
+                       'lat' => null,
+                       'lng' => null,
+                       'type' => null,
+                       'streetViewControl' => false,
+                       'navigationControl' => true,
+                       'mapTypeControl' => true,
+                       'scaleControl' => true,
+                       'scrollwheel' => false,
+                       'keyboardShortcuts' => true,
+                       'typeOptions' => [],
+                       'navOptions' => [],
+                       'scaleOptions' => [],
+                       'defaultLat' => 51, // only last fallback, use Configure::write('Google.lat', ...); to define own one
+                       'defaultLng' => 11, // only last fallback, use Configure::write('Google.lng', ...); to define own one
+                       'defaultZoom' => 5,
+               ],
+               'staticMap' => [
+                       'size' => '300x300',
+                       'format' => 'png',
+                       'mobile' => false,
+                       //'shadow' => true // for icons
+               ],
+               'geolocate' => false,
+               'language' => null,
+               'region' => null,
+               'showMarker' => true,
+               //'showInfoWindow' => true,
+               'infoWindow' => [
+                       'content' => '',
+                       'useMultiple' => false, // Using single infowindow object for all
+                       'maxWidth' => 300,
+                       'lat' => null,
+                       'lng' => null,
+                       'pixelOffset' => 0,
+                       'zIndex' => 200,
+                       'disableAutoPan' => false
+               ],
+               'marker' => [
+                       //'autoCenter' => true,
+                       'animation' => null, // BOUNCE or DROP  https://developers.google.com/maps/documentation/javascript/3.exp/reference#Animation
+                       'icon' => null, // => default (red marker) //http://google-maps-icons.googlecode.com/files/home.png
+                       'title' => null,
+                       'shadow' => null,
+                       'shape' => null,
+                       'zIndex' => null,
+                       'draggable' => false,
+                       'cursor' => null,
+                       'directions' => false, // add form with directions
+                       'open' => false, // New in 1.5
+               ],
+               'div' => [
+                       'id' => 'map_canvas',
+                       'width' => '100%',
+                       'height' => '400px',
+                       'class' => 'map',
+                       'escape' => true
+               ],
+               'event' => [
+               ],
+               'animation' => [
+                       //TODO
+               ],
+               'polyline' => [
+                       'color' => '#FF0000',
+                       'opacity' => 1.0,
+                       'weight' => 2,
+               ],
+               'directions' => [
+                       'travelMode' => self::TRAVEL_MODE_DRIVING,
+                       'unitSystem' => 'METRIC',
+                       'directionsDiv' => null,
+               ],
+               'callbacks' => [
+                       'geolocate' => null //TODO
+               ],
+               'plugins' => [
+                       'keydragzoom' => false, // http://google-maps-utility-library-v3.googlecode.com/svn/tags/keydragzoom/
+                       'markermanager' => false, // http://google-maps-utility-library-v3.googlecode.com/svn/tags/markermanager/
+                       'markercluster' => false, // http://google-maps-utility-library-v3.googlecode.com/svn/tags/markerclusterer/
+               ],
+               'autoCenter' => false, // try to fit all markers in (careful, all zooms values are omitted)
+               'autoScript' => false, // let the helper include the necessary js script links
+               'block' => true, // for scripts
+               'localImages' => false,
+               'https' => null, // auto detect
+               'key' => null,
+       ];
+
+       /**
+        * @var array
+        */
+       protected $_runtimeConfig = [];
+
+       /**
+        * @var bool
+        */
+       protected $_apiIncluded = false;
+
+       /**
+        * @var bool
+        */
+       protected $_gearsIncluded = false;
+
+       /**
+        * @var bool
+        */
+       protected $_located = false;
+
+       /**
+        * @param \Cake\View\View|null $View
+        * @param array $config
+        */
+       public function __construct(View $View, array $config = []) {
+               parent::__construct($View, $config);
+       }
+
+       /**
+        * @param array $config
+        * @return void
+        */
+       public function initialize(array $config) {
+               parent::initialize($config);
+
+               $defaultConfig = Hash::merge($this->_defaultConfig, (array)Configure::read('GoogleMap'));
+               $config = Hash::merge($defaultConfig, $config);
+
+               if (isset($config['api']) && !isset($config['map']['api'])) {
+                       $config['map']['api'] = $config['api'];
+               }
+               if (isset($config['zoom']) && !isset($config['map']['zoom'])) {
+                       $config['map']['zoom'] = $config['zoom'];
+               }
+               if (isset($config['lat']) && !isset($config['map']['lat'])) {
+                       $config['map']['lat'] = $config['lat'];
+               }
+               if (isset($config['lng']) && !isset($config['map']['lng'])) {
+                       $config['map']['lng'] = $config['lng'];
+               }
+               if (isset($config['type']) && !isset($config['map']['type'])) {
+                       $config['map']['type'] = $config['type'];
+               }
+               if (isset($config['size'])) {
+                       $config['div']['width'] = $config['size']['width'];
+                       $config['div']['height'] = $config['size']['height'];
+               }
+               if (isset($config['staticSize'])) {
+                       $config['staticMap']['size'] = $config['staticSize'];
+               }
+               // the following are convenience defaults - if not available the map lat/lng/zoom defaults will be used
+               if (isset($config['staticZoom'])) {
+                       $config['staticMap']['zoom'] = $config['staticZoom'];
+               }
+               if (isset($config['staticLat'])) {
+                       $config['staticMap']['lat'] = $config['staticLat'];
+               }
+               if (isset($config['staticLng'])) {
+                       $config['staticMap']['lng'] = $config['staticLng'];
+               }
+               if (isset($config['localImages'])) {
+                       if ($config['localImages'] === true) {
+                               $config['localImages'] = Router::url('/img/google_map/', true);
+                       }
+               }
+
+               // BC
+               if (!empty($config['inline'])) {
+                       trigger_error('Deprecated inline option, use block instead.', E_USER_DEPRECATED);
+                       $config['block'] = null;
+               }
+
+               $this->_config = $config;
+               $this->_runtimeConfig = $this->_config;
+       }
+
+       /**
+        * JS maps.google API url.
+        *
+        * Options read via configs
+        * - key
+        * - api
+        * - language (iso2: en, de, ja, ...)
+        *
+        * You can adds more after the URL like "&key=value&..." via
+        * - query string array: additional query strings (e.g. callback for deferred execution - not supported yet by this helper)
+        *
+        * @param array $query
+        * @return string Full URL
+        */
+       public function apiUrl(array $query = []) {
+               $url = $this->_protocol() . static::API;
+
+               if ($this->_runtimeConfig['map']['api']) {
+                        $query['v'] = $this->_runtimeConfig['map']['api'];
+               }
+               if ($this->_runtimeConfig['key']) {
+                       $query['key'] = $this->_runtimeConfig['key'];
+               }
+
+               if ($this->_runtimeConfig['language']) {
+                       $query['language'] = $this->_runtimeConfig['language'];
+               }
+
+               if ($query) {
+                       $query = http_build_query($query);
+
+                       $url .= '?' . $query;
+               }
+
+               return $url;
+       }
+
+       /**
+        * @deprecated
+        * @return string
+        */
+       public function gearsUrl() {
+               $this->_gearsIncluded = true;
+               $url = $this->_protocol() . 'code.google.com/apis/gears/gears_init.js';
+               return $url;
+       }
+
+       /**
+        * @return string currentMapObject
+        */
+       public function name() {
+               return 'map' . static::$mapCount;
+       }
+
+       /**
+        * @return string currentContainerId
+        */
+       public function id() {
+               return $this->_runtimeConfig['div']['id'];
+       }
+
+       /**
+        * Make it possible to include multiple maps per page
+        * resets markers, infoWindows etc
+        *
+        * @param bool $full true=optionsAsWell
+        * @return void
+        */
+       public function reset($full = true) {
+               static::$markerCount = static::$infoWindowCount = 0;
+               $this->markers = $this->infoWindows = [];
+               if ($full) {
+                       $this->_runtimeConfig = $this->_config;
+               }
+       }
+
+       /**
+        * Set the controls of current map
+        *
+        * Control options
+        * - zoom, scale, overview: TRUE/FALSE
+        *
+        * - map: FALSE, small, large
+        * - type: FALSE, normal, menu, hierarchical
+        * TIP: faster/shorter by using only the first character (e.g. "H" for "hierarchical")
+        *
+        * @param array $options
+        * @return void
+        */
+       public function setControls(array $options = []) {
+               if (isset($options['streetView'])) {
+                       $this->_runtimeConfig['map']['streetViewControl'] = $options['streetView'];
+               }
+               if (isset($options['zoom'])) {
+                       $this->_runtimeConfig['map']['scaleControl'] = $options['zoom'];
+               }
+               if (isset($options['scrollwheel'])) {
+                       $this->_runtimeConfig['map']['scrollwheel'] = $options['scrollwheel'];
+               }
+               if (isset($options['keyboardShortcuts'])) {
+                       $this->_runtimeConfig['map']['keyboardShortcuts'] = $options['keyboardShortcuts'];
+               }
+               if (isset($options['type'])) {
+                       $this->_runtimeConfig['map']['type'] = $options['type'];
+               }
+       }
+
+       /**
+        * This the initialization point of the script
+        * Returns the div container you can echo on the website
+        *
+        * @param array $options associative array of settings are passed
+        * @return string divContainer
+        */
+       public function map(array $options = []) {
+               $this->reset();
+               $this->_runtimeConfig = Hash::merge($this->_runtimeConfig, $options);
+               $this->_runtimeConfig['map'] = $options + $this->_runtimeConfig['map'];
+
+               if (!isset($this->_runtimeConfig['map']['lat']) || !isset($this->_runtimeConfig['map']['lng'])) {
+                       $this->_runtimeConfig['map']['lat'] = $this->_runtimeConfig['map']['defaultLat'];
+                       $this->_runtimeConfig['map']['lng'] = $this->_runtimeConfig['map']['defaultLng'];
+               }
+               if (!isset($this->_runtimeConfig['map']['zoom'])) {
+                       $this->_runtimeConfig['map']['zoom'] = $this->_runtimeConfig['map']['defaultZoom'];
+               }
+
+               $result = '';
+
+               // autoinclude js?
+               if ($this->_runtimeConfig['autoScript'] && !$this->_apiIncluded) {
+                       $res = $this->Html->script($this->apiUrl(), ['block' => $this->_runtimeConfig['block']]);
+                       $this->_apiIncluded = true;
+
+                       if (!$this->_runtimeConfig['block']) {
+                               $result .= $res . PHP_EOL;
+                       }
+                       // usually already included
+                       //http://ajax.googleapis.com/ajax/libs/jquery/1/jquery.min.js
+               }
+               // still not very common: http://code.google.com/intl/de-DE/apis/maps/documentation/javascript/basics.html
+               if (false && !empty($this->_runtimeConfig['autoScript']) && !$this->_gearsIncluded) {
+                       $res = $this->Html->script($this->gearsUrl(), ['block' => $this->_runtimeConfig['block']]);
+                       if (!$this->_runtimeConfig['block']) {
+                               $result .= $res . PHP_EOL;
+                       }
+               }
+
+               $map = "
+                       var initialLocation = " . $this->_initialLocation() . ";
+                       var browserSupportFlag = new Boolean();
+                       var myOptions = " . $this->_mapOptions() . ";
+
+                       // deprecated
+                       gMarkers" . static::$mapCount . " = new Array();
+                       gInfoWindows" . static::$mapCount . " = new Array();
+                       gWindowContents" . static::$mapCount . " = new Array();
+               ";
+
+               #rename "map_canvas" to "map_canvas1", ... if multiple maps on one page
+               while (in_array($this->_runtimeConfig['div']['id'], $this->_mapIds)) {
+                       $this->_runtimeConfig['div']['id'] .= '-1'; //TODO: improve
+               }
+               $this->_mapIds[] = $this->_runtimeConfig['div']['id'];
+
+               $map .= "
+                       var " . $this->name() . ' = new google.maps.Map(document.getElementById("' . $this->_runtimeConfig['div']['id'] . "\"), myOptions);
+                       ";
+               $this->map = $map;
+
+               $this->_runtimeConfig['div']['style'] = '';
+               if (is_numeric($this->_runtimeConfig['div']['width'])) {
+                       $this->_runtimeConfig['div']['width'] .= 'px';
+               }
+               if (is_numeric($this->_runtimeConfig['div']['height'])) {
+                       $this->_runtimeConfig['div']['height'] .= 'px';
+               }
+
+               $this->_runtimeConfig['div']['style'] .= 'width: ' . $this->_runtimeConfig['div']['width'] . ';';
+               $this->_runtimeConfig['div']['style'] .= 'height: ' . $this->_runtimeConfig['div']['height'] . ';';
+               unset($this->_runtimeConfig['div']['width']);
+               unset($this->_runtimeConfig['div']['height']);
+
+               $defaultText = isset($this->_runtimeConfig['content']) ? $this->_runtimeConfig['content'] : __('Map cannot be displayed!');
+               $result .= $this->Html->tag('div', $defaultText, $this->_runtimeConfig['div']);
+
+               return $result;
+       }
+
+       /**
+        * Generate a new LatLng object with the current lat and lng.
+        *
+        * @return string
+        */
+       protected function _initialLocation() {
+               if ($this->_runtimeConfig['map']['lat'] && $this->_runtimeConfig['map']['lng']) {
+                       return 'new google.maps.LatLng(' . $this->_runtimeConfig['map']['lat'] . ', ' . $this->_runtimeConfig['map']['lng'] . ')';
+               }
+               $this->_runtimeConfig['autoCenter'] = true;
+               return 'false';
+       }
+
+       /**
+        * Add a marker to the map.
+        *
+        * Options:
+        * - lat and lng or address (to geocode on demand, not recommended, though)
+        * - title, content, icon, directions, maxWidth, open (optional)
+        *
+        * Note, that you can only set one marker to "open" for single window mode.
+        * If you declare multiple ones, the last one will be the one shown as open.
+        *
+        * @param array $options
+        * @return mixed Integer marker count or boolean false on failure
+        * @throws \Cake\Core\Exception\Exception
+        */
+       public function addMarker($options) {
+               $defaults = $this->_runtimeConfig['marker'];
+               if (isset($options['icon']) && is_array($options['icon'])) {
+                       $defaults = $options['icon'] + $defaults;
+                       unset($options['icon']);
+               }
+               $options += $defaults;
+
+               $params = [];
+               $params['map'] = $this->name();
+
+               if (isset($options['title'])) {
+                       $params['title'] = json_encode($options['title']);
+               }
+               if (isset($options['icon'])) {
+                       $params['icon'] = $options['icon'];
+                       if (is_int($params['icon'])) {
+                               $params['icon'] = 'gIcons' . static::$mapCount . '[' . $params['icon'] . ']';
+                       } else {
+                               $params['icon'] = json_encode($params['icon']);
+                       }
+               }
+               if (isset($options['shadow'])) {
+                       $params['shadow'] = $options['shadow'];
+                       if (is_int($params['shadow'])) {
+                               $params['shadow'] = 'gIcons' . static::$mapCount . '[' . $params['shadow'] . ']';
+                       } else {
+                               $params['shadow'] = json_encode($params['shadow']);
+                       }
+               }
+               if (isset($options['shape'])) {
+                       $params['shape'] = $options['shape'];
+               }
+               if (isset($options['zIndex'])) {
+                       $params['zIndex'] = $options['zIndex'];
+               }
+               if (isset($options['animation'])) {
+                       $params['animation'] = 'google.maps.Animation.' . strtoupper($options['animation']);
+               }
+
+               // geocode if necessary
+               if (!isset($options['lat']) || !isset($options['lng'])) {
+                       $this->map .= "
+var geocoder = new google.maps.Geocoder();
+
+function geocodeAddress(address) {
+       geocoder.geocode({'address': address}, function(results, status) {
+               if (status == google.maps.GeocoderStatus.OK) {
+
+                       x" . static::$markerCount . " = new google.maps.Marker({
+                               position: results[0].geometry.location,
+                               " . $this->_toObjectParams($params, false, false) . "
+                       });
+                       gMarkers" . static::$mapCount . " .push(
+                               x" . static::$markerCount . "
+                       );
+                       return results[0].geometry.location;
+               } else {
+                       //alert('Geocoding was not successful for the following reason: ' + status);
+                       return null;
+               }
+       });
+}";
+                       if (!isset($options['address'])) {
+                               throw new Exception('Either use lat/lng or address to add a marker');
+                       }
+                       $position = 'geocodeAddress("' . h($options['address']) . '")';
+               } else {
+                       $position = 'new google.maps.LatLng(' . $options['lat'] . ',' . $options['lng'] . ')';
+               }
+
+               $marker = "
+                       var x" . static::$markerCount . " = new google.maps.Marker({
+                               position: " . $position . ",
+                               " . $this->_toObjectParams($params, false, false) . "
+                       });
+                       gMarkers" . static::$mapCount . " .push(
+                               x" . static::$markerCount . "
+                       );
+               ";
+               $this->map .= $marker;
+
+               if (!empty($options['directions'])) {
+                       $options['content'] .= $this->_directions($options['directions'], $options);
+               }
+
+               // Fill popup windows
+               if (!empty($options['content']) && $this->_runtimeConfig['infoWindow']['useMultiple']) {
+                       $x = $this->addInfoWindow(['content' => $options['content']]);
+                       $this->addEvent(static::$markerCount, $x, $options['open']);
+
+               } elseif (!empty($options['content'])) {
+                       if (!isset($this->_runtimeConfig['marker']['infoWindow'])) {
+                               $this->_runtimeConfig['marker']['infoWindow'] = $this->addInfoWindow();
+                       }
+
+                       $x = $this->addInfoContent($options['content']);
+                       $event = "
+                       gInfoWindows" . static::$mapCount . '[' . $this->_runtimeConfig['marker']['infoWindow'] . ']. setContent(gWindowContents' . static::$mapCount . '[' . $x . "]);
+                       gInfoWindows" . static::$mapCount . '[' . $this->_runtimeConfig['marker']['infoWindow'] . '].open(' . $this->name() . ', gMarkers' . static::$mapCount . '[' . $x . "]);
+                       ";
+                       $this->addCustomEvent(static::$markerCount, $event);
+
+                       if (!empty($options['open'])) {
+                               $this->addCustom($event);
+                       }
+               }
+
+               // Custom matching event?
+               if (isset($options['id'])) {
+                       $this->matching[$options['id']] = static::$markerCount;
+               }
+
+               return static::$markerCount++;
+       }
+
+       /**
+        * Build directions form (type get) for directions inside infoWindows
+        *
+        * Options for directions (if array)
+        * - label
+        * - submit
+        * - escape: defaults to true
+        *
+        * @param mixed $directions
+        * - bool TRUE for autoDirections (using lat/lng)
+        * @param array $markerOptions
+        * - options array of marker for autoDirections etc (optional)
+        * @return string HTML
+        */
+       protected function _directions($directions, array $markerOptions = []) {
+               $options = [
+                       'from' => null,
+                       'to' => null,
+                       'label' => __('Enter your address'),
+                       'submit' => __('Get directions'),
+                       'escape' => true,
+                       'zoom' => null, // auto
+               ];
+               if ($directions === true) {
+                       $options['to'] = $markerOptions['lat'] . ',' . $markerOptions['lng'];
+               } elseif (is_array($directions)) {
+                       $options = $directions + $options;
+               }
+               if (empty($options['to']) && empty($options['from'])) {
+                       return '';
+               }
+               $form = '<form action="http://maps.google.com/maps" method="get" target="_blank">';
+               $form .= $options['escape'] ? h($options['label']) : $options['label'];
+               if (!empty($options['from'])) {
+                       $form .= '<input type="hidden" name="saddr" value="' . $options['from'] . '" />';
+               } else {
+                       $form .= '<input type="text" name="saddr" />';
+               }
+               if (!empty($options['to'])) {
+                       $form .= '<input type="hidden" name="daddr" value="' . $options['to'] . '" />';
+               } else {
+                       $form .= '<input type="text" name="daddr" />';
+               }
+               if (isset($options['zoom'])) {
+                       $form .= '<input type="hidden" name="z" value="' . $options['zoom'] . '" />';
+               }
+               $form .= '<input type="submit" value="' . $options['submit'] . '" />';
+               $form .= '</form>';
+
+               return '<div class="directions">' . $form . '</div>';
+       }
+
+       /**
+        * @param string $content
+        * @return int Current marker counter
+        */
+       public function addInfoContent($content) {
+               $this->infoContents[static::$markerCount] = $this->escapeString($content);
+               $event = "
+                       gWindowContents" . static::$mapCount . '.push(' . $this->escapeString($content) . ");
+                       ";
+               $this->addCustom($event);
+
+               //TODO: own count?
+               return static::$markerCount;
+       }
+
+       /**
+        * @var array
+        */
+       public $setIcons = [
+               'color' => 'http://www.google.com/mapfiles/marker%s.png',
+               'alpha' => 'http://www.google.com/mapfiles/marker%s%s.png',
+               'numeric' => 'http://google-maps-icons.googlecode.com/files/%s%s.png',
+               'special' => 'http://google-maps-icons.googlecode.com/files/%s.png'
+       ];
+
+       /**
+        * Get a custom icon set
+        *
+        * @param string $color Color: green, red, purple, ... or some special ones like "home", ...
+        * @param string|null $char Char: A...Z or 0...20/100 (defaults to none)
+        * @param string $size Size: s, m, l (defaults to medium)
+        * NOTE: for special ones only first parameter counts!
+        * @return array Array(icon, shadow, shape, ...)
+        */
+       public function iconSet($color, $char = null, $size = 'm') {
+               $colors = ['red', 'green', 'yellow', 'blue', 'purple', 'white', 'black'];
+               if (!in_array($color, $colors)) {
+                       $color = 'red';
+               }
+
+               if (!empty($this->_runtimeConfig['localImages'])) {
+                       $this->setIcons['color'] = $this->_runtimeConfig['localImages'] . 'marker%s.png';
+                       $this->setIcons['alpha'] = $this->_runtimeConfig['localImages'] . 'marker%s%s.png';
+                       $this->setIcons['numeric'] = $this->_runtimeConfig['localImages'] . '%s%s.png';
+                       $this->setIcons['special'] = $this->_runtimeConfig['localImages'] . '%s.png';
+               }
+
+               if (!empty($char)) {
+                       if ($color === 'red') {
+                               $color = '';
+                       } else {
+                               $color = '_' . $color;
+                       }
+                       $url = sprintf($this->setIcons['alpha'], $color, $char);
+               } else {
+                       if ($color === 'red') {
+                               $color = '';
+                       } else {
+                               $color = '_' . $color;
+                       }
+                       $url = sprintf($this->setIcons['color'], $color);
+               }
+
+               /*
+        var iconImage = new google.maps.MarkerImage('images/' + images[0] + ' .png',
+            new google.maps.Size(iconData[images[0]].width, iconData[images[0]].height),
+            new google.maps.Point(0,0),
+            new google.maps.Point(0, 32)
+        );
+
+        var iconShadow = new google.maps.MarkerImage('images/' + images[1] + ' .png',
+            new google.maps.Size(iconData[images[1]].width, iconData[images[1]].height),
+            new google.maps.Point(0,0),
+            new google.maps.Point(0, 32)
+        );
+
+        var iconShape = {
+            coord: [1, 1, 1, 32, 32, 32, 32, 1],
+            type: 'poly'
+        };
+        */
+
+               $shadow = 'http://www.google.com/mapfiles/shadow50.png';
+               $res = [
+                       'url' => $url,
+                       'icon' => $this->icon($url, ['size' => ['width' => 20, 'height' => 34]]),
+                       'shadow' => $this->icon($shadow, ['size' => ['width' => 37, 'height' => 34], 'shadow' => ['width' => 10, 'height' => 34]])
+               ];
+               return $res;
+       }
+
+       /**
+        * Generate icon array.
+        *
+        * custom icon: http://thydzik.com/thydzikGoogleMap/markerlink.php?text=?&color=FFFFFF
+        * custom icons: http://code.google.com/p/google-maps-icons/wiki/NumericIcons#Lettered_Balloons_from_A_to_Z,_in_10_Colors
+        * custom shadows: http://www.cycloloco.com/shadowmaker/shadowmaker.htm
+        *
+        * @param string $image Image Url (http://...)
+        * @param string|null $shadow ShadowImage Url (http://...)
+        * @param array $imageOptions Image options
+        * @param array $shadowOptions Shadow image options
+        * @return array Resulting array
+        */
+       public function addIcon($image, $shadow = null, array $imageOptions = [], array $shadowOptions = []) {
+               $res = ['url' => $image];
+               $res['icon'] = $this->icon($image, $imageOptions);
+               if ($shadow) {
+                       $last = $this->_iconRemember[$res['icon']];
+                       if (!isset($shadowOptions['anchor'])) {
+                               $shadowOptions['anchor'] = [];
+                       }
+                       $shadowOptions['anchor'] = $last['options']['anchor'] + $shadowOptions['anchor'];
+
+                       $res['shadow'] = $this->icon($shadow, $shadowOptions);
+               }
+               return $res;
+       }
+
+       /**
+        * @var array
+        */
+       protected $_iconRemember = [];
+
+       /**
+        * Generate icon object
+        *
+        * @param string $url (required)
+        * @param array $options (optional):
+        * - size: array(width=>x, height=>y)
+        * - origin: array(width=>x, height=>y)
+        * - anchor: array(width=>x, height=>y)
+        * @return int Icon count
+        */
+       public function icon($url, array $options = []) {
+               // The shadow image is larger in the horizontal dimension
+               // while the position and offset are the same as for the main image.
+               if (empty($options['size'])) {
+                       if (substr($url, 0, 1) === '/') {
+                               // patch local paths to use the document root.  otherwise getimagesize fails filesystem lookup.
+                               // paths with http or other protocol in front will be handled more simply in 'else' below.
+                               $canonicalPath = realpath(WWW_ROOT . $url);
+                               if (! $canonicalPath) {
+                                       // failed to resolve the path, so just fall back to the url provided.
+                                       $canonicalPath = "$url";
+                               }
+                               $data = getimagesize($canonicalPath);
+                       } else {
+                               $data = getimagesize($url);
+                       }
+                       if ($data) {
+                               $options['size']['width'] = $data[0];
+                               $options['size']['height'] = $data[1];
+                       } else {
+                               $options['size']['width'] = $options['size']['height'] = 0;
+                       }
+               }
+               if (empty($options['anchor'])) {
+                       $options['anchor']['width'] = (int)($options['size']['width'] / 2);
+                       $options['anchor']['height'] = $options['size']['height'];
+               }
+               if (empty($options['origin'])) {
+                       $options['origin']['width'] = $options['origin']['height'] = 0;
+               }
+               if (isset($options['shadow'])) {
+                       $options['anchor'] = $options['shadow'];
+               }
+
+               $icon = 'new google.maps.MarkerImage("' . $url . '",
+       new google.maps.Size(' . $options['size']['width'] . ', ' . $options['size']['height'] . '),
+       new google.maps.Point(' . $options['origin']['width'] . ', ' . $options['origin']['height'] . '),
+       new google.maps.Point(' . $options['anchor']['width'] . ', ' . $options['anchor']['height'] . ')
+)';
+               $this->icons[static::$iconCount] = $icon;
+               $this->_iconRemember[static::$iconCount] = ['url' => $url, 'options' => $options, 'id' => static::$iconCount];
+               return static::$iconCount++;
+       }
+
+       /**
+        * Creates a new InfoWindow.
+        *
+        * @param array $options
+        * - lat, lng, content, maxWidth, pixelOffset, zIndex
+        * @return int windowCount
+        */
+       public function addInfoWindow(array $options = []) {
+               $defaults = $this->_runtimeConfig['infoWindow'];
+               $options += $defaults;
+
+               if (!empty($options['lat']) && !empty($options['lng'])) {
+                       $position = 'new google.maps.LatLng(' . $options['lat'] . ', ' . $options['lng'] . ')';
+               } else {
+                       $position = ' ' . $this->name() . ' .getCenter()';
+               }
+
+               $windows = "
+                       gInfoWindows" . static::$mapCount . ".push(new google.maps.InfoWindow({
+                                       position: {$position},
+                                       content: " . $this->escapeString($options['content']) . ",
+                                       maxWidth: {$options['maxWidth']},
+                                       pixelOffset: {$options['pixelOffset']}
+                                       /*zIndex: {$options['zIndex']},*/
+                       }));
+                       ";
+               $this->map .= $windows;
+               return static::$infoWindowCount++;
+       }
+
+       /**
+        * Add event to open marker on click.
+        *
+        * @param int $marker
+        * @param int $infoWindow
+        * @param bool $open Also open it right away.
+        * @return void
+        */
+       public function addEvent($marker, $infoWindow, $open = false) {
+               $this->map .= "
+                       google.maps.event.addListener(gMarkers" . static::$mapCount . "[{$marker}], 'click', function() {
+                               gInfoWindows" . static::$mapCount . "[$infoWindow].open(" . $this->name() . ", this);
+                       });
+               ";
+               if ($open) {
+                       $event = 'gInfoWindows' . static::$mapCount . "[$infoWindow].open(" . $this->name() .
+                               ', gMarkers' . static::$mapCount . '[' . $marker . ']);';
+                       $this->addCustom($event);
+               }
+       }
+
+       /**
+        * Add a custom event for a marker on click.
+        *
+        * @param int $marker
+        * @param string $event (js)
+        * @return void
+        */
+       public function addCustomEvent($marker, $event) {
+               $this->map .= "
+                       google.maps.event.addListener(gMarkers" . static::$mapCount . "[{$marker}], 'click', function() {
+                               $event
+                       });
+               ";
+       }
+
+       /**
+        * Add custom JS.
+        *
+        * @param string $js Custom JS
+        * @return void
+        */
+       public function addCustom($js) {
+               $this->map .= $js;
+       }
+
+       /**
+        * Add directions to the map.
+        *
+        * @param array|string $from Location as array(fixed lat/lng pair) or string (to be geocoded at runtime)
+        * @param array|string $to Location as array(fixed lat/lng pair) or string (to be geocoded at runtime)
+        * @param array $options
+        * - directionsDiv: Div to place directions in text form
+        * - travelMode: TravelMode,
+        * - transitOptions: TransitOptions,
+        * - unitSystem: UnitSystem (IMPERIAL, METRIC, AUTO),
+        * - waypoints[]: DirectionsWaypoint,
+        * - optimizeWaypoints: Boolean,
+        * - provideRouteAlternatives: Boolean,
+        * - avoidHighways: Boolean,
+        * - avoidTolls: Boolean
+        * - region: String
+        * @see https://developers.google.com/maps/documentation/javascript/3.exp/reference#DirectionsRequest
+        * @return void
+        */
+       public function addDirections($from, $to, array $options = []) {
+               $id = 'd' . static::$markerCount++;
+               $defaults = $this->_runtimeConfig['directions'];
+               $options += $defaults;
+               $travelMode = $this->travelModes[$options['travelMode']];
+
+               $directions = "
+                       var {$id}Service = new google.maps.DirectionsService();
+                       var {$id}Display;
+                       {$id}Display = new google.maps.DirectionsRenderer();
+                       {$id}Display. setMap(" . $this->name() . ");
+                       ";
+
+               if (!empty($options['directionsDiv'])) {
+                       $directions .= "{$id}Display. setPanel(document.getElementById('" . $options['directionsDiv'] . "'));";
+               }
+
+               if (is_array($from)) {
+                       $from = 'new google.maps.LatLng(' . (float)$from['lat'] . ', ' . (float)$from['lng'] . ')';
+               } else {
+                       $from = '"' . h($from) . '"';
+               }
+               if (is_array($to)) {
+                       $to = 'new google.maps.LatLng(' . (float)$to['lat'] . ', ' . (float)$to['lng'] . ')';
+               } else {
+                       $to = '"' . h($to) . '"';
+               }
+
+               $directions .= "
+                       var request = {
+                               origin: $from,
+                               destination: $to,
+                               unitSystem: google.maps.UnitSystem." . $options['unitSystem'] . ",
+                               travelMode: google.maps.TravelMode. $travelMode
+                       };
+                       {$id}Service.route(request, function(result, status) {
+                               if (status == google.maps.DirectionsStatus.OK) {
+                                       {$id}Display. setDirections(result);
+                               }
+                       });
+               ";
+               $this->map .= $directions;
+       }
+
+       /**
+        * Add a polyline
+        *
+        * This method adds a line between 2 points
+        *
+        * @param array|string $from Location as array(fixed lat/lng pair) or string (to be geocoded at runtime)
+        * @param array|string $to Location as array(fixed lat/lng pair) or string (to be geocoded at runtime)
+        * @param array $options
+        * - color (#FFFFFF ... #000000)
+        * - opacity (0.1 ... 1, defaults to 1)
+        * - weight in pixels (defaults to 2)
+        * @see https://developers.google.com/maps/documentation/javascript/3.exp/reference#Polyline
+        * @return void
+        */
+       public function addPolyline($from, $to, array $options = []) {
+               if (is_array($from)) {
+                       $from = 'new google.maps.LatLng(' . (float)$from['lat'] . ', ' . (float)$from['lng'] . ')';
+               } else {
+                       throw new Exception('not implemented yet, use array of lat/lng');
+                       //$from = '\'' . h($from) . '\'';
+               }
+               if (is_array($to)) {
+                       $to = 'new google.maps.LatLng(' . (float)$to['lat'] . ', ' . (float)$to['lng'] . ')';
+               } else {
+                       throw new Exception('not implemented yet, use array of lat/lng');
+                       //$to = '\'' . h($to) . '\'';
+               }
+
+               $defaults = $this->_runtimeConfig['polyline'];
+               $options += $defaults;
+
+               $id = 'p' . static::$markerCount++;
+
+               $polyline = "var start = $from;";
+               $polyline .= "var end = $to;";
+               $polyline .= "
+                               var poly = [
+                                       start,
+                                       end
+                               ];
+                               var {$id}Polyline = new google.maps.Polyline({
+                                       path: poly,
+                                       strokeColor: '" . $options['color'] . "',
+                                       strokeOpacity: " . $options['opacity'] . ",
+                                       strokeWeight: " . $options['weight'] . "
+                               });
+                               {$id}Polyline.setMap(" . $this->name() . ");
+                       ";
+               $this->map .= $polyline;
+       }
+
+       /**
+        * @param string $content (html/text)
+        * @param int $index infoWindowCount
+        * @return void
+        */
+       public function setContentInfoWindow($content, $index) {
+               $this->map .= "
+                       gInfoWindows" . static::$mapCount . "[$index]. setContent(" . $this->escapeString($content) . ');';
+       }
+
+       /**
+        * Json encode string
+        *
+        * @param mixed $content
+        * @return string JSON
+        */
+       public function escapeString($content) {
+               return json_encode($content);
+       }
+
+       /**
+        * This method returns the javascript for the current map container.
+        * Including script tags.
+        * Just echo it below the map container. New: Alternativly, use finalize() directly.
+        *
+        * @return string
+        */
+       public function script() {
+               $script = '<script>
+               ' . $this->finalize(true) . '
+</script>';
+               return $script;
+       }
+
+       /**
+        * Finalize the map and write the javascript to the buffer.
+        * Make sure that your view does also output the buffer at some place!
+        *
+        * @param bool $return If the output should be returned instead
+        * @return null|string Javascript if $return is true
+        */
+       public function finalize($return = false) {
+               $script = $this->_arrayToObject('matching', $this->matching, false, true) . PHP_EOL;
+               $script .= $this->_arrayToObject('gIcons' . static::$mapCount, $this->icons, false, false) . '
+
+       jQuery(document).ready(function() {
+               ';
+
+               $script .= $this->map;
+               if ($this->_runtimeConfig['geolocate']) {
+                       $script .= $this->_geolocate();
+               }
+
+               if ($this->_runtimeConfig['showMarker'] && !empty($this->markers) && is_array($this->markers)) {
+                       $script .= implode($this->markers, ' ');
+               }
+
+               if ($this->_runtimeConfig['autoCenter']) {
+                       $script .= $this->_autoCenter();
+               }
+               $script .= '
+
+       });';
+               static::$mapCount++;
+               if ($return) {
+                       return $script;
+               }
+               $this->Html->scriptBlock($script, ['block' => true]);
+       }
+
+       /**
+        * Set a custom geolocate callback
+        *
+        * @param string|bool $js Custom JS
+        * false: no callback at all
+        * @return void
+        */
+       public function geolocateCallback($js) {
+               if ($js === false) {
+                       $this->_runtimeConfig['callbacks']['geolocate'] = false;
+                       return;
+               }
+               $this->_runtimeConfig['callbacks']['geolocate'] = $js;
+       }
+
+       /**
+        * Experimental - works in cutting edge browsers like chrome10
+        *
+        * @return string
+        */
+       protected function _geolocate() {
+               return '
+       // Try W3C Geolocation (Preferred)
+       if (navigator.geolocation) {
+               browserSupportFlag = true;
+               navigator.geolocation.getCurrentPosition(function(position) {
+                       geolocationCallback(position.coords.latitude, position.coords.longitude);
+               }, function() {
+                       handleNoGeolocation(browserSupportFlag);
+               });
+               // Try Google Gears Geolocation
+       } else if (google.gears) {
+               browserSupportFlag = true;
+               var geo = google.gears.factory.create("beta.geolocation");
+               geo.getCurrentPosition(function(position) {
+                       geolocationCallback(position.latitude, position.longitude);
+               }, function() {
+                       handleNoGeoLocation(browserSupportFlag);
+               });
+               // Browser doesn\'t support Geolocation
+       } else {
+               browserSupportFlag = false;
+               handleNoGeolocation(browserSupportFlag);
+       }
+
+       function geolocationCallback(lat, lng) {
+               ' . $this->_geolocationCallback() . '
+       }
+
+       function handleNoGeolocation(errorFlag) {
+       if (errorFlag == true) {
+               //alert("Geolocation service failed.");
+       } else {
+               //alert("Your browser doesn\'t support geolocation. We\'ve placed you in Siberia.");
+       }
+       //' . $this->name() . ' . setCenter(initialLocation);
+       }
+       ';
+       }
+
+       /**
+        * @return string
+        */
+       protected function _geolocationCallback() {
+               if (($js = $this->_runtimeConfig['callbacks']['geolocate']) === false) {
+                       return '';
+               }
+               if ($js === null) {
+                       $js = 'initialLocation = new google.maps.LatLng(lat, lng);
+               ' . $this->name() . ' . setCenter(initialLocation);
+';
+               }
+               return $js;
+       }
+
+       /**
+        * Auto center map
+        * careful: with only one marker this can result in too high zoom values!
+        *
+        * @return string autoCenterCommands
+        */
+       protected function _autoCenter() {
+               return '
+               var bounds = new google.maps.LatLngBounds();
+               $.each(gMarkers' . static::$mapCount . ',function (index, marker) { bounds.extend(marker.position);});
+               ' . $this->name() . ' .fitBounds(bounds);
+               ';
+       }
+
+       /**
+        * @return string JSON like js string
+        */
+       protected function _mapOptions() {
+               $options = $this->_runtimeConfig['map'] + $this->_runtimeConfig;
+
+               $mapOptions = array_intersect_key($options, [
+                       'streetViewControl' => null,
+                       'navigationControl' => null,
+                       'mapTypeControl' => null,
+                       'scaleControl' => null,
+                       'scrollwheel' => null,
+                       'zoom' => null,
+                       'keyboardShortcuts' => null,
+                       'styles' => null,
+               ]);
+               $res = [];
+               foreach ($mapOptions as $key => $mapOption) {
+                       $res[] = $key . ': ' . $this->value($mapOption);
+               }
+               if (empty($options['autoCenter'])) {
+                       $res[] = 'center: initialLocation';
+               }
+               if (!empty($options['navOptions'])) {
+                       $res[] = 'navigationControlOptions: ' . $this->_controlOptions('nav', $options['navOptions']);
+               }
+               if (!empty($options['typeOptions'])) {
+                       $res[] = 'mapTypeControlOptions: ' . $this->_controlOptions('type', $options['typeOptions']);
+               }
+               if (!empty($options['scaleOptions'])) {
+                       $res[] = 'scaleControlOptions: ' . $this->_controlOptions('scale', $options['scaleOptions']);
+               }
+
+               if (array_key_exists($options['type'], $this->types)) {
+                       $type = $this->types[$options['type']];
+               } else {
+                       $type = $options['type'];
+               }
+               $res[] = 'mapTypeId: google.maps.MapTypeId.' . $type;
+
+               return '{' . implode(', ', $res) . '}';
+       }
+
+       /**
+        * @param string $type
+        * @param array $options
+        * @return string JSON like js string
+        */
+       protected function _controlOptions($type, $options) {
+               $mapping = [
+                       'nav' => 'NavigationControlStyle',
+                       'type' => 'MapTypeControlStyle',
+                       'scale' => ''
+               ];
+               $res = [];
+               if (!empty($options['style']) && ($m = $mapping[$type])) {
+                       $res[] = 'style: google.maps.' . $m . '.' . $options['style'];
+               }
+               if (!empty($options['pos'])) {
+                       $res[] = 'position: google.maps.ControlPosition.' . $options['pos'];
+               }
+
+               return '{' . implode(', ', $res) . '}';
+       }
+
+       /**
+        * Returns a maps.google link
+        *
+        * @param string $title  Link title
+        * @param array $mapOptions
+        * @param array $linkOptions
+        * @return string HTML link
+        */
+       public function mapLink($title, $mapOptions = [], $linkOptions = []) {
+               return $this->Html->link($title, $this->mapUrl($mapOptions + ['escape' => false]), $linkOptions);
+       }
+
+       /**
+        * Returns a maps.google url
+        *
+        * Options:
+        * - from: necessary (address or lat,lng)
+        * - to: 1x necessary (address or lat,lng - can be an array of multiple destinations: array('dest1', 'dest2'))
+        * - zoom: optional (defaults to none)
+        * - query: Additional query strings as array
+        * - escape: defaults to true
+        *
+        * @param array $options Options
+        * @return string link: http://...
+        */
+       public function mapUrl(array $options = []) {
+               $url = $this->_protocol() . 'maps.google.com/maps?';
+
+               $urlArray = !empty($options['query']) ? $options['query'] : [];
+               if (!empty($options['from'])) {
+                       $urlArray['saddr'] = $options['from'];
+               }
+
+               if (!empty($options['to']) && is_array($options['to'])) {
+                       $to = array_shift($options['to']);
+                       foreach ($options['to'] as $key => $value) {
+                               $to .= '+to:' . $value;
+                       }
+                       $urlArray['daddr'] = $to;
+               } elseif (!empty($options['to'])) {
+                       $urlArray['daddr'] = $options['to'];
+               }
+
+               if (isset($options['zoom']) && $options['zoom'] !== false) {
+                       $urlArray['z'] = (int)$options['zoom'];
+               }
+               //$urlArray[] = 'f=d';
+               //$urlArray[] = 'hl=de';
+               //$urlArray[] = 'ie=UTF8';
+
+               $options += [
+                       'escape' => true,
+               ];
+
+               $query = http_build_query($urlArray);
+               if ($options['escape']) {
+                       $query = h($query);
+               }
+
+               return $url . $query;
+       }
+
+       /**
+        * Creates a plain image map.
+        *
+        * @link http://code.google.com/intl/de-DE/apis/maps/documentation/staticmaps
+        * @param array $options Options
+        * - string $size [necessary: VALxVAL, e.g. 500x400 - max 640x640]
+        * - string $center: x,y or address [necessary, if no markers are given; else tries to take defaults if available] or TRUE/FALSE
+        * - int $zoom [optional; if no markers are given, default value is used; if set to "auto" and ]*
+        * - array $markers [optional, @see staticPaths() method]
+        * - string $type [optional: roadmap/hybrid, ...; default:roadmap]
+        * - string $mobile TRUE/FALSE
+        * - string $visible: $area (x|y|...)
+        * - array $paths [optional, @see staticPaths() method]
+        * - string $language [optional]
+        * @param array $attributes HTML attributes for the image
+        * - title
+        * - alt (defaults to 'Map')
+        * - url (tip: you can pass $this->link(...) and it will create a link to maps.google.com)
+        * @return string imageTag
+        */
+       public function staticMap(array $options = [], array $attributes = []) {
+               $defaultAttributes = ['alt' => __d('tools', 'Map')];
+               $attributes += $defaultAttributes;
+
+               // This was fixed in 3.5.1 to auto-escape URL query strings for security reasons
+               $escape = version_compare(Configure::version(), '3.5.1') < 0 ? true : false;
+               return $this->Html->image($this->staticMapUrl($options + ['escape' => $escape]), $attributes);
+       }
+
+       /**
+        * Create a link to a plain image map
+        *
+        * @param string $title Link title
+        * @param array $mapOptions
+        * @param array $linkOptions
+        * @return string HTML link
+        */
+       public function staticMapLink($title, array $mapOptions = [], array $linkOptions = []) {
+               return $this->Html->link($title, $this->staticMapUrl($mapOptions + ['escape' => false]), $linkOptions);
+       }
+
+       /**
+        * Creates a URL to a plain image map.
+        *
+        * Options:
+        * - escape: defaults to true (Deprecated as of CakePHP 3.5.1 and now has to be always false)
+        *
+        * @param array $options
+        * - see staticMap() for details
+        * @return string urlOfImage: http://...
+        */
+       public function staticMapUrl(array $options = []) {
+               $mapUrl = $this->_protocol() . static::STATIC_API;
+               /*
+               $params = array(
+                       'mobile' => 'false',
+                       'format' => 'png',
+                       //'center' => false
+               );
+
+               if (!empty($options['mobile'])) {
+                       $params['mobile'] = 'true';
+               }
+               */
+
+               $defaults = $this->_config['staticMap'] + $this->_config;
+
+               $mapOptions = $options + $defaults;
+
+               $params = array_intersect_key($mapOptions, [
+                       'mobile' => null,
+                       'format' => null,
+                       'size' => null,
+                       //'zoom' => null,
+                       //'lat' => null,
+                       //'lng' => null,
+                       //'visible' => null,
+                       //'type' => null,
+               ]);
+
+               // add API key to parameters.
+               if ($this->_runtimeConfig['key']) {
+                       $params['key'] = $this->_runtimeConfig['key'];
+               }
+
+               // do we want zoom to auto-correct itself?
+               if (!isset($options['zoom']) && !empty($mapOptions['markers']) || !empty($mapOptions['paths']) || !empty($mapOptions['visible'])) {
+                       $options['zoom'] = 'auto';
+               }
+
+               // a position on the map that is supposed to stay visible at all cost
+               if (!empty($mapOptions['visible'])) {
+                       $params['visible'] = urlencode($mapOptions['visible']);
+               }
+
+               // center and zoom are not necessary if path, visible or markers are given
+               if (!isset($options['center']) || $options['center'] === false) {
+                       // dont use it
+               } elseif ($options['center'] === true && $mapOptions['lat'] !== null && $mapOptions['lng'] !== null) {
+                       $params['center'] = urlencode((string)$mapOptions['lat'] . ',' . (string)$mapOptions['lng']);
+               } elseif (!empty($options['center'])) {
+                       $params['center'] = urlencode($options['center']);
+               } /*else {
+                       // try to read from markers array???
+                       if (isset($options['markers']) && count($options['markers']) == 1) {
+                               //pr ($options['markers']);
+                       }
+               }*/
+
+               if (!isset($options['zoom']) || $options['zoom'] === false) {
+                       // dont use it
+               } else {
+                       if ($options['zoom'] === 'auto') {
+                               if (!empty($options['markers']) && strpos($options['zoom'], '|') !== false) {
+                                       // let google find the best zoom value itself
+                               } else {
+                                       // do something here?
+                               }
+                       } else {
+                               $params['zoom'] = $options['zoom'];
+                       }
+               }
+
+               if (array_key_exists($mapOptions['type'], $this->types)) {
+                       $params['maptype'] = $this->types[$mapOptions['type']];
+               } else {
+                       $params['maptype'] = $mapOptions['type'];
+               }
+               $params['maptype'] = strtolower($params['maptype']);
+
+               // old: {latitude},{longitude},{color}{alpha-character}
+               // new: @see staticMarkers()
+               if (!empty($options['markers'])) {
+                       $params['markers'] = $options['markers'];
+               }
+
+               if (!empty($options['paths'])) {
+                       $params['path'] = $options['paths'];
+               }
+
+               // valXval
+               if (!empty($options['size'])) {
+                       $params['size'] = $options['size'];
+               }
+
+               $pieces = [];
+               foreach ($params as $key => $value) {
+                       if (is_array($value)) {
+                               $value = implode('&' . $key . '=', $value);
+                       } elseif ($value === true) {
+                               $value = 'true';
+                       } elseif ($value === false) {
+                               $value = 'false';
+                       } elseif ($value === null) {
+                               continue;
+                       }
+                       $pieces[] = $key . '=' . $value;
+               }
+
+               $options += [
+                       'escape' => true,
+               ];
+               $query = implode('&', $pieces);
+               if ($options['escape']) {
+                       $query = h($query);
+               }
+
+               return $mapUrl . '?' . $query;
+       }
+
+       /**
+        * Prepare paths for staticMap
+        *
+        * @param array $pos PathElementArrays
+        * - elements: [required] (multiple array(lat=>x, lng=>y) or just a address strings)
+        * - color: red/blue/green (optional, default blue)
+        * - weight: numeric (optional, default: 5)
+        * @return array Array of paths: e.g: color:0x0000FF80|weight:5|37.40303,-122.08334|37.39471,-122.07201|37.40589,-122.06171{|...}
+        */
+       public function staticPaths(array $pos = []) {
+               $defaults = [
+                       'color' => 'blue',
+                       'weight' => 5 // pixel
+               ];
+
+               // not a 2-level array? make it one
+               if (!isset($pos[0])) {
+                       $pos = [$pos];
+               }
+
+               $res = [];
+               foreach ($pos as $p) {
+                       $options = $p + $defaults;
+
+                       $markers = $options['path'];
+                       unset($options['path']);
+
+                       // prepare color
+                       if (!empty($options['color'])) {
+                               $options['color'] = $this->_prepColor($options['color']);
+                       }
+
+                       $path = [];
+                       foreach ($options as $key => $value) {
+                               $path[] = $key . ':' . urlencode($value);
+                       }
+                       foreach ($markers as $key => $pos) {
+                               if (is_array($pos)) {
+                                       // lat/lng?
+                                       $pos = $pos['lat'] . ',' . $pos['lng'];
+                               }
+                               $path[] = $pos;
+                       }
+                       $res[] = implode('|', $path);
+               }
+               return $res;
+       }
+
+       /**
+        * Prepare markers for staticMap
+        *
+        * @param array $pos markerArrays
+        * - lat: xx.xxxxxx (necessary)
+        * - lng: xx.xxxxxx (necessary)
+        * - address: (instead of lat/lng)
+        * - color: red/blue/green (optional, default blue)
+        * - label: a-z or numbers (optional, default: s)
+        * - icon: custom icon (png, gif, jpg - max 64x64 - max 5 different icons per image)
+        * - shadow: TRUE/FALSE
+        * @param array $style (global) (overridden by custom marker styles)
+        * - color
+        * - label
+        * - icon
+        * - shadow
+        * @return array markers: color:green|label:Z|48,11|Berlin
+        *
+        * NEW: size:mid|color:red|label:E|37.400465,-122.073003|37.437328,-122.159928&markers=size:small|color:blue|37.369110,-122.096034
+        * OLD: 40.702147,-74.015794,blueS|40.711614,-74.012318,greenG{|...}
+        */
+       public function staticMarkers(array $pos = [], array $style = []) {
+               $markers = [];
+               $verbose = false;
+
+               $defaults = [
+                       'shadow' => 'true',
+                       'color' => 'blue',
+                       'label' => '',
+                       'address' => '',
+                       'size' => ''
+               ];
+
+               // not a 2-level array? make it one
+               if (!isset($pos[0])) {
+                       $pos = [$pos];
+               }
+
+               // new in staticV2: separate styles! right now just merged
+               foreach ($pos as $p) {
+                       $p += $style + $defaults;
+
+                       // adress or lat/lng?
+                       if (!empty($p['lat']) && !empty($p['lng'])) {
+                               $p['address'] = $p['lat'] . ',' . $p['lng'];
+                       }
+                       $p['address'] = urlencode($p['address']);
+
+                       $values = [];
+
+                       // prepare color
+                       if (!empty($p['color'])) {
+                               $p['color'] = $this->_prepColor($p['color']);
+                               $values[] = 'color:' . $p['color'];
+                       }
+                       // label? A-Z0-9
+                       if (!empty($p['label'])) {
+                               $values[] = 'label:' . strtoupper($p['label']);
+                       }
+                       if (!empty($p['size'])) {
+                               $values[] = 'size:' . $p['size'];
+                       }
+                       if (!empty($p['shadow'])) {
+                               $values[] = 'shadow:' . $p['shadow'];
+                       }
+                       if (!empty($p['icon'])) {
+                               $values[] = 'icon:' . urlencode($p['icon']);
+                       }
+                       $values[] = $p['address'];
+
+                       //TODO: icons
+                       $markers[] = implode('|', $values);
+               }
+
+               //TODO: shortcut? only possible if no custom params!
+               if ($verbose) {
+
+               }
+               // long: markers=styles1|address1&markers=styles2|address2&...
+               // short: markers=styles,address1|address2|address3|...
+
+               return $markers;
+       }
+
+       /**
+        * Ensure that we stay on the appropriate protocol
+        *
+        * @return string protocol base (including ://)
+        */
+       protected function _protocol() {
+               $https = $this->_runtimeConfig['https'];
+               if ($https === null) {
+                       $https = !empty($_SERVER['HTTPS']) && $_SERVER['HTTPS'] === 'on';
+               }
+               return ($https ? 'https' : 'http') . '://';
+       }
+
+       /**
+        * // to 0x
+        * or // added
+        *
+        * @param string $color Color: FFFFFF, #FFFFFF, 0xFFFFFF or blue
+        * @return string Color
+        */
+       protected function _prepColor($color) {
+               if (strpos($color, '#') !== false) {
+                       return str_replace('#', '0x', $color);
+               }
+               if (is_numeric($color)) {
+                       return '0x' . $color;
+               }
+               return $color;
+       }
+
+       /**
+        * @param string $name
+        * @param array $array
+        * @param bool $asString
+        * @param bool $keyAsString
+        * @return string
+        */
+       protected function _arrayToObject($name, $array, $asString = true, $keyAsString = false) {
+               $res = 'var ' . $name . ' = {' . PHP_EOL;
+               $res .= $this->_toObjectParams($array, $asString, $keyAsString);
+               $res .= '};';
+               return $res;
+       }
+
+       /**
+        * @param array $array
+        * @param bool $asString
+        * @param bool $keyAsString
+        * @return string
+        */
+       protected function _toObjectParams($array, $asString = true, $keyAsString = false) {
+               $pieces = [];
+               foreach ($array as $key => $value) {
+                       $e = ($asString && strpos($value, 'new ') !== 0 ? '"' : '');
+                       $ke = ($keyAsString ? '"' : '');
+                       $pieces[] = $ke . $key . $ke . ': ' . $e . $value . $e;
+               }
+               return implode(',' . PHP_EOL, $pieces);
+       }
+
+}
diff --git a/production/sites/cakelampvm.com/goog_maps_helper_mod/compare_with_install.sh b/production/sites/cakelampvm.com/goog_maps_helper_mod/compare_with_install.sh
new file mode 100644 (file)
index 0000000..cd46374
--- /dev/null
@@ -0,0 +1,11 @@
+#!/bin/bash
+
+
+if [ ! -d "$HOME/apps/mapsdemo/avenger5" ]; then
+  echo Not seeing the mapsdemo checked out man.
+  exit 1
+fi
+
+meld $(find ~/apps/mapsdemo/avenger5/ -iname goog*map*helper.php) ./Goog*Map*Help*p
+
+
diff --git a/production/sites/cakelampvm.com/hello.txt b/production/sites/cakelampvm.com/hello.txt
new file mode 100644 (file)
index 0000000..7bf0d1b
--- /dev/null
@@ -0,0 +1,25 @@
+Welcome to the CakePHP LAMP VM.
+
+Please refer to the built-in documentation available at: http://cakelampvm.com
+
+Some first steps to make this vm your own:
+
+####
+
+1) change your password for the developer account.
+(may eventually be automatically required)
+
+####
+
+2) change your git configuration for user and email.  this is how we've
+configured it so far:
+
+  # git config --global user.email "developer@cakelampvm.com"
+  # git config --global user.name "Developer J. Cakemo"
+
+if you're developing on a real project, you probably don't want the bogus
+email and even more bogus name above attached to your commits.
+Just run the two commands again but with proper values.
+
+####
+
diff --git a/production/sites/cakelampvm.com/images/green-check-mark-in-circle-hi.png b/production/sites/cakelampvm.com/images/green-check-mark-in-circle-hi.png
new file mode 100644 (file)
index 0000000..ab1d67b
Binary files /dev/null and b/production/sites/cakelampvm.com/images/green-check-mark-in-circle-hi.png differ
diff --git a/production/sites/cakelampvm.com/images/green_globe_exclamation_point_570.jpg b/production/sites/cakelampvm.com/images/green_globe_exclamation_point_570.jpg
new file mode 100644 (file)
index 0000000..8cb9aa7
Binary files /dev/null and b/production/sites/cakelampvm.com/images/green_globe_exclamation_point_570.jpg differ
diff --git a/production/sites/cakelampvm.com/images/red_exclamation_mark_icon_256.png b/production/sites/cakelampvm.com/images/red_exclamation_mark_icon_256.png
new file mode 100644 (file)
index 0000000..bae1cf6
Binary files /dev/null and b/production/sites/cakelampvm.com/images/red_exclamation_mark_icon_256.png differ
diff --git a/production/sites/cakelampvm.com/images/red_x_1194985626525719339tasto_11_architetto_fran_01.svg.med.png b/production/sites/cakelampvm.com/images/red_x_1194985626525719339tasto_11_architetto_fran_01.svg.med.png
new file mode 100644 (file)
index 0000000..c6d020e
Binary files /dev/null and b/production/sites/cakelampvm.com/images/red_x_1194985626525719339tasto_11_architetto_fran_01.svg.med.png differ
diff --git a/production/sites/cakelampvm.com/index.html b/production/sites/cakelampvm.com/index.html
new file mode 100644 (file)
index 0000000..66fc8d6
--- /dev/null
@@ -0,0 +1,25 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta content="text/html; charset=windows-1252" http-equiv="content-type">
+    <title>cakelampvm.com official internet site (NOT the local virtual machine)</title>
+  </head>
+  <body>
+    <div style="text-align: center;"><img alt="uh oh red exclamation" src="images/red_exclamation_mark_icon_256.png" width="140" height="140">
+      <img alt="x marks a bad spot" src="images/red_x_1194985626525719339tasto_11_architetto_fran_01.svg.med.png" width="140" height="140">
+      <img alt="uh oh red exclamation" src="images/red_exclamation_mark_icon_256.png" width="140" height="140"></div>
+    <h1 style=" text-align: center;">cakelampvm.com REAL INTERNET site</h1>
+    <h2 style=" text-align: center;"> </h2>
+    <h2 style=" text-align: center;"> &#9733;&#9733; If you are running the cakelampvm, &#9733;&#9733;<br>
+      &#9733;&#9733; then it is not yet configured properly. &#9733;&#9733; </h2>
+    <h3 style=" text-align: center;">This is the cakelampvm.com domain that is
+      actually <span style="font-weight: bold;">on the internet</span>.</h3>
+    <h4 style=" text-align: center;">Official cakelampvm setup guide: <a
+        target="_blank" href="docs/manual/cakelampvm_guide_v002.html">The
+        cakelampvm.com VM: Configuration and Usage</a></h4>
+    <div style="text-align: center;"><img alt="uh oh red exclamation" src="images/red_exclamation_mark_icon_256.png" width="140" height="140">
+      <img alt="x marks a bad spot" src="images/red_x_1194985626525719339tasto_11_architetto_fran_01.svg.med.png" width="140" height="140">
+      <img alt="uh oh red exclamation" src="images/red_exclamation_mark_icon_256.png" width="140" height="140">
+</div>
+  </body>
+</html>
diff --git a/production/sites/cakelampvm.com/vm_index.html b/production/sites/cakelampvm.com/vm_index.html
new file mode 100644 (file)
index 0000000..362b321
--- /dev/null
@@ -0,0 +1,34 @@
+<!DOCTYPE html>
+<html>
+  <head>
+    <meta content="text/html; charset=windows-1252" http-equiv="content-type">
+    <title>cakelampvm.com served by your local virtual machine!</title>
+  </head>
+  <body>
+    <div style="text-align: center;"><img alt="yay, green exclaimed" src="images/green_globe_exclamation_point_570.jpg"
+        height="140" width="140"> <img alt="you're in the right place, and dns is being hosted by your local cakelampvm.com virtual machine"
+        src="images/green-check-mark-in-circle-hi.png" height="140" width="140">
+      <img alt="yay, green exclaimed" src="images/green_globe_exclamation_point_570.jpg"
+        height="140" width="140"></div>
+    <h1 style=" text-align: center;">Yes!<br>
+      You're on the cakelampvm.com virtualized site!</h1>
+    <h2 style=" text-align: center;"> </h2>
+    <h2 style=" text-align: center;"> &#9733;&#9733; Your cakelampvm virtual machine seems
+      to be &#9733;&#9733;<br>
+      &#9733;&#9733;&nbsp; successfully overriding this domain (and any&nbsp; &#9733;&#9733;<br>
+      &#9733;&#9733; other sites you have added to host on the vm). &#9733;&#9733; </h2>
+    <h3 style=" text-align: center;">This is the cakelampvm.com domain that is
+      served from your cakelampvm virtual machine<br>
+      <span style="font-style: italic;">(as long as browser URL bar actually
+        says just "http://cakelampvm.com" or "http://www.cakelampvm.com").</span></h3>
+    <h4 style=" text-align: center;">Official cakelampvm setup guide: <a target="_blank"
+        href="docs/manual/cakelampvm_guide_v002.html">The cakelampvm.com VM:
+        Configuration and Usage</a></h4>
+    <div style="text-align: center;"><img alt="yay, green exclaimed" src="images/green_globe_exclamation_point_570.jpg"
+        height="140" width="140"> <img alt="you're in the right place, and dns is being hosted by your local cakelampvm.com virtual machine"
+        src="images/green-check-mark-in-circle-hi.png" height="140" width="140">
+      <img alt="yay, green exclaimed" src="images/green_globe_exclamation_point_570.jpg"
+        height="140" width="140">
+    </div>
+  </body>
+</html>
index 0fda046ea9e12470e4bf01335b68dc7ea8888cd7..ca3b745926850fed6a873ed75741dbbe2fd02423 100644 (file)
@@ -34,6 +34,10 @@ Connect the feisty meow scripts to your login script (in ~/.bashrc).
 Note that this actually modifies ~/.bashrc.  This step is only needed once.
 # bash /opt/feistymeow.org/feisty_meow/scripts/core/connect_feisty_meow.sh
 
+| For the root user, you can pass a flag '--root' to the connect_feisty_meow
+| script.  This will add an alias for 'feistyme' which loads the feisty meow
+| scripts on demand (instead of automatically upon login).
+
 Load the script environment into the current shell.  This can be done for
 any new shell.  This is idempotent, so it does no harm to run it again.
 Note that you should not need this step if you connected feisty meow to
index a2f89349398941c57c4e804dc18cfac1cb0511a3..c6dbbafb01d003aa578677235c4cc89dca1068d3 100644 (file)
@@ -116,6 +116,9 @@ analyze_hierarchy_and_report ~/cloud/urgent "high priority (aieeee!)"
 # notes are individual files of tasks, usually, although some are combined.
 analyze_hierarchy_and_report ~/cloud/grunty_notes "grunty notes (externalities)"
 
+# web site development tasks.
+analyze_hierarchy_and_report ~/cloud/webular "web design (ideas and tasks)"
+
 # feisty notes are about feisty meow(r) concerns ltd codebase development.
 analyze_hierarchy_and_report ~/cloud/feisty_notes "feisty meow notes (mondo coding)"
 
index 7f64b96dc49a4a761ccdf1c9ca2956cb9735c418..e6c54770b2d92693b940f5f267cc06f580b65fd3 100644 (file)
@@ -58,6 +58,7 @@ local($root) = &canonicalize("$FEISTY_MEOW_APEX");
 &backup_hierarchy($snarf_file_base, $number, "$root", "production/assign_bases");
 &backup_hierarchy($snarf_file_base, $number, "$root", "production/check_versions");
 &backup_hierarchy($snarf_file_base, $number, "$root", "production/setup_src");
+&backup_hierarchy($snarf_file_base, $number, "$root", "production/sites");
 
 # now rename the file so only the unpacker can access it.
 &rename_archive($snarf_file);
index e15541526878d62d4f551cff3da238b860a05d0d..87e47676a43e7ededa30ff106fa0e83a0eac7577 100644 (file)
@@ -83,7 +83,7 @@ define_yeti_alias lesser='bash $FEISTY_MEOW_SCRIPTS/files/lesser.sh'
 
 # some aliases that are just generally nice to have.
 define_yeti_alias aliases=alias
-define_yeti_alias calc='kcalc'
+define_yeti_alias calc='galculator'
 define_yeti_alias cd..='\cd ..'
 define_yeti_alias cd...='\cd ../..'
 define_yeti_alias cd....='\cd ../../..'
@@ -102,7 +102,10 @@ define_yeti_alias feisty_branch='pushd $FEISTY_MEOW_APEX ; git branch ; popd'
 # some important retreads on aliases that provide a sudo-ized version of other scripts.
 
 define_yeti_alias snarf_linux_config="sudo -E PERLLIB=\$PERLLIB perl \$FEISTY_MEOW_SCRIPTS/archival/snarf_linux_config.pl"
-#no, does its own sudo wrangling.edefine_yeti_alias standup="sudo bash \"$FEISTY_MEOW_SCRIPTS/site_avenger/standup.sh\""
+define_yeti_alias add_domain="sudo bash \$FEISTY_MEOW_SCRIPTS/system/add_domain.sh"
+define_yeti_alias remove_domain="sudo bash \$FEISTY_MEOW_SCRIPTS/system/remove_domain.sh"
+define_yeti_alias add_apache_site="sudo bash \$FEISTY_MEOW_SCRIPTS/system/add_apache_site.sh"
+define_yeti_alias remove_apache_site="sudo bash \$FEISTY_MEOW_SCRIPTS/system/remove_apache_site.sh"
 
 #hmmm: some magma intrusions from the fred customizations...
 define_yeti_alias revamp_cakelampvm="sudo bash \"$FEISTY_MEOW_SCRIPTS/site_avenger/revamp_cakelampvm.sh\""
@@ -133,11 +136,10 @@ define_yeti_alias why='echo We all wonder what the point of the universe is at t
 
 ##############
 
-# remove the fredization macro if it was defined, helping to avoid running
-# the shell scripts twice for users like root that don't always load this
-# stuff.
-unalias fredme &>/dev/null
-unalias feistyme &>/dev/null
+# remove the "fredization" or "feistymeowization" macros if they're defined,
+# which reduces the chance of loading the shell environment twice for users
+# like root that don't load feisty meow automatically.
+unalias fredme feistyme &>/dev/null
 
 ##############
 
index 6a226b8dfe3e8c15b4c670e084a2f89154922c58..6f6ffd3840536016553661e94eb259b9133ac001 100644 (file)
@@ -27,10 +27,12 @@ else
         >> "$HOME/.bashrc"
     echo "Feisty Meow is now configured in '~/.bashrc' for standard users."
   else
-    # stuff the root user init file into .bashrc.  this one doesn't automatically load
-    # feisty meow.  instead, it provides a fredme macro to load the feisty meow scripts.
-    # fredme comes from the main author being fred t. hamster.  we have since added a
-    # feistyme macro too, to be less personalized...
+    # stuff the root user init file into .bashrc.  this one doesn't
+    # automatically load the feisty meow scripts.  instead, there is a macro
+    # (uhh, an alias) that loads the feisty meow scripts.  the 'fredme' macro
+    # comes from the main author of feisty meow, named fred t. hamster.  we
+    # have since added a 'feistyme' macro too, to be slightly less
+    # idiosyncratic, as if that were possible.
     cat $FEISTY_MEOW_APEX/infobase/feisty_inits/dot.bashrc-root |
       sed -e \
         "s?FEISTY_MEOW_APEX=\".*\"?FEISTY_MEOW_APEX=\"$FEISTY_MEOW_APEX\"?" \
index 6797708525179750e9e84c5de98e4b211c44c2de..daaabcb16cece5a745d7735421fbecfc1c4691f3 100644 (file)
@@ -49,6 +49,8 @@ if [ -z "$skip_all" ]; then
     return $?
   }
 
+  ##############
+
   # displays the value of a variable in bash friendly format.
   function var() {
     HOLDIFS="$IFS"
@@ -84,6 +86,8 @@ if [ -z "$skip_all" ]; then
     IFS="$HOLDIFS"
   }
 
+  ##############
+
   # when passed a list of things, this will return the unique items from that list as an echo.
   function uniquify()
   {
@@ -104,6 +108,8 @@ if [ -z "$skip_all" ]; then
     fi
   }
 
+  ##############
+
   function success_sound()
   {
     if [ ! -z "$CLAM_FINISH_SOUND" ]; then
@@ -118,6 +124,20 @@ if [ -z "$skip_all" ]; then
     fi
   }
 
+  ##############
+
+  # echoes the maximum number of columns that the terminal supports.  usually
+  # anything you print to the terminal with length less than (but not equal to)
+  # maxcols will never wrap.
+  function get_maxcols()
+  {
+    # calculate the number of columsn in the terminal.
+    local cols=$(stty size | awk '{print $2}')
+    echo $cols
+  }
+
+  ##############
+
   # checks the result of the last command that was run, and if that failed,
   # then this complains and exits from bash.  the function parameters are
   # used as the message to print as a complaint.
@@ -139,19 +159,29 @@ if [ -z "$skip_all" ]; then
     fi
   }
 
+  ##############
+
   # wraps secure shell with some parameters we like, most importantly to enable X forwarding.
   function ssh()
   {
     local args=($*)
-    save_terminal_title
     # we remember the old terminal title, then force the TERM variable to a more generic
     # version for the other side (just 'linux'); we don't want the remote side still
     # thinking it's running xterm.
-    export TERM=linux
+    save_terminal_title
+#hmmm: why were we doing this?  it scorches the user's logged in session, leaving it without proper terminal handling.
+#    # we save the value of TERM; we don't want to leave the user's terminal
+#    # brain dead once we come back from this function.
+#    local oldterm="$TERM"
+#    export TERM=linux
     /usr/bin/ssh -X -C "${args[@]}"
+#    # restore the terminal variable also.
+#    TERM="$oldterm"
     restore_terminal_title
   }
 
+  ##############
+
   # locates a process given a search pattern to match in the process list.
   # supports a single command line flag style parameter of "-u USERNAME";
   # if the -u flag is found, a username is expected afterwards, and only the
@@ -267,6 +297,9 @@ if [ -z "$skip_all" ]; then
     fi
   }
   
+  ##############
+
+#hmmm: holy crowbars, this is an old one.  do we ever still have any need of it?
   # an unfortunately similarly named function to the above 'ps' as in process
   # methods, but this 'ps' stands for postscript.  this takes a postscript file
   # and converts it into pcl3 printer language and then ships it to the printer.
@@ -280,9 +313,21 @@ if [ -z "$skip_all" ]; then
     done
   }
   
-#  function fix_alsa() {
-#    sudo /etc/init.d/alsasound restart
-#  }
+#hmmm: not really doing anything yet; ubuntu seems to have changed from pulseaudio in 17.04?
+  # restarts the sound driver.
+  function fix_sound_driver() {
+    # stop bash complaining about blank function body.
+    local nothing=
+#if alsa something
+#    sudo service alsasound restart
+#elif pulse something
+#    sudo pulseaudio -k
+#    sudo pulseaudio -D
+#else
+#    something else...?
+#fi
+
+  }
 
   function screen() {
     save_terminal_title
@@ -431,6 +476,14 @@ if [ -z "$skip_all" ]; then
       echo "but that folder does not exist.  Skipping customization."
       return 1
     fi
+
+    # prevent permission foul-ups.
+#hmmm: save error output here instead of muting it.
+#hmmm: better yet actually, just don't complain on freaking cygwin, since that's where this happens
+    chown -R "$(logname):$(logname)" \
+        "$FEISTY_MEOW_LOADING_DOCK"/* "$FEISTY_MEOW_GENERATED_STORE"/* 2>/dev/null
+    test_or_continue "chowning to $(logname) didn't happen."
+
     regenerate >/dev/null
     pushd "$FEISTY_MEOW_LOADING_DOCK/custom" &>/dev/null
     incongruous_files="$(bash "$FEISTY_MEOW_SCRIPTS/files/list_non_dupes.sh" "$FEISTY_MEOW_SCRIPTS/customize/$custom_user" "$FEISTY_MEOW_LOADING_DOCK/custom")"
@@ -460,6 +513,11 @@ if [ -z "$skip_all" ]; then
     echo
     regenerate
 
+    # prevent permission foul-ups, again.
+    chown -R "$(logname):$(logname)" \
+        "$FEISTY_MEOW_LOADING_DOCK" "$FEISTY_MEOW_GENERATED_STORE" 2>/dev/null
+    test_or_continue "chowning to $(logname) didn't happen."
+
     restore_terminal_title
   }
 
@@ -803,6 +861,69 @@ return 0
 
   ##############
 
+  # given a filename and a string to seek and a number of lines, then this
+  # function will remove the first occurrence of a line in the file that
+  # matches the string, and it will also axe the next N lines as specified.
+  function create_chomped_copy_of_file()
+  {
+    local filename="$1"; shift
+    local seeker="$1"; shift
+    local numlines=$1; shift
+
+#echo into create_chomped_copy...
+#var filename seeker numlines 
+
+    # make a backup first, oy.
+    \cp -f "$filename" "/tmp/$(basename ${filename}).bkup-${RANDOM}" 
+    test_or_die "backing up file: $filename"
+
+    # make a temp file to write to before we move file into place in bind.
+    local new_version="/tmp/$(basename ${filename}).bkup-${RANDOM}" 
+    \rm -f "$new_version"
+    test_or_die "cleaning out new version of file from: $new_version"
+
+    local line
+    local skip_count=0
+    local found_any=
+    while read line; do
+      # don't bother looking at the lines if we're already in skip mode.
+      if [[ $skip_count == 0 ]]; then
+        # find the string they're seeking.
+        if [[ ! "$line" =~ .*${seeker}.* ]]; then
+          # no match.
+          echo "$line" >> "$new_version"
+        else
+          # a match!  start skipping.  we will delete this line and the next N lines.
+          ((skip_count++))
+#echo first skip count is now $skip_count
+          found_any=yes
+        fi
+      else
+        # we're already skipping.  let's keep going until we hit the limit.
+        ((skip_count++))
+#echo ongoing skip count is now $skip_count
+        if (( $skip_count > $numlines )); then
+          echo "Done skipping, and back to writing output file."
+          skip_count=0
+        fi
+      fi
+    done < "$filename"
+
+#echo file we created looks like this:
+#cat "$new_version"
+
+    if [ ! -z "$found_any" ]; then
+      # put the file back into place under the original name.
+      \mv "$new_version" "$filename"
+      test_or_die "moving the new version into place in: $filename"
+    else
+      # cannot always be considered an error, but we can at least gripe.
+      echo "Did not find any matches for seeker '$seeker' in file: $filename"
+    fi
+  }
+
+  ##############
+
   # NOTE: no more function definitions are allowed after this point.
 
   function function_sentinel()
index eeb77039ad4cacc95c434ceeec771214b76370d3..db33f74cf5b0afddab17b93f6fd0e5b96f3ebaa6 100644 (file)
@@ -38,7 +38,7 @@ fi
 # decide whether they've got splitter available or not.
 if [ -f "$FEISTY_MEOW_BINARIES/splitter" -o -f "$FEISTY_MEOW_BINARIES/splitter.exe" ]; then
   # calculate the number of columsn in the terminal.
-  cols=$(stty size | awk '{print $2}')
+  cols=$(get_maxcols)
   splitter="$FEISTY_MEOW_BINARIES/splitter --maxcol $(($cols - 1))"
 else
   # not available, so just emit as huge overly long string.
index fc50921c06c3adb2843ef01e9753e77df43f07e5..f793c6d9d412d4ed80afc8663f686e7a8dffd090 100644 (file)
@@ -215,6 +215,23 @@ if [ "$NO_REPAIRS_NEEDED" == "true" ]; then
     unset FEISTY_MEOW_SHOW_LAUNCH_GREETING
   fi
 
+  # only run this hello file if the core feisty meow support haven't been loaded already.  this
+  # hopefully guarantees we show the info at most once in one shell continuum.
+  # this can also be disabled if the NO_HELLO variable has a non-empty value.
+  type CORE_VARIABLES_LOADED &>/dev/null
+  if [ $? -ne 0 -a -z "$NO_HELLO" ]; then
+    # print out a personalized hello file if we find one.
+    if [ -f ~/hello.txt ]; then
+      echo
+      sep 28
+      perl $FEISTY_MEOW_SCRIPTS/*/filedump.pl ~/hello.txt
+      sep 28
+      echo
+    fi
+    # from now on there should be no extra helloing.
+    export NO_HELLO=true
+  fi
+
   # load the last bits we do here.
   source "$FEISTY_MEOW_LOADING_DOCK/fmc_ending_sentinel.sh"
 
index b6421224607bcd18d5fd39ca55e630eff0046113..2224c716e3f0e12ab664b6757919716764867169 100644 (file)
@@ -25,15 +25,15 @@ export FEISTY_MEOW_APEX="$(/bin/pwd)"
 unset CORE_VARIABLES_LOADED
 
 # repetitive bit stolen from variables.  should make a file out of this somehow.
-IS_DOS=$(uname | grep -i ming)
-if [ -z "$IS_DOS" ]; then IS_DOS=$(uname | grep -i cygwin); fi
-# now if we're stuck in DOS, then fix the feisty meow variable name.
-if [ ! -z "$IS_DOS" ]; then
-  FEISTY_MEOW_APEX="$(cmd /c chdir | tr A-Z a-z | sed -e 's/\\/\//g')"
-echo feisty meow dos is: $FEISTY_MEOW_APEX
-  FEISTY_MEOW_APEX="$(dos_to_unix_path "$FEISTY_MEOW_APEX")"
-echo new feisty meow fixed dir is: $FEISTY_MEOW_APEX
-fi
+#IS_DOS=$(uname | grep -i ming)
+#if [ -z "$IS_DOS" ]; then IS_DOS=$(uname | grep -i cygwin); fi
+## now if we're stuck in DOS, then fix the feisty meow variable name.
+#if [ ! -z "$IS_DOS" ]; then
+#  FEISTY_MEOW_APEX="$(cmd /c chdir | tr A-Z a-z | sed -e 's/\\/\//g')"
+#echo feisty meow dos is: $FEISTY_MEOW_APEX
+#  FEISTY_MEOW_APEX="$(dos_to_unix_path "$FEISTY_MEOW_APEX")"
+#echo new feisty meow fixed dir is: $FEISTY_MEOW_APEX
+#fi
 
 popd &>/dev/null
 
index 7c552cbe65c3148fdf8285f72f9f9ccd60066132..b64b167098c89726696ab05b7edb3ebeddf51ea4 100644 (file)
@@ -290,8 +290,8 @@ fi
 
 ##############
 
-# pull in the custom overrides for feisty_meow scripts.  this is done last,
-# because we want to set everything up as expected, then let the user
+# pull in the custom overrides for feisty_meow scripts.  this is done almost
+# last, because we want to set everything up as expected, then let the user
 # override individual variables and definitions.  we also don't guard this
 # to avoid running it again, because we don't know what mix of functions and
 # aliases they want to define in there.
@@ -306,3 +306,37 @@ for i in $FEISTY_MEOW_LOADING_DOCK/custom/*.sh; do
   source "$i"
 done
 
+##############
+
+# a late breaking action is to set the editor, if we can.
+# we will fallback to whatever we can find on the host.
+export EDITOR
+if [ ! -z "$DISPLAY" ]; then
+  # only try to add bluefish, a gui editor, if there is an X display for it.
+  if [ -z "$EDITOR" ]; then
+    EDITOR="$(which bluefish)"
+  fi
+fi
+if [ -z "$EDITOR" ]; then
+  EDITOR="$(which gvim)"
+  if [ ! -z "$EDITOR" ]; then
+    # if we found gvim, then add in the no forking flag.
+    EDITOR+=" --nofork"
+  fi
+fi
+if [ -z "$EDITOR" ]; then
+  EDITOR="$(which vim)"
+fi
+if [ -z "$EDITOR" ]; then
+  EDITOR="$(which vi)"
+fi
+##
+# out of ideas about editors at this point.
+##
+# set the VISUAL variable from EDITOR if we found an editor to use.
+if [ ! -z "$EDITOR" ]; then
+  VISUAL="$EDITOR"
+fi
+
+##############
+
index ba0d8dff1fff4d8e37edc929d5612911eb4c72b5..403a0f7c7b11c05bec8a3b6b37b4b0efd0a7b183 100644 (file)
@@ -4,9 +4,8 @@ function print_instructions()
 {
   echo -e "\n$(basename $0 .sh):\n"
 
-#hmmm: extract to a terminal size function.
-# calculate the number of columsn in the terminal.
-cols=$(stty size | awk '{print $2}')
+  # calculate the number of columsn in the terminal.
+  local cols=$(get_maxcols)
 
   echo -e 'this script takes two parameters, a "here" folder and a "there" folder, almost as if it were a copy command.  but instead, this removes any file from under the "here" location if it cannot be found in the "there" location.  so the "there" location is considered a more definitive template of what should be in "here", such that we strip out what "there" does not have.\n\n
 the most" useful way to use this script is for a "here" hierarchy that is a copy of an older version of another "there" hierarchy.  the "there" hierarchy may have changed a lot, including new files, changed files, and deleted files.  it is a simple operation to copy everything from "there" into "here" (such as by using the command [ cp -R "$there"/* "$here" ] ) , but it is a lot harder to determine what stuff in "here" is out of date and should be removed.  that is where this script comes in; it can be run to flush out any older things in "here", rather than requiring the user to manually find all those files.  ' | splitter --maxcol $(($cols - 1))
diff --git a/scripts/rev_control/puffer.sh b/scripts/rev_control/puffer.sh
new file mode 100644 (file)
index 0000000..c02ef3b
--- /dev/null
@@ -0,0 +1,32 @@
+#!/bin/bash
+
+# puffer: "puffs out" all of the folders present in the REPOSITORY_LIST
+# variable, which causes the repo to be merged with its remote versions.
+# this enables a clean check-in; after puffer runs, there will be no secret
+# upstream changes that could mess up the git push (svn and cvs are not
+# supported in this script, since they branch differently than git).
+
+source "$FEISTY_MEOW_SCRIPTS/core/launch_feisty_meow.sh"
+source "$FEISTY_MEOW_SCRIPTS/rev_control/version_control.sh"
+
+##############
+
+echo "puffing out repositories at: $(date)"
+echo
+
+FULL_LIST=" $(dirname $FEISTY_MEOW_APEX) $HOME "
+if [ "$OS" == "Windows_NT" ]; then
+  FULL_LIST+=" c:/ d:/ e:/ "
+fi
+
+puff_out_list $FULL_LIST
+test_or_die "puffing out list: $FULL_LIST"
+
+##############
+
+# regenerate the scripts after puffing out, since this could mean a modified version
+# of feisty meow is present.
+regenerate
+
+##############
+
index 08a9a07c1f0880bb33b07e1eb7cac00fd56f8ad8..75f344cf66ab8485f0e8b980f6b83c8eef924859 100644 (file)
@@ -15,12 +15,10 @@ source "$FEISTY_MEOW_SCRIPTS/tty/terminal_titler.sh"
 export MAX_DEPTH=5
 
 # use our splitter tool for lengthy output if it's available.
-if [ ! -z "$(which splitter)" ]; then
+if [ ! -z "$(which splitter 2>/dev/null)" ]; then
   TO_SPLITTER="$(which splitter)"
-
-#hmmm: another reusable chunk here, getting terminal size.
   # calculate the number of columsn in the terminal.
-  cols=$(stty size | awk '{print $2}')
+  cols=$(get_maxcols)
   TO_SPLITTER+=" --maxcol $(($cols - 1))"
 else
   TO_SPLITTER=cat
@@ -98,8 +96,19 @@ function do_checkin()
       # see if there are any changes in the local repository.
       if ! git diff-index --quiet HEAD --; then
         # tell git about all the files and get a check-in comment.
+#hmmm: begins to look like, you guessed it, a reusable bit that all commit actions could enjoy.
         git commit .
-        test_or_die "git commit"
+        retval=$?
+        test_or_continue "git commit"
+        if [ $retval -ne 0 ]; then
+          echo -e -n "Commit failed or was aborted:\nShould we continue with other check-ins? [y/N] "
+          local line
+          read line
+          if [[ "${line:0:1}" != "y" ]]; then
+            echo "Stopping check-in process due to missing commit and user request."
+            exit 1
+          fi
+        fi
       fi
 
       # a new set of steps we have to take to make sure the branch integrity is good.
@@ -109,7 +118,7 @@ function do_checkin()
       # there could already be committed changes that haven't been pushed yet.
 
       # upload any changes to the upstream repo so others can see them.
-      git push origin "$(my_branch_name)" 2>&1 | grep -v "X11 forwarding request failed" | $TO_SPLITTER
+      git push --tags origin "$(my_branch_name)" 2>&1 | grep -v "X11 forwarding request failed" | $TO_SPLITTER
       promote_pipe_return 0
       test_or_die "git push"
 
@@ -181,7 +190,7 @@ function do_report_new
   return 0
 }
 
-# checks in all the folders in a specified list.
+# checks in all the folders in the specified list.
 function checkin_list()
 {
   # make the list of directories unique.
@@ -199,7 +208,7 @@ function checkin_list()
     if [[ $outer =~ /.* ]]; then
       # yep, this path is absolute.  just handle it directly.
       if [ ! -d "$outer" ]; then continue; fi
-      do_checkin $outer
+      do_checkin "$outer"
       test_or_die "running check-in (absolute) on path: $outer"
       sep 28
     else
@@ -207,7 +216,7 @@ function checkin_list()
         # add in the directory component to see if we can find the folder.
         local path="$inner/$outer"
         if [ ! -d "$path" ]; then continue; fi
-        do_checkin $path
+        do_checkin "$path"
         test_or_die "running check-in (relative) on path: $path"
         sep 28
       done
@@ -217,24 +226,62 @@ function checkin_list()
   restore_terminal_title
 }
 
-# takes out the first few carriage returns that are in the input.
-function squash_first_few_crs()
+# does a careful git update on all the folders in the specified list.
+function puff_out_list()
 {
-  i=0
-  while read input_text; do
-    i=$((i+1))
-    if [ $i -le 5 ]; then
-      echo -n "$input_text  "
+  # make the list of directories unique.
+  local list="$(uniquify $*)"
+
+  save_terminal_title
+
+  # turn repo list back into an array.
+  eval "repository_list=( ${REPOSITORY_LIST[*]} )"
+
+  local outer inner
+
+#hmmm: once again, seeing some reusable code in this loop...
+  for outer in "${repository_list[@]}"; do
+    # check the repository first, since it might be an absolute path.
+    if [[ $outer =~ /.* ]]; then
+      # yep, this path is absolute.  just handle it directly.
+      if [ ! -d "$outer" ]; then continue; fi
+      do_careful_git_update "$outer"
+      test_or_die "running puff-out (absolute) on path: $outer"
+      sep 28
     else
-      echo $input_text
+      for inner in $list; do
+        # add in the directory component to see if we can find the folder.
+        local path="$inner/$outer"
+        if [ ! -d "$path" ]; then continue; fi
+        do_careful_git_update "$path"
+        test_or_die "running puff-out (relative) on path: $path"
+        sep 28
+      done
     fi
   done
-  if [ $i -le 3 ]; then
-    # if we're still squashing eols, make sure we don't leave them hanging.
-    echo
-  fi
+
+  restore_terminal_title
 }
 
+#hmmm: to go below.
+### takes out the first few carriage returns that are in the input.
+##function squash_first_few_crs()
+##{
+  ##i=0
+  ##while read input_text; do
+    ##i=$((i+1))
+    ##if [ $i -le 5 ]; then
+      ##echo -n "$input_text  "
+    ##else
+      ##echo $input_text
+    ##fi
+  ##done
+  ##if [ $i -le 3 ]; then
+    ### if we're still squashing eols, make sure we don't leave them hanging.
+    ##echo
+  ##fi
+##}
+
 #hmmm: the below are git specific and should be named that way.
 
 function all_branch_names()
@@ -277,19 +324,34 @@ function check_branch_state()
   local remote_branch=$(git rev-parse "$branch")
   local merge_base=$(git merge-base @ "$branch")
 
+  local to_echo=
   if [ "$local_branch" == "$remote_branch" ]; then
-    echo "okay"
+    to_echo="okay"
   elif [ "$local_branch" == "$merge_base" ]; then
-    echo "needs_pull"
+    to_echo="needs_pull"
   elif [ "$remote_branch" == "$merge_base" ]; then
-    echo "needs_push"
+    to_echo="needs_push"
   else
-    echo "diverged"
+    to_echo="diverged"
   fi
 
+  echo -n "$to_echo"
+
   return $to_return
 }
 
+# only shows the branch state if it's not okay.
+# note that this is not the same as a conditional branch (ha ha).
+function show_branch_conditionally()
+{
+  local this_branch="$1"; shift
+
+  local state=$(check_branch_state "$this_branch")
+  if [ "$state" != "okay" ]; then
+    echo "=> branch '$this_branch' state is not clean: $state"
+  fi
+}
+
 # the git update process just gets more and more complex when you bring in
 # branches, so we've moved this here to avoid having a ton of code in the
 # other methods.
@@ -307,16 +369,14 @@ function do_careful_git_update()
 
   local this_branch="$(my_branch_name)"
 
-  state=$(check_branch_state "$this_branch")
-  echo "=> branch '$this_branch' state prior to remote update is: $state"
+  show_branch_conditionally "$this_branch"
 
   # first update all our remote branches to their current state from the repos.
   git remote update | $TO_SPLITTER
   promote_pipe_return 0
   test_or_die "git remote update"
 
-  state=$(check_branch_state "$this_branch")
-  echo "=> branch '$this_branch' state after remote update is: $state"
+  show_branch_conditionally "$this_branch"
 
   # this code is now doing what i have to do when i repair the repo.  and it seems to be good so far.
   local branch_list=$(all_branch_names)
@@ -327,16 +387,13 @@ function do_careful_git_update()
     promote_pipe_return 0
     test_or_die "git switching checkout to remote branch: $bran"
 
-    state=$(check_branch_state "$bran")
-    echo "=> branch '$bran' state is: $state"
+    show_branch_conditionally "$this_branch"
 
     remote_branch_info=$(git ls-remote --heads origin $bran 2>/dev/null)
     if [ ! -z "$remote_branch_info" ]; then
       # we are pretty sure the remote branch does exist.
       git pull --no-ff origin "$bran" | $TO_SPLITTER
       promote_pipe_return 0
-
-      echo "=> branch '$bran' state after pull is: $state"
     fi
     test_or_die "git pull of remote branch: $bran"
   done
index f759494973e4b686b50d8d61807810e89869dfef..2ae511cbb8eb50148b9efd2841ee0bddf088463e 100644 (file)
@@ -18,18 +18,18 @@ source "$WORKDIR/shared_site_mgr.sh"
 
 sep
 
-check_application_dir "$APPLICATION_DIR"
+check_application_dir "$BASE_APPLICATION_PATH"
 
 # find proper webroot where the site will be initialized.
 if [ -z "$app_dirname" ]; then
   # no dir was passed, so guess it.
-  find_app_folder "$APPLICATION_DIR"
+  find_app_folder "$BASE_APPLICATION_PATH"
 else
-  test_app_folder "$APPLICATION_DIR" "$app_dirname"
+  test_app_folder "$BASE_APPLICATION_PATH" "$app_dirname"
 fi
 
 # where we expect to find our checkout folder underneath.
-full_app_dir="$APPLICATION_DIR/$app_dirname"
+full_app_dir="$BASE_APPLICATION_PATH/$app_dirname"
 
 # simplistic approach here; just go to the folder and pull the changes.
 
index a7b9e90982241e8b1433fb38c6d3d29ae35e7f95..0235eb5074a7334736f64b69d51b648cf40fc988 100644 (file)
 
 # basic information that is constant for all site avenger sites.
 
-export APPLICATION_DIR="$HOME/apps"
+# the top level of the user's application storage.
+export BASE_APPLICATION_PATH="$HOME/apps"
+# where the code should come from.
 export DEFAULT_REPOSITORY_ROOT="git@github.com:kwentworth"
+# we checkout the git repository to a directory underneath the
+# app storage directory named this:
 export CHECKOUT_DIR_NAME="avenger5"
+# the subfolder that the web browser will look for the site in,
+# underneath the application's specific path.
+export STORAGE_SUFFIX="/public"
+
+####
+
+# constants within our cakelampvm machine.
+
+# in our scheme, the single IP address that all our domains map to.
+export IP_ADDRESS="10.28.42.20"
+# the email address (where first dot is replaced by @) for the administrator of the domain.
+export SERVER_ADMIN="developer.cakelampvm.com"
+# the name of the name server for the new domains (should already be configured).
+export MAIN_NAME_SERVER="ns.cakelampvm.com"
+# the name of the mail server for a new domain (should already be configured).
+export MAIL_SERVER="mail.cakelampvm.com"
+# the distribution name to be listed in info for the new domain or subdomain.
+export DISTRO="ubuntu"
 
 ####
 
index c67bd25a689cf5f7613727c6bdc076165049ac3d..c2ed28d5afaa15f9bb87d552dab1dccab7567528 100644 (file)
@@ -63,18 +63,18 @@ source "$WORKDIR/shared_site_mgr.sh"
 
 sep
 
-check_application_dir "$APPLICATION_DIR"
+check_application_dir "$BASE_APPLICATION_PATH"
 
 # find proper webroot where the site will be initialized.
 if [ -z "$app_dirname" ]; then
   # no dir was passed, so guess it.
-  find_app_folder "$APPLICATION_DIR"
+  find_app_folder "$BASE_APPLICATION_PATH"
 else
-  test_app_folder "$APPLICATION_DIR" "$app_dirname"
+  test_app_folder "$BASE_APPLICATION_PATH" "$app_dirname"
 fi
 
 # where we expect to find our checkout folder underneath.
-full_app_dir="$APPLICATION_DIR/$app_dirname"
+full_app_dir="$BASE_APPLICATION_PATH/$app_dirname"
 
 # use our default values for the repository and theme if they're not provided.
 if [ -z "$repo_name" ]; then
@@ -110,8 +110,8 @@ sep
 #if [ ! -z "$user_name" ]; then
 #  echo "Chowning the apps folder to be owned by: $user_name"
 ##hmmm: have to hope for now for standard group named after user 
-#  chown -R "$user_name:$user_name" "$APPLICATION_DIR"
-#  test_or_die "Chowning $APPLICATION_DIR to be owned by $user_name"
+#  chown -R "$user_name:$user_name" "$BASE_APPLICATION_PATH"
+#  test_or_die "Chowning $BASE_APPLICATION_PATH to be owned by $user_name"
 #fi
 
 sep
index 8e87c652917e7b959276c08643bcc7e081724642..d024cd10922782e072c9570d7dbb535fc4e66a77 100644 (file)
@@ -14,29 +14,89 @@ fi
 export WORKDIR="$( \cd "$(\dirname "$0")" && \pwd )"  # obtain the script's working directory.
 export FEISTY_MEOW_APEX="$( \cd "$WORKDIR/../.." && \pwd )"
 
+export NO_HELLO=right
 source "$FEISTY_MEOW_APEX/scripts/core/launch_feisty_meow.sh"
 
 ##############
 
 # fix up the main web storage.
 chown -R www-data:www-data /var/www 
+test_or_die "chown www-data"
 group_perm /var/www 
+test_or_die "group_perm www-data"
 
 ##############
 
 # set up access on some important folders for the developer user.
 chown -R developer:developer /home/developer
+test_or_die "chown developer home"
 harsh_perm /home/developer/.ssh
-chown -R developer:developer /opt/feistymeow.org 
-group_perm /opt/feistymeow.org 
+test_or_die "harsh_perm setting on developer .ssh"
 chown -R developer:developer /etc/apache2 /etc/bind 
+test_or_die "chown apache2 and bind to developer"
 group_perm /etc/apache2 /etc/bind 
+test_or_die "group perms on apache2 and bind"
 
 ##############
 
 # fix perms for fred user.
 chown -R fred:fred /home/fred /home/archives/stuffing 
+test_or_die "chown fred home"
 harsh_perm /home/fred/.ssh
+test_or_die "harsh_perm setting on fred .ssh"
+chown -R fred:fred /opt/feistymeow.org 
+test_or_die "chown feisty meow to fred"
+group_perm /opt/feistymeow.org 
+test_or_die "group perms on feisty meow"
+
+##############
+#
+# some slightly tricky bits start here.  we want to massage the vm into the
+# best possible shape without needing to re-release it.
+#
+##############
+
+# only update hello if they've still got the file there.  we don't want to
+# keep forcing our hellos at people.
+if [ -f "$HOME/hello.txt" ]; then
+  # copy the most recent hello file into place for the user.
+  \cp -f "$FEISTY_MEOW_APEX/production/sites/cakelampvm.com/hello.txt" "$HOME"
+  test_or_continue "copying hello file for user"
+fi
+
+##############
+
+# install a better editor app.
+
+echo "
+The script is about to install the bluefish editor and some dependencies.
+If the app is not already installed, then this process takes only about a
+minute on a slower home DSL internet connection...
+
+"
+
+apt-get install -y bluefish &> "/tmp/install_bluefish-$(logname).log"
+test_or_continue "failed to install bluefish editor.  not good."
 
 ##############
 
+
+#hmmm: todo
+# deploy the site updater here to fix the local cakelampvm.com site...
+
+
+##############
+
+# sequel--tell them they're great and show the hello again also.
+
+regenerate
+
+echo "
+
+
+Thanks for revamping your cakelampvm.  :-)
+"
+
+##############
+
+
index 56e658e2d633201244ee88fdca034b8fa7680ccc..3f73b3a8f033a615bbad7c7d85ec436192b7f97f 100644 (file)
@@ -44,7 +44,7 @@ function locate_config_file()
   local app_dirname="$1"; shift
 
   local configfile="$WORKDIR/config/${app_dirname}.app"
-echo hoping config file would be: $configfile
+  echo "config file?: $configfile"
   if [ ! -f "$configfile" ]; then
     # this is not a good config file.  we can't auto-guess the config.
     echo -e "
index 79156ea2ac76bb02e1ea1c28e2c0748321a4d552..1336a891917b6871f3bb6f3150311203dcf597af 100644 (file)
@@ -19,18 +19,18 @@ source "$WORKDIR/shared_site_mgr.sh"
 
 sep
 
-check_application_dir "$APPLICATION_DIR"
+check_application_dir "$BASE_APPLICATION_PATH"
 
 # find proper webroot where the site will be initialized.
 if [ -z "$app_dirname" ]; then
   # no dir was passed, so guess it.
-  find_app_folder "$APPLICATION_DIR"
+  find_app_folder "$BASE_APPLICATION_PATH"
 else
-  test_app_folder "$APPLICATION_DIR" "$app_dirname"
+  test_app_folder "$BASE_APPLICATION_PATH" "$app_dirname"
 fi
 
 # where we expect to find our checkout folder underneath.
-full_app_dir="$APPLICATION_DIR/$app_dirname"
+full_app_dir="$BASE_APPLICATION_PATH/$app_dirname"
 
 # use our default values for the repository and theme if they're not provided.
 if [ -z "$repo_name" ]; then
index a63744be2ee02355999c770401ca0c74a3932ded..282d0cdc060f93a7d25746a89f32f43b12c078c6 100644 (file)
@@ -19,18 +19,18 @@ source "$WORKDIR/shared_site_mgr.sh"
 
 sep
 
-check_application_dir "$APPLICATION_DIR"
+check_application_dir "$BASE_APPLICATION_PATH"
 
 # find proper webroot where the site will be initialized.
 if [ -z "$app_dirname" ]; then
   # no dir was passed, so guess it.
-  find_app_folder "$APPLICATION_DIR"
+  find_app_folder "$BASE_APPLICATION_PATH"
 else
-  test_app_folder "$APPLICATION_DIR" "$app_dirname"
+  test_app_folder "$BASE_APPLICATION_PATH" "$app_dirname"
 fi
 
 # where we expect to find our checkout folder underneath.
-full_app_dir="$APPLICATION_DIR/$app_dirname"
+full_app_dir="$BASE_APPLICATION_PATH/$app_dirname"
 
 # use our default values for the repository and theme if they're not provided.
 if [ -z "$repo_name" ]; then
index ae64d2b877b60d9daf7d86c1aa904f5678e24f3e..c31a33d8390e0e11487dec439146a08e780d3cdb 100644 (file)
@@ -47,24 +47,22 @@ elif [ -z "$app_dirname" ]; then
   print_instructions
 fi
 
-#we will require sudo later.
-#if [[ $EUID != 0 ]]; then
-#  echo "This script must be run as root or sudo."
-#  exit 1
-#fi
+# force the sudo at the start of the script, rather than waiting halfway
+# through to ask for access.
+sudo bash -c 'echo sudo permissions acquired.'
 
 source "$WORKDIR/shared_site_mgr.sh"
 
 sep
 
-check_application_dir "$APPLICATION_DIR"
+check_application_dir "$BASE_APPLICATION_PATH"
 
 # find proper webroot where the site will be initialized.
 if [ -z "$app_dirname" ]; then
   # no dir was passed, so guess it.
-  find_app_folder "$APPLICATION_DIR"
+  find_app_folder "$BASE_APPLICATION_PATH"
 else
-  test_app_folder "$APPLICATION_DIR" "$app_dirname"
+  test_app_folder "$BASE_APPLICATION_PATH" "$app_dirname"
 fi
 
 #echo "!! domain being added is: $DOMAIN_NAME"
index b72aa8e0fff5a5e5d1ed7a356262de225604dbb1..3e194083baa03488ab4705dd7edd9b30619b82a0 100644 (file)
@@ -1,12 +1,78 @@
 #!/bin/bash
 
-echo "sorry--this script is not implemented yet."
+# this performs the inverse operation of standup, by relying on the
+# remove_domain and remove_apache_site scripts.
+#
+# Author: Chris Koeritz
 
+export WORKDIR="$( \cd "$(\dirname "$0")" && \pwd )"  # obtain the script's working directory.
+export FEISTY_MEOW_APEX="$( \cd "$WORKDIR/../.." && \pwd )"
 
+source "$FEISTY_MEOW_APEX/scripts/core/launch_feisty_meow.sh"
 
-# need the inverse of add_domain and add_apache_site.
-# can use the same machinery as standup, just need to invoke these two new removal methods.
+############################
 
-# the decommissioning of the app is a question though.  we don't want to delete it, i'm pretty sure.
-# so how about that part is to do nothing?
+function print_instructions()
+{
+  echo
+  echo "$(basename $0 .sh) {app name}"
+  echo
+  echo "
+$(basename $0 .sh) will drop a web site out of apache server and out of the
+DNS server, as if it never existed.  The site storage is left untouched; we
+don't know what valuable assets lurk there.
+This script must be run as sudo or root; it makes changes to system files.
+"
+  exit 0
+}
+
+############################
+
+# main body of script.
+
+# check for parameters.
+app_dirname="$1"; shift
+
+if [ "$app_dirname" == "-help" -o "$app_dirname" == "--help" ]; then
+  print_instructions
+elif [ -z "$app_dirname" ]; then
+  print_instructions
+fi
+
+# force the sudo at the start of the script, rather than waiting halfway
+# through to ask for access.
+sudo bash -c 'echo sudo permissions acquired.'
+
+source "$WORKDIR/shared_site_mgr.sh"
+
+sep
+
+check_application_dir "$BASE_APPLICATION_PATH"
+
+# find proper webroot where the site will be initialized.
+if [ -z "$app_dirname" ]; then
+  # no dir was passed, so guess it.
+  find_app_folder "$BASE_APPLICATION_PATH"
+else
+  test_app_folder "$BASE_APPLICATION_PATH" "$app_dirname"
+fi
+
+sep
+
+sudo bash "$FEISTY_MEOW_SCRIPTS/system/remove_apache_site.sh" "$DOMAIN_NAME"
+test_or_die "dropping apache site for: $DOMAIN_NAME"
+
+sep
+
+#echo "!! domain being removed is: $DOMAIN_NAME"
+
+sudo bash "$FEISTY_MEOW_SCRIPTS/system/remove_domain.sh" "$DOMAIN_NAME"
+test_or_die "dropping domain: $DOMAIN_NAME"
+
+sep
+
+echo "
+Finished tearing down the domain name and apache site for:
+  $DOMAIN_NAME
+"
 
index 57b5d519be85af004ec2f17bcae60036bc25a2c8..a9a379fd748ad3a5901e8176471ed1d108685181 100644 (file)
@@ -7,128 +7,16 @@ export WORKDIR="$( \cd "$(\dirname "$0")" && /bin/pwd )"  # obtain the script's
 export FEISTY_MEOW_APEX="$( \cd "$WORKDIR/../.." && \pwd )"
 
 source "$FEISTY_MEOW_APEX/scripts/core/launch_feisty_meow.sh"
+source "$FEISTY_MEOW_SCRIPTS/system/common_sysadmin.sh"
 
 # some convenient defaults for our current usage.
 
-BASE_PATH="$HOME/apps"
-STORAGE_SUFFIX="/public"
-
-# this function writes out the new configuration file for the site.
-function write_apache_config()
-{
-  local appname="$1"; shift
-  local sitename="$1"; shift
-  local site_path="$1"; shift
-
-  local site_config="/etc/apache2/sites-available/${sitename}.conf"
-
-  # check if config file already exists and bail if so.
-  if [ -f "$site_config" ]; then
-    echo "The apache configuration file already exists at:"
-    echo "  $site_config"
-    echo "Since apache configuration files can get very complex, we do not want to"
-    echo "assume that this file is removable.  Calling the site addition done."
-    exit 0
-  fi
-
-  echo "Creating a new apache2 site for $sitename with config file:"
-  echo "  $site_config"
-
-  # if no path, then we default to our standard app storage location.  otherwise, we
-  # put the site where they told us to.
-  if [ -z "$site_path" ]; then
-    # path where site gets checked out, in some arcane manner, and which happens to be
-    # above the path where we put webroot (in the storage suffix, if defined).
-    local path_above="${BASE_PATH}/${appname}"
-    # no slash between appname and suffix, in case suffix is empty.
-    local full_path="${path_above}${STORAGE_SUFFIX}"
-#echo really full path is $full_path
-  else
-    # we'll go with their specification for the site storage.
-    local full_path="$site_path"
-  fi
-
-  echo "
-# set up the user's web folder as an apache user web directory.
-
-# set permissions on the actual app folder.
-<Directory \"$full_path\">
-  Options +ExecCGI +Indexes +FollowSymLinks +Includes +MultiViews 
-  Require all granted
-</Directory>
-
-<VirtualHost *:80>
-    ServerName ${sitename}
-    DocumentRoot ${full_path}
-    ErrorLog \${APACHE_LOG_DIR}/${sitename}-error.log
-    CustomLog \${APACHE_LOG_DIR}/${sitename}-access.log combined
-    Include /etc/apache2/conf-library/basic-options.conf
-    Include /etc/apache2/conf-library/rewrite-enabling.conf
-</VirtualHost>
-" >"$site_config" 
-
-  chown "$(logname):$(logname)" "$site_config"
-  test_or_die "setting ownership on: $site_config"
-}
-
-# turns on the config file we create above for apache.
-function enable_site()
-{
-  local sitename="$1"; shift
-  local site_config="/etc/apache2/sites-available/${sitename}.conf"
-
-  outfile="$TMP/apacheout.$RANDOM"
-  a2ensite "$(basename $site_config)" &>$outfile
-  if [ $? -ne 0 ]; then
-    # an error happened, so we show the command's output at least.
-    cat $outfile
-    echo
-    echo "There was a problem enabling the apache config file in:"
-    echo "  $site_config"
-    echo "Please consult the apache error logs for more details."
-    exit 1
-  fi
-  \rm "$outfile"
-}
-
-# restarts the apache2 service.
-function restart_apache()
-{
-  service apache2 restart
-  if [ $? -ne 0 ]; then
-    echo "There was a problem restarting the apache2 service."
-    echo "Please consult the apache error logs for more details."
-    exit 1
-  fi
-}
-
-# sets up the serverpilot storage location for a user hosted web site.
-function maybe_create_site_storage()
-{
-  local our_app="$1"; shift
-  # make sure the base path for storage of all the apps for this user exists.
-  local full_path="$BASE_PATH/$our_app"
-  if [ ! -d "$full_path" ]; then
-    mkdir -p $full_path
-    test_or_die "The app storage path could not be created.\n  Path in question is: $full_path"
-  fi
-
-  # now give the web server some access to the folder.  this is crucial since the folders
-  # can be hosted in any user folder, and the group permissions will not necessarily be correct already.
-  local chow_path="$full_path"
-  # only the first chmod is recursive; the rest just apply to the specific folder of interest.
-  chmod -R g+rx "$chow_path"
-  # walk backwards up the path and fix perms.
-  while [[ $chow_path != $HOME ]]; do
-echo chow path is now $chow_path
-    chmod g+rx "$chow_path"
-    test_or_die "Failed to add group permissions on the path: $chow_path"
-    # reassert the user's ownership of any directories we might have just created.
-    chown $(logname) "$chow_path"
-    test_or_die "changing ownership to user failed on the path: $chow_path"
-    chow_path="$(dirname "$chow_path")"
-  done
-}
+if [ -z "$BASE_APPLICATION_PATH" ]; then
+  BASE_APPLICATION_PATH="$HOME/apps"
+fi
+if [ -z "$STORAGE_SUFFIX" ]; then
+  STORAGE_SUFFIX="/public"
+fi
 
 # main body of script.
 
@@ -151,7 +39,7 @@ This script needs to know (1) the application name for the new site and
 appropriate name for a file-system compatible folder name.  There is an
 optional third parameter (3) the path for site storage.  If the site path
 is not provided, we'll use this path:
-  $BASE_PATH/{app name}/$STORAGE_SUFFIX"
+  $BASE_APPLICATION_PATH/{app name}/$STORAGE_SUFFIX"
   exit 1
 fi
 
index 80ebb2f37d0563e6d0d9b0d768d1e9b80a0797c3..e12faa7be928ddaa8cba56bc9babc60ac2d5e040 100644 (file)
@@ -1,10 +1,12 @@
 #!/bin/bash
 
-# this set of functions serve the main purpose of adding new domains or subdomains to the bind9 DNS server on the current host.
-# it is currently highly specific to running a bunch of domains on a linux VM, where the VM has one IP address.
-# note that bind 'named' must already be configured.
-# also, it is assumed that if a subdomain is being added, then the containing domain has already been configured and is 
-# configured in a file similar to "blah.com.conf" in /etc/bind.
+# this set of functions serve the main purpose of adding new domains or
+# subdomains to the bind9 DNS server on the current host.  it is currently
+# highly specific to running a bunch of domains on a linux VM, where the VM
+# has one IP address.  note that the bind 'named' must already be configured.
+# also, it is assumed that, if a subdomain is being added, then the containing
+# domain has already been configured and is configured in a file similar to
+# "blah.com.conf" in /etc/bind.
 #
 # Author: Chris Koeritz
 
@@ -12,141 +14,31 @@ export WORKDIR="$( \cd "$(\dirname "$0")" && \pwd )"  # obtain the script's work
 export FEISTY_MEOW_APEX="$( \cd "$WORKDIR/../.." && \pwd )"
 
 source "$FEISTY_MEOW_APEX/scripts/core/launch_feisty_meow.sh"
+source "$FEISTY_MEOW_SCRIPTS/system/common_sysadmin.sh"
 
 # some defaults that are convenient for current purposes.
+# existing values will be respected over our defaults.
 
-# hmmm: !!! these would need to be parameterized somehow for this script to become really general.
-
-# in our scheme, the single IP address that all our domains map to.
-IP_ADDRESS="10.28.42.20"
-# the email address (where first dot is replaced by @) for the administrator of the domain.
-SERVER_ADMIN="developer.cakelampvm.com"
-# the name of the name server for the new domains (should already be configured).
-MAIN_NAME_SERVER="ns.cakelampvm.com"
-# the name of the mail server for a new domain (should already be configured).
-MAIL_SERVER="mail.cakelampvm.com"
-# the distribution name to be listed in info for the new domain or subdomain.
-DISTRO="ubuntu"
-
-# creates a totally new domain config file for DNS.
-function write_new_domain_file()
-{
-  local domain_name="$1"; shift
-
-  local domain_file="/etc/bind/${domain_name}.conf"
-
-  echo "adding a totally new domain called $domain_name"
-  echo "using the config file: $domain_file"
-
-  if [ -f $domain_file ]; then
-    echo
-    echo "The domain configuration file already exists at:"
-    echo "  $domain_file"
-    echo "Since we don't want to tear that down if it has specialized configuration"
-    echo "data in it, we will just leave it in place and consider our job done."
-    echo
-    exit 0
-  fi
-
-  echo "
-\$TTL 1W
-@      IN SOA  @       ${SERVER_ADMIN}. (
-               2017100801 ; serial
-               2H ; refresh
-               8M ; retry
-               14D ; expiry
-               6H ) ; minimum
-
-       IN NS           ${MAIN_NAME_SERVER}.
-       IN MX   10      ${MAIL_SERVER}.
-
-${domain_name}.        IN A    ${IP_ADDRESS}
-       IN HINFO        \"linux server\" \"${DISTRO}\"
-" >"$domain_file"
-
-  # our personalized configuration approach wants the real owner to own the file.
-  chown "$(logname):$(logname)" $domain_file
-  test_or_die "setting ownership on: $domain_file"
-}
-
-# hooks up a new config file into bind's list of zones.
-function add_zone_for_new_domain()
-{
-  local domain_name="$1"; shift
-
-  local domain_file="/etc/bind/${domain_name}.conf"
-
-  echo "adding a new domain configured by ${domain_file} into"
-  echo "the named.conf.local configuration file."
-
-  # append the reference to the new conf file in the zone list.
-  echo "
-zone \"${domain_name}\" in {
-       file \"${domain_file}\";
-       type master;
-       allow-query { any; };
-};
-
-////////////////////////////////////////////////////////////////////////////
-
-" >> /etc/bind/named.conf.local
-
-  # keep ownership for the real user.
-  chown "$(logname):$(logname)" /etc/bind/named.conf.local
-  test_or_die "setting ownership on: /etc/bind/named.conf.local"
-
-}
-
-# adds a new subdomain under a containing domain.
-function add_new_subdomain()
-{
-  local new_domain="$1"; shift
-
-  # split up the full domain name into subdomain portion and containing domain.
-  local subdomain="${new_domain%.*.*}"
-  local containing_domain="${new_domain#*.}"
-
-  echo "adding a subdomain $subdomain to containing domain $containing_domain"
-
-  local domain_file="/etc/bind/${containing_domain}.conf"
-  # see if config file already exists; if not, complain.
-  if [ ! -f "$domain_file" ]; then
-    echo "The domain configuration file for $new_domain is missing."
-    echo "It should already be present in: $domain_file"
-    echo "Please add the containing domain before trying to add a subdomain."
-    exit 1
-  fi
-
-  # see if subdomain already present in config.
-  if [ $(grep -q "$new_domain" "$domain_file") ]; then
-    echo "The subdomain $subdomain already seems to exist in the domain"
-    echo "configuration file: $domain_file"
-    echo "Please edit the config file to remove the subdomain before trying"
-    echo "to re-add the subdomain."
-    exit 1
-  fi
-
-  # append the new subdomain into the config file.
-  echo "
-${subdomain}.${containing_domain}.    IN A    ${IP_ADDRESS}
-        IN HINFO \"linux server\" \"${DISTRO}\"
-" >> /etc/bind/${containing_domain}.conf
-
-  # keep ownership for real user.
-  chown "$(logname):$(logname)" "/etc/bind/${containing_domain}.conf"
-  test_or_die "setting ownership on: /etc/bind/${containing_domain}.conf"
-}
-
-function restart_bind()
-{
-  echo restarting DNS server.
-  service bind9 restart
-  if [ $? -ne 0 ]; then
-    echo "The bind service did not restart properly.  Please check the error logs."
-    exit 1
-  fi
-  echo DNS server restarted.
-}
+if [ -z "$IP_ADDRESS" ]; then
+  # in our scheme, the single IP address that all our domains map to.
+  IP_ADDRESS="10.28.42.20"
+fi
+if [ -z "$SERVER_ADMIN" ]; then
+  # the email address (where first dot is replaced by @) for the administrator of the domain.
+  SERVER_ADMIN="developer.cakelampvm.com"
+fi
+if [ -z "$MAIN_NAME_SERVER" ]; then
+  # the name of the name server for the new domains (should already be configured).
+  MAIN_NAME_SERVER="ns.cakelampvm.com"
+fi
+if [ -z "$MAIL_SERVER" ]; then
+  # the name of the mail server for a new domain (should already be configured).
+  MAIL_SERVER="mail.cakelampvm.com"
+fi
+if [ -z "$DISTRO" ]; then
+  # the distribution name to be listed in info for the new domain or subdomain.
+  DISTRO="ubuntu"
+fi
 
 # main body of script.
 
diff --git a/scripts/system/common_sysadmin.sh b/scripts/system/common_sysadmin.sh
new file mode 100644 (file)
index 0000000..223067f
--- /dev/null
@@ -0,0 +1,352 @@
+#!/bin/bash
+
+# this is a library of functions shared by scripts in the system folder.
+#
+# Author: Chris Koeritz
+
+############################################################################
+
+# bind9 methods...
+
+# removes a full domain from the DNS.
+function remove_domain_file()
+{
+  local domain_name="$1"; shift
+
+  local domain_file="/etc/bind/${domain_name}.conf"
+  if [ -f "$domain_file" ]; then
+    # don't destroy, just shuffle.
+    \mv -f "$domain_file" "/tmp/$(basename ${domain_file})-old-${RANDOM}"
+    test_or_die "removing domain file: $domain_file"
+  else
+    echo "Did not see a domain file to remove: $domain_file"
+  fi
+}
+
+# creates a totally new domain config file for DNS.
+function write_new_domain_file()
+{
+  local domain_name="$1"; shift
+
+  local domain_file="/etc/bind/${domain_name}.conf"
+
+  echo "adding a totally new domain called $domain_name"
+  echo "using the config file: $domain_file"
+
+  if [ -f $domain_file ]; then
+    echo
+    echo "The domain configuration file already exists at:"
+    echo "  $domain_file"
+    echo "Since we don't want to tear that down if it has specialized configuration"
+    echo "data in it, we will just leave it in place and consider our job done."
+    echo
+    exit 0
+  fi
+
+  echo "
+\$TTL 1W
+@      IN SOA  @       ${SERVER_ADMIN}. (
+               2017100801 ; serial
+               2H ; refresh
+               8M ; retry
+               14D ; expiry
+               6H ) ; minimum
+
+       IN NS           ${MAIN_NAME_SERVER}.
+       IN MX   10      ${MAIL_SERVER}.
+
+${domain_name}.        IN A    ${IP_ADDRESS}
+       IN HINFO        \"linux server\" \"${DISTRO}\"
+" >"$domain_file"
+
+  # our personalized configuration approach wants the real owner to own the file.
+  chown "$(logname):$(logname)" $domain_file
+  test_or_die "setting ownership on: $domain_file"
+}
+
+# takes a zone back out of the local conf file for bind
+function remove_zone_for_domain()
+{
+  local domain_name="$1"; shift
+
+  local domain_file="/etc/bind/${domain_name}.conf"
+
+  # eat the zone file definition.  this will botch up badly if more text was added
+  # or the zone info shrank.
+  create_chomped_copy_of_file "/etc/bind/named.conf.local" "zone.*${domain_name}" 6
+}
+
+# hooks up a new config file into bind's list of zones.
+function add_zone_for_new_domain()
+{
+  local domain_name="$1"; shift
+
+  local domain_file="/etc/bind/${domain_name}.conf"
+
+  echo "adding a new domain configured by ${domain_file} into"
+  echo "the named.conf.local configuration file."
+
+  # append the reference to the new conf file in the zone list.
+  echo "
+zone \"${domain_name}\" in {
+       file \"${domain_file}\";
+       type master;
+       allow-query { any; };
+};
+
+////////////////////////////////////////////////////////////////////////////
+
+" >> /etc/bind/named.conf.local
+
+  # keep ownership for the real user.
+  chown "$(logname):$(logname)" /etc/bind/named.conf.local
+  test_or_die "setting ownership on: /etc/bind/named.conf.local"
+}
+
+# zaps a subdomain out of the containing domain file.
+function remove_subdomain()
+{
+  local old_domain="$1"; shift
+
+  # split up the full domain name into subdomain portion and containing domain.
+  local subdomain="${old_domain%.*.*}"
+  local containing_domain="${old_domain#*.}"
+
+  echo "removing subdomain $subdomain from containing domain $containing_domain"
+
+  local domain_file="/etc/bind/${containing_domain}.conf"
+  # see if config file already exists; if not, complain.
+  if [ ! -f "$domain_file" ]; then
+    echo "The domain configuration file for $old_domain is missing."
+    echo "It should already be present in: $domain_file"
+    echo "We cannot remove a subdomain if the containing domain isn't there."
+    exit 1
+  fi
+
+  # see if subdomain already present in config.
+  if ! grep -q "$old_domain" "$domain_file"; then
+    echo "The subdomain $subdomain is already missing from the domain"
+    echo "configuration file: $domain_file"
+    echo "Our work is apparently done for removing it."
+    return 0
+  fi
+
+  create_chomped_copy_of_file "$domain_file" "${old_domain}" 2
+}
+
+# adds a new subdomain under a containing domain.
+function add_new_subdomain()
+{
+  local new_domain="$1"; shift
+
+  # split up the full domain name into subdomain portion and containing domain.
+  local subdomain="${new_domain%.*.*}"
+  local containing_domain="${new_domain#*.}"
+
+  echo "adding a subdomain $subdomain to containing domain $containing_domain"
+
+  local domain_file="/etc/bind/${containing_domain}.conf"
+  # see if config file already exists; if not, complain.
+  if [ ! -f "$domain_file" ]; then
+    echo "The domain configuration file for $new_domain is missing."
+    echo "It should already be present in: $domain_file"
+    echo "Please add the containing domain before trying to add a subdomain."
+    exit 1
+  fi
+
+  # see if subdomain already present in config.
+  if grep -q "$new_domain" "$domain_file"; then
+    echo "The subdomain $subdomain already seems to exist in the domain"
+    echo "configuration file: $domain_file"
+    echo "We are considering our work done; if you want to modify the subdomain,"
+    echo "then please call remove_domain on it first."
+    return 0
+  fi
+
+  # append the new subdomain into the config file.
+  echo "${subdomain}.${containing_domain}.    IN A    ${IP_ADDRESS}
+        IN HINFO \"linux server\" \"${DISTRO}\"
+" >> /etc/bind/${containing_domain}.conf
+
+  # keep ownership for real user.
+  chown "$(logname):$(logname)" "/etc/bind/${containing_domain}.conf"
+  test_or_die "setting ownership on: /etc/bind/${containing_domain}.conf"
+}
+
+function restart_bind()
+{
+  echo restarting DNS server.
+  service bind9 restart
+  if [ $? -ne 0 ]; then
+    echo "The bind service did not restart properly.  Please check the error logs."
+    exit 1
+  fi
+  echo DNS server restarted.
+}
+
+############################################################################
+
+# apache2 methods...
+
+# removes a config file for apache given the app name and site name.
+function remove_apache_config()
+{
+  local sitename="$1"; shift
+
+  local site_config="/etc/apache2/sites-available/${sitename}.conf"
+
+  if [ -f "$site_config" ]; then
+    # don't destroy, just shuffle.
+    \mv -f "$site_config" "/tmp/$(basename ${site_config})-old-${RANDOM}"
+    test_or_die "removing site config: $site_config"
+  else
+    echo "Did not see a site config to remove: $site_config"
+  fi
+}
+
+# this function writes out the new configuration file for the site.
+function write_apache_config()
+{
+  local appname="$1"; shift
+  local sitename="$1"; shift
+  local site_path="$1"; shift
+
+  local site_config="/etc/apache2/sites-available/${sitename}.conf"
+
+  # check if config file already exists and bail if so.
+  if [ -f "$site_config" ]; then
+    echo "The apache configuration file already exists at:"
+    echo "  $site_config"
+    echo "Since apache configuration files can get very complex, we do not want to"
+    echo "assume that this file is removable.  Calling the site addition done."
+    exit 0
+  fi
+
+  echo "Creating a new apache2 site for $sitename with config file:"
+  echo "  $site_config"
+
+  # if no path, then we default to our standard app storage location.  otherwise, we
+  # put the site where they told us to.
+  if [ -z "$site_path" ]; then
+    # path where site gets checked out, in some arcane manner, and which happens to be
+    # above the path where we put webroot (in the storage suffix, if defined).
+    local path_above="${BASE_APPLICATION_PATH}/${appname}"
+    # no slash between appname and suffix, in case suffix is empty.
+    local full_path="${path_above}${STORAGE_SUFFIX}"
+#echo really full path is $full_path
+  else
+    # we'll go with their specification for the site storage.
+    local full_path="$site_path"
+  fi
+
+  echo "
+# set up the user's web folder as an apache user web directory.
+
+# set permissions on the actual app folder.
+<Directory \"$full_path\">
+  Options +ExecCGI +Indexes +FollowSymLinks +Includes +MultiViews 
+  Require all granted
+</Directory>
+
+<VirtualHost *:80>
+    ServerName ${sitename}
+    DocumentRoot ${full_path}
+    ErrorLog \${APACHE_LOG_DIR}/${sitename}-error.log
+    CustomLog \${APACHE_LOG_DIR}/${sitename}-access.log combined
+    Include /etc/apache2/conf-library/basic-options.conf
+    Include /etc/apache2/conf-library/rewrite-enabling.conf
+</VirtualHost>
+" >"$site_config" 
+
+  chown "$(logname):$(logname)" "$site_config"
+  test_or_die "setting ownership on: $site_config"
+}
+
+# stops apache from serving up the site.
+function disable_site()
+{
+  local sitename="$1"; shift
+  local site_config="/etc/apache2/sites-available/${sitename}.conf"
+
+  if [ ! -f "$site_config" ]; then
+    echo "The site config did not exist and could not be disabled: $site_config"
+    return 0
+  fi
+
+#hmmm: repeated pattern of hidden output file, very useful.  abstract it...
+  local outfile="$TMP/apacheout.$RANDOM"
+  a2dissite "$(basename $site_config)" &>$outfile
+  if [ $? -ne 0 ]; then
+    # an error happened, so we show the command's output at least.
+    cat $outfile
+    echo
+    echo "There was a problem disabling the apache config file in:"
+    echo "  $site_config"
+    echo "Please consult the apache error logs for more details."
+    exit 1
+  fi
+  \rm "$outfile"
+}
+
+# turns on the config file we create above for apache.
+function enable_site()
+{
+  local sitename="$1"; shift
+  local site_config="/etc/apache2/sites-available/${sitename}.conf"
+
+  local outfile="$TMP/apacheout.$RANDOM"
+  a2ensite "$(basename $site_config)" &>$outfile
+  if [ $? -ne 0 ]; then
+    # an error happened, so we show the command's output at least.
+    cat $outfile
+    echo
+    echo "There was a problem enabling the apache config file in:"
+    echo "  $site_config"
+    echo "Please consult the apache error logs for more details."
+    exit 1
+  fi
+  \rm "$outfile"
+}
+
+# restarts the apache2 service.
+function restart_apache()
+{
+  service apache2 restart
+  if [ $? -ne 0 ]; then
+    echo "There was a problem restarting the apache2 service."
+    echo "Please consult the apache error logs for more details."
+    exit 1
+  fi
+}
+
+# sets up the serverpilot storage location for a user hosted web site.
+function maybe_create_site_storage()
+{
+  local our_app="$1"; shift
+  # make sure the path for storage this app exists for the user.
+  local full_path="$BASE_APPLICATION_PATH/$our_app"
+  if [ ! -d "$full_path" ]; then
+    mkdir -p $full_path
+    test_or_die "The app storage path could not be created.\n  Path in question is: $full_path"
+  fi
+
+  # now give the web server some access to the folder.  this is crucial since the folders
+  # can be hosted in any user folder, and the group permissions will not necessarily be correct already.
+  local chow_path="$full_path"
+  # only the first chmod is recursive; the rest just apply to the specific folder of interest.
+  chmod -R g+rx "$chow_path"
+  # walk backwards up the path and fix perms.
+  while [[ $chow_path != $HOME ]]; do
+#echo chow path is now $chow_path
+    chmod g+rx "$chow_path"
+    test_or_die "Failed to add group permissions on the path: $chow_path"
+    # reassert the user's ownership of any directories we might have just created.
+    chown $(logname) "$chow_path"
+    test_or_die "changing ownership to user failed on the path: $chow_path"
+    chow_path="$(dirname "$chow_path")"
+  done
+}
+
+############################################################################
+
+
diff --git a/scripts/system/remove_apache_site.sh b/scripts/system/remove_apache_site.sh
new file mode 100644 (file)
index 0000000..ef6c8b6
--- /dev/null
@@ -0,0 +1,44 @@
+#!/bin/bash
+
+# uninstalls the apache website for a specified domain.
+
+# auto-find the scripts, since we might want to run this as sudo.
+export WORKDIR="$( \cd "$(\dirname "$0")" && /bin/pwd )"  # obtain the script's working directory.
+export FEISTY_MEOW_APEX="$( \cd "$WORKDIR/../.." && \pwd )"
+
+source "$FEISTY_MEOW_APEX/scripts/core/launch_feisty_meow.sh"
+source "$FEISTY_MEOW_SCRIPTS/system/common_sysadmin.sh"
+
+# some convenient defaults for our current usage.
+
+if [ -z "$BASE_APPLICATION_PATH" ]; then
+  BASE_APPLICATION_PATH="$HOME/apps"
+fi
+if [ -z "$STORAGE_SUFFIX" ]; then
+  STORAGE_SUFFIX="/public"
+fi
+
+# main body of script.
+
+if [[ $EUID != 0 ]]; then
+  echo "This script must be run as root or sudo."
+  exit 1
+fi
+
+site="$1"; shift
+
+if [ -z "$site" ]; then
+#hmmm: move to a print_instructions function.
+  echo "
+$(basename $0): {dns name} 
+
+This script needs to know (1) the DNS name for the apache virtual host.
+The script will uninstall that site's configuration files for apache2.
+"
+  exit 1
+fi
+
+disable_site "$site"
+remove_apache_config "$site"
+restart_apache
+
diff --git a/scripts/system/remove_domain.sh b/scripts/system/remove_domain.sh
new file mode 100644 (file)
index 0000000..f734878
--- /dev/null
@@ -0,0 +1,66 @@
+#!/bin/bash
+
+# performs the inverse function of add_domain by deconfiguring a domain
+# in bind.  the domain needs to have been set up by add_domain, or this will
+# not succeed.
+#
+# Author: Chris Koeritz
+
+export WORKDIR="$( \cd "$(\dirname "$0")" && \pwd )"  # obtain the script's working directory.
+export FEISTY_MEOW_APEX="$( \cd "$WORKDIR/../.." && \pwd )"
+
+source "$FEISTY_MEOW_APEX/scripts/core/launch_feisty_meow.sh"
+source "$FEISTY_MEOW_SCRIPTS/system/common_sysadmin.sh"
+
+# some defaults that are convenient for current purposes.
+# existing values will be respected over our defaults.
+
+#if [ -z "$IP_ADDRESS" ]; then
+#  # in our scheme, the single IP address that all our domains map to.
+#  IP_ADDRESS="10.28.42.20"
+#fi
+#if [ -z "$SERVER_ADMIN" ]; then
+#  # the email address (where first dot is replaced by @) for the administrator of the domain.
+#  SERVER_ADMIN="developer.cakelampvm.com"
+#fi
+#if [ -z "$MAIN_NAME_SERVER" ]; then
+#  # the name of the name server for the new domains (should already be configured).
+#  MAIN_NAME_SERVER="ns.cakelampvm.com"
+#fi
+#if [ -z "$MAIL_SERVER" ]; then
+#  # the name of the mail server for a new domain (should already be configured).
+#  MAIL_SERVER="mail.cakelampvm.com"
+#fi
+#if [ -z "$DISTRO" ]; then
+#  # the distribution name to be listed in info for the new domain or subdomain.
+#  DISTRO="ubuntu"
+#fi
+
+# main body of script.
+
+if [[ $EUID != 0 ]]; then
+  echo "This script must be run as root or sudo."
+  exit 1
+fi
+
+old_domain="$1"; shift
+
+if [ -z "$old_domain" ]; then
+  echo "This script needs a domain name to remove from DNS." 
+  exit 1
+fi
+
+# if domain name has three or more components, then remove a subdomain.
+# otherwise, remove a full domain.
+if [[ $old_domain == *"."*"."* ]]; then
+  # remove a subdomain from the containing domain.
+  remove_subdomain "$old_domain"
+  restart_bind
+else
+  # remove the full domain in DNS.
+  remove_domain_file "$old_domain"
+  remove_zone_for_domain "$old_domain"
+  restart_bind
+fi
+
+
index 8bc6ccd36d3c31b1145b3df0c1c401d58414beba..0203b9ae7fd771bbacbc80b1c2d2319e34462e3f 100644 (file)
@@ -17,7 +17,8 @@ function apply_title_to_terminal()
     title="$(hostname)"
   fi
   
-  if [ "${TERM}" != "dumb" -a -z "$PBS_ENVIRONMENT" -a ! -z "$PS1" ]; then
+  if [ "${TERM}" != "dumb" -a -z "$PBS_ENVIRONMENT" -a \
+        ! -z "$PS1" -a "${TERM}" != "linux" ]; then
     echo -n -e "\033]0;${title}\007"
   else
     # not running interactively, so just echo the title.
@@ -31,28 +32,46 @@ function apply_title_to_terminal()
 function set_terminal_title()
 {
   apply_title_to_terminal $*
+
+#tricky tries to get it to be available when we ask for it in get_terminal_title
+  sync
+#  echo -n
+
+#  # we're enforcing a new title from here on.
+#  unset PRIOR_TERMINAL_TITLE
   save_terminal_title
 }
 
-# reads the current terminal title, if possible, and saves it to our record.
-function save_terminal_title()
+# echoes back the current title on the terminal window, if we can acquire it.
+function get_terminal_title()
 {
+  # this is an important value now; it is checked for in save_terminal_title.
+  local term_title_found="unknown"
   # save the former terminal title if we're running in X with xterm.
   which xprop &>/dev/null
   if [ $? -eq 0 ]; then
     # make sure we're actually using xterm *and* that we have a window ID.
     if [[ "$TERM" =~ .*"xterm".* && ! -z "$WINDOWID" ]]; then
-      local prior_title="$(xprop -id $WINDOWID | perl -nle 'print $1 if /^WM_NAME.+= \"(.*)\"$/')"
-      if [ ! -z "$prior_title" ]; then
-        if [ ! -z "$DEBUG_TERM_TITLE" ]; then
-          echo "saving prior terminal title as '$prior_title'"
-        fi
-        export PRIOR_TERMINAL_TITLE="$prior_title"
-      else
-        if [ ! -z "$DEBUG_TERM_TITLE" ]; then
-          echo "not saving prior terminal title which was empty"
-        fi
-      fi
+      term_title_found="$(xprop -id $WINDOWID | perl -nle 'print $1 if /^WM_NAME.+= \"(.*)\"$/')"
+    fi
+  fi
+  echo -n "$term_title_found"
+}
+
+# reads the current terminal title, if possible, and saves it to our record.
+function save_terminal_title()
+{
+  local title="$(get_terminal_title)"
+  if [ "$title" != "unknown" ]; then
+    # there was a title, so save it.
+    if [ ! -z "$DEBUG_TERM_TITLE" ]; then
+      echo "saving prior terminal title as '$title'"
+    fi
+    export PRIOR_TERMINAL_TITLE="$title"
+  else
+    # the terminal had no title, or we couldn't access it, or there's no terminal.
+    if [ ! -z "$DEBUG_TERM_TITLE" ]; then
+      echo "not saving prior terminal title which was empty"
     fi
   fi
 }
@@ -83,7 +102,7 @@ function label_terminal_with_info()
     fi
     pruned_host=$(echo $HOSTNAME | sed -e 's/^\([^\.]*\)\..*$/\1/')
     date_string=$(date +"%Y %b %e @ %T")
-    user=$USER
+    user=$(logname)
     if [ -z "$user" ]; then
       # try snagging the windoze name.
       user=$USERNAME
index 9c98033fe7031b946f892ffe9f9ead4a995980fd..275bbde0bb51d77b7639dbda413290158d12f082 100644 (file)
@@ -54,4 +54,20 @@ if you need to jam a bunch of videos together, crossfade them, and that kind of
 then try openshot.  it also supports simple saving with different quality levels, which
 lets you drop a bunch of size at the expense of the picture.  sometimes needed.
 
+---------------------------
+music collection management
+---------------------------
+
+dupeguru:
+found this to be pretty helpful in finding my duplicate songs.  it is able to analyze
+when the files are the same, even if their names don't match.
+check: did it even compare by metadata?  not remembering currently.
+
+fdupes:
+another useful tool for finding duplicate tracks in a music collection, but relies on exact
+matches in the names(?).  was not so powerful as dupeguru, but still useful.
+
+
+
+