diff --git a/.flowconfig b/.flowconfig deleted file mode 100644 index 5022ec76d..000000000 --- a/.flowconfig +++ /dev/null @@ -1,12 +0,0 @@ -[ignore] -.*/venv/.* -.*/dist/.* -.*/jsonlint/.* - -[include] - -[libs] - -[lints] - -[options] diff --git a/.github/dependabot.yml b/.github/dependabot.yml index 185084047..25ce47364 100644 --- a/.github/dependabot.yml +++ b/.github/dependabot.yml @@ -7,7 +7,6 @@ version: 2 updates: - package-ecosystem: "pip" # See documentation for possible values directory: "/" # Location of package manifests - versioning-strategy: widen schedule: interval: "weekly" @@ -15,3 +14,12 @@ updates: directory: "/" # Location of package manifests schedule: interval: "weekly" + ignore: + - dependency-name: "webpack" + versions: [">=5.0.0"] + - dependency-name: "webpack-dev-server" + versions: [">=5.0.0"] + - dependency-name: "css-loader" + versions: [">=6.0.0"] + - dependency-name: "style-loader" + versions: [">=2.0.0"] diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index a68954358..62c99b517 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -1,5 +1,22 @@ -## Checklist +### Purpose + + +### What has changed + + +### Steps to test + + + + +### Checklist - [ ] This pull request is not adding new forms that use redux - [ ] This pull request is adding missing TypeScript types to modified code segments where it's easy to do so with confidence - [ ] This pull request is replacing `lodash.get` with optional chaining for modified code segments + +Resolves: #[issue-number] diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index b21a5c60c..d1e606a8e 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -27,10 +27,8 @@ jobs: sudo apt-get update sudo apt-get install pkg-config libxml2-dev libxmlsec1-dev libxmlsec1-openssl - - name: pip install - run: | - python -m pip install -U pip wheel setuptools - python -m pip install -Ur dev-requirements.txt + - run: python -m pip install --upgrade pip wheel setuptools + - run: python -m pip install -r dev-requirements.txt - name: pytest run: pytest --ignore=tests/aap/ --disable-pytest-warnings --cov=newsroom @@ -47,12 +45,16 @@ jobs: client: runs-on: ubuntu-latest + strategy: + matrix: + node-version: ['14', '18', '20'] + steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4 with: - node-version: 14.x + node-version: ${{ matrix.node-version }} cache: npm - name: npm install diff --git a/.jscsrc b/.jscsrc deleted file mode 100644 index a8ee520c5..000000000 --- a/.jscsrc +++ /dev/null @@ -1,3 +0,0 @@ -{ - "preset": "airbnb" -} diff --git a/.travis.yml b/.travis.yml deleted file mode 100644 index 576dadf0c..000000000 --- a/.travis.yml +++ /dev/null @@ -1,57 +0,0 @@ -language: python - -python: 3.6 - -dist: bionic - -services: - - docker - -addons: - chrome: stable - -cache: - - pip - - npm - - yarn - -before_install: - - | - if [ "$SKIP_SERVER_INSTALL" != "true" ]; then - docker-compose -f .travis-docker-compose.yml up -d - fi - -install: - - | - if [ "$SKIP_SERVER_REQUIREMENTS_INSTALL" != "true" ]; then - pip install -r requirements.txt - fi - - | - if [ "$SKIP_SERVER_REQUIREMENTS_INSTALL" != "true" ]; then - pip install -r dev-requirements.txt - fi - - | - if [ "$SKIP_CLIENT_REQUIREMENTS_INSTALL" != "true" ]; then - yarn install - fi - -jobs: - include: - - script: flake8 - name: "flake8" - env: SKIP_SERVER_INSTALL=true SKIP_CLIENT_REQUIREMENTS_INSTALL=true - - script: pytest --disable-pytest-warnings --cov=newsroom - name: "pytest" - env: SKIP_CLIENT_REQUIREMENTS_INSTALL=true - - script: behave --format progress2 --logging-level=ERROR - name: "behave" - env: SKIP_CLIENT_REQUIREMENTS_INSTALL=true - - script: npm run lint - name: "lint" - env: SKIP_SERVER_INSTALL=true SKIP_SERVER_REQUIREMENTS_INSTALL=true - - script: npm run test - name: "karma" - env: SKIP_SERVER_INSTALL=true SKIP_SERVER_REQUIREMENTS_INSTALL=true - - script: npm run build - name: "build" - env: SKIP_SERVER_INSTALL=true SKIP_SERVER_REQUIREMENTS_INSTALL=true diff --git a/assets/agenda/actions.ts b/assets/agenda/actions.ts index cf6b2f4ff..afb577ab5 100644 --- a/assets/agenda/actions.ts +++ b/assets/agenda/actions.ts @@ -375,7 +375,7 @@ function setListGroupsAndLoadHiddenItems(items: Array, next?: boole } } - const groups: Array = (searchParams.sortQuery ?? '_score') === '_score' ? + const groups: Array = (searchParams.sortQuery ?? '') === '' ? groupItems(items, minDate, maxDate, activeGrouping, featuredOnly) : [{ date: '', diff --git a/assets/agenda/components/AgendaApp.tsx b/assets/agenda/components/AgendaApp.tsx index 30b7d784a..17862b4ea 100644 --- a/assets/agenda/components/AgendaApp.tsx +++ b/assets/agenda/components/AgendaApp.tsx @@ -5,8 +5,8 @@ import {connect} from 'react-redux'; import {get, isEmpty} from 'lodash'; import {ISearchSortValue} from 'interfaces'; -import {getItemFromArray, gettext} from 'utils'; -import {noNavigationSelected} from 'search/utils'; +import {gettext} from 'utils'; +import {noNavigationSelected, searchParamsUpdated} from 'search/utils'; import { fetchItems, @@ -137,7 +137,7 @@ class AgendaApp extends SearchBase { showTotalItems = showTotalLabel = true; } - const showFilters = Object.values(this.props.searchParams ?? {}).find((val) => val != null) != null || + const showFilters = searchParamsUpdated(this.props.searchParams) || this.props.activeTopic != null || Object.keys(this.props.activeFilter ?? {}).length > 0 || this.props.activeQuery != null || @@ -252,7 +252,12 @@ class AgendaApp extends SearchBase { setQuery={this.props.setQuery} setSortQuery={this.props.setSortQuery} showSortDropdown={true} - defaultSortValue="_score" + sortOptions={[ + {label: gettext('Date'), value: ''}, + {label: gettext('Newest updates'), value: 'versioncreated:desc'}, + {label: gettext('Oldest updates'), value: 'versioncreated:asc'}, + {label: gettext('Relevance'), value: '_score'}, + ]} /> ) } @@ -428,6 +433,7 @@ const mapDispatchToProps = (dispatch: any) => ({ toggleFeaturedFilter: (fetch: any) => dispatch(toggleFeaturedFilter(fetch)), setQuery: (query: any) => dispatch(setQuery(query)), setSortQuery: (query: ISearchSortValue) => dispatch(setSortQuery(query)), + saveMyTopic: (params: any) => dispatch(saveMyTopic(params)), }); const component: React.ComponentType = connect(mapStateToProps, mapDispatchToProps)(AgendaApp); diff --git a/assets/agenda/components/AgendaList.tsx b/assets/agenda/components/AgendaList.tsx index 34b73666e..30a3a8e82 100644 --- a/assets/agenda/components/AgendaList.tsx +++ b/assets/agenda/components/AgendaList.tsx @@ -352,17 +352,21 @@ class AgendaList extends React.Component { const plans = this.props.itemTypeFilter !== 'planning' ? [] : getPlanningItemsByGroup(this.props.itemsById[itemId], group.date); + const version = getIntVersion(this.props.itemsById[itemId]); + const isRead = version == null + ? this.props.readItems[itemId] != null // event + : version === this.props.readItems[itemId]; // planning item return plans.length > 0 ? ( {plans.map((plan) => ( { ) : ( {gettext('More hidden')} - - ({gettext('{{ numberOfItems }} Event', {numberOfItems: itemIds.length})}) - ) : (
{gettext('More hidden')} - - ({gettext('{{ numberOfItems }} Event & Planning', {numberOfItems: itemCount})}) -
{coverageTypes.map((coverageType) => ( diff --git a/assets/agenda/components/AgendaListItem.tsx b/assets/agenda/components/AgendaListItem.tsx index 8bb547e76..e4be185e4 100644 --- a/assets/agenda/components/AgendaListItem.tsx +++ b/assets/agenda/components/AgendaListItem.tsx @@ -1,7 +1,7 @@ import React from 'react'; import classNames from 'classnames'; -import {IAgendaItem, IItemAction, IListConfig, IUser} from 'interfaces'; +import {IAgendaItem, IAgendaListGroup, IItemAction, IListConfig, IUser} from 'interfaces'; import ActionButton from 'components/ActionButton'; import AgendaListItemIcons from './AgendaListItemIcons'; @@ -27,7 +27,7 @@ import {LabelGroup} from 'ui/components/LabelGroup'; interface IProps { item: IAgendaItem; - group: string; + group: IAgendaListGroup; isActive: boolean; isSelected: boolean; isRead: boolean; @@ -40,9 +40,9 @@ interface IProps { showShortcutActionIcons: boolean; listConfig: IListConfig; - onClick(item: IAgendaItem, group: string, planningId?: IAgendaItem['_id']): void; - onDoubleClick(item: IAgendaItem, group: string, planningId?: IAgendaItem['_id']): void; - onActionList(event: React.MouseEvent, item: IAgendaItem, group: string, plan?: IAgendaItem): void; + onClick(item: IAgendaItem, date: string, planningId?: IAgendaItem['_id']): void; + onDoubleClick(item: IAgendaItem, date: string, planningId?: IAgendaItem['_id']): void; + onActionList(event: React.MouseEvent, item: IAgendaItem, date: string, plan?: IAgendaItem): void; toggleSelected(): void; resetActioningItem(): void; } @@ -83,11 +83,11 @@ class AgendaListItem extends React.Component { } onArticleClick() { - this.props.onClick(this.props.item, this.props.group, this.props.planningId); + this.props.onClick(this.props.item, this.props.group.date, this.props.planningId); } onArticleDoubleClick() { - this.props.onDoubleClick(this.props.item, this.props.group, this.props.planningId); + this.props.onDoubleClick(this.props.item, this.props.group.date, this.props.planningId); } onMouseEnter() { @@ -209,9 +209,9 @@ class AgendaListItem extends React.Component { if (segmentsRemainingToBeAdded > 0) { paragraphsInnerText += paragraph.innerText; paragraph.innerHTML = paragraph.innerHTML.split('
').filter((p: string) => p.trim() !== '').slice(0, segmentsRemainingToBeAdded).join('
'); - + segmentsRemainingToBeAdded = segmentsRemainingToBeAdded - getSegmentCount(paragraph); - + descriptionHTMLArr.push(paragraph); } }); @@ -293,6 +293,14 @@ class AgendaListItem extends React.Component { } })(); + const renderTopStoryLabel = () => { + if (group.hiddenItems.includes(item._id) === false) { + return ; + } else { + return null; + } + }; + return (
{
- + {renderTopStoryLabel()} @@ -338,7 +346,7 @@ class AgendaListItem extends React.Component { { item={this.props.item} plan={planningItem} user={this.props.user} - group={this.props.group} + group={this.props.group.date} actions={this.props.actions} onActionList={this.props.onActionList} showActions={this.props.showActions} @@ -418,7 +426,7 @@ class AgendaListItem extends React.Component { item={this.props.item} plan={planningItem} user={this.props.user} - group={this.props.group} + group={this.props.group.date} actions={this.props.actions} onActionList={this.props.onActionList} showActions={this.props.showActions} diff --git a/assets/agenda/components/AgendaMetaTime.tsx b/assets/agenda/components/AgendaMetaTime.tsx index 8162e563e..6497f40dd 100644 --- a/assets/agenda/components/AgendaMetaTime.tsx +++ b/assets/agenda/components/AgendaMetaTime.tsx @@ -53,7 +53,7 @@ export default function AgendaMetaTime({item, borderRight, isRecurring, group, i {'m-0': onlyDates})} > {format(item, onlyDates === true)} - {itemDays <= 1 ? null : ( + {itemDays <= 1 || group === '' ? null : ( void; + item: IAgendaItem; +} /** * Display map image for item location * @param {Object} item * @param {function} onClick */ -export default function AgendaPreviewImage({item, onClick}: any) { +export default function AgendaPreviewImage({item, onClick}: IProps) { if (!shouldRenderLocation(item)) { return null; } @@ -20,10 +24,11 @@ export default function AgendaPreviewImage({item, onClick}: any) { return ( + isOpenDefault={true} + >
onClick(item)}>
@@ -34,4 +39,4 @@ export default function AgendaPreviewImage({item, onClick}: any) { AgendaPreviewImage.propTypes = { item: PropTypes.object.isRequired, onClick: PropTypes.func.isRequired, -}; \ No newline at end of file +}; diff --git a/assets/agenda/index.ts b/assets/agenda/index.ts index 636d9bdbb..ac408c81e 100644 --- a/assets/agenda/index.ts +++ b/assets/agenda/index.ts @@ -8,6 +8,7 @@ import AgendaApp from './components/AgendaApp'; import {fetchItems, setState, initData, initParams, pushNotification, openItemDetails, previewItem} from './actions'; import {setView} from 'search/actions'; import {render} from 'render-utils'; +import 'user-profile'; const store = createStore(agendaReducer, 'Agenda'); diff --git a/assets/agenda/tests/CoverageItemStatus.spec.tsx b/assets/agenda/tests/CoverageItemStatus.spec.tsx index b0a524d06..3811ed8db 100644 --- a/assets/agenda/tests/CoverageItemStatus.spec.tsx +++ b/assets/agenda/tests/CoverageItemStatus.spec.tsx @@ -1,4 +1,3 @@ -import expect from 'expect'; import React from 'react'; import {shallow} from 'enzyme'; import CoverageItemStatus from '../components/CoverageItemStatus'; diff --git a/assets/cards/index.ts b/assets/cards/index.ts index 8cd971df2..1ff017f64 100644 --- a/assets/cards/index.ts +++ b/assets/cards/index.ts @@ -3,6 +3,7 @@ import cardReducer from './reducers'; import CardsApp from './components/CardsApp'; import {initViewData} from './actions'; import {render} from 'render-utils'; +import 'user-profile'; const store = createStore(cardReducer, 'Cards'); diff --git a/assets/common.ts b/assets/common.ts index e057d7785..2fe162258 100644 --- a/assets/common.ts +++ b/assets/common.ts @@ -17,7 +17,6 @@ import 'redux-logger'; import 'reselect'; import 'store'; import 'url-search-params-polyfill'; -import 'whatwg-fetch'; import 'react-datepicker'; export const KEYS = { diff --git a/assets/companies/components/CompanyListItem.tsx b/assets/companies/components/CompanyListItem.tsx index bb5e7d99c..e346be9e2 100644 --- a/assets/companies/components/CompanyListItem.tsx +++ b/assets/companies/components/CompanyListItem.tsx @@ -1,10 +1,19 @@ import React from 'react'; -import PropTypes from 'prop-types'; import classNames from 'classnames'; import {gettext, shortDate, isInPast} from 'utils'; import {getCountryLabel} from '../utils'; +import {ICompany, ICountry} from 'interfaces'; -function CompanyListItem({company, type, isActive, onClick, showSubscriberId, countries}: any) { +interface IProps { + company: ICompany; + type?: {name: string}; + isActive: boolean; + onClick: (id: string) => void; + showSubscriberId: boolean; + countries: Array; +} + +export default function CompanyListItem({company, type, isActive, onClick, showSubscriberId, countries}: IProps) { return ( - {company.name} + {company.name} {company.internal ? ( + {gettext('Internal')} + ) : null} {type ? gettext(type.name) : ''} {showSubscriberId && {company.sd_subscriber_id}} {company.account_manager} @@ -26,17 +37,4 @@ function CompanyListItem({company, type, isActive, onClick, showSubscriberId, co {company.expiry_date && shortDate(company.expiry_date.substring(0, 10), false)} ); -} - -CompanyListItem.propTypes = { - company: PropTypes.object, - type: PropTypes.shape({ - name: PropTypes.string.isRequired, - }), - isActive: PropTypes.bool, - onClick: PropTypes.func, - showSubscriberId: PropTypes.bool, - countries: PropTypes.array -}; - -export default CompanyListItem; +} \ No newline at end of file diff --git a/assets/companies/components/EditCompanyAPI.tsx b/assets/companies/components/EditCompanyAPI.tsx index a524407ab..943a708d1 100644 --- a/assets/companies/components/EditCompanyAPI.tsx +++ b/assets/companies/components/EditCompanyAPI.tsx @@ -90,10 +90,10 @@ export default class EditCompanyAPI extends React.Component { render() { return ( - +
{this.renderIPWhiteList()} - +
); } } diff --git a/assets/companies/components/EditCompanyDetails.tsx b/assets/companies/components/EditCompanyDetails.tsx index 3dc5294b2..40d2f2ad6 100644 --- a/assets/companies/components/EditCompanyDetails.tsx +++ b/assets/companies/components/EditCompanyDetails.tsx @@ -181,6 +181,14 @@ export function EditCompanyDetails({ onChange={onChange} /> + +
{company._id && { - static propTypes: any; - constructor(props: any) { +interface IProps { + isOpenDefault: boolean; + id?: string; + children?: React.ReactNode; + labelCollapsed: string; + labelOpened: string; +} + +interface IState { + open: boolean; +} + +export default class BannerDrop extends React.Component { + constructor(props: IProps) { super(props); - this.state = {open: this.props.isOpen}; + this.state = { + open: this.props.isOpenDefault + }; this.toggleOpen = this.toggleOpen.bind(this); } - componentWillReceiveProps(nextProps: any) { + componentWillReceiveProps(nextProps: IProps) { if (this.props.id !== nextProps.id && - this.state.open !== nextProps.isOpen) { - this.setState({open: nextProps.isOpen}); + this.state.open !== nextProps.isOpenDefault) { + this.setState({open: nextProps.isOpenDefault}); } } @@ -40,13 +52,3 @@ class BannerDrop extends React.Component {
); } } - -BannerDrop.propTypes = { - id: PropTypes.string, - children: PropTypes.node, - isOpen: PropTypes.bool, - labelCollapsed: PropTypes.string, - labelOpened: PropTypes.string, -}; - -export default BannerDrop; diff --git a/assets/components/ExpiryDateInput.tsx b/assets/components/ExpiryDateInput.tsx index 5691077bc..784ec5571 100644 --- a/assets/components/ExpiryDateInput.tsx +++ b/assets/components/ExpiryDateInput.tsx @@ -2,6 +2,7 @@ import React from 'react'; import PropTypes from 'prop-types'; import moment from 'moment'; import DatePicker from 'react-datepicker'; +import format from 'date-fns/format'; import ExpiryButtonWrapper from './ExpiryButtonWrapper'; @@ -63,7 +64,7 @@ export default class ExpiryDateInput extends React.Component { onDateChange(newExpiry: any) { this.setState({value: 'on'}); - this.props.onChange(newExpiry.format('YYYY-MM-DD')); + this.props.onChange(format(newExpiry, 'yyyy-MM-dd')); } render() { @@ -89,11 +90,11 @@ export default class ExpiryDateInput extends React.Component { dropdownMode="select" disabled={this.state.value === 'never'} customInput={} - dateFormat="DD MMMM YYYY" + dateFormat="dd MMMM yyyy" todayButton={gettext('Today')} - selected={this.props.value ? moment(this.props.value) : moment()} + selected={(this.props.value ? moment(this.props.value) : moment()).toDate()} onChange={this.onDateChange} - highlightDates={[moment()]} + highlightDates={[moment().toDate()]} locale={window.locale || 'en'} minDate={new Date()} popperModifiers={[ diff --git a/assets/components/NotificationPopup.tsx b/assets/components/NotificationPopup.tsx index e3bb04de7..2267f9c83 100644 --- a/assets/components/NotificationPopup.tsx +++ b/assets/components/NotificationPopup.tsx @@ -1,6 +1,6 @@ import React from 'react'; -import {formatDate, gettext} from 'utils'; +import {formatDate, gettext, notificationsArePaused, notificationsWillBePaused, parseISODate} from 'utils'; import {IArticle, IUser} from 'interfaces'; import NotificationListItem from './NotificationListItem'; @@ -19,15 +19,10 @@ export interface IProps { } export const NotificationPopup = (props: IProps) => { - const today = (new Date()).toISOString().substring(0, 10); - const pausedFrom = props.fullUser.notification_schedule?.pause_from ?? ''; - const pausedTo = props.fullUser.notification_schedule?.pause_to ?? ''; - const notificationsArePaused = ( - pausedFrom != '' && pausedFrom <= today && - pausedTo != '' && pausedTo >= today - ); + const pausedFrom = parseISODate(props.fullUser.notification_schedule?.pause_from); + const pausedTo = parseISODate(props.fullUser.notification_schedule?.pause_to); - if (notificationsArePaused) { + if (notificationsArePaused(pausedFrom, pausedTo)) { return (
@@ -36,7 +31,7 @@ export const NotificationPopup = (props: IProps) => {
- {gettext('All notifications are paused until {{date}}', {date: formatDate(pausedTo)})} + {gettext('All notifications are paused until {{date}}', {date: formatDate(pausedTo, true)})}
- {(pausedFrom != '' && pausedTo != '' && notificationsArePaused === false) && ( + {(notificationsWillBePaused(pausedFrom, pausedTo)) && (
- {gettext('All notifications are set to be paused from {{dateFrom}} to {{dateTo}}', {dateFrom: formatDate(pausedFrom), dateTo: formatDate(pausedTo)})} + {gettext('All notifications are set to be paused from {{dateFrom}} to {{dateTo}}', {dateFrom: formatDate(pausedFrom, true), dateTo: formatDate(pausedTo, true)})}
)} diff --git a/assets/components/cards/render/CardRow.tsx b/assets/components/cards/render/CardRow.tsx index 6b9089b06..7da2a932f 100644 --- a/assets/components/cards/render/CardRow.tsx +++ b/assets/components/cards/render/CardRow.tsx @@ -63,9 +63,9 @@ class CardRow extends React.Component { } const mapStateToProps = (state: any): IReduxStateProps => ({ - userProducts: state.userProducts, + userProducts: state.userProducts || [], userType: state.userType, - companyProducts: state.companyProducts, + companyProducts: state.companyProducts || [], }); const component: React.ComponentType = connect(mapStateToProps)(CardRow); diff --git a/assets/components/cards/utils.ts b/assets/components/cards/utils.ts index c155b782f..f8738d953 100644 --- a/assets/components/cards/utils.ts +++ b/assets/components/cards/utils.ts @@ -170,7 +170,7 @@ const CARD_TYPES: Array = [ }, { _id: 'wire-list', - text: gettext('wire-list'), + text: gettext('Wire list'), editComponent: ConfigWireList, dashboardComponent: WireListCard, size: 4, diff --git a/assets/design_pages.ts b/assets/design_pages.ts index 118dbcd61..f0d54c1a8 100644 --- a/assets/design_pages.ts +++ b/assets/design_pages.ts @@ -1,5 +1,4 @@ import 'url-search-params-polyfill'; -import 'whatwg-fetch'; import {Tooltip, Dropdown} from 'bootstrap'; import {isTouchDevice} from 'utils'; import {setElementStyle, elementHasClass, replaceElementClasses, onElementClicked, removeElementClass} from 'domUtils'; diff --git a/assets/general-settings/index.ts b/assets/general-settings/index.ts index 847a139f7..4eff0bd39 100644 --- a/assets/general-settings/index.ts +++ b/assets/general-settings/index.ts @@ -3,11 +3,10 @@ import settingsReducer from './reducers'; import GeneralSettingsApp from './components/GeneralSettingsApp'; import {initViewData} from './actions'; import {render} from 'render-utils'; - +import 'user-profile'; const store = createStore(settingsReducer, 'GeneralSettings'); - if (window.viewData) { store.dispatch(initViewData(window.viewData)); } diff --git a/assets/globals.d.ts b/assets/globals.d.ts index 7a4a9f060..8618de643 100644 --- a/assets/globals.d.ts +++ b/assets/globals.d.ts @@ -61,6 +61,7 @@ interface IClientConfig { } }; agenda_sort_events_with_coverage_on_top?: boolean; + collapsed_search_by_default?: boolean; show_user_register?: boolean; multimedia_website_search_url?: string; } diff --git a/assets/home/index.ts b/assets/home/index.ts index a667653ef..5ab18d36b 100644 --- a/assets/home/index.ts +++ b/assets/home/index.ts @@ -4,6 +4,7 @@ import homeReducer from './reducers'; import HomeApp from './components/HomeApp'; import {initData, pushNotification} from './actions'; import {render} from 'render-utils'; +import 'user-profile'; const store = createStore(homeReducer, 'Home'); diff --git a/assets/interfaces/agenda.ts b/assets/interfaces/agenda.ts index 20fdbba7e..3ac24c376 100644 --- a/assets/interfaces/agenda.ts +++ b/assets/interfaces/agenda.ts @@ -1,6 +1,6 @@ import {AnyAction} from 'redux'; import {ThunkAction, ThunkDispatch} from 'redux-thunk'; -import {IFilterGroup, IOccurStatus, ISingleItemAction, IResourceItem, ISection, ISubject, TDatetime, IDateFilters} from './common'; +import {IFilterGroup, IOccurStatus, ISingleItemAction, IResourceItem, ISection, ISubject, TDatetime, IDateFilter} from './common'; import {IAgendaUIConfig} from './configs'; import {IUser, IUserType} from './user'; import {ITopic, ITopicFolder} from './topic'; @@ -313,7 +313,7 @@ export interface IAgendaState { }; errors?: {[field: string]: Array}; loadingAggregations?: boolean; - dateFilters?: IDateFilters + dateFilters?: IDateFilter } export type AgendaGetState = () => IAgendaState; diff --git a/assets/interfaces/common.ts b/assets/interfaces/common.ts index abae2969d..c24b4c89f 100644 --- a/assets/interfaces/common.ts +++ b/assets/interfaces/common.ts @@ -81,9 +81,9 @@ export interface IOccurStatus { label: string; } -export interface IDateFilters{ +export interface IDateFilter { name?: string, default?: boolean, filter?: string; - query?:string; + query?: string; } diff --git a/assets/interfaces/company.ts b/assets/interfaces/company.ts index c2bb7db5a..f5b5fa530 100644 --- a/assets/interfaces/company.ts +++ b/assets/interfaces/company.ts @@ -1,4 +1,4 @@ -import {TDatetime} from './common'; +import {IResourceItem, TDatetime} from './common'; import {IUser} from './user'; export interface IAuthProvider { @@ -17,8 +17,7 @@ export interface IService { code: string; } -export interface ICompany { - _id: string; +export interface ICompany extends IResourceItem { name: string; url?: string; sd_subscriber_id?: string; @@ -48,4 +47,5 @@ export interface ICompany { }>; auth_domains?: Array; auth_provider?: IAuthProvider['_id']; // if not defined, system assumes a value of 'newshub' + internal?: boolean; } diff --git a/assets/interfaces/search.ts b/assets/interfaces/search.ts index dfe60c213..59730435d 100644 --- a/assets/interfaces/search.ts +++ b/assets/interfaces/search.ts @@ -2,7 +2,13 @@ import {ITopic} from './topic'; import {INavigation} from './navigation'; import {IProduct} from './product'; -export type ISearchSortValue = 'versioncreated:desc' | 'versioncreated:asc' | '_score'; +export type ISearchSortValue = 'versioncreated:desc' | 'versioncreated:asc' | '_score' | ''; + +export interface ICreatedFilter { + from?: string; + to?: string; + date_filter?: string; +} export interface ISearchState { activeTopic?: ITopic['_id']; @@ -10,11 +16,7 @@ export interface ISearchState { activeQuery?: string; activeSortQuery?: ISearchSortValue; activeFilter: {[key: string]: any}; - createdFilter: { - from?: string; - to?: string; - date_filter?: string; - }; + createdFilter: ICreatedFilter; productId?: IProduct['_id']; navigations: Array; products: Array; diff --git a/assets/layout/components/SearchBase.tsx b/assets/layout/components/SearchBase.tsx index a61e42cb7..7d1e52286 100644 --- a/assets/layout/components/SearchBase.tsx +++ b/assets/layout/components/SearchBase.tsx @@ -3,7 +3,7 @@ import {get} from 'lodash'; import {createPortal} from 'react-dom'; import {Tooltip} from 'bootstrap'; -import {IArticle, IAgendaItem, IListConfig, IPreviewConfig, IItemAction} from 'interfaces'; +import {IArticle, IAgendaItem, IListConfig, IPreviewConfig, IItemAction, ISearchParams} from 'interfaces'; import {isTouchDevice, gettext, isDisplayed, isMobilePhone} from 'utils'; import {getSingleFilterValue} from 'search/utils'; import {getFilterPanelOpenState, setFilterPanelOpenState} from 'local-store'; @@ -32,6 +32,7 @@ interface IBaseProps { state: any; fetchItems: () => Promise; activeQuery: any; + searchParams: ISearchParams; } export default class SearchBase extends React.Component { diff --git a/assets/maps/components/static.tsx b/assets/maps/components/static.tsx index 2dc0d8fed..806396543 100644 --- a/assets/maps/components/static.tsx +++ b/assets/maps/components/static.tsx @@ -1,12 +1,18 @@ import React from 'react'; -import PropTypes from 'prop-types'; import {getMapSource} from '../utils'; import {gettext} from '../../utils'; +interface IProps { + scale?: number; + locations?: Array; +} + +interface IState { + mapLoaded: boolean; +} -class StaticMap extends React.Component { - static propTypes: any; - constructor(props: any) { +export default class StaticMap extends React.Component { + constructor(props: IProps) { super(props); this.state = { mapLoaded: false @@ -39,10 +45,3 @@ class StaticMap extends React.Component { ); } } - -StaticMap.propTypes = { - scale: PropTypes.number, - locations: PropTypes.arrayOf(PropTypes.object), -}; - -export default StaticMap; diff --git a/assets/monitoring/index.ts b/assets/monitoring/index.ts index f9d450fb2..c4647deb4 100644 --- a/assets/monitoring/index.ts +++ b/assets/monitoring/index.ts @@ -24,6 +24,7 @@ import { toggleNavigation, } from 'search/actions'; +import 'user-profile'; let store: any; diff --git a/assets/navigations/index.ts b/assets/navigations/index.ts index d9961db83..398654bdc 100644 --- a/assets/navigations/index.ts +++ b/assets/navigations/index.ts @@ -3,6 +3,7 @@ import {render} from 'render-utils'; import navigationReducer from './reducers'; import NavigationsApp from './components/NavigationsApp'; import {initViewData} from './actions'; +import 'user-profile'; const store = createStore(navigationReducer, 'Navigations'); diff --git a/assets/news-api/components/EditAPIToken.tsx b/assets/news-api/components/EditAPIToken.tsx index 688df1b35..6224a986b 100644 --- a/assets/news-api/components/EditAPIToken.tsx +++ b/assets/news-api/components/EditAPIToken.tsx @@ -63,7 +63,7 @@ export default class EditAPIToken extends React.Component { creating: false, }); }, (error: any) => { - if (error.response.status === 404) { + if (error.status === 404) { this.setState({ loaded: true, token: { diff --git a/assets/oauth_clients/index.ts b/assets/oauth_clients/index.ts index 9d2ce18d9..4500b5f7a 100644 --- a/assets/oauth_clients/index.ts +++ b/assets/oauth_clients/index.ts @@ -3,6 +3,7 @@ import {render} from 'render-utils'; import clientReducer from './reducers'; import ClientsApp from './components/ClientsApp'; import {initViewData} from './actions'; +import 'user-profile'; const store = createStore(clientReducer, 'oauth_clients'); diff --git a/assets/products/index.ts b/assets/products/index.ts index c2df9f1f5..534fe3507 100644 --- a/assets/products/index.ts +++ b/assets/products/index.ts @@ -3,6 +3,7 @@ import {render} from 'render-utils'; import productReducer from './reducers'; import ProductsApp from './components/ProductsApp'; import {initViewData} from './actions'; +import 'user-profile'; const store = createStore(productReducer, 'Products'); diff --git a/assets/render-utils.tsx b/assets/render-utils.tsx index d09fab91f..1920dc72f 100644 --- a/assets/render-utils.tsx +++ b/assets/render-utils.tsx @@ -1,13 +1,16 @@ -import React from 'react'; +import React, {StrictMode} from 'react'; import {Provider} from 'react-redux'; import {render as _render} from 'react-dom'; import '@superdesk/common/dist/src/index.css'; export function render(store: any, App: any, element?: any, props?: T) { return _render( - - - , + + + + + + , element ); } diff --git a/assets/search/actions.ts b/assets/search/actions.ts index 58e9b3279..1aabf9f56 100644 --- a/assets/search/actions.ts +++ b/assets/search/actions.ts @@ -194,25 +194,27 @@ export function setView(view: any) { * @param {Object} searchParams */ export function saveMyTopic(searchParams: any) { - const type = get(searchParams, 'topic_type') || 'wire'; + return (dispatch: any) => { + const type = get(searchParams, 'topic_type') || 'wire'; - const menu = type === 'agenda' ? - 'events' : - 'topics'; + const menu = type === 'agenda' ? + 'events' : + 'topics'; - if (!get(searchParams, 'label')) { - searchParams.label = get(searchParams, 'query.length', 0) > 0 ? - searchParams.query : - ''; - } + if (!get(searchParams, 'label')) { + searchParams.label = get(searchParams, 'query.length', 0) > 0 ? + searchParams.query : + ''; + } - createOrUpdateTopic(menu, searchParams, true); + createOrUpdateTopic(menu, searchParams, true); + }; } export function followStory(item: any, type: any) { const slugline = get(item, 'slugline'); - saveMyTopic({ + return saveMyTopic({ label: slugline, query: `slugline:"${slugline}"`, topic_type: type, diff --git a/assets/search/components/SearchResultsBar/SearchResultTagsList.tsx b/assets/search/components/SearchResultsBar/SearchResultTagsList.tsx index 9c6b4709f..717e75f0c 100644 --- a/assets/search/components/SearchResultsBar/SearchResultTagsList.tsx +++ b/assets/search/components/SearchResultsBar/SearchResultTagsList.tsx @@ -8,7 +8,7 @@ import {IFilterGroup, INavigation, ISearchFields, ISearchParams, ITopic, IUser} import {SearchResultTagList} from './SearchResultTagList'; import {gettext} from 'utils'; import {getTopicUrl} from 'search/utils'; -import {IDateFilters} from 'interfaces/common'; +import {IDateFilter} from 'interfaces/common'; export interface IProps { user: IUser; @@ -35,7 +35,7 @@ export interface IProps { saveMyTopic?: (params: ISearchParams) => void; deselectMyTopic?: (topicId: ITopic['_id']) => void; - dateFilters?: IDateFilters; + dateFilters?: IDateFilter; } export function SearchResultTagsList({ diff --git a/assets/search/components/SearchResultsBar/SearchResultsFiltersRow.tsx b/assets/search/components/SearchResultsBar/SearchResultsFiltersRow.tsx index 9cafe7edb..c7138ac6f 100644 --- a/assets/search/components/SearchResultsBar/SearchResultsFiltersRow.tsx +++ b/assets/search/components/SearchResultsBar/SearchResultsFiltersRow.tsx @@ -163,6 +163,7 @@ function SearchResultsFiltersRow(props: IPropsAgendaExtended) { ...searchParams.created, from: null, to: null, + date_filter:null, }); }} /> diff --git a/assets/search/components/SearchResultsBar/index.tsx b/assets/search/components/SearchResultsBar/index.tsx index 59e782dd5..fc8114fa8 100644 --- a/assets/search/components/SearchResultsBar/index.tsx +++ b/assets/search/components/SearchResultsBar/index.tsx @@ -4,7 +4,7 @@ import {connect} from 'react-redux'; import {IAgendaState, IFilterGroup, INavigation, ISearchFields, ISearchParams, ITopic, IUser, ISearchSortValue} from 'interfaces'; -import {gettext} from 'utils'; +import {COLLAPSED_SEARCH_BY_DEFAULT, gettext} from 'utils'; import {searchParamsSelector, navigationsByIdSelector, filterGroupsByIdSelector} from '../../selectors'; import {getAdvancedSearchFields} from '../../utils'; import { @@ -17,12 +17,19 @@ import { clearAdvancedSearchParams, resetFilter, deselectMyTopic, + saveMyTopic, } from '../../actions'; import {Dropdown} from './../../../components/Dropdown'; import {SearchResultTagsList} from './SearchResultTagsList'; -import {IDateFilters} from 'interfaces/common'; +import {IDateFilter} from 'interfaces/common'; +import {ToolTip} from 'ui/components/ToolTip'; + +interface ISortOption { + label: string; + value: ISearchSortValue; +} interface IReduxStoreProps { user: IUser; @@ -44,16 +51,20 @@ interface IDispatchProps { resetFilter(): void; } -function getSortValueLabel(sortValue: ISearchSortValue): string { - switch (sortValue) { - case 'versioncreated:desc': - return gettext('Newest first'); - case 'versioncreated:asc': - return gettext('Oldest first'); - case '_score': - return gettext('Relevance'); - } -} +const defaultSortOptions: ISortOption[] = [ + { + value: '', // versioncreated:desc is default + label: gettext('Newest first'), + }, + { + value: 'versioncreated:asc', + label: gettext('Oldest first'), + }, + { + value: '_score', + label: gettext('Relevance'), + }, +]; interface IOwnProps { initiallyOpen?: boolean; @@ -66,20 +77,19 @@ interface IOwnProps { activeTopic: ITopic; topicType: ITopic['topic_type']; saveMyTopic?: (params: ISearchParams) => void; - defaultSortValue?: ISearchSortValue; + sortOptions?: ISortOption[]; refresh(): void; onClearAll?(): void; setQuery(query: string): void; setSortQuery(query: ISearchSortValue): void; - dateFilters?: IDateFilters + dateFilters?: IDateFilter } type IProps = IReduxStoreProps & IDispatchProps & IOwnProps; interface IState { isTagSectionShown: boolean; - sortValue: ISearchSortValue; } @@ -96,9 +106,9 @@ class SearchResultsBarComponent extends React.Component { super(props); this.topicNotNull = new URLSearchParams(window.location.search).get('topic') != null; + this.state = { - isTagSectionShown: this.props.initiallyOpen || this.topicNotNull, - sortValue: this.props.defaultSortValue ?? 'versioncreated:desc', + isTagSectionShown: COLLAPSED_SEARCH_BY_DEFAULT === true ? false : (this.props.initiallyOpen || this.topicNotNull), }; this.toggleTagSection = this.toggleTagSection.bind(this); @@ -173,11 +183,9 @@ class SearchResultsBarComponent extends React.Component { render() { const {isTagSectionShown} = this.state; const numberFormatter = (new Intl.NumberFormat(undefined, {style: 'decimal'})); - const sortValues: Array = [ - 'versioncreated:desc', - 'versioncreated:asc', - '_score', - ]; + + const sortOptions = this.props.sortOptions || defaultSortOptions; + const selectedSortOption = sortOptions.find((option) => option.value === (this.props.searchParams.sortQuery || '')); return ( @@ -201,21 +209,20 @@ class SearchResultsBarComponent extends React.Component { {this.props.showSortDropdown !== true ? null : ( - {sortValues.map((sortValue) => ( + {sortOptions.map((sortOption) => ( ))} @@ -230,19 +237,26 @@ class SearchResultsBarComponent extends React.Component { > {gettext('Clear All')} - + data-test-id="toggle-search-bar" + onClick={this.toggleTagSection} + className="icon-button icon-button--tertiary icon-button--bordered" + > + + +
)} @@ -297,6 +311,7 @@ const mapDispatchToProps = { clearAdvancedSearchParams, deselectMyTopic, resetFilter, + saveMyTopic, }; export const SearchResultsBar = connect< diff --git a/assets/search/utils.ts b/assets/search/utils.ts index 28c7efc7c..268de96fb 100644 --- a/assets/search/utils.ts +++ b/assets/search/utils.ts @@ -1,5 +1,5 @@ -import {ITopic} from 'interfaces'; -import {get} from 'lodash'; +import {ISearchParams, ITopic} from 'interfaces'; +import {get, isEmpty} from 'lodash'; import {getConfig} from 'utils'; export const noNavigationSelected = (activeNavigation: any) => ( @@ -58,3 +58,14 @@ export function getAdvancedSearchFields(context: any) { export function getTopicUrl(topic: ITopic) { return `/${topic.topic_type}?topic=${topic._id}`; } + +export function searchParamsUpdated(searchParams: ISearchParams): boolean { + return ( + searchParams.query != null || + searchParams.navigation != null && searchParams.navigation.length > 0 || + searchParams.product != null || + Object.values(searchParams.advanced || {}).some((val) => !isEmpty(val)) || + Object.values(searchParams.created || {}).some((val) => !isEmpty(val)) || + Object.values(searchParams.filter || {}).some((val) => !isEmpty(val)) + ); +} \ No newline at end of file diff --git a/assets/section-filters/index.ts b/assets/section-filters/index.ts index 0e76597a6..92a978af0 100644 --- a/assets/section-filters/index.ts +++ b/assets/section-filters/index.ts @@ -3,6 +3,7 @@ import {render} from 'render-utils'; import sectionFiltersReducer from './reducers'; import SectionFiltersApp from './components/SectionFiltersApp'; import {initViewData} from './actions'; +import 'user-profile'; const store = createStore(sectionFiltersReducer, 'SectionFilters'); diff --git a/assets/styles/app.scss b/assets/styles/app.scss index d4270979d..1b06e356c 100644 --- a/assets/styles/app.scss +++ b/assets/styles/app.scss @@ -1,4 +1,5 @@ @import './breakpoints.scss'; +@import './primitives.scss'; @import './color-functions.scss'; @import './custom.scss'; // Bootstrap customized @import './index.scss'; @@ -24,4 +25,5 @@ @import './vendor'; @import './tips-content'; @import './time-picker'; +@import './content-divider'; @import '../components/pagination/sd-pagination'; diff --git a/assets/styles/article-list.scss b/assets/styles/article-list.scss index 6055c74bb..30ccc9d7e 100644 --- a/assets/styles/article-list.scss +++ b/assets/styles/article-list.scss @@ -678,7 +678,7 @@ } @container contentListItem (max-width: 399px) { - &.wire-articles__item--wire { + .wire-articles__item--wire { flex-direction: column; padding: var(--content-list-item-padding--mobile) !important; row-gap: var(--space--1); diff --git a/assets/styles/content-divider.scss b/assets/styles/content-divider.scss new file mode 100644 index 000000000..92e48d5de --- /dev/null +++ b/assets/styles/content-divider.scss @@ -0,0 +1,219 @@ +$sd-base-increment: 8px; +.sd-content-divider { + box-sizing: border-box; + margin: 0; + padding: 0; + color: var(--color-text); + list-style: none; + border-block-end: 0; + border-inline-start: 0; + border-inline-end: 0; + border-block-start: 1px var(--color-line--light); + border-style: solid; + + &.sd-content-divider--with-text { + display: flex; + color: var(--color-text); + font-weight: 500; + line-height: 1; + white-space: nowrap; + text-align: center; + border-block-start: 0; + border-block-end: 0; + border-inline-start: 0; + border-inline-end: 0; + border-top-color: var(--color-line--light); + border-style: solid; + &.sd-content-divider--text-small { + font-size: var(--text-size--small); + color: var(--color-text--muted); + text-transform: uppercase; + } + &.sd-content-divider--text-medium { + font-size: var(--text-size--medium); + } + &.sd-content-divider--text-large { + font-size: var(--text-size--large); + } + } + + &.sd-content-divider--horizontal { + display: flex; + clear: both; + width: 100%; + min-width: 100%; + + &.sd-content-divider--margin-x-small { + margin: $sd-base-increment * 1 0; + } + + &.sd-content-divider--margin-small { + margin: $sd-base-increment * 2 0; + } + + &.sd-content-divider--margin-medium { + margin: $sd-base-increment * 3 0; + } + + &.sd-content-divider--margin-large { + margin: $sd-base-increment * 4 0; + } + + &.sd-content-divider--margin-none { + margin: 0 0; + } + } + + &.sd-content-divider--horizontal.sd-content-divider--with-text { + margin: 1.6rem 0; + + &::before, &::after { + content: ""; + position: relative; + width: 50%; + border-block-start: 1px transparent; + border-top-color: transparent; + border-top-color: inherit; + border-block-end: 0; + border-inline-start: 0; + border-inline-end: 0; + transform: translateY(50%); + border-style: inherit; + } + + &.sd-content-divider--text-left { + &::before { + width: 5%; + } + &::after { + width: 95%; + } + } + + &.sd-content-divider--text-right { + &::before { + width: 95%; + } + &::after { + width: 5%; + } + } + + &.sd-content-divider--margin-x-small { + margin: $sd-base-increment * 1 0; + } + + &.sd-content-divider--margin-small { + margin: $sd-base-increment * 2 0; + } + + &.sd-content-divider--margin-medium { + margin: $sd-base-increment * 3 0; + } + + &.sd-content-divider--margin-large { + margin: $sd-base-increment * 4 0; + } + + &.sd-content-divider--margin-none { + margin: $sd-base-increment / 2 0; + } + } + + &.sd-content-divider--vertical { + position: relative; + display: inline-block; + //margin: $sd-base-increment * 0.5 $sd-base-increment * 1.5; + vertical-align: middle; + border-block-start: 0; + border-inline-start: 1px var(--color-line--light); + border-style: solid; + flex-grow: 0; + align-self: stretch; + min-height: $sd-base-increment * 2; + + &.sd-content-divider--with-text { + margin: 0 $sd-base-increment * 1.5; + display: flex; + flex-direction: column; + justify-content: center; + align-items: center; + border-inline-start: 0; + + .sd-content-divider__inner-text { + padding: $sd-base-increment * 0.5 $sd-base-increment * 2; + } + + &::before, &::after { + content: ""; + background-color: transparent; + position: relative; + height: 50%; + border-inline-start: 1px var(--color-line--light); + border-inline-end: 0; + border-block-end: 0; + border-block-start: 0; + width: 1px; + border-style: solid; + } + + &.sd-content-divider--dashed { + &::before, &::after { + border-left-style: dashed; + } + + } + + &.sd-content-divider--dotted { + &::before, &::after { + border-style: dotted; + border-color: var(--color-line--medium); + } + } + } + + &.sd-content-divider--margin-x-small { + margin: 0 $sd-base-increment * 1; + } + + &.sd-content-divider--margin-small { + margin: 0 $sd-base-increment * 2; + } + + &.sd-content-divider--margin-medium { + margin: 0 $sd-base-increment * 3; + } + + &.sd-content-divider--margin-large { + margin: 0 $sd-base-increment * 4; + } + + &.sd-content-divider--margin-none { + margin: 0 0; + } + } + + &.sd-content-divider--dashed { + border-style: dashed; + } + + &.sd-content-divider--dotted { + border-style: dotted; + border-color: var(--color-line--medium); + } + + &.sd-content-divider--no-border { + border: 0 !important; + + &::before, &::after { + border: 0 !important; + } + } +} + +.sd-content-divider__inner-text { + font-size: inherit; + color: inherit; + display: inline-block; + padding: 0 $sd-base-increment; +} diff --git a/assets/styles/custom-breadcrumb.scss b/assets/styles/custom-breadcrumb.scss index 139994c54..0d366d538 100644 --- a/assets/styles/custom-breadcrumb.scss +++ b/assets/styles/custom-breadcrumb.scss @@ -13,6 +13,9 @@ @include xxsm { display: none; } + @include phone { + font-size: var(--text-size--small); + } } a.breadcrumb-item { diff --git a/assets/styles/custom-card.scss b/assets/styles/custom-card.scss index 0abfb8c00..60fe28239 100644 --- a/assets/styles/custom-card.scss +++ b/assets/styles/custom-card.scss @@ -266,3 +266,28 @@ text-decoration: none; } } + + +.login-box { + display: flex; + flex-direction: column; + padding: var(--space--4); + box-shadow: 0 1px 6px hsla(0, 0%, 0%, 0.16), 0 3px 8px hsla(0, 0%, 0%, 0.24), 0 0 1px hsla(0, 0%, 0%, 0.1); + border-radius: var(--border-radius--l); + width: 100%; + max-width: 560px; + background-color: var(--color-neutral-000); + .login-box__title { + font-size: var(--text-size--x-large); + text-align: center; + margin-block-end: var(--space--2); + } + @include phone { + box-shadow: none; + border-radius: 0; + padding: var(--space--2); + .login-box__title { + margin-block-end: var(--space--1); + } + } +} \ No newline at end of file diff --git a/assets/styles/custom-variables.scss b/assets/styles/custom-variables.scss index 5205d84b8..def31ee16 100644 --- a/assets/styles/custom-variables.scss +++ b/assets/styles/custom-variables.scss @@ -111,11 +111,11 @@ $container-max-widths: ( xxl: 1360px ) !default; - // NEW COLORS //-------------------------- :root { + // NEUTRAL COLORS // Light --color-neutral-000: hsl(0, 2%, 100%); @@ -127,7 +127,6 @@ $container-max-widths: ( --color-neutral-300: hsl(0, 2%, 74%); --color-neutral-400: hsl(0, 2%, 54%); --color-neutral-500: hsl(0, 2%, 40%); - --color-neutral-600: hsl(0, 2%, 34%); // Dark --color-neutral-700: hsl(0, 2%, 20%); @@ -159,7 +158,7 @@ $container-max-widths: ( --color-home-section-heading: var(--color-primary); --color-text--slugline: hsl(201, 62%, 30%); - --color-text-link: var(--color-primary); + //--color-text-link: var(--color-primary); --color-text-link--navbar: var(--color-primary); --color-icon-font: var(--color-text--muted); @@ -226,11 +225,13 @@ $container-max-widths: ( --font-family-core--sans: "Roboto", "Helvetica Neue", Arial, sans-serif; --font-family-core--serif: "Merriweather", serif; --font-size-base: 1rem; // 16px + --text-size--x-small: calc(var(--font-size-base) * 0.625); // 10px --text-size--small: calc(var(--font-size-base) * 0.75); // 12px --text-size--medium: calc(var(--font-size-base) * 0.875); // 14px --text-size--large: var(--font-size-base); // 16px --text-size--x-large: calc(var(--font-size-base) * 1.25); // 20px --text-size--xx-large: calc(var(--font-size-base) * 1.5); // 24px + --text-size--xxx-large: calc(var(--font-size-base) * 2); // 32px --font-family-core--primary: var(--font-family-core--sans); --font-family-core--secondary: var(--font-family-core--serif); @@ -317,7 +318,7 @@ $container-max-widths: ( --top-bar-logo-main-max-width: 240px; --top-bar-logo-main-max-width--mobile: 160px; --top-bar-logo-main-padding-y: 12px; - --top-bar-logo-main-padding-x: 16px; + --top-bar-logo-main-padding-x: 16px 8px; --top-bar-logo-main-margin-top: 0; --top-bar-logo-main-margin-top--mobile: 0; // Logo additional diff --git a/assets/styles/helpers.scss b/assets/styles/helpers.scss index 351e24874..d5dd94a98 100644 --- a/assets/styles/helpers.scss +++ b/assets/styles/helpers.scss @@ -165,3 +165,27 @@ hr.dashed { } +// FONT SIZE +.text-xs { + font-size: var(--text-size--x-small) !important; // 10px; +} +.text-sm { + font-size: var(--text-size--small) !important; // 12px; +} +.text-md { + font-size: var(--text-size--medium) !important; // 14px; +} +.text-lg { + font-size: var(--text-size--large) !important; // 16px; +} +.text-xl { + font-size: var(--text-size--x-large) !important; // 20px; +} +.text-2xl { + font-size: var(--text-size--xx-large) !important; // 24px; +} +.text-3xl { + font-size: var(--text-size--xxx-large) !important; // 32px; +} + + diff --git a/assets/styles/index.scss b/assets/styles/index.scss index eaac6feb2..dd0a4e7a1 100644 --- a/assets/styles/index.scss +++ b/assets/styles/index.scss @@ -97,7 +97,7 @@ $nav-sidebar-border: #323847; label { color: var(--color-text--muted); - font-size: 14px; + font-size: var(--text-size--medium); } label:not(.label--no-spacing) { margin-block-end: 0.3rem; @@ -113,6 +113,7 @@ label:not(.label--no-spacing) { .alertify-notifier { + z-index: 1000000001 !important; .ajs-message { border: none; border-radius: var(--ajs-message-border-radius); @@ -375,7 +376,7 @@ body.print { } @include phone { - padding: 0 2px; + padding: 0 2px; overflow-y: hidden; } @include md { @@ -431,7 +432,7 @@ body.print { line-height: var(--navbar-height--mobile); text-align: center; background-color: var(--sidenav-color-item-bg); - + @include md { width: var(--sidenav-item-width); min-height: var(--navbar-height); @@ -1220,7 +1221,7 @@ article.list { } } &.nh-collapsible-panel--open { - + .nh-collapsible-panel__caret { transform: rotate(90deg); } @@ -2828,7 +2829,7 @@ article.list { width: 12px; position: absolute; inset-block-end: -4px; - inset-inline-end: -4px; + inset-inline-end: -4px; box-shadow: 0 0 0 2px var(--content-list-item-color-bg); background-repeat: no-repeat; background-size: 100%; @@ -4376,7 +4377,20 @@ article.list { } .nh-login-page__content { // login page background color + flex-grow: 1; background-color: var(--login-page-color-bg); + display: grid; + grid-template-rows: 1fr; + grid-template-columns: 1fr; + align-items: center; + justify-items: center; + overflow: auto; + + .card--login { + width: 100%; + max-width: 640px; + } + } .login-logo { padding-inline: var(--login-logo-padding--inline); diff --git a/assets/styles/navbar.scss b/assets/styles/navbar.scss index 007d8cf25..509da2018 100644 --- a/assets/styles/navbar.scss +++ b/assets/styles/navbar.scss @@ -424,6 +424,7 @@ margin-inline: var(--top-bar-nav-brand-margin-x); padding-inline-start: var(--top-bar-nav-brand-padding-x); border-inline-start: 1px var(--top-bar-nav-brand-border-type) var(--top-bar-nav-brand-border-color); + overflow: hidden; @include phone { padding-inline: 0; border-inline-start: none; @@ -431,6 +432,12 @@ .breadcrumb-sub-item { display: none; } + .breadcrumb-item { + font-size: var(--text-size--small) !important; + } + .navbar__title { + font-size: var(--text-size--medium); + } } nav { display: flex; @@ -438,6 +445,15 @@ align-items: center; } } +.navbar__title { + font-size: var(--text-size--large); + color: var(--top-bar-color-fg); + line-height: 1; + opacity: 0.7; + overflow: hidden; + text-overflow: ellipsis; + white-space: nowrap; +} .navbar__right { display: flex; align-items: center; diff --git a/assets/styles/primitives.scss b/assets/styles/primitives.scss new file mode 100644 index 000000000..53c2ab176 --- /dev/null +++ b/assets/styles/primitives.scss @@ -0,0 +1,113 @@ +:root { + // PRIMITIVES / COLORS + // These colors should not to be applied directly to elements and components + --l-base: 35%; + --l-100-add: 48; + --l-200-add: 34; + --l-300-add: 20; + --l-400-add: 8; + --l-600-remove: 10; + --l-700-remove: 20; + --l-800-remove: 35; + + --c-base: 75; + --c-100-remove: 60; + --c-200-remove: 45; + --c-300-remove: 30; + --c-400-remove: 10; + --c-600-add: 0; + --c-700-add: 0; + --c-800-add: 0; + + // Morocco Red + --morocco-red-500: lch(var(--l-base) var(--c-base) 28); + --morocco-red-100: lch(from var(--morocco-red-500) calc(l + var(--l-100-add)) calc(c - var(--c-100-remove)) h); + --morocco-red-200: lch(from var(--morocco-red-500) calc(l + var(--l-200-add)) calc(c - var(--c-200-remove)) h); + --morocco-red-300: lch(from var(--morocco-red-500) calc(l + var(--l-300-add)) calc(c - var(--c-300-remove)) h); + --morocco-red-400: lch(from var(--morocco-red-500) calc(l + var(--l-400-add)) calc(c - var(--c-400-remove)) h); + --morocco-red-600: lch(from var(--morocco-red-500) calc(l - var(--l-600-remove)) calc(c + var(--c-600-add)) h); + --morocco-red-700: lch(from var(--morocco-red-500) calc(l - var(--l-700-remove)) calc(c + var(--c-700-add)) h); + --morocco-red-800: lch(from var(--morocco-red-500) calc(l - var(--l-800-remove)) calc(c + var(--c-800-add)) h); + --morocco-red-500-a32: lch(from var(--morocco-red-500) l c h / .32); + --morocco-red-500-a24: lch(from var(--morocco-red-500) l c h / .24); + --morocco-red-500-a16: lch(from var(--morocco-red-500) l c h / .16); + --morocco-red-500-a12: lch(from var(--morocco-red-500) l c h / .12); + // Syrah Soil + --syrah-soil-500: lch(var(--l-base) var(--c-base) 66); + --syrah-soil-100: lch(from var(--syrah-soil-500) calc(l + var(--l-100-add)) calc(c - var(--c-100-remove)) h); + --syrah-soil-200: lch(from var(--syrah-soil-500) calc(l + var(--l-200-add)) calc(c - var(--c-200-remove)) h); + --syrah-soil-300: lch(from var(--syrah-soil-500) calc(l + var(--l-300-add)) calc(c - var(--c-300-remove)) h); + --syrah-soil-400: lch(from var(--syrah-soil-500) calc(l + var(--l-400-add)) calc(c - var(--c-400-remove)) h); + --syrah-soil-600: lch(from var(--syrah-soil-500) calc(l - var(--l-600-remove)) calc(c + var(--c-600-add)) h); + --syrah-soil-700: lch(from var(--syrah-soil-500) calc(l - var(--l-700-remove)) calc(c + var(--c-700-add)) h); + --syrah-soil-800: lch(from var(--syrah-soil-500) calc(l - var(--l-800-remove)) calc(c + var(--c-800-add)) h); + --syrah-soil-500-a32: hsl(from var(--syrah-soil-500) h s l / .32); + --syrah-soil-500-a24: hsl(from var(--syrah-soil-500) h s l / .24); + --syrah-soil-500-a16: hsl(from var(--syrah-soil-500) h s l / .16); + --syrah-soil-500-a12: hsl(from var(--syrah-soil-500) h s l / .12); + // Green Brier + --green-brier-500: lch(var(--l-base) var(--c-base) 126); + --green-brier-100: lch(from var(--green-brier-500) calc(l + var(--l-100-add)) calc(c - var(--c-100-remove)) h); + --green-brier-200: lch(from var(--green-brier-500) calc(l + var(--l-200-add)) calc(c - var(--c-200-remove)) h); + --green-brier-300: lch(from var(--green-brier-500) calc(l + var(--l-300-add)) calc(c - var(--c-300-remove)) h); + --green-brier-400: lch(from var(--green-brier-500) calc(l + var(--l-400-add)) calc(c - var(--c-400-remove)) h); + --green-brier-600: lch(from var(--green-brier-500) calc(l - var(--l-600-remove)) calc(c + var(--c-600-add)) h); + --green-brier-700: lch(from var(--green-brier-500) calc(l - var(--l-700-remove)) calc(c + var(--c-700-add)) h); + --green-brier-800: lch(from var(--green-brier-500) calc(l - var(--l-800-remove)) calc(c + var(--c-800-add)) h); + --green-brier-500-a32: hsl(from var(--green-brier-500) h s l / .32); + --green-brier-500-a24: hsl(from var(--green-brier-500) h s l / .24); + --green-brier-500-a16: hsl(from var(--green-brier-500) h s l / .16); + --green-brier-500-a12: hsl(from var(--green-brier-500) h s l / .12); + // Green Brier + --sorcerer-blue-500: lch(var(--l-base) var(--c-base) 202); + --sorcerer-blue-100: lch(from var(--sorcerer-blue-500) calc(l + var(--l-100-add)) calc(c - var(--c-100-remove)) h); + --sorcerer-blue-200: lch(from var(--sorcerer-blue-500) calc(l + var(--l-200-add)) calc(c - var(--c-200-remove)) h); + --sorcerer-blue-300: lch(from var(--sorcerer-blue-500) calc(l + var(--l-300-add)) calc(c - var(--c-300-remove)) h); + --sorcerer-blue-400: lch(from var(--sorcerer-blue-500) calc(l + var(--l-400-add)) calc(c - var(--c-400-remove)) h); + --sorcerer-blue-600: lch(from var(--sorcerer-blue-500) calc(l - var(--l-600-remove)) calc(c + var(--c-600-add)) h); + --sorcerer-blue-700: lch(from var(--sorcerer-blue-500) calc(l - var(--l-700-remove)) calc(c + var(--c-700-add)) h); + --sorcerer-blue-800: lch(from var(--sorcerer-blue-500) calc(l - var(--l-800-remove)) calc(c + var(--c-800-add)) h); + --sorcerer-blue-500-a32: hsl(from var(--sorcerer-blue-500) h s l / .32); + --sorcerer-blue-500-a24: hsl(from var(--sorcerer-blue-500) h s l / .24); + --sorcerer-blue-500-a16: hsl(from var(--sorcerer-blue-500) h s l / .16); + --sorcerer-blue-500-a12: hsl(from var(--sorcerer-blue-500) h s l / .12); + // Palatinate Blue + --palatinate-blue-500: lch(var(--l-base) var(--c-base) 280); + --palatinate-blue-100: lch(from var(--palatinate-blue-500) calc(l + var(--l-100-add)) calc(c - var(--c-100-remove)) h); + --palatinate-blue-200: lch(from var(--palatinate-blue-500) calc(l + var(--l-200-add)) calc(c - var(--c-200-remove)) h); + --palatinate-blue-300: lch(from var(--palatinate-blue-500) calc(l + var(--l-300-add)) calc(c - var(--c-300-remove)) h); + --palatinate-blue-400: lch(from var(--palatinate-blue-500) calc(l + var(--l-400-add)) calc(c - var(--c-400-remove)) h); + --palatinate-blue-600: lch(from var(--palatinate-blue-500) calc(l - var(--l-600-remove)) calc(c + var(--c-600-add)) h); + --palatinate-blue-700: lch(from var(--palatinate-blue-500) calc(l - var(--l-700-remove)) calc(c + var(--c-700-add)) h); + --palatinate-blue-800: lch(from var(--palatinate-blue-500) calc(l - var(--l-800-remove)) calc(c + var(--c-800-add)) h); + --palatinate-blue-500-a32: hsl(from var(--palatinate-blue-500) h s l / .32); + --palatinate-blue-500-a24: hsl(from var(--palatinate-blue-500) h s l / .24); + --palatinate-blue-500-a16: hsl(from var(--palatinate-blue-500) h s l / .16); + --palatinate-blue-500-a12: hsl(from var(--palatinate-blue-500) h s l / .12); + // Purple Spot + --purple-spot-500: lch(var(--l-base) var(--c-base) 307); + --purple-spot-100: lch(from var(--purple-spot-500) calc(l + var(--l-100-add)) calc(c - var(--c-100-remove)) h); + --purple-spot-200: lch(from var(--purple-spot-500) calc(l + var(--l-200-add)) calc(c - var(--c-200-remove)) h); + --purple-spot-300: lch(from var(--purple-spot-500) calc(l + var(--l-300-add)) calc(c - var(--c-300-remove)) h); + --purple-spot-400: lch(from var(--purple-spot-500) calc(l + var(--l-400-add)) calc(c - var(--c-400-remove)) h); + --purple-spot-600: lch(from var(--purple-spot-500) calc(l - var(--l-600-remove)) calc(c + var(--c-600-add)) h); + --purple-spot-700: lch(from var(--purple-spot-500) calc(l - var(--l-700-remove)) calc(c + var(--c-700-add)) h); + --purple-spot-800: lch(from var(--purple-spot-500) calc(l - var(--l-800-remove)) calc(c + var(--c-800-add)) h); + --purple-spot-500-a32: hsl(from var(--purple-spot-500) h s l / .32); + --purple-spot-500-a24: hsl(from var(--purple-spot-500) h s l / .24); + --purple-spot-500-a16: hsl(from var(--purple-spot-500) h s l / .16); + --purple-spot-500-a12: hsl(from var(--purple-spot-500) h s l / .12); + // Blissful Berry + --blissful-berry-500: lch(var(--l-base) var(--c-base) 338); + --blissful-berry-100: lch(from var(--blissful-berry-500) calc(l + var(--l-100-add)) calc(c - var(--c-100-remove)) h); + --blissful-berry-200: lch(from var(--blissful-berry-500) calc(l + var(--l-200-add)) calc(c - var(--c-200-remove)) h); + --blissful-berry-300: lch(from var(--blissful-berry-500) calc(l + var(--l-300-add)) calc(c - var(--c-300-remove)) h); + --blissful-berry-400: lch(from var(--blissful-berry-500) calc(l + var(--l-400-add)) calc(c - var(--c-400-remove)) h); + --blissful-berry-600: lch(from var(--blissful-berry-500) calc(l - var(--l-600-remove)) calc(c + var(--c-600-add)) h); + --blissful-berry-700: lch(from var(--blissful-berry-500) calc(l - var(--l-700-remove)) calc(c + var(--c-700-add)) h); + --blissful-berry-800: lch(from var(--blissful-berry-500) calc(l - var(--l-800-remove)) calc(c + var(--c-800-add)) h); + --blissful-berry-500-a32: hsl(from var(--blissful-berry-500) h s l / .32); + --blissful-berry-500-a24: hsl(from var(--blissful-berry-500) h s l / .24); + --blissful-berry-500-a16: hsl(from var(--blissful-berry-500) h s l / .16); + --blissful-berry-500-a12: hsl(from var(--blissful-berry-500) h s l / .12); +} \ No newline at end of file diff --git a/assets/styles/user-profile.scss b/assets/styles/user-profile.scss index a692a5fbe..8c24022cd 100644 --- a/assets/styles/user-profile.scss +++ b/assets/styles/user-profile.scss @@ -84,7 +84,7 @@ .profile__info { color: var(--profile-sidebar-color-fg); opacity: 0.7; - font-size: var(--text-size--small); // 13px + font-size: var(--text-size--small); // 12px } .profile__characters { diff --git a/assets/tests/TextInput.spec.tsx b/assets/tests/TextInput.spec.tsx index 62b7d1864..b7378ae49 100644 --- a/assets/tests/TextInput.spec.tsx +++ b/assets/tests/TextInput.spec.tsx @@ -1,4 +1,3 @@ -import expect from 'expect'; import React from 'react'; import {shallow} from 'enzyme'; import TextInput from '../components/TextInput'; diff --git a/assets/ui/components/ContentDivider.tsx b/assets/ui/components/ContentDivider.tsx new file mode 100644 index 000000000..78d769f2a --- /dev/null +++ b/assets/ui/components/ContentDivider.tsx @@ -0,0 +1,40 @@ +import React from 'react'; +import classNames from 'classnames'; + +interface IProps { + type?: 'dashed' |'dotted' | 'solid'; // defaults to 'solid' + orientation?: 'horizontal' |'vertical'; // defaults to 'horizontal' + align?: 'center' | 'left' | 'right'; // defaults to 'center' + margin?: 'x-small' | 'small' |'medium' | 'large' | 'none'; + textSize?: 'small' |'medium' | 'large'; + className?: string; + children?: React.ReactNode; +} + +export class ContentDivider extends React.Component { + + render() { + const classes = classNames('sd-content-divider', { + 'sd-content-divider--horizontal': this.props.orientation === undefined, + [`sd-content-divider--${this.props.type}`]: this.props.type || this.props.type !== undefined, + [`sd-content-divider--text-${this.props.align}`]: this.props.align || this.props.align !== undefined, + [`sd-content-divider--${this.props.orientation}`]: + this.props.orientation || this.props.orientation !== undefined, + 'sd-content-divider--margin-small': this.props.margin === undefined, + [`sd-content-divider--margin-${this.props.margin}`]: this.props.margin || this.props.margin !== undefined, + + 'sd-content-divider--text-large': this.props.textSize === undefined, + [`sd-content-divider--text-${this.props.textSize}`]: this.props.textSize || this.props.textSize !== undefined, + }, this.props.className); + + if (this.props.children) { + return ( +
+ {this.props.children} +
+ ); + } else { + return
; + } + } +} \ No newline at end of file diff --git a/assets/user-profile/components/ProfileToggle.tsx b/assets/user-profile/components/ProfileToggle.tsx index b6869ae07..12bc19555 100644 --- a/assets/user-profile/components/ProfileToggle.tsx +++ b/assets/user-profile/components/ProfileToggle.tsx @@ -15,7 +15,7 @@ class ProfileToggle extends React.Component { } componentDidMount() { - if (!isTouchDevice() && this.elem) { + if (!isTouchDevice() && this.elem && !this.tooltip) { this.tooltip = new Tooltip(this.elem); } } diff --git a/assets/user-profile/components/profile/UserProfile.tsx b/assets/user-profile/components/profile/UserProfile.tsx index ba217b5e7..01326dced 100644 --- a/assets/user-profile/components/profile/UserProfile.tsx +++ b/assets/user-profile/components/profile/UserProfile.tsx @@ -2,7 +2,7 @@ import React from 'react'; import {connect} from 'react-redux'; import {IUser} from 'interfaces'; -import {gettext, getSubscriptionTimesString, formatDate} from 'utils'; +import {gettext, getSubscriptionTimesString, formatDate, parseISODate, notificationsArePaused, notificationsWillBePaused} from 'utils'; import TextInput from 'components/TextInput'; import SelectInput from 'components/SelectInput'; @@ -73,6 +73,8 @@ class UserProfile extends React.PureComponent { const {user, onChange, errors, authProviderFeatures} = this.props; const onCancel = () => this.props.fetchUser(this.props.user._id); const localeOptions = getLocaleInputOptions(); + const pausedFrom = parseISODate(this.props.user.notification_schedule?.pause_from); + const pausedTo = parseISODate(this.props.user.notification_schedule?.pause_to); return (
@@ -187,12 +189,12 @@ class UserProfile extends React.PureComponent {
- {this.props.user.notification_schedule && this.props.user.notification_schedule.pause_from != '' && this.props.user.notification_schedule.pause_to != '' - ? ( + {(notificationsArePaused(pausedFrom, pausedTo) || notificationsWillBePaused(pausedFrom, pausedTo)) ? + (
- {gettext('All notifications will be paused from {{dateFrom}} to {{dateTo}}', {dateFrom: formatDate(this.props.user.notification_schedule.pause_from), dateTo: formatDate(this.props.user.notification_schedule.pause_to)})} + {gettext('All notifications will be paused from {{dateFrom}} to {{dateTo}}', {dateFrom: formatDate(pausedFrom, true), dateTo: formatDate(pausedTo, true)})}
+ +
+ + + +
+ Or +
+ +
+

Note! If your company credentials don't work for login, your company might not have an integration done to this system. Please contact your company IT.

+
+ +
+
+ + Don't have an account? + Sign up + +
+ {/* Don't have an account? + Sign up */} +
+ + +
+
+
+ +
+ +
+
+
+
+
+
+ ); + } + + export default Login; + \ No newline at end of file diff --git a/dev-requirements.in b/dev-requirements.in index cc477bad7..f5f7ad1a1 100644 --- a/dev-requirements.in +++ b/dev-requirements.in @@ -1,8 +1,8 @@ -r requirements.txt +-r mypy-requirements.txt -r black-requirements.txt -r mypy-requirements.txt -black>=23,<24 flake8 pytest pytest-cov diff --git a/dev-requirements.txt b/dev-requirements.txt index 0b1490adc..1fb8342af 100644 --- a/dev-requirements.txt +++ b/dev-requirements.txt @@ -8,9 +8,9 @@ aiofiles==24.1.0 # via # quart # quart-uploads -aiohappyeyeballs==2.3.6 +aiohappyeyeballs==2.4.0 # via aiohttp -aiohttp==3.10.3 +aiohttp==3.10.5 # via elasticsearch aiosignal==1.3.1 # via aiohttp @@ -26,6 +26,8 @@ arrow==1.3.0 # superdesk-core asgi-tools==1.0.10 # via quart-babel +asgiref==3.8.1 + # via superdesk-core asn1crypto==1.5.1 # via # oscrypto @@ -38,7 +40,7 @@ async-timeout==4.0.3 # redis attrs==24.2.0 # via aiohttp -authlib==1.3.1 +authlib==1.3.2 # via superdesk-core babel==2.16.0 # via quart-babel @@ -46,12 +48,10 @@ bcrypt==4.1.3 # via superdesk-core behave==1.2.6 # via wooper -billiard==4.2.0 +billiard==4.2.1 # via celery black==23.12.1 - # via - # -r black-requirements.txt - # -r dev-requirements.in + # via -r black-requirements.txt blinker==1.8.2 # via # elastic-apm @@ -61,15 +61,15 @@ blinker==1.8.2 # raven # sentry-sdk # superdesk-core -boto3==1.34.162 +boto3==1.35.25 # via superdesk-core -botocore==1.34.162 +botocore==1.35.25 # via # boto3 # s3transfer cachelib==0.9.0 # via flask-caching -cachetools==5.4.0 +cachetools==5.5.0 # via # flask-oidc-ex # google-auth @@ -79,13 +79,13 @@ cerberus==1.3.5 # via # eve # superdesk-core -certifi==2024.7.4 +certifi==2024.8.30 # via # elastic-apm # elasticsearch # requests # sentry-sdk -cffi==1.17.0 +cffi==1.17.1 # via cryptography chardet==5.2.0 # via @@ -115,7 +115,7 @@ coverage[toml]==7.6.1 # via pytest-cov croniter==2.0.7 # via superdesk-core -cryptography==43.0.0 +cryptography==43.0.1 # via # authlib # jwcrypto @@ -123,7 +123,7 @@ cryptography==43.0.0 # pyhanko-certvalidator cssselect2==0.7.0 # via svglib -deepdiff==7.0.1 +deepdiff==8.0.1 # via superdesk-planning dnspython==2.6.1 # via @@ -135,7 +135,7 @@ ecs-logging==2.2.0 # via elastic-apm elastic-apm[flask]==6.23.0 # via superdesk-core -elasticsearch[async]==7.17.9 +elasticsearch[async]==7.17.12 # via # eve-elastic # superdesk-core @@ -210,12 +210,12 @@ hyperframe==6.0.1 # via h2 icalendar==5.0.13 # via superdesk-planning -idna==3.7 +idna==3.10 # via # email-validator # requests # yarl -importlib-metadata==8.2.0 +importlib-metadata==8.5.0 # via quart-babel iniconfig==2.0.0 # via pytest @@ -263,14 +263,14 @@ markupsafe==2.1.5 # wtforms mccabe==0.7.0 # via flake8 -motor==3.5.1 +motor==3.5.3 # via superdesk-core -multidict==6.0.5 +multidict==6.1.0 # via # aiohttp # asgi-tools # yarl -mypy==1.11.1 +mypy==1.11.2 # via -r mypy-requirements.txt mypy-extensions==1.0.0 # via @@ -280,7 +280,7 @@ oauth2client==4.1.3 # via flask-oidc-ex oauthlib==3.2.2 # via requests-oauthlib -ordered-set==4.1.0 +orderly-set==5.2.2 # via deepdiff oscrypto==1.3.0 # via pyhanko-certvalidator @@ -292,7 +292,7 @@ parse==1.20.2 # via # behave # parse-type -parse-type==0.6.2 +parse-type==0.6.3 # via behave pathspec==0.12.1 # via black @@ -301,7 +301,7 @@ pillow==10.3.0 # reportlab # superdesk-core # xhtml2pdf -platformdirs==4.2.2 +platformdirs==4.3.6 # via black pluggy==1.5.0 # via pytest @@ -309,13 +309,13 @@ priority==2.0.0 # via hypercorn prompt-toolkit==3.0.47 # via click-repl -pyasn1==0.6.0 +pyasn1==0.6.1 # via # ldap3 # oauth2client # pyasn1-modules # rsa -pyasn1-modules==0.4.0 +pyasn1-modules==0.4.1 # via # google-auth # oauth2client @@ -323,9 +323,9 @@ pycodestyle==2.12.1 # via flake8 pycparser==2.22 # via cffi -pydantic==2.8.2 +pydantic==2.9.2 # via superdesk-core -pydantic-core==2.20.1 +pydantic-core==2.23.4 # via pydantic pyflakes==3.2.0 # via flake8 @@ -344,23 +344,23 @@ pymongo==4.7.3 # eve # motor # superdesk-core -pyparsing==3.1.2 +pyparsing==3.1.4 # via # httplib2 # pyrtf3 -pypdf==4.3.1 +pypdf==5.0.0 # via xhtml2pdf pypng==0.20220715.0 # via qrcode pyrtf3==0.47.5 # via -r requirements.txt -pytest==8.3.2 +pytest==8.3.3 # via # -r dev-requirements.in # pytest-asyncio # pytest-cov # pytest-mock -pytest-asyncio==0.23.8 +pytest-asyncio==0.24.0 # via -r dev-requirements.in pytest-cov==5.0.0 # via -r dev-requirements.in @@ -386,7 +386,7 @@ python-twitter==3.5 # via superdesk-core python3-saml==1.16.0 # via -r requirements.txt -pytz==2024.1 +pytz==2024.2 # via # croniter # eve-elastic @@ -416,9 +416,9 @@ quart-flask-patch==0.3.0 # via superdesk-core quart-rate-limiter==0.10.0 # via -r requirements.txt -quart-uploads==0.0.2 +quart-uploads==0.0.3 # via quart-wtforms -quart-wtforms==1.0.1 +quart-wtforms==1.0.2 # via -r requirements.txt raven[flask]==6.10.0 # via superdesk-core @@ -453,7 +453,7 @@ rsa==4.9 # oauth2client s3transfer==0.10.2 # via boto3 -sentry-sdk[quart]==2.6.0 +sentry-sdk[quart]==2.14.0 # via -r requirements.txt sgmllib3k==1.0.0 # via feedparser @@ -490,6 +490,8 @@ tomli==2.0.1 # hypercorn # mypy # pytest +types-aiofiles==24.1.0.20240626 + # via quart-uploads types-click==7.1.8 # via types-flask types-flask==1.1.6 @@ -500,13 +502,13 @@ types-jinja2==2.11.9 # types-flask types-markupsafe==1.1.10 # via types-jinja2 -types-protobuf==5.27.0.20240626 +types-protobuf==5.28.0.20240924 # via -r mypy-requirements.txt -types-python-dateutil==2.9.0.20240316 +types-python-dateutil==2.9.0.20240906 # via # -r mypy-requirements.txt # arrow -types-pytz==2024.1.0.20240417 +types-pytz==2024.2.0.20240913 # via # -r mypy-requirements.txt # types-tzlocal @@ -523,16 +525,18 @@ types-werkzeug==1.0.9 typing-extensions==4.12.2 # via # -r mypy-requirements.txt + # asgiref # black # hypercorn # jwcrypto + # multidict # mypy # pydantic # pydantic-core # pypdf # qrcode # superdesk-core -tzdata==2024.1 +tzdata==2024.2 # via celery tzlocal==5.2 # via @@ -542,7 +546,7 @@ unidecode==1.3.8 # via superdesk-core uritools==4.0.3 # via pyhanko-certvalidator -urllib3==1.26.19 +urllib3==1.26.20 # via # botocore # elastic-apm @@ -565,7 +569,7 @@ webencodings==0.5.1 # tinycss2 websockets==12.0 # via superdesk-core -werkzeug==3.0.3 +werkzeug==3.0.4 # via # flask # quart @@ -586,11 +590,11 @@ xmlsec==1.3.14 # via # python3-saml # superdesk-core -yarl==1.9.4 +yarl==1.12.1 # via # aiohttp # asgi-tools -zipp==3.20.0 +zipp==3.20.2 # via importlib-metadata # The following packages are considered to be unsafe in a requirements file: diff --git a/e2e/cypress/e2e/wire/wire_action.cy.js b/e2e/cypress/e2e/wire/wire_action.cy.js index 1e9cce1c9..408a84262 100644 --- a/e2e/cypress/e2e/wire/wire_action.cy.js +++ b/e2e/cypress/e2e/wire/wire_action.cy.js @@ -27,6 +27,8 @@ describe('wire - filters', () => { }); it('can search using navigation & filters', () => { + cy.get('[data-test-id="search-results-bar"]').should('not.exist'); + /** SEARCH */ @@ -63,10 +65,8 @@ describe('wire - filters', () => { FilterPanel.button('clear'); - /** - check filtering by date input - */ - cy.get('[data-test-id="date-filter-select"]').should('have.value', 'last_30_days'); + // check filtering by date input + cy.get('[data-test-id="date-filter-select"]').should('have.value', ''); WirePage.item(WireItems.item_1._id).should('exist'); WirePage.item(WireItems.item_2._id).should('exist'); @@ -81,5 +81,10 @@ describe('wire - filters', () => { // Add assertions to verify the items displayed based on the custom date range WirePage.item(WireItems.item_3._id).should('exist'); + + cy.get('[data-test-id="date-filter-select"]').select(''); + FilterPanel.button('search'); + + cy.get('[data-test-id="search-results-bar"]').should('not.exist'); }); }); diff --git a/e2e/docker-compose.yml b/e2e/docker-compose.yml index f0d2c4cc5..a703a7765 100644 --- a/e2e/docker-compose.yml +++ b/e2e/docker-compose.yml @@ -1,8 +1,10 @@ version: "3.8" +name: newsroom-core-e2e + services: elastic: - image: docker.elastic.co/elasticsearch/elasticsearch-oss:7.10.2 + image: docker.elastic.co/elasticsearch/elasticsearch:7.17.22 environment: - discovery.type=single-node networks: @@ -32,7 +34,7 @@ services: networks: - newsroom volumes: - - ./dist:/opt/client-dist + - ./dist:/opt/client-dist:ro depends_on: - redis - mongo diff --git a/e2e/package.json b/e2e/package.json index a4346db33..922a2ff8b 100644 --- a/e2e/package.json +++ b/e2e/package.json @@ -5,19 +5,18 @@ "newsroom-core": "file:../" }, "devDependencies": { - "babel-plugin-transform-object-rest-spread": "^6.26.0", - "babel-preset-es2015": "^6.24.1", - "babel-preset-react": "^6.24.1", "cypress": "^12.17.1", "http-server": "^14.1.1", - "webpack": "3.11.0", - "webpack-dev-server": "2.11.1" + "webpack": "~4.47.0", + "webpack-cli": "~4.10.0", + "webpack-dev-server": "~4.15.2", + "webpack-manifest-plugin": "~4.1.1" }, "scripts": { "cypress-ui": "cypress open", "cypress-ci": "cypress run", - "build": "export NODE_ENV=production && webpack --progress --profile --colors", - "start": "webpack-dev-server --progress --colors --content-base dist --host 0.0.0.0", + "build": "webpack --mode production --progress", + "start": "webpack-dev-server --mode development --progress --host 0.0.0.0", "start-client-server": "http-server dist -p 8080 -s" }, "volta": { diff --git a/e2e/server/Dockerfile b/e2e/server/Dockerfile index f9a39ad52..9e8149783 100644 --- a/e2e/server/Dockerfile +++ b/e2e/server/Dockerfile @@ -27,12 +27,11 @@ RUN python3 -m pip install -U pip wheel setuptools # install requirements WORKDIR /opt/newsroom/ -COPY requirements.txt . -RUN python3 -m pip install -Ur requirements.txt +COPY requirements.txt setup.py /opt/newsroom/ +RUN python3 -m pip install -Ue . -# install newsroom app +# copy app COPY . . -RUN python3 -m pip install -e . # setup entrypoint WORKDIR /opt/newsroom/e2e/server/ diff --git a/e2e/webpack.config.js b/e2e/webpack.config.js index 2109d7df0..c88e76e03 100644 --- a/e2e/webpack.config.js +++ b/e2e/webpack.config.js @@ -1,8 +1,3 @@ const config = require('../webpack.config'); -config.devServer = { - compress: true, - disableHostCheck: true, -}; - module.exports = config; diff --git a/karma.conf.js b/karma.conf.js index 0bd9b69c7..5c12127a0 100644 --- a/karma.conf.js +++ b/karma.conf.js @@ -19,8 +19,8 @@ module.exports = function(config) { webpack: { module: webpackConfig.module, resolve: webpackConfig.resolve, - plugins: webpackConfig.plugins.filter((plugin) => plugin instanceof webpack.ProvidePlugin), devtool: 'inline-source-map', + mode: 'development', }, webpackMiddleware: { @@ -28,7 +28,7 @@ module.exports = function(config) { }, reporters: ['dots'], - frameworks: ['jasmine'], + frameworks: ['jasmine', 'webpack'], browsers: ['ChromeHeadless'], // Allow typescript files diff --git a/messages.pot b/messages.pot index 08a181a5d..582c9da28 100644 --- a/messages.pot +++ b/messages.pot @@ -7,9 +7,9 @@ #, fuzzy msgid "" msgstr "" -"Project-Id-Version: Newsroom-Core 2.7.0rc1\n" +"Project-Id-Version: Newsroom-Core 2.8.0.dev0\n" "Report-Msgid-Bugs-To: EMAIL@ADDRESS\n" -"POT-Creation-Date: 2024-07-08 10:01+0200\n" +"POT-Creation-Date: 2024-08-22 11:23+0200\n" "PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n" "Last-Translator: FULL NAME \n" "Language-Team: LANGUAGE \n" @@ -369,11 +369,11 @@ msgid "I agree to" msgstr "" #: assets/company-admin/components/CompanyUsers.tsx:25 -#: assets/user-profile/components/profile/UserProfile.tsx:102 +#: assets/user-profile/components/profile/UserProfile.tsx:104 #: assets/users/components/EditUser.tsx:236 #: assets/users/components/UsersList.tsx:33 newsroom/auth/forms.py:39 #: newsroom/auth/forms.py:46 newsroom/auth/forms.py:62 -#: newsroom/users/forms.py:46 +#: newsroom/users/forms.py:48 msgid "Email" msgstr "" @@ -434,7 +434,7 @@ msgstr "" msgid "Invalid login type, Oauth not enabled for your user" msgstr "" -#: newsroom/auth/saml.py:133 +#: newsroom/auth/saml.py:137 msgid "There was an error when using SSO" msgstr "" @@ -453,43 +453,43 @@ msgstr "" msgid "Exceeded number of allowed login attempts" msgstr "" -#: newsroom/auth/views.py:227 +#: newsroom/auth/views.py:230 msgid "Account already exists." msgstr "" -#: newsroom/auth/views.py:296 +#: newsroom/auth/views.py:299 msgid "Your account has been validated." msgstr "" -#: newsroom/auth/views.py:299 +#: newsroom/auth/views.py:302 msgid "Token has expired. Please create a new token" msgstr "" -#: newsroom/auth/views.py:318 +#: newsroom/auth/views.py:321 msgid "Your password has been changed. Please login again." msgstr "" -#: newsroom/auth/views.py:344 +#: newsroom/auth/views.py:347 msgid "A reset password token has been sent to your email address." msgstr "" -#: newsroom/auth/views.py:405 newsroom/auth/views.py:418 +#: newsroom/auth/views.py:408 newsroom/auth/views.py:421 msgid "Your password has been changed." msgstr "" -#: newsroom/auth/views.py:407 newsroom/auth/views.py:414 +#: newsroom/auth/views.py:410 newsroom/auth/views.py:417 msgid "Current password invalid." msgstr "" -#: newsroom/auth/views.py:421 +#: newsroom/auth/views.py:424 msgid "Change password is not available." msgstr "" -#: newsroom/auth/views.py:441 +#: newsroom/auth/views.py:444 msgid "User token is not valid" msgstr "" -#: newsroom/auth/views.py:452 +#: newsroom/auth/views.py:455 msgid "Could not change your password. Please contact us for assistance." msgstr "" @@ -522,20 +522,20 @@ msgstr "" msgid "{0}: {1}" msgstr "" -#: newsroom/companies/views.py:144 newsroom/companies/views.py:198 +#: newsroom/companies/views.py:152 newsroom/companies/views.py:206 #: newsroom/company_admin/views.py:58 msgid "Company not found" msgstr "" -#: newsroom/companies/views.py:165 +#: newsroom/companies/views.py:173 msgid "Value is already used" msgstr "" -#: newsroom/companies/views.py:167 +#: newsroom/companies/views.py:175 msgid "Company already exists" msgstr "" -#: newsroom/companies/views.py:201 +#: newsroom/companies/views.py:209 msgid "Company is already approved" msgstr "" @@ -649,11 +649,11 @@ msgstr "" #: assets/users/components/EditUser.tsx:273 #: assets/users/components/EditUser.tsx:282 #: assets/users/components/UsersList.tsx:38 newsroom/monitoring/forms.py:24 -#: newsroom/reports/reports.py:188 newsroom/users/forms.py:51 +#: newsroom/reports/reports.py:188 newsroom/users/forms.py:53 msgid "Company" msgstr "" -#: assets/companies/components/CompanyListItem.tsx:20 +#: assets/companies/components/CompanyListItem.tsx:31 #: assets/companies/components/EditCompanyDetails.tsx:179 #: assets/monitoring/components/EditMonitoringProfile.tsx:191 #: assets/monitoring/components/MonitoringItem.tsx:18 @@ -729,7 +729,7 @@ msgstr "" #: assets/company-reports/components/Company.tsx:24 #: assets/navigations/components/EditNavigation.tsx:26 #: assets/users/components/EditUser.tsx:354 newsroom/products/__init__.py:15 -#: newsroom/users/forms.py:63 +#: newsroom/users/forms.py:65 msgid "Products" msgstr "" @@ -832,7 +832,7 @@ msgid "API retrieval" msgstr "" #: assets/company-reports/components/CompanyReportsApp.tsx:39 -#: newsroom/reports/content_activity.py:283 +#: newsroom/reports/content_activity.py:285 msgid "Content activity" msgstr "" @@ -993,7 +993,7 @@ msgstr "" #: newsroom/templates/base_layout.html:104 #: newsroom/templates/base_layout.html:111 #: newsroom/templates/company_admin_index.html:3 -#: newsroom/templates/company_admin_index.html:6 newsroom/users/forms.py:40 +#: newsroom/templates/company_admin_index.html:6 newsroom/users/forms.py:42 msgid "Company Admin" msgstr "" @@ -1080,7 +1080,7 @@ msgstr "" msgid "click here to register." msgstr "" -#: assets/user-profile/components/profile/UserProfile.tsx:233 +#: assets/user-profile/components/profile/UserProfile.tsx:235 #: assets/users/components/EditUser.tsx:308 #: newsroom/templates/login_layout.html:50 #: newsroom/templates/login_locale_dropdown.html:5 @@ -1140,7 +1140,7 @@ msgstr "" #: assets/search/components/TopicEditor.tsx:556 #: assets/search/components/TopicFolderEditor.jsx:37 #: assets/search/components/TopicFolderEditor.jsx:39 -#: assets/user-profile/components/profile/UserProfile.tsx:249 +#: assets/user-profile/components/profile/UserProfile.tsx:251 #: assets/wire/components/SelectedItemsBar.tsx:48 #: newsroom/templates/reset_password.html:38 msgid "Cancel" @@ -1249,103 +1249,105 @@ msgid "User Management" msgstr "" #: assets/monitoring/utils.ts:4 assets/users/utils.ts:8 -#: newsroom/users/forms.py:36 +#: newsroom/users/forms.py:38 msgid "Administrator" msgstr "" #: assets/monitoring/utils.ts:6 assets/users/utils.ts:10 -#: newsroom/users/forms.py:37 +#: newsroom/users/forms.py:39 msgid "Public" msgstr "" +#: assets/companies/components/CompanyListItem.tsx:25 +#: assets/companies/components/EditCompanyDetails.tsx:187 #: assets/monitoring/utils.ts:5 assets/users/utils.ts:9 -#: newsroom/users/forms.py:38 +#: newsroom/users/forms.py:40 msgid "Internal" msgstr "" #: assets/monitoring/utils.ts:7 assets/users/utils.ts:11 -#: newsroom/users/forms.py:39 +#: newsroom/users/forms.py:41 msgid "Account Management" msgstr "" -#: assets/user-profile/components/profile/UserProfile.tsx:84 +#: assets/user-profile/components/profile/UserProfile.tsx:86 #: assets/users/components/EditUser.tsx:222 #: assets/users/components/filters/UserListSortFilter.tsx:23 -#: newsroom/users/forms.py:44 +#: newsroom/users/forms.py:46 msgid "First Name" msgstr "" -#: assets/user-profile/components/profile/UserProfile.tsx:93 +#: assets/user-profile/components/profile/UserProfile.tsx:95 #: assets/users/components/EditUser.tsx:229 #: assets/users/components/filters/UserListSortFilter.tsx:26 -#: newsroom/users/forms.py:45 +#: newsroom/users/forms.py:47 msgid "Last Name" msgstr "" #: assets/companies/components/CompanyList.tsx:41 #: assets/companies/components/EditCompanyDetails.tsx:126 -#: newsroom/users/forms.py:47 +#: newsroom/users/forms.py:49 msgid "Telephone" msgstr "" -#: assets/user-profile/components/profile/UserProfile.tsx:133 -#: assets/users/components/EditUser.tsx:250 newsroom/users/forms.py:48 +#: assets/user-profile/components/profile/UserProfile.tsx:135 +#: assets/users/components/EditUser.tsx:250 newsroom/users/forms.py:50 msgid "Mobile" msgstr "" -#: assets/user-profile/components/profile/UserProfile.tsx:142 +#: assets/user-profile/components/profile/UserProfile.tsx:144 #: assets/users/components/EditUser.tsx:257 -#: assets/users/components/UsersList.tsx:36 newsroom/users/forms.py:49 +#: assets/users/components/UsersList.tsx:36 newsroom/users/forms.py:51 msgid "Role" msgstr "" #: assets/users/components/EditUser.tsx:263 #: assets/users/components/UsersList.tsx:37 #: assets/users/components/filters/UserListSortFilter.tsx:35 -#: newsroom/users/forms.py:50 +#: newsroom/users/forms.py:52 msgid "User Type" msgstr "" -#: newsroom/users/forms.py:52 +#: newsroom/users/forms.py:54 msgid "Sign Up Details" msgstr "" -#: newsroom/users/forms.py:53 +#: newsroom/users/forms.py:55 msgid "Email Validated" msgstr "" -#: newsroom/users/forms.py:54 +#: newsroom/users/forms.py:56 msgid "Account Enabled" msgstr "" -#: newsroom/users/forms.py:55 +#: newsroom/users/forms.py:57 msgid "Account Approved" msgstr "" -#: assets/users/components/EditUser.tsx:452 newsroom/users/forms.py:56 +#: assets/users/components/EditUser.tsx:452 newsroom/users/forms.py:58 msgid "Company Expiry Alert" msgstr "" -#: assets/user-profile/components/profile/UserProfile.tsx:160 -#: newsroom/users/forms.py:57 +#: assets/user-profile/components/profile/UserProfile.tsx:162 +#: newsroom/users/forms.py:59 msgid "Receive Email Notifications" msgstr "" -#: assets/user-profile/components/profile/UserProfile.tsx:151 -#: newsroom/users/forms.py:58 +#: assets/user-profile/components/profile/UserProfile.tsx:153 +#: newsroom/users/forms.py:60 msgid "Receive App Notifications" msgstr "" -#: newsroom/users/forms.py:59 +#: newsroom/users/forms.py:61 msgid "Locale" msgstr "" -#: assets/users/components/EditUser.tsx:462 newsroom/users/forms.py:60 +#: assets/users/components/EditUser.tsx:462 newsroom/users/forms.py:62 msgid "Manage Company Topics" msgstr "" #: assets/companies/components/CompanyPermissions.tsx:74 -#: assets/users/components/EditUser.tsx:320 newsroom/users/forms.py:62 +#: assets/users/components/EditUser.tsx:320 newsroom/users/forms.py:64 msgid "Sections" msgstr "" @@ -1433,27 +1435,27 @@ msgstr "" msgid "Not Intended / Cancelled" msgstr "" -#: newsroom/web/default_settings.py:635 +#: newsroom/web/default_settings.py:636 msgid "Home" msgstr "" -#: newsroom/web/default_settings.py:640 +#: newsroom/web/default_settings.py:641 msgid "Wire" msgstr "" -#: newsroom/web/default_settings.py:645 +#: newsroom/web/default_settings.py:646 msgid "Agenda" msgstr "" -#: newsroom/web/default_settings.py:650 +#: newsroom/web/default_settings.py:651 msgid "Monitoring" msgstr "" -#: newsroom/web/default_settings.py:655 +#: newsroom/web/default_settings.py:656 msgid "Saved / Watched" msgstr "" -#: newsroom/web/default_settings.py:693 +#: newsroom/web/default_settings.py:694 msgid "Newshub" msgstr "" @@ -1461,24 +1463,24 @@ msgstr "" #: assets/agenda/components/AgendaList.tsx:329 #: assets/company-reports/components/ProductStories.tsx:26 #: assets/components/CalendarButton.tsx:49 -#: assets/components/ExpiryDateInput.tsx:93 -#: newsroom/web/default_settings.py:816 newsroom/web/default_settings.py:842 +#: assets/components/ExpiryDateInput.tsx:94 +#: newsroom/web/default_settings.py:817 newsroom/web/default_settings.py:843 msgid "Today" msgstr "" -#: newsroom/web/default_settings.py:818 +#: newsroom/web/default_settings.py:819 msgid "Last Week" msgstr "" -#: newsroom/web/default_settings.py:824 +#: newsroom/web/default_settings.py:825 msgid "Last 30 days" msgstr "" -#: newsroom/web/default_settings.py:844 +#: newsroom/web/default_settings.py:845 msgid "This Week" msgstr "" -#: newsroom/web/default_settings.py:848 +#: newsroom/web/default_settings.py:849 msgid "This Month" msgstr "" @@ -1563,29 +1565,29 @@ msgstr "" msgid "New navigation" msgstr "" -#: assets/utils.tsx:519 +#: assets/utils.tsx:520 msgid "" "There is no product associated with your user. Please reach out to your " "Company Admin" msgstr "" -#: assets/utils.tsx:523 +#: assets/utils.tsx:524 msgid "Resource was updated in the meantime, please refresh." msgstr "" -#: assets/utils.tsx:525 +#: assets/utils.tsx:526 msgid "Failed to process request!" msgstr "" -#: assets/utils.tsx:713 +#: assets/utils.tsx:714 msgid "Item copied successfully." msgstr "" -#: assets/utils.tsx:717 +#: assets/utils.tsx:718 msgid "Sorry, Copy is not supported." msgstr "" -#: assets/utils.tsx:748 +#: assets/utils.tsx:749 msgid "Daily @ {{ time1 }}, {{ time2 }} and {{ time3 }} {{ timezone }}" msgstr "" @@ -1787,6 +1789,23 @@ msgstr "" msgid "Side filter panel" msgstr "" +#: assets/agenda/components/AgendaApp.tsx:256 +msgid "Date" +msgstr "" + +#: assets/agenda/components/AgendaApp.tsx:257 +msgid "Newest updates" +msgstr "" + +#: assets/agenda/components/AgendaApp.tsx:258 +msgid "Oldest updates" +msgstr "" + +#: assets/agenda/components/AgendaApp.tsx:259 +#: assets/search/components/SearchResultsBar/index.tsx:65 +msgid "Relevance" +msgstr "" + #: assets/agenda/components/AgendaAttachments.tsx:36 msgid "Size:" msgstr "" @@ -1976,7 +1995,7 @@ msgstr "" msgid "Tomorrow" msgstr "" -#: assets/agenda/components/AgendaList.tsx:462 +#: assets/agenda/components/AgendaList.tsx:466 #: assets/am-news/components/AmNewsList.tsx:196 #: assets/wire/components/ItemsList.tsx:180 msgid "No items found." @@ -1987,23 +2006,15 @@ msgid "(Watching)" msgstr "" #: assets/agenda/components/AgendaListGroupHeader.tsx:60 -#: assets/agenda/components/AgendaListGroupHeader.tsx:68 +#: assets/agenda/components/AgendaListGroupHeader.tsx:65 msgid "More hidden" msgstr "" -#: assets/agenda/components/AgendaListGroupHeader.tsx:62 -msgid "{{ numberOfItems }} Event" -msgstr "" - -#: assets/agenda/components/AgendaListGroupHeader.tsx:70 -msgid "{{ numberOfItems }} Event & Planning" -msgstr "" - -#: assets/agenda/components/AgendaListGroupHeader.tsx:94 +#: assets/agenda/components/AgendaListGroupHeader.tsx:88 msgid "Hide" msgstr "" -#: assets/agenda/components/AgendaListGroupHeader.tsx:94 +#: assets/agenda/components/AgendaListGroupHeader.tsx:88 msgid "Show all" msgstr "" @@ -2328,7 +2339,7 @@ msgid "Edit Cards" msgstr "" #: assets/cards/components/EditCard.tsx:90 -#: assets/companies/components/EditCompanyDetails.tsx:189 +#: assets/companies/components/EditCompanyDetails.tsx:197 #: assets/components/CardEditor.tsx:106 #: assets/components/PersonalizeHomeModal.tsx:224 #: assets/monitoring/components/EditMonitoringProfile.tsx:207 @@ -2345,7 +2356,7 @@ msgstr "" #: assets/cards/components/EditCard.tsx:95 #: assets/companies/components/CompanyPermissions.tsx:167 #: assets/companies/components/EditCompanyAPI.tsx:86 -#: assets/companies/components/EditCompanyDetails.tsx:196 +#: assets/companies/components/EditCompanyDetails.tsx:204 #: assets/components/CardEditor.tsx:65 assets/components/EditPanel.tsx:141 #: assets/components/Modal.tsx:151 #: assets/components/PersonalizeHomeModal.tsx:243 @@ -2411,7 +2422,7 @@ msgstr "" msgid "Expires On" msgstr "" -#: assets/companies/components/CompanyListItem.tsx:20 +#: assets/companies/components/CompanyListItem.tsx:31 #: assets/company-admin/components/CompanyUserListItem.tsx:21 #: assets/company-reports/components/Company.tsx:57 #: assets/company-reports/components/ExpiredCompanies.tsx:12 @@ -2763,7 +2774,7 @@ msgstr "" #: assets/company-reports/components/SavedMyTopicsAndCompanyTopics.tsx:33 #: assets/components/PersonalizeHomeModal.tsx:214 #: assets/components/PersonalizeHomeModal.tsx:276 -#: assets/layout/components/SearchBase.tsx:75 +#: assets/layout/components/SearchBase.tsx:76 #: assets/search/components/FollowedTopics.tsx:201 #: assets/search/components/TopicsTab.tsx:99 msgid "My Topics" @@ -2841,31 +2852,31 @@ msgstr "" msgid "Select All" msgstr "" -#: assets/components/ExpiryDateInput.tsx:22 +#: assets/components/ExpiryDateInput.tsx:23 msgid "on" msgstr "" -#: assets/components/ExpiryDateInput.tsx:23 +#: assets/components/ExpiryDateInput.tsx:24 msgid "in 1 week" msgstr "" -#: assets/components/ExpiryDateInput.tsx:24 +#: assets/components/ExpiryDateInput.tsx:25 msgid "in 2 weeks" msgstr "" -#: assets/components/ExpiryDateInput.tsx:25 +#: assets/components/ExpiryDateInput.tsx:26 msgid "in 1 month" msgstr "" -#: assets/components/ExpiryDateInput.tsx:26 +#: assets/components/ExpiryDateInput.tsx:27 msgid "in 6 months" msgstr "" -#: assets/components/ExpiryDateInput.tsx:27 +#: assets/components/ExpiryDateInput.tsx:28 msgid "in 1 year" msgstr "" -#: assets/components/ExpiryDateInput.tsx:28 +#: assets/components/ExpiryDateInput.tsx:29 #: assets/news-api/components/EditAPIToken.tsx:195 msgid "Never" msgstr "" @@ -2890,35 +2901,35 @@ msgstr "" #: assets/components/NotificationList.tsx:80 #: assets/components/NotificationList.tsx:93 #: assets/components/NotificationList.tsx:95 -#: assets/components/NotificationPopup.tsx:34 -#: assets/components/NotificationPopup.tsx:59 -#: assets/components/NotificationPopup.tsx:71 +#: assets/components/NotificationPopup.tsx:29 +#: assets/components/NotificationPopup.tsx:54 +#: assets/components/NotificationPopup.tsx:66 msgid "Notifications" msgstr "" -#: assets/components/NotificationPopup.tsx:39 +#: assets/components/NotificationPopup.tsx:34 msgid "All notifications are paused until {{date}}" msgstr "" -#: assets/components/NotificationPopup.tsx:49 +#: assets/components/NotificationPopup.tsx:44 msgid "Resume all notifications" msgstr "" -#: assets/components/NotificationPopup.tsx:63 +#: assets/components/NotificationPopup.tsx:58 msgid "No new notifications!" msgstr "" -#: assets/components/NotificationPopup.tsx:77 +#: assets/components/NotificationPopup.tsx:72 #: assets/search/components/AdvancedSearchPanel.tsx:168 -#: assets/search/components/SearchResultsBar/index.tsx:231 +#: assets/search/components/SearchResultsBar/index.tsx:238 msgid "Clear All" msgstr "" -#: assets/components/NotificationPopup.tsx:84 +#: assets/components/NotificationPopup.tsx:79 msgid "All notifications are set to be paused from {{dateFrom}} to {{dateTo}}" msgstr "" -#: assets/components/NotificationPopup.tsx:91 +#: assets/components/NotificationPopup.tsx:86 msgid "Loading..." msgstr "" @@ -3026,7 +3037,7 @@ msgid "2x2-events" msgstr "" #: assets/components/cards/utils.ts:173 -msgid "wire-list" +msgid "Wire list" msgstr "" #: assets/components/cards/utils.ts:180 @@ -3087,9 +3098,9 @@ msgid "All" msgstr "" #: assets/components/cards/render/EventsTwoByTwoCard.tsx:33 -#: assets/search/components/SearchResultsBar/SearchResultsFiltersRow.tsx:194 +#: assets/search/components/SearchResultsBar/SearchResultsFiltersRow.tsx:195 #: assets/user-profile/components/PauseNotificationModal.tsx:126 -#: assets/wire/components/filters/NavCreatedPicker.tsx:80 +#: assets/wire/components/filters/NavCreatedPicker.tsx:74 msgid "To" msgstr "" @@ -3132,7 +3143,7 @@ msgstr "" #: assets/search/components/AdvancedSearchPanel.tsx:178 #: assets/search/components/SearchBar.tsx:59 #: assets/search/components/SearchBar.tsx:81 -#: assets/wire/components/filters/FiltersTab.tsx:173 +#: assets/wire/components/filters/FiltersTab.tsx:167 msgid "Search" msgstr "" @@ -3143,15 +3154,15 @@ msgstr "" msgid "Search for..." msgstr "" -#: assets/layout/components/SearchBase.tsx:74 +#: assets/layout/components/SearchBase.tsx:75 msgid "Topics" msgstr "" -#: assets/layout/components/SearchBase.tsx:76 +#: assets/layout/components/SearchBase.tsx:77 msgid "Filters" msgstr "" -#: assets/layout/components/SearchBase.tsx:130 +#: assets/layout/components/SearchBase.tsx:131 msgid "Custom View" msgstr "" @@ -3415,7 +3426,7 @@ msgid "Notifications cleared successfully" msgstr "" #: assets/notifications/components/NotificationsApp.tsx:46 -#: assets/user-profile/components/profile/UserProfile.tsx:202 +#: assets/user-profile/components/profile/UserProfile.tsx:204 msgid "Notifications resumed" msgstr "" @@ -3583,11 +3594,11 @@ msgstr "" msgid "Register" msgstr "" -#: assets/search/actions.ts:296 +#: assets/search/actions.ts:298 msgid "Items were shared successfully." msgstr "" -#: assets/search/actions.ts:298 +#: assets/search/actions.ts:300 msgid "Item was shared successfully." msgstr "" @@ -3828,7 +3839,7 @@ msgid "" msgstr "" #: assets/search/components/TopicForm.tsx:242 -#: assets/user-profile/components/profile/UserProfile.tsx:183 +#: assets/user-profile/components/profile/UserProfile.tsx:185 msgid "Edit schedule" msgstr "" @@ -3861,7 +3872,7 @@ msgid "not" msgstr "" #: assets/search/components/SearchResultsBar/SearchResultsAdvancedSearchRow.tsx:140 -#: assets/wire/components/filters/FiltersTab.tsx:166 +#: assets/wire/components/filters/FiltersTab.tsx:160 msgid "Clear" msgstr "" @@ -3875,17 +3886,17 @@ msgid "inside" msgstr "" #: assets/search/components/SearchResultsBar/SearchResultsFiltersRow.tsx:157 -#: assets/search/components/SearchResultsBar/SearchResultsFiltersRow.tsx:176 +#: assets/search/components/SearchResultsBar/SearchResultsFiltersRow.tsx:177 #: assets/user-profile/components/PauseNotificationModal.tsx:111 -#: assets/wire/components/filters/NavCreatedPicker.tsx:68 +#: assets/wire/components/filters/NavCreatedPicker.tsx:62 msgid "From" msgstr "" -#: assets/search/components/SearchResultsBar/SearchResultsFiltersRow.tsx:261 +#: assets/search/components/SearchResultsBar/SearchResultsFiltersRow.tsx:262 msgid "Clear filters" msgstr "" -#: assets/search/components/SearchResultsBar/SearchResultsFiltersRow.tsx:269 +#: assets/search/components/SearchResultsBar/SearchResultsFiltersRow.tsx:270 msgid "Filters applied" msgstr "" @@ -3909,35 +3920,39 @@ msgstr "" msgid "Save new topic" msgstr "" -#: assets/search/components/SearchResultsBar/index.tsx:50 +#: assets/search/components/SearchResultsBar/index.tsx:57 msgid "Newest first" msgstr "" -#: assets/search/components/SearchResultsBar/index.tsx:52 +#: assets/search/components/SearchResultsBar/index.tsx:61 msgid "Oldest first" msgstr "" -#: assets/search/components/SearchResultsBar/index.tsx:54 -msgid "Relevance" -msgstr "" - -#: assets/search/components/SearchResultsBar/index.tsx:193 +#: assets/search/components/SearchResultsBar/index.tsx:201 msgid "1 result" msgstr "" -#: assets/search/components/SearchResultsBar/index.tsx:195 +#: assets/search/components/SearchResultsBar/index.tsx:203 msgid "{{ count }} results" msgstr "" -#: assets/search/components/SearchResultsBar/index.tsx:203 +#: assets/search/components/SearchResultsBar/index.tsx:211 #: assets/users/components/filters/UserListSortFilter.tsx:83 msgid "Sort by:" msgstr "" -#: assets/search/components/SearchResultsBar/index.tsx:206 +#: assets/search/components/SearchResultsBar/index.tsx:214 msgid "Sort results by" msgstr "" +#: assets/search/components/SearchResultsBar/index.tsx:244 +msgid "Hide search terms" +msgstr "" + +#: assets/search/components/SearchResultsBar/index.tsx:245 +msgid "Show search terms" +msgstr "" + #: assets/section-filters/actions.ts:72 msgid "Section Filter updated successfully" msgstr "" @@ -4096,36 +4111,36 @@ msgstr "" msgid "Please provide last name" msgstr "" -#: assets/user-profile/components/profile/UserProfile.tsx:115 +#: assets/user-profile/components/profile/UserProfile.tsx:117 msgid "Change password" msgstr "" -#: assets/user-profile/components/profile/UserProfile.tsx:124 +#: assets/user-profile/components/profile/UserProfile.tsx:126 #: assets/users/components/EditUser.tsx:243 #: assets/users/components/UsersList.tsx:35 msgid "Phone" msgstr "" -#: assets/user-profile/components/profile/UserProfile.tsx:169 +#: assets/user-profile/components/profile/UserProfile.tsx:171 msgid "" "You will receive email notifications daily, sent in a digest format at " "regular intervals. This setting will apply to all subscribed scheduled " "email notifications (e.g. {{ wire }} Topics, {{ agenda }} Topics)." msgstr "" -#: assets/user-profile/components/profile/UserProfile.tsx:195 +#: assets/user-profile/components/profile/UserProfile.tsx:197 msgid "All notifications will be paused from {{dateFrom}} to {{dateTo}}" msgstr "" -#: assets/user-profile/components/profile/UserProfile.tsx:205 +#: assets/user-profile/components/profile/UserProfile.tsx:207 msgid "Clear Pausing" msgstr "" -#: assets/user-profile/components/profile/UserProfile.tsx:218 +#: assets/user-profile/components/profile/UserProfile.tsx:220 msgid "Pause All Notifications" msgstr "" -#: assets/user-profile/components/profile/UserProfile.tsx:255 +#: assets/user-profile/components/profile/UserProfile.tsx:257 msgid "Save Changes" msgstr "" @@ -4241,19 +4256,19 @@ msgstr "" msgid "Item was removed successfully" msgstr "" -#: assets/wire/utils.ts:396 +#: assets/wire/utils.ts:395 msgid "segments" msgstr "" -#: assets/wire/utils.ts:396 +#: assets/wire/utils.ts:395 msgid "segment" msgstr "" -#: assets/wire/utils.ts:399 +#: assets/wire/utils.ts:398 msgid "versions" msgstr "" -#: assets/wire/utils.ts:399 +#: assets/wire/utils.ts:398 msgid "version" msgstr "" @@ -4390,7 +4405,7 @@ msgstr "" msgid "Published By" msgstr "" -#: assets/wire/components/filters/NavCreatedPicker.tsx:62 +#: assets/wire/components/filters/NavCreatedPicker.tsx:56 msgid "Custom date range" msgstr "" diff --git a/newsroom/__init__.py b/newsroom/__init__.py index 1c26f0ee9..59c757ae3 100644 --- a/newsroom/__init__.py +++ b/newsroom/__init__.py @@ -12,7 +12,7 @@ from newsroom.user_roles import UserRole -__version__ = "2.8.0.dev" +__version__ = "2.8.0rc1" # reuse content api dbs MONGO_PREFIX = "CONTENTAPI_MONGO" diff --git a/newsroom/agenda/formatters/ical_formatter.py b/newsroom/agenda/formatters/ical_formatter.py index 2ac4f1926..0be5f530b 100644 --- a/newsroom/agenda/formatters/ical_formatter.py +++ b/newsroom/agenda/formatters/ical_formatter.py @@ -57,15 +57,17 @@ def format_event(self, item): event.add("priority", item["priority"]) # calendar as category - for calendar in item.get("calendars", []): + for calendar in item.get("calendars") or []: if calendar.get("name"): event.add("categories", [calendar["name"]]) # dates dates = item.get("dates", {}) - event.add("dtstart", datetime(dates["start"])) + start = datetime(dates["start"]) + event.add("dtstart", start.date() if dates.get("all_day") else start) if dates.get("end"): - event.add("dtend", datetime(dates["end"])) + end = datetime(dates["end"]) + event.add("dtend", end.date() if dates.get("no_end_time") or dates.get("all_day") else end) try: rrule = item["event"]["dates"]["recurring_rule"] event.add("rrule", get_rrule_kwargs(rrule)) diff --git a/newsroom/assets/module.py b/newsroom/assets/module.py index 0badecea2..f004fca66 100644 --- a/newsroom/assets/module.py +++ b/newsroom/assets/module.py @@ -38,6 +38,7 @@ def legacy_init_app(app: SuperdeskAsyncApp): "authentication": None, "mongo_prefix": MONGO_PREFIX, "internal_resource": True, + "mongo_indexes": {}, }, ) diff --git a/newsroom/auth/saml.py b/newsroom/auth/saml.py index b0a280064..7b21cfc6b 100644 --- a/newsroom/auth/saml.py +++ b/newsroom/auth/saml.py @@ -38,6 +38,7 @@ SESSION_SESSION_ID = "samlSessionIndex" SESSION_USERDATA_KEY = "samlUserdata" SESSION_SAML_CLIENT = "_saml_client" +SAML_NAME_KEY = "http://schemas.microsoft.com/identity/claims/displayname" logger = logging.getLogger(__name__) @@ -83,12 +84,21 @@ def get_userdata(nameid: str, saml_data: Dict[str, List[str]]) -> UserData: if saml_data.get(saml_key): userdata[user_key] = saml_data[saml_key][0] # type: ignore + if saml_data.get(SAML_NAME_KEY): + name_list = saml_data[SAML_NAME_KEY][0].split(" ") + userdata.setdefault("first_name", name_list[0]) + userdata.setdefault( + "last_name", name_list[-1] + ) # this might be again first name, but we need something not empty + # first we try to find company based on email domain domain = nameid.split("@")[-1] if domain: company = superdesk.get_resource_service("companies").find_one(req=None, auth_domains=domain) if company is not None: userdata["company"] = company["_id"] + if not company.get("internal"): + userdata["user_type"] = "public" # then based on preconfigured saml client if session.get(SESSION_SAML_CLIENT) and not userdata.get("company"): @@ -97,6 +107,8 @@ def get_userdata(nameid: str, saml_data: Dict[str, List[str]]) -> UserData: ) if company is not None: userdata["company"] = company["_id"] + if not company.get("internal"): + userdata["user_type"] = "public" # last option is global env variable saml_company_config = get_app_config("SAML_COMPANY") diff --git a/newsroom/auth/views.py b/newsroom/auth/views.py index 28f542808..af571a163 100644 --- a/newsroom/auth/views.py +++ b/newsroom/auth/views.py @@ -223,9 +223,13 @@ def logout(): @blueprint.route("/signup", methods=["GET", "POST"]) +# <<<<<<< HEAD async def signup(): + if not get_app_config("SIGNUP_EMAIL_RECIPIENTS"): + abort(404) app = get_current_app().as_any() form = await (app.signup_form_class or SignupForm).create_form() + if len(app.countries): form.country.choices += [(item.get("value"), item.get("text")) for item in app.countries] diff --git a/newsroom/companies/companies.py b/newsroom/companies/companies.py index 8d2568af9..2a11303b9 100644 --- a/newsroom/companies/companies.py +++ b/newsroom/companies/companies.py @@ -77,6 +77,7 @@ class CompaniesResource(newsroom.Resource): "auth_provider": {"type": "string"}, "company_size": {"type": "string"}, "referred_by": {"type": "string"}, + "internal": {"type": "boolean", "default": False}, } datasource = {"source": "companies", "default_sort": [("name", 1)]} diff --git a/newsroom/companies/views.py b/newsroom/companies/views.py index 94d272953..1486a4d02 100644 --- a/newsroom/companies/views.py +++ b/newsroom/companies/views.py @@ -109,7 +109,15 @@ def get_company_updates(data, original=None): } # "seats" are not in CompanyResource - for field in ["sections", "archive_access", "events_only", "restrict_coverage_info", "products", "seats"]: + for field in [ + "sections", + "archive_access", + "events_only", + "restrict_coverage_info", + "products", + "seats", + "internal", + ]: if field in data: updates[field] = data[field] diff --git a/newsroom/factory/app.py b/newsroom/factory/app.py index f652ab4d4..5c995cec1 100644 --- a/newsroom/factory/app.py +++ b/newsroom/factory/app.py @@ -15,6 +15,7 @@ from flask_caching import Cache from elasticapm.contrib.flask import ElasticAPM from pydantic import ValidationError +from eve.io.mongo import ensure_mongo_indexes from superdesk.flask import jsonify, request, render_template, g from superdesk.storage import AmazonMediaStorage, SuperdeskGridFSMediaStorage @@ -295,3 +296,7 @@ def _get_apm_environment(self): if "localhost" in self.config["CLIENT_URL"] or self.debug: return "testing" return "production" + + def init_indexes(self): + for resource in self.config["DOMAIN"]: + ensure_mongo_indexes(self, resource) diff --git a/newsroom/monitoring/__init__.py b/newsroom/monitoring/__init__.py index d7d118502..a7b28a749 100644 --- a/newsroom/monitoring/__init__.py +++ b/newsroom/monitoring/__init__.py @@ -44,5 +44,7 @@ def init_app(app): superdesk.register_resource("monitoring_search", MonitoringSearchResource, MonitoringSearchService, _app=app) app.add_template_global(get_keywords_in_text, "get_keywords_in_text") - theme_folder = getattr(app, "theme_folder", None) or path.join(app.config["SERVER_PATH"], "theme") - app.add_template_global(theme_folder, "monitoring_image_path") + + # TODO-ASYNC: Removed in `develop` branch, investigate + # theme_folder = getattr(app, "theme_folder", None) or path.join(app.config["SERVER_PATH"], "theme") + # app.add_template_global(theme_folder, "monitoring_image_path") diff --git a/newsroom/news_api/factory.py b/newsroom/news_api/factory.py index e291f72f9..7e97d3e3f 100644 --- a/newsroom/news_api/factory.py +++ b/newsroom/news_api/factory.py @@ -134,7 +134,8 @@ def base_exception_error(err): self.register_error_handler(AssertionError, assertion_error) self.register_error_handler(Exception, base_exception_error) - def settings_app(self, app, name, weight=1000, data=None, allow_account_mgr=False): + # TODO-ASYNC: Method signature removed in `develop` branch, investigate + def settings_app(self, *args, **kwargs): pass diff --git a/newsroom/push.py b/newsroom/push.py index 9f02b4d59..6fad9822d 100644 --- a/newsroom/push.py +++ b/newsroom/push.py @@ -90,36 +90,42 @@ async def push(): assert "guid" in item or "_id" in item, {"guid": 1} assert "type" in item, {"type": 1} - app = get_current_app() - signals.push.send(app.as_any(), item=item) - - if item.get("type") == "event": - orig = app.data.find_one("agenda", req=None, _id=item["guid"]) - _id = publish_event(item, orig) - notify_new_agenda_item.delay(_id, check_topics=True, is_new=orig is None) - elif item.get("type") == "planning": - orig = app.data.find_one("agenda", req=None, _id=item["guid"]) or {} - item["planning_date"] = parse_date_str(item["planning_date"]) - plan_id = publish_planning_item(item, orig) - event_id = publish_planning_into_event(item) - # Prefer parent Event when sending notificaitons - _id = event_id or plan_id - notify_new_agenda_item.delay(_id, check_topics=True, is_new=orig is None) - elif item.get("type") == "text": - orig = superdesk.get_resource_service("items").find_one(req=None, _id=item["guid"]) - item["_id"] = publish_item(item, orig) - if not item.get("nextversion"): - notify_new_wire_item.delay( - item["_id"], check_topics=orig is None or get_app_config("WIRE_NOTIFICATIONS_ON_CORRECTIONS") - ) - elif item["type"] == "planning_featured": - publish_planning_featured(item) - else: - abort(400, gettext("Unknown type {}".format(item.get("type")))) + lock_name = f"push-{item.get('guid') or item.get('_id')}" + if not lock(lock_name, expire=60): + return abort(503) + try: + app = get_current_app() + signals.push.send(app.as_any()._get_current_object(), item=item) + + if item.get("type") == "event": + orig = app.data.find_one("agenda", req=None, _id=item["guid"]) + _id = publish_event(item, orig) + notify_new_agenda_item.delay(_id, check_topics=True, is_new=orig is None) + elif item.get("type") == "planning": + orig = app.data.find_one("agenda", req=None, _id=item["guid"]) or {} + item["planning_date"] = parse_date_str(item["planning_date"]) + plan_id = publish_planning_item(item, orig) + event_id = publish_planning_into_event(item) + # Prefer parent Event when sending notificaitons + _id = event_id or plan_id + notify_new_agenda_item.delay(_id, check_topics=True, is_new=orig is None) + elif item.get("type") == "text": + orig = superdesk.get_resource_service("items").find_one(req=None, _id=item["guid"]) + item["_id"] = publish_item(item, orig) + if not item.get("nextversion"): + notify_new_wire_item.delay( + item["_id"], check_topics=orig is None or get_app_config("WIRE_NOTIFICATIONS_ON_CORRECTIONS") + ) + elif item["type"] == "planning_featured": + publish_planning_featured(item) + else: + abort(400, gettext("Unknown type {}".format(item.get("type")))) - if get_app_config("DELETE_DASHBOARD_CACHE_ON_PUSH", True): - delete_dashboard_caches() - return jsonify({}) + if get_app_config("DELETE_DASHBOARD_CACHE_ON_PUSH", True): + delete_dashboard_caches() + return jsonify({}) + finally: + unlock(lock_name) def set_dates(doc): @@ -280,8 +286,10 @@ def publish_event(event, orig): updates["coverages"] = None updates["planning_items"] = None - elif event.get("state") == WORKFLOW_STATE.SCHEDULED: - # event is reposted (possibly after a cancel) + elif parse_date_str(event.get("versioncreated")) > orig.get( + "versioncreated" + ): # event is reposted (possibly after a cancel) + logger.info("Updating event %s", orig["_id"]) updates = { "event": event, "version": event.get("version", event.get(VERSION)), @@ -293,6 +301,9 @@ def publish_event(event, orig): set_agenda_metadata_from_event(updates, event, False) + else: + logger.info("Ignoring event %s", orig["_id"]) + if updates: updated = orig.copy() updated.update(updates) @@ -442,6 +453,7 @@ def set_agenda_metadata_from_event(agenda, event, set_doc_id=True): agenda["definition_short"] = event.get("definition_short") agenda["definition_long"] = event.get("definition_long") agenda["version"] = event.get("version") + agenda["versioncreated"] = event.get("versioncreated") agenda["calendars"] = event.get("calendars") agenda["location"] = event.get("location") agenda["ednote"] = event.get("ednote") diff --git a/newsroom/reports/content_activity.py b/newsroom/reports/content_activity.py index 7bbdb3080..e2277b18d 100644 --- a/newsroom/reports/content_activity.py +++ b/newsroom/reports/content_activity.py @@ -258,6 +258,8 @@ def export_csv(args, results): if action_name in actions: row.append((aggs.get("actions") or {}).get(action_name, 0)) + rows.append(row) + return rows diff --git a/newsroom/templates/layout_login.html b/newsroom/templates/layout_login.html index e34589b95..f2929bcac 100644 --- a/newsroom/templates/layout_login.html +++ b/newsroom/templates/layout_login.html @@ -1,22 +1 @@ -{% extends "layout_wire.html" %} - -{% block contentMain %} - -{% endblock %} +{% extends "login_layout.html" %} \ No newline at end of file diff --git a/newsroom/templates/login_client.html b/newsroom/templates/login_client.html index 544368ba2..add818dbf 100644 --- a/newsroom/templates/login_client.html +++ b/newsroom/templates/login_client.html @@ -1,10 +1,10 @@ -{% extends "layout_login.html" %} +{% extends "login_layout.html" %} {% block login_title %}
{{ gettext("Login") }}
{% endblock %} -{% block login_content %} +{% block login_form %} diff --git a/newsroom/templates/login_form.html b/newsroom/templates/login_form.html deleted file mode 100644 index 2a2d10419..000000000 --- a/newsroom/templates/login_form.html +++ /dev/null @@ -1,33 +0,0 @@ -
- {{ form.csrf_token }} - -
- {{ form.firebase_status(type="hidden", id="firebase-status") }} -
- - {% include "form_alerts.html" %} - -
- - {{ form.email(class="form-control", id="email", required="true", autocomplete="username") }} -
-
- - {{ form.password(class="form-control", id="password", required="true", autocomplete="current-password") }} -
-
-
- {{ form.remember_me(class="form-check-input", id="remember_me") }} - -
-
- - {% if config.GOOGLE_LOGIN %} - - {{ gettext('Log In with Google') }} - - {% endif %} -
diff --git a/newsroom/templates/login_layout.html b/newsroom/templates/login_layout.html index f5b394a8c..ce666397d 100644 --- a/newsroom/templates/login_layout.html +++ b/newsroom/templates/login_layout.html @@ -2,65 +2,106 @@ {% block contentMain %} -