Skip to content

Commit

Permalink
feat(Projects): advanced source material license info
Browse files Browse the repository at this point in the history
  • Loading branch information
jakeaturner committed Aug 12, 2024
1 parent e1f8c5a commit bfc759f
Show file tree
Hide file tree
Showing 15 changed files with 184 additions and 93 deletions.
6 changes: 3 additions & 3 deletions client/src/components/FilesManager/EditFile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ import {
AssetTagFramework,
Author,
CentralIdentityLicense,
License,
ProjectFile,
} from "../../types";
import CtlTextInput from "../ControlledInputs/CtlTextInput";
Expand Down Expand Up @@ -44,7 +45,6 @@ import AuthorsForm from "./AuthorsForm";
import FilePreview from "./FilePreview";
import ManageCaptionsModal from "./ManageCaptionsModal";
import { useQuery } from "@tanstack/react-query";
import { ProjectFileLicense } from "../../types/Project";
const FilesUploader = React.lazy(() => import("./FilesUploader"));
const FileRenderer = React.lazy(() => import("./FileRenderer"));

Expand Down Expand Up @@ -143,7 +143,7 @@ const EditFile: React.FC<EditFileProps> = ({
const {
data: projectLicenseSettings,
isFetching: projectLicenseSettingsLoading,
} = useQuery<ProjectFileLicense | null>({
} = useQuery<License | null>({
queryKey: ["projectLicenseSettings", projectID],
queryFn: loadProjectLicenseSettings,
staleTime: Infinity,
Expand Down Expand Up @@ -265,7 +265,7 @@ const EditFile: React.FC<EditFileProps> = ({
res.data.project.defaultFileLicense &&
typeof res.data.project.defaultFileLicense === "object"
) {
return res.data.project.defaultFileLicense as ProjectFileLicense;
return res.data.project.defaultFileLicense as License
}
return null;
} catch (err) {
Expand Down
118 changes: 89 additions & 29 deletions client/src/components/projects/ProjectPropertiesModal.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -91,8 +91,14 @@ const ProjectPropertiesModal: React.FC<ProjectPropertiesModalProps> = ({
adaptURL: "",
author: "",
authorEmail: "",
license: "",
resourceURL: "",
license: {
name: "",
version: "",
url: "",
sourceURL: "",
modifiedFromSource: false,
additionalTerms: "",
},
notes: "",
associatedOrgs: [],
thumbnail: "",
Expand Down Expand Up @@ -204,7 +210,15 @@ const ProjectPropertiesModal: React.FC<ProjectPropertiesModalProps> = ({
}, [watch("defaultFileLicense.name")]);

// Return new license version options when license name changes
const selectedLicenseVersions = useCallback(() => {
const selectedSourceLicenseVersions = useCallback(() => {
const license = licenseOptions.find(
(l) => l.name === getValues("license.name")
);
if (!license) return [];
return license.versions ?? [];
}, [watch("license.name"), licenseOptions]);

const selectedFileLicenseVersions = useCallback(() => {
const license = licenseOptions.find(
(l) => l.name === getValues("defaultFileLicense.name")
);
Expand Down Expand Up @@ -1095,7 +1109,7 @@ const ProjectPropertiesModal: React.FC<ProjectPropertiesModalProps> = ({
or tool.
</em>
</p>
<div className="flex flex-row justify-between">
<div className="flex flex-row justify-between mb-3">
<CtlTextInput
name="author"
control={control}
Expand All @@ -1111,43 +1125,89 @@ const ProjectPropertiesModal: React.FC<ProjectPropertiesModalProps> = ({
className="basis-1/2"
/>
</div>
<div className="flex flex-row justify-between mt-4">
<div className="w-full mr-6">
<label htmlFor="license" className="form-field-label">
License
<div>
<label className="form-field-label" htmlFor="selectSourceLicenseName">
Name
</label>
<Controller
render={({ field }) => (
<Dropdown
id="selectSourceLicenseName"
options={licenseOptions.map((l) => ({
key: crypto.randomUUID(),
value: l.name,
text: l.name,
}))}
{...field}
onChange={(e, data) => {
field.onChange(data.value?.toString() ?? "");
}}
fluid
selection
placeholder="Select a license..."
/>
)}
name="license.name"
control={control}
/>
</div>
{selectedSourceLicenseVersions().length > 0 && (
<div className="mt-2">
<label
className="form-field-label form-required"
htmlFor="selectSourceLicenseVersion"
>
Version
</label>
<Controller
name="license"
control={control}
render={({ field }) => (
<Dropdown
id="license"
options={licenseOptions.map((l) => ({
id="selectSourceLicenseVersion"
options={selectedSourceLicenseVersions().map((v) => ({
key: crypto.randomUUID(),
value: l.name,
text: l.name,
value: v,
text: v,
}))}
{...field}
onChange={(e, data) => {
field.onChange(data.value?.toString() ?? "text");
field.onChange(data.value?.toString() ?? "");
}}
fluid
selection
className="mr-8"
placeholder="License..."
placeholder="Select license version"
/>
)}
name="license.version"
control={control}
rules={required}
/>
</div>
<CtlTextInput
name="resourceURL"
label="Original URL"
)}
<CtlTextInput
name="license.sourceURL"
control={control}
label="Original URL"
placeholder="https://example.com"
className="mt-2"
/>
<div className="flex items-start mt-3">
<CtlCheckbox
name="license.modifiedFromSource"
control={control}
placeholder="Enter resource URL..."
type="url"
className="w-full"
label="Modified from source?"
className="ml-2"
labelDirection="row-reverse"
/>
</div>
<CtlTextArea
name="license.additionalTerms"
control={control}
label="Additional License Terms"
placeholder="Additional terms (if applicable)..."
className="mt-2"
maxLength={DESCRIP_MAX_CHARS}
showRemaining
/>
<Divider />
<Header as="h3">Asset Settings</Header>
<p className="!my-1">
Expand All @@ -1161,13 +1221,13 @@ const ProjectPropertiesModal: React.FC<ProjectPropertiesModalProps> = ({
</em>
</p>
<div>
<label className="form-field-label" htmlFor="selectLicenseName">
<label className="form-field-label" htmlFor="selectFileLicenseName">
Name
</label>
<Controller
render={({ field }) => (
<Dropdown
id="selectLicenseName"
id="selectFileLicenseName"
options={licenseOptions.map((l) => ({
key: crypto.randomUUID(),
value: l.name,
Expand All @@ -1186,19 +1246,19 @@ const ProjectPropertiesModal: React.FC<ProjectPropertiesModalProps> = ({
control={control}
/>
</div>
{selectedLicenseVersions().length > 0 && (
{selectedFileLicenseVersions().length > 0 && (
<div className="mt-2">
<label
className="form-field-label form-required"
htmlFor="selectLicenseVersion"
htmlFor="selectFileLicenseVersion"
>
Version
</label>
<Controller
render={({ field }) => (
<Dropdown
id="selectLicenseVersion"
options={selectedLicenseVersions().map((v) => ({
id="selectFileLicenseVersion"
options={selectedFileLicenseVersions().map((v) => ({
key: crypto.randomUUID(),
value: v,
text: v,
Expand Down
14 changes: 5 additions & 9 deletions client/src/components/projects/ProjectView.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -71,10 +71,6 @@ import {
checkProjectMemberPermission,
PROJECT_ROLE_SORT_ORDER
} from '../util/ProjectHelpers';
import {
licenseOptions,
getLicenseText
} from '../util/LicenseOptions.js';
import {
getRoadmapStepName
} from '../util/RoadmapOptions.jsx';
Expand Down Expand Up @@ -1419,7 +1415,7 @@ const ProjectView = (props) => {
}

// Rendering Helper Booleans
const hasResourceInfo = project.author || project.license || project.resourceURL;
const hasResourceInfo = project.author || project.license?.sourceURL || project.license?.licenseName;
const hasNotes = project.notes && !isEmptyString(project.notes);
const hasFlag = project.flag && !isEmptyString(project.flag);
const flagCrumbEnabled = hasFlag && showReviewerCrumb;
Expand Down Expand Up @@ -1844,15 +1840,15 @@ const ProjectView = (props) => {
<a href={`mailto:${project.authorEmail}`} target='_blank' rel='noopener noreferrer'>{project.authorEmail}</a>
</div>
}
{(project.license && !isEmptyString(project.license)) &&
{project.license && project.license.name &&
<div className='mt-1p mb-1p'>
<Header as='span' sub>License: </Header>
<span>{getLicenseText(project.license)}</span>
<span>{project.license.name ?? 'Unknown License'} {project.license.version ?? ""}</span>
</div>
}
{(project.resourceURL && !isEmptyString(project.resourceURL)) &&
{project.license && project.license.sourceURL &&
<div className='mt-1p'>
<a href={normalizeURL(project.resourceURL)} target='_blank' rel='noopener noreferrer'>Resource Link<Icon name='external' className='ml-1p' /></a>
<a href={normalizeURL(project.license.sourceURL)} target='_blank' rel='noopener noreferrer'>Resource Link<Icon name='external' className='ml-1p' /></a>
</div>
}
</Grid.Column>
Expand Down
5 changes: 2 additions & 3 deletions client/src/components/util/ProjectHelpers.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
import { Project, ProjectFile, User } from "../../types";
import { ProjectFileLicense } from "../../types/Project";
import { Project, User, License } from "../../types";
import { threePointLikertOptions, fivePointLikertOptions, sevenPointLikertOptions } from "./LikertHelpers";

const visibilityOptions = [
Expand Down Expand Up @@ -359,7 +358,7 @@ const getFilesAccessText = (access: string) => {
return 'Unknown';
};

const getFilesLicenseText = (license: ProjectFileLicense | undefined) => {
const getFilesLicenseText = (license: License | undefined) => {
if (license) {
if(license.name && license.version){
return `${license.name} (${license.version})`;
Expand Down
4 changes: 2 additions & 2 deletions client/src/screens/commons/Book/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -35,8 +35,8 @@ import { truncateString } from "../../../components/util/HelperFunctions.js";
import { useTypedSelector } from "../../../state/hooks";
import {
Book,
License,
LicenseReport,
LicenseReportLicense,
LicenseReportText,
PeerReview as PeerReviewType,
ProjectFile,
Expand Down Expand Up @@ -557,7 +557,7 @@ const CommonsBook = () => {
* and version (if applicable).
* @returns {React.ReactElement | null} The UI-ready license information presentation.
*/
function renderLicenseLink(licenseObj: License) {
function renderLicenseLink(licenseObj: LicenseReportLicense) {
if (typeof licenseObj === "object") {
if (licenseObj.link && licenseObj.link !== "#") {
return (
Expand Down
13 changes: 9 additions & 4 deletions client/src/types/LicenseReport.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ export type LicenseReport = {
}

export type LicenseReportText = {
license: License;
license: LicenseReportLicense;
id: string;
url: string;
title: string;
Expand All @@ -18,12 +18,17 @@ export type LicenseReportText = {
}

export type LicenseReportMeta = {
mostRestrictiveLicense: Omit<License, 'count' | 'percent'>;
mostRestrictiveLicense: Omit<LicenseReportLicense, 'count' | 'percent'>;
specialRestrictions: string[];
licenses: License[]
licenses: LicenseReportLicense[]
}

export type License = {
/**
* @deprecated
* Should be used for LicenseReport purposes only
* @see License in types/Misc.ts
*/
export type LicenseReportLicense = {
label: string;
link: string;
raw: string;
Expand Down
9 changes: 9 additions & 0 deletions client/src/types/Misc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,3 +34,12 @@ export type CloudflareCaptionData = {
language: string;
label: string;
}

export type License = {
name?: string;
url?: string;
version?: string;
sourceURL?: string;
modifiedFromSource?: boolean;
additionalTerms?: string;
}
17 changes: 4 additions & 13 deletions client/src/types/Project.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { AssetTag } from "./AssetTagging";
import { Author } from "./Author";
import { License } from "./Misc";
import { User } from "./User";
import { a11ySectionReviewSchema } from "./a11y";

Expand All @@ -17,15 +18,6 @@ export type CIDDescriptor = {
expires?: Date;
};

export interface ProjectFileLicense {
name?: string;
url?: string;
version?: string;
sourceURL?: string;
modifiedFromSource?: boolean;
additionalTerms?: string;
}

export type ProjectFileAuthor = Omit<Author, "userUUID">;

export interface ProjectFilePublisher {
Expand All @@ -51,7 +43,7 @@ export type ProjectFile = {
downloadCount: number;
tags?: AssetTag[];
createdDate?: Date;
license?: ProjectFileLicense;
license?: License;
primaryAuthor?: ProjectFileAuthor;
authors?: ProjectFileAuthor[];
correspondingAuthor?: ProjectFileAuthor;
Expand Down Expand Up @@ -131,8 +123,7 @@ export type Project = {
libreCampus: string;
author: string;
authorEmail: string;
license: string;
resourceURL: string;
license: License;
projectURL: string;
adaptURL: string;
adaptCourseID: string;
Expand All @@ -150,7 +141,7 @@ export type Project = {
preferredPRRubric: String;
cidDescriptors: CIDDescriptor[];
associatedOrgs: string[];
defaultFileLicense?: ProjectFileLicense;
defaultFileLicense?: License;
didCreateWorkbench?: boolean;
thumbnail?: string;
projectModules?: ProjectModuleSettings;
Expand Down
Loading

0 comments on commit bfc759f

Please sign in to comment.