понедельник, 21 ноября 2016 г.

regex word wrapping в духе notepad

Иногда возникает необходимость разбить строку на подстроки, чья длина не превышает заданную максимальную длину. При этом, если слово не умещается в текущей строке, то его необходимо целиком перенести на новую строку.

Т.е. нам надо симулировать поведение функции Word Wrap из приложения Notepad в Windows.

Есть два варианта решения этой задачи:
  1. Написать алгоритм
  2. Прикрутить регулярные выражения. И если учесть, что такая задача может возникнуть где угодно (т.е. как на клиенте, так и на сервере), а регулярные выражения поддерживаются практически всеми языками программирования, то этот вариант кажется наиболее предпочтительным.

Вот просто пример на C#, когда нам нужно разбить строку с длинным почтовым адресом longAddressString и представить результат в виде коллекции строк, чья длинна не превышает 40 символов.

List<string> adresses = (from Match m in Regex.Matches(longAddressString, @"(?:((?>.{1,40}(?:(?<=[^\S\r\n])[^\S\r\n]?|(?=\r?\n)|$|[^\S\r\n]))|.{1,40})(?:\r?\n)?|(?:\r?\n|$))") where !String.IsNullOrWhiteSpace(m.Value) select m.Value ).ToList();

А ниже объяснение работы данного регулярного выражения от автора.

# MS-Windows  "Notepad.exe Word Wrap" simulation
 # ( N = 16 )
 # ============================
 # Find:     @"(?:((?>.{1,16}(?:(?<=[^\S\r\n])[^\S\r\n]?|(?=\r?\n)|$|[^\S\r\n]))|.{1,16})(?:\r?\n)?|(?:\r?\n|$))"
 # Replace:  @"$1\r\n"
 # Flags:    Global     

 # Note - Through trial and error discovery, it apparears Notepad accepts an extra whitespace
 # (possibly in the N+1 position) to help alignment. This matters not because thier viewport hides it.
 # There is no trimming of any whitespace, so the wrapped buffer could be reconstituted by inserting/detecting a
 # wrap point code which is different than a linebreak.
 # This regex works on un-wrapped source, but could probably be adjusted to produce/work on wrapped buffer text.
 # To reconstitute the source all that is needed is to remove the wrap code which is probably just an extra "\r".

 (?:
      # -- Words/Characters 
      (                       # (1 start)
           (?>                     # Atomic Group - Match words with valid breaks
                .{1,16}                 #  1-N characters
                                        #  Followed by one of 4 prioritized, non-linebreak whitespace
                (?:                     #  break types:
                     (?<= [^\S\r\n] )        # 1. - Behind a non-linebreak whitespace
                     [^\S\r\n]?              #      ( optionally accept an extra non-linebreak whitespace )
                  |  (?= \r? \n )            # 2. - Ahead a linebreak
                  |  $                       # 3. - EOS
                  |  [^\S\r\n]               # 4. - Accept an extra non-linebreak whitespace
                )
           )                       # End atomic group
        |  
           .{1,16}                 # No valid word breaks, just break on the N'th character
      )                       # (1 end)
      (?: \r? \n )?           # Optional linebreak after Words/Characters
   |  
      # -- Or, Linebreak
      (?: \r? \n | $ )        # Stand alone linebreak or at EOS
 )

среда, 16 ноября 2016 г.

Изменил логин (ФИО и email) учётной записи в AD, а в SharePoint Foundation отображаются старый данные пользователя

У нас в компании сложилась интересная ситуация. Время от времени у руководителя меняться ассистент и секретарь. Следовательно "новому" сотруднику необходимо передать календарь Exchenge и всё всё всё, с чем работал "старый" сотрудник.. Ведь учётная запись такого важного человека как ассистент или секретарь светится в разных системах предприятия.

Администраторы вышли из этого положения очень просто. Они полностью переименовывают учётную запись "старого" секретаря. Т.е. заменяют в учётной записи "старого" секретаря все поля:

  • Логин (я кстати не знал, что его запросто можно поменять в AD)
  • ФИО
  • почтовый ящик
  • и т.д.

И вроде бы все системы подсасывают из AD новые данные из учётки.. все.. кроме SharePoint Foundation.

SharePoint Foundation не синхронизирует данные пользователя с AD (Active Directory)!

Вот в очередной раз "старый" секретарь уступил своё место "новому". "Новый" секретарь заходит на портал и авторизуется вводя свой логин и пароль ( хотя за частую авторизация проходит прозрачно и ни какого логина и пароля портал вообще не запрашивается). Однако на первой же странице портала в верхнем правом углу он видит Имя "старого" секретаря. У сотрудника складывается впечатление, что он каким то образом зашёл на портал под другим пользователем. Попытка перелогиниться результатов не даёт. Сотрудник по прежнему видит имя "старого" секретаря.

