[release-branch.go1.7] godoc/dl: clean up display of stable versions

Under "Stable Versions", only show the latest minor versions of the most two
recent major versions.

Under each of those versions, move the downloads for primary ports to
the top of the table and the other downloads under a separate
"Other Ports" heading.

Add a listing for every stable Go version under "All versions",
collapsed by default.

Fixes golang/go#17574.
Updates golang/go#17018.

Change-Id: I7d74fef1b44a319a4bf65dedf24ffb27a7009f60
Reviewed-on: https://go-review.googlesource.com/34824
Reviewed-by: Brad Fitzpatrick <bradfitz@golang.org>
Reviewed-on: https://go-review.googlesource.com/35095
Reviewed-by: Chris Broadfoot <cbro@golang.org>
diff --git a/godoc/dl/dl.go b/godoc/dl/dl.go
index ed6e590..d86e784 100644
--- a/godoc/dl/dl.go
+++ b/godoc/dl/dl.go
@@ -98,6 +98,22 @@
 	return fmt.Sprintf("%.0fMB", float64(f.Size)/mb)
 }
 
+var primaryPorts = map[string]bool{
+	"darwin/amd64":  true,
+	"linux/386":     true,
+	"linux/amd64":   true,
+	"linux/armv6l":  true,
+	"windows/386":   true,
+	"windows/amd64": true,
+}
+
+func (f File) PrimaryPort() bool {
+	if f.Kind == "source" {
+		return true
+	}
+	return primaryPorts[f.OS+"/"+f.Arch]
+}
+
 func (f File) Highlight() bool {
 	switch {
 	case f.Kind == "source":
@@ -122,10 +138,11 @@
 }
 
 type Release struct {
-	Version string
-	Stable  bool
-	Files   []File
-	Visible bool // show files on page load
+	Version        string
+	Stable         bool
+	Files          []File
+	Visible        bool // show files on page load
+	SplitPortTable bool // whether files should be split by primary/other ports.
 }
 
 type Feature struct {
@@ -164,9 +181,9 @@
 
 // data to send to the template; increment cacheKey if you change this.
 type listTemplateData struct {
-	Featured         []Feature
-	Stable, Unstable []Release
-	LoginURL         string
+	Featured                  []Feature
+	Stable, Unstable, Archive []Release
+	LoginURL                  string
 }
 
 var (
@@ -196,7 +213,7 @@
 			log.Errorf(c, "error listing: %v", err)
 			return
 		}
-		d.Stable, d.Unstable = filesToReleases(fs)
+		d.Stable, d.Unstable, d.Archive = filesToReleases(fs)
 		if len(d.Stable) > 0 {
 			d.Featured = filesToFeatured(d.Stable[0].Files)
 		}
@@ -229,7 +246,7 @@
 	return
 }
 
-func filesToReleases(fs []File) (stable, unstable []Release) {
+func filesToReleases(fs []File) (stable, unstable, archive []Release) {
 	sort.Sort(fileOrder(fs))
 
 	var r *Release
@@ -239,12 +256,30 @@
 			return
 		}
 		if r.Stable {
-			if len(stable) == 0 {
-				// Display files for latest stable release.
-				stableMaj, stableMin, _ = parseVersion(r.Version)
-				r.Visible = len(stable) == 0
+			// Show all stable versions in the archive
+			archive = append(archive, *r)
+
+			// Show the most two recent major version under "Stable".
+			if len(stable) < 2 {
+				if len(stable) == 0 {
+					// Most recent stable version.
+					stableMaj, stableMin, _ = parseVersion(r.Version)
+				} else if maj, _, _ := parseVersion(r.Version); maj == stableMaj {
+					// Older minor version of most recent major version.
+					return
+				}
+				// Split the file list into primary/other ports for the stable releases.
+				// NOTE(cbro): This is only done for stable releases because maintaining the historical
+				// nature of primary/other ports for older versions is infeasible.
+				// If freebsd is considered primary some time in the future, we'd not want to
+				// mark all of the older freebsd binaries as "primary".
+				// It might be better if we set that as a flag when uploading.
+				r.SplitPortTable = true
+				r.Visible = true // Toggle open all stable releases.
+				stable = append(stable, *r)
+			} else if len(stable) == 1 {
+				// Show the second most recent major version (i.e., security release)
 			}
-			stable = append(stable, *r)
 			return
 		}
 		if len(unstable) != 0 {
@@ -257,7 +292,6 @@
 			// latest stable release.
 			return
 		}
-		r.Visible = true
 		unstable = append(unstable, *r)
 	}
 	for _, f := range fs {
diff --git a/godoc/dl/dl_test.go b/godoc/dl/dl_test.go
index 9d6a057..05d85ad 100644
--- a/godoc/dl/dl_test.go
+++ b/godoc/dl/dl_test.go
@@ -70,3 +70,68 @@
 		t.Errorf("sort order is\n%s\nwant:\n%s", got, want)
 	}
 }
+
+func TestFilesToReleases(t *testing.T) {
+	fs := []File{
+		{Version: "go1.7.4", OS: "darwin"},
+		{Version: "go1.7.4", OS: "windows"},
+		{Version: "go1.7", OS: "darwin"},
+		{Version: "go1.7", OS: "windows"},
+		{Version: "go1.6.2", OS: "darwin"},
+		{Version: "go1.6.2", OS: "windows"},
+		{Version: "go1.6", OS: "darwin"},
+		{Version: "go1.6", OS: "windows"},
+		{Version: "go1.5.2", OS: "darwin"},
+		{Version: "go1.5.2", OS: "windows"},
+		{Version: "go1.5", OS: "darwin"},
+		{Version: "go1.5", OS: "windows"},
+		{Version: "go1.5beta1", OS: "windows"},
+	}
+	stable, unstable, all := filesToReleases(fs)
+	if got, want := len(stable), 2; want != got {
+		t.Errorf("len(stable): got %v, want %v", got, want)
+	} else {
+		if got, want := stable[0].Version, "go1.7.4"; want != got {
+			t.Errorf("stable[0].Version: got %v, want %v", got, want)
+		}
+		if got, want := stable[1].Version, "go1.6.2"; want != got {
+			t.Errorf("stable[1].Version: got %v, want %v", got, want)
+		}
+	}
+	if got, want := len(unstable), 0; want != got {
+		t.Errorf("len(unstable): got %v, want %v", got, want)
+	}
+	if got, want := len(all), 6; want != got {
+		t.Errorf("len(all): got %v, want %v", got, want)
+	}
+}
+
+func TestOldUnstableNotShown(t *testing.T) {
+	fs := []File{
+		{Version: "go1.7.4"},
+		{Version: "go1.7"},
+		{Version: "go1.7beta1"},
+	}
+	_, unstable, _ := filesToReleases(fs)
+	if len(unstable) != 0 {
+		t.Errorf("got unstable, want none")
+	}
+}
+
+func TestUnstableShown(t *testing.T) {
+	fs := []File{
+		{Version: "go1.8beta2"},
+		{Version: "go1.8rc1"},
+		{Version: "go1.7.4"},
+		{Version: "go1.7"},
+		{Version: "go1.7beta1"},
+	}
+	_, unstable, _ := filesToReleases(fs)
+	if got, want := len(unstable), 1; got != want {
+		t.Fatalf("len(unstable): got %v, want %v", got, want)
+	}
+	// show rcs ahead of betas.
+	if got, want := unstable[0].Version, "go1.8rc1"; got != want {
+		t.Fatalf("unstable[0].Version: got %v, want %v", got, want)
+	}
+}
diff --git a/godoc/dl/tmpl.go b/godoc/dl/tmpl.go
index c4c31a8..6683241 100644
--- a/godoc/dl/tmpl.go
+++ b/godoc/dl/tmpl.go
@@ -134,6 +134,19 @@
 {{template "releases" .}}
 {{end}}
 
+{{with .Archive}}
+<div class="toggle">
+  <a name="archive"></a>
+  <div class="collapsed">
+    <h3 class="toggleButton" title="Click to show versions">All versions▹</h3>
+  </div>
+  <div class="expanded">
+    <h3 class="toggleButton" title="Click to hide versions">All versions▾</h3>
+    {{template "releases" .}}
+  </div>
+</div>
+{{end}}
+
 
 <!-- Disabled for now; there's no admin functionality yet.
 <p>
@@ -205,7 +218,8 @@
 
 {{define "releases"}}
 {{range .}}
-<div class="toggle{{if .Visible}}Visible{{end}}" id="{{.Version}}">
+<div class="toggle{{if .Visible}}Visible{{end}}">
+	<a name="{{.Version}}"></a> {{/* NOTE(cbro): versions may show multiple times. Don't use "id". */}}
 	<div class="collapsed">
 		<h2 class="toggleButton" title="Click to show downloads for this version">{{.Version}} ▹</h2>
 	</div>
@@ -219,7 +233,7 @@
 </pre>
 			<p>Then, use the <code>{{.Version}}</code> command instead of the <code>go</code> command to use {{.Version}}.</p>
 		{{end}}
-		{{template "files" .Files}}
+		{{template "files" .}}
 	</div>
 </div>
 {{end}}
@@ -235,10 +249,22 @@
   <th>Arch</th>
   <th>Size</th>
   {{/* Use the checksum type of the first file for the column heading. */}}
-  <th>{{(index . 0).ChecksumType}} Checksum</th>
+  <th>{{(index .Files 0).ChecksumType}} Checksum</th>
 </tr>
 </thead>
-{{range .}}
+{{if .SplitPortTable}}
+  {{range .Files}}{{if .PrimaryPort}}{{template "file" .}}{{end}}{{end}}
+
+  {{/* TODO(cbro): add a link to an explanatory doc page */}}
+  <tr class="first"><th colspan="6" class="first">Other Ports</th></tr>
+  {{range .Files}}{{if not .PrimaryPort}}{{template "file" .}}{{end}}{{end}}
+{{else}}
+  {{range .Files}}{{template "file" .}}{{end}}
+{{end}}
+</table>
+{{end}}
+
+{{define "file"}}
 <tr{{if .Highlight}} class="highlight"{{end}}>
   <td class="filename"><a class="download" href="{{.URL}}">{{.Filename}}</a></td>
   <td>{{pretty .Kind}}</td>
@@ -247,12 +273,6 @@
   <td>{{.PrettySize}}</td>
   <td><tt>{{.PrettyChecksum}}</tt></td>
 </tr>
-{{else}}
-<tr>
-  <td colspan="5">No downloads available.</td>
-</tr>
-{{end}}
-</table>
 {{end}}
 
 {{define "download"}}