Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
24 commits
Select commit Hold shift + click to select a range
f826c75
Debug mapping table loader issue
sayalijoshi27 Nov 6, 2025
027cb64
Merge branch 'dev' of https://github.com/contentstack/migration-v2-no…
sayalijoshi27 Nov 10, 2025
a3adf58
refactor LegacyCMS componnet: set default affix and adjust step compl…
yashin4112 Nov 12, 2025
b5823fa
Merge branch 'dev' of https://github.com/contentstack/migration-v2-no…
sayalijoshi27 Nov 13, 2025
d6bb94a
implement default affix handling on blur and chnaged the affix descri…
yashin4112 Nov 14, 2025
2d37d02
Merge pull request #836 from contentstack/feature/aem-final
sayalijoshi27 Nov 17, 2025
cd52fc7
refactor LegacyCMS componnet: set default affix and adjust step compl…
yashin4112 Nov 12, 2025
4cc217d
implement default affix handling on blur and chnaged the affix descri…
yashin4112 Nov 14, 2025
4b336a7
Merge branch 'feature/affix' of https://github.com/contentstack/migra…
yashin4112 Nov 19, 2025
041ac1c
refactor LegacyCMS componnet: set default affix and adjust step compl…
yashin4112 Nov 12, 2025
172c21e
dev merged
yashin4112 Nov 19, 2025
0e2fc7e
refactor: Added backupFieldUid to group for resetting issue
AishDani Nov 19, 2025
d926e58
bugfix: null check added
yashin4112 Nov 19, 2025
6703666
fix: Enhance resetToInitialMapping to include contentTypeId in field …
AishDani Nov 19, 2025
3cc0630
fix: made a consistent uidCorrector function for embed objects
AishDani Nov 19, 2025
7775cd3
comment resolved: null check added
yashin4112 Nov 19, 2025
0aeec46
Merge pull request #839 from contentstack/feature/affix
sayalijoshi27 Nov 19, 2025
2df121d
Merge pull request #845 from contentstack/bugfix/cmg-752
sayalijoshi27 Nov 19, 2025
ee92669
code added to set content type default value for link datatype
yashin4112 Nov 19, 2025
153f49b
Merge pull request #846 from contentstack/bugfix/cmg-764
sayalijoshi27 Nov 19, 2025
616ac45
table loader fixes
sayalijoshi27 Nov 19, 2025
d6d57d5
Merge pull request #848 from contentstack/feature/aem-final
sayalijoshi27 Nov 19, 2025
72bb064
copilot comments resolved
sayalijoshi27 Nov 19, 2025
ac18582
Merge pull request #849 from contentstack/feature/aem-final
sayalijoshi27 Nov 19, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions api/src/services/contentMapper.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -751,7 +751,7 @@ const resetToInitialMapping = async (req: Request) => {
const fieldMappingData = contentTypeData.fieldMapping.map((itemId: any) => {
const fieldData = FieldMapperModel.chain
.get("field_mapper")
.find({ id: itemId, projectId: projectId })
.find({ id: itemId, projectId: projectId, contentTypeId: contentTypeId})
.value();
return fieldData;
});
Expand All @@ -771,7 +771,7 @@ const resetToInitialMapping = async (req: Request) => {
//await FieldMapperModel.read();
(fieldMappingData || []).forEach((field: any) => {
const fieldIndex = FieldMapperModel.data.field_mapper.findIndex(
(f: any) => f?.id === field?.id
(f: any) => f?.id === field?.id && f?.projectId === projectId && f?.contentTypeId === contentTypeId
);
if (fieldIndex > -1) {
FieldMapperModel.update((data: any) => {
Expand Down
31 changes: 23 additions & 8 deletions api/src/services/sitecore.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -105,16 +105,31 @@ async function writeFiles(
console.error('Error writing files:', error);
}
}
const uidCorrector = ({ uid } :{uid : string}) => {
if (!uid || typeof uid !== 'string') {
return '';
}

let newUid = uid;

// Note: UIDs starting with numbers and restricted keywords are handled externally in Sitecore
// The prefix is applied in contentTypeMaker function when needed

const uidCorrector = ({ uid }: any) => {
if (startsWithNumber(uid)) {
return `${append}_${_.replace(
uid,
new RegExp('[ -]', 'g'),
'_'
)?.toLowerCase()}`;
// Clean up the UID
newUid = newUid
.replace(/[ -]/g, '_') // Replace spaces and hyphens with underscores
.replace(/[^a-zA-Z0-9_]+/g, '_') // Replace non-alphanumeric characters (except underscore)
.replace(/([A-Z])/g, (match) => `_${match.toLowerCase()}`) // Handle camelCase
.toLowerCase() // Convert to lowercase
.replace(/_+/g, '_') // Replace multiple underscores with single
.replace(/^_|_$/g, ''); // Remove leading/trailing underscores

// Ensure UID doesn't start with underscore (Contentstack requirement)
if (newUid.startsWith('_')) {
newUid = newUid.substring(1);
}
return _.replace(uid, new RegExp('[ -]', 'g'), '_')?.toLowerCase();

return newUid;
};

const createAssets = async ({
Expand Down
34 changes: 27 additions & 7 deletions api/src/utils/content-type-creator.utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -74,12 +74,32 @@ function startsWithNumber(str: string) {
return /^\d/.test(str);
}

const uidCorrector = ({ uid }: any) => {
if (startsWithNumber(uid)) {
return `a_${_.replace(uid, new RegExp("[ -]", "g"), '_')?.toLowerCase()}`
const uidCorrector = ({ uid } : {uid : string}) => {
if (!uid || typeof uid !== 'string') {
return '';
}
return _.replace(uid, new RegExp("[ -]", "g"), '_')?.toLowerCase()
}

let newUid = uid;

// Note: UIDs starting with numbers and restricted keywords are handled externally in Sitecore
// The prefix is applied in contentTypeMaker function when needed

// Clean up the UID
newUid = newUid
.replace(/[ -]/g, '_') // Replace spaces and hyphens with underscores
.replace(/[^a-zA-Z0-9_]+/g, '_') // Replace non-alphanumeric characters (except underscore)
.replace(/([A-Z])/g, (match) => `_${match.toLowerCase()}`) // Handle camelCase
.toLowerCase() // Convert to lowercase
.replace(/_+/g, '_') // Replace multiple underscores with single
.replace(/^_|_$/g, ''); // Remove leading/trailing underscores

// Ensure UID doesn't start with underscore (Contentstack requirement)
if (newUid.startsWith('_')) {
newUid = newUid.substring(1);
}

return newUid;
};


function buildFieldSchema(item: any, marketPlacePath: string, parentUid = ''): any {
Expand Down Expand Up @@ -577,8 +597,8 @@ const convertToSchemaFormate = ({ field, advanced = false, marketPlacePath, keyM
"field_metadata": {
description: "",
"default_value": {
"title": "",
"url": '',
"title": field?.advanced?.title ?? '',
"url": field?.advanced?.url ?? '',
}
},
"format": field?.advanced?.validationRegex ?? '',
Expand Down
2 changes: 1 addition & 1 deletion ui/src/cmsData/legacyCms.json
Original file line number Diff line number Diff line change
Expand Up @@ -338,7 +338,7 @@
"uid": "cs6c761d71844ac800"
},
"title": "Add Source Affix",
"description": "Add a 2–5 character affix for the source name. Use only letters, no numbers or special characters",
"description": "Add a 2–5 letter-only affix for the source name. The affix will function as a prefix if the content type UID matches with <a href=\"https://www.contentstack.com/docs/developers/create-content-types/restricted-keywords-for-uids\" target=\"_blank\">restricted UIDs</a>. The affix will function as a suffix for field UIDs. If left blank, \"cs\" will be used by default. Avoid numbers, special characters, and restricted keywords",
"step_lock_text": "Editing this step is currently disabled. To make changes in Draft mode, please deactivate the migration",
"lock": false,
"active": false,
Expand Down
4 changes: 1 addition & 3 deletions ui/src/components/ContentMapper/index.scss
Original file line number Diff line number Diff line change
Expand Up @@ -151,10 +151,8 @@
}
.Table {
border-left: 0 none;
// min-height: inherit;
min-height: 26.25rem;
.Table__body__row {
// height: auto!important;
// min-height: 80px;
.Table-select-body {
>.checkbox-wrapper {
align-items: flex-start;
Expand Down
26 changes: 6 additions & 20 deletions ui/src/components/ContentMapper/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -760,15 +760,16 @@ const ContentMapper = forwardRef(({ handleStepChange }: contentMapperProps, ref:
}

setItemStatusMap(itemStatusMap);
setLoading(true);

const { data } = await getFieldMapping(contentTypeId || '', 0, 1000, searchText || '', projectId);


for (let index = 0; index <= 1000; index++) {
itemStatusMap[index] = 'loaded';
}

setItemStatusMap({ ...itemStatusMap });
setLoading(false);

const validTableData = data?.fieldMapping?.filter((field: FieldMapType) => field?.otherCmsType !== undefined);

Expand All @@ -777,7 +778,6 @@ const ContentMapper = forwardRef(({ handleStepChange }: contentMapperProps, ref:
setSelectedEntries(validTableData ?? []);
setTotalCounts(validTableData?.length);
setInitialRowSelectedData(validTableData?.filter((item: FieldMapType) => !item?.isDeleted))
setIsLoading(false);
generateSourceGroupSchema(validTableData);
} catch (error) {
console.error('fetchData -> error', error);
Expand Down Expand Up @@ -811,15 +811,15 @@ const ContentMapper = forwardRef(({ handleStepChange }: contentMapperProps, ref:
}

setItemStatusMap({ ...updateditemStatusMapCopy });
setLoading(false);

const validTableData = data?.fieldMapping?.filter((field: FieldMapType) => field?.otherCmsType !== undefined);

// eslint-disable-next-line no-unsafe-optional-chaining
setTableData([...tableData, ...validTableData ?? tableData]);
setTotalCounts([...tableData, ...validTableData ?? tableData]?.length);
setIsLoading(false);
setTableData(validTableData ?? []);
setSelectedEntries(validTableData ?? []);
setTotalCounts(validTableData?.length);
setIsAllCheck(true);

} catch (error) {
console.error('loadMoreItems -> error', error);
}
Expand Down Expand Up @@ -2645,20 +2645,6 @@ const ContentMapper = forwardRef(({ handleStepChange }: contentMapperProps, ref:
initialRowSelectedData={initialRowSelectedData}
initialSelectedRowIds={rowIds}
itemSize={80}
v2Features={{
isNewEmptyState: true
}}
customEmptyState={
<EmptyState
forPage="list"
heading={<div className="empty_search_heading">No Fields available</div>}
moduleIcon="NoSearchResult"
description="Try changing the search query to find what you are looking for."
version="v2"
testId="no-results-found-page"
className="custom-empty-state"
/>
}
withExportCta={{
component: (
<div className='d-flex align-items-center'>
Expand Down
27 changes: 19 additions & 8 deletions ui/src/components/LegacyCms/Actions/LoadPrefix.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ const LoadPreFix = (props: LoadSelectCmsProps) => {

const dispatch = useDispatch();

const [prefix, setPrefix] = useState<string>(newMigrationData?.legacy_cms?.affix || '');
const [prefix, setPrefix] = useState<string>(newMigrationData?.legacy_cms?.affix || 'cs');

const [isError, setIsError] = useState<boolean>(false);
const [errorMessage, setErrorMessage] = useState<string>('');
Expand Down Expand Up @@ -76,7 +76,6 @@ const LoadPreFix = (props: LoadSelectCmsProps) => {
isRestictedKeywordCheckboxChecked: isCheckedBoxChecked
}
};

dispatch(updateNewMigrationData(newMigrationDataObj));
} else {
setPrefix(value);
Expand All @@ -93,28 +92,37 @@ const LoadPreFix = (props: LoadSelectCmsProps) => {
};

dispatch(updateNewMigrationData(newMigrationDataObj));

setIsError(false);

//call for Step Change
props?.handleStepChange(props?.currentStep);
return;
}
} else {
setIsError(true);
setErrorMessage('Please enter Affix');
setIsError(false);
setErrorMessage('');
setIsRestrictedKey(false);
setPrefix('');
}
});

const handleOnBlur = (value: string) => {
if (isEmptyString(value?.trim())) {
setIsError(false);
setErrorMessage('');
setIsRestrictedKey(false);
setPrefix('cs');
const newMigrationDataObj: INewMigration = {
...newMigrationData,
legacy_cms: {
...newMigrationData?.legacy_cms,
affix: value,
affix: 'cs',
isRestictedKeywordCheckboxChecked: isCheckedBoxChecked
}
};

dispatch(updateNewMigrationData(newMigrationDataObj));
}
});
};

/**** ALL USEEffects HERE ****/

Expand All @@ -136,6 +144,9 @@ const LoadPreFix = (props: LoadSelectCmsProps) => {
aria-label="affix"
disabled={newMigrationData?.legacy_cms?.uploadedFile?.isValidated}
isReadOnly={newMigrationData?.legacy_cms?.uploadedFile?.isValidated}
onBlur={(e: React.FocusEvent<HTMLInputElement>) => {
handleOnBlur(e.target.value);
}}
/>
{isError && <p className="errorMessage">{errorMessage}</p>}
</div>
Expand Down
18 changes: 8 additions & 10 deletions ui/src/components/LegacyCms/Actions/LoadUploadFile.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -154,7 +154,6 @@ const LoadUploadFile = (props: LoadUploadFileProps) => {
setIsDisabled(true);

if (
!isEmptyString(newMigrationData?.legacy_cms?.affix) &&
!isEmptyString(newMigrationData?.legacy_cms?.selectedCms?.cms_id) &&
!isEmptyString(newMigrationData?.legacy_cms?.selectedFileFormat?.fileformat_id)
) {
Expand Down Expand Up @@ -358,24 +357,23 @@ const LoadUploadFile = (props: LoadUploadFileProps) => {
setShowMessage(true);
setValidationMessage('File validated successfully.');
setIsDisabled(true);
!isEmptyString(newMigrationData?.legacy_cms?.affix) ||
if (
!isEmptyString(newMigrationData?.legacy_cms?.selectedCms?.cms_id) ||
(!isEmptyString(newMigrationData?.legacy_cms?.selectedFileFormat?.fileformat_id) &&
props.handleStepChange(props?.currentStep, true));
!isEmptyString(newMigrationData?.legacy_cms?.selectedFileFormat?.fileformat_id)
) {
props.handleStepChange(props?.currentStep, true);
}
}
if (newMigrationData?.legacy_cms?.uploadedFile?.reValidate) {
setValidationMessage('');
}
if(!isEmptyString(newMigrationData?.legacy_cms?.affix) && !newMigrationData?.legacy_cms?.uploadedFile?.isValidated && !newMigrationData?.legacy_cms?.uploadedFile?.reValidate){
if(!newMigrationData?.legacy_cms?.uploadedFile?.isValidated && !newMigrationData?.legacy_cms?.uploadedFile?.reValidate){
setIsDisabled(false);
}
setReValidate(newMigrationData?.legacy_cms?.uploadedFile?.reValidate || false);

// else{
// setIsValidated(false);
// }
}, [isValidated, newMigrationData]);


useEffect(() => {
if (newMigrationData?.legacy_cms?.selectedFileFormat?.fileformat_id) {
setFileFormat(newMigrationData?.legacy_cms?.selectedFileFormat?.fileformat_id);
Expand Down Expand Up @@ -448,7 +446,7 @@ const LoadUploadFile = (props: LoadUploadFileProps) => {
isLoading={isLoading}
loadingColor="#6c5ce7"
version="v2"
disabled={!(reValidate || (!isDisabled && !isEmptyString(newMigrationData?.legacy_cms?.affix)))}
disabled={!(reValidate || (!isDisabled))}
>
Validate File
</Button>
Expand Down
10 changes: 4 additions & 6 deletions ui/src/components/LegacyCms/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ const LegacyCMSComponent = forwardRef(({ legacyCMSData, isCompleted, handleOnAll
}

//Make Step 2 complete
if (!isEmptyString(selectedCmsData?.cms_id) && (!isEmptyString(legacyCMSData?.affix) || !isEmptyString(newMigrationData?.legacy_cms?.affix))) {
if (!isEmptyString(selectedCmsData?.cms_id) && (!isEmptyString(legacyCMSData?.affix) )) {
setInternalActiveStepIndex(1);
}

Expand Down Expand Up @@ -197,22 +197,20 @@ const LegacyCMSComponent = forwardRef(({ legacyCMSData, isCompleted, handleOnAll

//Make Step 2 complete
if (
!isEmptyString(newMigrationData?.legacy_cms?.selectedCms?.cms_id) &&
!isEmptyString(newMigrationData?.legacy_cms?.affix)
!isEmptyString(newMigrationData?.legacy_cms?.selectedCms?.cms_id)
) {
setInternalActiveStepIndex(1);
}

if(!isEmptyString(newMigrationData?.legacy_cms?.selectedCms?.cms_id) && !isEmptyString(newMigrationData?.legacy_cms?.affix) && newMigrationData?.legacy_cms?.uploadedFile?.isValidated){
if(!isEmptyString(newMigrationData?.legacy_cms?.selectedCms?.cms_id) && newMigrationData?.legacy_cms?.uploadedFile?.isValidated){
setInternalActiveStepIndex(3);
}
setisProjectMapped(newMigrationData?.isprojectMapped)

},[newMigrationData]);

useEffect(()=>{
if(! isEmptyString(newMigrationData?.legacy_cms?.affix)
&& !isEmptyString(newMigrationData?.legacy_cms?.selectedFileFormat?.title) &&
if( !isEmptyString(newMigrationData?.legacy_cms?.selectedFileFormat?.title) &&
! isEmptyString(newMigrationData?.legacy_cms?.selectedCms?.title) &&
newMigrationData?.legacy_cms?.uploadedFile?.isValidated){
setIsAllStepsCompleted(true);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -159,11 +159,6 @@ const HorizontalStepper = forwardRef(
shouldCloseOnOverlayClick: false
}
});
} else if (
-1 < newMigrationData?.legacy_cms?.currentStep &&
newMigrationData?.legacy_cms?.currentStep < 2
) {
showNotification(newMigrationData?.legacy_cms?.currentStep + 1);
} else if (
newMigrationData?.destination_stack?.selectedStack === undefined ||
newMigrationData?.destination_stack?.selectedStack === null ||
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,12 @@ const AutoVerticalStepper = React.forwardRef<
</Tooltip>
) : null} */}
</div>
{data.description && <div className="stepper-discription"> {data.description}</div>}
{data?.description && (
<div
className="stepper-discription"
dangerouslySetInnerHTML={{ __html: data?.description }}
Copy link

Copilot AI Nov 19, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Using dangerouslySetInnerHTML without sanitization can lead to XSS vulnerabilities. The data.description value should be sanitized before rendering to prevent potential security risks from malicious HTML content.

Copilot uses AI. Check for mistakes.
/>
)}
</>
);
};
Expand Down
2 changes: 1 addition & 1 deletion ui/src/services/api/upload.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ export const uploadFilePath = () => {
return `${UPLOAD_FILE_RELATIVE_URL}upload`;
};

export const fileValidation = async (projectId: string, affix: string) => {
export const fileValidation = async (projectId: string, affix = 'cs') => {
try {
const options = {
headers: {
Expand Down
3 changes: 2 additions & 1 deletion upload-api/migration-sitecore/libs/contenttypes.js
Original file line number Diff line number Diff line change
Expand Up @@ -413,7 +413,8 @@ const groupFlat = (data, item) => {
contentstackField: item?.meta?.name,
contentstackFieldUid: data?.uid, // Use the corrected UID from groupSchema
contentstackFieldType: 'group',
backupFieldType: 'group'
backupFieldType: 'group',
backupFieldUid: data?.uid
};
flat?.push(group);
data?.schema?.forEach((element) => {
Expand Down