spec/lib/data/del.cfm
2025-06-02 16:16:51 +03:00

297 lines
12 KiB
Plaintext
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

<cfsilent>
<!--- v0.4---><!---9:50 06.10.2020--->
<!--- v0.5---><!---2024-12-11 15:10:07--->
<!--- DEL.CFM - унифицированный шаблон операции удаления записи --->
<cfimport prefix="m" taglib="../../lib"/>
<cfimport prefix="m" taglib="../../lib/controls"/>
</cfsilent><cfif thisTag.executionMode IS "end" OR !thisTag.hasEndTag><m:silent silent="No">
<cfparam name="ATTRIBUTES.savemarker" type="string" default="delete"/>
<cfparam name="ATTRIBUTES.readonly" type="boolean" default="No"/>
<cfparam name="ATTRIBUTES.checkQuery" type="string" default="select 0 as chk"/> <!--- запрос проверки целостности.
Простая строка (параметр преобразуется до вызова тега).
Если можно удалять, должен вернуть 0 в поле chk --->
<cfparam name="ATTRIBUTES.displayDependencies" type="boolean" default="Yes"/>
<cfparam name="ATTRIBUTES.ignoreDependencies" type="boolean" default="No"/><!--- будем удалять, несмотря на наличие зависимостей Предположительно, мы примем меры в запросе удаления, или расчитываем на каскадное удаление. Имеет смысл, видимо, только в комбинации с displayDependencies=TRUE, иначе зачем описывать зависимости и потом их игнорировать. --->
<cfparam name="ATTRIBUTES.entity" type="string"/>
<cfparam name="ATTRIBUTES.accessObj" type="string" default="#ATTRIBUTES.entity#"/> <!--- объект доступа, права к которому проверяем --->
<cfparam name="ATTRIBUTES.deleteQuery" type="string" default=""/> <!--- запрос удаления *** надо бы параметризовать --->
<cfparam name="ATTRIBUTES.confirmMessage" type="string"/> <!--- текст для javascript:confirm() --->
<cfparam name="ATTRIBUTES.denyMessage" type="string" default="Удаление невозможно, существуют зависимые записи."/> <!---
сообщение о невозможности удаления записи (если проверка целостности говорит нельзя) --->
<cfparam name="ATTRIBUTES.queryString" type="string" default=""/>
<cfparam name="ATTRIBUTES.defaultBackUrl" type="string" default="#ATTRIBUTES.entity#_ls.cfm"/>
<cfparam name="ATTRIBUTES.output" type="string" default=""/>
<cfparam name="ATTRIBUTES.status" type="string" default=""/>
<cfparam name="thisTag.dependencies" type="array" default=#arrayNew(1)#/>
<cfparam name="thisTag.conditions" type="array"/>
<cfset "CALLER.#ATTRIBUTES.status#"=CreateObject("component","status").init(false,"","")/>
<cfif len(ATTRIBUTES.queryString) EQ 0>
<cfset i = 0 />
<cfloop array=#thisTag.conditions# index="condition">
<cfif i GT 0>
<cfset ATTRIBUTES.queryString="#ATTRIBUTES.queryString#&"/>
</cfif>
<cfset i = i+1/>
<cfset ATTRIBUTES.queryString="#ATTRIBUTES.queryString##condition.field#=#condition.value#"/>
</cfloop>
</cfif>
<m:track
thisURL="#request.thisPage#?#ATTRIBUTES.queryString#"
defaultBackUrl="#ATTRIBUTES.defaultBackUrl#"
output="tr"
/>
<cfquery name="qCheck" datasource="#request.DS#">
#preserveSingleQuotes(ATTRIBUTES.checkQuery)#
</cfquery>
<cfset sDenyMessage=""/>
<cfoutput query="qCheck">
<cfset sDenyMessage=evaluate("""#ATTRIBUTES.denyMessage#""")/>
</cfoutput>
<!--- быстрая проверка --->
<cfset dependentRecordsExist = false/>
<cfloop array=#thisTag.dependencies# index="dep">
<cfif dep.query.recordCount GT 0>
<cfset dependentRecordsExist = true/>
<cfbreak/>
</cfif>
</cfloop>
<!--- do delete --->
<m:ac obj="#ATTRIBUTES.accessObj#" act="#request.PERMISSION_WRITE#" output="writePermitted">
<cfset doSave = false/>
<cfif !ATTRIBUTES.readonly>
<cfloop list=#ATTRIBUTES.savemarker# index="marker">
<cfif structKeyExists(FORM,"#marker#")><!--- удаление можно делать только постом --->
<cfset doSave = true/>
<cfbreak/>
</cfif>
</cfloop>
</cfif>
<cfif doSave AND qCheck.chk EQ 0>
<cftry> <!--- <cfoutput> <cfset i = 0 />
<cfloop list=#ATTRIBUTES.deleteQuery# delimiters="?" index="queryPart">
<cfif i GT 0>
cfqueryparam
cfsqltype='#condition.cfsqltype#'
value='#condition.value#'
null='#condition.null#'/
</cfif><cfset i = i + 1/>*#queryPart#* #i#
</cfloop>
</cfoutput> --->
<cfquery name="qDel" datasource="#request.DS#" result="result">
<cfif len(ATTRIBUTES.deleteQuery)>
<!--- запрос удаления должен быть сформулирован так,
чтобы условия удаления были в конце --->
<cfset i = 0 />
<cfloop list=#ATTRIBUTES.deleteQuery# delimiters="?" index="queryPart">
<cfif i GT 0>
<cfqueryparam
cfsqltype='#condition.cfsqltype#'
value='#condition.value#'
null='#condition.null#'/>
</cfif><cfset i = i + 1/>#queryPart#
</cfloop>
<cfelse>
delete from #ATTRIBUTES.entity# where /**/
<cfset i = 0 />
<cfloop array=#thisTag.conditions# index="condition">
<cfif i GT 0> AND </cfif>
<cfset i = i + 1/>
#condition.expression#=<cfqueryparam
cfsqltype='#condition.cfsqltype#'
value='#condition.value#'
null='#condition.null#'/>
</cfloop>
</cfif>
</cfquery>
<!--- *** неуклюжий костыль --->
<cfset CALLER.saveAndClose = true/><!--- сигнал для закрытия экрана --->
<cfcatch type="database">
<cfset "CALLER.#ATTRIBUTES.status#.errorState"=true/>
<cfset "CALLER.#ATTRIBUTES.status#.errorMessage"="Ошибка при обращении к базе данных. #cfcatch.message# #cfcatch.detail# #cfcatch.tagcontext[1].template# line #cfcatch.tagcontext[1].line#"/>
<!--- <cfrethrow/> --->
</cfcatch>
</cftry>
</cfif>
</m:ac>
<cfif !writePermitted><!--- перетирания cfcatch не будет --->
<cfset "CALLER.#ATTRIBUTES.status#.errorState"=true/>
<cfset "CALLER.#ATTRIBUTES.status#.errorMessage"="Ваших полномочий недостаточно для выполнения данной операции."/>
</cfif>
<!--- print output --->
<cfsavecontent variable="markup">
<cfoutput>
<div class="container-fluid">
<div class="row">
<br>
<form name="frmDel" action="#request.thisPage#" method="POST">
<cfif NOT writePermitted>
<!--- ситуация возможна при прямом заходе по URL. Надо высказать юзарю свое неодобрение. --->
<!--- <h4 class="text-danger">Ваших полномочий недостаточно для выполнения данной операции.</h4>
<br/> --->
<cfelseif qCheck.chk GT 0>
<h4 class="text-danger">#ATTRIBUTES.denyMessage#</h4>
<br/>
<cfelseif dependentRecordsExist>
<cfif ATTRIBUTES.displayDependencies>
<h4 class="text-danger">#ATTRIBUTES.denyMessage#</h4>
<br/>
<cftry>
<cfloop array=#thisTag.dependencies# index="dep">
<cfif dep.query.recordCount GT 0>
<h5>#dep.title#</h5>
<cfif dep.showDetails>
<table class="worktable">
<thead>
<tr>
<th width="1%" class="c"></th>
<cfloop array=#dep.fields# item="fld">
<cfif len(fld.title)>
<th>#fld.title#</th>
</cfif>
</cfloop>
<th width="1%"></th>
</tr>
</thead>
<cfoutput query=#dep.query#>
<cfset urlQueryString=""/>
<cfloop array=#dep.keys# index="keyName">
<cfif len(urlQueryString)>
<cfset urlQueryString="#urlQueryString#&"/><!---***--->
</cfif>
<cfset urlQueryString="#urlQueryString##keyName#=#dep.query[keyName][currentRow]#"/>
</cfloop>
<tr>
<td>
<a href="#dep.page#?#urlQueryString#&#tr.fwx#" <!---class="far fa-edit"--->><img src="img/edit.gif"/></a>
</td>
<cfloop array=#dep.fields# item="fld">
<cfif len(fld.title)>
<td>
<cfif isDefined("fld.formatter")>
#fld.formatter(dep.query[fld.name][currentRow])#
<cfelse>
#dep.query[fld.name][currentRow]#
</cfif>
</td>
</cfif>
</cfloop>
<td>
<a href="#dep.delete_page#?#urlQueryString#&#tr.fwx#" <!---class="far fa-trash-alt text-danger"--->><img src="img/del.gif"/></a>
</td>
</tr>
</cfoutput>
</table>
<cfelse><!---dep.showDetails--->
<cfoutput><p>#dep.denyMessage# (#dep.query.recordCount#)</p></cfoutput>
</cfif><!---dep.showDetails--->
</cfif>
</cfloop>
<cfcatch type="any">
<cfoutput>#cfcatch.message# #cfcatch.detail# #cfcatch.tagcontext[1].template# line #cfcatch.tagcontext[1].line#/></cfoutput><cfrethrow/>
</cfcatch>
</cftry>
<cfelse><!---ATTRIBUTES.displayDependencies--->
<cfoutput>#ATTRIBUTES.denyMessage#</cfoutput>
</cfif><!---ATTRIBUTES.displayDependencies--->
</cfif>
<cfif NOT dependentRecordsExist OR ATTRIBUTES.ignoreDependencies><!--- qCheck.chk GT 0 | dependentRecordsExist --->
<cfif writePermitted>
<input type="hidden" name="track" value="#tr.self#">
<cfloop array=#thisTag.conditions# index="condition">
<!---<input type="hidden" name="#condition.expression#" value="#evaluate('#condition.expression#')#"/>--->
<input type="hidden" name="#condition.field#" value="#evaluate('#condition.field#')#"/><!--- по замыслу, берется из скоупа FORM (*** слишком неявно)--->
</cfloop>
<button type="submit" class="maincontrol btn btn-secondary" name="delete" onclick="if(!confirm('#evaluate("""#ATTRIBUTES.confirmMessage#""")#')) return false;">Удалить</button>
</cfif>
</cfif>
<button type="button" class="maincontrol btn btn-secondary" name="close" onclick="document.location.href='#tr.backURL#'">
Закрыть
</button>
<!--- <cfdump var=#dependentRecordsExist#/>
<cfdump var=#ATTRIBUTES.ignoreDependencies#/>
<cfdump var=#NOT dependentRecordsExist OR ATTRIBUTES.ignoreDependencies#/> --->
</form>
</div>
</div>
</cfoutput>
</cfsavecontent>
</m:silent><cfif len(ATTRIBUTES.output)><cfset "CALLER.#ATTRIBUTES.output#" = #markup#/><cfelse>
<cfoutput>#markup#</cfoutput>
</cfif></cfif>
<!--- пример монструозной конструкции Postgre для каскадного удаления --->
<!--- with
inst as (
delete from instance
where instance_uid='e283e337-0ddc-459c-88b8-57d1963b4a7d'
returning instance_uid
),
st as (
delete from instance_state
where instance_uid in (select instance_uid from inst)
returning instance_state_uid
),
op as (
delete from instance_operation
where instance_uid in (select instance_uid from inst)
returning instance_operation_uid
),
par as (
delete from instance_operation_param
where instance_operation_uid in (select instance_operation_uid from op)
returning instance_operation_param_uid
),
cfs as (
delete from instance_operation_cfs_param
where instance_operation_uid in (select instance_operation_uid from op)
returning instance_operation_cfs_param_uid
)
select * from inst
left outer join st on (inst.instance_uid=st.instance_uid)
left outer join op on (inst.instance_uid=op.instance_uid)
left outer join par on (op.instance_operation_uid=par.instance_operation_uid)
left outer join cfs on (op.instance_operation_uid=cfs.instance_operation_uid)
; --->
<!--- такой вопрос: точно ли будут задействованы все CTE, если я на них не сошлюсь? На месте оптимизатора я бы проигнорил незадействованные. Наверное, надо все включить в джойн для уверенности? --->