Pārlūkot izejas kodu

Merge remote-tracking branch 'origin/ng-helpdesk' into ng-helpdesk

jalbr74 7 gadi atpakaļ
vecāks
revīzija
6d3a507a65
100 mainītis faili ar 1373 papildinājumiem un 2157 dzēšanām
  1. BIN
      client/images/avatars/1.jpg
  2. BIN
      client/images/avatars/10.jpg
  3. BIN
      client/images/avatars/11.jpg
  4. BIN
      client/images/avatars/12.jpg
  5. BIN
      client/images/avatars/14.jpg
  6. BIN
      client/images/avatars/15.jpg
  7. BIN
      client/images/avatars/16.jpg
  8. BIN
      client/images/avatars/17.jpg
  9. BIN
      client/images/avatars/18.jpg
  10. BIN
      client/images/avatars/19.jpg
  11. BIN
      client/images/avatars/2.jpg
  12. BIN
      client/images/avatars/20.jpg
  13. BIN
      client/images/avatars/21.jpg
  14. BIN
      client/images/avatars/3.jpg
  15. BIN
      client/images/avatars/4.jpg
  16. BIN
      client/images/avatars/5.jpg
  17. BIN
      client/images/avatars/7.jpg
  18. BIN
      client/images/avatars/8.jpg
  19. BIN
      client/images/avatars/9.jpg
  20. 0 23
      client/images/icons/m_check_thick.svg
  21. 0 23
      client/images/icons/m_close_thick.svg
  22. 0 23
      client/images/icons/m_configure_thin.svg
  23. 0 23
      client/images/icons/m_down_thick.svg
  24. 0 23
      client/images/icons/m_lock_thin.svg
  25. 0 23
      client/images/icons/m_orgchart_thin.svg
  26. 0 23
      client/images/icons/m_password_thin.svg
  27. 0 23
      client/images/icons/m_reload_refresh_thin.svg
  28. 0 23
      client/images/icons/m_search_thick.svg
  29. 0 23
      client/images/icons/m_unlock_thin.svg
  30. 0 23
      client/images/icons/m_up_thick.svg
  31. 0 23
      client/images/icons/m_view-list_thin.svg
  32. 0 23
      client/images/icons/m_view-tile_thin.svg
  33. 4 0
      client/index.html
  34. 72 72
      client/package.json
  35. 30 28
      client/src/changepassword/autogen-change-password.component.html
  36. 1 1
      client/src/changepassword/autogen-change-password.component.scss
  37. 3 4
      client/src/changepassword/autogen-change-password.controller.ts
  38. 20 16
      client/src/changepassword/random-change-password.component.html
  39. 2 3
      client/src/changepassword/random-change-password.controller.ts
  40. 27 23
      client/src/changepassword/success-change-password.component.html
  41. 2 3
      client/src/changepassword/success-change-password.controller.ts
  42. 64 59
      client/src/changepassword/type-change-password.component.html
  43. 3 11
      client/src/changepassword/type-change-password.component.scss
  44. 3 4
      client/src/changepassword/type-change-password.controller.ts
  45. 1 1
      client/src/helpdesk/date.filters.ts
  46. 38 33
      client/src/helpdesk/helpdesk-detail-dialog.template.html
  47. 41 59
      client/src/helpdesk/helpdesk-detail.component.html
  48. 46 88
      client/src/helpdesk/helpdesk-detail.component.scss
  49. 6 11
      client/src/helpdesk/helpdesk-detail.component.ts
  50. 43 116
      client/src/helpdesk/helpdesk-search-base.component.ts
  51. 54 0
      client/src/helpdesk/helpdesk-search-cards.component.html
  52. 120 0
      client/src/helpdesk/helpdesk-search-cards.component.ts
  53. 76 0
      client/src/helpdesk/helpdesk-search-table.component.html
  54. 109 0
      client/src/helpdesk/helpdesk-search-table.component.ts
  55. 0 65
      client/src/helpdesk/helpdesk-search.component.html
  56. 19 161
      client/src/helpdesk/helpdesk-search.component.scss
  57. 8 5
      client/src/helpdesk/helpdesk.module.ts
  58. 0 0
      client/src/helpdesk/helpdesk.scss
  59. 3 4
      client/src/helpdesk/main.dev.ts
  60. 1 3
      client/src/helpdesk/main.ts
  61. 1 1
      client/src/helpdesk/recent-verifications-dialog.controller.ts
  62. 41 39
      client/src/helpdesk/recent-verifications-dialog.template.html
  63. 8 2
      client/src/helpdesk/routes.ts
  64. 2 3
      client/src/helpdesk/verifications-dialog.controller.ts
  65. 70 64
      client/src/helpdesk/verifications-dialog.template.html
  66. 0 15
      client/src/icons.json
  67. 2 3
      client/src/main.dev.ts
  68. 0 3
      client/src/main.ts
  69. 35 31
      client/src/peoplesearch/orgchart-search.component.html
  70. 0 4
      client/src/peoplesearch/orgchart-search.component.scss
  71. 6 2
      client/src/peoplesearch/orgchart-search.component.ts
  72. 1 1
      client/src/peoplesearch/orgchart.component.html
  73. 88 61
      client/src/peoplesearch/orgchart.component.scss
  74. 13 11
      client/src/peoplesearch/peoplesearch-base.component.ts
  75. 23 28
      client/src/peoplesearch/peoplesearch-cards.component.html
  76. 52 36
      client/src/peoplesearch/peoplesearch-table.component.html
  77. 8 1
      client/src/peoplesearch/peoplesearch-table.component.scss
  78. 22 1
      client/src/peoplesearch/peoplesearch-table.component.ts
  79. 3 2
      client/src/peoplesearch/peoplesearch.module.ts
  80. 25 20
      client/src/peoplesearch/peoplesearch.scss
  81. 15 14
      client/src/peoplesearch/person-card.component.html
  82. 24 15
      client/src/peoplesearch/person-card.component.ts
  83. 38 23
      client/src/peoplesearch/person-details-dialog.component.html
  84. 64 93
      client/src/peoplesearch/person-details-dialog.component.scss
  85. 8 0
      client/src/peoplesearch/person-details-dialog.component.ts
  86. 1 1
      client/src/services/base-config.service.dev.ts
  87. 1 1
      client/src/services/helpdesk-config.service.dev.ts
  88. 1 1
      client/src/services/helpdesk-config.service.ts
  89. 2 2
      client/src/services/helpdesk.service.dev.ts
  90. 1 1
      client/src/services/helpdesk.service.ts
  91. 1 0
      client/src/services/local-storage.service.ts
  92. 20 22
      client/src/services/people.data.json
  93. 1 1
      client/src/services/peoplesearch-config.service.ts
  94. 0 93
      client/src/ux/app-bar.component.scss
  95. 0 46
      client/src/ux/app-bar.component.ts
  96. 0 86
      client/src/ux/auto-complete.component.scss
  97. 0 236
      client/src/ux/auto-complete.component.ts
  98. 0 65
      client/src/ux/button.component.scss
  99. 0 32
      client/src/ux/button.component.ts
  100. 0 28
      client/src/ux/dialog.component.html

BIN
client/images/avatars/1.jpg


BIN
client/images/avatars/10.jpg


BIN
client/images/avatars/11.jpg


BIN
client/images/avatars/12.jpg


BIN
client/images/avatars/14.jpg


BIN
client/images/avatars/15.jpg


BIN
client/images/avatars/16.jpg


BIN
client/images/avatars/17.jpg


BIN
client/images/avatars/18.jpg


BIN
client/images/avatars/19.jpg


BIN
client/images/avatars/2.jpg


BIN
client/images/avatars/20.jpg


BIN
client/images/avatars/21.jpg


BIN
client/images/avatars/3.jpg


BIN
client/images/avatars/4.jpg


BIN
client/images/avatars/5.jpg


BIN
client/images/avatars/7.jpg


BIN
client/images/avatars/8.jpg


BIN
client/images/avatars/9.jpg


+ 0 - 23
client/images/icons/m_check_thick.svg

@@ -1,23 +0,0 @@
-<!--
-  ~ Password Management Servlets (PWM)
-  ~ http://www.pwm-project.org
-  ~
-  ~ Copyright (c) 2006-2009 Novell, Inc.
-  ~ Copyright (c) 2009-2017 The PWM Project
-  ~
-  ~ This program is free software; you can redistribute it and/or modify
-  ~ it under the terms of the GNU General Public License as published by
-  ~ the Free Software Foundation; either version 2 of the License, or
-  ~ (at your option) any later version.
-  ~
-  ~ This program 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 General Public License for more details.
-  ~
-  ~ You should have received a copy of the GNU General Public License
-  ~ along with this program; if not, write to the Free Software
-  ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-  -->
-
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 72 72"><title>1-icons_expanded</title><polygon points="27.28 57.2 7.5 37.41 10.32 34.59 27.28 51.54 64.36 14.45 67.19 17.28 27.28 57.2" fill="gray"/></svg>

+ 0 - 23
client/images/icons/m_close_thick.svg

@@ -1,23 +0,0 @@
-<!--
-  ~ Password Management Servlets (PWM)
-  ~ http://www.pwm-project.org
-  ~
-  ~ Copyright (c) 2006-2009 Novell, Inc.
-  ~ Copyright (c) 2009-2018 The PWM Project
-  ~
-  ~ This program is free software; you can redistribute it and/or modify
-  ~ it under the terms of the GNU General Public License as published by
-  ~ the Free Software Foundation; either version 2 of the License, or
-  ~ (at your option) any later version.
-  ~
-  ~ This program 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 General Public License for more details.
-  ~
-  ~ You should have received a copy of the GNU General Public License
-  ~ along with this program; if not, write to the Free Software
-  ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-  -->
-
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 72 72"><title>1-icons_expanded</title><polygon points="60.45 14.55 57.62 11.72 36.08 33.26 14.61 11.78 11.78 14.61 33.26 36.08 11.62 57.72 14.45 60.55 36.08 38.91 57.88 60.7 60.7 57.88 38.91 36.08 60.45 14.55" fill="gray"/></svg>

+ 0 - 23
client/images/icons/m_configure_thin.svg

@@ -1,23 +0,0 @@
-<!--
-  ~ Password Management Servlets (PWM)
-  ~ http://www.pwm-project.org
-  ~
-  ~ Copyright (c) 2006-2009 Novell, Inc.
-  ~ Copyright (c) 2009-2018 The PWM Project
-  ~
-  ~ This program is free software; you can redistribute it and/or modify
-  ~ it under the terms of the GNU General Public License as published by
-  ~ the Free Software Foundation; either version 2 of the License, or
-  ~ (at your option) any later version.
-  ~
-  ~ This program 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 General Public License for more details.
-  ~
-  ~ You should have received a copy of the GNU General Public License
-  ~ along with this program; if not, write to the Free Software
-  ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-  -->
-
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 72 72"><title>1-icons_expanded</title><path d="M36,46.32A10.37,10.37,0,1,1,46.45,36,10.4,10.4,0,0,1,36,46.32Zm0-17.38a7,7,0,1,0,7.07,7A7.05,7.05,0,0,0,36,28.93Z" fill="gray"/><path d="M54.43,39.26a3.35,3.35,0,0,0-2.52,2.18,16.94,16.94,0,0,1-.75,1.8,3.32,3.32,0,0,0,.23,3.32L55,51.6l-3.24,3.22-5.08-3.6A3.38,3.38,0,0,0,43.37,51a17.06,17.06,0,0,1-1.81.75,3.35,3.35,0,0,0-2.19,2.5l-1,6H33.75l-1-6a3.35,3.35,0,0,0-2.19-2.5A17.16,17.16,0,0,1,28.72,51a3.37,3.37,0,0,0-3.33.23L20.33,54.8l-3.24-3.22,3.59-5a3.34,3.34,0,0,0,.23-3.31,16.86,16.86,0,0,1-.75-1.8,3.36,3.36,0,0,0-2.52-2.18l-6.46-1V33.68l6.46-1a3.36,3.36,0,0,0,2.52-2.18,16.62,16.62,0,0,1,.75-1.8,3.32,3.32,0,0,0-.23-3.31L17,20.25,20.25,17l5.14,3.66a3.38,3.38,0,0,0,3.33.23,17,17,0,0,1,1.81-.75,3.35,3.35,0,0,0,2.19-2.5l1-6.33h4.58l1,6.33a3.35,3.35,0,0,0,2.2,2.5,16.79,16.79,0,0,1,1.81.75,3.38,3.38,0,0,0,3.33-.23L51.85,17l3.24,3.22-3.7,5.13a3.34,3.34,0,0,0-.23,3.31,16.84,16.84,0,0,1,.76,1.8,3.35,3.35,0,0,0,2.52,2.18l6,1v4.55Zm6.79-9.08L55.87,29.1a21,21,0,0,0-.94-2.24l3-4.53a3.47,3.47,0,0,0-.44-4.39l-3.37-3.35a3.51,3.51,0,0,0-4.42-.44l-4.56,3a21.22,21.22,0,0,0-2.26-.93L41.85,10.9a3.49,3.49,0,0,0-3.43-2.8H33.66a3.5,3.5,0,0,0-3.43,2.8l-1.07,5.34a21.23,21.23,0,0,0-2.26.93l-4.56-3a3.51,3.51,0,0,0-4.42.44l-3.37,3.35a3.47,3.47,0,0,0-.44,4.39l3,4.53a20.8,20.8,0,0,0-.94,2.25l-5.37,1.07A3.49,3.49,0,0,0,8,33.59v4.73a3.49,3.49,0,0,0,2.81,3.41l5.36,1.07A21,21,0,0,0,17.15,45l-3,4.53A3.47,3.47,0,0,0,14.56,54l3.37,3.35a3.51,3.51,0,0,0,4.42.44l4.56-3a20.7,20.7,0,0,0,2.26.93L30.23,61a3.5,3.5,0,0,0,3.43,2.8h4.76A3.5,3.5,0,0,0,41.85,61l1.08-5.33a21.24,21.24,0,0,0,2.26-.93l4.56,3a3.51,3.51,0,0,0,4.42-.44L57.53,54A3.47,3.47,0,0,0,58,49.58l-3-4.53a20.64,20.64,0,0,0,.94-2.25l5.36-1.07A3.49,3.49,0,0,0,64,38.32V33.59A3.49,3.49,0,0,0,61.23,30.17Z" fill="gray" fill-rule="evenodd"/></svg>

+ 0 - 23
client/images/icons/m_down_thick.svg

@@ -1,23 +0,0 @@
-<!--
-  ~ Password Management Servlets (PWM)
-  ~ http://www.pwm-project.org
-  ~
-  ~ Copyright (c) 2006-2009 Novell, Inc.
-  ~ Copyright (c) 2009-2018 The PWM Project
-  ~
-  ~ This program is free software; you can redistribute it and/or modify
-  ~ it under the terms of the GNU General Public License as published by
-  ~ the Free Software Foundation; either version 2 of the License, or
-  ~ (at your option) any later version.
-  ~
-  ~ This program 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 General Public License for more details.
-  ~
-  ~ You should have received a copy of the GNU General Public License
-  ~ along with this program; if not, write to the Free Software
-  ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-  -->
-
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 72 72"><title>1-icons_expanded</title><polygon points="35.85 53.38 7.68 25.2 10.5 22.38 35.85 47.72 61.41 22.15 64.24 24.98 35.85 53.38" fill="gray"/></svg>

+ 0 - 23
client/images/icons/m_lock_thin.svg

@@ -1,23 +0,0 @@
-<!--
-  ~ Password Management Servlets (PWM)
-  ~ http://www.pwm-project.org
-  ~
-  ~ Copyright (c) 2006-2009 Novell, Inc.
-  ~ Copyright (c) 2009-2017 The PWM Project
-  ~
-  ~ This program is free software; you can redistribute it and/or modify
-  ~ it under the terms of the GNU General Public License as published by
-  ~ the Free Software Foundation; either version 2 of the License, or
-  ~ (at your option) any later version.
-  ~
-  ~ This program 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 General Public License for more details.
-  ~
-  ~ You should have received a copy of the GNU General Public License
-  ~ along with this program; if not, write to the Free Software
-  ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-  -->
-
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 72 72"><title>1-icons_expanded</title><path d="M59.23,34.43a38,38,0,0,0-8-3.38V22.19a15.17,15.17,0,0,0-30.34,0v8.86A38.07,38.07,0,0,0,13,34.37a1.5,1.5,0,0,0-.76,1.3V57.93a1.5,1.5,0,0,0,.76,1.3c6.16,3.51,14.35,5.45,23.07,5.45s17-2,23.18-5.51a1.5,1.5,0,0,0,.75-1.3V35.73A1.5,1.5,0,0,0,59.23,34.43ZM23.88,22.19a12.17,12.17,0,1,1,24.34,0v8.08a55.87,55.87,0,0,0-24.34,0V22.19ZM57,57c-5.64,3-13,4.69-20.93,4.69S20.85,60,15.23,57V36.56c5.63-3,13-4.64,20.82-4.64S51.34,33.59,57,36.62V57Z" fill="gray"/></svg>

+ 0 - 23
client/images/icons/m_orgchart_thin.svg

@@ -1,23 +0,0 @@
-<!--
-  ~ Password Management Servlets (PWM)
-  ~ http://www.pwm-project.org
-  ~
-  ~ Copyright (c) 2006-2009 Novell, Inc.
-  ~ Copyright (c) 2009-2018 The PWM Project
-  ~
-  ~ This program is free software; you can redistribute it and/or modify
-  ~ it under the terms of the GNU General Public License as published by
-  ~ the Free Software Foundation; either version 2 of the License, or
-  ~ (at your option) any later version.
-  ~
-  ~ This program 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 General Public License for more details.
-  ~
-  ~ You should have received a copy of the GNU General Public License
-  ~ along with this program; if not, write to the Free Software
-  ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-  -->
-
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 72 72"><title>m_brand</title><path d="M65,43.46H55.4V35.75a1,1,0,0,0-1-1H37.48V28.36H47.92a1.5,1.5,0,0,0,1.5-1.5V8.91a1.5,1.5,0,0,0-1.5-1.5H25.18a1.5,1.5,0,0,0-1.5,1.5V26.86a1.5,1.5,0,0,0,1.5,1.5H35.48v6.39H18.55a1,1,0,0,0-1,1v7.71H7.93A1.5,1.5,0,0,0,6.43,45V62.9a1.5,1.5,0,0,0,1.5,1.5H29.17a1.5,1.5,0,0,0,1.5-1.5V45a1.5,1.5,0,0,0-1.5-1.5H19.55V36.75H53.4v6.71H43.78a1.5,1.5,0,0,0-1.5,1.5V62.9a1.5,1.5,0,0,0,1.5,1.5H65a1.5,1.5,0,0,0,1.5-1.5V45A1.5,1.5,0,0,0,65,43.46Zm-38.34-33H46.42V25.36H26.68V10.41Zm1,51H9.43V46.46H27.67V61.4Zm35.85,0H45.28V46.46H63.52V61.4Z" fill="gray"/></svg>

+ 0 - 23
client/images/icons/m_password_thin.svg

@@ -1,23 +0,0 @@
-<!--
-  ~ Password Management Servlets (PWM)
-  ~ http://www.pwm-project.org
-  ~
-  ~ Copyright (c) 2006-2009 Novell, Inc.
-  ~ Copyright (c) 2009-2017 The PWM Project
-  ~
-  ~ This program is free software; you can redistribute it and/or modify
-  ~ it under the terms of the GNU General Public License as published by
-  ~ the Free Software Foundation; either version 2 of the License, or
-  ~ (at your option) any later version.
-  ~
-  ~ This program 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 General Public License for more details.
-  ~
-  ~ You should have received a copy of the GNU General Public License
-  ~ along with this program; if not, write to the Free Software
-  ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-  -->
-
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 72 72"><title>1-icons_expanded</title><path d="M62.7,57.32H28a21.38,21.38,0,0,1,0-42.75H62.7v3H28c-9.75,0-18,8.41-18,18.37s8.24,18.37,18,18.37H62.7v3Z" fill="gray"/><circle cx="27.2" cy="35.95" r="4.66" fill="gray"/><circle cx="58.34" cy="35.95" r="4.66" fill="gray"/><circle cx="42.69" cy="35.95" r="4.66" fill="gray"/></svg>

+ 0 - 23
client/images/icons/m_reload_refresh_thin.svg

@@ -1,23 +0,0 @@
-<!--
-  ~ Password Management Servlets (PWM)
-  ~ http://www.pwm-project.org
-  ~
-  ~ Copyright (c) 2006-2009 Novell, Inc.
-  ~ Copyright (c) 2009-2017 The PWM Project
-  ~
-  ~ This program is free software; you can redistribute it and/or modify
-  ~ it under the terms of the GNU General Public License as published by
-  ~ the Free Software Foundation; either version 2 of the License, or
-  ~ (at your option) any later version.
-  ~
-  ~ This program 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 General Public License for more details.
-  ~
-  ~ You should have received a copy of the GNU General Public License
-  ~ along with this program; if not, write to the Free Software
-  ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-  -->
-
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 72 72"><title>m_014_reload_refresh_thin</title><path d="M52.91,20.76a1.5,1.5,0,0,0-2.21,2,22.24,22.24,0,1,1-11.36-6.65l-6.51,6.51A1.52,1.52,0,0,0,35,24.76l9.12-9.12a1.5,1.5,0,0,0,0-2.12l-9-9A1.5,1.5,0,0,0,33,6.64L39.36,13a25.26,25.26,0,1,0,13.55,7.74Z" fill="gray"/></svg>

+ 0 - 23
client/images/icons/m_search_thick.svg

