Merge branch 'dev' of feistymeow.org:feisty_meow into dev
authorFred T. Hamster <fred@gruntose.com>
Thu, 23 Nov 2017 06:35:17 +0000 (01:35 -0500)
committerFred T. Hamster <fred@gruntose.com>
Thu, 23 Nov 2017 06:35:17 +0000 (01:35 -0500)
45 files changed:
documentation/cygwin_install_list.txt
documentation/feisty_meow_command_reference.txt [new file with mode: 0644]
infobase/fortunes.dat
nucleus/library/versions/version_ini.cpp
nucleus/library/versions/version_ini.h
nucleus/tools/clam_tools/version_stamper.cpp
nucleus/tools/clam_tools/vsts_version_fixer.cpp
nucleus/tools/clam_tools/write_build_config.cpp
production/feisty_meow_config.ini
production/sites/cakelampvm.com/.gitignore [new file with mode: 0644]
production/sites/cakelampvm.com/docs/manual/cakelampvm_guide_v002.html
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/rolling/default_page.001/001-default-http.conf [new file with mode: 0644]
production/sites/cakelampvm.com/rolling/default_page.001/001-default-ssl.conf [new file with mode: 0644]
readme.txt
scripts/agenda/info_overload_report.sh
scripts/archival/snarf_feisty_meow.pl
scripts/clam/cpp/rules.def
scripts/clam/cpp/variables.def
scripts/core/common.alias
scripts/core/functions.sh
scripts/core/launch_feisty_meow.sh
scripts/core/reconfigure_feisty_meow.sh
scripts/core/variables.sh
scripts/opensim/opensim_utils.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/clean_mapsdemo.sh [new file with mode: 0644]
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

index acd8d7775c871f938d4a091a34bbb927dc43123a..0d881b6175209f60236d7f09bf01844d1a2da7b8 100644 (file)
@@ -1,65 +1,69 @@
-\r
-recommended packages to install in cygwin's setup\r
-if you're going to use cygwin on windows:\r
-\r
-first, keep all the standard packages that cygwin will enable.\r
-second, add these packages to get the full recommended set...\r
-\r
-===========\r
-\r
-using apt-cyg (https://github.com/transcode-open/apt-cyg),\r
-this is the only command needed:\r
-\r
-apt-cyg install bc crypt emacs email expect gcc-g++ git gitk gvim inetutils \\r
-  libcrypt-devel libcurl-devel libgnutls-devel make mutt ncftp openssh \\r
-  openssl-devel perl python subversion time unzip util-linux vim xinit \\r
-  xterm zip\r
-\r
-\r
-===========\r
-\r
-list broken out by category:\r
-\r
-shells:\r
-  python\r
-  perl\r
-\r
-network:\r
-  ncftp\r
-  openssh\r
-\r
-editors:\r
-  vim\r
-  gvim\r
-  emacs\r
-\r
-revision control:\r
-  git\r
-  gitk\r
-  subversion\r
-\r
-general tools:\r
-  bc\r
-  expect\r
-  util-linux\r
-  inetutils\r
-  email\r
-  mutt\r
-  unzip\r
-  zip\r
-  crypt\r
-  time\r
-\r
-libraries:\r
-  libcurl-devel\r
-  libgnutls-devel \r
-  openssl-devel\r
-\r
-build tools:\r
-  gcc4\r
-  make\r
-\r
-x window support:\r
-  xterm\r
-  xinit\r
-\r
+
+The cygwin setup app is available at: http://cygwin.com
+
+The default packages selected by Cygwin are a good starting point for running
+Feisty Meow on windows.  If you supplement this set with a few additional
+packages, you can rely on the apt-cyg tool rather than having to run the
+Cygwin setup program (which can be a little fiddly).  Here is a step by step
+process to getting going with apt-cyg:
+
+1) Install Cygwin.
+
+Run the Cygwin setup exe from their website.  Keep all the default packages
+that the installer suggests, but add the following additional ones (the
+easiest way to add additional packages is to switch to the "Full" view for the
+package list and then search for the terms below):
+
+  + lynx
+  + wget
+  + subversion
+
+2) Install apt-cyg.
+
+The apt-cyg program brings the convenience of the Debian and Ubuntu installer
+application (apt) to Cygwin.  This program does require a couple of additional
+setup steps.
+
+This material is drawn from the apt-cyg home page:
+  https://github.com/transcode-open/apt-cyg
+
+Start the cygwin bash prompt (there should be a desktop icon or start menu
+icon for it called something like "cygwin64") and run the following
+commands (omitting the '#' in front):
+
+  # lynx -source rawgit.com/transcode-open/apt-cyg/master/apt-cyg > apt-cyg
+  # install apt-cyg /bin
+
+3) Install the basic set of cygwin apps and libraries.
+
+These tools are not necessarily needed by the Feisty Meow scripts, but they
+are all required to create a sane and useful Unix or Linux environment on
+MS-Windows.  You may find you will want additional packages installed in the
+future, and you can use this same approach.
+
+In the cygwin bash prompt, type this command:
+
+# apt-cyg install bc crypt cygutils emacs email expect gcc-g++ git gitk \
+  gvim inetutils less lynx make mutt ncftp openssh perl procps python \
+  sharutils shutdown subversion time unzip util-linux vim wget xinit \
+  xterm zip
+
+You may have cleverly spotted that we repeated some package names that were
+already installed using the Cygwin setup program in step 1.  That is fine and
+should just fetch the latest versions.
+
+Later, to update the apt-cyg package datebase to the latest available on the
+internet, you can run this command:
+
+# apt-cyg update
+
+We don't currently know of an analog in apt-cyg of the "apt dist-upgrade"
+command, which fetches all updated versions of the installed packages.  We
+think the install command above will upgrade packages when there are new ones
+available.  Also, the cygwin setup tool (bless its heart) may automatically
+update packages if you run it again (you don't need to select anything again,
+but just run through the install process to get the latest).
+
+==> hmmm: verify above claims.
+
+
diff --git a/documentation/feisty_meow_command_reference.txt b/documentation/feisty_meow_command_reference.txt
new file mode 100644 (file)
index 0000000..75371b1
--- /dev/null
@@ -0,0 +1,199 @@
+
+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 (that is a
+"documentation fail" on our part; 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
+
+generally useful commands
+=========================
+
+  pwd:
+  reports similarly to the good old system "pwd", but translates the $HOME
+  variable into the '~' name.  e.g., if you're fred in /home/fred/turnips
+  and you run 'pwd', then it will print: ~/turnips
+
+  i:
+  take inventory.  prints out some time and relative dimension in space
+  information and shows the current directory's contents.
+
+  dir or l (lower-case L):
+  show the directory with a "summing" feature that calculates the full size
+  consumed by all files in the listing, with somewhat esthetic output.
+
+  ls:
+  the standard ls command (not the summing directory), but with ls colors
+  enabled.
+
+  del or rm:
+  invoke "safedel" feature to remove the files specified.  this archives the
+  deleted files in "$TMP/zz_safedel_keep" and writes a report of the deletion
+  history in "$TMP/zz_safedel_report.txt".
+
+note: currently there is no "empty the trash" function aside from running a
+command such as:
+  # \rm -rf $TMP/zz_safedel*
+the backslash forces bash to run the "rm" tool from the path rather than
+using the feisty meow alias.  a trash flushing feature is planned for the
+somewhat near future.
+
+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 for
+the powerup command, but is not ready yet.)
+  
+  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: satis-refresh is the one site avenger command that hasn't been "feisty meowicized" yet.
+
+lower level scripts used by site avenger scripts
+------------------------------------------------
+
+  add_domain and remove_domain: (from system script collection)
+  adds (or removes) a DNS domain to the bind9 configuration.  the domain
+  tools, are very sensitive to any edits within the chunks of code they have
+  written.  when it comes time to remove the domain again, the script will eat
+  the number of lines it expects to find after the beginning of the domain
+  definition that it added.  to avoid any issues, if you need to edit the bind
+  config files, be sure to do it way above or way below the auto-generated
+  domain chunks.
+
+  add_apache_site and remove_apache_site: (from system script collection)
+  creates (or removes) an apache compatible site definition.  this will rely
+  on the site's domain previously having been added to the DNS.
+
+note: currently we only implement the http site, but we're planning to add https support via self-signed certificates soon.
+
+
index 1f462d80b874fe56a077245b02073bdd8f2508a6..32f238a0d6e647be2608f36af39af467a2fef668 100644 (file)
@@ -43025,4 +43025,29 @@ you see he gets so high,
 mind expanded to sky;
 real world grokking just not ready.
   -- fred t. hamster
