Copyright 2020 The Go Authors. All rights reserved. Use of this source code is governed by a BSD-style license that can be found in the LICENSE file.

package frontend

import (
	
	
	
	
	
	
	
	

	
	
	
	
	
	
	
	
)
UnitPage contains data needed to render the unit template.
type UnitPage struct {
Unit is the unit for this page.
Breadcrumb contains data used to render breadcrumb UI elements.
Title is the title of the page.
URLPath is the path suitable for links on the page. See the unitURLPath for details.
CanonicalURLPath is a permanent representation of the URL path for a unit. It uses the resolved module path and version. For example, if the latest version of /my.module/pkg is version v1.5.2, the canonical URL path for that unit would be /my.module@v1.5.2/pkg
The version string formatted for display.
LinkVersion is version string suitable for links used to compute latest badges.
LatestURL is a url pointing to the latest version of a unit.
LatestMinorClass is the CSS class that describes the current unit's minor version in relationship to the latest version of the unit.
Information about the latest major version of the module.
PageType is the type of page (pkg, cmd, dir, std, or mod).
PageLabels are the labels that will be displayed for a given page.
CanShowDetails indicates whether details can be shown or must be hidden due to issues like license restrictions.
Settings contains settings for the selected tab.
RedirectedFromPath is the path that redirected to the current page. If non-empty, a "redirected from" banner will be displayed (see content/static/html/helpers/_unit_header.tmpl).
Details contains data specific to the type of page being rendered.
	Details interface{}
}
serveUnitPage serves a unit page for a path using the paths, modules, documentation, readmes, licenses, and package_imports tables.
func ( *Server) ( context.Context,  http.ResponseWriter,  *http.Request,
	 internal.DataSource,  *urlPathInfo) ( error) {
	defer derrors.Wrap(&, "serveUnitPage(ctx, w, r, ds, %v)", )
	defer middleware.ElapsedStat(, "serveUnitPage")()

	 := .FormValue("tab")
Default to details tab when there is no tab param.
		 = tabMain
Redirect to clean URL path when tab param is invalid.
	if ,  := unitTabLookup[]; ! {
		http.Redirect(, , .URL.Path, http.StatusFound)
		return nil
	}

	,  := .GetUnitMeta(, .fullPath, .modulePath, .requestedVersion)
	if  != nil {
		if !errors.Is(, derrors.NotFound) {
			return 
		}
		return .servePathNotFoundPage(, , , .fullPath, .modulePath, .requestedVersion)
	}
Use GOOS and GOARCH query parameters to create a build context, which affects the documentation and synopsis. Omitting both results in an empty build context, which will match the first (and preferred) build context. It's also okay to provide just one (e.g. GOOS=windows), which will select the first doc with that value, ignoring the other one.
	 := internal.BuildContext{GOOS: .FormValue("GOOS"), GOARCH: .FormValue("GOARCH")}
	,  := fetchDetailsForUnit(, , , , , )
	if  != nil {
		return 
	}
	if .serveStats && .FormValue("m") == "json" {
		,  := json.Marshal()
		if  != nil {
			return fmt.Errorf("json.Marshal: %v", )
		}
		if ,  := .Write();  != nil {
			return fmt.Errorf("w.Write: %v", )
		}
		return nil
	}

	recordVersionTypeMetric(, .requestedVersion)
Since path@master is a moving target, we don't want it to be stale. As a result, we enqueue every request of path@master to the frontend task queue, which will initiate a fetch request depending on the last time we tried to fetch this module version. Use a separate context here to prevent the context from being canceled elsewhere before a task is enqueued.
		go func() {
			,  := context.WithTimeout(context.Background(), 1*time.Minute)
			defer ()
			log.Infof(, "serveUnitPage: Scheduling %q@%q to be fetched", .ModulePath, .requestedVersion)
			if ,  := .queue.ScheduleFetch(, .ModulePath, .requestedVersion, "", false);  != nil {
				log.Errorf(, "serveUnitPage(%q): scheduling fetch for %q@%q: %v",
					.URL.Path, .ModulePath, .requestedVersion, )
			}
		}()
	}

