297 lines
12 KiB
Plaintext
297 lines
12 KiB
Plaintext
<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, если я на них не сошлюсь? На месте оптимизатора я бы проигнорил незадействованные. Наверное, надо все включить в джойн для уверенности? ---> |