diff --git a/app/assets/images/logos/book-small.png b/app/assets/images/logos/book-small.png
index d19bbfcf..96733bdd 100644
Binary files a/app/assets/images/logos/book-small.png and b/app/assets/images/logos/book-small.png differ
diff --git a/app/assets/images/screenshots/integration-references.png b/app/assets/images/screenshots/integration-references.png
new file mode 100644
index 00000000..c3b5b640
Binary files /dev/null and b/app/assets/images/screenshots/integration-references.png differ
diff --git a/app/assets/images/screenshots/integrations.png b/app/assets/images/screenshots/integrations.png
new file mode 100644
index 00000000..2425a3c1
Binary files /dev/null and b/app/assets/images/screenshots/integrations.png differ
diff --git a/app/assets/images/screenshots/page-types.png b/app/assets/images/screenshots/page-types.png
new file mode 100644
index 00000000..d5685922
Binary files /dev/null and b/app/assets/images/screenshots/page-types.png differ
diff --git a/app/assets/javascripts/api_docs.coffee b/app/assets/javascripts/api_docs.coffee
new file mode 100644
index 00000000..24f83d18
--- /dev/null
+++ b/app/assets/javascripts/api_docs.coffee
@@ -0,0 +1,3 @@
+# Place all the behaviors and hooks related to the matching controller here.
+# All this logic will automatically be available in application.js.
+# You can use CoffeeScript in this file: http://coffeescript.org/
diff --git a/app/assets/javascripts/integration_authorizations.coffee b/app/assets/javascripts/integration_authorizations.coffee
new file mode 100644
index 00000000..24f83d18
--- /dev/null
+++ b/app/assets/javascripts/integration_authorizations.coffee
@@ -0,0 +1,3 @@
+# Place all the behaviors and hooks related to the matching controller here.
+# All this logic will automatically be available in application.js.
+# You can use CoffeeScript in this file: http://coffeescript.org/
diff --git a/app/assets/stylesheets/api_docs.scss b/app/assets/stylesheets/api_docs.scss
new file mode 100644
index 00000000..5d0b541a
--- /dev/null
+++ b/app/assets/stylesheets/api_docs.scss
@@ -0,0 +1,21 @@
+.api-docs {
+ h1 {
+ font-size: 24px;
+ }
+
+ h2 {
+ font-size: 20px;
+ }
+
+ h3 {
+ font-size: 16px;
+ }
+
+ .code {
+ font-family: monospace;
+ color: white;
+ background: black;
+ white-space: pre-wrap;
+ padding: 0 20px;
+ }
+}
\ No newline at end of file
diff --git a/app/assets/stylesheets/integration_authorizations.scss b/app/assets/stylesheets/integration_authorizations.scss
new file mode 100644
index 00000000..43397831
--- /dev/null
+++ b/app/assets/stylesheets/integration_authorizations.scss
@@ -0,0 +1,3 @@
+// Place all the styles related to the IntegrationAuthorizations controller here.
+// They will automatically be included in application.css.
+// You can use Sass (SCSS) here: https://sass-lang.com/
diff --git a/app/assets/stylesheets/sidenav.css.scss b/app/assets/stylesheets/sidenav.css.scss
index cff23eaf..acb8221f 100644
--- a/app/assets/stylesheets/sidenav.css.scss
+++ b/app/assets/stylesheets/sidenav.css.scss
@@ -41,3 +41,7 @@
transform: rotate(-90deg);
}
}
+
+#page-lookup-list {
+ min-width: 33em;
+}
\ No newline at end of file
diff --git a/app/authorizers/content_authorizer.rb b/app/authorizers/content_authorizer.rb
index c8d7f28d..ef17875d 100644
--- a/app/authorizers/content_authorizer.rb
+++ b/app/authorizers/content_authorizer.rb
@@ -1,11 +1,11 @@
class ContentAuthorizer < ApplicationAuthorizer
def readable_by? user
[
- PermissionService.user_owns_any_containing_universe?(user: user, content: resource),
- PermissionService.user_owns_content?(user: user, content: resource),
- PermissionService.content_is_public?(content: resource),
- PermissionService.content_is_in_a_public_universe?(content: resource),
- PermissionService.user_can_contribute_to_containing_universe?(user: user, content: resource)
+ ::PermissionService.user_owns_any_containing_universe?(user: user, content: resource),
+ ::PermissionService.user_owns_content?(user: user, content: resource),
+ ::PermissionService.content_is_public?(content: resource),
+ ::PermissionService.content_is_in_a_public_universe?(content: resource),
+ ::PermissionService.user_can_contribute_to_containing_universe?(user: user, content: resource)
].any?
end
diff --git a/app/controllers/api/api_docs_controller.rb b/app/controllers/api/api_docs_controller.rb
new file mode 100644
index 00000000..66c7b78c
--- /dev/null
+++ b/app/controllers/api/api_docs_controller.rb
@@ -0,0 +1,29 @@
+module Api
+ class ApiDocsController < ApplicationController
+ layout 'developer', except: [:integrations]
+
+ before_action :authenticate_user!, except: [:index, :docs, :references]
+
+ def index
+ end
+
+ def docs
+ end
+
+ def integrations
+ end
+
+ def pricing
+ end
+
+ def applications
+ @applications = current_user.application_integrations
+ end
+
+ def approvals
+ end
+
+ def references
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/api/application_integrations_controller.rb b/app/controllers/api/application_integrations_controller.rb
new file mode 100644
index 00000000..9025fde2
--- /dev/null
+++ b/app/controllers/api/application_integrations_controller.rb
@@ -0,0 +1,69 @@
+module Api
+ class ApplicationIntegrationsController < ApplicationController
+ layout 'developer'
+
+ before_action :authenticate_user!
+ before_action :set_integration, only: [:show, :authorize, :edit, :update, :destroy]
+
+ # GET /application_integrations
+ def index
+ @applications = current_user.application_integrations
+ end
+
+ # GET /application_integrations/1
+ def show
+ end
+
+ def authorize
+ end
+
+ # GET /application_integrations/new
+ def new
+ @integration = ApplicationIntegration.new
+ end
+
+ # GET /application_integrations/1/edit
+ def edit
+ end
+
+ # POST /application_integrations
+ def create
+ @integration = ApplicationIntegration.new(application_integration_params.merge({user: current_user}))
+
+ if @integration.save
+ redirect_to api_application_path(@integration), notice: 'Application integration was successfully created.'
+ else
+ render :new
+ end
+ end
+
+ # PATCH/PUT /application_integrations/1
+ def update
+ if @integration.update(application_integration_params)
+ redirect_to @integration, notice: 'Application integration was successfully updated.'
+ else
+ render :edit
+ end
+ end
+
+ # DELETE /application_integrations/1
+ def destroy
+ @integration.destroy
+ redirect_to application_integrations_url, notice: 'Application integration was successfully destroyed.'
+ end
+
+ private
+
+ # Use callbacks to share common setup or constraints between actions.
+ def set_integration
+ @application_integration = current_user.application_integrations.find_by(id: params[:id])
+ end
+
+ # Only allow a trusted parameter "white list" through.
+ def application_integration_params
+ params.require(:application_integration).permit(
+ :name, :description, :organization_name, :organization_url, :website_url, :privacy_policy_url, :authorization_callback_url
+ )
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/api/integration_authorizations_controller.rb b/app/controllers/api/integration_authorizations_controller.rb
new file mode 100644
index 00000000..e5a47643
--- /dev/null
+++ b/app/controllers/api/integration_authorizations_controller.rb
@@ -0,0 +1,24 @@
+module Api
+ class IntegrationAuthorizationsController < ApplicationController
+ protect_from_forgery
+
+ def create
+ authorization = IntegrationAuthorization.create(integration_authorization_params.merge({
+ user_id: current_user.id,
+ referral_url: request.referrer,
+ ip_address: request.remote_ip,
+ origin: request.headers['HTTP_ORIGIN'],
+ content_type: request.headers['CONTENT_TYPE'],
+ user_agent: request.headers['HTTP_USER_AGENT'],
+ user_token: SecureRandom.hex(24)
+ }))
+ return redirect_to(authorization.application_integration.authorization_callback_url + "?token=#{authorization.user_token}")
+ end
+
+ private
+
+ def integration_authorization_params
+ params.require(:integration_authorization).permit(:application_integration_id)
+ end
+ end
+end
\ No newline at end of file
diff --git a/app/controllers/api/v1/api_controller.rb b/app/controllers/api/v1/api_controller.rb
index 92bf5cb2..74a6d6cc 100644
--- a/app/controllers/api/v1/api_controller.rb
+++ b/app/controllers/api/v1/api_controller.rb
@@ -1,6 +1,96 @@
module Api
module V1
class ApiController < ApplicationController
+ before_action :initialize_updates_used_tracker
+
+ before_action :authenticate_application!
+ before_action :authenticate_api_user!
+
+ after_action :log_api_request
+
+ def authenticate_application!
+ @application_integration = ApplicationIntegration.find_by(application_token: params[:application_token])
+
+ unless @application_integration
+ @request_success = :error
+ log_api_request
+
+ render json: {
+ "Error" => "Invalid application_token",
+ "Param" => "application_token",
+ "Token" => params[:application_token]
+ }
+ return
+ end
+ end
+
+ def authenticate_api_user!
+ @authorization = @application_integration.integration_authorizations.find_by(user_token: params[:authorization_token])
+ # todo error on this if not set
+
+ @current_api_user = @authorization.try(:user)
+ unless @current_api_user
+ @request_success = :error
+ log_api_request
+
+ render json: {
+ "Error" => "Invalid authorization_token",
+ "Param" => "authorization_token",
+ "Token" => params[:authorization_token]
+ }
+ return
+ end
+ end
+
+ def initialize_updates_used_tracker
+ @updates_used_this_request = 0
+ end
+
+ def log_api_request
+ ApiRequest.create!(
+ application_integration: @application_integration,
+ integration_authorization: @authorization,
+ result: @request_success || :success,
+ updates_used: @updates_used_this_request,
+ ip_address: request.remote_ip
+ )
+ end
+
+ # Content page list endpoints
+ Rails.application.config.content_types[:all].each do |content_type|
+ define_method(content_type.name.downcase.pluralize) do
+ pages = @current_api_user.send(content_type.name.downcase.pluralize)
+
+ render json: pages.map { |page|
+ {
+ id: page.id,
+ name: page.name,
+ description: page.description,
+ universe: page.try(:universe).nil? ? nil : {
+ id: page.universe_id,
+ name: page.universe.name
+ },
+ meta: {
+ created_at: page.created_at,
+ updated_at: page.updated_at
+ }
+ }
+ }
+ end
+ end
+
+ # Content page show endpoints
+ Rails.application.config.content_types[:all].each do |content_type|
+ define_method(content_type.name.downcase) do
+ page = content_type.find_by(id: params[:id])
+
+ if page && page.readable_by?(@current_api_user || User.new)
+ render json: ApiContentSerializer.new(page, include_blank_fields: params.fetch(:include_blank_fields, false)).data
+ else
+ render json: { error: "Page not found" }
+ end
+ end
+ end
end
end
end
\ No newline at end of file
diff --git a/app/helpers/api_docs_helper.rb b/app/helpers/api_docs_helper.rb
new file mode 100644
index 00000000..a5054aa8
--- /dev/null
+++ b/app/helpers/api_docs_helper.rb
@@ -0,0 +1,2 @@
+module ApiDocsHelper
+end
diff --git a/app/helpers/application_integrations_helper.rb b/app/helpers/application_integrations_helper.rb
new file mode 100644
index 00000000..861ebf7c
--- /dev/null
+++ b/app/helpers/application_integrations_helper.rb
@@ -0,0 +1,2 @@
+module ApplicationIntegrationsHelper
+end
diff --git a/app/helpers/integration_authorizations_helper.rb b/app/helpers/integration_authorizations_helper.rb
new file mode 100644
index 00000000..7c5fc8e2
--- /dev/null
+++ b/app/helpers/integration_authorizations_helper.rb
@@ -0,0 +1,2 @@
+module IntegrationAuthorizationsHelper
+end
diff --git a/app/javascript/components/PageLookupSidebar.js b/app/javascript/components/PageLookupSidebar.js
index 209705a1..6fcc8364 100644
--- a/app/javascript/components/PageLookupSidebar.js
+++ b/app/javascript/components/PageLookupSidebar.js
@@ -13,16 +13,17 @@ import Divider from '@material-ui/core/Divider';
import ListItem from '@material-ui/core/ListItem';
import ListItemIcon from '@material-ui/core/ListItemIcon';
import ListItemText from '@material-ui/core/ListItemText';
-import InboxIcon from '@material-ui/icons/MoveToInbox';
-import MailIcon from '@material-ui/icons/Mail';
import Button from '@material-ui/core/Button';
import ListSubheader from '@material-ui/core/ListSubheader';
import ExpandLess from '@material-ui/icons/ExpandLess';
import ExpandMore from '@material-ui/icons/ExpandMore';
import StarBorder from '@material-ui/icons/StarBorder';
+import HelpIcon from '@material-ui/icons/Help';
import Collapse from '@material-ui/core/Collapse';
+import axios from 'axios';
+
class PageLookupSidebar extends React.Component {
constructor(props) {
@@ -36,56 +37,37 @@ class PageLookupSidebar extends React.Component {
};
}
- loadPage(page_type, page_id) {
+ async loadPage(page_type, page_id) {
this.setDrawerVisible(true);
-
- // show loading icon
+ this.setState({ show_data: false });
// make api request
-
- // hide loading icon
-
- // load response into list
- this.setState({
- page_data: {
- name: page_type + ' ' + 'Bob',
- categories: [
- {
- id: 1,
- label: 'General',
- fields: [
- {
- label: 'Name',
- value: 'Bob',
- type: 'text'
- },
- {
- label: 'Age',
- value: '55',
- type: 'text'
- }
- ]
- },
- {
- id: 2,
- label: 'Family',
- fields: [
- {
- label: 'Mom',
- value: 'Robin',
- type: 'link',
- link: ['Character', 534]
- }
-
- ]
- }
- ]
+ await axios.get(
+ "/api/v1/" + page_type.toLowerCase() + "/" + page_id
+ + '?application_token=4756de490e82956dc6329e6650aaec664e27ccd27e153e2f'
+ + '&authorization_token=167bb93139303904cf67f6480a29e71c9f1eaf7a28e902e1',
+ {
+ headers: {
+ 'Content-Type': 'application/json',
+ 'Accept': 'application/json'
+ }
}
+ ).then(response => {
+ console.log("get request");
+ console.log(response.data);
+
+ // load response into list
+ this.setState({
+ page_data: response.data,
+ show_data: true,
+ page_type: page_type
+ });
+
+ }).catch(err => {
+ console.log(err);
+ return null;
});
- console.log("setting show_data = true");
- this.setState({ show_data: true });
- // this.state.show_data = true;
console.log("show data? " + this.state.show_data);
};
@@ -99,6 +81,37 @@ class PageLookupSidebar extends React.Component {
}});
}
+ fieldData(field) {
+ switch (field.type) {
+ case "name":
+ case "text_area":
+ return (
+
+POST /api/v1/<%= content_type.name.downcase.pluralize %> +
++ The token for your application. +
++ The authorization token for your user. +
++ This hash should pass field IDs as keys with strings to set that field's initial value to. For example, +
+{ + "id": 12345, + "name": "Some <%= content_type.name %>", +<% unless content_type.name == Universe.name %> + "universe_id": 2, +<% end %> + "meta": { + "created_at": "2020-02-01 08:24:20 UTC", + "updated_at": "2020-02-09 06:57:12 UTC" + }, + "categories": { + "Overview": { + "fields": [ + { + "id": 123, + "label": "Description", + "value": "Some Description" + }, + { + "id": 124, + "label": "Another Field", + "value": "Some other value" + }, + ... + ], + }, + ... + }, + "references": [...] +} +
++ Not supported: + Deleting <%= content_type.name.downcase.pluralize %> is currently not supported over the API. + Please ask your user to delete their <%= content_type.name.downcase %> manually, or + <%= link_to 'contact us', 'https://github.com/indentlabs/notebook' %> if you need this permission for your application. +
\ No newline at end of file diff --git a/app/views/api/api_docs/endpoints/content/_fetch_a_specific_content.html.erb b/app/views/api/api_docs/endpoints/content/_fetch_a_specific_content.html.erb new file mode 100644 index 00000000..0c91ffff --- /dev/null +++ b/app/views/api/api_docs/endpoints/content/_fetch_a_specific_content.html.erb @@ -0,0 +1,106 @@ ++GET /api/v1/<%= content_type.name.downcase %>/<id> +
++ The token for your application. +
++ The authorization token for your user. +
++ The ID of the <%= content_type.name.downcase %> you're requesting. +
++ The API only returns fields that have been answered by the user. If you wish to return all fields regardless + of whether they have an answer or not, you can set include_blank_fields to true. +
++{ + "id": 1, + "name": "Some <%= content_type.name %>", + "description": "A description of this page", +<% unless content_type.name == Universe.name %> + "universe": { + "id": 134, + "name": My Super Amazing Universe + }, +<% end %> + "meta": { + "created_at": "2020-02-01 08:24:20 UTC", + "updated_at": "2020-02-09 06:57:12 UTC" + }, + "categories": [ + { + "id": 123, + "label": "Overview", + "fields": [ + { + "id": 123, + "type": "text_area", + "label": "Coolness factor", + "value": "Off the charts" + }, + { + "id": 124, + "label": "Related Buildings", + "type": "link", + "value": [ + { + "id": 345, + "type": "Building", + "name": "The Office of Examples", + }, + ... + ] + }, + ... + ], + }, + ... + ], + "references": [TBD] +} +
++GET /api/v1/<%= content_type.name.downcase.pluralize %> +
++ The token for your application. +
++ The authorization token for your user. +
++ Limit <%= content_type.name.downcase.pluralize%> returned to only those within a particular universe.
++[ + { + "id": 1, + "name": "Some <%= content_type.name %>", + "description": "This is a user-supplied description of the page", +<% unless content_type.name == Universe.name %> + "universe": { + "id": 2, + "name": "The Great Story World" + } +<% end %> + "meta": { + "created_at": "2020-02-01 08:24:20 UTC", + "updated_at": "2020-02-09 06:57:12 UTC" + }, + }, + { + "id": 2, + "name": "Some other <%= content_type.name %>", + "description": "This is an even better page", +<% unless content_type.name == Universe.name %> + "universe": { + "id": 2, + "name": "The Great Story World" + } +<% end %> + "meta": { + "created_at": "2020-02-01 08:24:20 UTC", + "updated_at": "2020-02-09 06:57:12 UTC" + }, + }, + ... +] +
++POST /api/v1/<%= content_type.name.downcase.pluralize %>/<id> +
++ The token for your application. +
++ The authorization token for your user. +
++ The ID of the <%= content_type.name.downcase %> you're modifying. +
++ This hash should pass field IDs as keys with strings to update that field's value to. For example, +
+{ + "id": 1, + "name": "Some <%= content_type.name %>", +<% unless content_type.name == Universe.name %> + "universe_id": 2, +<% end %> + "meta": { + "created_at": "2020-02-01 08:24:20 UTC", + "updated_at": "2020-02-09 06:57:12 UTC" + }, + "categories": { + "Overview": { + "fields": [ + { + "id": 123, + "label": "Description", + "value": "Some Description" + }, + { + "id": 124, + "label": "Another Field", + "value": "Some other value" + }, + ... + ], + }, + ... + }, + "references": [...] +} +
++ <%= link_to "Click here to read more about how online references work on Notebook.ai.", api_references_path %> +
+<% end %> ++POST /api/v1/<%= content_type.name.downcase.pluralize %>/<id>/references +
++ The token for your application. +
++ The authorization token for your user. +
++ The ID of the <%= content_type.name.downcase %> you're adding a reference to. +
++ The URL that this reference will link users to when they click it on Notebook.ai. +
++ The title that should be displayed on Notebook.ai when this reference is displayed. +
++ The description that should be shown next to this reference on Notebook.ai. + Only used if reference_title is also given. + Maximum of 300 characters; more than that will be truncated with an ellipses when displaying. +
++ An image may optionally be included to display with references in some views when the design permits. +
++{ + "id": 1, + "name": "Some <%= content_type.name %>", +<% unless content_type.name == Universe.name %> + "universe_id": 2, +<% end %> + "meta": { + "created_at": "2020-02-01 08:24:20 UTC", + "updated_at": "2020-02-09 06:57:12 UTC" + }, + "categories": { + "Overview": { + "fields": [ + { + "id": 123, + "label": "Description", + "value": "Some Description" + }, + { + "id": 124, + "label": "Another Field", + "value": "Some other value" + }, + ... + ], + }, + ... + }, + "references": [ + { + "url": "https://www.example.com/something/wow", + "title": "Reference name", + "description": "This is something really cool on the Internet that this <%= content_type.name.downcase %> appears in!", + "reference_image": "https://www.example.com/<%= content_type.name.downcase.pluralize %>/12345.png" + } + ] +} +
++GET /api/v1/<%= User.name.downcase.pluralize %>/<id>/active_page_types +
++ The token for your application. +
++ The authorization token for your user. +
++ The ID of the <%= User.name.downcase %> you're fetching. +
++{ + "id": 1, + "name": "Alice Quinn", + "username": "@alice", + "active_page_types": [ + "Character", + "Location", + "Building", + "Landmark", + "Town", + ... + ] +} +
++GET /api/v1/<%= User.name.downcase.pluralize %>/<id>/authorization +
++ The token for your application. +
++ The ID of the <%= User.name.downcase %> you're fetching. +
++ Each user's authorization token is already passed to your app through your + <%= link_to 'callback URL', '#' %> when that user first authorizes your app and should be stored (and associated with your user) at that time. + However, this endpoint is useful if you need to retrieve a particular user's authorization token later. +
++ This endpoint will only return an authorization token if the user you're requesting has already authorized your app. +
++{ + "id": 1, + "token": "a1ed29894c458d0093f74ededa59debc953712d9b412c224" +} +
++GET /api/v1/<%= User.name.downcase.pluralize %>/<id> +
++ The token for your application. +
++ The authorization token for your user. +
++ The ID of the <%= User.name.downcase %> you're fetching. +
++{ + "id": 1, + "name": "Alice Quinn", + "username": "@alice", + "meta": { + "referral_code": "ABCDEFGHI-JKLMNOP-QRSTUV-WXYZ", + "premium_active": true + }, + "profile": { + "bio": "Just a small-town girl", + "interests": "cooking, going on walks, living life, magic", + "website": "http://www.example.com", + "inspirations": "old Lovecraft stories", + "other_names": "Allie", + "occupation": "I exist solely as an example in some API documentation", + "favorite": { + "author": "Oscar Wilde", + "genre": "Fantasy", + "book": "Harry Potter and the Sorcerer's Stone", + "quote": "This isn't even a real quote!", + "page_type": "Creature" + } + } +} +
++ <% Rails.application.config.content_types[:all].each do |content_type| %> + <%= content_type.icon %> + <% end %> +
++ We now offer an API for developers that would like to take advantage of the + features and worlds in Notebook.ai. +
++ To get started, you'll want to <%= link_to 'register an application', api_applications_path %> + and get your API key. In order to access any user's notebook, you'll first need + them to <%= link_to 'authenticate your application', api_approvals_path %> and give you an + access key specific to their notebook. +
++ In other words, it's just three easy steps to get started: +
+ ++ To receive an access token for your application, you'll first need to register it for use. +
++ In order to access or modify a user's notebook pages on their behalf, you'll first need that user to + approve your application. You can set up an approval flow that notifies your application whenever + a user authorizes its use, or you can direct them to <%= link_to 'their integrations page', api_integrations_path %> + and have them paste their authorization code directly into your app. +
+Documentation for developers, by developers
++ After a user authenticates your application, you'll have full access to integrate their worldbuilding pages into your app. + You can show them their characters, let them edit one of their existing creatures, pin their towns and landmarks to your maps, + create new items, and more — all without leaving your app. +
++ <%= link_to 'See the available endpoints by clicking here.', api_docs_path %> +
++ When a user links their notebook page in your app, we can automatically show a link back to your page from that notebook page, too. +
++ For example, if a user publishes a story about Alice and Bob on your site and links their Alice and Bob pages from Notebook.ai, we'll + show a link to that story on both Alice and Bob's Notebook.ai pages also. +
++ <%= link_to 'See the available endpoints by clicking here.', api_docs_path %> +
++ Want to build something our API doesn't support yet? + <%= link_to 'Get in touch!', 'https://github.com/indentlabs/notebook/issues' %> +
++ Application developers can now add external links directly to individual Notebook.ai pages via <%= link_to 'the API', api_path %>. +
++ Each application receives its own tab under the "References" sidebar when viewing any notebook page, which will list + all external references when clicked. Each reference must have a URL, and may optionally also have a title, description, + and image to accompany it. +
++ Please use this API only to link pages in which the content of a particular Notebook.ai page appears or is predominantly featured. + For a character, for example, consider adding references to stories in which they appear. For a location, consider adding references + to stories set in that location. +
++ If you'd like to create an application that is able to use the <%= link_to 'Notebook.ai API', api_path %>, + please fill out the following form to get started. +
++
+ Authorizing this application will give it full access to manage your Notebook.ai pages, including your private pages. Please only authorize this application + if you trust the developer and are here on purpose. +
++ You can manage your authorized Notebook.ai integrations at any time in your <%= link_to 'Data Vault', '#' %>. +
++ <%= ApplicationIntegration.icon %> + <%= application.name %> + + live +
++ <%= truncate(application.description, length: 400) %> +
++ Some helpful text here +
++ 47% API quota left +
+<%= @application_integration.application_token || 'No token set' %>+
+ You've used <%= number_with_delimiter @application_integration.api_requests.successful.count %> of your alotted 10,000 calls this month. + <%= link_to 'Upgrade to Premium', '#' %> to ensure you don't experience any disruptions when hitting your limit. +
+Find me in app/views/integration_authorizations/create.html.erb
diff --git a/app/views/integration_authorizations/show.html.erb b/app/views/integration_authorizations/show.html.erb new file mode 100644 index 00000000..8ace5fd9 --- /dev/null +++ b/app/views/integration_authorizations/show.html.erb @@ -0,0 +1,2 @@ +Find me in app/views/integration_authorizations/show.html.erb
diff --git a/app/views/layouts/_common_head.html.erb b/app/views/layouts/_common_head.html.erb index f691c230..655f4a2d 100644 --- a/app/views/layouts/_common_head.html.erb +++ b/app/views/layouts/_common_head.html.erb @@ -10,6 +10,7 @@ +<%# todo: Is there a way to play nicer with thredded's jquery? %> <% unless request.env['REQUEST_PATH'].start_with?('/forum/') %> <% end %> diff --git a/app/views/layouts/_developer_navbar.html.erb b/app/views/layouts/_developer_navbar.html.erb new file mode 100644 index 00000000..16aebd80 --- /dev/null +++ b/app/views/layouts/_developer_navbar.html.erb @@ -0,0 +1,102 @@ +<% if user_signed_in? %> + <%= render partial: 'layouts/navbar/recent_content_dropdown' %> + <%= render partial: 'layouts/modals/search' %> +<% end %> + + diff --git a/app/views/layouts/_seo.html.erb b/app/views/layouts/_seo.html.erb index ec552d07..56de4242 100644 --- a/app/views/layouts/_seo.html.erb +++ b/app/views/layouts/_seo.html.erb @@ -8,7 +8,7 @@ # Default & site-wide values display_meta_tags site: 'Notebook.ai', -publisher: 'https://plus.google.com/118076966717703203223', +publisher: 'https://www.facebook.com/IndentLabs', image_src: image_url('logos/both-original.png'), description: 'Notebook.ai is a set of tools for writers, game designers, and roleplayers to create magnificent universes — and everything within them.', # Recommended keywords tag length: up to 255 characters, 20 words. diff --git a/app/views/layouts/admin.html.erb b/app/views/layouts/admin.html.erb index 2b12ae90..5a13289c 100644 --- a/app/views/layouts/admin.html.erb +++ b/app/views/layouts/admin.html.erb @@ -20,7 +20,6 @@ <%= yield %> -