Разделение RT на 2 отдельных: текст и аттачи

  • Автор темы Автор темы Omh
  • Дата начала Дата начала
NickProstoNick
Копируете RT-item, содержащий вложения, во временный документ.
Удаляете оттуда ненужные вложения.
Копируете RT-item с оставшимися вложениями в нужный документ. Ну или делаете append... к существующему item'у нужного документа.
Это самый корректный способ.

Добавлено:
Можно попроще, без сохранения на диск.
Геморройность этого кода уже обсуждали. Как по моему, это не годный код.
 
VladSh
Да вот мне кажется не все так просто.
Нужно удалить текст, вложенные изображения, таблицы... :)
 
Нужно удалить текст, вложенные изображения, таблицы... :)
Ну тут надо определиться: или в dxl заморачиваемся или удаляем.

Удаление через NotesRichTextNavigator - удалять всё, кроме RTELEM_TYPE_FILEATTACHMENT. Только надо проверить, будут ли удаляться картинки, вставленные из буфера обмена (у класса нет метода, получающего их). Если не покатит, то dxl.
 
Да вот мне кажется не все так просто.
Нужно удалить текст, вложенные изображения, таблицы... :)
По сабжу пришли к выводу, что задача не решается без dxl - в общем случае аттачи могут быть внутри секций, ячеек таблицы и т.п. Однако и dxl все не так просто как хотелось бы... Вариант решения я тогда отправил автору в личку, постеснявшись выкладывать портянку в паблик :-)
<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">"Разложить RT поле на два: в одном только аттачи, в другом - все остальное(без потери разметки)"</div></div><div class="sp-body"><div class="sp-content">
Основная проблема с dxl - это то, что при DXLExport документ выгружается с аттачами, да еще и кодированными в Base64. Т.о. оверхед будет огромным, т.к. нам по сути требуется только поменять разметку в RT и не трогать $Files вообще. Чтобы не гонять $Files туда-обратно, можно аккуратно удалить $Files из документа перед экспортом и вернуть после импорта. Все эти манипуляции происходят без обращения к диску.
Единственное, чего я не смог придумать - как не использовать временный док. В приведенном ниже решении он создается и сохраняется(sic!) в текущей БД.

наколенка, не очень правильная с точки зрения дизайна, но суть передана верно
<div class="sp-wrap"><div class="sp-head-wrap"><div class="sp-head folded clickable">"многабукв"</div></div><div class="sp-body"><div class="sp-content">
Код:
Const XSLT = |<xsl:stylesheet version="1.0" xmlns:xsl="http://www.w3.org/1999/XSL/Transform" xmlns:ln="http://www.lotus.com/dxl">
<xsl:output
method="xml"
omit-xml-declaration="yes"
indent="no"/>
<xsl:param name="unid" select="'unid'"/>
<xsl:param name="replica" select="'replica'"/>
<xsl:template match="/">
<document form='tmpForm'>
<xsl:attribute name="replicaid"><xsl:value-of select="$replica" /></xsl:attribute>
<noteinfo><xsl:attribute name="unid"><xsl:value-of select="$unid" /></xsl:attribute></noteinfo>
<item name='Body'>
<richtext>
<pardef id='1'/>
<par def='1'>
<xsl:for-each select="//ln:attachmentref">
<xsl:copy-of select="."/>
</xsl:for-each>
</par>
</richtext>
</item>
</document>
</xsl:template>
</xsl:stylesheet>|

Class OmhProblem

Private t_fileItemName As String
Private t_stuffItemName As String
Private t_doc As NotesDocument
Private t_tmpDb As NotesDatabase

Private t_item As NotesRichTextItem

'
' @link https://codeby.net/ipb.html?s=&showtopic=42747&view=findpost&p=209313
' @param i_itemName - имя RT поля, которое нужно разобрать
' @param i_fileItemName - имя RT поля, в которое лягут файлы
' @param i_stuffItemName - имя RT поля, в которое ляжет все остальное
Sub new( i_doc As NotesDocument , i_itemName As String, i_fileItemName As String, i_stuffItemName As String )
Set t_doc = i_doc
Set t_item = t_doc.GetFirstItem( i_itemName )
t_fileItemName = i_fileItemName
t_stuffItemName = i_stuffItemName
Set t_tmpDb = t_doc.ParentDatabase '' в кач-ве временной базы лучше использовать какую-нить локальную БД
End Sub

