Parcourir la source

[enhancement] Show who is currently viewing a document

version-14
Anand Doshi il y a 9 ans
Parent
révision
40f679322b
31 fichiers modifiés avec 402 ajouts et 163 suppressions
  1. +3
    -1
      frappe/public/build.json
  2. +1
    -1
      frappe/public/css/calendar.css
  3. +4
    -4
      frappe/public/css/common.css
  4. +12
    -12
      frappe/public/css/desk.css
  5. +3
    -3
      frappe/public/css/desktop.css
  6. +11
    -11
      frappe/public/css/docs.css
  7. +6
    -6
      frappe/public/css/form.css
  8. +1
    -1
      frappe/public/css/form_grid.css
  9. +1
    -1
      frappe/public/css/gantt.css
  10. +1
    -1
      frappe/public/css/indicator.css
  11. +4
    -4
      frappe/public/css/list.css
  12. +7
    -7
      frappe/public/css/mobile.css
  13. +1
    -1
      frappe/public/css/module.css
  14. +4
    -4
      frappe/public/css/navbar.css
  15. +3
    -3
      frappe/public/css/offcanvas-website.css
  16. +2
    -2
      frappe/public/css/offcanvas.css
  17. +26
    -17
      frappe/public/css/sidebar.css
  18. +3
    -3
      frappe/public/css/slickgrid.css
  19. +1
    -1
      frappe/public/css/tree.css
  20. +13
    -13
      frappe/public/css/website.css
  21. +5
    -0
      frappe/public/js/frappe/form/form_sidebar.html
  22. +76
    -0
      frappe/public/js/frappe/form/form_viewers.js
  23. +30
    -29
      frappe/public/js/frappe/form/share.js
  24. +8
    -0
      frappe/public/js/frappe/form/sidebar.js
  25. +11
    -0
      frappe/public/js/frappe/form/users_in_sidebar.html
  26. +6
    -0
      frappe/public/js/frappe/model/model.js
  27. +47
    -12
      frappe/public/js/frappe/socketio_client.js
  28. +3
    -0
      frappe/public/js/frappe/views/formview.js
  29. +1
    -1
      frappe/public/js/legacy/form.js
  30. +8
    -4
      frappe/public/less/sidebar.less
  31. +100
    -21
      socketio.js

+ 3
- 1
frappe/public/build.json Voir le fichier

@@ -65,7 +65,7 @@
"public/js/frappe/ui/messages.js",

"public/js/frappe/request.js",
"public/js/frappe/socket.js",
"public/js/frappe/socketio_client.js",
"public/js/frappe/router.js",
"public/js/frappe/defaults.js",
"public/js/lib/microtemplate.js",
@@ -149,8 +149,10 @@
"public/js/frappe/form/print_layout.html",
"public/js/frappe/form/print.js",
"public/js/frappe/form/sidebar.js",
"public/js/frappe/form/users_in_sidebar.html",
"public/js/frappe/form/share.js",
"public/js/frappe/form/set_sharing.html",
"public/js/frappe/form/form_viewers.js",
"public/js/frappe/form/form_sidebar.html",

"public/js/frappe/form/footer/form_footer.html",


+ 1
- 1
frappe/public/css/calendar.css Voir le fichier

@@ -6,7 +6,7 @@
margin-left: -1px;
}
th.fc-widget-header {
background-color: #f7fafc;
background-color: #F7FAFC;
color: #8C99A5;
}
.fc-unthemed th,


+ 4
- 4
frappe/public/css/common.css Voir le fichier