Redirect to clean URL path when tab param is invalid for the unit type.
		http.Redirect(, , .URL.Path, http.StatusFound)
		return nil
	}
If we've already called GetUnitMeta for an unknown module path and the latest version, pass it to GetLatestInfo to avoid a redundant call.
	var  *internal.UnitMeta
	if .modulePath == internal.UnknownModulePath && .requestedVersion == internal.LatestVersion {
		 = 
	}
	 := .GetLatestInfo(, .Path, .ModulePath, )
	var  string
	,  = cookie.Extract(, , cookie.AlternativeModuleFlash)
Don't fail, but don't display a banner either.
		log.Errorf(, "extracting AlternativeModuleFlash cookie: %v", )
	}
	 := unitTabLookup[]
	 := pageTitle()
	 := .newBasePage(, )
	.AllowWideContent = true
	 := linkVersion(.Version, .ModulePath)
	 := UnitPage{
		basePage:              ,
		Unit:                  ,
		Breadcrumb:            displayBreadcrumb(, .requestedVersion),
		Title:                 ,
		SelectedTab:           ,
		URLPath:               constructUnitURL(.Path, .ModulePath, .requestedVersion),
		CanonicalURLPath:      canonicalURLPath(),
		DisplayVersion:        displayVersion(.Version, .ModulePath),
		LinkVersion:           ,
		LatestURL:             constructUnitURL(.Path, .ModulePath, internal.LatestVersion),
		LatestMinorClass:      latestMinorClass(, ),
		LatestMajorVersionURL: .MajorUnitPath,
		PageLabels:            pageLabels(),
		PageType:              pageType(),
		RedirectedFromPath:    ,
	}
Show the banner if there was no error getting the latest major version, and it is different from the major version of the current module path.
	 := internal.MajorVersionForModule(.MajorModulePath)
	if  != "" &&  != internal.MajorVersionForModule(.ModulePath) {
		.LatestMajorVersion = 
	}

	.Details = 
	,  := .(*MainDetails)
	if  {
		.MetaDescription = metaDescription(strconv.Itoa(.ImportedByCount))
	}
	.servePage(, , .TemplateName, )
	return nil
}

func ( string,  internal.LatestInfo) string {
	 := "DetailsHeader-badge"
	switch {
	case .MinorVersion == "":
		 += "--unknown"
	case .MinorVersion ==  && !.UnitExistsAtMinor:
		 += "--notAtLatest"
	case .MinorVersion == :
		 += "--latest"
	default:
		 += "--goToLatest"
	}
	return 
}
metaDescription uses a safehtml escape hatch to build HTML used to render the <meta name="Description"> for unit pages as a workaround for https://github.com/google/safehtml/issues/6.
isValidTabForUnit reports whether the tab is valid for the given unit. It is assumed that tab is a key in unitTabLookup.
func ( string,  *internal.UnitMeta) bool {
	if  == tabLicenses && !.IsRedistributable {
		return false
	}
	if !.IsPackage() && ( == tabImports ||  == tabImportedBy) {
		return false
	}
	return true
}
constructUnitURL returns a URL path that refers to the given unit at the requested version. If requestedVersion is "latest", then the resulting path has no version; otherwise, it has requestedVersion.
func (, ,  string) string {
	if  == internal.LatestVersion {
		return "/" + 
	}
	 := linkVersion(, )
	if  ==  ||  == stdlib.ModulePath {
		return fmt.Sprintf("/%s@%s", , )
	}
	return fmt.Sprintf("/%s@%s/%s", , , strings.TrimPrefix(, +"/"))
}
canonicalURLPath constructs a URL path to the unit that always includes the resolved version.