func reverseAppsArticle(a *app.App, art interface{}, checked map[*app.App]bool) (string, error) { checked[a] = true var articles []*article.Article aa, _ := reusableapp.AppData(a).(*appData) if aa != nil { articles = aa.Articles } switch x := art.(type) { case string: for _, v := range articles { if articleId(v) == x { return a.Reverse(ArticleHandlerName, v.Slug()) } } case *article.Article: return a.Reverse(ArticleHandlerName, x.Slug()) case article.Article: return a.Reverse(ArticleHandlerName, x.Slug()) } if p := a.Parent(); p != nil && !checked[p] { return reverseAppsArticle(p, art, checked) } for _, v := range a.Included() { if checked[v] { continue } if url, err := reverseAppsArticle(v, art, checked); err == nil { return url, nil } } if id, ok := art.(string); ok { return "", fmt.Errorf("no article with id %q found", id) } return "", fmt.Errorf("can't reverse Article from %T, must be *Article or string (article id)", art) }
// AppDataWithKey works similarly to AppData, but uses the provided key instead. // Also, if the data is not found in the *app.App passed in as the first argument, // its children apps are also searched. This allows reusable apps to retrieve their // additional data in contexts where the reusable app pointer is not available. // (e.g. in template plugins which are called from the parent app). See gnd.la/apps/users // for an example of this usage. func AppDataWithKey(a *app.App, key interface{}) interface{} { ra, _ := a.Get(key).(*App) if ra != nil { return ra.Data() } if key != reusableAppKey { for _, ia := range a.Included() { if data := AppDataWithKey(ia, key); data != nil { return data } } } return nil }