From 5c02bfb0002a43f212e38979e1538f0adcae25cb Mon Sep 17 00:00:00 2001 From: Penar Musaraj Date: Tue, 27 Aug 2019 14:05:37 -0400 Subject: [PATCH] FEATURE: Site settings for linking with iOS/Android native apps - Adds support for iOS Universal Links via an `apple-app-site-association` endpoint Adds support for Google Digital Asset Links at the `.well-known/assetlinks.json` endpoint --- app/controllers/metadata_controller.rb | 10 +++++ config/locales/server.en.yml | 3 ++ config/routes.rb | 2 + config/site_settings.yml | 10 +++++ spec/requests/metadata_controller_spec.rb | 49 +++++++++++++++++++++++ 5 files changed, 74 insertions(+) diff --git a/app/controllers/metadata_controller.rb b/app/controllers/metadata_controller.rb index 8148b9c0fd0..189852afb15 100644 --- a/app/controllers/metadata_controller.rb +++ b/app/controllers/metadata_controller.rb @@ -12,6 +12,16 @@ class MetadataController < ApplicationController render template: "metadata/opensearch.xml" end + def app_association_android + raise Discourse::NotFound unless SiteSetting.app_association_android.present? + render plain: SiteSetting.app_association_android, content_type: 'application/json' + end + + def app_association_ios + raise Discourse::NotFound unless SiteSetting.app_association_ios.present? + render plain: SiteSetting.app_association_ios, content_type: 'application/json' + end + private def default_manifest diff --git a/config/locales/server.en.yml b/config/locales/server.en.yml index 9a86c6a3733..a1954385f5f 100644 --- a/config/locales/server.en.yml +++ b/config/locales/server.en.yml @@ -1929,6 +1929,9 @@ en: native_app_install_banner_android: "Displays DiscourseHub app banner on Android devices to regular users (trust level 1 and up)." + app_association_android: "Contents of .well-known/assetlinks.json endpoint, used for Google's Digital Asset Links API." + app_association_ios: "Contents of apple-app-site-association endpoint, used to create Universal Links between this site and iOS apps." + share_anonymized_statistics: "Share anonymized usage statistics." auto_handle_queued_age: "Automatically handle records that are waiting to be reviewed after this many days. Flags will be ignored. Queued posts and users will be rejected. Set to 0 to disable this feature." diff --git a/config/routes.rb b/config/routes.rb index befb4d1b78f..7b638267d6a 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -812,6 +812,8 @@ Discourse::Application.routes.draw do get "offline.html" => "offline#index" get "manifest.webmanifest" => "metadata#manifest", as: :manifest get "manifest.json" => "metadata#manifest" + get ".well-known/assetlinks.json" => "metadata#app_association_android" + get "apple-app-site-association" => "metadata#app_association_ios", format: false get "opensearch" => "metadata#opensearch", constraints: { format: :xml } scope "/tags" do diff --git a/config/site_settings.yml b/config/site_settings.yml index bd25b6f28af..99c53f32da8 100644 --- a/config/site_settings.yml +++ b/config/site_settings.yml @@ -1931,6 +1931,16 @@ uncategorized: default: "iPad|iPhone" hidden: true + app_association_android: + client: false + default: "" + textarea: true + + app_association_ios: + client: false + default: "" + textarea: true + share_anonymized_statistics: true auto_handle_queued_age: diff --git a/spec/requests/metadata_controller_spec.rb b/spec/requests/metadata_controller_spec.rb index 473d65d6783..eaa8f05da24 100644 --- a/spec/requests/metadata_controller_spec.rb +++ b/spec/requests/metadata_controller_spec.rb @@ -101,4 +101,53 @@ RSpec.describe MetadataController do expect(response.content_type).to eq('application/xml') end end + + describe '#app_association_android' do + it 'returns 404 by default' do + get "/.well-known/assetlinks.json" + expect(response.status).to eq(404) + end + + it 'returns the right output' do + SiteSetting.app_association_android = <<~EOF + [{ + "relation": ["delegate_permission/common.handle_all_urls"], + "target" : { "namespace": "android_app", "package_name": "com.example.app", + "sha256_cert_fingerprints": ["hash_of_app_certificate"] } + }] + EOF + get "/.well-known/assetlinks.json" + + expect(response.status).to eq(200) + expect(response.body).to include("hash_of_app_certificate") + expect(response.body).to include("com.example.app") + expect(response.content_type).to eq('application/json') + end + end + + describe '#app_association_ios' do + it 'returns 404 by default' do + get "/apple-app-site-association" + expect(response.status).to eq(404) + end + + it 'returns the right output' do + SiteSetting.app_association_ios = <<~EOF + { + "applinks": { + "apps": [] + } + } + EOF + get "/apple-app-site-association" + + expect(response.status).to eq(200) + expect(response.body).to include("applinks") + expect(response.content_type).to eq('application/json') + + get "/apple-app-site-association.json" + expect(response.status).to eq(404) + end + end + end