CourseHomeDialog.js 7.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236
  1. /*
  2. * Copyright (C) 2017 - present Instructure, Inc.
  3. *
  4. * This file is part of Canvas.
  5. *
  6. * Canvas is free software: you can redistribute it and/or modify it under
  7. * the terms of the GNU Affero General Public License as published by the Free
  8. * Software Foundation, version 3 of the License.
  9. *
  10. * Canvas is distributed in the hope that it will be useful, but WITHOUT ANY
  11. * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR
  12. * A PARTICULAR PURPOSE. See the GNU Affero General Public License for more
  13. * details.
  14. *
  15. * You should have received a copy of the GNU Affero General Public License along
  16. * with this program. If not, see <http://www.gnu.org/licenses/>.
  17. */
  18. import React from 'react'
  19. import PropTypes from 'prop-types'
  20. import axios from 'axios'
  21. import Modal, {ModalHeader, ModalBody, ModalFooter} from 'instructure-ui/lib/components/Modal'
  22. import Heading from 'instructure-ui/lib/components/Heading'
  23. import RadioInputGroup from 'instructure-ui/lib/components/RadioInputGroup'
  24. import RadioInput from 'instructure-ui/lib/components/RadioInput'
  25. import Button from 'instructure-ui/lib/components/Button'
  26. import Typography from 'instructure-ui/lib/components/Typography'
  27. import Link from 'instructure-ui/lib/components/Link'
  28. import ScreenReaderContent from 'instructure-ui/lib/components/ScreenReaderContent'
  29. import AccessibleContent from 'instructure-ui/lib/components/AccessibleContent'
  30. import I18n from 'i18n!course_home_dialog'
  31. import plainStoreShape from 'jsx/shared/proptypes/plainStoreShape'
  32. class CourseHomeDialog extends React.Component {
  33. static propTypes = {
  34. store: PropTypes.shape(plainStoreShape).isRequired,
  35. open: PropTypes.bool.isRequired,
  36. onRequestClose: PropTypes.func.isRequired,
  37. wikiFrontPageTitle: PropTypes.string,
  38. wikiUrl: PropTypes.string.isRequired,
  39. courseId: PropTypes.string.isRequired,
  40. isPublishing: PropTypes.bool.isRequired,
  41. onSubmit: PropTypes.func,
  42. returnFocusTo: PropTypes.instanceOf(Element),
  43. }
  44. static defaultProps = {
  45. onSubmit: () => { window.location.reload() },
  46. wikiFrontPageTitle: null,
  47. }
  48. constructor (props) {
  49. super(props)
  50. this.state = props.store.getState()
  51. }
  52. renderWikiLabelContent () {
  53. const {wikiUrl, wikiFrontPageTitle} = this.props
  54. if (wikiFrontPageTitle) {
  55. return (
  56. <span>
  57. <Typography size="small" color="secondary">
  58. &nbsp;&nbsp;
  59. <i>{wikiFrontPageTitle}</i>
  60. &nbsp;
  61. [<Link href={wikiUrl}>{I18n.t('Change')}</Link>]
  62. </Typography>
  63. </span>
  64. )
  65. }
  66. return (
  67. <span>
  68. <AccessibleContent>*</AccessibleContent>
  69. <ScreenReaderContent>
  70. <Link href={wikiUrl}>
  71. {I18n.t('Front page must be set first')}
  72. </Link>
  73. </ScreenReaderContent>
  74. </span>
  75. )
  76. }
  77. renderWikiLabel () {
  78. return (
  79. <span>
  80. {I18n.t('Pages Front Page')}
  81. {this.renderWikiLabelContent()}
  82. </span>
  83. )
  84. }
  85. render () {
  86. const {selectedDefaultView} = this.state
  87. const {wikiFrontPageTitle, wikiUrl} = this.props
  88. const inputs = [
  89. {
  90. value: 'feed',
  91. label: I18n.t('Course Activity Stream'),
  92. checked: selectedDefaultView === 'feed'
  93. },
  94. {
  95. value: 'wiki',
  96. label: this.renderWikiLabel(),
  97. checked: selectedDefaultView === 'wiki',
  98. disabled: !wikiFrontPageTitle
  99. },
  100. {
  101. value: 'modules',
  102. label: I18n.t('Course Modules'),
  103. checked: selectedDefaultView === 'modules'
  104. },
  105. {
  106. value: 'assignments',
  107. label: I18n.t('Assignments List'),
  108. checked: selectedDefaultView === 'assignments'
  109. },
  110. {
  111. value: 'syllabus',
  112. label: I18n.t('Syllabus'),
  113. checked: selectedDefaultView === 'syllabus'
  114. }
  115. ]
  116. const instructions = this.props.isPublishing ?
  117. I18n.t('Before publishing your course, you must either publish a module in the Modules page, or choose a different home page.') :
  118. I18n.t("Select what you'd like to display on the home page.")
  119. return (
  120. <Modal
  121. open={this.props.open}
  122. transition="fade"
  123. label={I18n.t('Choose Course Home Page')}
  124. closeButtonLabel={I18n.t('Close')}
  125. applicationElement={() => document.getElementById('application')}
  126. onDismiss={this.props.onRequestClose}
  127. onClose={this.onClose}
  128. >
  129. <ModalHeader>
  130. <Heading tag="h2" level="h3">{I18n.t('Choose Home Page')}</Heading>
  131. </ModalHeader>
  132. <ModalBody>
  133. <div className="content-box-mini" style={{marginTop: '0'}}>
  134. <AccessibleContent>
  135. <Typography weight="bold" size="small">
  136. {instructions}
  137. </Typography>
  138. </AccessibleContent>
  139. </div>
  140. <RadioInputGroup
  141. description={<ScreenReaderContent>{instructions}</ScreenReaderContent>}
  142. name="course[default_view]"
  143. onChange={this.onChange}
  144. defaultValue={selectedDefaultView}
  145. >
  146. {inputs.map(input =>
  147. <RadioInput
  148. key={input.value}
  149. checked={input.checked}
  150. value={input.value}
  151. label={input.label}
  152. disabled={input.disabled}
  153. />)
  154. }
  155. </RadioInputGroup>
  156. {
  157. wikiFrontPageTitle ? (
  158. null
  159. ) : (
  160. <div className="content-box-mini">
  161. * <Link href={wikiUrl}>{I18n.t('Front page must be set first')}
  162. </Link></div>
  163. )
  164. }
  165. </ModalBody>
  166. <ModalFooter>
  167. <Button onClick={this.props.onRequestClose}>{I18n.t('Cancel')}</Button>&nbsp;
  168. <Button
  169. onClick={this.onSubmit}
  170. disabled={this.props.isPublishing && this.state.selectedDefaultView === 'modules'}
  171. variant="primary"
  172. >{
  173. this.props.isPublishing ? I18n.t('Choose and Publish') : I18n.t('Save')
  174. }</Button>
  175. </ModalFooter>
  176. </Modal>
  177. );
  178. }
  179. componentDidMount () {
  180. this.props.store.addChangeListener(this.onStoreChange)
  181. }
  182. componentWillUnmount () {
  183. this.props.store.removeChangeListener(this.onStoreChange)
  184. }
  185. onClose = () => {
  186. // this (unnecessary?) setTimeout fixes returning focus in ie11
  187. window.setTimeout(() => {
  188. const returnFocusTo = this.props.returnFocusTo
  189. returnFocusTo && returnFocusTo.focus()
  190. })
  191. }
  192. onStoreChange = () => {
  193. this.setState(this.props.store.getState())
  194. }
  195. onSubmit = () => {
  196. const {selectedDefaultView, savedDefaultView} = this.state
  197. let savingPromise
  198. if (selectedDefaultView !== savedDefaultView) {
  199. savingPromise = axios.put(`/api/v1/courses/${this.props.courseId}`, {
  200. course: {default_view: this.state.selectedDefaultView}
  201. }).then(({data: course}) => course.default_view)
  202. } else {
  203. savingPromise = Promise.resolve(savedDefaultView)
  204. }
  205. savingPromise.then((newDefaultView) => {
  206. this.props.store.setState({savedDefaultView: newDefaultView})
  207. this.props.onSubmit()
  208. })
  209. }
  210. onChange = (value) => {
  211. this.props.store.setState({selectedDefaultView: value})
  212. }
  213. }
  214. export default CourseHomeDialog