eintopf.info@v0.13.16/web/place.go (about)

     1  // Copyright (C) 2022 The Eintopf authors
     2  //
     3  // This program is free software: you can redistribute it and/or modify
     4  // it under the terms of the GNU Affero General Public License as
     5  // published by the Free Software Foundation, either version 3 of the
     6  // License, or (at your option) any later version.
     7  //
     8  // This program is distributed in the hope that it will be useful,
     9  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    10  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    11  // GNU Affero General Public License for more details.
    12  //
    13  // You should have received a copy of the GNU Affero General Public License
    14  // along with this program.  If not, see <https://www.gnu.org/licenses/>.
    15  
    16  package web
    17  
    18  import (
    19  	"fmt"
    20  	"net/http"
    21  	"net/url"
    22  	"text/template"
    23  
    24  	"eintopf.info/internal/xerror"
    25  	"eintopf.info/internal/xtime"
    26  	"eintopf.info/service/eventsearch"
    27  	"eintopf.info/service/search"
    28  
    29  	"github.com/go-chi/chi/v5"
    30  )
    31  
    32  // PlacePage renders a place page.
    33  func (renderer *Renderer) PlacePage(w http.ResponseWriter, r *http.Request) {
    34  	id := chi.URLParam(r, "id")
    35  	if id == "" {
    36  		renderer.errorPage(w, r, xerror.NotFoundError{Err: fmt.Errorf("place not found: empty id"), Type: "place"})
    37  		return
    38  	}
    39  	id, err := url.QueryUnescape(id)
    40  	if err != nil {
    41  		renderer.errorPage(w, r, xerror.NotFoundError{Err: fmt.Errorf("place not found: invalid id=%s", id), Type: "place"})
    42  		return
    43  	}
    44  	result, err := renderer.search.Search(r.Context(), &search.Options{
    45  		Filters: []search.Filter{
    46  			&search.TermsFilter{Field: "type", Terms: []string{"place"}},
    47  			&search.TermsFilter{Field: "id", Terms: []string{id}},
    48  		},
    49  	})
    50  	if err != nil {
    51  		renderer.errorPage(w, r, err)
    52  		return
    53  	}
    54  	if len(result.Hits) != 1 {
    55  		renderer.errorPage(w, r, xerror.NotFoundError{Err: fmt.Errorf("place not found: id=%s", id), Type: "place"})
    56  		return
    57  	}
    58  
    59  	place := PlaceDocument{}
    60  	err = result.Hits[0].Unmarshal(&place)
    61  	if err != nil {
    62  		renderer.errorPage(w, r, fmt.Errorf("unmarshal place: id=%s: %w", id, err))
    63  		return
    64  	}
    65  
    66  	pastEvents, err := renderer.eventSearch.Search(r.Context(), eventsearch.Options{
    67  		Sort:           "date",
    68  		SortDescending: true,
    69  		PageSize:       5,
    70  		Filters: []eventsearch.Filter{
    71  			eventsearch.ListedFilter{Listed: true},
    72  			eventsearch.PlaceIDFilter{PlaceID: id},
    73  			eventsearch.StartRangeFilter{StartMax: xtime.NowInLocation(renderer.tz)},
    74  			eventsearch.MultidayRangeFilter{MultidayMax: iptr(1)},
    75  		},
    76  	})
    77  	if err != nil {
    78  		renderer.errorPage(w, r, fmt.Errorf("search past events: placeID=%s: %w", id, err))
    79  		return
    80  	}
    81  
    82  	futureEvents, err := renderer.eventSearch.Search(r.Context(), eventsearch.Options{
    83  		Sort:     "date",
    84  		PageSize: 5,
    85  		Filters: []eventsearch.Filter{
    86  			eventsearch.ListedFilter{Listed: true},
    87  			eventsearch.PlaceIDFilter{PlaceID: id},
    88  			eventsearch.StartRangeFilter{StartMin: xtime.NowInLocation(renderer.tz)},
    89  			eventsearch.MultidayRangeFilter{MultidayMax: iptr(1)},
    90  		},
    91  	})
    92  	if err != nil {
    93  		renderer.errorPage(w, r, fmt.Errorf("search future events: placeID=%s: %w", id, err))
    94  		return
    95  	}
    96  
    97  	metaTags := MetaTags{Title: place.Name, Description: place.Description, Image: place.Image, NotPublished: !place.Published}
    98  
    99  	err = renderer.renderPage(w, r, "place", map[string]any{
   100  		"Place":        place,
   101  		"Options":      eventListOptions{},
   102  		"PastEvents":   pastEvents.Events,
   103  		"FutureEvents": futureEvents.Events,
   104  		"Meta":         metaTags,
   105  	}, template.FuncMap{
   106  		"placeShouldRenderMap": func(p PlaceDocument) bool {
   107  			if p.Lat == 0 || p.Lng == 0 {
   108  				return false
   109  			}
   110  			return true
   111  		},
   112  	})
   113  	if err != nil {
   114  		renderer.errorPage(w, r, err)
   115  		return
   116  	}
   117  }