+~
+    Generally speaking, when we are too desirous of something in life, we’re
+less likely to attain it.  Success seems to increase in direct proportion to
+the diminution of our desires.  The same logic applies to our need for
+recognition.  We might want to be appreciated and respected, but we have only
+a limited ability to influence how other people respond and we can’t make
+somebody show us gratitude any more than we can force someone to love us.  If
+we show love without expecting it to be reciprocated, we will have more chance
+of finding love than if we simply yearn for it.  Likewise, doing something
+without expecting gratitude is more likely to elicit appreciation for what we
+do.  Whether someone can acknowledge our actions or not should be no concern
+of ours.
+    We simply commit ourselves to doing things to the best of our ability and
+in as thorough a manner as possible without sloppiness.  We should never think
+that other people are indebted to us or obligated to help us in return.  We
+should simply do things because we love doing them, not because we want other
+people to feel indebted to us.  Shantideva says:
+
+        The work of bringing benefit to beings
+        Will not, then, make me proud and self-admiring.
+        The happiness of others is itself my satisfaction;
+        I do not expect another recompense.
+
+  -- Traleg Kyabgon, from "The Practice of Lojong: Cultivating Compassion
+     through Training the Mind", published by Shambhala Publications
 
index 6a5519e14e1c73fd0666f5b936cbee5ef51a8b9c..e9fc8b23afb5a02e22aa770f348bf291c03d1dcc 100644 (file)
@@ -269,8 +269,9 @@ END\n\
 #define REPLACE(tag, replacement) \
   new_version_entry.replace_all(tag, replacement); \
 