При этом админы AD разводят руки, у них то всё правильно.

Что же происходит?

Когда пользователь заходит на портал, то для него создаётся запись в скрытом списке:
 User Information List (/_catalogs/users/simple.aspx)
Эти записи, скажем так, выполнят роль профиля пользователя в SharePoint Foundation. Данные в этот список подтягиваются из AD из учётной записи пользователя.

Однако данные в User Information List сами по себе не обновляются! (По крайней мере в SharePoint Foundation. Возможно в SharePoint Server, где есть синхронизация с AD, всё иначе и данный с AD синхронизируются корректно)

Таким образом, если пользователя завели в AD и дали доступ на портал, то для него будет создана запись в User Information List. Но если после этого админы зашли в Active Directory и поменяли у данной учётно записи все параметры (включая логи пользователя, ФИО и адрес почты), то в список User Information List эти новые данные автоматически не подтянутся и следовательно данные в User Information List останутся старыми и будут отличаться от данных в AD.

Например, если первоначально секретарём был Иванов Иван Иванович с логином i.ivanov, а потом администраторы переименовали (именно изменили существующую, а не создали ) его учётную запись в AD в Петров Пётр Петрович с логином p.petrov, то такой пользователь будет нормально логиниться на портал под логином p.petrov, НО будет видеть вместо своего имени (Петров Пётр Петрович), имя старого секретаря (Иванов Иван Иванович), потому что имя и прочие данные будут подтягиваться из User Information List, куда они были записаны ещё в те давние времена, когда учётная запись секретаря именовалась Иванов Иван Иванович.

Самый простой способ исправить эту ситуацию, это найти и удалить пользователя с портала и удалить запись в списке User Information List, а затем вновь добавить пользователя на портал.

Чтобы удалить пользователя и запись в User Information List, для начала её необходимо найти. Для этого нужно открыть список User Information List, который находится по адресу /_catalogs/users/simple.aspx и ручками прошерстить его в поисках записи для соответсвующей учётной записи.

В нашем случае нам необходимо найти учётную запись с именем "старого" секретаря. Так как именно она содержит устаревшие данные.

 Однако если вы знаете ID пользователя, то можно открыть интересующую нас запись, воспользовавшись следующим URL-адресом:
 /_layouts/15/userdisp.aspx?ID={ID пользователя}
или
/_layouts/listform.aspx?PageType=4&ListId={{GUID списка - User Information List}}&ID={ID пользователя}


Например:
http://emg-sed/_layouts/15/userdisp.aspx?ID=150
или
http://emg-sed/_layouts/listform.aspx?PageType=4&ListId={246E4250-6364-4241-10C3-34BF2E063802}&ID=150

А далее достаточно нажать кнопку "Удалить пользователя из семейства веб-сайтов":

После этого я, как правило, прошу пользователя вновь зайти на портал. При этом SharePoint разумеется не пустит пользователя и покажет ему страницу с которой он может написать обращение к администратору SharePoint - "Зачем вам нужен доступ на портал".
Получив такой запрос я вновь выдаю пользователю все необходимые права и SharePoint вновь создаёт (хотя правильнее сказать, восстанавливает) запись в User Information List, но уже с правильными, новыми, актуальными данными, которые он подтягивает из AD.

Теперь наш "новый" секретарь, заходя на портал видит своё имя и запись в User Information List содержит актуальные данные из учётной записи AD.

PS: Что интересно, запись в списке User Information List создаётся с тем же самым ID, с каким она была, до удаления. По этому можно говорить о том, что запись не создаётся а восстанавливается.

Как вы наверное помните, каждая новая запись в списке SharePoint имеет уникальный ID, который на единицу больше, чем был у предыдущей записи в этом же списке. Причём не важно удалили вы предыдущую запись или она по прежнему в списке. Всё равно ID у новой записи списка будет на единицу больше. SharePoint даже не пытается искать в списке запись с наибольшим значением в ID. Вместо этого он запоминает текущий ID в данном списке и инкрементирует это значение по мере добавления новых значений в этот список. В общем тут всё логично и понятно.

Соответственно с моей стороны разумно было бы ожидать, что удалив и вновь добавив пользователя на портал, я получу пользователя с новым ID. Однако это не так.
На практике, добавляя на портал пользователя (а точнее говоря учётную запись из AD), который однажды уже имел доступ к этому порталу, мы увидим, что SharePoint восстановит для него запись в User Information List с тем же самым ID, правда при этом обновит её значения новыми данными из AD. Чего мы и добивались.