
أداة تحليل التعليمات البرمجية الثابتة لـ CFML.
الترخيص: BSD
الإصدار الحالي: 1.5.x
انظر changelog.md لمزيد من المعلومات.
Cflint هو مشروع تم تطويره وعمل عليه من قبل المتطوعين. عند تسجيل المشكلات من فضلك ، كن لطيفًا ومراعيًا. نحن هنا للمساعدة. نحن نقدر حقًا الإصلاحات والتحسينات ، لذلك لا تتردد في التحدث إلينا و/أو تقديم طلبات السحب.
/src/main يحتوي على رمز المصدر. يمكن العثور على الاختبارات في /src/test . تعتمد Cflint اعتمادًا كبيرًا على مشروع CFParser بالإضافة إلى مجموعة من مكتبات Java من طرف ثالث.
يعتبر الفرع الرئيسي قاعدة كود مستقرة. معظم التطوير يحدث في فرع DEV. فروع التنمية المحلية لقضايا محددة.
شوك المستودع في حسابك واستنساخ أو قم بتنزيل قاعدة الكود كملف zipile.
قم بتثبيت الأدوات التي تختارها وبناءها عبر Gradle أو Maven (تم إهمالها). Cflint يتطلب java 8.
أ. Gradle: Execute
gradlew build
في دليل Cflint
ب. مافن: تنفيذ
mvn clean install
في دليل Cflint
بدلاً من ذلك ، استيراد قاعدة كود cflint في IDE التي تختارها واستخدم تكامل Gradle/Maven على التوالي. يجب أن يعمل هذا خارج المربع لمستخدمي Eclipse و Intellij.
احصل على أحدث إصدار من صفحة إصدار Maven Central أو Cflint Github أو بناء المشروع.
إذا كنت ترغب في استخدام cflint من داخل مشروع Maven آخر ، فاستخدم:
< dependency >
< groupId >com.github.cflint</ groupId >
< artifactId >CFLint</ artifactId >
< version >1.4.0</ version >
</ dependency >أو استخدم دائمًا أحدث:
< dependency >
< groupId >com.github.cflint</ groupId >
< artifactId >CFLint</ artifactId >
< version >LATEST</ version >
</ dependency >مع استعادة الثنائيات واحدة أو في الاتجاه الآخر ، يمكنك الآن استخدام Cflint على سطر الأوامر.
CFLint-1.5.0-all.jar
java -jar CFLint-1.5.0-all.jar -folder <baseFolder>
java -jar CFLint-1.5.0-all.jar -file <fullPathToFile>
java -jar CFLint-1.5.0-all.jar -help
ملاحظة: هذا عمل مستمر ، نقوم حاليًا بتجميع المعلومات من مجموعة متنوعة من المصادر.
أبسط الخيارات لتنفيذ CFLINT هي عبر سطر الأوامر. يحتوي CFLINT حاليًا على وضع واجهة المستخدم (الناتج عن -ui على سطر الأوامر) والذي سيتم إزالته من خلال الأحدث لـ CFLINT 2.0 - انظر العدد رقم 316. إذا كنت تعتمد على وضع واجهة المستخدم ، فأنت للأسف بمفردك - لن يذهب أي عمل إلى هذا من هنا فصاعدًا.
بدلاً من ذلك إلى سطر الأوامر ، يمكنك وضع ملفات .cflintrc في أدلة معينة. يتيح لك تكوين CFLINT بهذه الطريقة تشغيل قواعد محددة في أجزاء محددة من التطبيق الخاص بك.
يدعم CFLINT حاليًا التكوين القائم على JSON- و XML. يتم إهمال التكوين المستند إلى XML في CFLINT 1.3.0 وسيتم إزالته في CFLINT 2.0.
عند تنفيذ CFLINT ، فإنه يقوم بمسح الكود الخاص بك (باستخدام CFParser). ثم يتم فحص شجرة بناء الجملة مقابل مجموعة من القواعد المدمجة.
في cflint ، يتم استدعاء هذه القواعد وتنفيذها كملابس إضافية (فهي تعيش في /src/main/java/com/cflint/plugins ). بشكل افتراضي ، سيتم استخدام جميع القواعد مقابل قاعدة الشفرة الخاصة بك. هذا ما سيفعله الكثير من الأشخاص ، لكن استخدام التكوين يتيح لك إنشاء سيناريو مخصص لاختبار الرمز الخاص بك مقابل. انظر القواعد. md لمزيد من المعلومات حول القواعد ومعناها.
Cflint هو رأي وكل إصدار بعد 1.3.0 لن يقوم أبدًا بالمسح الضوئي في الدلائل التي تبدأ بـ . لمنع إضاعة وقت الدلائل المخفية مثل تكوين البناء أو وحدة تخزين المكتبة أو معلومات التحكم في الإصدار.
ملف التكوين الافتراضي والعالمي هو /src/main/resources/cflint.definition.json main/resources/cflint.definition.json. الاستخدام الشائع لـ Cflint عادة لا يتطلب استبدال هذا الملف.
يتيح لك وضع ملف .cflintrc في دليل تحديد قواعد معينة يجب تنفيذها لهذا الدليل وأطفاله. بالإضافة إلى ذلك ، يمكنك تحديد حفنة من الخصائص الأخرى.
مثال .cflintrc يظهر أدناه:
{
"rule" : [ ],
"excludes" : [ ],
"includes" : [ {
"code" : " FUNCTION_HINT_MISSING "
} ],
"inheritParent" : false ,
"parameters" : { }
} تتيح لك rule إضافة مكون إضافي لهذا المجلد غير مدرج في التكوين العالمي. انظر ruleImpl في cflint.definition.json للحصول على أمثلة.
excludes includes تحديد مجموعة من الكائنات التي تصف القواعد التي تريد تطبيقها على هذا الدليل وأطفالها. في المثال أعلاه ، القاعدة الوحيدة التي يتم فحصها ستكون function_hint_missing.
يتم تكوينات inheritParent إذا كانت القواعد المحددة في العالم العالمي أو أي تكوين الوالدين يجب أن تُرث كمجموعة أساسية من القواعد.
تسمح parameters بتكوين القواعد. انظر القواعد. يجب أن تسبق اسم المعلمة مع اسم القاعدة مفصولة بنقطة.
يرجى ملاحظة: تم إهمال inheritPlugins output في CFLINT 1.2.0 وإزالته في 1.4.0. يتم الآن معاملة ميراث البرنامج المساعد دائمًا على أنه صحيح لأن الفريق لا يمكنه رؤية حالة الاستخدام التي يجب تعطيلها. يمكن التحكم في نوع الإخراج في مكان آخر ، مثل أعلام سطر الأوامر.
نحن نقدم المخطط مع الخصائص المنهكة المستبعدة.
انظر وصفات لبعض أمثلة الاستخدام من .cflintrc . يمكن العثور على ملفات مثال عن طريق تصفح ملفات اختبار المشروع.
غالبًا ما تكون هناك سيناريوهات تريد فيها عمومًا تشغيل مجموعة معينة من القواعد مقابل الكود الخاص بك ولكن في حالات محددة تحتاج إلى تجاهل انتهاك صالح.
مثال شائع هو انتهاكات cfqueryparam_req التي لا يمكن إصلاحها عن طريق تطبيق <cfqueryparam> لأن خادم DB الخاص بك لا يسمح للمعلمات في مواضع معينة (على سبيل المثال في SELECT something FROM #application.config.linkedServerName#.DefaultDatabase.dbo.Comment . انظر العدد رقم 282 لمزيد من الأمثلة.
تقدم CFLINT تكوينًا قائمًا على التعليقات التوضيحية للتعامل مع هذا السيناريوهات المماثلة. يمكن وضع التعليقات التوضيحية على مستوى المكون أو الوظيفة في CFC أو مضمّنة بالرمز.
<!---
@CFLintIgnore SOMETHINGELSE,MISSING_VAR,ANOTHERTHINGTOIGNORE
--->
تجاهل جميع القواعد على الخط الحالي:
//cflint ignore:line
تجاهل قاعدة محددة (أو قائمة قواعد مفصولة بفاصلة) على السطر الحالي:
//cflint ignore:MISSING_VAR
تجاهل المتعدد التعليقات التوضيحية:
/*
@CFLintIgnore SOMETHINGELSE,MISSING_VAR,ANOTHERTHINGTOIGNORE
*/
داخل SQL ، يمكنك أيضًا الاستخدام
<!--- @CFLintIgnore CFQUERYPARAM_REQ --->
لتجاهل انتهاك القاعدة على السطر التالي.
يتم تشغيل تكوين المكونات الإضافية والتي يتم تضمين القواعد التي تبدأ بالتكوين العالمي وتتدفق من خلال معلمات سطر الأوامر ، وقواعد مستوى المجلد ، وصولاً إلى التعليقات التوضيحية داخل المصدر.
-configfile ، لا نشجع هذا الخيار لاستخدامه في العمليات اليومية لـ CFLINT)-rulegroups ، السلوك الافتراضي هو -RuleGroups! تجريبية)-includeRule و -excludeRule )قاعدة التكوين التي هي الأقرب إلى القاعدة هي القاعدة التي تسري.
.cflintrc ، فسيتم إطلاق النار على ملفات المصدر في هذا الجزء من شجرة المصدر.-includeRule "MISSING_VAR,CFQUERYPARAM_REQ" تدعم CFLINT مجموعة متنوعة من خيارات التقارير والإخراج التي يمكنك التحكم فيها عبر أعلام سطر الأوامر. إلى جانب تنسيقات الإخراج المستهدفة للنص أو XML أو JSON أو HTML ، يمكنك أيضًا تشغيل CFLINT مع خيارات لإخراج هادئ وموافق على الأخطاء.
إذا لم يتم تحديد تنسيق إخراج مستهدف على الإطلاق ، فستكون CFLINT افتراضية لإنشاء تقرير HTML في ملف cflint-result.html .
يمكنك فرض سلوك الإخراج الخاص بـ CFLINT على STDOUT و STDERR من خلال تحديد خيارات للهدوء والموافق والتصحيح. إذا لم تحدد أيضًا ، فسيقوم CFLINT بإرجاع المعلومات الداخلية الأساسية وإخراج الخطأ إلى Stdout و STDERR.
وضع الهادئ ( -quiet <boolean> ) يمنع معظم الإخراج الذي يمكن أن تنشئه Cflint أثناء البصر. قد يحتوي هذا على أخطاء واستثناءات فعلية ولكن أيضًا معلومات مثل إنهاء تحليل القالب المتكرر أو مشكلات تكوين معينة. لا تقم بتشغيل الوضع الهادئ إذا كنت ستحتاج على الأرجح إلى مساعدة مع رسائل الخطأ أو تريد أن تفهم بشكل أفضل ما تفعله Cflint.
هذا هو الحد الأدنى للوضع للإخراج الذي يمكنك تشغيل CFLINT فيه ، وقد استلهمت الميزة في الأصل العدد رقم 4.
قد تكون هناك رسائل عرضية من CFParser و ANTLR يتم دفعها إلى STDERR في هذه المرحلة - على الرغم من أن cflint يعمل في وضع هادئ. هذه قضية معروفة وسيتم معالجتها في المستقبل.
وضع المطوّل ( -verbose <boolean> ) يتيح إخراج Linting المطوّل. يحتوي هذا على معلومات حول تنسيقات الإخراج المحددة وملفات التكوين التي يتم العثور عليها ومعالجتها أثناء linting وكذلك الملف الذي تم معالجته حاليًا يعمل عليه CFLINT (يوضح الملفات التي تم مسحها ضوئيًا فقط).
إذا كنت ترغب في مزيد من المعلومات حول الأعمال الداخلية لـ CFLINT أثناء التنفيذ ، فإن وضع المطالب هو الحد الأدنى الذي يجب عليك تشغيله.
يمكّن وضع التصحيح ( -debug <boolean> ) ناتج التصحيح. ينطوي وضع التصحيح على وضع المطول ولكنه يضيف معلومات إضافية مثل الرموز المميزة لكل ملف محلل وكل ملف معالج (بغض النظر عن دعمه بواسطة قائمة التمديد الافتراضية) في تدفقات الإخراج.
من الممكن تشغيل وتشغيل أوضاع الهادئة والمسامير والتصحيح معًا في نفس الوقت. هذا المقصود جزئيًا لأنك قد لا ترغب في رؤية معلومات الخطأ التي يتم قمعها بواسطة الوضع الهادئ ، ولكن لا تزال تريد ، لذا راجع بعض المعلومات التي يتم عرضها في وضع المطوّل. يرجى أخذ هذا السلوك مع حبة من الملح -قد يكون هناك السيناريو الفردي الذي يسبب فيه الجمع بين -quiet ، -verbose و -debug ناتجًا غير عادي.
الاستثناء هو وضع التصحيح. في وضع التصحيح ، ستتجاهل CFLINT دائمًا إعدادات المستخدم من أجل المطوّل والهدوء وتعيين verbose إلى true quiet إلى false .
يرشد العلم -html Cflint إلى إنشاء مستند HTML. بناء الجملة الكامل هو:
-html -html <outputFileName>
العلم -xml يرشد cflint لإنشاء XML. هناك خياران لتقارير XML.
الخيار الأول هو ما نسميه Cflint XML. إنه تنسيق داخلي يلتزم بالمخطط الأساسي المقدم هنا. يمكنك بعد ذلك استخدام هذا التنسيق كما هو أو للقيام بمزيد من المعالجة من اختيارك.
الخيار الثاني هو FindBugs XML. تلتزم مستند XML الناتج بالإصدار الحالي من تعريف مخطط XML FindBugs BugCollection ويمكن استخدامه في معظم منتجات Ci-/Build Server. يمكن لـ JetBrains TeamCity 10+ استيراد هذا التنسيق خارج الصندوق.
يرجى ملاحظة : حاليًا لا يمكن إنتاج كل من نكهات تقارير XML في نفس الوقت. هذا هو القيد المعروف. ستتم إزالة هذا القيد كجزء من CFLINT 2.0 (انظر العدد رقم 331).
لإنشاء Cflint XML توفير وسيطات سطر الأوامر التالية:
-xml -xmlstyle cflint -xmlfile <outputFileName>
مثال على Cflint XML:
<? xml version = " 1.0 " encoding = " UTF-8 " ?>
< issues version = " 1.2.1 " timestamp = " 1500107134 " >
< issue severity = " WARNING " id = " CFQUERYPARAM_REQ " message = " CFQUERYPARAM_REQ " category = " CFLint " abbrev = " CR " >
< location file = " /Users/kai/Documents/Code/paypal.cfc " fileName = " paypal.cfc " function = " doSomething " column = " 0 " line = " 325 " message = " < cfquery > should use < cfqueryparam/ > for variable 'arguments.PaymentType'. " variable = " arguments.PaymentType " >
< Expression > <![CDATA[ <cfquery name="doPayment" datasource="#paymentDatasource#">...some more Details... ]]> </ Expression >
</ location >
</ issue >
< issue severity = " WARNING " id = " CFQUERYPARAM_REQ " message = " CFQUERYPARAM_REQ " category = " CFLint " abbrev = " CR " >
< location file = " /Users/kai/Documents/Code/paypal.cfc " fileName = " paypal.cfc " function = " doSomethingElse " column = " 0 " line = " 432 " message = " < cfquery > should use < cfqueryparam/ > for variable 'arguments.something'. " variable = " arguments.something " >
< Expression > <![CDATA[ <cfquery name="doPayment" datasource="#paymentDatasource#">...some more Details... ]]> </ Expression >
</ location >
</ issue >
...
< counts totalfiles = " 108 " totallines = " 55596 " >
< count code = " CFQUERYPARAM_REQ " count = " 39 " ></ count >
< count severity = " WARNING " count = " 39 " ></ count >
</ counts >
</ issues >لإنشاء FindBugs XML توفير وسيطات سطر الأوامر التالية:
-xml -xmlstyle findbugs -xmlfile <outputFileName>
يتم إنشاء تنسيق FindBugs XML حاليًا باستخدام مستند XSLT ، مما يحول تقرير CFLINT إلى FindBugs XML ( /src/main/resources/findbugs/cflint-to-findbugs.xsl ).
يمكن إنشاء إخراج JSON مع
-json -jsonfile <outputFileName>
مثال على cflint json:
{
"version" : " 1.2.1 " ,
"timestamp" : 1501202128 ,
"issues" : [ {
"severity" : " ERROR " ,
"id" : " MISSING_VAR " ,
"message" : " MISSING_VAR " ,
"category" : " CFLINT " ,
"abbrev" : " MV " ,
"locations" : [ {
"file" : " src/test/resources/com/cflint/tests/Ignores/ignoreCFMLAny2.cfc " ,
"fileName" : " ignoreCFMLAny2.cfc " ,
"function" : " testFunction " ,
"column" : 6 ,
"line" : 14 ,
"message" : " Variable someVar is not declared with a var statement. " ,
"variable" : " someVar " ,
"expression" : " someVar "
} ]
} ],
"counts" : {
"totalFiles" : 7 ,
"totalLines" : 49 ,
"countByCode" : [ {
"code" : " MISSING_VAR " ,
"count" : 1
} ],
"countBySeverity" : [ {
"severity" : " ERROR " ,
"count" : 1
} ]
}
}مخطط JSON متاح هنا.
يمكن إنشاء إخراج النص العادي مع
-text -textfile <outputFileName>
مثال على إخراج النص العادي:
Issue
Severity:WARNING
Message code:CFQUERYPARAM_REQ
File:/Users/kai/Documents/Code/paypal.cfc
Column:0
Line:79
Message:<cfquery> should use <cfqueryparam/> for variable 'arguments.something'.
Variable:'arguments.something' in function:
Expression:<cfquery name="qry" datasource="#variables.dsn#" cachedwithin="#createTimeSpan(0,0,arguments.cacheInMins,0)#">rn...some Details...
Severity:WARNING
Message code:CFQUERYPARAM_REQ
File:/Users/kai/Documents/Code/paypal.cfc
Column:0
Line:145
Message:<cfquery> should use <cfqueryparam/> for variable 'arguments.something'.
Variable:'arguments.something' in function:
Expression:<cfquery name="qry" datasource="#variables.dsn#" cachedwithin="#createTimeSpan(0,0,arguments.cacheInMins,0)#">rn...some Details...
...
Total files:108
Total lines:55690
Issue counts:1
CFQUERYPARAM_REQ:4
Total issues:4
Total warnings:4
للتفاعل مباشرة مع CFLINT داخل JVM استخدم API CFLINT.
import com . cflint . api . CFLintAPI ;
import com . cflint . api . CFLintResult ;
CFLintAPI api = new CFLintAPI ();
CFLintResult result = api . scan ( filename );
String jsonResult = result . getJSON ();بالنسبة إلى Jenkins ، يرجى إلقاء نظرة على المكون الإضافي Jenkins/Hudson المذكور أدناه.
حصل فريق JetBrains على دعم لتقارير Indagbugs XML Code Inspection. يمكن إنتاجها خارج الصندوق مع CFLINT من 1.2.0 فصاعدًا (انظر أعلاه في قسم FindBugs XML).
هناك دعم لـ Sonarqube من خلال البرنامج المساعد Sonar Coldfusion الخاص بـ Stepstone المذكور أدناه.
بالنسبة إلى Azure DevOps/TFS ، يرجى إلقاء نظرة على ملحق إنشاء أنابيب Azure/TFS المذكورة أدناه.
هناك غلاف NPM لـ cflint أدناه. يرجى العلم أن الغلاف يبدو أنه يأتي مع ثنائي cflint المجمل الذي قد لا يكون محدثًا ، وهو خارج عن سيطرتنا.
قد تعمل المنتجات الأخرى في فئة خادم التكامل/الإنشاء أيضًا. إذا كنت تستخدم منتجًا معينًا يعمل لك مع Cflint ، فيرجى إخبارنا بذلك. إذا لم تتمكن من الحصول على cflint للعمل في بيئة تستخدمها ، فيرجى إخبارنا أيضًا - قد نتمكن من المساعدة.
هناك العديد من عمليات تكامل IDE لـ CFLINT المتوفرة. فيما يلي بعض الأوصاف الموجزة ، ولكن إذا كنت ترغب في معرفة المزيد ، راجع مشاريع الطرف الثالث المثير للاهتمام.
هناك دعم IDE للنص Sublime 3 من خلال مشروع طرف ثالث باستخدام SupelimeLinter.
هناك أيضًا دعم لباني Adobe Coldfusion من خلال مشروع طرف ثالث. مستخدمو CFBuilder ، يرجى أيضًا رؤية المناقشة في العدد رقم 327.
يمكن لمستخدمي ATOM الاندماج عبر AtomLinter من خلال مشروع طرف ثالث.
يتوفر أيضًا امتداد لرمز Visual Studio كمشروع طرف ثالث.
يتوفر دعم intellij Jetbrains كمكون إضافي لجهة خارجية.
package com . cflint . plugins . core ;
import net . htmlparser . jericho . Element ;
import cfml . parsing . cfscript . script . CFFuncDeclStatement ;
import cfml . parsing . cfscript . script . CFFunctionParameter ;
import cfml . parsing . cfscript . script . CFScriptStatement ;
import com . cflint . BugList ;
import com . cflint . plugins . CFLintScannerAdapter ;
import com . cflint . plugins . Context ;
import com . cflint . tools . CFTool ;
public class ArgDefChecker extends CFLintScannerAdapter {
@ Override
public void expression ( final CFScriptStatement expression , final Context context , final BugList bugs ) {
if ( expression instanceof CFFuncDeclStatement ) {
final CFFuncDeclStatement function = ( CFFuncDeclStatement ) expression ;
for ( final CFFunctionParameter argument : function . getFormals ()) {
// handler.addArgument(param.getName());
final String name = argument . getName ();
if (! argument . toString (). contains ( "required" ) && ! argument . toString (). contains ( "=" )) {
function . getLine ();
function . getColumn ();
context . addMessage ( "ARG_DEFAULT_MISSING" , name );
}
}
}
}
@ Override
public void element ( final Element element , final Context context , final BugList bugs ) {
if ( element . getName (). equals ( "cfargument" )) {
final String name = element . getAttributeValue ( "name" );
final boolean required = CFTool . toBoolean ( element . getAttributeValue ( "required" ));
final String defaultExpr = element . getAttributeValue ( "default" );
if (! required && defaultExpr == null ) {
element . getSource (). getRow ( element . getBegin ());
element . getSource (). getColumn ( element . getBegin ());
context . addMessage ( "ARG_DEFAULT_MISSING" , name );
}
}
}
} بالنظر إلى element الوظيفة ، فإن الوسائط هي:
element - علامة CFML الحاليةcontext - يتم فحص الملف الحاليbugs - كائن الانتهاكات الملحمة أسهل طريقة لتحقيق ذلك هي مع ملف .cflintrc مخصص:
يتم تجاهل الحقل includes إذا كانت قائمة فارغة ، لذا ببساطة إضافة عنصر واحد لا يتطابق معه.
{
"code" : " NOTHING "
}أو ببساطة أكثر:
{}سيتجاهل ما يلي جميع القواعد في المجلد الحالي وأدلي.
{
"rule" : [ ],
"excludes" : [ ],
"includes" : [ {} ],
"inheritParent" : false ,
"parameters" : {}
} يمكن تبسيط ذلك باستخدام القيم الافتراضية لملف .cflintrc :
{
"includes" : [{}],
"inheritParent" : false
}انظر المناقشة في العدد رقم 290 لمزيد من المعلومات.
يمكن تجاوز المعلمات داخل القواعد في ملفات .cflintrc . استخدم اسم القاعدة والمعلمة المرتبطة بنقطة.
{
"parameters" : {
"VariableNameChecker.maxLength" : " 15 "
}
} قم بتزويد ملف cflintexclude.json في سطر الأوامر مع وسيطة -filterfile.
لتصفية رسائل global_var في مجلد "some package location" ، أضف ما يلي إلى cflintexclude.json
[
other exclude rules...,
{"file":".*some\\package\\location\\.*","code":"GLOBAL_VAR"}
]
ملاحظة: يجب الهروب من المائل الخلفي مرتين ، مرة واحدة ل json ، مرة واحدة للتعبيرات العادية
[
other exclude rules...,
{"file":".*some/package/location/.*","code":"GLOBAL_VAR"}
]
رفع المشكلات هنا على جيثب وسننظر إليهم.
يحتوي فريق CFML Slack على قناة #cflint التي يمكنك الانضمام إليها والتحدث مع معظم المساهمين العاديين والمستخدمين الآخرين.
انظر المساهمة. md لمزيد من المعلومات.
يرجى ملاحظة أن غالبية المكتبات والمشاريع المذكورة هنا لا ترتبط مباشرة والاحتفاظ بها من قبل فريق CFLINT. يرجى الاطلاع على مؤلفي ومحلل المشروع المعني للدعم باستخدام مكتباتهم أولاً.
إذا كنت تعمل على (أو كنت تفكر في البدء) مشروع يتعلق بـ Cflint ، فيرجى إخبارنا بذلك. يسعدنا تضمين مشاريع الجهات الخارجية ذات الصلة في القائمة أعلاه.