Public Sub solve()
Call processStuff() ' текст
Call processFiles() ' аттачи

Call t_doc.Save( True, False )
End Sub

' формируем RT без аттачей. тут как бы проблем не возникает:
' копируем исходное поле во временный документ, валим все аттачи, копируем обратно
' нельзя использовать исходный документ, т.к. удаление аттача из RT удаляет $File.
Private Sub processStuff()
Dim doc As New NotesDocument( t_tmpDb )
Dim tmpItem As NotesRichTextItem
Set tmpItem = t_item.copyItemToDocument( doc , "Body" )
Forall eo In tmpItem.EmbeddedObjects
If ( eo.Type = EMBED_ATTACHMENT ) Then
Call eo.remove()
End If
End Forall
Call tmpItem.CopyItemToDocument( t_doc, t_stuffItemName )
End Sub

' формируем RT, содержащее только вложения исходного поля
' 1. копируем поле во временный документ 
' 2. удаляем $File через doc.GetAttachment().remove - этот метод не удаляет ссылки на файлы в RT. Т.е. имеем RT с "битыми ссылками" на аттачи
' 3 .прогоняем документ через XSL Transformer, убирая из RT все, кроме аттачей 
' 4. результирующий документ рендерим в указанное поле исходного документа.
' 
' шаг  2 (танцы с удалением $File) нужен для оптимизации использования DXL: 
' в противном случае через парсер пройдет контент файлов. Пара файлов по 10Mb вполне в состоянии уложить клиента.
Private Sub processFiles()
Dim doc As New NotesDocument( t_tmpDb )
Call t_item.copyItemToDocument( doc , "Body" )
Dim filenames As Variant
filenames = Evaluate("@AttachmentNames" , doc )
Forall filename In filenames
Call doc.GetAttachment(filename).remove()
End Forall
Set doc = doMagic( doc )

' копируем обратно $Files - без них падает copyItemToDocument
Call t_item.copyItemToDocument( doc , "TmpBody" )
Call safeRemoveRtField( "TmpBody" ) 

Call t_item.remove() ' чистим исходное поле

Call doc.GetFirstItem("Body").CopyItemToDocument( t_doc , t_fileItemName )
End Sub


Private Function doMagic(doc As NotesDocument) As NotesDocument
Dim session As New NotesSession
Call doc.Save(True, False ) ' сохранение нужно для того, чтобы найти документ после DXLImport.
Dim unid As String
unid = doc.UniversalID
Dim exporter As NotesDXLExporter
Set exporter = session.CreateDXLExporter(doc)
Dim xsl As NotesStream
Set xsl = session.CreateStream()
Call xsl.WriteText( XSLT ) 
Dim transformer As NotesXSLTransformer 
Set transformer=session.CreateXSLTransformer( exporter , xsl )
Call transformer.AddParameter("unid" , unid )
Call transformer.AddParameter("replica" , doc.ParentDatabase.ReplicaID)

Dim importer As NotesDXLImporter
Set importer = session.CreateDXLImporter( transformer , t_tmpDb )
importer.DocumentImportOption = DXLIMPORTOPTION_REPLACE_ELSE_CREATE
Call exporter.process
Delete doc
Set doMagic = t_tmpDb.GetDocumentByUNID( unid )
End Function

' простое удаление RT поля сносит $Files. 
' поэтому удалять RT следует примерно так:
Private Function safeRemoveRtField(itemName As String)
Const TMP_ITEM_NAME = "Killme"
Dim tmpItem As New NotesItem( t_doc , TMP_ITEM_NAME , "" )
Call t_doc.ReplaceItemValue(itemName , tmpItem )
Call t_doc.RemoveItem(itemName)
Call t_doc.RemoveItem(TMP_ITEM_NAME)
End Function
End Class
 
