Merge branch 'main' into feature/pythonSDKDocs

This commit is contained in:
Tyler Mairose
2024-04-08 15:16:32 -05:00
275 changed files with 6809 additions and 1796 deletions

1
.gitignore vendored
View File

@@ -16,6 +16,7 @@
.env.test.local
.env.production.local
.env
.npmrc
npm-debug.log*
yarn-debug.log*

View File

@@ -59,9 +59,9 @@ Scopes are granular permissions you can add to personal access tokens (PATs) to
Scopes contain one or more rights, which are low level permissions that grant access to individual endpoints. This means that a single scope, like `idn:access-request:manage`, can grant access to multiple API endpoints. To determine which scopes a PAT needs, you must first identify which endpoints the PAT needs to invoke. Each endpoint's API specification indicates which scope is necessary to call the endpoint. You can use this approach to curate a list of scopes that must be applied to the credential to call the necessary endpoints. [Learn more about how to find an API's required scopes here](#identifying-necessary-authorization-for-an-endpoint).
By default, each PAT has the scope, `sp:scopes:all`, which grants access to all the rights appropriate for the [user level](https://documentation.sailpoint.com/saas/help/common/users/user_level_matrix.html). For example, a user with the **Admin** user level has access to all APIs, so `sp:scopes:all` grants **Admin** users access to all APIs. A user with the **Cert Admin** user level, however, has access to only a subset of APIs necessary to perform their role, most notably the certification APIs, so `sp:scopes:all` grants **Cert Admin** users access to only that subset of APIs.
By default, each PAT has the scope `sp:scopes:default`, which is the least privileged scope. It only grants access to endpoints that require no authorization at all, such as [List Public Identities](https://developer.sailpoint.com/idn/api/v3/get-public-identities). Access to the endpoint may still be determined by the user's [user level](https://documentation.sailpoint.com/saas/help/common/users/user_level_matrix.html).
Alternatively, `sp:scopes:default` is the least privileged scope that only grants access to endpoints that require no authorization at all, such as [list public identities](https://developer.sailpoint.com/docs/api/v3/get-public-identities).
Alternatively, `sp:scopes:all` grants access to all the rights appropriate for the [user level](https://documentation.sailpoint.com/saas/help/common/users/user_level_matrix.html). For example, a user with the **Admin** user level has access to all APIs, so `sp:scopes:all` grants **Admin** users access to all APIs. A user with the **Cert Admin** user level, however, has access to only a subset of APIs necessary to perform their role, most notably the certification APIs, so `sp:scopes:all` grants **Cert Admin** users access to only that subset of APIs.
Scopes are additive, which means the final right set is the intersection of all the rights granted by the scopes assigned to a PAT, excluding any rights that fall outside of the user level. Each scope added to an PAT builds up the credential's permission set, incrementally increasing access to the API. If a PAT has `sp:scopes:all` granted, then any additional scope is ignored because `sp:scopes:all` already contains the complete set of rights available to the user level.

View File

@@ -16,7 +16,7 @@ You can use the Identity Security Cloud APIs to update existing resources. Many
- You can send a **PUT** request to replace the existing resource with a new one. For example, if you wanted to update one of John Doe's source accounts, you could use the [Put Account](https://developer.sailpoint.com/docs/api/v3/put-account) endpoint to replace John Doe's existing source account with a new one. This is a viable way to update a resource, but it requires you to update the entire resource each time.
- You can send a **PATCH** request to make a specific change to the resource. For example, if you wanted to update John Doe's account's associated "city" attribute, you could use the [Patch Account](https://developer.sailpoint.com/docs/api/v3/update-account) endpoint to replace his existing "city" with a new one, all without affecting any of the other source account details. This can be very helpful when you want to make specific updates to resources, but it requires some knowledge of the types of changes, or "operations", that are possible, the specific paths of the fields you want to update, and some understanding of the basic data types.
- You can send a **PATCH** request to make a specific change to the resource. For example, if you wanted to update John Doe's account's associated `identityId` attribute, you could use the [Patch Account](https://developer.sailpoint.com/docs/api/v3/update-account) endpoint to replace his existing `identityId` with a new one, all without affecting any of the other source account details. This can be very helpful when you want to make specific updates to resources, but it requires some knowledge of the types of changes, or "operations", that are possible, the specific paths of the fields you want to update, and some understanding of the basic data types.
This guide will focus on the partial update method, PATCH requests. Read this guide to learn how to start sending PATCH requests.

View File

@@ -0,0 +1,62 @@
---
id: connectivity-customizers-config
title: Customizer Config Object
pagination_label: Config Object
sidebar_label: Config Object
sidebar_position: 6.5
sidebar_class_name: saasConnectivity
keywords: ['connectivity', 'connectors', customizers]
description: The config object in a customizer
slug: /connectivity/saas-connectivity/customizers/config
tags: ['Connectivity']
---
# Customizer Config Object
The connector config object holds all the config values that are set in the SaaS connector. These can be used to fetch custom settings added by the user, as well as inspect values from the connector instance itself.
The config object is fetched during initialization of the connector
```typescript
const config: Config = await readConfig()
```
### Example Config Object
Below is an example object model that can be used to type your config. Any values set by the connector itself are added at the top level json.
```typescript
export interface Config {
beforeProvisioningRule: any
cloudCacheUpdate: number
cloudDisplayName: string
cloudExternalId: string
connectionType: string
connectorName: string
deleteThresholdPercentage: number
deltaAggregation: DeltaAggregation
deltaAggregationEnabled: boolean
formPath: any
hasFullAggregationCompleted: boolean
healthCheckTimeout: number
healthy: boolean
idnProxyType: string
managementWorkgroup: any
managerCorrelationFilter: any
since: string
"slpt-source-diagnostics": string
sourceConnected: boolean
sourceDescription: string
spConnEnableStatefulCommands: boolean
spConnectorInstanceId: string
spConnectorSpecId: string
status: string
supportsDeltaAgg: boolean
templateApplication: string
}
export interface DeltaAggregation {
"std:account:list": any
"std:entitlement:list": any
}
```

View File

@@ -17,6 +17,6 @@ Below is a list of limits set in SaaS Connectivity:
- The actual run time of a connector is not limited at this time, however a response in the form of `res.send()` must be recieved from a command at least every 3 minutes
- If you have a long running call, you can use `res.keepAlive()` to send a heartbeat to Identity Security Cloud in between `res.send()` calls to let it know the connector is still running
- **Response Size**
- The maximum size of a single `res.send()` call is 256 KiB. Not that some metadata is sent along with the call, so the max size of the payload sent will be slightly less than 256 KiB
- The maximum size of a single `res.send()` call is 256 KiB. Note that some metadata is sent along with the call, so the max size of the payload sent will be slightly less than 256 KiB
- **Memory Limits**
- Each instance of a running SaaS connector is limited to 512 MB

View File

@@ -0,0 +1,23 @@
---
id: configuration-hub
title: Configuration Hub
pagination_label: Configuration Hub
sidebar_position: 3
sidebar_class_name: configurationHub
keywords: ['configuration']
description: Manage tenant configurations with the ISC UI.
slug: /extensibility/configuration-management/configuration-hub
tags: ['Configuration', 'Hub']
---
## Overview
The SailPoint Configuration Hub supports management of configuration objects in your Identity Security Cloud (ISC) tenant through backup and deploy operations from the ISC UI. For example, you can back up configurations like sources and identity profiles defined for your business, restore them in the event of configuration errors or loss, or migrate and deploy them to your other tenants.
To learn more about Configuration Hub, refer to the [Configuration Hub documentation](https://documentation.sailpoint.com/saas/help/confighub/config_hub.html).
## Discuss
The most valuable resource for ISC developers is the SailPoint Developer Community itself, where ISC users and experts all over the world come together to ask questions and provide solutions.
To learn more about the SaiLPoint Configuration Hub and discuss it with SailPoint Developer Community members, go to the [SailPoint Developer Community Forum](https://developer.sailpoint.com/discuss/c/isc/6).

View File

@@ -19,9 +19,9 @@ tags: ['Introduction', 'Getting Started']
## Overview
Configuration Management provides you with a form of version control for your tenant configurations.
With Configuration Management, you can export snapshots of your current tenant configurations, downloading them in a JSON.
With Configuration Management, you can export (backup in Configuration Hub) and import (deploy in Configuration Hub) snapshots of your current tenant configurations, downloading them in a JSON.
These configurations can serve as different versions of your tenant configuration.
You can then import those configurations into tenants to update, resore, or migrate tenant configurations.
You can then import those configurations into tenants to update, restore, or migrate tenant configurations.
```mdx-code-block
import DocCardList from '@theme/DocCardList';

View File

@@ -2,10 +2,10 @@
id: saas-configuration
title: SaaS Configuration
pagination_label: SaaS Configuration
sidebar_position: 3
sidebar_position: 4
sidebar_class_name: saasConfiguration
keywords: ['configuration']
description: Use SaaS Configuration APIs to import and export configurations.
description: Manage tenant configurations with the SaaS Configuration APIs.
slug: /extensibility/configuration-management/saas-configuration
tags: ['SaaS Configuration']
---

View File

@@ -428,7 +428,7 @@ return generateUsername( identity.getFirstname(), identity.getLastname() );
Before you send the rule to the professional services team to upload your rule to your tenant for use, you can send it through the rule validator to check for any errors.
Refer to [Rule Validator](https://community.sailpoint.com/t5/Professional-Services/Identity Security Cloud-Rule-Validator/ta-p/166116) for installation.
Refer to [Rule Validator](https://community.sailpoint.com/t5/Professional-Services/IdentityNow-Rule-Validator/ta-p/166116) for installation.
Run the rule validator against your newly written rule.

View File

@@ -191,7 +191,7 @@ sailpoint.object.Identity identity = plan.getIdentity();
String sAMAccountName = identity.getAttribute("adUsername");
sailpoint.rule.Identity foundIdentity = idn.getIdentityById("uid");
String email = foundIdentity.getAttribute("email");
String email = foundIdentity.getEmail();
```
The below section provides a full accounting of the methods available to rule writers using the IdnRuleUtil class:

View File

@@ -90,7 +90,7 @@ Some examples of expressions are:
- `true` indicates the transform rounds up (i.e., truncate the fractional date/time component indicated and then add one unit of that component).
- `false` indicates the transform rounds down (i.e., truncate the fractional date/time component indicated).
- `input` - This is an optional attribute that can explicitly define the input data passed into the transform logic. If no input is provided, the transform takes its input from the source and attribute combination configured with the UI.
- **input** - This is an optional attribute that can explicitly define the input data passed into the transform logic. If no input is provided, the transform takes its input from the source and attribute combination configured with the UI.
## Examples

View File

@@ -14,7 +14,23 @@ hide_table_of_contents: true
import MermaidViewer from '@site/src/components/MermaidViewer';
Secure Data Share (SDS) makes your SailPoint identity data available directly in your own AWS Snowflake account. The provided dataset is comprised of structured tables that include identity, audit events, entitlements, roles, access profiles, accounts, and application data. SDS is an alternative to [search](https://documentation.sailpoint.com/saas/help/search/index.html) that gives you direct access to your tenant data, allowing you to connect your favorite data analytics tools, like PowerBI and Tableau. For information on how to use SDS in your environment, see the documentation [here](https://documentation.sailpoint.com/saas/help/secure_data_share/secure_data_share.html). For the entity relationship (ER) diagrams that represent the data model, please see the following pages.
# Secure Data Share
Secure Data Share (SDS) makes your SailPoint identity data available directly in your own [AWS Snowflake](https://aws.amazon.com/financial-services/partner-solutions/snowflake/) account. The provided dataset comprises structured tables that include identity, audit event, entitlement, role, access profile, account, and application data. Secure Data Share is an alternative to SailPoint's [Search](https://documentation.sailpoint.com/saas/help/search/index.html). SDS uses SQL as the query language, whereas Search uses the [Elasticsearch Query Language](https://www.elastic.co/guide/en/elasticsearch/reference/current/query-dsl.html). SQL provides certain advantages over Elasticsearch: joining and aggregating data is easier in SQL, and the Snowflake interface has a built-in SQL explorer and allows you to download your search results in CSV format. SDS also allows you to connect your SailPoint data to your favorite data analytics tools, like [PowerBI](https://learn.microsoft.com/en-us/power-bi/connect-data/service-connect-snowflake) and [Tableau](https://help.tableau.com/current/pro/desktop/en-us/examples_snowflake.htm), further enhancing the reporting capabilities for your SailPoint data.
If you would like to speak to a SailPoint representative about Secure Data Share, please [schedule a meeting here](https://calendly.com/jordan-mandernach/secure_data_share).
## Requirements
Secure Data Share is an add-on for Identity Security Cloud. Please contact your sales representative to discuss your SDS options. You must also have an AWS Snowflake account so that SailPoint can synchronize your tenant data with your Snowflake instance.
## Data Synchronization SLA
Similar to Search, SDS has a synchronization service-level agreement (SLA) of 24 hours. This means it can take up to 24 hours for operational data in your tenant to be synchronized with your Snowflake database. SailPoint Search and SDS are two separate systems, and there is no guarantee on which service will receive updated data first. In some cases, operational data may appear in Search before SDS, and in other cases SDS may receive the data first.
## How to use SDS
For information on how to install and use SDS in your environment, see the documentation [here](https://documentation.sailpoint.com/saas/help/secure_data_share/secure_data_share.html). Because SDS uses SQL as the query language, your tenant data is formatted into relational tables. To view the relationships between these tables, please see the following pages for the entity relationship (ER) diagrams that represent the data model.
```mdx-code-block
import DocCardList from '@theme/DocCardList';
@@ -22,3 +38,8 @@ import {useCurrentSidebarCategory} from '@docusaurus/theme-common';
<DocCardList items={useCurrentSidebarCategory().items}/>
```
## Discuss
The most valuable resource for ISC developers is the SailPoint Developer Community itself, where ISC users and experts all over the world come together to ask questions and provide solutions.
To learn more about SDS and discuss it with SailPoint Developer Community members, go to the [SailPoint Developer Community Forum](https://developer.sailpoint.com/discuss/c/identity-security-cloud/6).

View File

@@ -7,34 +7,43 @@ module.exports = {
},
items: [
{
type: 'dropdown',
label: 'Documentation',
position: 'left',
label: 'Identity Security Cloud',
to: '/docs',
items: [
{label: 'Identity Security Cloud', to: '/docs'},
{label: 'IdentityIQ', to: '/docs/iiq'},
],
},
{
type: 'dropdown',
label: 'Community',
position: 'left',
items: [
{
label: 'Developer Forum',
to: 'https://developer.sailpoint.com/discuss/',
},
{
label: 'CoLab Marketplace',
to: '/colab',
},
{
label: 'Developer Blog',
to: '/blog',
},
{
label: 'Ambassador Program',
to: '/ambassadors',
},
],
},
{
position: 'left',
label: 'IdentityIQ',
to: '/docs/iiq',
},
{
position: 'left',
label: 'CoLab',
to: '/colab',
},
{
position: 'left',
label: 'Blog',
to: '/blog',
},
{
position: 'left',
label: 'Ideas',
to: 'https://ideas.sailpoint.com',
},
{
position: 'left',
label: 'Discuss',
to: 'https://developer.sailpoint.com/discuss/',
label: 'Video Library',
to: '/videos',
},
{
type: 'dropdown',

18
package-lock.json generated
View File

@@ -14,6 +14,7 @@
"@docusaurus/theme-mermaid": "2.4.3",
"@fortawesome/fontawesome-svg-core": "^6.5.1",
"@fortawesome/pro-duotone-svg-icons": "^6.5.1",
"@fortawesome/pro-solid-svg-icons": "^6.5.1",
"@fortawesome/react-fontawesome": "^0.2.0",
"@mdx-js/react": "^1.6.22",
"@typeform/embed-react": "^1.21.0",
@@ -23,6 +24,7 @@
"docusaurus-plugin-openapi-docs": "^2.0.2",
"docusaurus-theme-openapi-docs": "^2.0.2",
"docusaurus2-dotenv": "^1.4.0",
"ldrs": "^1.0.1",
"prism-react-renderer": "^1.3.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",
@@ -3263,6 +3265,17 @@
"node": ">=6"
}
},
"node_modules/@fortawesome/pro-solid-svg-icons": {
"version": "6.5.1",
"resolved": "https://npm.fontawesome.com/@fortawesome/pro-solid-svg-icons/-/6.5.1/pro-solid-svg-icons-6.5.1.tgz",
"integrity": "sha512-UnJzqw7w+RVtEnQ2Bqg09bGYkJEZRkTnbgweZDTznHjtxsJzQdJkD3hJPL6N00c8GbWpRYslNDnTNDGVSd94cQ==",
"dependencies": {
"@fortawesome/fontawesome-common-types": "6.5.1"
},
"engines": {
"node": ">=6"
}
},
"node_modules/@fortawesome/react-fontawesome": {
"version": "0.2.0",
"resolved": "https://registry.npmjs.org/@fortawesome/react-fontawesome/-/react-fontawesome-0.2.0.tgz",
@@ -10614,6 +10627,11 @@
"shell-quote": "^1.8.1"
}
},
"node_modules/ldrs": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/ldrs/-/ldrs-1.0.1.tgz",
"integrity": "sha512-eeCP1XEG72Yz6JX50zlYWRJcEfNBrpp8QSzyjD3HHhxrnt4steLX4iDHF3Dezjbnnj5qtuJn7lr1nln+/kpUIQ=="
},
"node_modules/leven": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz",

View File

@@ -27,6 +27,7 @@
"@docusaurus/theme-mermaid": "2.4.3",
"@fortawesome/fontawesome-svg-core": "^6.5.1",
"@fortawesome/pro-duotone-svg-icons": "^6.5.1",
"@fortawesome/pro-solid-svg-icons": "^6.5.1",
"@fortawesome/react-fontawesome": "^0.2.0",
"@mdx-js/react": "^1.6.22",
"@typeform/embed-react": "^1.21.0",
@@ -36,6 +37,7 @@
"docusaurus-plugin-openapi-docs": "^2.0.2",
"docusaurus-theme-openapi-docs": "^2.0.2",
"docusaurus2-dotenv": "^1.4.0",
"ldrs": "^1.0.1",
"prism-react-renderer": "^1.3.1",
"react": "^18.2.0",
"react-dom": "^18.2.0",

View File

@@ -0,0 +1,19 @@
module.exports = function (context, options) {
return {
name: 'plugin-dynamic-routes',
async contentLoaded({content, actions}) {
const {routes} = options;
const {addRoute} = actions;
routes.forEach((route) => {
addRoute({
path: route.path,
exact: route.exact || false,
component: route.component,
customProps: route.customProps || {},
});
});
},
};
};

View File

@@ -23,7 +23,7 @@ module.exports = [
to: '/ambassadors',
from: ['/ambassador-program', '/ambassador'],
},
{
{
from: ['/idn/docs/saas-configuration'],
to: '/docs/extensibility/configuration-management/saas-configuration',
},

View File

@@ -12,7 +12,7 @@ const sidebars = {
{
type: 'category',
label: 'API Specifications',
collapsible: true,
collapsible: false,
link: {
type: 'doc',
id: 'api/api-specifications',
@@ -202,11 +202,6 @@ const sidebars = {
label: 'Product Documentation',
href: 'https://documentation.sailpoint.com',
},
{
type: 'link',
label: 'Certifications',
href: 'https://university.sailpoint.com/Saba/Web_spf/NA10P1PRD075/guest/categorydetail/categ000000000003041/true/xxemptyxx/',
},
],
},
],
@@ -223,6 +218,7 @@ const sidebars = {
{
type: 'category',
label: 'API Specifications',
collapsible: false,
link: {
type: 'generated-index',
title: 'API Specifications',
@@ -252,11 +248,6 @@ const sidebars = {
label: 'Product Documentation',
href: 'https://documentation.sailpoint.com/#identityiq',
},
{
type: 'link',
label: 'Certifications',
href: 'https://university.sailpoint.com/Saba/Web_spf/NA10P1PRD075/guest/categorydetail/categ000000000003042/true/xxemptyxx/',
},
],
},
],

View File

@@ -4,35 +4,49 @@ import Link from '@docusaurus/Link';
import useBaseUrl from '@docusaurus/useBaseUrl';
import ThemedImage from '@theme/ThemedImage';
import {addDarkToFileName} from '../../../util/util';
export default function AmbassadorCard({
data
}) {
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {
faSquareCheck,
faCalendarClock,
} from '@fortawesome/pro-duotone-svg-icons';
export default function AmbassadorCard({data}) {
return (
<Link to={data.link} className={styles.link}>
<div className={styles.card}>
<div className={styles.cardFaceContainer}>
<img className={styles.cardFace} src={useBaseUrl(data.creatorImage)}></img>
<img
className={styles.cardFace}
src={useBaseUrl(data.creatorImage)}></img>
<div className={styles.cardNameContainer}>
<div className={styles.name}>{data.name}</div>
</div>
</div>
<div className={styles.bio} dangerouslySetInnerHTML={{__html: data.bio}}></div>
<div
className={styles.bio}
dangerouslySetInnerHTML={{__html: data.bio}}></div>
<div className={styles.cardData}>
<img className={styles.cardEye} src={useBaseUrl('/icons/square-check-regular.svg')}></img>
<div className={styles.cardCommentText}>{data.answers} solutions</div>
<FontAwesomeIcon
icon={faSquareCheck}
className={styles.docCardIcon}
size="lg"
/>
<div className={styles.cardCommentTextUpper}>
{data.answers} solutions
</div>
</div>
<div className={styles.cardDataLower}>
<img className={styles.cardEye} src={useBaseUrl('/icons/calendar-clock-light.svg')}></img>
<div className={styles.cardCommentText}>member since {data.member_since}</div>
<FontAwesomeIcon
icon={faCalendarClock}
className={styles.docCardIcon}
size="lg"
/>
<div className={styles.cardCommentText}>
member since {data.member_since}
</div>
</div>
</div>
</Link>
);
}

View File

@@ -10,16 +10,23 @@
.card {
position: relative;
margin-top: 20px;
min-height: 500px;
width: 365px;
min-height: 545px;
/* UI Properties */
background: var(--dev-card-background);
box-shadow: var(--dev-card-shadow);
border: 1px solid var(--dev-card-background);
border-radius: 40px;
opacity: 1;
transition: all 0.3s;
opacity: 1;
transition: all 0.3s;
background: var(--dev-new-car-background);
border-radius: 0.5rem;
--tw-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);
--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color),
0 1px 2px -1px var(--tw-shadow-color);
border-radius: 0.5rem;
box-shadow: 0 0 transparent, 0 0 transparent, 0 1px 3px 0 rgba(0, 0, 0, 0.1),
0 1px 2px -1px rgba(0, 0, 0, 0.1);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000),
var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
border: 1px solid var(--dev-card-border);
}
.card:hover {
@@ -28,10 +35,6 @@
box-shadow: var(--dev-card-selected);
}
.cardFaceContainer {
display: flex;
margin: 30px;
@@ -41,8 +44,8 @@
border-radius: 99px;
justify-content: center;
display: flex;
height: 100px;
width: 100px;
height: 80px;
width: 80px;
}
.cardNameContainer {
@@ -53,7 +56,7 @@
}
.name {
font-size: 25px;
font-size: 22px;
font-weight: 500;
width: 100%;
top: 124px;
@@ -89,19 +92,16 @@
width: 20px;
}
.bio {
margin: 30px auto;
font-size: 16px;
font-weight: 500;
width: 100%;
color: var(--ifm-color-primary);
padding: 30px;
padding: 0px 30px;
margin-bottom: 50px;
}
.cardData {
position: absolute;
bottom: 50px;
@@ -113,7 +113,8 @@
.cardEye {
fill: #96a9bb;
/* computed from codepen https://codepen.io/sosuke/pen/Pjoqqp */
filter: invert(79%) sepia(12%) saturate(484%) hue-rotate(168deg) brightness(84%) contrast(84%);
filter: invert(79%) sepia(12%) saturate(484%) hue-rotate(168deg)
brightness(84%) contrast(84%);
margin-left: 5px;
height: 18px;
width: 18px;
@@ -128,10 +129,23 @@
font-size: 16px;
}
.cardCommentTextUpper {
color: #96a9ba;
/* computed from codepen https://codepen.io/sosuke/pen/Pjoqqp */
margin-left: 10px;
margin-bottom: 5px;
height: 20px;
font-size: 16px;
}
.cardDataLower {
position: absolute;
bottom: 20px;
left: 22px;
left: 20px;
display: flex;
align-items: center;
}
.docCardIcon {
color: var(--ifm-color-primary);
}

View File

@@ -1,50 +1,56 @@
import React from 'react';
import styles from './styles.module.css';
import AmbassadorCard from '../AmbassadorCard';
import BounceLoader from 'react-spinners/BounceLoader';
import NewtonsCradle from '../../newtonsCradle';
import {discourseBaseURL, developerWebsiteDomain} from '../../../util/util';
import {getAmbassadors, getAmbassadorDetails} from '../../../services/DiscourseService';
export default function AmbassadorCards({
expert
}) {
import {
getAmbassadors,
getAmbassadorDetails,
} from '../../../services/DiscourseService';
export default function AmbassadorCards({expert}) {
const [cardData, setCardData] = React.useState();
const [loadingCards, setLoadingCards] = React.useState(true);
const getPosts = async () => {
let data = await getAmbassadors(expert, 1, 0);
const resultset = []
const resultset = [];
if (data.meta) {
let count = 0
let count = 0;
while (count < data.meta.total) {
data = await getAmbassadors(expert, 50, count);
count += 50
count += 50;
if (data.members) {
const memberDetails = await getAmbassadorDetails(data.members.map(item => item.id))
const memberDetails = await getAmbassadorDetails(
data.members.map((item) => item.id),
);
for (const member of data.members) {
const memberDetail = memberDetails.users.filter(item => item.id === member.id)[0]
if (member.avatar_template.includes("developer.sailpoint.com") && memberDetail.bio_excerpt && memberDetail.bio_excerpt.length > 150) {
resultset.push(await getMemberList(member, memberDetail))
}
const memberDetail = memberDetails.users.filter(
(item) => item.id === member.id,
)[0];
if (
member.avatar_template.includes('developer.sailpoint.com') &&
memberDetail.bio_excerpt &&
memberDetail.bio_excerpt.length > 150
) {
resultset.push(await getMemberList(member, memberDetail));
}
}
}
}
} else {
setCardData(undefined);
setLoadingCards(false);
return
return;
}
resultset.sort((a, b) => a.date - b.date)
resultset.sort((a, b) => a.date - b.date);
setCardData(resultset);
setLoadingCards(false);
};
React.useEffect(() => {
getPosts();
setCardData(undefined);
@@ -55,25 +61,17 @@ export default function AmbassadorCards({
return (
<div className={styles.center}>
<div className={styles.gridContainer}>
{cardData.map(function(a, index){
return <AmbassadorCard
key={a.link}
data={a}
></AmbassadorCard>
{cardData.map(function (a, index) {
return <AmbassadorCard key={a.link} data={a}></AmbassadorCard>;
})}
</div>
</div>
);
} else if (loadingCards) {
return (
<BounceLoader
className={styles.spinnerCenter}
color={'#0033a1'}
loading={true}
size={150}
aria-label="Loading Spinner"
data-testid="loader"
/>
<div className={styles.spinnerCenter}>
<NewtonsCradle />
</div>
);
} else {
return (
@@ -86,38 +84,37 @@ export default function AmbassadorCards({
}
async function getMemberList(member, details) {
return {
name: member.name,
creatorImage: getavatarURL(member.avatar_template),
title: member.title,
bio: details.bio_excerpt,
member_since: new Date(member.added_at).toLocaleString('default', {month: 'long'}) + ' ' + new Date(member.added_at).toISOString().slice(0, 4),
member_since:
new Date(member.added_at).toLocaleString('default', {month: 'long'}) +
' ' +
new Date(member.added_at).toISOString().slice(0, 4),
badge_count: details.badge_count,
answers: details.accepted_answers,
location: details.location,
website: details.website_name,
link:
discourseBaseURL() + 'u/' +
member.username +
'/summary',
link: discourseBaseURL() + 'u/' + member.username + '/summary',
};
}
function getavatarURL(avatar) {
if (avatar.includes(developerWebsiteDomain())) {
return "https://" + developerWebsiteDomain() + avatar.replace("{size}", "120")
return (
'https://' + developerWebsiteDomain() + avatar.replace('{size}', '120')
);
} else {
return avatar.replace("{size}", "120")
return avatar.replace('{size}', '120');
}
}
function styleExcerpt(excerpt) {
if (excerpt.length > 150) {
return excerpt.slice(0, 150) + "..."
return excerpt.slice(0, 150) + '...';
} else {
return excerpt.replace("&hellip;", "")
return excerpt.replace('&hellip;', '');
}
}

View File

@@ -1,17 +1,16 @@
/* Getting Started Card container */
.gridContainer {
display: grid;
place-content: center;
grid-template-columns: repeat(auto-fit, minmax(320px, 1fr));
display: flex;
flex-wrap: wrap;
grid-template-columns: 1fr 1fr 1fr 1fr;
grid-gap: 40px;
margin-left: 40px;
margin-right: 40px;
width: 1620px;
}
.center {
margin: 0px auto;
max-width: 1300px;
margin-bottom: 50px;
width: 1620px;
}
.space {
@@ -26,10 +25,38 @@
margin: 50px;
}
.spinnerCenter {
margin: 100px auto;
max-width: 1300px;
margin: auto;
width: 4%;
margin-bottom: 50px;
}
}
@media only screen and (max-width: 1950px) {
.gridContainer {
width: 1215px;
}
.center {
width: 1215px;
}
}
@media only screen and (max-width: 1315px) {
.gridContainer {
width: 810px;
}
.center {
width: 810px;
}
}
@media only screen and (max-width: 910px) {
.gridContainer {
width: 365px;
}
.center {
width: 365px;
}
}

View File

@@ -6,14 +6,8 @@ import Link from '@docusaurus/Link';
export default function BlogBanner() {
return (
<div>
<div className={styles.imageContainer}>
<img className={styles.headerImage} src={useBaseUrl('/blog/blog_banner_template.png')}></img>
<div className={styles.blogHeaderText}>
Blog
</div>
</div >
<div className={styles.titleContainer}>
{/* <h1 className={styles.blogTitle}>Blog</h1> */}
</div>
);
}

View File

@@ -1,28 +1,20 @@
.blogHeaderText {
position: relative;
.blogTitle {
text-align: center;
margin-bottom: 2%;
color: #ffffff;
font-size: 48px;
max-width: 396px;
font-weight: bold;
line-height: 133%;
top: -84px;
left: 59px;
position: relative;
top: 25px;
}
.background {
.titleContainer {
width: 100%;
object-fit: repeat;
height: 100%;
height: 45px;
background: rgb(0, 51, 161);
background: linear-gradient(
90deg,
rgba(0, 51, 161, 1) 0%,
rgba(84, 192, 232, 1) 100%
);
align-content: center;
margin-bottom: 2%;
}
.imageContainer {
width: 100%;
height: 90px;
background: rgb(0,51,161);
background: linear-gradient(90deg, rgba(0,51,161,1) 0%, rgba(84,192,232,1) 100%);
}
.headerImage {
height: 90px;
}

View File

@@ -2,55 +2,45 @@ import React from 'react';
import styles from './styles.module.css';
import Link from '@docusaurus/Link';
import useBaseUrl from '@docusaurus/useBaseUrl';
import ThemedImage from '@theme/ThemedImage';
import {addDarkToFileName} from '../../../util/util';
export default function BlogCard({
featured,
link,
title,
tags,
creatorImage,
creatorTitle,
image,
excerpt,
name,
views,
replies,
readTime
readTime,
}) {
return (
<Link to={link}>
<div className={styles.card} >
<div className={styles.cardData}>
<img className={styles.cardEye} src={useBaseUrl('/blog/eye-regular.svg')}></img>
<div className={styles.cardCommentText}>{views}</div>
<img className={styles.cardComment} src={useBaseUrl('/blog/comment-light.svg')}></img>
<div className={styles.cardCommentText}>{replies}</div>
</div>
<div className={styles.cardUser}>
<img className={styles.cardFace} src={useBaseUrl(creatorImage)}></img>
<div className={styles.cardName}>{name}</div>
</div>
<div className={featured ? styles.featuredCard : styles.card}>
<div className={styles.cardText}>
<img className={styles.cardImage} src={useBaseUrl(image)}></img>
<div className={styles.cardTitle}>{title}</div>
<div className={styles.tags}>
{tags?.map((tag, index) => {
if (index > 2) {
return '';
}
return <div key={tag} className={styles.tag}>{tag}</div>;
})}
<img
className={featured ? styles.featuredCardImage : styles.cardImage}
src={useBaseUrl(image)}></img>
<div className={styles.split}></div>
<div
className={featured ? styles.featuredCardTitle : styles.cardTitle}>
{title}
</div>
<div className={styles.cardUser}>
<img
className={featured ? styles.featuredCardFace : styles.cardFace}
src={useBaseUrl(creatorImage)}></img>
<div className={styles.cardName}>{name}</div>
<div className={styles.cardCreatorTitle}>{creatorTitle}</div>
<div></div>
</div>
<div className={styles.cardBody}>{excerpt}</div>
</div>
</div>
</Link>
);

View File

@@ -1,127 +1,197 @@
/* Getting Started Card */
.card {
position: relative;
margin-top: 20px;
height: 650px;
/* UI Properties */
background: var(--dev-card-background);
box-shadow: var(--dev-card-shadow);
border: 1px solid var(--dev-card-background);
border-radius: 40px;
margin-top: 30px;
margin-bottom: 30px;
opacity: 1;
transition: all 0.3s;
max-width: 600px;
transition: all 0.3s;
width: 275px;
max-width: 275px;
background: var(--dev-new-car-background);
border-radius: 0.5rem;
--tw-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);
--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color),
0 1px 2px -1px var(--tw-shadow-color);
border-radius: 0.5rem;
box-shadow: 0 0 transparent, 0 0 transparent, 0 1px 3px 0 rgba(0, 0, 0, 0.1),
0 1px 2px -1px rgba(0, 0, 0, 0.1);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000),
var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
border: 1px solid var(--dev-card-border);
}
.card:hover {
.featuredCard {
position: relative;
margin-top: 30px;
margin-bottom: 30px;
opacity: 1;
transition: all 0.3s;
width: 400px;
height: 475px;
background: var(--dev-new-car-background);
border-radius: 0.5rem;
--tw-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);
--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color),
0 1px 2px -1px var(--tw-shadow-color);
border-radius: 0.5rem;
box-shadow: 0 0 transparent, 0 0 transparent, 0 1px 3px 0 rgba(0, 0, 0, 0.1),
0 1px 2px -1px rgba(0, 0, 0, 0.1);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000),
var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
border: 1px solid var(--dev-card-border);
}
.featuredCardImage {
overflow: hidden;
position: relative;
width: 400px;
height: 225px;
border-top-left-radius: 0.5rem;
border-top-right-radius: 0.5rem;
}
a:link {
text-decoration: none;
}
a:visited {
text-decoration: none;
}
a:hover {
text-decoration: none;
}
a:active {
text-decoration: none;
}
.card:hover,
.featuredCard:hover {
cursor: pointer;
transform: translate(0px, -5px);
box-shadow: var(--dev-card-selected);
text-decoration: none;
}
.cardText {
position: absolute;
margin: 22px;
min-width: 170px;
top: 10px;
left: 0;
display: flex;
flex-direction: column;
}
.cardImage {
overflow: hidden;
position: relative;
width: 300px;
height: 144px;
border-top-left-radius: 0.5rem;
border-top-right-radius: 0.5rem;
}
.featuredCardTitle {
font-size: 20px;
font-weight: 500;
display: flex;
gap: 1rem;
margin-top: 1rem;
padding-right: 1rem;
padding-left: 1rem;
text-align: left;
height: 80px;
}
.cardTitle {
font-size: 22px;
font-weight: 700;
}
.cardBody {
font-size: 16px;
font-weight: 500;
top: 10px;
left: 0;
}
.tag {
font-size: 16px;
font-weight: 500;
color: var(--dev-secondary-text);
background-color: var(--dev-tag-highlight);
padding: 0px 8px;
margin-left: 5px;
margin-top: 5px;
}
.tags {
margin: 0px;
width: 100%;
display: flex;
flex-wrap: wrap;
gap: 1rem;
margin-top: 1rem;
padding-right: 0.5rem;
padding-left: 0.5rem;
text-align: left;
height: 80px;
}
.cardUser {
position: absolute;
bottom: 10px;
left: 25px;
display: flex;
margin-top: 15px;
margin-bottom: 5%;
}
.cardFace {
border-radius: 9999px;
margin: auto;
margin-left: 2%;
height: 40px;
width: 40px;
}
.featuredCardFace {
border-radius: 9999px;
margin-left: 2%;
height: 50px;
width: 50px;
}
.cardName {
min-width: 170px;
font-weight: 800;
margin-top: 7px;
margin-left: 10px;
}
.cardData {
position: absolute;
bottom: 50px;
left: 20px;
display: flex;
align-items: center;
color: var(--text-color);
}
.cardEye {
fill: #96a9bb;
/* computed from codepen https://codepen.io/sosuke/pen/Pjoqqp */
filter: invert(79%) sepia(12%) saturate(484%) hue-rotate(168deg) brightness(84%) contrast(84%);
margin-left: 5px;
height: 18px;
width: 18px;
}
.cardComment {
.cardCreatorTitle {
min-width: 170px;
margin-left: 10px;
fill: #96a9bb;
/* computed from codepen https://codepen.io/sosuke/pen/Pjoqqp */
filter: invert(79%) sepia(12%) saturate(484%) hue-rotate(168deg) brightness(84%) contrast(84%);
height: 18px;
width: 18px;
margin-bottom: 4px;
display: flex;
color: var(--text-color);
}
.cardCommentText {
color: #96a9ba;
/* computed from codepen https://codepen.io/sosuke/pen/Pjoqqp */
margin-left: 5px;
margin-bottom: 5px;
height: 20px;
font-size: 16px;
.featuredCardName {
min-width: 170px;
font-weight: 800;
margin-top: 7px;
margin-left: 10px;
display: flex;
color: var(--text-color);
}
.featuredCardCreatorTitle {
min-width: 170px;
margin-left: 10px;
display: flex;
color: var(--text-color);
}
.split {
background: linear-gradient(
145deg,
rgba(0, 51, 161, 1) 40%,
rgba(0, 79, 181, 1) 65%,
rgba(0, 113, 206, 1) 100%
);
height: 15px;
}
@media only screen and (max-width: 550px) {
.cardText {
margin-bottom: 0px;
}
.featuredCard {
width: 275px;
max-width: 275px;
height: auto;
}
.featuredCardImage {
width: 275px;
height: 144px;
}
.featuredCardTitle {
font-size: 16px;
}
.featuredCardFace {
height: 40px;
width: 40px;
}
}

View File

@@ -1,18 +1,25 @@
import React from 'react';
import styles from './styles.module.css';
import BlogCard from '../BlogCard';
import BounceLoader from 'react-spinners/BounceLoader';
import NewtonsCradle from '../../newtonsCradle';
import {discourseBaseURL, developerWebsiteDomain} from '../../../util/util';
import {getBlogPosts, getUserTitle} from '../../../services/DiscourseService';
import {getBlogPosts, getTopic} from '../../../services/DiscourseService';
export default function BlogCards({filterCallback}) {
export default function BlogCards({filterCallback, limit, featured}) {
const [cardData, setCardData] = React.useState();
const [loadingCards, setLoadingCards] = React.useState(true);
const getPosts = async () => {
const data = await getBlogPosts(filterCallback.join('+'));
if (!filterCallback) {
filterCallback = ['identity-security-cloud'];
}
if (featured) {
filterCallback = ['featured'];
}
const data = await getBlogPosts(filterCallback.join('+'));
const resultset = [];
const titleList = [];
if (data.topic_list.topics) {
for (const topic of data.topic_list.topics) {
if (topic.tags.length > 0) {
@@ -21,80 +28,106 @@ export default function BlogCards({filterCallback}) {
if (topicUser.description.includes('Original Poster')) {
for (let user of data.users) {
if (user.id === topicUser.user_id) {
if (
!titleList.find((x) => x.group === user.primary_group_name)
) {
let usertitle = await getUserTitle(user.primary_group_name);
titleList.push({
group: user.primary_group_name,
title: usertitle.group.title,
});
user.title = usertitle.group.title;
} else {
user.title = titleList.find(
(x) => x.group === user.primary_group_name,
).title;
}
poster = user;
}
}
}
}
if (topic.category_id !== 57) {
resultset.push(await getPostList(topic, poster));
if (featured || (!featured && !topic.tags.includes('featured'))) {
resultset.push(await getPostList(topic, poster));
}
}
}
}
setCardData(resultset);
if (limit) {
setCardData(resultset.slice(0, limit));
} else {
setCardData(resultset);
}
} else {
setCardData(undefined);
}
setLoadingCards(false);
};
function shortenTitle(title) {
if (title.length > 63) {
return title.substring(0, 62) + '...';
}
return title;
}
React.useEffect(() => {
getPosts();
setCardData(undefined);
setLoadingCards(true);
}, [filterCallback]);
if (cardData && cardData.length > 0) {
return (
<div className={styles.center}>
<div className={styles.gridContainer}>
{cardData.map(function (a, index) {
return (
<BlogCard
key={a.link}
id={index + a.link}
excerpt={a.excerpt}
name={a.name}
tags={a.tags}
link={a.link}
image={a.image}
title={a.title}
views={a.views}
replies={a.replies}
readTime={a.readTime}
creatorImage={a.creatorImage}></BlogCard>
);
})}
return (
<div className={featured ? styles.featuredCenter : styles.center}>
{loadingCards ? (
// Show loading icon when data is still loading
<div
className={
featured ? styles.featuredSpinnerCenter : styles.spinnerCenter
}>
<NewtonsCradle />
</div>
</div>
);
} else if (loadingCards) {
return (
<BounceLoader
className={styles.spinnerCenter}
color={'#0033a1'}
loading={true}
size={150}
aria-label="Loading Spinner"
data-testid="loader"
/>
);
} else {
return (
<div className={styles.noFound}>
{' '}
No Blogposts Found with the Given Search Criteria
</div>
);
}
) : cardData && cardData.length > 0 ? (
// Show cards if not loading and cardData is available
<div
className={
featured ? styles.featuredGridContainer : styles.gridContainer
}>
{cardData.map((a, index) => (
<BlogCard
featured={featured}
key={a.link}
id={index + a.link}
excerpt={a.excerpt}
name={a.name}
tags={a.tags}
link={a.link}
image={a.image}
title={shortenTitle(a.title)}
views={a.views}
replies={a.replies}
readTime={a.readTime}
creatorImage={a.creatorImage}
creatorTitle={a.creatorTitle}></BlogCard>
))}
</div>
) : (
// Show no content message if not loading and no cardData
<div className={styles.noFound}>
No Blogposts Found with the Given Search Criteria
</div>
)}
</div>
);
}
async function getPostList(topic, user) {
console.log(topic);
return {
name: user.name,
excerpt: styleExcerpt(topic.excerpt),
creatorImage: getavatarURL(user.avatar_template),
creatorTitle: user.title,
tags: topic.tags,
image: topic.image_url,
link: discourseBaseURL() + 't/' + topic.slug + '/' + topic.id,
@@ -103,7 +136,7 @@ async function getPostList(topic, user) {
liked: topic.like_count,
replies: topic.posts_count,
solution: topic.has_accepted_answer,
readTime: parseInt(500 / 100),
readTime: parseInt(500 / 200),
};
}

View File

@@ -1,21 +1,26 @@
/* Getting Started Card container */
.featuredGridContainer {
display: flex;
margin-left: 9%;
min-height: 600px;
}
.gridContainer {
display: grid;
place-content: center;
grid-template-columns: repeat(auto-fit, minmax(345px, 1fr));
grid-gap: 40px;
margin-left: 40px;
margin-right: 40px;
display: flex;
/* place-content: center; */
flex-wrap: wrap;
gap: 23px;
margin-left: 20px;
justify-content: left;
min-height: 600px;
width: 1500px;
}
.center {
margin: 0px auto;
max-width: 1300px;
margin-bottom: 50px;
}
.space {
height: 200px;
width: 50%;
width: 1500px;
}
.noFound {
@@ -26,10 +31,90 @@
margin: 50px;
}
.featuredSpinnerCenter {
width: 50%;
margin: auto;
margin-top: 45%;
}
.spinnerCenter {
margin: 100px auto;
max-width: 1300px;
margin-bottom: 50px;
}
position: absolute;
left: 48%;
top: 1025px;
}
@media only screen and (max-width: 1950px) {
.gridContainer {
width: 1225px;
}
.center {
width: 1225px;
}
}
@media only screen and (max-width: 1350px) {
.gridContainer {
width: 950px;
}
.center {
width: 950px;
}
}
@media only screen and (max-width: 1050px) {
.gridContainer {
width: 675px;
}
.center {
width: 675px;
}
}
@media only screen and (max-width: 700px) {
.gridContainer {
width: 275px;
}
.center {
width: 275px;
}
}
@media only screen and (max-width: 550px) {
.featuredGridContainer {
margin-left: 0%;
min-height: 350px;
width: 275px;
margin: auto;
}
.featuredCenter {
width: 275px;
margin: auto;
}
.gridContainer,
.featuredGridContainer {
justify-content: center;
margin-left: 0px;
}
.spinnerCenter {
left: 43%;
top: 1225px;
}
.featuredSpinnerCenter {
width: 20%;
margin: auto;
margin-top: 35%;
}
.noFound {
width: 350px;
position: absolute;
left: 10px;
}
}

View File

@@ -1,70 +1,56 @@
import React from 'react';
import clsx from 'clsx';
import styles from './styles.module.css';
import useBaseUrl from '@docusaurus/useBaseUrl';
import Link from '@docusaurus/Link';
import { getTags } from '../../../services/DiscourseService';
import BlogSidebarButton from './BlogSidebarButton';
import {getTags} from '../../../services/DiscourseService';
export default function BlogSidebar({
filterCallback
}) {
export default function BlogSidebar({filterCallback, defaultValue}) {
const [tagProductData, setTagProductData] = React.useState();
const [tagTechnologyData, setTagTechnologyData] = React.useState();
const [filterTags, setFilterTags] = React.useState(true);
let initialCheckState = defaultValue === 'Identityiq' ? true : false;
const [isChecked, setIsChecked] = React.useState(initialCheckState);
const handleChange = () => {
setIsChecked(!isChecked);
let value = isChecked ? 'Identity-Security-Cloud' : 'Identityiq';
filterCallback(value);
};
const getTagData = async () => {
const data = await getTags();
const tagTechnologyResultset = []
const tagProductResultset = []
const tagTechnologyResultset = [];
const tagProductResultset = [];
for (const tagGroup of data.extras.tag_groups) {
if (tagGroup.id === 45) {
for (const tag of tagGroup.tags) {
tagProductResultset.push(tag.text)
}
}
if (tagGroup.id === 17) {
for (const tag of tagGroup.tags) {
tagTechnologyResultset.push(tag.text)
tagProductResultset.push(tag.text);
}
}
}
setTagProductData(tagProductResultset)
setTagTechnologyData(tagTechnologyResultset)
setTagProductData(tagProductResultset);
};
function toggleSeeAll() {
filterTags ? setFilterTags(false) : setFilterTags(true)
}
React.useEffect(() => {
getTagData();
}, []);
},[]);
const filterText = filterTags ? 'See All Tags' : 'See Less Tags'
if (tagProductData && tagTechnologyData) {
if (tagProductData) {
return (
<div className={styles.sidebar}>
<div className={styles.tagHeader}>Blogs by Product</div>
<div className={styles.tagContainer}>
{tagProductData.map(function(a, index){
return <BlogSidebarButton key={a} text={a} filterCallback={filterCallback}></BlogSidebarButton>
})}
</div>
<div className={styles.tagHeader}>Blogs by Identity Governance</div>
<div className={styles.tagContainer}>
{tagTechnologyData.map(function(a, index){
return <div key={'div' + a} className={index > 10 && filterTags ? styles.hidden : ''} > <BlogSidebarButton key={a} text={a} filterCallback={filterCallback}></BlogSidebarButton></div>
})}
</div>
<div className={styles.seeAll} onClick={(e) => toggleSeeAll()}>
{filterText}
{/* <img className={styles.caretDown} src={useBaseUrl('/blog/caret-down-thin.svg')}></img> */}
<div className={styles.toggleContainer}>
<div className={styles.toggleWrapper}>
<input
type="checkbox"
id="product-toggle"
className={styles.toggleCheckbox}
checked={isChecked}
onChange={handleChange}
/>
<label htmlFor="product-toggle" className={styles.toggleLabel}>
<div className={styles.toggleBackground}></div>
<span className={styles.toggleTextLeft}>
Identity Security Cloud
</span>
<span className={styles.toggleTextRight}>IdentityIQ</span>
</label>
</div>
</div>
);
} else {
return <div></div>;

View File

@@ -1,8 +1,31 @@
.sidebar {
width: 400px;
height: 100%;
margin-top: 50px;
margin-left: 50px;
display: flex;
}
.toggleContainer {
margin: auto;
/* width: 31%; */
margin-bottom: 3%;
}
/* @media screen and (max-width: 2100px) {
.toggleContainer {
width: 43%;
}
}
@media screen and (max-width: 500px) {
.toggleContainer {
width: 82%;
}
} */
.title {
margin-left: 2%;
margin-bottom: 2%;
margin-top: 2%;
}
.tagHeader {
@@ -16,7 +39,6 @@
flex-wrap: wrap;
}
.hidden {
display: none;
}
@@ -30,7 +52,6 @@
width: 100%;
transition: background-color 500ms;
text-align: center;
}
.seeAll:hover {
@@ -38,3 +59,181 @@
background-color: var(--dev-text-color-normal);
color: var(--dev-card-background);
}
.dropdownContainer {
position: relative;
display: inline-block;
margin-left: 2%;
}
.buttonText {
float: left;
margin-left: 5%;
font-size: 16px;
font-weight: 300;
}
.dropdownButton {
background-color: var(
--dropdown-background
); /* Dropdown background color */ /* Text color */
padding: 14px 45px; /* Padding inside the dropdown button */
border: 2px solid #cccccc; /* Border around the dropdown */
border-radius: 5px; /* Rounded corners */
cursor: pointer; /* Change mouse cursor to indicate it's clickable */
position: relative; /* To position the arrow icon correctly */
display: inline-block; /* To keep the button's block behavior */
margin-bottom: 10%;
padding-left: 1%;
width: 220px;
height: 50px;
}
.dropdownButton:after {
content: '▼'; /* Adds a dropdown arrow after the button text */
position: absolute;
right: 10px; /* Position the arrow to the right */
top: 50%; /* Align vertically */
transform: translateY(-50%); /* Center the arrow vertically */
pointer-events: none; /* Prevent the arrow from being clickable */
}
.dropdownButton:hover {
background-color: var(
--dev-card-background
); /* Lighter background on hover */
}
.dropdownButton:focus {
outline: none; /* Remove default focus outline */
border-color: #666666; /* Darker border color when focused */
}
.dropdownContent {
display: none;
position: absolute;
background-color: var(--dropdown-background);
min-width: 300px;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
z-index: 1;
padding: 10px;
border-radius: 5px;
overflow-y: scroll;
max-height: 400px;
}
.dropdownContainer .dropdownContent label {
display: flex;
margin: 5px 5px;
}
.dropdownContainer:hover .dropdownContent {
display: block;
}
.dropdownItem {
display: flex;
}
.dropdownItem:hover {
--tw-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);
--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color),
0 1px 2px -1px var(--tw-shadow-color);
box-shadow: 0 0 transparent, 0 0 transparent, 0 1px 3px 0 rgba(0, 0, 0, 0.1),
0 1px 2px -1px rgba(0, 0, 0, 0.1);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000),
var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
background-color: var(--dev-tag-highlight);
}
.toggleWrapper {
position: relative;
width: 400px;
height: 50px;
border-radius: 25px;
background-color: #ddd;
overflow: hidden;
box-shadow: 0 2px 5px rgba(0, 0, 0, 0.2);
}
.toggleCheckbox {
display: none;
}
.toggleCheckbox:checked + .toggleLabel .toggleBackground {
transform: translateX(100%);
background-color: #93d500; /* IIQ color */
}
.toggleCheckbox:not(:checked) + .toggleLabel .toggleBackground {
background-color: #cc27b0;
}
.toggleLabel {
display: flex;
align-items: center;
position: relative;
height: 100%;
cursor: pointer;
}
.toggleBackground {
position: absolute;
left: 0;
top: 0;
bottom: 0;
width: 50%;
background-color: #cc27b0; /* Default color for "Identity Security Cloud" */
border-radius: 25px;
transition: transform 0.25s ease, background-color 0.25s ease;
}
.toggleText {
width: 50%;
text-align: center;
z-index: 1;
transition: color 0.25s;
}
.toggleCheckbox:not(:checked) + .toggleLabel .toggleTextLeft {
width: 50%;
text-align: center;
z-index: 1;
transition: color 0.25s;
color: white; /* "Identity Security Cloud" highlighted */
}
.toggleCheckbox:not(:checked) + .toggleLabel .toggleTextRight {
width: 50%;
text-align: center;
z-index: 1;
transition: color 0.25s;
color: #415364; /* "IdentityIQ" not highlighted */
}
.toggleCheckbox:checked + .toggleLabel .toggleTextRight {
width: 50%;
text-align: center;
z-index: 1;
transition: color 0.25s;
color: white; /* "IdentityIQ" highlighted */
}
.toggleCheckbox:checked + .toggleLabel .toggleTextLeft {
width: 50%;
text-align: center;
z-index: 1;
transition: color 0.25s;
color: #415364; /* "Identity Security Cloud" not highlighted */
}
@media only screen and (max-width: 550px) {
.toggleTextLeft {
font-size: 14px;
}
.toggleWrapper {
width: 325px;
height: 40px;
}
}

View File

@@ -4,6 +4,8 @@ import Link from '@docusaurus/Link';
import useBaseUrl from '@docusaurus/useBaseUrl';
import ThemedImage from '@theme/ThemedImage';
import {addDarkToFileName} from '../../../util/util';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faSquareCheck, faEye} from '@fortawesome/pro-duotone-svg-icons';
export default function DiscussCard({
link,
title,
@@ -20,12 +22,11 @@ export default function DiscussCard({
let linkText = <div className={styles.linkText}>Join the Discussion</div>;
if (solution) {
solved = (
<ThemedImage
<FontAwesomeIcon
icon={faSquareCheck}
className={styles.cardSolved}
sources={{
light: useBaseUrl('/homepage/solved.png'),
dark: useBaseUrl(addDarkToFileName('/homepage/solved.png')),
}}></ThemedImage>
size="lg"
/>
);
linkText = <div className={styles.linkSolvedText}>View the Solution</div>;
}
@@ -44,12 +45,7 @@ export default function DiscussCard({
light: useBaseUrl('/homepage/arrow-right.png'),
dark: useBaseUrl('/homepage/arrow-right-dark.png'),
}}></ThemedImage>
<ThemedImage
className={styles.cardEye}
sources={{
light: useBaseUrl('/homepage/eyeball.png'),
dark: useBaseUrl(addDarkToFileName('/homepage/eyeball.png')),
}}></ThemedImage>
<FontAwesomeIcon icon={faEye} className={styles.cardEye} size="lg" />
<img
className={styles.cardLiked}
src={useBaseUrl('/homepage/liked.png')}></img>
@@ -64,7 +60,11 @@ export default function DiscussCard({
if (index > 2) {
return '';
}
return <div key={tag} className={styles.tag}>{tag}</div>;
return (
<div key={tag} className={styles.tag}>
{tag}
</div>
);
})}
</div>
</div>

View File

@@ -4,11 +4,19 @@
margin-top: 20px;
height: 300px;
/* UI Properties */
background: var(--dev-card-background);
box-shadow: var(--dev-card-shadow);
border: 1px solid var(--dev-card-background);
border-radius: 40px;
opacity: 1;
transition: all 0.3s;
background: var(--dev-new-car-background);
border-radius: 0.5rem;
--tw-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);
--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color),
0 1px 2px -1px var(--tw-shadow-color);
border-radius: 0.5rem;
box-shadow: 0 0 transparent, 0 0 transparent, 0 1px 3px 0 rgba(0, 0, 0, 0.1),
0 1px 2px -1px rgba(0, 0, 0, 0.1);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000),
var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
border: 1px solid var(--dev-card-border);
}
.card:hover {
@@ -85,6 +93,7 @@
right: 132px;
top: 18px;
width: 20px;
color: var(--ifm-color-primary);
}
.cardDiscuss {
@@ -99,8 +108,8 @@
.cardEye {
position: absolute;
margin: 20px;
right: 25px;
top: 0;
right: 28px;
top: -3px;
width: 22px;
}

View File

@@ -4,6 +4,12 @@ import Link from '@docusaurus/Link';
import useBaseUrl from '@docusaurus/useBaseUrl';
import {addDarkToFileName} from '../../../util/util';
import ThemedImage from '@theme/ThemedImage';
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {
faComments,
faPeopleGroup,
faUser,
} from '@fortawesome/pro-duotone-svg-icons';
export default function HomepageBasics({
link,
title,
@@ -21,14 +27,47 @@ export default function HomepageBasics({
</div>
);
}
let icon = '';
if (image) {
switch (image) {
case 'discuss': {
icon = (
<FontAwesomeIcon
icon={faComments}
className={styles.docCardIcon}
size="3x"
/>
);
break;
}
case 'team': {
icon = (
<FontAwesomeIcon
icon={faPeopleGroup}
className={styles.docCardIcon}
size="3x"
/>
);
break;
}
case 'user': {
icon = (
<FontAwesomeIcon
icon={faUser}
className={styles.docCardIcon}
size="3x"
/>
);
break;
}
default: {
icon = '';
}
}
}
return (
<div className={styles.gettingStartedText}>
<ThemedImage
className={styles.gettingStartedCardIcon}
sources={{
light: useBaseUrl(image),
dark: useBaseUrl(addDarkToFileName(image)),
}}></ThemedImage>
{icon}
<div className={styles.gettingStartedOne}>{title}</div>
<div
className={styles.gettingStartedThree}

View File

@@ -57,7 +57,7 @@
box-shadow: 0px 20px 60px #00000015;
border: 2px solid #df61ca;
border-radius: 5px;
transition: all 0.3s;
transition: all 0.3s;
}
.link {
@@ -67,3 +67,7 @@
color: #df61ca;
text-decoration: none;
}
.docCardIcon {
color: var(--ifm-color-primary);
}

View File

@@ -11,18 +11,25 @@ export default function HomepageCard({link, title, image, product}) {
return (
<Link to={link}>
<div className={styles.card}>
<ThemedImage
className={styles.cardIcon}
sources={{
light: useBaseUrl(image),
dark: useBaseUrl(addDarkToFileName(image)),
}}></ThemedImage>
<ThemedImage
className={styles.cardArrow}
sources={{
light: useBaseUrl('/homepage/arrow-right.png'),
dark: useBaseUrl('/homepage/arrow-right-dark.png'),
}}></ThemedImage>
{image && (
<>
<ThemedImage
className={styles.cardIcon}
sources={{
light: useBaseUrl(image),
dark: useBaseUrl(addDarkToFileName(image)),
}}
/>
<ThemedImage
className={styles.cardArrow}
sources={{
light: useBaseUrl('/homepage/arrow-right.png'),
dark: useBaseUrl('/homepage/arrow-right-dark.png'),
}}
/>
</>
)}
<div className={styles.cardText}>{title}</div>
<div className={`${styles.product} ${productStyles}`}>{product}</div>
</div>

View File

@@ -1,14 +1,22 @@
/* Getting Started Card */
.card {
position: relative;
margin-top: 20px;
height: 200px;
/* UI Properties */
background: var(--dev-card-background);
box-shadow: var(--dev-card-shadow);
border: 1px solid var(--dev-card-background);
border-radius: 40px;
position: relative;
margin-top: 30px;
margin-bottom: 30px;
opacity: 1;
transition: all 0.3s;
background: var(--dev-new-car-background);
border-radius: 0.5rem;
--tw-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);
--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color),
0 1px 2px -1px var(--tw-shadow-color);
border-radius: 0.5rem;
box-shadow: 0 0 transparent, 0 0 transparent, 0 1px 3px 0 rgba(0, 0, 0, 0.1),
0 1px 2px -1px rgba(0, 0, 0, 0.1);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000),
var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
border: 1px solid var(--dev-card-border);
}
.card:hover {
@@ -32,7 +40,7 @@
margin: 20px;
top: 0;
left: 0;
width: 38px;
width: 30%;
}
.cardArrow {
@@ -60,3 +68,9 @@
.iiq {
color: #60b359;
}
@media only screen and (max-width: 570px) {
.cardIcon {
width: 15%;
}
}

View File

@@ -0,0 +1,50 @@
import React from 'react';
import clsx from 'clsx';
import styles from './styles.module.css';
import Link from '@docusaurus/Link';
import HomepageCard from '../HomepageCard';
import useBaseUrl from '@docusaurus/useBaseUrl';
import ThemedImage from '@theme/ThemedImage';
export default function HomepageGettingStarted() {
return (
<div>
<div className={styles.mainCard}>
<div className={styles.contentContainer}>
<div className={styles.gettingStartedText}>
<div className={styles.gettingStartedOne}>
SailPoint Developer Days
</div>
<div className={styles.gettingStartedTwo}>
Join us for our annual conference for developers in April!
</div>
<div className={styles.gettingStartedThree}>
Our largest conference for developers, architects, administrators,
and more is finally here! Explore
<span className={styles.bold}> cutting-edge technologies</span>,
gain insights from industry leaders, and connect with fellow
developers.
</div>
</div>
<div className={styles.gridContainer}>
<div>
<a
href="https://developer.sailpoint.com/discuss/developerdays"
className={styles.registerButtonPink}>
Register now
</a>
</div>
</div>
</div>
<div className={styles.devDaysImageContainer}>
<ThemedImage
className={styles.cardIcon}
sources={{
light: useBaseUrl('/homepage/devdays2024-sm.png'),
dark: useBaseUrl('/homepage/devdays2024-dark-sm.png'),
}}
/>
</div>
</div>
</div>
);
}

View File

@@ -0,0 +1,144 @@
.mainCard {
display: grid;
grid-gap: 20px;
max-width: 1180px;
justify-content: center;
}
.gettingStartedText {
margin-top: 50px;
margin-left: 50px;
}
.gettingStartedOne {
color: var(--ifm-color-primary);
font-size: 30px;
max-width: 396px;
font-weight: bold;
line-height: 100%;
}
.gettingStartedTwo {
margin-top: 20px;
font-size: 20px;
font-weight: bold;
}
.gettingStartedThree {
margin-top: 20px;
font-size: 16px;
font-weight: 500;
margin-bottom: 20px;
}
.bold {
font-weight: bold;
}
/* Getting Started Card container */
.gridContainer {
display: grid;
margin-left: 50px;
margin-bottom: 50px;
grid-gap: 20px;
grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
place-content: center;
margin-right: 40px;
}
.video {
width: 100%;
height: 300px;
max-width: 520px;
}
.cardIcon {
width: 90%;
margin-top: 100px;
}
.registerButton {
align-items: center;
background: var(--ifm-color-primary);
border-radius: 0.5rem;
border: none;
color: #eee;
display: inline-flex;
padding: 10px 15px;
margin-right: 45px;
font-size: 16px;
font-weight: bold;
}
.registerButtonPink {
text-decoration: none;
text-align: center;
margin: 10px auto;
font-size: 20px;
font-weight: bold;
line-height: 100%;
width: 259px;
height: 61px;
line-height: 61px;
background: transparent 0% 0% no-repeat padding-box;
opacity: 1;
box-shadow: 0px 20px 60px #00000015;
border: 2px solid #df61ca;
border-radius: 0.5rem;
color: #df61ca;
padding: 10px 15px;
margin-right: 45px;
font-size: 16px;
font-weight: bold;
}
.registerButtonPink:hover {
cursor: pointer;
top: -2px;
box-shadow: 0 4px 5px rgba(0, 0, 0, 0.2);
background-color: var(--dev-button-hover);
color: #cc27b0;
text-decoration: none;
}
@media only screen and (max-width: 1178px) {
.carousel {
margin-top: 0px;
margin-left: 50px;
margin-bottom: 50px;
}
.contentContainer {
width: 600px;
}
}
@media only screen and (min-width: 1179px) {
.carousel {
margin-top: 100px;
}
.mainCard {
grid-template-columns: repeat(auto-fit, minmax(520px, 1fr));
/* UI Properties */
background: var(--main-hero-card-background);
box-shadow: var(--dev-main-card-shadow);
border: 1px solid var(--dev-card-background);
border-radius: 40px;
opacity: 1;
margin: 50px auto;
width: calc(100% - 100px);
}
}
@media only screen and (max-width: 1200px) {
.devDaysImageContainer {
display: none;
}
}
@media only screen and (max-width: 570px) {
.video {
display: none;
}
.contentContainer {
width: auto;
}
.carousel {
margin-bottom: 0px;
}
}

View File

@@ -25,13 +25,13 @@ export default function HomepageGettingStarted() {
<HomepageCard
link={'/docs'}
title={'Get Started with Identity Security Cloud'}
image={'/homepage/cloud.png'}></HomepageCard>
image={'/homepage/SailPointIdentitySecurityCloud.svg'}></HomepageCard>
<HomepageCard
link={
'https://documentation.sailpoint.com/identityiq/help/iiqlandingpage.html'
}
title={'Get Started with IdentityIQ'}
image={'/homepage/cloud.png'}></HomepageCard>
image={'/homepage/IdentityIQ.svg'}></HomepageCard>
</div>
</div>
<div className={styles.carousel}>

View File

@@ -25,7 +25,7 @@ export default function HomepageTeam() {
expertise={'Identity Security Cloud'.toUpperCase()}
image={'/homepage/tyler-mairose.png'}
/>
<TeamCard
<TeamCard
link={'https://developer.sailpoint.com/discuss/u/jthaytko/summary'}
name={'James Haytko'}
title={'Technical Writer'.toUpperCase()}
@@ -41,9 +41,6 @@ export default function HomepageTeam() {
expertise={'DEVELOPER TOOLS'}
image={'/homepage/phil-ellis.png'}
/>
</div>
<div className={styles.bottomGridContainer}>
<TeamCard
link={'https://developer.sailpoint.com/discuss/u/Darrell/summary'}
name={'Darrell Thobe'}
@@ -51,14 +48,14 @@ export default function HomepageTeam() {
expertise={'DEVELOPER TOOLS'}
image={'/homepage/darrell-thobe.png'}
/>
<TeamCard
link={'https://developer.sailpoint.com/discuss/u/lukehagar/summary'}
name={'Luke Hagar'}
title={'Software Engineer'.toUpperCase()}
expertise={'DEVELOPER TOOLS'}
image={'/homepage/lukehagar.png'}
<TeamCard
link={'https://developer.sailpoint.com/discuss/u/putty/summary'}
name={'Derek Putnam'}
title={'Community Manager'.toUpperCase()}
expertise={'DEVELOPER COMMUNITY'}
image={'/homepage/derek-putnam.png'}
/>
<TeamCard
<TeamCard
link={
'https://developer.sailpoint.com/discuss/u/jordan_violet/summary'
}

View File

@@ -1,24 +1,45 @@
/* Getting Started Card container */
.bottomGridContainer {
display: grid;
place-content: center;
grid-template-columns: repeat(auto-fit, minmax(195px, 1fr));
grid-gap: 30px;
margin-right: 170px;
margin-left: 190px;
}
.gridContainer {
display: grid;
place-content: center;
grid-template-columns: repeat(auto-fit, minmax(195px, 1fr));
display: flex;
grid-template-columns: 1fr 1fr 1fr 1fr;
flex-wrap: wrap;
grid-gap: 40px;
margin-left: 40px;
margin-right: 40px;
width: 1100px;
margin-left: 20px;
}
.center {
margin: 0px auto;
max-width: 1300px;
margin-bottom: 50px;
width: 1100px;
margin: auto;
padding-bottom: 5%;
}
@media only screen and (max-width: 1150px) {
.gridContainer {
width: 820px;
}
.center {
width: 820px;
}
}
@media only screen and (max-width: 920px) {
.gridContainer {
width: 560px;
}
.center {
width: 560px;
}
}
@media only screen and (max-width: 660px) {
.gridContainer {
width: 220px;
margin-left: 0px;
}
.center {
width: 220px;
}
}

View File

@@ -11,24 +11,25 @@ export default function HomepageTrainingGuides() {
product={'isc'}
link={'/docs/api/getting-started'}
title={'Make Your First API Call'}
image={'/homepage/cloud.png'}></HomepageCard>
image={'/homepage/cloud-data.svg'}></HomepageCard>
<HomepageCard
product={'isc'}
link={'/docs/extensibility/transforms/guides/your-first-transform'}
title={'Build a Transform'}
image={'/homepage/cloud.png'}></HomepageCard>
image={'/homepage/process.svg'}></HomepageCard>
<HomepageCard
product={'isc'}
link={'/docs/connectivity/saas-connectivity'}
title={'Build a SaaS Connector'}
image={'/homepage/cloud.png'}></HomepageCard>
image={'/homepage/connectivity.svg'}></HomepageCard>
<HomepageCard
product={'iiq'}
link={
'https://documentation.sailpoint.com/identityiq/help/plugins/identityiq_plugins.html'
}
title={'Build an IIQ Plugin'}
image={'/homepage/cloud.png'}></HomepageCard>
image={'/homepage/puzzle.svg'}
isThemedImage={true}></HomepageCard>
</div>
</div>
);

View File

@@ -5,11 +5,19 @@
height: 300px;
width: 220px;
/* UI Properties */
background: var(--dev-card-background);
box-shadow: var(--dev-card-shadow);
border: 1px solid var(--dev-card-background);
border-radius: 40px;
opacity: 1;
transition: all 0.3s;
background: var(--dev-new-car-background);
border-radius: 0.5rem;
--tw-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);
--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color),
0 1px 2px -1px var(--tw-shadow-color);
border-radius: 0.5rem;
box-shadow: 0 0 transparent, 0 0 transparent, 0 1px 3px 0 rgba(0, 0, 0, 0.1),
0 1px 2px -1px rgba(0, 0, 0, 0.1);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000),
var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
border: 1px solid var(--dev-card-border);
}
.card:hover {

View File

@@ -1,22 +1,13 @@
import React from 'react';
import clsx from 'clsx';
import styles from './styles.module.css';
import useBaseUrl from '@docusaurus/useBaseUrl';
import Link from '@docusaurus/Link';
export default function MarketplaceBanner() {
return (
<div>
<div className={styles.imageContainer}>
<img className={styles.headerImage} src={useBaseUrl('/blog/marketplace_banner_template.png')}></img>
<div className={styles.blogHeaderText}>
CoLab
</div>
</div >
</div>
<Link to={'/colab'}>
<div className={styles.titleContainer}>
{/* <h1 className={styles.colabTitle}>CoLab</h1> */}
</div>
</Link>
);
}

View File

@@ -1,28 +1,20 @@
.blogHeaderText {
position: relative;
.colabTitle {
text-align: center;
margin-bottom: 2%;
color: #ffffff;
font-size: 48px;
max-width: 459px;
font-weight: bold;
line-height: 130%;
top: -84px;
left: 101px;
position: relative;
top: 25px;
}
.background {
.titleContainer {
width: 100%;
object-fit: repeat;
height: 100%;
height: 45px;
background: rgb(0, 51, 161);
background: linear-gradient(
90deg,
rgba(0, 51, 161, 1) 0%,
rgba(84, 192, 232, 1) 100%
);
align-content: center;
margin-bottom: 2%;
}
.imageContainer {
width: 100%;
height: 90px;
background: rgb(0,51,161);
background: linear-gradient(90deg, rgba(0,51,161,1) 0%, rgba(84,192,232,1) 100%);
}
.headerImage {
height: 90px;
}

View File

@@ -1,84 +1,64 @@
import React from 'react';
import styles from './styles.module.css';
import Link from '@docusaurus/Link';
import useBaseUrl from '@docusaurus/useBaseUrl';
import ThemedImage from '@theme/ThemedImage';
import {addDarkToFileName} from '../../../util/util';
import ReactMarkdown from 'react-markdown'
export default function MarketplaceCard({
post,
openDialogFunc,
}) {
import {FontAwesomeIcon} from '@fortawesome/react-fontawesome';
import {faShieldCheck, faAward} from '@fortawesome/pro-solid-svg-icons';
import Link from '@docusaurus/Link';
function setFilters(e) {
openDialogFunc({"title": post.title, "image": post.image, "link": post.link, "id": post.id});
}
let badge = (
<div></div>
);
if (post.tags.includes("sailpoint-developed")) {
export default function MarketplaceCard({post, featured}) {
let badge = <div></div>;
if (post.tags.includes('sailpoint-developed')) {
badge = (
<div className={styles.cardBadge}>
<img
className={styles.cardBadgeImage}
src={useBaseUrl('/icons/SailPoint-LogoIcon-RGB-Color.svg')}></img>
<img
className={styles.cardBadgeImage}
src={useBaseUrl('/icons/SailPoint-LogoIcon-RGB-Color.svg')}></img>
<span className={styles.cardBadgeText}>SailPoint Developed</span>
</div>
);
} else if (post.tags.includes("sailpoint-certified")) {
} else if (post.tags.includes('sailpoint-certified')) {
badge = (
<div className={styles.cardBadgeCertified}>
<img
className={styles.cardBadgeCertifiedImage}
src={useBaseUrl('/marketplace/award-simple-sharp-solid.svg')}></img>
<span>SailPoint Certified</span>
<div className={styles.badgeContainer}>
<div title="SailPoint Certified" className={styles.cardBadgeCertified}>
<FontAwesomeIcon
icon={faShieldCheck}
className={styles.docCardIcon}
size="2x"
/>
</div>
</div>
);
}
return (
<div onClick={(e) => setFilters(e)}>
<div className={styles.card} >
<Link to={post.link}>
{/* <div onClick={(e) => setFilters(e)}> */}
<div className={featured ? styles.featuredCard : styles.card}>
<div className={styles.cardText}>
<img className={styles.cardImage} src={useBaseUrl(post.image)}></img>
<div className={styles.cardTitle}>{post.title}</div>
<div className={styles.tags}>
{post.tags?.map((tag, index) => {
if (index > 2 || tag == 'sailpoint-certified' || tag == 'sailpoint-authored') {
return '';
}
return <div key={tag} className={styles.tag}>{tag}</div>;
})}
<img
className={featured ? styles.featuredCardImage : styles.cardImage}
src={useBaseUrl(post.image)}></img>
<div className={styles.split}></div>
<div
className={featured ? styles.featuredCardTitle : styles.cardTitle}>
{post.title}
</div>
<div className={styles.cardUser}>
<img
className={featured ? styles.featuredCardFace : styles.cardFace}
src={useBaseUrl(post.creatorImage)}></img>
<div className={styles.cardName}>{post.creatorName}</div>
<div className={styles.cardCreatorTitle}>{post.creatorTitle}</div>
<div></div>
</div>
<div className={styles.cardBody}>{post.excerpt}</div>
</div>
<div className={styles.cardData}>
<img className={styles.cardEye} src={useBaseUrl('/blog/eye-regular.svg')}></img>
<div className={styles.cardCommentText}>{post.views}</div>
<img className={styles.cardComment} src={useBaseUrl('/blog/comment-light.svg')}></img>
<div className={styles.cardCommentText}>{post.replies}</div>
</div>
{/* <div className={styles.cardUser}>
<img className={styles.cardFace} src={useBaseUrl(post.creatorImage)}></img>
<div className={styles.cardName}>{post.name}</div>
</div> */}
{badge}
</div>
</div>
{/* </div> */}
</Link>
);
}

View File

@@ -1,16 +1,53 @@
/* Getting Started Card */
.card {
position: relative;
margin-top: 20px;
height: 500px;
/* UI Properties */
background: var(--dev-card-background);
box-shadow: var(--dev-card-shadow);
border: 1px solid var(--dev-card-background);
border-radius: 40px;
margin-top: 30px;
margin-bottom: 30px;
opacity: 1;
transition: all 0.3s;
max-width: 400px;
transition: all 0.3s;
width: 275px;
height: 393.17px;
background: var(--dev-new-car-background);
border-radius: 0.5rem;
--tw-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);
--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color),
0 1px 2px -1px var(--tw-shadow-color);
border-radius: 0.5rem;
box-shadow: 0 0 transparent, 0 0 transparent, 0 1px 3px 0 rgba(0, 0, 0, 0.1),
0 1px 2px -1px rgba(0, 0, 0, 0.1);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000),
var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
border: 1px solid var(--dev-card-border);
}
.featuredCard {
position: relative;
margin-top: 30px;
margin-bottom: 30px;
opacity: 1;
transition: all 0.3s;
width: 400px;
height: 475px;
background: var(--dev-new-car-background);
border-radius: 0.5rem;
--tw-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);
--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color),
0 1px 2px -1px var(--tw-shadow-color);
border-radius: 0.5rem;
box-shadow: 0 0 transparent, 0 0 transparent, 0 1px 3px 0 rgba(0, 0, 0, 0.1),
0 1px 2px -1px rgba(0, 0, 0, 0.1);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000),
var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
border: 1px solid var(--dev-card-border);
}
.featuredCardImage {
overflow: hidden;
position: relative;
width: 400px;
height: 225px;
border-top-left-radius: 0.5rem;
border-top-right-radius: 0.5rem;
}
.card:hover {
@@ -19,131 +56,98 @@
box-shadow: var(--dev-card-selected);
}
.cardText {
display: flex;
flex-direction: column;
margin: 22px;
min-width: 170px;
margin-bottom: 75px;
}
.cardImage {
border-radius: 4px;
justify-content: center;
overflow: hidden;
position: relative;
height: 144px;
border-top-left-radius: 0.5rem;
border-top-right-radius: 0.5rem;
aspect-ratio: 16/9;
}
.split {
background: linear-gradient(
145deg,
rgba(0, 51, 161, 1) 40%,
rgba(0, 79, 181, 1) 65%,
rgba(0, 113, 206, 1) 100%
);
height: 15px;
}
.featuredCardTitle {
font-size: 20px;
font-weight: 500;
display: flex;
gap: 1rem;
margin-top: 1rem;
padding-right: 1rem;
padding-left: 1rem;
text-align: left;
height: 80px;
}
.cardTitle {
font-size: 22px;
font-weight: 700;
font-size: 16px;
font-weight: 500;
display: flex;
gap: 1rem;
margin-top: 1rem;
padding-right: 0.5rem;
padding-left: 0.5rem;
text-align: left;
height: 80px;
color: var(--ifm-color-primary);
}
.cardBody {
font-size: 16px;
font-weight: 500;
top: 10px;
left: 0;
font-size: 14px;
font-weight: 400;
height: 80px;
margin-top: 1rem;
padding-right: 0.5rem;
padding-left: 0.5rem;
text-align: left;
}
.tag {
font-size: 16px;
font-weight: 500;
color: var(--dev-secondary-text);
background-color: var(--dev-tag-highlight);
padding: 0px 8px;
margin-right: 5px;
margin-top: 5px;
}
.tags {
margin: 0px;
width: 100%;
display: flex;
flex-wrap: wrap;
margin-bottom: 10px;
}
.cardUser {
position: absolute;
bottom: 10px;
left: 25px;
display: flex;
margin-top: 15px;
margin-bottom: 5%;
}
.featuredCardFace {
border-radius: 9999px;
margin-left: 2%;
height: 50px;
width: 50px;
}
.cardFace {
border-radius: 9999px;
margin: auto;
margin-left: 2%;
height: 40px;
width: 40px;
}
.cardName {
min-width: 170px;
font-weight: 800;
margin-top: 7px;
margin-left: 10px;
}
.cardData {
position: absolute;
bottom: 20px;
left: 20px;
display: flex;
align-items: center;
color: var(--text-color);
}
.cardEye {
fill: #96a9bb;
/* computed from codepen https://codepen.io/sosuke/pen/Pjoqqp */
filter: invert(79%) sepia(12%) saturate(484%) hue-rotate(168deg) brightness(84%) contrast(84%);
margin-left: 5px;
height: 18px;
width: 18px;
}
.cardComment {
.cardCreatorTitle {
min-width: 170px;
margin-left: 10px;
fill: #96a9bb;
/* computed from codepen https://codepen.io/sosuke/pen/Pjoqqp */
filter: invert(79%) sepia(12%) saturate(484%) hue-rotate(168deg) brightness(84%) contrast(84%);
height: 18px;
width: 18px;
margin-bottom: 4px;
display: flex;
color: var(--text-color);
}
.cardCommentText {
color: #96a9ba;
/* computed from codepen https://codepen.io/sosuke/pen/Pjoqqp */
margin-left: 5px;
margin-bottom: 5px;
height: 20px;
font-size: 16px;
}
/* .cardBadge {
position: absolute;
right: 5px;
bottom: 0px;
margin-left: 5px;
height: 80px;
width: 80px;
} */
.cardBadge {
position: absolute;
display: flex;
@@ -161,20 +165,24 @@
width: 80px;
}
.cardBadgeText {
margin-left: -19px;
margin-top: 15px;
.certifiedText {
position: absolute;
top: 107px;
left: 95px;
color: var(--dev-text-color-secondary);
visibility: hidden;
}
.cardBadgeText {
margin-top: 15px;
color: var(--dev-text-color-secondary);
}
.cardBadgeCertified {
position: absolute;
display: flex;
right: 85px;
bottom: 15px;
margin-left: 5px;
height: 50px;
width: 50px;
width: 250px;
left: 225px;
bottom: 10px;
color: var(--dev-text-color-secondary);
}
@@ -183,4 +191,45 @@
height: 50px;
width: 50px;
filter: var(--dev-icon-color-primary);
}
}
.docCardIcon {
margin-right: 0.5em;
color: var(--ifm-color-primary);
padding-top: 4%;
padding-left: 4%;
}
.badgeText {
margin-top: 6%;
}
.badgeContainer:hover .certifiedText {
visibility: visible;
}
@media only screen and (max-width: 550px) {
.cardText {
margin-bottom: 0px;
}
.featuredCard {
width: 275px;
max-width: 275px;
height: 393.17px;
}
.featuredCardImage {
width: 275px;
height: 144px;
}
.featuredCardTitle {
font-size: 16px;
}
.featuredCardFace {
height: 40px;
width: 40px;
}
}

View File

@@ -3,39 +3,71 @@ import styles from './styles.module.css';
import MarketplaceCard from '../MarketplaceCard';
import Modal from 'react-modal';
import useBaseUrl from '@docusaurus/useBaseUrl';
import BounceLoader from 'react-spinners/BounceLoader';
import NewtonsCradle from '../../newtonsCradle';
import {discourseBaseURL, developerWebsiteDomain} from '../../../util/util';
import {
getMarketplacePosts,
getMarketplaceTopic,
getMarketplaceTopicRaw,
getUserTitle,
} from '../../../services/DiscourseService';
import MarketplaceCardDetail from '../MarketplaceCardDetail';
export default function MarketplaceCards({filterCallback}) {
export default function MarketplaceCards({
filterCallback,
featured,
limit,
multiple,
}) {
const [cardData, setCardData] = React.useState();
const [detailsOpen, setDetailsOpen] = React.useState(false);
const [details, setDetails] = React.useState('');
const [loadingCards, setLoadingCards] = React.useState(true);
const handleCloseModal = () => {
setDetailsOpen(false);
}
const xImage = useBaseUrl('/icons/circle-xmark-regular.svg');
const getPosts = async () => {
const data = await getMarketplacePosts(filterCallback.tags.join('+'), filterCallback.category);
let tags = filterCallback.tags;
if (featured) {
tags = ['featured'];
}
const data = await getMarketplacePosts(
tags ? tags.join('+') : '',
filterCallback.category,
);
const resultset = [];
const titleList = [];
if (data.topic_list) {
for (const topic of data.topic_list.topics) {
if (topic.tags.length > 0) {
let poster = {}
let poster = {};
for (let topicUser of topic.posters) {
if (topicUser.description.includes("Original Poster")) {
if (topicUser.description.includes('Original Poster')) {
for (let user of data.users) {
if (user.id === topicUser.user_id) {
poster = user
if (
!titleList.find((x) => x.group === user.primary_group_name)
) {
let usertitle = await getUserTitle(user.primary_group_name);
if (usertitle.group === undefined) {
titleList.push({
group: user.primary_group_name,
title: '',
});
} else {
titleList.push({
group: user.primary_group_name,
title: usertitle.group.title || '',
});
user.title = usertitle.group.title;
}
} else {
user.title = titleList.find(
(x) => x.group === user.primary_group_name,
).title;
}
poster = user;
}
}
}
@@ -44,7 +76,11 @@ export default function MarketplaceCards({filterCallback}) {
resultset.push(await getPostList(topic, poster));
}
}
setCardData(resultset);
if (limit) {
setCardData(resultset.slice(0, limit));
} else {
setCardData(resultset);
}
} else {
setCardData(undefined);
}
@@ -68,82 +104,104 @@ export default function MarketplaceCards({filterCallback}) {
Modal.setAppElement('#__docusaurus');
React.useEffect(() => {
getPosts();
setCardData(undefined);
setLoadingCards(true);
}, [filterCallback]);
const xImage = useBaseUrl('/icons/circle-xmark-regular.svg')
if (cardData && cardData.length > 0) {
return (
<div className={styles.center}>
<div className={styles.gridContainer}>
{cardData.map(function (a, index) {
return (
<MarketplaceCard
post={a}
key={index + a.link}
openDialogFunc={openDialog}></MarketplaceCard>
);
})}
</div>
<Modal
isOpen={detailsOpen}
className={styles.modal}
onRequestClose={handleCloseModal}
contentLabel="Details">
<div>
<div>
<MarketplaceCardDetail
details={details.data}
rawPost={details.raw}></MarketplaceCardDetail>
return (
<div className={featured ? styles.featuredCenter : styles.center}>
{loadingCards ? (
// Show loading icon when data is still loading
<div>
{featured ? (
<div className={styles.featuredSpinnerCenter}>
<NewtonsCradle />
</div>
<img
className={styles.cardExit}
src={xImage}
onClick={async () => {
setDetailsOpen(false);
}}></img>
) : (
<div
className={
multiple
? styles.spinnerCenterMultiple
: styles.spinnerCenterSingle
}>
<NewtonsCradle />
</div>
)}
</div>
) : cardData && cardData.length > 0 ? (
<>
{multiple ? (
<div className={styles.multipleGridContainer}>
{cardData.map(function (a, index) {
return (
<MarketplaceCard
featured={featured}
post={a}
key={index + a.link}
openDialogFunc={openDialog}></MarketplaceCard>
);
})}
</div>
) : (
<div
className={
featured ? styles.featuredGridContainer : styles.gridContainer
}>
{cardData.map(function (a, index) {
return (
<MarketplaceCard
featured={featured}
post={a}
key={index + a.link}
openDialogFunc={openDialog}></MarketplaceCard>
);
})}
</div>
)}
</>
) : (
<div>
<div className={styles.noFound}>
{' '}
Hey there, looks like no integrations match your search criteria.
Check out our{' '}
<a href="https://developer.sailpoint.com/discuss/t/about-the-sailpoint-developer-community-colab/11230">
getting started guide
</a>
, and consider being the first to contribute this integration!
</div>
</Modal>
</div>
);
} else if (loadingCards) {
return (
<BounceLoader
className={styles.spinnerCenter}
color={'#0033a1'}
loading={true}
size={150}
aria-label="Loading Spinner"
data-testid="loader"
/>
);
} else {
return (
<div className={styles.noFound}>
{' '}
Hey there, looks like no integrations match your search criteria. Check out our <a href='https://developer.sailpoint.com/discuss/t/about-the-sailpoint-developer-community-colab/11230'>getting started guide</a>, and consider being the first to contribute this integration!
</div>
);
</div>
)}
</div>
);
}
function shortenTitle(title) {
if (title.length > 63) {
return title.substring(0, 62) + '...';
}
return title;
}
function shortenDesc(desc) {
if (desc.length > 93) {
return desc.substring(0, 93) + '...';
}
return desc;
}
async function getPostList(topic, user) {
return {
id: topic.id,
name: user.name,
excerpt: styleExcerpt(topic.excerpt),
creatorName: user.name,
excerpt: shortenDesc(styleExcerpt(topic.excerpt)),
creatorImage: getavatarURL(user.avatar_template),
creatorTitle: user.title,
tags: topic.tags,
image: topic.image_url,
link:
discourseBaseURL() + 't/' +
topic.slug +
'/' +
topic.id,
title: topic.title,
link: discourseBaseURL() + 't/' + topic.slug + '/' + topic.id,
title: shortenTitle(topic.title),
views: topic.views,
liked: topic.like_count,
replies: topic.posts_count,
@@ -154,9 +212,11 @@ async function getPostList(topic, user) {
function getavatarURL(avatar) {
if (avatar.includes(developerWebsiteDomain())) {
return "https://" + developerWebsiteDomain() + avatar.replace("{size}", "120")
return (
'https://' + developerWebsiteDomain() + avatar.replace('{size}', '120')
);
} else {
return avatar.replace("{size}", "120")
return avatar.replace('{size}', '120');
}
}
@@ -165,9 +225,9 @@ function styleExcerpt(excerpt) {
// remove any strings that have colons between them
excerpt = excerpt.replace(/:[^:]*:/g, '');
// get text between "Description" and "Legal Agreement"
const match = excerpt.match(/Description([\s\S]*?)Legal Agreement/)
const match = excerpt.match(/Description([\s\S]*?)Legal Agreement/);
if (match) {
excerpt = match[1].trim()
excerpt = match[1].trim();
}
if (excerpt.length > 150) {
return excerpt.slice(0, 100) + '...';

View File

@@ -1,31 +1,35 @@
/* Getting Started Card container */
.featuredGridContainer {
display: flex;
/* place-content: center; */
grid-template-columns: 1fr;
flex-wrap: wrap;
min-height: 500px;
margin-left: 9%;
}
.multipleGridContainer {
display: flex;
/* place-content: center; */
flex-wrap: wrap;
gap: 23px;
width: 1500px;
}
.gridContainer {
display: flex;
/* place-content: center; */
flex-wrap: wrap;
gap: 40px;
margin-left: 40px;
margin-right: 40px;
gap: 23px;
width: 1500px;
}
.gridContainer::after {
content: "";
/* .gridContainer::after {
content: '';
flex: auto;
}
@media only screen and (max-width: 870px) {
.gridContainer {
place-content: center;
}
.gridContainer::after {
display: none;
}
}
} */
.center {
margin: 0px auto;
margin-bottom: 50px;
width: 1500px;
}
.space {
@@ -40,41 +44,18 @@
margin: 50px;
}
.modal {
position: absolute;
top: 50%;
left: 50%;
right: auto;
bottom: auto;
margin-right: -50%;
transform: translate(-50%, -50%);
box-shadow: 2px 3px 10px rgba(0, 0, 0, 0.25);
border-radius: 20px;
background-color: var(--dev-popup-background);
max-height: 80%;
max-width: 90%;
min-height: 80%;
scroll-behavior: auto;
overflow-y: auto;
}
.cardExit {
position: absolute;
top: 6px;
right: 6px;
fill: #96a9bb;
/* computed from codepen https://codepen.io/sosuke/pen/Pjoqqp */
filter: invert(79%) sepia(12%) saturate(484%) hue-rotate(168deg) brightness(84%) contrast(84%);
filter: invert(79%) sepia(12%) saturate(484%) hue-rotate(168deg)
brightness(84%) contrast(84%);
margin-left: 5px;
height: 24px;
width: 24px;
transition: all 0.2s;
transition: all 0.2s;
}
.cardExit:hover {
@@ -84,9 +65,128 @@
width: 23px;
}
.featuredSpinnerCenter {
width: 50%;
margin: auto;
margin-top: 45%;
}
.spinnerCenter {
margin: 100px auto;
max-width: 1300px;
margin-bottom: 50px;
}
.spinnerCenterMultiple {
position: absolute;
left: 50%;
}
.spinnerCenterSingle {
position: absolute;
top: 300px;
left: 50%;
}
@media only screen and (max-width: 1950px) {
.multipleGridContainer {
width: 1225px;
}
.gridContainer {
width: 1225px;
}
.center {
width: 1225px;
}
.multipleGridContainer > a:nth-child(5) {
display: none;
}
}
@media only screen and (max-width: 1350px) {
.multipleGridContainer {
width: 950px;
}
.multipleGridContainer > a:nth-child(4),
.multipleGridContainer > a:nth-child(5) {
display: none;
}
.gridContainer {
width: 950px;
}
.center {
width: 950px;
}
}
@media only screen and (max-width: 1050px) {
.multipleGridContainer {
width: 675px;
}
.multipleGridContainer > a:nth-child(3),
.multipleGridContainer > a:nth-child(4),
.multipleGridContainer > a:nth-child(5) {
display: none;
}
.multipleGridContainer {
place-content: center;
}
.gridContainer::after {
display: none;
}
.gridContainer {
width: 675px;
}
.center {
width: 675px;
}
}
@media only screen and (max-width: 700px) {
.multipleGridContainer {
width: 275px;
}
.multipleGridContainer > a:nth-child(2),
.multipleGridContainer > a:nth-child(3),
.multipleGridContainer > a:nth-child(4),
.multipleGridContainer > a:nth-child(5) {
display: none;
}
.multipleGridContainer {
place-content: center;
}
.gridContainer::after {
display: none;
}
.gridContainer {
width: 275px;
}
.center {
width: 275px;
}
}
@media only screen and (max-width: 550px) {
.featuredGridContainer {
min-height: 350px;
width: 275px;
margin: auto;
}
.featuredSpinnerCenter {
width: 20%;
margin: auto;
margin-top: 35%;
}
.spinnerCenterMultiple,
.spinnerCenterSingle {
left: 43%;
}
.noFound {
width: 350px;
position: absolute;
left: 10px;
}
}

View File

@@ -0,0 +1,23 @@
import React from 'react';
import Styles from './styles.module.css';
export default function NewtonsCradle() {
return (
<>
<div className={Styles.container}>
<div className={Styles.line}></div>
<div className={Styles.line}></div>
<div className={Styles.line}></div>
<div className={Styles.line}></div>
<div className={Styles.line}></div>
<div className={Styles.line}></div>
<div className={Styles.line}></div>
<div className={Styles.line}></div>
<div className={Styles.line}></div>
<div className={Styles.line}></div>
<div className={Styles.line}></div>
<div className={Styles.line}></div>
</div>
</>
);
}

View File

@@ -0,0 +1,124 @@
.container {
--uib-size: 100px;
--uib-color: var(--ifm-color-primary);
--uib-speed: 1s;
--uib-stroke: 3px;
position: relative;
display: flex;
align-items: center;
justify-content: flex-start;
height: var(--uib-size);
width: var(--uib-size);
}
.line {
position: absolute;
top: 0;
left: calc(50% - var(--uib-stroke) / 2);
display: flex;
align-items: flex-start;
height: 100%;
width: var(--uib-stroke);
}
.line::before {
content: '';
height: 22%;
width: 100%;
border-radius: calc(var(--uib-stroke) / 2);
background-color: var(--uib-color);
animation: pulse calc(var(--uib-speed)) ease-in-out infinite;
transition: background-color 0.3s ease;
transform-origin: center bottom;
}
.line:nth-child(1) {
transform: rotate(calc(360deg / -12 * 1));
&::before {
animation-delay: calc(var(--uib-speed) / -12 * 1);
}
}
.line:nth-child(2) {
transform: rotate(calc(360deg / -12 * 2));
&::before {
animation-delay: calc(var(--uib-speed) / -12 * 2);
}
}
.line:nth-child(3) {
transform: rotate(calc(360deg / -12 * 3));
&::before {
animation-delay: calc(var(--uib-speed) / -12 * 3);
}
}
.line:nth-child(4) {
transform: rotate(calc(360deg / -12 * 4));
&::before {
animation-delay: calc(var(--uib-speed) / -12 * 4);
}
}
.line:nth-child(5) {
transform: rotate(calc(360deg / -12 * 5));
&::before {
animation-delay: calc(var(--uib-speed) / -12 * 5);
}
}
.line:nth-child(6) {
transform: rotate(calc(360deg / -12 * 6));
&::before {
animation-delay: calc(var(--uib-speed) / -12 * 6);
}
}
.line:nth-child(7) {
transform: rotate(calc(360deg / -12 * 7));
&::before {
animation-delay: calc(var(--uib-speed) / -12 * 7);
}
}
.line:nth-child(8) {
transform: rotate(calc(360deg / -12 * 8));
&::before {
animation-delay: calc(var(--uib-speed) / -12 * 8);
}
}
.line:nth-child(9) {
transform: rotate(calc(360deg / -12 * 9));
&::before {
animation-delay: calc(var(--uib-speed) / -12 * 9);
}
}
.line:nth-child(10) {
transform: rotate(calc(360deg / -12 * 10));
&::before {
animation-delay: calc(var(--uib-speed) / -12 * 10);
}
}
.line:nth-child(11) {
transform: rotate(calc(360deg / -12 * 11));
&::before {
animation-delay: calc(var(--uib-speed) / -12 * 11);
}
}
@keyframes pulse {
0%,
80%,
100% {
transform: scaleY(0.75);
opacity: 0;
}
20% {
transform: scaleY(1);
opacity: 1;
}
}

View File

@@ -0,0 +1,60 @@
import React from 'react';
import styles from './styles.module.css';
import Link from '@docusaurus/Link';
import useBaseUrl from '@docusaurus/useBaseUrl';
export default function VideoCard({
featured,
videoURL,
thumbnail,
title,
body,
avatar,
username,
tags,
}) {
return (
<Link to={videoURL}>
<div
title={username}
className={featured ? styles.featuredCard : styles.card}>
<div className={styles.cardText}>
<div
className={
featured ? styles.featuredThumbContainer : styles.thumbContainer
}>
<img
className={featured ? styles.featuredCardImage : styles.cardImage}
src={thumbnail}></img>
</div>
<div
className={featured ? styles.featuredCardTitle : styles.cardTitle}>
<div className={styles.avatarContainer}>
<img
title={username}
className={styles.avatar}
src={useBaseUrl(avatar)}></img>
</div>
<div
className={
featured ? styles.featuredTitleContainer : styles.titleContainer
}>
{title}
</div>
</div>
<div className={styles.tags}>
{tags?.map((tag, index) => {
return (
<div key={tag} className={styles.tag}>
{tag}
</div>
);
})}
</div>
</div>
</div>
</Link>
);
}

View File

@@ -0,0 +1,165 @@
/* Getting Started Card */
.card {
position: relative;
margin-top: 30px;
opacity: 1;
transition: all 0.3s;
max-width: 300px;
}
.featuredCard {
position: relative;
margin-top: 30px;
opacity: 1;
transition: all 0.3s;
width: 400px;
height: 225px;
}
.card:hover .thumbContainer {
cursor: pointer;
transform: translate(0px, -5px);
box-shadow: var(--dev-card-selected);
}
.cardText {
display: flex;
flex-direction: column;
margin: 22px;
min-width: 170px;
margin-bottom: 75px;
}
.featuredThumbContainer {
border: 1px solid var(--dev-card-border);
--tw-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);
--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color),
0 1px 2px -1px var(--tw-shadow-color);
border-radius: 0.5rem;
box-shadow: 0 0 transparent, 0 0 transparent, 0 1px 3px 0 rgba(0, 0, 0, 0.1),
0 1px 2px -1px rgba(0, 0, 0, 0.1);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000),
var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
overflow: hidden;
position: relative;
height: 225px;
}
.thumbContainer {
border: 1px solid var(--dev-card-border);
--tw-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);
--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color),
0 1px 2px -1px var(--tw-shadow-color);
border-radius: 0.5rem;
box-shadow: 0 0 transparent, 0 0 transparent, 0 1px 3px 0 rgba(0, 0, 0, 0.1),
0 1px 2px -1px rgba(0, 0, 0, 0.1);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000),
var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
overflow: hidden;
position: relative;
height: 144px;
}
.featuredCardImage {
height: auto;
max-width: 100%;
width: 400px;
height: 225px;
border-radius: 0.5rem;
}
.cardImage {
height: auto;
max-width: 100%;
width: 256px;
height: 144px;
border-radius: 0.5rem;
}
.avatarContainer {
padding-left: 2rem;
position: relative;
width: 2.125rem;
}
.avatar {
--tw-border-opacity: 1;
--w: 2.125rem;
border-color: #f1f1e7;
border-color: rgb(241 241 231 / var(--tw-border-opacity));
border-radius: 9999px;
border-width: 2px;
left: 0;
max-width: 2.125rem;
max-width: var(--w);
min-width: 2.125rem;
min-width: var(--w);
position: absolute;
width: var(--w);
z-index: 10;
}
.featuredTitleContainer {
height: 50px;
}
.titleContainer {
height: 50px;
}
.featuredCardTitle {
font-size: 16px;
font-weight: 500;
display: flex;
gap: 1rem;
margin-top: 1rem;
padding-right: 0.5rem;
text-align: left;
}
.cardTitle {
font-size: 12px;
font-weight: 400;
display: flex;
gap: 1rem;
margin-top: 1rem;
padding-right: 0.5rem;
text-align: left;
}
.cardBody {
font-size: 16px;
font-weight: 500;
left: 0;
margin-top: 5px;
padding: 10px;
}
.tag {
font-size: 10px;
font-weight: 300;
color: var(--dev-secondary-text);
background-color: var(--dev-tag-highlight);
padding: 0px 8px;
margin-right: 5px;
margin-top: 5px;
}
.tags {
margin: 0px;
width: 100%;
display: flex;
flex-wrap: wrap;
margin-bottom: 10px;
margin-top: 5px;
}
@media only screen and (max-width: 550px) {
.cardText {
margin-bottom: 0px;
}
.tags {
margin-top: 2px;
}
}

View File

@@ -0,0 +1,50 @@
import React, {useEffect} from 'react';
import Video from '../../Video.js';
import styles from './styles.module.css';
import Layout from '@theme/Layout';
import DiscourseEmbed from '../VideoComments/index.js';
const VideoCardDetail = (props) => {
const base = 'https://play.vidyard.com/';
useEffect(() => {
// This code would be part of your iframe's JavaScript
window.addEventListener('message', (event) => {
// console.log(event);
if (event.origin === 'https://developer.identitysoon.com') {
// The data that was sent from the iframe
// console.log(event.data);
// setIframeStyle();
}
});
}, []);
return (
<Layout description="The SailPoint Developer Community has everything you need to build, extend, and automate scalable identity solutions.">
<main>
<div className={styles.videoPlayer}>
<Video
source={base + props.route.customProps.uuid}
container="vidyard"></Video>
<div>
<h1 className={styles.videoTitle}>
{props.route.customProps.title}
</h1>
</div>
<div>
<p>
{props.route.customProps.body}
</p>
</div>
<div id="discourseContainer" className={styles.discourseContainer}>
<DiscourseEmbed
discourseEmbedUrl={props.route.customProps.uuid}
topicId={props.route.customProps.topicid}></DiscourseEmbed>
</div>
</div>
</main>
</Layout>
);
};
export default VideoCardDetail;

View File

@@ -0,0 +1,11 @@
.videoPlayer {
width: 40%;
height: 40%;
align-content: center;
margin: auto;
}
.discourseContainer {
border-radius: 0.5em;
height: 400px;
}

View File

@@ -0,0 +1,158 @@
import React from 'react';
import styles from './styles.module.css';
import VideoCard from '../VideoCard';
import {
videoBaseURL,
discourseBaseURL,
developerWebsiteDomain,
} from '../../../util/util';
import NewtonsCradle from '../../newtonsCradle';
import {getVideoPosts} from '../../../services/DiscourseService';
export default function VideoCards({filterCallback, limit, featured}) {
const [cardData, setCardData] = React.useState();
const [loadingCards, setLoadingCards] = React.useState(true);
function buildTopicUrl(slug, id) {
return discourseBaseURL() + `t/${slug}/${id}`;
}
function parseVideoDetails(inputStr) {
// Split the string by the known separator for the description
const parts = inputStr.split('\n\nDescription\n');
const videoUrl = parts[0].trim(); // Get the video URL, trimming any whitespace(
const lastSegment = videoUrl.split('/').pop();
const page = lastSegment.replace('.html', '');
const description = parts.length > 1 ? parts[1].trim() : ''; // Get the description if it exists
return {
videoUrl,
description,
page,
};
}
function shortenTitle(title) {
if (title.length > 63) {
return title.substring(0, 62) + '...';
}
return title;
}
const getVideoTopics = async () => {
let tags = featured ? ['featured'] : filterCallback.tags;
const data = await getVideoPosts(tags ? tags : '');
const resultset = [];
if (data.topic_list) {
for (const topic of data.topic_list.topics) {
if (topic.tags.length > 0) {
let {videoUrl, description} = parseVideoDetails(topic.excerpt);
let thumbnail = videoUrl.replace('.html', '.jpg');
let avatar = '';
let username = '';
let ogPoster = '';
for (const poster of topic.posters) {
if (poster.description.includes('Original Poster')) {
ogPoster = poster;
break;
}
}
for (const user of data.users) {
if (user.id === ogPoster.user_id) {
username = user.name;
avatar = getavatarURL(user.avatar_template);
break;
}
}
if (!description) {
thumbnail = topic.image_url;
}
if (featured || (!featured && !topic.tags.includes('featured'))) {
resultset.push({
key: topic.id,
title: shortenTitle(topic.title),
tags: topic.tags,
body: description | topic.excerpt,
thumbnail: thumbnail,
avatar: avatar,
username: username,
url: buildTopicUrl(topic.slug, topic.id),
});
}
}
}
} else {
setCardData(undefined);
}
if (limit) {
setCardData(resultset.slice(0, limit));
} else {
setCardData(resultset);
}
setLoadingCards(false);
};
React.useEffect(() => {
getVideoTopics();
setCardData(undefined);
setLoadingCards(true);
}, [filterCallback]);
return (
<div className={featured ? null : styles.center}>
{loadingCards ? (
// Show loading icon when data is still loading
<div
className={
featured ? styles.featuredSpinnerCenter : styles.spinnerCenter
}>
<NewtonsCradle />
</div>
) : cardData && cardData.length > 0 ? (
<div
className={
featured ? styles.featuredGridContainer : styles.gridContainer
}>
{cardData.map(function (a, index) {
return (
<VideoCard
featured={featured}
key={a.key}
videoURL={a.url}
thumbnail={a.thumbnail}
title={a.title}
body={a.body}
avatar={a.avatar}
tags={a.tags}></VideoCard>
);
})}
</div>
) : (
<div>
<div className={styles.noFound}>
{' '}
Hey there, looks like no integrations match your search criteria.
Check out our{' '}
<a href="https://developer.sailpoint.com/discuss/t/about-the-sailpoint-developer-community-colab/11230">
getting started guide
</a>
, and consider being the first to contribute this integration!
</div>
</div>
)}
</div>
);
}
function getavatarURL(avatar) {
if (avatar.includes(developerWebsiteDomain())) {
return (
'https://' + developerWebsiteDomain() + avatar.replace('{size}', '120')
);
} else {
return avatar.replace('{size}', '120');
}
}

View File

@@ -0,0 +1,155 @@
/* Getting Started Card container */
.featuredGridContainer {
min-height: 300px;
}
.gridContainer {
display: flex;
/* place-content: center; */
flex-wrap: wrap;
gap: 5px;
margin-left: 20px;
justify-content: left;
width: 1525px;
}
/* .gridContainer::after {
content: '';
flex: auto;
}
.gridContainer::before {
content: '';
flex: auto;
} */
.center {
margin: 0px auto;
margin-bottom: 50px;
width: 50%;
width: 1525px;
}
.space {
height: 200px;
}
.noFound {
font-size: 28px;
font-weight: 500;
color: var(--dev-secondary-text);
padding: 8px;
margin: 50px;
}
.modal {
position: absolute;
top: 50%;
left: 50%;
right: auto;
bottom: auto;
margin-right: -50%;
transform: translate(-50%, -50%);
box-shadow: 2px 3px 10px rgba(0, 0, 0, 0.25);
border-radius: 20px;
background-color: var(--dev-popup-background);
max-height: 80%;
max-width: 90%;
min-height: 80%;
scroll-behavior: auto;
overflow-y: auto;
}
.cardExit {
position: absolute;
top: 6px;
right: 6px;
fill: #96a9bb;
/* computed from codepen https://codepen.io/sosuke/pen/Pjoqqp */
filter: invert(79%) sepia(12%) saturate(484%) hue-rotate(168deg)
brightness(84%) contrast(84%);
margin-left: 5px;
height: 24px;
width: 24px;
transition: all 0.2s;
}
.cardExit:hover {
top: 8px;
cursor: pointer;
height: 23px;
width: 23px;
}
.featuredSpinnerCenter {
width: 50%;
margin: auto;
margin-top: 25%;
}
.spinnerCenter {
position: absolute;
left: 50%;
top: 725px;
}
@media only screen and (max-width: 1950px) {
.gridContainer {
width: 1225px;
}
.center {
width: 1225px;
}
}
@media only screen and (max-width: 1350px) {
.gridContainer {
width: 925px;
}
.center {
width: 925px;
}
}
@media only screen and (max-width: 1050px) {
.gridContainer {
width: 650px;
}
.center {
width: 650px;
}
}
@media only screen and (max-width: 775px) {
.gridContainer {
width: 300px;
}
.center {
width: 300px;
}
}
@media only screen and (max-width: 958px) {
.spinnerCenter {
left: 47%;
top: 855px;
}
}
@media only screen and (max-width: 550px) {
.gridContainer {
margin-left: 0px;
}
.featuredSpinnerCenter {
width: 100%;
margin-top: 30%;
}
.spinnerCenter {
left: 40%;
top: 825px;
}
}

View File

@@ -0,0 +1,54 @@
import React, {useEffect} from 'react';
const DiscourseEmbed = ({discourseEmbedUrl, topicId}) => {
useEffect(() => {
const discourseUrl = 'https://developer.sailpoint.com/discuss/';
const embedUrl = `https://d1vrqvoe9hgpx0.cloudfront.net/videos/${discourseEmbedUrl}/index.html`;
// Set up Discourse Embed
window.DiscourseEmbed = {
discourseUrl,
topicId: topicId,
// discourseEmbedUrl: embedUrl,
className: 'EMBEDDED_BODY',
};
// Create and append meta tag for discourse username
const metaTag = document.createElement('meta');
metaTag.name = 'discourse-username';
metaTag.content = 'Darrell'; // Replace with your Discourse username
metaTag.setAttribute('discourse-embed-url', embedUrl); // Add attribute to meta tag for Discourse Embed URL
metaTag.setAttribute('discourse-embed-class-name', 'EMBEDDED_BODY'); // Add attribute to meta tag for Discourse Embed class name (optional)
metaTag.setAttribute('discourse-embed-title', 'SailPoint Developer Community'); // Add attribute to meta tag for Discourse Embed title (optional)
metaTag.setAttribute('discourse-embed-description', 'The SailPoint Developer Community has everything you need to build, extend, and automate scalable identity solutions.'); // Add attribute to meta tag for Discourse Embed description (optional)
metaTag.setAttribute('discourse-embed-color', '#00A2E8'); // Add attribute to meta tag for Discourse Embed color (optional)
document.getElementsByTagName('head')[0].appendChild(metaTag); // Append to head to avoid duplicating if component re-renders
// Create and append Discourse embed script
const scriptTag = document.createElement('script');
scriptTag.type = 'text/javascript';
scriptTag.async = true;
scriptTag.src = `${discourseUrl}javascripts/embed.js`;
document.body.appendChild(scriptTag); // Append to body to avoid duplicating if component re-renders
// Cleanup on component unmount
return () => {
scriptTag.remove(); // Remove script tag
if (metaTag.parentNode) {
metaTag.parentNode.removeChild(metaTag); // Remove meta tag if it has been appended
}
};
}, [discourseEmbedUrl]); // Effect dependency array includes discourseEmbedUrl
return (
<>
<meta name="discourse-username" content="Darrell-Thobe" />
<meta name="discourse-embed-url" content={discourseEmbedUrl} />
<div style={{display: 'flex', paddingBottom: '2%'}} id="discourse-comments"></div>
</>
); // Container for the Discourse comments
};
export default DiscourseEmbed;

View File

@@ -0,0 +1,27 @@
import React from 'react';
import styles from './styles.module.css';
export default function MarketplaceSidebarButton({
filterCallback,
text,
id,
isCategory,
category,
}) {
const [isActive, setIsActive] = React.useState(false);
const activeClass = isActive ? styles.tagSelected : '';
function setFilters(e, id) {
filterCallback({tag: id});
setIsActive((current) => !current);
}
return (
<div
key={id}
onClick={(e) => setFilters(e, id)}
className={activeClass + ' ' + styles.tag}>
{text}
</div>
);
}

View File

@@ -0,0 +1,35 @@
.tag {
font-size: 13px;
font-weight: 300;
color: var(--dev-secondary-text);
text-align: center;
margin-right: 0.5%;
margin-top: 0.5%;
padding: 1%;
border-style: solid;
border-width: 1px;
border-color: var(--dev-text-color-normal);
transition: background-color 500ms;
border-radius: 0.5rem;
}
.tagSelected {
font-size: 13px;
font-weight: 300;
background-color: var(--dev-secondary-text);
color: var(--dev-card-background);
text-align: center;
margin-right: 0.5%;
margin-top: 0.5%;
padding: 1%;
border-style: solid;
border-width: 1px;
border-color: var(--dev-text-color-normal);
transition: background-color 500ms;
}
.tag:hover {
cursor: pointer;
background-color: var(--dev-text-color-normal);
color: var(--dev-card-background);
}

View File

@@ -0,0 +1,182 @@
import React from 'react';
import styles from './styles.module.css';
import {getVideoPosts, getTags} from '../../../services/DiscourseService';
import {forEach} from 'lodash';
export default function MarketplaceSidebar({filterCallback}) {
const [tagProductData, setTagProductData] = React.useState();
const [videoTag, setVideoTag] = React.useState();
const [isOpen, setIsOpen] = React.useState(false);
const [productTags, setProductTags] = React.useState('Filter by Product');
const [videoTags, setVideoTags] = React.useState('Filter by Video Type');
const [checkedItemsProdcut, setCheckedItemsProduct] = React.useState({});
const [checkedItemsVideo, setCheckedItemsVideo] = React.useState(null);
const toggleDropdown = () => setIsOpen(!isOpen);
const handleCheckboxChangeProduct = (event) => {
setCheckedItemsProduct({
...checkedItemsProdcut,
[event.target.name]: event.target.checked,
});
let product = '';
if (event.target.checked) {
product = event.target.name;
} else {
console.log('productTags', productTags);
console.log('event.target.name', event.target.name);
product = productTags.replace(event.target.name, '');
console.log('product', product);
}
let filters = [];
if (event.target.checked) {
filters.push(event.target.name);
}
forEach(checkedItemsProdcut, (value, key) => {
if (key !== event.target.name && value === true) {
filters.push(key);
if (!product.includes(key)) product = product + ' ' + key;
}
});
if (checkedItemsVideo) {
filters.push(checkedItemsVideo);
}
if (event.target.checked && product !== '') {
setProductTags(product);
} else {
setProductTags(product);
}
if (product === '') {
setProductTags('Filter by Product');
}
filterCallback({tag: filters});
};
const handleCheckboxChangeVideo = (event) => {
const newCheckedItems = event.target.checked ? event.target.name : null;
setCheckedItemsVideo(newCheckedItems);
if (event.target.checked) {
setVideoTags(event.target.name);
} else {
setVideoTags('Filter by Video Type');
}
if (!checkedItemsProdcut) {
filterCallback({tag: newCheckedItems});
} else {
let filters = [];
if (newCheckedItems) filters.push(newCheckedItems);
forEach(checkedItemsProdcut, (value, key) => {
if (value === true) filters.push(key);
});
filterCallback({tag: filters});
}
};
const getTagData = async () => {
const uniqueProductTags = new Set();
const uniqueTags = new Set();
const data = await getTags();
if (data.extras.tag_groups) {
for (const tagGroup of data.extras.tag_groups) {
if (tagGroup.name === 'Products') {
for (const tag of tagGroup.tags) {
uniqueProductTags.add(tag.name);
}
}
if (tagGroup.name === 'Video Library') {
for (const tag of tagGroup.tags) {
uniqueTags.add(tag.name);
}
}
}
}
setTagProductData(Array.from(uniqueProductTags));
setVideoTag(Array.from(uniqueTags));
};
function displayText(text) {
if (text === 'identity-security-cloud') {
return 'Identity Security Cloud';
}
if (text === 'access-intelligence-center') {
return 'Access Intelligence Center';
}
if (text === 'developer-days-2023-iiq') {
return 'Developer Days 2023 iiq';
}
return text;
}
React.useEffect(() => {
getTagData();
}, []);
if (tagProductData) {
return (
<div className={styles.tagContainer}>
<div>
<div className={styles.dropdownContainer}>
<button onClick={toggleDropdown} className={styles.dropdownButton}>
{productTags}
</button>
{isOpen && (
<div className={styles.dropdownContent}>
{tagProductData.map(function (a, index) {
return (
<div className={styles.dropdownItem} key={index}>
<input
type="checkbox"
id={a}
name={a}
checked={checkedItemsProdcut[a] || false}
onChange={handleCheckboxChangeProduct}
/>
<label htmlFor={a}>{displayText(a)}</label>
</div>
);
})}
</div>
)}
</div>
</div>
<div className={styles.videoTypeFilter}>
<div className={styles.dropdownContainer}>
<button onClick={toggleDropdown} className={styles.dropdownButton}>
{videoTags}
</button>
{isOpen && (
<div className={styles.dropdownContent}>
{videoTag.map(function (a, index) {
return (
<div className={styles.dropdownItem} key={index}>
<input
type="checkbox"
id={a}
name={a}
checked={checkedItemsVideo === a}
onChange={handleCheckboxChangeVideo}
/>
<label htmlFor={a}>{displayText(a)}</label>
</div>
);
})}
</div>
)}
</div>
</div>
</div>
);
} else {
return <div></div>;
}
}

View File

@@ -0,0 +1,181 @@
.sidebar {
width: 400px;
height: 100%;
margin-left: 50px;
}
.filterBy {
margin-top: 1%;
}
.tagHeader {
margin-top: 30px;
font-size: 22px;
font-weight: 700;
}
.hidden {
display: none;
}
.seeAll {
margin-top: 5px;
margin-bottom: 10px;
color: var(--dev-secondary-text);
border-style: solid;
border-width: 1px;
width: 100%;
transition: background-color 500ms;
text-align: center;
}
.seeAll:hover {
cursor: pointer;
background-color: var(--dev-text-color-normal);
color: var(--dev-card-background);
}
.tagContainer {
margin: auto;
width: 59%;
display: flex;
}
.dropdownContainer {
position: relative;
display: inline-block;
}
.videoTypeFilter {
margin-left: 2%;
}
.dropdownButton {
background-color: var(
--dropdown-background
); /* Dropdown background color */ /* Text color */
padding: 5px 15px; /* Padding inside the dropdown button */
border: 0.5px solid #cccccc; /* Border around the dropdown */
border-radius: 5px; /* Rounded corners */
cursor: pointer; /* Change mouse cursor to indicate it's clickable */
position: relative; /* To position the arrow icon correctly */
display: inline-block; /* To keep the button's block behavior */
width: 200px;
height: 50px;
text-align: left;
}
.dropdownButton:after {
content: '▼'; /* Adds a dropdown arrow after the button text */
position: absolute;
right: 10px; /* Position the arrow to the right */
top: 50%; /* Align vertically */
transform: translateY(-50%); /* Center the arrow vertically */
pointer-events: none; /* Prevent the arrow from being clickable */
}
.dropdownButton:hover {
background-color: var(
--dev-card-background
); /* Lighter background on hover */
}
.dropdownButton:focus {
outline: none; /* Remove default focus outline */
border-color: #666666; /* Darker border color when focused */
}
.dropdownContent {
display: none;
position: absolute;
background-color: var(--dropdown-background);
min-width: 300px;
box-shadow: 0px 8px 16px 0px rgba(0, 0, 0, 0.2);
z-index: 25;
padding: 10px;
border-radius: 5px;
}
.dropdownContainer .dropdownContent label {
display: flex;
margin: 5px 5px;
}
.dropdownContainer:hover .dropdownContent {
display: block;
}
.dropdownItem {
display: flex;
}
.dropdownItem:hover {
--tw-shadow: 0 1px 3px 0 rgba(0, 0, 0, 0.1), 0 1px 2px -1px rgba(0, 0, 0, 0.1);
--tw-shadow-colored: 0 1px 3px 0 var(--tw-shadow-color),
0 1px 2px -1px var(--tw-shadow-color);
box-shadow: 0 0 transparent, 0 0 transparent, 0 1px 3px 0 rgba(0, 0, 0, 0.1),
0 1px 2px -1px rgba(0, 0, 0, 0.1);
box-shadow: var(--tw-ring-offset-shadow, 0 0 #0000),
var(--tw-ring-shadow, 0 0 #0000), var(--tw-shadow);
background-color: var(--dev-tag-highlight);
}
@media only screen and (max-width: 1920px) {
.tagContainer {
width: 61%;
}
}
@media only screen and (max-width: 1447px) {
.tagContainer {
width: 69%;
}
}
@media only screen and (max-width: 1278px) {
.tagContainer {
width: 68%;
}
}
@media only screen and (max-width: 1179px) {
.tagContainer {
margin-top: 10%;
width: 83%;
}
}
@media only screen and (max-width: 1085px) {
.tagContainer {
margin-top: 10%;
width: 100%;
}
}
@media only screen and (max-width: 958px) {
.tagContainer {
width: 72%;
}
}
@media only screen and (max-width: 550px) {
.tagContainer {
margin-top: 12%;
margin-bottom: 3%;
width: 100%;
justify-content: center;
align-items: center;
align-content: center;
}
}
@media only screen and (max-width: 450px) {
.tagContainer {
display: block;
width: 50%;
}
.videoTypeFilter {
margin-top: 5%;
margin-left: 0%;
}
}

View File

@@ -243,14 +243,20 @@
--dev-boarder-color-theme: black;
--dev-text-color-normal: #415364;
--dev-text-color-secondary: #0033a1;
--dev-icon-color-primary: invert(12%) sepia(90%) saturate(3876%) hue-rotate(221deg) brightness(89%) contrast(102%);
--dev-icon-color-primary: invert(12%) sepia(90%) saturate(3876%)
hue-rotate(221deg) brightness(89%) contrast(102%);
--dev-secondary-text: #415364;
--dev-tag-highlight: #eaeef1;
--text-on-primary: #ffffff;
--text-color: black;
--dropdown-background: #ffffff;
/*card css*/
--dev-card-border: #dadce0;
--dev-card-background: #e9e9e963;
--dev-tag-selected-text: #ffffffef;
--dev-popup-background: #fdfdfd;
@@ -259,6 +265,8 @@
--dev-card-selected: 0 4px 5px rgba(0, 0, 0, 0.2);
--dev-button-hover: #c552ae10;
--dev-new-car-background: #ffffff;
--ifm-github-logo: url("data:image/svg+xml;charset=utf-8,%3Csvg viewBox='0 0 24 24' xmlns='http://www.w3.org/2000/svg'%3E%3Cpath d='M12 .297c-6.63 0-12 5.373-12 12 0 5.303 3.438 9.8 8.205 11.385.6.113.82-.258.82-.577 0-.285-.01-1.04-.015-2.04-3.338.724-4.042-1.61-4.042-1.61C4.422 18.07 3.633 17.7 3.633 17.7c-1.087-.744.084-.729.084-.729 1.205.084 1.838 1.236 1.838 1.236 1.07 1.835 2.809 1.305 3.495.998.108-.776.417-1.305.76-1.605-2.665-.3-5.466-1.332-5.466-5.93 0-1.31.465-2.38 1.235-3.22-.135-.303-.54-1.523.105-3.176 0 0 1.005-.322 3.3 1.23.96-.267 1.98-.399 3-.405 1.02.006 2.04.138 3 .405 2.28-1.552 3.285-1.23 3.285-1.23.645 1.653.24 2.873.12 3.176.765.84 1.23 1.91 1.23 3.22 0 4.61-2.805 5.625-5.475 5.92.42.36.81 1.096.81 2.22 0 1.606-.015 2.896-.015 3.286 0 .315.21.69.825.57C20.565 22.092 24 17.592 24 12.297c0-6.627-5.373-12-12-12'/%3E%3C/svg%3E");
--ifm-medium-logo: url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" width="24px" height="24px" viewBox="0 0 1770 1000"><circle cx="500" cy="500" r="500"/><ellipse ry="475" rx="250" cy="501" cx="1296"/><ellipse cx="1682" cy="502" rx="88" ry="424"/></svg>');
--dev-sailpoint-small-logo: url('../../static/img/SailPoint-Logo-RGB-Color.png');
@@ -286,11 +294,18 @@
--dev-sailpoint-small-logo: url('../../static/img/SailPoint-Logo-RGB-Inverse.png');
--dev-text-color-normal: #dae1e9;
--dev-text-color-secondary: #54c0e8;
--dev-icon-color-primary: invert(87%) sepia(26%) saturate(6104%) hue-rotate(165deg) brightness(96%) contrast(89%);
--dev-icon-color-primary: invert(87%) sepia(26%) saturate(6104%)
hue-rotate(165deg) brightness(96%) contrast(89%);
--text-on-primary: #ffffff;
--text-color: #ffffff;
--dropdown-background: #2a2b2d;
/*card css*/
--dev-card-border: rgba(218,220,224,0.3);
--dev-new-car-background: #2a2b2d;
--dev-card-background: #2a2b2d;
--dev-popup-background: #2a2b2d;
--dev-card-shadow: 0 0px 0px rgba(0, 0, 0, 0.2);
@@ -460,14 +475,24 @@ ul {
}
div[class^='announcementBar_'] {
background: repeating-linear-gradient(145deg,rgba(0,51,161,1),rgba(0,79,181,1),rgba(0,113,206,1));
background: repeating-linear-gradient(
145deg,
rgba(0, 51, 161, 1),
rgba(0, 79, 181, 1),
rgba(0, 113, 206, 1)
);
color: #ffffff;
height: 35px;
}
@media screen and (max-width: 750px) {
div[class^='announcementBar_'] {
background: repeating-linear-gradient(145deg,rgba(0,51,161,1),rgba(0,79,181,1),rgba(0,113,206,1));
background: repeating-linear-gradient(
145deg,
rgba(0, 51, 161, 1),
rgba(0, 79, 181, 1),
rgba(0, 113, 206, 1)
);
color: #ffffff;
height: 52.5px;
}
@@ -488,6 +513,25 @@ code[class^='openapi-explorer__code-block'] {
max-height: 400px !important;
}
iframe#webpack-dev-server-client-overlay{
display:none!important
iframe#webpack-dev-server-client-overlay {
display: none !important;
}
iframe[id^='discourse-embed-frame'] {
border-radius: 0.5em !important;
border: solid;
border-width: thin;
margin: auto;
padding-bottom: -15%;
align-items: center;
}
div[id^='discourseContainer'] {
border-radius: 0.5em;
}
div[id^='discourse-comments'] {
display: flex;
padding-bottom: 2%;
}

View File

@@ -8,33 +8,70 @@ import BlogBanner from '../components/blog/BlogBanner';
import styles from './blog.module.css';
import BlogCards from '../components/blog/BlogCards';
import BlogSidebar from '../components/blog/BlogSidebar';
import useBaseUrl from '@docusaurus/useBaseUrl';
export default function Blog() {
const [filteredProduct, setFilteredProduct] = React.useState([]);
const {siteConfig} = useDocusaurusContext();
const handleClick = (data) => {
var tempFilter = filteredProduct.slice()
var tempFilter = [];
const index = tempFilter.indexOf(data);
if (index !== -1) {
tempFilter.splice(index, 1);
} else {
tempFilter.push(data)
tempFilter.push(data);
}
setFilteredProduct(tempFilter)
setFilteredProduct(tempFilter);
};
return (
<Layout description="The SailPoint Developer Community has everything you need to build, extend, and automate scalable identity solutions.">
<main>
<BlogBanner />
<div className={styles.blogContainer}>
<div className={styles.blogSidbarContainer}><BlogSidebar filterCallback={handleClick}/></div>
<div className={styles.blogCardContainer}><BlogCards filterCallback={filteredProduct}/></div>
</div>
<main className={styles.main}>
<BlogBanner />
<div>
<div className={styles.mainCard}>
<div className={styles.contentContainer}>
<div className={styles.gettingStartedText}>
<div className={styles.gettingStartedOne}>Community Blog</div>
<div className={styles.gettingStartedThree}>
<span>
Our community blog is a collection of technical writings
provided by members of our communityyour peersdiscussing
detailed walkthroughs, challenges faced (and how they were
overcome), and thoughts on managing identity in a meaningful
way.
</span>{' '}
<br />
<br />
<a href="https://developer.sailpoint.com/discuss/t/guide-for-writing-blog-posts/10277">
Become an author &#8594;
</a>
</div>
</div>
</div>
<div className={styles.featuredBlogContainer} title="Featured Blog">
<div className={styles.featuredGettingStartedText}>
<div className={styles.gettingStartedOne}>Featured</div>
</div>
<BlogCards
filterCallback={filteredProduct}
limit={1}
featured={true}
/>
</div>
</div>
</div>
<div className={styles.blogContainer}>
<div className={styles.blogSidbarContainer}>
<BlogSidebar filterCallback={handleClick} />
</div>
<div className={styles.blogCardContainer}>
<BlogCards filterCallback={filteredProduct} />
</div>
</div>
</main>
</Layout>
);

View File

@@ -1,17 +1,141 @@
.blogContainer {
display: flex;
min-height: 600px;
}
.mainCard {
display: grid;
grid-gap: 20px;
max-width: 1180px;
justify-content: center;
min-height: 587.22px;
}
.featuredGettingStartedText {
display: none;
}
.gettingStartedText,
.featuredGettingStartedText {
margin-top: 50px;
margin-left: 50px;
}
.gettingStartedOne {
color: var(--ifm-color-primary);
font-size: 30px;
max-width: 396px;
font-weight: bold;
line-height: 100%;
}
.gettingStartedTwo {
margin-top: 20px;
font-size: 20px;
font-weight: bold;
}
.gettingStartedThree {
margin-top: 20px;
font-size: 16px;
font-weight: 500;
}
.bold {
font-weight: bold;
}
/* Getting Started Card container */
.gridContainer {
display: grid;
margin-left: 50px;
margin-bottom: 50px;
grid-gap: 20px;
grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
place-content: center;
margin-right: 40px;
}
.video {
width: 100%;
height: 300px;
max-width: 520px;
}
.blogSidbarContainer {
flex: 5%;
margin: auto;
width: 400px;
}
@media only screen and (max-width: 870px) {
.blogSidbarContainer {
display: none;
}
.featuredBlogContainer {
margin-left: 8%;
}
.blogCardContainer {
flex: 95%;
}
.contentContainer {
margin-left: 5%;
}
@media only screen and (max-width: 1178px) {
.carousel {
margin-top: 0px;
margin-left: 50px;
margin-bottom: 50px;
}
.contentContainer {
width: 600px;
margin-left: 10%;
}
.featuredGettingStartedText {
display: flex;
}
}
@media only screen and (min-width: 1179px) {
.carousel {
margin-top: 35px;
padding-bottom: 5%;
}
.mainCard {
grid-template-columns: repeat(auto-fit, minmax(520px, 1fr));
/* UI Properties */
background: var(--main-hero-card-background);
box-shadow: var(--dev-main-card-shadow);
border: 1px solid var(--dev-card-background);
border-radius: 40px;
opacity: 1;
margin: 50px auto;
width: calc(100% - 100px);
height: 545px;
min-height: 535px;
}
}
@media only screen and (max-width: 570px) {
.gettingStartedThree {
padding-right: 15%;
}
.contentContainer {
margin-left: 0%;
width: auto;
}
.featuredBlogContainer {
margin-left: 0%;
}
.video {
display: none;
}
.carousel {
margin-bottom: 0px;
}
}
@media screen and (max-width: 500px) {
.blogSidbarContainer {
width: 325px;
}
}

View File

@@ -1,48 +1,173 @@
import React from 'react';
import clsx from 'clsx';
import Link from '@docusaurus/Link';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import Layout from '@theme/Layout';
import MarketplaceBanner from '../components/marketplace/MarketplaceBanner';
import Link from '@docusaurus/Link';
import styles from './exchange.module.css';
import MarketplaceCards from '../components/marketplace/MarketplaceCards';
import MarketplaceSidebar from '../components/marketplace/MarketplaceSidebar';
import HomepageCard from '../components/homepage/HomepageCard';
export default function Marketplace() {
const [filteredProduct, setFilteredProduct] = React.useState({"category": "colab", "tags": []});
const [plugins] = React.useState({
category: 'colab-iiq-plugins',
tags: [],
});
const {siteConfig} = useDocusaurusContext();
const [rules] = React.useState({
category: 'colab-rules',
tags: ['identity-security-cloud'],
});
const handleClick = (data) => {
var tempFilter = filteredProduct.tags.slice()
const [saas] = React.useState({
category: 'colab-saas-connectors',
tags: [],
});
if (data.tag) {
const index = tempFilter.indexOf(data.tag);
if (index !== -1) {
tempFilter.splice(index, 1);
} else {
tempFilter.push(data.tag)
}
}
const [transforms] = React.useState({
category: 'colab-transforms',
tags: [],
});
if (data.category) {
setFilteredProduct({"category": data.category, "tags": tempFilter})
} else {
setFilteredProduct({"category": filteredProduct.category, "tags": tempFilter})
}
const [workflows] = React.useState({
category: 'colab',
tags: ['workflows'],
});
const [communityTools] = React.useState({
category: 'colab-community-tools',
tags: [],
});
};
return (
<Layout description="The SailPoint Developer Community has everything you need to build, extend, and automate scalable identity solutions.">
<main>
<MarketplaceBanner />
<div className={styles.blogContainer}>
<div className={styles.blogSidbarContainer}><MarketplaceSidebar selectedCategory={filteredProduct.category} filterCallback={handleClick}/></div>
<div className={styles.blogCardContainer}><MarketplaceCards filterCallback={filteredProduct}/></div>
</div>
<MarketplaceBanner />
<div>
<div className={styles.mainCard}>
<div className={styles.contentContainer}>
<div className={styles.gettingStartedText}>
<div className={styles.gettingStartedOne}>
CoLab Marketplace
</div>
<div className={styles.gettingStartedTwo}>
What is the CoLab?
</div>
<div className={styles.gettingStartedThree}>
<span>
The community CoLab is a place where members of our
community can build fully-featured solutions on our platform
and share those solutions with each other. Users collaborate
on these solutions to benefit both themselves and the
community at large.
</span>{' '}
<br />
<br />
<a href="https://developer.sailpoint.com/discuss/t/developer-community-colab-getting-started-guide/11230">
Get started with the CoLab &#8594;
</a>
</div>
</div>
</div>
<div className={styles.featuredGettingStartedText}>
<div className={styles.gettingStartedOne}>Featured</div>
</div>
<div className={styles.carousel}>
<div className={styles.featured} title="Featured CoLab">
<MarketplaceCards
filterCallback={['']}
limit={1}
featured={true}
/>
</div>
</div>
</div>
</div>
<div className={styles.container}>
<div>
<div className={styles.cardContainer}>
<div className={styles.titleContainer}>
<h1 className={styles.title}>Workflows</h1>
<Link to={'/colab/workflows'} className={styles.link}>
View All &#8594;
</Link>
</div>
<MarketplaceCards
filterCallback={workflows}
limit={5}
multiple={true}
/>
</div>
<div className={styles.cardContainer}>
<div className={styles.titleContainer}>
<h1 className={styles.title}>SaaS Connectors</h1>
<Link to={'/colab/saasconnectors'} className={styles.link}>
View All &#8594;
</Link>
</div>
<MarketplaceCards
filterCallback={saas}
limit={5}
multiple={true}
/>
</div>
<div className={styles.cardContainer}>
<div className={styles.titleContainer}>
<h1 className={styles.title}>Community Tools</h1>
<Link to={'/colab/communitytools'} className={styles.link}>
View All &#8594;
</Link>
</div>
<MarketplaceCards
filterCallback={communityTools}
limit={5}
multiple={true}
/>
</div>
<div className={styles.cardContainer}>
<div className={styles.titleContainer}>
<h1 className={styles.title}>Rules</h1>
<Link to={'/colab/rules'} className={styles.link}>
View All &#8594;
</Link>
</div>
<MarketplaceCards
filterCallback={rules}
limit={5}
multiple={true}
/>
</div>
<div className={styles.cardContainer}>
<div className={styles.titleContainer}>
<h1 className={styles.title}>Transforms</h1>
<Link to={'/colab/transforms'} className={styles.link}>
View All &#8594;
</Link>
</div>
<MarketplaceCards
filterCallback={transforms}
limit={5}
multiple={true}
/>
</div>
<div className={styles.cardContainer}>
<div>
<h1 className={styles.title}>IIQ Plugins</h1>
<Link to={'/colab/plugins'} className={styles.link}>
View All &#8594;
</Link>
</div>
<MarketplaceCards
filterCallback={plugins}
limit={5}
multiple={true}
/>
</div>
</div>
</div>
</main>
</Layout>
);

View File

@@ -0,0 +1,44 @@
import React from 'react';
import Layout from '@theme/Layout';
import styles from './filter.module.css';
import BlogSidebar from '../../components/blog/BlogSidebar';
import MarketplaceCards from '../../components/marketplace/MarketplaceCards';
import MarketplaceBanner from '../../components/marketplace/MarketplaceBanner';
export default function CommunityTools() {
const [filteredProduct, setFilteredProduct] = React.useState({
category: 'colab-community-tools',
tags: ['identity-security-cloud'],
});
const handleClick = (data) => {
var tempFilter = [];
const index = tempFilter.indexOf(data);
if (index !== -1) {
tempFilter.splice(index, 1);
} else {
tempFilter.push(data);
}
setFilteredProduct({
category: 'colab-community-tools',
tags: tempFilter,
});
};
return (
<Layout description="The SailPoint Developer Community has everything you need to build, extend, and automate scalable identity solutions.">
<main>
<MarketplaceBanner />
<div className={styles.container}>
<div className={styles.filterContainer}>
<BlogSidebar filterCallback={handleClick} isChecked={true} />
</div>
<div className={styles.cardContainer}>
<MarketplaceCards filterCallback={filteredProduct} />
</div>
</div>
</main>
</Layout>
);
}

View File

@@ -0,0 +1,20 @@
.colabContainer {
min-height: 600px;
}
.filterContainer {
margin: auto;
width: 400px;
margin-top: 3%;
}
@media screen and (max-width: 550px) {
.filterContainer {
width: 325px;
}
}
.cardContainer {
width: 63%;
margin: auto;
}

View File

@@ -0,0 +1,25 @@
import React from 'react';
import Layout from '@theme/Layout';
import styles from './filter.module.css';
import MarketplaceCards from '../../components/marketplace/MarketplaceCards';
import MarketplaceBanner from '../../components/marketplace/MarketplaceBanner';
export default function Workflows() {
const [filteredProduct] = React.useState({
category: 'colab-iiq-plugins',
tags: ['Identityiq'],
});
return (
<Layout description="The SailPoint Developer Community has everything you need to build, extend, and automate scalable identity solutions.">
<main>
<MarketplaceBanner />
<div className={styles.container}>
<div className={styles.cardContainer}>
<MarketplaceCards filterCallback={filteredProduct} />
</div>
</div>
</main>
</Layout>
);
}

46
src/pages/colab/rules.js Normal file
View File

@@ -0,0 +1,46 @@
import React from 'react';
import Layout from '@theme/Layout';
import styles from './filter.module.css';
import BlogSidebar from '../../components/blog/BlogSidebar';
import MarketplaceCards from '../../components/marketplace/MarketplaceCards';
import MarketplaceBanner from '../../components/marketplace/MarketplaceBanner';
export default function Rules() {
const [filteredProduct, setFilteredProduct] = React.useState({
category: 'colab-rules',
tags: ['identity-security-cloud'],
});
const handleClick = (data) => {
var tempFilter = [];
const index = tempFilter.indexOf(data);
if (index !== -1) {
tempFilter.splice(index, 1);
} else {
tempFilter.push(data);
}
setFilteredProduct({
category: 'colab-rules',
tags: tempFilter,
});
};
return (
<Layout description="The SailPoint Developer Community has everything you need to build, extend, and automate scalable identity solutions.">
<main>
<MarketplaceBanner />
<div className={styles.container}>
<div className={styles.filterContainer}>
<BlogSidebar filterCallback={handleClick} isChecked={true} />
</div>
<div className={styles.cardContainer}>
<MarketplaceCards filterCallback={filteredProduct} />
</div>
</div>
</main>
</Layout>
);
}

View File

@@ -0,0 +1,46 @@
import React from 'react';
import Layout from '@theme/Layout';
import styles from './filter.module.css';
import BlogSidebar from '../../components/blog/BlogSidebar';
import MarketplaceCards from '../../components/marketplace/MarketplaceCards';
import MarketplaceBanner from '../../components/marketplace/MarketplaceBanner';
export default function SaasConnector() {
const [filteredProduct, setFilteredProduct] = React.useState({
category: 'colab-saas-connectors',
tags: [],
});
const handleClick = (data) => {
var tempFilter = [];
const index = tempFilter.indexOf(data);
if (index !== -1) {
tempFilter.splice(index, 1);
} else {
tempFilter.push(data);
}
setFilteredProduct({
category: 'colab-saas-connectors',
tags: tempFilter,
});
};
return (
<Layout description="The SailPoint Developer Community has everything you need to build, extend, and automate scalable identity solutions.">
<main>
<MarketplaceBanner />
<div className={styles.container}>
<div className={styles.filterContainer}>
<BlogSidebar filterCallback={handleClick} isChecked={true} />
</div>
<div className={styles.cardContainer}>
<MarketplaceCards filterCallback={filteredProduct} />
</div>
</div>
</main>
</Layout>
);
}

View File

@@ -0,0 +1,46 @@
import React from 'react';
import Layout from '@theme/Layout';
import styles from './filter.module.css';
import BlogSidebar from '../../components/blog/BlogSidebar';
import MarketplaceCards from '../../components/marketplace/MarketplaceCards';
import MarketplaceBanner from '../../components/marketplace/MarketplaceBanner';
export default function Transforms() {
const [filteredProduct, setFilteredProduct] = React.useState({
category: 'colab-transforms',
tags: ['identity-security-cloud'],
});
const handleClick = (data) => {
var tempFilter = [];
const index = tempFilter.indexOf(data);
if (index !== -1) {
tempFilter.splice(index, 1);
} else {
tempFilter.push(data);
}
setFilteredProduct({
category: 'colab-transforms',
tags: tempFilter,
});
};
return (
<Layout description="The SailPoint Developer Community has everything you need to build, extend, and automate scalable identity solutions.">
<main>
<MarketplaceBanner />
<div className={styles.container}>
<div className={styles.filterContainer}>
<BlogSidebar filterCallback={handleClick} isChecked={true} />
</div>
<div className={styles.cardContainer}>
<MarketplaceCards filterCallback={filteredProduct} />
</div>
</div>
</main>
</Layout>
);
}

View File

@@ -0,0 +1,46 @@
import React from 'react';
import Layout from '@theme/Layout';
import styles from './filter.module.css';
import BlogSidebar from '../../components/blog/BlogSidebar';
import MarketplaceCards from '../../components/marketplace/MarketplaceCards';
import MarketplaceBanner from '../../components/marketplace/MarketplaceBanner';
export default function Workflows() {
const [filteredProduct, setFilteredProduct] = React.useState({
category: 'colab',
tags: ['workflows'],
});
const handleClick = (data) => {
var tempFilter = [];
const index = tempFilter.indexOf(data);
if (index !== -1) {
tempFilter.splice(index, 1);
} else {
tempFilter.push(data);
}
setFilteredProduct({
category: 'colab-workflows',
tags: tempFilter,
});
};
return (
<Layout description="The SailPoint Developer Community has everything you need to build, extend, and automate scalable identity solutions.">
<main>
<MarketplaceBanner />
<div className={styles.container}>
<div className={styles.filterContainer}>
<BlogSidebar filterCallback={handleClick} />
</div>
<div className={styles.cardContainer}>
<MarketplaceCards filterCallback={filteredProduct} />
</div>
</div>
</main>
</Layout>
);
}

View File

@@ -1,17 +1,176 @@
.blogContainer {
.cardContainer {
min-height: 400px;
width: 1500px;
}
.container {
display: flex;
margin: auto;
width: 1500px;
}
.featured {
margin-left: 8%;
}
.discourseContainer {
border-radius: 0.5em;
height: 100%;
}
.title {
margin-top: 1%;
width: 300px;
}
.link {
margin-top: 3%;
margin-left: 1%;
}
.mainCard {
display: grid;
grid-gap: 20px;
max-width: 1180px;
justify-content: center;
min-height: 535px;
}
.featuredGettingStartedText {
display: none;
}
.gettingStartedText,
.featuredGettingStartedText {
margin-top: 50px;
margin-left: 50px;
}
.gettingStartedOne {
color: var(--ifm-color-primary);
font-size: 30px;
max-width: 396px;
font-weight: bold;
line-height: 100%;
}
.gettingStartedTwo {
margin-top: 20px;
font-size: 20px;
font-weight: bold;
}
.gettingStartedThree {
margin-top: 20px;
font-size: 16px;
font-weight: 500;
}
.bold {
font-weight: bold;
}
/* Getting Started Card container */
.gridContainer {
display: grid;
margin-left: 50px;
margin-bottom: 50px;
grid-gap: 20px;
grid-template-columns: repeat(auto-fit, minmax(190px, 1fr));
place-content: center;
margin-right: 40px;
}
.video {
width: 100%;
height: 300px;
max-width: 520px;
}
.contentContainer {
margin-left: 5%;
}
@media only screen and (max-width: 1950px) {
.container {
width: 1225px;
}
.cardContainer {
width: 1225px;
}
}
@media only screen and (max-width: 1350px) {
.container {
width: 950px;
}
.cardContainer {
width: 950px;
}
}
@media only screen and (max-width: 1050px) {
.container {
width: 675px;
}
.cardContainer {
width: 675px;
}
}
@media only screen and (max-width: 700px) {
.container {
width: 275px;
}
.cardContainer {
width: 275px;
}
}
@media only screen and (max-width: 1178px) {
.carousel {
margin-top: 0px;
margin-left: 50px;
margin-bottom: 50px;
}
.contentContainer {
width: 600px;
margin-left: 10%;
}
}
@media only screen and (min-width: 1179px) {
.mainCard {
grid-template-columns: repeat(auto-fit, minmax(520px, 1fr));
/* UI Properties */
background: var(--main-hero-card-background);
box-shadow: var(--dev-main-card-shadow);
border: 1px solid var(--dev-card-background);
border-radius: 40px;
opacity: 1;
margin: 50px auto;
width: calc(100% - 100px);
height: 535px;
min-height: 535px;
}
}
@media only screen and (max-width: 570px) {
.featured {
margin-left: 0%;
}
.gettingStartedThree {
padding-right: 15%;
}
.featuredGettingStartedText {
display: flex;
}
.contentContainer {
width: auto;
margin-left: 0%;
}
.carousel {
margin: auto;
}
}
.blogSidbarContainer {
flex: 5%;
}
@media only screen and (max-width: 870px) {
.blogSidbarContainer {
display: none;
}
}
.blogCardContainer {
flex: 95%;
}

View File

@@ -4,6 +4,7 @@ import Link from '@docusaurus/Link';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import Layout from '@theme/Layout';
import HomepageGettingStarted from '@site/src/components/homepage/HomepageGettingStarted';
import HomepageDeveloperDays from '@site/src/components/homepage/HomepageDeveloperDays';
import HomepageBasics from '../components/homepage/HomepageBasics';
import HomepageTrainingGuides from '../components/homepage/HomepageTrainingGuides';
import HomepageDiscuss from '../components/homepage/HomepageDiscuss';
@@ -16,14 +17,15 @@ export default function Home() {
return (
<Layout description="The SailPoint Developer Community has everything you need to build, extend, and automate scalable identity solutions.">
<main>
<HomepageGettingStarted />
{/* <HomepageGettingStarted /> */}
<HomepageDeveloperDays />
<HomepageBasics
description={
"If you still aren't sure where to get started, try one of the <b>Getting Started Guides</b> below, or see what our platform has to offer."
}
link={'https://www.sailpoint.com/products/IdentityNow/'}
title={'Start With the Basics'}
image={'/homepage/person-head.png'}
image={'user'}
buttonText={'Explore our platform'}
/>
<HomepageTrainingGuides />
@@ -33,19 +35,22 @@ export default function Home() {
}
link={'https://developer.sailpoint.com/discuss/'}
title={'What is the Community saying?'}
image={'/homepage/discuss.png'}
image={'discuss'}
buttonText={'Join the Discussion'}
/>
<HomepageDiscuss />
<HomepageBasics
description={
"The Developer Relations team is responsible for creating a better developer experience on our platform. Click on someone to reach out to them, or <a href='https://developer.sailpoint.com/discuss/new-message?groupname=developer_relations/'>contact our team directly</a>."
}
title={'Meet Our Team'}
image={'/homepage/team.png'}
buttonText={''}
/>
<HomepageTeam />
<div>
<HomepageBasics
description={
"The Developer Relations team is responsible for creating a better developer experience on our platform. Click on someone to reach out to them, or <a href='https://developer.sailpoint.com/discuss/new-message?groupname=developer_relations/'>contact our team directly</a>."
}
title={'Meet Our Team'}
image={'team'}
buttonText={''}
/>
<HomepageTeam />
</div>
</main>
</Layout>
);

View File

@@ -21,3 +21,5 @@
align-items: center;
justify-content: center;
}

70
src/pages/videos.js Normal file
View File

@@ -0,0 +1,70 @@
import React from 'react';
import Layout from '@theme/Layout';
import useDocusaurusContext from '@docusaurus/useDocusaurusContext';
import VideoCards from '../components/video-library/VideoCards';
import VideoSidebar from '../components/video-library/VideoSidebar';
import DiscourseEmbed from '../components/video-library/VideoComments';
import styles from './videos.module.css';
export default function VideoLibrary() {
const [filteredProduct, setFilteredProduct] = React.useState({
tags: [],
});
const {siteConfig} = useDocusaurusContext();
const handleClick = (data) => {
setFilteredProduct({tags: data.tag});
};
return (
<Layout description="The SailPoint Developer Community has everything you need to build, extend, and automate scalable identity solutions.">
<main>
<div>
<div className={styles.titleContainer}></div>
<div>
<div className={styles.mainCard}>
<div className={styles.contentContainer}>
<div className={styles.gettingStartedText}>
<div className={styles.gettingStartedOne}>Video Library</div>
<div className={styles.gettingStartedThree}>
<span>
Our video library is a collection of educational videos
from our Developer Relations team, live streams,
conferences, and other community members.
</span>{' '}
<br />
<br />
<a href="https://developer.sailpoint.com/discuss/new-message?groupname=developer_relations&title=Proposal%20for%20Developer%20Community%20Video&body=Write%20your%20request%20here.">
Contribute to our library &#8594;
</a>
</div>
</div>
<div className={styles.featuredGettingStartedText}>
<div className={styles.gettingStartedOne}>Featured</div>
</div>
</div>
<div className={styles.featuredVideo} title="Featured Video">
<VideoCards
filterCallback={filteredProduct}
limit={1}
featured={true}
/>
</div>
</div>
<div className={styles.videosCardContainer}>
<div className={styles.videoSideBar}>
<VideoSidebar
selectedCategory={filteredProduct}
filterCallback={handleClick}
/>
</div>
<VideoCards filterCallback={filteredProduct} />
</div>
</div>
</div>
</main>
</Layout>
);
}

147
src/pages/videos.module.css Normal file
View File

@@ -0,0 +1,147 @@
.videosCardContainer {
flex: 95%;
min-height: 600px;
}
.featuredVideo {
margin-left: 75px;
}
.videosTitle {
text-align: center;
margin-bottom: 2%;
color: #ffffff;
position: relative;
top: 25px;
}
.titleContainer {
width: 100%;
height: 45px;
background: rgb(0, 51, 161);
background: linear-gradient(
90deg,
rgba(0, 51, 161, 1) 0%,
rgba(84, 192, 232, 1) 100%
);
align-content: center;
margin-bottom: 2%;
}
.mainCard {
display: grid;
grid-gap: 20px;
max-width: 1180px;
justify-content: center;
min-height: 400px;
}
.featuredGettingStartedText {
display: none;
}
.gettingStartedText,
.featuredGettingStartedText {
margin-top: 50px;
margin-left: 50px;
}
.gettingStartedOne {
color: var(--ifm-color-primary);
font-size: 30px;
max-width: 396px;
font-weight: bold;
line-height: 100%;
}
.gettingStartedTwo {
margin-top: 20px;
font-size: 20px;
font-weight: bold;
}
.gettingStartedThree {
margin-top: 20px;
font-size: 16px;
font-weight: 500;
}
.bold {
font-weight: bold;
}
.video {
width: 100%;
height: 300px;
max-width: 520px;
}
.contentContainer {
margin-left: 75px;
}
@media only screen and (max-width: 1178px) {
.carousel {
margin-top: 0px;
margin-left: 50px;
margin-bottom: 50px;
}
.contentContainer {
width: 600px;
margin-left: 75px;
}
.featuredVideo {
margin-left: 175px;
}
.videoSideBar {
margin-left: 5%;
}
.featuredGettingStartedText {
display: flex;
}
}
@media only screen and (min-width: 1179px) {
.carousel {
margin-top: 35px;
padding-bottom: 5%;
}
.mainCard {
grid-template-columns: repeat(auto-fit, minmax(520px, 1fr));
/* UI Properties */
background: var(--main-hero-card-background);
box-shadow: var(--dev-main-card-shadow);
border: 1px solid var(--dev-card-background);
border-radius: 40px;
opacity: 1;
margin: 50px auto;
width: calc(100% - 100px);
}
}
@media only screen and (max-width: 900px) {
.videoSideBar {
margin-left: 0%;
}
.featuredVideo {
margin: auto;
}
.carousel {
margin-bottom: 1%;
}
.gettingStartedThree {
padding-right: 15%;
}
}
@media only screen and (max-width: 570px) {
.contentContainer {
width: auto;
margin: auto;
}
.carousel {
margin-bottom: 0px;
}
}

View File

@@ -57,9 +57,52 @@ export async function checkImage(url) {
export async function getBlogPosts(tags) {
let url = '';
if (tags) {
url = discourseBaseURL() + 'c/blog/l/latest.json?tags=' + tags;
url =
discourseBaseURL() +
'c/content/community-blog/l/latest.json?tags=' +
tags;
} else {
url = discourseBaseURL() + 'c/blog/l/latest.json';
url = discourseBaseURL() + 'c/content/community-blog/l/latest.json';
}
try {
const response = await fetch(url);
return await response.json();
} catch (error) {
return [];
}
}
export async function getUserTitle(primary_group_name) {
let url = discourseBaseURL() + 'g/' + primary_group_name + '.json';
try {
const response = await fetch(url);
return await response.json();
} catch (error) {
console.log(error);
return [];
}
}
export async function getVideoPosts(tags) {
let url = '';
if (tags) {
if (tags.length < 1) {
url = discourseBaseURL() + 'c/content/video-library/l/latest.json';
}
if (tags.length === 1) {
url =
discourseBaseURL() +
'c/content/video-library/l/latest.json?tags=' +
tags;
}
if (tags.length === 2) {
url =
discourseBaseURL() +
`filter.json?q=category%3Avideo-library%20tag%3A${tags[0]}%2B${tags[1]}`;
}
} else {
url = discourseBaseURL() + 'c/content/video-library/l/latest.json';
}
try {
const response = await fetch(url);

View File

@@ -9,8 +9,7 @@ import isInternalUrl from '@docusaurus/isInternalUrl';
import {translate} from '@docusaurus/Translate';
import styles from './styles.module.css';
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
import { faFolderOpen, faLink, faBook, faArrowUpRightFromSquare } from '@fortawesome/pro-duotone-svg-icons'
import { useColorMode } from '@docusaurus/theme-common';
import { faFolderOpen, faBook, faArrowUpRightFromSquare } from '@fortawesome/pro-duotone-svg-icons'
function CardContainer({href, children}) {
return (
@@ -39,10 +38,6 @@ function CardLayout({href, icon, title, description}) {
}
function CardCategory({item}) {
const href = findFirstCategoryLink(item);
const {colorMode} = useColorMode();
//const icon = colorMode === 'dark' ? <FontAwesomeIcon icon={faFolderOpen} style={{marginRight: "0.5em"}} color='#0033a1' /> : <FontAwesomeIcon icon={faFolderOpen} style={{marginRight: "0.5em"}} color='#7ecfee' />;
//const icon = <FontAwesomeIcon icon={faFolderOpen} style={{marginRight: "0.5em"}} color='#0033a1' />;
//console.log(item)
// Unexpected: categories that don't have a link have been filtered upfront
if (!href) {
return null;
@@ -50,7 +45,7 @@ function CardCategory({item}) {
return (
<CardLayout
href={href}
icon={<FontAwesomeIcon icon={faFolderOpen} style={{marginRight: "0.5em"}} color={colorMode === 'dark' ? '#7ecfee' : '#0033a1'} />}
icon={<FontAwesomeIcon icon={faFolderOpen} className={styles.docCardIcon} />}
//icon={icon}
title={item.label}
description={
@@ -69,8 +64,7 @@ function CardCategory({item}) {
);
}
function CardLink({item}) {
const {colorMode} = useColorMode();
const icon = isInternalUrl(item.href) ? <FontAwesomeIcon icon={faBook} style={{marginRight: "0.5em"}} color={colorMode === 'dark' ? '#7ecfee' : '#0033a1'} /> : <FontAwesomeIcon icon={faArrowUpRightFromSquare} style={{marginRight: "0.5em"}} color={colorMode === 'dark' ? '#7ecfee' : '#0033a1'} />;
const icon = isInternalUrl(item.href) ? <FontAwesomeIcon icon={faBook} className={styles.docCardIcon} /> : <FontAwesomeIcon icon={faArrowUpRightFromSquare} className={styles.docCardIcon} />;
const doc = useDocById(item.docId ?? undefined);
return (
<CardLayout

View File

@@ -26,3 +26,8 @@
.cardDescription {
font-size: 0.9rem;
}
.docCardIcon {
margin-right: .5em;
color: var(--ifm-color-primary);
}

View File

@@ -2,23 +2,34 @@ export function addDarkToFileName(filename) {
const parts = filename.split('.');
return parts[0] + '-dark.' + parts[1];
}
export function discourseBaseURL() {return 'https://developer.sailpoint.com/discuss/'}
export function developerWebsiteDomain() {return 'developer.sailpoint.com'}
export function videoThumbnailBaseURL() {
return 'https://play.vidyard.com/';
}
export function videoBaseURL() {
return '/videos/';
}
export function discourseBaseURL() {
return 'https://developer.sailpoint.com/discuss/';
}
export function developerWebsiteDomain() {
return 'developer.sailpoint.com';
}
export function discourseMarketplaceCatagoryId() {
if (discourseBaseURL().includes('soon')) {
return 57
return 57;
} else {
return 59
return 59;
}
}
export function discourseProductTag() {
if (discourseBaseURL().includes('soon')) {
return 11
return 11;
} else {
return 45
}
}
export function CMSBaseURL() { return process.env.CMS_APP_API_ENDPOINT}
export function CMSBaseURL() {
return process.env.CMS_APP_API_ENDPOINT;
}

View File

@@ -42,11 +42,15 @@ patch:
- Accounts
summary: Update Account
description: >-
Use this endpoint to update an account with a PATCH request.
This updates account details.
A token with ORG_ADMIN, SOURCE_ADMIN, or SOURCE_SUBADMIN authority is required to call this API.
The request must provide a JSONPatch payload.
A token with ORG_ADMIN authority is required to call this API.
This endpoint supports updating an account's correlation. It can only modify the identityId and manuallyCorrelated
attributes. To re-assign an account from one identity to another, replace the current identityId with a new value.
If the account you're assigning was provisioned by IdentityNow, it's possible IdentityNow could create a new account
for the previous identity as soon as the account is moved. If the account you're assigning is authoritative,
this will cause the previous identity to become uncorrelated and could even result in its deletion. All accounts
that are are reassigned will be set to manuallyCorrelated: true.
security:
- UserContextAuth: [idn:accounts:manage]
parameters:
@@ -180,4 +184,4 @@ delete:
"429":
$ref: "../../v3/responses/429.yaml"
"500":
$ref: "../../v3/responses/500.yaml"
$ref: "../../v3/responses/500.yaml"

View File

@@ -6,16 +6,18 @@ post:
description: >-
This API submits a task to unlock an account and returns the task ID.
To use this endpoint to unlock an account that has the `forceProvisioning` option set to true, the `idn:accounts-provisioning:manage` scope is required.
A token with ORG_ADMIN authority is required to call this API.
security:
- UserContextAuth: [idn:accounts-state:manage]
- UserContextAuth: [idn:accounts-state:manage, idn:accounts-provisioning:manage]
parameters:
- in: path
name: id
schema:
type: string
required: true
description: The account id
description: The account ID.
example: ef38f94347e94562b5bb8424a56397d8
requestBody:
required: true

View File

@@ -26,38 +26,6 @@ get:
$ref: '../../v3/responses/429.yaml'
'500':
$ref: '../../v3/responses/500.yaml'
post:
operationId: createProfileConfig
tags:
- Auth Profile
summary: Create Auth Profile.
description: >-
This API creates an auth profile.
security:
- UserContextAuth: [sp:auth-profile:create]
requestBody:
required: true
content:
application/json:
schema:
$ref: "../schemas/AuthProfileRequest.yaml"
responses:
'202':
description: Auth Profile details
content:
application/json:
schema:
$ref: '../schemas/AuthProfile.yaml'
'400':
$ref: '../../v3/responses/400.yaml'
'401':
$ref: '../../v3/responses/401.yaml'
'403':
$ref: '../../v3/responses/403.yaml'
'429':
$ref: '../../v3/responses/429.yaml'
'500':
$ref: '../../v3/responses/500.yaml'
patch:
operationId: patchProfileConfig
tags:
@@ -70,7 +38,7 @@ patch:
parameters:
- name: id
in: path
description: ID of the Auth Profile to patch
description: ID of the Auth Profile to patch.
required: true
schema:
type: string
@@ -85,7 +53,7 @@ patch:
required: true
responses:
'200':
description: Responds with the Access Profile as updated.
description: Responds with the Auth Profile as updated.
content:
application/json:
schema:
@@ -102,33 +70,3 @@ patch:
$ref: '../../v3/responses/500.yaml'
security:
- UserContextAuth: [sp:auth-profile:update]
delete:
operationId: deleteProfileConfig
tags:
- Auth Profile
summary: Delete the specified Auth Profile
description: >-
This API deletes an existing Auth Profile.
parameters:
- name: id
in: path
description: ID of the Access Profile to delete
required: true
schema:
type: string
example: 2c91808a7813090a017814121919ecca
responses:
'204':
$ref: "../../v3/responses/204.yaml"
'400':
$ref: '../../v3/responses/400.yaml'
'401':
$ref: '../../v3/responses/401.yaml'
'403':
$ref: '../../v3/responses/403.yaml'
'429':
$ref: '../../v3/responses/429.yaml'
'500':
$ref: '../../v3/responses/500.yaml'
security:
- UserContextAuth: [sp:auth-profile:delete]

View File

@@ -0,0 +1,61 @@
get:
operationId: getDiscoveredApplications
tags:
- Discovered Applications
summary: Retrieve discovered applications for tenant
description: >
Fetches a list of applications that have been identified within the environment. This includes details such as application names, discovery dates, potential correlated saas_vendors and related suggested connectors.
security:
- UserContextAuth:
- 'idn:application-discovery:read'
parameters:
- $ref: '../../v3/parameters/limit.yaml'
- $ref: '../../v3/parameters/offset.yaml'
- in: query
name: filter
schema:
type: string
description: >
Filter results using the standard syntax described in [V3 API Standard Collection Parameters](https://developer.sailpoint.com/idn/api/standard-collection-parameters#filtering-results)
Filtering is supported for the following fields and operators:
**name**: *eq, sw, co*
**description**: *eq, sw, co*
example: name eq "Okta" and description co "Okta"
required: false
style: form
- in: query
name: sorters
schema:
type: string
format: comma-separated
description: >-
Sort results using the standard syntax described in [V3 API Standard Collection Parameters](https://developer.sailpoint.com/idn/api/standard-collection-parameters#sorting-results)
Sorting is supported for the following fields: **name, description, discoveredAt, discoverySource**
example: name
responses:
'200':
description: Successfully retrieved list of discovered applications.
content:
application/json:
schema:
type: array
items:
$ref: '../../beta/schemas/DiscoveredApplications.yaml'
'400':
$ref: '../../v3/responses/400.yaml'
'401':
$ref: '../../v3/responses/401.yaml'
'403':
$ref: '../../v3/responses/403.yaml'
'429':
$ref: '../../v3/responses/429.yaml'
'500':
$ref: '../../v3/responses/500.yaml'

View File

@@ -0,0 +1,103 @@
put:
operationId: setIcon
tags:
- Icons
summary: Update an icon
description: >-
This API endpoint updates an icon by object type and object id.
A token with ORG_ADMIN authority is required to call this API.
parameters:
- in: path
name: objectType
schema:
type: string
required: true
description: Object type. Available options ['application']
example: application
- in: path
name: objectId
schema:
type: string
required: true
description: Object id.
example: a291e870-48c3-4953-b656-fb5ce2a93169
requestBody:
required: true
content:
multipart/form-data:
schema:
type: object
required:
- image
properties:
image:
type: string
format: binary
description: file with icon. Allowed mime-types ['image/png', 'image/jpeg']
example: \x00\x00\x00\x02
security:
- UserContextAuth: [ ]
responses:
'200':
description: Icon updated
content:
application/json:
schema:
type: object
properties:
icon:
type: string
description: url to file with icon
example: ""
'400':
$ref: '../../v3/responses/400.yaml'
'401':
$ref: '../../v3/responses/401.yaml'
'403':
$ref: '../../v3/responses/403.yaml'
'404':
$ref: '../../v3/responses/404.yaml'
'429':
$ref: '../../v3/responses/429.yaml'
'500':
$ref: '../../v3/responses/500.yaml'
delete:
operationId: deleteIcon
tags:
- Icons
summary: Delete an icon
description: >-
This API endpoint delete an icon by object type and object id.
A token with ORG_ADMIN authority is required to call this API.
parameters:
- in: path
name: objectType
schema:
type: string
required: true
description: Object type. Available options ['application']
example: application
- in: path
name: objectId
schema:
type: string
required: true
description: Object id.
example: a291e870-48c3-4953-b656-fb5ce2a93169
security:
- UserContextAuth: [ ]
responses:
'204':
$ref: '../../v3/responses/204.yaml'
'400':
$ref: '../../v3/responses/400.yaml'
'401':
$ref: '../../v3/responses/401.yaml'
'403':
$ref: '../../v3/responses/403.yaml'
'404':
$ref: '../../v3/responses/404.yaml'
'429':
$ref: '../../v3/responses/429.yaml'
'500':
$ref: '../../v3/responses/500.yaml'

View File

@@ -4,16 +4,19 @@ post:
- Identities
summary: Process a list of identityIds
description: |
You could use this endpoint to:
This operation should not be used to schedule your own identity processing or to perform system wide identity refreshes. The system will use a combination of [event-based processing](https://documentation.sailpoint.com/saas/help/setup/identity_processing.html?h=process#event-based-processing) and [scheduled processing](https://documentation.sailpoint.com/saas/help/setup/identity_processing.html?h=process#scheduled-processing) that runs every day at 8:00 AM and 8:00 PM in the tenant's timezone to keep your identities synchronized.
This endpoint will perform the following tasks:
1. Calculate identity attributes, including applying or running any rules or transforms (e.g. calculate Lifecycle State at a point-in-time it's expected to change).
2. Evaluate role assignments, leading to assignment of new roles and removal of existing roles.
3. Enforce provisioning for any assigned accesses that haven't been fulfilled (e.g. failure due to source health).
4. Recalculate manager relationships.
5. Potentially clean-up identity processing errors, assuming the error has been resolved.
To learn more, refer to the [identity processing documentation](https://documentation.sailpoint.com/saas/help/setup/identity_processing.html).
A token with ORG_ADMIN or HELPDESK authority is required to call this API.
externalDocs:
description: 'Learn more about manually processing identities here'
url: 'https://documentation.sailpoint.com/saas/help/setup/identity_processing.html'
security:
- UserContextAuth:
- "idn:identity:manage"

View File

@@ -5,6 +5,10 @@ get:
summary: List Identities
description: >-
This API returns a list of identities.
security:
- UserContextAuth:
- "idn:identity:read"
- "idn:identity:manage"
parameters:
- in: query
name: filters

View File

@@ -6,8 +6,21 @@ post:
description: >-
Process identities under the profile
This operation should not be used to schedule your own identity processing or to perform system wide identity refreshes. The system will use a combination of [event-based processing](https://documentation.sailpoint.com/saas/help/setup/identity_processing.html?h=process#event-based-processing) and [scheduled processing](https://documentation.sailpoint.com/saas/help/setup/identity_processing.html?h=process#scheduled-processing) that runs every day at 8:00 AM and 8:00 PM in the tenant's timezone to keep your identities synchronized.
This should only be run on identity profiles that have the `identityRefreshRequired` attribute set to `true`. If `identityRefreshRequired` is false, then there is no benefit to running this operation. Typically, this operation is performed when a change is made to the identity profile or its related lifecycle states that requires a refresh.
This operation will perform the following activities on all identities under the identity profile.
1. Updates identity attribute according to the identity profile mappings.
2. Determines the identity's correct manager through manager correlation.
3. Updates the identity's access according to their assigned lifecycle state.
4. Updates the identity's access based on role assignment criteria.
A token with ORG_ADMIN authority is required to call this API.
externalDocs:
description: 'Learn more about manually processing identities here'
url: 'https://documentation.sailpoint.com/saas/help/setup/identity_processing.html'
parameters:
- in: path
name: identity-profile-id

View File

@@ -0,0 +1,34 @@
post:
operationId: resetIdentity
tags:
- Identities
summary: Reset an identity
description: >-
Use this endpoint to reset a user's identity if they have forgotten their authentication information like their answers to knowledge-based questions.
Resetting an identity de-registers the user and removes any elevated user levels they have.
security:
- UserContextAuth:
- "idn:identity:update"
parameters:
- in: path
name: identityId
schema:
type: string
required: true
description: Identity Id
example: "ef38f94347e94562b5bb8424a56397d8"
responses:
"202":
description: Accepted. The reset request accepted and is in progress.
"400":
$ref: "../../v3/responses/400.yaml"
"401":
$ref: "../../v3/responses/401.yaml"
"403":
$ref: "../../v3/responses/403.yaml"
"404":
$ref: "../../v3/responses/404.yaml"
"429":
$ref: "../../v3/responses/429.yaml"
"500":
$ref: "../../v3/responses/500.yaml"

View File

@@ -4,14 +4,14 @@ post:
- Lifecycle States
summary: Set Lifecycle State
description: |
This endpoint will set/update an identity's lifecycle state to the one provided and updates the corresponding Identity Profile.
This endpoint will set/update an identity's lifecycle state to the one provided and updates the corresponding identity profile.
A token with ORG_ADMIN or API authority is required to call this API.
parameters:
- in: path
name: identity-id
description: >-
The ID of the identity to update
The ID of the identity to update.
required: true
example: 2c9180857893f1290178944561990364
schema:
@@ -27,7 +27,7 @@ post:
lifecycleStateId:
type: string
format: uuid
description: The ID of the lifecycle state to set
description: The ID of the lifecycle state to set.
example: 2c9180877a86e408017a8c19fefe046c
responses:
'200':
@@ -41,7 +41,7 @@ post:
type: string
format: uuid
example: 2c9180837ab5b716017ab7c6c9ef1e20
description: The ID of the IdentityRequest object that was generated when the workflow launches
description: The ID of the IdentityRequest object that is generated when the workflow launches. To follow the IdentityRequest, you can provide this ID with a [Get Account Activity request](https://developer.sailpoint.com/docs/api/beta/get-account-activity/). The response will contain relevant information about the IdentityRequest, such as its status.
'401':
$ref: '../../v3/responses/401.yaml'
'403':

View File

@@ -8,6 +8,7 @@ get:
security:
- UserContextAuth:
- "idn:identity:read"
- "idn:identity:manage"
parameters:
- in: path
name: id

View File

@@ -1,11 +1,14 @@
post:
tags:
- Entitlements
summary: Import Entitlement CSV File
operationId: importEntitlementCsv
summary: Aggregate Entitlements
operationId: importEntitlements
description: >-
Uploads a comma separated file (CSV) to a delimited file source and starts an entitlement aggregation on the source.
Starts an entitlement aggregation on the specified source.
If the target source is a direct connection, then the request body must be empty. You will also need to make sure the Content-Type header is not set. If you set the Content-Type header without specifying a body, then you will receive a 500 error.
If the target source is a delimited file source, then the CSV file needs to be included in the request body. You will also need to set the Content-Type header to `multipart/form-data`.
parameters:
- in: path
name: id
@@ -27,7 +30,7 @@ post:
- csvFile
responses:
"202":
description: Load Entitlements Task
description: Aggregate Entitlements Task
content:
application/json:
schema:

View File

@@ -3,6 +3,7 @@ get:
- Managed Clients
summary: Specified Managed Client Status.
description: Retrieve Managed Client Status by ID.
deprecated: true
operationId: getManagedClientStatus
parameters:
- name: id
@@ -45,6 +46,7 @@ post:
- Managed Clients
summary: Handle status request from client
description: Update a status detail passed in from the client
deprecated: true
operationId: updateManagedClientStatus
parameters:
- name: id

View File

@@ -3,6 +3,7 @@ get:
- Managed Clusters
summary: Get managed cluster's log configuration
description: Get managed cluster's log configuration.
deprecated: true
operationId: getClientLogConfiguration
parameters:
- name: id
@@ -40,6 +41,7 @@ put:
- Managed Clusters
summary: Update managed cluster's log configuration
description: Update managed cluster's log configuration
deprecated: true
operationId: putClientLogConfiguration
parameters:
- name: id

View File

@@ -3,6 +3,7 @@ get:
- Managed Clusters
summary: Get a specified ManagedCluster.
description: Retrieve a ManagedCluster by ID.
deprecated: true
operationId: getManagedCluster
parameters:
- name: id

View File

@@ -3,6 +3,7 @@ get:
- Managed Clusters
summary: Retrieve all Managed Clusters.
description: Retrieve all Managed Clusters for the current Org, based on request context.
deprecated: true
operationId: getManagedClusters
parameters:
- $ref: '../../v3/parameters/offset.yaml'

View File

@@ -0,0 +1,28 @@
get:
summary: CSV template download for discovery
tags:
- Manual Discover Applications Template
description: >
Allows the user to download an example CSV file with two columns `application_name` and `domain`.
The CSV file contains a single row with the values 'Example Application' and 'Example Description'.
security:
- UserContextAuth:
- 'idn:application-discovery:read'
operationId: getManualDiscoverApplicationsCsvTemplate
responses:
'200':
description: A CSV file download was successful.
content:
text/csv:
schema:
$ref: '../schemas/ManualDiscoverApplicationsTemplate.yaml'
'400':
$ref: '../../v3/responses/400.yaml'
'401':
$ref: '../../v3/responses/401.yaml'
'403':
$ref: '../../v3/responses/403.yaml'
'429':
$ref: '../../v3/responses/429.yaml'
'500':
$ref: '../../v3/responses/500.yaml'

View File

@@ -0,0 +1,43 @@
post:
summary: CSV Upload to discover applications
tags:
- Manual Discover Applications
description: >-
This API allows for the upload of a CSV file containing application data to
be manually correlated to potential IDN connector(s).
security:
- UserContextAuth:
- 'idn:application-discovery:write'
operationId: sendManualDiscoverApplicationsCsvTemplate
requestBody:
content:
multipart/form-data:
schema:
type: object
properties:
csvFile:
type: string
format: binary
required:
- csvFile
responses:
'200':
description: The CSV has been successfully processed.
content:
multipart/form-data:
schema:
$ref: '../schemas/ManualDiscoverApplications.yaml'
'400':
$ref: '../../v3/responses/400.yaml'
description: >
Bad request - There was an error with the CSV format or validation
failed (e.g., `application_name` missing). Error message should be
provided in response.
'401':
$ref: '../../v3/responses/401.yaml'
'403':
$ref: '../../v3/responses/403.yaml'
'429':
$ref: '../../v3/responses/429.yaml'
'500':
$ref: '../../v3/responses/500.yaml'

Some files were not shown because too many files have changed in this diff Show More