-bool version_ini::write_rc(const version_record &to_write)
+bool version_ini::write_rc(const astring &header_store, const version_record &to_write)
 {
+  FUNCDEF("write_rc");
   astring new_version_entry(version_rc_template);
 
   // $file_ver -> w, x, y, z for version of the file.
@@ -320,7 +321,7 @@ bool version_ini::write_rc(const version_record &to_write)
   astring root_part = "/";
   root_part += _ini->load(VERSION_SECTION, ROOT, "");
 
-  astring rc_filename(astring(_path_name->dirname()) + root_part
+  astring rc_filename(header_store + "/" + root_part
       + "_version.rc");
 
   filename(rc_filename).chmod(filename::ALLOW_BOTH, filename::USER_RIGHTS);
@@ -374,8 +375,9 @@ const astring version_header_template = "\
 
 //////////////
 
-bool version_ini::write_code(const version_record &to_write)
+bool version_ini::write_code(const astring &header_store, const version_record &to_write)
 {
+  FUNCDEF("write_code");
   astring root_part = _ini->load(VERSION_SECTION, ROOT, "");
   astring root = root_part.upper();  // make upper case for naming sake.
   astring name = _ini->load(VERSION_SECTION, NAME, "");
@@ -409,7 +411,7 @@ bool version_ini::write_code(const version_record &to_write)
   // $web_address -> the web site for the company.  not actually stored.
   REPLACE("$web_address", to_write.web_address);
 
-  astring header_filename(_path_name->dirname().raw() + "/" + root_part
+  astring header_filename(header_store + "/" + root_part
       + astring("_version.h"));
 
   filename(header_filename).chmod(filename::ALLOW_BOTH, filename::USER_RIGHTS);
@@ -447,12 +449,16 @@ bool replace_version_entry(astring &full_string, const astring &look_for,
   return to_return;
 }
 
-bool version_ini::write_assembly(const version_record &to_write,
+bool version_ini::write_assembly(const astring &header_store, const version_record &to_write,
     bool do_logging)
 {
   FUNCDEF("write_assembly");
-  filename just_dir = _path_name->dirname();
-//LOG(astring("dir is set to: ") + just_dir);
+  filename just_dir = filename(header_store);
+         //_path_name->dirname();
+
+//hmmm: make condit on debug
+LOG(astring("dir is set to: ") + just_dir);
+
   directory dir(just_dir);
   filename to_patch;
 //LOG(astring("dir has: ") + dir.files().text_form());
@@ -530,8 +536,9 @@ bool version_ini::write_assembly(const version_record &to_write,
 }
 
 bool version_ini::one_stop_version_stamp(const astring &path,
-    const astring &source_version, bool do_logging)
+    const astring &header_store, const astring &source_version, bool do_logging)
 {
+  FUNCDEF("one_stop_version_stamp");
   astring path_name = path;
   if (path_name.equal_to("."))
     path_name = application_configuration::current_directory();
@@ -569,24 +576,26 @@ bool version_ini::one_stop_version_stamp(const astring &path,
   version_ini verini(path_name);
   verini.set_record(source.get_record(), false);
 
-//  LOG(a_sprintf("The file \"%s\" contains this version information:",
-//      path_name.s()));
-//  LOG(verini.get_record().text_form());
+//put this in debug brackets
+  LOG(a_sprintf("The file \"%s\" contains this version information:",
+      path_name.s()));
+  LOG(verini.get_record().text_form());
+//...debug to here
 
-  if (!verini.write_rc(verini.get_record())) {
+  if (!verini.write_rc(header_store, verini.get_record())) {
     critical_events::alert_message(a_sprintf("Could not write the RC file in \"%s\".",
         filename(path_name).basename().raw().s()));
     return false;
   }
 
-  if (verini.library() && !verini.write_code(verini.get_record())) {
+  if (verini.library() && !verini.write_code(header_store, verini.get_record())) {
     critical_events::alert_message(astring("Could not write the C++ header file for "
         "the directory \"")
         + filename(path_name).basename() + astring("\".\n"));
     return false;
   }
 
-  if (!verini.write_assembly(verini.get_record(), do_logging)) {
+  if (!verini.write_assembly(header_store, verini.get_record(), do_logging)) {
     critical_events::alert_message(astring("Could not write the Assembly info file for "
         "the directory \"")
         + filename(path_name).basename() + astring("\".\n"));
index 6838adee20a151e072d7099b3eccb857c60f374c..0fe559d20762165a7ed852b067b0c5238d659ca3 100644 (file)
@@ -80,18 +80,18 @@ public:
   bool ole_auto_registering();
     //!< returns true if this version file specifies ole auto registration.
 
-  bool write_rc(const structures::version_record &to_write);
+  bool write_rc(const basis::astring &header_store, const structures::version_record &to_write);
     //!< writes out the file 'X_version.rc' for the X library or application.
     /*!< the contents will include the information specified in "to_write",
     where X is the library name from that record. */
 
-  bool write_code(const structures::version_record &to_write);
+  bool write_code(const basis::astring &header_store, const structures::version_record &to_write);
     //!< writes out the header ('X_version.h') with the version information.
     /*!< this file is needed for other libraries or application to know this
     project's version number.  the users can make sure that the header info
     agrees with the actual version seen on the file. */
 
-  bool write_assembly(const structures::version_record &to_write, bool do_logging);
+  bool write_assembly(const basis::astring &header_store, const structures::version_record &to_write, bool do_logging);
     //!< fixes any assemblies with the info in "to_write".
 
   static bool executable(const basis::astring &path_name);
@@ -103,9 +103,11 @@ public:
     //!< specialized version ignores cache and gets version directly from file.
 
   static bool one_stop_version_stamp(const basis::astring &path,
-          const basis::astring &source_version, bool do_logging);
+          const basis::astring &header_store, const basis::astring &source_version,
+          bool do_logging);
     //!< performs version stamping using the ini file in "path".
-    /*!< "source_version" supplies the name of the main version file where
+    /*!< the created version files will be written to the "header_store".
+    "source_version" supplies the name of the main version file where
     we retrieve the current version numbers.  if that is not specified, then
     only the version header and RC file are created.  if "do_logging" is
     true, then version info will be sent to the program-wide logger. */
index ea90cd41822b2291551384c88783548ea92d5eb1..3313906d82f90b50343b49cfa44413f470ab49a0 100644 (file)
@@ -67,24 +67,27 @@ int version_stamper::execute()
 {
   FUNCDEF("execute");
   SETUP_CONSOLE_LOGGER;  // override the file_logger from app_shell.
-  if (application::_global_argc < 2) {
-    log(astring("The directory where the 'version.ini' file is located\n"
-        "must be specified as the first parameter of this program.  Another\n"
-        "version file may optionally be specified as the second parameter of\n"
-        "the program; the version contained in this file will be used to set\n"
-        "the version of the file specified in the first parameter.\n"
-        "Additionally, if the environment variable 'DEBUG' exists, then the\n"
-        "generated RC file will be marked as a debug build.  Otherwise it is\n"
-        "marked as a release build.  Note that the CLAM system automatically\n"
-        "sets this for you.\n\n"), ALWAYS_PRINT);
+  if (application::_global_argc < 3) {
+    log(astring(
+"The directory where the 'version.ini' file is located must be specified as the\n"
+"first parameter of this program.  The second parameter must provide the\n"
+"storage location where the version header will be written.  Another version\n"
+"file may optionally be specified as the third parameter of the program; the\n"
+"version contained in this file will be used to set the version of the file\n"
+"specified in the first parameter.\n"
+"Additionally, if the environment variable 'DEBUG' exists, then the\n"
+"generated RC file will be marked as a debug build.  Otherwise it is\n"
+"marked as a release build.  Note that the CLAM system automatically\n"
+"sets this for you.\n\n"), ALWAYS_PRINT);
     return 1;
   }
 
   astring path_name = application::_global_argv[1];
+  astring storage_name = application::_global_argv[2];
   astring source_version_file;  // blank by default.
-  if (application::_global_argc > 2)
-    source_version_file = application::_global_argv[2];
-  bool ret = version_ini::one_stop_version_stamp(path_name, source_version_file, true);
+  if (application::_global_argc > 3)
+    source_version_file = application::_global_argv[3];
+  bool ret = version_ini::one_stop_version_stamp(path_name, storage_name, source_version_file, true);
   if (!ret) return 1;  // failure.
   return 0;  // success.
 }
index bef8fcbafaf727d3b5f0b215280e4c54f8181722..3beb8779ee2310e125a71ef2eff21a010aa6577e 100644 (file)
@@ -219,8 +219,13 @@ bool stamping_spider(const directory &current)
     if (current_file.ends("version.ini")
         && !current_file.iequals("core_version.ini") ) {
 //LOG(astring("found ver file: ") + current.path() + "/" + current_file);
+//
+      astring versions_directory = environment::get("FEISTY_MEOW_GENERATED_STORE");
+     // we keep our version files one level below the top of the generated store.
+      versions_directory += "/versions";
+
       version_ini::one_stop_version_stamp(current.path() + "/" + current_file,
-          global_build_ini, true);
+          versions_directory, global_build_ini, true);
     }
   }
   return true;
index 4afed778ac91223bc51ba5b1d1047a20bda871d4..6eb06ad2ef3015e79806e98348a065e1eda09a0b 100644 (file)
@@ -206,12 +206,15 @@ if (read < 1) LOG("why is existing header contentless?");
   if (write_header) {
     // we actually want to blast out a new file.
     byte_filer build_header(filename, "wb");
-    if (!build_header.good())
-      non_continuable_error(static_class_name(), func, astring("failed to create "
+    if (!build_header.good()) {
+      continuable_error(static_class_name(), func, astring("could not create "
           "build header file in ") + build_header.name());
-    build_header.write(new_contents);
-    LOG(astring(static_class_name()) + ": wrote config to "
-        + build_header.name());
+      return false;
+    } else {
+      build_header.write(new_contents);
+      LOG(astring(static_class_name()) + ": wrote config to "
+          + build_header.name());
+    }
   } else {
     // nothing has changed.
 //    LOG(astring(static_class_name()) + ": config already up to date in "
@@ -245,8 +248,10 @@ int write_build_config::execute()
   // find our storage area for the build headers.  we know a couple build
   // configurations by now, but this should really be coming out of a config
   // file instead.
-  astring library_directory = repodir + "/nucleus/library";
-  if (!filename(library_directory).good()) {
+  astring versions_directory = environment::get("FEISTY_MEOW_GENERATED_STORE");
+  // we keep our version files one level below the top of the generated store.
+  versions_directory += "/versions";
+  if (!filename(versions_directory).good()) {
     non_continuable_error(static_class_name(), func,
         astring("failed to locate the library folder storing the generated files."));
   }
@@ -254,10 +259,8 @@ int write_build_config::execute()
   // these are very specific paths, but they really are where we expect to
   // see the headers.
 
-  astring cfg_header_filename = library_directory + "/"
-      "__build_configuration.h";
-  astring ver_header_filename = library_directory + "/"
-      "__build_version.h";
+  astring cfg_header_filename = versions_directory + "/__build_configuration.h";
+  astring ver_header_filename = versions_directory + "/__build_version.h";
 
   // open the ini file for reading.
   byte_filer ini(fname, "r");
@@ -380,6 +383,17 @@ int write_build_config::execute()
     LOG(astring("failed writing output file ") + ver_header_filename);
   }
 
+//NO, not any more  // now make a copy into the library folder, for when we release a production version.
+//  astring library_directory = repodir + "/nucleus/library";
+//  astring cfg_header_copy = library_directory + "/__build_configuration.h";
+//  astring ver_header_copy = library_directory + "/__build_version.h";
+//  if (!write_output_file(cfg_header_copy, cfg_accumulator)) {
+//    LOG(astring("skipping copy due to read-only issue on output file: ") + cfg_header_copy);
+//  }
+//  if (!write_output_file(ver_header_copy, ver_accumulator)) {
+//    LOG(astring("skipping copy due to read-only issue on output file: ") + ver_header_copy);
+//  }
+
   return 0;
 }
 
index 8e74eaac095ce53353605b888f84319a28bec05b..a8dca7cadaee7a9db4487ec6b9b85611a5317be1 100644 (file)
@@ -3,7 +3,7 @@
 # specifies the version of the code that is being constructed here.
 major=2
 minor=140
-revision=99
+revision=105
 build=420
 
 # specifies the remainder of the version record info.
diff --git a/production/sites/cakelampvm.com/.gitignore b/production/sites/cakelampvm.com/.gitignore
new file mode 100644 (file)
index 0000000..114c763
--- /dev/null
@@ -0,0 +1 @@
+google*.html
index 610b4564f960ec684afd245a279c08a7bdffbbbc..883179042f6431ac276a20a3cbaad29c29cfa8e7 100644 (file)
@@ -7,39 +7,68 @@
   <body>
     <h1 style="text-align: center;">The cakelampvm VM:<br>
       Configuration and Usage</h1>
-    <h2 style=" text-align: center;">By Chris Koeritz</h2>
+    <div style="text-align: center;"><span style="font-weight: bold;">By Chris
+        Koeritz</span><span style="font-family: Comic Sans MS;"></span><span style="font-family: Comic Sans MS;"></span><br>
+      <span style="font-family: Comic Sans MS;">feisty meow® concerns ltd</span>.</div>
     <h3 style="   text-align: center;"> Vintage: cakelampvm v002 &nbsp;&nbsp;
-      Updated: 2017-11-10</h3>
-    <h6> </h6>
-    <h2>Basic info for the guest VM</h2>
+      Updated: 2017-11-20 (rev. g)</h3>
+    <p>The cakelampvm project provides a Virtualbox VM that acts as an "internet
+      in a bottle", serving up your web sites securely and only to your local
+      host.&nbsp; The virtual machine provides DNS services (<a target="_blank"
+        title="dns server" href="http://www.bind9.net/">bind9</a>), a Web server
+      (<a target="_blank" title="patchy" href="https://httpd.apache.org/">Apache2</a>),
+      a full <a target="_blank" title="ubuntu means compassion and humanity" href="https://www.ubuntu.com/">Ubuntu</a>
+      <a target="_blank" title="it's pronounced leenoox" href="https://www.linuxfoundation.org/">Linux</a>
+      desktop environment, the <a target="_blank" title="flux is change" href="http://fluxbox.org/">Fluxbox</a>
+      <a target="_blank" title="x11 -- best windowing system" href="https://www.x.org/">X
+        window manager</a>, and a suite of tools called the <a target="_blank"
+        title="feisty meow® concerns ltd. website" href="https://feistymeow.org/">Feisty
+        Meow® codebase</a> .&nbsp; Together, these services provide you with a
+      very flexible and powerful testbed for web development, especially suited
+      for <a target="_blank" title="it's cake" href="https://cakephp.org/">CakePHP</a>.&nbsp;
+      This VM was built with the assistance of and was partially funded by <a target="_blank"
+        title="saco design" href="http://sacodesign.com">Saco Design</a> of <a
+        target="_blank" title="winterport" href="http://www.winterportmaine.gov/">Winterport,
+        Maine</a>.</p>
+    <p>Commands preceded by an octothorpe ('#') below are intended to be typed
+      into a bash shell running on the cakelampvm virtual machine.&nbsp; The
+      bash shell can be obtained either by logging into the VM through ssh or by
+      logging in directly to the Virtualbox VM console.&nbsp; You may find the
+      ssh session more convenient, because copy &amp; paste features work as
+      expected.</p>
+    <p>Commands preceded by a greater-than symbol ('&gt;') are intended to be
+      run on the Host PC in a Windows command prompt (or in a bash prompt
+      running on the Host PC).</p>
+    <h2> Guest VM Configuration<a id="#config" name="#config"></a></h2>
     <ul>
-      <li>hostname: cakelampvm.com</li>
-      <li>local IP address: 10.28.42.20</li>
-      <li>services: DNS (bind9), apache2, fluxbox X windowing system, gnome
-        display manager</li>
-      <li>main user: developer (password distributed separately)</li>
-      <li>mysql root password: (password distributed separately)</li>
+      <li>Hostname: <a target="_blank" 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 Included: DNS (bind9), apache2, fluxbox X windowing system, <a
+          target="_blank" title="not just in the garden" href="https://www.gnome.org/">gnome
+          display manager</a></li>
+      <li>Main VM User: developer (password distributed separately)</li>
+      <li>Database Access: mysql root account, password: (password distributed
+        separately)</li>
     </ul>
-    <h2>How to set up virtualbox for your host PC</h2>
+    <h2>How to set up virtualbox for your host PC<a id="#virtualbox-setup" name="#virtualbox-setup"></a></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>
+      <li>Download and install virtualbox: <a target="_blank" href="https://www.virtualbox.org/wiki/Downloads">https://www.virtualbox.org/wiki/Downloads</a></li>
+      <li>Install the extension pack for Virtualbox: This provides USB drivers
+        and other features.&nbsp; This is installed on Virtualbox itself (on the
+        Host PC), 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>Download the extension pack (also) at <a target="_blank" href="https://www.virtualbox.org/wiki/Downloads">https://www.virtualbox.org/wiki/Downloads</a></li>
+        <li>Stop any running Virtualbox VMs.</li>
+        <li>Close the Virtualbox control panel.</li>
         <li>Double-click on the downloaded extensions package (in a file
-          explorer) and virtualbox should install it.</li>
+          explorer) and Virtualbox should be launched to install it.</li>
       </ol>
-      <li>Run the virtualbox control panel.</li>
+      <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,
+      <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>
         machines.&nbsp; Before starting it, perform the following network
         configuration sections.</li>
     </ol>
-    <h3>Configure the Host-Only network on virtualbox</h3>
+    <h3>Configure the Host-Only network on Virtualbox<a id="#host-only" name="#host-only"></a></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>
+      internet or from the LAN, and can only be accessed by your host PC.&nbsp;
+      This is a key component of security for your VM and your host PC, and is
+      considered a crucial configuration step.</p>
+    <p>Note: If the host-only or NAT network exist ahead of time, Virtualbox may
+      complain about them even if they have the correct configuration.&nbsp;
+      This can be corrected simply by opening the cakelampvm settings and
+      selecting the appropriate network names again.</p>
+    <p>To configure the host-only network, follow these steps:</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.</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>
         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>
+      at: https://www.virtualbox.org/manual/ch06.html#network_nat_service</p>
+    <h3>Configure the NAT Network on Virtualbox<a id="#nat-network" name="#nat-network"></a></h3>
+    <p>The NAT (Network Address Translation) network allows the VM to get off of
+      the machine and onto the internet safely.&nbsp; It will use this interface
+      for any communication off of the host machine.&nbsp; This is another
+      crucial component for the security of the VM and your host PC.&nbsp; Since
+      the real IP address of the VM is hidden behind the NAT firewall on
+      Virtualbox, this keeps the VM safe from attackers, and hence your machine
+      stays safe as well.</p>
+    <p>To set up the NAT network, follow these steps:</p>
     <ol>
       <li> Go to virtual box "Preferences" (global preferences, not for a
         specific vm).</li>
         These are my settings, with IPv6 left disabled:<br>
         <img alt="nat net config" src="images/nat_network_config.png"></li>
     </ol>
-    <h2>Using the guest VM's DNS services</h2>
+    <h2>Starting up the VM and Connecting to It<a id="#start-vm" name="#start-vm"></a></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.&nbsp; The Linux boot sequence
+      will show many lines of text, before bringing up a black console window
+      with a login dialog.</p>
+    <p>If Windows complains about the Virtualbox application slamming into its
+      firewall, then allow the Virtualbox to get through.&nbsp; Usually, telling
+      Windows that once is enough, but if any odd network access problems
+      result, edit the Windows firewall settings and allow Virtualbox to use
+      both "Public" and "Private" networks.&nbsp; (Cortana can find the firewall
+      settings if you ask her about 'firewall'.&nbsp; Within the firewall
+      configuration dialog, look for "Allow an app or feature through..." on the
+      left and configure Virtualbox from within that list.)</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:
+    <pre># ssh developer@10.28.42.20&nbsp;  # or the equivalent with your ssh client</pre>
+    <p>And then provide the password to log in.</p>
+    <p>Once the DNS services are set up (discussed in detail below), you will be
+      able to run the much friendlier command:</p>
+    <pre># ssh developer@cakelampvm.com</pre>
+    <h4>Key Forwarding to the VM</h4>
+    <p>It is important to set up ssh key forwarding to enable your use of git
+      repositories while logged into the VM.&nbsp; Key forwarding should be
+      enabled for the VM's two fake host identities:</p>
+    <pre>cakelampvm.com<br>10.28.42.20</pre>
+    <h2>Updating cakelampvm to the Latest Model<a id="#update-vm" name="#update-vm"></a></h2>
+    <p>The cakelampvm v002 is released with the intention that it not need to be
+      released again.&nbsp; Version 001 was not built with that explicit
+      intention, which then required the release of Version 002.&nbsp; But we
+      hope to not need a v003 release...</p>
+    <p>There is an update feature built into the VM that is quite easy to
+      use.&nbsp; The updates are driven by the feisty meow script repository in
+      conjunction with a local scripted command.&nbsp; To activate the "update
+      process" for your VM, run the following commands on the VM (without the
+      initial '#' symbol):</p>
+    <pre># rpuffer $FEISTY_MEOW_APEX&nbsp;&nbsp; # updates to the latest version of feisty meow
+# revamp_cakelampvm&nbsp;&nbsp;&nbsp;        # enacts any configuration changes needed,<br>                              # plus fixes web folder and other permissions.</pre>
+    <p>These two commands can be run at any time to patch up your VM to the
+      latest.</p>
+    <p>The first command ("rpuffer ...") is also useful on its own for getting
+      the latest version of the feisty meow code.&nbsp; Run it again if there
+      are bug fixes you need for any of the scripts or if you would like the
+      most up-to-date cakelampvm documentation.</p>
+    <h2>First Tasks as the Developer User</h2>
+    <p>Here are some first steps that will make the vm your own:</p>
+    <ol>
+      <li>Change your password for the developer account.&nbsp; (This may
+        eventually be required and automatic.)&nbsp; First, log into the VM with
+        ssh.&nbsp; Then type this command:<br>
+        <pre># passwd</pre>
+        The 'passwd' command will ask for your current password, and then for a
+        new password plus a verification of that new password.<br>
+        &nbsp;</li>
+      <li>Change your git configuration for the user and email address.&nbsp;
+        This is how we've configured it so far:<br>
+        <pre># git config --global user.email "developer@cakelampvm.com"</pre>
+        <pre># git config --global user.name "Developer J. Cakemo"</pre>
+        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.&nbsp; Just run the two commands again but with proper values.</li>
+    </ol>
+    <h2>Powering up with the Feisty Meow® scripts<a id="#powerup" name="#powerup"></a></h2>
+    The feisty meow scripts are a cohesive bash scripting environment for
+    getting a variety of tasks done.&nbsp; The feisty meow scripts recently
+    incorporated the "avbash" collection from Saco Design and added those
+    scripts to a new "site_avenger" collection of scripts.&nbsp; The site
+    avenger scripts provide 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 an accompanying DNS domain and an
+    Apache2 site definition.
+    <p>The site avenger scripts are documented separately within the feisty meow
+      codebase.&nbsp; Consult the <span style="text-decoration: underline;">f</span><a
+        target="_blank" title="quickstart" href="https://feistymeow.org/feisty_meow/readme.txt">eisty
+meow
+        readme</a> file first, as it provides some valuable information on
+      configuring the codebase initially.&nbsp; The site avenger script commands
+      are documented in the <a target="_blank" title="useful commands" href="https://feistymeow.org/feisty_meow/documentation/feisty_meow_command_reference.txt">feisty
+        meow command reference</a> file.</p>
+    <p>(The feisty meow codebase is already configured for the developer account
+      on the cakelampvm virtual machine.)</p>
+    <h2>X11 applications launched from the VM</h2>
+    <p>[incomplete section]</p>
+    <p>If a feature called "X forwarding" is enabled in your ssh client, then
+      you can start graphical applications on the VM and display them on your
+      local machine.&nbsp; This works right away on most Linux hosts, but can
+      also work on PCs with X window system installed.&nbsp; This section
+      describes how to set up Cygwin to run X server, which enables X11
+      forwarding to your local display.</p>
+    <p>...{insert that info}...</p>
+    <h2>Using the guest VM's DNS services<a id="#dns-from-vm" name="#dns-from-vm"></a></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>
+      name lookup requests on any of the sites that the cakelampvm is hosting
+      for you.&nbsp; It will also serve as a general DNS server for any other
+      domains that need to be looked up.</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>
+      DNS service.&nbsp; The cakelampvm is available at the local IP 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>
-    <h2>Editing files on the guest VM from the host</h2>
+      override any DNS names that actually exist out on the internet.&nbsp; Further,
+      we have found it most effective to have *only* the cakelampvm as your DNS
+      server, because a secondary DNS server can "take over" providing the name
+      lookups, and thus foul up DNS requests that should succeed for your
+      VM-hosted sites.</p>
+    <p>If your Host PC is running Windows, see the DNS configuration section
+      below that is tailored to that operating system.</p>
+    <p>Important Note: It behooves you to remember to switch back to a normal
+      DNS server configuration when you shut off the cakelampvm, or your machine
+      will not know the names of any sites on the internet any more!&nbsp; The
+      official Google DNS servers are 8.8.8.8 and 8.8.4.4.</p>
+    <p>Once the DNS server is properly set up (by whatever means necessary),
+      these ping commands should get answering responses (from 10.28.42.20) on
+      both the cakelampvm VM and on your host PC.&nbsp; Note: ping on Linux
+      keeps going forever, so hit control-C when you are tired of seeing the
+      pings:</p>
+    <pre># ping cakelampvm.com</pre>
+    <pre># ping mapsdemo.cakelampvm.com</pre>
+    <p>Note that any other answer than 10.28.42.20 for the address is *bzzzt*
+      wrong, and means something needs to be fixed.</p>
+    <p>If these pings succeed (which hopefully they will!), then try accessing
+      the websites of each domain:</p>
+    <pre>(browse to) <a target="_blank" title="vm website if dns working" href="http://cakelampvm.com">http://cakelampvm.com</a></pre>
+    <pre>(browse to) <a target="_blank" title="mapsdemo app, hopefully functional"
+href="http://mapsdemo.cakelampvm.com">http://mapsdemo.cakelampvm.com</a></pre>
+    <p>These should show local sites on the VM rather than sites on the
+      internet.&nbsp; If you instead get failures to find the domains, or if the
+      "real internet" site comes up for cakelampvm.com (the page covered with
+      red X marks and complaining), then the DNS is not hooked up properly yet.</p>
+    <h4>Setting up DNS on Windows<a id="#windoze-dns" name="#windoze-dns"></a></h4>
+    <p>The ipconfig tool will provide helpful information about your current
+      networking and DNS configuration:</p>
+    <pre>&gt; 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; Internet | Network &amp; Sharing | click WiFI or Ethernet link near top right | click Adapter Settings button...<br>  &nbsp;| click on the specific network device to modify | select Properties</pre>
+    <p>Once the properties dialog is displayed, find "internet protocol version
+      4" in the list and double click it.</p>
+    <p>Change the DNS setting from "obtain...automatically" to "use the
+      following dns addresses".</p>
+    <p>Enter 10.28.42.20 as the first DNS address and clear the second address
+      (all blanks).</p>
+    <p>Hit okay, then okay, then close, etc to back out of adapter
+      configuration.</p>
+    <h4>Troubleshooting the DNS</h4>
+    <p>If your pings are getting the wrong answers and you're certain the DNS
+      settings on your Host PC are right, then you may need to flush your DNS
+      cache, and that might be sufficient to start getting the right IP
+      address.&nbsp; On Windows, the command for flushing DNS is:</p>
+    <pre>&gt; ipconfig /flushdns</pre>
+    <p>and on Linux the flush DNS command can be many different things, but try
+      these two most common options:</p>
+    <pre># sudo service dns-clean restart&nbsp;&nbsp; # restarts the client side DNS cache.</pre>
+    <p>or</p>
+    <pre># sudo service nscd restart&nbsp;&nbsp; # restarts the nscd caching server.</pre>
+    After, this try the pings again.&nbsp; If they still fail, please go back
+    over your DNS configuration very carefully.&nbsp; The cakelampvm's DNS
+    feature *does* actually work, but operating systems sometimes do their best
+    to deny this.<br>
+    <h4>Host Key Issues for ssh</h4>
+    <p>There is one caveat to be aware of when connecting to the cakelampvm.com
+      domain.&nbsp; If you have accidentally added the "real" cakelampvm.com
+      domain from the internet to your ssh known_keys at some point, then ssh
+      will complain about connecting to the VM on the cakelampvm.com
+      domain.&nbsp; This complaint will look like:</p>
+    <pre>The authenticity of host 'cakelampvm.com (104.236.56.82)' can't be established.</pre>
+    <p>Note that the IP address shown is not our beloved 10.28.42.20 local IP
+      address.</p>
+    <p>To fix this, remove the entry pointing at the "real" site from the
+      known_hosts file (ssh will print out the line number of the offending
+      entry).&nbsp; The DNS configuration needs to be configured before you will
+      get the warning about the cakelampvm.com domain.&nbsp; Up until then, the
+      domain name is always referring to the site out on the internet with the
+      red X's and warnings.&nbsp; See the DNS configuration section below to
+      configure DNS the first time.</p>
+    <p>Once you connect to the VM and the ssh client records the VM's host key
+      in your known_hosts, then you're in good shape.&nbsp; This state also
+      gives you a "canary in a coal mine" warning system...&nbsp; Once the VM is
+      registered as a known host, then any attempt to connect back to the "real"
+      internet version of cakelampvm.com will garner a complaint from ssh.&nbsp;
+      This version of the ssh warning should be heeded; you do not want to
+      connect to the real internet site, and the warning indicates that the host
+      PC is no longer using the DNS on the VM (since it reached the real
+      internet site instead of the VM).&nbsp; That situation needs to be
+      corrected by running through the DNS configuration section again (and
+      testing the DNS until it is working).</p>
+    <h4>Troubleshooting the Apache Sites</h4>
+    <p>If your DNS pings and lookups are functioning properly, but you're just
+      not getting the right websites, then try clearing your browser's cache and
+      shutting the browser application down.&nbsp; Then, start the browser up
+      and try the address again.&nbsp; Often this cache dumping is enough to fix
+      the browser so that you start seeing the local website versions on
+      cakelampvm.com.</p>
+    <h2>Editing files on the guest VM from the host<a id="#editing-files-on-vm"
+        name="#editing-files-on-vm"></a></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"
       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>
+      The existing mapsdemo site is owned by a different user ("fred") rather
+      than developer, mostly as a test case.&nbsp; The "fred", "developer", and
+      "www-data" accounts on the VM have all been put into each others Unix
+      "groups" so that they can access each other's files, and thus you may not
+      notice any issues editing fred's files.</p>
+    <p>One should be able to create a new directory over the network also.&nbsp;
+      Try creating a junk folder in the "www" folder, and then deleting it
+      again.&nbsp; That should succeed, and this approach can be used to create
+      folders (from the Host PC) that are owned by the developer user (on the
+      VM).&nbsp; You should be able to create folders or copy files within the
+      developer's home folder also ("/home/developer").</p>
+    <p>If you run into any permission problems that prevent file access, either
+      remotely or within the VM itself, then try running this command to fix
+      them (repeated from the section above about updating the cakelampvm):</p>
+    <pre># revamp_cakelampvm</pre>
+    <p>Afterwards, the www folder and others should allow the developer user to
+      create new folders at will.</p>
+    <h2>Accessing files on the host PC from the guest VM<a id="#samba-shares" name="#samba-shares"></a></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>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>
+        <pre># mkdir ~/shared&nbsp;&nbsp;&nbsp; # for the guest's version of the shared folder<br># 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>Note: these instructions, even the quick approaches below, pale in
+      comparison to the ease of use of the "standup" command in feisty meow's
+      site avenger scripts.&nbsp; The standup command is detailed in the&nbsp;<a
+        target="_blank" title="useful commands" href="https://feistymeow.org/feisty_meow/documentation/feisty_meow_command_reference.txt">feisty
+        meow command reference</a> document.&nbsp; These instructions are for
+      situations when the domain or site is idiosyncratic in some way that
+      standup doesn't support.</p>
+    <p>To add a new website, you will first need to pick one of the DNS options
+      below (A or B) depending on how you want to name the site.&nbsp; If the
+      DNS name of the site is contained within another existing domain (e.g.,
+      "A.B.C" has subdomain A contained in domain B.C), use Option A.&nbsp; If
+      the DNS name is a so-called "Second Level Domain" (SLD), then it stands on
+      its own (e.g., "B.C" is an SLD).</p>
+    <p>Once the DNS option has been picked and implemented, continue to the next
+      section of "Creating a New Apache Site".</p>
+    <p>For either Option A or Option B, first connect to the cakelampvm via ssh
+      as the developer user, e.g.: ssh developer@cakelampvm.com </p>
+    <h3>DNS Option A: Adding a sub-domain in an existing domain</h3>
+    <p>Let us say a customer needs an application called "excalibur".&nbsp; It
+      will be a new subdomain within an existing domain, such as the
+      "cakelampvm.com" domain, meaning we want the VM to start answering
+      requests for "excalibur.cakelampvm.com".</p>
+    Note that this option requires the containing domain "cakelampvm.com" to
+    already exist before adding the subdomain; see DNS Option B below for
+    details on how to add a containing domain for the first time.
+    <h4>Quick approach: Use the feisty meow "add_domain" command.</h4>
+    <p>Run this command in a bash shell on the VM:</p>
+    <pre># add_domain excalibur.cakelampvm.com</pre>
+    <p>Done.</p>
+    <h4>Manual approach: Edit the bind9 configuration.</h4>
+    <p>Note: the manual approach is not compatible with later use of feisty
+      meow's "remove_domain".</p>
+    Execute the following command to edit the DNS file for the cakelampvm
+    domain:
+    <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>
+    <pre>excalibur.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:</p>
+    <pre># sudo service bind9 restart</pre>
+    <p>Afterwards, pinging excalibur.cakelampvm.com should work from both the
+      guest VM and the host PC.</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
+    <p>This is a similar procedure to Option A, but we will create a totally new
+      config file for the new domain and add it to the bind directory.&nbsp; For
+      this example, we need to add the site "excalibur.tv" into the DNS.</p>
+    <h4>Quick approach: Use the feisty meow "add_domain" command.</h4>
+    Run this command in a bash shell on the VM:
+    <pre># add_domain excalibur.tv</pre>
+    <p>Done.</p>
+    <h4>Manual approach: Edit a new DNS config file</h4>
+    <p>Note: the manual approach is not compatible with later use of feisty
+      meow's "remove_domain".</p>
+    Create a file called /etc/bind/excalibur.tv.conf for our new domain
+    excalibur.tv with these contents:
+    <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># new SLD for our excalibur site.<br>excalibur.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 "excalibur.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
+    is the domain's name server.&nbsp; However, the new domain does <span style="text-decoration: underline;">not</span>
+    live inside the cakelampvm.com domain.<br>
+    <p>Now that the config file is in place, edit "/etc/bind/named.conf.local"
+      to add the new file by adding this bit of configuration at the end:</p>
+    <pre>zone "excalibur.tv" in {<br>&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp;&nbsp; file "/etc/bind/excalibur.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:</p>
+    <pre># sudo service bind9 restart</pre>
+    <p>Afterwards, pinging excalibur.tv should work from both the guest and the
       host.</p>
-    <h3>Create a new apache configuration file and load it</h3>
+    <h3>Creating a New Apache Site</h3>
+    <p>First, connect to the cakelampvm via ssh as the developer user, e.g.: ssh
+      developer@cakelampvm.com </p>
+    <h4>Quick approach: Use the feisty meow "add_apache_site" command.</h4>
+    <p>Run this command in a bash shell on the VM:</p>
+    <pre># add_apache_site excalibur excalibur.tv</pre>
+    <p>(The first parameter is the application name, the second is the domain
+      name.)</p>
+    <p>Done.</p>
+    <h4>Manual approach: Edit an Apache config file</h4>
+    <p>Note: the manual approach is not compatible with later use of feisty
+      meow's "remove_apache_site".</p>
+    <p>For Apache, the choice of DNS Option A or B, subdomain or SLD, does not
+      matter.&nbsp; The site configuration file just has to accurately specify
+      the domain in question.</p>
     <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>
+      for the appropriate host name and "DocumentRoot" path:</p>
+    <pre>&lt;VirtualHost *:80&gt;<br>&nbsp;&nbsp;&nbsp; ServerName excalibur.tv
+&nbsp;&nbsp;&nbsp; DocumentRoot /home/apps/excalibur<br>&nbsp;&nbsp;&nbsp; ErrorLog ${APACHE_LOG_DIR}/excalibur.tv-error.log<br>&nbsp;&nbsp;&nbsp; CustomLog ${APACHE_LOG_DIR}/excalibur.tv-access.log combined<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 appropriate for our excalibur app in the
+      excalibur.tv domain (using DNS Option B).&nbsp; Modifying the excalibur.tv
+      references in it (and the path in the DocumentRoot) is sufficient to
+      re-target it for any domain you want.</p>
+    <p>Copy the new site config file into "/etc/apache2/sites-available" with an
+      appropriate file name that includes the site's domain name.&nbsp; We will
+      call our config file "excalibur.tv.conf".&nbsp; If you developed the file
+      in your home folder, this would be the command to move it up to Apache:</p>
+    <pre># sudo cp ~/excalibur.tv.conf /etc/apache2/sites-available</pre>
+    <p>Then tell apache to use the new file:</p>
+    <pre># sudo a2ensite excalibur.tv  # the '.conf' portion of the filename is unnecessary for this command.
+</pre>
     <p>Finally, restart apache to get it to begin serving the site:</p>
-    <pre>sudo service apache2 restart</pre>
+    <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>
+      the new website on the domain "excalibur.tv".</p>
+    <p>To test this, first try pinging the new DNS name:</p>
+    <pre># ping excalibur.tv</pre>
+    <p>If there are responses to the ping *and* the answer is 10.28.42.20, then
+      it means the DNS is working.&nbsp; If there are no responses or it's some
+      other IP address talking back, check the instructions in the above DNS
+      sections.</p>
+    <p>Once the DNS is working, try browsing to the site at "<a title="it's excalibur, wilbur!"
+        href="http://excalibur.tv">http://excalibur.tv</a>".&nbsp; That should
+      at least bring up the configured site storage path, even if nothing is
+      being served from that folder yet.</p>
+    <p>If the new site is not showing up properly, try examining the apache logs
+      for any error messages that can be corrected.&nbsp; The log files are
+      stored in "/var/log/apache2" and are named after the website (if
+      configured through the above process).</p>
+    <h2>Handy Techniques for Using cakelampvm</h2>
+    <h3>Assorted Guides and Cheat-Sheets</h3>
+    <p>A Cheat sheet for the Vim editor (there are many of these available): <a
+        target="_blank" title="vim commands" href="https://vim.rtorr.com/">https://vim.rtorr.com/</a></p>
+    <p>A git branching model that seems to work well: <a target="_blank" title="release and patch process"
+        href="http://nvie.com/posts/a-successful-git-branching-model/">http://nvie.com/posts/a-successful-git-branching-model/</a></p>
+    <p>This is a basic guide to the Google Developer Console and API Key
+      management: <a target="_blank" title="apis and creds at google" href="https://feistymeow.org//feisty_meow/documentation/google_apis/google_apis_and_credentials.pdf">Google
+        API Docs</a></p>
+    <p>&nbsp;</p>
+    <h3>Using the "meld" Tool to Compare Files &amp; Directories</h3>
+    <p>Meld is a great comparison tool that displays differences between two
+      files or directories or directory trees in a graphical view.&nbsp; Meld is
+      pre-installed on the VM.&nbsp; This tool can be launched either in the
+      VM's X Windowing System (on the console) or if X11 forwarding is enabled.</p>
+    <p>To run meld, just type this command:</p>
+    <p>#meld A B</p>
+    <p>where A and B are either both file names or they are both directory names.&nbsp;
+      If A and B are directories, meld will compare the entire tree structure
+      between the two directories.&nbsp; It allows one to copy from one side to
+      the other, even if the item that needs to be copied is an entire
+      subdirectory.</p>
+    <h3>Get the network address on the guest vm</h3>
+    <p>Run this command:</p>
+    <pre># ifconfig</pre>
+    <p>In the results, look for "<span style="font-family: monospace;">inet addr</span>".&nbsp;
+      There may be more than one, if there are multiple network interfaces.</p>
+    <p>The standard IP address is 10.28.42.20 for the cakelampvm.</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:</p>
+    <pre># ssh developer@cakelampvm.com</pre>
+    <p>Then, to reboot the guest VM:</p>
+    <pre># sudo reboot</pre>
+    <p>Or, to halt the guest VM:</p>
+    <pre># sudo shutdown -h now</pre>
+    <p>Using these commands is kinder to the VM than just cycling the power from
+      the Virtualbox control panel.</p>
+    <h1>Gritty Details of the Nitty Variety<a id="#nitty-gritty" name="#nitty-gritty"></a></h1>
+    <p>This is the lowest level of plumbing for your VM.&nbsp; Hopefully you
+      will not need to engage with this section.&nbsp; The most useful doc
+      section here is the one below about the "Virtualbox guest additions",
+      which you will probably need at some future point.&nbsp; Oracle releases
+      updates to the guest additions fairly regularly.</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>
+    <h3>Set up Virtualbox guest additions for the VM</h3>
+    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.
     <ol>
       <li>To install the guest additions, open the guest VM and have its window
         in focus.</li>
         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>
+        <pre># sudo mount /dev/sr0 /media/cdrom</pre>
+        <p>Linux will mention that the device is mounted "read-only".</p>
+      </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>
+        <pre># cd /media/cdrom<br># sudo sh VBoxLinuxAdditions.run</pre>
+      </li>
+      <li>The latest Virtualbox guest additions should now be installed.</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
+      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>
     <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>
+    <h3>Compacting the VM Disk Image</h3>
+    <p>To minimize the size used for the disk image, there are three major
+      steps.</p>
+    <p>1. While running the VM, run this command:</p>
+    <pre># sudo apt clean</pre>
+    <p>This throws away any cached data from the apt tool, which can be
+      substantial.</p>
+    <p>If there are other junk files you know of that can be removed, delete
+      those now also.</p>
+    <p>2. Reboot the VM to the gparted ISO image (available at the <a target="_blank"
+        title="great free partition editor" href="https://gparted.org/livecd.php">gparted
+        site</a>) and run the following command:</p>
+    <pre># sudo zerofree /dev/sda</pre>
+    <p>This sets all free space to the zero byte, enabling Virtualbox to free
+      that space in the next step.</p>
+    <p>3. Shut the vm down after zerofree is complete and run this command on
+      the host PC (this is the Linux version of the command):</p>
+    <pre># VBoxManage modifyhd --compact ~/cake_lamp_vm/cake-lamp-vm-hd.vdi</pre>
+    <p>Replace the <span style="font-family: monospace;">~/cake_lamp_vm</span>
+      path with the real VM storage path.&nbsp; This command compacts the root
+      (and only) partition of the VM.</p>
+    <p>After these steps are complete, the VM should be its minimal size.</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
+      <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
+      <li>Downloaded ubuntu server 16.04 iso. (<a target="_blank" title="ubuntu server"
+          href="https://www.ubuntu.com/download/server">https://www.ubuntu.com/download/server</a>)</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>Installed LAMP stack on guest VM.&nbsp; Some help here: <a target="_blank"
+          title="lamplighter" href="http://howtoubuntu.org/how-to-install-lamp-on-ubuntu">http://howtoubuntu.org/how-to-install-lamp-on-ubuntu</a></li>
+      <li>Configured CAKE on the guest VM.&nbsp; Useful link: <a target="_blank"
+          title="cakebundtu" href="https://askubuntu.com/questions/628938/how-to-install-cakephp-in-ubuntu-14-04">https://askubuntu.com/questions/628938/how-to-install-cakephp-in-ubuntu-14-04</a></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>
+        Virtualbox networking with two adapters similar to our setup: <a target="_blank"
+          href="https://askubuntu.com/questions/293816/in-virtualbox-how-do-i-set-up-host-only-virtual-machines-that-can-access-the-in">https://askubuntu.com/questions/293816/in-virtualbox-how-do-i-set-up-host-only-...</a><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>
+        here: <a target="_blank" href="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">https://help.ubuntu.com/community/How%20to%20Create...</a></li>
+      <li><br>
+      </li>
     </ul>
     <p><br>
     </p>
+    <ul>
+    </ul>
+    <h6> </h6>
+    <p> </p>
   </body>
 </html>
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..f430c4f
--- /dev/null
@@ -0,0 +1,2 @@
+Welcome to the CakePHP LAMP VM.
+Documentation is available at: http://cakelampvm.com
diff --git a/production/sites/cakelampvm.com/rolling/default_page.001/001-default-http.conf b/production/sites/cakelampvm.com/rolling/default_page.001/001-default-http.conf
new file mode 100644 (file)
index 0000000..1360081
--- /dev/null
@@ -0,0 +1,21 @@
+
+
+<Directory "/opt/feistymeow.org/feisty_meow/production/sites/cakelampvm.com">
+  Options +ExecCGI +Indexes +FollowSymLinks +Includes +MultiViews
+  Require all granted
+</Directory>
+
+
+<VirtualHost *:80>
+       ServerName cakelampvm.com
+       ServerAlias www.cakelampvm.com
+       DocumentRoot /opt/feistymeow.org/feisty_meow/production/sites/cakelampvm.com
+       DirectoryIndex vm_index.html
+
+       ServerAdmin developer@localhost
+
+       ErrorLog ${APACHE_LOG_DIR}/error.log
+       CustomLog ${APACHE_LOG_DIR}/access.log combined
+
+</VirtualHost>
+
diff --git a/production/sites/cakelampvm.com/rolling/default_page.001/001-default-ssl.conf b/production/sites/cakelampvm.com/rolling/default_page.001/001-default-ssl.conf
new file mode 100644 (file)
index 0000000..c89b459
--- /dev/null
@@ -0,0 +1,37 @@
+
+
+<Directory "/opt/feistymeow.org/feisty_meow/production/sites/cakelampvm.com">
+  Options +ExecCGI +Indexes +FollowSymLinks +Includes +MultiViews
+  Require all granted
+</Directory>
+
+<VirtualHost *:443>
+       ServerName cakelampvm.com
+       ServerAlias www.cakelampvm.com
+       DocumentRoot /opt/feistymeow.org/feisty_meow/production/sites/cakelampvm.com
+       DirectoryIndex vm_index.html
+
+       ServerAdmin developer@localhost
+
+       ErrorLog ${APACHE_LOG_DIR}/error.log
+       CustomLog ${APACHE_LOG_DIR}/access.log combined
+
+       SSLEngine on
+
+       #   A self-signed (snakeoil) certificate can be created by installing
+       #   the ssl-cert package. See
+       #   /usr/share/doc/apache2/README.Debian.gz for more info.
+       #   If both key and certificate are stored in the same file, only the
+       #   SSLCertificateFile directive is needed.
+       SSLCertificateFile      /etc/ssl/certs/ssl-cert-snakeoil.pem
+       SSLCertificateKeyFile /etc/ssl/private/ssl-cert-snakeoil.key
+
+       <FilesMatch "\.(cgi|shtml|phtml|php)$">
+                       SSLOptions +StdEnvVars
+       </FilesMatch>
+       <Directory /usr/lib/cgi-bin>
+                       SSLOptions +StdEnvVars
+       </Directory>
+
+</VirtualHost>
+
index ca3b745926850fed6a873ed75741dbbe2fd02423..aaa8283b77d290095a210d1b309b9797cd16beef 100644 (file)
@@ -58,12 +58,15 @@ A couple of perl modules are required by some of the scripts (including the
 differ utility and the feisty meow customization system).  They can be
 installed with either apt-get or CPAN.
 
-Install using apt-get:
+Install using apt-get (on Ubuntu or Debian-based OS):
 # sudo apt-get install libfile-which-perl libtext-diff-perl
 
 or Install using perl's CPAN:
 # sudo cpan install Text::Diff File::Which
 
+or on Cygwin (MS-Windows), install with apt-cyg:
+# apt-cyg install perl-File-Which perl-Text-Diff
+
 ==============
 
 How to install the build dependencies for feisty meow
index c6dbbafb01d003aa578677235c4cc89dca1068d3..1c7784aae368e7c39cfff19a84bdf0c1d91b04eb 100644 (file)
@@ -133,6 +133,11 @@ analyze_hierarchy_and_report ~/cloud/disordered "unsorted files"
 # up our weight accounting considerably.
 analyze_hierarchy_and_report ~/cloud/reading "reading list (for a quiet afternoon)"
 
+####
+
+# vocation is a prefix for anything i consider part of my life's work.
+analyze_by_dir_patterns "life's work and other oddities" ~/cloud/vocation*
+
 # scan all the items declared as active projects.
 analyze_by_dir_patterns "active issues" ~/cloud/active*
 
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 208728d9cb54e9ff718a367297f31fcfde68331b..40a72ba49f22903464a01799b25f2de0a1c3f2d1 100644 (file)
@@ -678,7 +678,7 @@ ifeq "$(OMIT_VERSIONS)" ""
   ifneq "$(VERSION_RC_ROOT)" ""
 # only redo the version resource if it or version.ini is out of date.
 %_version.rc: version.ini $(PARAMETER_FILE)
-       $(CATCHER)$(CLAM_BINARIES)/version_stamper$(EXE_END) . $(PARAMETER_FILE)
+       $(CATCHER)$(CLAM_BINARIES)/version_stamper$(EXE_END) . $(FEISTY_MEOW_GENERATED_STORE)/versions $(PARAMETER_FILE)
   endif
 endif
 
@@ -769,10 +769,11 @@ endif # no-deps.
 
 ############################################################################
 
-# examines the required variables and complains if they're missing.
+# examines the required variables and complains if they are missing.
 check_requirements:
        $(HIDESH) -c 'if [ -z "$(PROJECT)" ]; then echo the PROJECT variable is not defined!; . $(CLAM_SCRIPTS)/exit_make.sh; fi'
        $(HIDESH) -c 'if [ -z "$(TYPE)" ]; then echo the TYPE variable is not defined!; . $(CLAM_SCRIPTS)/exit_make.sh; fi'
+       $(HIDESH) -c 'if [ ! -d $(FEISTY_MEOW_GENERATED_STORE)/versions ]; then mkdir $(FEISTY_MEOW_GENERATED_STORE)/versions; fi'
  
 ############################################################################
 
index 60fcc7bc6bd43b30784cc53eddbabfd5e367f638..c9210586b8b3ab3003de4cdaca976ef3e966f3f0 100644 (file)
@@ -228,7 +228,8 @@ export DYNAMIC_LIBRARY_DIR = $(TARGETS_STORE)
 export STATIC_LIBRARY_DIR = $(TARGETS_STORE)
 
 # "HEADER_SEARCH_PATH" is where the class interface files are to be found.
-#HEADER_SEARCH_PATH =
+# we add the generated store folder for the build version file.
+HEADER_SEARCH_PATH = $(FEISTY_MEOW_GENERATED_STORE)/versions
 
 # "HOOPLE_HEADERS" are locations where the HOOPLE headers can be found.
 ifeq "$(HOOPLE_HEADERS)" ""
index 9c94ef167597c8075920465714826f8220784dcd..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\""
index c078c90afdb1d6feaa3060f8f2e8402af55cb012..189dac016546307e7e665e05f816285e4389cbf9 100644 (file)
@@ -169,14 +169,15 @@ if [ -z "$skip_all" ]; then
     # version for the other side (just 'linux'); we don't want the remote side still
     # thinking it's running xterm.
     save_terminal_title
-    # 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
+#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
-    # restore the terminal variable also.
-    TERM="$oldterm"
   }
 
   ##############
@@ -475,13 +476,24 @@ 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")"
 
-    local fail_message="\nare the perl dependencies installed?  if you're on ubuntu or debian, try this:\n
-    $(grep "apt.*perl" $FEISTY_MEOW_APEX/readme.txt)\n"
-    
+    local fail_message="\n
+are the perl dependencies installed?  if you're on ubuntu or debian, try this:\n
+    $(grep "apt-get.*perl" $FEISTY_MEOW_APEX/readme.txt)\n
+or if you're on cygwin, then try this (if apt-cyg is available):\n
+    $(grep "apt-cyg.*perl" $FEISTY_MEOW_APEX/readme.txt)\n";
+
     #echo "the incongruous files list is: $incongruous_files"
     # disallow a single character result, since we get "*" as result when nothing exists yet.
     if [ ${#incongruous_files} -ge 2 ]; then
@@ -504,6 +516,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
   }
 
@@ -847,6 +864,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 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 d9d9d654784b9732a893c233486cced77a157005..f10d6b95ea3e21446d39f9371ff665935968f8e1 100644 (file)
@@ -37,9 +37,9 @@ function launch_screen()
   app_name="$1"; shift
   echo "$(date_stringer ' '): starting $screen_name now..."
 #hmmm: version check for if we're using old screen?  this -L change was a mistake though for the screen project owners on ubuntu.
-#local boguslog=$HOME/screen_junk_$(date_stringer).log
+local boguslog=$HOME/screen_junk_$(date_stringer).log
 #maybe they unbroke it in 17.10?
-  screen -L$boguslog -S "$screen_name" -d -m nice -n $NICENESS_LEVEL mono "$app_name" 
+  screen -L $boguslog -S "$screen_name" -d -m nice -n $NICENESS_LEVEL mono "$app_name" 
 
   echo "$(date_stringer ' '): $screen_name started."
   # only sleep if we are not at the last process that gets started.
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 5c343b5d07b2fc357d83c516782dcb8ee96cb45c..d74fe1bd67c43c713249809e0bcbf0a991cb4b0a 100644 (file)
@@ -15,7 +15,7 @@ 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)"
   # calculate the number of columsn in the terminal.
   cols=$(get_maxcols)
@@ -64,7 +64,7 @@ function do_checkin()
   if [ $nicedir == "." ]; then
     nicedir=$(\pwd)
   fi
-  local blatt="echo checking in '$nicedir'..."
+  local blatt="echo -n checking in '$nicedir'...  "
 
   do_update "$directory"
   test_or_die "repository update--this should be fixed before check-in."
@@ -96,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.
@@ -107,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"
 
@@ -179,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.
@@ -197,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
@@ -205,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
@@ -215,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()
@@ -275,16 +324,19 @@ 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
 }
 
@@ -294,7 +346,7 @@ function show_branch_conditionally()
 {
   local this_branch="$1"; shift
 
-  state=$(check_branch_state "$this_branch")
+  local state=$(check_branch_state "$this_branch")
   if [ "$state" != "okay" ]; then
     echo "=> branch '$this_branch' state is not clean: $state"
   fi
@@ -342,8 +394,6 @@ function do_careful_git_update()
       # 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.
 
diff --git a/scripts/site_avenger/clean_mapsdemo.sh b/scripts/site_avenger/clean_mapsdemo.sh
new file mode 100644 (file)
index 0000000..5054cdd
--- /dev/null
@@ -0,0 +1,42 @@
+#!/bin/bash
+
+# some code i wrote to add to revamp that turned out to be unsuitable.
+# but it corrects a problem in cakelampvm v002 release that i find annoying,
+# so here it is as its own file.
+
+# clean out some old files that were not checked in in mapsdemo.
+echo Doing some git repository maintenance in fred account.
+#
+# change over to fred folder
+pushd /home/fred
+test_or_die "changing dir to fred's home; what have you done with fred?"
+
+pushd apps/mapsdemo/avenger5
+test_or_die "changing dir to mapsdemo app"
+
+rpuffer . &>/dev/null
+if [ $? -ne 0 ]; then
+  # it seems our old files are still conflicting this.
+  if [ -f config/config_google.php ]; then
+    \rm -f config/config_google.php
+    test_or_die "removing old config for google"
+  fi
+  if [ -f config/app.php ]; then
+    \rm -f config/app.php
+    test_or_die "removing old config for app"
+  fi
+
+  git reset --hard HEAD
+  test_or_die "resetting git's hard head"
+
+  rpuffer .
+#hmmm: use output saver thing when that exists.
+  test_or_die "puffing out mapsdemo app after inadequate corrective action was taken"
+fi
+
+popd
+
+popd
+#...coolness, if we got to here.
+
+
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..9db5eef39399a1bfbb9ca412a497b816e711e2f0 100644 (file)
@@ -14,29 +14,146 @@ 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"
+source "$FEISTY_MEOW_SCRIPTS/system/common_sysadmin.sh"
 
 ##############
 
+echo "Regenerating feisty meow loading dock."
+
+reconfigure_feisty_meow
+test_or_die "feisty meow reconfiguration"
+chown -R "$(logname)":"$(logname)" /home/$(logname)/.[a-zA-Z0-9]*
+test_or_die "fix after reconfigured as sudo"
+
+##############
+
+echo "Making some important permission changes..."
+
 # 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
+chown -R developer:developer /home/developer /home/developer/.[a-zA-Z0-9]*
+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 
+chown -R fred:fred /home/fred /home/archives/stuffing /home/fred/.[a-zA-Z0-9]*
+test_or_die "chown fred home"
+group_perm $HOME/apps
+test_or_die "group perms on fred's apps"
 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"
+group_perm /home/fred/apps/mapsdemo
+test_or_die "group perms on mapsdemo app"
+
+echo "Done with important permission changes."
 
 ##############
+#
+# some slightly tricky bits start here.  we want to massage the vm into the
+# best possible shape without needing to re-release it.
+#
+##############
+
+echo "Updating developer welcome file."
+
+# 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 "installing bluefish editor"
+
+##############
+
+# deploy any site updates here to the VM's cakelampvm.com site.
+#
+# we want to upgrade the default apache site to the latest, since the new
+# version mirrors the one on the internet (but with green checks instead
+# of red X's) and since we also support https on the new default version.
+# we can do this again later if needed, by upping the numbers on the apache
+# site config files.  our original site was 000 and the new version is 001,
+# which we've done as a prefix on the config for some reason.  makes the
+# code below easy at least.
+if [ -L /etc/apache2/sites-enabled/000-default.conf ]; then
+  # the old site is in place still, so let's update that.
+  echo "Updating default web sites to latest version."
+
+  a2enmod ssl
+  test_or_die "enabling SSL for secure websites"
+
+  restart_apache
+  test_or_die "getting SSL loaded in apache"
+
+  a2dissite 000-default
+  test_or_die "disabling old apache site"
+
+  rm -f /etc/apache2/sites-available/000-default.conf 
+  test_or_die "removing old apache site"
+
+  # copy in our new 000 version (which  
+  cp $FEISTY_MEOW_APEX/production/sites/cakelampvm.com/rolling/default_page.001/* \
+      /etc/apache2/sites-available
+  test_or_die "installing new apache default sites"
+
+  # there should only be ours at this version level and with that prefix.
+  a2ensite 001-*
+  test_or_die "enabling new apache default sites"
+
+  restart_apache
+fi
+
+##############
+
+##############
+
+# sequel--tell them they're great and show the hello again also.
+
+echo "
+
+
+"
+regenerate
+test_or_die "regenerating feisty meow scripts"
+chown -R "$(logname)":"$(logname)" /home/$(logname)/.[a-zA-Z0-9]*
+test_or_die "fix after regenerate as sudo"
+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..778b22d57d69733837dd271125e25a1d6c87bf96 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..4ab80ed
--- /dev/null
@@ -0,0 +1,353 @@
+#!/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 service 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
+  echo Apache2 service restarted.
+}
+
+# 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 360860215b5f596339daaa3932af23f0c0cb4d0b..0203b9ae7fd771bbacbc80b1c2d2319e34462e3f 100644 (file)
@@ -4,7 +4,7 @@ source "$FEISTY_MEOW_SCRIPTS/core/functions.sh"
 source "$FEISTY_MEOW_SCRIPTS/core/common.alias"
 
 # uncomment this to get extra noisy debugging.
-export DEBUG_TERM_TITLE=true
+#export DEBUG_TERM_TITLE=true
 
 # puts a specific textual label on the terminal title bar.
 # this doesn't consider any previous titles; it just labels the terminal.
@@ -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,15 +32,21 @@ function apply_title_to_terminal()
 function set_terminal_title()
 {
   apply_title_to_terminal $*
-  # we're enforcing a new title from here on.
-  unset PRIOR_TERMINAL_TITLE
+
+#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
 }
 
 # echoes back the current title on the terminal window, if we can acquire it.
 function get_terminal_title()
 {
-  local term_title_found=""
+  # 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
@@ -48,19 +55,19 @@ function get_terminal_title()
       term_title_found="$(xprop -id $WINDOWID | perl -nle 'print $1 if /^WM_NAME.+= \"(.*)\"$/')"
     fi
   fi
-  echo "$term_title_found"
+  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 [ ! -z "$title" ]; then
+  if [ "$title" != "unknown" ]; then
     # there was a title, so save it.
     if [ ! -z "$DEBUG_TERM_TITLE" ]; then
-      echo "saving prior terminal title as '$prior_title'"
+      echo "saving prior terminal title as '$title'"
     fi
-    export PRIOR_TERMINAL_TITLE="$prior_title"
+    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
@@ -95,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