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