From 1b0ff4d40df9a4b6d9eb9f122071502eea41c57d Mon Sep 17 00:00:00 2001 From: Yusuke Komiyama Date: Sun, 25 Feb 2018 22:46:37 +0900 Subject: [PATCH 1/5] update ja/translations.ts by NII --- app/locales/ja/translations.ts | 64 ++++++++++++++++++++++++++++++++++ 1 file changed, 64 insertions(+) diff --git a/app/locales/ja/translations.ts b/app/locales/ja/translations.ts index 4a3b22c263b..1c4f0f4686a 100644 --- a/app/locales/ja/translations.ts +++ b/app/locales/ja/translations.ts @@ -13,6 +13,13 @@ export default { md5: 'MD5', date: '日付', sha2: 'SHA2', + title: 'タイトル', + contributors: '共同研究者', + modified: '編集', + description: '説明', + create: '作成', + and: 'and', + more: '詳細', }, quickfiles: { title: '{{user-name}} のクイックファイル', @@ -53,4 +60,61 @@ export default { save_success: 'ファイルが保存されました', save_fail: 'エラー、ファイルを保存できません', }, + dashboard: { + title: 'ダッシュボード', + create_new_project_button: '新規プロジェクト作成', + quicksearch: { + search: 'マイプロジェクト内の検索', + other_links: 'あなたの仕事を整理したり、OSFを検索するには、My Projectsに行きます。', + no_results: 'プロジェクトが見つかりません!', + no_projects: { + line1: 'まだプロジェクトがありません。画面右上のボタンからプロジェクトを作成して下さい。', + line2: 'この機能を使用すると、プロジェクトを検索してすばやくアクセスできます。', + preview_alt: 'クイックプロジェクトの全画面プレビュー', + }, + private_parent: '非公開プロジェクト / ', + private_grandparent: '非公開プロジェクト / 非公開 / ', + }, + noteworthy: { + description: '公開プロジェクトの検索', + new_and_noteworthy: '新着と注目', + failed_noteworthy: '"新着と注目"プロジェクトのローディングに失敗しました。', + most_popular: '最人気', + failed_popular: '"最人気"プロジェクトのローディングに失敗しました。', + search_more: '更にプロジェクトを探す', + by: 'by', + }, + meetings: { + title: '学会や研究会をホスティングしますか?', + description: 'OSF for Meetings サービスを使用し、学術集会への投稿するためのポータルを提供します。', + button: '学術集会の表示', + }, + preprints: { + title: '最新の研究を閲覧', + description: 'さまざまな研究分野をカバーするOSFで、ホストされている最新のプレプリントをご覧ください。', + button: 'プレプリントの表示', + }, + }, + new_project: { + header: '新規プロジェクト作成', + title_placeholder: 'プロジェクトタイトルの入力', + more: 'More', + affiliation: '所属', + remove_all: '全件削除', + select_all: '全件選択', + no_matches: '一致しません', + description_placeholder: 'プロジェクト説明の入力', + template_title: 'テンプレート(オプション)', + template_search_help: 'プロジェクトを検索するタイピングの開始。テンプレートとしてプロジェクトを選択すると、そのプロジェクトの内容をインポートせずにその構造が新しいプロジェクトに複製されます。', + template_placeholder: 'テンプレートとして使用するプロジェクトを選択する', + success_message: '新規プロジェクトの作成に成功しました', + stay_here: 'この場に留まる', + go_to_new: '作成したプロジェクトへ移動', + }, + banners: { + prereg: { + description: 'あなたの次の研究を改善してください。 Prereg Challengeに参加すれば$ 1,000を獲得できます。', + button: 'Prereg Challengeをはじめる', + }, + }, }; From b81b56a561f852f8c6b34e0f12ff8e228a4536a5 Mon Sep 17 00:00:00 2001 From: Yusuke Komiyama Date: Thu, 8 Mar 2018 09:53:55 +0900 Subject: [PATCH 2/5] add new items to ja/translations.ts by NII --- app/locales/ja/translations.ts | 193 +++++++++++++++++++++------------ 1 file changed, 126 insertions(+), 67 deletions(-) diff --git a/app/locales/ja/translations.ts b/app/locales/ja/translations.ts index 940ae58c650..a3bd8caad0e 100644 --- a/app/locales/ja/translations.ts +++ b/app/locales/ja/translations.ts @@ -2,6 +2,7 @@ export default { general: { + OSF: 'OSF', share: '共有', embed: '埋込み', download: 'ダウンロード', @@ -15,17 +16,23 @@ export default { sha2: 'SHA2', title: 'タイトル', contributors: '共同研究者', - modified: '編集', + modified: '変更', description: '説明', create: '作成', and: 'and', more: '詳細', - clipboard_copy: 'Copy to clipboard', - sort_asc: 'Sort ascending', - sort_desc: 'Sort descending', - next: 'next', - previous: 'previous', - help: 'help', + upload: 'アップロード', + rename: '名前の変更', + move: '移動', + name: 'ファイル名', + size: 'ファイルサイズ', + version: 'バージョン', + downloads: 'ダウンロード', + close: '閉じる', + back: '戻る', + filter: 'フィルター', + revert: '復帰', + save: '保存', }, quickfiles: { title: '{{user-name}} のクイックファイル', @@ -65,47 +72,42 @@ export default { delete_fail: 'エラー、ファイルを削除できません', save_success: 'ファイルが保存されました', save_fail: 'エラー、ファイルを保存できません', - mfr_iframe_title: 'Rendering of document', - add_tag: 'add a tag to enhance discoverability', }, file_browser: { - loading: 'Loading...', - delete_multiple: 'Delete multiple', - download_zip: 'Download as zip', - drop_placeholder: 'Drop files here to upload', - drop_reminder: 'Drop file to upload', - no_files: 'This user has not uploaded any quickfiles', - share_title: 'Share', - clipboard_copy: 'Copy to clipboard', + loading: 'ローディング中...', + delete_multiple: '複数削除', + download_zip: 'Zipとしてダウンロード', + drop_placeholder: 'アップロードするファイルをここにドロップ', + drop_reminder: 'アップロードするファイルをドロップ', + no_files: 'このユーザーはクイックファイルをアップロードしていません', info: { - title: 'How to use the file browser', - upload: 'Upload: Single file uploads via drag and drop or by clicking the upload button.', - select: 'Select rows: Click on a row to show further actions in the toolbar. Use Command or Shift keys to select multiple files.', - folders: 'Folders: Not supported; consider an OSF project for uploading and managing many files.', - open1: 'Open files: Click a file name to go to view the file in the OSF.', - open2: 'Open files in new tab: Press Command (Ctrl in Windows) and click a file name to open it in a new tab.', - download: 'Download as zip: Click the Download as Zip button in the toolbar to download all files as a .zip.', + title: 'ファイルブラウザの使い方', + upload: 'アップロード:ドラッグアンドドロップまたはアップロードボタンをクリックして、ファイルをアップロードします。', + select: '行を選択:行をクリックすると、ツールバーにさらにアクションが表示されます。 複数のファイルを選択するには、CommandキーまたはShiftキーを使用します。', + folders: 'フォルダ:サポートされていません; 多くのファイルをアップロードおよび管理するためのOSFプロジェクトを検討してください。', + open1: 'ファイルを開く:ファイル名をクリックすると、OSF内のファイルが表示されます。', + open2: '新規タブでファイルを開く: command(WindowsではCtrl)を押し、ファイル名をクリックして新しいタブで開きます。', + download: 'Zipとしてダウンロード: ツールバーの[Zipとしてダウンロード]ボタンをクリックすると、すべてのファイルが.zipとしてダウンロードされます。', }, delete_modal: { - title: 'Delete "{{selectedItems.firstObject.itemName}}"?', - title_multiple: 'Delete multiple?', - body: 'This action is irreversible', + title: '"{{selectedItems.firstObject.itemName}}"を削除しますか?', + title_multiple: '複数削除しますか?', + body: 'この操作は不可逆的です', }, conflict_modal: { - title: 'An item named {{textValue}} already exists in this location.', - keep_info: '"Keep both" will retain both files (and their version histories) in this location.', - replace_info: '"Replace" will overwrite the existing file in this location. You will lose previous versions of the overwritten file. You will keep previous versions of the moved file.', - keep_button: 'Keep both', - replace_button: 'Replace', + title: '{{textValue}}という名前のアイテムは、この場所に既に存在します。', + keep_info: '"両方を保持"は、両方のファイル(およびそのバージョン履歴)をこの場所に保持します。', + replace_info: '"置換"は、この場所にある既存のファイルを上書きします。 上書きされたファイルの以前のバージョンは失われます。 移動したファイルの以前のバージョンを保持します。', + keep_button: '両方を保持', + replace_button: '置換', }, move_modal: { - title: 'Move file to project', - move_button: 'Move file', + title: 'プロジェクトへファイルを移動', + move_button: 'ファイルを移動', }, }, dashboard: { - page_title: 'Home', title: 'ダッシュボード', create_new_project_button: '新規プロジェクト作成', quicksearch: { @@ -162,38 +164,95 @@ export default { button: 'Prereg Challengeをはじめる', }, }, -<<<<<<< HEAD -======= + move_to_project: { + create_new_project: '新規プロジェクトの作成', + connect_to_existing: '既存のOSFプロジェクトへファイルを接続する', + enter_project_title: 'プロジェクトタイトルの入力', + new_project_message: 'ファイル用の新しいパブリックプロジェクトを作成することを選択しました。 プロジェクトがプライベートにならない限り、ユーザーは引き続きファイルにアクセスできます。', + choose_project: 'プロジェクトを選択', + project_select_message: '表示されるプロジェクトのリストは、書き込みアクセス権を持つプロジェクトとコンポーネントです。 登録はここには含まれていません。', + no_projects_exist_error: '利用可能なプロジェクトはありません。 新しいプロジェクトを作成するには、元に戻ってください。', + could_not_create_project: 'プロジェクトを作成できませんでした。 もう一度お試しください。', + convert_or_copy_message: { + project: '"ファイルの移動"をクリックすると、すぐにOSFプロジェクトが変更され、ファイルが移動されます。', + component: '"ファイルの移動"をクリックすると、すぐにOSFコンポーネントが変更され、ファイルが移動されます。', + }, + no_longer_public_warning: { + project: '非公開プロジェクトに移動されたファイルは公開されなくなり、他のユーザーによって発見されなくなります。', + component: 'プライベートコンポーネントに移動されたファイルは公開されなくなり、他のユーザーによって検出されなくなります。', + }, + file_successfully_moved: 'ファイルは正常に移動されました!', + could_not_move_file: 'ファイルを移動できませんでした。 もう一度お試しください', + keep_working_here: 'ここで作業を続ける', + go_to_new_project: '新規プロジェクトへ移動', + go_to_component: 'コンポーネントへ移動', + go_to_project: 'プロジェクトへ移動', + }, + navbar: { + add: '追加する', + add_a_preprint: '{{preprintWords.preprint}}を追加', + browse: '探す', + cancel_search: '検索をキャンセル', + donate: '寄付する', + go_home: 'ホームへ行く', + my_projects: 'マイプロジェクト', + my_quick_files: 'マイクイックファイル', + reviews: 'マイレビュー', + search: '検索', + search_help: '検索ヘルプ', + search_the_OSF: 'OSF中を検索', + send_search: '検索クエリの送信', + support: 'サポート', + toggle_primary: 'トグル・プライマリー・ナビゲーション', + toggle_secondary: 'トグル・セカンダリー・ナビゲーション', + }, + auth_dropdown: { + log_out: 'ログアウト', + my_profile: 'マイプロファイル', + osf_support: 'OSFサポート', + settings: '設定', + sign_up: '新規登録', + sign_in: 'ログイン', + user_gravatar: 'ユーザーgravatar', + toggle_auth_dropdown: 'トグルauthドロップダウン', + }, + search_help_modal: { + close: '閉じる', + search_help: 'ヘルプを検索', + queries: 'クエリ', + search_uses_the: '検索では', + search_syntax: 'search syntax', + help_description: 'これは多くのオプションを提供しますが、非常にシンプルにすることもできます。 有効な検索の例は次のとおりです。', + }, support: { - title: 'Support', - faq_title: 'Frequently Asked Questions', - faq_paragraph: 'How can it be free? How will the OSF be useful to my research? What is a registration? Get your questions about the Open Science Framework answered on our ', - faq_link_text: 'FAQ page.', - faq_button: 'Visit FAQ', - guides_title: 'OSF Guides', - guides_paragraph_1: 'Learn how to use the OSF for improving your research workflow. Read our ', - guides_link_text: 'Guides', - guides_paragraph_2: 'for step-by-step screenshots that show you the basics of project structures, version control, privacy, files, add-on support, and more!', - guides_button: 'Visit Guides', - contact_title: 'Get in Touch', - contact_technical: 'For emails about technical support:', - contact_questions: 'For all other questions or comments', - prereg_title: 'Do you have Prereg Challenge related questions?', - prereg_paragraph_1: 'Check out our ', - prereg_link_text: 'Prereg section', - prereg_paragraph_2: ' on the cos.io website.', - status_title: 'Are you experiencing downtime with our services?', - status_paragraph_1: 'Check out our', - status_link_text: 'status page', - status_paragraph_2: 'for updates on how our services are operating.', - consultation_title: 'Are you looking for statistics consultations?', - consultation_paragraph: 'COS provides statistics consulation for free. To learn more about this service visit the', - consultation_link_text: 'COS statistics consulting page.', - social_title: 'Other ways to get help', - social_twitter: 'Ask us a question on twitter', - social_mailing: 'Join our mailing list', - social_facebook: 'Follow us on Facebook', - social_github: 'Connect with COS on GitHub', + title: 'サポート', + faq_title: 'よくあるご質問', + faq_paragraph: 'どうすれば無料になるのですか? OSFは私の研究にどのように役立つでしょうか? 登録とは何ですか? Open Science Frameworkに関する質問は、', + faq_link_text: 'FAQページ.', + faq_button: 'FAQを訪問', + guides_title: 'OSFガイド', + guides_paragraph_1: '研究ワークフローを改善するためにOSFを使用する方法を学びます。 私たちを読む', + guides_link_text: 'ガイド', + guides_paragraph_2: 'プロジェクトの構造、バージョン管理、プライバシー、ファイル、アドオンサポートなどの基本を示すステップバイステップのスクリーンショットがあります。', + guides_button: 'ガイドを訪問', + contact_title: '連絡する', + contact_technical: 'テクニカルサポートの電子メール:', + contact_questions: 'その他の質問やコメント', + prereg_title: 'Prereg Challengeに関する質問はありますか?', + prereg_paragraph_1: '調査して下さい、我々の', + prereg_link_text: 'Preregセクション', + prereg_paragraph_2: 'cos.io ウェブサイト上を', + status_title: 'あなたは私たちのサービスでダウンタイムを経験していますか?', + status_paragraph_1: '調査して下さい、我々の', + status_link_text: 'ステータスページ', + status_paragraph_2: '私たちのサービスがどのように動作しているかについての更新情報。', + consultation_title: '統計コンサルティングをお探しですか', + consultation_paragraph: 'COSは統計コンサルティングを無料で提供しています。 このサービスの詳細については、', + consultation_link_text: 'COS統計コンサルティングページ。', + social_title: '助けを得るための他の方法', + social_twitter: 'Twitterで質問する', + social_mailing: '我々のメーリングリストへ参加', + social_facebook: 'Facebookでフォロー', + social_github: 'GitHubのCOSとコンタクト', }, ->>>>>>> cos_ember-osf-web/develop }; From 504d52b121b01193d608fa3e43fec270599958b0 Mon Sep 17 00:00:00 2001 From: Yusuke Komiyama Date: Thu, 8 Mar 2018 11:52:39 +0900 Subject: [PATCH 3/5] modify .gitignore --- .gitignore | 2 + app/locales/ja/translations.ts | 154 --------------------------------- 2 files changed, 2 insertions(+), 154 deletions(-) delete mode 100644 app/locales/ja/translations.ts diff --git a/.gitignore b/.gitignore index 848c61dfc4e..452f89ab05f 100644 --- a/.gitignore +++ b/.gitignore @@ -18,3 +18,5 @@ testem.log yarn-debug.log yarn-error.log config/local.js + +.DS_Store diff --git a/app/locales/ja/translations.ts b/app/locales/ja/translations.ts deleted file mode 100644 index 42d79da4a8a..00000000000 --- a/app/locales/ja/translations.ts +++ /dev/null @@ -1,154 +0,0 @@ -/* tslint:disable:object-literal-sort-keys max-line-length */ - -export default { - general: { - share: '共有', - embed: '埋込み', - download: 'ダウンロード', - delete: '削除', - view: '表示', - edit: '編集', - cancel: 'キャンセル', - revisions: '修正', - md5: 'MD5', - date: '日付', - sha2: 'SHA2', - title: 'タイトル', - contributors: '共同研究者', - modified: '編集', - description: '説明', - create: '作成', - and: 'and', - more: '詳細', - }, - quickfiles: { - title: '{{user-name}} のクイックファイル', - description: 'ここにアップロードされたファイルは一般公開されており、共有リンクを使用して他のユーザーと簡単に共有できます。', - feedback_dialog_text: 'クイックファイルの考え方を我々に教えて下さい', - transition_auth: 'クイックファイルを表示するには、ログインしている必要があります。ログインページにリダイレクトします。', - }, - feedback: { - button_text: 'フィードバック', - placeholder: 'フィードバックをシェア', - follow_up_label: 'OSFを改良するため、また機会があれば我々に連絡して下さい。', - title: 'フィードバックの送信', - confirm_button_text: '送信', - thank_you: 'ありがとうございます', - success: 'あなたからのフィードバックは提出されました', - dismiss: '了解', - }, - file_detail: { - version: { - id: 'バージョンID', - title: '(バージョン: {{version-number}})', - }, - embed: { - dynamic: 'JavaScriptを用いたダイレクトレンダーiframe', - direct: '高さと幅を固定したダイレクトiframe', - }, - tags: 'タグ:', - toggle: 'トグルビュー:', - delete_file: { - question: 'ファイルを削除しますか?', - confirm: '本当に {{file-name}} を削除してもよろしいでしょうか?', - }, - sha2_description: 'SHA-2は、データの完全性を検証するために使用されるNSAによって設計された暗号ハッシュ関数です。', - md5_description: 'MD5は、データの完全性を検証するために使用されるアルゴリズムです。', - // toast messages - delete_success: 'ファイルが削除されました', - delete_fail: 'エラー、ファイルを削除できません', - save_success: 'ファイルが保存されました', - save_fail: 'エラー、ファイルを保存できません', - }, - dashboard: { - title: 'ダッシュボード', - create_new_project_button: '新規プロジェクト作成', - quicksearch: { - search: 'マイプロジェクト内の検索', - other_links: 'あなたの仕事を整理したり、OSFを検索するには、My Projectsに行きます。', - no_results: 'プロジェクトが見つかりません!', - no_projects: { - line1: 'まだプロジェクトがありません。画面右上のボタンからプロジェクトを作成して下さい。', - line2: 'この機能を使用すると、プロジェクトを検索してすばやくアクセスできます。', - preview_alt: 'クイックプロジェクトの全画面プレビュー', - }, - private_parent: '非公開プロジェクト / ', - private_grandparent: '非公開プロジェクト / 非公開 / ', - }, - noteworthy: { - description: '公開プロジェクトの検索', - new_and_noteworthy: '新着と注目', - failed_noteworthy: '"新着と注目"プロジェクトのローディングに失敗しました。', - most_popular: '最人気', - failed_popular: '"最人気"プロジェクトのローディングに失敗しました。', - search_more: '更にプロジェクトを探す', - by: 'by', - }, - meetings: { - title: '学会や研究会をホスティングしますか?', - description: 'OSF for Meetings サービスを使用し、学術集会への投稿するためのポータルを提供します。', - button: '学術集会の表示', - }, - preprints: { - title: '最新の研究を閲覧', - description: 'さまざまな研究分野をカバーするOSFで、ホストされている最新のプレプリントをご覧ください。', - button: 'プレプリントの表示', - }, - }, - new_project: { - header: '新規プロジェクト作成', - title_placeholder: 'プロジェクトタイトルの入力', - more: 'More', - affiliation: '所属', - remove_all: '全件削除', - select_all: '全件選択', - no_matches: '一致しません', - description_placeholder: 'プロジェクト説明の入力', - template_title: 'テンプレート(オプション)', - template_search_help: 'プロジェクトを検索するタイピングの開始。テンプレートとしてプロジェクトを選択すると、そのプロジェクトの内容をインポートせずにその構造が新しいプロジェクトに複製されます。', - template_placeholder: 'テンプレートとして使用するプロジェクトを選択する', - success_message: '新規プロジェクトの作成に成功しました', - stay_here: 'この場に留まる', - go_to_new: '作成したプロジェクトへ移動', - }, - banners: { - prereg: { - description: 'あなたの次の研究を改善してください。 Prereg Challengeに参加すれば$ 1,000を獲得できます。', - button: 'Prereg Challengeをはじめる', - }, - }, -<<<<<<< HEAD -======= - support: { - title: 'Support', - faq_title: 'Frequently Asked Questions', - faq_paragraph: 'How can it be free? How will the OSF be useful to my research? What is a registration? Get your questions about the Open Science Framework answered on our ', - faq_link_text: 'FAQ page.', - faq_button: 'Visit FAQ', - guides_title: 'OSF Guides', - guides_paragraph_1: 'Learn how to use the OSF for improving your research workflow. Read our ', - guides_link_text: 'Guides', - guides_paragraph_2: 'for step-by-step screenshots that show you the basics of project structures, version control, privacy, files, add-on support, and more!', - guides_button: 'Visit Guides', - contact_title: 'Get in Touch', - contact_technical: 'For emails about technical support:', - contact_questions: 'For all other questions or comments', - prereg_title: 'Do you have Prereg Challenge related questions?', - prereg_paragraph_1: 'Check out our ', - prereg_link_text: 'Prereg section', - prereg_paragraph_2: ' on the cos.io website.', - status_title: 'Are you experiencing downtime with our services?', - status_paragraph_1: 'Check out our', - status_link_text: 'status page', - status_paragraph_2: 'for updates on how our services are operating.', - consultation_title: 'Are you looking for statistics consultations?', - consultation_paragraph: 'COS provides statistics consulation for free. To learn more about this service visit the', - consultation_link_text: 'COS statistics consulting page.', - social_title: 'Other ways to get help', - social_twitter: 'Ask us a question on twitter', - social_mailing: 'Join our mailing list', - social_facebook: 'Follow us on Facebook', - social_github: 'Connect with COS on GitHub', - }, ->>>>>>> cos_ember-osf-web/develop -}; From cacdce18241b1013d5e187f14bf936d12f7c384a Mon Sep 17 00:00:00 2001 From: Yusuke Komiyama Date: Thu, 8 Mar 2018 12:36:37 +0900 Subject: [PATCH 4/5] Add new items into ja/translations.ts by NII --- CHANGELOG.md | 4 +- app/adapters/guid.ts | 16 + app/adapters/osf-adapter.js | 5 +- app/application/route.ts | 12 +- app/components/contributor-list/component.js | 25 -- app/components/contributor-list/component.ts | 38 +++ app/components/dashboard-item/component.js | 31 -- app/components/dashboard-item/component.ts | 39 +++ app/components/dashboard-item/template.hbs | 15 +- app/components/feedback-button/styles.scss | 6 +- app/components/file-browser/component.js | 4 + app/components/file-browser/template.hbs | 26 +- app/components/file-renderer/template.hbs | 2 +- app/components/file-share-button/template.hbs | 4 +- app/components/file-version/template.hbs | 2 +- .../{component.js => component.ts} | 65 ++-- .../institution-carousel/styles.scss | 9 +- .../institution-carousel/template.hbs | 8 +- app/components/loading-indicator/component.js | 4 - app/components/loading-indicator/component.ts | 3 + app/components/loading-indicator/template.hbs | 4 +- app/components/new-project-modal/component.js | 78 ----- app/components/new-project-modal/component.ts | 20 ++ app/components/new-project-modal/styles.scss | 9 +- app/components/new-project-modal/template.hbs | 74 +++-- .../component.js | 16 - .../component.ts | 14 + .../template.hbs | 2 +- app/components/osf-mode-footer/component.ts | 15 + app/components/osf-mode-footer/style.scss | 13 + app/components/osf-mode-footer/template.hbs | 6 + app/components/quickfile-nav/component.ts | 2 +- app/components/quickfile-nav/template.hbs | 19 +- app/components/sort-button/component.js | 14 - app/components/sort-button/component.ts | 14 + app/components/sort-button/template.hbs | 4 +- app/components/zoom-to-guid/component.ts | 26 ++ app/components/zoom-to-guid/template.hbs | 13 + app/dashboard/controller.js | 116 ------- app/dashboard/controller.ts | 193 ++++++++++++ app/dashboard/route.js | 4 - app/dashboard/route.ts | 3 + app/dashboard/styles.scss | 5 - app/dashboard/template.hbs | 48 ++- app/{file-detail => guid-file}/controller.ts | 6 +- app/{file-detail => guid-file}/route.ts | 19 +- app/{file-detail => guid-file}/styles.scss | 2 - app/{file-detail => guid-file}/template.hbs | 3 +- app/guid-node/route.ts | 11 + app/guid-node/template.hbs | 2 + app/guid-preprint/route.ts | 11 + app/guid-preprint/template.hbs | 2 + app/guid-registration/route.ts | 11 + app/guid-registration/template.hbs | 2 + app/guid-user/index/route.ts | 8 + app/guid-user/index/template.hbs | 1 + .../quickfiles}/controller.ts | 11 +- .../quickfiles}/route.ts | 4 +- .../quickfiles}/styles.scss | 2 - .../quickfiles}/template.hbs | 6 +- app/guid-user/route.ts | 11 + app/guid-user/template.hbs | 1 + app/helpers/if-filter.js | 4 +- app/locales/en/translations.ts | 25 ++ app/locales/ja/translations.ts | 283 ++++++++++++++++++ app/locales/zh/translations.ts | 45 +++ app/mixins/osf-token-login-route.js | 2 +- app/models/guid.ts | 29 ++ app/not-found/controller.ts | 13 + app/not-found/route.ts | 5 + app/not-found/template.hbs | 8 + app/quickfiles/route.ts | 2 +- app/resolve-guid/resolved-guid-route.ts | 17 ++ app/resolve-guid/route.ts | 18 ++ app/router.ts | 34 ++- app/serializers/guid.ts | 10 + app/styles/_accessibility.scss | 20 ++ app/styles/_variables.scss | 7 +- app/styles/app.scss | 1 + config/environment.js | 205 ++++--------- package.json | 9 +- testem.js | 4 + .../contributor-list/component-test.js | 11 - .../contributor-list/component-test.ts | 70 +++++ .../dashboard-item/component-test.js | 4 +- .../new-project-modal/component-test.js | 11 - .../new-project-modal/component-test.ts | 41 +++ .../component-test.js | 8 +- .../components/sort-button/component-test.js | 15 - .../components/sort-button/component-test.ts | 17 ++ .../controller-test.ts | 2 +- .../quickfiles}/controller-test.ts | 2 +- .../quickfiles}/route-test.ts | 2 +- tsconfig.json | 1 + tslint.json | 5 +- yarn.lock | 195 +++++++++++- 96 files changed, 1604 insertions(+), 664 deletions(-) create mode 100644 app/adapters/guid.ts delete mode 100644 app/components/contributor-list/component.js create mode 100644 app/components/contributor-list/component.ts delete mode 100644 app/components/dashboard-item/component.js create mode 100644 app/components/dashboard-item/component.ts rename app/components/institution-carousel/{component.js => component.ts} (50%) delete mode 100644 app/components/loading-indicator/component.js create mode 100644 app/components/loading-indicator/component.ts delete mode 100644 app/components/new-project-modal/component.js create mode 100644 app/components/new-project-modal/component.ts delete mode 100644 app/components/noteworthy-and-popular-project/component.js create mode 100644 app/components/noteworthy-and-popular-project/component.ts create mode 100644 app/components/osf-mode-footer/component.ts create mode 100644 app/components/osf-mode-footer/style.scss create mode 100644 app/components/osf-mode-footer/template.hbs delete mode 100644 app/components/sort-button/component.js create mode 100644 app/components/sort-button/component.ts create mode 100644 app/components/zoom-to-guid/component.ts create mode 100644 app/components/zoom-to-guid/template.hbs delete mode 100644 app/dashboard/controller.js create mode 100644 app/dashboard/controller.ts delete mode 100644 app/dashboard/route.js create mode 100644 app/dashboard/route.ts rename app/{file-detail => guid-file}/controller.ts (95%) rename app/{file-detail => guid-file}/route.ts (55%) rename app/{file-detail => guid-file}/styles.scss (99%) rename app/{file-detail => guid-file}/template.hbs (98%) create mode 100644 app/guid-node/route.ts create mode 100644 app/guid-node/template.hbs create mode 100644 app/guid-preprint/route.ts create mode 100644 app/guid-preprint/template.hbs create mode 100644 app/guid-registration/route.ts create mode 100644 app/guid-registration/template.hbs create mode 100644 app/guid-user/index/route.ts create mode 100644 app/guid-user/index/template.hbs rename app/{user-quickfiles => guid-user/quickfiles}/controller.ts (58%) rename app/{user-quickfiles => guid-user/quickfiles}/route.ts (89%) rename app/{user-quickfiles => guid-user/quickfiles}/styles.scss (82%) rename app/{user-quickfiles => guid-user/quickfiles}/template.hbs (60%) create mode 100644 app/guid-user/route.ts create mode 100644 app/guid-user/template.hbs create mode 100644 app/locales/ja/translations.ts create mode 100644 app/models/guid.ts create mode 100644 app/not-found/controller.ts create mode 100644 app/not-found/route.ts create mode 100644 app/not-found/template.hbs create mode 100644 app/resolve-guid/resolved-guid-route.ts create mode 100644 app/resolve-guid/route.ts create mode 100644 app/serializers/guid.ts create mode 100644 app/styles/_accessibility.scss delete mode 100644 tests/integration/components/contributor-list/component-test.js create mode 100644 tests/integration/components/contributor-list/component-test.ts delete mode 100644 tests/integration/components/new-project-modal/component-test.js create mode 100644 tests/integration/components/new-project-modal/component-test.ts delete mode 100644 tests/integration/components/sort-button/component-test.js create mode 100644 tests/integration/components/sort-button/component-test.ts rename tests/unit/{file-detail => guid-file}/controller-test.ts (80%) rename tests/unit/{user-quickfiles => guid-user/quickfiles}/controller-test.ts (75%) rename tests/unit/{user-quickfiles => guid-user/quickfiles}/route-test.ts (73%) diff --git a/CHANGELOG.md b/CHANGELOG.md index 39012b0eed8..2e696aa74a4 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,10 +7,12 @@ and this project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0. ## [Unreleased] ### Added - TypeScript: Add ember-cli-typscript and ember-cli-tslint -- CSS: Add _typography.scss with responsive font styling +- CSS: Add _typography.scss with responsive font styling and _accessibility.scss for accessibility-related styling +- Addon: ember-a11y-testing ### Changed - TypeScript: Rename files to .ts +- Modified several templates and css properties for increased accessibility ## [0.2.0] - 2018-02-14 ### Added diff --git a/app/adapters/guid.ts b/app/adapters/guid.ts new file mode 100644 index 00000000000..ed98fa6a90c --- /dev/null +++ b/app/adapters/guid.ts @@ -0,0 +1,16 @@ +import OsfAdapter from './osf-adapter'; + +export default class Guid extends OsfAdapter.extend({ +}) { + buildQuery(this: Guid): any { + const query = this._super(...arguments) || {}; + query.resolve = false; + return query; + } +} + +declare module 'ember-data' { + interface AdapterRegistry { + 'guid': Guid; + } +} diff --git a/app/adapters/osf-adapter.js b/app/adapters/osf-adapter.js index 538982818e5..794b267a364 100644 --- a/app/adapters/osf-adapter.js +++ b/app/adapters/osf-adapter.js @@ -4,13 +4,12 @@ import { underscore, capitalize } from '@ember/string'; import $ from 'jquery'; import { getWithDefault } from '@ember/object'; import { merge } from '@ember/polyfills'; -import Ember from 'ember'; import DS from 'ember-data'; import config from 'ember-get-config'; import GenericDataAdapterMixin from 'ember-osf-web/mixins/generic-data-adapter'; -import { singularize } from 'ember-inflector'; +import { pluralize, singularize } from 'ember-inflector'; /** * @module ember-osf-web @@ -328,6 +327,6 @@ export default DS.JSONAPIAdapter.extend(GenericDataAdapterMixin, { }, pathForType(modelName) { const underscored = underscore(modelName); - return Ember.String.pluralize(underscored); + return pluralize(underscored); }, }); diff --git a/app/application/route.ts b/app/application/route.ts index 188c39cecf6..7ecb1b9f178 100644 --- a/app/application/route.ts +++ b/app/application/route.ts @@ -3,6 +3,12 @@ import { inject as service } from '@ember/service'; import OSFAgnosticAuthRouteMixin from 'ember-osf-web/mixins/osf-agnostic-auth-route'; export default class Application extends Route.extend(OSFAgnosticAuthRouteMixin, { + beforeModel(...args) { + this.get('moment').setTimeZone('UTC'); + + return this._super(...args); + }, + actions: { didTransition() { Object.assign(window, { prerenderReady: true }); @@ -12,12 +18,6 @@ export default class Application extends Route.extend(OSFAgnosticAuthRouteMixin, }) { moment = service('moment'); - beforeModel(...args) { - this.get('moment').setTimeZone('UTC'); - - return this._super(...args); - } - afterModel() { const availableLocales: [string] = this.get('i18n.locales').toArray(); let locale: string; diff --git a/app/components/contributor-list/component.js b/app/components/contributor-list/component.js deleted file mode 100644 index 707ea290cfb..00000000000 --- a/app/components/contributor-list/component.js +++ /dev/null @@ -1,25 +0,0 @@ -import { computed } from '@ember/object'; -import { inject as service } from '@ember/service'; -import Component from '@ember/component'; - -export default Component.extend({ - tagName: 'span', - i18n: service(), - - contributorList: computed('node.contributors', function() { - let contribs = this.get('node.contributors'); - if (!contribs) { - return; - } - const and = this.get('i18n').t('general.and'); - contribs = contribs.toArray(); - const len = contribs.length; - const name = index => contribs[index].get('users.familyName') || contribs[index].get('users.givenName'); - switch (len) { - case 1: return name(0); - case 2: return `${name(0)} ${and} ${name(1)}`; - case 3: return `${name(0)}, ${name(1)}, ${and} ${name(2)}`; - default: return `${name(0)}, ${name(1)}, ${name(2)}, ${and} ${len - 3} ${this.get('i18n').t('general.more')}`; - } - }), -}); diff --git a/app/components/contributor-list/component.ts b/app/components/contributor-list/component.ts new file mode 100644 index 00000000000..57cb97e07cb --- /dev/null +++ b/app/components/contributor-list/component.ts @@ -0,0 +1,38 @@ +import { A } from '@ember/array'; +import Component from '@ember/component'; +import { computed } from '@ember/object'; +import { inject as service } from '@ember/service'; + +export default class ContributorList extends Component.extend({ + tagName: 'span', + + contributors: A([]), +}) { + i18n = service('i18n'); + + contributorList = computed('contributors.[]', function(): string { + const contributors = this.get('contributors').toArray(); + + if (!(contributors && contributors.length)) { + return ''; + } + + const len = contributors.length; + const max = 3; + + const names: string[] = contributors + .slice(0, max) + .map(c => c.get('users.familyName') || c.get('users.givenName')); + + const i18n = this.get('i18n'); + const and = i18n.t('general.and'); + const more = i18n.t('general.more'); + + if (len < 3) { + return names.join(` ${and} `); + } + + const last = len <= max ? names.splice(-1) : `${len - max} ${more}`; + return [...names, `${and} ${last}`].join(', '); + }); +} diff --git a/app/components/dashboard-item/component.js b/app/components/dashboard-item/component.js deleted file mode 100644 index d3d02922927..00000000000 --- a/app/components/dashboard-item/component.js +++ /dev/null @@ -1,31 +0,0 @@ -import { computed } from '@ember/object'; -import moment from 'moment'; -import Component from '@ember/component'; - -export default Component.extend({ - private: computed('node.root', function() { - return !this.get('node.root.id'); - }), - ancestry: computed('node', 'node.{parent,root}', function() { - const rootId = this.get('node.root.id'); - const rootLink = this.get('node.root.links.self'); - const parentId = this.get('node.parent.id'); - const grandpaLink = this.get('node.parent.links.relationships.parent.links.related.href'); - if (!rootId) { - return parentId ? this.get('node.parent.title') : ''; - } - if (!parentId) { - return ''; - } - if (parentId === rootId) { - return `${this.get('node.root.title')} / `; - } - if (grandpaLink === rootLink) { - return `${this.get('node.root.title')} / ${this.get('node.parent.title')} / `; - } - return `${this.get('node.root.title')} / ... / ${this.get('node.parent.title')} / `; - }), - date: computed('node.dateModified', function() { - return moment(this.get('node.dateModified')).utc().format('YYYY-MM-DD h:mm A'); - }), -}); diff --git a/app/components/dashboard-item/component.ts b/app/components/dashboard-item/component.ts new file mode 100644 index 00000000000..170d69ddcdf --- /dev/null +++ b/app/components/dashboard-item/component.ts @@ -0,0 +1,39 @@ +import Component from '@ember/component'; +import { computed } from '@ember/object'; +import { alias, not } from '@ember/object/computed'; +import { inject as service } from '@ember/service'; +import moment from 'moment'; + +export default class DashboardItem extends Component { + i18n = service('i18n'); + + private = not('node.root.id'); + + ancestry = computed('node', 'node.{parent,root}', function(): string { + const rootId = this.get('node.root.id'); + const rootLink = this.get('node.root.links.self'); + const parentId = this.get('node.parent.id'); + const grandpaLink = this.get('node.parent.links.relationships.parent.links.related.href'); + + if (!rootId || !parentId) { + return parentId ? this.get('node.parent.title') : ''; + } + + const items = [this.get('node.root.title')]; + const separator = ' / '; + + if (grandpaLink === rootLink) { + items.push(this.get('node.parent.title')); + } else if (parentId !== rootId) { + items.push(this.get('i18n').t('general.ellipses'), this.get('node.parent.title')); + } + + return [...items, ''].join(separator); + }); + + contributors = alias('node.contributors'); + + date = computed('node.dateModified', function(): string { + return moment(this.get('node.dateModified')).utc().format('YYYY-MM-DD h:mm A'); + }); +} diff --git a/app/components/dashboard-item/template.hbs b/app/components/dashboard-item/template.hbs index dfe44bb1e5d..5bedcb686e0 100644 --- a/app/components/dashboard-item/template.hbs +++ b/app/components/dashboard-item/template.hbs @@ -4,20 +4,15 @@
- {{#if private}} - {{#if ancestry}} - {{t 'dashboard.quicksearch.private_parent'}}{{ancestry}} - {{else}} - {{t 'dashboard.quicksearch.private_grandparent'}} - {{/if}} - {{else}} - {{ancestry}} - {{/if}} + + {{ancestry}} {{node.title}}
-
{{contributor-list node=node}}
+
{{contributor-list contributors=contributors}}
{{date}}
diff --git a/app/components/feedback-button/styles.scss b/app/components/feedback-button/styles.scss index 29ed7a8bab2..a5d8909a69a 100644 --- a/app/components/feedback-button/styles.scss +++ b/app/components/feedback-button/styles.scss @@ -1,8 +1,8 @@ .FeedbackButton { - background-color: $btn-success-bg; + background-color: $brand-success; border-radius: $border-radius-base $border-radius-base 0 0; bottom: 0; - color: $btn-success-color; + color: $color-text-white; cursor: pointer; padding: 4px 7px; position: fixed; @@ -27,7 +27,7 @@ .FeedbackButton:hover, .FeedbackButton--open { - background-color: darken($btn-success-bg, 10%); + background-color: $brand-success; padding-bottom: 10px; } diff --git a/app/components/file-browser/component.js b/app/components/file-browser/component.js index dd1cdd248d3..3ab10546379 100644 --- a/app/components/file-browser/component.js +++ b/app/components/file-browser/component.js @@ -310,6 +310,10 @@ export default Ember.Component.extend(Analytics, { const downloadLink = this.get('selectedItems.firstObject.links.download'); window.location = downloadLink; }, + downloadZip() { + const downloadLink = this.get('downloadUrl'); + window.location = downloadLink; + }, _deleteItem(item) { item.destroyRecord().then(() => { this.flash(item, 'This file has been deleted.', 'danger'); diff --git a/app/components/file-browser/template.hbs b/app/components/file-browser/template.hbs index 4c1ce21475f..1f8d93f8fb5 100644 --- a/app/components/file-browser/template.hbs +++ b/app/components/file-browser/template.hbs @@ -12,20 +12,20 @@ {{else}}
{{#if (and edit (if-filter 'upload-button' display))}} - + {{/if}} {{#if selectedItems}} {{! TODO: show available actions for selected files }} {{#if (eq selectedItems.length 1)}} {{#if (if-filter 'share-button' display)}} - {{/if}} {{#if (if-filter 'download-button' display)}} - + {{/if}} {{#if (if-filter 'view-button' display)}} - + {{/if}} {{#if (if-filter 'move-button' display)}} - + {{/if}} {{#if (and edit (if-filter 'delete-button' display))}} - + {{/if}} {{#if (and edit (if-filter 'rename-button' display))}} - + {{/if}} {{else}} {{#if (and edit (if-filter 'delete-button' display))}} - + {{/if}} {{/if}} {{else}} {{#if (and (eq browserState 'show') (if-filter 'download-button' display))}} - {{fa-icon "download"}} {{t 'file_browser.download_zip'}} + {{/if}} {{/if}} - - {{#if (if-filter 'info-button' display)}}{{/if}} + + {{#if (if-filter 'info-button' display)}}{{/if}}
{{/if}}
diff --git a/app/components/file-renderer/template.hbs b/app/components/file-renderer/template.hbs index dfb684754ce..011ea5dbe5a 100644 --- a/app/components/file-renderer/template.hbs +++ b/app/components/file-renderer/template.hbs @@ -1,4 +1,4 @@ {{#if download}} - {{/if}} diff --git a/app/components/file-share-button/template.hbs b/app/components/file-share-button/template.hbs index 99d946dbaf1..cfbe1e3ac39 100644 --- a/app/components/file-share-button/template.hbs +++ b/app/components/file-share-button/template.hbs @@ -1,6 +1,6 @@ - + + {{/modal.footer}} {{else}} {{#modal.body}} - -

{{t 'new_project.success_message'}}

+ +

+ {{t 'new_project.success_message'}} +

{{/modal.body}} {{#modal.footer}} - - {{t 'new_project.go_to_new'}} + + + {{t 'new_project.go_to_new'}} + {{/modal.footer}} {{/if}} -{{/bs-modal}} \ No newline at end of file +{{/bs-modal}} diff --git a/app/components/noteworthy-and-popular-project/component.js b/app/components/noteworthy-and-popular-project/component.js deleted file mode 100644 index 508fc7983dd..00000000000 --- a/app/components/noteworthy-and-popular-project/component.js +++ /dev/null @@ -1,16 +0,0 @@ -import { computed } from '@ember/object'; -import Component from '@ember/component'; - -export default Component.extend({ - didInsertElement() { - this._super(...arguments); - this.$('.prevent-overflow').tooltip(); - }, - compactDescription: computed('project.description', function() { - const desc = this.get('project.description'); - if (!desc) { - return ''; - } - return desc.length > 115 ? `${desc.slice(0, 111)}...` : desc; - }), -}); diff --git a/app/components/noteworthy-and-popular-project/component.ts b/app/components/noteworthy-and-popular-project/component.ts new file mode 100644 index 00000000000..aedd5dff681 --- /dev/null +++ b/app/components/noteworthy-and-popular-project/component.ts @@ -0,0 +1,14 @@ +import Component from '@ember/component'; +import { computed } from '@ember/object'; + +export default class NoteworthyAndPopularProject extends Component.extend({ + didInsertElement(...args) { + this._super(...args); + this.$('.prevent-overflow').tooltip(); + }, +}) { + compactDescription = computed('project.description', function(): string { + const desc = this.get('project.description') || ''; + return desc.length > 115 ? `${desc.slice(0, 111)}\u2026` : desc; + }); +} diff --git a/app/components/noteworthy-and-popular-project/template.hbs b/app/components/noteworthy-and-popular-project/template.hbs index 1c443ba18bd..aab3b6fa07c 100644 --- a/app/components/noteworthy-and-popular-project/template.hbs +++ b/app/components/noteworthy-and-popular-project/template.hbs @@ -1,7 +1,7 @@
{{project.title}}
- {{t 'dashboard.noteworthy.by'}} {{contributor-list node=project}} + {{t 'dashboard.noteworthy.by'}} {{contributor-list contributors=project.contributors}}

{{project.description}}

\ No newline at end of file diff --git a/app/components/osf-mode-footer/component.ts b/app/components/osf-mode-footer/component.ts new file mode 100644 index 00000000000..bb8c8825778 --- /dev/null +++ b/app/components/osf-mode-footer/component.ts @@ -0,0 +1,15 @@ +import Component from '@ember/component'; +import config from 'ember-get-config'; + +/** + * @module ember-osf-web + * @submodule components + */ + +/** + * If development mode, display a red banner in the footer + * @class osf-mode-footer + */ +export default class OsfModeFooter extends Component { + isDevMode: boolean = config.OSF.devMode; +} diff --git a/app/components/osf-mode-footer/style.scss b/app/components/osf-mode-footer/style.scss new file mode 100644 index 00000000000..9f06a90c0db --- /dev/null +++ b/app/components/osf-mode-footer/style.scss @@ -0,0 +1,13 @@ +.DevBanner { + position: fixed; + bottom: 0; + left: 0; + border-top-right-radius: 8px; + background-color: $color-bg-red; + color: $color-text-white; + padding: 0.5em; + + strong { + text-transform: uppercase; + } +} diff --git a/app/components/osf-mode-footer/template.hbs b/app/components/osf-mode-footer/template.hbs new file mode 100644 index 00000000000..42586a64c80 --- /dev/null +++ b/app/components/osf-mode-footer/template.hbs @@ -0,0 +1,6 @@ +{{#if isDevMode}} +
+ {{t 'general.warning'}}: {{t 'osf_mode_footer.dev_mode'}} + {{zoom-to-guid}} +
+{{/if}} diff --git a/app/components/quickfile-nav/component.ts b/app/components/quickfile-nav/component.ts index 7ccaf11084d..539d40f4a8f 100644 --- a/app/components/quickfile-nav/component.ts +++ b/app/components/quickfile-nav/component.ts @@ -3,7 +3,7 @@ import Component from '@ember/component'; export default class QuickfileNav extends Component.extend({ tagName: 'nav', - classNames: ['navbar', 'osf-project-navbar', 'row'], + classNames: ['row'], attributeBindings: ['role'], diff --git a/app/components/quickfile-nav/template.hbs b/app/components/quickfile-nav/template.hbs index 17ea687d445..532ccb56db3 100644 --- a/app/components/quickfile-nav/template.hbs +++ b/app/components/quickfile-nav/template.hbs @@ -1,13 +1,10 @@
- +

+ {{#link-to + 'guid-user-quickfiles' + user.id + }} + {{t 'quickfiles.title' user-name=user.fullName}} + {{/link-to}} +

diff --git a/app/components/sort-button/component.js b/app/components/sort-button/component.js deleted file mode 100644 index 0c5be8e4d5f..00000000000 --- a/app/components/sort-button/component.js +++ /dev/null @@ -1,14 +0,0 @@ -import Component from '@ember/component'; - -export default Component.extend({ - tagName: 'span', - actions: { - sort(sortOrder) { - if (this.get('sortBy')) { - this.get('sortAction')(this.get('sortBy'), sortOrder); - } else { - this.get('sortAction')(sortOrder); - } - }, - }, -}); diff --git a/app/components/sort-button/component.ts b/app/components/sort-button/component.ts new file mode 100644 index 00000000000..39c9fab391c --- /dev/null +++ b/app/components/sort-button/component.ts @@ -0,0 +1,14 @@ +import Component from '@ember/component'; +import { computed } from '@ember/object'; + +export default class SortButton extends Component.extend({ + tagName: 'span', + + classNames: ['sort-group'], +}) { + sortBy: string; + + sortByDesc = computed('sortBy', function(): string { + return `-${this.get('sortBy')}`; + }); +} diff --git a/app/components/sort-button/template.hbs b/app/components/sort-button/template.hbs index 8ef4f394cfe..db8795fedde 100644 --- a/app/components/sort-button/template.hbs +++ b/app/components/sort-button/template.hbs @@ -1,6 +1,6 @@ - - diff --git a/app/components/zoom-to-guid/component.ts b/app/components/zoom-to-guid/component.ts new file mode 100644 index 00000000000..e045ce98e56 --- /dev/null +++ b/app/components/zoom-to-guid/component.ts @@ -0,0 +1,26 @@ +import Component from '@ember/component'; +import { inject as service } from '@ember/service'; + +/** + * Component to allow easily transitioning to any GUID without leaving the + * Ember app. Intended for dev use, testing routes which are not yet + * configured to use Ember in the OSF backend. + * + * Displays an icon link/button that pops up a modal form. + * @class zoom-to-guid + */ +export default class ZoomToGuid extends Component.extend({ + tagName: 'span', + + actions: { + zoom(this: ZoomToGuid): void { + this.get('router').transitionTo('resolve-guid', this.get('guid')); + this.set('showModal', false); + }, + }, +}) { + router = service('router'); + + guid: string = ''; + showModal: boolean = false; +} diff --git a/app/components/zoom-to-guid/template.hbs b/app/components/zoom-to-guid/template.hbs new file mode 100644 index 00000000000..24295f16ee7 --- /dev/null +++ b/app/components/zoom-to-guid/template.hbs @@ -0,0 +1,13 @@ +{{fa-icon 'rocket'}} +{{#if showModal}} + {{#bs-modal-simple + onHide=(action (mut showModal) false) + title=(t 'zoom_to_guid.title') + closeTitle=(t 'general.cancel') + submitTitle=(t 'zoom_to_guid.zoom') + }} + {{#bs-form onSubmit=(action 'zoom')}} + {{input type='text' value=guid placeholder=(t 'zoom_to_guid.placeholder') autofocus=true}} + {{/bs-form}} + {{/bs-modal-simple}} +{{/if}} diff --git a/app/dashboard/controller.js b/app/dashboard/controller.js deleted file mode 100644 index 51ab2c8d964..00000000000 --- a/app/dashboard/controller.js +++ /dev/null @@ -1,116 +0,0 @@ -import { computed } from '@ember/object'; -import { A } from '@ember/array'; -import { inject as service } from '@ember/service'; -import Controller from '@ember/controller'; -import { task, timeout } from 'ember-concurrency'; - -const popularNode = '57tnq'; -const noteworthyNode = 'z3sg2'; - -export default Controller.extend({ - currentUser: service(), - - init() { - this.get('initialLoad').perform(); - this.get('store').findAll('institution').then(institutions => this.set('institutions', institutions)); - this.get('getPopularAndNoteworthy').perform(popularNode, 'popular'); - this.get('getPopularAndNoteworthy').perform(noteworthyNode, 'noteworthy'); - }, - - loading: false, - loadingMore: false, - curPage: 1, - filter: '', - sortBy: 'last_logged', - sortOrder: 'desc', - modalOpen: false, - nodes: A([]), - institutions: A([]), - popular: A([]), - noteworthy: A([]), - - hasMore: computed('nodes.length', function() { - if (this.get('nodes.length') < 10) { - return false; - } - return this.get('totalPages') ? this.get('totalPages') > this.get('curPage') : false; - }), - - actions: { - more() { - this.incrementProperty('curPage'); - this.get('findNodes').perform(true); - }, - sort(by, order) { - this.set('sortBy', by); - this.set('sortOrder', order); - this.get('findNodes').perform(); - }, - setSortBy(by) { - this.set('sortBy', by); - this.get('findNodes').perform(); - }, - setSortOrder(order) { - this.set('sortOrder', order); - this.get('findNodes').perform(); - }, - toggleModal() { - this.toggleProperty('modalOpen'); - }, - reloadNodes() { - this.set('loading', true); - this.set('curPage', 1); - this.get('initialLoad').perform(); - }, - }, - - initialLoad: task(function* () { - const user = yield this.get('currentUser.user'); - if (!user) { return; } - const nodes = yield user.queryHasMany('nodes', { embed: 'contributors' }); - this.set('nodes', nodes.slice()); - this.set('totalNodes', nodes.meta.total); - const pages = Math.ceil(nodes.meta.total / nodes.meta.per_page); - this.set('totalPages', pages); - this.set('loading', false); - }), - - filterNodes: task(function* (term) { - this.set('filter', term); - yield timeout(500); - this.get('findNodes').perform(); - }).restartable(), - - findNodes: task(function* (more) { - this.set(more ? 'loadingMore' : 'loading', true); - const user = yield this.get('currentUser.user'); - const query = { embed: 'contributors', filter: {} }; - query.sort = `${this.get('sortOrder') === 'desc' ? '-' : ''}${this.get('sortBy')}`; - query.filter.title = this.get('filter'); - if (more) { - query.page = this.get('curPage'); - } else { - query.page = 1; - this.set('curPage', 1); - } - const nodes = yield user.queryHasMany('nodes', query); - const pages = Math.ceil(nodes.meta.total / nodes.meta.per_page); - this.set('totalPages', pages); - this.set(more ? 'loadingMore' : 'loading', false); - if (more) { - this.get('nodes').pushObjects(nodes.slice()); - } else { - this.set('nodes', nodes.slice()); - } - }).restartable(), - - getPopularAndNoteworthy: task(function* (id, dest) { - try { - const node = yield this.get('store').findRecord('node', id); - const linkedNodes = yield node.queryHasMany('linkedNodes', { page: { size: 5 }, embed: 'contributors' }); - this.get(dest).pushObjects(linkedNodes.slice()); - } catch (e) { - this.set(`failedLoading-${dest}`, true); - } - }), -}); diff --git a/app/dashboard/controller.ts b/app/dashboard/controller.ts new file mode 100644 index 00000000000..5786bcc2ee8 --- /dev/null +++ b/app/dashboard/controller.ts @@ -0,0 +1,193 @@ +import { A } from '@ember/array'; +import Controller from '@ember/controller'; +import { computed } from '@ember/object'; +import { alias, oneWay } from '@ember/object/computed'; +import { inject as service } from '@ember/service'; +import { task, timeout } from 'ember-concurrency'; +import config from 'ember-get-config'; + +// TODO pull these from the database +const { + dashboard: { + noteworthyNode, + popularNode, + }, +} = config; + +export default class Dashboard extends Controller.extend({ + currentUser: service('currentUser'), + + filter: null, + loading: false, + loadingMore: false, + modalOpen: false, + newNode: null, + page: 1, + sort: '', + + institutions: A([]), + nodes: A([]), + noteworthy: A([]), + popular: A([]), + + user: alias('currentUser.user'), + + institutionsSelected: oneWay('user.institutions'), + + actions: { + more() { + this.get('findNodes').perform(true); + }, + sort(sort) { + this.setProperties({ sort }); + this.get('findNodes').perform(); + }, + selectInstitution(this: NewProjectModal, institution) { + const selected = this.set('institutionsSelected', this.get('institutionsSelected').slice()); + + if (selected.includes(institution)) { + selected.removeObject(institution); + } else { + selected.pushObject(institution); + } + }, + selectAllInstitutions(this: NewProjectModal) { + this.set('institutionsSelected', this.get('user.institutions').slice()); + }, + removeAllInstitutions(this: NewProjectModal) { + this.set('institutionsSelected', A([])); + }, + toggleModal() { + if (this.get('modalOpen')) { + this.set('newNode', null); + } + + this.toggleProperty('modalOpen'); + }, + closeModal(reload = false) { + // Need to explicitly pass reload when the action in the onclick event of a button + // otherwise the first argument is a mouse event which in turn is always truthy + + this.closeModal(); + if (reload) { + this.get('findNodes').perform(); + } + }, + }, + + getInstitutions: task(function* (this: Dashboard) { + this.set('institutions', yield this.get('store').findAll('institution')); + }).restartable(), + + initialLoad: task(function* (this: Dashboard) { + yield this.get('findNodes').perform(); + }), + + filterNodes: task(function* (this: Dashboard, filter) { + yield timeout(500); + this.setProperties({ filter }); + yield this.get('findNodes').perform(); + }).restartable(), + + findNodes: task(function* (this: Dashboard, more?: boolean) { + const indicatorProperty = `loading${more ? 'More' : ''}`; + const filter = this.get('filter'); + + this.set(indicatorProperty, true); + + const user = yield this.get('currentUser.user'); + + const nodes = yield user.queryHasMany('nodes', { + embed: 'contributors', + filter: filter ? { title: filter } : undefined, + page: more ? this.incrementProperty('page') : this.set('page', 1), + sort: this.get('sort') || undefined, + }); + + if (more) { + this.get('nodes').pushObjects(nodes); + } else { + this.set('nodes', nodes); + } + + this.set(indicatorProperty, false); + }).restartable(), + + getPopularAndNoteworthy: task(function* (this: Dashboard, id, dest) { + try { + const node = yield this.get('store').findRecord('node', id); + const linkedNodes = yield node.queryHasMany('linkedNodes', { + embed: 'contributors', + page: { size: 5 }, + }); + this.set(dest, linkedNodes); + } catch (e) { + this.set(`failedLoading-${dest}`, true); + } + }), + + searchNodes: task(function* (this: Dashboard, title) { + yield timeout(500); + const user = yield this.get('user'); + return yield user.queryHasMany('nodes', { filter: { title } }); + }).restartable(), + + createNode: task(function* (this: Dashboard, title, description, templateFrom) { + if (!title) { + return; + } + + const store = this.get('store'); + + const data = { + category: 'project', + description, + public: false, + templateFrom, + title, + }; + + const node = yield store.createRecord('node', data).save(); + + if (this.get('institutionsSelected.length')) { + const affiliatedInstitutions = yield node.get('affiliatedInstitutions'); + this.get('institutionsSelected').forEach(inst => affiliatedInstitutions.pushObject(inst)); + yield node.save(); + } + + this.set('newNode', node); + }).drop(), +}) { + page: number; + loading: boolean; + loadingMore: boolean; + filter: string; + sort: string; + modalOpen: boolean; + + store = service('store'); + + institutionsSelected = computed.oneWay('user.institutions'); + + hasNodes = computed('filter', 'nodes.meta.total', function (): boolean { + return this.get('nodes.meta.total') || this.get('filter') !== null; + }); + + hasMore = computed('nodes.{length,meta.total}', function (): boolean { + return this.get('nodes.length') < this.get('nodes.meta.total'); + }); + + init() { + this.get('initialLoad').perform(); + this.get('getInstitutions').perform(); + this.get('getPopularAndNoteworthy').perform(popularNode, 'popular'); + this.get('getPopularAndNoteworthy').perform(noteworthyNode, 'noteworthy'); + } + + closeModal() { + this.setProperties({ + modalOpen: false, + newNode: null, + }); + } +} diff --git a/app/dashboard/route.js b/app/dashboard/route.js deleted file mode 100644 index 6c74252aa1b..00000000000 --- a/app/dashboard/route.js +++ /dev/null @@ -1,4 +0,0 @@ -import Route from '@ember/routing/route'; - -export default Route.extend({ -}); diff --git a/app/dashboard/route.ts b/app/dashboard/route.ts new file mode 100644 index 00000000000..6153271ec06 --- /dev/null +++ b/app/dashboard/route.ts @@ -0,0 +1,3 @@ +import Route from '@ember/routing/route'; + +export default class Dashboard extends Route {} diff --git a/app/dashboard/styles.scss b/app/dashboard/styles.scss index a4096816a3b..6381a1c87e1 100644 --- a/app/dashboard/styles.scss +++ b/app/dashboard/styles.scss @@ -184,11 +184,6 @@ cursor: pointer; } -.not-selected.img-circle { - opacity: 0.25; - filter: grayscale(100%); -} - .osf-project-navbar a.project-title { max-width: 100%; } diff --git a/app/dashboard/template.hbs b/app/dashboard/template.hbs index 12dfab51779..204fe682bb3 100644 --- a/app/dashboard/template.hbs +++ b/app/dashboard/template.hbs @@ -1,6 +1,18 @@ -{{title 'OSF | Home'}} +{{title (t 'dashboard.page_title')}} {{#if modalOpen}} - {{new-project-modal user=currentUser.user closeModal=(action 'toggleModal') reloadNodes=(action 'reloadNodes')}} + {{new-project-modal + newNode=newNode + institutions=user.institutions + institutionsSelected=institutionsSelected + closeModal=(action 'closeModal') + selectInstitution=(action 'selectInstitution') + selectAll=(action 'selectAllInstitutions') + removeAll=(action 'removeAllInstitutions') + search=(perform searchNodes) + searchSelected=templateFrom + searchChange=(action (mut templateFrom)) + create=(perform createNode) + }} {{/if}}
@@ -33,7 +45,9 @@

{{t 'dashboard.title'}}

- +
@@ -42,7 +56,7 @@ {{#if initialLoad.isRunning}} {{loading-indicator}} {{else}} - {{#if totalNodes}} + {{#if hasNodes}}
@@ -55,7 +69,7 @@
{{t 'general.title'}} - {{sort-button sortAction=(action 'sort') curOrder=sortOrder curBy=sortBy sortBy='title'}} + {{sort-button sortAction=(action 'sort') sort=sort sortBy='title'}}
@@ -64,19 +78,17 @@
{{t 'general.modified'}} - - {{sort-button sortAction=(action 'sort') curOrder=sortOrder curBy=sortBy sortBy='last_logged'}} - + {{sort-button sortAction=(action 'sort') sort=sort sortBy='last_logged'}}
- {{sort-button sortAction=(action 'setSortOrder') curOrder=sortOrder}} + {{!-- {{sort-button sortAction=(action 'sort') curOrder=sortOrder}} --}}
@@ -98,7 +112,7 @@ {{loading-indicator}} {{else}}
-
@@ -200,7 +214,9 @@
@@ -223,11 +239,13 @@
- + \ No newline at end of file diff --git a/app/file-detail/controller.ts b/app/guid-file/controller.ts similarity index 95% rename from app/file-detail/controller.ts rename to app/guid-file/controller.ts index 6095c88d09b..1f4c116ddc6 100644 --- a/app/file-detail/controller.ts +++ b/app/guid-file/controller.ts @@ -44,7 +44,7 @@ export default class FileDetail extends Controller.extend(Analytics, { try { await this.get('model.file').destroyRecord(); - this.transitionToRoute('user-quickfiles', this.get('model.user.id')); + this.transitionToRoute('guid-user.quickfiles', this.get('model.user.id')); const message: string = this.get('i18n').t('file_detail.delete_success'); return this.get('toast').success(message); } catch (e) { @@ -87,7 +87,7 @@ export default class FileDetail extends Controller.extend(Analytics, { const guid = file.get('guid') || await file.getGuid(); this.set('revision', null); - this.transitionToRoute('file-detail', guid, { queryParams: { show: 'view' } }); + this.transitionToRoute('guid-file', guid, { queryParams: { show: 'view' } }); }, addTag(this: FileDetail, tag) { @@ -156,6 +156,6 @@ export default class FileDetail extends Controller.extend(Analytics, { declare module '@ember/controller' { interface Registry { - 'file-detail': FileDetail; + 'guid-file': FileDetail; } } diff --git a/app/file-detail/route.ts b/app/guid-file/route.ts similarity index 55% rename from app/file-detail/route.ts rename to app/guid-file/route.ts index 788a43a6706..b58a6127437 100644 --- a/app/file-detail/route.ts +++ b/app/guid-file/route.ts @@ -6,14 +6,19 @@ export default class FileDetail extends Route.extend(Analytics) { currentUser = service('currentUser'); async model(params) { - const file = await this.store.findRecord('file', params.file_id); - const fileUser = await file.get('user'); - const user = await fileUser.reload(); + try { + const file = await this.store.findRecord('file', params.file_guid); + const fileUser = await file.get('user'); + const user = await fileUser.reload(); - return { - file, - user, - }; + return { + file, + user, + }; + } catch (error) { + this.transitionTo('not-found', id); + throw error; + } } resetController(controller, isExiting, transition) { diff --git a/app/file-detail/styles.scss b/app/guid-file/styles.scss similarity index 99% rename from app/file-detail/styles.scss rename to app/guid-file/styles.scss index 580f26adec9..50811a59f50 100644 --- a/app/file-detail/styles.scss +++ b/app/guid-file/styles.scss @@ -15,8 +15,6 @@ } .TitleBar { - padding-top: 40px; - &__version-link { cursor: pointer; } diff --git a/app/file-detail/template.hbs b/app/guid-file/template.hbs similarity index 98% rename from app/file-detail/template.hbs rename to app/guid-file/template.hbs index 1dd7a07c79d..4ab43b0b6ff 100644 --- a/app/file-detail/template.hbs +++ b/app/guid-file/template.hbs @@ -86,7 +86,8 @@ addTag=(action 'addTag') removeTagAtIndex=(action 'removeTagAtIndex') allowSpacesInTags=true - placeholder='add a tag to enhance discoverability' + placeholder=(t 'file_detail.add_tag') + aria_label=(t 'file_detial.tags') readOnly=(unless canEdit true false) as |tag| }} diff --git a/app/guid-node/route.ts b/app/guid-node/route.ts new file mode 100644 index 00000000000..6febce5cd57 --- /dev/null +++ b/app/guid-node/route.ts @@ -0,0 +1,11 @@ +import ResolvedGuidRoute from '../resolve-guid/resolved-guid-route'; + +export default class GuidNode extends ResolvedGuidRoute.extend({ +}) { + model(this: GuidNode, params: { node_guid: string }) { + return { + taskInstance: this.get('loadModel').perform('node', params.node_guid), + nodeId: params.node_guid, + }; + } +} diff --git a/app/guid-node/template.hbs b/app/guid-node/template.hbs new file mode 100644 index 00000000000..b8c26b9fcbc --- /dev/null +++ b/app/guid-node/template.hbs @@ -0,0 +1,2 @@ +

{{model.taskInstance.value.title}}

+{{outlet}} diff --git a/app/guid-preprint/route.ts b/app/guid-preprint/route.ts new file mode 100644 index 00000000000..6969d1bda44 --- /dev/null +++ b/app/guid-preprint/route.ts @@ -0,0 +1,11 @@ +import ResolvedGuidRoute from '../resolve-guid/resolved-guid-route'; + +export default class GuidPreprint extends ResolvedGuidRoute.extend({ +}) { + model(this: GuidPreprint, params: { preprint_guid: string }) { + return { + taskInstance: this.get('loadModel').perform('preprint', params.preprint_guid), + preprintId: params.preprint_guid, + }; + } +} diff --git a/app/guid-preprint/template.hbs b/app/guid-preprint/template.hbs new file mode 100644 index 00000000000..b8c26b9fcbc --- /dev/null +++ b/app/guid-preprint/template.hbs @@ -0,0 +1,2 @@ +

{{model.taskInstance.value.title}}

+{{outlet}} diff --git a/app/guid-registration/route.ts b/app/guid-registration/route.ts new file mode 100644 index 00000000000..3a69455cbde --- /dev/null +++ b/app/guid-registration/route.ts @@ -0,0 +1,11 @@ +import ResolvedGuidRoute from '../resolve-guid/resolved-guid-route'; + +export default class GuidRegistration extends ResolvedGuidRoute.extend({ +}) { + model(this: GuidRegistration, params: { registration_guid: string }) { + return { + taskInstance: this.get('loadModel').perform('registration', params.registration_guid), + registrationId: params.registration_guid, + }; + } +} diff --git a/app/guid-registration/template.hbs b/app/guid-registration/template.hbs new file mode 100644 index 00000000000..b8c26b9fcbc --- /dev/null +++ b/app/guid-registration/template.hbs @@ -0,0 +1,2 @@ +

{{model.taskInstance.value.title}}

+{{outlet}} diff --git a/app/guid-user/index/route.ts b/app/guid-user/index/route.ts new file mode 100644 index 00000000000..9c41d99acae --- /dev/null +++ b/app/guid-user/index/route.ts @@ -0,0 +1,8 @@ +import Route from '@ember/routing/route'; + +export default class GuidUserIndex extends Route.extend({ +}) { + model(this: GuidUserIndex) { + return this.modelFor('guid-user'); + } +} diff --git a/app/guid-user/index/template.hbs b/app/guid-user/index/template.hbs new file mode 100644 index 00000000000..b6d47f9b196 --- /dev/null +++ b/app/guid-user/index/template.hbs @@ -0,0 +1 @@ +

{{model.taskInstance.value.fullName}}

diff --git a/app/user-quickfiles/controller.ts b/app/guid-user/quickfiles/controller.ts similarity index 58% rename from app/user-quickfiles/controller.ts rename to app/guid-user/quickfiles/controller.ts index d80c9884ff2..ef27473b06c 100644 --- a/app/user-quickfiles/controller.ts +++ b/app/guid-user/quickfiles/controller.ts @@ -7,22 +7,23 @@ export default class UserQuickfiles extends Controller.extend(Analytics, { actions: { async openFile(this: UserQuickfiles, file, show) { const guid = file.get('guid') || await file.getGuid(); - this.transitionToRoute('file-detail', guid, { queryParams: { show } }); + this.transitionToRoute('guid-file', guid, { queryParams: { show } }); }, }, }) { currentUser = service('currentUser'); pageName = 'QuickFiles'; - canEdit = computed('currentUser', 'model', function(this: UserQuickfiles): boolean { - const modelId = this.get('model.id'); + user = computed.alias('model.taskInstance.value'); - return modelId && modelId === this.get('currentUser.currentUserId'); + canEdit = computed('currentUser.currentUserId', 'user.id', function(this: UserQuickfiles): boolean { + const userId = this.get('user.id'); + return userId && userId === this.get('currentUser.currentUserId'); }); } declare module '@ember/controller' { interface Registry { - 'user-quickfiles': UserQuickfiles; + 'guid-user/quickfiles': UserQuickfiles; } } diff --git a/app/user-quickfiles/route.ts b/app/guid-user/quickfiles/route.ts similarity index 89% rename from app/user-quickfiles/route.ts rename to app/guid-user/quickfiles/route.ts index de8348ea0c4..8e65ffc0075 100644 --- a/app/user-quickfiles/route.ts +++ b/app/guid-user/quickfiles/route.ts @@ -22,7 +22,7 @@ export default class UserQuickfiles extends Route.extend(Analytics, { }) { currentUser = service('currentUser'); - model(params) { - return this.store.findRecord('user', params.user_id); + model(this: UserQuickfiles) { + return this.modelFor('guid-user'); } } diff --git a/app/user-quickfiles/styles.scss b/app/guid-user/quickfiles/styles.scss similarity index 82% rename from app/user-quickfiles/styles.scss rename to app/guid-user/quickfiles/styles.scss index e3d1b805d18..82941d926f9 100644 --- a/app/user-quickfiles/styles.scss +++ b/app/guid-user/quickfiles/styles.scss @@ -1,6 +1,4 @@ .Content { - padding-top: 60px; - .file-browser { // stylelint-disable-line selector-class-pattern height: 600px; } diff --git a/app/user-quickfiles/template.hbs b/app/guid-user/quickfiles/template.hbs similarity index 60% rename from app/user-quickfiles/template.hbs rename to app/guid-user/quickfiles/template.hbs index 2a4d3765754..5c4d9e926b6 100644 --- a/app/user-quickfiles/template.hbs +++ b/app/guid-user/quickfiles/template.hbs @@ -1,14 +1,14 @@
- {{title (t 'quickfiles.title' user-name=model.fullName)}} + {{title (t 'quickfiles.title' user-name=user.fullName)}} - {{quickfile-nav user=model onQuickfiles=true}} + {{quickfile-nav user=user onQuickfiles=true}}
{{#if canEdit}}
{{t 'quickfiles.description'}}
{{else}}
{{/if}} - {{file-browser user=model openFile=(action 'openFile') dropZoneId='quickfiles-dropzone'}} + {{file-browser user=user openFile=(action 'openFile') dropZoneId='quickfiles-dropzone'}}
diff --git a/app/guid-user/route.ts b/app/guid-user/route.ts new file mode 100644 index 00000000000..b130c306e41 --- /dev/null +++ b/app/guid-user/route.ts @@ -0,0 +1,11 @@ +import ResolvedGuidRoute from '../resolve-guid/resolved-guid-route'; + +export default class GuidUser extends ResolvedGuidRoute.extend({ +}) { + model(this: GuidUser, params: { user_guid: string }) { + return { + taskInstance: this.get('loadModel').perform('user', params.user_guid), + userId: params.user_guid, + }; + } +} diff --git a/app/guid-user/template.hbs b/app/guid-user/template.hbs new file mode 100644 index 00000000000..c24cd68950a --- /dev/null +++ b/app/guid-user/template.hbs @@ -0,0 +1 @@ +{{outlet}} diff --git a/app/helpers/if-filter.js b/app/helpers/if-filter.js index 95b145bbc2b..3c41b355438 100644 --- a/app/helpers/if-filter.js +++ b/app/helpers/if-filter.js @@ -20,10 +20,10 @@ export function ifFilter(params) { const match = element.filter(each => filter.includes(each)); return match.length; } - if ((typeof filter === 'object')) { + if (typeof filter === 'object') { return filter.includes(element); } - if ((typeof filter === 'undefined') || ((element.toLowerCase()).includes(filter.toLowerCase()))) { + if (typeof filter === 'undefined' || element.toLowerCase().includes(filter.toLowerCase())) { return true; } return false; diff --git a/app/locales/en/translations.ts b/app/locales/en/translations.ts index d99143ee9ce..5a243653a8a 100644 --- a/app/locales/en/translations.ts +++ b/app/locales/en/translations.ts @@ -33,6 +33,14 @@ export default { filter: 'Filter', revert: 'Revert', save: 'Save', + ellipses: '\u2026', + warning: 'Warning', + clipboard_copy: 'Copy to clipboard', + sort_asc: 'Sort ascending', + sort_desc: 'Sort descending', + next: 'next', + previous: 'previous', + help: 'help', }, quickfiles: { title: '{{user-name}}\'s Quick Files', @@ -72,6 +80,8 @@ export default { delete_fail: 'Error, unable to delete file', save_success: 'File saved', save_fail: 'Error, unable to save file', + mfr_iframe_title: 'Rendering of document', + add_tag: 'add a tag to enhance discoverability', }, file_browser: { loading: 'Loading...', @@ -80,6 +90,8 @@ export default { drop_placeholder: 'Drop files here to upload', drop_reminder: 'Drop file to upload', no_files: 'This user has not uploaded any quickfiles', + share_title: 'Share', + clipboard_copy: 'Copy to clipboard', info: { title: 'How to use the file browser', upload: 'Upload: Single file uploads via drag and drop or by clicking the upload button.', @@ -108,6 +120,7 @@ export default { }, dashboard: { + page_title: 'Home', title: 'Dashboard', create_new_project_button: 'Create new project', quicksearch: { @@ -255,4 +268,16 @@ export default { social_facebook: 'Follow us on Facebook', social_github: 'Connect with COS on GitHub', }, + not_found: { + title: 'Page not found', + body: 'The requested resource could not be found. If this should not have occurred and the issue persists, please report it to {{supportEmail}}', + }, + zoom_to_guid: { + title: 'Zoom to a GUID within this app', + zoom: 'Zoom!', + placeholder: 'Enter a GUID', + }, + osf_mode_footer: { + dev_mode: 'This site is running in development mode.', + }, }; diff --git a/app/locales/ja/translations.ts b/app/locales/ja/translations.ts new file mode 100644 index 00000000000..d0238ae131c --- /dev/null +++ b/app/locales/ja/translations.ts @@ -0,0 +1,283 @@ +/* tslint:disable:object-literal-sort-keys max-line-length */ + +export default { + general: { + OSF: 'OSF', + share: '共有', + embed: '埋込み', + download: 'ダウンロード', + delete: '削除', + view: '表示', + edit: '編集', + cancel: 'キャンセル', + revisions: '修正', + md5: 'MD5', + date: '日付', + sha2: 'SHA2', + title: 'タイトル', + contributors: '共同研究者', + modified: '変更', + description: '説明', + create: '作成', + and: 'and', + more: '詳細', + upload: 'アップロード', + rename: '名前の変更', + move: '移動', + name: 'ファイル名', + size: 'ファイルサイズ', + version: 'バージョン', + downloads: 'ダウンロード', + close: '閉じる', + back: '戻る', + filter: 'フィルター', + revert: '復帰', + save: '保存', + ellipses: '\u2026', + warning: '警告', + clipboard_copy: 'クリップボードへコピー', + sort_asc: '昇順ソート', + sort_desc: '降順ソート', + next: '次へ', + previous: '戻る', + help: 'ヘルプ', + }, + quickfiles: { + title: '{{user-name}} のクイックファイル', + description: 'ここにアップロードされたファイルは一般公開されており、共有リンクを使用して他のユーザーと簡単に共有できます。', + feedback_dialog_text: 'クイックファイルの考え方を我々に教えて下さい', + transition_auth: 'クイックファイルを表示するには、ログインしている必要があります。ログインページにリダイレクトします。', + }, + feedback: { + button_text: 'フィードバック', + placeholder: 'フィードバックをシェア', + follow_up_label: 'OSFを改善するさらなる機会について私に連絡してください', + title: 'フィードバックの提出', + confirm_button_text: '送信', + thank_you: 'ありがとうございます', + success: 'あなたからのフィードバックは提出されました', + dismiss: '了解', + }, + file_detail: { + version: { + id: 'バージョンID', + title: '(バージョン: {{version-number}})', + }, + embed: { + dynamic: 'JavaScriptを用いたダイレクトレンダーiframe', + direct: '高さと幅を固定したダイレクトiframe', + }, + tags: 'タグ:', + toggle: 'ビュー切替:', + delete_file: { + question: 'ファイルを削除しますか?', + confirm: '本当に {{file-name}} を削除してもよろしいでしょうか?', + }, + sha2_description: 'SHA-2は、データの完全性を検証するために使用されるNSAによって設計された暗号ハッシュ関数です。', + md5_description: 'MD5は、データの完全性を検証するために使用されるアルゴリズムです。', + // toast messages + delete_success: 'ファイルが削除されました', + delete_fail: 'エラー、ファイルを削除できません', + save_success: 'ファイルが保存されました', + save_fail: 'エラー、ファイルを保存できません', + mfr_iframe_title: 'ドキュメントのレンダリング', + add_tag: '検索性を高めるタグを追加する', + }, + file_browser: { + loading: 'ローディング中...', + delete_multiple: '複数削除', + download_zip: 'Zipとしてダウンロード', + drop_placeholder: 'アップロードするファイルをここにドロップ', + drop_reminder: 'アップロードするファイルをドロップ', + no_files: 'このユーザーはクイックファイルをアップロードしていません', + share_title: '共有', + clipboard_copy: 'クリップボードへコピー', + info: { + title: 'ファイルブラウザの使い方', + upload: 'アップロード:ドラッグアンドドロップまたはアップロードボタンをクリックして、ファイルをアップロードします。', + select: '行を選択:行をクリックすると、ツールバーにさらにアクションが表示されます。 複数のファイルを選択するには、CommandキーまたはShiftキーを使用します。', + folders: 'フォルダ:サポートされていません; 多くのファイルをアップロードおよび管理するためのOSFプロジェクトを検討してください。', + open1: 'ファイルを開く:ファイル名をクリックすると、OSF内のファイルが表示されます。', + open2: '新規タブでファイルを開く: command(WindowsではCtrl)を押し、ファイル名をクリックして新しいタブで開きます。', + download: 'Zipとしてダウンロード: ツールバーの[Zipとしてダウンロード]ボタンをクリックすると、すべてのファイルが.zipとしてダウンロードされます。', + }, + delete_modal: { + title: '"{{selectedItems.firstObject.itemName}}"を削除しますか?', + title_multiple: '複数削除しますか?', + body: 'この操作は不可逆的です', + }, + conflict_modal: { + title: '{{textValue}}という名前のアイテムは、この場所に既に存在します。', + keep_info: '"両方を保持"は、両方のファイル(およびそのバージョン履歴)をこの場所に保持します。', + replace_info: '"置換"は、この場所にある既存のファイルを上書きします。 上書きされたファイルの以前のバージョンは失われます。 移動したファイルの以前のバージョンを保持します。', + keep_button: '両方を保持', + replace_button: '置換', + }, + move_modal: { + title: 'プロジェクトへファイルを移動', + move_button: 'ファイルを移動', + }, + + }, + dashboard: { + page_title: 'ホーム', + title: 'ダッシュボード', + create_new_project_button: '新規プロジェクト作成', + quicksearch: { + search: 'マイプロジェクト内の検索', + other_links: 'あなたの仕事を整理したり、OSFを検索するには、My Projectsに行きます。', + no_results: 'プロジェクトが見つかりません!', + no_projects: { + line1: 'まだプロジェクトがありません。画面右上のボタンからプロジェクトを作成して下さい。', + line2: 'この機能を使用すると、プロジェクトを検索してすばやくアクセスできます。', + preview_alt: 'クイックプロジェクトの全画面プレビュー', + }, + private_parent: '非公開プロジェクト / ', + private_grandparent: '非公開プロジェクト / 非公開 / ', + }, + noteworthy: { + description: '公開プロジェクトの検索', + new_and_noteworthy: '新着と注目', + failed_noteworthy: '"新着と注目"プロジェクトのローディングに失敗しました。', + most_popular: '最人気', + failed_popular: '"最人気"プロジェクトのローディングに失敗しました。', + search_more: '更にプロジェクトを探す', + by: 'by', + }, + meetings: { + title: '学会や研究会をホスティングしますか?', + description: 'OSF for Meetings サービスを使用し、学術集会への投稿するためのポータルを提供します。', + button: '学術集会の表示', + }, + preprints: { + title: '最新の研究を閲覧', + description: 'さまざまな研究分野をカバーするOSFで、ホストされている最新のプレプリントをご覧ください。', + button: 'プレプリントの表示', + }, + }, + new_project: { + header: '新規プロジェクト作成', + title_placeholder: 'プロジェクトタイトルの入力', + more: 'More', + affiliation: '所属', + remove_all: '全件削除', + select_all: '全件選択', + no_matches: '一致しません', + description_placeholder: 'プロジェクト説明の入力', + template_title: 'テンプレート(オプション)', + template_search_help: 'プロジェクトを検索するタイピングの開始。テンプレートとしてプロジェクトを選択すると、そのプロジェクトの内容をインポートせずにその構造が新しいプロジェクトに複製されます。', + template_placeholder: 'テンプレートとして使用するプロジェクトを選択する', + success_message: '新規プロジェクトの作成に成功しました', + stay_here: 'この場に留まる', + go_to_new: '作成したプロジェクトへ移動', + }, + banners: { + prereg: { + description: 'あなたの次の研究を改善してください。 Prereg Challengeに参加すれば$ 1,000を獲得できます。', + button: 'Prereg Challengeをはじめる', + }, + }, + move_to_project: { + create_new_project: '新規プロジェクトの作成', + connect_to_existing: '既存のOSFプロジェクトへファイルを接続する', + enter_project_title: 'プロジェクトタイトルの入力', + new_project_message: 'ファイル用の新しいパブリックプロジェクトを作成することを選択しました。 プロジェクトがプライベートにならない限り、ユーザーは引き続きファイルにアクセスできます。', + choose_project: 'プロジェクトを選択', + project_select_message: '表示されるプロジェクトのリストは、書き込みアクセス権を持つプロジェクトとコンポーネントです。 登録はここには含まれていません。', + no_projects_exist_error: '利用可能なプロジェクトはありません。 新しいプロジェクトを作成するには、元に戻ってください。', + could_not_create_project: 'プロジェクトを作成できませんでした。 もう一度お試しください。', + convert_or_copy_message: { + project: '"ファイルの移動"をクリックすると、すぐにOSFプロジェクトが変更され、ファイルが移動されます。', + component: '"ファイルの移動"をクリックすると、すぐにOSFコンポーネントが変更され、ファイルが移動されます。', + }, + no_longer_public_warning: { + project: '非公開プロジェクトに移動されたファイルは公開されなくなり、他のユーザーによって発見されなくなります。', + component: 'プライベートコンポーネントに移動されたファイルは公開されなくなり、他のユーザーによって検出されなくなります。', + }, + file_successfully_moved: 'ファイルは正常に移動されました!', + could_not_move_file: 'ファイルを移動できませんでした。 もう一度お試しください', + keep_working_here: 'ここで作業を続ける', + go_to_new_project: '新規プロジェクトへ移動', + go_to_component: 'コンポーネントへ移動', + go_to_project: 'プロジェクトへ移動', + }, + navbar: { + add: '追加する', + add_a_preprint: '{{preprintWords.preprint}}を追加', + browse: '探す', + cancel_search: '検索をキャンセル', + donate: '寄付する', + go_home: 'ホームへ行く', + my_projects: 'マイプロジェクト', + my_quick_files: 'マイクイックファイル', + reviews: 'マイレビュー', + search: '検索', + search_help: '検索ヘルプ', + search_the_OSF: 'OSF中を検索', + send_search: '検索クエリの送信', + support: 'サポート', + toggle_primary: 'プライマリー・ナビゲーション切替', + toggle_secondary: 'セカンダリー・ナビゲーション切替', + }, + auth_dropdown: { + log_out: 'ログアウト', + my_profile: 'マイプロファイル', + osf_support: 'OSFサポート', + settings: '設定', + sign_up: '新規登録', + sign_in: 'ログイン', + user_gravatar: 'ユーザーgravatar', + toggle_auth_dropdown: 'authドロップダウンの切り替え', + }, + search_help_modal: { + close: '閉じる', + search_help: 'ヘルプを検索', + queries: 'クエリ', + search_uses_the: '検索では', + search_syntax: 'search syntax', + help_description: 'これは多くのオプションを提供しますが、非常にシンプルにすることもできます。 有効な検索の例は次のとおりです。', + }, + support: { + title: 'サポート', + faq_title: 'よくあるご質問', + faq_paragraph: 'どうすれば無料になるのですか? OSFは私の研究にどのように役立つでしょうか? 登録とは何ですか? Open Science Frameworkに関する質問は、', + faq_link_text: 'FAQ.', + faq_button: 'FAQへ', + guides_title: 'OSFガイド', + guides_paragraph_1: '研究ワークフローを改善するためにOSFを使用する方法を学びます。 私たちを読む', + guides_link_text: 'ガイド', + guides_paragraph_2: 'プロジェクトの構造、バージョン管理、プライバシー、ファイル、アドオンサポートなどの基本を示すステップバイステップのスクリーンショットがあります。', + guides_button: 'ガイドを訪問', + contact_title: '連絡する', + contact_technical: 'テクニカルサポートの電子メール:', + contact_questions: 'その他の質問やコメント', + prereg_title: 'Prereg Challengeに関する質問はありますか?', + prereg_paragraph_1: '調査して下さい、我々の', + prereg_link_text: 'Preregセクション', + prereg_paragraph_2: 'cos.io ウェブサイト上を', + status_title: 'あなたは私たちのサービスでダウンタイムを経験していますか?', + status_paragraph_1: '調査して下さい、我々の', + status_link_text: 'ステータスページ', + status_paragraph_2: '私たちのサービスがどのように動作しているかについての更新情報。', + consultation_title: '統計コンサルティングをお探しですか', + consultation_paragraph: 'COSは統計コンサルティングを無料で提供しています。 このサービスの詳細については、', + consultation_link_text: 'COS統計コンサルティングページ。', + social_title: '助けを得るための他の方法', + social_twitter: 'Twitterで質問する', + social_mailing: '我々のメーリングリストへ参加', + social_facebook: 'Facebookでフォロー', + social_github: 'GitHubのCOSとコンタクト', + }, + not_found: { + title: 'ぺーじが見つかりません', + body: '要求されたリソースが見つかりませんでした。これが発生してはならず、問題が解決しない場合は、サポート{{supportEmail}}へ報告してください', + }, + zoom_to_guid: { + title: 'このアプリ内のGUIDに直行する', + zoom: '直行!', + placeholder: 'GUIDを入力', + }, + osf_mode_footer: { + dev_mode: 'このサイトは開発モードで動作しています。', + }, +}; diff --git a/app/locales/zh/translations.ts b/app/locales/zh/translations.ts index 8770fe8c741..f4aa3edd317 100644 --- a/app/locales/zh/translations.ts +++ b/app/locales/zh/translations.ts @@ -20,6 +20,12 @@ export default { create: '创建', and: 'and', more: '更多', + clipboard_copy: 'Copy to clipboard', + sort_asc: 'Sort ascending', + sort_desc: 'Sort descending', + next: 'next', + previous: 'previous', + help: 'help', }, quickfiles: { title: '{{user-name}} 的 Quick Files', @@ -59,8 +65,47 @@ export default { delete_fail: '错误,文件删除失败', save_success: '文件已保存', save_fail: '错误,无法保存文件', + mfr_iframe_title: 'Rendering of document', + add_tag: 'add a tag to enhance discoverability', + }, + file_browser: { + loading: 'Loading...', + delete_multiple: 'Delete multiple', + download_zip: 'Download as zip', + drop_placeholder: 'Drop files here to upload', + drop_reminder: 'Drop file to upload', + no_files: 'This user has not uploaded any quickfiles', + share_title: 'Share', + clipboard_copy: 'Copy to clipboard', + info: { + title: 'How to use the file browser', + upload: 'Upload: Single file uploads via drag and drop or by clicking the upload button.', + select: 'Select rows: Click on a row to show further actions in the toolbar. Use Command or Shift keys to select multiple files.', + folders: 'Folders: Not supported; consider an OSF project for uploading and managing many files.', + open1: 'Open files: Click a file name to go to view the file in the OSF.', + open2: 'Open files in new tab: Press Command (Ctrl in Windows) and click a file name to open it in a new tab.', + download: 'Download as zip: Click the Download as Zip button in the toolbar to download all files as a .zip.', + }, + delete_modal: { + title: 'Delete "{{selectedItems.firstObject.itemName}}"?', + title_multiple: 'Delete multiple?', + body: 'This action is irreversible', + }, + conflict_modal: { + title: 'An item named {{textValue}} already exists in this location.', + keep_info: '"Keep both" will retain both files (and their version histories) in this location.', + replace_info: '"Replace" will overwrite the existing file in this location. You will lose previous versions of the overwritten file. You will keep previous versions of the moved file.', + keep_button: 'Keep both', + replace_button: 'Replace', + }, + move_modal: { + title: 'Move file to project', + move_button: 'Move file', + }, + }, dashboard: { + page_title: 'Home', title: '仪表盘', create_new_project_button: '创建新项目', quicksearch: { diff --git a/app/mixins/osf-token-login-route.js b/app/mixins/osf-token-login-route.js index e304b941bfc..22feb3f977e 100644 --- a/app/mixins/osf-token-login-route.js +++ b/app/mixins/osf-token-login-route.js @@ -24,7 +24,7 @@ export default Ember.Mixin.create({ this._super(...arguments); let accessToken; - if (config.OSF.isLocal) { + if (config.OSF.backend === 'local') { ({ accessToken } = config.OSF); } else { accessToken = getTokenFromHash(window.location.hash); diff --git a/app/models/guid.ts b/app/models/guid.ts new file mode 100644 index 00000000000..286186aada1 --- /dev/null +++ b/app/models/guid.ts @@ -0,0 +1,29 @@ +import { computed } from '@ember/object'; +import { singularize } from 'ember-inflector'; +import OsfModel from './osf-model'; + +/** + * @module ember-osf-web + * @submodule models + */ + +/** + * Model for GUIDs + * @class Guid + */ +export default class Guid extends OsfModel.extend({ +}) { + referentType = computed(function(this: Guid): string { + return singularize(this.get('links.relationships.referent.data.type')); + }); + + resolve(this: Guid): RSVP.Promise { + return this.get('store').findRecord(this.get('referentType'), this.get('id')); + } +} + +declare module 'ember-data' { + interface ModelRegistry { + 'guid': Guid; + } +} diff --git a/app/not-found/controller.ts b/app/not-found/controller.ts new file mode 100644 index 00000000000..27bc18b904f --- /dev/null +++ b/app/not-found/controller.ts @@ -0,0 +1,13 @@ +import Controller from '@ember/controller'; +import config from 'ember-get-config'; + +export default class NotFound extends Controller.extend({ +}) { + supportEmail: string = config.support.supportEmail; +} + +declare module '@ember/controller' { + interface Registry { + 'not-found': NotFound; + } +} diff --git a/app/not-found/route.ts b/app/not-found/route.ts new file mode 100644 index 00000000000..07c88697dc1 --- /dev/null +++ b/app/not-found/route.ts @@ -0,0 +1,5 @@ +import Route from '@ember/routing/route'; + +export default class NotFound extends Route.extend({ +}) { +} diff --git a/app/not-found/template.hbs b/app/not-found/template.hbs new file mode 100644 index 00000000000..aead553679d --- /dev/null +++ b/app/not-found/template.hbs @@ -0,0 +1,8 @@ +
+
+
+

{{t 'not_found.title'}}

+

{{t 'not_found.body' supportEmail=supportEmail}}

+
+
+
diff --git a/app/quickfiles/route.ts b/app/quickfiles/route.ts index 186812ec73f..f026aedd5ee 100644 --- a/app/quickfiles/route.ts +++ b/app/quickfiles/route.ts @@ -12,7 +12,7 @@ export default class Quickfiles extends Route.extend(CasAuthenticatedRouteMixin) afterModel(model) { if (model) { - return this.transitionTo('user-quickfiles', model); + return this.transitionTo('guid-user.quickfiles', model); } } } diff --git a/app/resolve-guid/resolved-guid-route.ts b/app/resolve-guid/resolved-guid-route.ts new file mode 100644 index 00000000000..b71ab1bb069 --- /dev/null +++ b/app/resolve-guid/resolved-guid-route.ts @@ -0,0 +1,17 @@ +import Route from '@ember/routing/route'; +import { task } from 'ember-concurrency'; + +/** + * Base class for the root-level GUID routes, once the GUID's referent type is known. + * + * Not an ES6 class, so it can be further extended without confusing Ember's _super + */ +export default Route.extend({ + loadModel: task(function* (this: Route, typeName: string, id: string) { + try { + return yield this.get('store').findRecord(typeName, id); + } catch (error) { + this.transitionTo('not-found', this.get('router.currentURL').slice(1)); + } + }), +}); diff --git a/app/resolve-guid/route.ts b/app/resolve-guid/route.ts new file mode 100644 index 00000000000..8ccf0ac1870 --- /dev/null +++ b/app/resolve-guid/route.ts @@ -0,0 +1,18 @@ +import Route from '@ember/routing/route'; +import { inject as service } from '@ember/service'; + +export default class ResolveGuid extends Route.extend({ +}) { + store = service('store'); + + async model(this: ResolveGuid, params: { guid: string }): void { + // Block until the Guid is resolved, then always transition somewhere else. + try { + const guid = await this.get('store').findRecord('guid', params.guid); + this.transitionTo(`guid-${guid.get('referentType')}`, guid.get('id')); + } catch (error) { + this.transitionTo('not-found', params.guid); + throw error; + } + } +} diff --git a/app/router.ts b/app/router.ts index 829006262b9..14e34a66615 100644 --- a/app/router.ts +++ b/app/router.ts @@ -27,12 +27,36 @@ const Router = EmberRouter.extend({ /* eslint-disable array-callback-return */ Router.map(function() { + // All non-guid routes (except `not-found`) belong above "Guid Routing" this.route('dashboard', { path: '/' }); - this.route('quickfiles', { path: '/quickfiles' }); - this.route('quickfiles', { path: '/me/quickfiles' }); - this.route('user-quickfiles', { path: '/:user_id/quickfiles' }); - this.route('file-detail', { path: '/:file_id' }); - this.route('support', { path: '/support' }); + this.route('quickfiles'); + this.route('support'); + + /* + * Guid Routing + * + * Root guid URLs (e.g. "/mst3k/") will match the `resolve-guid` route, which + * will ask the API what type of object the guid refers to, then transition + * to the appropriate `guid-` route below. + * + * Nested routes that begin with a guid should be unique across all types. + * Do not add duplicate nested routes, like `guid-node/quickfiles`. + */ + this.route('guid-file', { path: '/:file_guid' }); + this.route('guid-node', { path: '/:node_guid' }); + this.route('guid-preprint', { path: '/:preprint_guid' }); + this.route('guid-registration', { path: '/:registration_guid' }); + this.route('guid-user', { path: '/:user_guid' }, function() { + this.route('quickfiles'); + }); + + // If there are multiple routes with the same path pattern (e.g. `resolve-guid` + // and all the `guid-*` routes above), URLs that match will resolve to the + // route defined last. It's very intuitive. + this.route('resolve-guid', { path: '/:guid' }); + + // Catch-all 404 page + this.route('not-found', { path: '*path' }); }); /* eslint-enable array-callback-return */ diff --git a/app/serializers/guid.ts b/app/serializers/guid.ts new file mode 100644 index 00000000000..c5a739c8915 --- /dev/null +++ b/app/serializers/guid.ts @@ -0,0 +1,10 @@ +import OsfSerializer from './osf-serializer'; + +export default class Guid extends OsfSerializer.extend({ +}) {} + +declare module 'ember-data' { + interface SerializerRegistry { + 'guid': Guid; + } +} diff --git a/app/styles/_accessibility.scss b/app/styles/_accessibility.scss new file mode 100644 index 00000000000..104b0ee36a1 --- /dev/null +++ b/app/styles/_accessibility.scss @@ -0,0 +1,20 @@ +.btn-success { // stylelint-disable-line selector-class-pattern + color: $btn-success-high-contrast-color; + background-color: $brand-success; + border-color: darken($brand-success, 5%); +} +/* stylelint-disable selector-class-pattern */ +.btn-success.disabled, +.btn-success.disabled:hover { + color: darken($brand-success, 85%); + background-color: lighten($brand-success, 50%); + border-color: lighten($brand-success, 45%); + opacity: 1; +} +/* stylelint-enable */ + +.btn-info { // stylelint-disable-line selector-class-pattern + color: $btn-info-high-contrast-color; + background-color: $brand-info; + border-color: darken($brand-info, 5%); +} diff --git a/app/styles/_variables.scss b/app/styles/_variables.scss index 628afb28301..d3c171367bb 100644 --- a/app/styles/_variables.scss +++ b/app/styles/_variables.scss @@ -32,12 +32,17 @@ $color-bg-gray-dark: #4c4c4c; $color-bg-gray: #e0e0e0; $color-bg-gray-light: #efefef; $color-bg-gray-blue-dark: #34495e; - $color-bg-blue-dark: #337ab7; $color-bg-blue-light: #def; +$color-bg-red: #f00; $color-shadow-light: rgba(0, 0, 0, 0.05); $color-shadow-gray-light: #ccc; $color-shadow-gray-dark: #898787; $color-text-placeholder-grey-dark: #666; + +$brand-success: #357935; +$brand-info: #1b6d85; +$btn-success-high-contrast-color: #fff; +$btn-info-high-contrast-color: #fff; diff --git a/app/styles/app.scss b/app/styles/app.scss index 4676d9867f9..1fbb24dfe39 100644 --- a/app/styles/app.scss +++ b/app/styles/app.scss @@ -11,3 +11,4 @@ @import 'mixins'; @import 'pod-styles'; @import 'typography'; +@import 'accessibility'; diff --git a/config/environment.js b/config/environment.js index 6946f879c1e..bbefa2a87f0 100644 --- a/config/environment.js +++ b/config/environment.js @@ -1,100 +1,40 @@ /* eslint-env node */ -const knownBackends = { - local: { - url: 'http://localhost:5000/', - apiUrl: 'http://localhost:8000', - renderUrl: 'http://localhost:7778/render', - waterbutlerUrl: 'http://localhost:7777/', - helpUrl: 'http://localhost:4200/help', - cookieLoginUrl: 'http://localhost:8080/login', - oauthUrl: 'http://localhost:8080/oauth2/profile', - shareBaseUrl: 'https://staging-share.osf.io/', - shareApiUrl: 'https://staging-share.osf.io/api/v2', - shareSearchUrl: 'https://staging-share.osf.io/api/v2/search/creativeworks/_search', - }, - stage: { - url: 'https://staging.osf.io/', - apiUrl: 'https://staging-api.osf.io', - renderUrl: 'https://staging-mfr.osf.io/render', - waterbutlerUrl: 'https://staging-files.osf.io/', - helpUrl: 'http://help.osf.io', - cookieLoginUrl: 'https://staging-accounts.osf.io/login', - oauthUrl: 'https://staging-accounts.osf.io/oauth2/authorize', - shareBaseUrl: 'https://staging-share.osf.io/', - shareApiUrl: 'https://staging-share.osf.io/api/v2', - shareSearchUrl: 'https://staging-share.osf.io/api/v2/search/creativeworks/_search', - }, - stage2: { - url: 'https://staging2.osf.io/', - apiUrl: 'https://staging2-api.osf.io', - renderUrl: 'https://staging2-mfr.osf.io/render', - waterbutlerUrl: 'https://staging2-files.osf.io/', - helpUrl: 'http://help.osf.io', - cookieLoginUrl: 'https://staging2-accounts.osf.io/login', - oauthUrl: 'https://staging2-accounts.osf.io/oauth2/authorize', - shareBaseUrl: 'https://staging-share.osf.io/', - shareApiUrl: 'https://staging-share.osf.io/api/v2', - shareSearchUrl: 'https://staging-share.osf.io/api/v2/search/creativeworks/_search', - }, - stage3: { - url: 'https://staging3.osf.io/', - apiUrl: 'https://staging3-api.osf.io', - renderUrl: 'https://staging3-mfr.osf.io/render', - waterbutlerUrl: 'https://staging3-files.osf.io/', - helpUrl: 'http://help.osf.io', - cookieLoginUrl: 'https://staging3-accounts.osf.io/login', - oauthUrl: 'https://staging3-accounts.osf.io/oauth2/authorize', - shareBaseUrl: 'https://staging-share.osf.io/', - shareApiUrl: 'https://staging-share.osf.io/api/v2', - shareSearchUrl: 'https://staging-share.osf.io/api/v2/search/creativeworks/_search', - }, - prod: { - url: 'https://osf.io/', - apiUrl: 'https://api.osf.io', - renderUrl: 'https://mfr.osf.io/render', - waterbutlerUrl: 'https://files.osf.io/', - helpUrl: 'http://help.osf.io', - cookieLoginUrl: 'https://accounts.osf.io/login', - oauthUrl: 'https://accounts.osf.io/oauth2/authorize', - shareBaseUrl: 'https://share.osf.io/', - shareApiUrl: 'https://share.osf.io/api/v2', - shareSearchUrl: 'https://share.osf.io/api/v2/search/creativeworks/_search', - }, - test: { - url: 'https://test.osf.io/', - apiUrl: 'https://test-api.osf.io', - renderUrl: 'https://test-mfr.osf.io/render', - waterbutlerUrl: 'https://test-files.osf.io/', - helpUrl: 'http://help.osf.io', - cookieLoginUrl: 'https://test-accounts.osf.io/login', - oauthUrl: 'https://test-accounts.osf.io/oauth2/authorize', - shareBaseUrl: 'https://staging-share.osf.io/', - shareApiUrl: 'https://staging-share.osf.io/api/v2', - shareSearchUrl: 'https://staging-share.osf.io/api/v2/search/creativeworks/_search', - }, - env: { - url: 'OSF_URL', - apiUrl: 'OSF_API_URL', - renderUrl: 'OSF_RENDER_URL', - waterbutlerUrl: 'OSF_FILE_URL', - helpUrl: 'OSF_HELP_URL', - cookieLoginUrl: 'OSF_COOKIE_LOGIN_URL', - oauthUrl: 'OSF_OAUTH_URL', - shareBaseUrl: 'SHARE_BASE_URL', - shareApiUrl: 'SHARE_API_URL', - shareSearchUrl: 'SHARE_SEARCH_URL', - }, -}; -function envOrSource(env, source) { - function getKey(keyName) { - return env[keyName] || source[keyName]; - } - return getKey; +let localConfig; + +try { + localConfig = require('./local'); // eslint-disable-line global-require +} catch (ex) { + localConfig = {}; } +const { + BACKEND: backend = 'local', + CLIENT_ID: clientId, + FB_APP_ID, + GIT_COMMIT: release, + GOOGLE_ANALYTICS_ID, + OAUTH_SCOPES: scope, + OSF_URL: url = 'http://localhost:5000/', + OSF_API_URL: apiUrl = 'http://localhost:8000', + OSF_RENDER_URL: renderUrl = 'http://localhost:7778/render', + OSF_FILE_URL: waterbutlerUrl = 'http://localhost:7777/', + OSF_HELP_URL: helpUrl = 'http://localhost:4200/help', + OSF_COOKIE_LOGIN_URL: cookieLoginUrl = 'http://localhost:8080/login', + OSF_OAUTH_URL: oauthUrl = 'http://localhost:8080/oauth2/profile', + PERSONAL_ACCESS_TOKEN: accessToken, + POPULAR_LINKS_NODE: popularNode = '57tnq', + // POPULAR_LINKS_REGISTRATIONS = '', + NEW_AND_NOTEWORTHY_LINKS_NODE: noteworthyNode = 'z3sg2', + REDIRECT_URI: redirectUri, + SHARE_BASE_URL: shareBaseUrl = 'https://staging-share.osf.io/', + SHARE_API_URL: shareApiUrl = 'https://staging-share.osf.io/api/v2', + SHARE_SEARCH_URL: shareSearchUrl = 'https://staging-share.osf.io/api/v2/search/creativeworks/_search', +} = { ...process.env, ...localConfig }; + module.exports = function(environment) { const authorizationType = 'cookie'; + const devMode = environment !== 'production'; const ENV = { modulePrefix: 'ember-osf-web', @@ -104,7 +44,7 @@ module.exports = function(environment) { authorizationType, sentryDSN: null, sentryOptions: { - release: process.env.GIT_COMMIT, + release, }, 'ember-simple-auth': { authorizer: `authorizer:osf-${authorizationType}`, @@ -125,6 +65,9 @@ module.exports = function(environment) { // Here you can pass flags/options to your application instance // when it is created }, + i18n: { + defaultLocale: 'en-US', + }, moment: { includeTimezone: 'all', outputFormat: 'YYYY-MM-DD h:mm A z', @@ -134,11 +77,11 @@ module.exports = function(environment) { name: 'GoogleAnalytics', environments: ['all'], config: { - id: process.env.GOOGLE_ANALYTICS_ID, + id: GOOGLE_ANALYTICS_ID, }, }, ], - FB_APP_ID: process.env.FB_APP_ID, + FB_APP_ID, microfeedback: { enabled: true, url: null, @@ -154,12 +97,30 @@ module.exports = function(environment) { QuickFiles: {}, }, }, + OSF: { + clientId, + scope, + apiNamespace: 'v2', // URL suffix (after host) + backend, + redirectUri, + url, + apiUrl, + renderUrl, + waterbutlerUrl, + helpUrl, + cookieLoginUrl, + oauthUrl, + shareBaseUrl, + shareApiUrl, + shareSearchUrl, + accessToken, + devMode, + }, social: { twitter: { viaHandle: 'OSFramework', }, }, - support: { preregUrl: 'https://cos.io/prereg/', statusPageUrl: 'https://status.cos.io', @@ -172,6 +133,10 @@ module.exports = function(environment) { facebookUrl: 'https://www.facebook.com/CenterForOpenScience/', githubUrl: 'https://github.com/centerforopenscience', }, + dashboard: { + popularNode, + noteworthyNode, + }, }; if (environment === 'development') { @@ -195,59 +160,11 @@ module.exports = function(environment) { ENV.APP.rootElement = '#ember-testing'; } - if (environment !== 'production') { + if (devMode) { // Fallback to throwaway defaults if the environment variables are not set ENV.metricsAdapters[0].config.id = ENV.metricsAdapters[0].config.id || 'UA-84580271-1'; ENV.FB_APP_ID = ENV.FB_APP_ID || '1039002926217080'; } - ENV.i18n = { - defaultLocale: 'en-US', - }; - - const BACKEND = process.env.BACKEND || 'local'; - - // if no local.js, env vars need to be passed - const configFileSettings = BACKEND === 'local' && environment === 'local' ? require('./local') : {}; // eslint-disable-line global-require - - const eitherConfig = envOrSource(process.env, configFileSettings); - - ENV.OSF = { - clientId: eitherConfig('CLIENT_ID'), - scope: eitherConfig('OAUTH_SCOPES'), - apiNamespace: 'v2', // URL suffix (after host) - backend: BACKEND, - redirectUri: eitherConfig('REDIRECT_URI'), - }; - - // Fetch configuration information for the application - let backendUrlConfig = knownBackends[BACKEND] || {}; - - if (!Object.keys(knownBackends).includes(BACKEND)) { - console.warn('WARNING: You have specified an unknown backend environment. If you need to customize URL settings, specify BACKEND=env'); - } - - if (BACKEND === 'local') { - backendUrlConfig.accessToken = eitherConfig('PERSONAL_ACCESS_TOKEN'); - backendUrlConfig.isLocal = true; - } else if (BACKEND === 'prod') { - console.warn("WARNING: you've specified production as a backend. Please do not use production for testing or development purposes"); - } else if (BACKEND === 'env') { - // Optionally draw backend URL settings entirely from environment variables. - // This is all or nothing: If you want to specify a custom backend, you must provide ALL URLs. - const newConfig = {}; - // Map internal config names to the corresponding env var names, eg {url: OSF_URL}. All keys must be present - Object.keys(backendUrlConfig).forEach((internalName) => { - const envVarName = backendUrlConfig[internalName]; - newConfig[internalName] = envVarName; - }); - backendUrlConfig = newConfig; - } - // Warn the user if some URL entries not present - Object.keys(backendUrlConfig).forEach((key) => { - if (!backendUrlConfig[key]) console.error(`This backend must define a value for: ${key}`); - }); - // Combine URLs + auth settings into final auth config - Object.assign(ENV.OSF, backendUrlConfig); return ENV; }; diff --git a/package.json b/package.json index b2f7f197269..a97eaeec419 100644 --- a/package.json +++ b/package.json @@ -23,7 +23,8 @@ "eslint", "tslint" ], - "*.scss": "stylelint" + "*.scss": "stylelint", + "*.hbs": "ember-template-lint" }, "devDependencies": { "@centerforopenscience/eslint-config": "^2.0.0", @@ -36,6 +37,7 @@ "coveralls": "^3.0.0", "ember-ace": "^1.3.1", "ember-ajax": "^2.4.1", + "ember-a11y-testing": "^0.5.0", "ember-bootstrap": "^1.2.1", "ember-browserify": "^1.2.0", "ember-cli": "~2.16.0", @@ -60,7 +62,7 @@ "ember-cli-template-lint": "^0.7.5", "ember-cli-test-loader": "^1.1.0", "ember-cli-tslint": "^0.1.3", - "ember-cli-typescript": "^1.1.5", + "ember-cli-typescript": "^1.2.0", "ember-cli-uglify": "^1.2.0", "ember-click-outside": "^0.1.12", "ember-collapsible-panel": "^2.1.1", @@ -106,7 +108,7 @@ "typescript-eslint-parser": "^13.0.0" }, "engines": { - "node": ">= 0.12.0" + "node": ">= 8" }, "private": true, "bugs": { @@ -114,6 +116,7 @@ }, "homepage": "https://github.com/CenterForOpenScience/ember-osf-web#readme", "dependencies": { + "@types/ember-data": "^2.14.12", "dropzone": "^5.3.0", "jquery-mockjax": "^2.3.0" } diff --git a/testem.js b/testem.js index fae2b82a5d4..c43bbb099ce 100644 --- a/testem.js +++ b/testem.js @@ -1,8 +1,12 @@ /* eslint-env node */ + +const DotReporter = require('testem/lib/reporters/dot_reporter'); + module.exports = { framework: 'qunit', test_page: 'tests/index.html?hidepassed', disable_watching: true, + reporter: new DotReporter(), launch_in_ci: [ 'Chrome', 'Firefox', diff --git a/tests/integration/components/contributor-list/component-test.js b/tests/integration/components/contributor-list/component-test.js deleted file mode 100644 index 2dec9a4a98d..00000000000 --- a/tests/integration/components/contributor-list/component-test.js +++ /dev/null @@ -1,11 +0,0 @@ -import { moduleForComponent, test } from 'ember-qunit'; -import hbs from 'htmlbars-inline-precompile'; - -moduleForComponent('contributor-list', 'Integration | Component | contributor list', { - integration: true, -}); - -test('it renders', function(assert) { - this.render(hbs`{{contributor-list}}`); - assert.equal(this.$().text().trim(), ''); -}); diff --git a/tests/integration/components/contributor-list/component-test.ts b/tests/integration/components/contributor-list/component-test.ts new file mode 100644 index 00000000000..7338148ead8 --- /dev/null +++ b/tests/integration/components/contributor-list/component-test.ts @@ -0,0 +1,70 @@ +import { A } from '@ember/array'; +import EmberObject from '@ember/object'; +import Service from '@ember/service'; +import { moduleForComponent, test } from 'ember-qunit'; +import hbs from 'htmlbars-inline-precompile'; + +const i18nStub = Service.extend({ + translations: EmberObject.create({ + general: EmberObject.create({ + and: 'and', + more: 'more', + }), + }), + + t(key) { + return this.get('translations').get(key); + }, +}); + +moduleForComponent('contributor-list', 'Integration | Component | contributor list', { + integration: true, + + beforeEach() { + this.register('service:i18n', i18nStub); + this.inject.service('i18n', { as: 'i18n' }); + }, +}); + +function nameToUsersFamilyNames(familyName): EmberObject { + return EmberObject.create({ + users: EmberObject.create({ + familyName, + }), + }); +} + +test('it renders', function(assert) { + const testCases = [ + [ + [], + '', + ], + [ + ['Doe'], + 'Doe', + ], + [ + ['Doe', 'Smith'], + 'Doe and Smith', + ], + [ + ['Doe', 'Smith', 'Johnson'], + 'Doe, Smith, and Johnson', + ], + [ + ['Doe', 'Smith', 'Johnson', 'Green'], + 'Doe, Smith, Johnson, and 1 more', + ], + [ + ['Doe', 'Smith', 'Johnson', 'Green', 'Thompson'], + 'Doe, Smith, Johnson, and 2 more', + ], + ]; + + for ([input, expected] of testCases) { + this.set('contributors', A(input.map(nameToUsersFamilyNames))); + this.render(hbs`{{contributor-list contributors=contributors}}`); + assert.equal(this.$().text().trim(), expected); + } +}); diff --git a/tests/integration/components/dashboard-item/component-test.js b/tests/integration/components/dashboard-item/component-test.js index 98d0aa6c6a6..8be3fe80b7c 100644 --- a/tests/integration/components/dashboard-item/component-test.js +++ b/tests/integration/components/dashboard-item/component-test.js @@ -1,11 +1,11 @@ -import { moduleForComponent, test } from 'ember-qunit'; +import { moduleForComponent, skip } from 'ember-qunit'; import hbs from 'htmlbars-inline-precompile'; moduleForComponent('dashboard-item', 'Integration | Component | dashboard item', { integration: true, }); -test('it renders', function(assert) { +skip('it renders', function(assert) { this.render(hbs`{{dashboard-item}}`); assert.ok(this.$().text().trim()); diff --git a/tests/integration/components/new-project-modal/component-test.js b/tests/integration/components/new-project-modal/component-test.js deleted file mode 100644 index 3b3d63375c9..00000000000 --- a/tests/integration/components/new-project-modal/component-test.js +++ /dev/null @@ -1,11 +0,0 @@ -import { moduleForComponent, test } from 'ember-qunit'; -import hbs from 'htmlbars-inline-precompile'; - -moduleForComponent('new-project-modal', 'Integration | Component | new project modal', { - integration: true, -}); - -test('it renders', function(assert) { - this.render(hbs`{{new-project-modal}}`); - assert.ok(this.$().text().trim()); -}); diff --git a/tests/integration/components/new-project-modal/component-test.ts b/tests/integration/components/new-project-modal/component-test.ts new file mode 100644 index 00000000000..8b335035278 --- /dev/null +++ b/tests/integration/components/new-project-modal/component-test.ts @@ -0,0 +1,41 @@ +import EmberObject from '@ember/object'; +import { A } from '@ember/array'; +import { moduleForComponent, test } from 'ember-qunit'; +import hbs from 'htmlbars-inline-precompile'; + +moduleForComponent('new-project-modal', 'Integration | Component | new project modal', { + integration: true, + beforeEach(assert) { + this.setProperties({ + closeModal: () => assert.ok(true), + create: () => assert.ok(true), + removeAll: () => assert.ok(true), + search: () => assert.ok(true), + searchChange: () => assert.ok(true), + selectAll: () => assert.ok(true), + selectInstitution: () => assert.ok(true), + + institutions: A([]), + institutionsSelected: A([]), + newNode: null, + templateFrom: EmberObject.create({}), + }); + }, +}); + +test('it renders', function (assert) { + this.render(hbs`{{new-project-modal + newNode=newNode + institutions=institutions + institutionsSelected=institutionsSelected + closeModal=closeModal + selectInstitution=selectInstitution + selectAll=selectAll + removeAll=removeAll + search=search + searchSelected=templateFrom + searchChange=searchChange + create=create + }}`); + assert.ok(this.$().text().trim()); +}); diff --git a/tests/integration/components/noteworthy-and-popular-project/component-test.js b/tests/integration/components/noteworthy-and-popular-project/component-test.js index 8743809073f..9a9e4dff898 100644 --- a/tests/integration/components/noteworthy-and-popular-project/component-test.js +++ b/tests/integration/components/noteworthy-and-popular-project/component-test.js @@ -1,11 +1,17 @@ import { moduleForComponent, test } from 'ember-qunit'; import hbs from 'htmlbars-inline-precompile'; +import FactoryGuy, { manualSetup } from 'ember-data-factory-guy'; moduleForComponent('noteworthy-and-popular-project', 'Integration | Component | noteworthy and popular project', { integration: true, + + beforeEach() { + manualSetup(this.container); + }, }); test('it renders', function(assert) { - this.render(hbs`{{noteworthy-and-popular-project}}`); + this.set('project', FactoryGuy.make('node')); + this.render(hbs`{{noteworthy-and-popular-project project=project}}`); assert.ok(this.$().text().trim()); }); diff --git a/tests/integration/components/sort-button/component-test.js b/tests/integration/components/sort-button/component-test.js deleted file mode 100644 index ff3ea20baf5..00000000000 --- a/tests/integration/components/sort-button/component-test.js +++ /dev/null @@ -1,15 +0,0 @@ -import { moduleForComponent, test } from 'ember-qunit'; -import hbs from 'htmlbars-inline-precompile'; - -moduleForComponent('sort-button', 'Integration | Component | sort button', { - integration: true, - beforeEach() { - this.set('sortAction', () => {}); - }, -}); - -test('selected works with sortBy', function(assert) { - this.render(hbs`{{sort-button sort=sortAction curOrder='desc' curBy='speed' sortBy='kindness'}}`); - assert.ok(this.$().html().indexOf('not-selected') !== -1, 'sortBy doesn\'t match, should be unselected'); -}); - diff --git a/tests/integration/components/sort-button/component-test.ts b/tests/integration/components/sort-button/component-test.ts new file mode 100644 index 00000000000..85a23b37c5f --- /dev/null +++ b/tests/integration/components/sort-button/component-test.ts @@ -0,0 +1,17 @@ + +import { moduleForComponent, test } from 'ember-qunit'; +import hbs from 'htmlbars-inline-precompile'; + +moduleForComponent('sort-button', 'Integration | Component | sort button', { + integration: true, + beforeEach(assert) { + this.set('sortAction', () => assert.ok(true)); + }, +}); + +test('selected works with sortBy', function (assert) { + this.render(hbs`{{sort-button sortAction=sortAction sortBy='kindness' sort='-kindndess'}}`); + + assert.ok(this.$('button').length, 2); + assert.ok(this.$('.not-selected').length, 'sortBy doesn\'t match, should be unselected'); +}); diff --git a/tests/unit/file-detail/controller-test.ts b/tests/unit/guid-file/controller-test.ts similarity index 80% rename from tests/unit/file-detail/controller-test.ts rename to tests/unit/guid-file/controller-test.ts index f46dcd488c1..5b550a344cb 100644 --- a/tests/unit/file-detail/controller-test.ts +++ b/tests/unit/guid-file/controller-test.ts @@ -1,6 +1,6 @@ import { moduleFor, test } from 'ember-qunit'; -moduleFor('controller:file-detail', 'Unit | Controller | file detail', { +moduleFor('controller:guid-file', 'Unit | Controller | guid file', { needs: [ 'service:currentUser', 'service:toast', diff --git a/tests/unit/user-quickfiles/controller-test.ts b/tests/unit/guid-user/quickfiles/controller-test.ts similarity index 75% rename from tests/unit/user-quickfiles/controller-test.ts rename to tests/unit/guid-user/quickfiles/controller-test.ts index 53954b04a1c..b45617fc71e 100644 --- a/tests/unit/user-quickfiles/controller-test.ts +++ b/tests/unit/guid-user/quickfiles/controller-test.ts @@ -1,6 +1,6 @@ import { moduleFor, test } from 'ember-qunit'; -moduleFor('controller:user-quickfiles', 'Unit | Controller | user quickfiles', { +moduleFor('controller:guid-user/quickfiles', 'Unit | Controller | guid-user/quickfiles', { needs: [ 'service:currentUser', 'service:metrics', diff --git a/tests/unit/user-quickfiles/route-test.ts b/tests/unit/guid-user/quickfiles/route-test.ts similarity index 73% rename from tests/unit/user-quickfiles/route-test.ts rename to tests/unit/guid-user/quickfiles/route-test.ts index 9bc63a90e61..d719cc665f8 100644 --- a/tests/unit/user-quickfiles/route-test.ts +++ b/tests/unit/guid-user/quickfiles/route-test.ts @@ -1,6 +1,6 @@ import { moduleFor, test } from 'ember-qunit'; -moduleFor('route:user-quickfiles', 'Unit | Route | user quickfiles', { +moduleFor('route:guid-user/quickfiles', 'Unit | Route | guid-user/quickfiles', { needs: [ 'service:currentUser', 'service:metrics', diff --git a/tsconfig.json b/tsconfig.json index 606058590ea..6a05a3ea606 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -7,6 +7,7 @@ "noImplicitThis": true, "noEmitOnError": false, "noEmit": true, + "strictNullChecks": true, "sourceMap": true, "baseUrl": ".", "module": "es6", diff --git a/tslint.json b/tslint.json index a07f348aac7..59cb27e67ec 100644 --- a/tslint.json +++ b/tslint.json @@ -16,7 +16,10 @@ "quotemark": [true, "single", "avoid-escape", "avoid-template"], "member-access": false, "no-namespace": [true, "allow-declarations"], - "interface-name": [true, "never-prefix"] + "interface-name": [true, "never-prefix"], + "space-before-function-paren": [true, { "anonymous": false }], + "object-literal-sort-keys": false, + "object-literal-key-quotes": [true, "as-needed"] }, "rulesDirectory": [] } diff --git a/yarn.lock b/yarn.lock index fa58a1ee012..5b787936055 100644 --- a/yarn.lock +++ b/yarn.lock @@ -161,6 +161,12 @@ dependencies: samsam "1.3.0" +"@types/ember-data@^2.14.12": + version "2.14.12" + resolved "https://registry.yarnpkg.com/@types/ember-data/-/ember-data-2.14.12.tgz#015aa5e7fd561023f626eb0001c2203fafd27525" + dependencies: + "@types/rsvp" "*" + "@types/ember-testing-helpers@^0.0.3": version "0.0.3" resolved "https://registry.yarnpkg.com/@types/ember-testing-helpers/-/ember-testing-helpers-0.0.3.tgz#1a6cfc484b63d19ddd822c87e4dd710597db17d9" @@ -586,6 +592,10 @@ aws4@^1.2.1, aws4@^1.6.0: version "1.6.0" resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.6.0.tgz#83ef5ca860b2b32e4a0deedee8c771b9db57471e" +axe-core@^2.4.2: + version "2.6.1" + resolved "https://registry.yarnpkg.com/axe-core/-/axe-core-2.6.1.tgz#28772c4f76966d373acda35b9a409299dc00d1b5" + babel-code-frame@^6.22.0, babel-code-frame@^6.26.0: version "6.26.0" resolved "https://registry.yarnpkg.com/babel-code-frame/-/babel-code-frame-6.26.0.tgz#63fd43f7dc1e3bb7ce35947db8fe369a3f58c74b" @@ -3096,6 +3106,15 @@ elliptic@^6.0.0: minimalistic-assert "^1.0.0" minimalistic-crypto-utils "^1.0.0" +ember-a11y-testing@^0.5.0: + version "0.5.1" + resolved "https://registry.yarnpkg.com/ember-a11y-testing/-/ember-a11y-testing-0.5.1.tgz#30cc67119a0c49b4138a8a5dec08175caa187e8f" + dependencies: + axe-core "^2.4.2" + broccoli-funnel "^2.0.1" + ember-cli-babel "^6.8.2" + ember-cli-version-checker "^2.1.0" + ember-ace@^1.3.1: version "1.3.1" resolved "https://registry.yarnpkg.com/ember-ace/-/ember-ace-1.3.1.tgz#b690cd1fe09a65a264dec40f9a54050af982a8da" @@ -3398,6 +3417,28 @@ ember-cli-legacy-blueprints@^0.1.2: rsvp "^3.0.17" silent-error "^1.0.0" +ember-cli-legacy-blueprints@^0.2.0: + version "0.2.1" + resolved "https://registry.yarnpkg.com/ember-cli-legacy-blueprints/-/ember-cli-legacy-blueprints-0.2.1.tgz#480f37cb83f1eda2d46bbc7d07c59ea2e8ce9b84" + dependencies: + chalk "^2.3.0" + ember-cli-get-component-path-option "^1.0.0" + ember-cli-get-dependency-depth "^1.0.0" + ember-cli-is-package-missing "^1.0.0" + ember-cli-lodash-subset "^2.0.1" + ember-cli-normalize-entity-name "^1.0.0" + ember-cli-path-utils "^1.0.0" + ember-cli-string-utils "^1.0.0" + ember-cli-test-info "^1.0.0" + ember-cli-valid-component-name "^1.0.0" + ember-cli-version-checker "^2.1.0" + ember-router-generator "^1.0.0" + exists-sync "0.0.3" + fs-extra "^4.0.0" + inflection "^1.7.1" + rsvp "^4.7.0" + silent-error "^1.0.0" + ember-cli-lodash-subset@^1.0.7: version "1.0.12" resolved "https://registry.yarnpkg.com/ember-cli-lodash-subset/-/ember-cli-lodash-subset-1.0.12.tgz#af2e77eba5dcb0d77f3308d3a6fd7d3450f6e537" @@ -3557,9 +3598,9 @@ ember-cli-tslint@^0.1.3: tslint "^5.5.0" walk-sync "^0.3.2" -ember-cli-typescript@^1.1.5: - version "1.1.5" - resolved "https://registry.yarnpkg.com/ember-cli-typescript/-/ember-cli-typescript-1.1.5.tgz#64dc940abd5273e1ad108209bf6fcb78dcc0a162" +ember-cli-typescript@^1.2.0: + version "1.2.0" + resolved "https://registry.yarnpkg.com/ember-cli-typescript/-/ember-cli-typescript-1.2.0.tgz#0a46f72b92fee8ac3b1669933cdb83c57c9ed43f" dependencies: broccoli-debug "^0.6.4" broccoli-funnel "^2.0.1" @@ -3568,6 +3609,7 @@ ember-cli-typescript@^1.1.5: broccoli-stew "^1.4.0" chalk "^2.3.0" debug "^3.1.0" + ember-cli "~2.18.2" ember-cli-get-component-path-option "^1.0.0" ember-cli-is-package-missing "^1.0.0" ember-cli-normalize-entity-name "^1.0.0" @@ -3579,6 +3621,7 @@ ember-cli-typescript@^1.1.5: ember-router-generator "^1.2.3" execa "^0.9.0" exists-sync "^0.0.4" + fs-extra "^5.0.0" inflection "^1.12.0" mkdirp "^0.5.1" resolve "^1.5.0" @@ -3702,6 +3745,95 @@ ember-cli@~2.16.0: walk-sync "^0.3.0" yam "0.0.22" +ember-cli@~2.18.2: + version "2.18.2" + resolved "https://registry.yarnpkg.com/ember-cli/-/ember-cli-2.18.2.tgz#bb15313a15139a85248a86d203643f918ba40f57" + dependencies: + amd-name-resolver "1.0.0" + babel-plugin-transform-es2015-modules-amd "^6.24.0" + bower-config "^1.3.0" + bower-endpoint-parser "0.2.2" + broccoli-babel-transpiler "^6.0.0" + broccoli-brocfile-loader "^0.18.0" + broccoli-builder "^0.18.8" + broccoli-concat "^3.2.2" + broccoli-config-loader "^1.0.0" + broccoli-config-replace "^1.1.2" + broccoli-debug "^0.6.3" + broccoli-funnel "^2.0.0" + broccoli-funnel-reducer "^1.0.0" + broccoli-merge-trees "^2.0.0" + broccoli-middleware "^1.0.0" + broccoli-source "^1.1.0" + broccoli-stew "^1.2.0" + calculate-cache-key-for-tree "^1.0.0" + capture-exit "^1.1.0" + chalk "^2.0.1" + clean-base-url "^1.0.0" + compression "^1.4.4" + configstore "^3.0.0" + console-ui "^2.0.0" + core-object "^3.1.3" + dag-map "^2.0.2" + diff "^3.2.0" + ember-cli-broccoli-sane-watcher "^2.0.4" + ember-cli-is-package-missing "^1.0.0" + ember-cli-legacy-blueprints "^0.2.0" + ember-cli-lodash-subset "^2.0.1" + ember-cli-normalize-entity-name "^1.0.0" + ember-cli-preprocess-registry "^3.1.0" + ember-cli-string-utils "^1.0.0" + ember-try "^0.2.15" + ensure-posix-path "^1.0.2" + execa "^0.8.0" + exists-sync "0.0.4" + exit "^0.1.2" + express "^4.12.3" + filesize "^3.1.3" + find-up "^2.1.0" + fs-extra "^4.0.0" + fs-tree-diff "^0.5.2" + get-caller-file "^1.0.0" + git-repo-info "^1.4.1" + glob "7.1.1" + heimdalljs "^0.2.3" + heimdalljs-fs-monitor "^0.1.0" + heimdalljs-graph "^0.3.1" + heimdalljs-logger "^0.1.7" + http-proxy "^1.9.0" + inflection "^1.7.0" + is-git-url "^1.0.0" + isbinaryfile "^3.0.0" + js-yaml "^3.6.1" + json-stable-stringify "^1.0.1" + leek "0.0.24" + lodash.template "^4.2.5" + markdown-it "^8.3.0" + markdown-it-terminal "0.1.0" + minimatch "^3.0.0" + morgan "^1.8.1" + node-modules-path "^1.0.0" + nopt "^3.0.6" + npm-package-arg "^6.0.0" + portfinder "^1.0.7" + promise-map-series "^0.2.1" + quick-temp "^0.1.8" + resolve "^1.3.0" + rsvp "^4.7.0" + sane "^2.2.0" + semver "^5.1.1" + silent-error "^1.0.0" + sort-package-json "^1.4.0" + symlink-or-copy "^1.1.8" + temp "0.8.3" + testem "^2.0.0" + tiny-lr "^1.0.3" + tree-sync "^1.2.1" + uuid "^3.0.0" + validate-npm-package-name "^3.0.0" + walk-sync "^0.3.0" + yam "0.0.22" + ember-click-outside@^0.1.12: version "0.1.12" resolved "https://registry.yarnpkg.com/ember-click-outside/-/ember-click-outside-0.1.12.tgz#4b78ab675b613c2a989e792ca396cb19331e5ad8" @@ -5049,14 +5181,6 @@ fs-extra@^4.0.0, fs-extra@^4.0.1: jsonfile "^4.0.0" universalify "^0.1.0" -fs-extra@^5.0.0: - version "5.0.0" - resolved "https://registry.yarnpkg.com/fs-extra/-/fs-extra-5.0.0.tgz#414d0110cdd06705734d055652c5411260c31abd" - dependencies: - graceful-fs "^4.1.2" - jsonfile "^4.0.0" - universalify "^0.1.0" - fs-readdir-recursive@^0.1.0: version "0.1.2" resolved "https://registry.yarnpkg.com/fs-readdir-recursive/-/fs-readdir-recursive-0.1.2.tgz#315b4fb8c1ca5b8c47defef319d073dad3568059" @@ -5578,7 +5702,7 @@ homedir-polyfill@^1.0.0, homedir-polyfill@^1.0.1: dependencies: parse-passwd "^1.0.0" -hosted-git-info@^2.1.4, hosted-git-info@^2.1.5: +hosted-git-info@^2.1.4, hosted-git-info@^2.1.5, hosted-git-info@^2.5.0: version "2.5.0" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.5.0.tgz#6d60e34b3abbc8313062c3b798ef8d901a07af3c" @@ -6690,6 +6814,10 @@ lodash.bind@~2.3.0: lodash._renative "~2.3.0" lodash._slice "~2.3.0" +lodash.castarray@^4.4.0: + version "4.4.0" + resolved "https://registry.yarnpkg.com/lodash.castarray/-/lodash.castarray-4.4.0.tgz#c02513515e309daddd4c24c60cfddcf5976d9115" + lodash.clonedeep@^4.3.2, lodash.clonedeep@^4.4.1: version "4.5.0" resolved "https://registry.yarnpkg.com/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz#e23f3f9c4f8fbdde872529c1071857a086e5ccef" @@ -7548,6 +7676,15 @@ npm-package-arg@^4.1.1: hosted-git-info "^2.1.5" semver "^5.1.0" +npm-package-arg@^6.0.0: + version "6.0.0" + resolved "https://registry.yarnpkg.com/npm-package-arg/-/npm-package-arg-6.0.0.tgz#8cce04b49d3f9faec3f56b0fe5f4391aeb9d2fac" + dependencies: + hosted-git-info "^2.5.0" + osenv "^0.1.4" + semver "^5.4.1" + validate-npm-package-name "^3.0.0" + npm-path@^2.0.2: version "2.0.4" resolved "https://registry.yarnpkg.com/npm-path/-/npm-path-2.0.4.tgz#c641347a5ff9d6a09e4d9bce5580c4f505278e64" @@ -8844,7 +8981,7 @@ sane@^1.6.0: walker "~1.0.5" watch "~0.10.0" -sane@^2.4.1: +sane@^2.2.0, sane@^2.4.1: version "2.4.1" resolved "https://registry.yarnpkg.com/sane/-/sane-2.4.1.tgz#29f991208cf28636720efdc584293e7fd66663a5" dependencies: @@ -9678,6 +9815,38 @@ testem@^1.18.0: tap-parser "^5.1.0" xmldom "^0.1.19" +testem@^2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/testem/-/testem-2.0.0.tgz#b05c96200c7ac98bae998d71c94c0c5345907d13" + dependencies: + backbone "^1.1.2" + bluebird "^3.4.6" + charm "^1.0.0" + commander "^2.6.0" + consolidate "^0.14.0" + execa "^0.9.0" + express "^4.10.7" + fireworm "^0.7.0" + glob "^7.0.4" + http-proxy "^1.13.1" + js-yaml "^3.2.5" + lodash.assignin "^4.1.0" + lodash.castarray "^4.4.0" + lodash.clonedeep "^4.4.1" + lodash.find "^4.5.1" + lodash.uniqby "^4.7.0" + mkdirp "^0.5.1" + mustache "^2.2.1" + node-notifier "^5.0.1" + npmlog "^4.0.0" + printf "^0.2.3" + rimraf "^2.4.4" + socket.io "1.6.0" + spawn-args "^0.2.0" + styled_string "0.0.1" + tap-parser "^5.1.0" + xmldom "^0.1.19" + text-encoding@0.6.4, text-encoding@^0.6.4: version "0.6.4" resolved "https://registry.yarnpkg.com/text-encoding/-/text-encoding-0.6.4.tgz#e399a982257a276dae428bb92845cb71bdc26d19" From 1ec607bec99934b518757b1ba98eb9292ac70e95 Mon Sep 17 00:00:00 2001 From: Yusuke Komiyama Date: Thu, 8 Mar 2018 12:41:28 +0900 Subject: [PATCH 5/5] fix minor mistakes --- app/locales/ja/translations.ts | 23 +---------------------- 1 file changed, 1 insertion(+), 22 deletions(-) diff --git a/app/locales/ja/translations.ts b/app/locales/ja/translations.ts index b645a3ee7a3..7ac3ae8b272 100644 --- a/app/locales/ja/translations.ts +++ b/app/locales/ja/translations.ts @@ -32,7 +32,6 @@ export default { back: '戻る', filter: 'フィルター', revert: '復帰', -<<<<<<< HEAD save: '保存', ellipses: '\u2026', warning: '警告', @@ -42,9 +41,6 @@ export default { next: '次へ', previous: '戻る', help: 'ヘルプ', -======= - save: '保存', ->>>>>>> origin/develop }, quickfiles: { title: '{{user-name}} のクイックファイル', @@ -254,13 +250,8 @@ export default { search_the_OSF: 'OSF中を検索', send_search: '検索クエリの送信', support: 'サポート', -<<<<<<< HEAD toggle_primary: 'プライマリー・ナビゲーション切替', toggle_secondary: 'セカンダリー・ナビゲーション切替', -======= - toggle_primary: 'トグル・プライマリー・ナビゲーション', - toggle_secondary: 'トグル・セカンダリー・ナビゲーション', ->>>>>>> origin/develop }, auth_dropdown: { log_out: 'ログアウト', @@ -270,11 +261,7 @@ export default { sign_up: '新規登録', sign_in: 'ログイン', user_gravatar: 'ユーザーgravatar', -<<<<<<< HEAD - toggle_auth_dropdown: 'authドロップダウンの切り替え', -======= - toggle_auth_dropdown: 'トグルauthドロップダウン', ->>>>>>> origin/develop + toggle_auth_dropdown: 'authドロップダウンの切替', }, search_help_modal: { close: '閉じる', @@ -288,13 +275,8 @@ export default { title: 'サポート', faq_title: 'よくあるご質問', faq_paragraph: 'どうすれば無料になるのですか? OSFは私の研究にどのように役立つでしょうか? 登録とは何ですか? Open Science Frameworkに関する質問は、', -<<<<<<< HEAD faq_link_text: 'FAQ.', faq_button: 'FAQへ', -======= - faq_link_text: 'FAQページ.', - faq_button: 'FAQを訪問', ->>>>>>> origin/develop guides_title: 'OSFガイド', guides_paragraph_1: '研究ワークフローを改善するためにOSFを使用する方法を学びます。 私たちを読む', guides_link_text: 'ガイド', @@ -320,7 +302,6 @@ export default { social_facebook: 'Facebookでフォロー', social_github: 'GitHubのCOSとコンタクト', }, -<<<<<<< HEAD not_found: { title: 'ぺーじが見つかりません', body: '要求されたリソースが見つかりませんでした。これが発生してはならず、問題が解決しない場合は、サポート{{supportEmail}}へ報告してください', @@ -333,6 +314,4 @@ export default { osf_mode_footer: { dev_mode: 'このサイトは開発モードで動作しています。', }, -======= ->>>>>>> origin/develop };