@@ -18,10 +18,10 @@ p {
margin: 10px 0px;
}
.text-color {
color: #36414c !important;
color: #36414C !important;
}
.text-muted {
color: #8d99a6 !important;
color: #8D99A6 !important;
}
.text-extra-muted {
color: #d1d8dd !important;
@@ -82,7 +82,7 @@ a.text-extra-muted {
}
kbd {
color: inherit;
background-color: #f0f4f7;
background-color: #F0F4F7;
}
.btn [class^="icon-"],
.nav [class^="icon-"],
@@ -186,7 +186,7 @@ a.badge-hover:active .badge {
transform: translate(-50%, -50%);
-webkit-transform: translate(-50%, -50%);
text-align: center;
color: #36414c !important;
color: #36414C !important;
}
#freeze.dark {
background-color: #334143;


+ 12
- 12
frappe/public/css/desk.css Voir le fichier

@@ -18,10 +18,10 @@ p {
margin: 10px 0px;
}
.text-color {
color: #36414c !important;
color: #36414C !important;
}
.text-muted {
color: #8d99a6 !important;
color: #8D99A6 !important;
}
.text-extra-muted {
color: #d1d8dd !important;
@@ -82,7 +82,7 @@ a.text-extra-muted {
}
kbd {
color: inherit;
background-color: #f0f4f7;
background-color: #F0F4F7;
}
.btn [class^="icon-"],
.nav [class^="icon-"],
@@ -186,7 +186,7 @@ a.badge-hover:active .badge {
transform: translate(-50%, -50%);
-webkit-transform: translate(-50%, -50%);
text-align: center;
color: #36414c !important;
color: #36414C !important;
}
#freeze.dark {
background-color: #334143;
@@ -237,11 +237,11 @@ a.form-link {
display: none;
}
.link-primary {
color: #5e64ff;
color: #5E64FF;
}
.link-primary:hover,
.link-primary:focus {
color: #5e64ff;
color: #5E64FF;
}
.ui-autocomplete .link-option {
font-weight: normal;
@@ -358,13 +358,13 @@ ul.linked-with-list li {
.ui-autocomplete .ui-state-focus,
.ui-datepicker .ui-state-hover,
.ui-autocomplete .ui-state-hover {
background-color: #f0f4f7 !important;
color: #36414c !important;
background-color: #F0F4F7 !important;
color: #36414C !important;
text-shadow: none !important;
}
.ui-datepicker .ui-state-active,
.ui-autocomplete .ui-state-active {
background-color: #5e64ff !important;
background-color: #5E64FF !important;
color: #fff !important;
text-shadow: none !important;
}
@@ -390,7 +390,7 @@ ul.linked-with-list li {
}
}
.panel-bg {
background-color: #f7fafc;
background-color: #F7FAFC;
}
.light-bg {
background-color: #fafbfc;
@@ -452,7 +452,7 @@ ul.linked-with-list li {
.msg-box {
padding: 30px 15px;
text-align: center;
color: #8d99a6;
color: #8D99A6;
}
.no-border {
border: none !important;
@@ -483,7 +483,7 @@ ul.linked-with-list li {
border-top: 1px solid #d1d8dd;
}
.file-upload .input-group-addon {
color: #8d99a6;
color: #8D99A6;
font-size: 12px;
}
.file-upload .file-upload-or {


+ 3
- 3
frappe/public/css/desktop.css Voir le fichier

@@ -75,11 +75,11 @@ body[data-route="desktop"] .navbar-default {
letter-spacing: normal;
}
.app-icon:hover path {
fill: #ffffff;
fill: #fff;
}
.app-icon:hover i,
.app-icon:hover {
color: #ffffff;
color: #fff;
}
.app-icon-small {
padding: 12px;
@@ -168,7 +168,7 @@ body[data-route="desktop"] .navbar-default {
}
.desktop-list-item:hover,
.desktop-list-item:focus {
background-color: #f7fafc;
background-color: #F7FAFC;
}
.desktop-list-item h4 {
display: inline-block;


+ 11
- 11
frappe/public/css/docs.css Voir le fichier

@@ -38,7 +38,7 @@ body {
.offcanvas .sidebar .divider {
height: 1px;
overflow: hidden;
background-color: #ebeff2;
background-color: #EBEFF2;
width: 100%;
margin: 0px;
}
@@ -52,7 +52,7 @@ body {
.offcanvas .sidebar .dropdown-menu > li > a:focus,
.offcanvas .sidebar .sidebar-menu > li > a:active,
.offcanvas .sidebar .dropdown-menu > li > a:active {
background-color: #f0f4f7;
background-color: #F0F4F7;
}
@media (max-width: 767px) {
.page-content {
@@ -124,7 +124,7 @@ body {
}
.offcanvas .sidebar-label {
text-transform: uppercase;
color: #8d99a6;
color: #8D99A6;
font-size: 85%;
margin: 0px;
font-weight: bold;
@@ -175,7 +175,7 @@ body {
background-color: rgba(255, 255, 255, 0.9);
}
.navbar .navbar-search-icon {
color: #6c7680;
color: #6C7680;
font-size: inherit;
position: relative;
right: 24px;
@@ -194,7 +194,7 @@ body {
}
.navbar-center {
float: left;
color: #6c7680;
color: #6C7680;
}
#navbar-breadcrumbs > li > a:before {
font-family: FontAwesome;
@@ -211,12 +211,12 @@ body {
top: 3px;
content: "\f105";
margin-right: 10px;
color: #c0c9d2;
color: #C0C9D2;
}
#navbar-breadcrumbs > li > a:hover:before,
#navbar-breadcrumbs > li > a:focus:before,
#navbar-breadcrumbs > li > a:active:before {
color: #36414c;
color: #36414C;
}
#navbar-breadcrumbs > li > a {
padding: 6px 15px 10px 0px;
@@ -245,7 +245,7 @@ body {
}
.breadcrumb {
line-height: 1.5em;
color: #8d99a6;
color: #8D99A6;
background-color: transparent;
margin-bottom: 10px;
padding: 0px;
@@ -261,7 +261,7 @@ a,
a:hover,
a:focus,
a:visited {
color: #5e64ff;
color: #5E64FF;
}
a.btn-primary {
color: #fff;
@@ -296,7 +296,7 @@ h2 {
border-top: 1px solid #d1d8dd;
}
.docs-footer a {
color: #8d99a6;
color: #8D99A6;
}
.docs-footer li {
display: inline-block;
@@ -312,7 +312,7 @@ h2 {
font-weight: 400;
}
.jumbotron p {
font-color: #8d99a6 !important;
font-color: #8D99A6 !important;
}
.browser-image {
min-height: 300px;


+ 6
- 6
frappe/public/css/form.css Voir le fichier

@@ -22,7 +22,7 @@
.form-clickable-section {
border-top: 1px solid #d1d8dd;
padding: 10px 15px;
background-color: #f7fafc;
background-color: #F7FAFC;
}
.form-page.second-page {
border-top: 1px solid #d1d8dd;
@@ -56,7 +56,7 @@
}
.form-section:not(:last-child),
.form-inner-toolbar {
border-bottom: 1px solid #ebeff2;
border-bottom: 1px solid #EBEFF2;
}
.empty-section {
display: none !important;
@@ -125,7 +125,7 @@
}
.control-label,
.grid-heading-row {
color: #8d99a6;
color: #8D99A6;
font-size: 12px;
}
.control-label {
@@ -186,7 +186,7 @@ select.form-control {
margin-top: -3px;
margin-left: 1px;
font-weight: 500;
color: #8d99a6;
color: #8D99A6;
}
@media (min-width: 768px) {
.layout-main .form-column.col-sm-12 > form > .input-max-width {
@@ -208,7 +208,7 @@ select.form-control {
padding: 15px 15px 15px 0px;
}
.form-column {
border-bottom: 1px solid #ebeff2;
border-bottom: 1px solid #EBEFF2;
}
.form-column:last-child {
border-bottom: 0px;
@@ -229,7 +229,7 @@ select.form-control {
}
.form-page .frappe-control {
padding: 7px 15px;
border-bottom: 1px solid #ebeff2;
border-bottom: 1px solid #EBEFF2;
margin: 0px -15px;
}
.form-page .frappe-control .link-btn {


+ 1
- 1
frappe/public/css/form_grid.css Voir le fichier

@@ -4,7 +4,7 @@
}
.grid-heading-row {
border-bottom: 1px solid #d1d8dd;
background-color: #f7fafc;
background-color: #F7FAFC;
font-weight: bold;
}
.grid-row {


+ 1
- 1
frappe/public/css/gantt.css Voir le fichier

@@ -31,7 +31,7 @@
.fn-gantt .rightPanel .month,
.fn-gantt .rightPanel .year,
.fn-gantt .bottom {
background-color: #f7fafc !important;
background-color: #F7FAFC !important;
}
.fn-gantt .today {
background-color: #D9F6FF !important;


+ 1
- 1
frappe/public/css/indicator.css Voir le fichier

@@ -22,7 +22,7 @@
}
.indicator.grey::before,
.indicator-right.grey::after {
background: #f0f4f7;
background: #F0F4F7;
}
.indicator.blue::before,
.indicator-right.blue::after {


+ 4
- 4
frappe/public/css/list.css Voir le fichier

@@ -20,7 +20,7 @@
.set-filters .btn-group .btn-default {
background-color: transparent;
border: 1px solid #d1d8dd;
color: #8d99a6;
color: #8D99A6;
}
.filter-box {
border-top: 1px solid #d1d8dd;
@@ -61,18 +61,18 @@
margin-bottom: 0px;
}
.list-row-head {
background-color: #f7fafc;
background-color: #F7FAFC;
border-bottom: 1px solid #d1d8dd !important;
}
.list-row:hover,
.grid-row:hover {
background: #f7fafc;
background: #F7FAFC;
}
.list-row:last-child {
border-bottom: 0px;
}
.list-row-head {
background-color: #f7fafc;
background-color: #F7FAFC;
border-bottom: 1px solid #d1d8dd !important;
}
.list-row .h6 {


+ 7
- 7
frappe/public/css/mobile.css Voir le fichier

@@ -38,7 +38,7 @@ body {
.offcanvas .sidebar .divider {
height: 1px;
overflow: hidden;
background-color: #ebeff2;
background-color: #EBEFF2;
width: 100%;
margin: 0px;
}
@@ -52,7 +52,7 @@ body {
.offcanvas .sidebar .dropdown-menu > li > a:focus,
.offcanvas .sidebar .sidebar-menu > li > a:active,
.offcanvas .sidebar .dropdown-menu > li > a:active {
background-color: #f0f4f7;
background-color: #F0F4F7;
}
@media (max-width: 991px) {
input[type='checkbox'] {
@@ -133,7 +133,7 @@ body {
#navbar-breadcrumbs > li > a:before {
content: "\f104";
margin-right: 10px;
color: #6c7680;
color: #6C7680;
}
#navbar-breadcrumbs li:not(:nth-last-child(-n+1)) {
display: none;
@@ -204,7 +204,7 @@ body {
}
.sidebar .navbar-search-icon {
float: right;
color: #6c7680;
color: #6C7680;
font-size: inherit;
position: relative;
right: 7px;
@@ -232,7 +232,7 @@ body {
}
.sidebar .user-menu,
.sidebar .user-menu .octicon {
color: #6c7680;
color: #6C7680;
}
.sidebar .user-menu img {
margin-top: -1px;
@@ -266,12 +266,12 @@ body {
top: 3px;
content: "\f104";
margin-right: 10px;
color: #6c7680;
color: #6C7680;
}
body.no-breadcrumbs .navbar .navbar-home:hover:before,
body.no-breadcrumbs .navbar .navbar-home:focus:before,
body.no-breadcrumbs .navbar .navbar-home:active:before {
color: #36414c !important;
color: #36414C !important;
}
body[data-route=""] .navbar .navbar-home,
body[data-route="desktop"] .navbar .navbar-home {


+ 1
- 1
frappe/public/css/module.css Voir le fichier

@@ -18,7 +18,7 @@
}
.module-item:hover,
.module-item:focus {
background-color: #f7fafc;
background-color: #F7FAFC;
}
.module-item:last-child {
border: none;


+ 4
- 4
frappe/public/css/navbar.css Voir le fichier

@@ -42,7 +42,7 @@
background-color: rgba(255, 255, 255, 0.9);
}
.navbar .navbar-search-icon {
color: #6c7680;
color: #6C7680;
font-size: inherit;
position: relative;
right: 24px;
@@ -61,7 +61,7 @@
}
.navbar-center {
float: left;
color: #6c7680;
color: #6C7680;
}
#navbar-breadcrumbs > li > a:before {
font-family: FontAwesome;
@@ -78,12 +78,12 @@
top: 3px;
content: "\f105";
margin-right: 10px;
color: #c0c9d2;
color: #C0C9D2;
}
#navbar-breadcrumbs > li > a:hover:before,
#navbar-breadcrumbs > li > a:focus:before,
#navbar-breadcrumbs > li > a:active:before {
color: #36414c;
color: #36414C;
}
#navbar-breadcrumbs > li > a {
padding: 6px 15px 10px 0px;


+ 3
- 3
frappe/public/css/offcanvas-website.css Voir le fichier

@@ -38,7 +38,7 @@ body {
.offcanvas .sidebar .divider {
height: 1px;
overflow: hidden;
background-color: #ebeff2;
background-color: #EBEFF2;
width: 100%;
margin: 0px;
}
@@ -52,7 +52,7 @@ body {
.offcanvas .sidebar .dropdown-menu > li > a:focus,
.offcanvas .sidebar .sidebar-menu > li > a:active,
.offcanvas .sidebar .dropdown-menu > li > a:active {
background-color: #f0f4f7;
background-color: #F0F4F7;
}
@media (max-width: 767px) {
.page-content {
@@ -124,7 +124,7 @@ body {
}
.offcanvas .sidebar-label {
text-transform: uppercase;
color: #8d99a6;
color: #8D99A6;
font-size: 85%;
margin: 0px;
font-weight: bold;


+ 2
- 2
frappe/public/css/offcanvas.css Voir le fichier

@@ -38,7 +38,7 @@ body {
.offcanvas .sidebar .divider {
height: 1px;
overflow: hidden;
background-color: #ebeff2;
background-color: #EBEFF2;
width: 100%;
margin: 0px;
}
@@ -52,5 +52,5 @@ body {
.offcanvas .sidebar .dropdown-menu > li > a:focus,
.offcanvas .sidebar .sidebar-menu > li > a:active,
.offcanvas .sidebar .dropdown-menu > li > a:active {
background-color: #f0f4f7;
background-color: #F0F4F7;
}

+ 26
- 17
frappe/public/css/sidebar.css Voir le fichier

@@ -38,7 +38,7 @@ body {
.offcanvas .sidebar .divider {
height: 1px;
overflow: hidden;
background-color: #ebeff2;
background-color: #EBEFF2;
width: 100%;
margin: 0px;
}
@@ -52,7 +52,7 @@ body {
.offcanvas .sidebar .dropdown-menu > li > a:focus,
.offcanvas .sidebar .sidebar-menu > li > a:active,
.offcanvas .sidebar .dropdown-menu > li > a:active {
background-color: #f0f4f7;
background-color: #F0F4F7;
}
.hide-form-sidebar .form-sidebar {
display: none !important;
@@ -108,7 +108,7 @@ body[data-route^="Module"] .main-menu .form-sidebar {
.sidebar-menu h6,
.sidebar-menu .h6 {
text-transform: uppercase;
color: #8d99a6;
color: #8D99A6;
font-size: 10px;
margin-top: 0px;
}
@@ -122,7 +122,7 @@ body[data-route^="Module"] .main-menu .form-sidebar {
}
.form-sidebar .form-tags input {
font-size: 12px !important;
color: #36414c !important;
color: #36414C !important;
font-style: italic;
}
.form-sidebar .form-tags .tagit-new {
@@ -137,34 +137,39 @@ body[data-route^="Module"] .main-menu .form-sidebar {
.form-sidebar .attachment-row a.close {
margin-top: -5px;
}
.form-sidebar .form-shared .share-doc-btn {
.form-sidebar .form-shared .share-doc-btn,
.form-sidebar .form-viewers .share-doc-btn {
cursor: pointer;
}
.form-sidebar .form-shared .octicon {
.form-sidebar .form-shared .octicon,
.form-sidebar .form-viewers .octicon {
position: relative;
top: 2px;
left: 7px;
}
.form-sidebar .form-shared .avatar {
.form-sidebar .form-shared .avatar,
.form-sidebar .form-viewers .avatar {
margin-top: 5px;
}
.form-sidebar .form-shared .shared-with-everyone {
.form-sidebar .form-shared .shared-with-everyone,
.form-sidebar .form-viewers .shared-with-everyone {
border-style: solid;
border-color: #f0f4f7;
background-color: #f0f4f7;
border-color: #F0F4F7;
background-color: #F0F4F7;
}
.form-sidebar .form-shared .shared-with-everyone .octicon {
color: #36414c !important;
.form-sidebar .form-shared .shared-with-everyone .octicon,
.form-sidebar .form-viewers .shared-with-everyone .octicon {
color: #36414C !important;
}
.form-sidebar .form-shared .share-doc-btn:hover,
.form-sidebar .form-shared .share-doc-btn:focus,
.form-sidebar .form-shared .share-doc-btn:active {
background-color: #f0f4f7;
background-color: #F0F4F7;
}
.form-sidebar .form-shared .share-doc-btn:hover .octicon-plus,
.form-sidebar .form-shared .share-doc-btn:focus .octicon-plus,
.form-sidebar .form-shared .share-doc-btn:active .octicon-plus {
color: #36414c !important;
color: #36414C !important;
}
.sidebar-left .form-sidebar .form-tags,
.sidebar-left .form-sidebar .assignment-row,
@@ -172,20 +177,24 @@ body[data-route^="Module"] .main-menu .form-sidebar {
.sidebar-left .form-sidebar .modified-by,
.sidebar-left .form-sidebar .created-by,
.sidebar-left .form-sidebar .tags-label,
.sidebar-left .form-sidebar .shared-with-label {
.sidebar-left .form-sidebar .shared-with-label,
.sidebar-left .form-sidebar .form-viewers,
.sidebar-left .form-sidebar .viewers-label {
padding: 12px 14px;
}
.sidebar-left .form-sidebar .assigned-to-label,
.sidebar-left .form-sidebar .attachments-label,
.sidebar-left .form-sidebar .tags-label,
.sidebar-left .form-sidebar .shared-with-label {
.sidebar-left .form-sidebar .shared-with-label,
.sidebar-left .form-sidebar .viewers-label {
padding: 12px 14px;
margin-bottom: 0px;
}
.sidebar-left .form-sidebar .assigned-to-label,
.sidebar-left .form-sidebar .tags-label,
.sidebar-left .form-sidebar .attachments-label.has-attachments,
.sidebar-left .form-sidebar .shared-with-label {
.sidebar-left .form-sidebar .shared-with-label,
.sidebar-left .form-sidebar .viewers-label {
padding-bottom: 0px;
}
.sidebar-left .form-sidebar a.close {


+ 3
- 3
frappe/public/css/slickgrid.css Voir le fichier

@@ -13,7 +13,7 @@
border-bottom: 1px solid #d1d8dd;
}
.slick-headerrow-column {
background-color: #f7fafc !important;
background-color: #F7FAFC !important;
text-overflow: clip;
}
.slick-headerrow-column input {
@@ -37,9 +37,9 @@
.slick-header-columns {
font-size: 12px;
font-weight: bold;
background-color: #f7fafc;
background-color: #F7FAFC;
border-color: #d1d8dd !important;
color: #8d99a6 !important;
color: #8D99A6 !important;
}
.slick-header-column:hover,
.slick-header-column-active {


+ 1
- 1
frappe/public/css/tree.css Voir le fichier

@@ -15,7 +15,7 @@ ul.tree-children {
color: #5e64ff !important;
}
.tree-hover {
background-color: #f7fafc;
background-color: #F7FAFC;
min-height: 20px;
border: 1px solid #d1d8dd;
}


+ 13
- 13
frappe/public/css/website.css Voir le fichier

@@ -18,10 +18,10 @@ p {
margin: 10px 0px;
}
.text-color {
color: #36414c !important;
color: #36414C !important;
}
.text-muted {
color: #8d99a6 !important;
color: #8D99A6 !important;
}
.text-extra-muted {
color: #d1d8dd !important;
@@ -82,7 +82,7 @@ a.text-extra-muted {
}
kbd {
color: inherit;
background-color: #f0f4f7;
background-color: #F0F4F7;
}
.btn [class^="icon-"],
.nav [class^="icon-"],
@@ -186,7 +186,7 @@ a.badge-hover:active .badge {
transform: translate(-50%, -50%);
-webkit-transform: translate(-50%, -50%);
text-align: center;
color: #36414c !important;
color: #36414C !important;
}
#freeze.dark {
background-color: #334143;
@@ -288,7 +288,7 @@ a.no-decoration:active {
}
.indicator.grey::before,
.indicator-right.grey::after {
background: #f0f4f7;
background: #F0F4F7;
}
.indicator.blue::before,
.indicator-right.blue::after {
@@ -339,7 +339,7 @@ a.no-decoration:active {
background-color: #fafbfc;
}
.panel-bg {
background-color: #f7fafc;
background-color: #F7FAFC;
}
.navbar-bg {
background-color: #f5f7fa;
@@ -463,7 +463,7 @@ fieldset {
margin: 0px;
padding: 5px 0px;
border: none;
color: #8d99a6;
color: #8D99A6;
font-size: 12px;
}
.page-sidebar .sidebar-item:first-child {
@@ -473,7 +473,7 @@ fieldset {
padding-bottom: 10px;
}
.page-sidebar .sidebar-item a {
color: #8d99a6;
color: #8D99A6;
}
.page-sidebar .sidebar-item a:hover {
border-color: inherit;
@@ -499,7 +499,7 @@ fieldset {
}
.web-list-item {
padding: 15px 0px;
border-bottom: 1px solid #ebeff2;
border-bottom: 1px solid #EBEFF2;
}
.web-list-item h1,
.web-list-item h2,
@@ -609,7 +609,7 @@ a.active {
color: inherit;
}
.breadcrumb > .active {
color: #8d99a6;
color: #8D99A6;
}
.post:last-child {
border-bottom: none;
@@ -662,7 +662,7 @@ body {
.offcanvas .sidebar .divider {
height: 1px;
overflow: hidden;
background-color: #ebeff2;
background-color: #EBEFF2;
width: 100%;
margin: 0px;
}
@@ -676,7 +676,7 @@ body {
.offcanvas .sidebar .dropdown-menu > li > a:focus,
.offcanvas .sidebar .sidebar-menu > li > a:active,
.offcanvas .sidebar .dropdown-menu > li > a:active {
background-color: #f0f4f7;
background-color: #F0F4F7;
}
@media (max-width: 767px) {
.page-content {
@@ -748,7 +748,7 @@ body {
}
.offcanvas .sidebar-label {
text-transform: uppercase;
color: #8d99a6;
color: #8D99A6;
font-size: 85%;
margin: 0px;
font-weight: bold;


+ 5
- 0
frappe/public/js/frappe/form/form_sidebar.html Voir le fichier

@@ -39,6 +39,11 @@
<li class="h6 shared-with-label">{%= __("Shared With") %}</li>
<li class="form-shared"></li>
</ul>
<ul class="list-unstyled sidebar-menu">
<li class="divider"></li>
<li class="h6 viewers-label">{%= __("Currently Viewing") %}</li>
<li class="form-viewers"></li>
</ul>
<ul class="list-unstyled sidebar-menu text-muted">
<li class="modified-by"></li>
<li class="created-by"></li>


+ 76
- 0
frappe/public/js/frappe/form/form_viewers.js Voir le fichier

@@ -0,0 +1,76 @@
frappe.provide("frappe.ui.form");

frappe.ui.form.Viewers = Class.extend({
init: function(opts) {
$.extend(this, opts);
},
refresh: function(data_updated) {
var me = this;
this.parent.empty();

var viewers = this.frm.get_docinfo().viewers || {};

var users = [];
var new_users = [];
for (var i=0, l=(viewers.current || []).length; i < l; i++) {
var username = viewers.current[i];
if (username===user) {
// current user
continue;
}

var user_info = frappe.user_info(username);
users.push({
image: user_info.image,
fullname: user_info.fullname,
title: __("{0} is currently viewing this document", [user_info.fullname])
});

if (viewers.new.indexOf(username)!==-1) {
new_users.push(user_info.fullname);
}
}

if (users.length) {
this.parent.parent().removeClass("hidden");
this.parent.append(frappe.render_template("users_in_sidebar", {"users": users}));
} else {
this.parent.parent().addClass("hidden");
}

if (data_updated && new_users.length) {
// new user viewing this document, who wasn't viewing in the past
if (new_users.length===1) {
show_alert(__("{0} is currently viewing this document", [new_users[0]]));
} else {
show_alert(__("{0} are currently viewing this document", [frappe.utils.comma_and(new_users)]));
}

}
}
});

frappe.ui.form.set_viewers = function(data) {
var doctype = data.doctype;
var docname = data.docname;
var past_viewers = (frappe.model.get_docinfo(doctype, docname).viewers || {}).past || [];
var new_viewers = [];
var viewers = data.viewers || [];

for (i=0, l=viewers.length; i < l; i++) {
var username = viewers[i];
if (past_viewers.indexOf(username)===-1) {
new_viewers.push(username);
}
}

frappe.model.set_docinfo(doctype, docname, "viewers", {
past: past_viewers.concat(new_viewers),
new: new_viewers,
current: viewers
});

if (cur_frm.doc && cur_frm.doc.doctype===doctype && cur_frm.doc.name==docname) {
cur_frm.viewers.refresh(true);
}
}

+ 30
- 29
frappe/public/js/frappe/form/share.js Voir le fichier

@@ -8,44 +8,45 @@ frappe.ui.form.Share = Class.extend({
$.extend(this, opts);
},
refresh: function() {
this.render_sidebar();
},
render_sidebar: function() {
var me = this;
this.parent.empty();

var everyone = null;
var shared = $.map(this.shared || this.frm.get_docinfo().shared, function(s) {
var shared = this.shared || this.frm.get_docinfo().shared;
var users = [];
for (var i=0, l=shared.length; i < l; i++) {
var s = shared[i];

if (s.everyone) {
everyone = s;
users.push({
icon: "octicon octicon-megaphone text-muted",
avatar_class: "avatar-empty share-doc-btn shared-with-everyone",
title: __("Shared with everyone")
});
} else {
var user_info = frappe.user_info(s.user);
users.push({
image: user_info.image,
fullname: user_info.fullname,
title: __("Shared with {0}", [user_info.fullname])
});
}

return s ? s.user : null;
});

if (everyone) {
$(repl('<span><a class="avatar avatar-small avatar-empty share-doc-btn shared-with-everyone" title="%(title)s">\
<i class="octicon octicon-megaphone text-muted"></i></a></span>', {title: __("Shared with everyone")}))
.appendTo(this.parent)
.on("click", function() { me.frm.share_doc(); });
}

for(var i=0; i<shared.length; i++) {
var user_info = frappe.user_info(shared[i]);
$(repl('<span class="avatar avatar-small" title="'
+__("Shared with {0}", [user_info.fullname])+'">\
<img class="media-object" src="%(image)s" alt="%(fullname)s"></span>',
{image: user_info.image, fullname: user_info.fullname}))
.appendTo(this.parent)
.on("click", function() { me.frm.share_doc(); });
}

// share
if(!me.frm.doc.__islocal) {
$(repl('<span><a class="avatar avatar-small avatar-empty share-doc-btn" title="%(title)s">\
<i class="octicon octicon-plus text-muted"></i></a></span>', {title: __("Share")}))
.appendTo(this.parent)
.on("click", function() { me.frm.share_doc(); });

if (!me.frm.doc.__islocal) {
users.push({
icon: "octicon octicon-plus text-muted",
avatar_class: "avatar-empty share-doc-btn",
title: __("Share")
});
}

this.parent.append(frappe.render_template("users_in_sidebar", {"users": users}));
this.parent.find(".avatar").on("click", function() {
me.frm.share_doc();
});
},
show: function() {
var me = this;


+ 8
- 0
frappe/public/js/frappe/form/sidebar.js Voir le fichier

@@ -20,6 +20,7 @@ frappe.ui.form.Sidebar = Class.extend({
this.make_assignments();
this.make_attachments();
this.make_shared();
this.make_viewers();
this.make_tags();

this.bind_events();
@@ -46,6 +47,7 @@ frappe.ui.form.Sidebar = Class.extend({
this.frm.assign_to.refresh();
this.frm.attachments.refresh();
this.frm.shared.refresh();
this.frm.viewers.refresh();
this.frm.tags && this.frm.tags.refresh(this.frm.doc._user_tags);
this.sidebar.find(".modified-by").html(__("{0} edited this {1}",
["<strong>" + frappe.user.full_name(this.frm.doc.modified_by) + "</strong>",
@@ -97,6 +99,12 @@ frappe.ui.form.Sidebar = Class.extend({
parent: this.sidebar.find(".form-shared")
});
},
make_viewers: function() {
this.frm.viewers = new frappe.ui.form.Viewers({
frm: this.frm,
parent: this.sidebar.find(".form-viewers")
});
},
add_user_action: function(label, click) {
return $('<a>').html(label).appendTo($('<li class="user-action-row">')
.appendTo(this.user_actions.removeClass("hide"))).on("click", click);


+ 11
- 0
frappe/public/js/frappe/form/users_in_sidebar.html Voir le fichier

@@ -0,0 +1,11 @@
{% for (var i=0, l=users.length; i < l; i++) {
var u = users[i];
%}
<span class="avatar avatar-small {{ u.avatar_class || "" }}" title="{{ u.title }}">
{% if (u.icon) { %}
<i class="{{ u.icon }}"></i>
{% } else { %}
<img class="media-object" src="{{ u.image }}" alt="{{ u.fullname }}">
{% } %}
</span>
{% } %}

+ 6
- 0
frappe/public/js/frappe/model/model.js Voir le fichier

@@ -153,6 +153,12 @@ $.extend(frappe.model, {
return frappe.model.docinfo[doctype] && frappe.model.docinfo[doctype][name] || null;
},

set_docinfo: function(doctype, name, key, value) {
if (frappe.model.docinfo[doctype] && frappe.model.docinfo[doctype][name]) {
frappe.model.docinfo[doctype][name][key] = value;
}
},

new_comment: function(comment) {
var reference_doctype = comment.comment_doctype || comment.reference_doctype;
var reference_name = comment.comment_docname || comment.reference_name;


frappe/public/js/frappe/socket.js → frappe/public/js/frappe/socketio_client.js Voir le fichier

@@ -42,9 +42,21 @@ frappe.socket = {
frappe.socket.doc_subscribe(frm.doctype, frm.docname);
});

// $(document).on('form-unload', function(e, frm) {
// frappe.socket.doc_unsubscribe(frm.doctype, frm.docname);
// });
$(document).on("form_refresh", function(e, frm) {
frappe.socket.doc_open(frm.doctype, frm.docname);
});

$(document).on('form-unload', function(e, frm) {
// frappe.socket.doc_unsubscribe(frm.doctype, frm.docname);
frappe.socket.doc_close(frm.doctype, frm.docname);
});

window.onbeforeunload = function() {
// if tab/window is closed, notify other users
if (cur_frm && cur_frm.doc) {
frappe.socket.doc_close(cur_frm.doctype, cur_frm.docname);
}
}
},
get_host: function() {
var host = frappe.urllib.get_base_url();
@@ -77,6 +89,14 @@ frappe.socket = {
}
})
},
doc_open: function(doctype, docname) {
// notify that the user has opened this doc
frappe.socket.socket.emit('doc_open', doctype, docname);
},
doc_close: function(doctype, docname) {
// notify that the user has closed this doc
frappe.socket.socket.emit('doc_close', doctype, docname);
},
setup_listeners: function() {
frappe.socket.socket.on('task_status_change', function(data) {
frappe.socket.process_response(data, data.status.toLowerCase());
@@ -88,16 +108,25 @@ frappe.socket = {
setup_reconnect: function() {
// subscribe again to open_tasks
frappe.socket.socket.on("connect", function() {
$.each(frappe.socket.open_tasks, function(task_id, opts) {
frappe.socket.subscribe(task_id, opts);
});

// re-connect open docs
$.each(frappe.socket.open_docs, function(d) {
if(locals[d.doctype] && locals[d.doctype][d.name]) {
frappe.socket.doc_subscribe(d.doctype, d.name);
// wait for 5 seconds before subscribing again
// because it takes more time to start python server than nodejs server
// and we use validation requests to python server for subscribing
setTimeout(function() {
$.each(frappe.socket.open_tasks, function(task_id, opts) {
frappe.socket.subscribe(task_id, opts);
});

// re-connect open docs
$.each(frappe.socket.open_docs, function(d) {
if(locals[d.doctype] && locals[d.doctype][d.name]) {
frappe.socket.doc_subscribe(d.doctype, d.name);
}
});

if (cur_frm && cur_frm.doc) {
frappe.socket.doc_open(cur_frm.doc.doctype, cur_frm.doc.name);
}
})
}, 5000);
});

},
@@ -136,3 +165,9 @@ frappe.realtime.on = function(event, callback) {
frappe.socket.socket.on(event, callback);
}
}

frappe.realtime.publish = function(event, message) {
if(frappe.socket.socket) {
frappe.socket.socket.emit(event, message);
}
}

+ 3
- 0
frappe/public/js/frappe/views/formview.js Voir le fichier

@@ -29,6 +29,9 @@ frappe.views.FormFactory = frappe.views.Factory.extend({
frappe.model.new_comment(data);
});

frappe.realtime.on("doc_viewers", function(data) {
frappe.ui.form.set_viewers(data);
});
}




+ 1
- 1
frappe/public/js/legacy/form.js Voir le fichier

@@ -445,7 +445,7 @@ _f.Frm.prototype.render_form = function(is_a_different_doc) {

// trigger global trigger
// to use this
$(document).trigger('form_refresh');
$(document).trigger('form_refresh', [this]);

// fields
this.refresh_fields();


+ 8
- 4
frappe/public/less/sidebar.less Voir le fichier

@@ -115,7 +115,7 @@ body[data-route^="Module"] .main-menu {
margin-top: -5px;
}

.form-shared {
.form-shared, .form-viewers {
.share-doc-btn {
cursor: pointer;
}
@@ -161,14 +161,17 @@ body[data-route^="Module"] .main-menu {
.modified-by,
.created-by,
.tags-label,
.shared-with-label {
.shared-with-label,
.form-viewers,
.viewers-label {
.sidebar-padding;
}

.assigned-to-label,
.attachments-label,
.tags-label,
.shared-with-label {
.shared-with-label,
.viewers-label {
.sidebar-padding;
margin-bottom: 0px;
}
@@ -176,7 +179,8 @@ body[data-route^="Module"] .main-menu {
.assigned-to-label,
.tags-label,
.attachments-label.has-attachments,
.shared-with-label {
.shared-with-label,
.viewers-label {
padding-bottom: 0px;
}



+ 100
- 21
socketio.js Voir le fichier

@@ -6,7 +6,6 @@ var fs = require('fs');

var redis = require("redis")
var subscriber = redis.createClient(12311);
var r = redis.createClient(12311);

var request = require('superagent')
var default_site;
@@ -31,6 +30,9 @@ io.on('connection', function(socket){
if(!sid) {
return;
}

socket.user = cookie.parse(socket.request.headers.cookie).user_id;

// console.log("firing get_user_info");
request.get(get_url(socket, '/api/method/frappe.async.get_user_info'))
.type('form')
@@ -63,25 +65,17 @@ io.on('connection', function(socket){

socket.on('doc_subscribe', function(doctype, docname) {
// console.log('trying to subscribe', doctype, docname)
request.get(get_url(socket, '/api/method/frappe.async.can_subscribe_doc'))
.type('form')
.send({
sid: sid,
doctype: doctype,
docname: docname
})
.end(function(err, res) {
if(err) console.log(err);
if(!res) {
console.log("No response for doc_subscribe");
return;
}
if(res.status == 200) {
var room = get_doc_room(socket, doctype, docname);
// console.log('joining', room)
socket.join(room);
}
})
can_subscribe_doc({
socket: socket,
sid: sid,
doctype: doctype,
docname: docname,
callback: function(err, res) {
var room = get_doc_room(socket, doctype, docname);
// console.log('joining', room)
socket.join(room);
}
});
});

socket.on('doc_unsubscribe', function(doctype, docname) {
@@ -89,13 +83,45 @@ io.on('connection', function(socket){
socket.leave(room);
});

socket.on('doc_open', function(doctype, docname) {
// show who is currently viewing the form
can_subscribe_doc({
socket: socket,
sid: sid,
doctype: doctype,
docname: docname,
callback: function(err, res) {
var room = get_open_doc_room(socket, doctype, docname);
// console.log('joining', room)
socket.join(room);

send_viewers({
socket: socket,
doctype: doctype,
docname: docname,
});
}
});
});

socket.on('doc_close', function(doctype, docname) {
// remove this user from the list of 'who is currently viewing the form'
var room = get_open_doc_room(socket, doctype, docname);
socket.leave(room);
send_viewers({
socket: socket,
doctype: doctype,
docname: docname,
});
});

// socket.on('disconnect', function (arguments) {
// console.log("user disconnected", arguments);
// });
});

function send_existing_lines(task_id, socket) {
r.hgetall('task_log:' + task_id, function(err, lines) {
subscriber.hgetall('task_log:' + task_id, function(err, lines) {
socket.emit('task_progress', {
"task_id": task_id,
"message": {
@@ -122,6 +148,10 @@ function get_doc_room(socket, doctype, docname) {
return get_site_name(socket) + ':doc:'+ doctype + '/' + docname;
}

function get_open_doc_room(socket, doctype, docname) {
return get_site_name(socket) + ':open_doc:'+ doctype + '/' + docname;
}

function get_user_room(socket, user) {
return get_site_name(socket) + ':user:' + user;
}
@@ -159,3 +189,52 @@ function get_url(socket, path) {
}
return socket.request.headers.origin + path;
}

function can_subscribe_doc(args) {
request.get(get_url(args.socket, '/api/method/frappe.async.can_subscribe_doc'))
.type('form')
.send({
sid: args.sid,
doctype: args.doctype,
docname: args.docname
})
.end(function(err, res) {
if(err) console.log(err);
if(!res) {
console.log("No response for doc_subscribe");
return;
}
if(res.status == 200) {
args.callback(err, res);
}
});
}

function send_viewers(args) {
// send to doc room, 'users currently viewing this document'
if (!(args && args.doctype && args.docname)) {
return;
}

// open doc room
var room = get_open_doc_room(args.socket, args.doctype, args.docname);

// socket ids connected to this room
var clients = Object.keys(io.sockets.adapter.rooms[room] || {});

var viewers = [];
for (var i in io.sockets.sockets) {
var s = io.sockets.sockets[i];
if (clients.indexOf(s.id)!==-1) {
// this socket is connected to the room
viewers.push(s.user);
}
}

// notify
io.to(room).emit("doc_viewers", {
doctype: args.doctype,
docname: args.docname,
viewers: viewers
});
}

Chargement…
Annuler
Enregistrer