@@ -1,23 +0,0 @@
-<!--
-  ~ Password Management Servlets (PWM)
-  ~ http://www.pwm-project.org
-  ~
-  ~ Copyright (c) 2006-2009 Novell, Inc.
-  ~ Copyright (c) 2009-2018 The PWM Project
-  ~
-  ~ This program is free software; you can redistribute it and/or modify
-  ~ it under the terms of the GNU General Public License as published by
-  ~ the Free Software Foundation; either version 2 of the License, or
-  ~ (at your option) any later version.
-  ~
-  ~ This program 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 General Public License for more details.
-  ~
-  ~ You should have received a copy of the GNU General Public License
-  ~ along with this program; if not, write to the Free Software
-  ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-  -->
-
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 72 72"><title>1-icons_expanded</title><path d="M64.5,61.57,46.4,42.7a20.11,20.11,0,1,0-2.88,2.77l18.1,18.87ZM15.16,29.77a16,16,0,1,1,16,16A16,16,0,0,1,15.16,29.77Z" fill="gray"/></svg>

+ 0 - 23
client/images/icons/m_unlock_thin.svg

@@ -1,23 +0,0 @@
-<!--
-  ~ Password Management Servlets (PWM)
-  ~ http://www.pwm-project.org
-  ~
-  ~ Copyright (c) 2006-2009 Novell, Inc.
-  ~ Copyright (c) 2009-2017 The PWM Project
-  ~
-  ~ This program is free software; you can redistribute it and/or modify
-  ~ it under the terms of the GNU General Public License as published by
-  ~ the Free Software Foundation; either version 2 of the License, or
-  ~ (at your option) any later version.
-  ~
-  ~ This program 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 General Public License for more details.
-  ~
-  ~ You should have received a copy of the GNU General Public License
-  ~ along with this program; if not, write to the Free Software
-  ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-  -->
-
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 72 72"><title>1-icons_expanded</title><path d="M59.23,34.43c-6.17-3.55-14.41-5.51-23.18-5.51a54.71,54.71,0,0,0-12.17,1.34V22.18a12.17,12.17,0,0,1,24.34,0h3a15.17,15.17,0,0,0-30.34,0V31A38.09,38.09,0,0,0,13,34.36a1.5,1.5,0,0,0-.76,1.3V57.92a1.5,1.5,0,0,0,.76,1.3c6.16,3.51,14.35,5.45,23.07,5.45s17-2,23.18-5.51a1.5,1.5,0,0,0,.75-1.3V35.73A1.5,1.5,0,0,0,59.23,34.43ZM57,57c-5.64,3-13,4.69-20.93,4.69S20.85,60,15.23,57V36.55c5.63-3,13-4.64,20.82-4.64S51.34,33.58,57,36.61V57Z" fill="gray"/></svg>

+ 0 - 23
client/images/icons/m_up_thick.svg

@@ -1,23 +0,0 @@
-<!--
-  ~ Password Management Servlets (PWM)
-  ~ http://www.pwm-project.org
-  ~
-  ~ Copyright (c) 2006-2009 Novell, Inc.
-  ~ Copyright (c) 2009-2018 The PWM Project
-  ~
-  ~ This program is free software; you can redistribute it and/or modify
-  ~ it under the terms of the GNU General Public License as published by
-  ~ the Free Software Foundation; either version 2 of the License, or
-  ~ (at your option) any later version.
-  ~
-  ~ This program 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 General Public License for more details.
-  ~
-  ~ You should have received a copy of the GNU General Public License
-  ~ along with this program; if not, write to the Free Software
-  ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-  -->
-
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 72 72"><title>1-icons_expanded</title><polygon points="61.58 51.96 36.01 26.4 10.67 51.74 7.84 48.91 36.01 20.74 64.41 49.13 61.58 51.96" fill="gray"/></svg>

+ 0 - 23
client/images/icons/m_view-list_thin.svg

@@ -1,23 +0,0 @@
-<!--
-  ~ Password Management Servlets (PWM)
-  ~ http://www.pwm-project.org
-  ~
-  ~ Copyright (c) 2006-2009 Novell, Inc.
-  ~ Copyright (c) 2009-2018 The PWM Project
-  ~
-  ~ This program is free software; you can redistribute it and/or modify
-  ~ it under the terms of the GNU General Public License as published by
-  ~ the Free Software Foundation; either version 2 of the License, or
-  ~ (at your option) any later version.
-  ~
-  ~ This program 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 General Public License for more details.
-  ~
-  ~ You should have received a copy of the GNU General Public License
-  ~ along with this program; if not, write to the Free Software
-  ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-  -->
-
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 72 72"><title>1-icons_special</title><rect x="13.31" y="9.52" width="45.01" height="3" fill="gray"/><rect x="13.31" y="34.31" width="45.01" height="3" fill="gray"/><rect x="13.31" y="21.92" width="45.01" height="3" fill="gray"/><rect x="13.31" y="46.7" width="45.01" height="3" fill="gray"/><rect x="13.31" y="59.1" width="45.01" height="3" fill="gray"/><rect x="0.36" y="0.36" width="71.28" height="71.28" fill="none"/></svg>

+ 0 - 23
client/images/icons/m_view-tile_thin.svg

@@ -1,23 +0,0 @@
-<!--
-  ~ Password Management Servlets (PWM)
-  ~ http://www.pwm-project.org
-  ~
-  ~ Copyright (c) 2006-2009 Novell, Inc.
-  ~ Copyright (c) 2009-2018 The PWM Project
-  ~
-  ~ This program is free software; you can redistribute it and/or modify
-  ~ it under the terms of the GNU General Public License as published by
-  ~ the Free Software Foundation; either version 2 of the License, or
-  ~ (at your option) any later version.
-  ~
-  ~ This program 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 General Public License for more details.
-  ~
-  ~ You should have received a copy of the GNU General Public License
-  ~ along with this program; if not, write to the Free Software
-  ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-  -->
-
-<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 72 72"><title>1-icons_expanded</title><path d="M31.35,32.85H8.76a1.5,1.5,0,0,1-1.5-1.5V8.91a1.5,1.5,0,0,1,1.5-1.5H31.35a1.5,1.5,0,0,1,1.5,1.5V31.35A1.5,1.5,0,0,1,31.35,32.85Zm-21.09-3H29.85V10.41H10.26V29.85Z" fill="gray"/><path d="M63,32.85H40.41a1.5,1.5,0,0,1-1.5-1.5V8.91a1.5,1.5,0,0,1,1.5-1.5H63a1.5,1.5,0,0,1,1.5,1.5V31.35A1.5,1.5,0,0,1,63,32.85Zm-21.09-3H61.5V10.41H41.91V29.85Z" fill="gray"/><path d="M31.35,64.5H8.76A1.5,1.5,0,0,1,7.26,63V40.56a1.5,1.5,0,0,1,1.5-1.5H31.35a1.5,1.5,0,0,1,1.5,1.5V63A1.5,1.5,0,0,1,31.35,64.5Zm-21.09-3H29.85V42.06H10.26V61.5Z" fill="gray"/><path d="M63,64.5H40.41a1.5,1.5,0,0,1-1.5-1.5V40.56a1.5,1.5,0,0,1,1.5-1.5H63a1.5,1.5,0,0,1,1.5,1.5V63A1.5,1.5,0,0,1,63,64.5Zm-21.09-3H61.5V42.06H41.91V61.5Z" fill="gray"/></svg>

+ 4 - 0
client/index.html

@@ -27,6 +27,8 @@
     <meta name="viewport" content="initial-scale=1, maximum-scale=1">
     <title>PWM Development</title>
 
