Include detailed timeline with specific links
diff --git a/www/members/meeting-util.rb b/www/members/meeting-util.rb
index 40ada2f..cc87490 100644
--- a/www/members/meeting-util.rb
+++ b/www/members/meeting-util.rb
@@ -5,13 +5,14 @@
 class MeetingUtil
   RECORDS = 'https://svn.apache.org/repos/private/foundation/Meetings'
   MEETING_FILES = { # Filename in meeting dir, or pathname to another tool
-    'README.txt' => 'README For Meeting Process',
+    'README.txt' => 'README For Meeting Process And Roll Call',
     'nomination_of_board.txt' => 'How To Nominate Someone For Board',
     'nomination_of_members.txt' => 'How To Nominate A New Member',
     '/members/proxy.cgi' => 'How To Submit A Proxy/Check Your Proxies',
     'https://www.apache.org/foundation/governance/meetings' => 'How Voting Via Email Works',
     'agenda.txt' => 'Official Meeting Agenda',
     'board_ballot.txt' => 'Official Board Candidate Ballots',
+    'nominated-members.txt' => 'Official New Member Nominees/Seconds',
     'proxies' => 'Official List Of Meeting Proxies',
     'record' => 'Official List Of Voting Members',
     'attend' => 'Official List Of Meeting Attendees (afterwards)',
diff --git a/www/members/meeting.cgi b/www/members/meeting.cgi
index 281c81a..48d8d70 100755
--- a/www/members/meeting.cgi
+++ b/www/members/meeting.cgi
@@ -8,6 +8,12 @@
 require 'json'
 require 'wunderbar/jquery/stupidtable'
 require_relative 'meeting-util'
+FOUNDATION_SVN = 'https://svn.apache.org/repos/private/foundation/'
+
+# Utility function for links, Note: cheezy path detection within MEETING_FILES
+def emit_link(cur_mtg_dir, f, desc)
+  _a desc, href: f.include?('/') ? f : File.join(cur_mtg_dir, f)
+end
 
 # Output action links for meeting records, depending on if current or past
 def emit_meeting(cur_mtg_dir, meeting, active)
@@ -39,8 +45,8 @@
     _p active ? "Live links to the upcoming meeting records/how-tos below." : "These are historical links to the past meeting's record."
     _ul do
       MeetingUtil::MEETING_FILES.each do |f, desc|
-        _li do # Note: cheezy path detection within MEETING_FILES
-          _a desc, href: f.include?('/') ? f : File.join(cur_mtg_dir, f)
+        _li do
+          emit_link(cur_mtg_dir, f, desc)
         end
       end
     end
@@ -55,7 +61,14 @@
     meeting = File.basename(cur_mtg_dir)
     mtg_date = Date.parse(meeting)
     today = Date.today.strftime('%Y%m%d')
-    
+    begin
+      ics = IO.read(File.join(cur_mtg_dir, "ASF-members-#{mtg_date.strftime('%Y')}.ics"))
+    rescue StandardError => e
+      # Ensure we can't break rest of script
+      puts "ERROR: #{e}"
+      return 0, 0, 0, 0
+    end
+
     ROSTER = "/roster/committer"
     _whimsy_body(
       title: PAGETITLE,
@@ -87,7 +100,7 @@
         else
           _p do
             _ "The next Member's Meeting will start on #{mtg_date.strftime('%A, %d %B %Y')}, as an online meeting on IRC, and will finish up two days later after voting via email is held."
-            _ 'For more details, read on below, or see the links to the right. '
+            _ 'Please read below for a Timeline of Meeting activities and links to how you can take action, or see additional links to the right. '
             _span.text_warning 'REMINDER: '
             _ 'Nominations for the board or new members close 10 days before the meeting starts; no new names may be added after that date.'
           end
@@ -98,7 +111,6 @@
       attendance = JSON.parse(IO.read(File.join(MEETINGS, 'attendance.json')))
       user = ASF::Person.find($USER)
       _whimsy_panel("#{user.public_name} Details For Meeting #{meeting}", style: 'panel-primary') do
-        # TODO: remind member to check their committer.:email_forward address is correct (where ballots are sent)
         _p do
           if help
             _p help
@@ -116,10 +128,98 @@
         _p do
           _span.text_warning 'REMINDER: '
           _ "Ballots are sent to your official email address as found in members.txt, please double-check it is correct!"
-          _a 'See members.txt', href: 'https://svn.apache.org/repos/private/foundation/members.txt'
+          _a 'See members.txt', href: "#{FOUNDATION_SVN}members.txt"
+        end
+      end
+
+      _whimsy_panel("Timeline: Nomination Period (now until TEN DAYS before the meeting)", style: 'panel-default') do
+        _p do
+          _ 'Before the meeting, any Member may nominate people either for the Board, or as a New Member Candidate.  Much of this discussion happens on members@ mailing list.  Remember, all new nominated names must be checked into SVN 10 days before the meeting.'
+          _ 'Also, you should submit a proxy if you might not attend the first half of the meeting.'
+          _ul do
+            ['nomination_of_board.txt', 'nomination_of_members.txt', '/members/proxy.cgi'].each do |f|
+              _li do
+                emit_link(cur_mtg_dir, f, MeetingUtil::MEETING_FILES[f])
+              end
+            end
+          end
         end
       end
       
+      _whimsy_panel("Timeline: Seconds Period (last ten days before meeting)", style: 'panel-default') do
+        _p do
+          _ 'The 10 days before the meeting, you may add seconds to existing nomination files, but no new nominations are allowed.'
+          _ 'Also, you can still submit a proxy if you might not attend the first half of the meeting.'
+          _ul do
+            ['nominated-members.txt', '/members/proxy.cgi'].each do |f|
+              _li do
+                emit_link(cur_mtg_dir, f, MeetingUtil::MEETING_FILES[f])
+              end
+            end
+          end
+        end
+      end
+      
+      _whimsy_panel("Timeline: First Half Of Meeting on IRC (at #{mtg_date.strftime('%A, %d %B %Y')})", style: 'panel-primary') do
+        _p do
+          _ 'The Meeting itself starts on IRC - please be sure your client is setup ahead of time, and sign in with your Apache ID as nick if at all possible.'
+          _ 'The #asfmembers channel is for the official meeting itself; please raise your hand if you have a formal question there.  Backchannel (jokes, comments, etc.) is on #asf channel.'
+          _ 'During the First Half of Meeting, the Chairman and various officers run through reports in the Agenda, which you can read ahead of time.'
+          _ 'Expect the First Half to last about an hour; then the Chairman will call for a recess.'
+          _ul do
+            ['agenda.txt', 'README.txt', 'https://www.apache.org/foundation/governance/meetings'].each do |f|
+              _li do
+                emit_link(cur_mtg_dir, f, MeetingUtil::MEETING_FILES[f])
+              end
+            end
+          end
+        end
+      end
+      
+      _whimsy_panel("Timeline: Meeting Recess To Vote Via Email (approx 40+ hours)", style: 'panel-info') do
+        _p do
+          _ 'Shortly after the Chairman calls the recess, the STeVe vote monitors will send you multiple emails with your voting keys.'
+          _ 'All voting is done via a simple web interface at vote.apache.org after you login with your Apache ID.'
+          _ul do
+            _li do
+              _a 'New Members Elected By Majority Yes/No/Abstain vote', href: 'https://www.apache.org/foundation/governance/meetings#how-member-votes-are-tallied'
+              _a 'Board Seats Are Elected By STV - ORDER OF YOUR VOTE MATTERS!', href: 'https://www.apache.org/foundation/governance/meetings#how-votes-for-the-board-are-tallied'
+            end
+          end
+        end
+      end
+      
+      _whimsy_panel("Timeline: Second Half Of Meeting (48 hours after #{mtg_date.strftime('%A, %d %B %Y')})", style: 'panel-primary') do
+        _p do
+          _ 'The Second Half Meeting is short - it\'s primarily announcing vote results and any last-minute announcements.  Members do not need to attend the second half; all results will be emailed or checked into SVN.'
+          _ 'Various data files about the meeting will be checked in within a day after the meeting for historical records.'
+          _ul do
+            ['record', 'attend', 'voter-tally', 'raw_board_votes.txt'].each do |f|
+              _li do
+                emit_link(cur_mtg_dir, f, MeetingUtil::MEETING_FILES[f])
+              end
+            end
+          end
+        end
+      end
+      
+      _whimsy_panel("Timeline: After This Year's Meeting", style: 'panel-default') do
+        _p do 
+          _ 'Shortly after the second half Meeting ends, '
+          _a '@TheASF twitter', href: 'https://twitter.com/theasf'
+          _ ' will formally announce the new board - please wait to retweet the official announcement.'
+          _span.text_warning 'IMPORTANT:'
+          _ ' Do not publicise the names of newly elected members!  In rare cases, the new candidate might not accept the honor.'
+        end
+        _p do
+          _span.text_primary 'If you nominated a new member:'
+          _ ' You must send an email with '
+          _a 'foundation/membership-application-email.txt', href: "#{FOUNDATION_SVN}membership-application-email.txt"
+          _ ' to formally invite the new member to fill out the application form.  Applications must be signed and submitted to the secretary within 30 days of the meeting to be valid.'
+        end
+      end
+      
+      # Most/all of these links should already be included above
       emit_meeting(cur_mtg_dir, meeting, meeting >= today)
       
       _whimsy_panel("Member Meeting History", style: 'panel-info') do