Compare commits
995 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 187a4c47a1 | |||
| 1296d0657f | |||
| 3b7b45cc03 | |||
| 6be629ae82 | |||
| 752b192ef9 | |||
| cf3b450743 | |||
| 85b8cac9fe | |||
| c9b16729f6 | |||
| b2500e6504 | |||
| 86e6056d3c | |||
| 155d214215 | |||
| c21ad97b6c | |||
| 805f0c790d | |||
| 55fedef21b | |||
| bd9222d9e2 | |||
| 5a51f6d714 | |||
| 14350c5f5d | |||
| 7a2405aa9e | |||
| 7809431873 | |||
| a798c16929 | |||
| b73c55c8da | |||
| fb8a28865f | |||
| 2344e92ec6 | |||
| 97e961d580 | |||
| 42bb568172 | |||
| 04651ea65d | |||
| f2fc803d2e | |||
| 0c8c102f4a | |||
| fa99b7678f | |||
| 33194ef06b | |||
| 95b86b167e | |||
| 81907d0285 | |||
| 03da4eafc6 | |||
| 3f45b27940 | |||
| 6cb40b0189 | |||
| 68b4a5abca | |||
| 57bf994f1b | |||
| fe8a46cee4 | |||
| 7e887f8ba3 | |||
| 23b08853db | |||
| 68716bc6f3 | |||
| 3880e932bb | |||
| 49f7a7714a | |||
| 0ff938a0fd | |||
| 0801c626c5 | |||
| 9cb09b49d1 | |||
| a17bcb9d14 | |||
| 7c57f4b93a | |||
| ec4cde64ff | |||
| c77ae11ea0 | |||
| 57c835d859 | |||
| f8418098ec | |||
| 2e1ca707b0 | |||
| 719989f328 | |||
| da01a3a388 | |||
| bc9867566c | |||
| 92d6c285c4 | |||
| 5c79d37524 | |||
| c80e215110 | |||
| 1c2e8ad040 | |||
| 56be1ad2ae | |||
| c8a104c76e | |||
| 07557f45dc | |||
| 165eeccb23 | |||
| 58989c96c9 | |||
| 4286864263 | |||
| ba7beb827a | |||
| f8a4f85193 | |||
| b89cbfad03 | |||
| 5a38ae488e | |||
| 75b989240a | |||
| 816cefe1b6 | |||
| 360783606e | |||
| 74ebb15dad | |||
| f992f76ae1 | |||
| 7309ef9bfd | |||
| a8251338af | |||
| 55dd5725b5 | |||
| a27884733d | |||
| ca94ac46ba | |||
| 83cc7ed3cc | |||
| 015b17098e | |||
| e096ba8d6e | |||
| a97900b916 | |||
| db20b94fee | |||
| ff2bfb0f02 | |||
| 4deefdfee9 | |||
| a00732c10e | |||
| 5cbcc82e9b | |||
| 99c7262971 | |||
| 3de60daf85 | |||
| c1c62e8bf8 | |||
| cb700404f3 | |||
| f85fb90cae | |||
| 56b8802120 | |||
| 2d4d76e70b | |||
| 5760bea00b | |||
| e2bfdef967 | |||
| 503ca52806 | |||
| e5340d2a7a | |||
| b23715c906 | |||
| 6dea54ccd7 | |||
| f82090fabc | |||
| f6dcee3417 | |||
| 9f66c39839 | |||
| aeb5edf8a0 | |||
| ace029b124 | |||
| d30df21097 | |||
| 892a9bad69 | |||
| ba1f1efb4a | |||
| 61b91881d7 | |||
| 620416f18a | |||
| 0710c569b7 | |||
| 35c82f9c17 | |||
| bb83bf553d | |||
| ec93d5e472 | |||
| af8c78fdee | |||
| 70b3f26db8 | |||
| 141518d6c9 | |||
| dfdada853b | |||
| f61049bac2 | |||
| 74febbec12 | |||
| b89a523d10 | |||
| 4a97a87459 | |||
| 9932c32956 | |||
| 60cf5f7440 | |||
| 50c2070188 | |||
| 152c636fa3 | |||
| b84f10a51a | |||
| a32a383b65 | |||
| 06236b49f7 | |||
| cf794db595 | |||
| 2c1a11e9a3 | |||
| 39c31e759a | |||
| 62da03b9dc | |||
| c7472e6d80 | |||
| 959cfe0425 | |||
| f9e85aadb9 | |||
| 33ca929630 | |||
| a61b96289e | |||
| 9edddc9fd4 | |||
| b297fdfbaf | |||
| 13fb9faa4c | |||
| 737d39dce9 | |||
| 9cf7e53c99 | |||
| 68b17a586a | |||
| ae80f76fd6 | |||
| 7e27feb70b | |||
| 4da03957a9 | |||
| 8355c59b00 | |||
| 8444b2c333 | |||
| fe363e742b | |||
| 5e4fe3b306 | |||
| 3be109df54 | |||
| b0de03cc09 | |||
| db6db06a78 | |||
| 140254ad62 | |||
| d7079e7880 | |||
| 39962c54df | |||
| e3f5d40139 | |||
| 76faee591f | |||
| ccb1be1734 | |||
| da19a23be1 | |||
| e8ed94db98 | |||
| 093487a6cc | |||
| 51824ad84b | |||
| 7411eba8e0 | |||
| 6b628d62d3 | |||
| 6104b3d713 | |||
| 891a32a15c | |||
| f47d68ea18 | |||
| 0806bf4e58 | |||
| 5a77128185 | |||
| 9e3d8f2821 | |||
| 2fbbaebadb | |||
| 3b447cf333 | |||
| 7a44a6b6e8 | |||
| 6fc402e40b | |||
| 8f454cad0e | |||
| 28302107bd | |||
| 8f3ba8a038 | |||
| c92d6778c8 | |||
| 7d881377d2 | |||
| 4bea4bb64c | |||
| ce1e0d6737 | |||
| 6fa547fb83 | |||
| 6cceafa081 | |||
| 12f62de6e4 | |||
| 16647b5c7f | |||
| 90032045a4 | |||
| c7ade09ee8 | |||
| a48bd084e8 | |||
| 7823f65067 | |||
| d3c806de60 | |||
| 238107536e | |||
| fef02ee76f | |||
| 35812c21c6 | |||
| 9ba74825f0 | |||
| 8e701c8460 | |||
| 2ab0c7e067 | |||
| c81c6ab417 | |||
| 8788664061 | |||
| a6d0e2a695 | |||
| 6dd78ca41b | |||
| 44bd2315b2 | |||
| 5f9e82c705 | |||
| b08b1ff013 | |||
| f8a9d71b77 | |||
| aeaa1e7d45 | |||
| 855694c716 | |||
| dadc57d9ea | |||
| a11d79f1dc | |||
| 8049ddbd01 | |||
| 1d88f54b44 | |||
| 63b6bb7407 | |||
| 33d7fa4e1e | |||
| 5c7062d7c9 | |||
| 5aa1868f63 | |||
| f4990b4523 | |||
| 10ef20363a | |||
| 9d14b561a5 | |||
| 37decd9b43 | |||
| 311806faa7 | |||
| 7738499978 | |||
| da82867abe | |||
| 2f41b02e72 | |||
| 3b8b1abb59 | |||
| 977e93754d | |||
| 13c58fb3c2 | |||
| d10ec802c6 | |||
| 135444310d | |||
| e7e0c7624c | |||
| 21cb768fc4 | |||
| a06da481db | |||
| e2156059ef | |||
| 3f3c69d85e | |||
| 4646dd863b | |||
| d04ab7013a | |||
| bda696619e | |||
| 4ff7c2465e | |||
| dd6cea92fb | |||
| bbec5bdf2d | |||
| fb651113fd | |||
| b243e12e5e | |||
| 7100fea5c0 | |||
| 11cdd1ec14 | |||
| 509215ae8d | |||
| 6dc0ffc5c1 | |||
| 348713d4df | |||
| c72d77dbcb | |||
| fab3b479c9 | |||
| f9d193bb5e | |||
| b8eddf73a9 | |||
| 2395ea4d69 | |||
| 9070aa4e6a | |||
| dbbfb27810 | |||
| 5a74f6a080 | |||
| 7d3acc9914 | |||
| 83f9f65489 | |||
| 3dbcf56454 | |||
| a82526736b | |||
| bf04743bc5 | |||
| 7fa7d2f525 | |||
| b23c1d11b3 | |||
| 03cdbd6b69 | |||
| 4cc45ab017 | |||
| 71f35c4c50 | |||
| 73061bdba9 | |||
| 3eead129c9 | |||
| d0fb3750f2 | |||
| 79da307aa1 | |||
| 89a7b70091 | |||
| 3e7fb7ecc2 | |||
| 6bb831fb27 | |||
| 8596a3f121 | |||
| 84fb615005 | |||
| e670260f29 | |||
| 43911b9fd5 | |||
| 037e4f1c43 | |||
| 90dff3dc7f | |||
| b95a9e450d | |||
| 469562c87d | |||
| 11a83320bc | |||
| 2da2ee068d | |||
| 2af1f7242f | |||
| 0092a28bec | |||
| edcda54f89 | |||
| 50f4d5e4f8 | |||
| 2c1472daff | |||
| 54234ef54b | |||
| 475374090a | |||
| 03266730e8 | |||
| b7aa5406ea | |||
| 4093a89b4a | |||
| 2cc1fdf281 | |||
| 92da1f6f87 | |||
| c75c624a1a | |||
| feccee7d62 | |||
| 005c738e4d | |||
| 31258272c2 | |||
| 3641f1b953 | |||
| 9fa79bcc16 | |||
| b67bee2638 | |||
| 01c93b1f67 | |||
| 68b231dbdb | |||
| eb8cc9b17f | |||
| d6b7e975b0 | |||
| e15e119d5d | |||
| 3f8ffa61d7 | |||
| 5949a6e243 | |||
| 397b4b742f | |||
| 967130e57c | |||
| 8cbfc3f730 | |||
| df1705d9ea | |||
| eb965e5c52 | |||
| 8a8eee52fe | |||
| 005ec84c73 | |||
| 42e6e4f235 | |||
| 734f7a757e | |||
| c13a83a3f3 | |||
| 09de6b6739 | |||
| 984e5cc085 | |||
| 079d64795b | |||
| 445800d382 | |||
| 6bc43d8e52 | |||
| 315a83957e | |||
| 1e5f4da905 | |||
| 0ad2ac9eab | |||
| 0eeda259d1 | |||
| acd5825622 | |||
| 8d245e656a | |||
| bcf6ff2245 | |||
| ebff9d3625 | |||
| 4476245f29 | |||
| 1ea8ca5411 | |||
| b291b0e409 | |||
| 07a1860f21 | |||
| 9a8e661fb1 | |||
| ce281d8b87 | |||
| 546b816e4e | |||
| c708f19f10 | |||
| 81107e8492 | |||
| f48bb3c0b2 | |||
| 5a18da1a2f | |||
| a08a8be24e | |||
| c955f8b766 | |||
| 82f210bdaf | |||
| ec3f31354c | |||
| 8cccbfde3f | |||
| 8a4118a9c1 | |||
| d35a554199 | |||
| c92c8ad1ca | |||
| 2b9ab05e1e | |||
| da80fb55c7 | |||
| e61946a0f4 | |||
| 933408f68b | |||
| 9c9867b4e7 | |||
| 12193c2008 | |||
| cf5a873493 | |||
| b895549c04 | |||
| 7854485534 | |||
| 6f3b8a0851 | |||
| 8f188ae505 | |||
| 90793544a7 | |||
| 628790733e | |||
| c1de7f8f70 | |||
| b9dcaa1b18 | |||
| 49def8f405 | |||
| 03bde37a2b | |||
| 3f5affd116 | |||
| 58cfb0ec30 | |||
| e0a4d485bf | |||
| c14f4dc31c | |||
| 4a791ae0b5 | |||
| 073400b51b | |||
| 9967e276c0 | |||
| 091b548e90 | |||
| b2f2e73bbc | |||
| ec47225abd | |||
| bf5922fca0 | |||
| 6d29422c31 | |||
| e13e762349 | |||
| 7c6ad6d903 | |||
| 55d4a21dcb | |||
| 666ee12988 | |||
| d3b669d552 | |||
| 666231db6c | |||
| 01b8dbbd0e | |||
| fd6077696d | |||
| 194b5ddfa0 | |||
| 121dce6d3c | |||
| 53f14f24b0 | |||
| ded61af40e | |||
| e24dc5381d | |||
| d203f43946 | |||
| b590a5a264 | |||
| 2090b9f8e6 | |||
| 390ccf71c2 | |||
| 061b69f962 | |||
| 201a394813 | |||
| 843d42a1d8 | |||
| 02413cefb2 | |||
| 1eddd564d5 | |||
| 459d0ed582 | |||
| 9bd5a659d5 | |||
| 760c942780 | |||
| 0372dc98bb | |||
| cfab2fd191 | |||
| d3eb064b96 | |||
| 2c85345f9f | |||
| 9ddc057589 | |||
| 177bf4d595 | |||
| 3d2a0a060b | |||
| 915ed9381d | |||
| 96ace3265e | |||
| c31cf17b22 | |||
| 4e1f709126 | |||
| ea8acaff4c | |||
| 589536b18d | |||
| 050fa21189 | |||
| cdf465dcda | |||
| de81223413 | |||
| 4d14c5fc23 | |||
| 89f36c1766 | |||
| f87c2bfd16 | |||
| 7e3c44575e | |||
| 1256554ce6 | |||
| 853e712a43 | |||
| 98405c7d86 | |||
| 4d69d06ba1 | |||
| 921f252223 | |||
| 6d6ac3efdd | |||
| 6e73bbdb5a | |||
| 7c20decafe | |||
| a5fd4a08b5 | |||
| 7bafd41a56 | |||
| 46e0595e3a | |||
| 402de8189e | |||
| e42d2d0984 | |||
| e1fdc8f58b | |||
| 60f5cd5c81 | |||
| 4fc1714db8 | |||
| 02b342b157 | |||
| d123d5add0 | |||
| 2f52310d46 | |||
| e0afcdec23 | |||
| 973c95339c | |||
| 5f237194b1 | |||
| 58201d5763 | |||
| 2775b18993 | |||
| 403a7c4e40 | |||
| beadd4c7d3 | |||
| c472f751a1 | |||
| a080d3dd62 | |||
| 453e6b7c96 | |||
| 9057615784 | |||
| eb252fb3f0 | |||
| 79188a6cae | |||
| 0934919e44 | |||
| 4e31366012 | |||
| 2973c46869 | |||
| ee0a339e2b | |||
| 05614bbae4 | |||
| 7b96d5ae41 | |||
| f7c88cfc6a | |||
| 0a6bce1384 | |||
| 81aea827ba | |||
| 68eb67752c | |||
| d3a9ae32a9 | |||
| b23f94dfb5 | |||
| 0b64acaecf | |||
| 42f7d4f8d2 | |||
| d1e50f2ef6 | |||
| 4e16e99758 | |||
| 02c0d577f8 | |||
| 0e4adc0049 | |||
| 1e75f5e822 | |||
| f2373ad21e | |||
| 744a21b244 | |||
| c4eeee3d40 | |||
| 8b2c472536 | |||
| 2c37f87cbc | |||
| 7d1c9e87a9 | |||
| 670efeae31 | |||
| a055b88eb7 | |||
| ee26036105 | |||
| 9dd30138ba | |||
| edc29ede02 | |||
| eb723be3d3 | |||
| 56be3a5b13 | |||
| 347ed2bdc1 | |||
| 8c63f793a5 | |||
| 5a3ad3159c | |||
| ad2321e3e9 | |||
| 5ecb3e419e | |||
| c410c0b8ef | |||
| 31b5e96ba6 | |||
| 979f8d172d | |||
| 506b44f80a | |||
| bef6083e84 | |||
| 860a8a2f91 | |||
| 65270bdbb0 | |||
| 5e3809b35e | |||
| 7501be3bbc | |||
| 3c01bfcdaa | |||
| 85da83f8b0 | |||
| eb82425747 | |||
| ea759d551c | |||
| 355bee8059 | |||
| 026bb2794e | |||
| 92f4dd613c | |||
| 3b1cd41ae2 | |||
| 1af434cd21 | |||
| 43ba3dff86 | |||
| bc78feb0bd | |||
| 5a8758d396 | |||
| c7a0c6a463 | |||
| 523d9a38f2 | |||
| 09afd9154f | |||
| f1b4089d15 | |||
| 338953d728 | |||
| 2a96269d44 | |||
| fc18976b6d | |||
| 40b05b1626 | |||
| a3158b5872 | |||
| 5d4bd1b851 | |||
| e4f50026f1 | |||
| 4f2b190239 | |||
| 315bb1af1c | |||
| f6bd959c4c | |||
| cb2587a6c6 | |||
| 0fc85636f1 | |||
| 7828c15286 | |||
| e153528fcc | |||
| 98f7afb9a1 | |||
| ce2c9c1822 | |||
| 20b1e3b45a | |||
| b8c19cef05 | |||
| efe0e70b03 | |||
| ccbd46f045 | |||
| 18214037c3 | |||
| bb17771a03 | |||
| 9e0a7b513f | |||
| 3aaa735934 | |||
| 2603e945c8 | |||
| d985681e0d | |||
| a0a99b34c2 | |||
| f5c33e9306 | |||
| 790a0365c5 | |||
| d11dc42f85 | |||
| b5958834b2 | |||
| ce819b6929 | |||
| f95f08e5c2 | |||
| 6e4925ecdf | |||
| 9cf9e9f23d | |||
| 30c3a5a10a | |||
| 3a7fc56bd2 | |||
| 7a3436cdea | |||
| 78c9903f7c | |||
| c5e020e90d | |||
| 157915a235 | |||
| cb939735bc | |||
| 92a6db1a25 | |||
| d85d7b38b0 | |||
| a682dbcfc8 | |||
| 36ad8f695a | |||
| ccea24ce6c | |||
| e1fc628202 | |||
| 98395328b2 | |||
| 006a2029dd | |||
| 990f72ebf4 | |||
| 9467f756e2 | |||
| 3e0238eb0c | |||
| de1e75fbf8 | |||
| 190fc57f5e | |||
| c3d1ce3a04 | |||
| f7fef926f8 | |||
| e90ca9cf48 | |||
| 70acb9a7e1 | |||
| 996518eea7 | |||
| eac8290001 | |||
| f88d67f6fb | |||
| 83a9edf81d | |||
| 9638ebcbc5 | |||
| 1a5cecc0c5 | |||
| 661d6f9bdf | |||
| f2ea874801 | |||
| 1fbd89d2fc | |||
| b971a6f6c2 | |||
| be84e7e4ed | |||
| 0360c78219 | |||
| 251d85d88a | |||
| 7dfab1482d | |||
| 77f181fd71 | |||
| cf55847429 | |||
| 4d5eec3174 | |||
| 7574bc482c | |||
| bf2ea7f9f2 | |||
| f44b776d1e | |||
| 720dac6012 | |||
| c42e4d960b | |||
| fb651e53f3 | |||
| adf4b68008 | |||
| c3ddb17514 | |||
| 4a4fcec6fb | |||
| 64b1122550 | |||
| 23abdb52ee | |||
| 52e628272b | |||
| d8f6c3f872 | |||
| d7b9e7a322 | |||
| 2d03054e46 | |||
| 55afbe99f5 | |||
| 75bde6a42d | |||
| 31e27c983b | |||
| 63ada8b3db | |||
| bf6138d5bb | |||
| acb0189d78 | |||
| 6d0f84877a | |||
| 284cd07fa5 | |||
| 7b19f8fa4f | |||
| 84d7d8992a | |||
| 6acb924c38 | |||
| 7371cf50c2 | |||
| c5bb915363 | |||
| 79161523dd | |||
| 2e064de0a9 | |||
| 4fc5b26fe2 | |||
| f48ce84e96 | |||
| a252ccc05b | |||
| b5aa7edd16 | |||
| 2866c16fe3 | |||
| 2c36932ea7 | |||
| 73f6ec846f | |||
| 794754bba8 | |||
| 48bd5ee8a4 | |||
| 75f48f4979 | |||
| f6dd3d34eb | |||
| 91760d284b | |||
| 9f74b0e2c0 | |||
| b9efff586a | |||
| a0c99dde47 | |||
| bc57caa806 | |||
| fe5a567788 | |||
| 3473069bda | |||
| 8e731e878b | |||
| 4896133d58 | |||
| 57c49ca8f4 | |||
| 63a6cdb28b | |||
| bba42bb708 | |||
| c5b5d1e445 | |||
| 84caa20078 | |||
| 327da4d503 | |||
| 1c160a8b27 | |||
| aac0696e02 | |||
| 32bbc80415 | |||
| 9b2424cde0 | |||
| c8a799bce8 | |||
| 5ff41d0590 | |||
| 751aecf360 | |||
| 56fe9d3640 | |||
| 1d2cbf6871 | |||
| b7ae2e591c | |||
| d43229af7e | |||
| 6d04198918 | |||
| 6bd5d81697 | |||
| c595ba9895 | |||
| a14007743f | |||
| d39a11059e | |||
| 822dd27509 | |||
| 88776eaadb | |||
| 5e3aa72955 | |||
| f6715b83ae | |||
| 4d648534f7 | |||
| 95a98f6574 | |||
| dd4629908c | |||
| a7d307aec1 | |||
| 3dea09ee9b | |||
| 2b0301da38 | |||
| c07cfd0d93 | |||
| 88f408c092 | |||
| 362ae1d6dd | |||
| 57aa72ecee | |||
| 154bb883b6 | |||
| b15868fb3c | |||
| 5ba5b663ac | |||
| 7474a3c6a0 | |||
| 9a13a4683e | |||
| 1d2e142f5b | |||
| a6d6dc5136 | |||
| 18cefe8c0b | |||
| 1f34174df1 | |||
| 8d484e2c0e | |||
| 38beb2a7cc | |||
| 5daa5bab45 | |||
| 6e0b232c3e | |||
| c0b46be547 | |||
| 08f6f32779 | |||
| 3195bb4429 | |||
| ab6655554c | |||
| ef8fc2c0e4 | |||
| 37af17fae1 | |||
| 49779b2dd1 | |||
| 79bb782319 | |||
| 404134aa8d | |||
| a0036e244b | |||
| 6f2eef7437 | |||
| 532c7bd2af | |||
| 5553a8ffaa | |||
| 6ebdd43709 | |||
| fff8a52cc2 | |||
| c60e25f71c | |||
| 5521402ee7 | |||
| 7429349d3b | |||
| 9c23c88735 | |||
| c38032ac20 | |||
| 0b90e130c6 | |||
| d24e8fad14 | |||
| 5a7c3133da | |||
| 9883616f9c | |||
| 16def655cf | |||
| 2e8670a8ab | |||
| 992c154c10 | |||
| 8ff05643dc | |||
| ef23cae73d | |||
| c73064a70a | |||
| ecee282fc4 | |||
| ffff724712 | |||
| 56a72ade9d | |||
| 4d9d0ebb97 | |||
| e915aba9db | |||
| 8df16d2651 | |||
| 1571e27ac0 | |||
| 8fe66d81f1 | |||
| e3389c84d1 | |||
| 8cdbf1d5e6 | |||
| b3f37cd3b3 | |||
| 895af7494e | |||
| 8b41dec8ac | |||
| 41a543f6b8 | |||
| efa9e08dfc | |||
| 1954ba4cff | |||
| e22d163a3f | |||
| de98fd0a3c | |||
| 655516535c | |||
| 0e7277a046 | |||
| 9f26485fb5 | |||
| f94678627c | |||
| 33d43c8f43 | |||
| 3b67ffee40 | |||
| b8785a3bf8 | |||
| b3a2d9e39e | |||
| 5174bf1c1e | |||
| 207ec6ffb2 | |||
| 1bb5be68a5 | |||
| de5bbc44f2 | |||
| 1617b7dba1 | |||
| ec250f3ed5 | |||
| 90b965d03f | |||
| fd1b586c6e | |||
| a9be611d00 | |||
| cf2fac019e | |||
| a2ffd74042 | |||
| 99fb4cc478 | |||
| 967c805836 | |||
| daaf305c30 | |||
| 5c3fd15323 | |||
| 5c281a1a3f | |||
| f0f0638be3 | |||
| dcd24d61be | |||
| 423ec6c828 | |||
| c05d2e314d | |||
| d83ec1a027 | |||
| 4fb1a008ca | |||
| 074a002abf | |||
| 94d0d1acd7 | |||
| d1ea30f94c | |||
| 748f6c1f6a | |||
| 261c46c421 | |||
| cb77c6b5af | |||
| e477948713 | |||
| f6742fd440 | |||
| 1ae7996dd9 | |||
| 8624b132a3 | |||
| 7190779abb | |||
| ba50105dd9 | |||
| 317e6e8cc5 | |||
| 44a8e4ea97 | |||
| 81d460f1c2 | |||
| ba4b7a018e | |||
| 71395003c1 | |||
| 4c842c8e8d | |||
| d7db798adc | |||
| 907b8e949e | |||
| 810b62f66e | |||
| 3a97e91844 | |||
| effcf89146 | |||
| 512892a6bc | |||
| 3c28da67ac | |||
| a6fc8f0f2f | |||
| a27fbec44a | |||
| e7b97e3d67 | |||
| ffcc841fe7 | |||
| 0cf5baddb2 | |||
| 04d07ee8d4 | |||
| 1b204e7e7f | |||
| 4434c31fed | |||
| f58d76c5ef | |||
| efc2e3ab17 | |||
| f8af2fc3bf | |||
| 0d1df083d9 | |||
| 0d252add03 | |||
| 66ff9c7a2f | |||
| 7b65b04d9b | |||
| f742e1af78 | |||
| e98037fd64 | |||
| f0610f363d | |||
| 8ff0018eac | |||
| fc0ecc1bb9 | |||
| 562e84fc0d | |||
| 162c7a3085 | |||
| b8e3045b30 | |||
| d7083363b7 | |||
| 40aa64f43f | |||
| c7fbaa3f80 | |||
| 8220a09aa1 | |||
| 2a2e4f285c | |||
| 4b9ca3f13e | |||
| 572134f344 | |||
| 7b15b84daa | |||
| 5e5f31c375 | |||
| f285377abf | |||
| 89b1afa470 | |||
| fd2dc3abec | |||
| a9892d2dcb | |||
| 6a668149b6 | |||
| 11f0c1f5e6 | |||
| d6aee4d2d1 | |||
| 4e87dcbd64 | |||
| d7881fa31a | |||
| 6234bdc0df | |||
| e60407378a | |||
| 12a448aa24 | |||
| 5f6d70f751 | |||
| d11390fadd | |||
| 3696fa95d7 | |||
| f91c6d193a | |||
| 0202d09a9d | |||
| 00a3deec29 | |||
| 9a738c4432 | |||
| 40cb48a10a | |||
| 690a12ceba | |||
| 8ea2cc0928 | |||
| 473860aaa3 | |||
| 775285bba9 | |||
| 212618de47 | |||
| 7c0c944cb4 | |||
| c5cfc621ca | |||
| ce4c8b3bff | |||
| 55b83226c9 | |||
| 29e273f5a1 | |||
| a6b05f5fd3 | |||
| 8089decc17 | |||
| f5ab362f96 | |||
| 20a2c570ba | |||
| 01ea22ed6d | |||
| b7801f9874 | |||
| 1412b39620 | |||
| cec4393837 | |||
| f36741f131 | |||
| 4394b0b6e7 | |||
| 278df69c55 | |||
| c5a1f3bbce | |||
| 91644281c4 | |||
| 047edbd6e8 | |||
| 407e8ee13c | |||
| 137b8450a8 | |||
| 121ec4b3f7 | |||
| 51c3ae9807 | |||
| 6b118ce933 | |||
| 19ffe483c6 | |||
| 3b985a6dbb | |||
| 0d2a5a4c90 | |||
| fa5f27d17f | |||
| 590e189474 | |||
| 68d61d115c | |||
| 67c32a396f | |||
| 60a5b57121 | |||
| cbdf32e7b1 | |||
| 8647993443 | |||
| e2ad6e7715 | |||
| 930ed809f5 | |||
| adeb844918 | |||
| fd1b550f93 | |||
| b51ac6aa7c | |||
| 8a9f2a3cc2 | |||
| 3bb0c26f33 | |||
| 21e0a3558d | |||
| 3c11aca1aa | |||
| 91b9bb4054 | |||
| d383f142ee | |||
| ecd101833a | |||
| 9764ac6848 | |||
| dcf199c0aa | |||
| efcc312e9e | |||
| ab7ae8434b | |||
| efbf42f698 | |||
| 2bb41977ed | |||
| e3e32365d0 | |||
| 924ba7256b | |||
| 056289789a | |||
| 840c677c5e | |||
| 1acf901c68 | |||
| 63cdbdc874 | |||
| 0b9fdb371e | |||
| c802e4806a | |||
| d9c5d37810 | |||
| f0ab2924db | |||
| a6b313f38e | |||
| 337345560d | |||
| 3748bb2140 | |||
| 60d8a595f0 | |||
| c348bd6bb1 | |||
| 979f796cb5 | |||
| ad5f218f23 | |||
| d2a677d15f | |||
| f0011a564e | |||
| df90f4aade | |||
| 7c6174edc5 | |||
| 939820bffb | |||
| c16197670b | |||
| 2dc033ee20 | |||
| ad7aaa1a11 | |||
| 54adff3779 | |||
| f6bff7a58f | |||
| 78976f3f51 | |||
| 8790640fb0 | |||
| 1c966663e0 | |||
| e1eca134fc | |||
| d1b81a385f | |||
| 5d5cfd1b7d | |||
| b2b2c641da | |||
| ce6ec0acfb | |||
| 086a39ca3d | |||
| 8cffc4872c | |||
| 26a9f588a1 | |||
| 911bfb446e | |||
| 766066f9dc | |||
| 9d55a88d4a | |||
| b81eadda8d | |||
| 37d9ae4136 | |||
| bc67d9e1e5 | |||
| 7fadd6be69 | |||
| 83642f22c1 | |||
| bc9c2c1232 | |||
| 8390e075e1 | |||
| 39b687a58f | |||
| 46bb456063 | |||
| 2b065eb7fa | |||
| 2b0f75e62b | |||
| 4f70c9a8e5 | |||
| 520c489e62 | |||
| 2b28c750a8 | |||
| 657feb777f | |||
| 56489ee6cd | |||
| c1549193f2 | |||
| fa64e53792 | |||
| d9a95ffc18 | |||
| 79949b4aa6 | |||
| 140547d035 | |||
| 11a61180ed | |||
| 475646f7c1 | |||
| a9d5215d2b | |||
| 45fb18e609 | |||
| 1bcd176285 | |||
| 39414e269b | |||
| ede80075c1 | |||
| 9e5250353b | |||
| de44d8024b | |||
| 32ec2bacce | |||
| 5042e2572c | |||
| af3c45b019 | |||
| 79d63e7b70 | |||
| 6c1074443f | |||
| 0dce8d2bf5 | |||
| a1a41bfa43 | |||
| 3dd7b33363 | |||
| 434021239c | |||
| f189c98c56 | |||
| 6cdcd89387 | |||
| 1ddc3172eb | |||
| 0f8a4734b2 | |||
| b452932767 | |||
| 5347d902c0 | |||
| e6d01ef1b9 | |||
| 4037b6304e |
@@ -2,10 +2,10 @@
|
|||||||
.git
|
.git
|
||||||
.gitignore
|
.gitignore
|
||||||
README.md
|
README.md
|
||||||
|
SECURITY.md
|
||||||
|
|
||||||
# Rails
|
# Rails
|
||||||
.env
|
.env
|
||||||
.env.prod
|
|
||||||
*.rbc
|
*.rbc
|
||||||
capybara-*.html
|
capybara-*.html
|
||||||
.rspec
|
.rspec
|
||||||
@@ -13,14 +13,17 @@ log
|
|||||||
tmp
|
tmp
|
||||||
/db/**/*.sqlite3
|
/db/**/*.sqlite3
|
||||||
/db/**/*.sqlite3-journal
|
/db/**/*.sqlite3-journal
|
||||||
public/system
|
/db/production
|
||||||
|
/db/production-postgres
|
||||||
|
public/assets
|
||||||
|
public/b
|
||||||
coverage/
|
coverage/
|
||||||
spec/tmp
|
|
||||||
.rvmrc
|
.rvmrc
|
||||||
.byebug_history
|
vendor/bundle
|
||||||
|
.bundle
|
||||||
# docker
|
Dockerfile
|
||||||
docker-compose.yml
|
.gitlab-ci.yml
|
||||||
production-compose.yml
|
.rubocop.yml
|
||||||
|
.travis.yml
|
||||||
.atom
|
spec
|
||||||
|
test
|
||||||
@@ -6,21 +6,46 @@
|
|||||||
|
|
||||||
# Ignore bundler config.
|
# Ignore bundler config.
|
||||||
/.bundle
|
/.bundle
|
||||||
vendor/.bundle
|
vendor/bundle
|
||||||
|
|
||||||
# Ignore the default SQLite database.
|
# Ignore the default SQLite database.
|
||||||
/db/**/*.sqlite3
|
/db/*.sqlite3
|
||||||
/db/**/*.sqlite3-journal
|
/db/*.sqlite3-journal
|
||||||
|
|
||||||
|
# Ignore static assets.
|
||||||
/public/system/**
|
/public/system/**
|
||||||
/public/assets/**
|
/public/assets/**
|
||||||
|
/public/b/**
|
||||||
|
|
||||||
|
# Ignore uploaded files
|
||||||
|
/storage
|
||||||
|
|
||||||
|
# Ignore production paths.
|
||||||
|
/db/production
|
||||||
|
/db/production-postgres
|
||||||
|
|
||||||
# Ignore all logfiles and tempfiles.
|
# Ignore all logfiles and tempfiles.
|
||||||
/log/*
|
/log/*
|
||||||
/tmp/*
|
/tmp
|
||||||
!/log/.keep
|
!/log/.keep
|
||||||
!/tmp/.keep
|
|
||||||
.env
|
|
||||||
coverage
|
|
||||||
|
|
||||||
# Ignore Byebug command history file.
|
# Ignore Byebug command history file.
|
||||||
.byebug_history
|
.byebug_history
|
||||||
|
|
||||||
|
# Ignore environment configuration.
|
||||||
|
.env
|
||||||
|
env
|
||||||
|
|
||||||
|
# Ignore yarn configs
|
||||||
|
/node_modules
|
||||||
|
|
||||||
|
# IDEs
|
||||||
|
.idea
|
||||||
|
.idea/**
|
||||||
|
.vscode
|
||||||
|
.vscode/**
|
||||||
|
|
||||||
|
config/terms.md
|
||||||
|
coverage*
|
||||||
|
|
||||||
|
/atom-sftp-sync.json
|
||||||
|
|||||||
@@ -0,0 +1,71 @@
|
|||||||
|
stages:
|
||||||
|
- test
|
||||||
|
- build
|
||||||
|
- deploy
|
||||||
|
|
||||||
|
cache:
|
||||||
|
paths:
|
||||||
|
- /cache
|
||||||
|
|
||||||
|
before_script:
|
||||||
|
|
||||||
|
test:
|
||||||
|
stage: test
|
||||||
|
image: ruby:2.5
|
||||||
|
script:
|
||||||
|
- apt-get update -qq && apt-get install -y -qq sqlite3 libsqlite3-dev nodejs
|
||||||
|
- bundle install --path /cache
|
||||||
|
- bundle exec rake db:create RAILS_ENV=test
|
||||||
|
- bundle exec rake test & bundle exec rspec & bundle exec rubocop
|
||||||
|
except:
|
||||||
|
variables:
|
||||||
|
- $CD_TEST_IGNORE
|
||||||
|
|
||||||
|
build:
|
||||||
|
stage: build
|
||||||
|
image: docker:stable
|
||||||
|
services:
|
||||||
|
- docker:dind
|
||||||
|
script:
|
||||||
|
# Install bash, curl, git for deployment script
|
||||||
|
- apk update && apk add --no-cache bash curl git
|
||||||
|
# Install CA certs, openssl to https downloads, python for gcloud sdk
|
||||||
|
- apk add --update make ca-certificates openssl python
|
||||||
|
- update-ca-certificates
|
||||||
|
# Build.
|
||||||
|
- ./scripts/image_build.sh $CI_PROJECT_PATH $CI_COMMIT_REF_NAME $CI_COMMIT_SHA
|
||||||
|
only:
|
||||||
|
refs:
|
||||||
|
- branches
|
||||||
|
- tags
|
||||||
|
variables:
|
||||||
|
- $CD_DOCKER_USERNAME
|
||||||
|
- $CD_DOCKER_PASSWORD
|
||||||
|
except:
|
||||||
|
variables:
|
||||||
|
- $CD_BUILD_IGNORE
|
||||||
|
|
||||||
|
deploy:
|
||||||
|
stage: deploy
|
||||||
|
image: docker:stable
|
||||||
|
services:
|
||||||
|
- docker:dind
|
||||||
|
script:
|
||||||
|
# Install bash, curl, git for deployment script
|
||||||
|
- apk update && apk add --no-cache bash curl git
|
||||||
|
# Install CA certs, openssl to https downloads, python for gcloud sdk
|
||||||
|
- apk add --update make ca-certificates openssl python
|
||||||
|
- update-ca-certificates
|
||||||
|
# Deploy.
|
||||||
|
- ./scripts/image_deploy.sh $CI_PROJECT_PATH $CI_COMMIT_REF_NAME $CI_COMMIT_SHA $CI_COMMIT_BEFORE_SHA
|
||||||
|
only:
|
||||||
|
refs:
|
||||||
|
- branches
|
||||||
|
- tags
|
||||||
|
variables:
|
||||||
|
- $CD_DOCKER_USERNAME
|
||||||
|
- $CD_DOCKER_PASSWORD
|
||||||
|
- $CD_DEPLOY_SCRIPT
|
||||||
|
except:
|
||||||
|
variables:
|
||||||
|
- $CD_DEPLOY_IGNORE
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
about
|
||||||
|
app:template
|
||||||
|
app:update
|
||||||
|
assets:clean[keep]
|
||||||
|
assets:clobber
|
||||||
|
assets:environment
|
||||||
|
assets:precompile
|
||||||
|
autoprefixer:info
|
||||||
|
cache_digests:dependencies
|
||||||
|
cache_digests:nested_dependencies
|
||||||
|
conf:check
|
||||||
|
db:create
|
||||||
|
db:drop
|
||||||
|
db:environment:set
|
||||||
|
db:fixtures:load
|
||||||
|
db:migrate
|
||||||
|
db:migrate:status
|
||||||
|
db:rollback
|
||||||
|
db:schema:cache:clear
|
||||||
|
db:schema:cache:dump
|
||||||
|
db:schema:dump
|
||||||
|
db:schema:load
|
||||||
|
db:seed
|
||||||
|
db:setup
|
||||||
|
db:structure:dump
|
||||||
|
db:structure:load
|
||||||
|
db:version
|
||||||
|
dev:cache
|
||||||
|
initializers
|
||||||
|
log:clear
|
||||||
|
middleware
|
||||||
|
notes
|
||||||
|
notes:custom
|
||||||
|
restart
|
||||||
|
routes
|
||||||
|
secret
|
||||||
|
stats
|
||||||
|
test
|
||||||
|
test:db
|
||||||
|
time:zones[country_or_offset]
|
||||||
|
tmp:clear
|
||||||
|
tmp:create
|
||||||
@@ -0,0 +1,179 @@
|
|||||||
|
AllCops:
|
||||||
|
Exclude:
|
||||||
|
- 'db/schema.rb'
|
||||||
|
- 'vendor/**/*'
|
||||||
|
DisabledByDefault: false
|
||||||
|
TargetRubyVersion: 2.5
|
||||||
|
|
||||||
|
# Gems within groups in the Gemfile should be alphabetically sorted.
|
||||||
|
Bundler/OrderedGems:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
Style/BlockDelimiters:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
# Checks if uses of quotes match the configured preference.
|
||||||
|
Style/StringLiterals:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
# Document classes and non-namespace modules.
|
||||||
|
Style/Documentation:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
# Check for conditionals that can be replaced with guard clauses
|
||||||
|
Style/GuardClause:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
# Checks the formatting of empty method definitions.
|
||||||
|
Style/EmptyMethod:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
# Checks for trailing comma in hash literals.
|
||||||
|
Style/TrailingCommaInHashLiteral:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
# Checks for trailing comma in argument lists.
|
||||||
|
Style/TrailingCommaInArguments:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
# Checks that `include`, `extend` and `prepend` exists at the top level.
|
||||||
|
Style/MixinUsage:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
# Use %i or %I for arrays of symbols.
|
||||||
|
Style/SymbolArray:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
# Don't use begin blocks when they are not needed.
|
||||||
|
Style/RedundantBegin:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
# Use `%`-literal delimiters consistently
|
||||||
|
Style/PercentLiteralDelimiters:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
# Only use if/unless modifiers on single line statements.
|
||||||
|
Style/MultilineIfModifier:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
# Checks for trailing comma in array literals.
|
||||||
|
Style/TrailingCommaInArrayLiteral:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
# Use `expand_path(__dir__)` instead of `expand_path('..', __FILE__)`.
|
||||||
|
Style/ExpandPathArguments:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
# Do not assign mutable objects to constants.
|
||||||
|
Style/MutableConstant:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
# Avoid rescuing without specifying an error class.
|
||||||
|
Style/RescueStandardError:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
# Align the elements of a hash literal if they span more than one line.
|
||||||
|
Layout/HashAlignment:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
# Align the parameters of a method definition if they span more than one line.
|
||||||
|
Layout/ParameterAlignment:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
# Align ends corresponding to defs correctly.
|
||||||
|
Layout/EndAlignment:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
# Align elses and elsifs correctly.
|
||||||
|
Layout/ElseAlignment:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
# Add empty line after guard clause.
|
||||||
|
Layout/EmptyLineAfterGuardClause:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
# Align the arguments of a method call if they span more than one line.
|
||||||
|
Layout/ArgumentAlignment:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
#
|
||||||
|
Layout/IndentationWidth:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
Layout/CaseIndentation:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
# Checks for ambiguous block association with method when param passed without parentheses.
|
||||||
|
Lint/AmbiguousBlockAssociation:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
# Avoid long blocks with many lines.
|
||||||
|
Metrics/BlockLength:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
# A complexity metric geared towards measuring complexity for a human reader.
|
||||||
|
Metrics/PerceivedComplexity:
|
||||||
|
Max: 17
|
||||||
|
|
||||||
|
# Avoid classes longer than 100 lines of code.
|
||||||
|
Metrics/ClassLength:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
# Limit lines to 80 characters.
|
||||||
|
Layout/LineLength:
|
||||||
|
Max: 130
|
||||||
|
|
||||||
|
# Avoid methods longer than 10 lines of code.
|
||||||
|
Metrics/MethodLength:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
Metrics/ModuleLength:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
# A calculated magnitude based on number of assignments,
|
||||||
|
# branches, and conditions.
|
||||||
|
Metrics/AbcSize:
|
||||||
|
Max: 60
|
||||||
|
|
||||||
|
# A complexity metric that is strongly correlated to the number
|
||||||
|
# of test cases needed to validate a method.
|
||||||
|
Metrics/CyclomaticComplexity:
|
||||||
|
Max: 17
|
||||||
|
|
||||||
|
# Checks for method parameter names that contain capital letters, end in numbers, or do not meet a minimal length.
|
||||||
|
Naming/MethodParameterName:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
Lint/LiteralInInterpolation:
|
||||||
|
Enabled: false
|
||||||
|
|
||||||
|
Layout/EmptyLinesAroundAttributeAccessor:
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
Layout/SpaceAroundMethodCallOperator:
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
Lint/DeprecatedOpenSSLConstant:
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
Lint/RaiseException:
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
Lint/StructNewOverride:
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
Style/ExponentialNotation:
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
Style/HashEachMethods:
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
Style/HashTransformKeys:
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
Style/HashTransformValues:
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
|
Style/SlicingWithRange:
|
||||||
|
Enabled: true
|
||||||
|
|
||||||
@@ -0,0 +1 @@
|
|||||||
|
2.5.1
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
sudo: required
|
||||||
|
language: ruby
|
||||||
|
cache: bundler
|
||||||
|
rvm:
|
||||||
|
- 2.5.1
|
||||||
|
jobs:
|
||||||
|
include:
|
||||||
|
- stage: test
|
||||||
|
name: rubocop
|
||||||
|
script: bundle exec rubocop
|
||||||
|
if: env(CD_TEST_IGNORE) IS NOT present
|
||||||
|
- stage: test
|
||||||
|
name: rspec
|
||||||
|
script: gem install bundler & bundle exec rspec
|
||||||
|
if: env(CD_TEST_IGNORE) IS NOT present
|
||||||
|
- stage: build
|
||||||
|
name: build docker image
|
||||||
|
script: bash scripts/image_build.sh $TRAVIS_REPO_SLUG $TRAVIS_BRANCH
|
||||||
|
if: env(CD_BUILD_IGNORE) IS NOT present AND type NOT IN (pull_request) AND env(CD_DOCKER_USERNAME)
|
||||||
|
IS present AND env(CD_DOCKER_PASSWORD) IS present
|
||||||
|
notifications:
|
||||||
|
email: false
|
||||||
|
slack:
|
||||||
|
secure: T64A1ut/3LdnUpPfsywNkjbyR2AIRlm99SM40xnLmXjM6Q0EtcFJ4luymC0EQB2q86WmFiK3AyPNm/EKGw8ZmjgeJIUkhMm8zZOLnYLsqMZeYmNNlDtV4zbhQ0HO3uvBqGVCSD2LTcGhCU/9VAplWpa9VnWUsWeRfklQ1yJ4y9oKtzEuiFHrILbDfqF47SlkQqbhS/Bl/oGHVVXijAmomJGG4wa9UI0PlSOUL2sJxxMhti+fptSwKo+TgKS8Vtjeu3C10QfRlcSmoDS0vaQgm7fggm/GrQ8zkSC/SKzOes/+hhRBE+TERWkx26azEy2kmI6xIkNf2BqFjDtQkjKN9yJLdJ1Kyry0uk7Eco67klEN/WoJ38NBuHVkq7p2CWWawFMg3OCS/3D9DStvYJBTBFrv7LMGKfhB4UdOgr/aSRlCTiF2grxqaXmDp0p/1J3uHIfDxbXiwx2ySjJFSC9e0/nTkJprPCQxVoZO+OFk0y2ElHMrX5L0qA2kZ4tQaPf04pQNguCgEsTnBtivVVMxCn9+ymgcHAnzGZmQmQ52NoZBQpa2FjBfhfxGOqHWJm6Kuk3p5yrGZTqlkf3GlTIlFNL8y9/5w6QaxQJbZLYSBbxAtJNvMWN/1cUwdbRTE4D0QTThASTgN6TtPKfWwIRrfj5+G9cAWo6DunmNhzATZ9g=
|
||||||
@@ -1,20 +1,65 @@
|
|||||||
FROM ruby:2.4.0
|
FROM ruby:2.5.1-alpine AS base
|
||||||
|
|
||||||
# app dependencies
|
# Set a variable for the install location.
|
||||||
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
|
ARG RAILS_ROOT=/usr/src/app
|
||||||
|
# Set Rails environment.
|
||||||
|
ENV RAILS_ENV production
|
||||||
|
ENV BUNDLE_APP_CONFIG="$RAILS_ROOT/.bundle"
|
||||||
|
|
||||||
ENV RAILS_ENV=production \
|
# Make the directory and set as working.
|
||||||
APP_HOME=/usr/src/app
|
RUN mkdir -p $RAILS_ROOT
|
||||||
|
WORKDIR $RAILS_ROOT
|
||||||
|
|
||||||
|
ARG BUILD_PACKAGES="build-base curl-dev git"
|
||||||
|
ARG DEV_PACKAGES="postgresql-dev sqlite-libs sqlite-dev yaml-dev zlib-dev nodejs yarn"
|
||||||
|
ARG RUBY_PACKAGES="tzdata"
|
||||||
|
|
||||||
|
# Install app dependencies.
|
||||||
|
RUN apk update \
|
||||||
|
&& apk upgrade \
|
||||||
|
&& apk add --update --no-cache $BUILD_PACKAGES $DEV_PACKAGES $RUBY_PACKAGES
|
||||||
|
|
||||||
|
COPY Gemfile* ./
|
||||||
|
COPY Gemfile Gemfile.lock $RAILS_ROOT/
|
||||||
|
|
||||||
|
RUN bundle config --global frozen 1 \
|
||||||
|
&& bundle install --deployment --without development:test:assets -j4 --path=vendor/bundle \
|
||||||
|
&& rm -rf vendor/bundle/ruby/2.5.0/cache/*.gem \
|
||||||
|
&& find vendor/bundle/ruby/2.5.0/gems/ -name "*.c" -delete \
|
||||||
|
&& find vendor/bundle/ruby/2.5.0/gems/ -name "*.o" -delete
|
||||||
|
|
||||||
|
# Adding project files.
|
||||||
|
COPY . .
|
||||||
|
|
||||||
|
# Remove folders not needed in resulting image
|
||||||
|
RUN rm -rf tmp/cache spec
|
||||||
|
|
||||||
|
############### Build step done ###############
|
||||||
|
|
||||||
|
FROM ruby:2.5.1-alpine
|
||||||
|
|
||||||
|
# Set a variable for the install location.
|
||||||
|
ARG RAILS_ROOT=/usr/src/app
|
||||||
|
ARG PACKAGES="tzdata curl postgresql-client sqlite-libs yarn nodejs bash"
|
||||||
|
|
||||||
|
ENV RAILS_ENV=production
|
||||||
|
ENV BUNDLE_APP_CONFIG="$RAILS_ROOT/.bundle"
|
||||||
|
|
||||||
|
WORKDIR $RAILS_ROOT
|
||||||
|
|
||||||
|
RUN apk update \
|
||||||
|
&& apk upgrade \
|
||||||
|
&& apk add --update --no-cache $PACKAGES
|
||||||
|
|
||||||
|
|
||||||
RUN mkdir $APP_HOME
|
COPY --from=base $RAILS_ROOT $RAILS_ROOT
|
||||||
WORKDIR $APP_HOME
|
|
||||||
|
|
||||||
# Add the greenlight app
|
# Expose port 80.
|
||||||
ADD . $APP_HOME
|
EXPOSE 80
|
||||||
|
|
||||||
# Install app dependencies
|
# Sets the footer of greenlight application with current build version
|
||||||
RUN bundle install --without development test doc --deployment --clean
|
ARG version_code
|
||||||
RUN bundle exec rake assets:precompile --trace
|
ENV VERSION_CODE=$version_code
|
||||||
|
|
||||||
CMD ["scripts/default_start.sh"]
|
# Start the application.
|
||||||
|
CMD ["bin/start"]
|
||||||
|
|||||||
@@ -1,10 +0,0 @@
|
|||||||
FROM ruby:2.4.0
|
|
||||||
|
|
||||||
# app dependencies
|
|
||||||
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs
|
|
||||||
|
|
||||||
ENV RAILS_ENV=development \
|
|
||||||
APP_HOME=/usr/src/app
|
|
||||||
|
|
||||||
RUN mkdir $APP_HOME
|
|
||||||
WORKDIR $APP_HOME
|
|
||||||
@@ -1,98 +1,143 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
source 'https://rubygems.org'
|
source 'https://rubygems.org'
|
||||||
|
|
||||||
ruby '2.4.0'
|
git_source(:github) do |repo_name|
|
||||||
|
repo_name = "#{repo_name}/#{repo_name}" unless repo_name.include?("/")
|
||||||
|
"https://github.com/#{repo_name}.git"
|
||||||
|
end
|
||||||
|
|
||||||
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
|
# Bundle edge Rails instead: gem 'rails', github: 'rails/rails'
|
||||||
gem 'rails', '~> 5.0.0', '>= 5.0.0.1'
|
gem 'rails', '~> 5.2.4.4'
|
||||||
# Use sqlite3 as the database for Active Record
|
|
||||||
gem 'sqlite3'
|
|
||||||
# Use postgres as the database for Active Record
|
|
||||||
gem 'pg'
|
|
||||||
# Use Puma as the app server
|
# Use Puma as the app server
|
||||||
gem 'puma', '~> 3.0'
|
gem 'puma', '~> 3.12'
|
||||||
|
|
||||||
# Use SCSS for stylesheets
|
# Use SCSS for stylesheets
|
||||||
gem 'sass-rails', '~> 5.0'
|
gem 'sassc-rails'
|
||||||
|
|
||||||
# Use Uglifier as compressor for JavaScript assets
|
# Use Uglifier as compressor for JavaScript assets
|
||||||
gem 'uglifier', '>= 1.3.0'
|
gem 'uglifier', '>= 1.3.0'
|
||||||
|
|
||||||
# Use CoffeeScript for .coffee assets and views
|
# Use CoffeeScript for .coffee assets and views
|
||||||
gem 'coffee-rails', '~> 4.2'
|
gem 'coffee-rails', '~> 4.2'
|
||||||
|
|
||||||
# See https://github.com/rails/execjs#readme for more supported runtimes
|
# See https://github.com/rails/execjs#readme for more supported runtimes
|
||||||
gem 'therubyracer', platforms: :ruby
|
# gem 'mini_racer', platforms: :ruby
|
||||||
|
|
||||||
# Use jquery as the JavaScript library
|
# Use jquery as the JavaScript library
|
||||||
gem 'jquery-rails'
|
gem 'jquery-rails', '~> 4.4'
|
||||||
|
gem 'jquery-ui-rails'
|
||||||
|
|
||||||
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
|
# Turbolinks makes navigating your web application faster. Read more: https://github.com/turbolinks/turbolinks
|
||||||
gem 'turbolinks', '~> 5'
|
gem 'turbolinks', '~> 5'
|
||||||
|
|
||||||
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
|
# Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder
|
||||||
gem 'jbuilder', '~> 2.5'
|
gem 'jbuilder', '~> 2.5'
|
||||||
|
|
||||||
# Use Redis adapter to run Action Cable in production
|
# Use Redis adapter to run Action Cable in production
|
||||||
# gem 'redis', '~> 3.0'
|
# gem 'redis', '~> 3.0'
|
||||||
|
|
||||||
# Use ActiveModel has_secure_password
|
# Use ActiveModel has_secure_password
|
||||||
# gem 'bcrypt', '~> 3.1.7'
|
gem 'bcrypt', '~> 3.1.7'
|
||||||
|
|
||||||
|
# Reduces boot times through caching; required in config/boot.rb
|
||||||
|
gem 'bootsnap', '>= 1.1.0', require: false
|
||||||
|
|
||||||
|
gem 'sprockets', '< 4.0.0'
|
||||||
|
|
||||||
|
# Authentication.
|
||||||
|
gem 'omniauth'
|
||||||
|
gem 'omniauth-twitter'
|
||||||
|
gem 'omniauth-google-oauth2'
|
||||||
|
gem 'omniauth_openid_connect'
|
||||||
|
gem 'omniauth-bn-launcher', '~> 0.1.3'
|
||||||
|
gem 'net-ldap'
|
||||||
|
gem 'bn-ldap-authentication', '~> 0.1.4'
|
||||||
|
gem 'omniauth-bn-office365', '~> 0.1.1'
|
||||||
|
|
||||||
|
# BigBlueButton API wrapper.
|
||||||
|
gem 'bigbluebutton-api-ruby', git: 'https://github.com/mconf/bigbluebutton-api-ruby.git', branch: 'master'
|
||||||
|
|
||||||
|
# Front-end.
|
||||||
|
gem 'bootstrap', '~> 4.3.1'
|
||||||
|
gem 'tabler-rubygem', git: 'https://github.com/blindsidenetworks/tabler-rubygem.git', tag: '0.1.4.1'
|
||||||
|
gem 'pagy'
|
||||||
|
gem 'font-awesome-sass', '~> 5.9.0'
|
||||||
|
|
||||||
|
# For detecting the users preferred language.
|
||||||
|
gem 'http_accept_language'
|
||||||
|
|
||||||
# Use Capistrano for deployment
|
# Use Capistrano for deployment
|
||||||
# gem 'capistrano-rails', group: :development
|
# gem 'capistrano-rails', group: :development
|
||||||
|
|
||||||
|
# Markdown parsing.
|
||||||
|
gem 'redcarpet'
|
||||||
|
|
||||||
|
# For limiting access based on user roles
|
||||||
|
gem 'cancancan', '~> 2.0'
|
||||||
|
|
||||||
|
# Active Storage gems
|
||||||
|
gem 'aws-sdk-s3', '~> 1.75'
|
||||||
|
gem 'google-cloud-storage', '~> 1.26'
|
||||||
|
|
||||||
|
gem 'pluck_to_hash', '~> 1.0.2'
|
||||||
|
|
||||||
|
group :production do
|
||||||
|
# Use a postgres database in production.
|
||||||
|
gem 'pg', '~> 0.18'
|
||||||
|
gem 'sequel'
|
||||||
|
|
||||||
|
# For a better logging library in production
|
||||||
|
gem "lograge"
|
||||||
|
|
||||||
|
# Use for the cache store in production
|
||||||
|
gem 'redis'
|
||||||
|
gem 'hiredis'
|
||||||
|
end
|
||||||
|
|
||||||
|
# Ruby linting.
|
||||||
|
gem 'rubocop'
|
||||||
|
|
||||||
group :development, :test do
|
group :development, :test do
|
||||||
# For environment configuration
|
|
||||||
gem 'dotenv-rails'
|
|
||||||
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
|
# Call 'byebug' anywhere in the code to stop execution and get a debugger console
|
||||||
gem 'byebug', platform: :mri
|
gem 'byebug', platform: :mri
|
||||||
|
# Environment configuration.
|
||||||
|
gem 'dotenv-rails'
|
||||||
|
# Use a sqlite database in test and development.
|
||||||
|
gem 'sqlite3', '~> 1.3.6'
|
||||||
|
end
|
||||||
|
|
||||||
|
group :test do
|
||||||
|
# Include Rspec and other testing utilities.
|
||||||
|
gem 'rspec-rails', '~> 3.7'
|
||||||
|
gem 'action-cable-testing'
|
||||||
|
gem 'rails-controller-testing'
|
||||||
|
gem 'shoulda-matchers', '~> 3.1'
|
||||||
|
gem 'faker'
|
||||||
|
gem "factory_bot_rails"
|
||||||
|
gem 'webmock'
|
||||||
end
|
end
|
||||||
|
|
||||||
group :development do
|
group :development do
|
||||||
# Access an IRB console on exception pages or by using <%= console %> anywhere in the code.
|
# Access an IRB console on exception pages or by using <%= console %> anywhere in the code.
|
||||||
gem 'web-console'
|
gem 'web-console', '>= 3.3.0'
|
||||||
gem 'listen', '~> 3.0.5'
|
gem 'listen', '~> 3.0.5'
|
||||||
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
|
# Spring speeds up development by keeping your application running in the background. Read more: https://github.com/rails/spring
|
||||||
gem 'spring'
|
gem 'spring'
|
||||||
gem 'spring-watcher-listen', '~> 2.0.0'
|
gem 'spring-watcher-listen', '~> 2.0.0'
|
||||||
end
|
end
|
||||||
|
|
||||||
group :test do
|
gem 'remote_syslog_logger'
|
||||||
gem 'mocha'
|
|
||||||
gem 'simplecov', :require => false
|
|
||||||
end
|
|
||||||
|
|
||||||
group :production do
|
|
||||||
# For more condensed logging
|
|
||||||
gem "lograge"
|
|
||||||
end
|
|
||||||
|
|
||||||
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
|
# Windows does not include zoneinfo files, so bundle the tzinfo-data gem
|
||||||
gem 'tzinfo-data', platforms: [:mingw, :mswin, :x64_mingw, :jruby]
|
gem 'tzinfo-data'
|
||||||
|
|
||||||
gem 'omniauth', '1.6.1'
|
gem 'coveralls', require: false
|
||||||
gem 'omniauth-twitter', '1.2.1'
|
|
||||||
gem 'omniauth-google-oauth2', '0.4.1'
|
|
||||||
gem 'omniauth-ldap'
|
|
||||||
|
|
||||||
gem 'bigbluebutton-api-ruby'
|
gem 'random_password'
|
||||||
|
|
||||||
gem 'bootstrap-sass', '3.3.0.0'
|
# Adds helpers for the Google reCAPTCHA API
|
||||||
gem 'bootstrap-social-rails', '~> 4.12'
|
gem "recaptcha"
|
||||||
gem 'font-awesome-sass', '4.7.0'
|
|
||||||
gem 'jquery-ui-rails'
|
|
||||||
gem 'jquery-datatables-rails', '~> 3.4.0'
|
|
||||||
gem 'rails-timeago', '~> 2.0'
|
|
||||||
|
|
||||||
gem 'http_accept_language'
|
gem 'i18n-language-mapping', '~> 0.1.1'
|
||||||
|
|
||||||
# For sending slack notifications.
|
|
||||||
gem 'slack-notifier'
|
|
||||||
|
|
||||||
# For landing background image uploading.
|
|
||||||
gem 'paperclip', '~> 5.1'
|
|
||||||
|
|
||||||
# For uploading recordings to Youtube.
|
|
||||||
gem 'yt', '~> 0.28.0'
|
|
||||||
|
|
||||||
# Simple HTTP client.
|
|
||||||
gem 'faraday'
|
|
||||||
|
|
||||||
|
|
||||||
# For SAML authentication
|
|
||||||
gem 'omniauth-saml'
|
|
||||||
|
|
||||||
# For device detection to determine BigBlueButton client.
|
|
||||||
gem 'browser'
|
|
||||||
|
|||||||
@@ -1,57 +1,110 @@
|
|||||||
|
GIT
|
||||||
|
remote: https://github.com/blindsidenetworks/tabler-rubygem.git
|
||||||
|
revision: 4c16e6dc930f6b92dec093abaf9652d768b46d97
|
||||||
|
tag: 0.1.4.1
|
||||||
|
specs:
|
||||||
|
tabler-rubygem (0.1.4.1)
|
||||||
|
autoprefixer-rails (>= 6.0.3)
|
||||||
|
|
||||||
|
GIT
|
||||||
|
remote: https://github.com/mconf/bigbluebutton-api-ruby.git
|
||||||
|
revision: 91dc495324a6b7e162773227ec3650f8a5b39c50
|
||||||
|
branch: master
|
||||||
|
specs:
|
||||||
|
bigbluebutton-api-ruby (1.7.0)
|
||||||
|
childprocess (>= 1.0.1)
|
||||||
|
ffi (>= 1.9.24)
|
||||||
|
json (>= 1.8.6)
|
||||||
|
nokogiri (>= 1.10.4)
|
||||||
|
rack (>= 1.6.11)
|
||||||
|
rubyzip (>= 1.3.0)
|
||||||
|
xml-simple (~> 1.1)
|
||||||
|
|
||||||
GEM
|
GEM
|
||||||
remote: https://rubygems.org/
|
remote: https://rubygems.org/
|
||||||
specs:
|
specs:
|
||||||
actioncable (5.0.7)
|
action-cable-testing (0.6.1)
|
||||||
actionpack (= 5.0.7)
|
actioncable (>= 5.0)
|
||||||
nio4r (>= 1.2, < 3.0)
|
actioncable (5.2.4.4)
|
||||||
websocket-driver (~> 0.6.1)
|
actionpack (= 5.2.4.4)
|
||||||
actionmailer (5.0.7)
|
nio4r (~> 2.0)
|
||||||
actionpack (= 5.0.7)
|
websocket-driver (>= 0.6.1)
|
||||||
actionview (= 5.0.7)
|
actionmailer (5.2.4.4)
|
||||||
activejob (= 5.0.7)
|
actionpack (= 5.2.4.4)
|
||||||
|
actionview (= 5.2.4.4)
|
||||||
|
activejob (= 5.2.4.4)
|
||||||
mail (~> 2.5, >= 2.5.4)
|
mail (~> 2.5, >= 2.5.4)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
actionpack (5.0.7)
|
actionpack (5.2.4.4)
|
||||||
actionview (= 5.0.7)
|
actionview (= 5.2.4.4)
|
||||||
activesupport (= 5.0.7)
|
activesupport (= 5.2.4.4)
|
||||||
rack (~> 2.0)
|
rack (~> 2.0, >= 2.0.8)
|
||||||
rack-test (~> 0.6.3)
|
rack-test (>= 0.6.3)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
rails-html-sanitizer (~> 1.0, >= 1.0.2)
|
||||||
actionview (5.0.7)
|
actionview (5.2.4.4)
|
||||||
activesupport (= 5.0.7)
|
activesupport (= 5.2.4.4)
|
||||||
builder (~> 3.1)
|
builder (~> 3.1)
|
||||||
erubis (~> 2.7.0)
|
erubi (~> 1.4)
|
||||||
rails-dom-testing (~> 2.0)
|
rails-dom-testing (~> 2.0)
|
||||||
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
rails-html-sanitizer (~> 1.0, >= 1.0.3)
|
||||||
activejob (5.0.7)
|
activejob (5.2.4.4)
|
||||||
activesupport (= 5.0.7)
|
activesupport (= 5.2.4.4)
|
||||||
globalid (>= 0.3.6)
|
globalid (>= 0.3.6)
|
||||||
activemodel (5.0.7)
|
activemodel (5.2.4.4)
|
||||||
activesupport (= 5.0.7)
|
activesupport (= 5.2.4.4)
|
||||||
activerecord (5.0.7)
|
activerecord (5.2.4.4)
|
||||||
activemodel (= 5.0.7)
|
activemodel (= 5.2.4.4)
|
||||||
activesupport (= 5.0.7)
|
activesupport (= 5.2.4.4)
|
||||||
arel (~> 7.0)
|
arel (>= 9.0)
|
||||||
activesupport (5.0.7)
|
activestorage (5.2.4.4)
|
||||||
|
actionpack (= 5.2.4.4)
|
||||||
|
activerecord (= 5.2.4.4)
|
||||||
|
marcel (~> 0.3.1)
|
||||||
|
activesupport (5.2.4.4)
|
||||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||||
i18n (>= 0.7, < 2)
|
i18n (>= 0.7, < 2)
|
||||||
minitest (~> 5.1)
|
minitest (~> 5.1)
|
||||||
tzinfo (~> 1.1)
|
tzinfo (~> 1.1)
|
||||||
arel (7.1.4)
|
addressable (2.7.0)
|
||||||
bigbluebutton-api-ruby (1.6.0)
|
public_suffix (>= 2.0.2, < 5.0)
|
||||||
xml-simple (~> 1.1)
|
aes_key_wrap (1.1.0)
|
||||||
bindex (0.5.0)
|
arel (9.0.0)
|
||||||
bootstrap-sass (3.3.0.0)
|
ast (2.4.0)
|
||||||
sass (~> 3.2)
|
attr_required (1.0.1)
|
||||||
bootstrap-social-rails (4.12.0)
|
autoprefixer-rails (9.7.6)
|
||||||
railties (>= 3.1)
|
execjs
|
||||||
browser (2.5.2)
|
aws-eventstream (1.1.0)
|
||||||
builder (3.2.3)
|
aws-partitions (1.343.0)
|
||||||
byebug (9.1.0)
|
aws-sdk-core (3.104.1)
|
||||||
climate_control (0.2.0)
|
aws-eventstream (~> 1, >= 1.0.2)
|
||||||
cocaine (0.5.8)
|
aws-partitions (~> 1, >= 1.239.0)
|
||||||
climate_control (>= 0.0.3, < 1.0)
|
aws-sigv4 (~> 1.1)
|
||||||
|
jmespath (~> 1.0)
|
||||||
|
aws-sdk-kms (1.36.0)
|
||||||
|
aws-sdk-core (~> 3, >= 3.99.0)
|
||||||
|
aws-sigv4 (~> 1.1)
|
||||||
|
aws-sdk-s3 (1.75.0)
|
||||||
|
aws-sdk-core (~> 3, >= 3.104.1)
|
||||||
|
aws-sdk-kms (~> 1)
|
||||||
|
aws-sigv4 (~> 1.1)
|
||||||
|
aws-sigv4 (1.2.1)
|
||||||
|
aws-eventstream (~> 1, >= 1.0.2)
|
||||||
|
bcrypt (3.1.13)
|
||||||
|
bindata (2.4.8)
|
||||||
|
bindex (0.8.1)
|
||||||
|
bn-ldap-authentication (0.1.4)
|
||||||
|
net-ldap (~> 0)
|
||||||
|
bootsnap (1.4.6)
|
||||||
|
msgpack (~> 1.0)
|
||||||
|
bootstrap (4.3.1)
|
||||||
|
autoprefixer-rails (>= 9.1.0)
|
||||||
|
popper_js (>= 1.14.3, < 2)
|
||||||
|
sassc-rails (>= 2.0.0)
|
||||||
|
builder (3.2.4)
|
||||||
|
byebug (11.1.3)
|
||||||
|
cancancan (2.3.0)
|
||||||
|
childprocess (4.0.0)
|
||||||
coffee-rails (4.2.2)
|
coffee-rails (4.2.2)
|
||||||
coffee-script (>= 2.2.0)
|
coffee-script (>= 2.2.0)
|
||||||
railties (>= 4.0.0)
|
railties (>= 4.0.0)
|
||||||
@@ -59,281 +112,404 @@ GEM
|
|||||||
coffee-script-source
|
coffee-script-source
|
||||||
execjs
|
execjs
|
||||||
coffee-script-source (1.12.2)
|
coffee-script-source (1.12.2)
|
||||||
concurrent-ruby (1.0.5)
|
concurrent-ruby (1.1.7)
|
||||||
concurrent-ruby (1.0.5-java)
|
coveralls (0.8.23)
|
||||||
crass (1.0.4)
|
json (>= 1.8, < 3)
|
||||||
docile (1.1.5)
|
simplecov (~> 0.16.1)
|
||||||
dotenv (2.2.1)
|
term-ansicolor (~> 1.3)
|
||||||
dotenv-rails (2.2.1)
|
thor (>= 0.19.4, < 2.0)
|
||||||
dotenv (= 2.2.1)
|
tins (~> 1.6)
|
||||||
railties (>= 3.2, < 5.2)
|
crack (0.4.3)
|
||||||
erubis (2.7.0)
|
safe_yaml (~> 1.0.0)
|
||||||
|
crass (1.0.6)
|
||||||
|
declarative (0.0.20)
|
||||||
|
declarative-option (0.1.0)
|
||||||
|
diff-lcs (1.3)
|
||||||
|
digest-crc (0.6.1)
|
||||||
|
rake (~> 13.0)
|
||||||
|
docile (1.3.2)
|
||||||
|
dotenv (2.7.5)
|
||||||
|
dotenv-rails (2.7.5)
|
||||||
|
dotenv (= 2.7.5)
|
||||||
|
railties (>= 3.2, < 6.1)
|
||||||
|
erubi (1.9.0)
|
||||||
execjs (2.7.0)
|
execjs (2.7.0)
|
||||||
faraday (0.12.2)
|
factory_bot (5.2.0)
|
||||||
|
activesupport (>= 4.2.0)
|
||||||
|
factory_bot_rails (5.2.0)
|
||||||
|
factory_bot (~> 5.2.0)
|
||||||
|
railties (>= 4.2.0)
|
||||||
|
faker (2.11.0)
|
||||||
|
i18n (>= 1.6, < 2)
|
||||||
|
faraday (1.0.1)
|
||||||
multipart-post (>= 1.2, < 3)
|
multipart-post (>= 1.2, < 3)
|
||||||
ffi (1.9.18)
|
ffi (1.12.2)
|
||||||
ffi (1.9.18-java)
|
font-awesome-sass (5.9.0)
|
||||||
ffi (1.9.18-x64-mingw32)
|
sassc (>= 1.11)
|
||||||
ffi (1.9.18-x86-mingw32)
|
globalid (0.4.2)
|
||||||
font-awesome-sass (4.7.0)
|
|
||||||
sass (>= 3.2)
|
|
||||||
globalid (0.4.1)
|
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
hashie (3.5.7)
|
google-api-client (0.42.1)
|
||||||
|
addressable (~> 2.5, >= 2.5.1)
|
||||||
|
googleauth (~> 0.9)
|
||||||
|
httpclient (>= 2.8.1, < 3.0)
|
||||||
|
mini_mime (~> 1.0)
|
||||||
|
representable (~> 3.0)
|
||||||
|
retriable (>= 2.0, < 4.0)
|
||||||
|
signet (~> 0.12)
|
||||||
|
google-cloud-core (1.5.0)
|
||||||
|
google-cloud-env (~> 1.0)
|
||||||
|
google-cloud-errors (~> 1.0)
|
||||||
|
google-cloud-env (1.3.3)
|
||||||
|
faraday (>= 0.17.3, < 2.0)
|
||||||
|
google-cloud-errors (1.0.1)
|
||||||
|
google-cloud-storage (1.26.2)
|
||||||
|
addressable (~> 2.5)
|
||||||
|
digest-crc (~> 0.4)
|
||||||
|
google-api-client (~> 0.33)
|
||||||
|
google-cloud-core (~> 1.2)
|
||||||
|
googleauth (~> 0.9)
|
||||||
|
mini_mime (~> 1.0)
|
||||||
|
googleauth (0.13.0)
|
||||||
|
faraday (>= 0.17.3, < 2.0)
|
||||||
|
jwt (>= 1.4, < 3.0)
|
||||||
|
memoist (~> 0.16)
|
||||||
|
multi_json (~> 1.11)
|
||||||
|
os (>= 0.9, < 2.0)
|
||||||
|
signet (~> 0.14)
|
||||||
|
hashdiff (1.0.1)
|
||||||
|
hashie (4.1.0)
|
||||||
|
hiredis (0.6.3)
|
||||||
http_accept_language (2.1.1)
|
http_accept_language (2.1.1)
|
||||||
i18n (1.0.1)
|
httpclient (2.8.3)
|
||||||
|
i18n (1.8.5)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
jbuilder (2.7.0)
|
i18n-language-mapping (0.1.2)
|
||||||
activesupport (>= 4.2.0)
|
jbuilder (2.10.0)
|
||||||
multi_json (>= 1.2)
|
activesupport (>= 5.0.0)
|
||||||
jquery-datatables-rails (3.4.0)
|
jmespath (1.4.0)
|
||||||
actionpack (>= 3.1)
|
jquery-rails (4.4.0)
|
||||||
jquery-rails
|
|
||||||
railties (>= 3.1)
|
|
||||||
sass-rails
|
|
||||||
jquery-rails (4.3.1)
|
|
||||||
rails-dom-testing (>= 1, < 3)
|
rails-dom-testing (>= 1, < 3)
|
||||||
railties (>= 4.2.0)
|
railties (>= 4.2.0)
|
||||||
thor (>= 0.14, < 2.0)
|
thor (>= 0.14, < 2.0)
|
||||||
jquery-ui-rails (6.0.1)
|
jquery-ui-rails (6.0.1)
|
||||||
railties (>= 3.2.16)
|
railties (>= 3.2.16)
|
||||||
json (1.8.6)
|
json (2.3.0)
|
||||||
json (1.8.6-java)
|
json-jwt (1.13.0)
|
||||||
jwt (1.5.6)
|
activesupport (>= 4.2)
|
||||||
libv8 (3.16.14.19)
|
aes_key_wrap
|
||||||
|
bindata
|
||||||
|
jwt (2.2.1)
|
||||||
listen (3.0.8)
|
listen (3.0.8)
|
||||||
rb-fsevent (~> 0.9, >= 0.9.4)
|
rb-fsevent (~> 0.9, >= 0.9.4)
|
||||||
rb-inotify (~> 0.9, >= 0.9.7)
|
rb-inotify (~> 0.9, >= 0.9.7)
|
||||||
lograge (0.9.0)
|
lograge (0.11.2)
|
||||||
actionpack (>= 4)
|
actionpack (>= 4)
|
||||||
activesupport (>= 4)
|
activesupport (>= 4)
|
||||||
railties (>= 4)
|
railties (>= 4)
|
||||||
request_store (~> 1.0)
|
request_store (~> 1.0)
|
||||||
loofah (2.2.2)
|
loofah (2.7.0)
|
||||||
crass (~> 1.0.2)
|
crass (~> 1.0.2)
|
||||||
nokogiri (>= 1.5.9)
|
nokogiri (>= 1.5.9)
|
||||||
mail (2.7.0)
|
mail (2.7.1)
|
||||||
mini_mime (>= 0.1.1)
|
mini_mime (>= 0.1.1)
|
||||||
metaclass (0.0.4)
|
marcel (0.3.3)
|
||||||
method_source (0.9.0)
|
mimemagic (~> 0.3.2)
|
||||||
mime-types (3.1)
|
memoist (0.16.2)
|
||||||
mime-types-data (~> 3.2015)
|
method_source (1.0.0)
|
||||||
mime-types-data (3.2016.0521)
|
mimemagic (0.3.5)
|
||||||
mimemagic (0.3.2)
|
mini_mime (1.0.2)
|
||||||
mini_mime (1.0.0)
|
mini_portile2 (2.5.0)
|
||||||
mini_portile2 (2.3.0)
|
minitest (5.14.2)
|
||||||
minitest (5.11.3)
|
msgpack (1.3.3)
|
||||||
mocha (1.3.0)
|
multi_json (1.14.1)
|
||||||
metaclass (~> 0.0.1)
|
|
||||||
multi_json (1.13.1)
|
|
||||||
multi_xml (0.6.0)
|
multi_xml (0.6.0)
|
||||||
multipart-post (2.0.0)
|
multipart-post (2.1.1)
|
||||||
net-ldap (0.16.1)
|
net-ldap (0.16.2)
|
||||||
nio4r (2.3.1)
|
nio4r (2.5.4)
|
||||||
nio4r (2.3.1-java)
|
nokogiri (1.11.1)
|
||||||
nokogiri (1.8.2)
|
mini_portile2 (~> 2.5.0)
|
||||||
mini_portile2 (~> 2.3.0)
|
racc (~> 1.4)
|
||||||
nokogiri (1.8.2-java)
|
|
||||||
nokogiri (1.8.2-x64-mingw32)
|
|
||||||
mini_portile2 (~> 2.3.0)
|
|
||||||
nokogiri (1.8.2-x86-mingw32)
|
|
||||||
mini_portile2 (~> 2.3.0)
|
|
||||||
oauth (0.5.4)
|
oauth (0.5.4)
|
||||||
oauth2 (1.4.0)
|
oauth2 (1.4.4)
|
||||||
faraday (>= 0.8, < 0.13)
|
faraday (>= 0.8, < 2.0)
|
||||||
jwt (~> 1.0)
|
jwt (>= 1.0, < 3.0)
|
||||||
multi_json (~> 1.3)
|
multi_json (~> 1.3)
|
||||||
multi_xml (~> 0.5)
|
multi_xml (~> 0.5)
|
||||||
rack (>= 1.2, < 3)
|
rack (>= 1.2, < 3)
|
||||||
omniauth (1.6.1)
|
omniauth (1.9.1)
|
||||||
hashie (>= 3.4.6, < 3.6.0)
|
hashie (>= 3.4.6)
|
||||||
rack (>= 1.6.2, < 3)
|
rack (>= 1.6.2, < 3)
|
||||||
omniauth-google-oauth2 (0.4.1)
|
omniauth-bn-launcher (0.1.3)
|
||||||
jwt (~> 1.5.2)
|
omniauth (~> 1.3, >= 1.3.2)
|
||||||
multi_json (~> 1.3)
|
omniauth-oauth2 (= 1.5.0)
|
||||||
|
omniauth-bn-office365 (0.1.1)
|
||||||
|
omniauth (>= 1.3.2)
|
||||||
|
omniauth-oauth2 (>= 1.5.0)
|
||||||
|
omniauth-google-oauth2 (0.7.0)
|
||||||
|
jwt (>= 2.0)
|
||||||
omniauth (>= 1.1.1)
|
omniauth (>= 1.1.1)
|
||||||
omniauth-oauth2 (>= 1.3.1)
|
omniauth-oauth2 (>= 1.5)
|
||||||
omniauth-ldap (1.0.5)
|
|
||||||
net-ldap (~> 0.12)
|
|
||||||
omniauth (~> 1.0)
|
|
||||||
pyu-ruby-sasl (~> 0.0.3.2)
|
|
||||||
rubyntlm (~> 0.3.4)
|
|
||||||
omniauth-oauth (1.1.0)
|
omniauth-oauth (1.1.0)
|
||||||
oauth
|
oauth
|
||||||
omniauth (~> 1.0)
|
omniauth (~> 1.0)
|
||||||
omniauth-oauth2 (1.5.0)
|
omniauth-oauth2 (1.5.0)
|
||||||
oauth2 (~> 1.1)
|
oauth2 (~> 1.1)
|
||||||
omniauth (~> 1.2)
|
omniauth (~> 1.2)
|
||||||
omniauth-saml (1.8.1)
|
omniauth-twitter (1.4.0)
|
||||||
omniauth (~> 1.3)
|
|
||||||
ruby-saml (~> 1.4, >= 1.4.3)
|
|
||||||
omniauth-twitter (1.2.1)
|
|
||||||
json (~> 1.3)
|
|
||||||
omniauth-oauth (~> 1.1)
|
omniauth-oauth (~> 1.1)
|
||||||
paperclip (5.2.1)
|
rack
|
||||||
activemodel (>= 4.2.0)
|
omniauth_openid_connect (0.3.5)
|
||||||
activesupport (>= 4.2.0)
|
addressable (~> 2.5)
|
||||||
cocaine (~> 0.5.5)
|
omniauth (~> 1.9)
|
||||||
mime-types
|
openid_connect (~> 1.1)
|
||||||
mimemagic (~> 0.3.0)
|
openid_connect (1.2.0)
|
||||||
pg (1.0.0)
|
activemodel
|
||||||
pg (1.0.0-x64-mingw32)
|
attr_required (>= 1.0.0)
|
||||||
pg (1.0.0-x86-mingw32)
|
json-jwt (>= 1.5.0)
|
||||||
puma (3.11.2)
|
rack-oauth2 (>= 1.6.1)
|
||||||
puma (3.11.2-java)
|
swd (>= 1.0.0)
|
||||||
pyu-ruby-sasl (0.0.3.3)
|
tzinfo
|
||||||
rack (2.0.5)
|
validate_email
|
||||||
rack-test (0.6.3)
|
validate_url
|
||||||
rack (>= 1.0)
|
webfinger (>= 1.0.1)
|
||||||
rails (5.0.7)
|
os (1.1.0)
|
||||||
actioncable (= 5.0.7)
|
pagy (3.8.1)
|
||||||
actionmailer (= 5.0.7)
|
parallel (1.19.1)
|
||||||
actionpack (= 5.0.7)
|
parser (2.7.1.3)
|
||||||
actionview (= 5.0.7)
|
ast (~> 2.4.0)
|
||||||
activejob (= 5.0.7)
|
pg (0.21.0)
|
||||||
activemodel (= 5.0.7)
|
pluck_to_hash (1.0.2)
|
||||||
activerecord (= 5.0.7)
|
activerecord (>= 4.0.2)
|
||||||
activesupport (= 5.0.7)
|
activesupport (>= 4.0.2)
|
||||||
|
popper_js (1.16.0)
|
||||||
|
public_suffix (4.0.5)
|
||||||
|
puma (3.12.6)
|
||||||
|
racc (1.5.2)
|
||||||
|
rack (2.2.3)
|
||||||
|
rack-oauth2 (1.16.0)
|
||||||
|
activesupport
|
||||||
|
attr_required
|
||||||
|
httpclient
|
||||||
|
json-jwt (>= 1.11.0)
|
||||||
|
rack (>= 2.1.0)
|
||||||
|
rack-test (1.1.0)
|
||||||
|
rack (>= 1.0, < 3)
|
||||||
|
rails (5.2.4.4)
|
||||||
|
actioncable (= 5.2.4.4)
|
||||||
|
actionmailer (= 5.2.4.4)
|
||||||
|
actionpack (= 5.2.4.4)
|
||||||
|
actionview (= 5.2.4.4)
|
||||||
|
activejob (= 5.2.4.4)
|
||||||
|
activemodel (= 5.2.4.4)
|
||||||
|
activerecord (= 5.2.4.4)
|
||||||
|
activestorage (= 5.2.4.4)
|
||||||
|
activesupport (= 5.2.4.4)
|
||||||
bundler (>= 1.3.0)
|
bundler (>= 1.3.0)
|
||||||
railties (= 5.0.7)
|
railties (= 5.2.4.4)
|
||||||
sprockets-rails (>= 2.0.0)
|
sprockets-rails (>= 2.0.0)
|
||||||
|
rails-controller-testing (1.0.4)
|
||||||
|
actionpack (>= 5.0.1.x)
|
||||||
|
actionview (>= 5.0.1.x)
|
||||||
|
activesupport (>= 5.0.1.x)
|
||||||
rails-dom-testing (2.0.3)
|
rails-dom-testing (2.0.3)
|
||||||
activesupport (>= 4.2.0)
|
activesupport (>= 4.2.0)
|
||||||
nokogiri (>= 1.6)
|
nokogiri (>= 1.6)
|
||||||
rails-html-sanitizer (1.0.4)
|
rails-html-sanitizer (1.3.0)
|
||||||
loofah (~> 2.2, >= 2.2.2)
|
loofah (~> 2.3)
|
||||||
rails-timeago (2.16.0)
|
railties (5.2.4.4)
|
||||||
actionpack (>= 3.1)
|
actionpack (= 5.2.4.4)
|
||||||
activesupport (>= 3.1)
|
activesupport (= 5.2.4.4)
|
||||||
railties (5.0.7)
|
|
||||||
actionpack (= 5.0.7)
|
|
||||||
activesupport (= 5.0.7)
|
|
||||||
method_source
|
method_source
|
||||||
rake (>= 0.8.7)
|
rake (>= 0.8.7)
|
||||||
thor (>= 0.18.1, < 2.0)
|
thor (>= 0.19.0, < 2.0)
|
||||||
rake (12.3.1)
|
rainbow (3.0.0)
|
||||||
rb-fsevent (0.10.2)
|
rake (13.0.1)
|
||||||
rb-inotify (0.9.10)
|
random_password (0.1.1)
|
||||||
ffi (>= 0.5.0, < 2)
|
rb-fsevent (0.10.4)
|
||||||
ref (2.0.0)
|
rb-inotify (0.10.1)
|
||||||
request_store (1.4.0)
|
ffi (~> 1.0)
|
||||||
|
recaptcha (5.5.0)
|
||||||
|
json
|
||||||
|
redcarpet (3.5.0)
|
||||||
|
redis (4.1.4)
|
||||||
|
remote_syslog_logger (1.0.4)
|
||||||
|
syslog_protocol
|
||||||
|
representable (3.0.4)
|
||||||
|
declarative (< 0.1.0)
|
||||||
|
declarative-option (< 0.2.0)
|
||||||
|
uber (< 0.2.0)
|
||||||
|
request_store (1.5.0)
|
||||||
rack (>= 1.4)
|
rack (>= 1.4)
|
||||||
ruby-saml (1.4.3)
|
retriable (3.1.2)
|
||||||
nokogiri (>= 1.5.10)
|
rexml (3.2.4)
|
||||||
rubyntlm (0.3.4)
|
rspec-core (3.9.2)
|
||||||
sass (3.5.5)
|
rspec-support (~> 3.9.3)
|
||||||
sass-listen (~> 4.0.0)
|
rspec-expectations (3.9.2)
|
||||||
sass-listen (4.0.0)
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
rb-fsevent (~> 0.9, >= 0.9.4)
|
rspec-support (~> 3.9.0)
|
||||||
rb-inotify (~> 0.9, >= 0.9.7)
|
rspec-mocks (3.9.1)
|
||||||
sass-rails (5.0.7)
|
diff-lcs (>= 1.2.0, < 2.0)
|
||||||
railties (>= 4.0.0, < 6)
|
rspec-support (~> 3.9.0)
|
||||||
sass (~> 3.1)
|
rspec-rails (3.9.1)
|
||||||
sprockets (>= 2.8, < 4.0)
|
actionpack (>= 3.0)
|
||||||
sprockets-rails (>= 2.0, < 4.0)
|
activesupport (>= 3.0)
|
||||||
tilt (>= 1.1, < 3)
|
railties (>= 3.0)
|
||||||
simplecov (0.15.1)
|
rspec-core (~> 3.9.0)
|
||||||
docile (~> 1.1.0)
|
rspec-expectations (~> 3.9.0)
|
||||||
|
rspec-mocks (~> 3.9.0)
|
||||||
|
rspec-support (~> 3.9.0)
|
||||||
|
rspec-support (3.9.3)
|
||||||
|
rubocop (0.84.0)
|
||||||
|
parallel (~> 1.10)
|
||||||
|
parser (>= 2.7.0.1)
|
||||||
|
rainbow (>= 2.2.2, < 4.0)
|
||||||
|
rexml
|
||||||
|
rubocop-ast (>= 0.0.3)
|
||||||
|
ruby-progressbar (~> 1.7)
|
||||||
|
unicode-display_width (>= 1.4.0, < 2.0)
|
||||||
|
rubocop-ast (0.0.3)
|
||||||
|
parser (>= 2.7.0.1)
|
||||||
|
ruby-progressbar (1.10.1)
|
||||||
|
rubyzip (2.3.0)
|
||||||
|
safe_yaml (1.0.5)
|
||||||
|
sassc (2.3.0)
|
||||||
|
ffi (~> 1.9)
|
||||||
|
sassc-rails (2.1.2)
|
||||||
|
railties (>= 4.0.0)
|
||||||
|
sassc (>= 2.0)
|
||||||
|
sprockets (> 3.0)
|
||||||
|
sprockets-rails
|
||||||
|
tilt
|
||||||
|
sequel (5.32.0)
|
||||||
|
shoulda-matchers (3.1.3)
|
||||||
|
activesupport (>= 4.0.0)
|
||||||
|
signet (0.14.0)
|
||||||
|
addressable (~> 2.3)
|
||||||
|
faraday (>= 0.17.3, < 2.0)
|
||||||
|
jwt (>= 1.5, < 3.0)
|
||||||
|
multi_json (~> 1.10)
|
||||||
|
simplecov (0.16.1)
|
||||||
|
docile (~> 1.1)
|
||||||
json (>= 1.8, < 3)
|
json (>= 1.8, < 3)
|
||||||
simplecov-html (~> 0.10.0)
|
simplecov-html (~> 0.10.0)
|
||||||
simplecov-html (0.10.2)
|
simplecov-html (0.10.2)
|
||||||
slack-notifier (2.3.2)
|
spring (2.1.0)
|
||||||
spring (2.0.2)
|
|
||||||
activesupport (>= 4.2)
|
|
||||||
spring-watcher-listen (2.0.1)
|
spring-watcher-listen (2.0.1)
|
||||||
listen (>= 2.7, < 4.0)
|
listen (>= 2.7, < 4.0)
|
||||||
spring (>= 1.2, < 3.0)
|
spring (>= 1.2, < 3.0)
|
||||||
sprockets (3.7.1)
|
sprockets (3.7.2)
|
||||||
concurrent-ruby (~> 1.0)
|
concurrent-ruby (~> 1.0)
|
||||||
rack (> 1, < 3)
|
rack (> 1, < 3)
|
||||||
sprockets-rails (3.2.1)
|
sprockets-rails (3.2.2)
|
||||||
actionpack (>= 4.0)
|
actionpack (>= 4.0)
|
||||||
activesupport (>= 4.0)
|
activesupport (>= 4.0)
|
||||||
sprockets (>= 3.0.0)
|
sprockets (>= 3.0.0)
|
||||||
sqlite3 (1.3.13)
|
sqlite3 (1.3.13)
|
||||||
sqlite3 (1.3.13-x64-mingw32)
|
swd (1.2.0)
|
||||||
sqlite3 (1.3.13-x86-mingw32)
|
activesupport (>= 3)
|
||||||
sqlite3 (1.3.13-x86-mswin32-60)
|
attr_required (>= 0.0.5)
|
||||||
therubyracer (0.12.3)
|
httpclient (>= 2.4)
|
||||||
libv8 (~> 3.16.14.15)
|
sync (0.5.0)
|
||||||
ref
|
syslog_protocol (0.9.2)
|
||||||
thor (0.20.0)
|
term-ansicolor (1.7.1)
|
||||||
|
tins (~> 1.0)
|
||||||
|
thor (1.0.1)
|
||||||
thread_safe (0.3.6)
|
thread_safe (0.3.6)
|
||||||
thread_safe (0.3.6-java)
|
tilt (2.0.10)
|
||||||
tilt (2.0.8)
|
tins (1.25.0)
|
||||||
turbolinks (5.1.0)
|
sync
|
||||||
turbolinks-source (~> 5.1)
|
turbolinks (5.2.1)
|
||||||
turbolinks-source (5.1.0)
|
turbolinks-source (~> 5.2)
|
||||||
tzinfo (1.2.5)
|
turbolinks-source (5.2.0)
|
||||||
|
tzinfo (1.2.7)
|
||||||
thread_safe (~> 0.1)
|
thread_safe (~> 0.1)
|
||||||
tzinfo-data (1.2017.3)
|
tzinfo-data (1.2020.1)
|
||||||
tzinfo (>= 1.0.0)
|
tzinfo (>= 1.0.0)
|
||||||
uglifier (4.1.3)
|
uber (0.1.0)
|
||||||
|
uglifier (4.2.0)
|
||||||
execjs (>= 0.3.0, < 3)
|
execjs (>= 0.3.0, < 3)
|
||||||
web-console (3.5.1)
|
unicode-display_width (1.7.0)
|
||||||
|
validate_email (0.1.6)
|
||||||
|
activemodel (>= 3.0)
|
||||||
|
mail (>= 2.2.5)
|
||||||
|
validate_url (1.0.13)
|
||||||
|
activemodel (>= 3.0.0)
|
||||||
|
public_suffix
|
||||||
|
web-console (3.7.0)
|
||||||
actionview (>= 5.0)
|
actionview (>= 5.0)
|
||||||
activemodel (>= 5.0)
|
activemodel (>= 5.0)
|
||||||
bindex (>= 0.4.0)
|
bindex (>= 0.4.0)
|
||||||
railties (>= 5.0)
|
railties (>= 5.0)
|
||||||
websocket-driver (0.6.5)
|
webfinger (1.1.0)
|
||||||
websocket-extensions (>= 0.1.0)
|
|
||||||
websocket-driver (0.6.5-java)
|
|
||||||
websocket-extensions (>= 0.1.0)
|
|
||||||
websocket-extensions (0.1.3)
|
|
||||||
xml-simple (1.1.5)
|
|
||||||
yt (0.28.5)
|
|
||||||
activesupport
|
activesupport
|
||||||
|
httpclient (>= 2.4)
|
||||||
|
webmock (3.8.3)
|
||||||
|
addressable (>= 2.3.6)
|
||||||
|
crack (>= 0.3.2)
|
||||||
|
hashdiff (>= 0.4.0, < 2.0.0)
|
||||||
|
websocket-driver (0.7.3)
|
||||||
|
websocket-extensions (>= 0.1.0)
|
||||||
|
websocket-extensions (0.1.5)
|
||||||
|
xml-simple (1.1.5)
|
||||||
|
|
||||||
PLATFORMS
|
PLATFORMS
|
||||||
java
|
|
||||||
ruby
|
ruby
|
||||||
x64-mingw32
|
|
||||||
x86-mingw32
|
|
||||||
x86-mswin32
|
|
||||||
|
|
||||||
DEPENDENCIES
|
DEPENDENCIES
|
||||||
bigbluebutton-api-ruby
|
action-cable-testing
|
||||||
bootstrap-sass (= 3.3.0.0)
|
aws-sdk-s3 (~> 1.75)
|
||||||
bootstrap-social-rails (~> 4.12)
|
bcrypt (~> 3.1.7)
|
||||||
browser
|
bigbluebutton-api-ruby!
|
||||||
|
bn-ldap-authentication (~> 0.1.4)
|
||||||
|
bootsnap (>= 1.1.0)
|
||||||
|
bootstrap (~> 4.3.1)
|
||||||
byebug
|
byebug
|
||||||
|
cancancan (~> 2.0)
|
||||||
coffee-rails (~> 4.2)
|
coffee-rails (~> 4.2)
|
||||||
|
coveralls
|
||||||
dotenv-rails
|
dotenv-rails
|
||||||
faraday
|
factory_bot_rails
|
||||||
font-awesome-sass (= 4.7.0)
|
faker
|
||||||
|
font-awesome-sass (~> 5.9.0)
|
||||||
|
google-cloud-storage (~> 1.26)
|
||||||
|
hiredis
|
||||||
http_accept_language
|
http_accept_language
|
||||||
|
i18n-language-mapping (~> 0.1.1)
|
||||||
jbuilder (~> 2.5)
|
jbuilder (~> 2.5)
|
||||||
jquery-datatables-rails (~> 3.4.0)
|
jquery-rails (~> 4.4)
|
||||||
jquery-rails
|
|
||||||
jquery-ui-rails
|
jquery-ui-rails
|
||||||
listen (~> 3.0.5)
|
listen (~> 3.0.5)
|
||||||
lograge
|
lograge
|
||||||
mocha
|
net-ldap
|
||||||
omniauth (= 1.6.1)
|
omniauth
|
||||||
omniauth-google-oauth2 (= 0.4.1)
|
omniauth-bn-launcher (~> 0.1.3)
|
||||||
omniauth-ldap
|
omniauth-bn-office365 (~> 0.1.1)
|
||||||
omniauth-saml
|
omniauth-google-oauth2
|
||||||
omniauth-twitter (= 1.2.1)
|
omniauth-twitter
|
||||||
paperclip (~> 5.1)
|
omniauth_openid_connect
|
||||||
pg
|
pagy
|
||||||
puma (~> 3.0)
|
pg (~> 0.18)
|
||||||
rails (~> 5.0.0, >= 5.0.0.1)
|
pluck_to_hash (~> 1.0.2)
|
||||||
rails-timeago (~> 2.0)
|
puma (~> 3.12)
|
||||||
sass-rails (~> 5.0)
|
rails (~> 5.2.4.4)
|
||||||
simplecov
|
rails-controller-testing
|
||||||
slack-notifier
|
random_password
|
||||||
|
recaptcha
|
||||||
|
redcarpet
|
||||||
|
redis
|
||||||
|
remote_syslog_logger
|
||||||
|
rspec-rails (~> 3.7)
|
||||||
|
rubocop
|
||||||
|
sassc-rails
|
||||||
|
sequel
|
||||||
|
shoulda-matchers (~> 3.1)
|
||||||
spring
|
spring
|
||||||
spring-watcher-listen (~> 2.0.0)
|
spring-watcher-listen (~> 2.0.0)
|
||||||
sqlite3
|
sprockets (< 4.0.0)
|
||||||
therubyracer
|
sqlite3 (~> 1.3.6)
|
||||||
|
tabler-rubygem!
|
||||||
turbolinks (~> 5)
|
turbolinks (~> 5)
|
||||||
tzinfo-data
|
tzinfo-data
|
||||||
uglifier (>= 1.3.0)
|
uglifier (>= 1.3.0)
|
||||||
web-console
|
web-console (>= 3.3.0)
|
||||||
yt (~> 0.28.0)
|
webmock
|
||||||
|
|
||||||
RUBY VERSION
|
|
||||||
ruby 2.4.0p0
|
|
||||||
|
|
||||||
BUNDLED WITH
|
|
||||||
1.16.1
|
|
||||||
|
|||||||
@@ -0,0 +1,165 @@
|
|||||||
|
GNU LESSER GENERAL PUBLIC LICENSE
|
||||||
|
Version 3, 29 June 2007
|
||||||
|
|
||||||
|
Copyright (C) 2007 Free Software Foundation, Inc. <https://fsf.org/>
|
||||||
|
Everyone is permitted to copy and distribute verbatim copies
|
||||||
|
of this license document, but changing it is not allowed.
|
||||||
|
|
||||||
|
|
||||||
|
This version of the GNU Lesser General Public License incorporates
|
||||||
|
the terms and conditions of version 3 of the GNU General Public
|
||||||
|
License, supplemented by the additional permissions listed below.
|
||||||
|
|
||||||
|
0. Additional Definitions.
|
||||||
|
|
||||||
|
As used herein, "this License" refers to version 3 of the GNU Lesser
|
||||||
|
General Public License, and the "GNU GPL" refers to version 3 of the GNU
|
||||||
|
General Public License.
|
||||||
|
|
||||||
|
"The Library" refers to a covered work governed by this License,
|
||||||
|
other than an Application or a Combined Work as defined below.
|
||||||
|
|
||||||
|
An "Application" is any work that makes use of an interface provided
|
||||||
|
by the Library, but which is not otherwise based on the Library.
|
||||||
|
Defining a subclass of a class defined by the Library is deemed a mode
|
||||||
|
of using an interface provided by the Library.
|
||||||
|
|
||||||
|
A "Combined Work" is a work produced by combining or linking an
|
||||||
|
Application with the Library. The particular version of the Library
|
||||||
|
with which the Combined Work was made is also called the "Linked
|
||||||
|
Version".
|
||||||
|
|
||||||
|
The "Minimal Corresponding Source" for a Combined Work means the
|
||||||
|
Corresponding Source for the Combined Work, excluding any source code
|
||||||
|
for portions of the Combined Work that, considered in isolation, are
|
||||||
|
based on the Application, and not on the Linked Version.
|
||||||
|
|
||||||
|
The "Corresponding Application Code" for a Combined Work means the
|
||||||
|
object code and/or source code for the Application, including any data
|
||||||
|
and utility programs needed for reproducing the Combined Work from the
|
||||||
|
Application, but excluding the System Libraries of the Combined Work.
|
||||||
|
|
||||||
|
1. Exception to Section 3 of the GNU GPL.
|
||||||
|
|
||||||
|
You may convey a covered work under sections 3 and 4 of this License
|
||||||
|
without being bound by section 3 of the GNU GPL.
|
||||||
|
|
||||||
|
2. Conveying Modified Versions.
|
||||||
|
|
||||||
|
If you modify a copy of the Library, and, in your modifications, a
|
||||||
|
facility refers to a function or data to be supplied by an Application
|
||||||
|
that uses the facility (other than as an argument passed when the
|
||||||
|
facility is invoked), then you may convey a copy of the modified
|
||||||
|
version:
|
||||||
|
|
||||||
|
a) under this License, provided that you make a good faith effort to
|
||||||
|
ensure that, in the event an Application does not supply the
|
||||||
|
function or data, the facility still operates, and performs
|
||||||
|
whatever part of its purpose remains meaningful, or
|
||||||
|
|
||||||
|
b) under the GNU GPL, with none of the additional permissions of
|
||||||
|
this License applicable to that copy.
|
||||||
|
|
||||||
|
3. Object Code Incorporating Material from Library Header Files.
|
||||||
|
|
||||||
|
The object code form of an Application may incorporate material from
|
||||||
|
a header file that is part of the Library. You may convey such object
|
||||||
|
code under terms of your choice, provided that, if the incorporated
|
||||||
|
material is not limited to numerical parameters, data structure
|
||||||
|
layouts and accessors, or small macros, inline functions and templates
|
||||||
|
(ten or fewer lines in length), you do both of the following:
|
||||||
|
|
||||||
|
a) Give prominent notice with each copy of the object code that the
|
||||||
|
Library is used in it and that the Library and its use are
|
||||||
|
covered by this License.
|
||||||
|
|
||||||
|
b) Accompany the object code with a copy of the GNU GPL and this license
|
||||||
|
document.
|
||||||
|
|
||||||
|
4. Combined Works.
|
||||||
|
|
||||||
|
You may convey a Combined Work under terms of your choice that,
|
||||||
|
taken together, effectively do not restrict modification of the
|
||||||
|
portions of the Library contained in the Combined Work and reverse
|
||||||
|
engineering for debugging such modifications, if you also do each of
|
||||||
|
the following:
|
||||||
|
|
||||||
|
a) Give prominent notice with each copy of the Combined Work that
|
||||||
|
the Library is used in it and that the Library and its use are
|
||||||
|
covered by this License.
|
||||||
|
|
||||||
|
b) Accompany the Combined Work with a copy of the GNU GPL and this license
|
||||||
|
document.
|
||||||
|
|
||||||
|
c) For a Combined Work that displays copyright notices during
|
||||||
|
execution, include the copyright notice for the Library among
|
||||||
|
these notices, as well as a reference directing the user to the
|
||||||
|
copies of the GNU GPL and this license document.
|
||||||
|
|
||||||
|
d) Do one of the following:
|
||||||
|
|
||||||
|
0) Convey the Minimal Corresponding Source under the terms of this
|
||||||
|
License, and the Corresponding Application Code in a form
|
||||||
|
suitable for, and under terms that permit, the user to
|
||||||
|
recombine or relink the Application with a modified version of
|
||||||
|
the Linked Version to produce a modified Combined Work, in the
|
||||||
|
manner specified by section 6 of the GNU GPL for conveying
|
||||||
|
Corresponding Source.
|
||||||
|
|
||||||
|
1) Use a suitable shared library mechanism for linking with the
|
||||||
|
Library. A suitable mechanism is one that (a) uses at run time
|
||||||
|
a copy of the Library already present on the user's computer
|
||||||
|
system, and (b) will operate properly with a modified version
|
||||||
|
of the Library that is interface-compatible with the Linked
|
||||||
|
Version.
|
||||||
|
|
||||||
|
e) Provide Installation Information, but only if you would otherwise
|
||||||
|
be required to provide such information under section 6 of the
|
||||||
|
GNU GPL, and only to the extent that such information is
|
||||||
|
necessary to install and execute a modified version of the
|
||||||
|
Combined Work produced by recombining or relinking the
|
||||||
|
Application with a modified version of the Linked Version. (If
|
||||||
|
you use option 4d0, the Installation Information must accompany
|
||||||
|
the Minimal Corresponding Source and Corresponding Application
|
||||||
|
Code. If you use option 4d1, you must provide the Installation
|
||||||
|
Information in the manner specified by section 6 of the GNU GPL
|
||||||
|
for conveying Corresponding Source.)
|
||||||
|
|
||||||
|
5. Combined Libraries.
|
||||||
|
|
||||||
|
You may place library facilities that are a work based on the
|
||||||
|
Library side by side in a single library together with other library
|
||||||
|
facilities that are not Applications and are not covered by this
|
||||||
|
License, and convey such a combined library under terms of your
|
||||||
|
choice, if you do both of the following:
|
||||||
|
|
||||||
|
a) Accompany the combined library with a copy of the same work based
|
||||||
|
on the Library, uncombined with any other library facilities,
|
||||||
|
conveyed under the terms of this License.
|
||||||
|
|
||||||
|
b) Give prominent notice with the combined library that part of it
|
||||||
|
is a work based on the Library, and explaining where to find the
|
||||||
|
accompanying uncombined form of the same work.
|
||||||
|
|
||||||
|
6. Revised Versions of the GNU Lesser General Public License.
|
||||||
|
|
||||||
|
The Free Software Foundation may publish revised and/or new versions
|
||||||
|
of the GNU Lesser General Public License from time to time. Such new
|
||||||
|
versions will be similar in spirit to the present version, but may
|
||||||
|
differ in detail to address new problems or concerns.
|
||||||
|
|
||||||
|
Each version is given a distinguishing version number. If the
|
||||||
|
Library as you received it specifies that a certain numbered version
|
||||||
|
of the GNU Lesser General Public License "or any later version"
|
||||||
|
applies to it, you have the option of following the terms and
|
||||||
|
conditions either of that published version or of any later version
|
||||||
|
published by the Free Software Foundation. If the Library as you
|
||||||
|
received it does not specify a version number of the GNU Lesser
|
||||||
|
General Public License, you may choose any version of the GNU Lesser
|
||||||
|
General Public License ever published by the Free Software Foundation.
|
||||||
|
|
||||||
|
If the Library as you received it specifies that a proxy can decide
|
||||||
|
whether future versions of the GNU Lesser General Public License shall
|
||||||
|
apply, that proxy's public statement of acceptance of any version is
|
||||||
|
permanent authorization for you to choose that version for the
|
||||||
|
Library.
|
||||||
@@ -1,31 +1,36 @@
|
|||||||
# Greenlight
|
# Greenlight
|
||||||
|
|
||||||
GreenLight is a simple (but powerful) front-end interface for your BigBlueButton server. At its core, GreenLight provides a minimalistic web-based application that lets users
|

|
||||||
|

|
||||||
|

|
||||||
|
|
||||||
* Create a meeting
|
Greenlight is a simple front-end interface for your BigBlueButton server. At its heart, Greenlight provides a minimalistic web-based application that allows users to:
|
||||||
* Invite others to the meeting
|
|
||||||
* Join a meeting
|
|
||||||
|
|
||||||
Furthermore, if you configure GreenLight to use either Google, Twitter (both via OAuth2) or LDAP for authentication, users can login to record meetings and manage recordings.
|
* Signup/Login with Google, Office365, OpenID Connect, or through the application itself.
|
||||||
|
* Manage your account settings and user preferences.
|
||||||
|
* Create and manage your own personal rooms ([BigBlueButton](https://github.com/bigbluebutton/bigbluebutton) sessions).
|
||||||
|
* Invite others to your room using a simple URL.
|
||||||
|
* View recordings and share them with others.
|
||||||
|
|
||||||
## Overview video
|
Interested? Try Greenlight out on our [demo server](https://demo.bigbluebutton.org/gl)!
|
||||||
|
|
||||||
For a overview of how GreenLight works, see the following video
|
Greenlight is also completely configurable. This means you can turn on/off features to make Greenlight fit your specific use case. For more information on Greenlight and its features, see our [documentation](http://docs.bigbluebutton.org/greenlight/gl-install.html).
|
||||||
|
|
||||||
[](https://youtu.be/yGX3JCv7OVM)
|
For a overview of how Greenlight works, checkout our Introduction to Greenlight Video:
|
||||||
|
|
||||||
|
[](https://youtu.be/Hso8yLzkqj8)
|
||||||
|
|
||||||
## Installation on the BigBlueButton server
|
## Installation on a BigBlueButton Server
|
||||||
We designed GreenLight to install on a [BigBlueButton 1.1-beta](http://docs.bigbluebutton.org/greenlight-v1.html) (or later) server. This means you don't need a separate server to run GreenLight.
|
|
||||||
|
|
||||||
For more informaiton see [Installing GreenLight](http://docs.bigbluebutton.org/greenlight-v1.html#installing-greenlight).
|
Greenlight is designed to work on a [BigBlueButton 2.0](https://github.com/bigbluebutton/bigbluebutton) (or later) server.
|
||||||
|
|
||||||
# Source Code
|
For information on installing Greenlight, checkout our [Installing Greenlight on a BigBlueButton Server](http://docs.bigbluebutton.org/greenlight/gl-install.html#installing-on-a-bigbluebutton-server) documentation.
|
||||||
|
|
||||||
GreenLight is a rails 5 application.
|
## Source Code & Contributing
|
||||||
|
|
||||||
Many developers already know Rails well, and we wanted to create both a full front-end to BigBlueButton but also a reference implementation of how to fully leverage the [BigBlueButton API](http://docs.bigbluebutton.org/dev/api.html).
|
Greenlight is built using Ruby on Rails. Many developers already know Rails well, and we wanted to create both a full front-end to BigBlueButton but also a reference implementation of how to fully leverage the [BigBlueButton API](http://docs.bigbluebutton.org/dev/api.html).
|
||||||
|
|
||||||
We invite you to build upon GreenLight and help make it better. See [Contributing to BigBlueButton](http://docs.bigbluebutton.org/support/faq.html#contributing-to-bigbluebutton).
|
We invite you to build upon Greenlight and help make it better. See [Contributing to BigBlueButton](http://docs.bigbluebutton.org/support/faq.html#contributing-to-bigbluebutton).
|
||||||
|
|
||||||
We invite your feedback, questions, and suggests about GreenLight too. Please post them to the [developer mailing list](https://groups.google.com/forum/#!forum/bigbluebutton-dev).
|
We invite your feedback, questions, and suggests about Greenlight too. Please post them to the [developer mailing list](https://groups.google.com/forum/#!forum/bigbluebutton-dev).
|
||||||
|
|||||||
@@ -1,6 +1,15 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
# Add your own tasks in files placed in lib/tasks ending in .rake,
|
||||||
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
# for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
|
||||||
|
|
||||||
require_relative 'config/application'
|
require_relative 'config/application'
|
||||||
|
require 'rake/testtask'
|
||||||
|
|
||||||
|
Rake::TestTask.new do |t|
|
||||||
|
t.libs << "test"
|
||||||
|
t.test_files = FileList['test/test*.rb']
|
||||||
|
t.verbose = true
|
||||||
|
end
|
||||||
|
|
||||||
Rails.application.load_tasks
|
Rails.application.load_tasks
|
||||||
|
|||||||
@@ -0,0 +1,11 @@
|
|||||||
|
# Security Policy
|
||||||
|
|
||||||
|
## Supported Versions
|
||||||
|
|
||||||
|
We support only the latest version of this project.
|
||||||
|
|
||||||
|
## Reporting a Vulnerability
|
||||||
|
|
||||||
|
If you think you’ve found a security issue with BigBlueButton or Greenlight, we ask that you do a [responsible disclosure](https://en.wikipedia.org/wiki/Responsible_disclosure) and e-mail us directly at security@bigbluebutton.org.
|
||||||
|
|
||||||
|
We will respond to you quickly, work with you to examine the scope of the issue, and give priority to fixing it as soon as possible.
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
//
|
//
|
||||||
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or modify it under the
|
// This program is free software; you can redistribute it and/or modify it under the
|
||||||
// terms of the GNU Lesser General Public License as published by the Free Software
|
// terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
|||||||
@@ -0,0 +1,93 @@
|
|||||||
|
Copyright 2010, 2012, 2014 Adobe Systems Incorporated (http://www.adobe.com/), with Reserved Font Name ‘Source’.
|
||||||
|
|
||||||
|
This Font Software is licensed under the SIL Open Font License, Version 1.1.
|
||||||
|
This license is copied below, and is also available with a FAQ at:
|
||||||
|
http://scripts.sil.org/OFL
|
||||||
|
|
||||||
|
|
||||||
|
-----------------------------------------------------------
|
||||||
|
SIL OPEN FONT LICENSE Version 1.1 - 26 February 2007
|
||||||
|
-----------------------------------------------------------
|
||||||
|
|
||||||
|
PREAMBLE
|
||||||
|
The goals of the Open Font License (OFL) are to stimulate worldwide
|
||||||
|
development of collaborative font projects, to support the font creation
|
||||||
|
efforts of academic and linguistic communities, and to provide a free and
|
||||||
|
open framework in which fonts may be shared and improved in partnership
|
||||||
|
with others.
|
||||||
|
|
||||||
|
The OFL allows the licensed fonts to be used, studied, modified and
|
||||||
|
redistributed freely as long as they are not sold by themselves. The
|
||||||
|
fonts, including any derivative works, can be bundled, embedded,
|
||||||
|
redistributed and/or sold with any software provided that any reserved
|
||||||
|
names are not used by derivative works. The fonts and derivatives,
|
||||||
|
however, cannot be released under any other type of license. The
|
||||||
|
requirement for fonts to remain under this license does not apply
|
||||||
|
to any document created using the fonts or their derivatives.
|
||||||
|
|
||||||
|
DEFINITIONS
|
||||||
|
"Font Software" refers to the set of files released by the Copyright
|
||||||
|
Holder(s) under this license and clearly marked as such. This may
|
||||||
|
include source files, build scripts and documentation.
|
||||||
|
|
||||||
|
"Reserved Font Name" refers to any names specified as such after the
|
||||||
|
copyright statement(s).
|
||||||
|
|
||||||
|
"Original Version" refers to the collection of Font Software components as
|
||||||
|
distributed by the Copyright Holder(s).
|
||||||
|
|
||||||
|
"Modified Version" refers to any derivative made by adding to, deleting,
|
||||||
|
or substituting -- in part or in whole -- any of the components of the
|
||||||
|
Original Version, by changing formats or by porting the Font Software to a
|
||||||
|
new environment.
|
||||||
|
|
||||||
|
"Author" refers to any designer, engineer, programmer, technical
|
||||||
|
writer or other person who contributed to the Font Software.
|
||||||
|
|
||||||
|
PERMISSION & CONDITIONS
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining
|
||||||
|
a copy of the Font Software, to use, study, copy, merge, embed, modify,
|
||||||
|
redistribute, and sell modified and unmodified copies of the Font
|
||||||
|
Software, subject to the following conditions:
|
||||||
|
|
||||||
|
1) Neither the Font Software nor any of its individual components,
|
||||||
|
in Original or Modified Versions, may be sold by itself.
|
||||||
|
|
||||||
|
2) Original or Modified Versions of the Font Software may be bundled,
|
||||||
|
redistributed and/or sold with any software, provided that each copy
|
||||||
|
contains the above copyright notice and this license. These can be
|
||||||
|
included either as stand-alone text files, human-readable headers or
|
||||||
|
in the appropriate machine-readable metadata fields within text or
|
||||||
|
binary files as long as those fields can be easily viewed by the user.
|
||||||
|
|
||||||
|
3) No Modified Version of the Font Software may use the Reserved Font
|
||||||
|
Name(s) unless explicit written permission is granted by the corresponding
|
||||||
|
Copyright Holder. This restriction only applies to the primary font name as
|
||||||
|
presented to the users.
|
||||||
|
|
||||||
|
4) The name(s) of the Copyright Holder(s) or the Author(s) of the Font
|
||||||
|
Software shall not be used to promote, endorse or advertise any
|
||||||
|
Modified Version, except to acknowledge the contribution(s) of the
|
||||||
|
Copyright Holder(s) and the Author(s) or with their explicit written
|
||||||
|
permission.
|
||||||
|
|
||||||
|
5) The Font Software, modified or unmodified, in part or in whole,
|
||||||
|
must be distributed entirely under this license, and must not be
|
||||||
|
distributed under any other license. The requirement for fonts to
|
||||||
|
remain under this license does not apply to any document created
|
||||||
|
using the Font Software.
|
||||||
|
|
||||||
|
TERMINATION
|
||||||
|
This license becomes null and void if any of the above conditions are
|
||||||
|
not met.
|
||||||
|
|
||||||
|
DISCLAIMER
|
||||||
|
THE FONT SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||||
|
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO ANY WARRANTIES OF
|
||||||
|
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT
|
||||||
|
OF COPYRIGHT, PATENT, TRADEMARK, OR OTHER RIGHT. IN NO EVENT SHALL THE
|
||||||
|
COPYRIGHT HOLDER BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
||||||
|
INCLUDING ANY GENERAL, SPECIAL, INDIRECT, INCIDENTAL, OR CONSEQUENTIAL
|
||||||
|
DAMAGES, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
||||||
|
FROM, OUT OF THE USE OR INABILITY TO USE THE FONT SOFTWARE OR FROM
|
||||||
|
OTHER DEALINGS IN THE FONT SOFTWARE.
|
||||||
|
Before Width: | Height: | Size: 1.1 MiB After Width: | Height: | Size: 1.1 MiB |
|
Before Width: | Height: | Size: 3.9 KiB After Width: | Height: | Size: 3.9 KiB |
|
Before Width: | Height: | Size: 40 KiB |
|
After Width: | Height: | Size: 2.5 KiB |
|
Before Width: | Height: | Size: 771 B |
|
After Width: | Height: | Size: 23 KiB |
|
After Width: | Height: | Size: 92 KiB |
|
After Width: | Height: | Size: 56 KiB |
|
After Width: | Height: | Size: 54 KiB |
|
After Width: | Height: | Size: 8.0 KiB |
|
Before Width: | Height: | Size: 22 KiB |
|
Before Width: | Height: | Size: 673 B |
|
After Width: | Height: | Size: 6.6 KiB |
|
After Width: | Height: | Size: 30 KiB |
|
After Width: | Height: | Size: 20 KiB |
|
After Width: | Height: | Size: 10 KiB |
|
Before Width: | Height: | Size: 4.2 KiB |
|
After Width: | Height: | Size: 14 KiB |
|
Before Width: | Height: | Size: 1.2 KiB |
|
After Width: | Height: | Size: 17 KiB |
@@ -1,236 +0,0 @@
|
|||||||
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
// terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
// Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
// Handles live updating and initial population of the previous meetings and active meetings lists on
|
|
||||||
// the landing page using custom Actioncable events.
|
|
||||||
|
|
||||||
var MEETINGS = {}
|
|
||||||
var WAITING = {}
|
|
||||||
var LOADING_DELAY = 1750 // milliseconds.
|
|
||||||
|
|
||||||
// Updates the previous meetings section.
|
|
||||||
var updatePreviousMeetings = function(){
|
|
||||||
$("ul.previously-joined li").each(function(idx, li) {
|
|
||||||
var previous_meeting = $(li);
|
|
||||||
if(Object.keys(MEETINGS).indexOf(previous_meeting.text()) > -1){
|
|
||||||
previous_meeting.remove()
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Ignore excess on either side of user_id.
|
|
||||||
var trimUserId = function(user_id){
|
|
||||||
components = user_id.split('_')
|
|
||||||
return components.sort(function (a, b) {return b.length - a.length;})[0]
|
|
||||||
}
|
|
||||||
|
|
||||||
// Finds a user by their user_id.
|
|
||||||
var findByUserId = function(users, user_id){
|
|
||||||
for(i = 0; i < users.length; i++){
|
|
||||||
if(trimUserId(users[i]['user_id']) == trimUserId(user_id)){
|
|
||||||
return i
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return undefined
|
|
||||||
}
|
|
||||||
|
|
||||||
// Adds a user to a meeting.
|
|
||||||
var addUser = function(data){
|
|
||||||
if(data['role'] == 'MODERATOR'){
|
|
||||||
MEETINGS[data['meeting']]['moderators'].push({'name': data['user'], 'user_id': data['user_id']})
|
|
||||||
} else {
|
|
||||||
MEETINGS[data['meeting']]['participants'].push({'name':data['user'], 'user_id': data['user_id']})
|
|
||||||
}
|
|
||||||
updateMeetingText(MEETINGS[data['meeting']])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Removes a user from a meeting.
|
|
||||||
var removeUser = function(data){
|
|
||||||
user = findByUserId(MEETINGS[data['meeting']]['moderators'], data['user_id'])
|
|
||||||
if(user == undefined){
|
|
||||||
user = findByUserId(MEETINGS[data['meeting']]['participants'], data['user_id']);
|
|
||||||
if(user == undefined){ return; }
|
|
||||||
MEETINGS[data['meeting']]['participants'].splice(user, 1);
|
|
||||||
} else {
|
|
||||||
MEETINGS[data['meeting']]['moderators'].splice(user, 1);
|
|
||||||
}
|
|
||||||
updateMeetingText(MEETINGS[data['meeting']])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Updates the display text for an active meeting.
|
|
||||||
var updateMeetingText = function(m){
|
|
||||||
// If a meeting has a moderators property, it is running.
|
|
||||||
var body;
|
|
||||||
if(m.hasOwnProperty('moderators')){
|
|
||||||
var list;
|
|
||||||
if(m['moderators'].length + m['participants'].length == 0){
|
|
||||||
list = '(empty)'
|
|
||||||
} else {
|
|
||||||
list = m['moderators'].map(function(x){ return x['name']; }).join('(mod), ') +
|
|
||||||
(m['moderators'].length > 0 ? '(mod)' : '') +
|
|
||||||
(m['participants'].length > 0 && m['moderators'].length != 0 ? ', ' : '') +
|
|
||||||
(m['participants'].map(function(x){ return x['name']; }).join(', '))
|
|
||||||
}
|
|
||||||
body = '<a>' + m['name'] + '</a><i>: ' + list + '</i>'
|
|
||||||
// Otherwise it hasn't started (users waiting the join).
|
|
||||||
} else {
|
|
||||||
body = '<a>' + m['name'] + '</a><i> (not yet started): ' + m['users'].join(', ') + '</i>'
|
|
||||||
}
|
|
||||||
|
|
||||||
// If the item doesn't exist, add it and set up join meeting event.
|
|
||||||
if($("li[id='" + m['name'].replace(' ', '_') + "']").length == 0){
|
|
||||||
var meeting_item = $("<li>" + body + "</li>")
|
|
||||||
meeting_item.attr('id', m['name'].replace(' ', '_'))
|
|
||||||
$('.actives').append(meeting_item);
|
|
||||||
|
|
||||||
// Set up join on click.
|
|
||||||
meeting_item.click(function(){
|
|
||||||
joinMeeting(m['name']);
|
|
||||||
});
|
|
||||||
// Otherwise, just change the body.
|
|
||||||
} else {
|
|
||||||
$("li[id='" + m['name'].replace(' ', '_') + "']").html(body)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Initially populates the active meetings when the page loads using the API.
|
|
||||||
var initialPopulate = function(){
|
|
||||||
// Only populate on room resources.
|
|
||||||
var chopped = window.location.href.split('/')
|
|
||||||
if (!window.location.href.includes('rooms') || chopped[chopped.length - 2] == $('body').data('current-user')) { return; }
|
|
||||||
|
|
||||||
$.post((window.location.href + '/statuses').replace('#', ''), {previously_joined: getPreviouslyJoined()})
|
|
||||||
.done(function(data) {
|
|
||||||
|
|
||||||
// Populate waiting meetings.
|
|
||||||
Object.keys(data['waiting']).forEach(function(key) {
|
|
||||||
WAITING[key] = {'name': key, 'users': data['waiting'][key]}
|
|
||||||
updateMeetingText(WAITING[key])
|
|
||||||
})
|
|
||||||
|
|
||||||
// Add the meetings to the active meetings list.
|
|
||||||
for(var i = 0; i < data['active'].length; i++){
|
|
||||||
var meeting = data['active'][i]
|
|
||||||
|
|
||||||
var name = meeting['name']
|
|
||||||
var participants = meeting['participants']
|
|
||||||
var moderators = meeting['moderators']
|
|
||||||
|
|
||||||
// Create meeting.
|
|
||||||
MEETINGS[name] = {'name': name, 'participants': participants, 'moderators': moderators}
|
|
||||||
updateMeetingText(MEETINGS[name])
|
|
||||||
}
|
|
||||||
|
|
||||||
// Remove from previous meetings if they are active.
|
|
||||||
updatePreviousMeetings();
|
|
||||||
$('.hidden-list').show();
|
|
||||||
$('.active-spinner').hide();
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
// Gets a list of known previously joined meetings.
|
|
||||||
var getPreviouslyJoined = function(){
|
|
||||||
var joinedMeetings = localStorage.getItem('joinedRooms-' + $('body').data('current-user'));
|
|
||||||
if (joinedMeetings == '' || joinedMeetings == null){ return []; }
|
|
||||||
return joinedMeetings.split(',')
|
|
||||||
}
|
|
||||||
|
|
||||||
// Removes an active meeting.
|
|
||||||
var removeActiveMeeting = function(meeting){
|
|
||||||
if(meeting){ $("li[id='" + meeting['name'].replace(' ', '_') + "']").remove() }
|
|
||||||
}
|
|
||||||
|
|
||||||
// Directly join a meeting from active meetings.
|
|
||||||
var joinMeeting = function(meeting_name){
|
|
||||||
if (meeting_name == undefined || meeting_name == null) { return; }
|
|
||||||
Meeting.getInstance().setUserName(localStorage.getItem('lastJoinedName'));
|
|
||||||
Meeting.getInstance().setMeetingId(meeting_name);
|
|
||||||
|
|
||||||
var jqxhr = Meeting.getInstance().getJoinMeetingResponse();
|
|
||||||
if (jqxhr) {
|
|
||||||
jqxhr.done(function(data) {
|
|
||||||
if (data.messageKey === 'wait_for_moderator') {
|
|
||||||
waitForModerator(Meeting.getInstance().getURL());
|
|
||||||
} else {
|
|
||||||
$(location).attr("href", data.response.join_url);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
jqxhr.fail(function() {
|
|
||||||
console.info("meeting join failed");
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
$('.meeting-user-name').parent().addClass('has-error');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// Only need to register for logged in users.
|
|
||||||
$(document).on('turbolinks:load', function(){
|
|
||||||
if($('body').data('current-user')){
|
|
||||||
MEETINGS = {}
|
|
||||||
// Ensure actives is empty.
|
|
||||||
$('.actives').empty();
|
|
||||||
|
|
||||||
if(!App.messages){
|
|
||||||
// Setup actioncable.
|
|
||||||
App.messages = App.cable.subscriptions.create('RefreshMeetingsChannel', {
|
|
||||||
received: function(data) {
|
|
||||||
console.log('Recieved ' + data['method'] + ' action for ' + data['meeting'] + ' with room id ' + data['room'] + '.')
|
|
||||||
if(data['room'] == $('body').data('current-user')){
|
|
||||||
// Handle webhook event.
|
|
||||||
if(data['method'] == 'create'){
|
|
||||||
// Create an empty meeting.
|
|
||||||
MEETINGS[data['meeting']] = {'name': data['meeting'],
|
|
||||||
'participants': [],
|
|
||||||
'moderators': []}
|
|
||||||
updateMeetingText(MEETINGS[data['meeting']])
|
|
||||||
updatePreviousMeetings();
|
|
||||||
if (WAITING.hasOwnProperty(data['meeting'])){ delete WAITING[data['meeting']]; }
|
|
||||||
} else if(data['method'] == 'destroy'){
|
|
||||||
removeActiveMeeting(MEETINGS[data['meeting']])
|
|
||||||
PreviousMeetings.uniqueAdd([data['meeting']])
|
|
||||||
delete MEETINGS[data['meeting']]
|
|
||||||
} else if(data['method'] == 'join'){
|
|
||||||
addUser(data)
|
|
||||||
} else if(data['method'] == 'leave'){
|
|
||||||
removeUser(data)
|
|
||||||
} else if(data['method'] == 'waiting'){
|
|
||||||
// Handle waiting meeting.
|
|
||||||
if(WAITING.hasOwnProperty(data['meeting'])){
|
|
||||||
WAITING[data['meeting']]['users'].push(data['user'])
|
|
||||||
updateMeetingText(WAITING[data['meeting']])
|
|
||||||
} else {
|
|
||||||
WAITING[data['meeting']] = {'name': data['meeting'],
|
|
||||||
'users': [data['user']]}
|
|
||||||
updateMeetingText(WAITING[data['meeting']])
|
|
||||||
}
|
|
||||||
} else if((data['method'] == 'no_longer_waiting') && (WAITING.hasOwnProperty(data['meeting']))){
|
|
||||||
WAITING[data['meeting']]['users'].splice(WAITING[data['meeting']]['users'].indexOf(data['user']), 1)
|
|
||||||
updateMeetingText(WAITING[data['meeting']])
|
|
||||||
if(WAITING[data['meeting']]['users'].length == 0){
|
|
||||||
removeActiveMeeting(WAITING[data['meeting']])
|
|
||||||
delete WAITING[data['meeting']]
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
console.log('Populating active meetings.');
|
|
||||||
// Short delay to hide the previous meetings population.
|
|
||||||
setTimeout(initialPopulate, LOADING_DELAY);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
@@ -0,0 +1,340 @@
|
|||||||
|
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
// terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
// Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
// version.
|
||||||
|
//
|
||||||
|
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
$(document).on('turbolinks:load', function(){
|
||||||
|
var controller = $("body").data('controller');
|
||||||
|
var action = $("body").data('action');
|
||||||
|
|
||||||
|
// Only run on the admins page.
|
||||||
|
if (controller == "admins") {
|
||||||
|
if(action == "index") {
|
||||||
|
//clear the role filter if user clicks on the x
|
||||||
|
$(".clear-role").click(function() {
|
||||||
|
var search = new URL(location.href).searchParams.get('search')
|
||||||
|
|
||||||
|
var url = window.location.pathname + "?page=1"
|
||||||
|
|
||||||
|
if (search) {
|
||||||
|
url += "&search=" + search
|
||||||
|
}
|
||||||
|
|
||||||
|
window.location.replace(url);
|
||||||
|
})
|
||||||
|
|
||||||
|
// Handle selected user tags
|
||||||
|
$(".manage-users-tab").click(function() {
|
||||||
|
$(".manage-users-tab").removeClass("selected")
|
||||||
|
$(this).addClass("selected")
|
||||||
|
|
||||||
|
updateTabParams(this.id)
|
||||||
|
})
|
||||||
|
|
||||||
|
$('.selectpicker').selectpicker({
|
||||||
|
liveSearchPlaceholder: getLocalizedString('javascript.search.start')
|
||||||
|
});
|
||||||
|
// Fixes turbolinks issue with bootstrap select
|
||||||
|
$(window).trigger('load.bs.select.data-api');
|
||||||
|
|
||||||
|
// Display merge accounts modal with correct info
|
||||||
|
$(".merge-user").click(function() {
|
||||||
|
// Update the path of save button
|
||||||
|
$("#merge-save-access").attr("data-path", $(this).data("path"))
|
||||||
|
let userInfo = $(this).data("info")
|
||||||
|
$("#merge-to").html("") // Clear current inputs
|
||||||
|
|
||||||
|
let spanName = document.createElement("span"),
|
||||||
|
spanEmail = document.createElement("span"),
|
||||||
|
spanUid = document.createElement("span");
|
||||||
|
spanName.innerText = userInfo.name
|
||||||
|
spanEmail.setAttribute('class', 'text-muted d-block')
|
||||||
|
spanEmail.innerText = userInfo.email
|
||||||
|
spanUid.setAttribute('class', 'text-muted d-block')
|
||||||
|
spanUid.innerText = userInfo.uid
|
||||||
|
|
||||||
|
$("#merge-to").append(spanName, spanEmail, spanUid)
|
||||||
|
})
|
||||||
|
|
||||||
|
$("#mergeUserModal").on("show.bs.modal", function() {
|
||||||
|
$(".selectpicker").selectpicker('val','')
|
||||||
|
})
|
||||||
|
|
||||||
|
$(".bootstrap-select").on("click", function() {
|
||||||
|
$(".bs-searchbox").siblings().hide()
|
||||||
|
})
|
||||||
|
|
||||||
|
$("#merge-user-select ~ button").on("click", function() {
|
||||||
|
$(".bs-searchbox").siblings().hide()
|
||||||
|
})
|
||||||
|
|
||||||
|
$(".bs-searchbox input").on("input", function() {
|
||||||
|
if ($(".bs-searchbox input").val() == '' || $(".bs-searchbox input").val().length < 3) {
|
||||||
|
$(".select-options").remove()
|
||||||
|
$(".bs-searchbox").siblings().hide()
|
||||||
|
} else {
|
||||||
|
// Manually populate the dropdown
|
||||||
|
$.get($("#merge-user-select").data("path"), { search: $(".bs-searchbox input").val() }, function(users) {
|
||||||
|
$(".select-options").remove()
|
||||||
|
if (users.length > 0) {
|
||||||
|
users.forEach(function(user) {
|
||||||
|
let opt = document.createElement("option")
|
||||||
|
$(opt).val(JSON.stringify({uid: user.uid, email: user.email, name: user.name}))
|
||||||
|
$(opt).text(user.name)
|
||||||
|
$(opt).addClass("select-options")
|
||||||
|
$(opt).attr("data-subtext", user.email)
|
||||||
|
$("#merge-user-select").append(opt)
|
||||||
|
})
|
||||||
|
// Only refresh the select dropdown if there are results to show
|
||||||
|
$('#merge-user-select').selectpicker('refresh');
|
||||||
|
}
|
||||||
|
$(".bs-searchbox").siblings().show()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// User selects an option from the Room Access dropdown
|
||||||
|
$(".bootstrap-select").on("changed.bs.select", function(){
|
||||||
|
// Get the uid of the selected user
|
||||||
|
let user = $(".selectpicker").selectpicker('val')
|
||||||
|
if (user != "") {
|
||||||
|
let userInfo = JSON.parse(user)
|
||||||
|
$("#merge-from").html("") // Clear current input
|
||||||
|
|
||||||
|
let spanName = document.createElement("span"),
|
||||||
|
spanEmail = document.createElement("span"),
|
||||||
|
spanUid = document.createElement("span");
|
||||||
|
spanName.innerText = userInfo.name
|
||||||
|
spanEmail.setAttribute('class', 'text-muted d-block')
|
||||||
|
spanEmail.innerText = userInfo.email
|
||||||
|
spanUid.setAttribute('class', 'text-muted d-block')
|
||||||
|
spanUid.id = 'from-uid'
|
||||||
|
spanUid.innerText = userInfo.uid
|
||||||
|
|
||||||
|
$("#merge-from").append(spanName, spanEmail, spanUid)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
else if(action == "site_settings"){
|
||||||
|
var urlParams = new URLSearchParams(window.location.search);
|
||||||
|
// Only load the colour selectors if on the appearance tab
|
||||||
|
if (urlParams.get("tab") == null || urlParams.get("tab") == "appearance") {
|
||||||
|
loadColourSelectors()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (action == "roles"){
|
||||||
|
// Refreshes the new role modal
|
||||||
|
$("#newRoleButton").click(function(){
|
||||||
|
$("#createRoleName").val("")
|
||||||
|
})
|
||||||
|
|
||||||
|
// Updates the colour picker to the correct colour
|
||||||
|
let role_colour = $("#role-colorinput-regular").data("colour")
|
||||||
|
$("#role-colorinput-regular").css("background-color", role_colour);
|
||||||
|
$("#role-colorinput-regular").css("border-color", role_colour);
|
||||||
|
|
||||||
|
loadRoleColourSelector(role_colour, $("#role-colorinput-regular").data("disabled"));
|
||||||
|
|
||||||
|
// Loads the jquery sortable so users can manually sort roles
|
||||||
|
$("#rolesSelect").sortable({
|
||||||
|
items: "a:not(.sort-disabled)",
|
||||||
|
update: function() {
|
||||||
|
$.ajax({
|
||||||
|
url: $(this).data("url"),
|
||||||
|
type: 'PATCH',
|
||||||
|
data: $(this).sortable('serialize')
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Change the branding image to the image provided
|
||||||
|
function changeBrandingImage(path) {
|
||||||
|
var url = $("#branding-url").val()
|
||||||
|
$.post(path, {value: url, tab: "appearance"})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change the Legal URL to the one provided
|
||||||
|
function changeLegalURL(path) {
|
||||||
|
var url = $("#legal-url").val()
|
||||||
|
$.post(path, {value: url, tab: "administration"})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change the Privacy Policy URL to the one provided
|
||||||
|
function changePrivacyPolicyURL(path) {
|
||||||
|
var url = $("#privpolicy-url").val()
|
||||||
|
$.post(path, {value: url, tab: "administration"})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Display the maintenance Banner
|
||||||
|
function displayMaintenanceBanner(path) {
|
||||||
|
var message = $("#maintenance-banner").val()
|
||||||
|
$.post(path, {value: message, tab: "administration"})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear the maintenance Banner
|
||||||
|
function clearMaintenanceBanner(path) {
|
||||||
|
$.post(path, {value: "", tab: "administration"})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Change the email mapping to the string provided
|
||||||
|
function changeEmailMapping(path) {
|
||||||
|
var url = $("#email-mapping").val()
|
||||||
|
$.post(path, {value: url, tab: "registration"})
|
||||||
|
}
|
||||||
|
|
||||||
|
function mergeUsers() {
|
||||||
|
let userToMerge = $("#from-uid").text()
|
||||||
|
$.post($("#merge-save-access").data("path"), {merge: userToMerge})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Filters by role
|
||||||
|
function filterRole(role) {
|
||||||
|
var search = new URL(location.href).searchParams.get('search')
|
||||||
|
|
||||||
|
var url = window.location.pathname + "?page=1" + "&role=" + role
|
||||||
|
|
||||||
|
if (search) {
|
||||||
|
url += "&search=" + search
|
||||||
|
}
|
||||||
|
|
||||||
|
window.location.replace(url);
|
||||||
|
}
|
||||||
|
|
||||||
|
function updateTabParams(tab) {
|
||||||
|
var search_params = new URLSearchParams(window.location.search)
|
||||||
|
|
||||||
|
if (window.location.href.includes("tab=")) {
|
||||||
|
search_params.set("tab", tab)
|
||||||
|
} else {
|
||||||
|
search_params.append("tab", tab)
|
||||||
|
}
|
||||||
|
|
||||||
|
search_params.delete("page")
|
||||||
|
|
||||||
|
window.location.search = search_params.toString()
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadColourSelectors() {
|
||||||
|
const pickrRegular = new Pickr({
|
||||||
|
el: '#colorinput-regular',
|
||||||
|
theme: 'monolith',
|
||||||
|
useAsButton: true,
|
||||||
|
lockOpacity: true,
|
||||||
|
defaultRepresentation: 'HEX',
|
||||||
|
closeWithKey: 'Enter',
|
||||||
|
default: $("#colorinput-regular").css("background-color"),
|
||||||
|
|
||||||
|
components: {
|
||||||
|
palette: true,
|
||||||
|
preview: true,
|
||||||
|
hue: true,
|
||||||
|
interaction: {
|
||||||
|
input: true,
|
||||||
|
save: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const pickrLighten = new Pickr({
|
||||||
|
el: '#colorinput-lighten',
|
||||||
|
theme: 'monolith',
|
||||||
|
useAsButton: true,
|
||||||
|
lockOpacity: true,
|
||||||
|
defaultRepresentation: 'HEX',
|
||||||
|
closeWithKey: 'Enter',
|
||||||
|
default: $("#colorinput-lighten").css("background-color"),
|
||||||
|
|
||||||
|
components: {
|
||||||
|
palette: true,
|
||||||
|
preview: true,
|
||||||
|
hue: true,
|
||||||
|
interaction: {
|
||||||
|
input: true,
|
||||||
|
save: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
const pickrDarken = new Pickr({
|
||||||
|
el: '#colorinput-darken',
|
||||||
|
theme: 'monolith',
|
||||||
|
useAsButton: true,
|
||||||
|
lockOpacity: true,
|
||||||
|
defaultRepresentation: 'HEX',
|
||||||
|
closeWithKey: 'Enter',
|
||||||
|
default: $("#colorinput-darken").css("background-color"),
|
||||||
|
|
||||||
|
components: {
|
||||||
|
palette: true,
|
||||||
|
preview: true,
|
||||||
|
hue: true,
|
||||||
|
interaction: {
|
||||||
|
input: true,
|
||||||
|
save: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
pickrRegular.on("save", (color, instance) => {
|
||||||
|
$.post($("#coloring-path-regular").val(), {value: color.toHEXA().toString()}).done(function() {
|
||||||
|
location.reload()
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
pickrLighten.on("save", (color, instance) => {
|
||||||
|
$.post($("#coloring-path-lighten").val(), {value: color.toHEXA().toString(), tab: "appearance"}).done(function() {
|
||||||
|
location.reload()
|
||||||
|
});
|
||||||
|
})
|
||||||
|
|
||||||
|
pickrDarken.on("save", (color, instance) => {
|
||||||
|
$.post($("#coloring-path-darken").val(), {value: color.toHEXA().toString(), tab: "appearance"}).done(function() {
|
||||||
|
location.reload()
|
||||||
|
});
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function loadRoleColourSelector(role_colour, disabled) {
|
||||||
|
if (!disabled) {
|
||||||
|
const pickrRoleRegular = new Pickr({
|
||||||
|
el: '#role-colorinput-regular',
|
||||||
|
theme: 'monolith',
|
||||||
|
useAsButton: true,
|
||||||
|
lockOpacity: true,
|
||||||
|
defaultRepresentation: 'HEX',
|
||||||
|
closeWithKey: 'Enter',
|
||||||
|
default: role_colour,
|
||||||
|
|
||||||
|
components: {
|
||||||
|
palette: true,
|
||||||
|
preview: true,
|
||||||
|
hue: true,
|
||||||
|
interaction: {
|
||||||
|
input: true,
|
||||||
|
save: true,
|
||||||
|
},
|
||||||
|
},
|
||||||
|
});
|
||||||
|
|
||||||
|
// On save update the colour input's background colour and update the role colour input
|
||||||
|
pickrRoleRegular.on("save", (color, instance) => {
|
||||||
|
$("#role-colorinput-regular").css("background-color", color.toHEXA().toString());
|
||||||
|
$("#role-colorinput-regular").css("border-color", color.toHEXA().toString());
|
||||||
|
$("#role-colour").val(color.toHEXA().toString());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
//
|
//
|
||||||
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or modify it under the
|
// This program is free software; you can redistribute it and/or modify it under the
|
||||||
// terms of the GNU Lesser General Public License as published by the Free Software
|
// terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
@@ -14,13 +14,25 @@
|
|||||||
// You should have received a copy of the GNU Lesser General Public License along
|
// You should have received a copy of the GNU Lesser General Public License along
|
||||||
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
//= require jquery2
|
// This is a manifest file that'll be compiled into application.js, which will include all the files
|
||||||
//= require jquery-ui
|
// listed below.
|
||||||
//= require dataTables/jquery.dataTables
|
//
|
||||||
//= require dataTables/bootstrap/3/jquery.dataTables.bootstrap
|
// Any JavaScript/Coffee file within this directory, lib/assets/javascripts, vendor/assets/javascripts,
|
||||||
//= require rails-timeago-all
|
// or any plugin's vendor/assets/javascripts directory can be referenced here using a relative path.
|
||||||
//= require bootstrap-sprockets
|
//
|
||||||
//= require qrcode/qrcode.js
|
// It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
|
||||||
|
// compiled file. JavaScript code in this file should be added after the last require_* statement.
|
||||||
|
//
|
||||||
|
// Read Sprockets README (https://github.com/rails/sprockets#sprockets-directives) for details
|
||||||
|
// about supported directives.
|
||||||
|
//
|
||||||
//= require turbolinks
|
//= require turbolinks
|
||||||
//= require_self
|
//= require jquery3
|
||||||
|
//= require tabler
|
||||||
|
//= require tabler.plugins
|
||||||
|
//= require jquery_ujs
|
||||||
|
//= require jquery-ui/widget
|
||||||
|
//= require jquery-ui/widgets/sortable
|
||||||
|
//= require pickr.min.js
|
||||||
|
//= require bootstrap-select.min.js
|
||||||
//= require_tree .
|
//= require_tree .
|
||||||
|
|||||||
@@ -1,19 +0,0 @@
|
|||||||
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
# terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
# Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
# version.
|
|
||||||
#
|
|
||||||
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
# Place all the behaviors and hooks related to the matching controller here.
|
|
||||||
# All this logic will automatically be available in application.js.
|
|
||||||
# You can use CoffeeScript in this file: http://coffeescript.org/
|
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
//
|
//
|
||||||
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or modify it under the
|
// This program is free software; you can redistribute it and/or modify it under the
|
||||||
// terms of the GNU Lesser General Public License as published by the Free Software
|
// terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
@@ -22,13 +22,11 @@
|
|||||||
//= require_tree ./channels
|
//= require_tree ./channels
|
||||||
|
|
||||||
(function() {
|
(function() {
|
||||||
var protocol = window.location.protocol === "https:" ? "wss://" : "ws://";
|
var protocol = (window.location.protocol === "https:" ? "wss://" : "ws://");
|
||||||
var host = window.GreenLight.WEBSOCKET_HOST || window.location.host + window.GreenLight.RELATIVE_ROOT;
|
var host = window.GreenLight.WEBSOCKET_HOST || window.location.host + window.GreenLight.RELATIVE_ROOT;
|
||||||
var path = window.GreenLight.WEBSOCKET_PATH || '/cable';
|
var url = protocol + host + '/cable';
|
||||||
var url = protocol + host + path;
|
|
||||||
|
|
||||||
this.App || (this.App = {});
|
this.App || (this.App = {});
|
||||||
|
|
||||||
App.cable = ActionCable.createConsumer(url);
|
App.cable = ActionCable.createConsumer(url);
|
||||||
|
|
||||||
}).call(this);
|
}).call(this);
|
||||||
|
|||||||
@@ -1,81 +0,0 @@
|
|||||||
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
// terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
// Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
var sessionStatusRefresh = function(url) {
|
|
||||||
$.get(url + "/session_status_refresh", function(html) {
|
|
||||||
$(".center-panel-wrapper").html(html);
|
|
||||||
displayRoomURL();
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var enableMeetingUpdates = function() {
|
|
||||||
var meetingId = ''
|
|
||||||
if (!$(".page-wrapper.rooms").data('main-room')) {
|
|
||||||
meetingId = $(".page-wrapper.rooms").data('id');
|
|
||||||
}
|
|
||||||
App.meeting_update = App.cable.subscriptions.create({
|
|
||||||
channel: 'MeetingUpdatesChannel',
|
|
||||||
admin_id: $(".page-wrapper.rooms").data('admin-id'),
|
|
||||||
meeting_id: meetingId
|
|
||||||
},
|
|
||||||
{
|
|
||||||
received: function(data) {
|
|
||||||
if (data.action === 'moderator_joined') {
|
|
||||||
if (!Meeting.getInstance().getModJoined()) {
|
|
||||||
Meeting.getInstance().setModJoined(true);
|
|
||||||
if (Meeting.getInstance().getWaitingForMod()) {
|
|
||||||
loopJoin();
|
|
||||||
} else {
|
|
||||||
sessionStatusRefresh($('.meeting-url').val());
|
|
||||||
showAlert(I18n.meeting_started, 4000);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else if (data.action === 'meeting_ended') {
|
|
||||||
sessionStatusRefresh($('.meeting-url').val());
|
|
||||||
showAlert(I18n.meeting_ended, 4000);
|
|
||||||
} else if (data.action === 'user_waiting') {
|
|
||||||
// show a browser notification only to the owner
|
|
||||||
if (isRoomOwner()) {
|
|
||||||
showNotification(I18n.user_waiting_title, {
|
|
||||||
body: I18n.user_waiting_body.replace(/%{user}/, data.user).replace(/%{meeting}/, '"'+data.meeting_name+'"')
|
|
||||||
});
|
|
||||||
}
|
|
||||||
} else if(data.action === 'unable_to_join') {
|
|
||||||
showAlert(I18n.unable_to_join_mobile, 6000)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var disableMeetingUpdates = function() {
|
|
||||||
App.meeting_update.unsubscribe();
|
|
||||||
delete App.meeting_update
|
|
||||||
};
|
|
||||||
|
|
||||||
$(document).on("turbolinks:load", function() {
|
|
||||||
// disable meeting updates if enabled from a previous page
|
|
||||||
if (App.meeting_update) {
|
|
||||||
disableMeetingUpdates();
|
|
||||||
}
|
|
||||||
if ($("body[data-controller=landing]").get(0)) {
|
|
||||||
if ($("body[data-action=rooms]").get(0)) {
|
|
||||||
enableMeetingUpdates();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}).call(this);
|
|
||||||
@@ -1,75 +0,0 @@
|
|||||||
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
// terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
// Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
var enableRecordingUpdates = function() {
|
|
||||||
App.recording_update = App.cable.subscriptions.create({
|
|
||||||
channel: 'RecordingUpdatesChannel',
|
|
||||||
admin_id: $(".page-wrapper.rooms").data('admin-id'),
|
|
||||||
meeting_id: $(".page-wrapper.rooms").data('id')
|
|
||||||
},
|
|
||||||
{
|
|
||||||
received: function(data) {
|
|
||||||
|
|
||||||
var recordings = Recordings.getInstance();
|
|
||||||
var table = recordings.table.api();
|
|
||||||
var row = table.row("#"+data.id);
|
|
||||||
|
|
||||||
if (data.action === 'update') {
|
|
||||||
var rowData = row.data();
|
|
||||||
|
|
||||||
rowData.published = data.published;
|
|
||||||
rowData.listed = data.listed;
|
|
||||||
table.row("#"+data.id).data(rowData);
|
|
||||||
recordings.draw();
|
|
||||||
|
|
||||||
var status = data.published ? (data.listed ? 'published' : 'unlisted') : 'unpublished';
|
|
||||||
showAlert(I18n['recording_'+status], 4000);
|
|
||||||
|
|
||||||
} else if (data.action === 'delete') {
|
|
||||||
row.remove();
|
|
||||||
recordings.draw();
|
|
||||||
showAlert(I18n.recording_deleted, 4000);
|
|
||||||
|
|
||||||
} else if (data.action === 'create') {
|
|
||||||
if (isRoomOwner() && row.length == 0) {
|
|
||||||
recordings.refresh();
|
|
||||||
showAlert(I18n.recording_created, 4000);
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var disableRecordingUpdates = function() {
|
|
||||||
App.recording_update.unsubscribe();
|
|
||||||
delete App.recording_update
|
|
||||||
};
|
|
||||||
|
|
||||||
$(document).on("turbolinks:load", function() {
|
|
||||||
// disable recording updates if enabled from a previous page
|
|
||||||
if (App.recording_update) {
|
|
||||||
disableRecordingUpdates();
|
|
||||||
}
|
|
||||||
if ($("body[data-controller=landing]").get(0)) {
|
|
||||||
if ($("body[data-action=rooms]").get(0)) {
|
|
||||||
enableRecordingUpdates();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}).call(this);
|
|
||||||
@@ -0,0 +1,37 @@
|
|||||||
|
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
// terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
// Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
// version.
|
||||||
|
//
|
||||||
|
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
$(document).on('turbolinks:load', function(){
|
||||||
|
$("#cookies-agree-button").click(function() {
|
||||||
|
//create a cookie that lasts 1 year
|
||||||
|
var cookieDate = new Date();
|
||||||
|
cookieDate.setFullYear(cookieDate.getFullYear() + 1); //1 year from now
|
||||||
|
document.cookie = "cookie_consented=true; path=/; expires=" + cookieDate.toUTCString() + ";"
|
||||||
|
|
||||||
|
//hide the banner at the bottom
|
||||||
|
$(".cookies-banner").attr("style","display:none !important")
|
||||||
|
})
|
||||||
|
|
||||||
|
$("#maintenance-close").click(function(event) {
|
||||||
|
//create a cookie that lasts 1 day
|
||||||
|
|
||||||
|
var cookieDate = new Date()
|
||||||
|
cookieDate.setDate(cookieDate.getDate() + 1) //1 day from now
|
||||||
|
console.log("maintenance_window=" + $(event.target).data("date") + "; path=/; expires=" + cookieDate.toUTCString() + ";")
|
||||||
|
|
||||||
|
document.cookie = "maintenance_window=" + $(event.target).data("date") + "; path=/; expires=" + cookieDate.toUTCString() + ";"
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -0,0 +1,57 @@
|
|||||||
|
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
// terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
// Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
// version.
|
||||||
|
//
|
||||||
|
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
$(document).on('turbolinks:load', function(){
|
||||||
|
var controller = $("body").data('controller');
|
||||||
|
var action = $("body").data('action');
|
||||||
|
|
||||||
|
// Only run on the admins page.
|
||||||
|
if (controller == "admins" && action == "index") {
|
||||||
|
// show the modal with the correct form action url
|
||||||
|
$(".delete-user").click(function(){
|
||||||
|
$("#delete-confirm").parent().attr("action", $(this).data("path"))
|
||||||
|
|
||||||
|
if ($(this).data("delete") == "temp-delete") {
|
||||||
|
$("#perm-delete").hide()
|
||||||
|
$("#delete-warning").show()
|
||||||
|
} else {
|
||||||
|
$("#perm-delete").show()
|
||||||
|
$("#delete-warning").hide()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
$(".delete-user").click(function(data){
|
||||||
|
document.getElementById("delete-checkbox").checked = false
|
||||||
|
$("#delete-confirm").prop("disabled", "disabled")
|
||||||
|
|
||||||
|
if ($(data.target).data("delete") == "temp-delete") {
|
||||||
|
$("#perm-delete").hide()
|
||||||
|
$("#delete-warning").show()
|
||||||
|
} else {
|
||||||
|
$("#perm-delete").show()
|
||||||
|
$("#delete-warning").hide()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
$("#delete-checkbox").click(function(data){
|
||||||
|
if (document.getElementById("delete-checkbox").checked) {
|
||||||
|
$("#delete-confirm").removeAttr("disabled")
|
||||||
|
} else {
|
||||||
|
$("#delete-confirm").prop("disabled", "disabled")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -1,6 +1,6 @@
|
|||||||
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
//
|
//
|
||||||
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
//
|
//
|
||||||
// This program is free software; you can redistribute it and/or modify it under the
|
// This program is free software; you can redistribute it and/or modify it under the
|
||||||
// terms of the GNU Lesser General Public License as published by the Free Software
|
// terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
@@ -14,10 +14,23 @@
|
|||||||
// You should have received a copy of the GNU Lesser General Public License along
|
// You should have received a copy of the GNU Lesser General Public License along
|
||||||
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
.login {
|
$(document).on('turbolinks:load', function(){
|
||||||
.center-panel {
|
// Stores the current url when the user clicks the sign in button
|
||||||
.center-panel-size {
|
$(".sign-in-button").click(function(){
|
||||||
max-width: 400px
|
var url = location.href
|
||||||
}
|
// Add the slash at the end if it's missing
|
||||||
}
|
url += url.endsWith("/") ? "" : "/"
|
||||||
}
|
document.cookie ="return_to=" + url
|
||||||
|
})
|
||||||
|
|
||||||
|
// Checks to see if the user provided an image url and displays it if they did
|
||||||
|
$("#user-image")
|
||||||
|
.on("load", function() {
|
||||||
|
$("#user-image").show()
|
||||||
|
$("#user-avatar").hide()
|
||||||
|
})
|
||||||
|
.on("error", function() {
|
||||||
|
$("#user-image").hide()
|
||||||
|
$("#user-avatar").show()
|
||||||
|
})
|
||||||
|
})
|
||||||
@@ -1,297 +0,0 @@
|
|||||||
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
// terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
// Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
/** global: Meeting */
|
|
||||||
/** global: PreviousMeetings */
|
|
||||||
/** global: QRCode */
|
|
||||||
/** global: Recordings */
|
|
||||||
/** global: Turbolinks */
|
|
||||||
|
|
||||||
(function() {
|
|
||||||
|
|
||||||
var qrcode;
|
|
||||||
|
|
||||||
var waitForModerator = function(url) {
|
|
||||||
window.localStorage.setItem("waitingName", $('.meeting-user-name').val());
|
|
||||||
$.post(url + "/wait", {name: $('.meeting-user-name').val()}, function(html) {
|
|
||||||
$(".center-panel-wrapper").html(html);
|
|
||||||
});
|
|
||||||
if (!Meeting.getInstance().getWaitingForMod()) {
|
|
||||||
Meeting.getInstance().setWaitingForMod(true);
|
|
||||||
if (Meeting.getInstance().getModJoined()) {
|
|
||||||
loopJoin();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var init = function() {
|
|
||||||
Meeting.clear();
|
|
||||||
var nameInput = $('.meeting-user-name');
|
|
||||||
if (!nameInput.val()) {
|
|
||||||
var lastName = window.localStorage.getItem('lastJoinedName');
|
|
||||||
if (lastName !== 'undefined') {
|
|
||||||
nameInput.val(lastName);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
// setup event handlers
|
|
||||||
$('.center-panel-wrapper').on ('click', '.meeting-join', function () {
|
|
||||||
var name = $('.meeting-user-name').val();
|
|
||||||
Meeting.getInstance().setUserName(name);
|
|
||||||
Meeting.getInstance().setMeetingId($(".page-wrapper").data('id'));
|
|
||||||
|
|
||||||
// a user name is set, join the user into the session
|
|
||||||
if (name !== undefined && name !== null) {
|
|
||||||
var jqxhr = Meeting.getInstance().getJoinMeetingResponse();
|
|
||||||
if (jqxhr) {
|
|
||||||
jqxhr.done(function(data) {
|
|
||||||
if (data.messageKey === 'wait_for_moderator') {
|
|
||||||
waitForModerator(Meeting.getInstance().getURL());
|
|
||||||
} else if (data.messageKey === 'ok') {
|
|
||||||
$(location).attr("href", data.response.join_url);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
jqxhr.fail(function() {
|
|
||||||
console.info("meeting join failed");
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
$('.meeting-user-name').parent().addClass('has-error');
|
|
||||||
}
|
|
||||||
|
|
||||||
// if not user name was set it means we must ask for a name
|
|
||||||
} else {
|
|
||||||
$(location).attr("href", Meeting.getInstance().getURL());
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.center-panel-wrapper').on ('click', '.meeting-start', function () {
|
|
||||||
Turbolinks.visit($('.meeting-url').val());
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.center-panel-wrapper').on ('input', '.meeting-user-name', function () {
|
|
||||||
if ($(this).val() === '') {
|
|
||||||
$(this).parent().addClass('has-error')
|
|
||||||
} else {
|
|
||||||
$(this).parent().removeClass('has-error')
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.center-panel-wrapper').on ('keypress', '.meeting-user-name', function (event) {
|
|
||||||
if (event.keyCode === 13) {
|
|
||||||
event.preventDefault();
|
|
||||||
$('.meeting-join').click();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.center-panel-wrapper').on ('keypress', '.meeting-name', function (event) {
|
|
||||||
if (event.keyCode === 13) {
|
|
||||||
event.preventDefault();
|
|
||||||
$('.meeting-start').click();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.center-panel-wrapper').on ('click', '.meeting-end', function () {
|
|
||||||
var jqxhr = Meeting.getInstance().endMeeting();
|
|
||||||
var btn = $(this);
|
|
||||||
btn.prop("disabled", true);
|
|
||||||
jqxhr.fail(function() {
|
|
||||||
console.info("meeting end failed");
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.center-panel-wrapper').on ('click', '.meeting-url-copy', function () {
|
|
||||||
var meetingURLInput = $('.meeting-url');
|
|
||||||
|
|
||||||
// copy URL
|
|
||||||
meetingURLInput.select();
|
|
||||||
try {
|
|
||||||
var success = document.execCommand("copy");
|
|
||||||
if (success) {
|
|
||||||
meetingURLInput.blur();
|
|
||||||
$(this).trigger('hint', [$(this).data('copied-hint')]);
|
|
||||||
} else {
|
|
||||||
$(this).trigger('hint', [$(this).data('copy-error')]);
|
|
||||||
}
|
|
||||||
} catch (err) {
|
|
||||||
$(this).trigger('hint', [$(this).data('copy-error')]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.center-panel-wrapper').on('hint', '.meeting-url-copy', function (event, msg) {
|
|
||||||
$(this).focus();
|
|
||||||
$(this).attr('title', msg)
|
|
||||||
.tooltip('fixTitle')
|
|
||||||
.tooltip('show')
|
|
||||||
.attr('title', $(this).data('copy-hint'))
|
|
||||||
.tooltip('fixTitle');
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.center-panel-wrapper').on('mouseleave', '.meeting-url-copy', function () {
|
|
||||||
$(this).blur();
|
|
||||||
});
|
|
||||||
|
|
||||||
// button used to send invitations to the meeting (i.e. "mailto:" link)
|
|
||||||
$('.center-panel-wrapper').on('click', '.meeting-invite', function () {
|
|
||||||
var meetingURL = Meeting.getInstance().getURL();
|
|
||||||
var subject = $(this).data("invite-subject");
|
|
||||||
var body = $(this).data("invite-body").replace("&&URL&&", meetingURL);
|
|
||||||
var mailto = "mailto:?subject=" + encodeURIComponent(subject) + "&body=" + encodeURIComponent(body);
|
|
||||||
window.open(mailto);
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.center-panel-wrapper').on ('click', '.meeting-url-qrcode', function () {
|
|
||||||
var meetingURL;
|
|
||||||
|
|
||||||
try {
|
|
||||||
meetingURL = $('.meeting-url').val();
|
|
||||||
if ($('.meeting-url-qrcode-group').is(':empty')) {
|
|
||||||
// generate code
|
|
||||||
qrcode = new QRCode($(".meeting-url-qrcode-group")[0], {
|
|
||||||
text: meetingURL,
|
|
||||||
width: 128,
|
|
||||||
height: 128,
|
|
||||||
colorDark : "#000000",
|
|
||||||
colorLight : "#ffffff",
|
|
||||||
correctLevel : QRCode.CorrectLevel.H
|
|
||||||
});
|
|
||||||
} else {
|
|
||||||
// clear the code.
|
|
||||||
qrcode.clear();
|
|
||||||
// make another code.
|
|
||||||
qrcode.makeCode(meetingURL);
|
|
||||||
}
|
|
||||||
$(this).trigger('hint', [$(this).data('qrcode-generated-hint')]);
|
|
||||||
} catch (err) {
|
|
||||||
$(this).trigger('hint', [$(this).data('qrcode-generate-error')]);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.center-panel-wrapper').on('hint', '.meeting-url-qrcode', function (event, msg) {
|
|
||||||
$(this).focus();
|
|
||||||
$(this).attr('title', msg)
|
|
||||||
.tooltip('fixTitle')
|
|
||||||
.tooltip('show')
|
|
||||||
.attr('title', $(this).data('qrcode-generate-hint'))
|
|
||||||
.tooltip('fixTitle');
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.center-panel-wrapper').on('mouseleave', '.meeting-url-qrcode', function () {
|
|
||||||
$(this).blur();
|
|
||||||
});
|
|
||||||
|
|
||||||
$('.center-panel-wrapper').on('focus', '.meeting-url', function () {
|
|
||||||
$(this).select();
|
|
||||||
});
|
|
||||||
|
|
||||||
// only allow ctrl commands
|
|
||||||
$('.center-panel-wrapper').on('keydown', '.meeting-url', function (event) {
|
|
||||||
if(!event.ctrlKey) {
|
|
||||||
event.preventDefault();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
// enable tooltips
|
|
||||||
var options = {
|
|
||||||
selector: '.has-tooltip',
|
|
||||||
container: 'body'
|
|
||||||
};
|
|
||||||
$(document).tooltip(options);
|
|
||||||
options = {
|
|
||||||
selector: '.bottom-tooltip',
|
|
||||||
container: 'body',
|
|
||||||
placement: 'bottom'
|
|
||||||
};
|
|
||||||
$(document).tooltip(options);
|
|
||||||
|
|
||||||
// focus name input or join button
|
|
||||||
if ($('.meeting-name').is(':visible')) {
|
|
||||||
$('.meeting-name').focus();
|
|
||||||
} else if ($('.meeting-user-name').is(':visible')) {
|
|
||||||
$('.meeting-user-name').focus();
|
|
||||||
} else {
|
|
||||||
$('.meeting-join').focus();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var initIndex = function() {
|
|
||||||
|
|
||||||
$('.center-panel-wrapper').on('input', '.meeting-name', function () {
|
|
||||||
var newId = $(this).val();
|
|
||||||
Meeting.getInstance().setMeetingId(newId);
|
|
||||||
$(".page-wrapper.meetings").data('id', newId);
|
|
||||||
$('.meeting-url').val(Meeting.getInstance().getURL());
|
|
||||||
$('.join-meeting-title').text('"'+newId+'"');
|
|
||||||
if (newId === '') {
|
|
||||||
$('.invite-join-wrapper').addClass('hidden');
|
|
||||||
} else {
|
|
||||||
$('.invite-join-wrapper').removeClass('hidden');
|
|
||||||
}
|
|
||||||
if (!$('.meeting-url-qrcode-group').is(':empty')) {
|
|
||||||
$('.meeting-url-qrcode-group').empty();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
PreviousMeetings.init('joinedMeetings');
|
|
||||||
};
|
|
||||||
|
|
||||||
var initMeetings = function() {
|
|
||||||
$('.meeting-url').val(Meeting.getInstance().getURL());
|
|
||||||
};
|
|
||||||
|
|
||||||
var initRooms = function() {
|
|
||||||
displayRoomURL();
|
|
||||||
var roomAdmin = $('.page-wrapper.rooms').data('admin-id');
|
|
||||||
|
|
||||||
$('.center-panel-wrapper').on('input', '.meeting-name', function () {
|
|
||||||
var newId = $(this).val();
|
|
||||||
Meeting.getInstance().setMeetingId(newId);
|
|
||||||
$('.meeting-url').val(Meeting.getInstance().getURL());
|
|
||||||
$('.join-meeting-title').text('"'+newId+'"');
|
|
||||||
if (newId === '') {
|
|
||||||
$('.invite-join-wrapper').addClass('hidden');
|
|
||||||
} else {
|
|
||||||
$('.invite-join-wrapper').removeClass('hidden');
|
|
||||||
}
|
|
||||||
if (!$('.meeting-url-qrcode-group').is(':empty')) {
|
|
||||||
$('.meeting-url-qrcode-group').empty();
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
if ($(".page-wrapper.rooms").data('main-room')) {
|
|
||||||
PreviousMeetings.init('joinedRooms-'+roomAdmin);
|
|
||||||
|
|
||||||
if ($('input.meeting-name').val() !== '') {
|
|
||||||
$('input.meeting-name').trigger('input');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Recordings.getInstance().refresh();
|
|
||||||
Recordings.getInstance().setupActionHandlers();
|
|
||||||
};
|
|
||||||
|
|
||||||
$(document).on("turbolinks:load", function() {
|
|
||||||
if ($("body[data-controller=landing]").get(0)) {
|
|
||||||
init();
|
|
||||||
if ($("body[data-action=index]").get(0)) {
|
|
||||||
initIndex();
|
|
||||||
} else if ($("body[data-action=meetings]").get(0)) {
|
|
||||||
initMeetings();
|
|
||||||
} else if ($("body[data-action=rooms]").get(0)) {
|
|
||||||
initRooms();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
});
|
|
||||||
}).call(this);
|
|
||||||
@@ -0,0 +1,51 @@
|
|||||||
|
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
// terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
// Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
// version.
|
||||||
|
//
|
||||||
|
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
$(document).on('turbolinks:load', function(){
|
||||||
|
$.rails.refreshCSRFTokens();
|
||||||
|
})
|
||||||
|
|
||||||
|
document.addEventListener("turbolinks:before-cache", function() {
|
||||||
|
$(".alert").remove()
|
||||||
|
})
|
||||||
|
|
||||||
|
// Gets the localized string
|
||||||
|
function getLocalizedString(key) {
|
||||||
|
var keyArr = key.split(".")
|
||||||
|
var translated = I18n
|
||||||
|
|
||||||
|
// Search current language for the key
|
||||||
|
try {
|
||||||
|
keyArr.forEach(function(k) {
|
||||||
|
translated = translated[k]
|
||||||
|
})
|
||||||
|
} catch (e) {
|
||||||
|
// Key is missing in selected language so default to english
|
||||||
|
translated = undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// If key is not found, search the fallback language for the key
|
||||||
|
if (translated === null || translated === undefined) {
|
||||||
|
translated = I18nFallback
|
||||||
|
|
||||||
|
keyArr.forEach(function(k) {
|
||||||
|
translated = translated[k]
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
return translated
|
||||||
|
}
|
||||||
@@ -1,135 +0,0 @@
|
|||||||
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
# terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
# Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
# version.
|
|
||||||
#
|
|
||||||
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
# Meeting class
|
|
||||||
|
|
||||||
_meetingInstance = null
|
|
||||||
|
|
||||||
class @Meeting
|
|
||||||
constructor: (@meetingId, @type, @userName, @adminId) ->
|
|
||||||
|
|
||||||
# Gets the current instance or creates a new one
|
|
||||||
@getInstance: ->
|
|
||||||
if _meetingInstance
|
|
||||||
return _meetingInstance
|
|
||||||
meetingId = $(".page-wrapper").data('id')
|
|
||||||
type = $("body").data('resource')
|
|
||||||
name = $('.meeting-user-name').val()
|
|
||||||
adminId = $(".page-wrapper").data('admin-id')
|
|
||||||
_meetingInstance = new Meeting(meetingId, type, name, adminId)
|
|
||||||
return _meetingInstance
|
|
||||||
|
|
||||||
@clear: ->
|
|
||||||
_meetingInstance = null
|
|
||||||
|
|
||||||
@buildMeetingURL: (meetingId, type, adminId) ->
|
|
||||||
fullId = ''
|
|
||||||
if adminId
|
|
||||||
fullId = encodeURIComponent(adminId) + '/' + encodeURIComponent(meetingId)
|
|
||||||
else
|
|
||||||
fullId = encodeURIComponent(meetingId)
|
|
||||||
return @buildRootURL() + '/' + type + '/' + fullId
|
|
||||||
|
|
||||||
@buildRootURL: ->
|
|
||||||
url = location.protocol + '//' + location.hostname
|
|
||||||
if location.port
|
|
||||||
url += ':' + location.port
|
|
||||||
if GreenLight.RELATIVE_ROOT
|
|
||||||
url += GreenLight.RELATIVE_ROOT
|
|
||||||
return url
|
|
||||||
|
|
||||||
# Sends the end meeting request
|
|
||||||
# Returns a response object
|
|
||||||
endMeeting: ->
|
|
||||||
return $.ajax({
|
|
||||||
url: @getURL() + "/end",
|
|
||||||
type: 'DELETE'
|
|
||||||
})
|
|
||||||
|
|
||||||
# Makes a call to get the join meeting url
|
|
||||||
# Returns a response object
|
|
||||||
# The response object contains the URL to join the meeting
|
|
||||||
getJoinMeetingResponse: ->
|
|
||||||
if !@userName
|
|
||||||
showAlert(I18n.enter_name, 4000, 'danger')
|
|
||||||
return false
|
|
||||||
return $.get @getURL() + "/join?name=" + @userName, (data) =>
|
|
||||||
# update name used to join meeting
|
|
||||||
localStorage.setItem('lastJoinedName', @getUserName())
|
|
||||||
|
|
||||||
if data.messageKey == 'ok'
|
|
||||||
key = ''
|
|
||||||
if @type == 'meetings'
|
|
||||||
key = 'joinedMeetings'
|
|
||||||
else if @type == 'rooms'
|
|
||||||
key = 'joinedRooms-'+@adminId
|
|
||||||
|
|
||||||
# update previously joined meetings/rooms on client
|
|
||||||
try
|
|
||||||
joinedMeetings = localStorage.getItem(key) || ''
|
|
||||||
joinedMeetings = joinedMeetings.split(',')
|
|
||||||
joinedMeetings = joinedMeetings.filter (item) => item != @meetingId.toString()
|
|
||||||
if joinedMeetings.length >= 5
|
|
||||||
joinedMeetings.splice(0, 1)
|
|
||||||
joinedMeetings.push(@meetingId)
|
|
||||||
localStorage.setItem(key, joinedMeetings.join(','))
|
|
||||||
catch err
|
|
||||||
localStorage.setItem(key, @meetingId)
|
|
||||||
|
|
||||||
getMeetingId: ->
|
|
||||||
return @meetingId
|
|
||||||
|
|
||||||
setMeetingId: (id) ->
|
|
||||||
@meetingId = id
|
|
||||||
return this
|
|
||||||
|
|
||||||
getAdminId: ->
|
|
||||||
return @adminId
|
|
||||||
|
|
||||||
setAdminId: (id) ->
|
|
||||||
@adminId = id
|
|
||||||
return this
|
|
||||||
|
|
||||||
getType: ->
|
|
||||||
return @type
|
|
||||||
|
|
||||||
setType: (type) ->
|
|
||||||
@type = type
|
|
||||||
return this
|
|
||||||
|
|
||||||
getURL: ->
|
|
||||||
return Meeting.buildMeetingURL(@meetingId, @type, @adminId)
|
|
||||||
|
|
||||||
getUserName: ->
|
|
||||||
return @userName
|
|
||||||
|
|
||||||
setUserName: (name) ->
|
|
||||||
@userName = name
|
|
||||||
return this
|
|
||||||
|
|
||||||
getModJoined: ->
|
|
||||||
return @modJoined
|
|
||||||
|
|
||||||
setModJoined: (modJoined) ->
|
|
||||||
@modJoined = modJoined
|
|
||||||
return this
|
|
||||||
|
|
||||||
getWaitingForMod: ->
|
|
||||||
return @waitingForMod
|
|
||||||
|
|
||||||
setWaitingForMod: (wMod) ->
|
|
||||||
@waitingForMod = wMod
|
|
||||||
return this
|
|
||||||
@@ -1,55 +0,0 @@
|
|||||||
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
# terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
# Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
# version.
|
|
||||||
#
|
|
||||||
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
# Previous Meetings class
|
|
||||||
|
|
||||||
class @PreviousMeetings
|
|
||||||
MAX_MEETINGS = 5
|
|
||||||
|
|
||||||
# initializes and populates previous meetings list with entries from localStorage
|
|
||||||
@init: (type) ->
|
|
||||||
$('.center-panel-wrapper').off 'click', '.fill-meeting-name'
|
|
||||||
$('.center-panel-wrapper').on 'click', '.fill-meeting-name', (event, msg) ->
|
|
||||||
name = $(this).text()
|
|
||||||
$('input.meeting-name').val(name).trigger('input')
|
|
||||||
|
|
||||||
$('ul.previously-joined').empty()
|
|
||||||
joinedMeetings = localStorage.getItem(type)
|
|
||||||
if joinedMeetings && joinedMeetings.length > 0
|
|
||||||
joinedMeetings = joinedMeetings.split(',')
|
|
||||||
PreviousMeetings.append(joinedMeetings.reverse())
|
|
||||||
|
|
||||||
# adds to previous meetings list if its unique
|
|
||||||
@uniqueAdd: (names) ->
|
|
||||||
meetings = $('ul.previously-joined > li').toArray().map( (li) ->
|
|
||||||
return li.innerText
|
|
||||||
)
|
|
||||||
index = meetings.indexOf('')
|
|
||||||
if index > 1
|
|
||||||
meetings.splice(index, 1)
|
|
||||||
if Array.isArray(names)
|
|
||||||
names = names.filter( (value) ->
|
|
||||||
return $.inArray(value, meetings) == -1
|
|
||||||
)
|
|
||||||
PreviousMeetings.append(names)
|
|
||||||
|
|
||||||
@append: (meeting_names) ->
|
|
||||||
for m in meeting_names
|
|
||||||
if $('ul.previously-joined > li').length > MAX_MEETINGS
|
|
||||||
break
|
|
||||||
$('ul.previously-joined').append('<li><a class="fill-meeting-name">'+m+'</a></li>')
|
|
||||||
|
|
||||||
$('.center-panel-wrapper .previously-joined-wrapper').removeClass('hidden')
|
|
||||||
@@ -0,0 +1,48 @@
|
|||||||
|
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
// terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
// Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
// version.
|
||||||
|
//
|
||||||
|
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// Handle changing of settings tabs.
|
||||||
|
$(document).on('turbolinks:load', function(){
|
||||||
|
var controller = $("body").data('controller');
|
||||||
|
var action = $("body").data('action');
|
||||||
|
|
||||||
|
if (controller == "rooms" && action == "show"
|
||||||
|
|| controller == "rooms" && action == "update"
|
||||||
|
|| controller == "users" && action == "recordings"
|
||||||
|
|| controller == "admins" && action == "server_recordings"){
|
||||||
|
// Handle recording emails.
|
||||||
|
$('.email-link').each(function(){
|
||||||
|
$(this).click(function(){
|
||||||
|
var subject = $(".username").text() + " " + getLocalizedString('javascript.room.mailer.subject');
|
||||||
|
var body = getLocalizedString('javascript.room.mailer.body') + "\n\n" + $(this).attr("data-pres-link");
|
||||||
|
var autogenerated = getLocalizedString('javascript.room.mailer.autogenerated') + "\n";
|
||||||
|
var footer = getLocalizedString('javascript.room.mailer.footer');
|
||||||
|
|
||||||
|
var url = "mailto:?subject=" + encodeURIComponent(subject) + "&body=" + encodeURIComponent(body) + encodeURIComponent(autogenerated) + encodeURIComponent(footer);
|
||||||
|
var win = window.open(url, '_blank');
|
||||||
|
|
||||||
|
win.focus();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// Handle recording delete modal
|
||||||
|
$(".delete-rec").click(function() {
|
||||||
|
$("#delete-rec-confirm").parent().attr("action", $(this).data("path"))
|
||||||
|
})
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -1,430 +0,0 @@
|
|||||||
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
# terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
# Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
# version.
|
|
||||||
#
|
|
||||||
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
# Recordings class
|
|
||||||
|
|
||||||
_recordingsInstance = null
|
|
||||||
|
|
||||||
class @Recordings
|
|
||||||
# adding or removing a column will require updates to all subsequent column positions
|
|
||||||
COLUMNS = [
|
|
||||||
'DATE',
|
|
||||||
'NAME',
|
|
||||||
'PREVIEW',
|
|
||||||
'DURATION',
|
|
||||||
'PARTICIPANTS',
|
|
||||||
'PLAYBACK',
|
|
||||||
'VISIBILITY',
|
|
||||||
'LISTED',
|
|
||||||
'ACTION'
|
|
||||||
]
|
|
||||||
COLUMN = {}
|
|
||||||
i = 0
|
|
||||||
for c in COLUMNS
|
|
||||||
COLUMN[c] = i++
|
|
||||||
|
|
||||||
constructor: ->
|
|
||||||
recordingsObject = this
|
|
||||||
|
|
||||||
# configure the datatable for recordings
|
|
||||||
this.table = $('#recordings').dataTable({
|
|
||||||
data: [],
|
|
||||||
rowId: 'id',
|
|
||||||
paging: false,
|
|
||||||
dom: 't',
|
|
||||||
info: false,
|
|
||||||
order: [[ COLUMN.DATE, "desc" ]],
|
|
||||||
language: {
|
|
||||||
emptyTable: '<h3>'+I18n.no_recordings_yet+'</h3>',
|
|
||||||
zeroRecords: '<h3>'+I18n.no_recordings+'</h3>'
|
|
||||||
},
|
|
||||||
columns: [
|
|
||||||
{ data: "start_time" },
|
|
||||||
{ data: "name", visible: $(".page-wrapper.rooms").data('main-room') },
|
|
||||||
{ data: "previews", orderable: false },
|
|
||||||
{ data: "duration" },
|
|
||||||
{ data: "participants" },
|
|
||||||
{ data: "playbacks", orderable: false },
|
|
||||||
{ data: "published" },
|
|
||||||
{ data: "listed", visible: false },
|
|
||||||
{ data: "id", orderable: false }
|
|
||||||
],
|
|
||||||
columnDefs: [
|
|
||||||
{
|
|
||||||
targets: COLUMN.DATE,
|
|
||||||
render: (data, type, row) ->
|
|
||||||
if type == 'display'
|
|
||||||
date = new Date(data)
|
|
||||||
title = date
|
|
||||||
.toLocaleString($('html').attr('lang'),
|
|
||||||
{month: 'long', day: 'numeric', year: 'numeric',
|
|
||||||
hour12: 'true', hour: '2-digit', minute: '2-digit'})
|
|
||||||
timeago = '<time datetime="'+date.toISOString()+'" data-time-ago="'+date.toISOString()+'">'+date.toISOString()+'</time>'
|
|
||||||
return title+'<span class="timeago">('+timeago+')</span>'
|
|
||||||
return data
|
|
||||||
},
|
|
||||||
{
|
|
||||||
targets: COLUMN.PREVIEW,
|
|
||||||
render: (data, type, row) ->
|
|
||||||
if type == 'display'
|
|
||||||
str = ''
|
|
||||||
if row.published
|
|
||||||
for d in data
|
|
||||||
str += '<img height="50" width="50" class="img-thumbnail" src="'+d.url+'" alt="'+d.alt+'"></img><img class="img-thumbnail large" src="'+d.url+'"></img>'
|
|
||||||
return str
|
|
||||||
return data
|
|
||||||
},
|
|
||||||
{
|
|
||||||
targets: COLUMN.PLAYBACK,
|
|
||||||
render: (data, type, row) ->
|
|
||||||
if type == 'display'
|
|
||||||
str = ''
|
|
||||||
if row.published
|
|
||||||
if data.length == 1
|
|
||||||
str = '<a class="btn btn-default play-tooltip" href="'+data[0].url+'" target="_blank"><i class="fa fa-play-circle"></i></a>'
|
|
||||||
else
|
|
||||||
for d in data
|
|
||||||
str += '<a href="'+d.url+'" target="_blank">'+d.type_i18n+'</a> '
|
|
||||||
return str
|
|
||||||
return data
|
|
||||||
},
|
|
||||||
{
|
|
||||||
targets: COLUMN.VISIBILITY,
|
|
||||||
render: (data, type, row) ->
|
|
||||||
visibility = ['unpublished', 'unlisted', 'published']
|
|
||||||
if row.published
|
|
||||||
if row.listed
|
|
||||||
state = visibility[2]
|
|
||||||
else
|
|
||||||
state = visibility[1]
|
|
||||||
else
|
|
||||||
state = visibility[0]
|
|
||||||
if type == 'display'
|
|
||||||
return I18n[state]
|
|
||||||
return state
|
|
||||||
},
|
|
||||||
{
|
|
||||||
targets: COLUMN.ACTION,
|
|
||||||
render: (data, type, row) ->
|
|
||||||
if type == 'display'
|
|
||||||
roomName = Meeting.getInstance().getMeetingId()
|
|
||||||
recordingActions = $('.hidden-elements').find('.recording-actions')
|
|
||||||
classes = ['recording-unpublished', 'recording-unlisted', 'recording-published']
|
|
||||||
if row.published
|
|
||||||
if row.listed
|
|
||||||
cls = classes[2]
|
|
||||||
else
|
|
||||||
cls = classes[1]
|
|
||||||
else
|
|
||||||
cls = classes[0]
|
|
||||||
trigger = recordingActions.find('.recording-update-trigger')
|
|
||||||
trigger.removeClass(classes.join(' '))
|
|
||||||
trigger.addClass(cls)
|
|
||||||
|
|
||||||
return recordingActions.html()
|
|
||||||
return data
|
|
||||||
}
|
|
||||||
]
|
|
||||||
})
|
|
||||||
options = {
|
|
||||||
selector: '.delete-tooltip',
|
|
||||||
placement: 'bottom',
|
|
||||||
title: I18n.delete_recording
|
|
||||||
};
|
|
||||||
$('#recordings').tooltip(options)
|
|
||||||
|
|
||||||
options.selector = '.visibility-tooltip'
|
|
||||||
options.title = I18n.change_visibility
|
|
||||||
$('#recordings').tooltip(options)
|
|
||||||
|
|
||||||
options.selector = '.play-tooltip'
|
|
||||||
options.title = I18n.play_recording
|
|
||||||
$('#recordings').tooltip(options)
|
|
||||||
|
|
||||||
options.selector = '.youtube-tooltip'
|
|
||||||
options.title = I18n.upload_youtube
|
|
||||||
$('#recordings').tooltip(options)
|
|
||||||
|
|
||||||
options.selector = '.upload-tooltip'
|
|
||||||
options.title = I18n.share
|
|
||||||
$('#recordings').tooltip(options)
|
|
||||||
|
|
||||||
options.selector = '.mail-tooltip'
|
|
||||||
options.title = I18n.mail_recording
|
|
||||||
$('#recordings').tooltip(options)
|
|
||||||
|
|
||||||
$(document).one "turbolinks:before-cache", =>
|
|
||||||
@getTable().api().clear().draw().destroy()
|
|
||||||
|
|
||||||
# enable popovers
|
|
||||||
# can't use trigger:'focus' because it doesn't work will with buttons inside
|
|
||||||
# the popover
|
|
||||||
options = {
|
|
||||||
selector: '.has-popover',
|
|
||||||
html: true,
|
|
||||||
trigger: 'click',
|
|
||||||
title: ->
|
|
||||||
return $(this).data("popover-title");
|
|
||||||
content: ->
|
|
||||||
bodySelector = $(this).data("popover-body")
|
|
||||||
return $(bodySelector).html()
|
|
||||||
}
|
|
||||||
$('#recordings').popover(options)
|
|
||||||
|
|
||||||
# close popovers manually when clicking outside of them or in buttons
|
|
||||||
# with [data-dismiss="popover"]
|
|
||||||
# careful to hide only the open popover and not all of them, otherwise they won't reopen
|
|
||||||
$('body').on 'click', (e) ->
|
|
||||||
$('.has-popover').each ->
|
|
||||||
if !$(this).is(e.target) and $(this).has(e.target).length == 0 and $('.popover.in').has(e.target).length == 0
|
|
||||||
if $(this).next(".popover.in").length > 0
|
|
||||||
$(this).popover('hide')
|
|
||||||
$(document).on 'click', '[data-dismiss="popover"]', (e) ->
|
|
||||||
$('.has-popover').each ->
|
|
||||||
if $(this).next(".popover.in").length > 0
|
|
||||||
$(this).popover('hide')
|
|
||||||
|
|
||||||
# Gets the current instance or creates a new one
|
|
||||||
@getInstance: ->
|
|
||||||
if _recordingsInstance && Recordings.initialized()
|
|
||||||
return _recordingsInstance
|
|
||||||
_recordingsInstance = new Recordings()
|
|
||||||
return _recordingsInstance
|
|
||||||
|
|
||||||
@initialize: ->
|
|
||||||
Recordings.getInstance()
|
|
||||||
|
|
||||||
@initialized: ->
|
|
||||||
return $.fn.DataTable.isDataTable('#recordings') && _recordingsInstance
|
|
||||||
|
|
||||||
draw: ->
|
|
||||||
if !@isOwner()
|
|
||||||
@table.api().columns(COLUMN.LISTED).search('true')
|
|
||||||
@table.api().columns.adjust().draw()
|
|
||||||
|
|
||||||
# refresh the recordings from the server
|
|
||||||
refresh: ->
|
|
||||||
table_api = this.table.api()
|
|
||||||
$.get @getRecordingsURL(), (data) =>
|
|
||||||
@setOwner(data.is_owner)
|
|
||||||
if !@owner
|
|
||||||
table_api.column(COLUMN.ACTION).visible(false)
|
|
||||||
table_api.column(COLUMN.VISIBILITY).visible(false)
|
|
||||||
for recording in data.recordings
|
|
||||||
# NOTE: Temporary fix for the minutes to milliseconds bug some recordings may have
|
|
||||||
# experienced when transitioning from BigBlueButton 1.1 to BigBlueButton 2.0.
|
|
||||||
recording.duration = if recording.duration < 1000 then recording.duration else parseInt(recording.length / 60000)
|
|
||||||
data.recordings.sort (a,b) ->
|
|
||||||
return new Date(b.start_time) - new Date(a.start_time)
|
|
||||||
table_api.clear()
|
|
||||||
table_api.rows.add(data.recordings)
|
|
||||||
@draw()
|
|
||||||
|
|
||||||
if $(".page-wrapper.rooms").data('main-room')
|
|
||||||
recording_names = data.recordings.map (r) ->
|
|
||||||
return r.name
|
|
||||||
output = {}
|
|
||||||
for key in [0...recording_names.length]
|
|
||||||
output[recording_names[key]] = recording_names[key]
|
|
||||||
PreviousMeetings.uniqueAdd(value for key, value of output)
|
|
||||||
|
|
||||||
|
|
||||||
# setup click handlers for the action buttons
|
|
||||||
setupActionHandlers: ->
|
|
||||||
table_api = this.table.api()
|
|
||||||
recordingsObject = this
|
|
||||||
selectedUpload = null
|
|
||||||
canUpload = false
|
|
||||||
|
|
||||||
@getTable().on 'click', '.recording-update', (event) ->
|
|
||||||
btn = $(this)
|
|
||||||
row = table_api.row($(this).closest('tr')).data()
|
|
||||||
url = recordingsObject.getRecordingsURL()
|
|
||||||
id = row.id
|
|
||||||
|
|
||||||
published = btn.data('visibility') == "unlisted" ||
|
|
||||||
btn.data('visibility') == "published"
|
|
||||||
listed = btn.data('visibility') == "published"
|
|
||||||
|
|
||||||
btn.prop('disabled', true)
|
|
||||||
|
|
||||||
data = { published: published.toString() }
|
|
||||||
data["meta_" + GreenLight.META_LISTED] = listed.toString();
|
|
||||||
$.ajax({
|
|
||||||
method: 'PATCH',
|
|
||||||
url: url+'/'+id,
|
|
||||||
data: data
|
|
||||||
}).done((data) ->
|
|
||||||
btn.prop('disabled', false)
|
|
||||||
).fail((xhr, text) ->
|
|
||||||
btn.prop('disabled', false)
|
|
||||||
)
|
|
||||||
|
|
||||||
@getTable().on 'click', '.recording-delete', (event) ->
|
|
||||||
btn = $(this)
|
|
||||||
row = table_api.row($(this).closest('tr'))
|
|
||||||
url = recordingsObject.getRecordingsURL()
|
|
||||||
id = row.data().id
|
|
||||||
btn.prop('disabled', true)
|
|
||||||
$.ajax({
|
|
||||||
method: 'DELETE',
|
|
||||||
url: url+'/'+id
|
|
||||||
}).done((data) ->
|
|
||||||
btn.prop('disabled', false)
|
|
||||||
).fail((xhr, text) ->
|
|
||||||
btn.prop('disabled', false)
|
|
||||||
if xhr.status == 404
|
|
||||||
row.remove();
|
|
||||||
recordingsObject.draw()
|
|
||||||
showAlert(I18n.recording_deleted, 4000);
|
|
||||||
)
|
|
||||||
|
|
||||||
@getTable().on 'click', '.upload-button', (event) ->
|
|
||||||
btn = $(this)
|
|
||||||
row = table_api.row($(this).closest('tr')).data()
|
|
||||||
url = recordingsObject.getRecordingsURL()
|
|
||||||
id = row.id
|
|
||||||
|
|
||||||
title = $('#video-title').val()
|
|
||||||
privacy_status = $('input[name=privacy_status]:checked').val()
|
|
||||||
|
|
||||||
if title == ''
|
|
||||||
title = row.name
|
|
||||||
|
|
||||||
$.ajax({
|
|
||||||
method: 'POST',
|
|
||||||
url: url+'/'+id
|
|
||||||
data: {video_title: title, privacy_status: privacy_status}
|
|
||||||
success: (data) ->
|
|
||||||
|
|
||||||
if data['url'] != null
|
|
||||||
window.location.href = data['url']
|
|
||||||
else
|
|
||||||
cloud = selectedUpload.find('.cloud-blue')
|
|
||||||
check = selectedUpload.find('.green-check')
|
|
||||||
spinner = selectedUpload.find('.load-spinner')
|
|
||||||
|
|
||||||
showAlert(I18n.successful_upload, 4000);
|
|
||||||
|
|
||||||
spinner.hide()
|
|
||||||
check.show()
|
|
||||||
|
|
||||||
setTimeout ( ->
|
|
||||||
cloud.show()
|
|
||||||
check.hide()
|
|
||||||
), 2500
|
|
||||||
})
|
|
||||||
|
|
||||||
selectedUpload.find('.cloud-blue').hide()
|
|
||||||
selectedUpload.find('.load-spinner').show()
|
|
||||||
|
|
||||||
@getTable().on 'click', '.mail-recording', (event) ->
|
|
||||||
btn = $(this)
|
|
||||||
row = table_api.row($(this).closest('tr')).data()
|
|
||||||
url = recordingsObject.getRecordingsURL()
|
|
||||||
id = row.id
|
|
||||||
|
|
||||||
# Take the username from the header.
|
|
||||||
username = $('#title-header').text().replace('Welcome ', '').trim()
|
|
||||||
|
|
||||||
recording_url = row.playbacks[0].url
|
|
||||||
webcams_url = getHostName(recording_url) + '/presentation/' + id + '/video/webcams.webm'
|
|
||||||
subject = username + I18n.recording_mail_subject
|
|
||||||
body = I18n.recording_mail_body + "\n\n" + recording_url + "\n\n" + I18n.email_footer_1 + "\n" + I18n.email_footer_2
|
|
||||||
|
|
||||||
mailto = "mailto:?subject=" + encodeURIComponent(subject) + "&body=" + encodeURIComponent(body);
|
|
||||||
window.open(mailto);
|
|
||||||
|
|
||||||
@getTable().on 'click', '.youtube-upload', (event) ->
|
|
||||||
row = table_api.row($(this).closest('tr')).data()
|
|
||||||
$('#video-title').attr('value', row.name)
|
|
||||||
|
|
||||||
@getTable().on 'click', '.cloud-upload', (event) ->
|
|
||||||
btn = $(this)
|
|
||||||
row = table_api.row($(this).closest('tr')).data()
|
|
||||||
id = row.id
|
|
||||||
|
|
||||||
selectedUpload = $(this)
|
|
||||||
|
|
||||||
# Determine if the recording can be uploaded to Youtube.
|
|
||||||
$.ajax({
|
|
||||||
method: 'POST',
|
|
||||||
data: {'rec_id': id},
|
|
||||||
async: false,
|
|
||||||
url: recordingsObject.getRecordingsURL() + '/can_upload'
|
|
||||||
}).success((res_data) ->
|
|
||||||
canUpload = res_data['uploadable']
|
|
||||||
)
|
|
||||||
|
|
||||||
youtube_button = $('.share-popover').find('.youtube-upload')
|
|
||||||
|
|
||||||
attr = $(this).attr('data-popover-body');
|
|
||||||
|
|
||||||
# Check if the cloud button has a popover body.
|
|
||||||
if (typeof attr == typeof undefined || attr == false)
|
|
||||||
switch canUpload
|
|
||||||
# We can upload the recording.
|
|
||||||
when 'true'
|
|
||||||
youtube_button.attr('title', I18n.upload_youtube)
|
|
||||||
youtube_button.removeClass('disabled-button')
|
|
||||||
youtube_button.addClass('has-popover')
|
|
||||||
youtube_button.show()
|
|
||||||
# Can't upload because uploading is disabled.
|
|
||||||
when 'uploading_disabled'
|
|
||||||
youtube_button.hide()
|
|
||||||
# Can't upload because account is not authenticated with Google.
|
|
||||||
when 'invalid_provider'
|
|
||||||
youtube_button.attr('title', I18n.invalid_provider)
|
|
||||||
youtube_button.addClass('disabled-button')
|
|
||||||
youtube_button.removeClass('has-popover')
|
|
||||||
youtube_button.show()
|
|
||||||
# Can't upload because recording does not contain video.
|
|
||||||
else
|
|
||||||
youtube_button.attr('title', I18n.no_video)
|
|
||||||
youtube_button.addClass('disabled-button')
|
|
||||||
youtube_button.removeClass('has-popover')
|
|
||||||
youtube_button.show()
|
|
||||||
|
|
||||||
$(this).attr('data-popover-body', '.share-popover')
|
|
||||||
$(this).popover('show')
|
|
||||||
else
|
|
||||||
$(this).popover('hide')
|
|
||||||
$(this).removeAttr('data-popover-body')
|
|
||||||
|
|
||||||
|
|
||||||
@getTable().on 'draw.dt', (event) ->
|
|
||||||
$('time[data-time-ago]').timeago();
|
|
||||||
|
|
||||||
getTable: ->
|
|
||||||
@table
|
|
||||||
|
|
||||||
getHostName = (url) ->
|
|
||||||
parser = document.createElement('a');
|
|
||||||
parser.href = url;
|
|
||||||
parser.origin;
|
|
||||||
|
|
||||||
getRecordingsURL: ->
|
|
||||||
if $(".page-wrapper.rooms").data('main-room')
|
|
||||||
base_url = Meeting.buildRootURL()+'/'+$('body').data('resource')+'/'+Meeting.getInstance().getAdminId()
|
|
||||||
else
|
|
||||||
base_url = $('.meeting-url').val()
|
|
||||||
base_url+'/recordings'
|
|
||||||
|
|
||||||
isOwner: ->
|
|
||||||
@owner
|
|
||||||
|
|
||||||
setOwner: (owner) ->
|
|
||||||
@owner = owner
|
|
||||||
@@ -0,0 +1,162 @@
|
|||||||
|
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
// terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
// Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
// version.
|
||||||
|
//
|
||||||
|
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
$(document).on('turbolinks:load', function(){
|
||||||
|
var controller = $("body").data('controller');
|
||||||
|
var action = $("body").data('action');
|
||||||
|
|
||||||
|
if(controller == "rooms" && action == "show"
|
||||||
|
|| controller == "rooms" && action == "update"
|
||||||
|
|| controller == "users" && action == "recordings"
|
||||||
|
|| controller == "admins" && action == "server_recordings"){
|
||||||
|
|
||||||
|
// Set a room header rename event
|
||||||
|
var configure_room_header = function(room_title){
|
||||||
|
|
||||||
|
function register_room_title_event(e){
|
||||||
|
// Remove current window events
|
||||||
|
$(window).off('mousedown keydown');
|
||||||
|
|
||||||
|
if(e.type == 'focusout'){
|
||||||
|
submit_rename_request(room_title);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
room_title.addClass("dotted_underline");
|
||||||
|
room_title.find('#user-text').fadeTo('medium', 0.7);
|
||||||
|
room_title.find('#user-text').attr("contenteditable", true);
|
||||||
|
room_title.find('#user-text').focus();
|
||||||
|
|
||||||
|
// Stop automatic refresh
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
register_window_event(room_title, 'user-text', '#edit-room', 'edit-room');
|
||||||
|
}
|
||||||
|
|
||||||
|
room_title.find('#user-text').on('dblclick focusout', function(e){
|
||||||
|
if(room_title.find('#edit-room').length){
|
||||||
|
register_room_title_event(e);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
room_title.find('.fa-edit').on('click', function(e){
|
||||||
|
register_room_title_event(e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Set a recording row rename event
|
||||||
|
var configure_recording_row = function(recording_title){
|
||||||
|
|
||||||
|
function register_recording_title_event(e){
|
||||||
|
// Remove current window events
|
||||||
|
$(window).off('mousedown keydown');
|
||||||
|
|
||||||
|
if(e.type == 'focusout'){
|
||||||
|
submit_rename_request(recording_title);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
recording_title.addClass("dotted_underline");
|
||||||
|
recording_title.fadeTo('medium', 0.7);
|
||||||
|
recording_title.find('span').attr("contenteditable", true);
|
||||||
|
recording_title.find('span').focus();
|
||||||
|
|
||||||
|
// Stop automatic refresh
|
||||||
|
e.preventDefault();
|
||||||
|
|
||||||
|
register_window_event(recording_title, 'recording-text', '#edit-record', 'edit-recordid');
|
||||||
|
}
|
||||||
|
|
||||||
|
recording_title.find('a').on('click focusout', function(e){
|
||||||
|
register_recording_title_event(e);
|
||||||
|
});
|
||||||
|
|
||||||
|
recording_title.find('#recording-text').on('dblclick focusout', function(e){
|
||||||
|
register_recording_title_event(e);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Register window event to submit new name
|
||||||
|
// upon click or upon pressing the enter key
|
||||||
|
var register_window_event = function(element, textfield_id, edit_button_id, edit_button_data){
|
||||||
|
$(window).on('mousedown keydown', function(clickEvent){
|
||||||
|
|
||||||
|
// Return if the text is clicked
|
||||||
|
if(clickEvent.type == "mousedown" && clickEvent.target.id == textfield_id){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Return if the edit icon is clicked
|
||||||
|
if(clickEvent.type == "mousedown" && $(clickEvent.target).is(edit_button_id) &&
|
||||||
|
$(clickEvent.target).data(edit_button_data) === element.find(edit_button_id).data(edit_button_data)){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check if event is keydown and enter key is not pressed
|
||||||
|
if(clickEvent.type == "keydown" && clickEvent.which !== 13){
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
clickEvent.preventDefault();
|
||||||
|
submit_rename_request(element);
|
||||||
|
|
||||||
|
// Remove window event when ajax call to update name is submitted
|
||||||
|
$(window).off('mousedown keydown');
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Apply ajax request depending on the element that triggered the event
|
||||||
|
var submit_rename_request = function(element){
|
||||||
|
if(element.is('#room-title')){
|
||||||
|
submit_update_request({
|
||||||
|
setting: "rename_header",
|
||||||
|
name: element.find('#user-text').text(),
|
||||||
|
}, element.data('path'), "POST");
|
||||||
|
}
|
||||||
|
else if(element.is('#recording-title')){
|
||||||
|
submit_update_request({
|
||||||
|
setting: "rename_recording",
|
||||||
|
record_id: element.data('recordid'),
|
||||||
|
record_name: element.find('span').text(),
|
||||||
|
room_uid: element.data('room-uid'),
|
||||||
|
}, element.data('path'), "PATCH");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Helper for submitting ajax requests
|
||||||
|
var submit_update_request = function(data, path, action){
|
||||||
|
// Send ajax request for update
|
||||||
|
$.ajax({
|
||||||
|
url: path,
|
||||||
|
type: action,
|
||||||
|
data: data,
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Elements that can be renamed
|
||||||
|
var room_title = $('#room-title');
|
||||||
|
var recording_rows = $('#recording-table').find('tr');
|
||||||
|
|
||||||
|
// Configure renaming for room header
|
||||||
|
configure_room_header(room_title);
|
||||||
|
|
||||||
|
// Configure renaming for recording rows
|
||||||
|
recording_rows.each(function(){
|
||||||
|
var recording_title = $(this).find('#recording-title');
|
||||||
|
configure_recording_row(recording_title);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -0,0 +1,399 @@
|
|||||||
|
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
// terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
// Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
// version.
|
||||||
|
//
|
||||||
|
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// Room specific js for copy button and email link.
|
||||||
|
$(document).on('turbolinks:load', function(){
|
||||||
|
var controller = $("body").data('controller');
|
||||||
|
var action = $("body").data('action');
|
||||||
|
|
||||||
|
// highlight current room
|
||||||
|
$('.room-block').removeClass('current');
|
||||||
|
$('a[href="' + window.location.pathname + '"] .room-block').addClass('current');
|
||||||
|
|
||||||
|
// Only run on room pages.
|
||||||
|
if (controller == "rooms" && action == "show"){
|
||||||
|
// Display and update all fields related to creating a room in the createRoomModal
|
||||||
|
$("#create-room-block").click(function(){
|
||||||
|
showCreateRoom(this)
|
||||||
|
})
|
||||||
|
|
||||||
|
checkIfAutoJoin()
|
||||||
|
}
|
||||||
|
|
||||||
|
// Autofocus on the Room Name label when creating a room only
|
||||||
|
$('#createRoomModal').on('shown.bs.modal', function (){
|
||||||
|
if ($(".create-only").css("display") == "block"){
|
||||||
|
$('#create-room-name').focus()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
if (controller == "rooms" && action == "show" || controller == "admins" && action == "server_rooms"){
|
||||||
|
// Display and update all fields related to creating a room in the createRoomModal
|
||||||
|
$(".update-room").click(function(){
|
||||||
|
showUpdateRoom(this)
|
||||||
|
})
|
||||||
|
|
||||||
|
$(".delete-room").click(function() {
|
||||||
|
showDeleteRoom(this)
|
||||||
|
})
|
||||||
|
|
||||||
|
$('.selectpicker').selectpicker({
|
||||||
|
liveSearchPlaceholder: getLocalizedString('javascript.search.start')
|
||||||
|
});
|
||||||
|
// Fixes turbolinks issue with bootstrap select
|
||||||
|
$(window).trigger('load.bs.select.data-api');
|
||||||
|
|
||||||
|
$(".share-room").click(function() {
|
||||||
|
// Update the path of save button
|
||||||
|
$("#save-access").attr("data-path", $(this).data("path"))
|
||||||
|
|
||||||
|
// Get list of users shared with and display them
|
||||||
|
displaySharedUsers($(this).data("users-path"))
|
||||||
|
})
|
||||||
|
|
||||||
|
$("#shareRoomModal").on("show.bs.modal", function() {
|
||||||
|
$(".selectpicker").selectpicker('val','')
|
||||||
|
})
|
||||||
|
|
||||||
|
$(".bootstrap-select").on("click", function() {
|
||||||
|
$(".bs-searchbox").siblings().hide()
|
||||||
|
})
|
||||||
|
|
||||||
|
$("#share-room-select ~ button").on("click", function() {
|
||||||
|
$(".bs-searchbox").siblings().hide()
|
||||||
|
})
|
||||||
|
|
||||||
|
$(".bs-searchbox input").on("input", function() {
|
||||||
|
if ($(".bs-searchbox input").val() == '' || $(".bs-searchbox input").val().length < 3) {
|
||||||
|
$(".select-options").remove()
|
||||||
|
$(".bs-searchbox").siblings().hide()
|
||||||
|
} else {
|
||||||
|
// Manually populate the dropdown
|
||||||
|
$.get($("#share-room-select").data("path"), { search: $(".bs-searchbox input").val() }, function(users) {
|
||||||
|
$(".select-options").remove()
|
||||||
|
if (users.length > 0) {
|
||||||
|
users.forEach(function(user) {
|
||||||
|
let opt = document.createElement("option")
|
||||||
|
$(opt).val(user.uid)
|
||||||
|
$(opt).text(user.name)
|
||||||
|
$(opt).addClass("select-options")
|
||||||
|
$(opt).attr("data-subtext", user.uid)
|
||||||
|
$("#share-room-select").append(opt)
|
||||||
|
})
|
||||||
|
// Only refresh the select dropdown if there are results to show
|
||||||
|
$('#share-room-select').selectpicker('refresh');
|
||||||
|
}
|
||||||
|
$(".bs-searchbox").siblings().show()
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
$(".remove-share-room").click(function() {
|
||||||
|
$("#remove-shared-confirm").parent().attr("action", $(this).data("path"))
|
||||||
|
})
|
||||||
|
|
||||||
|
// User selects an option from the Room Access dropdown
|
||||||
|
$(".bootstrap-select").on("changed.bs.select", function(){
|
||||||
|
// Get the uid of the selected user
|
||||||
|
let uid = $(".selectpicker").selectpicker('val')
|
||||||
|
|
||||||
|
// If the value was changed to blank, ignore it
|
||||||
|
if (uid == "") return
|
||||||
|
|
||||||
|
let currentListItems = $("#user-list li").toArray().map(user => $(user).data("uid"))
|
||||||
|
|
||||||
|
// Check to make sure that the user is not already there
|
||||||
|
if (!currentListItems.includes(uid)) {
|
||||||
|
// Create the faded list item and display it
|
||||||
|
let option = $("option[value='" + uid + "']")
|
||||||
|
|
||||||
|
let listItem = document.createElement("li")
|
||||||
|
listItem.setAttribute('class', 'list-group-item text-left not-saved add-access');
|
||||||
|
listItem.setAttribute("data-uid", uid)
|
||||||
|
|
||||||
|
let spanItemAvatar = document.createElement("span"),
|
||||||
|
spanItemName = document.createElement("span"),
|
||||||
|
spanItemUser = document.createElement("span");
|
||||||
|
spanItemAvatar.setAttribute('class', 'avatar float-left mr-2');
|
||||||
|
spanItemAvatar.innerText = option.text().charAt(0);
|
||||||
|
spanItemName.setAttribute('class', 'shared-user');
|
||||||
|
spanItemName.innerText = option.text();
|
||||||
|
spanItemUser.setAttribute('class', 'text-muted');
|
||||||
|
spanItemUser.innerText = option.data('subtext');
|
||||||
|
spanItemName.append(spanItemUser);
|
||||||
|
|
||||||
|
listItem.innerHTML = "<span class='text-primary float-right shared-user cursor-pointer' onclick='removeSharedUser(this)'><i class='fas fa-times'></i></span>"
|
||||||
|
listItem.prepend(spanItemName);
|
||||||
|
listItem.prepend(spanItemAvatar);
|
||||||
|
|
||||||
|
$("#user-list").append(listItem)
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
$("#presentation-upload").change(function(data) {
|
||||||
|
var file = data.target.files[0]
|
||||||
|
|
||||||
|
// Check file type and size to make sure they aren't over the limit
|
||||||
|
if (validFileUpload(file)) {
|
||||||
|
$("#presentation-upload-label").text(file.name)
|
||||||
|
} else {
|
||||||
|
$("#invalid-file-type").show()
|
||||||
|
$("#presentation-upload").val("")
|
||||||
|
$("#presentation-upload-label").text($("#presentation-upload-label").data("placeholder"))
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
$(".preupload-room").click(function() {
|
||||||
|
updatePreuploadPresentationModal(this)
|
||||||
|
})
|
||||||
|
|
||||||
|
$("#remove-presentation").click(function(data) {
|
||||||
|
removePreuploadPresentation($(this).data("remove"))
|
||||||
|
})
|
||||||
|
|
||||||
|
// trigger initial room filter
|
||||||
|
filterRooms();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
function copyInvite() {
|
||||||
|
$('#invite-url').select()
|
||||||
|
if (document.execCommand("copy")) {
|
||||||
|
$('#invite-url').blur();
|
||||||
|
copy = $("#copy-invite")
|
||||||
|
copy.addClass('btn-success');
|
||||||
|
copy.html("<i class='fas fa-check mr-1'></i>" + getLocalizedString("copied"))
|
||||||
|
setTimeout(function(){
|
||||||
|
copy.removeClass('btn-success');
|
||||||
|
copy.html("<i class='fas fa-copy mr-1'></i>" + getLocalizedString("copy"))
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function copyAccess() {
|
||||||
|
$('#copy-code').attr("type", "text")
|
||||||
|
$('#copy-code').select()
|
||||||
|
if (document.execCommand("copy")) {
|
||||||
|
$('#copy-code').attr("type", "hidden")
|
||||||
|
copy = $("#copy-access")
|
||||||
|
copy.addClass('btn-success');
|
||||||
|
copy.html("<i class='fas fa-check mr-1'></i>" + getLocalizedString("copied"))
|
||||||
|
setTimeout(function(){
|
||||||
|
copy.removeClass('btn-success');
|
||||||
|
copy.html("<i class='fas fa-copy mr-1'></i>" + getLocalizedString("room.copy_access"))
|
||||||
|
}, 1000)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showCreateRoom(target) {
|
||||||
|
$("#create-room-name").val("")
|
||||||
|
$("#create-room-access-code").text(getLocalizedString("modal.create_room.access_code_placeholder"))
|
||||||
|
$("#room_access_code").val(null)
|
||||||
|
|
||||||
|
$("#createRoomModal form").attr("action", $("body").data('relative-root'))
|
||||||
|
$("#room_mute_on_join").prop("checked", $("#room_mute_on_join").data("default"))
|
||||||
|
$("#room_require_moderator_approval").prop("checked", $("#room_require_moderator_approval").data("default"))
|
||||||
|
$("#room_anyone_can_start").prop("checked", $("#room_anyone_can_start").data("default"))
|
||||||
|
$("#room_all_join_moderator").prop("checked", $("#room_all_join_moderator").data("default"))
|
||||||
|
$("#room_recording").prop("checked", $("#room_recording").data("default"))
|
||||||
|
|
||||||
|
//show all elements & their children with a create-only class
|
||||||
|
$(".create-only").each(function() {
|
||||||
|
$(this).show()
|
||||||
|
if($(this).children().length > 0) { $(this).children().show() }
|
||||||
|
})
|
||||||
|
|
||||||
|
//hide all elements & their children with a update-only class
|
||||||
|
$(".update-only").each(function() {
|
||||||
|
$(this).attr('style',"display:none !important")
|
||||||
|
if($(this).children().length > 0) { $(this).children().attr('style',"display:none !important") }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function showUpdateRoom(target) {
|
||||||
|
var modal = $(target)
|
||||||
|
var update_path = modal.closest(".room-block").data("path")
|
||||||
|
var settings_path = modal.data("settings-path")
|
||||||
|
$("#create-room-name").val(modal.closest(".room-block").find(".room-name-text").text().trim())
|
||||||
|
$("#createRoomModal form").attr("action", update_path)
|
||||||
|
|
||||||
|
//show all elements & their children with a update-only class
|
||||||
|
$(".update-only").each(function() {
|
||||||
|
$(this).show()
|
||||||
|
if($(this).children().length > 0) { $(this).children().show() }
|
||||||
|
})
|
||||||
|
|
||||||
|
//hide all elements & their children with a create-only class
|
||||||
|
$(".create-only").each(function() {
|
||||||
|
$(this).attr('style',"display:none !important")
|
||||||
|
if($(this).children().length > 0) { $(this).children().attr('style',"display:none !important") }
|
||||||
|
})
|
||||||
|
|
||||||
|
updateCurrentSettings(settings_path)
|
||||||
|
|
||||||
|
var accessCode = modal.closest(".room-block").data("room-access-code")
|
||||||
|
|
||||||
|
if(accessCode){
|
||||||
|
$("#create-room-access-code").text(getLocalizedString("modal.create_room.access_code") + ": " + accessCode)
|
||||||
|
$("#room_access_code").val(accessCode)
|
||||||
|
} else {
|
||||||
|
$("#create-room-access-code").text(getLocalizedString("modal.create_room.access_code_placeholder"))
|
||||||
|
$("#room_access_code").val(null)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function showDeleteRoom(target) {
|
||||||
|
$("#delete-header").text(getLocalizedString("modal.delete_room.confirm").replace("%{room}", $(target).data("name")))
|
||||||
|
$("#delete-confirm").parent().attr("action", $(target).data("path"))
|
||||||
|
}
|
||||||
|
|
||||||
|
//Update the createRoomModal to show the correct current settings
|
||||||
|
function updateCurrentSettings(settings_path){
|
||||||
|
// Get current room settings and set checkbox
|
||||||
|
$.get(settings_path, function(settings) {
|
||||||
|
$("#room_mute_on_join").prop("checked", $("#room_mute_on_join").data("default") || settings.muteOnStart)
|
||||||
|
$("#room_require_moderator_approval").prop("checked", $("#room_require_moderator_approval").data("default") || settings.requireModeratorApproval)
|
||||||
|
$("#room_anyone_can_start").prop("checked", $("#room_anyone_can_start").data("default") || settings.anyoneCanStart)
|
||||||
|
$("#room_all_join_moderator").prop("checked", $("#room_all_join_moderator").data("default") || settings.joinModerator)
|
||||||
|
$("#room_recording").prop("checked", $("#room_recording").data("default") || Boolean(settings.recording))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function generateAccessCode(){
|
||||||
|
const accessCodeLength = 6
|
||||||
|
var validCharacters = "0123456789"
|
||||||
|
var accessCode = ""
|
||||||
|
|
||||||
|
for( var i = 0; i < accessCodeLength; i++){
|
||||||
|
accessCode += validCharacters.charAt(Math.floor(Math.random() * validCharacters.length));
|
||||||
|
}
|
||||||
|
|
||||||
|
$("#create-room-access-code").text(getLocalizedString("modal.create_room.access_code") + ": " + accessCode)
|
||||||
|
$("#room_access_code").val(accessCode)
|
||||||
|
}
|
||||||
|
|
||||||
|
function ResetAccessCode(){
|
||||||
|
$("#create-room-access-code").text(getLocalizedString("modal.create_room.access_code_placeholder"))
|
||||||
|
$("#room_access_code").val(null)
|
||||||
|
}
|
||||||
|
|
||||||
|
function saveAccessChanges() {
|
||||||
|
let listItemsToAdd = $("#user-list li:not(.remove-shared)").toArray().map(user => $(user).data("uid"))
|
||||||
|
|
||||||
|
$.post($("#save-access").data("path"), {add: listItemsToAdd})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Get list of users shared with and display them
|
||||||
|
function displaySharedUsers(path) {
|
||||||
|
$.get(path, function(users) {
|
||||||
|
// Create list element and add to user list
|
||||||
|
var user_list_html = ""
|
||||||
|
|
||||||
|
users.forEach(function(user) {
|
||||||
|
user_list_html += "<li class='list-group-item text-left' data-uid='" + user.uid + "'>"
|
||||||
|
|
||||||
|
if (user.image) {
|
||||||
|
user_list_html += "<img id='user-image' class='avatar float-left mr-2' src='" + user.image + "'></img>"
|
||||||
|
} else {
|
||||||
|
user_list_html += "<span class='avatar float-left mr-2'>" + user.name.charAt(0) + "</span>"
|
||||||
|
}
|
||||||
|
user_list_html += "<span class='shared-user'>" + user.name + "<span class='text-muted ml-1'>" + user.uid + "</span></span>"
|
||||||
|
user_list_html += "<span class='text-primary float-right shared-user cursor-pointer' onclick='removeSharedUser(this)'><i class='fas fa-times'></i></span>"
|
||||||
|
user_list_html += "</li>"
|
||||||
|
})
|
||||||
|
|
||||||
|
$("#user-list").html(user_list_html)
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Removes the user from the list of shared users
|
||||||
|
function removeSharedUser(target) {
|
||||||
|
let parentLI = target.closest("li")
|
||||||
|
|
||||||
|
if (parentLI.classList.contains("not-saved")) {
|
||||||
|
parentLI.parentNode.removeChild(parentLI)
|
||||||
|
} else {
|
||||||
|
parentLI.removeChild(target)
|
||||||
|
parentLI.classList.add("remove-shared")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function updatePreuploadPresentationModal(target) {
|
||||||
|
$.get($(target).data("settings-path"), function(presentation) {
|
||||||
|
if(presentation.attached) {
|
||||||
|
$("#current-presentation").show()
|
||||||
|
$("#presentation-name").text(presentation.name)
|
||||||
|
$("#change-pres").show()
|
||||||
|
$("#use-pres").hide()
|
||||||
|
} else {
|
||||||
|
$("#current-presentation").hide()
|
||||||
|
$("#change-pres").hide()
|
||||||
|
$("#use-pres").show()
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
$("#preuploadPresentationModal form").attr("action", $(target).data("path"))
|
||||||
|
$("#remove-presentation").data("remove", $(target).data("remove"))
|
||||||
|
|
||||||
|
// Reset values to original to prevent confusion
|
||||||
|
$("#presentation-upload").val("")
|
||||||
|
$("#presentation-upload-label").text($("#presentation-upload-label").data("placeholder"))
|
||||||
|
$("#invalid-file-type").hide()
|
||||||
|
}
|
||||||
|
|
||||||
|
function removePreuploadPresentation(path) {
|
||||||
|
$.post(path, {})
|
||||||
|
}
|
||||||
|
|
||||||
|
function validFileUpload(file) {
|
||||||
|
return file.size/1024/1024 <= 30
|
||||||
|
}
|
||||||
|
|
||||||
|
// Automatically click the join button if this is an action cable reload
|
||||||
|
function checkIfAutoJoin() {
|
||||||
|
var url = new URL(window.location.href)
|
||||||
|
|
||||||
|
if (url.searchParams.get("reload") == "true") {
|
||||||
|
$("#joiner-consent").click()
|
||||||
|
$("#room-join").click()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function filterRooms() {
|
||||||
|
let search = $('#room-search').val()
|
||||||
|
|
||||||
|
if (search == undefined) { return }
|
||||||
|
|
||||||
|
let search_term = search.toLowerCase(),
|
||||||
|
rooms = $('#room_block_container > div:not(:last-child)');
|
||||||
|
clear_room_search = $('#clear-room-search');
|
||||||
|
|
||||||
|
if (search_term) {
|
||||||
|
clear_room_search.show();
|
||||||
|
} else {
|
||||||
|
clear_room_search.hide();
|
||||||
|
}
|
||||||
|
|
||||||
|
rooms.each(function(i, room) {
|
||||||
|
let text = $(this).find('h4').text();
|
||||||
|
room.style.display = (text.toLowerCase().indexOf(search_term) < 0) ? 'none' : 'block';
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
function clearRoomSearch() {
|
||||||
|
$('#room-search').val('');
|
||||||
|
filterRooms()
|
||||||
|
}
|
||||||
@@ -0,0 +1,103 @@
|
|||||||
|
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
// terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
// Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
// version.
|
||||||
|
//
|
||||||
|
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
$(document).on('turbolinks:load', function(){
|
||||||
|
var controller = $("body").data('controller');
|
||||||
|
var action = $("body").data('action');
|
||||||
|
|
||||||
|
if ((controller == "admins" && action == "index") ||
|
||||||
|
(controller == "rooms" && action == "show") ||
|
||||||
|
(controller == "rooms" && action == "update") ||
|
||||||
|
(controller == "rooms" && action == "join") ||
|
||||||
|
(controller == "users" && action == "recordings") ||
|
||||||
|
(controller == "admins" && action == "server_recordings") ||
|
||||||
|
(controller == "admins" && action == "server_rooms")) {
|
||||||
|
// Submit search if the user hits enter
|
||||||
|
$("#search-input").keypress(function(key) {
|
||||||
|
if (key.which == 13) {
|
||||||
|
searchPage()
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Add listeners for sort
|
||||||
|
$("th[data-order]").click(function(data){
|
||||||
|
var header_elem = $(data.target)
|
||||||
|
|
||||||
|
if(header_elem.data('order') === 'asc'){ // asc
|
||||||
|
header_elem.data('order', 'desc');
|
||||||
|
}
|
||||||
|
else if(header_elem.data('order') === 'desc'){ // desc
|
||||||
|
header_elem.data('order', 'none');
|
||||||
|
}
|
||||||
|
else{ // none
|
||||||
|
header_elem.data('order', 'asc');
|
||||||
|
}
|
||||||
|
|
||||||
|
var search = $("#search-input").val();
|
||||||
|
|
||||||
|
var url = window.location.pathname + "?page=1&search=" + search + "&column=" + header_elem.data("header") +
|
||||||
|
"&direction=" + header_elem.data('order')
|
||||||
|
|
||||||
|
window.location.replace(addRecordingTable(url))
|
||||||
|
})
|
||||||
|
|
||||||
|
if(controller === "rooms" && action === "show"){
|
||||||
|
$(".page-item > a").each(function(){
|
||||||
|
if(!$(this).attr('href').endsWith("#")){
|
||||||
|
$(this).attr('href', $(this).attr('href') + "#recordings-table")
|
||||||
|
}
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
})
|
||||||
|
|
||||||
|
// Searches the user table for the given string
|
||||||
|
function searchPage() {
|
||||||
|
var search = $("#search-input").val();
|
||||||
|
|
||||||
|
// Check if the user filtered by role
|
||||||
|
var role = new URL(location.href).searchParams.get('role')
|
||||||
|
var tab = new URL(location.href).searchParams.get('tab')
|
||||||
|
|
||||||
|
var url = window.location.pathname + "?page=1&search=" + search
|
||||||
|
|
||||||
|
if (role) { url += "&role=" + role }
|
||||||
|
if (tab) { url += "&tab=" + tab }
|
||||||
|
|
||||||
|
window.location.replace(addRecordingTable(url));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clears the search bar
|
||||||
|
function clearSearch() {
|
||||||
|
var role = new URL(location.href).searchParams.get('role')
|
||||||
|
var tab = new URL(location.href).searchParams.get('tab')
|
||||||
|
|
||||||
|
var url = window.location.pathname + "?page=1"
|
||||||
|
|
||||||
|
if (role) { url += "&role=" + role }
|
||||||
|
if (tab) { url += "&tab=" + tab }
|
||||||
|
|
||||||
|
window.location.replace(addRecordingTable(url));
|
||||||
|
|
||||||
|
var search_params = new URLSearchParams(window.location.search)
|
||||||
|
}
|
||||||
|
|
||||||
|
function addRecordingTable(url) {
|
||||||
|
if($("body").data('controller') === "rooms" && $("body").data('action') === "show") {
|
||||||
|
url += "#recordings-table"
|
||||||
|
}
|
||||||
|
return url
|
||||||
|
}
|
||||||
@@ -0,0 +1,42 @@
|
|||||||
|
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
// terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
// Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
// version.
|
||||||
|
//
|
||||||
|
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// Handle changing of settings tabs.
|
||||||
|
$(document).on('turbolinks:load', function(){
|
||||||
|
var controller = $("body").data('controller');
|
||||||
|
var action = $("body").data('action');
|
||||||
|
|
||||||
|
// Only run on the settings page.
|
||||||
|
if ((controller == "users" && action == "edit") || (controller == "users" && action == "update")){
|
||||||
|
var settingsButtons = $('.setting-btn');
|
||||||
|
var settingsViews = $('.setting-view');
|
||||||
|
|
||||||
|
settingsButtons.each(function(i, btn) {
|
||||||
|
if(!$(btn).hasClass("active")){ $(settingsViews[i]).hide(); }
|
||||||
|
$(btn).click(function(){
|
||||||
|
$(btn).addClass("active");
|
||||||
|
settingsViews.each(function(i, view){
|
||||||
|
if($(view).attr("id") == $(btn).attr("id")){
|
||||||
|
$(view).show();
|
||||||
|
} else {
|
||||||
|
$(settingsButtons[i]).removeClass("active");
|
||||||
|
$(view).hide();
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -1,93 +0,0 @@
|
|||||||
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
// terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
// Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
$.ajaxSetup({
|
|
||||||
headers: {
|
|
||||||
'X-CSRF-Token': $('meta[name="csrf-token"]').attr('content')
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
var loopJoin = function() {
|
|
||||||
var jqxhr = Meeting.getInstance().getJoinMeetingResponse();
|
|
||||||
jqxhr.done(function(data) {
|
|
||||||
if (data.messageKey === 'wait_for_moderator') {
|
|
||||||
setTimeout(loopJoin, 5000);
|
|
||||||
} else {
|
|
||||||
$(location).attr("href", data.response.join_url);
|
|
||||||
}
|
|
||||||
});
|
|
||||||
jqxhr.fail(function(xhr, status, error) {
|
|
||||||
console.info("meeting join failed");
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
var alertTimeout = null;
|
|
||||||
var showAlert = function(html, timeout_delay, type) {
|
|
||||||
type = type || 'success'
|
|
||||||
|
|
||||||
if (!html) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
$('.alert-template .alert-message').html(html);
|
|
||||||
$('.alert-template .alert').removeClass('alert-success alert-danger alert-info alert-warning').addClass('alert-'+type);
|
|
||||||
$('#alerts').html($('.alert-template').html());
|
|
||||||
|
|
||||||
if (timeout_delay) {
|
|
||||||
clearTimeout(alertTimeout);
|
|
||||||
alertTimeout = setTimeout(function() {
|
|
||||||
$('#alerts > .alert').alert('close');
|
|
||||||
}, timeout_delay);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
// remove flash alerts after 5 seconds
|
|
||||||
var flashAlertDelayedDismiss = function() {
|
|
||||||
setTimeout(function(){
|
|
||||||
$('.flash-alerts').remove();
|
|
||||||
}, 5000);
|
|
||||||
};
|
|
||||||
|
|
||||||
var displayRoomURL = function() {
|
|
||||||
$('.meeting-url').val(Meeting.getInstance().getURL());
|
|
||||||
};
|
|
||||||
|
|
||||||
var showNotification = function(title, options) {
|
|
||||||
if (Notification.permission === "granted") {
|
|
||||||
var icon = '<%= asset_path("bbb-logo.png") %>';
|
|
||||||
options = $.extend(options, {
|
|
||||||
icon: icon,
|
|
||||||
tag: 'UserWaiting'
|
|
||||||
});
|
|
||||||
var notification = new Notification(title, options);
|
|
||||||
notification.onclick = function() {
|
|
||||||
window.focus();
|
|
||||||
};
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
var isRoomOwner = function() {
|
|
||||||
return $('body').data('current-user') === $('.page-wrapper.rooms').data('admin-id')
|
|
||||||
}
|
|
||||||
|
|
||||||
// For now there are notifications only for users signed in and when they
|
|
||||||
// are in their room's page
|
|
||||||
$(document).on("turbolinks:load", function() {
|
|
||||||
if (isRoomOwner()) {
|
|
||||||
Notification.requestPermission();
|
|
||||||
}
|
|
||||||
flashAlertDelayedDismiss();
|
|
||||||
});
|
|
||||||
@@ -0,0 +1,105 @@
|
|||||||
|
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
// terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
// Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
// version.
|
||||||
|
//
|
||||||
|
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
$(document).on('turbolinks:load', function(){
|
||||||
|
var controller = $("body").data('controller');
|
||||||
|
var action = $("body").data('action');
|
||||||
|
|
||||||
|
if(controller == "rooms" && action == "show"
|
||||||
|
|| controller == "rooms" && action == "update"
|
||||||
|
|| controller == "users" && action == "recordings"
|
||||||
|
|| controller == "admins" && action == "server_recordings"){
|
||||||
|
|
||||||
|
// Choose active header
|
||||||
|
// (Name, Length or Users)
|
||||||
|
$('th').each(function(){
|
||||||
|
if($(this).data("header")){
|
||||||
|
$(this).on('click', function(){
|
||||||
|
set_active_header($(this).data("header"));
|
||||||
|
sort_by($(this).data("header"), $(this).data('order'));
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
// Based on the header (Name, Length or Users) clicked,
|
||||||
|
// Modify the ui for the tables
|
||||||
|
var set_active_header = function(active_header){
|
||||||
|
$('th').each(function(){
|
||||||
|
if($(this).data("header") == active_header){
|
||||||
|
configure_order($(this));
|
||||||
|
}
|
||||||
|
else{
|
||||||
|
$(this).text($(this).data("header"));
|
||||||
|
$(this).data('order', 'none');
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
// Based on the header (Name, Length or Users) clicked,
|
||||||
|
// Modify the ui for the tables
|
||||||
|
var configure_order = function(header_elem){
|
||||||
|
if(header_elem.data('order') === 'asc'){ // asc
|
||||||
|
header_elem.data('order', 'desc');
|
||||||
|
}
|
||||||
|
else if(header_elem.data('order') === 'desc'){ // desc
|
||||||
|
header_elem.data('order', 'none');
|
||||||
|
}
|
||||||
|
else{ // none
|
||||||
|
header_elem.data('order', 'asc');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Given a label and an order, sort recordings by order
|
||||||
|
// under a given label
|
||||||
|
var sort_by = function(label, order){
|
||||||
|
var recording_list_tbody = $('.table-responsive').find('tbody');
|
||||||
|
if(label === "Name"){
|
||||||
|
sort_recordings(recording_list_tbody, order, "#recording-title");
|
||||||
|
}
|
||||||
|
else if(label === "Length"){
|
||||||
|
sort_recordings(recording_list_tbody, order, "#recording-length");
|
||||||
|
}
|
||||||
|
else if(label === "Users"){
|
||||||
|
sort_recordings(recording_list_tbody, order, "#recording-users");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Generalized function for sorting recordings
|
||||||
|
var sort_recordings = function(recording_list_tbody, order, recording_id){
|
||||||
|
recording_list_tbody.find('tr').sort(function(a, b){
|
||||||
|
var a_val, b_val;
|
||||||
|
if (recording_id == "#recording-length") {
|
||||||
|
a_val = $.trim($(a).find(recording_id).data("full-length"));
|
||||||
|
b_val = $.trim($(b).find(recording_id).data("full-length"));
|
||||||
|
} else {
|
||||||
|
a_val = $.trim($(a).find(recording_id).text());
|
||||||
|
b_val = $.trim($(b).find(recording_id).text());
|
||||||
|
}
|
||||||
|
|
||||||
|
if(order === "asc"){
|
||||||
|
return a_val.localeCompare(b_val);
|
||||||
|
}
|
||||||
|
else if(order === "desc"){
|
||||||
|
return b_val.localeCompare(a_val);
|
||||||
|
} else {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}).appendTo(recording_list_tbody);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
@@ -0,0 +1,46 @@
|
|||||||
|
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
// terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
// Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
// version.
|
||||||
|
//
|
||||||
|
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
$(document).on('turbolinks:load', function(){
|
||||||
|
var controller = $("body").data('controller');
|
||||||
|
var action = $("body").data('action');
|
||||||
|
if ((controller == "admins" && action == "edit_user") || (controller == "users" && action == "edit")) {
|
||||||
|
// Hack to make it play nice with turbolinks
|
||||||
|
if ($("#role-dropdown:visible").length == 0){
|
||||||
|
$(window).trigger('load.bs.select.data-api')
|
||||||
|
}
|
||||||
|
|
||||||
|
// Check to see if the role dropdown was set up
|
||||||
|
if ($("#role-dropdown").length != 0){
|
||||||
|
$("#role-dropdown").selectpicker('val', $("#user_role_id").val())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Update hidden field with new value
|
||||||
|
$("#role-dropdown").on("changed.bs.select", function(){
|
||||||
|
$("#user_role_id").val($("#role-dropdown").selectpicker('val'))
|
||||||
|
})
|
||||||
|
|
||||||
|
// Update hidden field with new value
|
||||||
|
// $("#language-dropdown").on("show.bs.select", function(){
|
||||||
|
// $("#language-dropdown").selectpicker('val', $("#user_language").val())
|
||||||
|
// })
|
||||||
|
|
||||||
|
// Update hidden field with new value
|
||||||
|
$("#language-dropdown").on("changed.bs.select", function(){
|
||||||
|
$("#user_language").val($("#language-dropdown").selectpicker('val'))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
})
|
||||||
@@ -0,0 +1,67 @@
|
|||||||
|
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
// terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
// Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
// version.
|
||||||
|
//
|
||||||
|
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
// Handle client request to join when meeting starts.
|
||||||
|
$(document).on("turbolinks:load", function(){
|
||||||
|
var controller = $("body").data('controller');
|
||||||
|
var action = $("body").data('action');
|
||||||
|
|
||||||
|
if(controller == "rooms" && action == "join"){
|
||||||
|
App.waiting = App.cable.subscriptions.create({
|
||||||
|
channel: "WaitingChannel",
|
||||||
|
roomuid: $(".background").attr("room"),
|
||||||
|
useruid: $(".background").attr("user")
|
||||||
|
}, {
|
||||||
|
connected: function() {
|
||||||
|
console.log("connected");
|
||||||
|
setTimeout(startRefreshTimeout, 120000);
|
||||||
|
},
|
||||||
|
|
||||||
|
disconnected: function(data) {
|
||||||
|
console.log("disconnected");
|
||||||
|
console.log(data);
|
||||||
|
},
|
||||||
|
|
||||||
|
rejected: function() {
|
||||||
|
console.log("rejected");
|
||||||
|
},
|
||||||
|
|
||||||
|
received: function(data){
|
||||||
|
console.log(data);
|
||||||
|
if(data.action == "started"){
|
||||||
|
request_to_join_meeting();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
});
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
var join_attempts = 0;
|
||||||
|
|
||||||
|
function request_to_join_meeting() {
|
||||||
|
$.post(window.location.pathname, { join_name: $(".background").attr("join-name") }, function() {
|
||||||
|
//Successful post - set up retry incase
|
||||||
|
if(join_attempts < 4){ setTimeout(request_to_join_meeting, 10000); }
|
||||||
|
join_attempts++;
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
// Refresh the page after 2 mins and attempt to reconnect to ActionCable
|
||||||
|
function startRefreshTimeout() {
|
||||||
|
var url = new URL(window.location.href)
|
||||||
|
url.searchParams.set("reload","true")
|
||||||
|
window.location.href = url.href
|
||||||
|
}
|
||||||
@@ -0,0 +1,81 @@
|
|||||||
|
/* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
* Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
* version.
|
||||||
|
*
|
||||||
|
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
@import "tabler/functions";
|
||||||
|
|
||||||
|
@import "tabler/core";
|
||||||
|
@import "tabler/type";
|
||||||
|
@import "tabler/grid";
|
||||||
|
|
||||||
|
@import "tabler/layout";
|
||||||
|
@import "tabler/aside";
|
||||||
|
@import "tabler/header";
|
||||||
|
@import "tabler/footer";
|
||||||
|
@import "tabler/colors";
|
||||||
|
@import "tabler/text";
|
||||||
|
@import "tabler/utilities";
|
||||||
|
|
||||||
|
@import "tabler/nav";
|
||||||
|
@import "tabler/button";
|
||||||
|
@import "tabler/alert";
|
||||||
|
//@import "tabler/close";
|
||||||
|
//@import "tabler/badge";
|
||||||
|
@import "tabler/tables";
|
||||||
|
//@import "tabler/breadcrumb";
|
||||||
|
//@import "tabler/pagination";
|
||||||
|
@import "tabler/cards";
|
||||||
|
//@import "tabler/popover";
|
||||||
|
@import "tabler/dropdown";
|
||||||
|
@import "tabler/list";
|
||||||
|
@import "tabler/list-group";
|
||||||
|
@import "tabler/avatar";
|
||||||
|
//@import "tabler/product";
|
||||||
|
@import "tabler/progress";
|
||||||
|
@import "tabler/icon";
|
||||||
|
//@import "tabler/image";
|
||||||
|
//@import "tabler/link";
|
||||||
|
//@import "tabler/media";
|
||||||
|
@import "tabler/form";
|
||||||
|
//@import "tabler/sparkline";
|
||||||
|
@import "tabler/social";
|
||||||
|
//@import "tabler/maps";
|
||||||
|
//@import "tabler/statuses";
|
||||||
|
//@import "tabler/charts";
|
||||||
|
//@import "tabler/chips";
|
||||||
|
@import "tabler/stamp";
|
||||||
|
//@import "tabler/chat";
|
||||||
|
//@import "tabler/example";
|
||||||
|
@import "tabler/tag";
|
||||||
|
//@import "tabler/syntax";
|
||||||
|
//@import "tabler/infobox";
|
||||||
|
//@import "tabler/carousel";
|
||||||
|
|
||||||
|
//@import "tabler/forms/custom-range";
|
||||||
|
//@import "tabler/forms/custom-selectgroup";
|
||||||
|
@import "tabler/forms/custom-switch";
|
||||||
|
//@import "tabler/forms/custom-imagecheck";
|
||||||
|
@import "tabler/forms/custom-colorinput";
|
||||||
|
|
||||||
|
//@import "tabler/timeline";
|
||||||
|
|
||||||
|
//@import "tabler/browser";
|
||||||
|
//@import "tabler/flag";
|
||||||
|
//@import "tabler/payments";
|
||||||
|
//@import "tabler/jvectormap";
|
||||||
|
//@import "tabler/selectize";
|
||||||
|
|
||||||
|
//@import "tabler/fonts/feather";
|
||||||
@@ -0,0 +1,101 @@
|
|||||||
|
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
//
|
||||||
|
// Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
//
|
||||||
|
// This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
// terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
// Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
// version.
|
||||||
|
//
|
||||||
|
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
//
|
||||||
|
// You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
#users-table {
|
||||||
|
.user-role {
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-email {
|
||||||
|
max-width: 250px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#clear-search {
|
||||||
|
z-index: 9;
|
||||||
|
position: absolute;
|
||||||
|
right: 55px;
|
||||||
|
top: 8px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.tag i {
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#branding-image{
|
||||||
|
z-index: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.authentication-required{
|
||||||
|
padding-top: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#site_settings {
|
||||||
|
.colorinput-color {
|
||||||
|
text-align: center;
|
||||||
|
padding-top: 4px;
|
||||||
|
height: 2rem;
|
||||||
|
width: 2rem;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sort-disabled{
|
||||||
|
background: #e6e6e6 !important;
|
||||||
|
color: rgb(110, 118, 135) !important;
|
||||||
|
opacity: 0.75;
|
||||||
|
&:hover{
|
||||||
|
opacity: 0.9;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.form-disable{
|
||||||
|
background-color: #e6e6e6;
|
||||||
|
}
|
||||||
|
|
||||||
|
.role-colour-picker{
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.custom-role-tag{
|
||||||
|
color: white !important;
|
||||||
|
// Make it consistent with the manage users tab tags
|
||||||
|
padding-top: 1px;
|
||||||
|
padding-bottom: 1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.user-role-tag{
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
#manage-users-nav.nav-tabs .nav-item {
|
||||||
|
margin-bottom: -1px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#merge-account-arrow {
|
||||||
|
position: absolute;
|
||||||
|
top: 47%;
|
||||||
|
right: 47%;
|
||||||
|
z-index: 999;
|
||||||
|
background: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
.admin-tabs {
|
||||||
|
justify-content: space-around;
|
||||||
|
}
|
||||||
@@ -1,34 +0,0 @@
|
|||||||
/* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
*
|
|
||||||
* Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
*
|
|
||||||
* This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
* terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
* Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
* version.
|
|
||||||
*
|
|
||||||
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
*
|
|
||||||
* You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
*/
|
|
||||||
|
|
||||||
/*
|
|
||||||
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
|
||||||
* listed below.
|
|
||||||
*
|
|
||||||
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
|
||||||
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
|
||||||
*
|
|
||||||
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
|
||||||
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
|
||||||
* files in this directory. Styles in this file should be added after the last require_* statement.
|
|
||||||
* It is generally better to create a new file per style scope.
|
|
||||||
*
|
|
||||||
*= require jquery-ui
|
|
||||||
*= require dataTables/bootstrap/3/jquery.dataTables.bootstrap
|
|
||||||
*= require_tree ./main
|
|
||||||
*= require_self
|
|
||||||
*/
|
|
||||||
@@ -0,0 +1,189 @@
|
|||||||
|
/* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
* Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
* version.
|
||||||
|
*
|
||||||
|
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
/*
|
||||||
|
* This is a manifest file that'll be compiled into application.css, which will include all the files
|
||||||
|
* listed below.
|
||||||
|
*
|
||||||
|
* Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets,
|
||||||
|
* or any plugin's vendor/assets/stylesheets directory can be referenced here using a relative path.
|
||||||
|
*
|
||||||
|
* You're free to add application-wide styles to this file and they'll appear at the bottom of the
|
||||||
|
* compiled file so the styles you add here take precedence over styles defined in any other CSS/SCSS
|
||||||
|
* files in this directory. Styles in this file should be added after the last require_* statement.
|
||||||
|
* It is generally better to create a new file per style scope.
|
||||||
|
*
|
||||||
|
*= require_self
|
||||||
|
*/
|
||||||
|
|
||||||
|
@import "tabler/variables";
|
||||||
|
@import "bootstrap";
|
||||||
|
@import "jquery-ui/sortable";
|
||||||
|
@import "tabler-custom";
|
||||||
|
@import "font-awesome-sprockets";
|
||||||
|
@import "font-awesome";
|
||||||
|
@import "monolith.min.scss";
|
||||||
|
@import "bootstrap-select.min";
|
||||||
|
|
||||||
|
@import "utilities/variables";
|
||||||
|
@import "admins";
|
||||||
|
@import "main";
|
||||||
|
@import "rooms";
|
||||||
|
@import "sessions";
|
||||||
|
@import "utilities/fonts";
|
||||||
|
@import "users";
|
||||||
|
|
||||||
|
* {
|
||||||
|
outline: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
html, body {
|
||||||
|
font-family: "Source Sans Pro" !important;
|
||||||
|
position: relative;
|
||||||
|
width: 100%;
|
||||||
|
height: 100%;
|
||||||
|
background-color: white;
|
||||||
|
}
|
||||||
|
|
||||||
|
a {
|
||||||
|
text-decoration: none !important;
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
.dotted_underline {
|
||||||
|
border-bottom: 3px dashed #d3d3d3;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.disable-click {
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header {
|
||||||
|
height: $header-height;
|
||||||
|
}
|
||||||
|
|
||||||
|
.wrapper {
|
||||||
|
position: relative;
|
||||||
|
height: auto;
|
||||||
|
min-height: calc(100% - #{$header-height} - #{$footer-height});
|
||||||
|
}
|
||||||
|
|
||||||
|
.footer {
|
||||||
|
height: $footer-height;
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.background {
|
||||||
|
background-color: $background-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.error-section {
|
||||||
|
background-color: $error-background-color;
|
||||||
|
}
|
||||||
|
|
||||||
|
.font-weight-400 {
|
||||||
|
font-weight: 400;
|
||||||
|
}
|
||||||
|
|
||||||
|
.subtitle {
|
||||||
|
font-size: 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.force-bottom {
|
||||||
|
display: flex;
|
||||||
|
justify-content: flex-end;
|
||||||
|
flex-direction: column;
|
||||||
|
}
|
||||||
|
|
||||||
|
.invite-link-input {
|
||||||
|
width: 100%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.no-border-top {
|
||||||
|
td {
|
||||||
|
border-top: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.force-text-normal {
|
||||||
|
color: #495057;
|
||||||
|
}
|
||||||
|
|
||||||
|
.terms {
|
||||||
|
overflow: scroll;
|
||||||
|
height: 55vh;
|
||||||
|
}
|
||||||
|
|
||||||
|
[contenteditable]:focus {
|
||||||
|
outline: 0px solid transparent;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cookies-banner {
|
||||||
|
color: white;
|
||||||
|
background-color: $button-color-blue;
|
||||||
|
#cookies-agree-button {
|
||||||
|
margin: 0;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
input:focus {
|
||||||
|
border-color: $primary !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.input-group button:focus {
|
||||||
|
box-shadow: none !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.list-group-item-action.active {
|
||||||
|
color: $primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
.header .header-nav {
|
||||||
|
color: $text-muted !important;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
padding-bottom: 21px;
|
||||||
|
border-bottom: 1px solid $primary;
|
||||||
|
}
|
||||||
|
|
||||||
|
&.active {
|
||||||
|
color: $primary !important;
|
||||||
|
padding-bottom: 21px;
|
||||||
|
border-bottom: 1px solid $primary;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
table {
|
||||||
|
thead {
|
||||||
|
th[data-order]:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.cursor-pointer{
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
|
||||||
|
#delete-confirm:disabled {
|
||||||
|
cursor: not-allowed;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn i {
|
||||||
|
transition: all .15s;
|
||||||
|
}
|
||||||
@@ -0,0 +1,20 @@
|
|||||||
|
/* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
* Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
* version.
|
||||||
|
*
|
||||||
|
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Place all the styles related to the Errors controller here.
|
||||||
|
// They will automatically be included in application.css.
|
||||||
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||||
@@ -1,18 +0,0 @@
|
|||||||
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
// terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
// Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
@import "font-awesome-sprockets";
|
|
||||||
@import "font-awesome";
|
|
||||||
@@ -0,0 +1,178 @@
|
|||||||
|
/* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
* Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
* version.
|
||||||
|
*
|
||||||
|
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Place all the styles related to the Main controller here.
|
||||||
|
// They will automatically be included in application.css.
|
||||||
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||||
|
|
||||||
|
.responsive-header {
|
||||||
|
font-size: 3.3vw;
|
||||||
|
}
|
||||||
|
|
||||||
|
.landing-section {
|
||||||
|
position: relative;
|
||||||
|
height: 40%;
|
||||||
|
}
|
||||||
|
|
||||||
|
.lead {
|
||||||
|
font-size:18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.feature-stamp {
|
||||||
|
.stamp {
|
||||||
|
padding:1em 1.5em;
|
||||||
|
height:auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.or-line {
|
||||||
|
div {
|
||||||
|
width: 100%;
|
||||||
|
height: 16px;
|
||||||
|
border-bottom: 1px solid #d3d3d3;
|
||||||
|
text-align: center;
|
||||||
|
span {
|
||||||
|
font-size: 120%;
|
||||||
|
background-color: #ffffff;
|
||||||
|
padding: 0 10px;
|
||||||
|
color: #d3d3d3
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.display-4 {
|
||||||
|
font-weight: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customBtn {
|
||||||
|
display: block;
|
||||||
|
text-align: center;
|
||||||
|
background: #cccccc;
|
||||||
|
color: #ffffff;
|
||||||
|
box-shadow: 0 2px 4px 0 rgba(0,0,0,.25);
|
||||||
|
-webkit-transition: background-color .218s,border-color .218s,box-shadow .218s;
|
||||||
|
transition: background-color .218s,border-color .218s,box-shadow .218s;
|
||||||
|
white-space: nowrap;
|
||||||
|
border-radius: 2px;
|
||||||
|
border: 1px solid transparent;
|
||||||
|
margin-bottom:20px;
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
color: #ffffff;
|
||||||
|
-webkit-box-shadow: 0 0 3px 3px rgba(66,133,244,.3);
|
||||||
|
box-shadow: 0 0 3px 3px rgba(66,133,244,.3);
|
||||||
|
}
|
||||||
|
|
||||||
|
.customBtn-icon {
|
||||||
|
display: inline-block;
|
||||||
|
vertical-align: middle;
|
||||||
|
padding:13px 15px 13px 15px;
|
||||||
|
float:left;
|
||||||
|
background: #ffffff;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customBtn-text {
|
||||||
|
font-size: 19px;
|
||||||
|
line-height: 48px;
|
||||||
|
font-weight: 600;
|
||||||
|
letter-spacing: .21px;
|
||||||
|
padding:0 25px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.customBtn-image {
|
||||||
|
background: #ffffff;
|
||||||
|
background-size: 18px 18px;
|
||||||
|
padding:10px 10px 10px 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.customBtn-google {
|
||||||
|
@extend .customBtn;
|
||||||
|
background: #4688f1;
|
||||||
|
|
||||||
|
.customBtn-image {
|
||||||
|
background: #ffffff image-url("google-logo.png") no-repeat left top;
|
||||||
|
background-size: 18px 18px;
|
||||||
|
padding:10px 10px 10px 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.customBtn-twitter {
|
||||||
|
@extend .customBtn;
|
||||||
|
background: #1da1f2;
|
||||||
|
|
||||||
|
.customBtn-image {
|
||||||
|
background: #ffffff image-url("twitter-logo.png") no-repeat left top;
|
||||||
|
background-size: 18px 18px;
|
||||||
|
padding:10px 10px 10px 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.customBtn-office365 {
|
||||||
|
@extend .customBtn;
|
||||||
|
background: #f65314;
|
||||||
|
|
||||||
|
.customBtn-image {
|
||||||
|
background: #ffffff image-url("office365-logo.jpeg") no-repeat left top;
|
||||||
|
background-size: 18px 18px;
|
||||||
|
padding:10px 10px 10px 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.customBtn-microsoft_windows {
|
||||||
|
@extend .customBtn;
|
||||||
|
background: #00a1f1;
|
||||||
|
|
||||||
|
.customBtn-image {
|
||||||
|
background: #ffffff image-url("windows-logo.png") no-repeat left top;
|
||||||
|
background-size: 18px 18px;
|
||||||
|
padding:10px 10px 10px 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.customBtn-ldap {
|
||||||
|
@extend .customBtn;
|
||||||
|
background: #d61515;
|
||||||
|
|
||||||
|
.customBtn-image {
|
||||||
|
background: #ffffff image-url("ldap-logo.png") no-repeat left top;
|
||||||
|
background-size: 18px 18px;
|
||||||
|
padding:10px 10px 10px 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.customBtn-openid_connect {
|
||||||
|
@extend .customBtn;
|
||||||
|
background: #ef8e1f;
|
||||||
|
|
||||||
|
.customBtn-image {
|
||||||
|
background: #ffffff image-url("openid-logo.png") no-repeat left top;
|
||||||
|
background-size: 18px 18px;
|
||||||
|
padding:10px 10px 10px 10px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.signin-button {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.table-responsive tbody td:first-child > *:first-child {
|
||||||
|
max-height: 3em;
|
||||||
|
overflow: hidden;
|
||||||
|
max-width: 200px;
|
||||||
|
display: flex;
|
||||||
|
}
|
||||||
@@ -1,15 +0,0 @@
|
|||||||
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
// terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
// Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
@@ -1,133 +0,0 @@
|
|||||||
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
// terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
// Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
.meeting-url-button-group {
|
|
||||||
padding-top: 5px;
|
|
||||||
width: 150px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.meeting-url-qrcode-group {
|
|
||||||
padding-top: 5px;
|
|
||||||
width: 128px;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.previously-joined, .actives {
|
|
||||||
list-style-type: none;
|
|
||||||
margin:auto;
|
|
||||||
width: 200px;
|
|
||||||
padding: 0;
|
|
||||||
cursor: pointer;
|
|
||||||
}
|
|
||||||
|
|
||||||
.meetings {
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
.rooms {
|
|
||||||
|
|
||||||
.table-wrapper {
|
|
||||||
padding: 40px 50px 10px 50px;
|
|
||||||
|
|
||||||
#recordings {
|
|
||||||
thead {
|
|
||||||
th:after {
|
|
||||||
content: none; //removes the sort icon in table header
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.dataTables_empty {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
.timeago {
|
|
||||||
margin-left: 5px;
|
|
||||||
font-size: 13px;
|
|
||||||
cursor: pointer;
|
|
||||||
color: #999;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.img-thumbnail{
|
|
||||||
padding: 4px;
|
|
||||||
background-color: white;
|
|
||||||
border: 1px solid #dddddd;
|
|
||||||
border-radius: 4px;
|
|
||||||
|
|
||||||
&.large{
|
|
||||||
display: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
&:hover + &.large{
|
|
||||||
display: inline-block;
|
|
||||||
position: absolute;
|
|
||||||
z-index: 1;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.meeting-url-group {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.recording-update-trigger {
|
|
||||||
&.recording-unpublished {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.recording-unlisted {
|
|
||||||
color: #e3a91e;
|
|
||||||
}
|
|
||||||
|
|
||||||
&.recording-published {
|
|
||||||
color: green;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.fa-spinner {
|
|
||||||
width: 100%;
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
|
|
||||||
.disabled-button {
|
|
||||||
opacity: 0.5;
|
|
||||||
}
|
|
||||||
|
|
||||||
.youtube-red {
|
|
||||||
color: red;
|
|
||||||
}
|
|
||||||
|
|
||||||
.cloud-blue {
|
|
||||||
color: cornflowerblue;
|
|
||||||
}
|
|
||||||
|
|
||||||
.green-check {
|
|
||||||
color: limegreen;
|
|
||||||
}
|
|
||||||
|
|
||||||
.top-buffer {
|
|
||||||
margin-top: 8px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tooltip-wrapper {
|
|
||||||
display: inline-block;
|
|
||||||
}
|
|
||||||
|
|
||||||
#youtube-footer{
|
|
||||||
font-size: 10px;
|
|
||||||
text-align: center;
|
|
||||||
margin-top: 10px;
|
|
||||||
}
|
|
||||||
@@ -1,187 +0,0 @@
|
|||||||
// BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
//
|
|
||||||
// Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
//
|
|
||||||
// This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
// terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
// Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
// version.
|
|
||||||
//
|
|
||||||
// BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
// WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
// PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
//
|
|
||||||
// You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
// with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
@import "bootstrap-sprockets";
|
|
||||||
@import "bootstrap";
|
|
||||||
@import "bootstrap-social";
|
|
||||||
|
|
||||||
|
|
||||||
html, body {
|
|
||||||
width: 100%;
|
|
||||||
height: 100%;
|
|
||||||
background: #ffffff;
|
|
||||||
}
|
|
||||||
|
|
||||||
.container-fluid {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.background {
|
|
||||||
height: 350px;
|
|
||||||
width: 100%;
|
|
||||||
background-position: center;
|
|
||||||
background-size: cover;
|
|
||||||
background-repeat: no-repeat;
|
|
||||||
}
|
|
||||||
|
|
||||||
body[data-controller=landing].app-background {
|
|
||||||
@extend .background;
|
|
||||||
}
|
|
||||||
|
|
||||||
#alerts {
|
|
||||||
position: absolute;
|
|
||||||
top: 10px;
|
|
||||||
left: 50%;
|
|
||||||
margin-left: -250px;
|
|
||||||
width: 500px;
|
|
||||||
z-index: 999;
|
|
||||||
}
|
|
||||||
|
|
||||||
.header {
|
|
||||||
padding: 20px 40px;
|
|
||||||
margin-bottom: 160px;
|
|
||||||
.logo {
|
|
||||||
max-width: 150px;
|
|
||||||
max-height: 50px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.footer {
|
|
||||||
padding: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.center-block {
|
|
||||||
float: none;
|
|
||||||
}
|
|
||||||
.center-panel-wrapper {
|
|
||||||
height: 100%;
|
|
||||||
}
|
|
||||||
.center-panel {
|
|
||||||
height: 100%;
|
|
||||||
.center-panel-size {
|
|
||||||
max-width: 1200px
|
|
||||||
}
|
|
||||||
.center-panel-content-size {
|
|
||||||
height: 100%;
|
|
||||||
max-width: 1100px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel {
|
|
||||||
position: relative;
|
|
||||||
overflow: hidden;
|
|
||||||
}
|
|
||||||
|
|
||||||
.input-spacing {
|
|
||||||
margin-top: 15px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel-footer {
|
|
||||||
background-color: white;
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel-body {
|
|
||||||
padding-bottom: 40px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.title-wrapper {
|
|
||||||
margin-bottom: 30px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.join-form-wrapper {
|
|
||||||
.center-block {
|
|
||||||
max-width: 400px;
|
|
||||||
}
|
|
||||||
.join-form {
|
|
||||||
width: 100%;
|
|
||||||
}
|
|
||||||
.btn {
|
|
||||||
width: initial;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.meeting-url-wrapper {
|
|
||||||
.meeting-url {
|
|
||||||
cursor: default;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.loading-wrapper {
|
|
||||||
text-align: center;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.popover {
|
|
||||||
max-width: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
a.signin-link {
|
|
||||||
&:hover, &:focus {
|
|
||||||
cursor: pointer;
|
|
||||||
text-decoration: none;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.signin-link {
|
|
||||||
.signin-icon {
|
|
||||||
vertical-align: middle;
|
|
||||||
width: 35px;
|
|
||||||
height: 35px;
|
|
||||||
}
|
|
||||||
.signin-button {
|
|
||||||
background: white;
|
|
||||||
width: 250px;
|
|
||||||
border: thin solid #888;
|
|
||||||
border-radius: 2px;
|
|
||||||
white-space: nowrap;
|
|
||||||
padding: 5px;
|
|
||||||
margin-bottom: 14px;
|
|
||||||
}
|
|
||||||
.signin-icon-wrapper {
|
|
||||||
display: inline-block;
|
|
||||||
width: 40px;
|
|
||||||
}
|
|
||||||
.signin-text-wrapper {
|
|
||||||
display: inline-block;
|
|
||||||
width: 200px;
|
|
||||||
}
|
|
||||||
.signin-text {
|
|
||||||
font-family: 'Roboto', sans-serif;
|
|
||||||
vertical-align: middle;
|
|
||||||
font-size: 14px;
|
|
||||||
font-weight: bold;
|
|
||||||
color: #444;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.verticle-line {
|
|
||||||
// parent must be position relative to work
|
|
||||||
width: 1px;
|
|
||||||
background-color: lightgray;
|
|
||||||
height: 100%;
|
|
||||||
position: absolute;
|
|
||||||
left: 50%;
|
|
||||||
}
|
|
||||||
|
|
||||||
.invite-join-wrapper {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.help-link {
|
|
||||||
position: absolute;
|
|
||||||
top: 0;
|
|
||||||
right: 0;
|
|
||||||
padding-right: 3px;
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,146 @@
|
|||||||
|
/* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
* Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
* version.
|
||||||
|
*
|
||||||
|
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Place all the styles related to the Rooms controller here.
|
||||||
|
// They will automatically be included in application.css.
|
||||||
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||||
|
|
||||||
|
.start-button {
|
||||||
|
font-size: 26px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.thumbnail {
|
||||||
|
height: 60px !important;
|
||||||
|
width: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.join-form {
|
||||||
|
font-size: 20px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.join-input {
|
||||||
|
height: 48px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.home-indicator {
|
||||||
|
font-size: 22px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.btn-del-room {
|
||||||
|
width: 70% !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit_hover_class a{
|
||||||
|
visibility: hidden;
|
||||||
|
}
|
||||||
|
|
||||||
|
.edit_hover_class:hover a {
|
||||||
|
visibility: visible;
|
||||||
|
}
|
||||||
|
|
||||||
|
#room-settings-dropdown-label {
|
||||||
|
vertical-align: middle;
|
||||||
|
padding-top: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.room-block {
|
||||||
|
&:not(.current) {
|
||||||
|
.stamp {
|
||||||
|
opacity: 0.5;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#create-room-block {
|
||||||
|
border: 1px dashed lightgray;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
background-color: rgba(0, 0, 0, 0.04);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.allow-icon-click{
|
||||||
|
pointer-events: auto;
|
||||||
|
}
|
||||||
|
|
||||||
|
.cant-create-rooms-title{
|
||||||
|
align-items: center;
|
||||||
|
justify-content: center;
|
||||||
|
}
|
||||||
|
|
||||||
|
.avatar-xxxl{
|
||||||
|
width: 8rem;
|
||||||
|
height: 8rem;
|
||||||
|
line-height: 8rem;
|
||||||
|
max-width: 8rem;
|
||||||
|
margin-top: -6rem;
|
||||||
|
font-size: 5rem;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bootstrap-select .dropdown-menu li.active small.text-muted{
|
||||||
|
color: #9aa0ac !important
|
||||||
|
}
|
||||||
|
|
||||||
|
.not-saved {
|
||||||
|
color: grey;
|
||||||
|
background: rgba(0, 40, 100, 0.12);
|
||||||
|
}
|
||||||
|
|
||||||
|
.dropdown-menu.show {
|
||||||
|
min-height: 0px !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.remove-shared {
|
||||||
|
text-decoration: line-through;
|
||||||
|
}
|
||||||
|
|
||||||
|
.enabled-setting {
|
||||||
|
background: lightgray;
|
||||||
|
pointer-events: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
#recording-table .edit_hover_class {
|
||||||
|
word-break: break-word;
|
||||||
|
white-space: normal;
|
||||||
|
}
|
||||||
|
|
||||||
|
#room-owner-name {
|
||||||
|
line-height: 12px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.create-room-button {
|
||||||
|
width: 49%;
|
||||||
|
}
|
||||||
|
|
||||||
|
#presentation-upload-label {
|
||||||
|
overflow: hidden;
|
||||||
|
white-space: nowrap;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
padding-right: 75px;
|
||||||
|
}
|
||||||
|
|
||||||
|
#clear-room-search {
|
||||||
|
z-index: 9;
|
||||||
|
position: absolute;
|
||||||
|
right: 15px;
|
||||||
|
top: 8px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
cursor: pointer;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,66 @@
|
|||||||
|
/* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
* Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
* version.
|
||||||
|
*
|
||||||
|
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Place all the styles related to the Sessions controller here.
|
||||||
|
// They will automatically be included in application.css.
|
||||||
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||||
|
|
||||||
|
.login {
|
||||||
|
.center-panel {
|
||||||
|
.center-panel-size {
|
||||||
|
max-width: 400px;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
a.signin-link {
|
||||||
|
&:hover, &:focus {
|
||||||
|
cursor: pointer;
|
||||||
|
text-decoration: none;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.signin-link {
|
||||||
|
.signin-icon {
|
||||||
|
vertical-align: middle;
|
||||||
|
width: 35px;
|
||||||
|
height: 35px;
|
||||||
|
}
|
||||||
|
.signin-button {
|
||||||
|
background: white;
|
||||||
|
width: 250px;
|
||||||
|
border: thin solid #888;
|
||||||
|
border-radius: 2px;
|
||||||
|
white-space: nowrap;
|
||||||
|
padding: 5px;
|
||||||
|
margin-bottom: 14px;
|
||||||
|
}
|
||||||
|
.signin-icon-wrapper {
|
||||||
|
display: inline-block;
|
||||||
|
width: 40px;
|
||||||
|
}
|
||||||
|
.signin-text-wrapper {
|
||||||
|
display: inline-block;
|
||||||
|
width: 200px;
|
||||||
|
}
|
||||||
|
.signin-text {
|
||||||
|
vertical-align: middle;
|
||||||
|
font-size: 14px;
|
||||||
|
font-weight: bold;
|
||||||
|
color: #444;
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,32 @@
|
|||||||
|
/* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
* Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
* version.
|
||||||
|
*
|
||||||
|
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
// Place all the styles related to the Users controller here.
|
||||||
|
// They will automatically be included in application.css.
|
||||||
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||||
|
|
||||||
|
.user-role-tag{
|
||||||
|
color: white !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
.shared-user {
|
||||||
|
line-height: 30px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.bootstrap-select {
|
||||||
|
border: 1px solid rgba(0, 40, 100, 0.12);
|
||||||
|
}
|
||||||
@@ -0,0 +1,504 @@
|
|||||||
|
/* cyrillic-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-LightItalic.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||||
|
}
|
||||||
|
/* cyrillic */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-LightItalic.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||||
|
}
|
||||||
|
/* greek-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-LightItalic.ttf')) format('truetype');
|
||||||
|
unicode-range: U+1F00-1FFF;
|
||||||
|
}
|
||||||
|
/* greek */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-LightItalic.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0370-03FF;
|
||||||
|
}
|
||||||
|
/* vietnamese */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-LightItalic.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
||||||
|
}
|
||||||
|
/* latin-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-LightItalic.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||||
|
}
|
||||||
|
/* latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 300;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-LightItalic.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
/* cyrillic-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-Italic.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||||
|
}
|
||||||
|
/* cyrillic */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-Italic.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||||
|
}
|
||||||
|
/* greek-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-Italic.ttf')) format('truetype');
|
||||||
|
unicode-range: U+1F00-1FFF;
|
||||||
|
}
|
||||||
|
/* greek */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-Italic.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0370-03FF;
|
||||||
|
}
|
||||||
|
/* vietnamese */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-Italic.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
||||||
|
}
|
||||||
|
/* latin-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-Italic.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||||
|
}
|
||||||
|
/* latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-Italic.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
/* cyrillic-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 600;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBoldItalic.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||||
|
}
|
||||||
|
/* cyrillic */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 600;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBoldItalic.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||||
|
}
|
||||||
|
/* greek-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 600;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBoldItalic.ttf')) format('truetype');
|
||||||
|
unicode-range: U+1F00-1FFF;
|
||||||
|
}
|
||||||
|
/* greek */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 600;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBoldItalic.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0370-03FF;
|
||||||
|
}
|
||||||
|
/* vietnamese */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 600;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBoldItalic.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
||||||
|
}
|
||||||
|
/* latin-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 600;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBoldItalic.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||||
|
}
|
||||||
|
/* latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 600;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBoldItalic.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
/* cyrillic-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 700;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-BoldItalic.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||||
|
}
|
||||||
|
/* cyrillic */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 700;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-BoldItalic.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||||
|
}
|
||||||
|
/* greek-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 700;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-BoldItalic.ttf')) format('truetype');
|
||||||
|
unicode-range: U+1F00-1FFF;
|
||||||
|
}
|
||||||
|
/* greek */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 700;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-BoldItalic.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0370-03FF;
|
||||||
|
}
|
||||||
|
/* vietnamese */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 700;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-BoldItalic.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
||||||
|
}
|
||||||
|
/* latin-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 700;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-BoldItalic.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||||
|
}
|
||||||
|
/* latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: italic;
|
||||||
|
font-weight: 700;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-BoldItalic.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
/* cyrillic-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 300;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-Light.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||||
|
}
|
||||||
|
/* cyrillic */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 300;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-Light.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||||
|
}
|
||||||
|
/* greek-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 300;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-Light.ttf')) format('truetype');
|
||||||
|
unicode-range: U+1F00-1FFF;
|
||||||
|
}
|
||||||
|
/* greek */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 300;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-Light.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0370-03FF;
|
||||||
|
}
|
||||||
|
/* vietnamese */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 300;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-Light.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
||||||
|
}
|
||||||
|
/* latin-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 300;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-Light.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||||
|
}
|
||||||
|
/* latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 300;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-Light.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
/* cyrillic-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-Regular.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||||
|
}
|
||||||
|
/* cyrillic */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-Regular.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||||
|
}
|
||||||
|
/* greek-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-Regular.ttf')) format('truetype');
|
||||||
|
unicode-range: U+1F00-1FFF;
|
||||||
|
}
|
||||||
|
/* greek */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-Regular.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0370-03FF;
|
||||||
|
}
|
||||||
|
/* vietnamese */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-Regular.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
||||||
|
}
|
||||||
|
/* latin-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-Regular.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||||
|
}
|
||||||
|
/* latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 400;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-Regular.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
/* cyrillic-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBold.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||||
|
}
|
||||||
|
/* cyrillic */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBold.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||||
|
}
|
||||||
|
/* greek-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBold.ttf')) format('truetype');
|
||||||
|
unicode-range: U+1F00-1FFF;
|
||||||
|
}
|
||||||
|
/* greek */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBold.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0370-03FF;
|
||||||
|
}
|
||||||
|
/* vietnamese */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBold.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
||||||
|
}
|
||||||
|
/* latin-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBold.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||||
|
}
|
||||||
|
/* latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 600;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-SemiBold.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
|
/* cyrillic-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-Bold.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0460-052F, U+1C80-1C88, U+20B4, U+2DE0-2DFF, U+A640-A69F, U+FE2E-FE2F;
|
||||||
|
}
|
||||||
|
/* cyrillic */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-Bold.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0400-045F, U+0490-0491, U+04B0-04B1, U+2116;
|
||||||
|
}
|
||||||
|
/* greek-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-Bold.ttf')) format('truetype');
|
||||||
|
unicode-range: U+1F00-1FFF;
|
||||||
|
}
|
||||||
|
/* greek */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-Bold.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0370-03FF;
|
||||||
|
}
|
||||||
|
/* vietnamese */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-Bold.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0102-0103, U+0110-0111, U+1EA0-1EF9, U+20AB;
|
||||||
|
}
|
||||||
|
/* latin-ext */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-Bold.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0100-024F, U+0259, U+1E00-1EFF, U+2020, U+20A0-20AB, U+20AD-20CF, U+2113, U+2C60-2C7F, U+A720-A7FF;
|
||||||
|
}
|
||||||
|
/* latin */
|
||||||
|
@font-face {
|
||||||
|
font-family: 'Source Sans Pro';
|
||||||
|
font-style: normal;
|
||||||
|
font-weight: 700;
|
||||||
|
font-display: swap;
|
||||||
|
src: asset-url(font-path('SourceSansPro/SourceSansPro-Bold.ttf')) format('truetype');
|
||||||
|
unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6, U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193, U+2212, U+2215, U+FEFF, U+FFFD;
|
||||||
|
}
|
||||||
@@ -0,0 +1,24 @@
|
|||||||
|
/* BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
*
|
||||||
|
* Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
*
|
||||||
|
* This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
* terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
* Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
* version.
|
||||||
|
*
|
||||||
|
* BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
* WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
* PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
* with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
// Declare all variables here.
|
||||||
|
// They will automatically be included in application.css.
|
||||||
|
// You can use Sass (SCSS) here: http://sass-lang.com/
|
||||||
|
$background-color: #F5F7FB;
|
||||||
|
$error-background-color: #EFE6E6;
|
||||||
|
$button-color-blue: #467FCF;
|
||||||
|
$header-height: 65px;
|
||||||
|
$footer-height: 65px;
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
#
|
#
|
||||||
# Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify it under the
|
# This program is free software; you can redistribute it and/or modify it under the
|
||||||
# terms of the GNU Lesser General Public License as published by the Free Software
|
# terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
#
|
#
|
||||||
# Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify it under the
|
# This program is free software; you can redistribute it and/or modify it under the
|
||||||
# terms of the GNU Lesser General Public License as published by the Free Software
|
# terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
#
|
#
|
||||||
# Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify it under the
|
# This program is free software; you can redistribute it and/or modify it under the
|
||||||
# terms of the GNU Lesser General Public License as published by the Free Software
|
# terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
@@ -14,8 +16,8 @@
|
|||||||
# You should have received a copy of the GNU Lesser General Public License along
|
# You should have received a copy of the GNU Lesser General Public License along
|
||||||
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
class RefreshMeetingsChannel < ApplicationCable::Channel
|
class WaitingChannel < ApplicationCable::Channel
|
||||||
def subscribed
|
def subscribed
|
||||||
stream_from "refresh_meetings"
|
stream_from "#{params[:roomuid]}_waiting_channel"
|
||||||
end
|
end
|
||||||
end
|
end
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
# terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
# Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
# version.
|
||||||
|
#
|
||||||
|
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
class AccountActivationsController < ApplicationController
|
||||||
|
include Emailer
|
||||||
|
include Authenticator
|
||||||
|
|
||||||
|
before_action :ensure_unauthenticated
|
||||||
|
before_action :find_user_by_token, only: :edit
|
||||||
|
before_action :find_user_by_digest, only: :resend
|
||||||
|
|
||||||
|
# GET /account_activations
|
||||||
|
def show
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /account_activations/edit
|
||||||
|
def edit
|
||||||
|
# If the user exists and is not verified and provided the correct token
|
||||||
|
if @user && !@user.activated?
|
||||||
|
# Verify user
|
||||||
|
@user.set_role(initial_user_role(@user.email)) if @user.role.nil?
|
||||||
|
@user.activate
|
||||||
|
|
||||||
|
# Redirect user to root with account pending flash if account is still pending
|
||||||
|
return redirect_to root_path,
|
||||||
|
flash: { success: I18n.t("registration.approval.signup") } if @user.has_role?(:pending)
|
||||||
|
|
||||||
|
# Redirect user to sign in path with success flash
|
||||||
|
redirect_to signin_path, flash: { success: I18n.t("verify.activated") + " " + I18n.t("verify.signin") }
|
||||||
|
else
|
||||||
|
redirect_to root_path, flash: { alert: I18n.t("verify.invalid") }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# POST /account_activations/resend
|
||||||
|
def resend
|
||||||
|
if @user.activated?
|
||||||
|
# User is already verified
|
||||||
|
flash[:alert] = I18n.t("verify.already_verified")
|
||||||
|
else
|
||||||
|
# Resend
|
||||||
|
send_activation_email(@user, @user.create_activation_token)
|
||||||
|
end
|
||||||
|
|
||||||
|
redirect_to root_path
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def find_user_by_token
|
||||||
|
return redirect_to root_path, flash: { alert: I18n.t("verify.invalid") } unless params[:token].present?
|
||||||
|
|
||||||
|
@user = User.find_by!(activation_digest: User.hash_token(params[:token]), provider: @user_domain)
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_user_by_digest
|
||||||
|
@user = User.find_by!(activation_digest: params[:digest], provider: @user_domain)
|
||||||
|
end
|
||||||
|
|
||||||
|
def ensure_unauthenticated
|
||||||
|
redirect_to current_user.main_room || root_path if current_user
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,372 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
# terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
# Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
# version.
|
||||||
|
#
|
||||||
|
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
class AdminsController < ApplicationController
|
||||||
|
include Pagy::Backend
|
||||||
|
include Themer
|
||||||
|
include Emailer
|
||||||
|
include Recorder
|
||||||
|
include Rolify
|
||||||
|
include Populator
|
||||||
|
|
||||||
|
manage_users = [:edit_user, :promote, :demote, :ban_user, :unban_user, :approve, :reset, :merge_user]
|
||||||
|
manage_deleted_users = [:undelete]
|
||||||
|
authorize_resource class: false
|
||||||
|
before_action :find_user, only: manage_users
|
||||||
|
before_action :find_deleted_user, only: manage_deleted_users
|
||||||
|
before_action :verify_admin_of_user, only: [manage_users, manage_deleted_users]
|
||||||
|
|
||||||
|
# GET /admins
|
||||||
|
def index
|
||||||
|
# Initializa the data manipulation variables
|
||||||
|
@search = params[:search] || ""
|
||||||
|
@order_column = params[:column] && params[:direction] != "none" ? params[:column] : "created_at"
|
||||||
|
@order_direction = params[:direction] && params[:direction] != "none" ? params[:direction] : "DESC"
|
||||||
|
@tab = params[:tab] || "active"
|
||||||
|
@role = params[:role] ? Role.find_by(name: params[:role], provider: @user_domain) : nil
|
||||||
|
|
||||||
|
users = if @tab == "invited"
|
||||||
|
invited_users_list
|
||||||
|
else
|
||||||
|
manage_users_list
|
||||||
|
end
|
||||||
|
|
||||||
|
@pagy, @users = pagy(users)
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /admins/site_settings
|
||||||
|
def site_settings
|
||||||
|
@tab = params[:tab] || "appearance"
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /admins/server_recordings
|
||||||
|
def server_recordings
|
||||||
|
server_rooms = rooms_list_for_recordings
|
||||||
|
|
||||||
|
@search, @order_column, @order_direction, recs =
|
||||||
|
all_recordings(server_rooms, params.permit(:search, :column, :direction), true, true)
|
||||||
|
|
||||||
|
@pagy, @recordings = pagy_array(recs)
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /admins/rooms
|
||||||
|
def server_rooms
|
||||||
|
@search = params[:search] || ""
|
||||||
|
@order_column = params[:column] && params[:direction] != "none" ? params[:column] : "status"
|
||||||
|
@order_direction = params[:direction] && params[:direction] != "none" ? params[:direction] : "DESC"
|
||||||
|
|
||||||
|
begin
|
||||||
|
meetings = all_running_meetings[:meetings]
|
||||||
|
rescue BigBlueButton::BigBlueButtonException
|
||||||
|
flash[:alert] = I18n.t("administrator.rooms.timeout", server: I18n.t("bigbluebutton"))
|
||||||
|
meetings = []
|
||||||
|
end
|
||||||
|
|
||||||
|
@order_column = "created_at" if meetings.empty?
|
||||||
|
@running_room_bbb_ids = meetings.pluck(:meetingID)
|
||||||
|
|
||||||
|
@participants_count = {}
|
||||||
|
meetings.each do |meet|
|
||||||
|
@participants_count[meet[:meetingID]] = meet[:participantCount]
|
||||||
|
end
|
||||||
|
|
||||||
|
@pagy, @rooms = pagy_array(server_rooms_list)
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /admins/room_configuration
|
||||||
|
def room_configuration
|
||||||
|
end
|
||||||
|
|
||||||
|
# MANAGE USERS
|
||||||
|
|
||||||
|
# GET /admins/edit/:user_uid
|
||||||
|
def edit_user
|
||||||
|
session[:prev_url] = request.referer if request.referer.present?
|
||||||
|
end
|
||||||
|
|
||||||
|
# POST /admins/ban/:user_uid
|
||||||
|
def ban_user
|
||||||
|
@user.set_role :denied
|
||||||
|
|
||||||
|
redirect_back fallback_location: admins_path, flash: { success: I18n.t("administrator.flash.banned") }
|
||||||
|
end
|
||||||
|
|
||||||
|
# POST /admins/unban/:user_uid
|
||||||
|
def unban_user
|
||||||
|
@user.set_role :user
|
||||||
|
|
||||||
|
redirect_back fallback_location: admins_path, flash: { success: I18n.t("administrator.flash.unbanned") }
|
||||||
|
end
|
||||||
|
|
||||||
|
# POST /admins/approve/:user_uid
|
||||||
|
def approve
|
||||||
|
@user.set_role :user
|
||||||
|
|
||||||
|
send_user_approved_email(@user)
|
||||||
|
|
||||||
|
redirect_back fallback_location: admins_path, flash: { success: I18n.t("administrator.flash.approved") }
|
||||||
|
end
|
||||||
|
|
||||||
|
# POST /admins/approve/:user_uid
|
||||||
|
def undelete
|
||||||
|
# Undelete the user and all of his rooms
|
||||||
|
@user.undelete!
|
||||||
|
@user.rooms.deleted.each(&:undelete!)
|
||||||
|
|
||||||
|
redirect_back fallback_location: admins_path, flash: { success: I18n.t("administrator.flash.restored") }
|
||||||
|
end
|
||||||
|
|
||||||
|
# POST /admins/invite
|
||||||
|
def invite
|
||||||
|
emails = params[:invite_user][:email].split(",")
|
||||||
|
|
||||||
|
emails.each do |email|
|
||||||
|
invitation = create_or_update_invite(email)
|
||||||
|
|
||||||
|
send_invitation_email(current_user.name, email, invitation.invite_token)
|
||||||
|
end
|
||||||
|
|
||||||
|
redirect_back fallback_location: admins_path
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /admins/reset
|
||||||
|
def reset
|
||||||
|
send_password_reset_email(@user, @user.create_reset_digest)
|
||||||
|
|
||||||
|
if session[:prev_url].present?
|
||||||
|
redirect_path = session[:prev_url]
|
||||||
|
session.delete(:prev_url)
|
||||||
|
else
|
||||||
|
redirect_path = admins_path
|
||||||
|
end
|
||||||
|
|
||||||
|
redirect_to redirect_path, flash: { success: I18n.t("administrator.flash.reset_password") }
|
||||||
|
end
|
||||||
|
|
||||||
|
# POST /admins/merge/:user_uid
|
||||||
|
def merge_user
|
||||||
|
begin
|
||||||
|
# Get uid of user that will be merged into the other account
|
||||||
|
uid_to_merge = params[:merge]
|
||||||
|
logger.info "#{current_user.uid} is attempting to merge #{uid_to_merge} into #{@user.uid}"
|
||||||
|
|
||||||
|
# Check to make sure the 2 users are unique
|
||||||
|
raise "Can not merge the user into themself" if uid_to_merge == @user.uid
|
||||||
|
|
||||||
|
# Find user to merge
|
||||||
|
user_to_merge = User.find_by(uid: uid_to_merge)
|
||||||
|
|
||||||
|
# Move over user's rooms
|
||||||
|
user_to_merge.rooms.each do |room|
|
||||||
|
room.owner = @user
|
||||||
|
|
||||||
|
room.name = "(#{I18n.t('merged')}) #{room.name}"
|
||||||
|
|
||||||
|
room.save!
|
||||||
|
end
|
||||||
|
|
||||||
|
# Reload user to update merge rooms
|
||||||
|
user_to_merge.reload
|
||||||
|
|
||||||
|
# Delete merged user
|
||||||
|
user_to_merge.destroy(true)
|
||||||
|
rescue => e
|
||||||
|
logger.info "Failed to merge #{uid_to_merge} into #{@user.uid}: #{e}"
|
||||||
|
flash[:alert] = I18n.t("administrator.flash.merge_fail")
|
||||||
|
else
|
||||||
|
logger.info "#{current_user.uid} successfully merged #{uid_to_merge} into #{@user.uid}"
|
||||||
|
flash[:success] = I18n.t("administrator.flash.merge_success")
|
||||||
|
end
|
||||||
|
|
||||||
|
redirect_back fallback_location: admins_path
|
||||||
|
end
|
||||||
|
|
||||||
|
# GET /admins/merge_list
|
||||||
|
def merge_list
|
||||||
|
# Returns a list of users that can merged into another user
|
||||||
|
initial_list = User.without_role(:super_admin)
|
||||||
|
.where.not(uid: current_user.uid)
|
||||||
|
.merge_list_search(params[:search])
|
||||||
|
.pluck_to_hash(:uid, :name, :email)
|
||||||
|
|
||||||
|
initial_list = initial_list.where(provider: @user_domain) if Rails.configuration.loadbalanced_configuration
|
||||||
|
|
||||||
|
# Respond with JSON object of users
|
||||||
|
respond_to do |format|
|
||||||
|
format.json { render body: initial_list.to_json }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# SITE SETTINGS
|
||||||
|
|
||||||
|
# POST /admins/update_settings
|
||||||
|
def update_settings
|
||||||
|
tab = params[:tab] || "settings"
|
||||||
|
@settings.update_value(params[:setting], params[:value])
|
||||||
|
|
||||||
|
flash_message = I18n.t("administrator.flash.settings")
|
||||||
|
|
||||||
|
if params[:value] == "Default Recording Visibility"
|
||||||
|
flash_message += ". " + I18n.t("administrator.site_settings.recording_visibility.warning")
|
||||||
|
end
|
||||||
|
|
||||||
|
redirect_to admin_site_settings_path(tab: tab), flash: { success: flash_message }
|
||||||
|
end
|
||||||
|
|
||||||
|
# POST /admins/color
|
||||||
|
def coloring
|
||||||
|
@settings.update_value("Primary Color", params[:value])
|
||||||
|
@settings.update_value("Primary Color Lighten", color_lighten(params[:value]))
|
||||||
|
@settings.update_value("Primary Color Darken", color_darken(params[:value]))
|
||||||
|
redirect_to admin_site_settings_path(tab: "appearance"), flash: { success: I18n.t("administrator.flash.settings") }
|
||||||
|
end
|
||||||
|
|
||||||
|
# POST /admins/registration_method/:method
|
||||||
|
def registration_method
|
||||||
|
new_method = Rails.configuration.registration_methods[params[:value].to_sym]
|
||||||
|
|
||||||
|
# Only allow change to Join by Invitation if user has emails enabled
|
||||||
|
if !Rails.configuration.enable_email_verification && new_method == Rails.configuration.registration_methods[:invite]
|
||||||
|
redirect_to admin_site_settings_path(tab: "settings"),
|
||||||
|
flash: { alert: I18n.t("administrator.flash.invite_email_verification") }
|
||||||
|
else
|
||||||
|
@settings.update_value("Registration Method", new_method)
|
||||||
|
redirect_to admin_site_settings_path(tab: "settings"),
|
||||||
|
flash: { success: I18n.t("administrator.flash.registration_method_updated") }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# POST /admins/clear_auth
|
||||||
|
def clear_auth
|
||||||
|
User.include_deleted.where(provider: @user_domain).update_all(social_uid: nil)
|
||||||
|
|
||||||
|
redirect_to admin_site_settings_path(tab: "settings"), flash: { success: I18n.t("administrator.flash.settings") }
|
||||||
|
end
|
||||||
|
|
||||||
|
# POST /admins/clear_cache
|
||||||
|
def clear_cache
|
||||||
|
Rails.cache.delete("#{@user_domain}/getUser")
|
||||||
|
Rails.cache.delete("#{@user_domain}/getUserGreenlightCredentials")
|
||||||
|
|
||||||
|
redirect_to admin_site_settings_path(tab: "settings"), flash: { success: I18n.t("administrator.flash.settings") }
|
||||||
|
end
|
||||||
|
|
||||||
|
# POST /admins/log_level
|
||||||
|
def log_level
|
||||||
|
Rails.logger.level = params[:value].to_i
|
||||||
|
|
||||||
|
redirect_to admin_site_settings_path(tab: "administration"), flash: { success: I18n.t("administrator.flash.settings") }
|
||||||
|
end
|
||||||
|
|
||||||
|
# ROOM CONFIGURATION
|
||||||
|
# POST /admins/update_room_configuration
|
||||||
|
def update_room_configuration
|
||||||
|
@settings.update_value(params[:setting], params[:value])
|
||||||
|
|
||||||
|
flash_message = I18n.t("administrator.flash.room_configuration")
|
||||||
|
|
||||||
|
redirect_to admin_room_configuration_path, flash: { success: flash_message }
|
||||||
|
end
|
||||||
|
|
||||||
|
# ROLES
|
||||||
|
|
||||||
|
# GET /admins/roles
|
||||||
|
def roles
|
||||||
|
@roles = all_roles(params[:selected_role])
|
||||||
|
end
|
||||||
|
|
||||||
|
# POST /admins/role
|
||||||
|
# This method creates a new role scoped to the users provider
|
||||||
|
def new_role
|
||||||
|
new_role = create_role(params[:role][:name])
|
||||||
|
|
||||||
|
return redirect_to admin_roles_path, flash: { alert: I18n.t("administrator.roles.invalid_create") } if new_role.nil?
|
||||||
|
|
||||||
|
redirect_to admin_roles_path(selected_role: new_role.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
# PATCH /admin/roles/order
|
||||||
|
# This updates the priority of a site's roles
|
||||||
|
# Note: A lower priority role will always get used before a higher priority one
|
||||||
|
def change_role_order
|
||||||
|
unless update_priority(params[:role])
|
||||||
|
redirect_to admin_roles_path, flash: { alert: I18n.t("administrator.roles.invalid_order") }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# POST /admin/role/:role_id
|
||||||
|
# This method updates the permissions assigned to a role
|
||||||
|
def update_role
|
||||||
|
role = Role.find(params[:role_id])
|
||||||
|
flash[:alert] = I18n.t("administrator.roles.invalid_update") unless update_permissions(role)
|
||||||
|
redirect_to admin_roles_path(selected_role: role.id)
|
||||||
|
end
|
||||||
|
|
||||||
|
# DELETE admins/role/:role_id
|
||||||
|
# This deletes a role
|
||||||
|
def delete_role
|
||||||
|
role = Role.find(params[:role_id])
|
||||||
|
|
||||||
|
# Make sure no users are assigned to the role and the role isn't a reserved role
|
||||||
|
# before deleting
|
||||||
|
if role.users.count.positive?
|
||||||
|
flash[:alert] = I18n.t("administrator.roles.role_has_users", user_count: role.users.count)
|
||||||
|
return redirect_to admin_roles_path(selected_role: role.id)
|
||||||
|
elsif Role::RESERVED_ROLE_NAMES.include?(role) || role.provider != @user_domain ||
|
||||||
|
role.priority <= current_user.role.priority
|
||||||
|
return redirect_to admin_roles_path(selected_role: role.id)
|
||||||
|
else
|
||||||
|
role.role_permissions.delete_all
|
||||||
|
role.delete
|
||||||
|
end
|
||||||
|
|
||||||
|
redirect_to admin_roles_path
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def find_user
|
||||||
|
@user = User.find_by(uid: params[:user_uid])
|
||||||
|
end
|
||||||
|
|
||||||
|
def find_deleted_user
|
||||||
|
@user = User.deleted.find_by(uid: params[:user_uid])
|
||||||
|
end
|
||||||
|
|
||||||
|
# Verifies that admin is an administrator of the user in the action
|
||||||
|
def verify_admin_of_user
|
||||||
|
redirect_to admins_path,
|
||||||
|
flash: { alert: I18n.t("administrator.flash.unauthorized") } unless current_user.admin_of?(@user, "can_manage_users")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Creates the invite if it doesn't exist, or updates the updated_at time if it does
|
||||||
|
def create_or_update_invite(email)
|
||||||
|
invite = Invitation.find_by(email: email, provider: @user_domain)
|
||||||
|
|
||||||
|
# Invite already exists
|
||||||
|
if invite.present?
|
||||||
|
# Updates updated_at to now
|
||||||
|
invite.touch
|
||||||
|
else
|
||||||
|
# Creates invite
|
||||||
|
invite = Invitation.create(email: email, provider: @user_domain)
|
||||||
|
end
|
||||||
|
|
||||||
|
invite
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -1,6 +1,8 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
#
|
#
|
||||||
# Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
#
|
#
|
||||||
# This program is free software; you can redistribute it and/or modify it under the
|
# This program is free software; you can redistribute it and/or modify it under the
|
||||||
# terms of the GNU Lesser General Public License as published by the Free Software
|
# terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
@@ -14,46 +16,291 @@
|
|||||||
# You should have received a copy of the GNU Lesser General Public License along
|
# You should have received a copy of the GNU Lesser General Public License along
|
||||||
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
require 'bigbluebutton_api'
|
|
||||||
require 'digest/sha1'
|
|
||||||
|
|
||||||
class ApplicationController < ActionController::Base
|
class ApplicationController < ActionController::Base
|
||||||
protect_from_forgery with: :exception
|
include BbbServer
|
||||||
before_action :set_locale
|
include Errors
|
||||||
MEETING_NAME_LIMIT = 200
|
|
||||||
USER_NAME_LIMIT = 100
|
|
||||||
|
|
||||||
def set_locale
|
before_action :block_unknown_hosts, :redirect_to_https, :set_user_domain, :set_user_settings, :maintenance_mode?,
|
||||||
I18n.locale = http_accept_language.language_region_compatible_from(I18n.available_locales)
|
:migration_error?, :user_locale, :check_admin_password, :check_user_role
|
||||||
end
|
|
||||||
|
|
||||||
|
protect_from_forgery with: :exceptions
|
||||||
|
|
||||||
|
# Retrieves the current user.
|
||||||
def current_user
|
def current_user
|
||||||
@current_user ||= User.find_by(id: session[:user_id])
|
@current_user ||= User.includes(:role, :main_room).find_by(id: session[:user_id])
|
||||||
|
|
||||||
|
if Rails.configuration.loadbalanced_configuration
|
||||||
|
if @current_user && !@current_user.has_role?(:super_admin) &&
|
||||||
|
@current_user.provider != @user_domain
|
||||||
|
@current_user = nil
|
||||||
|
session.clear
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
@current_user
|
||||||
end
|
end
|
||||||
helper_method :current_user
|
helper_method :current_user
|
||||||
|
|
||||||
|
def bbb_server
|
||||||
|
@bbb_server ||= Rails.configuration.loadbalanced_configuration ? bbb(@user_domain) : bbb("greenlight")
|
||||||
|
end
|
||||||
|
|
||||||
|
# Block unknown hosts to mitigate host header injection attacks
|
||||||
|
def block_unknown_hosts
|
||||||
|
return if Rails.configuration.hosts.blank?
|
||||||
|
raise UnsafeHostError, "#{request.host} is not a safe host" unless Rails.configuration.hosts.include?(request.host)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Force SSL
|
||||||
|
def redirect_to_https
|
||||||
|
if Rails.configuration.loadbalanced_configuration && request.headers["X-Forwarded-Proto"] == "http"
|
||||||
|
redirect_to protocol: "https://"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Sets the user domain variable
|
||||||
|
def set_user_domain
|
||||||
|
if Rails.env.test? || !Rails.configuration.loadbalanced_configuration
|
||||||
|
@user_domain = "greenlight"
|
||||||
|
else
|
||||||
|
@user_domain = parse_user_domain(request.host)
|
||||||
|
|
||||||
|
check_provider_exists
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Sets the settinfs variable
|
||||||
|
def set_user_settings
|
||||||
|
@settings = Setting.includes(:features).find_or_create_by(provider: @user_domain)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Redirects the user to a Maintenance page if turned on
|
||||||
|
def maintenance_mode?
|
||||||
|
if ENV["MAINTENANCE_MODE"] == "true"
|
||||||
|
render "errors/greenlight_error", status: 503, formats: :html,
|
||||||
|
locals: {
|
||||||
|
status_code: 503,
|
||||||
|
message: I18n.t("errors.maintenance.message"),
|
||||||
|
help: I18n.t("errors.maintenance.help"),
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
maintenance_string = @settings.get_value("Maintenance Banner").presence || Rails.configuration.maintenance_window
|
||||||
|
if maintenance_string.present?
|
||||||
|
flash.now[:maintenance] = maintenance_string unless cookies[:maintenance_window] == maintenance_string
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Show an information page when migration fails and there is a version error.
|
||||||
|
def migration_error?
|
||||||
|
render :migration_error, status: 500 unless ENV["DB_MIGRATE_FAILED"].blank?
|
||||||
|
end
|
||||||
|
|
||||||
|
# Sets the appropriate locale.
|
||||||
|
def user_locale(user = current_user)
|
||||||
|
locale = if user && user.language != 'default'
|
||||||
|
user.language
|
||||||
|
else
|
||||||
|
Rails.configuration.default_locale.presence || http_accept_language.language_region_compatible_from(I18n.available_locales)
|
||||||
|
end
|
||||||
|
|
||||||
|
begin
|
||||||
|
I18n.locale = locale.tr('-', '_') unless locale.nil?
|
||||||
|
rescue
|
||||||
|
# Default to English if there are any issues in language
|
||||||
|
logger.error("Support: User locale is not supported (#{locale}")
|
||||||
|
I18n.locale = "en"
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Checks to make sure that the admin has changed his password from the default
|
||||||
|
def check_admin_password
|
||||||
|
if current_user&.has_role?(:admin) && current_user.email == "admin@example.com" &&
|
||||||
|
current_user&.greenlight_account? && current_user&.authenticate(Rails.configuration.admin_password_default)
|
||||||
|
|
||||||
|
flash.now[:alert] = I18n.t("default_admin",
|
||||||
|
edit_link: change_password_path(user_uid: current_user.uid)).html_safe
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Checks if the user is banned and logs him out if he is
|
||||||
|
def check_user_role
|
||||||
|
if current_user&.has_role? :denied
|
||||||
|
session.delete(:user_id)
|
||||||
|
redirect_to root_path, flash: { alert: I18n.t("registration.banned.fail") }
|
||||||
|
elsif current_user&.has_role? :pending
|
||||||
|
session.delete(:user_id)
|
||||||
|
redirect_to root_path, flash: { alert: I18n.t("registration.approval.fail") }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Relative root helper (when deploying to subdirectory).
|
||||||
def relative_root
|
def relative_root
|
||||||
Rails.configuration.relative_url_root || ""
|
Rails.configuration.relative_url_root || ""
|
||||||
end
|
end
|
||||||
helper_method :relative_root
|
helper_method :relative_root
|
||||||
|
|
||||||
def meeting_name_limit
|
# Determines if the BigBlueButton endpoint is configured (or set to default).
|
||||||
MEETING_NAME_LIMIT
|
|
||||||
end
|
|
||||||
helper_method :meeting_name_limit
|
|
||||||
|
|
||||||
def user_name_limit
|
|
||||||
USER_NAME_LIMIT
|
|
||||||
end
|
|
||||||
helper_method :user_name_limit
|
|
||||||
|
|
||||||
def bigbluebutton_endpoint_default?
|
def bigbluebutton_endpoint_default?
|
||||||
|
return false if Rails.configuration.loadbalanced_configuration
|
||||||
Rails.configuration.bigbluebutton_endpoint_default == Rails.configuration.bigbluebutton_endpoint
|
Rails.configuration.bigbluebutton_endpoint_default == Rails.configuration.bigbluebutton_endpoint
|
||||||
end
|
end
|
||||||
helper_method :bigbluebutton_endpoint_default?
|
helper_method :bigbluebutton_endpoint_default?
|
||||||
|
|
||||||
def qrcode_generation_enabled?
|
def allow_greenlight_accounts?
|
||||||
Rails.configuration.enable_qrcode_generation
|
return Rails.configuration.allow_user_signup unless Rails.configuration.loadbalanced_configuration
|
||||||
|
return false unless @user_domain && !@user_domain.empty? && Rails.configuration.allow_user_signup
|
||||||
|
return false if @user_domain == "greenlight"
|
||||||
|
# Proceed with retrieving the provider info
|
||||||
|
begin
|
||||||
|
provider_info = retrieve_provider_info(@user_domain, 'api2', 'getUserGreenlightCredentials')
|
||||||
|
provider_info['provider'] == 'greenlight'
|
||||||
|
rescue => e
|
||||||
|
logger.error "Error in checking if greenlight accounts are allowed: #{e}"
|
||||||
|
false
|
||||||
|
end
|
||||||
|
end
|
||||||
|
helper_method :allow_greenlight_accounts?
|
||||||
|
|
||||||
|
# Determine if Greenlight is configured to allow user signups.
|
||||||
|
def allow_user_signup?
|
||||||
|
Rails.configuration.allow_user_signup
|
||||||
|
end
|
||||||
|
helper_method :allow_user_signup?
|
||||||
|
|
||||||
|
# Gets all configured omniauth providers.
|
||||||
|
def configured_providers
|
||||||
|
Rails.configuration.providers.select do |provider|
|
||||||
|
Rails.configuration.send("omniauth_#{provider}")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
helper_method :configured_providers
|
||||||
|
|
||||||
|
# Indicates whether users are allowed to share rooms
|
||||||
|
def shared_access_allowed
|
||||||
|
@settings.get_value("Shared Access") == "true"
|
||||||
|
end
|
||||||
|
helper_method :shared_access_allowed
|
||||||
|
|
||||||
|
# Indicates whether users are allowed to share rooms
|
||||||
|
def recording_consent_required?
|
||||||
|
@settings.get_value("Require Recording Consent") == "true"
|
||||||
|
end
|
||||||
|
helper_method :recording_consent_required?
|
||||||
|
|
||||||
|
# Returns a list of allowed file types
|
||||||
|
def allowed_file_types
|
||||||
|
Rails.configuration.allowed_file_types
|
||||||
|
end
|
||||||
|
helper_method :allowed_file_types
|
||||||
|
|
||||||
|
# Allows admins to edit a user's details
|
||||||
|
def can_edit_user?(user_to_edit, editting_user)
|
||||||
|
return user_to_edit.greenlight_account? if user_to_edit == editting_user
|
||||||
|
|
||||||
|
editting_user.admin_of?(user_to_edit, "can_manage_users")
|
||||||
|
end
|
||||||
|
helper_method :can_edit_user?
|
||||||
|
|
||||||
|
# Returns the page that the logo redirects to when clicked on
|
||||||
|
def home_page
|
||||||
|
return admins_path if current_user.has_role? :super_admin
|
||||||
|
return current_user.main_room if current_user.role.get_permission("can_create_rooms")
|
||||||
|
cant_create_rooms_path
|
||||||
|
end
|
||||||
|
helper_method :home_page
|
||||||
|
|
||||||
|
# Parses the url for the user domain
|
||||||
|
def parse_user_domain(hostname)
|
||||||
|
return hostname.split('.').first if Rails.configuration.url_host.empty?
|
||||||
|
Rails.configuration.url_host.split(',').each do |url_host|
|
||||||
|
return hostname.chomp(url_host).chomp('.') if hostname.include?(url_host)
|
||||||
|
end
|
||||||
|
''
|
||||||
|
end
|
||||||
|
|
||||||
|
# Include user domain in lograge logs
|
||||||
|
def append_info_to_payload(payload)
|
||||||
|
super
|
||||||
|
payload[:host] = @user_domain
|
||||||
|
end
|
||||||
|
|
||||||
|
# Manually handle BigBlueButton errors
|
||||||
|
rescue_from BigBlueButton::BigBlueButtonException do |ex|
|
||||||
|
logger.error "BigBlueButtonException: #{ex}"
|
||||||
|
render "errors/bigbluebutton_error"
|
||||||
|
end
|
||||||
|
|
||||||
|
# Manually deal with 401 errors
|
||||||
|
rescue_from CanCan::AccessDenied do |_exception|
|
||||||
|
if current_user
|
||||||
|
render "errors/greenlight_error"
|
||||||
|
else
|
||||||
|
# Store the current url as a cookie to redirect to after sigining in
|
||||||
|
cookies[:return_to] = request.url
|
||||||
|
|
||||||
|
# Get the correct signin path
|
||||||
|
path = if allow_greenlight_accounts?
|
||||||
|
signin_path
|
||||||
|
elsif Rails.configuration.loadbalanced_configuration
|
||||||
|
"#{Rails.configuration.relative_url_root}/auth/bn_launcher"
|
||||||
|
else
|
||||||
|
signin_path
|
||||||
|
end
|
||||||
|
|
||||||
|
redirect_to path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def check_provider_exists
|
||||||
|
# Checks to see if the user exists
|
||||||
|
begin
|
||||||
|
# Check if the session has already checked that the user exists
|
||||||
|
# and return true if they did for this domain
|
||||||
|
return if session[:provider_exists] == @user_domain
|
||||||
|
|
||||||
|
retrieve_provider_info(@user_domain, 'api2', 'getUserGreenlightCredentials')
|
||||||
|
|
||||||
|
# Add a session variable if the provider exists
|
||||||
|
session[:provider_exists] = @user_domain
|
||||||
|
rescue => e
|
||||||
|
logger.error "Error in retrieve provider info: #{e}"
|
||||||
|
@hide_signin = true
|
||||||
|
if e.message.eql? "No user with that id exists"
|
||||||
|
set_default_settings
|
||||||
|
|
||||||
|
render "errors/greenlight_error", locals: { message: I18n.t("errors.not_found.user_not_found.message"),
|
||||||
|
help: I18n.t("errors.not_found.user_not_found.help") }
|
||||||
|
elsif e.message.eql? "Provider not included."
|
||||||
|
set_default_settings
|
||||||
|
|
||||||
|
render "errors/greenlight_error", locals: { message: I18n.t("errors.not_found.user_missing.message"),
|
||||||
|
help: I18n.t("errors.not_found.user_missing.help") }
|
||||||
|
elsif e.message.eql? "That user has no configured provider."
|
||||||
|
if Setting.exists?(provider: @user_domain)
|
||||||
|
# Keep the branding
|
||||||
|
@settings = Setting.find_by(provider: @user_domain)
|
||||||
|
else
|
||||||
|
set_default_settings
|
||||||
|
end
|
||||||
|
|
||||||
|
render "errors/greenlight_error", locals: { status_code: 501,
|
||||||
|
message: I18n.t("errors.no_provider.message"),
|
||||||
|
help: I18n.t("errors.no_provider.help") }
|
||||||
|
else
|
||||||
|
set_default_settings
|
||||||
|
|
||||||
|
render "errors/greenlight_error", locals: { status_code: 500, message: I18n.t("errors.internal.message"),
|
||||||
|
help: I18n.t("errors.internal.help"), display_back: true }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def set_default_settings
|
||||||
|
# Use the default site settings
|
||||||
|
@user_domain = "greenlight"
|
||||||
|
@settings = Setting.find_or_create_by(provider: @user_domain)
|
||||||
end
|
end
|
||||||
helper_method :qrcode_generation_enabled?
|
|
||||||
end
|
end
|
||||||
|
|||||||
@@ -1,383 +0,0 @@
|
|||||||
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
|
||||||
#
|
|
||||||
# Copyright (c) 2016 BigBlueButton Inc. and by respective authors (see below).
|
|
||||||
#
|
|
||||||
# This program is free software; you can redistribute it and/or modify it under the
|
|
||||||
# terms of the GNU Lesser General Public License as published by the Free Software
|
|
||||||
# Foundation; either version 3.0 of the License, or (at your option) any later
|
|
||||||
# version.
|
|
||||||
#
|
|
||||||
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
|
||||||
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
|
||||||
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
|
||||||
#
|
|
||||||
# You should have received a copy of the GNU Lesser General Public License along
|
|
||||||
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
|
||||||
|
|
||||||
class BbbController < ApplicationController
|
|
||||||
include BbbApi
|
|
||||||
|
|
||||||
before_action :authorize_recording_owner!, only: [:update_recordings, :delete_recordings]
|
|
||||||
before_action :load_and_authorize_room_owner!, only: [:end]
|
|
||||||
|
|
||||||
skip_before_action :verify_authenticity_token, only: :callback
|
|
||||||
|
|
||||||
# GET /:resource/:id/join
|
|
||||||
# GET /:resource/:room_id/:id/join
|
|
||||||
def join
|
|
||||||
if params[:name].blank?
|
|
||||||
return render_bbb_response(
|
|
||||||
messageKey: "missing_parameter",
|
|
||||||
message: "user name was not included",
|
|
||||||
status: :unprocessable_entity
|
|
||||||
)
|
|
||||||
elsif params[:name].size > user_name_limit
|
|
||||||
return render_bbb_response(
|
|
||||||
messageKey: "invalid_parameter",
|
|
||||||
message: "user name is too long",
|
|
||||||
status: :unprocessable_entity
|
|
||||||
)
|
|
||||||
elsif params[:id].size > meeting_name_limit
|
|
||||||
return render_bbb_response(
|
|
||||||
messageKey: "invalid_parameter",
|
|
||||||
message: "meeting name is too long",
|
|
||||||
status: :unprocessable_entity
|
|
||||||
)
|
|
||||||
else
|
|
||||||
if params[:room_id]
|
|
||||||
user = User.find_by encrypted_id: params[:room_id]
|
|
||||||
if !user
|
|
||||||
return render_bbb_response(
|
|
||||||
messageKey: "not_found",
|
|
||||||
message: "User Not Found",
|
|
||||||
status: :not_found
|
|
||||||
)
|
|
||||||
end
|
|
||||||
|
|
||||||
meeting_id = "#{params[:room_id]}-#{params[:id]}"
|
|
||||||
meeting_name = params[:id]
|
|
||||||
meeting_path = "#{URI.encode(params[:room_id])}/#{URI.encode(params[:id]).gsub('/','%2F')}"
|
|
||||||
else
|
|
||||||
user = User.find_by encrypted_id: params[:id]
|
|
||||||
meeting_id = params[:id]
|
|
||||||
meeting_path = URI.encode(meeting_id).gsub('/','%2F')
|
|
||||||
end
|
|
||||||
|
|
||||||
options = if user
|
|
||||||
{
|
|
||||||
wait_for_moderator: true,
|
|
||||||
meeting_recorded: true,
|
|
||||||
meeting_name: meeting_name,
|
|
||||||
room_owner: params[:room_id],
|
|
||||||
user_is_moderator: current_user == user
|
|
||||||
}
|
|
||||||
else
|
|
||||||
{
|
|
||||||
user_is_moderator: true
|
|
||||||
}
|
|
||||||
end
|
|
||||||
|
|
||||||
base_url = "#{request.base_url}#{relative_root}/#{params[:resource]}/#{meeting_path}"
|
|
||||||
options[:meeting_logout_url] = base_url
|
|
||||||
options[:hook_url] = "#{base_url}/callback"
|
|
||||||
options[:moderator_message] = t('moderator_default_message', url: "<a href=\"#{base_url}\" target=\"_blank\"><u>#{base_url}</u></a>")
|
|
||||||
|
|
||||||
bbb_res = bbb_join_url(
|
|
||||||
meeting_id,
|
|
||||||
params[:name],
|
|
||||||
options
|
|
||||||
)
|
|
||||||
|
|
||||||
# the user can join the meeting
|
|
||||||
if user
|
|
||||||
if bbb_res[:returncode] && current_user == user
|
|
||||||
JoinMeetingJob.perform_later(user, params[:id], base_url)
|
|
||||||
WaitingList.empty(options[:room_owner], options[:meeting_name])
|
|
||||||
# the user can't join because they are on mobile and HTML5 is not enabled.
|
|
||||||
elsif bbb_res[:messageKey] == 'unable_to_join'
|
|
||||||
NotifyUserCantJoinJob.perform_later(user.encrypted_id, params[:id], params[:name])
|
|
||||||
# user will be waiting for a moderator
|
|
||||||
else
|
|
||||||
NotifyUserWaitingJob.perform_later(user.encrypted_id, params[:id], params[:name])
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
render_bbb_response bbb_res, bbb_res[:response]
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# POST /:resource/:room_id/:id/callback
|
|
||||||
# Endpoint for webhook calls from BigBlueButton
|
|
||||||
def callback
|
|
||||||
# respond with 200 anyway so BigBlueButton knows the hook call was ok
|
|
||||||
# but abort execution
|
|
||||||
head(:ok) && return unless validate_checksum
|
|
||||||
|
|
||||||
begin
|
|
||||||
event = params['event']
|
|
||||||
data = event.is_a?(String) ? JSON.parse(params['event']) : event
|
|
||||||
treat_callback_event(data)
|
|
||||||
rescue Exception => e
|
|
||||||
logger.error "Error parsing webhook data. Data: #{data}, exception: #{e.inspect}"
|
|
||||||
|
|
||||||
# respond with 200 anyway so BigBlueButton knows the hook call was ok
|
|
||||||
head(:ok) && return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# DELETE /rooms/:room_id/:id/end
|
|
||||||
def end
|
|
||||||
load_and_authorize_room_owner!
|
|
||||||
|
|
||||||
bbb_res = bbb_end_meeting "#{@user.encrypted_id}-#{params[:id]}"
|
|
||||||
if bbb_res[:returncode] || bbb_res[:status] == :not_found
|
|
||||||
EndMeetingJob.perform_later(@user.encrypted_id, params[:id])
|
|
||||||
bbb_res[:status] = :ok
|
|
||||||
end
|
|
||||||
render_bbb_response bbb_res
|
|
||||||
end
|
|
||||||
|
|
||||||
# GET /rooms/:room_id/recordings
|
|
||||||
# GET /rooms/:room_id/:id/recordings
|
|
||||||
def recordings
|
|
||||||
load_room!
|
|
||||||
|
|
||||||
# bbb_res = bbb_get_recordings "#{@user.encrypted_id}-#{params[:id]}"
|
|
||||||
options = { "meta_room-id": @user.encrypted_id }
|
|
||||||
if params[:id]
|
|
||||||
options["meta_meeting-name"] = params[:id]
|
|
||||||
end
|
|
||||||
bbb_res = bbb_get_recordings(options)
|
|
||||||
render_bbb_response bbb_res, bbb_res[:recordings]
|
|
||||||
end
|
|
||||||
|
|
||||||
# PATCH /rooms/:room_id/recordings/:record_id
|
|
||||||
# PATCH /rooms/:room_id/:id/recordings/:record_id
|
|
||||||
def update_recordings
|
|
||||||
published = params[:published] == 'true'
|
|
||||||
metadata = params.select{ |k, v| k.match(/^meta_/) }
|
|
||||||
bbb_res = bbb_update_recordings(params[:record_id], published, metadata)
|
|
||||||
if bbb_res[:returncode]
|
|
||||||
RecordingUpdatesJob.perform_later(@user.encrypted_id, params[:record_id])
|
|
||||||
end
|
|
||||||
render_bbb_response bbb_res
|
|
||||||
end
|
|
||||||
|
|
||||||
# DELETE /rooms/:room_id/recordings/:record_id
|
|
||||||
# DELETE /rooms/:room_id/:id/recordings/:record_id
|
|
||||||
def delete_recordings
|
|
||||||
recording = bbb_get_recordings({recordID: params[:record_id]})[:recordings].first
|
|
||||||
bbb_res = bbb_delete_recordings(params[:record_id])
|
|
||||||
if bbb_res[:returncode]
|
|
||||||
RecordingDeletesJob.perform_later(@user.encrypted_id, params[:record_id], recording[:metadata][:"meeting-name"])
|
|
||||||
end
|
|
||||||
render_bbb_response bbb_res
|
|
||||||
end
|
|
||||||
|
|
||||||
# POST /rooms/:room_id/recordings/:record_id
|
|
||||||
# POST /rooms/:room_id/:id/recordings/:record_id
|
|
||||||
def youtube_publish
|
|
||||||
# If we can't get the client, then they don't have a Youtube account.
|
|
||||||
begin
|
|
||||||
client = Yt::Account.new(access_token: current_user.token)
|
|
||||||
video = client.upload_video(get_webcams_url(params[:record_id]),
|
|
||||||
title: params[:video_title],
|
|
||||||
description: t('youtube_description', url: 'https://bigbluebutton.org/'),
|
|
||||||
privacy_status: params[:privacy_status])
|
|
||||||
rescue => e
|
|
||||||
errors = e.response_body['error']['errors']
|
|
||||||
# Many complications, start by getting them to refresh their token.
|
|
||||||
if errors.length > 1
|
|
||||||
redirect_url = user_login_url
|
|
||||||
else
|
|
||||||
error = errors[0]
|
|
||||||
if error['message'] == "Unauthorized"
|
|
||||||
redirect_url = 'https://m.youtube.com/create_channel'
|
|
||||||
else
|
|
||||||
# In this case, they don't have a youtube channel connected to their account, so prompt to create one.
|
|
||||||
redirect_url = user_login_url
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
render json: {:url => redirect_url}
|
|
||||||
end
|
|
||||||
|
|
||||||
# POST /rooms/:room_id/recordings/can_upload
|
|
||||||
def can_upload
|
|
||||||
# The recording is uploadable if it contains webcam data and they are logged in through Google.
|
|
||||||
if Rails.configuration.enable_youtube_uploading == false then
|
|
||||||
uploadable = 'uploading_disabled'
|
|
||||||
elsif current_user.provider != 'google'
|
|
||||||
uploadable = 'invalid_provider'
|
|
||||||
else
|
|
||||||
uploadable = (Faraday.head(get_webcams_url(params[:rec_id])).status == 200 && current_user.provider == 'google').to_s
|
|
||||||
end
|
|
||||||
render json: {:uploadable => uploadable}
|
|
||||||
end
|
|
||||||
|
|
||||||
def get_webcams_url(recording_id)
|
|
||||||
uri = URI.parse(Rails.configuration.bigbluebutton_endpoint)
|
|
||||||
uri.scheme + '://' + uri.host + '/presentation/' + recording_id + '/video/webcams.webm'
|
|
||||||
end
|
|
||||||
|
|
||||||
private
|
|
||||||
|
|
||||||
def load_room!
|
|
||||||
@user = User.find_by encrypted_id: params[:room_id]
|
|
||||||
if !@user
|
|
||||||
render head(:not_found) && return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def load_and_authorize_room_owner!
|
|
||||||
load_room!
|
|
||||||
|
|
||||||
if !current_user || current_user != @user
|
|
||||||
render head(:unauthorized) && return
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
def authorize_recording_owner!
|
|
||||||
load_and_authorize_room_owner!
|
|
||||||
|
|
||||||
recordings = bbb_get_recordings({recordID: params[:record_id]})[:recordings]
|
|
||||||
recordings.each do |recording|
|
|
||||||
if recording[:recordID] == params[:record_id]
|
|
||||||
return true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
render head(:not_found) && return
|
|
||||||
end
|
|
||||||
|
|
||||||
def render_bbb_response(bbb_res, response={})
|
|
||||||
@messageKey = bbb_res[:messageKey]
|
|
||||||
@message = bbb_res[:message]
|
|
||||||
@status = bbb_res[:status]
|
|
||||||
@response = response
|
|
||||||
render status: @status
|
|
||||||
end
|
|
||||||
|
|
||||||
def read_body(request)
|
|
||||||
request.body.read.force_encoding("UTF-8")
|
|
||||||
end
|
|
||||||
|
|
||||||
def treat_callback_event(event)
|
|
||||||
# Check if the event is a BigBlueButton 2.0 event.
|
|
||||||
if event.has_key?('envelope')
|
|
||||||
eventName = (event.present? && event['envelope'].present?) ? event['envelope']['name'] : nil
|
|
||||||
else # The event came from BigBlueButton 1.1 (or earlier).
|
|
||||||
eventName = (event.present? && event['header'].present?) ? event['header']['name'] : nil
|
|
||||||
end
|
|
||||||
|
|
||||||
# a recording is ready
|
|
||||||
if eventName == "publish_ended"
|
|
||||||
if event['payload'] && event['payload']['metadata'] && event['payload']['meeting_id']
|
|
||||||
token = event['payload']['metadata'][META_TOKEN]
|
|
||||||
room_id = event['payload']['metadata']['room-id']
|
|
||||||
record_id = event['payload']['meeting_id']
|
|
||||||
duration_data = event['payload']['duration']
|
|
||||||
|
|
||||||
# the webhook event doesn't have all the data we need, so we need
|
|
||||||
# to send a getRecordings anyway
|
|
||||||
# TODO: if the webhooks included all data in the event we wouldn't need this
|
|
||||||
rec_info = bbb_get_recordings({recordID: record_id})
|
|
||||||
rec_info = rec_info[:recordings].first
|
|
||||||
RecordingCreatedJob.perform_later(token, room_id, parse_recording_for_view(rec_info))
|
|
||||||
|
|
||||||
rec_info[:duration] = duration_data.to_json
|
|
||||||
|
|
||||||
# send an email to the owner of this recording, if defined
|
|
||||||
if Rails.configuration.mail_notifications
|
|
||||||
owner = User.find_by(encrypted_id: room_id)
|
|
||||||
RecordingReadyEmailJob.perform_later(owner, parse_recording_for_view(rec_info)) if owner.present?
|
|
||||||
end
|
|
||||||
else
|
|
||||||
logger.error "Bad format for event #{event}, won't process"
|
|
||||||
end
|
|
||||||
elsif eventName == "meeting_created_message" || eventName == "MeetingCreatedEvtMsg"
|
|
||||||
# Fire an Actioncable event that updates _previously_joined for the client.
|
|
||||||
actioncable_event('create')
|
|
||||||
elsif eventName == "meeting_destroyed_event" || eventName == "MeetingEndedEvtMsg"
|
|
||||||
actioncable_event('destroy')
|
|
||||||
|
|
||||||
# Since the meeting is destroyed we have no way get the callback url to remove the meeting, so we must build it.
|
|
||||||
remove_url = build_callback_url(params[:id], params[:room_id])
|
|
||||||
|
|
||||||
# Remove webhook for the meeting.
|
|
||||||
webhook_remove(remove_url)
|
|
||||||
elsif eventName == "user_joined_message"
|
|
||||||
actioncable_event('join', {user_id: event['payload']['user']['extern_userid'], user: event['payload']['user']['name'], role: event['payload']['user']['role']})
|
|
||||||
elsif eventName == "UserJoinedMeetingEvtMsg"
|
|
||||||
actioncable_event('join', {user_id: event['core']['body']['intId'], user: event['core']['body']['name'], role: event['core']['body']['role']})
|
|
||||||
elsif eventName == "user_left_message"
|
|
||||||
actioncable_event('leave', {user_id: event['payload']['user']['extern_userid']})
|
|
||||||
elsif eventName == "UserLeftMeetingEvtMsg"
|
|
||||||
actioncable_event('leave', {user_id: event['core']['body']['intId']})
|
|
||||||
else
|
|
||||||
logger.info "Callback event will not be treated. Event name: #{eventName}"
|
|
||||||
end
|
|
||||||
|
|
||||||
render head(:ok) && return
|
|
||||||
end
|
|
||||||
|
|
||||||
def build_callback_url(id, room_id)
|
|
||||||
"#{request.base_url}#{relative_root}/rooms/#{room_id}/#{URI.encode(id)}/callback"
|
|
||||||
end
|
|
||||||
|
|
||||||
def actioncable_event(method, data = {})
|
|
||||||
data = {method: method, meeting: params[:id], room: params[:room_id]}.merge(data)
|
|
||||||
ActionCable.server.broadcast('refresh_meetings', data)
|
|
||||||
end
|
|
||||||
|
|
||||||
# Validates the checksum received in a callback call.
|
|
||||||
# If the checksum doesn't match, renders an ok and aborts execution.
|
|
||||||
def validate_checksum
|
|
||||||
secret = ENV['BIGBLUEBUTTON_SECRET']
|
|
||||||
checksum = params["checksum"]
|
|
||||||
return false unless checksum
|
|
||||||
|
|
||||||
# Message is only encoded if it comes from the bbb-webhooks node application.
|
|
||||||
# The post process script does not encode it's response body.
|
|
||||||
begin
|
|
||||||
# Decode and break the body into parts.
|
|
||||||
parts = URI.decode_www_form(read_body(request))
|
|
||||||
|
|
||||||
# Convert the data into the correct checksum format, replace ruby hash arrows.
|
|
||||||
converted_data = {parts[0][0]=>parts[0][1],parts[1][0]=>parts[1][1].to_i}.to_s.gsub!('=>', ':')
|
|
||||||
|
|
||||||
# Manually remove the space between the two elements.
|
|
||||||
converted_data[converted_data.rindex("timestamp") - 2] = ''
|
|
||||||
callback_url = uri_remove_param(request.original_url, "checksum")
|
|
||||||
checksum_str = "#{callback_url}#{converted_data}#{secret}"
|
|
||||||
rescue
|
|
||||||
# Data was not recieved encoded (sent from post process script).
|
|
||||||
data = read_body(request)
|
|
||||||
callback_url = uri_remove_param(request.original_url, "checksum")
|
|
||||||
checksum_str = "#{callback_url}#{data}#{secret}"
|
|
||||||
end
|
|
||||||
|
|
||||||
calculated_checksum = Digest::SHA1.hexdigest(checksum_str)
|
|
||||||
|
|
||||||
if calculated_checksum != checksum
|
|
||||||
logger.error "Checksum did not match. Calculated: #{calculated_checksum}, received: #{checksum}"
|
|
||||||
false
|
|
||||||
else
|
|
||||||
true
|
|
||||||
end
|
|
||||||
end
|
|
||||||
|
|
||||||
# Removes parameters from an URI
|
|
||||||
def uri_remove_param(uri, params = nil)
|
|
||||||
return uri unless params
|
|
||||||
params = Array(params)
|
|
||||||
uri_parsed = URI.parse(uri)
|
|
||||||
return uri unless uri_parsed.query
|
|
||||||
new_params = uri_parsed.query.gsub(/&/, '&').split('&').reject { |q| params.include?(q.split('=').first) }
|
|
||||||
uri = uri.split('?').first
|
|
||||||
if new_params.count > 0
|
|
||||||
"#{uri}?#{new_params.join('&')}"
|
|
||||||
else
|
|
||||||
uri
|
|
||||||
end
|
|
||||||
end
|
|
||||||
end
|
|
||||||
@@ -0,0 +1,123 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
# terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
# Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
# version.
|
||||||
|
#
|
||||||
|
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
module Authenticator
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
# Logs a user into GreenLight.
|
||||||
|
def login(user)
|
||||||
|
migrate_twitter_user(user)
|
||||||
|
|
||||||
|
session[:user_id] = user.id
|
||||||
|
user.update(last_login: Time.zone.now)
|
||||||
|
|
||||||
|
logger.info("Support: #{user.email} has successfully logged in.")
|
||||||
|
|
||||||
|
# If there are not terms, or the user has accepted them, check for email verification
|
||||||
|
if !Rails.configuration.terms || user.accepted_terms
|
||||||
|
check_email_verified(user)
|
||||||
|
else
|
||||||
|
redirect_to terms_path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# If email verification is disabled, or the user has verified, go to their room
|
||||||
|
def check_email_verified(user)
|
||||||
|
# Admin users should be redirected to the admin page
|
||||||
|
if user.has_role? :super_admin
|
||||||
|
redirect_to admins_path
|
||||||
|
elsif user.activated?
|
||||||
|
# Dont redirect to any of these urls
|
||||||
|
dont_redirect_to = [root_url, signin_url, ldap_signin_url, ldap_callback_url, signup_url, unauthorized_url,
|
||||||
|
internal_error_url, not_found_url]
|
||||||
|
url = if cookies[:return_to] && !dont_redirect_to.include?(cookies[:return_to])
|
||||||
|
cookies[:return_to]
|
||||||
|
elsif user.role.get_permission("can_create_rooms")
|
||||||
|
user.main_room
|
||||||
|
else
|
||||||
|
cant_create_rooms_path
|
||||||
|
end
|
||||||
|
|
||||||
|
# Delete the cookie if it exists
|
||||||
|
cookies.delete :return_to if cookies[:return_to]
|
||||||
|
|
||||||
|
redirect_to url
|
||||||
|
else
|
||||||
|
redirect_to resend_path
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def ensure_unauthenticated_except_twitter
|
||||||
|
redirect_to current_user.main_room || root_path if current_user && params[:old_twitter_user_id].nil?
|
||||||
|
end
|
||||||
|
|
||||||
|
# Logs current user out of GreenLight.
|
||||||
|
def logout
|
||||||
|
session.delete(:user_id) if current_user
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check if the user is using local accounts
|
||||||
|
def auth_changed_to_local?(user)
|
||||||
|
Rails.configuration.loadbalanced_configuration && user.social_uid.present? && allow_greenlight_accounts?
|
||||||
|
end
|
||||||
|
|
||||||
|
# Check if the user exists under the same email with no social uid and that social accounts are allowed
|
||||||
|
def auth_changed_to_social?(email)
|
||||||
|
Rails.configuration.loadbalanced_configuration &&
|
||||||
|
User.exists?(email: email, provider: @user_domain, social_uid: nil) &&
|
||||||
|
!allow_greenlight_accounts?
|
||||||
|
end
|
||||||
|
|
||||||
|
# Sets the initial user role based on the email mapping
|
||||||
|
def initial_user_role(email)
|
||||||
|
mapping = @settings.get_value("Email Mapping")
|
||||||
|
return "user" unless mapping.present?
|
||||||
|
|
||||||
|
mapping.split(",").each do |map|
|
||||||
|
email_role = map.split("=")
|
||||||
|
return email_role[1] if email.ends_with?(email_role[0])
|
||||||
|
end
|
||||||
|
|
||||||
|
"user" # default to user if role not found
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Migrates all of the twitter users rooms to the new account
|
||||||
|
def migrate_twitter_user(user)
|
||||||
|
if !session["old_twitter_user_id"].nil? && user.provider != "twitter"
|
||||||
|
old_user = User.find(session["old_twitter_user_id"])
|
||||||
|
|
||||||
|
old_user.rooms.each do |room|
|
||||||
|
room.owner = user
|
||||||
|
|
||||||
|
room.name = "Old " + room.name if room.id == old_user.main_room.id
|
||||||
|
|
||||||
|
room.save!
|
||||||
|
end
|
||||||
|
|
||||||
|
# Query for the old user again so the migrated rooms don't get deleted
|
||||||
|
old_user.reload
|
||||||
|
old_user.destroy!
|
||||||
|
|
||||||
|
session["old_twitter_user_id"] = nil
|
||||||
|
|
||||||
|
flash[:success] = I18n.t("registration.deprecated.merge_success")
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,120 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
# terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
# Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
# version.
|
||||||
|
#
|
||||||
|
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
require 'bigbluebutton_api'
|
||||||
|
|
||||||
|
module BbbServer
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
include BbbApi
|
||||||
|
|
||||||
|
META_LISTED = "gl-listed"
|
||||||
|
|
||||||
|
# Checks if a room is running on the BigBlueButton server.
|
||||||
|
def room_running?(bbb_id)
|
||||||
|
bbb_server.is_meeting_running?(bbb_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns a list of all running meetings
|
||||||
|
def all_running_meetings
|
||||||
|
bbb_server.get_meetings
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_recordings(meeting_id)
|
||||||
|
bbb_server.get_recordings(meetingID: meeting_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
def get_multiple_recordings(meeting_ids)
|
||||||
|
bbb_server.get_recordings(meetingID: meeting_ids)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns a URL to join a user into a meeting.
|
||||||
|
def join_path(room, name, options = {}, uid = nil)
|
||||||
|
# Create the meeting, even if it's running
|
||||||
|
start_session(room, options)
|
||||||
|
|
||||||
|
# Determine the password to use when joining.
|
||||||
|
password = options[:user_is_moderator] ? room.moderator_pw : room.attendee_pw
|
||||||
|
|
||||||
|
# Generate the join URL.
|
||||||
|
join_opts = {}
|
||||||
|
join_opts[:userID] = uid if uid
|
||||||
|
join_opts[:join_via_html5] = true
|
||||||
|
|
||||||
|
bbb_server.join_meeting_url(room.bbb_id, name, password, join_opts)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Creates a meeting on the BigBlueButton server.
|
||||||
|
def start_session(room, options = {})
|
||||||
|
create_options = {
|
||||||
|
record: options[:record].to_s,
|
||||||
|
logoutURL: options[:meeting_logout_url] || '',
|
||||||
|
moderatorPW: room.moderator_pw,
|
||||||
|
attendeePW: room.attendee_pw,
|
||||||
|
moderatorOnlyMessage: options[:moderator_message],
|
||||||
|
"meta_#{META_LISTED}": options[:recording_default_visibility] || false,
|
||||||
|
"meta_bbb-origin-version": Greenlight::Application::VERSION,
|
||||||
|
"meta_bbb-origin": "Greenlight",
|
||||||
|
"meta_bbb-origin-server-name": options[:host]
|
||||||
|
}
|
||||||
|
|
||||||
|
create_options[:muteOnStart] = options[:mute_on_start] if options[:mute_on_start]
|
||||||
|
create_options[:guestPolicy] = "ASK_MODERATOR" if options[:require_moderator_approval]
|
||||||
|
|
||||||
|
# Send the create request.
|
||||||
|
begin
|
||||||
|
meeting = if room.presentation.attached?
|
||||||
|
modules = BigBlueButton::BigBlueButtonModules.new
|
||||||
|
url = rails_blob_url(room.presentation).gsub("&", "%26")
|
||||||
|
logger.info("Support: Room #{room.uid} starting using presentation: #{url}")
|
||||||
|
modules.add_presentation(:url, url)
|
||||||
|
bbb_server.create_meeting(room.name, room.bbb_id, create_options, modules)
|
||||||
|
else
|
||||||
|
bbb_server.create_meeting(room.name, room.bbb_id, create_options)
|
||||||
|
end
|
||||||
|
|
||||||
|
unless meeting[:messageKey] == 'duplicateWarning'
|
||||||
|
room.update_attributes(sessions: room.sessions + 1, last_session: DateTime.now)
|
||||||
|
end
|
||||||
|
rescue BigBlueButton::BigBlueButtonException => e
|
||||||
|
puts "BigBlueButton failed on create: #{e.key}: #{e.message}"
|
||||||
|
raise e
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Gets the number of recordings for this room
|
||||||
|
def recording_count(bbb_id)
|
||||||
|
bbb_server.get_recordings(meetingID: bbb_id)[:recordings].length
|
||||||
|
end
|
||||||
|
|
||||||
|
# Update a recording from a room
|
||||||
|
def update_recording(record_id, meta)
|
||||||
|
meta[:recordID] = record_id
|
||||||
|
bbb_server.send_api_request("updateRecordings", meta)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Deletes a recording from a room.
|
||||||
|
def delete_recording(record_id)
|
||||||
|
bbb_server.delete_recordings(record_id)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Deletes all recordings associated with the room.
|
||||||
|
def delete_all_recordings(bbb_id)
|
||||||
|
record_ids = bbb_server.get_recordings(meetingID: bbb_id)[:recordings].pluck(:recordID)
|
||||||
|
bbb_server.delete_recordings(record_ids) unless record_ids.empty?
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,152 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
# terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
# Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
# version.
|
||||||
|
#
|
||||||
|
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
module Emailer
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
# Sends account activation email.
|
||||||
|
def send_activation_email(user, token)
|
||||||
|
begin
|
||||||
|
return unless Rails.configuration.enable_email_verification
|
||||||
|
|
||||||
|
UserMailer.verify_email(user, user_verification_link(token), @settings).deliver
|
||||||
|
rescue => e
|
||||||
|
logger.error "Support: Error in email delivery: #{e}"
|
||||||
|
flash[:alert] = I18n.t(params[:message], default: I18n.t("delivery_error"))
|
||||||
|
else
|
||||||
|
flash[:success] = I18n.t("email_sent", email_type: t("verify.verification"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Sends password reset email.
|
||||||
|
def send_password_reset_email(user, token)
|
||||||
|
begin
|
||||||
|
return unless Rails.configuration.enable_email_verification
|
||||||
|
|
||||||
|
UserMailer.password_reset(user, reset_link(token), @settings).deliver_now
|
||||||
|
rescue => e
|
||||||
|
logger.error "Support: Error in email delivery: #{e}"
|
||||||
|
flash[:alert] = I18n.t(params[:message], default: I18n.t("delivery_error"))
|
||||||
|
else
|
||||||
|
flash[:success] = I18n.t("email_sent", email_type: t("reset_password.subtitle"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_user_promoted_email(user, role)
|
||||||
|
begin
|
||||||
|
return unless Rails.configuration.enable_email_verification
|
||||||
|
|
||||||
|
UserMailer.user_promoted(user, role, root_url, @settings).deliver_now
|
||||||
|
rescue => e
|
||||||
|
logger.error "Support: Error in email delivery: #{e}"
|
||||||
|
flash[:alert] = I18n.t(params[:message], default: I18n.t("delivery_error"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_user_demoted_email(user, role)
|
||||||
|
begin
|
||||||
|
return unless Rails.configuration.enable_email_verification
|
||||||
|
|
||||||
|
UserMailer.user_demoted(user, role, root_url, @settings).deliver_now
|
||||||
|
rescue => e
|
||||||
|
logger.error "Support: Error in email delivery: #{e}"
|
||||||
|
flash[:alert] = I18n.t(params[:message], default: I18n.t("delivery_error"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Sends inivitation to join
|
||||||
|
def send_invitation_email(name, email, token)
|
||||||
|
begin
|
||||||
|
return unless Rails.configuration.enable_email_verification
|
||||||
|
|
||||||
|
UserMailer.invite_email(name, email, invitation_link(token), @settings).deliver_now
|
||||||
|
rescue => e
|
||||||
|
logger.error "Support: Error in email delivery: #{e}"
|
||||||
|
flash[:alert] = I18n.t(params[:message], default: I18n.t("delivery_error"))
|
||||||
|
else
|
||||||
|
flash[:success] = I18n.t("administrator.flash.invite", email: email)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_user_approved_email(user)
|
||||||
|
begin
|
||||||
|
return unless Rails.configuration.enable_email_verification
|
||||||
|
|
||||||
|
UserMailer.approve_user(user, root_url, @settings).deliver_now
|
||||||
|
rescue => e
|
||||||
|
logger.error "Support: Error in email delivery: #{e}"
|
||||||
|
flash[:alert] = I18n.t(params[:message], default: I18n.t("delivery_error"))
|
||||||
|
else
|
||||||
|
flash[:success] = I18n.t("email_sent", email_type: t("verify.verification"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_approval_user_signup_email(user)
|
||||||
|
begin
|
||||||
|
return unless Rails.configuration.enable_email_verification
|
||||||
|
admin_emails = admin_emails()
|
||||||
|
UserMailer.approval_user_signup(user, admins_url(tab: "pending"),
|
||||||
|
admin_emails, @settings).deliver_now unless admin_emails.empty?
|
||||||
|
rescue => e
|
||||||
|
logger.error "Support: Error in email delivery: #{e}"
|
||||||
|
flash[:alert] = I18n.t(params[:message], default: I18n.t("delivery_error"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def send_invite_user_signup_email(user)
|
||||||
|
begin
|
||||||
|
return unless Rails.configuration.enable_email_verification
|
||||||
|
|
||||||
|
admin_emails = admin_emails()
|
||||||
|
UserMailer.invite_user_signup(user, admins_url, admin_emails, @settings).deliver_now unless admin_emails.empty?
|
||||||
|
rescue => e
|
||||||
|
logger.error "Support: Error in email delivery: #{e}"
|
||||||
|
flash[:alert] = I18n.t(params[:message], default: I18n.t("delivery_error"))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Returns the link the user needs to click to verify their account
|
||||||
|
def user_verification_link(token)
|
||||||
|
edit_account_activation_url(token: token)
|
||||||
|
end
|
||||||
|
|
||||||
|
def admin_emails
|
||||||
|
roles = Role.where(provider: @user_domain, role_permissions: { name: "can_manage_users", value: "true" })
|
||||||
|
.pluck(:name)
|
||||||
|
|
||||||
|
admins = User.with_role(roles - ["super_admin"])
|
||||||
|
|
||||||
|
admins = admins.where(provider: @user_domain) if Rails.configuration.loadbalanced_configuration
|
||||||
|
|
||||||
|
admins.collect(&:email).join(",")
|
||||||
|
end
|
||||||
|
|
||||||
|
def reset_link(token)
|
||||||
|
edit_password_reset_url(token)
|
||||||
|
end
|
||||||
|
|
||||||
|
def invitation_link(token)
|
||||||
|
if allow_greenlight_accounts?
|
||||||
|
signup_url(invite_token: token)
|
||||||
|
else
|
||||||
|
root_url(invite_token: token)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,5 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
module Errors
|
||||||
|
class UnsafeHostError < StandardError; end
|
||||||
|
end
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
# terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
# Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
# version.
|
||||||
|
#
|
||||||
|
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
module Joiner
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
# Displays the join room page to the user
|
||||||
|
def show_user_join
|
||||||
|
# Get users name
|
||||||
|
@name = if current_user
|
||||||
|
current_user.name
|
||||||
|
elsif cookies.encrypted[:greenlight_name]
|
||||||
|
cookies.encrypted[:greenlight_name]
|
||||||
|
else
|
||||||
|
""
|
||||||
|
end
|
||||||
|
|
||||||
|
@search, @order_column, @order_direction, pub_recs =
|
||||||
|
public_recordings(@room.bbb_id, params.permit(:search, :column, :direction), true)
|
||||||
|
|
||||||
|
@pagy, @public_recordings = pagy_array(pub_recs)
|
||||||
|
|
||||||
|
render :join
|
||||||
|
end
|
||||||
|
|
||||||
|
# create or update cookie to track the three most recent rooms a user joined
|
||||||
|
def save_recent_rooms
|
||||||
|
if current_user
|
||||||
|
recently_joined_rooms = cookies.encrypted["#{current_user.uid}_recently_joined_rooms"].to_a
|
||||||
|
cookies.encrypted["#{current_user.uid}_recently_joined_rooms"] =
|
||||||
|
recently_joined_rooms.prepend(@room.id).uniq[0..2]
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def join_room(opts)
|
||||||
|
@room_settings = JSON.parse(@room[:room_settings])
|
||||||
|
|
||||||
|
if room_running?(@room.bbb_id) || @room.owned_by?(current_user) || room_setting_with_config("anyoneCanStart")
|
||||||
|
|
||||||
|
# Determine if the user needs to join as a moderator.
|
||||||
|
opts[:user_is_moderator] = @room.owned_by?(current_user) || room_setting_with_config("joinModerator") || @shared_room
|
||||||
|
opts[:record] = record_meeting
|
||||||
|
opts[:require_moderator_approval] = room_setting_with_config("requireModeratorApproval")
|
||||||
|
opts[:mute_on_start] = room_setting_with_config("muteOnStart")
|
||||||
|
|
||||||
|
if current_user
|
||||||
|
redirect_to join_path(@room, current_user.name, opts, current_user.uid)
|
||||||
|
else
|
||||||
|
join_name = params[:join_name] || params[@room.invite_path][:join_name]
|
||||||
|
|
||||||
|
redirect_to join_path(@room, join_name, opts, fetch_guest_id)
|
||||||
|
end
|
||||||
|
else
|
||||||
|
search_params = params[@room.invite_path] || params
|
||||||
|
@search, @order_column, @order_direction, pub_recs =
|
||||||
|
public_recordings(@room.bbb_id, search_params.permit(:search, :column, :direction), true)
|
||||||
|
|
||||||
|
@pagy, @public_recordings = pagy_array(pub_recs)
|
||||||
|
|
||||||
|
# They need to wait until the meeting begins.
|
||||||
|
render :wait
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def incorrect_user_domain
|
||||||
|
Rails.configuration.loadbalanced_configuration && @room.owner.provider != @user_domain
|
||||||
|
end
|
||||||
|
|
||||||
|
# Default, unconfigured meeting options.
|
||||||
|
def default_meeting_options
|
||||||
|
invite_msg = I18n.t("invite_message")
|
||||||
|
{
|
||||||
|
user_is_moderator: false,
|
||||||
|
meeting_logout_url: request.base_url + logout_room_path(@room),
|
||||||
|
moderator_message: "#{invite_msg}<br> #{request.base_url + room_path(@room)}",
|
||||||
|
host: request.host,
|
||||||
|
recording_default_visibility: @settings.get_value("Default Recording Visibility") == "public"
|
||||||
|
}
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
def fetch_guest_id
|
||||||
|
return cookies[:guest_id] if cookies[:guest_id].present?
|
||||||
|
|
||||||
|
guest_id = "gl-guest-#{SecureRandom.hex(12)}"
|
||||||
|
|
||||||
|
cookies[:guest_id] = {
|
||||||
|
value: guest_id,
|
||||||
|
expires: 1.day.from_now
|
||||||
|
}
|
||||||
|
|
||||||
|
guest_id
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,78 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
# terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
# Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
# version.
|
||||||
|
#
|
||||||
|
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
module Populator
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
# Returns a list of users that are in the same context of the current user
|
||||||
|
def manage_users_list
|
||||||
|
initial_list = case @tab
|
||||||
|
when "active"
|
||||||
|
User.without_role([:pending, :denied])
|
||||||
|
when "deleted"
|
||||||
|
User.deleted
|
||||||
|
when "pending"
|
||||||
|
User.with_role(:pending)
|
||||||
|
when "denied"
|
||||||
|
User.with_role(:denied)
|
||||||
|
else
|
||||||
|
User.all
|
||||||
|
end
|
||||||
|
|
||||||
|
initial_list = initial_list.with_role(@role.name) if @role.present?
|
||||||
|
|
||||||
|
initial_list = initial_list.without_role(:super_admin)
|
||||||
|
|
||||||
|
initial_list = initial_list.where(provider: @user_domain) if Rails.configuration.loadbalanced_configuration
|
||||||
|
|
||||||
|
initial_list.where.not(id: current_user.id)
|
||||||
|
.admins_search(@search)
|
||||||
|
.admins_order(@order_column, @order_direction)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns a list of rooms that are in the same context of the current user
|
||||||
|
def server_rooms_list
|
||||||
|
if Rails.configuration.loadbalanced_configuration
|
||||||
|
Room.includes(:owner).where(users: { provider: @user_domain })
|
||||||
|
.admins_search(@search)
|
||||||
|
.admins_order(@order_column, @order_direction, @running_room_bbb_ids)
|
||||||
|
else
|
||||||
|
Room.includes(:owner).admins_search(@search).admins_order(@order_column, @order_direction, @running_room_bbb_ids)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns list of rooms needed to get the recordings on the server
|
||||||
|
def rooms_list_for_recordings
|
||||||
|
if Rails.configuration.loadbalanced_configuration
|
||||||
|
Room.includes(:owner).where(users: { provider: @user_domain }).pluck(:bbb_id)
|
||||||
|
else
|
||||||
|
Room.pluck(:bbb_id)
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns a list off all current invitations
|
||||||
|
def invited_users_list
|
||||||
|
list = if Rails.configuration.loadbalanced_configuration
|
||||||
|
Invitation.where(provider: @user_domain)
|
||||||
|
else
|
||||||
|
Invitation.all
|
||||||
|
end
|
||||||
|
|
||||||
|
list.admins_search(@search).order(updated_at: :desc)
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,132 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
# terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
# Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
# version.
|
||||||
|
#
|
||||||
|
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
module Recorder
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
include RecordingsHelper
|
||||||
|
|
||||||
|
# Fetches all recordings for a room.
|
||||||
|
def recordings(room_bbb_id, search_params = {}, ret_search_params = false)
|
||||||
|
res = get_recordings(room_bbb_id)
|
||||||
|
|
||||||
|
format_recordings(res, search_params, ret_search_params)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Fetches a rooms public recordings.
|
||||||
|
def public_recordings(room_bbb_id, search_params = {}, ret_search_params = false)
|
||||||
|
search, order_col, order_dir, recs = recordings(room_bbb_id, search_params, ret_search_params)
|
||||||
|
[search, order_col, order_dir, recs.select { |r| r[:metadata][:"gl-listed"] == "true" }]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Makes paginated API calls to get recordings
|
||||||
|
def all_recordings(room_bbb_ids, search_params = {}, ret_search_params = false, search_name = false)
|
||||||
|
res = { recordings: [] }
|
||||||
|
|
||||||
|
until room_bbb_ids.empty?
|
||||||
|
# bbb.get_recordings returns an object
|
||||||
|
# take only the array portion of the object that is returned
|
||||||
|
full_res = get_multiple_recordings(room_bbb_ids.pop(Rails.configuration.pagination_number))
|
||||||
|
res[:recordings].push(*full_res[:recordings])
|
||||||
|
end
|
||||||
|
|
||||||
|
format_recordings(res, search_params, ret_search_params, search_name)
|
||||||
|
end
|
||||||
|
|
||||||
|
# Format, filter, and sort recordings to match their current use in the app
|
||||||
|
def format_recordings(api_res, search_params, ret_search_params, search_name = false)
|
||||||
|
search = search_params[:search] || ""
|
||||||
|
order_col = search_params[:column] && search_params[:direction] != "none" ? search_params[:column] : "end_time"
|
||||||
|
order_dir = search_params[:column] && search_params[:direction] != "none" ? search_params[:direction] : "desc"
|
||||||
|
|
||||||
|
search = search.downcase
|
||||||
|
|
||||||
|
api_res[:recordings].each do |r|
|
||||||
|
next if r.key?(:error)
|
||||||
|
# Format playbacks in a more pleasant way.
|
||||||
|
r[:playbacks] = if !r[:playback] || !r[:playback][:format]
|
||||||
|
[]
|
||||||
|
elsif r[:playback][:format].is_a?(Array)
|
||||||
|
r[:playback][:format]
|
||||||
|
else
|
||||||
|
[r[:playback][:format]]
|
||||||
|
end
|
||||||
|
r.delete(:playback)
|
||||||
|
end
|
||||||
|
|
||||||
|
recs = filter_recordings(api_res, search, search_name)
|
||||||
|
recs = sort_recordings(recs, order_col, order_dir)
|
||||||
|
|
||||||
|
if ret_search_params
|
||||||
|
[search, order_col, order_dir, recs]
|
||||||
|
else
|
||||||
|
recs
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def filter_recordings(api_res, search, search_name = false)
|
||||||
|
api_res[:recordings].select do |r|
|
||||||
|
(!r[:metadata].nil? && ((!r[:metadata][:name].nil? &&
|
||||||
|
r[:metadata][:name].downcase.include?(search)) ||
|
||||||
|
(r[:metadata][:"gl-listed"] == "true" && search == "public") ||
|
||||||
|
(r[:metadata][:"gl-listed"] == "false" && search == "unlisted"))) ||
|
||||||
|
((r[:metadata].nil? || r[:metadata][:name].nil?) &&
|
||||||
|
r[:name].downcase.include?(search)) ||
|
||||||
|
r[:participants].include?(search) ||
|
||||||
|
!r[:playbacks].select { |p| p[:type].downcase.include?(search) }.empty? ||
|
||||||
|
(search_name && recording_owner(r[:meetingID]).downcase.include?(search))
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
def sort_recordings(recs, order_col, order_dir)
|
||||||
|
recs = case order_col
|
||||||
|
when "end_time"
|
||||||
|
recs.sort_by { |r| r[:endTime] }
|
||||||
|
when "name"
|
||||||
|
recs.sort_by do |r|
|
||||||
|
if !r[:metadata].nil? && !r[:metadata][:name].nil?
|
||||||
|
r[:metadata][:name].downcase
|
||||||
|
else
|
||||||
|
r[:name].downcase
|
||||||
|
end
|
||||||
|
end
|
||||||
|
when "length"
|
||||||
|
recs.sort_by { |r| r[:playbacks].reject { |p| p[:type] == "statistics" }.first[:length] }
|
||||||
|
when "users"
|
||||||
|
recs.sort_by { |r| r[:participants] }
|
||||||
|
when "visibility"
|
||||||
|
recs.sort_by { |r| r[:metadata][:"gl-listed"] }
|
||||||
|
when "formats"
|
||||||
|
recs.sort_by { |r| r[:playbacks].first[:type].downcase }
|
||||||
|
else
|
||||||
|
recs.sort_by { |r| r[:endTime] }
|
||||||
|
end
|
||||||
|
|
||||||
|
if order_dir == 'asc'
|
||||||
|
recs
|
||||||
|
else
|
||||||
|
recs.reverse
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
private
|
||||||
|
|
||||||
|
# Gets the email of the room owner to which the recording belongs to
|
||||||
|
def recording_owner(room_id)
|
||||||
|
Room.find_by(bbb_id: room_id).owner.email.presence || Room.find_by(bbb_id: room_id).owner.username
|
||||||
|
end
|
||||||
|
end
|
||||||
@@ -0,0 +1,75 @@
|
|||||||
|
# frozen_string_literal: true
|
||||||
|
|
||||||
|
# BigBlueButton open source conferencing system - http://www.bigbluebutton.org/.
|
||||||
|
#
|
||||||
|
# Copyright (c) 2018 BigBlueButton Inc. and by respective authors (see below).
|
||||||
|
#
|
||||||
|
# This program is free software; you can redistribute it and/or modify it under the
|
||||||
|
# terms of the GNU Lesser General Public License as published by the Free Software
|
||||||
|
# Foundation; either version 3.0 of the License, or (at your option) any later
|
||||||
|
# version.
|
||||||
|
#
|
||||||
|
# BigBlueButton is distributed in the hope that it will be useful, but WITHOUT ANY
|
||||||
|
# WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
|
||||||
|
# PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
|
||||||
|
#
|
||||||
|
# You should have received a copy of the GNU Lesser General Public License along
|
||||||
|
# with BigBlueButton; if not, see <http://www.gnu.org/licenses/>.
|
||||||
|
|
||||||
|
module Registrar
|
||||||
|
extend ActiveSupport::Concern
|
||||||
|
|
||||||
|
def approval_registration
|
||||||
|
@settings.get_value("Registration Method") == Rails.configuration.registration_methods[:approval]
|
||||||
|
end
|
||||||
|
|
||||||
|
def invite_registration
|
||||||
|
@settings.get_value("Registration Method") == Rails.configuration.registration_methods[:invite]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Returns a hash containing whether the user has been invited and if they
|
||||||
|
# signed up with the same email that they were invited with
|
||||||
|
def check_user_invited(email, token, domain)
|
||||||
|
return { present: true, verified: false } unless invite_registration
|
||||||
|
return { present: false, verified: false } if token.nil?
|
||||||
|
|
||||||
|
invite = Invitation.valid.find_by(invite_token: token, provider: domain)
|
||||||
|
if invite.present?
|
||||||
|
# Check if they used the same email to sign up
|
||||||
|
same_email = email.casecmp(invite.email).zero?
|
||||||
|
invite.destroy
|
||||||
|
{ present: true, verified: same_email }
|
||||||
|
else
|
||||||
|
{ present: false, verified: false }
|
||||||
|
end
|
||||||
|
end
|
||||||
|
|
||||||
|
# Checks if the user passes the requirements to be invited
|
||||||
|
def passes_invite_reqs
|
||||||
|
# check if user needs to be invited and IS invited
|
||||||
|
invitation = check_user_invited(@user.email, session[:invite_token], @user_domain)
|
||||||
|
|
||||||
|
@user.email_verified = true if invitation[:verified]
|
||||||
|
|
||||||
|
invitation[:present]
|
||||||
|
end
|
||||||
|
|
||||||
|
# Add validation errors to model if they exist
|
||||||
|
def valid_user_or_captcha
|
||||||
|
valid_user = @user.valid?
|
||||||
|
valid_captcha = Rails.configuration.recaptcha_enabled ? verify_recaptcha(model: @user) : true
|
||||||
|
|
||||||
|
logger.error("Support: #{@user.email} creation failed: User params are not valid.") unless valid_user
|
||||||
|
|
||||||
|
valid_user && valid_captcha
|
||||||
|
end
|
||||||
|
|
||||||
|
# Checks if the user trying to sign in with twitter account
|
||||||
|
def check_if_twitter_account(log_out = false)
|
||||||
|
unless params[:old_twitter_user_id].nil? && session[:old_twitter_user_id].nil?
|
||||||
|
logout if log_out
|
||||||
|
flash.now[:alert] = I18n.t("registration.deprecated.new_signin")
|
||||||
|
session[:old_twitter_user_id] = params[:old_twitter_user_id] unless params[:old_twitter_user_id].nil?
|
||||||
|
end
|
||||||
|
end
|
||||||
|
end
|
||||||