turumbay
Спасибо!
Полезный код. Особенно по части удаления RT-поля:
Код:
Private Function safeRemoveRtField(itemName As String)
Const TMP_ITEM_NAME = "Killme"
Dim tmpItem As New NotesItem( t_doc , TMP_ITEM_NAME , "" )
Call t_doc.ReplaceItemValue(itemName , tmpItem )
Call t_doc.RemoveItem(itemName)
Call t_doc.RemoveItem(TMP_ITEM_NAME)
End Function
Что сделал я:
- cоздаю копию моего документа с аттачами (docMain);
- удаляю все поля из docMain, кроме $Files;
- формирую xml-строку временного документа в который дописываю теги attachmentref соответственно полям $Files из docMain;
- импорт временного документа
- дописываю RT-поле временного документа в docMain

Теги attachmentref для нескольких типов файлов загоняю в константы.
Такой метод мне показался менее геморройным по части парса xml.
 
turumbay я так понимаю "хитрости" по совмещению LS и трансформации вызваны опасениями оверхеда по памяти?
может вариант создания нового дока с SAX обработкой был бы менее затратным по ресурсам?!
ведь здесь по памяти >2-х раз гоняются файлы...
а так - да, подход интересный
 
Товарищи, а как насчет:
Код:
Call notesRichTextItem.AppendRTItem( notesRichTextItem2 )
?
 
а в чём трабла через DXML узнать какие аттачи в нужном РТ и отдельно изолировать их?
или вообще сразу все аттачи изолировать?
 
turumbay я так понимаю "хитрости" по совмещению LS и трансформации вызваны опасениями оверхеда по памяти?
"хитрости" с XSL затеяны для того, чтобы убрать из RT поля все, кроме аттачей. При этом сохраняются исходные иконки аттачей и все это - без выгрузки файлов на диск(т.е. security level = Don't allow restricted ops, что бывает важно для серверных агентов)
может вариант создания нового дока с SAX обработкой был бы менее затратным по ресурсам?!
ведь здесь по памяти >2-х раз гоняются файлы...
Файлы-то как раз не гоняются. Для этого используется вторая "хитрость": аттачи удаляются нафиг из документа перед экспортом и подкладываются обратно после импорта. Т.е. дерево DOM получается достаточно легковесным и его обработка не вносит сколько-нибудь значимой задержки. Кроме этого экономим на трафике и кодировании/декодировании в base64.
SAX ессно обыграет XSL, но есть подозрение, что с учетом приведенной выше оптимизации разница будет невелика. Реальный выигрыш можно будет почувствовать на документах, у которых само RT-поле содержит кучу разметки. Если в боди скопипащены html-версии второго тома "Войны и мира" и восемнадцатого издания таблиц Брадиса - я бы тоже подумал в сторону SAX
 
Код:
	Private Sub processStuff()
Dim doc As New NotesDocument( t_tmpDb )
Dim tmpItem As NotesRichTextItem
Set tmpItem = t_item.copyItemToDocument( doc , "Body" )
как один из примеров - аттачи гоняются в памяти
еще два раза в processFiles() (подобный код)
т.е. в инмомори док копируются (неоднократно) приатаченные файлы

яже предполагал - сразу гнать через SAX в новый док (однократно) со всеми сопутствующими коррекциями
 
Код:
	Private Sub processStuff()
Dim doc As New NotesDocument( t_tmpDb )
Dim tmpItem As NotesRichTextItem
Set tmpItem = t_item.copyItemToDocument( doc , "Body" )
как один из примеров - аттачи гоняются в памяти
еще два раза в processFiles() (подобный код)
т.е. в инмомори док копируются (неоднократно) приатаченные файлы
При таком способе копирования контент аттача не передается на клиента, что легко проверяется снифером. Т.е. ни трафика ни памяти данный код не жрет...

updated: а вот если "сразу гнать через SAX" - то файл сначала скачается с сервера, а затем еще и закодируется в base64. Поэтому имеет смысл "отцепить" файлы от документа перед выгрузкой...
 
я немного не о том (хотя аспект сетевого трафика небезинтересен -_- ), а именно о памяти, например - в упомянутом случае серверного кода
что бывает важно для серверных агентов
как я себе это представлял - полное содержимое поля (в т.ч. и с файлами) копируется в инстанс нового дока
 
Мы в соцсетях:

Обучение наступательной кибербезопасности в игровой форме. Начать игру!