+    <link rel="stylesheet" href="vendor/ias-icons.css">
+    <link rel="stylesheet" href="vendor/ux-ias.css">
     <style>
         html, body {
             margin: 0;
@@ -39,7 +41,9 @@
 <ui-view>Loading...</ui-view>
 
 <script src="vendor/angular.js"></script>
+<script src="vendor/angular-aria.js"></script>
 <script src="vendor/angular-translate.js"></script>
 <script src="vendor/angular-ui-router.js"></script>
+<script src="vendor/ng-ias.js"></script>
 </body>
 </html>

+ 72 - 72
client/package.json

@@ -1,74 +1,74 @@
 {
-    "name": "ng1-webpack",
-    "version": "0.0.1",
-    "description": "",
-    "main": "index.js",
-    "engines": {
-        "node": ">=6.2",
-        "npm": ">=3.9"
-    },
-    "scripts": {
-        "build": "webpack --config=webpack.build.js",
-        "clean": "rimraf dist/",
-        "test": "karma start",
-        "test-single-run": "karma start --singleRun --no-auto-watch",
-        "start": "webpack-dev-server --config=webpack.dev.js --colors",
-        "sync": "webpack --config=webpack.build.js --output-path=../server/target/pwm-1.8.0-SNAPSHOT/public/resources/webjars/pwm-client --watch --colors"
-    },
-    "author": "",
-    "license": "ISC",
-    "dependencies": {},
-    "devDependencies": {
-        "@types/angular": "1.6.42",
-        "@types/angular-mocks": "1.5.11",
-        "@types/angular-translate": "2.15.1",
-        "@types/angular-ui-router": "1.1.40",
-        "@types/jasmine": "2.8.6",
-        "@types/node": "9.4.2",
-        "@uirouter/angularjs": "1.0.14",
-        "angular": "1.6.9",
-        "angular-mocks": "1.6.9",
-        "angular-translate": "2.17.0",
-        "autoprefixer": "7.2.5",
-        "copy-webpack-plugin": "4.4.1",
-        "css-loader": "0.28.9",
-        "file-loader": "1.1.6",
-        "fontgen-loader": "0.2.1",
-        "html-loader": "0.5.5",
-        "html-webpack-plugin": "2.30.1",
-        "ignore-loader": "0.1.2",
-        "jasmine": "3.0.0",
-        "jasmine-core": "^2.99.1",
-        "jshint": "2.9.5",
-        "jshint-loader": "0.8.4",
-        "json-loader": "0.5.7",
-        "karma": "2.0.0",
-        "karma-chrome-launcher": "2.2.0",
-        "karma-jasmine": "1.1.1",
-        "karma-jasmine-html-reporter": "0.2.2",
-        "karma-phantomjs-launcher": "1.0.4",
-        "karma-sourcemap-loader": "0.3.7",
-        "karma-spec-reporter": "0.0.32",
-        "karma-webpack": "2.0.9",
-        "ngtemplate-loader": "2.0.1",
-        "node-sass": "4.7.2",
-        "phantomjs": "2.1.7",
-        "phantomjs-prebuilt": "2.1.16",
-        "postcss-loader": "2.1.0",
-        "raw-loader": "0.5.1",
-        "rimraf": "2.6.2",
-        "sass-loader": "6.0.6",
-        "string-replace-loader": "1.3.0",
-        "style-loader": "0.20.1",
-        "ts-loader": "3.5.0",
-        "ts-mockito": "2.2.9",
-        "tslint": "^5.9.1",
-        "tslint-loader": "3.5.3",
-        "typescript": "2.7.1",
-        "url-loader": "0.6.2",
-        "webpack": "3.10.0",
-        "webpack-dev-server": "2.11.1",
-        "webpack-merge": "4.1.1",
-        "write-file-webpack-plugin": "4.2.0"
-    }
+  "name": "ng1-webpack",
+  "version": "0.0.1",
+  "description": "",
+  "main": "index.js",
+  "engines": {
+    "node": ">=6.2",
+    "npm": ">=3.9"
+  },
+  "scripts": {
+    "build": "webpack --config=webpack.build.js --NODE_ENV=production",
+    "clean": "rimraf dist/",
+    "test": "karma start --NODE_ENV=test",
+    "test-single-run": "karma start --NODE_ENV=test --singleRun --no-auto-watch",
+    "start": "webpack-dev-server --config=webpack.dev.js --NODE_ENV=dev --colors",
+    "sync": "webpack --config=webpack.build.js --NODE_ENV=production --output-path=../server/src/main/webapp/public/resources/webjars/pwm-client --watch --colors"
+  },
+  "author": "",
+  "license": "ISC",
+  "dependencies": {},
+  "devDependencies": {
+    "@types/angular": "1.6.42",
+    "@types/angular-mocks": "1.5.11",
+    "@types/angular-translate": "2.15.1",
+    "@types/angular-ui-router": "1.1.40",
+    "@types/jasmine": "2.8.6",
+    "@types/node": "9.4.2",
+    "@uirouter/angularjs": "1.0.14",
+    "angular": "1.6.9",
+    "angular-aria": "1.6.9",
+    "angular-mocks": "1.6.9",
+    "angular-translate": "2.17.0",
+    "autoprefixer": "7.2.5",
+    "copy-webpack-plugin": "4.4.1",
+    "css-loader": "0.28.9",
+    "file-loader": "1.1.6",
+    "html-loader": "0.5.5",
+    "html-webpack-plugin": "2.30.1",
+    "ignore-loader": "0.1.2",
+    "jasmine": "3.0.0",
+    "jasmine-core": "^2.99.1",
+    "jshint": "2.9.5",
+    "jshint-loader": "0.8.4",
+    "json-loader": "0.5.7",
+    "karma": "2.0.0",
+    "karma-chrome-launcher": "2.2.0",
+    "karma-jasmine": "1.1.1",
+    "karma-jasmine-html-reporter": "0.2.2",
+    "karma-phantomjs-launcher": "1.0.4",
+    "karma-sourcemap-loader": "0.3.7",
+    "karma-spec-reporter": "0.0.32",
+    "karma-webpack": "2.0.9",
+    "ngtemplate-loader": "2.0.1",
+    "node-sass": "4.7.2",
+    "phantomjs": "2.1.7",
+    "phantomjs-prebuilt": "2.1.16",
+    "postcss-loader": "2.1.0",
+    "raw-loader": "0.5.1",
+    "rimraf": "2.6.2",
+    "sass-loader": "6.0.6",
+    "string-replace-loader": "1.3.0",
+    "style-loader": "0.20.1",
+    "ts-loader": "3.5.0",
+    "ts-mockito": "2.2.9",
+    "tslint": "^5.9.1",
+    "tslint-loader": "3.5.3",
+    "typescript": "2.7.1",
+    "url-loader": "0.6.2",
+    "webpack": "3.10.0",
+    "webpack-dev-server": "2.11.1",
+    "webpack-merge": "4.1.1",
+    "write-file-webpack-plugin": "4.2.0"
+  }
 }

+ 30 - 28
client/src/changepassword/autogen-change-password.component.html

@@ -3,7 +3,7 @@
   ~ http://www.pwm-project.org
   ~
   ~ Copyright (c) 2006-2009 Novell, Inc.
-  ~ Copyright (c) 2009-2017 The PWM Project
+  ~ Copyright (c) 2009-2018 The PWM Project
   ~
   ~ This program is free software; you can redistribute it and/or modify
   ~ it under the terms of the GNU General Public License as published by
@@ -20,34 +20,36 @@
   ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   -->
 
-<ias-dialog class="autogen-change-password-dialog">
-    <div class="ias-dialog-header">
-        <div class="ias-title" ng-bind="'Title_RandomPasswords' | translate"></div>
-    </div>
-    <div class="ias-dialog-body">
-        <p ng-bind="'Display_PasswordGeneration' | translate"></p>
-        <table>
-            <tbody>
-            <tr ng-repeat="i in [0,2,4,6,8,10,12,14,16,18]">
-                <td ng-repeat="j in [i, i+1]">
-                    <div ng-bind="$ctrl.passwordSuggestions[j]" ng-click="$ctrl.onChoosePasswordSuggestion(j)">
-                    </div>
-                </td>
-            </tr>
-            </tbody>
-        </table>
-    </div>
-    <div class="ias-actions">
-        <mf-button ng-click="$ctrl.populatePasswordSuggestions()"
-                   ng-disabled="$ctrl.fetchingRandoms">{{ 'Button_More' | translate }}
-        </mf-button>
-        <mf-button ng-click="cancel()">{{ 'Button_Cancel' | translate }}</mf-button>
-    </div>
+<div class="ias-dialog autogen-change-password-dialog">
+    <div class="ias-dialog-container">
+        <div class="ias-dialog-label">
+            <div class="ias-title" ng-bind="'Title_RandomPasswords' | translate"></div>
+        </div>
+        <div class="ias-dialog-content">
+            <p ng-bind="'Display_PasswordGeneration' | translate"></p>
+            <table>
+                <tbody>
+                <tr ng-repeat="i in [0,2,4,6,8,10,12,14,16,18]">
+                    <td ng-repeat="j in [i, i+1]">
+                        <div ng-bind="$ctrl.passwordSuggestions[j]" ng-click="$ctrl.onChoosePasswordSuggestion(j)">
+                        </div>
+                    </td>
+                </tr>
+                </tbody>
+            </table>
+        </div>
+        <div class="ias-actions">
+            <ias-button ng-click="$ctrl.populatePasswordSuggestions()"
+                        ng-disabled="$ctrl.fetchingRandoms">{{ 'Button_More' | translate }}
+            </ias-button>
+            <ias-button ng-click="cancel()">{{ 'Button_Cancel' | translate }}</ias-button>
+        </div>
 
-    <mf-icon-button class="ias-dialog-close-button"
-                    icon="close_thick"
+        <ias-button class="ias-icon-button ias-dialog-cancel-button"
                     id="close-icon"
                     ng-attr-title="{{ 'Button_CloseWindow' | translate }}"
                     ng-click="cancel()">
-    </mf-icon-button>
-</ias-dialog>
+            <ias-icon icon="close_thick"></ias-icon>
+        </ias-button>
+    </div>
+</div>

+ 1 - 1
client/src/changepassword/autogen-change-password.component.scss

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  *
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by

+ 3 - 4
client/src/changepassword/autogen-change-password.controller.ts

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  *
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -23,7 +23,6 @@
 
 import {IHelpDeskService, IRandomPasswordResponse, ISuccessResponse} from '../services/helpdesk.service';
 import {IPromise, IQService} from 'angular';
-import DialogService from '../ux/ias-dialog.service';
 import {IChangePasswordSuccess} from './success-change-password.controller';
 
 const RANDOM_MAPPING_SIZE = 20;
@@ -37,9 +36,9 @@ export default class AutogenChangePasswordController {
     static $inject = [ '$q', 'HelpDeskService', 'IasDialogService', 'personUserKey' ];
     constructor(private $q: IQService,
                 private HelpDeskService: IHelpDeskService,
-                private IasDialogService: DialogService,
+                private IasDialogService: any,
                 private personUserKey: string) {
-        this.passwordSuggestions = Array(20).fill('');
+        this.passwordSuggestions = Array<string>(20).fill('');
         this.populatePasswordSuggestions();
     }
 

+ 20 - 16
client/src/changepassword/random-change-password.component.html

@@ -3,7 +3,7 @@
   ~ http://www.pwm-project.org
   ~
   ~ Copyright (c) 2006-2009 Novell, Inc.
-  ~ Copyright (c) 2009-2017 The PWM Project
+  ~ Copyright (c) 2009-2018 The PWM Project
   ~
   ~ This program is free software; you can redistribute it and/or modify
   ~ it under the terms of the GNU General Public License as published by
@@ -20,22 +20,26 @@
   ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   -->
 
-<ias-dialog class="random-change-password-dialog">
-    <div class="ias-dialog-header">
-        <div class="ias-title" ng-bind="('Title_ChangePassword' | translate) + ': ' + $ctrl.personUsername"></div>
-    </div>
-    <div class="ias-dialog-body">
-        <p ng-bind="'Display_SetRandomPasswordPrompt' | translate"></p>
-    </div>
-    <div class="ias-actions">
-        <mf-button ng-click="$ctrl.confirmSetRandomPassword()">{{ 'Button_OK' | translate }}</mf-button>
-        <mf-button ng-click="cancel()">{{ 'Button_Cancel' | translate }}</mf-button>
-    </div>
+<div class="ias-dialog random-change-password-dialog">
+    <div class="ias-dialog-container">
+        <div class="ias-dialog-label">
+            <div class="ias-title" ng-bind="('Title_ChangePassword' | translate) + ': ' + $ctrl.personUsername"></div>
+        </div>
 
-    <mf-icon-button class="ias-dialog-close-button"
-                    icon="close_thick"
+        <div class="ias-dialog-content">
+            <p ng-bind="'Display_SetRandomPasswordPrompt' | translate"></p>
+        </div>
+
+        <div class="ias-actions">
+            <ias-button ng-click="$ctrl.confirmSetRandomPassword()">{{ 'Button_OK' | translate }}</ias-button>
+            <ias-button ng-click="cancel()">{{ 'Button_Cancel' | translate }}</ias-button>
+        </div>
+
+        <ias-button class="ias-icon-button ias-dialog-cancel-button"
                     id="close-icon"
                     ng-attr-title="{{ 'Button_CloseWindow' | translate }}"
                     ng-click="cancel()">
-    </mf-icon-button>
-</ias-dialog>
+            <ias-icon icon="close_thick"></ias-icon>
+        </ias-button>
+    </div>
+</div>

+ 2 - 3
client/src/changepassword/random-change-password.controller.ts

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  *
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -22,7 +22,6 @@
 
 
 import {IHelpDeskService, ISuccessResponse} from '../services/helpdesk.service';
-import DialogService from '../ux/ias-dialog.service';
 import {IChangePasswordSuccess} from './success-change-password.controller';
 
 export default class RandomChangePasswordController {
@@ -35,7 +34,7 @@ export default class RandomChangePasswordController {
         'translateFilter'
     ];
     constructor(private HelpDeskService: IHelpDeskService,
-                private IasDialogService: DialogService,
+                private IasDialogService: any,
                 private personUsername: string,
                 private personUserKey: string,
                 private translateFilter: (id: string) => string) {

+ 27 - 23
client/src/changepassword/success-change-password.component.html

@@ -3,7 +3,7 @@
   ~ http://www.pwm-project.org
   ~
   ~ Copyright (c) 2006-2009 Novell, Inc.
-  ~ Copyright (c) 2009-2017 The PWM Project
+  ~ Copyright (c) 2009-2018 The PWM Project
   ~
   ~ This program is free software; you can redistribute it and/or modify
   ~ it under the terms of the GNU General Public License as published by
@@ -20,29 +20,33 @@
   ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   -->
 
-<ias-dialog class="success-change-password-dialog">
-    <div class="ias-dialog-header">
-        <div class="ias-title" ng-bind="('Title_ChangePassword' | translate) + ' - ' + $ctrl.personUsername"></div>
-    </div>
-    <div class="ias-dialog-body">
-        <p ng-bind="$ctrl.successMessage"></p>
-        <span ng-bind="'Field_NewPassword' | translate"></span>
-        <mf-button ng-click="$ctrl.togglePasswordMasked()" ng-if="$ctrl.maskPasswords">
-            {{ 'Button_Show' | translate }}
-        </mf-button>
-        <input ng-model="$ctrl.password" ng-hide="$ctrl.passwordMasked" readonly type="text">
-    </div>
-    <div class="ias-actions">
-        <mf-button ng-click="cancel()">{{ 'Button_OK' | translate }}</mf-button>
-        <mf-button ng-click="$ctrl.clearAnswers()"
-                   ng-if="$ctrl.clearResponsesSetting==='ask'">{{ 'Button_ClearResponses' | translate }}
-        </mf-button>
-    </div>
+<div class="ias-dialog success-change-password-dialog">
+    <div class="ias-dialog-container">
+        <div class="ias-dialog-label">
+            <div class="ias-title" ng-bind="('Title_ChangePassword' | translate) + ' - ' + $ctrl.personUsername"></div>
+        </div>
 
-    <mf-icon-button class="ias-dialog-close-button"
-                    icon="close_thick"
+        <div class="ias-dialog-content">
+            <p ng-bind="$ctrl.successMessage"></p>
+            <span ng-bind="'Field_NewPassword' | translate"></span>
+            <ias-button ng-click="$ctrl.togglePasswordMasked()" ng-if="$ctrl.maskPasswords">
+                {{ 'Button_Show' | translate }}
+            </ias-button>
+            <input ng-model="$ctrl.password" ng-hide="$ctrl.passwordMasked" readonly type="text">
+        </div>
+
+        <div class="ias-actions">
+            <ias-button ng-click="cancel()">{{ 'Button_OK' | translate }}</ias-button>
+            <ias-button ng-click="$ctrl.clearAnswers()"
+                       ng-if="$ctrl.clearResponsesSetting==='ask'">{{ 'Button_ClearResponses' | translate }}
+            </ias-button>
+        </div>
+
+        <ias-button class="ias-icon-button ias-dialog-cancel-button"
                     id="close-icon"
                     ng-attr-title="{{ 'Button_CloseWindow' | translate }}"
                     ng-click="cancel()">
-    </mf-icon-button>
-</ias-dialog>
+            <ias-icon icon="close_thick"></ias-icon>
+        </ias-button>
+    </div>
+</div>

+ 2 - 3
client/src/changepassword/success-change-password.controller.ts

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  *
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -24,7 +24,6 @@
 import {IHelpDeskService } from '../services/helpdesk.service';
 import {IQService} from 'angular';
 import {IHelpDeskConfigService} from '../services/helpdesk-config.service';
-import DialogService from '../ux/ias-dialog.service';
 
 export interface IChangePasswordSuccess {
     password: string;
@@ -52,7 +51,7 @@ export default class SuccessChangePasswordController {
                 changePasswordSuccessData: IChangePasswordSuccess,
                 private configService: IHelpDeskConfigService,
                 private HelpDeskService: IHelpDeskService,
-                private IasDialogService: DialogService,
+                private IasDialogService: any,
                 private personUsername: string,
                 private personUserKey: string,
                 private translateFilter: (id: string) => string) {

+ 64 - 59
client/src/changepassword/type-change-password.component.html

@@ -3,7 +3,7 @@
   ~ http://www.pwm-project.org
   ~
   ~ Copyright (c) 2006-2009 Novell, Inc.
-  ~ Copyright (c) 2009-2017 The PWM Project
+  ~ Copyright (c) 2009-2018 The PWM Project
   ~
   ~ This program is free software; you can redistribute it and/or modify
   ~ it under the terms of the GNU General Public License as published by
@@ -20,67 +20,72 @@
   ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   -->
 
-<ias-dialog class="type-change-password-dialog">
-    <div class="ias-dialog-header">
-        <div class="ias-title" ng-bind="('Title_ChangePassword' | translate) + ' - ' + $ctrl.personUsername"></div>
-    </div>
-    <div class="ias-dialog-body">
-        <p ng-bind="$ctrl.message"></p>
+<div class="ias-dialog type-change-password-dialog">
+    <div class="ias-dialog-container">
+        <div class="ias-dialog-label">
+            <div class="ias-title" ng-bind="('Title_ChangePassword' | translate) + ' - ' + $ctrl.personUsername"></div>
+        </div>
+        <div class="ias-dialog-content">
+            <p ng-bind="$ctrl.message"></p>
 
-        <table>
-        <tbody>
-            <tr>
-                <td>
-                    <input ng-model="$ctrl.password1" ng-hide="$ctrl.password1Masked" type="text">
-                    <input ng-model="$ctrl.password1" ng-show="$ctrl.password1Masked" type="password">
-                    <mf-icon-button
-                        icon="password_thin"
-                        id="password-icon1"
-                        ng-attr-title="{{ 'Button_Show' | translate }}"
-                        ng-click="$ctrl.togglePassword1Masked()"
-                        ng-if="$ctrl.maskPasswords"
-                        ng-show="!!$ctrl.password1"></mf-icon-button>
-                </td>
-                <td>
-                    <div ng-if="$ctrl.showStrengthMeter" ng-show="!!$ctrl.password1">
-                        <div>Strength: <span class="strength-label" ng-bind="$ctrl.strength"></span>
+            <table>
+                <tbody>
+                <tr>
+                    <td>
+                        <input ng-model="$ctrl.password1" ng-hide="$ctrl.password1Masked" type="text">
+                        <input ng-model="$ctrl.password1" ng-show="$ctrl.password1Masked" type="password">
+                        <ias-button class="ias-icon-button"
+                                    id="password-icon1"
+                                    ng-attr-title="{{ 'Button_Show' | translate }}"
+                                    ng-click="$ctrl.togglePassword1Masked()"
+                                    ng-if="$ctrl.maskPasswords"
+                                    ng-show="!!$ctrl.password1">
+                            <ias-icon icon="password_thin"></ias-icon>
+                        </ias-button>
+                    </td>
+                    <td>
+                        <div ng-if="$ctrl.showStrengthMeter" ng-show="!!$ctrl.password1">
+                            <div>Strength: <span class="strength-label" ng-bind="$ctrl.strength"></span>
+                            </div>
+                            <div style="height:10px; width:80px; background-color:red; border:1px solid black;"></div>
                         </div>
-                        <div style="height:10px; width:80px; background-color:red; border:1px solid black;"></div>
-                    </div>
-                </td>
-            </tr>
-            <tr>
-                <td>
-                    <input ng-model="$ctrl.password2" ng-hide="$ctrl.password2Masked" type="text">
-                    <input ng-model="$ctrl.password2" ng-show="$ctrl.password2Masked" type="password">
-                    <mf-icon-button
-                        icon="password_thin"
-                        id="password-icon2"
-                        ng-attr-title="{{ 'Button_Show' | translate }}"
-                        ng-click="$ctrl.togglePassword2Masked()"
-                        ng-if="$ctrl.maskPasswords"
-                        ng-show="!!$ctrl.password2"></mf-icon-button>
-                </td>
-                <td>
-                    <span>check</span>
-                    <span>X</span>
-                </td>
-            </tr>
-        </tbody>
-        </table>
-    </div>
-    <div class="ias-actions">
-        <mf-button ng-click="$ctrl.chooseTypedPassword()"
-                   ng-disabled="!$ctrl.passwordAcceptable">{{ 'Button_ChangePassword' | translate }}</mf-button>
-        <mf-button ng-click="$ctrl.status = 'autogen'"
-                   ng-if="$ctrl.passwordUiMode === 'BOTH'">{{ 'Title_RandomPasswords' | translate }}
-        </mf-button>
-    </div>
+                    </td>
+                </tr>
+                <tr>
+                    <td>
+                        <input ng-model="$ctrl.password2" ng-hide="$ctrl.password2Masked" type="text">
+                        <input ng-model="$ctrl.password2" ng-show="$ctrl.password2Masked" type="password">
+                        <ias-button class="ias-icon-button"
+                                    id="password-icon2"
+                                    ng-attr-title="{{ 'Button_Show' | translate }}"
+                                    ng-click="$ctrl.togglePassword2Masked()"
+                                    ng-if="$ctrl.maskPasswords"
+                                    ng-show="!!$ctrl.password2">
+                            <ias-icon icon="password_thin"></ias-icon>
+                        </ias-button>
+                    </td>
+                    <td>
+                        <span>check</span>
+                        <span>X</span>
+                    </td>
+                </tr>
+                </tbody>
+            </table>
+        </div>
+        <div class="ias-actions">
+            <ias-button ng-click="$ctrl.chooseTypedPassword()"
+                        ng-disabled="!$ctrl.passwordAcceptable">{{ 'Button_ChangePassword' | translate }}
+            </ias-button>
+            <ias-button ng-click="$ctrl.status = 'autogen'"
+                        ng-if="$ctrl.passwordUiMode === 'BOTH'">{{ 'Title_RandomPasswords' | translate }}
+            </ias-button>
+        </div>
 
-    <mf-icon-button class="ias-dialog-close-button"
-                    icon="close_thick"
+        <ias-button class="ias-icon-button ias-dialog-cancel-button"
                     id="close-icon"
                     ng-attr-title="{{ 'Button_CloseWindow' | translate }}"
                     ng-click="cancel()">
-    </mf-icon-button>
-</ias-dialog>
+            <ias-icon icon="close_thick"></ias-icon>
+        </ias-button>
+    </div>
+</div>

+ 3 - 11
client/src/changepassword/type-change-password.component.scss

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  *
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -22,9 +22,9 @@
 
 ias-dialog {
   &.type-change-password-dialog {
-    .ias-dialog-body {
+    .ias-dialog-content {
       table {
-        input, mf-icon-button {
+        input {
           vertical-align: middle
         }
 
@@ -32,14 +32,6 @@ ias-dialog {
           margin: 7px;
         }
 
-        mf-icon-button {
-          display: inline-block;
-
-          mf-icon {
-            font-size: 20px;
-          }
-        }
-
         td {
           min-width: 250px;
         }

+ 3 - 4
client/src/changepassword/type-change-password.controller.ts

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  *
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -24,7 +24,6 @@
 import {IHelpDeskService, ISuccessResponse} from '../services/helpdesk.service';
 import {IQService, IScope} from 'angular';
 import {IHelpDeskConfigService} from '../services/helpdesk-config.service';
-import DialogService from '../ux/ias-dialog.service';
 import {IChangePasswordSuccess} from './success-change-password.controller';
 
 require('changepassword/type-change-password.component.scss');
@@ -56,12 +55,12 @@ export default class TypeChangePasswordController {
                 private $scope: IScope,
                 private configService: IHelpDeskConfigService,
                 private HelpDeskService: IHelpDeskService,
-                private IasDialogService: DialogService,
+                private IasDialogService: any,
                 private personUsername: string,
                 private personUserKey: string,
                 private translateFilter: (id: string) => string) {
         this.passwordAcceptable = true;
-        this.passwordSuggestions = Array(20).fill('');
+        this.passwordSuggestions = Array<string>(20).fill('');
         this.message = translateFilter('Display_PasswordPrompt');
         this.showStrengthMeter = HelpDeskService.showStrengthMeter;
 

+ 1 - 1
client/src/helpdesk/date.filters.ts

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  *
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by

+ 38 - 33
client/src/helpdesk/helpdesk-detail-dialog.template.html

@@ -3,7 +3,7 @@
   ~ http://www.pwm-project.org
   ~
   ~ Copyright (c) 2006-2009 Novell, Inc.
-  ~ Copyright (c) 2009-2017 The PWM Project
+  ~ Copyright (c) 2009-2018 The PWM Project
   ~
   ~ This program is free software; you can redistribute it and/or modify
   ~ it under the terms of the GNU General Public License as published by
@@ -20,43 +20,48 @@
   ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   -->
 
-<ias-dialog>
-    <div ng-switch="status">
-        <div class="ias-actions" ng-switch-default>
-            <div class="WaitDialogBlank"></div>
-        </div>
+<div class="ias-dialog" ng-switch="status">
+    <div class="ias-dialog-container" ng-switch-default>
+        <div class="WaitDialogBlank"></div>
+    </div>
 
-        <div ng-switch-when="confirm">
-            <div class="ias-dialog-header">
-                <div class="ias-title" ng-bind="title"></div>
-            </div>
-            <div class="ias-dialog-body">
-                <p ng-bind="text"></p>
-                <p ng-bind="secondaryText" ng-if="!!secondaryText"></p>
-            </div>
-            <div class="ias-actions">
-                <mf-button ng-click="confirm()">{{ 'Button_OK' | translate }}</mf-button>
-                <mf-button ng-click="close()">{{ 'Button_Cancel' | translate }}</mf-button>
-            </div>
+    <div class="ias-dialog-container" ng-switch-when="confirm">
+        <div class="ias-dialog-label">
+            <div class="ias-title" ng-bind="title"></div>
         </div>
-
-        <div ng-switch-when="success">
-            <div class="ias-dialog-header">
-                <div class="ias-title" ng-bind="title"></div>
-            </div>
-            <div class="ias-dialog-body">
-                <p ng-bind="text"></p>
-            </div>
-            <div class="ias-actions">
-                <mf-button ng-click="close()">{{ 'Button_OK' | translate }}</mf-button>
-            </div>
+        <div class="ias-dialog-content">
+            <p ng-bind="text"></p>
+            <p ng-bind="secondaryText" ng-if="!!secondaryText"></p>
+        </div>
+        <div class="ias-actions">
+            <ias-button ng-click="confirm()">{{ 'Button_OK' | translate }}</ias-button>
+            <ias-button ng-click="close()">{{ 'Button_Cancel' | translate }}</ias-button>
         </div>
+
+        <ias-button class="ias-icon-button ias-dialog-cancel-button"
+                    id="close-icon"
+                    ng-attr-title="{{ 'Button_CloseWindow' | translate }}"
+                    ng-click="close()">
+            <ias-icon icon="close_thick"></ias-icon>
+        </ias-button>
     </div>
 
-    <mf-icon-button class="ias-dialog-close-button"
-                    icon="close_thick"
+    <div class="ias-dialog-container" ng-switch-when="success">
+        <div class="ias-dialog-label">
+            <div class="ias-title" ng-bind="title"></div>
+        </div>
+        <div class="ias-dialog-content">
+            <p ng-bind="text"></p>
+        </div>
+        <div class="ias-actions">
+            <ias-button ng-click="close()">{{ 'Button_OK' | translate }}</ias-button>
+        </div>
+
+        <ias-button class="ias-icon-button ias-dialog-cancel-button"
                     id="close-icon"
                     ng-attr-title="{{ 'Button_CloseWindow' | translate }}"
                     ng-click="close()">
-    </mf-icon-button>
-</ias-dialog>
+            <ias-icon icon="close_thick"></ias-icon>
+        </ias-button>
+    </div>
+</div>

+ 41 - 59
client/src/helpdesk/helpdesk-detail.component.html

@@ -3,7 +3,7 @@
   ~ http://www.pwm-project.org
   ~
   ~ Copyright (c) 2006-2009 Novell, Inc.
-  ~ Copyright (c) 2009-2017 The PWM Project
+  ~ Copyright (c) 2009-2018 The PWM Project
   ~
   ~ This program is free software; you can redistribute it and/or modify
   ~ it under the terms of the GNU General Public License as published by
@@ -20,35 +20,17 @@
   ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   -->
 
-<mf-app-bar>
-    <div id="page-content-title" class="page-content-title" translate="Title_HelpDesk">Help Desk</div>
-    <mf-icon-button
-        icon="password_thin"
-        ng-attr-title="{{ 'Button_ChangePassword' | translate }}"
-        ng-click=""
-        id="password-icon"></mf-icon-button>
-    <mf-icon-button
-        icon="unlock_thin"
-        ng-attr-title="{{ 'Button_Unlock' | translate }}"
-        ng-click=""
-        id="unlock-icon"></mf-icon-button>
-    <mf-icon-button
-        icon="reload_refresh_thin"
-        ng-attr-title="{{ 'Display_CaptchaRefresh' | translate }}"
-        ng-click=""
-        id="reload-refresh-icon"></mf-icon-button>
-</mf-app-bar>
+<div class="ias-header">
+    <h2 id="page-content-title" translate="Title_HelpDesk">Help Desk</h2>
+</div>
 
-
-
-<person-card person="$ctrl.personCard" show-image="$ctrl.photosEnabled">
-</person-card>
+<person-card person="$ctrl.personCard" show-image="$ctrl.photosEnabled"></person-card>
 
 <div class="help-desk-content">
-    <div class="person-details-content">
-        <mf-tabset>
-            <mf-tab id="Field_Profile" label="Profile">
-                <table>
+    <div>
+        <ias-tabset>
+            <ias-tab id="Field_Profile" label="Profile">
+                <table class="details-table">
                     <tbody>
                     <tr ng-repeat="item in $ctrl.person.profileData">
                         <td ng-bind="item.label"></td>
@@ -58,9 +40,9 @@
                     </tr>
                     </tbody>
                 </table>
-            </mf-tab>
-            <mf-tab id="Title_Status" label="Status">
-                <table>
+            </ias-tab>
+            <ias-tab id="Title_Status" label="Status">
+                <table class="details-table">
                     <tbody>
                     <tr ng-repeat="item in $ctrl.person.statusData">
                         <td ng-bind="item.label"></td>
@@ -70,9 +52,9 @@
                     </tr>
                     </tbody>
                 </table>
-            </mf-tab>
-            <mf-tab ng-if="!!$ctrl.person.userHistory" id="Title_UserEventHistory" label="Password History">
-                <table>
+            </ias-tab>
+            <ias-tab ng-if="!!$ctrl.person.userHistory" id="Title_UserEventHistory" label="Password History">
+                <table class="details-table">
                     <tbody>
                     <tr ng-repeat="item in $ctrl.person.userHistory">
                         <td ng-bind="item.timestamp | dateFilter"></td>
@@ -80,9 +62,9 @@
                     </tr>
                     </tbody>
                 </table>
-            </mf-tab>
-            <mf-tab id="Title_PasswordPolicy" label="Password Policy">
-                <table>
+            </ias-tab>
+            <ias-tab id="Title_PasswordPolicy" label="Password Policy">
+                <table class="details-table">
                     <tbody>
                     <tr>
                         <td ng-bind="'Field_Policy' | translate"></td>
@@ -106,9 +88,9 @@
                     </tr>
                     </tbody>
                 </table>
-            </mf-tab>
-            <mf-tab id="Title_SecurityResponses" label="Security Responses">
-                <table>
+            </ias-tab>
+            <ias-tab id="Title_SecurityResponses" label="Security Responses">
+                <table class="details-table">
                     <tbody>
                     <tr ng-repeat="item in $ctrl.person.helpdeskResponses">
                         <td ng-bind="item.label"></td>
@@ -118,37 +100,37 @@
                     </tr>
                     </tbody>
                 </table>
-            </mf-tab>
-        </mf-tabset>
+            </ias-tab>
+        </ias-tabset>
     </div>
 
     <div class="help-desk-buttons">
-        <mf-button ng-click="$ctrl.gotoSearch()"
+        <ias-button ng-click="$ctrl.gotoSearch()"
                    ng-disabled="$ctrl.buttonDisabled('back')"
-                   ng-if="$ctrl.buttonVisible('back')">{{ 'Button_GoBack' | translate }}</mf-button>
-        <mf-button ng-click="$ctrl.refresh()"
+                   ng-if="$ctrl.buttonVisible('back')">{{ 'Button_GoBack' | translate }}</ias-button>
+        <ias-button ng-click="$ctrl.refresh()"
                    ng-disabled="$ctrl.buttonDisabled('refresh')"
-                   ng-if="$ctrl.buttonVisible('refresh')">{{ 'Display_CaptchaRefresh' | translate }}</mf-button>
-        <mf-button ng-click="$ctrl.changePassword()"
+                   ng-if="$ctrl.buttonVisible('refresh')">{{ 'Display_CaptchaRefresh' | translate }}</ias-button>
+        <ias-button ng-click="$ctrl.changePassword()"
                    ng-disabled="$ctrl.buttonDisabled('changePassword')"
-                   ng-if="$ctrl.buttonVisible('changePassword')">{{ 'Button_ChangePassword' | translate }}</mf-button>
-        <mf-button ng-click="$ctrl.unlockUser()"
+                   ng-if="$ctrl.buttonVisible('changePassword')">{{ 'Button_ChangePassword' | translate }}</ias-button>
+        <ias-button ng-click="$ctrl.unlockUser()"
                    ng-disabled="$ctrl.buttonDisabled('unlock')"
-                   ng-if="$ctrl.buttonVisible('unlock')">{{ 'Button_Unlock' | translate }}</mf-button>
-        <mf-button ng-click="$ctrl.clearResponses()"
+                   ng-if="$ctrl.buttonVisible('unlock')">{{ 'Button_Unlock' | translate }}</ias-button>
+        <ias-button ng-click="$ctrl.clearResponses()"
                    ng-disabled="$ctrl.buttonDisabled('clearResponses')"
-                   ng-if="$ctrl.buttonVisible('clearResponses')">{{ 'Button_ClearResponses' | translate }}</mf-button>
-        <mf-button ng-click="$ctrl.clearOtpSecret()"
+                   ng-if="$ctrl.buttonVisible('clearResponses')">{{ 'Button_ClearResponses' | translate }}</ias-button>
+        <ias-button ng-click="$ctrl.clearOtpSecret()"
                    ng-disabled="$ctrl.buttonDisabled('clearOtpSecret')"
-                   ng-if="$ctrl.buttonVisible('clearOtpSecret')">{{ 'Button_HelpdeskClearOtpSecret' | translate }}</mf-button>
-        <mf-button ng-click="$ctrl.verifyUser()"
+                   ng-if="$ctrl.buttonVisible('clearOtpSecret')">{{ 'Button_HelpdeskClearOtpSecret' | translate }}</ias-button>
+        <ias-button ng-click="$ctrl.verifyUser()"
                    ng-disabled="$ctrl.buttonDisabled('verification')"
-                   ng-if="$ctrl.buttonVisible('verification')">{{ 'Button_Verify' | translate }}</mf-button>
-        <mf-button ng-click="$ctrl.deleteUser()"
+                   ng-if="$ctrl.buttonVisible('verification')">{{ 'Button_Verify' | translate }}</ias-button>
+        <ias-button ng-click="$ctrl.deleteUser()"
                    ng-disabled="$ctrl.buttonDisabled('deleteUser')"
-                   ng-if="$ctrl.buttonVisible('deleteUser')">{{ 'Button_Delete' | translate }}</mf-button>
-        <mf-button ng-click="$ctrl.clickCustomButton(button)"
+                   ng-if="$ctrl.buttonVisible('deleteUser')">{{ 'Button_Delete' | translate }}</ias-button>
+        <ias-button ng-click="$ctrl.clickCustomButton(button)"
                    ng-repeat="button in $ctrl.person.customButtons"
-                   ng-attr-title="{{button.description}}">{{ button.label }}</mf-button>
+                   ng-attr-title="{{button.description}}">{{ button.label }}</ias-button>
     </div>
 </div>

+ 46 - 88
client/src/helpdesk/helpdesk-detail.component.scss

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  *
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -25,103 +25,61 @@ help-desk-detail {
     margin-right: 20px;
   }
 
-  mf-icon-button {
-    margin-left: 5px;
-  }
-
-  person-card {
-    margin-top: 20px;
+  .ias-tile {
+    margin: 20px 0 15px;
   }
 
   > .help-desk-content {
-    > .person-details-content,
-    > .help-desk-buttons {
-      display: inline-block;
-      vertical-align: top;
-    }
-
-    > .person-details-content {
-      min-width: 580px;
-      max-width: 700px;
-      padding: 10px;
-
-      .mf-tab-pane-container {
-        max-height: 500px;
-        overflow: auto;
-
-        table {
-          border: none;
-          border-collapse: collapse;
-          max-width: 560px;
-          max-height: 500px;
-          width: 100%;
-
-          tr {
-            height: 25px;
-
-            &.bottom-border {
-              border-bottom: 1px solid #949494;
-            }
-
-            td {
-              border: none;
-              font-size: 12px;
-              height: 19px;
-              text-align: left;
-
-              &:first-child {
-                color: #949494;
-                width: 200px;
-                text-align: right;
-                padding: 3px 0;
-              }
-
-              &:last-child {
-                padding: 3px 10px;
+    display: flex;
+    flex-flow: row wrap;
 
-                > .detail-container {
-                  > ul {
-                    > li {
-                      > mf-icon-button {
-                        display: inline-block;
-                        height: 16px;
-                        width: 16px;
-
-                        > button {
-                          > mf-icon {
-                            font-size: 16px;
-                          }
-                        }
-                      }
-                    }
-                  }
-                }
-              }
-
-              ul {
-                list-style: none;
-                margin: 0;
-                padding: 0;
+    > .help-desk-buttons {
+      margin-left: 15px;
 
-                > li {
-                  margin: 0;
-                  padding: 0;
-                }
-              }
-            }
-          }
-        }
+      > .ias-button {
+        display: block;
+        margin-bottom: 5px;
+        width: 100%;
       }
     }
+  }
+}
+
+.details-table {
+  border: none;
+  border-collapse: collapse;
+  //width: 100%;
+
+  tr {
+    height: 25px;
+
+    td {
+      border: none;
+      font-size: 12px;
+      height: 19px;
+      text-align: left;
+
+      &:first-child {
+        color: #949494;
+        //width: 100px;
+        text-align: right;
+        padding: 3px 0;
+      }
 
-    > .help-desk-buttons {
-      > mf-button {
-        display: block;
+      &:last-child {
+        padding: 3px 15px;
+      }
+
+      ul {
+        list-style: none;
+        margin: 0;
+        padding: 0;
 
-        > button {
-          margin: 0 auto;
+        > li {
+          margin: 0;
+          padding: 0;
         }
       }
     }
   }
-}
+}

+ 6 - 11
client/src/helpdesk/helpdesk-detail.component.ts

@@ -3,7 +3,7 @@
   htt://www.pwm-project.org
  *
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -24,9 +24,8 @@
 import {Component} from '../component';
 import {IButtonInfo, IHelpDeskService, ISuccessResponse} from '../services/helpdesk.service';
 import {IScope, ui} from 'angular';
-import {IQService, noop} from 'angular';
+import {noop} from 'angular';
 import {IHelpDeskConfigService, PASSWORD_UI_MODES} from '../services/helpdesk-config.service';
-import DialogService from '../ux/ias-dialog.service';
 import {IPeopleService} from '../services/people.service';
 import {IPerson} from '../models/person.model';
 import {IChangePasswordSuccess} from '../changepassword/success-change-password.controller';
@@ -55,7 +54,6 @@ export default class HelpDeskDetailComponent {
     photosEnabled: boolean;
 
     static $inject = [
-        '$q',
         '$state',
         '$stateParams',
         'ConfigService',
@@ -63,12 +61,11 @@ export default class HelpDeskDetailComponent {
         'IasDialogService',
         'PeopleService'
     ];
-    constructor(private $q: IQService,
-                private $state: ui.IStateService,
+    constructor(private $state: ui.IStateService,
                 private $stateParams: ui.IStateParamsService,
                 private configService: IHelpDeskConfigService,
                 private helpDeskService: IHelpDeskService,
-                private IasDialogService: DialogService,
+                private IasDialogService: any,
                 private peopleService: IPeopleService) {
     }
 
@@ -331,7 +328,7 @@ export default class HelpDeskDetailComponent {
                     'translateFilter',
                     function ($scope: IScope | any,
                               helpDeskService: IHelpDeskService,
-                              IasDialogService: DialogService,
+                              IasDialogService: any,
                               translateFilter: (id: string) => string) {
                         $scope.status = STATUS_CONFIRM;
                         $scope.title = translateFilter('Button_Confirm');
@@ -355,14 +352,12 @@ export default class HelpDeskDetailComponent {
             });
     }
 
-
-
     getUserKey(): string {
         return this.$stateParams['personId'];
     }
 
     gotoSearch(): void {
-        this.$state.go('search');
+        this.$state.go('search.cards');
     }
 
     initialize(): void {

+ 43 - 116
client/src/helpdesk/helpdesk-search.component.ts → client/src/helpdesk/helpdesk-search-base.component.ts

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  *
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -21,65 +21,66 @@
  */
 
 
-import {Component} from '../component';
 import {IPeopleService} from '../services/people.service';
 import SearchResult from '../models/search-result.model';
-import {IPromise, IQService} from 'angular';
+import {isArray, isString, IPromise, IQService, IScope} from 'angular';
 import {IPerson} from '../models/person.model';
-import DialogService from '../ux/ias-dialog.service';
 import {IHelpDeskConfigService} from '../services/helpdesk-config.service';
+import LocalStorageService from '../services/local-storage.service';
 
 let verificationsDialogTemplateUrl = require('./verifications-dialog.template.html');
 let recentVerificationsDialogTemplateUrl = require('./recent-verifications-dialog.template.html');
 
-@Component({
-    stylesheetUrl: require('helpdesk/helpdesk-search.component.scss'),
-    templateUrl: require('helpdesk/helpdesk-search.component.html')
-})
-export default class HelpDeskSearchComponent {
+export default abstract class HelpDeskSearchBaseComponent {
     columnConfiguration: any;
     protected pendingRequests: IPromise<any>[] = [];
     photosEnabled: boolean;
     query: string;
     searchResult: SearchResult;
+    searchTextLocalStorageKey: string;
+    searchViewLocalStorageKey: string;
     verificationsEnabled: boolean;
     view: string;
 
-    static $inject = ['$q',
-        'ConfigService',
-        'IasDialogService',
-        'PeopleService'
-    ];
-
-    constructor(private $q: IQService,
-                private configService: IHelpDeskConfigService,
-                private IasDialogService: DialogService,
-                private peopleService: IPeopleService) {
+    constructor(protected $q: IQService,
+                protected $scope: IScope,
+                protected $stateParams: angular.ui.IStateParamsService,
+                protected configService: IHelpDeskConfigService,
+                protected IasDialogService: any,
+                protected localStorageService: LocalStorageService,
+                protected peopleService: IPeopleService) {
+        this.searchTextLocalStorageKey = this.localStorageService.keys.HELPDESK_SEARCH_TEXT;
+        this.searchViewLocalStorageKey = this.localStorageService.keys.HELPDESK_SEARCH_VIEW;
+
+        $scope.$watch('$ctrl.query', (newValue: string, oldValue: string) => {
+            this.onSearchTextChange(newValue, oldValue);
+        });
     }
 
-    $onInit(): void {
-        this.view = 'cards';
-
-        this.configService.photosEnabled().then((photosEnabled: boolean) => {
-            this.photosEnabled = photosEnabled;
-        }); // TODO: only if in cards view (some other things are like that too)
+    protected initialize(): void {
+        this.query = this.getSearchText();
 
         this.configService.verificationsEnabled().then((verificationsEnabled: boolean) => {
             this.verificationsEnabled = verificationsEnabled;
         });
-
-        this.fetchData();
     }
 
-    private fetchData() {
-        let searchResultPromise = this.fetchSearchData();
-        if (searchResultPromise) {
-
-            searchResultPromise.then(this.onSearchResult.bind(this));
+    private getSearchText(): string {
+        let param: string = this.$stateParams['query'];
+        // If multiple query parameters are defined, use the first one
+        if (isArray(param)) {
+            param = param[0].trim();
         }
+        else if (isString(param)) {
+            param = param.trim();
+        }
+
+        return param || this.localStorageService.getItem(this.searchTextLocalStorageKey);
     }
 
-    private fetchSearchData(): IPromise<void | SearchResult> {
+    abstract fetchData(): void;
+
+    protected fetchSearchData(): IPromise<void | SearchResult> {
         // this.abortPendingRequests();
         this.searchResult = null;
 
@@ -88,10 +89,7 @@ export default class HelpDeskSearchComponent {
             return null;
         }
 
-        const self = this;
-
         let promise = this.peopleService.search(this.query);
-
         this.pendingRequests.push(promise);
 
         return promise
@@ -126,93 +124,18 @@ export default class HelpDeskSearchComponent {
             });
     }
 
-    gotoCardsView(): void {
-        if (this.view !== 'cards') {
-            this.view = 'cards';
-            this.fetchData();
-        }
-    }
-
-    gotoTableView(): void {
-        if (this.view !== 'table') {
-            this.view = 'table';
-            this.fetchData();
-        }
-
-        let self = this;
-
-        // The table columns are dynamic and configured via a service
-        this.configService.getColumnConfig().then(
-            (columnConfiguration: any) => {
-                self.columnConfiguration = columnConfiguration;
-            },
-            (error) => {
-                // self.setErrorMessage(error);
-            }); // TODO: remove self
-    }
-
-    private onSearchResult(searchResult: SearchResult): void {
-        if (this.view === 'table') {
-            this.searchResult = searchResult;
-            return;
-        }
-
-        // Aborted request
-        if (!searchResult) {
-            return;
-        }
-
-        this.searchResult = new SearchResult({
-            sizeExceeded: searchResult.sizeExceeded,
-            searchResults: []
-        });
-
-        let self = this;
-
-        this.pendingRequests = searchResult.people.map(
-            (person: IPerson) => {
-                // Store this promise because it is abortable
-                let promise = this.peopleService.getPerson(person.userKey);
-
-                promise
-                    .then((person: IPerson) => {
-                            // Aborted request
-                            if (!person) {
-                                return;
-                            }
-
-                            // searchResult may be overwritten by ESC->[LETTER] typed in after a search
-                            // has started but before all calls to peopleService.getPerson have resolved
-                            if (self.searchResult) {
-                                self.searchResult.people.push(person);
-                            }
-                        },
-                        (error) => {
-                            // self.setErrorMessage(error);
-                        })
-                    .finally(() => {
-                        // self.removePendingRequest(promise);
-                    });
-
-                return promise;
-            }
-        );  // TODO: this arg
-    }
-
-    onSearchTextChange(value: string): void {
-        if (value === this.query) {
+    private onSearchTextChange(newValue: string, oldValue: string): void {
+        if (newValue === oldValue) {
             return;
         }
 
-        this.query = value;
-
-        // this.storeSearchText();
+        this.storeSearchText();
         // this.clearSearchMessage();
         // this.clearErrorMessage();
         this.fetchData();
     }
 
-    selectPerson(person: IPerson): void {
+    protected selectPerson(person: IPerson): void {
         this.IasDialogService
             .open({
                 controller: 'VerificationsDialogController as $ctrl',
@@ -224,11 +147,15 @@ export default class HelpDeskSearchComponent {
             });
     }
 
-    showVerifications(): void {
+    protected showVerifications(): void {
         this.IasDialogService
             .open({
                 controller: 'RecentVerificationsDialogController as $ctrl',
                 templateUrl: recentVerificationsDialogTemplateUrl
             });
     }
+
+    protected storeSearchText(): void {
+        this.localStorageService.setItem(this.searchTextLocalStorageKey, this.query || '');
+    }
 }

+ 54 - 0
client/src/helpdesk/helpdesk-search-cards.component.html

@@ -0,0 +1,54 @@
+<!--
+  ~ Password Management Servlets (PWM)
+  ~ http://www.pwm-project.org
+  ~
+  ~ Copyright (c) 2006-2009 Novell, Inc.
+  ~ Copyright (c) 2009-2018 The PWM Project
+  ~
+  ~ This program is free software; you can redistribute it and/or modify
+  ~ it under the terms of the GNU General Public License as published by
+  ~ the Free Software Foundation; either version 2 of the License, or
+  ~ (at your option) any later version.
+  ~
+  ~ This program 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 General Public License for more details.
+  ~
+  ~ You should have received a copy of the GNU General Public License
+  ~ along with this program; if not, write to the Free Software
+  ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+  -->
+
+<div class="ias-header">
+    <h2 id="page-content-title" translate="Title_HelpDesk">Help Desk</h2>
+    <ias-search-box ng-model="$ctrl.query" ng-model-options="{debounce: $ctrl.inputDebounce}"
+                    placeholder="{{'Placeholder_Search' | translate}}" auto-focus>
+    </ias-search-box>
+
+    <span class="ias-fill"></span>
+
+    <ias-button id="view-tile-icon" class="ias-icon-button ias-selected"
+                ng-disabled="true"
+                ng-attr-title="{{ 'Title_HelpDeskCard' | translate }}">
+        <ias-icon icon="view_tile_thin"></ias-icon>
+    </ias-button>
+    <ias-button id="view-list-icon" class="ias-icon-button"
+                ng-click="$ctrl.gotoTableView()"
+                ng-attr-title="{{ 'Title_HelpDeskTable' | translate }}">
+        <ias-icon icon="view_list_thin"></ias-icon>
+    </ias-button>
+</div>
+
+<div class="people-search-component-content">
+    <ias-button class="verifications-button ias-cta" ng-if="$ctrl.verificationsEnabled"
+                ng-click="$ctrl.showVerifications()">{{ 'Button_Verifications' | translate }}</ias-button>
+
+    <div class="ias-grid">
+        <person-card person="person"
+                     show-image="$ctrl.photosEnabled"
+                     ng-repeat="person in $ctrl.searchResult.people | orderBy:'displayNames[0]'"
+                     ng-click="$ctrl.selectPerson(person)">
+        </person-card>
+    </div>
+</div>

+ 120 - 0
client/src/helpdesk/helpdesk-search-cards.component.ts

@@ -0,0 +1,120 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2018 The PWM Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+import {Component} from '../component';
+import {IPeopleService} from '../services/people.service';
+import {IQService, IScope} from 'angular';
+import {IHelpDeskConfigService} from '../services/helpdesk-config.service';
+import LocalStorageService from '../services/local-storage.service';
+import HelpDeskSearchBaseComponent from './helpdesk-search-base.component';
+import SearchResult from '../models/search-result.model';
+import {IPerson} from '../models/person.model';
+
+@Component({
+    stylesheetUrl: require('helpdesk/helpdesk-search.component.scss'),
+    templateUrl: require('helpdesk/helpdesk-search-cards.component.html')
+})
+export default class HelpDeskSearchCardsComponent extends HelpDeskSearchBaseComponent {
+    static $inject = [
+        '$q',
+        '$scope',
+        '$state',
+        '$stateParams',
+        'ConfigService',
+        'IasDialogService',
+        'LocalStorageService',
+        'PeopleService'
+    ];
+    constructor($q: IQService,
+                $scope: IScope,
+                private $state: angular.ui.IStateService,
+                $stateParams: angular.ui.IStateParamsService,
+                configService: IHelpDeskConfigService,
+                IasDialogService: any,
+                localStorageService: LocalStorageService,
+                peopleService: IPeopleService) {
+        super($q, $scope, $stateParams, configService, IasDialogService, localStorageService, peopleService);
+    }
+
+    $onInit() {
+        this.initialize();
+        this.fetchData();
+
+        this.configService.photosEnabled().then((photosEnabled: boolean) => {
+            this.photosEnabled = photosEnabled;
+        });
+    }
+
+    fetchData() {
+        let searchResult = this.fetchSearchData();
+        if (searchResult) {
+            searchResult.then(this.onSearchResult.bind(this));
+        }
+    }
+
+    gotoTableView(): void {
+        this.$state.go('search.table', {query: this.query});
+    }
+
+    private onSearchResult(searchResult: SearchResult): void {
+        // Aborted request
+        if (!searchResult) {
+            return;
+        }
+
+        this.searchResult = new SearchResult({
+            sizeExceeded: searchResult.sizeExceeded,
+            searchResults: []
+        });
+
+        this.pendingRequests = searchResult.people.map(
+            (person: IPerson) => {
+                // Store this promise because it is abortable
+                let promise = this.peopleService.getPerson(person.userKey);
+
+                promise
+                    .then(function(person: IPerson) {
+                            // Aborted request
+                            if (!person) {
+                                return;
+                            }
+
+                            // searchResult may be overwritten by ESC->[LETTER] typed in after a search
+                            // has started but before all calls to peopleService.getPerson have resolved
+                            if (this.searchResult) {
+                                this.searchResult.people.push(person);
+                            }
+                        }.bind(this),
+                        (error) => {
+                            // self.setErrorMessage(error);
+                        })
+                    .finally(() => {
+                        // self.removePendingRequest(promise);
+                    });
+
+                return promise;
+            },
+            this
+        );
+    }
+}

+ 76 - 0
client/src/helpdesk/helpdesk-search-table.component.html

@@ -0,0 +1,76 @@
+<!--
+  ~ Password Management Servlets (PWM)
+  ~ http://www.pwm-project.org
+  ~
+  ~ Copyright (c) 2006-2009 Novell, Inc.
+  ~ Copyright (c) 2009-2018 The PWM Project
+  ~
+  ~ This program is free software; you can redistribute it and/or modify
+  ~ it under the terms of the GNU General Public License as published by
+  ~ the Free Software Foundation; either version 2 of the License, or
+  ~ (at your option) any later version.
+  ~
+  ~ This program 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 General Public License for more details.
+  ~
+  ~ You should have received a copy of the GNU General Public License
+  ~ along with this program; if not, write to the Free Software
+  ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+  -->
+
+<div class="ias-header">
+    <h2 id="page-content-title" translate="Title_HelpDesk">Help Desk</h2>
+    <ias-search-box ng-model="$ctrl.query" ng-model-options="{debounce: $ctrl.inputDebounce}"
+                    placeholder="{{'Placeholder_Search' | translate}}" auto-focus>
+    </ias-search-box>
+
+    <span class="ias-fill"></span>
+
+    <ias-button id="view-tile-icon" class="ias-icon-button"
+                ng-click="$ctrl.gotoCardsView()"
+                ng-attr-title="{{ 'Title_HelpDeskCard' | translate }}">
+        <ias-icon icon="view_tile_thin"></ias-icon>
+    </ias-button>
+    <ias-button id="view-list-icon" class="ias-icon-button ias-selected"
+                ng-disabled="true"
+                ng-attr-title="{{ 'Title_HelpDeskTable' | translate }}">
+        <ias-icon icon="view_list_thin"></ias-icon>
+    </ias-button>
+    <div class="icon-divider vertical"></div>
+    <ias-button class="ias-icon-button table-configuration-menu-toggle" ias-toggle="menu1">
+        <ias-icon icon="configure_thick"></ias-icon>
+    </ias-button>
+    <ias-menu name="menu1" ias-align="end end">
+        <div class="ias-input-container">
+            <div class="checkbox-button" ng-repeat="(key, value) in $ctrl.columnConfiguration">
+                <input type="checkbox" ng-checked="value.visible" aria-label="Toggle column visibility" disabled/>
+                <ias-button ng-click="$ctrl.toggleColumnVisible($event, key)">{{value.label}}</ias-button>
+            </div>
+        </div>
+    </ias-menu>
+</div>
+
+<div class="people-search-component-content">
+    <ias-button class="verifications-button ias-cta" ng-if="$ctrl.verificationsEnabled"
+                ng-click="$ctrl.showVerifications()">{{ 'Button_Verifications' | translate }}</ias-button>
+
+    <table class="ias-table" ias-sort="$ctrl.sort" ng-show="$ctrl.searchResult.people.length">
+        <thead>
+        <tr>
+            <th ng-repeat="(key, value) in $ctrl.columnConfiguration" ng-if="value.visible"
+                ias-sort-on="key">{{value.label}}</th>
+        </tr>
+        </thead>
+        <tbody>
+        <tr ng-repeat="person in $ctrl.searchResult.people | orderBy:$ctrl.sort"
+            ng-click="$ctrl.selectPerson(person)">
+            <td ng-repeat="(key, value) in $ctrl.columnConfiguration" ng-if="value.visible">
+                <span ng-bind="person[key]"></span>
+            </td>
+            <td></td>
+        </tr>
+        </tbody>
+    </table>
+</div>

+ 109 - 0
client/src/helpdesk/helpdesk-search-table.component.ts

@@ -0,0 +1,109 @@
+/*
+ * Password Management Servlets (PWM)
+ * http://www.pwm-project.org
+ *
+ * Copyright (c) 2006-2009 Novell, Inc.
+ * Copyright (c) 2009-2018 The PWM Project
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program 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 General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+ */
+
+
+import {IQService, IScope} from 'angular';
+import HelpDeskSearchBaseComponent from './helpdesk-search-base.component';
+import {Component} from '../component';
+import SearchResult from '../models/search-result.model';
+import {IPeopleService} from '../services/people.service';
+import {IHelpDeskConfigService} from '../services/helpdesk-config.service';
+import LocalStorageService from '../services/local-storage.service';
+
+@Component({
+    stylesheetUrl: require('helpdesk/helpdesk-search.component.scss'),
+    templateUrl: require('helpdesk/helpdesk-search-table.component.html')
+})
+export default class HelpDeskSearchTableComponent extends HelpDeskSearchBaseComponent {
+    columnConfiguration: any;
+
+    static $inject = [
+        '$q',
+        '$scope',
+        '$state',
+        '$stateParams',
+        'ConfigService',
+        'IasDialogService',
+        'LocalStorageService',
+        'PeopleService'
+    ];
+    constructor($q: IQService,
+                $scope: IScope,
+                private $state: angular.ui.IStateService,
+                $stateParams: angular.ui.IStateParamsService,
+                configService: IHelpDeskConfigService,
+                IasDialogService: any,
+                localStorageService: LocalStorageService,
+                peopleService: IPeopleService) {
+        super($q, $scope, $stateParams, configService, IasDialogService, localStorageService, peopleService);
+    }
+
+    $onInit() {
+        this.initialize();
+        this.fetchData();
+
+        // The table columns are dynamic and configured via a service
+        this.configService.getColumnConfig().then(
+            (columnConfiguration: any) => {
+                this.columnConfiguration = Object.keys(columnConfiguration).reduce(
+                    function(accumulator, columnId) {
+                        accumulator[columnId] = {
+                            label: columnConfiguration[columnId],
+                            visible: true
+                        };
+
+                        return accumulator;
+                    },
+                    {});
+            },
+            (error) => {
+                // self.setErrorMessage(error);
+            });
+    }
+
+    fetchData() {
+        let searchResult = this.fetchSearchData();
+        if (searchResult) {
+            searchResult.then(this.onSearchResult.bind(this));
+        }
+    }
+
+    gotoCardsView(): void {
+        this.$state.go('search.cards', {query: this.query});
+    }
+
+    toggleColumnVisible(event, columnId): void {
+        const visibleColumns = Object.keys(this.columnConfiguration).filter((columnId) => {
+            return this.columnConfiguration[columnId].visible;
+        });
+
+        if (!(visibleColumns.length === 1 && this.columnConfiguration[columnId].visible)) {
+            this.columnConfiguration[columnId].visible = !this.columnConfiguration[columnId].visible;
+        }
+
+        event.stopImmediatePropagation();
+    }
+
+    private onSearchResult(searchResult: SearchResult): void {
+        this.searchResult = searchResult;
+    }
+}

+ 0 - 65
client/src/helpdesk/helpdesk-search.component.html

@@ -1,65 +0,0 @@
-<!--
-  ~ Password Management Servlets (PWM)
-  ~ http://www.pwm-project.org
-  ~
-  ~ Copyright (c) 2006-2009 Novell, Inc.
-  ~ Copyright (c) 2009-2017 The PWM Project
-  ~
-  ~ This program is free software; you can redistribute it and/or modify
-  ~ it under the terms of the GNU General Public License as published by
-  ~ the Free Software Foundation; either version 2 of the License, or
-  ~ (at your option) any later version.
-  ~
-  ~ This program 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 General Public License for more details.
-  ~
-  ~ You should have received a copy of the GNU General Public License
-  ~ along with this program; if not, write to the Free Software
-  ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-  -->
-
-<mf-app-bar>
-    <div id="page-content-title" translate="Title_HelpDesk">Help Desk</div>
-    <mf-search-bar search-text="$ctrl.query"
-                   on-search-text-change="$ctrl.onSearchTextChange(value)"
-                   auto-focus></mf-search-bar>
-    <span flex></span>
-    <mf-icon-button
-        icon="view-tile_thin"
-        ng-attr-title="{{ 'Title_HelpDeskCard' | translate }}"
-        ng-class="{selected: $ctrl.view === 'cards'}"
-        ng-click="$ctrl.gotoCardsView()"
-        ng-disabled="$ctrl.view === 'cards'"
-        id="view-tile-icon"></mf-icon-button>
-    <mf-icon-button
-        icon="view-list_thin"
-        ng-attr-title="{{ 'Title_HelpDeskTable' | translate }}"
-        ng-class="{selected: $ctrl.view === 'table'}"
-        ng-click="$ctrl.gotoTableView()"
-        ng-disabled="$ctrl.view === 'table'"
-        id="view-list-icon"></mf-icon-button>
-</mf-app-bar>
-<mf-button ng-if="$ctrl.verificationsEnabled"
-           ng-click="$ctrl.showVerifications()">{{ 'Button_Verifications' | translate }}</mf-button>
-
-<div class="people-search-component-content">
-    <div class="person-card-list" ng-show="$ctrl.view === 'cards'">
-        <person-card person="person"
-                     show-image="$ctrl.photosEnabled"
-                     ng-repeat="person in $ctrl.searchResult.people | orderBy:'displayNames[0]'"
-                     ng-click="$ctrl.selectPerson(person)">
-        </person-card>
-    </div>
-
-    <mf-table data="person in $ctrl.searchResult.people"
-              ng-show="$ctrl.view === 'table' && $ctrl.searchResult.people.length"
-              search-highlight="$ctrl.query"
-              on-click-item="$ctrl.selectPerson(person)">
-        <mf-table-column ng-repeat="(key, value) in $ctrl.columnConfiguration"
-                         label="value"
-                         value="'person.' + key">
-        </mf-table-column>
-    </mf-table>
-</div>

+ 19 - 161
client/src/helpdesk/helpdesk-search.component.scss

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  *
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -20,180 +20,38 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
-help-desk-search {
+
+.verifications-button {
+  margin-bottom: 15px;
+}
+
+help-desk-search-cards,
+help-desk-search-table {
   display: flex;
   flex-flow: column nowrap;
   height: 100%;
 
-  // At medium size, cards are centered and no longer take up 100% width
-  &.medium {
-    > .people-search-component-content {
-      > .person-card-list {
-        > person-card {
-          margin: 0 auto;
-          display: block;
-          width: 272px;
-        }
-      }
-    }
-  }
-
-  // At large size, cards fit next to each other
-  &.large {
-    > .people-search-component-content {
-      > .person-card-list {
-        text-align: left;
-        margin: 0;
-
-        > person-card {
-          display: inline-block;
-          margin-right: 5px;
-        }
-      }
-    }
-  }
-
   > .people-search-component-content {
     flex: 1 1;
     overflow: auto;
-    text-align: center;
-
-    > .person-card-list {
-      > person-card {
-        display: inline-block;
-        width: 100%;
-
-        &:not(:last-child) {
-          margin-bottom: 5px;
-        }
-      }
-    }
   }
 }
 
-ias-dialog {
-  p,
-  .paragraph {
-    max-width: 400px;
-  }
-
-  .aligned-inputs {
-    margin-top: 8px;
-
-    input,
-    label,
-    .first-col,
-    .second-col {
-      display: inline-block;
-    }
-
-    input,
-    .second-col {
-      min-width: 200px;
-    }
+.aligned-input {
+  margin-top: 15px;
 
-    input {
-      background-color: transparent;
-      border: 1px solid #dae1e1;
-      border-radius: 3px;
-      font-size: 15px;
-      padding: 8px;
-    }
-
-    label,
-    .first-col {
-      color: #808080;
-      font-size: 13px;
-      font-weight: normal;
-      line-height: 21px;
-      margin-right: 5px;
-      min-width: 150px;
-      text-align: right;
-      user-select: none;
-    }
-
-    mf-button {
-      margin: 0;
-
-      > button {
-        min-width: 90px;
-      }
-    }
-  }
-
-  .ias-actions {
-    mf-icon {
-      .icon_m_check_thick,
-      .icon_m_close_thick {
-        margin-left: 5px;
-      }
-
-      .icon_m_check_thick {
-        color: green;
-      }
-
-      .icon_m_close_thick {
-        color: red;
-      }
-    }
-
-    .loading-gif-25 {
-      background-image: url('../../images/icons/wait_25.gif');
-      display: inline-block;
-      height: 25px;
-      margin-left: 5px;
-      vertical-align: top;
-      width: 25px;
-    }
+  > * {
+    vertical-align: middle;
   }
 
-  table {
-    border: 1px solid #dae1e1;
-    border-collapse: collapse;
-    width: 100%;
-
-    > tbody {
-      > tr {
-        height: 25px;
-      }
-    }
-
-    th, td {
-      font-weight: normal;
-      overflow: hidden;
-      padding: 5px;
-      text-align: left;
-      vertical-align: top;
-    }
-
-    th {
-      background-color: #eeeeee;
-      color: #697c87;
-      user-select: none;
-    }
-
-    tr {
-      > th,
-      > td {
-        border-bottom: 1px solid #dae1e1;
-      }
-    }
+  .ias-button {
+    margin-right: 5px;
   }
 }
 
-[dir="rtl"] {
-  people-search-cards {
-    &.large {
-      > .people-search-component-content {
-        .person-card-list {
-          text-align: right;
-
-          > person-card {
-            margin-right: auto;
-            margin-left: 5px;
-          }
-        }
-      }
-    }
-  }
+.loading-gif-25 {
+  background-image: url('../../images/icons/wait_25.gif');
+  display: inline-block;
+  height: 25px;
+  width: 25px;
 }

+ 8 - 5
client/src/helpdesk/helpdesk.module.ts

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  *
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -24,10 +24,11 @@
 import {IComponentOptions, module} from 'angular';
 import { DateFilter } from './date.filters';
 import HelpDeskDetailComponent from './helpdesk-detail.component';
-import HelpDeskSearchComponent from './helpdesk-search.component';
+import HelpDeskSearchTableComponent from './helpdesk-search-table.component';
+import HelpDeskSearchCardsComponent from './helpdesk-search-cards.component';
 import LocalStorageService from '../services/local-storage.service';
 import ObjectService from '../services/object.service';
-import PersonCardComponent from '../peoplesearch/person-card.component';
+import PersonCardDirective from '../peoplesearch/person-card.component';
 import PromiseService from '../services/promise.service';
 import RecentVerificationsDialogController from './recent-verifications-dialog.controller';
 import uxModule from '../ux/ux.module';
@@ -42,12 +43,14 @@ require('../peoplesearch/peoplesearch.scss');
 const moduleName = 'help-desk';
 
 module(moduleName, [
+    'ngAria',
     uxModule
 ])
 
-    .component('helpDeskSearch', HelpDeskSearchComponent as IComponentOptions)
+    .component('helpDeskSearchCards', HelpDeskSearchCardsComponent as IComponentOptions)
+    .component('helpDeskSearchTable', HelpDeskSearchTableComponent as IComponentOptions)
     .component('helpDeskDetail', HelpDeskDetailComponent as IComponentOptions)
-    .component('personCard', PersonCardComponent as IComponentOptions)
+    .directive('personCard', PersonCardDirective)
     .controller('AutogenChangePasswordController', AutogenChangePasswordController)
     .controller('RandomChangePasswordController', RandomChangePasswordController)
     .controller('RecentVerificationsDialogController', RecentVerificationsDialogController)

+ 0 - 0
client/src/helpdesk/helpdesk.scss


+ 3 - 4
client/src/helpdesk/main.dev.ts

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  *
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -28,13 +28,12 @@ import PeopleService from '../services/people.service.dev';
 import HelpDeskConfigService from '../services/helpdesk-config.service.dev';
 import HelpDeskService from '../services/helpdesk.service.dev';
 
-// fontgen-loader needs this :(
-require('../icons.json');
 
 module('app', [
     uiRouter,
     helpDeskModule,
-    'pascalprecht.translate'
+    'pascalprecht.translate',
+    'ng-ias'
 ])
     .config(['$translateProvider', ($translateProvider: angular.translate.ITranslateProvider) => {
         $translateProvider.translations('en', require('i18n/translations_en.json'));

+ 1 - 3
client/src/helpdesk/main.ts

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  *
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -30,8 +30,6 @@ import uiRouter from '@uirouter/angularjs';
 import HelpDeskConfigService from '../services/helpdesk-config.service';
 import HelpDeskService from '../services/helpdesk.service';
 
-// fontgen-loader needs this :(
-require('../icons.json');
 
 module('app', [
     uiRouter,

+ 1 - 1
client/src/helpdesk/recent-verifications-dialog.controller.ts

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  *
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by

+ 41 - 39
client/src/helpdesk/recent-verifications-dialog.template.html

@@ -3,7 +3,7 @@
   ~ http://www.pwm-project.org
   ~
   ~ Copyright (c) 2006-2009 Novell, Inc.
-  ~ Copyright (c) 2009-2017 The PWM Project
+  ~ Copyright (c) 2009-2018 The PWM Project
   ~
   ~ This program is free software; you can redistribute it and/or modify
   ~ it under the terms of the GNU General Public License as published by
@@ -20,47 +20,49 @@
   ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   -->
 
-<ias-dialog>
-    <div class="ias-dialog-header">
-        <div class="ias-title" ng-bind="'Title_RecentVerifications' | translate"></div>
-    </div>
+<div class="ias-dialog">
+    <div class="ias-dialog-container">
+        <div class="ias-dialog-label">
+            <div class="ias-title" ng-bind="'Title_RecentVerifications' | translate"></div>
+        </div>
 
-    <div class="ias-dialog-body">
-        <p ng-bind="'Display_SearchResultsNone' | translate"
-           ng-if="!$ctrl.recentVerifications.length"></p>
-        <table ng-if="!!$ctrl.recentVerifications.length">
-            <thead>
-            <tr>
-                <th ng-bind="'Field_LdapProfile' | translate"></th>
-                <th ng-bind="'Field_Username' | translate"></th>
-                <th ng-bind="'Field_DateTime' | translate"></th>
-                <th ng-bind="'Field_Method' | translate"></th>
-            </tr>
-            </thead>
-            <tbody>
-            <tr ng-repeat="record in $ctrl.recentVerifications">
-                <td ng-bind="record.profile"></td>
-                <td ng-bind="record.username"></td>
-                <td ng-bind="record.timestamp | dateFilter"></td>
-                <td ng-bind="record.method"></td>
-            </tr>
-            </tbody>
-        </table>
-        <div ng-repeat="method in $ctrl.availableVerificationMethods" class="aligned-inputs">
-            <div class="first-col"></div>
-            <mf-button ng-click="$ctrl.selectVerificationMethod(method.name)">
-                {{ method.label | translate }}
-            </mf-button>
+        <div class="ias-dialog-content">
+            <p ng-bind="'Display_SearchResultsNone' | translate"
+               ng-if="!$ctrl.recentVerifications.length"></p>
+            <table class="ias-table" ng-if="!!$ctrl.recentVerifications.length">
+                <thead>
+                <tr>
+                    <th ng-bind="'Field_LdapProfile' | translate"></th>
+                    <th ng-bind="'Field_Username' | translate"></th>
+                    <th ng-bind="'Field_DateTime' | translate"></th>
+                    <th ng-bind="'Field_Method' | translate"></th>
+                </tr>
+                </thead>
+                <tbody>
+                <tr ng-repeat="record in $ctrl.recentVerifications">
+                    <td ng-bind="record.profile"></td>
+                    <td ng-bind="record.username"></td>
+                    <td ng-bind="record.timestamp | dateFilter"></td>
+                    <td ng-bind="record.method"></td>
+                </tr>
+                </tbody>
+            </table>
+            <div ng-repeat="method in $ctrl.availableVerificationMethods">
+                <ias-button ng-click="$ctrl.selectVerificationMethod(method.name)">
+                    {{ method.label | translate }}
+                </ias-button>
+            </div>
         </div>
-    </div>
 
-    <div class="ias-actions">
-        <mf-button ng-click="close()">{{ 'Button_OK' | translate }}</mf-button>
-    </div>
-    <mf-icon-button class="ias-dialog-close-button"
-                    icon="close_thick"
+        <div class="ias-actions">
+            <ias-button ng-click="close()">{{ 'Button_OK' | translate }}</ias-button>
+        </div>
+
+        <ias-button class="ias-icon-button ias-dialog-cancel-button"
                     id="close-icon"
                     ng-attr-title="{{ 'Button_CloseWindow' | translate }}"
                     ng-click="close()">
-    </mf-icon-button>
-</ias-dialog>
+            <ias-icon icon="close_thick"></ias-icon>
+        </ias-button>
+    </div>
+</div>

+ 8 - 2
client/src/helpdesk/routes.ts

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  *
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -33,6 +33,12 @@ export default [
                 $location.url('search');
             });
 
-        $stateProvider.state('search', { url: '/search?query', component: 'helpDeskSearch' });
+        $stateProvider.state('search', {
+            url: '/search?query',
+            abstract: true,
+            template: '<div><ui-view/></div>',
+        });
+        $stateProvider.state('search.cards', { url: '/cards', component: 'helpDeskSearchCards' });
+        $stateProvider.state('search.table', { url: '/table', component: 'helpDeskSearchTable' });
         $stateProvider.state('details', { url: '/details/{personId}', component: 'helpDeskDetail' });
     }];

+ 2 - 3
client/src/helpdesk/verifications-dialog.controller.ts

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  *
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -27,7 +27,6 @@ import {
     VERIFICATION_METHOD_NAMES
 } from '../services/helpdesk-config.service';
 import {IHelpDeskService, IVerificationTokenResponse} from '../services/helpdesk.service';
-import DialogService from '../ux/ias-dialog.service';
 import {IPerson} from '../models/person.model';
 import ObjectService from '../services/object.service';
 
@@ -62,7 +61,7 @@ export default class VerificationsDialogController {
                 private $timeout: ITimeoutService,
                 private configService: IHelpDeskConfigService,
                 private helpDeskService: IHelpDeskService,
-                private IasDialogService: DialogService,
+                private IasDialogService: any,
                 private objectService: ObjectService,
                 private personUserKey: string,
                 private search: boolean) {

+ 70 - 64
client/src/helpdesk/verifications-dialog.template.html

@@ -3,7 +3,7 @@
   ~ http://www.pwm-project.org
   ~
   ~ Copyright (c) 2006-2009 Novell, Inc.
-  ~ Copyright (c) 2009-2017 The PWM Project
+  ~ Copyright (c) 2009-2018 The PWM Project
   ~
   ~ This program is free software; you can redistribute it and/or modify
   ~ it under the terms of the GNU General Public License as published by
@@ -20,92 +20,98 @@
   ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   -->
 
-<ias-dialog>
-    <div ng-switch="$ctrl.status">
-        <div class="ias-actions" ng-switch-default>
-            <div class="WaitDialogBlank"></div>
-        </div>
-
-        <div ng-switch-when="select">
-            <div class="ias-dialog-header">
+<div class="ias-dialog" ng-switch="$ctrl.status">
+        <div class="ias-dialog-container" ng-switch-when="select">
+            <div class="ias-dialog-label">
                 <div class="ias-title" ng-bind="'Title_VerificationSend' | translate"></div>
             </div>
-            <div class="ias-dialog-body">
+            <div class="ias-dialog-content">
                 <p ng-bind="'Long_Title_VerificationSend' | translate"></p>
-                <div ng-repeat="method in $ctrl.availableVerificationMethods" class="aligned-inputs">
-                    <div class="first-col"></div>
-                    <mf-button ng-click="$ctrl.selectVerificationMethod(method.name)">
-                        {{ method.label | translate }}
-                    </mf-button>
-                </div>
+
+                <ias-button ng-click="$ctrl.selectVerificationMethod(method.name)"
+                            ng-repeat="method in $ctrl.availableVerificationMethods"
+                            style="margin-right: 3px;">
+                    {{ method.label | translate }}
+                </ias-button>
+
             </div>
             <div class="ias-actions">
-                <mf-button ng-click="close()">{{ 'Button_Cancel' | translate }}</mf-button>
+                <ias-button ng-click="close()">{{ 'Button_Cancel' | translate }}</ias-button>
             </div>
+
+            <ias-button class="ias-icon-button ias-dialog-cancel-button"
+                        id="close-icon"
+                        ng-attr-title="{{ 'Button_CloseWindow' | translate }}"
+                        ng-click="close()">
+                <ias-icon icon="close_thick"></ias-icon>
+            </ias-button>
         </div>
 
-        <div ng-switch-when="verify">
-            <div class="ias-dialog-header">
+        <div class="ias-dialog-container" ng-switch-when="verify">
+            <div class="ias-dialog-label">
                 <div class="ias-title" ng-bind="'Title_ValidateCode' | translate"></div>
             </div>
 
-            <div ng-switch="$ctrl.verificationMethod">
-                <div class="ias-dialog-body" ng-switch-when="ATTRIBUTES">
-                    <div ng-repeat="input in $ctrl.inputs" class="aligned-inputs">
-                        <label ng-attr-for="{{input.name}}" ng-bind="input.label"></label>
-                        <input ng-attr-id="{{input.name}}" type="text" ng-model="$ctrl.formData[input.name]">
+            <div class="ias-dialog-content" ng-switch="$ctrl.verificationMethod">
+                <form>
+                    <div ng-switch-when="ATTRIBUTES">
+                        <div class="ias-input-container" ng-repeat="input in $ctrl.inputs">
+                            <label ng-attr-for="{{input.name}}" ng-bind="input.label"></label>
+                            <input ng-attr-id="{{input.name}}" type="text" ng-model="$ctrl.formData[input.name]">
+                        </div>
                     </div>
-                </div>
-                <div class="ias-dialog-body" ng-switch-when="EMAIL|SMS" ng-switch-when-separator="|">
-                    <div class="aligned-inputs">
-                        <div class="first-col" ng-bind="'Display_TokenDestination' | translate"></div>
-                        <div class="second-col" ng-bind="$ctrl.tokenData.destination"></div>
+                    <div ng-switch-when="EMAIL|SMS" ng-switch-when-separator="|">
+                        <div class="ias-input-container">
+                            <label ng-bind="'Display_TokenDestination' | translate"></label>
+                            <input type="text" ng-value="$ctrl.tokenData.destination" readonly>
+                        </div>
+                        <div class="ias-input-container">
+                            <label>Token</label>
+                            <input id="token" type="text" ng-model="$ctrl.formData.code">
+                        </div>
+
                     </div>
-                    <div class="aligned-inputs">
-                        <div class="first-col">Token</div>
-                        <input class="second-col" id="code" type="text" ng-model="$ctrl.formData.code">
+                    <div ng-switch-when="OTP">
+                        <p ng-bind="'Display_HelpdeskOtpValidation' | translate"></p>
+                        <div class="ias-input-container">
+                            <label>Code</label>
+                            <input id="code" type="text" ng-model="$ctrl.formData.code">
+                        </div>
                     </div>
-                </div>
-                <div class="ias-dialog-body" ng-switch-when="OTP">
-                    <p ng-bind="'Display_HelpdeskOtpValidation' | translate"></p>
-                    <div class="aligned-inputs">
-                        <div class="first-col">Code</div>
-                        <input id="code" type="text" ng-model="$ctrl.formData.code">
+
+                    <div class="aligned-input">
+                        <ias-button ng-click="$ctrl.sendVerificationData()" ng-disabled="$ctrl.verificationStatus==='passed'">
+                            {{ 'Button_Verify' | translate }}
+                        </ias-button>
+                        <div class="loading-gif-25" ng-if="$ctrl.verificationStatus==='wait'"></div>
+                        <ias-icon icon="status_ok_thick" style="color:#37c26a;" class="ias-success" ng-if="$ctrl.verificationStatus==='passed'"></ias-icon>
+                        <ias-icon icon="status_error_thick" style="color:#e50000;" class="ias-error" ng-if="$ctrl.verificationStatus==='failed'"></ias-icon>
                     </div>
-                </div>
+                    <p ng-if="$ctrl.verificationStatus==='failed'">
+                        Viewing details only available after a user has been successfully verified
+                    </p>
+                </form>
             </div>
 
             <div class="ias-actions">
-                    <div class="aligned-inputs">
-                        <div class="first-col"></div>
-                        <mf-button ng-click="$ctrl.sendVerificationData()">{{ 'Button_Verify' | translate }}</mf-button>
-                        <div class="loading-gif-25" ng-if="$ctrl.verificationStatus==='wait'"></div>
-                        <mf-icon icon="check_thick" ng-if="$ctrl.verificationStatus==='passed'"></mf-icon>
-                        <mf-icon icon="close_thick" ng-if="$ctrl.verificationStatus==='failed'"></mf-icon>
-                    </div>
-                <br>
-                <div class="paragraph" ng-if="$ctrl.verificationStatus==='failed'">
-                    Viewing details only available after a user has been successfully verified
-                </div>
-                <mf-button ng-disabled="$ctrl.verificationStatus!=='passed'"
+                <ias-button ng-disabled="$ctrl.verificationStatus!=='passed'"
                            ng-click="$ctrl.viewDetails()"
                            ng-if="!$ctrl.isDetailsView">
                     View Details
-                </mf-button>
-                <mf-button ng-disabled="$ctrl.verificationStatus!=='passed'"
+                </ias-button>
+                <ias-button ng-disabled="$ctrl.verificationStatus!=='passed'"
                            ng-click="$ctrl.clickOkButton()"
                            ng-if="$ctrl.isDetailsView">
                     {{ 'Button_OK' | translate }}
-                </mf-button>
-                <mf-button ng-click="close()">{{ 'Button_Cancel' | translate }}</mf-button>
+                </ias-button>
+                <ias-button ng-click="close()">{{ 'Button_Cancel' | translate }}</ias-button>
             </div>
-        </div>
-    </div>
 
-    <mf-icon-button class="ias-dialog-close-button"
-                    icon="close_thick"
-                    id="close-icon"
-                    ng-attr-title="{{ 'Button_CloseWindow' | translate }}"
-                    ng-click="close()">
-    </mf-icon-button>
-</ias-dialog>
+            <ias-button class="ias-icon-button ias-dialog-cancel-button"
+                        id="close-icon"
+                        ng-attr-title="{{ 'Button_CloseWindow' | translate }}"
+                        ng-click="close()">
+                <ias-icon icon="close_thick"></ias-icon>
+            </ias-button>
+        </div>
+</div>

+ 0 - 15
client/src/icons.json

@@ -1,15 +0,0 @@
-{
-  "baseClass": "icon",
-  "classPrefix": "icon_",
-  "files": [ "../images/icons/**/*.svg" ],
-  "fixedWidth": true,
-  "fontName": "icons",
-  "formatOptions": {
-    "ttf": {
-      "ts": 1451512800000
-    }
-  },
-  "html": true,
-  "normalize": true,
-  "types": [ "eot", "woff", "ttf", "svg" ]
-}

+ 2 - 3
client/src/main.dev.ts

@@ -30,13 +30,12 @@ import routes from './routes';
 import routeErrorHandler from './route-error-handler';
 import uiRouter from '@uirouter/angularjs';
 
-// fontgen-loader needs this :(
-require('./icons.json');
 
 module('app', [
     uiRouter,
     peopleSearchModule,
-    'pascalprecht.translate'
+    'pascalprecht.translate',
+    'ng-ias'
 ])
 
     .config(routes)

+ 0 - 3
client/src/main.ts

@@ -31,9 +31,6 @@ import routeErrorHandler from './route-error-handler';
 import TranslationsLoaderFactory from './services/translations-loader.factory';
 import uiRouter from '@uirouter/angularjs';
 
-// fontgen-loader needs this :(
-require('./icons.json');
-
 module('app', [
     uiRouter,
     peopleSearchModule,

+ 35 - 31
client/src/peoplesearch/orgchart-search.component.html

@@ -20,37 +20,41 @@
   ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   -->
 
-<mf-app-bar>
-    <div id="page-content-title" translate="Title_Organization">Organization</div>
-    <mf-auto-complete search-text="$ctrl.query"
-                      on-search-text-change="$ctrl.onSearchTextChange(value)"
-                      search="$ctrl.autoCompleteSearch(query)"
-                      input-debounce="$ctrl.inputDebounce"
-                      item-selected="$ctrl.onAutoCompleteItemSelected(person)"
-                      item="person">
-        <content-template>
-            <span ng-bind="person._displayName"></span>
-        </content-template>
-    </mf-auto-complete>
-    <span flex></span>
-    <mf-icon-button
-            icon="view-tile_thin"
-            ng-click="$ctrl.gotoSearchState('search.cards')"
-            ng-attr-title="{{ 'Title_PeopleSearchCard' | translate }}"
-            id="view-tile-icon"></mf-icon-button>
-    <mf-icon-button
-            icon="view-list_thin"
-            ng-click="$ctrl.gotoSearchState('search.table')"
-            ng-attr-title="{{ 'Title_PeopleSearchTable' | translate }}"
-            id="view-list-icon"></mf-icon-button>
-    <div class="mf-divider vertical"></div>
-    <mf-icon-button
-            icon="orgchart_thin"
-            class="selected"
-            disabled="true"
-            ng-attr-title="{{ 'Title_OrgChart' | translate }}"
-            id="orgcharg-icon"></mf-icon-button>
-</mf-app-bar>
+<div class="ias-header">
+    <h2 id="page-content-title" translate="Title_Organization">Organization</h2>
+    <ias-search-box id="input" ng-model="$ctrl.query" ng-model-options="{debounce: $ctrl.inputDebounce}"
+                    placeholder="{{'Placeholder_Search' | translate}}" auto-focus>
+    </ias-search-box>
+    <!--<mf-auto-complete search-text="$ctrl.query"-->
+                      <!--on-search-text-change="$ctrl.onSearchTextChange(value)"-->
+                      <!--search="$ctrl.autoCompleteSearch(query)"-->
+                      <!--input-debounce="$ctrl.inputDebounce"-->
+                      <!--item-selected="$ctrl.onAutoCompleteItemSelected(person)"-->
+                      <!--item="person">-->
+        <!--<content-template>-->
+            <!--<span ng-bind="person._displayName"></span>-->
+        <!--</content-template>-->
+    <!--</mf-auto-complete>-->
+
+    <span class="ias-fill"></span>
+
+    <ias-button id="view-tile-icon" class="ias-icon-button"
+                ng-click="$ctrl.gotoSearchState('search.cards')"
+                ng-attr-title="{{ 'Title_PeopleSearchCard' | translate }}">
+        <ias-icon class="ias-selected" icon="view_tile_thin"></ias-icon>
+    </ias-button>
+    <ias-button id="view-list-icon" class="ias-icon-button"
+                ng-click="$ctrl.gotoSearchState('search.table')"
+                ng-attr-title="{{ 'Title_PeopleSearchTable' | translate }}">
+        <ias-icon class="ias-selected" icon="view_list_thin"></ias-icon>
+    </ias-button>
+    <div class="icon-divider vertical"></div>
+    <ias-button id="orgchart-icon" class="ias-icon-button ias-selected" ng-disabled="true"
+                ng-attr-title="{{ 'Title_OrgChart' | translate }}">
+        <ias-icon class="ias-selected" icon="orgchart_thin"></ias-icon>
+    </ias-button>
+</div>
+
 
 <org-chart person="$ctrl.person"
            direct-reports="$ctrl.directReports"

+ 0 - 4
client/src/peoplesearch/orgchart-search.component.scss

@@ -30,8 +30,4 @@ org-chart-search {
     flex: 1 1;
     overflow-x: auto;
   }
-
-  mf-app-bar {
-    margin-bottom: 10px;
-  }
 }

+ 6 - 2
client/src/peoplesearch/orgchart-search.component.ts

@@ -63,6 +63,10 @@ export default class OrgChartSearchComponent {
                 private pwmService: IPwmService) {
         this.searchTextLocalStorageKey = this.localStorageService.keys.SEARCH_TEXT;
         this.inputDebounce = this.pwmService.ajaxTypingWait;
+
+        $scope.$watch('$ctrl.query', () => {
+            this.onSearchTextChange();
+        });
     }
 
     $onInit(): void {
@@ -131,8 +135,8 @@ export default class OrgChartSearchComponent {
         this.$state.go('orgchart.search', { personId: person.userKey, query: null });
     }
 
-    onSearchTextChange(value: string): void {
-        this.query = value;
+    onSearchTextChange(): void {
+        // this.query = value;
         this.storeSearchText();
     }
 

+ 1 - 1
client/src/peoplesearch/orgchart.component.html

@@ -60,7 +60,7 @@
     <h3 translate="Title_DirectReports">Direct Reports</h3>
     <div class="org-chart-connector"></div>
 
-    <div class="person-card-list">
+    <div class="ias-grid">
         <person-card person="directReport"
                      show-direct-report-count="true"
                      show-image="$ctrl.showImages"

+ 88 - 61
client/src/peoplesearch/orgchart.component.scss

@@ -27,6 +27,71 @@ $org-chart-text-color: #808080;
 
 $manager-connector-height: 16px;
 
+
+.assistant,
+.manager {
+  .ias-tile {
+    border: none;
+    background-color: transparent;
+    display: block;
+    height: 96px;
+    padding: 0;
+    vertical-align: top;
+    width: 120px;
+
+    > .ias-avatar {
+      border: 3px solid #808080;
+      border-radius: 100%;
+      display: block;
+      margin: 0 auto;
+
+      &:hover {
+        //border-color: $person-card-border-color;
+      }
+    }
+
+    > .ias-tile-content {
+      background-color: white;
+      display: block;
+      margin-top: 8px;
+      text-align: center;
+      width: 100%;
+
+      > .reports {
+        right: 20px;
+      }
+
+      :nth-child(n + 3) {
+        display: none;
+      }
+    }
+  }
+}
+
+.self {
+  &.ias-tile {
+    background-color: #ffffff;
+    border: 3px solid #808080;
+    border-radius: 3px;
+    height: auto;
+    min-height: 96px;
+    //width: 346px;
+    max-width: 100%;
+
+    > .ias-avatar {
+      flex: 0 0 65px;
+      height: 65px;
+      width: 65px;
+      margin-bottom: 5px;
+    }
+
+    > .ias-tile-content {
+      flex-flow: row nowrap;
+    }
+  }
+}
+
+
 // (XS) Default display
 org-chart {
   display: block;
@@ -36,24 +101,12 @@ org-chart {
     display: none;
   }
 
-  // (S) Too wide for full width person-card in direct reports
-  &.small {
-    > .org-chart-section {
-      > .person-card-list {
-        > person-card {
-          margin-right: 5px;
-          width: 272px;
-        }
-      }
-    }
-  }
-
   // (L) Wide enough to show main person offset to right and display managers horizontally
   &.large {
     > .org-chart-section {
       text-align: left;
 
-      > person-card {
+      > .ias-tile {
         &[size="large"] {
           margin: 0 0 0 128px;
         }
@@ -107,16 +160,15 @@ org-chart {
               width: 69px;
             }
 
-            > person-card {
-              > .person-card-content {
-                > .avatar {
-                  background-color: $org-chart-secondary-connector-color;
+            .ias-tile {
+              > .ias-avatar {
+                background-color: $org-chart-secondary-connector-color;
 
-                  &:not(:hover) {
-                    border-color: $org-chart-secondary-connector-color;
-                  }
+                &:not(:hover) {
+                  border-color: $org-chart-secondary-connector-color;
                 }
-
+              }
+              > .ias-tile-content {
                 > .details {
                   > :first-child {
                     color: $org-chart-connector-color;
@@ -133,23 +185,19 @@ org-chart {
       }
 
       &.self {
-        person-card {
-          display: inline-block;
-        }
+        display: inline-flex;
 
         > .assistant {
           display: inline-block;
           margin-left: 33px;
           position: relative;
 
-          > person-card {
-            > .person-card-content {
-              > .avatar {
-                background-color: $org-chart-secondary-connector-color;
+          .ias-tile {
+            > .ias-avatar {
+              background-color: $org-chart-secondary-connector-color;
 
-                &:not(:hover) {
-                  border-color: $org-chart-secondary-connector-color;
-                }
+              &:not(:hover) {
+                border-color: $org-chart-secondary-connector-color;
               }
             }
           }
@@ -184,10 +232,10 @@ org-chart {
       &.overflow {
         .manager {
           &:last-child {
-            > person-card {
-              > .person-card-content {
+            > .ias-tile {
+              > .ias-tile-content {
                 > .avatar {
-                  background-image: url('../../images/icons/m_circle-horz-menu_thin.svg');
+                  //background-image: url('../../images/icons/m_circle-horz-menu_thin.svg');
                 }
 
                 > .reports {
@@ -205,10 +253,10 @@ org-chart {
         text-align: center;
 
         &.empty-manager {
-          > person-card {
+          > .ias-tile {
             cursor: initial;
 
-            > .person-card-content {
+            > .ias-tile-content {
               > .avatar {
                 background: $org-chart-secondary-connector-color;
                 border-color: $org-chart-secondary-connector-color;
@@ -221,7 +269,7 @@ org-chart {
           }
         }
 
-        > person-card {
+        > .ias-tile {
           display: inline-block;
         }
       }
@@ -243,25 +291,16 @@ org-chart {
       text-align: left;
     }
 
-    > person-card {
+    > .ias-tile {
       &[size="large"] {
         margin: 0 auto;
       }
     }
 
-    > .person-card-list {
+    > .ias-grid {
       border-top: 3px solid $org-chart-connector-color;
       min-height: 90px;
       padding-top: 5px;
-
-      > person-card {
-        display: inline-block;
-        width: 100%;
-
-        &:not(:last-child) {
-          margin-bottom: 5px;
-        }
-      }
     }
 
     .org-chart-connector {
@@ -285,24 +324,12 @@ org-chart {
       }
     }
 
-    // (S) Too wide for full width person-card in direct reports
-    &.small {
-      > .org-chart-section {
-        > .person-card-list {
-          > person-card {
-            margin-left: 5px;
-            margin-right: auto;
-          }
-        }
-      }
-    }
-
     // (L) Wide enough to show main person offset to right and display managers horizontally
     &.large {
       > .org-chart-section {
         text-align: right;
 
-        > person-card {
+        > .ias-tile {
           &[size="large"] {
             margin: 0 128px 0 0;
           }

+ 13 - 11
client/src/peoplesearch/peoplesearch-base.component.ts

@@ -57,6 +57,10 @@ abstract class PeopleSearchBaseComponent {
         this.searchViewLocalStorageKey = this.localStorageService.keys.SEARCH_VIEW;
 
         this.inputDebounce = this.pwmService.ajaxTypingWait;
+
+        $scope.$watch('$ctrl.query', (newValue: string, oldValue: string) => {
+            this.onSearchTextChange(newValue, oldValue);
+        });
     }
 
     getMessage(): string {
@@ -71,21 +75,19 @@ abstract class PeopleSearchBaseComponent {
         this.$state.go(state, { query: this.query });
     }
 
-    onSearchBoxKeyDown(event: KeyboardEvent): void {
-        switch (event.keyCode) {
-            case 27: // ESC
-                this.clearSearch();
-                break;
-        }
-    }
+    // onSearchBoxKeyDown(event: KeyboardEvent): void {
+    //     switch (event.keyCode) {
+    //         case 27: // ESC
+    //             this.clearSearch();
+    //             break;
+    //     }
+    // }
 
-    onSearchTextChange(value: string): void {
-        if (value === this.query) {
+    private onSearchTextChange(newValue: string, oldValue: string): void {
+        if (newValue === oldValue) {
             return;
         }
 
-        this.query = value;
-
         this.storeSearchText();
         this.clearSearchMessage();
         this.clearErrorMessage();

+ 23 - 28
client/src/peoplesearch/peoplesearch-cards.component.html

@@ -20,33 +20,28 @@
   ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   -->
 
-<mf-app-bar>
-    <div id="page-content-title" translate="Title_PeopleSearch">People Search</div>
-    <mf-search-bar input-debounce="$ctrl.inputDebounce"
-                   search-text="$ctrl.query"
-                   on-search-text-change="$ctrl.onSearchTextChange(value)"
-                   on-key-down="$ctrl.onSearchBoxKeyDown($event)"
-                   auto-focus></mf-search-bar>
-    <span flex></span>
-    <mf-icon-button
-            icon="view-tile_thin"
-            class="selected"
-            disabled="true"
-            ng-attr-title="{{ 'Title_PeopleSearchCard' | translate }}"
-            id="view-tile-icon"></mf-icon-button>
-    <mf-icon-button
-            icon="view-list_thin"
-            ng-click="$ctrl.gotoTableView()"
-            ng-attr-title="{{ 'Title_PeopleSearchTable' | translate }}"
-            id="view-list-icon"></mf-icon-button>
-    <div class="mf-divider vertical" ng-if="$ctrl.orgChartEnabled"></div>
-    <mf-icon-button
-            icon="orgchart_thin"
-            ng-click="$ctrl.gotoOrgchart()"
-            ng-if="$ctrl.orgChartEnabled"
-            ng-attr-title="{{ 'Title_OrgChart' | translate }}"
-            id="orgchart-icon"></mf-icon-button>
-</mf-app-bar>
+<div class="ias-header">
+    <h2 id="page-content-title" translate="Title_PeopleSearch">People Search</h2>
+    <ias-search-box id="input" ng-model="$ctrl.query" ng-model-options="{debounce: $ctrl.inputDebounce}"
+                    placeholder="{{'Placeholder_Search' | translate}}" auto-focus>
+    </ias-search-box>
+
+    <span class="ias-fill"></span>
+
+    <ias-button id="view-tile-icon" class="ias-icon-button ias-selected" ng-disabled="true"
+                ng-attr-title="{{ 'Title_PeopleSearchCard' | translate }}">
+        <ias-icon class="ias-selected" icon="view_tile_thin"></ias-icon>
+    </ias-button>
+    <ias-button id="view-list-icon" class="ias-icon-button" ng-click="$ctrl.gotoTableView()"
+                ng-attr-title="{{ 'Title_PeopleSearchTable' | translate }}">
+        <ias-icon class="ias-selected" icon="view_list_thin"></ias-icon>
+    </ias-button>
+    <div class="icon-divider vertical" ng-if="$ctrl.orgChartEnabled"></div>
+    <ias-button id="orgchart-icon" class="ias-icon-button" ng-click="$ctrl.gotoOrgchart()" ng-if="$ctrl.orgChartEnabled"
+                ng-attr-title="{{ 'Title_OrgChart' | translate }}">
+        <ias-icon class="ias-selected" icon="orgchart_thin"></ias-icon>
+    </ias-button>
+</div>
 
 <div class="search-info-container">
     <div class="search-info" ng-class="{'loading': !$ctrl.getMessage()}"
@@ -56,7 +51,7 @@
 </div>
 
 <div class="people-search-component-content">
-    <div class="person-card-list">
+    <div class="ias-grid">
         <person-card person="person"
                      show-image="$ctrl.photosEnabled"
                      ng-repeat="person in $ctrl.searchResult.people | orderBy:'displayNames[0]'"

+ 52 - 36
client/src/peoplesearch/peoplesearch-table.component.html

@@ -20,33 +20,41 @@
   ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   -->
 
-<mf-app-bar>
-    <div id="page-content-title" translate="Title_PeopleSearch">People Search</div>
-    <mf-search-bar input-debounce="$ctrl.inputDebounce"
-                   search-text="$ctrl.query"
-                   on-search-text-change="$ctrl.onSearchTextChange(value)"
-                   on-key-down="$ctrl.onSearchBoxKeyDown($event)"
-                   auto-focus></mf-search-bar>
-    <span flex></span>
-    <mf-icon-button
-            icon="view-tile_thin"
-            id="view-title-button"
-            ng-click="$ctrl.gotoCardsView()"
-            ng-attr-title="{{ 'Title_PeopleSearchCard' | translate }}"></mf-icon-button>
-    <mf-icon-button
-            icon="view-list_thin"
-            id="view-list-button"
-            class="selected"
-            disabled="true"
-            ng-attr-title="{{ 'Title_PeopleSearchTable' | translate }}"></mf-icon-button>
-    <div class="mf-divider vertical" ng-if="$ctrl.orgChartEnabled"></div>
-    <mf-icon-button
-            icon="orgchart_thin"
-            id="view-orgchart-button"
-            ng-click="$ctrl.gotoOrgchart()"
-            ng-if="$ctrl.orgChartEnabled"
-            ng-attr-title="{{ 'Title_OrgChart' | translate }}"></mf-icon-button>
-</mf-app-bar>
+<div class="ias-header">
+    <h2 id="page-content-title" translate="Title_PeopleSearch">People Search</h2>
+    <ias-search-box id="input" ng-model="$ctrl.query" ng-model-options="{debounce: $ctrl.inputDebounce}"
+                    placeholder="{{'Placeholder_Search' | translate}}" auto-focus>
+    </ias-search-box>
+
+    <span class="ias-fill"></span>
+
+    <ias-button id="view-title-button" class="ias-icon-button"
+                ng-click="$ctrl.gotoCardsView()"
+                ng-attr-title="{{ 'Title_PeopleSearchCard' | translate }}">
+        <ias-icon class="ias-selected" icon="view_tile_thin"></ias-icon>
+    </ias-button>
+    <ias-button id="view-list-button" class="ias-icon-button ias-selected" ng-disabled="true"
+                ng-attr-title="{{ 'Title_PeopleSearchTable' | translate }}">
+        <ias-icon class="ias-selected" icon="view_list_thin"></ias-icon>
+    </ias-button>
+    <div class="icon-divider vertical"></div>
+    <ias-button id="view-orgchart-button" class="ias-icon-button" ng-click="$ctrl.gotoOrgchart()"
+                ng-if="$ctrl.orgChartEnabled"
+                ng-attr-title="{{ 'Title_OrgChart' | translate }}">
+        <ias-icon class="ias-selected" icon="orgchart_thin"></ias-icon>
+    </ias-button>
+    <ias-button class="ias-icon-button table-configuration-menu-toggle" ias-toggle="menu1">
+        <ias-icon icon="configure_thick"></ias-icon>
+    </ias-button>
+    <ias-menu name="menu1" ias-align="end end">
+        <div class="ias-input-container">
+            <div class="checkbox-button" ng-repeat="(key, value) in $ctrl.columnConfiguration">
+                <input type="checkbox" ng-checked="value.visible" aria-label="Toggle column visibility" disabled/>
+                <ias-button ng-click="$ctrl.toggleColumnVisible($event, key)">{{value.label}}</ias-button>
+            </div>
+        </div>
+    </ias-menu>
+</div>
 
 <div class="search-info-container">
     <div class="search-info" ng-class="{'loading': !$ctrl.getMessage()}"
@@ -56,15 +64,23 @@
 </div>
 
 <div class="people-search-component-content">
-    <mf-table data="person in $ctrl.searchResult.people"
-              ng-show="$ctrl.searchResult.people.length"
-              search-highlight="$ctrl.query"
-              on-click-item="$ctrl.selectPerson(person)">
-        <mf-table-column ng-repeat="(key, value) in $ctrl.columnConfiguration"
-                         label="value"
-                         value="'person.' + key">
-        </mf-table-column>
-    </mf-table>
+    <table class="ias-table" ias-sort="$ctrl.sort" ng-show="$ctrl.searchResult.people.length">
+        <thead>
+        <tr>
+            <th ng-repeat="(key, value) in $ctrl.columnConfiguration" ng-if="value.visible"
+                ias-sort-on="key">{{value.label}}</th>
+        </tr>
+        </thead>
+        <tbody>
+        <tr ng-repeat="person in $ctrl.searchResult.people | orderBy:$ctrl.sort"
+            ng-click="$ctrl.selectPerson(person)">
+            <td ng-repeat="(key, value) in $ctrl.columnConfiguration" ng-if="value.visible">
+                <span ng-bind="person[key]"></span>
+            </td>
+            <td></td>
+        </tr>
+        </tbody>
+    </table>
 
     <ui-view></ui-view>
 </div>

+ 8 - 1
client/src/peoplesearch/peoplesearch-table.component.scss

@@ -29,6 +29,13 @@ people-search-table {
     > .people-search-component-content {
         flex: 1 1;
         overflow: auto;
+        position: relative;
         text-align: center;
+
+        .table-configuration-menu-toggle {
+            position: absolute;
+            top: 0;
+            right: 0;
+        }
     }
-}
+}

+ 22 - 1
client/src/peoplesearch/peoplesearch-table.component.ts

@@ -81,7 +81,16 @@ export default class PeopleSearchTableComponent extends PeopleSearchBaseComponen
         // The table columns are dynamic and configured via a service
         this.configService.getColumnConfig().then(
             (columnConfiguration: any) => {
-                self.columnConfiguration = columnConfiguration;
+                self.columnConfiguration = Object.keys(columnConfiguration).reduce(
+                    function(accumulator, columnId) {
+                        accumulator[columnId] = {
+                            label: columnConfiguration[columnId],
+                            visible: true
+                        };
+
+                        return accumulator;
+                    },
+                    {});
             },
             (error) => {
                 self.setErrorMessage(error);
@@ -99,6 +108,18 @@ export default class PeopleSearchTableComponent extends PeopleSearchBaseComponen
         }
     }
 
+    toggleColumnVisible(event, columnId): void {
+        const visibleColumns = Object.keys(this.columnConfiguration).filter((columnId) => {
+            return this.columnConfiguration[columnId].visible;
+        });
+
+        if (!(visibleColumns.length === 1 && this.columnConfiguration[columnId].visible)) {
+            this.columnConfiguration[columnId].visible = !this.columnConfiguration[columnId].visible;
+        }
+
+        event.stopImmediatePropagation();
+    }
+
     private onSearchResult(searchResult: SearchResult): void {
         this.searchResult = searchResult;
     }

+ 3 - 2
client/src/peoplesearch/peoplesearch.module.ts

@@ -28,7 +28,7 @@ import OrgChartComponent from './orgchart.component';
 import OrgChartSearchComponent from './orgchart-search.component';
 import PeopleSearchTableComponent from './peoplesearch-table.component';
 import PeopleSearchCardsComponent from './peoplesearch-cards.component';
-import PersonCardComponent from './person-card.component';
+import PersonCardDirective from './person-card.component';
 import PersonDetailsDialogComponent from './person-details-dialog.component';
 import LocalStorageService from '../services/local-storage.service';
 import PromiseService from '../services/promise.service';
@@ -39,6 +39,7 @@ require('./peoplesearch.scss');
 const moduleName = 'people-search';
 
 module(moduleName, [
+    'ngAria',
     'pascalprecht.translate',
     uxModule
 ])
@@ -46,7 +47,7 @@ module(moduleName, [
     .filter('highlight', HighlightFilter)
     .component('orgChart', OrgChartComponent as IComponentOptions)
     .component('orgChartSearch', OrgChartSearchComponent as IComponentOptions)
-    .component('personCard', PersonCardComponent as IComponentOptions)
+    .directive('personCard', PersonCardDirective)
     .component('peopleSearchTable', PeopleSearchTableComponent as IComponentOptions)
     .component('peopleSearchCards', PeopleSearchCardsComponent as IComponentOptions)
     .component('personDetailsDialogComponent', PersonDetailsDialogComponent as IComponentOptions)

+ 25 - 20
client/src/peoplesearch/peoplesearch.scss

@@ -36,21 +36,6 @@ body {
   }
 }
 
-a {
-  color: #007cd0;
-  cursor: pointer;
-  font-weight: normal;
-  text-decoration: none;
-
-  &:focus {
-    outline: 1px solid #01a9e7;
-  }
-
-  &:hover {
-    background-color: #f6f9f8;
-  }
-}
-
 .people-search-component {
   height: 100%;
 
@@ -59,10 +44,6 @@ a {
     overflow: auto;
   }
 
-  mf-app-bar {
-    margin-bottom: 10px;
-  }
-
   .search-info-container {
     text-align: left;
 
@@ -93,4 +74,28 @@ a {
       text-align: right;
     }
   }
-}
+}
+
+.ias-avatar {
+  background: transparent url('../../images/user.png') no-repeat center center;
+  background-size: contain;
+}
+
+.ias-header {
+  max-width: 100%;
+}
+
+.checkbox-button {
+  align-items: center;
+  display: flex;
+  flex-flow: row nowrap;
+}
+
+.icon-divider {
+  &.vertical {
+    background-color: rgba(#808080, .5);
+    height: 25px;
+    margin: 0 5px;
+    width: 1px;
+  }
+}

+ 15 - 14
client/src/peoplesearch/person-card.component.html

@@ -20,34 +20,35 @@
   ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   -->
 
-<div class="person-card-content" ng-attr-id="{{'displayName-' + $ctrl.person.displayNames[0] }}" ng-switch="$ctrl.size">
-    <div class="avatar" ng-if="$ctrl.isSmall() || $ctrl.showImage" ng-style="$ctrl.getAvatarStyle()" aria-label="User avatar"></div>
+<div class="ias-tile ias-medium" ng-attr-id="{{'displayName-' + $ctrl.person.displayNames[0] }}" ng-switch="$ctrl.size">
+    <div class="ias-avatar" ng-if="$ctrl.isSmall() || $ctrl.showImage"
+         ng-style="$ctrl.getAvatarStyle()"
+         aria-label="User avatar"></div>
+
     <div class="reports"
          ng-if="$ctrl.numDirectReportsVisible"
          ng-bind="$ctrl.person.numDirectReports"
          ng-attr-title="{{$ctrl.person.numDirectReports}} {{ 'Title_DirectReports' | translate }}"></div>
 
-    <div class="details" ng-switch-when="small">
-        <div ng-bind="$ctrl.person.displayNames[0]"></div>
+    <div class="ias-tile-content" ng-switch-when="small">
+        <h3 ng-bind="$ctrl.person.displayNames[0]"></h3>
         <div ng-bind="$ctrl.person.displayNames[1]"></div>
     </div>
 
-    <div ng-class="{details: true, 'direct-reports': $ctrl.numDirectReportsVisible}" ng-switch-when="large">
-        <div ng-bind="$ctrl.person.displayNames[0]"></div>
+    <div class="ias-tile-content" ng-class="{'direct-reports': $ctrl.numDirectReportsVisible}" ng-switch-when="large">
+        <h3 ng-bind="$ctrl.person.displayNames[0]"></h3>
         <div ng-bind="$ctrl.person.displayNames[1]"></div>
         <div ng-bind="$ctrl.person.displayNames[2]"></div>
         <div ng-bind="$ctrl.person.displayNames[3]"></div>
 
-        <div class="secondary-details">
-            <div ng-bind="$ctrl.person.displayNames[4]"></div>
-            <div ng-bind="$ctrl.person.displayNames[5]"></div>
-            <div ng-bind="$ctrl.person.displayNames[6]"></div>
-            <div ng-bind="$ctrl.person.displayNames[7]"></div>
-        </div>
+        <div ng-bind="$ctrl.person.displayNames[4]"></div>
+        <div ng-bind="$ctrl.person.displayNames[5]"></div>
+        <div ng-bind="$ctrl.person.displayNames[6]"></div>
+        <div ng-bind="$ctrl.person.displayNames[7]"></div>
     </div>
 
-    <div ng-class="{details: true, 'direct-reports': $ctrl.numDirectReportsVisible}" ng-switch-default>
-        <div ng-bind="$ctrl.person.displayNames[0]"></div>
+    <div class="ias-tile-content" ng-class="{'direct-reports': $ctrl.numDirectReportsVisible}" ng-switch-default>
+        <h3 ng-bind="$ctrl.person.displayNames[0]"></h3>
         <div ng-bind="$ctrl.person.displayNames[1]"></div>
         <div ng-bind="$ctrl.person.displayNames[2]"></div>
         <div ng-bind="$ctrl.person.displayNames[3]"></div>

+ 24 - 15
client/src/peoplesearch/person-card.component.ts

@@ -21,24 +21,14 @@
  */
 
 
-import { isString, IAugmentedJQuery } from 'angular';
-import { Component } from '../component';
+import {IAugmentedJQuery} from 'angular';
 import { IPerson } from '../models/person.model';
 import { IPeopleService } from '../services/people.service';
 
-@Component({
-    bindings: {
-        directReports: '<',
-        disableFocus: '<',
-        person: '<',
-        showImage: '<',
-        size: '@',
-        showDirectReportCount: '<'
-    },
-    stylesheetUrl: require('peoplesearch/person-card.component.scss'),
-    templateUrl: require('peoplesearch/person-card.component.html')
-})
-export default class PersonCardComponent {
+const templateUrl = require('peoplesearch/person-card.component.html');
+require('peoplesearch/person-card.component.scss');
+
+class PersonCardController {
     private details: any[]; // For large style cards
     private disableFocus: boolean;
     private person: IPerson;
@@ -124,3 +114,22 @@ export default class PersonCardComponent {
         }
     }
 }
+
+export default function PersonCardDirectiveFactory() {
+    return {
+        bindToController: true,
+        controller: PersonCardController,
+        controllerAs: '$ctrl',
+        restrict: 'E',
+        replace: true,
+        scope: {
+            directReports: '<',
+            disableFocus: '<',
+            person: '<',
+            showImage: '<',
+            size: '@',
+            showDirectReportCount: '<'
+        },
+        templateUrl: templateUrl
+    };
+}

+ 38 - 23
client/src/peoplesearch/person-details-dialog.component.html

@@ -20,23 +20,33 @@
   ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
   -->
 
-<mf-dialog class="person-details" on-close="$ctrl.closeDialog()">
-    <div class="mf-dialog-content">
-        <div class="person-details-header">
-            <person-card size="medium"
-                         person="$ctrl.person"
-                         disable-focus="true"
-                         show-image="$ctrl.photosEnabled"
-                         show-direct-report-count="false"></person-card>
-            <mf-button type="button" ng-click="$ctrl.gotoOrgChart()" ng-if="$ctrl.orgChartEnabled">
-                <mf-icon icon="orgchart_thin" id="orgchart-button"></mf-icon>
-                <span translate="Title_OrgChart">Organizational Chart</span>
-            </mf-button>
-        </div>
+<div class="ias-dialog person-details-dialog">
+    <div class="ias-dialog-container">
+        <div class="ias-dialog-content">
+            <div class="person-details-dialog-header">
+                <div class="ias-avatar" ng-if="$ctrl.photosEnabled" ng-style="$ctrl.getAvatarStyle()" alt="User image"></div>
 
-        <div class="person-details-content">
-            <table>
-                <tbody>
+                <div class="ias-header">
+                    <h2 ng-bind="$ctrl.person.displayNames[0]"></h2>
+                    <span class="ias-fill"></span>
+                    <ias-button class="ias-icon-button" ng-click="$ctrl.closeDialog()">
+                        <ias-icon icon="close_thick"></ias-icon>
+                    </ias-button>
+                </div>
+                <div ng-bind="$ctrl.person.displayNames[1]"></div>
+                <div ng-bind="$ctrl.person.displayNames[2]"></div>
+                <div ng-bind="$ctrl.person.displayNames[3]"></div>
+                <div class="person-dialog-actions">
+                    <ias-button type="button" class="ias-icon-text-button"
+                                ng-click="$ctrl.gotoOrgChart()" ng-if="$ctrl.orgChartEnabled">
+                        <ias-icon icon="orgchart_thin" id="orgchart-button"></ias-icon>
+                        <span translate="Title_OrgChart">Organizational Chart</span>
+                    </ias-button>
+                </div>
+            </div>
+            <div class="person-details-content">
+                <table class="details-table">
+                    <tbody>
                     <tr>
                         <td></td>
                         <td>
@@ -69,17 +79,22 @@
                                            ng-if="detail.type === 'tel'"></a>
                                         <span ng-bind="value"
                                               ng-if="detail.type !== 'email' && detail.type !== 'tel'"></span>
-                                        <mf-icon-button icon="search_thick"
-                                                        ng-click="$ctrl.searchText(value)"
-                                                        ng-if="detail.searchable"
-                                                        ng-attr-title="{{('Placeholder_Search' | translate) + ' \'' + value + '\''}}"></mf-icon-button>
+
+                                        <a ui-sref="search.table({ query: value })"
+                                           class="details-table-search-link"
+                                           ng-if="detail.searchable"
+                                           ng-attr-title="{{('Placeholder_Search' | translate) + ' \'' + value + '\''}}">
+                                            <ias-icon icon="search_thick"></ias-icon>
+                                        </a>
+
                                     </li>
                                 </ul>
                             </div>
                         </td>
                     </tr>
-                </tbody>
-            </table>
+                    </tbody>
+                </table>
+            </div>
         </div>
     </div>
-</mf-dialog>
+</div>

+ 64 - 93
client/src/peoplesearch/person-details-dialog.component.scss

@@ -20,114 +20,85 @@
  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
  */
 
+.person-dialog-actions {
+  clear: both;
+  margin-top: 15px;
+  text-align: center;
+}
+
+.person-details-dialog {
+  text-align: left;
 
-.person-details {
-  mf-app-bar {
-    padding: 0 5px;
+  .ias-dialog-container {
+    padding: 0;
   }
 
-  .mf-dialog-content {
-    > .person-details-header {
-      background-color: #eef2f2;
+  .ias-avatar {
+    float: left;
+    height: 100px;
+    margin-bottom: 15px;
+    margin-right: 15px;
+    width: 100px;
+  }
+}
 
-      > mf-button {
-        margin: 0 0 10px 0;
-      }
+.person-details-dialog-header {
+  background-color: #eef2f2;
+  color: #434c50;
+  font-size: 12px;
+  line-height: 15px;
+  padding: 15px;
+}
 
-      > person-card {
-        height: 120px;
-        max-width: 100%;
-        width: 100%;
+.person-details-content {
+  padding: 15px;
+}
 
-        > .person-card-content {
-          > .avatar {
-            flex-basis: 100px;
-            height: 100px;
-            width: 100px;
-          }
-        }
+.details-table {
+  border: none;
+  border-collapse: collapse;
+  width: 100%;
+
+  tr {
+    height: 25px;
+
+    td {
+      border: none;
+      font-size: 12px;
+      height: 19px;
+      text-align: left;
+
+      &:first-child {
+        color: #949494;
+        width: 100px;
+        text-align: right;
+        padding: 3px 0;
       }
-    }
-
-    > .person-details-content {
-      padding: 10px;
-
-      > table {
-        border: none;
-        border-collapse: collapse;
-        width: 100%;
 
-        tr {
-          height: 25px;
-
-          td {
-            border: none;
-            font-size: 12px;
-            height: 19px;
-            text-align: left;
-
-            &:first-child {
-              color: #949494;
-              width: 100px;
-              text-align: right;
-              padding: 3px 0;
-            }
-
-            &:last-child {
-              padding: 3px 10px;
-
-              > .detail-container {
-                > ul {
-                  > li {
-                    > mf-icon-button {
-                      display: inline-block;
-                      height: 16px;
-                      width: 16px;
-
-                      > button {
-                        > mf-icon {
-                          font-size: 16px;
-                        }
-                      }
-                    }
-                  }
-                }
-              }
-            }
+      &:last-child {
+        padding: 3px 15px;
+      }
 
-            ul {
-              list-style: none;
-              margin: 0;
-              padding: 0;
+      ul {
+        list-style: none;
+        margin: 0;
+        padding: 0;
 
-              > li {
-                margin: 0;
-                padding: 0;
-              }
-            }
-          }
+        > li {
+          margin: 0;
+          padding: 0;
         }
       }
     }
   }
-}
 
-[dir="rtl"] {
-  .person-details {
-    .mf-dialog-content {
-      > .person-details-content {
-        > table {
-          tr {
-            td {
-              text-align: right;
+  .details-table-search-link {
+    display: inline-block;
+    font-size: 25px;
+    vertical-align: middle;
 
-              &:first-child {
-                text-align: left;
-              }
-            }
-          }
-        }
-      }
+    .ias-icon {
+      display:block;
     }
   }
-}
+}

+ 8 - 0
client/src/peoplesearch/person-details-dialog.component.ts

@@ -79,6 +79,14 @@ export default class PersonDetailsDialogComponent {
         this.$state.go('^', { query: this.$stateParams['query'] });
     }
 
+    getAvatarStyle(): any {
+        if (!this.person || !this.person.photoURL || !this.photosEnabled) {
+            return null;
+        }
+
+        return  { 'background-image': 'url(' + this.person.photoURL + ')' };
+    }
+
     gotoOrgChart(): void {
         this.$state.go('orgchart.search', { personId: this.person.userKey });
     }

+ 1 - 1
client/src/services/base-config.service.dev.ts

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  *
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by

+ 1 - 1
client/src/services/helpdesk-config.service.dev.ts

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  *
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by

+ 1 - 1
client/src/services/helpdesk-config.service.ts

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  *
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by

+ 2 - 2
client/src/services/helpdesk.service.dev.ts

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  *
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by
@@ -26,7 +26,7 @@ import {
 } from './helpdesk.service';
 import {IPromise, IQService, ITimeoutService, IWindowService} from 'angular';
 
-const SIMULATED_RESPONSE_TIME = 30;
+const SIMULATED_RESPONSE_TIME = 300;
 
 export default class HelpDeskService implements IHelpDeskService {
     PWM_GLOBAL: any;

+ 1 - 1
client/src/services/helpdesk.service.ts

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  *
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by

+ 1 - 0
client/src/services/local-storage.service.ts

@@ -26,6 +26,7 @@ import { ILogService, IWindowService } from 'angular';
 const PWM_PREFIX = 'PWM_';
 const KEYS = {
     SEARCH_TEXT: 'searchText',
+    HELPDESK_SEARCH_TEXT: 'helpdeskSearchText',
     SEARCH_VIEW: 'searchView',
     VERIFICATION_STATE: 'verificationState'
 };

+ 20 - 22
client/src/services/people.data.json

@@ -9,7 +9,7 @@
     "title": "Senior Sales Associate",
     "workforceId": "E84-001",
     "managerId": null,
-    "photoURL": "/images/avatars/1.jpg",
+    "photoURL": "http://i.pravatar.cc/128?img=1",
     "_displayName": "Jean Ryan - Senior Sales Associate",
     "displayNames": [
       "Jean Ryan",
@@ -90,7 +90,7 @@
     "title": "Quality Engineer",
     "workforceId": "E84-002",
     "managerId": 1,
-    "photoURL": "/images/avatars/2.jpg",
+    "photoURL": "http://i.pravatar.cc/128?img=2",
     "_displayName": "Ruby Bowman - Quality Engineer",
     "displayNames": [
       "Ruby Bowman",
@@ -171,7 +171,7 @@
     "title": "Community Outreach Specialist",
     "workforceId": "E84-003",
     "managerId": 1,
-    "photoURL": "/images/avatars/3.jpg",
+    "photoURL": "http://i.pravatar.cc/128?img=3",
     "_displayName": "William Carter - Community Outreach Specialist",
     "displayNames": [
       "William Carter",
@@ -252,7 +252,7 @@
     "title": "Research Nurse",
     "workforceId": "E84-004",
     "managerId": 1,
-    "photoURL": "/images/avatars/4.jpg",
+    "photoURL": "http://i.pravatar.cc/128?img=4",
     "_displayName": "Alan Snyder - Research Nurse",
     "displayNames": [
       "Alan Snyder",
@@ -333,7 +333,7 @@
     "title": "Financial Analyst",
     "workforceId": "E84-005",
     "managerId": 2,
-    "photoURL": "/images/avatars/5.jpg",
+    "photoURL": "http://i.pravatar.cc/128?img=5",
     "_displayName": "Aaron Alvarez - Financial Analyst",
     "displayNames": [
       "Aaron Alvarez",
@@ -495,7 +495,7 @@
     "title": "Design Engineer",
     "workforceId": "E84-007",
     "managerId": 2,
-    "photoURL": "/images/avatars/7.jpg",
+    "photoURL": "http://i.pravatar.cc/128?img=7",
     "_displayName": "Mildred Hayes - Design Engineer",
     "displayNames": [
       "Mildred Hayes",
@@ -576,7 +576,7 @@
     "title": "Structural Engineer",
     "workforceId": "E84-008",
     "managerId": 3,
-    "photoURL": "/images/avatars/8.jpg",
+    "photoURL": "http://i.pravatar.cc/128?img=8",
     "_displayName": "Margaret Holmes - Structural Engineer",
     "displayNames": [
       "Margaret Holmes",
@@ -657,7 +657,7 @@
     "title": "Junior Executive",
     "workforceId": "E84-009",
     "managerId": 3,
-    "photoURL": "/images/avatars/9.jpg",
+    "photoURL": "http://i.pravatar.cc/128?img=9",
     "_displayName": "Jack Jackson - Junior Executive",
     "displayNames": [
       "Jack Jackson",
@@ -749,7 +749,7 @@
     "title": "Chief Design Engineer",
     "workforceId": "E84-010",
     "managerId": 3,
-    "photoURL": "/images/avatars/10.jpg",
+    "photoURL": "http://i.pravatar.cc/128?img=10",
     "_displayName": "Judy Butler - Chief Design Engineer",
     "displayNames": [
       "Judy Butler",
@@ -830,7 +830,7 @@
     "title": "Engineer IV",
     "workforceId": "E84-011",
     "managerId": 9,
-    "photoURL": "/images/avatars/11.jpg",
+    "photoURL": "http://i.pravatar.cc/128?img=11",
     "_displayName": "Tina Gutierrez - Engineer IV",
     "displayNames": [
       "Tina Gutierrez",
@@ -911,7 +911,7 @@
     "title": "Human Resources Manager",
     "workforceId": "E84-012",
     "managerId": 9,
-    "photoURL": "/images/avatars/12.jpg",
+    "photoURL": "http://i.pravatar.cc/128?img=12",
     "_displayName": "Jose Cox - Human Resources Manager",
     "displayNames": [
       "Jose Cox",
@@ -1073,7 +1073,7 @@
     "title": "Automation Specialist I",
     "workforceId": "E84-014",
     "managerId": 9,
-    "photoURL": "/images/avatars/14.jpg",
+    "photoURL": "http://i.pravatar.cc/128?img=14",
     "_displayName": "Joe Stevens - Automation Specialist I",
     "displayNames": [
       "Joe Stevens",
@@ -1154,7 +1154,7 @@
     "title": "Safety Technician I",
     "workforceId": "E84-015",
     "managerId": 20,
-    "photoURL": "/images/avatars/15.jpg",
+    "photoURL": "http://i.pravatar.cc/128?img=15",
     "_displayName": "Randy Grant - Safety Technician I",
     "displayNames": [
       "Randy Grant",
@@ -1235,7 +1235,7 @@
     "title": "Compensation Analyst",
     "workforceId": "E84-016",
     "managerId": 9,
-    "photoURL": "/images/avatars/16.jpg",
+    "photoURL": "http://i.pravatar.cc/128?img=16",
     "_displayName": "Martin Mason - Compensation Analyst",
     "displayNames": [
       "Martin Mason",
@@ -1316,7 +1316,7 @@
     "title": "Data Coordiator",
     "workforceId": "E84-017",
     "managerId": 16,
-    "photoURL": "/images/avatars/17.jpg",
+    "photoURL": "http://i.pravatar.cc/128?img=17",
     "_displayName": "Cynthia Porter - Data Coordiator",
     "displayNames": [
       "Cynthia Porter",
@@ -1397,13 +1397,11 @@
     "title": "Business Systems Development Analyst",
     "workforceId": "E84-018",
     "managerId": 17,
-    "photoURL": "/images/avatars/18.jpg",
+    "photoURL": "http://i.pravatar.cc/128?img=18",
     "_displayName": "Nancy Burns - Business Systems Development Analyst",
     "displayNames": [
       "Nancy Burns",
-      "nburnsh@wordpress.org",
-      "Business Systems Development Analyst",
-      "(444) 231-5492"
+      "nburnsh@wordpress.org"
     ],
     "detail": {
       "sn": {
@@ -1478,7 +1476,7 @@
     "title": "Structural Engineer",
     "workforceId": "E84-019",
     "managerId": 18,
-    "photoURL": "/images/avatars/19.jpg",
+    "photoURL": "http://i.pravatar.cc/128?img=26",
     "_displayName": "Jimmy Montgomery - Structural Engineer",
     "displayNames": [
       "Jimmy Montgomery",
@@ -1559,7 +1557,7 @@
     "title": "Desktop Support Technician",
     "workforceId": "E84-020",
     "managerId": 19,
-    "photoURL": "/images/avatars/20.jpg",
+    "photoURL": "http://i.pravatar.cc/128?img=20",
     "_displayName": "Bruce Carroll - Desktop Support Technician",
     "displayNames": [
       "Bruce Carroll",
@@ -1640,7 +1638,7 @@
     "title": "No Real Position",
     "workforceId": "E84-021",
     "managerId": null,
-    "photoURL": "/images/avatars/21.jpg",
+    "photoURL": "http://i.pravatar.cc/128?img=21",
     "_displayName": "Orphan User - No Real Position",
     "displayNames": [
       "Orphan User",

+ 1 - 1
client/src/services/peoplesearch-config.service.ts

@@ -3,7 +3,7 @@
  * http://www.pwm-project.org
  *
  * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2017 The PWM Project
+ * Copyright (c) 2009-2018 The PWM Project
  *
  * This program is free software; you can redistribute it and/or modify
  * it under the terms of the GNU General Public License as published by

+ 0 - 93
client/src/ux/app-bar.component.scss

@@ -1,93 +0,0 @@
-/*!
- * Password Management Servlets (PWM)
- * http://www.pwm-project.org
- *
- * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2018 The PWM Project
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-
-$mf-app-bar-height: 37px;
-$mf-app-bar-icon-size: 32px;
-
-mf-app-bar {
-  display: block;
-  min-height: $mf-app-bar-height;
-
-  // For larger displays, place the search bar inline
-  &.large {
-    > .mf-app-bar-content {
-      > mf-auto-complete,
-      > mf-search-bar {
-        margin: 0 10px;
-        max-width: 320px;
-        flex: 1000 1;
-        order: initial;
-        width: auto;
-      }
-    }
-  }
-
-  // For extra-large displays, add extra spacing around search bar
-  &.extra-large {
-    > .mf-app-bar-content {
-      > mf-auto-complete,
-      > mf-search-bar {
-        margin: 0 30px;
-      }
-    }
-  }
-
-  > .mf-app-bar-content {
-    display: flex;
-    flex-flow: row wrap;
-    line-height: $mf-app-bar-height;
-
-    > mf-auto-complete,
-    > mf-search-bar {
-      height: $mf-app-bar-height;
-      margin-top: 5px;
-      order: 1;
-      width: 100%;
-    }
-
-    > span[flex] {
-      display: inline-block;
-      flex: 1 1;
-    }
-
-    > #page-content-title {
-      font-size: 20px;
-      font-weight: normal;
-      height: $mf-app-bar-height;
-      line-height: $mf-app-bar-height;
-    }
-
-    > mf-icon-button {
-      margin-top: ($mf-app-bar-height - $mf-app-bar-icon-size) / 2;
-    }
-  }
-
-  .mf-divider {
-    &.vertical {
-      background-color: transparentize(#808080, .50);
-      height: $mf-app-bar-height;
-      margin: 0 5px;
-      width: 1px;
-    }
-  }
-}

+ 0 - 46
client/src/ux/app-bar.component.ts

@@ -1,46 +0,0 @@
-/*
- * Password Management Servlets (PWM)
- * http://www.pwm-project.org
- *
- * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2018 The PWM Project
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-
-import { Component } from '../component';
-import { IAugmentedJQuery } from 'angular';
-import ElementSizeService from './element-size.service';
-
-export enum AppBarSize {
-    Large = 413,
-    ExtraLarge = 473
-}
-
-@Component({
-    stylesheetUrl: require('ux/app-bar.component.scss'),
-    template: `<div class="mf-app-bar-content" ng-transclude></div>`,
-    transclude: true
-})
-export default class AppBarComponent {
-    static $inject = [ '$element', 'MfElementSizeService' ];
-    constructor(private $element: IAugmentedJQuery, private elementSizeService: ElementSizeService) {
-    }
-
-    $onInit(): void {
-        this.elementSizeService.watchWidth(this.$element, AppBarSize);
-    }
-}

+ 0 - 86
client/src/ux/auto-complete.component.scss

@@ -1,86 +0,0 @@
-/*!
- * Password Management Servlets (PWM)
- * http://www.pwm-project.org
- *
- * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2018 The PWM Project
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-
-mf-auto-complete {
-  display: block;
-  height: 21px;
-  position: relative;
-
-  > mf-search-bar {
-    height: 100%;
-    max-width: 100%;
-    width: 100%;
-  }
-
-  > .results {
-    background-color: white;
-    bottom: 0;
-    box-shadow: 0 2px 5px 0 rgba(0, 0, 0, .26);
-    list-style: none;
-    margin: 0;
-    max-height: 240px;
-    max-width: 100%;
-    overflow-y: auto;
-    padding: 0;
-    position: absolute;
-    transform: translateY(100%);
-    width: 100%;
-    z-index: 100;
-
-    > li {
-      box-sizing: border-box;
-      cursor: pointer;
-      height: 24px;
-      line-height: 24px;
-      overflow: hidden;
-      padding: 0 10px;
-      text-overflow: ellipsis;
-      text-align: left;
-      white-space: nowrap;
-      width: 100%;
-
-      &:hover {
-        background-color: #eef2f2;
-      }
-
-      &.search-message {
-        color: #808080;
-        text-align: center;
-      }
-
-      &.selected {
-        background-color: #dae1e1;
-      }
-    }
-  }
-}
-
-[dir="rtl"] {
-  mf-auto-complete {
-    > .results {
-      > li {
-        text-align: right;
-      }
-    }
-  }
-}

+ 0 - 236
client/src/ux/auto-complete.component.ts

@@ -1,236 +0,0 @@
-/*
- * Password Management Servlets (PWM)
- * http://www.pwm-project.org
- *
- * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2018 The PWM Project
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-
-import { Component } from '../component';
-import { IAttributes, IAugmentedJQuery, IDocumentService, IPromise, IScope } from 'angular';
-
-@Component({
-    bindings: {
-        'onSearchTextChange': '&',
-        'inputDebounce': '<',
-        'itemSelected': '&',
-        'item': '@',
-        'itemText': '@',
-        'searchFunction': '&search',
-        'searchText': '<'
-    },
-    template: [
-        '$element',
-        '$attrs',
-        ($element: IAugmentedJQuery, $attrs: IAttributes): string => {
-            // Remove content template from dom
-            const contentTemplate: IAugmentedJQuery = $element.find('content-template');
-            contentTemplate.detach();
-
-            return `
-                <mf-search-bar input-debounce="$ctrl.inputDebounce"
-                               search-text="$ctrl.searchText"
-                               on-search-text-change="$ctrl.onSearchBarTextChange(value)"
-                               on-key-down="$ctrl.onSearchBarKeyDown($event)"
-                               ng-click="$ctrl.onSearchBarClick($event)"
-                               auto-focus></mf-search-bar>
-                <ul class="results" ng-if="$ctrl.show" ng-click="$event.stopPropagation()">
-                    <li ng-repeat="item in $ctrl.items"
-                       ng-click="$ctrl.selectItem(item)"
-                       ng-class="{ \'selected\': $index == $ctrl.selectedIndex }\">` +
-                contentTemplate.html().replace(new RegExp($attrs['item'], 'g'), 'item') +
-                    `</li>
-                    <li class="search-message"
-                        ng-if="$ctrl.show && $ctrl.searchText && !$ctrl.loading && !$ctrl.items.length">
-                        <span translate="Display_SearchResultsNone"></span>
-                    </li>
-                </ul>`;
-        }],
-    stylesheetUrl: require('ux/auto-complete.component.scss')
-})
-export default class AutoCompleteComponent {
-    item: string;
-    items: any[];
-    itemSelected: (item: any) => void;
-    loading: boolean;
-    onSearchTextChange: Function;
-    searchText: string;
-    searchFunction: (query: any) => IPromise<any[]>;
-    searchMessage: string;
-    selectedIndex: number;
-    show: boolean;
-
-    static $inject = [ '$document', '$element', '$scope' ];
-    constructor(private $document: IDocumentService,
-                private $element: IAugmentedJQuery,
-                private $scope: IScope) {
-    }
-
-    $onDestroy(): void {
-        this.$document.off('click', this.onDocumentClick.bind(this));
-    }
-
-    $onInit(): void {
-        this.selectedIndex = -1;
-
-        if (this.searchText) {
-            this.fetchAutoCompleteData(this.searchText);
-        }
-
-        this.hideResults();
-    }
-
-    $postLink(): void {
-        let self = this;
-
-        // Listen for clicks outside of the auto-complete component
-        // Implemented as a click event instead of a blur event, so the results list can be clicked
-        this.$document.on('click', this.onDocumentClick.bind(this));
-    }
-
-    onSearchBarClick(event: Event): void {
-        event.stopImmediatePropagation();
-    }
-
-    onSearchBarFocus(): void {
-        if (this.hasItems()) {
-            this.showResults();
-        }
-    }
-
-    onSearchBarTextChange(value: string): void {
-        this.searchText = value;
-        this.fetchAutoCompleteData(value);
-        this.showResults();
-
-        this.onSearchTextChange({ value: value });
-    }
-
-    onSearchBarKeyDown(event: KeyboardEvent): void {
-        switch (event.keyCode) {
-            case 40: // ArrowDown
-                if (this.hasItems() && !this.show) {
-                    this.showResults();
-                }
-                else {
-                    this.selectNextItem();
-                    event.preventDefault();
-                }
-                break;
-            case 38: // ArrowUp
-                this.selectPreviousItem();
-                event.preventDefault();
-                break;
-            case 27: // Escape
-                if (!this.show || !this.hasItems()) {
-                    this.clearResults();
-                }
-                else {
-                    this.hideResults();
-                }
-
-                break;
-            case 13: // Enter
-                if (this.hasItems() && this.show) {
-                    const item = this.getSelectedItem();
-                    this.selectItem(item);
-                }
-                break;
-            case 9: // Tab
-                if (!this.searchText || !this.show) {
-                    return;
-                }
-
-                if (event.shiftKey) {
-                    this.selectPreviousItem();
-                }
-                else {
-                    this.selectNextItem();
-                }
-
-                event.preventDefault();
-                break;
-        }
-    }
-
-    selectItem(item: any): void {
-        this.clearResults();
-
-        const data = {};
-        data[this.item] = item;
-        this.itemSelected(data);
-    }
-
-    private clearResults(): void {
-        this.resetSelection();
-        this.searchText = null;
-        this.items = [];
-    }
-
-    private onDocumentClick(): void {
-        if (this.show) {
-            this.hideResults();
-            this.$scope.$apply();
-        }
-    }
-
-    private fetchAutoCompleteData(value: string): void {
-        this.loading = true;
-        const self = this;
-        this.searchFunction({ query: value })
-            .then((results: any[]) => {
-                self.items = results;
-                self.resetSelection();
-            })
-            .finally(() => {
-                self.loading = false;
-            });
-    }
-
-    private getSelectedItem(): any {
-        return this.items[this.selectedIndex];
-    }
-
-    private hasItems(): boolean {
-        return this.items && !!this.items.length;
-    }
-
-    private hideResults(): void  {
-        this.show = false;
-    }
-
-    private resetSelection(): void {
-        this.selectedIndex = 0;
-    }
-
-    private selectNextItem(): void {
-        if (this.hasItems() && this.selectedIndex < this.items.length - 1) {
-            this.selectedIndex++;
-        }
-    }
-
-    private selectPreviousItem(): void {
-        if (this.hasItems() && this.selectedIndex > 0) {
-            this.selectedIndex--;
-        }
-    }
-
-    private showResults(): void  {
-        this.show = true;
-    }
-}

+ 0 - 65
client/src/ux/button.component.scss

@@ -1,65 +0,0 @@
-/*!
- * Password Management Servlets (PWM)
- * http://www.pwm-project.org
- *
- * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2018 The PWM Project
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-
-mf-button {
-  display: inline-block;
-  margin: 10px 0;
-
-  > button {
-    background-color: #f6f9f8;
-    border: 1px solid #dae1e1;
-    border-radius: 3px;
-    color: #434c50;
-    cursor: pointer;
-    display: block;
-    font-size: 14px;
-    height: 26px;
-    line-height: 26px;
-    padding: 0 10px;
-
-    &:focus,
-    &:hover {
-      color: #007cd0;
-      outline: none;
-    }
-
-    &:focus {
-      border-color: #01a9e7;
-    }
-
-    &:hover {
-      border-color: #dae1e1;
-    }
-  }
-
-  &[disabled] {
-    > button {
-      background-color: #f6f9f8;
-      border-color: #dae1e1;
-      color: #434c50;
-      cursor: default;
-      opacity: 0.4;
-      outline: none;
-    }
-  }
-}

+ 0 - 32
client/src/ux/button.component.ts

@@ -1,32 +0,0 @@
-/*
- * Password Management Servlets (PWM)
- * http://www.pwm-project.org
- *
- * Copyright (c) 2006-2009 Novell, Inc.
- * Copyright (c) 2009-2018 The PWM Project
- *
- * This program is free software; you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation; either version 2 of the License, or
- * (at your option) any later version.
- *
- * This program 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 General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program; if not, write to the Free Software
- * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
- */
-
-
-import { Component } from '../component';
-
-@Component({
-    stylesheetUrl: require('ux/button.component.scss'),
-    template: '<button type="button"><ng-transclude></ng-transclude></button>',
-    transclude: true
-})
-export default class ButtonComponent {
-}

+ 0 - 28
client/src/ux/dialog.component.html

@@ -1,28 +0,0 @@
-<!--
-  ~ Password Management Servlets (PWM)
-  ~ http://www.pwm-project.org
-  ~
-  ~ Copyright (c) 2006-2009 Novell, Inc.
-  ~ Copyright (c) 2009-2018 The PWM Project
-  ~
-  ~ This program is free software; you can redistribute it and/or modify
-  ~ it under the terms of the GNU General Public License as published by
-  ~ the Free Software Foundation; either version 2 of the License, or
-  ~ (at your option) any later version.
-  ~
-  ~ This program 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 General Public License for more details.
-  ~
-  ~ You should have received a copy of the GNU General Public License
-  ~ along with this program; if not, write to the Free Software
-  ~ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
-  -->
-
-<div class="scrim" ng-click="$ctrl.closeDialog()"></div>
-<div class="mf-dialog-container">
-    <mf-icon-button icon="close_thick" ng-click="$ctrl.closeDialog()"></mf-icon-button>
-
-    <ng-transclude></ng-transclude>
-</div>

Daži faili netika attēloti, jo izmaiņu fails ir pārāk liels